fix(reports): mount off-screen dashboard charts before standalone capture - #42901
fix(reports): mount off-screen dashboard charts before standalone capture#42901eschutho wants to merge 1 commit into
Conversation
…ture
Scheduled reports could deliver a dashboard screenshot with blank regions
where off-screen ("virtualized") charts should be. The non-tiled standalone
capture path waits only for viewport-visible chart holders to reach a
terminal state (#42624), then takes a full_page screenshot that includes
below-the-fold content. DashboardVirtualization only mounts holders that
intersect the window viewport, so any holder that never scrolls into view is
declared ready yet captured blank -- with no timeout, since the gate believes
it is done.
Grow the viewport to the full dashboard height before the readiness wait so
every holder intersects the viewport and virtualization mounts it; the
existing readiness gate then requires them all to reach a terminal state
before capture.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Code Review Agent Run #171ff6Actionable Suggestions - 0Additional Suggestions - 1
Review Details
Bito Usage GuideCommands Type the following command in the pull request comment and save the comment.
Refer to the documentation for additional commands. Configuration This repository uses Documentation & Help |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #42901 +/- ##
==========================================
- Coverage 66.38% 66.37% -0.01%
==========================================
Files 2857 2857
Lines 161160 161183 +23
Branches 37074 37078 +4
==========================================
+ Hits 106987 106988 +1
- Misses 52151 52172 +21
- Partials 2022 2023 +1
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
SUMMARY
Scheduled reports (and dashboard thumbnails) could deliver a screenshot with
blank regions where charts should be. The blank charts are ones that were
virtualized (rendered off-screen and never mounted) at capture time.
WebDriverPlaywright.get_screenshot's standard, non-tiled path — the default,since
SCREENSHOT_TILED_ENABLEDis off — waits for chart readiness via_wait_for_charts_readyand then captures the dashboard withpage.screenshot(full_page=True).Problem
The readiness gate (
REPORT_CHART_HOLDERS_READY_JS/UNREADY_CHART_HOLDERS_JS_BODY) only requires viewport-visible chartholders to reach a terminal state; it deliberately skips holders whose bounding
rect is outside the window viewport so it won't deadlock on lazy-rendered
charts (readiness logic added in #42624). Those off-screen holders are
classified
virtualizedand counted as "ready".But
full_page=Truecaptures the entire document height, includingbelow-the-fold content.
DashboardVirtualization(anIntersectionObserverkeyed to the window viewport) only mounts a chart holder once it intersects the
viewport, so a holder that never scrolls into view is never mounted — and is
captured blank. Because the gate believes it is done, this happens with no
timeout and no error (an affected report showed
virtualized_holders=2withhundreds of seconds of budget still remaining).
Superset does try to disable virtualization for capture browsers via
isCurrentUserBot()(navigator.webdriver), but that heuristic is fragile:any deployment where
navigator.webdriveris falsy (custom browser args,anti-automation flags) re-enables virtualization and reintroduces blank
reports. The capture path should not rely on it.
Fix
Before the readiness wait in the non-tiled standalone path, grow the browser
viewport to the full dashboard height (
_mount_offscreen_chart_holders). Onceevery holder intersects the (now tall) viewport, virtualization mounts it and
the existing readiness gate requires them all to reach a terminal state
(rendered, empty, or error) before capture. This is defense-in-depth,
independent of the
navigator.webdriverheuristic, and does not reintroducethe deadlock #42624 avoided: mounted charts either render or reach a terminal
error/empty state, and genuinely stuck charts still fail the report loudly via
the readiness timeout rather than shipping blank.
The tiled path (
SCREENSHOT_TILED_ENABLED=True) was already immune — it scrollsand captures tile-by-tile, mounting each tile's holders as it goes.
A memory guard (
MAX_STANDALONE_CAPTURE_VIEWPORT_HEIGHT = 30000) caps theexpanded viewport for pathologically tall dashboards and logs a warning
recommending tiled screenshots;
full_pagealready rasterizes the wholedocument, so this cap is not a new capability limit.
BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF
Not applicable — server-side capture pipeline change; behavior is covered by
unit tests.
TESTING INSTRUCTIONS
Automated:
pytest tests/unit_tests/utils/webdriver_test.py \ tests/unit_tests/utils/test_screenshot_utils.pyNew/updated tests assert that a standalone dashboard taller than the viewport is
resized to its full height before the readiness wait, that the resize is
scoped to standalone dashboard captures (not chart captures), that it is skipped
when the dashboard already fits, that the height is capped with a warning, and
that mounting failures are swallowed so they never abort a capture.
Manual: schedule a report for a dashboard tall enough to have below-the-fold
charts, in an environment where
navigator.webdriveris not detected as a bot,and confirm the delivered image renders every chart rather than leaving blank
regions.
ADDITIONAL INFORMATION
Builds on the report readiness logic introduced in #42624.