import importlib.util
import json
from pathlib import Path

ROOT = Path(__file__).parents[2]
SPEC = importlib.util.spec_from_file_location("theme_ci", ROOT / "tools" / "theme_ci.py")
ci = importlib.util.module_from_spec(SPEC)
assert SPEC.loader
SPEC.loader.exec_module(ci)


def test_ledger_and_contracts_are_valid():
    summary = ci.check_ledger()
    assert summary["tests"] >= 167
    contracts = ci.check_contracts()
    assert "united-pets" in contracts["themes"]


def test_profile_selection_is_specific_not_blanket():
    up = ci.plan("united-pets")
    assert "TF-EDITOR-001" in up["tests"]
    assert "TF-INSTALL-001" in up["tests"]
    assert "TF-WOO-001" not in up["tests"]
    assert "TF-EL-001" not in up["tests"]
    assert "TF-FSE-002" not in up["tests"]

    kidearn = ci.plan("Kidearn")
    assert "TF-WOO-001" in kidearn["tests"]
    assert "TF-EL-001" not in kidearn["tests"]

    hap = ci.plan("hapantheon")
    assert "TF-FSE-002" in hap["tests"]
    assert "TF-WOO-001" not in hap["tests"]


def test_runtime_shards_are_nonempty_disjoint_and_complete():
    plan = ci.plan("united-pets")
    rows = {r["id"]: r for r in ci.load_ledger()}
    expected = {
        tid
        for tid in plan["tests"]
        if rows[tid]["runner"] == "runtime" and rows[tid]["phase"] in {"runtime", "release", "scheduled"}
    }
    shards = ci.build_shards("united-pets", max_shards=6)
    assert shards
    assert all(s["tests"] for s in shards)
    ids = [tid for shard in shards for tid in shard["tests"]]
    assert len(ids) == len(set(ids))
    assert set(ids) == expected
    assert [s["shard_id"] for s in shards] == [f"s{i:02d}" for i in range(1, len(shards) + 1)]


def _receipt(theme, tree, shard_id, ids, status="passed", repo=False):
    return {
        "schema": 1,
        "theme": "*" if repo else theme,
        "scope": "repo" if repo else "theme",
        "shard_id": shard_id,
        "tree": "" if repo else tree,
        "expected_ids": ids,
        "executed_ids": ids,
        "results": [{"id": x, "status": status} for x in ids],
        "artifact": {} if repo else {"sha256": "a" * 64, "freshInstall": True},
        "environment": {} if repo else {"engine": "docker", "images": ci.runtime_environment_contract()},
    }


def test_aggregate_fails_when_expected_test_is_missing(tmp_path):
    plan = ci.plan("united-pets")
    rows = {r["id"]: r for r in ci.load_ledger()}
    aggregate_ids = {tid for tid in plan["tests"] if rows[tid]["phase"] == "aggregate"}
    repo_ids = [tid for tid in plan["tests"] if rows[tid].get("scope") == "repo" and tid not in aggregate_ids]
    theme_ids = [tid for tid in plan["tests"] if rows[tid].get("scope") != "repo" and tid not in aggregate_ids]
    assert theme_ids
    missing = theme_ids.pop()
    p = tmp_path / "plan.json"
    p.write_text(json.dumps(plan))
    receipts = tmp_path / "receipts"
    receipts.mkdir()
    (receipts / "repo.json").write_text(json.dumps(_receipt("united-pets", plan["tree"], "repo", repo_ids, repo=True)))
    (receipts / "theme.json").write_text(json.dumps(_receipt("united-pets", plan["tree"], "theme", theme_ids)))
    try:
        ci.aggregate("united-pets", p, receipts, tmp_path / "out.json")
    except ci.VerificationError as exc:
        assert missing in str(exc)
    else:
        raise AssertionError("aggregate accepted a missing expected test")


