diff --git a/__tests__/components/TeaIconsBackground.test.tsx b/__tests__/components/TeaIconsBackground.test.tsx
new file mode 100644
index 0000000..015b0bf
--- /dev/null
+++ b/__tests__/components/TeaIconsBackground.test.tsx
@@ -0,0 +1,82 @@
+/**
+ * Tests for the TeaIconsBackground component
+ * @jest-environment jsdom
+ */
+
+import React from 'react';
+import { render } from '@testing-library/react';
+import TeaIconsBackground from '@/components/TeaIconsBackground';
+
+// Mock next/image
+jest.mock('next/image', () => ({
+ __esModule: true,
+ default: (props: any) => {
+ // eslint-disable-next-line @next/next/no-img-element
+ return
;
+ },
+}));
+
+describe('TeaIconsBackground', () => {
+ it('should render the background container', () => {
+ const { container } = render();
+
+ // Check for fixed positioning
+ const fixedDiv = container.querySelector('.fixed');
+ expect(fixedDiv).toBeInTheDocument();
+ });
+
+ it('should render all 35 tea icons', () => {
+ const { getAllByTestId } = render();
+
+ const icons = getAllByTestId('tea-icon');
+ expect(icons).toHaveLength(35);
+ });
+
+ it('should render gradient background', () => {
+ const { container } = render();
+
+ const gradientDiv = container.querySelector('.bg-gradient-to-br');
+ expect(gradientDiv).toBeInTheDocument();
+ });
+
+ it('should render overlay', () => {
+ const { container } = render();
+
+ const overlayDiv = container.querySelector('.bg-gradient-radial');
+ expect(overlayDiv).toBeInTheDocument();
+ });
+
+ it('should apply animationKey to icon container when provided', () => {
+ const animationKey = 12345;
+ const { container } = render();
+
+ // The icons container should be keyed
+ const iconsContainer = container.querySelector('.opacity-\\[0\\.5\\]');
+ expect(iconsContainer).toBeInTheDocument();
+ });
+
+ it('should render icons with correct alt texts', () => {
+ const { getAllByAltText } = render();
+
+ // Check for some expected alt texts
+ const teapotGreen = getAllByAltText('Théière verte');
+ const teaCup = getAllByAltText('Tasse de thé');
+ const giftBox = getAllByAltText('Boîte cadeau');
+ const teaLeaves = getAllByAltText('Feuilles de thé');
+ const teapotPink = getAllByAltText('Théière rose');
+
+ // Each icon type appears 7 times (7 rows × 1 per row approximately)
+ expect(teapotGreen.length).toBeGreaterThan(0);
+ expect(teaCup.length).toBeGreaterThan(0);
+ expect(giftBox.length).toBeGreaterThan(0);
+ expect(teaLeaves.length).toBeGreaterThan(0);
+ expect(teapotPink.length).toBeGreaterThan(0);
+ });
+
+ it('should apply animate-float-gentle class to icons', () => {
+ const { container } = render();
+
+ const animatedIcons = container.querySelectorAll('.animate-float-gentle');
+ expect(animatedIcons.length).toBe(35);
+ });
+});
diff --git a/__tests__/components/ui/PrizeCard.test.tsx b/__tests__/components/ui/PrizeCard.test.tsx
new file mode 100644
index 0000000..6445ad7
--- /dev/null
+++ b/__tests__/components/ui/PrizeCard.test.tsx
@@ -0,0 +1,82 @@
+/**
+ * Tests for the PrizeCard component
+ * @jest-environment jsdom
+ */
+
+import React from 'react';
+import { render, screen } from '@testing-library/react';
+import { PrizeCard } from '@/components/ui/PrizeCard';
+
+// Mock next/image
+jest.mock('next/image', () => ({
+ __esModule: true,
+ default: (props: any) => {
+ // eslint-disable-next-line @next/next/no-img-element
+ return
;
+ },
+}));
+
+describe('PrizeCard', () => {
+ const defaultProps = {
+ imageSrc: '/images/lots/infuseur.png',
+ imageAlt: 'Infuseur à thé premium',
+ badge: '60%',
+ title: 'Infuseur à thé premium',
+ description: 'Un infuseur en acier inoxydable de haute qualité',
+ };
+
+ it('should render prize card with all props', () => {
+ render();
+
+ expect(screen.getByText('Infuseur à thé premium')).toBeInTheDocument();
+ expect(screen.getByText('Un infuseur en acier inoxydable de haute qualité')).toBeInTheDocument();
+ expect(screen.getByText('60%')).toBeInTheDocument();
+ expect(screen.getByAltText('Infuseur à thé premium')).toBeInTheDocument();
+ });
+
+ it('should render image with correct src', () => {
+ render();
+
+ const image = screen.getByAltText('Infuseur à thé premium');
+ expect(image).toHaveAttribute('src', '/images/lots/infuseur.png');
+ });
+
+ it('should render with default styling for non-grand prix', () => {
+ const { container } = render();
+
+ const card = container.firstChild;
+ expect(card).toHaveClass('border-[#e5e4dc]');
+ expect(card).not.toHaveClass('border-[#d4a574]');
+ });
+
+ it('should render with grand prix styling when isGrandPrix is true', () => {
+ const { container } = render();
+
+ const card = container.firstChild;
+ expect(card).toHaveClass('border-[#d4a574]');
+ });
+
+ it('should apply custom className', () => {
+ const { container } = render();
+
+ const card = container.firstChild;
+ expect(card).toHaveClass('custom-class');
+ });
+
+ it('should render badge with different text', () => {
+ render();
+
+ expect(screen.getByText('1 an de THÉ')).toBeInTheDocument();
+ });
+
+ it('should have correct structure with image container and content', () => {
+ const { container } = render();
+
+ // Check image container exists
+ expect(container.querySelector('.aspect-square')).toBeInTheDocument();
+
+ // Check title is h3
+ const title = screen.getByText('Infuseur à thé premium');
+ expect(title.tagName).toBe('H3');
+ });
+});
diff --git a/__tests__/components/ui/StatCard.test.tsx b/__tests__/components/ui/StatCard.test.tsx
new file mode 100644
index 0000000..8c0b2d5
--- /dev/null
+++ b/__tests__/components/ui/StatCard.test.tsx
@@ -0,0 +1,96 @@
+/**
+ * Tests for the StatCard component
+ * @jest-environment jsdom
+ */
+
+import React from 'react';
+import { render, screen } from '@testing-library/react';
+import { StatCard } from '@/components/ui/StatCard';
+
+describe('StatCard', () => {
+ it('should render with title and value', () => {
+ render();
+
+ expect(screen.getByText('Total Tickets')).toBeInTheDocument();
+ expect(screen.getByText('150')).toBeInTheDocument();
+ });
+
+ it('should render string values', () => {
+ render();
+
+ expect(screen.getByText('Status')).toBeInTheDocument();
+ expect(screen.getByText('Active')).toBeInTheDocument();
+ });
+
+ it('should render with icon', () => {
+ const TestIcon = () => ;
+ render(} />);
+
+ expect(screen.getByTestId('test-icon')).toBeInTheDocument();
+ });
+
+ it('should render subtitle when provided', () => {
+ render();
+
+ expect(screen.getByText('Last 30 days')).toBeInTheDocument();
+ });
+
+ it('should have white background for card', () => {
+ const { container } = render();
+
+ const card = container.firstChild;
+ expect(card).toHaveClass('bg-white');
+ });
+
+ it('should apply blue color to value text by default', () => {
+ const { container } = render();
+
+ // The value text should have blue color
+ const valueText = container.querySelector('.text-blue-600');
+ expect(valueText).toBeInTheDocument();
+ });
+
+ it('should apply green color to value text', () => {
+ const { container } = render();
+
+ const valueText = container.querySelector('.text-green-600');
+ expect(valueText).toBeInTheDocument();
+ });
+
+ it('should apply yellow color to value text', () => {
+ const { container } = render();
+
+ const valueText = container.querySelector('.text-yellow-600');
+ expect(valueText).toBeInTheDocument();
+ });
+
+ it('should apply red color to value text', () => {
+ const { container } = render();
+
+ const valueText = container.querySelector('.text-red-600');
+ expect(valueText).toBeInTheDocument();
+ });
+
+ it('should apply purple color to value text', () => {
+ const { container } = render();
+
+ const valueText = container.querySelector('.text-purple-600');
+ expect(valueText).toBeInTheDocument();
+ });
+
+ it('should apply custom className', () => {
+ const { container } = render();
+
+ const card = container.firstChild;
+ expect(card).toHaveClass('custom-class');
+ });
+
+ it('should render formatted numbers with French locale', () => {
+ render();
+
+ // French locale uses non-breaking space as thousands separator
+ // The exact format may vary, so we just check the number is present
+ const valueElement = screen.getByText(/1.*000.*000/);
+ expect(valueElement).toBeInTheDocument();
+ });
+});
diff --git a/__tests__/components/ui/StatusBadge.test.tsx b/__tests__/components/ui/StatusBadge.test.tsx
new file mode 100644
index 0000000..b049b02
--- /dev/null
+++ b/__tests__/components/ui/StatusBadge.test.tsx
@@ -0,0 +1,160 @@
+/**
+ * Tests for the StatusBadge component
+ * @jest-environment jsdom
+ */
+
+import React from 'react';
+import { render, screen } from '@testing-library/react';
+import { StatusBadge, getRoleBadgeColor, getTicketStatusColor, getStatusColor } from '@/components/ui/StatusBadge';
+
+describe('StatusBadge', () => {
+ describe('ticket type', () => {
+ it('should render PENDING status', () => {
+ render();
+
+ expect(screen.getByText('En attente')).toBeInTheDocument();
+ });
+
+ it('should render CLAIMED status', () => {
+ render();
+
+ expect(screen.getByText('Réclamé')).toBeInTheDocument();
+ });
+
+ it('should render REJECTED status', () => {
+ render();
+
+ expect(screen.getByText('Rejeté')).toBeInTheDocument();
+ });
+
+ it('should show icon when showIcon is true', () => {
+ const { container } = render();
+
+ // Check that SVG icon is rendered
+ expect(container.querySelector('svg')).toBeInTheDocument();
+ });
+
+ it('should not show icon by default', () => {
+ const { container } = render();
+
+ // Check that no SVG icon is rendered
+ expect(container.querySelector('svg')).not.toBeInTheDocument();
+ });
+ });
+
+ describe('role type', () => {
+ it('should render ADMIN role', () => {
+ render();
+
+ expect(screen.getByText('Admin')).toBeInTheDocument();
+ });
+
+ it('should render EMPLOYEE role', () => {
+ render();
+
+ expect(screen.getByText('Employé')).toBeInTheDocument();
+ });
+
+ it('should render CLIENT role', () => {
+ render();
+
+ expect(screen.getByText('Client')).toBeInTheDocument();
+ });
+ });
+
+ describe('status type', () => {
+ it('should render active status', () => {
+ render();
+
+ expect(screen.getByText('Actif')).toBeInTheDocument();
+ });
+
+ it('should render inactive status', () => {
+ render();
+
+ expect(screen.getByText('Inactif')).toBeInTheDocument();
+ });
+
+ it('should render verified status', () => {
+ render();
+
+ expect(screen.getByText('Vérifié')).toBeInTheDocument();
+ });
+ });
+
+ describe('custom className', () => {
+ it('should apply custom className', () => {
+ const { container } = render(
+
+ );
+
+ expect(container.firstChild).toHaveClass('custom-class');
+ });
+ });
+});
+
+describe('getRoleBadgeColor', () => {
+ it('should return correct colors for ADMIN', () => {
+ const result = getRoleBadgeColor('ADMIN');
+ expect(result).toContain('red');
+ });
+
+ it('should return correct colors for EMPLOYEE', () => {
+ const result = getRoleBadgeColor('EMPLOYEE');
+ expect(result).toContain('blue');
+ });
+
+ it('should return correct colors for CLIENT', () => {
+ const result = getRoleBadgeColor('CLIENT');
+ expect(result).toContain('green');
+ });
+
+ it('should return default colors for unknown role', () => {
+ const result = getRoleBadgeColor('UNKNOWN');
+ expect(result).toContain('gray');
+ });
+});
+
+describe('getTicketStatusColor', () => {
+ it('should return correct colors for PENDING', () => {
+ const result = getTicketStatusColor('PENDING');
+ expect(result).toContain('yellow');
+ });
+
+ it('should return correct colors for CLAIMED', () => {
+ const result = getTicketStatusColor('CLAIMED');
+ expect(result).toContain('green');
+ });
+
+ it('should return correct colors for REJECTED', () => {
+ const result = getTicketStatusColor('REJECTED');
+ expect(result).toContain('red');
+ });
+
+ it('should return default colors for unknown status', () => {
+ const result = getTicketStatusColor('UNKNOWN');
+ expect(result).toContain('gray');
+ });
+});
+
+describe('getStatusColor', () => {
+ it('should return correct colors for active', () => {
+ const result = getStatusColor('active');
+ expect(result).toContain('green');
+ });
+
+ it('should return correct colors for inactive', () => {
+ const result = getStatusColor('inactive');
+ expect(result).toContain('gray');
+ });
+
+ it('should return correct colors for verified', () => {
+ const result = getStatusColor('verified');
+ expect(result).toContain('green');
+ });
+
+ it('should return default colors for unknown status', () => {
+ const result = getStatusColor('unknown');
+ expect(result).toContain('gray');
+ });
+});