fix(career): preserve edits during polling
CI and Deploy / test (pull_request) Failing after 2m59s
CI and Deploy / deploy (pull_request) Has been skipped

Refresh extraction runs without reloading controlled profile fields. Keep stored-profile AI actions gated until pending edits are saved.
This commit is contained in:
cesnimda
2026-08-15 13:49:58 +02:00
parent 998ee07a9a
commit d424633f95
8 changed files with 136 additions and 45 deletions
+50 -1
View File
@@ -1,6 +1,6 @@
import React from 'react';
import '@testing-library/jest-dom';
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
import { act, fireEvent, render, screen, waitFor } from '@testing-library/react';
import { ToastProvider } from './toast';
import { I18nProvider } from './i18n/I18nProvider';
import ProfilePage from './views/ProfilePage';
@@ -284,6 +284,55 @@ test('profile page can reprocess from stored artifact history', async () => {
});
});
test('active extraction polling refreshes run status without resetting unsaved career fields', async () => {
extractionRunsResponse = [{
id: 15,
trigger: 'upload',
status: 'queued',
artifactFileName: 'active-cv.pdf',
startedAtUtc: '2026-03-28T12:00:00Z',
parserVersion: 'm005-s01',
normalizerVersion: 'm005-s01',
llmPromptVersion: 'm005-s01',
operation: {
id: '00000000-0000-0000-0000-000000000015',
taskType: 'cv.process',
status: 'queued',
createdAtUtc: '2026-03-28T12:00:00Z',
canCancel: true,
canRetry: false,
},
}];
let poll: (() => void) | undefined;
const interval = jest.spyOn(window, 'setInterval').mockImplementation((handler: TimerHandler, timeout?: number) => {
if (timeout === 4000 && typeof handler === 'function') poll = handler as () => void;
return 1 as any;
});
renderPage();
const nameField = await screen.findByLabelText(/full name/i);
await waitFor(() => expect(poll).toBeDefined());
fireEvent.change(nameField, { target: { value: 'Unsaved Poll-Safe Name' } });
extractionRunsResponse = [{ ...extractionRunsResponse[0], status: 'running', operation: { ...extractionRunsResponse[0].operation, status: 'running' } }];
await act(async () => {
poll?.();
await Promise.resolve();
await Promise.resolve();
});
await waitFor(() => expect(mockedApi.get.mock.calls.filter(([url]) => url === '/profile-cv/runs').length).toBeGreaterThanOrEqual(2));
expect(mockedApi.get.mock.calls.filter(([url]) => url === '/career/profile')).toHaveLength(1);
expect(nameField).toHaveValue('Unsaved Poll-Safe Name');
expect(screen.getByText('Unsaved changes')).toBeInTheDocument();
fireEvent.click(screen.getByRole('button', { name: /save changes/i }));
await waitFor(() => expect(mockedApi.put).toHaveBeenCalledWith('/career/profile', expect.objectContaining({
profile: expect.objectContaining({ contact: expect.objectContaining({ fullName: 'Unsaved Poll-Safe Name' }) }),
})));
interval.mockRestore();
});
test('profile page shows durable CV operation state and retries a failed run', async () => {
extractionRunsResponse = [{
id: 14,