def test_aggregate_fails_when_required_test_is_red(tmp_path):
    plan = ci.plan("united-pets")
    rows = {r["id"]: r for r in ci.load_ledger()}
    aggregate_ids = {tid for tid in plan["tests"] if rows[tid]["phase"] == "aggregate"}
    repo_ids = [tid for tid in plan["tests"] if rows[tid].get("scope") == "repo" and tid not in aggregate_ids]
    theme_ids = [tid for tid in plan["tests"] if rows[tid].get("scope") != "repo" and tid not in aggregate_ids]
    p = tmp_path / "plan.json"
    p.write_text(json.dumps(plan))
    receipts = tmp_path / "receipts"
    receipts.mkdir()
    (receipts / "repo.json").write_text(json.dumps(_receipt("united-pets", plan["tree"], "repo", repo_ids, repo=True)))
    bad = _receipt("united-pets", plan["tree"], "theme", theme_ids)
    bad["results"][0]["status"] = "failed"
    (receipts / "theme.json").write_text(json.dumps(bad))
    try:
        ci.aggregate("united-pets", p, receipts, tmp_path / "out.json")
    except ci.VerificationError as exc:
        assert theme_ids[0] in str(exc)
    else:
        raise AssertionError("aggregate accepted a failed required test")


def test_aggregate_rejects_runtime_environment_identity_mismatch(tmp_path):
    plan = ci.plan("united-pets")
    rows = {r["id"]: r for r in ci.load_ledger()}
    aggregate_ids = {tid for tid in plan["tests"] if rows[tid]["phase"] == "aggregate"}
    theme_ids = [tid for tid in plan["tests"] if rows[tid].get("scope") != "repo" and tid not in aggregate_ids]
    assert "TF-REL-001" in aggregate_ids
    p = tmp_path / "plan.json"
    p.write_text(json.dumps(plan))
    receipts = tmp_path / "receipts"
    receipts.mkdir()
    receipt = _receipt("united-pets", plan["tree"], "theme", theme_ids)
    receipt["environment"]["images"]["wordpress_image"] = "docker.io/library/wordpress@sha256:" + "0" * 64
    (receipts / "theme.json").write_text(json.dumps(receipt))
    out = tmp_path / "out.json"
    try:
        ci.aggregate("united-pets", p, receipts, out)
    except ci.VerificationError as exc:
        assert "TF-REL-001" in str(exc)
        result = json.loads(out.read_text())
        rel = next(item for item in result["aggregate_results"] if item["id"] == "TF-REL-001")
        assert rel["status"] == "failed"
        assert "runtime environment does not match pinned policy" in rel["error"]
    else:
        raise AssertionError("aggregate accepted mismatched runtime environment identity")


def test_changed_theme_detector_does_not_treat_contract_only_bootstrap_as_source_change(monkeypatch):
    # This is the one-time migration behavior: adding theme-test.yaml alone does not assert the old theme tree changed.
    class CP:
        returncode = 0
        stdout = "themes/united-pets/theme-test.yaml\nci/policy.yaml\n"
        stderr = ""
    monkeypatch.setattr(ci.subprocess, "run", lambda *a, **k: CP())
    got = ci.changed_themes("base", "head")
    assert got["themes"] == []
    assert got["infra_changed"] is True



def test_runtime_fixture_images_are_digest_pinned():
    env = ci.runtime_environment_contract()
    assert set(env) == {"wordpress_image", "wp_cli_image", "database_image", "selenium_image", "reference_image"}
    assert all("@sha256:" in ref for ref in env.values())


def test_every_ledger_row_has_executable_implementation():
    coverage = ci.implementation_coverage()
    assert coverage["tests"] >= 167
    assert coverage["holes"] == []
    assert sum(coverage["authorities"].values()) == coverage["tests"]

def test_runtime_registry_covers_every_runtime_ledger_id():
    spec = importlib.util.spec_from_file_location("runtime_harness", ROOT / "tests" / "runtime" / "runtime_harness.py")
    runtime = importlib.util.module_from_spec(spec)
    assert spec.loader
    import sys
    sys.modules[spec.name] = runtime
    spec.loader.exec_module(runtime)
    runtime.self_check_registry()



