feat: implement LiveTemperatureThresholdChart and add visibility policy unit tests

This commit is contained in:
2569718930@qq.com
2026-05-26 05:46:45 +08:00
parent 8ca9ce4223
commit e1417f607d
3 changed files with 261 additions and 25 deletions
@@ -1,5 +1,7 @@
import {
__buildTemperatureChartDataForTest,
__getObservationDisplayMetricsForTest,
__getVisibleTemperatureSeriesForTest,
__isTemperatureSeriesVisibleByDefaultForTest,
} from "@/components/dashboard/scan-terminal/LiveTemperatureThresholdChart";
@@ -50,6 +52,7 @@ export function runTests() {
} as any;
const { series } = __buildTemperatureChartDataForTest(guangzhou, hourly, "1D");
const defaultVisibleSeries = __getVisibleTemperatureSeriesForTest("guangzhou", series, {});
const settlementRunway = seriesByKey(series, "runway_02L_20R") as any;
assert(settlementRunway, "settlement runway should use a stable runway-pair key");
@@ -82,6 +85,16 @@ export function runTests() {
!__isTemperatureSeriesVisibleByDefaultForTest("guangzhou", "model_curve_ECMWF"),
"multi-model curves should be hidden by default",
);
assert(
!defaultVisibleSeries.some((item) => item.key === "model_curve_ECMWF"),
"hidden multi-model curves should not affect the active chart series by default",
);
assert(
__getVisibleTemperatureSeriesForTest("guangzhou", series, { model_curve_ECMWF: true }).some(
(item) => item.key === "model_curve_ECMWF",
),
"users should still be able to enable a hidden multi-model curve from the legend",
);
assert(
__isTemperatureSeriesVisibleByDefaultForTest("paris", "model_curve_AROME HD"),
"Paris AROME HD should be the only default-visible model curve exception",
@@ -107,4 +120,111 @@ export function runTests() {
);
assert(seriesByKey(shenzhen.series, "metar"), "Shenzhen/Lau Fau Shan observations should stay as METAR/HKO observations, not runway data");
assert(!shenzhen.series.some((item) => item.key.startsWith("runway_")), "Shenzhen should not be treated as an AMSC runway city");
const chengduFromAmosSnapshot = __buildTemperatureChartDataForTest(
{
city: "chengdu",
local_date: "2026-05-26",
local_time: "05:25",
tz_offset_seconds: 8 * 60 * 60,
airport: "ZUUU",
} as any,
{
localTime: "05:25",
times: ["00:00", "06:00", "12:00", "18:00"],
temps: [24, 28, 31, 27],
amos: {
observation_time: "2026-05-25T21:25:00+00:00",
observation_time_local: "2026-05-26 05:25:00",
runway_obs: {
runway_pairs: [
["02L", "20R"],
["02R", "20L"],
],
temperatures: [
[24.4, null],
[24.2, null],
],
point_temperatures: [
{ runway: "02L/20R", tdz_temp: 24.4, mid_temp: null, end_temp: 24.8 },
{ runway: "02R/20L", tdz_temp: 24.2, mid_temp: null, end_temp: 24.6 },
],
},
},
} as any,
"1D",
);
const chengduSettlementRunway = seriesByKey(chengduFromAmosSnapshot.series, "runway_02L_20R") as any;
assert(chengduSettlementRunway, "AMOS runway_obs snapshot should still create the settlement runway chart line");
assert(chengduSettlementRunway.color === "#009688", "AMOS snapshot settlement runway should use highlight cyan");
assert(chengduSettlementRunway.featured === true, "AMOS snapshot settlement runway should be featured");
assert(!chengduSettlementRunway.dashed, "AMOS snapshot settlement runway should be solid");
const chengduAuxRunway = seriesByKey(chengduFromAmosSnapshot.series, "runway_02R_20L") as any;
assert(chengduAuxRunway, "AMOS runway_obs snapshot should create auxiliary runway chart lines");
assert(chengduAuxRunway.dashed === true, "AMOS snapshot auxiliary runway should be dashed");
const newYorkMetrics = __getObservationDisplayMetricsForTest(
{
city: "new york",
local_date: "2026-05-25",
local_time: "17:30",
tz_offset_seconds: -4 * 60 * 60,
current_temp: 0,
current_max_so_far: 0,
metar_context: {
airport_max_so_far: 0,
},
} as any,
{
localTime: "17:30",
times: ["00:00"],
temps: [55],
airportCurrent: {
temp: 73.9,
max_so_far: 73.9,
},
metarTodayObs: [
{ time: "16:51", temp: 73.9 },
{ time: "15:51", temp: 73.0 },
{ time: "00:34", temp: 55.0 },
],
} as any,
null,
);
assert(newYorkMetrics.currentRunwayTemp === 73.9, "weather-station header should use detail METAR/current temp before stale row zero");
assert(newYorkMetrics.observedHighMetar === 73.9, "METAR high header should use detail METAR high before stale row zero");
const newYorkWithMadis = __buildTemperatureChartDataForTest(
{
city: "new york",
local_date: "2026-05-25",
local_time: "17:30",
tz_offset_seconds: -4 * 60 * 60,
airport: "KLGA",
} as any,
{
localTime: "17:30",
times: ["00:00", "06:00", "12:00", "18:00"],
temps: [55, 57, 65, 72],
airportPrimary: {
source_code: "madis_hfmetar",
source_label: "NOAA MADIS",
},
airportPrimaryTodayObs: [
["16:51", 73.9],
["15:51", 73],
["15:47", 71.6],
["15:44", 72],
],
metarTodayObs: [{ time: "16:51", temp: 73.9 }],
} as any,
"1D",
);
const madisSeries = seriesByKey(newYorkWithMadis.series, "madis") as any;
assert(madisSeries, "US MADIS airport-primary observations should render as a dedicated chart series");
assert(madisSeries.label.includes("MADIS"), "US MADIS series should be labeled as NOAA MADIS instead of plain METAR");
assert(madisSeries.values.filter((value: number | null) => value !== null).length >= 2, "MADIS series should keep sub-hourly observations");
}