@cueloop/client 0.1.0-alpha.21 → 0.1.0-alpha.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -25,36 +25,57 @@ The daemon persists sessions to disk atomically.
25
25
 
26
26
  describe("buildDisplay reconciliation", () => {
27
27
  test("no working copy: everything is same", () => {
28
+ // Act
28
29
  const display = buildDisplay(BASE);
30
+
31
+ // Assert
29
32
  expect(display.every((block) => block.type === "same")).toBe(true);
30
33
  });
31
34
 
32
35
  test("reworded block pairs as mod (similar)", () => {
36
+ // Arrange
33
37
  const working = BASE.replace("persists sessions to disk", "writes sessions to disk");
38
+
39
+ // Act
34
40
  const display = buildDisplay(BASE, working);
41
+
42
+ // Assert
35
43
  const mod = display.find((block) => block.type === "mod")!;
36
44
  expect(mod.base!.text).toContain("persists");
37
45
  expect(mod.work!.text).toContain("writes");
38
46
  });
39
47
 
40
48
  test("unrelated cut + insert do NOT merge into a mod", () => {
49
+ // Arrange
41
50
  const working = BASE.replace("- second item\n", "") + "\nA totally new closing note.\n";
51
+
52
+ // Act
42
53
  const display = buildDisplay(BASE, working);
54
+
55
+ // Assert
43
56
  expect(display.some((block) => block.type === "del" && block.base!.text === "second item")).toBe(true);
44
57
  expect(display.some((block) => block.type === "add" && block.work!.text.includes("closing note"))).toBe(true);
45
58
  expect(display.some((block) => block.type === "mod")).toBe(false);
46
59
  });
47
60
 
48
61
  test("ordered list items get running numbers", () => {
62
+ // Act
49
63
  const display = buildDisplay("1. one\n2. two\n3. three\n");
64
+
65
+ // Assert
50
66
  expect(display.map((block) => block.orderedItemNumber)).toEqual([1, 2, 3]);
51
67
  });
52
68
  });
53
69
 
