```typescript // vitest.config.ts import { defineConfig } from 'vitest/config'; import react from '@vitejs/plugin-react';
export default defineConfig({ plugins: [react()], test: { environment: 'jsdom', setupFiles: ['./src/setupTests.ts'], globals: true, }, }); ```
``typescript // src/setupTests.ts import '@testing-library/jest-dom/extend-expect'; // Any global test setup, like mocking fetch or other browser APIs, can go here. // For React Testing Library, extending jest-dom is often sufficient. ``
```typescript // src/apiMocks.ts import { vi } from 'vitest';
// A mock function to simulate an API call export const submitContactForm = vi.fn((data: { name: string; email: string; message: string }) => { return new Promise((resolve) => { setTimeout(() => { if (data.email.includes('fail')) { resolve({ success: false, message: 'Server error' }); } else { resolve({ success: true, message: 'Form submitted' }); } }, 100); }); }); ```
```typescript // src/components/ContactForm.test.tsx import { render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { expect, vi, describe, it, beforeEach } from 'vitest'; import ContactForm from './ContactForm'; // Assuming this component exists import { submitContactForm } from '../apiMocks';
// Mock the API service to control its behavior during tests vi.mock('../apiMocks', () => ({ submitContactForm: vi.fn(), }));
describe('ContactForm', () => { beforeEach(() => { // Clear mock calls and reset mock implementation before each test vi.clearAllMocks(); });
it('renders with initial empty fields and a disabled submit button', () => { render(<ContactForm />); expect(screen.getByLabelText(/name/i)).toHaveValue(''); expect(screen.getByLabelText(/email/i)).toHaveValue(''); expect(screen.getByLabelText(/message/i)).toHaveValue(''); expect(screen.getByRole('button', { name: /submit/i })).toBeDisabled(); });
it('displays client-side validation error for invalid email format', async () => { render(<ContactForm />); const nameInput = screen.getByLabelText(/name/i); const emailInput = screen.getByLabelText(/email/i); const messageInput = screen.getByLabelText(/message/i); const submitButton = screen.getByRole('button', { name: /submit/i });
await userEvent.type(nameInput, 'Test User'); await userEvent.type(emailInput, 'invalid-email'); // Invalid format await userEvent.type(messageInput, 'Test message'); expect(submitButton).toBeEnabled(); // Should be enabled if other fields are valid
await userEvent.click(submitButton);
// Assert client-side validation message appears expect(await screen.findByText(/please enter a valid email/i)).toBeInTheDocument(); expect(submitContactForm).not.toHaveBeenCalled(); // API should not be called });
it('submits the form successfully with valid data and shows a success message', async () => { // Configure the mock API to resolve successfully (submitContactForm as ReturnType<typeof vi.fn>).mockResolvedValueOnce({ success: true, message: 'Form submitted successfully!' });
render(<ContactForm />); const nameInput = screen.getByLabelText(/name/i); const emailInput = screen.getByLabelText(/email/i); const messageInput = screen.getByLabelText(/message/i); const submitButton = screen.getByRole('button', { name: /submit/i });
await userEvent.type(nameInput, 'John Doe'); await userEvent.type(emailInput, 'john.doe@example.com'); await userEvent.type(messageInput, 'This is a test message.'); expect(submitButton).toBeEnabled();
await userEvent.click(submitButton);
// Assert the API was called with the correct data await waitFor(() => { expect(submitContactForm).toHaveBeenCalledWith({ name: 'John Doe', email: 'john.doe@example.com', message: 'This is a test message.', }); });
// Assert success message is displayed and form fields are cleared expect(await screen.findByText(/form submitted successfully!/i)).toBeInTheDocument(); expect(screen.getByLabelText(/name/i)).toHaveValue(''); expect(screen.getByLabelText(/email/i)).toHaveValue(''); }); }); ```