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' ;
89import { 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' ;
1811import { 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
2619describe ( '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