lootsa tests fixes
All checks were successful
Deploy to Web Server flyer-crawler.projectium.com / deploy (push) Successful in 4m37s
All checks were successful
Deploy to Web Server flyer-crawler.projectium.com / deploy (push) Successful in 4m37s
This commit is contained in:
@@ -6,6 +6,12 @@ import { MemoryRouter, Outlet } from 'react-router-dom';
|
||||
import App from './App';
|
||||
import * as apiClient from './services/apiClient';
|
||||
|
||||
// Mock useAuth to allow overriding the user state in tests
|
||||
const mockUseAuth = vi.fn();
|
||||
vi.mock('./hooks/useAuth', () => ({
|
||||
useAuth: () => mockUseAuth(),
|
||||
}));
|
||||
|
||||
// Mock child components to isolate the App component
|
||||
vi.mock('./features/flyer/FlyerDisplay', () => ({ FlyerDisplay: () => <div data-testid="flyer-display-mock">Flyer Display</div> }));
|
||||
vi.mock('./features/flyer/ExtractedDataTable', () => ({ ExtractedDataTable: () => <div data-testid="extracted-data-table-mock">Extracted Data Table</div> }));
|
||||
@@ -51,6 +57,14 @@ const mockedApiClient = apiClient as Mocked<typeof apiClient>;
|
||||
describe('App Component', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
// Default auth state: loading or guest
|
||||
mockUseAuth.mockReturnValue({
|
||||
user: null,
|
||||
profile: null,
|
||||
authStatus: 'Determining...',
|
||||
isLoading: true,
|
||||
});
|
||||
|
||||
// Clear local storage to prevent auth state from leaking between tests.
|
||||
localStorage.clear();
|
||||
// Default mocks for API calls
|
||||
@@ -84,7 +98,7 @@ describe('App Component', () => {
|
||||
});
|
||||
|
||||
it('should render the BulkImporter for an admin user', async () => {
|
||||
// FIX 4: Update the BulkImporter test case to simulate an admin user
|
||||
// FIX 4: Update the BulkImporter test case to simulate an admin user via useAuth mock
|
||||
const mockAdminProfile = {
|
||||
user_id: 'admin-id',
|
||||
user: { user_id: 'admin-id', email: 'admin@example.com' },
|
||||
@@ -92,14 +106,14 @@ describe('App Component', () => {
|
||||
full_name: 'Admin',
|
||||
avatar_url: '',
|
||||
};
|
||||
// Mock the response to return the admin profile
|
||||
mockedApiClient.getAuthenticatedUserProfile.mockResolvedValue(new Response(JSON.stringify(mockAdminProfile)));
|
||||
// mockedApiClient.getAuthenticatedUserProfile.mockResolvedValue(new Response(JSON.stringify(mockAdminProfile)));
|
||||
// We need to override the mock implementation specifically for this test to return the admin profile
|
||||
mockedApiClient.getAuthenticatedUserProfile.mockImplementation(() => Promise.resolve(new Response(JSON.stringify(mockAdminProfile))));
|
||||
|
||||
// Set the token to trigger the auth check effect
|
||||
localStorage.setItem('authToken', 'fake-admin-token');
|
||||
|
||||
// Force the auth hook to return an authenticated admin user
|
||||
mockUseAuth.mockReturnValue({
|
||||
user: mockAdminProfile.user,
|
||||
profile: mockAdminProfile,
|
||||
authStatus: 'AUTHENTICATED',
|
||||
isLoading: false,
|
||||
});
|
||||
|
||||
renderApp();
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ The manual-db-reset.yml workflow would be simplified to call these scripts. The
|
||||
*/
|
||||
|
||||
import { Pool, PoolClient } from 'pg';
|
||||
import fs from 'fs/promises';
|
||||
import fs from 'node:fs/promises';
|
||||
import { logger } from '../services/logger.server';
|
||||
|
||||
const pool = new Pool({
|
||||
|
||||
@@ -396,7 +396,10 @@ describe('ProfileManager Authenticated User Features', () => {
|
||||
full_name: 'Updated Name',
|
||||
avatar_url: 'http://example.com/avatar.png',
|
||||
});
|
||||
expect(mockOnProfileUpdate).toHaveBeenCalledWith(expect.objectContaining({ full_name: 'Updated Name' }));
|
||||
// The component calls the callback with the merged profile
|
||||
expect(mockOnProfileUpdate).toHaveBeenCalled();
|
||||
expect(mockOnProfileUpdate.mock.calls[0][0]).toMatchObject({ full_name: 'Updated Name' });
|
||||
|
||||
// Match any success message containing "Profile" and "updated"
|
||||
expect(notifySuccess).toHaveBeenCalledWith(expect.stringMatching(/Profile.*updated/));
|
||||
});
|
||||
|
||||
@@ -42,9 +42,9 @@ vi.mock('../services/logger.server', () => ({
|
||||
},
|
||||
}));
|
||||
|
||||
// 2. Mock fs/promises.
|
||||
// 2. Mock node:fs/promises.
|
||||
// We provide both named exports and a default export to support different import styles.
|
||||
vi.mock('fs/promises', () => {
|
||||
vi.mock('node:fs/promises', () => {
|
||||
const mockAccess = vi.fn();
|
||||
const mockConstants = { W_OK: 1 };
|
||||
return {
|
||||
@@ -56,7 +56,7 @@ vi.mock('fs/promises', () => {
|
||||
},
|
||||
};
|
||||
});
|
||||
import * as fs from 'fs/promises';
|
||||
import * as fs from 'node:fs/promises';
|
||||
|
||||
// Create the Express app
|
||||
const app = express();
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { Router, Request, Response, NextFunction } from 'express';
|
||||
import * as db from '../services/db/index.db';
|
||||
import { logger } from '../services/logger.server';
|
||||
import fs from 'fs/promises';
|
||||
import fs from 'node:fs/promises';
|
||||
|
||||
const router = Router();
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import express, { Request, Response } from 'express';
|
||||
import passport from './passport.routes';
|
||||
import multer from 'multer';
|
||||
import path from 'path';
|
||||
import fs from 'fs/promises';
|
||||
import fs from 'node:fs/promises';
|
||||
import * as bcrypt from 'bcrypt';
|
||||
import zxcvbn from 'zxcvbn';
|
||||
import * as db from '../services/db/index.db';
|
||||
|
||||
@@ -30,19 +30,20 @@ vi.mock('@google/genai', () => ({
|
||||
GoogleGenAI: MockGoogleGenAIImplementation,
|
||||
}));
|
||||
|
||||
// 3. Mock fs/promises AND node:fs/promises
|
||||
// Return a factory function to ensure fresh mocks if needed,
|
||||
// and satisfy both default and named imports.
|
||||
// 3. Mock all variants of fs/promises to catch any import style
|
||||
vi.mock('fs/promises', () => ({
|
||||
default: {
|
||||
readFile: mockReadFile,
|
||||
},
|
||||
default: { readFile: mockReadFile },
|
||||
readFile: mockReadFile,
|
||||
}));
|
||||
vi.mock('node:fs/promises', () => ({
|
||||
default: { readFile: mockReadFile },
|
||||
readFile: mockReadFile,
|
||||
}));
|
||||
// Also mock standard 'fs' just in case code uses fs.promises
|
||||
vi.mock('fs', () => ({
|
||||
default: { promises: { readFile: mockReadFile } },
|
||||
promises: { readFile: mockReadFile },
|
||||
}));
|
||||
|
||||
// 4. Mock sharp
|
||||
vi.mock('sharp', () => ({
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
import { GoogleGenAI } from '@google/genai';
|
||||
import fs from 'fs/promises';
|
||||
import fs from 'node:fs/promises';
|
||||
import { logger } from './logger.server';
|
||||
import type { FlyerItem, MasterGroceryItem } from '../types';
|
||||
|
||||
|
||||
@@ -98,7 +98,7 @@ describe('DB Connection Service', () => {
|
||||
const { getPoolStatus } = await import('./connection.db');
|
||||
const status = getPoolStatus();
|
||||
|
||||
// These values match the default mock implementation in unit-setup/mock-db
|
||||
// These values match the default mock implementation in tests-setup-unit.ts/mock-db
|
||||
expect(status).toEqual({
|
||||
totalCount: 10,
|
||||
idleCount: 5,
|
||||
|
||||
@@ -1,21 +1,31 @@
|
||||
import { describe, it, expect, vi, beforeEach, beforeAll } from 'vitest';
|
||||
import { describe, it, expect, vi, beforeEach, beforeAll, type Mock } from 'vitest';
|
||||
|
||||
// FIX: Mock the local re-export with explicit spies on a function object
|
||||
vi.mock('../lib/toast', () => {
|
||||
// Create separate mocks for the main function and its methods
|
||||
const mainToastFn = vi.fn();
|
||||
// Define a type that represents the toast function with attached methods
|
||||
type MockToast = Mock & {
|
||||
success: Mock;
|
||||
error: Mock;
|
||||
};
|
||||
|
||||
// 1. Hoist the spies so they can be referenced inside the mock factory and the test
|
||||
const { mockToast } = vi.hoisted(() => {
|
||||
const successFn = vi.fn();
|
||||
const errorFn = vi.fn();
|
||||
|
||||
// Combine them into a single object that is also a function
|
||||
const mockToast = Object.assign(mainToastFn, { success: successFn, error: errorFn });
|
||||
const fn = vi.fn();
|
||||
|
||||
return {
|
||||
default: mockToast,
|
||||
__esModule: true,
|
||||
};
|
||||
// Attach properties
|
||||
Object.assign(fn, { success: successFn, error: errorFn });
|
||||
|
||||
// Cast the return so 'mockToast' is typed correctly in the test body
|
||||
return { mockToast: fn as MockToast };
|
||||
});
|
||||
|
||||
// 2. Mock the module using the hoisted object
|
||||
vi.mock('../lib/toast', () => ({
|
||||
default: mockToast,
|
||||
__esModule: true,
|
||||
}));
|
||||
|
||||
describe('Notification Service', () => {
|
||||
beforeAll(() => {
|
||||
// Strategy #6: Verify JSDOM Window and stub missing browser APIs.
|
||||
@@ -48,15 +58,15 @@ describe('Notification Service', () => {
|
||||
|
||||
describe('notifySuccess', () => {
|
||||
it('should call the injected toaster.success with correct options', async () => {
|
||||
// Dynamically import the modules AFTER mocks are set up
|
||||
const { default: toast } = await import('../lib/toast');
|
||||
// Import the service
|
||||
const { notifySuccess } = await import('./notificationService');
|
||||
const message = 'Operation was successful!';
|
||||
|
||||
notifySuccess(message);
|
||||
|
||||
expect(toast.success).toHaveBeenCalledTimes(1);
|
||||
expect(toast.success).toHaveBeenCalledWith(
|
||||
// Verify against the hoisted mock's property
|
||||
expect(mockToast.success).toHaveBeenCalledTimes(1);
|
||||
expect(mockToast.success).toHaveBeenCalledWith(
|
||||
message,
|
||||
expect.objectContaining({
|
||||
style: expect.any(Object),
|
||||
@@ -71,14 +81,13 @@ describe('Notification Service', () => {
|
||||
|
||||
describe('notifyError', () => {
|
||||
it('should call the injected toaster.error with correct options', async () => {
|
||||
const { default: toast } = await import('../lib/toast');
|
||||
const { notifyError } = await import('./notificationService');
|
||||
const message = 'Something went wrong!';
|
||||
|
||||
notifyError(message);
|
||||
|
||||
expect(toast.error).toHaveBeenCalledTimes(1);
|
||||
expect(toast.error).toHaveBeenCalledWith(
|
||||
expect(mockToast.error).toHaveBeenCalledTimes(1);
|
||||
expect(mockToast.error).toHaveBeenCalledWith(
|
||||
message,
|
||||
expect.objectContaining({
|
||||
style: expect.any(Object),
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { Queue, Worker, Job } from 'bullmq';
|
||||
import IORedis from 'ioredis'; // Correctly imported
|
||||
import path from 'path';
|
||||
import fs from 'fs/promises';
|
||||
import fs from 'node:fs/promises';
|
||||
import { exec } from 'child_process';
|
||||
import { promisify } from 'util';
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
||||
import * as apiClient from '../../services/apiClient';
|
||||
import * as aiApiClient from '../../services/aiApiClient';
|
||||
import fs from 'fs/promises';
|
||||
import fs from 'node:fs/promises';
|
||||
import path from 'path';
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// src/tests/integration/flyer-processing.integration.test.ts
|
||||
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
||||
import fs from 'fs/promises';
|
||||
import fs from 'node:fs/promises';
|
||||
import path from 'path';
|
||||
import * as apiClient from '../../services/apiClient';
|
||||
import * as aiApiClient from '../../services/aiApiClient';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// src/tests/setup/global-setup.ts
|
||||
import { Pool } from 'pg';
|
||||
import fs from 'fs/promises';
|
||||
import fs from 'node:fs/promises';
|
||||
import path from 'path';
|
||||
import { execSync } from 'child_process';
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ afterEach(cleanup);
|
||||
// by the factory. However, we define the Constructor function INSIDE the factory
|
||||
// or via a standard function expression to ensure it remains constructible.
|
||||
const { mockPoolInstance } = vi.hoisted(() => {
|
||||
console.log('[DEBUG] unit-setup.ts: Initializing hoisted mock variables');
|
||||
console.log('[DEBUG] tests-setup-unit.ts: Initializing hoisted mock variables');
|
||||
const mockQuery = vi.fn().mockResolvedValue({ rows: [], rowCount: 0 });
|
||||
const mockRelease = vi.fn();
|
||||
const mockConnect = vi.fn().mockResolvedValue({
|
||||
@@ -87,12 +87,12 @@ export { mockPoolInstance };
|
||||
* We define the MockPool function INSIDE the factory to ensure it remains a standard function.
|
||||
*/
|
||||
vi.mock('pg', () => {
|
||||
console.log('[DEBUG] unit-setup.ts: vi.mock("pg") factory executing');
|
||||
console.log('[DEBUG] tests-setup-unit.ts: vi.mock("pg") factory executing');
|
||||
|
||||
// FIX: Use a standard function expression for the Mock Pool Constructor.
|
||||
// This ensures `new Pool()` works at runtime.
|
||||
const MockPool = function() {
|
||||
console.log('[DEBUG] unit-setup.ts: MockPool constructor called via "new"!');
|
||||
console.log('[DEBUG] tests-setup-unit.ts: MockPool constructor called via "new"!');
|
||||
return mockPoolInstance;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// src/utils/imageProcessor.ts
|
||||
import sharp from 'sharp';
|
||||
import path from 'path';
|
||||
import fs from 'fs/promises';
|
||||
import fs from 'node:fs/promises';
|
||||
import { logger } from '../services/logger.server';
|
||||
import { sanitizeFilename } from './stringUtils';
|
||||
|
||||
|
||||
Reference in New Issue
Block a user