diff --git a/api/audit-products.js b/api/audit-products.js new file mode 100644 index 000000000..1b6f63678 --- /dev/null +++ b/api/audit-products.js @@ -0,0 +1,47 @@ +const { chromium } = require("playwright"); +const fs = require("fs"); +const TS = new Date().toISOString().replace(/[:.]/g, "-").slice(0, 19); +const OUT = "/var/www/html/proofs/audit-products-" + TS; +fs.mkdirSync(OUT + "/zooms", { recursive: true }); +const PAGES = ["leadforge","consulting","academy","arsenal","affiliates","bizplan","auditai","ai-sdr","adscontrol","workspace"]; +(async () => { + const browser = await chromium.launch({ headless: true, args: ["--no-sandbox","--disable-gpu","--disable-dev-shm-usage"] }); + const results = []; + for (const name of PAGES) { + let ctx; + try { + ctx = await browser.newContext({ viewport: { width: 1440, height: 900 } }); + const pg = await ctx.newPage(); + await pg.goto("https://weval-consulting.com/products/" + name + ".html", { waitUntil: "domcontentloaded", timeout: 15000 }); + await pg.waitForTimeout(1500); + await pg.screenshot({ path: OUT + "/zooms/" + name + "-tr.png", clip: { x: 1040, y: 0, width: 400, height: 400 } }); + await pg.screenshot({ path: OUT + "/zooms/" + name + "-br.png", clip: { x: 1040, y: 500, width: 400, height: 400 } }); + const overlaps = await pg.evaluate(() => { + const fn = (x1,y1,x2,y2) => { + const all = document.querySelectorAll("button,.btn,.toggle,.tab,[class*=toggle],[class*=btn],.chip,.badge,.fab"); + let n = 0; + for (const el of all) { + const r = el.getBoundingClientRect(); + if (r.width<2 || r.height<2) continue; + const pos = getComputedStyle(el).position; + if (pos !== "fixed" && pos !== "absolute") continue; + const cx = r.x+r.width/2, cy = r.y+r.height/2; + if (cx>=x1 && cx<=x2 && cy>=y1 && cy<=y2) n++; + } + return n; + }; + return { tr: fn(1040,0,1440,400), br: fn(1040,500,1440,900) }; + }); + results.push({ page: name, tr: overlaps.tr, br: overlaps.br, ok: true }); + await pg.close(); + await ctx.close(); + } catch (e) { + results.push({ page: name, error: e.message.slice(0,100) }); + try { await ctx.close(); } catch {} + } + } + await browser.close(); + const overlaps = results.filter(r => r.ok && (r.tr>1 || r.br>1)); + fs.writeFileSync(OUT + "/summary.json", JSON.stringify({ doctrine: "196", ts: new Date().toISOString(), pages: PAGES.length, overlaps_count: overlaps.length, overlaps, results }, null, 2)); + console.log(JSON.stringify({ ok: true, outdir: OUT.replace("/var/www/html",""), pages: PAGES.length, overlaps_count: overlaps.length })); +})(); diff --git a/proofs/audit-products-2026-04-24T14-06-59/summary.json b/proofs/audit-products-2026-04-24T14-06-59/summary.json new file mode 100644 index 000000000..f96b757ed --- /dev/null +++ b/proofs/audit-products-2026-04-24T14-06-59/summary.json @@ -0,0 +1,69 @@ +{ + "doctrine": "196", + "ts": "2026-04-24T14:08:25.698Z", + "pages": 10, + "overlaps_count": 0, + "overlaps": [], + "results": [ + { + "page": "leadforge", + "tr": 0, + "br": 0, + "ok": true + }, + { + "page": "consulting", + "tr": 0, + "br": 0, + "ok": true + }, + { + "page": "academy", + "tr": 0, + "br": 0, + "ok": true + }, + { + "page": "arsenal", + "tr": 0, + "br": 0, + "ok": true + }, + { + "page": "affiliates", + "tr": 0, + "br": 0, + "ok": true + }, + { + "page": "bizplan", + "tr": 0, + "br": 0, + "ok": true + }, + { + "page": "auditai", + "tr": 0, + "br": 0, + "ok": true + }, + { + "page": "ai-sdr", + "tr": 0, + "br": 0, + "ok": true + }, + { + "page": "adscontrol", + "tr": 0, + "br": 0, + "ok": true + }, + { + "page": "workspace", + "tr": 0, + "br": 0, + "ok": true + } + ] +} \ No newline at end of file