Files
jobtrackingapp/job-tracker-ui/src/application-checklist.test.tsx
T
cesnimda 9edcbfc5de feat(workspace): refine career workflows
Make job/CV comparisons language-aware and filter recruitment noise. Improve responsive career navigation, shared spacing, dashboard priorities, settings, localized workspace controls, and portable browser tests.
2026-08-29 16:44:44 +02:00

133 lines
4.8 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("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"));
});