From 392e73b9d163f39555895e9652d3a80f41cce6f3 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Sun, 15 Nov 2026 14:27:20 -0700 Subject: The footer is just the language and currency; everything else moved to Settings → About. "Let's move all the footer stuff to an about page or something except for the language and currency toggles" (Adam, 2026-11-15). A new About tile (an info mark) on the Settings hub opens a page with the credits (made by Adam Soltys, source on git.coinos.io), How it works, Show your support (once a wallet is open), Install app (when the browser offers it), the Dark/Light mode switch and the running commit. footer-support-test now checks the footer holds only the two pickers and drives About (credits, theme switch, support → hat shop); onboard-backup, chat-layout, features-smoke and signup-quick pass MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01VMan6SwkivduFDDkMmnXjZ --- tools/footer-support-test.js | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) (limited to 'tools') diff --git a/tools/footer-support-test.js b/tools/footer-support-test.js index a37c070..93ae8a1 100644 --- a/tools/footer-support-test.js +++ b/tools/footer-support-test.js @@ -1,5 +1,6 @@ -// The footer's "Show your support" link opens the hat shop, from any page, -// and only shows once a wallet is open (the shop pays from it). +// "Show your support" lives on Settings → About (2026-11-15: the footer is +// just the language and currency now) and opens the hat shop; it shows only +// once a wallet is open (the shop pays from it). // Run: bun tools/footer-support-test.js import puppeteer from './puppeteer.js'; import { generateMnemonic } from '@scure/bip39'; @@ -18,31 +19,40 @@ const browser = await puppeteer.launch({ executablePath: process.env.CHROME || ' const page = await browser.newPage(); const errs = []; page.on('pageerror', (e) => errs.push(String(e))); const click = (t) => page.evaluate((x) => { const e = [...document.querySelectorAll('button')].find((n) => n.textContent.trim().toLowerCase().includes(x)); if (e) { e.click(); return true; } return false; }, t); -const link = () => page.evaluate(() => [...document.querySelectorAll('.footer button')].some((b) => b.textContent.trim() === 'Show your support')); +const link = () => page.evaluate(() => !!document.getElementById('about-support')); +const footerOnly = () => page.evaluate(() => { const f = document.querySelector('.footer'); return f ? { selects: f.querySelectorAll('select').length, other: f.querySelectorAll('button, a').length, text: f.innerText.replace(/\s+/g, ' ').trim() } : null; }); +const openAbout = async () => { + await page.evaluate(() => [...document.querySelectorAll('button')].find((e) => e.getAttribute('aria-label') === 'Settings').click()); await sleep(500); + await page.evaluate(() => [...document.querySelectorAll('.settings-tile')].find((e) => /^About/.test(e.textContent.trim())).click()); await sleep(500); +}; try { await page.setViewport({ width: 390, height: 844 }); await page.goto(server.url.origin, { waitUntil: 'domcontentloaded' }); await sleep(600); check('no link before there is a wallet', !(await link())); - await click('create a new wallet'); await sleep(500); await click('to import'); await sleep(400); + await page.waitForSelector('#onb-signin'); await page.click('#onb-signin'); await sleep(400); + await page.click('#onb-restore'); await sleep(400); await page.waitForSelector('textarea'); await page.type('textarea', generateMnemonic(wordlist)); await click('open wallet'); for (let i = 0; i < 80 && !(await page.evaluate(() => [...document.querySelectorAll('button')].some((b) => b.textContent.trim() === 'Send'))); i++) { await page.evaluate(() => [...document.querySelectorAll('button')].find((b) => /^(not now|skip|continue|later)$/i.test(b.textContent.trim()))?.click()); await sleep(400); } await sleep(1500); - check('the footer has "Show your support"', await link()); - // from another page: the feed - await page.evaluate(() => [...document.querySelectorAll('nav button, .bottom-nav button')].find((b) => /feed/i.test(b.textContent))?.click()); await sleep(800); - await page.evaluate(() => [...document.querySelectorAll('.footer button')].find((b) => b.textContent.trim() === 'Show your support').click()); + const f = await footerOnly(); + check('the footer is the language and currency alone', f && f.selects === 2 && f.other === 0, JSON.stringify(f)); + await openAbout(); + const about = await page.evaluate(() => document.querySelector('.about-card')?.innerText || ''); + check('About has the credits, How it works, the theme and the commit', /Adam Soltys/.test(about) && /git\.coinos\.io/.test(about) && /How it works/.test(about) && /(Dark|Light) mode/.test(about) && /Running git commit [0-9a-f]{7}/.test(about), about.replace(/\n/g, ' | ')); + check('...and "Show your support"', await link()); + const th = await page.evaluate(() => document.documentElement.dataset.theme); + await page.click('#about-theme'); await sleep(300); + check('the theme switch works from there', await page.evaluate(() => document.documentElement.dataset.theme) !== th); + await page.click('#about-theme'); await sleep(300); + if (process.env.SHOT) await page.screenshot({ path: process.env.SHOT }); + await page.click('#about-support'); await sleep(1200); const txt = await page.evaluate(() => document.body.innerText); - check('it opens the hat shop', /hat/i.test(txt) && !!(await page.evaluate(() => document.querySelector('.hat-btn') || /Crown|Top hat|hats?/i.test(document.querySelector('.card')?.innerText || ''))), txt.slice(0, 160).replace(/\n/g, ' | ')); - await page.evaluate(() => { document.querySelector('.bottom-nav, nav')?.remove(); window.scrollTo(0, 1e6); }); - await sleep(300); - if (process.env.SHOT) await (await page.$('.footer')).screenshot({ path: process.env.SHOT }); - else await page.screenshot({ path: '/tmp/footer-support.png', fullPage: true }); - // || '/tmp/footer-support.png', fullPage: true }); + check('it opens the hat shop', /hat/i.test(txt), txt.slice(0, 160).replace(/\n/g, ' | ')); check('no page errors', errs.length === 0, errs.join('; ').slice(0, 300)); } catch (e) { console.error(e); ok = false; } finally { await browser.close(); server.stop(); } console.log(ok ? 'PASS' : 'FAIL'); -- cgit v1.2.3