Files
Jyotisha/frontend/tests/loading-motion-20260930.test.tsx
T

59 lines
3.8 KiB
TypeScript

// 2026-09-30 loading round: the chart wait lights houses up in order, the people
// archive uses the shared orbit, and today's line enters softly (BUG-1125).
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import test from "node:test";
import React from "react";
import { renderToStaticMarkup } from "react-dom/server";
import { ChartPageSkeleton } from "../src/components/chart-page/chart-page-skeleton.tsx";
import { EMPTY_NORTH_INDIAN_CHART, VedicChartSvg } from "../src/components/personal-report/vedic-chart-svg.tsx";
import { StarterGreeting } from "../src/components/starter-home.tsx";
import { cssDeclarations } from "./css-contract-test-support.ts";
Object.assign(globalThis, { React });
const css = readFileSync(new URL("../src/app/globals.css", import.meta.url), "utf8");
const read = (path: string) => readFileSync(new URL(path, import.meta.url), "utf8");
test("the Vedic wait numbers its twelve houses in house order; the finished chart carries no index", () => {
const waiting = renderToStaticMarkup(<ChartPageSkeleton copy="虚构的等待句" />);
const indexes = [...waiting.matchAll(/--house-index:\s*(\d+)/g)].map((match) => Number(match[1]));
assert.deepEqual(indexes, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]);
assert.doesNotMatch(waiting, /app-loading-orbit|app-loading-mark/, "no orbit logo over the chart");
assert.match(waiting, /role="status"/);
assert.match(waiting, /<span class="sr-only">虚构的等待句<\/span>/);
const finished = renderToStaticMarkup(<VedicChartSvg chart={EMPTY_NORTH_INDIAN_CHART} ariaLabel="x" />);
assert.doesNotMatch(finished, /--house-index/);
});
test("the western wait draws twelve sectors that light up around the wheel", () => {
const waiting = renderToStaticMarkup(<ChartPageSkeleton copy="虚构的等待句" western />);
assert.equal((waiting.match(/class="chart-skeleton-sector"/g) ?? []).length, 12);
assert.deepEqual([...waiting.matchAll(/--house-index:\s*(\d+)/g)].map((match) => Number(match[1])), [...Array(12).keys()]);
});
test("the house glow is a fill-only keyframe; lines stay still", () => {
const start = css.indexOf("@keyframes chart-skeleton-house");
const keyframes = css.slice(start, css.indexOf("\n}", start) + 2);
assert.match(keyframes, /fill-opacity: 0\.18/);
assert.doesNotMatch(keyframes, /(^|[^-])opacity:|transform|stroke/);
assert.match(cssDeclarations(".chart-page-skeleton .chart-page-western-spoke", css), /stroke-opacity: 0\.55/);
});
test("people archive and the picker wait with the shared orbit, sentence for screen readers only", () => {
const people = read("../src/components/people/people-page.tsx");
assert.match(people, /<SecondaryPageShell title="星盘档案" waiting=\{status === "loading" \? "人物列表还在读" : null\}>/);
assert.doesNotMatch(people, /<p className="secondary-page-waiting">/);
const picker = read("../src/components/subject-switcher.tsx");
assert.match(picker, /<OrbitWaiting className="chat-profile-picker-waiting" label="人物列表还在读" \/>/);
});
test("today's line fades in (240ms, 4px) and is keyed on its text; reduced motion is instant (BUG-1125)", () => {
const markup = renderToStaticMarkup(<StarterGreeting starterGreeting={{ text: "你好" }} dailyTrend="虚构的一句。" />);
assert.match(markup, /<p class="starter-greeting-sub">虚构的一句。<\/p>/);
assert.match(read("../src/components/starter-home.tsx"), /<p key=\{dailyTrend\} className="starter-greeting-sub"/);
assert.match(css, /\.starter-greeting-sub \{ animation: starter-greeting-sub-in 240ms var\(--ease-out\) both; \}/);
assert.match(css, /@keyframes starter-greeting-sub-in \{\s*from \{ opacity: 0; transform: translateY\(4px\); \}/);
assert.match(css, /@media \(prefers-reduced-motion: reduce\) \{\s*\.starter-greeting-sub \{ animation: none; \}/);
});