#!/usr/bin/env node /** * ObsiGate — Configurations modal mobile usability tests (BUG-071 + #114). * * Static checks (no jsdom needed — runs in the "Frontend unit tests" CI step): * - BUG-071a: #config-nav shared the .help-nav rule that hides it below * 768px, but the config modal had no toggle (the help modal has * #help-hamburger) → the table of contents was unreachable on mobile. * The header must carry #config-hamburger (same .help-hamburger * treatment: hidden on desktop, visible on mobile) wired in config.js. * - BUG-071b: the TOC links were bare anchors with no JS — no active state, * no auto-collapse on mobile, unreliable scrolling inside the modal. * config.js must smooth-scroll to the section, mark it active and collapse * the nav on mobile, and reset the nav on open. * - BUG-071c: two-column grids (.ai-default-grid, .ai-provider-fields), * fixed-width add-rows (.config-add-row, 180/140/100px inline widths) and * single-line webhook/token/share items overflowed a 360px viewport. * style.css must stack/wrap them below 768px with 44px touch targets. * - BUG-071d: every #config-nav link target must exist (dead-anchor guard, * same class of bug as BUG-067 for the help modal). * - BUG-071e: data-i18n-attr supports several "attr:key" pairs (";" * separated) so the toggle carries translated title AND aria-label. * - #114a: the TOC is a left slide-over drawer (position: fixed) opened by * .config-toc-open on #config-modal, with a backdrop (::before) and a * close button (#config-toc-close). * - #114b: the modal is full-screen (100dvh), inputs are ≥16px anti-zoom * with 44px min-height, buttons are 44px touch targets, the backend Save * row is sticky, and the MFA recovery list is single-column on mobile. * - #114i18n: dead settings.* keys purged, settings.explorer and * config.toc_close present in FR and EN, settings.tabs translated in FR. * * Usage: node tests/frontend/config-mobile.test.mjs */ import { strict as assert } from "node:assert"; import { readFileSync } from "node:fs"; import path from "node:path"; import { fileURLToPath } from "node:url"; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const ROOT = path.join(__dirname, "..", ".."); const indexHtml = readFileSync(path.join(ROOT, "frontend", "index.html"), "utf8"); const configJs = readFileSync(path.join(ROOT, "frontend", "js", "config.js"), "utf8"); const i18nJs = readFileSync(path.join(ROOT, "frontend", "js", "i18n.js"), "utf8"); const css = readFileSync(path.join(ROOT, "frontend", "style.css"), "utf8"); const fr = JSON.parse(readFileSync(path.join(ROOT, "frontend", "locales", "fr.json"), "utf8")); const en = JSON.parse(readFileSync(path.join(ROOT, "frontend", "locales", "en.json"), "utf8")); function test(label, fn) { try { fn(); console.log(" ✓ " + label); } catch (err) { console.error(" ✗ " + label + "\n " + err.message); process.exitCode = 1; } } // ── BUG-071a: header TOC toggle ───────────────────────────────────────────── test("index.html — #config-hamburger exists in the config modal header", () => { const modal = indexHtml.match(/