Files
Jyotisha/frontend/tests/birth-sky-sentence.test.ts
T
Jesse_ChenandClaude Opus 5.5 d277cbd8ad feat(chart): 「那一刻的天空」封面 — engine, BFF, canvas painter, chart header entry
T1 scripts/birth_sky.py: real alt/az of the seven visible grahas (swe.calc_ut +
swe.azalt, geometric altitude, azimuth converted from swe's south origin to
north-east) and Yale BSC5 stars V<=4.0 (IAU 1976 precession); phase
day / twilight_morning / twilight_evening / night. Thin POST /api/birth_sky
registration in do_POST (no new handler method, no __new__).

T2 GET /api/birth-sky?subject=: birth truth read server side through
loadSubjectBirth + prepareChartViewProfile; zod contract; no billing, no
writes, no birth data in the response.

T3/T4 lib/birth-sky: sentence table, pure Canvas 2D drawBirthSky (night /
paper palettes from DESIGN tokens, east on the left, serif headline with
1.5 s font wait), PNG export via navigator.share or Blob + a.download.
Chart page prefetches and paints the cover once the chart is ok; the header
button appears only when the PNG is ready. Dialog is a lazy chunk.

T7 DESIGN §17, VOICE, CONTEXT glossary, CHANGELOG, real-device checklist.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0199rbQDTsUbCVw84wc8BTFe
2026-09-28 20:27:40 +08:00

74 lines
3.2 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 test from "node:test";
import {
BIRTH_SKY_HEADLINES,
birthSkyFactLine,
birthSkyFootnote,
birthSkyHeadline,
octantName,
} from "../src/lib/birth-sky/sentence.ts";
import type { BirthSkyOk } from "../src/lib/birth-sky/contract.ts";
import { birthSkyGolden, goldenSky } from "./birth-sky-test-support.ts";
test("the fictional sample (07:40) reads exactly as the brief", () => {
const sky = goldenSky("day");
assert.equal(birthSkyGolden.times.day, "07:40");
assert.equal(birthSkyHeadline(sky), "星星都在,只是还看不见。");
assert.equal(birthSkyFactLine(sky), "太阳在东,月亮在脚下,火星正悬在东南");
});
test("each phase takes its one headline from the four-line table", () => {
assert.deepEqual(Object.values(BIRTH_SKY_HEADLINES), [
"星星都在,只是还看不见。",
"天快亮了,星星正一颗颗退场。",
"天刚暗下来,星星正一颗颗出来。",
"那一刻,头顶就是这片天。",
]);
assert.equal(birthSkyHeadline(goldenSky("day")), BIRTH_SKY_HEADLINES.day);
assert.equal(birthSkyHeadline(goldenSky("twilight_morning")), "天快亮了,星星正一颗颗退场。");
assert.equal(birthSkyHeadline(goldenSky("twilight_evening")), "天刚暗下来,星星正一颗颗出来。");
assert.equal(birthSkyHeadline(goldenSky("night")), "那一刻,头顶就是这片天。");
});
test("outside daytime the fact line never names the Sun", () => {
for (const phase of ["twilight_morning", "twilight_evening", "night"] as const) {
const line = birthSkyFactLine(goldenSky(phase));
assert.doesNotMatch(line, /太阳/);
assert.match(line, /^月亮在(脚下|北|东北|东|东南|南|西南|西|西北)/);
assert.ok(line.split(",").length <= 2, line);
}
});
test("fact line combinations: moon above, no visible planet, highest planet wins", () => {
const base = goldenSky("night");
const planets = (overrides: Record<string, [number, number]>): BirthSkyOk["planets"] => base.planets.map((planet) => {
const next = overrides[planet.id];
return next ? { ...planet, altitude: next[0], azimuth: next[1] } : { ...planet, altitude: -10 };
});
assert.equal(
birthSkyFactLine({ phase: "night", planets: planets({ moon: [40, 225], jupiter: [20, 10], venus: [35, 300] }) }),
"月亮在西南,金星正悬在西北",
);
assert.equal(birthSkyFactLine({ phase: "night", planets: planets({}) }), "月亮在脚下");
assert.equal(
birthSkyFactLine({ phase: "day", planets: planets({ sun: [10, 225], saturn: [5, 170] }) }),
"太阳在西南,月亮在脚下,土星正悬在南",
);
});
test("eight-point compass rounds to the nearest 45 degrees, north at both ends", () => {
assert.equal(octantName(0), "北");
assert.equal(octantName(22.4), "北");
assert.equal(octantName(22.6), "东北");
assert.equal(octantName(84.69), "东");
assert.equal(octantName(117.24), "东南");
assert.equal(octantName(191.14), "南");
assert.equal(octantName(350), "北");
});
test("the reported-time footnote appears only when the time was not adopted", () => {
assert.equal(birthSkyFootnote({ timeBasis: "reported" }), "按你填写的时间");
assert.equal(birthSkyFootnote({ timeBasis: "adopted" }), null);
});