refactor(career): Phase 1 increment — user-facing terminology + component split
UI-only restructuring of the Career Profile surface. No change to
database models, CareerProfiles schema, CvVariants, extraction APIs, AI
services, CV rendering, or public CV.
Terminology -> user-facing (i18n strings):
- "Structured CV editor" -> "Career information"
- "CV structure overview" -> "Profile sections"
- "Summary bullets" -> "Professional summary"
- "Core skills" -> "Skills"
- "Analyze sections" -> "Read sections"
- "Original extraction" -> "Original import"
- hardcoded "Master career profile" -> "Career profile"
Help text de-jargoned; the Career information help now frames it as the
source the CV Builder consumes.
Component split (first step): extract ProfileCompleteness (completeness
meter + missing chips + version history) into src/views/career/. Display
only, props in, no state or API.
Save path untouched: api.put("/career/profile", { profile, cvText }). A
new test pins that exact call as the refactor invariant so the remaining
section extraction cannot silently change save behaviour. Existing
profile-page tests re-pointed to the new labels; every behavioural
assertion (save, parse, field values) kept.
Verified: tsc clean, production build clean, 136 frontend tests pass
(135 + 1 invariant). Sidebar fix from the previous task still passes.
Backend untouched.
The remaining Phase 1 work (per-section editor components, hiding the
template-driven builder and structure-overview blocks, actionable
per-section empty states) is staged in docs/career-workspace-ux-refactor.md
because it touches the live extraction test surface and is best verified
by driving the authenticated UI. This increment is a clean, non-regressing
checkpoint.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -205,13 +205,13 @@ test('profile page loads persisted structured cv and can re-parse it', async ()
|
||||
renderPage();
|
||||
|
||||
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(/profile sections/i)).toBeInTheDocument();
|
||||
expect(screen.getAllByText(/career information/i).length).toBeGreaterThan(0);
|
||||
expect(screen.getByText(/extraction history/i)).toBeInTheDocument();
|
||||
expect(screen.getByText(/resume.pdf/i)).toBeInTheDocument();
|
||||
expect(screen.getByText(/current run/i)).toBeInTheDocument();
|
||||
expect(screen.getAllByText(/original extraction/i).length).toBeGreaterThan(0);
|
||||
const originalExtractionToggle = screen.getByRole('button', { name: /original extraction/i });
|
||||
expect(screen.getAllByText(/original import/i).length).toBeGreaterThan(0);
|
||||
const originalExtractionToggle = screen.getByRole('button', { name: /original import/i });
|
||||
expect(originalExtractionToggle).toHaveAttribute('aria-expanded', 'false');
|
||||
expect(screen.getAllByText(/professional summary/i).length).toBeGreaterThan(0);
|
||||
expect(screen.getByLabelText(/full name/i)).toHaveValue('Demo User');
|
||||
@@ -222,7 +222,7 @@ test('profile page loads persisted structured cv and can re-parse it', async ()
|
||||
expect(originalExtractionToggle).toHaveAttribute('aria-expanded', 'true');
|
||||
expect(await screen.findByLabelText(/profile cv \/ master resume text/i)).toHaveValue('Professional Summary\nBuilt backend systems');
|
||||
|
||||
const analyzeButton = screen.getByRole('button', { name: /analyze sections/i });
|
||||
const analyzeButton = screen.getByRole('button', { name: /read sections/i });
|
||||
await waitFor(() => expect(analyzeButton).toBeEnabled());
|
||||
fireEvent.click(analyzeButton);
|
||||
|
||||
@@ -233,6 +233,24 @@ test('profile page loads persisted structured cv and can re-parse it', async ()
|
||||
expect(screen.getAllByText(/core skills/i).length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
test('saving the career profile PUTs the structured profile and cv text unchanged (Phase 1 refactor invariant)', async () => {
|
||||
renderPage();
|
||||
|
||||
const saveButton = await screen.findByRole('button', { name: /save changes/i });
|
||||
await waitFor(() => expect(saveButton).toBeEnabled());
|
||||
fireEvent.click(saveButton);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockedApi.put).toHaveBeenCalledWith(
|
||||
'/career/profile',
|
||||
expect.objectContaining({
|
||||
profile: expect.objectContaining({ contact: expect.objectContaining({ fullName: 'Demo User' }) }),
|
||||
cvText: 'Professional Summary\nBuilt backend systems',
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
test('profile page can reprocess from stored artifact history', async () => {
|
||||
renderPage();
|
||||
|
||||
@@ -249,9 +267,9 @@ test('profile page keeps raw extraction collapsed until expanded', async () => {
|
||||
renderPage();
|
||||
|
||||
expect(await screen.findByText(/cv ready/i)).toBeInTheDocument();
|
||||
expect(screen.getByText(/the structured cv stays front and center/i)).toBeInTheDocument();
|
||||
expect(screen.getByText(/your career information stays front and center/i)).toBeInTheDocument();
|
||||
|
||||
const originalExtractionToggle = screen.getByRole('button', { name: /original extraction/i });
|
||||
const originalExtractionToggle = screen.getByRole('button', { name: /original import/i });
|
||||
expect(originalExtractionToggle).toHaveAttribute('aria-expanded', 'false');
|
||||
const copyButton = screen.getByRole('button', { name: /copy cv text/i });
|
||||
expect(copyButton).toBeDisabled();
|
||||
|
||||
Reference in New Issue
Block a user