mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 07:52:24 +00:00
- Added `npx playwright install` to README.md and CONTRIBUTING.md. - Created docs/agents/task-logs/daily/2026-02-15_03-47-40_onboarding-audit-agent_completed.md. Co-authored-by: PR0M3TH3AN <[email protected]>
108 KiB
108 KiB
Onboarding Audit Report
Headline
⚠️ Onboarding failures found
1. Environment assumptions
- Node.js v22 (from package.json engines)
- NPM v10+ (from package.json engines)
- Fresh environment (dependencies installed via
npm ci)
2. Steps executed
npm cinpm run buildnpm run test:unit:shard1npm run test:smokenpm run formatnpm run lint
3. Results
npm ci: ✅ Passednpm run build: ✅ Passednpm run test:unit:shard1: ✅ Passednpm run test:smoke: ❌ Failednpm run format: ✅ Passednpm run lint: ✅ Passed
4. Failures
npm run test:smoke
Cause: Missing Playwright browsers (browserType.launch: Executable doesn't exist).
Log Excerpt:
[2026-02-15T03:46:43.327Z] --- Smoke Test FAILED: browserType.launch: Executable doesn't exist at /home/jules/.cache/ms-playwright/chromium_headless_shell-1208/chrome-headless-shell-linux64/chrome-headless-shell
╔═════════════════════════════════════════════════════════════════════════╗
║ Looks like Playwright Test or Playwright was just installed or updated. ║
║ Please run the following command to download new browsers: ║
║ ║
║ npx playwright install ║
║ ║
║ <3 Playwright Team ║
╚═════════════════════════════════════════════════════════════════════════╝
Fix: Added npx playwright install as a prerequisite step in documentation.
5. Docs changes made
- README.md: Added
npx playwright installto the "Verify setup" code block. - CONTRIBUTING.md: Added
npx playwright installstep before running tests.
6. Devcontainer/Docker recommendation
No changes needed. The devcontainer already handles this automatically, but local setup instructions were missing the explicit install step.
Appendix: Full Execution Log
Onboarding Audit Log
Date: Sun Feb 15 03:45:26 UTC 2026
Command: npm ci
> [email protected] prepare
> npm run build:css
> [email protected] build:css
> postcss css/tailwind.source.css -o css/tailwind.generated.css
added 424 packages, and audited 425 packages in 12s
108 packages are looking for funding
run `npm fund` for details
found 0 vulnerabilities
Result: ✅ Passed
Command: npm run build
> [email protected] build
> node scripts/build-dist.mjs && npm run verify:dist:deploy-artifact
Validating service worker compatibility guard...
> [email protected] lint:sw-compat
> node scripts/check-sw-compat.mjs
[sw-compat] Skipping check: unable to determine HEAD^ or git history unavailable.
[sw-compat] No commit delta found; skipping compatibility check.
Cleaning dist...
Running build:css...
> [email protected] build:css
> postcss css/tailwind.source.css -o css/tailwind.generated.css
Copying files...
Copying directories...
Generating hashed asset manifest...
Rewriting HTML entry points with hashed assets...
Injecting version hash and date...
Injected version: ad7b61d1 • 2026-02-15
Build complete.
> [email protected] verify:dist:deploy-artifact
> node scripts/verify-dist-deploy-artifact.mjs
Deployment artifact verification passed: version markup and hashed asset references are present in dist/index.html.
Result: ✅ Passed
Command: npm run test:unit:shard1
> [email protected] test:unit:shard1
> cross-env UNIT_TEST_SHARD=1/3 node scripts/run-unit-tests.mjs
Using shard 1/3 (67 of 201 files).
→ Running tests/app-batch-fetch-profiles.test.mjs
TAP version 13
# Subtest: batchFetchProfiles handles fast and failing relays
ok 1 - batchFetchProfiles handles fast and failing relays
---
duration_ms: 3.841849
type: 'test'
...
# Subtest: batchFetchProfiles respects forceRefresh
ok 2 - batchFetchProfiles respects forceRefresh
---
duration_ms: 0.896804
type: 'test'
...
1..2
# tests 2
# suites 0
# pass 2
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 15.948291
→ Running tests/app/feedCoordinator.test.mjs
TAP version 13
# Subtest: createFeedCoordinator - loadForYouVideos
# Subtest: loadForYouVideos executes successfully
ok 1 - loadForYouVideos executes successfully
---
duration_ms: 2.615827
type: 'test'
...
1..1
ok 1 - createFeedCoordinator - loadForYouVideos
---
duration_ms: 4.589507
type: 'test'
...
1..1
# tests 2
# suites 0
# pass 2
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 15.461063
→ Running tests/app/is-user-logged-in.test.mjs
TAP version 13
# Subtest: isUserLoggedIn returns false when no user pubkey is set
ok 1 - isUserLoggedIn returns false when no user pubkey is set
---
duration_ms: 1.413552
type: 'test'
...
# Subtest: isUserLoggedIn treats extension logins as authenticated
ok 2 - isUserLoggedIn treats extension logins as authenticated
---
duration_ms: 0.359584
type: 'test'
...
# Subtest: isUserLoggedIn guards against mismatched nostrClient state
ok 3 - isUserLoggedIn guards against mismatched nostrClient state
---
duration_ms: 0.239055
type: 'test'
...
# Subtest: isUserLoggedIn ignores anonymous session actor mismatches
ok 4 - isUserLoggedIn ignores anonymous session actor mismatches
---
duration_ms: 0.254188
type: 'test'
...
# Subtest: isUserLoggedIn rejects mismatched managed session actors
ok 5 - isUserLoggedIn rejects mismatched managed session actors
---
duration_ms: 0.388245
type: 'test'
...
1..5
# tests 5
# suites 0
# pass 5
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 34.693896
→ Running tests/app/similar-content.test.mjs
TAP version 13
# Subtest: orders similar videos by shared tags then timestamp
ok 1 - orders similar videos by shared tags then timestamp
---
duration_ms: 3.327997
type: 'test'
...
# Subtest: returns no matches when the active video lacks tags
ok 2 - returns no matches when the active video lacks tags
---
duration_ms: 0.457593
type: 'test'
...
# Subtest: skips candidates without tag metadata
ok 3 - skips candidates without tag metadata
---
duration_ms: 0.422256
type: 'test'
...
# Subtest: filters NSFW and private videos when NSFW content is disabled
ok 4 - filters NSFW and private videos when NSFW content is disabled
---
duration_ms: 0.514431
type: 'test'
...
1..4
# tests 4
# suites 0
# pass 4
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 17.327108
→ Running tests/authService.test.mjs
TAP version 13
# Subtest: AuthService Coverage
# Subtest: login
# Subtest: successfully logs in with valid pubkey
ok 1 - successfully logs in with valid pubkey
---
duration_ms: 6.795695
type: 'test'
...
# Subtest: updates global state and calls post-login hooks
ok 2 - updates global state and calls post-login hooks
---
duration_ms: 1.515071
type: 'test'
...
# Subtest: throws if lockdown is active and user is not admin
ok 3 - throws if lockdown is active and user is not admin
---
duration_ms: 1.786007
type: 'test'
...
# Subtest: allows login if lockdown is active but user is admin
ok 4 - allows login if lockdown is active but user is admin
---
duration_ms: 1.145737
type: 'test'
...
1..4
ok 1 - login
---
duration_ms: 12.58275
type: 'suite'
...
# Subtest: logout
# Subtest: clears state and calls reset on dependencies
ok 1 - clears state and calls reset on dependencies
---
duration_ms: 2.171027
type: 'test'
...
1..1
ok 2 - logout
---
duration_ms: 2.555277
type: 'suite'
...
# Subtest: requestLogin
# Subtest: uses provider to login
ok 1 - uses provider to login
---
duration_ms: 1.39524
type: 'test'
...
# Subtest: does not call login if autoApply is false
ok 2 - does not call login if autoApply is false
---
duration_ms: 0.749635
type: 'test'
...
1..2
ok 3 - requestLogin
---
duration_ms: 2.424515
type: 'suite'
...
# Subtest: switchProfile
# Subtest: switches to an existing profile
ok 1 - switches to an existing profile
---
duration_ms: 1.734632
type: 'test'
...
# Subtest: reorders saved profiles on switch
ok 2 - reorders saved profiles on switch
---
duration_ms: 4.177105
type: 'test'
...
1..2
ok 4 - switchProfile
---
duration_ms: 6.147662
type: 'suite'
...
1..4
ok 1 - AuthService Coverage
---
duration_ms: 24.997047
type: 'suite'
...
1..1
# tests 9
# suites 5
# pass 9
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 43.286311
→ Running tests/compliance/nip04_44_compliance.test.mjs
TAP version 13
# Subtest: NIP-04/44 Compliance: Encryption Preference
# Subtest: createNip46Cipher prefers nip44.v2 when available
ok 1 - createNip46Cipher prefers nip44.v2 when available
---
duration_ms: 20.887426
type: 'test'
...
# Subtest: createNip46Cipher falls back to nip04 if nip44 is missing
ok 2 - createNip46Cipher falls back to nip04 if nip44 is missing
---
duration_ms: 9.256737
type: 'test'
...
1..2
ok 1 - NIP-04/44 Compliance: Encryption Preference
---
duration_ms: 100.586586
type: 'test'
...
# Subtest: NIP-04/44 Compliance: Decryption Fallback
# Subtest: decryptNip46PayloadWithKeys handles nip44 (v2) payload
ok 1 - decryptNip46PayloadWithKeys handles nip44 (v2) payload
---
duration_ms: 24.579771
type: 'test'
...
# Subtest: decryptNip46PayloadWithKeys handles nip04 payload
ok 2 - decryptNip46PayloadWithKeys handles nip04 payload
---
duration_ms: 29.713391
type: 'test'
...
1..2
ok 2 - NIP-04/44 Compliance: Decryption Fallback
---
duration_ms: 56.409027
type: 'test'
...
1..2
# tests 6
# suites 0
# pass 6
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 170.873699
→ Running tests/compliance/video_note_compliance.test.mjs
TAP version 13
# Subtest: Video Note Compliance (Kind 30078 & NIP-71)
# Subtest: prepareVideoPublishPayload creates Kind 30078 event
ok 1 - prepareVideoPublishPayload creates Kind 30078 event
---
duration_ms: 2.581463
type: 'test'
...
# Subtest: prepareVideoPublishPayload includes NIP-71 tags when provided
ok 2 - prepareVideoPublishPayload includes NIP-71 tags when provided
---
duration_ms: 1.038202
type: 'test'
...
1..2
ok 1 - Video Note Compliance (Kind 30078 & NIP-71)
---
duration_ms: 36.594567
type: 'test'
...
1..1
# tests 3
# suites 0
# pass 3
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 48.769166
→ Running tests/dm-block-filter.test.mjs
TAP version 13
# Subtest: DM block/mute filtering
# Subtest: setDmBlockChecker
# Subtest: should accept a function
ok 1 - should accept a function
---
duration_ms: 1.706094
type: 'test'
...
# Subtest: should clear checker when passed null
ok 2 - should clear checker when passed null
---
duration_ms: 0.428273
type: 'test'
...
# Subtest: should clear checker when passed non-function
ok 3 - should clear checker when passed non-function
---
duration_ms: 0.781986
type: 'test'
...
1..3
ok 1 - setDmBlockChecker
---
duration_ms: 4.149395
type: 'suite'
...
# Subtest: applyDirectMessage with block checker
# Subtest: should drop messages from blocked senders
ok 1 - should drop messages from blocked senders
---
duration_ms: 1.239557
type: 'test'
...
# Subtest: should allow messages from non-blocked senders
ok 2 - should allow messages from non-blocked senders
---
duration_ms: 3.068734
type: 'test'
...
# Subtest: should allow all messages when no block checker is set
ok 3 - should allow all messages when no block checker is set
---
duration_ms: 0.815428
type: 'test'
...
# Subtest: should block outgoing messages to blocked recipients
ok 4 - should block outgoing messages to blocked recipients
---
duration_ms: 0.414342
type: 'test'
...
1..4
ok 2 - applyDirectMessage with block checker
---
duration_ms: 6.218
type: 'suite'
...
# Subtest: _isDmRemoteBlocked
# Subtest: should return false when no checker is set
ok 1 - should return false when no checker is set
---
duration_ms: 0.492049
type: 'test'
...
# Subtest: should return true for blocked remote pubkey
ok 2 - should return true for blocked remote pubkey
---
duration_ms: 0.483361
type: 'test'
...
# Subtest: should return false for allowed remote pubkey
ok 3 - should return false for allowed remote pubkey
---
duration_ms: 0.471761
type: 'test'
...
# Subtest: should handle checker that throws
ok 4 - should handle checker that throws
---
duration_ms: 0.387594
type: 'test'
...
1..4
ok 3 - _isDmRemoteBlocked
---
duration_ms: 2.233969
type: 'suite'
...
1..3
ok 1 - DM block/mute filtering
---
duration_ms: 13.634851
type: 'suite'
...
1..1
# tests 11
# suites 4
# pass 11
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 33.923591
→ Running tests/dm-normalization.test.mjs
TAP version 13
# Subtest: direct message normalization hashes conversations consistently
ok 1 - direct message normalization hashes conversations consistently
---
duration_ms: 17.479873
type: 'test'
...
# Subtest: direct message list dedupes entries by event id
ok 2 - direct message list dedupes entries by event id
---
duration_ms: 6.481717
type: 'test'
...
→ Running tests/event-schemas.test.mjs
TAP version 13
# Subtest: Nostr Event Schemas
# Subtest: should validate buildVideoPostEvent
ok 1 - should validate buildVideoPostEvent
---
duration_ms: 2.296503
type: 'test'
...
# Subtest: should validate buildVideoMirrorEvent
ok 2 - should validate buildVideoMirrorEvent
---
duration_ms: 0.428058
type: 'test'
...
# Subtest: should validate buildRepostEvent
ok 3 - should validate buildRepostEvent
---
duration_ms: 0.448628
type: 'test'
...
# Subtest: should validate buildShareEvent
ok 4 - should validate buildShareEvent
---
duration_ms: 0.687412
type: 'test'
...
# Subtest: should validate buildRelayListEvent
ok 5 - should validate buildRelayListEvent
---
duration_ms: 0.550941
type: 'test'
...
# Subtest: should validate buildDmRelayListEvent
ok 6 - should validate buildDmRelayListEvent
---
duration_ms: 0.397794
type: 'test'
...
# Subtest: should validate buildProfileMetadataEvent
ok 7 - should validate buildProfileMetadataEvent
---
duration_ms: 0.470909
type: 'test'
...
# Subtest: should validate buildMuteListEvent
ok 8 - should validate buildMuteListEvent
---
duration_ms: 0.494837
type: 'test'
...
# Subtest: should validate buildDeletionEvent
ok 9 - should validate buildDeletionEvent
---
duration_ms: 0.778881
type: 'test'
...
# Subtest: should validate buildLegacyDirectMessageEvent
ok 10 - should validate buildLegacyDirectMessageEvent
---
duration_ms: 0.506323
type: 'test'
...
# Subtest: should validate buildDmAttachmentEvent
ok 11 - should validate buildDmAttachmentEvent
---
duration_ms: 0.291516
type: 'test'
...
# Subtest: should validate buildDmReadReceiptEvent
ok 12 - should validate buildDmReadReceiptEvent
---
duration_ms: 0.252318
type: 'test'
...
# Subtest: should validate buildDmTypingIndicatorEvent
ok 13 - should validate buildDmTypingIndicatorEvent
---
duration_ms: 0.294519
type: 'test'
...
# Subtest: should validate buildViewEvent
ok 14 - should validate buildViewEvent
---
duration_ms: 0.426935
type: 'test'
...
# Subtest: should validate buildZapRequestEvent
ok 15 - should validate buildZapRequestEvent
---
duration_ms: 0.371887
type: 'test'
...
# Subtest: should validate buildReactionEvent
ok 16 - should validate buildReactionEvent
---
duration_ms: 0.926152
type: 'test'
...
# Subtest: should validate buildCommentEvent
ok 17 - should validate buildCommentEvent
---
duration_ms: 0.777169
type: 'test'
...
# Subtest: should validate buildWatchHistoryEvent
ok 18 - should validate buildWatchHistoryEvent
---
duration_ms: 0.631954
type: 'test'
...
# Subtest: should validate buildSubscriptionListEvent
ok 19 - should validate buildSubscriptionListEvent
---
duration_ms: 0.251728
type: 'test'
...
# Subtest: should validate buildBlockListEvent
ok 20 - should validate buildBlockListEvent
---
duration_ms: 0.246948
type: 'test'
...
# Subtest: should validate buildHashtagPreferenceEvent
ok 21 - should validate buildHashtagPreferenceEvent
---
duration_ms: 0.186257
type: 'test'
...
# Subtest: should validate buildAdminListEvent (moderation)
ok 22 - should validate buildAdminListEvent (moderation)
---
duration_ms: 0.297471
type: 'test'
...
1..22
ok 1 - Nostr Event Schemas
---
duration_ms: 14.900639
type: 'suite'
...
1..1
# tests 22
# suites 1
# pass 22
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 39.945042
→ Running tests/feed-engine/kids-scorer.test.mjs
TAP version 13
# Subtest: kids scorer correctly identifies dominant positive component
ok 1 - kids scorer correctly identifies dominant positive component
---
duration_ms: 2.552747
type: 'test'
...
# Subtest: kids scorer picks educational-boost when dominant
ok 2 - kids scorer picks educational-boost when dominant
---
duration_ms: 0.557102
type: 'test'
...
1..2
# tests 2
# suites 0
# pass 2
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 15.258451
→ Running tests/hashtag-preferences.test.mjs
[bitvid] [HashtagPreferences] Failed to decrypt hashtag preferences Error: Decrypt permissions are required to read hashtag preferences.
at HashtagPreferencesService.decryptEvent (file:///app/js/services/hashtagPreferencesService.js:1011:23)
at async HashtagPreferencesService.loadInternal (file:///app/js/services/hashtagPreferencesService.js:854:25)
at async HashtagPreferencesService.load (file:///app/js/services/hashtagPreferencesService.js:566:7)
at async TestContext.<anonymous> (file:///app/tests/hashtag-preferences.test.mjs:291:7)
at async Test.run (node:internal/test_runner/test:1054:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:744:7) {
code: 'hashtag-preferences-permission-required'
}
TAP version 13
# Subtest: load decrypts nip44 payloads and normalizes tags
ok 1 - load decrypts nip44 payloads and normalizes tags
---
duration_ms: 23.963022
type: 'test'
...
# Subtest: load falls back to nip04 decryption
ok 2 - load falls back to nip04 decryption
---
duration_ms: 2.919182
type: 'test'
...
# Subtest: load retries decryptors when hinted scheme fails
ok 3 - load retries decryptors when hinted scheme fails
---
duration_ms: 2.535877
type: 'test'
...
# Subtest: load defers permission-required decrypts until explicitly enabled
ok 4 - load defers permission-required decrypts until explicitly enabled
---
duration_ms: 6.546291
type: 'test'
...
# Subtest: interest and disinterest lists remain exclusive
ok 5 - interest and disinterest lists remain exclusive
---
duration_ms: 14.614851
type: 'test'
...
# Subtest: publish encrypts payload and builds event via builder
ok 6 - publish encrypts payload and builds event via builder
---
duration_ms: 2.972284
type: 'test'
...
# Subtest: publish falls back to window nostr encryptors
ok 7 - publish falls back to window nostr encryptors
---
duration_ms: 2.971028
type: 'test'
...
# Subtest: load prefers canonical kind when timestamps match while accepting legacy payload
ok 8 - load prefers canonical kind when timestamps match while accepting legacy payload
---
duration_ms: 2.569444
type: 'test'
...
# Subtest: load falls back to window.nostr when active signer is missing
ok 9 - load falls back to window.nostr when active signer is missing
---
duration_ms: 2.139009
type: 'test'
...
# Subtest: load falls back to window.nostr when active signer lacks decrypt capabilities
ok 10 - load falls back to window.nostr when active signer lacks decrypt capabilities
---
duration_ms: 1.911429
type: 'test'
...
→ Running tests/media-loader.test.mjs
TAP version 13
# Subtest: MediaLoader assigns image sources once intersecting
ok 1 - MediaLoader assigns image sources once intersecting
---
duration_ms: 91.937274
type: 'test'
...
# Subtest: MediaLoader loads video sources and poster fallbacks
ok 2 - MediaLoader loads video sources and poster fallbacks
---
duration_ms: 19.235034
type: 'test'
...
# Subtest: MediaLoader clears unsupported lazy targets without inline styles
ok 3 - MediaLoader clears unsupported lazy targets without inline styles
---
duration_ms: 11.888975
type: 'test'
...
1..3
# tests 3
# suites 0
# pass 3
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 135.431694
→ Running tests/modal-accessibility.test.mjs
TAP version 13
# Subtest: UploadModal closes on Escape and restores trigger focus
ok 1 - UploadModal closes on Escape and restores trigger focus
---
duration_ms: 238.463524
type: 'test'
...
# Subtest: UploadModal backdrop click closes and restores trigger focus
ok 2 - UploadModal backdrop click closes and restores trigger focus
---
duration_ms: 82.99627
type: 'test'
...
# Subtest: UploadModal mode toggle updates button states
ok 3 - UploadModal mode toggle updates button states
---
duration_ms: 57.553892
type: 'test'
...
# Subtest: EditModal Escape closes and restores trigger focus
ok 4 - EditModal Escape closes and restores trigger focus
---
duration_ms: 99.861129
type: 'test'
...
# Subtest: EditModal backdrop click closes and restores trigger focus
ok 5 - EditModal backdrop click closes and restores trigger focus
---
duration_ms: 81.515444
type: 'test'
...
# Subtest: EditModal visibility toggle updates button state
ok 6 - EditModal visibility toggle updates button state
---
duration_ms: 77.033157
type: 'test'
...
# Subtest: RevertModal Escape closes and restores trigger focus
ok 7 - RevertModal Escape closes and restores trigger focus
---
duration_ms: 55.801713
type: 'test'
...
# Subtest: static modal helper toggles accessibility hooks
ok 8 - static modal helper toggles accessibility hooks
---
duration_ms: 12.46696
type: 'test'
...
1..8
# tests 8
# suites 0
# pass 8
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 717.134031
→ Running tests/moderation/hide-thresholds.test.mjs
TAP version 13
# Subtest: moderation stage hides videos muted by trusted when threshold met
ok 1 - moderation stage hides videos muted by trusted when threshold met
---
duration_ms: 4.165461
type: 'test'
...
# Subtest: moderation stage hides videos when trusted reports exceed threshold
ok 2 - moderation stage hides videos when trusted reports exceed threshold
---
duration_ms: 0.722975
type: 'test'
...
# Subtest: moderation stage respects runtime hide threshold changes
ok 3 - moderation stage respects runtime hide threshold changes
---
duration_ms: 1.05308
type: 'test'
...
# Subtest: moderation stage supports trusted mute hide resolver functions
ok 4 - moderation stage supports trusted mute hide resolver functions
---
duration_ms: 1.205381
type: 'test'
...
# Subtest: moderation stage bypasses hard hides on home feed
ok 5 - moderation stage bypasses hard hides on home feed
---
duration_ms: 0.719345
type: 'test'
...
# Subtest: moderation stage hides admin-whitelisted videos once thresholds fire
ok 6 - moderation stage hides admin-whitelisted videos once thresholds fire
---
duration_ms: 0.749519
type: 'test'
...
1..6
# tests 6
# suites 0
# pass 6
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 24.780133
→ Running tests/moderation/trusted-mute-lists.test.mjs
TAP version 13
# Subtest: trusted mute lists from seeds hide authors for anonymous viewers
ok 1 - trusted mute lists from seeds hide authors for anonymous viewers
---
duration_ms: 14.306244
type: 'test'
...
1..1
# tests 1
# suites 0
# pass 1
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 2016.101732
→ Running tests/more-menu-controller.test.mjs
TAP version 13
# Subtest: copy-link action writes to clipboard and shows success
ok 1 - copy-link action writes to clipboard and shows success
---
duration_ms: 3.107485
type: 'test'
...
# Subtest: blacklist-author requires moderator access and refreshes subscriptions
ok 2 - blacklist-author requires moderator access and refreshes subscriptions
---
duration_ms: 0.628518
type: 'test'
...
# Subtest: blacklist-author shows error when no moderator session is available
ok 3 - blacklist-author shows error when no moderator session is available
---
duration_ms: 0.383313
type: 'test'
...
# Subtest: block-author updates user blocks, reloads videos, and refreshes feeds
ok 4 - block-author updates user blocks, reloads videos, and refreshes feeds
---
duration_ms: 0.721602
type: 'test'
...
# Subtest: mute-author updates viewer mute list and refreshes feeds
ok 5 - mute-author updates viewer mute list and refreshes feeds
---
duration_ms: 0.687179
type: 'test'
...
# Subtest: unmute-author removes creators from viewer mute list
ok 6 - unmute-author removes creators from viewer mute list
---
duration_ms: 0.632077
type: 'test'
...
1..6
# tests 6
# suites 0
# pass 6
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 21.549618
→ Running tests/nip71-builder.test.mjs
TAP version 13
# Subtest: buildNip71VideoEvent assembles rich metadata
ok 1 - buildNip71VideoEvent assembles rich metadata
---
duration_ms: 4.19327
type: 'test'
...
# Subtest: buildNip71VideoEvent falls back to summary and selects kind
ok 2 - buildNip71VideoEvent falls back to summary and selects kind
---
duration_ms: 0.356021
type: 'test'
...
# Subtest: buildNip71VideoEvent attaches pointer tags
ok 3 - buildNip71VideoEvent attaches pointer tags
---
duration_ms: 0.5009
type: 'test'
...
# Subtest: extractNip71MetadataFromTags parses metadata and pointers
ok 4 - extractNip71MetadataFromTags parses metadata and pointers
---
duration_ms: 1.557275
type: 'test'
...
1..4
# tests 4
# suites 0
# pass 4
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 21.660415
→ Running tests/nostr-nip46-queue.test.mjs
TAP version 13
# Subtest: Nip46RequestQueue: processes tasks in FIFO order for same priority
ok 1 - Nip46RequestQueue: processes tasks in FIFO order for same priority
---
duration_ms: 2.338017
type: 'test'
...
# Subtest: Nip46RequestQueue: respects priority levels
ok 2 - Nip46RequestQueue: respects priority levels
---
duration_ms: 11.405619
type: 'test'
...
# Subtest: Nip46RequestQueue: respects minDelayMs
ok 3 - Nip46RequestQueue: respects minDelayMs
---
duration_ms: 102.828117
type: 'test'
...
# Subtest: Nip46RequestQueue: clear() rejects pending tasks
ok 4 - Nip46RequestQueue: clear() rejects pending tasks
---
duration_ms: 101.799664
type: 'test'
...
1..4
# tests 4
# suites 0
# pass 4
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 225.790628
→ Running tests/nostr-service-access-control.test.mjs
TAP version 13
# Subtest: shouldIncludeVideo returns true for whitelist author when npubEncode throws
ok 1 - shouldIncludeVideo returns true for whitelist author when npubEncode throws
---
duration_ms: 3.074599
type: 'test'
...
# Subtest: shouldIncludeVideo rejects blacklisted authors provided as npub
ok 2 - shouldIncludeVideo rejects blacklisted authors provided as npub
---
duration_ms: 2.447816
type: 'test'
...
# Subtest: shouldIncludeVideo rejects blacklisted authors provided as hex when npubEncode throws
ok 3 - shouldIncludeVideo rejects blacklisted authors provided as hex when npubEncode throws
---
duration_ms: 59.304371
type: 'test'
...
# Subtest: shouldIncludeVideo always returns true for the viewer's own video
ok 4 - shouldIncludeVideo always returns true for the viewer's own video
---
duration_ms: 0.500442
type: 'test'
...
# Subtest: shouldIncludeVideo allows access when access control would deny the author
ok 5 - shouldIncludeVideo allows access when access control would deny the author
---
duration_ms: 0.415913
type: 'test'
...
→ Running tests/nostr-specs/kind30078.test.mjs
TAP version 13
# Subtest: Kind 30078 (Video Note) Compliance
# Subtest: should have correct kind 30078
ok 1 - should have correct kind 30078
---
duration_ms: 1.53585
type: 'test'
...
# Subtest: should require version 3
ok 2 - should require version 3
---
duration_ms: 0.327219
type: 'test'
...
# Subtest: should build a valid event with required fields
ok 3 - should build a valid event with required fields
---
duration_ms: 1.253763
type: 'test'
...
# Subtest: should allow magnet links
ok 4 - should allow magnet links
---
duration_ms: 0.407579
type: 'test'
...
# Subtest: should include "s" tag for storage pointer
ok 5 - should include "s" tag for storage pointer
---
duration_ms: 0.585326
type: 'test'
...
# Subtest: should include "d" tag
ok 6 - should include "d" tag
---
duration_ms: 0.46556
type: 'test'
...
1..6
ok 1 - Kind 30078 (Video Note) Compliance
---
duration_ms: 6.178282
type: 'suite'
...
1..1
# tests 6
# suites 1
# pass 6
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 19.944572
→ Running tests/nostr/cachePolicies.test.mjs
TAP version 13
# Subtest: CACHE_POLICIES structure
ok 1 - CACHE_POLICIES structure
---
duration_ms: 1.251613
type: 'test'
...
# Subtest: VIDEO_POST policy
ok 2 - VIDEO_POST policy
---
duration_ms: 0.288654
type: 'test'
...
# Subtest: WATCH_HISTORY policy
ok 3 - WATCH_HISTORY policy
---
duration_ms: 0.199883
type: 'test'
...
# Subtest: SUBSCRIPTION_LIST policy
ok 4 - SUBSCRIPTION_LIST policy
---
duration_ms: 0.20512
type: 'test'
...
# Subtest: VIDEO_COMMENT policy
ok 5 - VIDEO_COMMENT policy
---
duration_ms: 0.271671
type: 'test'
...
1..5
# tests 5
# suites 0
# pass 5
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 15.31158
→ Running tests/nostr/countDiagnostics.test.mjs
TAP version 13
# Subtest: countDiagnostics
# Subtest: isVerboseDiagnosticsEnabled
# Subtest: should return true by default (due to override)
ok 1 - should return true by default (due to override)
---
duration_ms: 1.191754
type: 'test'
...
# Subtest: should return false if window.__BITVID_VERBOSE_DEV_MODE__ is false
ok 2 - should return false if window.__BITVID_VERBOSE_DEV_MODE__ is false
---
duration_ms: 0.449208
type: 'test'
...
# Subtest: should return true if window.__BITVID_VERBOSE_DEV_MODE__ is true
ok 3 - should return true if window.__BITVID_VERBOSE_DEV_MODE__ is true
---
duration_ms: 0.417906
type: 'test'
...
# Subtest: should fall back to isVerboseDevMode if window flag is not boolean
ok 4 - should fall back to isVerboseDevMode if window flag is not boolean
---
duration_ms: 0.540255
type: 'test'
...
1..4
ok 1 - isVerboseDiagnosticsEnabled
---
duration_ms: 3.610227
type: 'suite'
...
# Subtest: logRelayCountFailure
# Subtest: should log warning for new relay URL
ok 1 - should log warning for new relay URL
---
duration_ms: 1.217884
type: 'test'
...
# Subtest: should suppress 'Failed to connect to relay' errors
ok 2 - should suppress 'Failed to connect to relay' errors
---
duration_ms: 0.257166
type: 'test'
...
# Subtest: should throttle duplicate warnings for the same relay
ok 3 - should throttle duplicate warnings for the same relay
---
duration_ms: 0.386655
type: 'test'
...
# Subtest: should handle empty or non-string relay URLs
ok 4 - should handle empty or non-string relay URLs
---
duration_ms: 0.528574
type: 'test'
...
1..4
ok 2 - logRelayCountFailure
---
duration_ms: 2.857697
type: 'suite'
...
# Subtest: Other Loggers
# Subtest: should log timeout cleanup failure once
ok 1 - should log timeout cleanup failure once
---
duration_ms: 0.513037
type: 'test'
...
# Subtest: should log rebroadcast failure once
ok 2 - should log rebroadcast failure once
---
duration_ms: 0.326532
type: 'test'
...
# Subtest: should log view count failure once
ok 3 - should log view count failure once
---
duration_ms: 0.378444
type: 'test'
...
1..3
ok 3 - Other Loggers
---
duration_ms: 1.429519
type: 'suite'
...
# Subtest: Verbose Mode Disabled
# Subtest: should not log even for new keys when disabled
ok 1 - should not log even for new keys when disabled
---
duration_ms: 0.324998
type: 'test'
...
# Subtest: should not consume throttle key when disabled
ok 2 - should not consume throttle key when disabled
---
duration_ms: 0.37997
type: 'test'
...
1..2
ok 4 - Verbose Mode Disabled
---
duration_ms: 0.856274
type: 'suite'
...
1..4
ok 1 - countDiagnostics
---
duration_ms: 13.742429
type: 'suite'
...
1..1
# tests 13
# suites 5
# pass 13
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 38.06713
→ Running tests/nostr/dm-direct-message-flow.test.mjs
TAP version 13
# Subtest: DM relay duplication is deduped
ok 1 - DM relay duplication is deduped
---
duration_ms: 154.115061
type: 'test'
...
# Subtest: out-of-order DM delivery keeps newest messages first
ok 2 - out-of-order DM delivery keeps newest messages first
---
duration_ms: 88.467177
type: 'test'
...
# Subtest: DM reconnect replays do not duplicate messages or reset seen state
ok 3 - DM reconnect replays do not duplicate messages or reset seen state
---
duration_ms: 174.609346
type: 'test'
...
1..3
# tests 3
# suites 0
# pass 3
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 425.568639
→ Running tests/nostr/dmDecryptWorkerClient.test.mjs
TAP version 13
# Subtest: dmDecryptWorkerClient
# Subtest: should support worker environment
ok 1 - should support worker environment
---
duration_ms: 5.374374
type: 'test'
...
# Subtest: should reject if Worker is unavailable
ok 2 - should reject if Worker is unavailable
---
duration_ms: 2.95107
type: 'test'
...
# Subtest: should reject if inputs are invalid
ok 3 - should reject if inputs are invalid
---
duration_ms: 2.720202
type: 'test'
...
# Subtest: should successfully decrypt message
ok 4 - should successfully decrypt message
---
duration_ms: 9.059537
type: 'test'
...
# Subtest: should handle worker error response
ok 5 - should handle worker error response
---
duration_ms: 3.692516
type: 'test'
...
# Subtest: should handle worker error event
ok 6 - should handle worker error event
---
duration_ms: 4.255104
type: 'test'
...
# Subtest: should timeout if worker does not respond
ok 7 - should timeout if worker does not respond
---
duration_ms: 101.850183
type: 'test'
...
# Subtest: should initialize worker lazily and reuse instance
ok 8 - should initialize worker lazily and reuse instance
---
duration_ms: 3.154795
type: 'test'
...
# Subtest: should recreate worker if creation fails
ok 9 - should recreate worker if creation fails
---
duration_ms: 1.780904
type: 'test'
...
1..9
ok 1 - dmDecryptWorkerClient
---
duration_ms: 137.677385
type: 'suite'
...
1..1
# tests 9
# suites 1
# pass 9
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 146.784447
→ Running tests/nostr/eventsCacheStore.test.mjs
TAP version 13
# Subtest: EventsCacheStore
# Subtest: persistSnapshot should store events and tombstones
ok 1 - persistSnapshot should store events and tombstones
---
duration_ms: 15.448183
type: 'test'
...
# Subtest: persistSnapshot should only update changed items
ok 2 - persistSnapshot should only update changed items
---
duration_ms: 3.734208
type: 'test'
...
# Subtest: persistSnapshot should delete removed items
ok 3 - persistSnapshot should delete removed items
---
duration_ms: 3.105603
type: 'test'
...
# Subtest: persistSnapshot should respect dirty keys optimization
ok 4 - persistSnapshot should respect dirty keys optimization
---
duration_ms: 2.443119
type: 'test'
...
1..4
ok 1 - EventsCacheStore
---
duration_ms: 26.573291
type: 'suite'
...
1..1
# tests 4
# suites 1
# pass 4
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 34.250108
→ Running tests/nostr/maxListenerDiagnostics.test.mjs
TAP version 13
# Subtest: maxListenerDiagnostics
# Subtest: collectCandidateStrings
# Subtest: should return empty array for null/undefined/falsy
ok 1 - should return empty array for null/undefined/falsy
---
duration_ms: 1.755901
type: 'test'
...
# Subtest: should return array with string for string input
ok 2 - should return array with string for string input
---
duration_ms: 0.54973
type: 'test'
...
# Subtest: should extract relevant fields from object
ok 3 - should extract relevant fields from object
---
duration_ms: 0.604648
type: 'test'
...
# Subtest: should ignore non-string fields in object
ok 4 - should ignore non-string fields in object
---
duration_ms: 0.570708
type: 'test'
...
1..4
ok 1 - collectCandidateStrings
---
duration_ms: 4.411108
type: 'suite'
...
# Subtest: shouldSuppressWarning
# Subtest: should NOT suppress anything if verbose mode is enabled
ok 1 - should NOT suppress anything if verbose mode is enabled
---
duration_ms: 0.633823
type: 'test'
...
# Subtest: should suppress warning by code string
ok 2 - should suppress warning by code string
---
duration_ms: 0.234248
type: 'test'
...
# Subtest: should suppress warning by object code property
ok 3 - should suppress warning by object code property
---
duration_ms: 0.284711
type: 'test'
...
# Subtest: should suppress warning by message snippet
ok 4 - should suppress warning by message snippet
---
duration_ms: 0.376485
type: 'test'
...
# Subtest: should suppress warning by object message property snippet
ok 5 - should suppress warning by object message property snippet
---
duration_ms: 0.433506
type: 'test'
...
# Subtest: should NOT suppress unrelated warnings
ok 6 - should NOT suppress unrelated warnings
---
duration_ms: 0.462472
type: 'test'
...
# Subtest: should handle multiple arguments
ok 7 - should handle multiple arguments
---
duration_ms: 0.439738
type: 'test'
...
1..7
ok 2 - shouldSuppressWarning
---
duration_ms: 3.543761
type: 'suite'
...
# Subtest: process.emitWarning patch
# Subtest: should have patched process.emitWarning
ok 1 - should have patched process.emitWarning
---
duration_ms: 0.255569
type: 'test'
...
1..1
ok 3 - process.emitWarning patch
---
duration_ms: 0.384431
type: 'suite'
...
1..3
ok 1 - maxListenerDiagnostics
---
duration_ms: 10.123805
type: 'suite'
...
1..1
# tests 12
# suites 4
# pass 12
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 30.979715
→ Running tests/nostr/nip07Permissions.test.js
TAP version 13
# Subtest: writeStoredNip07Permissions normalizes and persists granted methods
ok 1 - writeStoredNip07Permissions normalizes and persists granted methods
---
duration_ms: 2.173318
type: 'test'
...
# Subtest: clearStoredNip07Permissions removes persisted grants
ok 2 - clearStoredNip07Permissions removes persisted grants
---
duration_ms: 0.292226
type: 'test'
...
# Subtest: requestEnablePermissions retries explicit and fallback variants
ok 3 - requestEnablePermissions retries explicit and fallback variants
---
duration_ms: 1.792873
type: 'test'
...
# Subtest: requestEnablePermissions reports unavailable extension
ok 4 - requestEnablePermissions reports unavailable extension
---
duration_ms: 0.21902
type: 'test'
...
# Subtest: waitForNip07Extension resolves when extension is present
ok 5 - waitForNip07Extension resolves when extension is present
---
duration_ms: 0.486451
type: 'test'
...
# Subtest: waitForNip07Extension resolves when extension appears later
ok 6 - waitForNip07Extension resolves when extension appears later
---
duration_ms: 50.709536
type: 'test'
...
# Subtest: waitForNip07Extension rejects when extension never appears
ok 7 - waitForNip07Extension rejects when extension never appears
---
duration_ms: 101.272522
type: 'test'
...
1..7
# tests 7
# suites 0
# pass 7
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 164.949654
→ Running tests/nostr/nip71.test.js
TAP version 13
# Subtest: buildNip71MetadataTags normalizes structured fields
ok 1 - buildNip71MetadataTags normalizes structured fields
---
duration_ms: 3.253825
type: 'test'
...
# Subtest: collectNip71PointerRequests aggregates events and tags
ok 2 - collectNip71PointerRequests aggregates events and tags
---
duration_ms: 0.578783
type: 'test'
...
# Subtest: processNip71Events reconciles pointers and filters video hashtags
ok 3 - processNip71Events reconciles pointers and filters video hashtags
---
duration_ms: 1.371637
type: 'test'
...
# Subtest: populateNip71MetadataForVideos fetches missing records once
ok 4 - populateNip71MetadataForVideos fetches missing records once
---
duration_ms: 0.905554
type: 'test'
...
# Subtest: buildNip71VideoEvent composes pointer tags
ok 5 - buildNip71VideoEvent composes pointer tags
---
duration_ms: 0.810467
type: 'test'
...
1..5
# tests 5
# suites 0
# pass 5
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 20.261854
→ Running tests/nostr/publishHelpers.test.mjs
TAP version 13
# Subtest: mirrorVideoEvent lowercases provided MIME types
ok 1 - mirrorVideoEvent lowercases provided MIME types
---
duration_ms: 4.452107
type: 'test'
...
# Subtest: mirrorVideoEvent lowercases inferred MIME types
ok 2 - mirrorVideoEvent lowercases inferred MIME types
---
duration_ms: 0.878048
type: 'test'
...
# Subtest: mirrorVideoEvent includes hash tags when provided
ok 3 - mirrorVideoEvent includes hash tags when provided
---
duration_ms: 1.521652
type: 'test'
...
1..3
# tests 3
# suites 0
# pass 3
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 19.072876
→ Running tests/nostr/session-actor.test.mjs
TAP version 13
# Subtest: SessionActor
# Subtest: encrypts and decrypts private key
ok 1 - encrypts and decrypts private key
---
duration_ms: 304.532976
type: 'test'
...
# Subtest: fails to decrypt with wrong passphrase
ok 2 - fails to decrypt with wrong passphrase
---
duration_ms: 294.542657
type: 'test'
...
# Subtest: persists and reads session actor
ok 3 - persists and reads session actor
---
duration_ms: 1.437758
type: 'test'
...
# Subtest: clears stored session actor
ok 4 - clears stored session actor
---
duration_ms: 0.374769
type: 'test'
...
1..4
ok 1 - SessionActor
---
duration_ms: 602.949098
type: 'test'
...
1..1
# tests 5
# suites 0
# pass 5
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 615.899874
→ Running tests/nostr/sign-request-queue.test.mjs
TAP version 13
# Subtest: queueSignEvent: processes requests sequentially
ok 1 - queueSignEvent: processes requests sequentially
---
duration_ms: 104.408913
type: 'test'
...
# Subtest: queueSignEvent: handles timeouts
ok 2 - queueSignEvent: handles timeouts
---
duration_ms: 21.414134
type: 'test'
...
# Subtest: queueSignEvent: handles permission denied errors
ok 3 - queueSignEvent: handles permission denied errors
---
duration_ms: 0.621015
type: 'test'
...
# Subtest: queueSignEvent: handles signer disconnected
ok 4 - queueSignEvent: handles signer disconnected
---
duration_ms: 0.527632
type: 'test'
...
# Subtest: queueSignEvent: fails if signer is missing or invalid
ok 5 - queueSignEvent: fails if signer is missing or invalid
---
duration_ms: 0.520472
type: 'test'
...
# Subtest: queueSignEvent: continues queue processing after failure
ok 6 - queueSignEvent: continues queue processing after failure
---
duration_ms: 0.801907
type: 'test'
...
1..6
# tests 6
# suites 0
# pass 6
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 211.158917
→ Running tests/nostr/toolkit.test.mjs
TAP version 13
# Subtest: toolkit: DEFAULT_RELAY_URLS is frozen and contains valid URLs
ok 1 - toolkit: DEFAULT_RELAY_URLS is frozen and contains valid URLs
---
duration_ms: 1.171268
type: 'test'
...
# Subtest: toolkit: resolveSimplePoolConstructor finds SimplePool
ok 2 - toolkit: resolveSimplePoolConstructor finds SimplePool
---
duration_ms: 0.51039
type: 'test'
...
# Subtest: toolkit: shimLegacySimplePoolMethods adds sub/list/map if missing
ok 3 - toolkit: shimLegacySimplePoolMethods adds sub/list/map if missing
---
duration_ms: 0.742595
type: 'test'
...
# Subtest: toolkit: readToolkitFromScope finds NostrTools in global scope
ok 4 - toolkit: readToolkitFromScope finds NostrTools in global scope
---
duration_ms: 0.305169
type: 'test'
...
# Subtest: toolkit: normalizeToolkitCandidate validation
ok 5 - toolkit: normalizeToolkitCandidate validation
---
duration_ms: 0.663416
type: 'test'
...
# Subtest: toolkit: shimLegacySimplePoolMethods handles simple list operation
ok 6 - toolkit: shimLegacySimplePoolMethods handles simple list operation
---
duration_ms: 10.742613
type: 'test'
...
1..6
# tests 6
# suites 0
# pass 6
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 21.58526
→ Running tests/nostr/watchHistory.test.js
[bitvid] [ProfileCache] Expired watchHistory for 2222222222222222222222222222222222222222222222222222222222222222
TAP version 13
# Subtest: buildWatchHistoryPayload enforces byte limits and records skipped entries
ok 1 - buildWatchHistoryPayload enforces byte limits and records skipped entries
---
duration_ms: 3.020853
type: 'test'
...
# Subtest: getWatchHistoryStorage prunes entries that exceed the configured TTL
ok 2 - getWatchHistoryStorage prunes entries that exceed the configured TTL
---
duration_ms: 2.484765
type: 'test'
...
# Subtest: fetchWatchHistory prefers decrypted chunk payloads when nip04 decrypt succeeds
ok 3 - fetchWatchHistory prefers decrypted chunk payloads when nip04 decrypt succeeds
---
duration_ms: 9.542144
type: 'test'
...
# Subtest: fetchWatchHistory falls back to pointer payload when nip04 decrypt fails
ok 4 - fetchWatchHistory falls back to pointer payload when nip04 decrypt fails
---
duration_ms: 2.321171
type: 'test'
...
# Subtest: publishWatchHistorySnapshot uses injected nostr-tools helpers when signer cannot encrypt
ok 5 - publishWatchHistorySnapshot uses injected nostr-tools helpers when signer cannot encrypt
---
duration_ms: 5.145558
type: 'test'
...
# Subtest: publishWatchHistorySnapshot caches successful snapshot results
ok 6 - publishWatchHistorySnapshot caches successful snapshot results
---
duration_ms: 2.97558
type: 'test'
...
1..6
# tests 6
# suites 0
# pass 6
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 33.328616
→ Running tests/performance/resolvePostedAt.test.mjs
pool.list calls: 2
pool.get calls: 0
TAP version 13
# Subtest: hydrateVideoHistoryBatch optimizes network calls
ok 1 - hydrateVideoHistoryBatch optimizes network calls
---
duration_ms: 5.077898
type: 'test'
...
# Subtest: resolveVideoPostedAtBatch uses batch hydration
ok 2 - resolveVideoPostedAtBatch uses batch hydration
---
duration_ms: 0.762494
type: 'test'
...
1..2
# tests 2
# suites 0
# pass 2
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 18.779541
→ Running tests/profile-modal-controller.test.mjs
TAP version 13
# Subtest: Profile modal Escape closes and restores trigger focus
ok 1 - Profile modal Escape closes and restores trigger focus
---
duration_ms: 1469.7277
type: 'test'
...
# Subtest: Add profile request suspends focus trap and elevates login modal
ok 2 - Add profile request suspends focus trap and elevates login modal
---
duration_ms: 1323.568723
type: 'test'
...
# Subtest: Profile modal navigation buttons toggle active state
ok 3 - Profile modal navigation buttons toggle active state
---
duration_ms: 1453.20504
type: 'test'
...
# Subtest: Profile modal toggles mobile menu and pane views
ok 4 - Profile modal toggles mobile menu and pane views
---
duration_ms: 1442.70442
type: 'test'
...
# Subtest: wallet URI input masks persisted values and restores on focus
ok 5 - wallet URI input masks persisted values and restores on focus
---
duration_ms: 1044.304186
type: 'test'
...
# Subtest: Profile modal uses abbreviated npub display
ok 6 - Profile modal uses abbreviated npub display
---
duration_ms: 783.009092
type: 'test'
...
# Subtest: renderSavedProfiles applies provider metadata
ok 7 - renderSavedProfiles applies provider metadata
---
duration_ms: 782.447932
type: 'test'
...
# Subtest: Hashtag pane shows empty states by default
ok 8 - Hashtag pane shows empty states by default
---
duration_ms: 969.506899
type: 'test'
...
# Subtest: Hashtag pane adds, moves, and removes tags
ok 9 - Hashtag pane adds, moves, and removes tags
---
duration_ms: 968.954229
type: 'test'
...
# Subtest: handleAddHashtagPreference publishes updates
ok 10 - handleAddHashtagPreference publishes updates
---
duration_ms: 983.262356
type: 'test'
...
# Subtest: Hashtag pane resets after logout when service clears tags
ok 11 - Hashtag pane resets after logout when service clears tags
---
duration_ms: 1192.802164
type: 'test'
...
# Subtest: load() injects markup and caches expected elements
ok 12 - load() injects markup and caches expected elements
---
duration_ms: 513.149987
type: 'test'
...
# Subtest: show()/hide() toggle panes, trap focus, and refresh the wallet pane
ok 13 - show()/hide() toggle panes, trap focus, and refresh the wallet pane
---
duration_ms: 984.570203
type: 'test'
...
# Subtest: populateProfileRelays renders entries and wires action buttons
ok 14 - populateProfileRelays renders entries and wires action buttons
---
duration_ms: 535.202202
type: 'test'
...
# Subtest: admin mutations invoke accessControl stubs and update admin DOM
ok 15 - admin mutations invoke accessControl stubs and update admin DOM
---
duration_ms: 536.036364
type: 'test'
...
# Subtest: history pane lazily initializes the watch history renderer
ok 16 - history pane lazily initializes the watch history renderer
---
duration_ms: 1407.155808
type: 'test'
...
# Subtest: handleDirectMessagesRelayWarning throttles status updates
ok 17 - handleDirectMessagesRelayWarning throttles status updates
---
duration_ms: 516.386463
type: 'test'
...
# Subtest: handleDirectMessagesRelayWarning suppresses status updates when disabled
ok 18 - handleDirectMessagesRelayWarning suppresses status updates when disabled
---
duration_ms: 511.17663
type: 'test'
...
1..18
# tests 18
# suites 0
# pass 18
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 17427.497349
→ Running tests/revert-modal-controller.test.mjs
TAP version 13
# Subtest: RevertModalController
# Subtest: open() fetches history and opens modal
ok 1 - open() fetches history and opens modal
---
duration_ms: 1.450544
type: 'test'
...
# Subtest: open() shows error if not logged in
ok 2 - open() shows error if not logged in
---
duration_ms: 0.440968
type: 'test'
...
# Subtest: handleConfirm() calls revertVideo and refreshes
ok 3 - handleConfirm() calls revertVideo and refreshes
---
duration_ms: 0.48174
type: 'test'
...
1..3
ok 1 - RevertModalController
---
duration_ms: 4.159724
type: 'test'
...
1..1
# tests 4
# suites 0
# pass 4
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 15.959592
→ Running tests/services/authUtils.test.mjs
TAP version 13
# Subtest: authUtils
# Subtest: normalizeProviderId
# Subtest: returns trimmed string if valid
ok 1 - returns trimmed string if valid
---
duration_ms: 2.144222
type: 'test'
...
# Subtest: returns 'nip07' fallback if empty
ok 2 - returns 'nip07' fallback if empty
---
duration_ms: 0.201611
type: 'test'
...
# Subtest: returns 'nip07' fallback if null
ok 3 - returns 'nip07' fallback if null
---
duration_ms: 0.318762
type: 'test'
...
# Subtest: returns 'nip07' fallback if undefined
ok 4 - returns 'nip07' fallback if undefined
---
duration_ms: 0.243121
type: 'test'
...
# Subtest: returns 'nip07' fallback if not a string
ok 5 - returns 'nip07' fallback if not a string
---
duration_ms: 0.169876
type: 'test'
...
1..5
ok 1 - normalizeProviderId
---
duration_ms: 4.681913
type: 'test'
...
# Subtest: normalizeAuthType
# Subtest: prioritizes authTypeCandidate
ok 1 - prioritizes authTypeCandidate
---
duration_ms: 0.466432
type: 'test'
...
# Subtest: falls back to providerResult.authType
ok 2 - falls back to providerResult.authType
---
duration_ms: 0.207213
type: 'test'
...
# Subtest: falls back to providerResult.providerId
ok 3 - falls back to providerResult.providerId
---
duration_ms: 0.395975
type: 'test'
...
# Subtest: falls back to providerId
ok 4 - falls back to providerId
---
duration_ms: 0.234249
type: 'test'
...
# Subtest: returns 'nip07' if all else fails
ok 5 - returns 'nip07' if all else fails
---
duration_ms: 0.127395
type: 'test'
...
# Subtest: trims whitespace from results
ok 6 - trims whitespace from results
---
duration_ms: 0.134673
type: 'test'
...
# Subtest: ignores empty strings in candidates
ok 7 - ignores empty strings in candidates
---
duration_ms: 0.155309
type: 'test'
...
1..7
ok 2 - normalizeAuthType
---
duration_ms: 3.084977
type: 'test'
...
1..2
ok 1 - authUtils
---
duration_ms: 8.791832
type: 'test'
...
1..1
# tests 15
# suites 0
# pass 15
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 27.600349
→ Running tests/services/exploreDataService.test.mjs
TAP version 13
# Subtest: exploreDataService - buildWatchHistoryTagCounts
# Subtest: should return empty Map if watchHistoryService is missing
ok 1 - should return empty Map if watchHistoryService is missing
---
duration_ms: 1.259941
type: 'test'
...
# Subtest: should handle missing loadLatest method gracefully
ok 2 - should handle missing loadLatest method gracefully
---
duration_ms: 0.475673
type: 'test'
...
# Subtest: should handle loadLatest failure gracefully
ok 3 - should handle loadLatest failure gracefully
---
duration_ms: 7.966391
type: 'test'
...
# Subtest: should return counts from worker on success
ok 4 - should return counts from worker on success
---
duration_ms: 2.426911
type: 'test'
...
# Subtest: should handle worker error gracefully
ok 5 - should handle worker error gracefully
---
duration_ms: 1.556333
type: 'test'
...
1..5
ok 1 - exploreDataService - buildWatchHistoryTagCounts
---
duration_ms: 23.175866
type: 'suite'
...
1..1
# tests 5
# suites 1
# pass 5
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 30.86583
→ Running tests/services/nostr-service.test.mjs
TAP version 13
# Subtest: NostrService
# Subtest: loadVideos
# Subtest: should load cached videos and start subscription
ok 1 - should load cached videos and start subscription
---
duration_ms: 3.459953
type: 'test'
...
1..1
ok 1 - loadVideos
---
duration_ms: 4.557931
type: 'suite'
...
# Subtest: fetchVideosByAuthors
# Subtest: should fetch videos from relays for specific authors
ok 1 - should fetch videos from relays for specific authors
---
duration_ms: 2.337507
type: 'test'
...
1..1
ok 2 - fetchVideosByAuthors
---
duration_ms: 2.581505
type: 'suite'
...
1..2
ok 1 - NostrService
---
duration_ms: 7.98116
type: 'suite'
...
1..1
# tests 2
# suites 3
# pass 2
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 23.777942
→ Running tests/services/playbackService.test.mjs
(node:2901) ExperimentalWarning: The MockTimers API is an experimental feature and might change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
TAP version 13
# Subtest: PlaybackService
# Subtest: Initialization sets defaults and dependencies
ok 1 - Initialization sets defaults and dependencies
---
duration_ms: 1.493747
type: 'test'
...
# Subtest: prepareVideoElement respects localStorage and binds listener
ok 2 - prepareVideoElement respects localStorage and binds listener
---
duration_ms: 4.785662
type: 'test'
...
# Subtest: registerUrlPlaybackWatchdogs triggers onFallback on error
ok 3 - registerUrlPlaybackWatchdogs triggers onFallback on error
---
duration_ms: 1.642048
type: 'test'
...
# Subtest: registerUrlPlaybackWatchdogs triggers onSuccess on playing
ok 4 - registerUrlPlaybackWatchdogs triggers onSuccess on playing
---
duration_ms: 0.726003
type: 'test'
...
# Subtest: createSession returns a PlaybackSession
ok 5 - createSession returns a PlaybackSession
---
duration_ms: 0.537659
type: 'test'
...
# Subtest: PlaybackSession Flow
# Subtest: URL Probe Success starts playback
ok 1 - URL Probe Success starts playback
---
duration_ms: 8.917385
type: 'test'
...
# Subtest: URL Probe Failure triggers fallback
ok 2 - URL Probe Failure triggers fallback
---
duration_ms: 2.636913
type: 'test'
...
# Subtest: Watchdog Stall triggers fallback
ok 3 - Watchdog Stall triggers fallback
---
duration_ms: 3.045819
type: 'test'
...
1..3
ok 6 - PlaybackSession Flow
---
duration_ms: 15.229365
type: 'suite'
...
1..6
ok 1 - PlaybackService
---
duration_ms: 108.524429
type: 'suite'
...
1..1
# tests 8
# suites 2
# pass 8
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 122.895195
→ Running tests/services/relay-health-service.test.mjs
TAP version 13
# Subtest: RelayHealthService: initializes with default values
ok 1 - RelayHealthService: initializes with default values
---
duration_ms: 1.641452
type: 'test'
...
# Subtest: RelayHealthService: manages telemetry opt-in
ok 2 - RelayHealthService: manages telemetry opt-in
---
duration_ms: 0.356629
type: 'test'
...
# Subtest: RelayHealthService: getRelayUrls fetches from relayManager
ok 3 - RelayHealthService: getRelayUrls fetches from relayManager
---
duration_ms: 1.02515
type: 'test'
...
# Subtest: RelayHealthService: ensureRelayState creates default state
ok 4 - RelayHealthService: ensureRelayState creates default state
---
duration_ms: 0.23577
type: 'test'
...
# Subtest: RelayHealthService: checkRelay success flow
ok 5 - RelayHealthService: checkRelay success flow
---
duration_ms: 1.054715
type: 'test'
...
# Subtest: RelayHealthService: checkRelay failure flow
ok 6 - RelayHealthService: checkRelay failure flow
---
duration_ms: 0.497958
type: 'test'
...
# Subtest: RelayHealthService: checkRelay handles missing nostrClient
ok 7 - RelayHealthService: checkRelay handles missing nostrClient
---
duration_ms: 0.332384
type: 'test'
...
# Subtest: RelayHealthService: refresh checks all relays
ok 8 - RelayHealthService: refresh checks all relays
---
duration_ms: 0.695152
type: 'test'
...
# Subtest: RelayHealthService: emits telemetry if opted in
ok 9 - RelayHealthService: emits telemetry if opted in
---
duration_ms: 1.016069
type: 'test'
...
# Subtest: RelayHealthService: checkRelay times out after DEFAULT_TIMEOUT_MS
ok 10 - RelayHealthService: checkRelay times out after DEFAULT_TIMEOUT_MS
---
duration_ms: 10.048085
type: 'test'
...
# Subtest: RelayHealthService: failure threshold triggers user warning
ok 11 - RelayHealthService: failure threshold triggers user warning
---
duration_ms: 0.888607
type: 'test'
...
# Subtest: RelayHealthService: user warning respects cooldown
ok 12 - RelayHealthService: user warning respects cooldown
---
duration_ms: 0.49601
type: 'test'
...
# Subtest: RelayHealthService: relay disconnect/error events trigger failure
ok 13 - RelayHealthService: relay disconnect/error events trigger failure
---
duration_ms: 0.337866
type: 'test'
...
# Subtest: RelayHealthService: integrates with nostrClient.markRelayUnreachable
ok 14 - RelayHealthService: integrates with nostrClient.markRelayUnreachable
---
duration_ms: 0.333207
type: 'test'
...
1..14
# tests 14
# suites 0
# pass 14
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 5016.469349
→ Running tests/services/trustBootstrap.test.mjs
[bitvid] [bootstrap] Failed to hydrate admin lists for trusted seeds Error: accessControl ready check timed out
at AccessControl.<anonymous> (file:///app/tests/services/trustBootstrap.test.mjs:147:13)
at Object.apply (node:internal/test_runner/mock/mock:765:20)
at waitForAccessControl (file:///app/js/services/trustBootstrap.js:80:39)
at hydrate (file:///app/js/services/trustBootstrap.js:138:28)
at bootstrapTrustedSeeds (file:///app/js/services/trustBootstrap.js:151:31)
at TestContext.<anonymous> (file:///app/tests/services/trustBootstrap.test.mjs:150:11)
at Test.runInAsyncScope (node:async_hooks:214:14)
at Test.run (node:internal/test_runner/test:1047:25)
at async Suite.processPendingSubtests (node:internal/test_runner/test:744:7)
[bitvid] [bootstrap] Failed to hydrate admin lists for trusted seeds Error: accessControl ready check timed out
at AccessControl.<anonymous> (file:///app/tests/services/trustBootstrap.test.mjs:147:13)
at Object.apply (node:internal/test_runner/mock/mock:765:20)
at waitForAccessControl (file:///app/js/services/trustBootstrap.js:80:39)
at hydrate (file:///app/js/services/trustBootstrap.js:138:28)
at runAsyncRetry (file:///app/js/services/trustBootstrap.js:160:13)
[bitvid] [bootstrap] Failed to hydrate admin lists for trusted seeds Error: accessControl failed
at AccessControl.<anonymous> (file:///app/tests/services/trustBootstrap.test.mjs:164:13)
at Object.apply (node:internal/test_runner/mock/mock:765:20)
at waitForAccessControl (file:///app/js/services/trustBootstrap.js:80:39)
at hydrate (file:///app/js/services/trustBootstrap.js:138:28)
at bootstrapTrustedSeeds (file:///app/js/services/trustBootstrap.js:151:31)
at TestContext.<anonymous> (file:///app/tests/services/trustBootstrap.test.mjs:171:30)
at Test.runInAsyncScope (node:async_hooks:214:14)
at Test.run (node:internal/test_runner/test:1047:25)
at async Suite.processPendingSubtests (node:internal/test_runner/test:744:7)
TAP version 13
# Subtest: trustBootstrap
# Subtest: should return early if feature is disabled
ok 1 - should return early if feature is disabled
---
duration_ms: 2.490273
type: 'test'
...
# Subtest: should apply seeds when accessControl is ready
ok 2 - should apply seeds when accessControl is ready
---
duration_ms: 1.474724
type: 'test'
...
# Subtest: should subscribe to accessControl changes
ok 3 - should subscribe to accessControl changes
---
duration_ms: 1.076454
type: 'test'
...
# Subtest: should handle accessControl timeout and apply seeds anyway
ok 4 - should handle accessControl timeout and apply seeds anyway
---
duration_ms: 2.779063
type: 'test'
...
# Subtest: should recompute summaries after applying seeds
ok 5 - should recompute summaries after applying seeds
---
duration_ms: 0.534346
type: 'test'
...
# Subtest: should wait for relays if hydration fails initially
ok 6 - should wait for relays if hydration fails initially
---
duration_ms: 300.835822
type: 'test'
...
1..6
ok 1 - trustBootstrap
---
duration_ms: 311.306473
type: 'suite'
...
1..1
# tests 6
# suites 1
# pass 6
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 3767.382466
→ Running tests/sign-request-queue.test.mjs
TAP version 13
# Subtest: queueSignEvent executes successfully
ok 1 - queueSignEvent executes successfully
---
duration_ms: 1.881206
type: 'test'
...
# Subtest: queueSignEvent executes sequentially for same signer
ok 2 - queueSignEvent executes sequentially for same signer
---
duration_ms: 101.461989
type: 'test'
...
# Subtest: queueSignEvent executes concurrently for different signers
ok 3 - queueSignEvent executes concurrently for different signers
---
duration_ms: 51.333849
type: 'test'
...
# Subtest: queueSignEvent times out
ok 4 - queueSignEvent times out
---
duration_ms: 51.980091
type: 'test'
...
# Subtest: queueSignEvent normalizes errors
ok 5 - queueSignEvent normalizes errors
---
duration_ms: 0.762469
type: 'test'
...
# Subtest: queueSignEvent handles signer disconnect
ok 6 - queueSignEvent handles signer disconnect
---
duration_ms: 0.663496
type: 'test'
...
# Subtest: queueSignEvent handles missing signer
ok 7 - queueSignEvent handles missing signer
---
duration_ms: 0.411497
type: 'test'
...
1..7
# tests 7
# suites 0
# pass 7
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 363.601967
→ Running tests/state/cache.test.mjs
TAP version 13
# Subtest: js/state/cache.js
# Subtest: Saved Profiles Persistence
ok 1 - Saved Profiles Persistence
---
duration_ms: 2.396226
type: 'test'
...
# Subtest: Active Profile Pubkey
ok 2 - Active Profile Pubkey
---
duration_ms: 0.579387
type: 'test'
...
# Subtest: URL Health Caching
ok 3 - URL Health Caching
---
duration_ms: 0.636598
type: 'test'
...
# Subtest: URL Health Expiration
ok 4 - URL Health Expiration
---
duration_ms: 11.280865
type: 'test'
...
# Subtest: Moderation Settings
ok 5 - Moderation Settings
---
duration_ms: 1.4103
type: 'test'
...
# Subtest: Legacy Moderation Overrides Support
# Subtest: ignores legacy v1 overrides
ok 1 - ignores legacy v1 overrides
---
duration_ms: 0.483389
type: 'test'
...
# Subtest: loads v2 overrides
ok 2 - loads v2 overrides
---
duration_ms: 0.511395
type: 'test'
...
1..2
ok 6 - Legacy Moderation Overrides Support
---
duration_ms: 1.930102
type: 'test'
...
1..6
ok 1 - js/state/cache.js
---
duration_ms: 21.415125
type: 'test'
...
1..1
# tests 9
# suites 0
# pass 9
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 29.994156
→ Running tests/storage-service.test.mjs
TAP version 13
# Subtest: StorageService
# Subtest: init() creates database and object store
ok 1 - init() creates database and object store
---
duration_ms: 11.515462
type: 'test'
...
[bitvid] [StorageService] Unlocked storage for 00000000...
# Subtest: unlock() generates and stores master key with NIP-44
ok 2 - unlock() generates and stores master key with NIP-44
---
duration_ms: 9.964094
type: 'test'
...
[bitvid] [StorageService] Unlocked storage for 00000000...
[bitvid] [StorageService] Locked storage for 00000000...
[bitvid] [StorageService] Unlocked storage for 00000000...
# Subtest: unlock() restores existing master key
ok 3 - unlock() restores existing master key
---
duration_ms: 4.639025
type: 'test'
...
[bitvid] [StorageService] Unlocked storage for 00000000...
# Subtest: unlock() falls back to NIP-04 if NIP-44 unavailable
ok 4 - unlock() falls back to NIP-04 if NIP-44 unavailable
---
duration_ms: 3.15478
type: 'test'
...
[bitvid] [StorageService] Unlocked storage for 00000000...
[bitvid] [StorageService] Locked storage for 00000000...
# Subtest: unlock() normalizes permission denied decrypt errors
ok 5 - unlock() normalizes permission denied decrypt errors
---
duration_ms: 4.078727
type: 'test'
...
[bitvid] [StorageService] Unlocked storage for 00000000...
[bitvid] [StorageService] Locked storage for 00000000...
# Subtest: unlock() normalizes missing decryptor errors
ok 6 - unlock() normalizes missing decryptor errors
---
duration_ms: 4.985059
type: 'test'
...
[bitvid] [StorageService] Unlocked storage for 00000000...
[bitvid] [StorageService] Locked storage for 00000000...
# Subtest: unlock() normalizes unknown decrypt errors
ok 7 - unlock() normalizes unknown decrypt errors
---
duration_ms: 2.563247
type: 'test'
...
[bitvid] [StorageService] Unlocked storage for 00000000...
[bitvid] [StorageService] Saved connection conn_1
# Subtest: saveConnection() encrypts and stores connection
ok 8 - saveConnection() encrypts and stores connection
---
duration_ms: 4.340388
type: 'test'
...
[bitvid] [StorageService] Unlocked storage for 00000000...
[bitvid] [StorageService] Saved connection conn_1
# Subtest: getConnection() decrypts and returns connection
ok 9 - getConnection() decrypts and returns connection
---
duration_ms: 4.236908
type: 'test'
...
# Subtest: getConnection() throws if locked
ok 10 - getConnection() throws if locked
---
duration_ms: 1.35787
type: 'test'
...
[bitvid] [StorageService] Unlocked storage for 00000000...
[bitvid] [StorageService] Saved connection conn_1
[bitvid] [StorageService] Deleted connection conn_1
# Subtest: deleteConnection() removes connection
ok 11 - deleteConnection() removes connection
---
duration_ms: 3.150325
type: 'test'
...
[bitvid] [StorageService] Unlocked storage for 00000000...
[bitvid] [StorageService] Saved connection conn_1
[bitvid] [StorageService] Saved connection conn_2
# Subtest: setDefaultConnection() updates metadata
ok 12 - setDefaultConnection() updates metadata
---
duration_ms: 3.808745
type: 'test'
...
[bitvid] [StorageService] Unlocked storage for 00000000...
[bitvid] [StorageService] Saved connection conn_1
[bitvid] [StorageService] Saved connection conn_2
# Subtest: saveConnection() with defaultForUploads=true clears other defaults
ok 13 - saveConnection() with defaultForUploads=true clears other defaults
---
duration_ms: 3.566813
type: 'test'
...
1..13
ok 1 - StorageService
---
duration_ms: 64.212338
type: 'suite'
...
1..1
# tests 13
# suites 1
# pass 13
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 72.820971
→ Running tests/torrent/style-helpers.test.mjs
TAP version 13
# Subtest: torrent/ui/styleHelpers
# Subtest: returns null when no element is provided
ok 1 - returns null when no element is provided
---
duration_ms: 0.940898
type: 'test'
...
# Subtest: returns the original element without mutations
ok 2 - returns the original element without mutations
---
duration_ms: 76.998441
type: 'test'
...
# Subtest: provides an empty, frozen fallback map
ok 3 - provides an empty, frozen fallback map
---
duration_ms: 1.371952
type: 'test'
...
# Subtest: no-ops when removing styles
ok 4 - no-ops when removing styles
---
duration_ms: 10.133292
type: 'test'
...
1..4
ok 1 - torrent/ui/styleHelpers
---
duration_ms: 91.147249
type: 'suite'
...
1..1
# tests 4
# suites 1
# pass 4
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 104.59707
→ Running tests/ui/components/debug_hashtag_strip_helper.test.mjs
Global ResizeObserver type: function
Helper window ResizeObserver: undefined
TAP version 13
# Subtest: Debug HashtagStripHelper fallback
ok 1 - Debug HashtagStripHelper fallback
---
duration_ms: 103.886988
type: 'test'
...
1..1
# tests 1
# suites 0
# pass 1
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 114.385638
→ Running tests/ui/components/VideoModalSimilarHelpers.test.mjs
TAP version 13
# Subtest: derivePointerKeyFromInput
# Subtest: derives key from string
ok 1 - derives key from string
---
duration_ms: 1.175664
type: 'test'
...
# Subtest: derives key from array
ok 2 - derives key from array
---
duration_ms: 0.230831
type: 'test'
...
# Subtest: derives key from object
ok 3 - derives key from object
---
duration_ms: 0.214485
type: 'test'
...
# Subtest: handles empty/invalid input
ok 4 - handles empty/invalid input
---
duration_ms: 0.212874
type: 'test'
...
1..4
ok 1 - derivePointerKeyFromInput
---
duration_ms: 3.972707
type: 'test'
...
# Subtest: formatViewCountLabel
# Subtest: formats numbers
ok 1 - formats numbers
---
duration_ms: 41.721759
type: 'test'
...
1..1
ok 2 - formatViewCountLabel
---
duration_ms: 42.0532
type: 'test'
...
# Subtest: getViewCountLabel
# Subtest: returns formatted count
ok 1 - returns formatted count
---
duration_ms: 0.610811
type: 'test'
...
# Subtest: handles partial
ok 2 - handles partial
---
duration_ms: 0.659836
type: 'test'
...
# Subtest: handles hydrating
ok 3 - handles hydrating
---
duration_ms: 0.341993
type: 'test'
...
1..3
ok 3 - getViewCountLabel
---
duration_ms: 2.960498
type: 'test'
...
# Subtest: buildSimilarCardIdentity
# Subtest: uses overrides
ok 1 - uses overrides
---
duration_ms: 1.230005
type: 'test'
...
# Subtest: uses video author
ok 2 - uses video author
---
duration_ms: 0.496707
type: 'test'
...
# Subtest: derives npub from pubkey
ok 3 - derives npub from pubkey
---
duration_ms: 0.234671
type: 'test'
...
1..3
ok 4 - buildSimilarCardIdentity
---
duration_ms: 2.53546
type: 'test'
...
# Subtest: prepareSimilarVideoCard
# Subtest: wires up onPlay
ok 1 - wires up onPlay
---
duration_ms: 0.348381
type: 'test'
...
1..1
ok 5 - prepareSimilarVideoCard
---
duration_ms: 0.724345
type: 'test'
...
1..5
# tests 17
# suites 0
# pass 17
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 73.521197
→ Running tests/ui/engagement-controller.test.mjs
TAP version 13
# Subtest: EngagementController
# Subtest: handleRepostAction - should show error if no event ID is available
ok 1 - handleRepostAction - should show error if no event ID is available
---
duration_ms: 2.83818
type: 'test'
...
# Subtest: handleRepostAction - should call repostEvent with correct parameters
ok 2 - handleRepostAction - should call repostEvent with correct parameters
---
duration_ms: 0.801469
type: 'test'
...
# Subtest: handleRepostAction - should handle failure from repostEvent
ok 3 - handleRepostAction - should handle failure from repostEvent
---
duration_ms: 0.617609
type: 'test'
...
# Subtest: handleRepostAction - should use currentVideoPointer when in modal context
ok 4 - handleRepostAction - should use currentVideoPointer when in modal context
---
duration_ms: 0.659489
type: 'test'
...
# Subtest: handleMirrorAction - should show error if video has no URL
ok 5 - handleMirrorAction - should show error if video has no URL
---
duration_ms: 0.955105
type: 'test'
...
# Subtest: handleMirrorAction - should call mirrorVideoEvent when URL is provided
ok 6 - handleMirrorAction - should call mirrorVideoEvent when URL is provided
---
duration_ms: 0.605678
type: 'test'
...
# Subtest: handleMirrorAction - should prevent mirroring private videos
ok 7 - handleMirrorAction - should prevent mirroring private videos
---
duration_ms: 0.345144
type: 'test'
...
# Subtest: handleEnsurePresenceAction - should show error if no event ID
ok 8 - handleEnsurePresenceAction - should show error if no event ID
---
duration_ms: 0.637397
type: 'test'
...
# Subtest: handleEnsurePresenceAction - should handle throttled response
ok 9 - handleEnsurePresenceAction - should handle throttled response
---
duration_ms: 0.804655
type: 'test'
...
# Subtest: handleEnsurePresenceAction - should show success on successful rebroadcast
ok 10 - handleEnsurePresenceAction - should show success on successful rebroadcast
---
duration_ms: 0.584021
type: 'test'
...
1..10
ok 1 - EngagementController
---
duration_ms: 12.71314
type: 'test'
...
1..1
# tests 11
# suites 0
# pass 11
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 30.391049
→ Running tests/ui/profile-modal-moderation-settings.test.mjs
TAP version 13
# Subtest: moderation settings save updates service and disables control
ok 1 - moderation settings save updates service and disables control
---
duration_ms: 147.620961
type: 'test'
...
# Subtest: moderation reset restores defaults and clearing inputs uses defaults
ok 2 - moderation reset restores defaults and clearing inputs uses defaults
---
duration_ms: 36.918342
type: 'test'
...
# Subtest: guest fallback uses config moderation defaults
ok 3 - guest fallback uses config moderation defaults
---
duration_ms: 20.98265
type: 'test'
...
1..3
# tests 3
# suites 0
# pass 3
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 219.284121
→ Running tests/ui/similar-content-card.test.mjs
TAP version 13
# Subtest: cached thumbnails reuse existing src without lazy-loading
ok 1 - cached thumbnails reuse existing src without lazy-loading
---
duration_ms: 239.782537
type: 'test'
...
# Subtest: uncached thumbnails use fallback, cache on load, and retain blur state
ok 2 - uncached thumbnails use fallback, cache on load, and retain blur state
---
duration_ms: 43.697704
type: 'test'
...
# Subtest: primary clicks trigger onPlay while modifiers and right clicks do not
ok 3 - primary clicks trigger onPlay while modifiers and right clicks do not
---
duration_ms: 20.24453
type: 'test'
...
# Subtest: author identity fields render supplied values and datasets
ok 4 - author identity fields render supplied values and datasets
---
duration_ms: 16.904346
type: 'test'
...
# Subtest: view counter wiring exposes pointer datasets
ok 5 - view counter wiring exposes pointer datasets
---
duration_ms: 15.674805
type: 'test'
...
1..5
# tests 5
# suites 0
# pass 5
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 350.370667
→ Running tests/ui/tag-pill-list.test.mjs
TAP version 13
# Subtest: renderTagPillStrip builds buttons with labels and icons
ok 1 - renderTagPillStrip builds buttons with labels and icons
---
duration_ms: 96.82217
type: 'test'
...
# Subtest: renderTagPillStrip applies preference state styling
ok 2 - renderTagPillStrip applies preference state styling
---
duration_ms: 12.92567
type: 'test'
...
# Subtest: renderTagPillStrip wires the activation callback
ok 3 - renderTagPillStrip wires the activation callback
---
duration_ms: 10.52196
type: 'test'
...
1..3
# tests 3
# suites 0
# pass 3
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 133.170243
→ Running tests/ui/torrentStatusController.test.mjs
TAP version 13
# Subtest: TorrentStatusController throws if accessor is missing
ok 1 - TorrentStatusController throws if accessor is missing
---
duration_ms: 1.487921
type: 'test'
...
# Subtest: TorrentStatusController updates video modal and calls onRemovePoster
ok 2 - TorrentStatusController updates video modal and calls onRemovePoster
---
duration_ms: 0.640655
type: 'test'
...
# Subtest: TorrentStatusController handles missing modal gracefully
ok 3 - TorrentStatusController handles missing modal gracefully
---
duration_ms: 0.346886
type: 'test'
...
1..3
# tests 3
# suites 0
# pass 3
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 15.753871
→ Running tests/ui/url-health-controller.test.mjs
TAP version 13
# Subtest: UrlHealthController
# Subtest: probeUrl returns ok for valid URL
ok 1 - probeUrl returns ok for valid URL
---
duration_ms: 100.787371
type: 'test'
...
# Subtest: probeUrl returns error for 404
ok 2 - probeUrl returns error for 404
---
duration_ms: 21.221713
type: 'test'
...
# Subtest: handleUrlHealthBadge updates badge
ok 3 - handleUrlHealthBadge updates badge
---
duration_ms: 75.855241
type: 'test'
...
# Subtest: getUrlHealthPlaceholderMarkup returns string
ok 4 - getUrlHealthPlaceholderMarkup returns string
---
duration_ms: 11.734257
type: 'test'
...
1..4
ok 1 - UrlHealthController
---
duration_ms: 211.847
type: 'suite'
...
1..1
# tests 4
# suites 1
# pass 4
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 220.023171
→ Running tests/unit/app_guard_logic.test.mjs
TAP version 13
# Subtest: Application.resetTorrentStats logic
# Subtest: does not throw when videoModal is null
ok 1 - does not throw when videoModal is null
---
duration_ms: 0.948271
type: 'test'
...
# Subtest: does not throw when videoModal is undefined
ok 2 - does not throw when videoModal is undefined
---
duration_ms: 1.431554
type: 'test'
...
# Subtest: does not throw when videoModal lacks resetStats
ok 3 - does not throw when videoModal lacks resetStats
---
duration_ms: 0.253518
type: 'test'
...
# Subtest: calls resetStats when available
ok 4 - calls resetStats when available
---
duration_ms: 0.304177
type: 'test'
...
1..4
ok 1 - Application.resetTorrentStats logic
---
duration_ms: 4.972786
type: 'test'
...
1..1
# tests 5
# suites 0
# pass 5
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 18.915023
→ Running tests/unit/editModalController.test.mjs
TAP version 13
# Subtest: EditModalController
# Subtest: open()
# Subtest: should resolve target and open modal if authorized
ok 1 - should resolve target and open modal if authorized
---
duration_ms: 2.384748
type: 'test'
...
# Subtest: should show error if not logged in
ok 2 - should show error if not logged in
---
duration_ms: 0.876537
type: 'test'
...
# Subtest: should show error if user does not own video
ok 3 - should show error if user does not own video
---
duration_ms: 0.552771
type: 'test'
...
# Subtest: should handle modal load errors
ok 4 - should handle modal load errors
---
duration_ms: 0.980764
type: 'test'
...
1..4
ok 1 - open()
---
duration_ms: 6.014044
type: 'suite'
...
# Subtest: handleSubmit()
# Subtest: should handle successful submission
ok 1 - should handle successful submission
---
duration_ms: 1.016892
type: 'test'
...
# Subtest: should show error if not logged in
ok 2 - should show error if not logged in
---
duration_ms: 0.488905
type: 'test'
...
# Subtest: should handle submission errors
ok 3 - should handle submission errors
---
duration_ms: 1.385083
type: 'test'
...
1..3
ok 2 - handleSubmit()
---
duration_ms: 3.607268
type: 'suite'
...
1..2
ok 1 - EditModalController
---
duration_ms: 10.408835
type: 'suite'
...
1..1
# tests 7
# suites 3
# pass 7
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 28.906156
→ Running tests/unit/hashChangeHandler.test.mjs
TAP version 13
# Subtest: createHashChangeHandler
# Subtest: loads default view if hash is empty
ok 1 - loads default view if hash is empty
---
duration_ms: 1.188263
type: 'test'
...
# Subtest: redirects legacy view
ok 2 - redirects legacy view
---
duration_ms: 0.26584
type: 'test'
...
# Subtest: skips redundant reload
ok 3 - skips redundant reload
---
duration_ms: 0.284841
type: 'test'
...
1..3
ok 1 - createHashChangeHandler
---
duration_ms: 3.618637
type: 'test'
...
1..1
# tests 4
# suites 0
# pass 4
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 15.600244
→ Running tests/unit/security-config.test.mjs
TAP version 13
# Subtest: Security configuration: Development mode should be disabled in production
ok 1 - Security configuration: Development mode should be disabled in production
---
duration_ms: 2.430889
type: 'test'
...
# Subtest: Security configuration: Verbose diagnostics should be disabled in production
ok 2 - Security configuration: Verbose diagnostics should be disabled in production
---
duration_ms: 0.198032
type: 'test'
...
1..2
# tests 2
# suites 0
# pass 2
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 13.906388
→ Running tests/unit/ui/videoModalController.test.mjs
TAP version 13
# Subtest: VideoModalController bindEvents attaches listeners
ok 1 - VideoModalController bindEvents attaches listeners
---
duration_ms: 1.543004
type: 'test'
...
# Subtest: VideoModalController handleShareNostr triggers callback
ok 2 - VideoModalController handleShareNostr triggers callback
---
duration_ms: 0.447022
type: 'test'
...
# Subtest: VideoModalController handleCopyCdn triggers clipboard write
ok 3 - VideoModalController handleCopyCdn triggers clipboard write
---
duration_ms: 6.194991
type: 'test'
...
# Subtest: VideoModalController handleCopyMagnet triggers callback
ok 4 - VideoModalController handleCopyMagnet triggers callback
---
duration_ms: 0.346044
type: 'test'
...
# Subtest: VideoModalController handleSourceSwitch calls playVideoWithFallback
ok 5 - VideoModalController handleSourceSwitch calls playVideoWithFallback
---
duration_ms: 2.184679
type: 'test'
...
1..5
# tests 5
# suites 0
# pass 5
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 17.91256
→ Running tests/utils/hex.test.mjs
TAP version 13
# Subtest: hex utils
# Subtest: normalizeHexString
# Subtest: should return empty string for non-string inputs
ok 1 - should return empty string for non-string inputs
---
duration_ms: 0.949331
type: 'test'
...
# Subtest: should return empty string for empty or whitespace-only strings
ok 2 - should return empty string for empty or whitespace-only strings
---
duration_ms: 0.275501
type: 'test'
...
# Subtest: should trim and lowercase valid hex strings
ok 3 - should trim and lowercase valid hex strings
---
duration_ms: 0.178834
type: 'test'
...
1..3
ok 1 - normalizeHexString
---
duration_ms: 2.423874
type: 'suite'
...
# Subtest: aliases
# Subtest: should export normalizeHexId as an alias
ok 1 - should export normalizeHexId as an alias
---
duration_ms: 0.228652
type: 'test'
...
# Subtest: should export normalizeHexPubkey as an alias
ok 2 - should export normalizeHexPubkey as an alias
---
duration_ms: 0.132261
type: 'test'
...
1..2
ok 2 - aliases
---
duration_ms: 0.62027
type: 'suite'
...
# Subtest: HEX64_REGEX
# Subtest: should match valid 64-character hex strings
ok 1 - should match valid 64-character hex strings
---
duration_ms: 0.489978
type: 'test'
...
# Subtest: should not match strings with incorrect length
ok 2 - should not match strings with incorrect length
---
duration_ms: 0.471827
type: 'test'
...
# Subtest: should not match strings with non-hex characters
ok 3 - should not match strings with non-hex characters
---
duration_ms: 0.178185
type: 'test'
...
# Subtest: should not match empty strings
ok 4 - should not match empty strings
---
duration_ms: 0.234511
type: 'test'
...
1..4
ok 3 - HEX64_REGEX
---
duration_ms: 2.133813
type: 'suite'
...
# Subtest: normalizeHexHash
# Subtest: should return empty string for non-string inputs
ok 1 - should return empty string for non-string inputs
---
duration_ms: 0.317789
type: 'test'
...
# Subtest: should return empty string for invalid hex
ok 2 - should return empty string for invalid hex
---
duration_ms: 0.127703
type: 'test'
...
# Subtest: should return normalized hex for valid inputs
ok 3 - should return normalized hex for valid inputs
---
duration_ms: 0.109855
type: 'test'
...
1..3
ok 4 - normalizeHexHash
---
duration_ms: 0.778391
type: 'suite'
...
1..4
ok 1 - hex utils
---
duration_ms: 7.121221
type: 'suite'
...
1..1
# tests 12
# suites 5
# pass 12
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 29.708245
→ Running tests/utils/profileMedia.test.mjs
TAP version 13
# Subtest: sanitizeProfileMediaUrl handles non-string inputs
ok 1 - sanitizeProfileMediaUrl handles non-string inputs
---
duration_ms: 1.486125
type: 'test'
...
# Subtest: sanitizeProfileMediaUrl handles empty or whitespace-only strings
ok 2 - sanitizeProfileMediaUrl handles empty or whitespace-only strings
---
duration_ms: 0.259288
type: 'test'
...
# Subtest: sanitizeProfileMediaUrl trims whitespace and removes quotes
ok 3 - sanitizeProfileMediaUrl trims whitespace and removes quotes
---
duration_ms: 0.831109
type: 'test'
...
# Subtest: sanitizeProfileMediaUrl allows data:image/ URLs
ok 4 - sanitizeProfileMediaUrl allows data:image/ URLs
---
duration_ms: 0.173274
type: 'test'
...
# Subtest: sanitizeProfileMediaUrl allows blob: URLs
ok 5 - sanitizeProfileMediaUrl allows blob: URLs
---
duration_ms: 0.453106
type: 'test'
...
# Subtest: sanitizeProfileMediaUrl rejects specific placeholder images
ok 6 - sanitizeProfileMediaUrl rejects specific placeholder images
---
duration_ms: 0.176684
type: 'test'
...
# Subtest: sanitizeProfileMediaUrl normalizes IPFS URLs
ok 7 - sanitizeProfileMediaUrl normalizes IPFS URLs
---
duration_ms: 0.361848
type: 'test'
...
# Subtest: sanitizeProfileMediaUrl handles protocol-relative URLs
ok 8 - sanitizeProfileMediaUrl handles protocol-relative URLs
---
duration_ms: 0.233362
type: 'test'
...
# Subtest: sanitizeProfileMediaUrl allows relative paths
ok 9 - sanitizeProfileMediaUrl allows relative paths
---
duration_ms: 0.498777
type: 'test'
...
# Subtest: sanitizeProfileMediaUrl adds protocol to domains and localhost
ok 10 - sanitizeProfileMediaUrl adds protocol to domains and localhost
---
duration_ms: 1.011359
type: 'test'
...
# Subtest: sanitizeProfileMediaUrl coerces http to https except for localhost
ok 11 - sanitizeProfileMediaUrl coerces http to https except for localhost
---
duration_ms: 0.296965
type: 'test'
...
# Subtest: sanitizeProfileMediaUrl rejects unsupported patterns
ok 12 - sanitizeProfileMediaUrl rejects unsupported patterns
---
duration_ms: 0.176928
type: 'test'
...
1..12
# tests 12
# suites 0
# pass 12
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 29.775616
→ Running tests/utils/videoPointer.test.mjs
TAP version 13
# Subtest: resolveVideoPointer returns address pointer with dTag
ok 1 - resolveVideoPointer returns address pointer with dTag
---
duration_ms: 2.81294
type: 'test'
...
# Subtest: resolveVideoPointer returns address pointer with videoRootId
ok 2 - resolveVideoPointer returns address pointer with videoRootId
---
duration_ms: 0.371316
type: 'test'
...
# Subtest: resolveVideoPointer returns event pointer with fallbackEventId
ok 3 - resolveVideoPointer returns event pointer with fallbackEventId
---
duration_ms: 0.232669
type: 'test'
...
# Subtest: resolveVideoPointer prioritizes dTag over videoRootId
ok 4 - resolveVideoPointer prioritizes dTag over videoRootId
---
duration_ms: 0.185584
type: 'test'
...
# Subtest: resolveVideoPointer prioritizes videoRootId over fallbackEventId
ok 5 - resolveVideoPointer prioritizes videoRootId over fallbackEventId
---
duration_ms: 0.194959
type: 'test'
...
# Subtest: resolveVideoPointer prioritizes dTag over fallbackEventId
ok 6 - resolveVideoPointer prioritizes dTag over fallbackEventId
---
duration_ms: 0.261415
type: 'test'
...
# Subtest: resolveVideoPointer includes relay in pointer
ok 7 - resolveVideoPointer includes relay in pointer
---
duration_ms: 0.278699
type: 'test'
...
# Subtest: resolveVideoPointer normalizes inputs
ok 8 - resolveVideoPointer normalizes inputs
---
duration_ms: 0.181043
type: 'test'
...
# Subtest: resolveVideoPointer uses default kind when kind is missing or invalid
ok 9 - resolveVideoPointer uses default kind when kind is missing or invalid
---
duration_ms: 0.702891
type: 'test'
...
# Subtest: resolveVideoPointer returns null for invalid inputs
ok 10 - resolveVideoPointer returns null for invalid inputs
---
duration_ms: 0.80627
type: 'test'
...
1..10
# tests 10
# suites 0
# pass 10
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 25.645203
→ Running tests/video-card-source-visibility.test.mjs
TAP version 13
# Subtest: updateVideoCardSourceVisibility hides non-owner cards without healthy sources and restores visibility
ok 1 - updateVideoCardSourceVisibility hides non-owner cards without healthy sources and restores visibility
---
duration_ms: 92.256459
type: 'test'
...
# Subtest: VideoCard hides cards without playable sources until a healthy CDN update arrives
ok 2 - VideoCard hides cards without playable sources until a healthy CDN update arrives
---
duration_ms: 40.550504
type: 'test'
...
# Subtest: VideoCard.closeMoreMenu only restores focus when the trigger was expanded
ok 3 - VideoCard.closeMoreMenu only restores focus when the trigger was expanded
---
duration_ms: 20.016359
type: 'test'
...
# Subtest: VideoCard.closeSettingsMenu only restores focus when the trigger was expanded
ok 4 - VideoCard.closeSettingsMenu only restores focus when the trigger was expanded
---
duration_ms: 17.325929
type: 'test'
...
1..4
# tests 4
# suites 0
# pass 4
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 184.021264
→ Running tests/video-modal-controller.test.mjs
TAP version 13
# Subtest: VideoModalController
# Subtest: ensureVideoModalReady throws if modal is missing
ok 1 - ensureVideoModalReady throws if modal is missing
---
duration_ms: 1.681314
type: 'test'
...
# Subtest: ensureVideoModalReady loads modal if needs rehydrate
ok 2 - ensureVideoModalReady loads modal if needs rehydrate
---
duration_ms: 0.516535
type: 'test'
...
# Subtest: showModalWithPoster uses provided video
ok 3 - showModalWithPoster uses provided video
---
duration_ms: 0.817263
type: 'test'
...
# Subtest: showModalWithPoster falls back to current video
ok 4 - showModalWithPoster falls back to current video
---
duration_ms: 0.497957
type: 'test'
...
# Subtest: forceRemoveModalPoster calls modal method
ok 5 - forceRemoveModalPoster calls modal method
---
duration_ms: 0.278304
type: 'test'
...
1..5
ok 1 - VideoModalController
---
duration_ms: 5.719221
type: 'test'
...
1..1
# tests 6
# suites 0
# pass 6
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 19.203363
→ Running tests/video-modal-tags.test.mjs
TAP version 13
# Subtest: video modal rehydrates and plays through a connected element across views
ok 1 - video modal rehydrates and plays through a connected element across views
---
duration_ms: 344.609439
type: 'test'
...
# Subtest: torrent-only playback resets the modal video before the next hosted session
ok 2 - torrent-only playback resets the modal video before the next hosted session
---
duration_ms: 24.708146
type: 'test'
...
[VideoModal] Cleared loading poster (close).
[VideoModal] Cleared loading poster (close).
[VideoModal] Cleared loading poster (close).
[VideoModal] Cleared loading poster (close).
[VideoModal] Cleared loading poster (close).
# Subtest: backdrop data-dismiss closes the modal and restores focus
ok 3 - backdrop data-dismiss closes the modal and restores focus
---
duration_ms: 323.341032
type: 'test'
...
# Subtest: Escape key closes the modal and returns focus to the trigger
ok 4 - Escape key closes the modal and returns focus to the trigger
---
duration_ms: 110.850796
type: 'test'
...
# Subtest: video modal sticky navigation responds to scroll direction
ok 5 - video modal sticky navigation responds to scroll direction
---
duration_ms: 117.972697
type: 'test'
...
# Subtest: video modal video shell is not sticky at mobile breakpoints
ok 6 - video modal video shell is not sticky at mobile breakpoints
---
duration_ms: 98.901188
type: 'test'
...
# Subtest: video modal toggles document scroll locking on open/close
ok 7 - video modal toggles document scroll locking on open/close
---
duration_ms: 87.029389
type: 'test'
...
# Subtest: video modal comments region exposes aria landmarks and stays in the focus order
ok 8 - video modal comments region exposes aria landmarks and stays in the focus order
---
duration_ms: 147.166832
type: 'test'
...
# Subtest: video modal zap dialog updates aria state while toggling
ok 9 - video modal zap dialog updates aria state while toggling
---
duration_ms: 248.293715
type: 'test'
...
# Subtest: video modal inherits design system mode when loaded dynamically
ok 10 - video modal inherits design system mode when loaded dynamically
---
duration_ms: 70.876445
type: 'test'
...
# Subtest: VideoModal renders tag metadata and toggles visibility
ok 11 - VideoModal renders tag metadata and toggles visibility
---
duration_ms: 79.99425
type: 'test'
...
1..11
# tests 11
# suites 0
# pass 11
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 1669.862147
→ Running tests/video-settings-menu-controller.test.mjs
TAP version 13
# Subtest: VideoSettingsMenuController - requestMenu opens popover
ok 1 - VideoSettingsMenuController - requestMenu opens popover
---
duration_ms: 1.535652
type: 'test'
...
# Subtest: VideoSettingsMenuController - closeMenu closes popover
ok 2 - VideoSettingsMenuController - closeMenu closes popover
---
duration_ms: 0.32641
type: 'test'
...
# Subtest: VideoSettingsMenuController - requestMenu toggles if open
ok 3 - VideoSettingsMenuController - requestMenu toggles if open
---
duration_ms: 0.404461
type: 'test'
...
# Subtest: VideoSettingsMenuController - closeAll closes all popovers
ok 4 - VideoSettingsMenuController - closeAll closes all popovers
---
duration_ms: 0.507031
type: 'test'
...
1..4
# tests 4
# suites 0
# pass 4
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 15.514533
✔ All unit tests passed
Result: ✅ Passed
Command: npm run test:smoke
> [email protected] test:smoke
> node scripts/agent/smoke-test.mjs
[2026-02-15T03:46:42.165Z] --- Smoke Test Started ---
[2026-02-15T03:46:42.166Z] Ephemeral Pubkey: d90a515374fb210fe1c82ac6b9415491e2b3e1bd3beb85cf97fdfa23e31cc183
Load Test Relay starting on port 8899
[2026-02-15T03:46:42.169Z] Starting HTTP Server...
[2026-02-15T03:46:43.257Z] HTTP Server is ready.
[2026-02-15T03:46:43.257Z] Initializing Node Client...
[2026-02-15T03:46:43.282Z] Node Client connected.
[2026-02-15T03:46:43.282Z] Node: Publishing Video Post...
[2026-02-15T03:46:43.296Z] Node: Published Video ID: 15c1fad90d9cf7f756a1fc255a8e1fb1d7be6d58ffc78206fd2e0c3904e79477
[2026-02-15T03:46:43.296Z] Node: Publishing Encrypted DM...
[2026-02-15T03:46:43.313Z] Node: Published DM ID: d97bac8d676e9b4b2e515082d3468a2879529bdef6a3f4f97beaf8484e45e6ae
[2026-02-15T03:46:43.313Z] Browser: Launching...
[2026-02-15T03:46:43.327Z] --- Smoke Test FAILED: browserType.launch: Executable doesn't exist at /home/jules/.cache/ms-playwright/chromium_headless_shell-1208/chrome-headless-shell-linux64/chrome-headless-shell
╔═════════════════════════════════════════════════════════════════════════╗
║ Looks like Playwright Test or Playwright was just installed or updated. ║
║ Please run the following command to download new browsers: ║
║ ║
║ npx playwright install ║
║ ║
║ <3 Playwright Team ║
╚═════════════════════════════════════════════════════════════════════════╝ ---
[2026-02-15T03:46:43.327Z] browserType.launch: Executable doesn't exist at /home/jules/.cache/ms-playwright/chromium_headless_shell-1208/chrome-headless-shell-linux64/chrome-headless-shell
╔═════════════════════════════════════════════════════════════════════════╗
║ Looks like Playwright Test or Playwright was just installed or updated. ║
║ Please run the following command to download new browsers: ║
║ ║
║ npx playwright install ║
║ ║
║ <3 Playwright Team ║
╚═════════════════════════════════════════════════════════════════════════╝
at runSmokeTest (/app/scripts/agent/smoke-test.mjs:138:34)
[2026-02-15T03:46:43.327Z] Cleaning up...
Result: ❌ Failed (Exit Code: 1)
Command: npm run format
> [email protected] format
> prettier --cache --write css/tailwind.source.css css/tokens.css css/docs.css "docs/**/*.html" tailwind.config.cjs README.md CONTRIBUTING.md
css/tailwind.source.css 1249ms (unchanged)
css/tokens.css 123ms (unchanged)
css/docs.css 14ms (unchanged)
docs/kitchen-sink.html 192ms (unchanged)
docs/moderation/fixtures/index.html 153ms (unchanged)
docs/popover-scenarios.html 39ms (unchanged)
tailwind.config.cjs 40ms (unchanged)
README.md 232ms (unchanged)
CONTRIBUTING.md 54ms (unchanged)
Result: ✅ Passed
Command: npm run lint
> [email protected] lint
> npm run lint:css && npm run lint:hex && npm run lint:inline-styles && npm run lint:tokens && npm run lint:tailwind-brackets && npm run lint:tailwind-colors && npm run lint:file-size && npm run lint:innerhtml && npm run lint:assets && npm run lint:sw-compat
> [email protected] lint:css
> stylelint css/tailwind.source.css css/tokens.css css/docs.css
> [email protected] lint:hex
> node scripts/check-hex.js
> [email protected] lint:inline-styles
> node scripts/check-inline-styles.mjs
No inline style usage found.
> [email protected] lint:tokens
> node scripts/check-design-tokens.mjs --check=tokens
> [email protected] lint:tailwind-brackets
> node scripts/check-design-tokens.mjs --check=brackets
> [email protected] lint:tailwind-colors
> node scripts/check-tailwind-colors.mjs
> [email protected] lint:file-size
> node scripts/check-file-size.mjs
> [email protected] lint:innerhtml
> node scripts/check-innerhtml.mjs
> [email protected] lint:assets
> node scripts/check-asset-references.mjs
Asset reference lint passed: all local CSS/JS references are hashed or allowlisted, and no ?v= query cache-busting remains.
> [email protected] lint:sw-compat
> node scripts/check-sw-compat.mjs
[sw-compat] Skipping check: unable to determine HEAD^ or git history unavailable.
[sw-compat] No commit delta found; skipping compatibility check.
Result: ✅ Passed