Skip to content

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

157 linesCodeBlameRaw
1import assert from "node:assert/strict";
2import { test } from "node:test";
3
4import * as Y from "yjs";
5
6import { parseInline, parseMarkdown, seed } from "./blocks.ts";
7import { bodyCitations } from "./citations.ts";
8import { citationNodes, documentMarkdown, excerpt, mentionedIds, outline, searchText, topContainers } from "./markdown.ts";
9
10function docFrom(markdown: string) {
11 const doc = new Y.Doc();
12 const fragment = doc.getXmlFragment("document-store");
13 seed(doc, fragment, markdown);
14 return { doc, fragment };
15}
16
17const SAMPLE = `# Release plan
18
19Ship **v2** on _Thursday_ with ~~no~~ \`zero\` downtime. See [the runbook](https://g1t.sh/x).
20
21## Steps
22
231. Freeze main
242. Tag the release
25 - with a signed tag
263. Deploy
27
28- [x] Write notes
29- [ ] Tell #support
30
31> [!WARNING]
32> Migrations run first.
33
34> Quotes stay quotes.
35
36\`\`\`ts
37const a = 1;
38\`\`\`
39
40\`\`\`mermaid
41flowchart LR
42 A --> B
43\`\`\`
44
45$$
46E = mc^2
47$$
48
49| Step | Owner |
50| --- | --- |
51| Tag | Ana |
52
53---
54
55![Diagram](https://g1tusercontent.com/docs/x.png)
56
57<details>
58<summary>More</summary>
59
60Hidden text.
61
62</details>
63`;
64
65test("Markdown survives a trip through the document", () => {
66 const { fragment } = docFrom(SAMPLE);
67 assert.equal(documentMarkdown(fragment), SAMPLE);
68});
69
70test("the document has BlockNote's shape: a group of containers with ids", () => {
71 const { fragment } = docFrom("# Title\n\nText");
72 const group = fragment.get(0) as Y.XmlElement;
73 assert.equal(group.nodeName, "blockGroup");
74 const tops = topContainers(fragment);
75 assert.equal(tops.length, 2);
76 assert.match(String(tops[0]!.getAttribute("id")), /^[0-9a-f-]{36}$/);
77 const heading = tops[0]!.get(0) as Y.XmlElement;
78 assert.equal(heading.nodeName, "heading");
79 assert.equal(heading.getAttribute("level"), 1 as unknown as string);
80 assert.equal(heading.getAttribute("textAlignment"), "left");
81});
82
83test("marks are stored as y-prosemirror stores them", () => {
84 const { fragment } = docFrom("a **b** [c](https://x.y)");
85 const paragraph = topContainers(fragment)[0]!.get(0) as Y.XmlElement;
86 const text = paragraph.get(0) as Y.XmlText;
87 assert.deepEqual(text.toDelta(), [
88 { insert: "a " },
89 { insert: "b", attributes: { bold: {} } },
90 { insert: " " },
91 { insert: "c", attributes: { link: { href: "https://x.y" } } },
92 ]);
93});
94
95test("nested list items become children", () => {
96 const specs = parseMarkdown("- one\n - two\n - three\n- four");
97 assert.equal(specs.length, 2);
98 assert.equal(specs[0]!.children![0]!.children![0]!.type, "bulletListItem");
99});
100
101test("snake_case and URLs are not italicized", () => {
102 assert.deepEqual(parseInline("set max_retry_count to 3"), [{ text: "set max_retry_count to 3" }]);
103});
104
105test("the outline names each top-level block for agents", () => {
106 const { fragment } = docFrom("# A\n\npara\n\n- x\n - y");
107 const blocks = outline(fragment);
108 assert.deepEqual(
109 blocks.map((b) => [b.type, b.level, b.markdown]),
110 [
111 ["heading", 1, "# A"],
112 ["paragraph", null, "para"],
113 ["bulletListItem", null, "- x\n - y"],
114 ],
115 );
116});
117
118test("mentions are found by id; search text drops markup", () => {
119 const doc = new Y.Doc();
120 const fragment = doc.getXmlFragment("document-store");
121 seed(doc, fragment, "hello");
122 doc.transact(() => {
123 const paragraph = topContainers(fragment)[0]!.get(0) as Y.XmlElement;
124 const mention = new Y.XmlElement("mention");
125 mention.setAttribute("kind", "user");
126 mention.setAttribute("id", "usr_1");
127 mention.setAttribute("name", "ana");
128 paragraph.insert(paragraph.length, [mention]);
129 });
130 assert.deepEqual(mentionedIds(fragment), { users: ["usr_1"], agents: [] });
131 assert.equal(documentMarkdown(fragment), "hello@ana\n");
132 assert.equal(searchText("## Steps\n\n- [x] **Ship** [it](https://x)"), "Steps\n Ship it");
133 assert.equal(excerpt("# T\n\n" + "word ".repeat(100), 20).length, 20);
134});
135
136test("a citation chip is a link to the code, and is found as a citation", () => {
137 const doc = new Y.Doc();
138 const fragment = doc.getXmlFragment("document-store");
139 seed(doc, fragment, "Exports run in ");
140 doc.transact(() => {
141 const paragraph = topContainers(fragment)[0]!.get(0) as Y.XmlElement;
142 const chip = new Y.XmlElement("citation");
143 chip.setAttribute("repo", "acme/web");
144 chip.setAttribute("path", "src/export.ts");
145 chip.setAttribute("kind", "symbol");
146 chip.setAttribute("label", "exportCsv");
147 chip.setAttribute("ref", "abc1234");
148 paragraph.insert(paragraph.length, [chip]);
149 });
150 const markdown = documentMarkdown(fragment);
151 assert.equal(markdown, "Exports run in [`exportCsv`](/acme/web/blob/abc1234/src/export.ts)\n");
152 const found = bodyCitations(citationNodes(fragment), markdown);
153 assert.deepEqual(
154 found.map((c) => [c.repo, c.path, c.kind, c.label]),
155 [["acme/web", "src/export.ts", "symbol", "exportCsv"]],
156 );
157});