diff --git a/packages/app/e2e/browser/terminal-profile-icons.spec.ts b/packages/app/e2e/browser/terminal-profile-icons.spec.ts new file mode 100644 index 0000000000..29d786cfd3 --- /dev/null +++ b/packages/app/e2e/browser/terminal-profile-icons.spec.ts @@ -0,0 +1,71 @@ +import { ACP_PROVIDER_ICON_SVGS } from "../../src/assets/acp-provider-icons"; +import { expect, test, type Page } from "../support/fixtures"; +import { gotoAppShell } from "../support/helpers/app"; +import { openNewWorkspaceComposer } from "../support/helpers/new-workspace"; +import { openLaunchMenu } from "../support/helpers/new-workspace-launch"; +import { seedWorkspace } from "../support/helpers/seed-client"; +import { waitForSidebarHydration } from "../support/helpers/workspace-ui"; + +// Harmless commands: this journey opens the profile menu without launching them. +test.use({ + e2eDaemonConfig: { + version: 1, + daemon: { + terminalProfiles: [ + { id: "antigravity", name: "Antigravity artwork", command: "/bin/sh", icon: "agy" }, + { id: "cursor", name: "Cursor artwork", command: "/bin/sh", icon: "cursor" }, + { id: "unknown", name: "Unknown artwork", command: "/bin/sh", icon: "unknown-icon" }, + ], + }, + }, +}); + +function profileIcon(page: Page, name: string) { + return page.getByRole("menuitem", { name: new RegExp(`^${name} artwork`) }).locator("svg"); +} + +async function expectBundledArtwork(page: Page, name: string, artwork: string) { + const icon = profileIcon(page, name); + await expect(icon).toBeVisible(); + const expectedPaths = await page.evaluate((xml) => { + const document = new DOMParser().parseFromString(xml, "image/svg+xml"); + return Array.from(document.querySelectorAll("path"), (path) => path.getAttribute("d")!); + }, artwork); + await expect(icon.locator("path")).toHaveCount(expectedPaths.length); + for (const [index, path] of expectedPaths.entries()) { + await expect(icon.locator("path").nth(index)).toHaveAttribute("d", path); + } + await expect(icon).toHaveAttribute("width", "14"); + await expect(icon).toHaveAttribute("height", "14"); + const terminalColor = await page + .getByRole("menuitem", { name: "Terminal", exact: true }) + .locator("svg") + .evaluate((element) => getComputedStyle(element).stroke); + await expect(icon.locator("path").first()).toHaveCSS("fill", terminalColor); +} + +async function expectUnknownArtworkFallback(page: Page) { + const icon = profileIcon(page, "Unknown"); + await expect(icon).toBeVisible(); + await expect(icon.locator('path[d="M12 8V4H8"]')).toHaveCount(1); +} + +test("terminal profiles show bundled artwork while unknown icons keep the Bot fallback", async ({ + page, +}) => { + const workspace = await seedWorkspace({ repoPrefix: "terminal-icons-" }); + try { + await gotoAppShell(page); + await waitForSidebarHydration(page); + await openNewWorkspaceComposer(page, { + projectKey: workspace.projectKey, + projectDisplayName: workspace.projectDisplayName, + }); + await openLaunchMenu(page); + await expectBundledArtwork(page, "Antigravity", ACP_PROVIDER_ICON_SVGS.agy); + await expectBundledArtwork(page, "Cursor", ACP_PROVIDER_ICON_SVGS.cursor); + await expectUnknownArtworkFallback(page); + } finally { + await workspace.cleanup(); + } +}); diff --git a/packages/app/src/components/provider-icons.test.ts b/packages/app/src/components/provider-icons.test.ts index 6b9314c99f..025ff14bef 100644 --- a/packages/app/src/components/provider-icons.test.ts +++ b/packages/app/src/components/provider-icons.test.ts @@ -1,7 +1,5 @@ // @vitest-environment jsdom import { act, cleanup, renderHook } from "@testing-library/react"; -import { TERMINAL_PROFILE_ICON_NAMES } from "@getpaseo/protocol/provider-icon-names"; -import { ACP_PROVIDER_ICON_SVGS } from "@/assets/acp-provider-icons"; import { Bot } from "lucide-react-native"; import { SvgXml } from "react-native-svg"; import { afterEach, describe, expect, it } from "vitest"; @@ -86,32 +84,3 @@ it("updates collection consumers when their host snapshot arrives", () => { act(() => replaceProviderSnapshotIcons(serverId, [{ provider, iconSvg: svg }])); expect(renderIcon(result.current(provider))).toMatchObject({ props: { xml: svg } }); }); - -it("renders the shipped Antigravity SVG for a terminal profile icon", () => { - const { result } = renderHook(() => useProviderIcon("agy")); - expect(result.current).not.toBe(Bot); - expect(renderIcon(result.current)).toMatchObject({ - type: SvgXml, - props: { xml: ACP_PROVIDER_ICON_SVGS.agy, width: 18, height: 18, color: "#123456" }, - }); -}); - -it.each(TERMINAL_PROFILE_ICON_NAMES)( - "renders terminal icon %s through collection consumers", - (name) => { - const { result } = renderHook(() => useProviderIcons()); - expect(renderIcon(result.current(name))).toMatchObject({ - type: SvgXml, - props: { xml: ACP_PROVIDER_ICON_SVGS[name as keyof typeof ACP_PROVIDER_ICON_SVGS] }, - }); - }, -); - -it("preserves catalog icons and the unknown icon fallback", () => { - const catalog = renderHook(() => useProviderIcon("cursor")); - expect(renderIcon(catalog.result.current)).toMatchObject({ - type: SvgXml, - props: { xml: ACP_PROVIDER_ICON_SVGS.cursor }, - }); - expect(renderHook(() => useProviderIcon("definitely-not-a-real-icon")).result.current).toBe(Bot); -});