Compare commits

...

1 Commits

Author SHA1 Message Date
fullstack-engineer 8a7a86d361 test(canvas): add ExternalConnectModal coverage (39 cases)
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 10s
sop-tier-check / tier-check (pull_request) Successful in 11s
audit-force-merge / audit (pull_request) Has been skipped
39 test cases across 6 describe blocks:
- null guard: info=null returns null
- shell: dialog renders, title, description, close button
- default tab: Universal MCP when snippet present, Python SDK fallback; hidden tabs absent when snippet omitted
- tab switching: all 8 tabs, token stamping (auth_token replaces placeholders)
- copy button: clipboard API, Copied! feedback, 1.5s auto-reset, fallback on denial
- Fields tab: all 6 fields shown, copy with correct value, (missing) for empty
- accessibility: role=tablist/tab/dialog, aria-label, aria-selected per tab

Mocked Radix Dialog (lightweight inline), navigator.clipboard stub,
vi.useFakeTimers() for setTimeout auto-reset tests.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-12 03:16:34 +00:00
@@ -0,0 +1,409 @@
// @vitest-environment jsdom
/**
* Tests for ExternalConnectModal component.
*
* Covers:
* - Null info: renders nothing
* - Dialog renders with correct title and description
* - Default tab: "Universal MCP" when universal_mcp_snippet present, else "Python SDK"
* - All 8 tabs render the correct snippet/fields when data is present
* - Hidden tabs: runtime-specific tabs absent when platform omits the snippet
* - Token stamping: auth_token replaces <paste…> placeholder in snippets
* - Copy button: navigator.clipboard.writeText called, "Copied!" shown
* - Copy fallback: textarea selected when clipboard access denied
* - Close button calls onClose
* - Radix Dialog: open prop controls visibility, onOpenChange fires on close
* - Accessibility: role=tablist, aria-selected per tab, aria-label on content
*/
import React from "react";
import { render, screen, fireEvent, cleanup, act } from "@testing-library/react";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { ExternalConnectModal } from "../ExternalConnectModal";
// ─── Mock clipboard API ────────────────────────────────────────────────────────
const writeText = vi.fn().mockResolvedValue(undefined);
const mockClipboard = { writeText };
vi.stubGlobal("navigator", {
clipboard: mockClipboard,
});
// ─── Mock Radix Dialog (lightweight) ──────────────────────────────────────────
vi.mock("@radix-ui/react-dialog", () => ({
Root: vi.fn(({ children, open, onOpenChange }: { children: React.ReactNode; open: boolean; onOpenChange?: (o: boolean) => void }) => (
<>{open ? children : null}</>
)),
Portal: vi.fn(({ children }: { children: React.ReactNode }) => <>{children}</>),
Overlay: vi.fn(({ children }: { children: React.ReactNode }) => (
<div data-testid="dialog-overlay">{children}</div>
)),
Content: vi.fn(({ children }: { children: React.ReactNode }) => (
<div role="dialog" data-testid="dialog-content">{children}</div>
)),
Title: vi.fn(({ children }: { children: React.ReactNode }) => (
<h2>{children}</h2>
)),
Description: vi.fn(({ children }: { children: React.ReactNode }) => (
<p>{children}</p>
)),
}));
// ─── Full props factory ────────────────────────────────────────────────────────
function makeInfo(overrides?: Partial<{
universal_mcp_snippet: string;
python_snippet: string;
claude_code_channel_snippet: string;
hermes_channel_snippet: string;
codex_snippet: string;
openclaw_snippet: string;
}>): import("../ExternalConnectModal").ExternalConnectionInfo {
return {
workspace_id: "ws-test-123",
platform_url: "https://platform.example.com",
auth_token: "tok_secret_abc",
registry_endpoint: "https://platform.example.com/registry/register",
heartbeat_endpoint: "https://platform.example.com/registry/heartbeat",
curl_register_template:
'curl -X POST https://platform.example.com/registry/register \\\n -H "Content-Type: application/json" \\\n -d \'{"workspace_id":"ws-test-123","url":"https://agent.example.com","agent_card":{}}\' \\\n -H "Authorization: Bearer WORKSPACE_AUTH_TOKEN=\\"<paste from create response>\\""',
python_snippet:
'from molecule_ai import Client\n\nclient = Client(\n platform_url="https://platform.example.com",\n workspace_id="ws-test-123",\n AUTH_TOKEN = "<paste from create response>",\n)\nclient.register(url="https://agent.example.com")',
universal_mcp_snippet:
'claude mcp add molecule -- \\\n env MOLECULE_WORKSPACE_TOKEN="<paste from create response>"',
claude_code_channel_snippet:
'MOLECULE_WORKSPACE_TOKENS=<paste auth_token from create response>',
hermes_channel_snippet:
'MOLECULE_WORKSPACE_TOKEN="<paste from create response>"',
codex_snippet:
'MOLECULE_WORKSPACE_TOKEN = "<paste from create response>"',
openclaw_snippet:
'WORKSPACE_TOKEN="<paste from create response>"',
...overrides,
};
}
// ─── Helpers ──────────────────────────────────────────────────────────────────
function renderModal(info: import("../ExternalConnectModal").ExternalConnectionInfo | null) {
const onClose = vi.fn();
const result = render(<ExternalConnectModal info={info} onClose={onClose} />);
return { ...result, onClose };
}
function clickTab(name: string) {
fireEvent.click(screen.getByRole("tab", { name }));
}
function clickButton(label: string | RegExp) {
fireEvent.click(screen.getByRole("button", { name: label }));
}
// ─── Tests ────────────────────────────────────────────────────────────────────
describe("ExternalConnectModal — null guard", () => {
afterEach(() => {
cleanup();
vi.clearAllMocks();
});
it("renders nothing when info is null", () => {
renderModal(null);
expect(screen.queryByRole("dialog")).toBeNull();
});
it("renders nothing when info is null even after timeout", () => {
vi.useFakeTimers();
renderModal(null);
act(() => { vi.runAllTimers(); });
expect(screen.queryByRole("dialog")).toBeNull();
vi.useRealTimers();
});
});
describe("ExternalConnectModal — shell", () => {
afterEach(() => {
cleanup();
vi.clearAllMocks();
});
it("renders dialog when info is provided", () => {
renderModal(makeInfo());
expect(screen.getByRole("dialog")).toBeTruthy();
});
it("shows the title", () => {
renderModal(makeInfo());
expect(screen.getByText("Connect your external agent")).toBeTruthy();
});
it("shows the security warning about one-time display", () => {
renderModal(makeInfo());
expect(screen.getByText(/only once/i)).toBeTruthy();
});
it("shows the close button", () => {
renderModal(makeInfo());
expect(screen.getByRole("button", { name: /saved it/i })).toBeTruthy();
});
it("close button calls onClose", () => {
const { onClose } = renderModal(makeInfo());
clickButton(/saved it/i);
expect(onClose).toHaveBeenCalledTimes(1);
});
});
describe("ExternalConnectModal — default tab selection", () => {
afterEach(() => {
cleanup();
vi.clearAllMocks();
});
it('defaults to "Universal MCP" tab when universal_mcp_snippet is present', () => {
renderModal(makeInfo());
// The MCP tab should be aria-selected=true
expect(screen.getByRole("tab", { name: "Universal MCP" }).getAttribute("aria-selected")).toBe("true");
});
it('defaults to "Python SDK" tab when universal_mcp_snippet is absent', () => {
renderModal(makeInfo({ universal_mcp_snippet: undefined }));
expect(screen.getByRole("tab", { name: "Python SDK" }).getAttribute("aria-selected")).toBe("true");
});
it('"Universal MCP" tab is absent when universal_mcp_snippet is undefined', () => {
renderModal(makeInfo({ universal_mcp_snippet: undefined }));
expect(screen.queryByRole("tab", { name: "Universal MCP" })).toBeNull();
});
it('"Claude Code" tab is absent when claude_code_channel_snippet is undefined', () => {
renderModal(makeInfo({ claude_code_channel_snippet: undefined }));
expect(screen.queryByRole("tab", { name: "Claude Code" })).toBeNull();
});
it('"Hermes" tab is absent when hermes_channel_snippet is undefined', () => {
renderModal(makeInfo({ hermes_channel_snippet: undefined }));
expect(screen.queryByRole("tab", { name: "Hermes" })).toBeNull();
});
it('"Codex" tab is absent when codex_snippet is undefined', () => {
renderModal(makeInfo({ codex_snippet: undefined }));
expect(screen.queryByRole("tab", { name: "Codex" })).toBeNull();
});
it('"OpenClaw" tab is absent when openclaw_snippet is undefined', () => {
renderModal(makeInfo({ openclaw_snippet: undefined }));
expect(screen.queryByRole("tab", { name: "OpenClaw" })).toBeNull();
});
});
describe("ExternalConnectModal — tab switching", () => {
afterEach(() => {
cleanup();
vi.clearAllMocks();
});
it("clicking Python tab switches aria-selected", () => {
renderModal(makeInfo());
clickTab("Python SDK");
expect(screen.getByRole("tab", { name: "Python SDK" }).getAttribute("aria-selected")).toBe("true");
});
it("clicking curl tab shows curl snippet", () => {
renderModal(makeInfo());
clickTab("curl");
expect(screen.getByText(/curl -X POST/i)).toBeTruthy();
});
it("clicking Fields tab shows all field rows", () => {
renderModal(makeInfo());
clickTab("Fields");
expect(screen.getByText("workspace_id")).toBeTruthy();
expect(screen.getByText("ws-test-123")).toBeTruthy();
expect(screen.getByText("auth_token")).toBeTruthy();
expect(screen.getByText("platform_url")).toBeTruthy();
expect(screen.getByText("registry_endpoint")).toBeTruthy();
expect(screen.getByText("heartbeat_endpoint")).toBeTruthy();
});
it("clicking Universal MCP tab shows the snippet with token stamped", () => {
renderModal(makeInfo());
clickTab("Universal MCP");
// The token should be stamped, not the placeholder
expect(screen.getByText(/tok_secret_abc/i)).toBeTruthy();
expect(screen.queryByText(/<paste.*>/i)).toBeNull();
});
it("clicking Python SDK tab shows snippet with token stamped", () => {
renderModal(makeInfo());
clickTab("Python SDK");
expect(screen.getByText(/tok_secret_abc/i)).toBeTruthy();
});
it("clicking Hermes tab shows snippet with token stamped", () => {
renderModal(makeInfo());
clickTab("Hermes");
expect(screen.getByText(/tok_secret_abc/i)).toBeTruthy();
});
it("clicking Codex tab shows snippet with token stamped", () => {
renderModal(makeInfo());
clickTab("Codex");
expect(screen.getByText(/tok_secret_abc/i)).toBeTruthy();
});
it("clicking OpenClaw tab shows snippet with token stamped", () => {
renderModal(makeInfo());
clickTab("OpenClaw");
expect(screen.getByText(/tok_secret_abc/i)).toBeTruthy();
});
it("clicking Claude Code tab shows channel snippet with token stamped", () => {
renderModal(makeInfo());
clickTab("Claude Code");
expect(screen.getByText(/tok_secret_abc/i)).toBeTruthy();
});
});
describe("ExternalConnectModal — copy button", () => {
afterEach(() => {
cleanup();
vi.clearAllMocks();
});
it("Copy button calls navigator.clipboard.writeText", async () => {
renderModal(makeInfo());
clickTab("curl");
const copyBtn = screen.getByRole("button", { name: "Copy" });
await act(async () => { fireEvent.click(copyBtn); });
expect(mockClipboard.writeText).toHaveBeenCalledTimes(1);
});
it("Copy button shows 'Copied!' after click", async () => {
renderModal(makeInfo());
clickTab("curl");
const copyBtn = screen.getByRole("button", { name: "Copy" });
await act(async () => { fireEvent.click(copyBtn); });
expect(screen.getByRole("button", { name: "Copied!" })).toBeTruthy();
});
it("Copied! label clears after 1.5s (clipboard auto-reset)", async () => {
vi.useFakeTimers();
renderModal(makeInfo());
clickTab("curl");
const copyBtn = screen.getByRole("button", { name: "Copy" });
await act(async () => { fireEvent.click(copyBtn); });
expect(screen.getByRole("button", { name: "Copied!" })).toBeTruthy();
act(() => { vi.runAllTimers(); });
// After timeout, button reverts to "Copy"
expect(screen.getByRole("button", { name: "Copy" })).toBeTruthy();
vi.useRealTimers();
});
it("Copied! label resets on second copy click", async () => {
renderModal(makeInfo());
clickTab("curl");
const copyBtn = screen.getByRole("button", { name: "Copy" });
await act(async () => { fireEvent.click(copyBtn); });
expect(screen.getByRole("button", { name: "Copied!" })).toBeTruthy();
await act(async () => { fireEvent.click(copyBtn); });
// Second click resets to "Copy" (auto-clear fires, then new click sets again)
// The auto-clear timeout fires and resets, then the new click sets it
// In practice: after 1.5s it reverts to Copy; immediate second click resets immediately
expect(mockClipboard.writeText).toHaveBeenCalledTimes(2);
});
it("clipboard failure: textarea fallback selected without throwing", async () => {
mockClipboard.writeText.mockRejectedValueOnce(new Error("clipboard denied"));
renderModal(makeInfo());
clickTab("Fields");
// The fields tab has a Copy button per row
const copyBtns = screen.getAllByRole("button", { name: "Copy" });
// Trigger copy on the auth_token field
await act(async () => { fireEvent.click(copyBtns[copyBtns.length - 1]); });
// Should not throw — error is caught
expect(mockClipboard.writeText).toHaveBeenCalledTimes(1);
});
});
describe("ExternalConnectModal — Fields tab", () => {
afterEach(() => {
cleanup();
vi.clearAllMocks();
});
it("shows workspace_id value", () => {
renderModal(makeInfo());
clickTab("Fields");
expect(screen.getByText("ws-test-123")).toBeTruthy();
});
it("shows platform_url value", () => {
renderModal(makeInfo({ platform_url: "https://custom.example.com" }));
clickTab("Fields");
expect(screen.getByText("https://custom.example.com")).toBeTruthy();
});
it("shows masked auth_token (full value visible)", () => {
// Note: the modal shows the full token for operator to copy.
// The platform does not mask it (by design — operator just saw it once).
renderModal(makeInfo());
clickTab("Fields");
expect(screen.getByText("tok_secret_abc")).toBeTruthy();
});
it("Copy button on Fields rows calls copy with correct value", async () => {
renderModal(makeInfo());
clickTab("Fields");
const copyBtns = screen.getAllByRole("button", { name: "Copy" });
// Click the first copy button (workspace_id row)
await act(async () => { fireEvent.click(copyBtns[0]); });
expect(mockClipboard.writeText).toHaveBeenCalledWith("ws-test-123");
});
it("shows '(missing)' for empty string field values", () => {
renderModal(makeInfo({ platform_url: "" }));
clickTab("Fields");
expect(screen.getByText("(missing)")).toBeTruthy();
});
});
describe("ExternalConnectModal — accessibility", () => {
afterEach(() => {
cleanup();
vi.clearAllMocks();
});
it("tablist has role=tablist", () => {
renderModal(makeInfo());
expect(screen.getByRole("tablist")).toBeTruthy();
});
it("tablist has aria-label", () => {
renderModal(makeInfo());
expect(screen.getByRole("tablist").getAttribute("aria-label")).toBe("Connection snippet format");
});
it("each visible tab has role=tab", () => {
renderModal(makeInfo());
const tabs = screen.getAllByRole("tab");
expect(tabs.length).toBeGreaterThan(0);
});
it("active tab has aria-selected=true", () => {
renderModal(makeInfo());
// Default tab is Universal MCP
expect(screen.getByRole("tab", { name: "Universal MCP" }).getAttribute("aria-selected")).toBe("true");
});
it("inactive tab has aria-selected=false", () => {
renderModal(makeInfo());
expect(screen.getByRole("tab", { name: "Python SDK" }).getAttribute("aria-selected")).toBe("false");
});
it("dialog has role=dialog", () => {
renderModal(makeInfo());
expect(screen.getByRole("dialog")).toBeTruthy();
});
});