import { describe, expect, it } from 'vitest'; import { EMPTY_ARTICLE_DOCUMENT, parseArticleDocument } from './document'; describe('article documents', () => { it('accepts formatted prose, multiple Builds, and article images', () => { const parsed = parseArticleDocument({ version: 1, document: { type: 'doc', content: [ { type: 'heading', attrs: { level: 2 }, content: [ { type: 'text', text: 'Opening', marks: [{ type: 'bold' }] }, ], }, { type: 'paragraph', content: [ { type: 'text', text: 'Use this rotation.', marks: [ { type: 'link', attrs: { href: 'https://shd.build/guides' } }, ], }, ], }, { type: 'buildEmbed', attrs: { buildId: '11111111-1111-4111-8111-111111111111', embedKey: 'build-11111111-1111-4111-8111-111111111111', }, }, { type: 'buildEmbed', attrs: { buildId: '22222222-2222-4222-8222-222222222222', embedKey: 'build-22222222-2222-4222-8222-222222222222', }, }, { type: 'articleImage', attrs: { imageId: '33333333-3333-4333-8333-333333333333', alt: 'A loadout screen', caption: 'Final configuration', }, }, ], }, }); expect(parsed?.buildEmbeds).toHaveLength(2); expect(parsed?.imageIds).toEqual(['33333333-3333-4333-8333-333333333333']); expect(parsed?.plainText).toContain('Opening Use this rotation.'); }); it('rejects unsafe links and unknown nodes', () => { expect( parseArticleDocument({ version: 1, document: { type: 'doc', content: [ { type: 'paragraph', content: [ { type: 'text', text: 'unsafe', marks: [ { type: 'link', attrs: { href: 'javascript:alert(1)' } }, ], }, ], }, ], }, }), ).toBeNull(); expect( parseArticleDocument({ version: 1, document: { type: 'doc', content: [{ type: 'iframe', attrs: { src: 'https://example.com' } }], }, }), ).toBeNull(); }); it('accepts and canonicalizes metadata emitted by the Tiptap editor', () => { const parsed = parseArticleDocument({ version: 1, document: { type: 'doc', content: [ { type: 'paragraph', content: [ { type: 'text', text: 'Read more', marks: [ { type: 'link', attrs: { href: 'https://shd.build/news', target: '_blank', rel: 'noopener noreferrer nofollow', class: null, title: 'News archive', }, }, ], }, ], }, { type: 'orderedList', attrs: { start: 1, type: null }, content: [ { type: 'listItem', content: [ { type: 'paragraph', content: [{ type: 'text', text: 'First item' }], }, ], }, ], }, ], }, }); expect(parsed?.document.document.content).toEqual([ { type: 'paragraph', content: [ { type: 'text', text: 'Read more', marks: [ { type: 'link', attrs: { href: 'https://shd.build/news', title: 'News archive', }, }, ], }, ], }, { type: 'orderedList', content: [ { type: 'listItem', content: [ { type: 'paragraph', content: [{ type: 'text', text: 'First item' }], }, ], }, ], }, ]); expect(parsed?.plainText).toContain('News archive'); }); it('preserves supported ordered-list numbering', () => { const parsed = parseArticleDocument({ version: 1, document: { type: 'doc', content: [ { type: 'orderedList', attrs: { start: 2, type: 'A' }, content: [ { type: 'listItem', content: [{ type: 'paragraph' }], }, ], }, ], }, }); expect(parsed?.document.document.content?.[0]).toMatchObject({ type: 'orderedList', attrs: { start: 2, type: 'A' }, }); expect(parseArticleDocument(parsed?.document)?.document).toEqual( parsed?.document, ); }); it('rejects unknown link metadata and non-default ordered lists', () => { const documentWith = (content: unknown[]) => ({ version: 1, document: { type: 'doc', content }, }); expect( parseArticleDocument( documentWith([ { type: 'paragraph', content: [ { type: 'text', text: 'unsafe metadata', marks: [ { type: 'link', attrs: { href: 'https://shd.build', onclick: 'alert(1)', }, }, ], }, ], }, ]), ), ).toBeNull(); expect( parseArticleDocument( documentWith([ { type: 'orderedList', attrs: { start: 0, type: 'decimal' }, content: [], }, ]), ), ).toBeNull(); expect( parseArticleDocument( documentWith([ { type: 'orderedList', attrs: { start: 1, type: 1 }, content: [], }, ]), ), ).toBeNull(); }); it('rejects duplicate embed keys and unknown envelope fields', () => { const embed = { type: 'buildEmbed', attrs: { buildId: '11111111-1111-4111-8111-111111111111', embedKey: 'build-11111111-1111-4111-8111-111111111111', }, }; expect( parseArticleDocument({ version: 1, document: { type: 'doc', content: [embed, embed] }, }), ).toBeNull(); expect( parseArticleDocument({ ...EMPTY_ARTICLE_DOCUMENT, legacy: true }), ).toBeNull(); }); });