Skip to content

Commit 1edfea8

Browse files
committed
fix: use vi.restoreAllMocks and toMatchSnapshot in dialog tests
- OverageMenuDialog.test.tsx: add afterEach with vi.restoreAllMocks(), remove RadioButtonSelect mock, use renderWithProviders + toMatchSnapshot for rendering tests and keyboard input for handler tests - EmptyWalletDialog.test.tsx: same treatment as OverageMenuDialog - useQuotaAndFallback.test.ts: vi.clearAllMocks() → vi.restoreAllMocks() in afterEach to prevent test pollution
1 parent 929a591 commit 1edfea8

5 files changed

Lines changed: 196 additions & 178 deletions

File tree

packages/cli/src/ui/components/EmptyWalletDialog.test.tsx

Lines changed: 45 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,17 @@
44
* SPDX-License-Identifier: Apache-2.0
55
*/
66

7-
import { render } from '../../test-utils/render.js';
7+
import { renderWithProviders } from '../../test-utils/render.js';
8+
import { waitFor } from '../../test-utils/async.js';
89
import { act } from 'react';
9-
import {
10-
describe,
11-
it,
12-
expect,
13-
vi,
14-
beforeEach,
15-
afterEach,
16-
type Mock,
17-
} from 'vitest';
10+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
1811
import { EmptyWalletDialog } from './EmptyWalletDialog.js';
19-
import { RadioButtonSelect } from './shared/RadioButtonSelect.js';
2012

21-
// Mock the child component to make it easier to test the parent
22-
vi.mock('./shared/RadioButtonSelect.js', () => ({
23-
RadioButtonSelect: vi.fn(),
24-
}));
13+
const writeKey = (stdin: { write: (data: string) => void }, key: string) => {
14+
act(() => {
15+
stdin.write(key);
16+
});
17+
};
2518

