1import { RunId } from "@t3tools/contracts";
2import { renderToStaticMarkup } from "react-dom/server";
3import { describe, expect, it } from "vite-plus/test";
4
5import { ChangedFilesCard, ChangedFilesTree } from "./ChangedFilesTree";
6
7describe("ChangedFilesCard", () => {
8 it("keeps its compact header sticky while preserving singular labels", () => {
9 const markup = renderToStaticMarkup(
10 <ChangedFilesCard
11 runId={RunId.make("run-1")}
12 files={[{ path: "README.md", kind: "modified", additions: 2, deletions: 1 }]}
13 allDirectoriesExpanded
14 resolvedTheme="light"
15 onToggleAllDirectories={() => {}}
16 onOpenTurnDiff={() => {}}
17 />,
18 );
19
20 expect(markup).toContain('data-changed-files-state="tree"');
21 expect(markup).toContain('aria-label="Open diff"');
22 expect(markup).toContain('role="group" aria-label="2 additions, 1 deletions"');
23 expect(markup).toContain("1 changed file");
24 expect(markup).not.toContain("1 changed files");
25 });
26
27 it("shows collapsed folders and root files together", () => {
28 const markup = renderToStaticMarkup(
29 <ChangedFilesCard
30 runId={RunId.make("run-1")}
31 files={[
32 { path: "apps/web/src/App.tsx", kind: "modified", additions: 120, deletions: 20 },
33 { path: "apps/web/src/App.test.tsx", kind: "modified", additions: 30, deletions: 2 },
34 {
35 path: "packages/shared/src/git.ts",
36 kind: "modified",
37 additions: 15,
38 deletions: 4,
39 },
40 { path: "README.md", kind: "modified", additions: 3, deletions: 0 },
41 ]}
42 allDirectoriesExpanded={false}
43 resolvedTheme="light"
44 onToggleAllDirectories={() => {}}
45 onOpenTurnDiff={() => {}}
46 />,
47 );
48
49 expect(markup).toContain('data-changed-files-state="tree"');
50 expect(markup).toContain('aria-expanded="false"');
51 expect(markup).toContain("apps/web/src");
52 expect(markup).not.toContain("App.tsx");
53 expect(markup).toContain("packages/shared/src");
54 expect(markup).not.toContain("git.ts");
55 expect(markup).toContain("README.md");
56 expect(markup).not.toContain("Show all");
57 expect(markup).not.toContain("App.test.tsx");
58 });
59
60 it("keeps the folder tree visible when folders are collapsed", () => {
61 const markup = renderToStaticMarkup(
62 <ChangedFilesCard
63 runId={RunId.make("run-1")}
64 files={[{ path: "apps/web/src/App.tsx", kind: "modified", additions: 120, deletions: 20 }]}
65 allDirectoriesExpanded={false}
66 resolvedTheme="light"
67 onToggleAllDirectories={() => {}}
68 onOpenTurnDiff={() => {}}
69 />,
70 );
71
72 expect(markup).toContain('data-changed-files-state="tree"');
73 expect(markup).toContain("1 changed file");
74 expect(markup).toContain("apps/web/src");
75 expect(markup).not.toContain("Show all");
76 expect(markup).not.toContain("App.tsx");
77 });
78});
79
80describe("ChangedFilesTree", () => {
81 it.each([
82 {
83 name: "a compacted single-chain directory",
84 files: [
85 { path: "apps/web/src/index.ts", kind: "modified", additions: 2, deletions: 1 },
86 { path: "apps/web/src/main.ts", kind: "modified", additions: 3, deletions: 0 },
87 ],
88 visibleLabels: ["apps/web/src"],
89 hiddenLabels: ["index.ts", "main.ts"],
90 },
91 {
92 name: "a branch point after a compacted prefix",
93 files: [
94 {
95 path: "apps/server/src/git/Layers/GitCore.ts",
96 kind: "modified",
97 additions: 4,
98 deletions: 3,
99 },
100 {
101 path: "apps/server/src/provider/Layers/CodexAdapter.ts",
102 kind: "modified",
103 additions: 7,
104 deletions: 2,
105 },
106 ],
107 visibleLabels: ["apps/server/src"],
108 hiddenLabels: ["git", "provider", "GitCore.ts", "CodexAdapter.ts"],
109 },
110 {
111 name: "mixed root files and nested compacted directories",
112 files: [
113 { path: "README.md", kind: "modified", additions: 1, deletions: 0 },
114 { path: "packages/shared/src/git.ts", kind: "modified", additions: 8, deletions: 2 },
115 {
116 path: "packages/contracts/src/orchestration.ts",
117 kind: "modified",
118 additions: 13,
119 deletions: 3,
120 },
121 ],
122 visibleLabels: ["README.md", "packages"],
123 hiddenLabels: ["shared/src", "contracts/src", "git.ts", "orchestration.ts"],
124 },
125 ])(
126 "renders $name collapsed on the first render when collapse-all is active",
127 ({ files, visibleLabels, hiddenLabels }) => {
128 const markup = renderToStaticMarkup(
129 <ChangedFilesTree
130 runId={RunId.make("turn-1")}
131 files={files}
132 allDirectoriesExpanded={false}
133 resolvedTheme="light"
134 onOpenTurnDiff={() => {}}
135 />,
136 );
137
138 for (const label of visibleLabels) {
139 expect(markup).toContain(label);
140 }
141 for (const label of hiddenLabels) {
142 expect(markup).not.toContain(label);
143 }
144 },
145 );
146
147 it.each([
148 {
149 name: "a compacted single-chain directory",
150 files: [
151 { path: "apps/web/src/index.ts", kind: "modified", additions: 2, deletions: 1 },
152 { path: "apps/web/src/main.ts", kind: "modified", additions: 3, deletions: 0 },
153 ],
154 visibleLabels: ["apps/web/src", "index.ts", "main.ts"],
155 },
156 {
157 name: "a branch point after a compacted prefix",
158 files: [
159 {
160 path: "apps/server/src/git/Layers/GitCore.ts",
161 kind: "modified",
162 additions: 4,
163 deletions: 3,
164 },
165 {
166 path: "apps/server/src/provider/Layers/CodexAdapter.ts",
167 kind: "modified",
168 additions: 7,
169 deletions: 2,
170 },
171 ],
172 visibleLabels: [
173 "apps/server/src",
174 "git/Layers",
175 "provider/Layers",
176 "GitCore.ts",
177 "CodexAdapter.ts",
178 ],
179 },
180 {
181 name: "mixed root files and nested compacted directories",
182 files: [
183 { path: "README.md", kind: "modified", additions: 1, deletions: 0 },
184 { path: "packages/shared/src/git.ts", kind: "modified", additions: 8, deletions: 2 },
185 {
186 path: "packages/contracts/src/orchestration.ts",
187 kind: "modified",
188 additions: 13,
189 deletions: 3,
190 },
191 ],
192 visibleLabels: [
193 "README.md",
194 "packages",
195 "shared/src",
196 "contracts/src",
197 "git.ts",
198 "orchestration.ts",
199 ],
200 },
201 ])(
202 "renders $name expanded on the first render when expand-all is active",
203 ({ files, visibleLabels }) => {
204 const markup = renderToStaticMarkup(
205 <ChangedFilesTree
206 runId={RunId.make("turn-1")}
207 files={files}
208 allDirectoriesExpanded
209 resolvedTheme="light"
210 onOpenTurnDiff={() => {}}
211 />,
212 );
213
214 for (const label of visibleLabels) {
215 expect(markup).toContain(label);
216 }
217 },
218 );
219});