#!/usr/bin/env python3 """Does each of F1-F6 still reproduce? Measured in a browser, not read from source. T0.2 has to record reproduces / does not reproduce for every rendering defect before wave 1 starts fixing them, and waves 1-3 have to prove each one stopped. Both halves are the same measurement, so this is one script that reports either way: REPRODUCES the defect is present -- correct before its fix lands FIXED the defect is gone -- correct after INCONCLUSIVE the probe could not decide; never silently a pass python tests/f_items.py # all six python tests/f_items.py F2 F5 # a subset, e.g. after one task Self-contained on the same terms as tests/browser_check.py, whose seed() and start_server() it reuses: throwaway SQLite, its own uvicorn, headless Edge or Chrome, all torn down afterwards. Your real database is never touched. Each probe states the threshold it applies, because "the drawer is off-screen" and "the fields look disabled" are judgements a screenshot cannot settle in CI. Where the review gave a number, that number is the threshold. Exit codes: 0 ran to completion (read the report) - 2 could not run. """ import argparse import json import os import shutil 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 # noqa: E402 # Project names and page copy contain em dashes and other non-cp1252 characters, and # the default Windows console encoding raises UnicodeEncodeError on them mid-run. try: sys.stdout.reconfigure(encoding="utf-8", errors="replace") except (AttributeError, ValueError): # pragma: no cover pass REPRO, FIXED, UNKNOWN = "REPRODUCES", "FIXED", "INCONCLUSIVE" _RESULTS = [] # F3 only breaks with a real project name. "Micron EUV Cleanroom Enable 2667008" is # the one the review used and the one that overflows the header. LONG_ID = "projLong" LONG_NAME = "Micron EUV Cleanroom Enable 2667008" def add_long_project(db_path): """Add the long-named project to the seeded database, before the server starts. It has to exist in the database rather than be faked in localStorage: every page reached with ?project= re-pulls the project from the server, which would overwrite a local fake and quietly measure the short name instead -- a false FIXED.""" os.environ["DATABASE_URL"] = "sqlite:///" + db_path.replace("\\", "/") from server.db import SessionLocal from server import models with SessionLocal() as db: db.add(models.Project(id=LONG_ID, name=LONG_NAME, number="2667008", client="Micron")) db.flush() db.add(models.ProjectMember(id="pmLong", user_id="user_root", project_id=LONG_ID, role="")) db.commit() def _c(s, code): return f"\033[{code}m{s}\033[0m" if sys.stdout.isatty() else s def report(item, verdict, detail): colour = {REPRO: "31", FIXED: "32", UNKNOWN: "33"}[verdict] _RESULTS.append((item, verdict, detail)) print(f" {item:3s} {_c(verdict, colour):22s} {detail}") def rect(page, sel): """Bounding box of the first match, or None. Zero-size counts as absent.""" got = page.eval( f"(function(){{var e=document.querySelector({sel!r});if(!e)return 'null';" "var r=e.getBoundingClientRect();" "return JSON.stringify({x:r.x,y:r.y,w:r.width,h:r.height," "right:r.right,bottom:r.bottom,text:(e.textContent||'').trim().slice(0,60)});})()") if not got or got == "null": return None r = json.loads(got) return None if r["w"] == 0 and r["h"] == 0 else r # ── F1 ──────────────────────────────────────────────────────────────────────── def f1(page, base, tok): """App bar stale while the page body shows the active project. Driven through the real control: land on the launcher with storage cleared, pick a project from the picker, and read both labels without reloading. That is the interaction the review describes -- the hero and the picker update, the app bar does not, because wp-chrome.js renders projectLabel() once and only refreshes it on the /api/projects callback, never on the selection itself. Priming localStorage before load would hide this, so it is cleared first. Updated at T5.2: this drove the launcher's project-picker