1import type { PreviewAnnotationPayload } from "@t3tools/contracts";
2import { describe, expect, it } from "vite-plus/test";
3
4import type { ComposerFileAttachment, ComposerImageAttachment } from "../../composerDraftStore";
5import {
6 buildMessageContext,
7 fileContextReference,
8 previewAnnotationContextId,
9} from "../../lib/composerContextRecords";
10import {
11 reconcileAttachmentContextReferences,
12 type RetainedAttachmentContextPayloads,
13} from "./composerContextUndo";
14
15const binary = new File(["binary"], "context.png", { type: "image/png" });
16const file = {
17 type: "file",
18 id: "file-1",
19 name: "notes.txt",
20 mimeType: "text/plain",
21 sizeBytes: 5,
22 file: new File(["notes"], "notes.txt", { type: "text/plain" }),
23} satisfies ComposerFileAttachment;
24const image = {
25 type: "image",
26 id: "annotation-1",
27 name: "annotation.png",
28 mimeType: "image/png",
29 sizeBytes: binary.size,
30 previewUrl: "blob:annotation",
31 file: binary,
32} satisfies ComposerImageAttachment;
33const annotation = {
34 id: "annotation-1",
35 pageUrl: "https://example.com",
36 pageTitle: "Example",
37 comment: "Fix this",
38 elements: [],
39 regions: [],
40 strokes: [],
41 styleChanges: [],
42 screenshot: {
43 dataUrl: "data:image/png;base64,YmluYXJ5",
44 width: 1,
45 height: 1,
46 cropRect: { x: 0, y: 0, width: 1, height: 1 },
47 },
48 createdAt: "2026-01-01T00:00:00.000Z",
49} satisfies PreviewAnnotationPayload;
50
51function retention(): RetainedAttachmentContextPayloads {
52 return { files: new Map(), previewAnnotations: new Map() };
53}
54
55describe("reconcileAttachmentContextReferences", () => {
56 it("restores file bytes after deleting and undoing its chip", () => {
57 const retained = retention();
58 const removed = reconcileAttachmentContextReferences({
59 referencedContextIds: new Set(),
60 files: [file],
61 images: [],
62 previewAnnotations: [],
63 retained,
64 });
65 expect(removed.filesToRemove).toEqual(["file-1"]);
66
67 const restored = reconcileAttachmentContextReferences({
68 referencedContextIds: new Set([fileContextReference(file).contextId]),
69 files: [],
70 images: [],
71 previewAnnotations: [],
72 retained,
73 });
74 expect(restored.filesToRestore).toEqual([file]);
75 expect(restored.filesToRestore[0]?.file).toBe(file.file);
76 expect(
77 buildMessageContext({
78 terminalContexts: [],
79 reviewComments: [],
80 previewAnnotations: [],
81 attachments: [{ attachment: restored.filesToRestore[0]!, attachmentId: "uploaded-file" }],
82 })?.records,
83 ).toEqual([
84 expect.objectContaining({
85 kind: "file",
86 contextId: "file_file-1",
87 attachmentId: "uploaded-file",
88 }),
89 ]);
90 });
91
92 it("restores an annotation and its screenshot attachment after undo", () => {
93 const retained = retention();
94 const removed = reconcileAttachmentContextReferences({
95 referencedContextIds: new Set(),
96 files: [],
97 images: [image],
98 previewAnnotations: [annotation],
99 retained,
100 });
101 expect(removed.annotationIdsToRemove).toEqual(["annotation-1"]);
102
103 const restored = reconcileAttachmentContextReferences({
104 referencedContextIds: new Set([previewAnnotationContextId(annotation.id)]),
105 files: [],
106 images: [],
107 previewAnnotations: [],
108 retained,
109 });
110 expect(restored.annotationsToRestore).toEqual([{ annotation, image }]);
111 expect(restored.annotationsToRestore[0]?.annotation.screenshot?.dataUrl).toBe(
112 "data:image/png;base64,YmluYXJ5",
113 );
114 expect(restored.annotationsToRestore[0]?.image?.file).toBe(binary);
115 expect(
116 buildMessageContext({
117 terminalContexts: [],
118 reviewComments: [],
119 previewAnnotations: restored.annotationsToRestore.map((entry) => entry.annotation),
120 attachments: [
121 {
122 attachment: restored.annotationsToRestore[0]!.image!,
123 attachmentId: "uploaded-screenshot",
124 },
125 ],
126 })?.records,
127 ).toEqual([
128 expect.objectContaining({
129 kind: "preview-annotation",
130 screenshotContextId: "image_annotation-1",
131 }),
132 expect.objectContaining({
133 kind: "image",
134 contextId: "image_annotation-1",
135 attachmentId: "uploaded-screenshot",
136 }),
137 ]);
138 });
139});