@akiojin/gwt 2.9.1 → 2.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -0
- package/dist/cli/ui/components/App.d.ts.map +1 -1
- package/dist/cli/ui/components/App.js +4 -37
- package/dist/cli/ui/components/App.js.map +1 -1
- package/dist/cli/ui/components/common/Input.d.ts +1 -1
- package/dist/cli/ui/components/screens/BranchListScreen.d.ts +1 -2
- package/dist/cli/ui/components/screens/BranchListScreen.d.ts.map +1 -1
- package/dist/cli/ui/components/screens/BranchListScreen.js +19 -14
- package/dist/cli/ui/components/screens/BranchListScreen.js.map +1 -1
- package/dist/cli/ui/components/screens/ModelSelectorScreen.d.ts.map +1 -1
- package/dist/cli/ui/components/screens/ModelSelectorScreen.js +6 -6
- package/dist/cli/ui/components/screens/ModelSelectorScreen.js.map +1 -1
- package/dist/cli/ui/components/screens/PRCleanupScreen.d.ts.map +1 -1
- package/dist/cli/ui/components/screens/PRCleanupScreen.js +0 -3
- package/dist/cli/ui/components/screens/PRCleanupScreen.js.map +1 -1
- package/dist/cli/ui/hooks/useGitData.d.ts.map +1 -1
- package/dist/cli/ui/hooks/useGitData.js +43 -2
- package/dist/cli/ui/hooks/useGitData.js.map +1 -1
- package/dist/cli/ui/types.d.ts +16 -4
- package/dist/cli/ui/types.d.ts.map +1 -1
- package/dist/cli/ui/utils/branchFormatter.d.ts.map +1 -1
- package/dist/cli/ui/utils/branchFormatter.js +124 -15
- package/dist/cli/ui/utils/branchFormatter.js.map +1 -1
- package/dist/cli/ui/utils/modelOptions.d.ts.map +1 -1
- package/dist/cli/ui/utils/modelOptions.js.map +1 -1
- package/dist/client/assets/{index-CNWntAlF.js → index-Dl798X5w.js} +1 -1
- package/dist/client/index.html +1 -1
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js.map +1 -1
- package/dist/git.d.ts +6 -0
- package/dist/git.d.ts.map +1 -1
- package/dist/git.js +40 -5
- package/dist/git.js.map +1 -1
- package/dist/web/client/src/components/BranchGraph.js +1 -1
- package/dist/web/client/src/components/BranchGraph.js.map +1 -1
- package/dist/web/client/src/pages/BranchDetailPage.d.ts.map +1 -1
- package/dist/web/client/src/pages/BranchDetailPage.js +8 -3
- package/dist/web/client/src/pages/BranchDetailPage.js.map +1 -1
- package/dist/web/server/routes/sessions.d.ts.map +1 -1
- package/dist/web/server/routes/sessions.js +4 -2
- package/dist/web/server/routes/sessions.js.map +1 -1
- package/dist/worktree.d.ts.map +1 -1
- package/dist/worktree.js +69 -62
- package/dist/worktree.js.map +1 -1
- package/package.json +1 -1
- package/src/cli/ui/__tests__/acceptance/navigation.acceptance.test.tsx +9 -17
- package/src/cli/ui/__tests__/components/App.protected-branch.test.tsx +77 -83
- package/src/cli/ui/__tests__/components/App.shortcuts.test.tsx +107 -160
- package/src/cli/ui/__tests__/components/App.test.tsx +108 -84
- package/src/cli/ui/__tests__/components/ModelSelectorScreen.initial.test.tsx +12 -5
- package/src/cli/ui/__tests__/components/common/Confirm.test.tsx +3 -2
- package/src/cli/ui/__tests__/components/common/ErrorBoundary.test.tsx +3 -2
- package/src/cli/ui/__tests__/components/common/Input.test.tsx +3 -2
- package/src/cli/ui/__tests__/components/common/LoadingIndicator.test.tsx +15 -14
- package/src/cli/ui/__tests__/components/common/Select.memo.test.tsx +3 -3
- package/src/cli/ui/__tests__/components/common/Select.test.tsx +1 -4
- package/src/cli/ui/__tests__/components/parts/Footer.test.tsx +3 -2
- package/src/cli/ui/__tests__/components/parts/Header.test.tsx +3 -2
- package/src/cli/ui/__tests__/components/parts/ScrollableList.test.tsx +3 -2
- package/src/cli/ui/__tests__/components/parts/Stats.test.tsx +3 -2
- package/src/cli/ui/__tests__/components/screens/AIToolSelectorScreen.test.tsx +3 -2
- package/src/cli/ui/__tests__/components/screens/BranchCreatorScreen.test.tsx +3 -2
- package/src/cli/ui/__tests__/components/screens/BranchListScreen.test.tsx +31 -41
- package/src/cli/ui/__tests__/components/screens/ExecutionModeSelectorScreen.test.tsx +3 -2
- package/src/cli/ui/__tests__/components/screens/PRCleanupScreen.test.tsx +3 -2
- package/src/cli/ui/__tests__/components/screens/SessionSelectorScreen.test.tsx +3 -2
- package/src/cli/ui/__tests__/hooks/useScreenState.test.ts +18 -17
- package/src/cli/ui/__tests__/hooks/useTerminalSize.test.ts +3 -2
- package/src/cli/ui/__tests__/integration/edgeCases.test.tsx +61 -41
- package/src/cli/ui/__tests__/integration/navigation.test.tsx +15 -10
- package/src/cli/ui/__tests__/integration/realtimeUpdate.test.tsx +3 -2
- package/src/cli/ui/__tests__/performance/branchList.performance.test.tsx +0 -4
- package/src/cli/ui/__tests__/performance/useMemoOptimization.test.tsx +3 -5
- package/src/cli/ui/__tests__/utils/branchFormatter.test.ts +24 -22
- package/src/cli/ui/components/App.tsx +5 -63
- package/src/cli/ui/components/common/Input.tsx +1 -1
- package/src/cli/ui/components/screens/BranchListScreen.tsx +32 -22
- package/src/cli/ui/components/screens/ModelSelectorScreen.tsx +46 -49
- package/src/cli/ui/components/screens/PRCleanupScreen.tsx +0 -3
- package/src/cli/ui/hooks/useGitData.ts +59 -1
- package/src/cli/ui/screens/__tests__/BranchActionSelectorScreen.test.tsx +3 -2
- package/src/cli/ui/types.ts +24 -5
- package/src/cli/ui/utils/branchFormatter.ts +123 -15
- package/src/cli/ui/utils/modelOptions.test.ts +4 -6
- package/src/cli/ui/utils/modelOptions.ts +1 -2
- package/src/config/index.ts +2 -1
- package/src/git.ts +56 -16
- package/src/index.test.ts +1 -1
- package/src/web/client/src/components/BranchGraph.tsx +1 -1
- package/src/web/client/src/pages/BranchDetailPage.tsx +8 -3
- package/src/web/server/routes/sessions.ts +12 -5
- package/src/worktree.ts +80 -91
- package/dist/cli/ui/components/screens/WorktreeManagerScreen.d.ts +0 -20
- package/dist/cli/ui/components/screens/WorktreeManagerScreen.d.ts.map +0 -1
- package/dist/cli/ui/components/screens/WorktreeManagerScreen.js +0 -65
- package/dist/cli/ui/components/screens/WorktreeManagerScreen.js.map +0 -1
- package/src/cli/ui/__tests__/components/screens/WorktreeManagerScreen.test.tsx +0 -151
- package/src/cli/ui/components/screens/WorktreeManagerScreen.tsx +0 -117
|
@@ -27,8 +27,9 @@ describe("BranchListScreen", () => {
|
|
|
27
27
|
vi.useFakeTimers();
|
|
28
28
|
// Setup happy-dom
|
|
29
29
|
const window = new Window();
|
|
30
|
-
globalThis.window = window as
|
|
31
|
-
globalThis.document =
|
|
30
|
+
globalThis.window = window as unknown as typeof globalThis.window;
|
|
31
|
+
globalThis.document =
|
|
32
|
+
window.document as unknown as typeof globalThis.document;
|
|
32
33
|
});
|
|
33
34
|
|
|
34
35
|
afterEach(() => {
|
|
@@ -142,7 +143,7 @@ describe("BranchListScreen", () => {
|
|
|
142
143
|
|
|
143
144
|
it("should display loading indicator after the configured delay", async () => {
|
|
144
145
|
const onSelect = vi.fn();
|
|
145
|
-
const {
|
|
146
|
+
const { getByText } = render(
|
|
146
147
|
<BranchListScreen
|
|
147
148
|
branches={mockBranches}
|
|
148
149
|
stats={mockStats}
|
|
@@ -153,8 +154,8 @@ describe("BranchListScreen", () => {
|
|
|
153
154
|
);
|
|
154
155
|
|
|
155
156
|
await act(async () => {
|
|
156
|
-
if (typeof
|
|
157
|
-
|
|
157
|
+
if (typeof vi.advanceTimersByTime === "function") {
|
|
158
|
+
vi.advanceTimersByTime(10);
|
|
158
159
|
} else {
|
|
159
160
|
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
160
161
|
}
|
|
@@ -299,7 +300,7 @@ describe("BranchListScreen", () => {
|
|
|
299
300
|
);
|
|
300
301
|
});
|
|
301
302
|
|
|
302
|
-
const frame = renderResult
|
|
303
|
+
const frame = renderResult?.lastFrame() ?? "";
|
|
303
304
|
expect(frame).toContain("\u001b[46m"); // cyan background ANSI code
|
|
304
305
|
});
|
|
305
306
|
|
|
@@ -356,7 +357,7 @@ describe("BranchListScreen", () => {
|
|
|
356
357
|
);
|
|
357
358
|
});
|
|
358
359
|
|
|
359
|
-
const frame = renderResult
|
|
360
|
+
const frame = renderResult?.lastFrame() ?? "";
|
|
360
361
|
const timestampLines = frame
|
|
361
362
|
.split("\n")
|
|
362
363
|
.map((line) => stripControlSequences(stripAnsi(line)))
|
|
@@ -417,10 +418,9 @@ describe("BranchListScreen", () => {
|
|
|
417
418
|
expect(container.textContent).toContain("(press f to filter)");
|
|
418
419
|
|
|
419
420
|
// Press 'f' key
|
|
420
|
-
const fKeyEvent = new
|
|
421
|
-
"
|
|
422
|
-
|
|
423
|
-
);
|
|
421
|
+
const fKeyEvent = new globalThis.window.KeyboardEvent("keydown", {
|
|
422
|
+
key: "f",
|
|
423
|
+
});
|
|
424
424
|
document.dispatchEvent(fKeyEvent);
|
|
425
425
|
|
|
426
426
|
// Filter input should be active (placeholder visible)
|
|
@@ -439,17 +439,15 @@ describe("BranchListScreen", () => {
|
|
|
439
439
|
);
|
|
440
440
|
|
|
441
441
|
// Enter filter mode first
|
|
442
|
-
const fKeyEvent = new
|
|
443
|
-
"
|
|
444
|
-
|
|
445
|
-
);
|
|
442
|
+
const fKeyEvent = new globalThis.window.KeyboardEvent("keydown", {
|
|
443
|
+
key: "f",
|
|
444
|
+
});
|
|
446
445
|
document.dispatchEvent(fKeyEvent);
|
|
447
446
|
|
|
448
447
|
// Press Escape
|
|
449
|
-
const escKeyEvent = new
|
|
450
|
-
"
|
|
451
|
-
|
|
452
|
-
);
|
|
448
|
+
const escKeyEvent = new globalThis.window.KeyboardEvent("keydown", {
|
|
449
|
+
key: "Escape",
|
|
450
|
+
});
|
|
453
451
|
document.dispatchEvent(escKeyEvent);
|
|
454
452
|
|
|
455
453
|
// Should return to branch selection mode
|
|
@@ -473,7 +471,7 @@ describe("BranchListScreen", () => {
|
|
|
473
471
|
);
|
|
474
472
|
});
|
|
475
473
|
|
|
476
|
-
const frame = renderResult
|
|
474
|
+
const frame = renderResult?.lastFrame() ?? "";
|
|
477
475
|
// Should contain cyan background (cursor highlight) even in filter mode
|
|
478
476
|
expect(frame).toContain("\u001b[46m");
|
|
479
477
|
});
|
|
@@ -570,10 +568,9 @@ describe("BranchListScreen", () => {
|
|
|
570
568
|
);
|
|
571
569
|
|
|
572
570
|
// Enter filter mode
|
|
573
|
-
const fKeyEvent = new
|
|
574
|
-
"
|
|
575
|
-
|
|
576
|
-
);
|
|
571
|
+
const fKeyEvent = new globalThis.window.KeyboardEvent("keydown", {
|
|
572
|
+
key: "f",
|
|
573
|
+
});
|
|
577
574
|
document.dispatchEvent(fKeyEvent);
|
|
578
575
|
|
|
579
576
|
// Type something in filter
|
|
@@ -584,10 +581,9 @@ describe("BranchListScreen", () => {
|
|
|
584
581
|
}
|
|
585
582
|
|
|
586
583
|
// Press Escape (should clear query first)
|
|
587
|
-
const escKeyEvent = new
|
|
588
|
-
"
|
|
589
|
-
|
|
590
|
-
);
|
|
584
|
+
const escKeyEvent = new globalThis.window.KeyboardEvent("keydown", {
|
|
585
|
+
key: "Escape",
|
|
586
|
+
});
|
|
591
587
|
document.dispatchEvent(escKeyEvent);
|
|
592
588
|
|
|
593
589
|
// Filter input should still be visible, but query cleared
|
|
@@ -608,17 +604,15 @@ describe("BranchListScreen", () => {
|
|
|
608
604
|
);
|
|
609
605
|
|
|
610
606
|
// Enter filter mode
|
|
611
|
-
const fKeyEvent = new
|
|
612
|
-
"
|
|
613
|
-
|
|
614
|
-
);
|
|
607
|
+
const fKeyEvent = new globalThis.window.KeyboardEvent("keydown", {
|
|
608
|
+
key: "f",
|
|
609
|
+
});
|
|
615
610
|
document.dispatchEvent(fKeyEvent);
|
|
616
611
|
|
|
617
612
|
// Press Escape with empty query (should exit filter mode)
|
|
618
|
-
const escKeyEvent = new
|
|
619
|
-
"
|
|
620
|
-
|
|
621
|
-
);
|
|
613
|
+
const escKeyEvent = new globalThis.window.KeyboardEvent("keydown", {
|
|
614
|
+
key: "Escape",
|
|
615
|
+
});
|
|
622
616
|
document.dispatchEvent(escKeyEvent);
|
|
623
617
|
|
|
624
618
|
// Should return to branch selection mode
|
|
@@ -641,9 +635,8 @@ describe("BranchListScreen", () => {
|
|
|
641
635
|
expect(container.textContent).toContain("feature/test");
|
|
642
636
|
});
|
|
643
637
|
|
|
644
|
-
it("should disable other key bindings (
|
|
638
|
+
it("should disable other key bindings (c, r) while typing in filter", () => {
|
|
645
639
|
const onSelect = vi.fn();
|
|
646
|
-
const onNavigate = vi.fn();
|
|
647
640
|
const onCleanupCommand = vi.fn();
|
|
648
641
|
const onRefresh = vi.fn();
|
|
649
642
|
|
|
@@ -652,7 +645,6 @@ describe("BranchListScreen", () => {
|
|
|
652
645
|
branches={mockBranches}
|
|
653
646
|
stats={mockStats}
|
|
654
647
|
onSelect={onSelect}
|
|
655
|
-
onNavigate={onNavigate}
|
|
656
648
|
onCleanupCommand={onCleanupCommand}
|
|
657
649
|
onRefresh={onRefresh}
|
|
658
650
|
/>,
|
|
@@ -665,10 +657,8 @@ describe("BranchListScreen", () => {
|
|
|
665
657
|
act(() => {
|
|
666
658
|
inkApp.stdin.write("c");
|
|
667
659
|
inkApp.stdin.write("r");
|
|
668
|
-
inkApp.stdin.write("m");
|
|
669
660
|
});
|
|
670
661
|
|
|
671
|
-
expect(onNavigate).not.toHaveBeenCalled();
|
|
672
662
|
expect(onCleanupCommand).not.toHaveBeenCalled();
|
|
673
663
|
expect(onRefresh).not.toHaveBeenCalled();
|
|
674
664
|
|
|
@@ -11,8 +11,9 @@ describe("ExecutionModeSelectorScreen", () => {
|
|
|
11
11
|
beforeEach(() => {
|
|
12
12
|
// Setup happy-dom
|
|
13
13
|
const window = new Window();
|
|
14
|
-
globalThis.window = window as
|
|
15
|
-
globalThis.document =
|
|
14
|
+
globalThis.window = window as unknown as typeof globalThis.window;
|
|
15
|
+
globalThis.document =
|
|
16
|
+
window.document as unknown as typeof globalThis.document;
|
|
16
17
|
});
|
|
17
18
|
|
|
18
19
|
it("should render header with title", () => {
|
|
@@ -12,8 +12,9 @@ describe("PRCleanupScreen", () => {
|
|
|
12
12
|
beforeEach(() => {
|
|
13
13
|
// Setup happy-dom
|
|
14
14
|
const window = new Window();
|
|
15
|
-
globalThis.window = window as
|
|
16
|
-
globalThis.document =
|
|
15
|
+
globalThis.window = window as unknown as typeof globalThis.window;
|
|
16
|
+
globalThis.document =
|
|
17
|
+
window.document as unknown as typeof globalThis.document;
|
|
17
18
|
});
|
|
18
19
|
|
|
19
20
|
const mockTargets: CleanupTarget[] = [
|
|
@@ -11,8 +11,9 @@ describe("SessionSelectorScreen", () => {
|
|
|
11
11
|
beforeEach(() => {
|
|
12
12
|
// Setup happy-dom
|
|
13
13
|
const window = new Window();
|
|
14
|
-
globalThis.window = window as
|
|
15
|
-
globalThis.document =
|
|
14
|
+
globalThis.window = window as unknown as typeof globalThis.window;
|
|
15
|
+
globalThis.document =
|
|
16
|
+
window.document as unknown as typeof globalThis.document;
|
|
16
17
|
});
|
|
17
18
|
|
|
18
19
|
const mockSessions = ["session-1", "session-2", "session-3"];
|
|
@@ -11,8 +11,9 @@ describe("useScreenState", () => {
|
|
|
11
11
|
beforeEach(() => {
|
|
12
12
|
// Setup happy-dom
|
|
13
13
|
const window = new Window();
|
|
14
|
-
globalThis.window = window as
|
|
15
|
-
globalThis.document =
|
|
14
|
+
globalThis.window = window as unknown as typeof globalThis.window;
|
|
15
|
+
globalThis.document =
|
|
16
|
+
window.document as unknown as typeof globalThis.document;
|
|
16
17
|
});
|
|
17
18
|
it("should initialize with branch-list as active screen", () => {
|
|
18
19
|
const { result } = renderHook(() => useScreenState());
|
|
@@ -24,20 +25,20 @@ describe("useScreenState", () => {
|
|
|
24
25
|
const { result } = renderHook(() => useScreenState());
|
|
25
26
|
|
|
26
27
|
act(() => {
|
|
27
|
-
result.current.navigateTo("
|
|
28
|
+
result.current.navigateTo("branch-creator");
|
|
28
29
|
});
|
|
29
30
|
|
|
30
|
-
expect(result.current.currentScreen).toBe("
|
|
31
|
+
expect(result.current.currentScreen).toBe("branch-creator");
|
|
31
32
|
});
|
|
32
33
|
|
|
33
34
|
it("should navigate back to previous screen", () => {
|
|
34
35
|
const { result } = renderHook(() => useScreenState());
|
|
35
36
|
|
|
36
37
|
act(() => {
|
|
37
|
-
result.current.navigateTo("
|
|
38
|
+
result.current.navigateTo("branch-creator");
|
|
38
39
|
});
|
|
39
40
|
|
|
40
|
-
expect(result.current.currentScreen).toBe("
|
|
41
|
+
expect(result.current.currentScreen).toBe("branch-creator");
|
|
41
42
|
|
|
42
43
|
act(() => {
|
|
43
44
|
result.current.goBack();
|
|
@@ -50,20 +51,20 @@ describe("useScreenState", () => {
|
|
|
50
51
|
const { result } = renderHook(() => useScreenState());
|
|
51
52
|
|
|
52
53
|
act(() => {
|
|
53
|
-
result.current.navigateTo("
|
|
54
|
+
result.current.navigateTo("branch-creator");
|
|
54
55
|
});
|
|
55
56
|
|
|
56
57
|
act(() => {
|
|
57
|
-
result.current.navigateTo("
|
|
58
|
+
result.current.navigateTo("ai-tool-selector");
|
|
58
59
|
});
|
|
59
60
|
|
|
60
|
-
expect(result.current.currentScreen).toBe("
|
|
61
|
+
expect(result.current.currentScreen).toBe("ai-tool-selector");
|
|
61
62
|
|
|
62
63
|
act(() => {
|
|
63
64
|
result.current.goBack();
|
|
64
65
|
});
|
|
65
66
|
|
|
66
|
-
expect(result.current.currentScreen).toBe("
|
|
67
|
+
expect(result.current.currentScreen).toBe("branch-creator");
|
|
67
68
|
|
|
68
69
|
act(() => {
|
|
69
70
|
result.current.goBack();
|
|
@@ -88,9 +89,9 @@ describe("useScreenState", () => {
|
|
|
88
89
|
const { result } = renderHook(() => useScreenState());
|
|
89
90
|
|
|
90
91
|
const screens: ScreenType[] = [
|
|
91
|
-
"worktree-manager",
|
|
92
92
|
"branch-creator",
|
|
93
93
|
"ai-tool-selector",
|
|
94
|
+
"model-selector",
|
|
94
95
|
"execution-mode-selector",
|
|
95
96
|
];
|
|
96
97
|
|
|
@@ -106,17 +107,17 @@ describe("useScreenState", () => {
|
|
|
106
107
|
act(() => {
|
|
107
108
|
result.current.goBack();
|
|
108
109
|
});
|
|
109
|
-
expect(result.current.currentScreen).toBe("
|
|
110
|
+
expect(result.current.currentScreen).toBe("model-selector");
|
|
110
111
|
|
|
111
112
|
act(() => {
|
|
112
113
|
result.current.goBack();
|
|
113
114
|
});
|
|
114
|
-
expect(result.current.currentScreen).toBe("
|
|
115
|
+
expect(result.current.currentScreen).toBe("ai-tool-selector");
|
|
115
116
|
|
|
116
117
|
act(() => {
|
|
117
118
|
result.current.goBack();
|
|
118
119
|
});
|
|
119
|
-
expect(result.current.currentScreen).toBe("
|
|
120
|
+
expect(result.current.currentScreen).toBe("branch-creator");
|
|
120
121
|
|
|
121
122
|
act(() => {
|
|
122
123
|
result.current.goBack();
|
|
@@ -128,14 +129,14 @@ describe("useScreenState", () => {
|
|
|
128
129
|
const { result } = renderHook(() => useScreenState());
|
|
129
130
|
|
|
130
131
|
act(() => {
|
|
131
|
-
result.current.navigateTo("
|
|
132
|
+
result.current.navigateTo("branch-creator");
|
|
132
133
|
});
|
|
133
134
|
|
|
134
135
|
act(() => {
|
|
135
|
-
result.current.navigateTo("
|
|
136
|
+
result.current.navigateTo("ai-tool-selector");
|
|
136
137
|
});
|
|
137
138
|
|
|
138
|
-
expect(result.current.currentScreen).toBe("
|
|
139
|
+
expect(result.current.currentScreen).toBe("ai-tool-selector");
|
|
139
140
|
|
|
140
141
|
act(() => {
|
|
141
142
|
result.current.reset();
|
|
@@ -13,8 +13,9 @@ describe("useTerminalSize", () => {
|
|
|
13
13
|
beforeEach(() => {
|
|
14
14
|
// Setup happy-dom
|
|
15
15
|
const window = new Window();
|
|
16
|
-
globalThis.window = window as
|
|
17
|
-
globalThis.document =
|
|
16
|
+
globalThis.window = window as unknown as typeof globalThis.window;
|
|
17
|
+
globalThis.document =
|
|
18
|
+
window.document as unknown as typeof globalThis.document;
|
|
18
19
|
|
|
19
20
|
// デフォルト値を設定
|
|
20
21
|
process.stdout.rows = 24;
|
|
@@ -2,38 +2,58 @@
|
|
|
2
2
|
* @vitest-environment happy-dom
|
|
3
3
|
* Edge case tests for UI components
|
|
4
4
|
*/
|
|
5
|
-
import {
|
|
6
|
-
describe,
|
|
7
|
-
it,
|
|
8
|
-
expect,
|
|
9
|
-
beforeEach,
|
|
10
|
-
afterEach,
|
|
11
|
-
afterAll,
|
|
12
|
-
vi,
|
|
13
|
-
} from "vitest";
|
|
5
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
|
|
14
6
|
import { render } from "@testing-library/react";
|
|
15
7
|
import React from "react";
|
|
16
|
-
import
|
|
17
|
-
import {
|
|
8
|
+
import * as BranchListScreenModule from "../../components/screens/BranchListScreen.js";
|
|
9
|
+
import type { BranchListScreenProps } from "../../components/screens/BranchListScreen.js";
|
|
18
10
|
import { Window } from "happy-dom";
|
|
19
11
|
import type { BranchInfo, BranchItem, Statistics } from "../../types.js";
|
|
20
|
-
import * as useGitDataModule from "../../hooks/useGitData.js";
|
|
21
12
|
|
|
22
13
|
const mockRefresh = vi.fn();
|
|
23
|
-
const
|
|
24
|
-
const
|
|
14
|
+
const branchListProps: BranchListScreenProps[] = [];
|
|
15
|
+
const useGitDataMock = vi.fn();
|
|
16
|
+
let App: typeof import("../../components/App.js").App;
|
|
17
|
+
|
|
18
|
+
vi.mock("../../hooks/useGitData.js", () => ({
|
|
19
|
+
useGitData: (...args: unknown[]) => useGitDataMock(...args),
|
|
20
|
+
}));
|
|
21
|
+
|
|
22
|
+
vi.mock("../../components/screens/BranchListScreen.js", () => ({
|
|
23
|
+
BranchListScreen: (props: BranchListScreenProps) => {
|
|
24
|
+
branchListProps.push(props);
|
|
25
|
+
return (
|
|
26
|
+
<div>
|
|
27
|
+
<div>BranchList</div>
|
|
28
|
+
{props.error && <div>Error: {props.error.message}</div>}
|
|
29
|
+
<div>
|
|
30
|
+
Local:{props.stats?.localCount ?? 0} Remote:
|
|
31
|
+
{props.stats?.remoteCount ?? 0} Worktrees:
|
|
32
|
+
{props.stats?.worktreeCount ?? 0}
|
|
33
|
+
</div>
|
|
34
|
+
<ul>
|
|
35
|
+
{props.branches.map((b) => (
|
|
36
|
+
<li key={b.name}>{b.name}</li>
|
|
37
|
+
))}
|
|
38
|
+
</ul>
|
|
39
|
+
</div>
|
|
40
|
+
);
|
|
41
|
+
},
|
|
42
|
+
}));
|
|
25
43
|
|
|
26
44
|
describe("Edge Cases Integration Tests", () => {
|
|
27
|
-
beforeEach(() => {
|
|
45
|
+
beforeEach(async () => {
|
|
28
46
|
// Setup happy-dom
|
|
29
47
|
const window = new Window();
|
|
30
|
-
globalThis.window = window as
|
|
31
|
-
globalThis.document =
|
|
48
|
+
globalThis.window = window as unknown as typeof globalThis.window;
|
|
49
|
+
globalThis.document =
|
|
50
|
+
window.document as unknown as typeof globalThis.document;
|
|
32
51
|
|
|
33
52
|
// Reset mocks
|
|
34
53
|
vi.clearAllMocks();
|
|
35
|
-
|
|
36
|
-
|
|
54
|
+
useGitDataMock.mockReset();
|
|
55
|
+
branchListProps.length = 0;
|
|
56
|
+
App = (await import("../../components/App.js")).App;
|
|
37
57
|
});
|
|
38
58
|
|
|
39
59
|
/**
|
|
@@ -62,7 +82,7 @@ describe("Edge Cases Integration Tests", () => {
|
|
|
62
82
|
|
|
63
83
|
const onSelect = vi.fn();
|
|
64
84
|
const { container } = render(
|
|
65
|
-
<BranchListScreen
|
|
85
|
+
<BranchListScreenModule.BranchListScreen
|
|
66
86
|
branches={mockBranches}
|
|
67
87
|
stats={mockStats}
|
|
68
88
|
onSelect={onSelect}
|
|
@@ -93,16 +113,16 @@ describe("Edge Cases Integration Tests", () => {
|
|
|
93
113
|
};
|
|
94
114
|
|
|
95
115
|
const onSelect = vi.fn();
|
|
96
|
-
const {
|
|
97
|
-
<BranchListScreen
|
|
116
|
+
const { container } = render(
|
|
117
|
+
<BranchListScreenModule.BranchListScreen
|
|
98
118
|
branches={mockBranches}
|
|
99
119
|
stats={mockStats}
|
|
100
120
|
onSelect={onSelect}
|
|
101
121
|
/>,
|
|
102
122
|
);
|
|
103
123
|
|
|
104
|
-
|
|
105
|
-
expect(
|
|
124
|
+
expect(container).toBeDefined();
|
|
125
|
+
expect(branchListProps.at(-1)?.branches).toHaveLength(1);
|
|
106
126
|
|
|
107
127
|
process.stdout.rows = originalRows;
|
|
108
128
|
});
|
|
@@ -139,15 +159,17 @@ describe("Edge Cases Integration Tests", () => {
|
|
|
139
159
|
|
|
140
160
|
const onSelect = vi.fn();
|
|
141
161
|
const { container } = render(
|
|
142
|
-
<BranchListScreen
|
|
162
|
+
<BranchListScreenModule.BranchListScreen
|
|
143
163
|
branches={mockBranches}
|
|
144
164
|
stats={mockStats}
|
|
145
165
|
onSelect={onSelect}
|
|
146
166
|
/>,
|
|
147
167
|
);
|
|
148
168
|
|
|
149
|
-
|
|
150
|
-
expect(
|
|
169
|
+
expect(container.textContent).toContain(longBranchName);
|
|
170
|
+
expect(
|
|
171
|
+
branchListProps.at(-1)?.branches?.some((b) => b.name === longBranchName),
|
|
172
|
+
).toBe(true);
|
|
151
173
|
});
|
|
152
174
|
|
|
153
175
|
it("[T092] should handle branch names with special characters", () => {
|
|
@@ -177,7 +199,7 @@ describe("Edge Cases Integration Tests", () => {
|
|
|
177
199
|
|
|
178
200
|
const onSelect = vi.fn();
|
|
179
201
|
const { container } = render(
|
|
180
|
-
<BranchListScreen
|
|
202
|
+
<BranchListScreenModule.BranchListScreen
|
|
181
203
|
branches={mockBranches}
|
|
182
204
|
stats={mockStats}
|
|
183
205
|
onSelect={onSelect}
|
|
@@ -196,7 +218,7 @@ describe("Edge Cases Integration Tests", () => {
|
|
|
196
218
|
it("[T093] should catch errors in App component", async () => {
|
|
197
219
|
// Mock useGitData to throw an error after initial render
|
|
198
220
|
let callCount = 0;
|
|
199
|
-
|
|
221
|
+
useGitDataMock.mockImplementation(() => {
|
|
200
222
|
callCount++;
|
|
201
223
|
if (callCount > 1) {
|
|
202
224
|
throw new Error("Simulated error");
|
|
@@ -220,7 +242,7 @@ describe("Edge Cases Integration Tests", () => {
|
|
|
220
242
|
|
|
221
243
|
it("[T093] should display error message when data loading fails", () => {
|
|
222
244
|
const testError = new Error("Test error: Failed to load Git data");
|
|
223
|
-
|
|
245
|
+
useGitDataMock.mockReturnValue({
|
|
224
246
|
branches: [],
|
|
225
247
|
worktrees: [],
|
|
226
248
|
loading: false,
|
|
@@ -232,13 +254,15 @@ describe("Edge Cases Integration Tests", () => {
|
|
|
232
254
|
const onExit = vi.fn();
|
|
233
255
|
const { getByText } = render(<App onExit={onExit} />);
|
|
234
256
|
|
|
235
|
-
|
|
257
|
+
expect(branchListProps).not.toHaveLength(0);
|
|
258
|
+
expect(branchListProps.at(-1)?.error).toBe(testError);
|
|
259
|
+
// Error should be displayed via stubbed screen
|
|
236
260
|
expect(getByText(/Error:/i)).toBeDefined();
|
|
237
261
|
expect(getByText(/Failed to load Git data/i)).toBeDefined();
|
|
238
262
|
});
|
|
239
263
|
|
|
240
264
|
it("[T093] should handle empty branches list gracefully", () => {
|
|
241
|
-
|
|
265
|
+
useGitDataMock.mockReturnValue({
|
|
242
266
|
branches: [],
|
|
243
267
|
worktrees: [],
|
|
244
268
|
loading: false,
|
|
@@ -265,7 +289,7 @@ describe("Edge Cases Integration Tests", () => {
|
|
|
265
289
|
isCurrent: false,
|
|
266
290
|
}));
|
|
267
291
|
|
|
268
|
-
|
|
292
|
+
useGitDataMock.mockReturnValue({
|
|
269
293
|
branches: mockBranches,
|
|
270
294
|
worktrees: Array.from({ length: 30 }, (_, i) => ({
|
|
271
295
|
branch: `feature/branch-${i}`,
|
|
@@ -305,7 +329,7 @@ describe("Edge Cases Integration Tests", () => {
|
|
|
305
329
|
|
|
306
330
|
const onSelect = vi.fn();
|
|
307
331
|
const { container, rerender } = render(
|
|
308
|
-
<BranchListScreen
|
|
332
|
+
<BranchListScreenModule.BranchListScreen
|
|
309
333
|
branches={mockBranches}
|
|
310
334
|
stats={mockStats}
|
|
311
335
|
onSelect={onSelect}
|
|
@@ -319,7 +343,7 @@ describe("Edge Cases Integration Tests", () => {
|
|
|
319
343
|
|
|
320
344
|
// Re-render
|
|
321
345
|
rerender(
|
|
322
|
-
<BranchListScreen
|
|
346
|
+
<BranchListScreenModule.BranchListScreen
|
|
323
347
|
branches={mockBranches}
|
|
324
348
|
stats={mockStats}
|
|
325
349
|
onSelect={onSelect}
|
|
@@ -332,11 +356,7 @@ describe("Edge Cases Integration Tests", () => {
|
|
|
332
356
|
});
|
|
333
357
|
|
|
334
358
|
afterEach(() => {
|
|
335
|
-
|
|
336
|
-
|
|
359
|
+
useGitDataMock.mockReset();
|
|
360
|
+
branchListProps.length = 0;
|
|
337
361
|
});
|
|
338
362
|
});
|
|
339
|
-
|
|
340
|
-
afterAll(() => {
|
|
341
|
-
useGitDataSpy.mockRestore();
|
|
342
|
-
});
|
|
@@ -18,7 +18,9 @@ import { App } from "../../components/App.js";
|
|
|
18
18
|
import { Window } from "happy-dom";
|
|
19
19
|
import type { BranchInfo, BranchItem } from "../../types.js";
|
|
20
20
|
import * as BranchListScreenModule from "../../components/screens/BranchListScreen.js";
|
|
21
|
+
import type { BranchListScreenProps } from "../../components/screens/BranchListScreen.js";
|
|
21
22
|
import * as BranchActionSelectorScreenModule from "../../screens/BranchActionSelectorScreen.js";
|
|
23
|
+
import type { BranchActionSelectorScreenProps } from "../../screens/BranchActionSelectorScreen.js";
|
|
22
24
|
|
|
23
25
|
vi.mock("../../../../git.ts", () => ({
|
|
24
26
|
__esModule: true,
|
|
@@ -26,6 +28,8 @@ vi.mock("../../../../git.ts", () => ({
|
|
|
26
28
|
getRepositoryRoot: vi.fn(async () => "/repo"),
|
|
27
29
|
deleteBranch: vi.fn(async () => undefined),
|
|
28
30
|
fetchAllRemotes: vi.fn(async () => undefined),
|
|
31
|
+
collectUpstreamMap: vi.fn(async () => new Map()),
|
|
32
|
+
getBranchDivergenceStatuses: vi.fn(async () => []),
|
|
29
33
|
}));
|
|
30
34
|
|
|
31
35
|
const { mockIsProtectedBranchName, mockSwitchToProtectedBranch } = vi.hoisted(
|
|
@@ -90,8 +94,9 @@ describe("Navigation Integration Tests", () => {
|
|
|
90
94
|
beforeEach(() => {
|
|
91
95
|
// Setup happy-dom
|
|
92
96
|
const window = new Window();
|
|
93
|
-
globalThis.window = window as
|
|
94
|
-
globalThis.document =
|
|
97
|
+
globalThis.window = window as unknown as typeof globalThis.window;
|
|
98
|
+
globalThis.document =
|
|
99
|
+
window.document as unknown as typeof globalThis.document;
|
|
95
100
|
|
|
96
101
|
// Reset mocks
|
|
97
102
|
mockedGetAllBranches.mockReset();
|
|
@@ -243,8 +248,8 @@ describe("Navigation Integration Tests", () => {
|
|
|
243
248
|
});
|
|
244
249
|
|
|
245
250
|
describe("Protected Branch Navigation (T103)", () => {
|
|
246
|
-
const branchListProps:
|
|
247
|
-
const branchActionProps:
|
|
251
|
+
const branchListProps: BranchListScreenProps[] = [];
|
|
252
|
+
const branchActionProps: BranchActionSelectorScreenProps[] = [];
|
|
248
253
|
let branchListSpy: ReturnType<typeof vi.spyOn>;
|
|
249
254
|
let branchActionSpy: ReturnType<typeof vi.spyOn>;
|
|
250
255
|
|
|
@@ -265,8 +270,9 @@ describe("Protected Branch Navigation (T103)", () => {
|
|
|
265
270
|
|
|
266
271
|
beforeEach(() => {
|
|
267
272
|
const window = new Window();
|
|
268
|
-
globalThis.window = window as
|
|
269
|
-
globalThis.document =
|
|
273
|
+
globalThis.window = window as unknown as typeof globalThis.window;
|
|
274
|
+
globalThis.document =
|
|
275
|
+
window.document as unknown as typeof globalThis.document;
|
|
270
276
|
mockedGetAllBranches.mockReset();
|
|
271
277
|
mockedListAdditionalWorktrees.mockReset();
|
|
272
278
|
mockedGetRepositoryRoot.mockReset();
|
|
@@ -284,13 +290,13 @@ describe("Protected Branch Navigation (T103)", () => {
|
|
|
284
290
|
aiToolScreenProps.length = 0;
|
|
285
291
|
branchListSpy = vi
|
|
286
292
|
.spyOn(BranchListScreenModule, "BranchListScreen")
|
|
287
|
-
.mockImplementation((props:
|
|
293
|
+
.mockImplementation((props: BranchListScreenProps) => {
|
|
288
294
|
branchListProps.push(props);
|
|
289
295
|
return React.createElement(originalBranchListScreen, props);
|
|
290
296
|
});
|
|
291
297
|
branchActionSpy = vi
|
|
292
298
|
.spyOn(BranchActionSelectorScreenModule, "BranchActionSelectorScreen")
|
|
293
|
-
.mockImplementation((props:
|
|
299
|
+
.mockImplementation((props: BranchActionSelectorScreenProps) => {
|
|
294
300
|
branchActionProps.push(props);
|
|
295
301
|
return React.createElement(originalBranchActionSelectorScreen, props);
|
|
296
302
|
});
|
|
@@ -337,8 +343,7 @@ describe("Protected Branch Navigation (T103)", () => {
|
|
|
337
343
|
|
|
338
344
|
// Simulate selecting the branch from the list
|
|
339
345
|
const listProps =
|
|
340
|
-
branchListProps.find((p) => p.branches?.length) ??
|
|
341
|
-
branchListProps.at(-1);
|
|
346
|
+
branchListProps.find((p) => p.branches?.length) ?? branchListProps.at(-1);
|
|
342
347
|
expect(listProps?.branches?.length).toBeGreaterThan(0);
|
|
343
348
|
listProps.onSelect(listProps.branches[0]);
|
|
344
349
|
|
|
@@ -26,8 +26,9 @@ describe("Real-time Update Integration", () => {
|
|
|
26
26
|
beforeEach(() => {
|
|
27
27
|
// Setup happy-dom
|
|
28
28
|
const window = new Window();
|
|
29
|
-
globalThis.window = window as
|
|
30
|
-
globalThis.document =
|
|
29
|
+
globalThis.window = window as unknown as typeof globalThis.window;
|
|
30
|
+
globalThis.document =
|
|
31
|
+
window.document as unknown as typeof globalThis.document;
|
|
31
32
|
|
|
32
33
|
// Reset mocks
|
|
33
34
|
vi.clearAllMocks();
|
|
@@ -84,7 +84,6 @@ describe("BranchListScreen Performance", () => {
|
|
|
84
84
|
branches={branches}
|
|
85
85
|
stats={stats}
|
|
86
86
|
onSelect={() => {}}
|
|
87
|
-
onNavigate={() => {}}
|
|
88
87
|
onQuit={() => {}}
|
|
89
88
|
/>,
|
|
90
89
|
);
|
|
@@ -123,7 +122,6 @@ describe("BranchListScreen Performance", () => {
|
|
|
123
122
|
branches={branches}
|
|
124
123
|
stats={stats}
|
|
125
124
|
onSelect={() => {}}
|
|
126
|
-
onNavigate={() => {}}
|
|
127
125
|
onQuit={() => {}}
|
|
128
126
|
lastUpdated={new Date()}
|
|
129
127
|
/>,
|
|
@@ -137,7 +135,6 @@ describe("BranchListScreen Performance", () => {
|
|
|
137
135
|
branches={branches}
|
|
138
136
|
stats={{ ...stats, total: stats.total + 1 }}
|
|
139
137
|
onSelect={() => {}}
|
|
140
|
-
onNavigate={() => {}}
|
|
141
138
|
onQuit={() => {}}
|
|
142
139
|
lastUpdated={new Date()}
|
|
143
140
|
/>,
|
|
@@ -174,7 +171,6 @@ describe("BranchListScreen Performance", () => {
|
|
|
174
171
|
branches={branches}
|
|
175
172
|
stats={stats}
|
|
176
173
|
onSelect={() => {}}
|
|
177
|
-
onNavigate={() => {}}
|
|
178
174
|
onQuit={() => {}}
|
|
179
175
|
/>,
|
|
180
176
|
);
|