Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c4742959e4 | ||
| 97c54c0c5c | |||
| 7cc50907d1 |
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "flyer-crawler",
|
||||
"version": "0.2.2",
|
||||
"version": "0.2.3",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "flyer-crawler",
|
||||
"version": "0.2.2",
|
||||
"version": "0.2.3",
|
||||
"dependencies": {
|
||||
"@bull-board/api": "^6.14.2",
|
||||
"@bull-board/express": "^6.14.2",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "flyer-crawler",
|
||||
"private": true,
|
||||
"version": "0.2.2",
|
||||
"version": "0.2.3",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "concurrently \"npm:start:dev\" \"vite\"",
|
||||
|
||||
@@ -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.');
|
||||
});
|
||||
|
||||
|
||||
@@ -87,7 +87,9 @@ export const FlyerUploader: React.FC<FlyerUploaderProps> = ({ onProcessingComple
|
||||
)}
|
||||
|
||||
{processingState === 'completed' && (
|
||||
<p className="text-green-600 dark:text-green-400 mt-2 font-bold">Processing complete! Redirecting...</p>
|
||||
<p className="text-green-600 dark:text-green-400 mt-2 font-bold">
|
||||
Processing complete! Redirecting to flyer {flyerId}...
|
||||
</p>
|
||||
)}
|
||||
|
||||
{errorMessage && (
|
||||
@@ -95,7 +97,8 @@ export const FlyerUploader: React.FC<FlyerUploaderProps> = ({ onProcessingComple
|
||||
<p>{errorMessage}</p>
|
||||
{duplicateFlyerId && (
|
||||
<p>
|
||||
<Link to={`/flyers/${duplicateFlyerId}`} className="text-blue-500 underline">
|
||||
This flyer has already been processed. You can view it here:{' '}
|
||||
<Link to={`/flyers/${duplicateFlyerId}`} className="text-blue-500 underline" data-discover="true">
|
||||
Flyer #{duplicateFlyerId}
|
||||
</Link>
|
||||
</p>
|
||||
|
||||
@@ -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<typeof import('../services/aiApiClient')>(
|
||||
'../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');
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user