Repository navigation
Keep the polling filmstrip from queueing in front of the test - #567
Open
vishnuv688 wants to merge 11 commits into
Open
vishnuv688 wants to merge 11 commits into
vishnuv688 wants to merge 11 commits into
Conversation
Without a CDP screencast a driver serves the 200 ms screenshot polls and the test's own requests one at a time, so the polls sat in front of commands and per-action captures (#516: a per-action screenshot 600-665 ms on Android with the poller running, 229 ms without). The recorder now skips a tick while a command or a capture holds the driver, through one bounded gate in core that the adapters' command hooks and captureActionSnapshot open; it replaces the input-only gate, whose click race it covers. Waits and host-side commands open nothing, since the driver is idle between their polls. Each capture's screenshot is kept as a filmstrip frame and the poll that would repeat it is skipped. The recorder's own screenshots and skipped ticks are counted in the DEVTOOLS_CAPTURE_TIMING report so this cost stays visible. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
A tick skipped for a busy driver was dropped, not deferred, so a run of back-to-back commands that take no capture (reads, lookups, wait polls, or any live-mode run on Safari or Appium) could leave the filmstrip without a frame for as long as the run lasted. Once the newest frame is three intervals old the recorder polls anyway, still one shot at a time. An input-dispatching command is never overridden, since a poll inside its dispatch makes the click activate nothing, so the gate tells input windows apart again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#afterCommand returns before closing the window while #injecting, so an injection call from a re-arm (reloadSession, a context change) held the gate to its 3 s bound and replaced the user command's single-field window. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The window closes from the callback the proxy appends, so a wrapped function that never calls it (a builder or scoped API such as actions() or element(), or a function-style custom command that never calls done) held the filmstrip's gate for its full 3 s bound on every call. The window now opens only for an input command, a command the trace vocabulary maps, or a known driver read. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Recorder counters are only drained when a trace is exported, so a live-mode run with DEVTOOLS_CAPTURE_TIMING on kept every shot's duration for the life of the process. The count and total stay exact; the median reads a ring of the newest 2000 shots. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The gap cap counted from the newest frame, usually the previous capture's, so a 1.4 to 2.4 s native capture crossed it partway and the capped poll queued behind the capture's own screenshot, adding nothing and holding the driver in front of the next command. A capture window is now never overridden; its own screenshot is the frame. WDIO runs its hooks for the protocol commands nested in click and setValue, whose single window then dropped the input mark, so elementClick, elementSendKeys, elementClear, performActions and releaseActions count as input, as do Nightwatch's updateValue, setPassword, clickAndHold and releaseMouseButton. Selenium's actions().perform() goes through execute(), so it opens its input window itself. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
In Nightwatch v3 browser.element() builds a scoped element and never calls the appended callback, yet it is listed among the read-only commands, so it held the gate to its bound on every call. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A capped poll still waits behind the busy command and takes its own screenshot time, so the longest gap is about three intervals plus one screenshot, and captures as well as input commands are never overridden. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
vishnuv688
marked this pull request as ready for review
November 10, 2026 12:45
4 of 13 tasks
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What & why
Closes #561. Part of the #516 follow-ups.
With no CDP screencast (Appium, Safari), the filmstrip recorder polls a screenshot every 200 ms, and a driver serves one session's requests one at a time, so polls queued in front of the test's commands and per-action captures. Measured in #516: a per-action screenshot took 600-665 ms on the Android emulator with the poller running, against 229 ms with no recorder.
Lossless changes, default on:
driver-activity.ts) is held by each adapter's command hook and by the per-action capture; the recorder skips a tick while it is held. Waits (whose polls signal on their own) and host-only commands (pause,debug, ...) do not hold it. It replaces the old input-only gate, which covered a subset of commands, so the chromedriver click race stays covered.elementClick/elementSendKeys/performActions, Selenium'sactions().perform()) or a per-action capture, whose own screenshot is the frame.maxBufferFramesand thinning apply as usual.DEVTOOLS_CAPTURE_TIMING=1, the report prints screenshots taken, ticks skipped (and why) and capture screenshots reused as frames.The polling interval and the filmstrip default are unchanged; lossy options stay out of scope.
Type of change
Packages touched
shared(types and contracts)core(framework-agnostic capture/reporting)elements(published element/snapshot API —@wdio/elements)service(WebdriverIO adapter)nightwatch-devtools(Nightwatch adapter)selenium-devtools(Selenium adapter)selenium-devtools-py(Selenium Python adapter)backend(server)app(UI)script(page-injected runtime)trace(Trace mode)Capability matrix
packages/conformance/src/features.tsupdated, and tables regenerated withpnpm docs:capabilities, if this adds or changes user-facing behaviourNo capability change: the filmstrip reasons ("other browsers poll", "poll every 200 ms") still hold.
Notes for reviewers
ScreencastRecorderBase; the adapters only signal command start and end from the hooks they already have. Python has no polling recorder.browser.element()and custom commands get none, so they cannot hold the gate for its 3 s bound.Testing
pnpm build,pnpm typecheck,pnpm test(4090 passed, 1 skipped),pnpm test:ui,pnpm lint,pnpm docs:capabilities --checkare green. New tests fail without the change.DEVTOOLS_CAPTURE_TIMING=1:Filmstrip frames in the traces are equal or more (Nightwatch 7 → 9, Selenium 4 → 5). The iOS simulator gain is small because its polls are fast (about 120 ms).
Needs checking: the Android emulator, where #516 measured the 600-665 ms:
DEVTOOLS_MODE=trace DEVTOOLS_CAPTURE_TIMING=1 pnpm demo:selenium:mobileandpnpm demo:nightwatch:mobile, comparing the per-capture screenshot median and thefilmstrip recorder:line.Screenshots / recordings
None.
🤖 Generated with Claude Code