import { describe, expect, it } from 'vitest'; import { extractText, plainExcerpt } from './extractText'; describe('extractText', () => { it('returns empty string for null/undefined/empty', () => { expect(extractText(null)).toBe(''); expect(extractText(undefined)).toBe(''); expect(extractText('')).toBe(''); }); it('strips tags and preserves visible text', () => { expect(extractText('

Hello world

')).toBe('Hello world'); }); it('collapses whitespace within and between blocks', () => { expect(extractText('

One

Two

')).toBe('OneTwo'); expect(extractText('

foo bar

')).toBe('foo bar'); }); // XSS-shaped inputs: extractText must NOT execute, render, or expose the // payload as HTML. It is only required to return *some* string. The fact // that it exists is documented as a non-sanitiser; these tests prevent // silent regressions where the function might somehow leak a tag. describe('XSS-shaped input — never re-emits markup, even though this is not a sanitiser', () => { it('drops '); expect(out).not.toContain(''); }); it('drops markup', () => { const out = extractText(''); expect(out).not.toContain(' markup', () => { const out = extractText(' payload (security regression)', () => { // plainExcerpt calls extractText which regex-strips tags in Node (no DOMParser). // SvelteKit SSR auto-escapes the result, so onerror= in output is the first-paint risk. const out = plainExcerpt(''); expect(out).not.toContain('onerror='); }); }); describe('plainExcerpt', () => { it('returns full text when under the limit', () => { expect(plainExcerpt('

short

', 80)).toBe('short'); }); it('truncates at the boundary with an ellipsis', () => { const html = '

' + 'a'.repeat(100) + '

'; const out = plainExcerpt(html, 20); expect(out.length).toBeLessThanOrEqual(21); expect(out.endsWith('…')).toBe(true); }); it('breaks at a word boundary when possible', () => { const out = plainExcerpt('

The quick brown fox jumps over

', 18); expect(out).toBe('The quick brown…'); }); });