@akiojin/gwt 2.2.0 → 2.3.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.
Files changed (149) hide show
  1. package/README.ja.md +4 -4
  2. package/README.md +4 -4
  3. package/dist/cli/ui/components/App.d.ts +4 -4
  4. package/dist/cli/ui/components/App.d.ts.map +1 -1
  5. package/dist/cli/ui/components/App.js +144 -105
  6. package/dist/cli/ui/components/App.js.map +1 -1
  7. package/dist/cli/ui/components/common/Confirm.d.ts +1 -1
  8. package/dist/cli/ui/components/common/Confirm.d.ts.map +1 -1
  9. package/dist/cli/ui/components/common/Confirm.js +7 -7
  10. package/dist/cli/ui/components/common/Confirm.js.map +1 -1
  11. package/dist/cli/ui/components/common/ErrorBoundary.d.ts +1 -1
  12. package/dist/cli/ui/components/common/ErrorBoundary.d.ts.map +1 -1
  13. package/dist/cli/ui/components/common/ErrorBoundary.js +4 -4
  14. package/dist/cli/ui/components/common/ErrorBoundary.js.map +1 -1
  15. package/dist/cli/ui/components/common/Input.d.ts +2 -2
  16. package/dist/cli/ui/components/common/Input.d.ts.map +1 -1
  17. package/dist/cli/ui/components/common/Input.js +4 -4
  18. package/dist/cli/ui/components/common/Input.js.map +1 -1
  19. package/dist/cli/ui/components/common/LoadingIndicator.d.ts +1 -1
  20. package/dist/cli/ui/components/common/LoadingIndicator.d.ts.map +1 -1
  21. package/dist/cli/ui/components/common/LoadingIndicator.js +4 -4
  22. package/dist/cli/ui/components/common/LoadingIndicator.js.map +1 -1
  23. package/dist/cli/ui/components/common/Select.d.ts +1 -1
  24. package/dist/cli/ui/components/common/Select.d.ts.map +1 -1
  25. package/dist/cli/ui/components/common/Select.js +11 -12
  26. package/dist/cli/ui/components/common/Select.js.map +1 -1
  27. package/dist/cli/ui/components/screens/AIToolSelectorScreen.d.ts +2 -2
  28. package/dist/cli/ui/components/screens/AIToolSelectorScreen.d.ts.map +1 -1
  29. package/dist/cli/ui/components/screens/AIToolSelectorScreen.js +11 -11
  30. package/dist/cli/ui/components/screens/AIToolSelectorScreen.js.map +1 -1
  31. package/dist/cli/ui/components/screens/BranchCreatorScreen.d.ts +1 -1
  32. package/dist/cli/ui/components/screens/BranchCreatorScreen.d.ts.map +1 -1
  33. package/dist/cli/ui/components/screens/BranchCreatorScreen.js +39 -36
  34. package/dist/cli/ui/components/screens/BranchCreatorScreen.js.map +1 -1
  35. package/dist/cli/ui/components/screens/BranchListScreen.d.ts +3 -3
  36. package/dist/cli/ui/components/screens/BranchListScreen.d.ts.map +1 -1
  37. package/dist/cli/ui/components/screens/BranchListScreen.js +55 -50
  38. package/dist/cli/ui/components/screens/BranchListScreen.js.map +1 -1
  39. package/dist/cli/ui/components/screens/ExecutionModeSelectorScreen.d.ts +2 -2
  40. package/dist/cli/ui/components/screens/ExecutionModeSelectorScreen.d.ts.map +1 -1
  41. package/dist/cli/ui/components/screens/ExecutionModeSelectorScreen.js +25 -25
  42. package/dist/cli/ui/components/screens/ExecutionModeSelectorScreen.js.map +1 -1
  43. package/dist/cli/ui/components/screens/PRCleanupScreen.d.ts +2 -2
  44. package/dist/cli/ui/components/screens/PRCleanupScreen.js +21 -21
  45. package/dist/cli/ui/components/screens/SessionSelectorScreen.d.ts +1 -1
  46. package/dist/cli/ui/components/screens/SessionSelectorScreen.js +8 -8
  47. package/dist/cli/ui/components/screens/WorktreeManagerScreen.d.ts +1 -1
  48. package/dist/cli/ui/components/screens/WorktreeManagerScreen.js +8 -8
  49. package/dist/cli/ui/screens/BranchActionSelectorScreen.d.ts.map +1 -1
  50. package/dist/cli/ui/screens/BranchActionSelectorScreen.js +7 -4
  51. package/dist/cli/ui/screens/BranchActionSelectorScreen.js.map +1 -1
  52. package/dist/cli/ui/types.d.ts.map +1 -1
  53. package/dist/client/assets/{index-V6hDu9KS.js → index-Difv1Hwu.js} +2 -2
  54. package/dist/client/index.html +1 -1
  55. package/dist/config/builtin-tools.d.ts +10 -2
  56. package/dist/config/builtin-tools.d.ts.map +1 -1
  57. package/dist/config/builtin-tools.js +40 -4
  58. package/dist/config/builtin-tools.js.map +1 -1
  59. package/dist/config/index.d.ts.map +1 -1
  60. package/dist/config/index.js.map +1 -1
  61. package/dist/config/tools.d.ts.map +1 -1
  62. package/dist/config/tools.js +4 -3
  63. package/dist/config/tools.js.map +1 -1
  64. package/dist/gemini.d.ts +12 -0
  65. package/dist/gemini.d.ts.map +1 -0
  66. package/dist/gemini.js +154 -0
  67. package/dist/gemini.js.map +1 -0
  68. package/dist/git.d.ts.map +1 -1
  69. package/dist/git.js.map +1 -1
  70. package/dist/index.d.ts.map +1 -1
  71. package/dist/index.js +30 -0
  72. package/dist/index.js.map +1 -1
  73. package/dist/qwen.d.ts +12 -0
  74. package/dist/qwen.d.ts.map +1 -0
  75. package/dist/qwen.js +154 -0
  76. package/dist/qwen.js.map +1 -0
  77. package/dist/services/git.service.d.ts.map +1 -1
  78. package/dist/services/git.service.js.map +1 -1
  79. package/dist/web/client/src/components/BranchGraph.d.ts.map +1 -1
  80. package/dist/web/client/src/components/BranchGraph.js +1 -1
  81. package/dist/web/client/src/components/BranchGraph.js.map +1 -1
  82. package/dist/web/client/src/components/EnvEditor.d.ts.map +1 -1
  83. package/dist/web/client/src/components/EnvEditor.js +7 -4
  84. package/dist/web/client/src/components/EnvEditor.js.map +1 -1
  85. package/dist/web/client/src/pages/BranchDetailPage.d.ts.map +1 -1
  86. package/dist/web/client/src/pages/BranchDetailPage.js +55 -18
  87. package/dist/web/client/src/pages/BranchDetailPage.js.map +1 -1
  88. package/dist/web/client/src/pages/BranchListPage.d.ts.map +1 -1
  89. package/dist/web/client/src/pages/BranchListPage.js +10 -4
  90. package/dist/web/client/src/pages/BranchListPage.js.map +1 -1
  91. package/dist/web/client/src/pages/ConfigManagementPage.d.ts.map +1 -1
  92. package/dist/web/client/src/pages/ConfigManagementPage.js +4 -2
  93. package/dist/web/client/src/pages/ConfigManagementPage.js.map +1 -1
  94. package/package.json +2 -1
  95. package/src/cli/ui/__tests__/acceptance/navigation.acceptance.test.tsx +69 -50
  96. package/src/cli/ui/__tests__/components/App.protected-branch.test.tsx +67 -45
  97. package/src/cli/ui/__tests__/components/App.shortcuts.test.tsx +117 -75
  98. package/src/cli/ui/__tests__/components/App.test.tsx +45 -37
  99. package/src/cli/ui/__tests__/components/common/Confirm.test.tsx +35 -22
  100. package/src/cli/ui/__tests__/components/common/ErrorBoundary.test.tsx +22 -22
  101. package/src/cli/ui/__tests__/components/common/Input.test.tsx +29 -22
  102. package/src/cli/ui/__tests__/components/common/LoadingIndicator.test.tsx +40 -34
  103. package/src/cli/ui/__tests__/components/common/Select.memo.test.tsx +57 -66
  104. package/src/cli/ui/__tests__/components/common/Select.test.tsx +121 -91
  105. package/src/cli/ui/__tests__/components/parts/Footer.test.tsx +18 -16
  106. package/src/cli/ui/__tests__/components/parts/Header.test.tsx +13 -13
  107. package/src/cli/ui/__tests__/components/parts/ScrollableList.test.tsx +20 -20
  108. package/src/cli/ui/__tests__/components/parts/Stats.test.tsx +38 -26
  109. package/src/cli/ui/__tests__/components/screens/AIToolSelectorScreen.test.tsx +31 -31
  110. package/src/cli/ui/__tests__/components/screens/BranchCreatorScreen.test.tsx +73 -37
  111. package/src/cli/ui/__tests__/components/screens/BranchListScreen.test.tsx +261 -153
  112. package/src/cli/ui/__tests__/components/screens/ExecutionModeSelectorScreen.test.tsx +38 -32
  113. package/src/cli/ui/__tests__/components/screens/PRCleanupScreen.test.tsx +39 -39
  114. package/src/cli/ui/__tests__/components/screens/SessionSelectorScreen.test.tsx +49 -21
  115. package/src/cli/ui/__tests__/components/screens/WorktreeManagerScreen.test.tsx +52 -28
  116. package/src/cli/ui/__tests__/integration/edgeCases.test.tsx +84 -48
  117. package/src/cli/ui/__tests__/integration/navigation.test.tsx +111 -83
  118. package/src/cli/ui/__tests__/integration/realtimeUpdate.test.tsx +111 -108
  119. package/src/cli/ui/__tests__/performance/branchList.performance.test.tsx +50 -37
  120. package/src/cli/ui/__tests__/performance/useMemoOptimization.test.tsx +75 -76
  121. package/src/cli/ui/components/App.tsx +247 -150
  122. package/src/cli/ui/components/common/Confirm.tsx +13 -9
  123. package/src/cli/ui/components/common/ErrorBoundary.tsx +8 -5
  124. package/src/cli/ui/components/common/Input.tsx +12 -4
  125. package/src/cli/ui/components/common/LoadingIndicator.tsx +8 -5
  126. package/src/cli/ui/components/common/Select.tsx +28 -17
  127. package/src/cli/ui/components/parts/Header.test.tsx +5 -15
  128. package/src/cli/ui/components/screens/AIToolSelectorScreen.tsx +19 -13
  129. package/src/cli/ui/components/screens/BranchCreatorScreen.tsx +74 -54
  130. package/src/cli/ui/components/screens/BranchListScreen.tsx +92 -75
  131. package/src/cli/ui/components/screens/ExecutionModeSelectorScreen.tsx +35 -28
  132. package/src/cli/ui/components/screens/PRCleanupScreen.tsx +22 -22
  133. package/src/cli/ui/components/screens/SessionSelectorScreen.tsx +8 -8
  134. package/src/cli/ui/components/screens/WorktreeManagerScreen.tsx +8 -8
  135. package/src/cli/ui/screens/BranchActionSelectorScreen.tsx +9 -4
  136. package/src/cli/ui/types.ts +8 -1
  137. package/src/config/builtin-tools.ts +42 -4
  138. package/src/config/index.ts +2 -12
  139. package/src/config/tools.ts +16 -6
  140. package/src/gemini.ts +202 -0
  141. package/src/git.ts +2 -1
  142. package/src/index.ts +30 -0
  143. package/src/qwen.ts +208 -0
  144. package/src/services/git.service.ts +2 -1
  145. package/src/web/client/src/components/BranchGraph.tsx +3 -2
  146. package/src/web/client/src/components/EnvEditor.tsx +44 -11
  147. package/src/web/client/src/pages/BranchDetailPage.tsx +165 -54
  148. package/src/web/client/src/pages/BranchListPage.tsx +37 -13
  149. package/src/web/client/src/pages/ConfigManagementPage.tsx +28 -9
