veza/apps/web/src/features/tracks/components/TrackHistory.test.tsx

310 lines
7.6 KiB
TypeScript
Raw Normal View History

import { describe, it, expect, vi, beforeEach } from 'vitest';
import { render, screen, waitFor, fireEvent } from '@testing-library/react';
import { TrackHistory } from './TrackHistory';
import {
getTrackHistory,
TrackHistoryError,
TrackHistoryItem,
} from '../services/trackHistoryService';
import { useToast } from '@/hooks/useToast';
// Mock services
vi.mock('../services/trackHistoryService');
vi.mock('@/hooks/useToast');
describe('TrackHistory', () => {
const mockToast = {
success: vi.fn(),
error: vi.fn(),
};
beforeEach(() => {
vi.clearAllMocks();
vi.mocked(useToast).mockReturnValue(mockToast);
});
const mockHistoryItems: TrackHistoryItem[] = [
{
id: 1,
track_id: 123,
user_id: 1,
action: 'created',
old_value: undefined,
new_value: '{"title": "Test Track"}',
created_at: '2024-01-01T00:00:00Z',
},
{
id: 2,
track_id: 123,
user_id: 1,
action: 'updated',
old_value: '{"title": "Test Track"}',
new_value: '{"title": "Updated Track"}',
created_at: '2024-01-02T00:00:00Z',
},
{
id: 3,
track_id: 123,
user_id: 1,
action: 'published',
old_value: undefined,
new_value: undefined,
created_at: '2024-01-03T00:00:00Z',
},
];
it('should render loading state initially', async () => {
vi.mocked(getTrackHistory).mockImplementation(
2025-12-13 02:34:34 +00:00
() =>
new Promise((resolve) =>
setTimeout(
() =>
resolve({
history: mockHistoryItems,
total: 3,
limit: 50,
offset: 0,
}),
100,
),
),
);
render(<TrackHistory trackId={123} />);
expect(screen.getByRole('status')).toBeInTheDocument();
await waitFor(() => {
2025-12-13 02:34:34 +00:00
expect(getTrackHistory).toHaveBeenCalledWith(123, {
limit: 50,
offset: 0,
});
});
});
it('should display history timeline', async () => {
vi.mocked(getTrackHistory).mockResolvedValue({
history: mockHistoryItems,
total: 3,
limit: 50,
offset: 0,
});
render(<TrackHistory trackId={123} />);
await waitFor(() => {
2025-12-13 02:34:34 +00:00
expect(
screen.getByText('Historique des modifications'),
).toBeInTheDocument();
});
expect(screen.getByText('Créé')).toBeInTheDocument();
expect(screen.getByText('Modifié')).toBeInTheDocument();
expect(screen.getByText('Publié')).toBeInTheDocument();
});
it('should display empty state when no history', async () => {
vi.mocked(getTrackHistory).mockResolvedValue({
history: [],
total: 0,
limit: 50,
offset: 0,
});
render(<TrackHistory trackId={123} />);
await waitFor(() => {
2025-12-13 02:34:34 +00:00
expect(
screen.getByText('Aucune modification enregistrée'),
).toBeInTheDocument();
});
});
it('should display error message on load error', async () => {
const error = new TrackHistoryError('Failed to load', 'SERVER', true);
vi.mocked(getTrackHistory).mockRejectedValue(error);
render(<TrackHistory trackId={123} />);
await waitFor(() => {
expect(screen.getByText('Failed to load')).toBeInTheDocument();
});
});
it('should display old and new values when available', async () => {
vi.mocked(getTrackHistory).mockResolvedValue({
history: [mockHistoryItems[1]], // Updated action with values
total: 1,
limit: 50,
offset: 0,
});
render(<TrackHistory trackId={123} />);
await waitFor(() => {
expect(screen.getByText('Ancienne valeur:')).toBeInTheDocument();
expect(screen.getByText('Nouvelle valeur:')).toBeInTheDocument();
});
});
it('should handle pagination', async () => {
2025-12-13 02:34:34 +00:00
const largeHistory: TrackHistoryItem[] = Array.from(
{ length: 10 },
(_, i) => ({
id: i + 1,
track_id: 123,
user_id: 1,
action: 'updated' as const,
created_at: `2024-01-0${i + 1}T00:00:00Z`,
}),
);
vi.mocked(getTrackHistory).mockResolvedValue({
history: largeHistory.slice(0, 5),
total: 10,
limit: 5,
offset: 0,
});
render(<TrackHistory trackId={123} limit={5} />);
await waitFor(() => {
expect(screen.getByText(/Affichage 1 - 5 sur 10/)).toBeInTheDocument();
});
const nextButton = screen.getByText('Suivant');
expect(nextButton).toBeInTheDocument();
expect(nextButton).not.toBeDisabled();
// Mock next page response
vi.mocked(getTrackHistory).mockResolvedValue({
history: largeHistory.slice(5, 10),
total: 10,
limit: 5,
offset: 5,
});
fireEvent.click(nextButton);
await waitFor(() => {
2025-12-13 02:34:34 +00:00
expect(getTrackHistory).toHaveBeenCalledWith(123, {
limit: 5,
offset: 5,
});
});
});
it('should disable previous button on first page', async () => {
vi.mocked(getTrackHistory).mockResolvedValue({
history: mockHistoryItems.slice(0, 2),
total: 10,
limit: 2,
offset: 0,
});
render(<TrackHistory trackId={123} limit={2} />);
await waitFor(() => {
const previousButton = screen.getByText('Précédent');
expect(previousButton).toBeDisabled();
});
});
it('should disable next button on last page', async () => {
vi.mocked(getTrackHistory).mockResolvedValue({
history: mockHistoryItems.slice(0, 2),
total: 2,
limit: 2,
offset: 0,
});
render(<TrackHistory trackId={123} limit={2} />);
await waitFor(() => {
const nextButton = screen.getByText('Suivant');
expect(nextButton).toBeDisabled();
});
});
it('should display all action types correctly', async () => {
const allActions: TrackHistoryItem[] = [
2025-12-13 02:34:34 +00:00
{
id: 1,
track_id: 123,
user_id: 1,
action: 'created',
created_at: '2024-01-01T00:00:00Z',
},
{
id: 2,
track_id: 123,
user_id: 1,
action: 'updated',
created_at: '2024-01-02T00:00:00Z',
},
{
id: 3,
track_id: 123,
user_id: 1,
action: 'deleted',
created_at: '2024-01-03T00:00:00Z',
},
{
id: 4,
track_id: 123,
user_id: 1,
action: 'published',
created_at: '2024-01-04T00:00:00Z',
},
{
id: 5,
track_id: 123,
user_id: 1,
action: 'unpublished',
created_at: '2024-01-05T00:00:00Z',
},
{
id: 6,
track_id: 123,
user_id: 1,
action: 'restored',
created_at: '2024-01-06T00:00:00Z',
},
];
vi.mocked(getTrackHistory).mockResolvedValue({
history: allActions,
total: 6,
limit: 50,
offset: 0,
});
render(<TrackHistory trackId={123} />);
await waitFor(() => {
expect(screen.getByText('Créé')).toBeInTheDocument();
expect(screen.getByText('Modifié')).toBeInTheDocument();
expect(screen.getByText('Supprimé')).toBeInTheDocument();
expect(screen.getByText('Publié')).toBeInTheDocument();
expect(screen.getByText('Dépublié')).toBeInTheDocument();
expect(screen.getByText('Restauré')).toBeInTheDocument();
});
});
it('should format dates correctly', async () => {
vi.mocked(getTrackHistory).mockResolvedValue({
history: [mockHistoryItems[0]],
total: 1,
limit: 50,
offset: 0,
});
render(<TrackHistory trackId={123} />);
await waitFor(() => {
// Check that date is formatted (should contain month name in French)
const dateElement = screen.getByText(/janvier/i);
expect(dateElement).toBeInTheDocument();
});
});
});