diff --git a/notes-to-ai4.txt b/notes-to-ai4.txt index 2580758b..fe189b11 100644 --- a/notes-to-ai4.txt +++ b/notes-to-ai4.txt @@ -13,6 +13,12 @@ RULES: +latest refacter + +Refactor `RecipeSuggester.test.tsx` to use `renderWithProviders`. +Create a new test file for `StatCard.tsx` to verify its props and rendering. + + UPC SCANNING ! diff --git a/src/components/AchievementsList.test.tsx b/src/components/AchievementsList.test.tsx index 90d98fa3..cf933454 100644 --- a/src/components/AchievementsList.test.tsx +++ b/src/components/AchievementsList.test.tsx @@ -1,9 +1,10 @@ // src/components/AchievementsList.test.tsx import React from 'react'; -import { render, screen } from '@testing-library/react'; +import { screen } from '@testing-library/react'; import { describe, it, expect } from 'vitest'; import { AchievementsList } from './AchievementsList'; import { createMockUserAchievement } from '../tests/utils/mockFactories'; +import { renderWithProviders } from '../tests/utils/renderWithProviders'; describe('AchievementsList', () => { it('should render the list of achievements with correct details', () => { @@ -24,7 +25,7 @@ describe('AchievementsList', () => { createMockUserAchievement({ achievement_id: 3, name: 'Unknown Achievement', icon: 'star' }), // This icon is not in the component's map ]; - render(); + renderWithProviders(); expect(screen.getByRole('heading', { name: /achievements/i })).toBeInTheDocument(); @@ -44,7 +45,7 @@ describe('AchievementsList', () => { }); it('should render a message when there are no achievements', () => { - render(); + renderWithProviders(); expect( screen.getByText('No achievements earned yet. Keep exploring to unlock them!'), ).toBeInTheDocument(); diff --git a/src/components/AdminRoute.test.tsx b/src/components/AdminRoute.test.tsx index 72847a25..cced3ceb 100644 --- a/src/components/AdminRoute.test.tsx +++ b/src/components/AdminRoute.test.tsx @@ -1,11 +1,12 @@ // src/components/AdminRoute.test.tsx import React from 'react'; -import { render, screen } from '@testing-library/react'; -import { describe, it, expect } from 'vitest'; -import { MemoryRouter, Routes, Route } from 'react-router-dom'; +import { screen } from '@testing-library/react'; +import { describe, it, expect, vi } from 'vitest'; +import { Routes, Route } from 'react-router-dom'; import { AdminRoute } from './AdminRoute'; import type { Profile } from '../types'; import { createMockProfile } from '../tests/utils/mockFactories'; +import { renderWithProviders } from '../tests/utils/renderWithProviders'; // Unmock the component to test the real implementation vi.unmock('./AdminRoute'); @@ -14,15 +15,14 @@ const AdminContent = () =>
Admin Page Content
; const HomePage = () =>
Home Page
; const renderWithRouter = (profile: Profile | null, initialPath: string) => { - render( - - - } /> - }> - } /> - - - , + renderWithProviders( + + } /> + }> + } /> + + , + { initialEntries: [initialPath] }, ); }; diff --git a/src/components/AnonymousUserBanner.test.tsx b/src/components/AnonymousUserBanner.test.tsx index ffbef8e2..41b1afb3 100644 --- a/src/components/AnonymousUserBanner.test.tsx +++ b/src/components/AnonymousUserBanner.test.tsx @@ -1,8 +1,9 @@ // src/components/AnonymousUserBanner.test.tsx import React from 'react'; -import { render, screen, fireEvent } from '@testing-library/react'; +import { screen, fireEvent } from '@testing-library/react'; import { describe, it, expect, vi } from 'vitest'; import { AnonymousUserBanner } from './AnonymousUserBanner'; +import { renderWithProviders } from '../tests/utils/renderWithProviders'; // Mock the icon to ensure it is rendered correctly vi.mock('./icons/InformationCircleIcon', () => ({ @@ -14,7 +15,7 @@ vi.mock('./icons/InformationCircleIcon', () => ({ describe('AnonymousUserBanner', () => { it('should render the banner with the correct text content and accessibility role', () => { const mockOnOpenProfile = vi.fn(); - render(); + renderWithProviders(); // Check for accessibility role expect(screen.getByRole('alert')).toBeInTheDocument(); @@ -30,7 +31,7 @@ describe('AnonymousUserBanner', () => { it('should call onOpenProfile when the "sign up or log in" button is clicked', () => { const mockOnOpenProfile = vi.fn(); - render(); + renderWithProviders(); const loginButton = screen.getByRole('button', { name: /sign up or log in/i }); fireEvent.click(loginButton); diff --git a/src/components/AppGuard.test.tsx b/src/components/AppGuard.test.tsx index f3c75e0d..9650da2d 100644 --- a/src/components/AppGuard.test.tsx +++ b/src/components/AppGuard.test.tsx @@ -1,14 +1,16 @@ // src/components/AppGuard.test.tsx import React from 'react'; -import { render, screen, waitFor } from '@testing-library/react'; +import { screen, waitFor } from '@testing-library/react'; import { describe, it, expect, vi, beforeEach } from 'vitest'; import { AppGuard } from './AppGuard'; import { useAppInitialization } from '../hooks/useAppInitialization'; import { useModal } from '../hooks/useModal'; +import { renderWithProviders } from '../tests/utils/renderWithProviders'; // Mock dependencies vi.mock('../hooks/useAppInitialization'); vi.mock('../hooks/useModal'); +vi.mock('../services/apiClient'); vi.mock('./WhatsNewModal', () => ({ WhatsNewModal: ({ isOpen }: { isOpen: boolean }) => isOpen ?
: null, @@ -38,7 +40,7 @@ describe('AppGuard', () => { }); it('should render children', () => { - render( + renderWithProviders(
Child Content
, @@ -51,7 +53,7 @@ describe('AppGuard', () => { ...mockedUseModal(), isModalOpen: (modalId) => modalId === 'whatsNew', }); - render( + renderWithProviders(
Child
, @@ -64,7 +66,7 @@ describe('AppGuard', () => { isDarkMode: true, unitSystem: 'imperial', }); - render( + renderWithProviders(
Child
, @@ -78,7 +80,7 @@ describe('AppGuard', () => { }); it('should set light mode styles for toaster', async () => { - render( + renderWithProviders(
Child
, diff --git a/src/components/ConfirmationModal.test.tsx b/src/components/ConfirmationModal.test.tsx index 4c3d95f2..d3deb336 100644 --- a/src/components/ConfirmationModal.test.tsx +++ b/src/components/ConfirmationModal.test.tsx @@ -1,8 +1,9 @@ // src/components/ConfirmationModal.test.tsx import React from 'react'; -import { render, screen, fireEvent } from '@testing-library/react'; +import { screen, fireEvent } from '@testing-library/react'; import { describe, it, expect, vi, beforeEach } from 'vitest'; import { ConfirmationModal } from './ConfirmationModal'; +import { renderWithProviders } from '../tests/utils/renderWithProviders'; describe('ConfirmationModal (in components)', () => { const mockOnClose = vi.fn(); @@ -21,12 +22,12 @@ describe('ConfirmationModal (in components)', () => { }); it('should not render when isOpen is false', () => { - const { container } = render(); + const { container } = renderWithProviders(); expect(container.firstChild).toBeNull(); }); it('should render correctly when isOpen is true', () => { - render(); + renderWithProviders(); expect(screen.getByRole('heading', { name: 'Confirm Action' })).toBeInTheDocument(); expect(screen.getByText('Are you sure you want to do this?')).toBeInTheDocument(); expect(screen.getByRole('button', { name: 'Confirm' })).toBeInTheDocument(); @@ -34,38 +35,38 @@ describe('ConfirmationModal (in components)', () => { }); it('should call onConfirm when the confirm button is clicked', () => { - render(); + renderWithProviders(); fireEvent.click(screen.getByRole('button', { name: 'Confirm' })); expect(mockOnConfirm).toHaveBeenCalledTimes(1); }); it('should call onClose when the cancel button is clicked', () => { - render(); + renderWithProviders(); fireEvent.click(screen.getByRole('button', { name: 'Cancel' })); expect(mockOnClose).toHaveBeenCalledTimes(1); }); it('should call onClose when the close icon is clicked', () => { - render(); + renderWithProviders(); fireEvent.click(screen.getByLabelText('Close confirmation modal')); expect(mockOnClose).toHaveBeenCalledTimes(1); }); it('should call onClose when the overlay is clicked', () => { - render(); + renderWithProviders(); // The overlay is the parent of the modal content div fireEvent.click(screen.getByRole('dialog')); expect(mockOnClose).toHaveBeenCalledTimes(1); }); it('should not call onClose when clicking inside the modal content', () => { - render(); + renderWithProviders(); fireEvent.click(screen.getByText('Are you sure you want to do this?')); expect(mockOnClose).not.toHaveBeenCalled(); }); it('should render custom button text and classes', () => { - render( + renderWithProviders( ({ @@ -20,7 +21,7 @@ describe('DarkModeToggle', () => { }); it('should render in light mode state', () => { - render(); + renderWithProviders(); const checkbox = screen.getByRole('checkbox'); expect(checkbox).not.toBeChecked(); @@ -29,7 +30,7 @@ describe('DarkModeToggle', () => { }); it('should render in dark mode state', () => { - render(); + renderWithProviders(); const checkbox = screen.getByRole('checkbox'); expect(checkbox).toBeChecked(); @@ -38,7 +39,7 @@ describe('DarkModeToggle', () => { }); it('should call onToggle when the label is clicked', () => { - render(); + renderWithProviders(); // Clicking the label triggers the checkbox change const label = screen.getByTitle('Switch to Dark Mode'); diff --git a/src/components/Dashboard.test.tsx b/src/components/Dashboard.test.tsx new file mode 100644 index 00000000..ee445094 --- /dev/null +++ b/src/components/Dashboard.test.tsx @@ -0,0 +1,67 @@ +// src/components/Dashboard.test.tsx +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { screen } from '@testing-library/react'; +import { Dashboard } from './Dashboard'; +import { renderWithProviders } from '../tests/utils/renderWithProviders'; + +// Mock child components to isolate Dashboard logic +// Note: The Dashboard component imports these using '../components/RecipeSuggester' +// which resolves to the same file as './RecipeSuggester' when inside src/components. +vi.mock('./RecipeSuggester', () => ({ + RecipeSuggester: () =>
Recipe Suggester
, +})); + +vi.mock('./FlyerCountDisplay', () => ({ + FlyerCountDisplay: () =>
Flyer Count Display
, +})); + +vi.mock('./Leaderboard', () => ({ + Leaderboard: () =>
Leaderboard
, +})); + +describe('Dashboard Component', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('renders the dashboard title', () => { + console.log('TEST: Verifying dashboard title render'); + renderWithProviders(); + expect(screen.getByRole('heading', { name: /dashboard/i, level: 1 })).toBeInTheDocument(); + }); + + it('renders the RecipeSuggester widget', () => { + console.log('TEST: Verifying RecipeSuggester presence'); + renderWithProviders(); + expect(screen.getByTestId('recipe-suggester-mock')).toBeInTheDocument(); + }); + + it('renders the FlyerCountDisplay widget within the "Your Flyers" section', () => { + console.log('TEST: Verifying FlyerCountDisplay presence and section title'); + renderWithProviders(); + + // Check for the section heading + expect(screen.getByRole('heading', { name: /your flyers/i, level: 2 })).toBeInTheDocument(); + + // Check for the component + expect(screen.getByTestId('flyer-count-display-mock')).toBeInTheDocument(); + }); + + it('renders the Leaderboard widget in the sidebar area', () => { + console.log('TEST: Verifying Leaderboard presence'); + renderWithProviders(); + expect(screen.getByTestId('leaderboard-mock')).toBeInTheDocument(); + }); + + it('renders with the correct grid layout classes', () => { + console.log('TEST: Verifying layout classes'); + const { container } = renderWithProviders(); + + // The main grid container + const gridContainer = container.querySelector('.grid'); + expect(gridContainer).toBeInTheDocument(); + expect(gridContainer).toHaveClass('grid-cols-1'); + expect(gridContainer).toHaveClass('lg:grid-cols-3'); + expect(gridContainer).toHaveClass('gap-6'); + }); +}); \ No newline at end of file diff --git a/src/components/ErrorDisplay.test.tsx b/src/components/ErrorDisplay.test.tsx index 20bb7520..30efc02a 100644 --- a/src/components/ErrorDisplay.test.tsx +++ b/src/components/ErrorDisplay.test.tsx @@ -1,24 +1,25 @@ // src/components/ErrorDisplay.test.tsx import React from 'react'; -import { render, screen } from '@testing-library/react'; +import { screen } from '@testing-library/react'; import { describe, it, expect } from 'vitest'; import { ErrorDisplay } from './ErrorDisplay'; +import { renderWithProviders } from '../tests/utils/renderWithProviders'; describe('ErrorDisplay (in components)', () => { it('should not render when the message is empty', () => { - const { container } = render(); + const { container } = renderWithProviders(); expect(container.firstChild).toBeNull(); }); it('should not render when the message is null', () => { // The component expects a string, but we test for nullish values as a safeguard. - const { container } = render(); + const { container } = renderWithProviders(); expect(container.firstChild).toBeNull(); }); it('should render the error message when provided', () => { const errorMessage = 'Something went terribly wrong.'; - render(); + renderWithProviders(); const alert = screen.getByRole('alert'); expect(alert).toBeInTheDocument(); diff --git a/src/components/FlyerCorrectionTool.test.tsx b/src/components/FlyerCorrectionTool.test.tsx index 0965f8a3..370ad30c 100644 --- a/src/components/FlyerCorrectionTool.test.tsx +++ b/src/components/FlyerCorrectionTool.test.tsx @@ -1,10 +1,11 @@ // src/components/FlyerCorrectionTool.test.tsx import React from 'react'; -import { render, screen, fireEvent, waitFor, act } from '@testing-library/react'; +import { screen, fireEvent, waitFor, act } from '@testing-library/react'; import { describe, it, expect, vi, beforeEach, type Mocked } from 'vitest'; import { FlyerCorrectionTool } from './FlyerCorrectionTool'; import * as aiApiClient from '../services/aiApiClient'; import { notifyError, notifySuccess } from '../services/notificationService'; +import { renderWithProviders } from '../tests/utils/renderWithProviders'; // Unmock the component to test the real implementation vi.unmock('./FlyerCorrectionTool'); @@ -54,12 +55,12 @@ describe('FlyerCorrectionTool', () => { }); it('should not render when isOpen is false', () => { - const { container } = render(); + const { container } = renderWithProviders(); expect(container.firstChild).toBeNull(); }); it('should render correctly when isOpen is true', () => { - render(); + renderWithProviders(); expect(screen.getByRole('heading', { name: /flyer correction tool/i })).toBeInTheDocument(); expect(screen.getByAltText('Flyer for correction')).toBeInTheDocument(); expect(screen.getByRole('button', { name: /extract store name/i })).toBeInTheDocument(); @@ -67,7 +68,7 @@ describe('FlyerCorrectionTool', () => { }); it('should call onClose when the close button is clicked', () => { - render(); + renderWithProviders(); // Use the specific aria-label defined in the component to find the close button const closeButton = screen.getByLabelText(/close correction tool/i); fireEvent.click(closeButton); @@ -75,13 +76,13 @@ describe('FlyerCorrectionTool', () => { }); it('should have disabled extraction buttons initially', () => { - render(); + renderWithProviders(); expect(screen.getByRole('button', { name: /extract store name/i })).toBeDisabled(); expect(screen.getByRole('button', { name: /extract sale dates/i })).toBeDisabled(); }); it('should enable extraction buttons after a selection is made', () => { - render(); + renderWithProviders(); const canvas = screen.getByRole('dialog').querySelector('canvas')!; // Simulate drawing a rectangle @@ -94,7 +95,7 @@ describe('FlyerCorrectionTool', () => { }); it('should stop drawing when the mouse leaves the canvas', () => { - render(); + renderWithProviders(); const canvas = screen.getByRole('dialog').querySelector('canvas')!; fireEvent.mouseDown(canvas, { clientX: 10, clientY: 10 }); @@ -114,7 +115,7 @@ describe('FlyerCorrectionTool', () => { }); mockedAiApiClient.rescanImageArea.mockReturnValue(rescanPromise); - render(); + renderWithProviders(); // Wait for the image fetch to complete to ensure 'imageFile' state is populated console.log('--- [TEST LOG] ---: Awaiting image fetch inside component...'); @@ -192,7 +193,7 @@ describe('FlyerCorrectionTool', () => { // Mock fetch to reject global.fetch = vi.fn(() => Promise.reject(new Error('Network error'))) as Mocked; - render(); + renderWithProviders(); await waitFor(() => { expect(mockedNotifyError).toHaveBeenCalledWith('Could not load the image for correction.'); @@ -211,7 +212,7 @@ describe('FlyerCorrectionTool', () => { return new Promise(() => {}); }) as Mocked; - render(); + renderWithProviders(); const canvas = screen.getByRole('dialog').querySelector('canvas')!; @@ -238,7 +239,7 @@ describe('FlyerCorrectionTool', () => { it('should handle non-standard API errors during rescan', async () => { console.log('TEST: Starting "should handle non-standard API errors during rescan"'); mockedAiApiClient.rescanImageArea.mockRejectedValue('A plain string error'); - render(); + renderWithProviders(); // Wait for image fetch to ensure imageFile is set before we interact await waitFor(() => expect(global.fetch).toHaveBeenCalled()); diff --git a/src/components/FlyerCountDisplay.test.tsx b/src/components/FlyerCountDisplay.test.tsx index 8f893925..485f6c09 100644 --- a/src/components/FlyerCountDisplay.test.tsx +++ b/src/components/FlyerCountDisplay.test.tsx @@ -1,11 +1,12 @@ // src/components/FlyerCountDisplay.test.tsx import React from 'react'; -import { render, screen } from '@testing-library/react'; +import { screen } from '@testing-library/react'; import { describe, it, expect, vi, beforeEach } from 'vitest'; import { FlyerCountDisplay } from './FlyerCountDisplay'; import { useFlyers } from '../hooks/useFlyers'; import type { Flyer } from '../types'; import { createMockFlyer } from '../tests/utils/mockFactories'; +import { renderWithProviders } from '../tests/utils/renderWithProviders'; // Mock the dependencies vi.mock('../hooks/useFlyers'); @@ -32,7 +33,7 @@ describe('FlyerCountDisplay', () => { }); // Act: Render the component. - render(); + renderWithProviders(); // Assert: Check that the loading spinner is visible. expect(screen.getByTestId('loading-spinner')).toBeInTheDocument(); @@ -53,7 +54,7 @@ describe('FlyerCountDisplay', () => { }); // Act - render(); + renderWithProviders(); // Assert: Check that the error message is displayed. expect(screen.getByRole('alert')).toHaveTextContent(errorMessage); @@ -73,7 +74,7 @@ describe('FlyerCountDisplay', () => { }); // Act - render(); + renderWithProviders(); // Assert: Check that the correct count is displayed. const countDisplay = screen.getByTestId('flyer-count'); diff --git a/src/components/Footer.test.tsx b/src/components/Footer.test.tsx index c5801012..08e38782 100644 --- a/src/components/Footer.test.tsx +++ b/src/components/Footer.test.tsx @@ -1,8 +1,9 @@ // src/components/Footer.test.tsx import React from 'react'; -import { render, screen } from '@testing-library/react'; +import { screen } from '@testing-library/react'; import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import { Footer } from './Footer'; +import { renderWithProviders } from '../tests/utils/renderWithProviders'; describe('Footer', () => { beforeEach(() => { @@ -21,7 +22,7 @@ describe('Footer', () => { vi.setSystemTime(mockDate); // Act: Render the component - render(