import assert from "node:assert/strict"; import test from "node:test"; import { readFileSync } from "node:fs"; import { createElement } from "react"; import { renderToStaticMarkup } from "react-dom/server"; import { PathnameContext } from "next/dist/shared/lib/hooks-client-context.shared-runtime"; import { AppSidebar } from "../src/components/app-sidebar.tsx"; import { SidebarProvider } from "../src/components/ui/sidebar.tsx"; import { reportSurfaceRedirect } from "../src/lib/synastry-report-href.ts"; function renderSidebar(pathname: string) { return renderToStaticMarkup( createElement( PathnameContext.Provider, { value: pathname }, createElement( SidebarProvider, null, createElement(AppSidebar, { sessions: [], activeSessionId: null, compatibilityEnabled: true, account: { name: "示例甲", email: "fictional@example.invalid", credits: 12, initial: "示", avatar: null, }, }), ), ), ); } function navAnchor(markup: string, label: string) { // 原值:锚点类名 `report-nav-button` // 新值:锚点类名 `sidebar-nav-link` // 原因:K1 侧栏导航共用一个类,合盘入口仍是这个链接 const anchors = markup.match(/]*\bsidebar-nav-link\b[^>]*>[\s\S]*?<\/a>/g) ?? []; const hit = anchors.find((anchor) => anchor.includes(`>${label}<`)); assert.ok(hit, `sidebar nav link missing: ${label}`); return hit; } const read = (path: string) => readFileSync(new URL(path, import.meta.url), "utf8"); test("an old synastry url moves to the synastry route and keeps the language", () => { assert.equal( reportSurfaceRedirect({ surface: "personal", reportType: "synastry_full", reportId: "abc", language: "en" }), "/synastry/abc?lang=en", ); assert.equal( reportSurfaceRedirect({ surface: "synastry", reportType: "personal_full", reportId: "abc", language: "zh" }), "/reports/abc", ); assert.equal( reportSurfaceRedirect({ surface: "synastry", reportType: "synastry_full", reportId: "abc", language: "zh" }), null, ); assert.equal( reportSurfaceRedirect({ surface: "personal", reportType: "personal_full", reportId: "abc", language: "en" }), null, ); }); test("the synastry route reuses the reader and the sidebar stays on 婚恋合盘", () => { const page = read("../src/app/(app)/synastry/[reportId]/page.tsx"); const reader = read("../src/components/personal-report/personal-report-page.tsx"); const sidebar = read("../src/components/app-sidebar.tsx"); const picker = read("../src/components/synastry/synastry-picker.tsx"); const center = read("../src/components/personal-report/personal-report-center.tsx"); assert.match(page, /title: "婚恋合盘 · Jyotisha"/); assert.match(page, /readReportLanguage\(lang\)/); assert.match(page, /surface="synastry"/); assert.match(reader, /window\.location\.replace\(redirectTo\)/); assert.match(sidebar, /\{ href: "\/synastry", label: "婚恋合盘"/); assert.match(sidebar, /pathname === href \|\| pathname\.startsWith\(`\$\{href\}\/`\)/); // 原值: assert.equal("/synastry/abc".startsWith("/synastry/"), true) // 新值: pathname 为 /synastry/ 时渲染侧栏,当前项(data-active="true")是「婚恋合盘」,不是「我的报告」 // 原因: 原断言永远成立,没有渲染侧栏(issue #85 G3) const markup = renderSidebar("/synastry/abc"); const synastry = navAnchor(markup, "婚恋合盘"); const reports = navAnchor(markup, "我的报告"); assert.match(synastry, /\bdata-active="true"/); assert.doesNotMatch(reports, /\bdata-active="true"/); assert.doesNotMatch(reports, /aria-current="page"/); assert.match(picker, /window\.location\.assign\(`\/synastry\/\$\{payload\.report\.id\}`\)/); assert.match(picker, /href=\{`\/synastry\/\$\{report\.id\}`\}/); assert.doesNotMatch(picker, /\/reports\/\$\{/); assert.match(center, /reportType === "synastry_full" \? `\/synastry\/\$\{id\}` : `\/reports\/\$\{id\}`/); });