Skip to content

v1.3.36.1 -- Capture automation: auth state + dimensions + banner fixes

A bugfix on top of v1.3.36 closing three regressions discovered in the operator's first capture session against prod.

argosVersion and frontend/package.json deliberately stay at 1.3.35.4 (tooling-only release; no panel binary change). scripts/capture/package.json bumps 1.3.36.01.3.36.1.

Bugs fixed

BUG 1 — auth state did not persist between Playwright tests

Symptom: tests #3+ (post-login captures) silently rendered the /login redirect instead of the authenticated panel content. They reported PASS but the PNGs were the wrong surface.

Root cause: v1.3.36's capture.spec.js had an empty test.beforeAll with a comment claiming the browser context is reused automatically. Default Playwright behaviour: each test() gets a fresh BrowserContext from the per-test page fixture, with no shared cookies. Test #2 (auth: log in for remaining captures) DID log in successfully, but the session cookie was scoped to test #2's context and discarded when the test ended. Tests #3+ got fresh contexts → no session → page.goto('/') redirected to /login.

Fix: canonical Playwright storageState pattern. Two new pieces:

  • scripts/capture/auth.setup.js (new): a separate "setup" Playwright project. Logs in via lib/auth.js and persists cookies via context.storageState({ path: AUTH_STATE }).
  • scripts/capture/playwright.config.js declares two projects: setup (runs auth.setup.js) and captures (runs capture.spec.js, depends on setup, reads use.storageState = AUTH_STATE). Playwright loads the state into every test's context automatically.

The login.png capture is wrapped in test.describe('login (anon)') with test.use({ storageState: { cookies: [], origins: [] } }) so the /login page renders un-authenticated for that single capture.

run.sh and run-demo.sh gain a consolidated trap on EXIT INT TERM that deletes ${AUTH_STATE} so the operator's session cookie doesn't persist on disk past the run.

BUG 2 — banner output had un-expanded shell command-substitution

Symptom: end-of-run banner printed:

skipped=$(wc -l < /tmp/argos-captures-pending/.skip-list 2>/dev/null || echo 0)

— the literal string, not the count.

Root cause: test.afterAll in capture.spec.js used a JS template literal:

console.log(`skipped=$(wc -l < ${SKIP_LIST_PATH} ...)`);

JS template literals expand ${SKIP_LIST_PATH} (JS interpolation) but NOT $(...) (bash command substitution). The literal text reached stdout.

Fix: replace the bash command-sub with fs.readFileSync + JS line counting. The new code:

const skippedCount = fs.readFileSync(SKIP_LIST_PATH, 'utf8')
    .split('\n')
    .filter((line) => line.trim().length > 0)
    .length;
console.log(`skipped=${skippedCount}`);

BUG 3 — viewport too short for scrollable surfaces

Symptom: surfaces with long content (Hosts list, Banned IPs, Activity audit log, Scenarios, Notification deliveries, Backups) were clipped at the 1440×900 viewport bottom. Most of the data wasn't in the screenshot.

Root cause: playwright.config.js set viewport to 1440×900 (matched the docs portal width but not deep enough for vertical lists), and shotFull() used fullPage: false which only captures the viewport.

Fix: two parts:

  • Viewport bumped to 1440×1080 so above-fold cards (dashboard, settings) have more room before scroll.
  • New shotFullScroll(page, name) helper: same page.screenshot() but with fullPage: true for full scrollable content. The 15 long-list surfaces use it; the 21 above-fold / modal / card-shaped surfaces continue to use shotFull() (viewport-only):
shotFullScroll (15) shotFull (21)
hosts-list-auth-column login
hosts-detect-badge dashboard-overview
security-banned dashboard-security
security-whitelist host-form
security-activity host-form-dns-provider-dropdown
security-scenarios host-form-true-detect
security-overview target-group-form
threats-decisions target-group-first-target
notifications-deliveries target-group-two-targets
logs-browser appsec-status
backups-list drift-indicators
settings-panel selfblock-banner
appsec-metrics
backup-settings
geoip-status
sso-allowlist
totp-setup
settings-dns-providers
country-bans-progress

