#!/usr/bin/env python3 """Inline validation in the SOP wizard — S1, wizard half (T5.8). The defect S1 records has two halves and they are easy to conflate. One is that validation was a native dialog. The other is that it guarded three steps while the markup marked required fields on five — so two steps' asterisks meant nothing at all, which is worse than no asterisk. 1. every step with required fields validates them (1, 3, 5, 6, 7 — counted from the markup, not from the old guard) 2. each error renders at its field and is associated via aria-describedby 3. submitting an invalid step focuses and scrolls to the first error 4. errors announce to screen readers 5. the wizard's native dialog count is 0 Check 5 is measured two ways, because the wave 0 metric counts the word `alert(` inside a comment as readily as inside code (BL-017). Both figures are recorded: raw, and with comments stripped. Every dialog is stubbed before anything is driven, so a survivor is caught and reported rather than hanging the session. Exit 0 all passed, 1 a failure, 2 could not run. """ import json import os import re import subprocess 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, _c # noqa: E402 ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) STUB = """ window.__dialogs = []; window.alert = function (m) { window.__dialogs.push(['alert', String(m)]); }; window.confirm = function (m) { window.__dialogs.push(['confirm', String(m)]); return false; }; window.prompt = function (m) { window.__dialogs.push(['prompt', String(m)]); return null; }; true """ # Read out of the markup, not out of the app's gate table: the S1 defect is # precisely that the two disagreed, so a probe that read the table would agree # with whatever the table says and prove nothing. STEP_FIELDS = { 1: ["proj_name", "proj_number", "proj_client", "proj_division", "proj_site"], 3: ["role_super_title", "role_foreman_title"], 5: ["gov_woformat", "gov_discmode"], 6: ["qual_qcreq"], 7: ["plat_tracking", "plat_commissioning"], } NO_REQUIRED = [2, 4, 8, 9, 10, 11, 12] def settle(seconds=1.4): time.sleep(seconds) def ascii_(v): """Windows consoles are cp1252, and a failure message carrying the toast's close glyph crashes the reporter instead of reporting the failure. Losing the glyph costs nothing; losing the diagnosis costs the run.""" return str(v).encode("ascii", "replace").decode("ascii") def open_wizard(page, base, tok, query="?project=projA"): page.clear_cookies() page.set_cookie("wp_session", tok["root"]) page.goto(base + "/work-package-suite.html" + query) settle(2.0) page.eval(STUB) def required_fields_in_markup(): """Which steps mark a field required, straight from work-package-suite.html. Two notations: an asterisk in the