Add comprehensive tests for: - Pagination: navigation, page numbers, disabled states - EmptyState: message, icon, title, action button - LoadingState: different types (page, card, table, list) - Modal: open/close, backdrop click, escape key, sizes 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
201 lines
5.4 KiB
TypeScript
201 lines
5.4 KiB
TypeScript
/**
|
|
* Tests for the Modal component
|
|
* @jest-environment jsdom
|
|
*/
|
|
|
|
import React from 'react';
|
|
import { render, screen, fireEvent } from '@testing-library/react';
|
|
import { Modal } from '@/components/ui/Modal';
|
|
|
|
describe('Modal', () => {
|
|
const mockOnClose = jest.fn();
|
|
|
|
beforeEach(() => {
|
|
jest.clearAllMocks();
|
|
});
|
|
|
|
it('should not render when isOpen is false', () => {
|
|
const { container } = render(
|
|
<Modal isOpen={false} onClose={mockOnClose}>
|
|
<div>Modal content</div>
|
|
</Modal>
|
|
);
|
|
expect(container.firstChild).toBeNull();
|
|
});
|
|
|
|
it('should render when isOpen is true', () => {
|
|
render(
|
|
<Modal isOpen={true} onClose={mockOnClose}>
|
|
<div>Modal content</div>
|
|
</Modal>
|
|
);
|
|
expect(screen.getByText('Modal content')).toBeInTheDocument();
|
|
});
|
|
|
|
it('should render title when provided', () => {
|
|
render(
|
|
<Modal isOpen={true} onClose={mockOnClose} title="Test Title">
|
|
<div>Content</div>
|
|
</Modal>
|
|
);
|
|
expect(screen.getByText('Test Title')).toBeInTheDocument();
|
|
});
|
|
|
|
it('should render close button by default', () => {
|
|
render(
|
|
<Modal isOpen={true} onClose={mockOnClose} title="Test">
|
|
<div>Content</div>
|
|
</Modal>
|
|
);
|
|
expect(screen.getByLabelText('Fermer')).toBeInTheDocument();
|
|
});
|
|
|
|
it('should not render close button when showCloseButton is false', () => {
|
|
render(
|
|
<Modal isOpen={true} onClose={mockOnClose} showCloseButton={false}>
|
|
<div>Content</div>
|
|
</Modal>
|
|
);
|
|
expect(screen.queryByLabelText('Fermer')).not.toBeInTheDocument();
|
|
});
|
|
|
|
it('should call onClose when close button is clicked', () => {
|
|
render(
|
|
<Modal isOpen={true} onClose={mockOnClose} title="Test">
|
|
<div>Content</div>
|
|
</Modal>
|
|
);
|
|
fireEvent.click(screen.getByLabelText('Fermer'));
|
|
expect(mockOnClose).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it('should call onClose when backdrop is clicked', () => {
|
|
render(
|
|
<Modal isOpen={true} onClose={mockOnClose}>
|
|
<div>Content</div>
|
|
</Modal>
|
|
);
|
|
// The dialog role is on the backdrop element itself
|
|
const backdrop = screen.getByRole('dialog');
|
|
fireEvent.click(backdrop);
|
|
expect(mockOnClose).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it('should not call onClose when modal content is clicked', () => {
|
|
render(
|
|
<Modal isOpen={true} onClose={mockOnClose}>
|
|
<div>Content</div>
|
|
</Modal>
|
|
);
|
|
fireEvent.click(screen.getByText('Content'));
|
|
expect(mockOnClose).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('should call onClose when Escape key is pressed', () => {
|
|
render(
|
|
<Modal isOpen={true} onClose={mockOnClose}>
|
|
<div>Content</div>
|
|
</Modal>
|
|
);
|
|
fireEvent.keyDown(document, { key: 'Escape' });
|
|
expect(mockOnClose).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it('should have dialog role', () => {
|
|
render(
|
|
<Modal isOpen={true} onClose={mockOnClose}>
|
|
<div>Content</div>
|
|
</Modal>
|
|
);
|
|
expect(screen.getByRole('dialog')).toBeInTheDocument();
|
|
});
|
|
|
|
it('should have aria-modal attribute', () => {
|
|
render(
|
|
<Modal isOpen={true} onClose={mockOnClose}>
|
|
<div>Content</div>
|
|
</Modal>
|
|
);
|
|
expect(screen.getByRole('dialog')).toHaveAttribute('aria-modal', 'true');
|
|
});
|
|
|
|
it('should apply sm size class', () => {
|
|
const { container } = render(
|
|
<Modal isOpen={true} onClose={mockOnClose} size="sm">
|
|
<div>Content</div>
|
|
</Modal>
|
|
);
|
|
expect(container.querySelector('.max-w-md')).toBeInTheDocument();
|
|
});
|
|
|
|
it('should apply md size class by default', () => {
|
|
const { container } = render(
|
|
<Modal isOpen={true} onClose={mockOnClose}>
|
|
<div>Content</div>
|
|
</Modal>
|
|
);
|
|
expect(container.querySelector('.max-w-lg')).toBeInTheDocument();
|
|
});
|
|
|
|
it('should apply lg size class', () => {
|
|
const { container } = render(
|
|
<Modal isOpen={true} onClose={mockOnClose} size="lg">
|
|
<div>Content</div>
|
|
</Modal>
|
|
);
|
|
expect(container.querySelector('.max-w-2xl')).toBeInTheDocument();
|
|
});
|
|
|
|
it('should apply xl size class', () => {
|
|
const { container } = render(
|
|
<Modal isOpen={true} onClose={mockOnClose} size="xl">
|
|
<div>Content</div>
|
|
</Modal>
|
|
);
|
|
expect(container.querySelector('.max-w-4xl')).toBeInTheDocument();
|
|
});
|
|
|
|
it('should set body overflow to hidden when open', () => {
|
|
render(
|
|
<Modal isOpen={true} onClose={mockOnClose}>
|
|
<div>Content</div>
|
|
</Modal>
|
|
);
|
|
expect(document.body.style.overflow).toBe('hidden');
|
|
});
|
|
|
|
it('should reset body overflow when closed', () => {
|
|
const { rerender } = render(
|
|
<Modal isOpen={true} onClose={mockOnClose}>
|
|
<div>Content</div>
|
|
</Modal>
|
|
);
|
|
rerender(
|
|
<Modal isOpen={false} onClose={mockOnClose}>
|
|
<div>Content</div>
|
|
</Modal>
|
|
);
|
|
expect(document.body.style.overflow).toBe('unset');
|
|
});
|
|
|
|
it('should have aria-labelledby when title is provided', () => {
|
|
render(
|
|
<Modal isOpen={true} onClose={mockOnClose} title="My Modal">
|
|
<div>Content</div>
|
|
</Modal>
|
|
);
|
|
const dialog = screen.getByRole('dialog');
|
|
expect(dialog).toHaveAttribute('aria-labelledby', 'modal-title');
|
|
});
|
|
|
|
it('should not have aria-labelledby when title is not provided', () => {
|
|
render(
|
|
<Modal isOpen={true} onClose={mockOnClose}>
|
|
<div>Content</div>
|
|
</Modal>
|
|
);
|
|
const dialog = screen.getByRole('dialog');
|
|
expect(dialog).not.toHaveAttribute('aria-labelledby');
|
|
});
|
|
});
|