Skip to content
Source
Sign inSign up

composer-list-continuation.test.ts

162 lines6.4 KB

1import { describe, expect, it } from "vite-plus/test";
2​
3import {
4 listContinuationForEnter,
5 listIndentForTab,
6 listOutdentForTab,
7 nextOrderedMarkerText,
8} from "./composer-list-continuation";
9​
10function applyEdit(value: string, edit: { start: number; end: number; replacement: string }) {
11 return value.slice(0, edit.start) + edit.replacement + value.slice(edit.end);
13​
14describe("composer list continuation", () => {
15 it("continues bullets and keeps indentation", () => {
16 expect(applyEdit("- foo", listContinuationForEnter("- foo", 5)!)).toBe("- foo\n- ");
17 expect(applyEdit(" * foo", listContinuationForEnter(" * foo", 7)!)).toBe(" * foo\n * ");
18 });
19​
20 it("continues tasks unchecked", () => {
21 expect(applyEdit("- [x] done", listContinuationForEnter("- [x] done", 10)!)).toBe(
22 "- [x] done\n- [ ] ",
23 );
24 });
25​
26 it("splits mid-line items", () => {
27 expect(applyEdit("1. foobar", listContinuationForEnter("1. foobar", 5)!)).toBe(
28 "1. fo\n2. obar",
29 );
30 });
31​
32 it("exits the list on an empty item", () => {
33 expect(applyEdit("1. foo\n2. ", listContinuationForEnter("1. foo\n2. ", 10)!)).toBe("1. foo\n");
34 expect(applyEdit("- ", listContinuationForEnter("- ", 2)!)).toBe("");
35 expect(applyEdit("1)\t", listContinuationForEnter("1)\t", 3)!)).toBe("");
36 expect(applyEdit("- [ ] ", listContinuationForEnter("- [ ] ", 6)!)).toBe("");
37 });
38​
39 it("lifts an empty nested item one level per press before leaving the list", () => {
40 let value = "1. parent\n 1. child\n 1. ";
41 for (const expected of [
42 "1. parent\n 1. child\n 2. ",
43 "1. parent\n 1. child\n2. ",
44 "1. parent\n 1. child\n",
45 ]) {
46 value = applyEdit(value, listContinuationForEnter(value, value.length)!);
47 expect(value).toBe(expected);
48 }
49 });
50​
51 it("renumbers the parent list after lifting an empty nested item", () => {
52 const value = "1. a\n 1. \n2. b";
53 const edit = listContinuationForEnter(value, value.indexOf("\n2."))!;
54 expect(applyEdit(value, edit)).toBe("1. a\n2. \n3. b");
55 expect(edit.cursorAfter).toBe("1. a\n2. ".length);
56 });
57​
58 it("refuses to split a supplementary currency skill chip", () => {
59 const value = "1. 𑿝review go";
60 const cursor = value.indexOf(" go") - 1;
61 expect(listContinuationForEnter(value, cursor)).toBeNull();
62 });
63​
64 it("refuses to split an inline chip", () => {
65 const value = "1. @README.md go";
66 const cursor = value.indexOf("README") + 2;
67 expect(listContinuationForEnter(value, cursor)).toBeNull();
68 });
69});
70​
71describe("composer list indentation", () => {
72 // regression: #17124 Tab must nest a numbered item at its parent's content column
73 it.each([
74 ["1. parent\n2. child", "1. parent\n 1. child"],
75 ["10. parent\n11. child", "10. parent\n 1. child"],
76 ["1.\tparent\n2. child", "1.\tparent\n 1. child"],
77 ['1. @"file\n 9. name"\n2. child', '1. @"file\n 9. name"\n 1. child'],
78 ])("nests %j as a first child and keeps the caret on its text", (value, expected) => {
79 const cursor = value.lastIndexOf("child") + 2;
80 const edit = listIndentForTab(value, cursor, cursor)!;
81 expect(applyEdit(value, edit)).toBe(expected);
82 expect(edit.cursorAfter).toBe(expected.lastIndexOf("child") + 2);
83 });
84​
85 it("continues the numbering of an existing nested sibling", () => {
86 const value = "1. parent\n 1. first\n2. second";
87 const edit = listIndentForTab(value, value.length, value.length)!;
88 expect(applyEdit(value, edit)).toBe("1. parent\n 1. first\n 2. second");
89 });
90​
91 it.each(["- parent\n- child", "1. parent\n2. child", " 7. child"])(
92 "undoes Tab on %j with Shift+Tab and keeps the caret on its text",
93 (value) => {
94 const cursor = value.lastIndexOf("child") + 2;
95 const nest = listIndentForTab(value, cursor, cursor)!;
96 const nested = applyEdit(value, nest);
97 const edit = listOutdentForTab(nested, nest.cursorAfter, nest.cursorAfter)!;
98 expect(applyEdit(nested, edit)).toBe(value);
99 expect(edit.cursorAfter).toBe(cursor);
100 },
101 );
102​
103 it.each([
104 ["1. a\n 1. b\n2. c", "1. a\n2. b\n3. c"],
105 ["- a\n 5. b", "- a\n5. b"],
106 ])("outdents %j to %j and renumbers only within the parent's list", (value, expected) => {
107 const edit = listOutdentForTab(value, value.indexOf("b"), value.indexOf("b"))!;
108 expect(applyEdit(value, edit)).toBe(expected);
109 });
110​
111 it("leaves a top-level item alone", () => {
112 expect(listOutdentForTab("- item", 6, 6)).toBeNull();
113 });
114});
115​
116describe("nextOrderedMarkerText", () => {
117 it.each([
118 ["1.", "2."],
119 ["1)", "2)"],
120 ["09.", "10."],
121 ["001)", "002)"],
122 ["99.", "100."],
123 ["99999999999999999999.", "99999999999999999999."],
124 ])("counts %s up to %s", (marker, expected) => {
125 expect(nextOrderedMarkerText(marker)).toBe(expected);
126 });
127});
128​
129describe("composer list renumbering", () => {
130 it("renumbers following items with their padding and leaves the caret after the inserted marker", () => {
131 const value = "1. @README.md go\n2. two\n03. three\n10. four";
132 const edit = listContinuationForEnter(value, value.indexOf("go"))!;
133 expect(applyEdit(value, edit)).toBe("1. @README.md \n2. go\n3. two\n04. three\n5. four");
134 expect(edit.cursorAfter).toBe("1. @README.md \n2. ".length);
135 });
136​
137 it("renumbers indented paren siblings across tab-indented children and stops at a parent", () => {
138 const value = " 1) a\n\t1) child\n 2) b\n1) parent\n 3) untouched";
139 const edit = listContinuationForEnter(value, value.indexOf("\n"))!;
140 expect(applyEdit(value, edit)).toBe(
141 " 1) a\n 2) \n\t1) child\n 3) b\n1) parent\n 3) untouched",
142 );
143 expect(edit.cursorAfter).toBe(" 1) a\n 2) ".length);
144 });
145​
146 // regression: #14332 continuing a numbered list must renumber following items only up to the list boundary
147 it.each([
148 ["a blank line", ""],
149 ["plain text", "text"],
150 ["a different delimiter", "1) other list"],
151 ])("stops renumbering at %s", (_label, boundary) => {
152 const value = `1. a\n2. b\n${boundary}\n3. untouched`;
153 const edit = listContinuationForEnter(value, value.indexOf("\n"))!;
154 expect(applyEdit(value, edit)).toBe(`1. a\n2. \n3. b\n${boundary}\n3. untouched`);
155 });
156​
157 it("preserves multiline chip contents while renumbering surrounding items", () => {
158 const value = '1. a\n2. @"file\n2. name" \n3. b';
159 const edit = listContinuationForEnter(value, value.indexOf("\n"))!;
160 expect(applyEdit(value, edit)).toBe('1. a\n2. \n3. @"file\n2. name" \n4. b');
161 });
162});