9edcbfc5de
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.
133 lines
4.8 KiB
TypeScript
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"));
|
|
});
|