/** * The app picker on the Administrator card. * * The console is workspace-wide, so this picker is not a scope: it decides * which app's records the console opens showing. What has to hold: it offers * exactly the apps sitting beside it on the home, the choice travels to the * console as `?app=`, a workspace with one app is not asked, and the console's * two lists agree about which record types that app reads. */ import { render, screen } from "@testing-library/react" import userEvent from "@testing-library/user-event" import { MemoryRouter } from "react-router" import { beforeEach, describe, expect, it, vi } from "vitest" import { TooltipProvider } from "@/components/ui/tooltip" import { DEFAULT_LOGIN_SESSION, setLoginSession } from "@/lib/login-session" import { adminImportsForObjects, adminObjectsForApp, adminObjectSummaries, } from "@/lib/mock/admin-directory" import { adminConsoleHref, buildProductHomeInventory, type ProductHomeCard, } from "@/lib/product-home" import { OwnedProductTile } from "./product-home-parts" vi.mock("react-router", async () => { const actual = await vi.importActual("react-router") return { ...actual, useNavigate: () => vi.fn() } }) // The real provider boots a tenant catalogue over `fetch` on mount, and which // product the shell has open is not what decides any of this. vi.mock("@/contexts/product-context", () => ({ useProduct: () => ({ product: "exxat-prism" }), })) const onOpen = vi.fn() function adminCard(): ProductHomeCard { const card = buildProductHomeInventory([], []).owned.find( entry => entry.slug === "admin", ) if (!card) throw new Error("this session has no Administrator card") return card } function renderTile(card: ProductHomeCard) { return render(
, ) } beforeEach(() => { localStorage.clear() onOpen.mockClear() setLoginSession(DEFAULT_LOGIN_SESSION) }) describe("the Administrator card", () => { it("offers the apps this workspace owns, and nothing else", () => { const card = adminCard() const owned = buildProductHomeInventory([], []).owned.filter( entry => entry.slug !== "admin", ) expect(card.adminApps?.map(app => app.slug)).toEqual(owned.map(entry => entry.slug)) }) it("opens on every app until one is chosen", async () => { renderTile(adminCard()) expect(screen.getByRole("button", { name: /all apps/i })).toBeInTheDocument() await userEvent.click(screen.getByRole("button", { name: "Open" })) expect(onOpen).toHaveBeenCalledWith(expect.objectContaining({ href: "/admin" })) }) it("sends the chosen app to the console", async () => { const card = adminCard() const app = card.adminApps?.[0] if (!app) throw new Error("the card offered no apps") renderTile(card) await userEvent.click(screen.getByRole("button", { name: /all apps/i })) await userEvent.click(screen.getByRole("menuitem", { name: app.label })) await userEvent.click(screen.getByRole("button", { name: "Open" })) expect(onOpen).toHaveBeenCalledWith( expect.objectContaining({ href: adminConsoleHref(app.slug) }), ) }) it("does not ask a workspace that has one app", () => { const card = adminCard() renderTile({ ...card, adminApps: card.adminApps?.slice(0, 1) }) expect(screen.queryByRole("button", { name: /all apps/i })).not.toBeInTheDocument() expect(screen.getByText("Whole workspace")).toBeInTheDocument() }) }) describe("the console pointed at one app", () => { it("shows only the records that app reads", () => { const everything = adminObjectSummaries().map(object => object.id) const oneSchools = adminObjectsForApp("exxat-one-schools").map(object => object.id) expect(oneSchools).not.toEqual(everything) expect(oneSchools).toContain("people") expect(oneSchools).not.toContain("courses") }) it("gives a custom product Prism's records, whose IA it inherits", () => { expect(adminObjectsForApp("exxat-custom")).toEqual(adminObjectsForApp("exxat-prism")) }) it("lists only landings in the records on screen", () => { const objects = adminObjectsForApp("exxat-one-schools") const landed = adminImportsForObjects(objects).map(event => event.object) expect(landed.length).toBeGreaterThan(0) expect(landed).not.toContain("Course Management") }) })