File size: 4,395 Bytes
fae625a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
import { describe, expect, test } from "vitest";
import { parseIncompleteMarkdown } from "./parseIncompleteMarkdown";

describe("parseIncompleteMarkdown (remend)", () => {
	describe("marker completion", () => {
		test("completes incomplete bold", () => {
			expect(parseIncompleteMarkdown("**hello")).toBe("**hello**");
		});

		test("completes incomplete italic", () => {
			expect(parseIncompleteMarkdown("*hello")).toBe("*hello*");
		});

		test("completes incomplete inline code", () => {
			expect(parseIncompleteMarkdown("`code")).toBe("`code`");
		});

		test("completes incomplete strikethrough", () => {
			expect(parseIncompleteMarkdown("~~text")).toBe("~~text~~");
		});

		test("completes incomplete block math", () => {
			expect(parseIncompleteMarkdown("$$\nx = 1")).toBe("$$\nx = 1\n$$");
		});

		test("completes a partial closing $ of block math with a single $", () => {
			expect(parseIncompleteMarkdown("$$E=mc^2$")).toBe("$$E=mc^2$$");
		});
	});

	describe("links and images", () => {
		test("closes incomplete link text with placeholder URL", () => {
			expect(parseIncompleteMarkdown("[link tex")).toBe("[link tex](streamdown:incomplete-link)");
		});

		test("replaces incomplete link URL with placeholder", () => {
			expect(parseIncompleteMarkdown("[text](https://exa")).toBe(
				"[text](streamdown:incomplete-link)"
			);
		});

		test("removes images with incomplete URLs", () => {
			const result = parseIncompleteMarkdown("before ![alt](https://x");
			expect(result).not.toContain("![");
			expect(result).toContain("before");
		});

		test("leaves complete links untouched", () => {
			const input = "[link](https://example.com) end";
			expect(parseIncompleteMarkdown(input)).toBe(input);
		});
	});

	describe("streaming flash guards", () => {
		test("breaks a potential setext heading underline with a zero-width space", () => {
			// Without this, "Some text" would flash as an <h2> while a list item streams in
			expect(parseIncompleteMarkdown("Some text\n-")).toBe("Some text\n-​");
		});

		test("escapes single tildes between word characters (no false strikethrough)", () => {
			expect(parseIncompleteMarkdown("Temperature is 20~25°C")).toBe("Temperature is 20\\~25°C");
		});

		test("escapes > used as comparison operator in list items (no blockquote)", () => {
			expect(parseIncompleteMarkdown("- > 25: high")).toBe("- \\> 25: high");
		});

		test("strips a half-typed trailing HTML tag", () => {
			expect(parseIncompleteMarkdown("Hello <div cla")).toBe("Hello");
		});

		test("keeps complete HTML tags", () => {
			const input = "Hello <b>hi</b>";
			expect(parseIncompleteMarkdown(input)).toBe(input);
		});
	});

	describe("false-positive protection", () => {
		test("does not close emphasis markers inside a complete inline code span", () => {
			const input = "`**bold`";
			expect(parseIncompleteMarkdown(input)).toBe(input);
		});

		test("does not treat $$ inside inline code as math", () => {
			const input = "run `echo $$` now";
			expect(parseIncompleteMarkdown(input)).toBe(input);
		});

		test("does not complete a lone $ (currency, inlineKatex disabled)", () => {
			const input = "I have $20";
			expect(parseIncompleteMarkdown(input)).toBe(input);
		});

		test("does not treat asterisks inside math blocks as italics", () => {
			const input = "$$a * b$$ and more";
			expect(parseIncompleteMarkdown(input)).toBe(input);
		});

		test("does not append underscores to word-internal underscores", () => {
			const input = "snake_case_name";
			expect(parseIncompleteMarkdown(input)).toBe(input);
		});

		test("does not complete markers inside an open code fence", () => {
			const input = "```js\nconst a = b ** 2;\nconst c = a **";
			expect(parseIncompleteMarkdown(input)).toBe(input);
		});
	});

	describe("whitespace handling", () => {
		test("removes a single trailing space (mid-word streaming)", () => {
			expect(parseIncompleteMarkdown("hello ")).toBe("hello");
		});

		test("preserves a double trailing space (markdown line break)", () => {
			expect(parseIncompleteMarkdown("hello  ")).toBe("hello  ");
		});
	});

	describe("input edge cases", () => {
		test("returns empty string unchanged", () => {
			expect(parseIncompleteMarkdown("")).toBe("");
		});

		test("returns plain text unchanged", () => {
			expect(parseIncompleteMarkdown("just some plain text")).toBe("just some plain text");
		});
	});
});