83 lines
8.3 KiB
JavaScript
83 lines
8.3 KiB
JavaScript
// Proba consolei web (consola-web/index.html) intr-un Chromium ADEVARAT (playwright-core + Chromium-ul instalat de Playwright), pe
|
|
// telefon (390x844, isMobile, cu media queries aplicate) si pe desktop (1440x900). Inchide randul NEMASURAT din 29 sept ("aspectul
|
|
// consolei web intr-un browser adevarat"): pana atunci pagina fusese rulata numai intr-un DOM minim, fara browser.
|
|
// Cere, pe fiecare latime: nicio eroare de consola sau de pagina; latimea interna = latimea ecranului (pagina nu iese din telefon);
|
|
// nicio derulare orizontala; exemplul redat (OK); o stare REALA a consolei (facuta chiar de consola.mjs pe un jurnal real al
|
|
// sidecar-ului) redata cu verdictul ei, OK si apoi BROKEN; un buraf brut redat UNCHECKED cu nota; un JSON stricat -> mesajul de eroare;
|
|
// si finalitatea: consola nu o masoara, deci pentru o stare lipita nu are voie sa arate o treapta ca fiind cea curenta.
|
|
// Reteaua e oprita (fonturile Google se cer si se refuza): proba nu atinge internetul.
|
|
// node proba-consola-web.mjs [cale index.html] iesire 0 = toate cum trebuia, 1 = una rosie, 2 = nu s-a putut rula (fara browser)
|
|
import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; import crypto from 'node:crypto';
|
|
import { execFileSync } from 'node:child_process'; import { fileURLToPath, pathToFileURL, } from 'node:url';
|
|
import { createRequire } from 'node:module';
|
|
import { evalueaza, caleaSidecarului } from './consola.mjs';
|
|
|
|
const AICI = path.dirname(fileURLToPath(import.meta.url));
|
|
const PAGINA = path.resolve(process.argv[2] || path.join(AICI, 'consola-web', 'index.html'));
|
|
const EXE = process.env.CHROME || path.join(os.homedir(), 'AppData', 'Local', 'ms-playwright', 'chromium-1228', 'chrome-win64', 'chrome.exe');
|
|
let chromium;
|
|
try { ({ chromium } = createRequire(import.meta.url)('playwright-core')); } catch { /* mai jos */ }
|
|
if (!chromium || !fs.existsSync(EXE)) { console.log(`NEMASURAT: lipseste playwright-core (npm i playwright-core, in dosarul acesta sau mai sus) sau Chromium (${EXE}); CHROME=<chrome> pentru altul`); process.exit(2); }
|
|
|
|
let rele = 0, bune = 0; const cer = (n, c, d = '') => { console.log(` [${c ? 'OK ' : 'RAU '}] ${n}${c || !d ? '' : ' -- ' + d}`); if (c) bune++; else rele++; };
|
|
|
|
// stari reale, facute de uneltele adevarate: un jurnal al sidecar-ului, un buraf, consola pe el; apoi acelasi jurnal atins
|
|
const T = fs.mkdtempSync(path.join(os.tmpdir(), 'cons-web-'));
|
|
const SIDE = caleaSidecarului();
|
|
const side = (args) => { try { execFileSync(process.execPath, [SIDE, ...args], { encoding: 'utf8', stdio: 'pipe' }); } catch { /* verificat prin rezultat */ } };
|
|
const log = path.join(T, 'audit.log'); const bin = path.join(T, 'node'); fs.writeFileSync(bin, 'BIN');
|
|
const sh = '0x' + crypto.createHash('sha256').update(fs.readFileSync(bin)).digest('hex');
|
|
side(['record', '--kind', 'runtime', '--artifact', bin, '--attested', sh, '--host', 'h1', '--log', log, '--at', '2026-09-26T09:00:00Z']);
|
|
side(['record', '--kind', 'deployment', '--name', 'app', '--version', '1.0', '--content-file', bin, '--log', log, '--at', '2026-09-26T09:01:00Z']);
|
|
const bf = path.join(T, 'b.json'); side(['bundle', '--log', log, '--out', bf, '--host', 'h1', '--at', '2026-09-26T09:02:00Z']);
|
|
const bundle = JSON.parse(fs.readFileSync(bf, 'utf8'));
|
|
const stareOk = await evalueaza({ bundle });
|
|
const atins = JSON.parse(JSON.stringify(bundle)); atins.entries[0].proof.statement.host = 'h2';
|
|
const stareRupta = await evalueaza({ bundle: atins });
|
|
if (stareOk.verdict !== 'OK' || stareRupta.verdict !== 'BROKEN') { console.log(`NEMASURAT: starile de proba nu sunt cele asteptate (${stareOk.verdict}, ${stareRupta.verdict})`); process.exit(2); }
|
|
|
|
const browser = await chromium.launch({ executablePath: EXE, headless: true });
|
|
try {
|
|
for (const vp of [{ w: 390, h: 844, mobil: true }, { w: 1440, h: 900, mobil: false }]) {
|
|
const et = `${vp.w}px`;
|
|
const ctx = await browser.newContext({ viewport: { width: vp.w, height: vp.h }, deviceScaleFactor: 1, isMobile: vp.mobil, hasTouch: vp.mobil });
|
|
const page = await ctx.newPage();
|
|
const erori = []; const cereri = [];
|
|
page.on('console', (m) => { if (m.type() === 'error' && !/ERR_(BLOCKED|FAILED|INTERNET|NAME)|net::|Failed to load resource/.test(m.text())) erori.push(m.text().slice(0, 160)); });
|
|
page.on('pageerror', (e) => erori.push('pageerror: ' + String(e.message).slice(0, 160)));
|
|
await page.route('**/*', (r) => { const u = r.request().url(); if (u.startsWith('file:')) return r.continue(); cereri.push(u); return r.abort(); });
|
|
await page.goto(pathToFileURL(PAGINA).href, { waitUntil: 'load' });
|
|
const m = await page.evaluate(() => ({ iw: window.innerWidth, sw: document.documentElement.scrollWidth, vtext: document.getElementById('vtext').textContent, tiles: document.querySelectorAll('#tiles .tile').length }));
|
|
cer(`${et}: latimea interna = latimea ecranului (${m.iw})`, m.iw === vp.w, `innerWidth ${m.iw}`);
|
|
cer(`${et}: nicio derulare orizontala (scrollWidth ${m.sw})`, m.sw <= m.iw, `scrollWidth ${m.sw} > ${m.iw}`);
|
|
cer(`${et}: exemplul redat: verdict OK, patru casete`, m.vtext === 'OK' && m.tiles === 4, JSON.stringify(m));
|
|
// un buton pe care un om nu il poate apasa (acoperit, in afara ecranului) e un defect al paginii, deci iese RAU, nu opreste proba
|
|
const lipeste = async (o) => { try { await page.fill('#in', typeof o === 'string' ? o : JSON.stringify(o), { timeout: 5000 }); await page.click('#render', { timeout: 5000 }); }
|
|
catch (e) { cer(`${et}: campul si butonul Render se pot folosi`, false, String(e.message).split('\n')[0].slice(0, 140)); return {}; }
|
|
return page.evaluate(() => ({
|
|
vtext: document.getElementById('vtext').textContent, bad: document.getElementById('verdict').classList.contains('bad'), alert: document.getElementById('alert').textContent,
|
|
perr: document.getElementById('perr').hidden ? null : document.getElementById('perr').textContent, cur: document.querySelectorAll('#ladder .step.cur').length,
|
|
fin: [...document.querySelectorAll('#tiles .tile')].map((t) => t.textContent).find((x) => /Finality/.test(x)) || null, sw: document.documentElement.scrollWidth })); };
|
|
const a = await lipeste(stareOk);
|
|
cer(`${et}: o stare reala OK (consola.mjs pe un jurnal real) redata OK`, a.vtext === 'OK' && !a.bad, JSON.stringify(a));
|
|
cer(`${et}: pentru o stare lipita nicio treapta de finalitate nu e aratata drept cea curenta (consola nu o masoara)`, a.cur === 0, `${a.cur} treapta marcata curenta`);
|
|
cer(`${et}: caseta de finalitate spune ca finalitatea nu e masurata aici`, a.fin && /not checked/i.test(a.fin), a.fin);
|
|
const b = await lipeste(stareRupta);
|
|
cer(`${et}: o stare reala BROKEN (jurnal atins) redata BROKEN, in culoarea rea`, b.vtext === 'BROKEN' && b.bad, JSON.stringify(b));
|
|
const c = await lipeste(bundle);
|
|
cer(`${et}: un buraf brut redat UNCHECKED, cu nota`, c.vtext === 'UNCHECKED' && /checks nothing/.test(c.alert), JSON.stringify(c).slice(0, 200));
|
|
const d = await lipeste('{ nu e json');
|
|
cer(`${et}: un JSON stricat -> mesajul de eroare, nimic redat peste`, d.perr && /invalid JSON/.test(d.perr) && d.vtext === 'UNCHECKED', JSON.stringify(d).slice(0, 200));
|
|
cer(`${et}: tot fara derulare orizontala dupa redari (scrollWidth ${c.sw})`, c.sw <= vp.w, `scrollWidth ${c.sw}`);
|
|
try { await page.click('#reset', { timeout: 5000 }); } catch (e) { cer(`${et}: butonul Example se poate apasa`, false, String(e.message).split('\n')[0].slice(0, 140)); }
|
|
const e = await page.evaluate(() => ({ vtext: document.getElementById('vtext').textContent, cur: document.querySelectorAll('#ladder .step.cur').length }));
|
|
cer(`${et}: butonul Example reface exemplul (verdict OK)`, e.vtext === 'OK', JSON.stringify(e));
|
|
cer(`${et}: nicio eroare de consola sau de pagina`, !erori.length, erori.join(' | '));
|
|
cer(`${et}: nicio cerere in afara fisierului a ajuns la retea (${cereri.length} cerute, toate refuzate: fonturile)`, cereri.every((u) => /fonts\.(googleapis|gstatic)\.com/.test(u)), cereri.join(', '));
|
|
if (process.env.CAPTURI) { fs.mkdirSync(process.env.CAPTURI, { recursive: true }); await page.screenshot({ path: path.join(process.env.CAPTURI, `consola-${vp.w}.png`), fullPage: true }); }
|
|
await ctx.close();
|
|
}
|
|
} finally { await browser.close(); fs.rmSync(T, { recursive: true, force: true }); }
|
|
console.log(`\nconsola-web: ${bune}/${bune + rele} cum trebuia (Chromium ${path.basename(path.dirname(path.dirname(EXE)))})`);
|
|
process.exitCode = rele ? 1 : 0;
|