From 2df80503b4572cbd49d85709f30a95c1d7a4c9d2 Mon Sep 17 00:00:00 2001 From: Molecule AI Core-UIUX Date: Tue, 12 May 2026 00:16:51 +0000 Subject: [PATCH] test(canvas/settings): add ServiceGroup coverage (10 cases) - role=group with aria-label containing service label - Service icon aria-hidden, correct emoji per service name - Count label: "1 key" vs "N keys" - Renders SecretRow for each secret - Header and rows div structure Co-Authored-By: Claude Opus 4.7 --- .../settings/__tests__/ServiceGroup.test.tsx | 196 ++++++++++++++++++ 1 file changed, 196 insertions(+) create mode 100644 canvas/src/components/settings/__tests__/ServiceGroup.test.tsx diff --git a/canvas/src/components/settings/__tests__/ServiceGroup.test.tsx b/canvas/src/components/settings/__tests__/ServiceGroup.test.tsx new file mode 100644 index 00000000..11bb1bda --- /dev/null +++ b/canvas/src/components/settings/__tests__/ServiceGroup.test.tsx @@ -0,0 +1,196 @@ +// @vitest-environment jsdom +/** + * ServiceGroup — collapsible group of secret rows under a service header. + * + * Per spec §3.1: + * ── GitHub ────────────────────────── 1 key ── + * GITHUB_TOKEN + * ghp_••••••••••••••xK9f [👁] [✓] [⎘] [✏] [🗑] + * + * NOTE: No @testing-library/jest-dom import — use DOM APIs. + * + * Covers: + * - Renders group with role=group and aria-label + * - Service icon is aria-hidden + * - Label text matches service + * - Count: "1 key" for single, "N keys" for multiple + * - Renders SecretRow for each secret + * - Renders nothing when secrets array is empty (not called) + * - Different services show correct label and icon + */ +import { afterEach, describe, expect, it, vi } from "vitest"; +import { cleanup, render } from "@testing-library/react"; +import React from "react"; + +import { ServiceGroup } from "../ServiceGroup"; +import type { Secret, SecretGroup, ServiceConfig } from "@/types/secrets"; + +// ─── Mock SecretRow ──────────────────────────────────────────────────────────── + +vi.mock("../SecretRow", () => ({ + SecretRow: ({ secret, workspaceId }: { secret: Secret; workspaceId: string }) => ( +
+ SecretRow:{secret.name} +
+ ), +})); + +// ─── Helpers ─────────────────────────────────────────────────────────────────── + +function makeService(icon: string, label: string): ServiceConfig { + return { icon, label, docsUrl: "https://example.com/docs" }; +} + +function makeSecret(name: string): Secret { + return { + name, + value: "sk-test-••••••••••••", + group: "custom" as SecretGroup, + masked: true, + }; +} + +// ─── Tests ──────────────────────────────────────────────────────────────────── + +afterEach(() => { + cleanup(); + vi.restoreAllMocks(); + vi.resetModules(); +}); + +describe("ServiceGroup — render", () => { + it("renders group with role=group", () => { + const { container } = render( + , + ); + expect(container.querySelector('[role="group"]')).toBeTruthy(); + }); + + it("group aria-label contains service label", () => { + const { container } = render( + , + ); + const group = container.querySelector('[role="group"]'); + expect(group?.getAttribute("aria-label")).toContain("Anthropic"); + }); + + it("service icon is aria-hidden", () => { + const { container } = render( + , + ); + const icon = container.querySelector('[aria-hidden="true"]'); + expect(icon).toBeTruthy(); + expect(icon?.textContent).toContain("🔀"); + }); + + it("label text matches service label", () => { + const { container } = render( + , + ); + expect(container.textContent ?? "").toContain("GitHub"); + }); + + it('count label is "1 key" for single secret', () => { + const { container } = render( + , + ); + expect(container.textContent ?? "").toContain("1 key"); + }); + + it("count label is 'N keys' for multiple secrets", () => { + const { container } = render( + , + ); + expect(container.textContent ?? "").toContain("2 keys"); + }); + + it("renders SecretRow for each secret", () => { + const { container } = render( + , + ); + const rows = container.querySelectorAll('[data-testid="secret-row"]'); + expect(rows).toHaveLength(2); + expect(rows[0].getAttribute("data-name")).toBe("GITHUB_TOKEN"); + expect(rows[1].getAttribute("data-name")).toBe("GITHUB_ORG"); + }); + + it("renders header and rows divs", () => { + const { container } = render( + , + ); + expect(container.querySelector(".service-group__header")).toBeTruthy(); + expect(container.querySelector(".service-group__rows")).toBeTruthy(); + }); + + it("renders correct icon emoji for github", () => { + const { container } = render( + , + ); + const icon = container.querySelector(".service-group__icon"); + expect(icon?.textContent).toContain("🐙"); + }); + + it("renders default icon for unknown service name", () => { + const { container } = render( + , + ); + const icon = container.querySelector(".service-group__icon"); + expect(icon?.textContent).toContain("🔑"); + }); +});