diff --git a/open-sse/services/__tests__/claudeTlsClient.test.ts b/open-sse/services/__tests__/claudeTlsClient.test.ts index 7eb2479b1a..f4bd3b5f3f 100644 --- a/open-sse/services/__tests__/claudeTlsClient.test.ts +++ b/open-sse/services/__tests__/claudeTlsClient.test.ts @@ -273,15 +273,16 @@ describe("claudeTlsClient", () => { await tlsFetchClaude("https://claude.ai/test", {}); - // The testOverride is called with the raw options object BEFORE proxy - // resolution occurs (see claudeTlsClient.ts line 258: - // `if (testOverride) return testOverride(url, options)`). - // Proxy resolution (env var → proxyUrl) only runs inside the real - // tls-client path, which is bypassed when an override is active. - // So callOptions here is exactly the {} we passed — no proxyUrl injected. + // #10910 passou a resolver proxyUrl ANTES de chamar o testOverride + // (tlsClientBase.ts: "Resolve proxyUrl early so test overrides and the real + // path both see it"), justamente para que o override enxergue o mesmo proxy + // que o caminho real usaria. A assercao anterior travava o comportamento + // antigo — override recebia o {} cru — e contradizia o proprio nome deste + // teste, que diz verificar o fallback para a env var. Agora ela confere o + // fallback de fato. expect(mockFn).toHaveBeenCalledOnce(); const callOptions = mockFn.mock.calls[0][1]; - expect(callOptions.proxyUrl).toBeUndefined(); + expect(callOptions.proxyUrl).toBe("http://env-proxy:8080"); __setTlsFetchOverrideForTesting(null); delete process.env.HTTPS_PROXY; diff --git a/src/app/(dashboard)/dashboard/providers/components/__tests__/providerCardAudioBadge.test.tsx b/src/app/(dashboard)/dashboard/providers/components/__tests__/providerCardAudioBadge.test.tsx index 71850594aa..dbaa48cea8 100644 --- a/src/app/(dashboard)/dashboard/providers/components/__tests__/providerCardAudioBadge.test.tsx +++ b/src/app/(dashboard)/dashboard/providers/components/__tests__/providerCardAudioBadge.test.tsx @@ -7,6 +7,7 @@ import ProviderCard from "../ProviderCard"; vi.mock("next-intl", () => ({ useTranslations: () => (k: string) => k })); vi.mock("@/shared/components/ProviderTestSlideOver", () => ({ default: () => null })); vi.mock("@/shared/components/ProviderIcon", () => ({ default: () => null })); +vi.mock("next/navigation", () => ({ useRouter: () => ({ push: () => {} }) })); describe("ProviderCard — #6936 audio-transcriptions provider badge", () => { let container: HTMLDivElement | null = null; diff --git a/tests/unit/agent-skills-page.test.tsx b/tests/unit/agent-skills-page.test.tsx index b315e71806..1b241c4480 100644 --- a/tests/unit/agent-skills-page.test.tsx +++ b/tests/unit/agent-skills-page.test.tsx @@ -92,14 +92,16 @@ function make42Skills(): AgentSkill[] { const FULL_COVERAGE: SkillCoverage = { api: { have: 22, total: 22 }, cli: { have: 20, total: 20 }, - totalSkills: 42, + config: { have: 2, total: 2 }, + totalSkills: 44, generatedAt: new Date().toISOString(), }; const PARTIAL_COVERAGE: SkillCoverage = { api: { have: 10, total: 22 }, cli: { have: 8, total: 20 }, - totalSkills: 18, + config: { have: 1, total: 2 }, + totalSkills: 19, generatedAt: new Date().toISOString(), }; @@ -328,14 +330,17 @@ describe("AgentSkillsPageClient", () => { expect(coverageBar).not.toBeNull(); const progressBars = container.querySelectorAll("[role='progressbar']"); - expect(progressBars.length).toBe(2); + // A ordem de render em CoverageBar e api -> config -> cli, entao o indice do CLI + // acompanha a barra de config; sem isso [1] passaria a apontar para config e a + // assercao do CLI ficaria verde medindo a barra errada. + expect(progressBars.length).toBe(3); // API bar — 22/22 = 100%, should have emerald color class const apiBar = progressBars[0] as HTMLElement; expect(apiBar.className).toContain("bg-emerald-500"); // CLI bar — 20/20 = 100%, should have emerald color class - const cliBar = progressBars[1] as HTMLElement; + const cliBar = progressBars[2] as HTMLElement; expect(cliBar.className).toContain("bg-emerald-500"); }); @@ -425,7 +430,7 @@ describe("AgentSkillsPageClient", () => { // ── CoverageBar isolated tests ─────────────────────────────────────────────── describe("CoverageBar", () => { - it("renders two progressbars with correct aria attributes", async () => { + it("renders three progressbars with correct aria attributes", async () => { const { CoverageBar } = await import("../../src/app/(dashboard)/dashboard/agent-skills/components/CoverageBar"); const container = makeContainer(); @@ -435,13 +440,20 @@ describe("CoverageBar", () => { }); const bars = container.querySelectorAll("[role='progressbar']"); - expect(bars.length).toBe(2); + // api -> config -> cli. A barra de config entrou no MEIO, entao o indice do CLI + // desloca junto; conferir as tres aqui e o que impede um indice errado de passar + // despercebido medindo a barra vizinha. + expect(bars.length).toBe(3); const apiBar = bars[0] as HTMLElement; expect(apiBar.getAttribute("aria-valuenow")).toBe("22"); expect(apiBar.getAttribute("aria-valuemax")).toBe("22"); - const cliBar = bars[1] as HTMLElement; + const configBar = bars[1] as HTMLElement; + expect(configBar.getAttribute("aria-valuenow")).toBe("2"); + expect(configBar.getAttribute("aria-valuemax")).toBe("2"); + + const cliBar = bars[2] as HTMLElement; expect(cliBar.getAttribute("aria-valuenow")).toBe("20"); expect(cliBar.getAttribute("aria-valuemax")).toBe("20"); @@ -454,6 +466,7 @@ describe("CoverageBar", () => { const lowCoverage: SkillCoverage = { api: { have: 5, total: 22 }, cli: { have: 0, total: 20 }, + config: { have: 0, total: 2 }, totalSkills: 5, generatedAt: new Date().toISOString(), }; @@ -477,7 +490,8 @@ describe("CoverageBar", () => { const partialCoverage: SkillCoverage = { api: { have: 18, total: 22 }, // ~81.8% = amber cli: { have: 15, total: 20 }, // 75% = amber - totalSkills: 33, + config: { have: 3, total: 4 }, // 75% = amber + totalSkills: 36, generatedAt: new Date().toISOString(), }; diff --git a/tests/unit/ui/CliAgentsPage.test.tsx b/tests/unit/ui/CliAgentsPage.test.tsx index 257f5e4149..fd1cb1a6d6 100644 --- a/tests/unit/ui/CliAgentsPage.test.tsx +++ b/tests/unit/ui/CliAgentsPage.test.tsx @@ -4,6 +4,7 @@ import { act } from "react"; import { createRoot } from "react-dom/client"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import type { ToolBatchStatusMap } from "@/shared/types/cliBatchStatus"; +import { CLI_TOOLS } from "@/shared/constants/cliTools"; // ── Mocks (declared before any imports that depend on them) ─────────────────── @@ -48,9 +49,8 @@ vi.mock("@/app/(dashboard)/dashboard/cli-code/components/CliStatusBadge", () => // ── Static imports after mocks ──────────────────────────────────────────────── -const { default: CliAgentsPageClient } = await import( - "@/app/(dashboard)/dashboard/cli-agents/CliAgentsPageClient" -); +const { default: CliAgentsPageClient } = + await import("@/app/(dashboard)/dashboard/cli-agents/CliAgentsPageClient"); // ── Fixtures ────────────────────────────────────────────────────────────────── @@ -59,16 +59,14 @@ const { default: CliAgentsPageClient } = await import( * src/shared/constants/cliTools.ts). "omp" and "letta" were added to the * catalog after plan-14 shipped, bringing the count from 6 to 8. */ -const AGENT_IDS = [ - "openclaw", - "hermes-agent", - "goose", - "interpreter", - "omp", - "letta", - "warp", - "agent-deck", -] as const; +// Derivado do catalogo, NAO escrito a mao. A lista hardcoded anterior ja tinha +// derivado duas vezes (6 -> 8 com omp/letta, depois 8 -> 9 com prime-agent), e a +// consequencia nao e obvia: um agente ausente daqui nao entra no mapa de status, +// cai no default not_installed e contamina os testes de filtro/contagem com um +// card extra. Derivar mantem o fixture em sincronia com a fonte por construcao. +const AGENT_IDS = Object.values(CLI_TOOLS) + .filter((tool) => tool.category === "agent") + .map((tool) => tool.id); function makeBatchStatusMap(overrides: Partial = {}): ToolBatchStatusMap { const base: ToolBatchStatusMap = {}; @@ -150,9 +148,9 @@ describe("CliAgentsPageClient", () => { expect(container.textContent).toContain("pageTitle"); }, 15000); - it("2. renders exactly 8 agent tool cards", async () => { + it("2. renders exactly one card per agent in the catalog", async () => { const container = await renderPage(); - expect(countAgentCards(container)).toBe(8); + expect(countAgentCards(container)).toBe(AGENT_IDS.length); }, 15000); it("3. search filter — 'hermes' shows 1 card (hermes-agent)", async () => { @@ -175,9 +173,7 @@ describe("CliAgentsPageClient", () => { const visibleCards = countAgentCards(container); expect(visibleCards).toBe(1); - const remainingHrefs = Array.from( - container.querySelectorAll("a[href]") - ) + const remainingHrefs = Array.from(container.querySelectorAll("a[href]")) .filter((a) => a.getAttribute("href")?.startsWith("/dashboard/cli-agents/")) .map((a) => a.getAttribute("href") ?? ""); diff --git a/tests/unit/ui/highlightableProviderCard.test.tsx b/tests/unit/ui/highlightableProviderCard.test.tsx index cefafdce64..22abb02782 100644 --- a/tests/unit/ui/highlightableProviderCard.test.tsx +++ b/tests/unit/ui/highlightableProviderCard.test.tsx @@ -18,6 +18,7 @@ import HighlightableProviderCard from "@/app/(dashboard)/dashboard/providers/com vi.mock("next-intl", () => ({ useTranslations: () => (k: string) => k })); vi.mock("@/shared/components/ProviderTestSlideOver", () => ({ default: () => null })); vi.mock("@/shared/components/ProviderIcon", () => ({ default: () => null })); +vi.mock("next/navigation", () => ({ useRouter: () => ({ push: () => {} }) })); // Deterministic anchor so the click path does not depend on next/link's router. vi.mock("next/link", () => ({ __esModule: true, diff --git a/tests/unit/ui/providerCardHandle.test.tsx b/tests/unit/ui/providerCardHandle.test.tsx index ce8f4b7fd7..bd4860b13e 100644 --- a/tests/unit/ui/providerCardHandle.test.tsx +++ b/tests/unit/ui/providerCardHandle.test.tsx @@ -16,6 +16,7 @@ import ProviderCard, { vi.mock("next-intl", () => ({ useTranslations: () => (k: string) => k })); vi.mock("@/shared/components/ProviderTestSlideOver", () => ({ default: () => null })); vi.mock("@/shared/components/ProviderIcon", () => ({ default: () => null })); +vi.mock("next/navigation", () => ({ useRouter: () => ({ push: () => {} }) })); // jsdom does not implement scrollIntoView or animate if (typeof Element.prototype.scrollIntoView === "undefined") { diff --git a/tests/unit/ui/providerCardKimiPartnerAccent.test.tsx b/tests/unit/ui/providerCardKimiPartnerAccent.test.tsx index 07aceb699a..85fa7e7b30 100644 --- a/tests/unit/ui/providerCardKimiPartnerAccent.test.tsx +++ b/tests/unit/ui/providerCardKimiPartnerAccent.test.tsx @@ -26,6 +26,7 @@ import ProviderCard from "@/app/(dashboard)/dashboard/providers/components/Provi vi.mock("next-intl", () => ({ useTranslations: () => (k: string) => k })); vi.mock("@/shared/components/ProviderTestSlideOver", () => ({ default: () => null })); vi.mock("@/shared/components/ProviderIcon", () => ({ default: () => null })); +vi.mock("next/navigation", () => ({ useRouter: () => ({ push: () => {} }) })); describe("ProviderCard — Kimi (Moonshot AI) founding-friend accent", () => { let container: HTMLDivElement | null = null;