fix(cv): protect unsaved builder edits
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import '@testing-library/jest-dom';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { MemoryRouter, Route, Routes } from 'react-router-dom';
|
||||
import { fireEvent, render, screen } from '@testing-library/react';
|
||||
import { createMemoryRouter, RouterProvider } from 'react-router-dom';
|
||||
|
||||
import CvBuilderEditor from './views/CvBuilderEditor';
|
||||
import { I18nProvider } from './i18n/I18nProvider';
|
||||
@@ -37,14 +37,14 @@ const variant = {
|
||||
};
|
||||
|
||||
function renderAt(id: number) {
|
||||
const router = createMemoryRouter([
|
||||
{ path: '/career/builder/:id', element: <CvBuilderEditor /> },
|
||||
{ path: '/career/builder', element: <div>CV list destination</div> },
|
||||
], { initialEntries: [`/career/builder/${id}`] });
|
||||
return render(
|
||||
<I18nProvider>
|
||||
<ToastProvider>
|
||||
<MemoryRouter initialEntries={[`/career/builder/${id}`]} future={{ v7_startTransition: true, v7_relativeSplatPath: true }}>
|
||||
<Routes>
|
||||
<Route path="/career/builder/:id" element={<CvBuilderEditor />} />
|
||||
</Routes>
|
||||
</MemoryRouter>
|
||||
<RouterProvider router={router} future={{ v7_startTransition: true }} />
|
||||
</ToastProvider>
|
||||
</I18nProvider>,
|
||||
);
|
||||
@@ -52,6 +52,7 @@ function renderAt(id: number) {
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
mockedApi.put.mockReset();
|
||||
mockedApi.post.mockResolvedValue({ data: { themeId: 'nordic', html: '<p>cv</p>', suggestedFileName: 'cv.pdf' } } as any);
|
||||
});
|
||||
|
||||
@@ -91,3 +92,44 @@ test("another user's variant is refused by the server and never rendered", async
|
||||
|
||||
expect(await screen.findByText(/Could not open this CV/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('invalid names are explained and are not silently autosaved', async () => {
|
||||
routeGet(() => Promise.resolve({ data: variant } as any));
|
||||
renderAt(3);
|
||||
|
||||
fireEvent.change(await screen.findByLabelText('CV name'), { target: { value: '' } });
|
||||
|
||||
expect(screen.getByText('Enter a name before saving.')).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: 'Save now' })).toBeDisabled();
|
||||
expect(mockedApi.put).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('failed autosave is visible and can be retried with the latest data', async () => {
|
||||
routeGet(() => Promise.resolve({ data: variant } as any));
|
||||
mockedApi.put.mockRejectedValueOnce(new Error('offline')).mockResolvedValueOnce({ data: variant } as any);
|
||||
renderAt(3);
|
||||
|
||||
fireEvent.change(await screen.findByLabelText('Headline override'), { target: { value: 'Platform Engineer' } });
|
||||
expect(screen.getByText('Unsaved')).toBeInTheDocument();
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Save now' }));
|
||||
|
||||
expect(await screen.findByText('Save failed')).toBeInTheDocument();
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Save now' }));
|
||||
expect(await screen.findByText('Saved')).toBeInTheDocument();
|
||||
expect(mockedApi.put).toHaveBeenLastCalledWith('/cv/variants/3', expect.objectContaining({
|
||||
name: 'Backend CV',
|
||||
settings: expect.objectContaining({ headline: 'Platform Engineer' }),
|
||||
}));
|
||||
});
|
||||
|
||||
test('internal navigation warns and can be cancelled before discarding a pending edit', async () => {
|
||||
routeGet(() => Promise.resolve({ data: variant } as any));
|
||||
const confirm = jest.spyOn(window, 'confirm').mockReturnValue(false);
|
||||
renderAt(3);
|
||||
|
||||
fireEvent.change(await screen.findByLabelText('Headline override'), { target: { value: 'Unsaved headline' } });
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Back to CVs' }));
|
||||
expect(confirm).toHaveBeenCalledWith('This CV has unsaved changes. Leave and discard them?');
|
||||
expect(screen.getByDisplayValue('Backend CV')).toBeInTheDocument();
|
||||
confirm.mockRestore();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user