Refactor: Enhance test robustness by using form submission in ProfileManager and improve logging in FlyerUploader tests
All checks were successful
Deploy to Test Environment / deploy-to-test (push) Successful in 23m6s
All checks were successful
Deploy to Test Environment / deploy-to-test (push) Successful in 23m6s
This commit is contained in:
@@ -48,20 +48,19 @@ describe('FlyerUploader', () => {
|
||||
const navigateSpy = vi.fn();
|
||||
|
||||
beforeEach(() => {
|
||||
console.log(`\n--- [TEST LOG] ---: Starting test: "${expect.getState().currentTestName}" ---`);
|
||||
vi.resetAllMocks();
|
||||
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.');
|
||||
vi.resetAllMocks(); // Resets mock implementations AND call history.
|
||||
console.log('--- [TEST LOG] ---: Mocks reset.');
|
||||
vi.useFakeTimers();
|
||||
console.log('--- [TEST LOG] ---: Fake timers enabled.');
|
||||
|
||||
mockedChecksumModule.generateFileChecksum.mockResolvedValue('mock-checksum');
|
||||
(useNavigate as Mock).mockReturnValue(navigateSpy);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.useRealTimers();
|
||||
console.log('--- [TEST LOG] ---: Real timers restored.');
|
||||
console.log(`--- [TEST LOG] ---: Finished test: "${expect.getState().currentTestName}" ---\n`);
|
||||
console.log(`--- [TEST LOG] ---: Finished test: "${expect.getState().currentTestName}"\n`);
|
||||
});
|
||||
|
||||
it('should render the initial state correctly', () => {
|
||||
@@ -86,36 +85,43 @@ describe('FlyerUploader', () => {
|
||||
|
||||
console.log('--- [TEST LOG] ---: 3. Firing file change event.');
|
||||
fireEvent.change(input, { target: { files: [file] } });
|
||||
console.log('--- [TEST LOG] ---: 4. File change event fired.');
|
||||
console.log('--- [TEST LOG] ---: 4. File change event fired. Now AWAITING UI update.');
|
||||
|
||||
console.log('--- [TEST LOG] ---: 5. Waiting for UI to update to polling state ("Checking...").');
|
||||
try {
|
||||
await screen.findByText('Checking...');
|
||||
console.log('--- [TEST LOG] ---: 6. SUCCESS: UI updated to polling state.');
|
||||
console.log('--- [TEST LOG] ---: 5. SUCCESS: UI updated to polling state.');
|
||||
console.log('--- [DEBUG] ---: DOM after findByText success:');
|
||||
screen.debug();
|
||||
} catch (error) {
|
||||
console.error('--- [TEST LOG] ---: 6. ERROR: findByText("Checking...") timed out.');
|
||||
console.error('--- [TEST LOG] ---: 5. ERROR: findByText("Checking...") timed out.');
|
||||
console.log('--- [DEBUG] ---: DOM at time of failure:');
|
||||
screen.debug(); // Print the DOM when the error occurs
|
||||
throw error; // Re-throw the error to fail the test
|
||||
}
|
||||
|
||||
console.log('--- [TEST LOG] ---: 6. Asserting mock calls after UI update.');
|
||||
expect(mockedAiApiClient.uploadAndProcessFlyer).toHaveBeenCalledWith(file, 'mock-checksum');
|
||||
expect(mockedAiApiClient.getJobStatus).toHaveBeenCalledTimes(1);
|
||||
console.log('--- [TEST LOG] ---: 7. Verified upload and initial poll calls.');
|
||||
console.log('--- [TEST LOG] ---: 7. Verified upload and initial poll calls. Now AWAITING timer advancement.');
|
||||
|
||||
console.log(`--- [TEST LOG] ---: 8. Advancing timers by 3000ms.`);
|
||||
await act(async () => {
|
||||
await vi.advanceTimersByTimeAsync(3000);
|
||||
console.log('--- [TEST LOG] ---: 8. Advancing timers by 3000ms...');
|
||||
vi.advanceTimersByTime(3000);
|
||||
});
|
||||
console.log(`--- [TEST LOG] ---: 9. Timers advanced.`);
|
||||
console.log('--- [TEST LOG] ---: 9. Timers advanced. Now AWAITING second poll.');
|
||||
|
||||
console.log('--- [TEST LOG] ---: 10. Waiting for getJobStatus to be called a second time.');
|
||||
await waitFor(() => {
|
||||
expect(mockedAiApiClient.getJobStatus).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
console.log('--- [TEST LOG] ---: 11. Test finished successfully.');
|
||||
try {
|
||||
await waitFor(() => {
|
||||
console.log('--- [TEST LOG] ---: 10. waitFor check: checking for 2 getJobStatus calls...');
|
||||
expect(mockedAiApiClient.getJobStatus).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
console.log('--- [TEST LOG] ---: 11. SUCCESS: Second poll confirmed.');
|
||||
} catch(error) {
|
||||
console.error('--- [TEST LOG] ---: 11. ERROR: waitFor for second poll timed out.');
|
||||
console.log('--- [DEBUG] ---: DOM at time of failure:');
|
||||
screen.debug();
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
it('should poll for status, complete successfully, and redirect', async () => {
|
||||
@@ -134,41 +140,48 @@ describe('FlyerUploader', () => {
|
||||
const input = screen.getByLabelText(/click to select a file/i);
|
||||
fireEvent.change(input, { target: { files: [file] } });
|
||||
|
||||
console.log('--- [TEST LOG] ---: 3. Waiting for UI to update to "Analyzing...".');
|
||||
console.log('--- [TEST LOG] ---: 3. Fired event. Now AWAITING UI update to "Analyzing...".');
|
||||
try {
|
||||
await screen.findByText('Analyzing...');
|
||||
console.log('--- [TEST LOG] ---: 4. SUCCESS: UI is showing "Analyzing...".');
|
||||
await screen.findByText('Analyzing...');
|
||||
console.log('--- [TEST LOG] ---: 4. SUCCESS: UI is showing "Analyzing...".');
|
||||
} catch(error) {
|
||||
console.error('--- [TEST LOG] ---: 4. ERROR: findByText("Analyzing...") timed out.');
|
||||
console.log('--- [DEBUG] ---: DOM at time of failure:');
|
||||
screen.debug();
|
||||
throw error;
|
||||
console.error('--- [TEST LOG] ---: 4. ERROR: findByText("Analyzing...") timed out.');
|
||||
console.log('--- [DEBUG] ---: DOM at time of failure:');
|
||||
screen.debug();
|
||||
throw error;
|
||||
}
|
||||
expect(mockedAiApiClient.getJobStatus).toHaveBeenCalledTimes(1);
|
||||
console.log('--- [TEST LOG] ---: 5. First poll confirmed.');
|
||||
console.log('--- [TEST LOG] ---: 5. First poll confirmed. Now AWAITING timer advancement.');
|
||||
|
||||
console.log(`--- [TEST LOG] ---: 6. Advancing timers by 4000ms for the second poll.`);
|
||||
await act(async () => {
|
||||
await vi.advanceTimersByTimeAsync(4000);
|
||||
console.log(`--- [TEST LOG] ---: 6. Advancing timers by 4000ms for the second poll...`);
|
||||
vi.advanceTimersByTime(4000);
|
||||
});
|
||||
console.log(`--- [TEST LOG] ---: 7. Timers advanced.`);
|
||||
console.log(`--- [TEST LOG] ---: 7. Timers advanced. Now AWAITING completion message.`);
|
||||
|
||||
console.log('--- [TEST LOG] ---: 8. Waiting for completion message.');
|
||||
await waitFor(() => {
|
||||
expect(mockedAiApiClient.getJobStatus).toHaveBeenCalledTimes(2);
|
||||
expect(screen.getByText('Processing complete! Redirecting to flyer 42...')).toBeInTheDocument();
|
||||
});
|
||||
console.log('--- [TEST LOG] ---: 9. Completion message found.');
|
||||
try {
|
||||
await waitFor(() => {
|
||||
console.log('--- [TEST LOG] ---: 8. waitFor check: checking for completion message...');
|
||||
expect(screen.getByText('Processing complete! Redirecting to flyer 42...')).toBeInTheDocument();
|
||||
});
|
||||
console.log('--- [TEST LOG] ---: 9. SUCCESS: Completion message found.');
|
||||
} catch (error) {
|
||||
console.error('--- [TEST LOG] ---: 9. ERROR: waitFor for completion message timed out.');
|
||||
console.log('--- [DEBUG] ---: DOM at time of failure:');
|
||||
screen.debug();
|
||||
throw error;
|
||||
}
|
||||
expect(mockedAiApiClient.getJobStatus).toHaveBeenCalledTimes(2);
|
||||
|
||||
console.log(`--- [TEST LOG] ---: 10. Advancing timers by 2000ms for redirect.`);
|
||||
await act(async () => {
|
||||
await vi.advanceTimersByTimeAsync(2000);
|
||||
console.log(`--- [TEST LOG] ---: 10. Advancing timers by 2000ms for redirect...`);
|
||||
vi.advanceTimersByTime(2000);
|
||||
});
|
||||
console.log(`--- [TEST LOG] ---: 11. Timers advanced.`);
|
||||
console.log(`--- [TEST LOG] ---: 11. Timers advanced. Now asserting navigation.`);
|
||||
|
||||
expect(onProcessingComplete).toHaveBeenCalled();
|
||||
expect(navigateSpy).toHaveBeenCalledWith('/flyers/42');
|
||||
console.log('--- [TEST LOG] ---: 12. Callback and navigation confirmed. Test finished successfully.');
|
||||
console.log('--- [TEST LOG] ---: 12. Callback and navigation confirmed.');
|
||||
});
|
||||
|
||||
it('should handle a failed job', async () => {
|
||||
@@ -185,22 +198,23 @@ describe('FlyerUploader', () => {
|
||||
const file = new File(['content'], 'flyer.pdf', { type: 'application/pdf' });
|
||||
const input = screen.getByLabelText(/click to select a file/i);
|
||||
|
||||
console.log('--- [TEST LOG] ---: 3. Firing file change event.');
|
||||
fireEvent.change(input, { target: { files: [file] } });
|
||||
console.log('--- [TEST LOG] ---: 3. File upload triggered.');
|
||||
|
||||
console.log('--- [TEST LOG] ---: 4. Waiting for failure message.');
|
||||
try {
|
||||
expect(await screen.findByText(/Processing failed: AI model exploded/i)).toBeInTheDocument();
|
||||
console.log('--- [TEST LOG] ---: 5. SUCCESS: Failure message found.');
|
||||
console.log('--- [TEST LOG] ---: 4. AWAITING failure message...');
|
||||
expect(await screen.findByText(/Processing failed: AI model exploded/i)).toBeInTheDocument();
|
||||
console.log('--- [TEST LOG] ---: 5. SUCCESS: Failure message found.');
|
||||
} catch (error) {
|
||||
console.error('--- [TEST LOG] ---: 5. ERROR: findByText for failure message timed out.');
|
||||
console.log('--- [DEBUG] ---: DOM at time of failure:');
|
||||
screen.debug();
|
||||
throw error;
|
||||
console.error('--- [TEST LOG] ---: 5. ERROR: findByText for failure message timed out.');
|
||||
console.log('--- [DEBUG] ---: DOM at time of failure:');
|
||||
screen.debug();
|
||||
throw error;
|
||||
}
|
||||
|
||||
expect(screen.getByText('Upload Another Flyer')).toBeInTheDocument();
|
||||
console.log('--- [TEST LOG] ---: 6. "Upload Another" button confirmed. Test finished successfully.');
|
||||
console.log('--- [TEST LOG] ---: 6. "Upload Another" button confirmed.');
|
||||
});
|
||||
|
||||
it('should handle a duplicate flyer error (409)', async () => {
|
||||
@@ -209,27 +223,29 @@ describe('FlyerUploader', () => {
|
||||
new Response(JSON.stringify({ flyerId: 99, message: 'Duplicate' }), { status: 409 })
|
||||
);
|
||||
|
||||
console.log('--- [TEST LOG] ---: 2. Rendering and uploading.');
|
||||
renderComponent();
|
||||
const file = new File(['content'], 'flyer.pdf', { type: 'application/pdf' });
|
||||
const input = screen.getByLabelText(/click to select a file/i);
|
||||
|
||||
console.log('--- [TEST LOG] ---: 3. Firing file change event.');
|
||||
fireEvent.change(input, { target: { files: [file] } });
|
||||
console.log('--- [TEST LOG] ---: 3. File upload triggered.');
|
||||
|
||||
console.log('--- [TEST LOG] ---: 4. Waiting for duplicate flyer message.');
|
||||
try {
|
||||
expect(await screen.findByText('This flyer has already been processed. You can view it here:')).toBeInTheDocument();
|
||||
console.log('--- [TEST LOG] ---: 5. SUCCESS: Duplicate message found.');
|
||||
console.log('--- [TEST LOG] ---: 4. AWAITING duplicate flyer message...');
|
||||
expect(await screen.findByText('This flyer has already been processed. You can view it here:')).toBeInTheDocument();
|
||||
console.log('--- [TEST LOG] ---: 5. SUCCESS: Duplicate message found.');
|
||||
} catch(error) {
|
||||
console.error('--- [TEST LOG] ---: 5. ERROR: findByText for duplicate message timed out.');
|
||||
console.log('--- [DEBUG] ---: DOM at time of failure:');
|
||||
screen.debug();
|
||||
throw error;
|
||||
console.error('--- [TEST LOG] ---: 5. ERROR: findByText for duplicate message timed out.');
|
||||
console.log('--- [DEBUG] ---: DOM at time of failure:');
|
||||
screen.debug();
|
||||
throw error;
|
||||
}
|
||||
|
||||
const link = screen.getByRole('link', { name: /Flyer #99/i });
|
||||
expect(link).toHaveAttribute('href', '/flyers/99');
|
||||
console.log('--- [TEST LOG] ---: 6. Duplicate link confirmed. Test finished successfully.');
|
||||
console.log('--- [TEST LOG] ---: 6. Duplicate link confirmed.');
|
||||
});
|
||||
|
||||
it('should allow the user to stop watching progress', async () => {
|
||||
@@ -246,37 +262,37 @@ describe('FlyerUploader', () => {
|
||||
const file = new File(['content'], 'flyer.pdf', { type: 'application/pdf' });
|
||||
const input = screen.getByLabelText(/click to select a file/i);
|
||||
|
||||
console.log('--- [TEST LOG] ---: 3. Firing file change event.');
|
||||
fireEvent.change(input, { target: { files: [file] } });
|
||||
console.log('--- [TEST LOG] ---: 3. File upload triggered.');
|
||||
|
||||
console.log('--- [TEST LOG] ---: 4. Waiting for polling UI.');
|
||||
let stopButton;
|
||||
try {
|
||||
stopButton = await screen.findByRole('button', { name: 'Stop Watching Progress' });
|
||||
console.log('--- [TEST LOG] ---: 5. SUCCESS: Polling UI is visible.');
|
||||
console.log('--- [TEST LOG] ---: 4. AWAITING polling UI...');
|
||||
stopButton = await screen.findByRole('button', { name: 'Stop Watching Progress' });
|
||||
console.log('--- [TEST LOG] ---: 5. SUCCESS: Polling UI is visible.');
|
||||
} catch(error) {
|
||||
console.error('--- [TEST LOG] ---: 5. ERROR: findByRole for stop button timed out.');
|
||||
console.log('--- [DEBUG] ---: DOM at time of failure:');
|
||||
screen.debug();
|
||||
throw error;
|
||||
console.error('--- [TEST LOG] ---: 5. ERROR: findByRole for stop button timed out.');
|
||||
console.log('--- [DEBUG] ---: DOM at time of failure:');
|
||||
screen.debug();
|
||||
throw error;
|
||||
}
|
||||
|
||||
console.log('--- [TEST LOG] ---: 6. Clicking "Stop Watching Progress" button.');
|
||||
fireEvent.click(stopButton);
|
||||
console.log('--- [TEST LOG] ---: 7. Click event fired.');
|
||||
|
||||
console.log('--- [TEST LOG] ---: 8. Waiting for UI to reset to idle state.');
|
||||
try {
|
||||
expect(await screen.findByText(/click to select a file/i)).toBeInTheDocument();
|
||||
console.log('--- [TEST LOG] ---: 9. SUCCESS: UI has reset.');
|
||||
console.log('--- [TEST LOG] ---: 8. AWAITING UI reset to idle state...');
|
||||
expect(await screen.findByText(/click to select a file/i)).toBeInTheDocument();
|
||||
console.log('--- [TEST LOG] ---: 9. SUCCESS: UI has reset.');
|
||||
} catch(error) {
|
||||
console.error('--- [TEST LOG] ---: 9. ERROR: findByText for idle state timed out.');
|
||||
console.log('--- [DEBUG] ---: DOM at time of failure:');
|
||||
screen.debug();
|
||||
throw error;
|
||||
console.error('--- [TEST LOG] ---: 9. ERROR: findByText for idle state timed out.');
|
||||
console.log('--- [DEBUG] ---: DOM at time of failure:');
|
||||
screen.debug();
|
||||
throw error;
|
||||
}
|
||||
|
||||
expect(screen.queryByText('Analyzing...')).not.toBeInTheDocument();
|
||||
console.log('--- [TEST LOG] ---: 10. UI has reset. Test finished successfully.');
|
||||
});
|
||||
});
|
||||
@@ -195,8 +195,9 @@ describe('ProfileManager Authenticated User Features', () => {
|
||||
fireEvent.change(screen.getByLabelText(/city/i), { target: { value: 'NewCity' } });
|
||||
console.log('[TEST LOG] City input value after change:', (cityInput as HTMLInputElement).value);
|
||||
|
||||
console.log('[TEST LOG] Firing click event on "Save Profile" button.');
|
||||
fireEvent.click(screen.getByRole('button', { name: /save profile/i }));
|
||||
console.log('[TEST LOG] Firing SUBMIT event on the form.');
|
||||
// Use fireEvent.submit on the form for more robust testing of submission logic.
|
||||
fireEvent.submit(screen.getByTestId('profile-form'));
|
||||
|
||||
console.log('[TEST LOG] Waiting for notifyError to be called...');
|
||||
// Since only the address changed and it failed, we expect an error notification (handled by useApi)
|
||||
|
||||
@@ -82,7 +82,10 @@ describe('generateFileChecksum', () => {
|
||||
const checksum = await generateFileChecksum(file);
|
||||
// Correct SHA-256 for "error fallback" (without a trailing newline).
|
||||
expect(checksum).toBe('786aaab58cd94f0d74de6895575dcfa7ace2733c9713121e1fac197eb02ec1c2');
|
||||
expect(logger.warn).toHaveBeenCalledWith(expect.stringContaining('file.arrayBuffer() threw an error'));
|
||||
expect(logger.warn).toHaveBeenCalledWith(
|
||||
expect.stringContaining('file.arrayBuffer() threw an error'),
|
||||
expect.any(Object)
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw an error if crypto.subtle is not available', async () => {
|
||||
|
||||
Reference in New Issue
Block a user