mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-26 00:52:18 +03:00
test(vitest): realign five sibling-test contracts unmasked by the green mcp shard
None of these are cycle regressions. The Vitest job runs test:vitest (mcp shard) then test:vitest:ui; the mcp shard was failing on a missing glm-5.3-max and aborted the job before the ui shard ever ran. Fixing that shard this cycle unmasked 34 ui failures that had been broken since 18-23 Aug — four separate PRs that moved a contract and updated their own tests but not their siblings. - ProviderCard gained useRouter() in #10448; four test files render it without mocking next/navigation and died on 'invariant expected app router to be mounted'. The sibling created alongside #10448 already had the mock — it just was not applied to the other four consumers. - SkillCoverage gained a required config category. The four fixtures in agent-skills-page still described only api/cli, so the component read config.have off undefined. Values were chosen per scenario rather than pasted: full coverage gets 2/2 so its bar stays emerald, the amber fixture gets 3/4 so it stays amber. CoverageBar renders api -> config -> cli, so the new bar lands in the MIDDLE and the cli assertions moved from index [1] to [2]; without that the cli checks would have passed while measuring the config bar. The aria test now pins all three bars. - CliAgentsPage hardcoded AGENT_IDS, which had already drifted once (6 -> 8 with omp/letta, per its own comment) and drifted again with prime-agent (#11166). It is now derived from CLI_TOOLS. This is why an agent missing from that list is not cosmetic: it never enters the status map, defaults to not_installed, and adds a phantom card to the filter and count tests. Deriving keeps the fixture in sync by construction instead of waiting for the next agent. - claudeTlsClient asserted proxyUrl was undefined inside a test literally named 'falls back to env var when per-call proxyUrl not provided' — it pinned the old behaviour where testOverride bypassed proxy resolution. #10910 moved resolution ahead of the override on purpose ('so test overrides and the real path both see it'), so the assertion now checks the fallback the test name promises. test:vitest:ui goes from 34 failures to 14. The remaining 14 sit in six files none of this commit touches (AutoComboCatalog, CoolingConnectionsPanel, ProxyRegistryManager x2, connectionsSearchFilter) plus one claudeTlsClient case that passes in isolation and only fails in the full run — i.e. cross-file pollution. They need a clean environment to judge: this devbox resolves part of its tree through a stray pnpm store and has already produced one phantom failure count this cycle. Refs #10692
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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(),
|
||||
};
|
||||
|
||||
|
||||
@@ -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> = {}): 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<HTMLAnchorElement>("a[href]")
|
||||
)
|
||||
const remainingHrefs = Array.from(container.querySelectorAll<HTMLAnchorElement>("a[href]"))
|
||||
.filter((a) => a.getAttribute("href")?.startsWith("/dashboard/cli-agents/"))
|
||||
.map((a) => a.getAttribute("href") ?? "");
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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") {
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user