fix(reports): add per-tile animation wait to prevent partial ECharts renders in tiled screenshots (#40694)

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Elizabeth Thompson
2026-06-04 16:43:34 -07:00
committed by GitHub
parent 875673f670
commit 42367afb25
3 changed files with 35 additions and 0 deletions

View File

@@ -89,6 +89,7 @@ def take_tiled_screenshot(
element_name: str,
tile_height: int,
load_wait: int = 60,
animation_wait: int = 0,
) -> bytes | None:
"""
Take a tiled screenshot of a large dashboard by scrolling and capturing sections.
@@ -98,6 +99,7 @@ def take_tiled_screenshot(
element_name: CSS class name of the element to screenshot
tile_height: Height of each tile in pixels
load_wait: Seconds to wait for charts to load per tile (default 60)
animation_wait: Seconds to wait for chart animations per tile (default 0)
Returns:
Combined screenshot bytes or None if failed
@@ -174,6 +176,12 @@ def take_tiled_screenshot(
load_wait,
)
# Wait for chart animations (e.g. ECharts) to finish after spinner clears.
# The global animation wait before tiling only covers the first tile;
# subsequent tiles need their own wait after data loads.
if animation_wait > 0:
page.wait_for_timeout(animation_wait * 1000)
# Calculate what portion of the element we want to capture for this tile
tile_start_in_element = i * tile_height
remaining_content = dashboard_height - tile_start_in_element

View File

@@ -369,6 +369,7 @@ class WebDriverPlaywright(WebDriverProxy):
element_name,
tile_height,
load_wait=self._screenshot_load_wait,
animation_wait=selenium_animation_wait,
)
if img is None:
logger.warning(

View File

@@ -371,3 +371,29 @@ class TestTakeTiledScreenshot:
sig = inspect.signature(take_tiled_screenshot)
assert sig.parameters["load_wait"].default == 60
def test_per_tile_animation_wait_called_per_tile(self, mock_page):
"""animation_wait adds an extra wait per tile after the spinner check."""
with patch("superset.utils.screenshot_utils.combine_screenshot_tiles"):
take_tiled_screenshot(
mock_page, "dashboard", tile_height=2000, animation_wait=5
)
# 3 tiles × (1 scroll settle + 1 animation wait) = 6 total calls
assert mock_page.wait_for_timeout.call_count == 6
animation_calls = [
call
for call in mock_page.wait_for_timeout.call_args_list
if call[0][0] == 5 * 1000
]
assert len(animation_calls) == 3
def test_animation_wait_default_is_zero(self):
"""animation_wait defaults to 0 so no extra per-tile wait by default."""
import inspect
from superset.utils.screenshot_utils import take_tiled_screenshot
sig = inspect.signature(take_tiled_screenshot)
assert sig.parameters["animation_wait"].default == 0