Files
Jyotisha/frontend/tests/chart-glyph-motion.test.ts
T
jesse-uxandGrok (agent loop) b7ec8e78ed
Independent Staging Quality Gate / validate (push) Failing after 9m20s
Independent Staging Quality Gate / publish (push) Skipped
星盘页换成组件库:页签、盘型条、可拖分栏和换盘动效(#107)。
四个页签用下划线,盘型条用分段并可以横滑,全部盘型从底部打开。桌面上盘和参数可以拖宽,窄屏上下排。行星表仍跨满整行。换盘型时星体移到新位置;减少动态效果只淡入。第一次进场不重播。

自测:tsc 退出码 0;frontend 下 npm run lint 0 error(122 警告);定向测试 188 通过、0 失败、0 取消;npm run build 后 / 仍是 Static;首屏 JS gzip-9 为 640131,相对 d25dfdbe 记下的 639127 多 0.16%。没有跑全量 npm test、Python 快速门和 test:db。

Co-Authored-By: Grok (agent loop)
2026-10-25 04:02:14 +08:00

92 lines
3.7 KiB
TypeScript

import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import React from "react";
import { renderToStaticMarkup } from "react-dom/server";
import test from "node:test";
import { ChartVedicTab } from "../src/components/chart-page/chart-vedic-tab.tsx";
import { glyphMotionKey, planGlyphMotion, type GlyphPoint } from "../src/components/chart-page/chart-glyph-motion.ts";
import { VedicChartSvg } from "../src/components/personal-report/vedic-chart-svg.tsx";
import { assembleChartView } from "../src/lib/chart-view-load.ts";
import type { ChartViewOk } from "../src/lib/chart-view-contract.ts";
Object.assign(globalThis, { React });
const golden = JSON.parse(readFileSync(new URL("./fixtures/chart-view-golden.json", import.meta.url), "utf8"));
const profile = {
name: "示例",
date: "1990-06-15",
time: "12:00",
placeLabel: "北京",
latitude: 39.9042,
longitude: 116.4074,
timezoneOffset: 8,
timezoneId: "Asia/Shanghai",
ayanamsa: "raman",
birthTimeStatus: "confirmed",
};
function glyphPoints(markup: string): Record<string, GlyphPoint> {
const points: Record<string, GlyphPoint> = {};
for (const match of markup.matchAll(/<text\b([^>]*)>([^<]*)</g)) {
const attrs = match[1] ?? "";
const className = attrs.match(/class="([^"]*)"/)?.[1] ?? "";
const tokens = className.split(/\s+/);
const kind = tokens.includes("personal-report-chart-asc")
? "asc"
: tokens.includes("personal-report-chart-glyph")
? "glyph"
: null;
if (!kind) continue;
points[glyphMotionKey(kind, match[2] ?? "")] = {
x: Number(attrs.match(/\bx="([^"]+)"/)?.[1]),
y: Number(attrs.match(/\by="([^"]+)"/)?.[1]),
};
}
return points;
}
async function view(): Promise<ChartViewOk> {
const result = await assembleChartView({
userId: "user-1",
profile,
asOf: "2026-09-15",
layers: ["varga"],
postEngine: async (path) => {
if (path === "/api/chart") return { status: "ok", payload: golden.chart };
if (path === "/api/varga_full") return { status: "ok", payload: golden.varga_full };
return { status: "http_error", path, elapsedMs: 1, httpStatus: 500 };
},
});
assert.equal(result.body.status, "ok");
return result.body as ChartViewOk;
}
test("switching chart type ends on the same glyph coordinates as a direct render", async () => {
const chart = await view();
const d1 = renderToStaticMarkup(React.createElement(ChartVedicTab, { view: chart, vargaId: "D1", onSelectVarga: () => {} }));
const d9 = renderToStaticMarkup(React.createElement(ChartVedicTab, { view: chart, vargaId: "D9", onSelectVarga: () => {} }));
const from = glyphPoints(d1);
const next = glyphPoints(d9);
const varga = chart.vedic.vargas.find((item) => item.id === "D9");
assert.ok(varga);
const direct = glyphPoints(renderToStaticMarkup(React.createElement(VedicChartSvg, {
chart: varga.chart,
ariaLabel: `${varga.id} 北印度盘 ${varga.meaning}`,
interactive: true,
})));
assert.deepEqual(next, direct);
const moved = planGlyphMotion(from, next, { firstPaint: false, reduced: false, chartTypeChanged: true });
assert.ok(moved.moves.length > 0);
for (const move of moved.moves) {
assert.deepEqual(move.to, next[move.key]);
assert.notDeepEqual(move.from, move.to);
}
assert.deepEqual(planGlyphMotion(from, next, { firstPaint: true, reduced: false, chartTypeChanged: true }).moves, []);
const reduced = planGlyphMotion(from, next, { firstPaint: false, reduced: true, chartTypeChanged: true });
assert.deepEqual(reduced.moves, []);
assert.equal(reduced.fadeOnly, true);
assert.equal(planGlyphMotion(null, next, { firstPaint: false, reduced: false, chartTypeChanged: false }).fadeOnly, false);
});