import { afterAll, beforeAll, describe, expect, test } from "bun:test"; import { GlobalRegistrator } from "@happy-dom/global-registrator"; import { act, type ReactNode } from "react"; import { createRoot } from "react-dom/client"; import { NavGroup, NavItem, SettingsNav } from "./settings-nav"; beforeAll(() => { GlobalRegistrator.register(); ( globalThis as typeof globalThis & { IS_REACT_ACT_ENVIRONMENT: boolean } ).IS_REACT_ACT_ENVIRONMENT = true; }); afterAll(() => { GlobalRegistrator.unregister(); }); async function mount(node: ReactNode) { const container = document.createElement("div"); document.body.append(container); const root = createRoot(container); await act(async () => root.render(node)); return { container, async cleanup() { await act(async () => root.unmount()); container.remove(); }, }; } describe("NavItem", () => { test("the active item is the current page and shows its bar", async () => { const view = await mount( } active />, ); try { const link = view.container.querySelector("a")!; expect(link.getAttribute("aria-current")).toBe("page"); expect(link.getAttribute("data-active")).toBe("true"); expect(link.textContent).toBe("Schedules"); } finally { await view.cleanup(); } }); test("a router link child gets the item's content and classes", async () => { const view = await mount( , ); try { const links = view.container.querySelectorAll("a"); expect(links).toHaveLength(1); const link = links[0]!; expect(link.hasAttribute("data-router-link")).toBe(true); expect(link.getAttribute("data-slot")).toBe("nav-item"); expect(link.textContent).toBe("Knowledge, 3 waiting for review"); expect(link.getAttribute("aria-current")).toBeNull(); } finally { await view.cleanup(); } }); test("a disabled item is not a link, stays focusable and carries its reason", async () => { const view = await mount( , ); try { expect(view.container.querySelector("a")).toBeNull(); const item = view.container.querySelector('[data-slot="nav-item"]')!; expect(item.getAttribute("aria-disabled")).toBe("true"); expect(item.getAttribute("role")).toBe("link"); expect(item.textContent).toBe("Insights"); expect(item.getAttribute("tabindex")).toBe("0"); expect(item.getAttribute("aria-current")).toBeNull(); const reasonId = item.getAttribute("aria-describedby")!.split(" ")[0]!; expect(document.getElementById(reasonId)?.textContent).toBe( "Only workspace admins can see Insights.", ); } finally { await view.cleanup(); } }); test("a collapsed item keeps its name for screen readers", async () => { const view = await mount( } collapsed badge="2" />, ); try { const link = view.container.querySelector("a")!; expect(link.querySelector(".sr-only")?.textContent).toBe("Schedules"); // Counts hide with the label; the tooltip names the item. expect(link.textContent).toBe("Schedules"); } finally { await view.cleanup(); } }); }); describe("NavGroup and SettingsNav", () => { test("groups are labelled lists inside a named nav", async () => { const view = await mount( , ); try { const nav = view.container.querySelector("nav")!; expect(nav.getAttribute("aria-label")).toBe("Workspace settings"); const group = nav.querySelector('[role="group"]')!; const labelId = group.getAttribute("aria-labelledby")!; expect(document.getElementById(labelId)?.textContent).toBe("Runtime"); expect(group.querySelectorAll("ul > li > a")).toHaveLength(2); } finally { await view.cleanup(); } }); });