coaching-fence-attrs.test.ts1.6 KBView on GitHub /**
* The fence body arrives from a `data-*` attribute during ProseMirror's DOM parse, and the
* `recording:` link arrives from agent-authored document content. Both are untrusted, and a throw
* in the first takes down the whole editor rather than one node.
*/
import { decodeFenceAttr, safeHref } from '@/modules/documents/coaching/fence-attrs';
describe('decodeFenceAttr', () => {
it('decodes a normal percent-encoded body', () => {
expect(decodeFenceAttr(encodeURIComponent('deal: Marmon Mok\nverdict: bad'))).toBe(
'deal: Marmon Mok\nverdict: bad',
);
});
it('falls back to the raw value on a malformed sequence instead of throwing', () => {
// `decodeURIComponent('%')` throws a URIError — inside parseHTML that kills the editor.
expect(() => decodeFenceAttr('100%')).not.toThrow();
expect(decodeFenceAttr('100%')).toBe('100%');
});
it('treats a missing attribute as an empty body', () => {
expect(decodeFenceAttr(null)).toBe('');
});
});
describe('safeHref', () => {
it.each([
'https://fathom.video/share/abc',
'http://example.com/rec',
'mailto:<email>',
'/agent?conversationId=abc',
])('passes %s through', (url) => {
expect(safeHref(url)).toBe(url);
});
it.each([
'javascript:alert(1)',
' javascript:alert(1)',
'JavaScript:alert(1)',
'data:text/html,<script>alert(1)</script>',
])('drops %s', (url) => {
expect(safeHref(url)).toBeUndefined();
});
it('drops an empty or missing link', () => {
expect(safeHref(undefined)).toBeUndefined();
expect(safeHref('')).toBeUndefined();
});
});