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>
159 lines
6.7 KiB
Python
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())
|