Skip to content

feat: add the snapshot and dom tap commands for reading AUT DOM#34255

Open
davidr-cy wants to merge 8 commits into
davidr/feat/tap-cli/tap-statusfrom
davidr/feat/tap-cli/tap-snapshots
Open

feat: add the snapshot and dom tap commands for reading AUT DOM#34255
davidr-cy wants to merge 8 commits into
davidr/feat/tap-cli/tap-statusfrom
davidr/feat/tap-cli/tap-snapshots

Conversation

@davidr-cy

@davidr-cy davidr-cy commented Jul 8, 2026

Copy link
Copy Markdown
Contributor
  • Closes N/A — part of the experimental cypress tap CLI (internal dev tooling); no separate public issue.

Additional details

Adds two schema-driven cypress tap subcommands that let an agent read the app-under-test's DOM straight from the command log, so failures can be diagnosed from the terminal:

  • tap snapshot <test> <command> — reads the DOM snapshot the driver captured for a command-log entry (the same snapshots the reporter shows on hover). Returns the page HTML, or with --selector just the matching elements; the element(s) the command acted on (the [data-cypress-el] subject); the before/after snapshot listing; and, with --styles, the page stylesheets. --at selects which snapshot; --max-chars caps the payload (default 30000, flags truncated).
  • tap dom [selector] — reads the AUT's current live document, for inspecting an interactive session.

Implementation notes:

  • Historical snapshots are read from the live runner via getSnapshotPropsForLog — the reporter's own hover seam — not getTestsState, whose serialization blacklists snapshots. The handler adopts the detached clone, reads outerHTML, and never mutates it, so it can't disturb the visible AUT.
  • test/command are positional params, not options: commander 6 stores options as properties, so an option named command clashes with its built-in command property.
  • Both commands cap returned HTML and every failure is a typed TapCommandError; a zero-count selector match is a result, not an error.
  • No CLI changes — the commands are discovered from the schema. knip.json gets per-file types ignores matching the existing tap convention.

Steps to test

Manual end-to-end (open mode, Chrome-family or Electron browser):

  1. Start an instance: node scripts/cypress.js open --project <project> --e2e --browser electron
  2. cypress tap run <spec>, then poll cypress tap status until it finishes.
  3. cypress tap tests → pick a failing test id; cypress tap commands --test <id> → pick a command id.
  4. cypress tap snapshot <test> <command> --selector '<css>' to inspect the DOM at that command; cypress tap dom '<css>' to inspect the live page.

Validated end-to-end against a real instance (Cypress 15.19.0): element-not-found, wrong-text assertion, and visibility-regression scenarios each diagnosed from the CLI, plus the full typed-error model.

How has the user experience changed?

No change to end-user-facing Cypress behavior — this is internal cypress tap developer tooling. Two new subcommands appear in cypress tap help when talking to a running instance.

PR Tasks

  • [na] Is there an associated issue with maintainer approval for PR submission?
  • Have tests been added/updated?
  • [na] Has a PR for user-facing changes been opened in cypress-documentation?
  • [na] Have API changes been updated in the type definitions?

Note

Medium Risk
Programmatic pin mutates the live AUT DOM and coordinates reporter/snapshot store state; CDP frame reads are scoped to internal tap tooling with caps and typed errors, but incorrect pin restore could confuse an open interactive session.

Overview
Adds CLI-native cypress tap frame with dom, aria, and inspect subcommands that read the app-under-test over Chrome DevTools Protocol (isolated world + accessibility APIs), because the in-page tap binding cannot reach those domains. The CLI locates the AUT iframe by its Your project: frame name, and withTapSession now exposes client / sessionId for these extractors.

Introduces a schema tap pin command that renders a command-log DOM snapshot into the live AUT (via the app’s native pin path), captures pre-pin DOM for restore, supports --at / --clear, stale-pin reconciliation, and syncs the reporter when pinning from the CLI. tap status / hidden run-state now include an optional pinned field.

Reporter and event-manager wiring adds reporter:snapshot:pinned / snapshotPinned so programmatic pins match user-driven pin UI state.

Reviewed by Cursor Bugbot for commit d808bce. Bugbot is set up for automated code reviews on this repo. Configure here.

Comment thread packages/app/src/tap/commands/snapshot.ts Outdated
@cypress

cypress Bot commented Jul 8, 2026

Copy link
Copy Markdown

cypress    Run #72349

Run Properties:  status check failed Failed #72349  •  git commit d808bce873: feat: reflect the tap pin in the runner UI and move it with --at
Project cypress
Branch Review davidr/feat/tap-cli/tap-snapshots
Run status status check failed Failed #72349
Run duration 25m 36s
Commit git commit d808bce873: feat: reflect the tap pin in the runner UI and move it with --at
Committer David Rowe
View all properties for this run ↗︎

Test results
Tests that failed  Failures 7
Tests that were flaky  Flaky 3
Tests that did not run due to a developer annotating a test with .skip  Pending 988
Tests that did not run due to a failure in a mocha hook  Skipped 19
Tests that passed  Passing 15351
View all changes introduced in this branch ↗︎

