diff --git a/frontend/src/components/TwoFactorModal.test.tsx b/frontend/src/components/TwoFactorModal.test.tsx index e6f3aeb..4e11ffa 100644 --- a/frontend/src/components/TwoFactorModal.test.tsx +++ b/frontend/src/components/TwoFactorModal.test.tsx @@ -1,57 +1,68 @@ import { fireEvent, render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { describe, expect, it, vi } from 'vitest'; +import type { TwoFactorContext } from '../apple-signing'; import { TwoFactorModal } from './TwoFactorModal'; +function createTwoFactorContext(overrides: Partial = {}): TwoFactorContext { + return { + submitDeviceCode: vi.fn(), + trustedPhoneNumbers: [], + requestSms: vi.fn(), + submitSmsCode: vi.fn(), + ...overrides, + }; +} + describe('TwoFactorModal', () => { it('is hidden when closed', () => { - const { container } = render( {}} onCancel={() => {}} />); + const { container } = render( {}} />); expect(container.querySelector('.modal')).not.toHaveClass('open'); }); it('focuses the input when opened', async () => { - render( {}} onCancel={() => {}} />); + render( {}} />); // useEffect runs a setTimeout(0) before focusing — let it tick. await new Promise((resolve) => setTimeout(resolve, 10)); expect(screen.getByLabelText('Verification Code')).toHaveFocus(); }); it('shows an inline error when submitting an empty code', async () => { - const onSubmit = vi.fn(); - render( {}} />); + const ctx = createTwoFactorContext(); + render( {}} />); await userEvent.click(screen.getByRole('button', { name: 'Verify' })); - expect(screen.getByText('Please enter verification code.')).toBeInTheDocument(); - expect(onSubmit).not.toHaveBeenCalled(); + expect(screen.getByText('Please enter the verification code.')).toBeInTheDocument(); + expect(ctx.submitDeviceCode).not.toHaveBeenCalled(); }); it('clears the error once the user starts typing', async () => { - render( {}} onCancel={() => {}} />); + render( {}} />); await userEvent.click(screen.getByRole('button', { name: 'Verify' })); - expect(screen.getByText('Please enter verification code.')).toBeInTheDocument(); + expect(screen.getByText('Please enter the verification code.')).toBeInTheDocument(); await userEvent.type(screen.getByLabelText('Verification Code'), '1'); - expect(screen.queryByText('Please enter verification code.')).not.toBeInTheDocument(); + expect(screen.queryByText('Please enter the verification code.')).not.toBeInTheDocument(); }); it('submits a trimmed code on Enter', async () => { - const onSubmit = vi.fn(); - render( {}} />); + const ctx = createTwoFactorContext(); + render( {}} />); const input = screen.getByLabelText('Verification Code'); await userEvent.type(input, ' 123456 '); fireEvent.keyDown(input, { key: 'Enter' }); - expect(onSubmit).toHaveBeenLastCalledWith('123456'); + expect(ctx.submitDeviceCode).toHaveBeenLastCalledWith('123456'); }); it('submits via the Verify button', async () => { - const onSubmit = vi.fn(); - render( {}} />); + const ctx = createTwoFactorContext(); + render( {}} />); await userEvent.type(screen.getByLabelText('Verification Code'), '654321'); await userEvent.click(screen.getByRole('button', { name: 'Verify' })); - expect(onSubmit).toHaveBeenLastCalledWith('654321'); + expect(ctx.submitDeviceCode).toHaveBeenLastCalledWith('654321'); }); it('calls onCancel when Cancel is clicked', async () => { const onCancel = vi.fn(); - render( {}} onCancel={onCancel} />); + render(); await userEvent.click(screen.getByRole('button', { name: 'Cancel' })); expect(onCancel).toHaveBeenCalledTimes(1); });