feat: add scan terminal dashboard components, monitoring panels, and associated utility hooks
This commit is contained in:
+30
@@ -0,0 +1,30 @@
|
||||
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 clientPath = path.join(
|
||||
projectRoot,
|
||||
"components",
|
||||
"dashboard",
|
||||
"scan-terminal",
|
||||
"scan-terminal-client.ts",
|
||||
);
|
||||
const source = fs.readFileSync(clientPath, "utf8");
|
||||
const concurrencyMatch = source.match(/AI_CITY_READ_MAX_CONCURRENT_STREAMS\s*=\s*(\d+)/);
|
||||
const concurrency = Number(concurrencyMatch?.[1]);
|
||||
|
||||
assert(
|
||||
Number.isFinite(concurrency) && concurrency >= 4,
|
||||
"AI airport-read streams should allow at least 4 concurrent cards to avoid slow decision-card evidence queues",
|
||||
);
|
||||
assert(
|
||||
source.includes("queuedAiCityReadTasks.unshift(run)") &&
|
||||
source.includes("queuedAiCityReadTasks.pop()"),
|
||||
"newer AI airport-read requests should be prioritized instead of waiting behind older pinned cards",
|
||||
);
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
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 workspacePath = path.join(
|
||||
projectRoot,
|
||||
"components",
|
||||
"dashboard",
|
||||
"scan-terminal",
|
||||
"use-ai-pinned-city-workspace.ts",
|
||||
);
|
||||
const source = fs.readFileSync(workspacePath, "utf8");
|
||||
|
||||
assert(
|
||||
!source.includes("waitForDeepAnalysisQueue"),
|
||||
"decision-card deep analysis hydration must not add an artificial queue delay",
|
||||
);
|
||||
assert(
|
||||
/store\.ensureCityDetail\(\s*nextCity,\s*false,\s*"full",?\s*\)/.test(source),
|
||||
"automatic deep analysis hydration should use cache-friendly full detail requests",
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
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 dashboardPath = path.join(
|
||||
projectRoot,
|
||||
"components",
|
||||
"dashboard",
|
||||
"ScanTerminalDashboard.tsx",
|
||||
);
|
||||
const source = fs.readFileSync(dashboardPath, "utf8");
|
||||
|
||||
assert(
|
||||
source.includes("isMobileViewport") &&
|
||||
source.includes("showAnnouncement && !isMobileViewport"),
|
||||
"scan upgrade announcement must not render on mobile viewports",
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
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 hookPath = path.join(projectRoot, "hooks", "useLeafletMap.ts");
|
||||
const source = fs.readFileSync(hookPath, "utf8");
|
||||
|
||||
assert(
|
||||
source.includes("bindMarkerTouchSelect") &&
|
||||
source.includes('"touchend"') &&
|
||||
source.includes('"pointerup"'),
|
||||
"Leaflet city markers must bind touch/pointer selection events for mobile taps",
|
||||
);
|
||||
assert(
|
||||
source.includes("L.DomEvent.stopPropagation") &&
|
||||
source.includes("L.DomEvent.preventDefault"),
|
||||
"mobile marker tap handling must stop map click propagation and prevent browser touch defaults",
|
||||
);
|
||||
}
|
||||
@@ -27,6 +27,13 @@ export function runTests() {
|
||||
"scan-terminal",
|
||||
"RunwayObservationsPanel.tsx",
|
||||
);
|
||||
const monitorPath = path.join(
|
||||
projectRoot,
|
||||
"components",
|
||||
"dashboard",
|
||||
"monitoring",
|
||||
"MonitorPanel.tsx",
|
||||
);
|
||||
|
||||
const shellPartsSource = fs.readFileSync(shellPartsPath, "utf8");
|
||||
const dashboardSource = fs.readFileSync(dashboardPath, "utf8");
|
||||
@@ -50,4 +57,23 @@ export function runTests() {
|
||||
panelSource.includes("END"),
|
||||
"runway tab must identify AMSC AWOS and show TDZ/MID/END point temperatures",
|
||||
);
|
||||
assert(
|
||||
panelSource.includes('key: "qingdao"') &&
|
||||
panelSource.includes("青岛") &&
|
||||
panelSource.includes("ZSQD"),
|
||||
"runway tab must include Qingdao / ZSQD AMSC AWOS runway observations",
|
||||
);
|
||||
assert(
|
||||
panelSource.includes("RKSI") &&
|
||||
panelSource.includes("RKPK") &&
|
||||
panelSource.includes("runway_pairs") &&
|
||||
panelSource.includes("runway_temps"),
|
||||
"runway tab must also own Seoul/Busan AMOS runway-pair observations",
|
||||
);
|
||||
const monitorSource = fs.readFileSync(monitorPath, "utf8");
|
||||
assert(
|
||||
monitorSource.includes("ignoreRunway: KOREA_RUNWAY_MONITOR_KEYS.has(key)") &&
|
||||
monitorSource.includes("showRunwayRows = !KOREA_RUNWAY_MONITOR_KEYS.has(key)"),
|
||||
"market monitor must not render Seoul/Busan runway data after it moves to the runway tab",
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user