From cd7a396ac5d7edbfc4e7685ffeb00fca200f74cd Mon Sep 17 00:00:00 2001 From: "paseo-bot[bot]" <266920839+paseo-bot[bot]@users.noreply.github.com> Date: Thu, 15 Oct 2026 01:46:53 +0200 Subject: [PATCH] test(app): verify terminal profile artwork in the launch menu Exercise the rendered SVG, dimensions, theme color, catalog artwork and fallback through the real browser app instead of inspecting hook results in JSDOM. The browser regression fails on main before the lookup fix. --- .../browser/terminal-profile-icons.spec.ts | 71 +++++++++++++++++++ .../app/src/components/provider-icons.test.ts | 31 -------- 2 files changed, 71 insertions(+), 31 deletions(-) create mode 100644 packages/app/e2e/browser/terminal-profile-icons.spec.ts 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); -});