217 lines
6.7 KiB
TypeScript
217 lines
6.7 KiB
TypeScript
import { describe, it, expect, vi } from 'vitest';
|
|
import { render, screen } from '@testing-library/react';
|
|
import userEvent from '@testing-library/user-event';
|
|
import { AuthInput } from './AuthInput';
|
|
import { AuthButton } from './AuthButton';
|
|
import { AuthFormField } from './AuthFormField';
|
|
import { AuthErrorMessage } from './AuthErrorMessage';
|
|
|
|
describe('AuthInput', () => {
|
|
it('should render input with label', () => {
|
|
render(<AuthInput label="Email" id="email-input" />);
|
|
expect(screen.getByLabelText('Email')).toBeInTheDocument();
|
|
});
|
|
|
|
it('should display error message when error prop is provided', () => {
|
|
render(<AuthInput error="Invalid email" id="email-input" />);
|
|
expect(screen.getByText('Invalid email')).toBeInTheDocument();
|
|
expect(screen.getByRole('alert')).toBeInTheDocument();
|
|
});
|
|
|
|
it('should have aria-invalid when error is present', () => {
|
|
render(<AuthInput error="Error message" id="email-input" />);
|
|
const input = screen.getByRole('textbox');
|
|
expect(input).toHaveAttribute('aria-invalid', 'true');
|
|
});
|
|
|
|
it('should apply error styles when error is present', () => {
|
|
render(<AuthInput error="Error" id="email-input" />);
|
|
const input = screen.getByRole('textbox');
|
|
expect(input.className).toContain('border-red-500');
|
|
});
|
|
|
|
it('should handle user input', async () => {
|
|
const user = userEvent.setup();
|
|
const handleChange = vi.fn();
|
|
render(
|
|
<AuthInput
|
|
label="Email"
|
|
id="email-input"
|
|
onChange={handleChange}
|
|
type="email"
|
|
/>,
|
|
);
|
|
|
|
const input = screen.getByLabelText('Email');
|
|
await user.type(input, 'test@example.com');
|
|
|
|
expect(handleChange).toHaveBeenCalled();
|
|
expect(input).toHaveValue('test@example.com');
|
|
});
|
|
|
|
it('should support all standard input props', () => {
|
|
render(
|
|
<AuthInput
|
|
label="Password"
|
|
id="password-input"
|
|
type="password"
|
|
placeholder="Enter password"
|
|
required
|
|
/>,
|
|
);
|
|
|
|
const input = screen.getByLabelText('Password');
|
|
expect(input).toHaveAttribute('type', 'password');
|
|
expect(input).toHaveAttribute('placeholder', 'Enter password');
|
|
expect(input).toBeRequired();
|
|
});
|
|
});
|
|
|
|
describe('AuthButton', () => {
|
|
it('should render button with children', () => {
|
|
render(<AuthButton>Submit</AuthButton>);
|
|
expect(screen.getByRole('button', { name: 'Submit' })).toBeInTheDocument();
|
|
});
|
|
|
|
it('should show loading state', () => {
|
|
render(<AuthButton loading>Submit</AuthButton>);
|
|
expect(screen.getByText('Chargement...')).toBeInTheDocument();
|
|
expect(screen.getByRole('button')).toBeDisabled();
|
|
expect(screen.getByRole('button')).toHaveAttribute('aria-busy', 'true');
|
|
});
|
|
|
|
it('should be disabled when loading', () => {
|
|
render(<AuthButton loading>Submit</AuthButton>);
|
|
expect(screen.getByRole('button')).toBeDisabled();
|
|
});
|
|
|
|
it('should be disabled when disabled prop is true', () => {
|
|
render(<AuthButton disabled>Submit</AuthButton>);
|
|
expect(screen.getByRole('button')).toBeDisabled();
|
|
});
|
|
|
|
it('should apply primary variant styles by default', () => {
|
|
render(<AuthButton>Submit</AuthButton>);
|
|
const button = screen.getByRole('button');
|
|
expect(button.className).toContain('bg-blue-600');
|
|
});
|
|
|
|
it('should apply secondary variant styles', () => {
|
|
render(<AuthButton variant="secondary">Cancel</AuthButton>);
|
|
const button = screen.getByRole('button');
|
|
expect(button.className).toContain('bg-gray-200');
|
|
});
|
|
|
|
it('should handle click events', async () => {
|
|
const user = userEvent.setup();
|
|
const handleClick = vi.fn();
|
|
render(<AuthButton onClick={handleClick}>Click me</AuthButton>);
|
|
|
|
const button = screen.getByRole('button');
|
|
await user.click(button);
|
|
|
|
expect(handleClick).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it('should not trigger click when disabled', async () => {
|
|
const user = userEvent.setup();
|
|
const handleClick = vi.fn();
|
|
render(
|
|
<AuthButton disabled onClick={handleClick}>
|
|
Click me
|
|
</AuthButton>,
|
|
);
|
|
|
|
const button = screen.getByRole('button');
|
|
await user.click(button);
|
|
|
|
expect(handleClick).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe('AuthFormField', () => {
|
|
it('should render label and children', () => {
|
|
render(
|
|
<AuthFormField label="Email">
|
|
<input type="email" />
|
|
</AuthFormField>,
|
|
);
|
|
expect(screen.getByText('Email')).toBeInTheDocument();
|
|
});
|
|
|
|
it('should show required indicator', () => {
|
|
render(
|
|
<AuthFormField label="Email" required>
|
|
<input type="email" />
|
|
</AuthFormField>,
|
|
);
|
|
const label = screen.getByText('Email');
|
|
expect(label.textContent).toContain('*');
|
|
});
|
|
|
|
it('should display error message', () => {
|
|
render(
|
|
<AuthFormField label="Email" error="Invalid email">
|
|
<input type="email" />
|
|
</AuthFormField>,
|
|
);
|
|
expect(screen.getByText('Invalid email')).toBeInTheDocument();
|
|
expect(screen.getByRole('alert')).toBeInTheDocument();
|
|
});
|
|
|
|
it('should display help text when no error', () => {
|
|
render(
|
|
<AuthFormField label="Email" helpText="Enter your email address">
|
|
<input type="email" />
|
|
</AuthFormField>,
|
|
);
|
|
expect(screen.getByText('Enter your email address')).toBeInTheDocument();
|
|
});
|
|
|
|
it('should not display help text when error is present', () => {
|
|
render(
|
|
<AuthFormField
|
|
label="Email"
|
|
error="Invalid email"
|
|
helpText="Enter your email address"
|
|
>
|
|
<input type="email" />
|
|
</AuthFormField>,
|
|
);
|
|
expect(screen.getByText('Invalid email')).toBeInTheDocument();
|
|
expect(
|
|
screen.queryByText('Enter your email address'),
|
|
).not.toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
describe('AuthErrorMessage', () => {
|
|
it('should render error message', () => {
|
|
render(<AuthErrorMessage message="An error occurred" />);
|
|
expect(screen.getByText('An error occurred')).toBeInTheDocument();
|
|
expect(screen.getByRole('alert')).toBeInTheDocument();
|
|
});
|
|
|
|
it('should not render when message is empty', () => {
|
|
const { container } = render(<AuthErrorMessage message="" />);
|
|
expect(container.firstChild).toBeNull();
|
|
});
|
|
|
|
it('should have aria-live attribute', () => {
|
|
render(<AuthErrorMessage message="Error" />);
|
|
const alert = screen.getByRole('alert');
|
|
expect(alert).toHaveAttribute('aria-live', 'polite');
|
|
});
|
|
|
|
it('should apply custom className', () => {
|
|
render(<AuthErrorMessage message="Error" className="custom-class" />);
|
|
const alert = screen.getByRole('alert');
|
|
expect(alert.className).toContain('custom-class');
|
|
});
|
|
|
|
it('should support custom id', () => {
|
|
render(<AuthErrorMessage message="Error" id="custom-error-id" />);
|
|
const alert = screen.getByRole('alert');
|
|
expect(alert).toHaveAttribute('id', 'custom-error-id');
|
|
});
|
|
});
|