Files
meshcore-analyzer/tests/e2e/test-nav-priority-1311-e2e.js
Alex B 893773338e chore(tests): move root test-*.js into tests/unit and tests/e2e (#2036)
Moves 290 root test-*.js into tests/unit (177, listed in test-all.sh) and tests/e2e (113, classified in scripts/non-unit-tests.json), per #1981 and PR-D of #1385. Root goes from 348 entries to 48. test-all.sh and test-fixtures/ stay put. The inventory guard now fails if a test reappears in the root or sits in the wrong folder.

Verified independently of the diff: the invoked sets are unchanged (test-all.sh 177 before and after, deploy.yml 96 before and after, both identical as sets), and a full local run of test-all.sh on master and on the branch produced 4702 output lines each whose only differences are absolute paths, stack-trace line numbers shifted by the REPO_ROOT line, the inventory wording and two perf ratios. The guard was mutation-checked: a test back in the root, a unit suite in tests/e2e, and a suite dropped from test-all.sh each make it exit 1. CI run 35246304316 ran 97 suites from tests/e2e and is green.

Follow-up 9335c51d finished the instruction files: no bare root test command is left in AGENTS.md, the squad charters, .github or docs, and every tests/ path they name resolves.

Merged by the interim maintainer without a second human reviewer: CI and the local runs above are the independent checks.

Known and deliberately out of scope: 18 of the 113 files in tests/e2e are invoked by no runner at all, and one of them cannot run anywhere because it requires jsdom, which is not a declared dependency. Tracked separately.
2026-09-17 19:06:07 +02:00

137 lines
5.7 KiB
JavaScript

#!/usr/bin/env node
/* Issue #1311 — Active non-high-priority route causes ALL high-priority
* links to overflow into the More menu at narrow-desktop widths.
*
* Bug: applyNavPriority's iterative overflow loop in public/app.js had no
* floor — when fits() kept returning false (because the active-route pill
* is wider than other links), it walked off the end of the non-high tail
* and started dropping high-priority links too. On /#/perf at ~900-1100px
* in Firefox/Edge Windows, this nuked ALL 5 high-priority links from the
* inline strip, leaving only "More ▾" + the active pill visible.
*
* Acceptance:
* - At 900px / 1024px / 1100px width on /#/perf, /#/audio-lab,
* /#/analytics, /#/observers (active route is non-high-priority):
* ALL 5 high-priority links (#/home, #/packets, #/map, #/live, #/nodes)
* are visible inline (.is-overflow not present, getBoundingClientRect
* width > 0). The More menu may contain low-priority links — that's
* fine — but never high-priority links.
*
* Mutation guard: removing the `priority === 'high'` floor in the loop
* makes this test fail (loop walks into the high-priority tail).
*/
'use strict';
const assert = require('node:assert');
const { chromium } = require('playwright');
const BASE = process.env.BASE_URL || 'http://localhost:13581';
const HIGH_PRIORITY_HREFS = ['#/home', '#/packets', '#/map', '#/live', '#/nodes'];
// Active routes that are NOT high-priority. The bug surfaced when the
// active-route pill (which has wider padding/background) is one of these.
const NON_HIGH_ROUTES = ['#/perf', '#/audio-lab', '#/analytics', '#/observers'];
// Widths in the narrow-desktop band where the bug reproduces. The 768-1100
// branch in applyNavPriority is data-priority-only (already correct), so
// the regression actually surfaces just above 1100 where the iterative
// loop runs. We include 1101/1200 to cover that band, and 900/1024 for
// belt-and-braces (the CSS branch).
const WIDTHS = [900, 1024, 1101, 1200];
const HEIGHT = 900;
async function main() {
let browser;
try {
browser = await chromium.launch({
headless: true,
executablePath: process.env.CHROMIUM_PATH || undefined,
args: ['--no-sandbox', '--disable-gpu', '--disable-dev-shm-usage'],
});
} catch (err) {
if (process.env.CHROMIUM_REQUIRE === '1') {
console.error(`test-nav-priority-1311-e2e.js: FAIL — Chromium required but unavailable: ${err.message}`);
process.exit(1);
}
console.log(`test-nav-priority-1311-e2e.js: SKIP (Chromium unavailable: ${err.message.split('\n')[0]})`);
process.exit(0);
}
let failures = 0;
let passes = 0;
const ctx = await browser.newContext();
const page = await ctx.newPage();
page.setDefaultTimeout(15000);
for (const w of WIDTHS) {
for (const route of NON_HIGH_ROUTES) {
await page.setViewportSize({ width: w, height: HEIGHT });
await page.goto(`${BASE}/${route}`, { waitUntil: 'domcontentloaded' });
await page.waitForSelector('.top-nav .nav-links');
await page.evaluate(() => document.fonts && document.fonts.ready ? document.fonts.ready : null);
// Settle layout (two consecutive frames identical for nav-right).
await page.waitForFunction(() => {
const el = document.querySelector('.top-nav .nav-right');
if (!el) return false;
const r1 = el.getBoundingClientRect();
return new Promise((resolve) => {
requestAnimationFrame(() => requestAnimationFrame(() => {
const r2 = el.getBoundingClientRect();
resolve(r1.right === r2.right && r1.left === r2.left);
}));
});
}, null, { timeout: 5000 });
// Give applyNavPriority (wired to hashchange + rAF resize) a frame.
await page.evaluate(() => new Promise(r => requestAnimationFrame(() => requestAnimationFrame(r))));
const data = await page.evaluate((HIGH) => {
const links = Array.from(document.querySelectorAll('.nav-links .nav-link'));
const overflowedHighPri = [];
const visibleHighPri = [];
for (const a of links) {
const href = a.getAttribute('href');
if (!HIGH.includes(href)) continue;
const isOverflow = a.classList.contains('is-overflow');
const width = a.getBoundingClientRect().width;
if (isOverflow || width === 0) {
overflowedHighPri.push({ href, isOverflow, width });
} else {
visibleHighPri.push(href);
}
}
return { overflowedHighPri, visibleHighPri };
}, HIGH_PRIORITY_HREFS);
const tag = `${w}px @ ${route}`;
// Hard assertion (preflight gate requires `assert` call): every
// high-priority link href is in the visible set, never overflowed.
try {
assert.deepStrictEqual(
[...data.visibleHighPri].sort(),
[...HIGH_PRIORITY_HREFS].sort(),
`${tag}: expected all 5 high-priority links inline, got [${data.visibleHighPri.join(', ')}]`
);
passes++;
console.log(` ✅ ${tag}: 5/5 high-pri visible inline`);
} catch (e) {
failures++;
const detail = data.overflowedHighPri
.map(o => `${o.href}(overflow=${o.isOverflow},w=${o.width})`)
.join(', ');
console.log(` ❌ ${tag}: high-pri overflowed/hidden: ${detail || '(none)'} ` +
`| visible=[${data.visibleHighPri.join(', ')}]`);
}
}
}
await browser.close();
const total = WIDTHS.length * NON_HIGH_ROUTES.length;
console.log(`\ntest-nav-priority-1311-e2e.js: ${failures === 0 ? 'OK' : 'FAIL'} — ${passes}/${total} passed`);
process.exit(failures === 0 ? 0 : 1);
}
main().catch((err) => {
console.error('test-nav-priority-1311-e2e.js: fatal', err);
process.exit(1);
});