Files
OmniRoute/tests/unit/ui/providerCardKimiPartnerAccent.test.tsx
Xiangzhe dd3a0f0189 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
2026-08-25 09:48:12 -03:00

95 lines
4.2 KiB
TypeScript

// @vitest-environment jsdom
/**
* Kimi (Moonshot AI) official-partnership card accent (2026-07). Presentation
* only — see src/app/(dashboard)/dashboard/providers/featuredProviders.ts.
*
* NOTE on placement: this mirrors the sibling
* src/app/(dashboard)/dashboard/providers/components/__tests__/providerCardAudioBadge.test.tsx
* in every way EXCEPT location. That co-located `__tests__/` pattern is not
* actually picked up by either blocking vitest script today:
* - vitest.mcp.config.ts's "src/app/(dashboard)/**\/__tests__/**\/*.test.tsx"
* glob never matches anything (unescaped parens in tinyglobby — verified:
* `glob(["src/app/(dashboard)/**\/__tests__/**\/*.test.tsx"])` returns []).
* - test:vitest:ui runs `vitest run --config vitest.config.ts tests/unit/ui`,
* and that positional path filter excludes anything outside tests/unit/ui.
* Living under tests/unit/ui/ guarantees this file is discovered by both
* vitest.config.ts's own "tests/unit/**\/*.test.tsx" include entry AND the
* test:vitest:ui CLI filter, so it actually runs in the blocking `test-vitest`
* CI job (see .github/workflows/ci.yml).
*/
import React from "react";
import { createRoot } from "react-dom/client";
import { act } from "react";
import { afterEach, describe, expect, it, vi } from "vitest";
import ProviderCard from "@/app/(dashboard)/dashboard/providers/components/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 — Kimi (Moonshot AI) founding-friend accent", () => {
let container: HTMLDivElement | null = null;
afterEach(() => {
if (container) {
document.body.removeChild(container);
container = null;
}
});
function renderCard(providerId: string, name: string) {
container = document.createElement("div");
document.body.appendChild(container);
const root = createRoot(container);
act(() => {
root.render(
<ProviderCard
providerId={providerId}
provider={{ id: providerId, name }}
stats={{ total: 1, connected: 1, error: 0, warning: 0 }}
authType="apikey"
onToggle={() => {}}
/>
);
});
return container;
}
it("renders the Founding Friend badge + Kimi-blue accent for kimi-coding", () => {
const el = renderCard("kimi-coding", "Kimi Code CLI");
// The next-intl mock returns the key itself; the providerText() helper falls
// back to the English default only when t.has is undefined (as it is here),
// so the rendered text is the hardcoded English fallback.
expect(el.textContent).toContain("Founding Friend");
// The card's own accent border/glow (KIMI_BRAND_COLOR = #1783FF) must be
// present on some element in the tree — both the outer Card border classes
// and the badge chip carry it.
const accented = el.querySelector("[class*='1783FF']");
expect(accented).not.toBeNull();
});
it("renders the Founding Friend badge for kimi-web and moonshot too", () => {
const kimiWebEl = renderCard("kimi-web", "Kimi Web");
expect(kimiWebEl.textContent).toContain("Founding Friend");
const moonshotEl = renderCard("moonshot", "Kimi");
expect(moonshotEl.textContent).toContain("Founding Friend");
});
it("does NOT render the Kimi badge or accent for an unrelated provider", () => {
const el = renderCard("openai", "OpenAI");
expect(el.textContent).not.toContain("Founding Friend");
expect(el.querySelector("[class*='1783FF']")).toBeNull();
});
it("does NOT render the Kimi badge for the hidden kimi-coding-apikey alias id", () => {
// kimi-coding-apikey is hiddenFromDashboard (folds into the kimi-coding card),
// but featuredProviders.ts still lists it — verifying the card component
// itself would still flag it correctly if it were ever rendered directly.
const el = renderCard("kimi-coding-apikey", "Kimi Code API Key");
expect(el.textContent).toContain("Founding Friend");
});
});