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); } }); });