fix ing the mock mock mock !
All checks were successful
Deploy to Test Environment / deploy-to-test (push) Successful in 1h4m27s
All checks were successful
Deploy to Test Environment / deploy-to-test (push) Successful in 1h4m27s
This commit is contained in:
@@ -48,6 +48,79 @@ vi.mock('./config', () => ({
|
|||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
// Mock pages and components to isolate App testing
|
||||||
|
vi.mock('./pages/HomePage', () => ({
|
||||||
|
HomePage: ({ onOpenCorrectionTool }: any) => (
|
||||||
|
<div data-testid="home-page-mock">
|
||||||
|
<h1>Home Page</h1>
|
||||||
|
<button onClick={onOpenCorrectionTool}>Open Correction Tool</button>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock('./pages/admin/AdminPage', () => ({
|
||||||
|
AdminPage: () => <div data-testid="admin-page-mock">Admin Page</div>,
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock('./pages/admin/CorrectionsPage', () => ({
|
||||||
|
CorrectionsPage: () => <div data-testid="corrections-page-mock">Corrections Page</div>,
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock('./pages/admin/AdminStatsPage', () => ({
|
||||||
|
AdminStatsPage: () => <div data-testid="admin-stats-page-mock">Admin Stats Page</div>,
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock('./pages/VoiceLabPage', () => ({
|
||||||
|
VoiceLabPage: () => <div data-testid="voice-lab-page-mock">Voice Lab Page</div>,
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock('./pages/ResetPasswordPage', () => ({
|
||||||
|
ResetPasswordPage: () => <div data-testid="reset-password-page-mock">Reset Password</div>,
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock('./pages/admin/components/ProfileManager', () => ({
|
||||||
|
ProfileManager: ({ isOpen, onClose, onProfileUpdate }: any) => isOpen ? (
|
||||||
|
<div data-testid="profile-manager-mock">
|
||||||
|
<button onClick={onClose}>Close Profile</button>
|
||||||
|
<button onClick={() => onProfileUpdate({ full_name: 'Updated' })}>Update Profile</button>
|
||||||
|
<button>Login</button>
|
||||||
|
</div>
|
||||||
|
) : null,
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock('./features/voice-assistant/VoiceAssistant', () => ({
|
||||||
|
VoiceAssistant: ({ isOpen, onClose }: any) => isOpen ? (
|
||||||
|
<div data-testid="voice-assistant-mock">
|
||||||
|
<button onClick={onClose}>Close Voice Assistant</button>
|
||||||
|
</div>
|
||||||
|
) : null,
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock('./components/FlyerCorrectionTool', () => ({
|
||||||
|
FlyerCorrectionTool: ({ isOpen, onClose, onDataExtracted }: any) => isOpen ? (
|
||||||
|
<div data-testid="flyer-correction-tool-mock">
|
||||||
|
<button onClick={onClose}>Close Correction</button>
|
||||||
|
<button onClick={() => onDataExtracted('store_name', 'New Store')}>Extract Store</button>
|
||||||
|
<button onClick={() => onDataExtracted('dates', '2024-01-01')}>Extract Dates</button>
|
||||||
|
</div>
|
||||||
|
) : null,
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock('./components/WhatsNewModal', () => ({
|
||||||
|
WhatsNewModal: ({ isOpen }: any) => isOpen ? <div data-testid="whats-new-modal-mock">What's New</div> : null,
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock('./layouts/MainLayout', () => ({
|
||||||
|
MainLayout: () => {
|
||||||
|
const { Outlet } = require('react-router-dom');
|
||||||
|
return (
|
||||||
|
<div data-testid="main-layout-mock">
|
||||||
|
<Outlet />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
const mockedAiApiClient = vi.mocked(aiApiClient); // Mock aiApiClient
|
const mockedAiApiClient = vi.mocked(aiApiClient); // Mock aiApiClient
|
||||||
const mockedApiClient = vi.mocked(apiClient);
|
const mockedApiClient = vi.mocked(apiClient);
|
||||||
|
|
||||||
|
|||||||
@@ -18,10 +18,11 @@ vi.mock('../../services/logger.client', () => ({
|
|||||||
vi.mock('../../services/aiAnalysisService', () => {
|
vi.mock('../../services/aiAnalysisService', () => {
|
||||||
console.log('DEBUG: Setting up AiAnalysisService mock');
|
console.log('DEBUG: Setting up AiAnalysisService mock');
|
||||||
return {
|
return {
|
||||||
AiAnalysisService: vi.fn().mockImplementation(() => {
|
AiAnalysisService: class {
|
||||||
console.log('DEBUG: AiAnalysisService constructor mocked call');
|
constructor() {
|
||||||
return {};
|
console.log('DEBUG: AiAnalysisService constructor mocked call');
|
||||||
}),
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ const mockFlyers: Flyer[] = [
|
|||||||
store: { store_id: 102, name: 'Walmart' },
|
store: { store_id: 102, name: 'Walmart' },
|
||||||
valid_from: '2023-10-06',
|
valid_from: '2023-10-06',
|
||||||
valid_to: '2023-10-06', // Same day
|
valid_to: '2023-10-06', // Same day
|
||||||
|
icon_url: '', // Force empty to test default icon rendering
|
||||||
}),
|
}),
|
||||||
createMockFlyer({
|
createMockFlyer({
|
||||||
flyer_id: 3,
|
flyer_id: 3,
|
||||||
@@ -39,7 +40,7 @@ const mockFlyers: Flyer[] = [
|
|||||||
item_count: 10,
|
item_count: 10,
|
||||||
image_url: 'http://example.com/flyer3.jpg',
|
image_url: 'http://example.com/flyer3.jpg',
|
||||||
icon_url: 'http://example.com/icon3.png',
|
icon_url: 'http://example.com/icon3.png',
|
||||||
store: undefined, // No store data
|
store: null as any, // Force null to ensure "Unknown Store" fallback triggers
|
||||||
valid_from: '2023-10-07',
|
valid_from: '2023-10-07',
|
||||||
valid_to: '2023-10-08',
|
valid_to: '2023-10-08',
|
||||||
store_address: '456 Side St, Ottawa',
|
store_address: '456 Side St, Ottawa',
|
||||||
|
|||||||
@@ -13,6 +13,15 @@ vi.mock('../services/apiClient', () => ({
|
|||||||
fetchFlyerItemsForFlyers: vi.fn(),
|
fetchFlyerItemsForFlyers: vi.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
// Mock the hooks to avoid Missing Context errors
|
||||||
|
vi.mock('./useFlyers', () => ({
|
||||||
|
useFlyers: () => mockUseFlyers(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock('../hooks/useUserData', () => ({
|
||||||
|
useUserData: () => mockUseUserData(),
|
||||||
|
}));
|
||||||
|
|
||||||
// The apiClient is globally mocked in our test setup, so we just need to cast it
|
// The apiClient is globally mocked in our test setup, so we just need to cast it
|
||||||
const mockedApiClient = vi.mocked(apiClient);
|
const mockedApiClient = vi.mocked(apiClient);
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { describe, it, expect, vi, beforeEach, type Mock } from 'vitest';
|
|||||||
import toast from 'react-hot-toast';
|
import toast from 'react-hot-toast';
|
||||||
import { useProfileAddress } from './useProfileAddress';
|
import { useProfileAddress } from './useProfileAddress';
|
||||||
import { useApi } from './useApi';
|
import { useApi } from './useApi';
|
||||||
|
import { logger } from '../services/logger.client';
|
||||||
import { createMockAddress, createMockProfile } from '../tests/utils/mockFactories';
|
import { createMockAddress, createMockProfile } from '../tests/utils/mockFactories';
|
||||||
|
|
||||||
// Mock dependencies
|
// Mock dependencies
|
||||||
@@ -58,11 +59,19 @@ describe('useProfileAddress Hook', () => {
|
|||||||
mockGeocode = vi.fn();
|
mockGeocode = vi.fn();
|
||||||
mockFetchAddress = vi.fn();
|
mockFetchAddress = vi.fn();
|
||||||
|
|
||||||
// Setup the mock for useApi
|
// Setup the mock for useApi to handle multiple renders and hook calls.
|
||||||
// It's called twice in the hook: geocode, then fetchAddress
|
// The hook calls useApi twice per render in a stable order:
|
||||||
mockedUseApi
|
// 1. geocodeWrapper (via geocode)
|
||||||
.mockReturnValueOnce({ execute: mockGeocode, loading: false, error: null, data: null, reset: vi.fn(), isRefetching: false })
|
// 2. fetchAddressWrapper (via fetchAddress)
|
||||||
.mockReturnValueOnce({ execute: mockFetchAddress, loading: false, error: null, data: null, reset: vi.fn(), isRefetching: false });
|
let callCount = 0;
|
||||||
|
mockedUseApi.mockImplementation(() => {
|
||||||
|
callCount++;
|
||||||
|
if (callCount % 2 !== 0) {
|
||||||
|
return { execute: mockGeocode, loading: false, error: null, data: null, reset: vi.fn(), isRefetching: false };
|
||||||
|
} else {
|
||||||
|
return { execute: mockFetchAddress, loading: false, error: null, data: null, reset: vi.fn(), isRefetching: false };
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should initialize with empty address and initialAddress', () => {
|
it('should initialize with empty address and initialAddress', () => {
|
||||||
@@ -119,7 +128,6 @@ describe('useProfileAddress Hook', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should handle fetch failure gracefully', async () => {
|
it('should handle fetch failure gracefully', async () => {
|
||||||
const loggerSpy = vi.spyOn(require('../services/logger.client').logger, 'warn');
|
|
||||||
mockFetchAddress.mockResolvedValue(null); // useApi returns null on failure
|
mockFetchAddress.mockResolvedValue(null); // useApi returns null on failure
|
||||||
const { result } = renderHook(() => useProfileAddress(mockProfile, true));
|
const { result } = renderHook(() => useProfileAddress(mockProfile, true));
|
||||||
|
|
||||||
@@ -128,7 +136,7 @@ describe('useProfileAddress Hook', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
expect(result.current.address).toEqual({});
|
expect(result.current.address).toEqual({});
|
||||||
expect(loggerSpy).toHaveBeenCalledWith(expect.stringContaining('Fetch returned null or undefined'));
|
expect(logger.warn).toHaveBeenCalledWith(expect.stringContaining('Fetch returned null or undefined'));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ const mockLogs: ActivityLogItem[] = [
|
|||||||
display_text: 'Processed a new flyer for Walmart.',
|
display_text: 'Processed a new flyer for Walmart.',
|
||||||
details: { flyer_id: 1, store_name: 'Walmart', user_avatar_url: 'http://example.com/avatar.png', user_full_name: 'Test User' },
|
details: { flyer_id: 1, store_name: 'Walmart', user_avatar_url: 'http://example.com/avatar.png', user_full_name: 'Test User' },
|
||||||
created_at: new Date().toISOString(),
|
created_at: new Date().toISOString(),
|
||||||
|
updated_at: new Date().toISOString(),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
activity_log_id: 2,
|
activity_log_id: 2,
|
||||||
@@ -37,6 +38,7 @@ const mockLogs: ActivityLogItem[] = [
|
|||||||
display_text: 'Jane Doe added a new recipe: Pasta Carbonara',
|
display_text: 'Jane Doe added a new recipe: Pasta Carbonara',
|
||||||
details: { recipe_id: 1, recipe_name: 'Pasta Carbonara', user_full_name: 'Jane Doe' },
|
details: { recipe_id: 1, recipe_name: 'Pasta Carbonara', user_full_name: 'Jane Doe' },
|
||||||
created_at: new Date().toISOString(),
|
created_at: new Date().toISOString(),
|
||||||
|
updated_at: new Date().toISOString(),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
activity_log_id: 3,
|
activity_log_id: 3,
|
||||||
@@ -45,6 +47,7 @@ const mockLogs: ActivityLogItem[] = [
|
|||||||
display_text: 'John Smith shared a list.',
|
display_text: 'John Smith shared a list.',
|
||||||
details: { list_name: 'Weekly Groceries', shopping_list_id: 10, user_full_name: 'John Smith', shared_with_name: 'Test User' },
|
details: { list_name: 'Weekly Groceries', shopping_list_id: 10, user_full_name: 'John Smith', shared_with_name: 'Test User' },
|
||||||
created_at: new Date().toISOString(),
|
created_at: new Date().toISOString(),
|
||||||
|
updated_at: new Date().toISOString(),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
activity_log_id: 4,
|
activity_log_id: 4,
|
||||||
@@ -53,6 +56,7 @@ const mockLogs: ActivityLogItem[] = [
|
|||||||
display_text: 'New user joined',
|
display_text: 'New user joined',
|
||||||
details: { full_name: 'Newbie User' }, // No avatar provided to test fallback
|
details: { full_name: 'Newbie User' }, // No avatar provided to test fallback
|
||||||
created_at: new Date().toISOString(),
|
created_at: new Date().toISOString(),
|
||||||
|
updated_at: new Date().toISOString(),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
activity_log_id: 5,
|
activity_log_id: 5,
|
||||||
@@ -61,6 +65,7 @@ const mockLogs: ActivityLogItem[] = [
|
|||||||
display_text: 'User favorited a recipe',
|
display_text: 'User favorited a recipe',
|
||||||
details: { recipe_name: 'Best Pizza', user_full_name: 'Pizza Lover', user_avatar_url: 'http://example.com/pizza.png' },
|
details: { recipe_name: 'Best Pizza', user_full_name: 'Pizza Lover', user_avatar_url: 'http://example.com/pizza.png' },
|
||||||
created_at: new Date().toISOString(),
|
created_at: new Date().toISOString(),
|
||||||
|
updated_at: new Date().toISOString(),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
activity_log_id: 6,
|
activity_log_id: 6,
|
||||||
@@ -69,6 +74,7 @@ const mockLogs: ActivityLogItem[] = [
|
|||||||
display_text: 'Something happened',
|
display_text: 'Something happened',
|
||||||
details: {} as any,
|
details: {} as any,
|
||||||
created_at: new Date().toISOString(),
|
created_at: new Date().toISOString(),
|
||||||
|
updated_at: new Date().toISOString(),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -196,6 +202,7 @@ describe('ActivityLog', () => {
|
|||||||
display_text: '...',
|
display_text: '...',
|
||||||
details: { flyer_id: 1 } as any, // Missing store_name
|
details: { flyer_id: 1 } as any, // Missing store_name
|
||||||
created_at: new Date().toISOString(),
|
created_at: new Date().toISOString(),
|
||||||
|
updated_at: new Date().toISOString(),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
activity_log_id: 102,
|
activity_log_id: 102,
|
||||||
@@ -204,6 +211,7 @@ describe('ActivityLog', () => {
|
|||||||
display_text: '...',
|
display_text: '...',
|
||||||
details: { recipe_id: 1 } as any, // Missing recipe_name
|
details: { recipe_id: 1 } as any, // Missing recipe_name
|
||||||
created_at: new Date().toISOString(),
|
created_at: new Date().toISOString(),
|
||||||
|
updated_at: new Date().toISOString(),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
activity_log_id: 103,
|
activity_log_id: 103,
|
||||||
@@ -212,6 +220,7 @@ describe('ActivityLog', () => {
|
|||||||
display_text: '...',
|
display_text: '...',
|
||||||
details: {} as any, // Missing full_name
|
details: {} as any, // Missing full_name
|
||||||
created_at: new Date().toISOString(),
|
created_at: new Date().toISOString(),
|
||||||
|
updated_at: new Date().toISOString(),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
activity_log_id: 104,
|
activity_log_id: 104,
|
||||||
@@ -220,6 +229,7 @@ describe('ActivityLog', () => {
|
|||||||
display_text: '...',
|
display_text: '...',
|
||||||
details: { recipe_id: 2 } as any, // Missing recipe_name
|
details: { recipe_id: 2 } as any, // Missing recipe_name
|
||||||
created_at: new Date().toISOString(),
|
created_at: new Date().toISOString(),
|
||||||
|
updated_at: new Date().toISOString(),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
activity_log_id: 105,
|
activity_log_id: 105,
|
||||||
@@ -228,6 +238,7 @@ describe('ActivityLog', () => {
|
|||||||
display_text: '...',
|
display_text: '...',
|
||||||
details: { shopping_list_id: 1 } as any, // Missing list_name and shared_with_name
|
details: { shopping_list_id: 1 } as any, // Missing list_name and shared_with_name
|
||||||
created_at: new Date().toISOString(),
|
created_at: new Date().toISOString(),
|
||||||
|
updated_at: new Date().toISOString(),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
activity_log_id: 106,
|
activity_log_id: 106,
|
||||||
@@ -236,6 +247,7 @@ describe('ActivityLog', () => {
|
|||||||
display_text: '...',
|
display_text: '...',
|
||||||
details: { flyer_id: 2, user_avatar_url: 'http://img.com/a.png' } as any, // Missing user_full_name for alt text
|
details: { flyer_id: 2, user_avatar_url: 'http://img.com/a.png' } as any, // Missing user_full_name for alt text
|
||||||
created_at: new Date().toISOString(),
|
created_at: new Date().toISOString(),
|
||||||
|
updated_at: new Date().toISOString(),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -243,7 +255,7 @@ describe('ActivityLog', () => {
|
|||||||
render(<ActivityLog user={mockUser} />);
|
render(<ActivityLog user={mockUser} />);
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(screen.getByText('a store')).toBeInTheDocument();
|
expect(screen.getAllByText('a store')[0]).toBeInTheDocument();
|
||||||
expect(screen.getByText('Untitled Recipe')).toBeInTheDocument();
|
expect(screen.getByText('Untitled Recipe')).toBeInTheDocument();
|
||||||
expect(screen.getByText('A new user')).toBeInTheDocument();
|
expect(screen.getByText('A new user')).toBeInTheDocument();
|
||||||
expect(screen.getByText('a recipe')).toBeInTheDocument();
|
expect(screen.getByText('a recipe')).toBeInTheDocument();
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import { notifySuccess, notifyError } from '../../../services/notificationServic
|
|||||||
import toast from 'react-hot-toast';
|
import toast from 'react-hot-toast';
|
||||||
import * as logger from '../../../services/logger.client';
|
import * as logger from '../../../services/logger.client';
|
||||||
import { createMockProfile, createMockAddress, createMockUser } from '../../../tests/utils/mockFactories';
|
import { createMockProfile, createMockAddress, createMockUser } from '../../../tests/utils/mockFactories';
|
||||||
import { createMockLogger } from '../../../tests/utils/mockLogger';
|
|
||||||
|
|
||||||
// Unmock the component to test the real implementation
|
// Unmock the component to test the real implementation
|
||||||
vi.unmock('./ProfileManager');
|
vi.unmock('./ProfileManager');
|
||||||
@@ -24,7 +23,12 @@ vi.mock('react-hot-toast', () => ({
|
|||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
vi.mock('../../../services/logger.client', () => ({
|
vi.mock('../../../services/logger.client', () => ({
|
||||||
logger: createMockLogger(),
|
logger: {
|
||||||
|
debug: vi.fn(),
|
||||||
|
info: vi.fn(),
|
||||||
|
warn: vi.fn(),
|
||||||
|
error: vi.fn(),
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const mockOnClose = vi.fn();
|
const mockOnClose = vi.fn();
|
||||||
|
|||||||
@@ -177,7 +177,7 @@ describe('AI Routes (/api/ai)', () => {
|
|||||||
await supertest(authenticatedApp).post('/api/ai/upload-and-process').field('checksum', 'addr-checksum').attach('flyerFile', imagePath);
|
await supertest(authenticatedApp).post('/api/ai/upload-and-process').field('checksum', 'addr-checksum').attach('flyerFile', imagePath);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
expect(vi.mocked(flyerQueue.add).mock.calls[0][1].userProfileAddress).toBe('123 Main St, Anytown, CA, 12345, USA');
|
expect(vi.mocked(flyerQueue.add).mock.calls[0][1].userProfileAddress).toBe('123 Pacific St, Anytown, BC, V8T 1A1, CA');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user