1import { ThreadId, type WorktreeSetupSnapshot } from "@t3tools/contracts";
2import { resolveVisibleWorktreeSetup } from "@t3tools/client-runtime/worktree-setup";
3import { describe, expect, it } from "vite-plus/test";
4import { resolveWorktreeSetupSnapshot } from "./worktree-setup-state";
5
6const threadId = ThreadId.make("setup-thread");
7const running: WorktreeSetupSnapshot = {
8 threadId,
9 sequence: 1,
10 phase: "running",
11 startedAt: "2026-09-17T00:00:00.000Z",
12 endedAt: null,
13 branch: "feature",
14 baseRef: "main",
15 worktreePath: null,
16 setupScript: null,
17 stages: [],
18 error: null,
19};
20
21describe("mobile worktree setup handoff", () => {
22 it("keeps the setup identity after its card retires so the working header can take over", () => {
23 const done = { ...running, phase: "done" as const, sequence: 2 };
24 const held = resolveWorktreeSetupSnapshot(threadId, done, running);
25 expect(held).toBe(done);
26 expect(
27 resolveVisibleWorktreeSetup({
28 live: held,
29 recorded: null,
30 turnStarted: false,
31 followUpSent: false,
32 }),
33 ).toBe(done);
34 expect(
35 resolveVisibleWorktreeSetup({
36 live: held,
37 recorded: null,
38 turnStarted: true,
39 followUpSent: false,
40 }),
41 ).toBeNull();
42 expect(resolveWorktreeSetupSnapshot(threadId, undefined, held)).toBe(done);
43 });
44
45 it.each(["failed", "cancelled"] as const)(
46 "retains %s details after the stream closes",
47 (phase) => {
48 const settled = { ...running, phase, sequence: 3 };
49 const held = resolveWorktreeSetupSnapshot(threadId, settled, running);
50 expect(resolveWorktreeSetupSnapshot(threadId, null, held)).toBe(settled);
51 expect(resolveWorktreeSetupSnapshot(threadId, running, held)).toBe(settled);
52 expect(
53 resolveVisibleWorktreeSetup({
54 live: held,
55 recorded: null,
56 turnStarted: false,
57 followUpSent: false,
58 }),
59 ).toBe(settled);
60 expect(
61 resolveVisibleWorktreeSetup({
62 live: held,
63 recorded: null,
64 turnStarted: false,
65 followUpSent: true,
66 }),
67 ).toBeNull();
68 },
69 );
70
71 it("does not carry a previous thread's progress across navigation", () => {
72 const otherId = ThreadId.make("other-thread");
73 expect(resolveWorktreeSetupSnapshot(otherId, running, running)).toBeNull();
74 const other = { ...running, threadId: otherId };
75 expect(resolveWorktreeSetupSnapshot(otherId, other, running)).toBe(other);
76 });
77});