def test_verify_theme_uses_one_canonical_plan_static_runtime_aggregate_path(tmp_path, monkeypatch):
    tree = "a" * 64
    fake_plan = {
        "schema": 1,
        "theme": "united-pets",
        "mode": "pr",
        "architecture": "gutenberg-hybrid",
        "workflow": "enhancement",
        "profiles": ["core", "gutenberg-hybrid"],
        "tests": ["TF-RUNTIME-001"],
        "phases": {"runtime": ["TF-RUNTIME-001"]},
        "expected": 1,
        "tree": tree,
        "contract_sha256": "b" * 64,
        "ledger_sha256": "c" * 64,
    }
    monkeypatch.setattr(ci, "plan", lambda theme, mode="pr": fake_plan)
    monkeypatch.setattr(
        ci,
        "build_shards",
        lambda theme, mode="pr": [{"shard_id": "s01", "theme": theme, "groups": ["frontend"], "tests": ["TF-RUNTIME-001"], "weight": 5}],
    )

    real_run = ci.subprocess.run

    def fake_run(cmd, cwd=None, env=None, check=False, **kwargs):
        if str(ROOT / "tests" / "runtime" / "runtime_harness.py") in cmd:
            out = Path(cmd[cmd.index("--out") + 1])
            out.parent.mkdir(parents=True, exist_ok=True)
            out.write_text(
                json.dumps(
                    {
                        "schema": 1,
                        "theme": "united-pets",
                        "runner": "runtime",
                        "shard_id": "s01",
                        "tree": tree,
                        "expected_ids": ["TF-RUNTIME-001"],
                        "executed_ids": ["TF-RUNTIME-001"],
                        "results": [{"id": "TF-RUNTIME-001", "status": "passed"}],
                        "artifact": {"sha256": "d" * 64, "freshInstall": True},
                        "environment": {"engine": "docker", "images": ci.runtime_environment_contract()},
                        "status": "passed",
                    }
                )
            )
            class CP:
                returncode = 0
            return CP()
        return real_run(cmd, cwd=cwd, env=env, check=check, **kwargs)

    monkeypatch.setattr(ci.subprocess, "run", fake_run)
    result = ci.verify_theme("united-pets", "pr", tmp_path / "verify", "docker")
    assert result["result"] == "PASS"
    assert result["executed"] == 1
    assert (tmp_path / "verify" / "final.json").is_file()


def test_non_shipping_theme_metadata_is_excluded_but_real_theme_files_are_not(tmp_path):
    (tmp_path / "style.css").write_text("/* Theme Name: Canary */\n")
    (tmp_path / "index.php").write_text("<?php")
    (tmp_path / "theme-test.yaml").write_text("{\"schema\":1}\n")
    tree = ci.source_tree(tmp_path)
    assert "theme-test.yaml" not in tree
    assert set(tree) == {"style.css", "index.php"}


def test_editability_contract_must_name_real_implementation_identities(monkeypatch):
    monkeypatch.setattr(ci, "combined_text", lambda theme: "attributes section values postType title count")
    ci.static_editable_coverage(
        "canary",
        {"editable_fields": {"source-section": ["section", "values"], "query-cards": ["postType", "title", "count"]}},
    )
    try:
        ci.static_editable_coverage("canary", {"editable_fields": {"source-section": ["section", "textOverrides"]}})
    except ci.VerificationError as exc:
        assert "source-section.textOverrides" in str(exc)
    else:
        raise AssertionError("nonexistent editable field identity was accepted")


def test_runtime_candidate_zip_excludes_only_declared_non_shipping_metadata():
    import importlib.util
    import shutil
    import sys
    import tempfile
    import zipfile

    spec = importlib.util.spec_from_file_location("runtime_package_canary", ROOT / "tests" / "runtime" / "runtime_harness.py")
    runtime = importlib.util.module_from_spec(spec)
    assert spec.loader
    sys.modules[spec.name] = runtime
    spec.loader.exec_module(runtime)
    slug = "__tf-package-canary"
    theme = ROOT / "themes" / slug
    shutil.rmtree(theme, ignore_errors=True)
    theme.mkdir()
    try:
        (theme / "style.css").write_text("/*\nTheme Name: Package Canary\nVersion: 1.0.0\n*/\n")
        (theme / "index.php").write_text("<?php echo 'canary';\n")
        (theme / "theme-test.yaml").write_text("{\"schema\":1}\n")
        ctx = runtime.RuntimeContext(slug, {"schema": 1}, ["TF-RUNTIME-001"], engine="docker")
        ctx.work = Path(tempfile.mkdtemp(prefix="tf-package-unit-"))
        try:
            artifact = ctx._candidate_zip()
            with zipfile.ZipFile(artifact) as zf:
                names = set(zf.namelist())
            assert f"{slug}/theme-test.yaml" not in names
            assert f"{slug}/style.css" in names
            assert f"{slug}/index.php" in names
        finally:
            shutil.rmtree(ctx.work, ignore_errors=True)
    finally:
        shutil.rmtree(theme, ignore_errors=True)


