From 0bfa4fe6d49dce68fd16391478b00960f1f08cf5 Mon Sep 17 00:00:00 2001 From: Enzo Martellucci Date: Thu, 7 May 2026 10:56:42 +0200 Subject: [PATCH] fix(reports): keep body sized so standalone screenshots don't time out MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Screenshot capture (Playwright/Selenium) waits for body.standalone to become visible, but the SSR'd page only contains an absolutely-positioned spinner — body collapses to 0 height and the wait resolves "hidden" until React mounts the dashboard. On slow first paints (cold webpack, complex filter hydration), this exceeds the 60s wait and the report errors with "Locator.wait_for: Timeout 60000ms exceeded ... .standalone". Pin body.standalone to the viewport so the locator is measurable from first paint and the wait sequence drives off React mount, not body box. Co-Authored-By: Claude Opus 4.7 (1M context) --- superset/templates/superset/spa.html | 7 +++++ tests/unit_tests/extension_tests.py | 47 ++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/superset/templates/superset/spa.html b/superset/templates/superset/spa.html index 40f5af6f34fe..f4132a99fb60 100644 --- a/superset/templates/superset/spa.html +++ b/superset/templates/superset/spa.html @@ -45,6 +45,13 @@ color: #000; } {% endif %} + {% if standalone_mode %} + /* Keep body sized so screenshot waits don't see it as hidden before React mounts. */ + html, body.standalone { + min-height: 100vh; + margin: 0; + } + {% endif %} {% if dark_theme_bg and entry != 'embedded' %} diff --git a/tests/unit_tests/extension_tests.py b/tests/unit_tests/extension_tests.py index b2b91fea26ad..b10f259d1c1d 100644 --- a/tests/unit_tests/extension_tests.py +++ b/tests/unit_tests/extension_tests.py @@ -68,3 +68,50 @@ def test_spa_template_includes_css_bundles(): "spa.html must call css_bundle for the page entry to load " "entry-specific extracted CSS in production builds" ) + + +def test_spa_template_standalone_body_has_min_height(): + """Standalone body must be measurable so screenshot waits don't time out.""" + from jinja2 import DictLoader, Environment + + template_path = join(SUPERSET_DIR, "templates", "superset", "spa.html") + with open(template_path) as f: + template_content = f.read() + + env = Environment( # noqa: S701 + loader=DictLoader( + { + "spa.html": template_content, + # Stub out includes/imports that are not relevant for this test. + "appbuilder/general/lib.html": "", + "superset/partials/asset_bundle.html": ( + "{% macro css_bundle(prefix, entry) %}{% endmacro %}" + "{% macro js_bundle(prefix, entry) %}{% endmacro %}" + ), + "superset/macros.html": ("{% macro get_nonce() %}{% endmacro %}"), + "tail_js_custom_extra.html": "", + "head_custom_extra.html": "", + } + ) + ) + + appbuilder = Mock() + appbuilder.app.config = {"FAVICONS": []} + + def render(standalone_mode: bool) -> str: + return env.get_template("spa.html").render( + appbuilder=appbuilder, + assets_prefix="", + bootstrap_data="{}", + entry="spa", + standalone_mode=standalone_mode, + theme_tokens={}, + spinner_svg=None, + ) + + standalone_html = render(standalone_mode=True) + assert "body.standalone" in standalone_html + assert "min-height: 100vh" in standalone_html + + non_standalone_html = render(standalone_mode=False) + assert "body.standalone" not in non_standalone_html