import { describe, it, expect, vi, beforeEach } from 'vitest';
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import Button from '../components/ui/Button';
import Badge from '../components/ui/Badge';
import Card, { CardHeader, CardTitle, CardBody } from '../components/ui/Card';
import LoadingSpinner, { PageLoader } from '../components/ui/LoadingSpinner';
// ── Mock assessmentService before any import of useAssessment ─────────────────
// vi.mock is hoisted by Vitest, so this runs before module evaluation.
vi.mock('../services/assessmentService', () => ({
assessmentService: {
create: vi.fn().mockRejectedValue(new Error('Network error')),
getById: vi.fn().mockRejectedValue(new Error('Not found')),
getByUser: vi.fn().mockRejectedValue(new Error('User not found')),
},
}));
// ── Button Component ──────────────────────────────────────────────────────────
describe('Button Component', () => {
it('renders the button with children text', () => {
render();
const button = screen.getByRole('button', { name: /click me/i });
expect(button).toBeInTheDocument();
expect(button).not.toBeDisabled();
});
it('triggers onClick handler when clicked', () => {
const handleClick = vi.fn();
render();
const button = screen.getByRole('button', { name: /click me/i });
fireEvent.click(button);
expect(handleClick).toHaveBeenCalledTimes(1);
});
it('is disabled and shows spinner when loading', () => {
render();
const button = screen.getByRole('button');
expect(button).toBeDisabled();
expect(button).toHaveAttribute('aria-busy', 'true');
expect(screen.getByText(/submit/i)).toBeInTheDocument();
});
it('is disabled when disabled prop is true', () => {
render();
const button = screen.getByRole('button', { name: /submit/i });
expect(button).toBeDisabled();
});
it('applies variant classes correctly', () => {
const { rerender } = render();
expect(screen.getByRole('button')).toHaveClass('btn-secondary');
rerender();
expect(screen.getByRole('button')).toHaveClass('btn-primary');
});
it('does not trigger onClick when disabled', () => {
const handleClick = vi.fn();
render(
);
fireEvent.click(screen.getByRole('button'));
expect(handleClick).not.toHaveBeenCalled();
});
});
// ── Badge Component ──────────────────────────────────────────────────────────
describe('Badge Component', () => {
it('renders correctly with children', () => {
render(
with "Loading...", // so we use getAllByText and find the outer container via the
element. const loadingTexts = screen.getAllByText('Loading...'); // The outer container div has aria-live and aria-busy const container = loadingTexts[0].closest('[aria-live]'); expect(container).toHaveAttribute('aria-live', 'polite'); expect(container).toHaveAttribute('aria-busy', 'true'); }); }); // ── useAssessment error state tests ────────────────────────────────────────── describe('useAssessment error handling', () => { // These tests verify the hook properly surfaces API errors. // The assessmentService mock is hoisted at the top of this file. it('createAssessment surfaces error message on API failure', async () => { const { useAssessment } = await import('../hooks/useAssessment'); function TestComponent() { const { error, loading, createAssessment } = useAssessment(); return (