mirror of
https://github.com/Kpa-clawbot/meshcore-analyzer.git
synced 2026-10-07 19:57:21 +00:00
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.
160 lines
7.0 KiB
JavaScript
160 lines
7.0 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* Coverage route reachability on mobile navigation.
|
|
*
|
|
* The "Coverage" route (#/rx-coverage) is opt-in: roles.js injects its link
|
|
* into the DESKTOP top-nav (.nav-links) only, when window.MC_CLIENT_RX_COVERAGE
|
|
* is true. The two mobile navigation surfaces build their long-tail route lists
|
|
* independently:
|
|
* - public/bottom-nav.js → the "More" sheet (phones, ≤768px)
|
|
* - public/nav-drawer.js → the edge-swipe drawer (tablets, >768px)
|
|
* Both used hardcoded arrays that omitted rx-coverage, so when Coverage was
|
|
* enabled it was present on desktop but UNREACHABLE on mobile. This test pins
|
|
* that, once enabled, Coverage appears in BOTH mobile surfaces, ordered right
|
|
* after Analytics (matching the desktop top-nav insertion point).
|
|
*
|
|
* Skips cleanly when:
|
|
* - Chromium is unavailable (unless CHROMIUM_REQUIRE=1 → hard fail), or
|
|
* - the deployment under test has clientRxCoverage disabled (default off).
|
|
*
|
|
* Stable selectors consumed here:
|
|
* [data-bottom-nav-tab="more"] — More tab (opens the sheet)
|
|
* [data-bottom-nav-more-route="rx-coverage"] — Coverage row in the sheet
|
|
* window.__navDrawer.open() — opens the edge-swipe drawer
|
|
* [data-nav-drawer-item="rx-coverage"] — Coverage row in the drawer
|
|
*/
|
|
'use strict';
|
|
|
|
const { chromium } = require('playwright');
|
|
|
|
const BASE = process.env.BASE_URL || 'http://localhost:3000';
|
|
|
|
let passed = 0, failed = 0;
|
|
async function step(name, fn) {
|
|
try { await 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'); }
|
|
|
|
(async () => {
|
|
const requireChromium = process.env.CHROMIUM_REQUIRE === '1';
|
|
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 (requireChromium) {
|
|
console.error(`test-rx-coverage-mobile-nav-e2e.js: FAIL — Chromium required but unavailable: ${err.message}`);
|
|
process.exit(1);
|
|
}
|
|
console.log(`test-rx-coverage-mobile-nav-e2e.js: SKIP (Chromium unavailable: ${err.message.split('\n')[0]})`);
|
|
process.exit(0);
|
|
}
|
|
|
|
console.log(`\n=== Coverage mobile-nav reachability E2E against ${BASE} ===`);
|
|
|
|
// Coverage is opt-in (config flag, default off). Skip when the deployment
|
|
// under test has it disabled — mirrors test-node-reach-coverage-e2e.js.
|
|
const probeCtx = await browser.newContext();
|
|
const probe = await probeCtx.newPage();
|
|
let enabled = false;
|
|
try {
|
|
const cfg = await (await probe.request.get(BASE + '/api/config/client')).json();
|
|
enabled = cfg.clientRxCoverage === true;
|
|
} catch (e) {
|
|
console.log(`test-rx-coverage-mobile-nav-e2e.js: SKIP (could not read /api/config/client: ${e.message})`);
|
|
await browser.close();
|
|
process.exit(0);
|
|
}
|
|
await probeCtx.close();
|
|
if (!enabled) {
|
|
console.log('test-rx-coverage-mobile-nav-e2e.js: SKIP (clientRxCoverage disabled on this deployment)');
|
|
await browser.close();
|
|
process.exit(0);
|
|
}
|
|
|
|
// ── Phone viewport: bottom-nav "More" sheet ──
|
|
const phoneCtx = await browser.newContext({ viewport: { width: 360, height: 800 } });
|
|
const phone = await phoneCtx.newPage();
|
|
phone.setDefaultTimeout(10000);
|
|
phone.on('pageerror', (e) => console.error('[pageerror]', e.message));
|
|
|
|
await phone.goto(BASE + '/#/packets', { waitUntil: 'domcontentloaded' });
|
|
await phone.waitForSelector('main#app', { timeout: 8000 });
|
|
// Wait until roles.js has resolved the config flag (the lazy sheet build
|
|
// reads window.MC_CLIENT_RX_COVERAGE at open time).
|
|
await phone.waitForFunction(() => window.MC_CLIENT_RX_COVERAGE === true, null, { timeout: 8000 });
|
|
|
|
await step('(a) bottom-nav More sheet contains Coverage, ordered after Analytics', async () => {
|
|
await phone.click('[data-bottom-nav-tab="more"]');
|
|
await phone.waitForTimeout(150);
|
|
const info = await phone.evaluate(() => {
|
|
const cov = document.querySelector('[data-bottom-nav-more-route="rx-coverage"]');
|
|
if (!cov) return { present: false };
|
|
const items = Array.prototype.map.call(
|
|
document.querySelectorAll('[data-bottom-nav-more-route]'),
|
|
(a) => a.getAttribute('data-bottom-nav-more-route')
|
|
);
|
|
const label = (cov.textContent || '').trim();
|
|
return {
|
|
present: true,
|
|
label,
|
|
afterAnalytics: items.indexOf('rx-coverage') === items.indexOf('analytics') + 1,
|
|
items,
|
|
};
|
|
});
|
|
assert(info.present, 'Coverage row missing from bottom-nav More sheet');
|
|
assert(/Coverage/.test(info.label), `expected label "Coverage", got "${info.label}"`);
|
|
assert(info.afterAnalytics, `Coverage not directly after Analytics: ${info.items.join(', ')}`);
|
|
});
|
|
|
|
await step('(b) tapping Coverage in the sheet navigates to #/rx-coverage', async () => {
|
|
await phone.click('[data-bottom-nav-more-route="rx-coverage"]');
|
|
await phone.waitForTimeout(200);
|
|
const hash = await phone.evaluate(() => location.hash);
|
|
assert(hash.indexOf('#/rx-coverage') === 0, `expected hash #/rx-coverage, got ${hash}`);
|
|
});
|
|
|
|
await phoneCtx.close();
|
|
|
|
// ── Tablet viewport: edge-swipe drawer (enabled > 768px) ──
|
|
const tabletCtx = await browser.newContext({ viewport: { width: 1024, height: 800 } });
|
|
const tablet = await tabletCtx.newPage();
|
|
tablet.setDefaultTimeout(10000);
|
|
tablet.on('pageerror', (e) => console.error('[pageerror]', e.message));
|
|
|
|
await tablet.goto(BASE + '/#/packets', { waitUntil: 'domcontentloaded' });
|
|
await tablet.waitForSelector('main#app', { timeout: 8000 });
|
|
await tablet.waitForFunction(() => window.MC_CLIENT_RX_COVERAGE === true, null, { timeout: 8000 });
|
|
|
|
await step('(c) edge-swipe drawer contains Coverage, ordered after Analytics', async () => {
|
|
await tablet.evaluate(() => window.__navDrawer && window.__navDrawer.open && window.__navDrawer.open());
|
|
await tablet.waitForTimeout(150);
|
|
const info = await tablet.evaluate(() => {
|
|
const cov = document.querySelector('[data-nav-drawer-item="rx-coverage"]');
|
|
if (!cov) return { present: false };
|
|
const items = Array.prototype.map.call(
|
|
document.querySelectorAll('[data-nav-drawer-item]'),
|
|
(a) => a.getAttribute('data-nav-drawer-item')
|
|
);
|
|
return {
|
|
present: true,
|
|
label: (cov.textContent || '').trim(),
|
|
afterAnalytics: items.indexOf('rx-coverage') === items.indexOf('analytics') + 1,
|
|
items,
|
|
};
|
|
});
|
|
assert(info.present, 'Coverage row missing from edge-swipe drawer');
|
|
assert(/Coverage/.test(info.label), `expected label "Coverage", got "${info.label}"`);
|
|
assert(info.afterAnalytics, `Coverage not directly after Analytics: ${info.items.join(', ')}`);
|
|
});
|
|
|
|
await tabletCtx.close();
|
|
await browser.close();
|
|
console.log(`\n=== Results: passed ${passed} failed ${failed} ===`);
|
|
process.exit(failed > 0 ? 1 : 0);
|
|
})().catch((e) => { console.error(e); process.exit(1); });
|