mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-15 15:52:21 +00:00
Test layers on one engine and share selection and shader helpers
Layer display tests no longer re-check Cesium primitives in every layer: they run on the fake engine, and one Cesium translation check per ground kind lives in the Cesium engine's ground tests. Their harnesses are shared, and the fake-engine, Cesium-engine and flights tests that repeated the contract suite are gone, 340 tests in all. The press-travel measurement is shared by the selection dispatcher and CCTV, which loses its ScreenSpaceEventHandler shim and world-click module. ALPR, installations and bikeshare share one selection keeper and selected marker write. Layers with their own click rules keep their handlers. Navara's pulse is a class like the ground pass, the ground and imagery passes share the surface uniforms, and the colour, log-depth and occlusion GLSL lives in one chunk module, each caller keeping its depth bias. The ground suite runs inside the contract suite (--only=ground:), and fixture and script names say what they test. Behaviour is unchanged on both engines.
This commit is contained in:
1 parent
7f478e3815
commit
b4556f9763
101 files changed
+1095
-4071
No files matched your search
@@ -7,10 +7,10 @@
|
||||
"navaraKindChecks": 1,
|
||||
"engineSniffCalls": 0,
|
||||
"engineExtensionReads": 3,
|
||||
"groundEffectPasses": 1,
|
||||
"groundEffectPasses": 0,
|
||||
"cesiumSdkLoads": 3,
|
||||
"screenSpaceHandlerFiles": 4,
|
||||
"screenSpaceHandlerFiles": 2,
|
||||
"engineNameLines": 27
|
||||
},
|
||||
"measuredAt": "Drop leftover viewer paths"
|
||||
"measuredAt": "One selection path and test level"
|
||||
}
|
||||
@@ -13,7 +13,7 @@ export const cameras = [
|
||||
].map((position, i) => ({
|
||||
...position,
|
||||
osmId: 9000000 + i,
|
||||
operator: i % 3 ? 'W2 fixture' : 'Cámara pública',
|
||||
operator: i % 3 ? 'Deterministic fixture' : 'Cámara pública',
|
||||
brand: 'Fixture camera',
|
||||
cameraType: 'fixed',
|
||||
direction: i % 5 ? String((i * 37) % 360) : '',
|
||||
@@ -2,8 +2,8 @@ import sharp from 'sharp';
|
||||
|
||||
// Transport fixtures are parsed by the real CCTV catalog and media owners.
|
||||
export const cameras = Array.from({ length: 25 }, (_, i) => ({
|
||||
id: `w2-cctv-${String(i).padStart(3, '0')}`,
|
||||
name: i === 12 ? 'Cámara pública — Congress' : `W2 camera ${i}`,
|
||||
id: `fixture-cctv-${String(i).padStart(3, '0')}`,
|
||||
name: i === 12 ? 'Cámara pública — Congress' : `Fixture camera ${i}`,
|
||||
cityId: 'austin',
|
||||
city: 'Austin',
|
||||
lat: 30.2672 + (Math.floor(i / 5) - 2) * 0.0018,
|
||||
@@ -17,7 +17,7 @@ export const cameras = Array.from({ length: 25 }, (_, i) => ({
|
||||
headingConfidence: i % 3 ? 'medium' : 'low',
|
||||
sourceKind: 'configured',
|
||||
feedType: 'image',
|
||||
provider: 'W2 deterministic fixture',
|
||||
provider: 'Deterministic fixture',
|
||||
url: 'https://cctv-fixture.invalid/frame.png',
|
||||
}));
|
||||
|
||||
@@ -51,8 +51,8 @@ export async function installCctvFixture(page, { video } = {}) {
|
||||
if (video)
|
||||
catalog.push({
|
||||
...cameras[0],
|
||||
id: 'w2-cctv-video',
|
||||
name: 'W2 moving video',
|
||||
id: 'fixture-cctv-video',
|
||||
name: 'Fixture moving video',
|
||||
lat: 30.2718,
|
||||
lon: -97.744,
|
||||
feedType: 'video',
|
||||
@@ -116,7 +116,7 @@ export async function installCctvFixture(page, { video } = {}) {
|
||||
id,
|
||||
status: 'online',
|
||||
sourceKind: feedType,
|
||||
label: 'W2 deterministic fixture',
|
||||
label: 'Deterministic fixture',
|
||||
updatedAt: Date.now(),
|
||||
})),
|
||||
});
|
||||
@@ -126,7 +126,7 @@ export async function installCctvFixture(page, { video } = {}) {
|
||||
const id = decodeURIComponent(media[2]);
|
||||
if (
|
||||
!ids.has(id) ||
|
||||
(media[1] === 'media' && (id !== 'w2-cctv-video' || !video))
|
||||
(media[1] === 'media' && (id !== 'fixture-cctv-video' || !video))
|
||||
) {
|
||||
return request.respond({ status: 404, body: 'Unknown fixture camera' });
|
||||
}
|
||||
@@ -4,8 +4,8 @@ const grid = (i, cols, spacing) => ({
|
||||
lon: ORIGIN.lon + ((i % cols) - cols / 2) * spacing,
|
||||
});
|
||||
const bikes = Array.from({ length: 200 }, (_, i) => ({
|
||||
station_id: `w2-bike-${i}`,
|
||||
name: `W2 station ${i}`,
|
||||
station_id: `fixture-bike-${i}`,
|
||||
name: `Fixture station ${i}`,
|
||||
capacity: 20,
|
||||
...grid(i, 15, 0.0015),
|
||||
}));
|
||||
File renamed without changes.
@@ -78,7 +78,7 @@ export async function installRadioFixture(page) {
|
||||
stale: false,
|
||||
degraded: false,
|
||||
acceptedGeneration: qa.generation,
|
||||
catalogInstance: 'w2-radio-fixture',
|
||||
catalogInstance: 'fixture-radio-fixture',
|
||||
};
|
||||
const mode = qa.response;
|
||||
const delay = qa.delay;
|
||||
@@ -6,7 +6,7 @@ export function transitSnapshot(step = 8) {
|
||||
fetchedAt,
|
||||
vehicles: [
|
||||
...Array.from({ length: 12 }, (_, i) => ({
|
||||
id: `w2-${i}`,
|
||||
id: `fixture-${i}`,
|
||||
routeId: routes[i % 6],
|
||||
tripId: `trip-${i}`,
|
||||
lat:
|
||||
@@ -1212,6 +1212,7 @@
|
||||
"src/testSupport/cockpitEngine.mjs",
|
||||
"src/layers/aircraft/render/contract.js",
|
||||
"src/layers/shared/color.test.mjs",
|
||||
"src/layers/shared/selectionKeeper.js",
|
||||
"src/layers/shared/color.js",
|
||||
"src/layers/shared/viewRectangle.js",
|
||||
"src/layers/military/render/display.js",
|
||||
@@ -1222,7 +1223,6 @@
|
||||
"src/layers/vessels/renderer.test.mjs",
|
||||
"src/layers/vessels/testFixtures.mjs",
|
||||
"src/layers/shared/render/engineView.js",
|
||||
"src/globe/navara/ground/effect.js",
|
||||
"src/layers/shared/render/moverBank.js",
|
||||
"src/layers/shared/render/imageAtlas.js",
|
||||
"src/layers/bikeshare/presentation.js",
|
||||
@@ -1233,8 +1233,8 @@
|
||||
"src/layers/cctv/lifecycle.test.mjs",
|
||||
"src/layers/cctv/projectionMedia.test.mjs",
|
||||
"src/layers/cctv/ingestion.test.mjs",
|
||||
"scripts/fixtures/w2/alpr.mjs",
|
||||
"scripts/fixtures/w2/cctv.mjs",
|
||||
"scripts/fixtures/layers/alpr.mjs",
|
||||
"scripts/fixtures/layers/cctv.mjs",
|
||||
"src/layers/radio/screenClusters.js",
|
||||
"src/layers/radio/screenClusters.test.mjs",
|
||||
"src/globe/cesium/mapScene.js",
|
||||
@@ -1258,7 +1258,7 @@
|
||||
"src/globe/cesium/testing/cameraViewer.mjs",
|
||||
"src/app/globeEngine.js",
|
||||
"src/app/globeEngine.test.mjs",
|
||||
"scripts/fixtures/w2/transit.mjs",
|
||||
"scripts/fixtures/layers/transit.mjs",
|
||||
"src/layers/localAdsb/render/display.js",
|
||||
"src/layers/localAdsb/renderer.test.mjs",
|
||||
"build/navaraAssets.js",
|
||||
@@ -1381,16 +1381,16 @@
|
||||
"src/globe/navara/movers/frameCamera.test.mjs",
|
||||
"src/layers/localAdsb/qaDrawn.js",
|
||||
"src/layers/localAdsb/qaDrawn.test.mjs",
|
||||
"src/tooling/w1RuntimeEvidence.test.mjs",
|
||||
"src/tooling/browserRuntimeEvidence.test.mjs",
|
||||
"src/layers/firms/rectangle.js",
|
||||
"src/layers/firms/rectangle.test.mjs",
|
||||
"src/layers/firms/viewport.test.mjs",
|
||||
"scripts/fixtures/w2/firms.mjs",
|
||||
"scripts/fixtures/layers/firms.mjs",
|
||||
"src/layers/firms/lifecycle.test.mjs",
|
||||
"scripts/fixtures/w2/radio.mjs",
|
||||
"scripts/fixtures/layers/radio.mjs",
|
||||
"scripts/qa-map-policy.mjs",
|
||||
"scripts/qa-map-policy.test.mjs",
|
||||
"scripts/qa-w2-imagery.mjs",
|
||||
"scripts/qa-imagery-contract.mjs",
|
||||
"src/globe/navara/postSamplingPass.test.mjs",
|
||||
"src/globe/navara/emptySceneRender.test.mjs",
|
||||
"src/data/firmsIntensity.js",
|
||||
@@ -1579,7 +1579,6 @@
|
||||
"src/globe/navara/ground/pack.js",
|
||||
"src/globe/navara/ground/pack.test.mjs",
|
||||
"src/globe/navara/ground/pass.js",
|
||||
"scripts/qa-ground-suite.mjs",
|
||||
"docs/GLOBE-PRIMITIVES.md",
|
||||
"src/layers/airQuality/render/display.js",
|
||||
"src/layers/airQuality/render/display.test.mjs",
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
"src/geo/",
|
||||
"src/layers/infrastructure/index.js",
|
||||
"src/layers/infrastructure/render/display.js",
|
||||
"src/layers/shared/selectionKeeper.js",
|
||||
"src/layers/shared/color.js",
|
||||
"src/sources/requestDeadline.js",
|
||||
"src/overlays/bindOverlayHost.js"
|
||||
@@ -1423,8 +1424,6 @@
|
||||
"src/data/routePlausible.js",
|
||||
"src/geoDistance.js",
|
||||
"src/data/trackedModelRegime.js",
|
||||
"src/data/trackingClickGesture.js",
|
||||
"src/data/trackingGesture.js",
|
||||
"src/layers/flights/controller.js",
|
||||
"src/layers/flights/enrichment.js",
|
||||
"src/layers/flights/evidence.js",
|
||||
@@ -1465,9 +1464,11 @@
|
||||
"src/geo/",
|
||||
"src/time/clock.js",
|
||||
"src/globe/contract.js",
|
||||
"src/layers/shared/selectionKeeper.js",
|
||||
"src/layers/shared/color.js",
|
||||
"src/layers/aircraft/qaDrawn.js",
|
||||
"src/overlays/bindOverlayHost.js"
|
||||
"src/overlays/bindOverlayHost.js",
|
||||
"src/data/trackingGesture.js"
|
||||
],
|
||||
"external": ["cesium"]
|
||||
},
|
||||
@@ -1495,8 +1496,6 @@
|
||||
"src/data/motionModel.js",
|
||||
"src/data/renderAltitude.js",
|
||||
"src/data/trackedModelRegime.js",
|
||||
"src/data/trackingClickGesture.js",
|
||||
"src/data/trackingGesture.js",
|
||||
"src/layers/military/controller.js",
|
||||
"src/layers/military/index.js",
|
||||
"src/layers/military/ingestion.js",
|
||||
@@ -1530,18 +1529,21 @@
|
||||
"src/i18n/locales/zh-Hant.json",
|
||||
"src/globe/cesium/sceneProjection.js",
|
||||
"src/globe/cesium/screenEvents.js",
|
||||
"src/layers/shared/selectionKeeper.js",
|
||||
"src/layers/shared/color.js",
|
||||
"src/geo/",
|
||||
"src/time/clock.js",
|
||||
"src/globe/contract.js",
|
||||
"src/layers/aircraft/qaDrawn.js",
|
||||
"src/overlays/bindOverlayHost.js"
|
||||
"src/overlays/bindOverlayHost.js",
|
||||
"src/data/trackingGesture.js"
|
||||
],
|
||||
"external": ["cesium"]
|
||||
},
|
||||
"aircraft-mechanics": {
|
||||
"exports": ["./aircraft"],
|
||||
"modules": [
|
||||
"src/data/trackingGesture.js",
|
||||
"src/i18n/marker.js",
|
||||
"src/data/aircraftClass.js",
|
||||
"src/data/aircraftIcons.js",
|
||||
@@ -1563,8 +1565,6 @@
|
||||
"src/data/routePlausible.js",
|
||||
"src/geoDistance.js",
|
||||
"src/data/trackedModelRegime.js",
|
||||
"src/data/trackingClickGesture.js",
|
||||
"src/data/trackingGesture.js",
|
||||
"src/layers/aircraft/classification.js",
|
||||
"src/layers/aircraft/index.js",
|
||||
"src/data/inputOwnership.js",
|
||||
@@ -1614,6 +1614,7 @@
|
||||
"src/i18n/locales/zh-CN.json",
|
||||
"src/i18n/locales/zh-Hant.json",
|
||||
"src/layers/vessels/icons.js",
|
||||
"src/layers/shared/selectionKeeper.js",
|
||||
"src/layers/shared/color.js",
|
||||
"src/globe/cesium/sceneProjection.js",
|
||||
"src/geo/",
|
||||
@@ -1630,6 +1631,7 @@
|
||||
"src/layers/earthquakes/source.js",
|
||||
"src/layers/earthquakes/records.js",
|
||||
"src/geo/",
|
||||
"src/layers/shared/selectionKeeper.js",
|
||||
"src/layers/shared/color.js",
|
||||
"src/layers/earthquakes/render/display.js",
|
||||
"src/overlays/bindOverlayHost.js"
|
||||
@@ -1700,6 +1702,7 @@
|
||||
"src/i18n/locales/zh-Hant.json",
|
||||
"src/geo/",
|
||||
"src/layers/firms/rectangle.js",
|
||||
"src/layers/shared/selectionKeeper.js",
|
||||
"src/layers/shared/color.js",
|
||||
"src/overlays/bindOverlayHost.js"
|
||||
],
|
||||
@@ -1743,6 +1746,7 @@
|
||||
"src/ui/unitLabels.js",
|
||||
"src/geo/",
|
||||
"src/time/clock.js",
|
||||
"src/layers/shared/selectionKeeper.js",
|
||||
"src/layers/shared/color.js",
|
||||
"src/overlays/bindOverlayHost.js"
|
||||
],
|
||||
@@ -1785,6 +1789,7 @@
|
||||
"src/i18n/locales/uk.json",
|
||||
"src/i18n/locales/zh-CN.json",
|
||||
"src/i18n/locales/zh-Hant.json",
|
||||
"src/layers/shared/selectionKeeper.js",
|
||||
"src/layers/shared/color.js",
|
||||
"src/geo/",
|
||||
"src/time/clock.js",
|
||||
@@ -1837,6 +1842,7 @@
|
||||
"src/i18n/locales/zh-CN.json",
|
||||
"src/i18n/locales/zh-Hant.json",
|
||||
"src/layers/installations/classLabel.js",
|
||||
"src/layers/shared/selectionKeeper.js",
|
||||
"src/layers/shared/color.js",
|
||||
"src/geo/",
|
||||
"src/globe/contract.js"
|
||||
@@ -1900,6 +1906,7 @@
|
||||
"traffic-layer": {
|
||||
"exports": ["./layers/traffic"],
|
||||
"modules": [
|
||||
"src/layers/shared/selectionKeeper.js",
|
||||
"src/layers/shared/color.js",
|
||||
"src/data/cameraSensitivity.js",
|
||||
"src/data/flowMatch.js",
|
||||
@@ -1992,6 +1999,7 @@
|
||||
"src/i18n/locales/zh-Hant.json",
|
||||
"src/layers/bikeshare/selectionModel.js",
|
||||
"src/layers/bikeshare/presentation.js",
|
||||
"src/layers/shared/selectionKeeper.js",
|
||||
"src/layers/shared/color.js",
|
||||
"src/geo/",
|
||||
"src/globe/contract.js",
|
||||
@@ -2051,6 +2059,7 @@
|
||||
"src/i18n/locales/uk.json",
|
||||
"src/i18n/locales/zh-CN.json",
|
||||
"src/i18n/locales/zh-Hant.json",
|
||||
"src/layers/shared/selectionKeeper.js",
|
||||
"src/layers/shared/color.js",
|
||||
"src/geo/",
|
||||
"src/overlays/bindOverlayHost.js"
|
||||
@@ -2128,6 +2137,7 @@
|
||||
"src/i18n/locales/zh-Hant.json",
|
||||
"src/layers/streetLevel/providers/mapillary/policy.js",
|
||||
"src/geo/",
|
||||
"src/layers/shared/selectionKeeper.js",
|
||||
"src/layers/shared/color.js",
|
||||
"src/globe/cameraMath.js",
|
||||
"src/layers/streetLevel/render/display.js",
|
||||
@@ -2246,6 +2256,7 @@
|
||||
"src/i18n/locales/zh-Hant.json",
|
||||
"src/geo/",
|
||||
"src/sources/requestDeadline.js",
|
||||
"src/layers/shared/selectionKeeper.js",
|
||||
"src/layers/shared/color.js",
|
||||
"src/layers/submarineCables/render/display.js",
|
||||
"src/overlays/bindOverlayHost.js"
|
||||
@@ -2610,7 +2621,6 @@
|
||||
"src/data/neighborhoodPolygons.js",
|
||||
"src/data/pickRegistry.js",
|
||||
"src/data/sceneServices.js",
|
||||
"src/data/trackingGesture.js",
|
||||
"src/data/radio.js",
|
||||
"src/data/radioCountry.js",
|
||||
"src/data/regionalBrief.js",
|
||||
@@ -2633,7 +2643,6 @@
|
||||
"src/data/trackedCamera.js",
|
||||
"src/data/trackedModelRegime.js",
|
||||
"src/data/trackedReadout.js",
|
||||
"src/data/trackingClickGesture.js",
|
||||
"src/data/traffic.js",
|
||||
"src/data/trafficBounds.js",
|
||||
"src/data/trafficFlowStyle.js",
|
||||
@@ -3496,6 +3505,7 @@
|
||||
"src/layers/flights/render/display.js",
|
||||
"src/layers/military/render/display.js",
|
||||
"src/data/selectionDispatcher.js",
|
||||
"src/layers/shared/selectionKeeper.js",
|
||||
"src/layers/shared/color.js",
|
||||
"src/layers/aircraft/qaDrawn.js",
|
||||
"src/layers/localAdsb/render/display.js",
|
||||
@@ -3503,6 +3513,7 @@
|
||||
"src/globe/cesium/celestial.js",
|
||||
"src/celestialRing/view.js",
|
||||
"src/globe/navara/ground/pulse.js",
|
||||
"src/globe/navara/shaderChunks.js",
|
||||
"src/globe/navara/sceneDepth.js",
|
||||
"src/layers/bhoteKoshi/render/story.js",
|
||||
"src/globe/navara/windFlowMesh.js",
|
||||
@@ -3513,7 +3524,6 @@
|
||||
"src/layers/radio/screenClusters.js",
|
||||
"src/layers/shared/render/imageAtlas.js",
|
||||
"src/globe/navara/ground/surface.js",
|
||||
"src/globe/navara/ground/effect.js",
|
||||
"src/annotations/outlineRungs.js",
|
||||
"src/sources/openFreeMapOutlines.js",
|
||||
"src/sources/nominatimOutlines.js",
|
||||
@@ -3581,7 +3591,6 @@
|
||||
"src/globe/navara/ground/index.js",
|
||||
"src/layers/cctv/render/meshes.js",
|
||||
"src/layers/cctv/render/display.js",
|
||||
"src/layers/cctv/worldClick.js",
|
||||
"src/layers/transit/render/display.js",
|
||||
"src/layers/earthquakes/render/display.js",
|
||||
"src/layers/airQuality/render/display.js",
|
||||
@@ -3625,7 +3634,8 @@
|
||||
"src/globe/exclusivePointer.js",
|
||||
"src/globe/cesium/followOffset.js",
|
||||
"src/overlays/bindOverlayHost.js",
|
||||
"src/ui/releaseTrackers.js"
|
||||
"src/ui/releaseTrackers.js",
|
||||
"src/data/trackingGesture.js"
|
||||
],
|
||||
"external": [
|
||||
"hls.js",
|
||||
@@ -3764,7 +3774,6 @@
|
||||
"src/data/neighborhoodPolygons.js",
|
||||
"src/data/pickRegistry.js",
|
||||
"src/data/sceneServices.js",
|
||||
"src/data/trackingGesture.js",
|
||||
"src/data/radioCountry.js",
|
||||
"src/data/renderAltitude.js",
|
||||
"src/data/bundledJson.js",
|
||||
@@ -3778,7 +3787,6 @@
|
||||
"src/data/trackedCamera.js",
|
||||
"src/data/trackedModelRegime.js",
|
||||
"src/data/trackedReadout.js",
|
||||
"src/data/trackingClickGesture.js",
|
||||
"src/data/trafficBounds.js",
|
||||
"src/data/trafficFlowStyle.js",
|
||||
"src/data/trafficPresetStyle.js",
|
||||
@@ -4275,6 +4283,7 @@
|
||||
"src/app/layers/displays.js",
|
||||
"src/layers/aircraft/render/display.js",
|
||||
"src/data/selectionDispatcher.js",
|
||||
"src/layers/shared/selectionKeeper.js",
|
||||
"src/layers/shared/color.js",
|
||||
"src/layers/flights/render/display.js",
|
||||
"src/layers/military/render/display.js",
|
||||
@@ -4336,7 +4345,6 @@
|
||||
"src/layers/cctv/render/style.js",
|
||||
"src/layers/cctv/render/meshes.js",
|
||||
"src/layers/cctv/render/display.js",
|
||||
"src/layers/cctv/worldClick.js",
|
||||
"src/layers/transit/render/display.js",
|
||||
"src/layers/earthquakes/render/display.js",
|
||||
"src/layers/airQuality/render/display.js",
|
||||
@@ -4368,7 +4376,6 @@
|
||||
"src/layers/bhoteKoshi/render/story.js",
|
||||
"src/globe/navara/ground/surface.js",
|
||||
"src/globe/navara/windFlowMesh.js",
|
||||
"src/globe/navara/ground/effect.js",
|
||||
"src/globe/navara/ground/pulse.js",
|
||||
"src/globe/cesium/windFlow.js",
|
||||
"src/globe/cesium/postStages.js",
|
||||
@@ -4386,7 +4393,8 @@
|
||||
"src/globe/meshes.js",
|
||||
"src/globe/exclusivePointer.js",
|
||||
"src/globe/cesium/followOffset.js",
|
||||
"src/overlays/bindOverlayHost.js"
|
||||
"src/overlays/bindOverlayHost.js",
|
||||
"src/data/trackingGesture.js"
|
||||
],
|
||||
"external": [
|
||||
"hls.js",
|
||||
@@ -4730,6 +4738,7 @@
|
||||
"src/data/distanceFormat.js",
|
||||
"src/i18n/marker.js",
|
||||
"src/layers/directions/index.js",
|
||||
"src/layers/shared/selectionKeeper.js",
|
||||
"src/layers/shared/color.js",
|
||||
"src/data/pointerOwnerNames.js",
|
||||
"src/layers/directions/source.js",
|
||||
@@ -5051,7 +5060,6 @@
|
||||
"src/data/meshFloorSampler.js",
|
||||
"src/data/pickRegistry.js",
|
||||
"src/data/sceneServices.js",
|
||||
"src/data/trackingGesture.js",
|
||||
"src/data/scenePick.js",
|
||||
"src/data/spriteOrder.js",
|
||||
"src/data/terrainHeights.js",
|
||||
@@ -5132,6 +5140,7 @@
|
||||
"src/celestialRing/view.js",
|
||||
"src/layers/shared/render/engineView.js",
|
||||
"src/globe/cesium/sceneProjection.js",
|
||||
"src/layers/shared/selectionKeeper.js",
|
||||
"src/layers/shared/color.js",
|
||||
"src/geo/",
|
||||
"src/time/clock.js",
|
||||
@@ -5156,7 +5165,8 @@
|
||||
"src/data/selectionDispatcher.js",
|
||||
"src/overlays/bindOverlayHost.js",
|
||||
"src/data/contextStore.js",
|
||||
"src/data/trackedReadout.js"
|
||||
"src/data/trackedReadout.js",
|
||||
"src/data/trackingGesture.js"
|
||||
],
|
||||
"external": ["cesium", "egm96-universal", "pbf"]
|
||||
},
|
||||
@@ -5208,6 +5218,7 @@
|
||||
"src/i18n/locales/uk.json",
|
||||
"src/i18n/locales/zh-CN.json",
|
||||
"src/i18n/locales/zh-Hant.json",
|
||||
"src/layers/shared/selectionKeeper.js",
|
||||
"src/layers/shared/color.js",
|
||||
"src/geo/",
|
||||
"src/globe/contract.js",
|
||||
@@ -5276,6 +5287,7 @@
|
||||
"src/i18n/locales/uk.json",
|
||||
"src/i18n/locales/zh-CN.json",
|
||||
"src/i18n/locales/zh-Hant.json",
|
||||
"src/layers/shared/selectionKeeper.js",
|
||||
"src/layers/shared/color.js",
|
||||
"src/layers/perimeters/render/display.js",
|
||||
"src/overlays/bindOverlayHost.js"
|
||||
@@ -5306,6 +5318,7 @@
|
||||
"src/ui/statusText.js",
|
||||
"src/ui/layerNames.js",
|
||||
"src/layers/shared/viewRectangle.js",
|
||||
"src/layers/shared/selectionKeeper.js",
|
||||
"src/layers/shared/color.js",
|
||||
"src/geo/math.js",
|
||||
"src/geo/cartesian2.js",
|
||||
@@ -5439,6 +5452,7 @@
|
||||
"src/i18n/locales/uk.json",
|
||||
"src/i18n/locales/zh-CN.json",
|
||||
"src/i18n/locales/zh-Hant.json",
|
||||
"src/layers/shared/selectionKeeper.js",
|
||||
"src/layers/shared/color.js",
|
||||
"src/layers/droneAirspace/render/display.js",
|
||||
"src/overlays/bindOverlayHost.js"
|
||||
@@ -5645,6 +5659,7 @@
|
||||
"src/i18n/locales/zh-Hant.json",
|
||||
"src/ui/statusText.js",
|
||||
"src/ui/layerNames.js",
|
||||
"src/layers/shared/selectionKeeper.js",
|
||||
"src/layers/shared/color.js",
|
||||
"src/layers/airQuality/render/display.js",
|
||||
"src/overlays/bindOverlayHost.js"
|
||||
@@ -5725,6 +5740,7 @@
|
||||
"src/globe/cesium/credits.js",
|
||||
"src/layers/wildlifeSightings/display.js",
|
||||
"src/data/spriteOrder.js",
|
||||
"src/layers/shared/selectionKeeper.js",
|
||||
"src/layers/shared/color.js",
|
||||
"src/overlays/bindOverlayHost.js"
|
||||
],
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* qa-heading-b3.mjs — throwaway QA harness for skylight Task 4
|
||||
* qa-aircraft-heading.mjs — aircraft display-heading checks
|
||||
* (path-derived, rate-limited display course; Batch 3 overnight run).
|
||||
*
|
||||
* The permanent track-regression harness flies planes STRAIGHT, so it cannot
|
||||
@@ -45,7 +45,7 @@
|
||||
* normalized nose -X, offset 180°).
|
||||
*
|
||||
* VISUAL — with the turning flights plane tracked in 3D, capture screenshots
|
||||
* (two zoom levels × two orbit angles) into qa-shots/b3/ for human review:
|
||||
* (two zoom levels × two orbit angles) into qa-shots/aircraft-heading/ for human review:
|
||||
* the nose must lead the curved trail, never sideways/backwards.
|
||||
*
|
||||
* LOW-SPEED (heading-v2, 2026-07-03 field-test regression) — two extra
|
||||
@@ -93,7 +93,7 @@
|
||||
* exceeds 15°/s (truth is 2°/s; the pre-fix snap replayed ~120° of frozen
|
||||
* divergence at the 60°/s slew cap).
|
||||
*
|
||||
* Run: node scripts/qa-heading-b3.mjs --url http://localhost:4300
|
||||
* Run: node scripts/qa-aircraft-heading.mjs --url http://localhost:4300
|
||||
* Exits non-zero if any assertion fails. Never commits anything.
|
||||
*/
|
||||
|
||||
@@ -120,7 +120,7 @@ if (requestedEngine) {
|
||||
}
|
||||
const APP_URL = appUrl.href;
|
||||
const HEADFUL = getFlag('--headful');
|
||||
const SHOT_DIR = path.resolve('qa-shots/b3');
|
||||
const SHOT_DIR = path.resolve('qa-shots/aircraft-heading');
|
||||
const SHOT_DIR_V2 = path.resolve('qa-shots/heading-v2');
|
||||
|
||||
const CHROME_EXECUTABLE_CANDIDATES = [
|
||||
@@ -774,7 +774,7 @@ async function main() {
|
||||
}
|
||||
|
||||
// ---- Screenshots: two zoom levels × two orbit angles --------------------
|
||||
console.log('\n Screenshots (turning plane tracked in 3D) → qa-shots/b3/');
|
||||
console.log('\n Screenshots (turning plane tracked in 3D) → qa-shots/aircraft-heading/');
|
||||
await page.evaluate(() => {
|
||||
window.__godsEyeView.styleManager._setDetectionMode('DENSE');
|
||||
});
|
||||
@@ -1,12 +1,12 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* qa-sprites-b5.mjs — throwaway QA harness for skylight Tasks 8+9
|
||||
* qa-aircraft-sprites.mjs — aircraft sprite checks
|
||||
* (type-aware billboard silhouettes + per-class 2D/3D scale; Batch 5).
|
||||
*
|
||||
* Injects synthetic STRAIGHT-FLYING aircraft covering ALL 8 classifyAircraft
|
||||
* classes — the flights layer via OpenSky extended categories (state[17]),
|
||||
* the military layer via adsb.lol type codes — using the same fetch-shim +
|
||||
* history-priming pattern as scripts/qa-heading-b3.mjs (positions analytic at
|
||||
* history-priming pattern as scripts/qa-aircraft-heading.mjs (positions analytic at
|
||||
* serve time so every fix is self-consistent through the render delay).
|
||||
*
|
||||
* Machine assertions:
|
||||
@@ -15,17 +15,17 @@
|
||||
* per-class glyph (image data-URI === aircraftIcon(klass),
|
||||
* imported from src) and per-class scale (flights:
|
||||
* CLASS_SCALE_2D; military: 0.7 × CLASS_SCALE_2D)
|
||||
* B3 models : with 3D on, fleet Cesium.Model.scale === the layer's
|
||||
* Model scales : with 3D on, fleet Cesium.Model.scale === the layer's
|
||||
* MODEL_SCALE (both 1; legacy scale is baked into the GLB)
|
||||
* × CLASS_SCALE_3D[klass] per icao; tracked too
|
||||
* B4 clean : no console errors
|
||||
*
|
||||
* Visual output (qa-shots/b5/, untracked): top-down 2D spreads of both rows,
|
||||
* Visual output (qa-shots/aircraft-sprites/, untracked): top-down 2D spreads of both rows,
|
||||
* per-row close-ups, a tracked-cyan tint shot, and 3D cluster shots at two
|
||||
* zooms per layer showing per-class model scale differences. The PNGs are the
|
||||
* acceptance test — read them.
|
||||
*
|
||||
* Run: node scripts/qa-sprites-b5.mjs --url http://localhost:4300
|
||||
* Run: node scripts/qa-aircraft-sprites.mjs --url http://localhost:4300
|
||||
* Exits non-zero if any assertion fails. Never commits anything.
|
||||
*/
|
||||
|
||||
@@ -44,7 +44,7 @@ const militaryWantScale = (klass) => (CLASS_MODEL_REAL[klass] ? 1
|
||||
import { aircraftIcon } from '../src/data/aircraftIcons.js';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Args (same shape as qa-heading-b3.mjs)
|
||||
// Args (same shape as qa-aircraft-heading.mjs)
|
||||
// ---------------------------------------------------------------------------
|
||||
const argv = process.argv.slice(2);
|
||||
const getFlag = (name) => argv.includes(name);
|
||||
@@ -61,7 +61,7 @@ const ENGINE = (argv.find((arg) => arg.startsWith('--engine=')) ?? '--engine=ces
|
||||
const APP_URL =
|
||||
ENGINE === 'cesium' ? BASE_URL : `${BASE_URL.replace(/\/$/, '')}/?engine=${ENGINE}`;
|
||||
const HEADFUL = getFlag('--headful');
|
||||
const SHOT_DIR = path.resolve(ENGINE === 'cesium' ? 'qa-shots/b5' : `qa-shots/b5-${ENGINE}`);
|
||||
const SHOT_DIR = path.resolve(ENGINE === 'cesium' ? 'qa-shots/aircraft-sprites' : `qa-shots/aircraft-sprites-${ENGINE}`);
|
||||
|
||||
const FL_MODEL_SCALE = 1; // flights.js (airplane.glb is transform-applied and meter-scale)
|
||||
const MIL_PLANE_MODEL_SCALE = 1; // military heavy classes share the same baked airplane.glb
|
||||
@@ -137,7 +137,7 @@ const SPRITES = {
|
||||
{ hex: 'dda006', flight: 'ML-QUAD', t: 'B744', lon0: ROW_LON0 + 5 * ROW_STEP_DEG, lat0: 30.28, courseDeg: 0, speedMps: 80, altFt: 13000 },
|
||||
{ hex: 'dda007', flight: 'ML-HEL', t: 'H60', lon0: ROW_LON0 + 6 * ROW_STEP_DEG, lat0: 30.28, courseDeg: 0, speedMps: 40, altFt: 3000 },
|
||||
// course 0 like the rest — keeps the row a clean size-comparison line (2D
|
||||
// rotation is demoed by caa004 and gated by qa-heading-b3 anyway).
|
||||
// rotation is demoed by caa004 and gated by qa-aircraft-heading anyway).
|
||||
{ hex: 'dda008', flight: 'ML-FJ', t: 'F16', lon0: ROW_LON0 + 7 * ROW_STEP_DEG, lat0: 30.28, courseDeg: 0, speedMps: 80, altFt: 15000 },
|
||||
// 3D cluster: C17 vs F16 (tracked) vs B744 vs C130. jet.glb is a
|
||||
// real-world-scale asset (native bounding radius ~29.8) rendered at
|
||||
@@ -332,7 +332,7 @@ async function main() {
|
||||
};
|
||||
});
|
||||
|
||||
// ---- Prime history through the render delay (same pattern as b3) -------
|
||||
// ---- Prime history through the render delay (same pattern as the heading checks) -------
|
||||
console.log('Priming straight-flight history through the render delay (30 s / 15 s)...');
|
||||
const primed = await page.evaluate(async () => {
|
||||
const dm = window.__godsEyeView.dataManager;
|
||||
@@ -394,7 +394,7 @@ async function main() {
|
||||
// ========================================================================
|
||||
// 2D screenshots
|
||||
// ========================================================================
|
||||
console.log('\n2D screenshots → qa-shots/b5/');
|
||||
console.log('\n2D screenshots → qa-shots/aircraft-sprites/');
|
||||
const topDown = async (lat, lon, height) => {
|
||||
await page.evaluate(async ({ lat, lon, height }) => {
|
||||
const camera = window.__godsEyeView.engine.camera;
|
||||
@@ -429,21 +429,21 @@ async function main() {
|
||||
const flRowLat = await rowLat('flights', 2, 30);
|
||||
const milRowLat = await rowLat('military', 4, 15);
|
||||
await topDown((flRowLat + milRowLat) / 2, ROW_LON0 + 3 * ROW_STEP_DEG, 16000);
|
||||
await page.screenshot({ path: path.join(SHOT_DIR, 'b5-2d-spread-bothrows.png') });
|
||||
console.log(' saved b5-2d-spread-bothrows.png (flights row top, military row bottom)');
|
||||
await page.screenshot({ path: path.join(SHOT_DIR, 'aircraft-sprites-2d-spread-bothrows.png') });
|
||||
console.log(' saved aircraft-sprites-2d-spread-bothrows.png (flights row top, military row bottom)');
|
||||
|
||||
// Row widths: military spans 7 steps (~8.1 km) → frame ~13.9 km at 12 km up;
|
||||
// flights spans 5 steps (~5.8 km, +caa004's eastward drift) → ~10.4 km at 9 km up.
|
||||
if (hasMilitary) {
|
||||
await topDown(await rowLat('military', 4, 15), ROW_LON0 + 3.5 * ROW_STEP_DEG, 17000);
|
||||
await page.screenshot({ path: path.join(SHOT_DIR, 'b5-2d-military-row.png') });
|
||||
console.log(' saved b5-2d-military-row.png (8 classes, amber)');
|
||||
await page.screenshot({ path: path.join(SHOT_DIR, 'aircraft-sprites-2d-military-row.png') });
|
||||
console.log(' saved aircraft-sprites-2d-military-row.png (8 classes, amber)');
|
||||
console.log(` glyph positions: ${await page.evaluate(() => window.__planeCanvasCoords('military', 15, 'ft'))}`);
|
||||
}
|
||||
|
||||
await topDown(await rowLat('flights', 2, 30), ROW_LON0 + 2.5 * ROW_STEP_DEG, 12000);
|
||||
await page.screenshot({ path: path.join(SHOT_DIR, 'b5-2d-flights-row.png') });
|
||||
console.log(' saved b5-2d-flights-row.png (6 category classes, white)');
|
||||
await page.screenshot({ path: path.join(SHOT_DIR, 'aircraft-sprites-2d-flights-row.png') });
|
||||
console.log(' saved aircraft-sprites-2d-flights-row.png (6 category classes, white)');
|
||||
console.log(` glyph positions: ${await page.evaluate(() => window.__planeCanvasCoords('flights', 30, 'm'))}`);
|
||||
|
||||
// Tracked tint: commercial tracked plane goes cyan; neighbors stay white.
|
||||
@@ -451,11 +451,11 @@ async function main() {
|
||||
window.__godsEyeView.dataManager.layers.get('flights').module.trackById(icao);
|
||||
}, 'caa002');
|
||||
await sleep(3000);
|
||||
await page.screenshot({ path: path.join(SHOT_DIR, 'b5-2d-tracked-cyan.png') });
|
||||
console.log(' saved b5-2d-tracked-cyan.png (tracked airliner cyan, follow camera)');
|
||||
await page.screenshot({ path: path.join(SHOT_DIR, 'aircraft-sprites-2d-tracked-cyan.png') });
|
||||
console.log(' saved aircraft-sprites-2d-tracked-cyan.png (tracked airliner cyan, follow camera)');
|
||||
|
||||
// ========================================================================
|
||||
// B3 + 3D screenshots — flights cluster
|
||||
// Model scale and 3D screenshots — flights cluster
|
||||
// ========================================================================
|
||||
console.log('\n3D — flights cluster (airplane.glb, per-class scale)');
|
||||
await page.evaluate(() => {
|
||||
@@ -492,15 +492,15 @@ async function main() {
|
||||
|
||||
await sleep(1500);
|
||||
await zoomToRange(900);
|
||||
await page.screenshot({ path: path.join(SHOT_DIR, 'b5-3d-flights-mid.png') });
|
||||
console.log(' saved b5-3d-flights-mid.png (900 m range — near the min-pixel clamp)');
|
||||
await page.screenshot({ path: path.join(SHOT_DIR, 'aircraft-sprites-3d-flights-mid.png') });
|
||||
console.log(' saved aircraft-sprites-3d-flights-mid.png (900 m range — near the min-pixel clamp)');
|
||||
await zoomToRange(450);
|
||||
await page.screenshot({ path: path.join(SHOT_DIR, 'b5-3d-flights-close.png') });
|
||||
console.log(' saved b5-3d-flights-close.png (450 m range: widebody left, airliner center, fastjet right)');
|
||||
await page.screenshot({ path: path.join(SHOT_DIR, 'aircraft-sprites-3d-flights-close.png') });
|
||||
console.log(' saved aircraft-sprites-3d-flights-close.png (450 m range: widebody left, airliner center, fastjet right)');
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// B3 + 3D screenshots — military cluster
|
||||
// Model scale and 3D screenshots — military cluster
|
||||
// ========================================================================
|
||||
if (!hasMilitary) {
|
||||
console.log(` [SKIP] 3D military — no military layer on ${ENGINE}`);
|
||||
@@ -543,11 +543,11 @@ async function main() {
|
||||
// jet.glb at MODEL_SCALE 1 is real-world size — same shot ranges as the
|
||||
// flights cluster (close zoom no longer puts the camera inside a model).
|
||||
await zoomToRange(900);
|
||||
await page.screenshot({ path: path.join(SHOT_DIR, 'b5-3d-military-far.png') });
|
||||
console.log(' saved b5-3d-military-far.png (900 m range: C130/C17 left, F16 center, B744 right)');
|
||||
await page.screenshot({ path: path.join(SHOT_DIR, 'aircraft-sprites-3d-military-far.png') });
|
||||
console.log(' saved aircraft-sprites-3d-military-far.png (900 m range: C130/C17 left, F16 center, B744 right)');
|
||||
await zoomToRange(450);
|
||||
await page.screenshot({ path: path.join(SHOT_DIR, 'b5-3d-military-close.png') });
|
||||
console.log(' saved b5-3d-military-close.png (450 m range: C17 left, F16 center, B744 right)');
|
||||
await page.screenshot({ path: path.join(SHOT_DIR, 'aircraft-sprites-3d-military-close.png') });
|
||||
console.log(' saved aircraft-sprites-3d-military-close.png (450 m range: C17 left, F16 center, B744 right)');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -334,7 +334,7 @@ const runs = [
|
||||
['--panoramax'],
|
||||
],
|
||||
['radio-engine-switch', 'radio-engine-switch', ['radio']],
|
||||
['w2-imagery', 'w2-imagery', ['recent-imagery']],
|
||||
['imagery-contract', 'imagery-contract', ['recent-imagery']],
|
||||
['fire-camera-switch', 'fire-camera-switch', ['alpr-cameras']],
|
||||
[
|
||||
'fire-camera-switch--fires',
|
||||
|
||||
@@ -41,7 +41,7 @@ import path from 'node:path';
|
||||
import puppeteer from 'puppeteer';
|
||||
import { blockPaidQaMaps } from './qa-map-policy.mjs';
|
||||
import sharp from 'sharp';
|
||||
import { installAlprFixture } from './fixtures/w2/alpr.mjs';
|
||||
import { installAlprFixture } from './fixtures/layers/alpr.mjs';
|
||||
import { redact, isExpectedPreviewSetupError } from './qa-browserEvidence.mjs';
|
||||
import {
|
||||
createScreencast,
|
||||
|
||||
@@ -47,7 +47,11 @@ export function isExpectedPreviewSetupError(
|
||||
|
||||
// The runtime evidence gate shares one strict runtime verdict across its main page and
|
||||
// every share-link page. Response diagnostics never retain query credentials.
|
||||
export function watchW1Page(page, report, { name, preview = false, baseUrl }) {
|
||||
export function watchBrowserPage(
|
||||
page,
|
||||
report,
|
||||
{ name, preview = false, baseUrl },
|
||||
) {
|
||||
const errors = (report.browserErrors ??= []);
|
||||
report.expectedPreviewErrors ??= [];
|
||||
report.failedResponses ??= [];
|
||||
|
||||
@@ -2,7 +2,7 @@ import { assertKeylessPreview } from './qa-strict-harness.mjs';
|
||||
import assert from 'node:assert/strict';
|
||||
import { promisify } from 'node:util';
|
||||
import { execFile } from 'node:child_process';
|
||||
import { installCctvFixture } from './fixtures/w2/cctv.mjs';
|
||||
import { installCctvFixture } from './fixtures/layers/cctv.mjs';
|
||||
import { createStrict, localPreview } from './qa-strict-harness.mjs';
|
||||
const url = localPreview(process.argv[2]);
|
||||
url.search = '?engine=navara&welcome=0';
|
||||
@@ -64,7 +64,7 @@ await strict.run({}, async (b) => {
|
||||
await dataManager.setEnabled('cctv', true, { origin: 'user' });
|
||||
window.qaCctv = dataManager.layers.get('cctv').module;
|
||||
window.qaCctv.setParams({
|
||||
selectedCameraId: 'w2-cctv-video',
|
||||
selectedCameraId: 'fixture-cctv-video',
|
||||
showProjection: true,
|
||||
coverageMode: 'viewshed',
|
||||
calibration: { patch: { heightM: 31, pitchDeg: 20 } },
|
||||
@@ -101,11 +101,12 @@ await strict.run({}, async (b) => {
|
||||
params: window.qaCctv.getParams(),
|
||||
plane: !!window.qaCctv
|
||||
._cctvRecordsForTest()
|
||||
.find((r) => r.camera.id === 'w2-cctv-video').projection?.planeEntity,
|
||||
.find((r) => r.camera.id === 'fixture-cctv-video').projection
|
||||
?.planeEntity,
|
||||
projection: (() => {
|
||||
const r = window.qaCctv
|
||||
._cctvRecordsForTest()
|
||||
.find((r) => r.camera.id === 'w2-cctv-video');
|
||||
.find((r) => r.camera.id === 'fixture-cctv-video');
|
||||
return {
|
||||
show: r.projection?.planeEntity?.show === true,
|
||||
visible: r.projection?.planeVisible === true,
|
||||
@@ -123,7 +124,7 @@ await strict.run({}, async (b) => {
|
||||
v.params.coverageMode,
|
||||
);
|
||||
assert.ok(v.same && v.media && v.plane && !v.paused);
|
||||
assert.equal(v.params.selectedCameraId, 'w2-cctv-video');
|
||||
assert.equal(v.params.selectedCameraId, 'fixture-cctv-video');
|
||||
assert.equal(v.params.coverageMode, 'viewshed');
|
||||
assert.equal(catalogs, count);
|
||||
await strict.passed(
|
||||
|
||||
@@ -77,9 +77,9 @@
|
||||
*/
|
||||
|
||||
import puppeteer from 'puppeteer';
|
||||
import { watchW1Page, redact } from './qa-browserEvidence.mjs';
|
||||
import { watchBrowserPage, redact } from './qa-browserEvidence.mjs';
|
||||
import { blockPaidQaMaps } from './qa-map-policy.mjs';
|
||||
import { installCctvFixture } from './fixtures/w2/cctv.mjs';
|
||||
import { installCctvFixture } from './fixtures/layers/cctv.mjs';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
@@ -581,7 +581,7 @@ async function main() {
|
||||
if (terrainRequests.delete(request)) terrainActivityAt = Date.now();
|
||||
});
|
||||
await page.setViewport({ width: 1280, height: 800 });
|
||||
watchW1Page(page, evidence, {
|
||||
watchBrowserPage(page, evidence, {
|
||||
name: 'cctv-legacy',
|
||||
baseUrl: APP_URL,
|
||||
preview: getFlag('--preview'),
|
||||
|
||||
@@ -90,6 +90,41 @@ async function runOn(browser, kind) {
|
||||
content:
|
||||
'* { pointer-events: none !important; } [data-contract-target] { pointer-events: auto !important; }',
|
||||
});
|
||||
// The globe drawn and its tiles loaded: a restored view hides the globe
|
||||
// for a moment after the loading screen goes.
|
||||
await page.evaluate(async () => {
|
||||
const { engine } = window.__godsEyeView;
|
||||
const canvas = engine.host.canvas;
|
||||
const probe = document.createElement('canvas');
|
||||
probe.width = 1;
|
||||
probe.height = 1;
|
||||
const context = probe.getContext('2d', { willReadFrequently: true });
|
||||
const started = performance.now();
|
||||
let settled = 0;
|
||||
while (performance.now() - started < 120_000 && settled < 20) {
|
||||
await new Promise((resolve) => {
|
||||
const remove = engine.frames.postRender.addEventListener(() => {
|
||||
remove();
|
||||
resolve();
|
||||
});
|
||||
engine.render.request('contract-suite-settle');
|
||||
});
|
||||
context.drawImage(
|
||||
canvas,
|
||||
canvas.width / 2,
|
||||
canvas.height / 2,
|
||||
1,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
);
|
||||
const [r, g, b] = context.getImageData(0, 0, 1, 1).data;
|
||||
settled =
|
||||
r + g + b > 60 && engine.surface.tilesLoaded() ? settled + 1 : 0;
|
||||
}
|
||||
});
|
||||
// A real left click and pointer move at a canvas position.
|
||||
await page.exposeFunction('__contractPointer', async (type, x, y) => {
|
||||
const box = await page.evaluate(() => {
|
||||
@@ -127,9 +162,32 @@ async function runOn(browser, kind) {
|
||||
// A real 2D canvas: a sprite atlas the engines can draw and pick.
|
||||
const makeCanvas = (width, height) =>
|
||||
Object.assign(document.createElement('canvas'), { width, height });
|
||||
// RGBA of the last drawn frame (both engines keep their drawing buffer).
|
||||
const scratch = document.createElement('canvas');
|
||||
const context = scratch.getContext('2d', { willReadFrequently: true });
|
||||
const readRegion = (x, y, width, height) => {
|
||||
const canvas = engine.host.canvas;
|
||||
const scale = canvas.width / canvas.clientWidth;
|
||||
scratch.width = width;
|
||||
scratch.height = height;
|
||||
context.clearRect(0, 0, width, height);
|
||||
context.drawImage(
|
||||
canvas,
|
||||
x * scale,
|
||||
y * scale,
|
||||
width * scale,
|
||||
height * scale,
|
||||
0,
|
||||
0,
|
||||
width,
|
||||
height,
|
||||
);
|
||||
return context.getImageData(0, 0, width, height).data;
|
||||
};
|
||||
for (const { name, run } of createContractSuite(engine, {
|
||||
waitFrames,
|
||||
pointer,
|
||||
readRegion,
|
||||
makeCanvas,
|
||||
})) {
|
||||
if (only && !only.some((prefix) => name.startsWith(prefix))) continue;
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
* them real types AMBIENTLY (nothing is ever tracked, 3D stays off), while
|
||||
* honoring the politeness bounds.
|
||||
*
|
||||
* Fetch shim (installed before app code, same pattern as qa-sprites-b5.mjs):
|
||||
* Fetch shim (installed before app code, same pattern as qa-aircraft-sprites.mjs):
|
||||
* - /api/flights → 12 straight-flying planes, category 0
|
||||
* - /api/adsbdb/type/:hex → varied REAL type codes (C172, B744, DH8D, H60,
|
||||
* F16, GLID, B77W, A320, B738, PC12, R44) + one
|
||||
@@ -57,7 +57,7 @@ import { classifyAircraft, CLASS_SCALE_2D } from '../src/data/aircraftClass.js';
|
||||
import { aircraftIcon } from '../src/data/aircraftIcons.js';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Args (same shape as qa-sprites-b5.mjs)
|
||||
// Args (same shape as qa-aircraft-sprites.mjs)
|
||||
// ---------------------------------------------------------------------------
|
||||
const argv = process.argv.slice(2);
|
||||
const getFlag = (name) => argv.includes(name);
|
||||
@@ -308,7 +308,7 @@ async function main() {
|
||||
await page.evaluate(() => window.__godsEyeView.styleManager.orbitController.stop());
|
||||
console.log(' App globals ready.');
|
||||
|
||||
// ---- In-page billboard probe (same as qa-sprites-b5.mjs) ---------------
|
||||
// ---- In-page billboard probe (same as qa-aircraft-sprites.mjs) ---------------
|
||||
// What the flights layer draws, through the engine-neutral probe
|
||||
// (layers.drawn: the layer's own sprite handles) on either engine. The
|
||||
// layer owns no Cesium billboards any more: the contract's batched
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { assertKeylessPreview } from './qa-strict-harness.mjs';
|
||||
import assert from 'node:assert/strict';
|
||||
import { installAlprFixture, cameras } from './fixtures/w2/alpr.mjs';
|
||||
import { firmsFixture, FIRMS_VALID_COUNT } from './fixtures/w2/firms.mjs';
|
||||
import { installAlprFixture, cameras } from './fixtures/layers/alpr.mjs';
|
||||
import { firmsFixture, FIRMS_VALID_COUNT } from './fixtures/layers/firms.mjs';
|
||||
import { createStrict, localPreview } from './qa-strict-harness.mjs';
|
||||
const url = localPreview(process.argv[2]),
|
||||
fire = process.argv.includes('--fires'),
|
||||
|
||||
@@ -9,7 +9,7 @@ import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import puppeteer from 'puppeteer';
|
||||
import sharp from 'sharp';
|
||||
import { watchW1Page, redact } from './qa-browserEvidence.mjs';
|
||||
import { watchBrowserPage, redact } from './qa-browserEvidence.mjs';
|
||||
|
||||
const urlIndex = process.argv.indexOf('--url');
|
||||
if (urlIndex < 0) throw new Error('An explicit --url is required');
|
||||
@@ -87,7 +87,7 @@ async function open(mode = 'normal') {
|
||||
await page.bringToFront();
|
||||
const errors = [];
|
||||
if (extended)
|
||||
watchW1Page(page, report, {
|
||||
watchBrowserPage(page, report, {
|
||||
name: mode,
|
||||
baseUrl: appUrl,
|
||||
preview: process.argv.includes('--preview'),
|
||||
|
||||
@@ -1,204 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Run the contract suite's engine.ground checks in headless Chrome on both
|
||||
* real engines, with what qa-contract-suite.mjs does not give the suite: a
|
||||
* readRegion of the engine canvas (the pixel check) besides the real
|
||||
* pointer (the click pick). Serve the app keyless:
|
||||
*
|
||||
* GOOGLE_MAPS_API_KEY= CESIUM_ION_TOKEN= npm run dev -- --port 4271
|
||||
* node scripts/qa-ground-suite.mjs http://localhost:4271 [--engine=cesium,navara] [--only=ground:]
|
||||
*
|
||||
* Exits 1 when a check fails on any engine.
|
||||
*/
|
||||
import puppeteer from 'puppeteer';
|
||||
|
||||
const BLOCKED_HOSTS =
|
||||
/(^|\.)(googleapis\.com|google\.com|gstatic\.com|cesium\.com)$/;
|
||||
const args = process.argv.slice(2);
|
||||
const origin = args.find((arg) => !arg.startsWith('--'));
|
||||
const option = (name) =>
|
||||
args.find((arg) => arg.startsWith(`--${name}=`))?.slice(name.length + 3);
|
||||
if (!origin) {
|
||||
console.error(
|
||||
'Usage: node scripts/qa-ground-suite.mjs <origin> [--engine=cesium,navara] [--only=prefix]',
|
||||
);
|
||||
process.exit(2);
|
||||
}
|
||||
const engines = (option('engine') ?? 'cesium,navara').split(',');
|
||||
const only = (option('only') ?? 'ground:').split(',');
|
||||
|
||||
async function runOn(browser, kind) {
|
||||
const page = await browser.newPage();
|
||||
await page.setViewport({ width: 1280, height: 800 });
|
||||
const pageErrors = [];
|
||||
page.on('pageerror', (error) => pageErrors.push(error.message));
|
||||
await page.setRequestInterception(true);
|
||||
page.on('request', (request) => {
|
||||
if (BLOCKED_HOSTS.test(new URL(request.url()).hostname)) request.abort();
|
||||
else request.continue();
|
||||
});
|
||||
await page.goto(
|
||||
`${origin}/?welcome=0&engine=${kind}#v=2&lat=10&lon=20&alt=1000000&heading=0&pitch=-90&map=osm&l=`,
|
||||
);
|
||||
await page.waitForFunction(
|
||||
(expected) =>
|
||||
window.__godsEyeView?.engine?.kind === expected &&
|
||||
document.getElementById('loading-screen')?.classList.contains('hidden'),
|
||||
{ timeout: 300_000, polling: 250 },
|
||||
kind,
|
||||
);
|
||||
// The app restores the view in the link with a flight that can start
|
||||
// after the loading screen goes: wait until the camera is there and still,
|
||||
// or that flight lands in the middle of a check.
|
||||
await page.waitForFunction(
|
||||
() => {
|
||||
const { camera } = window.__godsEyeView.engine;
|
||||
return (
|
||||
!camera.flightActive() && Math.abs(camera.height() - 1_000_000) < 1000
|
||||
);
|
||||
},
|
||||
{ timeout: 120_000, polling: 250 },
|
||||
);
|
||||
// The globe drawn and its tiles loaded: a restored view hides the globe
|
||||
// for a moment after the loading screen goes.
|
||||
await page.evaluate(async () => {
|
||||
const { engine } = window.__godsEyeView;
|
||||
const canvas = engine.host.canvas;
|
||||
const probe = document.createElement('canvas');
|
||||
probe.width = 1;
|
||||
probe.height = 1;
|
||||
const context = probe.getContext('2d', { willReadFrequently: true });
|
||||
const started = performance.now();
|
||||
let settled = 0;
|
||||
while (performance.now() - started < 120_000 && settled < 20) {
|
||||
await new Promise((resolve) => {
|
||||
const remove = engine.frames.postRender.addEventListener(() => {
|
||||
remove();
|
||||
resolve();
|
||||
});
|
||||
engine.render.request('ground-suite-settle');
|
||||
});
|
||||
context.drawImage(
|
||||
canvas,
|
||||
canvas.width / 2,
|
||||
canvas.height / 2,
|
||||
1,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
);
|
||||
const [r, g, b] = context.getImageData(0, 0, 1, 1).data;
|
||||
settled =
|
||||
r + g + b > 60 && engine.surface.tilesLoaded() ? settled + 1 : 0;
|
||||
}
|
||||
});
|
||||
await page.evaluate(() =>
|
||||
window.__godsEyeView.engine.host.canvas.setAttribute(
|
||||
'data-contract-target',
|
||||
'',
|
||||
),
|
||||
);
|
||||
await page.addStyleTag({
|
||||
content:
|
||||
'* { pointer-events: none !important; } [data-contract-target] { pointer-events: auto !important; }',
|
||||
});
|
||||
await page.exposeFunction('__contractPointer', async (type, x, y) => {
|
||||
const box = await page.evaluate(() => {
|
||||
const rect =
|
||||
window.__godsEyeView.engine.host.canvas.getBoundingClientRect();
|
||||
return { left: rect.left, top: rect.top };
|
||||
});
|
||||
const at = [box.left + x, box.top + y];
|
||||
if (type === 'click') await page.mouse.click(...at);
|
||||
else {
|
||||
await page.mouse.move(at[0] - 1, at[1]);
|
||||
await page.mouse.move(...at);
|
||||
}
|
||||
});
|
||||
const results = await page.evaluate(async (only) => {
|
||||
const { engine } = window.__godsEyeView;
|
||||
const waitFrames = (count) =>
|
||||
new Promise((resolve) => {
|
||||
let left = count;
|
||||
const remove = engine.frames.postRender.addEventListener(() => {
|
||||
if (--left > 0) return;
|
||||
remove();
|
||||
resolve();
|
||||
});
|
||||
engine.render.request('ground-suite');
|
||||
});
|
||||
const pointer = {
|
||||
click: (x, y) => window.__contractPointer('click', x, y),
|
||||
move: (x, y) => window.__contractPointer('move', x, y),
|
||||
};
|
||||
// RGBA of the last drawn frame (both engines keep their drawing buffer).
|
||||
const scratch = document.createElement('canvas');
|
||||
const context = scratch.getContext('2d', { willReadFrequently: true });
|
||||
const readRegion = (x, y, width, height) => {
|
||||
const canvas = engine.host.canvas;
|
||||
const scale = canvas.width / canvas.clientWidth;
|
||||
scratch.width = width;
|
||||
scratch.height = height;
|
||||
context.clearRect(0, 0, width, height);
|
||||
context.drawImage(
|
||||
canvas,
|
||||
x * scale,
|
||||
y * scale,
|
||||
width * scale,
|
||||
height * scale,
|
||||
0,
|
||||
0,
|
||||
width,
|
||||
height,
|
||||
);
|
||||
return context.getImageData(0, 0, width, height).data;
|
||||
};
|
||||
const makeCanvas = (width, height) =>
|
||||
Object.assign(document.createElement('canvas'), { width, height });
|
||||
const { createContractSuite } =
|
||||
await import('/src/globe/testing/contractSuite.mjs');
|
||||
const outcome = [];
|
||||
for (const { name, run } of createContractSuite(engine, {
|
||||
waitFrames,
|
||||
pointer,
|
||||
readRegion,
|
||||
makeCanvas,
|
||||
})) {
|
||||
if (!only.some((prefix) => name.startsWith(prefix))) continue;
|
||||
try {
|
||||
await run();
|
||||
outcome.push({ name, ok: true });
|
||||
} catch (error) {
|
||||
outcome.push({
|
||||
name,
|
||||
ok: false,
|
||||
error: String(error?.message ?? error),
|
||||
});
|
||||
}
|
||||
}
|
||||
return outcome;
|
||||
}, only);
|
||||
await page.close();
|
||||
return { engine: kind, results, pageErrors };
|
||||
}
|
||||
|
||||
const browser = await puppeteer.launch({ headless: true });
|
||||
let failed = false;
|
||||
try {
|
||||
for (const kind of engines) {
|
||||
const { results, pageErrors } = await runOn(browser, kind);
|
||||
const passed = results.filter((result) => result.ok).length;
|
||||
console.log(`${kind}: ${passed}/${results.length} checks pass`);
|
||||
for (const result of results)
|
||||
if (!result.ok) {
|
||||
failed = true;
|
||||
console.log(` FAIL ${result.name}: ${result.error}`);
|
||||
} else console.log(` ok ${result.name}`);
|
||||
for (const message of pageErrors) console.log(` page error: ${message}`);
|
||||
}
|
||||
} finally {
|
||||
await browser.close();
|
||||
}
|
||||
process.exit(failed ? 1 : 0);
|
||||
@@ -4,7 +4,7 @@
|
||||
// pixels; both pinned dates survive a switch to Cesium and back. The report
|
||||
// and its screenshots go next to the evidence file (or to the temp directory).
|
||||
//
|
||||
// node scripts/qa-w2-imagery.mjs http://127.0.0.1:<PORT>
|
||||
// node scripts/qa-imagery-contract.mjs http://127.0.0.1:<PORT>
|
||||
// [--layers=recent-imagery] [--evidence=FILE]
|
||||
import fs from 'node:fs/promises';
|
||||
import os from 'node:os';
|
||||
@@ -1086,15 +1086,25 @@ check({
|
||||
heavy: true, run: harness({ id: 'D1', script: 'track-regression.mjs', args: ['--url', APP_URL], timeoutMs: 1200000 }),
|
||||
});
|
||||
check({
|
||||
id: 'D2', group: 'D', desc: 'qa-heading-b3 — path-derived display heading',
|
||||
heavy: true, run: harness({ id: 'D2', script: 'qa-heading-b3.mjs', args: ['--url', APP_URL], timeoutMs: 900000 }),
|
||||
id: 'D2',
|
||||
group: 'D',
|
||||
desc: 'qa-aircraft-heading — path-derived display heading',
|
||||
heavy: true,
|
||||
run: harness({
|
||||
id: 'D2',
|
||||
script: 'qa-aircraft-heading.mjs',
|
||||
args: ['--url', APP_URL],
|
||||
timeoutMs: 900000,
|
||||
}),
|
||||
});
|
||||
check({
|
||||
id: 'D3', group: 'D', desc: 'qa-sprites-b5 — per-class billboard silhouettes',
|
||||
id: 'D3',
|
||||
group: 'D',
|
||||
desc: 'qa-aircraft-sprites — per-class billboard silhouettes',
|
||||
heavy: true,
|
||||
run: harness({
|
||||
id: 'D3',
|
||||
script: 'qa-sprites-b5.mjs',
|
||||
script: 'qa-aircraft-sprites.mjs',
|
||||
args: ['--url', APP_URL],
|
||||
timeoutMs: 900000,
|
||||
knownConditions: [{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import { stations, installRadioFixture } from './fixtures/w2/radio.mjs';
|
||||
import { stations, installRadioFixture } from './fixtures/layers/radio.mjs';
|
||||
import { createStrict, localPreview, openPage, waitForEngine } from './qa-strict-harness.mjs';
|
||||
const url = localPreview(process.argv[2]);
|
||||
// The long soak and switch timings live in qa-strict-soak.mjs.
|
||||
|
||||
@@ -15,7 +15,7 @@ import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import puppeteer from 'puppeteer';
|
||||
import { stations } from './fixtures/w2/radio.mjs';
|
||||
import { stations } from './fixtures/layers/radio.mjs';
|
||||
import { isExpectedPreviewSetupError } from './qa-browserEvidence.mjs';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
@@ -44,7 +44,7 @@ import {
|
||||
slope,
|
||||
startSession,
|
||||
} from './qa-strict-session.mjs';
|
||||
import { installRadioFixture, stations } from './fixtures/w2/radio.mjs';
|
||||
import { installRadioFixture, stations } from './fixtures/layers/radio.mjs';
|
||||
import { trafficFixtureResponse } from './traffic-fixtures.mjs';
|
||||
import { POSITION_HISTORY_LIMIT } from '../src/layers/flights/recordPolicy.js';
|
||||
import { heapSizesByType } from './qa/heapSnapshotSizes.mjs';
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import { fromDegrees } from '../src/geo/cartesian3.js';
|
||||
import { createStrict, localPreview, openPage, waitForEngine } from './qa-strict-harness.mjs';
|
||||
import { installTransitFixture } from './fixtures/w2/transit.mjs';
|
||||
import { installTransitFixture } from './fixtures/layers/transit.mjs';
|
||||
import {
|
||||
combinedPayloads,
|
||||
installCombinedFixture,
|
||||
} from './fixtures/w2/combined.mjs';
|
||||
} from './fixtures/layers/combined.mjs';
|
||||
const url = localPreview(process.argv[2]),
|
||||
bus = process.argv.includes('--transit'),
|
||||
id = bus ? 'transit' : 'bikeshare';
|
||||
|
||||
Reference in new issue
Block a user