@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.
- package/README.ja.md +4 -4
- package/README.md +4 -4
- package/dist/cli/ui/components/App.d.ts +4 -4
- package/dist/cli/ui/components/App.d.ts.map +1 -1
- package/dist/cli/ui/components/App.js +144 -105
- 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 +2 -2
- 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/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.map +1 -1
- package/dist/client/assets/{index-V6hDu9KS.js → index-Difv1Hwu.js} +2 -2
- package/dist/client/index.html +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 +12 -0
- package/dist/gemini.d.ts.map +1 -0
- package/dist/gemini.js +154 -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 +30 -0
- package/dist/index.js.map +1 -1
- package/dist/qwen.d.ts +12 -0
- package/dist/qwen.d.ts.map +1 -0
- package/dist/qwen.js +154 -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/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/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 +40 -34
- 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 +247 -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 +19 -13
- 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/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 +8 -1
- 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 +202 -0
- package/src/git.ts +2 -1
- package/src/index.ts +30 -0
- package/src/qwen.ts +208 -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
|
@@ -1,27 +1,28 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @vitest-environment happy-dom
|
|
3
3
|
*/
|
|
4
|
-
import { describe, it, expect, beforeEach, afterEach, vi } from
|
|
5
|
-
import { act, render } from
|
|
6
|
-
import { render as inkRender } from
|
|
7
|
-
import React from
|
|
8
|
-
import { BranchListScreen } from
|
|
9
|
-
import type { BranchInfo, BranchItem, Statistics } from
|
|
10
|
-
import { formatBranchItem } from
|
|
11
|
-
import stringWidth from
|
|
12
|
-
import { Window } from
|
|
13
|
-
|
|
14
|
-
const stripAnsi = (value: string): string =>
|
|
4
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
|
|
5
|
+
import { act, render } from "@testing-library/react";
|
|
6
|
+
import { render as inkRender } from "ink-testing-library";
|
|
7
|
+
import React from "react";
|
|
8
|
+
import { BranchListScreen } from "../../../components/screens/BranchListScreen.js";
|
|
9
|
+
import type { BranchInfo, BranchItem, Statistics } from "../../../types.js";
|
|
10
|
+
import { formatBranchItem } from "../../../utils/branchFormatter.js";
|
|
11
|
+
import stringWidth from "string-width";
|
|
12
|
+
import { Window } from "happy-dom";
|
|
13
|
+
|
|
14
|
+
const stripAnsi = (value: string): string =>
|
|
15
|
+
value.replace(/\u001b\[[0-9;]*m/g, "");
|
|
15
16
|
const stripControlSequences = (value: string): string =>
|
|
16
17
|
value.replace(/\u001b\[([0-9;?]*)([A-Za-z])/g, (_, params, command) => {
|
|
17
|
-
if (command ===
|
|
18
|
-
const count = Number(params ||
|
|
19
|
-
return
|
|
18
|
+
if (command === "C") {
|
|
19
|
+
const count = Number(params || "1");
|
|
20
|
+
return " ".repeat(Number.isNaN(count) ? 0 : count);
|
|
20
21
|
}
|
|
21
|
-
return
|
|
22
|
+
return "";
|
|
22
23
|
});
|
|
23
24
|
|
|
24
|
-
describe(
|
|
25
|
+
describe("BranchListScreen", () => {
|
|
25
26
|
beforeEach(() => {
|
|
26
27
|
vi.useFakeTimers();
|
|
27
28
|
// Setup happy-dom
|
|
@@ -36,25 +37,25 @@ describe('BranchListScreen', () => {
|
|
|
36
37
|
|
|
37
38
|
const mockBranches: BranchItem[] = [
|
|
38
39
|
{
|
|
39
|
-
name:
|
|
40
|
-
type:
|
|
41
|
-
branchType:
|
|
40
|
+
name: "main",
|
|
41
|
+
type: "local",
|
|
42
|
+
branchType: "main",
|
|
42
43
|
isCurrent: true,
|
|
43
|
-
icons: [
|
|
44
|
+
icons: ["⚡", "⭐"],
|
|
44
45
|
hasChanges: false,
|
|
45
|
-
label:
|
|
46
|
-
value:
|
|
46
|
+
label: "⚡ ⭐ main",
|
|
47
|
+
value: "main",
|
|
47
48
|
latestCommitTimestamp: 1_700_000_000,
|
|
48
49
|
},
|
|
49
50
|
{
|
|
50
|
-
name:
|
|
51
|
-
type:
|
|
52
|
-
branchType:
|
|
51
|
+
name: "feature/test",
|
|
52
|
+
type: "local",
|
|
53
|
+
branchType: "feature",
|
|
53
54
|
isCurrent: false,
|
|
54
|
-
icons: [
|
|
55
|
+
icons: ["✨"],
|
|
55
56
|
hasChanges: false,
|
|
56
|
-
label:
|
|
57
|
-
value:
|
|
57
|
+
label: "✨ feature/test",
|
|
58
|
+
value: "feature/test",
|
|
58
59
|
latestCommitTimestamp: 1_699_000_000,
|
|
59
60
|
},
|
|
60
61
|
];
|
|
@@ -67,46 +68,62 @@ describe('BranchListScreen', () => {
|
|
|
67
68
|
lastUpdated: new Date(),
|
|
68
69
|
};
|
|
69
70
|
|
|
70
|
-
it(
|
|
71
|
+
it("should render header with title", () => {
|
|
71
72
|
const onSelect = vi.fn();
|
|
72
73
|
const { getByText } = render(
|
|
73
|
-
<BranchListScreen
|
|
74
|
+
<BranchListScreen
|
|
75
|
+
branches={mockBranches}
|
|
76
|
+
stats={mockStats}
|
|
77
|
+
onSelect={onSelect}
|
|
78
|
+
/>,
|
|
74
79
|
);
|
|
75
80
|
|
|
76
81
|
expect(getByText(/gwt - Branch Selection/i)).toBeDefined();
|
|
77
82
|
});
|
|
78
83
|
|
|
79
|
-
it(
|
|
84
|
+
it("should render statistics", () => {
|
|
80
85
|
const onSelect = vi.fn();
|
|
81
86
|
const { container, getByText } = render(
|
|
82
|
-
<BranchListScreen
|
|
87
|
+
<BranchListScreen
|
|
88
|
+
branches={mockBranches}
|
|
89
|
+
stats={mockStats}
|
|
90
|
+
onSelect={onSelect}
|
|
91
|
+
/>,
|
|
83
92
|
);
|
|
84
93
|
|
|
85
|
-
expect(container.textContent).toContain(
|
|
94
|
+
expect(container.textContent).toContain("Local: 2");
|
|
86
95
|
expect(getByText(/Remote:/)).toBeDefined();
|
|
87
96
|
});
|
|
88
97
|
|
|
89
|
-
it(
|
|
98
|
+
it("should render branch list", () => {
|
|
90
99
|
const onSelect = vi.fn();
|
|
91
100
|
const { getByText } = render(
|
|
92
|
-
<BranchListScreen
|
|
101
|
+
<BranchListScreen
|
|
102
|
+
branches={mockBranches}
|
|
103
|
+
stats={mockStats}
|
|
104
|
+
onSelect={onSelect}
|
|
105
|
+
/>,
|
|
93
106
|
);
|
|
94
107
|
|
|
95
108
|
expect(getByText(/main/)).toBeDefined();
|
|
96
109
|
expect(getByText(/feature\/test/)).toBeDefined();
|
|
97
110
|
});
|
|
98
111
|
|
|
99
|
-
it(
|
|
112
|
+
it("should render footer with actions", () => {
|
|
100
113
|
const onSelect = vi.fn();
|
|
101
114
|
const { getAllByText } = render(
|
|
102
|
-
<BranchListScreen
|
|
115
|
+
<BranchListScreen
|
|
116
|
+
branches={mockBranches}
|
|
117
|
+
stats={mockStats}
|
|
118
|
+
onSelect={onSelect}
|
|
119
|
+
/>,
|
|
103
120
|
);
|
|
104
121
|
|
|
105
122
|
// Check for enter key (main screen doesn't have q key, exit is Ctrl+C only)
|
|
106
123
|
expect(getAllByText(/enter/i).length).toBeGreaterThan(0);
|
|
107
124
|
});
|
|
108
125
|
|
|
109
|
-
it(
|
|
126
|
+
it("should handle empty branch list", () => {
|
|
110
127
|
const onSelect = vi.fn();
|
|
111
128
|
const emptyStats: Statistics = {
|
|
112
129
|
localCount: 0,
|
|
@@ -117,13 +134,13 @@ describe('BranchListScreen', () => {
|
|
|
117
134
|
};
|
|
118
135
|
|
|
119
136
|
const { container } = render(
|
|
120
|
-
<BranchListScreen branches={[]} stats={emptyStats} onSelect={onSelect}
|
|
137
|
+
<BranchListScreen branches={[]} stats={emptyStats} onSelect={onSelect} />,
|
|
121
138
|
);
|
|
122
139
|
|
|
123
140
|
expect(container).toBeDefined();
|
|
124
141
|
});
|
|
125
142
|
|
|
126
|
-
it(
|
|
143
|
+
it("should display loading indicator after the configured delay", async () => {
|
|
127
144
|
const onSelect = vi.fn();
|
|
128
145
|
const { queryByText, getByText } = render(
|
|
129
146
|
<BranchListScreen
|
|
@@ -132,11 +149,11 @@ describe('BranchListScreen', () => {
|
|
|
132
149
|
onSelect={onSelect}
|
|
133
150
|
loading={true}
|
|
134
151
|
loadingIndicatorDelay={10}
|
|
135
|
-
|
|
152
|
+
/>,
|
|
136
153
|
);
|
|
137
154
|
|
|
138
155
|
await act(async () => {
|
|
139
|
-
if (typeof (vi as any).advanceTimersByTime ===
|
|
156
|
+
if (typeof (vi as any).advanceTimersByTime === "function") {
|
|
140
157
|
(vi as any).advanceTimersByTime(10);
|
|
141
158
|
} else {
|
|
142
159
|
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
@@ -146,18 +163,23 @@ describe('BranchListScreen', () => {
|
|
|
146
163
|
expect(getByText(/Loading Git information/i)).toBeDefined();
|
|
147
164
|
});
|
|
148
165
|
|
|
149
|
-
it(
|
|
166
|
+
it("should display error state", () => {
|
|
150
167
|
const onSelect = vi.fn();
|
|
151
|
-
const error = new Error(
|
|
168
|
+
const error = new Error("Failed to load branches");
|
|
152
169
|
const { getByText } = render(
|
|
153
|
-
<BranchListScreen
|
|
170
|
+
<BranchListScreen
|
|
171
|
+
branches={[]}
|
|
172
|
+
stats={mockStats}
|
|
173
|
+
onSelect={onSelect}
|
|
174
|
+
error={error}
|
|
175
|
+
/>,
|
|
154
176
|
);
|
|
155
177
|
|
|
156
178
|
expect(getByText(/Error:/i)).toBeDefined();
|
|
157
179
|
expect(getByText(/Failed to load branches/i)).toBeDefined();
|
|
158
180
|
});
|
|
159
181
|
|
|
160
|
-
it(
|
|
182
|
+
it("should use terminal height for layout calculation", () => {
|
|
161
183
|
const onSelect = vi.fn();
|
|
162
184
|
|
|
163
185
|
// Mock process.stdout
|
|
@@ -165,7 +187,11 @@ describe('BranchListScreen', () => {
|
|
|
165
187
|
process.stdout.rows = 30;
|
|
166
188
|
|
|
167
189
|
const { container } = render(
|
|
168
|
-
<BranchListScreen
|
|
190
|
+
<BranchListScreen
|
|
191
|
+
branches={mockBranches}
|
|
192
|
+
stats={mockStats}
|
|
193
|
+
onSelect={onSelect}
|
|
194
|
+
/>,
|
|
169
195
|
);
|
|
170
196
|
|
|
171
197
|
expect(container).toBeDefined();
|
|
@@ -174,10 +200,14 @@ describe('BranchListScreen', () => {
|
|
|
174
200
|
process.stdout.rows = originalRows;
|
|
175
201
|
});
|
|
176
202
|
|
|
177
|
-
it(
|
|
203
|
+
it("should display branch icons", () => {
|
|
178
204
|
const onSelect = vi.fn();
|
|
179
205
|
const { getByText } = render(
|
|
180
|
-
<BranchListScreen
|
|
206
|
+
<BranchListScreen
|
|
207
|
+
branches={mockBranches}
|
|
208
|
+
stats={mockStats}
|
|
209
|
+
onSelect={onSelect}
|
|
210
|
+
/>,
|
|
181
211
|
);
|
|
182
212
|
|
|
183
213
|
// Check for icons in labels
|
|
@@ -186,34 +216,42 @@ describe('BranchListScreen', () => {
|
|
|
186
216
|
expect(getByText(/✨/)).toBeDefined(); // feature icon
|
|
187
217
|
});
|
|
188
218
|
|
|
189
|
-
it(
|
|
219
|
+
it("should render latest commit timestamp for each branch", () => {
|
|
190
220
|
const onSelect = vi.fn();
|
|
191
221
|
const { container } = render(
|
|
192
|
-
<BranchListScreen
|
|
222
|
+
<BranchListScreen
|
|
223
|
+
branches={mockBranches}
|
|
224
|
+
stats={mockStats}
|
|
225
|
+
onSelect={onSelect}
|
|
226
|
+
/>,
|
|
193
227
|
);
|
|
194
228
|
|
|
195
|
-
const textContent = container.textContent ??
|
|
229
|
+
const textContent = container.textContent ?? "";
|
|
196
230
|
const matches = textContent.match(/\d{4}-\d{2}-\d{2} \d{2}:\d{2}/g) ?? [];
|
|
197
231
|
expect(matches.length).toBe(mockBranches.length);
|
|
198
232
|
});
|
|
199
233
|
|
|
200
|
-
it(
|
|
201
|
-
process.env.FORCE_COLOR =
|
|
234
|
+
it("should highlight the selected branch with cyan background", async () => {
|
|
235
|
+
process.env.FORCE_COLOR = "1";
|
|
202
236
|
const onSelect = vi.fn();
|
|
203
237
|
let renderResult: ReturnType<typeof inkRender>;
|
|
204
238
|
await act(async () => {
|
|
205
239
|
renderResult = inkRender(
|
|
206
|
-
<BranchListScreen
|
|
207
|
-
|
|
240
|
+
<BranchListScreen
|
|
241
|
+
branches={mockBranches}
|
|
242
|
+
stats={mockStats}
|
|
243
|
+
onSelect={onSelect}
|
|
244
|
+
/>,
|
|
245
|
+
{ stripAnsi: false },
|
|
208
246
|
);
|
|
209
247
|
});
|
|
210
248
|
|
|
211
|
-
const frame = renderResult!.lastFrame() ??
|
|
212
|
-
expect(frame).toContain(
|
|
249
|
+
const frame = renderResult!.lastFrame() ?? "";
|
|
250
|
+
expect(frame).toContain("\u001b[46m"); // cyan background ANSI code
|
|
213
251
|
});
|
|
214
252
|
|
|
215
|
-
it(
|
|
216
|
-
process.env.FORCE_COLOR =
|
|
253
|
+
it("should align timestamps even when unpushed icon is displayed", async () => {
|
|
254
|
+
process.env.FORCE_COLOR = "1";
|
|
217
255
|
const onSelect = vi.fn();
|
|
218
256
|
|
|
219
257
|
const originalColumns = process.stdout.columns;
|
|
@@ -221,25 +259,25 @@ describe('BranchListScreen', () => {
|
|
|
221
259
|
|
|
222
260
|
const branchInfos: BranchInfo[] = [
|
|
223
261
|
{
|
|
224
|
-
name:
|
|
225
|
-
type:
|
|
226
|
-
branchType:
|
|
262
|
+
name: "feature/update-ui",
|
|
263
|
+
type: "local",
|
|
264
|
+
branchType: "feature",
|
|
227
265
|
isCurrent: false,
|
|
228
266
|
hasUnpushedCommits: true,
|
|
229
267
|
latestCommitTimestamp: 1_700_000_000,
|
|
230
268
|
},
|
|
231
269
|
{
|
|
232
|
-
name:
|
|
233
|
-
type:
|
|
234
|
-
branchType:
|
|
270
|
+
name: "origin/main",
|
|
271
|
+
type: "remote",
|
|
272
|
+
branchType: "main",
|
|
235
273
|
isCurrent: false,
|
|
236
274
|
hasUnpushedCommits: false,
|
|
237
275
|
latestCommitTimestamp: 1_699_999_000,
|
|
238
276
|
},
|
|
239
277
|
{
|
|
240
|
-
name:
|
|
241
|
-
type:
|
|
242
|
-
branchType:
|
|
278
|
+
name: "main",
|
|
279
|
+
type: "local",
|
|
280
|
+
branchType: "main",
|
|
243
281
|
isCurrent: true,
|
|
244
282
|
hasUnpushedCommits: false,
|
|
245
283
|
latestCommitTimestamp: 1_699_998_000,
|
|
@@ -247,21 +285,25 @@ describe('BranchListScreen', () => {
|
|
|
247
285
|
];
|
|
248
286
|
|
|
249
287
|
const branchesWithUnpushed: BranchItem[] = branchInfos.map((branch) =>
|
|
250
|
-
formatBranchItem(branch)
|
|
288
|
+
formatBranchItem(branch),
|
|
251
289
|
);
|
|
252
290
|
|
|
253
291
|
try {
|
|
254
292
|
let renderResult: ReturnType<typeof inkRender>;
|
|
255
293
|
await act(async () => {
|
|
256
294
|
renderResult = inkRender(
|
|
257
|
-
<BranchListScreen
|
|
258
|
-
|
|
295
|
+
<BranchListScreen
|
|
296
|
+
branches={branchesWithUnpushed}
|
|
297
|
+
stats={mockStats}
|
|
298
|
+
onSelect={onSelect}
|
|
299
|
+
/>,
|
|
300
|
+
{ stripAnsi: false },
|
|
259
301
|
);
|
|
260
302
|
});
|
|
261
303
|
|
|
262
|
-
const frame = renderResult!.lastFrame() ??
|
|
304
|
+
const frame = renderResult!.lastFrame() ?? "";
|
|
263
305
|
const timestampLines = frame
|
|
264
|
-
.split(
|
|
306
|
+
.split("\n")
|
|
265
307
|
.map((line) => stripControlSequences(stripAnsi(line)))
|
|
266
308
|
.filter((line) => /\d{4}-\d{2}-\d{2} \d{2}:\d{2}/.test(line));
|
|
267
309
|
|
|
@@ -274,7 +316,7 @@ describe('BranchListScreen', () => {
|
|
|
274
316
|
|
|
275
317
|
let width = 0;
|
|
276
318
|
for (const char of Array.from(beforeTimestamp)) {
|
|
277
|
-
if (char ===
|
|
319
|
+
if (char === "\u2B06" || char === "\u2601") {
|
|
278
320
|
width += 1;
|
|
279
321
|
continue;
|
|
280
322
|
}
|
|
@@ -291,29 +333,40 @@ describe('BranchListScreen', () => {
|
|
|
291
333
|
}
|
|
292
334
|
});
|
|
293
335
|
|
|
294
|
-
describe(
|
|
295
|
-
it(
|
|
336
|
+
describe("Filter Mode", () => {
|
|
337
|
+
it("should always display filter input field", () => {
|
|
296
338
|
// Note: Filter input is now always visible (no need to press 'f' key)
|
|
297
339
|
const onSelect = vi.fn();
|
|
298
340
|
const { container } = render(
|
|
299
|
-
<BranchListScreen
|
|
341
|
+
<BranchListScreen
|
|
342
|
+
branches={mockBranches}
|
|
343
|
+
stats={mockStats}
|
|
344
|
+
onSelect={onSelect}
|
|
345
|
+
/>,
|
|
300
346
|
);
|
|
301
347
|
|
|
302
348
|
// Filter input field should be displayed by default
|
|
303
|
-
expect(container.textContent).toContain(
|
|
349
|
+
expect(container.textContent).toContain("Filter:");
|
|
304
350
|
});
|
|
305
351
|
|
|
306
|
-
it(
|
|
352
|
+
it("should enter filter mode when f key is pressed", () => {
|
|
307
353
|
const onSelect = vi.fn();
|
|
308
354
|
const { container } = render(
|
|
309
|
-
<BranchListScreen
|
|
355
|
+
<BranchListScreen
|
|
356
|
+
branches={mockBranches}
|
|
357
|
+
stats={mockStats}
|
|
358
|
+
onSelect={onSelect}
|
|
359
|
+
/>,
|
|
310
360
|
);
|
|
311
361
|
|
|
312
362
|
// Initially should show prompt to press f
|
|
313
|
-
expect(container.textContent).toContain(
|
|
363
|
+
expect(container.textContent).toContain("(press f to filter)");
|
|
314
364
|
|
|
315
365
|
// Press 'f' key
|
|
316
|
-
const fKeyEvent = new (globalThis.window as any).KeyboardEvent(
|
|
366
|
+
const fKeyEvent = new (globalThis.window as any).KeyboardEvent(
|
|
367
|
+
"keydown",
|
|
368
|
+
{ key: "f" },
|
|
369
|
+
);
|
|
317
370
|
document.dispatchEvent(fKeyEvent);
|
|
318
371
|
|
|
319
372
|
// Filter input should be active (placeholder visible)
|
|
@@ -321,45 +374,65 @@ describe('BranchListScreen', () => {
|
|
|
321
374
|
expect(container).toBeDefined();
|
|
322
375
|
});
|
|
323
376
|
|
|
324
|
-
it(
|
|
377
|
+
it("should exit filter mode and return to branch selection when Esc is pressed in filter mode", () => {
|
|
325
378
|
const onSelect = vi.fn();
|
|
326
379
|
const { container } = render(
|
|
327
|
-
<BranchListScreen
|
|
380
|
+
<BranchListScreen
|
|
381
|
+
branches={mockBranches}
|
|
382
|
+
stats={mockStats}
|
|
383
|
+
onSelect={onSelect}
|
|
384
|
+
/>,
|
|
328
385
|
);
|
|
329
386
|
|
|
330
387
|
// Enter filter mode first
|
|
331
|
-
const fKeyEvent = new (globalThis.window as any).KeyboardEvent(
|
|
388
|
+
const fKeyEvent = new (globalThis.window as any).KeyboardEvent(
|
|
389
|
+
"keydown",
|
|
390
|
+
{ key: "f" },
|
|
391
|
+
);
|
|
332
392
|
document.dispatchEvent(fKeyEvent);
|
|
333
393
|
|
|
334
394
|
// Press Escape
|
|
335
|
-
const escKeyEvent = new (globalThis.window as any).KeyboardEvent(
|
|
395
|
+
const escKeyEvent = new (globalThis.window as any).KeyboardEvent(
|
|
396
|
+
"keydown",
|
|
397
|
+
{ key: "Escape" },
|
|
398
|
+
);
|
|
336
399
|
document.dispatchEvent(escKeyEvent);
|
|
337
400
|
|
|
338
401
|
// Should return to branch selection mode
|
|
339
402
|
// Select should be active, Input should be inactive
|
|
340
|
-
expect(container.textContent).toContain(
|
|
403
|
+
expect(container.textContent).toContain("(press f to filter)");
|
|
341
404
|
});
|
|
342
405
|
|
|
343
|
-
it(
|
|
344
|
-
process.env.FORCE_COLOR =
|
|
406
|
+
it("should show branch list cursor highlight in filter mode", () => {
|
|
407
|
+
process.env.FORCE_COLOR = "1";
|
|
345
408
|
const onSelect = vi.fn();
|
|
346
409
|
let renderResult: ReturnType<typeof inkRender>;
|
|
347
410
|
act(() => {
|
|
348
411
|
renderResult = inkRender(
|
|
349
|
-
<BranchListScreen
|
|
350
|
-
|
|
412
|
+
<BranchListScreen
|
|
413
|
+
branches={mockBranches}
|
|
414
|
+
stats={mockStats}
|
|
415
|
+
onSelect={onSelect}
|
|
416
|
+
testFilterMode={true}
|
|
417
|
+
/>,
|
|
418
|
+
{ stripAnsi: false },
|
|
351
419
|
);
|
|
352
420
|
});
|
|
353
421
|
|
|
354
|
-
const frame = renderResult!.lastFrame() ??
|
|
422
|
+
const frame = renderResult!.lastFrame() ?? "";
|
|
355
423
|
// Should contain cyan background (cursor highlight) even in filter mode
|
|
356
|
-
expect(frame).toContain(
|
|
424
|
+
expect(frame).toContain("\u001b[46m");
|
|
357
425
|
});
|
|
358
426
|
|
|
359
|
-
it(
|
|
427
|
+
it("should allow cursor movement with arrow keys in filter mode", () => {
|
|
360
428
|
const onSelect = vi.fn();
|
|
361
429
|
const { container } = render(
|
|
362
|
-
<BranchListScreen
|
|
430
|
+
<BranchListScreen
|
|
431
|
+
branches={mockBranches}
|
|
432
|
+
stats={mockStats}
|
|
433
|
+
onSelect={onSelect}
|
|
434
|
+
testFilterMode={true}
|
|
435
|
+
/>,
|
|
363
436
|
);
|
|
364
437
|
|
|
365
438
|
// Arrow keys should work in filter mode (Select component should not be disabled)
|
|
@@ -367,10 +440,15 @@ describe('BranchListScreen', () => {
|
|
|
367
440
|
expect(container).toBeDefined();
|
|
368
441
|
});
|
|
369
442
|
|
|
370
|
-
it(
|
|
443
|
+
it("should allow branch selection with Enter key in filter mode", () => {
|
|
371
444
|
const onSelect = vi.fn();
|
|
372
445
|
const { container } = render(
|
|
373
|
-
<BranchListScreen
|
|
446
|
+
<BranchListScreen
|
|
447
|
+
branches={mockBranches}
|
|
448
|
+
stats={mockStats}
|
|
449
|
+
onSelect={onSelect}
|
|
450
|
+
testFilterMode={true}
|
|
451
|
+
/>,
|
|
374
452
|
);
|
|
375
453
|
|
|
376
454
|
// Simulate Enter key (this will trigger onSelect if Select is enabled)
|
|
@@ -379,10 +457,14 @@ describe('BranchListScreen', () => {
|
|
|
379
457
|
expect(container).toBeDefined();
|
|
380
458
|
});
|
|
381
459
|
|
|
382
|
-
it(
|
|
460
|
+
it("should disable filter input cursor when in branch selection mode", () => {
|
|
383
461
|
const onSelect = vi.fn();
|
|
384
462
|
const { container } = render(
|
|
385
|
-
<BranchListScreen
|
|
463
|
+
<BranchListScreen
|
|
464
|
+
branches={mockBranches}
|
|
465
|
+
stats={mockStats}
|
|
466
|
+
onSelect={onSelect}
|
|
467
|
+
/>,
|
|
386
468
|
);
|
|
387
469
|
|
|
388
470
|
// By default, should be in branch selection mode
|
|
@@ -390,19 +472,19 @@ describe('BranchListScreen', () => {
|
|
|
390
472
|
expect(container).toBeDefined();
|
|
391
473
|
});
|
|
392
474
|
|
|
393
|
-
it(
|
|
475
|
+
it("should filter branches in real-time as user types", () => {
|
|
394
476
|
const onSelect = vi.fn();
|
|
395
477
|
const branches: BranchItem[] = [
|
|
396
478
|
...mockBranches,
|
|
397
479
|
{
|
|
398
|
-
name:
|
|
399
|
-
type:
|
|
400
|
-
branchType:
|
|
480
|
+
name: "bugfix/issue-123",
|
|
481
|
+
type: "local",
|
|
482
|
+
branchType: "bugfix",
|
|
401
483
|
isCurrent: false,
|
|
402
|
-
icons: [
|
|
484
|
+
icons: ["🐛"],
|
|
403
485
|
hasChanges: false,
|
|
404
|
-
label:
|
|
405
|
-
value:
|
|
486
|
+
label: "🐛 bugfix/issue-123",
|
|
487
|
+
value: "bugfix/issue-123",
|
|
406
488
|
latestCommitTimestamp: 1_698_000_000,
|
|
407
489
|
},
|
|
408
490
|
];
|
|
@@ -414,62 +496,82 @@ describe('BranchListScreen', () => {
|
|
|
414
496
|
onSelect={onSelect}
|
|
415
497
|
testFilterMode={true}
|
|
416
498
|
testFilterQuery="feature"
|
|
417
|
-
|
|
499
|
+
/>,
|
|
418
500
|
);
|
|
419
501
|
|
|
420
502
|
// Only feature/test should be visible
|
|
421
|
-
expect(container.textContent).toContain(
|
|
422
|
-
expect(container.textContent).not.toContain(
|
|
503
|
+
expect(container.textContent).toContain("feature/test");
|
|
504
|
+
expect(container.textContent).not.toContain("bugfix/issue-123");
|
|
423
505
|
});
|
|
424
506
|
|
|
425
|
-
it(
|
|
507
|
+
it("should clear filter query when Esc key is pressed (with query)", () => {
|
|
426
508
|
// Note: Filter input remains visible, only the query is cleared
|
|
427
509
|
const onSelect = vi.fn();
|
|
428
510
|
const { container } = render(
|
|
429
|
-
<BranchListScreen
|
|
511
|
+
<BranchListScreen
|
|
512
|
+
branches={mockBranches}
|
|
513
|
+
stats={mockStats}
|
|
514
|
+
onSelect={onSelect}
|
|
515
|
+
/>,
|
|
430
516
|
);
|
|
431
517
|
|
|
432
518
|
// Enter filter mode
|
|
433
|
-
const fKeyEvent = new (globalThis.window as any).KeyboardEvent(
|
|
519
|
+
const fKeyEvent = new (globalThis.window as any).KeyboardEvent(
|
|
520
|
+
"keydown",
|
|
521
|
+
{ key: "f" },
|
|
522
|
+
);
|
|
434
523
|
document.dispatchEvent(fKeyEvent);
|
|
435
524
|
|
|
436
525
|
// Type something in filter
|
|
437
|
-
const input = container.querySelector(
|
|
526
|
+
const input = container.querySelector("input");
|
|
438
527
|
if (input) {
|
|
439
|
-
input.value =
|
|
440
|
-
input.dispatchEvent(new Event(
|
|
528
|
+
input.value = "feature";
|
|
529
|
+
input.dispatchEvent(new Event("input", { bubbles: true }));
|
|
441
530
|
}
|
|
442
531
|
|
|
443
532
|
// Press Escape (should clear query first)
|
|
444
|
-
const escKeyEvent = new (globalThis.window as any).KeyboardEvent(
|
|
533
|
+
const escKeyEvent = new (globalThis.window as any).KeyboardEvent(
|
|
534
|
+
"keydown",
|
|
535
|
+
{ key: "Escape" },
|
|
536
|
+
);
|
|
445
537
|
document.dispatchEvent(escKeyEvent);
|
|
446
538
|
|
|
447
539
|
// Filter input should still be visible, but query cleared
|
|
448
540
|
// All branches should be visible again
|
|
449
|
-
expect(container.textContent).toContain(
|
|
450
|
-
expect(container.textContent).toContain(
|
|
451
|
-
expect(container.textContent).toContain(
|
|
541
|
+
expect(container.textContent).toContain("Filter:");
|
|
542
|
+
expect(container.textContent).toContain("main");
|
|
543
|
+
expect(container.textContent).toContain("feature/test");
|
|
452
544
|
});
|
|
453
545
|
|
|
454
|
-
it(
|
|
546
|
+
it("should exit filter mode when Esc is pressed with empty query", () => {
|
|
455
547
|
const onSelect = vi.fn();
|
|
456
548
|
const { container } = render(
|
|
457
|
-
<BranchListScreen
|
|
549
|
+
<BranchListScreen
|
|
550
|
+
branches={mockBranches}
|
|
551
|
+
stats={mockStats}
|
|
552
|
+
onSelect={onSelect}
|
|
553
|
+
/>,
|
|
458
554
|
);
|
|
459
555
|
|
|
460
556
|
// Enter filter mode
|
|
461
|
-
const fKeyEvent = new (globalThis.window as any).KeyboardEvent(
|
|
557
|
+
const fKeyEvent = new (globalThis.window as any).KeyboardEvent(
|
|
558
|
+
"keydown",
|
|
559
|
+
{ key: "f" },
|
|
560
|
+
);
|
|
462
561
|
document.dispatchEvent(fKeyEvent);
|
|
463
562
|
|
|
464
563
|
// Press Escape with empty query (should exit filter mode)
|
|
465
|
-
const escKeyEvent = new (globalThis.window as any).KeyboardEvent(
|
|
564
|
+
const escKeyEvent = new (globalThis.window as any).KeyboardEvent(
|
|
565
|
+
"keydown",
|
|
566
|
+
{ key: "Escape" },
|
|
567
|
+
);
|
|
466
568
|
document.dispatchEvent(escKeyEvent);
|
|
467
569
|
|
|
468
570
|
// Should return to branch selection mode
|
|
469
|
-
expect(container.textContent).toContain(
|
|
571
|
+
expect(container.textContent).toContain("(press f to filter)");
|
|
470
572
|
});
|
|
471
573
|
|
|
472
|
-
it(
|
|
574
|
+
it("should perform case-insensitive search", () => {
|
|
473
575
|
const onSelect = vi.fn();
|
|
474
576
|
const { container } = render(
|
|
475
577
|
<BranchListScreen
|
|
@@ -478,14 +580,14 @@ describe('BranchListScreen', () => {
|
|
|
478
580
|
onSelect={onSelect}
|
|
479
581
|
testFilterMode={true}
|
|
480
582
|
testFilterQuery="FEATURE"
|
|
481
|
-
|
|
583
|
+
/>,
|
|
482
584
|
);
|
|
483
585
|
|
|
484
586
|
// "feature/test" should still be visible
|
|
485
|
-
expect(container.textContent).toContain(
|
|
587
|
+
expect(container.textContent).toContain("feature/test");
|
|
486
588
|
});
|
|
487
589
|
|
|
488
|
-
it(
|
|
590
|
+
it("should disable other key bindings (m, c, r) while typing in filter", () => {
|
|
489
591
|
// Note: Input component uses blockKeys prop to prevent c/r/m/f from
|
|
490
592
|
// triggering shortcuts while typing in the filter field
|
|
491
593
|
// This test verifies the intended behavior (though KeyboardEvent
|
|
@@ -503,17 +605,23 @@ describe('BranchListScreen', () => {
|
|
|
503
605
|
onNavigate={onNavigate}
|
|
504
606
|
onCleanupCommand={onCleanupCommand}
|
|
505
607
|
onRefresh={onRefresh}
|
|
506
|
-
|
|
608
|
+
/>,
|
|
507
609
|
);
|
|
508
610
|
|
|
509
611
|
// Enter filter mode
|
|
510
|
-
const fKeyEvent = new (globalThis.window as any).KeyboardEvent(
|
|
612
|
+
const fKeyEvent = new (globalThis.window as any).KeyboardEvent(
|
|
613
|
+
"keydown",
|
|
614
|
+
{ key: "f" },
|
|
615
|
+
);
|
|
511
616
|
document.dispatchEvent(fKeyEvent);
|
|
512
617
|
|
|
513
618
|
// When user types in filter, Input component blocks c/r/m/f keys
|
|
514
619
|
// Press m, c, r keys (should be blocked by Input's blockKeys)
|
|
515
|
-
[
|
|
516
|
-
const keyEvent = new (globalThis.window as any).KeyboardEvent(
|
|
620
|
+
["m", "c", "r"].forEach((key) => {
|
|
621
|
+
const keyEvent = new (globalThis.window as any).KeyboardEvent(
|
|
622
|
+
"keydown",
|
|
623
|
+
{ key },
|
|
624
|
+
);
|
|
517
625
|
document.dispatchEvent(keyEvent);
|
|
518
626
|
});
|
|
519
627
|
|
|
@@ -524,19 +632,19 @@ describe('BranchListScreen', () => {
|
|
|
524
632
|
expect(onRefresh).not.toHaveBeenCalled();
|
|
525
633
|
});
|
|
526
634
|
|
|
527
|
-
it(
|
|
635
|
+
it("should display match count when filtering", () => {
|
|
528
636
|
const onSelect = vi.fn();
|
|
529
637
|
const branches: BranchItem[] = [
|
|
530
638
|
...mockBranches,
|
|
531
639
|
{
|
|
532
|
-
name:
|
|
533
|
-
type:
|
|
534
|
-
branchType:
|
|
640
|
+
name: "feature/another",
|
|
641
|
+
type: "local",
|
|
642
|
+
branchType: "feature",
|
|
535
643
|
isCurrent: false,
|
|
536
|
-
icons: [
|
|
644
|
+
icons: ["✨"],
|
|
537
645
|
hasChanges: false,
|
|
538
|
-
label:
|
|
539
|
-
value:
|
|
646
|
+
label: "✨ feature/another",
|
|
647
|
+
value: "feature/another",
|
|
540
648
|
latestCommitTimestamp: 1_698_000_000,
|
|
541
649
|
},
|
|
542
650
|
];
|
|
@@ -548,14 +656,14 @@ describe('BranchListScreen', () => {
|
|
|
548
656
|
onSelect={onSelect}
|
|
549
657
|
testFilterMode={true}
|
|
550
658
|
testFilterQuery="feature"
|
|
551
|
-
|
|
659
|
+
/>,
|
|
552
660
|
);
|
|
553
661
|
|
|
554
662
|
// Should show "Showing 2 of 3 branches"
|
|
555
663
|
expect(container.textContent).toMatch(/Showing\s+2\s+of\s+3/i);
|
|
556
664
|
});
|
|
557
665
|
|
|
558
|
-
it(
|
|
666
|
+
it("should show empty list when no branches match", () => {
|
|
559
667
|
const onSelect = vi.fn();
|
|
560
668
|
const { container } = render(
|
|
561
669
|
<BranchListScreen
|
|
@@ -564,28 +672,28 @@ describe('BranchListScreen', () => {
|
|
|
564
672
|
onSelect={onSelect}
|
|
565
673
|
testFilterMode={true}
|
|
566
674
|
testFilterQuery="nonexistent"
|
|
567
|
-
|
|
675
|
+
/>,
|
|
568
676
|
);
|
|
569
677
|
|
|
570
678
|
// Should show "Showing 0 of 2 branches"
|
|
571
679
|
expect(container.textContent).toMatch(/Showing\s+0\s+of\s+2/i);
|
|
572
680
|
});
|
|
573
681
|
|
|
574
|
-
it(
|
|
682
|
+
it("should search in PR titles when available", () => {
|
|
575
683
|
const onSelect = vi.fn();
|
|
576
684
|
const branchesWithPR: BranchItem[] = [
|
|
577
685
|
...mockBranches,
|
|
578
686
|
{
|
|
579
|
-
name:
|
|
580
|
-
type:
|
|
581
|
-
branchType:
|
|
687
|
+
name: "feature/add-filter",
|
|
688
|
+
type: "local",
|
|
689
|
+
branchType: "feature",
|
|
582
690
|
isCurrent: false,
|
|
583
|
-
icons: [
|
|
691
|
+
icons: ["✨", "🔀"],
|
|
584
692
|
hasChanges: false,
|
|
585
|
-
label:
|
|
586
|
-
value:
|
|
693
|
+
label: "✨ 🔀 feature/add-filter",
|
|
694
|
+
value: "feature/add-filter",
|
|
587
695
|
latestCommitTimestamp: 1_698_000_000,
|
|
588
|
-
openPR: { number: 123, title:
|
|
696
|
+
openPR: { number: 123, title: "Add search filter to branch list" },
|
|
589
697
|
},
|
|
590
698
|
];
|
|
591
699
|
|
|
@@ -596,11 +704,11 @@ describe('BranchListScreen', () => {
|
|
|
596
704
|
onSelect={onSelect}
|
|
597
705
|
testFilterMode={true}
|
|
598
706
|
testFilterQuery="search"
|
|
599
|
-
|
|
707
|
+
/>,
|
|
600
708
|
);
|
|
601
709
|
|
|
602
710
|
// Branch with matching PR title should be visible
|
|
603
|
-
expect(container.textContent).toContain(
|
|
711
|
+
expect(container.textContent).toContain("feature/add-filter");
|
|
604
712
|
});
|
|
605
713
|
});
|
|
606
714
|
});
|