test(street-level): close the mutation survivors; Esc in rich text

A mutation review left 18 earlier survivors; each now has a test that
fails with the mutant applied:

- the expanded viewer returns into the panel on shrink, close and destroy
  (the fake DOM had no parentNode, so reinsertion was never exercised); the
  fake DOM gains offsetParent and a MutationObserver that honours its
  options, and a <body> child added while expanded goes inert;
- each pose moves the marker, drives FOLLOW and selects a new sequence;
  close() retires an open still mounting or opening; a provider toggled
  while the layer is off activates nothing; the adapter drops late and
  superseded poses;
- share-link defaults: "any date" (0 days), the codec max pinned to the
  filter's MAX_SINCE_DAYS (layerState cannot import it across package
  boundaries, so a test ties the two), Mapillary on when a link omits it;
- Esc that something else handled, or typed in a field, keeps the
  selection; a key problem outranks LOADING on the pill;
- the marker's terrain height (mesh, DEM, clamp then re-place);
- proxy: LRU memory cache and its half-budget rule, Retry-After capped,
  a request after a cancelled fetch starts a fresh one;
- date-line tile ranking with and without focus; the horizon cull idles
  while the camera stands still; the old zoom goes as soon as the new one
  lands; a rate limit holds every refresh inside its wait.

One real bug found and fixed: Esc inside a contenteditable cleared the
selected sequence (selection.js now ignores isContentEditable targets).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
Said TurkseverandClaude Opus 5.5 committed 2026-10-06 16:54:06 +01:00
1 parent 9dde7b4a62
commit 3af0f80cdb
15 files changed
+1198 -30

No files matched your search

+98
View File
@@ -3,6 +3,7 @@ import test from 'node:test';
import * as Cesium from 'cesium';
import {
cameraHeightAboveGround,
createHorizonCull,
metresBetween,
viewCentre,
viewFocus,
@@ -167,3 +168,100 @@ test('metresBetween measures the short way round the date line (review IC8 P2)',
) < 1e-9,
);
});
/**
* A horizon cull over `items` for a camera the test moves, with the frame's
* pre-render listeners and a count of the points tested against the horizon.
*/
function cullHarness(t, items) {
const visible = t.mock.method(
Cesium.EllipsoidalOccluder.prototype,
'isPointVisible',
);
const preRender = new Set();
const camera = { positionWC: Cesium.Cartesian3.fromDegrees(0, 0, 1e7) };
const viewer = {
camera,
scene: {
preRender: {
addEventListener(listener) {
preRender.add(listener);
return () => preRender.delete(listener);
},
},
},
};
let changes = 0;
const cull = createHorizonCull({
getViewer: () => viewer,
items: () => items,
onChange: () => changes++,
});
return {
cull,
preRender,
tested: () => visible.mock.callCount(),
changes: () => changes,
frame: () => {
for (const listener of [...preRender]) listener();
},
// Moved in place, as Cesium updates positionWC.
moveTo: (lon, lat) =>
Cesium.Cartesian3.fromDegrees(
lon,
lat,
1e7,
undefined,
camera.positionWC,
),
};
}
const point = (lon, lat) => ({
position: Cesium.Cartesian3.fromDegrees(lon, lat),
show: true,
});
test('the horizon cull does no work while the camera stands still', (t) => {
const near = point(0, 0);
const far = point(180, 0);
const h = cullHarness(t, [near, far]);
h.cull.update();
assert.equal(h.tested(), 2);
assert.deepEqual([near.show, far.show], [true, false]);
assert.equal(h.preRender.size, 1, 'listening for camera moves');
for (let i = 0; i < 5; i++) h.frame();
assert.equal(h.tested(), 2, 'a still camera re-culls nothing');
h.moveTo(180, 0);
h.frame();
assert.equal(h.tested(), 4, 'a moved camera re-culls everything once');
assert.deepEqual([near.show, far.show], [false, true]);
h.frame();
h.frame();
assert.equal(h.tested(), 4, 'and then rests again');
h.cull.stop();
assert.equal(h.preRender.size, 0);
});
test('the horizon cull stops listening once nothing is left, and starts again for new items', (t) => {
const items = [point(0, 0)];
const h = cullHarness(t, items);
h.cull.update();
assert.equal(h.preRender.size, 1);
// Every item goes (the tiles are dropped); the next camera move finds none.
items.length = 0;
h.moveTo(10, 0);
h.frame();
assert.equal(h.preRender.size, 0, 'the listener is removed');
// New items arrive: they are culled now, and camera moves are watched again.
const far = point(-170, 0);
items.push(far);
h.cull.update([far]);
assert.equal(far.show, false, 'culled on arrival');
assert.equal(h.preRender.size, 1, 'the listener is back');
h.moveTo(-170, 0);
h.frame();
assert.equal(far.show, true, 're-culled when the camera moves');
assert.equal(h.changes(), 2, 'a redraw for each change');
h.cull.stop();
});