Feature/image dialog #8
@@ -0,0 +1,68 @@
|
|||||||
|
import { fireEvent, render, screen } from '@testing-library/react';
|
||||||
|
import { ImageLightbox } from './ImageLightbox';
|
||||||
|
|
||||||
|
// jsdom does not implement dialog methods — mock them
|
||||||
|
beforeAll(() => {
|
||||||
|
HTMLDialogElement.prototype.showModal = vi.fn();
|
||||||
|
HTMLDialogElement.prototype.close = vi.fn();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
const DEFAULT_PROPS = { src: '/project.jpg', alt: 'My Project' };
|
||||||
|
|
||||||
|
describe('ImageLightbox', () => {
|
||||||
|
describe('thumbnail', () => {
|
||||||
|
it('renders a thumbnail image', () => {
|
||||||
|
render(<ImageLightbox {...DEFAULT_PROPS} />);
|
||||||
|
expect(screen.getByRole('img', { name: 'My Project' })).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('thumbnail button has cursor-zoom-in', () => {
|
||||||
|
render(<ImageLightbox {...DEFAULT_PROPS} />);
|
||||||
|
const btn = screen.getByRole('button', { name: 'My Project' });
|
||||||
|
expect(btn).toHaveClass('cursor-zoom-in');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('forwards className to the thumbnail button', () => {
|
||||||
|
render(<ImageLightbox {...DEFAULT_PROPS} className="extra-class" />);
|
||||||
|
expect(screen.getByRole('button', { name: 'My Project' })).toHaveClass('extra-class');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('dialog', () => {
|
||||||
|
it('clicking the thumbnail opens the dialog', () => {
|
||||||
|
render(<ImageLightbox {...DEFAULT_PROPS} />);
|
||||||
|
fireEvent.click(screen.getByRole('button', { name: 'My Project' }));
|
||||||
|
expect(HTMLDialogElement.prototype.showModal).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('clicking the close button closes the dialog', () => {
|
||||||
|
render(<ImageLightbox {...DEFAULT_PROPS} />);
|
||||||
|
fireEvent.click(screen.getByRole('button', { name: /close/i }));
|
||||||
|
expect(HTMLDialogElement.prototype.close).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('clicking the backdrop (dialog element itself) closes the dialog', () => {
|
||||||
|
render(<ImageLightbox {...DEFAULT_PROPS} />);
|
||||||
|
const dialog = document.querySelector('dialog')!;
|
||||||
|
fireEvent.click(dialog, { target: dialog });
|
||||||
|
expect(HTMLDialogElement.prototype.close).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('clicking inside the dialog (not backdrop) does not close', () => {
|
||||||
|
render(<ImageLightbox {...DEFAULT_PROPS} />);
|
||||||
|
const dialog = document.querySelector('dialog')!;
|
||||||
|
const inner = dialog.querySelector('div')!;
|
||||||
|
fireEvent.click(inner);
|
||||||
|
expect(HTMLDialogElement.prototype.close).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('dialog has accessible label matching alt text', () => {
|
||||||
|
render(<ImageLightbox {...DEFAULT_PROPS} />);
|
||||||
|
expect(document.querySelector('dialog')).toHaveAttribute('aria-label', 'My Project');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user