def _load_runtime_module(name: str):
    import sys
    spec = importlib.util.spec_from_file_location(name, ROOT / "tests" / "runtime" / "runtime_harness.py")
    runtime = importlib.util.module_from_spec(spec)
    assert spec.loader
    sys.modules[spec.name] = runtime
    spec.loader.exec_module(runtime)
    return runtime


def test_wp_cli_data_output_never_includes_stderr(monkeypatch):
    runtime = _load_runtime_module("runtime_wp_stderr_canary")

    class CP:
        returncode = 0
        stdout = '{"ok":true}\n'
        stderr = 'PHP Warning: harmless diagnostic on stderr\n'

    monkeypatch.setattr(runtime.subprocess, "run", lambda *a, **k: CP())
    ctx = runtime.RuntimeContext("canary", {"schema": 1}, ["TF-RUNTIME-001"], engine="docker")
    ctx.network = "canary-net"
    ctx.volume = "canary-volume"
    assert ctx.wp_eval("echo wp_json_encode(['ok'=>true]);") == '{"ok":true}'


def test_demo_import_navigation_uses_authenticated_admin_dom_not_wp_cli(monkeypatch):
    runtime = _load_runtime_module("runtime_import_nav_canary")

    class Browser:
        def __init__(self):
            self.navigated = []
            self.executed = []
            self.waited = []
        def navigate(self, url):
            self.navigated.append(url)
        def wait_js(self, expression, timeout=20, interval=0.2):
            self.waited.append(expression)
            return True
        def execute(self, script, args=None):
            self.executed.append(script)
            if "#menu-appearance" in script:
                return True
            if "const f=e.closest('form')" in script:
                return {"found": True, "form": True, "action": "admin-post.php"}
            if "__themefactoryImportMarker=arguments[0]" in script:
                return True
            if "e.click()" in script:
                return True
            return True

    ctx = runtime.RuntimeContext(
        "canary",
        {"schema": 1, "features": {"demo_import": True}, "demo_import": {"location": "appearance"}},
        ["TF-DEMO-004"],
        engine="docker",
    )
    ctx.browser = Browser()
    ctx.browser_base = "http://canary-wp"
    monkeypatch.setattr(ctx, "login_admin", lambda: None)
    monkeypatch.setattr(ctx, "wp_eval", lambda *a, **k: (_ for _ in ()).throw(AssertionError("wp_eval must not discover admin menus")))
    monkeypatch.setattr(ctx, "_discover_post_types", lambda: None)
    monkeypatch.setattr(ctx, "discover_routes", lambda: ["/"])
    ctx.import_demo_browser()
    assert ctx.imported is True
    assert "http://canary-wp/wp-admin/" in ctx.browser.navigated
    assert any("#menu-appearance" in script for script in ctx.browser.executed)
    marker_waits = [expr for expr in ctx.browser.waited if "__themefactoryImportMarker" in expr]
    assert len(marker_waits) == 1
    assert ctx.browser.waited.index(marker_waits[0]) < max(i for i, expr in enumerate(ctx.browser.waited) if expr == "document.readyState==='complete'")


def _load_safe_merge_module():
    spec = importlib.util.spec_from_file_location("theme_safe_merge_test", ROOT / "tools" / "theme_safe_merge.py")
    mod = importlib.util.module_from_spec(spec)
    assert spec.loader
    spec.loader.exec_module(mod)
    return mod


