mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-27 01:22:10 +03:00
Moonshot approved a 15% extra-credits offer for new users' first top-up, attached to a dedicated tracked link issued for OmniRoute (valid through 2026-09-30). The dashboard banner and the three README API platform placements now use that link, and the banner description leads with the discount in all 43 locales, keeping the commitment made when the offer was requested. The README CTA gains the 15% mention. A code comment marks the strings to revisit after 2026-09-30 if the offer is not renewed. Co-authored-by: backryun <bakryun0718@proton.me>
112 lines
4.6 KiB
TypeScript
112 lines
4.6 KiB
TypeScript
// @vitest-environment jsdom
|
|
/**
|
|
* KimiSponsorBanner (2026-07 partnership) — render gate (version window +
|
|
* localStorage dismissal), CTA aff link, and discreet partner-link note.
|
|
* See src/app/(dashboard)/dashboard/kimiSponsorBannerGate.ts for the version-gate
|
|
* pure logic (covered separately by tests/unit/kimi-sponsor-banner-version-gate.test.ts).
|
|
*/
|
|
import React from "react";
|
|
import { act } from "react";
|
|
import { createRoot } from "react-dom/client";
|
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
|
|
const STORAGE_KEY = "omniroute-kimi-sponsor-banner-dismissed-v2";
|
|
const LEGACY_V1_STORAGE_KEY = "omniroute-kimi-sponsor-banner-dismissed-v1";
|
|
const KIMI_PLATFORM_AFF_URL =
|
|
"https://platform.kimi.ai?track_id=track-8197581fdd7d4139a0f562e4a03c3798&aff=omniroute";
|
|
|
|
vi.mock("next-intl", () => ({ useTranslations: () => (k: string) => k }));
|
|
vi.mock("@/shared/components/ProviderIcon", () => ({ default: () => null }));
|
|
|
|
async function renderBanner(version: string): Promise<HTMLDivElement> {
|
|
vi.resetModules();
|
|
vi.doMock("@/shared/constants/appConfig", () => ({
|
|
APP_CONFIG: { name: "OmniRoute", description: "AI Gateway", version },
|
|
}));
|
|
const { default: KimiSponsorBanner } =
|
|
await import("../../../src/app/(dashboard)/dashboard/KimiSponsorBanner");
|
|
|
|
const container = document.createElement("div");
|
|
document.body.appendChild(container);
|
|
const root = createRoot(container);
|
|
act(() => {
|
|
root.render(<KimiSponsorBanner />);
|
|
});
|
|
return container;
|
|
}
|
|
|
|
describe("KimiSponsorBanner", () => {
|
|
beforeEach(() => {
|
|
(
|
|
globalThis as typeof globalThis & { IS_REACT_ACT_ENVIRONMENT?: boolean }
|
|
).IS_REACT_ACT_ENVIRONMENT = true;
|
|
localStorage.removeItem(STORAGE_KEY);
|
|
});
|
|
|
|
afterEach(() => {
|
|
document.body.innerHTML = "";
|
|
localStorage.removeItem(STORAGE_KEY);
|
|
vi.doUnmock("@/shared/constants/appConfig");
|
|
});
|
|
|
|
it("renders with the CTA pointing at the Kimi API Platform aff link inside the version window", async () => {
|
|
const container = await renderBanner("3.8.49");
|
|
expect(container.textContent).toContain("foundingFriendTitle");
|
|
expect(container.textContent).toContain("cta");
|
|
const link = container.querySelector("a[href]");
|
|
expect(link).not.toBeNull();
|
|
expect(link?.getAttribute("href")).toBe(KIMI_PLATFORM_AFF_URL);
|
|
expect(link?.getAttribute("target")).toBe("_blank");
|
|
expect(link?.getAttribute("rel")).toContain("noopener");
|
|
});
|
|
|
|
it("shows the discreet partner-link note near the CTA", async () => {
|
|
const container = await renderBanner("3.8.49");
|
|
// "partnerLinkNote" is the (mocked) translation key rendered as text, and
|
|
// also set as the CTA anchor's title attribute.
|
|
expect(container.textContent).toContain("partnerLinkNote");
|
|
const link = container.querySelector("a[href]");
|
|
expect(link?.getAttribute("title")).toBe("partnerLinkNote");
|
|
});
|
|
|
|
it("renders at the inclusive upper bound of the version window (v3.8.60)", async () => {
|
|
const container = await renderBanner("3.8.60");
|
|
expect(container.querySelector("a[href]")).not.toBeNull();
|
|
});
|
|
|
|
it("does not render past the sunset version (v3.8.61)", async () => {
|
|
const container = await renderBanner("3.8.61");
|
|
expect(container.querySelector("[role='complementary']")).toBeNull();
|
|
expect(container.textContent).toBe("");
|
|
});
|
|
|
|
it("dismiss button hides the banner and persists the dismissal to localStorage", async () => {
|
|
const container = await renderBanner("3.8.49");
|
|
expect(container.querySelector("[role='complementary']")).not.toBeNull();
|
|
|
|
const dismissButton = container.querySelector("button");
|
|
expect(dismissButton).not.toBeNull();
|
|
act(() => {
|
|
dismissButton?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
|
});
|
|
|
|
expect(container.querySelector("[role='complementary']")).toBeNull();
|
|
expect(localStorage.getItem(STORAGE_KEY)).toBe("true");
|
|
});
|
|
|
|
it("stays hidden across a fresh render once dismissed (localStorage persistence)", async () => {
|
|
localStorage.setItem(STORAGE_KEY, "true");
|
|
const container = await renderBanner("3.8.49");
|
|
expect(container.querySelector("[role='complementary']")).toBeNull();
|
|
});
|
|
|
|
it("re-shows the retargeted banner to users who dismissed the v1 (Get Kimi Code) version", async () => {
|
|
// 2026-08 CTA retarget (coding plan -> API platform) bumped the dismissal
|
|
// key to -v2 so the whole install base gets one fresh impression.
|
|
localStorage.setItem(LEGACY_V1_STORAGE_KEY, "true");
|
|
const container = await renderBanner("3.8.50");
|
|
expect(container.querySelector("[role='complementary']")).not.toBeNull();
|
|
localStorage.removeItem(LEGACY_V1_STORAGE_KEY);
|
|
});
|
|
});
|