import { describe, it, expect } from 'vitest'; import { stripDangerousTags, isExternalResourceUrl, decodeCssEscapes, stripExternalStyleSheetCss, blockExternalResources, hasRemoteContent, prepareEmailHtml, wrapPlainTextEmail, wrapEmailHtml, hasNativeDarkMode, sniffImageMime, TRANSPARENT_BLOCKED_PIXEL, isUserLinkNavigation, } from '../email-html'; describe('stripDangerousTags', () => { it('keeps
hello
'); expect(blocked).toBe(true); expect(html).not.toContain('https://t'); }); it('keeps CSS-escaped markup inside the scrubbed sheet', () => { const { html } = prepareEmailHtml( 'x
', { blockRemoteImages: true }, ); expect(html).not.toMatch(/z
`; const { html } = prepareEmailHtml(input, { blockRemoteImages: true, isDark: false }); expect(html).not.toMatch(/href="javascript:/i); expect(html).toContain(String.raw`click`); }); it('cuts external URLs out of a sheet and leaves its escapes as written', () => { const css = String.raw`a{background:url(https://x/)} b{background:\5c 75rl(https://y/)} c{background:\75rl(https://z/)} d{content:"\"\3c /style\3e"}`; const { html, blocked } = blockExternalResources(``); expect(blocked).toBe(true); // `\5c 75rl(` is the text `\75rl(`, not a url(): written decoded it // became one. The escaped quote stays escaped. expect(html).toBe(String.raw``); }); it('reads an escape past the last code point as U+FFFD instead of throwing', () => { expect(decodeCssEscapes('\\FFFFFF')).toBe('\ufffd'); expect(() => blockExternalResources('')).not.toThrow(); }); it('finds images the parser reads as
');
expect(blocked).toBe(true);
expect(html).toContain('data-blocked-src="https://t/a.png"');
});
it('reports nothing blocked for a local-only body', () => {
const { html, blocked } = blockExternalResources('hi
hi
'); expect(hasRemoteContent('hi
')).toBe(false); expect(hasRemoteContent('x
', { blockRemoteImages: true }); expect(strict.html).toContain('img-src data: cid:;'); expect(strict.html).toContain("media-src 'none'"); const open = prepareEmailHtml('x
', { blockRemoteImages: false }); expect(open.html).toContain('img-src data: cid: https: http:'); }); // The bridge scripts are injected by the WebView, so the page itself never // needs to run a script or an inline handler. it('runs no script or inline handler from the page in either mode', () => { for (const blockRemoteImages of [true, false]) { expect(prepareEmailHtml('x
', { blockRemoteImages }).html).toContain("script-src 'none'"); } expect(wrapPlainTextEmail('x')).toContain("script-src 'none'"); expect(prepareEmailHtml('x
').html).not.toMatch(/