@@ -1,13 +1,13 @@
1
1
  /**
2
2
  * @vitest-environment happy-dom
3
3
  */
4
- import { describe, it, expect, beforeEach } from 'vitest';
5
- import { render } from '@testing-library/react';
6
- import React from 'react';
7
- import { Footer } from '../../../components/parts/Footer.js';
8
- import { Window } from 'happy-dom';
4
+ import { describe, it, expect, beforeEach } from "vitest";
5
+ import { render } from "@testing-library/react";
6
+ import React from "react";
7
+ import { Footer } from "../../../components/parts/Footer.js";
8
+ import { Window } from "happy-dom";
9
9
 
10
- describe('Footer', () => {
10
+ describe("Footer", () => {
11
11
  beforeEach(() => {
12
12
  // Setup happy-dom
13
13
  const window = new Window();
@@ -16,12 +16,12 @@ describe('Footer', () => {
16
16
  });
17
17
 
18
18
  const mockActions = [
19
- { key: 'enter', description: 'Select' },
20
- { key: 'esc', description: 'Back' },
21
- { key: 'h', description: 'Help' },
19
+ { key: "enter", description: "Select" },
20
+ { key: "esc", description: "Back" },
21
+ { key: "h", description: "Help" },
22
22
  ];
23
23
 
24
- it('should render all actions', () => {
24
+ it("should render all actions", () => {
25
25
  const { getByText } = render(<Footer actions={mockActions} />);
26
26
 
27
27
  expect(getByText(/enter/)).toBeDefined();
@@ -32,29 +32,31 @@ describe('Footer', () => {
32
32
  expect(getByText(/Help/)).toBeDefined();
33
33
  });
34
34
 
35
- it('should render with empty actions array', () => {
35
+ it("should render with empty actions array", () => {
36
36
  const { container } = render(<Footer actions={[]} />);
37
37
 
38
38
  expect(container).toBeDefined();
39
39
  });
40
40
 
41
- it('should render single action', () => {
42
- const singleAction = [{ key: 'esc', description: 'Exit' }];
41
+ it("should render single action", () => {
42
+ const singleAction = [{ key: "esc", description: "Exit" }];
43
43
  const { getByText } = render(<Footer actions={singleAction} />);
44
44
 
45
45
  expect(getByText(/esc/)).toBeDefined();
46
46
  expect(getByText(/Exit/)).toBeDefined();
47
47
  });
48
48
 
49
- it('should render actions in a horizontal layout', () => {
49
+ it("should render actions in a horizontal layout", () => {
50
50
  const { container } = render(<Footer actions={mockActions} />);
51
51
 
52
52
  // Verify component renders without error
53
53
  expect(container).toBeDefined();
54
54
  });
55
55
 
56
- it('should accept custom separator', () => {
57
- const { getAllByText } = render(<Footer actions={mockActions} separator=" | " />);
56
+ it("should accept custom separator", () => {
57
+ const { getAllByText } = render(
58
+ <Footer actions={mockActions} separator=" | " />,
59
+ );
58
60
 
59
61
  const separators = getAllByText(/\|/);
60
62
  expect(separators.length).toBeGreaterThan(0);
@@ -1,13 +1,13 @@
1
1
  /**
2
2
  * @vitest-environment happy-dom
3
3
  */
4
- import { describe, it, expect, beforeEach } from 'vitest';
5
- import { render } from '@testing-library/react';
6
- import React from 'react';
7
- import { Header } from '../../../components/parts/Header.js';
8
- import { Window } from 'happy-dom';
4
+ import { describe, it, expect, beforeEach } from "vitest";
5
+ import { render } from "@testing-library/react";
6
+ import React from "react";
7
+ import { Header } from "../../../components/parts/Header.js";
8
+ import { Window } from "happy-dom";
9
9
 
10
- describe('Header', () => {
10
+ describe("Header", () => {
11
11
  beforeEach(() => {
12
12
  // Setup happy-dom
13
13
  const window = new Window();
@@ -15,38 +15,38 @@ describe('Header', () => {
15
15
  globalThis.document = window.document as any;
16
16
  });
17
17
 
18
- it('should render title', () => {
18
+ it("should render title", () => {
19
19
  const { getByText } = render(<Header title="gwt" />);
20
20
 
21
- expect(getByText('gwt')).toBeDefined();
21
+ expect(getByText("gwt")).toBeDefined();
22
22
  });
23
23
 
24
- it('should render divider', () => {
24
+ it("should render divider", () => {
25
25
  const { getByText } = render(<Header title="Test" />);
26
26
 
27
27
  // Check for a line of dashes (divider)
28
28
  expect(getByText(/─+/)).toBeDefined();
29
29
  });
30
30
 
31
- it('should render title in bold and cyan by default', () => {
31
+ it("should render title in bold and cyan by default", () => {
32
32
  const { container } = render(<Header title="Test Title" />);
33
33
 
34
34
  expect(container).toBeDefined();
35
35
  });
36
36
 
37
- it('should accept custom title color', () => {
37
+ it("should accept custom title color", () => {
38
38
  const { container } = render(<Header title="Test" titleColor="green" />);
39
39
 
40
40
  expect(container).toBeDefined();
41
41
  });
42
42
 
43
- it('should accept custom divider character', () => {
43
+ it("should accept custom divider character", () => {
44
44
  const { getByText } = render(<Header title="Test" dividerChar="=" />);
45
45
 
46
46
  expect(getByText(/=+/)).toBeDefined();
47
47
  });
48
48
 
49
- it('should render without divider when showDivider is false', () => {
49
+ it("should render without divider when showDivider is false", () => {
50
50
  const { queryByText } = render(<Header title="Test" showDivider={false} />);
51
51
 
52
52
  expect(queryByText(/─+/)).toBeNull();
@@ -1,14 +1,14 @@
1
1
  /**
2
2
  * @vitest-environment happy-dom
3
3
  */
4
- import { describe, it, expect, beforeEach } from 'vitest';
5
- import { render } from '@testing-library/react';
6
- import React from 'react';
7
- import { ScrollableList } from '../../../components/parts/ScrollableList.js';
8
- import { Text } from 'ink';
9
- import { Window } from 'happy-dom';
4
+ import { describe, it, expect, beforeEach } from "vitest";
5
+ import { render } from "@testing-library/react";
6
+ import React from "react";
7
+ import { ScrollableList } from "../../../components/parts/ScrollableList.js";
8
+ import { Text } from "ink";
9
+ import { Window } from "happy-dom";
10
10
 
11
- describe('ScrollableList', () => {
11
+ describe("ScrollableList", () => {
12
12
  beforeEach(() => {
13
13
  // Setup happy-dom
14
14
  const window = new Window();
@@ -16,53 +16,53 @@ describe('ScrollableList', () => {
16
16
  globalThis.document = window.document as any;
17
17
  });
18
18
 
19
- it('should render children', () => {
19
+ it("should render children", () => {
20
20
  const { getByText } = render(
21
21
  <ScrollableList>
22
22
  <Text>Item 1</Text>
23
23
  <Text>Item 2</Text>
24
24
  <Text>Item 3</Text>
25
- </ScrollableList>
25
+ </ScrollableList>,
26
26
  );
27
27
 
28
- expect(getByText('Item 1')).toBeDefined();
29
- expect(getByText('Item 2')).toBeDefined();
30
- expect(getByText('Item 3')).toBeDefined();
28
+ expect(getByText("Item 1")).toBeDefined();
29
+ expect(getByText("Item 2")).toBeDefined();
30
+ expect(getByText("Item 3")).toBeDefined();
31
31
  });
32
32
 
33
- it('should render with no children', () => {
33
+ it("should render with no children", () => {
34
34
  const { container } = render(<ScrollableList>{null}</ScrollableList>);
35
35
 
36
36
  expect(container).toBeDefined();
37
37
  });
38
38
 
39
- it('should accept maxHeight prop', () => {
39
+ it("should accept maxHeight prop", () => {
40
40
  const { container } = render(
41
41
  <ScrollableList maxHeight={10}>
42
42
  <Text>Content</Text>
43
- </ScrollableList>
43
+ </ScrollableList>,
44
44
  );
45
45
 
46
46
  expect(container).toBeDefined();
47
47
  });
48
48
 
49
- it('should render in a vertical layout', () => {
49
+ it("should render in a vertical layout", () => {
50
50
  const { container } = render(
51
51
  <ScrollableList>
52
52
  <Text>Content</Text>
53
- </ScrollableList>
53
+ </ScrollableList>,
54
54
  );
55
55
 
56
56
  expect(container).toBeDefined();
57
57
  });
58
58
 
59
- it('should handle single child', () => {
59
+ it("should handle single child", () => {
60
60
  const { getByText } = render(
61
61
  <ScrollableList>
62
62
  <Text>Single Item</Text>
63
- </ScrollableList>
63
+ </ScrollableList>,
64
64
  );
65
65
 
66
- expect(getByText('Single Item')).toBeDefined();
66
+ expect(getByText("Single Item")).toBeDefined();
67
67
  });
68
68
  });
@@ -1,14 +1,14 @@
1
1
  /**
2
2
  * @vitest-environment happy-dom
3
3
  */
4
- import { describe, it, expect, beforeEach } from 'vitest';
5
- import { render } from '@testing-library/react';
6
- import React from 'react';
7
- import { Stats } from '../../../components/parts/Stats.js';
8
- import type { Statistics } from '../../../types.js';
9
- import { Window } from 'happy-dom';
10
-
11
- describe('Stats', () => {
4
+ import { describe, it, expect, beforeEach } from "vitest";
5
+ import { render } from "@testing-library/react";
6
+ import React from "react";
7
+ import { Stats } from "../../../components/parts/Stats.js";
8
+ import type { Statistics } from "../../../types.js";
9
+ import { Window } from "happy-dom";
10
+
11
+ describe("Stats", () => {
12
12
  beforeEach(() => {
13
13
  // Setup happy-dom
14
14
  const window = new Window();
@@ -21,10 +21,10 @@ describe('Stats', () => {
21
21
  remoteCount: 8,
22
22
  worktreeCount: 3,
23
23
  changesCount: 2,
24
- lastUpdated: new Date('2025-01-25T12:00:00Z'),
24
+ lastUpdated: new Date("2025-01-25T12:00:00Z"),
25
25
  };
26
26
 
27
- it('should render all statistics', () => {
27
+ it("should render all statistics", () => {
28
28
  const { getByText } = render(<Stats stats={mockStats} />);
29
29
 
30
30
  expect(getByText(/Local:/)).toBeDefined();
@@ -37,7 +37,7 @@ describe('Stats', () => {
37
37
  expect(getByText(/2/)).toBeDefined();
38
38
  });
39
39
 
40
- it('should render with zero counts', () => {
40
+ it("should render with zero counts", () => {
41
41
  const zeroStats: Statistics = {
42
42
  localCount: 0,
43
43
  remoteCount: 0,
@@ -53,21 +53,23 @@ describe('Stats', () => {
53
53
  expect(zeros.length).toBe(4); // All 4 counts are 0
54
54
  });
55
55
 
56
- it('should render in a horizontal layout', () => {
56
+ it("should render in a horizontal layout", () => {
57
57
  const { container } = render(<Stats stats={mockStats} />);
58
58
 
59
59
  // Verify component renders without error
60
60
  expect(container).toBeDefined();
61
61
  });
62
62
 
63
- it('should accept custom separator', () => {
64
- const { getAllByText } = render(<Stats stats={mockStats} separator=" | " />);
63
+ it("should accept custom separator", () => {
64
+ const { getAllByText } = render(
65
+ <Stats stats={mockStats} separator=" | " />,
66
+ );
65
67
 
66
68
  const separators = getAllByText(/\|/);
67
69
  expect(separators.length).toBeGreaterThan(0);
68
70
  });
69
71
 
70
- it('should handle large numbers', () => {
72
+ it("should handle large numbers", () => {
71
73
  const largeStats: Statistics = {
72
74
  localCount: 999,
73
75
  remoteCount: 888,
@@ -84,51 +86,61 @@ describe('Stats', () => {
84
86
  expect(getByText(/666/)).toBeDefined();
85
87
  });
86
88
 
87
- it('should display lastUpdated when provided', () => {
89
+ it("should display lastUpdated when provided", () => {
88
90
  const now = new Date();
89
91
  const lastUpdated = new Date(now.getTime() - 5000); // 5 seconds ago
90
92
 
91
- const { getByText } = render(<Stats stats={mockStats} lastUpdated={lastUpdated} />);
93
+ const { getByText } = render(
94
+ <Stats stats={mockStats} lastUpdated={lastUpdated} />,
95
+ );
92
96
 
93
97
  expect(getByText(/Updated:/)).toBeDefined();
94
98
  expect(getByText(/ago/)).toBeDefined();
95
99
  });
96
100
 
97
- it('should not display lastUpdated when null', () => {
98
- const { queryByText } = render(<Stats stats={mockStats} lastUpdated={null} />);
101
+ it("should not display lastUpdated when null", () => {
102
+ const { queryByText } = render(
103
+ <Stats stats={mockStats} lastUpdated={null} />,
104
+ );
99
105
 
100
106
  expect(queryByText(/Updated:/)).toBeNull();
101
107
  });
102
108
 
103
- it('should not display lastUpdated when not provided', () => {
109
+ it("should not display lastUpdated when not provided", () => {
104
110
  const { queryByText } = render(<Stats stats={mockStats} />);
105
111
 
106
112
  expect(queryByText(/Updated:/)).toBeNull();
107
113
  });
108
114
 
109
- it('should format relative time correctly (seconds)', () => {
115
+ it("should format relative time correctly (seconds)", () => {
110
116
  const now = new Date();
111
117
  const lastUpdated = new Date(now.getTime() - 30000); // 30 seconds ago
112
118
 
113
- const { getByText } = render(<Stats stats={mockStats} lastUpdated={lastUpdated} />);
119
+ const { getByText } = render(
120
+ <Stats stats={mockStats} lastUpdated={lastUpdated} />,
121
+ );
114
122
 
115
123
  expect(getByText(/30s ago/)).toBeDefined();
116
124
  });
117
125
 
118
- it('should format relative time correctly (minutes)', () => {
126
+ it("should format relative time correctly (minutes)", () => {
119
127
  const now = new Date();
120
128
  const lastUpdated = new Date(now.getTime() - 120000); // 2 minutes ago
121
129
 
122
- const { getByText } = render(<Stats stats={mockStats} lastUpdated={lastUpdated} />);
130
+ const { getByText } = render(
131
+ <Stats stats={mockStats} lastUpdated={lastUpdated} />,
132
+ );
123
133
 
124
134
  expect(getByText(/2m ago/)).toBeDefined();
125
135
  });
126
136
 
127
- it('should format relative time correctly (hours)', () => {
137
+ it("should format relative time correctly (hours)", () => {
128
138
  const now = new Date();
129
139
  const lastUpdated = new Date(now.getTime() - 7200000); // 2 hours ago
130
140
 
131
- const { getByText } = render(<Stats stats={mockStats} lastUpdated={lastUpdated} />);
141
+ const { getByText } = render(
142
+ <Stats stats={mockStats} lastUpdated={lastUpdated} />,
143
+ );
132
144
 
133
145
  expect(getByText(/2h ago/)).toBeDefined();
134
146
  });
@@ -1,29 +1,29 @@
1
1
  /**
2
2
  * @vitest-environment happy-dom
3
3
  */
4
- import { describe, it, expect, beforeEach, vi } from 'vitest';
5
- import { render, waitFor } from '@testing-library/react';
6
- import React from 'react';
7
- import { AIToolSelectorScreen } from '../../../components/screens/AIToolSelectorScreen.js';
8
- import { Window } from 'happy-dom';
4
+ import { describe, it, expect, beforeEach, vi } from "vitest";
5
+ import { render, waitFor } from "@testing-library/react";
6
+ import React from "react";
7
+ import { AIToolSelectorScreen } from "../../../components/screens/AIToolSelectorScreen.js";
8
+ import { Window } from "happy-dom";
9
9
 
10
10
  // Mock getAllTools
11
- vi.mock('../../../config/tools.js', () => ({
11
+ vi.mock("../../../config/tools.js", () => ({
12
12
  getAllTools: vi.fn().mockResolvedValue([
13
13
  {
14
- id: 'claude-code',
15
- displayName: 'Claude Code',
14
+ id: "claude-code",
15
+ displayName: "Claude Code",
16
16
  isBuiltin: true,
17
17
  },
18
18
  {
19
- id: 'codex-cli',
20
- displayName: 'Codex CLI',
19
+ id: "codex-cli",
20
+ displayName: "Codex",
21
21
  isBuiltin: true,
22
22
  },
23
23
  ]),
24
24
  }));
25
25
 
26
- describe('AIToolSelectorScreen', () => {
26
+ describe("AIToolSelectorScreen", () => {
27
27
  beforeEach(() => {
28
28
  // Setup happy-dom
29
29
  const window = new Window();
@@ -31,49 +31,49 @@ describe('AIToolSelectorScreen', () => {
31
31
  globalThis.document = window.document as any;
32
32
  });
33
33
 
34
- it('should render header with title', () => {
34
+ it("should render header with title", () => {
35
35
  const onBack = vi.fn();
36
36
  const onSelect = vi.fn();
37
37
  const { getByText } = render(
38
- <AIToolSelectorScreen onBack={onBack} onSelect={onSelect} />
38
+ <AIToolSelectorScreen onBack={onBack} onSelect={onSelect} />,
39
39
  );
40
40
 
41
41
  expect(getByText(/AI Tool Selection/i)).toBeDefined();
42
42
  });
43
43
 
44
- it('should render AI tool options', async () => {
44
+ it("should render AI tool options", async () => {
45
45
  const onBack = vi.fn();
46
46
  const onSelect = vi.fn();
47
47
  const { getByText } = render(
48
- <AIToolSelectorScreen onBack={onBack} onSelect={onSelect} />
48
+ <AIToolSelectorScreen onBack={onBack} onSelect={onSelect} />,
49
49
  );
50
50
 
51
51
  // Wait for tools to load
52
52
  await waitFor(() => {
53
53
  expect(getByText(/Claude Code/i)).toBeDefined();
54
- expect(getByText(/Codex CLI/i)).toBeDefined();
54
+ expect(getByText(/Codex/i)).toBeDefined();
55
55
  });
56
56
  });
57
57
 
58
- it('should render footer with actions', () => {
58
+ it("should render footer with actions", () => {
59
59
  const onBack = vi.fn();
60
60
  const onSelect = vi.fn();
61
61
  const { getAllByText } = render(
62
- <AIToolSelectorScreen onBack={onBack} onSelect={onSelect} />
62
+ <AIToolSelectorScreen onBack={onBack} onSelect={onSelect} />,
63
63
  );
64
64
 
65
65
  expect(getAllByText(/enter/i).length).toBeGreaterThan(0);
66
66
  expect(getAllByText(/esc/i).length).toBeGreaterThan(0);
67
67
  });
68
68
 
69
- it('should use terminal height for layout calculation', () => {
69
+ it("should use terminal height for layout calculation", () => {
70
70
  const originalRows = process.stdout.rows;
71
71
  process.stdout.rows = 30;
72
72
 
73
73
  const onBack = vi.fn();
74
74
  const onSelect = vi.fn();
75
75
  const { container } = render(
76
- <AIToolSelectorScreen onBack={onBack} onSelect={onSelect} />
76
+ <AIToolSelectorScreen onBack={onBack} onSelect={onSelect} />,
77
77
  );
78
78
 
79
79
  expect(container).toBeDefined();
@@ -81,22 +81,22 @@ describe('AIToolSelectorScreen', () => {
81
81
  process.stdout.rows = originalRows;
82
82
  });
83
83
 
84
- it('should handle back navigation with ESC key', () => {
84
+ it("should handle back navigation with ESC key", () => {
85
85
  const onBack = vi.fn();
86
86
  const onSelect = vi.fn();
87
87
  const { container } = render(
88
- <AIToolSelectorScreen onBack={onBack} onSelect={onSelect} />
88
+ <AIToolSelectorScreen onBack={onBack} onSelect={onSelect} />,
89
89
  );
90
90
 
91
91
  // Test will verify onBack is called when ESC is pressed
92
92
  expect(container).toBeDefined();
93
93
  });
94
94
 
95
- it('should handle tool selection', () => {
95
+ it("should handle tool selection", () => {
96
96
  const onBack = vi.fn();
97
97
  const onSelect = vi.fn();
98
98
  const { container } = render(
99
- <AIToolSelectorScreen onBack={onBack} onSelect={onSelect} />
99
+ <AIToolSelectorScreen onBack={onBack} onSelect={onSelect} />,
100
100
  );
101
101
 
102
102
  // Test will verify onSelect is called with correct tool
@@ -106,15 +106,15 @@ describe('AIToolSelectorScreen', () => {
106
106
  /**
107
107
  * T210: カスタムツール表示のテスト
108
108
  */
109
- describe('Custom tool display', () => {
110
- it('should load tools from getAllTools() dynamically', async () => {
109
+ describe("Custom tool display", () => {
110
+ it("should load tools from getAllTools() dynamically", async () => {
111
111
  // TODO: 実装後にテストを記述
112
112
  // getAllTools()がモックされ、呼び出されることを確認
113
113
  // モックの戻り値がツールアイテムとして表示されることを確認
114
114
  expect(true).toBe(true);
115
115
  });
116
116
 
117
- it('should display both builtin and custom tools', async () => {
117
+ it("should display both builtin and custom tools", async () => {
118
118
  // TODO: 実装後にテストを記述
119
119
  // getAllTools()がビルトインツール(claude-code, codex-cli)と
120
120
  // カスタムツール(例: aider)を返す場合、
@@ -122,28 +122,28 @@ describe('AIToolSelectorScreen', () => {
122
122
  expect(true).toBe(true);
123
123
  });
124
124
 
125
- it('should display custom tool with icon if defined', async () => {
125
+ it("should display custom tool with icon if defined", async () => {
126
126
  // TODO: 実装後にテストを記述
127
127
  // カスタムツールにiconフィールドがある場合、
128
128
  // それが表示されることを確認
129
129
  expect(true).toBe(true);
130
130
  });
131
131
 
132
- it('should display custom tool without icon if not defined', async () => {
132
+ it("should display custom tool without icon if not defined", async () => {
133
133
  // TODO: 実装後にテストを記述
134
134
  // カスタムツールにiconフィールドがない場合、
135
135
  // ツール名のみが表示されることを確認
136
136
  expect(true).toBe(true);
137
137
  });
138
138
 
139
- it('should handle custom tool selection', async () => {
139
+ it("should handle custom tool selection", async () => {
140
140
  // TODO: 実装後にテストを記述
141
141
  // カスタムツールを選択した場合、
142
142
  // onSelect()がカスタムツールのIDで呼び出されることを確認
143
143
  expect(true).toBe(true);
144
144
  });
145
145
 
146
- it('should display only builtin tools if no custom tools exist', async () => {
146
+ it("should display only builtin tools if no custom tools exist", async () => {
147
147
  // TODO: 実装後にテストを記述
148
148
  // getAllTools()がビルトインツールのみを返す場合、
149
149
  // ビルトインツールのみが表示されることを確認