import { fireEvent, render, screen, waitFor } from "@testing-library/react"; import type React from "react"; import { toast } from "sonner"; import { beforeEach, describe, expect, it, vi } from "vitest"; import type { Job } from "../../shared/types"; import * as api from "../api"; import { ReadyPanel } from "./ReadyPanel"; vi.mock("@/components/ui/dropdown-menu", () => { return { DropdownMenu: ({ children }: { children: React.ReactNode }) => (
{children}
), DropdownMenuTrigger: ({ children }: { children: React.ReactNode }) => ( <>{children} ), DropdownMenuContent: ({ children }: { children: React.ReactNode }) => (
{children}
), DropdownMenuItem: ({ children, onSelect, ...props }: { children: React.ReactNode; onSelect?: () => void; }) => ( ), DropdownMenuSeparator: () =>
, }; }); vi.mock("../hooks/useProfile", () => ({ useProfile: () => ({ personName: "Test" }), })); vi.mock("../hooks/useSettings", () => ({ useSettings: () => ({ showSponsorInfo: false }), })); vi.mock("../api", () => ({ rescoreJob: vi.fn(), getResumeProjectsCatalog: vi.fn().mockResolvedValue([]), markAsApplied: vi.fn(), generateJobPdf: vi.fn(), checkSponsor: vi.fn(), skipJob: vi.fn(), updateJob: vi.fn(), })); vi.mock("sonner", () => ({ toast: { success: vi.fn(), error: vi.fn(), message: vi.fn(), }, })); const createJob = (overrides: Partial = {}): Job => ({ id: "job-1", source: "linkedin", sourceJobId: null, jobUrlDirect: null, datePosted: null, title: "Backend Engineer", employer: "Acme", employerUrl: null, jobUrl: "https://example.com/job", applicationLink: "https://example.com/apply", disciplines: null, deadline: "2025-02-01", salary: "GBP 50k", location: "London", degreeRequired: null, starting: null, jobDescription: "Build APIs", status: "ready", suitabilityScore: 82, suitabilityReason: "Strong fit", tailoredSummary: null, tailoredHeadline: null, tailoredSkills: null, selectedProjectIds: null, pdfPath: null, notionPageId: null, sponsorMatchScore: null, sponsorMatchNames: null, jobType: null, salarySource: null, salaryInterval: null, salaryMinAmount: null, salaryMaxAmount: null, salaryCurrency: null, isRemote: null, jobLevel: null, jobFunction: null, listingType: null, emails: null, companyIndustry: null, companyLogo: null, companyUrlDirect: null, companyAddresses: null, companyNumEmployees: null, companyRevenue: null, companyDescription: null, skills: null, experienceRange: null, companyRating: null, companyReviewsCount: null, vacancyCount: null, workFromHomeType: null, discoveredAt: "2025-01-01T00:00:00Z", processedAt: null, appliedAt: null, createdAt: "2025-01-01T00:00:00Z", updatedAt: "2025-01-02T00:00:00Z", ...overrides, }); describe("ReadyPanel", () => { beforeEach(() => { vi.clearAllMocks(); }); it("re-runs the fit assessment from the menu", async () => { const onJobUpdated = vi.fn().mockResolvedValue(undefined); const job = createJob(); vi.mocked(api.rescoreJob).mockResolvedValue(job as Job); render( , ); fireEvent.click( screen.getByRole("menuitem", { name: /recalculate match/i }), ); await waitFor(() => expect(api.rescoreJob).toHaveBeenCalledWith("job-1")); expect(onJobUpdated).toHaveBeenCalled(); expect(toast.success).toHaveBeenCalledWith("Match recalculated"); }); });