Share page-check helpers in tests/page_check_common.py

MODES, TEST_FILL, FAKE_BRIDGE, the stub loader, the page opener and the no-AI case list now live in one place; sample_data and push move to tests/conftest.py.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
VasanthandClaude Opus 5.5 committed 2026-10-16 15:52:35 +05:30
1 parent 7ad73c1fda
commit 344718007e
8 files changed
+125 -189

No files matched your search

+8 -61
View File
@@ -2,11 +2,11 @@
AI sections (rpa-dashboard-ai/templates/desktop/ai_sections.html) pasted in before </body>.
Page under test: set PAGE_CHECK_AI_DESKTOP_DIR to a generated dashboard folder (the one with
dashboard.html and server_stub.py). Without it, the no-AI template is filled with the no-AI
check's TEST_FILL with no optional sections (sections.py) and the AI sections are pasted in,
the way the skill does it.
dashboard.html and server_stub.py). Without it, the no-AI template is filled with the shared
TEST_FILL (tests/page_check_common.py) with no optional sections (sections.py) and the AI
sections are pasted in, the way the skill does it.
The page sees a fake Qt bridge, as in the no-AI check: each slot call lands in window.__calls.
The page sees the shared fake Qt bridge: each slot call lands in window.__calls.
The AI slot, ai_request(id, method, path, body), goes to the review backend's dispatch() in
this process, and the reply comes back as an `ai_response` envelope, exactly what
ai_bridge.py's slot does. So the approve gate under test is the Python one. Tests set run and
@@ -16,41 +16,14 @@ import importlib.util
import json
import os
import re
import sys
from pathlib import Path
import pytest
from page_check_common import FAKE_BRIDGE, MODES, TEST_FILL, WIRED, load_stub, open_page
ROOT = Path(__file__).resolve().parents[2]
NO_AI = ROOT / "rpa-dashboard-no-ai" / "templates" / "dashboard.html"
AI = ROOT / "rpa-dashboard-ai" / "templates"
TEST_FILL = {
"APP_NAME": "Test Extractor",
"APP_SUBTITLE": "Page check",
"LOGO_SRC": "",
"API_BASE": "",
"STEPS_JSON": json.dumps(["Initializing", "Login", "Process items", "Completed"]),
"REPORT_COLUMNS_JSON": json.dumps([{"key": "item", "label": "Item"}, {"key": "status", "label": "Status"}]),
}
FAKE_BRIDGE = """
window.__calls = [];
window.qt = {webChannelTransport: {}};
window.QWebChannel = function (_transport, ready) {
const pyBridge = new Proxy({}, {get: (_o, slot) => (...args) => {
window.__calls.push([slot, ...args]);
if (slot === 'ai_request') window.__aiRequest(...args).then(window.__pyMsg);
}});
ready({objects: {pyBridge}});
};
"""
MODES = {"light": (1280, 820, False), "dark": (1280, 820, True), "narrow": (800, 900, False)}
WIRED = os.environ.get("PAGE_CHECK_WIRED") == "1"
@pytest.fixture
def sample_data():
if WIRED:
pytest.skip("page is wired to a bot, so it has no sample data")
@pytest.fixture(scope="session")
@@ -80,11 +53,7 @@ def page_url(site):
@pytest.fixture(scope="session")
def stub_module(site):
sys.path.insert(0, str(site)) # the stub imports bands.py from its own folder
spec = importlib.util.spec_from_file_location("desktop_server_stub", site / "server_stub.py")
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
return mod
return load_stub(site, "desktop_server_stub")
@pytest.fixture
@@ -102,27 +71,5 @@ def ai_request(stub, req_id, method, path, body):
@pytest.fixture(params=list(MODES))
def page(request, browser, page_url, stub):
width, height, dark = MODES[request.param]
ctx = browser.new_context(viewport={"width": width, "height": height})
ctx.expose_function("__aiRequest", lambda i, method, path, body: ai_request(stub, i, method, path, body))
ctx.add_init_script(FAKE_BRIDGE)
pg = ctx.new_page()
pg.set_default_timeout(5000)
errors = []
pg.on("pageerror", lambda e: errors.append(e))
pg.goto(page_url)
assert not errors, f"page script errors: {errors}"
if dark:
push_to(pg, "apply_dark", {"on": True})
yield pg
assert not errors, f"page script errors: {errors}"
ctx.close()
def push_to(pg, event, data):
pg.evaluate("([event, data]) => window.__pyMsg(JSON.stringify({event, data}))", [event, data])
@pytest.fixture
def push(page):
return lambda event, data: push_to(page, event, data)
expose = ("__aiRequest", lambda i, method, path, body: ai_request(stub, i, method, path, body))
yield from open_page(browser, page_url, request.param, FAKE_BRIDGE, expose)
+3 -11
View File
@@ -25,17 +25,9 @@ from test_ai_cost import ( # noqa: E402,F401
test_spend_tiles_sum_the_priced_runs,
)
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "page_check"))
from test_contract import ( # noqa: E402,F401
test_dark_toggle_flips_theme,
test_donut_and_legend_match_batch_states,
test_kpi_counts_match_batch_states,
test_log_lines_show_their_type,
test_no_bot_page_runs_on_sample_data,
test_plain_browser_first_open_shows_sample_data,
test_report_search_filters_rows_and_shows_bucket_pill,
test_step_list_follows_step_label,
)
from page_check_common import no_ai_cases # noqa: E402
globals().update(no_ai_cases())
TAMPER = """async (path) => {
const reply = new Promise(done => { const base = window.__pyMsg;