Smoke updates

scripts/smoke/capture-automation.sh gains three new phases (6, 7, 8) on top of the existing 5:

6. storageState wiring:
   - auth.setup.js present
   - auth.setup.js calls context.storageState
   - playwright.config.js declares use.storageState
   - captures project depends on setup project
   - run.sh + run-demo.sh trap-clean the auth state file

7. Banner output uses fs.readFileSync (no live console.log
   with bash command-substitution syntax)

8. Viewport 1440×1080 + shotFullScroll() helper present;
   reports the shotFullScroll/shotFull call counts

Plus two pre-existing fixes caught while re-running:

  • Phase 5 (working-tree-clean) trap was being clobbered by a later trap '...' EXIT for SAFE_TEST_DIR. Consolidated into a single trap covering all cleanups.
  • Phase 1's .env backup was stored INSIDE the captures directory, so the bak file showed up in git status mid-smoke. Moved to mktemp -d outside the repo.

scripts/check-no-personal-data.sh gains --exclude-dir=test-results --exclude-dir=playwright-report so Playwright runtime artifacts (gitignored, but the script scans the live filesystem) don't trip the operator-LAN-IPs check on a stale post-run directory.

Live evidence (smoke output post-fix)

phase 1: run.sh refuses to run without .env...        PASS
phase 2: .env is git check-ignore'd...                PASS
phase 3: .env.example contains only RFC placeholders... PASS
phase 4: safeClick synthetic test (looksBlocked)...   PASS (13/13)
phase 5: working tree unchanged by smoke...           PASS
phase 6: storageState wiring (v1.3.36.1)...           PASS (5 sub-checks)
phase 7: banner output uses fs.readFileSync...        PASS
phase 8: viewport 1440x1080 + shotFullScroll...       PASS
  shotFullScroll calls: 15; shotFull calls: 21

Files changed

  • scripts/capture/auth.setup.js (new)
  • scripts/capture/playwright.config.js (two-project config; viewport 1440×1080)
  • scripts/capture/capture.spec.js (drop empty beforeAll + drop test #2 auth (handled by setup); login.png wrapped in anon describe; afterAll uses fs.readFileSync; new shotFullScroll helper; 15 surfaces use it)
  • scripts/capture/run.sh (consolidated trap; AUTH_STATE cleanup; pass ARGOS_AUTH_STATE through)
  • scripts/capture/run-demo.sh (same)
  • scripts/capture/package.json (1.3.36.01.3.36.1)
  • scripts/smoke/capture-automation.sh (phases 6-8 added; consolidated trap; backup outside repo)
  • scripts/check-no-personal-data.sh (test-results + playwright-report excluded)
  • docs/release-notes/v1.3.36.1.md (this file)
  • CHANGELOG.md, mkdocs.yml

NOT changed: argosVersion stays at 1.3.35.4, frontend/package.json version stays at 1.3.35.4. No Go code; no frontend code; no panel binary change.

Operator workflow post-fix

cd ~/argos-edge && git pull
cd scripts/capture
# package-lock unchanged; just bump version applied:
# (no npm reinstall needed unless you blew away node_modules)

cd ~/argos-edge
scripts/capture/run.sh

# Verify:
# - /tmp/argos-captures-pending/dashboard-overview.png shows the
#   real dashboard, not a /login redirect.
# - long-list surfaces (security-banned, security-activity,
#   notifications-deliveries) are full-page, not viewport-clipped.
# - end-of-run banner reads "skipped=N" with a real number,
#   not the literal "$(wc -l ...)" string.

When done:

scripts/demo/teardown.sh --purge   # if demo was used

The auth state file (/tmp/argos-auth-state.json) is auto-deleted by the trap on run.sh / run-demo.sh exit.

Versioning

scripts/capture/package.json 1.3.36.01.3.36.1 (independent of argosVersion; tracks tooling patches).

argosVersion + frontend/package.json deliberately stay at 1.3.35.4. Tag-without-rebuild precedent for tooling-only patches: v1.3.27.1, v1.3.34, v1.3.35.1, v1.3.35.5.