veza/apps/web/src/features/tracks/components/TrackDownloadButton.test.tsx
2025-12-12 21:34:34 -05:00

218 lines
6.2 KiB
TypeScript

import { describe, it, expect, vi, beforeEach } from 'vitest';
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { TrackDownloadButton } from './TrackDownloadButton';
import {
downloadTrack,
TrackDownloadError,
} from '../services/trackDownloadService';
import { useToast } from '@/hooks/useToast';
// Mock dependencies
vi.mock('../services/trackDownloadService');
vi.mock('@/hooks/useToast');
describe('TrackDownloadButton', () => {
const mockToast = {
success: vi.fn(),
error: vi.fn(),
warning: vi.fn(),
info: vi.fn(),
toast: vi.fn(),
};
beforeEach(() => {
vi.clearAllMocks();
vi.mocked(useToast).mockReturnValue(mockToast);
});
it('should render download button', () => {
render(<TrackDownloadButton trackId={123} />);
expect(screen.getByText('Télécharger')).toBeInTheDocument();
expect(screen.getByRole('button')).toBeInTheDocument();
});
it('should download track when button is clicked', async () => {
vi.mocked(downloadTrack).mockResolvedValue(undefined);
render(<TrackDownloadButton trackId={123} />);
const button = screen.getByRole('button');
await userEvent.click(button);
await waitFor(() => {
expect(downloadTrack).toHaveBeenCalledWith(123, {
shareToken: undefined,
filename: undefined,
onProgress: undefined,
});
});
expect(mockToast.success).toHaveBeenCalledWith('Téléchargement terminé');
});
it('should download track with share token', async () => {
vi.mocked(downloadTrack).mockResolvedValue(undefined);
render(<TrackDownloadButton trackId={123} shareToken="test-token" />);
const button = screen.getByRole('button');
await userEvent.click(button);
await waitFor(() => {
expect(downloadTrack).toHaveBeenCalledWith(123, {
shareToken: 'test-token',
filename: undefined,
onProgress: undefined,
});
});
});
it('should show loading state during download', async () => {
let resolveDownload: () => void;
const downloadPromise = new Promise<void>((resolve) => {
resolveDownload = resolve;
});
vi.mocked(downloadTrack).mockReturnValue(downloadPromise);
render(<TrackDownloadButton trackId={123} />);
const button = screen.getByRole('button');
await userEvent.click(button);
await waitFor(() => {
expect(screen.getByText(/Téléchargement.../i)).toBeInTheDocument();
});
expect(button).toBeDisabled();
// Résoudre le téléchargement
resolveDownload!();
await waitFor(() => {
expect(screen.getByText('Télécharger')).toBeInTheDocument();
});
});
it('should show progress bar when showProgress is true', async () => {
let progressCallback: (progress: number) => void;
const downloadPromise = new Promise<void>((resolve) => {
vi.mocked(downloadTrack).mockImplementation((id, options) => {
if (options?.onProgress) {
progressCallback = options.onProgress;
}
setTimeout(() => resolve(), 100);
return Promise.resolve();
});
});
render(<TrackDownloadButton trackId={123} showProgress={true} />);
const button = screen.getByRole('button');
await userEvent.click(button);
await waitFor(() => {
expect(screen.getByRole('progressbar')).toBeInTheDocument();
});
// Simuler la progression
if (progressCallback!) {
progressCallback(50);
await waitFor(() => {
expect(screen.getByText(/50%/i)).toBeInTheDocument();
});
}
});
it('should display error message on download failure', async () => {
const error = new TrackDownloadError('Download failed', 'NETWORK', true);
vi.mocked(downloadTrack).mockRejectedValue(error);
render(<TrackDownloadButton trackId={123} />);
const button = screen.getByRole('button');
await userEvent.click(button);
await waitFor(() => {
expect(screen.getByText('Download failed')).toBeInTheDocument();
});
expect(mockToast.error).toHaveBeenCalledWith('Download failed');
});
it('should call onDownloadStart callback', async () => {
const onDownloadStart = vi.fn();
vi.mocked(downloadTrack).mockResolvedValue(undefined);
render(
<TrackDownloadButton trackId={123} onDownloadStart={onDownloadStart} />,
);
const button = screen.getByRole('button');
await userEvent.click(button);
await waitFor(() => {
expect(onDownloadStart).toHaveBeenCalled();
});
});
it('should call onDownloadComplete callback', async () => {
const onDownloadComplete = vi.fn();
vi.mocked(downloadTrack).mockResolvedValue(undefined);
render(
<TrackDownloadButton
trackId={123}
onDownloadComplete={onDownloadComplete}
/>,
);
const button = screen.getByRole('button');
await userEvent.click(button);
await waitFor(() => {
expect(onDownloadComplete).toHaveBeenCalled();
});
});
it('should call onDownloadError callback on failure', async () => {
const onDownloadError = vi.fn();
const error = new TrackDownloadError('Download failed', 'NETWORK', true);
vi.mocked(downloadTrack).mockRejectedValue(error);
render(
<TrackDownloadButton trackId={123} onDownloadError={onDownloadError} />,
);
const button = screen.getByRole('button');
await userEvent.click(button);
await waitFor(() => {
expect(onDownloadError).toHaveBeenCalledWith('Download failed');
});
});
it('should use custom filename if provided', async () => {
vi.mocked(downloadTrack).mockResolvedValue(undefined);
render(<TrackDownloadButton trackId={123} filename="custom-track.mp3" />);
const button = screen.getByRole('button');
await userEvent.click(button);
await waitFor(() => {
expect(downloadTrack).toHaveBeenCalledWith(123, {
shareToken: undefined,
filename: 'custom-track.mp3',
onProgress: undefined,
});
});
});
it('should apply custom variant and size', () => {
render(<TrackDownloadButton trackId={123} variant="outline" size="sm" />);
const button = screen.getByRole('button');
expect(button).toBeInTheDocument();
});
});