3
0
mirror of https://github.com/larvit/adf-codec.git synced 2026-11-12 11:48:29 +01:00

Answer stability round 1: 0 🔴, 5 🟡 (0 misread); each fact list now tells its refusal apart, the HTML table pins the rule and escape, the duplicate proxy check dropped

This commit is contained in:
2026-11-10 22:58:24 +01:00
parent af0ff74566
commit faae4a77fd
3 changed files with 49 additions and 50 deletions
-1
View File
@@ -301,7 +301,6 @@ test('refuses a Proxy that throws on an array, and a revoked one, wherever they
refused(readAdfDocument({ content: throwing([], 'get'), type: 'doc', version: 1 }), 'not-an-adf-document', ...unreadable)
const revoked = Proxy.revocable({}, {})
revoked.revoke()
refused(readAdfDocument(revoked.proxy), 'not-an-adf-document', ...unreadable)
refused(readAdfDocument(documentOf(revoked.proxy)), 'not-an-adf-document', ...unreadable)
const read = readAdfDocument(revoked.proxy)
refused(read, 'not-an-adf-document', ...unreadable)
+42 -42
View File
@@ -490,49 +490,49 @@ test('refuses a tag of three million attributes, past what one pattern over them
})
const messages: readonly (readonly [string, readonly string[]])[] = [
['<p>a<b>b</b></p>\n', ['<b>', 'found "<b>b</b></p>\\n"']],
['<p>a > b</p>\n', ['& as &amp;', '< as &lt;', 'found "> b</p>\\n"']],
['<p><a href="a"b">a</a></p>\n', ['<a>', '& as &amp;', '" as &quot;', 'found "b\\">a</a></p>\\n"']],
['<p>&b</p>\n', ['& as &amp;', '< as &lt;', 'found "&b</p>\\n"']],
['<pre><code>a&b</code></pre>\n', ['& as &amp;', '< as &lt;', 'found "&b</code></pre>\\n"']],
['<p>a&nbsp</p>\n', ['& as &amp;', '< as &lt;', 'found "&nbsp</p>\\n"']],
['<pre><code>a</code></pre\n', ['found "</code></pre\\n"']],
['<pre><code class="hljs language-js">x</code></pre>\n', ['<code>', 'found "<code class=\\"hljs langua"…']],
['<ul>\n<li>a</ul>\n', ['< as &lt;', 'found "</ul>\\n"']],
['<ul>\n<li>a<b>b</b></li>\n</ul>\n', ['<b>', 'found "<b>b</b></li>\\n</ul>\\n"']],
['<ul>\n<li>if a < b</li>\n</ul>\n', ['< as &lt;', 'found "< b</li>\\n</ul>\\n"']],
['<ul>\n<li>see <a href="https://x.test/?a=1&b=2">x</a></li>\n</ul>\n', ['<a>', '& as &amp;', '" as &quot;', 'found "&b=2\\">x</a></li>\\n</ul>\\n"']],
['<ul>\n<li><p>a</p><strong>x</strong></li>\n</ul>\n', ['found "<strong>x</strong></li>\\n"…']],
['<ul>\n<li>Bolt<ul>\n<li>a</li>\n</ul></li>\n</ul>\n', ['found "<ul>\\n<li>a</li>\\n</ul></l"…']],
['<ul>\n<li>a<li>b</li>\n</ul>\n', ['found "<li>b</li>\\n</ul>\\n"']],
['<ul>\n<li>a\n<li>b</li>\n</ul>\n', ['found "<li>b</li>\\n</ul>\\n"']],
['<p><strong><b>x</b></strong></p>\n', ['<b>', 'found "<b>x</b></strong></p>\\n"']],
['<p><strong><br>x</strong></p>\n', ['<br>', 'found "<br>x</strong></p>\\n"']],
['<p><em></em></p>\n', ['found "</em></p>\\n"']],
['<p><strong><a href="https://x.test/?a=1&b=2">x</a></strong></p>\n', ['<a>', '& as &amp;', '" as &quot;', 'found "&b=2\\">x</a></strong></p>"…']],
[`<p>a</p>\n${carry('div', panel, '<p>Check the <b>collation</b>.</p>')}\n`, ['to change the node, edit its ADF and write the HTML again', 'found "<b>collation</b>.</p></d"…']],
['<p><span>a</span></p>\n', ['found "<span>a</span></p>\\n"']],
['<div class="adf-carry" data-adf="{ &quot;type&quot;:&quot;x&quot;}"></div>\n', ['found "{ &quot;type&quot;:&quot"…']],
[`${documentCarry}\n<hr>\n`, ['found "\\n<hr>\\n"']],
[`<hr>\n${documentCarry}\n`, ['found "<div class=\\"adf-carry\\" d"…']],
['<ul>\n<li><p>Bolt</p></li>\n</ul>\n', ['found "<p>Bolt</p></li>\\n</ul>\\n"']],
['<ul>\n<li><p></p></li>\n</ul>\n', ['found "<p></p></li>\\n</ul>\\n"']],
['<ul>\n<li>\n<p data-local-id="x"></p>\n</li>\n</ul>\n', ['found "<p data-local-id=\\"x\\"></p"…']],
['<p><img src="a">b</p>\n', ['found "b</p>\\n"']],
['<p><img src="a" alt=""></p>\n', ['found "<img src=\\"a\\" alt=\\"\\"></p>"…']],
['<ol start="03">\n<li>a</li>\n</ol>\n', ['found "03\\">\\n<li>a</li>\\n</ol>\\n"']],
['<p class="x">a</p>\n', ['<p>', 'takes no class attribute']],
['<p data-color="x">a</p>\n', ['<p>', 'takes no data-color attribute']],
['<ol data-local-id="x" start="2">\n<li>a</li>\n</ol>\n', ['<ol>', 'found start after data-local-id']],
['<ol start="2" start="3">\n<li>a</li>\n</ol>\n', ['<ol>', 'found start after start']],
['<ol start="2" data-bogus="1" start="3">\n<li>a</li>\n</ol>\n', ['<ol>', 'takes no data-bogus attribute']],
['<p>a<b>b</b></p>\n', ['<b> as written here is no inline element of the dialect', 'found "<b>b</b></p>\\n"']],
['<p>a > b</p>\n', ['text escapes & as &amp;', '> as &gt;', 'found "> b</p>\\n"']],
['<p><a href="a"b">a</a></p>\n', ['<a> holds its attributes as name="value"', '" as &quot;', 'found "b\\">a</a></p>\\n"']],
['<p>&b</p>\n', ['text escapes & as &amp;', 'found "&b</p>\\n"']],
['<pre><code>a&b</code></pre>\n', ['text escapes & as &amp;', 'found "&b</code></pre>\\n"']],
['<p>a&nbsp</p>\n', ['text escapes & as &amp;', 'found "&nbsp</p>\\n"']],
['<pre><code>a</code></pre\n', ['a code block\'s text ends at </code></pre>', 'found "</code></pre\\n"']],
['<pre><code class="hljs language-js">x</code></pre>\n', ['<code> as written here is no code element of the dialect', 'found "<code class=\\"hljs langua"…']],
['<ul>\n<li>a</ul>\n', ['the line ends at </li>', '< as &lt;', 'found "</ul>\\n"']],
['<ul>\n<li>a<b>b</b></li>\n</ul>\n', ['<b> as written here is no inline element of the dialect', 'found "<b>b</b></li>\\n</ul>\\n"']],
['<ul>\n<li>if a < b</li>\n</ul>\n', ['the line ends at </li>', '< as &lt;', 'found "< b</li>\\n</ul>\\n"']],
['<ul>\n<li>see <a href="https://x.test/?a=1&b=2">x</a></li>\n</ul>\n', ['<a> holds its attributes as name="value"', '& as &amp;', '" as &quot;', 'found "&b=2\\">x</a></li>\\n</ul>\\n"']],
['<ul>\n<li><p>a</p><strong>x</strong></li>\n</ul>\n', ['the line ends at </li>', 'found "<strong>x</strong></li>\\n"…']],
['<ul>\n<li>Bolt<ul>\n<li>a</li>\n</ul></li>\n</ul>\n', ['the line ends at </li>', 'found "<ul>\\n<li>a</li>\\n</ul></l"…']],
['<ul>\n<li>a<li>b</li>\n</ul>\n', ['the line ends at </li>', 'found "<li>b</li>\\n</ul>\\n"']],
['<ul>\n<li>a\n<li>b</li>\n</ul>\n', ['the line ends at </li>', 'found "<li>b</li>\\n</ul>\\n"']],
['<p><strong><b>x</b></strong></p>\n', ['<b> as written here is no mark element of the dialect', 'found "<b>x</b></strong></p>\\n"']],
['<p><strong><br>x</strong></p>\n', ['<br> as written here is no mark element of the dialect', 'found "<br>x</strong></p>\\n"']],
['<p><em></em></p>\n', ['a mark element holds text', 'found "</em></p>\\n"']],
['<p><strong><a href="https://x.test/?a=1&b=2">x</a></strong></p>\n', ['<a> holds its attributes as name="value"', '& as &amp;', '" as &quot;', 'found "&b=2\\">x</a></strong></p>"…']],
[`<p>a</p>\n${carry('div', panel, '<p>Check the <b>collation</b>.</p>')}\n`, ['a carry element shows what adfToHtml writes for its node', 'to change the node, edit its ADF and write the HTML again', 'found "<b>collation</b>.</p></d"…']],
['<p><span>a</span></p>\n', ['a <span> of the dialect is a carry element', 'found "<span>a</span></p>\\n"']],
['<div class="adf-carry" data-adf="{ &quot;type&quot;:&quot;x&quot;}"></div>\n', ['a carry element spells its node\'s JSON canonically', 'found "{ &quot;type&quot;:&quot"…']],
[`${documentCarry}\n<hr>\n`, ['a carry element holding the document stands alone', 'found "\\n<hr>\\n"']],
[`<hr>\n${documentCarry}\n`, ['a carry element holding the document stands alone', 'found "<div class=\\"adf-carry\\" d"…']],
['<ul>\n<li><p>Bolt</p></li>\n</ul>\n', ['an item\'s or cell\'s first paragraph holding content and no attribute stands bare on the line of the item\'s or cell\'s start tag', 'found "<p>Bolt</p></li>\\n</ul>\\n"']],
['<ul>\n<li><p></p></li>\n</ul>\n', ['an item\'s or cell\'s first paragraph holding neither content nor an attribute stands on a line of its own', 'found "<p></p></li>\\n</ul>\\n"']],
['<ul>\n<li>\n<p data-local-id="x"></p>\n</li>\n</ul>\n', ['an item\'s or cell\'s first paragraph holding an attribute stands as its <p> straight after the item\'s or cell\'s start tag', 'found "<p data-local-id=\\"x\\"></p"…']],
['<p><img src="a">b</p>\n', ['an image stands alone in its paragraph as <img src="…" alt="…">', 'found "b</p>\\n"']],
['<p><img src="a" alt=""></p>\n', ['an image stands alone in its paragraph as <img src="…" alt="…">', 'found "<img src=\\"a\\" alt=\\"\\"></p>"…']],
['<ol start="03">\n<li>a</li>\n</ol>\n', ['an ordered list starts at an integer', 'found "03\\">\\n<li>a</li>\\n</ol>\\n"']],
['<p class="x">a</p>\n', ['<p>', 'no class attribute']],
['<p data-color="x">a</p>\n', ['<p>', 'no data-color attribute']],
['<ol data-local-id="x" start="2">\n<li>a</li>\n</ol>\n', ['<ol> takes start once', 'found start after data-local-id']],
['<ol start="2" start="3">\n<li>a</li>\n</ol>\n', ['<ol> takes start once', 'found start after start']],
['<ol start="2" data-bogus="1" start="3">\n<li>a</li>\n</ol>\n', ['<ol>', 'no data-bogus attribute']],
['<pre data-wrap="yes"><code>x</code></pre>\n', ['data-wrap', 'boolean']],
['<p><span class="adf-emoji"></span></p>\n', ['<span class="adf-emoji">', 'found "</span></p>\\n"']],
['<p><a class="adf-inline-card" href="https://example.com/">Spec</a></p>\n', ['<a class="adf-inline-card">', 'to change the card, change them together', 'found "Spec</a></p>\\n"']],
[`<p><a class="adf-inline-card" href="https://example.org/plan" data-data="${planData}">Plan</a></p>\n`, ['<a class="adf-inline-card">', 'to change the card, change them together', 'found "Plan</a></p>\\n"']],
['<p><time class="adf-date" datetime="2025-08-26" data-timestamp="1756080000000">x</time></p>\n', ['<time class="adf-date">', 'to change the date, change all three', 'found "x</time></p>\\n"']],
['<p><span class="adf-emoji" data-size="big">a</span></p>\n', ['<span class="adf-emoji">', 'takes no data-size attribute']],
['<p><span class="adf-status">a&b</span></p>\n', ['& as &amp;', '< as &lt;', 'found "&b</span></p>\\n"']],
['<p><span class="adf-emoji"></span></p>\n', ['<span class="adf-emoji"> holds its text', 'found "</span></p>\\n"']],
['<p><a class="adf-inline-card" href="https://example.com/">Spec</a></p>\n', ['<a class="adf-inline-card"> holds one URL', 'to change the card, change them together', 'found "Spec</a></p>\\n"']],
[`<p><a class="adf-inline-card" href="https://example.org/plan" data-data="${planData}">Plan</a></p>\n`, ['<a class="adf-inline-card"> holds one URL', 'to change the card, change them together', 'found "Plan</a></p>\\n"']],
['<p><time class="adf-date" datetime="2025-08-26" data-timestamp="1756080000000">x</time></p>\n', ['<time class="adf-date"> shows the day its data-timestamp names', 'to change the date, change all three', 'found "x</time></p>\\n"']],
['<p><span class="adf-emoji" data-size="big">a</span></p>\n', ['<span class="adf-emoji">', 'no data-size attribute']],
['<p><span class="adf-status">a&b</span></p>\n', ['text escapes & as &amp;', 'found "&b</span></p>\\n"']],
]
for (const [html, facts] of messages) test(`names what it found where reading stopped, a character no one sees as its escape: ${JSON.stringify(html)}`, () => names(htmlToAdf(html), ...facts))
+7 -7
View File
@@ -139,7 +139,7 @@ test('builds an empty document from input holding no block, and one holding no c
assert.deepEqual(losslessMarkdownToAdf('\n \n\t\n'), { ok: true, value: { content: [], type: 'doc', version: 1 } })
assert.deepEqual(losslessMarkdownToAdf('!adf:doc {content=none}\n'), { ok: true, value: { type: 'doc', version: 1 } })
assert.deepEqual(losslessMarkdownToAdf('\n!adf:doc {content=none}\n\n'), { ok: true, value: { type: 'doc', version: 1 } })
const form = ['!adf:doc {content=none}']
const form = ['!adf:doc {content=none}', 'spells another']
refused(losslessMarkdownToAdf('!adf:doc\n'), 'unsupported-node-shape', ...form)
refused(losslessMarkdownToAdf('!adf:doc x {content=none}\n'), 'unsupported-node-shape', ...form)
refused(losslessMarkdownToAdf('!adf:doc {content=empty}\n'), 'unsupported-node-shape', 'empty markdown', '!adf:doc {content=none}', 'content=empty')
@@ -147,7 +147,7 @@ test('builds an empty document from input holding no block, and one holding no c
const alone = ['delete the !adf:doc {content=none} line', 'whole document']
refused(losslessMarkdownToAdf('x\n\n!adf:doc {content=none}\n'), 'unsupported-node-shape', ...alone)
refused(losslessMarkdownToAdf('!adf:panel info\n!adf:doc {content=none}\n!adf:/panel\n'), 'unsupported-node-shape', ...alone)
refused(losslessMarkdownToAdf('!adf:doc\n!adf:/doc\n'), 'malformed-directive', '!adf:/doc', '\\!adf:')
refused(losslessMarkdownToAdf('!adf:doc\n!adf:/doc\n'), 'malformed-directive', 'takes no body', '!adf:/doc', '\\!adf:')
refused(losslessMarkdownToAdf('a !adf:doc{content=none}\n'), 'unsupported-node-shape', '!adf:doc', 'inline form')
})
@@ -248,8 +248,8 @@ test('names the pipe table a claimed line does not spell', () => {
refused(losslessMarkdownToAdf('| a | b |\n'), 'malformed-pipe-table', '`-`', '\\|')
refused(losslessMarkdownToAdf('| a |\n| x |\n'), 'malformed-pipe-table', '`-`', '\\|')
refused(losslessMarkdownToAdf('| a | b |\n| :--- | ---: |\n'), 'malformed-pipe-table', 'alignment colon')
refused(losslessMarkdownToAdf('| a | b |\n| --- |\n'), 'malformed-pipe-table', '1 cell', '2 cells')
refused(losslessMarkdownToAdf('| a |\n| --- |\n| b | c |\n'), 'malformed-pipe-table', '2 cells', '1 cell')
refused(losslessMarkdownToAdf('| a | b |\n| --- |\n'), 'malformed-pipe-table', 'row holds 1 cell', 'header holds 2 cells')
refused(losslessMarkdownToAdf('| a |\n| --- |\n| b | c |\n'), 'malformed-pipe-table', 'row holds 2 cells', 'header holds 1 cell')
assert.deepEqual(path(losslessMarkdownToAdf('Part.\n\n| a |\n')), ['content', 1])
})
@@ -261,7 +261,7 @@ test('names the pipe table whose rows open with no pipe', () => {
refused(losslessMarkdownToAdf('a | b\n:--- | ---:\n'), 'malformed-pipe-table', ...bare)
refused(losslessMarkdownToAdf('a |\n--- |\n'), 'malformed-pipe-table', ...bare)
assert.deepEqual(content(losslessMarkdownToAdf('| a | b |\n| --- | --- |\nc\n')), [table(row(cell('tableHeader', text('a')), cell('tableHeader', text('b')))), paragraph('c')])
refused(losslessMarkdownToAdf('| a | b |\n| --- | --- |\n| c |\n'), 'malformed-pipe-table', '1 cell', '2 cells')
refused(losslessMarkdownToAdf('| a | b |\n| --- | --- |\n| c |\n'), 'malformed-pipe-table', 'row holds 1 cell', 'header holds 2 cells')
assert.deepEqual(content(losslessMarkdownToAdf('a | b\nc | d\n')), [paragraph('a | b c | d')])
assert.deepEqual(content(losslessMarkdownToAdf('a | b\n--- | --- | ---\n')), [paragraph('a | b --- | --- | ---')])
assert.deepEqual(content(losslessMarkdownToAdf('a \\| b\n--- | ---\n')), [paragraph('a | b --- | ---')])
@@ -314,7 +314,7 @@ test('claims a block line the prefix opens with no directive to parse it', () =>
assert.equal(code(losslessMarkdownToAdf('!adf:/panel\n')), 'malformed-directive')
assert.equal(code(losslessMarkdownToAdf('!adf:panel info\n')), 'malformed-directive')
assert.equal(code(losslessMarkdownToAdf('!adf:panel {a=1 a=2}\n!adf:/panel\n')), 'malformed-directive')
refused(losslessMarkdownToAdf('!adf:Panel\n'), 'malformed-directive', '\\!adf:')
refused(losslessMarkdownToAdf('!adf:Panel\n'), 'malformed-directive', 'no directive', '\\!adf:')
assert.deepEqual(content(losslessMarkdownToAdf(':::panel info\nPart.\n:::\n')), [paragraph(':::panel info Part. :::')])
assert.deepEqual(content(losslessMarkdownToAdf('::rule\n')), [paragraph('::rule')])
})
@@ -385,7 +385,7 @@ test('names the closer that finds no container open where it stands', () => {
})
test('names the leaf given a body at its opener, ahead of any refusal the leaf holds itself', () => {
refused(losslessMarkdownToAdf('!adf:rule\nPart.\n!adf:/rule\n'), 'malformed-directive', '!adf:/rule', '\\!adf:')
refused(losslessMarkdownToAdf('!adf:rule\nPart.\n!adf:/rule\n'), 'malformed-directive', 'takes no body', '!adf:/rule', '\\!adf:')
assert.deepEqual(position(losslessMarkdownToAdf('Part.\n\n!adf:rule\nPart.\n!adf:/rule\n')), { line: 3, offset: 7 })
assert.deepEqual(path(losslessMarkdownToAdf('!adf:rule {localId=a-1}\n!adf:rule {localId=a-2}\nPart.\n!adf:/rule\n')), ['content', 1])
assert.deepEqual(path(losslessMarkdownToAdf('!adf:rule {localId=a-1}\n!adf:/rule\n!adf:/rule\n')), ['content', 0])