/** * @vitest-environment happy-dom */ import fs from "node:fs"; import path from "node:path"; import type { ReactNode } from "react"; import { act } from "react"; import { createRoot } from "react-dom/client"; import { NextIntlClientProvider } from "next-intl"; import { describe, expect, test, vi } from "vitest"; import { UsageDocContent } from "@/app/[locale]/usage-doc/page"; import { locales } from "@/i18n/config"; // 测试环境不加载 next-intl/navigation -> next/navigation 的真实实现(避免 Next.js 运行时依赖) vi.mock("@/i18n/routing", () => ({ Link: ({ children }: { children: ReactNode }) => children, })); function loadUsageMessages(locale: string) { return JSON.parse( fs.readFileSync(path.join(process.cwd(), "messages", locale, "usage.json"), "utf8") ); } function renderWithIntl(locale: string, node: ReactNode) { const container = document.createElement("div"); document.body.appendChild(container); const root = createRoot(container); const usageMessages = loadUsageMessages(locale); act(() => { root.render( {node} ); }); return { unmount: () => { act(() => root.unmount()); container.remove(); }, }; } describe("UsageDoc - OpenCode 配置教程", () => { test("OpenCode 段落应位于 Gemini CLI 之后、Droid 之前", () => { const { unmount } = renderWithIntl("en", ); const h2Ids = Array.from(document.querySelectorAll("h2")).map((el) => el.id); expect(h2Ids).toContain("gemini"); expect(h2Ids).toContain("opencode"); expect(h2Ids).toContain("droid"); expect(h2Ids.indexOf("gemini")).toBeLessThan(h2Ids.indexOf("opencode")); expect(h2Ids.indexOf("opencode")).toBeLessThan(h2Ids.indexOf("droid")); unmount(); }); test("应提供单份 opencode.json 示例,且包含 cch 端点与所有要求模型", () => { const { unmount } = renderWithIntl("en", ); const text = document.body.textContent || ""; expect(text).toContain('"$schema": "https://opencode.ai/config.json"'); expect(text).toContain('"baseURL": "http://localhost:23000/v1"'); expect(text).toContain('"npm": "@ai-sdk/anthropic"'); expect(text).toContain('"npm": "@ai-sdk/google"'); expect(text).toContain('"npm": "@ai-sdk/openai"'); expect(text).not.toContain("@ai-sdk/openai-compatible"); expect(text).toContain("claude-haiku-4-5-20251001"); expect(text).toContain("claude-sonnet-4-5-20250929"); expect(text).toContain("claude-opus-4-5-20251101"); expect(text).toContain('"model": "openai/gpt-5.2"'); expect(text).toContain('"small_model": "openai/gpt-5.2-small"'); expect(text).toContain("gpt-5.2"); expect(text).toContain("gpt-5.2-small"); expect(text).toContain('"reasoningEffort": "xhigh"'); expect(text).toContain('"reasoningEffort": "medium"'); expect(text).toContain('"store": false'); expect(text).toContain('"setCacheKey": true'); expect(text).toContain("reasoning.encrypted_content"); expect(text).toContain("gemini-3-pro-preview"); expect(text).toContain("gemini-3-flash-preview"); expect(text).toContain('"baseURL": "http://localhost:23000/v1beta"'); unmount(); }); test("应包含官方安装方式示例(curl/npm/bun/brew/paru,以及 Windows 包管理器)", () => { const { unmount } = renderWithIntl("en", ); const text = document.body.textContent || ""; expect(text).toContain("curl -fsSL https://opencode.ai/install | bash"); expect(text).toContain("npm install -g opencode-ai"); expect(text).toContain("npm mirror registries"); expect(text).toContain("bun add -g opencode-ai"); expect(text).toContain("brew install anomalyco/tap/opencode"); expect(text).toContain("paru -S opencode-bin"); expect(text).toContain("choco install opencode"); expect(text).toContain("scoop bucket add extras"); expect(text).toContain("scoop install extras/opencode"); unmount(); }); test("5 语言 messages/ 需包含 OpenCode 段落的关键翻译键", () => { for (const locale of locales) { const usageMessages = loadUsageMessages(locale); expect(usageMessages).toHaveProperty("opencode.title"); expect(usageMessages).toHaveProperty("opencode.description"); expect(usageMessages).toHaveProperty("opencode.installation.title"); expect(usageMessages).toHaveProperty("opencode.installation.script.title"); expect(usageMessages).toHaveProperty("opencode.installation.npm.title"); expect(usageMessages).toHaveProperty("opencode.installation.npm.note"); expect(usageMessages).toHaveProperty("opencode.installation.bun.title"); expect(usageMessages).toHaveProperty("opencode.installation.macos.homebrew.title"); expect(usageMessages).toHaveProperty("opencode.installation.linux.homebrew.title"); expect(usageMessages).toHaveProperty("opencode.installation.linux.paru.title"); expect(usageMessages).toHaveProperty("opencode.installation.windows.note"); expect(usageMessages).toHaveProperty("opencode.configuration.title"); expect(usageMessages).toHaveProperty("opencode.startup.title"); expect(usageMessages).toHaveProperty("opencode.commonIssues.title"); expect(usageMessages).toHaveProperty("layout.headerTitle"); expect(usageMessages).toHaveProperty("layout.loginConsole"); expect(usageMessages).toHaveProperty("placeholders.windowsUserName"); expect(usageMessages).toHaveProperty("placeholders.shellConfig.linux"); expect(usageMessages).toHaveProperty("placeholders.shellConfig.macos"); expect(usageMessages).toHaveProperty("placeholders.codexVsCodeConfigFiles"); expect(usageMessages).toHaveProperty("claudeCode.installation.nativeInstall.macos.curls"); expect(usageMessages).toHaveProperty("snippets.comments.updateHomebrew"); expect(usageMessages).toHaveProperty("snippets.comments.installNodeJs"); expect(usageMessages).toHaveProperty("snippets.comments.ubuntuDebian"); expect(usageMessages).toHaveProperty("snippets.comments.centosRhelFedora"); expect(usageMessages).toHaveProperty("snippets.comments.addToPathIfMissing"); expect(usageMessages).toHaveProperty("snippets.comments.checkEnvVar"); expect(usageMessages).toHaveProperty("snippets.comments.testNetworkConnection"); } }); });