import test from 'node:test'; import assert from 'node:assert/strict'; import {literalQuery,highlightParts,renderHighlighted,displayExcerpt,matchExplanation} from '../src/match-display.ts'; const marked = (text,query) => highlightParts(text,literalQuery(query)).filter(part => part.matched).map(part => part.text); test('literal marks use whole words, identifiers, quoted phrases and filename separators', () => { assert.deepEqual(marked('Contract contractual subcontract CONTRACT.', 'contract'), ['Contract','CONTRACT']); assert.deepEqual(marked('E_SEARCH_071 E_SEARCH_0712', 'E_SEARCH_071'), ['E_SEARCH_071']); assert.deepEqual(marked('invoice_october.pdf', 'invoice october'), ['invoice','october']); assert.deepEqual(marked('Give end of contract notice. Contract end.', '"end of contract"'), ['end of contract','Contract','end']); assert.deepEqual(marked('Use C++ and literal a+b, not aab.', 'C++ a+b'), ['C++','a+b']); }); test('accent and case matching preserves original combining characters and surrogate offsets', () => { const source='🧾 Café Cafe\u0301 MÜNCHEN München. 𐐀cafe'; const parts=highlightParts(source,literalQuery('cafe munchen')); assert.equal(parts.map(part => part.text).join(''),source); assert.deepEqual(parts.filter(part => part.matched).map(part => part.text), ['Café','Cafe\u0301','MÜNCHEN','München']); assert.equal(parts[0].text, '🧾 '); }); test('common English/German stopwords and punctuation do not create noisy evidence', () => { assert.deepEqual(marked('The and für über supplier', 'the and für'), []); assert.deepEqual(marked('The and. Supplier.', 'the and.'), []); assert.deepEqual(marked('The supplier partnership may end.', 'cancel the supplier partnership'), ['supplier','partnership']); }); test('query snapshots and mark runs are bounded; later inputs cannot relabel older results', () => { let input='supplier'; const snapshot=literalQuery(input); input='garden'; assert.equal(snapshot.query,'supplier'); assert.deepEqual(highlightParts('supplier garden',snapshot).filter(part => part.matched).map(part => part.text),['supplier']); assert.equal(Object.isFrozen(snapshot.patterns),true); assert.ok(literalQuery('x'.repeat(9000)).query.length <= 4096); assert.ok(literalQuery(Array.from({length:100},(_,i)=>`term${i}`).join(' ')).patterns.length <= 64); assert.ok(marked('supplier '.repeat(1000),'supplier').length <= 256); }); test('list excerpts remain original text around the first literal hit and retain ellipsis', () => { const text='Intro '.repeat(200)+'supplier partnership notice '+ 'Closing '.repeat(200); const excerpt=displayExcerpt(text,literalQuery('supplier partnership')); assert.ok(excerpt.length <= 424); assert.ok(excerpt.startsWith('… ')); assert.ok(excerpt.endsWith(' …')); assert.ok(excerpt.includes('supplier partnership')); assert.ok(text.includes(excerpt.slice(2,-2))); }); test('bounded marks retain the earliest hit across terms instead of favoring longest-term repetition', () => { const source='short '+ 'longerterm '.repeat(400); const parts=highlightParts(source,literalQuery('short longerterm')); assert.equal(parts[0].text,'short'); assert.equal(parts[0].matched,true); assert.ok(parts.filter(part=>part.matched).length <= 256); assert.equal(parts.map(part=>part.text).join(''),source); }); test('markup and regex-looking queries create text nodes and marks, never parsed HTML', () => { const nodes=[]; const doc={createDocumentFragment:()=>({append:node=>nodes.push(node)}),createElement:tag=>({tag,dataset:{},textContent:''}),createTextNode:text=>({text})}; const target={ownerDocument:doc,replaceChildren:()=>{}}; const source=' supplier [a-z].'; renderHighlighted(target,source,literalQuery('supplier [a-z]')); assert.equal(nodes.map(node=>node.text ?? node.textContent).join(''),source); assert.ok(nodes.some(node=>node.tag==='mark' && node.textContent==='supplier')); assert.ok(nodes.every(node=>node.tag===undefined || node.tag==='mark')); }); test('optional reasons use real source routes and never claim semantic word confidence', () => { const text=matchExplanation({match_kind:'semantic',source:{kind:'page',number:2}}); assert.match(text.reason,/page 2/); assert.match(text.legend,/without the same words/); assert.equal(text.semanticPassage,true); assert.match(matchExplanation({match_kind:'semantic_audio',source:{kind:'timestamp_ms',number:13129}}).reason,/0:13.*not an exact spoken phrase/); assert.match(matchExplanation({match_kind:'semantic_video',source:{kind:'timestamp_ms',number:24167}}).reason,/frame.*0:24/); assert.match(matchExplanation({match_kind:'semantic_image'}).reason,/whole/); assert.match(matchExplanation({match_kind:'name_terms'}).reason,/filename, path or content/); assert.equal(matchExplanation({match_kind:'hybrid',source:{kind:'page',number:2}}).semanticPassage,false); assert.doesNotMatch(JSON.stringify(text),/confidence|percent|importance/); });