From 48dead992f2641d8c44b19bdb608cd5688af85de Mon Sep 17 00:00:00 2001 From: Jesse_Chen Date: Wed, 22 Jul 2026 18:41:25 +0800 Subject: [PATCH] test: restore production release gate --- docs/BUG_HISTORY.md | 16 ++++++++++++++++ frontend/src/components/chat-message-row.tsx | 2 +- .../birth-time-consultation-consent.test.ts | 2 +- .../tests/birth-time-guided-review-fixes.test.ts | 2 +- frontend/tests/chat-stream-layout.test.ts | 2 +- .../tests/rectification-question-handoff.test.ts | 8 ++++---- frontend/tests/starter-questions.test.ts | 13 ++++++++----- 7 files changed, 32 insertions(+), 13 deletions(-) diff --git a/docs/BUG_HISTORY.md b/docs/BUG_HISTORY.md index cc894144..62748f64 100644 --- a/docs/BUG_HISTORY.md +++ b/docs/BUG_HISTORY.md @@ -441,3 +441,19 @@ - 相关记录:BUG-007、BUG-020、BUG-021 - 复发自:BUG-007 - 修复版本:待提交(测试 Supabase smoke 通过) + +## BUG-026 | 首页与会话改版后旧测试阻断生产发布门禁 + +- 状态:resolved +- 首次发现:2026-07-22 +- 最近更新:2026-07-22 +- 影响面:production 发布门禁、首页主题入口、会话输入区、生时校正组件服务端渲染测试 +- 用户现象:production 应用构建成功,但完整前端测试有 7 项失败,导致最新 `main` 无法通过发布前验证。 +- 触发条件:运行完整前端测试;旧断言仍查找已移除的首页证据预览、固定 180px 模型菜单、无条件输入区类名和旧 `profile` 文案来源,组件测试还会在 Node ESM 下加载 GSAP 默认导出。 +- 根因:首页与普通 session 的布局和文案已经更新,测试契约没有随产品行为同步;`chat-message-row` 使用 GSAP 默认导入,在 Next 构建中可用,但 Node ESM 测试运行器解析到的模块形态不同。 +- 修复:测试改为锁定当前首页主题选择、内容自适应模型菜单、带首页修饰类的停靠输入区和 `profileDraft` 文案来源;GSAP 改用其正式命名导出,保持浏览器行为不变并恢复 Node ESM 组件测试。 +- 验证:6 个相关测试文件、完整前端测试、ESLint、production build 与 patch check 全部通过后方可发布。 +- 防复发:产品 UI 结构调整时必须在同一提交同步源码契约测试;跨 Next 与 Node 运行的依赖优先使用包声明的稳定命名导出。 +- 相关记录:BUG-022、BUG-024、BUG-025 +- 复发自:无 +- 修复版本:待提交(production gate) diff --git a/frontend/src/components/chat-message-row.tsx b/frontend/src/components/chat-message-row.tsx index ae2c3ee8..9d5dfa01 100644 --- a/frontend/src/components/chat-message-row.tsx +++ b/frontend/src/components/chat-message-row.tsx @@ -1,7 +1,7 @@ "use client"; import { useGSAP } from "@gsap/react"; -import gsap from "gsap"; +import { gsap } from "gsap"; import { useRef } from "react"; import { AgentActivityStatus } from "@/components/agent-activity-status"; diff --git a/frontend/tests/birth-time-consultation-consent.test.ts b/frontend/tests/birth-time-consultation-consent.test.ts index 4e58e221..d2b25d6c 100644 --- a/frontend/tests/birth-time-consultation-consent.test.ts +++ b/frontend/tests/birth-time-consultation-consent.test.ts @@ -239,7 +239,7 @@ test("homepage and profile result copy use the source-aware consultation options const intake = readFileSync(new URL("../src/components/birth-time-intake.tsx", import.meta.url), "utf8"); assert.match(page, /birthTimeConsultationOptionsCopy\(profileDraft\)/); - assert.match(page, /birthTimeConsultationOptionsCopy\(profile\)/); + assert.doesNotMatch(page, /birthTimeConsultationOptionsCopy\(profile\)/); assert.match(intake, /birthTimeConsultationOptionsCopy\(value\)/); }); diff --git a/frontend/tests/birth-time-guided-review-fixes.test.ts b/frontend/tests/birth-time-guided-review-fixes.test.ts index 3ee52cc7..5a8c7058 100644 --- a/frontend/tests/birth-time-guided-review-fixes.test.ts +++ b/frontend/tests/birth-time-guided-review-fixes.test.ts @@ -150,7 +150,7 @@ test("terminal CJK copy stays intact while homepage candidates remain unconfirme const pageSource = readFileSync(new URL("../src/app/page.tsx", import.meta.url), "utf8"); assert.match(candidateResultSource, /候选范围已保留,但当前证据不足以将具体分钟写入当前排盘时间。补充经历后可重新评估。/); - assert.match(pageSource, /未确认;\$\{birthTimeConsultationOptionsCopy\(profile\)\}/); + assert.match(pageSource, /`出生资料已保存。\$\{birthTimeConsultationOptionsCopy\(profileDraft\)\}`/); assert.match(pageSource, / { }); test("docks the composer inside the chat panel instead of floating over content", () => { - assert.match(pageSource, /
/); + assert.match(pageSource, /
/); assert.match(globalStyles, /\.chat-panel[^}]*grid-template-rows:\s*68px minmax\(0,\s*1fr\) auto/); assert.match(globalStyles, /\.conversation[^}]*padding-bottom:\s*var\(--composer-reserve\)/); assert.match(globalStyles, /\.composer-wrap[^}]*position:\s*sticky/); diff --git a/frontend/tests/rectification-question-handoff.test.ts b/frontend/tests/rectification-question-handoff.test.ts index 54b05b7f..8f8e8c0c 100644 --- a/frontend/tests/rectification-question-handoff.test.ts +++ b/frontend/tests/rectification-question-handoff.test.ts @@ -70,7 +70,7 @@ function controllerFor(turn: ConversationalRectificationTurn): ConversationalRec }; } -test("confirmed surface requires an explicit click before continuing the original question", () => { +test("confirmed surface requires an explicit click before returning to the original question", () => { let continuationCalls = 0; const markup = renderToStaticMarkup(React.createElement( ConversationalRectificationSurface, @@ -80,8 +80,8 @@ test("confirmed surface requires an explicit click before continuing the origina }, )); - assert.match(markup, /原问题:未来半年是否适合换工作?/); - assert.match(markup, />使用新确认时间继续回答原问题返回原问题]+disabled=""[^>]*>正在继续回答原问题…<\/button>/); + assert.match(markup, /]+disabled=""[^>]*>正在继续回答…<\/button>/); }); test("choosing rectify-first captures the visible question and passes it only to v3 start", async () => { diff --git a/frontend/tests/starter-questions.test.ts b/frontend/tests/starter-questions.test.ts index 2b0bc63b..0098060f 100644 --- a/frontend/tests/starter-questions.test.ts +++ b/frontend/tests/starter-questions.test.ts @@ -36,8 +36,9 @@ test("completed account initialization switches directly to the home cards", () test("default starter questions are guided Jyotish topics with evidence and claim boundaries", () => { assert.match(pageSource, /defaultGuidedJyotishTopics/); - assert.match(pageSource, /theme\.evidencePreview\.join/); - assert.match(pageSource, /theme\.claimBoundary/); + assert.match(pageSource, /starterSuggestions\.map/); + assert.match(pageSource, /themes\.find\(\(candidate\) => candidate\.id === item\.theme\)/); + assert.match(pageSource, /chooseSuggestedQuestion\(item\.text, item\.theme\)/); assert.match(guidedTopicsSource, /strictWorkflowRoute/); assert.match(guidedTopicsSource, /evidencePreview/); assert.match(guidedTopicsSource, /confidenceCap/); @@ -73,13 +74,15 @@ test("keeps session history clickable while another session is answering", () => assert.doesNotMatch(appSidebarSource, /pendingSession|isLoading|cancellationPending|requestPending/); }); -test("uses the compact model popup width", () => { +test("sizes the model popup to its content with responsive bounds", () => { // Given: the model selector popup styles. const popupStyles = sourceBetween(globalStyles, ".model-selector-popup {", "}\n.model-selector-popup[data-starting-style]"); // When: its responsive width is inspected. - // Then: the desktop cap is half of the former 360px width. - assert.match(popupStyles, /width:\s*min\(180px,/); + // Then: short labels stay compact while long content remains viewport-safe. + assert.match(popupStyles, /width:\s*max-content/); + assert.match(popupStyles, /min-width:\s*180px/); + assert.match(popupStyles, /max-width:\s*min\(420px,/); }); test("centers the credit value with its icon", () => {