342 lines
9.6 KiB
TypeScript
342 lines
9.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 { RepeatShuffleButtons } from './RepeatShuffleButtons';
|
|
|
|
describe('RepeatShuffleButtons', () => {
|
|
const mockOnRepeatChange = vi.fn();
|
|
const mockOnShuffleToggle = vi.fn();
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
it('should render both buttons', () => {
|
|
render(
|
|
<RepeatShuffleButtons
|
|
repeat="off"
|
|
shuffle={false}
|
|
onRepeatChange={mockOnRepeatChange}
|
|
onShuffleToggle={mockOnShuffleToggle}
|
|
/>,
|
|
);
|
|
|
|
expect(screen.getByLabelText('Répéter désactivé')).toBeInTheDocument();
|
|
expect(screen.getByLabelText('Mélanger désactivé')).toBeInTheDocument();
|
|
});
|
|
|
|
it('should call onRepeatChange when repeat button is clicked', async () => {
|
|
const user = userEvent.setup();
|
|
|
|
render(
|
|
<RepeatShuffleButtons
|
|
repeat="off"
|
|
shuffle={false}
|
|
onRepeatChange={mockOnRepeatChange}
|
|
onShuffleToggle={mockOnShuffleToggle}
|
|
/>,
|
|
);
|
|
|
|
const repeatButton = screen.getByLabelText('Répéter désactivé');
|
|
await user.click(repeatButton);
|
|
|
|
expect(mockOnRepeatChange).toHaveBeenCalledWith('track');
|
|
expect(mockOnShuffleToggle).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('should cycle through repeat modes: off -> track -> playlist -> off', async () => {
|
|
const user = userEvent.setup();
|
|
|
|
const { rerender } = render(
|
|
<RepeatShuffleButtons
|
|
repeat="off"
|
|
shuffle={false}
|
|
onRepeatChange={mockOnRepeatChange}
|
|
onShuffleToggle={mockOnShuffleToggle}
|
|
/>,
|
|
);
|
|
|
|
const repeatButton = screen.getByLabelText('Répéter désactivé');
|
|
|
|
// off -> track
|
|
await user.click(repeatButton);
|
|
expect(mockOnRepeatChange).toHaveBeenCalledWith('track');
|
|
|
|
// track -> playlist
|
|
rerender(
|
|
<RepeatShuffleButtons
|
|
repeat="track"
|
|
shuffle={false}
|
|
onRepeatChange={mockOnRepeatChange}
|
|
onShuffleToggle={mockOnShuffleToggle}
|
|
/>,
|
|
);
|
|
await user.click(screen.getByLabelText('Répéter la piste (actif)'));
|
|
expect(mockOnRepeatChange).toHaveBeenCalledWith('playlist');
|
|
|
|
// playlist -> off
|
|
rerender(
|
|
<RepeatShuffleButtons
|
|
repeat="playlist"
|
|
shuffle={false}
|
|
onRepeatChange={mockOnRepeatChange}
|
|
onShuffleToggle={mockOnShuffleToggle}
|
|
/>,
|
|
);
|
|
await user.click(screen.getByLabelText('Répéter la playlist (actif)'));
|
|
expect(mockOnRepeatChange).toHaveBeenCalledWith('off');
|
|
});
|
|
|
|
it('should call onShuffleToggle when shuffle button is clicked', async () => {
|
|
const user = userEvent.setup();
|
|
|
|
render(
|
|
<RepeatShuffleButtons
|
|
repeat="off"
|
|
shuffle={false}
|
|
onRepeatChange={mockOnRepeatChange}
|
|
onShuffleToggle={mockOnShuffleToggle}
|
|
/>,
|
|
);
|
|
|
|
const shuffleButton = screen.getByLabelText('Mélanger désactivé');
|
|
await user.click(shuffleButton);
|
|
|
|
expect(mockOnShuffleToggle).toHaveBeenCalledTimes(1);
|
|
expect(mockOnRepeatChange).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('should display active state for repeat when repeat is track', () => {
|
|
render(
|
|
<RepeatShuffleButtons
|
|
repeat="track"
|
|
shuffle={false}
|
|
onRepeatChange={mockOnRepeatChange}
|
|
onShuffleToggle={mockOnShuffleToggle}
|
|
/>,
|
|
);
|
|
|
|
const repeatButton = screen.getByLabelText('Répéter la piste (actif)');
|
|
expect(repeatButton).toHaveAttribute('aria-pressed', 'true');
|
|
expect(repeatButton).toHaveClass('bg-blue-600');
|
|
});
|
|
|
|
it('should display active state for repeat when repeat is playlist', () => {
|
|
render(
|
|
<RepeatShuffleButtons
|
|
repeat="playlist"
|
|
shuffle={false}
|
|
onRepeatChange={mockOnRepeatChange}
|
|
onShuffleToggle={mockOnShuffleToggle}
|
|
/>,
|
|
);
|
|
|
|
const repeatButton = screen.getByLabelText('Répéter la playlist (actif)');
|
|
expect(repeatButton).toHaveAttribute('aria-pressed', 'true');
|
|
expect(repeatButton).toHaveClass('bg-blue-600');
|
|
});
|
|
|
|
it('should display active state for shuffle when shuffle is true', () => {
|
|
render(
|
|
<RepeatShuffleButtons
|
|
repeat="off"
|
|
shuffle={true}
|
|
onRepeatChange={mockOnRepeatChange}
|
|
onShuffleToggle={mockOnShuffleToggle}
|
|
/>,
|
|
);
|
|
|
|
const shuffleButton = screen.getByLabelText('Mélanger activé');
|
|
expect(shuffleButton).toHaveAttribute('aria-pressed', 'true');
|
|
expect(shuffleButton).toHaveClass('bg-blue-600');
|
|
});
|
|
|
|
it('should disable buttons when disabled prop is true', () => {
|
|
render(
|
|
<RepeatShuffleButtons
|
|
repeat="off"
|
|
shuffle={false}
|
|
onRepeatChange={mockOnRepeatChange}
|
|
onShuffleToggle={mockOnShuffleToggle}
|
|
disabled={true}
|
|
/>,
|
|
);
|
|
|
|
const repeatButton = screen.getByLabelText('Répéter désactivé');
|
|
const shuffleButton = screen.getByLabelText('Mélanger désactivé');
|
|
|
|
expect(repeatButton).toBeDisabled();
|
|
expect(shuffleButton).toBeDisabled();
|
|
expect(repeatButton).toHaveAttribute('aria-disabled', 'true');
|
|
expect(shuffleButton).toHaveAttribute('aria-disabled', 'true');
|
|
});
|
|
|
|
it('should not call callbacks when buttons are disabled', async () => {
|
|
const user = userEvent.setup();
|
|
|
|
render(
|
|
<RepeatShuffleButtons
|
|
repeat="off"
|
|
shuffle={false}
|
|
onRepeatChange={mockOnRepeatChange}
|
|
onShuffleToggle={mockOnShuffleToggle}
|
|
disabled={true}
|
|
/>,
|
|
);
|
|
|
|
const repeatButton = screen.getByLabelText('Répéter désactivé');
|
|
const shuffleButton = screen.getByLabelText('Mélanger désactivé');
|
|
|
|
await user.click(repeatButton);
|
|
await user.click(shuffleButton);
|
|
|
|
expect(mockOnRepeatChange).not.toHaveBeenCalled();
|
|
expect(mockOnShuffleToggle).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('should apply custom className', () => {
|
|
const { container } = render(
|
|
<RepeatShuffleButtons
|
|
repeat="off"
|
|
shuffle={false}
|
|
onRepeatChange={mockOnRepeatChange}
|
|
onShuffleToggle={mockOnShuffleToggle}
|
|
className="custom-class"
|
|
/>,
|
|
);
|
|
|
|
expect(container.firstChild).toHaveClass('custom-class');
|
|
});
|
|
|
|
it('should apply size classes', () => {
|
|
const { container: smContainer } = render(
|
|
<RepeatShuffleButtons
|
|
repeat="off"
|
|
shuffle={false}
|
|
onRepeatChange={mockOnRepeatChange}
|
|
onShuffleToggle={mockOnShuffleToggle}
|
|
size="sm"
|
|
/>,
|
|
);
|
|
|
|
const buttons = smContainer.querySelectorAll('button');
|
|
buttons.forEach((button) => {
|
|
expect(button).toHaveClass('h-8', 'w-8');
|
|
});
|
|
|
|
const { container: mdContainer } = render(
|
|
<RepeatShuffleButtons
|
|
repeat="off"
|
|
shuffle={false}
|
|
onRepeatChange={mockOnRepeatChange}
|
|
onShuffleToggle={mockOnShuffleToggle}
|
|
size="md"
|
|
/>,
|
|
);
|
|
|
|
const mdButtons = mdContainer.querySelectorAll('button');
|
|
mdButtons.forEach((button) => {
|
|
expect(button).toHaveClass('h-10', 'w-10');
|
|
});
|
|
|
|
const { container: lgContainer } = render(
|
|
<RepeatShuffleButtons
|
|
repeat="off"
|
|
shuffle={false}
|
|
onRepeatChange={mockOnRepeatChange}
|
|
onShuffleToggle={mockOnShuffleToggle}
|
|
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(
|
|
<RepeatShuffleButtons
|
|
repeat="off"
|
|
shuffle={false}
|
|
onRepeatChange={mockOnRepeatChange}
|
|
onShuffleToggle={mockOnShuffleToggle}
|
|
variant="default"
|
|
/>,
|
|
);
|
|
|
|
const buttons = defaultContainer.querySelectorAll('button');
|
|
buttons.forEach((button) => {
|
|
expect(button).toHaveClass('bg-blue-600');
|
|
});
|
|
|
|
const { container: ghostContainer } = render(
|
|
<RepeatShuffleButtons
|
|
repeat="off"
|
|
shuffle={false}
|
|
onRepeatChange={mockOnRepeatChange}
|
|
onShuffleToggle={mockOnShuffleToggle}
|
|
variant="ghost"
|
|
/>,
|
|
);
|
|
|
|
const ghostButtons = ghostContainer.querySelectorAll('button');
|
|
ghostButtons.forEach((button) => {
|
|
expect(button).toHaveClass('bg-transparent');
|
|
});
|
|
});
|
|
|
|
it('should have accessible labels', () => {
|
|
render(
|
|
<RepeatShuffleButtons
|
|
repeat="off"
|
|
shuffle={false}
|
|
onRepeatChange={mockOnRepeatChange}
|
|
onShuffleToggle={mockOnShuffleToggle}
|
|
/>,
|
|
);
|
|
|
|
expect(screen.getByLabelText('Répéter désactivé')).toBeInTheDocument();
|
|
expect(screen.getByLabelText('Mélanger désactivé')).toBeInTheDocument();
|
|
});
|
|
|
|
it('should show correct labels for different repeat states', () => {
|
|
const { rerender } = render(
|
|
<RepeatShuffleButtons
|
|
repeat="off"
|
|
shuffle={false}
|
|
onRepeatChange={mockOnRepeatChange}
|
|
onShuffleToggle={mockOnShuffleToggle}
|
|
/>,
|
|
);
|
|
|
|
expect(screen.getByLabelText('Répéter désactivé')).toBeInTheDocument();
|
|
|
|
rerender(
|
|
<RepeatShuffleButtons
|
|
repeat="track"
|
|
shuffle={false}
|
|
onRepeatChange={mockOnRepeatChange}
|
|
onShuffleToggle={mockOnShuffleToggle}
|
|
/>,
|
|
);
|
|
|
|
expect(
|
|
screen.getByLabelText('Répéter la piste (actif)'),
|
|
).toBeInTheDocument();
|
|
|
|
rerender(
|
|
<RepeatShuffleButtons
|
|
repeat="playlist"
|
|
shuffle={false}
|
|
onRepeatChange={mockOnRepeatChange}
|
|
onShuffleToggle={mockOnShuffleToggle}
|
|
/>,
|
|
);
|
|
|
|
expect(
|
|
screen.getByLabelText('Répéter la playlist (actif)'),
|
|
).toBeInTheDocument();
|
|
});
|
|
});
|