def test_safe_merge_requires_exact_successful_full_gate():
    mod = _load_safe_merge_module()
    head = "a" * 40
    pr = {
        "state": "OPEN",
        "isDraft": False,
        "baseRefName": "main",
        "headRefOid": head,
        "statusCheckRollup": [
            {"name": "Plan verification", "workflowName": "ThemeFactory Verify", "status": "COMPLETED", "conclusion": "SUCCESS"},
            {"name": "Runtime verifier RED/GREEN canary", "workflowName": "ThemeFactory Verify", "status": "COMPLETED", "conclusion": "SUCCESS"},
            {"name": "Theme · ${{ matrix.theme }}", "workflowName": "ThemeFactory Verify", "status": "COMPLETED", "conclusion": "SKIPPED"},
            {"name": "Full gate", "workflowName": "ThemeFactory Verify", "status": "COMPLETED", "conclusion": "SUCCESS"},
        ],
    }
    mod.validate_pr_gate(pr, head)
    for mutation in (
        {"headRefOid": "b" * 40},
        {"isDraft": True},
        {"baseRefName": "develop"},
    ):
        bad = dict(pr); bad.update(mutation)
        try:
            mod.validate_pr_gate(bad, head)
        except mod.MergeGateError:
            pass
        else:
            raise AssertionError(f"safe merge accepted invalid PR state: {mutation}")

    bad = dict(pr)
    bad["statusCheckRollup"] = [dict(x) for x in pr["statusCheckRollup"]]
    bad["statusCheckRollup"][-1]["conclusion"] = "FAILURE"
    try:
        mod.validate_pr_gate(bad, head)
    except mod.MergeGateError as exc:
        assert "Full gate is not successful" in str(exc)
    else:
        raise AssertionError("safe merge accepted failed Full gate")


def test_safe_merge_script_is_executable():
    path = ROOT / "tools" / "theme-safe-merge"
    assert path.is_file()
    assert path.stat().st_mode & 0o111



def test_content_gate_establishes_declared_demo_state_once(monkeypatch):
    runtime = _load_runtime_module("runtime_demo_state_canary")
    ctx = runtime.RuntimeContext("canary", {"schema": 1, "features": {"demo_import": True}}, ["TF-UI-001"], engine="docker")
    calls = []
    def do_import():
        calls.append("import")
        ctx.imported = True
    monkeypatch.setattr(ctx, "import_demo_browser", do_import)
    runtime.ensure_demo_content(ctx)
    runtime.ensure_demo_content(ctx)
    assert calls == ["import"]





def test_runtime_image_acquisition_pulls_missing_digest_with_dedicated_timeout(monkeypatch):
    runtime = _load_runtime_module("runtime_image_acquisition_canary")
    ctx = runtime.RuntimeContext("canary", {"schema": 1}, ["TF-RUNTIME-001"], engine="docker")
    image = runtime.POLICY["runtime"]["selenium_image"]
    calls = []
    returncodes = iter([1, 0, 0])

    class CP:
        def __init__(self, returncode):
            self.returncode = returncode
            self.stdout = ""

    def fake_cmd(*args, **kwargs):
        calls.append((args, kwargs))
        return CP(next(returncodes))

    monkeypatch.setattr(ctx, "cmd", fake_cmd)
    ctx.ensure_image(image)
    assert calls[0][0] == ("image", "inspect", image)
    assert calls[1][0] == ("pull", image)
    assert calls[1][1]["timeout"] == 600
    assert calls[2][0] == ("image", "inspect", image)


def test_runtime_image_acquisition_skips_pull_when_cached(monkeypatch):
    runtime = _load_runtime_module("runtime_image_cache_canary")
    ctx = runtime.RuntimeContext("canary", {"schema": 1}, ["TF-RUNTIME-001"], engine="docker")
    image = runtime.POLICY["runtime"]["selenium_image"]
    calls = []

    class CP:
        returncode = 0
        stdout = ""

    def fake_cmd(*args, **kwargs):
        calls.append((args, kwargs))
        return CP()

    monkeypatch.setattr(ctx, "cmd", fake_cmd)
    ctx.ensure_image(image)
    assert [args for args, _ in calls] == [("image", "inspect", image)]


def test_http_status_discards_binary_body_instead_of_text_decoding_it(monkeypatch):
    runtime = _load_runtime_module("runtime_http_status_canary")
    ctx = runtime.RuntimeContext("canary", {"schema": 1}, ["TF-RUNTIME-006"], engine="docker")
    ctx.wp_name = "wp"
    ctx.base_url = "http://wp"
    calls = []

    class CP:
        stdout = "200"
        returncode = 0

    def fake_cmd(*args, **kwargs):
        calls.append((args, kwargs))
        return CP()

    monkeypatch.setattr(ctx, "cmd", fake_cmd)
    assert ctx.http_status("/image.png") == 200
    args = calls[0][0]
    assert "-o" in args and "/dev/null" in args
    assert "%{http_code}" in args
    assert "http://wp/image.png" in args