54
70
  describe("blockRuns + overlayMarks", () => {
55
71
  test("mod blocks emit word-diff runs that reconstruct both sides", () => {
72
+ // Arrange
56
73
  const block = buildDisplay("plain old text here\n", "plain new text here\n")[0]!;
74
+
75
+ // Act
57
76
  const runs = blockRuns(block, true);
77
+
78
+ // Assert
58
79
  const oldSide = runs.filter((run) => run.role !== "ins").map((run) => run.text).join("");
59
80
  const newSide = runs.filter((run) => run.role !== "del").map((run) => run.text).join("");
60
81
  expect(oldSide).toBe("plain old text here");
@@ -62,9 +83,14 @@ describe("blockRuns + overlayMarks", () => {
62
83
  });
63
84
 
64
85
  test("marks bind to working-text offsets and skip del runs", () => {
86
+ // Arrange
65
87
  const block = buildDisplay("old words linger\n", "new words linger\n")[0]!;
66
88
  const runs = blockRuns(block, true);
89
+
90
+ // Act
67
91
  const marked = overlayMarks(runs, [{ start: 4, end: 9, role: "mark-comment" }]); // "words"
92
+
93
+ // Assert
68
94
  const markedText = marked.filter((run) => run.role === "mark-comment").map((run) => run.text).join("");
69
95
  expect(markedText).toBe("words");
70
96
  // del run text untouched
@@ -72,14 +98,20 @@ describe("blockRuns + overlayMarks", () => {
72
98
  });
73
99
 
74
100
  test("marks never override ins emphasis", () => {
101
+ // Arrange
75
102
  const block = buildDisplay("keep\n", "keep added\n")[0]!;
103
+
104
+ // Act
76
105
  const runs = overlayMarks(blockRuns(block, true), [{ start: 0, end: 10, role: "mark-comment" }]);
106
+
107
+ // Assert
77
108
  expect(runs.some((run) => run.role === "ins")).toBe(true);
78
109
  });
79
110
  });
80
111
 
81
112
  describe("marksByDisplay", () => {
82
113
  test("annotations resolve into display coordinates", () => {
114
+ // Arrange
83
115
  const working = BASE.replace("first item", "first item edited beyond recognition of it");
84
116
  const display = buildDisplay(BASE, working);
85
117
  const ann: Annotation = {
@@ -89,13 +121,18 @@ describe("marksByDisplay", () => {
89
121
  body: "define atomically",
90
122
  createdAt: "",
91
123
  };
124
+
125
+ // Act
92
126
  const map = marksByDisplay([ann], display);
127
+
128
+ // Assert
93
129
  const [displayIndex, marks] = [...map.entries()][0]!;
94
130
  expect(displayText(display[displayIndex]!)).toContain("atomically");
95
131
  expect(marks[0]!.annotationId).toBe("a1");
96
132
  });
97
133
 
98
134
  test("orphaned annotations produce no marks", () => {
135
+ // Arrange
99
136
  const display = buildDisplay(BASE);
100
137
  const ann: Annotation = {
101
138
  id: "a1",
@@ -104,25 +141,35 @@ describe("marksByDisplay", () => {
104
141
  body: "",
105
142
  createdAt: "",
106
143
  };
144
+
145
+ // Assert
107
146
  expect(marksByDisplay([ann], display).size).toBe(0);
108
147
  });
109
148
  });
110
149
 
111
150
  describe("rendered/work offset mapping", () => {
112
151
  test("plain blocks map 1:1 in both directions", () => {
152
+ // Arrange
113
153
  const block = buildDisplay("alpha beta gamma\n")[0]!;
114
154
  const runs = blockRuns(block, true);
155
+
156
+ // Assert
115
157
  expect(renderedOffsetFor(runs, 6)).toBe(6);
116
158
  expect(workRangeForRendered(runs, 6, 10)).toEqual({ start: 6, end: 10 });
117
159
  });
118
160
 
119
161
  test("inline del runs shift rendered offsets in word-diffed blocks", () => {
162
+ // Arrange
120
163
  // rendered: "old new words linger" - "old " is a del run without offsets
121
164
  const block = buildDisplay("old words linger\n", "new words linger\n")[0]!;
122
165
  const runs = blockRuns(block, true);
123
166
  const workText = displayText(block);
124
167
  const wordsAt = workText.indexOf("words");
168
+
169
+ // Act
125
170
  const rendered = renderedOffsetFor(runs, wordsAt)!;
171
+
172
+ // Assert
126
173
  const renderedText = runs.map((run) => run.text).join("");
127
174
  expect(renderedText.slice(rendered, rendered + "words".length)).toBe("words");
128
175
  // reading a rendered selection over "words" recovers the work range
@@ -133,16 +180,24 @@ describe("rendered/work offset mapping", () => {
133
180
  });
134
181
 
135
182
  test("a rendered selection spanning a del run keeps only positioned text", () => {
183
+ // Arrange
136
184
  const block = buildDisplay("old words\n", "new words\n")[0]!;
137
185
  const runs = blockRuns(block, true);
138
186
  const renderedText = runs.map((run) => run.text).join("");
187
+
188
+ // Act
139
189
  // select everything rendered: the work range is the whole work text
140
190
  const range = workRangeForRendered(runs, 0, renderedText.length)!;
191
+
192
+ // Assert
141
193
  expect(displayText(block).slice(range.start, range.end)).toBe(displayText(block));
142
194
  });
143
195
 
144
196
  test("offsets outside any positioned run map to null", () => {
197
+ // Arrange
145
198
  const runs = blockRuns(buildDisplay("short\n")[0]!, true);
199
+
200
+ // Assert
146
201
  expect(renderedOffsetFor(runs, 99)).toBeNull();
147
202
  expect(workRangeForRendered(runs, 90, 99)).toBeNull();
148
203
  });
@@ -151,28 +206,54 @@ describe("rendered/work offset mapping", () => {
151
206
  describe("span mode", () => {
152
207
  const text = "one two three four";
153
208
  test("v selects the first word; l grows; w slides; $ to end", () => {
209
+ // Act
154
210
  let span = startSpan(0, text)!;
211
+
212
+ // Assert
155
213
  expect(text.slice(span.start, span.end)).toBe("one");
214
+
215
+ // Act
156
216
  span = spanKey(span, "l", text);
217
+
218
+ // Assert
157
219
  expect(text.slice(span.start, span.end)).toBe("one two");
220
+
221
+ // Act
158
222
  span = spanKey(span, "w", text);
223
+
224
+ // Assert
159
225
  expect(text.slice(span.start, span.end)).toBe("two three");
226
+
227
+ // Act
160
228
  span = spanKey(span, "$", text);
229
+
230
+ // Assert
161
231
  expect(text.slice(span.start, span.end)).toBe("two three four");
162
232
  });
163
233
  });
164
234
 
165
235
  describe("cut / restore round-trip", () => {
166
236
  test("cut removes the block lines; restore puts them back", () => {
237
+ // Arrange
167
238
  const blocks = parseBlocks(BASE);
168
239
  const listItem = blocks.find((candidate) => candidate.text === "second item")!;
240
+
241
+ // Act
169
242
  const cut = cutBlock(BASE, listItem);
243
+
244
+ // Assert
170
245
  expect(cut).not.toContain("second item");
246
+
247
+ // Arrange
171
248
  const display = buildDisplay(BASE, cut);
172
249
  const deletedIndex = display.findIndex((block) => block.type === "del");
173
250
  const line = restoreLine(nextWorkBlock(display, deletedIndex), cut.split("\n").length);
251
+
252
+ // Act
174
253
  // undefined = the working copy round-tripped back to the submitted revision
175
254
  const restored = restoreBlock(BASE, cut, listItem, line);
255
+
256
+ // Assert
176
257
  expect(restored).toBeUndefined();
177
258
  expect(buildDisplay(BASE, restored).every((block) => block.type === "same")).toBe(true);
178
259
  });
package/src/view-plan.ts CHANGED
@@ -8,6 +8,7 @@
8
8
  */
9
9
 
10
10
  import {
11
+ isAddressed,
11
12
  lcsDiff,
12
13
  parseBlocks,
13
14
  resolveAnchor,
@@ -161,6 +162,8 @@ export function marksByDisplay(
161
162
  const workBlocks = workEntries.map((entry) => entry.block.work!);
162
163
  const marksByIndex = new Map<number, Mark[]>();
163
164
  for (const annotation of annotations) {
165
+ // an addressed annotation keeps its record but paints no highlight
166
+ if (isAddressed(annotation)) continue;
164
167
  const resolved = resolveAnchor(annotation.anchor, workBlocks);
165
168
  if (!resolved) continue;
166
169
  const entry = workEntries[resolved.blockIndex];
package/src/walk.test.ts CHANGED
@@ -92,12 +92,16 @@ describe("noteForFile", () => {
92
92
  });
93
93
 
94
94
  test("finds the note anchored at the file path", () => {
95
+ // Arrange
95
96
  const annotations = [note("src/a.ts", "Renames b and adds c."), note("src/b.ts", "Swaps the line.")];
97
+
98
+ // Assert
96
99
  expect(noteForFile(annotations, "src/b.ts")).toBe("Swaps the line.");
97
100
  expect(noteForFile(annotations, "src/c.ts")).toBeUndefined();
98
101
  });
99
102
 
100
103
  test("ignores non-note annotations that happen to quote the path", () => {
104
+ // Arrange
101
105
  const comment: Annotation = {
102
106
  id: "a_1",
103
107
  kind: "comment",
@@ -105,6 +109,8 @@ describe("noteForFile", () => {
105
109
  body: "not a note",
106
110
  createdAt: "now",
107
111
  };
112
+
113
+ // Assert
108
114
  expect(noteForFile([comment], "src/a.ts")).toBeUndefined();
109
115
  });
110
116
  });