""" Regression tests for #1154 — fenced code block content leaking into markdown passes and corrupting subsequent message rendering. Root cause: fenced code blocks were rendered to
early in
the pipeline, BEFORE list/heading/table regexes ran. Lines inside
code blocks that looked like markdown (e.g. `- removed`, `+ added`
in diff blocks) were matched by those regexes, injecting /-
HTML inside
, breaking
closure and corrupting layout.
Fix: fenced blocks are converted to HTML inside the stash
callback and kept as \\x00P tokens until AFTER all markdown passes.
"""
import re
import shutil
import pytest
REPO_ROOT = __import__("pathlib").Path(__file__).parent.parent.resolve()
UI_JS_PATH = REPO_ROOT / "static" / "ui.js"
NODE = shutil.which("node")
pytestmark = pytest.mark.skipif(NODE is None, reason="node not on PATH")
_DRIVER_SRC = r"""
const fs = require('fs');
const src = fs.readFileSync(process.argv[2], 'utf8');
global.window = {};
global.document = { createElement: () => ({ innerHTML: '', textContent: '' }) };
const esc = s => String(s ?? '').replace(/[&<>\"']/g, c => (
{'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]));
function extractFunc(name) {
const re = new RegExp('function\\s+' + name + '\\s*\\(');
const start = src.search(re);
if (start < 0) throw new Error(name + ' not found');
let i = src.indexOf('{', start);
let depth = 1; i++;
while (depth > 0 && i < src.length) {
if (src[i] === '{') depth++;
else if (src[i] === '}') depth--;
i++;
}
return src.slice(start, i);
}
eval(extractFunc('_matchBacktickFenceLine'));
eval(extractFunc('_isBacktickFenceClose'));
eval(extractFunc('renderMd'));
let buf = '';
process.stdin.on('data', c => { buf += c; });
process.stdin.on('end', () => { process.stdout.write(renderMd(buf)); });
"""
@pytest.fixture(scope="module")
def driver_path(tmp_path_factory):
p = tmp_path_factory.mktemp("renderer_driver") / "driver.js"
p.write_text(_DRIVER_SRC, encoding="utf-8")
return str(p)
def _render(driver_path, markdown: str) -> str:
import subprocess
result = subprocess.run(
[NODE, driver_path, str(UI_JS_PATH)],
input=markdown, capture_output=True, text=True, timeout=10,
)
if result.returncode != 0:
raise RuntimeError(f"node driver failed: {result.stderr}")
return result.stdout
class TestFencedCodeBlockIsolation:
"""Content inside fenced code blocks must never be interpreted as markdown."""
def test_diff_block_plus_minus_lines_not_treated_as_list(self, driver_path):
html = _render(driver_path,
"before\n\n```diff\n- removed line\n+ added line\n```\nafter")
pre = _extract_pre(html)
assert pre is not None, "Expected a block"
assert "" not in pre, "List HTML must not appear inside
"
assert "- " not in pre, "List items must not appear inside
"
assert "- removed line" in pre
assert "+ added line" in pre
assert html.count(""), \
"Every must have a matching
"
def test_bash_block_asterisk_lines_not_treated_as_list(self, driver_path):
html = _render(driver_path,
"```bash\n* not a list item\n* another one\n```")
pre = _extract_pre(html)
assert pre is not None
assert "" not in pre
assert "- " not in pre
def test_markdown_block_heading_not_treated_as_h1(self, driver_path):
html = _render(driver_path,
"```markdown\n# Not a heading\n## Also not\n```")
pre = _extract_pre(html)
assert pre is not None
assert "
" not in pre
assert "" not in pre
assert "# Not a heading" in pre
def test_markdown_block_bold_not_treated_as_strong(self, driver_path):
html = _render(driver_path, "```text\n**not bold**\n```")
pre = _extract_pre(html)
assert pre is not None
assert "" not in pre
assert "**not bold**" in pre
def test_content_after_code_block_renders_normally(self, driver_path):
html = _render(driver_path,
"```diff\n- old\n+ new\n```\n\n"
"# Real Heading\n\n- real list item\n\n**bold text**")
assert "Real Heading
" in html
assert "" in html
assert "bold text" in html
pre = _extract_pre(html)
assert "" not in pre
def test_no_escaped_pre_closing_tag(self, driver_path):
html = _render(driver_path,
"```diff\n- old line\n+ new line\n```\n\nAfter the block")
assert "</pre>" not in html, \
"Escaped
indicates broken HTML nesting"
def test_code_block_without_language(self, driver_path):
html = _render(driver_path,
"```\n- item\n+ item\n```\n\n- real list")
pre = _extract_pre(html)
assert pre is not None
assert "" not in pre
assert html.count("
")
def test_inline_backtick_still_works(self, driver_path):
html = _render(driver_path, "Some **`code`** here")
assert "code" in html
def test_inline_backtick_inside_bold(self, driver_path):
html = _render(driver_path,
"Text with `inline code` and **bold**")
assert "inline code" in html
assert "bold" in html
def test_large_diff_output_no_corruption(self, driver_path):
diff_lines = "\n".join(
f"- old_line_{i}" if i % 2 == 0 else f"+ new_line_{i}"
for i in range(50)
)
html = _render(driver_path,
f"```diff\n{diff_lines}\n```\n\nSummary after diff")
pre = _extract_pre(html)
assert pre is not None
assert "" not in pre
assert "- " not in pre
assert html.count("
")
assert "Summary after diff" in html
def test_mixed_fenced_and_inline_code(self, driver_path):
html = _render(driver_path,
"Use `rm -rf` to delete:\n\n"
"```bash\nrm -rf /tmp/stuff\n```\n\nDone.")
assert "rm -rf" in html
pre = _extract_pre(html)
assert pre is not None
assert "rm -rf /tmp/stuff" in pre
class TestFencedBlockPreHeaderPreserved:
"""Pre-header div (language label) must still be generated."""
def test_language_header_present(self, driver_path):
html = _render(driver_path,
"```python\ndef hello():\n pass\n```")
assert 'python' in html
def test_no_language_no_header(self, driver_path):
html = _render(driver_path, "```\nsome code\n```")
assert "pre-header" not in html
def test_language_class_on_code_tag(self, driver_path):
html = _render(driver_path,
"```javascript\nconsole.log('hi')\n```")
assert 'class="language-javascript"' in html
def _extract_pre(html):
m = re.search(r"]*>[\s\S]*?
", html)
return m.group(0) if m else None