Repository navigation
Fix W3C capability rejection breaking every desktop session - #4
Merged
jochen-testingbot merged 1 commit intoSep 21, 2026
Merged
Conversation
tb_openBrowser sent the legacy JSON-Wire `platform` cap alongside the W3C `platformName`. The `webdriver` client rejects any top-level capability that is neither standard W3C nor colon-namespaced as soon as an extension cap (`tb:options`) is present, so every desktop session failed client-side with "Invalid or unsupported WebDriver capabilities found (platform)" before a request ever reached the hub. Mobile was unaffected — that branch emits only valid and `appium:`-namespaced keys — which is why mobile sessions worked. Also fix `screenResolution` in tb:options: TestingBot reads the kebab-case `screen-resolution` key, so the camelCase spelling was silently dropped and desktop VMs stayed at the 1280x1024 default. Neither bug was catchable by the existing tests, which mock WebDriver.newSession and assert capabilities against our own expectations — they could never disagree with the client that has to accept them. The integration suite that would have caught it had gone stale: it never awaited addAutomationTools after that function became async, so it would have failed on its first test, and CI never runs it. Test coverage added: - tests/tools/browse-capabilities.contract.test.ts drives the real webdriver client against a local stub hub, asserting the actual JSON body sent. Free and fast, so it runs in the normal unit pass. Verified it fails with the exact reported error when `platform` is reintroduced. - web.integration.test.ts now covers every browser and shared tool (click, type, pressKey, getText, getAttribute, executeScript, all six locator strategies) in a single session, and asserts the requested resolution actually reaches the VM. - mobile.integration.test.ts covers the appium:* capability branch on an Android emulator, resolved from the /v1/browsers catalog. - appium-child.integration.test.ts spawns the real appium-mcp child and checks the published tool surface. Opens no session, so it costs nothing. CI: .github/workflows/integration.yml runs the suites nightly and on demand using the TB_KEY/TB_SECRET secrets. It fails fast when the secrets are absent rather than letting the suites skip into a green run that verified nothing, serialises runs so two paid sessions never overlap, and always sweeps leaked sessions afterwards via scripts/close-stale-sessions.mjs.
jochen-testingbot
force-pushed
the
fix/w3c-capabilities-and-integration-coverage
branch
from
September 21, 2026 07:25
c992d37 to
2cf947c
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.
The bug
Reported from the field:
Accurate.
tb_openBrowsersent the legacy JSON-Wireplatformcap alongside the W3CplatformName. Thewebdriverclient'svalidateCapabilitiesrejects any top-level key that is neither standard W3C nor colon-namespaced as soon as an extension cap (tb:options) is present — so every desktop session failed client-side, before a request ever reached the hub.Mobile was unaffected because that branch emits only valid and
appium:-namespaced keys, which is exactly the asymmetry the reporter saw. Per TestingBot's docsplatformis just the legacy alias forplatformName, and the OS codes (VENTURA,WIN11, …) work onplatformNamedirectly, so the duplicate bought nothing.Second bug, found while cross-checking
screenResolutioninsidetb:optionswas never honoured. The VM reads the kebab-casescreen-resolutionkey only (lib/drivers/screenresolution.js,lib/protocols/webdriver.js,lib/protocols/rc.js), so the camelCase spelling was silently dropped and desktop VMs stayed at the 1280x1024 default. Now sent asscreen-resolution.Remaining keys were cross-checked against the VM source and are correct:
tb:options.realDevice,name,build, and theappium:*/ W3C top-level set.Why the tests didn't catch either
Two compounding gaps:
WebDriver.newSession. Capabilities were asserted against our own expectations, so they could never disagree with the client that has to accept them.git log -Sshowsplatformhas been there since the initial commit.addAutomationToolsafter that became async, sohandle.toolswasundefinedand it would have failed on its first test.Test coverage added
tests/tools/browse-capabilities.contract.test.ts— drives the realwebdriverclient against a local stub hub on127.0.0.1: real validation, real HTTP round trip, asserting the actual JSON body sent. No TestingBot session, ~130ms, runs in the normalnpm testpass. Verified it fails with the exact reported error whenplatformis reintroduced.web.integration.test.ts— 7 → 20 tests. Now covers every browser/shared tool (click,typeincl.pressEnter,pressKeyvia the W3C actions path,getText,getAttribute,executeScriptwith positional args, all six locator strategies, not-found error handling) in a single session, plus an end-to-end assertion that the requested resolution reaches the VM.mobile.integration.test.ts(new) — theappium:*branch on an Android emulator, resolved at runtime from the/v1/browserscatalog (not/v1/devices, which is the physical-device inventory). Separately gated behindRUN_MOBILE_INTEGRATION_TESTS.appium-child.integration.test.ts(new) — spawns the realappium-mcpchild and checksHIDDEN_TOOLS, the steering rewrite, and that no published schema exposesremoteServerUrl. Costs nothing; every other appium test stubs the child, so an upstream rename would currently pass CI and break mobile silently.CI
.github/workflows/integration.yml— nightly (03:17 UTC) +workflow_dispatch, using theTB_KEY/TB_SECRETrepo secrets. It fails fast when the secrets are absent rather than letting the suites skip into a green run that verified nothing, serialises runs so two paid sessions never overlap, skips forks, and always sweeps leaked sessions afterwards viascripts/close-stale-sessions.mjs(best-effort; never turns a passing run red).Verification
npm run buildpasses: lint, format, 120 unit tests, tsc.Not yet verified against the live hub — I have no credentials. Add the secrets, then trigger a manual run from the Actions tab rather than waiting for the first nightly; that's the real proof.