// 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= 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;