@akiojin/gwt 2.2.0 → 2.4.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.ja.md +6 -4
- package/README.md +6 -4
- package/dist/claude.d.ts +1 -0
- package/dist/claude.d.ts.map +1 -1
- package/dist/claude.js +6 -3
- package/dist/claude.js.map +1 -1
- package/dist/cli/ui/components/App.d.ts +6 -4
- package/dist/cli/ui/components/App.d.ts.map +1 -1
- package/dist/cli/ui/components/App.js +184 -107
- package/dist/cli/ui/components/App.js.map +1 -1
- package/dist/cli/ui/components/common/Confirm.d.ts +1 -1
- package/dist/cli/ui/components/common/Confirm.d.ts.map +1 -1
- package/dist/cli/ui/components/common/Confirm.js +7 -7
- package/dist/cli/ui/components/common/Confirm.js.map +1 -1
- package/dist/cli/ui/components/common/ErrorBoundary.d.ts +1 -1
- package/dist/cli/ui/components/common/ErrorBoundary.d.ts.map +1 -1
- package/dist/cli/ui/components/common/ErrorBoundary.js +4 -4
- package/dist/cli/ui/components/common/ErrorBoundary.js.map +1 -1
- package/dist/cli/ui/components/common/Input.d.ts +2 -2
- package/dist/cli/ui/components/common/Input.d.ts.map +1 -1
- package/dist/cli/ui/components/common/Input.js +4 -4
- package/dist/cli/ui/components/common/Input.js.map +1 -1
- package/dist/cli/ui/components/common/LoadingIndicator.d.ts +1 -1
- package/dist/cli/ui/components/common/LoadingIndicator.d.ts.map +1 -1
- package/dist/cli/ui/components/common/LoadingIndicator.js +4 -4
- package/dist/cli/ui/components/common/LoadingIndicator.js.map +1 -1
- package/dist/cli/ui/components/common/Select.d.ts +1 -1
- package/dist/cli/ui/components/common/Select.d.ts.map +1 -1
- package/dist/cli/ui/components/common/Select.js +11 -12
- package/dist/cli/ui/components/common/Select.js.map +1 -1
- package/dist/cli/ui/components/screens/AIToolSelectorScreen.d.ts +3 -3
- package/dist/cli/ui/components/screens/AIToolSelectorScreen.d.ts.map +1 -1
- package/dist/cli/ui/components/screens/AIToolSelectorScreen.js +11 -11
- package/dist/cli/ui/components/screens/AIToolSelectorScreen.js.map +1 -1
- package/dist/cli/ui/components/screens/BranchCreatorScreen.d.ts +1 -1
- package/dist/cli/ui/components/screens/BranchCreatorScreen.d.ts.map +1 -1
- package/dist/cli/ui/components/screens/BranchCreatorScreen.js +39 -36
- package/dist/cli/ui/components/screens/BranchCreatorScreen.js.map +1 -1
- package/dist/cli/ui/components/screens/BranchListScreen.d.ts +3 -3
- package/dist/cli/ui/components/screens/BranchListScreen.d.ts.map +1 -1
- package/dist/cli/ui/components/screens/BranchListScreen.js +55 -50
- package/dist/cli/ui/components/screens/BranchListScreen.js.map +1 -1
- package/dist/cli/ui/components/screens/ExecutionModeSelectorScreen.d.ts +2 -2
- package/dist/cli/ui/components/screens/ExecutionModeSelectorScreen.d.ts.map +1 -1
- package/dist/cli/ui/components/screens/ExecutionModeSelectorScreen.js +25 -25
- package/dist/cli/ui/components/screens/ExecutionModeSelectorScreen.js.map +1 -1
- package/dist/cli/ui/components/screens/ModelSelectorScreen.d.ts +18 -0
- package/dist/cli/ui/components/screens/ModelSelectorScreen.d.ts.map +1 -0
- package/dist/cli/ui/components/screens/ModelSelectorScreen.js +201 -0
- package/dist/cli/ui/components/screens/ModelSelectorScreen.js.map +1 -0
- package/dist/cli/ui/components/screens/PRCleanupScreen.d.ts +2 -2
- package/dist/cli/ui/components/screens/PRCleanupScreen.js +21 -21
- package/dist/cli/ui/components/screens/SessionSelectorScreen.d.ts +1 -1
- package/dist/cli/ui/components/screens/SessionSelectorScreen.js +8 -8
- package/dist/cli/ui/components/screens/WorktreeManagerScreen.d.ts +1 -1
- package/dist/cli/ui/components/screens/WorktreeManagerScreen.js +8 -8
- package/dist/cli/ui/screens/BranchActionSelectorScreen.d.ts.map +1 -1
- package/dist/cli/ui/screens/BranchActionSelectorScreen.js +7 -4
- package/dist/cli/ui/screens/BranchActionSelectorScreen.js.map +1 -1
- package/dist/cli/ui/types.d.ts +11 -1
- package/dist/cli/ui/types.d.ts.map +1 -1
- package/dist/cli/ui/utils/modelOptions.d.ts +6 -0
- package/dist/cli/ui/utils/modelOptions.d.ts.map +1 -0
- package/dist/cli/ui/utils/modelOptions.js +111 -0
- package/dist/cli/ui/utils/modelOptions.js.map +1 -0
- package/dist/client/assets/{index-V6hDu9KS.js → index-Difv1Hwu.js} +2 -2
- package/dist/client/index.html +1 -1
- package/dist/codex.d.ts +6 -0
- package/dist/codex.d.ts.map +1 -1
- package/dist/codex.js +11 -4
- package/dist/codex.js.map +1 -1
- package/dist/config/builtin-tools.d.ts +10 -2
- package/dist/config/builtin-tools.d.ts.map +1 -1
- package/dist/config/builtin-tools.js +40 -4
- package/dist/config/builtin-tools.js.map +1 -1
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js.map +1 -1
- package/dist/config/tools.d.ts.map +1 -1
- package/dist/config/tools.js +4 -3
- package/dist/config/tools.js.map +1 -1
- package/dist/gemini.d.ts +13 -0
- package/dist/gemini.d.ts.map +1 -0
- package/dist/gemini.js +157 -0
- package/dist/gemini.js.map +1 -0
- package/dist/git.d.ts.map +1 -1
- package/dist/git.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +59 -7
- package/dist/index.js.map +1 -1
- package/dist/qwen.d.ts +13 -0
- package/dist/qwen.d.ts.map +1 -0
- package/dist/qwen.js +157 -0
- package/dist/qwen.js.map +1 -0
- package/dist/services/git.service.d.ts.map +1 -1
- package/dist/services/git.service.js.map +1 -1
- package/dist/web/client/src/components/BranchGraph.d.ts.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/components/EnvEditor.d.ts.map +1 -1
- package/dist/web/client/src/components/EnvEditor.js +7 -4
- package/dist/web/client/src/components/EnvEditor.js.map +1 -1
- package/dist/web/client/src/pages/BranchDetailPage.d.ts.map +1 -1
- package/dist/web/client/src/pages/BranchDetailPage.js +55 -18
- package/dist/web/client/src/pages/BranchDetailPage.js.map +1 -1
- package/dist/web/client/src/pages/BranchListPage.d.ts.map +1 -1
- package/dist/web/client/src/pages/BranchListPage.js +10 -4
- package/dist/web/client/src/pages/BranchListPage.js.map +1 -1
- package/dist/web/client/src/pages/ConfigManagementPage.d.ts.map +1 -1
- package/dist/web/client/src/pages/ConfigManagementPage.js +4 -2
- package/dist/web/client/src/pages/ConfigManagementPage.js.map +1 -1
- package/package.json +2 -1
- package/src/claude.ts +8 -3
- package/src/cli/ui/__tests__/acceptance/navigation.acceptance.test.tsx +69 -50
- package/src/cli/ui/__tests__/components/App.protected-branch.test.tsx +67 -45
- package/src/cli/ui/__tests__/components/App.shortcuts.test.tsx +117 -75
- package/src/cli/ui/__tests__/components/App.test.tsx +45 -37
- package/src/cli/ui/__tests__/components/ModelSelectorScreen.initial.test.tsx +81 -0
- package/src/cli/ui/__tests__/components/common/Confirm.test.tsx +35 -22
- package/src/cli/ui/__tests__/components/common/ErrorBoundary.test.tsx +22 -22
- package/src/cli/ui/__tests__/components/common/Input.test.tsx +29 -22
- package/src/cli/ui/__tests__/components/common/LoadingIndicator.test.tsx +63 -43
- package/src/cli/ui/__tests__/components/common/Select.memo.test.tsx +57 -66
- package/src/cli/ui/__tests__/components/common/Select.test.tsx +121 -91
- package/src/cli/ui/__tests__/components/parts/Footer.test.tsx +18 -16
- package/src/cli/ui/__tests__/components/parts/Header.test.tsx +13 -13
- package/src/cli/ui/__tests__/components/parts/ScrollableList.test.tsx +20 -20
- package/src/cli/ui/__tests__/components/parts/Stats.test.tsx +38 -26
- package/src/cli/ui/__tests__/components/screens/AIToolSelectorScreen.test.tsx +31 -31
- package/src/cli/ui/__tests__/components/screens/BranchCreatorScreen.test.tsx +73 -37
- package/src/cli/ui/__tests__/components/screens/BranchListScreen.test.tsx +261 -153
- package/src/cli/ui/__tests__/components/screens/ExecutionModeSelectorScreen.test.tsx +38 -32
- package/src/cli/ui/__tests__/components/screens/PRCleanupScreen.test.tsx +39 -39
- package/src/cli/ui/__tests__/components/screens/SessionSelectorScreen.test.tsx +49 -21
- package/src/cli/ui/__tests__/components/screens/WorktreeManagerScreen.test.tsx +52 -28
- package/src/cli/ui/__tests__/integration/edgeCases.test.tsx +84 -48
- package/src/cli/ui/__tests__/integration/navigation.test.tsx +111 -83
- package/src/cli/ui/__tests__/integration/realtimeUpdate.test.tsx +111 -108
- package/src/cli/ui/__tests__/performance/branchList.performance.test.tsx +50 -37
- package/src/cli/ui/__tests__/performance/useMemoOptimization.test.tsx +75 -76
- package/src/cli/ui/components/App.tsx +317 -150
- package/src/cli/ui/components/common/Confirm.tsx +13 -9
- package/src/cli/ui/components/common/ErrorBoundary.tsx +8 -5
- package/src/cli/ui/components/common/Input.tsx +12 -4
- package/src/cli/ui/components/common/LoadingIndicator.tsx +8 -5
- package/src/cli/ui/components/common/Select.tsx +28 -17
- package/src/cli/ui/components/parts/Header.test.tsx +5 -15
- package/src/cli/ui/components/screens/AIToolSelectorScreen.tsx +20 -15
- package/src/cli/ui/components/screens/BranchCreatorScreen.tsx +74 -54
- package/src/cli/ui/components/screens/BranchListScreen.tsx +92 -75
- package/src/cli/ui/components/screens/ExecutionModeSelectorScreen.tsx +35 -28
- package/src/cli/ui/components/screens/ModelSelectorScreen.tsx +320 -0
- package/src/cli/ui/components/screens/PRCleanupScreen.tsx +22 -22
- package/src/cli/ui/components/screens/SessionSelectorScreen.tsx +8 -8
- package/src/cli/ui/components/screens/WorktreeManagerScreen.tsx +8 -8
- package/src/cli/ui/screens/BranchActionSelectorScreen.tsx +9 -4
- package/src/cli/ui/types.ts +21 -1
- package/src/cli/ui/utils/modelOptions.test.ts +36 -0
- package/src/cli/ui/utils/modelOptions.ts +122 -0
- package/src/codex.ts +23 -4
- package/src/config/builtin-tools.ts +42 -4
- package/src/config/index.ts +2 -12
- package/src/config/tools.ts +16 -6
- package/src/gemini.ts +207 -0
- package/src/git.ts +2 -1
- package/src/index.ts +86 -6
- package/src/qwen.ts +213 -0
- package/src/services/git.service.ts +2 -1
- package/src/web/client/src/components/BranchGraph.tsx +3 -2
- package/src/web/client/src/components/EnvEditor.tsx +44 -11
- package/src/web/client/src/pages/BranchDetailPage.tsx +165 -54
- package/src/web/client/src/pages/BranchListPage.tsx +37 -13
- package/src/web/client/src/pages/ConfigManagementPage.tsx +28 -9
|
@@ -2,46 +2,51 @@
|
|
|
2
2
|
* @vitest-environment happy-dom
|
|
3
3
|
* Acceptance tests for User Story 2: Sub-screen Navigation
|
|
4
4
|
*/
|
|
5
|
-
import { describe, it, expect, beforeEach, afterAll, vi } from
|
|
6
|
-
import type { Mock } from
|
|
7
|
-
import { render, waitFor } from
|
|
8
|
-
import React from
|
|
9
|
-
import { App } from
|
|
10
|
-
import { Window } from
|
|
11
|
-
import type { BranchInfo } from
|
|
5
|
+
import { describe, it, expect, beforeEach, afterAll, vi } from "vitest";
|
|
6
|
+
import type { Mock } from "vitest";
|
|
7
|
+
import { render, waitFor } from "@testing-library/react";
|
|
8
|
+
import React from "react";
|
|
9
|
+
import { App } from "../../components/App.js";
|
|
10
|
+
import { Window } from "happy-dom";
|
|
11
|
+
import type { BranchInfo } from "../../types.js";
|
|
12
12
|
|
|
13
13
|
// Mock git.ts and worktree.ts
|
|
14
|
-
vi.mock(
|
|
14
|
+
vi.mock("../../../../git.ts", () => ({
|
|
15
15
|
__esModule: true,
|
|
16
16
|
getAllBranches: vi.fn(),
|
|
17
|
-
getRepositoryRoot: vi.fn(async () =>
|
|
17
|
+
getRepositoryRoot: vi.fn(async () => "/repo"),
|
|
18
18
|
deleteBranch: vi.fn(async () => undefined),
|
|
19
19
|
}));
|
|
20
20
|
|
|
21
|
-
const { acceptanceIsProtectedBranchName, acceptanceSwitchToProtectedBranch } =
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
const { acceptanceIsProtectedBranchName, acceptanceSwitchToProtectedBranch } =
|
|
22
|
+
vi.hoisted(() => ({
|
|
23
|
+
acceptanceIsProtectedBranchName: vi.fn(() => false),
|
|
24
|
+
acceptanceSwitchToProtectedBranch: vi.fn(async () => "none" as const),
|
|
25
|
+
}));
|
|
25
26
|
|
|
26
|
-
vi.mock(
|
|
27
|
+
vi.mock("../../../../worktree.ts", () => ({
|
|
27
28
|
__esModule: true,
|
|
28
29
|
listAdditionalWorktrees: vi.fn(),
|
|
29
30
|
createWorktree: vi.fn(async () => undefined),
|
|
30
|
-
generateWorktreePath: vi.fn(async () =>
|
|
31
|
+
generateWorktreePath: vi.fn(async () => "/repo/.git/worktree/test"),
|
|
31
32
|
getMergedPRWorktrees: vi.fn(async () => []),
|
|
32
33
|
removeWorktree: vi.fn(async () => undefined),
|
|
33
34
|
isProtectedBranchName: acceptanceIsProtectedBranchName,
|
|
34
35
|
switchToProtectedBranch: acceptanceSwitchToProtectedBranch,
|
|
35
36
|
}));
|
|
36
37
|
|
|
37
|
-
import {
|
|
38
|
+
import {
|
|
39
|
+
getAllBranches,
|
|
40
|
+
getRepositoryRoot,
|
|
41
|
+
deleteBranch,
|
|
42
|
+
} from "../../../../git.ts";
|
|
38
43
|
import {
|
|
39
44
|
listAdditionalWorktrees,
|
|
40
45
|
createWorktree,
|
|
41
46
|
generateWorktreePath,
|
|
42
47
|
getMergedPRWorktrees,
|
|
43
48
|
removeWorktree,
|
|
44
|
-
} from
|
|
49
|
+
} from "../../../../worktree.ts";
|
|
45
50
|
|
|
46
51
|
const mockedGetAllBranches = getAllBranches as Mock;
|
|
47
52
|
const mockedGetRepositoryRoot = getRepositoryRoot as Mock;
|
|
@@ -54,7 +59,7 @@ const mockedRemoveWorktree = removeWorktree as Mock;
|
|
|
54
59
|
const mockedIsProtectedBranchName = acceptanceIsProtectedBranchName as Mock;
|
|
55
60
|
const mockedSwitchToProtectedBranch = acceptanceSwitchToProtectedBranch as Mock;
|
|
56
61
|
|
|
57
|
-
describe(
|
|
62
|
+
describe("Acceptance: Navigation (User Story 2)", () => {
|
|
58
63
|
beforeEach(() => {
|
|
59
64
|
// Setup happy-dom
|
|
60
65
|
const window = new Window();
|
|
@@ -72,21 +77,21 @@ describe('Acceptance: Navigation (User Story 2)', () => {
|
|
|
72
77
|
mockedRemoveWorktree.mockReset();
|
|
73
78
|
mockedIsProtectedBranchName.mockReset();
|
|
74
79
|
mockedSwitchToProtectedBranch.mockReset();
|
|
75
|
-
mockedGetRepositoryRoot.mockResolvedValue(
|
|
76
|
-
mockedSwitchToProtectedBranch.mockResolvedValue(
|
|
80
|
+
mockedGetRepositoryRoot.mockResolvedValue("/repo");
|
|
81
|
+
mockedSwitchToProtectedBranch.mockResolvedValue("none");
|
|
77
82
|
});
|
|
78
83
|
|
|
79
84
|
const mockBranches: BranchInfo[] = [
|
|
80
85
|
{
|
|
81
|
-
name:
|
|
82
|
-
type:
|
|
83
|
-
branchType:
|
|
86
|
+
name: "main",
|
|
87
|
+
type: "local",
|
|
88
|
+
branchType: "main",
|
|
84
89
|
isCurrent: true,
|
|
85
90
|
},
|
|
86
91
|
{
|
|
87
|
-
name:
|
|
88
|
-
type:
|
|
89
|
-
branchType:
|
|
92
|
+
name: "feature/test",
|
|
93
|
+
type: "local",
|
|
94
|
+
branchType: "feature",
|
|
90
95
|
isCurrent: false,
|
|
91
96
|
},
|
|
92
97
|
];
|
|
@@ -95,8 +100,10 @@ describe('Acceptance: Navigation (User Story 2)', () => {
|
|
|
95
100
|
* T074: Acceptance Scenario 1
|
|
96
101
|
* nキーで新規ブランチ作成画面に遷移
|
|
97
102
|
*/
|
|
98
|
-
it(
|
|
99
|
-
(getAllBranches as ReturnType<typeof vi.fn>).mockResolvedValue(
|
|
103
|
+
it("[AC1] should navigate to branch creator on n key", async () => {
|
|
104
|
+
(getAllBranches as ReturnType<typeof vi.fn>).mockResolvedValue(
|
|
105
|
+
mockBranches,
|
|
106
|
+
);
|
|
100
107
|
(listAdditionalWorktrees as ReturnType<typeof vi.fn>).mockResolvedValue([]);
|
|
101
108
|
|
|
102
109
|
const onExit = vi.fn();
|
|
@@ -107,23 +114,27 @@ describe('Acceptance: Navigation (User Story 2)', () => {
|
|
|
107
114
|
});
|
|
108
115
|
|
|
109
116
|
// Verify n key action is available in footer
|
|
110
|
-
const nKeyElements = container.querySelectorAll(
|
|
117
|
+
const nKeyElements = container.querySelectorAll("*");
|
|
111
118
|
let hasNKey = false;
|
|
112
119
|
nKeyElements.forEach((el) => {
|
|
113
|
-
if (el.textContent?.toLowerCase().includes(
|
|
120
|
+
if (el.textContent?.toLowerCase().includes("new branch")) {
|
|
114
121
|
hasNKey = true;
|
|
115
122
|
}
|
|
116
123
|
});
|
|
117
124
|
|
|
118
|
-
expect(hasNKey || container.textContent?.toLowerCase().includes(
|
|
125
|
+
expect(hasNKey || container.textContent?.toLowerCase().includes("n")).toBe(
|
|
126
|
+
true,
|
|
127
|
+
);
|
|
119
128
|
});
|
|
120
129
|
|
|
121
130
|
/**
|
|
122
131
|
* T075: Acceptance Scenario 2
|
|
123
132
|
* メイン画面にはqキーが存在しない(終了はCtrl+Cのみ)
|
|
124
133
|
*/
|
|
125
|
-
it(
|
|
126
|
-
(getAllBranches as ReturnType<typeof vi.fn>).mockResolvedValue(
|
|
134
|
+
it("[AC2] should not have q key on main screen", async () => {
|
|
135
|
+
(getAllBranches as ReturnType<typeof vi.fn>).mockResolvedValue(
|
|
136
|
+
mockBranches,
|
|
137
|
+
);
|
|
127
138
|
(listAdditionalWorktrees as ReturnType<typeof vi.fn>).mockResolvedValue([]);
|
|
128
139
|
|
|
129
140
|
const onExit = vi.fn();
|
|
@@ -134,23 +145,25 @@ describe('Acceptance: Navigation (User Story 2)', () => {
|
|
|
134
145
|
});
|
|
135
146
|
|
|
136
147
|
// Verify q key is NOT in the footer (main screen uses Ctrl+C for exit)
|
|
137
|
-
const footerText = container.textContent ||
|
|
148
|
+
const footerText = container.textContent || "";
|
|
138
149
|
// Main screen should not have 'q' for quit, but should have other keys
|
|
139
150
|
expect(footerText.toLowerCase()).not.toMatch(/\[q\]/);
|
|
140
|
-
expect(footerText.toLowerCase()).toContain(
|
|
151
|
+
expect(footerText.toLowerCase()).toContain("enter");
|
|
141
152
|
});
|
|
142
153
|
|
|
143
154
|
/**
|
|
144
155
|
* T076: Acceptance Scenario 3
|
|
145
156
|
* Worktree管理でアクション実行後に適切に遷移
|
|
146
157
|
*/
|
|
147
|
-
it(
|
|
148
|
-
(getAllBranches as ReturnType<typeof vi.fn>).mockResolvedValue(
|
|
158
|
+
it("[AC3] should handle worktree management navigation", async () => {
|
|
159
|
+
(getAllBranches as ReturnType<typeof vi.fn>).mockResolvedValue(
|
|
160
|
+
mockBranches,
|
|
161
|
+
);
|
|
149
162
|
(listAdditionalWorktrees as ReturnType<typeof vi.fn>).mockResolvedValue([
|
|
150
163
|
{
|
|
151
|
-
branch:
|
|
152
|
-
path:
|
|
153
|
-
head:
|
|
164
|
+
branch: "feature/test",
|
|
165
|
+
path: "/path/to/worktree",
|
|
166
|
+
head: "abc123",
|
|
154
167
|
isAccessible: true,
|
|
155
168
|
},
|
|
156
169
|
]);
|
|
@@ -163,19 +176,23 @@ describe('Acceptance: Navigation (User Story 2)', () => {
|
|
|
163
176
|
});
|
|
164
177
|
|
|
165
178
|
// Verify m key action is available for worktree management
|
|
166
|
-
const mKeyElements = container.querySelectorAll(
|
|
179
|
+
const mKeyElements = container.querySelectorAll("*");
|
|
167
180
|
let hasMKey = false;
|
|
168
181
|
mKeyElements.forEach((el) => {
|
|
169
|
-
if (el.textContent?.toLowerCase().includes(
|
|
182
|
+
if (el.textContent?.toLowerCase().includes("manage worktrees")) {
|
|
170
183
|
hasMKey = true;
|
|
171
184
|
}
|
|
172
185
|
});
|
|
173
186
|
|
|
174
|
-
expect(hasMKey || container.textContent?.toLowerCase().includes(
|
|
187
|
+
expect(hasMKey || container.textContent?.toLowerCase().includes("m")).toBe(
|
|
188
|
+
true,
|
|
189
|
+
);
|
|
175
190
|
});
|
|
176
191
|
|
|
177
|
-
it(
|
|
178
|
-
(getAllBranches as ReturnType<typeof vi.fn>).mockResolvedValue(
|
|
192
|
+
it("[Integration] should support all navigation keys", async () => {
|
|
193
|
+
(getAllBranches as ReturnType<typeof vi.fn>).mockResolvedValue(
|
|
194
|
+
mockBranches,
|
|
195
|
+
);
|
|
179
196
|
(listAdditionalWorktrees as ReturnType<typeof vi.fn>).mockResolvedValue([]);
|
|
180
197
|
|
|
181
198
|
const onExit = vi.fn();
|
|
@@ -191,8 +208,10 @@ describe('Acceptance: Navigation (User Story 2)', () => {
|
|
|
191
208
|
expect(enterKeys.length).toBeGreaterThan(0);
|
|
192
209
|
});
|
|
193
210
|
|
|
194
|
-
it(
|
|
195
|
-
(getAllBranches as ReturnType<typeof vi.fn>).mockResolvedValue(
|
|
211
|
+
it("[Integration] should display correct footer actions", async () => {
|
|
212
|
+
(getAllBranches as ReturnType<typeof vi.fn>).mockResolvedValue(
|
|
213
|
+
mockBranches,
|
|
214
|
+
);
|
|
196
215
|
(listAdditionalWorktrees as ReturnType<typeof vi.fn>).mockResolvedValue([]);
|
|
197
216
|
|
|
198
217
|
const onExit = vi.fn();
|
|
@@ -203,9 +222,9 @@ describe('Acceptance: Navigation (User Story 2)', () => {
|
|
|
203
222
|
});
|
|
204
223
|
|
|
205
224
|
// Verify footer has multiple action keys (main screen doesn't have q key)
|
|
206
|
-
const footerText = container.textContent ||
|
|
207
|
-
expect(footerText.toLowerCase()).toContain(
|
|
208
|
-
expect(footerText.toLowerCase()).toContain(
|
|
225
|
+
const footerText = container.textContent || "";
|
|
226
|
+
expect(footerText.toLowerCase()).toContain("enter");
|
|
227
|
+
expect(footerText.toLowerCase()).toContain("m"); // Manage worktrees
|
|
209
228
|
});
|
|
210
229
|
});
|
|
211
230
|
|
|
@@ -1,19 +1,27 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @vitest-environment happy-dom
|
|
3
3
|
*/
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
import
|
|
14
|
-
import
|
|
15
|
-
import
|
|
16
|
-
import
|
|
4
|
+
import {
|
|
5
|
+
describe,
|
|
6
|
+
it,
|
|
7
|
+
expect,
|
|
8
|
+
beforeEach,
|
|
9
|
+
afterEach,
|
|
10
|
+
afterAll,
|
|
11
|
+
vi,
|
|
12
|
+
} from "vitest";
|
|
13
|
+
import { act, render } from "@testing-library/react";
|
|
14
|
+
import React from "react";
|
|
15
|
+
import { Window } from "happy-dom";
|
|
16
|
+
import { App } from "../../components/App.js";
|
|
17
|
+
import type { BranchInfo, BranchItem } from "../../types.js";
|
|
18
|
+
import * as useGitDataModule from "../../hooks/useGitData.js";
|
|
19
|
+
import * as useScreenStateModule from "../../hooks/useScreenState.js";
|
|
20
|
+
import * as BranchListScreenModule from "../../components/screens/BranchListScreen.js";
|
|
21
|
+
import * as BranchActionSelectorScreenModule from "../../screens/BranchActionSelectorScreen.js";
|
|
22
|
+
import * as worktreeModule from "../../../../worktree.ts";
|
|
23
|
+
import * as gitModule from "../../../../git.ts";
|
|
24
|
+
import type { ScreenType } from "../../types.js";
|
|
17
25
|
|
|
18
26
|
const navigateToMock = vi.fn();
|
|
19
27
|
const goBackMock = vi.fn();
|
|
@@ -22,37 +30,47 @@ const resetMock = vi.fn();
|
|
|
22
30
|
const originalUseGitData = useGitDataModule.useGitData;
|
|
23
31
|
const originalUseScreenState = useScreenStateModule.useScreenState;
|
|
24
32
|
const originalBranchListScreen = BranchListScreenModule.BranchListScreen;
|
|
25
|
-
const originalBranchActionSelector =
|
|
33
|
+
const originalBranchActionSelector =
|
|
34
|
+
BranchActionSelectorScreenModule.BranchActionSelectorScreen;
|
|
26
35
|
const originalGetRepositoryRoot = gitModule.getRepositoryRoot;
|
|
27
36
|
|
|
28
|
-
const useGitDataSpy = vi.spyOn(useGitDataModule,
|
|
29
|
-
const useScreenStateSpy = vi.spyOn(useScreenStateModule,
|
|
30
|
-
const branchListScreenSpy = vi.spyOn(
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
37
|
+
const useGitDataSpy = vi.spyOn(useGitDataModule, "useGitData");
|
|
38
|
+
const useScreenStateSpy = vi.spyOn(useScreenStateModule, "useScreenState");
|
|
39
|
+
const branchListScreenSpy = vi.spyOn(
|
|
40
|
+
BranchListScreenModule,
|
|
41
|
+
"BranchListScreen",
|
|
42
|
+
);
|
|
43
|
+
const branchActionSelectorSpy = vi.spyOn(
|
|
44
|
+
BranchActionSelectorScreenModule,
|
|
45
|
+
"BranchActionSelectorScreen",
|
|
46
|
+
);
|
|
47
|
+
const switchToProtectedBranchSpy = vi.spyOn(
|
|
48
|
+
worktreeModule,
|
|
49
|
+
"switchToProtectedBranch",
|
|
50
|
+
);
|
|
51
|
+
const getRepositoryRootSpy = vi.spyOn(gitModule, "getRepositoryRoot");
|
|
34
52
|
|
|
35
53
|
const branchListProps: any[] = [];
|
|
36
54
|
const branchActionProps: any[] = [];
|
|
37
55
|
const aiToolProps: any[] = [];
|
|
38
56
|
let currentScreenState: ScreenType;
|
|
39
57
|
|
|
40
|
-
vi.mock(
|
|
58
|
+
vi.mock("../../components/screens/AIToolSelectorScreen.js", () => {
|
|
41
59
|
return {
|
|
42
60
|
AIToolSelectorScreen: (props: unknown) => {
|
|
43
61
|
aiToolProps.push(props);
|
|
44
|
-
return React.createElement(
|
|
62
|
+
return React.createElement("div");
|
|
45
63
|
},
|
|
46
64
|
};
|
|
47
65
|
});
|
|
48
66
|
|
|
49
|
-
describe(
|
|
67
|
+
describe("App protected branch handling", () => {
|
|
50
68
|
beforeEach(() => {
|
|
51
69
|
const window = new Window();
|
|
52
70
|
globalThis.window = window as any;
|
|
53
71
|
globalThis.document = window.document as any;
|
|
54
72
|
|
|
55
|
-
currentScreenState =
|
|
73
|
+
currentScreenState = "branch-list";
|
|
56
74
|
navigateToMock.mockReset();
|
|
57
75
|
goBackMock.mockReset();
|
|
58
76
|
resetMock.mockReset();
|
|
@@ -73,7 +91,7 @@ describe('App protected branch handling', () => {
|
|
|
73
91
|
goBack: goBackMock,
|
|
74
92
|
reset: () => {
|
|
75
93
|
resetMock();
|
|
76
|
-
currentScreenState =
|
|
94
|
+
currentScreenState = "branch-list";
|
|
77
95
|
},
|
|
78
96
|
}));
|
|
79
97
|
|
|
@@ -85,8 +103,8 @@ describe('App protected branch handling', () => {
|
|
|
85
103
|
branchActionProps.push(props);
|
|
86
104
|
return React.createElement(originalBranchActionSelector, props);
|
|
87
105
|
});
|
|
88
|
-
switchToProtectedBranchSpy.mockResolvedValue(
|
|
89
|
-
getRepositoryRootSpy.mockResolvedValue(
|
|
106
|
+
switchToProtectedBranchSpy.mockResolvedValue("local");
|
|
107
|
+
getRepositoryRootSpy.mockResolvedValue("/repo");
|
|
90
108
|
});
|
|
91
109
|
|
|
92
110
|
afterEach(() => {
|
|
@@ -95,7 +113,9 @@ describe('App protected branch handling', () => {
|
|
|
95
113
|
useScreenStateSpy.mockReset();
|
|
96
114
|
useScreenStateSpy.mockImplementation(originalUseScreenState);
|
|
97
115
|
branchListScreenSpy.mockImplementation(originalBranchListScreen as any);
|
|
98
|
-
branchActionSelectorSpy.mockImplementation(
|
|
116
|
+
branchActionSelectorSpy.mockImplementation(
|
|
117
|
+
originalBranchActionSelector as any,
|
|
118
|
+
);
|
|
99
119
|
switchToProtectedBranchSpy.mockReset();
|
|
100
120
|
getRepositoryRootSpy.mockReset();
|
|
101
121
|
branchActionProps.length = 0;
|
|
@@ -110,18 +130,18 @@ describe('App protected branch handling', () => {
|
|
|
110
130
|
getRepositoryRootSpy.mockRestore();
|
|
111
131
|
});
|
|
112
132
|
|
|
113
|
-
it(
|
|
133
|
+
it("shows protected branch warning and switches root without launching AI tool", async () => {
|
|
114
134
|
const branches: BranchInfo[] = [
|
|
115
135
|
{
|
|
116
|
-
name:
|
|
117
|
-
type:
|
|
118
|
-
branchType:
|
|
136
|
+
name: "main",
|
|
137
|
+
type: "local",
|
|
138
|
+
branchType: "main",
|
|
119
139
|
isCurrent: false,
|
|
120
140
|
},
|
|
121
141
|
{
|
|
122
|
-
name:
|
|
123
|
-
type:
|
|
124
|
-
branchType:
|
|
142
|
+
name: "feature/example",
|
|
143
|
+
type: "local",
|
|
144
|
+
branchType: "feature",
|
|
125
145
|
isCurrent: true,
|
|
126
146
|
},
|
|
127
147
|
];
|
|
@@ -141,15 +161,15 @@ describe('App protected branch handling', () => {
|
|
|
141
161
|
const latestProps = branchListProps.at(-1);
|
|
142
162
|
expect(latestProps).toBeDefined();
|
|
143
163
|
if (!latestProps) {
|
|
144
|
-
throw new Error(
|
|
164
|
+
throw new Error("BranchListScreen props missing");
|
|
145
165
|
}
|
|
146
166
|
|
|
147
167
|
const protectedBranch = (latestProps.branches as BranchItem[]).find(
|
|
148
|
-
(item) => item.name ===
|
|
168
|
+
(item) => item.name === "main",
|
|
149
169
|
);
|
|
150
170
|
expect(protectedBranch).toBeDefined();
|
|
151
171
|
if (!protectedBranch) {
|
|
152
|
-
throw new Error(
|
|
172
|
+
throw new Error("Protected branch item not found");
|
|
153
173
|
}
|
|
154
174
|
|
|
155
175
|
await act(async () => {
|
|
@@ -157,13 +177,15 @@ describe('App protected branch handling', () => {
|
|
|
157
177
|
await Promise.resolve();
|
|
158
178
|
});
|
|
159
179
|
|
|
160
|
-
expect(navigateToMock).toHaveBeenCalledWith(
|
|
180
|
+
expect(navigateToMock).toHaveBeenCalledWith("branch-action-selector");
|
|
161
181
|
expect(branchActionProps).not.toHaveLength(0);
|
|
162
182
|
const actionProps = branchActionProps.at(-1);
|
|
163
|
-
expect(actionProps?.mode).toBe(
|
|
164
|
-
expect(actionProps?.infoMessage).toContain(
|
|
165
|
-
expect(actionProps?.primaryLabel).toBe(
|
|
166
|
-
expect(actionProps?.secondaryLabel).toBe(
|
|
183
|
+
expect(actionProps?.mode).toBe("protected");
|
|
184
|
+
expect(actionProps?.infoMessage).toContain("is a root branch");
|
|
185
|
+
expect(actionProps?.primaryLabel).toBe("Use root branch (no worktree)");
|
|
186
|
+
expect(actionProps?.secondaryLabel).toBe(
|
|
187
|
+
"Create new branch from this branch",
|
|
188
|
+
);
|
|
167
189
|
|
|
168
190
|
await act(async () => {
|
|
169
191
|
actionProps?.onUseExisting();
|
|
@@ -172,12 +194,12 @@ describe('App protected branch handling', () => {
|
|
|
172
194
|
});
|
|
173
195
|
|
|
174
196
|
expect(switchToProtectedBranchSpy).toHaveBeenCalledWith({
|
|
175
|
-
branchName:
|
|
197
|
+
branchName: "main",
|
|
176
198
|
repoRoot: expect.any(String),
|
|
177
199
|
remoteRef: null,
|
|
178
200
|
});
|
|
179
201
|
|
|
180
|
-
expect(navigateToMock).toHaveBeenCalledWith(
|
|
202
|
+
expect(navigateToMock).toHaveBeenCalledWith("ai-tool-selector");
|
|
181
203
|
expect(aiToolProps).not.toHaveLength(0);
|
|
182
204
|
});
|
|
183
205
|
});
|