// @vitest-environment happy-dom import React, { act } from "react"; import { createRoot, type Root } from "react-dom/client"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { TooltipProvider } from "./ui/tooltip"; import { WorkspaceTemplateCard, WorkspaceTemplatesSection, type CuratedWorkspaceTemplate, } from "./workspace-template-card"; const clientState = vi.hoisted(() => ({ mutation: vi.fn(), options: null as Record | null, isPending: false, })); const toast = vi.hoisted(() => ({ error: vi.fn(), success: vi.fn(), })); vi.mock("@agent-native/core/client/hooks", () => ({ useActionMutation: (_name: string, options: Record) => { clientState.options = options; return { mutate: clientState.mutation, isPending: clientState.isPending, }; }, })); vi.mock("sonner", () => ({ toast })); const template: CuratedWorkspaceTemplate = { id: "weekly-report", name: "Weekly report", description: "Turn workspace activity into a concise weekly report.", source: "RevOps", integrationSetup: "Connect your CRM after installing the app.", liveUrl: "https://reports.example.test", }; describe("WorkspaceTemplateCard", () => { let container: HTMLDivElement; let root: Root; beforeEach(() => { vi.stubGlobal("IS_REACT_ACT_ENVIRONMENT", true); clientState.mutation.mockReset(); clientState.options = null; clientState.isPending = false; toast.error.mockReset(); toast.success.mockReset(); container = document.createElement("div"); document.body.appendChild(container); root = createRoot(container); }); afterEach(() => { act(() => root.unmount()); container.remove(); document.body .querySelectorAll("[data-radix-portal]") .forEach((portal) => portal.remove()); vi.unstubAllGlobals(); }); it("keeps template cards concise and moves setup context into the add flow", async () => { await act(async () => { root.render( , ); }); expect(container.textContent).toContain("Weekly report"); expect(container.textContent).not.toContain("RevOps"); expect(container.textContent).not.toContain( "Connect your CRM after installing the app.", ); const trigger = Array.from(container.querySelectorAll("button")).find( (button) => button.textContent?.includes("Add app"), ); expect(trigger).not.toBeUndefined(); await act(async () => trigger?.click()); expect(document.body.textContent).toContain( "Connect your CRM after installing the app.", ); const liveLink = container.querySelector( 'a[href="https://reports.example.test"]', ); expect(liveLink?.textContent).toContain("View the live app"); expect(liveLink?.target).toBe("_blank"); await act(async () => { root.render( , ); }); expect( container.querySelector('a[href="https://reports.example.test"]'), ).toBe(null); }); it("creates an app with the default app id and allows an override", async () => { await act(async () => { root.render( , ); }); const trigger = Array.from(container.querySelectorAll("button")).find( (button) => button.textContent?.includes("Add app"), ); expect(trigger).not.toBeUndefined(); await act(async () => { trigger?.click(); }); const input = document.body.querySelector( "#workspace-template-app-id-weekly-report", ); expect(input?.value).toBe("pipeline"); await act(async () => { if (!input) throw new Error("Expected app id input"); Object.getOwnPropertyDescriptor( HTMLInputElement.prototype, "value", )?.set?.call(input, "Sales Ops"); input.dispatchEvent(new Event("input", { bubbles: true })); }); await act(async () => { document.body.querySelector("form")?.requestSubmit(); }); expect(clientState.mutation).toHaveBeenCalledWith({ templateId: "weekly-report", appId: "sales-ops", }); await act(async () => { await (clientState.options?.onSuccess as (result: unknown) => unknown)({ appId: "sales-ops", }); }); expect(toast.success).toHaveBeenCalledWith( "Template app creation started.", ); }); it("keeps add app available in catalog cards", async () => { await act(async () => { root.render( , ); }); const settingsButton = container.querySelector( 'button[aria-label="Settings for Weekly report"]', ); expect(settingsButton).not.toBeNull(); await act(async () => { settingsButton?.dispatchEvent( new MouseEvent("pointerdown", { bubbles: true, button: 0 }), ); }); const addItem = Array.from( document.querySelectorAll('[role="menuitem"]'), ).find((item) => item.textContent?.includes("Add app")); expect(addItem).not.toBeUndefined(); const settingsMenu = document.querySelector('[role="menu"]'); expect(settingsMenu?.className).toContain("w-48"); expect(settingsMenu?.className).toContain("bg-popover"); expect(settingsMenu?.className).toContain("shadow-md"); expect(addItem?.querySelector("svg")).not.toBeNull(); await act(async () => addItem?.click()); expect(document.body.textContent).toContain("Choose the URL-safe id"); expect( container.querySelector('a[href="https://reports.example.test"]'), ).not.toBeNull(); }); it("accepts the list action envelope and renders installed state", async () => { await act(async () => { root.render( , ); }); expect(container.textContent).toContain("Curated templates"); expect(container.textContent).toContain("Installed"); expect(container.querySelector('a[href^="https://"]')).toBeNull(); const addButton = Array.from(container.querySelectorAll("button")).find( (button) => button.textContent?.includes("Add app"), ); expect(addButton).not.toHaveProperty("disabled", true); }); });