import { act } from "react-dom/test-utils"; import { createRoot, type Root } from "react-dom/client"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { ClientFederationPage } from "./ClientFederationPage"; let params: { id?: string } = { id: "client-a" }; const listIdpConfigsMock = vi.fn(); const createIdpConfigMock = vi.fn(); vi.mock("react-router-dom", async () => { const actual = await vi.importActual( "react-router-dom", ); return { ...actual, useParams: () => params, }; }); vi.mock("../../../lib/devApi", () => ({ listIdpConfigsForClient: (clientId: string) => listIdpConfigsMock(clientId), createIdpConfigForClient: (payload: unknown) => createIdpConfigMock(payload), })); vi.mock("../../../lib/i18n", () => ({ t: (key: string, fallback?: string, vars?: Record) => { let text = fallback ?? key; for (const [name, value] of Object.entries(vars ?? {})) { text = text.replaceAll(`{{${name}}}`, String(value)); } return text; }, })); const roots: Root[] = []; afterEach(() => { for (const root of roots.splice(0)) { act(() => { root.unmount(); }); } vi.clearAllMocks(); document.body.innerHTML = ""; }); beforeEach(() => { params = { id: "client-a" }; listIdpConfigsMock.mockResolvedValue([ { id: "idp-1", client_id: "client-a", provider_type: "oidc", display_name: "Workspace OIDC", status: "active", issuer_url: "https://accounts.example", oidc_client_id: "oidc-client", scopes: "openid email profile", createdAt: "2026-05-01T00:00:00Z", updatedAt: "2026-05-01T00:00:00Z", }, ]); createIdpConfigMock.mockResolvedValue({ id: "idp-2", client_id: "client-a", provider_type: "oidc", display_name: "New Provider", status: "active", createdAt: "2026-05-02T00:00:00Z", updatedAt: "2026-05-02T00:00:00Z", }); }); async function setInputValue(input: HTMLInputElement, value: string) { const descriptor = Object.getOwnPropertyDescriptor( HTMLInputElement.prototype, "value", ); descriptor?.set?.call(input, value); input.dispatchEvent(new Event("input", { bubbles: true })); await new Promise((resolve) => setTimeout(resolve, 0)); } async function renderPage() { const container = document.createElement("div"); document.body.appendChild(container); const root = createRoot(container); roots.push(root); const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false }, mutations: { retry: false }, }, }); await act(async () => { root.render( , ); }); await act(async () => { await new Promise((resolve) => setTimeout(resolve, 0)); }); return container; } describe("ClientFederationPage", () => { it("shows a missing client id message when no route param exists", async () => { params = {}; const container = await renderPage(); expect(container.textContent).toContain("Client ID is missing"); }); it("opens the create modal and submits a new IdP config", async () => { const container = await renderPage(); expect(container.textContent).toContain("Workspace OIDC"); const addButton = Array.from(container.querySelectorAll("button")).find( (button) => button.textContent === "Add Provider", ); expect(addButton).toBeTruthy(); await act(async () => { addButton?.dispatchEvent(new MouseEvent("click", { bubbles: true })); }); expect(container.textContent).toContain("Add Identity Provider"); const displayName = container.querySelector( 'input[name="display_name"]', ) as HTMLInputElement | null; const issuerUrl = container.querySelector( 'input[name="issuer_url"]', ) as HTMLInputElement | null; const clientId = container.querySelector( 'input[name="oidc_client_id"]', ) as HTMLInputElement | null; const clientSecret = container.querySelector( 'input[name="oidc_client_secret"]', ) as HTMLInputElement | null; if (!displayName || !issuerUrl || !clientId || !clientSecret) { throw new Error("Expected federation form inputs to be rendered"); } await act(async () => { await setInputValue(displayName, "New Provider"); await setInputValue(issuerUrl, "https://login.example"); await setInputValue(clientId, "client-oidc"); await setInputValue(clientSecret, "secret-value"); }); const submitButton = Array.from(container.querySelectorAll("button")).find( (button) => button.textContent === "Save Configuration", ); expect(submitButton).toBeTruthy(); await act(async () => { submitButton?.dispatchEvent(new MouseEvent("click", { bubbles: true })); await new Promise((resolve) => setTimeout(resolve, 0)); }); expect(createIdpConfigMock).toHaveBeenCalledWith( expect.objectContaining({ client_id: "client-a", display_name: "New Provider", issuer_url: "https://login.example", oidc_client_id: "client-oidc", oidc_client_secret: "secret-value", }), ); }); });