289 lines
9.1 KiB
TypeScript
289 lines
9.1 KiB
TypeScript
import { describe, it, expect, vi } from 'vitest';
|
|
import { render, screen, waitFor } from '@testing-library/react';
|
|
import userEvent from '@testing-library/user-event';
|
|
import { TrackListRow } from './TrackListRow';
|
|
import type { Track } from '../../player/types';
|
|
|
|
const mockTrack: Track = {
|
|
id: 1,
|
|
title: 'Test Track',
|
|
artist: 'Test Artist',
|
|
album: 'Test Album',
|
|
duration: 180,
|
|
url: 'https://example.com/track.mp3',
|
|
cover: 'https://example.com/cover.jpg',
|
|
genre: 'Rock',
|
|
};
|
|
|
|
describe('TrackListRow', () => {
|
|
const mockOnTrackClick = vi.fn();
|
|
const mockOnTrackPlay = vi.fn();
|
|
const mockOnTrackLike = vi.fn();
|
|
const mockOnTrackMore = vi.fn();
|
|
const mockOnTrackSelect = vi.fn();
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
it('should render track row', () => {
|
|
render(<TrackListRow track={mockTrack} />);
|
|
expect(screen.getByText('Test Track')).toBeInTheDocument();
|
|
});
|
|
|
|
it('should display track title', () => {
|
|
render(<TrackListRow track={mockTrack} />);
|
|
expect(screen.getByText('Test Track')).toBeInTheDocument();
|
|
});
|
|
|
|
it('should display track artist when showMetadata is true', () => {
|
|
render(<TrackListRow track={mockTrack} showMetadata={true} />);
|
|
// In list format, artist is displayed with album, so we check for text containing artist
|
|
expect(screen.getByText(/Test Artist/)).toBeInTheDocument();
|
|
});
|
|
|
|
it('should display track album when showMetadata is true', () => {
|
|
render(<TrackListRow track={mockTrack} showMetadata={true} />);
|
|
expect(screen.getByText(/Test Album/)).toBeInTheDocument();
|
|
});
|
|
|
|
it('should display track artist and album separately in table format', () => {
|
|
render(
|
|
<TrackListRow track={mockTrack} format="table" showMetadata={true} />,
|
|
);
|
|
expect(screen.getByText('Test Artist')).toBeInTheDocument();
|
|
expect(screen.getByText('Test Album')).toBeInTheDocument();
|
|
});
|
|
|
|
it('should display cover image when showCover is true', () => {
|
|
render(<TrackListRow track={mockTrack} showCover={true} />);
|
|
const image = screen.getByAltText('Cover de Test Track');
|
|
expect(image).toBeInTheDocument();
|
|
expect(image).toHaveAttribute('src', mockTrack.cover);
|
|
});
|
|
|
|
it('should display placeholder when cover is missing', () => {
|
|
const trackWithoutCover = { ...mockTrack, cover: undefined };
|
|
const { container } = render(
|
|
<TrackListRow track={trackWithoutCover} showCover={true} />,
|
|
);
|
|
const musicIcon = container.querySelector('svg');
|
|
expect(musicIcon).toBeInTheDocument();
|
|
});
|
|
|
|
it('should display duration when showDuration is true', () => {
|
|
render(<TrackListRow track={mockTrack} showDuration={true} />);
|
|
expect(screen.getByText('3:00')).toBeInTheDocument();
|
|
});
|
|
|
|
it('should call onTrackClick when row is clicked', async () => {
|
|
const user = userEvent.setup();
|
|
render(<TrackListRow track={mockTrack} onTrackClick={mockOnTrackClick} />);
|
|
|
|
const row = screen.getByRole('listitem');
|
|
await user.click(row);
|
|
|
|
expect(mockOnTrackClick).toHaveBeenCalledWith(mockTrack);
|
|
});
|
|
|
|
it('should call onTrackPlay when play button is clicked', async () => {
|
|
const user = userEvent.setup();
|
|
render(<TrackListRow track={mockTrack} onTrackPlay={mockOnTrackPlay} />);
|
|
|
|
const row = screen.getByRole('listitem');
|
|
await user.hover(row);
|
|
|
|
await waitFor(() => {
|
|
const playButtons = screen.queryAllByLabelText(/Lire/);
|
|
if (playButtons.length > 0) {
|
|
expect(playButtons[0]).toBeInTheDocument();
|
|
}
|
|
});
|
|
|
|
const playButtons = screen.queryAllByLabelText(/Lire/);
|
|
if (playButtons.length > 0) {
|
|
await user.click(playButtons[0]);
|
|
expect(mockOnTrackPlay).toHaveBeenCalledWith(mockTrack);
|
|
}
|
|
});
|
|
|
|
it('should call onTrackLike when like button is clicked', async () => {
|
|
const user = userEvent.setup();
|
|
render(<TrackListRow track={mockTrack} onTrackLike={mockOnTrackLike} />);
|
|
|
|
const row = screen.getByRole('listitem');
|
|
await user.hover(row);
|
|
|
|
await waitFor(() => {
|
|
const likeButtons = screen.queryAllByLabelText(/favoris/);
|
|
if (likeButtons.length > 0) {
|
|
expect(likeButtons[0]).toBeInTheDocument();
|
|
}
|
|
});
|
|
|
|
const likeButtons = screen.queryAllByLabelText(/Ajouter.*favoris/);
|
|
if (likeButtons.length > 0) {
|
|
await user.click(likeButtons[0]);
|
|
expect(mockOnTrackLike).toHaveBeenCalledWith(mockTrack);
|
|
}
|
|
});
|
|
|
|
it('should call onTrackMore when more button is clicked', async () => {
|
|
const user = userEvent.setup();
|
|
render(<TrackListRow track={mockTrack} onTrackMore={mockOnTrackMore} />);
|
|
|
|
const row = screen.getByRole('listitem');
|
|
await user.hover(row);
|
|
|
|
await waitFor(() => {
|
|
const moreButtons = screen.queryAllByLabelText(/Plus d'options/);
|
|
if (moreButtons.length > 0) {
|
|
expect(moreButtons[0]).toBeInTheDocument();
|
|
}
|
|
});
|
|
|
|
const moreButtons = screen.queryAllByLabelText(/Plus d'options/);
|
|
if (moreButtons.length > 0) {
|
|
await user.click(moreButtons[0]);
|
|
expect(mockOnTrackMore).toHaveBeenCalledWith(mockTrack);
|
|
}
|
|
});
|
|
|
|
it('should call onTrackSelect when checkbox is clicked', async () => {
|
|
const user = userEvent.setup();
|
|
render(
|
|
<TrackListRow
|
|
track={mockTrack}
|
|
showSelection={true}
|
|
onTrackSelect={mockOnTrackSelect}
|
|
/>,
|
|
);
|
|
|
|
const checkbox = screen.getByLabelText(/Sélectionner/);
|
|
await user.click(checkbox);
|
|
|
|
expect(mockOnTrackSelect).toHaveBeenCalledWith(mockTrack.id, true);
|
|
});
|
|
|
|
it('should display selected state when isSelected is true', () => {
|
|
const { container } = render(
|
|
<TrackListRow track={mockTrack} showSelection={true} isSelected={true} />,
|
|
);
|
|
const checkbox = screen.getByLabelText(/Sélectionner/);
|
|
expect(checkbox).toBeChecked();
|
|
});
|
|
|
|
it('should highlight selected row', () => {
|
|
const { container } = render(
|
|
<TrackListRow track={mockTrack} isSelected={true} />,
|
|
);
|
|
const row = container.querySelector('.bg-blue-50, .dark:bg-blue-900/20');
|
|
expect(row).toBeInTheDocument();
|
|
});
|
|
|
|
it('should display liked state when isLiked is true', () => {
|
|
render(
|
|
<TrackListRow
|
|
track={mockTrack}
|
|
onTrackLike={mockOnTrackLike}
|
|
isLiked={true}
|
|
/>,
|
|
);
|
|
const likedButtons = screen.queryAllByLabelText(/Retirer.*favoris/);
|
|
expect(likedButtons.length).toBeGreaterThanOrEqual(0);
|
|
});
|
|
|
|
it('should display playing state when isPlaying is true', () => {
|
|
render(
|
|
<TrackListRow
|
|
track={mockTrack}
|
|
onTrackPlay={mockOnTrackPlay}
|
|
isPlaying={true}
|
|
/>,
|
|
);
|
|
const pauseButtons = screen.queryAllByLabelText(/Mettre en pause/);
|
|
expect(pauseButtons.length).toBeGreaterThanOrEqual(0);
|
|
});
|
|
|
|
it('should not call onTrackClick when action button is clicked', async () => {
|
|
const user = userEvent.setup();
|
|
render(
|
|
<TrackListRow
|
|
track={mockTrack}
|
|
onTrackClick={mockOnTrackClick}
|
|
onTrackLike={mockOnTrackLike}
|
|
/>,
|
|
);
|
|
|
|
const row = screen.getByRole('listitem');
|
|
await user.hover(row);
|
|
|
|
await waitFor(() => {
|
|
const likeButtons = screen.queryAllByLabelText(/favoris/);
|
|
if (likeButtons.length > 0) {
|
|
expect(likeButtons[0]).toBeInTheDocument();
|
|
}
|
|
});
|
|
|
|
const likeButtons = screen.queryAllByLabelText(/Ajouter.*favoris/);
|
|
if (likeButtons.length > 0) {
|
|
await user.click(likeButtons[0]);
|
|
expect(mockOnTrackLike).toHaveBeenCalled();
|
|
expect(mockOnTrackClick).not.toHaveBeenCalled();
|
|
}
|
|
});
|
|
|
|
it('should render in table format when format is table', () => {
|
|
render(<TrackListRow track={mockTrack} format="table" />);
|
|
const row = screen.getByRole('row');
|
|
expect(row).toBeInTheDocument();
|
|
});
|
|
|
|
it('should render in list format when format is list', () => {
|
|
render(<TrackListRow track={mockTrack} format="list" />);
|
|
const row = screen.getByRole('listitem');
|
|
expect(row).toBeInTheDocument();
|
|
});
|
|
|
|
it('should apply custom className', () => {
|
|
const { container } = render(
|
|
<TrackListRow track={mockTrack} className="custom-class" />,
|
|
);
|
|
const row = container.querySelector('[role="listitem"]');
|
|
expect(row).toHaveClass('custom-class');
|
|
});
|
|
|
|
it('should have hover effects', async () => {
|
|
const user = userEvent.setup();
|
|
render(<TrackListRow track={mockTrack} onTrackPlay={mockOnTrackPlay} />);
|
|
|
|
const row = screen.getByRole('listitem');
|
|
await user.hover(row);
|
|
|
|
await waitFor(() => {
|
|
// Hover should trigger state change
|
|
expect(row).toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
it('should hide actions when showActions is false', () => {
|
|
render(
|
|
<TrackListRow
|
|
track={mockTrack}
|
|
showActions={false}
|
|
onTrackPlay={mockOnTrackPlay}
|
|
onTrackLike={mockOnTrackLike}
|
|
/>,
|
|
);
|
|
const playButtons = screen.queryAllByLabelText(/Lire/);
|
|
const likeButtons = screen.queryAllByLabelText(/favoris/);
|
|
expect(playButtons.length).toBe(0);
|
|
expect(likeButtons.length).toBe(0);
|
|
});
|
|
|
|
it('should handle track without artist', () => {
|
|
const trackWithoutArtist = { ...mockTrack, artist: undefined };
|
|
render(<TrackListRow track={trackWithoutArtist} showMetadata={true} />);
|
|
expect(screen.getByText('Test Track')).toBeInTheDocument();
|
|
});
|
|
});
|