import assert from "node:assert/strict"; import { readFileSync } from "node:fs"; import { createElement, createRef } from "react"; import { renderToString } from "react-dom/server"; import test from "node:test"; import { AccountDialogOverlay, type AccountOverlayModel, } from "../src/components/account-dialog-overlay.tsx"; function overlayModel(overrides: Partial = {}): AccountOverlayModel { const overlayRef = createRef() as AccountOverlayModel["overlayRef"]; const closeButtonRef = createRef() as AccountOverlayModel["closeButtonRef"]; return { title: "个人资料", dialogClass: "settings-modal", signingOut: false, close() {}, navigate() {}, overlayRef, closeButtonRef, renderProfile() { return null; }, renderChartLibrary() { return null; }, renderBilling() { return null; }, renderGeneral() { return null; }, renderLogout() { return null; }, ...overrides, }; } test("a closed account overlay does not render profile or logout content", () => { let profileRenders = 0; let logoutRenders = 0; const model = overlayModel({ renderProfile() { profileRenders += 1; return null; }, renderLogout() { logoutRenders += 1; return null; }, }); const html = renderToString(createElement(AccountDialogOverlay, { open: false, dialog: null, model, })); assert.equal(html, ""); assert.equal(profileRenders, 0); assert.equal(logoutRenders, 0); }); test("home does not write overlay epoch or chat actions during render", () => { const page = readFileSync(new URL("../src/app/page.tsx", import.meta.url), "utf8"); const overlay = readFileSync(new URL("../src/components/account-dialog-overlay.tsx", import.meta.url), "utf8"); assert.match(overlay, /model: AccountOverlayModel \| null/); assert.doesNotMatch(overlay, /openEpoch/); assert.doesNotMatch(overlay, /modelRef/); assert.doesNotMatch(page, /accountOverlayEpochRef/); assert.doesNotMatch(page, /sessionsRef\.current = sessions/); assert.match(page, /queueMicrotask\(\(\) => setActiveChartId\(storedChartId\)\)/); assert.match(page, /useEffect\(\(\) => \{\s*chatActionsRef\.current = \{/); }); test("settings navigation lists four panes and billing can be current", () => { const overlay = readFileSync(new URL("../src/components/account-dialog-overlay.tsx", import.meta.url), "utf8"); const styles = readFileSync(new URL("../src/app/globals.css", import.meta.url), "utf8"); const html = renderToString(createElement(AccountDialogOverlay, { open: true, dialog: "billing", model: overlayModel({ title: "账户与点数" }), })); assert.match(overlay, /dialog: "profile"/); assert.match(overlay, /dialog: "chart-library"/); assert.match(overlay, /dialog: "billing"/); assert.match(overlay, /dialog: "general"/); assert.doesNotMatch(overlay, /openRedeem/); assert.match(html, /个人资料/); assert.match(html, /星盘资料/); assert.match(html, /账户与点数/); assert.match(html, /通用设置/); assert.match(html, /aria-current="page"/); assert.match(styles, /\.settings-modal \{[^}]*width:[^}]*height:/); assert.doesNotMatch(styles, /chart-library-modal|profile-modal/); });