// The front door's "Migrate a coinos account" button: people moving from // the original coinos.io couldn't find the move, which waited behind the // Spending offer. The door stashes the /migrate link's intent, explains that // a wallet comes first, survives a refresh, and Back forgets the intent. // // Run: bun tools/onboard-migrate-door-test.js import puppeteer from './puppeteer.js'; import { buildHtml } from '../build.js'; const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); let ok = true; const check = (n, c, d = '') => { console.log(` ${c ? '✓' : '✗'} ${n}${d ? ' — ' + d : ''}`); if (!c) ok = false; }; const html = await buildHtml({ minify: true, pwa: false }); const server = Bun.serve({ port: 5293, 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 text = (page) => page.evaluate(() => document.body.innerText); const waitText = async (page, x, ms = 20000) => { for (let i = 0; i < ms / 250; i++) { if ((await text(page)).toLowerCase().includes(x.toLowerCase())) return true; await sleep(250); } return false; }; const visible = (page) => page.evaluate(() => [...document.querySelectorAll('button')].filter((b) => b.offsetParent).map((b) => ({ id: b.id, text: b.textContent.trim() }))); const clickText = (page, x) => page.evaluate((s) => { const e = [...document.querySelectorAll('button')].find((n) => n.textContent.trim().toLowerCase().replace(/^[^a-z]+/, '') === s); if (e) { e.click(); return true; } return false; }, x.toLowerCase()); try { const page = await browser.newPage(); await page.setViewport({ width: 390, height: 844 }); await page.goto('http://localhost:5293/', { waitUntil: 'domcontentloaded' }); check('the front door paints', await waitText(page, 'create a new wallet')); const btns = await visible(page); const iMigrate = btns.findIndex((b) => b.id === 'onb-migrate'); const iLastSignin = btns.map((b) => /sign in/i.test(b.text)).lastIndexOf(true); check('a Migrate a coinos account button sits below the sign-in buttons', iMigrate > 0 && iMigrate > iLastSignin, btns.map((b) => b.text).join(' | ')); await page.click('#onb-migrate'); check('it opens the move page', await waitText(page, 'move your coinos.io account', 5000)); check('…which says a wallet and its words come first', /first, set up the wallet.*twelve words/i.test((await text(page)).replace(/\s+/g, ' '))); const stashed = await page.evaluate(() => Number(localStorage.getItem('btc-wallet-migrate-link')) > 0); check('the migrate intent is stashed, as the /migrate link stashes it', stashed); const doors = await visible(page); check('it offers Create plus the sign-in doors', doors.some((b) => b.id === 'onb-migrate-create') && doors.some((b) => /sign in with nostr/i.test(b.text)), doors.map((b) => b.text).join(' | ')); await page.reload({ waitUntil: 'domcontentloaded' }); check('a refresh stays on the move page', await waitText(page, 'move your coinos.io account', 8000)); check('Back returns to the front door', await clickText(page, 'back') && await waitText(page, 'create a new wallet', 5000)); const forgot = await page.evaluate(() => !localStorage.getItem('btc-wallet-migrate-link')); check('…and forgets the intent', forgot); await page.click('#onb-migrate'); await waitText(page, 'move your coinos.io account', 5000); await page.click('#onb-migrate-create'); check('Create my new wallet shows the twelve words', await waitText(page, 'set up your wallet', 5000)); check('the intent survives into wallet creation', await page.evaluate(() => Number(localStorage.getItem('btc-wallet-migrate-link')) > 0)); // the /migrate link on a device with no wallet: straight to the move page const fresh = await browser.createBrowserContext(); const linked = await fresh.newPage(); await linked.setViewport({ width: 390, height: 844 }); const pageErrors = []; linked.on('pageerror', (e) => pageErrors.push(e.message)); await linked.goto('http://localhost:5293/migrate', { waitUntil: 'domcontentloaded' }); check('/migrate with no wallet opens on the move page', await waitText(linked, 'move your coinos.io account', 10000)); check('…at the address bar root', await linked.evaluate(() => location.pathname === '/')); check('no page errors', pageErrors.length === 0, pageErrors.join('; ')); } finally { await browser.close(); server.stop(true); } console.log(ok ? '\n✅ the migrate door' : '\n❌ failures above'); process.exit(ok ? 0 : 1);