fix(cv): bind letter proposals to current sources and preserve document choices
This commit is contained in:
@@ -12,11 +12,11 @@ const mockedApi = api as jest.Mocked<typeof api>;
|
||||
const settings: CoverLetterDocumentSettings = {
|
||||
schemaVersion: 1, senderName: "Saved sender", senderTitle: "Saved title", senderEmail: "saved@example.test", senderPhone: "123", senderLocation: "Oslo",
|
||||
recipient: "Preserve recipient", subject: "Preserve subject", date: "2026-10-25", themeId: "modern", accentColor: "#2563eb", bodyFont: "Arial, Helvetica, sans-serif",
|
||||
baseFontSizePt: 10, lineHeight: 1.42, pageMarginMm: 16, pageSize: "a4",
|
||||
baseFontSizePt: 10, lineHeight: 1.42, pageMarginMm: 16, pageSize: "a4", language: "nb-NO",
|
||||
};
|
||||
const letter = { revision: 7, text: "Preserve letter body", currentVersion: 2, aiSuggestionCount: 0, documentSettings: settings,
|
||||
versions: [{ version: 1, source: "manual", aiAction: null, length: 20, createdAtUtc: "2026-10-25T10:00:00Z", isCurrent: false }] };
|
||||
const variant = { id: 3, name: "Chosen CV", settings: { themeId: "modern", headline: null, accentColor: null, bodyFont: null, baseFontSizePt: null, lineHeight: null, pageMarginMm: null, pageSize: null, density: "compact" } };
|
||||
const variant = { id: 3, name: "Chosen CV", settings: { themeId: "modern", headline: null, accentColor: null, bodyFont: null, baseFontSizePt: null, lineHeight: null, pageMarginMm: null, pageSize: null, density: "compact", language: "en" } };
|
||||
const wrap = (ui: React.ReactNode) => <I18nProvider><ConfirmProvider>{ui}</ConfirmProvider></I18nProvider>;
|
||||
function deferred<T>() {
|
||||
let resolve!: (value: T) => void;
|
||||
@@ -52,6 +52,8 @@ test("contact copy is a reviewed canonical snapshot; cancel is inert and apply p
|
||||
expect(snapshot).toHaveTextContent("Canonical name");
|
||||
expect(snapshot).toHaveTextContent("Canonical title");
|
||||
expect(snapshot).not.toHaveTextContent("private");
|
||||
for (const name of [/Sender name/, /Professional title/, /Email/, /Phone/]) expect(within(snapshot).getByRole("checkbox", { name })).toBeChecked();
|
||||
expect(within(snapshot).getByRole("checkbox", { name: /Location/ })).not.toBeChecked();
|
||||
expect(onChange).not.toHaveBeenCalled();
|
||||
fireEvent.click(screen.getByRole("button", { name: "Cancel" }));
|
||||
await waitFor(() => expect(screen.queryByRole("dialog")).not.toBeInTheDocument());
|
||||
@@ -60,11 +62,52 @@ test("contact copy is a reviewed canonical snapshot; cancel is inert and apply p
|
||||
await selectSource();
|
||||
await screen.findByRole("region", { name: "Contact snapshot" });
|
||||
fireEvent.click(screen.getByRole("button", { name: "Use this copy" }));
|
||||
expect(onChange).toHaveBeenCalledWith({ ...settings, senderName: "Canonical name", senderTitle: "Canonical title", senderEmail: "canonical@example.test", senderPhone: "+47 555", senderLocation: "Bergen", sourceCvId: 3 });
|
||||
expect(onChange).toHaveBeenCalledWith({ ...settings, senderName: "Canonical name", senderTitle: "Canonical title", senderEmail: "canonical@example.test", senderPhone: "+47 555", sourceCvId: 3 });
|
||||
expect(mockedApi.get).not.toHaveBeenCalledWith("/auth/me");
|
||||
expect(mockedApi.put).not.toHaveBeenCalled(); expect(mockedApi.post).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test("contact location copies only after selection and unselected contact values preserve destination fields", async () => {
|
||||
const onChange = jest.fn();
|
||||
render(wrap(<CoverLetterDocumentFields settings={settings} disabled={false} onChange={onChange} />));
|
||||
fireEvent.click(screen.getByRole("button", { name: "Copy contact from CV" }));
|
||||
await selectSource();
|
||||
const snapshot = within(await screen.findByRole("region", { name: "Contact snapshot" }));
|
||||
for (const name of [/Sender name/, /Professional title/, /Email/, /Phone/]) fireEvent.click(snapshot.getByRole("checkbox", { name }));
|
||||
fireEvent.click(snapshot.getByRole("checkbox", { name: /Location/ }));
|
||||
fireEvent.click(screen.getByRole("button", { name: "Use this copy" }));
|
||||
expect(onChange).toHaveBeenCalledWith({ ...settings, senderLocation: "Bergen", sourceCvId: 3 });
|
||||
expect(mockedApi.put).not.toHaveBeenCalled(); expect(mockedApi.post).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test("blank source contacts and choosing no fields never clear destination contacts", async () => {
|
||||
const originalGet = mockedApi.get.getMockImplementation()!;
|
||||
mockedApi.get.mockImplementation(((url: string) => url === "/career/profile" ? Promise.resolve({ data: { profile: { contact: { fullName: "Canonical name", email: "", phone: " " } } } }) : originalGet(url)) as any);
|
||||
const onChange = jest.fn();
|
||||
render(wrap(<CoverLetterDocumentFields settings={settings} disabled={false} onChange={onChange} />));
|
||||
fireEvent.click(screen.getByRole("button", { name: "Copy contact from CV" }));
|
||||
await selectSource();
|
||||
const snapshot = within(await screen.findByRole("region", { name: "Contact snapshot" }));
|
||||
expect(snapshot.getByRole("checkbox", { name: /Email/ })).toBeDisabled();
|
||||
expect(snapshot.getByRole("checkbox", { name: /Phone/ })).toBeDisabled();
|
||||
fireEvent.click(snapshot.getByRole("checkbox", { name: /Sender name/ }));
|
||||
fireEvent.click(screen.getByRole("button", { name: "Use this copy" }));
|
||||
expect(onChange).toHaveBeenCalledWith(settings);
|
||||
});
|
||||
|
||||
test("letter language is an explicit document draft and saves independently of interface and CV language", async () => {
|
||||
mockedApi.put.mockResolvedValue({ data: { ...letter, revision: 8, documentSettings: { ...settings, language: "en" } } } as any);
|
||||
render(wrap(<ApplicationCoverLetterSection jobId={7} />));
|
||||
const language = await screen.findByRole("combobox", { name: "Letter language" });
|
||||
expect(language).toHaveTextContent(/Norsk bokmål/i);
|
||||
fireEvent.mouseDown(language);
|
||||
fireEvent.click(await screen.findByRole("option", { name: "English" }));
|
||||
expect(mockedApi.put).not.toHaveBeenCalled();
|
||||
fireEvent.click(screen.getByRole("button", { name: "Save" }));
|
||||
await waitFor(() => expect(mockedApi.put).toHaveBeenCalledWith("/jobapplications/7/cover-letter", expect.objectContaining({ documentSettings: { ...settings, language: "en" }, expectedVersion: 7 })));
|
||||
expect(screen.getByRole("textbox", { name: "Cover letter" })).toHaveValue(letter.text);
|
||||
});
|
||||
|
||||
test("design copy resolves null overrides through actual theme defaults and effective CV density", async () => {
|
||||
const onChange = jest.fn();
|
||||
render(wrap(<CoverLetterDocumentFields settings={settings} disabled={false} onChange={onChange} />));
|
||||
@@ -109,7 +152,7 @@ test("failed copy reads are retryable and cannot apply or save a partial snapsho
|
||||
});
|
||||
|
||||
test("restoring a historical letter restores saved details with text after explicit dirty confirmation", async () => {
|
||||
mockedApi.post.mockResolvedValue({ data: { ...letter, revision: 8, text: "Historical body", documentSettings: { ...settings, subject: "Historical subject", senderName: "Historical sender" } } } as any);
|
||||
mockedApi.post.mockResolvedValue({ data: { ...letter, revision: 8, text: "Historical body", documentSettings: { ...settings, subject: "Historical subject", senderName: "Historical sender", language: "en" } } } as any);
|
||||
render(wrap(<ApplicationCoverLetterSection jobId={7} />));
|
||||
fireEvent.change(await screen.findByRole("textbox", { name: "Subject" }), { target: { value: "Local unsaved subject" } });
|
||||
expect(screen.queryByRole("region", { name: "Letter text preview" })).not.toBeInTheDocument();
|
||||
@@ -118,6 +161,7 @@ test("restoring a historical letter restores saved details with text after expli
|
||||
fireEvent.click(within(dialog).getByRole("button", { name: "Discard and restore" }));
|
||||
await waitFor(() => expect(screen.getByRole("textbox", { name: "Subject" })).toHaveValue("Historical subject"));
|
||||
expect(screen.getByRole("textbox", { name: "Sender name" })).toHaveValue("Historical sender");
|
||||
expect(screen.getByRole("combobox", { name: "Letter language" })).toHaveTextContent("English");
|
||||
expect(screen.getByRole("textbox", { name: "Cover letter" })).toHaveValue("Historical body");
|
||||
expect(mockedApi.post).toHaveBeenCalledWith("/jobapplications/7/cover-letter/versions/1/restore", { expectedVersion: 7 });
|
||||
expect(mockedApi.put).not.toHaveBeenCalled();
|
||||
|
||||
Reference in New Issue
Block a user