In markdown mode each paragraph line is serialised before parseBlockMD joins them, so $x$ inside a ``` fence became data-mx-maths markup inside the resulting <pre><code> (rendered as math in a code block). Track fence state across lines and skip math for fenced lines and for backtick code spans. Unit tests added; verified in the browser. Also: scripts/dev-homeserver.sh enables MSC4140 delayed events so scheduled messages work locally. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
92 lines
3.4 KiB
TypeScript
92 lines
3.4 KiB
TypeScript
import { test } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { Descendant } from 'slate';
|
|
import { toMatrixCustomHTML } from './output';
|
|
import { BlockType } from './types';
|
|
|
|
// Loose Slate node builders for the test.
|
|
const txt = (text: string, marks: Record<string, unknown> = {}) =>
|
|
({ text, ...marks }) as unknown as Descendant;
|
|
const el = (type: BlockType, children: unknown[]) => ({ type, children }) as unknown as Descendant;
|
|
|
|
const OPTS = {
|
|
allowTextFormatting: true,
|
|
allowInlineMarkdown: false,
|
|
allowBlockMarkdown: false,
|
|
allowMath: true,
|
|
};
|
|
|
|
test('inline $…$ → data-mx-maths span, surrounding prose preserved', () => {
|
|
assert.equal(
|
|
toMatrixCustomHTML(txt('a $x^2$ b'), OPTS),
|
|
'a <span data-mx-maths="x^2"><code>x^2</code></span> b',
|
|
);
|
|
});
|
|
|
|
test('single-line block $$…$$ → data-mx-maths div', () => {
|
|
assert.equal(
|
|
toMatrixCustomHTML(txt('$$E=mc^2$$'), OPTS),
|
|
'<div data-mx-maths="E=mc^2"><code>E=mc^2</code></div>',
|
|
);
|
|
});
|
|
|
|
test('LaTeX special chars are escaped in attribute + fallback', () => {
|
|
assert.equal(
|
|
toMatrixCustomHTML(txt('$a<b$'), OPTS),
|
|
'<span data-mx-maths="a<b"><code>a<b</code></span>',
|
|
);
|
|
});
|
|
|
|
test('math bypasses markdown — underscores are not italicised', () => {
|
|
// Without pre-markdown extraction, `_` would become <em>.
|
|
assert.equal(
|
|
toMatrixCustomHTML(txt('$a_b$'), { ...OPTS, allowInlineMarkdown: true }),
|
|
'<span data-mx-maths="a_b"><code>a_b</code></span>',
|
|
);
|
|
});
|
|
|
|
test('currency ($5 and $10) stays literal', () => {
|
|
assert.equal(toMatrixCustomHTML(txt('$5 and $10'), OPTS), '$5 and $10');
|
|
});
|
|
|
|
test('no math conversion inside an inline code mark', () => {
|
|
const out = toMatrixCustomHTML(txt('$x$', { code: true }), OPTS);
|
|
assert.ok(!out.includes('data-mx-maths'));
|
|
assert.ok(out.includes('<code>$x$</code>'));
|
|
});
|
|
|
|
test('no math conversion inside a code block', () => {
|
|
const block = el(BlockType.CodeBlock, [el(BlockType.CodeLine, [txt('$x$')])]);
|
|
const out = toMatrixCustomHTML(block, OPTS);
|
|
assert.ok(!out.includes('data-mx-maths'));
|
|
assert.ok(out.includes('$x$'));
|
|
});
|
|
|
|
test('a message with no math is unchanged', () => {
|
|
assert.equal(toMatrixCustomHTML(txt('just hello'), OPTS), 'just hello');
|
|
});
|
|
|
|
// Gitea #184 O3 — markdown mode: fences and backtick spans typed as plain
|
|
// paragraphs are literal too (the block markdown parser only sees the fence
|
|
// after the lines are joined, so math must be skipped while serialising them).
|
|
const MD_OPTS = { ...OPTS, allowInlineMarkdown: true, allowBlockMarkdown: true };
|
|
|
|
test('markdown: no math conversion inside a typed ``` fence', () => {
|
|
const paragraphs = [
|
|
el(BlockType.Paragraph, [txt('```')]),
|
|
el(BlockType.Paragraph, [txt('$x$ literal')]),
|
|
el(BlockType.Paragraph, [txt('```')]),
|
|
el(BlockType.Paragraph, [txt('after $y$')]),
|
|
];
|
|
const out = toMatrixCustomHTML(paragraphs, MD_OPTS);
|
|
assert.ok(out.includes('<pre'), 'fence became a code block');
|
|
assert.ok(out.includes('$x$ literal'), 'code block content stays literal');
|
|
assert.ok(out.includes('data-mx-maths="y"'), 'math after the fence still converts');
|
|
});
|
|
|
|
test('markdown: no math conversion inside a backtick span, math outside still converts', () => {
|
|
const out = toMatrixCustomHTML(el(BlockType.Paragraph, [txt('use `$x$` and $y$')]), MD_OPTS);
|
|
assert.ok(/<code[^>]*>\$x\$<\/code>/.test(out), 'backtick span stays literal');
|
|
assert.ok(out.includes('data-mx-maths="y"'));
|
|
});
|