def test_image_gate_settles_lazy_images_before_declaring_failure():
    runtime = _load_runtime_module("runtime_lazy_image_canary")

    class Browser:
        def __init__(self):
            self.async_scripts = []
        def set_window(self, width, height):
            pass
        def navigate(self, url):
            pass
        def wait_js(self, expression, timeout=20, interval=0.2):
            return True
        def execute_async(self, script, args=None):
            self.async_scripts.append(script)
            return {"checked": 2, "backgrounds": ["http://canary/bg.jpg"], "broken": []}

    ctx = runtime.RuntimeContext("canary", {"schema": 1}, ["TF-RUNTIME-006"], engine="docker")
    ctx.browser = Browser()
    ctx.browser_base = "http://canary"
    ctx.routes = ["/"]
    ctx.http_status = lambda path, **kwargs: 200
    result = runtime.t_images(ctx)
    assert result == {"broken_images": 0, "checked_images": 4, "checked_background_urls": 1}
    assert len(ctx.browser.async_scripts) == 2
    assert all("scrollIntoView" in script and "naturalWidth" in script and "backgroundImage" in script for script in ctx.browser.async_scripts)


def test_performance_network_failures_use_real_same_origin_status_evidence():
    runtime = _load_runtime_module("runtime_network_log_canary")
    def entry(method, params):
        return {"message": json.dumps({"message": {"method": method, "params": params}})}
    entries = [
        entry("Network.requestWillBeSent", {"requestId": "1", "request": {"url": "http://wp.test/style.css"}}),
        entry("Network.responseReceived", {"requestId": "1", "response": {"url": "http://wp.test/style.css", "status": 200}}),
        entry("Network.requestWillBeSent", {"requestId": "2", "request": {"url": "http://wp.test/missing.js"}}),
        entry("Network.responseReceived", {"requestId": "2", "response": {"url": "http://wp.test/missing.js", "status": 404}}),
        entry("Network.requestWillBeSent", {"requestId": "3", "request": {"url": "https://cdn.example.invalid/x.js"}}),
        entry("Network.responseReceived", {"requestId": "3", "response": {"url": "https://cdn.example.invalid/x.js", "status": 500}}),
        entry("Network.requestWillBeSent", {"requestId": "4", "request": {"url": "http://wp.test/broken.css"}}),
        entry("Network.loadingFailed", {"requestId": "4", "errorText": "net::ERR_FAILED"}),
    ]
    failures, seen = runtime.performance_network_failures(entries, "wp.test")
    assert seen == 3
    assert [(x["kind"], x["url"]) for x in failures] == [
        ("response", "http://wp.test/missing.js"),
        ("loadingFailed", "http://wp.test/broken.css"),
    ]


def test_runtime_cleanup_removes_reference_server(monkeypatch):
    runtime = _load_runtime_module("runtime_reference_cleanup_canary")
    ctx = runtime.RuntimeContext("canary", {"schema": 1}, ["TF-CONV-001"], engine="docker")
    ctx.reference_name = "tf-canary-ref"
    calls = []
    monkeypatch.setattr(ctx, "cmd", lambda *args, **kwargs: calls.append(args))
    ctx.cleanup()
    assert ("rm", "-f", "tf-canary-ref") in calls


def test_shell_check_uses_contract_selectors():
    runtime = _load_runtime_module("runtime_shell_contract_canary")
    class Browser:
        def __init__(self): self.selectors=[]
        def set_window(self, width, height): pass
        def navigate(self, url): pass
        def wait_js(self, expression, timeout=20, interval=0.2): return True
        def execute(self, script, args=None):
            self.selectors.append(tuple(args or []))
            return [1, 1]
    ctx = runtime.RuntimeContext("canary", {"schema": 1, "shell": {"header_selector": "#masthead", "footer_selector": "body > #colophon"}}, ["TF-RUNTIME-008"], engine="docker")
    ctx.browser = Browser(); ctx.browser_base = "http://canary"; ctx.routes = ["/"]
    result = runtime.t_shell(ctx)
    assert result["header_selector"] == "#masthead"
    assert result["footer_selector"] == "body > #colophon"
    assert ctx.browser.selectors == [("#masthead", "body > #colophon"), ("#masthead", "body > #colophon")]




