diff options
| author | Adam Soltys <[email protected]> | 2026-10-07 12:02:51 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-10-07 12:02:51 -0700 |
| commit | 86d9818abfc160dac031bb292abe28d6f28fc624 (patch) | |
| tree | a96983d60fb0f4dcb92a04c7aba50761da05cf9f /tools | |
| parent | b415b1fb31bd734619e1da3c88ca7e3822151c87 (diff) | |
| download | coinosv3-86d9818abfc160dac031bb292abe28d6f28fc624.tar.gz coinosv3-86d9818abfc160dac031bb292abe28d6f28fc624.zip | |
Browser tests: public relays answered in the page, never reached
A suite run let ~90 throwaway test wallets subscribe and publish on public
relays from one home address, and nos.lol and relay.damus.io banned it.
tools/puppeteer.js wraps puppeteer-core: every socket to a non-local host
gets an in-page stand-in relay (EVENT -> OK, REQ -> EOSE). Tests that read
real content launch with { relays: 'read' } (real reads, publishes answered
in place); feed-live, chat-layout and cordn-ui launch with { relays: 'live' }.
LIVE_RELAYS=read|1|host,... overrides per run.
profile-key-qr: measure the QR again if a repaint swapped it out.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'tools')
91 files changed, 241 insertions, 113 deletions
diff --git a/tools/account-access-test.js b/tools/account-access-test.js index 4a83fd0..c9af32e 100644 --- a/tools/account-access-test.js +++ b/tools/account-access-test.js @@ -2,7 +2,7 @@ // account switching and logout. Uses an unfunded, generated public key only. // Run: bun tools/account-access-test.js import assert from 'node:assert/strict'; -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import { generateMnemonic } from '@scure/bip39'; import { wordlist } from '@scure/bip39/wordlists/english'; import { accountXpubFor, cacheKeyFor } from '../src/wallet.js'; diff --git a/tools/amber-login-test.js b/tools/amber-login-test.js index 0b4c3c9..9da49a8 100644 --- a/tools/amber-login-test.js +++ b/tools/amber-login-test.js @@ -3,7 +3,7 @@ // pasted); creating the wallet then makes an encrypt and a sign round trip. // The intent launcher and the clipboard are stubbed; relays answer nothing. // Run: bun tools/amber-login-test.js -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { npubEncode } from 'nostr-tools/nip19'; import { buildHtml } from '../build.js'; diff --git a/tools/ark/cooperative-exit-ui-test.js b/tools/ark/cooperative-exit-ui-test.js index d8f990d..d8fa0d8 100644 --- a/tools/ark/cooperative-exit-ui-test.js +++ b/tools/ark/cooperative-exit-ui-test.js @@ -1,7 +1,7 @@ // Real app navigation/forms with a local Ark manager stub; never transfers funds. // Run: bun tools/ark/cooperative-exit-ui-test.js import assert from 'node:assert/strict'; -import puppeteer from 'puppeteer-core'; +import puppeteer from '../puppeteer.js'; import { generateMnemonic } from '@scure/bip39'; import { wordlist } from '@scure/bip39/wordlists/english'; diff --git a/tools/ark/gift-shot.js b/tools/ark/gift-shot.js index aac6809..f8c96a8 100644 --- a/tools/ark/gift-shot.js +++ b/tools/ark/gift-shot.js @@ -2,7 +2,7 @@ // wallet, open its link in the same browser (wallet 1 still in session), // capture the "choose where to receive" screen. import { spawn, execSync } from 'node:child_process'; -import puppeteer from 'puppeteer-core'; +import puppeteer from '../puppeteer.js'; import { generateMnemonic } from '@scure/bip39'; import { wordlist } from '@scure/bip39/wordlists/english'; diff --git a/tools/ark/offboard-minimum-test.js b/tools/ark/offboard-minimum-test.js index 9593089..94d166c 100644 --- a/tools/ark/offboard-minimum-test.js +++ b/tools/ark/offboard-minimum-test.js @@ -3,7 +3,7 @@ // note for when the ASP actually failed to co-operate. Local manager stub. // Run: bun tools/ark/offboard-minimum-test.js import assert from 'node:assert/strict'; -import puppeteer from 'puppeteer-core'; +import puppeteer from '../puppeteer.js'; import { generateMnemonic } from '@scure/bip39'; import { wordlist } from '@scure/bip39/wordlists/english'; diff --git a/tools/ark/renew-confirm-ui-test.js b/tools/ark/renew-confirm-ui-test.js index e5a68c8..698c424 100644 --- a/tools/ark/renew-confirm-ui-test.js +++ b/tools/ark/renew-confirm-ui-test.js @@ -5,7 +5,7 @@ // 2026-10-03 report; nothing is sent anywhere. // Run: bun tools/ark/renew-confirm-ui-test.js import assert from 'node:assert/strict'; -import puppeteer from 'puppeteer-core'; +import puppeteer from '../puppeteer.js'; import { generateMnemonic } from '@scure/bip39'; import { wordlist } from '@scure/bip39/wordlists/english'; diff --git a/tools/ark/ui-shots.js b/tools/ark/ui-shots.js index 5331ab8..cda336a 100644 --- a/tools/ark/ui-shots.js +++ b/tools/ark/ui-shots.js @@ -1,7 +1,7 @@ // Screenshot run: import a wallet, receive over Ark, show the balance card + // Ark settings card. Writes PNGs to /tmp/ark-ui-*.png. import { spawn, execSync } from 'node:child_process'; -import puppeteer from 'puppeteer-core'; +import puppeteer from '../puppeteer.js'; import { generateMnemonic } from '@scure/bip39'; import { wordlist } from '@scure/bip39/wordlists/english'; diff --git a/tools/ark/ui-test.js b/tools/ark/ui-test.js index e4e2e5a..75d3fc5 100644 --- a/tools/ark/ui-test.js +++ b/tools/ark/ui-test.js @@ -9,7 +9,7 @@ // Usage: bun tools/ark/ui-test.js (ark regtest stack up) import { execSync } from 'node:child_process'; -import puppeteer from 'puppeteer-core'; +import puppeteer from '../puppeteer.js'; import { generateMnemonic } from '@scure/bip39'; import { wordlist } from '@scure/bip39/wordlists/english'; import { ALICE, aliceReady, aliceBalance } from './alice.js'; diff --git a/tools/avatar-profile-test.js b/tools/avatar-profile-test.js index 4ed1b25..5c5293d 100644 --- a/tools/avatar-profile-test.js +++ b/tools/avatar-profile-test.js @@ -3,7 +3,7 @@ // wherever the reader was in the feed. Back returns to the same place. // // Run: bun tools/avatar-profile-test.js -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import { generateMnemonic } from '@scure/bip39'; import { wordlist } from '@scure/bip39/wordlists/english'; import { cacheKeyFor } from '../src/wallet.js'; diff --git a/tools/avatar-thumb-test.js b/tools/avatar-thumb-test.js index e7ff843..4a13f33 100644 --- a/tools/avatar-thumb-test.js +++ b/tools/avatar-thumb-test.js @@ -9,7 +9,7 @@ // and that a refresh of the profile doesn't throw the thumbnail away. // // Run: bun tools/avatar-thumb-test.js -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import { generateMnemonic } from '@scure/bip39'; import { wordlist } from '@scure/bip39/wordlists/english'; import { seedPubkey } from '../src/nostr.js'; diff --git a/tools/boot-balance-test.js b/tools/boot-balance-test.js index 482149b..96a60d2 100644 --- a/tools/boot-balance-test.js +++ b/tools/boot-balance-test.js @@ -10,7 +10,7 @@ // first appears, on a RELOAD (session restore — the case the user sees). // // Run: bun tools/boot-balance-test.js -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import { generateMnemonic } from '@scure/bip39'; import { wordlist } from '@scure/bip39/wordlists/english'; import { buildHtml } from '../build.js'; diff --git a/tools/boot-signin-flash-test.js b/tools/boot-signin-flash-test.js index 75f5b50..c9c42de 100644 --- a/tools/boot-signin-flash-test.js +++ b/tools/boot-signin-flash-test.js @@ -9,7 +9,7 @@ // for the welcome screen. // // Run: bun tools/boot-signin-flash-test.js -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import { generateMnemonic } from '@scure/bip39'; import { wordlist } from '@scure/bip39/wordlists/english'; import { buildHtml } from '../build.js'; diff --git a/tools/chat-layout-test.js b/tools/chat-layout-test.js index 875f75b..a6f4162 100644 --- a/tools/chat-layout-test.js +++ b/tools/chat-layout-test.js @@ -4,7 +4,7 @@ // composer below the fold. // // Run: bun tools/chat-layout-test.js -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import { generateMnemonic } from '@scure/bip39'; import { wordlist } from '@scure/bip39/wordlists/english'; import { buildHtml } from '../build.js'; @@ -20,7 +20,7 @@ const check = (n, c, d = '') => { console.log(` ${c ? '✓' : '✗'} ${n}${d ? ' const html = await buildHtml({ minify: true, pwa: false }); const server = Bun.serve({ port: 5229, fetch: () => new Response(html, { headers: { 'content-type': 'text/html' } }) }); -const browser = await puppeteer.launch({ executablePath: '/usr/bin/google-chrome', headless: 'new', args: ['--no-sandbox'] }); +const browser = await puppeteer.launch({ relays: 'live', executablePath: '/usr/bin/google-chrome', headless: 'new', args: ['--no-sandbox'] }); const page = await browser.newPage(); const click = (sel, t) => page.evaluate((s, x) => { const e = [...document.querySelectorAll(s)].find((n) => n.textContent.trim().toLowerCase().includes(x.toLowerCase())); if (e) { e.click(); return true; } return false; }, sel, t); const setInput = (sel, v) => page.evaluate((s, val) => { const e = document.querySelector(s); if (!e) return false; e.value = val; e.dispatchEvent(new Event('input', { bubbles: true })); return true; }, sel, v); diff --git a/tools/chat-watchdog-test.js b/tools/chat-watchdog-test.js index 16a9ae0..d3f17c8 100644 --- a/tools/chat-watchdog-test.js +++ b/tools/chat-watchdog-test.js @@ -10,7 +10,7 @@ // sockets are reached through a registry shimmed onto window.WebSocket. // // Run: bun tools/chat-watchdog-test.js -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import { generateMnemonic } from '@scure/bip39'; import { wordlist } from '@scure/bip39/wordlists/english'; import { buildHtml } from '../build.js'; @@ -21,7 +21,7 @@ const check = (n, c, d = '') => { console.log(` ${c ? '✓' : '✗'} ${n}${d ? ' const html = await buildHtml({ minify: true, pwa: false }); const server = Bun.serve({ port: 5232, fetch: () => new Response(html, { headers: { 'content-type': 'text/html' } }) }); -const browser = await puppeteer.launch({ executablePath: '/usr/bin/google-chrome', headless: 'new', args: ['--no-sandbox'] }); +const browser = await puppeteer.launch({ relays: 'read', executablePath: '/usr/bin/google-chrome', headless: 'new', args: ['--no-sandbox'] }); const page = await browser.newPage(); // every socket the page opens, so the test can kill them the way the OS does await page.evaluateOnNewDocument(() => { diff --git a/tools/comments-test.js b/tools/comments-test.js index 6801f44..5a73e6f 100644 --- a/tools/comments-test.js +++ b/tools/comments-test.js @@ -5,7 +5,7 @@ // are caught at the socket; nothing is published. // // Run: bun tools/comments-test.js -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import { SimplePool } from 'nostr-tools/pool'; import { buildHtml } from '../build.js'; const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); @@ -23,7 +23,7 @@ for (let i = 0; i < 3 && !(root && comments.length); i++) { if (!root || !comments.length) { console.log(' ✗ the article or its comments are gone from the relays', !!root, comments.length); process.exit(1); } const html = await buildHtml({ minify: true, pwa: false }); const server = Bun.serve({ port: 5283, fetch: () => new Response(html, { headers: { 'content-type': 'text/html' } }) }); -const browser = await puppeteer.launch({ executablePath: '/usr/bin/google-chrome', headless: 'new', args: ['--no-sandbox'] }); +const browser = await puppeteer.launch({ relays: 'read', executablePath: '/usr/bin/google-chrome', headless: 'new', args: ['--no-sandbox'] }); const page = await browser.newPage(); // The article and its comments, fetched above, are also handed to the app // on every relay socket that asks for them: the app reaching the same public diff --git a/tools/compose-preview-test.js b/tools/compose-preview-test.js index c943655..fdfb05b 100644 --- a/tools/compose-preview-test.js +++ b/tools/compose-preview-test.js @@ -8,7 +8,7 @@ // preview shows is what a real post shows, for the same text. // // Run: bun tools/compose-preview-test.js -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import { generateMnemonic } from '@scure/bip39'; import { wordlist } from '@scure/bip39/wordlists/english'; import { cacheKeyFor } from '../src/wallet.js'; diff --git a/tools/composer-media-test.js b/tools/composer-media-test.js index 6f30995..512b8bd 100644 --- a/tools/composer-media-test.js +++ b/tools/composer-media-test.js @@ -5,7 +5,7 @@ // paperclip, its URL landing in the draft. // // Run: bun tools/composer-media-test.js -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { buildHtml } from '../build.js'; const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); diff --git a/tools/cordn-ui-test.js b/tools/cordn-ui-test.js index 6948428..8fc707a 100644 --- a/tools/cordn-ui-test.js +++ b/tools/cordn-ui-test.js @@ -5,7 +5,7 @@ // cordn-interop-test.js). // // Run: bun tools/cordn-ui-test.js [screenshot.png] -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import { generateMnemonic } from '@scure/bip39'; import { wordlist } from '@scure/bip39/wordlists/english'; import { npubEncode } from 'nostr-tools/nip19'; @@ -29,7 +29,7 @@ const aliceRef = /ref=([0-9a-f]{64})/.exec(alice('gen-kp kp' + Date.now().toStri const html = await buildHtml({ minify: true, pwa: false }); const server = Bun.serve({ port: 5234, fetch: () => new Response(html, { headers: { 'content-type': 'text/html' } }) }); -const browser = await puppeteer.launch({ executablePath: '/usr/bin/google-chrome', headless: 'new', args: ['--no-sandbox'] }); +const browser = await puppeteer.launch({ relays: 'live', executablePath: '/usr/bin/google-chrome', headless: 'new', args: ['--no-sandbox'] }); const page = await browser.newPage(); const errs = []; page.on('pageerror', (e) => { errs.push(e.message); console.log(' [pageerror]', e.message); }); diff --git a/tools/emoji-test.js b/tools/emoji-test.js index d661329..c91cec0 100644 --- a/tools/emoji-test.js +++ b/tools/emoji-test.js @@ -4,7 +4,7 @@ // installs the pack. Read-only: nothing is posted. // // Run: bun tools/emoji-test.js [screenshot.png] -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import { generateMnemonic } from '@scure/bip39'; import { wordlist } from '@scure/bip39/wordlists/english'; import { buildHtml } from '../build.js'; @@ -16,7 +16,7 @@ const check = (n, c, d = '') => { console.log(` ${c ? '✓' : '✗'} ${n}${d ? ' const html = await buildHtml({ minify: true, pwa: false }); const server = Bun.serve({ port: 5231, fetch: () => new Response(html, { headers: { 'content-type': 'text/html' } }) }); -const browser = await puppeteer.launch({ executablePath: '/usr/bin/google-chrome', headless: 'new', args: ['--no-sandbox'] }); +const browser = await puppeteer.launch({ relays: 'read', executablePath: '/usr/bin/google-chrome', headless: 'new', args: ['--no-sandbox'] }); const page = await browser.newPage(); page.on('pageerror', (e) => console.log(' [pageerror]', e.message)); const click = (sel, t) => page.evaluate((s, x) => { const e = [...document.querySelectorAll(s)].find((n) => n.textContent.trim().toLowerCase().includes(x.toLowerCase())); if (e) { e.click(); return true; } return false; }, sel, t); diff --git a/tools/feed-bottom-nav-test.js b/tools/feed-bottom-nav-test.js index 4354e97..ea466fd 100644 --- a/tools/feed-bottom-nav-test.js +++ b/tools/feed-bottom-nav-test.js @@ -1,7 +1,7 @@ // Real feed/navigation views with local posts and no relay traffic. // Run: bun tools/app-bottom-nav-test.js import assert from 'node:assert/strict'; -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; const app = await Bun.file('src/app.js').text(); const dom = app.slice(app.indexOf('function h(tag,'), app.indexOf("const root = document.getElementById('app');")); const entry = ` diff --git a/tools/feed-choice-per-wallet-test.js b/tools/feed-choice-per-wallet-test.js index 0642bde..322388e 100644 --- a/tools/feed-choice-per-wallet-test.js +++ b/tools/feed-choice-per-wallet-test.js @@ -1,7 +1,7 @@ // The feed you were on is remembered per wallet: a new account starts on // Popular, not on whatever the last account on this device was reading. // Run: bun tools/feed-choice-per-wallet-test.js -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import { generateMnemonic } from '@scure/bip39'; import { wordlist } from '@scure/bip39/wordlists/english'; import { buildHtml } from '../build.js'; diff --git a/tools/feed-custom-test.js b/tools/feed-custom-test.js index c2c94de..ef595ff 100644 --- a/tools/feed-custom-test.js +++ b/tools/feed-custom-test.js @@ -4,7 +4,7 @@ // feed, and deleting a feed. No relays are contacted for real — the // WebSocket shim answers the subscriptions. // Run: bun tools/feed-custom-test.js -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import { generateMnemonic } from '@scure/bip39'; import { wordlist } from '@scure/bip39/wordlists/english'; import { cacheKeyFor } from '../src/wallet.js'; @@ -28,7 +28,7 @@ const PACK_NADDR = naddrEncode({ kind: 39089, pubkey: PACK.pubkey, identifier: ' const html = await buildHtml({ minify: true, pwa: false }); const server = Bun.serve({ port: 5297, fetch: () => new Response(html, { headers: { 'content-type': 'text/html' } }) }); -const browser = await puppeteer.launch({ executablePath: '/usr/bin/google-chrome', headless: 'new', args: ['--no-sandbox'] }); +const browser = await puppeteer.launch({ relays: 'read', executablePath: '/usr/bin/google-chrome', headless: 'new', args: ['--no-sandbox'] }); const page = await browser.newPage(); await page.evaluateOnNewDocument((pack) => { window.__reqs = []; diff --git a/tools/feed-editor-relays-test.js b/tools/feed-editor-relays-test.js index 5a808a7..c12a22f 100644 --- a/tools/feed-editor-relays-test.js +++ b/tools/feed-editor-relays-test.js @@ -2,7 +2,7 @@ // behind the feed and leave the form up), and it can make a firehose — // every post on the relays you name — as a saved feed. // Run: bun tools/feed-editor-relays-test.js -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import { generateMnemonic } from '@scure/bip39'; import { wordlist } from '@scure/bip39/wordlists/english'; import { buildHtml } from '../build.js'; diff --git a/tools/feed-follow-test.js b/tools/feed-follow-test.js index bae06a9..f0f688c 100644 --- a/tools/feed-follow-test.js +++ b/tools/feed-follow-test.js @@ -6,7 +6,7 @@ // way it would arrive from one. // // Run: bun tools/feed-follow-test.js -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import { generateMnemonic } from '@scure/bip39'; import { wordlist } from '@scure/bip39/wordlists/english'; import { cacheKeyFor } from '../src/wallet.js'; diff --git a/tools/feed-list-test.js b/tools/feed-list-test.js index 641d43d..6d60240 100644 --- a/tools/feed-list-test.js +++ b/tools/feed-list-test.js @@ -5,7 +5,7 @@ // unticking "Keep as a nostr list" sends the NIP-09 deletion. No relay is // written for real — the WebSocket shim swallows EVENT and answers OK. // Run: bun tools/feed-list-test.js -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { privateKeyFromSeedWords } from 'nostr-tools/nip06'; diff --git a/tools/feed-live-test.js b/tools/feed-live-test.js index 8f80d46..b9879d0 100644 --- a/tools/feed-live-test.js +++ b/tools/feed-live-test.js @@ -10,7 +10,7 @@ // notice takes you up to them when tapped. // // Run: bun tools/feed-live-test.js -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import { generateMnemonic } from '@scure/bip39'; import { wordlist } from '@scure/bip39/wordlists/english'; import { cacheKeyFor } from '../src/wallet.js'; @@ -41,7 +41,7 @@ const server = Bun.serve({ port: 5296, fetch: async (req) => { } return new Response(html, { headers: { 'content-type': 'text/html' } }); } }); -const browser = await puppeteer.launch({ executablePath: '/usr/bin/google-chrome', headless: 'new', args: ['--no-sandbox'] }); +const browser = await puppeteer.launch({ relays: 'live', executablePath: '/usr/bin/google-chrome', headless: 'new', args: ['--no-sandbox'] }); const page = await browser.newPage(); await page.evaluateOnNewDocument(() => { window.__feedReqs = 0; diff --git a/tools/feed-media-cache-test.js b/tools/feed-media-cache-test.js index 46c485d..02850ef 100644 --- a/tools/feed-media-cache-test.js +++ b/tools/feed-media-cache-test.js @@ -3,7 +3,7 @@ // slow picture), a reload paints the grid from that cache at once and // resumes the crawl where it stopped, and tile frames are kept as pictures. // Run: bun tools/feed-media-cache-test.js -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { buildHtml } from '../build.js'; @@ -64,7 +64,7 @@ all.push(tagged(151, 'size hidden ' + base + '/h/h4.mp4', base + '/h/h4.mp4')); all.push(tagged(152, 'no cors ' + base + '/n/n5.mp4', base + '/n/n5.mp4')); all.push(tagged(153, 'seen in the feed first ' + base + '/r/r6.mp4', base + '/r/r6.mp4')); -const browser = await puppeteer.launch({ executablePath: process.env.CHROME || '/usr/bin/google-chrome', headless: 'new', args: ['--no-sandbox', '--autoplay-policy=no-user-gesture-required'] }); +const browser = await puppeteer.launch({ relays: 'read', executablePath: process.env.CHROME || '/usr/bin/google-chrome', headless: 'new', args: ['--no-sandbox', '--autoplay-policy=no-user-gesture-required'] }); const page = await browser.newPage(); await page.evaluateOnNewDocument(([evs, pk]) => { window.__reqs = []; diff --git a/tools/feed-media-views-test.js b/tools/feed-media-views-test.js index e29d346..4890981 100644 --- a/tools/feed-media-views-test.js +++ b/tools/feed-media-views-test.js @@ -5,7 +5,7 @@ // until tapped: then the author, the words as a caption, likes and sats. // Back and Escape close it. // Run: bun tools/feed-media-views-test.js -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { buildHtml } from '../build.js'; diff --git a/tools/feed-new-posts-test.js b/tools/feed-new-posts-test.js index 4f5b8fb..aa3e667 100644 --- a/tools/feed-new-posts-test.js +++ b/tools/feed-new-posts-test.js @@ -2,7 +2,7 @@ // each new post keeps a tint until it has been in view a couple of seconds. // Run: bun tools/feed-new-posts-test.js import assert from 'node:assert/strict'; -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; const app = await Bun.file('src/app.js').text(); const dom = app.slice(app.indexOf('function h(tag,'), app.indexOf("const root = document.getElementById('app');")); const entry = ` diff --git a/tools/feed-stability-test.js b/tools/feed-stability-test.js index 1426bba..6befa1e 100644 --- a/tools/feed-stability-test.js +++ b/tools/feed-stability-test.js @@ -1,6 +1,6 @@ // Offline browser regression: cached rows must arrive whole and stay still. // Run: bun tools/feed-stability-test.js -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import { generateMnemonic } from '@scure/bip39'; import { wordlist } from '@scure/bip39/wordlists/english'; import { cacheKeyFor } from '../src/wallet.js'; diff --git a/tools/feed-streaming-test.js b/tools/feed-streaming-test.js index 3070771..c69b1eb 100644 --- a/tools/feed-streaming-test.js +++ b/tools/feed-streaming-test.js @@ -1,7 +1,7 @@ // Signed relay events must surface before EOSE and independently of slow media. // Run: bun tools/feed-streaming-test.js import assert from 'node:assert/strict'; -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import { finalizeEvent, generateSecretKey, getPublicKey } from 'nostr-tools/pure'; import { buildVerifyWorker } from '../build.js'; const sk = generateSecretKey(), author = getPublicKey(sk), now = Math.floor(Date.now() / 1000); diff --git a/tools/feed-switch-seed-test.js b/tools/feed-switch-seed-test.js index 1bad161..bf2b830 100644 --- a/tools/feed-switch-seed-test.js +++ b/tools/feed-switch-seed-test.js @@ -3,7 +3,7 @@ // other open feeds already hold, and once the feed on screen settles the // other feeds' first posts are fetched ahead into the disk cache. // Run: bun tools/feed-switch-seed-test.js -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { buildHtml } from '../build.js'; diff --git a/tools/feed-video-aspect-test.js b/tools/feed-video-aspect-test.js index 65cf9bb..d052b33 100644 --- a/tools/feed-video-aspect-test.js +++ b/tools/feed-video-aspect-test.js @@ -4,7 +4,7 @@ // shape still gets the 16:9 box. // Photos too: a big one paints from a column-sized copy (see makeMediaThumb). // Run: bun tools/feed-video-aspect-test.js (CHROME=<path> to pick a browser) -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { buildHtml } from '../build.js'; diff --git a/tools/feed-wallet-back-test.js b/tools/feed-wallet-back-test.js index 1b0abe7..1b39728 100644 --- a/tools/feed-wallet-back-test.js +++ b/tools/feed-wallet-back-test.js @@ -1,7 +1,7 @@ // Exercise the real app history: a public feed must survive Wallet -> Back. // Run: bun tools/feed-wallet-back-test.js import assert from 'node:assert/strict'; -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import { buildHtml } from '../build.js'; const html = await buildHtml({ minify: true, pwa: false }); const server = Bun.serve({ port: 0, fetch: req => new URL(req.url).pathname === '/api/feed' diff --git a/tools/feed-window-dom-test.js b/tools/feed-window-dom-test.js index 72ca535..c86e98e 100644 --- a/tools/feed-window-dom-test.js +++ b/tools/feed-window-dom-test.js @@ -3,7 +3,7 @@ // page keeps its length, the row under your eyes never moves when the window // shifts, and scrolling back up brings the top posts back. // Run: bun tools/feed-window-dom-test.js -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { buildHtml } from '../build.js'; @@ -25,7 +25,7 @@ const server = Bun.serve({ port: 5305, fetch: (req) => { if (path.startsWith('/punks')) return new Response(Bun.file('dist' + path)); return new Response(html, { headers: { 'content-type': 'text/html' } }); } }); -const browser = await puppeteer.launch({ executablePath: '/usr/bin/google-chrome', headless: 'new', args: ['--no-sandbox'] }); +const browser = await puppeteer.launch({ relays: 'read', executablePath: '/usr/bin/google-chrome', headless: 'new', args: ['--no-sandbox'] }); const page = await browser.newPage(); // the stub relay holds every post: a kind-1 request is answered from it // (honouring until/limit), everything else gets a bare EOSE diff --git a/tools/follows-overwrite-test.js b/tools/follows-overwrite-test.js index 8ed2a82..d0ea3a2 100644 --- a/tools/follows-overwrite-test.js +++ b/tools/follows-overwrite-test.js @@ -8,7 +8,7 @@ // real list, and record every event the wallet publishes. // Run: bun tools/follows-overwrite-test.js import assert from 'node:assert/strict'; -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { bytesToHex } from '@noble/hashes/utils'; diff --git a/tools/image-tap-test.js b/tools/image-tap-test.js index 5ed182e..2bf840b 100644 --- a/tools/image-tap-test.js +++ b/tools/image-tap-test.js @@ -3,7 +3,7 @@ // the picture opens the picture. // // Run: bun tools/image-tap-test.js -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { buildHtml } from '../build.js'; const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); diff --git a/tools/image-zoom-test.js b/tools/image-zoom-test.js index f1a57c5..3ada708 100644 --- a/tools/image-zoom-test.js +++ b/tools/image-zoom-test.js @@ -3,7 +3,7 @@ // dismissal; the close button bypasses gesture suppression. // Run: bun tools/image-zoom-test.js import assert from 'node:assert/strict'; -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; const app = await Bun.file('src/app.js').text(); const dom = app.slice(app.indexOf('function h(tag,'), app.indexOf("const root = document.getElementById('app');")); const viewer = app.slice(app.indexOf('function imageViewer()'), app.indexOf("if (typeof window !== 'undefined') {\n window.addEventListener('keydown'")); diff --git a/tools/link-preview-test.js b/tools/link-preview-test.js index 0b9e863..52ddca1 100644 --- a/tools/link-preview-test.js +++ b/tools/link-preview-test.js @@ -4,7 +4,7 @@ // nothing to say stays a bare link; pictures and YouTube keep their own // boxes. // Run: bun tools/link-preview-test.js -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { privateKeyFromSeedWords } from 'nostr-tools/nip06'; @@ -37,7 +37,7 @@ const server = Bun.serve({ port: 5311, fetch: (req) => { if (u.pathname.startsWith('/punks') || u.pathname === '/verify-worker.js') return new Response(Bun.file('dist' + u.pathname)); return new Response(html, { headers: { 'content-type': 'text/html' } }); } }); -const browser = await puppeteer.launch({ executablePath: '/usr/bin/google-chrome', headless: 'new', args: ['--no-sandbox'] }); +const browser = await puppeteer.launch({ relays: 'read', executablePath: '/usr/bin/google-chrome', headless: 'new', args: ['--no-sandbox'] }); const page = await browser.newPage(); await page.evaluateOnNewDocument((posts, profile) => { // the registrar's preview endpoint answers from this test's server diff --git a/tools/lock-wallet-test.js b/tools/lock-wallet-test.js index 107ccd0..b6e8a10 100644 --- a/tools/lock-wallet-test.js +++ b/tools/lock-wallet-test.js @@ -6,7 +6,7 @@ // The padlock is the counterpart with its own rules (see padlock-flow-test). // // Run: bun tools/lock-wallet-test.js -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import { generateMnemonic } from '@scure/bip39'; import { wordlist } from '@scure/bip39/wordlists/english'; import { buildHtml } from '../build.js'; diff --git a/tools/locked-gift-test.js b/tools/locked-gift-test.js index 12c0dff..0522b47 100644 --- a/tools/locked-gift-test.js +++ b/tools/locked-gift-test.js @@ -3,7 +3,7 @@ // on the unlock screen, the right one walks into the ordinary claim flow. // // Run: bun tools/locked-gift-test.js -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import { hex, base32nopad } from '@scure/base'; import { buildHtml } from '../build.js'; import { lockGift, previewLockedGift } from '../src/features/gifts-wallet.js'; diff --git a/tools/markdown-note-test.js b/tools/markdown-note-test.js index 8e7cca1..d353b67 100644 --- a/tools/markdown-note-test.js +++ b/tools/markdown-note-test.js @@ -9,7 +9,7 @@ // mangle normal posts to pretty up the rare bridged one. // // Run: bun tools/markdown-note-test.js -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import { generateMnemonic } from '@scure/bip39'; import { wordlist } from '@scure/bip39/wordlists/english'; import { cacheKeyFor } from '../src/wallet.js'; diff --git a/tools/marmot-chat-test.js b/tools/marmot-chat-test.js index 7310fd0..f8bccca 100644 --- a/tools/marmot-chat-test.js +++ b/tools/marmot-chat-test.js @@ -14,7 +14,7 @@ // invitation → card → Join → messages both ways; "New message" to a White // Noise user founding a direct group whose replies land in the DM thread; // "New group" from the app. -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import { spawnSync } from 'node:child_process'; import { generateMnemonic } from '@scure/bip39'; import { wordlist } from '@scure/bip39/wordlists/english'; diff --git a/tools/minimal-smoke.js b/tools/minimal-smoke.js index ebb57f5..76162d5 100644 --- a/tools/minimal-smoke.js +++ b/tools/minimal-smoke.js @@ -5,7 +5,7 @@ // Usage: bun tools/minimal-smoke.js import { execSync } from 'node:child_process'; -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import { generateMnemonic } from '@scure/bip39'; import { wordlist } from '@scure/bip39/wordlists/english'; import { buildHtml } from '../build.js'; diff --git a/tools/moderation-test.js b/tools/moderation-test.js index d6df879..426115c 100644 --- a/tools/moderation-test.js +++ b/tools/moderation-test.js @@ -5,7 +5,7 @@ // are hidden; Settings → Nostr lays the mute list out and edits it. // No relay is written for real — the WebSocket shim swallows EVENT. // Run: bun tools/moderation-test.js -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { privateKeyFromSeedWords } from 'nostr-tools/nip06'; @@ -39,7 +39,7 @@ const html = await buildHtml({ minify: true, pwa: false }); const server = Bun.serve({ port: 5299, fetch: (req, srv) => (new URL(req.url).pathname === '/relay' && srv.upgrade(req) ? undefined : new Response(html, { headers: { 'content-type': 'text/html' } })), websocket: { message: (ws, data) => { try { const m = JSON.parse(data); if (m[0] === 'REQ') ws.send(JSON.stringify(['EOSE', m[1]])); } catch {} } } }); -const browser = await puppeteer.launch({ executablePath: '/usr/bin/google-chrome', headless: 'new', args: ['--no-sandbox'] }); +const browser = await puppeteer.launch({ relays: 'read', executablePath: '/usr/bin/google-chrome', headless: 'new', args: ['--no-sandbox'] }); const page = await browser.newPage(); await page.evaluateOnNewDocument((fx) => { window.__reqs = []; diff --git a/tools/note-page-test.js b/tools/note-page-test.js index 915d892..96ced70 100644 --- a/tools/note-page-test.js +++ b/tools/note-page-test.js @@ -4,7 +4,7 @@ // no Back button inventing a parent the visitor never came from. // Run: bun tools/note-page-test.js import assert from 'node:assert/strict'; -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { neventEncode } from 'nostr-tools/nip19'; import { parseNoteRef, buildThread, createThreadCache, renderNotePage } from '../feed/note-page.js'; diff --git a/tools/notif-categories-test.js b/tools/notif-categories-test.js index 92b0c71..f38dd01 100644 --- a/tools/notif-categories-test.js +++ b/tools/notif-categories-test.js @@ -1,7 +1,7 @@ // Settings → Notifications lists zaps and reactions as their own categories, // each with its own switch. Run: bun tools/notif-categories-test.js import assert from 'node:assert/strict'; -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import { buildHtml } from '../build.js'; const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); const html = await buildHtml({ minify: true, pwa: false }); diff --git a/tools/notif-faces-test.js b/tools/notif-faces-test.js index 3093f51..b43a41f 100644 --- a/tools/notif-faces-test.js +++ b/tools/notif-faces-test.js @@ -3,7 +3,7 @@ // opens with faces, not circles filling in. The stub relay answers the kind-0 // batch for the actors with pictures served by this test. // Run: bun tools/notif-faces-test.js -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { privateKeyFromSeedWords } from 'nostr-tools/nip06'; diff --git a/tools/notif-note-cache-test.js b/tools/notif-note-cache-test.js index c0ceeca..a3173fa 100644 --- a/tools/notif-note-cache-test.js +++ b/tools/notif-note-cache-test.js @@ -1,7 +1,7 @@ // Referenced notification notes are fetched before the page opens, persisted, // and available on the first paint after a reload. // Run: bun tools/notif-note-cache-test.js -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { privateKeyFromSeedWords } from 'nostr-tools/nip06'; diff --git a/tools/offline-cache-test.js b/tools/offline-cache-test.js index c01dc1c..ffa42e4 100644 --- a/tools/offline-cache-test.js +++ b/tools/offline-cache-test.js @@ -4,7 +4,7 @@ // build from dist/ so the service worker serves the shell offline. // // Run: bun run build && bun tools/offline-cache-test.js -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import { generateMnemonic } from '@scure/bip39'; import { wordlist } from '@scure/bip39/wordlists/english'; import { join } from 'node:path'; @@ -16,7 +16,7 @@ const server = Bun.serve({ port: 5234, async fetch(req) { if (await f.exists()) return new Response(f, { headers: { 'cache-control': 'no-store' } }); return new Response(Bun.file(dist + '/index.html'), { headers: { 'cache-control': 'no-store' } }); } }); -const browser = await puppeteer.launch({ executablePath: '/usr/bin/google-chrome', headless: 'new', args: ['--no-sandbox'] }); +const browser = await puppeteer.launch({ relays: 'read', executablePath: '/usr/bin/google-chrome', headless: 'new', args: ['--no-sandbox'] }); const page = await browser.newPage(); const errs = []; page.on('pageerror', (e) => errs.push(e.message)); page.on('console', (m) => { if (m.type() === 'error') errs.push('console: ' + m.text().slice(0, 160)); }); const click = (sel, t) => page.evaluate((s, x) => { const e = [...document.querySelectorAll(s)].find((n) => n.textContent.trim().toLowerCase().includes(x.toLowerCase())); if (e) { e.click(); return true; } return false; }, sel, t); diff --git a/tools/onboard-backup-test.js b/tools/onboard-backup-test.js index 9fe5a3e..ddef8d7 100644 --- a/tools/onboard-backup-test.js +++ b/tools/onboard-backup-test.js @@ -10,7 +10,7 @@ // setup, and if they put it off the wallet keeps one quiet line about it. // // Run: bun tools/onboard-backup-test.js -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import { bech32 } from '@scure/base'; import { generateSecretKey } from 'nostr-tools/pure'; import { seedFromNostrKey } from '../src/nostr-login.js'; diff --git a/tools/padlock-ask-test.js b/tools/padlock-ask-test.js index 5570b8f..763dcae 100644 --- a/tools/padlock-ask-test.js +++ b/tools/padlock-ask-test.js @@ -6,7 +6,7 @@ // leaves you exactly where you tapped. // // Run: bun tools/padlock-ask-test.js -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import { generateMnemonic } from '@scure/bip39'; import { wordlist } from '@scure/bip39/wordlists/english'; import { buildHtml } from '../build.js'; diff --git a/tools/padlock-flow-test.js b/tools/padlock-flow-test.js index 59aee32..dcc0a85 100644 --- a/tools/padlock-flow-test.js +++ b/tools/padlock-flow-test.js @@ -4,7 +4,7 @@ // wallet settings speak only of the on-chain wallet until Spending is set up. // Setting a password locks in place: watch-only home, closed padlock. // Run: bun tools/padlock-flow-test.js -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import { generateMnemonic } from '@scure/bip39'; import { wordlist } from '@scure/bip39/wordlists/english'; import { buildHtml } from '../build.js'; diff --git a/tools/poll-test.js b/tools/poll-test.js index bc1715e..edcb061 100644 --- a/tools/poll-test.js +++ b/tools/poll-test.js @@ -4,7 +4,7 @@ // caught at the socket so the test never votes on someone's real poll. // // Run: bun tools/poll-test.js -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import { SimplePool } from 'nostr-tools/pool'; import { cacheKeyFor } from '../src/wallet.js'; import { neventOf } from '../src/nostr.js'; @@ -21,7 +21,7 @@ const labels = poll.tags.filter((x) => x[0] === 'option').map((x) => x[2]); const html = await buildHtml({ minify: true, pwa: false }); const server = Bun.serve({ port: 5273, fetch: () => new Response(html, { headers: { 'content-type': 'text/html' } }) }); -const browser = await puppeteer.launch({ executablePath: '/usr/bin/google-chrome', headless: 'new', args: ['--no-sandbox'] }); +const browser = await puppeteer.launch({ relays: 'read', executablePath: '/usr/bin/google-chrome', headless: 'new', args: ['--no-sandbox'] }); const page = await browser.newPage(); await page.evaluateOnNewDocument(() => { window.__votes = []; diff --git a/tools/pos-test.js b/tools/pos-test.js index c7c7c29..55349cb 100644 --- a/tools/pos-test.js +++ b/tools/pos-test.js @@ -2,7 +2,7 @@ // the bill and the tip counted apart. The Ark side is faked: no relays, no // server, no money. Run: bun tools/pos-test.js import assert from 'node:assert/strict'; -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; const app = await Bun.file('src/app.js').text(); const domHelper = app.slice(app.indexOf('function h(tag,'), app.indexOf('// ---------------------------------------------------------------- morphing')); diff --git a/tools/post-actions-test.js b/tools/post-actions-test.js index 4c5d92e..a33079e 100644 --- a/tools/post-actions-test.js +++ b/tools/post-actions-test.js @@ -7,7 +7,7 @@ // a relay to answer, so the test doesn't depend on one). // // Run: bun tools/post-actions-test.js -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import { generateMnemonic } from '@scure/bip39'; import { wordlist } from '@scure/bip39/wordlists/english'; import { cacheKeyFor } from '../src/wallet.js'; diff --git a/tools/post-emoji-test.js b/tools/post-emoji-test.js index 6c7a9aa..558dca4 100644 --- a/tools/post-emoji-test.js +++ b/tools/post-emoji-test.js @@ -1,7 +1,7 @@ // Exercise public reaction ingestion, rendering, signing and withdrawal. // No requests or events leave this local fixture. Run: bun tools/post-emoji-test.js import assert from 'node:assert/strict'; -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; const app = await Bun.file('src/app.js').text(); const dom = app.slice(app.indexOf('function h(tag,'), app.indexOf('// ---------------------------------------------------------------- morphing')); const entry = ` diff --git a/tools/profile-actions-test.js b/tools/profile-actions-test.js index e44c037..1886058 100644 --- a/tools/profile-actions-test.js +++ b/tools/profile-actions-test.js @@ -1,7 +1,7 @@ // Send clears on leaving; profile Pay starts fresh; messaging Back pops history. // Run: bun tools/profile-actions-test.js import assert from 'node:assert/strict'; -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import { generateMnemonic } from '@scure/bip39'; import { wordlist } from '@scure/bip39/wordlists/english'; import { generateSecretKey, getPublicKey } from 'nostr-tools/pure'; @@ -26,7 +26,7 @@ const server = Bun.serve({ port: 0, fetch(req) { return new URL(req.url).pathname === '/' || new URL(req.url).pathname.startsWith('/npub1') ? new Response(html, { headers: { 'Content-Type': 'text/html' } }) : new Response(null, { status: 404 }); } }); -const browser = await puppeteer.launch({ executablePath: '/usr/bin/google-chrome', headless: true, args: ['--no-sandbox'] }); +const browser = await puppeteer.launch({ relays: 'read', executablePath: '/usr/bin/google-chrome', headless: true, args: ['--no-sandbox'] }); try { const page = await browser.newPage(), errors = []; page.on('pageerror', (e) => errors.push(e.message)); diff --git a/tools/profile-batch-test.js b/tools/profile-batch-test.js index 4a993a5..0b78f61 100644 --- a/tools/profile-batch-test.js +++ b/tools/profile-batch-test.js @@ -7,7 +7,7 @@ // relays and real pubkeys from that feed. // // Run: bun tools/profile-batch-test.js -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import { generateMnemonic } from '@scure/bip39'; import { wordlist } from '@scure/bip39/wordlists/english'; import { cacheKeyFor } from '../src/wallet.js'; @@ -31,7 +31,7 @@ if (notes.length < 10) { console.log(' ✗ relays too quiet to test with'); proc const html = await buildHtml({ minify: true, pwa: false }); const server = Bun.serve({ port: 5273, fetch: () => new Response(html, { headers: { 'content-type': 'text/html' } }) }); -const browser = await puppeteer.launch({ executablePath: '/usr/bin/google-chrome', headless: 'new', args: ['--no-sandbox'] }); +const browser = await puppeteer.launch({ relays: 'read', executablePath: '/usr/bin/google-chrome', headless: 'new', args: ['--no-sandbox'] }); const page = await browser.newPage(); page.on('console', (m) => { const t = m.text(); if (/profiles asked/.test(t)) console.log(' [page]', t); }); // count the kind-0 requests the app sends, and how many pubkeys each carries diff --git a/tools/profile-draft-test.js b/tools/profile-draft-test.js index 437dd61..9880d68 100644 --- a/tools/profile-draft-test.js +++ b/tools/profile-draft-test.js @@ -2,7 +2,7 @@ // without adding a browser-history entry for each keystroke. // Run: bun tools/profile-draft-test.js import assert from 'node:assert/strict'; -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import { generateMnemonic } from '@scure/bip39'; import { wordlist } from '@scure/bip39/wordlists/english'; import { seedPubkey } from '../src/nostr.js'; @@ -11,7 +11,7 @@ import { buildHtml } from '../build.js'; const html = await buildHtml({ minify: true, pwa: false }); const server = Bun.serve({ port: 0, fetch: () => new Response(html, { headers: { 'content-type': 'text/html' } }) }); const origin = `http://localhost:${server.port}`; -const browser = await puppeteer.launch({ executablePath: '/usr/bin/google-chrome', headless: true, args: ['--no-sandbox'] }); +const browser = await puppeteer.launch({ relays: 'read', executablePath: '/usr/bin/google-chrome', headless: true, args: ['--no-sandbox'] }); const page = await browser.newPage(); const errors = []; page.on('pageerror', e => errors.push(e.message)); diff --git a/tools/profile-feed-test.js b/tools/profile-feed-test.js index 8ea7faa..f2ca9e6 100644 --- a/tools/profile-feed-test.js +++ b/tools/profile-feed-test.js @@ -3,7 +3,7 @@ // live coinos community: open chat, tap an avatar, land on their profile. // // Run: bun tools/profile-feed-test.js -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import { generateMnemonic } from '@scure/bip39'; import { wordlist } from '@scure/bip39/wordlists/english'; import { buildHtml } from '../build.js'; @@ -14,7 +14,7 @@ const check = (n, c, d = '') => { console.log(` ${c ? '✓' : '✗'} ${n}${d ? ' const html = await buildHtml({ minify: true, pwa: false }); const server = Bun.serve({ port: 5234, fetch: () => new Response(html, { headers: { 'content-type': 'text/html' } }) }); -const browser = await puppeteer.launch({ executablePath: '/usr/bin/google-chrome', headless: 'new', args: ['--no-sandbox'] }); +const browser = await puppeteer.launch({ relays: 'read', executablePath: '/usr/bin/google-chrome', headless: 'new', args: ['--no-sandbox'] }); const page = await browser.newPage(); const pageErrors = []; page.on('pageerror', (e) => pageErrors.push(String(e))); diff --git a/tools/profile-key-qr-test.js b/tools/profile-key-qr-test.js index fb3c846..f67fc3d 100644 --- a/tools/profile-key-qr-test.js +++ b/tools/profile-key-qr-test.js @@ -2,7 +2,7 @@ // really says the npub printed under it. // // Run: bun tools/profile-key-qr-test.js -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import jsQR from 'jsqr'; import { generateMnemonic } from '@scure/bip39'; import { wordlist } from '@scure/bip39/wordlists/english'; @@ -43,7 +43,9 @@ try { await page.waitForSelector('.npub-qr svg', { timeout: 5000 }); const npub = await page.evaluate(() => document.querySelector('.npub-box').textContent.trim()); check('the key is shown', /^npub1[a-z0-9]{58}$/.test(npub), npub); - const box = await (await page.$('.npub-qr')).boundingBox(); + // a repaint can swap the box out between finding and measuring it + let box = null; + for (let i = 0; i < 20 && !box; i++) { box = await (await page.$('.npub-qr'))?.boundingBox().catch(() => null); if (!box) await sleep(100); } check('the QR fits a phone screen', box.width <= 420 && box.x >= 0, JSON.stringify(box)); // read it back the way a camera would: from pixels const shot = join(tmpdir(), 'profile-key-qr.png'); diff --git a/tools/profile-open-test.js b/tools/profile-open-test.js index aa0c943..c09a239 100644 --- a/tools/profile-open-test.js +++ b/tools/profile-open-test.js @@ -10,7 +10,7 @@ // Measured as the browser measures it — layout-shift entries after the tap. // // Run: bun tools/profile-open-test.js -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import { generateMnemonic } from '@scure/bip39'; import { wordlist } from '@scure/bip39/wordlists/english'; import { cacheKeyFor } from '../src/wallet.js'; @@ -45,7 +45,7 @@ console.log(` ${authors.length} people with a real bio, ${notes.length} of the const html = await buildHtml({ minify: true, pwa: false }); const server = Bun.serve({ port: 5276, fetch: () => new Response(html, { headers: { 'content-type': 'text/html' } }) }); -const browser = await puppeteer.launch({ executablePath: '/usr/bin/google-chrome', headless: 'new', args: ['--no-sandbox'] }); +const browser = await puppeteer.launch({ relays: 'read', executablePath: '/usr/bin/google-chrome', headless: 'new', args: ['--no-sandbox'] }); const page = await browser.newPage(); await page.evaluateOnNewDocument(() => { window.__shift = 0; diff --git a/tools/profile-recovery-test.js b/tools/profile-recovery-test.js index 2f6ae90..158a511 100644 --- a/tools/profile-recovery-test.js +++ b/tools/profile-recovery-test.js @@ -2,7 +2,7 @@ // and remain retryable. Tests real rendering with deterministic relay replies. // Run: bun tools/profile-recovery-test.js import assert from 'node:assert/strict'; -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; const app = await Bun.file('src/app.js').text(); const dom = app.slice(app.indexOf('function h(tag,'), app.indexOf('// ---------------------------------------------------------------- morphing')); const entry = ` diff --git a/tools/public-feed-browser-test.js b/tools/public-feed-browser-test.js index bfeb82b..59eeb3b 100644 --- a/tools/public-feed-browser-test.js +++ b/tools/public-feed-browser-test.js @@ -1,5 +1,5 @@ import assert from 'node:assert/strict'; -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import { finalizeEvent, generateSecretKey } from 'nostr-tools/pure'; import { buildHtml, buildVerifyWorker } from '../build.js'; const sk = generateSecretKey(), now = Math.floor(Date.now() / 1000); diff --git a/tools/puppeteer.js b/tools/puppeteer.js new file mode 100644 index 0000000..4e0f121 --- /dev/null +++ b/tools/puppeteer.js @@ -0,0 +1,126 @@ +// puppeteer-core for the browser tests, with one difference: a page's +// sockets to public relays never leave the machine. Throwaway test wallets +// boot, subscribe and publish like real ones, and a suite run from one home +// address gets that address banned (nos.lol, relay.damus.io, 2026-10-07). +// So every socket to a host that isn't local is answered in the page by a +// stand-in relay that has nothing stored: EVENT gets OK, REQ gets EOSE, +// COUNT gets 0. Local relays (ws://127.0.0.1:…, a test's own) are untouched, +// and a test's own WebSocket stubs still see every send, as before. +// +// A test that reads real content off the public relays launches with +// { relays: 'read' }: its sockets are real, but nothing it publishes leaves +// the page (each EVENT is answered OK in place). LIVE_RELAYS=read does the +// same for any test. { relays: 'live' } (a test of a public service, like +// the Cordn coordinator) or LIVE_RELAYS=1 turns the guard off, and +// LIVE_RELAYS=relay.example.com,other.example lets just those hosts through. +// puppeteer.connect (driving a real Chrome) is never guarded. +// +// Use: import puppeteer from './puppeteer.js'; (in place of 'puppeteer-core') +import real from 'puppeteer-core'; + +const live = (process.env.LIVE_RELAYS || '').trim(); +const allow = live && live !== '1' && live !== 'read' ? live.split(',').map((h) => h.trim()).filter(Boolean) : []; + +// Runs in the page before anything else. +function relayGuard(allow, readOnly) { + if (window.__relayGuard) return; + window.__relayGuard = { blocked: [], sent: 0, events: [] }; // events: what didn't go out + const Real = window.WebSocket; + const nativeSend = Real.prototype.send; + const nativeClose = Real.prototype.close; + const fakes = new WeakSet(); + const reading = new WeakSet(); // real public sockets in read mode + const local = (h) => h === 'localhost' || h === '[::1]' || h === '0.0.0.0' || /^127\./.test(h) || h.endsWith('.localhost'); + const answer = (ws, msg) => setTimeout(() => { + if (ws.readyState !== 1) return; + ws.dispatchEvent(new MessageEvent('message', { data: JSON.stringify(msg) })); + }, 5); + const relay = (ws, d) => { + let m; + try { m = JSON.parse(d); } catch { return; } + if (!Array.isArray(m)) return; + window.__relayGuard.sent++; + if (m[0] === 'EVENT' && m[1]?.id) { window.__relayGuard.events.push(m[1]); answer(ws, ['OK', m[1].id, true, '']); } + else if (m[0] === 'REQ') answer(ws, ['EOSE', m[1]]); + else if (m[0] === 'COUNT') answer(ws, ['COUNT', m[1], { count: 0 }]); + else if (m[0] === 'AUTH' && m[1]?.id) answer(ws, ['OK', m[1].id, true, '']); + }; + // Every send goes through here, so a test that wraps + // WebSocket.prototype.send (before or after) still sees a stand-in's. + Real.prototype.send = function (d) { + if (fakes.has(this)) { if (this.readyState !== 1) throw new DOMException('not open', 'InvalidStateError'); return relay(this, d); } + if (reading.has(this) && typeof d === 'string' && /^\s*\[\s*"EVENT"/.test(d)) return relay(this, d); + return nativeSend.call(this, d); + }; + Real.prototype.close = function (code, reason) { + if (!fakes.has(this)) return nativeClose.call(this, code, reason); + if (this.readyState >= 2) return; + this.readyState = 2; + setTimeout(() => { this.readyState = 3; this.dispatchEvent(new CloseEvent('close', { code: code || 1000, reason: reason || '', wasClean: true })); }, 0); + }; + const handlerProp = (ws, type) => { + let fn = null; + const call = (e) => fn && fn.call(ws, e); + ws.addEventListener(type, call); + Object.defineProperty(ws, 'on' + type, { get: () => fn, set: (v) => { fn = typeof v === 'function' ? v : null; }, configurable: true }); + }; + function WebSocket(url, protocols) { + if (!new.target) throw new TypeError("Failed to construct 'WebSocket': Please use the 'new' operator"); + let u; + try { u = new URL(url, location.href); } catch { return new Real(url, protocols); } + if (u.protocol !== 'wss:' && u.protocol !== 'ws:') return new Real(url, protocols); + if (local(u.hostname) || allow.includes(u.hostname)) return new Real(url, protocols); + if (readOnly) { const ws = new Real(url, protocols); reading.add(ws); return ws; } + window.__relayGuard.blocked.push(u.href); + // a genuine EventTarget (so dispatchEvent works) that passes instanceof + const ws = Object.setPrototypeOf(new EventTarget(), Real.prototype); + fakes.add(ws); + Object.defineProperties(ws, { + url: { value: u.href }, protocol: { value: '' }, extensions: { value: '' }, + bufferedAmount: { value: 0 }, binaryType: { value: 'blob', writable: true }, + readyState: { value: 0, writable: true }, + }); + for (const t of ['open', 'message', 'close', 'error']) handlerProp(ws, t); + setTimeout(() => { if (ws.readyState !== 0) return; ws.readyState = 1; ws.dispatchEvent(new Event('open')); }, 0); + return ws; + } + WebSocket.prototype = Real.prototype; + for (const k of ['CONNECTING', 'OPEN', 'CLOSING', 'CLOSED']) WebSocket[k] = Real[k]; + window.WebSocket = WebSocket; +} + +const guarded = new WeakSet(); +const guardPage = async (page, ro) => { + if (!page || guarded.has(page)) return; + guarded.add(page); + await page.evaluateOnNewDocument(relayGuard, allow, ro).catch(() => {}); +}; +const guardContext = (ctx, ro) => { + const newPage = ctx.newPage.bind(ctx); + ctx.newPage = async (...a) => { const p = await newPage(...a); await guardPage(p, ro); return p; }; +}; +const guardBrowser = async (browser, ro) => { + guardContext(browser, ro); + for (const name of ['createBrowserContext', 'createIncognitoBrowserContext']) { + if (typeof browser[name] !== 'function') continue; + const make = browser[name].bind(browser); + browser[name] = async (...a) => { const c = await make(...a); guardContext(c, ro); return c; }; + } + // pages it opened itself (popups, the first tab) + browser.on('targetcreated', async (t) => { if (t.type() === 'page') await guardPage(await t.page().catch(() => null), ro); }); + for (const p of await browser.pages()) await guardPage(p, ro); +}; + +const launch = async ({ relays, ...opts } = {}) => { + const browser = await real.launch(opts); + if (live !== '1' && relays !== 'live') await guardBrowser(browser, live === 'read' || relays === 'read'); + return browser; +}; + +export default new Proxy(real, { + get(t, k) { + if (k === 'launch') return launch; + const v = t[k]; + return typeof v === 'function' ? v.bind(t) : v; + }, +}); diff --git a/tools/push-unavailable-test.js b/tools/push-unavailable-test.js index 32fddff..6b754ea 100644 --- a/tools/push-unavailable-test.js +++ b/tools/push-unavailable-test.js @@ -5,7 +5,7 @@ // actually wrong and what works instead. // // Run: bun tools/push-unavailable-test.js -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import { generateMnemonic } from '@scure/bip39'; import { wordlist } from '@scure/bip39/wordlists/english'; import { cacheKeyFor } from '../src/wallet.js'; diff --git a/tools/quote-card-test.js b/tools/quote-card-test.js index 2cb9d41..3544c31 100644 --- a/tools/quote-card-test.js +++ b/tools/quote-card-test.js @@ -6,7 +6,7 @@ // fiatjaf's, chosen because it will still be there tomorrow. // // Run: bun tools/quote-card-test.js -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import { generateMnemonic } from '@scure/bip39'; import { wordlist } from '@scure/bip39/wordlists/english'; import { cacheKeyFor } from '../src/wallet.js'; @@ -29,7 +29,7 @@ const FAR = 'nevent1qvzqqqqqqypzqntcggz30qhq60ltqdx32zku9d46unhrkjtcv7fml7jx3dh4 const html = await buildHtml({ minify: true, pwa: false }); const server = Bun.serve({ port: 5269, fetch: () => new Response(html, { headers: { 'content-type': 'text/html' } }) }); -const browser = await puppeteer.launch({ executablePath: '/usr/bin/google-chrome', headless: 'new', args: ['--no-sandbox'] }); +const browser = await puppeteer.launch({ relays: 'read', executablePath: '/usr/bin/google-chrome', headless: 'new', args: ['--no-sandbox'] }); const page = await browser.newPage(); // --slow: the relays miss the quoted note the first time it is asked for // (ours was down for an evening once), so the row freezes with the fallback diff --git a/tools/relays-settings-test.js b/tools/relays-settings-test.js index df89388..9e18f59 100644 --- a/tools/relays-settings-test.js +++ b/tools/relays-settings-test.js @@ -3,7 +3,7 @@ // both only after a fresh lookup, newer than the copies it replaces. // No relay is written for real — the WebSocket shim swallows EVENT. // Run: bun tools/relays-settings-test.js -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import { getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { privateKeyFromSeedWords } from 'nostr-tools/nip06'; import { buildHtml } from '../build.js'; diff --git a/tools/reply-count-test.js b/tools/reply-count-test.js index 659d629..10f03f6 100644 --- a/tools/reply-count-test.js +++ b/tools/reply-count-test.js @@ -1,7 +1,7 @@ // Each post's reply button carries the number of replies the relays know. // Run: bun tools/reply-count-test.js import assert from 'node:assert/strict'; -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import { generateSecretKey, finalizeEvent } from 'nostr-tools/pure'; import { buildHtml } from '../build.js'; const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); diff --git a/tools/room-faces-test.js b/tools/room-faces-test.js index ab673ff..cc4c6fe 100644 --- a/tools/room-faces-test.js +++ b/tools/room-faces-test.js @@ -4,14 +4,14 @@ // profiles cached — the worst case — against the real coinos community. // // Run: bun tools/room-faces-test.js [--no-hover] -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import { buildHtml } from '../build.js'; const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); let ok = true; const check = (n, c, d = '') => { console.log(` ${c ? '✓' : '✗'} ${n}${d ? ' — ' + d : ''}`); if (!c) ok = false; }; const html = await buildHtml({ minify: true, pwa: false }); const server = Bun.serve({ port: 5277, fetch: () => new Response(html, { headers: { 'content-type': 'text/html' } }) }); -const browser = await puppeteer.launch({ executablePath: '/usr/bin/google-chrome', headless: 'new', args: ['--no-sandbox'] }); +const browser = await puppeteer.launch({ relays: 'read', executablePath: '/usr/bin/google-chrome', headless: 'new', args: ['--no-sandbox'] }); const page = await browser.newPage(); const click = (t) => page.evaluate((x) => { const e = [...document.querySelectorAll('button')].find((n) => n.textContent.trim().toLowerCase().includes(x)); if (e) { e.click(); return true; } return false; }, t); const waitText = async (x, ms = 25000) => { for (let i = 0; i < ms / 250; i++) { if ((await page.evaluate(() => document.body.innerText)).toLowerCase().includes(x)) return true; await sleep(250); } return false; }; diff --git a/tools/send-avatar-loading-test.js b/tools/send-avatar-loading-test.js index daedb7c..b1022bf 100644 --- a/tools/send-avatar-loading-test.js +++ b/tools/send-avatar-loading-test.js @@ -1,7 +1,7 @@ // Real recipient portraits and profile fetches with controlled relay answers. // Run: bun tools/send-avatar-loading-test.js import assert from 'node:assert/strict'; -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; const app = await Bun.file('src/app.js').text(); const domHelper = app.slice(app.indexOf('function h(tag,'), app.indexOf('// ---------------------------------------------------------------- morphing')); diff --git a/tools/send-dest-test.js b/tools/send-dest-test.js index 6dc331c..8403fc9 100644 --- a/tools/send-dest-test.js +++ b/tools/send-dest-test.js @@ -2,7 +2,7 @@ // (it used to be repeated in small type underneath), and the history list // steps aside once you're on to the amount. // Run: bun tools/send-dest-test.js -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import { generateMnemonic } from '@scure/bip39'; import { wordlist } from '@scure/bip39/wordlists/english'; import { p2tr } from '@scure/btc-signer'; diff --git a/tools/send-lnaddr-advance-test.js b/tools/send-lnaddr-advance-test.js index 5b79a47..fd6c1a1 100644 --- a/tools/send-lnaddr-advance-test.js +++ b/tools/send-lnaddr-advance-test.js @@ -3,7 +3,7 @@ // alone a moment — but only once it's real: a half-typed domain just waits, // with no error. Uses the live registrar and DNS ([email protected]). // Run: bun tools/send-lnaddr-advance-test.js -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import { generateMnemonic } from '@scure/bip39'; import { wordlist } from '@scure/bip39/wordlists/english'; import { buildHtml } from '../build.js'; diff --git a/tools/standalone-file-test.js b/tools/standalone-file-test.js index 9254481..743bc7d 100644 --- a/tools/standalone-file-test.js +++ b/tools/standalone-file-test.js @@ -3,7 +3,7 @@ // Run: bun run build && bun tools/standalone-file-test.js import assert from 'node:assert/strict'; import { resolve } from 'node:path'; -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); const browser = await puppeteer.launch({ executablePath: '/usr/bin/google-chrome', headless: true, args: ['--no-sandbox'] }); try { diff --git a/tools/sw-reload-test.js b/tools/sw-reload-test.js index 7a41c7f..6ad4fed 100644 --- a/tools/sw-reload-test.js +++ b/tools/sw-reload-test.js @@ -8,7 +8,7 @@ // case where a reload is genuinely owed. // // Run: bun tools/sw-reload-test.js (needs a built dist/) -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); let ok = true; diff --git a/tools/sync-browser-test.js b/tools/sync-browser-test.js index 05f8316..3de405f 100644 --- a/tools/sync-browser-test.js +++ b/tools/sync-browser-test.js @@ -2,7 +2,7 @@ // retry, and an already-connected second device adopting the missing change. // Run: bun tools/sync-browser-test.js import assert from 'node:assert/strict'; -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import { verifyEvent } from 'nostr-tools/pure'; const build = await Bun.build({ entrypoints: ['./tools/sync-browser-harness.js'], target: 'browser', minify: true }); diff --git a/tools/thread-back-scroll-test.js b/tools/thread-back-scroll-test.js index b1f63b3..55c8817 100644 --- a/tools/thread-back-scroll-test.js +++ b/tools/thread-back-scroll-test.js @@ -1,7 +1,7 @@ // The thread page's on-screen Back returns to the feed at the row the reader // left, not the top. Run: bun tools/thread-back-scroll-test.js import assert from 'node:assert/strict'; -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import { generateSecretKey, finalizeEvent } from 'nostr-tools/pure'; import { buildHtml } from '../build.js'; const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); diff --git a/tools/thread-reply-test.js b/tools/thread-reply-test.js index 16b6f50..738a31c 100644 --- a/tools/thread-reply-test.js +++ b/tools/thread-reply-test.js @@ -1,7 +1,7 @@ // Real reply buttons/composer with controlled relay replies and no publishing. // Run: bun tools/thread-reply-test.js import assert from 'node:assert/strict'; -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; const app = await Bun.file('src/app.js').text(); const dom = app.slice(app.indexOf('function h(tag,'), app.indexOf('// ---------------------------------------------------------------- morphing')); const entry = ` diff --git a/tools/topic-depth-test.js b/tools/topic-depth-test.js index 9876224..d027e60 100644 --- a/tools/topic-depth-test.js +++ b/tools/topic-depth-test.js @@ -4,14 +4,14 @@ // Before: ~5 posts, then "You're all caught up". // // Run: bun tools/topic-depth-test.js -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import { buildHtml } from '../build.js'; const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); let ok = true; const check = (n, c, d = '') => { console.log(` ${c ? '✓' : '✗'} ${n}${d ? ' — ' + d : ''}`); if (!c) ok = false; }; const html = await buildHtml({ minify: true, pwa: false }); const server = Bun.serve({ port: 5285, fetch: () => new Response(html, { headers: { 'content-type': 'text/html' } }) }); -const browser = await puppeteer.launch({ executablePath: '/usr/bin/google-chrome', headless: 'new', args: ['--no-sandbox'] }); +const browser = await puppeteer.launch({ relays: 'read', executablePath: '/usr/bin/google-chrome', headless: 'new', args: ['--no-sandbox'] }); const page = await browser.newPage(); try { await page.setViewport({ width: 390, height: 844 }); diff --git a/tools/ui-morph-test.js b/tools/ui-morph-test.js index 343ccd2..ad685bc 100644 --- a/tools/ui-morph-test.js +++ b/tools/ui-morph-test.js @@ -3,7 +3,7 @@ // across background renders — the property the morph exists to provide. // Usage: bun build.js && bun tools/ui-morph-test.js import { spawn } from 'node:child_process'; -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; const PORT = 5230; const server = spawn('bun', ['--bun', 'x', 'serve', '-p', String(PORT), 'dist'], { stdio: 'ignore' }); diff --git a/tools/unifiedpush-test.js b/tools/unifiedpush-test.js index 9bdb4cb..b5bcb6b 100644 --- a/tools/unifiedpush-test.js +++ b/tools/unifiedpush-test.js @@ -10,7 +10,7 @@ // have worked on such a phone. // // Run: bun tools/unifiedpush-test.js -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import { generateMnemonic } from '@scure/bip39'; import { wordlist } from '@scure/bip39/wordlists/english'; import { buildHtml } from '../build.js'; diff --git a/tools/unread-dot-test.js b/tools/unread-dot-test.js index b98388a..3b43b7d 100644 --- a/tools/unread-dot-test.js +++ b/tools/unread-dot-test.js @@ -4,7 +4,7 @@ // seven times in the first seconds after a refresh, which reads as flashing. // // Run: bun tools/unread-dot-test.js -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import { generateMnemonic } from '@scure/bip39'; import { wordlist } from '@scure/bip39/wordlists/english'; import { buildHtml } from '../build.js'; diff --git a/tools/upload-progress-test.js b/tools/upload-progress-test.js index 25bf91c..8d4b24b 100644 --- a/tools/upload-progress-test.js +++ b/tools/upload-progress-test.js @@ -2,7 +2,7 @@ // upload events — fetch() can't), from 0 to 1, on a slow uplink. // // Run: bun tools/upload-progress-test.js -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; let ok = true; const check = (n, c, d = '') => { console.log(` ${c ? '✓' : '✗'} ${n}${d ? ' — ' + d : ''}`); if (!c) ok = false; }; const js = await Bun.build({ entrypoints: ['src/media-upload.js'], target: 'browser' }).then((b) => b.outputs[0].text()); diff --git a/tools/video-autoplay-test.js b/tools/video-autoplay-test.js index 93453ee..6b943f2 100644 --- a/tools/video-autoplay-test.js +++ b/tools/video-autoplay-test.js @@ -4,7 +4,7 @@ // loaded for a box that never comes into view. Set CLIP to a webm to serve; // the default is made with ffmpeg on the fly. // Run: bun tools/video-autoplay-test.js -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { buildHtml } from '../build.js'; diff --git a/tools/video-compress-test.js b/tools/video-compress-test.js index c1175f8..19735c8 100644 --- a/tools/video-compress-test.js +++ b/tools/video-compress-test.js @@ -4,7 +4,7 @@ // upload is caught and its bytes checked with ffprobe. // // Run: bun tools/video-compress-test.js -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); let ok = true; const check = (n, c, d = '') => { console.log(` ${c ? '✓' : '✗'} ${n}${d ? ' — ' + d : ''}`); if (!c) ok = false; }; diff --git a/tools/video-unmute-test.js b/tools/video-unmute-test.js index fec3955..ac7cfc4 100644 --- a/tools/video-unmute-test.js +++ b/tools/video-unmute-test.js @@ -2,7 +2,7 @@ // Run: bun tools/video-unmute-test.js // Phone: adb reverse tcp:5302 tcp:5302; BROWSER_URL=http://localhost:9223 bun tools/video-unmute-test.js import assert from 'node:assert/strict'; -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; const app = await Bun.file('src/app.js').text(); const messages = await Bun.file('src/features/messages.js').text(); const dom = app.slice(app.indexOf('function h(tag,'), app.indexOf("const root = document.getElementById('app');")); diff --git a/tools/youtube-embed-test.js b/tools/youtube-embed-test.js index 5d50b35..5133d1e 100644 --- a/tools/youtube-embed-test.js +++ b/tools/youtube-embed-test.js @@ -5,7 +5,7 @@ // decided to watch anything. // // Run: bun tools/youtube-embed-test.js -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import { generateMnemonic } from '@scure/bip39'; import { wordlist } from '@scure/bip39/wordlists/english'; import { cacheKeyFor } from '../src/wallet.js'; diff --git a/tools/zap-deep-profile-test.js b/tools/zap-deep-profile-test.js index 55c7b5d..3f0e72d 100644 --- a/tools/zap-deep-profile-test.js +++ b/tools/zap-deep-profile-test.js @@ -6,7 +6,7 @@ // at "not enough in Spending" — past the address lookup, which is the point). // // Run: bun tools/zap-deep-profile-test.js -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import { generateMnemonic } from '@scure/bip39'; import { wordlist } from '@scure/bip39/wordlists/english'; import { buildHtml } from '../build.js'; @@ -19,7 +19,7 @@ const check = (n, c, d = '') => { console.log(` ${c ? '✓' : '✗'} ${n}${d ? ' const html = await buildHtml({ minify: true, pwa: false }); const server = Bun.serve({ port: 5253, fetch: () => new Response(html, { headers: { 'content-type': 'text/html' } }) }); -const browser = await puppeteer.launch({ executablePath: '/usr/bin/google-chrome', headless: 'new', args: ['--no-sandbox'] }); +const browser = await puppeteer.launch({ relays: 'read', executablePath: '/usr/bin/google-chrome', headless: 'new', args: ['--no-sandbox'] }); const page = await browser.newPage(); await page.setViewport({ width: 420, height: 900 }); const click = (sel, t) => page.evaluate((s, x) => { const e = [...document.querySelectorAll(s)].find((n) => n.textContent.trim().toLowerCase().includes(x.toLowerCase())); if (e) { e.click(); return true; } return false; }, sel, t); diff --git a/tools/zap-feedback-test.js b/tools/zap-feedback-test.js index 838aef4..3fdd3bc 100644 --- a/tools/zap-feedback-test.js +++ b/tools/zap-feedback-test.js @@ -2,7 +2,7 @@ // settlement. No wallet funds, relays, or payment services are contacted. // Run: bun tools/zap-feedback-test.js import assert from 'node:assert/strict'; -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; const app = await Bun.file('src/app.js').text(); const domHelper = app.slice(app.indexOf('function h(tag,'), app.indexOf('// ---------------------------------------------------------------- morphing')); diff --git a/tools/zap-tally-cache-test.js b/tools/zap-tally-cache-test.js index b1e37b6..e5eb8dd 100644 --- a/tools/zap-tally-cache-test.js +++ b/tools/zap-tally-cache-test.js @@ -4,7 +4,7 @@ // tally that changed (or a receipt that was deleted) still settles correctly. // // Run: bun tools/zap-tally-cache-test.js -import puppeteer from 'puppeteer-core'; +import puppeteer from './puppeteer.js'; import { generateMnemonic } from '@scure/bip39'; import { wordlist } from '@scure/bip39/wordlists/english'; import { buildHtml } from '../build.js'; |
