From 3c33129ebd9cb92aad21df5e8720ff4bd246b521 Mon Sep 17 00:00:00 2001 From: autonomic-bot Date: Thu, 11 Jun 2026 06:45:43 +0000 Subject: [PATCH] =?UTF-8?q?fix(shot):=20mattermost=20hook=20v2=20=E2=80=94?= =?UTF-8?q?=20interstitial=20appears=20on=20ANY=20first-visit=20route=20in?= =?UTF-8?q?cl=20/login=20(proven=20byte-identical=20PNG);=20click=20'View?= =?UTF-8?q?=20in=20Browser'=20best-effort=20then=20settle;=20unit=20test?= =?UTF-8?q?=20covers=20click=20+=20no-interstitial=20fallback;=20207=20pas?= =?UTF-8?q?s,=20lint=20PASS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/mattermost-lts/recipe_meta.py | 21 +++++++++++++++------ tests/unit/test_screenshot.py | 18 ++++++++++++++++-- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/tests/mattermost-lts/recipe_meta.py b/tests/mattermost-lts/recipe_meta.py index 5ccbf57..0e98165 100644 --- a/tests/mattermost-lts/recipe_meta.py +++ b/tests/mattermost-lts/recipe_meta.py @@ -21,11 +21,17 @@ EXTRA_ENV = {"TIMEOUT": "600"} def SCREENSHOT(page, ctx): - """Land the real sign-in form for the CI card (phase-shot). The default landing capture gets - mattermost's "view in desktop app or browser?" interstitial at `/` — a real page but not - representative of the app. `/login` renders the standard login form directly. Credential-free - (empty fields, R7 secret-safety: never a page showing generated secrets); the harness snaps - the PNG after this returns.""" + """Land the real sign-in form for the CI card (phase-shot). Mattermost serves a + "view in desktop app or browser?" interstitial on a browser's FIRST visit to ANY route + (including /login — proven by shot-proof2-mattermost-lts: byte-identical interstitial PNG with + and without a plain /login hook); a real user clicks "View in Browser" to reach the login + form, so the hook does exactly that. Click + second settle are best-effort (if the + interstitial is absent we are already on the form). Credential-free (empty fields, R7 + secret-safety); the harness snaps the PNG after this returns. Waits are kept short (8s/3s/8s) + so the realistic hook path stays well inside the ~60s step budget — the 45s nav deadline is + only burned when the app never serves, and then the hook raises before any settle.""" + import contextlib + from harness import browser as harness_browser from harness import screenshot as screenshot_mod @@ -36,4 +42,7 @@ def SCREENSHOT(page, ctx): deadline_seconds=screenshot_mod.NAV_DEADLINE_S, wait_until="domcontentloaded", ) - screenshot_mod.settle(page) + screenshot_mod.settle(page, 8_000) + with contextlib.suppress(Exception): + page.click("text=View in Browser", timeout=3_000) + screenshot_mod.settle(page, 8_000) diff --git a/tests/unit/test_screenshot.py b/tests/unit/test_screenshot.py index cb5b238..6c83d90 100644 --- a/tests/unit/test_screenshot.py +++ b/tests/unit/test_screenshot.py @@ -138,14 +138,21 @@ def test_mattermost_screenshot_hook_lands_login(): status = 200 class _NavPage(_FakePage): - def __init__(self): + def __init__(self, click_raises=False): super().__init__([]) self.urls = [] + self.clicks = [] + self._click_raises = click_raises def goto(self, url, wait_until=None, timeout=None): self.urls.append(url) return _Resp() + def click(self, selector, timeout=None): + self.clicks.append(selector) + if self._click_raises: + raise TimeoutError("no interstitial") + tests_dir = os.path.join(os.path.dirname(__file__), "..") meta = meta_mod.load("mattermost-lts", tests_dir=tests_dir) hook = S._load_screenshot_hook(meta) @@ -153,7 +160,14 @@ def test_mattermost_screenshot_hook_lands_login(): page = _NavPage() hook(page, meta_mod.hook_ctx("mm.example.org", meta)) assert page.urls == ["https://mm.example.org/login"] - assert page.idle_waits, "hook must settle before the harness snaps" + assert page.clicks == ["text=View in Browser"], "hook must click through the interstitial" + assert len(page.idle_waits) == 2, "hook must settle after nav AND after the click" + + # no interstitial (already on the form): the click times out and the hook still succeeds + page2 = _NavPage(click_raises=True) + hook(page2, meta_mod.hook_ctx("mm.example.org", meta)) + assert page2.clicks == ["text=View in Browser"] + assert len(page2.idle_waits) == 1, "failed click must skip the second settle, not raise" def test_screenshot_reachable_through_real_load_path(tmp_path):