80 lines
3.2 KiB
JavaScript
80 lines
3.2 KiB
JavaScript
/** @jest-environment jsdom */
|
|
|
|
// Load dependencies used by highlight utils
|
|
require('../sanitizer.js');
|
|
require('../highlight.js');
|
|
|
|
describe('highlightUtils', () => {
|
|
const { buildTokens, highlight, escape: esc, formatSnippet } = window.highlightUtils;
|
|
|
|
test('buildTokens normalizes punctuation, trims non-alphanumerics, and dedupes', () => {
|
|
const tokens = buildTokens(' John, Smith; "Smith" (J.) ');
|
|
// Expect order preserved except deduping
|
|
expect(tokens).toEqual(['John', 'Smith', 'J']);
|
|
const empty = buildTokens(' , ; : ');
|
|
expect(empty).toEqual([]);
|
|
});
|
|
|
|
test('escape encodes special characters safely', () => {
|
|
const out = esc('<div> & "quotes" and \'apostrophes\'');
|
|
expect(out).toContain('<div>');
|
|
expect(out).toContain('&');
|
|
expect(out).toContain('"');
|
|
expect(out).toContain(''');
|
|
expect(esc('Tom & Jerry')).toBe('Tom & Jerry');
|
|
});
|
|
|
|
test('highlight wraps tokens in <strong> and does not break HTML by escaping first', () => {
|
|
const tokens = buildTokens('john smith');
|
|
const result = highlight('Hello <b>John</b> Smith & Sons', tokens);
|
|
// Should escape original tags and then apply strong
|
|
expect(result).toContain('<b>');
|
|
expect(result).toMatch(/<strong>John<\/strong>/i);
|
|
expect(result).toMatch(/<strong>Smith<\/strong>/i);
|
|
// Ampersand must be escaped
|
|
expect(result).toContain('& Sons');
|
|
});
|
|
|
|
test('highlight handles overlapping tokens by sequential replacement', () => {
|
|
const tokens = buildTokens('ann anna');
|
|
const out = highlight('Anna and Ann went', tokens);
|
|
// Both tokens should appear highlighted; order of replacement should not remove prior highlights
|
|
const strongCount = (out.match(/<strong>/g) || []).length;
|
|
expect(strongCount).toBeGreaterThanOrEqual(2);
|
|
expect(out).toMatch(/<strong>Anna<\/strong> and <strong>Ann<\/strong> went/i);
|
|
});
|
|
|
|
test('formatSnippet uses server-provided strong tags if present', () => {
|
|
const tokens = buildTokens('alpha');
|
|
const serverSnippet = 'Value: <strong>Alpha</strong> beta';
|
|
const html = formatSnippet(serverSnippet, tokens);
|
|
// Should preserve strong from server
|
|
expect(html).toContain('<strong>Alpha</strong>');
|
|
// Should be sanitized and not double-escaped
|
|
expect(html).toContain('Value: ');
|
|
});
|
|
|
|
test('formatSnippet applies client-side bold when server snippet is plain text', () => {
|
|
const tokens = buildTokens('delta');
|
|
const plain = 'Gamma delta epsilon';
|
|
const html = formatSnippet(plain, tokens);
|
|
expect(html).toMatch(/Gamma <strong>delta<\/strong> epsilon/i);
|
|
});
|
|
|
|
test('highlight is case-insensitive and preserves original text casing', () => {
|
|
const tokens = buildTokens('joHN smiTH');
|
|
const out = highlight('John Smith', tokens);
|
|
// Must wrap both tokens and preserve the original casing from the source text
|
|
expect(out).toBe('<strong>John</strong> <strong>Smith</strong>');
|
|
});
|
|
|
|
test('formatSnippet highlights with mixed-case query tokens but keeps snippet casing', () => {
|
|
const tokens = buildTokens('doE');
|
|
const html = formatSnippet('Hello Doe', tokens);
|
|
// Exact casing from snippet should be preserved inside <strong>
|
|
expect(html).toContain('Hello <strong>Doe</strong>');
|
|
});
|
|
});
|
|
|
|
|