Files
Jyotisha/frontend/tests/session-conversation-layout.test.ts
T
Jesse_Chen 0fd111d16b
Independent Staging Quality Gate / validate (push) Successful in 13m6s
Independent Staging Quality Gate / publish (push) Successful in 8m3s
fix(ui): align message motion and composer button radius with DESIGN.md; tolerate unreadable staging revision state
- Message entry: 300/420ms power3.out -> Standard 180ms cubic-bezier(.22,1,.36,1) (DESIGN.md section 6); update the layout contract test to the shipped values.
- Composer icon button: full radius instead of 8px (DESIGN.md section 5 Button).
- Staging deploy workflow: fall back to container SHA discovery when .state/deployed-revision exists but is unreadable by the deploy user (Gitea pipeline writes it as root).
2026-08-14 09:13:57 +08:00

32 lines
2.2 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%\)/);
assert.match(globalStyles, /\.conversation:not\(\.is-empty\):not\(\.is-rectification\) \+ \.composer-wrap \.composer-suggestions[^\{]*[\s\S]*?width:\s*min\(var\(--session-column-width\),\s*100%\)/);
});
test("keeps message motion restrained and honors reduced-motion preferences", () => {
assert.match(messageRowSource, /gsap\.matchMedia\(\)/);
assert.match(messageRowSource, /prefers-reduced-motion:\s*no-preference/);
assert.match(messageRowSource, /duration:\s*0\.18/);
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/);
assert.match(globalStyles, /\.composer \.composer-stop\s*\{[^}]*background:\s*var\(--color-action\)/);
assert.match(globalStyles, /\.composer \.composer-stop:not\(:disabled\):hover\s*\{[^}]*background:\s*var\(--color-action-hover\)/);
});