@akiojin/gwt 2.9.0 → 2.10.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.
@@ -13,47 +13,69 @@ import {
13
13
  import { act, render } from "@testing-library/react";
14
14
  import React from "react";
15
15
  import { Window } from "happy-dom";
16
- import { App } from "../../components/App.js";
17
16
  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
17
  import type { ScreenType } from "../../types.js";
25
18
 
26
19
  const navigateToMock = vi.fn();
27
20
  const goBackMock = vi.fn();
28
21
  const resetMock = vi.fn();
29
22
 
30
- const originalUseGitData = useGitDataModule.useGitData;
31
- const originalUseScreenState = useScreenStateModule.useScreenState;
32
- const originalBranchListScreen = BranchListScreenModule.BranchListScreen;
33
- const originalBranchActionSelector =
34
- BranchActionSelectorScreenModule.BranchActionSelectorScreen;
35
- const originalGetRepositoryRoot = gitModule.getRepositoryRoot;
36
-
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");
52
-
53
23
  const branchListProps: any[] = [];
54
24
  const branchActionProps: any[] = [];
55
25
  const aiToolProps: any[] = [];
56
26
  let currentScreenState: ScreenType;
27
+ let App: typeof import("../../components/App.js").App;
28
+ const useGitDataMock = vi.fn();
29
+ const useScreenStateMock = vi.fn();
30
+ const switchToProtectedBranchMock = vi.fn();
31
+ const getRepositoryRootMock = vi.fn();
32
+
33
+ vi.mock("../../hooks/useGitData.js", () => ({
34
+ useGitData: (...args: any[]) => useGitDataMock(...args),
35
+ }));
36
+
37
+ vi.mock("../../hooks/useScreenState.js", () => ({
38
+ useScreenState: (...args: any[]) => useScreenStateMock(...args),
39
+ }));
40
+
41
+ vi.mock("../../../../worktree.js", async () => {
42
+ const actual = await vi.importActual<
43
+ typeof import("../../../../worktree.js")
44
+ >("../../../../worktree.js");
45
+ return {
46
+ ...actual,
47
+ switchToProtectedBranch: switchToProtectedBranchMock,
48
+ };
49
+ });
50
+
51
+ vi.mock("../../../../git.js", async () => {
52
+ const actual =
53
+ await vi.importActual<typeof import("../../../../git.js")>(
54
+ "../../../../git.js",
55
+ );
56
+ return {
57
+ ...actual,
58
+ getRepositoryRoot: getRepositoryRootMock,
59
+ };
60
+ });
61
+
62
+ vi.mock("../../components/screens/BranchListScreen.js", () => {
63
+ return {
64
+ BranchListScreen: (props: any) => {
65
+ branchListProps.push(props);
66
+ return React.createElement("div", null, "BranchListScreenMock");
67
+ },
68
+ };
69
+ });
70
+
71
+ vi.mock("../../screens/BranchActionSelectorScreen.js", () => {
72
+ return {
73
+ BranchActionSelectorScreen: (props: any) => {
74
+ branchActionProps.push(props);
75
+ return React.createElement("div", null, "BranchActionSelectorMock");
76
+ },
77
+ };
78
+ });
57
79
 
58
80
  vi.mock("../../components/screens/AIToolSelectorScreen.js", () => {
59
81
  return {
@@ -65,7 +87,7 @@ vi.mock("../../components/screens/AIToolSelectorScreen.js", () => {
65
87
  });
66
88
 
67
89
  describe("App protected branch handling", () => {
68
- beforeEach(() => {
90
+ beforeEach(async () => {
69
91
  const window = new Window();
70
92
  globalThis.window = window as any;
71
93
  globalThis.document = window.document as any;
@@ -78,11 +100,12 @@ describe("App protected branch handling", () => {
78
100
  branchActionProps.length = 0;
79
101
  aiToolProps.length = 0;
80
102
 
81
- useGitDataSpy.mockReset();
82
- switchToProtectedBranchSpy.mockReset();
83
- getRepositoryRootSpy.mockReset();
103
+ useGitDataMock.mockReset();
104
+ switchToProtectedBranchMock.mockReset();
105
+ getRepositoryRootMock.mockReset();
106
+ App = (await import("../../components/App.js")).App;
84
107
 
85
- useScreenStateSpy.mockImplementation(() => ({
108
+ useScreenStateMock.mockImplementation(() => ({
86
109
  currentScreen: currentScreenState,
87
110
  navigateTo: (screen: ScreenType) => {
88
111
  navigateToMock(screen);
@@ -95,41 +118,18 @@ describe("App protected branch handling", () => {
95
118
  },
96
119
  }));
97
120
 
98
- branchListScreenSpy.mockImplementation((props: any) => {
99
- branchListProps.push(props);
100
- return React.createElement(originalBranchListScreen, props);
101
- });
102
- branchActionSelectorSpy.mockImplementation((props: any) => {
103
- branchActionProps.push(props);
104
- return React.createElement(originalBranchActionSelector, props);
105
- });
106
- switchToProtectedBranchSpy.mockResolvedValue("local");
107
- getRepositoryRootSpy.mockResolvedValue("/repo");
121
+ switchToProtectedBranchMock.mockResolvedValue("local");
122
+ getRepositoryRootMock.mockResolvedValue("/repo");
108
123
  });
109
124
 
110
125
  afterEach(() => {
111
- useGitDataSpy.mockReset();
112
- useGitDataSpy.mockImplementation(originalUseGitData);
113
- useScreenStateSpy.mockReset();
114
- useScreenStateSpy.mockImplementation(originalUseScreenState);
115
- branchListScreenSpy.mockImplementation(originalBranchListScreen as any);
116
- branchActionSelectorSpy.mockImplementation(
117
- originalBranchActionSelector as any,
118
- );
119
- switchToProtectedBranchSpy.mockReset();
120
- getRepositoryRootSpy.mockReset();
126
+ useGitDataMock.mockReset();
127
+ useScreenStateMock.mockReset();
128
+ switchToProtectedBranchMock.mockReset();
129
+ getRepositoryRootMock.mockReset();
121
130
  branchActionProps.length = 0;
122
131
  });
123
132
 
124
- afterAll(() => {
125
- useGitDataSpy.mockRestore();
126
- useScreenStateSpy.mockRestore();
127
- branchListScreenSpy.mockRestore();
128
- branchActionSelectorSpy.mockRestore();
129
- switchToProtectedBranchSpy.mockRestore();
130
- getRepositoryRootSpy.mockRestore();
131
- });
132
-
133
133
  it("shows protected branch warning and switches root without launching AI tool", async () => {
134
134
  const branches: BranchInfo[] = [
135
135
  {
@@ -146,14 +146,14 @@ describe("App protected branch handling", () => {
146
146
  },
147
147
  ];
148
148
 
149
- useGitDataSpy.mockImplementation(() => ({
149
+ useGitDataMock.mockReturnValue({
150
150
  branches,
151
151
  worktrees: [],
152
152
  loading: false,
153
153
  error: null,
154
154
  refresh: vi.fn(),
155
155
  lastUpdated: null,
156
- }));
156
+ });
157
157
 
158
158
  render(<App onExit={vi.fn()} />);
159
159
 
@@ -193,7 +193,7 @@ describe("App protected branch handling", () => {
193
193
  await Promise.resolve();
194
194
  });
195
195
 
196
- expect(switchToProtectedBranchSpy).toHaveBeenCalledWith({
196
+ expect(switchToProtectedBranchMock).toHaveBeenCalledWith({
197
197
  branchName: "main",
198
198
  repoRoot: expect.any(String),
199
199
  remoteRef: null,
@@ -15,14 +15,7 @@ import { render, act, waitFor } from "@testing-library/react";
15
15
  import React from "react";
16
16
  import type { BranchItem, CleanupTarget } from "../../types.js";
17
17
  import { Window } from "happy-dom";
18
- import * as useGitDataModule from "../../hooks/useGitData.js";
19
- import * as useScreenStateModule from "../../hooks/useScreenState.js";
20
- import * as WorktreeManagerScreenModule from "../../components/screens/WorktreeManagerScreen.js";
21
- import * as BranchCreatorScreenModule from "../../components/screens/BranchCreatorScreen.js";
22
- import * as BranchListScreenModule from "../../components/screens/BranchListScreen.js";
23
- import * as worktreeModule from "../../../../worktree.ts";
24
- import * as gitModule from "../../../../git.ts";
25
- import { App } from "../../components/App.js";
18
+ let App: typeof import("../../components/App.js").App;
26
19
 
27
20
  const navigateToMock = vi.fn();
28
21
  const goBackMock = vi.fn();
@@ -32,49 +25,77 @@ const worktreeScreenProps: any[] = [];
32
25
  const branchCreatorProps: any[] = [];
33
26
  const branchListProps: any[] = [];
34
27
 
35
- const originalUseGitData = useGitDataModule.useGitData;
36
- const originalUseScreenState = useScreenStateModule.useScreenState;
37
- const originalWorktreeManagerScreen =
38
- WorktreeManagerScreenModule.WorktreeManagerScreen;
39
- const originalBranchCreatorScreen =
40
- BranchCreatorScreenModule.BranchCreatorScreen;
41
- const originalBranchListScreen = BranchListScreenModule.BranchListScreen;
42
- const originalGetMergedPRWorktrees = worktreeModule.getMergedPRWorktrees;
43
- const originalGenerateWorktreePath = worktreeModule.generateWorktreePath;
44
- const originalCreateWorktree = worktreeModule.createWorktree;
45
- const originalRemoveWorktree = worktreeModule.removeWorktree;
46
- const originalGetRepositoryRoot = gitModule.getRepositoryRoot;
47
- const originalDeleteBranch = gitModule.deleteBranch;
48
-
49
- const useGitDataSpy = vi.spyOn(useGitDataModule, "useGitData");
50
- const useScreenStateSpy = vi.spyOn(useScreenStateModule, "useScreenState");
51
- const worktreeManagerScreenSpy = vi.spyOn(
52
- WorktreeManagerScreenModule,
53
- "WorktreeManagerScreen",
54
- );
55
- const branchCreatorScreenSpy = vi.spyOn(
56
- BranchCreatorScreenModule,
57
- "BranchCreatorScreen",
58
- );
59
- const branchListScreenSpy = vi.spyOn(
60
- BranchListScreenModule,
61
- "BranchListScreen",
62
- );
63
- const getMergedPRWorktreesSpy = vi.spyOn(
64
- worktreeModule,
65
- "getMergedPRWorktrees",
66
- );
67
- const generateWorktreePathSpy = vi.spyOn(
68
- worktreeModule,
69
- "generateWorktreePath",
70
- );
71
- const createWorktreeSpy = vi.spyOn(worktreeModule, "createWorktree");
72
- const removeWorktreeSpy = vi.spyOn(worktreeModule, "removeWorktree");
73
- const getRepositoryRootSpy = vi.spyOn(gitModule, "getRepositoryRoot");
74
- const deleteBranchSpy = vi.spyOn(gitModule, "deleteBranch");
28
+ const useGitDataMock = vi.fn();
29
+ const useScreenStateMock = vi.fn();
30
+ const getMergedPRWorktreesMock = vi.fn();
31
+ const generateWorktreePathMock = vi.fn();
32
+ const createWorktreeMock = vi.fn();
33
+ const removeWorktreeMock = vi.fn();
34
+ const getRepositoryRootMock = vi.fn();
35
+ const deleteBranchMock = vi.fn();
36
+
37
+ vi.mock("../../hooks/useGitData.js", () => ({
38
+ useGitData: (...args: any[]) => useGitDataMock(...args),
39
+ }));
40
+
41
+ vi.mock("../../hooks/useScreenState.js", () => ({
42
+ useScreenState: (...args: any[]) => useScreenStateMock(...args),
43
+ }));
44
+
45
+ vi.mock("../../../../worktree.js", async () => {
46
+ const actual = await vi.importActual<
47
+ typeof import("../../../../worktree.js")
48
+ >("../../../../worktree.js");
49
+ return {
50
+ ...actual,
51
+ getMergedPRWorktrees: getMergedPRWorktreesMock,
52
+ generateWorktreePath: generateWorktreePathMock,
53
+ createWorktree: createWorktreeMock,
54
+ removeWorktree: removeWorktreeMock,
55
+ };
56
+ });
57
+
58
+ vi.mock("../../../../git.js", async () => {
59
+ const actual =
60
+ await vi.importActual<typeof import("../../../../git.js")>(
61
+ "../../../../git.js",
62
+ );
63
+ return {
64
+ ...actual,
65
+ getRepositoryRoot: getRepositoryRootMock,
66
+ deleteBranch: deleteBranchMock,
67
+ };
68
+ });
69
+
70
+ vi.mock("../../components/screens/WorktreeManagerScreen.js", () => {
71
+ return {
72
+ WorktreeManagerScreen: (props: any) => {
73
+ worktreeScreenProps.push(props);
74
+ return React.createElement("div", null, "WorktreeManagerScreenMock");
75
+ },
76
+ };
77
+ });
78
+
79
+ vi.mock("../../components/screens/BranchCreatorScreen.js", () => {
80
+ return {
81
+ BranchCreatorScreen: (props: any) => {
82
+ branchCreatorProps.push(props);
83
+ return React.createElement("div", null, "BranchCreatorScreenMock");
84
+ },
85
+ };
86
+ });
87
+
88
+ vi.mock("../../components/screens/BranchListScreen.js", () => {
89
+ return {
90
+ BranchListScreen: (props: any) => {
91
+ branchListProps.push(props);
92
+ return React.createElement("div", null, "BranchListScreenMock");
93
+ },
94
+ };
95
+ });
75
96
 
76
97
  describe("App shortcuts integration", () => {
77
- beforeEach(() => {
98
+ beforeEach(async () => {
78
99
  if (typeof globalThis.document === "undefined") {
79
100
  const window = new Window();
80
101
  globalThis.window = window as any;
@@ -86,7 +107,7 @@ describe("App shortcuts integration", () => {
86
107
  navigateToMock.mockClear();
87
108
  goBackMock.mockClear();
88
109
  resetMock.mockClear();
89
- useGitDataSpy.mockImplementation(() => ({
110
+ useGitDataMock.mockReturnValue({
90
111
  branches: [],
91
112
  worktrees: [
92
113
  {
@@ -99,26 +120,14 @@ describe("App shortcuts integration", () => {
99
120
  error: null,
100
121
  refresh: vi.fn(),
101
122
  lastUpdated: null,
102
- }));
103
- useScreenStateSpy.mockImplementation(() => ({
104
- currentScreen: "worktree-manager",
105
- navigateTo: navigateToMock as _Mock,
106
- goBack: goBackMock as _Mock,
107
- reset: resetMock as _Mock,
108
- }));
109
- worktreeManagerScreenSpy.mockImplementation((props: any) => {
110
- worktreeScreenProps.push(props);
111
- return React.createElement(originalWorktreeManagerScreen, props);
112
- });
113
- branchCreatorScreenSpy.mockImplementation((props: any) => {
114
- branchCreatorProps.push(props);
115
- return React.createElement(originalBranchCreatorScreen, props);
116
123
  });
117
- branchListScreenSpy.mockImplementation((props: any) => {
118
- branchListProps.push(props);
119
- return React.createElement(originalBranchListScreen, props);
124
+ useScreenStateMock.mockReturnValue({
125
+ currentScreen: "worktree-manager",
126
+ navigateTo: navigateToMock as Mock,
127
+ goBack: goBackMock as Mock,
128
+ reset: resetMock as Mock,
120
129
  });
121
- getMergedPRWorktreesSpy.mockResolvedValue([
130
+ getMergedPRWorktreesMock.mockResolvedValue([
122
131
  {
123
132
  branch: "feature/add-new-feature",
124
133
  cleanupType: "worktree-and-branch",
@@ -152,44 +161,26 @@ describe("App shortcuts integration", () => {
152
161
  isAccessible: true,
153
162
  },
154
163
  ] as CleanupTarget[]);
155
- generateWorktreePathSpy.mockResolvedValue("/worktrees/new-branch");
156
- createWorktreeSpy.mockResolvedValue(undefined);
157
- removeWorktreeSpy.mockResolvedValue(undefined);
158
- getRepositoryRootSpy.mockResolvedValue("/repo");
159
- deleteBranchSpy.mockResolvedValue(undefined);
164
+ generateWorktreePathMock.mockResolvedValue("/worktrees/new-branch");
165
+ createWorktreeMock.mockResolvedValue(undefined);
166
+ removeWorktreeMock.mockResolvedValue(undefined);
167
+ getRepositoryRootMock.mockResolvedValue("/repo");
168
+ deleteBranchMock.mockResolvedValue(undefined);
169
+ App = (await import("../../components/App.js")).App;
160
170
  });
161
171
 
162
172
  afterEach(() => {
163
- useGitDataSpy.mockReset();
164
- useScreenStateSpy.mockReset();
165
- worktreeManagerScreenSpy.mockReset();
166
- branchCreatorScreenSpy.mockReset();
167
- branchListScreenSpy.mockReset();
168
- getMergedPRWorktreesSpy.mockReset();
169
- generateWorktreePathSpy.mockReset();
170
- createWorktreeSpy.mockReset();
171
- removeWorktreeSpy.mockReset();
172
- getRepositoryRootSpy.mockReset();
173
- deleteBranchSpy.mockReset();
174
- useGitDataSpy.mockImplementation(originalUseGitData);
175
- useScreenStateSpy.mockImplementation(originalUseScreenState);
176
- worktreeManagerScreenSpy.mockImplementation(
177
- originalWorktreeManagerScreen as any,
178
- );
179
- branchCreatorScreenSpy.mockImplementation(
180
- originalBranchCreatorScreen as any,
181
- );
182
- branchListScreenSpy.mockImplementation(originalBranchListScreen as any);
183
- getMergedPRWorktreesSpy.mockImplementation(
184
- originalGetMergedPRWorktrees as any,
185
- );
186
- generateWorktreePathSpy.mockImplementation(
187
- originalGenerateWorktreePath as any,
188
- );
189
- createWorktreeSpy.mockImplementation(originalCreateWorktree as any);
190
- removeWorktreeSpy.mockImplementation(originalRemoveWorktree as any);
191
- getRepositoryRootSpy.mockImplementation(originalGetRepositoryRoot as any);
192
- deleteBranchSpy.mockImplementation(originalDeleteBranch as any);
173
+ useGitDataMock.mockReset();
174
+ useScreenStateMock.mockReset();
175
+ getMergedPRWorktreesMock.mockReset();
176
+ generateWorktreePathMock.mockReset();
177
+ createWorktreeMock.mockReset();
178
+ removeWorktreeMock.mockReset();
179
+ getRepositoryRootMock.mockReset();
180
+ deleteBranchMock.mockReset();
181
+ worktreeScreenProps.length = 0;
182
+ branchCreatorProps.length = 0;
183
+ branchListProps.length = 0;
193
184
  });
194
185
 
195
186
  it("navigates to AI tool selector when worktree is selected", () => {
@@ -209,11 +200,11 @@ describe("App shortcuts integration", () => {
209
200
  const onExit = vi.fn();
210
201
 
211
202
  // Update screen state mock to branch-creator for this test
212
- useScreenStateSpy.mockReturnValue({
203
+ useScreenStateMock.mockReturnValue({
213
204
  currentScreen: "branch-creator",
214
- navigateTo: navigateToMock as _Mock,
215
- goBack: goBackMock as _Mock,
216
- reset: resetMock as _Mock,
205
+ navigateTo: navigateToMock as Mock,
206
+ goBack: goBackMock as Mock,
207
+ reset: resetMock as Mock,
217
208
  });
218
209
 
219
210
  render(<App onExit={onExit} />);
@@ -225,7 +216,7 @@ describe("App shortcuts integration", () => {
225
216
  await onCreate("feature/new-branch");
226
217
  });
227
218
 
228
- expect(createWorktreeSpy).toHaveBeenCalledWith(
219
+ expect(createWorktreeMock).toHaveBeenCalledWith(
229
220
  expect.objectContaining({
230
221
  branchName: "feature/new-branch",
231
222
  isNewBranch: true,
@@ -243,25 +234,25 @@ describe("App shortcuts integration", () => {
243
234
  let resolveRemoveWorktree: (() => void) | undefined;
244
235
  let resolveDeleteBranch: (() => void) | undefined;
245
236
 
246
- removeWorktreeSpy.mockImplementationOnce(
237
+ removeWorktreeMock.mockImplementationOnce(
247
238
  () =>
248
239
  new Promise<void>((resolve) => {
249
240
  resolveRemoveWorktree = resolve;
250
241
  }),
251
242
  );
252
243
 
253
- deleteBranchSpy.mockImplementationOnce(
244
+ deleteBranchMock.mockImplementationOnce(
254
245
  () =>
255
246
  new Promise<void>((resolve) => {
256
247
  resolveDeleteBranch = resolve;
257
248
  }),
258
249
  );
259
250
 
260
- useScreenStateSpy.mockReturnValue({
251
+ useScreenStateMock.mockReturnValue({
261
252
  currentScreen: "branch-list",
262
- navigateTo: navigateToMock as _Mock,
263
- goBack: goBackMock as _Mock,
264
- reset: resetMock as _Mock,
253
+ navigateTo: navigateToMock as Mock,
254
+ goBack: goBackMock as Mock,
255
+ reset: resetMock as Mock,
265
256
  });
266
257
 
267
258
  render(<App onExit={onExit} />);
@@ -299,11 +290,11 @@ describe("App shortcuts integration", () => {
299
290
 
300
291
  resolveDeleteBranch?.();
301
292
 
302
- expect(removeWorktreeSpy).toHaveBeenCalledWith(
293
+ expect(removeWorktreeMock).toHaveBeenCalledWith(
303
294
  "/worktrees/feature-add-new-feature",
304
295
  true,
305
296
  );
306
- expect(deleteBranchSpy).toHaveBeenCalledWith(
297
+ expect(deleteBranchMock).toHaveBeenCalledWith(
307
298
  "feature/add-new-feature",
308
299
  true,
309
300
  );
@@ -339,17 +330,3 @@ describe("App shortcuts integration", () => {
339
330
  }
340
331
  });
341
332
  });
342
-
343
- afterAll(() => {
344
- useGitDataSpy.mockRestore();
345
- useScreenStateSpy.mockRestore();
346
- worktreeManagerScreenSpy.mockRestore();
347
- branchCreatorScreenSpy.mockRestore();
348
- branchListScreenSpy.mockRestore();
349
- getMergedPRWorktreesSpy.mockRestore();
350
- generateWorktreePathSpy.mockRestore();
351
- createWorktreeSpy.mockRestore();
352
- removeWorktreeSpy.mockRestore();
353
- getRepositoryRootSpy.mockRestore();
354
- deleteBranchSpy.mockRestore();
355
- });