Repository navigation
feat!: target Appium 4 beta - #442
Conversation
BREAKING CHANGE: Requires Appium ^4.0.0-beta.6 and Node ^22.22.2 || ^24.15.0 || >=26.0.0. The driver is no longer compatible with Appium 3.
| | --- | --- | | ||
| | >= 3.0.0 | Appium 3 | | ||
| | >= 5.0.0 | Appium 4 | | ||
| | 3.0.0 - 4.x | Appium 3 | |
There was a problem hiding this comment.
| | 3.0.0 - 4.x | Appium 3 | | |
| | 3.0.0 - 4.6.1 | Appium 3 | |
There was a problem hiding this comment.
🟡 Changes recommended
The timeout normalization accepts unknown legacy timeout types instead of raising the required InvalidArgumentError, leaving an active validation defect in lib/driver.ts.
1 open issue
What changed in this PR
Updates the macOS driver for Appium 4 beta compatibility, prepares the 5.0.0 major release, and adds implicit-wait handling for native element lookups.
Changes:
- Bumps Appium-related dependencies, WebdriverIO, and supported Node.js versions.
- Migrates session creation and timeout handling to the Appium 4 API.
- Adds implicit-wait retries for native element searches.
- Updates compatibility documentation and unit tests.
| File | Summary | Findings |
|---|---|---|
test/unit/find.test.ts |
Adds unit coverage for implicit-wait lookup behavior and error handling. | None |
README.md |
Documents Appium 4 compatibility beginning with driver 5.0.0. | None |
package.json |
Updates Appium 4 dependencies, WebdriverIO, and Node.js engine requirements. | None |
lib/driver.ts |
Adapts session creation and timeout handling to the Appium 4 API. | Moderate: Validate unknown legacy timeout types before normalization; currently they bypass base-driver validation. (2 votes) Moderate: Handle null timeout values explicitly before forwarding them to Chromedriver. (1 vote)Moderate: Copy base timeout values into the mirror fields after super.createSession so web views retain configured script and page-load timeouts. (1 vote) |
lib/commands/find.ts |
Applies implicit waits to native single- and multi-element lookups. | None |
docs/getting-started/index.md |
Updates the driver/Appium compatibility matrix. | Nit: Use the concrete Appium 3-compatible range through driver 4.6.1 instead of the open-ended 4.x range. (1 vote) |
This pull request requires changes.
Review effort: Lite
Method fields shadowed the base prototype, so subclasses could not call super for them.
BREAKING CHANGE: The package no longer has a default export. Use the named export: `import {Mac2Driver} from 'appium-mac2-driver'`.
| using: normalizedStrategy, | ||
| value: selector, | ||
| }); | ||
| if (!this.implicitWaitMs) { |
There was a problem hiding this comment.
Could we use this.implicitWaitMs === 0 here? Appium 4 accepts implicit: null to disable the timeout, and its implicitWaitForCondition maps that value to Infinity. This falsy check instead bypasses the helper, so a lookup fails immediately on the first NoSuchElementError even if the element appears on the next attempt. I reproduced this with implicit: null and a stub that fails once and then succeeds: only one lookup is made. XCUITest's native lookup delegates to the helper for an active session, so it preserves the unlimited wait. Checking explicitly for zero would retain the default behavior agreed in #436 while honoring the new nullable timeout semantics. A regression test for null would cover this.
There was a problem hiding this comment.
Good point. The check is now this.implicitWaitMs === 0, so null goes through implicitWaitForCondition and waits without a deadline. Added a regression test (fail once, then succeed with implicit: null).
| } | ||
| await super.timeouts(type, ms, script, pageLoad, implicit, command); | ||
| if (script !== undefined) { | ||
| this._scriptTimeoutMs = script; |
There was a problem hiding this comment.
Could we distinguish an unset script timeout from an explicitly supplied null? This now accepts and stores script: null (no timeout), but setContext only restores _scriptTimeoutMs when it is not null. After switching to another web view, or returning to a web view via NATIVE_APP, the new Chromedriver session therefore keeps its default script timeout instead of preserving the user's unlimited setting. I reproduced this with a mocked Chromedriver: the initial {script: null} is forwarded, but no script timeout is sent when the replacement session starts. Using a separate unset sentinel such as undefined, and restoring explicit null values, would preserve the setting across context changes.
There was a problem hiding this comment.
Instead of a separate unset sentinel, I dropped the driver's own _scriptTimeoutMs/_pageLoadTimeoutMs mirrors and now read the base driver's scriptTimeoutMs/pageLoadTimeoutMs/implicitWaitMs (which always hold a value, null included) when starting a Chromedriver session. This restores an explicit null across web view switches and also picks up timeouts set via capabilities. Added a test for getSessionTimeouts.
Only skip the lookup wait for a zero implicit wait, and restore the base driver's timeouts (including explicit null) on a new Chromedriver session.
## [5.0.0](v4.6.1...v5.0.0) (2026-11-09) ### ⚠ BREAKING CHANGES * Requires Appium ^4.0.0-beta.6 (peer dependency). No longer compatible with Appium 3. * Drops Node 20. The minimum is now ^22.22.2 || ^24.15.0 || >=26.0.0, matching Appium 4's engine range. * Driver API: createSession takes a single w3cCapabilities argument, following the Appium 4 base driver. This only matters to anyone subclassing Mac2Driver. * Default exports removed: the package no longer has a default export. Use the named export instead: import {Mac2Driver} from 'appium-mac2-driver'. The internal logger and constraints modules now use named exports too (log, MAC2_CONSTRAINTS). * Command methods live on the prototype: the command methods are attached to Mac2Driver.prototype (with declare module typings) rather than being instance fields, so subclasses can call super for them. ### Features * target Appium 4 beta ([#442](#442)) ([59e7605](59e7605)) ### Code Refactoring * use ConditionUnmetError instead of message matching ([#441](#441)) ([f2abf50](f2abf50))
|
🎉 This PR is included in version 5.0.0 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |

Bumps the Appium monorepo packages to the Appium 4 line, the same way appium-espresso-driver did in appium/appium-espresso-driver#1248. This triggers a major release.
Breaking changes
^4.0.0-beta.6(peer dependency). No longer compatible with Appium 3.^22.22.2 || ^24.15.0 || >=26.0.0, matching Appium 4's engine range.createSessiontakes a singlew3cCapabilitiesargument, following the Appium 4 base driver. This only matters to anyone subclassingMac2Driver.import {Mac2Driver} from 'appium-mac2-driver'. The internalloggerandconstraintsmodules now use named exports too (log,MAC2_CONSTRAINTS).Mac2Driver.prototype(withdeclare moduletypings) rather than being instance fields, so subclasses can callsuperfor them.Dependency bumps
appium(peer)^3.0.0-rc.2^4.0.0-beta.6@appium/docutils^3.0.0^4.0.0-beta.5@appium/oxc-config^1.1.0^2.0.0-beta.2@appium/semantic-release-config^1.1.0^2.0.0-beta.1@appium/tsconfig^1.2.0^2.0.0-beta.1@appium/types^1.0.0-rc.1^2.0.0-beta.5webdriverio(dev)^9.0.0^10.1.0engines.node^20.19.0 || ^22.12.0 || >=24.0.0^22.22.2 || ^24.15.0 || >=26.0.0Other changes
implicitWaitW3C,pageLoadTimeoutW3CandscriptTimeoutW3C. They are replaced by a singletimeoutsoverride that keeps the same behavior: the values are stored on the session and mirrored to Chromedriver in a web view, and page load timeout is still not implemented natively.Testing
tsc -b,npm run lint(no new warnings) andnpm run format:checkare clean.Included from other PRs
ConditionUnmetErrorinstead of message matching, in line with refactor: use ConditionUnmetError instead of message matching #441.