aboutsummaryrefslogtreecommitdiffstats
path: root/tools/gift-profile-setup-test.js
blob: 9304d1a754a0e6a4b909bf307cf922b0ddeb7798 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
// After a gift is opened into a fresh wallet and its seed written down, the
// first-run wizard asks for a username, then a picture and a cover — the gift's
// recipient should arrive as someone people can pay by name. "Not now" lands in
// the account the gift went to.
// Drives an ark gift on mutinynet whose bearer key holds nothing (a random
// secret: the staging ASP sees no coin, so the sweep finds it taken), then
// "Create wallet anyway" → the seed → on to the wizard.
// Run: bun run build && bun tools/gift-profile-setup-test.js [screenshot.png]
import puppeteer from './puppeteer.js';
import { base32nopad } from '@scure/base';
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 server = Bun.serve({ port: 0, async fetch(req) {
  const p = new URL(req.url).pathname;
  const f = Bun.file('dist' + (p === '/' ? '/index.html' : p));
  return (await f.exists()) ? new Response(f) : new Response(Bun.file('dist/index.html'), { headers: { 'content-type': 'text/html' } });
} });
const giftCode = () => {
  const b = new Uint8Array(42);
  b[0] = 0x11; b[1] = 3; // ark gift, mutinynet
  new DataView(b.buffer).setBigUint64(2, 1000n, true);
  b.set(crypto.getRandomValues(new Uint8Array(32)), 10);
  return base32nopad.encode(b);
};
const browser = await puppeteer.launch({ executablePath: '/usr/bin/google-chrome', headless: true, args: ['--no-sandbox'] });
const text = (page) => page.evaluate(() => document.body.innerText);
const click = (page, x) => page.evaluate((x) => {
  const e = [...document.querySelectorAll('button')].find((n) => n.textContent.trim().toLowerCase().includes(x));
  e?.click(); return !!e;
}, x);
const waitText = async (page, re, ms = 30000) => {
  for (let t = 0; t < ms; t += 250) { if (re.test(await text(page))) return true; await sleep(250); }
  return false;
};
async function toSeed(page, note) {
  await page.goto(server.url.href);
  await page.evaluate(() => { localStorage.clear(); localStorage.setItem('btc-wallet-network', 'mutinynet'); });
  await page.goto(server.url.href + 'ag/' + giftCode() + (note ? '#m=' + encodeURIComponent(note) : ''));
  check('the gift opens: the server sees no coin behind it', await waitText(page, /aren.t visible/i));
  if (note) {
    const q = await page.evaluate(() => document.querySelector('.gift-note')?.textContent || null);
    check('the sender\'s note shows on the gift', q === note, String(q));
    check('...and leaves the address bar with the code', await page.evaluate(() => location.pathname === '/' && !location.hash));
  }
  await click(page, 'claim');
  check('...so the claim finds it taken', await waitText(page, /create a wallet anyway/i));
  check('...create a wallet anyway shows the seed', await click(page, 'create a wallet anyway') && await waitText(page, /recovery phrase/i, 10000));
}
try {
  const page = await browser.newPage();
  const errors = [];
  page.on('pageerror', (e) => errors.push(String(e)));
  await page.setViewport({ width: 390, height: 844, deviceScaleFactor: 2 });
  await toSeed(page, 'Happy birthday! 🎂 <b>not bold</b>');
  await click(page, 'skip verification');
  check('the seed done: the wizard asks for a username', await waitText(page, /now pick a username/i, 10000), (await text(page)).slice(0, 200));
  check('...with the claim form (or its wait) and Not now', await page.evaluate(() => !!document.querySelector('.onb')) && /not now/i.test(await text(page)));
  if (process.argv[2]) await page.screenshot({ path: process.argv[2] });
  // the look step follows the name, as in any first run
  await page.evaluate(() => localStorage.setItem('btc-wallet-onb-step', 'avatar'));
  await page.reload();
  check('the next step is the picture and cover', !!await page.waitForSelector('.onb-cover', { timeout: 15000 }).catch(() => null));
  // a second gift, verified this time, then Not now
  const p2 = await browser.newPage();
  p2.on('pageerror', (e) => errors.push(String(e)));
  await p2.setViewport({ width: 390, height: 844, deviceScaleFactor: 2 });
  await toSeed(p2);
  check('Verify backup opens the word check', await click(p2, 'verify') && await waitText(p2, /word #?\d/i, 5000));
  await sleep(500);
  // the verify page doesn't show the words: read them from the account
  const filled = await p2.evaluate(() => {
    const accs = JSON.parse(sessionStorage.getItem('btc-wallet-accounts') || '{}');
    const list = Array.isArray(accs) ? accs : accs.accounts || accs.list || [];
    const m = list.map((a) => a.mnemonic).filter(Boolean).pop(); if (!m) return false;
    const w = m.split(' ');
    for (const lab of document.querySelectorAll('label.field')) {
      const n = Number(lab.querySelector('.lab').textContent.match(/\d+/)[0]);
      const inp = lab.querySelector('input');
      const set = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value').set;
      set.call(inp, w[n - 1]); inp.dispatchEvent(new Event('input', { bubbles: true }));
    }
    return true;
  });
  if (filled) {
    await click(p2, 'open wallet');
    check('a verified seed leads to the username step too', await waitText(p2, /now pick a username/i, 10000));
    await click(p2, 'not now');
    let gone = false;
    for (let i = 0; i < 20 && !gone; i++) { await sleep(250); gone = !(await p2.evaluate(() => !!document.querySelector('.onb'))); }
    check('Not now: into the wallet, the wizard gone', gone, (await text(p2)).slice(0, 200));
  } else console.log(' (accounts not in localStorage — verify path not driven)');
  check('no page errors', errors.length === 0, errors.join(' | '));
} finally { await browser.close(); server.stop(true); }
console.log(ok ? '\n✅ a gift\'s new wallet is offered a name, a picture and a cover' : '\n❌ failed');
process.exit(ok ? 0 : 1);