bug: the visual service fails on Android Chrome through Appium 3 in the default WebDriver BiDi session (workaround: `wdio:enforceWebDriverClassic`)
维护者通常 1 天内回复
@plum117 已经在做这个了。
开始于 2026年10月9日。
评估
- 难度
- 4/5
- 预计耗时
- 3-5 天
- 新手友好度
- 48/100
- Issue 类型
- 缺陷
- 描述清晰度
- 基本清楚
- 活跃度
- 活跃
- 技术栈
- android, typescript
调研方向
Start with the README and reproduce the failure using probe.mjs and test/visual.spec.js with MODE=bidi and MODE=classic. Inspect the visual service before hook and matcher or instance setup paths mentioned in the report. Done means Android Chrome users see the documented classic-session workaround and a clear warning instead of missing matcher errors.
由索引模型根据 Issue 内容生成。
描述
Environment (please complete the following information):
- Node.js version: 24.20.0
- NPM version: 11.19.0
- Browser name and version: Chrome 133.0.6943.137 on an Android 16 emulator (Pixel 9 Pro, API 36)
- Platform name and version: macOS 27; Appium 3.8.0, UiAutomator2 driver 8.7.0 (appium-android-driver 14.2.0), Chromedriver 133.0.6943.141
- WebdriverIO version: 9.32.0 (also 10.0.0-alpha.163)
@wdio/visual-serviceversion: 10.1.0 (also thewdio0-v10-compliancebranch of #1213)
Config of WebdriverIO + @wdio/visual-service
capabilities.js
// MODE=bidi (default), classic or forward
const mode = process.env.MODE ?? 'bidi'
export const capabilities = {
platformName: 'Android',
browserName: 'Chrome',
'appium:automationName': 'UiAutomator2',
'appium:deviceName': process.env.DEVICE ?? 'emulator-5554',
'appium:newCommandTimeout': 240,
'appium:chromeOptions': { args: ['--no-first-run', '--disable-fre'] },
...(mode === 'classic' ? { 'wdio:enforceWebDriverClassic': true } : {}),
...(mode === 'forward' ? { 'appium:chromedriverForwardBiDi': true } : {}),
}
// The Android emulator reaches the host at 10.0.2.2
export const pageUrl = 'http://10.0.2.2:8765/mobile.html'
export { mode }
wdio.conf.js
import { capabilities, mode } from './capabilities.js'
export const config = {
runner: 'local',
hostname: '127.0.0.1',
port: 4723,
specs: ['./test/visual.spec.js'],
capabilities: [capabilities],
framework: 'mocha',
reporters: ['spec'],
services: [['visual', {
baselineFolder: `./.tmp/${mode}/baseline`,
screenshotPath: `./.tmp/${mode}/actual`,
}]],
logLevel: 'warn',
connectionRetryCount: 0,
mochaOpts: { timeout: 240000 },
}
Describe the bug
On Android Chrome through Appium 3, the visual service does not work in the default session. WebdriverIO opens a WebDriver BiDi session (browser.isBidi is true), but Appium does not support the browser BiDi commands in this session:
browser.url()fails:WebDriver Bidi command "browsingContext.navigate" failed with error: unknown method - Method has not yet been implemented- every
browser.execute()fails:WebDriver Bidi command "script.callFunction" failed with error: unknown command
The visual service runs all its client-side scripts with browser.execute(), so all the visual commands and matchers fail. With WebdriverIO 9 and the published 10.1.0, the service before hook fails (Error getting mobile screen size), the matchers are not added, and the user only sees expect(...).toMatchScreenSnapshot is not a function.
This is not a bug in the service code. The workaround is 'wdio:enforceWebDriverClassic': true in the capabilities: then all the tests pass. This repository uses it in its Android web configs since #417, but the README does not tell users.
WebdriverIO v10 requires Appium 3, so all the users who upgrade will get this problem on Android Chrome.
To Reproduce
A small repro: a WebdriverIO-only script (probe.mjs, no service) and a visual test (test/visual.spec.js). Steps:
- Start an Android emulator with Chrome (
emulator-5554). npm i -g appium@3,appium driver install uiautomator2, thenappium --port 4723 --allow-insecure "uiautomator2:chromedriver_autodownload".npm install, thennpm run page(serveswww/on port 8765; the emulator openshttp://10.0.2.2:8765/mobile.html).MODE=bidi npm run probe,MODE=bidi npm test, and the same withMODE=classicandMODE=forward. RunMODE=classic npm testtwo times: the first run saves the baselines.
package.json
{
"name": "wdio-appium3-android-bidi-repro",
"private": true,
"type": "module",
"scripts": {
"page": "python3 -m http.server 8765 --bind 127.0.0.1 --directory www",
"test": "wdio run wdio.conf.js",
"probe": "node probe.mjs"
},
"devDependencies": {
"@wdio/cli": "^9.32.0",
"@wdio/globals": "^9.31.3",
"@wdio/local-runner": "^9.32.0",
"@wdio/mocha-framework": "^9.32.0",
"@wdio/spec-reporter": "^9.32.0",
"@wdio/visual-service": "^10.1.0",
"webdriverio": "^9.32.0"
}
}
probe.mjs
// No visual service, no test runner: shows the WebdriverIO + Appium result only.
// Usage: MODE=bidi|classic|forward node probe.mjs
import { remote } from 'webdriverio'
import { capabilities, mode, pageUrl } from './capabilities.js'
const browser = await remote({ hostname: '127.0.0.1', port: 4723, logLevel: 'error', capabilities })
const run = async (name, fn) => {
try {
console.log(`${name}: OK ${JSON.stringify(await fn())?.slice(0, 60) ?? ''}`)
} catch (err) {
console.log(`${name}: ERROR ${err.message.split('\n')[0].slice(0, 150)}`)
}
}
try {
console.log(`mode ${mode}, isBidi: ${browser.isBidi}`)
await run('url()', async () => { await browser.url(pageUrl) })
await run('getUrl()', () => browser.getUrl())
await run('execute(() => document.title)', () => browser.execute(() => document.title))
await run('executeScript("return document.title", [])', () => browser.executeScript('return document.title', []))
} finally {
await browser.deleteSession()
}
test/visual.spec.js
import { browser, $, expect } from '@wdio/globals'
import { pageUrl } from '../capabilities.js'
describe('Android Chrome through Appium 3', () => {
it('opens the page', async () => {
console.log(`isBidi: ${browser.isBidi}`)
await browser.url(pageUrl)
await expect($('h1')).toHaveText('Visual test')
})
it('matches the screen', async () => {
await expect(browser).toMatchScreenSnapshot('screen')
})
it('matches an element', async () => {
await expect($('#target')).toMatchElementSnapshot('target')
})
})
www/mobile.html
<!doctype html>
<html>
<head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>Mobile</title>
<style>
body { margin: 0; background: #fff; font-family: sans-serif; }
h1 { margin: 16px; font-size: 24px; color: #1b5e20; }
#target { margin: 16px; width: 200px; height: 100px; background: #388e3c; border: 4px solid #1b5e20; }
</style>
</head>
<body><h1>Visual test</h1><div id="target"></div></body>
</html>
Results (the same with WebdriverIO 9.32.0 and 10.0.0-alpha.163):
probe.mjs |
bidi (default) |
classic |
forward (appium:chromedriverForwardBiDi) |
|---|---|---|---|
isBidi |
true |
false |
true |
url() |
error: browsingContext.navigate … unknown method |
OK | error (same) |
execute(() => document.title) |
error: script.callFunction … unknown command |
"Mobile" |
error (same) |
executeScript('return document.title', []) |
OK | OK | OK |
npm test (visual service) |
3 of 3 fail | 3 of 3 pass | 3 of 3 fail |
Expected behavior
The visual service works on Android Chrome through Appium 3 in the default WebdriverIO session, or the docs tell users to use 'wdio:enforceWebDriverClassic': true, and the service gives a clear error.
Log
WebdriverIO 9.32.0 with the published 10.1.0, MODE=bidi:
WARN webdriverio:ShadowRootManager: Could not register shadow root preload script, shadow DOM will not be pierced in this session: WebDriver Bidi command "script.addPreloadScript" failed with error: unknown command
WARN @wdio/visual-service:@wdio/image-comparison-core:utils: Error getting mobile screen size:
Error: WebDriver Bidi command "script.callFunction" failed with error: unknown command - The requested resource could not be found, or a request was received using an HTTP method that is not supported by the mapped resource.
ERROR @wdio/utils:shim: Error: WebDriver Bidi command "script.callFunction" failed with error: unknown command - ...
1) Android Chrome through Appium 3 opens the page
WebDriver Bidi command "browsingContext.navigate" failed with error: unknown method - Method has not yet been implemented
2) Android Chrome through Appium 3 matches the screen
TypeError: expect(...).toMatchScreenSnapshot is not a function
3) Android Chrome through Appium 3 matches an element
TypeError: expect(...).toMatchElementSnapshot is not a function
Additional context
What I found about the cause (local tests on the emulator):
- WebdriverIO adds
webSocketUrl: trueto every session that has abrowserName, except Safari and'wdio:enforceWebDriverClassic': true(packages/webdriver/src/utils.ts). There is no exception for Appium. - Since Appium 2.5.0, the Appium server sends back its own BiDi URL when the client asks for one. UiAutomator2 does not forward the BiDi commands to Chromedriver by default, so
browsingContext.navigateandscript.callFunctionare not supported. An Appium maintainer said the same in webdriverio/webdriverio#14600. - With Appium 2.19.0 (UiAutomator2 4.2.9),
execute()also fails in this BiDi session, buturl()works: WebdriverIO uses the ClassicnavigateTo()whenbrowsingContext.navigatefails withunknown error(url.ts). Appium 3 sendsunknown method, so this fallback does not apply. Thus the visual service already fails with Appium 2.5+ in BiDi; Appium 3 also breaksurl(). - The option
'appium:chromedriverForwardBiDi': truemust forward BiDi to Chromedriver, but it has no effect in a browser session:startChromeSession()does not give the context name tosetupNewChromedriver(), socacheChromedriverCaps()returns before it saves the Chromedriver BiDi URL. (The webview path, line 292, gives it.) Chromedriver did return awebSocketUrl. - With that line patched locally, Appium forwards BiDi to Chromedriver and
url()works, butexecute()fails withno such frame - Context CHROMIUM not found: on mobile, the WebdriverIO context manager uses the Appium context name fromgetContext()as the current context (context.ts), and BiDi uses it as the browsing context ID. Afterbrowser.switchToWindow(await browser.getWindowHandle()),execute()works, and the visual service passes like in Classic. In this real BiDi session,execute('mobile: deviceInfo')fails withReferenceError: deviceInfo is not defined, andexecuteScript('mobile: deviceInfo', [])works (this confirms the change of #1220).
iOS Safari through Appium 3 (XCUITest) is not affected: WebdriverIO does not ask for BiDi for Safari.
Proposed work in this repository:
- README: for Android Chrome through Appium, set
'wdio:enforceWebDriverClassic': true(WebdriverIO v9 and v10). - When the matchers or the instance data cannot be set up because of this, give a clear warning with the workaround, not
toMatchScreenSnapshot is not a function.
The root causes are in WebdriverIO (points 1, 3, 5) and in appium-android-driver (point 4). Issues there are not opened yet.
Follow-up
The default BiDi session works only when all of the fixes below are done. A partial fix does not help: for example, with only the Appium fix, url() works but every execute() still fails.
This repository (closes this issue)
- README: for Android Chrome through Appium, set
'wdio:enforceWebDriverClassic': true(WebdriverIO v9 and v10). - A clear warning with this workaround when the service cannot set up in an Android BiDi session (not
toMatchScreenSnapshot is not a function).
appium/appium-android-driver (issue or PR to open)
-
startChromeSession()must give the context name tosetupNewChromedriver(), so thatappium:chromedriverForwardBiDiworks in a browser session (point 4). - Question: can the BiDi forward be the default for browser sessions?
webdriverio/webdriverio (issue to open)
- Mobile web BiDi: use the window handle, not the Appium context name (
CHROMIUM), as the browsing context ID (point 5). This bug is independent of Appium. - For Android Chrome through Appium: add
appium:chromedriverForwardBiDi: truewhen WebdriverIO asks for BiDi (after the Appium fix is released), or do not ask for BiDi (point 1). Keep BiDi when the user setswebSocketUrl: true, and do not change appium-geckodriver and appium-chromium-driver, because they support BiDi. - Optional: in
url(), also use the Classic fallback for the Appium 3 errorunknown method(point 3). - v10 migration guide, Appium section: Android Chrome needs
'wdio:enforceWebDriverClassic': trueuntil these fixes are released.
When the upstream fixes are released, we can test the default session again with the repro of this issue, and then update the README.
- 主要语言
- TypeScript
- 星标
- 157
- 派生
- 62
- 平均合并
- 2 小时 9 分钟
- 30 天内合并 PR
- 121
环境准备
- 没有 Dockerfile 或 Docker Compose 文件
- 没有 Pull Request 模板
- 阅读贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
webdriverio/visual-testing 的其他 Issue
-
[💡 Feature]: Support configuring Tesseract PSM in @wdio/ocr-service可能已有人在做 @plum117 于 2 天前认领。 未关闭
难度 3/5 1-2 天 新手友好度 67/100
webdriverio/visual-testing#1363 ·
维护者通常 1 天内回复
-
Proposal: a noise-tolerant compare mode with pixelmatch 8's windowSize可能已有人在做 @plum117 于 14 天前认领。 未关闭enhancement
webdriverio/visual-testing#1311 · 已指派 1 人 ·
维护者通常 1 天内回复
-
feat: support element screenshots of elements in a frame in WebDriver BiDi sessions (v9 `switchFrame()`, v10 `context.frame()`)可能已有人在做 @plum117 于 14 天前认领。 未关闭
难度 5/5 一周以上 新手友好度 50/100
webdriverio/visual-testing#1228 · 已指派 1 人 ·
维护者通常 1 天内回复
-
Wait for Screenshot to Match可能已有人在做 @plum117 于 12 天前认领。 未关闭
难度 4/5 3-5 天 新手友好度 45/100
webdriverio/visual-testing#690 · 已指派 1 人 ·
维护者通常 1 天内回复
-
Using checkFullPageScreen method, capturing images with duplicate viewport and is not scrolling to the bottom of the page可能已有人在做 @plum117 于 12 天前认领。 未关闭help wanted
难度 4/5 3-5 天 新手友好度 35/100
webdriverio/visual-testing#125 · 2 条评论 · 已指派 1 人 ·
维护者通常 1 天内回复
查看 webdriverio/visual-testing 的全部 Issue
相似的 Issue
-
bug hacktoberfest help wanted
难度 2/5 1-3 小时 新手友好度 82/100
libredb/libredb-studio#1941 ·
维护者通常 1 天内回复
-
chore other
难度 2/5 1-3 小时 新手友好度 78/100
alastair-smith/card-games#308 ·
维护者通常 1 天内回复
-
难度 1/5 1 小时以内 新手友好度 86/100
paperclipai/paperclip#17379 ·
维护者通常 1 天内回复
-
[good first issue, hacktoberfest] 🏯 Hacktoberfest Task - Add new Anime Quote 626 (good-first-issue)未关闭community first-timers-only good first issue hacktoberfest help wanted low hanging fruit up-for-grabs
难度 1/5 1 小时以内 新手友好度 76/100
lingdojo/kana-dojo#33040 · 1 条评论 · 5 个 reaction ·
维护者通常 1 天内回复
-
bug mobile ui
难度 2/5 1-3 小时 新手友好度 88/100
维护者通常 1 天内回复