Skip to content

feat!: target Appium 4 beta - #442

Merged
mykola-mokhnach merged 9 commits into
appium:masterfrom
mykola-mokhnach:feat/appium4
Nov 9, 2026
Merged

mykola-mokhnach merged 9 commits into
appium:masterfrom
mykola-mokhnach:feat/appium4

Conversation

@mykola-mokhnach

@mykola-mokhnach mykola-mokhnach commented Nov 7, 2026 •

Copy link
Copy Markdown
Contributor

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

  • 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.

Dependency bumps

Package From To
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.5
webdriverio (dev) ^9.0.0 ^10.1.0
engines.node ^20.19.0 || ^22.12.0 || >=24.0.0 ^22.22.2 || ^24.15.0 || >=26.0.0

Other changes

  • Timeouts: the base driver removed implicitWaitW3C, pageLoadTimeoutW3C and scriptTimeoutW3C. They are replaced by a single timeouts override 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.
  • Docs: README and getting-started compatibility notes now say Appium 4 from 5.0.0 and Appium 3 for 3.0.0 - 4.x.

Testing

  • tsc -b, npm run lint (no new warnings) and npm run format:check are clean.
  • All 53 unit tests pass.
  • Not run locally: e2e tests (need a macOS host with Accessibility granted).

Included from other PRs

mykola-mokhnach and others added 4 commits November 7, 2026 20:20
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.
Comment thread docs/getting-started/index.md Outdated
| --- | --- |
| >= 3.0.0 | Appium 3 |
| >= 5.0.0 | Appium 4 |
| 3.0.0 - 4.x | Appium 3 |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
| 3.0.0 - 4.x | Appium 3 |
| 3.0.0 - 4.6.1 | Appium 3 |

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 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

Comment thread lib/driver.ts Resolved
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'`.
Comment thread lib/commands/find.ts Outdated
using: normalizedStrategy,
value: selector,
});
if (!this.implicitWaitMs) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Comment thread lib/driver.ts Outdated
}
await super.timeouts(type, ms, script, pageLoad, implicit, command);
if (script !== undefined) {
this._scriptTimeoutMs = script;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.
@mykola-mokhnach
mykola-mokhnach merged commit 59e7605 into appium:master Nov 9, 2026
15 checks passed
@mykola-mokhnach
mykola-mokhnach deleted the feat/appium4 branch November 9, 2026 06:56
github-actions Bot pushed a commit that referenced this pull request Nov 9, 2026
## [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))
@github-actions

github-actions Bot commented Nov 9, 2026

Copy link
Copy Markdown

🎉 This PR is included in version 5.0.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants