1import type { EnvironmentId, ScopedThreadRef, ThreadId } from "@t3tools/contracts";
2import { beforeEach, describe, expect, it, vi } from "vite-plus/test";
3
4import type { DraftId } from "../composerDraftStore";
5import {
6 beginSnapShotAnimation,
7 dismissAllSnapShotAnimations,
8 finishSnapShotAnimation,
9 getPendingSnapShotAnimations,
10 pendingSnapShotAnimationIdsForTarget,
11 scheduleSnapShotAnimationDestination,
12 shouldAnimateSnapShotArrival,
13 updateSnapShotAnimationSource,
14} from "./snapShotAnimation";
15
16describe("window capture animation", () => {
17 beforeEach(() => dismissAllSnapShotAnimations());
18
19 it("keeps each reserved composer slot with its target and source", () => {
20 const draft = "draft-1" as DraftId;
21 const thread = {
22 environmentId: "environment-1" as EnvironmentId,
23 threadId: "thread-1" as ThreadId,
24 } satisfies ScopedThreadRef;
25 const source = {
26 kind: "snap-shot" as const,
27 capturedAt: "2026-08-29T00:00:00.000Z",
28 appName: "T3 Code",
29 windowTitle: "Capture animation",
30 };
31
32 beginSnapShotAnimation("capture-1", draft);
33 updateSnapShotAnimationSource("capture-1", source);
34
35 const pending = getPendingSnapShotAnimations();
36 expect(pending[0]).toMatchObject({ target: draft });
37 expect(pendingSnapShotAnimationIdsForTarget(pending, draft)).toEqual(["capture-1"]);
38 expect(pendingSnapShotAnimationIdsForTarget(pending, thread)).toEqual([]);
39 expect(pending[0]?.source).toEqual(source);
40 });
41
42 it("keeps one card through Strict Mode and the attachment handoff", async () => {
43 const start = vi.fn();
44 beginSnapShotAnimation("capture-1", "draft-1" as DraftId);
45
46 const stopFirstSetup = scheduleSnapShotAnimationDestination("capture-1", start);
47 stopFirstSetup();
48 const stopPlaceholder = scheduleSnapShotAnimationDestination("capture-1", start);
49 await Promise.resolve();
50 expect(start).toHaveBeenCalledTimes(1);
51 stopPlaceholder();
52 const stopAttachment = scheduleSnapShotAnimationDestination("capture-1", start);
53 await Promise.resolve();
54
55 expect(start).toHaveBeenCalledTimes(2);
56 expect(getPendingSnapShotAnimations()).toHaveLength(1);
57 finishSnapShotAnimation("capture-1");
58 stopAttachment();
59 });
60
61 it("uses composer arrival feedback only for a fresh capture", () => {
62 const now = Date.parse("2026-09-01T12:00:05.000Z");
63
64 expect(shouldAnimateSnapShotArrival("2026-09-01T12:00:02.000Z", now)).toBe(true);
65 expect(shouldAnimateSnapShotArrival("2026-09-01T11:59:00.000Z", now)).toBe(false);
66 expect(shouldAnimateSnapShotArrival("invalid", now)).toBe(false);
67 });
68});