test: restore production release gate

This commit is contained in:
Jesse_Chen
2026-07-22 18:41:25 +08:00
parent 79c7e90101
commit 48dead992f
7 changed files with 32 additions and 13 deletions
+16
View File
@@ -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
+1 -1
View File
@@ -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";
@@ -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\)/);
});
@@ -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, /<ConversationalBirthTimeRectification/);
assert.doesNotMatch(pageSource, /当前使用候选时间排盘/);
});
+1 -1
View File
@@ -65,7 +65,7 @@ test("keeps the suggestion row height stable while an answer streams", () => {
});
test("docks the composer inside the chat panel instead of floating over content", () => {
assert.match(pageSource, /<div className="composer-wrap">/);
assert.match(pageSource, /<div className=\{`composer-wrap \$\{starterHomeVisible \? "composer-wrap-starter" : ""\}`\}>/);
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/);
@@ -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, />使用新确认时间继续回答原问题</);
assert.doesNotMatch(markup, /原问题:未来半年是否适合换工作?/);
assert.match(markup, />返回原问题</);
assert.equal(continuationCalls, 0);
});
@@ -95,7 +95,7 @@ test("continuation action is visibly locked while ordinary consultation is pendi
},
));
assert.match(markup, /<button[^>]+disabled=""[^>]*>正在继续回答原问题…<\/button>/);
assert.match(markup, /<button[^>]+disabled=""[^>]*>正在继续回答…<\/button>/);
});
test("choosing rectify-first captures the visible question and passes it only to v3 start", async () => {
+8 -5
View File
@@ -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", () => {