Python 33.5%
JavaScript 30.1%
TypeScript 25%
CSS 10%
Shell 1.4%
1// Auteur : Simon-Pierre Boucher — contact@spboucher.ai2// ka-ui/validate.mjs — validation responsive + harmonisation des 12 sites.3// Usage : node validate.mjs [site…] (lancer depuis un dossier où playwright4// est installé, ex. : cd ~/Desktop/ora-ka && node ~/Desktop/ka-ui/validate.mjs)5// Pour chaque site × viewport (360, 768, 1440) : capture d'écran dans6// ka-ui/screenshots/, débordement horizontal, badge/lien Groupe KA, footer7// commun, title « — Un service Groupe KA », bouton KA au premier écran.8import { chromium } from "playwright";9import { mkdirSync, readFileSync } from "fs";10import { dirname, join } from "path";11import { fileURLToPath } from "url";1213const here = dirname(fileURLToPath(import.meta.url));14const eco = JSON.parse(readFileSync(join(here, "ecosystem.json"), "utf8"));15const outDir = join(here, "screenshots");16mkdirSync(outDir, { recursive: true });1718const only = process.argv.slice(2);19const sites = eco.sites.filter((s) => !only.length || only.includes(s.id));20const viewports = [21 { name: "mobile", width: 360, height: 780 },22 { name: "tablette", width: 768, height: 1024 },23 { name: "desktop", width: 1440, height: 900 },24];2526const browser = await chromium.launch();27const rows = [];28for (const site of sites) {29 for (const vp of viewports) {30 const ctx = await browser.newContext({31 viewport: { width: vp.width, height: vp.height },32 isMobile: vp.width < 700,33 hasTouch: vp.width < 700,34 });35 const page = await ctx.newPage();36 const r = { site: site.id, vp: vp.name, ok: true, issues: [] };37 try {38 const resp = await page.goto(`https://${site.domain}/`, {39 waitUntil: "networkidle",40 timeout: 45000,41 });42 if (!resp || resp.status() !== 200) {43 r.ok = false;44 r.issues.push(`HTTP ${resp ? resp.status() : "??"}`);45 }46 await page.waitForTimeout(1200);47 const m = await page.evaluate(() => ({48 overflow: document.documentElement.scrollWidth - window.innerWidth,49 title: document.title,50 gk: !!document.querySelector('a[href*="groupe-ka.com"]'),51 footer: !!document.querySelector(".ka-footer, footer"),52 emails: (document.body.innerHTML.match(/@groupe-ka\.com/g) || []).length,53 kaBtn: /Se connecter avec KA|KA ID/i.test(document.body.innerText),54 }));55 if (m.overflow > 1) { r.ok = false; r.issues.push(`débordement +${m.overflow}px`); }56 if (site.id !== "groupe-ka" && !/Un service Groupe KA/i.test(m.title))57 r.issues.push(`title: « ${m.title.slice(0, 60)} »`);58 if (!m.gk) { r.ok = false; r.issues.push("aucun lien groupe-ka.com"); }59 if (!m.footer) { r.ok = false; r.issues.push("footer absent"); }60 if (m.emails < 3) r.issues.push(`courriels contact: ${m.emails}/3`);61 if (site.id !== "groupe-ka" && !m.kaBtn) r.issues.push("bouton KA non vu (peut être dans menu)");62 await page.screenshot({63 path: join(outDir, `${site.id}-${vp.name}.png`),64 fullPage: vp.name === "desktop",65 });66 } catch (e) {67 r.ok = false;68 r.issues.push(String(e).split("\n")[0].slice(0, 100));69 }70 rows.push(r);71 console.log(72 `${r.ok && !r.issues.length ? "✅" : r.ok ? "⚠️ " : "❌"} ${site.id.padEnd(10)} ${vp.name.padEnd(8)} ${r.issues.join(" · ")}`73 );74 await ctx.close();75 }76}77await browser.close();78const bad = rows.filter((r) => !r.ok);79console.log(`\n${rows.length} vérifications, ${bad.length} échecs. Captures: ${outDir}`);80