Files
PaperClipAI/ui/src/pages/apps/chat/GitHubSetupPrompt.test.tsx
T
DottaandPaperclip 527e146980 feat(ui): share animated agent setup prompts (#14862)
Use the shared animated prompt-copy control across setup, invitations, webhooks, and task handoffs. Preserve first-click copying, clipboard recovery, and logo continuity. Add Storybook coverage and restore mention icon masks for the current Lucide data shape.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-01 11:46:14 -05:00

83 lines
3.9 KiB
TypeScript

// @vitest-environment jsdom
import { act } from "react";
import { createRoot, type Root } from "react-dom/client";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { copyTextToClipboard } from "@/lib/clipboard";
import { GitHubSetupPrompt, buildGitHubSetupPrompt, githubSetupPrompt } from "./GitHubSetupPrompt";
vi.mock("@/lib/clipboard", () => ({ copyTextToClipboard: vi.fn() }));
describe("GitHub setup prompt", () => {
let container: HTMLDivElement;
let root: Root;
beforeEach(async () => {
vi.clearAllMocks();
container = document.createElement("div");
document.body.append(container);
root = createRoot(container);
await act(async () => root.render(<GitHubSetupPrompt />));
});
afterEach(async () => {
await act(async () => root.unmount());
container.remove();
});
async function openPrompt() {
await act(async () => container.querySelector("button")!.click());
return document.querySelector('[role="dialog"]')!;
}
async function copyPrompt(dialog: Element) {
await act(async () => dialog.querySelector<HTMLButtonElement>(".agent-setup-copy")!.click());
}
it("copies on the opening click and confirms success in both buttons", async () => {
vi.mocked(copyTextToClipboard).mockResolvedValue(undefined);
const dialog = await openPrompt();
expect(copyTextToClipboard).toHaveBeenCalledTimes(1);
expect(copyTextToClipboard).toHaveBeenCalledWith(buildGitHubSetupPrompt(window.location.origin));
expect(vi.mocked(copyTextToClipboard).mock.calls[0][0]).toContain(`Paperclip instance URL: ${window.location.origin}`);
expect(dialog.querySelector(".agent-setup-copy")?.textContent).toBe("Copied to clipboard");
expect(container.querySelector('[role="status"]')?.textContent).toContain("Ready to paste into your agent.");
expect(container.querySelector("button")?.dataset.copied).toBe("true");
expect(dialog.querySelector("textarea")).toBeNull();
});
it("offers selectable instructions when clipboard access fails and allows retry", async () => {
vi.mocked(copyTextToClipboard).mockRejectedValueOnce(new Error("Clipboard unavailable"));
const dialog = await openPrompt();
const fallback = dialog.querySelector("textarea")!;
expect(fallback.value).toBe(buildGitHubSetupPrompt(window.location.origin));
expect(fallback.readOnly).toBe(true);
fallback.focus();
expect(fallback.selectionStart).toBe(0);
expect(fallback.selectionEnd).toBe(fallback.value.length);
expect(dialog.querySelector('[role="alert"]')?.textContent).toContain("Could not copy");
vi.mocked(copyTextToClipboard).mockResolvedValueOnce(undefined);
await copyPrompt(dialog);
expect(dialog.querySelector('[role="alert"]')).toBeNull();
expect(dialog.querySelector("textarea")).toBeNull();
});
it("uses the preview's configured instance instead of its Storybook host", async () => {
await act(async () => root.render(<GitHubSetupPrompt instanceUrl="https://my-company.paperclip.app" />));
vi.mocked(copyTextToClipboard).mockResolvedValue(undefined);
const dialog = await openPrompt();
expect(copyTextToClipboard).toHaveBeenCalledTimes(1);
expect(copyTextToClipboard).toHaveBeenCalledWith(buildGitHubSetupPrompt("https://my-company.paperclip.app"));
});
it("includes only the instance origin, without URL credentials or callback state", () => {
const prompt = buildGitHubSetupPrompt("https://user:private-password@my-company.paperclip.app/GIT/apps/chat/connect?code=private-code#private-state");
expect(prompt).toContain("Paperclip instance URL: https://my-company.paperclip.app\n");
expect(prompt).not.toMatch(/private-password|private-code|private-state/);
expect(prompt).toContain(githubSetupPrompt);
});
it.each(["", "not a URL", "javascript:alert(1)"])("asks for the URL when preview configuration is unavailable: %s", (url) => {
expect(buildGitHubSetupPrompt(url)).toMatch(/^Paperclip instance URL is unavailable\./);
});
});