1import { scopeThreadRef } from "@t3tools/client-runtime/environment";
2import { EnvironmentId, ThreadId, RunId } from "@t3tools/contracts";
3import { beforeEach, describe, expect, it } from "vite-plus/test";
4
5import { selectThreadDiffPanelSelection, useDiffPanelStore } from "./diffPanelStore";
6
7const THREAD_REF = scopeThreadRef(EnvironmentId.make("environment-1"), ThreadId.make("thread-1"));
8
9describe("diffPanelStore", () => {
10 beforeEach(() =>
11 useDiffPanelStore.setState({
12 byThreadKey: {},
13 branchBaseRefByThreadKey: {},
14 }),
15 );
16
17 it("defaults each thread to Changes without requiring git status", () => {
18 expect(
19 selectThreadDiffPanelSelection(useDiffPanelStore.getState().byThreadKey, THREAD_REF),
20 ).toEqual({ kind: "branch", baseRef: null });
21 });
22
23 it("defaults to Changes before a thread is selected", () => {
24 expect(selectThreadDiffPanelSelection(useDiffPanelStore.getState().byThreadKey, null)).toEqual({
25 kind: "branch",
26 baseRef: null,
27 });
28 });
29
30 it("keeps a custom base when a generic open selects Changes again", () => {
31 const store = useDiffPanelStore.getState();
32 store.selectBranchBaseRef(THREAD_REF, "origin/release");
33 store.selectGitScope(THREAD_REF, "branch");
34 store.selectTurn(THREAD_REF, RunId.make("turn-1"));
35 store.selectGitScope(THREAD_REF, "branch");
36
37 expect(
38 selectThreadDiffPanelSelection(useDiffPanelStore.getState().byThreadKey, THREAD_REF),
39 ).toEqual({ kind: "branch", baseRef: "origin/release" });
40 });
41
42 it("preserves an explicit branch selection", () => {
43 useDiffPanelStore.getState().selectGitScope(THREAD_REF, "branch");
44
45 expect(
46 selectThreadDiffPanelSelection(useDiffPanelStore.getState().byThreadKey, THREAD_REF),
47 ).toEqual({ kind: "branch", baseRef: null });
48 });
49
50 it("clears incompatible selection fields when changing scopes", () => {
51 const store = useDiffPanelStore.getState();
52 store.selectTurn(THREAD_REF, RunId.make("turn-1"), "src/app.ts");
53 store.selectGitScope(THREAD_REF, "unstaged");
54
55 expect(
56 selectThreadDiffPanelSelection(useDiffPanelStore.getState().byThreadKey, THREAD_REF),
57 ).toEqual({ kind: "unstaged" });
58
59 useDiffPanelStore.getState().selectBranchBaseRef(THREAD_REF, " origin/main ");
60 expect(
61 selectThreadDiffPanelSelection(useDiffPanelStore.getState().byThreadKey, THREAD_REF),
62 ).toEqual({ kind: "branch", baseRef: "origin/main" });
63 });
64
65 it("clears a thread's turn and file when selecting working tree without changing another thread's branch base", () => {
66 const otherThreadRef = scopeThreadRef(
67 EnvironmentId.make("environment-1"),
68 ThreadId.make("thread-2"),
69 );
70 const store = useDiffPanelStore.getState();
71 store.selectBranchBaseRef(THREAD_REF, "origin/release");
72 store.selectTurn(THREAD_REF, RunId.make("turn-1"), "src/app.ts");
73 store.selectBranchBaseRef(otherThreadRef, "origin/main");
74
75 store.selectGitScope(THREAD_REF, "unstaged");
76
77 const { byThreadKey } = useDiffPanelStore.getState();
78 expect(selectThreadDiffPanelSelection(byThreadKey, THREAD_REF)).toEqual({ kind: "unstaged" });
79 expect(selectThreadDiffPanelSelection(byThreadKey, otherThreadRef)).toEqual({
80 kind: "branch",
81 baseRef: "origin/main",
82 });
83
84 store.selectGitScope(THREAD_REF, "branch");
85 expect(
86 selectThreadDiffPanelSelection(useDiffPanelStore.getState().byThreadKey, THREAD_REF),
87 ).toEqual({ kind: "branch", baseRef: "origin/release" });
88 });
89
90 it("increments the reveal request when opening the same turn file again", () => {
91 const turnId = RunId.make("turn-1");
92 useDiffPanelStore.getState().selectTurn(THREAD_REF, turnId, "src/app.ts");
93 useDiffPanelStore.getState().selectTurn(THREAD_REF, turnId, "src/app.ts");
94
95 expect(
96 selectThreadDiffPanelSelection(useDiffPanelStore.getState().byThreadKey, THREAD_REF),
97 ).toEqual({ kind: "turn", turnId, filePath: "src/app.ts", revealRequestId: 2 });
98 });
99
100 it("restores the selected branch base after visiting another scope", () => {
101 useDiffPanelStore.getState().selectBranchBaseRef(THREAD_REF, "origin/main");
102 useDiffPanelStore.getState().selectGitScope(THREAD_REF, "unstaged");
103 useDiffPanelStore.getState().selectGitScope(THREAD_REF, "branch");
104
105 expect(
106 selectThreadDiffPanelSelection(useDiffPanelStore.getState().byThreadKey, THREAD_REF),
107 ).toEqual({ kind: "branch", baseRef: "origin/main" });
108 });
109
110 it("reconciles a missing turn selection to the latest available turn", () => {
111 const missingTurnId = RunId.make("turn-missing");
112 const latestTurnId = RunId.make("turn-latest");
113 useDiffPanelStore.getState().selectTurn(THREAD_REF, missingTurnId, "src/app.ts");
114 useDiffPanelStore.getState().reconcileTurnSelection(THREAD_REF, [latestTurnId]);
115
116 expect(
117 selectThreadDiffPanelSelection(useDiffPanelStore.getState().byThreadKey, THREAD_REF),
118 ).toEqual({
119 kind: "turn",
120 turnId: latestTurnId,
121 filePath: "src/app.ts",
122 revealRequestId: 1,
123 });
124 });
125});