Core CI / core (macos-latest) (push) Canceled after 0s
Core CI / core (windows-latest) (push) Canceled after 0s
Core CI / desktop (macos-latest) (push) Canceled after 0s
Core CI / desktop (ubuntu-latest) (push) Canceled after 0s
Core CI / desktop (windows-latest) (push) Canceled after 0s
Core CI / core (ubuntu-latest) (push) Canceled after 2m23s
88 lines
5.2 KiB
JavaScript
88 lines
5.2 KiB
JavaScript
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='<img src=x onerror=evil()> 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/);
|
|
});
|
|
|
|
test('code passage reasons retain actual indexed source line', () => {
|
|
assert.match(matchExplanation({match_kind:'semantic', source:{kind:'line',number:97}}).reason, /at line 97/);
|
|
assert.match(matchExplanation({match_kind:'hybrid', source:{kind:'line',number:97}}).reason, /showing the passage at line 97/);
|
|
});
|