Warning

Partial Report: The results for the Application Quality reports may be incomplete.

UI Coverage  64%
  Untested elements 27  
  Tested elements 48  
Accessibility  99.02%
  Failed rules  0 critical   3 serious   1 moderate   0 minor
  Failed elements 18  

Tests for review

Failed  src/tap/commands/run-state.cy.ts • 5 failed tests • app-ct

View Output

Test Artifacts
tap/commands/run-state > reports the spec list (no run) without the run-only fields Test Replay
tap/commands/run-state > reports totalSpecs of 0 when the specs global is absent Test Replay
tap/commands/run-state > reports a run in progress as running, with the active spec and partial results Test Replay
tap/commands/run-state > reports a settled run with no failures as passed Test Replay
tap/commands/run-state > falls back to a null spec when the active spec path is unavailable Test Replay
Failed  src/tap/commands/commands.cy.ts • 1 failed test • app-ct

View Output

Test Artifacts
tap/commands/commands > fails dispatch without reading the runner when the required test option is missing Test Replay
Failed  cypress/e2e/e2e/dom_hitbox.cy.js • 0 failed tests • 5x-driver-electron

View Output

Test Artifacts
Failed  cypress/e2e/e2e/origin/commands/actions.cy.ts • 0 failed tests • 5x-driver-electron

View Output

Test Artifacts
Failed  cypress/e2e/commands/actions/clear.cy.ts • 0 failed tests • 5x-driver-electron

View Output

Test Artifacts

The first 5 failed specs are shown, see all 800 specs in Cypress Cloud.

Flakiness  runner/ct-framework-errors.cy.ts • 1 flaky test • app-e2e

View Output

Test Artifacts
Angular 18 > error conditions Test Replay Screenshots
Flakiness  create-from-component.cy.ts • 1 flaky test • app-e2e

View Output

Test Artifacts
... > Shows create from component card for Vue projects Test Replay Screenshots
Flakiness  commands/net_stubbing.cy.ts • 1 flaky test • 5x-driver-electron

View Output

Test Artifacts
... > can timeout when retrieving upstream response Test Replay

Comment thread cli/lib/tap/frame/index.ts
Comment thread packages/app/src/tap/commands/snapshot-pin.ts
@davidr-cy davidr-cy force-pushed the davidr/feat/tap-cli/tap-status branch from 78fb9cb to 8ed2606 Compare July 10, 2026 01:20
@davidr-cy davidr-cy force-pushed the davidr/feat/tap-cli/tap-snapshots branch from ab6bd03 to a4e16ac Compare July 10, 2026 01:20
Comment thread cli/lib/tap/aut-frame.ts
@davidr-cy davidr-cy force-pushed the davidr/feat/tap-cli/tap-snapshots branch from a4e16ac to ddcd8ac Compare July 10, 2026 15:05
@davidr-cy davidr-cy force-pushed the davidr/feat/tap-cli/tap-status branch from 8ed2606 to 92dbaba Compare July 10, 2026 15:05
Comment thread packages/app/src/tap/commands/pin.ts
Comment thread packages/app/src/tap/commands/snapshot-pin.ts
@davidr-cy davidr-cy force-pushed the davidr/feat/tap-cli/tap-snapshots branch from ddcd8ac to a209a74 Compare July 10, 2026 15:13
@davidr-cy davidr-cy force-pushed the davidr/feat/tap-cli/tap-status branch from 92dbaba to 19f7898 Compare July 10, 2026 15:13
Comment thread packages/app/src/tap/commands/pin.ts
@davidr-cy davidr-cy force-pushed the davidr/feat/tap-cli/tap-snapshots branch from a209a74 to c196607 Compare July 10, 2026 19:12
@davidr-cy davidr-cy force-pushed the davidr/feat/tap-cli/tap-status branch from 19f7898 to 07941de Compare July 10, 2026 19:12
@davidr-cy davidr-cy force-pushed the davidr/feat/tap-cli/tap-snapshots branch from c196607 to 934ad77 Compare July 13, 2026 14:19
@davidr-cy davidr-cy force-pushed the davidr/feat/tap-cli/tap-status branch from 07941de to 7647a01 Compare July 13, 2026 14:19
Comment thread packages/app/src/tap/commands/pin.ts
Comment thread packages/app/src/tap/commands/pin.ts
@davidr-cy davidr-cy force-pushed the davidr/feat/tap-cli/tap-status branch from 7647a01 to 9bc0eb3 Compare July 13, 2026 15:11
@davidr-cy davidr-cy force-pushed the davidr/feat/tap-cli/tap-snapshots branch from 934ad77 to 52c822d Compare July 13, 2026 15:11
@davidr-cy davidr-cy force-pushed the davidr/feat/tap-cli/tap-status branch from 9bc0eb3 to 1177708 Compare July 13, 2026 16:48
@davidr-cy davidr-cy force-pushed the davidr/feat/tap-cli/tap-snapshots branch from 52c822d to 7d67ba1 Compare July 13, 2026 16:48

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want reviews to match your repository better? Bugbot Learning can learn team-specific rules from PR activity. A team admin can enable Learning in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 7d67ba1. Configure here.

