diff --git a/src/__tests__/components/BaseModal.test.tsx b/src/__tests__/components/BaseModal.test.tsx
new file mode 100644
index 0000000..714940c
--- /dev/null
+++ b/src/__tests__/components/BaseModal.test.tsx
@@ -0,0 +1,59 @@
+import React from 'react';
+import { render, screen } from '@testing-library/react';
+import '@testing-library/jest-dom';
+import { BaseModal } from '../../components/base/BaseModal';
+
+describe('BaseModal', () => {
+ const defaultProps = {
+ isOpen: true,
+ onClose: jest.fn(),
+ title: 'Test Modal',
+ children:
Modal Content
,
+ };
+
+ it('should render modal when open', () => {
+ render();
+
+ expect(screen.getByText('Test Modal')).toBeInTheDocument();
+ expect(screen.getByText('Modal Content')).toBeInTheDocument();
+ });
+
+ it('should not render modal when closed', () => {
+ render();
+
+ expect(screen.queryByText('Test Modal')).not.toBeInTheDocument();
+ expect(screen.queryByText('Modal Content')).not.toBeInTheDocument();
+ });
+
+ it('should render with custom maxWidth and maxHeight', () => {
+ render(
+
+ );
+
+ const modalContent = screen.getByTestId('modal-content');
+ expect(modalContent).toHaveClass('sm:max-w-[800px]');
+ expect(modalContent).toHaveClass('max-h-[80vh]');
+ });
+
+ it('should render with description when provided', () => {
+ render(
+
+ );
+
+ expect(screen.getByText('Test description')).toBeInTheDocument();
+ });
+
+ it('should render footer when provided', () => {
+ const footer = ;
+ render();
+
+ expect(screen.getByText('Save')).toBeInTheDocument();
+ });
+});
diff --git a/src/__tests__/lib/utils.test.ts b/src/__tests__/lib/utils.test.ts
new file mode 100644
index 0000000..373abff
--- /dev/null
+++ b/src/__tests__/lib/utils.test.ts
@@ -0,0 +1,120 @@
+import {
+ generateSlug,
+ generateShortId,
+ formatCurrency,
+ formatDate,
+ validateEmail,
+ sanitizeHtml
+} from '../../lib/utils';
+
+describe('Utils Module', () => {
+ describe('generateSlug', () => {
+ it('should generate valid slug from title', () => {
+ const title = 'Test Campaign Title';
+ const slug = generateSlug(title);
+
+ expect(slug).toBe('test-campaign-title');
+ });
+
+ it('should handle special characters', () => {
+ const title = 'Campagne avec des caractères spéciaux @#$%';
+ const slug = generateSlug(title);
+
+ expect(slug).toBe('campagne-avec-des-caracteres-speciaux-');
+ });
+
+ it('should handle empty string', () => {
+ const slug = generateSlug('');
+ expect(slug).toBe('');
+ });
+
+ it('should handle multiple spaces', () => {
+ const title = 'Multiple Spaces';
+ const slug = generateSlug(title);
+
+ expect(slug).toBe('multiple-spaces');
+ });
+ });
+
+ describe('generateShortId', () => {
+ it('should generate short ID with correct length', () => {
+ const shortId = generateShortId();
+
+ expect(shortId).toHaveLength(8);
+ expect(shortId).toMatch(/^[A-Z0-9]+$/);
+ });
+
+ it('should generate different IDs', () => {
+ const id1 = generateShortId();
+ const id2 = generateShortId();
+
+ expect(id1).not.toBe(id2);
+ });
+ });
+
+ describe('formatCurrency', () => {
+ it('should format currency correctly', () => {
+ const result1 = formatCurrency(1000);
+ const result2 = formatCurrency(1234.56);
+ const result3 = formatCurrency(0);
+
+ expect(result1).toMatch(/1\s*000,00\s*€/);
+ expect(result2).toMatch(/1\s*234,56\s*€/);
+ expect(result3).toMatch(/0,00\s*€/);
+ });
+
+ it('should handle negative values', () => {
+ const result = formatCurrency(-1000);
+ expect(result).toMatch(/-1\s*000,00\s*€/);
+ });
+ });
+
+ describe('formatDate', () => {
+ it('should format date correctly', () => {
+ const date = new Date('2024-01-15T10:30:00');
+ const formatted = formatDate(date);
+
+ expect(formatted).toBe('15/01/2024');
+ });
+
+ it('should handle string date', () => {
+ const formatted = formatDate('2024-01-15');
+ expect(formatted).toBe('15/01/2024');
+ });
+ });
+
+ describe('validateEmail', () => {
+ it('should validate correct email addresses', () => {
+ expect(validateEmail('test@example.com')).toBe(true);
+ expect(validateEmail('user.name+tag@domain.co.uk')).toBe(true);
+ expect(validateEmail('123@test.org')).toBe(true);
+ });
+
+ it('should reject invalid email addresses', () => {
+ expect(validateEmail('invalid-email')).toBe(false);
+ expect(validateEmail('test@')).toBe(false);
+ expect(validateEmail('@example.com')).toBe(false);
+ expect(validateEmail('')).toBe(false);
+ });
+ });
+
+ describe('sanitizeHtml', () => {
+ it('should remove dangerous HTML tags', () => {
+ const input = 'Safe content
';
+ const sanitized = sanitizeHtml(input);
+
+ expect(sanitized).toBe('Safe content
');
+ });
+
+ it('should allow safe HTML tags', () => {
+ const input = 'Paragraph
BoldItalic';
+ const sanitized = sanitizeHtml(input);
+
+ expect(sanitized).toBe(input);
+ });
+
+ it('should handle empty string', () => {
+ expect(sanitizeHtml('')).toBe('');
+ });
+ });
+});
diff --git a/src/components/base/BaseModal.tsx b/src/components/base/BaseModal.tsx
index 0d59e77..286c875 100644
--- a/src/components/base/BaseModal.tsx
+++ b/src/components/base/BaseModal.tsx
@@ -24,7 +24,10 @@ export function BaseModal({
}: BaseModalProps) {
return (