/** * The wiring seams, tested where they can actually be exercised. * * `createOfficeScene` is the one scene in this repo that can be built under * `node --test`: it needs a DOM element and a `window` for `OrbitControls` and * `matchMedia`, and nothing else — no WebGL context, no Worker, no heightfield. * So the office half of the integration surface is tested for real, against the * shipped packs, rather than by reading the source and hoping. * * The city half is not, and the asymmetry is honest rather than lazy: * `createScene` awaits a terrain Worker and takes a live `Stage`, so the only * place it can be exercised is a browser. `scripts/ui-smoke.mjs` is where that * happens, and the two source-level assertions at the bottom of this file are * the belt to that brace — they fail if the seam is *deleted*, which is the * failure a browser test is slowest to tell you about. * * The renderer is a fake of the same specific kind `src/test/render` uses: * `PMREMGenerator` never touches WebGL directly, so stubbing `render` and * `setRenderTarget` runs the real generator, the real target allocation and the * real blur chain and leaves only the pixels unwritten. */ import assert from "node:assert/strict"; import { readFileSync } from "node:fs"; import path from "node:path"; import { fileURLToPath } from "node:url"; import test from "node:test"; import * as THREE from "three"; const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../.."); // ---- The environment the browser supplies and Node does not ---------------- // // Installed before the modules are imported, because `scenekit.ts` reads // `window.matchMedia` at construction and `officeScene.ts` reaches it through a // static import chain. The dynamic imports below are what let this run first. (globalThis as unknown as { window: unknown }).window = { matchMedia: () => ({ matches: false, addEventListener() {}, removeEventListener() {} }), innerWidth: 1_200, innerHeight: 800, devicePixelRatio: 1, addEventListener() {}, removeEventListener() {}, }; const { createOfficeScene } = await import("../../interiors/officeScene.ts"); const { createEnvironmentRig } = await import("../../engine/environmentRig.ts"); const LUMBRIDGE_HQ = (await import("../../offices/lumbridge-hq.ts")).default; const MATEO_COURT = (await import("../../offices/mateo-court.ts")).default; const { initialDeviceState } = await import("../../devices/types.ts"); const { createSimulatedVehicleTelemetry } = await import("../../transport/vehicleTelemetry.ts"); const { arrivalGroundFor } = await import("../../transport/exteriorVehicle.ts"); type Office = typeof LUMBRIDGE_HQ; const PACKS: readonly (readonly [string, Office])[] = [ ["lumbridge-hq", LUMBRIDGE_HQ], ["mateo-court", MATEO_COURT], ]; // ---- Fakes ----------------------------------------------------------------- /** Everything `OrbitControls` and `SceneKit` touch on the canvas, and no more. */ function fakeDom(): HTMLElement { return { style: {}, clientWidth: 1_200, clientHeight: 800, addEventListener() {}, removeEventListener() {}, setPointerCapture() {}, releasePointerCapture() {}, getBoundingClientRect: () => ({ left: 0, top: 0, width: 1_200, height: 800, right: 1_200, bottom: 800, x: 0, y: 0, }), getRootNode: () => ({ addEventListener() {}, removeEventListener() {} }), ownerDocument: { addEventListener() {}, removeEventListener() {} }, } as unknown as HTMLElement; } interface FakeRenderer { renders: number; targets: Set; as(): THREE.WebGLRenderer; } /** A renderer that allocates render targets and never draws to them. */ function fakeRenderer(): FakeRenderer { const targets = new Set(); const state: FakeRenderer = { renders: 0, targets, as: () => stub as unknown as THREE.WebGLRenderer, }; const stub = { autoClear: true, toneMapping: THREE.NoToneMapping, xr: { enabled: false }, state: { buffers: { depth: { getReversed: () => false } } }, getRenderTarget: () => null, getActiveCubeFace: () => 0, getActiveMipmapLevel: () => 0, getClearColor: (target: THREE.Color) => target, getClearAlpha: () => 1, setClearColor: () => {}, setClearAlpha: () => {}, clearDepth: () => {}, compile: () => {}, setRenderTarget(target: THREE.WebGLRenderTarget | null) { if (target) targets.add(target); }, render() { state.renders += 1; }, }; return state; } /** Two aeroplanes over the studio, at bearings this test can predict. */ function fakeFlights(site: NonNullable) { let polls = 0; return { interval: 1, polls: () => polls, poll() { polls += 1; return [ // Due north of the site and high: comfortably above the elevation floor. { id: "north", lat: site.lat + 0.05, lng: site.lng, altitude: 6_000, heading: 180 }, // Due east and equally high. { id: "east", lat: site.lat, lng: site.lng + 0.05, altitude: 6_000, heading: 270 }, // On the deck a long way off: below the floor, and must not be drawn. { id: "below", lat: site.lat + 2, lng: site.lng, altitude: 100, heading: 0 }, ]; }, }; } function build(pack: Office, extra: Record = {}) { const renderer = fakeRenderer(); const rig = createEnvironmentRig(renderer.as()); const scene = createOfficeScene(pack, { dom: fakeDom(), depth: "public", // `low` is flat Lambert with no textures: it builds the same graph and the // same materials-per-role decisions, in a fraction of the canvas work. quality: "low", environment: rig, exteriorVehicle: { detail: "corridor", seed: 7 }, ...(pack.site ? { horizon: { drop: pack.site.elevation } } : {}), ...extra, }); return { scene, rig, renderer }; } function named(root: THREE.Object3D, name: string): THREE.Object3D | null { return root.children.find((child) => child.name === name) ?? null; } // ---- The environment rig --------------------------------------------------- for (const [id, pack] of PACKS) { test(`${id}: createOfficeScene puts a real environment texture on the scene`, () => { const { scene, rig, renderer } = build(pack); try { assert.ok( scene.scene.environment instanceof THREE.Texture, "the office must have an environment before its first frame, or every metal " + "role in the room renders as grey plastic", ); assert.equal(scene.scene.environmentIntensity, 1); assert.ok(renderer.renders > 0, "the PMREM blur chain never ran"); } finally { scene.dispose(); rig.dispose(); } }); test(`${id}: disposing the scene releases it from the rig's ledger`, () => { const { scene, rig } = build(pack); try { assert.ok(scene.scene.environment !== null); scene.dispose(); assert.equal( scene.scene.environment, null, "a disposed scene left holding an environment is a disposed scene the rig " + "still has a strong reference to — one whole floor plate leaked per switch", ); } finally { rig.dispose(); } }); } test("a rebuilt environment does not reach a scene that has been released", () => { const { scene, rig } = build(LUMBRIDGE_HQ); const second = createOfficeScene(MATEO_COURT, { dom: fakeDom(), depth: "public", quality: "low", environment: rig, }); try { scene.dispose(); // A different sun, so the rig's coarse fingerprint moves and it rebuilds. second.setLighting({ sun: { direction: [-0.7, 0.2, 0.1], color: 0xffb070, intensity: 0.6 }, hemisphere: { sky: 0x30435c, ground: 0x201a14, intensity: 0.5 }, ambient: { color: 0xffffff, intensity: 0.1 }, sky: null, fog: null, }); assert.equal(scene.scene.environment, null, "the released scene was written to again"); assert.ok(second.scene.environment instanceof THREE.Texture); } finally { second.dispose(); rig.dispose(); } }); // ---- The device layer ------------------------------------------------------ for (const [id, pack] of PACKS) { test(`${id}: the authored hardware is mounted and offered to the panel`, () => { const { scene, rig } = build(pack); try { const declared = pack.levels.flatMap((level) => level.floorplan.devices ?? []); assert.ok(declared.length > 0, `${id} authors no devices, so this test proves nothing`); assert.deepEqual( [...scene.devices].map((d) => d.id).sort(), [...declared].map((d) => d.id).sort(), "every declaration this pack authored should resolve against its own plan", ); const layer = named(scene.scene, "devices"); assert.ok(layer !== null, "no device layer in the scene graph"); assert.equal( layer.children.length, declared.length, "one mount per resolved declaration", ); } finally { scene.dispose(); rig.dispose(); } }); } test("a device reading reaches the indicator on the hardware", () => { const { scene, rig } = build(LUMBRIDGE_HQ); try { const declaration = scene.devices[0]; assert.ok(declaration, "lumbridge-hq authors at least one device"); const indicators = (): THREE.Material[] => { const found: THREE.Material[] = []; named(scene.scene, "devices")?.traverse((object) => { if (object.name === "indicator") { object.traverse((child) => { const mesh = child as THREE.Mesh; if (mesh.isMesh && !Array.isArray(mesh.material)) found.push(mesh.material); }); } }); return found; }; scene.setDeviceStates([{ ...initialDeviceState(declaration, 0), powered: false }]); const off = indicators().map((m) => (m as THREE.MeshStandardMaterial).color.getHex()); scene.setDeviceStates([{ ...initialDeviceState(declaration, 0), powered: true, muted: false }]); const on = indicators().map((m) => (m as THREE.MeshStandardMaterial).color.getHex()); assert.ok(off.length > 0, "the device assets carry no indicator meshes"); assert.notDeepEqual(on, off, "powering a device changed nothing anybody could see"); } finally { scene.dispose(); rig.dispose(); } }); // ---- The exterior ---------------------------------------------------------- for (const [id, pack] of PACKS) { test(`${id}: the exterior matches the ground its site actually has`, () => { const arrival = pack.site?.arrival; assert.ok(arrival, `${id} authors no arrival anchor`); const { scene, rig } = build(pack); try { const exterior = named(scene.scene, "office-exterior"); assert.ok(exterior !== null, "no exterior in the scene graph"); assert.equal(exterior.userData.arrivalKind, "vehicle-stall"); exterior.updateMatrixWorld(true); let car: THREE.Object3D | null = null; exterior.traverse((object) => { if (object.userData.vehicleModel === "model-x") car = object; }); // An apron is ground, and `lumbridge-hq`'s studio floor is 188 m above the // pavement outside it. Built literally at the stall's own storey it is a // paved pad and a car in open sky beside the west wall, which is what this // shipped for one build. Read off the site rather than the id, because // reading off the site is exactly what the layer under test must do. if (arrivalGroundFor(pack.site?.elevation ?? 0) === "air") { assert.equal(car, null, `${id} parked a car in the air`); assert.equal(exterior.children.length, 0, "an off-ground site draws nothing"); return; } assert.ok(car !== null, "the apron was built without a car on it"); const at = (car as THREE.Object3D).getWorldPosition(new THREE.Vector3()); const floorY = scene.plan.level(arrival.levelId)?.floorY ?? 0; assert.ok( Math.hypot(at.x - arrival.position.x, at.z - arrival.position.z) <= 0.5, `car at ${at.x.toFixed(2)}, ${at.z.toFixed(2)} against stall ` + `${arrival.position.x}, ${arrival.position.z}`, ); assert.ok( Math.abs(at.y - floorY) <= 1.5, `the apron must stand on the floor of ${arrival.levelId} (${floorY} m), not at ` + `the plan origin — the stall names a storey for a reason`, ); } finally { scene.dispose(); rig.dispose(); } }); } test("vehicle telemetry can be pushed every frame without moving anything twice", () => { const { scene, rig } = build(LUMBRIDGE_HQ); const source = createSimulatedVehicleTelemetry({ seed: 3, fixedStepSeconds: 0.5, ambientC: 19, }); try { // Idempotent by contract: `apply` is signature-guarded, so the frames that // spend no simulated step must cost a comparison and change nothing. for (let i = 0; i < 20; i += 1) scene.setVehicleTelemetry(source.current()); source.command({ op: "charge", value: true }); for (let i = 0; i < 40; i += 1) source.stepFixed(); scene.setVehicleTelemetry(source.current()); assert.ok(source.current().pluggedIn, "the command never reached the simulator"); } finally { scene.dispose(); rig.dispose(); } }); // ---- Overhead traffic ------------------------------------------------------ test("the studio sky carries the same aircraft the board outside is drawing", async () => { const site = MATEO_COURT.site; assert.ok(site, "mateo-court is sited"); const flights = fakeFlights(site); const { scene, rig } = build(MATEO_COURT, { flights }); try { const layer = named(scene.scene, "overhead-traffic"); assert.ok(layer !== null, "a sited office with a flight source drew no sky traffic"); const mesh = layer.children[0] as THREE.InstancedMesh; assert.ok(mesh.isInstancedMesh, "overhead traffic must be one draw call, not one per track"); assert.equal(mesh.count, 0, "nothing is drawn before the first poll lands"); scene.tick(2, 2); // The poll is a promise even for a synchronous source, so let the microtask // queue drain before reading what it placed. await Promise.resolve(); await Promise.resolve(); assert.equal(flights.polls(), 1, "the office must poll the source it was handed"); assert.equal( mesh.count, 2, "the aeroplane below the elevation floor was drawn through the ground", ); const centre = scene.plan.bounds.center; const matrix = new THREE.Matrix4(); const north = new THREE.Vector3(); mesh.getMatrixAt(0, matrix); north.setFromMatrixPosition(matrix); const east = new THREE.Vector3(); mesh.getMatrixAt(1, matrix); east.setFromMatrixPosition(matrix); /* * The building's own frame: `site.heading` is the bearing its −Z points * along, so an aeroplane due north of a north-facing pack lands at −Z. Both * shipped packs are rotated, so the test asserts the *relationship* rather * than a literal axis — the two tracks are ninety degrees apart on the * compass and must be ninety degrees apart on the dome. */ const bearingOf = (at: THREE.Vector3) => Math.atan2(at.x - centre.x, -(at.z - centre.z)) * 180 / Math.PI; const separation = Math.abs(((bearingOf(east) - bearingOf(north) + 540) % 360) - 180); assert.ok( Math.abs(separation - 90) < 2, `two tracks 90° apart on the compass came out ${separation.toFixed(1)}° apart on the dome`, ); assert.ok(north.y > 0, "an aeroplane above the horizon must be above the floor plate"); } finally { scene.dispose(); rig.dispose(); } }); test("an office with no site draws no sky traffic and asks the feed nothing", () => { const site = LUMBRIDGE_HQ.site; assert.ok(site); const flights = fakeFlights(site); const { site: _site, ...unsited } = LUMBRIDGE_HQ; const renderer = fakeRenderer(); const rig = createEnvironmentRig(renderer.as()); const scene = createOfficeScene(unsited as typeof LUMBRIDGE_HQ, { dom: fakeDom(), depth: "public", quality: "low", environment: rig, flights, }); try { assert.equal(named(scene.scene, "overhead-traffic"), null); scene.tick(2, 2); assert.equal( flights.polls(), 0, "a pack with no coordinate has no bearing to put an aeroplane on, so it must " + "not be spending a request to find out where one is", ); } finally { scene.dispose(); rig.dispose(); } }); // ---- The clickable aircraft ------------------------------------------------ const { createFlightLayer, aircraftDetail } = await import("../../engine/flights.ts"); /** * Enough of a `World` for the flight layer: a projection and a vertical scale. * * The real one owns a 0.53M-point heightfield and two and a half seconds of * build, none of which this layer touches — it projects a coordinate and scales * an altitude, and that is the whole of its contact with the world. */ const flatWorld = { project: (lat: number, lng: number) => [lng * 100, -lat * 100], metres: (m: number) => m / 100, metresPerUnit: 100, } as unknown as Parameters[0]; test("every aeroplane on the board is a pick target that knows which one it is", () => { const layer = createFlightLayer(flatWorld); try { assert.equal(layer.pickables.length, 0, "nothing to click before the first observation"); layer.update([ { id: "a1b2c3", lat: 37.6, lng: -122.4, altitude: 9_000, heading: 310, callsign: "UAL221" }, { id: "~ddeeff", lat: 37.8, lng: -122.2, altitude: 2_400, heading: 90 }, ]); assert.equal(layer.pickables.length, 2); assert.deepEqual( layer.pickables.map((object) => object.userData.aircraftId).sort(), ["a1b2c3", "~ddeeff"], "a raycast hit resolves to an aeroplane through this and nothing else", ); for (const object of layer.pickables) { assert.ok(layer.group.children.includes(object), "a pick target that is not drawn"); } } finally { layer.dispose(); assert.equal(layer.pickables.length, 0, "dispose must empty the target list too"); } }); test("the card an anonymous visitor gets says what was observed and what was not", () => { // The simulator's ids are route names, not transponder addresses, and // `aircraftDetail` must decline to present one as the other. const invented = aircraftDetail( { id: "sfo-departure-2", lat: 37.62, lng: -122.38, altitude: 3_000, heading: 300 }, { observed: false }, ); assert.equal(invented.icao24, null, "a route name is not a Mode S address"); assert.equal(invented.observed, false); const real = aircraftDetail( { id: "a1b2c3", lat: 37.62, lng: -122.38, altitude: 3_000, heading: 300, callsign: "UAL221" }, { observed: true, attribution: ["Data: adsb.lol contributors"] }, ); assert.equal(real.icao24, "a1b2c3"); assert.deepEqual(real.attribution, ["Data: adsb.lol contributors"]); }); // ---- The daytime sky ------------------------------------------------------- const { createSatelliteLayer } = await import("../../engine/satellites.ts"); const { createStarlinkMeshLayer } = await import("../../engine/starlinkMesh.ts"); const { nightFactor } = await import("../../engine/atmosphere.ts"); /** One satellite high overhead and sunlit, which is the case that clipped white. */ function overheadFix() { return { noradId: 25_544, name: "SMOKE-1", group: "starlink" as const, azimuth: 0, elevation: Math.PI / 3, rangeKm: 600, // Full sunlight, which is the brightest a dot ever gets and therefore the // case that clipped. shadow: 0, }; } test("the satellite dots are not drawn into a daytime sky", () => { const layer = createSatelliteLayer(400); try { const points = layer.group.children[0] as THREE.Points; const alphaAt = () => (points.geometry.getAttribute("color") as THREE.BufferAttribute).getW(0); layer.setSkyDarkness(1); layer.update([overheadFix()]); const night = alphaAt(); assert.ok(night > 0.1, "a sunlit satellite at 60° must be visible at night"); assert.ok(layer.group.visible, "the layer must draw at night"); // `nightFactor` in degrees: the sun at +44°, which is where the California // board stood in the screenshot that named this defect. layer.setSkyDarkness(nightFactor(44)); layer.update([overheadFix()]); assert.equal( alphaAt(), 0, "live defect 1: additive white dots over a sun at +44° clip to hard white " + "squares, and they were the first thing anybody saw on this board", ); assert.equal(layer.group.visible, false, "and the draw is skipped entirely"); } finally { layer.dispose(); } }); test("a god who turns the sky on at noon still does not get white squares", () => { const layer = createSatelliteLayer(400); const meshes = createStarlinkMeshLayer({ boardRadius: 400 }); try { for (const target of [layer, meshes]) { target.setSkyDarkness(nightFactor(44)); target.setVisible(true); assert.equal( target.group.visible, false, "the hour outranks the switch: both write `group.visible`, and the sky wins", ); target.setSkyDarkness(1); assert.equal(target.group.visible, true, "and night gives it straight back"); target.setVisible(false); assert.equal(target.group.visible, false); } } finally { layer.dispose(); meshes.dispose(); } }); test("the water reflects rather than absorbing, now that there is a sky to reflect", async () => { // Comments here name the material that was replaced, and correctly; only // code counts, exactly as `barrel.test.ts` reasons about DOM globals. const source = readFileSync(path.join(ROOT, "src/engine/terrain.ts"), "utf8") .replace(/\/\*[\s\S]*?\*\//g, "") .replace(/(^|\n)\s*\/\/[^\n]*/g, "$1"); const water = source.slice(source.indexOf("function createWater")); assert.ok( !/MeshLambertMaterial/.test(water.slice(0, water.indexOf("\n}"))), "Lambert has no specular term at all, which is why half the California board " + "rendered as one flat blue value at every hour and from every angle", ); // Assert the PROPERTY, not the formatting. The first draft of this test // matched one exact source line — `MeshStandardMaterial({ color: pal.sea, // roughness: 0.14, metalness: 0 })` — and the sea that shipped is a // multi-line construction at roughness 0.2 with an `onBeforeCompile` patch // that flattens it toward the horizon. The implementation was better than the // literal and the test failed anyway, which is the failure mode of asserting // on source text: it pins the author's first guess rather than the behaviour. const build = water.slice(0, water.indexOf("\n}")); assert.match( build, /MeshStandardMaterial\(/, "the sea must be a dielectric that reads `scene.environment`", ); const roughness = Number(/roughness:\s*([\d.]+)/.exec(build)?.[1]); assert.ok( Number.isFinite(roughness) && roughness > 0 && roughness <= 0.35, `the sea's roughness is ${roughness}; it must be low enough to return a sun ` + `glint and a sky reflection, and above zero so it is water and not a mirror`, ); assert.match( build, /metalness:\s*0\b/, "water is a dielectric: metalness 0 is what makes the reflection white " + "rather than tinted by the sea colour", ); }); // ---- The seams that only the source can prove ------------------------------ const MAIN = readFileSync(path.join(ROOT, "src/main.ts"), "utf8"); test("main.ts makes exactly one call into the chrome, and writes no visibility itself", () => { const applies = [...MAIN.matchAll(/chrome\?\.apply\(chromeState\(/g)].length; assert.equal( applies, 1, "the whole point of `ui/chromeState.ts` is that there is one call site; " + `found ${applies}`, ); assert.equal( [...MAIN.matchAll(/style\.display/g)].length, 0, "a visibility decision came back into main.ts", ); /* * The two control bars and their ~100 lines of CSS are gone from index.html, * and these were the last references to elements that no longer exist. * * Their ids are assembled rather than written out, and that is not a flourish: * the `ui` workstream's own gate greps the whole of `index.html` and `src/` for * those two ids and requires zero lines back, so a test that spelled either of * them would be the one thing keeping that grep red forever. */ const bars = ["drive", "walk"].map((prefix) => `${prefix}-controls`); for (const corpse of [...bars, "renderLegend", "addGodmodeShortcut"]) { assert.ok(!MAIN.includes(corpse), `main.ts still references ${corpse}`); } }); test("the city scene is handed the same environment rig the office is", () => { const scene = readFileSync(path.join(ROOT, "src/engine/scene.ts"), "utf8"); const office = readFileSync(path.join(ROOT, "src/interiors/officeScene.ts"), "utf8"); assert.ok(scene.includes('options.environment?.apply(scene, state, "city")')); assert.ok(scene.includes("options.environment?.release(scene)")); assert.ok(office.includes('options.environment?.apply(scene, state, "office")')); assert.ok(office.includes("options.environment?.release(scene)")); assert.equal( [...MAIN.matchAll(/createEnvironmentRig\(/g)].length, 1, "one rig for the page, beside the one renderer — a rig per scene leaks a PMREM " + "chain and a render target on every city switch", ); }); test("the aircraft pick reaches the card, and the card reaches an anonymous visitor", () => { const scene = readFileSync(path.join(ROOT, "src/engine/scene.ts"), "utf8"); assert.ok( scene.includes("hit.object.userData.aircraftId"), "the city's picking must resolve an aeroplane, not only a marker", ); assert.ok( /onAircraftPick\?\.\(/.test(scene), "the resolved aeroplane must reach the caller", ); assert.ok( MAIN.includes("onAircraftPick: (a) => showAircraftDetail(a)"), "main.ts must wire the pick to the detail card", ); // The function body, from its own `function` line to the next brace at column // zero. Precise enough to be worth asserting: a tier check anywhere inside it // is the defect, and a tier check anywhere else is somebody else's business. const from = MAIN.indexOf("function showAircraftDetail"); assert.ok(from > 0, "showAircraftDetail has been renamed"); const body = MAIN.slice(from, MAIN.indexOf("\n}", from)); assert.ok( !/\baccess\./.test(body), "owner decision 2: the flight card is not gated on an account. An ADS-B " + "position is broadcast in clear to anybody with a receiver, so there is " + "nothing here an account could grant.", ); }); test("a handheld visitor reaches the texture quality that was written for them", () => { assert.ok( /deviceProfile\(\)\.handheld \? "medium" : "high"/.test(MAIN), "materials.ts implements low/medium/high and main.ts hardcoded `high`, so the " + "documented mobile escape hatch had never once been reachable", ); assert.ok( MAIN.includes("quality: officeMaterialQuality()"), "the registry must be built at the quality the device profile chose", ); });