2619
describe('EmptyWalletDialog', () => {
2720
const mockOnChoice = vi.fn();
@@ -36,8 +29,8 @@ describe('EmptyWalletDialog', () => {
3629
});
3730

3831
describe('rendering', () => {
39-
it('should render with correct menu options when fallback is available', async () => {
40-
const { unmount, waitUntilReady } = render(
32+
it('should match snapshot with fallback available', async () => {
33+
const { lastFrame, unmount, waitUntilReady } = renderWithProviders(
4134
<EmptyWalletDialog
4235
failedModel="gemini-2.5-pro"
4336
fallbackModel="gemini-3-flash-preview"
@@ -47,62 +40,25 @@ describe('EmptyWalletDialog', () => {
4740
);
4841
await waitUntilReady();
4942

50-
expect(RadioButtonSelect).toHaveBeenCalledWith(
51-
expect.objectContaining({
52-
items: [
53-
{
54-
label: 'Get AI Credits - Open browser to purchase credits',
55-
value: 'get_credits',
56-
key: 'get_credits',
57-
},
58-
{
59-
label: 'Switch to gemini-3-flash-preview',
60-
value: 'use_fallback',
61-
key: 'use_fallback',
62-
},
63-
{
64-
label: 'Stop - Abort request',
65-
value: 'stop',
66-
key: 'stop',
67-
},
68-
],
69-
}),
70-
undefined,
71-
);
43+
expect(lastFrame()).toMatchSnapshot();
7244
unmount();
7345
});
7446

75-
it('should omit fallback option when fallbackModel is not provided', async () => {
76-
const { unmount, waitUntilReady } = render(
47+
it('should match snapshot without fallback', async () => {
48+
const { lastFrame, unmount, waitUntilReady } = renderWithProviders(
7749
<EmptyWalletDialog
7850
failedModel="gemini-2.5-pro"
7951
onChoice={mockOnChoice}
8052
/>,
8153
);
8254
await waitUntilReady();
8355

84-
expect(RadioButtonSelect).toHaveBeenCalledWith(
85-
expect.objectContaining({
86-
items: [
87-
{
88-
label: 'Get AI Credits - Open browser to purchase credits',
89-
value: 'get_credits',
90-
key: 'get_credits',
91-
},
92-
{
93-
label: 'Stop - Abort request',
94-
value: 'stop',
95-
key: 'stop',
96-
},
97-
],
98-
}),
99-
undefined,
100-
);
56+
expect(lastFrame()).toMatchSnapshot();
10157
unmount();
10258
});
10359

10460
it('should display the model name and usage limit message', async () => {
105-
const { lastFrame, unmount, waitUntilReady } = render(
61+
const { lastFrame, unmount, waitUntilReady } = renderWithProviders(
10662
<EmptyWalletDialog
10763
failedModel="gemini-2.5-pro"
10864
onChoice={mockOnChoice}
@@ -117,7 +73,7 @@ describe('EmptyWalletDialog', () => {
11773
});
11874

11975
it('should display purchase prompt and credits update notice', async () => {
120-
const { lastFrame, unmount, waitUntilReady } = render(
76+
const { lastFrame, unmount, waitUntilReady } = renderWithProviders(
12177
<EmptyWalletDialog
12278
failedModel="gemini-2.5-pro"
12379
onChoice={mockOnChoice}
@@ -134,7 +90,7 @@ describe('EmptyWalletDialog', () => {
13490
});
13591

13692
it('should display reset time when provided', async () => {
137-
const { lastFrame, unmount, waitUntilReady } = render(
93+
const { lastFrame, unmount, waitUntilReady } = renderWithProviders(
13894
<EmptyWalletDialog
13995
failedModel="gemini-2.5-pro"
14096
resetTime="3:45 PM"
@@ -150,7 +106,7 @@ describe('EmptyWalletDialog', () => {
150106
});
151107

152108
it('should not display reset time when not provided', async () => {
153-
const { lastFrame, unmount, waitUntilReady } = render(
109+
const { lastFrame, unmount, waitUntilReady } = renderWithProviders(
154110
<EmptyWalletDialog
155111
failedModel="gemini-2.5-pro"
156112
onChoice={mockOnChoice}
@@ -164,7 +120,7 @@ describe('EmptyWalletDialog', () => {
164120
});
165121

166122
it('should display slash command hints', async () => {
167-
const { lastFrame, unmount, waitUntilReady } = render(
123+
const { lastFrame, unmount, waitUntilReady } = renderWithProviders(
168124
<EmptyWalletDialog
169125
failedModel="gemini-2.5-pro"
170126
onChoice={mockOnChoice}
@@ -182,7 +138,8 @@ describe('EmptyWalletDialog', () => {
182138

183139
describe('onChoice handling', () => {
184140
it('should call onGetCredits and onChoice when get_credits is selected', async () => {
185-
const { unmount, waitUntilReady } = render(
141+
// get_credits is the first item, so just press Enter
142+
const { unmount, stdin, waitUntilReady } = renderWithProviders(
186143
<EmptyWalletDialog
187144
failedModel="gemini-2.5-pro"
188145
onChoice={mockOnChoice}
@@ -191,38 +148,36 @@ describe('EmptyWalletDialog', () => {
191148
);
192149
await waitUntilReady();
193150

194-
const onSelect = (RadioButtonSelect as Mock).mock.calls[0][0].onSelect;
151+
writeKey(stdin, '\r');
195152

196-
await act(async () => {
197-
onSelect('get_credits');
153+
await waitFor(() => {
154+
expect(mockOnGetCredits).toHaveBeenCalled();
155+
expect(mockOnChoice).toHaveBeenCalledWith('get_credits');
198156
});
199-
200-
expect(mockOnGetCredits).toHaveBeenCalled();
201-
expect(mockOnChoice).toHaveBeenCalledWith('get_credits');
202157
unmount();
203158
});
204159

205160
it('should call onChoice without onGetCredits when onGetCredits is not provided', async () => {
206-
const { unmount, waitUntilReady } = render(
161+
const { unmount, stdin, waitUntilReady } = renderWithProviders(
207162
<EmptyWalletDialog
208163
failedModel="gemini-2.5-pro"
209164
onChoice={mockOnChoice}
210165
/>,
211166
);
212167
await waitUntilReady();
213168

214-
const onSelect = (RadioButtonSelect as Mock).mock.calls[0][0].onSelect;
169+
writeKey(stdin, '\r');
215170

216-
await act(async () => {
217-
onSelect('get_credits');
171+
await waitFor(() => {
172+
expect(mockOnChoice).toHaveBeenCalledWith('get_credits');
218173
});
219-
220-
expect(mockOnChoice).toHaveBeenCalledWith('get_credits');
221174
unmount();
222175
});
223176

224177
it('should call onChoice with use_fallback when selected', async () => {
225-
const { unmount, waitUntilReady } = render(
178+
// With fallback: items are [get_credits, use_fallback, stop]
179+
// use_fallback is the second item: Down + Enter
180+
const { unmount, stdin, waitUntilReady } = renderWithProviders(
226181
<EmptyWalletDialog
227182
failedModel="gemini-2.5-pro"
228183
fallbackModel="gemini-3-flash-preview"
@@ -231,30 +186,32 @@ describe('EmptyWalletDialog', () => {
231186
);
232187
await waitUntilReady();
233188

234-
const onSelect = (RadioButtonSelect as Mock).mock.calls[0][0].onSelect;
235-
await act(async () => {
236-
onSelect('use_fallback');
237-
});
189+
writeKey(stdin, '\x1b[B'); // Down arrow
190+
writeKey(stdin, '\r');
238191

239-
expect(mockOnChoice).toHaveBeenCalledWith('use_fallback');
192+
await waitFor(() => {
193+
expect(mockOnChoice).toHaveBeenCalledWith('use_fallback');
194+
});
240195
unmount();
241196
});
242197

243198
it('should call onChoice with stop when selected', async () => {
244-
const { unmount, waitUntilReady } = render(
199+
// Without fallback: items are [get_credits, stop]
200+
// stop is the second item: Down + Enter
201+
const { unmount, stdin, waitUntilReady } = renderWithProviders(
245202
<EmptyWalletDialog
246203
failedModel="gemini-2.5-pro"
247204
onChoice={mockOnChoice}
248205
/>,
249206
);
250207
await waitUntilReady();
251208

252-
const onSelect = (RadioButtonSelect as Mock).mock.calls[0][0].onSelect;
253-
await act(async () => {
254-
onSelect('stop');
255-
});
209+
writeKey(stdin, '\x1b[B'); // Down arrow
210+
writeKey(stdin, '\r');
256211

257-
expect(mockOnChoice).toHaveBeenCalledWith('stop');
212+
await waitFor(() => {
213+
expect(mockOnChoice).toHaveBeenCalledWith('stop');
214+
});
258215
unmount();
259216
});
260217
});

0 commit comments

Comments
 (0)