Convert the manual test plan from the PR description into actual
Playwright tests that verify:
- Hex dump shows color-coded spans (not monochrome)
- Hex legend appears with color swatch items
- Field breakdown table section rows have tinted color classes
## Summary
Fixes the Playwright CI regression on master where the "Packets page
loads with filter" test times out after 15 seconds waiting for able
tbody tr to appear.
## Root Cause
Three packets tests used an bout:blank round-trip pattern to force a
full page reload:
`
page.goto(BASE) → set localStorage → page.goto('about:blank') →
page.goto(BASE/#/packets)
`
This cross-origin round-trip through bout:blank causes the SPA's config
fetch and router to not fire reliably in CI's headless Chromium, leaving
the page uninitialized past the 15-second timeout.
## Fix
Replace the bout:blank pattern with page.reload() in all three affected
tests:
`
page.goto(BASE/#/packets) → set localStorage → page.reload()
`
This stays on the same origin throughout. Playwright handles same-origin
reloads predictably — the page fully re-initializes, the IIFE re-reads
localStorage, and loadPackets() uses the correct time window.
## Tests affected
| Test | Change |
|------|--------|
| Packets page loads with filter | bout:blank → page.reload() |
| Packets initial fetch honors persisted time window | bout:blank →
page.reload() |
| Packets groupByHash toggle works | bout:blank → page.reload() |
## Validation
- All 318 unit tests pass (packet-filter: 62, aging: 29, frontend: 227)
- No public/ files changed — no cache buster needed
- Single file changed: est-e2e-playwright.js (9 insertions, 15
deletions)
Co-authored-by: Kpa-clawbot <259247574+Kpa-clawbot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
## Summary
Fixes#326 — the packets page crashes mobile browsers (iOS Safari, Edge)
by loading 50K+ packets when no time filter is persisted in
localStorage.
## Root Cause
Two problems in public/packets.js:
### Bug 1: savedTimeWindowMin defaults to 0 instead of 15
localStorage.getItem('meshcore-time-window') returns
ull when never set. Number(null) = 0. The guard checked < 0 but not <=
0, so savedTimeWindowMin = 0 meant "All time" — fetching all 50K+
packets.
**Fix:** Changed < 0 to <= 0 in both the initialization guard (line 30)
and the change handler (line 758).
### Bug 2: No mobile protection against large packet loads
Even with valid large time windows, mobile browsers crash under the
weight of thousands of DOM rows and packet data (~1.4 GB WebKit memory
limit).
**Fix:**
- Detect mobile viewport: window.innerWidth <= 768
- Cap limit at 1000 on mobile (vs 50000 on desktop)
- Disable 6h/12h/24h options and hide "All time" on mobile
- Reset persisted windows >3h to 15 min on mobile
## Testing
Added 9 unit tests in est-frontend-helpers.js covering:
- savedTimeWindowMin defaults to 15 when localStorage returns null
- savedTimeWindowMin defaults to 15 when localStorage returns "0"
- Valid values (60) are preserved
- Negative and NaN values default to 15
- PACKET_LIMIT is 1000 on mobile, 50000 on desktop
- Mobile caps large time windows (1440 → 15) but allows 180
All 218 frontend helper tests pass. Packet filter (62) and aging (29)
tests also pass.
## Changes
| File | Change |
|------|--------|
| public/packets.js | Fix <= 0 guard, add mobile detection, cap limit,
restrict time options |
| public/index.html | Cache buster bump |
| est-frontend-helpers.js | 9 new regression tests for time window
defaults and mobile caps |
---------
Co-authored-by: Kpa-clawbot <259247574+Kpa-clawbot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
## Summary
- fix packets initial load to honor persisted `meshcore-time-window`
before the filter UI is rendered
- keep the dropdown and effective query window in sync via a shared
`savedTimeWindowMin` value
- add a frontend regression test to ensure `loadPackets()` falls back to
persisted time window when `#fTimeWindow` is not yet present
- bump cache busters in `public/index.html`
## Root cause
`loadPackets()` could run before the filter bar existed, so
`document.getElementById('fTimeWindow')` was null and it fell back to
`15` minutes even though localStorage had a different saved value.
## Testing
- `node test-frontend-helpers.js`
- `node test-packet-filter.js`
- `node test-aging.js`
---------
Co-authored-by: Kpa-clawbot <259247574+Kpa-clawbot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
## Summary
Complete CI pipeline restructure. Sequential fail-fast chain, E2E tests
against Go server with real staging data, all deprecated Node.js server
tests removed.
### Pipeline (PR):
1. **Go unit tests** — fail-fast, coverage + badges
2. **Playwright E2E** — against Go server with fixture DB, frontend
coverage, fail-fast on first failure
3. **Docker build** — verify containers build
### Pipeline (master merge):
Same chain + deploy to staging + badge publishing
### Removed:
- All Node.js server-side unit tests (deprecated JS server)
- `npm ci` / `npm run test` steps
- JS server coverage collection (`COVERAGE=1 node server.js`)
- Changed-files detection logic
- Docs-only CI skip logic
- Cancel-workflow API hacks
### Added:
- `test-fixtures/e2e-fixture.db` — real data from staging (200 nodes, 31
observers, 500 packets)
- `scripts/capture-fixture.sh` — refresh fixture from staging API
- Go server launches with `-port 13581 -db test-fixtures/e2e-fixture.db
-public public-instrumented`
---------
Co-authored-by: Kpa-clawbot <kpabap+clawdbot@gmail.com>
Co-authored-by: you <you@example.com>
The test 'Node perf page should NOT show Go Runtime section' asserts
Node.js-specific behavior, but E2E tests now run against the Go server
(per this PR), so Go Runtime info is correctly present. Remove the
now-irrelevant assertion.
* docs: remove letsmesh.net reference from README
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* ci: remove paths-ignore from pull_request trigger
PR #233 only touches .md files, which were excluded by paths-ignore,
causing CI to be skipped entirely. Remove paths-ignore from the
pull_request trigger so all PRs get validated. Keep paths-ignore on
push to avoid unnecessary deploys for docs-only changes to master.
* ci: skip heavy CI jobs for docs-only PRs
Instead of using paths-ignore (which skips the entire workflow and
blocks required status checks), detect docs-only changes at the start
of each job and skip heavy steps while still reporting success.
This allows doc-only PRs to merge without waiting for Go builds,
Node.js tests, or Playwright E2E runs.
Reverts the approach from 7546ece (removing paths-ignore entirely)
in favor of a proper conditional skip within the jobs themselves.
* fix: update engine tests to match engine-badge HTML format
Tests expected [go]/[node] text but formatVersionBadge now renders
<span class="engine-badge">go</span>. Updated 6 assertions to
check for engine-badge class and engine name in HTML output.
---------
Co-authored-by: Kpa-clawbot <259247574+Kpa-clawbot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Kpa-clawbot <kpabap+clawdbot@gmail.com>
Co-authored-by: you <you@example.com>
Three optimizations to the CI frontend test pipeline:
1. Run E2E tests and coverage collection concurrently
- Previously sequential (E2E ~1.5min, then coverage ~5.75min)
- Now both run in parallel against the same instrumented server
- Expected savings: ~5 min (coverage runs alongside E2E instead of after)
2. Replace networkidle with domcontentloaded in coverage collector
- SPA uses hash routing — networkidle waits 500ms for network silence
on every navigation, adding ~10-15s of dead time across 23 navigations
- domcontentloaded fires immediately once HTML is parsed; JS initializes
the route handler synchronously
- For in-page hash changes, use 200ms setTimeout instead of
waitForLoadState (which would never re-fire for same-document nav)
3. Extract coverage from E2E tests too
- E2E tests already exercise the app against the instrumented server
- Now writes window.__coverage__ to .nyc_output/e2e-coverage.json
- nyc merges both coverage files for higher total coverage
Also:
- Split Playwright install into browser + deps steps (deps skip if present)
- Replace sleep 5 with health-check poll in quick E2E path
- app.js: render engine badge with .engine-badge span (was plain text)
- test: fix #pktRight waitForSelector to use state:'attached' (hidden by detail-collapsed)
- test: fix map heat persist race — wait for async init to restore checkbox state
- test: fix live heat persist race — test via localStorage set+reload instead of click
- test: fix live matrix toggle race — wait for Leaflet tiles before clicking
- test: increase packet detail timeouts for remote server resilience
- test: make close-button test self-contained (navigate if #pktRight missing)
- bump cache busters
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Issues fixed:
- #127: Firefox copy URL - shared copyToClipboard() with execCommand fallback
- #125: Dismiss packet detail pane - close button with keyboard support
- #124: Customize window scrollbar - flex layout fix for overflow
- #122: Last Activity stale times - use last_heard || last_seen
Test improvements:
- E2E perf: replace 19 networkidle waits, cut navigations 14->7, remove 11 sleeps
- 8 new unit tests for copyToClipboard helper (47->55 in test-frontend-helpers)
- 1 new E2E test for packet pane dismiss
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
browser.close() on line 274 was firing before tests 14-16 executed,
causing them to crash with 'Target page, context or browser has been
closed'. Moved to after test 16, just before the summary block.
Fixes 3 of 4 E2E failures (remaining 2 are data-dependent map tests).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Added map.on('resize') handler that re-renders markers, recalculating
pixel-based decollision offsets for the new container size. Previously
only zoomend triggered re-render — resize left stale offsets.
Added E2E test verifying markers survive a viewport resize.
- Live page showHeatMap() now reads meshcore-live-heatmap-opacity from
localStorage and applies it to the canvas element (was hardcoded 0.3)
- Customizer now has two clearly labeled sliders:
🗺️ Nodes Map — controls the static map page heatmap
📡 Live Map — controls the live page heatmap
- Each uses its own localStorage key (meshcore-heatmap-opacity vs
meshcore-live-heatmap-opacity)
- Added E2E tests for live opacity persistence and dual slider existence
- 13/15 E2E tests pass locally (2 fail due to ARM chromium OOM after
heavy live page tests — CI on x64 will handle them)
Closes#119 properly this time.
Live page liveHeatToggle now saves to localStorage (meshcore-live-heatmap).
Map page was already fixed but live page was missed.
Added E2E test that verifies persistence across reload.
13/13 E2E tests pass locally.
- Heat checkbox persists in localStorage across reload
- Heat checkbox is clickable (not disabled)
- Live page heat disabled when ghosts/matrix mode active
- Heatmap opacity value persists and applies to canvas
4 new tests, all verified locally before push.
- Full test file list with all 12+ test files
- Feature development workflow: write code → write tests → run locally → push
- Playwright defaults to localhost:3000, NEVER prod
- Coverage infrastructure explained (Istanbul instrument → Playwright → nyc)
- ARM testing notes (basic tests work, heavy coverage use CI)
- 4 new pitfalls from today's session
Tests now run in the test job, not after deploy. Spins up server.js
on port 13581, runs Playwright against it, kills it after.
If E2E fails, deploy is blocked — broken code never reaches prod.
BASE_URL env var makes the test configurable.
8 smoke tests against prod after deployment completes.
Uses Playwright bundled Chromium on x86 runner.
Falls back to CHROMIUM_PATH env var for other architectures.
Proof of concept: bare Playwright (not @playwright/test) running 8 critical
flow tests against analyzer.00id.net:
- Home page, nodes, map, packets, node detail, theme customizer, dark mode, analytics
- Uses system Chromium on ARM (Playwright bundled binary doesn't work on musl)
- Not added to test-all.sh or CI yet — POC only
- Run with: node test-e2e-playwright.js