mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-03-31 00:11:11 -07:00
complete
This commit is contained in:
@@ -200,4 +200,83 @@ describe('<AppHeader />', () => {
|
||||
expect(lastFrame()).not.toContain('First line\\nSecond line');
|
||||
unmount();
|
||||
});
|
||||
|
||||
it('should render Tips when tipsShown is false', () => {
|
||||
persistentStateMock.get.mockImplementation((key) => {
|
||||
if (key === 'tipsShown') return false;
|
||||
return {};
|
||||
});
|
||||
|
||||
const mockConfig = makeFakeConfig();
|
||||
const uiState = {
|
||||
history: [],
|
||||
bannerData: {
|
||||
defaultText: 'First line\\nSecond line',
|
||||
warningText: '',
|
||||
},
|
||||
bannerVisible: true,
|
||||
};
|
||||
const { lastFrame, unmount } = renderWithProviders(
|
||||
<AppHeader version="1.0.0" />,
|
||||
{ config: mockConfig, uiState },
|
||||
);
|
||||
|
||||
expect(lastFrame()).toContain('Tips');
|
||||
|
||||
expect(persistentStateMock.set).toHaveBeenCalledWith('tipsShown', true);
|
||||
unmount();
|
||||
});
|
||||
|
||||
it('should NOT render Tips when tipsShown is true', () => {
|
||||
persistentStateMock.get.mockImplementation((key) => {
|
||||
if (key === 'tipsShown') return true;
|
||||
return {};
|
||||
});
|
||||
|
||||
const mockConfig = makeFakeConfig();
|
||||
const { lastFrame, unmount } = renderWithProviders(
|
||||
<AppHeader version="1.0.0" />,
|
||||
{ config: mockConfig },
|
||||
);
|
||||
|
||||
expect(lastFrame()).not.toContain('Tips');
|
||||
unmount();
|
||||
});
|
||||
|
||||
it('should show tips on the first run and hide them on the second run (persistence flow)', () => {
|
||||
const fakeStore: Record<string, boolean> = {};
|
||||
|
||||
persistentStateMock.get.mockImplementation((key) => fakeStore[key]);
|
||||
persistentStateMock.set.mockImplementation((key, val) => {
|
||||
fakeStore[key] = val;
|
||||
});
|
||||
|
||||
const mockConfig = makeFakeConfig();
|
||||
const uiState = {
|
||||
history: [],
|
||||
bannerData: {
|
||||
defaultText: 'First line\\nSecond line',
|
||||
warningText: '',
|
||||
},
|
||||
bannerVisible: true,
|
||||
};
|
||||
const session1 = renderWithProviders(<AppHeader version="1.0.0" />, {
|
||||
config: mockConfig,
|
||||
uiState,
|
||||
});
|
||||
|
||||
expect(session1.lastFrame()).toContain('Tips');
|
||||
|
||||
expect(fakeStore['tipsShown']).toBe(true);
|
||||
|
||||
session1.unmount();
|
||||
|
||||
const session2 = renderWithProviders(<AppHeader version="1.0.0" />, {
|
||||
config: mockConfig,
|
||||
});
|
||||
|
||||
expect(session2.lastFrame()).not.toContain('Tips');
|
||||
|
||||
session2.unmount();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -12,6 +12,7 @@ import { useConfig } from '../contexts/ConfigContext.js';
|
||||
import { useUIState } from '../contexts/UIStateContext.js';
|
||||
import { Banner } from './Banner.js';
|
||||
import { useBanner } from '../hooks/useBanner.js';
|
||||
import { useTips } from '../hooks/useTips.js';
|
||||
|
||||
interface AppHeaderProps {
|
||||
version: string;
|
||||
@@ -23,6 +24,7 @@ export const AppHeader = ({ version }: AppHeaderProps) => {
|
||||
const { nightly, mainAreaWidth, bannerData, bannerVisible } = useUIState();
|
||||
|
||||
const { bannerText } = useBanner(bannerData, config);
|
||||
const tipsShown = useTips();
|
||||
|
||||
return (
|
||||
<Box flexDirection="column">
|
||||
@@ -38,9 +40,8 @@ export const AppHeader = ({ version }: AppHeaderProps) => {
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{!(settings.merged.ui.hideTips || config.getScreenReader()) && (
|
||||
<Tips config={config} />
|
||||
)}
|
||||
{!(settings.merged.ui.hideTips || config.getScreenReader()) &&
|
||||
!tipsShown && <Tips config={config} />}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -10,12 +10,7 @@ exports[`<AppHeader /> > should not render the banner when no flags are set 1`]
|
||||
███░ ░░███ ░░███
|
||||
███░ ░░█████████
|
||||
░░░ ░░░░░░░░░
|
||||
|
||||
Tips for getting started:
|
||||
1. Ask questions, edit files, or run commands.
|
||||
2. Be specific for the best results.
|
||||
3. Create GEMINI.md files to customize your interactions with Gemini.
|
||||
4. /help for more information."
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`<AppHeader /> > should not render the banner when previewFeatures is enabled 1`] = `
|
||||
@@ -28,12 +23,7 @@ exports[`<AppHeader /> > should not render the banner when previewFeatures is en
|
||||
███░ ░░███ ░░███
|
||||
███░ ░░█████████
|
||||
░░░ ░░░░░░░░░
|
||||
|
||||
Tips for getting started:
|
||||
1. Ask questions, edit files, or run commands.
|
||||
2. Be specific for the best results.
|
||||
3. Create GEMINI.md files to customize your interactions with Gemini.
|
||||
4. /help for more information."
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`<AppHeader /> > should not render the default banner if shown count is 5 or more 1`] = `
|
||||
@@ -46,12 +36,7 @@ exports[`<AppHeader /> > should not render the default banner if shown count is
|
||||
███░ ░░███ ░░███
|
||||
███░ ░░█████████
|
||||
░░░ ░░░░░░░░░
|
||||
|
||||
Tips for getting started:
|
||||
1. Ask questions, edit files, or run commands.
|
||||
2. Be specific for the best results.
|
||||
3. Create GEMINI.md files to customize your interactions with Gemini.
|
||||
4. /help for more information."
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`<AppHeader /> > should render the banner when previewFeatures is disabled 1`] = `
|
||||
@@ -67,12 +52,7 @@ exports[`<AppHeader /> > should render the banner when previewFeatures is disabl
|
||||
|
||||
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ This is the default banner │
|
||||
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
Tips for getting started:
|
||||
1. Ask questions, edit files, or run commands.
|
||||
2. Be specific for the best results.
|
||||
3. Create GEMINI.md files to customize your interactions with Gemini.
|
||||
4. /help for more information."
|
||||
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯"
|
||||
`;
|
||||
|
||||
exports[`<AppHeader /> > should render the banner with default text 1`] = `
|
||||
@@ -88,12 +68,7 @@ exports[`<AppHeader /> > should render the banner with default text 1`] = `
|
||||
|
||||
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ This is the default banner │
|
||||
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
Tips for getting started:
|
||||
1. Ask questions, edit files, or run commands.
|
||||
2. Be specific for the best results.
|
||||
3. Create GEMINI.md files to customize your interactions with Gemini.
|
||||
4. /help for more information."
|
||||
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯"
|
||||
`;
|
||||
|
||||
exports[`<AppHeader /> > should render the banner with warning text 1`] = `
|
||||
@@ -109,10 +84,5 @@ exports[`<AppHeader /> > should render the banner with warning text 1`] = `
|
||||
|
||||
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ There are capacity issues │
|
||||
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
Tips for getting started:
|
||||
1. Ask questions, edit files, or run commands.
|
||||
2. Be specific for the best results.
|
||||
3. Create GEMINI.md files to customize your interactions with Gemini.
|
||||
4. /help for more information."
|
||||
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯"
|
||||
`;
|
||||
|
||||
42
packages/cli/src/ui/hooks/useTips.test.ts
Normal file
42
packages/cli/src/ui/hooks/useTips.test.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2026 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { renderHookWithProviders } from '../../test-utils/render.js';
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { useTips } from './useTips.js';
|
||||
import { persistentState } from '../../utils/persistentState.js';
|
||||
|
||||
vi.mock('../../utils/persistentState.js', () => ({
|
||||
persistentState: {
|
||||
get: vi.fn(),
|
||||
set: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
describe('useTips()', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should return false and call set(true) if state is undefined', () => {
|
||||
vi.mocked(persistentState.get).mockReturnValue(undefined);
|
||||
|
||||
const { result } = renderHookWithProviders(() => useTips());
|
||||
|
||||
expect(result.current).toBe(false);
|
||||
|
||||
expect(persistentState.set).toHaveBeenCalledWith('tipsShown', true);
|
||||
});
|
||||
|
||||
it('should return true if state is already true', () => {
|
||||
vi.mocked(persistentState.get).mockReturnValue(true);
|
||||
|
||||
const { result } = renderHookWithProviders(() => useTips());
|
||||
|
||||
expect(result.current).toBe(true);
|
||||
expect(persistentState.set).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
20
packages/cli/src/ui/hooks/useTips.ts
Normal file
20
packages/cli/src/ui/hooks/useTips.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2026 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import { persistentState } from '../../utils/persistentState.js';
|
||||
|
||||
export function useTips() {
|
||||
const [tipsShown] = useState(() => !!persistentState.get('tipsShown'));
|
||||
|
||||
useEffect(() => {
|
||||
if (!tipsShown) {
|
||||
persistentState.set('tipsShown', true);
|
||||
}
|
||||
}, [tipsShown]);
|
||||
|
||||
return tipsShown;
|
||||
}
|
||||
@@ -12,6 +12,7 @@ const STATE_FILENAME = 'state.json';
|
||||
|
||||
interface PersistentStateData {
|
||||
defaultBannerShownCount?: Record<string, number>;
|
||||
tipsShown?: boolean;
|
||||
// Add other persistent state keys here as needed
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user