veza/apps/web/src/components/ui/ImageViewerModal.test.tsx

196 lines
4.6 KiB
TypeScript
Raw Normal View History

import { render, screen, fireEvent } from '@testing-library/react';
import { describe, it, expect, vi } from 'vitest';
import { ImageViewerModal } from './ImageViewerModal';
describe('ImageViewerModal Component', () => {
const mockOnClose = vi.fn();
const mockOnNext = vi.fn();
const mockOnPrev = vi.fn();
beforeEach(() => {
vi.clearAllMocks();
});
it('renders image', () => {
render(
<ImageViewerModal
src="test.jpg"
alt="Test image"
onClose={mockOnClose}
/>,
);
const image = screen.getByAltText('Test image');
expect(image).toBeInTheDocument();
expect(image).toHaveAttribute('src', 'test.jpg');
});
it('calls onClose when close button is clicked', () => {
render(
<ImageViewerModal src="test.jpg" alt="Test" onClose={mockOnClose} />,
);
const closeButton = screen.getByTitle('Close');
fireEvent.click(closeButton);
expect(mockOnClose).toHaveBeenCalled();
});
it('calls onClose when Escape key is pressed', () => {
render(
<ImageViewerModal src="test.jpg" alt="Test" onClose={mockOnClose} />,
);
fireEvent.keyDown(document, { key: 'Escape' });
expect(mockOnClose).toHaveBeenCalled();
});
it('calls onNext when ArrowRight key is pressed', () => {
render(
<ImageViewerModal
src="test.jpg"
alt="Test"
onClose={mockOnClose}
onNext={mockOnNext}
hasNext={true}
/>,
);
fireEvent.keyDown(document, { key: 'ArrowRight' });
expect(mockOnNext).toHaveBeenCalled();
});
it('calls onPrev when ArrowLeft key is pressed', () => {
render(
<ImageViewerModal
src="test.jpg"
alt="Test"
onClose={mockOnClose}
onPrev={mockOnPrev}
hasPrev={true}
/>,
);
fireEvent.keyDown(document, { key: 'ArrowLeft' });
expect(mockOnPrev).toHaveBeenCalled();
});
it('does not call onNext on ArrowRight when hasNext is false', () => {
render(
<ImageViewerModal
src="test.jpg"
alt="Test"
onClose={mockOnClose}
onNext={mockOnNext}
hasNext={false}
/>,
);
fireEvent.keyDown(document, { key: 'ArrowRight' });
expect(mockOnNext).not.toHaveBeenCalled();
});
it('shows next button when hasNext is true', () => {
render(
<ImageViewerModal
src="test.jpg"
alt="Test"
onClose={mockOnClose}
onNext={mockOnNext}
hasNext={true}
/>,
);
const nextButton = screen.getByLabelText('Next image');
expect(nextButton).toBeInTheDocument();
});
it('calls onNext when next button is clicked', () => {
render(
<ImageViewerModal
src="test.jpg"
alt="Test"
onClose={mockOnClose}
onNext={mockOnNext}
hasNext={true}
/>,
);
const nextButton = screen.getByLabelText('Next image');
fireEvent.click(nextButton);
expect(mockOnNext).toHaveBeenCalled();
});
it('shows prev button when hasPrev is true', () => {
render(
<ImageViewerModal
src="test.jpg"
alt="Test"
onClose={mockOnClose}
onPrev={mockOnPrev}
hasPrev={true}
/>,
);
const prevButton = screen.getByLabelText('Previous image');
expect(prevButton).toBeInTheDocument();
});
it('uses default alt text when alt is not provided', () => {
render(<ImageViewerModal src="test.jpg" onClose={mockOnClose} />);
expect(screen.getByText('Image Preview')).toBeInTheDocument();
});
it('shows image counter when currentIndex and totalImages are provided', () => {
render(
<ImageViewerModal
src="test.jpg"
alt="Test"
onClose={mockOnClose}
currentIndex={2}
totalImages={5}
/>,
);
expect(screen.getByText('2 / 5')).toBeInTheDocument();
});
it('does not show image counter for single images', () => {
render(
<ImageViewerModal
src="test.jpg"
alt="Test"
onClose={mockOnClose}
currentIndex={1}
totalImages={1}
/>,
);
expect(screen.queryByText('1 / 1')).not.toBeInTheDocument();
});
it('toggles zoom on image click', () => {
render(
<ImageViewerModal src="test.jpg" alt="Test" onClose={mockOnClose} />,
);
const image = screen.getByAltText('Test');
// Initially in fit mode (cursor-zoom-in)
expect(image.className).toContain('cursor-zoom-in');
// Click to zoom
fireEvent.click(image);
expect(image.className).toContain('cursor-zoom-out');
// Click to unzoom
fireEvent.click(image);
expect(image.className).toContain('cursor-zoom-in');
});
});