oxedyne/daimond/dev/verify_webpanel.mjs
5.5 KiB, 1 run
created by r2519314175:807, which is this file's identity for as long as the history lasts, whatever it is later renamed to
download · who wrote it · its history
| 1 | // The read-only text copy must be torn down when a new page opens, and the |
| 2 | // panel must refuse to frame a loopback address. Both exercised via the real |
| 3 | // DaimondWeb driver in the loaded page — no network needed. |
| 4 | // |
| 5 | // PROVED AGAINST TWO BREAKS FIRST, because both checks read a value that is |
| 6 | // `false`/`none` in more than one way: |
| 7 | // --break keepoverlay the fresh page is never opened, so the old text copy |
| 8 | // is still standing and the teardown check must go red. |
| 9 | // --break framehost the loopback probe is aimed at our own blob instead, |
| 10 | // which the panel DOES frame, so the refusal check must |
| 11 | // go red. Without this the check cannot tell a refusal |
| 12 | // from an open that never happened. |
| 13 | // |
| 14 | // node dev/verify_webpanel.mjs --break keepoverlay # expected to FAIL |
| 15 | // node dev/verify_webpanel.mjs --break framehost # expected to FAIL |
| 16 | // node dev/verify_webpanel.mjs # and then, clean |
| 17 | import fs from 'node:fs'; |
| 18 | import path from 'node:path'; |
| 19 | import { fileURLToPath } from 'node:url'; |
| 20 | import { open, shot, errors } from './harness.mjs'; |
| 21 | import { GW_URL } from './ports.mjs'; |
| 22 | |
| 23 | const HERE = path.dirname(fileURLToPath(import.meta.url)); |
| 24 | const WEBJS = path.join(HERE, '..', 'www', 'js', 'web.js'); |
| 25 | |
| 26 | const ok = [], bad = []; |
| 27 | const check = (name, pass, detail) => { |
| 28 | (pass ? ok : bad).push(name + (detail ? ' — ' + detail : '')); |
| 29 | console.log((pass ? ' ok ' : ' FAIL ') + name + (detail ? ' — ' + detail : '')); |
| 30 | }; |
| 31 | |
| 32 | const BREAK = (() => { |
| 33 | const i = process.argv.indexOf('--break'); |
| 34 | return i > 0 ? String(process.argv[i + 1] || '') : ''; |
| 35 | })(); |
| 36 | if (BREAK && !['keepoverlay', 'framehost'].includes(BREAK)) { |
| 37 | console.error(`unknown break '${BREAK}'; known: keepoverlay, framehost`); |
| 38 | process.exit(2); |
| 39 | } |
| 40 | |
| 41 | const s = await open({ name: 'webpanel' }); |
| 42 | if (BREAK) console.log(`\n*** RUNNING UNDER --break ${BREAK}: failures below are the point ***\n`); |
| 43 | |
| 44 | const r = await s.page.evaluate(async ([brk, GW_URL]) => { |
| 45 | const W = window.DaimondWeb; |
| 46 | const out = { hasDriver: !!W }; |
| 47 | if (!W) return out; |
| 48 | const body = document.getElementById('web-body') || document.querySelector('.web-body'); |
| 49 | out.hasBody = !!body; |
| 50 | // `_showTextForTest` used to be called here. There is no such export on |
| 51 | // DaimondWeb and there never was, so the call did nothing and the overlay |
| 52 | // below was always the hand-built one. |
| 53 | // A page of our own, so nothing here needs the network. |
| 54 | const blob = URL.createObjectURL(new Blob(['<h1>fresh</h1>'], { type: 'text/html' })); |
| 55 | // The overlay is seeded by hand in showText's own shape and under showText's |
| 56 | // own id: `hideText()` finds it by that id, so this exercises the real |
| 57 | // teardown even though the text itself was not fetched by the gateway. |
| 58 | let pre = document.getElementById('web-text'); |
| 59 | if (!pre) { pre = document.createElement('div'); pre.id = 'web-text'; (body || document.body).appendChild(pre); } |
| 60 | pre.style.display = ''; pre.innerHTML = '<div class="web-text-body">GITHUB TEXT</div>'; |
| 61 | out.beforeOpen = { display: pre.style.display, text: pre.textContent }; |
| 62 | |
| 63 | if (brk !== 'keepoverlay') { |
| 64 | try { await W.open(blob); } catch (e) { out.openErr = e.message; } |
| 65 | } else { |
| 66 | out.skippedOpen = true; |
| 67 | } |
| 68 | const after = document.getElementById('web-text'); |
| 69 | out.afterOpenDisplay = after ? after.style.display : 'removed'; |
| 70 | out.afterOpenText = after ? after.textContent : ''; |
| 71 | |
| 72 | // Loopback must be refused (not framed). Under `framehost` the same question |
| 73 | // is asked of a page the panel is entitled to frame. |
| 74 | const probe = brk === 'framehost' ? blob : `${GW_URL}/api/balance`; |
| 75 | out.probe = brk === 'framehost' ? 'our own blob' : probe; |
| 76 | try { |
| 77 | const res = await W.open(probe); |
| 78 | out.loopback = { framed: res.framed, driver: res.driver }; |
| 79 | } catch (e) { out.loopbackErr = e.message; } |
| 80 | return out; |
| 81 | }, [BREAK, GW_URL]); |
| 82 | console.log(JSON.stringify(r, null, 2)); |
| 83 | await shot(s, 'webpanel-after'); |
| 84 | |
| 85 | check('the web panel driver and its body are on the page', r.hasDriver && r.hasBody, |
| 86 | `driver ${!!r.hasDriver}, body ${!!r.hasBody}`); |
| 87 | // The overlay is seeded by hand, so the id it is seeded under has to be the one |
| 88 | // the app's own `showText` uses -- otherwise this file tears down its own div |
| 89 | // and calls that a pass while the real copy stays on the screen. |
| 90 | const ID = "pre.id = 'web-text';"; |
| 91 | const seeded = fs.readFileSync(WEBJS, 'utf8').split(ID).length - 1; |
| 92 | check('the id the overlay is seeded under is the id showText uses', seeded === 1, |
| 93 | `"${ID}" appears ${seeded} times in www/js/web.js`); |
| 94 | check('a read-only text copy is standing before the next page opens', |
| 95 | !!(r.beforeOpen && r.beforeOpen.text), JSON.stringify(r.beforeOpen)); |
| 96 | check('A FRESH PAGE TEARS THE OLD TEXT COPY DOWN — no site\'s words under another site\'s header', |
| 97 | (r.afterOpenDisplay === 'none' || r.afterOpenDisplay === 'removed') && !r.afterOpenText, |
| 98 | `display ${r.afterOpenDisplay}, text ${JSON.stringify((r.afterOpenText || '').slice(0, 40))}` |
| 99 | + (r.skippedOpen ? ' (no page was opened)' : '') + (r.openErr ? ' openErr: ' + r.openErr : '')); |
| 100 | check('A LOOPBACK ADDRESS IS REFUSED THE FRAME', |
| 101 | !!r.loopback && r.loopback.framed === false, |
| 102 | r.loopback ? `${r.probe}: framed=${r.loopback.framed} driver=${r.loopback.driver}` : 'threw: ' + r.loopbackErr); |
| 103 | |
| 104 | const errs = errors(s).filter(e => !/502|Bad Gateway/.test(e)); |
| 105 | check('nothing threw', errs.length === 0, errs.slice(0, 2).join(' | ')); |
| 106 | |
| 107 | await s.close(); |
| 108 | console.log(`\n${ok.length} passed, ${bad.length} failed`); |
| 109 | if (bad.length) { bad.forEach(x => console.log(' FAILED: ' + x)); process.exit(1); } |