1import { ThreadId } from "@t3tools/contracts";
2import { describe, expect, it } from "vite-plus/test";
3
4import { buildMessageContext } from "~/lib/composerContextRecords";
5import {
6 composerContextRecordsFromDraft,
7 uploadedContextRecordFromDraft,
8} from "./composerContextPresentation";
9
10describe("composerContextRecordsFromDraft", () => {
11 it("pairs a picked element with its crop, which the draft keeps as an image", () => {
12 const annotation = {
13 id: "pick-1",
14 pageUrl: "http://localhost:3000/",
15 pageTitle: "Example",
16 comment: "",
17 elements: [],
18 regions: [],
19 strokes: [],
20 styleChanges: [],
21 // The draft stores annotations without their data URL.
22 screenshot: {
23 dataUrl: "",
24 width: 10,
25 height: 10,
26 cropRect: { x: 0, y: 0, width: 10, height: 10 },
27 },
28 createdAt: "2026-10-08T00:00:00.000Z",
29 };
30 const crop = {
31 type: "image" as const,
32 id: "pick-1",
33 name: "preview-annotation-pick-1.png",
34 mimeType: "image/png",
35 sizeBytes: 1,
36 previewUrl: "blob:crop",
37 file: new File(["x"], "preview-annotation-pick-1.png"),
38 };
39
40 expect(
41 composerContextRecordsFromDraft({
42 terminalContexts: [],
43 previewAnnotations: [annotation],
44 images: [crop],
45 }).get("preview-annotation_pick-1"),
46 ).toMatchObject({ kind: "preview-annotation", screenshot: { previewUrl: "blob:crop" } });
47 });
48
49 it("recovers the uploaded record when clipboard data points at an attachment already in the draft", () => {
50 const file = {
51 type: "file" as const,
52 id: "file-1",
53 name: "file.txt",
54 mimeType: "text/plain",
55 sizeBytes: 4,
56 file: new File(["test"], "file.txt"),
57 uploadedAttachmentId: "attachment-1",
58 };
59 const draftRecord = composerContextRecordsFromDraft({
60 terminalContexts: [],
61 files: [file],
62 }).get("file_file-1");
63
64 expect(draftRecord && uploadedContextRecordFromDraft(draftRecord)).toMatchObject({
65 kind: "file",
66 contextId: "file_file-1",
67 attachmentId: "attachment-1",
68 });
69 });
70
71 it("resolves each wire reference to its own backing draft even when producer ids collide", () => {
72 const id = "same.id:1";
73 const terminal = {
74 id,
75 threadId: ThreadId.make("t1"),
76 terminalId: "default",
77 terminalLabel: "Terminal",
78 lineStart: 1,
79 lineEnd: 1,
80 text: "output",
81 createdAt: "2026-01-01T00:00:00.000Z",
82 };
83 const image = {
84 type: "image" as const,
85 id,
86 name: "shot.png",
87 mimeType: "image/png",
88 sizeBytes: 1,
89 file: new File(["x"], "shot.png"),
90 previewUrl: "blob:shot",
91 };
92 const file = {
93 type: "file" as const,
94 id,
95 name: "file.txt",
96 mimeType: "text/plain",
97 sizeBytes: 1,
98 file: null,
99 };
100 const draftRecords = composerContextRecordsFromDraft({
101 terminalContexts: [terminal],
102 images: [image],
103 files: [file],
104 });
105 const message = buildMessageContext({
106 terminalContexts: [terminal],
107 reviewComments: [],
108 previewAnnotations: [],
109 attachments: [
110 { attachment: image, attachmentId: "uploaded-image" },
111 { attachment: file, attachmentId: "uploaded-file" },
112 ],
113 })!;
114 expect(draftRecords.size).toBe(3);
115 for (const record of message.records) {
116 expect(draftRecords.get(record.contextId)?.kind).toBe(record.kind);
117 }
118 });
119});