feat(cv-qa): map missing records from reviewed source evidence
This commit is contained in:
@@ -6,6 +6,7 @@ import { emptyStructuredCv } from './profileCv';
|
||||
import { ToastProvider } from './toast';
|
||||
import { I18nProvider } from './i18n/I18nProvider';
|
||||
import CareerProfilePage from './views/CareerProfilePage';
|
||||
import type { MappingBlock, MappingCategory, MappingTarget } from './views/career/CvImportMappingEditor';
|
||||
|
||||
jest.mock('./api', () => ({
|
||||
getApiErrorMessage: jest.requireActual('./api').getApiErrorMessage,
|
||||
@@ -22,7 +23,7 @@ const review = { runId: 23, status: 'pending_review', revision: 7, fileName: 'ca
|
||||
{ id: 'Contact.fullName', label: 'Full name', currentValue: 'Saved name', extractedValue: 'Source name', sourceSnippet: 'Source name', sourcePage: 1, method: 'docling', reviewState: 'suggested' },
|
||||
{ id: 'Contact.headline', label: 'Headline', currentValue: 'Saved headline', extractedValue: 'Engineer', sourceSnippet: null, sourcePage: null, method: 'deterministic', reviewState: 'suggested' },
|
||||
] }] };
|
||||
let currentReview = review;
|
||||
let currentReview: typeof review & Partial<{ sourceBlocks: MappingBlock[]; mappingCategories: MappingCategory[]; targets: MappingTarget[] }> = review;
|
||||
let runs = [run];
|
||||
|
||||
function renderImport() {
|
||||
@@ -51,6 +52,30 @@ beforeEach(() => {
|
||||
|
||||
afterEach(() => jest.clearAllMocks());
|
||||
|
||||
test('a source-mapped missing record can be the only approved change and survives stale review without a stale target', async () => {
|
||||
currentReview = { ...review, groups: [], sourceBlocks: [{ id: 'b1', text: 'Engineer', kind: 'paragraph', page: 1 }, { id: 'b2', text: 'Example AS', kind: 'paragraph', page: 1 }], mappingCategories: [{ category: 'jobs', fields: [{ name: 'title', multiple: false }, { name: 'company', multiple: false }] }], targets: [{ category: 'jobs', index: 0, label: 'Saved job' }] };
|
||||
renderImport();
|
||||
fireEvent.click(await screen.findByRole('button', { name: 'Create missing record' }));
|
||||
const record = screen.getByRole('group', { name: 'Mapped record 1' });
|
||||
fireEvent.change(within(record).getByRole('textbox', { name: 'Value 1' }), { target: { value: 'Engineer' } });
|
||||
fireEvent.change(within(record).getByRole('textbox', { name: 'Value 2' }), { target: { value: 'Example AS' } });
|
||||
fireEvent.change(within(record).getByRole('combobox', { name: 'Source evidence 1' }), { target: { value: 'b1' } });
|
||||
fireEvent.change(within(record).getByRole('combobox', { name: 'Source evidence 2' }), { target: { value: 'b2' } });
|
||||
fireEvent.change(within(record).getByRole('combobox', { name: 'Destination' }), { target: { value: '0' } });
|
||||
fireEvent.click(within(record).getByRole('button', { name: 'Approve mapped record' }));
|
||||
currentReview = { ...currentReview, revision: 8 };
|
||||
mockedApi.post.mockRejectedValueOnce({ response: { status: 409 } });
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Apply reviewed changes' }));
|
||||
await waitFor(() => expect(mockedApi.post).toHaveBeenCalledWith('/profile-cv/runs/23/apply-reviewed', expect.objectContaining({ expectedVersion: 7, decisions: [], mappings: [expect.objectContaining({ category: 'jobs', targetIndex: 0 })] })));
|
||||
await waitFor(() => expect(screen.getByRole('button', { name: 'Apply reviewed changes' })).toBeDisabled());
|
||||
expect(within(record).getByRole('textbox', { name: 'Value 1' })).toHaveValue('Engineer');
|
||||
expect(within(record).getByRole('combobox', { name: 'Destination' })).toHaveValue('');
|
||||
fireEvent.click(within(record).getByRole('button', { name: 'Approve mapped record' }));
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Apply reviewed changes' }));
|
||||
await waitFor(() => expect(mockedApi.post).toHaveBeenLastCalledWith('/profile-cv/runs/23/apply-reviewed', expect.objectContaining({ expectedVersion: 8, mappings: [expect.objectContaining({ category: 'jobs', targetIndex: null })] })));
|
||||
expect(await screen.findByText('Reviewed changes saved to your Career Profile.')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('Docling review requires explicit decisions and sends only reviewed corrections before showing durable completion', async () => {
|
||||
renderImport();
|
||||
const choice = await screen.findByRole('combobox', { name: 'Decision: Full name' });
|
||||
|
||||
Reference in New Issue
Block a user