import {render, screen} from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import {describe, expect, it, vi} from 'vitest'; import {AuthFooter, CheckField, FormErrors, TextField, fieldErrors} from './AuthFields'; describe('fieldErrors', () => { const data = {errors: {email: ['is invalid', 'is taken'], recaptcha: ['failed'], base: ['Try later']}}; const result = fieldErrors(data, ['email']); it('groups messages for the named fields', () => { expect(result.byField).toEqual({email: ['is invalid', 'is taken']}); }); it('names the field in other messages', () => { expect(result.other).toContain('Recaptcha failed'); }); it('shows base messages as they are', () => { expect(result.other).toContain('Try later'); }); it('copes with no errors', () => { expect(fieldErrors(null, ['email'])).toEqual({byField: {}, other: []}); }); }); describe('TextField', () => { it('labels the input', () => { render(); expect(screen.getByLabelText('Email')).toBeInTheDocument(); }); it('shows the hint', () => { render(); expect(screen.getByLabelText('Email')).toHaveAccessibleDescription('We never spam'); }); it('shows the errors', () => { render(); expect(screen.getByLabelText('Email')).toHaveAccessibleDescription('is invalid'); }); it('marks the input invalid when there are errors', () => { render(); expect(screen.getByLabelText('Email')).toBeInvalid(); }); it('is valid without errors', () => { render(); expect(screen.getByLabelText('Email')).not.toHaveAttribute('aria-invalid'); }); it('passes other props to the input', () => { render(); expect(screen.getByLabelText('Email')).toHaveAttribute('type', 'email'); }); }); describe('CheckField', () => { it('is a labelled checkbox', () => { render(); expect(screen.getByRole('checkbox', {name: 'Agree'})).toBeInTheDocument(); }); it('shows the errors', () => { render(); expect(screen.getByRole('checkbox')).toHaveAccessibleDescription('must be accepted'); }); }); describe('FormErrors', () => { it('draws nothing without messages', () => { const {container} = render(); expect(container).toBeEmptyDOMElement(); }); it('announces the messages as an alert', () => { render(); expect(screen.getByRole('alert')).toHaveTextContent('BadWorse'); }); }); describe('AuthFooter', () => { const props = {label: 'Sign in', busyLabel: 'Signing in…', prompt: 'New here?', switchLabel: 'Join', switchHref: '/join', onSwitch: vi.fn()}; it('has a submit button', () => { render(); expect(screen.getByRole('button', {name: 'Sign in'})).toHaveAttribute('type', 'submit'); }); it('shows the busy label while busy', () => { render(); expect(screen.getByRole('button', {name: 'Signing in…'})).toBeDisabled(); }); it('keeps the other dialog\'s page as the link target', () => { render(); expect(screen.getByRole('link', {name: 'Join'})).toHaveAttribute('href', '/join'); }); it('switches dialog instead of following the link', async () => { const onSwitch = vi.fn(); render(); await userEvent.click(screen.getByRole('link', {name: 'Join'})); expect(onSwitch).toHaveBeenCalledTimes(1); }); });