Comment thread packages/app/src/tap/commands/pin.ts
Two schema-driven subcommands let an agent inspect the app under test:

- `snapshot <test> <command>` reads the DOM snapshot the driver captured
  for a command-log entry — the same snapshots the reporter shows on
  hover. It returns the page HTML (or, with --selector, just the matching
  elements), the element(s) the command acted on (the [data-cypress-el]
  subject), the before/after snapshot listing, and optionally the page
  stylesheets. Reads the live runner via getSnapshotPropsForLog rather
  than getTestsState, whose serialization blacklists snapshots.

- `dom [selector]` reads the AUT's current live document for interactive
  inspection.

Both cap returned HTML at a character budget (default 30000) and flag
truncation, so an unscoped read of a heavy page can't flood the caller.
Test and command ids are positional params on snapshot: commander 6
stores options as properties, so an option named `command` clashes with
its built-in `command` property.
…mands

Reads the app-under-test DOM directly over CDP instead of serializing it
in the runner page:

- Removes the app-side `snapshot` and `dom` tap commands (and their
  helpers/specs). Historical DOM will come from pinning a command (next),
  and live DOM now comes from CDP.
- Extends `withTapSession` to expose the attached CDP client + session so
  extractors can run Page/DOM/CSS/Accessibility against the AUT frame.
- Adds `resolveAutFrame`: the AUT is a same-process child frame of the
  runner page (name `Your project: '<name>'`) — verified empirically to
  be a child frame, not an OOPIF, so one attached session reaches it.
- Adds the CLI-native `tap frame` command group and `frame dom`, which
  reads the current AUT DOM (whole page or a selector's matches) via an
  isolated world, capping output browser-side.

`frame ax`, `frame inspect`, and the binding `pin` command follow. The
frozen getSchema/exec contract is unchanged; `frame` is CLI-native.
`cypress tap frame ax [selector]` returns the app-under-test's
accessibility tree — roles, names, values, and key states — from
Accessibility.getFullAXTree scoped to the AUT frame, projected to a
compact indented tree (noise roles collapsed, ignored nodes dropped)
with a --max-nodes cap. A selector roots the read at a subtree by
matching the element's backend node id; a non-matching selector is an
empty result, a malformed one is INVALID_SELECTOR.
`cypress tap pin <test> <command> [--at]` renders a command's captured
DOM snapshot into the live app-under-test frame (via the reporter's
restore path), so the CDP `tap frame` commands can read that historical
moment — its HTML, accessibility tree, computed styles — deterministically
instead of whatever the live frame happens to show. `pin --clear` restores
the pre-pin DOM; it is idempotent.

The pin is app state that persists between tap calls (each CLI call is a
fresh CDP connection, so no handle crosses them). Guards: NO_RUN,
RUN_IN_PROGRESS, ALREADY_PINNED (one pin at a time), TEST_NOT_FOUND,
COMMAND_NOT_FOUND, SNAPSHOT_UNAVAILABLE, SNAPSHOT_NOT_FOUND. The snapshot
store's pinned flag is set so the reporter's hover machinery can't clobber
the pinned DOM.
- `cypress tap frame inspect <selector>` returns one element's tag,
  attributes, curated computed styles, box rect, and accessibility node.
  It reads the element's info in an isolated world (getComputedStyle /
  getBoundingClientRect on the element) and takes only the AX node from
  CDP — avoiding the brittle requestNode/nodeId path.
- `run-state` (and therefore `status`) now reports the currently pinned
  command, so a pin is always visible and a stranded one is recoverable.
Align with Playwright's naming for the accessibility tree (its ariaSnapshot
/ toMatchAriaSnapshot API and the "aria snapshot" its agent tooling
returns). `tap frame ax` becomes `tap frame aria`; the extractor, its
result types, and the per-element accessibility field on `frame inspect`
follow suit. CDP protocol identifiers (AXNode, getFullAXTree) keep their
names.
A pin leaked across spec switches and re-runs: the module-level pin state
survived, so `status` kept reporting a pin that no longer existed and a
`pin --clear` could restore a stale snapshot over the new run's DOM.

Reconcile the pin against the current run before every pin/status/clear:
the pin is live only while the exact snapshot object it rendered is still
the command's current snapshot. A re-run re-captures fresh objects (even
when the log id is reused) and a spec switch drops the command entirely —
both fail the identity check, so the stale pin is released without
restoring its now-gone DOM.
Route the pin through the app's native pin so the AUT shows the pinned banner/controls and the reporter command log reflects it — the pinned command highlights and its test opens (mirrors the existing unpin notification). Re-pinning the same command with a new --at now moves the pin to that snapshot in place instead of erroring ALREADY_PINNED; a different command still requires a clear first.
@davidr-cy davidr-cy force-pushed the davidr/feat/tap-cli/tap-snapshots branch from 7d67ba1 to d808bce Compare July 13, 2026 19:29
@davidr-cy davidr-cy force-pushed the davidr/feat/tap-cli/tap-status branch from 1177708 to f3c2f7c Compare July 13, 2026 19:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant