Files
Project-SDE-WP-Suite/tests/kitting_check.py
n.siegfried 79c4a36c70 T8.1 - CR-009: kitting statuses are a set, and Micron EUV is not kitting
The statuses: the proposed five, adopted as proposed - Not Started, Picking,
Staged, In Transit, Delivered - as one named constant (KIT_STATUSES) building
the select. They describe fulfillment; the old four ('Open', 'In Progress',
'Kitted', 'Delivered') mixed fulfillment with workflow. A value stored before
the set existed is kept, selected, and shown as "(legacy)" - CR-016's rule
that renamed vocabularies must not orphan recorded data - and round-trips
through collect unchanged.

Micron EUV: the sample SOP is the Micron configuration on record (CR-016 /
T5.7), and it now names kitting:false beside assets:false - off by CR-006
TOGGLE, in both the creator sample and the wizard sample. The section leaves
the form, the rail and the export; its data and model stay exactly where they
are (the probe loads the example package under the Micron sample and finds
its kitting values intact through collect). Any other SOP turns the section
on and it works fully - driven against sopA with everything enabled.

sections_check re-pointed, not relaxed: its sample-map pin said "naming only
assets"; it now says "naming exactly assets (CR-016) and kitting (CR-009)" -
still refusing any section that goes off without a recorded item behind it.

Verification (each probe run alone): NEW tests/kitting_check.py 14/14.
Regression: sections_check 95/95.

Items: CR-009

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 11:55:39 -07:00

159 lines
6.7 KiB
Python

#!/usr/bin/env python3
"""Are kitting statuses a set, and is kitting off for Micron EUV? — CR-009, T8.1.
The statuses become an explicit five (Not Started / Picking / Staged /
In Transit / Delivered) - fulfillment states, not free text. A value stored
before the set existed is kept and shown as a legacy option: renamed sets must
not orphan recorded data. The Micron EUV configuration (the sample SOP, per
CR-016/T5.7) turns the section OFF through the CR-006 toggle; any other SOP
can turn it on and everything works; nothing is deleted either way.
Self-contained: throwaway SQLite, its own uvicorn, headless Edge or Chrome.
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
KIT = ["Not Started", "Picking", "Staged", "In Transit", "Delivered"]
def ascii_(v, n=280):
return re.sub(r"\s+", " ", str(v)).encode("ascii", "replace").decode()[:n]
def settle(seconds=0.5):
time.sleep(seconds)
def wait_creator(page, tries=40):
for _ in range(tries):
if page.eval("!!window.wpCreatorReady"):
return True
time.sleep(0.3)
return False
def kit_options(page):
return json.loads(page.eval(
"JSON.stringify([...document.getElementById('wp_kit_status').options]"
".map(o=>o.textContent))"))
def main():
exe = cdp.find_browser()
if not exe:
print("no headless-capable browser found; set WP_BROWSER.")
return 2
tmpdir = tempfile.mkdtemp(prefix="wpsuite-kit-")
db_path = os.path.join(tmpdir, "check.db")
server = None
browser = None
try:
tok = seed(db_path)
set_sop(db_path, {}) # sopA: every section ON, kitting included
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(1440, 900)
page.goto(base + "/wp-creation-index.html?project=projA")
dismiss_dialogs(page)
chk("the creator boots", wait_creator(page))
settle(1.6)
page.eval("window.alert=()=>{}; window.confirm=()=>false; window.prompt=()=>null;")
# ── 1. the set ────────────────────────────────────────────────────────
print("\n1. an explicit set, not free text")
opts = kit_options(page)
chk("the kitting status control is a select offering exactly the five states",
opts == [""] + KIT, ascii_(opts))
chk("...and the set is one named constant",
json.loads(page.eval("JSON.stringify(KIT_STATUSES)")) == KIT)
# a pre-CR-009 value survives as a legacy option, selected
page.eval("""loadPackageIntoForm({subject:'legacy kit', type:'Conduit Install',
kitStatus:'Kitted', kitOwner:'Paul Coonrod', constraints:[], holds:[]})""")
settle(0.8)
chk("a stored pre-CR-009 value is kept, selected, and marked legacy",
page.eval("document.getElementById('wp_kit_status').value") == "Kitted"
and any("legacy" in o for o in kit_options(page)), ascii_(kit_options(page)))
chk("...and it round-trips through collect unchanged - nothing re-written",
page.eval("collectPackage().kitStatus") == "Kitted")
page.eval("document.getElementById('wp_kit_status').value='Staged'")
chk("...and picking a new value works from the same control",
page.eval("collectPackage().kitStatus") == "Staged")
# ── 2. this SOP (kitting ON) has the section, working ─────────────────
print("\n2. an SOP with kitting on")
vis = page.eval("""(() => { const c=document.getElementById('mimo-card');
return c && !c.hidden && getComputedStyle(c).display !== 'none'; })()""")
chk("the Kitting & MIMO section is present and rendered", bool(vis))
chk("...and appears in the section rail", page.eval(
"!!document.querySelector(%s)" % json.dumps('.sec-rail-item[data-sec="mimo-card"]')))
# ── 3. Micron EUV (the sample): kitting OFF ───────────────────────────
print("\n3. Micron EUV: off, hidden, not deleted")
page.eval("loadSampleSOP()")
settle(1.0)
chk("the sample IS the Micron configuration and turns kitting off",
page.eval("SOP.sections && SOP.sections.kitting === false"))
hidden = page.eval("""(() => { const c=document.getElementById('mimo-card');
return c && (c.hidden || getComputedStyle(c).display === 'none'); })()""")
chk("the section is gone from the form", bool(hidden))
chk("...and from the rail", page.eval(
"!document.querySelector(%s)" % json.dumps('.sec-rail-item[data-sec="mimo-card"]')))
page.eval("loadExample()")
settle(1.0)
doc = page.eval("(() => { renderPackage(collectPackage()); "
"return document.getElementById('pkg-doc').innerHTML; })()")
chk("...and from the export", "Kitting & MIMO" not in doc)
chk("the example package's kitting DATA is still on the record - hidden, "
"never deleted",
page.eval("collectPackage().kitStatus") != ""
and bool(page.eval("collectPackage().kitOwner")), ascii_(
(page.eval("collectPackage().kitStatus"),
page.eval("collectPackage().kitOwner"))))
js_errors = [e for e in page.js_errors() if "beforeunload" not in e]
chk("no JavaScript errors anywhere in this run", 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())