Files
PolyWeather/frontend/components/dashboard/scan-terminal/__tests__/calendarGrouping.test.ts
T
2569718930@qq.com 12d90c5051 Lock decision edge cases with business state tests
The dashboard risk is now mostly contradictory state combinations rather than styling. This extracts calendar action grouping into a pure utility and adds a lightweight TypeScript business-state runner so key decision states can be asserted without adding a test dependency.

Constraint: No new dependencies; use the existing TypeScript package for a local runner.

Rejected: Only rely on Next build/typecheck | it cannot catch product-language regressions such as fallback AI being labeled complete.

Confidence: high

Scope-risk: moderate

Reversibility: clean

Tested: npm run test:business

Tested: npm run build

Not-tested: Browser-rendered mobile fold interaction snapshots.
2026-04-28 13:13:57 +08:00

82 lines
2.3 KiB
TypeScript

import assert from "node:assert/strict";
import {
buildCalendarCoreReason,
buildCalendarMeta,
getCalendarActionGroup,
} from "@/components/dashboard/scan-terminal/calendar-action-utils";
import type { ScanOpportunityRow } from "@/lib/dashboard-types";
function row(overrides: Partial<ScanOpportunityRow>): ScanOpportunityRow {
return {
city: "London",
city_display_name: "London",
current_temp: 21,
deb_prediction: 24,
id: "london-2026-04-27",
local_date: "2026-04-27",
local_time: "4/27·00:01",
selected_date: "2026-04-27",
temp_symbol: "°C",
...overrides,
} as ScanOpportunityRow;
}
export function runTests() {
const snapshotMs = Date.UTC(2026, 3, 27, 16, 1);
const sameNow = snapshotMs;
const localTimeCase = row({
minutes_until_peak_end: 119,
minutes_until_peak_start: 0,
window_phase: "active_peak",
});
const meta = buildCalendarMeta(localTimeCase, "zh-CN", snapshotMs, sameNow);
assert.equal(meta.startAtMs, snapshotMs);
assert.ok(meta.localWindowLabel, "should expose the user's local peak window");
assert.notEqual(meta.localWindowLabel, localTimeCase.local_time);
assert.doesNotMatch(meta.localWindowLabel || "", /4\/27·00:01/);
const activeGroup = getCalendarActionGroup(localTimeCase, meta, sameNow, "zh-CN");
assert.equal(activeGroup.key, "now");
const breakoutReason = buildCalendarCoreReason(
row({
cluster_core_high: 24.7,
cluster_core_low: 23.1,
cluster_model_count: 6,
current_temp: 25.2,
model_cluster_sources: {
ecmwf: 24.4,
gfs: 24.7,
},
}),
activeGroup,
"zh-CN",
);
assert.match(breakoutReason, /实测已高于模型上沿/);
assert.doesNotMatch(breakoutReason, /等待下一报文确认方向/);
const pastMeta = buildCalendarMeta(
row({
minutes_until_peak_end: -20,
minutes_until_peak_start: -120,
window_phase: "post_peak",
}),
"zh-CN",
snapshotMs,
sameNow,
);
const pastGroup = getCalendarActionGroup(
row({ minutes_until_peak_end: -20, minutes_until_peak_start: -120, window_phase: "post_peak" }),
pastMeta,
sameNow,
"zh-CN",
);
assert.equal(pastGroup.key, "past");
assert.match(
buildCalendarCoreReason(row({}), pastGroup, "zh-CN"),
/峰值窗口已过,若无新高应避免追高/,
);
}