147 lines
3.8 KiB
TypeScript
147 lines
3.8 KiB
TypeScript
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
|
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
import { AvatarUpload } from './avatar-upload';
|
|
|
|
// Mock useToast
|
|
const mockShowSuccess = vi.fn();
|
|
const mockShowError = vi.fn();
|
|
|
|
vi.mock('@/hooks/useToast', () => ({
|
|
useToast: () => ({
|
|
success: mockShowSuccess,
|
|
error: mockShowError,
|
|
}),
|
|
}));
|
|
|
|
// Mock avatarService
|
|
vi.mock('@/features/profile/services/avatarService', () => ({
|
|
uploadAvatar: vi.fn().mockResolvedValue({ avatar_url: 'new-avatar.jpg' }),
|
|
deleteAvatar: vi.fn().mockResolvedValue({}),
|
|
}));
|
|
|
|
describe('AvatarUpload Component', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
it('renders avatar upload component', () => {
|
|
render(<AvatarUpload userId="123" />);
|
|
|
|
expect(screen.getByRole('button', { name: /Cliquez pour uploader/i })).toBeInTheDocument();
|
|
});
|
|
|
|
it('renders current avatar when provided', () => {
|
|
render(<AvatarUpload userId="123" currentAvatarUrl="avatar.jpg" />);
|
|
|
|
const image = screen.getByAltText('Avatar preview');
|
|
expect(image).toBeInTheDocument();
|
|
expect(image).toHaveAttribute('src', 'avatar.jpg');
|
|
});
|
|
|
|
it('shows upload button when no avatar', () => {
|
|
render(<AvatarUpload userId="123" />);
|
|
|
|
expect(screen.getByText(/Cliquez pour uploader/i)).toBeInTheDocument();
|
|
});
|
|
|
|
it('shows change button when avatar exists', () => {
|
|
render(<AvatarUpload userId="123" currentAvatarUrl="avatar.jpg" />);
|
|
|
|
expect(screen.getByText(/Changer/i)).toBeInTheDocument();
|
|
});
|
|
|
|
it('handles file selection', async () => {
|
|
const mockOnAvatarUpdated = vi.fn();
|
|
const file = new File(['test'], 'test.jpg', { type: 'image/jpeg' });
|
|
|
|
render(<AvatarUpload userId="123" onAvatarUpdated={mockOnAvatarUpdated} />);
|
|
|
|
const input = document.querySelector('input[type="file"]');
|
|
expect(input).toBeInTheDocument();
|
|
if (input) {
|
|
Object.defineProperty(input, 'files', {
|
|
value: [file],
|
|
writable: false,
|
|
});
|
|
|
|
fireEvent.change(input);
|
|
|
|
await waitFor(
|
|
() => {
|
|
expect(mockOnAvatarUpdated).toHaveBeenCalled();
|
|
},
|
|
{ timeout: 2000 },
|
|
);
|
|
}
|
|
});
|
|
|
|
it('validates file type', async () => {
|
|
const file = new File(['test'], 'test.txt', { type: 'text/plain' });
|
|
|
|
render(<AvatarUpload userId="123" />);
|
|
|
|
const input = document.querySelector('input[type="file"]');
|
|
|
|
if (input) {
|
|
Object.defineProperty(input, 'files', {
|
|
value: [file],
|
|
writable: false,
|
|
});
|
|
|
|
fireEvent.change(input);
|
|
|
|
await waitFor(() => {
|
|
expect(mockShowError).toHaveBeenCalled();
|
|
});
|
|
}
|
|
});
|
|
|
|
it('validates file size', async () => {
|
|
const largeFile = new File(['x'.repeat(10 * 1024 * 1024)], 'large.jpg', {
|
|
type: 'image/jpeg',
|
|
});
|
|
|
|
render(<AvatarUpload userId="123" maxSize={5 * 1024 * 1024} />);
|
|
|
|
const input = document.querySelector('input[type="file"]');
|
|
|
|
if (input) {
|
|
Object.defineProperty(input, 'files', {
|
|
value: [largeFile],
|
|
writable: false,
|
|
});
|
|
|
|
fireEvent.change(input);
|
|
|
|
await waitFor(() => {
|
|
expect(mockShowError).toHaveBeenCalled();
|
|
});
|
|
}
|
|
});
|
|
|
|
it('handles delete action', async () => {
|
|
const mockOnAvatarDeleted = vi.fn();
|
|
|
|
render(
|
|
<AvatarUpload
|
|
userId="123"
|
|
currentAvatarUrl="avatar.jpg"
|
|
onAvatarDeleted={mockOnAvatarDeleted}
|
|
/>,
|
|
);
|
|
|
|
const deleteButton = screen.getByText(/Supprimer/i);
|
|
fireEvent.click(deleteButton);
|
|
|
|
await waitFor(() => {
|
|
expect(mockOnAvatarDeleted).toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
it('disables when disabled prop is true', () => {
|
|
render(<AvatarUpload userId="123" disabled={true} />);
|
|
|
|
const button = screen.getByText(/Cliquez pour uploader/i);
|
|
expect(button).toBeDisabled();
|
|
});
|
|
});
|