mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-04-30 20:50:34 +00:00
refactor(auth): save authType after successfully authenticated (#1036)
This commit is contained in:
parent
3ed93d5b5d
commit
d0e76c76a8
30 changed files with 822 additions and 518 deletions
|
|
@ -9,6 +9,53 @@ import { AuthDialog } from './AuthDialog.js';
|
|||
import { LoadedSettings, SettingScope } from '../../config/settings.js';
|
||||
import { AuthType } from '@qwen-code/qwen-code-core';
|
||||
import { renderWithProviders } from '../../test-utils/render.js';
|
||||
import { UIStateContext } from '../contexts/UIStateContext.js';
|
||||
import { UIActionsContext } from '../contexts/UIActionsContext.js';
|
||||
import type { UIState } from '../contexts/UIStateContext.js';
|
||||
import type { UIActions } from '../contexts/UIActionsContext.js';
|
||||
|
||||
const createMockUIState = (overrides: Partial<UIState> = {}): UIState => {
|
||||
// AuthDialog only uses authError and pendingAuthType
|
||||
const baseState = {
|
||||
authError: null,
|
||||
pendingAuthType: undefined,
|
||||
} as Partial<UIState>;
|
||||
|
||||
return {
|
||||
...baseState,
|
||||
...overrides,
|
||||
} as UIState;
|
||||
};
|
||||
|
||||
const createMockUIActions = (overrides: Partial<UIActions> = {}): UIActions => {
|
||||
// AuthDialog only uses handleAuthSelect
|
||||
const baseActions = {
|
||||
handleAuthSelect: vi.fn(),
|
||||
} as Partial<UIActions>;
|
||||
|
||||
return {
|
||||
...baseActions,
|
||||
...overrides,
|
||||
} as UIActions;
|
||||
};
|
||||
|
||||
const renderAuthDialog = (
|
||||
settings: LoadedSettings,
|
||||
uiStateOverrides: Partial<UIState> = {},
|
||||
uiActionsOverrides: Partial<UIActions> = {},
|
||||
) => {
|
||||
const uiState = createMockUIState(uiStateOverrides);
|
||||
const uiActions = createMockUIActions(uiActionsOverrides);
|
||||
|
||||
return renderWithProviders(
|
||||
<UIStateContext.Provider value={uiState}>
|
||||
<UIActionsContext.Provider value={uiActions}>
|
||||
<AuthDialog />
|
||||
</UIActionsContext.Provider>
|
||||
</UIStateContext.Provider>,
|
||||
{ settings },
|
||||
);
|
||||
};
|
||||
|
||||
describe('AuthDialog', () => {
|
||||
const wait = (ms = 50) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||
|
|
@ -66,13 +113,9 @@ describe('AuthDialog', () => {
|
|||
new Set(),
|
||||
);
|
||||
|
||||
const { lastFrame } = renderWithProviders(
|
||||
<AuthDialog
|
||||
onSelect={() => {}}
|
||||
settings={settings}
|
||||
initialErrorMessage="GEMINI_API_KEY environment variable not found"
|
||||
/>,
|
||||
);
|
||||
const { lastFrame } = renderAuthDialog(settings, {
|
||||
authError: 'GEMINI_API_KEY environment variable not found',
|
||||
});
|
||||
|
||||
expect(lastFrame()).toContain(
|
||||
'GEMINI_API_KEY environment variable not found',
|
||||
|
|
@ -116,9 +159,7 @@ describe('AuthDialog', () => {
|
|||
new Set(),
|
||||
);
|
||||
|
||||
const { lastFrame } = renderWithProviders(
|
||||
<AuthDialog onSelect={() => {}} settings={settings} />,
|
||||
);
|
||||
const { lastFrame } = renderAuthDialog(settings);
|
||||
|
||||
// Since the auth dialog only shows OpenAI option now,
|
||||
// it won't show GEMINI_API_KEY messages
|
||||
|
|
@ -162,9 +203,7 @@ describe('AuthDialog', () => {
|
|||
new Set(),
|
||||
);
|
||||
|
||||
const { lastFrame } = renderWithProviders(
|
||||
<AuthDialog onSelect={() => {}} settings={settings} />,
|
||||
);
|
||||
const { lastFrame } = renderAuthDialog(settings);
|
||||
|
||||
expect(lastFrame()).not.toContain(
|
||||
'Existing API key detected (GEMINI_API_KEY)',
|
||||
|
|
@ -208,9 +247,7 @@ describe('AuthDialog', () => {
|
|||
new Set(),
|
||||
);
|
||||
|
||||
const { lastFrame } = renderWithProviders(
|
||||
<AuthDialog onSelect={() => {}} settings={settings} />,
|
||||
);
|
||||
const { lastFrame } = renderAuthDialog(settings);
|
||||
|
||||
// Since the auth dialog only shows OpenAI option now,
|
||||
// it won't show GEMINI_API_KEY messages
|
||||
|
|
@ -255,9 +292,7 @@ describe('AuthDialog', () => {
|
|||
new Set(),
|
||||
);
|
||||
|
||||
const { lastFrame } = renderWithProviders(
|
||||
<AuthDialog onSelect={() => {}} settings={settings} />,
|
||||
);
|
||||
const { lastFrame } = renderAuthDialog(settings);
|
||||
|
||||
// This is a bit brittle, but it's the best way to check which item is selected.
|
||||
expect(lastFrame()).toContain('● 2. OpenAI');
|
||||
|
|
@ -297,9 +332,7 @@ describe('AuthDialog', () => {
|
|||
new Set(),
|
||||
);
|
||||
|
||||
const { lastFrame } = renderWithProviders(
|
||||
<AuthDialog onSelect={() => {}} settings={settings} />,
|
||||
);
|
||||
const { lastFrame } = renderAuthDialog(settings);
|
||||
|
||||
// Default is Qwen OAuth (first option)
|
||||
expect(lastFrame()).toContain('● 1. Qwen OAuth');
|
||||
|
|
@ -341,9 +374,7 @@ describe('AuthDialog', () => {
|
|||
new Set(),
|
||||
);
|
||||
|
||||
const { lastFrame } = renderWithProviders(
|
||||
<AuthDialog onSelect={() => {}} settings={settings} />,
|
||||
);
|
||||
const { lastFrame } = renderAuthDialog(settings);
|
||||
|
||||
// Since the auth dialog doesn't show QWEN_DEFAULT_AUTH_TYPE errors anymore,
|
||||
// it will just show the default Qwen OAuth option
|
||||
|
|
@ -352,7 +383,7 @@ describe('AuthDialog', () => {
|
|||
});
|
||||
|
||||
it('should prevent exiting when no auth method is selected and show error message', async () => {
|
||||
const onSelect = vi.fn();
|
||||
const handleAuthSelect = vi.fn();
|
||||
const settings: LoadedSettings = new LoadedSettings(
|
||||
{
|
||||
settings: { ui: { customThemes: {} }, mcpServers: {} },
|
||||
|
|
@ -386,8 +417,10 @@ describe('AuthDialog', () => {
|
|||
new Set(),
|
||||
);
|
||||
|
||||
const { lastFrame, stdin, unmount } = renderWithProviders(
|
||||
<AuthDialog onSelect={onSelect} settings={settings} />,
|
||||
const { lastFrame, stdin, unmount } = renderAuthDialog(
|
||||
settings,
|
||||
{},
|
||||
{ handleAuthSelect },
|
||||
);
|
||||
await wait();
|
||||
|
||||
|
|
@ -395,16 +428,16 @@ describe('AuthDialog', () => {
|
|||
stdin.write('\u001b'); // ESC key
|
||||
await wait();
|
||||
|
||||
// Should show error message instead of calling onSelect
|
||||
// Should show error message instead of calling handleAuthSelect
|
||||
expect(lastFrame()).toContain(
|
||||
'You must select an auth method to proceed. Press Ctrl+C again to exit.',
|
||||
);
|
||||
expect(onSelect).not.toHaveBeenCalled();
|
||||
expect(handleAuthSelect).not.toHaveBeenCalled();
|
||||
unmount();
|
||||
});
|
||||
|
||||
it('should not exit if there is already an error message', async () => {
|
||||
const onSelect = vi.fn();
|
||||
const handleAuthSelect = vi.fn();
|
||||
const settings: LoadedSettings = new LoadedSettings(
|
||||
{
|
||||
settings: { ui: { customThemes: {} }, mcpServers: {} },
|
||||
|
|
@ -438,12 +471,10 @@ describe('AuthDialog', () => {
|
|||
new Set(),
|
||||
);
|
||||
|
||||
const { lastFrame, stdin, unmount } = renderWithProviders(
|
||||
<AuthDialog
|
||||
onSelect={onSelect}
|
||||
settings={settings}
|
||||
initialErrorMessage="Initial error"
|
||||
/>,
|
||||
const { lastFrame, stdin, unmount } = renderAuthDialog(
|
||||
settings,
|
||||
{ authError: 'Initial error' },
|
||||
{ handleAuthSelect },
|
||||
);
|
||||
await wait();
|
||||
|
||||
|
|
@ -453,13 +484,13 @@ describe('AuthDialog', () => {
|
|||
stdin.write('\u001b'); // ESC key
|
||||
await wait();
|
||||
|
||||
// Should not call onSelect
|
||||
expect(onSelect).not.toHaveBeenCalled();
|
||||
// Should not call handleAuthSelect
|
||||
expect(handleAuthSelect).not.toHaveBeenCalled();
|
||||
unmount();
|
||||
});
|
||||
|
||||
it('should allow exiting when auth method is already selected', async () => {
|
||||
const onSelect = vi.fn();
|
||||
const handleAuthSelect = vi.fn();
|
||||
const settings: LoadedSettings = new LoadedSettings(
|
||||
{
|
||||
settings: { ui: { customThemes: {} }, mcpServers: {} },
|
||||
|
|
@ -493,8 +524,10 @@ describe('AuthDialog', () => {
|
|||
new Set(),
|
||||
);
|
||||
|
||||
const { stdin, unmount } = renderWithProviders(
|
||||
<AuthDialog onSelect={onSelect} settings={settings} />,
|
||||
const { stdin, unmount } = renderAuthDialog(
|
||||
settings,
|
||||
{},
|
||||
{ handleAuthSelect },
|
||||
);
|
||||
await wait();
|
||||
|
||||
|
|
@ -502,8 +535,8 @@ describe('AuthDialog', () => {
|
|||
stdin.write('\u001b'); // ESC key
|
||||
await wait();
|
||||
|
||||
// Should call onSelect with undefined to exit
|
||||
expect(onSelect).toHaveBeenCalledWith(undefined, SettingScope.User);
|
||||
// Should call handleAuthSelect with undefined to exit
|
||||
expect(handleAuthSelect).toHaveBeenCalledWith(undefined, SettingScope.User);
|
||||
unmount();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue