// src/pages/MyDealsPage.test.tsx import React from 'react'; import { render, screen, waitFor } from '@testing-library/react'; import { describe, it, expect, vi, beforeEach, type Mocked } from 'vitest'; import MyDealsPage from './MyDealsPage'; import * as apiClient from '../services/apiClient'; import type { WatchedItemDeal } from '../types'; import { logger } from '../services/logger.client'; import { createMockWatchedItemDeal } from '../tests/utils/mockFactories'; // The apiClient is mocked globally in `src/tests/setup/globalApiMock.ts`. const mockedApiClient = vi.mocked(apiClient); // Mock lucide-react icons to prevent rendering errors in the test environment vi.mock('lucide-react', () => ({ AlertCircle: () =>
, Tag: () =>
, Store: () =>
, Calendar: () =>
, })); describe('MyDealsPage', () => { beforeEach(() => { vi.clearAllMocks(); }); it('should display a loading message initially', () => { // Mock a pending promise mockedApiClient.fetchBestSalePrices.mockReturnValue(new Promise(() => {})); render(); expect(screen.getByText('Loading your deals...')).toBeInTheDocument(); }); it('should display an error message if the API call fails', async () => { mockedApiClient.fetchBestSalePrices.mockResolvedValue( new Response(null, { status: 500, statusText: 'Server Error' }), ); render(); await waitFor(() => { expect(screen.getByText('Error')).toBeInTheDocument(); expect( screen.getByText('Failed to fetch deals. Please try again later.'), ).toBeInTheDocument(); }); expect(logger.error).toHaveBeenCalledWith( 'Error fetching watched item deals:', 'Failed to fetch deals. Please try again later.', ); }); it('should handle network errors and log them', async () => { const networkError = new Error('Network connection failed'); mockedApiClient.fetchBestSalePrices.mockRejectedValue(networkError); render(); await waitFor(() => { expect(screen.getByText('Error')).toBeInTheDocument(); expect(screen.getByText('Network connection failed')).toBeInTheDocument(); }); expect(logger.error).toHaveBeenCalledWith( 'Error fetching watched item deals:', 'Network connection failed', ); }); it('should handle unknown errors and log them', async () => { // Mock a rejection with a non-Error object (e.g., a string) to trigger the fallback error message mockedApiClient.fetchBestSalePrices.mockRejectedValue('Unknown failure'); render(); await waitFor(() => { expect(screen.getByText('Error')).toBeInTheDocument(); expect(screen.getByText('An unknown error occurred.')).toBeInTheDocument(); }); expect(logger.error).toHaveBeenCalledWith( 'Error fetching watched item deals:', 'An unknown error occurred.', ); }); it('should display a message when no deals are found', async () => { mockedApiClient.fetchBestSalePrices.mockResolvedValue( new Response(JSON.stringify([]), { headers: { 'Content-Type': 'application/json' }, }), ); render(); await waitFor(() => { expect( screen.getByText('No deals found for your watched items right now.'), ).toBeInTheDocument(); }); }); it('should render the list of deals on successful fetch', async () => { const mockDeals: WatchedItemDeal[] = [ createMockWatchedItemDeal({ master_item_id: 1, item_name: 'Organic Bananas', best_price_in_cents: 99, store_name: 'Green Grocer', flyer_id: 101, valid_to: '2024-10-20', }), createMockWatchedItemDeal({ master_item_id: 2, item_name: 'Almond Milk', best_price_in_cents: 349, store_name: 'SuperMart', flyer_id: 102, valid_to: '2024-10-22', }), ]; mockedApiClient.fetchBestSalePrices.mockResolvedValue( new Response(JSON.stringify(mockDeals), { headers: { 'Content-Type': 'application/json' }, }), ); render(); await waitFor(() => { expect(screen.getByText('Organic Bananas')).toBeInTheDocument(); expect(screen.getByText('$0.99')).toBeInTheDocument(); expect(screen.getByText('Almond Milk')).toBeInTheDocument(); expect(screen.getByText('$3.49')).toBeInTheDocument(); expect(screen.getByText('Green Grocer')).toBeInTheDocument(); }); }); });