From 69669476a81e2096b89ff19735750d0ff80a0240 Mon Sep 17 00:00:00 2001 From: Lilleman auf Larv Date: Thu, 5 Nov 2026 12:14:18 +0100 Subject: [PATCH] Split the markdown writer's tests into one file per concern --- src/markdown/emit/adf-to-markdown.test.ts | 761 +-------------------- src/markdown/emit/escaping.test.ts | 148 ++++ src/markdown/emit/inline-nodes.test.ts | 125 ++++ src/markdown/emit/limits.test.ts | 205 ++++++ src/markdown/emit/marks.test.ts | 282 ++++++++ src/markdown/emit/media-and-tables.test.ts | 110 +++ 6 files changed, 871 insertions(+), 760 deletions(-) create mode 100644 src/markdown/emit/escaping.test.ts create mode 100644 src/markdown/emit/inline-nodes.test.ts create mode 100644 src/markdown/emit/limits.test.ts create mode 100644 src/markdown/emit/marks.test.ts create mode 100644 src/markdown/emit/media-and-tables.test.ts diff --git a/src/markdown/emit/adf-to-markdown.test.ts b/src/markdown/emit/adf-to-markdown.test.ts index 84a36bc..1aba145 100644 --- a/src/markdown/emit/adf-to-markdown.test.ts +++ b/src/markdown/emit/adf-to-markdown.test.ts @@ -4,8 +4,7 @@ import test from 'node:test' import type { AdfAttributes, AdfDocument, AdfMark, AdfNode } from '../../adf/document.ts' import type { JsonValue } from '../../json-value.ts' import type { Result } from '../../result.ts' -import { adfToLosslessMarkdown, losslessMarkdownToAdf } from '../../index.ts' -import { largestNesting } from '../../nesting.ts' +import { adfToLosslessMarkdown } from '../../index.ts' function document(...content: AdfNode[]): AdfDocument { return { content, type: 'doc', version: 1 } @@ -15,109 +14,10 @@ function paragraph(...content: AdfNode[]): AdfNode { return content.length === 0 ? { type: 'paragraph' } : { content, type: 'paragraph' } } -function code(result: Result): string { - return result.ok ? `emitted ${JSON.stringify(result.value)}` : result.error.code -} - function markdown(result: Result): string { return result.ok ? result.value : `${result.error.code}: ${result.error.message}` } -function path(result: Result): readonly (number | string)[] { - return result.ok ? ['emitted'] : result.error.path -} - -function position(result: Result): unknown { - return result.ok ? 'emitted' : result.error.position -} - -test('names the node a refusal came from, and no source the emitter never read', () => { - const unspellable: AdfNode = { text: 'x', type: 'paragraph' } - const list: AdfNode = { content: [{ content: [paragraph({ text: 'x', type: 'text' })], type: 'listItem' }, { content: [unspellable], type: 'listItem' }], type: 'bulletList' } - assert.deepEqual(path(adfToLosslessMarkdown(document(paragraph({ text: 'x', type: 'text' }), list))), ['content', 1, 'content', 1, 'content', 0]) - assert.deepEqual(path(adfToLosslessMarkdown(document(paragraph({ text: 'x', type: 'text' }, { text: 'x', type: 'status' })))), ['content', 0, 'content', 1]) - assert.deepEqual(path(adfToLosslessMarkdown({ type: 'doc', version: 2 })), []) - assert.equal(position(adfToLosslessMarkdown(document(paragraph({ text: 'x', type: 'text' }), list))), undefined) -}) - -test('refuses a node whose carry nests too deep ahead of a later node in its line', () => { - let id: JsonValue = 'a' - for (let depth = 0; depth < largestNesting; depth += 1) id = { id } - const twiceStrong: AdfNode = { marks: [{ type: 'strong' }, { type: 'strong' }], text: 'x', type: 'text' } - const refused = adfToLosslessMarkdown(document(paragraph({ attrs: { id }, type: 'mention' }, twiceStrong))) - assert.equal(code(refused), 'unsupported-nesting-depth') - assert.deepEqual(path(refused), ['content', 0, 'content', 0]) -}) - -test('refuses a carried run at the node whose carry nests too deep, among the run\'s own nodes', () => { - let deep: JsonValue = 'a' - for (let depth = 0; depth < largestNesting - 1; depth += 1) deep = { deep } - const carriedEm: AdfNode = { marks: [{ attrs: { deep }, type: 'em' }], text: 'x', type: 'text' } - const twiceStrong: AdfNode = { marks: [{ type: 'strong' }, { type: 'strong' }], text: 'y', type: 'text' } - const refused = adfToLosslessMarkdown(document(paragraph({ text: 'ok', type: 'text' }, carriedEm, twiceStrong))) - assert.equal(code(refused), 'unsupported-nesting-depth') - assert.deepEqual(path(refused), ['content', 0, 'content', 1]) -}) - -test('refuses a value that is not an ADF document, naming the check it failed', () => { - assert.equal(markdown(adfToLosslessMarkdown({ type: 'doc', version: Number.NaN })), 'not-an-adf-document: an ADF document holds a version number: found NaN') -}) - -// The code, and the bound the message names. -function pastTheBound(result: Result): boolean { - return !result.ok && result.error.code === 'unsupported-node-shape' && result.error.message.includes('536870888') -} - -// A code block of 300 lines; two of these together fall some 1000 characters short of V8's longest string, so only what their spelling adds passes it. -function nearlyHalfTheBound(): AdfNode { - return { attrs: {}, content: [{ text: `${'a'.repeat(894_782)}\n`.repeat(300), type: 'text' }], type: 'codeBlock' } -} - -// About 556 million characters of carry JSON: past V8's longest string, short of JavaScriptCore's and SpiderMonkey's. -test('refuses a carry whose JSON would run past V8\'s longest string, on every engine', () => { - let value: JsonValue = new Array(560_000).fill(0) - for (let level = 0; level < 495; level += 1) value = [value] - const result = adfToLosslessMarkdown(document({ attrs: { value }, type: 'unknownBlock' })) - assert.equal(pastTheBound(result), true) -}) - -test('refuses a quote whose prefixed lines would pass V8\'s longest string', () => { - const code = nearlyHalfTheBound() - assert.equal(pastTheBound(adfToLosslessMarkdown(document({ content: [code, code], type: 'blockquote' }))), true) -}) - -// A code block written as its text plus 53 characters; a blank line parts two blocks, and the markdown ends with a line break. -function codeBlockOf(textLength: number): AdfNode { - return { attrs: {}, content: [{ text: `${'a'.repeat(textLength - 1)}\n`, type: 'text' }], type: 'codeBlock' } -} - -test('refuses markdown one character past V8\'s longest string', () => { - const code = codeBlockOf(268_435_390) - assert.equal(pastTheBound(adfToLosslessMarkdown(document(code, code))), true) -}) - -test('writes markdown exactly as long as V8\'s longest string', () => { - const code = codeBlockOf(178_956_908) - const written = adfToLosslessMarkdown(document(code, code, code)) - assert.equal(written.ok && written.value.length, 536870888) -}) - -test('refuses text past V8\'s longest string', () => { - const long = 'a'.repeat(2 ** 28) - const result = adfToLosslessMarkdown(document(paragraph({ text: long, type: 'text' }, { text: long, type: 'text' }, { text: long, type: 'text' }))) - assert.equal(pastTheBound(result), true) -}) - -// Bun stops a test after 5 s by default, and this one writes and reads back a line of 33 million characters. -test('escapes more characters in a line than V8\'s Set holds', { timeout: 60000 }, () => { - const stars = '*'.repeat(2 ** 24 + 1) - assert.equal(markdown(adfToLosslessMarkdown(document(paragraph({ text: stars, type: 'text' })))), `${'\\*'.repeat(2 ** 24 + 1)}\n`) -}) - -test('refuses a document version the markdown cannot carry', () => { - assert.equal(code(adfToLosslessMarkdown({ type: 'doc', version: 2 })), 'unsupported-document-version') -}) - test('carries a text node attribute no spelling holds', () => { assert.equal( markdown(adfToLosslessMarkdown(document(paragraph({ attrs: { localId: 'a' }, text: 'x', type: 'text' })))), @@ -151,91 +51,6 @@ test('spells a code block language no info string holds as an attribute', () => assert.equal(language('a\tb'), '!adf:codeBlock {language="a\\tb"}\n```\n```\n!adf:/codeBlock\n') }) -test('spells a link destination CommonMark cannot as the directive link', () => { - const link = (href: string): string => markdown(adfToLosslessMarkdown(document(paragraph({ marks: [{ attrs: { href }, type: 'link' }], text: 't', type: 'text' })))) - assert.equal(link('https://example.com/a b>c'), '!adf:link[t]{href="https://example.com/a b>c"}\n') - assert.equal(link(' { - const link = (attrs: Record): string => - markdown(adfToLosslessMarkdown(document(paragraph({ marks: [{ attrs, type: 'link' }], text: 't', type: 'text' })))) - assert.equal(link({ collection: 'c', href: 'https://example.com/' }), '!adf:link[t]{collection=c href="https://example.com/"}\n') - assert.equal(link({ href: 'https://example.com/', id: 'i' }), '!adf:link[t]{href="https://example.com/" id=i}\n') - assert.equal(link({ href: 'https://example.com/', occurrenceKey: 'k' }), '!adf:link[t]{href="https://example.com/" occurrenceKey=k}\n') - assert.equal(link({ href: 'https://example.com/', id: 'i', title: 'a\nb' }), '!adf:link[t]{href="https://example.com/" id=i title="a\\nb"}\n') -}) - -test('escapes the parenthesis a link destination leaves unbalanced, and no other', () => { - const link = (href: string): string => markdown(adfToLosslessMarkdown(document(paragraph({ marks: [{ attrs: { href }, type: 'link' }], text: 't', type: 'text' })))) - assert.equal(link('https://en.example.com/a_(b)'), '[t](https://en.example.com/a_(b))\n') - assert.equal(link('https://example.com/a)b'), '[t](https://example.com/a\\)b)\n') - assert.equal(link('https://example.com/a(b'), '[t](https://example.com/a\\(b)\n') - assert.equal(link('https://example.com/)(') , '[t](https://example.com/\\)\\()\n') - assert.equal(link('https://example.com/a (b'), '[t]()\n') -}) - -test('escapes the quote a link title holds, and spells the rest as the directive link', () => { - const titled = (title: string): string => - markdown(adfToLosslessMarkdown(document(paragraph({ marks: [{ attrs: { href: 'https://example.com/', title }, type: 'link' }], text: 't', type: 'text' })))) - assert.equal(titled('He said "hi"'), '[t](https://example.com/ "He said \\"hi\\"")\n') - assert.equal(titled('a\nb'), '!adf:link[t]{href="https://example.com/" title="a\\nb"}\n') - assert.equal(titled('a\\b'), '!adf:link[t]{href="https://example.com/" title="a\\\\b"}\n') - assert.equal(titled('a & b'), '!adf:link[t]{href="https://example.com/" title="a \\u0026amp; b"}\n') -}) - -test('carries a link mark the link spelling cannot write', () => { - const carried = (mark: AdfMark): string => markdown(adfToLosslessMarkdown(document(paragraph({ marks: [mark], text: 't', type: 'text' })))) - assert.equal( - carried({ attrs: { href: 'x', rel: 'y' }, type: 'link' }), - '!adf:carry{json="{\\"marks\\":[{\\"attrs\\":{\\"href\\":\\"x\\",\\"rel\\":\\"y\\"},\\"type\\":\\"link\\"}],\\"text\\":\\"t\\",\\"type\\":\\"text\\"}"}\n', - ) - assert.equal(carried({ attrs: { href: 4 }, type: 'link' }), '!adf:carry{json="{\\"marks\\":[{\\"attrs\\":{\\"href\\":4},\\"type\\":\\"link\\"}],\\"text\\":\\"t\\",\\"type\\":\\"text\\"}"}\n') - assert.equal(carried({ type: 'link' }), '!adf:carry{json="{\\"marks\\":[{\\"type\\":\\"link\\"}],\\"text\\":\\"t\\",\\"type\\":\\"text\\"}"}\n') -}) - -test('carries a mark the canonical spellings cannot nest', () => { - const carried = (...marks: AdfMark[]): string => markdown(adfToLosslessMarkdown(document(paragraph({ marks, text: 'x', type: 'text' })))) - assert.equal(carried({ type: 'annotation' }), '!adf:carry{json="{\\"marks\\":[{\\"type\\":\\"annotation\\"}],\\"text\\":\\"x\\",\\"type\\":\\"text\\"}"}\n') - assert.equal( - carried({ type: 'code' }, { type: 'strong' }), - '!adf:carry{json="{\\"marks\\":[{\\"type\\":\\"code\\"},{\\"type\\":\\"strong\\"}],\\"text\\":\\"x\\",\\"type\\":\\"text\\"}"}\n', - ) - assert.equal( - carried({ attrs: { colour: 'red' }, type: 'em' }), - '!adf:carry{json="{\\"marks\\":[{\\"attrs\\":{\\"colour\\":\\"red\\"},\\"type\\":\\"em\\"}],\\"text\\":\\"x\\",\\"type\\":\\"text\\"}"}\n', - ) - assert.equal( - carried({ attrs: { localId: 'x' }, type: 'code' }), - '!adf:carry{json="{\\"marks\\":[{\\"attrs\\":{\\"localId\\":\\"x\\"},\\"type\\":\\"code\\"}],\\"text\\":\\"x\\",\\"type\\":\\"text\\"}"}\n', - ) -}) - -function readsBack(doc: AdfDocument): string { - const spelled = markdown(adfToLosslessMarkdown(doc)) - assert.deepEqual(losslessMarkdownToAdf(spelled), { ok: true, value: doc }, `reading ${JSON.stringify(spelled)}`) - return spelled -} - -test('spells a code span opening a line, whose backticks open no fence', () => { - const fence: AdfNode = { marks: [{ type: 'code' }], text: '``', type: 'text' } - assert.equal(readsBack(document(paragraph(fence))), '``` `` ```\n') - assert.equal(readsBack(document(paragraph({ text: 'a', type: 'text' }, { type: 'hardBreak' }, fence))), 'a\\\n``` `` ```\n') -}) - -test('escapes the delimiter row a hard break leaves opening a pipe table with no leading pipe', () => { - const broken = (second: string): string => markdown(adfToLosslessMarkdown(document(paragraph({ text: 'a | b', type: 'text' }, { type: 'hardBreak' }, { text: second, type: 'text' })))) - assert.equal(broken('--- | ---'), 'a | b\\\n\\--- | ---\n') - assert.equal(broken(':--- | ---:'), 'a | b\\\n\\:--- | ---:\n') - assert.equal(broken('c | d'), 'a | b\\\nc | d\n') -}) - test('alternates the marker between adjacent lists of a kind, the marker change being what parts them', () => { const list: AdfNode = { content: [{ content: [paragraph({ text: 'x', type: 'text' })], type: 'listItem' }], type: 'bulletList' } assert.equal(markdown(adfToLosslessMarkdown(document(list, list, list))), '- x\n\n* x\n\n- x\n') @@ -273,28 +88,6 @@ test('spells the code block whose language opens with the reserved info string p assert.equal(markdown(adfToLosslessMarkdown(document({ attrs: { language: 'adf' }, type: 'codeBlock' }))), '```adf\n```\n') }) -test('breaks a mark run at the node it carries', () => { - const strong: AdfMark = { type: 'strong' } - const carried: AdfNode = { marks: [strong], type: 'placeholder' } - assert.equal( - markdown(adfToLosslessMarkdown(document(paragraph({ marks: [strong], text: 'a', type: 'text' }, carried, { marks: [strong], text: 'b', type: 'text' })))), - '**a**!adf:carry{json="{\\"marks\\":[{\\"type\\":\\"strong\\"}],\\"type\\":\\"placeholder\\"}"}**b**\n', - ) -}) - -test('refuses a carried node nested deeper than the levels its position leaves', () => { - let node: AdfNode = { type: 'blockCard' } - for (let depth = 0; depth < 600; depth += 1) node = { content: [node], type: 'blockCard' } - assert.equal(code(adfToLosslessMarkdown(document(node))), 'unsupported-nesting-depth') - assert.equal(code(adfToLosslessMarkdown(document(paragraph(node)))), 'unsupported-nesting-depth') - let shallow: AdfNode = { type: 'blockCard' } - for (let depth = 0; depth < 200; depth += 1) shallow = { content: [shallow], type: 'blockCard' } - assert.ok(adfToLosslessMarkdown(document(shallow)).ok) - let quoted: AdfNode = shallow - for (let depth = 0; depth < 150; depth += 1) quoted = { content: [quoted], type: 'blockquote' } - assert.equal(code(adfToLosslessMarkdown(document(quoted))), 'unsupported-nesting-depth') -}) - test('carries a code block holding a node no fence holds', () => { assert.equal(markdown(adfToLosslessMarkdown(document({ content: [paragraph()], type: 'codeBlock' }))), '```adf:codeBlock\n{\n "content": [\n {\n "type": "paragraph"\n }\n ]\n}\n```\n') for (const child of [{ attrs: {}, text: 'x', type: 'text' }, { content: [], text: 'x', type: 'text' }, { marks: [], text: 'x', type: 'text' }, { content: [{ text: 'lost', type: 'text' }], text: 'x', type: 'text' }]) { @@ -317,173 +110,11 @@ test('spells an ordered list no marker fits as a directive', () => { assert.equal(markdown(adfToLosslessMarkdown(list(999999999, 2))), '!adf:orderedList {order=999999999}\n!adf:listItem\nx\n!adf:/listItem\n!adf:listItem\nx\n!adf:/listItem\n!adf:/orderedList\n') }) -test('carries a code mark over anything but text', () => { - assert.equal( - markdown(adfToLosslessMarkdown(document(paragraph({ marks: [{ type: 'code' }], type: 'hardBreak' })))), - '!adf:carry{json="{\\"marks\\":[{\\"type\\":\\"code\\"}],\\"type\\":\\"hardBreak\\"}"}\n', - ) -}) - -test('carries every inline node of a line that only the carry spells', () => { - const odd: AdfNode = { attrs: { id: 'a', odd: 'b' }, type: 'mention' } - const carriedOdd = '!adf:carry{json="{\\"attrs\\":{\\"id\\":\\"a\\",\\"odd\\":\\"b\\"},\\"type\\":\\"mention\\"}"}' - const unlinked = '!adf:carry{json="{\\"marks\\":[{\\"type\\":\\"link\\"}],\\"text\\":\\"x\\",\\"type\\":\\"text\\"}"}' - assert.equal( - markdown(adfToLosslessMarkdown(document(paragraph(odd, { text: ' ', type: 'text' }, { marks: [{ type: 'link' }], text: 'x', type: 'text' }, { text: ' ', type: 'text' }, odd)))), - `${carriedOdd} ${unlinked} ${carriedOdd}\n`, - ) - assert.equal( - markdown(adfToLosslessMarkdown(document(paragraph({ text: 'a', type: 'text' }, { attrs: { foo: 1 }, type: 'hardBreak' }, { text: 'b', type: 'text' })))), - 'a!adf:carry{json="{\\"attrs\\":{\\"foo\\":1},\\"type\\":\\"hardBreak\\"}"}b\n', - ) -}) - -test('carries an inline node holding a mark Atlassian\'s schema withholds from it', () => { - const carried = (node: AdfNode): string => markdown(adfToLosslessMarkdown(document(paragraph(node)))) - assert.equal(carried({ attrs: { id: 'b1c2', text: '@A' }, marks: [{ type: 'strong' }], type: 'mention' }), '!adf:carry{json="{\\"attrs\\":{\\"id\\":\\"b1c2\\",\\"text\\":\\"@A\\"},\\"marks\\":[{\\"type\\":\\"strong\\"}],\\"type\\":\\"mention\\"}"}\n') - assert.equal(carried({ attrs: { id: 'm1' }, marks: [{ type: 'em' }], type: 'mediaInline' }), '!adf:carry{json="{\\"attrs\\":{\\"id\\":\\"m1\\"},\\"marks\\":[{\\"type\\":\\"em\\"}],\\"type\\":\\"mediaInline\\"}"}\n') - assert.equal(carried({ attrs: { id: 'm1' }, marks: [{ attrs: { href: '/u' }, type: 'link' }], type: 'mediaInline' }), '[!adf:mediaInline{id=m1}](/u)\n') - assert.equal(carried({ marks: [{ type: 'strong' }, { type: 'code' }], text: 'x', type: 'text' }), '!adf:carry{json="{\\"marks\\":[{\\"type\\":\\"strong\\"},{\\"type\\":\\"code\\"}],\\"text\\":\\"x\\",\\"type\\":\\"text\\"}"}\n') - assert.equal(carried({ marks: [{ attrs: { href: '/u' }, type: 'link' }, { type: 'code' }], text: 'x', type: 'text' }), '[`x`](/u)\n') - const border: AdfMark = { attrs: { color: '#091e42', size: 2 }, type: 'border' } - const bordered = '!adf:carry{json="{\\"marks\\":[{\\"attrs\\":{\\"color\\":\\"#091e42\\",\\"size\\":2},\\"type\\":\\"border\\"}],\\"text\\":\\"x\\",\\"type\\":\\"text\\"}"}' - assert.equal(carried({ marks: [border], text: 'x', type: 'text' }), `${bordered}\n`) - assert.equal(markdown(adfToLosslessMarkdown(document(paragraph({ marks: [{ type: 'em' }], text: 'a', type: 'text' }, { marks: [border], text: 'x', type: 'text' })))), `_a_${bordered}\n`) -}) - test('spells a heading level no ATX heading fits as a directive', () => { assert.equal(markdown(adfToLosslessMarkdown(document({ attrs: { level: 7 }, content: [{ text: 'x', type: 'text' }], type: 'heading' }))), '!adf:heading {level=7}\nx\n!adf:/heading\n') assert.equal(markdown(adfToLosslessMarkdown(document({ content: [{ text: 'x', type: 'text' }], type: 'heading' }))), '!adf:heading\nx\n!adf:/heading\n') }) -test('escapes only text that would otherwise open a construct', () => { - const emitted = (text: string): string => markdown(adfToLosslessMarkdown(document(paragraph({ text, type: 'text' })))) - assert.equal(emitted('
'), '\\
\n') - assert.equal(emitted(' e'), 'a \\ e\n') - assert.equal(emitted('a c'), 'a \\ c\n') - const later = paragraph({ text: 'a', type: 'text' }, { type: 'hardBreak' }, { text: ' \\ c'), 'a \\ c\n') + const later = paragraph({ text: 'a', type: 'text' }, { type: 'hardBreak' }, { text: ' \\