diff options
100 files changed, 437 insertions, 324 deletions
@@ -1338,14 +1338,31 @@ function unitTag(cls = '') { // ================================================================ UNLOCK // The create/import card on its own — the unlock screen wraps it in the // classic chrome, the onboarding wizard's "Get started" step shows it bare. +// The front door's seed pages (first run, or "Sign into another account"): +// no tabs, and the page's own Back steps through the create flow. +function seedPages() { + return ui.screen === 'unlock' && !!((ui.onb && ui.onb.step === 'seed') || (ui.fromWallet && ui.addFlow)); +} +// Back within the create flow: the check → the passphrase → the words → the +// heads-up. False when there's no earlier step (the caller leaves the flow). +const CREATE_PREV = { gen: 'intro', pass: 'gen', confirm: 'pass' }; +function createStepBack() { + if (ui.unlockTab !== 'create' || ui.entropyPage || !CREATE_PREV[ui.createStep]) return false; + ui.createStep = CREATE_PREV[ui.createStep]; + ui.unlockError = ''; + render(); + return true; +} + function unlockCard() { // The entropy page takes the card over entirely — seed words, tabs and // other controls step aside until Submit or Back. if (ui.entropyPage) return h('div', { class: 'card col' }, entropyPage()); // Inside the first-run wizard the words are already on screen: no tabs, // just a line underneath for whoever brought their own seed (and one back). - const wizard = ui.screen === 'unlock' && ((ui.onb && ui.onb.step === 'seed') || (ui.fromWallet && ui.addFlow)); - const swap = wizard && ui.createStep !== 'confirm' ? h('button', { + const wizard = seedPages(); + // only before the words are up: once they're shown, it was already offered + const swap = wizard && (ui.unlockTab === 'import' || ui.createStep === 'intro') ? h('button', { class: 'linklike small', style: 'align-self:center', onClick: () => { clearSeedDrafts(); ui.unlockError = ''; @@ -1434,6 +1451,7 @@ function unlockScreen() { h('h2', { class: 'onb-title' }, t(ui.unlockTab === 'import' ? 'onbRestoreTitle' : 'onbSeedTitle')), unlockCard(), ui.entropyPage ? null : h('button', { class: 'linklike small', onClick: () => { + if (createStepBack()) return; const from = ui.unlockTab === 'import' ? 'doors' : 'welcome'; clearSeedDrafts(); ui.draftMnemonic = ''; ui.createStep = 'intro'; ui.confirm = []; ui.unlockError = ''; ui.addStep = from; render(); @@ -1603,33 +1621,47 @@ function createPane() { h('button', { class: 'btn-block', style: 'display:flex;align-items:center;justify-content:center;gap:8px', onClick: () => copy(ui.draftMnemonic) }, h('span', { style: 'display:flex', html: '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="14" height="14" x="8" y="8" rx="2" ry="2"/><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/></svg>' }), t('copy')), - optionsPanel(), h( 'button', { id: 'seed-next', class: 'btn-primary btn-block', - onClick: () => { - // catch a mistyped passphrase here, while the field to fix it is - // still on screen — the check only asks for one word - if (ui.passphrase && ui.confirmPass !== ui.passphrase) { - ui.unlockError = t('passphraseMismatch'); render(); return; - } - ui.confirm = pickConfirm(words); - ui.unlockError = ''; - ui.createStep = 'confirm'; - render(); - }, + onClick: () => { ui.unlockError = ''; ui.createStep = 'pass'; render(); }, }, t('next') ) ); } + // The passphrase, on a page of its own (Adam, 2026-11-07): what it is, + // that it's optional but recommended, and the field — a small "Add + // passphrase" button under the words was easy to miss. + if (ui.createStep === 'pass') { + const words = ui.draftMnemonic.split(' '); + const next = () => { + // a typo here IS a different wallet: caught while the fields are up + if (ui.passphrase && ui.confirmPass !== ui.passphrase) { + ui.unlockError = t('passphraseMismatch'); render(); return; + } + ui.confirm = pickConfirm(words); + ui.unlockError = ''; + ui.createStep = 'confirm'; + render(); + }; + return h( + 'div', + { class: 'col' }, + h('h3', { style: 'margin:0' }, t('passStepTitle')), + h('p', { style: 'margin:0' }, t('passStepBody', { n: words.length })), + h('p', { class: 'muted', style: 'margin:0' }, t('passStepNote')), + ...passFields(), + h('button', { id: 'pass-next', class: 'btn-primary btn-block', onClick: next }, t(ui.passphrase ? 'next' : 'passStepNone')), + ); + } // The check: which word is #5? One tap from a few of the phrase's own // words, or Skip (Adam, 2026-11-07) — no typing. const q = ui.confirm; - if (!q || !q.options) { ui.createStep = 'gen'; return createPane(); } + if (!q || !q.options) { ui.createStep = 'pass'; return createPane(); } return h( 'div', { class: 'col' }, @@ -1647,7 +1679,8 @@ function createPane() { }, w))), q.wrong && q.wrong.length ? h('div', { class: 'notice err' }, t('quizWrong')) : null, h('button', { id: 'seed-skip', class: 'btn-ghost btn-block', onClick: () => { ui.unlockError = ''; openWallet(ui.draftMnemonic, { generated: true }); } }, t('skip')), - h('button', { class: 'linklike small', style: 'align-self:center', onClick: () => { ui.createStep = 'gen'; ui.unlockError = ''; render(); } }, t('quizBack')), + // outside the seed pages (no Back of their own), a way back to the words + seedPages() ? null : h('button', { class: 'linklike small', style: 'align-self:center', onClick: () => { ui.createStep = 'pass'; ui.unlockError = ''; render(); } }, t('back')), ); } @@ -1753,22 +1786,28 @@ function optionsPanel() { // already typed keeps it open. if (!ui.passOpen && !ui.passphrase) { return [h('button', { - class: 'btn-ghost btn-sm', type: 'button', style: 'align-self:flex-start', + class: 'btn-block', type: 'button', onClick: (e) => { e.preventDefault(); ui.passOpen = true; render(); }, }, t('addPassphrase'))]; } + return passFields({ label: true }); +} + +// The passphrase field (+ its confirmation once something is typed). The +// create flow's own page explains it above; the import pane labels it here. +function passFields({ label = false } = {}) { const pass = h( 'label', { class: 'field' }, h('span', { class: 'lab' }, - t('passphraseOptional'), + t(label ? 'passphraseOptional' : 'passphrase'), // People assume a password logs in or encrypts; a BIP39 passphrase does // neither, and getting that wrong loses money — worth a sentence here. h('button', { class: 'linklike small', type: 'button', style: 'margin-left:8px', onClick: (e) => { e.preventDefault(); ui.passInfoOpen = !ui.passInfoOpen; render(); }, }, t('passWhatsThis'))), - ui.passInfoOpen ? h('p', { class: 'small muted', style: 'margin:0 0 6px' }, t('passInfo')) : h('p', { class: 'small muted', style: 'margin:0 0 6px' }, t('passOptionalHint')), + ui.passInfoOpen ? h('p', { class: 'small muted', style: 'margin:0 0 6px' }, t('passInfo')) : label ? h('p', { class: 'small muted', style: 'margin:0 0 6px' }, t('passOptionalHint')) : null, h( 'div', { class: 'input-group' }, @@ -1797,7 +1836,7 @@ function optionsPanel() { class: 'mono-input', autocomplete: 'off', value: ui.confirmPass, - onInput: (e) => (ui.confirmPass = e.target.value), + onInput: (e) => { ui.confirmPass = e.target.value; if (ui.unlockError && ui.confirmPass === ui.passphrase) { ui.unlockError = ''; render(); } }, }) ) : null; @@ -4412,7 +4451,7 @@ function onboardScreen() { title(t(ui.unlockTab === 'import' ? 'onbRestoreTitle' : 'onbSeedTitle')), unlockCard(), // the entropy page brings its own Back — one is enough - ui.entropyPage ? null : h('button', { class: 'linklike small', onClick: () => { ui.unlockError = ''; o.step = ui.unlockTab === 'import' ? 'doors' : 'welcome'; render(); } }, t('back')), + ui.entropyPage ? null : h('button', { class: 'linklike small', onClick: () => { if (createStepBack()) return; ui.unlockError = ''; o.step = ui.unlockTab === 'import' ? 'doors' : 'welcome'; render(); } }, t('back')), ]); } if (o.step === 'signin') { diff --git a/src/features/messages.js b/src/features/messages.js index 90bfaaf..6c4f5f8 100644 --- a/src/features/messages.js +++ b/src/features/messages.js @@ -760,14 +760,38 @@ export function messagesFeature(ctx) { let allUnsubs = []; let dmUnsubs = []; // the DM wrap subscriptions alone, so they can be rebuilt + // Is the page under the reader's finger, or still coasting from a flick? + // Background work (a repaint for a face that arrived, writing the profile + // cache) waits for it to settle: on Adam's Pixel a flick through the feed + // spent 1.7 s of 9 on the main thread doing exactly that, and taps queued + // behind it (2026-11-07, "flick to scroll … not getting up to speed … + // clicking around feels sluggish"). Never longer than `most` ms, though: + // a slow, steady read must still see what arrived. + let pageMovedAt = 0, fingerDown = false; + if (typeof window !== 'undefined') { + window.addEventListener('scroll', () => { pageMovedAt = Date.now(); }, { passive: true, capture: true }); + window.addEventListener('touchstart', () => { fingerDown = true; }, { passive: true, capture: true }); + for (const n of ['touchend', 'touchcancel']) window.addEventListener(n, (e) => { if (!e.touches || !e.touches.length) fingerDown = false; }, { passive: true, capture: true }); + } + const pageMoving = () => fingerDown || Date.now() - pageMovedAt < 200; + const whenSettled = (fn, most = 1500, step = 150) => { + const until = Date.now() + most; + const go = () => { if (pageMoving() && Date.now() < until) { setTimeout(go, step); return; } fn(); }; + go(); + }; + let repaintTimer = null; const scheduleRepaint = () => { if (repaintTimer) return; + const since = Date.now(); const run = () => { // not under a pager in motion: the page beneath is out of sight, and // its repaint (40–55 ms on Adam's phone) dropped frames from flights // through the pictures (2026-10-17) — it waits for the landing if (pagerMoving()) { repaintTimer = setTimeout(run, 120); return; } + // nor mid-flick on the page itself (up to 1.5 s): a whole-page morph + // in the middle of a fling is a dropped frame and a late tap + if (pageMoving() && Date.now() - since < 1500) { repaintTimer = setTimeout(run, 150); return; } repaintTimer = null; // a visitor's public page (a profile, a thread, a feed) repaints too if (ui.screen === 'wallet' || ui.pubProf) renderThreadStable(); @@ -826,11 +850,14 @@ export function messagesFeature(ctx) { // memory and is written back once per burst: a batch of thirty profiles // landing during a scroll used to parse and re-serialize the whole blob // thirty times over, on the main thread, mid-scroll. - let profBlob = null, profFlush = null; + let profBlob = null, profFlush = null, profFlushSince = 0; const PROFILE_ROWS = 1500; // a row without a thumbnail is ~430 chars; IndexedDB has room const profilesDisk = () => (profBlob ||= wallet.loadFeatureState('profiles', {})); - function flushProfiles() { - profFlush = null; + function flushProfiles(force) { + // up to 5 s for the page to settle: stringifying this blob (thumbnails + // and all, up to ~2 MB) is a long main-thread task + if (!force && pageMoving() && Date.now() - (profFlushSince || 0) < 5000) { profFlush = setTimeout(flushProfiles, 300); return; } + profFlush = null; profFlushSince = 0; const s = profBlob; if (!s) return; // Who stays when space runs out: your own faces, the people you follow, @@ -848,7 +875,10 @@ export function messagesFeature(ctx) { // Thumbnails are the bulk of this blob, so they live on a budget: the // least recently seen strangers give theirs up first. The row itself // stays — that face just paints the way it used to. - let size = JSON.stringify(s).length; + // (an estimate, not a JSON.stringify of the lot just to measure it: + // a row is ~430 chars plus its thumbnail) + let size = 0; + for (const k in s) size += 430 + ((s[k] && s[k].thumb) || '').length; if (size > THUMB_BUDGET) { // never our own: the header face is the one that must paint offline for (const k of evictFirst(Object.keys(s).filter((k) => s[k].thumb && !mine.has(k)))) { @@ -875,7 +905,7 @@ export function messagesFeature(ctx) { ? { thumb: p.thumb, thumbFor: p.thumbFor, thumbPx: p.thumbPx || 0 } : {}), ...(p.thumbFail ? { thumbFail: p.thumbFail, thumbFailAt: p.thumbFailAt || 0, thumbFails: p.thumbFails || 1, thumbFailVersion: p.thumbFailVersion || 0 } : {}) }; - if (!profFlush) profFlush = setTimeout(flushProfiles, 500); + if (!profFlush) { profFlushSince = Date.now(); profFlush = setTimeout(flushProfiles, 500); } } // A punk picture is OUR OWN art. Every coinos user who keeps the default // publishes https://v3.coinos.io/punks/N.webp as their nostr picture, and @@ -1081,6 +1111,39 @@ export function messagesFeature(ctx) { blobProbing.set(url, task); return task; } + // Centre-crop to a square (the way background-size:cover paints it), + // shrink, and encode — off the main thread where the browser allows: the + // crop and resize through createImageBitmap, the encode through an + // OffscreenCanvas's convertToBlob. The synchronous canvas.toDataURL it + // replaces cost ~30 ms a face on a phone, mid-scroll (2026-11-07). + async function encodeThumb(bmp) { + const side = Math.min(bmp.width, bmp.height); + if (!side) throw new Error('empty'); + const sx = (bmp.width - side) / 2, sy = (bmp.height - side) / 2; + if (typeof OffscreenCanvas !== 'undefined' && OffscreenCanvas.prototype.convertToBlob) { + let small = null; + try { + small = await createImageBitmap(bmp, sx, sy, side, side, { resizeWidth: THUMB_PX, resizeHeight: THUMB_PX, resizeQuality: 'high' }); + const oc = new OffscreenCanvas(THUMB_PX, THUMB_PX); + oc.getContext('2d').drawImage(small, 0, 0); + const asUrl = (b) => new Promise((ok, bad) => { const r = new FileReader(); r.onload = () => ok(r.result); r.onerror = () => bad(r.error); r.readAsDataURL(b); }); + let blob = await oc.convertToBlob({ type: 'image/webp', quality: 0.75 }); + if (blob.type !== 'image/webp') blob = await oc.convertToBlob({ type: 'image/jpeg', quality: 0.7 }); + if (blob.size * 4 / 3 > THUMB_MAX) blob = await oc.convertToBlob({ type: 'image/webp', quality: 0.5 }); // a busy picture, leaned on harder + const data = await asUrl(blob); + if (data.length > THUMB_MAX) throw new Error('too big'); + return data; + } finally { try { small && small.close(); } catch {} } + } + const c = document.createElement('canvas'); + c.width = c.height = THUMB_PX; + c.getContext('2d').drawImage(bmp, sx, sy, side, side, 0, 0, THUMB_PX, THUMB_PX); + let data = c.toDataURL('image/webp', 0.75); + if (!data.startsWith('data:image/webp')) data = c.toDataURL('image/jpeg', 0.7); + if (data.length > THUMB_MAX) data = c.toDataURL('image/webp', 0.5); + if (data.length > THUMB_MAX) throw new Error('too big'); + return data; + } function makeThumb(pk, p) { if (!p || !p.picture || typeof document === 'undefined') return; if (noCorsHost(p.picture)) return; // a host that won't hand over bytes; the <img> shows it anyway @@ -1152,18 +1215,8 @@ export function messagesFeature(ctx) { scheduleRepaint(); // the big face, where one is up, can paint from the copy now } bmp = await createImageBitmap(await res.blob()); - // centre-crop to a square, the way background-size:cover paints it - const side = Math.min(bmp.width, bmp.height); - if (!side) throw new Error('empty'); - const c = document.createElement('canvas'); - c.width = c.height = THUMB_PX; - c.getContext('2d').drawImage(bmp, (bmp.width - side) / 2, (bmp.height - side) / 2, - side, side, 0, 0, THUMB_PX, THUMB_PX); - let data = c.toDataURL('image/webp', 0.75); - if (!data.startsWith('data:image/webp')) data = c.toDataURL('image/jpeg', 0.7); - if (data.length > THUMB_MAX) data = c.toDataURL('image/webp', 0.5); // a busy picture, leaned on harder - if (data.length > THUMB_MAX) throw new Error('too big'); - done({ thumb: data, thumbFor: url, thumbPx: THUMB_PX }); + const data = await encodeThumb(bmp); + whenSettled(() => done({ thumb: data, thumbFor: url, thumbPx: THUMB_PX })); } catch (e) { // a quick TypeError is the CORS refusal (a dead host takes longer) // only a refusal corsFetch confirmed (a body cut off mid-download @@ -19062,7 +19115,7 @@ export function messagesFeature(ctx) { follows = null; followsAt = 0; feed = null; relayLists = null; mutes = null; mutesAt = 0; stCache = null; stAt = 0; // the next account's state, not this one's - if (profFlush) { clearTimeout(profFlush); flushProfiles(); } // this account's faces, written before the blob is dropped + if (profFlush) { clearTimeout(profFlush); flushProfiles(true); } // this account's faces, written before the blob is dropped profBlob = null; timedActivity.clear(); diskHeld.clear(); reacts.clear(); boosts.clear(); seenNoteEv.clear(); myReactEv.clear(); quoted.clear(); noteCountsReady.clear(); diff --git a/src/i18n.js b/src/i18n.js index a985ff9..b45638e 100644 --- a/src/i18n.js +++ b/src/i18n.js @@ -201,6 +201,10 @@ const STR = { seedIntroPrivate: 'Anyone who sees them can take your coins, so make sure no one is looking at your screen.', seedIntroShow: 'Show seed phrase', next: 'Next', + passStepTitle: 'Add a passphrase?', + passStepBody: 'A passphrase is an extra secret you choose yourself, on top of your {n} words. Anyone who finds your seed phrase still can’t open your wallet without it.', + passStepNote: 'It’s optional, but recommended. Write it down with your seed phrase: you’ll need both to recover your wallet, exactly as written, and nobody can reset it for you.', + passStepNone: 'Continue without one', skip: 'Skip', quizPrompt: 'Which one is word #{n}?', quizWrong: 'Not that one. Check what you wrote down.', diff --git a/tools/account-switch-leak-test.js b/tools/account-switch-leak-test.js index ea3b4ee..aa06579 100644 --- a/tools/account-switch-leak-test.js +++ b/tools/account-switch-leak-test.js @@ -3,7 +3,7 @@ // under its key, so a throwaway account showed adam's zaps and reactions for // good. Also: a DM cache already polluted that way is swept on load. // Run: bun tools/account-switch-leak-test.js -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { privateKeyFromSeedWords } from 'nostr-tools/nip06'; @@ -61,9 +61,9 @@ const nav = (label) => page.evaluate((l) => document.querySelector(`.app-nav-but async function newWallet() { await click('create a new wallet'); await sleep(600); if (!(await page.$('.words .w .t'))) { await click('generate'); await sleep(600); } - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); return mn; } diff --git a/tools/amber-dm-upgrade-test.js b/tools/amber-dm-upgrade-test.js index 250e8ef..3a1917a 100644 --- a/tools/amber-dm-upgrade-test.js +++ b/tools/amber-dm-upgrade-test.js @@ -7,7 +7,7 @@ // relays hold is opened again — including ones the old code wrote off. // The remote signer is played by this script over a shimmed relay. // Run: bun tools/amber-dm-upgrade-test.js (and ALL_LOGGED=1 bun tools/amber-dm-upgrade-test.js) -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import * as nip44 from 'nostr-tools/nip44'; @@ -102,9 +102,9 @@ try { await page.evaluate(() => localStorage.setItem('btc-wallet-network', 'regtest')); await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const base = cacheKeyFor(mn + '\n'); // an Amber login, and a wrap log in which the old code had already diff --git a/tools/avatar-profile-test.js b/tools/avatar-profile-test.js index 5c5293d..6d6c944 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.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { generateMnemonic } from '@scure/bip39'; import { wordlist } from '@scure/bip39/wordlists/english'; import { cacheKeyFor } from '../src/wallet.js'; @@ -25,9 +25,9 @@ try { await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const base = cacheKeyFor(mn + '\n'); const A = 'a'.repeat(63) + '9', B = 'b'.repeat(63) + '7'; diff --git a/tools/call-test.js b/tools/call-test.js index d5c3909..44f9b46 100644 --- a/tools/call-test.js +++ b/tools/call-test.js @@ -5,7 +5,7 @@ // both. A call can be declined, and a stranger's (neither followed nor // written to) doesn't ring, but is in the conversation as a missed call. Everything on the wire is an ephemeral gift wrap. // Run: bun tools/call-test.js (NO_BUILD=1 to reuse dist/) -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { privateKeyFromSeedWords } from 'nostr-tools/nip06'; import { getPublicKey } from 'nostr-tools/pure'; @@ -74,9 +74,9 @@ async function wallet(name) { await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive'); return { page, mn, pk: getPublicKey(privateKeyFromSeedWords(mn)), base: cacheKeyFor(mn + '\n'), click, waitText, name }; } diff --git a/tools/call-wake-test.js b/tools/call-wake-test.js index b0f64cc..3f81db1 100644 --- a/tools/call-wake-test.js +++ b/tools/call-wake-test.js @@ -6,7 +6,7 @@ // they talk. A push for a stranger's call, or for something that isn't an // offer, rings nothing. Plus the worker's check on its own (classifyCall). // Run: bun tools/call-wake-test.js (NO_BUILD=1 to reuse dist/) -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { privateKeyFromSeedWords } from 'nostr-tools/nip06'; import { getPublicKey, finalizeEvent, generateSecretKey } from 'nostr-tools/pure'; @@ -91,9 +91,9 @@ async function wallet(name) { await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive'); const sk = privateKeyFromSeedWords(mn); return { context, page, mn, sk, pk: getPublicKey(sk), base: cacheKeyFor(mn + '\n'), click, waitText, name }; diff --git a/tools/comments-test.js b/tools/comments-test.js index 5a73e6f..21d028d 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.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { SimplePool } from 'nostr-tools/pool'; import { buildHtml } from '../build.js'; const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); @@ -86,8 +86,8 @@ try { await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); - await click('skip verification'); + await seedWords(page); + await skipSeedCheck(page); await waitText('receive', 20000); await page.goto('http://localhost:5283/' + NEVENT, { waitUntil: 'domcontentloaded' }); const first = comments.sort((a, b) => a.created_at - b.created_at)[0]; diff --git a/tools/composer-media-test.js b/tools/composer-media-test.js index 94b343a..db1648a 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.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { buildHtml } from '../build.js'; const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); @@ -42,9 +42,9 @@ try { await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); await page.evaluate(() => document.querySelector('button[aria-label="Settings"]').click()); await sleep(300); diff --git a/tools/dm-nip42-test.js b/tools/dm-nip42-test.js index 51fa3a9..debe910 100644 --- a/tools/dm-nip42-test.js +++ b/tools/dm-nip42-test.js @@ -6,7 +6,7 @@ // copy of what we send in our own inbox so other clients see it too. // Both inboxes are played by this script's WebSocket shim. // Run: bun tools/dm-nip42-test.js -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent, verifyEvent } from 'nostr-tools/pure'; import { privateKeyFromSeedWords } from 'nostr-tools/nip06'; @@ -87,9 +87,9 @@ try { await page.evaluate(() => localStorage.setItem('btc-wallet-network', 'regtest')); await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const ME_SK = privateKeyFromSeedWords(mn); const ME = getPublicKey(ME_SK); const base = cacheKeyFor(mn + '\n'); diff --git a/tools/dm-row-resort-test.js b/tools/dm-row-resort-test.js index 9e400bc..0ecdd44 100644 --- a/tools/dm-row-resort-test.js +++ b/tools/dm-row-resort-test.js @@ -3,7 +3,7 @@ // it was first drawn for — a tap on Bill's face opened someone else's // profile. After the re-sort, each row's avatar and row open their own. // Run: bun tools/dm-row-resort-test.js -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { privateKeyFromSeedWords } from 'nostr-tools/nip06'; @@ -67,9 +67,9 @@ try { await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('Receive', 20000); const ME = getPublicKey(privateKeyFromSeedWords(mn)); const base = cacheKeyFor(mn + '\n'); diff --git a/tools/feed-appetite-test.js b/tools/feed-appetite-test.js index b79a2a9..9f469cb 100644 --- a/tools/feed-appetite-test.js +++ b/tools/feed-appetite-test.js @@ -6,7 +6,7 @@ // still a while brings the ordinary page back. A page held still under an // insert above is not travel. // Run: bun tools/feed-appetite-test.js -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { buildHtml } from '../build.js'; @@ -79,9 +79,9 @@ try { await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const base = cacheKeyFor(mn + '\n'); await page.evaluate(([k, pk, ns]) => { diff --git a/tools/feed-conversations-test.js b/tools/feed-conversations-test.js index f16fc35..7394986 100644 --- a/tools/feed-conversations-test.js +++ b/tools/feed-conversations-test.js @@ -4,7 +4,7 @@ // profile's Replies tab shows the same context; the view persists across // feeds and reloads like the other views. // Run: bun tools/feed-conversations-test.js -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { buildHtml } from '../build.js'; @@ -48,9 +48,9 @@ try { await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const base = cacheKeyFor(mn + '\n'); await page.evaluate(([k, alice, bob, roots, replies]) => { diff --git a/tools/feed-custom-test.js b/tools/feed-custom-test.js index 122cb0a..c5ed41f 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.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { generateMnemonic } from '@scure/bip39'; import { wordlist } from '@scure/bip39/wordlists/english'; import { cacheKeyFor } from '../src/wallet.js'; @@ -90,9 +90,9 @@ try { await sleep(400); // through the real first run: the wizard's own words, read off the screen await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const base = cacheKeyFor(mn + '\n'); // the starter follows land once the current list has been asked for diff --git a/tools/feed-list-test.js b/tools/feed-list-test.js index 6d60240..00942b8 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.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { privateKeyFromSeedWords } from 'nostr-tools/nip06'; @@ -83,9 +83,9 @@ try { await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const base = cacheKeyFor(mn + '\n'); const ME_SK = privateKeyFromSeedWords(mn); diff --git a/tools/feed-media-cache-test.js b/tools/feed-media-cache-test.js index 5a26ca2..8ca8185 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.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { buildHtml } from '../build.js'; @@ -115,9 +115,9 @@ try { await page.evaluate(() => localStorage.setItem('btc-wallet-network', 'regtest')); await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const key = cacheKeyFor(mn + '\n'); await page.evaluate(([k, pk]) => { diff --git a/tools/feed-media-views-test.js b/tools/feed-media-views-test.js index dac9804..74de24a 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.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { buildHtml } from '../build.js'; @@ -112,9 +112,9 @@ try { await page.evaluate(() => localStorage.setItem('btc-wallet-network', 'regtest')); await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const key = cacheKeyFor(mn + '\n'); await page.evaluate(([k, pk, ns]) => { diff --git a/tools/feed-mention-tap-test.js b/tools/feed-mention-tap-test.js index e4d3f7a..d381288 100644 --- a/tools/feed-mention-tap-test.js +++ b/tools/feed-mention-tap-test.js @@ -2,7 +2,7 @@ // thread on top of them (the mention's tap reached the row, which opened // the thread over the profile it had just shown). Links in a post likewise. // Run: bun tools/feed-mention-tap-test.js -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { npubEncode } from 'nostr-tools/nip19'; @@ -33,9 +33,9 @@ try { await page.evaluate(() => localStorage.setItem('btc-wallet-network', 'regtest')); await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('Receive', 20000); const base = cacheKeyFor(mn + '\n'); await page.evaluate(([k, a, f, ns]) => { diff --git a/tools/feed-pager-ahead-test.js b/tools/feed-pager-ahead-test.js index 057ac3e..0906034 100644 --- a/tools/feed-pager-ahead-test.js +++ b/tools/feed-pager-ahead-test.js @@ -3,7 +3,7 @@ // feed pages on, and the pager grows — past a run of posts with nothing to // show, too — so the reader never meets the end while there is more. // Run: bun tools/feed-pager-ahead-test.js -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { buildHtml } from '../build.js'; @@ -96,9 +96,9 @@ try { await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const base = cacheKeyFor(mn + '\n'); await page.evaluate(([k, pk, ns]) => { diff --git a/tools/feed-picture-pager-test.js b/tools/feed-picture-pager-test.js index 4cbe68c..f74c981 100644 --- a/tools/feed-picture-pager-test.js +++ b/tools/feed-picture-pager-test.js @@ -4,7 +4,7 @@ // closes it, as before; once the reader has moved on, a tap shows the post's // details and the next tap (or Escape) closes the pager onto that post. // Run: bun tools/feed-picture-pager-test.js -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { buildHtml } from '../build.js'; @@ -67,9 +67,9 @@ try { await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const base = cacheKeyFor(mn + '\n'); await page.evaluate(([k, pk, ns]) => { diff --git a/tools/feed-reboost-test.js b/tools/feed-reboost-test.js index e22df91..f43e7b5 100644 --- a/tools/feed-reboost-test.js +++ b/tools/feed-reboost-test.js @@ -4,7 +4,7 @@ // reload — stays out. A repost of a post you haven't read still lands, and // the row you read keeps its place. // Run: bun tools/feed-reboost-test.js -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { buildHtml } from '../build.js'; @@ -95,9 +95,9 @@ try { await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const base = cacheKeyFor(mn + '\n'); await page.evaluate(([k, pks]) => { diff --git a/tools/feed-repost-test.js b/tools/feed-repost-test.js index 4936dd6..1bf3d7a 100644 --- a/tools/feed-repost-test.js +++ b/tools/feed-repost-test.js @@ -4,7 +4,7 @@ // copy doesn't check out is left out, and a note already in the feed isn't // shown twice. // Run: bun tools/feed-repost-test.js -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { privateKeyFromSeedWords } from 'nostr-tools/nip06'; @@ -48,9 +48,9 @@ try { await page.evaluate(() => localStorage.setItem('btc-wallet-network', 'regtest')); await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const base = cacheKeyFor(mn + '\n'); await page.evaluate(([k, z, l, o]) => { diff --git a/tools/feed-switch-seed-test.js b/tools/feed-switch-seed-test.js index bf2b830..6d9d4f0 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.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { buildHtml } from '../build.js'; @@ -70,9 +70,9 @@ try { await page.evaluate(() => localStorage.setItem('btc-wallet-network', 'regtest')); await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const base = cacheKeyFor(mn + '\n'); await page.evaluate(([k, a, b, ns]) => { diff --git a/tools/feed-video-aspect-test.js b/tools/feed-video-aspect-test.js index 84c0568..4196fe1 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.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { buildHtml } from '../build.js'; @@ -96,9 +96,9 @@ try { await page.evaluate(() => localStorage.setItem('btc-wallet-network', 'regtest')); await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const key = cacheKeyFor(mn + '\n'); await page.evaluate(([k, pk, ns]) => { diff --git a/tools/feed-video-pager-test.js b/tools/feed-video-pager-test.js index 61119a0..5bba7d8 100644 --- a/tools/feed-video-pager-test.js +++ b/tools/feed-video-pager-test.js @@ -4,7 +4,7 @@ // Each clip has a seek bar to drag; a tap pauses (and shows the details), // another plays on. Leaving full screen leaves the pager. // Run: bun tools/feed-video-pager-test.js -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { buildHtml } from '../build.js'; @@ -82,9 +82,9 @@ try { await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const base = cacheKeyFor(mn + '\n'); await page.evaluate(([k, pk, ns]) => { diff --git a/tools/feed-window-dom-test.js b/tools/feed-window-dom-test.js index c86e98e..1f6ff93 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.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { buildHtml } from '../build.js'; @@ -78,9 +78,9 @@ try { await page.evaluate(() => localStorage.setItem('btc-wallet-network', 'regtest')); await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const base = cacheKeyFor(mn + '\n'); await page.evaluate(([k, pk, ns]) => { diff --git a/tools/fullscreen-notice-test.js b/tools/fullscreen-notice-test.js index d3b2785..a7c041a 100644 --- a/tools/fullscreen-notice-test.js +++ b/tools/fullscreen-notice-test.js @@ -5,7 +5,7 @@ // (feed-video-pager-test). A feed clip's own bar has no full-screen button: // a tap on the clip is the way, into the pager. // Run: bun tools/fullscreen-notice-test.js -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { buildHtml } from '../build.js'; @@ -79,9 +79,9 @@ async function run(lockOk) { await page.evaluate(() => localStorage.setItem('btc-wallet-network', 'regtest')); await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const base = cacheKeyFor(mn + '\n'); await page.evaluate(([k, pk, ns]) => { diff --git a/tools/gif-picker-test.js b/tools/gif-picker-test.js index f72cde3..9a48de9 100644 --- a/tools/gif-picker-test.js +++ b/tools/gif-picker-test.js @@ -4,7 +4,7 @@ // In a DM the picked GIF is sent at once. Uses the live API — localhost is // admitted as nostr.build's shared development client. // Run: bun tools/gif-picker-test.js -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey } from 'nostr-tools/pure'; import { buildHtml } from '../build.js'; @@ -35,9 +35,9 @@ try { await page.evaluate(() => localStorage.setItem('btc-wallet-network', 'regtest')); await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const base = cacheKeyFor(mn + '\n'); await page.evaluate(([k, pk]) => { diff --git a/tools/go-live-test.js b/tools/go-live-test.js index c3fc8e3..3990fa6 100644 --- a/tools/go-live-test.js +++ b/tools/go-live-test.js @@ -6,7 +6,7 @@ // recording, and the recording is complete. // Run: bun tools/go-live-test.js (LIVE_SERVER=path to coinosv3-server/live/server.js; NO_BUILD=1 to reuse dist/; // LIVE_CONFIG=a json with {sfuAppId, sfuSecret} to watch over WebRTC — the real Cloudflare SFU) -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { naddrEncode } from 'nostr-tools/nip19'; import { mkdtempSync, rmSync, readFileSync, writeFileSync } from 'node:fs'; @@ -129,9 +129,9 @@ try { await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive'); const base = cacheKeyFor(mn + '\n'); await page.evaluate((k) => { diff --git a/tools/grid-early-player-test.js b/tools/grid-early-player-test.js index 0ab1c1e..04e44fb 100644 --- a/tools/grid-early-player-test.js +++ b/tools/grid-early-player-test.js @@ -14,7 +14,7 @@ // that); on a link slower than the clip, more — to the 4 MB cap. With Data // Saver on, the pager is careful again: such a clip waits for a tap and // nothing loads ahead. -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { buildHtml } from '../build.js'; @@ -100,9 +100,9 @@ async function run() { await page.evaluate(() => localStorage.setItem('btc-wallet-network', 'regtest')); await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const base = cacheKeyFor(mn + '\n'); await page.evaluate(([k, pk, ns]) => { diff --git a/tools/history-export-test.js b/tools/history-export-test.js index f7fff82..877b95e 100644 --- a/tools/history-export-test.js +++ b/tools/history-export-test.js @@ -2,7 +2,7 @@ // movements) as a line of a CSV file, oldest first, with what was paid, the // fee beside it and the change to the balance; a failed payment is left out. // Run: bun tools/history-export-test.js (NO_BUILD=1 to reuse dist/) -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); let ok = true; @@ -43,9 +43,9 @@ try { await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive'); // Spending's history, as the Ark manager stores it (moved into IndexedDB at the next boot) const key = cacheKeyFor(mn + '\n') + ':ark:' + ns('http://localhost:3535'); diff --git a/tools/image-tap-test.js b/tools/image-tap-test.js index d3bcb7e..ce40d53 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.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { buildHtml } from '../build.js'; const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); @@ -27,9 +27,9 @@ try { await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const base = cacheKeyFor(mn + '\n'); const A = 'a'.repeat(63) + '9'; diff --git a/tools/lightbox-scroll-test.js b/tools/lightbox-scroll-test.js index be03e9b..f507557 100644 --- a/tools/lightbox-scroll-test.js +++ b/tools/lightbox-scroll-test.js @@ -6,7 +6,7 @@ // thread had been opened from, or to the top. Back out of a thread still // lands on the row that opened it. // Run: bun tools/lightbox-scroll-test.js -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { buildHtml } from '../build.js'; @@ -56,9 +56,9 @@ try { await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const base = cacheKeyFor(mn + '\n'); await page.evaluate(([k, pk, ns]) => { diff --git a/tools/link-preview-test.js b/tools/link-preview-test.js index 52ddca1..190b220 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.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { privateKeyFromSeedWords } from 'nostr-tools/nip06'; @@ -80,9 +80,9 @@ try { await page.evaluate(() => localStorage.setItem('btc-wallet-network', 'regtest')); await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const base = cacheKeyFor(mn + '\n'); // follow the author, so the feed asks for their posts diff --git a/tools/live-stream-test.js b/tools/live-stream-test.js index 2be6051..56dcb71 100644 --- a/tools/live-stream-test.js +++ b/tools/live-stream-test.js @@ -6,7 +6,7 @@ // profile says they're on, Back stops the player, the browser without HLS // of its own plays it through hls.js, and a zap carries the stream's `a`. // Run: bun tools/live-stream-test.js -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { naddrEncode } from 'nostr-tools/nip19'; @@ -150,9 +150,9 @@ try { await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive'); const base = cacheKeyFor(mn + '\n'); await page.evaluate(([k, pk, ns]) => { diff --git a/tools/media-grid-dups-test.js b/tools/media-grid-dups-test.js index e7b67da..1a00222 100644 --- a/tools/media-grid-dups-test.js +++ b/tools/media-grid-dups-test.js @@ -5,7 +5,7 @@ // that won't load leaves the Images grid instead of a broken-picture glyph. And on a big screen with a mouse, a // small clip opened in the pager is drawn at most 1.5× its own pixels, not // blown up across the monitor. Run: bun tools/media-grid-dups-test.js -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { buildHtml } from '../build.js'; @@ -92,9 +92,9 @@ try { await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const base = cacheKeyFor(mn + '\n'); await page.evaluate(([k, pk, ns]) => { diff --git a/tools/media-grid-order-test.js b/tools/media-grid-order-test.js index 9274d92..0e333f0 100644 --- a/tools/media-grid-order-test.js +++ b/tools/media-grid-order-test.js @@ -3,7 +3,7 @@ // jumped around): tiles found later go under the ones already showing, in // their own date order, even when they are dated between them; only a post // newer than the top tile goes above it. Run: bun tools/media-grid-order-test.js -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { buildHtml } from '../build.js'; @@ -97,9 +97,9 @@ try { await page.evaluate(() => localStorage.setItem('btc-wallet-network', 'regtest')); await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const key = cacheKeyFor(mn + '\n'); await page.evaluate(([k, pk]) => { diff --git a/tools/media-moments-feed-test.js b/tools/media-moments-feed-test.js index 7b429c9..5b4c8fb 100644 --- a/tools/media-moments-feed-test.js +++ b/tools/media-moments-feed-test.js @@ -1,6 +1,6 @@ // Full feed integration with local media and relay acknowledgements; no events leave the test. // Run: bun tools/media-moments-feed-test.js -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { buildHtml } from '../build.js'; @@ -111,9 +111,9 @@ try { await page.evaluate(() => localStorage.setItem('btc-wallet-network', 'regtest')); await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const key = cacheKeyFor(mn + '\n'); await page.evaluate(([k, pk, ns]) => { diff --git a/tools/mention-ac-test.js b/tools/mention-ac-test.js index 52ba1ef..e476e61 100644 --- a/tools/mention-ac-test.js +++ b/tools/mention-ac-test.js @@ -2,7 +2,7 @@ // people — the ones you follow first, then the search — and picking one puts // nostr:npub… in the text; the post goes out with their p tag. // Run: bun tools/mention-ac-test.js -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey } from 'nostr-tools/pure'; import { npubEncode } from 'nostr-tools/nip19'; @@ -59,9 +59,9 @@ try { await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); await sleep(3000); // a new wallet's starter follows land first; ours go over them const base = cacheKeyFor(mn + '\n'); diff --git a/tools/moderation-test.js b/tools/moderation-test.js index 426115c..8856864 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.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { privateKeyFromSeedWords } from 'nostr-tools/nip06'; @@ -114,9 +114,9 @@ try { await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const base = cacheKeyFor(mn + '\n'); const ME_SK = privateKeyFromSeedWords(mn); diff --git a/tools/negentropy-sync-test.js b/tools/negentropy-sync-test.js index e0257ca..c7adb84 100644 --- a/tools/negentropy-sync-test.js +++ b/tools/negentropy-sync-test.js @@ -8,7 +8,7 @@ // backlog, and is remembered as such for a day: the next ask there goes // straight out as a REQ. // Run: bun tools/negentropy-sync-test.js -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { privateKeyFromSeedWords } from 'nostr-tools/nip06'; import { buildHtml } from '../build.js'; @@ -48,9 +48,9 @@ try { await page.evaluate(() => localStorage.setItem('btc-wallet-network', 'regtest')); await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const ME = getPublicKey(privateKeyFromSeedWords(mn)); const MINE = finalizeEvent({ kind: 1, created_at: now - 600, tags: [], content: 'my post, replied to' }, FRIEND.sk); diff --git a/tools/notif-categories-test.js b/tools/notif-categories-test.js index f38dd01..8bd0da1 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.js'; +import puppeteer, { seedWords, skipSeedCheck } 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 }); @@ -19,7 +19,7 @@ try { await page.evaluate(() => localStorage.setItem('btc-wallet-network', 'regtest')); await page.reload(); await sleep(500); await click('create a new wallet'); await sleep(600); - await click('skip verification'); await sleep(300); + await skipSeedCheck(page); await sleep(300); await page.waitForFunction(() => document.body.innerText.toLowerCase().includes('receive'), { timeout: 20000 }); await page.evaluate(() => [...document.querySelectorAll('button')].find((e) => e.getAttribute('aria-label') === 'Settings').click()); await sleep(600); await page.evaluate(() => [...document.querySelectorAll('.settings-tile')].find((e) => /notification/i.test(e.textContent)).click()); await sleep(600); diff --git a/tools/notif-custom-emoji-test.js b/tools/notif-custom-emoji-test.js index 5847a0c..b1ff564 100644 --- a/tools/notif-custom-emoji-test.js +++ b/tools/notif-custom-emoji-test.js @@ -3,7 +3,7 @@ // :peepoClap: text, because the row kept the content and not the tag. // A row saved before that is healed when the relay hands the reaction again. // Run: bun tools/notif-custom-emoji-test.js -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { privateKeyFromSeedWords } from 'nostr-tools/nip06'; @@ -34,9 +34,9 @@ try { await page.evaluate(() => localStorage.setItem('btc-wallet-network', 'regtest')); await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const base = cacheKeyFor(mn + '\n'); const ME = getPublicKey(privateKeyFromSeedWords(mn)); diff --git a/tools/notif-faces-test.js b/tools/notif-faces-test.js index a1c1c8f..0aa7a92 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.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { privateKeyFromSeedWords } from 'nostr-tools/nip06'; @@ -65,9 +65,9 @@ try { await page.evaluate(() => localStorage.setItem('btc-wallet-network', 'regtest')); await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const base = cacheKeyFor(mn + '\n'); const ME = getPublicKey(privateKeyFromSeedWords(mn)); diff --git a/tools/notif-note-cache-test.js b/tools/notif-note-cache-test.js index e24c2e2..2b8be67 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.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { privateKeyFromSeedWords } from 'nostr-tools/nip06'; @@ -84,9 +84,9 @@ try { await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await clickText('create a new wallet'); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mnemonic = (await page.$$eval('.words .w .t', (nodes) => nodes.map((node) => node.textContent.trim()))).join(' '); - await clickText('skip verification'); + await skipSeedCheck(page); await waitText('receive'); const base = cacheKeyFor(mnemonic + '\n'); const me = getPublicKey(privateKeyFromSeedWords(mnemonic)); diff --git a/tools/notif-report-fold-test.js b/tools/notif-report-fold-test.js index 678aefd..6a906ec 100644 --- a/tools/notif-report-fold-test.js +++ b/tools/notif-report-fold-test.js @@ -4,7 +4,7 @@ // (Adam's phone, 2026-10-14: a reply Amethyst showed was missing here). A // person one follow flagged, or a stranger nobody did, shows as ever. // Run: bun tools/notif-report-fold-test.js -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { privateKeyFromSeedWords } from 'nostr-tools/nip06'; @@ -36,9 +36,9 @@ try { await page.evaluate(() => localStorage.setItem('btc-wallet-network', 'regtest')); await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const base = cacheKeyFor(mn + '\n'); const ME = getPublicKey(privateKeyFromSeedWords(mn)); diff --git a/tools/nwc-settings-ui-test.js b/tools/nwc-settings-ui-test.js index b88f9f7..ea52a6c 100644 --- a/tools/nwc-settings-ui-test.js +++ b/tools/nwc-settings-ui-test.js @@ -2,7 +2,7 @@ // budget that resets every day, week or month; the card says so, and // Revoke keeps it (greyed out of the list) answering for a while. // Run: bun tools/nwc-settings-ui-test.js (needs the regtest ark stack on :3535 for the card to show) -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { buildHtml } from '../build.js'; const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); let ok = true; @@ -47,8 +47,8 @@ try { await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); - await click('skip verification'); + await seedWords(page); + await skipSeedCheck(page); await waitText('receive', 20000); await page.evaluate(() => { const b = [...document.querySelectorAll('button')].find((e) => e.getAttribute('aria-label') === 'Settings'); b.click(); }); await sleep(600); await page.evaluate(() => { const b = [...document.querySelectorAll('.settings-tile')].find((e) => [...e.querySelectorAll('span')].some((x) => x.textContent.trim() === 'Nostr')); if (b) b.click(); }); await sleep(800); diff --git a/tools/onboard-cover-test.js b/tools/onboard-cover-test.js index 4262bc1..733a515 100644 --- a/tools/onboard-cover-test.js +++ b/tools/onboard-cover-test.js @@ -3,7 +3,7 @@ // twelve nature covers with a counter; the preview copies are what load. // Run: bun run build && bun tools/onboard-cover-test.js [screenshot.png] import assert from 'node:assert/strict'; -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); const server = Bun.serve({ port: 0, async fetch(req) { const p = new URL(req.url).pathname; @@ -21,7 +21,7 @@ try { await page.reload(); await sleep(1200); const click = (x) => page.evaluate((x) => { const e = [...document.querySelectorAll('button')].find((n) => n.textContent.trim().toLowerCase().includes(x)); e?.click(); return !!e; }, x); await click('create a new wallet'); await sleep(1500); - await click('skip verification'); await sleep(1500); + await skipSeedCheck(page); await sleep(1500); await page.evaluate(() => localStorage.setItem('btc-wallet-onb-step', 'avatar')); await page.reload(); await page.waitForSelector('.onb-cover', { timeout: 10000 }); diff --git a/tools/own-post-feeds-test.js b/tools/own-post-feeds-test.js index a3e8fc3..a877749 100644 --- a/tools/own-post-feeds-test.js +++ b/tools/own-post-feeds-test.js @@ -6,7 +6,7 @@ // whose tag it wears, which is now a t tag on the event) — not to a topic // feed that happens to be open and doesn't match. // Run: bun tools/own-post-feeds-test.js -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { privateKeyFromSeedWords } from 'nostr-tools/nip06'; import { generateSecretKey, getPublicKey } from 'nostr-tools/pure'; @@ -63,9 +63,9 @@ try { await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const base = cacheKeyFor(mn + '\n'); const ME = getPublicKey(privateKeyFromSeedWords(mn)); diff --git a/tools/pager-caption-test.js b/tools/pager-caption-test.js index 50ffe4f..a3d7800 100644 --- a/tools/pager-caption-test.js +++ b/tools/pager-caption-test.js @@ -4,7 +4,7 @@ // caption leaves the pager and opens the post, where the whole caption // reads. A link to the web opens in its own tab and the pager stays. // Run: bun tools/pager-caption-test.js -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { npubEncode } from 'nostr-tools/nip19'; @@ -87,9 +87,9 @@ try { await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const base = cacheKeyFor(mn + '\n'); await page.evaluate(([k, pk, valley, ns]) => { diff --git a/tools/pager-close-test.js b/tools/pager-close-test.js index a7c6c12..2f0bece 100644 --- a/tools/pager-close-test.js +++ b/tools/pager-close-test.js @@ -6,7 +6,7 @@ // the reader on that page, and that entry becomes it. And the ordinary case // still steps Back, so the phone's Back doesn't reopen the pager. // Run: bun tools/pager-close-test.js -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { buildHtml } from '../build.js'; @@ -96,9 +96,9 @@ try { await page.evaluate(() => localStorage.setItem('btc-wallet-network', 'regtest')); await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const base = cacheKeyFor(mn + '\n'); await page.evaluate(([k, pk, ns]) => { diff --git a/tools/pager-controls-test.js b/tools/pager-controls-test.js index a02591d..79e9bca 100644 --- a/tools/pager-controls-test.js +++ b/tools/pager-controls-test.js @@ -4,7 +4,7 @@ // the reaction button offers the emoji sheet, over the pager, as the feed's // does; the emoji picked shows on the button, and a tap again takes it back. // Run: bun tools/pager-controls-test.js -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { buildHtml } from '../build.js'; @@ -54,9 +54,9 @@ try { await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const base = cacheKeyFor(mn + '\n'); await page.evaluate(([k, pk, ns]) => { diff --git a/tools/pager-eager-test.js b/tools/pager-eager-test.js index 3d980fb..f1cd150 100644 --- a/tools/pager-eager-test.js +++ b/tools/pager-eager-test.js @@ -8,7 +8,7 @@ // Saver on, the pager is careful again: such a clip waits for a tap and // nothing loads ahead. // Run: bun tools/pager-eager-test.js -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { buildHtml } from '../build.js'; @@ -93,9 +93,9 @@ async function run(saveData, slow = false) { await page.evaluate(() => localStorage.setItem('btc-wallet-network', 'regtest')); await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const base = cacheKeyFor(mn + '\n'); await page.evaluate(([k, pk, ns]) => { diff --git a/tools/pager-first-frame-test.js b/tools/pager-first-frame-test.js index 0d477b7..63de5ac 100644 --- a/tools/pager-first-frame-test.js +++ b/tools/pager-first-frame-test.js @@ -5,7 +5,7 @@ // opening, and a clip beyond gets no picture (and stays out of the // carousel) until a first frame comes — never the post's still first. The slide left behind keeps its frame for the swipe // back. Run: bun tools/pager-first-frame-test.js -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { buildHtml } from '../build.js'; @@ -81,9 +81,9 @@ try { await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const base = cacheKeyFor(mn + '\n'); await page.evaluate(([k, pk, ns]) => { diff --git a/tools/pager-gone-picture-test.js b/tools/pager-gone-picture-test.js index 34c89b6..b7e3c8f 100644 --- a/tools/pager-gone-picture-test.js +++ b/tools/pager-gone-picture-test.js @@ -2,7 +2,7 @@ // nor the original can load) never joins the pager's carousel: a picture // joins once decoded (2026-10-17), and a step passes over it to the next. // (Its slide is marked gone, with the host's name, as before — out of sight.) Run: bun tools/pager-gone-picture-test.js -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { buildHtml } from '../build.js'; @@ -43,9 +43,9 @@ try { await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const base = cacheKeyFor(mn + '\n'); await page.evaluate(([k, pk, ns]) => { diff --git a/tools/pager-no-poster-test.js b/tools/pager-no-poster-test.js index 332eb53..c40f73e 100644 --- a/tools/pager-no-poster-test.js +++ b/tools/pager-no-poster-test.js @@ -7,7 +7,7 @@ // connection to be careful with (Data Saver), where a clip may never load, // the thumbnail poster stays. // Run: bun tools/pager-no-poster-test.js -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { buildHtml } from '../build.js'; @@ -77,9 +77,9 @@ try { await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const base = cacheKeyFor(mn + '\n'); await page.evaluate(([k, pk, ns]) => { diff --git a/tools/pager-prime-test.js b/tools/pager-prime-test.js index 4989790..df20cc7 100644 --- a/tools/pager-prime-test.js +++ b/tools/pager-prime-test.js @@ -15,7 +15,7 @@ // that); on a link slower than the clip, more — to the 4 MB cap. With Data // Saver on, the pager is careful again: such a clip waits for a tap and // nothing loads ahead. -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { buildHtml } from '../build.js'; @@ -97,9 +97,9 @@ async function run() { await page.evaluate(() => localStorage.setItem('btc-wallet-network', 'regtest')); await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const base = cacheKeyFor(mn + '\n'); await page.evaluate(([k, pk, ns]) => { diff --git a/tools/pager-ready-gate-test.js b/tools/pager-ready-gate-test.js index dd6982d..20cb337 100644 --- a/tools/pager-ready-gate-test.js +++ b/tools/pager-ready-gate-test.js @@ -5,7 +5,7 @@ // grid — shows the clips the screen's way round (tall ones on a phone held // upright), a square either way, until the toggle asks for all shapes. // Run: bun tools/pager-ready-gate-test.js -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { buildHtml } from '../build.js'; @@ -81,9 +81,9 @@ try { await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const base = cacheKeyFor(mn + '\n'); await page.evaluate(([k, pk, ns]) => { diff --git a/tools/pager-resize-test.js b/tools/pager-resize-test.js index 07fcbba..d18745b 100644 --- a/tools/pager-resize-test.js +++ b/tools/pager-resize-test.js @@ -5,7 +5,7 @@ // button on a clip 23 posts down showed him another post's picture. The // resize can come before the observer has even named the first slide. // Run: bun tools/pager-resize-test.js -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { buildHtml } from '../build.js'; @@ -68,9 +68,9 @@ try { await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const base = cacheKeyFor(mn + '\n'); await page.evaluate(([k, pk, ns]) => { diff --git a/tools/pager-rotate-test.js b/tools/pager-rotate-test.js index 9dcc7e3..4d453ad 100644 --- a/tools/pager-rotate-test.js +++ b/tools/pager-rotate-test.js @@ -4,7 +4,7 @@ // autoplay used to start it again, from the start, with sound if it had been // unmuted there), and not the slide a turn's half-way layout passes over. // Run: bun tools/pager-rotate-test.js -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { buildHtml } from '../build.js'; @@ -68,9 +68,9 @@ async function run(unmuteFirst) { await page.evaluate(() => localStorage.setItem('btc-wallet-network', 'regtest')); await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const base = cacheKeyFor(mn + '\n'); await page.evaluate(([k, pk, ns]) => { diff --git a/tools/pager-seek-hide-test.js b/tools/pager-seek-hide-test.js index 50863ba..59a0353 100644 --- a/tools/pager-seek-hide-test.js +++ b/tools/pager-seek-hide-test.js @@ -8,7 +8,7 @@ // position counter is the other way // round: it shows while the pager moves and fades out once it has settled. // Run: bun tools/pager-seek-hide-test.js -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { buildHtml } from '../build.js'; @@ -70,9 +70,9 @@ try { await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const base = cacheKeyFor(mn + '\n'); await page.evaluate(([k, pk, ns]) => { diff --git a/tools/pager-sharpen-test.js b/tools/pager-sharpen-test.js index ea5c878..e61b51d 100644 --- a/tools/pager-sharpen-test.js +++ b/tools/pager-sharpen-test.js @@ -3,7 +3,7 @@ // comes to rest; only the slide at rest carries the zoom layer. (Originals // decoded and uploaded as they passed made a flight stutter — Adam, // 2026-10-16.) Run: bun tools/pager-sharpen-test.js -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { buildHtml } from '../build.js'; @@ -42,9 +42,9 @@ try { await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const base = cacheKeyFor(mn + '\n'); await page.evaluate(([k, pk, ns]) => { diff --git a/tools/pager-swipe-fly-test.js b/tools/pager-swipe-fly-test.js index 657ea75..1184e7b 100644 --- a/tools/pager-swipe-fly-test.js +++ b/tools/pager-swipe-fly-test.js @@ -5,7 +5,7 @@ // mid-flight stops it on the post most on screen, and a swipe back from // there goes to the one before. // Run: bun tools/pager-swipe-fly-test.js -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { buildHtml } from '../build.js'; @@ -85,9 +85,9 @@ try { await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const base = cacheKeyFor(mn + '\n'); await page.evaluate(([k, pk, ns]) => { diff --git a/tools/pager-tail-test.js b/tools/pager-tail-test.js index 9d537e1..96227f8 100644 --- a/tools/pager-tail-test.js +++ b/tools/pager-tail-test.js @@ -4,7 +4,7 @@ // moved onto and plays by itself. With nothing older to be had, the end says // that's everything. Escape from there lands on the last clip's post. // Run: bun tools/pager-tail-test.js (SHOT=path.png saves the loading screen) -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { buildHtml } from '../build.js'; @@ -84,9 +84,9 @@ try { await page.evaluate(() => localStorage.setItem('btc-wallet-network', 'regtest')); await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const base = cacheKeyFor(mn + '\n'); await page.evaluate(([k, pk, ns]) => { diff --git a/tools/pager-tap-slop-test.js b/tools/pager-tap-slop-test.js index 4de87df..c184e3b 100644 --- a/tools/pager-tap-slop-test.js +++ b/tools/pager-tap-slop-test.js @@ -4,7 +4,7 @@ // 30px pauses (or plays) the clip and leaves the pager where it was; only a // longer one pages. // Run: bun tools/pager-tap-slop-test.js -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { buildHtml } from '../build.js'; @@ -66,9 +66,9 @@ try { await page.evaluate(() => localStorage.setItem('btc-wallet-network', 'regtest')); await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const base = cacheKeyFor(mn + '\n'); await page.evaluate(([k, pk, ns]) => { diff --git a/tools/pager-wheel-test.js b/tools/pager-wheel-test.js index 45a5115..7f59c34 100644 --- a/tools/pager-wheel-test.js +++ b/tools/pager-wheel-test.js @@ -5,7 +5,7 @@ // the wheel pans it while zoomed and pages again at 1×; and arrows at the // edges for the pointer, the sideways pair only where the post has more. // Run: bun tools/pager-wheel-test.js -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { buildHtml } from '../build.js'; @@ -68,9 +68,9 @@ try { await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const base = cacheKeyFor(mn + '\n'); await page.evaluate(([k, pk, ns]) => { diff --git a/tools/pic-proxy-test.js b/tools/pic-proxy-test.js index 881867c..69a09c1 100644 --- a/tools/pic-proxy-test.js +++ b/tools/pic-proxy-test.js @@ -5,7 +5,7 @@ // paints the original as before. The server is stood in for: its answers are // canned. // Run: bun tools/pic-proxy-test.js -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { buildHtml } from '../build.js'; @@ -76,9 +76,9 @@ try { await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const base = cacheKeyFor(mn + '\n'); await page.evaluate(([k, pk, ns]) => { diff --git a/tools/picture-pager-ready-test.js b/tools/picture-pager-ready-test.js index 7cfaa45..08a4e21 100644 --- a/tools/picture-pager-ready-test.js +++ b/tools/picture-pager-ready-test.js @@ -5,7 +5,7 @@ // the screen stay out, with the "Tall only" / "All shapes" toggle in the // top bar; and with fewer than ten ready and more still warming, no // counter shows and a flick goes one post. Run: bun tools/picture-pager-ready-test.js -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { buildHtml } from '../build.js'; @@ -50,9 +50,9 @@ try { await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const base = cacheKeyFor(mn + '\n'); await page.evaluate(([k, pk, ns]) => { diff --git a/tools/poll-test.js b/tools/poll-test.js index edcb061..41aa02e 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.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { SimplePool } from 'nostr-tools/pool'; import { cacheKeyFor } from '../src/wallet.js'; import { neventOf } from '../src/nostr.js'; @@ -41,9 +41,9 @@ try { await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const base = cacheKeyFor(mn + '\n'); const Q = 'a'.repeat(63) + '9'; diff --git a/tools/post-edit-test.js b/tools/post-edit-test.js index 2ed1260..08d1190 100644 --- a/tools/post-edit-test.js +++ b/tools/post-edit-test.js @@ -5,7 +5,7 @@ // deletion is remembered across a reload. // No relay is written for real — the WebSocket shim swallows EVENT. // Run: bun tools/post-edit-test.js -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { privateKeyFromSeedWords } from 'nostr-tools/nip06'; @@ -95,9 +95,9 @@ try { await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const base = cacheKeyFor(mn + '\n'); const ME_SK = privateKeyFromSeedWords(mn); diff --git a/tools/profile-pager-test.js b/tools/profile-pager-test.js index 2a78598..5593192 100644 --- a/tools/profile-pager-test.js +++ b/tools/profile-pager-test.js @@ -3,7 +3,7 @@ // else's — and closing it after moving on lands the profile on that post. // (It opened alone, in the lightbox, before: no pager on a profile.) // Run: bun tools/profile-pager-test.js -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { buildHtml } from '../build.js'; @@ -67,9 +67,9 @@ try { await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const base = cacheKeyFor(mn + '\n'); await page.evaluate(([k, pk, ns, other]) => { diff --git a/tools/profile-repost-test.js b/tools/profile-repost-test.js index 7825efe..5ee97e7 100644 --- a/tools/profile-repost-test.js +++ b/tools/profile-repost-test.js @@ -4,7 +4,7 @@ // boost, a video event still has its clip — they were all saved as kind 1 // notes, so a boost came back as "reply · {"content":…}". // Run: bun tools/profile-repost-test.js -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { npubEncode } from 'nostr-tools/nip19'; import { buildHtml } from '../build.js'; @@ -57,8 +57,8 @@ try { await page.evaluate(() => localStorage.setItem('btc-wallet-network', 'regtest')); await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); - await click('skip verification'); + await seedWords(page); + await skipSeedCheck(page); await waitText('receive', 20000); await page.goto(server.url.href + npubEncode(ME.pk), { waitUntil: 'domcontentloaded' }); await look('from the relays'); diff --git a/tools/puppeteer.js b/tools/puppeteer.js index 2609981..8cefd6e 100644 --- a/tools/puppeteer.js +++ b/tools/puppeteer.js @@ -126,3 +126,20 @@ export default new Proxy(real, { return typeof v === 'function' ? v.bind(t) : v; }, }); + +// The create flow (2026-11-07): a heads-up, then the words, then the +// passphrase page, then a one-word check. seedWords() gets a test from +// "Create a new wallet" to the words on screen; skipSeedCheck() from there +// (or from the heads-up) into the wallet, no passphrase, check skipped. +export async function seedWords(page) { + await page.waitForSelector('#seed-intro-next, .words .w .t', { timeout: 30000 }); + if (await page.$('#seed-intro-next')) await page.evaluate(() => document.getElementById('seed-intro-next').click()); + await page.waitForSelector('.words .w .t'); +} +export async function skipSeedCheck(page) { + if (await page.$('#seed-intro-next')) await seedWords(page); + for (const id of ['seed-next', 'pass-next', 'seed-skip']) { + await page.waitForSelector('#' + id, { timeout: 10000 }); + await page.evaluate((i) => document.getElementById(i).click(), id); + } +} diff --git a/tools/quote-pager-test.js b/tools/quote-pager-test.js index 7f8b76c..d125edd 100644 --- a/tools/quote-pager-test.js +++ b/tools/quote-pager-test.js @@ -6,7 +6,7 @@ // quoted picture's details name its author, and closing lands on the post // that quoted it. // Run: bun tools/quote-pager-test.js -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { noteEncode } from 'nostr-tools/nip19'; @@ -80,9 +80,9 @@ try { await page.evaluate(() => localStorage.setItem('btc-wallet-network', 'regtest')); await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const base = cacheKeyFor(mn + '\n'); await page.evaluate(([k, pa, pq]) => { diff --git a/tools/quoted-clip-fullscreen-test.js b/tools/quoted-clip-fullscreen-test.js index 41984a2..ce235f6 100644 --- a/tools/quoted-clip-fullscreen-test.js +++ b/tools/quoted-clip-fullscreen-test.js @@ -5,7 +5,7 @@ // handler took the clip back out of full screen for a pager that then // wouldn't open. // Run: bun tools/quoted-clip-fullscreen-test.js -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { buildHtml } from '../build.js'; @@ -47,9 +47,9 @@ try { await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const base = cacheKeyFor(mn + '\n'); await page.evaluate(([k, alice, bob, roots, replies]) => { diff --git a/tools/relay-budget-test.js b/tools/relay-budget-test.js index bb1dd11..d9798a3 100644 --- a/tools/relay-budget-test.js +++ b/tools/relay-budget-test.js @@ -9,7 +9,7 @@ // unchanged live subscriptions stay up, the reports sync honours the saved // clock, each relay's REQ closes on its own EOSE, and no relay carries more // than ten one-shot REQs at once. Run: bun tools/relay-budget-test.js -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { neventEncode } from 'nostr-tools/nip19'; @@ -101,9 +101,9 @@ try { await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const base = cacheKeyFor(mn + '\n'); await page.evaluate(([k, pk, ns]) => { diff --git a/tools/relays-settings-test.js b/tools/relays-settings-test.js index 9e18f59..771e3cd 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.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { privateKeyFromSeedWords } from 'nostr-tools/nip06'; import { buildHtml } from '../build.js'; @@ -71,9 +71,9 @@ try { await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const ME_SK = privateKeyFromSeedWords(mn); const ME = getPublicKey(ME_SK); diff --git a/tools/resume-wake-test.js b/tools/resume-wake-test.js index 3b992fb..c142dfb 100644 --- a/tools/resume-wake-test.js +++ b/tools/resume-wake-test.js @@ -7,7 +7,7 @@ // 2026-11-01). On resume every open socket is now asked one tiny REQ; a // silent one is closed and redialed at once, its subscriptions refired, and // the feed refresh runs on the live sockets. Run: bun tools/resume-wake-test.js -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { buildHtml } from '../build.js'; @@ -71,9 +71,9 @@ try { await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const base = cacheKeyFor(mn + '\n'); await page.evaluate(([k, pk, ns]) => { diff --git a/tools/room-faces-test.js b/tools/room-faces-test.js index cc4c6fe..ecbcc14 100644 --- a/tools/room-faces-test.js +++ b/tools/room-faces-test.js @@ -4,7 +4,7 @@ // profiles cached — the worst case — against the real coinos community. // // Run: bun tools/room-faces-test.js [--no-hover] -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { buildHtml } from '../build.js'; const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); let ok = true; @@ -22,8 +22,8 @@ try { await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); - await click('skip verification'); + await seedWords(page); + await skipSeedCheck(page); await waitText('receive', 20000); await page.evaluate(() => [...document.querySelectorAll('.app-bottom-nav .app-nav-button')][1].click()); await waitText('members', 20000); diff --git a/tools/scheduled-post-test.js b/tools/scheduled-post-test.js index 546537c..e4a61f1 100644 --- a/tools/scheduled-post-test.js +++ b/tools/scheduled-post-test.js @@ -4,7 +4,7 @@ // post waits in a "Scheduled" list, where Cancel sends a kind 5 for it to // /schedule/cancel. The notifier is stubbed. // Run: bun tools/scheduled-post-test.js -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { verifyEvent } from 'nostr-tools/pure'; import { buildHtml } from '../build.js'; const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); @@ -47,8 +47,8 @@ try { await page.evaluate(() => localStorage.setItem('btc-wallet-network', 'regtest')); await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); - await click('skip verification'); + await seedWords(page); + await skipSeedCheck(page); await waitText('receive', 20000); await page.evaluate(() => document.querySelector('.app-nav-button[aria-label="Feed"]')?.click()); diff --git a/tools/sensitive-feed-test.js b/tools/sensitive-feed-test.js index a050269..626da2c 100644 --- a/tools/sensitive-feed-test.js +++ b/tools/sensitive-feed-test.js @@ -4,7 +4,7 @@ // accounts whose own profile says they post adult work. Elsewhere a marked // post is folded behind its warning with a Show. // Run: bun tools/sensitive-feed-test.js -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { finalizeEvent, generateSecretKey, getPublicKey } from 'nostr-tools/pure'; import { buildHtml, buildVerifyWorker } from '../build.js'; @@ -74,9 +74,9 @@ try { await sleep(400); 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); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await page.waitForFunction(() => /receive/i.test(document.body.innerText), { timeout: 20000 }); await sleep(3000); // the starter follows land first const base = cacheKeyFor(mn + '\n'); diff --git a/tools/signer-resume-test.js b/tools/signer-resume-test.js index c35b655..9ca3950 100644 --- a/tools/signer-resume-test.js +++ b/tools/signer-resume-test.js @@ -6,7 +6,7 @@ // keeps trying the stored session and closes itself once the signer answers. // The remote signer is played by this script over a shimmed relay. // Run: bun tools/signer-resume-test.js -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import * as nip44 from 'nostr-tools/nip44'; @@ -100,9 +100,9 @@ try { await page.evaluate(() => localStorage.setItem('btc-wallet-network', 'regtest')); await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const base = cacheKeyFor(mn + '\n'); const local = Array.from(CLIENT_SK, (b) => b.toString(16).padStart(2, '0')).join(''); diff --git a/tools/slow-image-test.js b/tools/slow-image-test.js index 103f6da..43eff97 100644 --- a/tools/slow-image-test.js +++ b/tools/slow-image-test.js @@ -4,7 +4,7 @@ // inside a fixed box (nothing moves when it lands); a picture that refused to // load is still a link. // Run: bun tools/slow-image-test.js -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { buildHtml } from '../build.js'; @@ -46,9 +46,9 @@ try { await page.evaluate(() => localStorage.setItem('btc-wallet-network', 'regtest')); await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const base = cacheKeyFor(mn + '\n'); await page.evaluate(([k, pk, ns]) => { diff --git a/tools/standalone-file-test.js b/tools/standalone-file-test.js index 743bc7d..13445f6 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.js'; +import puppeteer, { seedWords, skipSeedCheck } 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 { @@ -25,7 +25,7 @@ try { await page.evaluate(() => { const s = [...document.querySelectorAll('select')].find((x) => [...x.options].some((o) => o.value === 'en')); s.value = 'en'; s.dispatchEvent(new Event('change', { bubbles: true })); }); await sleep(500); await click('create a new wallet'); await sleep(1500); - await click('skip verification'); await sleep(1500); + await skipSeedCheck(page); await sleep(1500); // the avatar step comes after a name claim; resume the wizard there await page.evaluate(() => localStorage.setItem('btc-wallet-onb-step', 'avatar')); await page.reload(); await sleep(2500); diff --git a/tools/thread-instant-test.js b/tools/thread-instant-test.js index bd80408..e9a9138 100644 --- a/tools/thread-instant-test.js +++ b/tools/thread-instant-test.js @@ -3,7 +3,7 @@ // is seeded from them — the relays only add to it. A thread with nothing in // hand paints its replies as they arrive, not after the slowest relay. // Run: bun tools/thread-instant-test.js -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { buildHtml } from '../build.js'; @@ -95,9 +95,9 @@ try { await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const base = cacheKeyFor(mn + '\n'); await page.evaluate(([k, alice, bob, carol, notes]) => { diff --git a/tools/translate-phone-lang-test.js b/tools/translate-phone-lang-test.js index 9e1eefc..28676eb 100644 --- a/tools/translate-phone-lang-test.js +++ b/tools/translate-phone-lang-test.js @@ -6,7 +6,7 @@ // the target and takes a DeepL key, which then rides with each request. // The registrar's /translate is stubbed (window.fetch shim). // Run: bun tools/translate-phone-lang-test.js -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { buildHtml } from '../build.js'; @@ -57,9 +57,9 @@ try { await page.evaluate(() => { localStorage.setItem('btc-wallet-network', 'regtest'); localStorage.setItem('btc-wallet-lang', 'en'); }); await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const base = cacheKeyFor(mn + '\n'); await page.evaluate(([k, pk, ns]) => { diff --git a/tools/translate-test.js b/tools/translate-test.js index d3fe095..f00ba22 100644 --- a/tools/translate-test.js +++ b/tools/translate-test.js @@ -3,7 +3,7 @@ // with the switch off they offer Translate instead; ⋯ → Translate asks for // any post. The registrar's /translate is stubbed here (window.fetch shim). // Run: bun tools/translate-test.js -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { buildHtml } from '../build.js'; @@ -70,9 +70,9 @@ try { await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const base = cacheKeyFor(mn + '\n'); await page.evaluate(([k, pk, ns]) => { diff --git a/tools/video-autoplay-test.js b/tools/video-autoplay-test.js index 6b943f2..6d245b3 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.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { buildHtml } from '../build.js'; @@ -53,9 +53,9 @@ try { await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const base = cacheKeyFor(mn + '\n'); await page.evaluate(([k, pk, ns]) => { diff --git a/tools/video-compress-test.js b/tools/video-compress-test.js index 19735c8..2b83edc 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.js'; +import puppeteer, { seedWords, skipSeedCheck } 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; }; @@ -73,8 +73,8 @@ try { await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(600); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); - await click('skip verification'); + await seedWords(page); + await skipSeedCheck(page); await waitText('receive', 20000); check('the browser can encode H.264 (WebCodecs)', await page.evaluate(async () => (await VideoEncoder.isConfigSupported({ codec: 'avc1.42001f', width: 1280, height: 720, bitrate: 2.5e6 })).supported)); // the post composer: the pencil on the feed diff --git a/tools/video-events-test.js b/tools/video-events-test.js index c024949..56ea486 100644 --- a/tools/video-events-test.js +++ b/tools/video-events-test.js @@ -6,7 +6,7 @@ // version. A note linking such a file, typed video/* in its imeta, plays it // too. And the clips are in the pager. // Run: bun tools/video-events-test.js -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { buildHtml } from '../build.js'; @@ -90,9 +90,9 @@ try { await page.evaluate(() => localStorage.setItem('btc-wallet-network', 'regtest')); await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const base = cacheKeyFor(mn + '\n'); await page.evaluate(([k, pk]) => { diff --git a/tools/video-fallback-test.js b/tools/video-fallback-test.js index 93726e1..ff45846 100644 --- a/tools/video-fallback-test.js +++ b/tools/video-fallback-test.js @@ -4,7 +4,7 @@ // post gave no poster wears the server's still. The server (vmedia) is // stood in for here: its answers are canned. // Run: bun tools/video-fallback-test.js -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { buildHtml } from '../build.js'; @@ -81,9 +81,9 @@ try { await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const base = cacheKeyFor(mn + '\n'); await page.evaluate(([k, pk, ns]) => { diff --git a/tools/video-gone-test.js b/tools/video-gone-test.js index 95826b5..48f2956 100644 --- a/tools/video-gone-test.js +++ b/tools/video-gone-test.js @@ -6,7 +6,7 @@ // file's link, its controls and cover gone; the next paint of the post says // it at once, with no request. A clip on a live host plays as before. // Run: bun tools/video-gone-test.js -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { buildHtml } from '../build.js'; @@ -73,9 +73,9 @@ try { await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const base = cacheKeyFor(mn + '\n'); await page.evaluate(([k, pk, ns]) => { diff --git a/tools/video-hls-play-test.js b/tools/video-hls-play-test.js index f1aea1c..4a18e76 100644 --- a/tools/video-hls-play-test.js +++ b/tools/video-hls-play-test.js @@ -4,7 +4,7 @@ // and a post linking an .m3u8 stream plays that. vmedia runs here for real, // on a local copy of the clip. // Run: bun tools/video-hls-play-test.js -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { buildHtml } from '../build.js'; @@ -90,9 +90,9 @@ try { await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const base = cacheKeyFor(mn + '\n'); await page.evaluate(([k, pk, ns]) => { diff --git a/tools/video-tap-test.js b/tools/video-tap-test.js index 50e5a69..f03aef6 100644 --- a/tools/video-tap-test.js +++ b/tools/video-tap-test.js @@ -3,7 +3,7 @@ // rest). With a mouse the middle of the clip is a big play/pause button that // never goes full screen. // Run: bun tools/video-tap-test.js -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { buildHtml } from '../build.js'; @@ -49,9 +49,9 @@ try { await page.evaluate(() => localStorage.setItem('btc-wallet-network', 'regtest')); await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const base = cacheKeyFor(mn + '\n'); await page.evaluate(([k, pk, ns]) => { diff --git a/tools/video-tile-poster-test.js b/tools/video-tile-poster-test.js index 48385fe..ed672c2 100644 --- a/tools/video-tile-poster-test.js +++ b/tools/video-tile-poster-test.js @@ -4,7 +4,7 @@ // picture for falls back to the frame grab, and one the browser can't load // either (deleted, its 404 without CORS headers) leaves the grid. // Run: bun tools/video-tile-poster-test.js -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { buildHtml } from '../build.js'; @@ -75,9 +75,9 @@ try { await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const base = cacheKeyFor(mn + '\n'); await page.evaluate(([k, pk, ns]) => { diff --git a/tools/video-touch-tap-test.js b/tools/video-touch-tap-test.js index 71ffe9c..ff25095 100644 --- a/tools/video-touch-tap-test.js +++ b/tools/video-touch-tap-test.js @@ -5,7 +5,7 @@ // on the control bar or the paused clip's play button stays the player's, a // drag is a scroll, and where the click does come it isn't taken twice. // Run: bun tools/video-touch-tap-test.js -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { buildHtml } from '../build.js'; @@ -51,9 +51,9 @@ try { await page.evaluate(() => localStorage.setItem('btc-wallet-network', 'regtest')); await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const base = cacheKeyFor(mn + '\n'); await page.evaluate(([k, pk, ns]) => { diff --git a/tools/web-comment-test.js b/tools/web-comment-test.js index 7a6258f..513217b 100644 --- a/tools/web-comment-test.js +++ b/tools/web-comment-test.js @@ -9,7 +9,7 @@ // tag beside it, counted as a report of the PERSON (es stayed 0 for an // 'other' e tag) — two such reports from follows folded the author's every // post. Run: bun tools/web-comment-test.js -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { buildHtml } from '../build.js'; @@ -70,9 +70,9 @@ try { await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const base = cacheKeyFor(mn + '\n'); await page.evaluate(([k, pks, ns]) => { diff --git a/tools/wn-only-device-test.js b/tools/wn-only-device-test.js index a91262b..7b19876 100644 --- a/tools/wn-only-device-test.js +++ b/tools/wn-only-device-test.js @@ -6,7 +6,7 @@ // a fresh one of ours. A device whose key was retired by another sees the // deletion and stops putting a key back; the card says so there. // Run: bun tools/wn-only-device-test.js -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { privateKeyFromSeedWords } from 'nostr-tools/nip06'; @@ -76,9 +76,9 @@ try { await page.evaluate(() => localStorage.setItem('btc-wallet-network', 'regtest')); await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const base = cacheKeyFor(mn + '\n'); const SK = privateKeyFromSeedWords(mn), ME = getPublicKey(SK); diff --git a/tools/youtube-tap-test.js b/tools/youtube-tap-test.js index 271277f..e08058b 100644 --- a/tools/youtube-tap-test.js +++ b/tools/youtube-tap-test.js @@ -4,7 +4,7 @@ // over the picture takes them; YouTube's control bar along the bottom is // left to YouTube, and full screen the layer steps aside. // Run: bun tools/youtube-tap-test.js -import puppeteer from './puppeteer.js'; +import puppeteer, { seedWords, skipSeedCheck } from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; import { buildHtml } from '../build.js'; @@ -60,9 +60,9 @@ async function run(saveData) { await page.evaluate(() => localStorage.setItem('btc-wallet-network', 'regtest')); await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); await click('create a new wallet'); await sleep(600); - await page.waitForSelector('.words .w .t'); + await seedWords(page); const mn = (await page.$$eval('.words .w .t', (l) => l.map((e) => e.textContent.trim()))).join(' '); - await click('skip verification'); + await skipSeedCheck(page); await waitText('receive', 20000); const base = cacheKeyFor(mn + '\n'); await page.evaluate(([k, pk, ns]) => { |
