check-no-js-page.py
tools/check-no-js-page.py
#!/usr/bin/env python3
import sys
from playwright.sync_api import sync_playwright
url = sys.argv[1] if len(sys.argv) > 1 else None
if not url:
print("usage: check-no-js-page.py <url>", file=sys.stderr); sys.exit(2)
fail = []
with sync_playwright() as p:
browser = p.chromium.launch()
# --- JS ENABLED: framework loads, no console errors, no failed requests ---
ctx = browser.new_context()
page = ctx.new_page()
errors, failed = [], []
page.on("console", lambda m: errors.append(m.text) if m.type == "error" else None)
page.on("requestfailed", lambda r: failed.append(r.url))
page.goto(url, wait_until="networkidle")
has_space = page.evaluate("() => typeof window._018Space === 'object'")
dup_controls = page.evaluate(
"() => [...document.scripts].filter(s => s.src.includes('/tools/controls.js')).length")
if errors: fail.append(f"JS-on console errors: {errors}")
if failed: fail.append(f"JS-on failed requests: {failed}")
if not has_space: fail.append("JS-on: window._018Space missing")
if dup_controls > 1: fail.append(f"JS-on: controls.js loaded {dup_controls}x")
ctx.close()
# --- JS DISABLED: static menu present + search disabled ---
ctx = browser.new_context(java_script_enabled=False)
page = ctx.new_page()
page.goto(url, wait_until="load")
links = page.locator("#file-list a").count()
search_disabled = page.locator(".menu-search input[disabled]").count()
loupe = page.locator(".meta-loupe").count()
if links < 50: fail.append(f"JS-off: too few menu links: {links}")
if search_disabled != 1: fail.append("JS-off: search not disabled")
if loupe != 1: fail.append("JS-off: loupe missing")
ctx.close()
browser.close()
for f in fail:
print(" -", f, file=sys.stderr)
print("FAIL" if fail else "PASS", url)
sys.exit(1 if fail else 0)