Add CV extraction review surfaces

This commit is contained in:
2026-03-28 23:51:03 +01:00
parent 107c181506
commit 2392b135c2
7 changed files with 390 additions and 34 deletions
+47
View File
@@ -24,6 +24,16 @@ const mockedApi = api as jest.Mocked<typeof api>;
const structuredCv = {
version: '1',
metadata: {
profileVersion: 3,
appliedExtractionRunId: 12,
updatedAtUtc: '2026-03-28T12:00:00Z',
fields: {
'contact.fullName': { confidence: 0.92, method: 'llm', reviewState: 'suggested', sourceSnippet: 'Demo User' },
summary: { confidence: 0.71, method: 'deterministic', reviewState: 'suggested', sourceSnippet: 'Built backend systems' },
skills: { confidence: 0.68, method: 'deterministic', reviewState: 'suggested', sourceSnippet: '.NET' },
},
},
contact: {
fullName: 'Demo User',
headline: 'Backend Developer',
@@ -80,6 +90,24 @@ beforeEach(() => {
},
} as any);
}
if (url === '/profile-cv/runs') {
return Promise.resolve({
data: [
{
id: 12,
trigger: 'upload',
status: 'applied',
artifactFileName: 'resume.pdf',
startedAtUtc: '2026-03-28T12:00:00Z',
completedAtUtc: '2026-03-28T12:00:05Z',
appliedAtUtc: '2026-03-28T12:00:05Z',
parserVersion: 'm005-s01',
normalizerVersion: 'm005-s01',
llmPromptVersion: 'm005-s01',
},
],
} as any);
}
return Promise.resolve({ data: {} } as any);
});
mockedApi.post.mockImplementation((url: string) => {
@@ -97,6 +125,9 @@ beforeEach(() => {
},
} as any);
}
if (url === '/profile-cv/reprocess') {
return Promise.resolve({ data: { reprocessed: true } } as any);
}
return Promise.resolve({ data: {} } as any);
});
mockedApi.put.mockResolvedValue({ data: {} } as any);
@@ -113,8 +144,12 @@ test('profile page loads persisted structured cv and can re-parse it', async ()
expect(await screen.findByText(/cv ready/i)).toBeInTheDocument();
expect(screen.getByText(/cv structure overview/i)).toBeInTheDocument();
expect(screen.getByText(/structured cv editor/i)).toBeInTheDocument();
expect(screen.getByText(/extraction history/i)).toBeInTheDocument();
expect(screen.getByText(/resume.pdf/i)).toBeInTheDocument();
expect(screen.getByText(/current run/i)).toBeInTheDocument();
expect(screen.getAllByText(/professional summary/i).length).toBeGreaterThan(0);
expect(screen.getByLabelText(/full name/i)).toHaveValue('Demo User');
expect(screen.getByText(/high 92%/i)).toBeInTheDocument();
const analyzeButton = screen.getByRole('button', { name: /analyze sections/i });
await waitFor(() => expect(analyzeButton).toBeEnabled());
@@ -127,6 +162,18 @@ test('profile page loads persisted structured cv and can re-parse it', async ()
expect(screen.getAllByText(/core skills/i).length).toBeGreaterThan(0);
});
test('profile page can reprocess from stored artifact history', async () => {
renderPage();
expect(await screen.findByText(/extraction history/i)).toBeInTheDocument();
const reprocessButton = screen.getByRole('button', { name: /reprocess cv/i });
fireEvent.click(reprocessButton);
await waitFor(() => {
expect(mockedApi.post).toHaveBeenCalledWith('/profile-cv/reprocess');
});
});
test('saving profile persists structured cv json', async () => {
renderPage();