@@ -19,7 +19,9 @@ import {
1919 PREVIEW_GEMINI_3_1_MODEL ,
2020 PREVIEW_GEMINI_3_1_CUSTOM_TOOLS_MODEL ,
2121 PREVIEW_GEMINI_FLASH_MODEL ,
22+ PREVIEW_GEMINI_3_1_FLASH_LITE_MODEL ,
2223 AuthType ,
24+ UserTierId ,
2325} from '@google/gemini-cli-core' ;
2426import type { Config , ModelSlashCommandEvent } from '@google/gemini-cli-core' ;
2527
@@ -28,8 +30,9 @@ const mockGetDisplayString = vi.fn();
2830const mockLogModelSlashCommand = vi . fn ( ) ;
2931const mockModelSlashCommandEvent = vi . fn ( ) ;
3032
31- vi . mock ( '@google/gemini-cli-core' , async ( ) => {
32- const actual = await vi . importActual ( '@google/gemini-cli-core' ) ;
33+ vi . mock ( '@google/gemini-cli-core' , async ( importOriginal ) => {
34+ const actual =
35+ await importOriginal < typeof import ( '@google/gemini-cli-core' ) > ( ) ;
3336 return {
3437 ...actual ,
3538 getDisplayString : ( val : string ) => mockGetDisplayString ( val ) ,
@@ -40,6 +43,7 @@ vi.mock('@google/gemini-cli-core', async () => {
4043 mockModelSlashCommandEvent ( model ) ;
4144 }
4245 } ,
46+ PREVIEW_GEMINI_3_1_FLASH_LITE_MODEL : 'gemini-3.1-flash-lite-preview' ,
4347 } ;
4448} ) ;
4549
@@ -49,13 +53,19 @@ describe('<ModelDialog />', () => {
4953 const mockOnClose = vi . fn ( ) ;
5054 const mockGetHasAccessToPreviewModel = vi . fn ( ) ;
5155 const mockGetGemini31LaunchedSync = vi . fn ( ) ;
56+ const mockGetProModelNoAccess = vi . fn ( ) ;
57+ const mockGetProModelNoAccessSync = vi . fn ( ) ;
58+ const mockGetUserTier = vi . fn ( ) ;
5259
5360 interface MockConfig extends Partial < Config > {
5461 setModel : ( model : string , isTemporary ?: boolean ) => void ;
5562 getModel : ( ) => string ;
5663 getHasAccessToPreviewModel : ( ) => boolean ;
5764 getIdeMode : ( ) => boolean ;
5865 getGemini31LaunchedSync : ( ) => boolean ;
66+ getProModelNoAccess : ( ) => Promise < boolean > ;
67+ getProModelNoAccessSync : ( ) => boolean ;
68+ getUserTier : ( ) => UserTierId | undefined ;
5969 }
6070
6171 const mockConfig : MockConfig = {
@@ -64,13 +74,19 @@ describe('<ModelDialog />', () => {
6474 getHasAccessToPreviewModel : mockGetHasAccessToPreviewModel ,
6575 getIdeMode : ( ) => false ,
6676 getGemini31LaunchedSync : mockGetGemini31LaunchedSync ,
77+ getProModelNoAccess : mockGetProModelNoAccess ,
78+ getProModelNoAccessSync : mockGetProModelNoAccessSync ,
79+ getUserTier : mockGetUserTier ,
6780 } ;
6881
6982 beforeEach ( ( ) => {
7083 vi . resetAllMocks ( ) ;
7184 mockGetModel . mockReturnValue ( DEFAULT_GEMINI_MODEL_AUTO ) ;
7285 mockGetHasAccessToPreviewModel . mockReturnValue ( false ) ;
7386 mockGetGemini31LaunchedSync . mockReturnValue ( false ) ;
87+ mockGetProModelNoAccess . mockResolvedValue ( false ) ;
88+ mockGetProModelNoAccessSync . mockReturnValue ( false ) ;
89+ mockGetUserTier . mockReturnValue ( UserTierId . STANDARD ) ;
7490
7591 // Default implementation for getDisplayString
7692 mockGetDisplayString . mockImplementation ( ( val : string ) => {
@@ -109,6 +125,55 @@ describe('<ModelDialog />', () => {
109125 unmount ( ) ;
110126 } ) ;
111127
128+ it ( 'renders the "manual" view initially for users with no pro access and filters Pro models with correct order' , async ( ) => {
129+ mockGetProModelNoAccessSync . mockReturnValue ( true ) ;
130+ mockGetProModelNoAccess . mockResolvedValue ( true ) ;
131+ mockGetHasAccessToPreviewModel . mockReturnValue ( true ) ;
132+ mockGetUserTier . mockReturnValue ( UserTierId . FREE ) ;
133+ mockGetDisplayString . mockImplementation ( ( val : string ) => val ) ;
134+
135+ const { lastFrame, unmount } = await renderComponent ( ) ;
136+
137+ const output = lastFrame ( ) ;
138+ expect ( output ) . toContain ( 'Select Model' ) ;
139+ expect ( output ) . not . toContain ( DEFAULT_GEMINI_MODEL ) ;
140+ expect ( output ) . not . toContain ( PREVIEW_GEMINI_MODEL ) ;
141+
142+ // Verify order: Flash Preview -> Flash Lite Preview -> Flash -> Flash Lite
143+ const flashPreviewIdx = output . indexOf ( PREVIEW_GEMINI_FLASH_MODEL ) ;
144+ const flashLitePreviewIdx = output . indexOf (
145+ PREVIEW_GEMINI_3_1_FLASH_LITE_MODEL ,
146+ ) ;
147+ const flashIdx = output . indexOf ( DEFAULT_GEMINI_FLASH_MODEL ) ;
148+ const flashLiteIdx = output . indexOf ( DEFAULT_GEMINI_FLASH_LITE_MODEL ) ;
149+
150+ expect ( flashPreviewIdx ) . toBeLessThan ( flashLitePreviewIdx ) ;
151+ expect ( flashLitePreviewIdx ) . toBeLessThan ( flashIdx ) ;
152+ expect ( flashIdx ) . toBeLessThan ( flashLiteIdx ) ;
153+
154+ expect ( output ) . not . toContain ( 'Auto' ) ;
155+ unmount ( ) ;
156+ } ) ;
157+
158+ it ( 'closes dialog on escape in "manual" view for users with no pro access' , async ( ) => {
159+ mockGetProModelNoAccessSync . mockReturnValue ( true ) ;
160+ mockGetProModelNoAccess . mockResolvedValue ( true ) ;
161+ const { stdin, waitUntilReady, unmount } = await renderComponent ( ) ;
162+
163+ // Already in manual view
164+ await act ( async ( ) => {
165+ stdin . write ( '\u001B' ) ; // Escape
166+ } ) ;
167+ await act ( async ( ) => {
168+ await waitUntilReady ( ) ;
169+ } ) ;
170+
171+ await waitFor ( ( ) => {
172+ expect ( mockOnClose ) . toHaveBeenCalled ( ) ;
173+ } ) ;
174+ unmount ( ) ;
175+ } ) ;
176+
112177 it ( 'switches to "manual" view when "Manual" is selected and uses getDisplayString for models' , async ( ) => {
113178 mockGetDisplayString . mockImplementation ( ( val : string ) => {
114179 if ( val === DEFAULT_GEMINI_MODEL ) return 'Formatted Pro Model' ;
@@ -369,5 +434,50 @@ describe('<ModelDialog />', () => {
369434 } ) ;
370435 unmount ( ) ;
371436 } ) ;
437+
438+ it ( 'hides Flash Lite Preview model for users with pro access' , async ( ) => {
439+ mockGetProModelNoAccessSync . mockReturnValue ( false ) ;
440+ mockGetProModelNoAccess . mockResolvedValue ( false ) ;
441+ mockGetHasAccessToPreviewModel . mockReturnValue ( true ) ;
442+ const { lastFrame, stdin, waitUntilReady, unmount } =
443+ await renderComponent ( ) ;
444+
445+ // Go to manual view
446+ await act ( async ( ) => {
447+ stdin . write ( '\u001B[B' ) ; // Manual
448+ } ) ;
449+ await waitUntilReady ( ) ;
450+ await act ( async ( ) => {
451+ stdin . write ( '\r' ) ;
452+ } ) ;
453+ await waitUntilReady ( ) ;
454+
455+ const output = lastFrame ( ) ;
456+ expect ( output ) . not . toContain ( PREVIEW_GEMINI_3_1_FLASH_LITE_MODEL ) ;
457+ unmount ( ) ;
458+ } ) ;
459+
460+ it ( 'shows Flash Lite Preview model for free tier users' , async ( ) => {
461+ mockGetProModelNoAccessSync . mockReturnValue ( false ) ;
462+ mockGetProModelNoAccess . mockResolvedValue ( false ) ;
463+ mockGetHasAccessToPreviewModel . mockReturnValue ( true ) ;
464+ mockGetUserTier . mockReturnValue ( UserTierId . FREE ) ;
465+ const { lastFrame, stdin, waitUntilReady, unmount } =
466+ await renderComponent ( ) ;
467+
468+ // Go to manual view
469+ await act ( async ( ) => {
470+ stdin . write ( '\u001B[B' ) ; // Manual
471+ } ) ;
472+ await waitUntilReady ( ) ;
473+ await act ( async ( ) => {
474+ stdin . write ( '\r' ) ;
475+ } ) ;
476+ await waitUntilReady ( ) ;
477+
478+ const output = lastFrame ( ) ;
479+ expect ( output ) . toContain ( PREVIEW_GEMINI_3_1_FLASH_LITE_MODEL ) ;
480+ unmount ( ) ;
481+ } ) ;
372482 } ) ;
373483} ) ;
0 commit comments