Oregami
Repositories/oxedyne/daimond

oxedyne/daimond/dev/verify_consolelink.mjs

4.4 KiB, 1 run

created by r2519314175:309, 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// Does the Home drawer show the console link to an account that holds a role?
2// The network path is the gateway's and is already proven by the console page
3// itself; what is under test here is the drawer's own logic.
4//
5// It counted its own reds all along and then exited 0, so the count reached
6// nobody: `run_all.sh` reads the exit code. It exits on the count now.
7//
8// PROVED AGAINST A ROLE-LESS ANSWER FIRST. `--break norole` has whoami answer as
9// the gateway would for an account holding nothing, and the run is expected to
10// FAIL — a check that has only ever been shown the owner cannot tell whether it
11// is reading the drawer's logic or just finding a link that is always there.
12//
13// node dev/verify_consolelink.mjs --break norole # expected to FAIL
14// node dev/verify_consolelink.mjs # and then, clean
15import { open, shot, errors } from './harness.mjs';
16
17const ok = [], bad = [];
18const check = (name, pass, detail) => {
19 (pass ? ok : bad).push(name + (detail ? ' — ' + detail : ''));
20 console.log((pass ? ' ok ' : ' FAIL ') + name + (detail ? ' — ' + detail : ''));
21};
22
23const BREAK = (() => {
24 const i = process.argv.indexOf('--break');
25 return i > 0 ? String(process.argv[i + 1] || '') : '';
26})();
27if (BREAK && BREAK !== 'norole') {
28 console.error(`unknown break '${BREAK}'; known: norole`);
29 process.exit(2);
30}
31
32const s = await open({ name: 'consolelink' });
33const { page } = s;
34if (BREAK) console.log(`\n*** RUNNING UNDER --break ${BREAK}: failures below are the point ***\n`);
35page.on('request', r => { if (r.url().includes('/api/admin')) console.log('REQ ', r.url()); });
36page.on('response', r => { if (r.url().includes('/api/admin')) console.log('RESP', r.status(), r.url()); });
37page.on('console', m => { const t = m.text(); if (/console|whoami|admin/i.test(t)) console.log('LOG ', t); });
38
39// Answer whoami as the gateway would for an owner, before the drawer is opened.
40let asked = 0;
41await page.route('**/api/admin*', route => {
42 asked++;
43 route.fulfill({
44 status: 200,
45 contentType: 'application/json',
46 body: JSON.stringify(BREAK === 'norole'
47 ? { ok: true, account_id: 'acct', client_fp: 'aaaa bbbb', role: null, can_grant: false }
48 : { ok: true, account_id: 'acct', client_fp: 'aaaa bbbb', role: 'owner', can_grant: true }),
49 });
50});
51
52// Open the Home drawer by the cog, which is what a user reaches for.
53//
54// The cog TOGGLES, so this first puts the drawer away. `open()` does not leave
55// it in a known state: `connectMock` drives the model form through the same
56// cog and does not close it afterwards. This test used to click once and find
57// the drawer open, but only because a bug elsewhere -- unlocking reopened it,
58// so connectMock's click was closing rather than opening. Fixing that bug
59// flipped the parity and failed this test, which was never really testing the
60// cog. Normalise, then open.
61await page.evaluate(() => {
62 document.body.dispatchEvent(new MouseEvent('mousedown', { bubbles: true }));
63});
64await page.waitForTimeout(400);
65await page.click('#settings-btn', { force: true });
66await page.waitForTimeout(1700);
67
68// The drawer has to be open, or "no link" below means "no drawer" and the check
69// reads as a pass for the wrong reason under --break.
70const secs = await page.$$eval('.admin-sec', els => els.map(e => e.textContent));
71check('the cog opened the Home drawer', secs.length > 0, `${secs.length} sections: ${JSON.stringify(secs)}`);
72check('and the drawer asked the gateway who we are', asked > 0, `${asked} calls to /api/admin`);
73
74const seen = await page.$('.admin-console-link');
75check('THE CONSOLE LINK IS IN THE DRAWER FOR A ROLE-HOLDER', !!seen, '.admin-console-link');
76if (seen) {
77 const info = await page.$eval('.admin-console-link', a => ({ href: a.getAttribute('href'), text: a.textContent, target: a.target }));
78 check('and it points at the console', info.href === '/console/', JSON.stringify(info));
79}
80
81// What the drawer actually contains, when it does not.
82const state = await page.evaluate(() => ({
83 unlocked: !!(window.DaimondIdentity && DaimondIdentity.isUnlocked()),
84 exists: !!(window.DaimondIdentity && DaimondIdentity.exists()),
85}));
86console.log('identity:', JSON.stringify(state));
87await shot(s, 'console-link');
88
89const errs = errors(s).filter(e => !/502|Bad Gateway/.test(e));
90check('nothing threw', errs.length === 0, errs.slice(0, 2).join(' | '));
91
92await s.close();
93console.log(`\n${ok.length} passed, ${bad.length} failed`);
94if (bad.length) { bad.forEach(x => console.log(' FAILED: ' + x)); process.exit(1); }