import React from "react"; import "@testing-library/jest-dom"; import { render, screen, waitFor } from "@testing-library/react"; import { cvBuilderApi } from "./cvBuilder"; import CareerWorkspaceOverview, { CareerWorkspaceImportRun } from "./views/career/CareerWorkspaceOverview"; jest.mock("./cvBuilder", () => ({ cvBuilderApi: { list: jest.fn() }, })); const mockedList = cvBuilderApi.list as jest.MockedFunction; function renderOverview({ percent = 0, missing = [], runs = [], loading = false, loadError = null, }: { percent?: number; missing?: string[]; runs?: CareerWorkspaceImportRun[]; loading?: boolean; loadError?: string | null; } = {}) { return render( , ); } beforeEach(() => { mockedList.mockResolvedValue([]); }); afterEach(() => { jest.clearAllMocks(); }); test("first-run workspace presents concise profile, import, general CV and job-specific actions", async () => { renderOverview(); expect(screen.getByRole("heading", { name: "Career Workspace", level: 1 })).toBeInTheDocument(); expect(await screen.findByText(/start by adding career information manually or importing a cv/i)).toBeInTheDocument(); expect(screen.queryByText(/job-specific CVs stay separate and never overwrite/i)).not.toBeInTheDocument(); expect(screen.getByRole("link", { name: "Edit career profile" })).toHaveAttribute("href", "#career-profile-editor"); expect(screen.getAllByRole("link", { name: "Import a CV" })[0]).toHaveAttribute("href", "#career-cv-import"); expect(screen.getByRole("link", { name: "Create a general CV" })).toHaveAttribute("href", "/career/builder"); expect(screen.getByRole("link", { name: "Choose a job" })).toHaveAttribute("href", "/jobs"); expect(await screen.findByText("No CV documents yet.")).toBeInTheDocument(); }); test("returning workspace shows missing profile information and recent general and job-specific CVs", async () => { mockedList.mockResolvedValue([ { id: 8, name: "Backend CV", themeId: "modern", publicSlug: "backend", isPublic: false, version: 2, jobApplicationId: 42, updatedAtUtc: "2026-08-08T10:00:00Z" }, { id: 7, name: "General CV", themeId: "modern", publicSlug: "general", isPublic: false, version: 1, jobApplicationId: null, updatedAtUtc: "2026-08-07T10:00:00Z" }, ]); renderOverview({ percent: 70, missing: ["Projects", "Education", "Languages", "Interests"] }); expect(screen.getByText("70%")).toBeInTheDocument(); expect(screen.getByText("Add next: Projects, Education, Languages")).toBeInTheDocument(); expect(await screen.findByText("Backend CV")).toBeInTheDocument(); expect(screen.getAllByText("General CV").length).toBeGreaterThan(1); expect(screen.getByText("Job-specific")).toBeInTheDocument(); expect(screen.getAllByRole("link", { name: "Open", exact: true })[0]).toHaveAttribute("href", "/career/builder/8"); }); test.each([ ["pending_review", "Review imported CV", /waiting for your approval/i], ["queued", "View CV processing", /is processing/i], ["failed", "Resolve CV import", /needs attention/i], ])("workspace exposes the %s import state as a resumable action", async (status, label, message) => { renderOverview({ runs: [{ id: 13, status }] }); expect(screen.getAllByRole("link", { name: label })[0]).toHaveAttribute("href", "#career-cv-import"); expect(screen.getByText(message)).toBeInTheDocument(); await waitFor(() => expect(mockedList).toHaveBeenCalledTimes(1)); }); test("workspace represents loading and profile-load errors without inventing status", async () => { renderOverview({ loading: true, loadError: "offline" }); expect(screen.getByRole("progressbar", { name: "Career profile completeness" })).toBeInTheDocument(); expect(screen.getByText(/workspace status is unavailable/i)).toBeInTheDocument(); expect(screen.queryByText(/start by adding career information/i)).not.toBeInTheDocument(); await waitFor(() => expect(mockedList).toHaveBeenCalledTimes(1)); });