#!/usr/bin/env python3 """Does the whole suite hold together at 390px? — C2, T9.6. Nothing in the original proposal touched mobile, and it is where the worst rendering was found. This drives all seven pages at 390px (mobile emulation, so the media queries under test actually fire) and asserts: - no page scrolls sideways - no visible control is clipped past the viewport or collapsed to nothing - every control meets the 24px WCAG floor; on the gloved-hands surfaces (Field View, and the creator's rail / status / save controls) the bar is 44px, which is what the shared coarse-pointer sizing in wp-chrome.css delivers The after-screenshots live in docs/reference/baseline/after-wave9 (captured by baseline_shots.py) beside the wave 0 set. Exit 0 all passed, 1 a failure, 2 could not run. """ import json import os import re import sys import tempfile import time sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) import cdp # noqa: E402 from browser_check import seed, start_server, chk, _PASS, _FAIL # noqa: E402 from sections_check import set_sop # noqa: E402 from stepper_check import dismiss_dialogs # noqa: E402 ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) PAGES = [ ("login", "/login.html"), ("launcher", "/index.html"), ("wizard", "/work-package-suite.html?tab=sop&project=projA"), ("creator", "/wp-creation-index.html?project=projA"), ("field", "/field.html?project=projA"), ("admin", "/admin.html"), ("users", "/users.html"), ] MEASURE = """(function(){ var out = {sw: document.documentElement.scrollWidth, total:0, under24:[], clipped:[]}; function skip(el){ // An off-canvas drawer is PARKED outside the viewport by design, and a row // inside an overflow-x container is scrollable, not clipped - the same two // lessons frame_check's widest-box scan learned the hard way. for (var n = el; n; n = n.parentElement){ var cs = getComputedStyle(n); if (cs.overflowX === 'auto' || cs.overflowX === 'scroll') return true; if (cs.position === 'fixed' && cs.transform && cs.transform !== 'none') return true; if (n.getAttribute && n.getAttribute('aria-hidden') === 'true') return true; } return false; } var els = document.querySelectorAll('button, a[href], input, select, textarea, [role=button]'); for (var i=0;i 392) && out.clipped.length < 6) out.clipped.push(id); } return JSON.stringify(out); })()""" FIELD44 = """(function(){ var out = {total:0, under:[]}; function skip(el){ for (var n = el; n; n = n.parentElement){ var cs = getComputedStyle(n); if (cs.position === 'fixed' && cs.transform && cs.transform !== 'none') return true; if (n.getAttribute && n.getAttribute('aria-hidden') === 'true') return true; } return false; } var els = document.querySelectorAll( 'button, a[href], input:not([type=checkbox]):not([type=radio]), select, textarea'); for (var i=0;i= 7, shots) tmpdir = tempfile.mkdtemp(prefix="wpsuite-mobile-") db_path = os.path.join(tmpdir, "check.db") server = None browser = None try: tok = seed(db_path) set_sop(db_path, {}) port = cdp.free_port() base = "http://127.0.0.1:%d" % port server = start_server(port, db_path) browser = cdp.Browser(exe) page = browser.page() page.clear_cookies() page.set_cookie("wp_session", tok["root"]) page.viewport(390, 844, mobile=True) for name, path in PAGES: print("\n%s" % name) page.goto(base + path) dismiss_dialogs(page) time.sleep(2.4) m = json.loads(page.eval(MEASURE)) chk("%s: no sideways scrolling" % name, m["sw"] <= 392, m["sw"]) chk("%s: no control clipped past the viewport" % name, not m["clipped"], ascii_(m["clipped"])) chk("%s: every control meets the 24px floor" % name, not m["under24"], ascii_(m["under24"])) # the gloved-hands bar: Field View, everything 44px print("\nfield view, the 44px bar") page.goto(base + "/field.html?project=projA") dismiss_dialogs(page) time.sleep(2.4) f = json.loads(page.eval(FIELD44)) chk("field view: every control is a 44px touch target", f["total"] > 0 and not f["under"], ascii_(f)) js_errors = [e for e in page.js_errors() if "beforeunload" not in e] chk("no JavaScript errors across the whole sweep", not js_errors, ascii_(js_errors[:2])) finally: if browser is not None: try: browser.close() except Exception: pass if server is not None: try: server.terminate() except Exception: pass print("\n" + "-" * 54) print("%d/%d checks passed." % (len(_PASS), len(_PASS) + len(_FAIL))) for f in _FAIL: print(" - " + f) return 1 if _FAIL else 0 if __name__ == "__main__": sys.exit(main())