142 lines
6.3 KiB
JavaScript
142 lines
6.3 KiB
JavaScript
// UX Audit Mobile Banner Overlap — doctrine Yacine "banner CTA chevauche Brain+WhatsApp bottom-right sur mobile"
|
|
// Crée par Opus session v1.9 — cible pages publiques + mobile viewport
|
|
const { chromium, devices } = require('playwright');
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
(async () => {
|
|
const out = '/var/www/html/proofs/ux-audit-mobile-banner-' + new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
|
|
fs.mkdirSync(out, { recursive: true });
|
|
const logFile = path.join(out, 'audit.log');
|
|
const log = (m) => { console.log(m); fs.appendFileSync(logFile, m + '\n'); };
|
|
fs.writeFileSync(logFile, '');
|
|
|
|
log('=== UX AUDIT MOBILE BANNER OVERLAP ===');
|
|
log('started: ' + new Date().toISOString());
|
|
|
|
const b = await chromium.launch({ headless: true, args: ['--no-sandbox', '--disable-setuid-sandbox'] });
|
|
// iPhone 12 viewport: 390x844
|
|
const c = await b.newContext({
|
|
...devices['iPhone 12'],
|
|
recordVideo: { dir: out, size: { width: 390, height: 844 } }
|
|
});
|
|
const p = await c.newPage();
|
|
|
|
const pages = [
|
|
{ url: 'https://weval-consulting.com/', name: 'landing-index', slug: 'index' },
|
|
{ url: 'https://weval-consulting.com/products/consulting.html', name: 'products-consulting', slug: 'consulting' },
|
|
{ url: 'https://weval-consulting.com/products/leadforge.html', name: 'products-leadforge', slug: 'leadforge' },
|
|
{ url: 'https://weval-consulting.com/products/academy-elearning-v2.html', name: 'products-academy', slug: 'academy' },
|
|
{ url: 'https://weval-consulting.com/products/workspace.html', name: 'products-workspace', slug: 'workspace' },
|
|
{ url: 'https://weval-consulting.com/wevia.html', name: 'wevia-public', slug: 'wevia' },
|
|
{ url: 'https://weval-consulting.com/wevia-widget.html', name: 'wevia-widget', slug: 'widget' },
|
|
{ url: 'https://weval-consulting.com/wevia-master.html', name: 'wevia-master', slug: 'master' },
|
|
{ url: 'https://weval-consulting.com/enterprise-model.html', name: 'enterprise-model', slug: 'enterprise' },
|
|
{ url: 'https://weval-consulting.com/tarifs', name: 'tarifs', slug: 'tarifs' }
|
|
];
|
|
|
|
const report = { mobile_viewport: '390x844 iPhone12', pages: [], total_overlaps: 0, cta_detected: 0 };
|
|
|
|
for (const target of pages) {
|
|
log(`--- scanning ${target.name} (${target.url}) ---`);
|
|
const pageReport = { name: target.name, url: target.url, overlaps: [], cta_text: null, bottom_right_elements: 0 };
|
|
try {
|
|
await p.goto(target.url, { waitUntil: 'domcontentloaded', timeout: 30000 });
|
|
await p.waitForTimeout(3500); // let widget/brain/whatsapp inject
|
|
|
|
// Full page screenshot
|
|
await p.screenshot({ path: path.join(out, `${target.slug}-full.png`), fullPage: false });
|
|
|
|
// Zoom bottom-right (viewport 0 - last 250px h, last 200px w)
|
|
await p.screenshot({
|
|
path: path.join(out, `${target.slug}-bottomright-zoom.png`),
|
|
clip: { x: 390 - 200, y: 844 - 250, width: 200, height: 250 }
|
|
});
|
|
|
|
// Detect all fixed/sticky positioned elements
|
|
const fixedEls = await p.evaluate(() => {
|
|
const all = document.querySelectorAll('*');
|
|
const fixed = [];
|
|
for (const el of all) {
|
|
const s = getComputedStyle(el);
|
|
if ((s.position === 'fixed' || s.position === 'sticky') && s.display !== 'none' && s.visibility !== 'hidden' && parseFloat(s.opacity) > 0.1) {
|
|
const r = el.getBoundingClientRect();
|
|
if (r.width > 0 && r.height > 0 && r.width < 500 && r.height < 500) {
|
|
fixed.push({
|
|
tag: el.tagName,
|
|
id: el.id || '',
|
|
cls: (el.className || '').toString().slice(0, 100),
|
|
pos: {
|
|
top: Math.round(r.top),
|
|
left: Math.round(r.left),
|
|
right: Math.round(window.innerWidth - r.right),
|
|
bottom: Math.round(window.innerHeight - r.bottom),
|
|
w: Math.round(r.width),
|
|
h: Math.round(r.height)
|
|
},
|
|
zIndex: s.zIndex,
|
|
text: (el.textContent || '').trim().slice(0, 60)
|
|
});
|
|
}
|
|
}
|
|
}
|
|
return fixed;
|
|
});
|
|
|
|
log(` fixed/sticky: ${fixedEls.length}`);
|
|
|
|
// Find CTA text ("Demander un devis", "Prendre RDV", "Devis")
|
|
const ctaEls = fixedEls.filter(e =>
|
|
/demander.*devis|prendre.*rdv|contact|devis|rdv/i.test(e.text)
|
|
);
|
|
if (ctaEls.length > 0) {
|
|
pageReport.cta_text = ctaEls[0].text;
|
|
report.cta_detected++;
|
|
log(` CTA found: "${ctaEls[0].text}" at pos ${JSON.stringify(ctaEls[0].pos)}`);
|
|
}
|
|
|
|
// Bottom-right zone: right < 80, bottom < 200
|
|
const br = fixedEls.filter(e => e.pos.right < 80 && e.pos.bottom < 200);
|
|
pageReport.bottom_right_elements = br.length;
|
|
log(` bottom-right elements: ${br.length}`);
|
|
for (const e of br) log(` - ${e.tag}#${e.id}.${e.cls.slice(0,40)} pos=${JSON.stringify(e.pos)} text="${e.text}"`);
|
|
|
|
// Detect overlaps in bottom-right
|
|
for (let i = 0; i < br.length; i++) {
|
|
for (let j = i + 1; j < br.length; j++) {
|
|
const a = br[i].pos, b = br[j].pos;
|
|
const aL = a.left, aR = a.left + a.w, aT = a.top, aB = a.top + a.h;
|
|
const bL = b.left, bR = b.left + b.w, bT = b.top, bB = b.top + b.h;
|
|
if (aL < bR && aR > bL && aT < bB && aB > bT) {
|
|
const overlap = {
|
|
zone: 'bottom-right',
|
|
elemA: { tag: br[i].tag, id: br[i].id, text: br[i].text, pos: a },
|
|
elemB: { tag: br[j].tag, id: br[j].id, text: br[j].text, pos: b }
|
|
};
|
|
pageReport.overlaps.push(overlap);
|
|
report.total_overlaps++;
|
|
log(` 🔴 OVERLAP detected: [${br[i].id || br[i].tag}] <-> [${br[j].id || br[j].tag}]`);
|
|
}
|
|
}
|
|
}
|
|
|
|
pageReport.all_fixed_els = fixedEls;
|
|
} catch (e) {
|
|
log(` ERROR: ${e.message}`);
|
|
pageReport.error = e.message;
|
|
}
|
|
report.pages.push(pageReport);
|
|
}
|
|
|
|
// Save JSON report
|
|
fs.writeFileSync(path.join(out, 'report.json'), JSON.stringify(report, null, 2));
|
|
log('=== SUMMARY ===');
|
|
log(`total_overlaps: ${report.total_overlaps}`);
|
|
log(`cta_detected: ${report.cta_detected}`);
|
|
log(`output: ${out}`);
|
|
log(`public URL: https://weval-consulting.com/proofs/${path.basename(out)}/`);
|
|
|
|
await c.close();
|
|
await b.close();
|
|
})().catch(e => { console.error('FATAL:', e.message); process.exit(1); });
|