Files
Jyotisha/frontend/tests/session-conversation-layout.test.ts
T
jesse-ux e4e73f56c0
Independent Staging Quality Gate / publish (push) Canceled after 0s
Independent Staging Quality Gate / validate (push) Canceled after 9m33s
fix(web): 四个页面共用一份会话列表,空会话不入列
对话、星盘、星历、报告进同一 (app) 外壳,列表只拉一次。服务端不再列出空咨询;新建复用已有空会话。新标题改成「生时校正 · M月D日」,侧栏副标题用创建时间。
2026-09-17 21:27:40 +08:00

62 lines
4.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import test from "node:test";
const readProjectFile = (path: string) => readFileSync(new URL(`../${path}`, import.meta.url), "utf8");
const globalStyles = readProjectFile("src/app/globals.css");
const messageRowSource = readProjectFile("src/components/chat-message-row.tsx");
test("aligns the session transcript and composer to one readable column", () => {
assert.match(globalStyles, /--session-column-width:\s*760px/);
assert.match(globalStyles, /\.conversation:not\(\.is-empty\):not\(\.is-rectification\) \.message-list\s*\{[^}]*width:\s*min\(calc\(var\(--session-column-width\)/);
assert.match(globalStyles, /\.conversation:not\(\.is-empty\):not\(\.is-rectification\) \+ \.composer-wrap \.composer[^\{]*\{[^}]*width:\s*min\(var\(--session-column-width\),\s*100%\)/);
// 原值:`.composer-footer` 与 `.composer` 同宽 / 新值:只剩 `.composer`(上一条已断言)
// / 原因:常驻底栏已删,工具行移进输入框内部,不再有第二个需要对齐的盒子。
assert.doesNotMatch(globalStyles, /\.composer-footer\s*\{/);
});
test("keeps onboarding transcript and intake card on the same session column", () => {
const pageSource = readProjectFile("src/app/(app)/page.tsx");
assert.match(pageSource, /\$\{!profileComplete \? " is-onboarding" : ""\}/);
assert.match(globalStyles, /\.conversation\.is-onboarding \.welcome \{[\s\S]*width:\s*min\(calc\(var\(--session-column-width\) \+ \(var\(--session-column-gutter\) \* 2\)\), 100%\)/);
assert.match(globalStyles, /\.conversation\.is-onboarding\.is-empty \.welcome \{[\s\S]*width:\s*min\(var\(--session-column-width\), 100%\)/);
assert.match(globalStyles, /\.conversation\.is-onboarding \.onboarding-card,\s*\n\.conversation\.is-onboarding \.onboarding-card\.birth-time-transition-card \{[\s\S]*width:\s*100%;[\s\S]*max-width:\s*none/);
assert.match(globalStyles, /\.conversation\.is-onboarding-form \.welcome \{[\s\S]*padding:\s*var\(--space-6\) var\(--session-column-gutter\) var\(--space-8\)/);
});
test("centers the starter home even when the conversation grid is not is-empty", () => {
const pageSource = readProjectFile("src/app/(app)/page.tsx");
// 原值:空态 `.welcome` 与 `.starter-list` 都是 1040px
// 新值:`.welcome` 收到 `--session-column-width``.starter-list` 已删
// 原因:空态输入框此前 1040px、会话态 760px,首条消息落地时输入框会当场变窄一截。
// 现在两态同宽——这比原断言更强:它顺带锁住了「不跳宽度」。
assert.match(globalStyles, /\.welcome \{\s*width:\s*min\(var\(--session-column-width\), 100%\);\s*margin-inline:\s*auto;/);
assert.match(globalStyles, /\.composer-wrap-starter \.composer \{\s*width:\s*min\(var\(--session-column-width\), 100%\);/);
assert.doesNotMatch(globalStyles, /\.starter-list/);
assert.match(
pageSource,
/!onboardingFormActive && !activeSession\?\.messages\.length && \(Boolean\(activeSession\?\.messagesHydrated\) \|\| activeSession\?\.sessionType !== "consultation"\) \? " is-empty"/,
);
});
test("keeps message motion restrained and honors reduced-motion preferences", () => {
assert.match(messageRowSource, /gsap\.matchMedia\(\)/);
assert.match(messageRowSource, /prefers-reduced-motion:\s*no-preference/);
// Former value: `duration:\s*0\.18`. DESIGN.md §6 gives the message entrance 160ms; 0.18 was
// the GSAP copy drifting from the (now removed) 160ms CSS keyframe that ran alongside it.
assert.match(messageRowSource, /duration:\s*0\.16/);
assert.match(messageRowSource, /ease:\s*"cubic-bezier\(\.22,\s*1,\s*\.36,\s*1\)"/);
assert.match(messageRowSource, /clearProps:\s*"opacity,transform,visibility"/);
});
test("lets model controls fit their labels and keeps the stop action on-brand", () => {
assert.match(globalStyles, /\.model-selector-trigger\s*\{[^}]*width:\s*fit-content/);
assert.match(globalStyles, /\.model-selector-popup\s*\{[^}]*width:\s*max-content[^}]*min-width:\s*180px[^}]*max-width:\s*min\(420px,/);
assert.match(globalStyles, /\.model-selector-copy b\s*\{[^}]*overflow:\s*visible[^}]*white-space:\s*normal/);
assert.doesNotMatch(globalStyles, /\.model-selector-copy b\s*\{[^}]*text-overflow:\s*ellipsis/);
// 原值 `var(--color-action)` / 新值 `var(--color-action-strong)` / 原因:停止键是填充面,
// 走品牌 coral 那一阶;文字阶留给 --color-action。见 BUG-738。
assert.match(globalStyles, /\.composer \.composer-stop\s*\{[^}]*background:\s*var\(--color-action-strong\)/);
assert.match(globalStyles, /\.composer \.composer-stop:not\(:disabled\):hover\s*\{[^}]*background:\s*var\(--color-action-hover\)/);
});