mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-15 07:42:23 +00:00
Undo the reformatting of four camera-slice tests
The camera-port commits ran prettier over four test files that were never prettier-formatted. Restore their earlier text and keep only the camera-port edits.
This commit is contained in:
1 parent
bc69e8a4ee
commit
68855b2089
4 files changed
+804
-2266
No files matched your search
+146
-320
@@ -1,13 +1,7 @@
|
|||||||
import { LayerBindings } from './ui/layerBindings.js';
|
import { LayerBindings } from './ui/layerBindings.js';
|
||||||
import {
|
import { readShellSource, shellMethod } from './testSupport/readShellSource.mjs';
|
||||||
readShellSource,
|
|
||||||
shellMethod,
|
|
||||||
} from './testSupport/readShellSource.mjs';
|
|
||||||
import { readLayerSource } from './testSupport/readLayerSource.mjs';
|
import { readLayerSource } from './testSupport/readLayerSource.mjs';
|
||||||
import {
|
import { enter as cockpitEnter, navigateContext } from './ui/cockpitTrackingController.js';
|
||||||
enter as cockpitEnter,
|
|
||||||
navigateContext,
|
|
||||||
} from './ui/cockpitTrackingController.js';
|
|
||||||
import { CockpitViewController } from './ui/cockpitController.js';
|
import { CockpitViewController } from './ui/cockpitController.js';
|
||||||
import assert from 'node:assert/strict';
|
import assert from 'node:assert/strict';
|
||||||
import fs from 'node:fs';
|
import fs from 'node:fs';
|
||||||
@@ -17,24 +11,11 @@ import { fileURLToPath } from 'node:url';
|
|||||||
|
|
||||||
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
||||||
const ui = readShellSource();
|
const ui = readShellSource();
|
||||||
const firms = readLayerSource(
|
const firms = readLayerSource(path.join(ROOT, 'src', 'data', 'firmsHeatmap.js'));
|
||||||
path.join(ROOT, 'src', 'data', 'firmsHeatmap.js'),
|
const vessels = readLayerSource(path.join(ROOT, 'src', 'data', 'aisLiveVessels.js'));
|
||||||
);
|
const voice = fs.readFileSync(path.join(ROOT, 'src', 'voice', 'gevActions.js'), 'utf8');
|
||||||
const vessels = readLayerSource(
|
const cameraVerbs = fs.readFileSync(path.join(ROOT, 'src', 'cameraVerbs.js'), 'utf8');
|
||||||
path.join(ROOT, 'src', 'data', 'aisLiveVessels.js'),
|
const cockpitTracking = fs.readFileSync(path.join(ROOT, 'src', 'cockpitTracking.js'), 'utf8');
|
||||||
);
|
|
||||||
const voice = fs.readFileSync(
|
|
||||||
path.join(ROOT, 'src', 'voice', 'gevActions.js'),
|
|
||||||
'utf8',
|
|
||||||
);
|
|
||||||
const cameraVerbs = fs.readFileSync(
|
|
||||||
path.join(ROOT, 'src', 'cameraVerbs.js'),
|
|
||||||
'utf8',
|
|
||||||
);
|
|
||||||
const cockpitTracking = fs.readFileSync(
|
|
||||||
path.join(ROOT, 'src', 'cockpitTracking.js'),
|
|
||||||
'utf8',
|
|
||||||
);
|
|
||||||
|
|
||||||
function body(source, pattern, label) {
|
function body(source, pattern, label) {
|
||||||
const name = pattern.source.match(/^(\w+)/)?.[1];
|
const name = pattern.source.match(/^(\w+)/)?.[1];
|
||||||
@@ -50,14 +31,8 @@ function body(source, pattern, label) {
|
|||||||
function ordered(source, needles, label) {
|
function ordered(source, needles, label) {
|
||||||
let previous = -1;
|
let previous = -1;
|
||||||
for (const needle of needles) {
|
for (const needle of needles) {
|
||||||
const match =
|
const match = needle instanceof RegExp ? needle.exec(source.slice(previous + 1)) : null;
|
||||||
needle instanceof RegExp ? needle.exec(source.slice(previous + 1)) : null;
|
const index = needle instanceof RegExp ? (match ? previous + 1 + match.index : -1) : source.indexOf(needle, previous + 1);
|
||||||
const index =
|
|
||||||
needle instanceof RegExp
|
|
||||||
? match
|
|
||||||
? previous + 1 + match.index
|
|
||||||
: -1
|
|
||||||
: source.indexOf(needle, previous + 1);
|
|
||||||
assert.ok(index >= 0, `${label}: missing ${needle}`);
|
assert.ok(index >= 0, `${label}: missing ${needle}`);
|
||||||
assert.ok(index > previous, `${label}: ${needle} is out of order`);
|
assert.ok(index > previous, `${label}: ${needle} is out of order`);
|
||||||
previous = index;
|
previous = index;
|
||||||
@@ -66,16 +41,12 @@ function ordered(source, needles, label) {
|
|||||||
|
|
||||||
test('Cockpit takeover invalidates deferred work before camera cancellation', () => {
|
test('Cockpit takeover invalidates deferred work before camera cancellation', () => {
|
||||||
const enter = cockpitEnter.toString();
|
const enter = cockpitEnter.toString();
|
||||||
ordered(
|
ordered(enter, [
|
||||||
enter,
|
'if (!info || !entity?.position) return false;',
|
||||||
[
|
'this.onCameraTakeover?.();',
|
||||||
'if (!info || !entity?.position) return false;',
|
'cameraOf(this.viewer).cancelFlight();',
|
||||||
'this.onCameraTakeover?.();',
|
'this.viewer.trackedEntity = undefined;',
|
||||||
'cameraOf(this.viewer).cancelFlight();',
|
], 'Cockpit takeover');
|
||||||
'this.viewer.trackedEntity = undefined;',
|
|
||||||
],
|
|
||||||
'Cockpit takeover',
|
|
||||||
);
|
|
||||||
assert.match(
|
assert.match(
|
||||||
ui,
|
ui,
|
||||||
/onCameraTakeover: \(\) =>\s*this\._stampNavigation\(\{\s*cancelPendingSelection: false,?\s*\}\),/,
|
/onCameraTakeover: \(\) =>\s*this\._stampNavigation\(\{\s*cancelPendingSelection: false,?\s*\}\),/,
|
||||||
@@ -109,23 +80,11 @@ test('navigation clears dormant tracker IDs without aborting unrelated layer res
|
|||||||
);
|
);
|
||||||
assert.doesNotMatch(stamp, /cancelPendingRestores\(\)/);
|
assert.doesNotMatch(stamp, /cancelPendingRestores\(\)/);
|
||||||
assert.match(stamp, /flightsLayer\.cancelPendingTrackingRestore\?\.\(\)/);
|
assert.match(stamp, /flightsLayer\.cancelPendingTrackingRestore\?\.\(\)/);
|
||||||
assert.match(
|
assert.match(stamp, /militaryFlightsLayer\.cancelPendingTrackingRestore\?\.\(\)/);
|
||||||
stamp,
|
|
||||||
/militaryFlightsLayer\.cancelPendingTrackingRestore\?\.\(\)/,
|
|
||||||
);
|
|
||||||
assert.match(stamp, /satellitesLayer\.cancelPendingTrackingRestore\?\.\(\)/);
|
assert.match(stamp, /satellitesLayer\.cancelPendingTrackingRestore\?\.\(\)/);
|
||||||
assert.match(
|
assert.match(stamp, /if \(\s*!passivelyClearedShareSelection\s*&&\s*!flightsLayer\.getTrackedInfo\?\.\(\)\s*\)[\s\S]*?selectedFlightsTrackingId: null/);
|
||||||
stamp,
|
assert.match(stamp, /if \(\s*!passivelyClearedShareSelection\s*&&\s*!militaryFlightsLayer\.getTrackedInfo\?\.\(\)\s*\)[\s\S]*?selectedMilitaryTrackingId: null/);
|
||||||
/if \(\s*!passivelyClearedShareSelection\s*&&\s*!flightsLayer\.getTrackedInfo\?\.\(\)\s*\)[\s\S]*?selectedFlightsTrackingId: null/,
|
assert.match(stamp, /if \(\s*!passivelyClearedShareSelection\s*&&\s*!satellitesLayer\.getTrackedInfo\?\.\(\)\s*\)[\s\S]*?selectedSatTrackingId: null/);
|
||||||
);
|
|
||||||
assert.match(
|
|
||||||
stamp,
|
|
||||||
/if \(\s*!passivelyClearedShareSelection\s*&&\s*!militaryFlightsLayer\.getTrackedInfo\?\.\(\)\s*\)[\s\S]*?selectedMilitaryTrackingId: null/,
|
|
||||||
);
|
|
||||||
assert.match(
|
|
||||||
stamp,
|
|
||||||
/if \(\s*!passivelyClearedShareSelection\s*&&\s*!satellitesLayer\.getTrackedInfo\?\.\(\)\s*\)[\s\S]*?selectedSatTrackingId: null/,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('voice Cockpit entry reaches the camera only through stamping seams', () => {
|
test('voice Cockpit entry reaches the camera only through stamping seams', () => {
|
||||||
@@ -145,14 +104,10 @@ test('voice Cockpit entry reaches the camera only through stamping seams', () =>
|
|||||||
/export function enterCockpitWithTracking\(\{[\s\S]*?\n\}\) \{([\s\S]*?)\n\}\n/,
|
/export function enterCockpitWithTracking\(\{[\s\S]*?\n\}\) \{([\s\S]*?)\n\}\n/,
|
||||||
'Cockpit entry transaction',
|
'Cockpit entry transaction',
|
||||||
);
|
);
|
||||||
ordered(
|
ordered(transaction, [
|
||||||
transaction,
|
/if \(\s*!selectedLayer\.trackById\?\.\(selectedTarget\.id, \{\s*origin: selectionOrigin,?\s*\}\)\s*\) \{/,
|
||||||
[
|
'if (!entryError) entered = Boolean(cockpitView.enter());',
|
||||||
/if \(\s*!selectedLayer\.trackById\?\.\(selectedTarget\.id, \{\s*origin: selectionOrigin,?\s*\}\)\s*\) \{/,
|
], 'Cockpit entry transaction');
|
||||||
'if (!entryError) entered = Boolean(cockpitView.enter());',
|
|
||||||
],
|
|
||||||
'Cockpit entry transaction',
|
|
||||||
);
|
|
||||||
// No direct camera control: every mutation goes through a layer tracker or
|
// No direct camera control: every mutation goes through a layer tracker or
|
||||||
// the cockpit controller, both of which stamp.
|
// the cockpit controller, both of which stamp.
|
||||||
assert.doesNotMatch(transaction, /\b(?:viewer\.)?camera\s*(?:\.|\[|=)/);
|
assert.doesNotMatch(transaction, /\b(?:viewer\.)?camera\s*(?:\.|\[|=)/);
|
||||||
@@ -191,41 +146,27 @@ test('voice Cockpit next/previous shares the manual Context navigation path', ()
|
|||||||
/this\._listen\(this\.contextNext, 'click', \(\) =>\s*this\.navigateContext\(1, \{ origin: 'user' \}\),?\s*\);/,
|
/this\._listen\(this\.contextNext, 'click', \(\) =>\s*this\.navigateContext\(1, \{ origin: 'user' \}\),?\s*\);/,
|
||||||
);
|
);
|
||||||
const funnel = navigateContext.toString();
|
const funnel = navigateContext.toString();
|
||||||
assert.match(
|
assert.match(funnel, /const navigationOptions = wasActive\s*\? \{ \.\.\.options, aircraftOnly: true \}\s*: options;/);
|
||||||
funnel,
|
ordered(funnel, [
|
||||||
/const navigationOptions = wasActive\s*\? \{ \.\.\.options, aircraftOnly: true \}\s*: options;/,
|
"const method = direction < 0 ? 'navigatePrevious' : 'navigateNext';",
|
||||||
);
|
'const navigationOptions = wasActive',
|
||||||
ordered(
|
'militaryAwarenessLayer?.[method]?.(navigationOptions)',
|
||||||
funnel,
|
'this._adoptTrackedEntity(performance.now());',
|
||||||
[
|
], 'Cockpit Context navigation funnel');
|
||||||
"const method = direction < 0 ? 'navigatePrevious' : 'navigateNext';",
|
|
||||||
'const navigationOptions = wasActive',
|
|
||||||
'militaryAwarenessLayer?.[method]?.(navigationOptions)',
|
|
||||||
'this._adoptTrackedEntity(performance.now());',
|
|
||||||
],
|
|
||||||
'Cockpit Context navigation funnel',
|
|
||||||
);
|
|
||||||
const navigate = body(
|
const navigate = body(
|
||||||
ui,
|
ui,
|
||||||
/if \(normalized === 'next' \|\| normalized === 'previous'\) \{([\s\S]*?)\n \}/,
|
/if \(normalized === 'next' \|\| normalized === 'previous'\) \{([\s\S]*?)\n \}/,
|
||||||
'controlCockpit navigation branch',
|
'controlCockpit navigation branch',
|
||||||
);
|
);
|
||||||
ordered(
|
ordered(navigate, [
|
||||||
navigate,
|
'this.cockpitView.navigateContext(',
|
||||||
['this.cockpitView.navigateContext(', "normalized === 'next' ? 1 : -1,"],
|
"normalized === 'next' ? 1 : -1,",
|
||||||
'controlCockpit navigation branch',
|
], 'controlCockpit navigation branch');
|
||||||
);
|
|
||||||
// No private camera route around the awareness layer.
|
// No private camera route around the awareness layer.
|
||||||
assert.doesNotMatch(
|
assert.doesNotMatch(navigate, /flyTo|camera|trackById|_runExplicitNavigation/);
|
||||||
navigate,
|
|
||||||
/flyTo|camera|trackById|_runExplicitNavigation/,
|
|
||||||
);
|
|
||||||
// An exhausted cohort is an honest failure, not a silent success.
|
// An exhausted cohort is an honest failure, not a silent success.
|
||||||
assert.match(navigate, /ok: changed,/);
|
assert.match(navigate, /ok: changed,/);
|
||||||
assert.match(
|
assert.match(navigate, /error: changed \? null : 'No further context target was available',/);
|
||||||
navigate,
|
|
||||||
/error: changed \? null : 'No further context target was available',/,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('accepted navigation releases through PR15-aware ownership before flight', () => {
|
test('accepted navigation releases through PR15-aware ownership before flight', () => {
|
||||||
@@ -234,69 +175,37 @@ test('accepted navigation releases through PR15-aware ownership before flight',
|
|||||||
/_runExplicitNavigation\(noun, navigate, releaseOptions = undefined\) \{([\s\S]*?)\n \}/,
|
/_runExplicitNavigation\(noun, navigate, releaseOptions = undefined\) \{([\s\S]*?)\n \}/,
|
||||||
'explicit navigation',
|
'explicit navigation',
|
||||||
);
|
);
|
||||||
ordered(
|
ordered(run, [
|
||||||
run,
|
'cockpitActive: this.isCockpitActive()',
|
||||||
[
|
'stamp: () => this._stampNavigation()',
|
||||||
'cockpitActive: this.isCockpitActive()',
|
'release: () => this._releaseFollowCamera(releaseOptions)',
|
||||||
'stamp: () => this._stampNavigation()',
|
'navigate,',
|
||||||
'release: () => this._releaseFollowCamera(releaseOptions)',
|
], 'explicit navigation');
|
||||||
'navigate,',
|
|
||||||
],
|
|
||||||
'explicit navigation',
|
|
||||||
);
|
|
||||||
const release = body(
|
const release = body(
|
||||||
ui,
|
ui,
|
||||||
/_releaseFollowCamera\(\{[\s\S]*?\} = \{\}\) \{([\s\S]*?)\n \}/,
|
/_releaseFollowCamera\(\{[\s\S]*?\} = \{\}\) \{([\s\S]*?)\n \}/,
|
||||||
'follow release',
|
'follow release',
|
||||||
);
|
);
|
||||||
ordered(
|
ordered(release, [
|
||||||
release,
|
'origin: trackingOrigin',
|
||||||
[
|
'satellitesLayer.stopTracking?.({ origin: trackingOrigin })',
|
||||||
'origin: trackingOrigin',
|
'rocketLaunchesLayer.releaseCameraOwnership?.()',
|
||||||
'satellitesLayer.stopTracking?.({ origin: trackingOrigin })',
|
'this.viewer.trackedEntity = undefined;',
|
||||||
'rocketLaunchesLayer.releaseCameraOwnership?.()',
|
"interruptCameraMotion('explicit-navigation')",
|
||||||
'this.viewer.trackedEntity = undefined;',
|
'this.viewer.camera.cancelFlight();',
|
||||||
"interruptCameraMotion('explicit-navigation')",
|
'this.viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY);',
|
||||||
'this.viewer.camera.cancelFlight();',
|
], 'follow release');
|
||||||
'this.viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY);',
|
assert.match(release, /flightsLayer\.stopTracking\?\.\(\{ origin: trackingOrigin \}\)/);
|
||||||
],
|
assert.match(release, /militaryFlightsLayer\.stopTracking\?\.\(\{ origin: trackingOrigin \}\)/);
|
||||||
'follow release',
|
|
||||||
);
|
|
||||||
assert.match(
|
|
||||||
release,
|
|
||||||
/flightsLayer\.stopTracking\?\.\(\{ origin: trackingOrigin \}\)/,
|
|
||||||
);
|
|
||||||
assert.match(
|
|
||||||
release,
|
|
||||||
/militaryFlightsLayer\.stopTracking\?\.\(\{ origin: trackingOrigin \}\)/,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('validated voice camera destinations share the UI navigation authority facade', () => {
|
test('validated voice camera destinations share the UI navigation authority facade', () => {
|
||||||
assert.match(
|
assert.match(ui, /runImmediateNavigation\(noun, navigate, releaseOptions = undefined\) \{\s*return this\._runExplicitNavigation\(noun, navigate, releaseOptions\);/);
|
||||||
ui,
|
assert.match(voice, /runManagedVoiceNavigation\(\s*styleManager,\s*'camera',\s*'move_camera',\s*navigate,\s*releaseOptions/);
|
||||||
/runImmediateNavigation\(noun, navigate, releaseOptions = undefined\) \{\s*return this\._runExplicitNavigation\(noun, navigate, releaseOptions\);/,
|
assert.match(voice, /runManagedVoiceNavigation\(\s*styleManager,\s*'route',\s*'fly_route',\s*navigate/);
|
||||||
);
|
assert.match(voice, /runManagedVoiceNavigation\(\s*styleManager,\s*'fire',\s*'track_entity'/);
|
||||||
assert.match(
|
assert.match(voice, /runManagedVoiceNavigation\(\s*styleManager,\s*family\.kind,\s*'track_entity'/);
|
||||||
voice,
|
assert.match(voice, /runManagedVoiceNavigation\(\s*styleManager,\s*'frame',\s*'frame_overhead'/);
|
||||||
/runManagedVoiceNavigation\(\s*styleManager,\s*'camera',\s*'move_camera',\s*navigate,\s*releaseOptions/,
|
|
||||||
);
|
|
||||||
assert.match(
|
|
||||||
voice,
|
|
||||||
/runManagedVoiceNavigation\(\s*styleManager,\s*'route',\s*'fly_route',\s*navigate/,
|
|
||||||
);
|
|
||||||
assert.match(
|
|
||||||
voice,
|
|
||||||
/runManagedVoiceNavigation\(\s*styleManager,\s*'fire',\s*'track_entity'/,
|
|
||||||
);
|
|
||||||
assert.match(
|
|
||||||
voice,
|
|
||||||
/runManagedVoiceNavigation\(\s*styleManager,\s*family\.kind,\s*'track_entity'/,
|
|
||||||
);
|
|
||||||
assert.match(
|
|
||||||
voice,
|
|
||||||
/runManagedVoiceNavigation\(\s*styleManager,\s*'frame',\s*'frame_overhead'/,
|
|
||||||
);
|
|
||||||
const trackedVoice = voice.slice(
|
const trackedVoice = voice.slice(
|
||||||
voice.indexOf('async function trackEntity'),
|
voice.indexOf('async function trackEntity'),
|
||||||
voice.indexOf('async function frameOverhead'),
|
voice.indexOf('async function frameOverhead'),
|
||||||
@@ -313,80 +222,47 @@ test('validated voice camera destinations share the UI navigation authority faca
|
|||||||
/export function moveCamera\(args = \{\}, runNavigation = null\) \{([\s\S]*?)\n\}/,
|
/export function moveCamera\(args = \{\}, runNavigation = null\) \{([\s\S]*?)\n\}/,
|
||||||
'move camera',
|
'move camera',
|
||||||
);
|
);
|
||||||
ordered(
|
ordered(move, [
|
||||||
move,
|
"if (!['orbit', 'pan', 'tilt', 'rotate', 'rotate_globe'].includes(motion))",
|
||||||
[
|
'const start = () => {',
|
||||||
"if (!['orbit', 'pan', 'tilt', 'rotate', 'rotate_globe'].includes(motion))",
|
/const preserveCameraFlight =\s*motion === 'orbit'/,
|
||||||
'const start = () => {',
|
'runNavigation(start, { preserveCameraFlight })',
|
||||||
/const preserveCameraFlight =\s*motion === 'orbit'/,
|
], 'move validation before handoff');
|
||||||
'runNavigation(start, { preserveCameraFlight })',
|
|
||||||
],
|
|
||||||
'move validation before handoff',
|
|
||||||
);
|
|
||||||
|
|
||||||
const route = body(
|
const route = body(
|
||||||
cameraVerbs,
|
cameraVerbs,
|
||||||
/export function flyRoute\(\s*annoList,\s*args = \{\},\s*floorFn = null,\s*runNavigation = null,\s*warmFn = null,?\s*\) \{([\s\S]*?)\n\}/,
|
/export function flyRoute\(\s*annoList,\s*args = \{\},\s*floorFn = null,\s*runNavigation = null,\s*warmFn = null,?\s*\) \{([\s\S]*?)\n\}/,
|
||||||
'fly route',
|
'fly route',
|
||||||
);
|
);
|
||||||
ordered(
|
ordered(route, [
|
||||||
route,
|
'if (!routes.length)',
|
||||||
[
|
'const pts = route.path.map',
|
||||||
'if (!routes.length)',
|
'const start = () => {',
|
||||||
'const pts = route.path.map',
|
/return typeof runNavigation === 'function'\s*\? runNavigation\(start\)\s*: start\(\);/,
|
||||||
'const start = () => {',
|
], 'route validation before handoff');
|
||||||
/return typeof runNavigation === 'function'\s*\? runNavigation\(start\)\s*: start\(\);/,
|
|
||||||
],
|
|
||||||
'route validation before handoff',
|
|
||||||
);
|
|
||||||
// The corridor warm is injected the same way the floor READ is — the dolly
|
// The corridor warm is injected the same way the floor READ is — the dolly
|
||||||
// never reaches into the data layer itself, and the voice dispatch is the one
|
// never reaches into the data layer itself, and the voice dispatch is the one
|
||||||
// place that binds both.
|
// place that binds both.
|
||||||
assert.match(
|
assert.match(voice, /\(lat, lon\) => floorServices\.cachedGroundFloor\(lat, lon\),[\s\S]{0,200}?\(cells\) => floorServices\.warmGroundFloor\(cells\),/);
|
||||||
voice,
|
|
||||||
/\(lat, lon\) => floorServices\.cachedGroundFloor\(lat, lon\),[\s\S]{0,200}?\(cells\) => floorServices\.warmGroundFloor\(cells\),/,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('deferred search releases only after its final authority check', () => {
|
test('deferred search releases only after its final authority check', () => {
|
||||||
const search = fs.readFileSync(
|
const search = fs.readFileSync(path.join(ROOT, 'src', 'ui', 'locationSearch.js'), 'utf8');
|
||||||
path.join(ROOT, 'src', 'ui', 'locationSearch.js'),
|
ordered(search, [
|
||||||
'utf8',
|
'const authority = this.begin();',
|
||||||
);
|
'this.onStart?.(authority);',
|
||||||
ordered(
|
'await this.search(query',
|
||||||
search,
|
'beforeFly: () => current() && this.beforeFly(authority)',
|
||||||
[
|
'if (!current() || controller.signal.aborted) return;',
|
||||||
'const authority = this.begin();',
|
'destination?.cancelled',
|
||||||
'this.onStart?.(authority);',
|
'finally',
|
||||||
'await this.search(query',
|
'this.onSettled?.(authority)',
|
||||||
'beforeFly: () => current() && this.beforeFly(authority)',
|
], 'deferred search component');
|
||||||
'if (!current() || controller.signal.aborted) return;',
|
assert.match(ui, /begin: \(\) => this\._beginDeferredNavigation\('location'\)/);
|
||||||
'destination?.cancelled',
|
assert.match(ui, /beforeFly: \(generation\) => this\._reassertNavigationHandoff\(generation\)/);
|
||||||
'finally',
|
assert.match(ui, /isCurrent: \(generation\) =>\s*!this\._disposed && generation === this\._navigationGeneration/);
|
||||||
'this.onSettled?.(authority)',
|
assert.match(ui, /change\.type === 'settled'\)\s*this\._settleLocationSearchUi\(change\.generation, \{\s*keepQuery: state\.status === 'missing' \|\| state\.status === 'failed',?\s*\}\)/);
|
||||||
],
|
assert.doesNotMatch(search.slice(0, search.indexOf('await this.search')), /_releaseFollowCamera/);
|
||||||
'deferred search component',
|
|
||||||
);
|
|
||||||
assert.match(
|
|
||||||
ui,
|
|
||||||
/begin: \(\) => this\._beginDeferredNavigation\('location'\)/,
|
|
||||||
);
|
|
||||||
assert.match(
|
|
||||||
ui,
|
|
||||||
/beforeFly: \(generation\) => this\._reassertNavigationHandoff\(generation\)/,
|
|
||||||
);
|
|
||||||
assert.match(
|
|
||||||
ui,
|
|
||||||
/isCurrent: \(generation\) =>\s*!this\._disposed && generation === this\._navigationGeneration/,
|
|
||||||
);
|
|
||||||
assert.match(
|
|
||||||
ui,
|
|
||||||
/change\.type === 'settled'\)\s*this\._settleLocationSearchUi\(change\.generation, \{\s*keepQuery: state\.status === 'missing' \|\| state\.status === 'failed',?\s*\}\)/,
|
|
||||||
);
|
|
||||||
assert.doesNotMatch(
|
|
||||||
search.slice(0, search.indexOf('await this.search')),
|
|
||||||
/_releaseFollowCamera/,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('a direct globe gesture retires delayed camera and selection restore only', () => {
|
test('a direct globe gesture retires delayed camera and selection restore only', () => {
|
||||||
@@ -411,115 +287,77 @@ test('a direct globe gesture retires delayed camera and selection restore only',
|
|||||||
/_stampNavigation\(\{ cancelPendingSelection = true[^)]*\} = \{\}\) \{([\s\S]*?)\n \}/,
|
/_stampNavigation\(\{ cancelPendingSelection = true[^)]*\} = \{\}\) \{([\s\S]*?)\n \}/,
|
||||||
'navigation stamp',
|
'navigation stamp',
|
||||||
);
|
);
|
||||||
assert.match(
|
assert.match(stamp, /if \(cancelPendingSelection\) \{[\s\S]*?cancelPendingTrackingRestore/);
|
||||||
stamp,
|
|
||||||
/if \(cancelPendingSelection\) \{[\s\S]*?cancelPendingTrackingRestore/,
|
|
||||||
);
|
|
||||||
assert.doesNotMatch(stamp, /cancelPendingRestores\(\)/);
|
assert.doesNotMatch(stamp, /cancelPendingRestores\(\)/);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('newer navigation, reset, Cockpit, and teardown share one generation', () => {
|
test('newer navigation, reset, Cockpit, and teardown share one generation', () => {
|
||||||
assert.equal((ui.match(/_navigationGeneration \+= 1/g) || []).length, 1);
|
assert.equal((ui.match(/_navigationGeneration \+= 1/g) || []).length, 1);
|
||||||
const reset = body(ui, /resetToGlobeView\(\) \{([\s\S]*?)\n \}/, 'reset');
|
const reset = body(ui, /resetToGlobeView\(\) \{([\s\S]*?)\n \}/, 'reset');
|
||||||
ordered(
|
ordered(reset, [
|
||||||
reset,
|
'if (this._globeResetPromise) return this._globeResetPromise;',
|
||||||
[
|
'this._stampNavigation();',
|
||||||
'if (this._globeResetPromise) return this._globeResetPromise;',
|
"interruptCameraMotion('reset-globe')",
|
||||||
'this._stampNavigation();',
|
], 'reset supersession');
|
||||||
"interruptCameraMotion('reset-globe')",
|
|
||||||
],
|
|
||||||
'reset supersession',
|
|
||||||
);
|
|
||||||
const dispose = body(ui, /async dispose\(\) \{([\s\S]*?)\n \}/, 'dispose');
|
const dispose = body(ui, /async dispose\(\) \{([\s\S]*?)\n \}/, 'dispose');
|
||||||
ordered(
|
ordered(dispose, [
|
||||||
dispose,
|
'this._disposed = true;',
|
||||||
[
|
'this._layerBindings.stop();',
|
||||||
'this._disposed = true;',
|
'this._navigation.destroy();',
|
||||||
'this._layerBindings.stop();',
|
'await this._contextControls.restoreForDisposal();',
|
||||||
'this._navigation.destroy();',
|
], 'dispose invalidation');
|
||||||
'await this._contextControls.restoreForDisposal();',
|
|
||||||
],
|
|
||||||
'dispose invalidation',
|
|
||||||
);
|
|
||||||
assert.match(shellMethod('destroy').toString(), /this\._stampNavigation\(\)/);
|
assert.match(shellMethod('destroy').toString(), /this\._stampNavigation\(\)/);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('teardown synchronously closes immediate camera entry points', () => {
|
test('teardown synchronously closes immediate camera entry points', () => {
|
||||||
const dispose = body(ui, /async dispose\(\) \{([\s\S]*?)\n \}/, 'dispose');
|
const dispose = body(ui, /async dispose\(\) \{([\s\S]*?)\n \}/, 'dispose');
|
||||||
ordered(
|
ordered(dispose, [
|
||||||
dispose,
|
'this._disposed = true;',
|
||||||
[
|
'this._layerBindings.stop();',
|
||||||
'this._disposed = true;',
|
'await this._contextControls.restoreForDisposal();',
|
||||||
'this._layerBindings.stop();',
|
], 'synchronous teardown barrier');
|
||||||
'await this._contextControls.restoreForDisposal();',
|
ordered(LayerBindings.prototype.stop.toString(), [
|
||||||
],
|
'this._disposed = true;',
|
||||||
'synchronous teardown barrier',
|
'this._removeCctvRequestFocusListener?.();',
|
||||||
);
|
'this._removeWorldRequestFocusListener?.();',
|
||||||
ordered(
|
'this._navigationOwnerChangedRemover?.();',
|
||||||
LayerBindings.prototype.stop.toString(),
|
], 'camera bindings close synchronously');
|
||||||
[
|
|
||||||
'this._disposed = true;',
|
|
||||||
'this._removeCctvRequestFocusListener?.();',
|
|
||||||
'this._removeWorldRequestFocusListener?.();',
|
|
||||||
'this._navigationOwnerChangedRemover?.();',
|
|
||||||
],
|
|
||||||
'camera bindings close synchronously',
|
|
||||||
);
|
|
||||||
|
|
||||||
const navigation = body(
|
const navigation = body(
|
||||||
ui,
|
ui,
|
||||||
/_runExplicitNavigation\(noun, navigate, releaseOptions = undefined\) \{([\s\S]*?)\n \}/,
|
/_runExplicitNavigation\(noun, navigate, releaseOptions = undefined\) \{([\s\S]*?)\n \}/,
|
||||||
'explicit navigation',
|
'explicit navigation',
|
||||||
);
|
);
|
||||||
ordered(
|
ordered(navigation, [
|
||||||
navigation,
|
'disposed: this._disposed',
|
||||||
[
|
'release: () => this._releaseFollowCamera(releaseOptions)',
|
||||||
'disposed: this._disposed',
|
'navigate,',
|
||||||
'release: () => this._releaseFollowCamera(releaseOptions)',
|
], 'disposed navigation guard');
|
||||||
'navigate,',
|
|
||||||
],
|
|
||||||
'disposed navigation guard',
|
|
||||||
);
|
|
||||||
|
|
||||||
const cctvFocus = body(
|
const cctvFocus = body(
|
||||||
ui,
|
ui,
|
||||||
/_runExplicitCctvFocus\(activate, focus\) \{([\s\S]*?)\n \}/,
|
/_runExplicitCctvFocus\(activate, focus\) \{([\s\S]*?)\n \}/,
|
||||||
'explicit CCTV focus',
|
'explicit CCTV focus',
|
||||||
);
|
);
|
||||||
ordered(
|
ordered(cctvFocus, [
|
||||||
cctvFocus,
|
'if (this._disposed) return false;',
|
||||||
['if (this._disposed) return false;', 'const cameraId = activate();'],
|
'const cameraId = activate();',
|
||||||
'disposed CCTV activation guard',
|
], 'disposed CCTV activation guard');
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('teardown refuses deferred location work before geocoding begins', () => {
|
test('teardown refuses deferred location work before geocoding begins', () => {
|
||||||
const deferred = body(
|
const deferred = body(ui, /_beginDeferredNavigation\(noun = 'location', \{ cancelPendingSelection = true \} = \{\}\) \{([\s\S]*?)\n \}/, 'deferred navigation');
|
||||||
ui,
|
|
||||||
/_beginDeferredNavigation\(noun = 'location', \{ cancelPendingSelection = true \} = \{\}\) \{([\s\S]*?)\n \}/,
|
|
||||||
'deferred navigation',
|
|
||||||
);
|
|
||||||
assert.match(deferred, /disposed: this\._disposed/);
|
assert.match(deferred, /disposed: this\._disposed/);
|
||||||
const search = fs.readFileSync(
|
const search = fs.readFileSync(path.join(ROOT, 'src', 'ui', 'locationSearch.js'), 'utf8');
|
||||||
path.join(ROOT, 'src', 'ui', 'locationSearch.js'),
|
ordered(search, [
|
||||||
'utf8',
|
'if (!query || this.destroyed) return;',
|
||||||
);
|
'const authority = this.begin();',
|
||||||
ordered(
|
'if (authority === false)',
|
||||||
search,
|
'this.input?.blur();',
|
||||||
[
|
'this.onStart?.(authority);',
|
||||||
'if (!query || this.destroyed) return;',
|
'await this.search(query',
|
||||||
'const authority = this.begin();',
|
], 'disposed search refusal');
|
||||||
'if (authority === false)',
|
assert.match(search, /if \(authority === false\) \{[\s\S]*?return;[\s\S]*?\}\s*this\.controller/);
|
||||||
'this.input?.blur();',
|
|
||||||
'this.onStart?.(authority);',
|
|
||||||
'await this.search(query',
|
|
||||||
],
|
|
||||||
'disposed search refusal',
|
|
||||||
);
|
|
||||||
assert.match(
|
|
||||||
search,
|
|
||||||
/if \(authority === false\) \{[\s\S]*?return;[\s\S]*?\}\s*this\.controller/,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('refused canned destinations commit no location or POI state', () => {
|
test('refused canned destinations commit no location or POI state', () => {
|
||||||
@@ -528,15 +366,11 @@ test('refused canned destinations commit no location or POI state', () => {
|
|||||||
['poi', /_onPoiClick\(cityId, poiIndex\) \{([\s\S]*?)\n \}/],
|
['poi', /_onPoiClick\(cityId, poiIndex\) \{([\s\S]*?)\n \}/],
|
||||||
]) {
|
]) {
|
||||||
const handler = body(ui, pattern, name);
|
const handler = body(ui, pattern, name);
|
||||||
ordered(
|
ordered(handler, [
|
||||||
handler,
|
'this._flyWithTransition(',
|
||||||
[
|
'if (result === false) return;',
|
||||||
'this._flyWithTransition(',
|
'this._setActiveLocation(cityId);',
|
||||||
'if (result === false) return;',
|
], name);
|
||||||
'this._setActiveLocation(cityId);',
|
|
||||||
],
|
|
||||||
name,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -558,15 +392,11 @@ test('vessel and fire layers announce valid clicks and never fly cameras', () =>
|
|||||||
/inputFor\(viewer\)\.onClick\(\(event\) => \{([\s\S]*?)\n \}\);/,
|
/inputFor\(viewer\)\.onClick\(\(event\) => \{([\s\S]*?)\n \}\);/,
|
||||||
'vessel click',
|
'vessel click',
|
||||||
);
|
);
|
||||||
ordered(
|
ordered(vesselClick, [
|
||||||
vesselClick,
|
"isOwnedByOtherLayer('ais-live-vessels', pickedId)",
|
||||||
[
|
'_vesselOverlayHost.hitTest?.(',
|
||||||
"isOwnedByOtherLayer('ais-live-vessels', pickedId)",
|
'selectAndFocusVessel(record)',
|
||||||
'_vesselOverlayHost.hitTest?.(',
|
], 'vessel sibling ownership');
|
||||||
'selectAndFocusVessel(record)',
|
|
||||||
],
|
|
||||||
'vessel sibling ownership',
|
|
||||||
);
|
|
||||||
const vesselFocus = body(
|
const vesselFocus = body(
|
||||||
vessels,
|
vessels,
|
||||||
/function selectAndFocusVessel\(record\) \{([\s\S]*?)\n \}/,
|
/function selectAndFocusVessel\(record\) \{([\s\S]*?)\n \}/,
|
||||||
@@ -578,13 +408,9 @@ test('vessel and fire layers announce valid clicks and never fly cameras', () =>
|
|||||||
/input\.onClick\(\(event\) => \{([\s\S]*?)\n \}\);/,
|
/input\.onClick\(\(event\) => \{([\s\S]*?)\n \}\);/,
|
||||||
'fire click',
|
'fire click',
|
||||||
);
|
);
|
||||||
ordered(
|
ordered(fireClick, [
|
||||||
fireClick,
|
'isOwnedByOtherLayer(id, pickedId)',
|
||||||
[
|
'overlayHost.hitTest?.(',
|
||||||
'isOwnedByOtherLayer(id, pickedId)',
|
'selectAndFocusFire(carded)',
|
||||||
'overlayHost.hitTest?.(',
|
], 'fire sibling ownership');
|
||||||
'selectAndFocusFire(carded)',
|
|
||||||
],
|
|
||||||
'fire sibling ownership',
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
File diff suppressed because it is too large.
Load diff
+464
-1166
File diff suppressed because it is too large.
Load diff
+30
-123
@@ -22,28 +22,16 @@ function stubCamera() {
|
|||||||
return {
|
return {
|
||||||
calls,
|
calls,
|
||||||
heading: Cesium.Math.toRadians(45),
|
heading: Cesium.Math.toRadians(45),
|
||||||
cancelFlight() {
|
cancelFlight() { calls.cancelFlight += 1; },
|
||||||
calls.cancelFlight += 1;
|
flyToBoundingSphere(sphere, options) { calls.flights.push({ sphere, options }); },
|
||||||
},
|
|
||||||
flyToBoundingSphere(sphere, options) {
|
|
||||||
calls.flights.push({ sphere, options });
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
test('a focus request carries the clicked target to any registered listener', () => {
|
test('a focus request carries the clicked target to any registered listener', () => {
|
||||||
const target = new EventTarget();
|
const target = new EventTarget();
|
||||||
const seen = [];
|
const seen = [];
|
||||||
const dispose = registerWorldFocusRequestListener(target, (event) =>
|
const dispose = registerWorldFocusRequestListener(target, (event) => seen.push(event.detail));
|
||||||
seen.push(event.detail),
|
assert.equal(requestWorldFocus({ kind: 'vessel', position: POSITION, id: '123' }, target), true);
|
||||||
);
|
|
||||||
assert.equal(
|
|
||||||
requestWorldFocus(
|
|
||||||
{ kind: 'vessel', position: POSITION, id: '123' },
|
|
||||||
target,
|
|
||||||
),
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
assert.equal(seen.length, 1);
|
assert.equal(seen.length, 1);
|
||||||
assert.equal(seen[0].kind, 'vessel');
|
assert.equal(seen[0].kind, 'vessel');
|
||||||
assert.equal(seen[0].id, '123');
|
assert.equal(seen[0].id, '123');
|
||||||
@@ -57,9 +45,7 @@ test('a focus request carries the clicked target to any registered listener', ()
|
|||||||
test('requests without a kind or a position are dropped, never dispatched', () => {
|
test('requests without a kind or a position are dropped, never dispatched', () => {
|
||||||
const target = new EventTarget();
|
const target = new EventTarget();
|
||||||
let count = 0;
|
let count = 0;
|
||||||
target.addEventListener(WORLD_FOCUS_REQUEST_EVENT, () => {
|
target.addEventListener(WORLD_FOCUS_REQUEST_EVENT, () => { count += 1; });
|
||||||
count += 1;
|
|
||||||
});
|
|
||||||
assert.equal(requestWorldFocus({ position: POSITION }, target), false);
|
assert.equal(requestWorldFocus({ position: POSITION }, target), false);
|
||||||
assert.equal(requestWorldFocus({ kind: 'vessel' }, target), false);
|
assert.equal(requestWorldFocus({ kind: 'vessel' }, target), false);
|
||||||
assert.equal(requestWorldFocus(null, target), false);
|
assert.equal(requestWorldFocus(null, target), false);
|
||||||
@@ -89,95 +75,51 @@ test('a refusing policy (cockpit owns the camera) never reaches the flight', ()
|
|||||||
const result = routeWorldFocusRequest(
|
const result = routeWorldFocusRequest(
|
||||||
{ detail: { kind: 'vessel', id: '123', position: POSITION } },
|
{ detail: { kind: 'vessel', id: '123', position: POSITION } },
|
||||||
() => false,
|
() => false,
|
||||||
() => {
|
() => { flights += 1; },
|
||||||
flights += 1;
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
assert.equal(result, false);
|
assert.equal(result, false);
|
||||||
assert.equal(flights, 0);
|
assert.equal(flights, 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('malformed requests are inert', () => {
|
test('malformed requests are inert', () => {
|
||||||
assert.equal(
|
assert.equal(routeWorldFocusRequest(null, () => 'x', () => 'y'), false);
|
||||||
routeWorldFocusRequest(
|
assert.equal(routeWorldFocusRequest({ detail: { kind: 'vessel' } }, () => 'x', () => 'y'), false);
|
||||||
null,
|
|
||||||
() => 'x',
|
|
||||||
() => 'y',
|
|
||||||
),
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
assert.equal(
|
|
||||||
routeWorldFocusRequest(
|
|
||||||
{ detail: { kind: 'vessel' } },
|
|
||||||
() => 'x',
|
|
||||||
() => 'y',
|
|
||||||
),
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// A request that cannot fly must never cost the user their tracking: the UI
|
// A request that cannot fly must never cost the user their tracking: the UI
|
||||||
// policy releases the follow camera, so validation happens BEFORE it runs.
|
// policy releases the follow camera, so validation happens BEFORE it runs.
|
||||||
test('an unflyable request never reaches the release policy', () => {
|
test('an unflyable request never reaches the release policy', () => {
|
||||||
const unflyable = [
|
const unflyable = [
|
||||||
{ kind: 'plane', position: POSITION }, // no framing for this kind
|
{ kind: 'plane', position: POSITION }, // no framing for this kind
|
||||||
{ kind: 'vessel', position: { x: NaN, y: 0, z: 0 } }, // malformed position
|
{ kind: 'vessel', position: { x: NaN, y: 0, z: 0 } }, // malformed position
|
||||||
{ kind: 'vessel', position: { x: 0, y: 0 } }, // truthy but incomplete
|
{ kind: 'vessel', position: { x: 0, y: 0 } }, // truthy but incomplete
|
||||||
{ kind: 'vessel', id: '123', position: { x: 0, y: 0, z: 0 } }, // ECEF origin
|
{ kind: 'vessel', id: '123', position: { x: 0, y: 0, z: 0 } }, // ECEF origin
|
||||||
{ kind: 'fire', id: 'fire-1', position: { x: 1, y: 1, z: 1 } }, // finite but inside Earth
|
{ kind: 'fire', id: 'fire-1', position: { x: 1, y: 1, z: 1 } }, // finite but inside Earth
|
||||||
{ kind: 'vessel', position: 'somewhere' },
|
{ kind: 'vessel', position: 'somewhere' },
|
||||||
{ kind: 'vessel', position: POSITION }, // missing owner id
|
{ kind: 'vessel', position: POSITION }, // missing owner id
|
||||||
{ position: POSITION },
|
{ position: POSITION },
|
||||||
];
|
];
|
||||||
for (const detail of unflyable) {
|
for (const detail of unflyable) {
|
||||||
assert.equal(
|
assert.equal(isValidWorldFocusTarget(detail), false, JSON.stringify(detail));
|
||||||
isValidWorldFocusTarget(detail),
|
|
||||||
false,
|
|
||||||
JSON.stringify(detail),
|
|
||||||
);
|
|
||||||
let policyRuns = 0;
|
let policyRuns = 0;
|
||||||
const result = routeWorldFocusRequest(
|
const result = routeWorldFocusRequest(
|
||||||
{ detail },
|
{ detail },
|
||||||
() => {
|
() => { policyRuns += 1; return 'released'; },
|
||||||
policyRuns += 1;
|
|
||||||
return 'released';
|
|
||||||
},
|
|
||||||
() => 'flew',
|
() => 'flew',
|
||||||
);
|
);
|
||||||
assert.equal(result, false, JSON.stringify(detail));
|
assert.equal(result, false, JSON.stringify(detail));
|
||||||
assert.equal(
|
assert.equal(policyRuns, 0, `the policy must not run for ${JSON.stringify(detail)}`);
|
||||||
policyRuns,
|
|
||||||
0,
|
|
||||||
`the policy must not run for ${JSON.stringify(detail)}`,
|
|
||||||
);
|
|
||||||
// Nor may such a request be dispatched in the first place.
|
// Nor may such a request be dispatched in the first place.
|
||||||
assert.equal(requestWorldFocus(detail, new EventTarget()), false);
|
assert.equal(requestWorldFocus(detail, new EventTarget()), false);
|
||||||
}
|
}
|
||||||
assert.equal(
|
assert.equal(isValidWorldFocusTarget({ kind: 'vessel', id: '123', position: POSITION }), true);
|
||||||
isValidWorldFocusTarget({ kind: 'vessel', id: '123', position: POSITION }),
|
assert.equal(isValidWorldFocusTarget({ kind: 'fire', id: 'fire-1', position: POSITION }), true);
|
||||||
true,
|
|
||||||
);
|
|
||||||
assert.equal(
|
|
||||||
isValidWorldFocusTarget({ kind: 'fire', id: 'fire-1', position: POSITION }),
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('the transfer flight supersedes any flight in progress and keeps the operator heading', () => {
|
test('the transfer flight supersedes any flight in progress and keeps the operator heading', () => {
|
||||||
const camera = stubCamera();
|
const camera = stubCamera();
|
||||||
assert.equal(
|
assert.equal(flyToWorldTarget(bindFakeCesiumCamera({ camera }), { kind: 'vessel', id: '123', position: POSITION }), true);
|
||||||
flyToWorldTarget(bindFakeCesiumCamera({ camera }), {
|
assert.equal(camera.calls.cancelFlight, 1, 'a prior flight must be cancelled, not queued');
|
||||||
kind: 'vessel',
|
|
||||||
id: '123',
|
|
||||||
position: POSITION,
|
|
||||||
}),
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
assert.equal(
|
|
||||||
camera.calls.cancelFlight,
|
|
||||||
1,
|
|
||||||
'a prior flight must be cancelled, not queued',
|
|
||||||
);
|
|
||||||
assert.equal(camera.calls.flights.length, 1);
|
assert.equal(camera.calls.flights.length, 1);
|
||||||
const { sphere, options } = camera.calls.flights[0];
|
const { sphere, options } = camera.calls.flights[0];
|
||||||
assert.equal(sphere.radius, WORLD_FOCUS_FRAMING.vessel.radiusM);
|
assert.equal(sphere.radius, WORLD_FOCUS_FRAMING.vessel.radiusM);
|
||||||
@@ -190,20 +132,12 @@ test('the transfer flight supersedes any flight in progress and keeps the operat
|
|||||||
|
|
||||||
test('fires frame wider than vessels — a fire is read by its surroundings', () => {
|
test('fires frame wider than vessels — a fire is read by its surroundings', () => {
|
||||||
const camera = stubCamera();
|
const camera = stubCamera();
|
||||||
flyToWorldTarget(bindFakeCesiumCamera({ camera }), {
|
flyToWorldTarget(bindFakeCesiumCamera({ camera }), { kind: 'fire', id: 'fire-1', position: POSITION });
|
||||||
kind: 'fire',
|
|
||||||
id: 'fire-1',
|
|
||||||
position: POSITION,
|
|
||||||
});
|
|
||||||
const { sphere, options } = camera.calls.flights[0];
|
const { sphere, options } = camera.calls.flights[0];
|
||||||
assert.equal(sphere.radius, WORLD_FOCUS_FRAMING.fire.radiusM);
|
assert.equal(sphere.radius, WORLD_FOCUS_FRAMING.fire.radiusM);
|
||||||
assert.equal(options.offset.range, WORLD_FOCUS_FRAMING.fire.rangeM);
|
assert.equal(options.offset.range, WORLD_FOCUS_FRAMING.fire.rangeM);
|
||||||
assert.ok(
|
assert.ok(WORLD_FOCUS_FRAMING.fire.rangeM > WORLD_FOCUS_FRAMING.vessel.rangeM);
|
||||||
WORLD_FOCUS_FRAMING.fire.rangeM > WORLD_FOCUS_FRAMING.vessel.rangeM,
|
assert.ok(WORLD_FOCUS_FRAMING.fire.radiusM > WORLD_FOCUS_FRAMING.vessel.radiusM);
|
||||||
);
|
|
||||||
assert.ok(
|
|
||||||
WORLD_FOCUS_FRAMING.fire.radiusM > WORLD_FOCUS_FRAMING.vessel.radiusM,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('every framing is a real oblique standoff, not a nadir or an inside-out sphere', () => {
|
test('every framing is a real oblique standoff, not a nadir or an inside-out sphere', () => {
|
||||||
@@ -212,52 +146,25 @@ test('every framing is a real oblique standoff, not a nadir or an inside-out sph
|
|||||||
for (const kind of kinds) {
|
for (const kind of kinds) {
|
||||||
const framing = WORLD_FOCUS_FRAMING[kind];
|
const framing = WORLD_FOCUS_FRAMING[kind];
|
||||||
// Looking DOWN at the target, but obliquely — a nadir drop reads as a map.
|
// Looking DOWN at the target, but obliquely — a nadir drop reads as a map.
|
||||||
assert.ok(
|
assert.ok(framing.pitchDeg < 0, `${kind}: pitch must look down, got ${framing.pitchDeg}`);
|
||||||
framing.pitchDeg < 0,
|
assert.ok(framing.pitchDeg > -80, `${kind}: pitch must stay oblique, got ${framing.pitchDeg}`);
|
||||||
`${kind}: pitch must look down, got ${framing.pitchDeg}`,
|
|
||||||
);
|
|
||||||
assert.ok(
|
|
||||||
framing.pitchDeg > -80,
|
|
||||||
`${kind}: pitch must stay oblique, got ${framing.pitchDeg}`,
|
|
||||||
);
|
|
||||||
// Standing off outside the framed sphere, at a human-readable distance.
|
// Standing off outside the framed sphere, at a human-readable distance.
|
||||||
assert.ok(framing.radiusM > 0, `${kind}: radius must be positive`);
|
assert.ok(framing.radiusM > 0, `${kind}: radius must be positive`);
|
||||||
assert.ok(
|
assert.ok(
|
||||||
framing.rangeM > framing.radiusM,
|
framing.rangeM > framing.radiusM,
|
||||||
`${kind}: range ${framing.rangeM} must stand off outside radius ${framing.radiusM}`,
|
`${kind}: range ${framing.rangeM} must stand off outside radius ${framing.radiusM}`,
|
||||||
);
|
);
|
||||||
assert.ok(
|
assert.ok(framing.rangeM <= 10000, `${kind}: range must stay close-in, got ${framing.rangeM}`);
|
||||||
framing.rangeM <= 10000,
|
|
||||||
`${kind}: range must stay close-in, got ${framing.rangeM}`,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
// Exact shipped values — a silent retune must show up as a failing test.
|
// Exact shipped values — a silent retune must show up as a failing test.
|
||||||
assert.deepEqual(
|
assert.deepEqual({ ...WORLD_FOCUS_FRAMING.vessel }, { radiusM: 150, rangeM: 1200, pitchDeg: -30 });
|
||||||
{ ...WORLD_FOCUS_FRAMING.vessel },
|
assert.deepEqual({ ...WORLD_FOCUS_FRAMING.fire }, { radiusM: 400, rangeM: 3000, pitchDeg: -35 });
|
||||||
{ radiusM: 150, rangeM: 1200, pitchDeg: -30 },
|
|
||||||
);
|
|
||||||
assert.deepEqual(
|
|
||||||
{ ...WORLD_FOCUS_FRAMING.fire },
|
|
||||||
{ radiusM: 400, rangeM: 3000, pitchDeg: -35 },
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('unknown kinds and missing viewers issue no flight', () => {
|
test('unknown kinds and missing viewers issue no flight', () => {
|
||||||
const camera = stubCamera();
|
const camera = stubCamera();
|
||||||
assert.equal(
|
assert.equal(flyToWorldTarget(bindFakeCesiumCamera({ camera }), { kind: 'plane', position: POSITION }), false);
|
||||||
flyToWorldTarget(bindFakeCesiumCamera({ camera }), {
|
assert.equal(flyToWorldTarget(bindFakeCesiumCamera({ camera }), { kind: 'vessel' }), false);
|
||||||
kind: 'plane',
|
assert.equal(flyToWorldTarget(null, { kind: 'vessel', position: POSITION }), false);
|
||||||
position: POSITION,
|
|
||||||
}),
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
assert.equal(
|
|
||||||
flyToWorldTarget(bindFakeCesiumCamera({ camera }), { kind: 'vessel' }),
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
assert.equal(
|
|
||||||
flyToWorldTarget(null, { kind: 'vessel', position: POSITION }),
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
assert.equal(camera.calls.flights.length, 0);
|
assert.equal(camera.calls.flights.length, 0);
|
||||||
});
|
});
|
||||||
Reference in new issue
Block a user