mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-30 15:04:16 -07:00
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
This commit is contained in:
@@ -4,24 +4,17 @@
|
|||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { render } from '../../test-utils/render.js';
|
import { renderWithProviders } from '../../test-utils/render.js';
|
||||||
|
import { waitFor } from '../../test-utils/async.js';
|
||||||
import { act } from 'react';
|
import { act } from 'react';
|
||||||
import {
|
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
||||||
describe,
|
|
||||||
it,
|
|
||||||
expect,
|
|
||||||
vi,
|
|
||||||
beforeEach,
|
|
||||||
afterEach,
|
|
||||||
type Mock,
|
|
||||||
} from 'vitest';
|
|
||||||
import { EmptyWalletDialog } from './EmptyWalletDialog.js';
|
import { EmptyWalletDialog } from './EmptyWalletDialog.js';
|
||||||
import { RadioButtonSelect } from './shared/RadioButtonSelect.js';
|
|
||||||
|
|
||||||
// Mock the child component to make it easier to test the parent
|
const writeKey = (stdin: { write: (data: string) => void }, key: string) => {
|
||||||
vi.mock('./shared/RadioButtonSelect.js', () => ({
|
act(() => {
|
||||||
RadioButtonSelect: vi.fn(),
|
stdin.write(key);
|
||||||
}));
|
});
|
||||||
|
};
|
||||||
|
|
||||||
describe('EmptyWalletDialog', () => {
|
describe('EmptyWalletDialog', () => {
|
||||||
const mockOnChoice = vi.fn();
|
const mockOnChoice = vi.fn();
|
||||||
@@ -36,8 +29,8 @@ describe('EmptyWalletDialog', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('rendering', () => {
|
describe('rendering', () => {
|
||||||
it('should render with correct menu options when fallback is available', async () => {
|
it('should match snapshot with fallback available', async () => {
|
||||||
const { unmount, waitUntilReady } = render(
|
const { lastFrame, unmount, waitUntilReady } = renderWithProviders(
|
||||||
<EmptyWalletDialog
|
<EmptyWalletDialog
|
||||||
failedModel="gemini-2.5-pro"
|
failedModel="gemini-2.5-pro"
|
||||||
fallbackModel="gemini-3-flash-preview"
|
fallbackModel="gemini-3-flash-preview"
|
||||||
@@ -47,33 +40,12 @@ describe('EmptyWalletDialog', () => {
|
|||||||
);
|
);
|
||||||
await waitUntilReady();
|
await waitUntilReady();
|
||||||
|
|
||||||
expect(RadioButtonSelect).toHaveBeenCalledWith(
|
expect(lastFrame()).toMatchSnapshot();
|
||||||
expect.objectContaining({
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
label: 'Get AI Credits - Open browser to purchase credits',
|
|
||||||
value: 'get_credits',
|
|
||||||
key: 'get_credits',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Switch to gemini-3-flash-preview',
|
|
||||||
value: 'use_fallback',
|
|
||||||
key: 'use_fallback',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Stop - Abort request',
|
|
||||||
value: 'stop',
|
|
||||||
key: 'stop',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
undefined,
|
|
||||||
);
|
|
||||||
unmount();
|
unmount();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should omit fallback option when fallbackModel is not provided', async () => {
|
it('should match snapshot without fallback', async () => {
|
||||||
const { unmount, waitUntilReady } = render(
|
const { lastFrame, unmount, waitUntilReady } = renderWithProviders(
|
||||||
<EmptyWalletDialog
|
<EmptyWalletDialog
|
||||||
failedModel="gemini-2.5-pro"
|
failedModel="gemini-2.5-pro"
|
||||||
onChoice={mockOnChoice}
|
onChoice={mockOnChoice}
|
||||||
@@ -81,28 +53,12 @@ describe('EmptyWalletDialog', () => {
|
|||||||
);
|
);
|
||||||
await waitUntilReady();
|
await waitUntilReady();
|
||||||
|
|
||||||
expect(RadioButtonSelect).toHaveBeenCalledWith(
|
expect(lastFrame()).toMatchSnapshot();
|
||||||
expect.objectContaining({
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
label: 'Get AI Credits - Open browser to purchase credits',
|
|
||||||
value: 'get_credits',
|
|
||||||
key: 'get_credits',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Stop - Abort request',
|
|
||||||
value: 'stop',
|
|
||||||
key: 'stop',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
undefined,
|
|
||||||
);
|
|
||||||
unmount();
|
unmount();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should display the model name and usage limit message', async () => {
|
it('should display the model name and usage limit message', async () => {
|
||||||
const { lastFrame, unmount, waitUntilReady } = render(
|
const { lastFrame, unmount, waitUntilReady } = renderWithProviders(
|
||||||
<EmptyWalletDialog
|
<EmptyWalletDialog
|
||||||
failedModel="gemini-2.5-pro"
|
failedModel="gemini-2.5-pro"
|
||||||
onChoice={mockOnChoice}
|
onChoice={mockOnChoice}
|
||||||
@@ -117,7 +73,7 @@ describe('EmptyWalletDialog', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should display purchase prompt and credits update notice', async () => {
|
it('should display purchase prompt and credits update notice', async () => {
|
||||||
const { lastFrame, unmount, waitUntilReady } = render(
|
const { lastFrame, unmount, waitUntilReady } = renderWithProviders(
|
||||||
<EmptyWalletDialog
|
<EmptyWalletDialog
|
||||||
failedModel="gemini-2.5-pro"
|
failedModel="gemini-2.5-pro"
|
||||||
onChoice={mockOnChoice}
|
onChoice={mockOnChoice}
|
||||||
@@ -134,7 +90,7 @@ describe('EmptyWalletDialog', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should display reset time when provided', async () => {
|
it('should display reset time when provided', async () => {
|
||||||
const { lastFrame, unmount, waitUntilReady } = render(
|
const { lastFrame, unmount, waitUntilReady } = renderWithProviders(
|
||||||
<EmptyWalletDialog
|
<EmptyWalletDialog
|
||||||
failedModel="gemini-2.5-pro"
|
failedModel="gemini-2.5-pro"
|
||||||
resetTime="3:45 PM"
|
resetTime="3:45 PM"
|
||||||
@@ -150,7 +106,7 @@ describe('EmptyWalletDialog', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should not display reset time when not provided', async () => {
|
it('should not display reset time when not provided', async () => {
|
||||||
const { lastFrame, unmount, waitUntilReady } = render(
|
const { lastFrame, unmount, waitUntilReady } = renderWithProviders(
|
||||||
<EmptyWalletDialog
|
<EmptyWalletDialog
|
||||||
failedModel="gemini-2.5-pro"
|
failedModel="gemini-2.5-pro"
|
||||||
onChoice={mockOnChoice}
|
onChoice={mockOnChoice}
|
||||||
@@ -164,7 +120,7 @@ describe('EmptyWalletDialog', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should display slash command hints', async () => {
|
it('should display slash command hints', async () => {
|
||||||
const { lastFrame, unmount, waitUntilReady } = render(
|
const { lastFrame, unmount, waitUntilReady } = renderWithProviders(
|
||||||
<EmptyWalletDialog
|
<EmptyWalletDialog
|
||||||
failedModel="gemini-2.5-pro"
|
failedModel="gemini-2.5-pro"
|
||||||
onChoice={mockOnChoice}
|
onChoice={mockOnChoice}
|
||||||
@@ -182,7 +138,8 @@ describe('EmptyWalletDialog', () => {
|
|||||||
|
|
||||||
describe('onChoice handling', () => {
|
describe('onChoice handling', () => {
|
||||||
it('should call onGetCredits and onChoice when get_credits is selected', async () => {
|
it('should call onGetCredits and onChoice when get_credits is selected', async () => {
|
||||||
const { unmount, waitUntilReady } = render(
|
// get_credits is the first item, so just press Enter
|
||||||
|
const { unmount, stdin, waitUntilReady } = renderWithProviders(
|
||||||
<EmptyWalletDialog
|
<EmptyWalletDialog
|
||||||
failedModel="gemini-2.5-pro"
|
failedModel="gemini-2.5-pro"
|
||||||
onChoice={mockOnChoice}
|
onChoice={mockOnChoice}
|
||||||
@@ -191,19 +148,17 @@ describe('EmptyWalletDialog', () => {
|
|||||||
);
|
);
|
||||||
await waitUntilReady();
|
await waitUntilReady();
|
||||||
|
|
||||||
const onSelect = (RadioButtonSelect as Mock).mock.calls[0][0].onSelect;
|
writeKey(stdin, '\r');
|
||||||
|
|
||||||
await act(async () => {
|
await waitFor(() => {
|
||||||
onSelect('get_credits');
|
expect(mockOnGetCredits).toHaveBeenCalled();
|
||||||
|
expect(mockOnChoice).toHaveBeenCalledWith('get_credits');
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(mockOnGetCredits).toHaveBeenCalled();
|
|
||||||
expect(mockOnChoice).toHaveBeenCalledWith('get_credits');
|
|
||||||
unmount();
|
unmount();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should call onChoice without onGetCredits when onGetCredits is not provided', async () => {
|
it('should call onChoice without onGetCredits when onGetCredits is not provided', async () => {
|
||||||
const { unmount, waitUntilReady } = render(
|
const { unmount, stdin, waitUntilReady } = renderWithProviders(
|
||||||
<EmptyWalletDialog
|
<EmptyWalletDialog
|
||||||
failedModel="gemini-2.5-pro"
|
failedModel="gemini-2.5-pro"
|
||||||
onChoice={mockOnChoice}
|
onChoice={mockOnChoice}
|
||||||
@@ -211,18 +166,18 @@ describe('EmptyWalletDialog', () => {
|
|||||||
);
|
);
|
||||||
await waitUntilReady();
|
await waitUntilReady();
|
||||||
|
|
||||||
const onSelect = (RadioButtonSelect as Mock).mock.calls[0][0].onSelect;
|
writeKey(stdin, '\r');
|
||||||
|
|
||||||
await act(async () => {
|
await waitFor(() => {
|
||||||
onSelect('get_credits');
|
expect(mockOnChoice).toHaveBeenCalledWith('get_credits');
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(mockOnChoice).toHaveBeenCalledWith('get_credits');
|
|
||||||
unmount();
|
unmount();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should call onChoice with use_fallback when selected', async () => {
|
it('should call onChoice with use_fallback when selected', async () => {
|
||||||
const { unmount, waitUntilReady } = render(
|
// With fallback: items are [get_credits, use_fallback, stop]
|
||||||
|
// use_fallback is the second item: Down + Enter
|
||||||
|
const { unmount, stdin, waitUntilReady } = renderWithProviders(
|
||||||
<EmptyWalletDialog
|
<EmptyWalletDialog
|
||||||
failedModel="gemini-2.5-pro"
|
failedModel="gemini-2.5-pro"
|
||||||
fallbackModel="gemini-3-flash-preview"
|
fallbackModel="gemini-3-flash-preview"
|
||||||
@@ -231,17 +186,19 @@ describe('EmptyWalletDialog', () => {
|
|||||||
);
|
);
|
||||||
await waitUntilReady();
|
await waitUntilReady();
|
||||||
|
|
||||||
const onSelect = (RadioButtonSelect as Mock).mock.calls[0][0].onSelect;
|
writeKey(stdin, '\x1b[B'); // Down arrow
|
||||||
await act(async () => {
|
writeKey(stdin, '\r');
|
||||||
onSelect('use_fallback');
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(mockOnChoice).toHaveBeenCalledWith('use_fallback');
|
await waitFor(() => {
|
||||||
|
expect(mockOnChoice).toHaveBeenCalledWith('use_fallback');
|
||||||
|
});
|
||||||
unmount();
|
unmount();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should call onChoice with stop when selected', async () => {
|
it('should call onChoice with stop when selected', async () => {
|
||||||
const { unmount, waitUntilReady } = render(
|
// Without fallback: items are [get_credits, stop]
|
||||||
|
// stop is the second item: Down + Enter
|
||||||
|
const { unmount, stdin, waitUntilReady } = renderWithProviders(
|
||||||
<EmptyWalletDialog
|
<EmptyWalletDialog
|
||||||
failedModel="gemini-2.5-pro"
|
failedModel="gemini-2.5-pro"
|
||||||
onChoice={mockOnChoice}
|
onChoice={mockOnChoice}
|
||||||
@@ -249,12 +206,12 @@ describe('EmptyWalletDialog', () => {
|
|||||||
);
|
);
|
||||||
await waitUntilReady();
|
await waitUntilReady();
|
||||||
|
|
||||||
const onSelect = (RadioButtonSelect as Mock).mock.calls[0][0].onSelect;
|
writeKey(stdin, '\x1b[B'); // Down arrow
|
||||||
await act(async () => {
|
writeKey(stdin, '\r');
|
||||||
onSelect('stop');
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(mockOnChoice).toHaveBeenCalledWith('stop');
|
await waitFor(() => {
|
||||||
|
expect(mockOnChoice).toHaveBeenCalledWith('stop');
|
||||||
|
});
|
||||||
unmount();
|
unmount();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,16 +4,17 @@
|
|||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { render } from '../../test-utils/render.js';
|
import { renderWithProviders } from '../../test-utils/render.js';
|
||||||
|
import { waitFor } from '../../test-utils/async.js';
|
||||||
import { act } from 'react';
|
import { act } from 'react';
|
||||||
import { describe, it, expect, vi, beforeEach, type Mock } from 'vitest';
|
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
||||||
import { OverageMenuDialog } from './OverageMenuDialog.js';
|
import { OverageMenuDialog } from './OverageMenuDialog.js';
|
||||||
import { RadioButtonSelect } from './shared/RadioButtonSelect.js';
|
|
||||||
|
|
||||||
// Mock the child component to make it easier to test the parent
|
const writeKey = (stdin: { write: (data: string) => void }, key: string) => {
|
||||||
vi.mock('./shared/RadioButtonSelect.js', () => ({
|
act(() => {
|
||||||
RadioButtonSelect: vi.fn(),
|
stdin.write(key);
|
||||||
}));
|
});
|
||||||
|
};
|
||||||
|
|
||||||
describe('OverageMenuDialog', () => {
|
describe('OverageMenuDialog', () => {
|
||||||
const mockOnChoice = vi.fn();
|
const mockOnChoice = vi.fn();
|
||||||
@@ -22,9 +23,13 @@ describe('OverageMenuDialog', () => {
|
|||||||
vi.clearAllMocks();
|
vi.clearAllMocks();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
vi.restoreAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
describe('rendering', () => {
|
describe('rendering', () => {
|
||||||
it('should render with correct menu options when fallback is available', async () => {
|
it('should match snapshot with fallback available', async () => {
|
||||||
const { unmount, waitUntilReady } = render(
|
const { lastFrame, unmount, waitUntilReady } = renderWithProviders(
|
||||||
<OverageMenuDialog
|
<OverageMenuDialog
|
||||||
failedModel="gemini-2.5-pro"
|
failedModel="gemini-2.5-pro"
|
||||||
fallbackModel="gemini-3-flash-preview"
|
fallbackModel="gemini-3-flash-preview"
|
||||||
@@ -35,38 +40,12 @@ describe('OverageMenuDialog', () => {
|
|||||||
);
|
);
|
||||||
await waitUntilReady();
|
await waitUntilReady();
|
||||||
|
|
||||||
expect(RadioButtonSelect).toHaveBeenCalledWith(
|
expect(lastFrame()).toMatchSnapshot();
|
||||||
expect.objectContaining({
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
label: 'Use AI Credits - Continue this request (Overage)',
|
|
||||||
value: 'use_credits',
|
|
||||||
key: 'use_credits',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Manage - View balance and purchase more credits',
|
|
||||||
value: 'manage',
|
|
||||||
key: 'manage',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Switch to gemini-3-flash-preview',
|
|
||||||
value: 'use_fallback',
|
|
||||||
key: 'use_fallback',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Stop - Abort request',
|
|
||||||
value: 'stop',
|
|
||||||
key: 'stop',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
undefined,
|
|
||||||
);
|
|
||||||
unmount();
|
unmount();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should omit fallback option when fallbackModel is not provided', async () => {
|
it('should match snapshot without fallback', async () => {
|
||||||
const { unmount, waitUntilReady } = render(
|
const { lastFrame, unmount, waitUntilReady } = renderWithProviders(
|
||||||
<OverageMenuDialog
|
<OverageMenuDialog
|
||||||
failedModel="gemini-2.5-pro"
|
failedModel="gemini-2.5-pro"
|
||||||
creditBalance={500}
|
creditBalance={500}
|
||||||
@@ -75,33 +54,12 @@ describe('OverageMenuDialog', () => {
|
|||||||
);
|
);
|
||||||
await waitUntilReady();
|
await waitUntilReady();
|
||||||
|
|
||||||
expect(RadioButtonSelect).toHaveBeenCalledWith(
|
expect(lastFrame()).toMatchSnapshot();
|
||||||
expect.objectContaining({
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
label: 'Use AI Credits - Continue this request (Overage)',
|
|
||||||
value: 'use_credits',
|
|
||||||
key: 'use_credits',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Manage - View balance and purchase more credits',
|
|
||||||
value: 'manage',
|
|
||||||
key: 'manage',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Stop - Abort request',
|
|
||||||
value: 'stop',
|
|
||||||
key: 'stop',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
undefined,
|
|
||||||
);
|
|
||||||
unmount();
|
unmount();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should display the credit balance', async () => {
|
it('should display the credit balance', async () => {
|
||||||
const { lastFrame, unmount, waitUntilReady } = render(
|
const { lastFrame, unmount, waitUntilReady } = renderWithProviders(
|
||||||
<OverageMenuDialog
|
<OverageMenuDialog
|
||||||
failedModel="gemini-2.5-pro"
|
failedModel="gemini-2.5-pro"
|
||||||
creditBalance={200}
|
creditBalance={200}
|
||||||
@@ -117,7 +75,7 @@ describe('OverageMenuDialog', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should display the model name', async () => {
|
it('should display the model name', async () => {
|
||||||
const { lastFrame, unmount, waitUntilReady } = render(
|
const { lastFrame, unmount, waitUntilReady } = renderWithProviders(
|
||||||
<OverageMenuDialog
|
<OverageMenuDialog
|
||||||
failedModel="gemini-2.5-pro"
|
failedModel="gemini-2.5-pro"
|
||||||
creditBalance={100}
|
creditBalance={100}
|
||||||
@@ -133,7 +91,7 @@ describe('OverageMenuDialog', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should display reset time when provided', async () => {
|
it('should display reset time when provided', async () => {
|
||||||
const { lastFrame, unmount, waitUntilReady } = render(
|
const { lastFrame, unmount, waitUntilReady } = renderWithProviders(
|
||||||
<OverageMenuDialog
|
<OverageMenuDialog
|
||||||
failedModel="gemini-2.5-pro"
|
failedModel="gemini-2.5-pro"
|
||||||
resetTime="3:45 PM"
|
resetTime="3:45 PM"
|
||||||
@@ -150,7 +108,7 @@ describe('OverageMenuDialog', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should not display reset time when not provided', async () => {
|
it('should not display reset time when not provided', async () => {
|
||||||
const { lastFrame, unmount, waitUntilReady } = render(
|
const { lastFrame, unmount, waitUntilReady } = renderWithProviders(
|
||||||
<OverageMenuDialog
|
<OverageMenuDialog
|
||||||
failedModel="gemini-2.5-pro"
|
failedModel="gemini-2.5-pro"
|
||||||
creditBalance={100}
|
creditBalance={100}
|
||||||
@@ -165,7 +123,7 @@ describe('OverageMenuDialog', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should display slash command hints', async () => {
|
it('should display slash command hints', async () => {
|
||||||
const { lastFrame, unmount, waitUntilReady } = render(
|
const { lastFrame, unmount, waitUntilReady } = renderWithProviders(
|
||||||
<OverageMenuDialog
|
<OverageMenuDialog
|
||||||
failedModel="gemini-2.5-pro"
|
failedModel="gemini-2.5-pro"
|
||||||
creditBalance={100}
|
creditBalance={100}
|
||||||
@@ -184,7 +142,8 @@ describe('OverageMenuDialog', () => {
|
|||||||
|
|
||||||
describe('onChoice handling', () => {
|
describe('onChoice handling', () => {
|
||||||
it('should call onChoice with use_credits when selected', async () => {
|
it('should call onChoice with use_credits when selected', async () => {
|
||||||
const { unmount, waitUntilReady } = render(
|
// use_credits is the first item, so just press Enter
|
||||||
|
const { unmount, stdin, waitUntilReady } = renderWithProviders(
|
||||||
<OverageMenuDialog
|
<OverageMenuDialog
|
||||||
failedModel="gemini-2.5-pro"
|
failedModel="gemini-2.5-pro"
|
||||||
creditBalance={100}
|
creditBalance={100}
|
||||||
@@ -193,17 +152,17 @@ describe('OverageMenuDialog', () => {
|
|||||||
);
|
);
|
||||||
await waitUntilReady();
|
await waitUntilReady();
|
||||||
|
|
||||||
const onSelect = (RadioButtonSelect as Mock).mock.calls[0][0].onSelect;
|
writeKey(stdin, '\r');
|
||||||
await act(async () => {
|
|
||||||
onSelect('use_credits');
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(mockOnChoice).toHaveBeenCalledWith('use_credits');
|
await waitFor(() => {
|
||||||
|
expect(mockOnChoice).toHaveBeenCalledWith('use_credits');
|
||||||
|
});
|
||||||
unmount();
|
unmount();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should call onChoice with manage when selected', async () => {
|
it('should call onChoice with manage when selected', async () => {
|
||||||
const { unmount, waitUntilReady } = render(
|
// manage is the second item: Down + Enter
|
||||||
|
const { unmount, stdin, waitUntilReady } = renderWithProviders(
|
||||||
<OverageMenuDialog
|
<OverageMenuDialog
|
||||||
failedModel="gemini-2.5-pro"
|
failedModel="gemini-2.5-pro"
|
||||||
creditBalance={100}
|
creditBalance={100}
|
||||||
@@ -212,17 +171,19 @@ describe('OverageMenuDialog', () => {
|
|||||||
);
|
);
|
||||||
await waitUntilReady();
|
await waitUntilReady();
|
||||||
|
|
||||||
const onSelect = (RadioButtonSelect as Mock).mock.calls[0][0].onSelect;
|
writeKey(stdin, '\x1b[B'); // Down arrow
|
||||||
await act(async () => {
|
writeKey(stdin, '\r');
|
||||||
onSelect('manage');
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(mockOnChoice).toHaveBeenCalledWith('manage');
|
await waitFor(() => {
|
||||||
|
expect(mockOnChoice).toHaveBeenCalledWith('manage');
|
||||||
|
});
|
||||||
unmount();
|
unmount();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should call onChoice with use_fallback when selected', async () => {
|
it('should call onChoice with use_fallback when selected', async () => {
|
||||||
const { unmount, waitUntilReady } = render(
|
// With fallback: items are [use_credits, manage, use_fallback, stop]
|
||||||
|
// use_fallback is the third item: Down x2 + Enter
|
||||||
|
const { unmount, stdin, waitUntilReady } = renderWithProviders(
|
||||||
<OverageMenuDialog
|
<OverageMenuDialog
|
||||||
failedModel="gemini-2.5-pro"
|
failedModel="gemini-2.5-pro"
|
||||||
fallbackModel="gemini-3-flash-preview"
|
fallbackModel="gemini-3-flash-preview"
|
||||||
@@ -232,17 +193,20 @@ describe('OverageMenuDialog', () => {
|
|||||||
);
|
);
|
||||||
await waitUntilReady();
|
await waitUntilReady();
|
||||||
|
|
||||||
const onSelect = (RadioButtonSelect as Mock).mock.calls[0][0].onSelect;
|
writeKey(stdin, '\x1b[B'); // Down arrow
|
||||||
await act(async () => {
|
writeKey(stdin, '\x1b[B'); // Down arrow
|
||||||
onSelect('use_fallback');
|
writeKey(stdin, '\r');
|
||||||
});
|
|
||||||
|
|
||||||
expect(mockOnChoice).toHaveBeenCalledWith('use_fallback');
|
await waitFor(() => {
|
||||||
|
expect(mockOnChoice).toHaveBeenCalledWith('use_fallback');
|
||||||
|
});
|
||||||
unmount();
|
unmount();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should call onChoice with stop when selected', async () => {
|
it('should call onChoice with stop when selected', async () => {
|
||||||
const { unmount, waitUntilReady } = render(
|
// Without fallback: items are [use_credits, manage, stop]
|
||||||
|
// stop is the third item: Down x2 + Enter
|
||||||
|
const { unmount, stdin, waitUntilReady } = renderWithProviders(
|
||||||
<OverageMenuDialog
|
<OverageMenuDialog
|
||||||
failedModel="gemini-2.5-pro"
|
failedModel="gemini-2.5-pro"
|
||||||
creditBalance={100}
|
creditBalance={100}
|
||||||
@@ -251,12 +215,13 @@ describe('OverageMenuDialog', () => {
|
|||||||
);
|
);
|
||||||
await waitUntilReady();
|
await waitUntilReady();
|
||||||
|
|
||||||
const onSelect = (RadioButtonSelect as Mock).mock.calls[0][0].onSelect;
|
writeKey(stdin, '\x1b[B'); // Down arrow
|
||||||
await act(async () => {
|
writeKey(stdin, '\x1b[B'); // Down arrow
|
||||||
onSelect('stop');
|
writeKey(stdin, '\r');
|
||||||
});
|
|
||||||
|
|
||||||
expect(mockOnChoice).toHaveBeenCalledWith('stop');
|
await waitFor(() => {
|
||||||
|
expect(mockOnChoice).toHaveBeenCalledWith('stop');
|
||||||
|
});
|
||||||
unmount();
|
unmount();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||||
|
|
||||||
|
exports[`EmptyWalletDialog > rendering > should match snapshot with fallback available 1`] = `
|
||||||
|
"╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||||
|
│ │
|
||||||
|
│ Usage limit reached for gemini-2.5-pro. │
|
||||||
|
│ Access resets at 2:00 PM. │
|
||||||
|
│ /stats model for usage details │
|
||||||
|
│ /model to switch models. │
|
||||||
|
│ /auth to switch to API key. │
|
||||||
|
│ │
|
||||||
|
│ To continue using this model now, purchase more AI Credits. │
|
||||||
|
│ │
|
||||||
|
│ Newly purchased AI credits may take a few minutes to update. │
|
||||||
|
│ │
|
||||||
|
│ How would you like to proceed? │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ ● 1. Get AI Credits - Open browser to purchase credits │
|
||||||
|
│ 2. Switch to gemini-3-flash-preview │
|
||||||
|
│ 3. Stop - Abort request │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||||
|
"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`EmptyWalletDialog > rendering > should match snapshot without fallback 1`] = `
|
||||||
|
"╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||||
|
│ │
|
||||||
|
│ Usage limit reached for gemini-2.5-pro. │
|
||||||
|
│ /stats model for usage details │
|
||||||
|
│ /model to switch models. │
|
||||||
|
│ /auth to switch to API key. │
|
||||||
|
│ │
|
||||||
|
│ To continue using this model now, purchase more AI Credits. │
|
||||||
|
│ │
|
||||||
|
│ Newly purchased AI credits may take a few minutes to update. │
|
||||||
|
│ │
|
||||||
|
│ How would you like to proceed? │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ ● 1. Get AI Credits - Open browser to purchase credits │
|
||||||
|
│ 2. Stop - Abort request │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||||
|
"
|
||||||
|
`;
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||||
|
|
||||||
|
exports[`OverageMenuDialog > rendering > should match snapshot with fallback available 1`] = `
|
||||||
|
"╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||||
|
│ │
|
||||||
|
│ Usage limit reached for gemini-2.5-pro. │
|
||||||
|
│ Access resets at 2:00 PM. │
|
||||||
|
│ /stats model for usage details │
|
||||||
|
│ /model to switch models. │
|
||||||
|
│ /auth to switch to API key. │
|
||||||
|
│ │
|
||||||
|
│ You have 500 AI Credits available. │
|
||||||
|
│ │
|
||||||
|
│ How would you like to proceed? │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ ● 1. Use AI Credits - Continue this request (Overage) │
|
||||||
|
│ 2. Manage - View balance and purchase more credits │
|
||||||
|
│ 3. Switch to gemini-3-flash-preview │
|
||||||
|
│ 4. Stop - Abort request │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||||
|
"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`OverageMenuDialog > rendering > should match snapshot without fallback 1`] = `
|
||||||
|
"╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||||
|
│ │
|
||||||
|
│ Usage limit reached for gemini-2.5-pro. │
|
||||||
|
│ /stats model for usage details │
|
||||||
|
│ /model to switch models. │
|
||||||
|
│ /auth to switch to API key. │
|
||||||
|
│ │
|
||||||
|
│ You have 500 AI Credits available. │
|
||||||
|
│ │
|
||||||
|
│ How would you like to proceed? │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ ● 1. Use AI Credits - Continue this request (Overage) │
|
||||||
|
│ 2. Manage - View balance and purchase more credits │
|
||||||
|
│ 3. Stop - Abort request │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||||
|
"
|
||||||
|
`;
|
||||||
@@ -103,7 +103,7 @@ describe('useQuotaAndFallback', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
vi.clearAllMocks();
|
vi.restoreAllMocks();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should register a fallback handler on initialization', () => {
|
it('should register a fallback handler on initialization', () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user