Files
gods-eye-view/src/scopeMask.test.mjs
T
Bilawal SidhuandBilawal Sidhu a3a19883b4 feat: add Scope controls and conditional HUD text backplates (#1410)
* feat: add scope controls and conditional HUD backplates

* docs: assign scope changelog to PR 1410

* fix: stabilize title backplate and brighten secondary labels

---------

Co-authored-by: Bilawal Sidhu <[email protected]>
2026-10-26 00:24:27 -05:00

542 lines
24 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { test, beforeEach } from 'node:test';
import assert from 'node:assert/strict';
import {
scopeMaskGeometry,
scopeMaskDevicePixelRatio,
scopeTerminusAlpha,
quantizeScopeTerminusAlpha,
updateScopeTerminusForHeight,
setScopeTerminusOverride,
getScopeTerminusOverride,
getScopeTerminusAlpha,
getScopeTerminusRepaintCount,
clampScopeTerminusPct,
installScopeMask,
destroyScopeMask,
setScopeMaskEnabled,
setScopeMaskFeather,
SCOPE_TERMINUS_MIN_PCT,
SCOPE_TERMINUS_MAX_PCT,
SCOPE_FEATHER_RATIO_DEFAULT,
getScopeMaskFeather,
SCOPE_OUTSIDE_ALPHA,
SCOPE_TERMINUS_ALPHA_NEAR,
SCOPE_TERMINUS_FAR_M,
SCOPE_TERMINUS_NEAR_M,
SCOPE_TERMINUS_QUANTUM,
_resetScopeMaskForTest,
} from './scopeMask.js';
import { KEYHOLE_OUTER_RADIUS } from './celestialRing.js';
import { SCOPE_FEATHER_RATIO_MAX, clampScopeFeatherPct } from './scopeMaskPolicy.js';
beforeEach(() => _resetScopeMaskForTest());
test('geometry anchors the visible edge to the shared keyhole radius', () => {
const geo = scopeMaskGeometry(1440, 860, 0.4);
const keyholeR = 860 * 0.5 * KEYHOLE_OUTER_RADIUS;
assert.ok(geo);
assert.equal(geo.centerX, 720);
assert.equal(geo.centerY, 430);
// Feather straddles the keyhole edge: inner+outer average = keyhole radius.
assert.ok(Math.abs((geo.innerR + geo.outerR) / 2 - keyholeR) < 1e-9);
assert.ok(Math.abs((geo.outerR - geo.innerR) - keyholeR * 0.4) < 1e-9);
});
test('zero feather produces a hard edge exactly at the keyhole radius', () => {
const geo = scopeMaskGeometry(1000, 800, 0);
const keyholeR = 800 * 0.5 * KEYHOLE_OUTER_RADIUS;
assert.equal(geo.innerR, keyholeR);
assert.equal(geo.outerR, keyholeR);
});
test('feather ratio reaches the center at 200% and clamps larger input', () => {
const wide = scopeMaskGeometry(1000, 800, 7);
const maximum = scopeMaskGeometry(1000, 800, SCOPE_FEATHER_RATIO_MAX);
const keyholeR = 800 * 0.5 * KEYHOLE_OUTER_RADIUS;
assert.equal(maximum.innerR, 0);
assert.equal(maximum.outerR, keyholeR * 2);
assert.equal(wide.innerR, maximum.innerR);
assert.equal(wide.outerR, maximum.outerR);
for (const ratio of [1, 1.5, 2]) {
setScopeMaskFeather(ratio);
assert.equal(getScopeMaskFeather(), ratio);
const geometry = scopeMaskGeometry(1000, 800);
assert.equal((geometry.innerR + geometry.outerR) / 2, keyholeR);
assert.equal(geometry.outerR - geometry.innerR, keyholeR * ratio);
}
setScopeMaskFeather(7);
assert.equal(getScopeMaskFeather(), SCOPE_FEATHER_RATIO_MAX);
const nan = scopeMaskGeometry(1000, 800, Number.NaN);
const zero = scopeMaskGeometry(1000, 800, 0);
assert.equal(nan.innerR, zero.innerR);
});
test('UI and share feather percentages share one bounded integer range', () => {
for (const [value, expected] of [[0, 0], [11, 11], [175.6, 176], [200, 200], [350, 200], [-4, 0], [Number.NaN, 0]]) {
assert.equal(clampScopeFeatherPct(value), expected);
}
});
test('degenerate viewport yields null instead of a broken gradient', () => {
assert.equal(scopeMaskGeometry(0, 0), null);
assert.equal(scopeMaskGeometry(-5, 100), null);
});
test('an omitted feather argument uses the module default, whatever it is', () => {
// The assertion here is the WIRING — that calling scopeMaskGeometry with no
// ratio really consults SCOPE_FEATHER_RATIO_DEFAULT rather than some second,
// private constant. Comparing the omitted-argument geometry against an
// explicitly-passed default keeps that alive at ANY default value. The old
// form (band width === keyholeR × default) went VACUOUSLY true the moment the
// default moved to 0 on 2026-08-22 — both sides collapse to zero — so it is
// written this way deliberately.
const omitted = scopeMaskGeometry(1200, 900);
const explicit = scopeMaskGeometry(1200, 900, SCOPE_FEATHER_RATIO_DEFAULT);
assert.equal(omitted.innerR, explicit.innerR);
assert.equal(omitted.outerR, explicit.outerR);
// And it is not simply ignoring the argument: a different ratio must differ,
// and must still derive its band from the keyhole radius.
const wider = scopeMaskGeometry(1200, 900, SCOPE_FEATHER_RATIO_DEFAULT + 0.4);
assert.notEqual(wider.outerR - wider.innerR, omitted.outerR - omitted.innerR);
const keyholeR = 900 * 0.5 * KEYHOLE_OUTER_RADIUS;
assert.ok(Math.abs((wider.outerR - wider.innerR)
- keyholeR * (SCOPE_FEATHER_RATIO_DEFAULT + 0.4)) < 1e-9);
// The default's VALUE (hidden feather, owner directive 2026-08-22) is pinned
// with the rest of the first-run batch in reasonableDefaults.test.mjs.
});
test('backing-store scale is clamped to 2x and survives junk input', () => {
assert.equal(scopeMaskDevicePixelRatio(1), 1);
assert.equal(scopeMaskDevicePixelRatio(2), 2);
assert.equal(scopeMaskDevicePixelRatio(3), 2); // clamp — a 3x panel still draws at 2x
assert.equal(scopeMaskDevicePixelRatio(0), 1);
assert.equal(scopeMaskDevicePixelRatio(Number.NaN), 1);
});
/**
* Minimal DOM/matchMedia surface for the install path. Returns the created
* canvas plus a `setDpr` that fires a real `(resolution: Ndppx)` change the
* way moving a window between a 1x and a 2x monitor does.
*/
function stubScopeMaskDom({ width = 1000, height = 800, dpr = 1 } = {}) {
const saved = { window: globalThis.window, document: globalThis.document, ResizeObserver: globalThis.ResizeObserver };
// Records every fillStyle assignment and gradient stop so the paint's actual
// colours (not just its call sequence) can be asserted.
const fillStyles = [];
const gradientStops = [];
// Canvas work actually performed — the disabled scope must do none of it.
const ops = { resizes: 0, clears: 0, fills: 0 };
const canvas = {
id: '', style: {},
_width: 0, _height: 0,
set width(value) { ops.resizes += 1; this._width = value; },
get width() { return this._width; },
set height(value) { this._height = value; },
get height() { return this._height; },
setAttribute() {}, remove() {},
getContext: () => ({
setTransform() {}, beginPath() {}, rect() {}, arc() {},
clearRect() { ops.clears += 1; },
fill() { ops.fills += 1; },
fillRect() { ops.fills += 1; },
createRadialGradient: () => ({
addColorStop(offset, color) { gradientStops.push({ offset, color }); },
}),
set fillStyle(value) { if (typeof value === 'string') fillStyles.push(value); },
get fillStyle() { return fillStyles[fillStyles.length - 1] || ''; },
}),
};
const listeners = new Set();
globalThis.window = {
devicePixelRatio: dpr,
matchMedia: (query) => ({
media: query,
matches: true,
addEventListener: (type, fn) => listeners.add(fn),
removeEventListener: (type, fn) => listeners.delete(fn),
}),
};
const documentElement = { dataset: {} };
globalThis.document = { documentElement, createElement: () => canvas };
globalThis.ResizeObserver = class { observe() {} disconnect() {} };
const container = { clientWidth: width, clientHeight: height, appendChild() {} };
return {
canvas,
container,
documentElement,
/** Canvas work counters: backing-store resizes, clears, fills. */
ops,
/** Every fillStyle string assigned since install (hard-crop path). */
fillStyles: () => [...fillStyles],
/** Every gradient stop added since install (feathered path). */
gradientStops: () => [...gradientStops],
setDpr(next) {
globalThis.window.devicePixelRatio = next;
for (const fn of [...listeners]) fn();
},
restore() {
globalThis.window = saved.window;
globalThis.document = saved.document;
globalThis.ResizeObserver = saved.ResizeObserver;
},
};
}
test('scope appearance follows direct state changes and install/destroy lifecycles', () => {
const dom = stubScopeMaskDom();
try {
setScopeMaskEnabled(false);
assert.equal(dom.documentElement.dataset.scopeMask, 'off');
delete dom.documentElement.dataset.scopeMask;
installScopeMask({ container: dom.container });
assert.equal(dom.documentElement.dataset.scopeMask, 'off', 'install publishes retained OFF state');
destroyScopeMask();
assert.equal(dom.documentElement.dataset.scopeMask, undefined, 'destroy clears the document marker');
installScopeMask({ container: dom.container });
assert.equal(dom.documentElement.dataset.scopeMask, 'off', 'reinstall retains the preference');
setScopeMaskEnabled(true);
assert.equal(dom.documentElement.dataset.scopeMask, undefined, 'ON uses the original presentation');
setScopeMaskEnabled(false);
_resetScopeMaskForTest();
assert.equal(dom.documentElement.dataset.scopeMask, undefined, 'test reset clears the marker');
} finally {
destroyScopeMask();
dom.restore();
}
});
test('a DPR change with no resize still repaints the backing store', () => {
const dom = stubScopeMaskDom({ width: 1000, height: 800, dpr: 1 });
try {
installScopeMask({ container: dom.container });
// 1x monitor: backing store matches CSS pixels.
assert.equal(dom.canvas.width, 1000);
assert.equal(dom.canvas.height, 800);
// Window dragged to a 2x monitor. The content box never changed, so the
// ResizeObserver stays silent — only the DPR watch can catch this.
dom.setDpr(2);
assert.equal(dom.canvas.width, 2000, 'backing store must follow the new DPR');
assert.equal(dom.canvas.height, 1600);
// And back again — the listener must be re-armed after each change.
dom.setDpr(1);
assert.equal(dom.canvas.width, 1000, 'DPR watch must re-arm, not fire once');
assert.equal(dom.canvas.height, 800);
} finally {
destroyScopeMask();
dom.restore();
}
});
test('destroy tears the DPR watch down (no redraw after teardown)', () => {
const dom = stubScopeMaskDom({ width: 640, height: 480, dpr: 1 });
try {
installScopeMask({ container: dom.container });
assert.equal(dom.canvas.width, 640);
destroyScopeMask();
dom.setDpr(2);
assert.equal(dom.canvas.width, 640, 'a destroyed mask must not repaint');
} finally {
dom.restore();
}
});
// ── Altitude-adaptive edge terminus ──────────────────────────────────────────
//
// 0.94 is right at TRUE full-globe altitude (faint stars survive in the
// corners) and wrong everywhere else, where the same 6% bleed reads as smeared
// geometry. Owner band retune (2026-08-17 field test): the fade STARTS at
// ~10 Mm and is finished by ~7 Mm, so every working altitude is solid black.
// FEATHER is untouched (owner locked 35).
test('the terminus band is the owner-approved 10 Mm → 7 Mm fade', () => {
assert.equal(SCOPE_TERMINUS_FAR_M, 10_000_000, 'the relaxed corners start above 10 Mm');
assert.equal(SCOPE_TERMINUS_NEAR_M, 7_000_000, 'and are gone by 7 Mm');
assert.equal(scopeTerminusAlpha(10_500_000), SCOPE_OUTSIDE_ALPHA, 'full-globe view keeps its stars');
assert.equal(scopeTerminusAlpha(6_900_000), SCOPE_TERMINUS_ALPHA_NEAR, 'below the band: solid black');
assert.equal(scopeTerminusAlpha(400_000), SCOPE_TERMINUS_ALPHA_NEAR, 'orbital view: solid black');
assert.equal(scopeTerminusAlpha(1_500), SCOPE_TERMINUS_ALPHA_NEAR, 'city view: solid black');
// Quick, not gradual: a fifth of the way down the band it is already past halfway.
const fifth = SCOPE_TERMINUS_FAR_M - (SCOPE_TERMINUS_FAR_M - SCOPE_TERMINUS_NEAR_M) * 0.5;
assert.ok(scopeTerminusAlpha(fifth) > SCOPE_OUTSIDE_ALPHA + 0.02,
'the fade must be well underway by mid-band');
});
test('terminus ramp: translucent at globe scale, full black up close', () => {
assert.equal(scopeTerminusAlpha(SCOPE_TERMINUS_FAR_M), SCOPE_OUTSIDE_ALPHA);
assert.equal(scopeTerminusAlpha(SCOPE_TERMINUS_FAR_M + 1_000_000), SCOPE_OUTSIDE_ALPHA);
assert.equal(scopeTerminusAlpha(SCOPE_TERMINUS_NEAR_M), SCOPE_TERMINUS_ALPHA_NEAR);
assert.equal(scopeTerminusAlpha(0), SCOPE_TERMINUS_ALPHA_NEAR);
});
test('terminus ramp is monotonic, bounded, and eased at both ends', () => {
const mid = (SCOPE_TERMINUS_FAR_M + SCOPE_TERMINUS_NEAR_M) / 2;
assert.ok(Math.abs(scopeTerminusAlpha(mid) - (SCOPE_OUTSIDE_ALPHA + 0.06 / 2)) < 1e-9,
'smoothstep is symmetric — the midpoint sits halfway');
let previous = -Infinity;
for (let h = 15_000_000; h >= 0; h -= 100_000) {
const alpha = scopeTerminusAlpha(h);
assert.ok(alpha >= previous, `alpha must never decrease while descending (h=${h})`);
assert.ok(alpha >= SCOPE_OUTSIDE_ALPHA && alpha <= 1, `alpha out of range at h=${h}`);
previous = alpha;
}
// Eased, not linear: near each end the slope is gentler than the midpoint's.
const step = 100_000;
const nearEnd = scopeTerminusAlpha(SCOPE_TERMINUS_NEAR_M + step) - scopeTerminusAlpha(SCOPE_TERMINUS_NEAR_M);
const middle = scopeTerminusAlpha(mid) - scopeTerminusAlpha(mid + step);
assert.ok(Math.abs(nearEnd) < Math.abs(middle), 'the ramp must ease into the near clamp');
});
test('terminus ramp survives junk heights without breaking the paint', () => {
assert.equal(scopeTerminusAlpha(Number.NaN), SCOPE_OUTSIDE_ALPHA);
assert.equal(scopeTerminusAlpha(undefined), SCOPE_OUTSIDE_ALPHA);
assert.equal(scopeTerminusAlpha(Number.POSITIVE_INFINITY), SCOPE_OUTSIDE_ALPHA);
assert.equal(scopeTerminusAlpha(-1000), SCOPE_TERMINUS_ALPHA_NEAR, 'below the datum is still "close"');
});
test('quantization snaps to the repaint grid', () => {
assert.equal(quantizeScopeTerminusAlpha(0.9412), 0.94);
assert.equal(quantizeScopeTerminusAlpha(0.9436), 0.945);
assert.equal(quantizeScopeTerminusAlpha(1), 1);
assert.equal(quantizeScopeTerminusAlpha(5), 1, 'clamped');
assert.equal(quantizeScopeTerminusAlpha(Number.NaN), 0);
});
test('repaint gate: only a QUANTIZED step repaints; hovering costs nothing', () => {
const dom = stubScopeMaskDom({ width: 1000, height: 800, dpr: 1 });
try {
installScopeMask({ container: dom.container });
const base = getScopeTerminusRepaintCount();
// Parked at true full-globe altitude: the seeded alpha already matches, so
// no repaint — and repeating the identical sample stays free.
assert.equal(updateScopeTerminusForHeight(14_000_000), false);
assert.equal(updateScopeTerminusForHeight(14_000_000), false);
assert.equal(getScopeTerminusRepaintCount(), base, 'a still camera must never repaint');
// A real step repaints exactly once, then holds.
assert.equal(updateScopeTerminusForHeight(SCOPE_TERMINUS_NEAR_M), true);
assert.equal(getScopeTerminusAlpha(), 1);
assert.equal(updateScopeTerminusForHeight(SCOPE_TERMINUS_NEAR_M), false);
assert.equal(getScopeTerminusRepaintCount(), base + 1);
// A sub-quantum drift inside the ramp must NOT repaint. One 0.005 step is
// ~166 km of altitude at mid-band, so 1 km is nowhere near it.
updateScopeTerminusForHeight(8_500_000);
const afterMid = getScopeTerminusRepaintCount();
updateScopeTerminusForHeight(8_501_000);
assert.equal(getScopeTerminusRepaintCount(), afterMid, 'sub-quantum drift must not repaint');
} finally {
destroyScopeMask();
dom.restore();
}
});
test('a full zoom-in gesture costs only a handful of repaints', () => {
const dom = stubScopeMaskDom({ width: 1000, height: 800, dpr: 1 });
try {
installScopeMask({ container: dom.container });
const base = getScopeTerminusRepaintCount();
// 20 Mm → ground in 10 km steps: ~2000 samples across the whole descent,
// far denser than the 120 ms sampler could ever produce in a real gesture,
// and covering the entire retuned band with room on both sides.
for (let h = 20_000_000; h >= 0; h -= 10_000) updateScopeTerminusForHeight(h);
const repaints = getScopeTerminusRepaintCount() - base;
// The ramp spans 0.06 alpha at 0.005 granularity → at most 13 distinct steps.
assert.ok(repaints <= 13, `expected ≤13 repaints across the descent, got ${repaints}`);
assert.ok(repaints >= 5, `expected a visibly smooth ramp, got only ${repaints}`);
assert.equal(getScopeTerminusAlpha(), 1, 'the descent must end fully opaque');
} finally {
destroyScopeMask();
dom.restore();
}
});
test('an override pins the terminus and null restores the ramp', () => {
const dom = stubScopeMaskDom({ width: 1000, height: 800, dpr: 1 });
try {
installScopeMask({ container: dom.container });
setScopeTerminusOverride(0.97);
assert.equal(getScopeTerminusOverride(), 0.97);
assert.equal(getScopeTerminusAlpha(), 0.97);
// Pinned: altitude no longer moves it.
assert.equal(updateScopeTerminusForHeight(0), false);
assert.equal(getScopeTerminusAlpha(), 0.97);
setScopeTerminusOverride(null);
assert.equal(getScopeTerminusOverride(), null);
assert.equal(updateScopeTerminusForHeight(0), true, 'the ramp is live again');
assert.equal(getScopeTerminusAlpha(), 1);
} finally {
destroyScopeMask();
dom.restore();
}
});
test('the hard-crop (feather 0) path honors the same terminus alpha', () => {
const dom = stubScopeMaskDom({ width: 1000, height: 800, dpr: 1 });
try {
installScopeMask({ container: dom.container });
updateScopeTerminusForHeight(SCOPE_TERMINUS_FAR_M + 5_000_000); // true full-globe view
setScopeMaskFeather(0); // hard crop — the evenodd rect-minus-circle path
// The globe-scale seed paint is legitimately in the history, so assert on
// the LAST fill: what the hard crop is painting right now, zoomed in.
const beforeDescent = dom.fillStyles().at(-1);
assert.equal(beforeDescent, `rgba(5,5,8,${SCOPE_OUTSIDE_ALPHA})`,
'at globe scale the hard crop is still the translucent terminus');
updateScopeTerminusForHeight(SCOPE_TERMINUS_NEAR_M);
assert.equal(dom.fillStyles().at(-1), 'rgba(5,5,8,1)',
'a zoomed-in hard crop must paint fully opaque, not the globe-scale 0.94');
} finally {
setScopeMaskFeather(SCOPE_FEATHER_RATIO_DEFAULT);
destroyScopeMask();
dom.restore();
}
});
test('one quantum is the smallest step that can repaint', () => {
assert.ok(SCOPE_TERMINUS_QUANTUM > 0 && SCOPE_TERMINUS_QUANTUM < 0.06,
'the quantum must be finer than the ramp it gates');
});
// ── SCOPE OFF must be the cheapest state (review round 2) ─────────────────────
//
// With sc=0 the mask paints nothing, but the camera listeners still sampled at
// ~8 Hz through the altitude band and draw() performed the full backing-store
// resize + clear BEFORE checking whether the scope was even enabled.
/** A viewer stub with the two camera signals the terminus sampler listens on. */
function stubScopeViewer(container, heightM) {
const preRenderListeners = new Set();
const moveEndListeners = new Set();
const positionCartographic = { height: heightM };
const addTo = (set) => (listener) => {
set.add(listener);
return () => set.delete(listener);
};
return {
viewer: {
container,
scene: { preRender: { addEventListener: addTo(preRenderListeners) } },
camera: { positionCartographic, moveEnd: { addEventListener: addTo(moveEndListeners) } },
},
setHeight(next) { positionCartographic.height = next; },
raisePreRender(times = 1) {
for (let i = 0; i < times; i += 1) for (const fn of [...preRenderListeners]) fn();
},
raiseMoveEnd() { for (const fn of [...moveEndListeners]) fn(); },
};
}
test('a disabled scope does no canvas work and samples no camera heights', () => {
const dom = stubScopeMaskDom({ width: 1000, height: 800, dpr: 1 });
const rig = stubScopeViewer(dom.container, 14_000_000); // true full-globe view
try {
installScopeMask(rig.viewer);
assert.equal(getScopeTerminusAlpha(), SCOPE_OUTSIDE_ALPHA);
// The transition must clear the painted mask EXACTLY once, and must not
// resize the backing store to do it.
const beforeDisable = { ...dom.ops };
setScopeMaskEnabled(false);
const afterDisable = { ...dom.ops };
assert.equal(afterDisable.clears, beforeDisable.clears + 1,
'the disable transition clears the mask exactly once');
assert.equal(afterDisable.resizes, beforeDisable.resizes,
'and clears without a backing-store resize');
assert.equal(afterDisable.fills, beforeDisable.fills, 'and paints nothing');
// A second disable is inert — the canvas already holds no ink.
setScopeMaskEnabled(false);
assert.deepEqual(dom.ops, afterDisable, 'a repeated disable must re-clear nothing');
// Fly the whole band while OFF: the sampler must not read the camera, and
// no draw may touch the backing store.
rig.setHeight(1_000);
rig.raisePreRender(40);
rig.raiseMoveEnd();
setScopeMaskFeather(0.5); // any tuning write still routes through draw()
assert.deepEqual(dom.ops, afterDisable, 'a disabled scope must do NO canvas work');
assert.equal(getScopeTerminusAlpha(), SCOPE_OUTSIDE_ALPHA, 'and must not track the camera');
// Re-enable: the alpha it skipped is re-synced once, before the first paint.
const beforeEnable = { ...dom.ops };
setScopeMaskEnabled(true);
assert.equal(getScopeTerminusAlpha(), SCOPE_TERMINUS_ALPHA_NEAR,
're-enabling must re-sync the terminus it stopped sampling');
assert.ok(dom.ops.resizes > beforeEnable.resizes, 'and repaint at the live altitude');
} finally {
setScopeMaskFeather(SCOPE_FEATHER_RATIO_DEFAULT);
destroyScopeMask();
dom.restore();
}
});
test('a DPR change that also crosses a terminus step paints once, not twice', () => {
const dom = stubScopeMaskDom({ width: 1000, height: 800, dpr: 1 });
const rig = stubScopeViewer(dom.container, SCOPE_TERMINUS_FAR_M);
try {
installScopeMask(rig.viewer);
assert.equal(getScopeTerminusAlpha(), SCOPE_OUTSIDE_ALPHA);
// Descend into the band WITHOUT a frame, then drag the window to a 2x
// monitor: the DPR change and the pending terminus step land together.
rig.setHeight(8_500_000);
const before = { ...dom.ops };
const repaintsBefore = getScopeTerminusRepaintCount();
dom.setDpr(2);
assert.equal(dom.ops.resizes - before.resizes, 1, 'both changes must share ONE paint');
assert.equal(dom.canvas.width, 2000, 'and it must be at the new backing-store scale');
assert.equal(getScopeTerminusAlpha(), 0.97, 'with the stepped terminus already folded in');
assert.equal(getScopeTerminusRepaintCount(), repaintsBefore + 1, 'counted as one repaint');
} finally {
destroyScopeMask();
dom.restore();
}
});
// ── `sce` band clamp (shared by the share layer and the UI) ──────────────────
test('terminus percents clamp into the supported band; junk is absent, not zero', () => {
assert.equal(SCOPE_TERMINUS_MIN_PCT, 94);
assert.equal(SCOPE_TERMINUS_MAX_PCT, 100);
assert.equal(clampScopeTerminusPct(97), 97);
assert.equal(clampScopeTerminusPct(96.6), 97);
assert.equal(clampScopeTerminusPct(0), 94, 'a sub-band value is floored, never honoured');
assert.equal(clampScopeTerminusPct(93), 94);
assert.equal(clampScopeTerminusPct(-100), 94);
assert.equal(clampScopeTerminusPct(1000), 100);
assert.equal(clampScopeTerminusPct('98'), 98, 'hash values arrive as strings');
assert.equal(clampScopeTerminusPct(null), null, 'absent means adaptive, not 0');
assert.equal(clampScopeTerminusPct(undefined), null);
assert.equal(clampScopeTerminusPct(''), null);
assert.equal(clampScopeTerminusPct('abc'), null);
assert.equal(clampScopeTerminusPct(Number.NaN), null);
assert.equal(clampScopeTerminusPct(true), null);
});
test('a pinned override is floored to the band at every entry point', () => {
const dom = stubScopeMaskDom({ width: 1000, height: 800, dpr: 1 });
try {
installScopeMask({ container: dom.container });
setScopeTerminusOverride(0); // an sce=0 link, or any stale caller
assert.equal(getScopeTerminusOverride(), SCOPE_OUTSIDE_ALPHA,
'a fully transparent terminus is a hole in the mask, not a scope');
assert.equal(getScopeTerminusAlpha(), SCOPE_OUTSIDE_ALPHA);
setScopeTerminusOverride(5);
assert.equal(getScopeTerminusOverride(), 1, 'and the ceiling still holds');
} finally {
destroyScopeMask();
dom.restore();
}
});