Skip to content

Commit b87745f

Browse files
committed
test(use-file-collapse-state): cover toggle, collapse-all, defaults reconcile, reset
Adds unit tests for `useFileCollapseState` covering initial state, toggle/collapse-all/expand-all, resetKey behavior, and how overrides reconcile when the defaults set changes (manually-collapsed files stay collapsed, manually-expanded files stay expanded).
1 parent 8de8e5d commit b87745f

1 file changed

Lines changed: 210 additions & 0 deletions

File tree

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
// @vitest-environment happy-dom
2+
3+
import { act, renderHook } from "@testing-library/react";
4+
import { describe, expect, it } from "vitest";
5+
import { useFileCollapseState } from "../use-file-collapse-state";
6+
7+
const EMPTY_DEFAULTS = new Set<string>();
8+
const RESET_KEY = "owner/repo/1";
9+
10+
describe("useFileCollapseState — basic operations", () => {
11+
describe("initial state", () => {
12+
it("collapses only default-collapsed files", () => {
13+
const defaults = new Set(["deleted.ts"]);
14+
const allPaths = ["a.ts", "b.ts", "deleted.ts"];
15+
const { result } = renderHook(() => useFileCollapseState(defaults, allPaths, RESET_KEY));
16+
expect(result.current.collapsedFiles).toEqual(new Set(["deleted.ts"]));
17+
});
18+
19+
it("starts with no files collapsed when there are no defaults", () => {
20+
const allPaths = ["a.ts", "b.ts"];
21+
const { result } = renderHook(() =>
22+
useFileCollapseState(EMPTY_DEFAULTS, allPaths, RESET_KEY),
23+
);
24+
expect(result.current.collapsedFiles.size).toBe(0);
25+
});
26+
});
27+
28+
describe("toggleFileCollapsed", () => {
29+
it("collapses a non-default file", () => {
30+
const allPaths = ["a.ts", "b.ts"];
31+
const { result } = renderHook(() =>
32+
useFileCollapseState(EMPTY_DEFAULTS, allPaths, RESET_KEY),
33+
);
34+
act(() => result.current.toggleFileCollapsed("a.ts"));
35+
expect(result.current.collapsedFiles).toEqual(new Set(["a.ts"]));
36+
});
37+
38+
it("un-collapses a toggled file on second toggle", () => {
39+
const allPaths = ["a.ts"];
40+
const { result } = renderHook(() =>
41+
useFileCollapseState(EMPTY_DEFAULTS, allPaths, RESET_KEY),
42+
);
43+
act(() => result.current.toggleFileCollapsed("a.ts"));
44+
act(() => result.current.toggleFileCollapsed("a.ts"));
45+
expect(result.current.collapsedFiles.size).toBe(0);
46+
});
47+
48+
it("expands a default-collapsed file", () => {
49+
const defaults = new Set(["deleted.ts"]);
50+
const allPaths = ["a.ts", "deleted.ts"];
51+
const { result } = renderHook(() => useFileCollapseState(defaults, allPaths, RESET_KEY));
52+
act(() => result.current.toggleFileCollapsed("deleted.ts"));
53+
expect(result.current.collapsedFiles.has("deleted.ts")).toBe(false);
54+
});
55+
});
56+
57+
describe("collapseAllFiles", () => {
58+
it("collapses all files when none are collapsed", () => {
59+
const allPaths = ["a.ts", "b.ts", "c.ts"];
60+
const { result } = renderHook(() =>
61+
useFileCollapseState(EMPTY_DEFAULTS, allPaths, RESET_KEY),
62+
);
63+
act(() => result.current.collapseAllFiles());
64+
expect(result.current.collapsedFiles).toEqual(new Set(["a.ts", "b.ts", "c.ts"]));
65+
});
66+
67+
it("collapses all files when some are already default-collapsed", () => {
68+
const defaults = new Set(["deleted.ts"]);
69+
const allPaths = ["a.ts", "b.ts", "deleted.ts"];
70+
const { result } = renderHook(() => useFileCollapseState(defaults, allPaths, RESET_KEY));
71+
act(() => result.current.collapseAllFiles());
72+
expect(result.current.collapsedFiles).toEqual(new Set(["a.ts", "b.ts", "deleted.ts"]));
73+
});
74+
75+
it("collapses all files even after some were manually expanded", () => {
76+
const defaults = new Set(["deleted.ts"]);
77+
const allPaths = ["a.ts", "deleted.ts"];
78+
const { result } = renderHook(() => useFileCollapseState(defaults, allPaths, RESET_KEY));
79+
act(() => result.current.toggleFileCollapsed("deleted.ts"));
80+
act(() => result.current.collapseAllFiles());
81+
expect(result.current.collapsedFiles).toEqual(new Set(["a.ts", "deleted.ts"]));
82+
});
83+
84+
it("is a no-op when all files are already collapsed", () => {
85+
const allPaths = ["a.ts"];
86+
const { result } = renderHook(() =>
87+
useFileCollapseState(EMPTY_DEFAULTS, allPaths, RESET_KEY),
88+
);
89+
act(() => result.current.collapseAllFiles());
90+
const first = result.current.collapsedFiles;
91+
act(() => result.current.collapseAllFiles());
92+
expect(result.current.collapsedFiles).toEqual(first);
93+
});
94+
});
95+
96+
describe("expandAllFiles", () => {
97+
it("expands all files when all are collapsed", () => {
98+
const allPaths = ["a.ts", "b.ts"];
99+
const { result } = renderHook(() =>
100+
useFileCollapseState(EMPTY_DEFAULTS, allPaths, RESET_KEY),
101+
);
102+
act(() => result.current.collapseAllFiles());
103+
act(() => result.current.expandAllFiles());
104+
expect(result.current.collapsedFiles.size).toBe(0);
105+
});
106+
107+
it("expands all files including default-collapsed ones", () => {
108+
const defaults = new Set(["deleted.ts"]);
109+
const allPaths = ["a.ts", "deleted.ts"];
110+
const { result } = renderHook(() => useFileCollapseState(defaults, allPaths, RESET_KEY));
111+
act(() => result.current.expandAllFiles());
112+
expect(result.current.collapsedFiles.size).toBe(0);
113+
});
114+
115+
it("expands all after a mix of manual toggles", () => {
116+
const defaults = new Set(["deleted.ts"]);
117+
const allPaths = ["a.ts", "b.ts", "deleted.ts"];
118+
const { result } = renderHook(() => useFileCollapseState(defaults, allPaths, RESET_KEY));
119+
act(() => result.current.toggleFileCollapsed("a.ts"));
120+
act(() => result.current.toggleFileCollapsed("deleted.ts"));
121+
act(() => result.current.expandAllFiles());
122+
expect(result.current.collapsedFiles.size).toBe(0);
123+
});
124+
});
125+
});
126+
127+
describe("useFileCollapseState — advanced behaviors", () => {
128+
describe("resetKey", () => {
129+
it("clears overrides when resetKey changes", () => {
130+
const allPaths = ["a.ts", "b.ts"];
131+
let resetKey = "owner/repo/1";
132+
const { result, rerender } = renderHook(() =>
133+
useFileCollapseState(EMPTY_DEFAULTS, allPaths, resetKey),
134+
);
135+
136+
act(() => result.current.toggleFileCollapsed("a.ts"));
137+
expect(result.current.collapsedFiles).toEqual(new Set(["a.ts"]));
138+
139+
resetKey = "owner/repo/2";
140+
rerender();
141+
142+
expect(result.current.collapsedFiles.size).toBe(0);
143+
});
144+
});
145+
146+
describe("default changes reconcile overrides", () => {
147+
it("keeps a manually-collapsed file collapsed when it becomes default-collapsed", () => {
148+
const allPaths = ["a.ts", "b.ts"];
149+
let defaults: ReadonlySet<string> = new Set<string>();
150+
const { result, rerender } = renderHook(() =>
151+
useFileCollapseState(defaults, allPaths, RESET_KEY),
152+
);
153+
154+
act(() => result.current.toggleFileCollapsed("a.ts"));
155+
expect(result.current.collapsedFiles.has("a.ts")).toBe(true);
156+
157+
defaults = new Set(["a.ts"]);
158+
rerender();
159+
160+
expect(result.current.collapsedFiles.has("a.ts")).toBe(true);
161+
});
162+
163+
it("keeps a manually-expanded file expanded when it leaves defaults", () => {
164+
const allPaths = ["a.ts", "b.ts"];
165+
let defaults: ReadonlySet<string> = new Set(["a.ts"]);
166+
const { result, rerender } = renderHook(() =>
167+
useFileCollapseState(defaults, allPaths, RESET_KEY),
168+
);
169+
170+
act(() => result.current.toggleFileCollapsed("a.ts"));
171+
expect(result.current.collapsedFiles.has("a.ts")).toBe(false);
172+
173+
defaults = new Set<string>();
174+
rerender();
175+
176+
expect(result.current.collapsedFiles.has("a.ts")).toBe(false);
177+
});
178+
179+
it("preserves overrides for files whose default status did not change", () => {
180+
const allPaths = ["a.ts", "b.ts"];
181+
let defaults: ReadonlySet<string> = new Set<string>();
182+
const { result, rerender } = renderHook(() =>
183+
useFileCollapseState(defaults, allPaths, RESET_KEY),
184+
);
185+
186+
act(() => result.current.toggleFileCollapsed("a.ts"));
187+
act(() => result.current.toggleFileCollapsed("b.ts"));
188+
189+
defaults = new Set(["a.ts"]);
190+
rerender();
191+
192+
expect(result.current.collapsedFiles.has("a.ts")).toBe(true);
193+
expect(result.current.collapsedFiles.has("b.ts")).toBe(true);
194+
});
195+
});
196+
197+
describe("collapse then expand round-trip", () => {
198+
it("returns to fully expanded state after collapse-all then expand-all", () => {
199+
const defaults = new Set(["deleted.ts"]);
200+
const allPaths = ["a.ts", "b.ts", "deleted.ts"];
201+
const { result } = renderHook(() => useFileCollapseState(defaults, allPaths, RESET_KEY));
202+
203+
act(() => result.current.collapseAllFiles());
204+
expect(result.current.collapsedFiles.size).toBe(3);
205+
206+
act(() => result.current.expandAllFiles());
207+
expect(result.current.collapsedFiles.size).toBe(0);
208+
});
209+
});
210+
});

0 commit comments

Comments
 (0)