veza/apps/web/src/features/player/components/NextPreviousButtons.test.tsx
2026-02-11 22:19:24 +01:00

248 lines
6.6 KiB
TypeScript

import { describe, it, expect, vi, beforeEach } from 'vitest';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { NextPreviousButtons } from './NextPreviousButtons';
describe('NextPreviousButtons', () => {
const mockOnNext = vi.fn();
const mockOnPrevious = vi.fn();
beforeEach(() => {
vi.clearAllMocks();
});
it('should render both buttons', () => {
render(
<NextPreviousButtons
onNext={mockOnNext}
onPrevious={mockOnPrevious}
canGoNext={true}
canGoPrevious={true}
/>,
);
expect(screen.getByLabelText('Piste précédente')).toBeInTheDocument();
expect(screen.getByLabelText('Piste suivante')).toBeInTheDocument();
});
it('should call onPrevious when previous button is clicked', async () => {
const user = userEvent.setup();
render(
<NextPreviousButtons
onNext={mockOnNext}
onPrevious={mockOnPrevious}
canGoNext={true}
canGoPrevious={true}
/>,
);
const previousButton = screen.getByLabelText('Piste précédente');
await user.click(previousButton);
expect(mockOnPrevious).toHaveBeenCalledTimes(1);
expect(mockOnNext).not.toHaveBeenCalled();
});
it('should call onNext when next button is clicked', async () => {
const user = userEvent.setup();
render(
<NextPreviousButtons
onNext={mockOnNext}
onPrevious={mockOnPrevious}
canGoNext={true}
canGoPrevious={true}
/>,
);
const nextButton = screen.getByLabelText('Piste suivante');
await user.click(nextButton);
expect(mockOnNext).toHaveBeenCalledTimes(1);
expect(mockOnPrevious).not.toHaveBeenCalled();
});
it('should disable previous button when canGoPrevious is false', () => {
render(
<NextPreviousButtons
onNext={mockOnNext}
onPrevious={mockOnPrevious}
canGoNext={true}
canGoPrevious={false}
/>,
);
const previousButton = screen.getByLabelText('Piste précédente');
expect(previousButton).toBeDisabled();
expect(previousButton).toHaveAttribute('aria-disabled', 'true');
});
it('should disable next button when canGoNext is false', () => {
render(
<NextPreviousButtons
onNext={mockOnNext}
onPrevious={mockOnPrevious}
canGoNext={false}
canGoPrevious={true}
/>,
);
const nextButton = screen.getByLabelText('Piste suivante');
expect(nextButton).toBeDisabled();
expect(nextButton).toHaveAttribute('aria-disabled', 'true');
});
it('should disable both buttons when disabled prop is true', () => {
render(
<NextPreviousButtons
onNext={mockOnNext}
onPrevious={mockOnPrevious}
canGoNext={true}
canGoPrevious={true}
disabled={true}
/>,
);
const previousButton = screen.getByLabelText('Piste précédente');
const nextButton = screen.getByLabelText('Piste suivante');
expect(previousButton).toBeDisabled();
expect(nextButton).toBeDisabled();
});
it('should not call callbacks when buttons are disabled', async () => {
const user = userEvent.setup();
render(
<NextPreviousButtons
onNext={mockOnNext}
onPrevious={mockOnPrevious}
canGoNext={false}
canGoPrevious={false}
/>,
);
const previousButton = screen.getByLabelText('Piste précédente');
const nextButton = screen.getByLabelText('Piste suivante');
await user.click(previousButton);
await user.click(nextButton);
expect(mockOnPrevious).not.toHaveBeenCalled();
expect(mockOnNext).not.toHaveBeenCalled();
});
it('should apply custom className', () => {
const { container } = render(
<NextPreviousButtons
onNext={mockOnNext}
onPrevious={mockOnPrevious}
canGoNext={true}
canGoPrevious={true}
className="custom-class"
/>,
);
expect(container.firstChild).toHaveClass('custom-class');
});
it('should apply size classes', () => {
const { container: smContainer } = render(
<NextPreviousButtons
onNext={mockOnNext}
onPrevious={mockOnPrevious}
canGoNext={true}
canGoPrevious={true}
size="sm"
/>,
);
const buttons = smContainer.querySelectorAll('button');
buttons.forEach((button) => {
expect(button).toHaveClass('h-8', 'w-8');
});
const { container: mdContainer } = render(
<NextPreviousButtons
onNext={mockOnNext}
onPrevious={mockOnPrevious}
canGoNext={true}
canGoPrevious={true}
size="md"
/>,
);
const mdButtons = mdContainer.querySelectorAll('button');
mdButtons.forEach((button) => {
expect(button).toHaveClass('h-10', 'w-10');
});
const { container: lgContainer } = render(
<NextPreviousButtons
onNext={mockOnNext}
onPrevious={mockOnPrevious}
canGoNext={true}
canGoPrevious={true}
size="lg"
/>,
);
const lgButtons = lgContainer.querySelectorAll('button');
lgButtons.forEach((button) => {
expect(button).toHaveClass('h-12', 'w-12');
});
});
it('should apply variant classes', () => {
const { container: defaultContainer } = render(
<NextPreviousButtons
onNext={mockOnNext}
onPrevious={mockOnPrevious}
canGoNext={true}
canGoPrevious={true}
variant="default"
/>,
);
const buttons = defaultContainer.querySelectorAll('button');
buttons.forEach((button) => {
expect(button).toHaveClass('bg-primary');
});
const { container: ghostContainer } = render(
<NextPreviousButtons
onNext={mockOnNext}
onPrevious={mockOnPrevious}
canGoNext={true}
canGoPrevious={true}
variant="ghost"
/>,
);
const ghostButtons = ghostContainer.querySelectorAll('button');
ghostButtons.forEach((button) => {
expect(button).toHaveClass('bg-transparent');
});
});
it('should have accessible labels', () => {
render(
<NextPreviousButtons
onNext={mockOnNext}
onPrevious={mockOnPrevious}
canGoNext={true}
canGoPrevious={true}
/>,
);
expect(screen.getByLabelText('Piste précédente')).toBeInTheDocument();
expect(screen.getByLabelText('Piste suivante')).toBeInTheDocument();
expect(
screen.getAllByText('Piste précédente', { selector: '.sr-only' }),
).toHaveLength(1);
expect(
screen.getAllByText('Piste suivante', { selector: '.sr-only' }),
).toHaveLength(1);
});
});