Files
jobtrackingapp/job-tracker-ui/src/a3-navigation.test.tsx
T

76 lines
4.8 KiB
TypeScript

import React, { useState } from "react";
import "@testing-library/jest-dom";
import { fireEvent, render, screen, within, waitFor } from "@testing-library/react";
import { I18nProvider } from "./i18n/I18nProvider";
import JobTrackerThemeProvider from "./ThemeProvider";
import AppShell, { type NavItem } from "./layout/AppShell";
let mockMobile = true;
jest.mock("@mui/material/useMediaQuery", () => ({ __esModule: true, default: () => mockMobile }));
const nav: NavItem[] = [
{ to: "/dashboard", label: "Dashboard", icon: <span /> },
{ to: "/jobs", label: "Jobs", icon: <span />, activeFor: ["/applications"], children: [{ to: "/companies", label: "Companies", icon: <span /> }] },
{ to: "/discover", label: "Discovery", icon: <span /> },
{ to: "/career", label: "Career profile", icon: <span />, children: [{ to: "/career/builder", label: "CV Builder", icon: <span /> }] },
];
const bottom: NavItem[] = [
{ to: "/settings", label: "Settings", icon: <span /> },
{ to: "/admin/users", label: "Hidden admin", icon: <span />, hidden: true },
];
function Shell({ initial = "/dashboard" }: { initial?: string }) {
const [pathname, setPath] = useState(initial);
const [open, setOpen] = useState(false);
return <JobTrackerThemeProvider preference="light" systemPrefersDark={false}><I18nProvider>
<AppShell pageTitle={pathname} breadcrumbs={[]} pathname={pathname} nav={nav} navBottom={bottom} drawerOpen={open}
onToggleDrawer={setOpen} onNavigate={to => { setOpen(false); setPath(to); }}><button>Page action</button></AppShell>
</I18nProvider></JobTrackerThemeProvider>;
}
beforeEach(() => { localStorage.clear(); mockMobile = true; });
test.each([
["en", "Primary navigation", "Applications", "Discover", "Career", "More"],
["nb", "Hovednavigasjon", "Søknader", "Finn jobber", "Karriere", "Mer"],
])("%s hybrid navigation has five labelled destinations and retains secondary tools", (language, label, applications, discover, career, more) => {
localStorage.setItem("uiLanguage", language);
render(<Shell initial="/companies" />);
const primary = screen.getByRole("navigation", { name: label });
expect(within(primary).getAllByRole("button")).toHaveLength(5);
for (const name of [applications, discover, career, more]) expect(within(primary).getByRole("button", { name })).toBeVisible();
expect(within(primary).getByRole("button", { name: more })).toHaveAttribute("aria-current", "page");
fireEvent.click(within(primary).getByRole("button", { name: more }));
const drawer = screen.getByRole("dialog");
expect(within(drawer).getByRole("button", { name: "Companies" })).toHaveAttribute("aria-current", "page");
expect(within(drawer).queryByText("Hidden admin")).not.toBeInTheDocument();
fireEvent.click(within(drawer).getByRole("button", { name: "Settings" }));
expect(screen.getByRole("heading", { level: 1, name: "/settings" })).toHaveFocus();
});
test.each(["/career/builder/42", "/applications/42", "/jobs/42"])("%s selects its primary destination and keyboard route focus survives", initial => {
localStorage.setItem("uiLanguage", "en");
render(<Shell initial={initial} />);
const primary = screen.getByRole("navigation", { name: "Primary navigation" });
expect(within(primary).getAllByRole("button").filter(button => button.getAttribute("aria-current") === "page")).toHaveLength(1);
expect(within(primary).getByRole("button", { name: initial.startsWith("/career") ? "Career" : "Applications" })).toHaveAttribute("aria-current", "page");
fireEvent.click(within(primary).getByRole("button", { name: "Discover" }));
expect(screen.getByRole("heading", { name: "/discover", level: 1 })).toHaveFocus();
});
test("desktop retains labelled collapse, persistence, and all secondary destinations", () => {
mockMobile = false; localStorage.setItem("uiLanguage", "en");
render(<Shell />);
expect(screen.queryByRole("navigation", { name: "Primary navigation" })).not.toBeInTheDocument();
fireEvent.click(screen.getByRole("button", { name: "Collapse sidebar" }));
expect(localStorage.getItem("appShellDesktopSidebarCollapsed")).toBe("1");
fireEvent.click(screen.getByRole("button", { name: "Jobs" }));
expect(within(screen.getByRole("menu")).getByRole("menuitem", { name: "Companies" })).toBeVisible();
});
test("choosing the current destination from More closes the drawer and focuses its heading", async () => {
localStorage.setItem("uiLanguage", "en"); render(<Shell initial="/companies" />);
fireEvent.click(within(screen.getByRole("navigation", { name: "Primary navigation" })).getByRole("button", { name: "More" }));
fireEvent.click(within(screen.getByRole("dialog")).getByRole("button", { name: "Companies" }));
await waitFor(() => expect(screen.queryByRole("dialog")).not.toBeInTheDocument());
expect(screen.getByRole("heading", { level: 1, name: "/companies" })).toHaveFocus();
});