74 lines
3.0 KiB
TypeScript
74 lines
3.0 KiB
TypeScript
import React from 'react';
|
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
import { cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react';
|
|
import { AuthForm } from '../components/auth-form';
|
|
import { signInWithPopup } from 'firebase/auth';
|
|
|
|
vi.mock('../lib/firebase-client', () => ({
|
|
firebaseAuth: {},
|
|
googleProvider: {},
|
|
}));
|
|
|
|
vi.mock('firebase/auth', () => ({
|
|
signInWithPopup: vi.fn(),
|
|
}));
|
|
|
|
describe('AuthForm Google session exchange', () => {
|
|
const originalFetch = global.fetch;
|
|
|
|
beforeEach(() => {
|
|
vi.restoreAllMocks();
|
|
vi.mocked(signInWithPopup).mockResolvedValue({
|
|
user: { getIdToken: async () => 'emulator-id-token' },
|
|
} as Awaited<ReturnType<typeof signInWithPopup>>);
|
|
});
|
|
|
|
afterEach(() => {
|
|
cleanup();
|
|
global.fetch = originalFetch;
|
|
});
|
|
|
|
it.each(['auth/popup-closed-by-user', 'auth/cancelled-popup-request'])('handles %s as cancellation without a console error or session exchange', async (code) => {
|
|
const consoleError = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
global.fetch = vi.fn();
|
|
vi.mocked(signInWithPopup).mockRejectedValueOnce(Object.assign(new Error(`Firebase: Error (${code}).`), { code }));
|
|
render(<AuthForm mode="login" />);
|
|
fireEvent.click(screen.getByRole('button', { name: /sign in with google/i }));
|
|
await waitFor(() => {
|
|
expect(screen.getByRole('status').textContent).toBe('Google sign-in was canceled. You can try again.');
|
|
});
|
|
expect(consoleError).not.toHaveBeenCalled();
|
|
expect(global.fetch).not.toHaveBeenCalled();
|
|
expect((screen.getByRole('button', { name: /sign in with google/i }) as HTMLButtonElement).disabled).toBe(false);
|
|
expect((screen.getByRole('button', { name: 'Sign In' }) as HTMLButtonElement).disabled).toBe(false);
|
|
expect(screen.queryByRole('alert')).toBeNull();
|
|
});
|
|
|
|
it('still reports genuine Google sign-in failures', async () => {
|
|
const consoleError = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
global.fetch = vi.fn();
|
|
vi.mocked(signInWithPopup).mockRejectedValueOnce(Object.assign(new Error('Google sign-in is unavailable.'), { code: 'auth/network-request-failed' }));
|
|
render(<AuthForm mode="login" />);
|
|
fireEvent.click(screen.getByRole('button', { name: /sign in with google/i }));
|
|
await waitFor(() => expect(screen.getByRole('alert').textContent).toBe('Google sign-in is unavailable.'));
|
|
expect(consoleError).toHaveBeenCalledOnce();
|
|
expect(global.fetch).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('shows session rejection when server exchange returns 401', async () => {
|
|
global.fetch = vi.fn().mockResolvedValue({
|
|
ok: false,
|
|
status: 401,
|
|
json: async () => ({ detail: 'Session exchange rejected.' }),
|
|
});
|
|
|
|
render(<AuthForm mode="login" />);
|
|
fireEvent.click(screen.getByRole('button', { name: /sign in with google/i }));
|
|
|
|
await waitFor(() => {
|
|
expect(screen.getByRole('alert').textContent).toBe('Session exchange rejected.');
|
|
});
|
|
expect(global.fetch).toHaveBeenCalledTimes(1);
|
|
});
|
|
});
|