Files
jobtrackingapp/job-tracker-ui/src/cv-template-preview.test.tsx
T

77 lines
4.7 KiB
TypeScript

import React from "react";
import "@testing-library/jest-dom";
import { act, fireEvent, render, screen, waitFor } from "@testing-library/react";
import CvTemplatePreviewDialog from "./components/cv/CvTemplatePreviewDialog";
import { cvBuilderApi, CvRender, CvTheme } from "./cvBuilder";
import { I18nProvider } from "./i18n/I18nProvider";
const modern: CvTheme = { id: "modern", name: "Modern", category: "Professional", description: "A clear document", layout: "single", accent: "#3157d5", photoShape: "none", supportsIcons: false, atsFriendly: true, requiresPro: false, available: true, swatches: [] };
const code = { ...modern, id: "code", name: "Code" };
const sample = (id: string): CvRender => ({ themeId: id, html: `<html><body><div class="page">${id} sample</div></body></html>`, suggestedFileName: "sample.pdf" });
function deferred<T>() {
let resolve!: (value: T) => void;
const promise = new Promise<T>(yes => { resolve = yes; });
return { promise, resolve };
}
beforeEach(() => {
localStorage.clear();
global.ResizeObserver = class { observe() {} unobserve() {} disconnect() {} };
jest.spyOn(cvBuilderApi, "themePreview").mockResolvedValue(sample("modern"));
});
afterEach(() => jest.restoreAllMocks());
test("candidate loads a labelled sample without applying; Cancel preserves the draft", async () => {
const onApply = jest.fn(); const onClose = jest.fn();
render(<I18nProvider><CvTemplatePreviewDialog theme={modern} onClose={onClose} onApply={onApply} /></I18nProvider>);
expect(await screen.findByTitle("Template sample preview")).toHaveAttribute("srcdoc", expect.stringContaining("modern sample"));
expect(screen.getByText(/Sample content/)).toBeVisible();
expect(onApply).not.toHaveBeenCalled();
fireEvent.click(screen.getByRole("button", { name: "Cancel" }));
expect(onClose).toHaveBeenCalledTimes(1);
expect(onApply).not.toHaveBeenCalled();
});
test("sample scales down to a narrow preview host", async () => {
const width = jest.spyOn(HTMLElement.prototype, "clientWidth", "get").mockReturnValue(280);
render(<I18nProvider><CvTemplatePreviewDialog theme={modern} onClose={jest.fn()} onApply={jest.fn()} /></I18nProvider>);
const frame = await screen.findByTitle("Template sample preview");
expect(Number((frame as HTMLIFrameElement).style.transform.match(/scale\(([^)]+)\)/)?.[1])).toBeLessThan(0.4);
width.mockRestore();
});
test("a late candidate response cannot replace the selected template", async () => {
const held = deferred<CvRender>();
jest.mocked(cvBuilderApi.themePreview).mockImplementation(id => id === "modern" ? held.promise : Promise.resolve(sample("code")));
const view = render(<I18nProvider><CvTemplatePreviewDialog theme={modern} onClose={jest.fn()} onApply={jest.fn()} /></I18nProvider>);
view.rerender(<I18nProvider><CvTemplatePreviewDialog theme={code} onClose={jest.fn()} onApply={jest.fn()} /></I18nProvider>);
await waitFor(() => expect(screen.getByTitle("Template sample preview")).toHaveAttribute("srcdoc", expect.stringContaining("code sample")));
await act(async () => held.resolve(sample("modern")));
expect(screen.getByTitle("Template sample preview")).toHaveAttribute("srcdoc", expect.stringContaining("code sample"));
});
test("failed preview has an explicit retry and cannot apply until it succeeds", async () => {
jest.mocked(cvBuilderApi.themePreview).mockRejectedValueOnce(new Error("synthetic failure"));
const onApply = jest.fn();
render(<I18nProvider><CvTemplatePreviewDialog theme={modern} onClose={jest.fn()} onApply={onApply} /></I18nProvider>);
expect(await screen.findByRole("alert")).toHaveTextContent("Could not preview this design");
expect(screen.getByRole("button", { name: "Apply template" })).toBeDisabled();
fireEvent.click(screen.getByRole("button", { name: "Retry preview" }));
await waitFor(() => expect(screen.getByRole("button", { name: "Apply template" })).toBeEnabled());
fireEvent.click(screen.getByRole("button", { name: "Apply template" }));
expect(onApply).toHaveBeenCalledWith(modern);
});
test("locked candidates cannot request or apply a preview", async () => {
render(<I18nProvider><CvTemplatePreviewDialog theme={{ ...modern, available: false }} onClose={jest.fn()} onApply={jest.fn()} /></I18nProvider>);
expect(screen.getByRole("button", { name: "Apply template" })).toBeDisabled();
expect(cvBuilderApi.themePreview).not.toHaveBeenCalled();
});
test("Bokmål sample labelling and actions are localised", async () => {
localStorage.setItem("uiLanguage", "nb");
render(<I18nProvider><CvTemplatePreviewDialog theme={modern} onClose={jest.fn()} onApply={jest.fn()} /></I18nProvider>);
expect(await screen.findByTitle("Eksempelforhåndsvisning av mal")).toBeInTheDocument();
expect(screen.getByRole("button", { name: "Bruk mal" })).toBeEnabled();
});