aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/minimal-smoke.js99
1 files changed, 99 insertions, 0 deletions
diff --git a/tools/minimal-smoke.js b/tools/minimal-smoke.js
new file mode 100644
index 0000000..ab31619
--- /dev/null
+++ b/tools/minimal-smoke.js
@@ -0,0 +1,99 @@
+// Smoke test for the minimal build profile (HAL_FEATURES=none): a plain
+// on-chain wallet with no swap/Ark/gift/SP surfaces — and still fully working
+// (imports a wallet, receives a payment) against the regtest stack.
+//
+// Usage: bun tools/minimal-smoke.js
+
+import { execSync } from 'node:child_process';
+import puppeteer from 'puppeteer-core';
+import { generateMnemonic } from '@scure/bip39';
+import { wordlist } from '@scure/bip39/wordlists/english';
+import { buildHtml } from '../build.js';
+
+const BCLI = `bitcoin-cli -regtest -datadir=${process.env.HOME}/ark-regtest/bitcoind -rpcport=18543 -rpcwallet=miner`;
+const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
+const sh = (cmd) => execSync(cmd, { shell: '/bin/bash' }).toString().trim();
+
+let ok = true;
+const check = (name, cond, detail = '') => {
+ console.log(` ${cond ? '✓' : '✗'} ${name}${detail ? ' — ' + detail : ''}`);
+ if (!cond) ok = false;
+};
+
+// --- build the minimal profile and serve it ---
+const html = await buildHtml({ minify: true, pwa: false, features: 'none' });
+const full = await buildHtml({ minify: true, pwa: false });
+console.log(`minimal bundle: ${(html.length / 1024).toFixed(0)}KB (full: ${(full.length / 1024).toFixed(0)}KB)`);
+check('meaningfully smaller than full', html.length < full.length * 0.9);
+check('no ark protocol code', !html.includes('Ark VTXO mailbox authorization'));
+check('no swap protocol code', !html.includes('swap/reverse'));
+check('no musig code', !html.includes('MuSig/noncecoef'));
+
+const server = Bun.serve({ port: 5198, 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 page = await browser.newPage();
+const errs = [];
+page.on('pageerror', (e) => errs.push(e.message));
+const bodyText = () => page.evaluate(() => document.body.innerText);
+const waitText = async (text, ms = 20000) => {
+ for (let i = 0; i < ms / 250; i++) {
+ if ((await bodyText()).toLowerCase().includes(text.toLowerCase())) return true;
+ await sleep(250);
+ }
+ return false;
+};
+const clickText = (sel, text) => page.evaluate((s, x) => {
+ const el = [...document.querySelectorAll(s)].find((e) => e.textContent.trim().toLowerCase().includes(x.toLowerCase()));
+ if (el) { el.click(); return true; }
+ return false;
+}, sel, text);
+
+try {
+ await page.goto('http://localhost:5198', { waitUntil: 'domcontentloaded' });
+ await page.evaluate(() => {
+ localStorage.setItem('btc-wallet-network', 'regtest');
+ localStorage.setItem('btc-wallet-source:regtest', JSON.stringify({ id: 'custom', url: 'http://localhost:30002' }));
+ localStorage.setItem('btc-wallet-ark-provider:regtest', 'local'); // must be ignored by this build
+ });
+ await page.reload({ waitUntil: 'domcontentloaded' });
+ await sleep(400);
+
+ console.log('\n[1] import + core wallet');
+ await clickText('button', 'Import existing');
+ await sleep(200);
+ await page.type('textarea', generateMnemonic(wordlist));
+ await clickText('button', 'Open wallet');
+ check('wallet opened', await waitText('receive', 15000));
+
+ console.log('\n[2] no feature surfaces');
+ await sleep(2500); // give any (wrongly present) feature time to appear
+ check('no receive-mode dropdown', await page.evaluate(() =>
+ ![...document.querySelectorAll('option')].some((o) => ['ln', 'ark', 'sp'].includes(o.value))));
+ await clickText('.tabs button', 'Send');
+ await sleep(300);
+ check('no gift button', !(await bodyText()).includes('Gift some Bitcoin'));
+ await clickText('.tabs button', 'Settings');
+ await sleep(300);
+ const settings = await bodyText();
+ check('no swap provider card', !settings.includes('Swap provider'));
+ check('no ark card', !/⚔ Ark/.test(settings));
+ check('no sp indexer card', !settings.includes('Silent payment indexer'));
+
+ console.log('\n[3] core receive still works');
+ await clickText('.tabs button', 'Receive');
+ await sleep(300);
+ const addr = await page.evaluate(() => document.querySelector('.addr-box')?.textContent.trim());
+ check('onchain address shown', /^bcrt1/.test(addr || ''), (addr || '').slice(0, 16) + '…');
+ sh(`${BCLI} sendtoaddress ${addr} 0.0004`);
+ sh(`${BCLI} generatetoaddress 1 $(${BCLI} getnewaddress) >/dev/null`);
+ check('payment lands', await waitText('40,000', 30000));
+ check('no page errors', errs.length === 0, errs.join(' | '));
+
+ console.log(ok ? '\n✅ SUCCESS: minimal profile is a working on-chain-only wallet'
+ : '\n❌ some checks failed');
+} finally {
+ await browser.close();
+ server.stop(true);
+}
+process.exit(ok ? 0 : 1);