diff options
Diffstat (limited to 'tools')
| -rw-r--r-- | tools/avatar-thumb-test.js | 2 | ||||
| -rw-r--r-- | tools/boot-balance-test.js | 2 | ||||
| -rw-r--r-- | tools/boot-signin-flash-test.js | 6 | ||||
| -rw-r--r-- | tools/chat-layout-test.js | 2 | ||||
| -rw-r--r-- | tools/feed-follow-test.js | 2 | ||||
| -rw-r--r-- | tools/lock-wallet-test.js | 8 | ||||
| -rw-r--r-- | tools/minimal-smoke.js | 2 | ||||
| -rw-r--r-- | tools/onboard-backup-test.js | 121 | ||||
| -rw-r--r-- | tools/padlock-ask-test.js | 2 | ||||
| -rw-r--r-- | tools/padlock-flow-test.js | 4 | ||||
| -rw-r--r-- | tools/post-actions-test.js | 2 | ||||
| -rw-r--r-- | tools/profile-batch-test.js | 2 | ||||
| -rw-r--r-- | tools/profile-draft-test.js | 2 | ||||
| -rw-r--r-- | tools/profile-feed-test.js | 2 | ||||
| -rw-r--r-- | tools/profile-open-test.js | 2 | ||||
| -rw-r--r-- | tools/push-unavailable-test.js | 2 | ||||
| -rw-r--r-- | tools/quote-card-test.js | 2 | ||||
| -rw-r--r-- | tools/ui-morph-test.js | 4 | ||||
| -rw-r--r-- | tools/unifiedpush-test.js | 2 | ||||
| -rw-r--r-- | tools/youtube-embed-test.js | 2 | ||||
| -rw-r--r-- | tools/zap-tally-cache-test.js | 2 |
21 files changed, 148 insertions, 27 deletions
diff --git a/tools/avatar-thumb-test.js b/tools/avatar-thumb-test.js index 727c49a..c776179 100644 --- a/tools/avatar-thumb-test.js +++ b/tools/avatar-thumb-test.js @@ -73,7 +73,7 @@ try { await page.evaluate(() => localStorage.setItem('btc-wallet-network', 'regtest')); await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); - await click('get started'); await sleep(500); + await click('create a new wallet'); await sleep(500); await click('import existing'); await sleep(400); await page.waitForSelector('textarea'); const mnemonic = generateMnemonic(wordlist); diff --git a/tools/boot-balance-test.js b/tools/boot-balance-test.js index 96f47a9..c34712b 100644 --- a/tools/boot-balance-test.js +++ b/tools/boot-balance-test.js @@ -28,7 +28,7 @@ try { await page.evaluate(() => localStorage.setItem('btc-wallet-network', 'regtest')); await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); - await click('get started'); await sleep(500); + await click('create a new wallet'); await sleep(500); await click('import existing'); await sleep(400); await page.waitForSelector('textarea'); await page.type('textarea', generateMnemonic(wordlist)); diff --git a/tools/boot-signin-flash-test.js b/tools/boot-signin-flash-test.js index e46ee72..b29bc63 100644 --- a/tools/boot-signin-flash-test.js +++ b/tools/boot-signin-flash-test.js @@ -1,7 +1,7 @@ // A signed-in wallet must never flash the signed-out welcome screen. Anything // that renders before boot has restored the session (a deferred feature chunk // landing, an emitter tick) is rendering without knowing whether anyone is -// signed in, and "Get started" over a wallet that's about to open is the one +// signed in, and the start page over a wallet that's about to open is the one // wrong guess that's unmistakable. // // The race is widened here (the locale fetch is delayed, so the deferred @@ -27,7 +27,7 @@ try { await page.evaluate(() => localStorage.setItem('btc-wallet-network', 'regtest')); await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); - await click('get started'); await sleep(500); + await click('create a new wallet'); await sleep(500); await click('import existing'); await sleep(400); await page.waitForSelector('textarea'); await page.type('textarea', generateMnemonic(wordlist)); @@ -48,7 +48,7 @@ try { window.__sawSignin = null; const tick = () => { const txt = (document.body && document.body.innerText) || ''; - if (!window.__sawSignin && /get started|sign in with/i.test(txt)) window.__sawSignin = Math.round(performance.now()); + if (!window.__sawSignin && /create a new wallet|sign in with/i.test(txt)) window.__sawSignin = Math.round(performance.now()); if (!window.__walletAt && document.querySelector('.balance')) window.__walletAt = Math.round(performance.now()); if (!window.__walletAt) requestAnimationFrame(tick); }; diff --git a/tools/chat-layout-test.js b/tools/chat-layout-test.js index 853aafa..772e38f 100644 --- a/tools/chat-layout-test.js +++ b/tools/chat-layout-test.js @@ -43,7 +43,7 @@ try { await page.evaluate(() => localStorage.setItem('btc-wallet-network', 'regtest')); await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); - await click('button', 'Get started'); + await click('button', 'Create a new wallet'); await sleep(300); await click('button', 'Import existing'); await sleep(300); diff --git a/tools/feed-follow-test.js b/tools/feed-follow-test.js index 4c1fed3..ae486a6 100644 --- a/tools/feed-follow-test.js +++ b/tools/feed-follow-test.js @@ -31,7 +31,7 @@ try { await page.evaluate(() => localStorage.setItem('btc-wallet-network', 'regtest')); await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); - await click('get started'); await sleep(500); + await click('create a new wallet'); await sleep(500); await click('import existing'); await sleep(400); await page.waitForSelector('textarea'); const mnemonic = generateMnemonic(wordlist); diff --git a/tools/lock-wallet-test.js b/tools/lock-wallet-test.js index 8db392a..494195e 100644 --- a/tools/lock-wallet-test.js +++ b/tools/lock-wallet-test.js @@ -44,7 +44,7 @@ try { await page.evaluate(() => localStorage.setItem('btc-wallet-network', 'regtest')); await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); - await click('button', 'Get started'); + await click('button', 'Create a new wallet'); await sleep(500); await click('button', 'Import existing'); await sleep(400); @@ -76,7 +76,7 @@ try { await logout(); txt = await body(); check('it does not ask a second time', !/protect this device/i.test(txt), txt.slice(0, 120).replace(/\n+/g, ' | ')); - check('logout means the front door', /create new|get started|sign in/i.test(txt), txt.slice(0, 120).replace(/\n+/g, ' | ')); + check('logout means the front door', /create a new wallet|sign in/i.test(txt), txt.slice(0, 120).replace(/\n+/g, ' | ')); await page.reload({ waitUntil: 'domcontentloaded' }); check('and a saved passwordless wallet is one reload away', await waitText('receive')); await sleep(1000); @@ -109,7 +109,7 @@ try { check('back in the wallet', await waitText('receive')); await logout(); txt = await body(); - check('logout is still the front door', /create new|get started|sign in/i.test(txt), txt.slice(0, 120).replace(/\n+/g, ' | ')); + check('logout is still the front door', /create a new wallet|sign in/i.test(txt), txt.slice(0, 120).replace(/\n+/g, ' | ')); check('the wallet is still saved', (await vaultSize()) > 0); await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(800); @@ -148,7 +148,7 @@ try { await sleep(1200); txt = await body(); check('everything is gone', (await vaultSize()) === 0); - check('back at the start page', /get started/i.test(txt), txt.slice(0, 120).replace(/\n+/g, ' | ')); + check('back at the start page', /create a new wallet/i.test(txt), txt.slice(0, 120).replace(/\n+/g, ' | ')); } catch (e) { check('run completed', false, e.message); } finally { diff --git a/tools/minimal-smoke.js b/tools/minimal-smoke.js index f3f896c..2bdc4a6 100644 --- a/tools/minimal-smoke.js +++ b/tools/minimal-smoke.js @@ -65,7 +65,7 @@ try { await sleep(400); console.log('\n[1] import + core wallet'); - await clickText('button', 'Get started'); // the front door gained a step + await clickText('button', 'Create a new wallet'); // the front door gained a step await sleep(300); await clickText('button', 'Import existing'); await sleep(200); diff --git a/tools/onboard-backup-test.js b/tools/onboard-backup-test.js new file mode 100644 index 0000000..3b242dd --- /dev/null +++ b/tools/onboard-backup-test.js @@ -0,0 +1,121 @@ +// Two things about the front door. +// +// One: making your own keys has to look like the way in. Four equally +// weighted buttons, where only the sign-in ones said what they did, sent +// people straight past the seed phrase — which IS the wallet — into a login +// that is only a convenience wrapped around one. +// +// Two: a wallet that arrived by SIGN-IN has a recovery phrase its owner has +// never seen, and nobody can reissue it. So it gets shown once, during +// setup, and if they put it off the wallet keeps one quiet line about it. +// +// Run: bun tools/onboard-backup-test.js +import puppeteer from 'puppeteer-core'; +import { bech32 } from '@scure/base'; +import { generateSecretKey } from 'nostr-tools/pure'; +import { seedFromNostrKey } from '../src/nostr-login.js'; +import { buildHtml } from '../build.js'; +const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); +let ok = true; +const check = (n, c, d = '') => { console.log(` ${c ? '✓' : '✗'} ${n}${d ? ' — ' + d : ''}`); if (!c) ok = false; }; + +const html = await buildHtml({ minify: true, pwa: false }); +const server = Bun.serve({ port: 5291, fetch: () => new Response(html, { headers: { 'content-type': 'text/html' } }) }); +const browser = await puppeteer.launch({ executablePath: '/usr/bin/google-chrome', headless: 'new', args: ['--no-sandbox'] }); + +const click = (page, t, sel = 'button') => page.evaluate((s, x) => { + const e = [...document.querySelectorAll(s)].find((n) => n.textContent.trim().toLowerCase().includes(x)); + if (e) { e.click(); return true; } return false; +}, sel, t.toLowerCase()); +const text = (page) => page.evaluate(() => document.body.innerText); +const waitText = async (page, x, ms = 30000) => { + for (let i = 0; i < ms / 250; i++) { if ((await text(page)).toLowerCase().includes(x.toLowerCase())) return true; await sleep(250); } + return false; +}; + +// Sign in with a pasted nsec and stop at whatever the wizard shows next. +// Its own browser context, so each run meets a genuinely fresh front door +// rather than the wallet the previous one left in localStorage. +async function signIn() { + const ctx = await browser.createBrowserContext(); + const page = await ctx.newPage(); + await page.setViewport({ width: 390, height: 844 }); + await page.goto('http://localhost:5291/', { waitUntil: 'domcontentloaded' }); + await page.evaluate(() => localStorage.setItem('btc-wallet-network', 'regtest')); + await page.reload({ waitUntil: 'domcontentloaded' }); + // wait for the front door to actually be painted before poking it + await waitText(page, 'sign in with nostr'); + const sk = generateSecretKey(); + const nsec = bech32.encode('nsec', bech32.toWords(sk), 1000); + await click(page, 'sign in with nostr'); + await waitText(page, 'log in with nostr'); + await page.waitForSelector('input[placeholder*="nsec" i], textarea[placeholder*="nsec" i]', { timeout: 15000 }); + await page.type('input[placeholder*="nsec" i], textarea[placeholder*="nsec" i]', nsec); + await click(page, 'log in'); + return { page, ctx, seed: seedFromNostrKey(sk) }; +} + +try { + console.log('[the start page]'); + { + const page = await browser.newPage(); + await page.setViewport({ width: 390, height: 844 }); + await page.goto('http://localhost:5291/', { waitUntil: 'domcontentloaded' }); + await sleep(1500); + const front = await page.evaluate(() => { + const btns = [...document.querySelectorAll('button')].filter((b) => b.offsetParent); + const primary = document.getElementById('onb-create'); + const idx = btns.indexOf(primary); + const signins = btns.filter((b) => /sign in with/i.test(b.textContent)); + return { + label: primary ? primary.textContent.trim() : null, + first: idx === 0 || (idx >= 0 && idx < btns.findIndex((b) => /sign in with/i.test(b.textContent))), + primaryTop: primary ? primary.getBoundingClientRect().top : -1, + signinTop: signins.length ? signins[0].getBoundingClientRect().top : -1, + divider: !!document.querySelector('.onb-or'), + body: (document.querySelector('.onb p') || {}).textContent || '', + }; + }); + check('the way in says what it makes', /wallet/i.test(front.label || ''), front.label || 'missing'); + check('...and comes before the sign-in buttons', front.first && front.primaryTop < front.signinTop, + `create at ${Math.round(front.primaryTop)}, first sign-in at ${Math.round(front.signinTop)}`); + check('...with a line between them, so they read as the alternative', front.divider); + check('the page says what the wallet actually is', /twelve words|seed|phrase/i.test(front.body), + front.body.slice(0, 60) + '…'); + await page.close(); + } + + console.log('\n[signing in, then writing it down]'); + { + const { page, ctx, seed } = await signIn(); + check('a sign-in is asked to write the phrase down', await waitText(page, 'write down your recovery phrase')); + const shown = await page.evaluate(() => [...document.querySelectorAll('.words .w .t')].map((e) => e.textContent.trim())); + check('...and the real twelve words are on screen', shown.length === 12 && shown.join(' ') === seed, + shown.length + ' words' + (shown.join(' ') === seed ? ', matching the derived seed' : ', WRONG')); + await click(page, "i've written it down"); + check('confirming lands in the wallet', await waitText(page, 'receive')); + const nag = await text(page); + check('...and there is no reminder afterwards', !/still unwritten/i.test(nag)); + await page.reload({ waitUntil: 'domcontentloaded' }); + check('...not after a reload either', await waitText(page, 'receive') && !/still unwritten/i.test(await text(page))); + await ctx.close(); + } + + console.log('\n[signing in, then putting it off]'); + { + const { page, ctx } = await signIn(); + check('the ask appears', await waitText(page, 'write down your recovery phrase')); + await click(page, 'later'); + check('later lands in the wallet too', await waitText(page, 'receive')); + check('...but the wallet keeps a quiet reminder', /still unwritten/i.test(await text(page))); + await page.reload({ waitUntil: 'domcontentloaded' }); + await waitText(page, 'receive'); + check('...which survives a reload', /still unwritten/i.test(await text(page))); + // and it is a way IN, not just a scold + await click(page, 'write it down'); + check('tapping it opens the recovery phrase', await waitText(page, 'recovery phrase')); + await ctx.close(); + } +} finally { await browser.close(); server.stop(true); } +console.log(ok ? '\n✅ the front door points at your own keys' : '\n❌ failed'); +process.exit(ok ? 0 : 1); diff --git a/tools/padlock-ask-test.js b/tools/padlock-ask-test.js index 8007aad..eb35047 100644 --- a/tools/padlock-ask-test.js +++ b/tools/padlock-ask-test.js @@ -26,7 +26,7 @@ try { await page.evaluate(() => localStorage.setItem('btc-wallet-network', 'regtest')); await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); - await click('button', 'get started'); + await click('button', 'create a new wallet'); await sleep(500); await click('button', 'Import existing'); await sleep(400); diff --git a/tools/padlock-flow-test.js b/tools/padlock-flow-test.js index 4b659aa..be4f5ec 100644 --- a/tools/padlock-flow-test.js +++ b/tools/padlock-flow-test.js @@ -29,7 +29,7 @@ try { await page.evaluate(() => localStorage.setItem('btc-wallet-network', 'regtest')); await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); - await click('button', 'Get started'); + await click('button', 'Create a new wallet'); await sleep(500); await click('button', 'Import existing'); await sleep(400); @@ -117,7 +117,7 @@ try { await p2.evaluate(() => localStorage.setItem('btc-wallet-network', 'regtest')); await p2.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); - await click2('Get started'); + await click2('Create a new wallet'); await sleep(500); check('wallet A open', await importWallet()); await sleep(2000); diff --git a/tools/post-actions-test.js b/tools/post-actions-test.js index 8cc525c..06d4739 100644 --- a/tools/post-actions-test.js +++ b/tools/post-actions-test.js @@ -38,7 +38,7 @@ try { await page.evaluate(() => localStorage.setItem('btc-wallet-network', 'regtest')); await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); - await click('get started'); await sleep(500); + await click('create a new wallet'); await sleep(500); await click('import existing'); await sleep(400); await page.waitForSelector('textarea'); const mn = generateMnemonic(wordlist); diff --git a/tools/profile-batch-test.js b/tools/profile-batch-test.js index 335bfbc..bf8dcf4 100644 --- a/tools/profile-batch-test.js +++ b/tools/profile-batch-test.js @@ -68,7 +68,7 @@ try { await page.evaluate(() => localStorage.setItem('btc-wallet-network', 'regtest')); await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); - await click('get started'); await sleep(500); + await click('create a new wallet'); await sleep(500); await click('import existing'); await sleep(400); await page.waitForSelector('textarea'); const mn = generateMnemonic(wordlist); diff --git a/tools/profile-draft-test.js b/tools/profile-draft-test.js index 243f8ec..cb016da 100644 --- a/tools/profile-draft-test.js +++ b/tools/profile-draft-test.js @@ -41,7 +41,7 @@ try { await page.goto(origin); await page.evaluate(() => localStorage.setItem('btc-wallet-network', 'regtest')); await page.reload({ waitUntil: 'domcontentloaded' }); - await click('Get started'); + await click('Create a new wallet'); await click('Import existing'); await page.waitForSelector('textarea'); const mnemonic = generateMnemonic(wordlist); diff --git a/tools/profile-feed-test.js b/tools/profile-feed-test.js index 1e7283b..d285c44 100644 --- a/tools/profile-feed-test.js +++ b/tools/profile-feed-test.js @@ -25,7 +25,7 @@ const waitText = async (x, ms = 20000) => { for (let i = 0; i < ms / 250; i++) { try { await page.goto('http://localhost:5234/', { waitUntil: 'domcontentloaded' }); await sleep(400); - await click('button', 'Get started'); + await click('button', 'Create a new wallet'); await sleep(300); await click('button', 'Import existing'); await sleep(300); diff --git a/tools/profile-open-test.js b/tools/profile-open-test.js index 7c88dee..da90af5 100644 --- a/tools/profile-open-test.js +++ b/tools/profile-open-test.js @@ -62,7 +62,7 @@ try { await page.evaluate(() => localStorage.setItem('btc-wallet-network', 'regtest')); await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); - await click('get started'); await sleep(500); + await click('create a new wallet'); await sleep(500); await click('import existing'); await sleep(400); await page.waitForSelector('textarea'); const mn = generateMnemonic(wordlist); diff --git a/tools/push-unavailable-test.js b/tools/push-unavailable-test.js index b285aee..4808501 100644 --- a/tools/push-unavailable-test.js +++ b/tools/push-unavailable-test.js @@ -38,7 +38,7 @@ try { await page.evaluate(() => localStorage.setItem('btc-wallet-network', 'regtest')); await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); - await click('get started'); await sleep(500); + await click('create a new wallet'); await sleep(500); await click('import existing'); await sleep(400); await page.waitForSelector('textarea'); const mn = generateMnemonic(wordlist); diff --git a/tools/quote-card-test.js b/tools/quote-card-test.js index 5225f4a..695ee93 100644 --- a/tools/quote-card-test.js +++ b/tools/quote-card-test.js @@ -38,7 +38,7 @@ try { await page.evaluate(() => localStorage.setItem('btc-wallet-network', 'regtest')); await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); - await click('get started'); await sleep(500); + await click('create a new wallet'); await sleep(500); await click('import existing'); await sleep(400); await page.waitForSelector('textarea'); const mn = generateMnemonic(wordlist); diff --git a/tools/ui-morph-test.js b/tools/ui-morph-test.js index 858a5a7..343ccd2 100644 --- a/tools/ui-morph-test.js +++ b/tools/ui-morph-test.js @@ -29,12 +29,12 @@ check('app booted to a screen with buttons', hasButton); // walk into a wallet if the entry button exists const clicked = await page.evaluate(() => { - const btn = [...document.querySelectorAll('button')].find((b) => /get started/i.test(b.textContent)); + const btn = [...document.querySelectorAll('button')].find((b) => /create a new wallet/i.test(b.textContent)); if (btn) { btn.click(); return true; } return false; }); await sleep(4000); -check('entered next screen', await page.evaluate(() => document.body.textContent.length > 100), clicked ? '' : '(no Get started button — already in wallet)'); +check('entered next screen', await page.evaluate(() => document.body.textContent.length > 100), clicked ? '' : '(no start button — already in wallet)'); // node identity across background renders: tag a button, force renders, compare const identity = await page.evaluate(async () => { diff --git a/tools/unifiedpush-test.js b/tools/unifiedpush-test.js index 022ead6..6a3819c 100644 --- a/tools/unifiedpush-test.js +++ b/tools/unifiedpush-test.js @@ -55,7 +55,7 @@ try { await page.evaluate(() => localStorage.setItem('btc-wallet-network', 'regtest')); await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); - await click('get started'); await sleep(500); + await click('create a new wallet'); await sleep(500); await click('import existing'); await sleep(400); await page.waitForSelector('textarea'); const mn = generateMnemonic(wordlist); diff --git a/tools/youtube-embed-test.js b/tools/youtube-embed-test.js index f9bbf0d..a70272c 100644 --- a/tools/youtube-embed-test.js +++ b/tools/youtube-embed-test.js @@ -22,7 +22,7 @@ await page.goto('http://localhost:5267/', { waitUntil: 'domcontentloaded' }); await page.evaluate(() => { localStorage.setItem('btc-wallet-network', 'regtest'); localStorage.setItem('btc-wallet-theme', 'dark'); }); await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); -await click('get started'); await sleep(500); +await click('create a new wallet'); await sleep(500); await click('import existing'); await sleep(400); await page.waitForSelector('textarea'); const mn = generateMnemonic(wordlist); diff --git a/tools/zap-tally-cache-test.js b/tools/zap-tally-cache-test.js index 98cfe7e..4e91260 100644 --- a/tools/zap-tally-cache-test.js +++ b/tools/zap-tally-cache-test.js @@ -24,7 +24,7 @@ try { await page.evaluate(() => localStorage.setItem('btc-wallet-network', 'regtest')); await page.reload({ waitUntil: 'domcontentloaded' }); await sleep(400); - await click('get started'); await sleep(500); + await click('create a new wallet'); await sleep(500); await click('import existing'); await sleep(400); await page.waitForSelector('textarea'); const mnemonic = generateMnemonic(wordlist); |
