./test/search.js annotated source

Back to index

        
1import {strict as assert} from 'node:assert';
2import {search} from '../dist/search.js';
3
4const item = name => ({name});
5

Most of the tests operate on this pre-set list of items to search

7const ITEMS = [
8    item('Linus Lee'),
9    item('@thesephist'),
10    item('@geohot'),
11    item('linuslee'),
12    item('linus is a person'),
13    item('@dlwlrma'),
14];
15
16describe('basic search', () => {
17    it('search empty array', () => {
18        assert.deepEqual(search([], 'query', x => x.name), []);
19    });
20
21    it('search with empty query', () => {
22        assert.deepEqual(search(ITEMS, '', x => x.name), ITEMS);
23    });
24
25    it('search with 1 letter returns correct result', () => {
26        assert.deepEqual(search(ITEMS, 'l', x => x.name), [
27            item('Linus Lee'),
28            item('linuslee'),
29            item('linus is a person'),
30        ]);
31    });
32
33    it('search does not match from middle of words', () => {
34        assert.deepEqual(search(ITEMS, 'w', x => x.name), []);
35    });
36
37    it('multi-word search returns correct result', () => {
38        assert.deepEqual(search(ITEMS, 'linus lee', x => x.name), [
39            item('Linus Lee'),
40        ]);
41    });
42
43    it('searching words out of order returns correct result', () => {
44        assert.deepEqual(search(ITEMS, 'lee linus', x => x.name), [
45            item('Linus Lee'),
46        ]);
47    });
48
49    it('search works even if the last query word is incomplete', () => {
50        assert.deepEqual(search(ITEMS, 'linus le', x => x.name), [
51            item('Linus Lee'),
52        ]);
53    });
54
55    it('search query may contain newlines, tabs, and multiple consecutive spaces', () => {
56        assert.deepEqual(search(ITEMS, '  linus\t is\nperson\t', x => x.name), [
57            item('linus is a person'),
58        ]);
59    });
60
61    it('correctly implements TF-IDF ranking', () => {

In this example, "mango" has much higher IDF (is a higher-signal word) in the corpus than "apple", which appears in nearly every document. Therefore, documents that mention "mango" more times (relative to the length of the document) should rank higher.

66        assert.deepEqual(
67            search([
68                // matches
69                item('mango mango mango apple'),
70                item('mango apple mango apple'),
71                item('apple mango apple mango apple mango apple mango'),
72                item('apple apple apple apple apple apple apple apple mango'),
73                // rejects
74                item('apple apple apple'),
75                item('mango mango mango'),
76                item('applemango'),
77                item('mangoapple'),
78                item('apple 1'),
79                item('apple 2'),
80                item('apple 3'),
81                item('apple 4'),
82                item('apple 5'),
83                item('apple 6'),
84                item('apple 7'),
85                item('apple 8'),
86                item('apple 9'),
87            ], 'apple mango', x => x.name),
88            [
89                item('mango mango mango apple'),
90                item('mango apple mango apple'),
91                item('apple mango apple mango apple mango apple mango'),
92                item('apple apple apple apple apple apple apple apple mango'),
93            ]
94        );
95    });
96});
97
98describe('custom search-by predicates', () => {
99    it('default predicate is provided as x => x', () => {
100        assert.deepEqual(
101            search([
102                'university',
103                'uni of california',
104                'university of california',
105            ], 'uni of cali'),
106            [
107                'uni of california',
108            ]
109        );
110    });
111
112    it('accepts and uses a custom predicate', () => {
113        assert.deepEqual(search(ITEMS, 'sunil ee', x => x.name.split('').reverse().join('')), [
114            item('Linus Lee'),
115        ]);
116    });
117});
118
119describe('search modes', () => {
120    it('in mode: word, search does not match if any words are incomplete', () => {
121        assert.deepEqual(search(ITEMS, 'linu lee', x => x.name, {mode: 'word'}), []);
122    });
123
124    it('in mode: prefix, every query word may be incomplete', () => {
125        assert.deepEqual(search(ITEMS, 'linu le', x => x.name, {mode: 'prefix'}), [
126            item('Linus Lee'),
127        ]);
128    });
129
130    it('in mode: autocomplete, only the last query word may be incomplete', () => {
131        assert.deepEqual(search(ITEMS, 'linus le', x => x.name, {mode: 'autocomplete'}), [
132            item('Linus Lee'),
133        ]);
134        assert.deepEqual(search(ITEMS, 'linu le', x => x.name, {mode: 'autocomplete'}), []);
135    });
136});
137
138describe('case sensitivity', () => {
139    it('caseSensitive: true omits non-matching results', () => {
140        assert.deepEqual(search(ITEMS, 'l', x => x.name, {caseSensitive: true}), [
141            item('linuslee'),
142            item('linus is a person'),
143        ]);
144    });
145
146    it('caseSensitive: false includes case-insensitive results', () => {
147        assert.deepEqual(search(ITEMS, 'l', x => x.name, {caseSensitive: false}), [
148            item('Linus Lee'),
149            item('linuslee'),
150            item('linus is a person'),
151        ]);
152    });
153});
154
155