mirror of
https://github.com/Kpa-clawbot/meshcore-analyzer.git
synced 2026-10-15 11:11:12 +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.
140 lines
7.1 KiB
JavaScript
140 lines
7.1 KiB
JavaScript
/**
|
|
* #1087 — Channel modal QR/share regression tests.
|
|
*
|
|
* Pure source-string + targeted DOM-string assertions covering all 4 bugs:
|
|
*
|
|
* 1. QR generator must use the vendored Kazuhiko Arase `qrcode()` API
|
|
* (lowercase). Old code checked `root.QRCode` which never existed,
|
|
* causing "[QR library not loaded]" on every Generate click.
|
|
* 2. The Share button must use the user's display label (not the
|
|
* internal `psk:<hex8>` lookup key) when building the QR/URL.
|
|
* 3. PSK channel persistence: the Add/Generate handlers must route
|
|
* writes through a single dedicated helper (`persistAddedChannel`)
|
|
* so storage happens synchronously inside the submit path — not as
|
|
* a side effect of subsequent UI events. The helper must also
|
|
* verify localStorage actually contains the key after the write.
|
|
* 4. The Share affordance must open a DEDICATED modal element
|
|
* (`chShareModal`) — not reuse the Add Channel modal
|
|
* (`chAddChannelModal`).
|
|
*
|
|
* Companion E2E coverage: test-channel-issue-1087-e2e.js
|
|
*/
|
|
'use strict';
|
|
const REPO_ROOT = require('path').resolve(__dirname, '..', '..');
|
|
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
let passed = 0;
|
|
let failed = 0;
|
|
function assert(cond, msg) {
|
|
if (cond) { passed++; console.log(' ✓ ' + msg); }
|
|
else { failed++; console.error(' ✗ ' + msg); }
|
|
}
|
|
|
|
const chSrc = fs.readFileSync(path.join(REPO_ROOT, 'public/channels.js'), 'utf8');
|
|
const qrSrc = fs.readFileSync(path.join(REPO_ROOT, 'public/channel-qr.js'), 'utf8');
|
|
const decSrc = fs.readFileSync(path.join(REPO_ROOT, 'public/channel-decrypt.js'), 'utf8');
|
|
const idxSrc = fs.readFileSync(path.join(REPO_ROOT, 'public/index.html'), 'utf8');
|
|
|
|
console.log('\n=== #1087 Bug 1: QR vendor library is wired correctly ===');
|
|
|
|
// The vendored library is Kazuhiko Arase's qrcode-generator (lowercase
|
|
// `qrcode` global). The generate() helper must call into that API —
|
|
// either via `root.qrcode(...)` / `window.qrcode(...)` / a direct
|
|
// `qrcode(` call producing an image with `createImgTag` /
|
|
// `createSvgTag` / `createDataURL`.
|
|
assert(/\bqrcode\s*\(\s*\d/.test(qrSrc) ||
|
|
/createImgTag|createSvgTag|createDataURL/.test(qrSrc),
|
|
'channel-qr.js generate() uses the vendored qrcode-generator API');
|
|
|
|
// The "[QR library not loaded]" fallback string must NOT be the only
|
|
// detection branch for the generator — the new code must support the
|
|
// lowercase qrcode global. We accept either (a) the old check is gone
|
|
// or (b) the new check is added alongside.
|
|
assert(/typeof\s+(root|window)\.qrcode\s*===\s*['"]function['"]/.test(qrSrc) ||
|
|
/typeof\s+qrcode\s*===\s*['"]function['"]/.test(qrSrc),
|
|
'channel-qr.js detects the lowercase `qrcode` global (not just `QRCode`)');
|
|
|
|
console.log('\n=== #1087 Bug 2: Share QR uses the user display label ===');
|
|
|
|
// The share-channel click handler must resolve a display label
|
|
// (via ChannelDecrypt.getLabel / .getLabels / userLabel lookup) and
|
|
// pass that human-readable name to ChannelQR.generate — NOT the raw
|
|
// `psk:<hex8>` key prefix.
|
|
var shareIdx = chSrc.indexOf("data-share-channel");
|
|
assert(shareIdx > 0, 'found share button DOM marker');
|
|
|
|
// Find a window of source covering the share button click handler.
|
|
var shareHandlerIdx = chSrc.indexOf("e.target.closest('[data-share-channel]')");
|
|
assert(shareHandlerIdx > 0, 'found share-channel click handler block');
|
|
var shareBlock = chSrc.substring(shareHandlerIdx, shareHandlerIdx + 2500);
|
|
|
|
assert(/getLabel\s*\(|getLabels\s*\(|userLabel|labels\s*\[/.test(shareBlock),
|
|
'share handler resolves the user display label before rendering QR');
|
|
|
|
// Belt-and-suspenders: the call to ChannelQR.generate() inside the
|
|
// share handler must NOT pass a value derived only from
|
|
// `shareHash.substring(5)` (which yields `psk:<hex8>`). Require an
|
|
// explicit label fallback chain.
|
|
assert(/ChannelQR\.generate\s*\(\s*[a-zA-Z_]*[Ll]abel/.test(shareBlock) ||
|
|
/ChannelQR\.generate\s*\(\s*displayLabel|displayName/.test(shareBlock),
|
|
'share handler passes a label-derived display name to ChannelQR.generate');
|
|
|
|
console.log('\n=== #1087 Bug 3: PSK channel persistence via dedicated helper ===');
|
|
|
|
// A single canonical helper must own the persistence path. Both the
|
|
// Generate and the PSK-Add submit handlers must route through it so
|
|
// storage cannot be skipped or deferred to a later UI event.
|
|
assert(/function\s+persistAddedChannel\s*\(/.test(chSrc),
|
|
'channels.js defines a persistAddedChannel(...) helper');
|
|
|
|
// Helper must call ChannelDecrypt.storeKey AND verify the write
|
|
// landed in localStorage by re-reading it.
|
|
var helperIdx = chSrc.indexOf('function persistAddedChannel');
|
|
assert(helperIdx > 0, 'helper definition located');
|
|
var helperBlock = helperIdx > 0 ? chSrc.substring(helperIdx, helperIdx + 1500) : '';
|
|
assert(/storeKey\s*\(/.test(helperBlock),
|
|
'persistAddedChannel calls ChannelDecrypt.storeKey()');
|
|
assert(/getStoredKeys\s*\(|getKeys\s*\(|localStorage\.getItem/.test(helperBlock),
|
|
'persistAddedChannel verifies the write by re-reading storage');
|
|
|
|
// Both submit paths must invoke the helper.
|
|
assert(/chGenerateBtn[\s\S]{0,2000}persistAddedChannel\s*\(/.test(chSrc),
|
|
'Generate (#chGenerateBtn) handler invokes persistAddedChannel');
|
|
assert(/chPskAddBtn[\s\S]{0,2500}persistAddedChannel\s*\(|addUserChannel[\s\S]{0,2500}persistAddedChannel\s*\(/.test(chSrc),
|
|
'PSK Add path invokes persistAddedChannel');
|
|
|
|
console.log('\n=== #1087 Bug 4: Dedicated Share modal (separate from Add) ===');
|
|
|
|
// A NEW DOM element distinct from #chAddChannelModal must exist for
|
|
// sharing. Title, hex key field, URL field, privacy warning.
|
|
assert(/id="chShareModal"/.test(chSrc),
|
|
'dedicated #chShareModal element exists in channels.js markup');
|
|
|
|
// Modal must NOT just be an alias for the Add modal — its internals
|
|
// must include share-specific affordances.
|
|
var shareModalIdx = chSrc.indexOf('id="chShareModal"');
|
|
assert(shareModalIdx > 0, 'share modal markup located');
|
|
var shareModalBlock = shareModalIdx > 0 ? chSrc.substring(shareModalIdx, shareModalIdx + 3000) : '';
|
|
assert(/id="chShareModalTitle"|class="ch-share-modal-title"|>Share[^<]*</.test(shareModalBlock),
|
|
'share modal has its own title element ("Share: <Channel Name>")');
|
|
assert(/id="chShareKey"|data-share-field="key"/.test(shareModalBlock),
|
|
'share modal exposes the hex key field with a copy affordance');
|
|
// #1101: meshcore:// URL field intentionally REMOVED — QR already
|
|
// encodes the URL, separate field/button was redundant.
|
|
assert(/trusted|privacy|do not share|only share/i.test(shareModalBlock),
|
|
'share modal includes a privacy warning');
|
|
|
|
// Share click handler must open #chShareModal — not openAddModal().
|
|
var shareClickIdx = chSrc.indexOf("e.target.closest('[data-share-channel]')");
|
|
var shareClickBlock = shareClickIdx > 0 ? chSrc.substring(shareClickIdx, shareClickIdx + 2500) : '';
|
|
assert(/openShareModal\s*\(|chShareModal/.test(shareClickBlock),
|
|
'share button click handler opens #chShareModal (not the Add modal)');
|
|
assert(!/openAddModal\s*\(\s*\)/.test(shareClickBlock),
|
|
'share button click handler does NOT call openAddModal()');
|
|
|
|
console.log('\n=== Results ===');
|
|
console.log('Passed: ' + passed + ', Failed: ' + failed);
|
|
process.exit(failed > 0 ? 1 : 0);
|