veza/apps/web/src/features/auth/components/TwoFactorVerify.test.tsx
senke 37981c2c17 chore(refactor/sumi-migration): commit pending changes — tests, stream server, dist_verification
- apps/web: test updates (Vitest/setup), playbackAnalyticsService, TrackGrid, serviceErrorHandler
- veza-common: logging, metrics, traits, validation, random
- veza-stream-server: audio pipeline, codecs, cache, monitoring, routes
- apps/web/dist_verification: refresh build assets (content-hashed filenames)

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-13 19:39:18 +01:00

173 lines
4.9 KiB
TypeScript

/**
* Tests for TwoFactorVerify Component
* FE-TEST-005: Test two-factor verification component
*/
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { render, screen, fireEvent } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { TwoFactorVerify } from './TwoFactorVerify';
// Mock useToast
vi.mock('@/hooks/useToast', () => ({
useToast: () => ({
toast: vi.fn(),
success: vi.fn(),
error: vi.fn(),
warning: vi.fn(),
info: vi.fn(),
}),
}));
describe('TwoFactorVerify', () => {
const mockOnSuccess = vi.fn();
const mockOnCancel = vi.fn();
beforeEach(() => {
vi.clearAllMocks();
});
it('should render two-factor verification form', () => {
render(
<TwoFactorVerify onSuccess={mockOnSuccess} onCancel={mockOnCancel} />,
);
expect(screen.getByText('Two-Factor Authentication')).toBeInTheDocument();
expect(
screen.getByText('Enter the code from your authenticator app'),
).toBeInTheDocument();
expect(screen.getByLabelText('Verification Code')).toBeInTheDocument();
});
it('should allow entering verification code', async () => {
const user = userEvent.setup();
render(
<TwoFactorVerify onSuccess={mockOnSuccess} onCancel={mockOnCancel} />,
);
const codeInput = screen.getByLabelText('Verification Code');
await user.type(codeInput, '123456');
expect(codeInput).toHaveValue('123456');
});
it('should only allow numeric input', async () => {
const user = userEvent.setup();
render(
<TwoFactorVerify onSuccess={mockOnSuccess} onCancel={mockOnCancel} />,
);
const codeInput = screen.getByLabelText('Verification Code');
await user.type(codeInput, 'abc123def456');
expect(codeInput).toHaveValue('123456');
});
it('should limit code to 6 digits', async () => {
const user = userEvent.setup();
render(
<TwoFactorVerify onSuccess={mockOnSuccess} onCancel={mockOnCancel} />,
);
const codeInput = screen.getByLabelText('Verification Code');
await user.type(codeInput, '1234567890');
expect(codeInput).toHaveValue('123456');
});
it('should call onSuccess when verify button is clicked', async () => {
const user = userEvent.setup();
render(
<TwoFactorVerify onSuccess={mockOnSuccess} onCancel={mockOnCancel} />,
);
const codeInput = screen.getByLabelText('Verification Code');
const verifyButton = screen.getByRole('button', { name: /verify/i });
await user.type(codeInput, '123456');
await user.click(verifyButton);
expect(mockOnSuccess).toHaveBeenCalledWith('123456');
});
it('should switch to backup code mode', async () => {
const user = userEvent.setup();
render(
<TwoFactorVerify onSuccess={mockOnSuccess} onCancel={mockOnCancel} />,
);
const useBackupLink = screen.getByText(/use a backup code/i);
await user.click(useBackupLink);
expect(screen.getByLabelText('Backup Code')).toBeInTheDocument();
expect(
screen.queryByLabelText('Verification Code'),
).not.toBeInTheDocument();
});
it('should switch back to regular code mode', async () => {
const user = userEvent.setup();
render(
<TwoFactorVerify onSuccess={mockOnSuccess} onCancel={mockOnCancel} />,
);
// Switch to backup code
const useBackupLink = screen.getByText(/use a backup code/i);
await user.click(useBackupLink);
// Switch back
const useAuthLink = screen.getByText(/use authenticator code instead/i);
await user.click(useAuthLink);
expect(screen.getByLabelText('Verification Code')).toBeInTheDocument();
expect(screen.queryByLabelText('Backup Code')).not.toBeInTheDocument();
});
it('should call onSuccess with backup code', async () => {
const user = userEvent.setup();
render(
<TwoFactorVerify onSuccess={mockOnSuccess} onCancel={mockOnCancel} />,
);
// Switch to backup code
const useBackupLink = screen.getByText(/use a backup code/i);
await user.click(useBackupLink);
const backupInput = screen.getByLabelText('Backup Code');
const verifyButton = screen.getByRole('button', { name: /verify/i });
await user.type(backupInput, 'backup-code-123');
await user.click(verifyButton);
expect(mockOnSuccess).toHaveBeenCalledWith('backup-code-123');
});
it('should call onCancel when cancel button is clicked', async () => {
const user = userEvent.setup();
render(
<TwoFactorVerify onSuccess={mockOnSuccess} onCancel={mockOnCancel} />,
);
const cancelButton = screen.getByRole('button', { name: /cancel/i });
await user.click(cancelButton);
expect(mockOnCancel).toHaveBeenCalled();
});
it('should disable verify button when no code entered', () => {
render(
<TwoFactorVerify onSuccess={mockOnSuccess} onCancel={mockOnCancel} />,
);
const verifyButton = screen.getByRole('button', { name: /verify/i });
expect(verifyButton).toBeDisabled();
});
});