147 lines
5.7 KiB
TypeScript
147 lines
5.7 KiB
TypeScript
import React from "react";
|
|
import "@testing-library/jest-dom";
|
|
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
|
|
|
|
import ApplicationChecklist from "./components/ApplicationChecklist";
|
|
import { api } from "./api";
|
|
import { I18nProvider } from "./i18n/I18nProvider";
|
|
|
|
jest.mock("./api", () => ({
|
|
api: {
|
|
get: jest.fn(),
|
|
post: jest.fn(),
|
|
patch: jest.fn(),
|
|
put: jest.fn(),
|
|
delete: jest.fn(),
|
|
interceptors: { request: { use: jest.fn() }, response: { use: jest.fn() } },
|
|
},
|
|
getApiErrorMessage: (_e: any, fallback?: string) => fallback || "Request failed.",
|
|
}));
|
|
|
|
const mockedApi = api as jest.Mocked<typeof api>;
|
|
|
|
const renderChecklist = () => render(<I18nProvider><ApplicationChecklist jobId={7} /></I18nProvider>);
|
|
|
|
const item = (over: Partial<any> = {}) => ({
|
|
id: 1,
|
|
systemKey: "prepare-cv",
|
|
title: "Prepare a CV for this role",
|
|
description: "Attach a CV variant tailored to this application.",
|
|
category: "preparation",
|
|
status: "pending",
|
|
section: "cv",
|
|
sortOrder: 0,
|
|
isSystemGenerated: true,
|
|
isAutoCompleted: false,
|
|
completedAt: null,
|
|
...over,
|
|
});
|
|
|
|
const checklist = (items: any[]) => ({
|
|
items,
|
|
progress: {
|
|
total: items.filter((i) => i.status !== "dismissed").length,
|
|
completed: items.filter((i) => i.status === "done").length,
|
|
dismissed: items.filter((i) => i.status === "dismissed").length,
|
|
percent: 0,
|
|
},
|
|
});
|
|
|
|
beforeEach(() => {
|
|
jest.clearAllMocks();
|
|
window.localStorage.setItem("uiLanguage", "en");
|
|
mockedApi.get.mockResolvedValue({
|
|
data: checklist([
|
|
item(),
|
|
item({ id: 2, systemKey: "create-cover-letter", title: "Create a cover letter", status: "done", isAutoCompleted: true }),
|
|
item({ id: 3, systemKey: null, title: "Ask Sara for a referral", category: "custom", isSystemGenerated: false, description: null }),
|
|
]),
|
|
} as any);
|
|
});
|
|
|
|
test("localizes system checklist content without changing user-authored tasks", async () => {
|
|
window.localStorage.setItem("uiLanguage", "nb");
|
|
renderChecklist();
|
|
|
|
expect(await screen.findByText("Forbered en CV for denne stillingen")).toBeInTheDocument();
|
|
expect(screen.getByText("Dine egne oppgaver")).toBeInTheDocument();
|
|
expect(screen.getByText("Ask Sara for a referral")).toBeInTheDocument();
|
|
});
|
|
|
|
test("renders the checklist grouped by category with progress", async () => {
|
|
renderChecklist();
|
|
|
|
expect(await screen.findByText("Prepare a CV for this role")).toBeInTheDocument();
|
|
expect(screen.getByText("Before applying")).toBeInTheDocument();
|
|
expect(screen.getByText("Your own tasks")).toBeInTheDocument();
|
|
expect(screen.getByText("1 of 3 done")).toBeInTheDocument();
|
|
// Items already satisfied by an existing readiness signal are marked as detected, not hand-ticked.
|
|
expect(screen.getByText("Detected")).toBeInTheDocument();
|
|
});
|
|
|
|
test("completing an item patches its status", async () => {
|
|
mockedApi.patch.mockResolvedValue({ data: item({ status: "done" }) } as any);
|
|
|
|
renderChecklist();
|
|
fireEvent.click(await screen.findByRole("checkbox", { name: "Prepare a CV for this role" }));
|
|
|
|
await waitFor(() =>
|
|
expect(mockedApi.patch).toHaveBeenCalledWith("/jobapplications/7/checklist/1", { status: "done" }));
|
|
});
|
|
|
|
test("un-ticking a completed item sends it back to pending", async () => {
|
|
mockedApi.patch.mockResolvedValue({ data: item({ id: 2, status: "pending" }) } as any);
|
|
|
|
renderChecklist();
|
|
fireEvent.click(await screen.findByRole("checkbox", { name: "Create a cover letter" }));
|
|
|
|
await waitFor(() =>
|
|
expect(mockedApi.patch).toHaveBeenCalledWith("/jobapplications/7/checklist/2", { status: "pending" }));
|
|
});
|
|
|
|
test("adding a custom task posts the title", async () => {
|
|
mockedApi.post.mockResolvedValue({ data: item({ id: 9, systemKey: null, isSystemGenerated: false }) } as any);
|
|
|
|
renderChecklist();
|
|
fireEvent.change(await screen.findByLabelText(/Add your own task/i), { target: { value: "Email the hiring manager" } });
|
|
fireEvent.click(screen.getByRole("button", { name: "Add" }));
|
|
|
|
await waitFor(() => expect(mockedApi.post).toHaveBeenCalledWith(
|
|
"/jobapplications/7/checklist",
|
|
{ title: "Email the hiring manager", description: undefined, category: undefined },
|
|
));
|
|
});
|
|
|
|
test("reordering sends the new id order", async () => {
|
|
mockedApi.put.mockResolvedValue({ data: checklist([]) } as any);
|
|
|
|
renderChecklist();
|
|
fireEvent.click(await screen.findByRole("button", { name: "Move down: Prepare a CV for this role" }));
|
|
|
|
await waitFor(() =>
|
|
expect(mockedApi.put).toHaveBeenCalledWith("/jobapplications/7/checklist/order", [2, 1, 3]));
|
|
});
|
|
|
|
test("move and remove tooltips describe their wrappers without replacing item-specific button names", async () => {
|
|
mockedApi.patch.mockReturnValueOnce(new Promise(() => {}));
|
|
renderChecklist();
|
|
const down = await screen.findByRole("button", { name: "Move down: Prepare a CV for this role" });
|
|
for (const label of ["Move up: Prepare a CV for this role", "Move down: Prepare a CV for this role", "Remove: Prepare a CV for this role"]) {
|
|
expect(screen.getByRole("button", { name: label }).parentElement).not.toHaveAttribute("aria-label");
|
|
}
|
|
fireEvent.click(screen.getByRole("checkbox", { name: "Prepare a CV for this role" }));
|
|
expect(down).toBeDisabled();
|
|
fireEvent.mouseOver(down.parentElement!);
|
|
expect(await screen.findByRole("tooltip")).toHaveTextContent("Move down");
|
|
expect(mockedApi.put).not.toHaveBeenCalled();
|
|
});
|
|
|
|
test("removing an item calls delete", async () => {
|
|
mockedApi.delete.mockResolvedValue({ data: undefined } as any);
|
|
|
|
renderChecklist();
|
|
fireEvent.click(await screen.findByRole("button", { name: "Remove: Ask Sara for a referral" }));
|
|
|
|
await waitFor(() => expect(mockedApi.delete).toHaveBeenCalledWith("/jobapplications/7/checklist/3"));
|
|
});
|