Repository navigation
fix: activate a page in a background tab before a check or save command - #1272
Merged
Merged
Conversation
🦋 Changeset detectedLatest commit: 35f959f The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
dprevost-LMI
marked this pull request as ready for review
October 8, 2026 11:48
|
After newWindow(), Chrome brings the new tab to the front, so page A is
in a background tab (`document.visibilityState` is `hidden`). A hidden
tab reports `outerHeight` = `innerHeight`, and the file name uses the
outer size on desktop. On Linux CI the second check got another name
(1366x625 instead of 1366x768), saved a new baseline, and passed
without a comparison.
- The local Chrome v10 config (also used by the Jasmine suite) names
the files without {width}x{height}: it runs one fixed Chrome.
- The test checks that the second check uses the same baseline file as
the first one, so a different name now fails instead of passing.
Page B is blue, so a screenshot of the tab in front cannot match the
red page A.
Checked in a Linux container (Chrome 155): the test passes with one
baseline, and with the old name format it now fails on the file name.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
In a WebDriver BiDi session, a page in a background tab (for example
after `browser.newWindow()`, which in WebdriverIO v10 does not switch
to the new tab) is `hidden`:
- it reports outerHeight = innerHeight, so the desktop file name
changes, and with autoSaveBaseline the check saves a new baseline and
returns 0 without comparing;
- its screenshot can hang until the bidiResponseTimeout (180 s).
A WebDriver Classic screenshot does not have these problems, because
ChromeDriver brings the tab to the front.
Before each command (wrapWithContext, so single browser, multiremote,
page and element commands), activate the browsing context of the
browser when `document.visibilityState` is `hidden`. Mobile, native
context and Classic sessions are not changed. A failed activation only
logs a warning.
Tests: unit tests for the helper and the call order; the e2e newWindow
test now also checks that page A is visible after the check. Checked in
a Linux container (Chrome 155): the suite passes; with tab B left open
it passes in 5 s (before: 180 s hang); with {width}x{height} in the
file name, both checks use one baseline (before: 1366x625 vs 1366x768).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Measured on Linux and macOS with the same Chrome 155.0.8059.39, and with Firefox 157 (BiDi) on Linux: - Chrome on Linux: a page in a background tab reports outerHeight = innerHeight, and after a DOM change `Page.captureScreenshot` never returns. The hang also happens with CDP only (Puppeteer), so it is a Chromium bug, not a BiDi or WebdriverIO bug. - Chrome on macOS and Firefox on Linux: neither problem. So activate the page only in BiDi sessions of Chromium-based browsers (Chrome, Chromium, Edge). It is not limited to Linux, because Windows was not tested and on macOS the activation does no harm (ChromeDriver does the same for a Classic screenshot). The helper is now fully in try/catch, so it can never make a check or save command fail, and its JSDoc explains the problem, the evidence, the scope and the side effect. Unit tests for Edge/Chromium, Firefox and a failed visibility check. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The tab activation before a check keeps the window size stable, so the names with the size stay the same. With the size in the name, the newWindow() test fails when the activation does not work. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The first and the second check of the same page return the same compare data, so toEqual checks the file name, the folders and the mismatch in one step. The interface, the type guard and the helper are not needed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
dprevost-LMI
force-pushed
the
fix/activate-hidden-tab
branch
from
October 8, 2026 15:47
9dcc21f to
35f959f
Compare
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.
Summary
In a WebDriver BiDi session with a Chromium-based browser, a check or save command on a page in a background tab could pass without a comparison, or hang for 180 seconds. The visual service now brings that page to the front before the command, as ChromeDriver does for a WebDriver Classic screenshot.
Replaces #1270 (its test commit is included here).
Problem
In WebdriverIO v10,
browser.newWindow()does not switch to the new tab, but Chrome brings the new tab to the front. The page of the browser commands is then in a background tab (document.visibilityStateishidden). On Linux headless Chrome (the CI platform):outerHeight=innerHeight, and the desktop file name uses the outer size. WithautoSaveBaseline(the default), the check saved a new baseline and returned 0. Found in CI: thenewWindow()test saved…-1366x625.pngand…-1366x768.pngfor the same page and never compared.browsingContext.captureScreenshoton the hidden page never returned, untilbidiResponseTimeout(180 s by default). Found in CI on refactor: find a stale ignore element again with WebdriverIO #1254.WebDriver Classic does not have these problems, because ChromeDriver brings the tab to the front before a screenshot (
ActivateWebViewinExecuteScreenshot). Our BiDi path did not.Root cause: a Chromium bug
The hang also happens with the Chrome DevTools Protocol only, without WebDriver:
Page.captureScreenshoton a page in a background tab never returns when the page had no new frame for about 300 ms. Pure CDP reproduction (Puppeteer, Chrome 154, 10 runs per mode):Page.bringToFrontFirefox 157 (BiDi) does not hang.
Reported:
browsingContext.captureScreenshotdo not say what to do for a hidden page):browsingContext.captureScreenshot: what should happen for a navigable that is not visible (background tab)? w3c/webdriver-bidi#1176Changes
Fix (
@wdio/visual-service, patch changeset)activateHiddenBrowsingContext(): ifdocument.visibilityStateishidden, callbrowsingContext.activatefor the current context, and log it. Any error only logs a warning, so the command never fails because of this step.executecall per command.wrapWithContext(), so before every check and save command (single browser, multiremote, page and element), before the page size is read and the screenshot is taken.Test
newWindow()e2e test checks that the second check uses the same baseline file as the first one (so a new name fails instead of passing), that the mismatch is 0, and that page A isvisibleafter the check. Page B is blue and page A is red, so a screenshot of the wrong tab fails.{width}x{height}: it runs one fixed Chrome.executeerror. A unit test for the call order inwrapWithContext(). The oldwrapWithContexttests passed the browser with the wrong key (browserinstead ofbrowserInstance); corrected.Test
pnpm test(lint, types, unit): pass.{width}x{height}in the file nameNotes
bidiResponseTimeout(WebdriverIO, 180 s by default) already ends a hung command, so the service does not add another timeout. This fix removes the cause.🤖 Generated with Claude Code