recon_gen.common.browser.screenshot

ScreenshotHarness: walk a deployed App’s tree and capture screenshots systematically.

Three capture modes:

  • capture_all_sheets() — one full-page screenshot per sheet. Returns dict[Sheet, Path] keyed by the Sheet object ref so handbook templates can look up by Sheet, not by sheet_id string. Filenames remain {sheet_id}.png for stable on-disk names.

  • capture_per_visual(sheet) — one screenshot per visual on the sheet, scroll-into-view + element crop. Returns dict[VisualLike, Path] keyed by the Visual object ref; filenames derive from each visual’s resolved visual_id.

  • capture_with_state(parameter_values) — apply parameter values via URL hash, then capture every sheet. Returns dict[Sheet, Path] keyed by Sheet ref.

Sheet/Visual object keys (M.1.10 / F8) means callers can do paths[my_sheet] from the same App they constructed, instead of carrying a parallel sheet_id string around. The on-disk filenames stay sheet_id-derived so previously-generated images overwrite cleanly across runs.

Why this matters for Phase M: when whitelabel-V2 swaps personas (Sasquatch → Acme Bank), the docs need Acme-shaped screenshots. Manual capture doesn’t scale; capture_all_sheets() regenerates the screenshot set against the new persona’s deploy in one call. The persona dataclass drives BOTH the seed generator AND the screenshot pipeline in lockstep.

Same Page-based infra as TreeValidator; different consumer — they could share a DeployedTreeContext helper if patterns crystallize. For now each tool has its own class.

Functions

capture_deployed_app(app, *, embed_url, ...)

Walk a deployed App's tree and capture full-page PNGs per sheet.

Classes

ScreenshotHarness(app, page, output_dir[, ...])

Walk an App + Page; produce a directory of named screenshots.

class recon_gen.common.browser.screenshot.ScreenshotHarness(app, page, output_dir, embed_url=None, timeout_ms=30000)[source]

Bases: object

Walk an App + Page; produce a directory of named screenshots.

Parameters:
  • app (App)

  • page (Any)

  • output_dir (Path)

  • embed_url (str | None)

  • timeout_ms (int)

app: App
capture_all_sheets()[source]

One full-page screenshot per sheet on the App’s analysis.

Returns dict[Sheet, Path] keyed by the Sheet object ref. Filenames remain {sheet_id}.png so re-running overwrites the same on-disk file; only the in-memory key shape changed (M.1.10 / F8).

Return type:

dict[Sheet, Path]

capture_per_visual(sheet)[source]

One screenshot per visual on sheet, element-cropped.

Returns dict[VisualLike, Path] keyed by the Visual object ref. Skips visuals without a resolved visual_id — the auto-ID walker hasn’t run, or the visual is a factory wrapper without an explicit id. Caller should app.emit_analysis() once before to resolve auto-IDs (the validator + this harness usually share a session-scoped fixture that already does that).

Return type:

dict[VisualLike, Path]

Parameters:

sheet (Sheet)

capture_with_state(*, parameter_values, suffix='state')[source]

Re-load the dashboard with parameter values applied via URL hash (#p.<name>=<value>), then capture every sheet.

Returns dict[Sheet, Path] keyed by Sheet object ref. Filenames suffix-tagged so multiple states don’t overwrite each other: {sheet_id}-{suffix}.png. Pass distinct suffix values per state.

Per the project memory project_qs_url_parameter_no_control_sync, the on-screen control widget may not reflect the URL value even when the data is filtered. The screenshot captures what the analyst SEES, which is the rendered visual state — that’s the right semantics for handbook screenshots.

Requires embed_url set on construction.

Return type:

dict[Sheet, Path]

Parameters:
embed_url: str | None = None
output_dir: Path
page: Any
timeout_ms: int = 30000
recon_gen.common.browser.screenshot.capture_deployed_app(app, *, embed_url, output_dir, viewport=(1280, 900), initial_settle_ms=10000, per_sheet_settle_ms=8000, page_timeout_ms=120000, headless=True, url_params=None)[source]

Walk a deployed App’s tree and capture full-page PNGs per sheet.

Differs from ScreenshotHarness.capture_all_sheets() (which assumes a Page is already open + the test fixture has set the viewport): this helper owns the browser lifecycle and uses settle timeouts instead of wait_for_visuals_present. Matches the proven shape of the pre-Q ad-hoc capture scripts. The forgiving settle timing tolerates QS spinner-forever cases (Operational Footguns) by capturing whatever painted in the window — a spinner-frame in the PNG is acceptable for handbook docs since the next run usually clears it.

url_params: optional {paramName: value} dict appended to the embed URL as #p.<name>=<value>&p.<name>=<value>... so the captured page renders with overridden parameter values. Used by the screenshots CLI to inject --date-from / --date-to when the seed anchors at a date the dashboard’s default rolling-window controls don’t span. Per the QuickSight URL-param control-sync limitation (see project memory project_qs_url_parameter_no_control_sync), the on-screen control widget may still show the default value; the data is filtered by the URL value, which is what matters for screenshots. Values are URL-encoded.

Returns dict[Sheet, Path] keyed by Sheet object ref. Filenames are {sheet_id}.png so re-running overwrites the same on-disk file.

Return type:

dict[Sheet, Path]

Parameters:
  • app (App)

  • embed_url (str)

  • output_dir (Path)

  • viewport (tuple[int, int])

  • initial_settle_ms (int)

  • per_sheet_settle_ms (int)

  • page_timeout_ms (int)

  • headless (bool)

  • url_params (dict[str, str] | None)