"""Regression checks for the worker dashboard's first-paint loading overlay.""" from __future__ import annotations import importlib.util import os import textwrap from pathlib import Path import pytest ROOT = Path(__file__).resolve().parents[2] SPEC = importlib.util.spec_from_file_location( "dashboard_activity_patch", ROOT / "services/hermes/scripts/patch_web_session_activity.py", ) assert SPEC and SPEC.loader PATCH = importlib.util.module_from_spec(SPEC) SPEC.loader.exec_module(PATCH) def _backend_source() -> str: """Provide the upstream anchors required by the deployment's patcher.""" return "\n".join( ( PATCH.HELPER_MARKER, PATCH.MESSAGES_BEFORE, PATCH.LATEST_ROWS_BEFORE, PATCH.LATEST_SELECTION_BEFORE, PATCH.SPA_INDEX_BEFORE, ) ) @pytest.mark.parametrize("occurrences", [0, 2]) def test_bootstrap_patch_rejects_upstream_drift(tmp_path, occurrences): """An ambiguous HTML insertion point must fail before writing a patch.""" source = tmp_path / "web_server.py" destination = tmp_path / "patched.py" source.write_text( _backend_source().replace( PATCH.SPA_INDEX_BEFORE, PATCH.SPA_INDEX_BEFORE * occurrences ), encoding="utf-8", ) with pytest.raises(RuntimeError, match="bootstrap context changed"): PATCH.patch(source, destination) assert not destination.exists() def _render_bootstrap(tmp_path: Path) -> str: """Run the patched HTML handler against the image's actual bootstrap.""" source = tmp_path / "web_server.py" destination = tmp_path / "patched.py" source.write_text(_backend_source(), encoding="utf-8") PATCH.patch(source, destination) patched = destination.read_text(encoding="utf-8") html_handler = patched[patched.index(PATCH.SPA_INDEX_BEFORE):] namespace = {} exec( "def serve(_index_path):\n" + textwrap.indent(textwrap.dedent(html_handler), " ") + " return html\n", namespace, ) dockerfile = (ROOT / "dockerfiles/Dockerfile.hermes-agent").read_text() body = dockerfile.split("` \n", 1)[1].split( '
`', 1 )[0] index = tmp_path / "index.html" index.write_text( "" + body + '
Dashboard
', encoding="utf-8", ) return namespace["serve"](index) def test_bootstrap_patch_preserves_the_page_and_resume_script(tmp_path): """HTML serving retains the app mount and the existing resume behavior.""" html = _render_bootstrap(tmp_path) assert '
Dashboard
' in html assert 'loading.hidden = false' in html assert html.count("") == html.count("") == 1 assert html.count("") == 1 @pytest.mark.parametrize("engine", ["chromium", "firefox"]) @pytest.mark.parametrize( ("route", "visible"), [("/", False), ("/chat", False), ("/sessions?resume=worker", False), ("/chat?resume=worker", True)], ) def test_bootstrap_visibility_without_the_bundle(tmp_path, engine, route, visible): """A failed or delayed bundle cannot cover ordinary dashboard routes.""" playwright = pytest.importorskip("playwright.sync_api") html = _render_bootstrap(tmp_path) with playwright.sync_playwright() as runtime: browser = getattr(runtime, engine).launch( executable_path=os.environ.get(f"HERMES_TEST_{engine.upper()}_EXECUTABLE"), headless=True, ) try: page = browser.new_page() page.route("**/*", lambda request: request.fulfill( content_type="text/html", body=html )) page.goto("http://hermes.test" + route) overlay = page.locator("#hermes-resume-bootstrap") assert overlay.is_visible() is visible if visible: # SessionActivityPanel removes the overlay when it mounts. overlay.evaluate("element => element.remove()") assert overlay.count() == 0 finally: browser.close()