aboutsummaryrefslogtreecommitdiff
path: root/tests/gallery-probes
diff options
context:
space:
mode:
Diffstat (limited to 'tests/gallery-probes')
-rw-r--r--tests/gallery-probes/probe.mjs140
1 files changed, 140 insertions, 0 deletions
diff --git a/tests/gallery-probes/probe.mjs b/tests/gallery-probes/probe.mjs
index 17b3d73..d196377 100644
--- a/tests/gallery-probes/probe.mjs
+++ b/tests/gallery-probes/probe.mjs
@@ -100,6 +100,146 @@ try {
const t1 = await evl(`document.getElementById('rd-01').textContent`);
ok('toggle click responds at 3x', t0 !== t1, `'${t0}' -> '${t1}'`);
+ // 5. card 02 console keys: reading order and per-key tone. LIVE is green
+ // because --pass is what the kit means by live everywhere else; gold stays
+ // the generic engaged look. Craig's call, 2026-07-16.
+ const order = await evl(`[...document.querySelectorAll('#card-02 .key')].map(b => b.textContent)`);
+ ok('card 02 keys read SCAN, LIVE, MUTED',
+ JSON.stringify(order) === JSON.stringify(['SCAN', 'LIVE', 'MUTED']), JSON.stringify(order));
+
+ const engaged = await evl(`(()=>{
+ const lit = [...document.querySelectorAll('#card-02 .key')].filter(b => /\\b(on|green|red)\\b/.test(b.className));
+ return lit.length === 1 ? lit[0].textContent + ':' + lit[0].className.replace('key ','') : 'lit=' + lit.length;
+ })()`);
+ ok('card 02 engages LIVE in green by default', engaged === 'LIVE:green', engaged);
+
+ const muted = await evl(`(()=>{
+ const keys = [...document.querySelectorAll('#card-02 .key')];
+ keys.find(b => b.textContent === 'MUTED').click();
+ const lit = keys.filter(b => /\\b(on|green|red)\\b/.test(b.className));
+ return lit.length === 1 ? lit[0].textContent + ':' + lit[0].className.replace('key ','') : 'lit=' + lit.length;
+ })()`);
+ ok('card 02 MUTED engages red and releases LIVE', muted === 'MUTED:red', muted);
+
+ const rd02 = await evl(`document.getElementById('rd-02').textContent`);
+ ok('card 02 readout tracks the engaged key', rd02 === 'MUTED', rd02);
+
+ // 6. card 01 slide toggle: the new tone atoms exist. red/warn on the `on` axis
+ // let the ENGAGED state be the notable one (mute, record, airplane) — before
+ // these, red was only reachable via the `off` axis, which colours the
+ // 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 want = { on: ['amber','green','dark','red','warn'], onText: ['panel','cream','green'],
+ off: ['dark','red'], offText: ['white','red','black','dim'],
+ thumb: ['light','dark','chrome','brass'] };
+ const missing = [];
+ for (const [axis, names] of Object.entries(want))
+ for (const n of names) if (!S[axis] || !S[axis][n]) missing.push(axis + '.' + n);
+ return missing.length ? 'missing: ' + missing.join(', ') : 'ok';
+ })()`);
+ ok('slide toggle style atoms present', atoms === 'ok', atoms);
+
+ // 7. presets name intent (a combination), not paint. Every axis a preset names
+ // 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;
+ if (!P) return 'no PRESETS';
+ const AX = GW.slideToggle.AXIS_ORDER;
+ const bad = [];
+ for (const [name, p] of Object.entries(P)) {
+ for (const ax of AX) {
+ if (!p[ax]) bad.push(name + ' missing ' + ax);
+ else if (!S[ax][p[ax]]) bad.push(name + '.' + ax + '="' + p[ax] + '" not in STYLES');
+ }
+ }
+ return bad.length ? bad.join('; ') : Object.keys(P).join(',');
+ })()`);
+ ok('every preset names a resolvable style on every axis',
+ presets === 'panel,run,armed,caution,dark', presets);
+
+ // 7a. the dark preset: neither state lights the pill, so the legend colour is
+ // 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 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;
+ if (onInk === offInk) return 'inks identical, state unreadable: ' + onInk;
+ return 'ok';
+ })()`);
+ 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
+ // `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
+ // only toggle its switch, which does not restyle), so the default still
+ // stands here — but this must stay ABOVE checks 8/8b/9, which do mutate
+ // the chips. Insert preset-touching checks after them, not before.
+ const defaultPreset = await evl(`(()=>{
+ const pg = [...document.querySelectorAll('#card-01 .fgroup')]
+ .find(g => g.querySelector('.lab')?.textContent === 'preset');
+ if (!pg) return 'no preset group';
+ const lit = [...pg.querySelectorAll('.fc')].filter(c => c.classList.contains('on'));
+ return lit.length === 1 ? lit[0].title : 'lit=' + lit.length;
+ })()`);
+ 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.
+ const applied = await evl(`(()=>{
+ const card = document.getElementById('card-01');
+ const groups = [...card.querySelectorAll('.fgroup')];
+ const pg = groups.find(g => g.querySelector('.lab')?.textContent === 'preset');
+ if (!pg) return 'no preset group';
+ const armed = [...pg.querySelectorAll('.fc')].find(c => c.title === 'armed');
+ if (!armed) return 'no armed chip';
+ armed.click();
+ const onGroup = groups.find(g => g.querySelector('.lab')?.textContent === 'on');
+ const lit = [...onGroup.querySelectorAll('.fc')].filter(c => c.classList.contains('on'));
+ 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);
+
+ // 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
+ // chip still shows lit. Drive it in the hazardous order and check the ink.
+ const orderSafe = await evl(`(()=>{
+ const card = document.getElementById('card-01');
+ const groups = [...card.querySelectorAll('.fgroup')];
+ const grp = l => groups.find(g => g.querySelector('.lab')?.textContent === l);
+ const chip = (l, t) => [...grp(l).querySelectorAll('.fc')].find(c => c.title === t);
+ chip('on text', 'green').click(); // legend green
+ chip('on', 'dark').click(); // then change the pill — must keep it
+ const ink = card.querySelector('.switch').style.getPropertyValue('--sw-on-ink');
+ const lit = [...grp('on text').querySelectorAll('.fc')].filter(c => c.classList.contains('on'));
+ return ink + '|' + (lit.length === 1 ? lit[0].title : 'lit=' + lit.length);
+ })()`);
+ ok('changing on keeps the chosen onText (chips cannot lie)',
+ orderSafe === 'var(--sevgrn)|green', orderSafe);
+
+ // 9. diverging on one axis clears the preset — the card stops claiming a preset
+ // it is no longer in. Re-establishes its own precondition (preset lit) rather
+ // than inheriting it: the checks above already clicked axis chips, each of
+ // which clears the preset, so asserting lit===0 without re-lighting first
+ // would pass against an already-cleared group and prove nothing — including
+ // against a regression where clearing fired for onText but not for `on`.
+ const diverged = await evl(`(()=>{
+ const groups = [...document.querySelectorAll('#card-01 .fgroup')];
+ const grp = l => groups.find(g => g.querySelector('.lab')?.textContent === l);
+ const litPresets = () => [...grp('preset').querySelectorAll('.fc')].filter(c => c.classList.contains('on')).length;
+ [...grp('preset').querySelectorAll('.fc')].find(c => c.title === 'run').click();
+ const before = litPresets();
+ [...grp('on').querySelectorAll('.fc')].find(c => c.title === 'green').click();
+ return before + '->' + litPresets();
+ })()`);
+ ok('changing an axis clears the preset selection', diverged === '1->0', diverged);
+
// late exceptions from interactions
const errs2 = events.filter(e => e.method === 'Runtime.exceptionThrown');
ok('no exceptions after interaction', errs2.length === 0);