对话、星盘、星历、报告进同一 (app) 外壳,列表只拉一次。服务端不再列出空咨询;新建复用已有空会话。新标题改成「生时校正 · M月D日」,侧栏副标题用创建时间。
62 lines
4.6 KiB
TypeScript
62 lines
4.6 KiB
TypeScript
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\)/);
|
||
});
|