"""Headless test for CONTROLS.html's bindings-loader. Builds a copy of the page with a self-test harness injected inside its IIFE, feeds it the fixture (a REAL board-2 migrated bindings.txt: the full defaults plus two player-authored rows and a wizard section), and writes the result to %TEMP%\controls_test.html. Run it under headless Chrome and read the PASS/FAIL lines out of the dumped DOM's
:

    python scratchpad/test_controls_page.py
    chrome --headless=new --disable-gpu --dump-dom file:///%TEMP%/controls_test.html

Asserts: player-authored rows are carried and marked custom, stock rows stay
unmarked, the PgUp/PgDn and backtick built-ins survive unbound keys, wizard
rows land in the extras list, and reset restores the stock board exactly.
Verified 11/11 on 2026-07-26.
"""
import io
import json
import os
import tempfile

SCRATCH = tempfile.gettempdir()

frag = io.open(r"C:\git\bt411\docs\CONTROLS.html", encoding="utf-8").read()
sample = io.open(r"C:\git\bt411\scratchpad\fixture_migrated_bindings.txt",
                 encoding="utf-8", errors="replace").read()

harness = r"""
    // ---------- INJECTED SELF-TEST (test copy only) ----------
    try {
      var SAMPLE = __SAMPLE__;
      applyFile(SAMPLE, "migrated-test");
      var out = [];
      function t(label, ok) { out.push((ok ? "PASS" : "FAIL") + " " + label); }
      function elOf(n) { return keyEls[n]; }
      function subOf(n) {
        var s = elOf(n) && elOf(n).querySelector(".k-sub");
        return s ? s.textContent : "";
      }
      t("T carries the custom button (VALVE face from 0x2B)",
        subOf("T") === "VALVE" && elOf("T").classList.contains("custom"));
      t("Q carries the tweaked throttle (THR +) and is custom",
        subOf("Q") === "THR +" && elOf("Q").classList.contains("custom"));
      t("W matches stock (VALVE, not custom)",
        subOf("W") === "VALVE" && !elOf("W").classList.contains("custom"));
      t("PgUp built-in volume survives an unbound key",
        subOf("PAGEUP") === "VOL +"
        && !elOf("PAGEUP").classList.contains("custom"));
      t("backtick view built-in survives", subOf("BACKTICK") === "VIEW");
      t("extras list shows the wizard rows",
        document.getElementById("load-extras").style.display === "block"
        && document.getElementById("load-extras")
             .textContent.indexOf("joyaxis") >= 0);
      t("status reports a load",
        document.getElementById("load-status")
          .textContent.indexOf("differ from stock") >= 0);
      var stray = 0, strayList = [];
      Object.keys(keyEls).forEach(function (n) {
        if (n !== "T" && n !== "Q"
            && keyEls[n].classList.contains("custom")) {
          stray += 1;
          var sEl = keyEls[n].querySelector(".k-sub");
          strayList.push(n + "[sub='" + (sEl ? sEl.textContent : "")
            + "' stockSub='" + stock[n].sub + "' addr='"
            + keyEls[n].getAttribute("data-addr") + "' stockAddr='"
            + stock[n].addr + "']");
        }
      });
      t("no stray custom marks (only T and Q); strays: "
        + strayList.join(" "), stray === 0);
      resetBoard();
      t("reset restores W", subOf("W") === "VALVE");
      t("reset restores Q to stock VALVE", subOf("Q") === "VALVE");
      t("reset clears custom marks",
        document.querySelectorAll(".key.custom").length === 0);
      var pre = document.createElement("pre");
      pre.id = "test-out";
      pre.textContent = out.join("\n");
      document.body.appendChild(pre);
    } catch (err) {
      var pre2 = document.createElement("pre");
      pre2.id = "test-out";
      pre2.textContent = "HARNESS ERROR: " + err + " :: " + (err && err.stack);
      document.body.appendChild(pre2);
    }
"""

harness = harness.replace("__SAMPLE__", json.dumps(sample))
closer = "  })();\n"
assert closer in frag, "closer not found"
test = frag.replace(closer, harness + closer, 1)

page = ("\n\n\n\n"
        "test\n\n\n" + test + "\n\n\n")
out = os.path.join(SCRATCH, "controls_test.html")
io.open(out, "w", encoding="utf-8").write(page)
print("test page written:", len(page), "chars")