mirror of
https://github.com/Kpa-clawbot/meshcore-analyzer.git
synced 2026-06-13 12:12:48 +00:00
## Summary Fixes the ~5% flake in `test-slideover-1056-e2e.js` packets@800 subtest caused by a virtual-scroll spacer race. ## Root cause (per issue) The packets `PAGES` entry used a loose selector with a bare-`tr` fallback (`#pktTable tbody tr[data-id], #pktTable tbody tr`). That fallback matches the virtual-scroll spacer `<tr>` (no `data-*`, no click handler). The row-wait guard counted any `<tr>`, so it was satisfied by the spacer alone — the test then clicked the spacer and no slide-over opened. ## Fix (test-only) 1. `test-slideover-1056-e2e.js` L42 — drop bare-`tr` fallback; use `#pktTable tbody tr[data-id]` only. 2. `test-slideover-1056-e2e.js` L69-71 — `waitForFunction` now queries the page-specific `rowSel` directly (`querySelector(rowSel) !== null`) instead of counting any `<tr>`. Works for all three pages (packets / nodes / observers) because each already uses an attribute-strict `rowSel`. ## TDD - Red commit `d02e496b`: new `test-slideover-1056-rowsel-strict.js` pins the discipline — fails when the bare-`tr` fallback or loose `tbody tr` count is present. - Green commit `a8b445d5`: applies the selector + guard tightening; pin test passes. ## Verification - `node test-slideover-1056-rowsel-strict.js` → 2/2 pass on the fix commit; 0/2 on parent. - `node -c test-slideover-1056-e2e.js` → syntax OK. - Preflight (`bash ~/.openclaw/skills/pr-preflight/scripts/run-all.sh origin/master`) → all gates green. Scope is strictly test-only — no production code touched. Fixes #1662. --------- Co-authored-by: clawbot <bot@corescope> Co-authored-by: clawbot <bot@local> Co-authored-by: clawbot <clawbot@users.noreply.github.com>
This commit is contained in:
@@ -39,7 +39,7 @@ step.skip = function (name, reason, fn) {
|
||||
function assert(c, m) { if (!c) throw new Error(m || 'assertion failed'); }
|
||||
|
||||
const PAGES = [
|
||||
{ hash: '#/packets', tableSel: '#pktTable', rowSel: '#pktTable tbody tr[data-id], #pktTable tbody tr', name: 'packets' },
|
||||
{ hash: '#/packets', tableSel: '#pktTable', rowSel: '#pktTable tbody tr[data-hash]', name: 'packets' },
|
||||
{ hash: '#/nodes', tableSel: '#nodesTable', rowSel: '#nodesTable tbody tr[data-value]', name: 'nodes' },
|
||||
{ hash: '#/observers', tableSel: '#obsTable', rowSel: '#obsTable tbody tr[data-action="navigate"]', name: 'observers' },
|
||||
];
|
||||
@@ -65,11 +65,12 @@ const PAGES = [
|
||||
await step(`${tag}: page renders + first row exists`, async () => {
|
||||
await page.goto(BASE + '/' + p.hash, { waitUntil: 'domcontentloaded' });
|
||||
await page.waitForSelector(p.tableSel, { timeout: 8000 });
|
||||
// wait for at least one tbody row
|
||||
await page.waitForFunction((sel) => {
|
||||
const t = document.querySelector(sel);
|
||||
return t && t.querySelectorAll('tbody tr').length > 0;
|
||||
}, p.tableSel, { timeout: 8000 });
|
||||
// Wait for at least one real data row (per the page-specific rowSel).
|
||||
// Using p.rowSel here — not a bare `tbody tr` — avoids the
|
||||
// virtual-scroll spacer race on the packets page (#1662).
|
||||
await page.waitForFunction((rowSel) => {
|
||||
return document.querySelector(rowSel) !== null;
|
||||
}, p.rowSel, { timeout: 8000 });
|
||||
});
|
||||
|
||||
await step(`${tag}: clicking row opens slide-over with backdrop`, async () => {
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* Pin test for #1662: the packets PAGES entry in test-slideover-1056-e2e.js
|
||||
* must use a STRICT row selector (data-id only) — never a bare
|
||||
* `tbody tr` fallback that would match the virtual-scroll spacer row.
|
||||
*
|
||||
* Also pin the waitForFunction guard: it must require a row matching the
|
||||
* page-specific attribute, not just any <tr>.
|
||||
*
|
||||
* This is a discipline test — it prevents the loose fallback from sneaking
|
||||
* back in and re-introducing the 5% flake described in #1662.
|
||||
*/
|
||||
'use strict';
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const SRC = path.join(__dirname, 'test-slideover-1056-e2e.js');
|
||||
const src = fs.readFileSync(SRC, 'utf8');
|
||||
|
||||
let passed = 0, failed = 0;
|
||||
function check(name, fn) {
|
||||
try { fn(); passed++; console.log(' ✓ ' + name); }
|
||||
catch (e) { failed++; console.error(' ✗ ' + name + ': ' + e.message); }
|
||||
}
|
||||
function assert(c, m) { if (!c) throw new Error(m || 'assertion failed'); }
|
||||
|
||||
console.log('\n=== #1662 strict row-selector pin ===');
|
||||
|
||||
check('packets PAGES entry uses strict attribute-based row selector (no bare fallback)', () => {
|
||||
// Find the packets entry line.
|
||||
const m = src.match(/{\s*hash:\s*'#\/packets'[^}]*}/);
|
||||
assert(m, 'could not find packets PAGES entry');
|
||||
const entry = m[0];
|
||||
// Must include a strict attribute-based selector under #pktTable tbody —
|
||||
// e.g. tr[data-hash] or tr[data-id]. The specific attribute may evolve;
|
||||
// what matters is that it's an attribute selector, not a bare `tr`.
|
||||
assert(/rowSel:\s*'#pktTable tbody tr\[data-[a-z-]+\]'/.test(entry),
|
||||
'packets rowSel is not a strict `#pktTable tbody tr[data-...]` selector; got: ' + entry);
|
||||
// Must NOT include any bare `tbody tr` fallback (no attribute).
|
||||
assert(!/#pktTable tbody tr\s*['",]/.test(entry),
|
||||
'packets rowSel still has a bare-`tr` fallback (no attribute selector)');
|
||||
});
|
||||
|
||||
check('waitForFunction guard requires a row matching the page-specific rowSel', () => {
|
||||
// The guard must reference `rowSel` (passed-through) or at minimum query
|
||||
// with an attribute selector — NOT a bare `tbody tr` count.
|
||||
// Acceptable shape: querySelector(rowSel) !== null
|
||||
// Or: querySelector('tbody tr[data-...]')
|
||||
const loose = /querySelectorAll\(\s*['"]tbody tr['"]\s*\)\.length\s*>\s*0/;
|
||||
assert(!loose.test(src),
|
||||
'waitForFunction still uses loose `querySelectorAll("tbody tr").length > 0` — ' +
|
||||
'will be satisfied by virtual-scroll spacer alone');
|
||||
});
|
||||
|
||||
console.log(`\nResults: ${passed} passed, ${failed} failed\n`);
|
||||
process.exit(failed === 0 ? 0 : 1);
|
||||
Reference in New Issue
Block a user