diff options
| -rw-r--r-- | src/app.js | 119 | ||||
| -rw-r--r-- | src/i18n.js | 11 | ||||
| -rw-r--r-- | src/style.css | 9 | ||||
| -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 |
24 files changed, 269 insertions, 45 deletions
@@ -1280,6 +1280,12 @@ async function enterWallet(mnemonic, passphrase, opts = {}) { mnemonic: (mnemonic || '').trim().replace(/\s+/g, ' '), passphrase: passphrase || '', }); + // Has this person ever had the recovery phrase in front of them? Creating + // a wallet shows it; importing one means they typed it. A sign-in DERIVES + // it — Google, a passkey and a nostr key all end up at a seed the user has + // never seen, and which nobody can reissue for them. That is the one case + // worth asking about, so it's recorded once and never guessed at again. + if (acc.seedSeen === undefined) acc.seedSeen = !opts.nostrPubkey; await activateAccount(acc, { ...opts, fresh: true }); } @@ -2888,7 +2894,12 @@ function recoveryCard(a) { a.passphrase ? copyBtn(a.passphrase, t('copyPassphrase')) : null, h('button', { class: 'btn-sm grow', onClick: () => { ui.revealShown = false; render(); } }, t('hide'))) : h('div', { class: 'row gap6' }, - h('button', { class: 'btn-primary grow', onClick: () => { ui.revealShown = 'words'; render(); } }, t('revealWords')), + h('button', { class: 'btn-primary grow', onClick: () => { + ui.revealShown = 'words'; + // seeing it here counts: no need to be asked again elsewhere + if (!a.seedSeen) { a.seedSeen = true; persistAccounts(); if (a.persisted) writeVault(); } + render(); + } }, t('revealWords')), h('button', { class: 'btn-sm', onClick: () => { ui.revealShown = false; render(); } }, t('hide'))) ); } @@ -3080,14 +3091,23 @@ function onboardScreen() { // the login finishes. if ((o.step === 'welcome' || o.step === 'seed' || o.step === 'signin') && ui.screen === 'wallet' && activeAccount() && !ui.nostrLoginBusy) { - ui.onb = null; - try { localStorage.removeItem(ONB_STEP_KEY); } catch {} - // On-chain first: the wizard's promise is "your keys, your coins", and - // Spending stays out of sight until the user sets it up. - ui.account = 'savings'; - try { localStorage.setItem(ACCOUNT_KEY, 'savings'); } catch {} - ui.tab = 'history'; - return null; // the caller falls through to the wallet itself + // A wallet that arrived by SIGN-IN has a recovery phrase its owner has + // never seen. Show it once, here, while it still reads as part of + // setting up — rather than leaving it buried in settings until the day + // they lose the Google account and find out it was never a backup. + // Handled further down, where page()/title() exist. + const arrived = activeAccount(); + if (arrived && arrived.mnemonic && !arrived.seedSeen) o.step = 'backup'; + else { + ui.onb = null; + try { localStorage.removeItem(ONB_STEP_KEY); } catch {} + // On-chain first: the wizard's promise is "your keys, your coins", and + // Spending stays out of sight until the user sets it up. + ui.account = 'savings'; + try { localStorage.setItem(ACCOUNT_KEY, 'savings'); } catch {} + ui.tab = 'history'; + return null; // the caller falls through to the wallet itself + } } if (o.step === 'spend') { const addr = featureHook('namesAddress'); @@ -3164,14 +3184,24 @@ function onboardScreen() { return page([ h('div', { class: 'onb-hero' }, brandHeader(false)), title(t('onbWelcomeTitle')), - // Starting is the primary path; bringing a nostr account is the - // alternative, offered here rather than as a question of its own. - h('button', { class: 'btn-primary btn-block', style: 'font-size:17px;padding:14px', onClick: () => { - ui.unlockTab = 'create'; - ui.unlockError = ''; - o.step = 'seed'; - render(); - } }, t('onbStart')), + // What this wallet IS, before the buttons. Four equally weighted + // buttons with only the sign-in ones saying what they do sent people + // straight past the seed phrase — which is the wallet — into a login + // that is only a convenience wrapped around it. + h('p', { class: 'muted', style: 'margin:0' }, t('onbWelcomeBody')), + // Making your own keys is the path; a sign-in is the alternative, + // below the line. + h('button', { + // a stable hook for tests, so this button survives its own copy + id: 'onb-create', + class: 'btn-primary btn-block', style: 'font-size:17px;padding:14px', onClick: () => { + ui.unlockTab = 'create'; + ui.unlockError = ''; + o.step = 'seed'; + render(); + } }, t('onbStart')), + h('p', { class: 'small muted', style: 'margin:-6px 0 0;text-align:center' }, t('onbStartHint')), + h('div', { class: 'onb-or' }, h('span', {}, t('onbOrSignIn'))), // All the sign-in doors right on the welcome screen (Google, passkey, // Nostr) — the feature supplies them; the single-button fallback keeps // builds without it working. @@ -3186,6 +3216,42 @@ function onboardScreen() { ui.onbError ? h('div', { class: 'notice err' }, ui.onbError) : null, ]); } + if (o.step === 'backup') { + const acc = activeAccount(); + // the wallet went away (switched, logged out) — nothing to back up + if (!acc || !acc.mnemonic) { ui.onb = null; try { localStorage.removeItem(ONB_STEP_KEY); } catch {} return null; } + const words = acc.mnemonic.split(' '); + const done = () => { + acc.seedSeen = true; + persistAccounts(); + if (acc.persisted) writeVault(); + ui.onb = null; + try { localStorage.removeItem(ONB_STEP_KEY); } catch {} + ui.account = 'savings'; + try { localStorage.setItem(ACCOUNT_KEY, 'savings'); } catch {} + ui.tab = 'history'; + render(); + }; + return page([ + title(t('onbBackupTitle')), + h('p', { class: 'muted', style: 'margin:0' }, t('onbBackupBody')), + h('div', { class: 'warn-box' }, t('writeDownWarn')), + h('div', { class: 'words' }, words.map((w, i) => + h('div', { class: 'w' }, h('span', { class: 'n' }, i + 1), h('span', { class: 't' }, w)))), + h('div', { class: 'row gap6' }, copyBtn(acc.mnemonic, t('copyPhrase'))), + h('button', { class: 'btn-primary btn-block', style: 'padding:14px', onClick: done }, t('onbBackupDone')), + // Later means later, not never: seedSeen stays false, so the wallet + // keeps a quiet reminder until the phrase has actually been seen. + h('button', { class: 'btn-ghost btn-block', onClick: () => { + ui.onb = null; + try { localStorage.removeItem(ONB_STEP_KEY); } catch {} + ui.account = 'savings'; + try { localStorage.setItem(ACCOUNT_KEY, 'savings'); } catch {} + ui.tab = 'history'; + render(); + } }, t('onbBackupLater')), + ]); + } if (o.step === 'seed') { // The classic create/import card, inside the wizard: getting started // means seeing (or bringing) your seed phrase, not a wallet minted @@ -3439,12 +3505,31 @@ function walletScreen() { brandHeader(true), h('div', { class: 'mt16' }, balanceCard()), ui.offlineFallback && wallet.offline ? offlineBanner() : null, + backupNotice(), ...featureAll('walletNotices'), tabsBar(), pane ); } +// A wallet whose recovery phrase has never been on screen keeps one quiet +// line about it. "Later" is a real answer, so this is a row and not a modal — +// but it doesn't dismiss, because the risk doesn't go away on its own. It +// disappears the moment the phrase is actually seen (here or in settings). +function backupNotice() { + const a = activeAccount(); + if (!a || !a.mnemonic || a.seedSeen || wallet.watchOnly) return null; + return h('div', { class: 'small muted', style: 'margin:10px 0 0;text-align:center' }, + t('backupReminder'), + ' ', + h('button', { class: 'linklike small', onClick: () => { + ui.tab = 'settings'; + ui.settingsPage = 'wallet'; + ui.revealShown = 'masked'; + render(); + } }, t('backupReminderGo'))); +} + function offlineBanner() { return h( 'div', diff --git a/src/i18n.js b/src/i18n.js index 3faa843..6a46823 100644 --- a/src/i18n.js +++ b/src/i18n.js @@ -716,7 +716,16 @@ const STR = { or: 'or', nlSignIn: 'Sign in', onbWelcomeTitle: 'Welcome to coinos', - onbStart: 'Get started', + onbWelcomeBody: 'Bitcoin you actually hold. Your wallet is twelve words — nobody else ever has them, and they work in any wallet, anywhere.', + onbStart: 'Create a new wallet', + onbStartHint: "Takes a minute: write down twelve words and it's yours", + onbOrSignIn: 'or, if you already have an account', + onbBackupTitle: 'Write down your recovery phrase', + onbBackupBody: "Signing in was the easy part — but the sign-in isn't your wallet. These twelve words are. They're the only way back in if you lose access to that account, and nobody can reissue them for you.", + onbBackupDone: "I've written it down", + onbBackupLater: 'Later', + backupReminder: 'Your recovery phrase is still unwritten.', + backupReminderGo: 'Write it down', onbSeedTitle: 'Set up your wallet', onbSigninTitle: 'Sign in', onbLegacyTitle: 'Move your coinos.io account', diff --git a/src/style.css b/src/style.css index a16101c..c9b4368 100644 --- a/src/style.css +++ b/src/style.css @@ -865,6 +865,15 @@ body::after { column to the 420px cap even on narrower phones, overflowing the page. */ .onb { width: 100%; max-width: 420px; margin: 8vh auto 0; padding: 0 8px; } .onb-title { margin: 0; font-size: 26px; letter-spacing: -0.02em; } +/* The line between making your own keys and signing in with someone else's + account. Without it the four buttons read as four equal choices. */ +.onb-or { + display: flex; align-items: center; gap: 12px; margin: 4px 0 -4px; + color: var(--faint); font-size: 13px; +} +.onb-or::before, .onb-or::after { + content: ''; flex: 1; height: 1px; background: var(--line); +} .onb-hero { margin-bottom: 8px; } .onb-punks { display: flex; gap: 10px; overflow-x: auto; padding: 6px 2px; } .onb-punk { width: 56px; height: 56px; border-radius: 50%; flex-shrink: 0; cursor: pointer; border: 3px solid transparent; transition: transform .15s, border-color .15s; } 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); |
