mirror of
https://github.com/Kpa-clawbot/meshcore-analyzer.git
synced 2026-08-28 21:28:32 +00:00
Adds cross-navigation between the observer detail page and the node
detail page for the same pubkey (community feature request from
cwichura).
**Changes**
- `public/observer-detail.js`: new `<a
href="#/nodes/${encodeURIComponent(currentId)}">View node detail →</a>`
inside `.page-header`, next to the `<h2 id="obsTitle">`.
- `public/nodes.js`: new sibling `<a
href="#/observers/${encodeURIComponent(n.public_key)}"
class="btn-primary">Observer →</a>` in the same button row as the
`Analytics` / `Reach` anchors on the full node detail page. Uses the
existing `ph-eye` phosphor icon.
**Test — TDD red→green**
- Red commit: `8c2315e1` (`test(#1825): red — observer<->node cross-link
anchors missing`) — 4/4 assertions fail on master; CI RED.
- Green commit: `ff8f6ed7` — minimum production change; 4/4 assertions
pass locally.
Test file: `test-issue-1825-observer-node-cross-links.js` —
static-source DOM-grep style consistent with the neighbouring
`test-issue-1789-observer-firmware-cols.js` /
`test-observers-headings.js` pattern. It asserts:
1. observer-detail.js contains
`href="#/nodes/${encodeURIComponent(currentId)}"`.
2. That anchor sits inside the `.page-header` block.
3. nodes.js contains
`href="#/observers/${encodeURIComponent(n.public_key)}"`.
4. That anchor is a sibling of the analytics/reach anchors in the same
flex row.
**Notes**
- Pubkeys are `encodeURIComponent`-escaped on both sides
(defense-in-depth; MeshCore pubkeys are hex only).
- No API changes. No CSS changes. No new dependencies.
Fixes #1825
---------
Co-authored-by: meshcore-bot <meshcore-bot@users.noreply.github.com>
This commit is contained in:
co-authored by
meshcore-bot
parent
096e16409c
commit
ba68069c23
@@ -617,6 +617,7 @@
|
||||
<button class="btn-primary" id="copyShortUrlBtn" title="Short URL using an 8-char pubkey prefix — easier to send over the mesh (issue #772)" style="flex:0 0 auto;font-size:12px;padding:4px 10px"><svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor-sprite.svg#ph-broadcast"/></svg> Copy short URL</button>
|
||||
<a href="#/nodes/${encodeURIComponent(n.public_key)}/analytics" class="btn-primary" style="flex:0 0 auto;text-decoration:none;font-size:12px;padding:4px 10px"><svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor-sprite.svg#ph-chart-bar"/></svg> Analytics</a>
|
||||
<a href="#/nodes/${encodeURIComponent(n.public_key)}/reach" class="btn-primary" style="flex:0 0 auto;text-decoration:none;font-size:12px;padding:4px 10px"><svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor-sprite.svg#ph-broadcast"/></svg> Reach</a>
|
||||
<a href="#/observers/${encodeURIComponent(n.public_key)}" class="btn-primary" title="View this pubkey as an observer" style="flex:0 0 auto;text-decoration:none;font-size:12px;padding:4px 10px"><svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor-sprite.svg#ph-eye"/></svg> Observer →</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -81,6 +81,7 @@ window.ObserverDetailNaiveBanner = {
|
||||
<div class="page-header" style="display:flex;align-items:center;gap:12px;margin-bottom:16px">
|
||||
<a href="#/observers" class="btn-icon" title="Back to Observers" aria-label="Back">←</a>
|
||||
<h2 style="margin:0" id="obsTitle">Observer Detail</h2>
|
||||
<a href="#/nodes/${encodeURIComponent(currentId)}" class="btn-secondary" title="View this pubkey as a node" style="text-decoration:none;font-size:12px;padding:4px 10px">View node detail →</a>
|
||||
<div style="margin-left:auto;display:flex;gap:8px;align-items:center;flex-wrap:wrap">
|
||||
<span class="compare-with-group">
|
||||
<label class="sr-only" for="obsCompareWithPicker">Compare with another observer</label>
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
/* test-issue-1825-observer-node-cross-links.js — Issue #1825 regression test.
|
||||
*
|
||||
* Asserts cross-navigation links exist between the observer detail page and
|
||||
* the node detail page for the same pubkey:
|
||||
*
|
||||
* - public/observer-detail.js renders an anchor `href="#/nodes/${encodeURIComponent(currentId)}"`
|
||||
* inside the .page-header (near the `<h2 id="obsTitle">`).
|
||||
* - public/nodes.js renders an anchor `href="#/observers/${encodeURIComponent(n.public_key)}"`
|
||||
* in the same button row as the analytics/reach anchors on the full node
|
||||
* detail page.
|
||||
*
|
||||
* Static-source test (grep the file text). No server, no DOM.
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const assert = require('assert');
|
||||
|
||||
let passed = 0, failed = 0;
|
||||
function test(name, fn) {
|
||||
try { fn(); passed++; console.log(` \u2713 ${name}`); }
|
||||
catch (e) { failed++; console.log(` \u2717 ${name}\n ${e.message}`); }
|
||||
}
|
||||
|
||||
console.log('\u2500\u2500 Observer <-> Node cross-links (#1825) \u2500\u2500');
|
||||
|
||||
const obsSrc = fs.readFileSync(path.join(__dirname, 'public', 'observer-detail.js'), 'utf8');
|
||||
const nodesSrc = fs.readFileSync(path.join(__dirname, 'public', 'nodes.js'), 'utf8');
|
||||
|
||||
test('observer-detail.js links to #/nodes/${encodeURIComponent(currentId)}', () => {
|
||||
assert.ok(
|
||||
/href="#\/nodes\/\$\{encodeURIComponent\(currentId\)\}"/.test(obsSrc),
|
||||
'expected anchor href="#/nodes/${encodeURIComponent(currentId)}" in observer-detail.js'
|
||||
);
|
||||
});
|
||||
|
||||
test('observer-detail.js cross-link sits inside the .page-header block', () => {
|
||||
const m = obsSrc.match(/class="page-header"[\s\S]*?<\/div>/);
|
||||
assert.ok(m, '.page-header block not found in observer-detail.js');
|
||||
assert.ok(
|
||||
/href="#\/nodes\/\$\{encodeURIComponent\(currentId\)\}"/.test(m[0]),
|
||||
'observer -> node cross-link is not inside the .page-header block'
|
||||
);
|
||||
});
|
||||
|
||||
test('nodes.js links to #/observers/${encodeURIComponent(n.public_key)}', () => {
|
||||
assert.ok(
|
||||
/href="#\/observers\/\$\{encodeURIComponent\(n\.public_key\)\}"/.test(nodesSrc),
|
||||
'expected anchor href="#/observers/${encodeURIComponent(n.public_key)}" in nodes.js'
|
||||
);
|
||||
});
|
||||
|
||||
test('nodes.js Observer link is a sibling of the analytics/reach anchors', () => {
|
||||
// Find the button row that contains both analytics and reach anchors and
|
||||
// assert it now also contains the observers anchor.
|
||||
const rowRe = /<div[^>]*display:flex[^>]*flex-wrap:wrap[^>]*>[\s\S]*?<\/div>/g;
|
||||
let found = false;
|
||||
let m;
|
||||
while ((m = rowRe.exec(nodesSrc)) !== null) {
|
||||
const row = m[0];
|
||||
if (/href="#\/nodes\/\$\{encodeURIComponent\(n\.public_key\)\}\/analytics"/.test(row) &&
|
||||
/href="#\/nodes\/\$\{encodeURIComponent\(n\.public_key\)\}\/reach"/.test(row)) {
|
||||
if (/href="#\/observers\/\$\{encodeURIComponent\(n\.public_key\)\}"/.test(row)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
assert.ok(found, 'observer anchor not found in the same button row as analytics/reach anchors');
|
||||
});
|
||||
|
||||
console.log(`\n${passed} passed, ${failed} failed`);
|
||||
process.exit(failed ? 1 : 0);
|
||||
Reference in New Issue
Block a user