@amsterdam/design-system-react
Version:
All React components from the Amsterdam Design System. Use it to compose pages in your website or application.
157 lines (156 loc) • 7.69 kB
JavaScript
import { jsx as _jsx } from "react/jsx-runtime";
/**
* @license EUPL-1.2+
* Copyright Gemeente Amsterdam
*/
import { act, render } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { createRef } from 'react';
import { describe, expect, it, vi } from 'vitest';
import { ImageSlider } from './ImageSlider';
// Mock implementation of IntersectionObserver
window.IntersectionObserver = vi.fn(function () {
return {
disconnect: vi.fn(),
observe: vi.fn(),
root: null,
rootMargin: '',
takeRecords: vi.fn(),
thresholds: [],
unobserve: vi.fn(),
};
});
const scrollTo = vi.fn();
// Mock scrollTo
Element.prototype.scrollTo = scrollTo;
const images = [
{ alt: 'One', src: 'https://picsum.photos/id/122/320/180' },
{ alt: 'Two', src: 'https://picsum.photos/id/101/320/180' },
{ alt: 'Three', src: 'https://picsum.photos/id/153/320/180' },
];
// All functionality that relies on IntersectionObserver, like setting aria-hidden and showing an image
// by clicking on the thumbnail button, cannot be properly tested here because IntersectionObserver is not implemented in JSDom.
// These tests are covered in Storybook interaction tests: storybook/src/components/ImageSlider/ImageSlider.test.stories.tsx
// The window resize functionality is currently not tested anywhere, because this is difficult in both environments.
describe('ImageSlider', () => {
it('renders', () => {
const { container } = render(_jsx(ImageSlider, { images: images }));
const component = container.querySelector(':only-child');
expect(component).toBeInTheDocument();
expect(component).toBeVisible();
});
it('renders a design system BEM class name', () => {
const { container } = render(_jsx(ImageSlider, { images: images }));
const component = container.querySelector(':only-child');
expect(component).toHaveClass('ams-image-slider');
});
it('renders an extra class name', () => {
const { container } = render(_jsx(ImageSlider, { className: "extra", images: images }));
const component = container.querySelector(':only-child');
expect(component).toHaveClass('ams-image-slider extra');
});
it('supports ForwardRef in React', () => {
const ref = createRef();
const { container } = render(_jsx(ImageSlider, { images: images, ref: ref }));
const component = container.querySelector(':only-child');
expect(ref.current).toBe(component);
});
it('does not render controls by default', () => {
const { container } = render(_jsx(ImageSlider, { images: images }));
const controls = container.querySelector('.ams-image-slider__controls');
expect(controls).not.toBeInTheDocument();
});
it('renders controls when the controls prop is true', () => {
const { container } = render(_jsx(ImageSlider, { controls: true, images: images }));
const controls = container.querySelector('.ams-image-slider__controls');
expect(controls).toBeInTheDocument();
});
it('shows the first image by default', () => {
const { getByAltText } = render(_jsx(ImageSlider, { images: images }));
const firstImage = getByAltText('One');
const secondImage = getByAltText('Two');
const thirdImage = getByAltText('Three');
expect(firstImage).not.toHaveAttribute('aria-hidden', 'true');
expect(secondImage).toHaveAttribute('aria-hidden', 'true');
expect(thirdImage).toHaveAttribute('aria-hidden', 'true');
expect(firstImage.src).toBe('https://picsum.photos/id/122/320/180');
expect(secondImage.src).toBe('https://picsum.photos/id/101/320/180');
expect(thirdImage.src).toBe('https://picsum.photos/id/153/320/180');
});
it('scrolls to the next image when clicking the next button', async () => {
const user = userEvent.setup();
const { getByRole } = render(_jsx(ImageSlider, { controls: true, images: images }));
const nextButton = getByRole('button', { name: 'Volgende' });
const previousButton = getByRole('button', { name: 'Vorige' });
// At the start, previous button is disabled
expect(previousButton).toBeDisabled();
expect(nextButton).not.toBeDisabled();
expect(scrollTo).not.toHaveBeenCalled();
await user.click(nextButton);
await user.click(nextButton);
expect(scrollTo).toHaveBeenCalledTimes(2);
});
it('renders custom labels for next and previous buttons', () => {
const { getByRole } = render(_jsx(ImageSlider, { controls: true, images: images, nextLabel: "Next image", previousLabel: "Previous image" }));
const nextButton = getByRole('button', { name: 'Next image' });
const previousButton = getByRole('button', { name: 'Previous image' });
expect(nextButton).toBeInTheDocument();
expect(previousButton).toBeInTheDocument();
});
it('renders thumbnails', () => {
const { container } = render(_jsx(ImageSlider, { images: images }));
expect(container.querySelector('.ams-image-slider__thumbnails')).toBeInTheDocument();
});
it('does not render anything if there are no images', () => {
const { container } = render(_jsx(ImageSlider, { images: [] }));
expect(container).toBeEmptyDOMElement();
});
it('calls scrollToSlide when a thumbnail is clicked', async () => {
scrollTo.mockClear();
const user = userEvent.setup();
const { getAllByRole } = render(_jsx(ImageSlider, { images: images }));
const thumbnails = getAllByRole('tab');
const scrollToCallCountAfterRender = scrollTo.mock.calls.length;
await user.click(thumbnails[1]);
expect(scrollTo).toHaveBeenCalledTimes(scrollToCallCountAfterRender + 1);
});
it('fires the IntersectionObserver callback and enables the previous button', async () => {
let observerCallback;
const originalIntersectionObserver = window.IntersectionObserver;
window.IntersectionObserver = vi.fn(function (cb) {
observerCallback = cb;
return {
disconnect: vi.fn(),
observe: vi.fn(),
root: null,
rootMargin: '',
takeRecords: vi.fn(),
thresholds: [],
unobserve: vi.fn(),
};
});
try {
scrollTo.mockClear();
const user = userEvent.setup();
const { container, getByRole } = render(_jsx(ImageSlider, { controls: true, images: images }));
const scroller = container.querySelector('.ams-image-slider__scroller');
act(() => {
observerCallback([{ isIntersecting: true, target: scroller.children[1] }]);
});
const previousButton = getByRole('button', { name: 'Vorige' });
expect(previousButton).not.toBeDisabled();
await user.click(previousButton);
expect(scrollTo).toHaveBeenCalled();
}
finally {
window.IntersectionObserver = originalIntersectionObserver;
}
});
it('passes additional props', () => {
const { container } = render(_jsx(ImageSlider, { "aria-hidden": "false", "data-test": "data-test", id: "id", images: images }));
const component = container.querySelector(':only-child');
expect(component).toHaveAttribute('aria-hidden', 'false');
expect(component).toHaveAttribute('id', 'id');
expect(component).toHaveAttribute('data-test', 'data-test');
});
});