brand new unit tests finally
Some checks failed
Deploy to Web Server flyer-crawler.projectium.com / deploy (push) Failing after 2m27s
Some checks failed
Deploy to Web Server flyer-crawler.projectium.com / deploy (push) Failing after 2m27s
This commit is contained in:
@@ -9,16 +9,17 @@ import { notifyError } from '../services/notificationService';
|
||||
// The aiApiClient and notificationService are mocked globally via src/tests/setup/unit-setup.ts.
|
||||
const mockedAiApiClient = aiApiClient as Mocked<typeof aiApiClient>;
|
||||
|
||||
// Mock the global Audio constructor
|
||||
const mockAudioPlay = vi.fn(() => Promise.resolve()); // play() returns a promise
|
||||
const mockAudio = vi.fn(() => ({
|
||||
play: mockAudioPlay,
|
||||
}));
|
||||
vi.stubGlobal('Audio', mockAudio);
|
||||
|
||||
describe('VoiceLabPage', () => {
|
||||
const mockAudioPlay = vi.fn(() => Promise.resolve());
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
|
||||
// Mock the global Audio constructor freshly for each test
|
||||
const mockAudio = vi.fn(() => ({
|
||||
play: mockAudioPlay,
|
||||
}));
|
||||
vi.stubGlobal('Audio', mockAudio);
|
||||
});
|
||||
|
||||
it('should render the initial state correctly', () => {
|
||||
@@ -42,11 +43,11 @@ describe('VoiceLabPage', () => {
|
||||
|
||||
// Check for loading state
|
||||
expect(generateButton).toBeDisabled();
|
||||
expect(generateButton.querySelector('svg.animate-spin')).toBeInTheDocument();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockedAiApiClient.generateSpeechFromText).toHaveBeenCalledWith('Hello! This is a test of the text-to-speech generation.');
|
||||
expect(mockAudio).toHaveBeenCalledWith(`data:audio/mpeg;base64,${mockBase64Audio}`);
|
||||
// Expect global.Audio to have been called (Audio is now a spy)
|
||||
expect(global.Audio).toHaveBeenCalledWith(`data:audio/mpeg;base64,${mockBase64Audio}`);
|
||||
expect(mockAudioPlay).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
|
||||
@@ -1,8 +1,16 @@
|
||||
// src/services/aiService.server.test.ts
|
||||
import { describe, it, expect, vi, beforeEach, type Mocked } from 'vitest';
|
||||
import * as fs from 'fs/promises';
|
||||
import type { MasterGroceryItem } from '../types';
|
||||
|
||||
// Mock fs/promises
|
||||
const mockReadFile = vi.fn();
|
||||
vi.mock('fs/promises', () => ({
|
||||
default: {
|
||||
readFile: (...args: any[]) => mockReadFile(...args),
|
||||
},
|
||||
readFile: (...args: any[]) => mockReadFile(...args),
|
||||
}));
|
||||
|
||||
// Mock the Google GenAI library
|
||||
const mockGenerateContent = vi.fn();
|
||||
vi.mock('@google/genai', () => {
|
||||
@@ -13,9 +21,6 @@ vi.mock('@google/genai', () => {
|
||||
return { GoogleGenAI: mockGoogleGenAI };
|
||||
});
|
||||
|
||||
// Spy on fs.readFile instead of mocking the whole module
|
||||
const readFileSpy = vi.spyOn(fs, 'readFile');
|
||||
|
||||
// Mock the logger
|
||||
vi.mock('./logger.server', () => ({
|
||||
logger: {
|
||||
@@ -42,7 +47,7 @@ describe('AI Service (Server)', () => {
|
||||
]`,
|
||||
};
|
||||
mockGenerateContent.mockResolvedValue(mockResponse);
|
||||
readFileSpy.mockResolvedValue(Buffer.from('mock-image-data'));
|
||||
mockReadFile.mockResolvedValue(Buffer.from('mock-image-data'));
|
||||
|
||||
const result = await extractItemsFromReceiptImage('path/to/image.jpg', 'image/jpeg');
|
||||
|
||||
@@ -56,7 +61,7 @@ describe('AI Service (Server)', () => {
|
||||
it('should throw an error if the AI response is not valid JSON', async () => {
|
||||
const { extractItemsFromReceiptImage } = await import('./aiService.server');
|
||||
mockGenerateContent.mockResolvedValue({ text: 'This is not JSON.' });
|
||||
readFileSpy.mockResolvedValue(Buffer.from('mock-image-data'));
|
||||
mockReadFile.mockResolvedValue(Buffer.from('mock-image-data'));
|
||||
|
||||
await expect(extractItemsFromReceiptImage('path/to/image.jpg', 'image/jpeg')).rejects.toThrow(
|
||||
'AI response did not contain a valid JSON array.'
|
||||
@@ -79,7 +84,7 @@ describe('AI Service (Server)', () => {
|
||||
],
|
||||
};
|
||||
mockGenerateContent.mockResolvedValue({ text: JSON.stringify(mockAiResponse) });
|
||||
readFileSpy.mockResolvedValue(Buffer.from('mock-image-data'));
|
||||
mockReadFile.mockResolvedValue(Buffer.from('mock-image-data'));
|
||||
|
||||
const result = await extractCoreDataFromFlyerImage([{ path: 'path/to/image.jpg', mimetype: 'image/jpeg' }], mockMasterItems);
|
||||
|
||||
@@ -95,7 +100,7 @@ describe('AI Service (Server)', () => {
|
||||
it('should throw an error if the AI response is not a valid JSON object', async () => {
|
||||
const { extractCoreDataFromFlyerImage } = await import('./aiService.server');
|
||||
mockGenerateContent.mockResolvedValue({ text: 'not a json object' });
|
||||
readFileSpy.mockResolvedValue(Buffer.from('mock-image-data'));
|
||||
mockReadFile.mockResolvedValue(Buffer.from('mock-image-data'));
|
||||
|
||||
await expect(extractCoreDataFromFlyerImage([], mockMasterItems)).rejects.toThrow(
|
||||
'AI response did not contain a valid JSON object.'
|
||||
|
||||
@@ -2,15 +2,17 @@
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
|
||||
// Mock the nodemailer library BEFORE importing the email service.
|
||||
// This ensures that when emailService.server.ts is imported, it gets our mock version of nodemailer.
|
||||
const mockSendMail = vi.fn();
|
||||
vi.mock('nodemailer', () => ({
|
||||
default: {
|
||||
createTransport: vi.fn(() => ({
|
||||
sendMail: mockSendMail,
|
||||
})),
|
||||
},
|
||||
}));
|
||||
// Mock both default and named exports to cover all import styles
|
||||
vi.mock('nodemailer', () => {
|
||||
const createTransport = vi.fn(() => ({
|
||||
sendMail: mockSendMail,
|
||||
}));
|
||||
return {
|
||||
default: { createTransport },
|
||||
createTransport,
|
||||
};
|
||||
});
|
||||
|
||||
// Mock the logger to prevent console output during tests
|
||||
vi.mock('./logger.server', () => ({
|
||||
@@ -32,14 +34,14 @@ describe('Email Service (Server)', () => {
|
||||
|
||||
describe('sendPasswordResetEmail', () => {
|
||||
it('should call sendMail with the correct recipient, subject, and link', async () => {
|
||||
// Ensure the mock result is set before import/invocation
|
||||
mockSendMail.mockResolvedValue({ messageId: 'test-id' });
|
||||
|
||||
// Dynamically import the service to use the fresh mock
|
||||
const { sendPasswordResetEmail } = await import('./emailService.server');
|
||||
const to = 'test@example.com';
|
||||
const resetLink = 'http://localhost:3000/reset/mock-token-123';
|
||||
|
||||
// Mock a successful email send
|
||||
mockSendMail.mockResolvedValue({ messageId: 'test-id' });
|
||||
|
||||
await sendPasswordResetEmail(to, resetLink);
|
||||
|
||||
expect(mockSendMail).toHaveBeenCalledTimes(1);
|
||||
|
||||
@@ -7,15 +7,18 @@
|
||||
|
||||
type LogLevel = 'INFO' | 'WARN' | 'ERROR' | 'DEBUG';
|
||||
|
||||
const log = <T extends unknown[]>(level: LogLevel, message: string, ...args: T) => {
|
||||
const logMessage = `[${level}] ${message}`;
|
||||
console.log(logMessage, ...args);
|
||||
};
|
||||
|
||||
// Export the logger object for use throughout the client-side application.
|
||||
export const logger = {
|
||||
info: <T extends unknown[]>(message: string, ...args: T) => log('INFO', message, ...args),
|
||||
warn: <T extends unknown[]>(message: string, ...args: T) => log('WARN', message, ...args),
|
||||
error: <T extends unknown[]>(message: string, ...args: T) => log('ERROR', message, ...args),
|
||||
debug: <T extends unknown[]>(message: string, ...args: T) => log('DEBUG', message, ...args),
|
||||
info: <T extends unknown[]>(message: string, ...args: T) => {
|
||||
console.log(`[INFO] ${message}`, ...args);
|
||||
},
|
||||
warn: <T extends unknown[]>(message: string, ...args: T) => {
|
||||
console.warn(`[WARN] ${message}`, ...args);
|
||||
},
|
||||
error: <T extends unknown[]>(message: string, ...args: T) => {
|
||||
console.error(`[ERROR] ${message}`, ...args);
|
||||
},
|
||||
debug: <T extends unknown[]>(message: string, ...args: T) => {
|
||||
console.debug(`[DEBUG] ${message}`, ...args);
|
||||
},
|
||||
};
|
||||
@@ -3,6 +3,14 @@ import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import toast from 'react-hot-toast';
|
||||
import { notifySuccess, notifyError } from './notificationService';
|
||||
|
||||
// Mock react-hot-toast
|
||||
vi.mock('react-hot-toast', () => ({
|
||||
default: {
|
||||
success: vi.fn(),
|
||||
error: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
describe('notificationService', () => {
|
||||
beforeEach(() => {
|
||||
// Clear mock history before each test to ensure isolation.
|
||||
|
||||
Reference in New Issue
Block a user