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>); }); 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(); 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(); 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(); 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); }); });