diff --git a/src/__tests__/components/DeleteModal.test.tsx b/src/__tests__/components/DeleteModal.test.tsx
new file mode 100644
index 0000000..0737f52
--- /dev/null
+++ b/src/__tests__/components/DeleteModal.test.tsx
@@ -0,0 +1,102 @@
+import React from 'react';
+import { render, screen, fireEvent, waitFor } from '@testing-library/react';
+import '@testing-library/jest-dom';
+import { DeleteModal } from '../../components/base/DeleteModal';
+
+describe('DeleteModal', () => {
+ const defaultProps = {
+ isOpen: true,
+ onClose: jest.fn(),
+ onConfirm: jest.fn().mockResolvedValue(undefined),
+ title: 'Supprimer la campagne',
+ description: 'Êtes-vous sûr de vouloir supprimer cette campagne ?',
+ itemName: 'Campagne Test',
+ itemDetails:
Détails de la campagne à supprimer
,
+ };
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('should render modal when open', () => {
+ render();
+
+ expect(screen.getByText('Supprimer la campagne')).toBeInTheDocument();
+ expect(screen.getByText('Êtes-vous sûr de vouloir supprimer cette campagne ?')).toBeInTheDocument();
+ expect(screen.getByText('Campagne Test à supprimer :')).toBeInTheDocument();
+ });
+
+ it('should not render modal when closed', () => {
+ render();
+
+ expect(screen.queryByText('Supprimer la campagne')).not.toBeInTheDocument();
+ });
+
+ it('should call onConfirm when delete button is clicked', async () => {
+ const onConfirm = jest.fn().mockResolvedValue(undefined);
+ render();
+
+ const deleteButton = screen.getByRole('button', { name: /supprimer définitivement/i });
+ fireEvent.click(deleteButton);
+
+ await waitFor(() => {
+ expect(onConfirm).toHaveBeenCalledTimes(1);
+ });
+ });
+
+ it('should call onClose when cancel button is clicked', () => {
+ const onClose = jest.fn();
+ render();
+
+ const cancelButton = screen.getByRole('button', { name: /annuler/i });
+ fireEvent.click(cancelButton);
+
+ expect(onClose).toHaveBeenCalledTimes(1);
+ });
+
+ it('should show loading state during deletion', async () => {
+ const onConfirm = jest.fn().mockImplementation(() => new Promise(resolve => setTimeout(resolve, 100)));
+ render();
+
+ const deleteButton = screen.getByRole('button', { name: /supprimer définitivement/i });
+ fireEvent.click(deleteButton);
+
+ await waitFor(() => {
+ expect(screen.getByText('Suppression...')).toBeInTheDocument();
+ });
+ });
+
+ it('should render with custom confirm text', () => {
+ render(
+
+ );
+
+ expect(screen.getByRole('button', { name: /oui, supprimer définitivement/i })).toBeInTheDocument();
+ });
+
+ it('should show warning message', () => {
+ render();
+
+ expect(screen.getByText(/⚠️ Cette action est irréversible./)).toBeInTheDocument();
+ });
+
+ it('should show custom warning message', () => {
+ render(
+
+ );
+
+ expect(screen.getByText(/⚠️ Attention, cette suppression est définitive !/)).toBeInTheDocument();
+ });
+
+ it('should display item details', () => {
+ render();
+
+ expect(screen.getByText('Détails de la campagne à supprimer')).toBeInTheDocument();
+ });
+});
diff --git a/src/__tests__/components/ErrorDisplay.test.tsx b/src/__tests__/components/ErrorDisplay.test.tsx
new file mode 100644
index 0000000..0e79c53
--- /dev/null
+++ b/src/__tests__/components/ErrorDisplay.test.tsx
@@ -0,0 +1,63 @@
+import React from 'react';
+import { render, screen } from '@testing-library/react';
+import '@testing-library/jest-dom';
+import { ErrorDisplay } from '../../components/base/ErrorDisplay';
+
+describe('ErrorDisplay', () => {
+ it('should render error message when error is provided', () => {
+ const error = 'Une erreur est survenue';
+ render();
+
+ expect(screen.getByText('Une erreur est survenue')).toBeInTheDocument();
+ expect(screen.getByText('Une erreur est survenue')).toHaveClass('text-red-600');
+ });
+
+ it('should not render when no error is provided', () => {
+ render();
+
+ expect(screen.queryByText('Une erreur est survenue')).not.toBeInTheDocument();
+ });
+
+ it('should not render when error is null', () => {
+ render();
+
+ expect(screen.queryByText('Une erreur est survenue')).not.toBeInTheDocument();
+ });
+
+ it('should not render when error is undefined', () => {
+ render();
+
+ expect(screen.queryByText('Une erreur est survenue')).not.toBeInTheDocument();
+ });
+
+ it('should handle long error messages', () => {
+ const longError = 'A'.repeat(500);
+ render();
+
+ expect(screen.getByText(longError)).toBeInTheDocument();
+ });
+
+ it('should handle special characters in error message', () => {
+ const specialError = 'Erreur avec des caractères spéciaux: @#$%^&*()_+{}|:"<>?[]\\;\',./';
+ render();
+
+ expect(screen.getByText(specialError)).toBeInTheDocument();
+ });
+
+ it('should handle HTML in error message', () => {
+ const htmlError = 'Erreur avec HTML';
+ render();
+
+ expect(screen.getByText(htmlError)).toBeInTheDocument();
+ // Vérifier que le HTML n'est pas interprété
+ expect(screen.queryByText('xss')).not.toBeInTheDocument();
+ });
+
+ it('should have proper accessibility attributes', () => {
+ const error = 'Erreur d\'accessibilité';
+ render();
+
+ const errorElement = screen.getByText(error);
+ expect(errorElement).toHaveAttribute('role', 'alert');
+ });
+});
diff --git a/src/__tests__/lib/markdown.test.ts b/src/__tests__/lib/markdown.test.ts
new file mode 100644
index 0000000..90d7896
--- /dev/null
+++ b/src/__tests__/lib/markdown.test.ts
@@ -0,0 +1,67 @@
+import { parseMarkdown } from '../../lib/markdown';
+
+describe('Markdown Module', () => {
+ describe('parseMarkdown', () => {
+ it('should parse basic markdown', () => {
+ const markdown = '# Titre\n\nContenu **gras** et *italique*.';
+ const result = parseMarkdown(markdown);
+
+ expect(result).toContain('Titre
');
+ expect(result).toContain('gras');
+ expect(result).toContain('italique');
+ });
+
+ it('should handle empty string', () => {
+ const result = parseMarkdown('');
+ expect(result).toBe('');
+ });
+
+ it('should handle null/undefined', () => {
+ expect(parseMarkdown(null as any)).toBe('');
+ expect(parseMarkdown(undefined as any)).toBe('');
+ });
+
+ it('should handle links', () => {
+ const markdown = '[Lien](https://example.com)';
+ const result = parseMarkdown(markdown);
+
+ expect(result).toContain('Lien');
+ });
+
+ it('should handle lists', () => {
+ const markdown = '- Item 1\n- Item 2\n- Item 3';
+ const result = parseMarkdown(markdown);
+
+ expect(result).toContain('');
+ expect(result).toContain('- Item 1
');
+ expect(result).toContain('- Item 2
');
+ expect(result).toContain('- Item 3
');
+ });
+ });
+
+ describe('renderMarkdown', () => {
+ it('should render markdown to HTML', () => {
+ const markdown = '**Texte en gras**';
+ const result = parseMarkdown(markdown);
+
+ expect(result).toContain('Texte en gras');
+ });
+
+ it('should handle code blocks', () => {
+ const markdown = '```javascript\nconsole.log("test");\n```';
+ const result = parseMarkdown(markdown);
+
+ expect(result).toContain('```javascript');
+ expect(result).toContain('console.log("test");');
+ expect(result).toContain('```');
+ });
+
+ it('should handle inline code', () => {
+ const markdown = 'Utilisez `console.log()` pour afficher.';
+ const result = parseMarkdown(markdown);
+
+ expect(result).toContain('`console.log()`');
+ });
+ });
+});
diff --git a/src/components/base/ErrorDisplay.tsx b/src/components/base/ErrorDisplay.tsx
index c4b26ef..a1d0d7f 100644
--- a/src/components/base/ErrorDisplay.tsx
+++ b/src/components/base/ErrorDisplay.tsx
@@ -8,7 +8,7 @@ export function ErrorDisplay({ error, className = "" }: ErrorDisplayProps) {
return (
);
}