From 7cc50907d1e8ea44ac4f1f595dae7ee98c363b52 Mon Sep 17 00:00:00 2001 From: Torben Sorensen Date: Fri, 26 Dec 2025 20:16:19 -0800 Subject: [PATCH] more testing fixes --- src/features/flyer/FlyerUploader.test.tsx | 13 +++++++------ src/features/flyer/FlyerUploader.tsx | 7 +++++-- src/hooks/useFlyerUploader.test.tsx | 11 +++++++---- src/hooks/useFlyerUploader.ts | 11 ++++++++++- src/services/flyerProcessingService.server.test.ts | 2 +- 5 files changed, 30 insertions(+), 14 deletions(-) diff --git a/src/features/flyer/FlyerUploader.test.tsx b/src/features/flyer/FlyerUploader.test.tsx index 70f48ef4..3b974bd9 100644 --- a/src/features/flyer/FlyerUploader.test.tsx +++ b/src/features/flyer/FlyerUploader.test.tsx @@ -61,9 +61,8 @@ describe('FlyerUploader', () => { beforeEach(() => { console.log(`\n--- [TEST LOG] ---: Starting test: "${expect.getState().currentTestName}"`); - // Use the 'modern' implementation of fake timers to handle promise microtasks correctly. - vi.useFakeTimers({ toFake: ['setTimeout'], shouldAdvanceTime: true }); - console.log('--- [TEST LOG] ---: MODERN fake timers enabled.'); + // Use fake timers to control polling intervals. + vi.useFakeTimers(); vi.resetAllMocks(); // Resets mock implementations AND call history. console.log('--- [TEST LOG] ---: Mocks reset.'); mockedChecksumModule.generateFileChecksum.mockResolvedValue('mock-checksum'); @@ -358,7 +357,7 @@ describe('FlyerUploader', () => { try { console.log('--- [TEST LOG] ---: 4. AWAITING duplicate flyer message...'); expect( - await screen.findByText('This flyer has already been processed. You can view it here:'), + await screen.findByText(/This flyer has already been processed/i), ).toBeInTheDocument(); console.log('--- [TEST LOG] ---: 5. SUCCESS: Duplicate message found.'); } catch (error) { @@ -471,7 +470,7 @@ describe('FlyerUploader', () => { fireEvent.change(input, { target: { files: [file] } }); console.log('--- [TEST LOG] ---: 3. Awaiting error message.'); - expect(await screen.findByText(/Polling Network Error/i)).toBeInTheDocument(); + expect(await screen.findByText(/Polling failed: Polling Network Error/i)).toBeInTheDocument(); console.log('--- [TEST LOG] ---: 4. Assertions passed.'); }); @@ -513,7 +512,9 @@ describe('FlyerUploader', () => { fireEvent.change(input, { target: { files: [file] } }); console.log('--- [TEST LOG] ---: 3. Awaiting error message.'); - expect(await screen.findByText(/Failed to parse JSON response from server/i)).toBeInTheDocument(); + expect( + await screen.findByText(/Polling failed: Failed to parse JSON response from server/i), + ).toBeInTheDocument(); console.log('--- [TEST LOG] ---: 4. Assertions passed.'); }); diff --git a/src/features/flyer/FlyerUploader.tsx b/src/features/flyer/FlyerUploader.tsx index 7a8f792d..3983ff81 100644 --- a/src/features/flyer/FlyerUploader.tsx +++ b/src/features/flyer/FlyerUploader.tsx @@ -87,7 +87,9 @@ export const FlyerUploader: React.FC = ({ onProcessingComple )} {processingState === 'completed' && ( -

Processing complete! Redirecting...

+

+ Processing complete! Redirecting to flyer {flyerId}... +

)} {errorMessage && ( @@ -95,7 +97,8 @@ export const FlyerUploader: React.FC = ({ onProcessingComple

{errorMessage}

{duplicateFlyerId && (

- + This flyer has already been processed. You can view it here:{' '} + Flyer #{duplicateFlyerId}

diff --git a/src/hooks/useFlyerUploader.test.tsx b/src/hooks/useFlyerUploader.test.tsx index 7749de72..f47b0823 100644 --- a/src/hooks/useFlyerUploader.test.tsx +++ b/src/hooks/useFlyerUploader.test.tsx @@ -5,6 +5,11 @@ import { useFlyerUploader } from './useFlyerUploader'; import * as aiApiClient from '../services/aiApiClient'; import * as checksumUtil from '../utils/checksum'; +// Import the actual error class because the module is mocked +const { JobFailedError } = await vi.importActual( + '../services/aiApiClient', +); + // Mock dependencies vi.mock('../services/aiApiClient'); vi.mock('../utils/checksum'); @@ -111,11 +116,9 @@ describe('useFlyerUploader Hook with React Query', () => { mockedAiApiClient.uploadAndProcessFlyer.mockResolvedValue({ jobId: mockJobId }); // Mock getJobStatus to throw a JobFailedError - const jobFailedError = new aiApiClient.JobFailedError( - 'AI validation failed.', - 'AI_VALIDATION_FAILED', + mockedAiApiClient.getJobStatus.mockRejectedValue( + new JobFailedError('AI validation failed.', 'AI_VALIDATION_FAILED'), ); - mockedAiApiClient.getJobStatus.mockRejectedValue(jobFailedError); const { result } = renderHook(() => useFlyerUploader(), { wrapper: createWrapper() }); const mockFile = new File([''], 'flyer.pdf'); diff --git a/src/hooks/useFlyerUploader.ts b/src/hooks/useFlyerUploader.ts index 8779dc87..cd0703d6 100644 --- a/src/hooks/useFlyerUploader.ts +++ b/src/hooks/useFlyerUploader.ts @@ -86,7 +86,13 @@ export const useFlyerUploader = () => { if (uploadMutation.isPending) return 'uploading'; if (jobStatus && (jobStatus.state === 'active' || jobStatus.state === 'waiting')) return 'polling'; - if (jobStatus?.state === 'completed') return 'completed'; + if (jobStatus?.state === 'completed') { + // If the job is complete but didn't return a flyerId, it's an error state. + if (!jobStatus.returnValue?.flyerId) { + return 'error'; + } + return 'completed'; + } if (uploadMutation.isError || jobStatus?.state === 'failed' || pollError) return 'error'; return 'idle'; })(); @@ -100,6 +106,9 @@ export const useFlyerUploader = () => { if (jobStatus?.state === 'failed') { return `Processing failed: ${jobStatus.progress?.message || jobStatus.failedReason}`; } + if (jobStatus?.state === 'completed' && !jobStatus.returnValue?.flyerId) { + return 'Job completed but did not return a flyer ID.'; + } return null; }; diff --git a/src/services/flyerProcessingService.server.test.ts b/src/services/flyerProcessingService.server.test.ts index 3d9e5398..2deb13e8 100644 --- a/src/services/flyerProcessingService.server.test.ts +++ b/src/services/flyerProcessingService.server.test.ts @@ -380,7 +380,7 @@ describe('FlyerProcessingService', () => { expect(job.updateProgress).toHaveBeenCalledWith({ errorCode: 'UNSUPPORTED_FILE_TYPE', message: - 'Error: Unsupported file type: .txt. Supported types are PDF, JPG, PNG, WEBP, HEIC, HEIF, GIF, TIFF, SVG, BMP.', + 'Unsupported file type: .txt. Supported types are PDF, JPG, PNG, WEBP, HEIC, HEIF, GIF, TIFF, SVG, BMP.', }); expect(mockCleanupQueue.add).not.toHaveBeenCalled(); });