aboutsummaryrefslogtreecommitdiff
path: root/tests/gallery-probes/probe.mjs
diff options
context:
space:
mode:
Diffstat (limited to 'tests/gallery-probes/probe.mjs')
-rw-r--r--tests/gallery-probes/probe.mjs108
1 files changed, 54 insertions, 54 deletions
diff --git a/tests/gallery-probes/probe.mjs b/tests/gallery-probes/probe.mjs
index e394316..d9ba22f 100644
--- a/tests/gallery-probes/probe.mjs
+++ b/tests/gallery-probes/probe.mjs
@@ -1,4 +1,4 @@
-// CDP probe for the widget gallery — no playwright, Node global WebSocket/fetch.
+// CDP probe for the instrument gallery — no playwright, Node global WebSocket/fetch.
// Usage: node probe.mjs [--shot out.png] [--size N]
import { spawn } from 'node:child_process';
import { writeFileSync } from 'node:fs';
@@ -65,7 +65,7 @@ try {
const errs = events.filter(e => e.method === 'Runtime.exceptionThrown');
ok('no exceptions on load', errs.length === 0, errs.map(e => e.params.exceptionDetails?.exception?.description).join('; ').slice(0, 200));
- // 1. card count (the widgets/row default is checked in 1h)
+ // 1. card count (the instruments/row default is checked in 1h)
const cards = await evl(`document.querySelectorAll('.card').length`);
ok('111 cards', cards === 111, `got ${cards}`);
@@ -89,7 +89,7 @@ try {
const chips = [...document.querySelectorAll('#card-07 .fc')].map(c => c.title);
const missing = want.filter(w => !chips.includes(w));
if (missing.length) return 'card 07 missing accents: ' + missing.join(' ');
- const built = Object.keys(GW.accentStyles('--x'));
+ const built = Object.keys(DUPRE.accentStyles('--x'));
return JSON.stringify(built) === JSON.stringify(want) ? 'ok' : 'family is ' + JSON.stringify(built);
})()`);
ok('card 07 offers the shared accent family', accents === 'ok', accents);
@@ -107,7 +107,7 @@ try {
})()`);
ok('card 07 defaults to gold and recolours on click', chipInk === 'ok', chipInk);
- // 1e. The colour-policy backbone. Every widget's colour is either free (the
+ // 1e. The colour-policy backbone. Every instrument's colour is either free (the
// consumer picks) or locked for a stated reason, and the colour pass kept
// finding cards where "is this one a standard?" was answered from memory and
// wrong. So policy is declared on the builder and checked here. The invariant
@@ -116,10 +116,10 @@ try {
// an accent card can't forget to say so. Coverage grows card by card; this
// round classifies the touched set, and the count is reported not gated.
const policy = await evl(`(()=>{
- const KINDS = GW.POLICIES;
+ const KINDS = DUPRE.POLICIES;
if (!KINDS) return 'no POLICIES vocabulary';
const valid = new Set(Object.keys(KINDS));
- const fns = Object.entries(GW).filter(([,v]) => typeof v === 'function');
+ const fns = Object.entries(DUPRE).filter(([,v]) => typeof v === 'function');
const declared = fns.filter(([,v]) => v.POLICY);
// each POLICY record is complete: a valid kind, a why, an authentic range
const shape = declared.filter(([,v]) => !valid.has(v.POLICY.kind) || !v.POLICY.why || !v.POLICY.authentic)
@@ -177,10 +177,10 @@ try {
})()`);
ok('colour policy tally sums to the card total', ptally.startsWith('ok'), ptally);
- // 1h. widgets/row: the control sets an explicit column count and the cards
+ // 1h. instruments/row: the control sets an explicit column count and the cards
// resize to match. One-per-row cards must be much wider than four-per-row,
// and the count actually reaches the grid.
- ok('default is 3 widgets/row', await evl(`document.body.dataset.cols`) === '3');
+ ok('default is 3 instruments/row', await evl(`document.body.dataset.cols`) === '3');
await evl(`document.querySelector('.szbar .key[data-cols="1"]').click()`);
const w1col = await evl(`document.querySelector('.card').getBoundingClientRect().width`);
const cols1 = await evl(`getComputedStyle(document.querySelector('.grid')).gridTemplateColumns.split(' ').length`);
@@ -189,7 +189,7 @@ try {
const cols4 = await evl(`getComputedStyle(document.querySelector('.grid')).gridTemplateColumns.split(' ').length`);
ok('1/row is much wider than 4/row', w1col > w4col * 2.5, `w1=${Math.round(w1col)} w4=${Math.round(w4col)}`);
ok('the grid renders the chosen column count', cols1 === 1 && cols4 === 4, `1->${cols1} 4->${cols4}`);
- ok('widgets/row chip flips state', await evl(`document.body.dataset.cols`) === '4');
+ ok('instruments/row chip flips state', await evl(`document.body.dataset.cols`) === '4');
// 3. behavioral, zoomed: fader drag on card 03 changes readout. Run at 2/row,
// not 1/row: at one-per-row the card is wide enough that its controls fall
@@ -245,7 +245,7 @@ try {
// disengaged state instead. dim offText lets an off toggle recede in a dense
// panel. Craig's call, 2026-07-16.
const atoms = await evl(`(()=>{
- const S = GW.slideToggle.STYLES;
+ const S = DUPRE.slideToggle.STYLES;
const want = { on: ['amber','green','dark','red','warn'], onText: ['panel','cream','green'],
off: ['dark','red'], offText: ['white','red','black','dim'],
thumb: ['light','dark','chrome','brass'] };
@@ -260,9 +260,9 @@ try {
// must resolve in STYLES — a typo here would silently fall through to the
// stylesheet default and look "nearly right", which is the worst outcome.
const presets = await evl(`(()=>{
- const P = GW.slideToggle.PRESETS, S = GW.slideToggle.STYLES;
+ const P = DUPRE.slideToggle.PRESETS, S = DUPRE.slideToggle.STYLES;
if (!P) return 'no PRESETS';
- const AX = GW.slideToggle.AXIS_ORDER;
+ const AX = DUPRE.slideToggle.AXIS_ORDER;
const bad = [];
for (const [name, p] of Object.entries(P)) {
for (const ax of AX) {
@@ -279,7 +279,7 @@ try {
// the only thing carrying state. Both backgrounds must stay dark while the
// inks diverge — if either pill lights, the preset has lost its point.
const dark = await evl(`(()=>{
- const P = GW.slideToggle.PRESETS.dark, S = GW.slideToggle.STYLES;
+ const P = DUPRE.slideToggle.PRESETS.dark, S = DUPRE.slideToggle.STYLES;
const onBg = S.on[P.on].vars['--sw-on-bg'], offBg = S.off[P.off].vars['--sw-off-bg'];
const onInk = S.onText[P.onText].vars['--sw-on-ink'], offInk = S.offText[P.offText].vars['--sw-off-ink'];
if (onBg !== offBg) return 'pills differ: on=' + onBg + ' off=' + offBg;
@@ -288,7 +288,7 @@ try {
})()`);
ok('dark preset keeps both pills dark and the inks distinct', dark === 'ok', dark);
- // 7b. the card claims the preset it is actually in. The widget defaults to
+ // 7b. the card claims the preset it is actually in. The instrument defaults to
// `panel`, so a preset group with nothing lit would assert "no preset
// active" — false, and exactly the kind of quiet mislabel this walk exists
// to catch. Nothing above this point touches card 01's chips (checks 2-4
@@ -304,8 +304,8 @@ try {
})()`);
ok('card 01 defaults to the panel preset', defaultPreset === 'panel', defaultPreset);
- // 8. a preset chip drives the widget AND re-syncs the axis chips, so the card
- // never shows a combination the widget isn't in.
+ // 8. a preset chip drives the instrument AND re-syncs the axis chips, so the card
+ // never shows a combination the instrument isn't in.
const applied = await evl(`(()=>{
const card = document.getElementById('card-01');
const groups = [...card.querySelectorAll('.fgroup')];
@@ -319,7 +319,7 @@ try {
const brd = card.querySelector('.switch').style.getPropertyValue('--sw-on-brd');
return (lit.length === 1 ? lit[0].title : 'lit=' + lit.length) + '|' + brd;
})()`);
- ok('armed preset drives widget and syncs the on chip', applied === 'red|var(--fail)', applied);
+ ok('armed preset drives instrument and syncs the on chip', applied === 'red|var(--fail)', applied);
// 8b. axes are not independent: onText overrides the ink `on` sets, so changing
// `on` after picking an onText must not silently revert the legend while its
@@ -505,10 +505,10 @@ try {
// 13. KEYS is a declarative TABLE, not a function over a DOM event. The Emacs
// port installs this same table into a keymap — it never sees a keydown —
- // so a function here would force it to re-derive the widget's intent and
+ // so a function here would force it to re-derive the instrument's intent and
// the two bindings would drift. (README, keyboard contract.)
const keysTable = await evl(`(()=>{
- const K = GW.abcKeypad.KEYS;
+ const K = DUPRE.abcKeypad.KEYS;
if (!K) return 'no KEYS';
if (typeof K !== 'object' || Array.isArray(K)) return 'KEYS is not a plain table: ' + typeof K;
const missing = [...'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'].filter(c => K[c] !== c);
@@ -519,7 +519,7 @@ try {
})()`);
ok('R57 KEYS is a declarative table covering the plate', keysTable === 'ok', keysTable);
- // 13b. THE contract's first rule: no document-level listener. A widget that
+ // 13b. THE contract's first rule: no document-level listener. An instrument that
// binds globally types into itself from anywhere on a 110-card page.
// Typing at the body with the card unfocused must do nothing at all.
const unfocused = await evl(`(()=>{
@@ -560,12 +560,12 @@ try {
const seq = ['A','B','SPC','7'];
key('CLR').dispatchEvent(new MouseEvent('click', {bubbles:true}));
seq.forEach(k => key(k).dispatchEvent(new MouseEvent('click', {bubbles:true})));
- const byClick = rd() + '/' + document.getElementById('card-R57').gw.get();
+ const byClick = rd() + '/' + document.getElementById('card-R57').dupre.get();
key('CLR').dispatchEvent(new MouseEvent('click', {bubbles:true}));
pad.focus();
['A','B',' ','7'].forEach(k =>
pad.dispatchEvent(new KeyboardEvent('keydown', { key: k, bubbles: true, cancelable: true })));
- const byKey = rd() + '/' + document.getElementById('card-R57').gw.get();
+ const byKey = rd() + '/' + document.getElementById('card-R57').dupre.get();
return byClick === byKey ? 'ok' : 'drift: click=' + byClick + ' key=' + byKey;
})()`);
ok('R57 click and key land in the same place', drift === 'ok', drift);
@@ -576,7 +576,7 @@ try {
// caller is a hole in exactly the target the table exists for.
const pressGuard = await evl(`(()=>{
const card = document.getElementById('card-R57');
- const h = card.gw;
+ const h = card.dupre;
if (!h || !h.press) return 'no handle';
const key = k => [...card.querySelectorAll('.kp-key')].find(e => e.dataset.k === k);
key('CLR').dispatchEvent(new MouseEvent('click', {bubbles:true}));
@@ -590,7 +590,7 @@ try {
// 13c-4. Every key the table maps must be a real plate action, or the Emacs
// port installs a binding that silently does nothing.
const keysSubset = await evl(`(()=>{
- const bad = Object.entries(GW.abcKeypad.KEYS).filter(([,v]) => !GW.abcKeypad.ACTIONS.has(v));
+ const bad = Object.entries(DUPRE.abcKeypad.KEYS).filter(([,v]) => !DUPRE.abcKeypad.ACTIONS.has(v));
return bad.length ? 'KEYS maps to non-actions: ' + JSON.stringify(bad) : 'ok';
})()`);
ok('R57 every KEYS value is a real plate action', keysSubset === 'ok', keysSubset);
@@ -598,7 +598,7 @@ try {
// 13d. preventDefault is spent only where there is a default worth killing.
// Space scrolls and Backspace navigates back, so those are claimed; Tab is
// how the page is navigable and Escape belongs to the audit stepper, so a
- // widget that swallows either breaks something it cannot see.
+ // instrument that swallows either breaks something it cannot see.
const defaults = await evl(`(()=>{
const pad = document.querySelector('#card-R57 .kp-pad');
pad.focus();
@@ -637,11 +637,11 @@ try {
// click ever prints, the card has silently become a keypad with extra steps.
// Reads the BUFFER, not the card readout: the readout is supposed to change
// while hunting ("stylus over g"), and asserting on it would fail a correct
- // widget for showing the operator where the pointer is.
+ // instrument for showing the operator where the pointer is.
const grammar = await evl(`(()=>{
const cell = c => document.querySelector('#card-R58 .ix-cell[data-c="' + c + '"]');
const lever = document.querySelector('#card-R58 .ix-lever');
- const buf = () => document.getElementById('card-R58').gw.get();
+ const buf = () => document.getElementById('card-R58').dupre.get();
document.querySelector('#card-R58 .ix-clear').dispatchEvent(new MouseEvent('click', {bubbles:true}));
const empty = buf();
['M','i','g'].forEach(c => cell(c).dispatchEvent(new MouseEvent('click', {bubbles:true})));
@@ -674,7 +674,7 @@ try {
cell('s').dispatchEvent(new MouseEvent('click', {bubbles:true}));
lever.dispatchEvent(new MouseEvent('click', {bubbles:true}));
lever.dispatchEvent(new MouseEvent('click', {bubbles:true}));
- return document.getElementById('card-R58').gw.get();
+ return document.getElementById('card-R58').dupre.get();
})()`);
/* Exact, on the buffer. /ss$/ on the readout also matched 'sss', so it passed
even if selecting printed — unable to fail on the one bug this card is about. */
@@ -722,7 +722,7 @@ try {
// redraw. Craig has already said the keys will change; a layout welded into
// the drawing is one that never does.
const layoutData = await evl(`(()=>{
- const L = GW.indexPlate && GW.indexPlate.LAYOUT;
+ const L = DUPRE.indexPlate && DUPRE.indexPlate.LAYOUT;
if (!L) return 'no LAYOUT';
if (!Array.isArray(L) || !L.every(Array.isArray)) return 'LAYOUT is not a grid';
const cells = document.querySelectorAll('#card-R58 .ix-cell').length;
@@ -778,7 +778,7 @@ try {
// jump the stylus across the plate to the second. 14e can't see it — a
// duplicate inflates the drawn count and the declared count equally.
const dupes = await evl(`(()=>{
- const flat = GW.indexPlate.LAYOUT.flat().filter(Boolean);
+ const flat = DUPRE.indexPlate.LAYOUT.flat().filter(Boolean);
const seen = new Set(), dup = new Set();
for (const c of flat) (seen.has(c) ? dup : seen).add(c);
return dup.size ? 'duplicated on the plate: ' + [...dup].join(' ') : 'ok';
@@ -788,7 +788,7 @@ try {
// 14i. press() is the allowlist for R58 too. R57 has this check; without it,
// press('F1') reaching select() unfiltered would ship green.
const ixPress = await evl(`(()=>{
- const h = document.getElementById('card-R58').gw;
+ const h = document.getElementById('card-R58').dupre;
h.press('CLR');
['F1', 'PRINT ', '', 'constructor', 'ZZ'].forEach(k => h.press(k));
return JSON.stringify([h.get(), h.selected()]);
@@ -799,7 +799,7 @@ try {
// relaying the plate can't leave a keybinding pointing at a character the
// plate no longer carries. Enter is the lever.
const ixKeys = await evl(`(()=>{
- const K = GW.indexPlate.KEYS, L = GW.indexPlate.LAYOUT;
+ const K = DUPRE.indexPlate.KEYS, L = DUPRE.indexPlate.LAYOUT;
if (!K) return 'no KEYS';
const chars = L.flat().filter(Boolean);
const missing = chars.filter(c => K[c] !== c);
@@ -816,7 +816,7 @@ try {
// demonstrate is gone.
const ixType = await evl(`(()=>{
const pad = document.querySelector('#card-R58 .ix-pad');
- const h = document.getElementById('card-R58').gw;
+ const h = document.getElementById('card-R58').dupre;
const send = k => pad.dispatchEvent(new KeyboardEvent('keydown', { key: k, bubbles: true, cancelable: true }));
document.querySelector('#card-R58 .ix-clear').dispatchEvent(new MouseEvent('click', {bubbles:true}));
pad.focus();
@@ -834,7 +834,7 @@ try {
// argument for having no shift key.
const ixCase = await evl(`(()=>{
const pad = document.querySelector('#card-R58 .ix-pad');
- const h = document.getElementById('card-R58').gw;
+ const h = document.getElementById('card-R58').dupre;
const send = k => pad.dispatchEvent(new KeyboardEvent('keydown', { key: k, bubbles: true, cancelable: true }));
document.querySelector('#card-R58 .ix-clear').dispatchEvent(new MouseEvent('click', {bubbles:true}));
pad.focus();
@@ -857,12 +857,12 @@ try {
ok('R58 leaves Space alone (not on the plate)', ixSpace === 'ok', ixSpace);
// 15e. Unfocused, it hears nothing — the contract's first rule, on the second
- // widget to take keys.
+ // instrument to take keys.
// Asserts nothing CHANGED, rather than expecting a cleared selection: CLR
// is fresh paper, and fresh paper doesn't move the operator's hand, so the
// stylus legitimately stays where the previous check left it.
const ixBlur = await evl(`(()=>{
- const h = document.getElementById('card-R58').gw;
+ const h = document.getElementById('card-R58').dupre;
document.querySelector('#card-R58 .ix-clear').dispatchEvent(new MouseEvent('click', {bubbles:true}));
document.activeElement.blur();
const before = JSON.stringify([h.get(), h.selected()]);
@@ -877,7 +877,7 @@ try {
// through the declared charset one flap at a time, retarget cleanly, and
// animate:false jumps straight to the target.
const flapApi = await evl(`(() => {
- const h = document.querySelector('#card-N20').gw;
+ const h = document.querySelector('#card-N20').dupre;
return { reading: typeof h.reading, chars: typeof h.chars, set: typeof h.set };
})()`);
ok('N20 handle exposes reading() and chars', flapApi.reading === 'function' && flapApi.chars === 'string',
@@ -889,12 +889,12 @@ try {
// Let the board settle on a known grid first — the demo ticker left it
// anywhere, and every command cascades (there is deliberately no teleport
// path). setText is the deterministic seam; next() is random by design.
- await evl(`document.querySelector('#card-N20').gw.setText('SYSTEM\\nVOLUME\\nSIGNAL')`);
+ await evl(`document.querySelector('#card-N20').dupre.setText('SYSTEM\\nVOLUME\\nSIGNAL')`);
await sleep(6500);
// Start a cascade and sample the displayed reading every 15ms until stable.
await evl(`(() => {
- const h = document.querySelector('#card-N20').gw;
+ const h = document.querySelector('#card-N20').dupre;
window.__n20 = { samples: [], anims: 0 };
h.setText('VOLUME\\nSIGNAL\\nRECORD'); // multi-step distances on several cells
window.__n20.n = 0;
@@ -911,7 +911,7 @@ try {
await sleep(6500);
const n20 = await evl(`(() => {
clearInterval(window.__n20.timer);
- const h = document.querySelector('#card-N20').gw;
+ const h = document.querySelector('#card-N20').dupre;
return { samples: [...new Set(window.__n20.samples)], final: h.reading(), anims: window.__n20.anims,
chars: h.chars };
})()`);
@@ -933,7 +933,7 @@ try {
// Retarget mid-cascade: aim at word 2, then word 3 while still spinning; must land on 3.
await evl(`(() => {
- const h = document.querySelector('#card-N20').gw;
+ const h = document.querySelector('#card-N20').dupre;
window.__n20r = { samples: [] };
window.__n20r.timer = setInterval(() => window.__n20r.samples.push(h.reading()), 15);
h.setText('SIGNAL\\nRECORD\\nONLINE'); setTimeout(() => h.setText('RECORD\\nONLINE\\nSTEREO'), 180);
@@ -941,7 +941,7 @@ try {
await sleep(7000);
const n20b = await evl(`(() => {
clearInterval(window.__n20r.timer);
- const h = document.querySelector('#card-N20').gw;
+ const h = document.querySelector('#card-N20').dupre;
return { final: h.reading().split('\\n')[0], target: 'RECORD',
sawSync: window.__n20r.samples.some(s => s.split('\\n')[0] === 'SIGNAL'),
anims: document.querySelector('#card-N20').getAnimations({ subtree: true }).length };
@@ -955,7 +955,7 @@ try {
const n20s = await evl(`(() => {
const el = document.createElement('div'); document.body.appendChild(el);
const pool = ['SYSTEM','VOLUME','SIGNAL','RECORD','ONLINE','STEREO','FILTER','OUTPUT'];
- const h = GW.splitFlap(el, { rows: 3, cells: 6, words: pool, animate: false });
+ const h = DUPRE.splitFlap(el, { rows: 3, cells: 6, words: pool, animate: false });
const grids = [h.reading().split('\\n')];
h.next(); grids.push(h.reading().split('\\n'));
h.next(); grids.push(h.reading().split('\\n'));
@@ -975,7 +975,7 @@ try {
const n20c = await evl(`(() => {
const el = document.createElement('div'); document.body.appendChild(el);
const settles = [];
- const h = GW.splitFlap(el, { animate: false, onSettle: r => settles.push(r) });
+ const h = DUPRE.splitFlap(el, { animate: false, onSettle: r => settles.push(r) });
h.set(1);
const r = h.reading(); el.remove();
return { r, settles };
@@ -986,7 +986,7 @@ try {
// The card is a three-row six-letter board; the demo draws random pool words.
const n20d = await evl(`(() => {
- const h = document.querySelector('#card-N20').gw;
+ const h = document.querySelector('#card-N20').dupre;
return { cells: document.querySelectorAll('#card-N20 .flapd').length,
lines: document.querySelectorAll('#card-N20 .flapline').length,
readLines: h.reading().split('\\n').length };
@@ -997,9 +997,9 @@ try {
// Skin axis: dark (cream on black) and the inverse light (black on cream),
// switched by the chip rig via setStyle, computed ink actually inverting.
const n20e = await evl(`(() => {
- const h = document.querySelector('#card-N20').gw;
- if (typeof h.setStyle !== 'function' || !GW.splitFlap.STYLES || !GW.splitFlap.STYLES.skin) return 'no skin axis';
- const skins = Object.keys(GW.splitFlap.STYLES.skin);
+ const h = document.querySelector('#card-N20').dupre;
+ if (typeof h.setStyle !== 'function' || !DUPRE.splitFlap.STYLES || !DUPRE.splitFlap.STYLES.skin) return 'no skin axis';
+ const skins = Object.keys(DUPRE.splitFlap.STYLES.skin);
if (skins[0] !== 'paper') return 'paper must list first, got: ' + skins.join(',');
if (!['dark','white','light','paper'].every(k => skins.includes(k))) return 'skins are: ' + skins.join(',');
const ink = () => getComputedStyle(document.querySelector('#card-N20 .fh')).color;
@@ -1025,8 +1025,8 @@ try {
ok('N20 skins: paper default first, white ink, cream, ivory — all switch and restore', n20e === 'ok', n20e);
const n20fnt = await evl(`(() => {
- const h = document.querySelector('#card-N20').gw;
- if (!GW.splitFlap.STYLES.font) return 'no font axis';
+ const h = document.querySelector('#card-N20').dupre;
+ if (!DUPRE.splitFlap.STYLES.font) return 'no font axis';
const fam = () => getComputedStyle(document.querySelector('#card-N20 .fh')).fontFamily;
const helv = fam(); // the card's default face is Helvetica
if (!/helvetica/i.test(helv)) return 'default face is not helvetica: ' + helv;
@@ -1056,7 +1056,7 @@ try {
// Flap speed: the handle takes setFlapMs and the card's slider drives it.
const n20v = await evl(`(() => {
- const h = document.querySelector('#card-N20').gw;
+ const h = document.querySelector('#card-N20').dupre;
if (typeof h.setFlapMs !== 'function' || typeof h.flapMs !== 'function') return 'no speed api';
const before = h.flapMs();
if (before !== 100) return 'default flap rate is ' + before + ', want 100';
@@ -1075,7 +1075,7 @@ try {
// board must settle, hold the reading ~2s, and only then take the next update.
await evl(`(() => {
IH.flap = window.__realFlap;
- const h = document.querySelector('#card-N20').gw;
+ const h = document.querySelector('#card-N20').dupre;
window.__n20dwell = { settleAt: 0, changeAt: 0, settled: '' };
h.onSettle(r => { if (!window.__n20dwell.settleAt) { window.__n20dwell.settleAt = performance.now(); window.__n20dwell.settled = r; } });
window.__n20dwell.timer = setInterval(() => {
@@ -1114,9 +1114,9 @@ try {
await send('Emulation.setEmulatedMedia', { features: [{ name: 'prefers-reduced-motion', value: 'reduce' }] });
await send('Page.reload');
await sleep(2500);
- const rm1 = await evl(`document.querySelector('#card-N20').gw.reading()`);
+ const rm1 = await evl(`document.querySelector('#card-N20').dupre.reading()`);
await sleep(5200);
- const rm2 = await evl(`(() => ({ r: document.querySelector('#card-N20').gw.reading(), anims: document.getAnimations().length }))()`);
+ const rm2 = await evl(`(() => ({ r: document.querySelector('#card-N20').dupre.reading(), anims: document.getAnimations().length }))()`);
ok('N20 rests under reduced motion (no self-advancing dwell chain)',
rm1 === rm2.r && rm2.anims === 0, JSON.stringify({ before: rm1, after: rm2.r, anims: rm2.anims }));