veza/apps/web/src/components/ui/dialog.test.tsx
senke ae586f6134 Phase 2 stabilisation: code mort, Modal→Dialog, feature flags, tests, router split, Rust legacy
Bloc A - Code mort:
- Suppression Studio (components, views, features)
- Suppression gamification + services mock (projectService, storageService, gamificationService)
- Mise à jour Sidebar, Navbar, locales

Bloc B - Frontend:
- Suppression modal.tsx deprecated, Modal.stories (doublon Dialog)
- Feature flags: PLAYLIST_SEARCH, PLAYLIST_RECOMMENDATIONS, ROLE_MANAGEMENT = true
- Suppression 19 tests orphelins, retrait exclusions vitest.config

Bloc C - Backend:
- Extraction routes_auth.go depuis router.go

Bloc D - Rust:
- Suppression security_legacy.rs (code mort, patterns déjà dans security/)
2026-02-14 17:23:32 +01:00

385 lines
10 KiB
TypeScript

import { render, screen, waitFor, fireEvent } from '@testing-library/react';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import userEvent from '@testing-library/user-event';
import '@testing-library/jest-dom/vitest';
import { Dialog, DialogBody, DialogFooter, DialogHeader } from './dialog';
describe('Dialog Component', () => {
const mockOnClose = vi.fn();
beforeEach(() => {
vi.clearAllMocks();
document.body.style.overflow = '';
});
it('renders nothing when open is false', () => {
render(
<Dialog open={false} onClose={mockOnClose} title="Test Dialog">
<div>Dialog content</div>
</Dialog>,
);
expect(screen.queryByText('Dialog content')).not.toBeInTheDocument();
});
it('renders dialog when open is true', () => {
render(
<Dialog open={true} onClose={mockOnClose} title="Test Dialog">
<div>Dialog content</div>
</Dialog>,
);
expect(screen.getByText('Dialog content')).toBeInTheDocument();
expect(screen.getByText('Test Dialog')).toBeInTheDocument();
});
it('displays title when provided', () => {
render(
<Dialog open={true} onClose={mockOnClose} title="Test Dialog">
<div>Dialog content</div>
</Dialog>,
);
expect(screen.getByText('Test Dialog')).toBeInTheDocument();
});
it('renders DialogHeader correctly', () => {
render(
<Dialog open={true} onClose={mockOnClose} title="Test Dialog">
<div>Dialog content</div>
</Dialog>,
);
const header = screen.getByText('Test Dialog').closest('.border-b');
expect(header).toBeInTheDocument();
});
it('renders DialogBody correctly', () => {
render(
<Dialog open={true} onClose={mockOnClose} title="Test Dialog">
<div>Dialog content</div>
</Dialog>,
);
expect(screen.getByText('Dialog content')).toBeInTheDocument();
});
it('renders custom footer when provided', () => {
render(
<Dialog
open={true}
onClose={mockOnClose}
title="Test Dialog"
footer={<button>Custom Footer</button>}
>
<div>Dialog content</div>
</Dialog>,
);
expect(screen.getByText('Custom Footer')).toBeInTheDocument();
});
it('shows default footer with confirm and cancel buttons for confirm variant', () => {
const mockOnConfirm = vi.fn();
render(
<Dialog
open={true}
onClose={mockOnClose}
title="Confirm Dialog"
variant="confirm"
onConfirm={mockOnConfirm}
showCancel={true}
>
<div>Are you sure?</div>
</Dialog>,
);
expect(screen.getByText('Confirm')).toBeInTheDocument();
expect(screen.getByText('Cancel')).toBeInTheDocument();
});
it('calls onConfirm when confirm button is clicked', async () => {
const user = userEvent.setup();
const mockOnConfirm = vi.fn();
render(
<Dialog
open={true}
onClose={mockOnClose}
title="Confirm Dialog"
variant="confirm"
onConfirm={mockOnConfirm}
>
<div>Are you sure?</div>
</Dialog>,
);
const confirmButton = screen.getByText('Confirm');
await user.click(confirmButton);
expect(mockOnConfirm).toHaveBeenCalledTimes(1);
expect(mockOnClose).toHaveBeenCalledTimes(1);
});
it('calls onCancel when cancel button is clicked', async () => {
const user = userEvent.setup();
const mockOnCancel = vi.fn();
render(
<Dialog
open={true}
onClose={mockOnClose}
title="Confirm Dialog"
variant="confirm"
onCancel={mockOnCancel}
showCancel={true}
>
<div>Are you sure?</div>
</Dialog>,
);
const cancelButton = screen.getByText('Cancel');
await user.click(cancelButton);
expect(mockOnCancel).toHaveBeenCalledTimes(1);
expect(mockOnClose).toHaveBeenCalledTimes(1);
});
it('shows alert icon for alert variant', () => {
render(
<Dialog
open={true}
onClose={mockOnClose}
title="Alert Dialog"
variant="alert"
>
<div>Alert message</div>
</Dialog>,
);
const icon = screen
.getByText('Alert Dialog')
.closest('.border-b')
?.querySelector('svg');
expect(icon).toBeInTheDocument();
});
it('shows info icon for info variant', () => {
render(
<Dialog
open={true}
onClose={mockOnClose}
title="Info Dialog"
variant="info"
>
<div>Info message</div>
</Dialog>,
);
const icon = screen
.getByText('Info Dialog')
.closest('.border-b')
?.querySelector('svg');
expect(icon).toBeInTheDocument();
});
it('applies destructive variant to confirm button for alert', () => {
const mockOnConfirm = vi.fn();
render(
<Dialog
open={true}
onClose={mockOnClose}
title="Alert Dialog"
variant="alert"
onConfirm={mockOnConfirm}
>
<div>Alert message</div>
</Dialog>,
);
const confirmButton = screen.getByText('Confirm');
const buttonElement = confirmButton.closest('button');
// Design: destructive variant uses text-destructive / bg-destructive
expect(buttonElement?.className).toMatch(/text-destructive|bg-destructive/);
});
it('uses custom confirm and cancel labels', () => {
const mockOnConfirm = vi.fn();
render(
<Dialog
open={true}
onClose={mockOnClose}
title="Confirm Dialog"
variant="confirm"
onConfirm={mockOnConfirm}
confirmLabel="Yes"
cancelLabel="No"
showCancel={true}
>
<div>Are you sure?</div>
</Dialog>,
);
expect(screen.getByText('Yes')).toBeInTheDocument();
expect(screen.getByText('No')).toBeInTheDocument();
});
it('hides cancel button when showCancel is false', () => {
const mockOnConfirm = vi.fn();
render(
<Dialog
open={true}
onClose={mockOnClose}
title="Confirm Dialog"
variant="confirm"
onConfirm={mockOnConfirm}
showCancel={false}
>
<div>Are you sure?</div>
</Dialog>,
);
expect(screen.queryByText('Cancel')).not.toBeInTheDocument();
expect(screen.getByText('Confirm')).toBeInTheDocument();
});
it('does not show footer for default variant without footer or actions', () => {
render(
<Dialog open={true} onClose={mockOnClose} title="Default Dialog">
<div>Dialog content</div>
</Dialog>,
);
const footer = document.querySelector('.border-t');
expect(footer).not.toBeInTheDocument();
});
it('calls onClose when clicking overlay (default variant)', async () => {
render(
<Dialog open={true} onClose={mockOnClose} title="Test Dialog">
<div>Dialog content</div>
</Dialog>,
);
const dialog = screen.getByRole('dialog');
const overlay = dialog.closest('.fixed.inset-0');
expect(overlay).toBeInTheDocument();
if (overlay) fireEvent.click(overlay);
expect(mockOnClose).toHaveBeenCalledTimes(1);
});
it('calls onClose when pressing Escape', async () => {
render(
<Dialog open={true} onClose={mockOnClose} title="Test Dialog">
<div>Dialog content</div>
</Dialog>,
);
fireEvent.keyDown(document, { key: 'Escape' });
await waitFor(() => {
expect(mockOnClose).toHaveBeenCalledTimes(1);
});
});
it('calls onClose when clicking close button', async () => {
const user = userEvent.setup();
render(
<Dialog open={true} onClose={mockOnClose} title="Test Dialog">
<div>Dialog content</div>
</Dialog>,
);
const closeButton = screen.getByLabelText('Fermer');
await user.click(closeButton);
expect(mockOnClose).toHaveBeenCalledTimes(1);
});
it('prevents body scroll when open and restores when closed', () => {
const { rerender } = render(
<Dialog open={true} onClose={mockOnClose} title="Test Dialog">
<div>Content</div>
</Dialog>,
);
expect(document.body.style.overflow).toBe('hidden');
rerender(
<Dialog open={false} onClose={mockOnClose} title="Test Dialog">
<div>Content</div>
</Dialog>,
);
expect(document.body.style.overflow).toBe('');
});
it('handles async onConfirm', async () => {
const user = userEvent.setup();
const mockOnConfirm = vi.fn().mockResolvedValue(undefined);
render(
<Dialog
open={true}
onClose={mockOnClose}
title="Confirm Dialog"
variant="confirm"
onConfirm={mockOnConfirm}
>
<div>Are you sure?</div>
</Dialog>,
);
const confirmButton = screen.getByText('Confirm');
await user.click(confirmButton);
await waitFor(() => {
expect(mockOnConfirm).toHaveBeenCalledTimes(1);
});
await waitFor(() => {
expect(mockOnClose).toHaveBeenCalledTimes(1);
});
});
describe('DialogHeader', () => {
it('renders DialogHeader correctly', () => {
render(
<Dialog open={true} onClose={mockOnClose}>
<DialogHeader>
<div>Header content</div>
</DialogHeader>
<DialogBody>
<div>Body content</div>
</DialogBody>
</Dialog>,
);
expect(screen.getByText('Header content')).toBeInTheDocument();
});
});
describe('DialogBody', () => {
it('renders DialogBody correctly', () => {
render(
<Dialog open={true} onClose={mockOnClose}>
<DialogBody>
<div>Body content</div>
</DialogBody>
</Dialog>,
);
expect(screen.getByText('Body content')).toBeInTheDocument();
});
});
describe('DialogFooter', () => {
it('renders DialogFooter correctly', () => {
render(
<Dialog open={true} onClose={mockOnClose}>
<DialogBody>
<div>Body content</div>
</DialogBody>
<DialogFooter>
<button>Footer button</button>
</DialogFooter>
</Dialog>,
);
expect(screen.getByText('Footer button')).toBeInTheDocument();
});
});
});