From 208da8d00d5cd196a839186a36d43dc9f8b4a6a1 Mon Sep 17 00:00:00 2001 From: Catlaxy AI Date: Tue, 6 Oct 2026 09:23:18 +0800 Subject: [PATCH] =?UTF-8?q?fix(welcome):=20=E6=AC=A2=E8=BF=8E=E9=A1=B5?= =?UTF-8?q?=E6=B2=BF=E7=94=A8=E7=AB=99=E7=82=B9=E5=A4=96=E8=A7=82=E4=B8=8E?= =?UTF-8?q?=20SEO=20=E6=A0=87=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 合并 #683:欢迎页沿用站点外观与 SEO 标题 --- web/src/main.tsx | 6 +++-- web/src/pages/welcome/index.tsx | 1 - web/test/welcome-title.test.mjs | 44 +++++++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 3 deletions(-) create mode 100644 web/test/welcome-title.test.mjs diff --git a/web/src/main.tsx b/web/src/main.tsx index 115869a6d..bd10f2abe 100644 --- a/web/src/main.tsx +++ b/web/src/main.tsx @@ -7,8 +7,10 @@ import { isIsolatedPrevisRepro } from "@/lib/dev-repro"; installChunkRecovery(); // The public film entry checks its availability independently of workspace bootstrap. -if (/^\/welcome\/?$/.test(window.location.pathname)) void import("./welcome-application"); -else { +if (/^\/welcome\/?$/.test(window.location.pathname)) { + void bootstrapAppearance().catch(() => undefined); + void import("./welcome-application"); +} else { // The backend-free DEV lab must not make requests before AppProviders isolates it. const appearanceReady = isIsolatedPrevisRepro(import.meta.env.DEV, window.location.pathname) ? Promise.resolve() : bootstrapAppearance(); void import("./application"); diff --git a/web/src/pages/welcome/index.tsx b/web/src/pages/welcome/index.tsx index 07b22ec78..15e1247a7 100644 --- a/web/src/pages/welcome/index.tsx +++ b/web/src/pages/welcome/index.tsx @@ -59,7 +59,6 @@ function WelcomeExperience({ look, brandName, onLookChange }: { look: WelcomeLoo const videoRef = useRef(null); useEffect(() => { - document.title = "影策 · 让一个故事从文字走向银幕"; const media = window.matchMedia("(prefers-reduced-motion: reduce)"); const onMotion = () => setReduced(media.matches); media.addEventListener("change", onMotion); diff --git a/web/test/welcome-title.test.mjs b/web/test/welcome-title.test.mjs new file mode 100644 index 000000000..69b6363f0 --- /dev/null +++ b/web/test/welcome-title.test.mjs @@ -0,0 +1,44 @@ +import assert from "node:assert/strict"; +import { readFileSync } from "node:fs"; +import { test } from "node:test"; +import vm from "node:vm"; + +const source = (path) => readFileSync(new URL(path, import.meta.url), "utf8"); + +test("welcome entry loads configured appearance", async () => { + let calls = 0; + const imports = []; + const main = source("../src/main.tsx") + .replace(/^import .*;\n/gm, "") + .replace(/import\.meta\.env\.DEV/g, "false") + .replace(/import\(/g, "loadModule("); + vm.runInNewContext(main, { + window: { location: { pathname: "/welcome" } }, + installChunkRecovery() {}, + isIsolatedDirectorRepro: () => false, + bootstrapAppearance: async () => { calls++; }, + loadModule: async (name) => { imports.push(name); }, + }); + await Promise.resolve(); + assert.equal(calls, 1, "welcome entry must bootstrap appearance"); + assert.deepEqual(imports, ["./welcome-application"]); +}); + +test("welcome effects preserve configured SEO title", () => { + const page = source("../src/pages/welcome/index.tsx"); + // Run the actual mount effects with inert motion/scroll listeners. + const effects = [...page.matchAll(/useEffect\(\(\) => \{([\s\S]*?)\n \}, \[([^\]]*)\]\);/g)].filter((match) => match[2] === ""); + assert.ok(effects.length > 0); + for (const title of ["Custom SEO Title", "Custom Site Name"]) { + const document = { title }; + for (const [, body] of effects) { + vm.runInNewContext(`(() => {${body}\n})()`, { + document, + window: { matchMedia: () => ({ matches: false, addEventListener() {}, removeEventListener() {} }), addEventListener() {}, removeEventListener() {} }, + storyRef: { current: null }, restorePickerFocus: { current: false }, + setReduced() {}, setLook() {}, getWelcomeLook() {}, + }); + } + assert.equal(document.title, title, "welcome must preserve appearance metadata title"); + } +});