1import { describe, expect, it } from "vite-plus/test";
2
3import { upgradeLegacyContextMessage } from "./composerContextLegacy.ts";
4
5describe("upgradeLegacyContextMessage", () => {
6 const review =
7 '<review_comment sectionId="s" filePath="f.ts" startIndex="1" endIndex="1">note</review_comment>';
8
9 it.each([
10 {
11 tag: "terminal_context",
12 body: `- Build line 1:\n ${review}`,
13 payload: { kind: "terminal", text: review },
14 },
15 {
16 tag: "element_context",
17 body: `- <div>:\n html:\n ${review}`,
18 payload: { kind: "element", htmlPreview: review },
19 },
20 {
21 tag: "preview_annotation",
22 body: `Page: Example\nComment: ${review}`,
23 payload: { kind: "preview-annotation", comment: review },
24 },
25 {
26 tag: "preview_annotation",
27 body: `Page: Example\n<element_context>\n- <div>:\n html:\n ${review}\n</element_context>`,
28 payload: {
29 kind: "preview-annotation",
30 elements: [expect.objectContaining({ htmlPreview: review })],
31 },
32 },
33 ])("preserves review-shaped payloads in $tag", ({ tag, body, payload }) => {
34 for (const suffix of ["", `\n${review}`]) {
35 const result = upgradeLegacyContextMessage(
36 `Before ${review} after\n<${tag}>\n${body}\n</${tag}>${suffix}`,
37 );
38 expect(result.records[0]).toMatchObject(payload);
39 expect(result.records.filter((record) => record.kind === "review-comment")).toHaveLength(
40 suffix ? 2 : 1,
41 );
42 expect(result.text).not.toContain("\uE000");
43 expect(result.text).toContain(
44 "Before [f.ts line](t3-context://v1/review-comment/legacy_review-comment_1) after",
45 );
46 }
47 });
48
49 it.each(["terminal_context", "element_context"])(
50 "keeps mixed messages atomic for malformed %s",
51 (tag) => {
52 const text = `Before ${review} after\n<${tag}>\ninvalid entry\n</${tag}>`;
53 expect(upgradeLegacyContextMessage(text)).toEqual({ text, records: [] });
54 },
55 );
56
57 it.each([
58 "unparsed content",
59 "- Invalid header:\n html: text",
60 "- <div>:\n html:\n <div />\nunparsed content",
61 ])("preserves malformed nested preview elements: %s", (body) => {
62 const text = `Before ${review} after\n<preview_annotation>\nPage: Example\n<element_context>\n${body}\n</element_context>\n</preview_annotation>`;
63 expect(upgradeLegacyContextMessage(text)).toEqual({ text, records: [] });
64 });
65
66 it("allows an empty nested element block", () => {
67 const result = upgradeLegacyContextMessage(
68 "<preview_annotation>\nPage: Example\n<element_context>\n\n</element_context>\n</preview_annotation>",
69 );
70 expect(result.records).toHaveLength(1);
71 expect(result.records[0]).toMatchObject({ kind: "preview-annotation", pageTitle: "Example" });
72 });
73
74 it.each([3, 4, 6])("preserves closing review tags inside a %i-backtick fence", (length) => {
75 const fence = "`".repeat(length);
76 const diff = '+ const tag = "</review_comment>";\n</review_comment>\n+ final line';
77 const block = `<review_comment sectionId="s" filePath="f.ts" startIndex="1" endIndex="1">\nComment\n${fence}diff\n${diff}\n${fence}\n</review_comment>`;
78 const result = upgradeLegacyContextMessage(`Before ${block} between ${review} after`);
79 expect(result.text).toBe(
80 "Before [f.ts line](t3-context://v1/review-comment/legacy_review-comment_1) between [f.ts line](t3-context://v1/review-comment/legacy_review-comment_2) after",
81 );
82 expect(result.records).toHaveLength(2);
83 expect(result.records[0]).toMatchObject({ text: "Comment", diff, fenceLanguage: "diff" });
84 });
85
86 it("does not close a review fence at a shorter backtick run", () => {
87 const diff = "```\n</review_comment>\n+ remaining source";
88 const text = `<review_comment sectionId="s" filePath="f.ts" startIndex="1" endIndex="1">\nNote\n\`\`\`\`diff\n${diff}\n\`\`\`\`\n</review_comment>`;
89 expect(upgradeLegacyContextMessage(text).records[0]).toMatchObject({ text: "Note", diff });
90 });
91
92 it("preserves a review with an unterminated fence", () => {
93 const text =
94 '<review_comment sectionId="s" filePath="f.ts" startIndex="1" endIndex="1">\nNote\n```diff\n+ source\n</review_comment>';
95 expect(upgradeLegacyContextMessage(text)).toEqual({ text, records: [] });
96 });
97
98 it.each([
99 `- ${"x".repeat(256)} line 1:\n output`,
100 `- Build line 1:\n ${"x".repeat(64_001)}`,
101 "- Build lines 9-1:\n output",
102 Array.from({ length: 201 }, () => "- Build line 1:\n output").join("\n"),
103 ])("keeps terminal source when conversion exceeds the wire contract", (body) => {
104 const text = `Prompt\n<terminal_context>\n${body}\n</terminal_context>`;
105 expect(upgradeLegacyContextMessage(text)).toEqual({ text, records: [] });
106 });
107
108 it.each([
109 `<element_context>\n- <div>:\n html:\n ${"x".repeat(8_001)}\n</element_context>`,
110 `<preview_annotation>\nComment: ${"x".repeat(8_001)}\n</preview_annotation>`,
111 ])("keeps oversized element and preview source", (text) => {
112 expect(upgradeLegacyContextMessage(text)).toEqual({ text, records: [] });
113 });
114
115 it.each([
116 "email@build:7",
117 "café@build:7",
118 "𐐀@build:7",
119 "@build:7foo",
120 "@build:7st",
121 "@build:7é",
122 "@build:7.foo",
123 "@build:7@mention",
124 "@build:7..foo",
125 "@build:7.é",
126 ])("does not replace terminal labels embedded in %s", (prompt) => {
127 const upgraded = upgradeLegacyContextMessage(
128 `${prompt}\n<terminal_context>\n- Build line 7:\n output\n</terminal_context>`,
129 );
130 expect(upgraded.text).toBe(
131 `${prompt}\n\n[Build line 7](t3-context://v1/terminal/legacy_terminal_1)`,
132 );
133 });
134
135 it.each([".", ",", ":", ";", "!", "?", ")", "@", ". Next sentence", "@ next", ".)"])(
136 "upgrades terminal labels before punctuation %s",
137 (suffix) => {
138 const result = upgradeLegacyContextMessage(
139 `See @build:7${suffix}\n<terminal_context>\n- Build line 7:\n output\n</terminal_context>`,
140 );
141 expect(result.text).toBe(
142 `See [Build line 7](t3-context://v1/terminal/legacy_terminal_1)${suffix}`,
143 );
144 expect(result.records).toHaveLength(1);
145 },
146 );
147
148 it.each(["terminal_context", "element_context"])(
149 "preserves malformed trailing %s blocks as text",
150 (tag) => {
151 for (const body of ["partial output", "- Unrecognized header:\n partial output"]) {
152 const text = `message\n<${tag}>\n${body}\n</${tag}>`;
153 expect(upgradeLegacyContextMessage(text)).toEqual({ text, records: [] });
154 }
155 },
156 );
157
158 it.each(["terminal_context", "element_context"])(
159 "preserves partially parsed %s blocks",
160 (tag) => {
161 const entry =
162 tag === "terminal_context"
163 ? "- Build line 7:\n output"
164 : "- <button>:\n html:\n <button>Pay</button>";
165 for (const body of [
166 `unexpected prefix\n${entry}`,
167 `${entry}\nunexpected suffix`,
168 `${entry}\nunexpected middle\n${entry}`,
169 ` orphaned body\n${entry}`,
170 ]) {
171 const text = `Prompt\n<${tag}>\n${body}\n</${tag}>`;
172 expect(upgradeLegacyContextMessage(text)).toEqual({ text, records: [] });
173 }
174 },
175 );
176
177 it.each(["x".repeat(16_001), `note\n\`\`\`diff\n${"+x".repeat(16_001)}\n\`\`\``])(
178 "preserves oversized legacy reviews instead of creating dangling references",
179 (body) => {
180 const text = `<review_comment sectionId="s" filePath="f.ts" startIndex="1" endIndex="1">${body}</review_comment>`;
181 expect(upgradeLegacyContextMessage(text)).toEqual({ text, records: [] });
182 },
183 );
184
185 it("preserves literal review tokens even beside real review blocks", () => {
186 const review =
187 '<review_comment sectionId="s" filePath="f.ts" startIndex="1" endIndex="1">note</review_comment>';
188 const upgraded = upgradeLegacyContextMessage(
189 `Literal \uE0000\uE000 before ${review} after \uE0007\uE000`,
190 );
191 expect(upgraded.text).toBe(
192 "Literal \uE0000\uE000 before [f.ts line](t3-context://v1/review-comment/legacy_review-comment_1) after \uE0007\uE000",
193 );
194 expect(upgraded.records).toHaveLength(1);
195 const unmatched =
196 "Literal \uE0000\uE000 <review_comment>invalid</review_comment> \uE00099\uE000";
197 expect(upgradeLegacyContextMessage(unmatched)).toEqual({ text: unmatched, records: [] });
198 });
199
200 it("matches complete terminal line ranges rather than numeric prefixes", () => {
201 const upgraded = upgradeLegacyContextMessage(
202 [
203 "Compare @terminal:10 and @terminal:1-2 with @terminal:1",
204 "<terminal_context>",
205 "- Terminal line 1:",
206 " 1 | one",
207 "- Terminal line 10:",
208 " 10 | ten",
209 "- Terminal lines 1-2:",
210 " 1 | one",
211 " 2 | two",
212 "</terminal_context>",
213 ].join("\n"),
214 );
215 expect(upgraded.text).toBe(
216 "Compare [Terminal line 10](t3-context://v1/terminal/legacy_terminal_2) and [Terminal lines 1-2](t3-context://v1/terminal/legacy_terminal_3) with [Terminal line 1](t3-context://v1/terminal/legacy_terminal_1)",
217 );
218 });
219
220 it("keeps URL-valued preview pages as locations", () => {
221 const upgraded = upgradeLegacyContextMessage(
222 "<preview_annotation>\nPage: https://example.com/checkout\n</preview_annotation>",
223 );
224 expect(upgraded.records[0]).toMatchObject({
225 pageUrl: "https://example.com/checkout",
226 pageTitle: null,
227 });
228 });
229 it("passes plain text through untouched", () => {
230 expect(upgradeLegacyContextMessage("hello **world**")).toEqual({
231 text: "hello **world**",
232 records: [],
233 });
234 });
235
236 it("preserves Markdown hard breaks while removing legacy context", () => {
237 const text = [
238 "Line with hard break ",
239 "next",
240 "",
241 "<terminal_context>",
242 "- Build line 7:",
243 " 7 | done",
244 "</terminal_context>",
245 ].join("\n");
246
247 expect(upgradeLegacyContextMessage(text).text).toContain("Line with hard break \nnext");
248 });
249
250 it("binds U+FFFC placeholders to trailing terminal entries in order", () => {
251 const text = [
252 "Look at  and  please",
253 "",
254 "<terminal_context>",
255 "- Terminal 1 lines 509-510:",
256 " 509 | error: boom",
257 " 510 | at main.ts:1",
258 "",
259 "- Build line 7:",
260 " 7 | done",
261 "</terminal_context>",
262 ].join("\n");
263 const upgraded = upgradeLegacyContextMessage(text);
264 expect(upgraded.text).toBe(
265 "Look at [Terminal 1 lines 509-510](t3-context://v1/terminal/legacy_terminal_1) and [Build line 7](t3-context://v1/terminal/legacy_terminal_2) please",
266 );
267 expect(upgraded.records).toEqual([
268 {
269 version: 1,
270 contextId: "legacy_terminal_1",
271 kind: "terminal",
272 label: "Terminal 1 lines 509-510",
273 terminalId: "terminal-1",
274 terminalLabel: "Terminal 1",
275 lineStart: 509,
276 lineEnd: 510,
277 text: "error: boom\n at main.ts:1",
278 },
279 {
280 version: 1,
281 contextId: "legacy_terminal_2",
282 kind: "terminal",
283 label: "Build line 7",
284 terminalId: "build",
285 terminalLabel: "Build",
286 lineStart: 7,
287 lineEnd: 7,
288 text: "done",
289 },
290 ]);
291 });
292
293 it("appends references for terminal entries without placeholders and drops extra placeholders", () => {
294 const text = " and \n\n<terminal_context>\n- T line 1:\n 1 | x\n</terminal_context>";
295 expect(upgradeLegacyContextMessage(text).text).toBe(
296 "[T line 1](t3-context://v1/terminal/legacy_terminal_1) and",
297 );
298 const noPlaceholder = "hi\n\n<terminal_context>\n- T line 1:\n 1 | x\n</terminal_context>";
299 expect(upgradeLegacyContextMessage(noPlaceholder).text).toBe(
300 "hi\n\n[T line 1](t3-context://v1/terminal/legacy_terminal_1)",
301 );
302 });
303
304 it("binds materialized inline terminal labels from sent messages in place", () => {
305 const text = [
306 "Look at @terminal-1:509-510 and again @build:7 please",
307 "",
308 "<terminal_context>",
309 "- Terminal 1 lines 509-510:",
310 " 509 | error: boom",
311 " 510 | at main.ts:1",
312 "",
313 "- Build line 7:",
314 " 7 | done",
315 "</terminal_context>",
316 ].join("\n");
317 expect(upgradeLegacyContextMessage(text).text).toBe(
318 "Look at [Terminal 1 lines 509-510](t3-context://v1/terminal/legacy_terminal_1) and again [Build line 7](t3-context://v1/terminal/legacy_terminal_2) please",
319 );
320 });
321
322 it("upgrades a trailing element block into element records", () => {
323 const text = [
324 "fix this",
325 "",
326 "<element_context>",
327 "- <Button> (Button.tsx:12):",
328 " url: http://localhost:3000/checkout",
329 " selector: #pay",
330 " source: src/Button.tsx:12:3",
331 " html:",
332 ' <button id="pay">Pay</button>',
333 " styles:",
334 " color: red;",
335 "</element_context>",
336 ].join("\n");
337 const upgraded = upgradeLegacyContextMessage(text);
338 expect(upgraded.text).toBe("fix this\n\n[<Button>](t3-context://v1/element/legacy_element_1)");
339 expect(upgraded.records).toEqual([
340 {
341 version: 1,
342 contextId: "legacy_element_1",
343 kind: "element",
344 label: "<Button>",
345 pageUrl: "http://localhost:3000/checkout",
346 pageTitle: null,
347 tagName: "button",
348 selector: "#pay",
349 htmlPreview: '<button id="pay">Pay</button>',
350 componentName: "Button",
351 source: { functionName: null, fileName: "src/Button.tsx", lineNumber: 12, columnNumber: 3 },
352 styles: "color: red;",
353 },
354 ]);
355 });
356
357 it("keeps every line of a multiline legacy annotation comment", () => {
358 // Older clients wrote the comment verbatim, newlines and all. Reading only the first line
359 // silently drops the rest while the original block is replaced by a chip, so the dropped
360 // instructions are gone from what the reader sees and copies.
361 const text = [
362 "<preview_annotation>",
363 "Preview annotation:",
364 "Id: ann_2",
365 "Page: Checkout",
366 "Comment: First instruction",
367 "Second instruction",
368 "Targets: 1 selected element.",
369 "</preview_annotation>",
370 ].join("\n");
371 const upgraded = upgradeLegacyContextMessage(text);
372 expect(upgraded.records[0]).toMatchObject({
373 kind: "preview-annotation",
374 comment: "First instruction\nSecond instruction",
375 });
376 });
377
378 it("keeps a blank line inside a legacy annotation comment", () => {
379 const text = [
380 "<preview_annotation>",
381 "Preview annotation:",
382 "Comment: First paragraph",
383 "",
384 "Second paragraph",
385 "Targets: 1 selected element.",
386 "</preview_annotation>",
387 ].join("\n");
388 expect(upgradeLegacyContextMessage(text).records[0]).toMatchObject({
389 comment: "First paragraph\n\nSecond paragraph",
390 });
391 });
392
393 it("keeps markup inside a legacy annotation comment", () => {
394 // Only a real block delimiter ends the comment. A line that merely starts with `<` is
395 // something the author typed.
396 const text = [
397 "<preview_annotation>",
398 "Preview annotation:",
399 "Comment: Change this",
400 "<div>keep me</div>",
401 "and this too",
402 "</preview_annotation>",
403 ].join("\n");
404 expect(upgradeLegacyContextMessage(text).records[0]).toMatchObject({
405 comment: "Change this\n<div>keep me</div>\nand this too",
406 });
407 });
408
409 it("upgrades trailing preview annotation blocks", () => {
410 const text = [
411 "bigger",
412 "",
413 "<preview_annotation>",
414 "Preview annotation:",
415 "Id: ann_1",
416 "Page: Checkout",
417 "Comment: Make it bigger",
418 "Targets: 1 selected element.",
419 "Requested visual changes:",
420 "- font-size: 12px → 20px",
421 "The attached screenshot is the annotated preview crop.",
422 "<element_context>",
423 "- <button>:",
424 " url: http://localhost:3000/",
425 " selector: #pay",
426 " source: src/Pay.tsx:12:3",
427 " html:",
428 " <button>Pay</button>",
429 "</element_context>",
430 "</preview_annotation>",
431 ].join("\n");
432 const upgraded = upgradeLegacyContextMessage(text);
433 expect(upgraded.text).toBe(
434 "bigger\n\n[Checkout](t3-context://v1/preview-annotation/legacy_preview-annotation_1)",
435 );
436 expect(upgraded.records).toEqual([
437 {
438 version: 1,
439 contextId: "legacy_preview-annotation_1",
440 kind: "preview-annotation",
441 label: "Checkout",
442 annotationId: "ann_1",
443 pageUrl: "http://localhost:3000/",
444 pageTitle: "Checkout",
445 comment: "Make it bigger",
446 targetSummary: "1 selected element.",
447 styleChanges: ["font-size: 12px → 20px"],
448 elements: [
449 {
450 pageUrl: "http://localhost:3000/",
451 pageTitle: null,
452 tagName: "button",
453 selector: "#pay",
454 htmlPreview: "<button>Pay</button>",
455 componentName: null,
456 source: {
457 functionName: null,
458 fileName: "src/Pay.tsx",
459 lineNumber: 12,
460 columnNumber: 3,
461 },
462 styles: "",
463 },
464 ],
465 },
466 ]);
467 });
468
469 it("keeps a literal preview opening tag inside its annotation comment", () => {
470 const upgraded = upgradeLegacyContextMessage(
471 [
472 "Explain this",
473 "<preview_annotation>",
474 "Id: annotation-1",
475 "Page: Checkout",
476 "Comment: Render <preview_annotation>",
477 "Targets: 1 selected element.",
478 "</preview_annotation>",
479 ].join("\n"),
480 );
481 expect(upgraded.text).toBe(
482 "Explain this\n\n[Checkout](t3-context://v1/preview-annotation/legacy_preview-annotation_1)",
483 );
484 expect(upgraded.records).toHaveLength(1);
485 expect(upgraded.records[0]).toMatchObject({
486 annotationId: "annotation-1",
487 comment: "Render <preview_annotation>",
488 targetSummary: "1 selected element.",
489 });
490 });
491
492 it("keeps adjacent preview annotations separate and in their original order", () => {
493 const upgraded = upgradeLegacyContextMessage(
494 [
495 "Compare",
496 "<preview_annotation>",
497 "Id: annotation-1",
498 "Page: First",
499 "Comment: First comment",
500 "</preview_annotation>",
501 "<preview_annotation>",
502 "Id: annotation-2",
503 "Page: Second",
504 "Comment: Second comment",
505 "</preview_annotation>",
506 ].join("\n"),
507 );
508 expect(upgraded.text).toBe(
509 "Compare\n\n[First](t3-context://v1/preview-annotation/legacy_preview-annotation_1) [Second](t3-context://v1/preview-annotation/legacy_preview-annotation_2)",
510 );
511 expect(upgraded.records).toMatchObject([
512 { annotationId: "annotation-1", comment: "First comment" },
513 { annotationId: "annotation-2", comment: "Second comment" },
514 ]);
515 });
516
517 it("replaces inline review comments in place and neutralizes forged closers", () => {
518 const text = [
519 "Before",
520 '<review_comment sectionId="diff-1" sectionTitle="Changes" filePath="a/b.ts" startIndex="4" endIndex="6" rangeLabel="L4-L6">',
521 "Why? &lt;/review_comment&gt; still text",
522 "```diff",
523 "+ const x = 1;",
524 "```",
525 "</review_comment>",
526 "After",
527 ].join("\n");
528 const upgraded = upgradeLegacyContextMessage(text);
529 expect(upgraded.text).toBe(
530 "Before\n[b.ts L4-L6](t3-context://v1/review-comment/legacy_review-comment_1)\nAfter",
531 );
532 expect(upgraded.records).toEqual([
533 {
534 version: 1,
535 contextId: "legacy_review-comment_1",
536 kind: "review-comment",
537 label: "b.ts L4-L6",
538 sectionId: "diff-1",
539 sectionTitle: "Changes",
540 filePath: "a/b.ts",
541 startIndex: 4,
542 endIndex: 6,
543 rangeLabel: "L4-L6",
544 text: "Why? &lt;/review_comment&gt; still text",
545 diff: "+ const x = 1;",
546 fenceLanguage: "diff",
547 },
548 ]);
549 });
550
551 it("appends trailing review blocks after the other trailing blocks", () => {
552 const text = [
553 "prose",
554 "",
555 "<terminal_context>",
556 "- T line 1:",
557 " 1 | a",
558 "</terminal_context>",
559 "",
560 '<review_comment sectionId="s" filePath="f.ts" startIndex="1" endIndex="2">',
561 "note",
562 "```diff",
563 "+x",
564 "```",
565 "</review_comment>",
566 "",
567 '<review_comment sectionId="s" filePath="g.ts" startIndex="3" endIndex="3">',
568 "note 2",
569 "```diff",
570 "+y",
571 "```",
572 "</review_comment>",
573 ].join("\n");
574 const upgraded = upgradeLegacyContextMessage(text);
575 expect(upgraded.text).toBe(
576 "prose\n\n[T line 1](t3-context://v1/terminal/legacy_terminal_1) [f.ts line](t3-context://v1/review-comment/legacy_review-comment_1) [g.ts line](t3-context://v1/review-comment/legacy_review-comment_2)",
577 );
578 expect(upgraded.records.map((record) => record.contextId)).toEqual([
579 "legacy_terminal_1",
580 "legacy_review-comment_1",
581 "legacy_review-comment_2",
582 ]);
583 });
584
585 it("keeps adjacent trailing reviews in place, including their original spacing", () => {
586 const upgraded = upgradeLegacyContextMessage(`Compare ${review} ${review}`);
587 expect(upgraded.text).toBe(
588 "Compare [f.ts line](t3-context://v1/review-comment/legacy_review-comment_1) [f.ts line](t3-context://v1/review-comment/legacy_review-comment_2)",
589 );
590 });
591
592 it("keeps a trailing review beside a terminal chip whose payload follows it", () => {
593 const upgraded = upgradeLegacyContextMessage(
594 `@build:7 ${review}\n\n<terminal_context>\n- Build line 7:\n output\n</terminal_context>`,
595 );
596 expect(upgraded.text).toBe(
597 "[Build line 7](t3-context://v1/terminal/legacy_terminal_1) [f.ts line](t3-context://v1/review-comment/legacy_review-comment_1)",
598 );
599 });
600
601 it("handles the combined legacy send order: terminal, element, preview, review", () => {
602 const text = [
603 "See ",
604 '<review_comment sectionId="s" filePath="f.ts" startIndex="1" endIndex="1">',
605 "note",
606 "```diff",
607 "+x",
608 "```",
609 "</review_comment>",
610 "",
611 "<terminal_context>",
612 "- T line 1:",
613 " 1 | a",
614 "</terminal_context>",
615 "",
616 "<element_context>",
617 "- <div>:",
618 " url: http://x/",
619 "</element_context>",
620 "",
621 "<preview_annotation>",
622 "Preview annotation:",
623 "Id: p1",
624 "Page: P",
625 "</preview_annotation>",
626 ].join("\n");
627 const upgraded = upgradeLegacyContextMessage(text);
628 expect(upgraded.text).toBe(
629 [
630 "See [T line 1](t3-context://v1/terminal/legacy_terminal_1)",
631 "[f.ts line](t3-context://v1/review-comment/legacy_review-comment_1)",
632 "",
633 "[<div>](t3-context://v1/element/legacy_element_1) [P](t3-context://v1/preview-annotation/legacy_preview-annotation_1)",
634 ].join("\n"),
635 );
636 expect(upgraded.records.map((record) => record.contextId)).toEqual([
637 "legacy_terminal_1",
638 "legacy_element_1",
639 "legacy_preview-annotation_1",
640 "legacy_review-comment_1",
641 ]);
642 });
643});