import { render, screen, waitFor, act } from '@testing-library/react';
import { describe, it, expect, vi } from 'vitest';
import { ToastComponent, Toast } from './Toast';
import { ToastProvider, useToastContext } from './ToastProvider';
import { useToast } from '@/hooks/useToast';
describe('Toast Components', () => {
describe('ToastComponent', () => {
const mockToast: Toast = {
id: '1',
message: 'Test message',
type: 'success',
};
it('renders toast with message', async () => {
const onDismiss = vi.fn();
render();
await waitFor(() => {
expect(screen.getByText('Test message')).toBeInTheDocument();
});
});
it('renders with correct type styling', async () => {
const onDismiss = vi.fn();
const { container } = render(
,
);
await waitFor(() => {
const toast = container.querySelector(
'.bg-red-50, .dark\\:bg-red-900\\/20',
);
expect(toast).toBeInTheDocument();
});
});
it('auto-dismisses after duration', async () => {
vi.useFakeTimers();
const onDismiss = vi.fn();
render(
,
);
await waitFor(() => {
expect(screen.getByText('Test message')).toBeInTheDocument();
});
act(() => {
vi.advanceTimersByTime(1100);
});
await waitFor(() => {
expect(onDismiss).toHaveBeenCalledWith('1');
});
vi.useRealTimers();
});
it('does not auto-dismiss when duration is 0', async () => {
vi.useFakeTimers();
const onDismiss = vi.fn();
render(
,
);
await waitFor(() => {
expect(screen.getByText('Test message')).toBeInTheDocument();
});
act(() => {
vi.advanceTimersByTime(6000);
});
await waitFor(() => {
expect(onDismiss).not.toHaveBeenCalled();
});
vi.useRealTimers();
});
it('calls onDismiss when close button is clicked', async () => {
const onDismiss = vi.fn();
render();
await waitFor(() => {
expect(screen.getByText('Test message')).toBeInTheDocument();
});
const closeButton = screen.getByLabelText('Fermer');
closeButton.click();
await waitFor(
() => {
expect(onDismiss).toHaveBeenCalledWith('1');
},
{ timeout: 500 },
);
});
it('renders correct icon for each type', async () => {
const onDismiss = vi.fn();
const { container, rerender } = render(
,
);
await waitFor(() => {
const icon = container.querySelector('svg');
expect(icon).toBeInTheDocument();
});
rerender(
,
);
rerender(
,
);
rerender(
,
);
});
it('renders with default type when type is not provided', async () => {
const onDismiss = vi.fn();
const { container } = render(
,
);
await waitFor(() => {
expect(screen.getByText('Test')).toBeInTheDocument();
const toast = container.querySelector(
'.bg-blue-50, .dark\\:bg-blue-900\\/20',
);
expect(toast).toBeInTheDocument();
});
});
});
describe('ToastProvider', () => {
it('provides toast context', () => {
const TestComponent = () => {
const context = useToastContext();
expect(context).toBeDefined();
expect(context.toasts).toEqual([]);
expect(typeof context.addToast).toBe('function');
expect(typeof context.removeToast).toBe('function');
return
Test
;
};
render(
,
);
});
it('throws error when used outside provider', () => {
const TestComponent = () => {
try {
useToastContext();
return Should not render
;
} catch (error: any) {
expect(error.message).toContain(
'useToastContext must be used within ToastProvider',
);
return Error caught
;
}
};
render();
});
it('adds toast to queue', async () => {
const TestComponent = () => {
const { addToast, toasts } = useToastContext();
return (
{toasts.length}
);
};
render(
,
);
const button = screen.getByText('Add Toast');
button.click();
await waitFor(() => {
expect(screen.getByTestId('toast-count')).toHaveTextContent('1');
});
});
it('removes toast from queue', async () => {
const TestComponent = () => {
const { addToast, removeToast, toasts } = useToastContext();
return (
{toasts.length}
);
};
render(
,
);
const addButton = screen.getByText('Add Toast');
addButton.click();
await waitFor(() => {
expect(screen.getByTestId('toast-count')).toHaveTextContent('1');
});
const removeButton = screen.getByText('Remove Toast');
removeButton.click();
await waitFor(() => {
expect(screen.getByTestId('toast-count')).toHaveTextContent('0');
});
});
it('renders toasts in container', async () => {
const TestComponent = () => {
const { addToast } = useToastContext();
return (
);
};
render(
,
);
const button = screen.getByText('Add Toast');
await act(async () => {
button.click();
});
await waitFor(
() => {
expect(screen.getByText('Test message')).toBeInTheDocument();
},
{ timeout: 1000 },
);
});
it('applies correct position classes', () => {
const { container, rerender } = render(
Test
,
);
let positionDiv = container.querySelector('.top-4.right-4');
expect(positionDiv).toBeInTheDocument();
rerender(
Test
,
);
positionDiv = container.querySelector('.bottom-4.left-4');
expect(positionDiv).toBeInTheDocument();
});
});
describe('useToast hook', () => {
it('provides toast methods', () => {
const TestComponent = () => {
const toast = useToast();
expect(typeof toast.success).toBe('function');
expect(typeof toast.error).toBe('function');
expect(typeof toast.warning).toBe('function');
expect(typeof toast.info).toBe('function');
expect(typeof toast.toast).toBe('function');
return Test
;
};
render(
,
);
});
it('displays success toast', async () => {
const TestComponent = () => {
const toast = useToast();
return (
);
};
render(
,
);
const button = screen.getByText('Show Success');
await act(async () => {
button.click();
});
// Wait for toast to appear (with animation delay)
await waitFor(
() => {
const toast = screen.queryByText('Success message');
expect(toast).toBeInTheDocument();
},
{ timeout: 1000 },
);
});
it('displays error toast', async () => {
const TestComponent = () => {
const toast = useToast();
return (
);
};
render(
,
);
const button = screen.getByText('Show Error');
await act(async () => {
button.click();
});
await waitFor(
() => {
expect(screen.getByText('Error message')).toBeInTheDocument();
},
{ timeout: 1000 },
);
});
it('displays warning toast', async () => {
const TestComponent = () => {
const toast = useToast();
return (
);
};
render(
,
);
const button = screen.getByText('Show Warning');
await act(async () => {
button.click();
});
await waitFor(
() => {
expect(screen.getByText('Warning message')).toBeInTheDocument();
},
{ timeout: 1000 },
);
});
it('displays info toast', async () => {
const TestComponent = () => {
const toast = useToast();
return (
);
};
render(
,
);
const button = screen.getByText('Show Info');
await act(async () => {
button.click();
});
await waitFor(
() => {
expect(screen.getByText('Info message')).toBeInTheDocument();
},
{ timeout: 1000 },
);
});
it('allows custom toast with toast method', async () => {
const TestComponent = () => {
const toast = useToast();
return (
);
};
render(
,
);
const button = screen.getByText('Show Custom');
await act(async () => {
button.click();
});
await waitFor(
() => {
expect(screen.getByText('Custom message')).toBeInTheDocument();
},
{ timeout: 1000 },
);
});
});
});