diff --git a/frontend/src/app/globals.css b/frontend/src/app/globals.css index da18261d..09e1215a 100644 --- a/frontend/src/app/globals.css +++ b/frontend/src/app/globals.css @@ -4959,7 +4959,6 @@ input:not([type="radio"]):not([type="checkbox"]):not([class^="ant-"]):not([class .chart-page-basics, .chart-page-dasha, .chart-page-western, -.chart-page-qizheng, .chart-page-western-side { min-width: 0; } @@ -5350,32 +5349,6 @@ input:not([type="radio"]):not([type="checkbox"]):not([class^="ant-"]):not([class .chart-page-western-table tbody tr[tabindex] { cursor: pointer; } .chart-page-western-table tbody tr[tabindex]:hover { background: var(--color-canvas-muted); } .chart-page-western-table tr.is-selected { background: var(--color-selected); } -.chart-page-qizheng-grid { - display: grid; - grid-template-columns: repeat(4, minmax(0, 1fr)); - gap: var(--space-2); - margin: var(--space-4) 0; -} -.chart-page-qizheng-cell { - min-height: 88px; - padding: var(--space-3); - border: 1px solid var(--color-border); - border-radius: var(--radius-sm); - background: var(--color-canvas); - font-size: var(--type-body-sm); -} -.chart-page-qizheng-cell strong { display: block; font-size: var(--type-title-sm); font-weight: 500; } -.chart-page-qizheng-cell span, -.chart-page-qizheng-cell small, -.chart-page-qizheng-cell p { display: block; margin: var(--space-1) 0 0; color: var(--color-ink-secondary); } -.chart-page-qizheng-cell small { font-size: var(--type-caption); } -.chart-page-qizheng-hole { min-height: 0; } -.chart-page-qizheng-rules { - margin: var(--space-3) 0; - padding-left: 1.2em; - color: var(--color-ink-secondary); - font-size: var(--type-body-sm); -} .chart-page-unavailable > p { margin: 0 0 var(--space-3); font-size: var(--type-body-md); } @media (min-width: 768px) { diff --git a/frontend/src/components/chart-page/chart-page-view.tsx b/frontend/src/components/chart-page/chart-page-view.tsx index 262bf55e..cfbd5361 100644 --- a/frontend/src/components/chart-page/chart-page-view.tsx +++ b/frontend/src/components/chart-page/chart-page-view.tsx @@ -17,7 +17,6 @@ import { chartLayerFailureCopy } from "@/lib/chart-view-failure"; import { ChartPageSkeleton } from "./chart-page-skeleton"; import { ChartBasicsTab } from "./chart-basics-tab"; import { ChartDashaTab } from "./chart-dasha-tab"; -import { ChartQizhengTab } from "./chart-qizheng-tab"; import { ChartVedicTab } from "./chart-vedic-tab"; import { ChartWesternTab } from "./chart-western-tab"; @@ -70,7 +69,6 @@ export function ChartPageView({ setTab(id); if (id === "dasha") onNeedLayer?.("chara"); if (id === "western") onNeedLayer?.("western"); - if (id === "qizheng") onNeedLayer?.("qizheng"); } function selectVarga(id: string) { @@ -136,15 +134,6 @@ export function ChartPageView({ ) ) : null} - {tab === "qizheng" ? ( - pendingLayers?.has("qizheng") ? ( - - ) : layerFailures?.has("qizheng") ? ( - - ) : ( - - ) - ) : null} } diff --git a/frontend/src/components/chart-page/chart-qizheng-tab.tsx b/frontend/src/components/chart-page/chart-qizheng-tab.tsx deleted file mode 100644 index c4bde825..00000000 --- a/frontend/src/components/chart-page/chart-qizheng-tab.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import type { ChartViewOk } from "@/lib/chart-view-contract"; -import { COORDINATE_BOUNDARY, QIZHENG_RULES } from "@/lib/chart-view-labels"; -import { QizhengPalaceGrid } from "./qizheng-palace-grid"; - -function Rules() { - return ( -
    - {QIZHENG_RULES.map((rule) =>
  • {rule}
  • )} -
- ); -} - -export function ChartQizhengTab({ view }: { view: ChartViewOk }) { - const qizheng = view.qizheng; - if (qizheng.status === "unavailable") { - return ( -
-

{COORDINATE_BOUNDARY.qizheng}

-

{qizheng.copy}

- -
- ); - } - return ( -
-

{qizheng.boundary}

-

计都派别:{qizheng.ketuMode} · 坐标系 {qizheng.coordinateSystem}

- - -
- - - - - - - - - - - - - {qizheng.bodies.map((body) => ( - - - - - - - - ))} - -
十一曜宿度
曜宿宿度宫庙旺
{body.name}{body.mansion}{body.mansionDegree.toFixed(2)}{body.palace}{body.dignityClosed ? body.dignity : `${body.dignity}(未闭合)`}
-
-
- ); -} diff --git a/frontend/src/components/chart-page/qizheng-palace-grid.tsx b/frontend/src/components/chart-page/qizheng-palace-grid.tsx deleted file mode 100644 index c8af345a..00000000 --- a/frontend/src/components/chart-page/qizheng-palace-grid.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import type { ChartViewQizhengOk } from "@/lib/chart-view-contract"; -import { QIZHENG_BRANCH_GRID } from "@/lib/chart-view-labels"; - -export function QizhengPalaceGrid({ qizheng }: { qizheng: ChartViewQizhengOk }) { - const byBranch = new Map(qizheng.palaces.map((palace) => [palace.branch, palace])); - return ( -
- {QIZHENG_BRANCH_GRID.flatMap((row, rowIndex) => row.map((branch, columnIndex) => { - if (!branch) { - return
; - } - const palace = byBranch.get(branch); - return ( -
- {branch} - {palace?.lifePalace ?? "—"} - {palace?.mansions.length ? palace.mansions.join(" ") : "所辖宿未返回"} -

{palace?.occupants.length ? palace.occupants.join(" ") : "空"}

-
- ); - }))} -
- ); -} diff --git a/frontend/src/hooks/use-chart-page.ts b/frontend/src/hooks/use-chart-page.ts index 8933f000..dbecf070 100644 --- a/frontend/src/hooks/use-chart-page.ts +++ b/frontend/src/hooks/use-chart-page.ts @@ -48,7 +48,6 @@ function mergeLayer(current: ChartViewOk, incoming: ChartViewOk, layer: ChartVie ...(layer === "varga" ? { vedic: incoming.vedic } : {}), ...(layer === "chara" ? { dasha: incoming.dasha } : {}), ...(layer === "western" ? { western: incoming.western } : {}), - ...(layer === "qizheng" ? { qizheng: incoming.qizheng } : {}), }; } diff --git a/frontend/src/lib/chart-view-contract.ts b/frontend/src/lib/chart-view-contract.ts index daa1f427..18824e6c 100644 --- a/frontend/src/lib/chart-view-contract.ts +++ b/frontend/src/lib/chart-view-contract.ts @@ -145,27 +145,6 @@ export const chartViewWesternOkSchema = z.object({ })), }); -export const chartViewQizhengOkSchema = z.object({ - status: z.literal("ok"), - coordinateSystem: z.string().min(1), - ketuMode: z.string().min(1), - boundary: z.string().min(1), - palaces: z.array(z.object({ - branch: z.string().min(1), - lifePalace: z.string().min(1), - mansions: z.array(z.string()), - occupants: z.array(z.string()), - })), - bodies: z.array(z.object({ - name: z.string().min(1), - mansion: z.string().min(1), - mansionDegree: z.number(), - palace: z.string().min(1), - dignity: z.string().min(1), - dignityClosed: z.boolean(), - })), -}); - export const chartViewProfileSchema = z.object({ name: z.string().min(1), date: isoDate, @@ -200,7 +179,6 @@ export const chartViewOkSchema = z.object({ varga: chartViewFailureStatusSchema.optional(), chara: chartViewFailureStatusSchema.optional(), western: chartViewFailureStatusSchema.optional(), - qizheng: chartViewFailureStatusSchema.optional(), }).optional(), status: z.literal("ok"), billed: z.literal(false), @@ -221,7 +199,6 @@ export const chartViewOkSchema = z.object({ dualTrackNote: z.string().min(1), }), western: z.union([chartViewWesternOkSchema, chartViewUnavailableLayerSchema]), - qizheng: z.union([chartViewQizhengOkSchema, chartViewUnavailableLayerSchema]), accountId: z.string().min(1).optional(), profileFingerprint: z.string().min(1).optional(), subjectId: z.string().min(1).optional(), @@ -248,7 +225,6 @@ export type ChartViewMessage = z.infer; export type ChartViewResponse = z.infer; export type ChartViewUnavailableLayer = z.infer; export type ChartViewWesternOk = z.infer; -export type ChartViewQizhengOk = z.infer; export type ChartViewVarga = z.infer; export type ChartViewPlanetRow = z.infer; export type NorthIndianChart = z.infer; @@ -259,7 +235,6 @@ export const CHART_VIEW_TABS = [ { id: "basics", label: "基础信息" }, { id: "dasha", label: "大运" }, { id: "western", label: "西洋盘" }, - { id: "qizheng", label: "七政四余" }, ] as const; export type ChartViewTabId = (typeof CHART_VIEW_TABS)[number]["id"]; diff --git a/frontend/src/lib/chart-view-engine.ts b/frontend/src/lib/chart-view-engine.ts index 996631a5..407f48a0 100644 --- a/frontend/src/lib/chart-view-engine.ts +++ b/frontend/src/lib/chart-view-engine.ts @@ -2,7 +2,7 @@ export const CHART_VIEW_ENGINE_TIMEOUT_MS = 10_000; export const CHART_VIEW_ENGINE_CACHE_TTL_MS = 5 * 60 * 1_000; const ENGINE_CACHE_MAX_ENTRIES = 200; -export const CHART_VIEW_LAYERS = ["varga", "chara", "western", "qizheng"] as const; +export const CHART_VIEW_LAYERS = ["varga", "chara", "western"] as const; export type ChartViewLayer = (typeof CHART_VIEW_LAYERS)[number]; export type EngineCallResult = diff --git a/frontend/src/lib/chart-view-labels.ts b/frontend/src/lib/chart-view-labels.ts index e280db4b..9b1cb6eb 100644 --- a/frontend/src/lib/chart-view-labels.ts +++ b/frontend/src/lib/chart-view-labels.ts @@ -121,17 +121,9 @@ export const MODE_ZH: Record = { mutable: "变动", }; -export const QIZHENG_BRANCH_GRID: ReadonlyArray = [ - ["巳", "午", "未", "申"], - ["辰", null, null, "酉"], - ["卯", null, null, "戌"], - ["寅", "丑", "子", "亥"], -]; - export const COORDINATE_BOUNDARY = { - vedic: "这是恒星黄道,岁差用你资料里选的那一套。它和西洋回归黄道、七政宿度不是同一套坐标,不能换算,也不能叠成双重印证。", - western: "这是回归黄道,宫位用 Placidus。它和印度盘的恒星黄经、七政宿度不是同一套,不能换算,也不能叠成双重印证。", - qizheng: "宿度从角宿初度起算,不是恒星黄经,也不是回归黄道。三套不能换算,也不能叠成双重印证。", + vedic: "这是恒星黄道,岁差用你资料里选的那一套。它和西洋回归黄道不是同一套坐标,不能换算,也不能叠成双重印证。", + western: "这是回归黄道,宫位用 Placidus。它和印度盘的恒星黄经不是同一套,不能换算,也不能叠成双重印证。", } as const; export const DASHA_DUAL_TRACK_NOTE = "只有两条同时指向同一段时间才算证据,单轨命中要降一级来看。"; @@ -146,14 +138,6 @@ export const CHARA_ZERO_YEAR_NOTE = "本段时长为 0,不展开"; export const WESTERN_UNAVAILABLE_COPY = "西洋盘这一栏要等回归黄道端点上线。现在还没有,先空着——不会拿印度盘的度数改写成回归黄道。"; -export const QIZHENG_UNAVAILABLE_COPY = "七政四余这一栏要等宿度端点上线。现在还没有,先空着。"; - -export const QIZHENG_RULES = [ - "宿度从角宿初度起算,不是恒星黄经。", - "计都派别(是否取罗睺对点)会改整张盘,必须写明,不能静默默认。", - "庙旺表还没闭合,这一列只能当占位,不能拿来下判断。", -] as const; - export const CHART_VIEW_CLIENT_TIMEOUT_MS = 25_000; export const CHART_VIEW_COPY = { diff --git a/frontend/src/lib/chart-view-load.ts b/frontend/src/lib/chart-view-load.ts index 031f1219..5c398e44 100644 --- a/frontend/src/lib/chart-view-load.ts +++ b/frontend/src/lib/chart-view-load.ts @@ -157,7 +157,7 @@ export async function assembleChartView(input: ChartViewLoadInput): Promise<{ if (result.status !== "ok") layerFailures[layer] = engineFailureReason(result.status); return enginePayload(result); }; - const [varga, chara, western, qizheng] = await Promise.all([ + const [varga, chara, western] = await Promise.all([ layers.has("varga") ? callEngine(input.postEngine, "/api/varga_full", { ...followUp, @@ -174,9 +174,6 @@ export async function assembleChartView(input: ChartViewLoadInput): Promise<{ layers.has("western") ? callEngine(input.postEngine, "/api/western", payload).then(layerPayload("western")) : Promise.resolve(null), - layers.has("qizheng") - ? callEngine(input.postEngine, "/api/qizheng", payload).then(layerPayload("qizheng")) - : Promise.resolve(null), ]); try { @@ -185,7 +182,6 @@ export async function assembleChartView(input: ChartViewLoadInput): Promise<{ varga, chara, western, - qizheng, profile, asOf: input.asOf, }); diff --git a/frontend/src/lib/chart-view-mapper.ts b/frontend/src/lib/chart-view-mapper.ts index 52487d9a..c2b70b8d 100644 --- a/frontend/src/lib/chart-view-mapper.ts +++ b/frontend/src/lib/chart-view-mapper.ts @@ -8,7 +8,6 @@ import { COORDINATE_BOUNDARY, DASHA_DUAL_TRACK_NOTE, PLANET_ORDER, - QIZHENG_UNAVAILABLE_COPY, SIGN_ORDER, VARGA_CHIP_ORDER, VIMSHOTTARI_ORDER, @@ -26,7 +25,6 @@ import { traditionalSymbol } from "./chart-view-symbols.ts"; import { chartViewOkSchema, type ChartViewOk, - type ChartViewQizhengOk, type ChartViewUnavailableLayer, type ChartViewVarga, type ChartViewWesternOk, @@ -51,7 +49,6 @@ export type ChartViewEngineBundle = { varga: Record | null; chara: Record | null; western: Record | null; - qizheng: Record | null; profile: ChartViewProfileInput; asOf: string; }; @@ -467,10 +464,6 @@ function unavailableWestern(reason: string): ChartViewUnavailableLayer { return { status: "unavailable", reason, copy: WESTERN_UNAVAILABLE_COPY }; } -function unavailableQizheng(reason: string): ChartViewUnavailableLayer { - return { status: "unavailable", reason, copy: QIZHENG_UNAVAILABLE_COPY }; -} - function mapWestern(raw: Record | null, ayanamsaDisplay: string): ChartViewWesternOk | ChartViewUnavailableLayer { if (!raw) return unavailableWestern("endpoint_missing"); const natal = record(raw.natal) ?? raw; @@ -559,96 +552,8 @@ function palaceBranch(row: Record): string | null { return text(row.branch) ?? text(row.zhi) ?? text(row.earthly_branch) ?? text(row.name); } -function mapQizheng(raw: Record | null): ChartViewQizhengOk | ChartViewUnavailableLayer { - if (!raw) return unavailableQizheng("endpoint_missing"); - const palacesRaw = raw.palaces; - const bodiesRaw = raw.bodies ?? raw.planets; - const palaces: ChartViewQizhengOk["palaces"] = []; - if (Array.isArray(palacesRaw)) { - for (const entry of palacesRaw) { - const row = record(entry); - if (!row) continue; - const branch = palaceBranch(row); - if (!branch) continue; - palaces.push({ - branch, - lifePalace: text(row.life_palace) ?? text(row.lifePalace) ?? text(row.human_palace) ?? "—", - mansions: Array.isArray(row.mansions) - ? row.mansions.filter((item): item is string => typeof item === "string") - : text(row.mansions) - ? [String(row.mansions)] - : (text(row.xiu) ? [String(row.xiu)] : []), - occupants: Array.isArray(row.occupants) - ? row.occupants.filter((item): item is string => typeof item === "string") - : [], - }); - } - } else { - const asRecord = record(palacesRaw); - if (asRecord) { - for (const [branch, value] of Object.entries(asRecord)) { - const row = record(value) ?? {}; - palaces.push({ - branch, - lifePalace: text(row.life_palace) ?? text(row.lifePalace) ?? "—", - mansions: Array.isArray(row.mansions) - ? row.mansions.filter((item): item is string => typeof item === "string") - : [], - occupants: Array.isArray(row.occupants) - ? row.occupants.filter((item): item is string => typeof item === "string") - : [], - }); - } - } - } - const bodies: ChartViewQizhengOk["bodies"] = []; - if (Array.isArray(bodiesRaw)) { - for (const entry of bodiesRaw) { - const row = record(entry); - if (!row) continue; - const name = text(row.name) ?? text(row.body) ?? text(row.planet); - if (!name) continue; - bodies.push({ - name, - mansion: text(row.mansion) ?? text(row.xiu) ?? "—", - mansionDegree: finite(row.mansion_degree) ?? finite(row.siderealLon) ?? finite(row.degree) ?? 0, - palace: text(row.palace) ?? text(row.branch) ?? "—", - dignity: text(row.dignity) ?? text(row.status) ?? "未闭合", - dignityClosed: false, - }); - } - } else { - const asRecord = record(bodiesRaw); - if (asRecord) { - for (const [name, value] of Object.entries(asRecord)) { - const row = record(value) ?? {}; - bodies.push({ - name, - mansion: text(row.mansion) ?? text(row.xiu) ?? "—", - mansionDegree: finite(row.mansion_degree) ?? finite(row.siderealLon) ?? finite(row.degree) ?? 0, - palace: text(row.palace) ?? text(row.branch) ?? "—", - dignity: text(row.dignity) ?? "未闭合", - dignityClosed: false, - }); - } - } - } - if (palaces.length === 0 || bodies.length === 0) return unavailableQizheng("payload_incomplete"); - return { - status: "ok", - coordinateSystem: text(raw.coordinate_system) ?? "qizheng_mansion_degrees_from_jiao", - ketuMode: text(record(raw.calculation)?.engine_ketu_mode) - ?? text(record(raw.chart)?.ketu_mode) - ?? text(raw.ketuMode) - ?? "unspecified", - boundary: COORDINATE_BOUNDARY.qizheng, - palaces, - bodies, - }; -} - export function buildChartView(bundle: ChartViewEngineBundle): ChartViewOk { - const { chart, varga, chara, western, qizheng, profile, asOf } = bundle; + const { chart, varga, chara, western, profile, asOf } = bundle; const planets = asPlanetMap(chart.planets); const moon = planets.Moon ?? {}; const ascendant = record(chart.ascendant); @@ -701,7 +606,6 @@ export function buildChartView(bundle: ChartViewEngineBundle): ChartViewOk { dualTrackNote: DASHA_DUAL_TRACK_NOTE, }, western: mapWestern(western, ayanamsaDisplay), - qizheng: mapQizheng(qizheng), }; return chartViewOkSchema.parse(view); } diff --git a/frontend/src/lib/secondary-page-data.ts b/frontend/src/lib/secondary-page-data.ts index d8fcea8b..eeabe822 100644 --- a/frontend/src/lib/secondary-page-data.ts +++ b/frontend/src/lib/secondary-page-data.ts @@ -214,7 +214,6 @@ async function loadChartSnapshot(started: number, signal: AbortSignal): Promise< ...(prior.vedic.vargas.length > 1 ? { vedic: prior.vedic } : {}), dasha: { ...snapshot.view.dasha, ...(prior.dasha.chara.periods.length ? { chara: prior.dasha.chara } : {}) }, ...(prior.western.status === "ok" ? { western: prior.western } : {}), - ...(prior.qizheng.status === "ok" ? { qizheng: prior.qizheng } : {}), } }; } return rememberChartSnapshot(snapshot); diff --git a/frontend/tests/chart-page-hook.test.ts b/frontend/tests/chart-page-hook.test.ts index e9475420..77b9ab15 100644 --- a/frontend/tests/chart-page-hook.test.ts +++ b/frontend/tests/chart-page-hook.test.ts @@ -81,7 +81,8 @@ for (const cached of [false, true]) test(`chart 401 ${cached ? "cache" : "respon assert.equal(state().view?.status, "unauthenticated"); assert.deepEqual(assigned, ["/login"]); })); -for (const layer of ["varga", "chara", "western", "qizheng"] as const) test(`real assemble to fetch to hook preserves ${layer} engine 429`, async () => harness(async (h, state) => { +// 原值: 含 "qizheng";新值: 去掉;原因: 七政懒加载层已删(TASK-chart-types D1) +for (const layer of ["varga", "chara", "western"] as const) test(`real assemble to fetch to hook preserves ${layer} engine 429`, async () => harness(async (h, state) => { const initial = await packet(); const failed = await packet(layer, { status: "busy", path: "/api/synthetic", elapsedMs: 1, httpStatus: 429 }); globalThis.fetch = async (url) => response(String(url).includes("layers=") ? failed : initial); diff --git a/frontend/tests/chart-page-view.test.tsx b/frontend/tests/chart-page-view.test.tsx index a12313ff..291cf304 100644 --- a/frontend/tests/chart-page-view.test.tsx +++ b/frontend/tests/chart-page-view.test.tsx @@ -33,7 +33,6 @@ const globalStyles = readFileSync(new URL("../src/app/globals.css", import.meta. const sidebar = readFileSync(new URL("../src/components/app-sidebar.tsx", import.meta.url), "utf8"); const vedicTab = readFileSync(new URL("../src/components/chart-page/chart-vedic-tab.tsx", import.meta.url), "utf8"); const westernTab = readFileSync(new URL("../src/components/chart-page/chart-western-tab.tsx", import.meta.url), "utf8"); -const qizhengTab = readFileSync(new URL("../src/components/chart-page/chart-qizheng-tab.tsx", import.meta.url), "utf8"); /** Every selector that has at least one rule, comments stripped first. */ function declaredSelectors(source: string): Set { @@ -86,7 +85,7 @@ function withSidebarProvider(element: React.ReactElement) { return renderToStaticMarkup(React.createElement(SidebarProvider, null, element)); } -test("the five tabs render and can each be selected", async () => { +test("the four tabs render and can each be selected", async () => { const view = await okView(); for (const tab of CHART_VIEW_TABS) { const markup = withSidebarProvider(React.createElement(ChartPageView, { view, initialTab: tab.id })); @@ -99,7 +98,10 @@ test("the five tabs render and can each be selected", async () => { assert.match(markup, />基础信息大运西洋盘七政四余 { assert.match(markup, /只有两条同时指向同一段时间才算证据/); } if (tab.id === "western") assert.match(markup, /西洋盘这一栏要等回归黄道端点上线/); - if (tab.id === "qizheng") { - assert.match(markup, /七政四余这一栏要等宿度端点上线/); - assert.match(markup, /角宿/); - assert.match(markup, /计都派别/); - assert.match(markup, /庙旺/); - } } }); -test("unavailable western and qizheng tabs stay explanatory", async () => { +// 原值: 西洋与七政两栏各断言一次;新值: 只断言西洋;原因: 七政 Tab 已删(TASK-chart-types D1) +test("unavailable western tab stays explanatory", async () => { const view = await okView(); const western = withSidebarProvider(React.createElement(ChartPageView, { view, initialTab: "western" })); - const qizheng = withSidebarProvider(React.createElement(ChartPageView, { view, initialTab: "qizheng" })); assert.doesNotMatch(western, /报错|出错了|500/); - assert.doesNotMatch(qizheng, /报错|出错了|500/); assert.match(western, /不能换算/); - assert.match(qizheng, /不能换算/); }); test("a tropical packet draws the western wheel instead of the unavailable copy", async () => { @@ -232,7 +226,8 @@ test("the planet table scrolls inside its own box instead of widening the page", assert.match(cssDeclarations(".chart-page-planet-table-wrap", globalStyles), /min-width:\s*0/); assert.match(cssDeclarations(".chart-page-planet-table", globalStyles), /min-width:\s*\d/); // Every planet table is wrapped, not just the one on the 星盘 tab. - for (const source of [vedicTab, westernTab, qizhengTab]) { + // 原值: [vedicTab, westernTab, qizhengTab];新值: 去掉 qizhengTab;原因: 文件已删(TASK-chart-types D1) + for (const source of [vedicTab, westernTab]) { const tables = [...source.matchAll(//g)].length; const wraps = [...source.matchAll(/
/g)].length; assert.equal(wraps, tables); @@ -264,7 +259,9 @@ test("the chart page shell is visible before the natal chart arrives", () => { // 原因:2026-09-24 D1 仅授权星盘盘位骨架(BUG-1016)。 assert.match(markup, /data-skeleton="true"/); assert.match(markup, /aria-busy="true"/); - assert.equal((markup.match(/disabled=""/g) ?? []).length, 5); + // 原值: 5 个禁用 Tab;新值: 4 个(CHART_VIEW_TABS.length);原因: 七政 Tab 已删(TASK-chart-types D1) + assert.equal((markup.match(/disabled=""/g) ?? []).length, CHART_VIEW_TABS.length); + assert.equal(CHART_VIEW_TABS.length, 4); assert.doesNotMatch(markup, /正在加载|InlineSpinner|spinner|打开即有/i); assert.doesNotMatch(markup, /chart-page-eyebrow/); }); @@ -332,18 +329,20 @@ test("varga pending shows the empty shared chart and failure replaces it", async assert.match(render(true, "engine_busy"), /data-skeleton="true"/); }); -test("western qizheng and chara failures are terminal copy rather than waiting", async () => { +// 原值: western / qizheng / chara 三栏;新值: 去掉 qizheng;原因: 七政 Tab 已删(TASK-chart-types D1) +test("western and chara failures are terminal copy rather than waiting", async () => { const view = await okView(); - for (const [tab, layer] of [["western", "western"], ["qizheng", "qizheng"], ["dasha", "chara"]] as const) { + for (const [tab, layer] of [["western", "western"], ["dasha", "chara"]] as const) { const markup = withSidebarProvider(React.createElement(ChartPageView, { view, initialTab: tab, layerFailures: new Map([[layer, "engine_busy" as const]]) })); assert.ok(markup.includes(CHART_VIEW_COPY.busy)); assert.doesNotMatch(markup, /data-skeleton|这一栏还没拿到/); if (tab === "dasha") assert.match(markup, /Vimshottari/); } }); -test("qizheng and chara pending remain static and only western has a wheel skeleton", async () => { +// 原值: qizheng 与 chara 两栏;新值: 只剩 chara;原因: 七政 Tab 已删(TASK-chart-types D1) +test("chara pending remains static and only western has a wheel skeleton", async () => { const view = await okView(); - for (const [tab, layer] of [["qizheng", "qizheng"], ["dasha", "chara"]] as const) { + for (const [tab, layer] of [["dasha", "chara"]] as const) { const markup = withSidebarProvider(React.createElement(ChartPageView, { view, initialTab: tab, pendingLayers: new Set([layer]) })); assert.match(markup, /这一栏还没拿到/); assert.doesNotMatch(markup, /data-skeleton|InlineSpinner/); } diff --git a/frontend/tests/chart-view-engine.test.ts b/frontend/tests/chart-view-engine.test.ts index 0381ae69..917ea0e4 100644 --- a/frontend/tests/chart-view-engine.test.ts +++ b/frontend/tests/chart-view-engine.test.ts @@ -121,5 +121,6 @@ test("engine cache keys include ayanamsa and node mode and expire", () => { test("layer query parsing keeps only known layers in stable order", () => { assert.deepEqual(parseChartViewLayers(null), []); - assert.deepEqual(parseChartViewLayers("western,varga,nope,qizheng"), ["varga", "western", "qizheng"]); + // 原值: "qizheng" 被保留为已知层;新值: 与 "nope" 一样被丢弃;原因: 七政层已删(TASK-chart-types D1) + assert.deepEqual(parseChartViewLayers("western,varga,nope,qizheng"), ["varga", "western"]); }); diff --git a/frontend/tests/chart-view-route.test.ts b/frontend/tests/chart-view-route.test.ts index 955c20d7..c2e39b11 100644 --- a/frontend/tests/chart-view-route.test.ts +++ b/frontend/tests/chart-view-route.test.ts @@ -65,7 +65,6 @@ function engine(overrides: Record assert.equal(body.billed, false); assert.ok(body.vedic.vargas.some((item) => item.id === "D1")); assert.equal(body.western.status, "unavailable"); - assert.equal(body.qizheng.status, "unavailable"); + // 原值: body.qizheng.status === "unavailable";新值: 响应不再带 qizheng;原因: TASK-chart-types D1 + assert.equal("qizheng" in body, false); assert.deepEqual(calls, ["/api/chart"]); }); -test("western and qizheng stay unavailable without failing the Indian tabs", async () => { +test("western stays unavailable without failing the Indian tabs", async () => { const { calls, postEngine } = engine(); const result = await assembleChartView({ userId: "user-1", @@ -183,18 +183,18 @@ test("western and qizheng stay unavailable without failing the Indian tabs", asy assert.equal(body.dasha.vimshottari.method, "Vimshottari"); assert.equal(body.dasha.chara.title, "Chara Dasha(kn_rao 变体)"); assert.equal(body.western.status, "unavailable"); - assert.equal(body.qizheng.status, "unavailable"); assert.ok(body.vedic.vargas.some((item) => item.id === "D9")); // 原值: 无 layers 时也会打 /api/western 与 /api/qizheng // 新值: 只有请求对应 layer 才打 // 原因: BUG-717 开页不得占满重计算配额;西洋/七政改按需 assert.equal(calls.includes("/api/western"), true); - assert.equal(calls.includes("/api/qizheng"), true); + // 原值: 全部 layer 时会打 /api/qizheng;新值: 任何情况都不打;原因: 七政层已删(TASK-chart-types D1) + assert.equal(calls.includes("/api/qizheng"), false); assert.doesNotMatch(calls.join(" "), /billing|mastra|consult/); }); test("a real western engine packet lights the tropical tab without mixing sidereal longitudes", async () => { - const { postEngine } = engine({ "/api/western": golden.western, "/api/qizheng": null }); + const { postEngine } = engine({ "/api/western": golden.western }); const result = await assembleChartView({ userId: "user-1", profile, @@ -209,30 +209,10 @@ test("a real western engine packet lights the tropical tab without mixing sidere assert.equal(body.western.houses.length, 12); assert.match(body.western.boundary, /回归黄道/); assert.notEqual(body.western.ascendantLongitude, body.vedic.planets[0]?.degreeInSign); - assert.equal(body.qizheng.status, "unavailable"); -}); - -test("qizheng ketuMode follows the engine field, not a request echo", async () => { - const qizheng = { - coordinate_system: "qizheng_mansion_degrees_from_jiao", - calculation: { ketu_mode: "descending-node", engine_ketu_mode: "apogee" }, - palaces: [{ name: "午", branch: "午", lifePalace: "命宫", mansions: ["星"], occupants: [] }], - bodies: { sun: { mansion: "奎", siderealLon: 177.94, palace: "亥宮", dignity: "陷" } }, - }; - const { postEngine } = engine({ "/api/qizheng": qizheng }); - const result = await assembleChartView({ - userId: "user-1", - profile, - postEngine, - asOf: "2026-09-15", - layers: ["qizheng"], - }); - const body = chartViewOkSchema.parse(result.body); - assert.equal(body.qizheng.status, "ok"); - if (body.qizheng.status !== "ok") throw new Error("expected qizheng ok"); - assert.equal(body.qizheng.ketuMode, "apogee"); }); +// 原值: 断言七政 ketuMode 取引擎字段;新值: 整条删除;原因: 七政层已从星盘页删除(TASK-chart-types D1), +// ketu_mode 的引擎侧合同仍由 tests/test_qizheng_api_productization.py 守住 test("busy vs other natal engine failures split copy and each leave a log line", async () => { const stubs: Array<{ status: EngineCallResult["status"]; httpStatus?: number; errorName?: string }> = [ { status: "busy", httpStatus: 429 }, @@ -298,7 +278,9 @@ test("the golden chart-view envelope stays inside the page contract", () => { }); assert.match(COORDINATE_BOUNDARY.vedic, /不能换算/); assert.match(COORDINATE_BOUNDARY.western, /不能换算/); - assert.match(COORDINATE_BOUNDARY.qizheng, /角宿/); + // 原值: COORDINATE_BOUNDARY.qizheng 含「角宿」;新值: 该条已删,印度/西洋两句不再提七政;原因: TASK-chart-types D1 + assert.equal("qizheng" in COORDINATE_BOUNDARY, false); + assert.doesNotMatch(COORDINATE_BOUNDARY.vedic + COORDINATE_BOUNDARY.western, /七政/); }); test("page.tsx does not host the chart page", () => { diff --git a/frontend/tests/chart-view-zero-year.test.ts b/frontend/tests/chart-view-zero-year.test.ts index d2c460c4..3f31177e 100644 --- a/frontend/tests/chart-view-zero-year.test.ts +++ b/frontend/tests/chart-view-zero-year.test.ts @@ -44,7 +44,6 @@ function viewFrom(chara: Record): ChartViewOk { varga: null, chara, western: null, - qizheng: null, profile, asOf: "2026-09-25", });