Files
PolyWeather/frontend/components/dashboard/scan-terminal/__tests__/terminalGridPolicy.test.ts
T

106 lines
4.6 KiB
TypeScript

import fs from "node:fs";
import path from "node:path";
function assert(condition: unknown, message: string) {
if (!condition) throw new Error(message);
}
export function runTests() {
const projectRoot = process.cwd();
const dashboardSource = fs.readFileSync(
path.join(projectRoot, "components", "dashboard", "ScanTerminalDashboard.tsx"),
"utf8",
);
const selectorSource = fs.readFileSync(
path.join(projectRoot, "components", "dashboard", "scan-terminal", "GridLayoutSelector.tsx"),
"utf8",
);
const chartSource = fs.readFileSync(
path.join(projectRoot, "components", "dashboard", "scan-terminal", "LiveTemperatureThresholdChart.tsx"),
"utf8",
);
const chartCanvasSource = fs.readFileSync(
path.join(projectRoot, "components", "dashboard", "scan-terminal", "TemperatureChartCanvas.tsx"),
"utf8",
);
const citySelectorSource = fs.readFileSync(
path.join(projectRoot, "components", "dashboard", "scan-terminal", "CitySelectorDropdown.tsx"),
"utf8",
);
assert(
dashboardSource.includes("MAX_TERMINAL_CHARTS = 9"),
"terminal dashboard must cap the desktop homepage at 9 charts",
);
assert(
dashboardSource.includes("MOBILE_TERMINAL_CHARTS = 1"),
"terminal dashboard must document that mobile renders exactly one chart",
);
assert(
dashboardSource.includes("clampGridSide") &&
dashboardSource.includes("Math.min(MAX_TERMINAL_CHARTS"),
"terminal grid dimensions must be clamped before rendering or persisting",
);
assert(
dashboardSource.includes("mobileChartRow") &&
dashboardSource.includes("建议横屏") &&
dashboardSource.includes("Rotate to landscape") &&
dashboardSource.includes("disableClose={true}"),
"mobile terminal should render one selected chart and suggest landscape for the full grid",
);
assert(
selectorSource.includes("[1, 2, 3].map") &&
selectorSource.includes("grid grid-cols-3"),
"grid selector must expose at most a 3 by 3 chart layout",
);
assert(
dashboardSource.includes("if (!cityInSlot || !rowForSlot)") &&
dashboardSource.includes("handleSelectCityForSlot(slotIndex, null);"),
"stale saved chart slots must render the empty city picker instead of a row=null Temperature Chart",
);
assert(
dashboardSource.includes("absolute left-1/2 top-12 z-50") &&
!dashboardSource.includes("top-1/2 -translate-x-1/2 -translate-y-1/2"),
"empty top-row slots must open the city selector downward so the search input is not clipped by the viewport header",
);
assert(
citySelectorSource.includes("max-h-[calc(100vh-72px)]") &&
citySelectorSource.includes("min-h-0 flex-1 overflow-y-auto"),
"city selector dropdown must cap its viewport height and keep the result list scrollable",
);
assert(
citySelectorSource.includes("MIN_DROPDOWN_TOP_PX") &&
citySelectorSource.includes("viewportNudgeY") &&
citySelectorSource.includes("getBoundingClientRect()"),
"city selector dropdown must nudge itself inside the viewport when opened from top-row chart cards",
);
assert(
chartSource.includes("setLiveTemp(null);") &&
chartSource.includes("lastAppliedPatchRevisionRef.current = 0;"),
"switching city slots must clear the previous live temperature so Fahrenheit values cannot leak into Celsius charts",
);
assert(
chartSource.includes('compact ? "min-h-0" : "min-h-[300px]"') &&
chartCanvasSource.includes("const minChartHeight = compact ? 120 : 220") &&
chartCanvasSource.includes('compact ? "min-h-[120px]" : "min-h-[220px]"'),
"compact grid charts must not force desktop minimum heights that get clipped inside 3x3 terminal slots",
);
const signedOutBlock = dashboardSource.slice(
dashboardSource.indexOf('if (event === "SIGNED_OUT")'),
dashboardSource.indexOf('} else if (event === "TOKEN_REFRESHED"'),
);
assert(
signedOutBlock.includes("await supabase.auth.getSession()") &&
signedOutBlock.includes("mergeAccessStateWithAuthPayload(prev, payload)") &&
signedOutBlock.indexOf("await supabase.auth.getSession()") <
signedOutBlock.indexOf("setProAccess(createEmptyAccess(false))"),
"terminal auth listener must re-check the current Supabase session before clearing access on SIGNED_OUT events",
);
assert(
dashboardSource.includes('event === "INITIAL_SESSION"') &&
dashboardSource.indexOf('event === "INITIAL_SESSION"') <
dashboardSource.indexOf('event === "TOKEN_REFRESHED"'),
"terminal auth listener must hydrate access from Supabase INITIAL_SESSION events during first navigation from the landing page",
);
}