Ralphy will execute each unchecked task sequentially using your chosen AI engine.
Build a comprehensive automated UI testing pipeline for the Nostria Angular web app using Playwright. The system must support authenticated testing via nsec private key injection, full console log capture and analysis, performance metrics collection, cross-browser/device testing, and produce actionable improvement reports.
The app uses Nostr protocol with advanced authentication (nsec, NIP-07 extensions,
NIP-46 bunkers). Private keys are encrypted with a default PIN ("0000") via
PBKDF2+AES-256-GCM and stored in localStorage under nostria-account /
nostria-accounts keys. All timestamps are in seconds (Nostr convention).
- Create
.env.examplewith documented test variables:TEST_NSEC(nsec1... private key for test account),TEST_PUBKEY(hex pubkey, auto-derived if omitted),BASE_URL(defaulthttp://localhost:4200),TEST_LOG_LEVEL(debug/info/warn/error),CI(boolean) - Create
.envloading in Playwright config: installdotenvas devDependency, addimport 'dotenv/config'toplaywright.config.ts, and readTEST_NSEC/TEST_PUBKEY/BASE_URLfromprocess.env - Validate
.envis already listed in.gitignore(confirmed: line 7). Addtest-results/to.gitignoreif not already present - Add a
test:e2e:authnpm script topackage.jsonthat runs only authenticated test files:playwright test --grep @auth - Add a
test:e2e:fullnpm script that runs all tests (public + authenticated) with full artifact collection:playwright test --project=chromium - Add a
test:e2e:metricsnpm script that runs tests and generates the performance/metrics report:playwright test --project=ai-debug --grep @metrics
- Create
e2e/helpers/auth.tswith aTestAuthHelperclass that: importsgetPublicKeyandnip19fromnostr-tools, accepts an nsec1 string or hex private key, derives the public key, and builds a validNostrUserobject withsource: 'nsec',hasActivated: true,lastUsed: Date.now(),isEncrypted: false, and plaintext hex privkey - Add a
injectAuth(page: Page)method toTestAuthHelperthat usespage.addInitScript()to setlocalStorage['nostria-account']andlocalStorage['nostria-accounts']with the constructedNostrUserbefore the app loads - Add a
clearAuth(page: Page)method toTestAuthHelperthat removes auth keys from localStorage and reloads the page - Add a
getTestKeypair()static method that generates a fresh random keypair usingnostr-tools/pure(generateSecretKey(),getPublicKey()), returning{ nsec, pubkey, privkeyHex }for use when no.envkey is provided - Add validation: if
TEST_NSECis set in env, use that key; otherwise auto-generate a throwaway keypair and log a warning that authenticated tests will use a random identity with no relay history - Replace the placeholder
NostrTestUtils.generateTestKeypair()ine2e/fixtures.tswith a real implementation using nostr-tools that callsTestAuthHelper.getTestKeypair()
- Add an
authenticatedPagefixture toe2e/fixtures.tsthat callsTestAuthHelper.injectAuth(page)before yielding the page, and callsclearAuth(page)after the test completes — this fixture provides a pre-logged-in browser context - Add a
performanceMetricsfixture that collects Web Vitals (LCP, FID, CLS, TTFB, FCP) viapage.evaluate()using the PerformanceObserver API, storing results intest-results/metrics/ - Add a
networkMonitorfixture that tracks all WebSocket connections (relay connections), HTTP requests, and failed requests — saving a summary JSON totest-results/network/ - Add a
consoleAnalyzerfixture that extends the existingconsoleLogsfixture to categorize logs by severity, count errors/warnings, detect Nostr-specific log patterns (e.g.,[AccountStateService],[SubscriptionCache], relay EOSE/NOTICE messages), and produce a structured analysis report - Add a
memoryMonitorfixture that capturesperformance.memory(Chrome only) at test start and end, computing memory delta, and flags potential memory leaks if growth exceeds a threshold (e.g., 50MB)
- Create
e2e/helpers/console-analyzer.tswith aConsoleAnalyzerclass that categorizes captured console logs into: errors, warnings, Nostr relay messages, Angular lifecycle events, network issues, and application debug logs - Add pattern matching for known Nostr log prefixes:
[AccountStateService],[Profile Loading],[Cache],[SubscriptionCache],[RelayService],[MediaPlayer], and extract structured data (relay URLs, event kinds, subscription IDs) - Add error classification: distinguish between expected errors (e.g., relay connection refused, 404 for missing profile images) and unexpected errors (unhandled promise rejections, Angular errors, TypeError/ReferenceError)
- Add a
generateReport()method that outputs a JSON summary: total log count by type, top 10 most frequent messages, list of unique errors, relay connection success/failure rates, and warnings about potential issues - Save the console analysis report to
test-results/reports/console-analysis-{timestamp}.jsonafter each test suite run - Add console log assertions: helper functions like
expectNoUnexpectedErrors(logs),expectRelayConnections(logs, minCount),expectNoAngularErrors(logs)that can be used in tests
- Refactor existing
e2e/tests/home.spec.tsto use descriptive tags (@public,@smoke) and ensure all tests save console logs on completion via thesaveConsoleLogsfixture - Refactor existing
e2e/tests/navigation.spec.tsto tag with@public @navigationand add console log saving - Refactor existing
e2e/tests/accessibility.spec.tsto tag with@public @a11yand add console log saving - Create
e2e/tests/public/discover.spec.ts— test the Discover page (/discover): verify page loads, content cards render, categories/filters are interactive, no JS errors in console - Create
e2e/tests/public/articles.spec.ts— test the Articles page (/articles): verify article list renders, article cards have titles, clicking an article navigates to detail view - Create
e2e/tests/public/music.spec.ts— test the Music page (/music): verify music list loads, player controls are present, track metadata displays - Create
e2e/tests/public/streams.spec.ts— test the Streams page (/streams): verify stream cards render, live indicator works if streams are active - Create
e2e/tests/public/search.spec.ts— test the Search page (/search): verify search input is focusable, typing triggers search, results display or empty state shows - Create
e2e/tests/public/profile-view.spec.ts— test viewing a public profile (/p/{npub}): verify profile header loads, display name renders, notes tab shows events, about tab shows bio - Create
e2e/tests/public/event-view.spec.ts— test viewing a single event (/e/{nevent}): verify event content renders, author info displays, reply thread loads if present - Create
e2e/tests/public/deep-links.spec.ts— test NIP-19 entity deep links: npub, note, nprofile, nevent, naddr URLs all resolve correctly without errors - Create
e2e/tests/public/error-handling.spec.ts— test 404 routes, malformed npub/nevent URLs, and verify the app handles them gracefully (no crash, shows fallback UI) - Create
e2e/tests/public/responsive.spec.ts— test responsive layout at 5 viewport sizes (mobile 375px, tablet 768px, small desktop 1024px, desktop 1440px, ultrawide 1920px): verify navigation adapts, content reflows, no horizontal overflow
- Create
e2e/tests/auth/login.spec.ts(@auth @smoke) — test nsec login flow via the LoginDialog UI: open login dialog, enter nsec, verify login succeeds, account appears in sidebar, pubkey matches expected. Also test invalid nsec handling (error message shown, no crash) - Create
e2e/tests/auth/account-state.spec.ts(@auth) — usingauthenticatedPagefixture, verify: profile name displays in sidebar, account menu shows the logged-in account, switching between accounts works if multiple are configured - Create
e2e/tests/auth/profile-edit.spec.ts(@auth) — navigate to own profile, click edit, verify form fields load (display name, about, picture URL, banner URL, NIP-05), make a change and verify it's reflected locally (do NOT publish to avoid polluting relays) - Create
e2e/tests/auth/create-note.spec.ts(@auth) — open the note creation dialog, type content, verify the note preview, test character count display, test cancel closes dialog without posting, verify the publish button is enabled with content - Create
e2e/tests/auth/messages.spec.ts(@auth) — navigate to Messages, verify DM list loads (may be empty for test account), verify new message UI is accessible, test conversation thread rendering - Create
e2e/tests/auth/settings.spec.ts(@auth) — navigate to Settings, verify all setting sections render (appearance, relays, notifications, privacy, backups), toggle theme between light/dark, verify relay list displays connected relays - Create
e2e/tests/auth/notifications.spec.ts(@auth) — navigate to Notifications, verify the page loads, notification list renders or empty state displays, notification filtering tabs are interactive - Create
e2e/tests/auth/following-feed.spec.ts(@auth) — verify the home feed in authenticated mode shows content from followed accounts (if any), test feed refresh, test infinite scroll loading - Create
e2e/tests/auth/relay-management.spec.ts(@auth) — navigate to relay settings, verify relay list shows URLs and connection status, test adding/removing a relay (UI only, verify the list updates), test relay connection indicators - Create
e2e/tests/auth/command-palette.spec.ts(@auth) — open command palette (Ctrl+K), verify authenticated commands are available (Create Note, Settings, Profile, etc.), execute navigation commands, verify search within command palette works - Create
e2e/tests/auth/logout.spec.ts(@auth) — verify logout flow: click account menu, click logout/remove account, verify the app returns to unauthenticated state, localStorage is cleared of account data
- Create
e2e/tests/performance/page-load.spec.ts(@metrics) — measure initial page load time for 5 key routes (/, /discover, /articles, /music, /settings), record Navigation Timing API metrics (domContentLoadedEventEnd, loadEventEnd), save totest-results/metrics/page-load.json - Create
e2e/tests/performance/web-vitals.spec.ts(@metrics) — collect Core Web Vitals (LCP, FID/INP, CLS) for the home page using PerformanceObserver, compare against "good" thresholds (LCP < 2.5s, CLS < 0.1), report pass/fail with actual values - Create
e2e/tests/performance/bundle-size.spec.ts(@metrics) — after page load, collect all JS/CSS resource sizes viaperformance.getEntriesByType('resource'), report total bundle size, flag resources over 500KB, save resource breakdown to JSON - Create
e2e/tests/performance/memory.spec.ts(@metrics @auth) — in authenticated mode, navigate through 10 pages sequentially, captureperformance.memory.usedJSHeapSizeat each step, report if memory grows monotonically (potential leak), save the memory timeline to JSON - Create
e2e/tests/performance/relay-performance.spec.ts(@metrics @auth) — in authenticated mode, measure WebSocket connection times to each relay, track message latency (REQ to EOSE), count total events received, report relay responsiveness - Create
e2e/helpers/metrics-collector.ts— a utility class that aggregates all performance data from individual tests into a unifiedtest-results/reports/performance-report.jsonwith: page load times, web vitals, bundle sizes, memory usage, relay performance, and historical comparison (if previous report exists)
- Create
e2e/helpers/websocket-monitor.ts— a utility that intercepts WebSocket connections via CDP (Chrome DevTools Protocol) usingpage.context().newCDPSession(page), logs all WebSocket frames (sent/received), and categorizes Nostr protocol messages (REQ, EVENT, EOSE, NOTICE, CLOSE) - Add relay connection tracking: record which relays connect successfully, which timeout, which return errors, and the connection duration for each
- Add subscription tracking: monitor REQ/CLOSE pairs, detect orphaned subscriptions (REQ without CLOSE), and track event delivery counts per subscription
- Create
e2e/tests/network/relay-connections.spec.ts(@network @auth) — verify the app connects to relays from the user's relay list, test reconnection behavior by simulating a connection drop, verify EOSE is received for initial subscriptions - Create
e2e/tests/network/api-calls.spec.ts(@network) — monitor HTTP requests to the Nostria API (api.nostria.apporlocalhost:3000), verify expected endpoints are called, check for failed requests, log response times
- Install
@playwright/testvisual comparison support (built-in). Createe2e/tests/visual/directory for screenshot comparison tests - Create
e2e/tests/visual/theme-consistency.spec.ts— capture screenshots of 5 key pages in both light and dark mode, compare against baseline screenshots, fail if pixel diff exceeds 1% threshold - Create
e2e/tests/visual/responsive-layout.spec.ts— capture screenshots at mobile (375px), tablet (768px), and desktop (1440px) for the home page, profile page, and settings page — compare against baselines - Create
e2e/tests/visual/component-gallery.spec.ts— navigate to pages that showcase key UI components (buttons, cards, dialogs, forms) and capture component-level screenshots for regression detection - Add baseline screenshot management: add
e2e/screenshots/directory for golden screenshots, document the update process (npx playwright test --update-snapshots), add to.gitignoreguidance
- Create
.github/workflows/e2e-tests.yml— GitHub Actions workflow that: checks out code, installs Node 20, runsnpm ci, installs Playwright browsers (npx playwright install --with-deps chromium), starts the dev server, runsnpm run test:e2e:full, uploadstest-results/as artifact on failure - Add secrets configuration: document adding
TEST_NSECas a GitHub Actions secret for authenticated tests, with fallback to auto-generated keypair if secret is not set - Add test result commenting: use
actions/github-scriptto post a summary comment on PRs with: total tests, passed/failed count, link to full report artifact, any performance regressions detected - Add test caching: cache
node_modulesand Playwright browsers between runs for faster CI execution - Create
.github/workflows/e2e-nightly.yml— nightly workflow that runs the full test suite including performance metrics and visual regression, and stores results for trend analysis
- Create
e2e/helpers/report-generator.ts— a utility that reads all JSON outputs fromtest-results/(test summary, console analysis, performance metrics, network monitoring, memory usage) and generates a unifiedtest-results/reports/full-report.json - Add a human-readable Markdown report generator: produce
test-results/reports/test-report.mdwith sections for: executive summary, test results table, performance metrics with pass/fail indicators, console error summary, network health, memory usage trends, and actionable improvement recommendations - Add historical comparison: if a previous
full-report.jsonexists (e.g., from last CI run), compare metrics and highlight regressions (page load time increased, new console errors, memory growth) - Add improvement suggestions engine: analyze the collected data and generate specific, actionable recommendations such as: "Reduce bundle size by code-splitting the music player module (currently 450KB)", "Fix unhandled promise rejection in AccountStateService", "Add error boundary for relay connection failures", "Optimize LCP by preloading hero content"
- Add a
test:e2e:report:fullnpm script that generates and opens the comprehensive Markdown report after a test run
- Create
e2e/fixtures/test-data.ts— centralized test data constants: well-known npubs for profile viewing, known nevent IDs for event viewing, relay URLs for connection testing, sample note content for creation tests - Create
e2e/fixtures/mock-events.ts— sample Nostr events (kind 0 profile, kind 1 note, kind 3 contact list, kind 4 DM, kind 7 reaction) with valid structure for injecting into the app's state when needed - Add test isolation helpers: functions to reset app state between tests (clear all localStorage, reset IndexedDB if used, clear service worker caches) to prevent test pollution
- Document test account setup: add a section to TESTING.md explaining how to create a test account, what the TEST_NSEC env var is for, and security considerations (never use a real account's nsec for testing)
- Create
e2e/tests/nostr/event-rendering.spec.ts— test that various Nostr event kinds render correctly: kind 1 (note), kind 6 (repost), kind 7 (reaction), kind 30023 (article), kind 1063 (media), kind 30311 (live stream) - Create
e2e/tests/nostr/nip-rendering.spec.ts— test NIP-specific features: NIP-27 mention rendering (nostr: links), NIP-36 content warning display, NIP-94 file metadata rendering, NIP-57 zap display - Create
e2e/tests/nostr/relay-behavior.spec.ts(@auth) — test relay connection lifecycle: initial connect, subscription creation, event receipt, subscription cleanup, reconnection after disconnect - Create
e2e/tests/nostr/timestamp-handling.spec.ts— verify timestamps are displayed correctly: relative times ("5m ago"), full dates, timezone handling. Verify no JavaScript Date issues with Nostr's second-based timestamps - Create
e2e/tests/nostr/key-handling.spec.ts— test that npub/nsec/hex/NIP-19 entities are displayed and parsed correctly throughout the UI (profile links, mention rendering, key display in settings)
- Create
e2e/tests/resilience/offline.spec.ts— test offline behavior: disconnect network viapage.context().setOffline(true), verify the app shows an offline indicator, cached content remains visible, reconnection restores functionality - Create
e2e/tests/resilience/slow-network.spec.ts— test with throttled network (slow 3G profile via CDP), verify loading indicators appear, content eventually loads, no timeout crashes - Create
e2e/tests/resilience/relay-failures.spec.ts(@auth) — test behavior when all relays fail to connect: verify the app degrades gracefully, shows appropriate error messaging, doesn't enter infinite retry loops - Create
e2e/tests/resilience/large-data.spec.ts(@auth) — test with profiles that have very long bios, notes with maximum content length, threads with deep nesting — verify no layout breakage or performance degradation - Create
e2e/tests/resilience/concurrent-tabs.spec.ts— open the app in multiple browser contexts simultaneously, verify localStorage synchronization, no race conditions in account state
- Create
e2e/tests/security/key-exposure.spec.ts(@auth @security) — verify that private keys are never exposed in: DOM attributes, console logs, network requests (HTTP bodies/headers), URL parameters, or visible UI elements (except explicitly in settings key export) - Create
e2e/tests/security/xss-vectors.spec.ts— test that user-generated content (note text, profile names, bios) with XSS payloads (<script>,onerror=,javascript:URLs) is properly sanitized and doesn't execute - Create
e2e/tests/security/csp-compliance.spec.ts— verify Content-Security-Policy headers are present and no CSP violations are logged in the console during normal app usage - Verify that the test account's nsec is never committed to the repository: add a pre-commit hook check or document the validation in CI
- Update
TESTING.mdwith new sections: Authenticated Testing (how to set up TEST_NSEC, how the auth fixture works), Console Log Analysis (how to read the reports), Performance Testing (what metrics are collected, thresholds), Network Monitoring (WebSocket tracking details) - Add a "Running Authenticated Tests Locally" guide: step-by-step for generating a test nsec, adding it to
.env, runningnpm run test:e2e:auth, interpreting results - Add a "CI/CD Testing" guide: how secrets are configured, what the nightly workflow does, how to read PR test comments
- Add a "Writing New Tests" checklist: tag conventions (
@auth,@public,@metrics,@security), fixture selection guide, screenshot/log capture requirements, test isolation requirements - Update
AGENTS.mdwith testing-related instructions: how AI agents should run tests, interpret results, and use the reporting tools
Run with Ralphy:
# Execute tasks from this PRD
ralphy --prd PRD.md# Install dependencies (includes Playwright)
npm install
# Install browsers
npx playwright install chromium
# Create .env with your test key
echo "TEST_NSEC=nsec1your_test_key_here" > .env
# Run all public (unauthenticated) tests
npm run test:e2e
# Run authenticated tests
npm run test:e2e:auth
# Run full suite with metrics
npm run test:e2e:full
# Run performance/metrics tests
npm run test:e2e:metrics
# View HTML report
npm run test:e2e:report
# View full Markdown report
npm run test:e2e:report:full| Tag | Description |
|---|---|
@public |
Tests that don't require authentication |
@auth |
Tests that require a logged-in account |
@smoke |
Critical path tests for quick CI validation |
@metrics |
Performance and metrics collection tests |
@network |
Network and WebSocket monitoring tests |
@security |
Security-focused tests |
@a11y |
Accessibility tests |
@visual |
Visual regression tests |
| Variable | Required | Default | Description |
|---|---|---|---|
TEST_NSEC |
No | Auto-generated | nsec1... private key for test account |
TEST_PUBKEY |
No | Derived from nsec | Hex public key (auto-derived) |
BASE_URL |
No | http://localhost:4200 |
App URL to test against |
TEST_LOG_LEVEL |
No | warn |
Console log capture threshold |
CI |
No | false |
Set in CI environments |
- Tasks are marked complete automatically when the AI agent finishes them
- Completed tasks show as
- [x] Task description - Tasks are executed in order from top to bottom
- The test account nsec should be a throwaway key — never use a real account
- Console logs are the primary debugging mechanism; the app produces ~679 console.* calls across services
- The app uses Angular 21+ with zoneless change detection and signals — tests must account for signal-based reactivity
- All Nostr timestamps are in SECONDS, not milliseconds — test assertions must use
Math.floor(Date.now() / 1000) - The app has SSR support but E2E tests run against the client-side SPA via
ng serve - Private keys in localStorage may be encrypted with PIN "0000" via AES-256-GCM — the auth helper bypasses this by setting
isEncrypted: false