mirror of
https://github.com/Kpa-clawbot/meshcore-analyzer.git
synced 2026-07-01 03:21:39 +00:00
Partial fix for #1402 ## Summary Re-fix two of the four #1402 regressions on mobile after `#1452` silently reverted the prior fix (`6ec08acb`). Two predicate flips in `public/gesture-hints.js` + extended E2E coverage to prevent another silent revert. This PR is intentionally **scoped to Bug 2 and Bug 4 only**. Bug 1 and Bug 3 were also dropped by `#1452` and are NOT restored here — `#1402` remains open for the rest. ## Changes - `public/gesture-hints.js` (edge-drawer): `window.innerWidth > 768` → `window.innerWidth <= 768`. The edge-swipe drawer is the MOBILE layout's nav per #1064/#1184; `nav-drawer.js` `NARROW_MAX=768` (inclusive — narrow when width <= NARROW_MAX). Above 768 the sidebar is persistent, no edge-swipe is needed. - `public/gesture-hints.js` (row-swipe): widen route filter from `/^#\/(packets|nodes)/` to `/^#\/(packets|nodes|channels|observers)/`. Channels and observers also render swipable row tables. - `public/gesture-hints.js`: expose read-only `window.__gestureHintsDefs` test hook (frozen) for direct predicate probes (avoids race with render path). - `test-gesture-hints-1065-e2e.js`: add assertions (i)+(j) at vw=393 — edge-drawer relevant on `/#/home`, row-swipe relevant on `/#/channels`; (k) negative-direction gate at vw=1024 asserts `edge-drawer.relevant() === false` on desktop. Retarget (e) from 1024x800 → 393x800 to match the corrected mobile-only gate. ## TDD - Red commit: `1e7545d1` — test additions fail against current production code (edge-drawer relevant returns false at vw=393, row-swipe filter rejects /channels). - Green commit: `6f844d5b` — predicate flips + route widening make both assertions pass. - Polish commit (round-1 fixes): boundary <= 768, doc-header refresh, freeze the test hook, negative-direction gate (k), precondition assertion on (i). ## Acceptance criteria from #1402 - [ ] Bug 1 (`window 'load'` rescheduler + `pointer: coarse` gate) — dropped by #1452, NOT restored in this PR. Tracked in #1402. - [x] Bug 2 (edge-drawer mobile-only) — fixed here. - [ ] Bug 3 (pull-refresh touch-gate decoupling) — dropped by #1452, NOT restored in this PR. Tracked in #1402. - [x] Bug 4 (row-swipe widening → /channels + /observers) — fixed here. - [x] E2E mutation gate: assertions (i)+(j)+(k) provably fail if either predicate is reverted or re-broadened. ## Notes - Silently reverted by #1452 — re-fix here, with regression gates so the next reviewer of the next refactor will see the assertions fail rather than the production behavior change unnoticed. ## Preflight All gates pass (PII, branch scope, red commit, CSS vars, XSS sinks, etc.). --------- Co-authored-by: meshcore-bot <bot@meshcore.local> Co-authored-by: fix-1166-bot <bot@corescope.local>
This commit is contained in:
+19
-5
@@ -9,7 +9,8 @@
|
||||
* - prefers-reduced-motion: animation-name: none (style.css handles via media query).
|
||||
* - Singleton + cleanup: module-scoped guard; SPA re-mount must not re-show dismissed.
|
||||
* - Pull-to-refresh hint only when .pull-to-reconnect element exists in DOM.
|
||||
* - Edge-drawer hint only at viewport > 768px (where edge-swipe drawer applies).
|
||||
* - Edge-drawer hint only at viewport <= 768px (mobile layout, where the
|
||||
* edge-swipe drawer is the nav UI; nav-drawer.js NARROW_MAX=768, inclusive).
|
||||
* - Row-swipe hint only on table pages: /#/packets, /#/nodes, etc.
|
||||
*/
|
||||
(function () {
|
||||
@@ -51,7 +52,7 @@
|
||||
if (!hasTouchCapability()) return false;
|
||||
if (onLiveRoute()) return false; // #1244
|
||||
var h = location.hash || '';
|
||||
return /^#\/(packets|nodes)/.test(h);
|
||||
return /^#\/(packets|nodes|channels|observers)/.test(h);
|
||||
},
|
||||
position: 'bottom',
|
||||
},
|
||||
@@ -71,9 +72,12 @@
|
||||
relevant: function () {
|
||||
if (!hasTouchCapability()) return false;
|
||||
if (onLiveRoute()) return false; // #1244
|
||||
// nav-drawer.js: NARROW_MAX=768; edge-swipe drawer is the WIDE
|
||||
// (>768) layout's nav UI. Below 768, the bottom-nav owns navigation.
|
||||
return window.innerWidth > 768 && !!document.querySelector('.nav-drawer, [data-nav-drawer]');
|
||||
// nav-drawer.js: NARROW_MAX=768; "narrow" is inclusive — narrow when
|
||||
// width <= NARROW_MAX (nav-drawer treats width > NARROW_MAX as the
|
||||
// non-narrow / sidebar layout). The edge-swipe drawer is the MOBILE
|
||||
// (≤768) layout's nav UI per #1064/#1184. Above 768, the persistent
|
||||
// sidebar is visible and no edge-swipe is needed.
|
||||
return window.innerWidth <= 768 && !!document.querySelector('.nav-drawer, [data-nav-drawer]');
|
||||
},
|
||||
position: 'top-left',
|
||||
},
|
||||
@@ -220,6 +224,16 @@
|
||||
init();
|
||||
}
|
||||
|
||||
// #1402 test hook: expose hint definitions for E2E predicate probes.
|
||||
// Read-only by convention; tests call .relevant() to verify routing/viewport gates.
|
||||
window.__gestureHintsDefs = HINTS;
|
||||
// M3: freeze the hint defs to prevent tests / page scripts from mutating
|
||||
// production state via the test hook. Shallow-freeze HINTS + each def.
|
||||
try {
|
||||
Object.keys(HINTS).forEach(function (id) { Object.freeze(HINTS[id]); });
|
||||
Object.freeze(HINTS);
|
||||
} catch (_e) {}
|
||||
|
||||
window.GestureHints = {
|
||||
show: show,
|
||||
dismiss: dismiss,
|
||||
|
||||
@@ -208,11 +208,11 @@ async function main() {
|
||||
|
||||
await ctx.close();
|
||||
|
||||
// ── (e) at 1024x800 with touch, edge-swipe hint visible on first visit ──
|
||||
// #1065 follow-up: edge-swipe is a touch gesture; the hint must only
|
||||
// appear when the viewport reports touch capability. Test context must
|
||||
// pass hasTouch:true (real edge-swipe-on-tablet/touch-laptop scenario).
|
||||
const ctx2 = await browser.newContext({ viewport: { width: 1024, height: 800 }, hasTouch: true });
|
||||
// ── (e) at vw=393 with touch, edge-swipe hint visible on first visit ──
|
||||
// #1065 follow-up: edge-swipe is a touch gesture. #1402 fix: edge-drawer
|
||||
// is the MOBILE layout's nav UI (per #1064/#1184, nav-drawer.js NARROW_MAX=768);
|
||||
// hint must appear at narrow viewports, not wide ones.
|
||||
const ctx2 = await browser.newContext({ viewport: { width: 393, height: 800 }, hasTouch: true });
|
||||
const page2 = await ctx2.newPage();
|
||||
await page2.goto(`${BASE}/#/packets`, { waitUntil: 'domcontentloaded' });
|
||||
await page2.evaluate((keys) => Object.values(keys).forEach((k) => localStorage.removeItem(k)), KEYS);
|
||||
@@ -220,9 +220,9 @@ async function main() {
|
||||
await page2.waitForTimeout(HINT_SETTLE_MS);
|
||||
const edgeHint = await hintVisible(page2, 'edge-drawer');
|
||||
if (edgeHint.present && edgeHint.visible) {
|
||||
pass('(e) edge-drawer hint visible at 1024x800');
|
||||
pass('(e) edge-drawer hint visible at 393x800 (mobile)');
|
||||
} else {
|
||||
fail(`(e) edge-drawer hint NOT visible at 1024x800 — state=${JSON.stringify(edgeHint)}`);
|
||||
fail(`(e) edge-drawer hint NOT visible at 393x800 — state=${JSON.stringify(edgeHint)}`);
|
||||
}
|
||||
await ctx2.close();
|
||||
|
||||
@@ -245,6 +245,81 @@ async function main() {
|
||||
}
|
||||
await ctx3.close();
|
||||
|
||||
// ── (i) #1402 regression — at vw=393 (mobile), edge-drawer hint IS relevant on /#/home ──
|
||||
// Bug 2 in #1402: edge-drawer.relevant had window.innerWidth > 768 (inverted).
|
||||
// nav-drawer.js NARROW_MAX=768; the edge-swipe drawer is the MOBILE feature
|
||||
// per #1064/#1184. At vw=393 with a .nav-drawer in the DOM, the hint MUST
|
||||
// be classified as relevant. Asserts the predicate directly so the test
|
||||
// does not depend on the schedule/render path.
|
||||
const ctx4 = await browser.newContext({ viewport: { width: 393, height: 800 }, hasTouch: true });
|
||||
const page4 = await ctx4.newPage();
|
||||
await page4.goto(`${BASE}/#/home`, { waitUntil: 'domcontentloaded' });
|
||||
await page4.evaluate((keys) => Object.values(keys).forEach((k) => localStorage.removeItem(k)), KEYS);
|
||||
await page4.reload({ waitUntil: 'domcontentloaded' });
|
||||
await page4.waitForTimeout(HINT_SETTLE_MS);
|
||||
const probe1402 = await page4.evaluate(() => {
|
||||
const hints = window.__gestureHintsDefs || null;
|
||||
return {
|
||||
hintsExposed: !!hints,
|
||||
vw: window.innerWidth,
|
||||
navDrawerInDom: !!document.querySelector('.nav-drawer, [data-nav-drawer]'),
|
||||
edgeDrawerRelevant: hints && hints['edge-drawer'] ? !!hints['edge-drawer'].relevant() : null,
|
||||
};
|
||||
});
|
||||
if (probe1402.hintsExposed && probe1402.navDrawerInDom && probe1402.edgeDrawerRelevant === true) {
|
||||
pass(`(i) #1402 — edge-drawer relevant at vw=393 on /#/home (navDrawer=${probe1402.navDrawerInDom})`);
|
||||
} else if (!probe1402.navDrawerInDom) {
|
||||
fail(`(i) #1402 — precondition failed: .nav-drawer NOT in DOM at vw=393 — state=${JSON.stringify(probe1402)}`);
|
||||
} else {
|
||||
fail(`(i) #1402 — edge-drawer NOT relevant at vw=${probe1402.vw} — state=${JSON.stringify(probe1402)}`);
|
||||
}
|
||||
|
||||
// ── (j) #1402 regression — at vw=393, row-swipe hint IS relevant on /#/channels ──
|
||||
// Bug 4 in #1402: row-swipe filter was /^#\/(packets|nodes)/ — must widen to
|
||||
// include channels and observers (both render swipable row tables).
|
||||
await page4.goto(`${BASE}/#/channels`, { waitUntil: 'domcontentloaded' });
|
||||
await page4.waitForTimeout(HINT_SETTLE_MS);
|
||||
const probe1402b = await page4.evaluate(() => {
|
||||
const hints = window.__gestureHintsDefs || null;
|
||||
return {
|
||||
hintsExposed: !!hints,
|
||||
hash: location.hash,
|
||||
rowSwipeRelevant: hints && hints['row-swipe'] ? !!hints['row-swipe'].relevant() : null,
|
||||
};
|
||||
});
|
||||
if (probe1402b.hintsExposed && probe1402b.rowSwipeRelevant === true) {
|
||||
pass(`(j) #1402 — row-swipe relevant at vw=393 on ${probe1402b.hash}`);
|
||||
} else {
|
||||
fail(`(j) #1402 — row-swipe NOT relevant on /#/channels — state=${JSON.stringify(probe1402b)}`);
|
||||
}
|
||||
await ctx4.close();
|
||||
|
||||
// ── (k) #1402 negative-direction regression gate — at vw=1024 (desktop),
|
||||
// edge-drawer.relevant() MUST return false. This locks the predicate so
|
||||
// it cannot be re-broadened to fire on desktop (the original #1402 Bug 2
|
||||
// had the inequality inverted; this assertion guards against the reverse
|
||||
// mistake — over-broadening — going forward).
|
||||
const ctx5 = await browser.newContext({ viewport: { width: 1024, height: 800 }, hasTouch: true });
|
||||
const page5 = await ctx5.newPage();
|
||||
await page5.goto(`${BASE}/#/home`, { waitUntil: 'domcontentloaded' });
|
||||
await page5.evaluate((keys) => Object.values(keys).forEach((k) => localStorage.removeItem(k)), KEYS);
|
||||
await page5.reload({ waitUntil: 'domcontentloaded' });
|
||||
await page5.waitForTimeout(HINT_SETTLE_MS);
|
||||
const probe1402c = await page5.evaluate(() => {
|
||||
const hints = window.__gestureHintsDefs || null;
|
||||
return {
|
||||
hintsExposed: !!hints,
|
||||
vw: window.innerWidth,
|
||||
edgeDrawerRelevant: hints && hints['edge-drawer'] ? !!hints['edge-drawer'].relevant() : null,
|
||||
};
|
||||
});
|
||||
if (probe1402c.hintsExposed && probe1402c.edgeDrawerRelevant === false) {
|
||||
pass(`(k) #1402 negative gate — edge-drawer NOT relevant at vw=${probe1402c.vw} (desktop)`);
|
||||
} else {
|
||||
fail(`(k) #1402 negative gate FAILED — edge-drawer relevant at desktop width — state=${JSON.stringify(probe1402c)}`);
|
||||
}
|
||||
await ctx5.close();
|
||||
|
||||
await browser.close();
|
||||
console.log(`\ntest-gesture-hints-1065-e2e.js: ${passes} passed, ${failures} failed`);
|
||||
process.exit(failures > 0 ? 1 : 0);
|
||||
|
||||
Reference in New Issue
Block a user