This is an automated email from the ASF dual-hosted git repository.

eschutho pushed a commit to branch poc/per-chart-dashboard-reports
in repository https://gitbox.apache.org/repos/asf/superset.git


The following commit(s) were added to 
refs/heads/poc/per-chart-dashboard-reports by this push:
     new a46dcf79ee8 fix(reports): wait for chart holders to attach before 
per-chart capture
a46dcf79ee8 is described below

commit a46dcf79ee89b18a0a0c64649f726dfbef1bf31d
Author: Elizabeth Thompson <[email protected]>
AuthorDate: Fri Jul 24 16:21:33 2026 +0000

    fix(reports): wait for chart holders to attach before per-chart capture
    
    Locator.count() reads the DOM at call time without waiting, and the
    dashboard's standalone container attaches before React renders any chart
    holders. In live testing this raced: some runs captured all charts, others
    found 0 and fell back to the full-dashboard screenshot.
    
    Wait for the first chart holder to attach (bounded by load_wait) before
    counting; return an empty list if none appear so the caller's fallback
    still engages for genuinely empty dashboards.
    
    Co-Authored-By: Claude Fable 5 <[email protected]>
---
 superset/utils/screenshot_utils.py              | 12 ++++++++++++
 tests/unit_tests/utils/test_screenshot_utils.py | 21 +++++++++++++++++++++
 2 files changed, 33 insertions(+)

diff --git a/superset/utils/screenshot_utils.py 
b/superset/utils/screenshot_utils.py
index 97bf282beb2..bc8c6c896ee 100644
--- a/superset/utils/screenshot_utils.py
+++ b/superset/utils/screenshot_utils.py
@@ -263,6 +263,18 @@ def take_per_chart_screenshots(
         in DOM (layout) order. Empty list if no charts could be captured.
     """
     chart_holders = page.locator(CHART_HOLDER_SELECTOR)
+    # count() reads the DOM at call time without waiting; the dashboard
+    # container can be attached before React has rendered any chart holders.
+    # Wait for the first holder so the count reflects the real layout.
+    try:
+        chart_holders.first.wait_for(state="attached", timeout=load_wait * 
1000)
+    except PlaywrightTimeout:
+        logger.warning(
+            "No chart holders appeared within %ss; dashboard may be empty "
+            "or failed to render",
+            load_wait,
+        )
+        return []
     chart_count = chart_holders.count()
     logger.info("Capturing %s charts individually", chart_count)
 
diff --git a/tests/unit_tests/utils/test_screenshot_utils.py 
b/tests/unit_tests/utils/test_screenshot_utils.py
index 8af63a14277..a7de93e3b4a 100644
--- a/tests/unit_tests/utils/test_screenshot_utils.py
+++ b/tests/unit_tests/utils/test_screenshot_utils.py
@@ -486,3 +486,24 @@ class TestTakePerChartScreenshots:
         # Scroll settle wait plus animation wait
         page.wait_for_timeout.assert_any_call(SCROLL_SETTLE_TIMEOUT_MS)
         page.wait_for_timeout.assert_any_call(3000)
+
+    def test_returns_empty_when_no_holders_appear(self):
+        """If no chart holder ever attaches, return [] instead of hanging."""
+        page, _ = self._make_page_with_charts([])
+        page.locator.return_value.first.wait_for.side_effect = 
PlaywrightTimeout(
+            "no holders"
+        )
+
+        result = take_per_chart_screenshots(page, load_wait=10)
+
+        assert result == []
+
+    def test_waits_for_first_holder_before_counting(self):
+        """The holder count must be taken after the first holder attaches."""
+        page, _ = self._make_page_with_charts([b"chart1"])
+
+        take_per_chart_screenshots(page, load_wait=42)
+
+        page.locator.return_value.first.wait_for.assert_called_once_with(
+            state="attached", timeout=42000
+        )

Reply via email to