feat(cv): expand builder studio tools

This commit is contained in:
cesnimda
2026-08-29 01:45:30 +02:00
parent f794265e3e
commit 0f17d95c57
10 changed files with 434 additions and 47 deletions
@@ -74,6 +74,11 @@ function routeGet(onVariant: () => Promise<any>, outline: any = { sections: [] }
});
}
async function openContentTab() {
await screen.findByDisplayValue('Backend CV');
fireEvent.click(screen.getByRole('tab', { name: 'Content' }));
}
test('deep link loads the exact variant named in the route', async () => {
routeGet(() => Promise.resolve({ data: variant } as any));
@@ -118,6 +123,7 @@ test('failed autosave is visible and can be retried with the latest data', async
mockedApi.put.mockRejectedValueOnce(new Error('offline')).mockResolvedValueOnce({ data: variant } as any);
renderAt(3);
await openContentTab();
fireEvent.change(await screen.findByLabelText('Headline override'), { target: { value: 'Platform Engineer' } });
expect(screen.getByText('Unsaved')).toBeInTheDocument();
fireEvent.click(screen.getByRole('button', { name: 'Save now' }));
@@ -136,6 +142,7 @@ test('session undo and redo restore content edits before autosave', async () =>
mockedApi.put.mockResolvedValue({ data: variant } as any);
renderAt(3);
await openContentTab();
const headline = await screen.findByLabelText('Headline override');
fireEvent.change(headline, { target: { value: 'Platform Engineer' } });
expect(screen.getByRole('button', { name: 'Undo' })).toBeEnabled();
@@ -146,19 +153,23 @@ test('session undo and redo restore content edits before autosave', async () =>
expect(headline).toHaveValue('Platform Engineer');
});
test('professional editor separates template, design and layout controls', async () => {
test('professional editor starts with templates and combines design and layout controls', async () => {
routeGet(() => Promise.resolve({ data: variant } as any));
renderAt(3);
await screen.findByLabelText('Headline override');
fireEvent.click(screen.getByRole('tab', { name: 'Design' }));
await screen.findByDisplayValue('Backend CV');
expect(screen.getByRole('tab', { name: 'Template' })).toHaveAttribute('aria-selected', 'true');
fireEvent.click(screen.getByRole('tab', { name: 'Customize' }));
expect(screen.getByText('Typography')).toBeInTheDocument();
expect(screen.getByLabelText('Body size')).toBeInTheDocument();
expect(screen.getByLabelText('Skills presentation')).toBeInTheDocument();
fireEvent.click(screen.getByRole('tab', { name: 'Layout' }));
expect(screen.getByLabelText('Page size')).toBeInTheDocument();
expect(screen.getByLabelText('Columns')).toBeInTheDocument();
expect(screen.getByLabelText('Language')).toBeInTheDocument();
fireEvent.click(screen.getByRole('tab', { name: 'AI Tools' }));
expect(screen.getByText('Translate resume')).toBeInTheDocument();
expect(screen.getByText('Improve writing')).toBeInTheDocument();
expect(screen.getByText('Check spelling and grammar')).toBeInTheDocument();
});
test('editor sections, design controls and document settings render in Bokmål', async () => {
@@ -166,13 +177,14 @@ test('editor sections, design controls and document settings render in Bokmål',
routeGet(() => Promise.resolve({ data: variant } as any));
renderAt(3);
await screen.findByDisplayValue('Backend CV');
fireEvent.click(screen.getByRole('tab', { name: 'Innhold' }));
expect(await screen.findByLabelText('Egen overskrift')).toBeInTheDocument();
expect(screen.getByRole('button', { name: 'Utvid Erfaring' })).toBeInTheDocument();
expect(screen.getByLabelText('Seksjonstype')).toBeInTheDocument();
fireEvent.click(screen.getByRole('tab', { name: 'Design' }));
expect(screen.getByRole('button', { name: 'Legg til innhold' })).toBeInTheDocument();
fireEvent.click(screen.getByRole('tab', { name: 'Tilpass' }));
expect(screen.getByText('Typografi')).toBeInTheDocument();
expect(screen.getByLabelText('Visning av ferdigheter')).toBeInTheDocument();
fireEvent.click(screen.getByRole('tab', { name: 'Oppsett' }));
expect(screen.getByLabelText('Sidestørrelse')).toBeInTheDocument();
expect(screen.getByLabelText('Dokumentspråk')).toBeInTheDocument();
});
@@ -181,6 +193,7 @@ test('internal navigation warns and can be cancelled before discarding a pending
routeGet(() => Promise.resolve({ data: variant } as any));
renderAt(3);
await openContentTab();
fireEvent.change(await screen.findByLabelText('Headline override'), { target: { value: 'Unsaved headline' } });
fireEvent.click(screen.getByRole('button', { name: 'Back to CVs' }));
const dialog = await screen.findByRole('dialog', { name: 'Discard unsaved CV changes?' });
@@ -195,10 +208,14 @@ test('custom entries can be added, edited, reordered and deleted with confirmati
mockedApi.put.mockResolvedValue({ data: variant } as any);
renderAt(3);
await openContentTab();
await screen.findByLabelText('Headline override');
fireEvent.mouseDown(screen.getByLabelText('Section type'));
fireEvent.click(await screen.findByRole('option', { name: 'Additional Experience' }));
fireEvent.click(screen.getByRole('button', { name: 'Add section' }));
fireEvent.click(screen.getByRole('button', { name: 'Add content' }));
const addContentDialog = await screen.findByRole('dialog', { name: 'Add content' });
const additionalExperienceCard = within(addContentDialog).getByText('Additional Experience').closest('.MuiPaper-root');
expect(additionalExperienceCard).not.toBeNull();
fireEvent.click(within(additionalExperienceCard as HTMLElement).getByRole('button', { name: 'Add' }));
await waitFor(() => expect(screen.queryByRole('dialog', { name: 'Add content' })).not.toBeInTheDocument());
const title = screen.getByLabelText(/Section name for custom:/);
fireEvent.change(title, { target: { value: 'Selected projects' } });
fireEvent.click(screen.getByRole('button', { name: 'Expand Selected projects' }));
@@ -253,8 +270,12 @@ test('profile-backed sections expand, reorder, hide and persist variant-only ove
mockedApi.put.mockResolvedValue({ data: variant } as any);
renderAt(3);
await openContentTab();
fireEvent.click(await screen.findByRole('button', { name: 'Expand Experience' }));
expect(screen.getByRole('button', { name: 'Collapse Experience' })).toHaveAttribute('aria-expanded', 'true');
fireEvent.mouseDown(screen.getByLabelText('Presentation'));
fireEvent.click(await screen.findByRole('option', { name: 'Grid' }));
fireEvent.click(within(screen.getByRole('group', { name: 'Columns' })).getByRole('button', { name: '2' }));
fireEvent.click(screen.getByRole('button', { name: 'Move Engineer entry down' }));
fireEvent.click(screen.getByRole('button', { name: 'Hide Lead entry' }));
fireEvent.click(screen.getByRole('button', { name: 'Move Experience section down' }));
@@ -263,7 +284,7 @@ test('profile-backed sections expand, reorder, hide and persist variant-only ove
expect(await screen.findByText('Saved')).toBeInTheDocument();
expect(mockedApi.put).toHaveBeenLastCalledWith('/cv/variants/3', expect.objectContaining({
settings: expect.objectContaining({
sections: expect.arrayContaining([expect.objectContaining({ key: 'experience', itemOrder: ['job-2', 'job-1'] })]),
sections: expect.arrayContaining([expect.objectContaining({ key: 'experience', itemOrder: ['job-2', 'job-1'], presentation: 'grid', columns: 2 })]),
overrides: expect.objectContaining({ 'job-2': expect.objectContaining({ hidden: true }) }),
}),
}));