import { MemoryRouter } from 'react-router-dom' import { render, screen, waitFor } from '@testing-library/react' import { beforeEach, describe, expect, it, vi } from 'vitest' import type { TokenBundle } from '@/types' import { AuthContext, type AuthContextValue } from '@/app/providers/auth-context' import { OAuthCallbackPage } from './OAuthCallbackPage' const exchangeOAuthHandoffMock = vi.fn<(code: string) => Promise>() vi.mock('@/services/auth', () => ({ exchangeOAuthHandoff: (code: string) => exchangeOAuthHandoffMock(code), })) const authContextValue: AuthContextValue = { user: null, roles: [], isAdmin: false, isAuthenticated: false, isLoading: false, onLoginSuccess: vi.fn(async () => {}), logout: vi.fn(async () => {}), refreshUser: vi.fn(async () => {}), } function renderOAuthCallbackPage(entry: string) { return render( , ) } describe('OAuthCallbackPage', () => { beforeEach(() => { exchangeOAuthHandoffMock.mockReset() vi.mocked(authContextValue.onLoginSuccess).mockClear() }) it('exchanges handoff code and completes login', async () => { exchangeOAuthHandoffMock.mockResolvedValue({ access_token: 'access-token', refresh_token: 'refresh-token', expires_in: 7200, user: { id: 1, username: 'oauth_user', email: 'oauth@example.com', phone: '', nickname: 'OAuth User', avatar: '', status: 1, }, }) renderOAuthCallbackPage('/login/oauth/callback?redirect=%2Fusers#status=success&code=handoff-code&provider=github') await waitFor(() => expect(exchangeOAuthHandoffMock).toHaveBeenCalledWith('handoff-code')) await waitFor(() => expect(authContextValue.onLoginSuccess).toHaveBeenCalledTimes(1)) }) it('shows error state returned from oauth callback fragment', async () => { renderOAuthCallbackPage('/login/oauth/callback#status=error&message=OAuth%20Denied') expect(await screen.findByText('第三方登录失败')).toBeInTheDocument() expect(screen.getByText('OAuth Denied')).toBeInTheDocument() }) })