aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/amber-login-test.js26
-rw-r--r--tools/onboard-backup-test.js14
-rw-r--r--tools/padlock-flow-test.js51
3 files changed, 58 insertions, 33 deletions
diff --git a/tools/amber-login-test.js b/tools/amber-login-test.js
index 7439f0d..0b4c3c9 100644
--- a/tools/amber-login-test.js
+++ b/tools/amber-login-test.js
@@ -73,15 +73,25 @@ try {
// walked through
const signedKinds = [];
let signedUrls = 1;
+ // answer the next sign_event intent, if one is waiting
+ const signNext = async () => {
+ const pend = (await urls()).filter((u) => /type=sign_event/.test(u));
+ if (pend.length <= signedUrls) return false;
+ const u = pend[signedUrls]; signedUrls++;
+ const ev = JSON.parse(decodeURIComponent(u.slice('nostrsigner:'.length).split('?')[0]));
+ signedKinds.push(ev.kind);
+ await deliver(JSON.stringify(finalizeEvent({ kind: ev.kind, created_at: ev.created_at, tags: ev.tags, content: ev.content }, SK)));
+ return true;
+ };
for (let i = 0; i < 80 && !(await page.evaluate(() => document.body.innerText.toLowerCase().includes('receive'))); i++) {
- const us = await urls();
- const pend = us.filter((u) => /type=sign_event/.test(u));
- if (pend.length > signedUrls) {
- const u = pend[signedUrls]; signedUrls++;
- const ev = JSON.parse(decodeURIComponent(u.slice('nostrsigner:'.length).split('?')[0]));
- signedKinds.push(ev.kind);
- await deliver(JSON.stringify(finalizeEvent({ kind: ev.kind, created_at: ev.created_at, tags: ev.tags, content: ev.content }, SK)));
- } else { await click('later') || await click('skip verification') || await click("i've written it down") || await click('continue'); }
+ if (!(await signNext())) { await click('later') || await click('skip verification') || await click("i've written it down") || await click('continue'); }
+ await sleep(500);
+ }
+ // The starter follows wait on a deep look for a list this key may already
+ // have (2026-10-02 follows-overwrite fix), so their signature can arrive
+ // after the wallet is open: keep answering until things go quiet.
+ for (let i = 0, idle = 0; i < 60 && idle < 8; i++) {
+ if (await signNext()) idle = 0; else if (signedKinds.length) idle++;
await sleep(500);
}
if (!(await page.evaluate(() => document.body.innerText.toLowerCase().includes('receive')))) console.log(' DEBUG loop end:', JSON.stringify(await page.evaluate(() => ({ btns: [...document.querySelectorAll('button')].map((b) => b.textContent.trim()).filter(Boolean).slice(0, 12), text: document.body.innerText.slice(0, 160).replace(/\n+/g, ' | ') }))));
diff --git a/tools/onboard-backup-test.js b/tools/onboard-backup-test.js
index 3b242dd..9fe5a3e 100644
--- a/tools/onboard-backup-test.js
+++ b/tools/onboard-backup-test.js
@@ -73,15 +73,23 @@ try {
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) + '…');
+ // The welcome card is just the title and the buttons now (42c8d4e): the
+ // way in itself says what the wallet is — tapping it puts the twelve
+ // words on screen at once, with what they are.
+ await page.evaluate(() => document.getElementById('onb-create')?.click());
+ await sleep(600);
+ const made = await page.evaluate(() => ({
+ words: document.querySelectorAll('.words .w .t').length,
+ says: /12 words|twelve words/i.test(document.body.innerText),
+ }));
+ check('the way in shows what the wallet actually is: its twelve words', made.words === 12 && made.says,
+ made.words + ' words' + (made.says ? ', explained' : ', unexplained'));
await page.close();
}
diff --git a/tools/padlock-flow-test.js b/tools/padlock-flow-test.js
index e67711f..59aee32 100644
--- a/tools/padlock-flow-test.js
+++ b/tools/padlock-flow-test.js
@@ -1,7 +1,7 @@
// The padlock on a fresh (passwordless) wallet must never cost you your
// session: the set-a-password ask renders over the open wallet, declining it
// lands you exactly where you were, the logo stays a way home, and the
-// Wallets list shows only the on-chain wallet until Spending is set up.
+// wallet settings speak only of the on-chain wallet until Spending is set up.
// Setting a password locks in place: watch-only home, closed padlock.
// Run: bun tools/padlock-flow-test.js
import puppeteer from 'puppeteer-core';
@@ -20,9 +20,8 @@ const page = await browser.newPage();
await page.setViewport({ width: 420, height: 880 });
const click = (sel, t) => page.evaluate((s, x) => { const e = [...document.querySelectorAll(s)].find((n) => n.textContent.trim().toLowerCase().includes(x.toLowerCase())); if (e) { e.click(); return true; } return false; }, sel, t);
-const clickTitle = (t) => page.evaluate((x) => { const b = [...document.querySelectorAll('button')].find((n) => n.title === x); if (b) { b.click(); return true; } return false; }, t);
-// the padlock lives on the Accounts page: open it from the balance card first
-const padlock = async (pg, title) => { await pg.evaluate(() => document.querySelector('.balance-switch')?.click()); await sleep(500); return pg.evaluate((x) => { const b = [...document.querySelectorAll('button')].find((n) => n.title === x); if (b) { b.click(); return true; } return false; }, title); };
+// the padlock lives on the wallet settings: open them from the balance card's gear first
+const padlock = async (pg, title) => { await pg.evaluate(() => document.querySelector('.balance-gear')?.click()); await sleep(500); return pg.evaluate((x) => { const b = [...document.querySelectorAll('button')].find((n) => n.title === x); if (b) { b.click(); return true; } return false; }, title); };
const body = () => page.evaluate(() => document.body.innerText);
const waitText = async (x, ms = 20000) => { for (let i = 0; i < ms / 250; i++) { if ((await body()).toLowerCase().includes(x.toLowerCase())) return true; await sleep(250); } return false; };
@@ -41,8 +40,8 @@ try {
check('wallet open', await waitText('receive'));
await sleep(2000);
- console.log('\n[the Wallets list before opting into Spending]');
- await page.evaluate(() => document.querySelector('.balance-switch')?.click());
+ console.log('\n[the wallet settings before opting into Spending]');
+ await page.evaluate(() => document.querySelector('.balance-gear')?.click());
await sleep(600);
let txt = await body();
check('shows the on-chain wallet', /savings/i.test(txt), txt.slice(0, 140).replace(/\n+/g, ' | '));
@@ -60,7 +59,7 @@ try {
await click('button', 'Not now');
await sleep(600);
txt = await body();
- check('declining lands back where the padlock was tapped (the Accounts page)', /add account/i.test(txt), txt.slice(0, 120).replace(/\n+/g, ' | '));
+ check('declining lands back where the padlock was tapped (the wallet settings)', /security/i.test(txt) && !(await page.$('input[type=password]')), txt.slice(0, 120).replace(/\n+/g, ' | '));
console.log('\n[the logo still means home]');
await page.evaluate(() => document.querySelector('.brand')?.click());
@@ -79,7 +78,7 @@ try {
await sleep(1200);
txt = await body();
check('still home (watch-only), not signed out', /receive/i.test(txt) && /balance/i.test(txt), txt.slice(0, 120).replace(/\n+/g, ' | '));
- await page.evaluate(() => document.querySelector('.balance-switch')?.click()); await sleep(500);
+ await page.evaluate(() => document.querySelector('.balance-gear')?.click()); await sleep(500);
const closed = await page.evaluate(() => [...document.querySelectorAll('button')].some((b) => b.title && /unlock/i.test(b.title)));
check('padlock is closed', closed);
await page.evaluate(() => document.querySelector('.brand')?.click()); await sleep(400);
@@ -95,7 +94,6 @@ try {
const p2 = await ctx2.newPage();
await p2.setViewport({ width: 420, height: 880 });
const click2 = (t) => p2.evaluate((x) => { const e = [...document.querySelectorAll('button')].find((n) => n.textContent.trim().toLowerCase().includes(x.toLowerCase())); if (e) { e.click(); return true; } return false; }, t);
- const clickTitle2 = (t) => p2.evaluate((x) => { const b = [...document.querySelectorAll('button')].find((n) => n.title === x); if (b) { b.click(); return true; } return false; }, t);
const body2 = () => p2.evaluate(() => document.body.innerText);
const waitText2 = async (x, ms = 20000) => { for (let i = 0; i < ms / 250; i++) { if ((await body2()).toLowerCase().includes(x.toLowerCase())) return true; await sleep(250); } return false; };
const importWallet = async () => {
@@ -106,15 +104,25 @@ try {
await click2('Open wallet');
return waitText2('receive');
};
+ // another wallet comes in the way the app offers it now: your profile →
+ // Sign into another account (→ a new one, once others exist) → import
const addWallet = async () => {
- await p2.evaluate(() => document.querySelector('.balance-switch')?.click()); await sleep(600);
- await click2('Add wallet'); await sleep(400);
- await click2('Savings'); await sleep(200);
- await click2('Continue'); await sleep(600);
+ await p2.evaluate(() => document.querySelector('.header-avatar')?.click()); await sleep(800);
+ await click2('Sign into another account'); await sleep(500);
+ await click2('Sign into a new account'); await sleep(500);
+ await click2('Import existing'); await sleep(300); // the sign-in screen's import tab
return importWallet();
};
- const savingsRows = () => p2.evaluate(() => [...document.querySelectorAll('button')].map((e) => e.textContent.trim()).filter((x) => /^[●○] Savings/.test(x)).length);
-
+ // the wallets on this device, as the identity switcher lists them (the
+ // one open plus the others)
+ const walletCount = async () => {
+ await p2.evaluate(() => document.querySelector('.header-avatar')?.click()); await sleep(800);
+ await click2('Sign into another account'); await sleep(500);
+ const n = await p2.evaluate(() => document.querySelectorAll('.confirm-pop .clickable').length + 1);
+ await p2.evaluate(() => document.querySelector('.confirm-pop-backdrop')?.click()); await sleep(300);
+ await p2.evaluate(() => document.querySelector('.brand')?.click()); await sleep(500);
+ return n;
+ };
console.log('\n[a wallet created after a reload still auto-saves]');
await p2.goto('http://localhost:5236/', { waitUntil: 'domcontentloaded' });
await p2.evaluate(() => localStorage.setItem('btc-wallet-network', 'regtest'));
@@ -144,16 +152,15 @@ try {
console.log('\n[setting the password kept every saved wallet]');
await p2.reload({ waitUntil: 'domcontentloaded' });
check('reload keeps the locked view on screen', await waitText2('receive'));
- await clickTitle2('Unlock');
+ await padlock(p2, 'Unlock');
await sleep(600);
check('re-entry asks for the password', /unlock saved wallets|enter your password/i.test(await body2()));
await (await p2.$('input[type=password]')).type('hunter22');
await click2('Unlock');
check('password opens it', await waitText2('receive'));
await sleep(1000);
- await p2.evaluate(() => document.querySelector('.balance-switch')?.click()); await sleep(600);
- check('both wallets survived the re-encrypt', (await savingsRows()) === 2, `${await savingsRows()} row(s)`);
- await click2('Back'); await sleep(500);
+ let n = await walletCount();
+ check('both wallets survived the re-encrypt', n === 2, `${n} wallet(s)`);
console.log('\n[a new wallet against a passworded vault: the ask explains itself]');
await p2.reload({ waitUntil: 'domcontentloaded' }); // session restore again: vault password not in memory
@@ -176,14 +183,14 @@ try {
check('the right one saves C and locks in place', await waitText2('receive'));
await p2.reload({ waitUntil: 'domcontentloaded' });
check('locked view survives the reload', await waitText2('receive'));
- await clickTitle2('Unlock');
+ await padlock(p2, 'Unlock');
await sleep(600);
await (await p2.$('input[type=password]')).type('hunter22');
await click2('Unlock');
check('unlocked once more', await waitText2('receive'));
await sleep(1000);
- await p2.evaluate(() => document.querySelector('.balance-switch')?.click()); await sleep(600);
- check('all three wallets are in the vault', (await savingsRows()) === 3, `${await savingsRows()} row(s)`);
+ n = await walletCount();
+ check('all three wallets are in the vault', n === 3, `${n} wallet(s)`);
await ctx2.close();
} catch (e) {
check('run completed', false, e.message);