mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-12 06:12:18 +00:00
Share copied test fixtures through src/testSupport
Environment overrides, manual timers, a cancellable streaming response, GhostMaps KML/KMZ fixtures and the satellite service doubles move into five shared helpers; 21 tests drop 418 lines of copies.
This commit is contained in:
1 parent
6c416f3fe6
commit
df02033465
26 files changed
+167
-418
No files matched your search
@@ -1,3 +1,4 @@
|
|||||||
|
import { streamingResponse } from '../testSupport/streamingResponse.mjs';
|
||||||
import { test } from 'node:test';
|
import { test } from 'node:test';
|
||||||
import assert from 'node:assert/strict';
|
import assert from 'node:assert/strict';
|
||||||
|
|
||||||
@@ -19,24 +20,6 @@ import { allocateSourceCap } from '../../server/providers/cctv/cap.js';
|
|||||||
import { createCctvCatalog } from '../../server/providers/cctv/catalog.js';
|
import { createCctvCatalog } from '../../server/providers/cctv/catalog.js';
|
||||||
import { directionToHeading } from './directionText.js';
|
import { directionToHeading } from './directionText.js';
|
||||||
|
|
||||||
/**
|
|
||||||
* A response whose body is a live stream, plus a flag that flips when the
|
|
||||||
* stream is cancelled. A rejection path that returns without cancelling holds
|
|
||||||
* the transport open, so the flag is what the refusal tests actually assert.
|
|
||||||
*/
|
|
||||||
const streamingResponse = (init = {}) => {
|
|
||||||
const state = { cancelled: false };
|
|
||||||
const body = new ReadableStream({
|
|
||||||
start(controller) {
|
|
||||||
controller.enqueue(new TextEncoder().encode('['));
|
|
||||||
},
|
|
||||||
cancel() {
|
|
||||||
state.cancelled = true;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return { response: new Response(body, init), state };
|
|
||||||
};
|
|
||||||
|
|
||||||
/** One Open Calgary row, shaped like the live `k7p9-kppz` payload. */
|
/** One Open Calgary row, shaped like the live `k7p9-kppz` payload. */
|
||||||
const row = (overrides = {}) => ({
|
const row = (overrides = {}) => ({
|
||||||
camera_url: {
|
camera_url: {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { withEnv } from '../testSupport/environment.mjs';
|
||||||
import { test } from 'node:test';
|
import { test } from 'node:test';
|
||||||
import assert from 'node:assert/strict';
|
import assert from 'node:assert/strict';
|
||||||
import fs from 'node:fs';
|
import fs from 'node:fs';
|
||||||
@@ -17,17 +18,6 @@ import {
|
|||||||
} from '../../server/providers/cctv/sources.js';
|
} from '../../server/providers/cctv/sources.js';
|
||||||
|
|
||||||
/** Set (or, for `undefined`, delete) environment variables for one test. */
|
/** Set (or, for `undefined`, delete) environment variables for one test. */
|
||||||
function withEnv(t, env) {
|
|
||||||
for (const [name, value] of Object.entries(env)) {
|
|
||||||
const previous = process.env[name];
|
|
||||||
t.after(() => {
|
|
||||||
if (previous === undefined) delete process.env[name];
|
|
||||||
else process.env[name] = previous;
|
|
||||||
});
|
|
||||||
if (value === undefined) delete process.env[name];
|
|
||||||
else process.env[name] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Silence the loaders' progress and failure logging. */
|
/** Silence the loaders' progress and failure logging. */
|
||||||
function quiet(t) {
|
function quiet(t) {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { withEnv } from '../testSupport/environment.mjs';
|
||||||
import { test } from 'node:test';
|
import { test } from 'node:test';
|
||||||
import assert from 'node:assert/strict';
|
import assert from 'node:assert/strict';
|
||||||
import fs from 'node:fs';
|
import fs from 'node:fs';
|
||||||
@@ -28,17 +29,6 @@ import {
|
|||||||
} from '../../server/providers/cctv/sources.js';
|
} from '../../server/providers/cctv/sources.js';
|
||||||
|
|
||||||
/** Set (or, for `undefined`, delete) environment variables for one test. */
|
/** Set (or, for `undefined`, delete) environment variables for one test. */
|
||||||
function withEnv(t, env) {
|
|
||||||
for (const [name, value] of Object.entries(env)) {
|
|
||||||
const previous = process.env[name];
|
|
||||||
t.after(() => {
|
|
||||||
if (previous === undefined) delete process.env[name];
|
|
||||||
else process.env[name] = previous;
|
|
||||||
});
|
|
||||||
if (value === undefined) delete process.env[name];
|
|
||||||
else process.env[name] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Silence the loaders' progress and failure logging. */
|
/** Silence the loaders' progress and failure logging. */
|
||||||
function quiet(t) {
|
function quiet(t) {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { withEnv } from '../testSupport/environment.mjs';
|
||||||
import { test } from 'node:test';
|
import { test } from 'node:test';
|
||||||
import assert from 'node:assert/strict';
|
import assert from 'node:assert/strict';
|
||||||
import fs from 'node:fs';
|
import fs from 'node:fs';
|
||||||
@@ -19,17 +20,6 @@ import {
|
|||||||
} from '../../server/providers/cctv/sources.js';
|
} from '../../server/providers/cctv/sources.js';
|
||||||
|
|
||||||
/** Set (or, for `undefined`, delete) environment variables for one test. */
|
/** Set (or, for `undefined`, delete) environment variables for one test. */
|
||||||
function withEnv(t, env) {
|
|
||||||
for (const [name, value] of Object.entries(env)) {
|
|
||||||
const previous = process.env[name];
|
|
||||||
t.after(() => {
|
|
||||||
if (previous === undefined) delete process.env[name];
|
|
||||||
else process.env[name] = previous;
|
|
||||||
});
|
|
||||||
if (value === undefined) delete process.env[name];
|
|
||||||
else process.env[name] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** One row shaped like the live `vefmyndavelar2014_1` payload. */
|
/** One row shaped like the live `vefmyndavelar2014_1` payload. */
|
||||||
const row = (over = {}) => ({
|
const row = (over = {}) => ({
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { withEnv } from '../testSupport/environment.mjs';
|
||||||
import { test } from 'node:test';
|
import { test } from 'node:test';
|
||||||
import assert from 'node:assert/strict';
|
import assert from 'node:assert/strict';
|
||||||
import { mkdtempSync, rmSync } from 'node:fs';
|
import { mkdtempSync, rmSync } from 'node:fs';
|
||||||
@@ -14,17 +15,6 @@ import { cctvProxy } from '../../server/providers/cctv.js';
|
|||||||
import { createCatalog } from '../layers/cctv/catalog.js';
|
import { createCatalog } from '../layers/cctv/catalog.js';
|
||||||
|
|
||||||
/** Set (or, for `undefined`, delete) environment variables for one test. */
|
/** Set (or, for `undefined`, delete) environment variables for one test. */
|
||||||
function withEnv(t, env) {
|
|
||||||
for (const [name, value] of Object.entries(env)) {
|
|
||||||
const previous = process.env[name];
|
|
||||||
t.after(() => {
|
|
||||||
if (previous === undefined) delete process.env[name];
|
|
||||||
else process.env[name] = previous;
|
|
||||||
});
|
|
||||||
if (value === undefined) delete process.env[name];
|
|
||||||
else process.env[name] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Silence the loaders' progress and failure logging. */
|
/** Silence the loaders' progress and failure logging. */
|
||||||
function quiet(t) {
|
function quiet(t) {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { withEnv } from '../testSupport/environment.mjs';
|
||||||
import { test } from 'node:test';
|
import { test } from 'node:test';
|
||||||
import assert from 'node:assert/strict';
|
import assert from 'node:assert/strict';
|
||||||
import { mkdtempSync, rmSync, writeFileSync } from 'node:fs';
|
import { mkdtempSync, rmSync, writeFileSync } from 'node:fs';
|
||||||
@@ -22,17 +23,6 @@ import { createModel } from '../layers/cctv/model.js';
|
|||||||
const ABSENT = [null, undefined, '', ' ', false, true];
|
const ABSENT = [null, undefined, '', ' ', false, true];
|
||||||
|
|
||||||
/** Set env vars for one test and restore them afterwards. */
|
/** Set env vars for one test and restore them afterwards. */
|
||||||
function withEnv(t, env) {
|
|
||||||
for (const [name, value] of Object.entries(env)) {
|
|
||||||
const previous = process.env[name];
|
|
||||||
t.after(() => {
|
|
||||||
if (previous === undefined) delete process.env[name];
|
|
||||||
else process.env[name] = previous;
|
|
||||||
});
|
|
||||||
if (value === undefined) delete process.env[name];
|
|
||||||
else process.env[name] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Silence the loaders' progress and failure logging. */
|
/** Silence the loaders' progress and failure logging. */
|
||||||
function quiet(t) {
|
function quiet(t) {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { streamingResponse } from '../testSupport/streamingResponse.mjs';
|
||||||
import { test } from 'node:test';
|
import { test } from 'node:test';
|
||||||
import assert from 'node:assert/strict';
|
import assert from 'node:assert/strict';
|
||||||
|
|
||||||
@@ -22,24 +23,6 @@ import {
|
|||||||
} from '../../server/providers/cctv/normalize.js';
|
} from '../../server/providers/cctv/normalize.js';
|
||||||
import { createCctvCatalog } from '../../server/providers/cctv/catalog.js';
|
import { createCctvCatalog } from '../../server/providers/cctv/catalog.js';
|
||||||
|
|
||||||
/**
|
|
||||||
* A response whose body is a live stream, plus a flag that flips when the
|
|
||||||
* stream is cancelled. A rejection path that returns without cancelling holds
|
|
||||||
* the transport open, so the flag is what the refusal tests actually assert.
|
|
||||||
*/
|
|
||||||
const streamingResponse = (init = {}) => {
|
|
||||||
const state = { cancelled: false };
|
|
||||||
const body = new ReadableStream({
|
|
||||||
start(controller) {
|
|
||||||
controller.enqueue(new TextEncoder().encode('['));
|
|
||||||
},
|
|
||||||
cancel() {
|
|
||||||
state.cancelled = true;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return { response: new Response(body, init), state };
|
|
||||||
};
|
|
||||||
|
|
||||||
const MIDTOWN_ID = '8a6bc417-4877-4ebe-8052-88c1b261baf1';
|
const MIDTOWN_ID = '8a6bc417-4877-4ebe-8052-88c1b261baf1';
|
||||||
const BROOKLYN_ID = '11111111-2222-3333-4444-555555555555';
|
const BROOKLYN_ID = '11111111-2222-3333-4444-555555555555';
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { withEnv } from '../testSupport/environment.mjs';
|
||||||
import { test } from 'node:test';
|
import { test } from 'node:test';
|
||||||
import assert from 'node:assert/strict';
|
import assert from 'node:assert/strict';
|
||||||
import fs from 'node:fs';
|
import fs from 'node:fs';
|
||||||
@@ -21,17 +22,6 @@ import {
|
|||||||
const CERT = { OTTAWA_CAMERA_CERTIFICATE: 'CERT123' };
|
const CERT = { OTTAWA_CAMERA_CERTIFICATE: 'CERT123' };
|
||||||
|
|
||||||
/** Set (or, for `undefined`, delete) environment variables for one test. */
|
/** Set (or, for `undefined`, delete) environment variables for one test. */
|
||||||
function withEnv(t, env) {
|
|
||||||
for (const [name, value] of Object.entries(env)) {
|
|
||||||
const previous = process.env[name];
|
|
||||||
t.after(() => {
|
|
||||||
if (previous === undefined) delete process.env[name];
|
|
||||||
else process.env[name] = previous;
|
|
||||||
});
|
|
||||||
if (value === undefined) delete process.env[name];
|
|
||||||
else process.env[name] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Silence the loader's progress and failure logging. */
|
/** Silence the loader's progress and failure logging. */
|
||||||
function quiet(t) {
|
function quiet(t) {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { withEnv } from '../testSupport/environment.mjs';
|
||||||
import { test } from 'node:test';
|
import { test } from 'node:test';
|
||||||
import assert from 'node:assert/strict';
|
import assert from 'node:assert/strict';
|
||||||
import fs from 'node:fs';
|
import fs from 'node:fs';
|
||||||
@@ -29,17 +30,6 @@ function quiet(t) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Set (or, for `undefined`, delete) environment variables for one test. */
|
/** Set (or, for `undefined`, delete) environment variables for one test. */
|
||||||
function withEnv(t, env) {
|
|
||||||
for (const [name, value] of Object.entries(env)) {
|
|
||||||
const previous = process.env[name];
|
|
||||||
t.after(() => {
|
|
||||||
if (previous === undefined) delete process.env[name];
|
|
||||||
else process.env[name] = previous;
|
|
||||||
});
|
|
||||||
if (value === undefined) delete process.env[name];
|
|
||||||
else process.env[name] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** A temporary source root holding the given World and Québec catalogs. */
|
/** A temporary source root holding the given World and Québec catalogs. */
|
||||||
function sourceRoot(t, { world = [], quebec = null } = {}) {
|
function sourceRoot(t, { world = [], quebec = null } = {}) {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { withEnv } from '../testSupport/environment.mjs';
|
||||||
import { test } from 'node:test';
|
import { test } from 'node:test';
|
||||||
import assert from 'node:assert/strict';
|
import assert from 'node:assert/strict';
|
||||||
import fs from 'node:fs';
|
import fs from 'node:fs';
|
||||||
@@ -15,17 +16,6 @@ import {
|
|||||||
} from '../../server/providers/cctv/sources.js';
|
} from '../../server/providers/cctv/sources.js';
|
||||||
|
|
||||||
/** Set (or, for `undefined`, delete) environment variables for one test. */
|
/** Set (or, for `undefined`, delete) environment variables for one test. */
|
||||||
function withEnv(t, env) {
|
|
||||||
for (const [name, value] of Object.entries(env)) {
|
|
||||||
const previous = process.env[name];
|
|
||||||
t.after(() => {
|
|
||||||
if (previous === undefined) delete process.env[name];
|
|
||||||
else process.env[name] = previous;
|
|
||||||
});
|
|
||||||
if (value === undefined) delete process.env[name];
|
|
||||||
else process.env[name] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Silence the loaders' progress and failure logging. */
|
/** Silence the loaders' progress and failure logging. */
|
||||||
function quiet(t) {
|
function quiet(t) {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import './testSupport/stubMgrs.mjs';
|
||||||
// The HUD's ALT readout datum. Field report (2026-08-22, cockpit parked at
|
// The HUD's ALT readout datum. Field report (2026-08-22, cockpit parked at
|
||||||
// SFO): the bottom-right OSD read "ALT: -15M" — and JFK, earlier, "ALT: -18M".
|
// SFO): the bottom-right OSD read "ALT: -15M" — and JFK, earlier, "ALT: -18M".
|
||||||
// Cesium reports the camera's height against the WGS84 ELLIPSOID, and San
|
// Cesium reports the camera's height against the WGS84 ELLIPSOID, and San
|
||||||
@@ -25,27 +26,8 @@ import { createFakeEngine } from './globe/testing/fakeEngine.js';
|
|||||||
import assert from 'node:assert/strict';
|
import assert from 'node:assert/strict';
|
||||||
import { hudReadouts } from './testSupport/hudReadouts.mjs';
|
import { hudReadouts } from './testSupport/hudReadouts.mjs';
|
||||||
import { readFileSync } from 'node:fs';
|
import { readFileSync } from 'node:fs';
|
||||||
import { registerHooks } from 'node:module';
|
|
||||||
import { ensureGeoidReady } from './data/geoid.js';
|
import { ensureGeoidReady } from './data/geoid.js';
|
||||||
|
|
||||||
const MGRS_STUB_URL = 'gev-test-stub:mgrs';
|
|
||||||
registerHooks({
|
|
||||||
resolve(specifier, context, next) {
|
|
||||||
if (specifier === 'mgrs') return { url: MGRS_STUB_URL, shortCircuit: true };
|
|
||||||
return next(specifier, context);
|
|
||||||
},
|
|
||||||
load(url, context, next) {
|
|
||||||
if (url === MGRS_STUB_URL) {
|
|
||||||
return {
|
|
||||||
format: 'module',
|
|
||||||
shortCircuit: true,
|
|
||||||
source:
|
|
||||||
'export function forward() { return "10SEG55776339"; }\nexport default { forward };\n',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return next(url, context);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const { IntelHUD } = await import('./hud.js');
|
const { IntelHUD } = await import('./hud.js');
|
||||||
|
|
||||||
|
|||||||
+1
-20
@@ -1,3 +1,4 @@
|
|||||||
|
import './testSupport/stubMgrs.mjs';
|
||||||
import { viewerEngine } from './testSupport/viewerEngine.mjs';
|
import { viewerEngine } from './testSupport/viewerEngine.mjs';
|
||||||
// The HUD's LAT/LON readout in degrees-minutes-seconds. Seconds are printed to
|
// The HUD's LAT/LON readout in degrees-minutes-seconds. Seconds are printed to
|
||||||
// two decimals, so a value within 0.005" of the next whole minute rounds up.
|
// two decimals, so a value within 0.005" of the next whole minute rounds up.
|
||||||
@@ -11,26 +12,6 @@ import { viewerEngine } from './testSupport/viewerEngine.mjs';
|
|||||||
import { test } from 'node:test';
|
import { test } from 'node:test';
|
||||||
import assert from 'node:assert/strict';
|
import assert from 'node:assert/strict';
|
||||||
import { hudReadouts } from './testSupport/hudReadouts.mjs';
|
import { hudReadouts } from './testSupport/hudReadouts.mjs';
|
||||||
import { registerHooks } from 'node:module';
|
|
||||||
|
|
||||||
const MGRS_STUB_URL = 'gev-test-stub:mgrs';
|
|
||||||
registerHooks({
|
|
||||||
resolve(specifier, context, next) {
|
|
||||||
if (specifier === 'mgrs') return { url: MGRS_STUB_URL, shortCircuit: true };
|
|
||||||
return next(specifier, context);
|
|
||||||
},
|
|
||||||
load(url, context, next) {
|
|
||||||
if (url === MGRS_STUB_URL) {
|
|
||||||
return {
|
|
||||||
format: 'module',
|
|
||||||
shortCircuit: true,
|
|
||||||
source:
|
|
||||||
'export function forward() { return "10SEG55776339"; }\nexport default { forward };\n',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return next(url, context);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const { IntelHUD } = await import('./hud.js');
|
const { IntelHUD } = await import('./hud.js');
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import './testSupport/stubMgrs.mjs';
|
||||||
import { viewerEngine } from './testSupport/viewerEngine.mjs';
|
import { viewerEngine } from './testSupport/viewerEngine.mjs';
|
||||||
// The HUD's 250 ms telemetry readout skips ticks in a hidden page, except in
|
// The HUD's 250 ms telemetry readout skips ticks in a hidden page, except in
|
||||||
// an inline panel, whose host may report it hidden while it is on screen.
|
// an inline panel, whose host may report it hidden while it is on screen.
|
||||||
@@ -8,25 +9,7 @@ import { viewerEngine } from './testSupport/viewerEngine.mjs';
|
|||||||
import { test } from 'node:test';
|
import { test } from 'node:test';
|
||||||
import assert from 'node:assert/strict';
|
import assert from 'node:assert/strict';
|
||||||
import { hudReadouts } from './testSupport/hudReadouts.mjs';
|
import { hudReadouts } from './testSupport/hudReadouts.mjs';
|
||||||
import { registerHooks } from 'node:module';
|
|
||||||
|
|
||||||
const MGRS_STUB_URL = 'gev-test-stub:mgrs';
|
|
||||||
registerHooks({
|
|
||||||
resolve(specifier, context, next) {
|
|
||||||
if (specifier === 'mgrs') return { url: MGRS_STUB_URL, shortCircuit: true };
|
|
||||||
return next(specifier, context);
|
|
||||||
},
|
|
||||||
load(url, context, next) {
|
|
||||||
if (url === MGRS_STUB_URL) {
|
|
||||||
return {
|
|
||||||
format: 'module',
|
|
||||||
shortCircuit: true,
|
|
||||||
source: 'export function forward() { return "10SEG55776339"; }\nexport default { forward };\n',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return next(url, context);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const { IntelHUD } = await import('./hud.js');
|
const { IntelHUD } = await import('./hud.js');
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import './testSupport/stubMgrs.mjs';
|
||||||
import { viewerEngine } from './testSupport/viewerEngine.mjs';
|
import { viewerEngine } from './testSupport/viewerEngine.mjs';
|
||||||
// The HUD's place lookups are paid Google requests made only to feed the AI
|
// The HUD's place lookups are paid Google requests made only to feed the AI
|
||||||
// summary, so a server without one must not be asked to make them.
|
// summary, so a server without one must not be asked to make them.
|
||||||
@@ -8,26 +9,6 @@ import { viewerEngine } from './testSupport/viewerEngine.mjs';
|
|||||||
import { test } from 'node:test';
|
import { test } from 'node:test';
|
||||||
import assert from 'node:assert/strict';
|
import assert from 'node:assert/strict';
|
||||||
import { hudReadouts } from './testSupport/hudReadouts.mjs';
|
import { hudReadouts } from './testSupport/hudReadouts.mjs';
|
||||||
import { registerHooks } from 'node:module';
|
|
||||||
|
|
||||||
const MGRS_STUB_URL = 'gev-test-stub:mgrs';
|
|
||||||
registerHooks({
|
|
||||||
resolve(specifier, context, next) {
|
|
||||||
if (specifier === 'mgrs') return { url: MGRS_STUB_URL, shortCircuit: true };
|
|
||||||
return next(specifier, context);
|
|
||||||
},
|
|
||||||
load(url, context, next) {
|
|
||||||
if (url === MGRS_STUB_URL) {
|
|
||||||
return {
|
|
||||||
format: 'module',
|
|
||||||
shortCircuit: true,
|
|
||||||
source:
|
|
||||||
'export function forward() { return "10SEG55776339"; }\nexport default { forward };\n',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return next(url, context);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const { IntelHUD } = await import('./hud.js');
|
const { IntelHUD } = await import('./hud.js');
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
|
import {
|
||||||
|
element,
|
||||||
|
kmz,
|
||||||
|
deferred,
|
||||||
|
} from '../../testSupport/ghostMapsFixtures.mjs';
|
||||||
// Edge cases for s2-ghostmaps:
|
// Edge cases for s2-ghostmaps:
|
||||||
// change the KMZ edition and the engine before parsing finishes; only the
|
// change the KMZ edition and the engine before parsing finishes; only the
|
||||||
// selected edition is shown, and a bad archive does not erase the working one.
|
// selected edition is shown, and a bad archive does not erase the working one.
|
||||||
@@ -5,25 +10,6 @@ import assert from 'node:assert/strict';
|
|||||||
import test from 'node:test';
|
import test from 'node:test';
|
||||||
import { createGhostMapsLayer } from './index.js';
|
import { createGhostMapsLayer } from './index.js';
|
||||||
|
|
||||||
function element(localName, children = [], value = '') {
|
|
||||||
const node = {
|
|
||||||
localName,
|
|
||||||
children,
|
|
||||||
get textContent() {
|
|
||||||
return value + children.map((child) => child.textContent).join('');
|
|
||||||
},
|
|
||||||
getAttribute: () => null,
|
|
||||||
getElementsByTagName(name) {
|
|
||||||
return children.flatMap((child) => [
|
|
||||||
...(name === '*' || name === child.localName ? [child] : []),
|
|
||||||
...child.getElementsByTagName(name),
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
for (const child of children) child.parentElement = node;
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The XML double turns `<kml>NAME</kml>` into one placemark called NAME.
|
// The XML double turns `<kml>NAME</kml>` into one placemark called NAME.
|
||||||
function documentFor(xml) {
|
function documentFor(xml) {
|
||||||
const name = /<kml>(.*)<\/kml>/.exec(xml)?.[1] ?? 'unnamed';
|
const name = /<kml>(.*)<\/kml>/.exec(xml)?.[1] ?? 'unnamed';
|
||||||
@@ -39,37 +25,6 @@ function documentFor(xml) {
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A stored (uncompressed) KMZ holding doc.kml only. */
|
|
||||||
function kmz(name) {
|
|
||||||
const key = Buffer.from('doc.kml');
|
|
||||||
const bytes = Buffer.from(`<kml>${name}</kml>`);
|
|
||||||
const header = Buffer.alloc(30);
|
|
||||||
header.writeUInt32LE(0x04034b50);
|
|
||||||
header.writeUInt32LE(bytes.length, 18);
|
|
||||||
header.writeUInt32LE(bytes.length, 22);
|
|
||||||
header.writeUInt16LE(key.length, 26);
|
|
||||||
const directory = Buffer.alloc(46);
|
|
||||||
directory.writeUInt32LE(0x02014b50);
|
|
||||||
directory.writeUInt32LE(bytes.length, 20);
|
|
||||||
directory.writeUInt32LE(bytes.length, 24);
|
|
||||||
directory.writeUInt16LE(key.length, 28);
|
|
||||||
directory.writeUInt32LE(0, 42);
|
|
||||||
const central = Buffer.concat([directory, key]);
|
|
||||||
const end = Buffer.alloc(22);
|
|
||||||
end.writeUInt32LE(0x06054b50);
|
|
||||||
end.writeUInt16LE(1, 8);
|
|
||||||
end.writeUInt16LE(1, 10);
|
|
||||||
end.writeUInt32LE(central.length, 12);
|
|
||||||
end.writeUInt32LE(header.length + key.length + bytes.length, 16);
|
|
||||||
return new Uint8Array(Buffer.concat([header, key, bytes, central, end]));
|
|
||||||
}
|
|
||||||
|
|
||||||
function deferred() {
|
|
||||||
let resolve;
|
|
||||||
const promise = new Promise((done) => (resolve = done));
|
|
||||||
return { promise, resolve };
|
|
||||||
}
|
|
||||||
|
|
||||||
const catalog = (version) => ({
|
const catalog = (version) => ({
|
||||||
products: [{ id: 'cip', version, defaultOn: true }],
|
products: [{ id: 'cip', version, defaultOn: true }],
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,26 +1,12 @@
|
|||||||
|
import {
|
||||||
|
element,
|
||||||
|
kmz,
|
||||||
|
deferred,
|
||||||
|
} from '../../testSupport/ghostMapsFixtures.mjs';
|
||||||
import assert from 'node:assert/strict';
|
import assert from 'node:assert/strict';
|
||||||
import test from 'node:test';
|
import test from 'node:test';
|
||||||
import { createGhostMapsLayer } from './index.js';
|
import { createGhostMapsLayer } from './index.js';
|
||||||
|
|
||||||
function element(localName, children = [], value = '') {
|
|
||||||
const node = {
|
|
||||||
localName,
|
|
||||||
children,
|
|
||||||
get textContent() {
|
|
||||||
return value + children.map((child) => child.textContent).join('');
|
|
||||||
},
|
|
||||||
getAttribute: () => null,
|
|
||||||
getElementsByTagName(name) {
|
|
||||||
return children.flatMap((child) => [
|
|
||||||
...(name === '*' || name === child.localName ? [child] : []),
|
|
||||||
...child.getElementsByTagName(name),
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
for (const child of children) child.parentElement = node;
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The XML double turns `<kml>NAME</kml>` into one placemark called NAME.
|
// The XML double turns `<kml>NAME</kml>` into one placemark called NAME.
|
||||||
function documentFor(xml) {
|
function documentFor(xml) {
|
||||||
const name = /<kml>(.*)<\/kml>/.exec(xml)?.[1] ?? 'unnamed';
|
const name = /<kml>(.*)<\/kml>/.exec(xml)?.[1] ?? 'unnamed';
|
||||||
@@ -41,37 +27,6 @@ function documentFor(xml) {
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A stored (uncompressed) KMZ holding doc.kml only. */
|
|
||||||
function kmz(name) {
|
|
||||||
const key = Buffer.from('doc.kml');
|
|
||||||
const bytes = Buffer.from(`<kml>${name}</kml>`);
|
|
||||||
const header = Buffer.alloc(30);
|
|
||||||
header.writeUInt32LE(0x04034b50);
|
|
||||||
header.writeUInt32LE(bytes.length, 18);
|
|
||||||
header.writeUInt32LE(bytes.length, 22);
|
|
||||||
header.writeUInt16LE(key.length, 26);
|
|
||||||
const directory = Buffer.alloc(46);
|
|
||||||
directory.writeUInt32LE(0x02014b50);
|
|
||||||
directory.writeUInt32LE(bytes.length, 20);
|
|
||||||
directory.writeUInt32LE(bytes.length, 24);
|
|
||||||
directory.writeUInt16LE(key.length, 28);
|
|
||||||
directory.writeUInt32LE(0, 42);
|
|
||||||
const central = Buffer.concat([directory, key]);
|
|
||||||
const end = Buffer.alloc(22);
|
|
||||||
end.writeUInt32LE(0x06054b50);
|
|
||||||
end.writeUInt16LE(1, 8);
|
|
||||||
end.writeUInt16LE(1, 10);
|
|
||||||
end.writeUInt32LE(central.length, 12);
|
|
||||||
end.writeUInt32LE(header.length + key.length + bytes.length, 16);
|
|
||||||
return new Uint8Array(Buffer.concat([header, key, bytes, central, end]));
|
|
||||||
}
|
|
||||||
|
|
||||||
function deferred() {
|
|
||||||
let resolve;
|
|
||||||
const promise = new Promise((done) => (resolve = done));
|
|
||||||
return { promise, resolve };
|
|
||||||
}
|
|
||||||
|
|
||||||
const catalog = (version) => ({
|
const catalog = (version) => ({
|
||||||
products: [{ id: 'cip', version, defaultOn: true }],
|
products: [{ id: 'cip', version, defaultOn: true }],
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import assert from 'node:assert/strict';
|
|||||||
import test from 'node:test';
|
import test from 'node:test';
|
||||||
import * as geo from '../../geo/index.js';
|
import * as geo from '../../geo/index.js';
|
||||||
import { createFakeEngine } from '../../globe/testing/fakeEngine.js';
|
import { createFakeEngine } from '../../globe/testing/fakeEngine.js';
|
||||||
import { createSatellitesDisplay } from './render/display.js';
|
import { satelliteServices } from '../../testSupport/satelliteServices.mjs';
|
||||||
import { constantRgba } from '../shared/color.js';
|
import { constantRgba } from '../shared/color.js';
|
||||||
import { createState } from './state.js';
|
import { createState } from './state.js';
|
||||||
import { createControls } from './controls.js';
|
import { createControls } from './controls.js';
|
||||||
@@ -98,31 +98,9 @@ async function fixture(t) {
|
|||||||
readSensors: async () => SENSORS,
|
readSensors: async () => SENSORS,
|
||||||
};
|
};
|
||||||
const noop = () => {};
|
const noop = () => {};
|
||||||
const services = {
|
const services = satelliteServices();
|
||||||
renderer: { create: ({ engine }) => createSatellitesDisplay(engine) },
|
services.focus.focusNowMs = (now) => now;
|
||||||
overlays: {
|
services.focus.focusPassIsNeeded = () => false;
|
||||||
setEntries: noop,
|
|
||||||
setVisible: noop,
|
|
||||||
clearSource: noop,
|
|
||||||
},
|
|
||||||
layerState: { isExplicitLayerStateOrigin: () => false },
|
|
||||||
focus: {
|
|
||||||
clearFocusTarget: noop,
|
|
||||||
publishFocusTargetFromCachedPosition: noop,
|
|
||||||
getFocusTarget: () => null,
|
|
||||||
focusNowMs: (now) => now,
|
|
||||||
focusPassIsNeeded: () => false,
|
|
||||||
},
|
|
||||||
context: {
|
|
||||||
clearTrackedSubjectContext: noop,
|
|
||||||
getContextStore: () => ({ entities: new Map() }),
|
|
||||||
refreshTrackedSubjectContext: noop,
|
|
||||||
selectTrackedSubjectContext: noop,
|
|
||||||
},
|
|
||||||
readout: { refreshTrackedReadout: noop },
|
|
||||||
render: { holdContinuousRender: noop, releaseContinuousRender: noop },
|
|
||||||
picking: { registerPickOwner: noop, unregisterPickOwner: noop },
|
|
||||||
};
|
|
||||||
const previousDocument = globalThis.document;
|
const previousDocument = globalThis.document;
|
||||||
globalThis.document = new EventTarget();
|
globalThis.document = new EventTarget();
|
||||||
const state = createState({ services });
|
const state = createState({ services });
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { createSatellitesDisplay } from './render/display.js';
|
import { satelliteServices } from '../../testSupport/satelliteServices.mjs';
|
||||||
import assert from 'node:assert/strict';
|
import assert from 'node:assert/strict';
|
||||||
import http from 'node:http';
|
import http from 'node:http';
|
||||||
import { EventEmitter, once } from 'node:events';
|
import { EventEmitter, once } from 'node:events';
|
||||||
@@ -76,32 +76,7 @@ async function fixture(t) {
|
|||||||
// Application host services and native EventTargets; the display on the
|
// Application host services and native EventTargets; the display on the
|
||||||
// fake engine, its input, events, SGP4, ingestion and lifecycle run without
|
// fake engine, its input, events, SGP4, ingestion and lifecycle run without
|
||||||
// a WebGL scene.
|
// a WebGL scene.
|
||||||
const noop = () => {};
|
const services = satelliteServices();
|
||||||
const services = {
|
|
||||||
renderer: {
|
|
||||||
create: ({ engine }) => createSatellitesDisplay(engine),
|
|
||||||
},
|
|
||||||
overlays: {
|
|
||||||
setEntries: noop,
|
|
||||||
setVisible: noop,
|
|
||||||
clearSource: noop,
|
|
||||||
},
|
|
||||||
layerState: { isExplicitLayerStateOrigin: () => false },
|
|
||||||
focus: {
|
|
||||||
clearFocusTarget: noop,
|
|
||||||
publishFocusTargetFromCachedPosition: noop,
|
|
||||||
getFocusTarget: () => null,
|
|
||||||
},
|
|
||||||
context: {
|
|
||||||
clearTrackedSubjectContext: noop,
|
|
||||||
getContextStore: () => ({ entities: new Map() }),
|
|
||||||
refreshTrackedSubjectContext: noop,
|
|
||||||
selectTrackedSubjectContext: noop,
|
|
||||||
},
|
|
||||||
readout: { refreshTrackedReadout: noop },
|
|
||||||
render: { holdContinuousRender: noop, releaseContinuousRender: noop },
|
|
||||||
picking: { registerPickOwner: noop, unregisterPickOwner: noop },
|
|
||||||
};
|
|
||||||
const previousDocument = globalThis.document;
|
const previousDocument = globalThis.document;
|
||||||
globalThis.document = new EventTarget();
|
globalThis.document = new EventTarget();
|
||||||
const state = createState({ services });
|
const state = createState({ services });
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { installManualTimers } from './testSupport/manualTimers.mjs';
|
||||||
import { viewerEngine } from './testSupport/viewerEngine.mjs';
|
import { viewerEngine } from './testSupport/viewerEngine.mjs';
|
||||||
import { test } from 'node:test';
|
import { test } from 'node:test';
|
||||||
import assert from 'node:assert/strict';
|
import assert from 'node:assert/strict';
|
||||||
@@ -72,23 +73,6 @@ function installTab(t, initialHash = '') {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Queue timers instead of running them, so delayed work runs only on demand. */
|
|
||||||
function installManualTimers(t) {
|
|
||||||
const prior = {
|
|
||||||
setTimeout: globalThis.setTimeout,
|
|
||||||
clearTimeout: globalThis.clearTimeout,
|
|
||||||
};
|
|
||||||
const timers = new Map();
|
|
||||||
let timerId = 0;
|
|
||||||
globalThis.setTimeout = (fn) => {
|
|
||||||
timers.set(++timerId, fn);
|
|
||||||
return timerId;
|
|
||||||
};
|
|
||||||
globalThis.clearTimeout = (id) => timers.delete(id);
|
|
||||||
t.after(() => Object.assign(globalThis, prior));
|
|
||||||
return timers;
|
|
||||||
}
|
|
||||||
|
|
||||||
function fakeViewer() {
|
function fakeViewer() {
|
||||||
return viewerEngine({
|
return viewerEngine({
|
||||||
camera: {
|
camera: {
|
||||||
@@ -175,7 +159,7 @@ test('a non-share hash is ignored and Back/Forward restores each entry once', (t
|
|||||||
|
|
||||||
test('a newer navigation supersedes an older restore and its pending write', (t) => {
|
test('a newer navigation supersedes an older restore and its pending write', (t) => {
|
||||||
const tab = installTab(t, SHARE_A);
|
const tab = installTab(t, SHARE_A);
|
||||||
const timers = installManualTimers(t);
|
const timers = installManualTimers(t).timers;
|
||||||
|
|
||||||
const manager = new ShareLinkManager(fakeViewer());
|
const manager = new ShareLinkManager(fakeViewer());
|
||||||
manager.completeInitialRestore(manager.parseInitialHash().restoreToken);
|
manager.completeInitialRestore(manager.parseInitialHash().restoreToken);
|
||||||
@@ -237,7 +221,7 @@ function deferred() {
|
|||||||
/** A ShareRestoration over a real ShareLinkManager with a stub restore sink. */
|
/** A ShareRestoration over a real ShareLinkManager with a stub restore sink. */
|
||||||
function restorationHarness(t, initialHash, navigationOverrides = {}) {
|
function restorationHarness(t, initialHash, navigationOverrides = {}) {
|
||||||
const tab = installTab(t, initialHash);
|
const tab = installTab(t, initialHash);
|
||||||
const timers = installManualTimers(t);
|
const timers = installManualTimers(t).timers;
|
||||||
|
|
||||||
const restores = [];
|
const restores = [];
|
||||||
let navigationGeneration = 0;
|
let navigationGeneration = 0;
|
||||||
@@ -427,7 +411,7 @@ test('the layer coordinator replaces in-flight share layers with the navigated s
|
|||||||
|
|
||||||
test('a manager held for an engine-changing visit neither stamps nor restores that entry', (t) => {
|
test('a manager held for an engine-changing visit neither stamps nor restores that entry', (t) => {
|
||||||
const tab = installTab(t, SHARE_A);
|
const tab = installTab(t, SHARE_A);
|
||||||
const timers = installManualTimers(t);
|
const timers = installManualTimers(t).timers;
|
||||||
const manager = new ShareLinkManager(fakeViewer());
|
const manager = new ShareLinkManager(fakeViewer());
|
||||||
manager.completeInitialRestore(manager.parseInitialHash().restoreToken);
|
manager.completeInitialRestore(manager.parseInitialHash().restoreToken);
|
||||||
// The page's own popstate listener runs first (src/main.js): the visit
|
// The page's own popstate listener runs first (src/main.js): the visit
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
export function withEnv(t, env) {
|
||||||
|
for (const [name, value] of Object.entries(env)) {
|
||||||
|
const previous = process.env[name];
|
||||||
|
t.after(() => {
|
||||||
|
if (previous === undefined) delete process.env[name];
|
||||||
|
else process.env[name] = previous;
|
||||||
|
});
|
||||||
|
if (value === undefined) delete process.env[name];
|
||||||
|
else process.env[name] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
export function element(localName, children = [], value = '') {
|
||||||
|
const node = {
|
||||||
|
localName,
|
||||||
|
children,
|
||||||
|
get textContent() {
|
||||||
|
return value + children.map((child) => child.textContent).join('');
|
||||||
|
},
|
||||||
|
getAttribute: () => null,
|
||||||
|
getElementsByTagName(name) {
|
||||||
|
return children.flatMap((child) => [
|
||||||
|
...(name === '*' || name === child.localName ? [child] : []),
|
||||||
|
...child.getElementsByTagName(name),
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
for (const child of children) child.parentElement = node;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A stored (uncompressed) KMZ holding doc.kml only. */
|
||||||
|
export function kmz(name) {
|
||||||
|
const key = Buffer.from('doc.kml');
|
||||||
|
const bytes = Buffer.from(`<kml>${name}</kml>`);
|
||||||
|
const header = Buffer.alloc(30);
|
||||||
|
header.writeUInt32LE(0x04034b50);
|
||||||
|
header.writeUInt32LE(bytes.length, 18);
|
||||||
|
header.writeUInt32LE(bytes.length, 22);
|
||||||
|
header.writeUInt16LE(key.length, 26);
|
||||||
|
const directory = Buffer.alloc(46);
|
||||||
|
directory.writeUInt32LE(0x02014b50);
|
||||||
|
directory.writeUInt32LE(bytes.length, 20);
|
||||||
|
directory.writeUInt32LE(bytes.length, 24);
|
||||||
|
directory.writeUInt16LE(key.length, 28);
|
||||||
|
directory.writeUInt32LE(0, 42);
|
||||||
|
const central = Buffer.concat([directory, key]);
|
||||||
|
const end = Buffer.alloc(22);
|
||||||
|
end.writeUInt32LE(0x06054b50);
|
||||||
|
end.writeUInt16LE(1, 8);
|
||||||
|
end.writeUInt16LE(1, 10);
|
||||||
|
end.writeUInt32LE(central.length, 12);
|
||||||
|
end.writeUInt32LE(header.length + key.length + bytes.length, 16);
|
||||||
|
return new Uint8Array(Buffer.concat([header, key, bytes, central, end]));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deferred() {
|
||||||
|
let resolve;
|
||||||
|
const promise = new Promise((done) => (resolve = done));
|
||||||
|
return { promise, resolve };
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
/** Queue timers instead of running them, so delayed work runs only on demand. */
|
||||||
|
export function installManualTimers(t) {
|
||||||
|
const prior = {
|
||||||
|
setTimeout: globalThis.setTimeout,
|
||||||
|
clearTimeout: globalThis.clearTimeout,
|
||||||
|
};
|
||||||
|
const timers = new Map();
|
||||||
|
let timerId = 0;
|
||||||
|
globalThis.setTimeout = (fn) => {
|
||||||
|
timers.set(++timerId, fn);
|
||||||
|
return timerId;
|
||||||
|
};
|
||||||
|
globalThis.clearTimeout = (id) => timers.delete(id);
|
||||||
|
t.after(() => Object.assign(globalThis, prior));
|
||||||
|
return {
|
||||||
|
timers,
|
||||||
|
runAll() {
|
||||||
|
for (const [id, fn] of [...timers]) {
|
||||||
|
timers.delete(id);
|
||||||
|
fn();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import { createSatellitesDisplay } from '../layers/satellites/render/display.js';
|
||||||
|
|
||||||
|
export function satelliteServices() {
|
||||||
|
const noop = () => {};
|
||||||
|
return {
|
||||||
|
renderer: {
|
||||||
|
create: ({ engine }) => createSatellitesDisplay(engine),
|
||||||
|
},
|
||||||
|
overlays: {
|
||||||
|
setEntries: noop,
|
||||||
|
setVisible: noop,
|
||||||
|
clearSource: noop,
|
||||||
|
},
|
||||||
|
layerState: { isExplicitLayerStateOrigin: () => false },
|
||||||
|
focus: {
|
||||||
|
clearFocusTarget: noop,
|
||||||
|
publishFocusTargetFromCachedPosition: noop,
|
||||||
|
getFocusTarget: () => null,
|
||||||
|
},
|
||||||
|
context: {
|
||||||
|
clearTrackedSubjectContext: noop,
|
||||||
|
getContextStore: () => ({ entities: new Map() }),
|
||||||
|
refreshTrackedSubjectContext: noop,
|
||||||
|
selectTrackedSubjectContext: noop,
|
||||||
|
},
|
||||||
|
readout: { refreshTrackedReadout: noop },
|
||||||
|
render: { holdContinuousRender: noop, releaseContinuousRender: noop },
|
||||||
|
picking: { registerPickOwner: noop, unregisterPickOwner: noop },
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
/**
|
||||||
|
* A response whose body is a live stream, plus a flag that flips when the
|
||||||
|
* stream is cancelled. A rejection path that returns without cancelling holds
|
||||||
|
* the transport open, so the flag is what the refusal tests actually assert.
|
||||||
|
*/
|
||||||
|
export const streamingResponse = (init = {}) => {
|
||||||
|
const state = { cancelled: false };
|
||||||
|
const body = new ReadableStream({
|
||||||
|
start(controller) {
|
||||||
|
controller.enqueue(new TextEncoder().encode('['));
|
||||||
|
},
|
||||||
|
cancel() {
|
||||||
|
state.cancelled = true;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return { response: new Response(body, init), state };
|
||||||
|
};
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { installManualTimers } from '../testSupport/manualTimers.mjs';
|
||||||
import { createFakeEngine } from '../globe/testing/fakeEngine.js';
|
import { createFakeEngine } from '../globe/testing/fakeEngine.js';
|
||||||
import { createCesiumMoversEngine } from '../testSupport/cesiumMoversEngine.mjs';
|
import { createCesiumMoversEngine } from '../testSupport/cesiumMoversEngine.mjs';
|
||||||
import { enuOffset } from '../globe/follow.js';
|
import { enuOffset } from '../globe/follow.js';
|
||||||
@@ -77,30 +78,6 @@ function installPage(t, href) {
|
|||||||
return { location, writes, target };
|
return { location, writes, target };
|
||||||
}
|
}
|
||||||
|
|
||||||
function installManualTimers(t) {
|
|
||||||
const prior = {
|
|
||||||
setTimeout: globalThis.setTimeout,
|
|
||||||
clearTimeout: globalThis.clearTimeout,
|
|
||||||
};
|
|
||||||
const timers = new Map();
|
|
||||||
let timerId = 0;
|
|
||||||
globalThis.setTimeout = (fn) => {
|
|
||||||
timers.set(++timerId, fn);
|
|
||||||
return timerId;
|
|
||||||
};
|
|
||||||
globalThis.clearTimeout = (id) => timers.delete(id);
|
|
||||||
t.after(() => Object.assign(globalThis, prior));
|
|
||||||
return {
|
|
||||||
timers,
|
|
||||||
runAll() {
|
|
||||||
for (const [id, fn] of [...timers]) {
|
|
||||||
timers.delete(id);
|
|
||||||
fn();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/** A viewer whose camera flights land at once. */
|
/** A viewer whose camera flights land at once. */
|
||||||
function fakeViewer() {
|
function fakeViewer() {
|
||||||
const flights = [];
|
const flights = [];
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import '../../testSupport/stubMgrs.mjs';
|
||||||
import { viewerEngine } from '../../testSupport/viewerEngine.mjs';
|
import { viewerEngine } from '../../testSupport/viewerEngine.mjs';
|
||||||
// src/ui/views/intelHudView.test.mjs — the Intel HUD view renders what
|
// src/ui/views/intelHudView.test.mjs — the Intel HUD view renders what
|
||||||
// IntelHUD publishes: its readouts, visibility, layout variant and colors.
|
// IntelHUD publishes: its readouts, visibility, layout variant and colors.
|
||||||
@@ -10,30 +11,10 @@ import test from 'node:test';
|
|||||||
import assert from 'node:assert/strict';
|
import assert from 'node:assert/strict';
|
||||||
import { createApp, nextTick } from 'vue';
|
import { createApp, nextTick } from 'vue';
|
||||||
import { createPinia } from 'pinia';
|
import { createPinia } from 'pinia';
|
||||||
import { registerHooks } from 'node:module';
|
|
||||||
import { presentSelectedVessel } from '../../hudReadouts.js';
|
import { presentSelectedVessel } from '../../hudReadouts.js';
|
||||||
import { useHudStore } from '../stores/hud.js';
|
import { useHudStore } from '../stores/hud.js';
|
||||||
import IntelHudView from './IntelHudView.vue';
|
import IntelHudView from './IntelHudView.vue';
|
||||||
|
|
||||||
const MGRS_STUB_URL = 'gev-test-stub:mgrs';
|
|
||||||
registerHooks({
|
|
||||||
resolve(specifier, context, next) {
|
|
||||||
if (specifier === 'mgrs') return { url: MGRS_STUB_URL, shortCircuit: true };
|
|
||||||
return next(specifier, context);
|
|
||||||
},
|
|
||||||
load(url, context, next) {
|
|
||||||
if (url === MGRS_STUB_URL) {
|
|
||||||
return {
|
|
||||||
format: 'module',
|
|
||||||
shortCircuit: true,
|
|
||||||
source:
|
|
||||||
'export function forward() { return "10SEG55776339"; }\nexport default { forward };\n',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return next(url, context);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const { IntelHUD } = await import('../../hud.js');
|
const { IntelHUD } = await import('../../hud.js');
|
||||||
|
|
||||||
const listeners = () => ({ addEventListener() {}, removeEventListener() {} });
|
const listeners = () => ({ addEventListener() {}, removeEventListener() {} });
|
||||||
|
|||||||
Reference in new issue
Block a user