|
| 1 | +// Tests fenced-code-block span scanning used to keep chunk breaks out of code blocks. |
| 2 | +import { describe, expect, it } from "vitest"; |
| 3 | +import { isSafeFenceBreak, parseFenceSpans } from "./fences.js"; |
| 4 | + |
| 5 | +describe("parseFenceSpans closing-fence rules", () => { |
| 6 | +it("treats a marker line with trailing text as code content, not a closing fence", () => { |
| 7 | +// CommonMark: a closing fence may be followed only by whitespace, so "``` not a close" is code |
| 8 | +// content and the block stays open until the real closing fence. Reporting an interior offset |
| 9 | +// as a safe break would let a chunker split inside the code block. |
| 10 | +const text = "```\ncode\n``` not a close\nmore code\n```\n"; |
| 11 | +const spans = parseFenceSpans(text); |
| 12 | + |
| 13 | +expect(spans).toHaveLength(1); |
| 14 | +expect(isSafeFenceBreak(spans, text.indexOf("more code") + 1)).toBe(false); |
| 15 | +}); |
| 16 | + |
| 17 | +it("does not close on non-space/tab whitespace", () => { |
| 18 | +for (const suffix of ["\u00a0", "\v", "\f"]) { |
| 19 | +const text = `\`\`\`\ncode\n\`\`\`${suffix}\nmore code\n`; |
| 20 | +const spans = parseFenceSpans(text); |
| 21 | + |
| 22 | +expect(spans).toHaveLength(1); |
| 23 | +expect(spans[0]?.end).toBe(text.length); |
| 24 | +expect(isSafeFenceBreak(spans, text.indexOf("more code") + 1)).toBe(false); |
| 25 | +} |
| 26 | +}); |
| 27 | + |
| 28 | +it("closes fences with CRLF line endings", () => { |
| 29 | +const text = "```\r\ncode\r\n```\r\nafter\r\n"; |
| 30 | +const spans = parseFenceSpans(text); |
| 31 | + |
| 32 | +expect(spans).toHaveLength(1); |
| 33 | +expect(isSafeFenceBreak(spans, text.indexOf("after") + 1)).toBe(true); |
| 34 | +}); |
| 35 | + |
| 36 | +it("still closes on a bare fence, a longer same-marker fence, and keeps an opener info string", () => { |
| 37 | +expect(parseFenceSpans("```\ncode\n```\nafter\n")).toHaveLength(1); |
| 38 | +expect(parseFenceSpans("```\ncode\n````` \nafter\n")).toHaveLength(1); |
| 39 | +expect(parseFenceSpans("```python\nx = 1\n```\n")).toHaveLength(1); |
| 40 | + |
| 41 | +const closed = "```\ncode\n```\nafter\n"; |
| 42 | +const spans = parseFenceSpans(closed); |
| 43 | +expect(isSafeFenceBreak(spans, closed.indexOf("after") + 1)).toBe(true); |
| 44 | +}); |
| 45 | +}); |