Improve PDF export + tests#541
Conversation
Update the Playwright browser-PDF readiness pipeline to match the actual print-mode HTML that the serverless export path stages for Chromium.
This change removes the DataTables and 3D viewer readiness waits from PlaywrightPDFRenderer._wait_for_render_ready(). In the browser-PDF path we do not rely on an HTTP query string, but we do set the same server-side print target by rendering with context['print'] = 'pdf' and TemplateEngine.set_print_style('pdf'). Under that print-style mode the product suppresses DataTables initialization and forces 3D viewers into inactive proxy behavior, so both waits were unnecessary for the staged offline HTML bundle.
The renderer keeps the remaining waits that still matter for this path: FOUC gate, FOUC transition, web fonts, MathJax, Plotly, images, videos, and the final double requestAnimationFrame settle step.
The test updates remove the old DataTables-specific readiness tests and replace them with a focused step-set assertion that locks in the print-PDF readiness pipeline. The existing image readiness coverage remains, including the stricter source-plus-naturalWidth fast path for images.
FOLLOWUP.md is also updated to drop stale backlog items that only applied while the removed waits still existed. The remaining follow-ups keep the TIFF readiness gap and Plotly theme-overlay investigation, which are still relevant after this simplification.
Validation:
- make check
- make smoketest
- uv run pytest tests/serverless/test_pdf_renderer.py -k "wait_for_render_ready_matches_print_pdf_step_set or wait_for_render_ready_images_step_requires_source_and_decoded_dimensions or signal_timeout" -q
- uv run ruff check src/ansys/dynamicreporting/core/serverless/pdf_renderer.py tests/serverless/test_pdf_renderer.py
- uv run ruff format --check src/ansys/dynamicreporting/core/serverless/pdf_renderer.py tests/serverless/test_pdf_renderer.py
- python -m compileall -q src/ansys/dynamicreporting/core/serverless/pdf_renderer.py tests/serverless/test_pdf_renderer.py
Refresh uv.lock after the browser-PDF readiness work, following the repository's required post-commit dependency update workflow.\n\nThis commit records the result of:\n- uv sync --upgrade --all-extras\n- uv lock --upgrade\n\nWhy this is separate:\n- the previous commit contains the intentional browser-PDF code and test changes\n- this commit only captures the lockfile state produced by the mandated dependency refresh\n- keeping them separate makes review and any future rollback cleaner\n\nScope:\n- update resolved dependency versions in uv.lock\n- no source changes\n- no API changes\n\nVerification before publish:\n- browser-PDF focused tests passed before the lock refresh\n- final verification will run again on the current tree before push
There was a problem hiding this comment.
Pull request overview
This PR refines the browser-based PDF readiness pipeline to match the serverless print-mode (print=pdf) export path, removing waits for components that aren’t part of the print-style HTML bundle, tightening image readiness criteria, and locking the intended step set with targeted unit tests. It also updates the repo’s uv-based workflow (and lockfile) to keep dependency management consistent and reproducible.
Changes:
- Simplify
_wait_for_render_ready()by removing DataTables + 3D viewer waits and tightening image readiness checks. - Add unit tests to lock in the expected readiness step set and verify the updated image readiness script.
- Update the Makefile + AGENTS.md to use
uv syncas the single install path; refreshuv.lock.
Reviewed changes
Copilot reviewed 4 out of 5 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
src/ansys/dynamicreporting/core/serverless/pdf_renderer.py |
Removes DataTables/3D viewer readiness steps and strengthens the image readiness fast-path condition. |
tests/serverless/test_pdf_renderer.py |
Removes DataTables-specific tests; adds tests asserting the exact readiness step set and updated image readiness script. |
Makefile |
Switches make test and make install to rely on uv sync (frozen, extras) rather than pip editable installs. |
AGENTS.md |
Documents uv sync as the canonical single installer workflow and warns against mixing with pip install -e. |
uv.lock |
Updates locked dependency versions via the documented uv upgrade/lock workflow. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Three non-obvious points were left without explanation after the
substantive changes landed:
tests.yml – Install Playwright Chromium step
The playwright Python package provides the CLI but not the browser
binary; the separate download step and the reason for --with-deps
(system library installation) were not documented inline. Added a
four-line comment directly above the `run:` key so the intent is
visible without reading Playwright's own install docs.
test_pdf_renderer.py – test_playwright_pdf_signal_timeout
The try/except/else pattern is deliberate: pytest.raises() cannot
assert two independent error-message fragments AND explicitly fail
when no exception is raised. Added a comment before the try block
explaining all three cases it handles.
test_pdf_renderer.py – test_apply_pdf_capture_styles_take_effect_under_screen_media
Two comments added:
- Before the function-scope `from playwright.sync_api import
sync_playwright`: explains why the import is co-located with this
test rather than at module level (makes the Chromium dependency
explicit and local).
- Before browser.close(): clarifies that sync_playwright()'s context
manager stops the Playwright server but does not close the browser,
so close() must be called explicitly.
No logic changes; comments only.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #541 +/- ##
==========================================
+ Coverage 82.33% 82.67% +0.34%
==========================================
Files 32 32
Lines 8705 8745 +40
Branches 1585 1590 +5
==========================================
+ Hits 7167 7230 +63
+ Misses 1054 1033 -21
+ Partials 484 482 -2 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Resolve the browser-PDF template once in each public ADR entry point and pass the concrete Template object into a shared private helper. This keeps export_report_as_browser_pdf() from calling Template.get(**kwargs) a second time when filename is omitted, while leaving the public API and the existing browser-PDF staging/export/render pipeline intact. The old _render_report_as_browser_pdf_impl() helper was replaced with _render_template_as_browser_pdf(template, ...). That helper now renders the print-mode HTML directly from the resolved Template instance before handing the staged bundle to the offline exporter and Playwright renderer. The surrounding comments were expanded so the single-lookup flow and the filename reuse are easier to follow during later maintenance. The browser-PDF ADR tests were updated to patch the new helper name, and a focused regression test now asserts that the default-filename export path reuses the original Template lookup rather than querying again just to rebuild <guid>.pdf. Verification: - make check - Focused pytest intentionally skipped per request - make smoketest not rerun after this patch because you asked to skip tests
Add a dedicated Iframes step to PlaywrightPDFRenderer._wait_for_render_ready() so browser-PDF capture no longer treats same-origin iframe content as ready as soon as the nested document loads. Report-link layouts can keep the iframe hidden at 10px tall and expand it asynchronously from their onload handler, so the renderer now watches for that post-load height/display transition before Chromium computes layout and prints the page. The new wait logic intentionally distinguishes between same-origin and inaccessible frames. When the parent document can read iframe.contentDocument, readiness now requires the child document to reach readyState='complete' and, when the child content has measurable height, the iframe element itself to become visible and expand beyond the collapsed placeholder size. When the frame is cross-origin or otherwise inaccessible, the renderer falls back to the existing page-level load guarantee instead of adding a speculative wait that could hang the export. A focused renderer test was added in tests/serverless/test_pdf_renderer.py. It uses a real local iframe page whose onload expansion is explicitly gated by the parent document, which makes the regression deterministic without any sleep-based timing. The step-order contract test was also updated so future changes cannot silently remove the iframe readiness stage from the print-style pipeline. Verification: - make check - Focused pytest intentionally skipped per request - make smoketest not rerun because you asked to skip tests
Extend the focused Playwright PDF renderer unit coverage with the two validation-path regressions called out in the follow-up list. The first test exercises _resolve_entrypoint_path() with a filename that attempts to escape the exported HTML directory via parent traversal. That locks in the is_relative_to() guard so a later refactor cannot accidentally let Chromium open an arbitrary file outside the export bundle. The second change extends the constructor-option validation coverage so margins with the required top/right/bottom/left keys plus an unexpected extra key are rejected. The implementation already enforced exact keys; this test makes that contract explicit and prevents the renderer from silently ignoring unsupported fields in the future. Verification: - uv run pytest tests/serverless/test_pdf_renderer.py -q -k "test_resolve_entrypoint_path_rejects_parent_traversal or test_renderer_constructor_rejects_invalid_options" - make check - make smoketest
Add a focused renderer unit test that locks in the sequencing relationship between _apply_pdf_capture_styles(), _wait_for_render_ready(), and _compute_pdf_width() inside render_pdf(). The browser-PDF flow depends on that order because width measurement is only meaningful after the capture CSS has already unclipped browser-rendered content and the readiness waits have settled the same styled DOM that Chromium will print. The new test uses the existing Playwright stub helper and records only the method-call sequence that matters for the contract. It stays narrowly scoped to the width-sensitive pipeline ordering rather than duplicating broader render_pdf() assertions. Verification: - uv run pytest tests/serverless/test_pdf_renderer.py -q -k test_playwright_pdf_applies_capture_styles_before_readiness_and_width - make check - make smoketest
Add Examples sections to ADR.render_report_as_browser_pdf() and ADR.export_report_as_browser_pdf() so the browser-PDF APIs match the surrounding documentation style used by the existing PDF and PPTX helpers. The render example shows the byte-stream workflow, including setup(), a report selector, and an explicit margins dictionary written out to a .pdf file. The export example shows the direct file-writing API with filename, name, item_filter, and a browser-PDF-specific landscape option. Keeping the examples local to the docstrings makes the two public entry points easier to discover without changing any runtime behavior. Verification: - make check - make smoketest
Add a narrow ADR helper that removes the dedicated fallback browser-PDF scratch parent only when ADR is using the external-database temp-directory path. The helper does not recurse into child contents; the per-render TemporaryDirectory remains responsible for deleting its own child directory, and ADR only attempts a best-effort rmdir() on the now-empty parent after the render path finishes. The cleanup helper ignores OSError so successful renders are not turned into cleanup failures by concurrent exports, already-removed roots, or stale non-empty directories. That preserves the existing rendering behavior while still removing the common empty-parent case. Two focused ADR tests were added. One verifies that a successful render through the fallback path removes the empty parent directory after the temporary child is cleaned up. The other forces Path.rmdir() to raise OSError and confirms that the browser-PDF render still succeeds and leaves the scratch root in place. Verification: - uv run pytest tests/serverless/test_adr.py -q -k "test_render_report_as_browser_pdf_cleans_empty_fallback_scratch_root or test_render_report_as_browser_pdf_ignores_fallback_scratch_cleanup_oserror" - make check - make smoketest
Refactor PlaywrightPDFRenderer.render_pdf() so the BrowserContext lifecycle is explicit instead of being spread across nested try/finally blocks. The renderer now initializes context to None, creates it inside the main browser lifetime block, and closes it only when creation succeeded before always closing the Browser itself. This matches Playwright's documented guidance for browser.new_context(): explicitly close contexts that were created via browser.new_context() before calling browser.close(). The guard on context.close() also makes the browser.new_context() failure path easier to read because it is obvious that a context cannot be closed if creation never returned one. A focused renderer unit test now forces browser.new_context() to fail and asserts that render_pdf() still closes the Browser and re-raises the failure as ADRException. That locks in the cleanup behavior without needing a live browser. Verification: - uv run pytest tests/serverless/test_pdf_renderer.py -q -k test_playwright_pdf_closes_browser_when_new_context_creation_fails - make check - make smoketest
Replace the iframe readiness heuristic that treated any visible frame taller than 10px as ready. That old check baked in an audited product placeholder height instead of using the nested document's real measured size, which made the readiness rule fragile for legitimately small report-link iframes. The iframe wait step now computes the child document's own scroll height and considers the frame ready only when its visible clientHeight has expanded to at least that measured child height. This keeps the readiness decision tied to the final nested content instead of a product-specific minimum height guess. The focused iframe regression test was rewritten to use a tiny same-origin srcdoc frame whose final height stays below the old hardcoded threshold. That makes the test prove the magic cutoff is gone rather than only proving the frame eventually grows. Verification: - uv run pytest tests/serverless/test_pdf_renderer.py -q -k test_wait_for_render_ready_iframes_step_waits_for_async_expansion - make check - make smoketest
Tighten the same-origin iframe readiness predicate so a hidden iframe placeholder cannot count as ready just because it reports a small positive clientHeight. The earlier child-height-based fix removed the hardcoded 10px threshold, but it still ignored visibility:hidden, which let the test fast-pass immediately on Linux CI when the iframe document reported a tiny height before the deferred expansion made the frame visible. The iframe readiness step now requires the frame to be both non-display:none and non-visibility:hidden before comparing its clientHeight to the child document's measured height. That matches the actual readiness contract we want for report-link frames: the nested document must be loaded, expanded, and visibly presented before PDF capture proceeds. Verification: - uv run pytest tests/serverless/test_pdf_renderer.py -q -k test_wait_for_render_ready_iframes_step_waits_for_async_expansion - make check - make smoketest
Extend the Playwright PDF renderer tests to cover the remaining _wait_for_render_ready() branches that were still unchecked in the follow-up list. The new tests exercise the no-#report_root fast path, the body.loaded MutationObserver path, the opacity transition wait, the Plotly loaded-class mutation path, the video loadeddata path, and the final double requestAnimationFrame step. Existing image, iframe, and step-order tests were kept and reused as part of the focused readiness subset. To keep the file readable, the repeated monkeypatch-and-evaluate harness was collapsed into two small local helpers that capture readiness scripts by step name and run an individual wait script on a real page while exposing its eventual success or failure back to the test. That keeps the new coverage signal-based rather than relying on sleeps, and it avoids scattering the same boilerplate across every branch-specific test. Verification: - uv run pytest tests/serverless/test_pdf_renderer.py -q -k "wait_for_render_ready_fouc_gate_fast_passes_without_report_root or wait_for_render_ready_fouc_gate_waits_for_body_loaded_class or wait_for_render_ready_fouc_transition_waits_for_opacity_transition or wait_for_render_ready_plotly_step_waits_for_loaded_class or wait_for_render_ready_images_step_does_not_fast_pass_srcless_images or wait_for_render_ready_iframes_step_waits_for_async_expansion or wait_for_render_ready_videos_step_waits_for_loadeddata or wait_for_render_ready_double_request_animation_frame_resolves or wait_for_render_ready_matches_print_pdf_step_set" - make check - make smoketest
Extend PlaywrightPDFRenderer._block_external_requests() so it aborts any request URL that carries an external authority, not just requests whose parsed scheme is explicitly http or https. This closes the gap for protocol-relative URLs such as //example.com/asset.js and for authority-bearing file URLs such as file://example.com/asset.js while still allowing local file:///..., data:, and blob: resources that belong to the offline export bundle. The request-routing tests now cover those additional URL forms directly, alongside the existing http/https, local file, data, and blob cases. The WebSocket blocking test stays unchanged because the request-routing hardening does not alter the dedicated ws/wss handling path. Verification: - uv run pytest tests/serverless/test_pdf_renderer.py -q -k "test_block_external_requests_keeps_browser_export_offline or test_block_external_websockets_keeps_browser_export_offline" - make check - make smoketest
Add debug-level timing around PlaywrightPDFRenderer._evaluate_ready_step() so each readiness step reports how long it took and whether it completed or failed. This keeps the existing info-level lifecycle logs unchanged while giving a direct way to see which specific readiness signal is slow during browser-PDF diagnostics. The timing is measured around the page.evaluate() call that waits on the in-page readiness promise. The implementation uses a small try/except/finally block so the debug line is emitted for both successful and failing steps without altering the existing timeout and exception behavior. Two focused unit tests pin the logging behavior with a deterministic monotonic() sequence: one verifies the completed-step message and one verifies the failed-step message when page.evaluate() raises. Verification: - uv run pytest tests/serverless/test_pdf_renderer.py -q -k "test_evaluate_ready_step_logs_duration_on_success or test_evaluate_ready_step_logs_duration_on_failure" - make check - make smoketest
Add an explicit debug diagnostic for readiness steps that run out of shared render budget before Playwright page.evaluate() is invoked. This keeps the new per-step readiness timing logs from going silent in the pre-evaluate timeout branch, which previously raised ADRException without any matching debug breadcrumb. The new message distinguishes "never reached Playwright" from steps that entered page.evaluate() and then failed or timed out inside the browser. Update the existing expired-deadline unit test to inject a mock logger and assert the new diagnostic while preserving the existing no-browser-call guarantee. Verification: - uv run pytest tests/serverless/test_pdf_renderer.py -q -k "test_evaluate_ready_step_rejects_expired_deadline_without_browser_call or test_evaluate_ready_step_logs_duration_on_success or test_evaluate_ready_step_logs_duration_on_failure" - make check - make smoketest
The browser-PDF renderer no longer treats iframe content as a supported readiness target. Remove the dedicated iframe wait step from PlaywrightPDFRenderer._wait_for_render_ready() and renumber the remaining video and double-requestAnimationFrame stages to keep the readiness pipeline comments and the public step-order contract aligned. Drop the iframe-specific regression test from tests/serverless/test_pdf_renderer.py and update the step-set assertion so it matches the reduced readiness pipeline. The remaining readiness tests still cover the supported browser-side signals: FOUC gate/transition, fonts, MathJax, Plotly, images, videos, and the final paint-settling requestAnimationFrame pair. A local-only follow-up note in local_docs/pdfex/FOLLOWUP.md was also updated to record that iframe content is now treated as unsupported on this browser-PDF path, but local_docs/ is gitignored and is therefore not part of this commit. Verification: - uv run pytest tests/serverless/test_pdf_renderer.py -q -k "wait_for_render_ready" - make check - make smoketest
Tighten the Plotly readiness step in PlaywrightPDFRenderer so a chart is not considered ready merely because its `.nexus-plot` element gained the product- owned `loaded` class. The staged print-mode HTML can still leave the sibling `.adr-spinner-loader-container` visible during theme-mismatch rerenders, which would allow the PDF export to capture a loader over the plot. The readiness step now resolves per plot only when both conditions are true: - the `.nexus-plot` element has class `loaded` - the nearest ADR loader overlay is hidden (or absent) Use a shared MutationObserver to watch the plot class and the loader's style/class/hidden attributes so the wait remains signal-based and bounded. This keeps the logic tied to the product's actual DOM contract instead of adding time-based sleeps or retries. Update the focused Plotly readiness regression to exercise the exact race: mark the plot as loaded while the loader is still visible, assert that the step stays pending, then hide the loader and confirm the wait resolves. A local-only cleanup to local_docs/pdfex/FOLLOWUP.md also checked off the Plotly item and reformatted several checked/struck checklist lines so VS Code Markdown preview shows them clearly, but local_docs/ is gitignored and is not part of this commit. Verification: - uv run pytest tests/serverless/test_pdf_renderer.py -q -k "plotly or wait_for_render_ready" - make check - make smoketest
Treat render_timeout as one shared monotonic deadline for the Chromium render phase instead of resetting a fresh budget for navigation and readiness. The renderer now spends that shared budget across browser launch, page navigation, readiness waits, and later browser-side preparation checks before width measurement and PDF generation. Keep the timeout scoped to the browser phase only. Server-side template rendering and offline asset export remain outside this budget, which preserves backwards compatibility for existing export flows while making the browser-side timeout behavior match the public API wording. Update the ADR public docstrings to describe the narrower browser-phase scope explicitly, add a helper that converts the remaining monotonic budget to Playwright milliseconds, and pass the original deadline into _wait_for_render_ready() so navigation and readiness consume the same window. Guard page.pdf() with a preflight deadline check because Playwright's Python API does not expose a timeout parameter for PDF generation. Refresh the focused renderer tests to use deterministic monotonic values, assert the launch/navigation timeout propagation, and verify that readiness reuses the original deadline rather than receiving a reset timeout. Verification: uv run pytest tests/serverless/test_pdf_renderer.py -q; make check; make smoketest
Tighten the Images readiness step so ADR canvas-backed image widgets do not fast-pass just because the underlying img element still has a completed source. This specifically targets the product flow used by TIFF and enhanced-image slider views, where the browser-visible output is gated by a companion canvas that is unhidden only after async decode and canvas generation complete. The installed v271 product templates and scripts show that print-style PDF reports still use the deep-image/TIFF slider path under TemplateEngine print style PDF. In that path, slider_template.html hides the visible canvas before each update, may leave the old completed img source in place while a new TIFF fetch/decode runs, and geotiff_nexus.js later assigns a data URL back onto the img before the slider onload handler regenerates and reveals the companion canvas. The previous readiness guard could therefore treat a stale completed img as ready too early. Fix the renderer by requiring any companion canvas named <img id>_canvas to be visibly unhidden before the image is considered ready. Keep the existing src/complete/naturalWidth guard, and watch both the img load/error events and companion-canvas attribute mutations so the wait resolves promptly once the product finishes its async image swap. Add a focused Playwright regression that models the ADR slider/deep-image shape directly: a completed img plus a hidden companion canvas must not resolve readiness until the canvas becomes visible. Keep the existing src-less image regression unchanged to preserve the earlier fast-pass fix. Verification: uv run pytest tests/serverless/test_pdf_renderer.py -q -k "images_step or matches_print_pdf_step_set or plotly"; make check; make smoketest
The installed Django product path stores HTML items as raw payload text, macro-expands them through render_html(), and injects the result back into report HTML with the safe filter. That means browser-PDF can see arbitrary user HTML and inline JavaScript exactly as authored, while the renderer itself only waits on a bounded set of ADR-owned readiness signals. Document this explicitly on the public browser-PDF APIs so callers do not mistake render_timeout and the current readiness pipeline for a general-purpose async completion contract. Static HTML content remains supported, but custom asynchronous HTML or JavaScript can still be captured mid-update unless it settles through the built-in browser-PDF signals. Also add a concise maintainer comment in PlaywrightPDFRenderer._wait_for_render_ready() so future readiness changes keep that boundary in mind rather than trying to infer arbitrary custom HTML completion from product-agnostic heuristics. Verification: make check; make smoketest
The py3.11 GitHub Actions failure on PR #541 was in `test_playwright_pdf_signal_timeout`. The renderer now uses one shared browser-phase deadline across launch, navigation, and readiness waits, so a tiny timeout budget can expire either while `page.goto()` is still navigating or later inside the readiness pipeline depending on runner speed. The test previously asserted only the readiness-style `timed out` wording, which made it too specific for the new shared-deadline behavior and let CI fail when Playwright surfaced its own `Timeout ... exceeded` navigation message first. Update the assertion to accept either timeout spelling while still verifying the user-facing `Browser PDF rendering failed` contract. Verification: - uv run pytest tests/serverless/test_pdf_renderer.py -q -k "test_playwright_pdf_signal_timeout or test_playwright_pdf_uses_render_timeout_for_browser_launch_and_navigation or test_playwright_pdf_reuses_one_browser_phase_deadline_for_readiness" - make check - make smoketest
Catch Playwright TimeoutError in the browser PDF renderer and translate it into the renderer's own ADRException timeout contract instead of exposing raw Playwright wording such as `Timeout ... exceeded`. Also normalize the readiness-step path that times out inside the in-page `page.evaluate()` promise guard. Those timeouts now surface through the same `Browser PDF rendering failed: <phase> timed out after <render_timeout>s` shape instead of depending on wrapped JavaScript error text. Update the browser-PDF tests to assert that normalized contract directly. Add deterministic unit coverage for both timeout normalization paths and remove one timing-sensitive intermediate assertion from the double-RAF test. That test was assuming the promise would still be pending immediately after startup, which is not guaranteed on a fast runner. Verification: - uv run playwright install chromium - uv run pytest tests/serverless/test_pdf_renderer.py -q - make check - make smoketest
Replace the browser-PDF readiness-step timeout normalization that was matching a formatted substring inside Playwright's wrapped exception text. That approach was still tied to how Playwright rendered the JavaScript error back into Python. The page-side readiness wrapper now returns a tagged timeout result when the shared step budget expires. Python checks that structured result directly and raises the normalized ADRException timeout message without inspecting raw error strings. The existing focused browser-PDF timeout coverage continues to validate the same public API contract while no longer relying on brittle text parsing. Verification: - uv run pytest tests/serverless/test_pdf_renderer.py -q - make check - make smoketest
Tighten the browser-PDF capture overrides for ADR slider layouts. Add keep-together pagination rules for the top slider container and its immediate row wrapper in the Playwright capture CSS. The previous rules protected inner browser-rendered content like images, canvases, plots, and viewers, but still allowed slider headers and controls to split away from the rest of the slider widget during Chromium pagination. Keep the change repo-side in the browser-PDF renderer instead of modifying the installed product templates. This matches the existing capture-style override pattern and keeps the fix local to the PDF export pass. Update focused tests to cover: - slider selectors in the injected capture CSS - effective keep-together behavior under screen media - the unchanged readiness-step set after removing the discarded slider-preset experiment Verification: - uv run pytest tests/serverless/test_pdf_renderer.py -q -k "capture_styles or matches_print_pdf_step_set" - make check - make smoketest - deepdbr smoke export via local_tests/test_sls_fluent_browser_pdf.py with a temporary runtime monkeypatch for the separate installed-product media_auth_hash bug in reports/template_layouts.py
Update uv.lock after the required post-commit `uv sync --upgrade --all-extras` and `uv lock --upgrade` refresh. The only resolved change is the lockfile entry for coverage moving from 7.14.0 to 7.14.1. Verification: - uv sync --upgrade --all-extras - uv lock --upgrade
Summary
Details
The browser-PDF renderer already stages HTML through the serverless template path with print=pdf, which drives the same print-style mode the product uses for PDF-targeted output. In that mode, the export path does not rely on DataTables initialization or active viewer loading, so those readiness waits were just extra product-specific complexity in the consumer.
This change keeps the readiness pipeline focused on the signals that still matter for the print-style bundle:
equestAnimationFrame settle step
Verification
Notes