// @vitest-environment jsdom import { act } from "react"; import { createRoot } from "react-dom/client"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { MemoryRouter } from "@/lib/router"; vi.mock("@/api/skillRegistry", () => ({ skillRegistryApi: { list: vi.fn(async () => [ { id: "a/one", name: "One", description: null, sourceId: "a", category: null, activeVersionId: "v1", removedAt: null, averageRating: null, ratingCount: null, taskCount: null, avgCostUsd: null, lastUsedAt: null }, { id: "a/two", name: "Two", description: null, sourceId: "a", category: null, activeVersionId: "v1", removedAt: null, averageRating: null, ratingCount: null, taskCount: null, avgCostUsd: null, lastUsedAt: null }, { id: "a/removed", name: "R", description: null, sourceId: "a", category: null, activeVersionId: null, removedAt: 123, averageRating: null, ratingCount: null, taskCount: null, avgCostUsd: null, lastUsedAt: null }, ]), }, })); vi.mock("@/api/skillGroups", () => ({ skillGroupsApi: { listGroups: vi.fn(async () => [{ id: "g1" }, { id: "g2" }]), }, })); vi.mock("@/context/CompanyContext", () => ({ useCompany: () => ({ companies: [], selectedCompanyId: "c1", selectedCompany: { id: "c1", name: "Test", issuePrefix: "NEX" }, selectionSource: "manual" as const, loading: false, error: null, setSelectedCompanyId: () => {}, reloadCompanies: async () => {}, createCompany: async () => { throw new Error("not implemented"); }, }), })); import { SkillsSection } from "./SkillsSection"; // eslint-disable-next-line @typescript-eslint/no-explicit-any (globalThis as any).IS_REACT_ACT_ENVIRONMENT = true; describe("SkillsSection", () => { let container: HTMLDivElement; let root: ReturnType | null = null; let queryClient: QueryClient; beforeEach(() => { container = document.createElement("div"); document.body.appendChild(container); root = null; queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } } }); }); afterEach(() => { if (root) { act(() => { root!.unmount(); }); root = null; } if (container.parentNode) container.remove(); queryClient.clear(); }); async function renderAndFlush() { root = createRoot(container); act(() => { root!.render( , ); }); for (let i = 0; i < 20; i++) { await act(async () => { await new Promise((resolve) => setTimeout(resolve, 0)); }); } } it("renders the Skills section and counts only non-removed skills", async () => { await renderAndFlush(); expect(container.querySelector("h2")?.textContent).toBe("Skills"); expect(container.textContent).toContain("Installed skills"); // Exactly 2 non-removed skills from the mock const counts = Array.from(container.querySelectorAll("span")).filter( (s) => s.textContent === "2", ); expect(counts.length).toBeGreaterThan(0); }); it("links Open Skill Aggregator to the company-prefixed skills route", async () => { await renderAndFlush(); const openLink = container.querySelector("a[aria-label='Open Skill Aggregator']"); expect(openLink).not.toBeNull(); expect(openLink?.getAttribute("href")).toBe("/NEX/skills"); }); });