def test_default_ui_viewports_cover_breakpoint_classes_without_duplicate_phone_samples():
    runtime = _load_runtime_module("runtime_ui_viewports_canary")
    assert runtime.POLICY["default_viewports"] == [320, 390, 480, 768, 1024, 1440, 1920]


def test_ui_batch_shares_one_route_viewport_traversal():
    runtime = _load_runtime_module("runtime_ui_batch_canary")

    class Browser:
        def __init__(self):
            self.navigations = []
            self.executes = 0
        def set_window(self, width, height):
            pass
        def navigate(self, url):
            self.navigations.append(url)
        def wait_js(self, expression, timeout=20, interval=0.2):
            return True
        def execute(self, script, args=None):
            self.executes += 1
            return []

    ids = ["TF-UI-001", "TF-UI-002", "TF-UI-008"]
    ctx = runtime.RuntimeContext("canary", {"schema": 1, "runtime": {"viewports": [320, 480]}}, ids, engine="docker")
    ctx.browser = Browser()
    ctx.browser_base = "http://canary"
    ctx.routes = ["/", "/about/"]

    results = [runtime.t_ui(ctx, tid) for tid in ids]

    assert len(ctx.browser.navigations) == 4
    assert ctx.browser.executes == 12
    assert all(result["shared_traversals"] == 4 for result in results)
    assert all(result["batched_invariants"] == 3 for result in results)



def test_representative_ui_routes_cover_declared_pages_and_route_families_without_duplicate_instances():
    runtime = _load_runtime_module("runtime_ui_routes_canary")
    routes = [
        "/", "/about/", "/blog/",
        "/alpha-post/", "/beta-post/",
        "/adoption/", "/adoption/a/", "/adoption/b/",
        "/events/", "/events/a/", "/events/b/",
        "/services/", "/services/a/",
    ]
    selected = runtime.representative_ui_routes(routes, ["/about/", "/blog/"])
    assert selected[:3] == ["/", "/about/", "/blog/"]
    assert "/alpha-post/" in selected
    assert "/beta-post/" not in selected
    assert "/adoption/" in selected and "/adoption/a/" in selected and "/adoption/b/" not in selected
    assert "/events/" in selected and "/events/a/" in selected and "/events/b/" not in selected
    assert "/services/" in selected and "/services/a/" in selected


def test_ui_invariants_ignore_only_intentionally_hidden_controls():
    runtime = _load_runtime_module("runtime_ui_filter_canary")
    assert "tabIndex<0" in runtime.UI_JS["TF-UI-002"]
    assert "document.activeElement===e" in runtime.UI_JS["TF-UI-002"]
    assert "document.activeElement.blur" in runtime.UI_JS["TF-UI-002"]
    assert "screen-reader-text" in runtime.UI_JS["TF-UI-009"]
    assert "input:not([type=hidden])" in runtime.UI_JS["TF-UI-011"]
    assert "visibleDesc" in runtime.UI_JS["TF-UI-011"]
    assert "document.activeElement===e" in runtime.UI_JS["TF-UI-011"]
    assert "e.blur()" in runtime.UI_JS["TF-UI-011"]
    assert "document.activeElement!==e" in runtime.UI_JS["TF-UI-012"]
    assert "overflowX" in runtime.UI_JS["TF-UI-012"] and "e.blur()" in runtime.UI_JS["TF-UI-012"]
    assert "position" in runtime.UI_JS["TF-UI-006"] and "z>=50" in runtime.UI_JS["TF-UI-006"]
    assert "elementFromPoint" in runtime.UI_JS["TF-UI-004"]
    assert "leaflet-container" in runtime.UI_JS["TF-UI-004"]
    assert "input:not([type=hidden])" in runtime.UI_JS["TF-UI-025"]

def test_png_decoder_roundtrip_simple():
    # Chrome screenshot PNG format is also validated live in runtime; this just verifies the deterministic parser's guard path.
    spec = importlib.util.spec_from_file_location("runtime_harness2", ROOT / "tests" / "runtime" / "runtime_harness.py")
    runtime = importlib.util.module_from_spec(spec)
    assert spec.loader
    import sys
    sys.modules[spec.name] = runtime
    spec.loader.exec_module(runtime)
    try:
        runtime.decode_png(b"not a png")
    except runtime.TestFailure:
        pass
    else:
        raise AssertionError("invalid PNG accepted")
