aboutsummaryrefslogtreecommitdiff
path: root/src/lib/components/fuzzy-search.test.ts
blob: 57d0d1d59e803697626160a19460612a2113c2f4 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

import { describe, it, expect } from 'vitest';
import { matchItems, FUZZ_LIMIT } from './fuzzy-search';
import type { FuzzyItem } from './fuzzy-search';

const catalog: FuzzyItem[] = [
	{ id: 1, name: 'Citron' },
	{ id: 2, name: 'Citrons' },
	{ id: 3, name: 'Concombre' },
	{ id: 4, name: 'Tomate' },
	{ id: 5, name: 'Carotte' },
	{ id: 6, name: 'Lait' },
];

describe('matchItems', () => {
	it('returns nothing for an empty or whitespace query', () => {
		expect(matchItems(catalog, '')).toHaveLength(0);
		expect(matchItems(catalog, '   ')).toHaveLength(0);
	});

	it('matches a fuzzy query, ranking the best match first', () => {
		const res = matchItems(catalog, 'citrn');
		expect(res.length).toBeGreaterThanOrEqual(1);
		// exact-ish run should beat the pluralised variant
		expect(res[0].obj.name).toBe('Citron');
	});

	it('is case-insensitive', () => {
		const res = matchItems(catalog, 'TOMA');
		expect(res[0].obj.name).toBe('Tomate');
	});

	it('keeps matched-across-junk subsequence results', () => {
		const res = matchItems(catalog, 'cb'); // C-o-n-**c**ombre → c arguments
		const names = res.map((r) => r.obj.name);
		expect(names).toContain('Concombre');
	});

	it('respects the limit', () => {
		const many = Array.from({ length: 40 }, (_, i) => ({
			id: i,
			name: `Article ${i}`,
		}));
		const res = matchItems(many, 'article');
		expect(res.length).toBeLessThanOrEqual(FUZZ_LIMIT);
	});

	it('sorts best matches first by descending score', () => {
		const res = matchItems(catalog, 'tomate');
		for (let i = 1; i < res.length; i++) {
			expect(res[i - 1].score).toBeGreaterThanOrEqual(res[i].score);
		}
	});
});