Compare commits

...
Author SHA1 Message Date
github-actions 5e87dc85d0 chore: publish
- appium@4.0.0-beta.6
 - @appium/base-driver@11.0.0-beta.5
 - @appium/base-plugin@4.0.0-beta.5
 - @appium/docutils@4.0.0-beta.5
 - @appium/driver-test-support@4.0.0-beta.5
 - @appium/execute-driver-plugin@8.0.0-beta.6
 - @appium/fake-driver@7.0.0-beta.6
 - @appium/fake-plugin@5.0.0-beta.6
 - @appium/http-server@1.0.0-beta.5
 - @appium/images-plugin@6.0.0-beta.6
 - @appium/opencv@6.0.0-beta.5
 - @appium/plugin-test-support@4.0.0-beta.6
 - @appium/relaxed-caps-plugin@4.0.0-beta.6
 - @appium/schema@2.0.0-beta.3
 - @appium/storage-plugin@3.0.0-beta.6
 - @appium/support@8.0.0-beta.5
 - @appium/types@2.0.0-beta.5
 - @appium/universal-xml-plugin@4.0.0-beta.6
2026-10-26 07:04:25 +00:00
Mohamed Mostafa 1db7aa9eb7 fix(execute-driver-plugin): return null when a script result refers to itself (#23085) 2026-10-26 07:57:05 +01:00
Mykola Mokhnach d91b92d037 ci: run e2e tests on Windows (#23092) 2026-10-26 07:51:05 +01:00
Mykola Mokhnach 0f1a76184b feat(support)!: Remove system unzip from extractAllTo (#23091)
BREAKING CHANGE: ExtractAllOptions.useSystemUnzip is removed.
BREAKING CHANGE: Non-UTF-8-flagged file names now default to utf8 instead of cp437, and cp437 decoding is no longer supported (modern archives use UTF-8). Such archives must be recreated or converted; fileNamesEncoding only accepts Node.js BufferEncodings.
2026-10-25 21:27:21 +01:00
Mohamed Mostafa 9ea7718f0a fix(http-server): reject invalid JSON only after the route is known (#23057) 2026-10-25 20:43:01 +01:00
Kazuaki Matsuo 75f35b828e Merge commit from fork 2026-10-26 04:17:30 +09:00
Mykola Mokhnach 77fab30f70 refactor(support): Move private helpers of public modules into the internal barrel (#23090) 2026-10-25 19:04:34 +01:00
Kazuaki Matsuo dd71f66e4a fix(appium): preserve npm arguments on Windows (#22957)
* fix(appium): preserve npm arguments on Windows

* test(appium): cover Windows npm argument preservation

* fix(appium): resolve project-local npm shims on Windows

* refactor(appium): extract Windows npm CLI resolution

* fix(appium): clarify npm lookup errors and share pending resolution

* fix(appium): respect the caller's Windows npm search path

* refactor(appium): simplify Windows npm lookup and test fixtures

* refactor(appium): use a plain error for missing npm shim

* fix(appium): preserve Windows npm shim runtime and prefix behavior

* refactor(appium): address Windows npm resolver review feedback

* refactor(appium): share npm resolution promises without AsyncLock

* fix(appium): avoid waiting for later Windows PATH lookups

* refactor(appium): clarify Windows npm resolver names

* refactor(appium): remove Windows npm resolution caching
2026-10-26 02:48:20 +09:00
Mykola Mokhnach c7e3b46f1a perf(support): Further optimize zip extraction (#23087) 2026-10-25 17:27:29 +01:00
pythonyx135793 3615d6df4c fix(appium)!: install local extensions without global npm links (#23086)
* fix(appium): install local extensions with live links

* docs(appium): explain local extension links in Appium 4
2026-10-26 00:59:13 +09:00
Mykola Mokhnach 52c92d6acd feat(support): Add zip streaming support (#23083) 2026-10-25 00:54:44 +02:00
Mykola Mokhnach 34e368e666 perf(support): Speed up zip extraction (#23078) 2026-10-24 22:34:38 +02:00
carrerasdarren-cellandcarrerasdarren-cell 6cc4095c59 fix(base-driver)!: preserve proxied command validation ordering (#23060)
* fix(base-driver): preserve proxied navigation error ordering

* fix(base-driver): address proxied validation review

* docs: explain proxied plugin validation migration

---------

Co-authored-by: carrerasdarren-cell <carrerasdarren-cell@users.noreply.github.com>
2026-10-25 01:41:26 +09:00
Mohamed Mostafa c62ffb7ddf fix(base-driver): reject an inherited name as a log type (#23082) 2026-10-24 16:07:18 +02:00
Mykola Mokhnach 17421ae61f fix(base-driver)!: use AsyncLock from asyncbox instead of async-lock (#23066)
BREAKING CHANGE: The protected BaseDriver#commandsQueueGuard beyond promise-based acquire(key, fn) - it is now an asyncbox AsyncLock, so callback-style acquire() and the internal queues field are gone (use getQueueLength() / isBusy()).
2026-10-24 09:39:04 +02:00
carrerasdarren-cellandcarrerasdarren-cell 531f482d87 fix(base-driver)!: validate every timeout capability candidate (#23062)
* fix(base-driver): validate every timeout capability candidate

* refactor(base-driver): address timeout validation review

---------

Co-authored-by: carrerasdarren-cell <carrerasdarren-cell@users.noreply.github.com>
2026-10-24 15:15:18 +09:00
Kazuaki Matsuo 8e42cbc82d fix(support): prevent zip extraction through symlink chains (#23002)
* fix(support): prevent zip extraction through symlink chains

* fix(support): address zip symlink review findings

* perf(support): revalidate only affected zip symlinks

* refactor(support): clarify zip symlink validation

* refactor(support): clarify symlink target traversal

* fix(appium): avoid npm audit during extension removal and e2e setup

* fix(support): normalize Unicode symlink dependency keys

* docs(support): shorten symlink validation comments
2026-10-24 01:05:25 +09:00
Mykola Mokhnach 16c84ea203 fix(appium)!: remove unused server arguments (#23063)
BREAKING CHANGE: removed the --callback-address (-ca) server argument and the server.callback-address config property
BREAKING CHANGE: removed the --callback-port (-cp) server argument and the server.callback-port config property
BREAKING CHANGE: removed the --debug-log-spacing server argument and the server.debug-log-spacing config property
BREAKING CHANGE: removed the --strict-caps server argument and the server.strict-caps config property
BREAKING CHANGE: removed the --trace-dir server argument and the server.trace-dir config property
2026-10-22 08:28:55 +02:00
Mohamed Mostafa 819905beb2 fix(appium): send log webhooks to ipv6 hosts (#23061) 2026-10-21 23:10:17 +02:00
Mohamed Mostafa 8f30c8abc7 fix(http-server): return unknown method for a known command url (#22991) 2026-10-21 12:56:57 +02:00
Mohamed Mostafa 39e9512b4a fix(base-driver)!: reject command POST bodies that are not JSON objects (#23007)
BREAKING CHANGE: POST body has to be a JSON object, otherwise the server returns invalid argument error.
2026-10-21 08:40:34 +02:00
Qingyuan Wang 2dd05e12d3 fix(docs): correct stale instructions in .NET quickstart (#23049) 2026-10-21 12:51:38 +09:00
Qingyuan Wang 2cf7f4e6b2 fix(docs): X-Appium-Is-Sensitive also accepts the value 'yes' (#23051) 2026-10-21 12:49:44 +09:00
Qingyuan Wang 7bff30ba65 fix(schema): correct tmp dir default description to match os.tmpdir() (#23046) 2026-10-21 12:41:16 +09:00
Qingyuan Wang dbc2125d82 docs: fix stale @see paths (#23042)
* docs: fix broken license links and stale @see paths on appium4

packages/appium/README.md links to ./LICENSE and ./packages/logger/LICENSE, neither of which resolves from the packages/appium directory (no LICENSE file exists there; the root LICENSE and packages/logger/LICENSE are the real files). This matches the fix already applied to master in #23001.

The @see ../../test/unit/command-timeout.spec.ts comments in fake-driver/fake-plugin resolve one level too high (packages/test/...); the specs live at ../test/unit/command-timeout.spec.ts relative to lib/.

* docs: exclude README changes from this PR

The Appium README will be fixed on `appium4` by porting the changes from `master`, per @eglitise.

Signed-off-by: NUAAwqy <146524024+NUAAwqy@users.noreply.github.com>

---------

Signed-off-by: NUAAwqy <146524024+NUAAwqy@users.noreply.github.com>
2026-10-20 14:56:04 +03:00
Qingyuan Wang 9e0349f398 chore: remove broken docs build scripts (#23026)
Signed-off-by: NUAAwqy <146524024+NUAAwqy@users.noreply.github.com>
2026-10-19 16:34:02 +03:00
Qingyuan Wang 589611f13e chore(base-driver): fix "misclassified" typo in comment (#23025)
Signed-off-by: NUAAwqy <146524024+NUAAwqy@users.noreply.github.com>
2026-10-19 16:32:00 +03:00
Qingyuan Wang 6575e29fba chore(appium): use consistent "writable" spelling (#23024)
Signed-off-by: NUAAwqy <146524024+NUAAwqy@users.noreply.github.com>
2026-10-19 16:31:40 +03:00
github-actions eed4a27fd5 chore: publish
- appium@4.0.0-beta.5
 - @appium/base-driver@11.0.0-beta.4
 - @appium/base-plugin@4.0.0-beta.4
 - @appium/docutils@4.0.0-beta.4
 - @appium/driver-test-support@4.0.0-beta.4
 - @appium/execute-driver-plugin@8.0.0-beta.5
 - @appium/fake-driver@7.0.0-beta.5
 - @appium/fake-plugin@5.0.0-beta.5
 - @appium/http-server@1.0.0-beta.4
 - @appium/images-plugin@6.0.0-beta.5
 - @appium/opencv@6.0.0-beta.4
 - @appium/plugin-test-support@4.0.0-beta.5
 - @appium/relaxed-caps-plugin@4.0.0-beta.5
 - @appium/storage-plugin@3.0.0-beta.5
 - @appium/support@8.0.0-beta.4
 - @appium/types@2.0.0-beta.4
 - @appium/universal-xml-plugin@4.0.0-beta.5
2026-10-18 22:48:48 +00:00
Kazuaki Matsuo 900f6d338d docs(appium): update recent changes in the migration guide (#22972)
* docs: update recent changes in the migration guide

* simplify a bit

* docs: reflect recent WebDriver fixes in migration guide

Document strict standard timeout inputs and null readback, capability key casing/null omission/error classification, optional error data, and BiDi idle-timeout tracking.

Refs #22961, #22974, #22975, #22976, #22977, #22947, and #22966.

* docs: shorten recent migration notes

Condense the timeout, capability, error-data, and command-activity notes while retaining migration actions and PR references.

* docs: track capability candidate and initial timeout follow-ups

Link pending #22982 and #22983 without presenting them as merged. Refs #22966.

* docs: correct directory walk error guidance

Describe the merged #22956 behavior: log and skip non-ENOENT walker errors, continue traversal, and retain ENOENT rejection. Clarify that completion does not guarantee every entry was visited.

Addresses review r4267074238. Verified against appium4 implementation and checked the documentation diff.

* docs: refresh migration notes and restore timeout details
2026-10-18 20:54:47 +08:00
Kazuaki Matsuo a090505184 fix(base-driver): reject implicit HEAD command routing (#22992) 2026-10-15 18:57:15 -07:00
Kazuaki Matsuo aba33a2d17 fix(base-driver): validate and merge all capability candidates (#22982) 2026-10-15 10:19:35 -07:00
Mykola Mokhnach 2bbb1d3a49 fix(http-server)!: reject browser-origin requests unless CORS is enabled (#22990)
BREAKING CHANGE: clients that send an Origin header must enable the CORS feature (--allow-insecure=*:cors).
BREAKING CHANGE: Library callers that depended on the implicit CORS default must now pass allowCors: true
2026-10-15 18:15:36 +02:00
Kazuaki Matsuo d05423e77d fix(base-driver): apply initial session timeouts (#22983)
* refactor(base-driver): share standard timeout value validation

Refs #22966 (F21).

* fix(base-driver): apply initial session timeouts

Refs #22966 (F21). Initialize local timeout state before driver backend startup.

* test(base-driver): cover initial session timeout state

Refs #22966 (F21). Covers nulls, bounds, invalid inputs, readback, defaults, and backend setter overrides.

* refactor(types): export the existing Timeouts type

* refactor(base-driver): reuse the shared Timeouts type

Use Partial<Timeouts> for initial session timeout state.

Validation: TypeScript build, base-driver type tests, and targeted lint/format checks passed.

* refactor(base-driver): share timeout mapping across initialization loops

* refactor(base-driver): normalize reset options in a loop

* refactor(base-driver): extract session preparation helpers

* fix(base-driver): identify timeout type in validation errors

* fix(base-driver): show invalid timeout values in errors

* refactor(base-driver): simplify timeout update mapping

* refactor(base-driver): centralize timeout metadata in timeout module

Share standard timeout names, local fields, and setters with session initialization. Reuse keyof Timeouts for validation. Refs #22966.

* refactor(base-driver): reuse timeout metadata for initial session state

Remove the duplicate timeout mapping from driver.ts. Preserve direct field initialization before backend startup. Validation: 578 base-driver tests, TypeScript build, type tests, and targeted lint/format checks passed.

* fix(types): allow partial initial timeout capabilities
2026-10-15 09:16:57 +09:00
Mohamed Mostafa f820e04000 fix(appium): apply default capabilities to every firstMatch candidate (#22988)
* fix(appium): apply default capabilities to every firstMatch candidate

* docs(appium): explain how default capabilities apply to firstMatch candidates
2026-10-15 09:16:28 +09:00
Mohamed Mostafa 814357aa7f fix(base-driver): reject an unsupported log type with InvalidArgumentError (#22986) 2026-10-14 17:48:34 +02:00
Mohamed Mostafa a9d1b1212d fix(base-driver): reject an unknown timeout type with InvalidArgumentError (#22987) 2026-10-14 18:43:50 +03:00
Kazuaki Matsuo f3edfc5e03 fix(base-driver)!: match standard capability names case-sensitively (#22976)
* Update migration guide for Appium 3 to 4

Refs #22966 (F12).

* Refactor capability imports and standard cap check

Refs #22966 (F12).

* Add test for insertAppiumPrefixes function

Refs #22966 (F12).

* Update isStandardCap to use exact matches

Refs #22966 (F12).

* Enhance capability tests for standard names

Refs #22966 (F12).

* Update error message in capability.spec.ts

Refs #22966 (F12).
2026-10-14 12:20:43 +09:00
Kazuaki Matsuo 07d324b488 fix(base-driver): preserve optional WebDriver error data (#22974)
* fix(base-driver): preserve optional WebDriver error data

Preserve supplied Classic WebDriver error data. Refs #22966 (F09).

* fix(base-driver): retain data in returned driver errors

* Add data handling to errorFromW3CJsonCode

Refs #22966 (F09).

* Add tests for error handling in ProxyRequestError

Refs #22966 (F09).

* Enhance proxy tests with error handling case

Refs #22966 (F09).

* test(http-server): cover returned driver error data
2026-10-14 12:09:48 +09:00
Kazuaki Matsuo b452a5eade fix(base-driver): preserve capability invalid argument errors (#22977)
* Change error type for non-W3C capabilities

Refs #22966 (F10).

* fix(base-driver): preserve capability invalid argument errors

Refs #22966 (F10).

* fix(base-driver): preserve capability invalid argument errors

Refs #22966 (F10).

* fix(base-driver): preserve capability invalid argument errors

Refs #22966 (F10).

* fix(base-driver): preserve capability invalid argument errors

Refs #22966 (F10).

* test(fake-driver): expect invalid argument for legacy capabilities
2026-10-14 12:07:37 +09:00
Kazuaki Matsuo f1cf93e368 fix(base-driver): omit null capabilities before merging (#22975)
* Implement omitNullCapabilities function

Refs #22966 (F13).

* Add tests for null capability values handling

Refs #22966 (F13).

* refactor(base-driver): avoid reassigning capability argument

Use a normalizedCaps local for validation and the return value, preserving null-omission behavior. Refs #22966 (F13).
2026-10-14 12:07:08 +09:00
Kazuaki Matsuo eef8246f8a fix(base-driver): validate standard timeouts and preserve null values (#22961)
* fix(base-driver): validate timeouts before applying changes

* test(base-driver): preserve timeouts on invalid updates

* fix(base-driver): preserve null script timeouts

* fix(base-driver): allow nullable script timeout state

* fix(types): accept null script timeouts

* test(base-driver): cover null script timeout updates

* docs(appium): document null script timeouts

* refactor(base-driver): loop over validated timeout updates

* fix(base-driver): validate standard timeouts and preserve null values

* refactor(types): introduce shared TimeoutValue type

Refactor Timeouts type to use TimeoutValue for better clarity.

* refactor(types): export TimeoutValue

* refactor(types): use TimeoutValue for driver timeout state

* refactor(types): use TimeoutValue in timeout command signatures

* refactor(base-driver): use shared type for timeout state

* refactor(base-driver): use TimeoutValue in timeout commands

* docs: simplify timeout API descriptions
2026-10-13 05:21:57 +09:00
Kazuaki Matsuo 3f82e6a181 fix(appium): suspend idle timeout during BiDi commands (#22947)
* fix(appium): track idle timeout across plugin commands

* fix(appium): suspend idle timeout during BiDi commands

* fix(base-driver): guard timeout restarts from legacy drivers

* review

* review

* fix(base-driver)!: require shared command activity tracking

Remove legacy timer method overrides and the compatibility capability flag. Document the driver migration requirement and verify shared activity across ESM copies without replacing timer methods.

BREAKING CHANGE: Drivers must use base-driver with shared command activity tracking. Custom timeout implementations must delegate timer creation to super.startNewCommandTimeout(); legacy private-counter implementations are no longer protected by method overrides.

* add fake driver/plugin example

* remove from readme

* fix(appium): release one proxied BiDi command per response

* test(appium): cover overlapping duplicate BiDi command IDs
2026-10-13 04:18:26 +09:00
Kazuaki Matsuo c10c41384c fix(appium)!: track idle timeout across plugin commands (#22946)
* fix(appium): track idle timeout across plugin commands

* fix(base-driver): guard timeout restarts from legacy drivers

* review

* fix(base-driver)!: require shared command activity tracking

Remove legacy timer method overrides and the compatibility capability flag. Document the driver migration requirement and verify shared activity across ESM copies without replacing timer methods.

BREAKING CHANGE: Drivers must use base-driver with shared command activity tracking. Custom timeout implementations must delegate timer creation to super.startNewCommandTimeout(); legacy private-counter implementations are no longer protected by method overrides.

* add fake driver/plugin example

* remove from readme

* Remove actions needed for driver updates in migration guide

Removed the section detailing actions needed for driver updates related to shared command activity tracking.

* refactor(base-driver): simplify shutdown promise handling

* Add actions needed for custom startNewCommandTimeout

Updated migration guide to include new requirements for custom command timeout implementations.
2026-10-13 03:22:51 +09:00
Kazuaki Matsuo 9b1ddb8bf6 fix(appium): classify driver selection failures as session not created (#22970) 2026-10-12 15:13:58 +09:00
Kazuaki Matsuo 4949ea4375 fix(http-server): set no-cache on WebDriver responses (#22971)
* fix(http-server): set no-cache on WebDriver responses

* test(http-server): isolate cache-control regression requests
2026-10-12 15:10:45 +09:00
Kazuaki Matsuo da8570d882 fix(http-server): return WebDriver errors for the root path (#22969)
* fix(http-server): return WebDriver errors for the root path

* test(http-server): isolate server ports between test cases
2026-10-12 15:07:04 +09:00
Kazuaki Matsuo 122c157292 fix(base-driver): preserve detached shadow root errors (#22968) 2026-10-12 15:05:41 +09:00
Mohamed Mostafa b0b2ce903c fix(images-plugin): return every image match from findElements (#22926) 2026-10-12 07:57:39 +02:00
Edgars Eglītis ba8d57f378 docs: fix missing prefix for platformVersion capability (#22967) 2026-10-11 22:59:46 +03:00
Edgars Eglītis 0b3fe1ce35 feat(appium): fallback to latest compatible extension version for CLI install (#22951)
* feat(appium): fallback to latest compatible extension version for CLI `install`

* fix: fail fast for fully outdated extensions

* fix: revert dep/peerDep requirement

* fix: address comments

* chore: fix tests

* docs: add feature to migration guide
2026-10-11 22:50:28 +03:00
Mykola Mokhnach b67054ad45 docs: update Appium 3 to 4 migration guide (#22965) 2026-10-11 20:32:27 +02:00
Kazuaki Matsuo 7b13975340 fix(support): preserve case-only moves on Windows (#22958)
* fix(support): move source symlinks without following their targets

* fix(support): compare move entries by device and inode

* fix(support): preserve case-only moves on Windows

* test(support): cover Windows case-only moves

* refactor

* refactor(support): extract internal MoveOperation module

This class handles moving files and directories with options for recursive transfers and error handling.

* refactor(support): use internal MoveOperation from fs

Removed the MoveOperation class and its associated methods, simplifying the file structure.
2026-10-11 23:22:17 +09:00
Kazuaki Matsuo 139f086941 fix(storage-plugin): contain upload event socket errors (#22963)
* fix(storage-plugin): contain upload event socket errors

* test(storage-plugin): cover upload event connection errors
2026-10-11 23:13:26 +09:00
Kazuaki Matsuo b54c51da1a fix(base-driver): contain idle timeout cleanup failures (#22964)
* fix(base-driver): contain idle timeout cleanup failures

* test(base-driver): keep other sessions alive after cleanup failures
2026-10-11 23:10:34 +09:00
Kazuaki Matsuo a520720d06 fix(base-driver): validate timeouts before applying changes (#22960)
* fix(base-driver): validate timeouts before applying changes

* test(base-driver): preserve timeouts on invalid updates

* refactor(base-driver): loop over validated timeout updates
2026-10-11 16:50:38 +09:00
Kazuaki Matsuo 6bae658c4b fix(support): reject all directory walk errors (#22956)
* fix(support): reject all directory walk errors

* review
2026-10-11 16:40:02 +09:00
Kazuaki Matsuo 9940156d1a fix(storage-plugin): reject concurrent WebSocket uploads (#22962)
* fix(storage-plugin): reject concurrent WebSocket uploads

* test(storage-plugin): cover upload contention and retries
2026-10-11 16:39:44 +09:00
Kazuaki Matsuo ef5468ceac fix(storage-plugin): reject reserved partial-file names (#22959)
* fix(storage-plugin): reject reserved partial-file names

* test(storage-plugin): cover reserved partial-file names
2026-10-11 16:36:23 +09:00
Kazuaki Matsuo f0d1cad091 fix(base-driver): notify IPC waiters on either unsubscribe path (#22955)
* fix(base-driver): notify IPC waiters on either unsubscribe path

* fix(base-driver): finish IPC waits before notifying observers
2026-10-11 05:33:11 +09:00
Kazuaki Matsuo 81dc35dc6b fix(support): move source symlinks without following their targets (#22954)
* fix(support): move source symlinks without following their targets

* fix(support): compare move entries by device and inode
2026-10-11 05:28:50 +09:00
Kazuaki Matsuo d8fff407cf fix(storage-plugin): preserve pending upload sockets across retries (#22948)
* fix(storage-plugin): preserve pending upload sockets across retries

* fix(storage-plugin): isolate upload names and renew retry expiry

* test(storage-plugin): bound upload waits and cleanup clients

* use sha1

* add test

* revert docs
2026-10-11 05:16:09 +09:00
Kazuaki Matsuo 8272eddcd2 chore: bump shell quote and remove workaround (#22953) 2026-10-11 02:24:46 +09:00
Mykola Mokhnach cce0b5814d feat(types)!: remove driver-specific capabilities from base constraints (#22952)
BREAKING CHANGE: the locale, language, autoWebview, orientation and udid capabilities are no longer part of the standard capability constraints. They are not generic enough and must be defined by the corresponding drivers.
2026-10-10 17:29:16 +02:00
Mykola Mokhnach 8822ceaabb feat(types)!: remove deprecated autoLaunch capability (#22950)
BREAKING CHANGE: the deprecated appium:autoLaunch capability is no longer part of the standard capability constraints.
2026-10-10 17:19:15 +02:00
Kazuaki Matsuo e870487757 fix(base-driver): preserve encoded proxy path parameters (#22945) 2026-10-10 16:15:43 +09:00
Kazuaki Matsuo 9cb4185b07 fix(http-server): contain WebSocket upgrade failures (#22944)
* fix(http-server): contain WebSocket upgrade failures

* test(http-server): bound and verify WebSocket exchanges
2026-10-10 16:15:24 +09:00
github-actions 3afce5b748 chore: publish
- appium@4.0.0-beta.4
 - @appium/base-driver@11.0.0-beta.3
 - @appium/base-plugin@4.0.0-beta.3
 - @appium/docutils@4.0.0-beta.3
 - @appium/driver-test-support@4.0.0-beta.3
 - @appium/execute-driver-plugin@8.0.0-beta.4
 - @appium/fake-driver@7.0.0-beta.4
 - @appium/fake-plugin@5.0.0-beta.4
 - @appium/http-server@1.0.0-beta.3
 - @appium/images-plugin@6.0.0-beta.4
 - @appium/logger@3.0.0-beta.2
 - @appium/opencv@6.0.0-beta.3
 - @appium/oxc-config@2.0.0-beta.2
 - @appium/plugin-test-support@4.0.0-beta.4
 - @appium/relaxed-caps-plugin@4.0.0-beta.4
 - @appium/schema@2.0.0-beta.2
 - @appium/storage-plugin@3.0.0-beta.4
 - @appium/support@8.0.0-beta.3
 - @appium/types@2.0.0-beta.3
 - @appium/universal-xml-plugin@4.0.0-beta.4
2026-10-09 17:41:46 +00:00
Kazuaki Matsuo c5ddcef894 fix: centralize URL hostname formatting and bracket IPv6 hosts (#22914)
* fix: bracket IPv6 hosts in proxy and helper URLs

* refactor(support): share URL hostname formatting across packages

* test: remove duplicate hostname formatting coverage

* refactor(support): move hostname formatting to net module
2026-10-08 23:52:44 +09:00
Edgars Eglītis 2b3e497ea7 chore: run audit fix (#22925) 2026-10-08 17:36:53 +03:00
Edgars Eglītis 01c14af88f chore: port oxc dependency bumps from master (#22924) 2026-10-08 17:25:17 +03:00
Edgars Eglītis 27d3fe41f7 feat(oxc-config): enable plugins at the root level (#22908)
BREAKING CHANGE: enable 19 additional oxlint rules
2026-10-08 17:15:52 +03:00
Edgars Eglītis 57af81cc3b chore: port dependency bumps from master (#22921)
* chore: port dependency bumps from master

* chore: fix format
2026-10-08 17:14:32 +03:00
Edgars Eglītis 9f8455e197 fix: tune wincore driver integration (#22922)
* fix: tune wincore driver integration

* docs: adjust CLI reference doc
2026-10-08 17:04:24 +03:00
Edgars Eglītis 602f8a5ec9 chore: disable more oxlint rules for tests (#22919) 2026-10-08 17:03:03 +03:00
Mykola Mokhnach 7544a1008d feat(appium)!: replace obsolete windows driver with appium-wincore-driver (#22917)
BREAKING CHANGE: Replace the `windows` known-driver shortcut with `wincore` pointing to appium-wincore-driver instead of the obsolete appium-windows-driver
2026-10-08 11:34:50 +02:00
Michael Lip 3063ec9917 fix(universal-xml-plugin): keep the enabled attribute in transformed source (#22911) 2026-10-08 10:09:49 +02:00
Mohamed Mostafa aed3af9538 fix(support): do not drop directories while fs.mv moves a folder (#22913) 2026-10-08 15:27:12 +09:00
Mohamed Mostafa dbae73b921 fix(execute-driver-plugin): bracket IPv6 server hosts for webdriverio (#22912) 2026-10-08 15:24:06 +09:00
Mykola Mokhnach 8297d31fb1 fix(storage-plugin)!: mount routes under the server base path (#22906)
BREAKING CHANGE: the storage routes (and the stream/events websocket paths) were always mounted at the server root, ignoring --base-path. They are now prefixed with the base path, e.g. /wd/hub/appium/storage/add.
2026-10-07 10:13:24 +02:00
Edgars Eglītis 28c1f9392e chore: fix one more oxlint warning (#22907) 2026-10-06 20:37:46 +03:00
Edgars Eglītis a9a84e7147 chore: apply oxlint plugin fixes (#22901)
* chore: apply oxlint plugin fixes

* chore: make various exports explicit
2026-10-06 20:30:33 +03:00
Mikhail Fleytman e230727090 fix(appium): prevent recursion when logging the first BiDi event of a method (#22897) 2026-10-06 07:40:35 +02:00
Edgars Eglītis c064fc8246 feat(oxc-config): enable all oxlint correctness rules (#22899)
BREAKING CHANGE: enable 45 additional correctness rules
2026-10-05 21:42:27 +03:00
Edgars Eglītis db2df4157c ci: fix CI not running for only mjs/cjs file changes (#22900) 2026-10-05 21:39:47 +03:00
Edgars Eglītis 298b09cdbf chore: apply oxlint correctness fixes (#22896)
* chore: fix some correctness issues

* chore: fix more correctness issues

* chore: more correctness fixes

* chore: remove unnecessary eslint-disable directives

* chore: more correctness fixes

* chore: apply remaining fixes

* chore: minor tuning

* chore: explicitly disable rule

* chore: handle TS error

* chore: tune

* chore: tune more

* chore: address comments

* chore: address some comments

* chore: address remaining comments

* chore: address previously missed copilot commens
2026-10-05 20:52:17 +03:00
Mykola MokhnachandClaude Sonnet 5.5 b8ef48d0cc ci: use --tag for npm publish in release recovery
`--dist-tag` is a lerna-only flag. `npm publish` ignored it and parsed
`beta` as a package spec, so recovery mode could not publish missing
packages.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-05 12:14:39 +02:00
github-actions 6815fbebac chore: publish
- appium@4.0.0-beta.3
 - @appium/base-driver@11.0.0-beta.2
 - @appium/base-plugin@4.0.0-beta.2
 - @appium/docutils@4.0.0-beta.2
 - @appium/driver-test-support@4.0.0-beta.2
 - @appium/execute-driver-plugin@8.0.0-beta.3
 - @appium/fake-driver@7.0.0-beta.3
 - @appium/fake-plugin@5.0.0-beta.3
 - @appium/http-server@1.0.0-beta.2
 - @appium/images-plugin@6.0.0-beta.3
 - @appium/logger@3.0.0-beta.1
 - @appium/opencv@6.0.0-beta.2
 - @appium/oxc-config@2.0.0-beta.1
 - @appium/plugin-test-support@4.0.0-beta.3
 - @appium/relaxed-caps-plugin@4.0.0-beta.3
 - @appium/schema@2.0.0-beta.1
 - @appium/semantic-release-config@2.0.0-beta.1
 - @appium/storage-plugin@3.0.0-beta.3
 - @appium/strongbox@3.0.0-beta.1
 - @appium/support@8.0.0-beta.2
 - @appium/tsconfig@2.0.0-beta.1
 - @appium/types@2.0.0-beta.2
 - @appium/universal-xml-plugin@4.0.0-beta.3
2026-10-05 09:57:28 +00:00
Cassian433andCassian433 b95aaac60b feat(universal-xml-plugin): add execute methods to toggle translation (#22862)
* feat(universal-xml-plugin): add execute methods to toggle translation

Translation could not be turned off during a session, so there was no
way to get the driver's original page source or to run an XPath query
against the original node names while the plugin was active.

This adds two execute methods:

- `universalXml: setEnabled` turns translation off or on for the
  current session. While it's off, getPageSource and findElement(s)
  are passed on unchanged.
- `universalXml: isEnabled` returns the current state.

Translation stays on by default, so existing sessions behave as
before. The context API is not touched, so drivers' assumptions about
the current context are unaffected.

Refs #21630

* chore(universal-xml-plugin): add jsdoc to public methods and mark unused args with void

---------

Co-authored-by: Cassian433 <262026629+Cassian433@users.noreply.github.com>
2026-10-05 17:00:54 +09:00
Kazuaki Matsuo 83122525db fix(appium): import extensions through escaped file URLs (#22884)
* fix(appium): import extensions through escaped file URLs

* docs(appium): clarify why extension imports still require file URLs

* Enhance comments for schemaPath handling

Add comments explaining file URL requirements for Windows and POSIX paths.
2026-10-05 15:27:08 +09:00
Mohamed Mostafa c159d1925c fix(base-driver): remove incomplete app downloads (#22878) 2026-10-05 15:24:38 +09:00
Kazuaki Matsuo b1ec406fd3 docs: document contributor style conventions for agents (#22879)
* feat(oxc-config): restore class member ordering through JS plugins

* docs: document class method ordering for agents and contributors

* docs: keep ordering rules in the contributor style guide

* docs: simplify the agent style guide reference

* docs: add JSDoc and module ordering conventions
2026-10-04 23:10:40 -07:00
Kazuaki Matsuo c5f4217806 fix(base-driver): decode credentials in app download URLs (#22887) 2026-10-05 15:00:38 +09:00
Kazuaki Matsuo 3188c9fbab fix(appium): resolve default extension export conditions (#22885) 2026-10-05 15:00:01 +09:00
Kazuaki Matsuo b791915ffc fix(base-driver): track and remove BiDi subscriptions by ID (#22876)
* fix(base-driver): track and remove BiDi subscriptions by ID

* refactor(base-driver): organize BiDi subscription helpers

* fix(base-driver): address BiDi subscription review feedback

* test(base-driver): cover BiDi unsubscribe review cases

* docs(appium): document BiDi subscription IDs
2026-10-05 14:58:56 +09:00
Kazuaki Matsuo 95c04611e7 fix(appium): apply initial settings from selected capabilities (#22882)
* fix(appium): apply initial settings from selected capabilities

* test(appium): keep settings regressions separate from timeout tests
2026-10-05 14:55:58 +09:00
Kazuaki Matsuo e37f1c1583 fix(appium): start idle timeout after session initialization (#22881) 2026-10-05 14:29:32 +09:00
Kazuaki Matsuo 879873bad5 fix(appium): match global and module BiDi event subscriptions (#22883) 2026-10-05 01:54:47 +09:00
Kazuaki Matsuo 6c9c930cbe fix(support): preserve symlinks inside moved directories (#22880) 2026-10-05 01:54:06 +09:00
Kazuaki Matsuo 4de1a3e62c fix(support): accept transparently decompressed downloads (#22886) 2026-10-05 01:53:47 +09:00
Kazuaki Matsuo ae8a27e02b fix(base-driver): reject queued commands when their session ends (#22874)
* fix(base-driver): reject queued commands when their session ends

* refactor(base-driver): cancel queued commands with AbortController
2026-10-05 01:12:52 +09:00
Kazuaki Matsuo b9c020c6eb fix(appium): explain manual recovery after manifest lock timeouts (#22875)
* fix(appium): recover abandoned manifest locks with heartbeats

* fix(appium): retry released manifest locks promptly

* fix(appium): explain manual manifest lock recovery without new dependencies

* test(appium): keep manifest lock mocks compatible with Node 22
2026-10-05 01:12:36 +09:00
Kazuaki Matsuo e391bb249f fix: use native ESM entrypoint detection (#22868) 2026-10-04 17:39:35 +02:00
Kazuaki Matsuo f24e7046eb fix(appium): roll back sessions when initialization fails (#22873) 2026-10-05 00:21:14 +09:00
Kazuaki Matsuo b077efd67d fix(base-driver): reject unsupported BiDi user-context scopes (#22877) 2026-10-04 10:18:50 +02:00
Kazuaki Matsuo 3875344e11 fix(appium): bracket IPv6 hosts in BiDi websocket URLs (#22872) 2026-10-04 10:02:13 +02:00
Kazuaki Matsuo 11d9904166 fix(appium): detect project homes with prerelease dependencies (#22871) 2026-10-04 10:01:42 +02:00
Kazuaki Matsuo 1b7d1e0eb7 fix(base-driver): validate reset options before creating session state (#22870) 2026-10-04 10:00:44 +02:00
Kazuaki Matsuo 14c42f791c fix(base-driver): return protocol errors for null rejections (#22869) 2026-10-04 09:59:50 +02:00
Kazuaki Matsuo 63e9aa4f57 fix(base-driver): declare the path-to-regexp runtime dependency (#22865) 2026-10-04 09:59:08 +02:00
Kazuaki Matsuo 9de70be6e2 fix(base-driver): keep the idle timer off while commands execute (#22867) 2026-10-04 09:55:12 +02:00
Kazuaki Matsuo c78cfa405f fix(types): align the logger peer dependency with Appium 4 (#22866) 2026-10-04 09:54:12 +02:00
Kazuaki Matsuo 29a60d6e93 fix(support): preserve directories on failed downloads (#22864) 2026-10-04 09:53:22 +02:00
Mohamed Mostafa 1b852557a3 fix(storage-plugin): reject storage file names that start with a dot (#22863) 2026-10-04 08:57:43 +02:00
Mohamed Mostafa 318329fba7 fix(base-driver): report noReset and fullReset together as session not created (#22861) 2026-10-04 15:53:00 +09:00
Edgars Eglītis 6d1f28ed05 fix(base-driver): reject empty array in capabilities.firstMatch (#22860)
BREAKING CHANGE: when creating a new session, capabilities.firstMatch can no longer be set to an empty array, as per the WebDriver spec
2026-10-03 20:54:41 +03:00
Mohamed Mostafa 00547fe9f6 refactor(support): share websocket close codes from net (#22855) 2026-10-03 14:43:16 +02:00
Mohamed Mostafa 3cdc69b198 fix(appium): close bidi sockets when a session ends unexpectedly (#22854) 2026-10-03 14:42:27 +02:00
Mykola Mokhnach 53b36283f3 chore: use version ranges instead of exact pins (#22846) 2026-10-03 14:41:42 +02:00
Edgars Eglītis 5cc6041ce9 chore: remove placeholder doctor script (#22841) 2026-10-02 12:06:06 +03:00
Edgars Eglītis 8fbdd42eab chore: add automatic label for http-server package (#22842) 2026-10-01 16:02:11 +03:00
Edgars Eglītis ebcac6a7e1 chore: replace ora with yocto-spinner (#22840) 2026-09-30 21:53:21 +03:00
Mohamed Mostafa 66ab396a71 fix(appium): do not crash when a proxied bidi socket closes without a sendable code (#22835) 2026-09-30 08:13:56 +02:00
Mohamed Mostafa 15ecc718c4 fix(support): do not leave a file behind when downloadFile fails (#22836) 2026-09-29 22:16:19 +02:00
Mohamed Mostafa f330716793 fix(base-driver): do not arm the idle timer while a command is still running (#22828) 2026-09-29 19:18:15 +02:00
Mykola Mokhnach e033851cdf refactor(support): vendor klaw as internal fs walker (#22826) 2026-09-28 08:06:55 +02:00
Mykola Mokhnach 6b0938d3ac refactor: drop type-fest dependency across the monorepo (#22825) 2026-09-27 20:18:03 +02:00
Mykola Mokhnach bb51494f67 chore: remove unused jsftp, serve-favicon, and mjpeg dev dependencies (#22827) 2026-09-27 20:14:29 +02:00
github-actions d8370ccf19 chore: publish
- appium@4.0.0-beta.2
 - @appium/base-driver@11.0.0-beta.1
 - @appium/base-plugin@4.0.0-beta.1
 - @appium/docutils@4.0.0-beta.1
 - @appium/driver-test-support@4.0.0-beta.1
 - @appium/execute-driver-plugin@8.0.0-beta.2
 - @appium/fake-driver@7.0.0-beta.2
 - @appium/fake-plugin@5.0.0-beta.2
 - @appium/http-server@1.0.0-beta.1
 - @appium/images-plugin@6.0.0-beta.2
 - @appium/opencv@6.0.0-beta.1
 - @appium/plugin-test-support@4.0.0-beta.2
 - @appium/relaxed-caps-plugin@4.0.0-beta.2
 - @appium/storage-plugin@3.0.0-beta.2
 - @appium/support@8.0.0-beta.1
 - @appium/types@2.0.0-beta.1
 - @appium/universal-xml-plugin@4.0.0-beta.2
2026-09-27 07:38:49 +00:00
Kazuaki Matsuo fb89bb7423 fix(support): preserve exclamation marks with apostrophes (#22816) (#22822)
* fix(support): preserve exclamation marks with apostrophes

* test(support): use async shell execution and document workaround

(cherry picked from commit 359ef4ba89)
2026-09-27 03:07:32 +09:00
Mykola Mokhnach dc0568bc65 fix(appium): release the manifest lock before errAndQuit exits (#22821) 2026-09-26 20:05:48 +02:00
Mohamed Mostafa f313306848 fix(base-driver): keep the default idle timeout when newCommandTimeout is null or blank (#22815) 2026-09-26 13:52:08 +02:00
Mykola Mokhnach b7356ee53e feat(appium): accept package names for installed extension commands (#22747) (#22812) 2026-09-24 23:06:25 +02:00
Mykola Mokhnach ad8e7d56f8 chore: backport recent fixes from master (#22810) 2026-09-24 19:16:20 +02:00
Mykola Mokhnach 7a79dc208c refactor(base-driver): vendor Levenshtein distance and drop http-status-codes dep (#22805) 2026-09-22 22:16:34 +02:00
Mykola Mokhnach 822e4d19d5 refactor(http-server): move network/listener-address helpers out of appium (#22804) 2026-09-22 21:33:05 +02:00
Mykola Mokhnach 2596da02d2 fix(base-driver): carry proxy req via AsyncLocalStorage instead of args (#22799) 2026-09-22 18:28:20 +02:00
Mykola Mokhnach 225a1c6d9d refactor(base-driver)!: extract the HTTP/Express server into @appium/http-server (#22800)
BREAKING CHANGE: @appium/base-driver no longer exports server, normalizeBasePath, configureServer, ServerOpts, ConfigureServerOpts, ConfigureHttpOpts, or StartServerOpts, and no longer depends on express/morgan/body-parser/ method-override/path-to-regexp. Use the new @appium/http-server package instead.

BREAKING CHANGE: appium/driver.js and driver.d.ts no longer re-export server/normalizeBasePath/configureServer/ServerOpts (or their types), and no longer use a blanket export *. Import from @appium/http-server directly if you were relying on this undocumented path.
2026-09-22 07:44:13 +02:00
Mykola Mokhnach 22a00c7a55 feat(base-driver)!: exempt getStatus from the commands queue by default (#22796) 2026-09-21 17:14:52 +02:00
Mykola Mokhnach 09a04f4883 fix(support): narrow fs.glob overload for default withFileTypes/lazy (#22795) 2026-09-20 20:49:25 +02:00
Mykola Mokhnach 4f70ff136f docs: add Appium 3 to 4 migration guide (#22790) 2026-09-20 16:25:16 +02:00
github-actions c2e53e15e5 chore: publish
- appium@4.0.0-beta.1
 - @appium/execute-driver-plugin@8.0.0-beta.1
 - @appium/fake-driver@7.0.0-beta.1
 - @appium/fake-plugin@5.0.0-beta.1
 - @appium/images-plugin@6.0.0-beta.1
 - @appium/plugin-test-support@4.0.0-beta.1
 - @appium/relaxed-caps-plugin@4.0.0-beta.1
 - @appium/storage-plugin@3.0.0-beta.1
 - @appium/universal-xml-plugin@4.0.0-beta.1
2026-09-19 21:26:08 +00:00
Mykola Mokhnach b09efc31c6 fix(deps): catch up peerDependencies to published beta.0; drop bootstrap_packages (#22793) 2026-09-19 23:21:31 +02:00
github-actions e303649a42 chore: publish
- appium@4.0.0-beta.0
 - @appium/base-driver@11.0.0-beta.0
 - @appium/base-plugin@4.0.0-beta.0
 - @appium/docutils@4.0.0-beta.0
 - @appium/driver-test-support@4.0.0-beta.0
 - @appium/execute-driver-plugin@8.0.0-beta.0
 - @appium/fake-driver@7.0.0-beta.0
 - @appium/fake-plugin@5.0.0-beta.0
 - @appium/images-plugin@6.0.0-beta.0
 - @appium/logger@3.0.0-beta.0
 - @appium/opencv@6.0.0-beta.0
 - @appium/oxc-config@2.0.0-beta.0
 - @appium/plugin-test-support@4.0.0-beta.0
 - @appium/relaxed-caps-plugin@4.0.0-beta.0
 - @appium/schema@2.0.0-beta.0
 - @appium/semantic-release-config@2.0.0-beta.0
 - @appium/storage-plugin@3.0.0-beta.0
 - @appium/strongbox@3.0.0-beta.0
 - @appium/support@8.0.0-beta.0
 - @appium/tsconfig@2.0.0-beta.0
 - @appium/types@2.0.0-beta.0
 - @appium/universal-xml-plugin@4.0.0-beta.0
2026-09-19 20:35:44 +00:00
Mykola Mokhnach d2483b8bf4 fix(ci): tolerate peer dependency lag in beta releases (#22792) 2026-09-19 22:33:37 +02:00
Mykola Mokhnach b78454ab3e fix(schema): write trailing newline in generated schema JSON (#22791) 2026-09-19 21:50:03 +02:00
Mykola Mokhnach 83b7835733 chore(deps): apply pending major version bumps (#22789) 2026-09-19 21:23:53 +02:00
JustasM 33d866e575 fix(base-driver): clean up aborted session creation (#22733) 2026-09-16 17:55:53 +02:00
Giorgio Boa 0b104f6bf9 feat(appium): add --app-url-rules server arg to restrict remote app URLs (#22753) 2026-09-16 17:51:48 +02:00
Edgars Eglītis 93115d1957 feat(base-driver)!: align timeouts endpoints closer to W3C standard (#22776)
* feat!: align timeouts endpoints closer to W3C standard

* chore: remove unnecessary wrappers
2026-09-14 22:57:53 +03:00
Mykola Mokhnach ac01aa83ad feat(base-driver): add definitions for all WebDriver BiDi commands (#22770) 2026-09-14 08:32:02 +02:00
Mykola Mokhnach 0e91fb756b fix(base-driver)!: don't implicitly allow sessionId/id params in bidi commands (#22771)
BREAKING CHANGE: PayloadParams['validate'] no longer receives a protocol argument; its signature is now (obj: any) => boolean | string | undefined. No implementation in this repo used the second argument.
2026-09-13 22:18:30 +02:00
Mykola Mokhnach 8b62a7971a fix(base-driver): improve bidi command param validation and error messages (#22769) 2026-09-13 18:28:04 +02:00
Mykola Mokhnach a73644fb4f fix(appium): lock the extension manifest while driver/plugin commands run (#22767) 2026-09-13 17:49:31 +02:00
Mykola Mokhnach 33def2cc0b refactor(base-driver)!: split basedriver helpers into a barrel (#22764)
BREAKING CHANGE: Removed the deprecated sessionId argument from generateDriverLogPrefix helper
2026-09-12 13:17:40 +02:00
Edgars Eglītis b7b09f750f chore!: cleanup remaining code for removed JSONWP/MJSONWP methods (#22765)
BREAKING CHANGE: types for the following commands have been removed: `getGeoLocation`, `setGeoLocation`, `getNetworkConnection`, `setNetworkConnection`. The `Location` type has also been removed.
2026-09-12 14:13:24 +03:00
Edgars Eglītis b129cbbcd5 fix: adjust deprecated JSONWP/MJSONWP routes (#22763)
* fix: adjust deprecated JSONWP/MJSONWP routes

* fix: add back geolocation routes for fake-driver

* test: update snapshot
2026-09-12 11:52:52 +03:00
Mykola Mokhnach d6ef7e3f12 fix: Address several TODOs (#22762) 2026-09-12 08:38:47 +02:00
Giorgio Boa b25fc20c51 feat(types): check execute method params against the command signature (#22754) 2026-09-11 18:57:21 +02:00
Mykola Mokhnach 74d1ee6d17 feat(base-driver)!: remove deprecated JSONWP/MJSONWP routes (#22757)
BREAKING CHANGE: Dropped APIs: receive_async_response, element/:elementId GET stub, IME endpoints, orientation, location (geolocation), context/contexts, network_connection, and rotation.
BREAKING CHANGE: Dropped the allowCrossDomainAsyncExecute middleware + its dedicated test suite, which existed solely to CORS-enable the now-removed receive_async_response endpoint.
2026-09-11 14:13:33 +02:00
Mykola Mokhnach bcf1c57999 fix: declare dependencies that were only resolving via hoisting (#22758) 2026-09-11 14:04:00 +02:00
Mykola Mokhnach 5e644a392f feat!: Change the default log level to info (#22756)
BREAKING CHANGE: users relying on the implicit debug default for console/file logs will now see info-level output unless they explicitly pass --log-level debug.
2026-09-10 19:47:36 +02:00
Mykola Mokhnach ff458d7cac refactor(base-driver): split protocol.ts into focused modules (#22750) 2026-09-10 18:39:09 +02:00
Edgars Eglītis 812ca2a9af feat(docutils)!: set 'latest' as default canonical version (#22752)
BREAKING CHANGE: new docs versions are now published with their canonical version set to 'latest'. This value can be changed in mkdocs.yml using the 'mike: canonical_version:' key. The previous behavior can be retained by setting the key value to null.
2026-09-10 18:32:26 +02:00
Mykola Mokhnach 6323df1d73 feat: backport diff from master (#22749) 2026-09-10 18:31:23 +02:00
Mykola Mokhnach 4eb4d7f317 fix(support)!: route non-error CLI console output to stdout (#22742)
BREAKING CHANGE: Any script or tool that consumes Appium CLI output (driver/plugin list/install/update, doctor, etc.) and relies on the previous stream layout will need to be updated. Previously, non-JSON human-readable output (info/success/warning messages) was written to STDERR, with STDOUT reserved almost exclusively for --json output. Now that output goes to STDOUT, and only actual errors go to STDERR.
BREAKING CHANGE: Scripts that discarded STDOUT (1>/dev/null) to suppress "chatter" while relying on STDERR for status text will now see that text on STDOUT instead.
BREAKING CHANGE: Scripts that captured STDERR to detect/log informational or success messages will no longer see them there — only genuine errors will appear on STDERR. --json output and error behavior are unaffected: --json continues to write clean JSON to STDOUT only, and errors continue to go to STDERR.
BREAKING CHANGE: @appium/logger's Log class changes its default output stream from STDERR to STDOUT for all levels except error, and adds an errorStream/stderrLevel pair of properties. Any code that set .stream on a Log instance expecting it to capture all levels (including error) will now need to also set .errorStream to the same target.
2026-09-09 12:55:34 +02:00
Mykola Mokhnach 506bfeeb9a refactor(base-driver)!: replace command mixin with explicit named exports (#22741)
BREAKING CHANGE: @appium/base-driver's default export (BaseDriver) and @appium/base-driver's basedriver/helpers.js default export are removed; use the named exports instead. The appium/driver.js and driver.d.ts re-exports of the default are removed accordingly.
BREAKING CHANGE: BaseDriver.reset() is removed; it was unused, not part of any protocol route or the Driver interface, and had no callers in this monorepo or any driver package that was checked.
BREAKING CHANGE: BaseDriver's constructor is removed in favor of inherited DriverCore construction; opts is no longer a required constructor argument (it now defaults to {}, matching DriverCore).
2026-09-07 20:58:00 +02:00
Mykola Mokhnach 0df8834638 fix: address code review cleanups (plist, env memoize, test reuse) (#22740) 2026-09-07 18:59:34 +02:00
Mykola Mokhnach add1615607 refactor(support)!: replace glob package with native node:fs glob (#22735)
BREAKING CHANGE: @appium/support's fs.glob option shape changed from the glob package's GlobOptions to a smaller, @appium/support-owned GlobOptions interface. Supported fields: cwd, withFileTypes, absolute, lazy. Options like nodir/ignore/nocase are no longer supported directly — callers needing directory filtering can pass withFileTypes: true and filter on .isFile()/.isDirectory(); callers needing path exclusion can filter the returned array by pattern.
2026-09-07 17:38:56 +02:00
Mykola Mokhnach 8840f9f932 feat(appium): validate extension manifests and extensions.yaml via ajv (#22732) 2026-09-06 22:55:54 +02:00
Mykola Mokhnach 926a0a5949 feat(appium)!: replace --allow-cors flag with --allow-insecure=*:cors (#22731)
BREAKING CHANGE: The --allow-cors CLI flag and allow-cors config file property have been removed. Use --allow-insecure=*:cors (or --relaxed-security, optionally overridden with --deny-insecure=*:cors) instead.
2026-09-05 18:19:48 +02:00
Mykola Mokhnach db72249873 refactor(base-driver)!: remove deprecated multi-argument createSession overload (#22730)
BREAKING CHANGE: ISessionHandler.createSession (@appium/types) and LegacyCreateSessionArgs — overload and type deleted, single-argument signature only.
BREAKING CHANGE: BaseDriver.createSession and AppiumDriver.createSession — collapsed to the single-argument mplementation; capability validation now checks the one argument directly instead of scanning three positions for the first W3C-shaped value.
BREAKING CHANGE: FakeDriver.createSession (and the base-driver e2e test double) — overrides updated to match.
BREAKING CHANGE: RelaxedCapsPlugin.createSession — this plugin existed specifically to normalize capabilities across the (now-gone) three positions; its hook is simplified to a single caps argument.
BREAKING CHANGE: POST /session wire route (w3c.ts) — payloadParams.optional no longer sends capabilities three times into the command args.
2026-09-05 16:31:00 +02:00
Mykola Mokhnach f71d980779 refactor(base-driver)!: remove deprecated getSession command (#22725)
BREAKING CHANGE: BaseDriver.getSession, ISessionHandler.getSession, and the GET /session/:sessionId JSONWP route are gone. Use getAppiumSessionCapabilities to retrieve session capabilities and EventCommands.getLogEvents to retrieve event history instead.
BREAKING CHANGE: The appium:eventTimings capability is removed; it only ever controlled whether getSession's response was decorated with event history, which getLogEvents already exposes unconditionally.
2026-09-05 12:33:19 +02:00
Mykola Mokhnach d82d496f9e refactor(support)!: move npm-related helpers into appium package (#22721) 2026-09-05 09:03:29 +02:00
Mykola Mokhnach fe24f527a4 refactor(logger)!: remove default exports and dead logging APIs (#22719)
BREAKING CHANGE: @appium/logger and @appium/support no longer export a default log — import the named log export instead.
BREAKING CHANGE: AppiumLogger#errorAndThrow has been removed; use errorWithException instead.
BREAKING CHANGE: Logger#enableProgress, disableProgress, progressEnabled, enableUnicode, disableUnicode have been removed; they were already permanently disabled no-ops.
2026-09-04 22:46:19 +02:00
Mykola Mokhnach 5875b94fc4 fix(types): declare express and ws as direct dependencies (#22720) 2026-09-04 22:36:39 +02:00
Mykola Mokhnach 62db5f0b20 refactor(base-plugin)!: remove deprecated legacy logger and default export (#22718)
BREAKING CHANGE: Plugin#logger has been removed; use Plugin#log instead.
BREAKING CHANGE: @appium/base-plugin no longer exports BasePlugin as the default export; use the named BasePlugin export.
2026-09-04 21:55:32 +02:00
Mykola Mokhnach ef8a8e1934 refactor!: remove deprecated driverData mechanism (#22716)
BREAKING CHANGE: Core.driverData, DriverData, and the driverData parameter on ISessionHandler.createSession/deleteSession are gone. Any driver overriding get driverData() or relying on the umbrella driver passing sibling-session data into createSession/deleteSession needs to migrate off this mechanism.
BREAKING CHANGE: FakeDriver no longer supports the uniqueApp capability.
2026-09-04 21:24:49 +02:00
Mykola Mokhnach 8709844b21 test: use native WebSocket client in e2e tests instead of ws (#22715) 2026-09-04 11:48:17 +02:00
Mykola Mokhnach 464c271fbc refactor: replace axios with native fetch across the monorepo (#22700) 2026-09-03 18:41:36 +02:00
Mykola Mokhnach 8db8e5748e feat(support)!: move env/APPIUM_HOME resolution into appium package (#22708)
BREAKING CHANGE: npm.installPackage now takes a hasAppiumDependency boolean computed by the caller instead of importing the check itself, avoiding a support -> appium dependency.
BREAKING CHANGE: The env export is removed from @appium/support.
2026-09-03 07:24:58 +02:00
Mykola Mokhnach 8e8c00e6db fix(support): replace bplist-creator/bplist-parser with plist v5 (#22705) 2026-09-02 19:04:22 +02:00
Mykola Mokhnach c6e42f3fb9 refactor(support): remove bluebird dependency (#22704) 2026-09-02 17:27:23 +02:00
Mykola Mokhnach fbeb496142 feat!: remove obsolete eslint-config-appium-ts package (#22699)
BREAKING CHANGE: The @appium/eslint-config-appium-ts package is removed from the monorepo as obsolete
2026-09-01 22:10:58 +02:00
Mykola Mokhnach 9f4a11e719 feat!: bump minimum supported Node.js engine to ^22.22.2 || ^24.15.0 || >=26.0.0 (#22685)
BREAKING CHANGE: the minimum supported Node.js engine is set to ^22.22.2 || ^24.15.0 || >=26.0.0
2026-09-01 18:28:58 +02:00
Mykola Mokhnach 8b7bf30d43 fix(base-driver)!: map timeout and script timeout errors to HTTP 500 (#22686)
BREAKING CHANGE: Changes HTTP return codes for TimeoutError and ScriptTimeoutError from 408 to 500
2026-08-31 21:07:03 +02:00
Mykola Mokhnach 835119d1ce feat(support)!: remove requirePackage and macOsxVersion (#22684)
BREAKING CHANGE: remove node.requirePackage()
BREAKING CHANGE: remove system.macOsxVersion()
2026-08-31 20:02:47 +02:00
Mykola Mokhnach 0b83e77e7a feat(base-driver)!: remove legacy (M)JSONWP protocol support (#22681)
BREAKING CHANGE: @appium/base-driver no longer exports statusCodes or getSummaryByCode (the jsonwp-status module was removed).
BREAKING CHANGE: errorFromMJSONWPStatusCode (aliased as errorFromCode) has been removed; use errorFromW3CJsonCode for W3C error-signature mapping.
BREAKING CHANGE: ProtocolError instances no longer have a jsonwpCode property.
BREAKING CHANGE: WebDriverProxy (formerly JWProxy) no longer proxies to or interprets responses from downstream servers speaking the legacy MJSONWP protocol; the ProtocolConverter class, COMMAND_URLS_CONFLICTS, and the downstreamProtocol getter/setter have been removed. Downstream automation servers must speak W3C WebDriver.
BREAKING CHANGE: Responses no longer duplicate element references under the legacy ELEMENT key; only the W3C element-6066-11e4-a52e-4f735466cecf key is present. The MJSONWP_ELEMENT_KEY constant has been removed.
BREAKING CHANGE: determineProtocol has been removed, and drivers can no longer produce the legacy {sessionId, status, value} response shape; all responses are now W3C-shaped regardless of how the client formatted its createSession capabilities.
BREAKING CHANGE: DriverCore#setProtocolMJSONWP() has been removed.
BREAKING CHANGE: The deprecated JWProxy export has been removed; use WebDriverProxy instead. Its source also moved from lib/jsonwp-proxy/proxy.js to lib/wd-proxy/proxy.js.
BREAKING CHANGE: DriverCore#isMjsonwpProtocol() has been removed.
2026-08-31 19:24:19 +02:00
Edgars Eglītis 454c22f4c2 docs: adjust appium/support readme (#22682) 2026-08-31 13:25:31 +03:00
Mykola Mokhnach a38539deaf feat(base-driver)!: remove deprecated legacy test pages (#22680)
BREAKING CHANGE: The STATIC_DIR export has been removed from @appium/base-driver, along with the built-in /welcome, /test/guinea-pig*, /produce_error, and /crash routes and the APPIUM_ENABLE_LEGACY_TEST_PAGES environment variable that used to enable them.
2026-08-30 15:05:47 +02:00
Mykola Mokhnach e8ee71685b feat(support)!: remove deprecated APIs (#22678)
BREAKING CHANGE: The following @appium/support exports have been removed: process (getProcessIds, killProcess), mkdirp, imageUtil (requireSharp, cropBase64Image), mjpeg (MJpegStream), net's FTP upload
support (FtpUploadOptions, NotHttpUploadOptions; uploadFile only supports http/https now), util's uuidV1/uuidV3/uuidV5/localIp/cancellableDelay/multiResolve, and fs's readPackageJsonFrom/findRoot/ F_OK/R_OK/W_OK/X_OK.
2026-08-30 06:56:59 +02:00
Mykola Mokhnach 8fcdcb30b0 feat(appium)!: remove Selenium Grid 3 support (#22677)
BREAKING CHANGE: The --nodeconfig server argument and the server.nodeconfig config file property no longer exist. Users who need to register with a Selenium Grid should use the Grid 4 relay feature instead (see the Grid guide).
2026-08-29 21:00:53 +02:00
Mykola Mokhnach ec7de4768f feat(base-driver)!: replace spdy with native https transport (#22676)
BREAKING CHANGE: TLS-enabled Appium servers no longer negotiate SPDY/HTTP2 (spdy/2, spdy/3, spdy/3.1, h2) via ALPN; connections are now served as plain HTTPS (HTTP/1.1) using Node's native https module. Clients relying on SPDY or HTTP/2 multiplexing against Appium's TLS listener must switch to HTTP/1.1.
2026-08-29 21:00:24 +02:00
Mykola Mokhnach cd0d8f14e9 feat(appium)!: remove postinstall autoinstall-extensions script (#22675)
BREAKING CHANGE: `npm install -g appium --drivers=... --plugins=...` no longer installs extensions automatically. Use `appium driver/plugin install` or add extensions to package.json instead.
2026-08-29 18:44:33 +02:00
Mykola Mokhnach 3516e50ce6 feat!: convert remaining monorepo packages to ESM-only (#22674)
BREAKING CHANGE: require('@appium/support') no longer works; consumers must import it instead.
BREAKING CHANGE: require('@appium/base-driver') no longer works; drivers extending BaseDriver via require must switch to import.
BREAKING CHANGE: require('@appium/base-plugin') no longer works; plugins extending BasePlugin via require must switch to import.
BREAKING CHANGE: require('appium') no longer works; plugins extending the package via require must switch to import.
BREAKING CHANGE: require('@appium/fake-plugin') no longer works; consumers must import it instead.
BREAKING CHANGE: require('@appium/fake-driver') no longer works; consumers must import it instead.
BREAKING CHANGE: require('@appium/types') no longer works; consumers must import it instead.
BREAKING CHANGE: require('@appium/logger') no longer works; consumers must import it instead.
BREAKING CHANGE: require('@appium/schema') no longer works; consumers must import it instead.
2026-08-29 17:25:29 +02:00
Edgars Eglītis dafc5985bb ci: tune docs publish workflow (#22670) 2026-08-25 20:06:49 +03:00
github-actions[bot]andmykola-mokhnach 02443f98d6 docs(appium): Update documentation translations (#22669)
Co-authored-by: mykola-mokhnach <7767781+mykola-mokhnach@users.noreply.github.com>
2026-08-25 14:16:56 +09:00
renovate[bot] b4097b54d4 chore(execute-driver-plugin): update dependency webdriverio to v9.31.2 (#22667)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-24 21:28:14 +00:00
github-actions b67de03966 chore: publish
- appium@3.7.0
 - @appium/base-driver@10.8.0
 - @appium/base-plugin@3.3.4
 - @appium/docutils@3.0.0
 - @appium/driver-test-support@3.0.0
 - @appium/eslint-config-appium-ts@3.2.5
 - @appium/execute-driver-plugin@7.0.0
 - @appium/fake-driver@6.2.3
 - @appium/fake-plugin@4.4.0
 - @appium/images-plugin@5.0.0
 - @appium/logger@2.0.11
 - @appium/opencv@5.0.0
 - @appium/oxc-config@1.1.1
 - @appium/plugin-test-support@3.0.0
 - @appium/relaxed-caps-plugin@3.0.0
 - @appium/semantic-release-config@1.2.0
 - @appium/storage-plugin@2.0.0
 - @appium/strongbox@2.0.0
 - @appium/support@7.2.7
 - @appium/tsconfig@1.2.1
 - @appium/types@1.7.0
 - @appium/universal-xml-plugin@3.0.0
2026-08-24 20:49:04 +00:00
Mykola Mokhnach 2b39493e98 ci: fix conventional changelog immutable (#22668) 2026-08-24 22:47:13 +02:00
Ajay Mandal e916673e32 docs: add Itsmeaj Linux driver listing (#22666)
* docs: update Linux driver listing

* docs: use Itsmeaj Linux driver package

* docs: identify Itsmeaj as Linux driver maintainer

* docs: add separate AT-SPI2 Linux driver entry

* docs: distinguish Linux drivers by maintainer
2026-08-24 18:42:17 +03:00
Mohamed Mostafa 76ff5aa69a fix(strongbox): custom container paths are always rejected on Windows (#22665) 2026-08-24 17:05:37 +02:00
Mykola Mokhnach b1b313b989 chore(eslint-config-appium-ts): deprecate in favour of oxc-config (#22664) 2026-08-24 15:42:36 +02:00
Mykola Mokhnach 658f88d8c1 feat(release): add next-major beta-branch support to release tooling (#22663) 2026-08-24 15:36:18 +02:00
github-actions[bot]andmykola-mokhnach 3999208fc0 docs(appium): Update documentation translations (#22662)
Co-authored-by: mykola-mokhnach <7767781+mykola-mokhnach@users.noreply.github.com>
2026-08-23 11:59:36 +03:00
Mohamed Mostafa 1f945716a0 fix(universal-xml-plugin): iOS findElements drops matches after the first one (#22644) 2026-08-23 08:58:11 +02:00
renovate[bot] 0a3ad48419 chore(oxc-config): update dependency oxlint to v1.79.0 (#22659)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-22 22:55:36 +02:00
Edgars Eglītis 17be28c750 chore(oxc-config): update oxfmt to v0.64.0 (#22661) 2026-08-22 23:14:52 +03:00
renovate[bot] 746073148f chore(universal-xml-plugin): update dependency @xmldom/xmldom to v0.9.12 (#22660)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-22 22:56:19 +03:00
renovate[bot] 73ef2f7d43 chore(deps): update dependency lerna to v10.0.1 (#22656)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-22 19:49:21 +00:00
renovate[bot] 6fb07a5dbc chore(universal-xml-plugin): update dependency @xmldom/xmldom to v0.9.11 (#22657)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-22 22:40:03 +03:00
Edgars EglītisandSri Harsha 5dd80daf63 docs: adjust formatting for TestMu AI (originally LT) partnership blogpost (#22645)
Co-authored-by: Sri Harsha <12621691+harsha509@users.noreply.github.com>
2026-08-22 11:57:24 +05:30
renovate[bot] 05e36962e6 chore(universal-xml-plugin): update dependency fast-xml-parser to v5.11.0 (#22653)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-22 08:00:45 +02:00
renovate[bot] eaa824f601 chore(eslint-config-appium-ts): update dependency typescript-eslint to v8.67.0 (#22652)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-22 05:35:32 +00:00
renovate[bot] 6cb62d21e8 chore(tsconfig): update dependency @tsconfig/node20 to v20.1.10 (#22654)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-22 01:46:44 +00:00
renovate[bot] 8254a7a3b5 chore(support): update dependency uuid to v14.0.2 (#22651)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-21 21:18:40 +00:00
renovate[bot] 83e99bc752 chore(docutils): update dependency mkdocs-git-revision-date-localized-plugin to v1.5.4 (#22650)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-21 21:13:25 +00:00
Edgars Eglītis 147c0bcac3 ci: adjust publish docs workflow trigger condition (#22649)
* ci: adjust docs workflow trigger condition

* ci: restrict condition to server releases
2026-08-21 18:42:39 +03:00
github-actions[bot]andmykola-mokhnach ddd4251108 docs(appium): Update documentation translations (#22648)
Co-authored-by: mykola-mokhnach <7767781+mykola-mokhnach@users.noreply.github.com>
2026-08-21 11:17:04 +03:00
Mykola Mokhnach b19bd7878b test(appium): re-enable BiDi/IPC e2e tests fixed by webdriverio 9.31.1 (#22647) 2026-08-20 22:32:38 +02:00
Edgars Eglītis 91446df648 docs: add new /appium prefixed routes (#22646) 2026-08-20 23:10:27 +03:00
Mykola Mokhnach d5b316031e feat(base-driver): add /appium-prefixed routes for JSONWP/MJSONWP end points (#22642) 2026-08-20 19:42:02 +02:00
Edgars Eglītis 737cf77b6e chore: run audit fix (#22643) 2026-08-20 11:24:09 +03:00
Mohamed Mostafa c35a12a51d fix(appium): parse --webhook as a URI instead of host:port (#22635) 2026-08-20 07:06:46 +02:00
Edgars Eglītis 5c2aafa208 docs(appium): prefer latest alias for SEO (#22641) 2026-08-19 16:54:21 +03:00
Mykola Mokhnach 4422d99d45 fix(base-driver): anchor CORS async-response regex and match on req.path (#22639) 2026-08-19 11:47:06 +02:00
github-actions[bot]andmykola-mokhnach ce50dd7fa7 docs(appium): Update documentation translations (#22638)
Co-authored-by: mykola-mokhnach <7767781+mykola-mokhnach@users.noreply.github.com>
2026-08-18 15:25:52 +09:00
Mykola Mokhnach f06226b7bb feat(base-driver): add httpServer.frontRouter for global extension middleware (#22632) 2026-08-17 08:22:11 +02:00
Mohamed Mostafa 33459705ed fix(images-plugin): compare yScale with the same float precision as xScale (#22634) 2026-08-14 14:03:06 +02:00
github-actions[bot]andmykola-mokhnach f4ca259852 docs(appium): Update documentation translations (#22630)
Co-authored-by: mykola-mokhnach <7767781+mykola-mokhnach@users.noreply.github.com>
2026-08-13 09:21:37 +09:00
Mykola Mokhnach a49ae68f13 test(appium): add e2e coverage for extension-provided CLI args (#22629) 2026-08-12 19:18:02 +02:00
renovate[bot] 50bd67e59d chore(eslint-config-appium-ts): update dependency eslint-plugin-n to v18.3.0 (#22627)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-12 18:28:21 +02:00
Edgars Eglītis 549b4d4179 docs: update extension script options (#22628) 2026-08-12 11:27:49 +03:00
Mohamed Mostafa 26ffad4f24 fix(base-driver): reject a non-string url without throwing TypeError (#22625) 2026-08-11 22:25:19 +02:00
renovate[bot] 1a81ac88f3 chore(storage-plugin): update dependency ws to v8.21.3 (#22626)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-11 20:14:31 +02:00
Mohamed Mostafa c9fca97cae fix(execute-driver-plugin): fail fast if the script process dies (#22607) 2026-08-08 13:50:14 +02:00
Edgars Eglītis 7d87b26047 chore(oxc-config): update oxfmt to 0.62.0 (#22624) 2026-08-08 10:41:39 +03:00
renovate[bot] 87930a2df1 chore(oxc-config): update dependency oxlint to v1.77.0 (#22622)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-08 09:21:31 +02:00
renovate[bot] 29ee04cf03 chore(eslint-config-appium-ts): update dependency typescript-eslint to v8.66.0 (#22620)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-08 08:52:33 +02:00
renovate[bot] 8892b1022d chore(semantic-release-config): update dependency semantic-release to v25.0.9 (#22619)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-08 08:52:24 +02:00
renovate[bot] b97fe497cf chore(storage-plugin): update dependency ws to v8.21.2 (#22618)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-07 16:38:51 +00:00
renovate[bot] a729d86637 chore(execute-driver-plugin): update dependency webdriverio to v9.30.1 (#22617)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-07 13:54:05 +00:00
renovate[bot] e5bd73272b chore(eslint-config-appium-ts): update dependency eslint-plugin-perfectionist to v5.10.1 (#22616)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-07 10:58:17 +00:00
github-actions[bot]andmykola-mokhnach 037b42454a docs(appium): Update documentation translations (#22615)
Co-authored-by: mykola-mokhnach <7767781+mykola-mokhnach@users.noreply.github.com>
2026-08-07 11:13:49 +03:00
renovate[bot] 5cf259f974 chore(eslint-config-appium-ts): update dependency eslint-plugin-jsdoc to v63.3.3 (#22612)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-06 20:07:56 +02:00
Edgars Eglītis 1486078c82 chore: replace safe occurrences of JWProxy (#22614) 2026-08-06 16:54:45 +03:00
Mykola Mokhnach 01a4eaec49 chore: remove chai and related packages now that all tests use node:assert (#22611) 2026-08-05 23:15:32 +02:00
Mykola Mokhnach 10086b543f test(appium): replace chai with node:assert/strict (#22604) 2026-08-05 22:22:23 +02:00
Mohamed Mostafa 68ce0e2bdd fix(base-driver): return a W3C error for a malformed JSON body (#22606) 2026-08-05 22:22:02 +02:00
renovate[bot] ae4ca98826 chore(deps): update dependency @types/semver to v7.8.0 (#22609)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-05 17:31:44 +02:00
Mykola Mokhnach 1d1631e64e test(base-driver): replace chai with node:assert/strict (#22603) 2026-08-05 17:31:35 +02:00
Mykola Mokhnach 5721d50b3a chore(deps): remove obsolete devDependencies (#22610) 2026-08-05 15:16:15 +02:00
Mykola Mokhnach bd3a120587 fix(driver-test-support): make import/require conditions explicit in exports map (#22605) 2026-08-05 14:41:58 +02:00
Mykola Mokhnach 3e2592804a test(strongbox): replace chai with node:assert/strict (#22593) 2026-08-05 13:30:33 +02:00
Mykola Mokhnach 93c0842dbb test(fake-driver): replace chai with node:assert/strict (#22600) 2026-08-05 12:19:37 +02:00
Mykola Mokhnach 13e8834405 test(support): replace chai with node:assert/strict (#22594) 2026-08-05 12:02:49 +02:00
Mykola Mokhnach 3ab2320e1b test(logger): replace chai with node:assert/strict (#22598) 2026-08-05 12:02:18 +02:00
Mykola Mokhnach 258fc2245a test(opencv): replace chai with node:assert/strict (#22597) 2026-08-05 11:46:11 +02:00
Mykola Mokhnach 5dd9baf5ea test(images-plugin): replace chai with node:assert/strict (#22599) 2026-08-05 11:42:07 +02:00
Mykola Mokhnach 481807f8db test(docutils): replace chai with node:assert/strict (#22602) 2026-08-05 11:41:52 +02:00
Mykola Mokhnach 8a90e74af3 test(execute-driver-plugin): replace chai with node:assert/strict (#22601) 2026-08-05 11:36:00 +02:00
Mykola Mokhnach ea9595ff53 test(relaxed-caps-plugin): replace chai with node:assert/strict (#22596) 2026-08-05 11:35:48 +02:00
github-actions[bot]andmykola-mokhnach 5d796139f6 docs(appium): Update documentation translations (#22608)
Co-authored-by: mykola-mokhnach <7767781+mykola-mokhnach@users.noreply.github.com>
2026-08-05 11:12:18 +03:00
Mykola Mokhnach b3f8a5eea6 test(storage-plugin): replace chai with node:assert/strict (#22595) 2026-08-04 23:47:22 +02:00
Mykola Mokhnach ae549c8613 feat(plugin-test-support)!: Migrate to ESM (#22592)
BREAKING CHANGE: @appium/plugin-test-support is now an ESM-only package. It can no longer be loaded via CommonJS require(); consumers must use import or dynamic import(). Deep imports into the package's internals are no longer possible — only the public entry point is exposed via exports.
2026-08-04 23:46:58 +02:00
Mykola Mokhnach af8efc0882 feat(storage-plugin)!: Migrate to ESM (#22590)
BREAKING CHANGE: @appium/storage-plugin is now an ESM-only package. It can no longer be loaded via CommonJS require(); consumers must use import or dynamic import(). Deep imports into the package's internals are no longer possible — only the public entry point is exposed via exports.
2026-08-04 21:12:58 +02:00
renovate[bot] 63103cfa0d chore(deps): update dependency lerna to v10 (#22582)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-04 21:12:34 +02:00
Mykola Mokhnach b0938e6334 feat(driver-test-support)!: Migrate to ESM (#22591)
BREAKING CHANGE: @appium/driver-test-support is now an ESM-only package. It can no longer be loaded via CommonJS require(); consumers must use import or dynamic import(). Deep imports into the package's internals are no longer possible — only the public entry point is exposed via exports.
2026-08-04 21:01:21 +02:00
Mykola Mokhnach 4b0cc9d37f chore(base-driver): Deprecate the legacy createSession API shape (#22587) 2026-08-04 18:55:03 +02:00
dependabot[bot] 909cc3d4c0 chore(deps): bump the npm_and_yarn group across 1 directory with 2 updates (#22589)
Bumps the npm_and_yarn group with 2 updates in the / directory: [undici](https://github.com/nodejs/undici) and [fast-uri](https://github.com/fastify/fast-uri).


Updates `undici` from 6.27.0 to 6.28.0
- [Release notes](https://github.com/nodejs/undici/releases)
- [Commits](https://github.com/nodejs/undici/compare/v6.27.0...v6.28.0)

Updates `undici` from 7.28.0 to 7.29.0
- [Release notes](https://github.com/nodejs/undici/releases)
- [Commits](https://github.com/nodejs/undici/compare/v6.27.0...v6.28.0)

Updates `fast-uri` from 3.1.4 to 3.1.5
- [Release notes](https://github.com/fastify/fast-uri/releases)
- [Commits](https://github.com/fastify/fast-uri/compare/v3.1.4...v3.1.5)

---
updated-dependencies:
- dependency-name: undici
  dependency-version: 6.28.0
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: undici
  dependency-version: 7.29.0
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: fast-uri
  dependency-version: 3.1.5
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-04 11:14:24 +03:00
dependabot[bot] 1a8f88029b chore(deps): bump ip-address (#22588)
Bumps the npm_and_yarn group with 1 update in the / directory: [ip-address](https://github.com/beaugunderson/ip-address).


Updates `ip-address` from 10.2.0 to 10.4.0
- [Release notes](https://github.com/beaugunderson/ip-address/releases)
- [Commits](https://github.com/beaugunderson/ip-address/compare/v10.2.0...v10.4.0)

---
updated-dependencies:
- dependency-name: ip-address
  dependency-version: 10.4.0
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-04 10:52:02 +03:00
Mohamed Mostafa 3a8049887d fix(support): do not treat a sibling folder as a subpath of the root (#22586) 2026-08-03 20:12:19 +02:00
Mykola Mokhnach 2ed7bb04d8 feat(docutils)!: Migrate to ESM (#22585)
BREAKING CHANGE: @appium/docutils is now an ESM-only package. It can no longer be loaded via CommonJS require(); consumers must use import or dynamic import(). Deep imports into the package's internals are no longer possible — only the public entry point is exposed via exports.
2026-08-03 14:22:39 +02:00
Mykola Mokhnach db87f3cdd4 feat(opencv)!: Migrate to ESM (#22581)
BREAKING CHANGE: @appium/opencv is now an ESM-only package. It can no longer be loaded via CommonJS require(); consumers must use import or dynamic import(). Deep imports into the package's internals are no longer possible — only the public entry point is exposed via exports.
2026-08-02 18:14:58 +02:00
Mykola Mokhnach 2c0f3fd9e3 fix(appium): load @appium/docutils dynamically in docs build script (#22584) 2026-08-02 18:09:44 +02:00
renovate[bot] 63b167c408 chore(support): update dependency axios to v1.19.0 (#22579)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-02 09:59:46 +02:00
renovate[bot] 406dfad41f chore(support): update dependency teen_process to v4.2.1 (#22580)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-02 09:59:33 +02:00
renovate[bot] 85574e7e80 chore(support): update dependency asyncbox to v6.4.2 (#22578)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-02 09:48:01 +02:00
renovate[bot] 336395fcfa chore(oxc-config): update dependency oxlint to v1.76.0 (#22577)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-02 09:47:50 +02:00
Mykola Mokhnach d9e8b3ba73 feat(execute-driver-plugin)!: Migrate to ESM (#22576)
BREAKING CHANGE: @appium/execute-driver-plugin is now an ESM-only package. It can no longer be loaded via CommonJS require(); consumers must use import or dynamic import(). Deep imports into the package's internals are no longer possible — only the public entry point is exposed via exports. 
BREAKING CHANGE: The forked child process (execute-child.js) now runs as ESM as well, since it inherits "type": "module" from the package's package.json.
2026-08-01 21:31:46 +02:00
Mykola Mokhnach e625408f55 feat(relaxed-caps-plugin)!: Migrate to ESM (#22573)
BREAKING CHANGE: @appium/relaxed-caps-plugin is now an ESM-only package. It can no longer be loaded via CommonJS require(); consumers must use import or dynamic import(). Deep imports into the package's internals are no longer possible — only the public entry point is exposed via exports.
2026-08-01 21:31:07 +02:00
Mykola Mokhnach d2a981a45f feat(images-plugin)!: Migrate to ESM (#22574)
BREAKING CHANGE: @appium/images-plugin is now an ESM-only package. It can no longer be loaded via CommonJS require(); consumers must use import or dynamic import(). Deep imports into the package's internals are no longer possible — only the public entry point is exposed via exports.
2026-08-01 21:30:46 +02:00
Mykola Mokhnach 76f2dd3b36 test(appium): use dynamic import instead of require in extension-config test (#22575) 2026-08-01 17:36:25 +02:00
Mohamed Mostafa 03dac9d6e8 fix(base-driver): keep the find error if the page source fails (#22572) 2026-08-01 16:57:03 +02:00
dependabot[bot] eb93d49802 chore(deps-dev): bump nx in the npm_and_yarn group across 1 directory (#22569)
Bumps the npm_and_yarn group with 1 update in the / directory: [nx](https://github.com/nrwl/nx/tree/HEAD/packages/nx).


Updates `nx` from 22.6.3 to 22.7.8
- [Release notes](https://github.com/nrwl/nx/releases)
- [Commits](https://github.com/nrwl/nx/commits/22.7.8/packages/nx)

---
updated-dependencies:
- dependency-name: nx
  dependency-version: 22.7.8
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-01 16:55:07 +02:00
renovate[bot] 3f21bcf1a7 chore(deps): update dependency npm-run-all2 to v9.0.3 (#22570)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-01 16:54:58 +02:00
renovate[bot] d708029325 chore(oxc-config): update dependency oxfmt to v0.61.0 (#22571)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-01 16:54:51 +02:00
renovate[bot] 485b8d7a70 chore(eslint-config-appium-ts): update dependency eslint-plugin-jsdoc to v63.3.2 (#22567)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-01 01:10:47 +00:00
renovate[bot] c688932525 chore(docutils): update dependency yargs to v18.1.0 (#22566)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-31 22:32:00 +00:00
Mohamed Mostafa 77d159838f fix(images-plugin): avoid NaN coordinates when an action offset is unset (#22568)
The plugin adds the image element center to the offset carried by
pointerMove and scroll actions. That payload is not validated anywhere,
so an action arriving without x or y made the sum NaN, and the driver
got NaN as the target coordinate. Count an unset offset as zero.
2026-07-31 19:31:02 +02:00
Mykola Mokhnach c733c090f6 feat(strongbox)!: Migrate package to ESM (#22563)
BREAKING CHANGE: @appium/strongbox is now an ESM-only package. It can no longer be loaded via CommonJS require(); consumers must use import or dynamic import(). No other package in this monorepo currently depends on @appium/strongbox, so this only affects external consumers.
2026-07-31 17:25:00 +02:00
renovate[bot] 2a07c2818c chore(deps): update dependency eslint to v10.8.0 (#22565)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-31 11:08:47 +00:00
github-actions[bot]andmykola-mokhnach 52cbfd2708 docs(appium): Update documentation translations (#22564)
Co-authored-by: mykola-mokhnach <7767781+mykola-mokhnach@users.noreply.github.com>
2026-07-31 09:47:59 +09:00
Mykola Mokhnach a551c38171 feat(storage-plugin): Add route aliases (#22558) 2026-07-30 21:17:15 +02:00
renovate[bot] 0f42831afa chore(semantic-release-config): update dependency @semantic-release/git to v11.0.1 (#22562)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-30 12:45:01 +00:00
Mykola Mokhnach 5c870329a3 feat(universal-xml-plugin)!: Migrate package to ESM (#22557)
BREAKING CHANGE: @appium/universal-xml-plugin is now an ESM-only package. It can no longer be loaded via CommonJS require(); consumers must use import or dynamic import(). Appium's own extension loader already loads plugins via dynamic import(), so appium plugin install/use universal-xml is unaffected. Deep imports into the package's internals are no longer possible — only the public entry point is exposed via exports.
2026-07-29 21:59:11 +02:00
Mykola Mokhnach a236fdbc97 chore(base-driver): Group route definitions (#22556) 2026-07-29 20:59:05 +02:00
Mykola Mokhnach a2e9cef145 fix(ci): fall back to plain npm install only on Socket Firewall's own backend failure (#22559) 2026-07-29 20:11:42 +02:00
renovate[bot] 7d72502769 chore(oxc-config): update dependency oxlint to v1.75.0 (#22547)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-29 19:13:22 +02:00
renovate[bot] 12a91e5293 chore(oxc-config): update dependency oxlint-tsgolint to v7 (#22551)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-29 18:58:27 +02:00
Mykola Mokhnach d093b76bef chore(support): Flex sharp dependency version (#22555) 2026-07-28 20:27:27 +02:00
Mykola Mokhnach 120b9ddfac style: Update EditorConfig to include additional file types 2026-07-28 20:03:30 +02:00
ian zhang 281e61c297 fix(base-driver,storage-plugin): redact sensitive payloads in debug logs (#22549) 2026-07-27 20:21:15 -07:00
Mohamed Mostafa 8ac6bfcc3b fix(logger): mask secure values surrounded by non-word characters (#22553)
`\b` only asserts a boundary next to a word character, so text rules like
`P@ssw0rd!` never matched anything. Now it is only added to the sides where
the value itself begins or ends with a word character.
2026-07-26 10:34:47 -07:00
Edgars Eglītis cdd448800e docs: update aggregate changelog (#22552) 2026-07-26 13:18:37 +03:00
renovate[bot] e977a29b0b chore(support): update dependency teen_process to v4.1.10 (#22550)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-26 08:31:55 +00:00
renovate[bot] 2d8fa2662e chore(oxc-config): update dependency oxfmt to v0.60.0 (#22543)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-25 20:00:57 +03:00
renovate[bot] b96c8fa718 chore(execute-driver-plugin): update dependency webdriverio to v9.30.0 (#22542)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-25 10:06:20 +00:00
Mykola Mokhnach c6a88e7dd7 ci: tune publish retry success criteria (#22546) 2026-07-25 09:06:09 +02:00
Mykola Mokhnach 087a83a27e ci: Fix lerna publish command (#22545) 2026-07-25 08:54:57 +02:00
Mykola Mokhnach 6a241d760a ci: Add an option to recover missing packages (#22544) 2026-07-25 08:48:19 +02:00
github-actions e648569dfb chore: publish
- appium@3.6.0
 - @appium/base-driver@10.7.2
 - @appium/base-plugin@3.3.3
 - @appium/docutils@2.5.2
 - @appium/driver-test-support@2.0.1
 - @appium/eslint-config-appium-ts@3.2.4
 - @appium/execute-driver-plugin@6.0.7
 - @appium/fake-driver@6.2.2
 - @appium/fake-plugin@4.3.3
 - @appium/images-plugin@4.2.8
 - @appium/logger@2.0.10
 - @appium/opencv@4.1.7
 - @appium/oxc-config@1.1.0
 - @appium/plugin-test-support@2.0.1
 - @appium/relaxed-caps-plugin@2.2.5
 - @appium/schema@1.3.0
 - @appium/semantic-release-config@1.1.0
 - @appium/storage-plugin@1.1.8
 - @appium/strongbox@1.1.3
 - @appium/support@7.2.6
 - @appium/tsconfig@1.2.0
 - @appium/types@1.6.0
 - @appium/universal-xml-plugin@2.1.7
2026-07-25 06:12:06 +00:00
Mykola Mokhnach ea6a2e117f fix: typescript config (#22540) 2026-07-24 22:22:31 +02:00
Edgars Eglītis 4fffe28f89 chore: run npm audit fix (#22541) 2026-07-24 23:19:25 +03:00
dependabot[bot] a4a9022303 chore(deps): bump fast-uri in the npm_and_yarn group across 1 directory (#22539)
Bumps the npm_and_yarn group with 1 update in the / directory: [fast-uri](https://github.com/fastify/fast-uri).


Updates `fast-uri` from 3.1.2 to 3.1.4
- [Release notes](https://github.com/fastify/fast-uri/releases)
- [Commits](https://github.com/fastify/fast-uri/compare/v3.1.2...v3.1.4)

---
updated-dependencies:
- dependency-name: fast-uri
  dependency-version: 3.1.4
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-24 23:07:00 +03:00
renovate[bot] 8fd47be066 chore(eslint-config-appium-ts): update dependency typescript-eslint to v8.65.0 (#22538)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-24 21:36:17 +02:00
renovate[bot] 0af2a4bfd8 chore(deps): update dependency sinon to v22.1.0 (#22537)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-24 21:36:04 +02:00
renovate[bot] e0c9018453 chore(eslint-config-appium-ts): update dependency eslint-plugin-jsdoc to v63.2.0 (#22536)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-23 12:51:38 +00:00
Mykola Mokhnach f4d14c1969 feat(tsconfig): hoist typescript version (#22533) 2026-07-22 18:11:49 +02:00
Mykola Mokhnach e44e5ea918 chore: Use https protocol to access versions and download icons (#22534) 2026-07-22 18:06:41 +02:00
Mykola Mokhnach 32f0bdf870 feat(semantic-release-config): add shared semantic-release configuration package (#22527) 2026-07-22 15:52:47 +02:00
Mykola Mokhnach bf30246a65 chore: Linter warnings (#22532) 2026-07-22 10:19:55 +02:00
renovate[bot] 764cfbae75 chore(oxc-config): update dependency oxlint-tsgolint to v0.25.0 (#22530)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-22 10:54:51 +03:00
dependabot[bot] 5f0534f154 chore(deps): bump fast-uri in the npm_and_yarn group across 1 directory (#22531)
Bumps the npm_and_yarn group with 1 update in the / directory: [fast-uri](https://github.com/fastify/fast-uri).


Updates `fast-uri` from 3.1.2 to 3.1.4
- [Release notes](https://github.com/fastify/fast-uri/releases)
- [Commits](https://github.com/fastify/fast-uri/compare/v3.1.2...v3.1.4)

---
updated-dependencies:
- dependency-name: fast-uri
  dependency-version: 3.1.4
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-22 09:37:46 +02:00
renovate[bot] 0b7e7419ef chore(oxc-config): update dependency oxlint to v1.74.0 (#22529)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-22 09:36:49 +02:00
Mykola Mokhnach fee213e49d feat: Add centralised Oxc lint and format config (#22489) 2026-07-22 09:24:20 +02:00
github-actions[bot]andmykola-mokhnach 2fa12e915a docs(appium): Update documentation translations (#22528)
Co-authored-by: mykola-mokhnach <7767781+mykola-mokhnach@users.noreply.github.com>
2026-07-21 19:22:18 -07:00
Asijit Manna f943fab141 feat(appium): add --allow-unknown-args to tolerate unrecognized server CLI arguments (#22477) 2026-07-21 15:20:09 +02:00
Edgars Eglītis 6609b2be57 fix(base-driver): fix typo in generic sensor protocol route (#22525) 2026-07-21 11:47:08 +03:00
Mykola Mokhnach f17899a0f5 ci: Remove the redundant recursion API param (#22524) 2026-07-21 10:36:45 +02:00
renovate[bot] 350eab89a5 chore(eslint-config-appium-ts): update dependency eslint-plugin-jsdoc to v63.1.0 (#22522)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-21 08:32:54 +02:00
renovate[bot] 57092711c0 chore(workflows): update actions/checkout action to v7.0.1 (#22521)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-21 08:30:47 +02:00
Mykola Mokhnach cac18e30b5 chore: Remove lodash usage from scripts (#22518) 2026-07-20 22:51:37 +02:00
Mykola Mokhnach 77d249226a fix(docutils): Duplicate package imports (#22520) 2026-07-20 22:49:00 +02:00
Mykola Mokhnach 154ea18252 fix(appium): Duplicated module import (#22519) 2026-07-20 22:48:51 +02:00
renovate[bot] 0cfafb23c0 chore(universal-xml-plugin): update dependency fast-xml-parser to v5.10.1 (#22516)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-19 14:37:19 +00:00
Kazuaki Matsuo b56646198c fix: use module sync in appium/support (#22514)
* fix: use module sync in appium/support

* use node assert

* add __filename

* fix format
2026-07-18 07:34:45 -07:00
renovate[bot] 13d3b615e6 chore(storage-plugin): update dependency ws to v8.21.1 (#22513)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-18 13:33:44 +00:00
renovate[bot] 7c0d771408 chore(docutils): update dependency mkdocs-material to v9.7.7 (#22512)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-18 09:15:07 +00:00
renovate[bot] 0a40623085 chore(eslint-config-appium-ts): update dependency typescript-eslint to v8.64.0 (#22505)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-17 15:02:17 +00:00
renovate[bot] b9ad1f1712 chore(workflows): update slackapi/slack-github-action action to v4 (#22511)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-17 13:17:37 +03:00
github-actions[bot]andmykola-mokhnach 608668ed47 docs(appium): Update documentation translations (#22504)
Co-authored-by: mykola-mokhnach <7767781+mykola-mokhnach@users.noreply.github.com>
2026-07-17 13:11:19 +03:00
renovate[bot] 73d4b7760b chore(workflows): update actions/setup-node action to v7 (#22509)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-17 12:57:46 +03:00
renovate[bot] 5112541a34 chore(deps): update dependency oxfmt to v0.59.0 (#22508)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-17 11:56:39 +03:00
Edgars Eglītis cfde82a40e chore(deps): update argparse to v3 (#22507) 2026-07-17 11:51:00 +03:00
renovate[bot] 0f3fd55c65 chore(workflows): update actions/setup-node action to v6.5.0 (#22503)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-16 22:10:20 +00:00
renovate[bot] 67cf081098 chore(universal-xml-plugin): update dependency fast-xml-parser to v5.10.0 (#22502)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-16 18:01:17 +00:00
renovate[bot] d63f5441c7 chore(support): update dependency teen_process to v4.1.9 (#22501)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-16 08:59:17 +00:00
Kazuaki Matsuo 0bb61073e2 docs: drop appiumpro.com (#22500) 2026-07-16 00:57:32 -07:00
renovate[bot] 52eb9e308e chore(support): update dependency shell-quote to v1.10.0 (#22498)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-14 22:14:53 +00:00
renovate[bot] 868153fee4 chore(deps): update dependency eslint to v10.7.0 (#22497)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-14 16:49:16 +00:00
renovate[bot] c25293b23e chore(eslint-config-appium-ts): update dependency eslint-plugin-n to v18.2.2 (#22496)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-14 09:58:51 +00:00
renovate[bot] 4c380d7604 chore(eslint-config-appium-ts): update dependency eslint-plugin-jsdoc to v63.0.13 (#22495)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-13 15:53:59 +00:00
renovate[bot] 1e93aec531 chore(workflows): update slackapi/slack-github-action action to v3.0.5 (#22493)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-12 17:08:10 +00:00
renovate[bot] 22f8a9b04d chore(support): update dependency asyncbox to v6.3.5 (#22492)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-12 08:45:48 +00:00
renovate[bot] 1a7199fbbc chore(support): update dependency teen_process to v4.1.8 (#22488)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-11 22:45:13 +00:00
renovate[bot] fa9c4b2aab chore(storage-plugin): update dependency lru-cache to v11.5.2 (#22487)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-11 18:34:39 +00:00
renovate[bot] 3836f0e614 chore(deps): update dependency @types/node to v24.13.3 (#22490)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-11 13:36:51 +00:00
Edgars Eglītis 6e3fd0a8bc chore: format (#22491) 2026-07-11 14:13:52 +03:00
Mykola Mokhnach d5b1393285 feat: Add codegraph (#22485) 2026-07-11 08:35:38 +02:00
renovate[bot] 123b0e1908 chore(eslint-config-appium-ts): update dependency typescript-eslint to v8.63.0 (#22484)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-11 00:40:30 +00:00
Mykola Mokhnach eabe372b0b test: enable source mapping (#22486) 2026-07-10 22:03:27 +02:00
renovate[bot] 416c3349b7 chore(eslint-config-appium-ts): update dependency eslint-plugin-perfectionist to v5.10.0 (#22483)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-10 09:42:04 +00:00
renovate[bot] ad720f5ca0 chore(deps): update dependency oxfmt to v0.58.0 (#22482)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-10 11:15:58 +03:00
renovate[bot] 6d31d6acb2 chore(eslint-config-appium-ts): update dependency eslint-plugin-jsdoc to v63.0.12 (#22480)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-09 21:46:14 +00:00
renovate[bot] 4e93a63ac5 chore(types): update dependency type-fest to v5.8.0 (#22479)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-08 09:46:27 +00:00
renovate[bot] 7233dd315a chore(eslint-config-appium-ts): update dependency eslint-plugin-jsdoc to v63.0.11 (#22478)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-07 16:14:48 +00:00
github-actions[bot]andmykola-mokhnach 97d53ffa5b docs(appium): Update documentation translations (#22476)
Co-authored-by: mykola-mokhnach <7767781+mykola-mokhnach@users.noreply.github.com>
2026-07-05 18:31:45 -07:00
Mykola Mokhnach 2cec9fa27f chore: Remove redundant chai type references (#22475) 2026-07-05 14:41:16 +02:00
renovate[bot] a1a7b7e981 chore(support): update dependency sharp to v0.35.3 (#22472)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-04 15:31:47 +03:00
Mykola Mokhnach 228103f489 chore(appium): Drop mocha (#22469) 2026-07-04 13:57:57 +02:00
renovate[bot] 9dcb2c4056 chore(deps): update dependency @types/sinon to v22 (#22471)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-04 11:59:35 +03:00
Asijit Manna a9dd7f5e41 fix(base-driver): redact proxied response body in proxy logs (#22470) 2026-07-03 21:09:38 +02:00
Mykola Mokhnach de2cd50b96 fix(base-driver): Perform shallow clone of initial options (#22465) 2026-07-03 20:59:18 +02:00
renovate[bot] ccb3106814 chore(support): update dependency asyncbox to v6.3.4 (#22468)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-03 13:53:38 +00:00
renovate[bot] f707dfa174 chore(eslint-config-appium-ts): update dependency typescript-eslint to v8.62.1 (#22467)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-03 09:52:31 +00:00
Mykola Mokhnach 53b2db196d chore(docutils): Drop mocha (#22466) 2026-07-03 08:38:03 +02:00
Mykola Mokhnach 279ebb5cd6 chore(driver-test-support): Drop mocha and chai (#22464) 2026-07-03 06:21:52 +02:00
Mykola Mokhnach f1fc5a6869 chore(fake-driver): Drop mocha (#22463) 2026-07-02 22:07:42 +02:00
Mykola Mokhnach 24586d4ed9 chore(fake-plugin): Drop mocha and chai (#22462) 2026-07-02 20:52:27 +02:00
Mykola Mokhnach 4915433aae chore(logger): Drop mocha (#22457) 2026-07-02 18:51:19 +02:00
Mykola Mokhnach fd70da7ab4 fix: Typescript references (#22458) 2026-07-02 18:39:05 +02:00
renovate[bot] fa769cebd9 chore(deps): update dependency eslint to v10.6.0 (#22455)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-02 14:43:41 +00:00
renovate[bot] 1eb55e85d7 chore(support): update dependency asyncbox to v6.3.1 (#22460)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-02 11:14:34 +00:00
dependabot[bot] f98eea77d1 chore(deps-dev): bump the npm_and_yarn group across 1 directory with 2 updates (#22459)
Bumps the npm_and_yarn group with 2 updates in the / directory: [@sigstore/verify](https://github.com/sigstore/sigstore-js) and [sigstore](https://github.com/sigstore/sigstore-js).


Updates `@sigstore/verify` from 3.1.0 to 3.1.1
- [Release notes](https://github.com/sigstore/sigstore-js/releases)
- [Commits](https://github.com/sigstore/sigstore-js/compare/sigstore@3.1.0...@sigstore/verify@3.1.1)

Updates `sigstore` from 4.1.0 to 4.1.1
- [Release notes](https://github.com/sigstore/sigstore-js/releases)
- [Commits](https://github.com/sigstore/sigstore-js/compare/sigstore@4.1.0...sigstore@4.1.1)

---
updated-dependencies:
- dependency-name: "@sigstore/verify"
  dependency-version: 3.1.1
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: sigstore
  dependency-version: 4.1.1
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-02 08:33:47 +03:00
renovate[bot] 4cbcf95d77 chore(support): update dependency teen_process to v4.1.7 (#22452)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-02 03:42:12 +00:00
renovate[bot] 4c947872f1 chore(execute-driver-plugin): update dependency webdriverio to v9.29.1 (#22451)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-01 23:26:49 +00:00
renovate[bot] 11882be379 chore(eslint-config-appium-ts): update dependency eslint-plugin-import-x to v4.17.1 (#22454)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-01 14:38:04 +00:00
Mykola Mokhnach e3334b4dd7 chore(opencv): Drop mocha (#22456) 2026-07-01 16:17:16 +02:00
Mykola Mokhnach a10f403bb1 chore(relaxed-caps-plugin): Ditch mocha (#22453) 2026-07-01 13:40:08 +02:00
Mykola Mokhnach 08d50889c2 chore: Migrate from prettier to oxfmt (#22450) 2026-07-01 08:54:13 +02:00
renovate[bot] d3628a9410 chore(eslint-config-appium-ts): update dependency eslint-plugin-jsdoc to v63.0.10 (#22448)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-30 13:51:26 +00:00
Mykola Mokhnach 8d6b5f87c7 chore(support): Drop mocha (#22444) 2026-06-30 07:56:15 +02:00
Mykola Mokhnach 531b20a3c1 chore(strongbox): Drop mocha (#22446) 2026-06-29 22:03:12 +02:00
renovate[bot] 6699d1b266 chore(eslint-config-appium-ts): update dependency eslint-plugin-n to v18.2.1 (#22445)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-29 12:36:06 +00:00
renovate[bot] 37a6006537 chore(support): update dependency shell-quote to v1.9.0 (#22442)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-28 12:49:57 +00:00
renovate[bot] 1b449709d1 chore(eslint-config-appium-ts): update dependency eslint-plugin-jsdoc to v63.0.8 (#22441)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-28 09:14:18 +00:00
Mykola Mokhnach 0e4ecc49d6 chore(universal-xml-plugin): Drop mocha and chai (#22440) 2026-06-27 22:18:18 +02:00
Mykola Mokhnach bf29503054 chore(base-driver): Drop mocha (#22435) 2026-06-27 11:58:04 +02:00
dependabot[bot] ab0633a70c chore(deps-dev): bump @sigstore/core (#22439)
Bumps the npm_and_yarn group with 1 update in the / directory: [@sigstore/core](https://github.com/sigstore/sigstore-js).


Updates `@sigstore/core` from 3.1.0 to 3.2.1
- [Release notes](https://github.com/sigstore/sigstore-js/releases)
- [Commits](https://github.com/sigstore/sigstore-js/compare/sigstore@3.1.0...@sigstore/core@3.2.1)

---
updated-dependencies:
- dependency-name: "@sigstore/core"
  dependency-version: 3.2.1
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-27 12:46:21 +03:00
Mykola Mokhnach 0c44ddeebe chore(base-plugin): Drop mocha and chai (#22438) 2026-06-27 08:33:28 +02:00
Mykola Mokhnach d9eb36f413 fix(support): Prevent extraction of external symlinks (#22436) 2026-06-27 07:59:04 +02:00
github-actions[bot]andmykola-mokhnach b483d82b13 docs(appium): Update documentation translations (#22437)
Co-authored-by: mykola-mokhnach <7767781+mykola-mokhnach@users.noreply.github.com>
2026-06-26 22:56:09 -07:00
Mykola Mokhnach 93e81777b9 chore(images-plugin): Drop mocha (#22434) 2026-06-26 20:34:34 +02:00
renovate[bot] 623ef38f2a chore(eslint-config-appium-ts): update dependency typescript-eslint to v8.62.0 (#22433)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-26 08:17:37 +02:00
renovate[bot] 5c696c5c91 chore(eslint-config-appium-ts): update dependency eslint-plugin-import-x to v4.17.0 (#22431)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-26 08:17:22 +02:00
renovate[bot] 98215066ee chore(support): update dependency axios to v1.18.1 (#22430)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-26 08:17:05 +02:00
Mykola Mokhnach 8026a953aa chore(execute-driver-plugin): Drop mocha usage (#22432) 2026-06-26 08:12:04 +02:00
Mykola Mokhnach c8556f172e chore(storage-plugin): Drop mocha usage (#22429) 2026-06-26 07:47:56 +02:00
Mykola Mokhnach e9a41bd9ae chore(base-driver): Migrate index to typescript (#22428) 2026-06-26 07:47:34 +02:00
Mykola Mokhnach e810f2f23d test(base-driver): Add tests to typescript build validation (#22427) 2026-06-25 19:35:23 +02:00
Mykola Mokhnach c04c38d1e6 chore: Update driver-test-support to not export mocha-specific suites (#22424) 2026-06-25 18:33:38 +02:00
renovate[bot] d9c4e4c4ad chore(eslint-config-appium-ts): update dependency eslint-plugin-jsdoc to v63.0.7 (#22426)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-25 09:05:44 +00:00
renovate[bot] bce22834ba chore(eslint-config-appium-ts): update dependency eslint-plugin-unicorn to v68 (#22420)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-24 16:25:18 +02:00
Mykola Mokhnach ac65be5a1e chore: Update pluginE2EHarness to drop implicit mocha deps (#22421) 2026-06-24 16:24:35 +02:00
renovate[bot] 1e570c4d29 chore(support): update dependency uuid to v14.0.1 (#22422)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-24 14:01:35 +00:00
renovate[bot] 52ec456d17 chore(universal-xml-plugin): update dependency fast-xml-parser to v5.9.3 (#22419)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-23 18:12:07 +00:00
renovate[bot] a89e429a84 chore(support): update dependency semver to v7.8.5 (#22417)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-23 14:49:58 +00:00
renovate[bot] 4b0666d7d5 chore(support): update dependency sharp to v0.35.2 (#22418)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-23 11:27:47 +03:00
renovate[bot] 816ca05020 chore(execute-driver-plugin): update webdriverio monorepo to v9.29.0 (#22416)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-22 13:56:01 +00:00
Mykola Mokhnach 31036f65b2 chore: Ditch unmaintained midnight-smoker dependency (#22414) 2026-06-21 14:22:23 +02:00
renovate[bot] 4bb987c037 chore(workflows): update actions/checkout action to v7 (#22413)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-21 15:07:56 +03:00
renovate[bot] 0162f3ab4e chore(base-driver): update dependency body-parser to v2.3.0 (#22411)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-21 13:09:00 +02:00
renovate[bot] 813c6b5b7a chore(eslint-config-appium-ts): update dependency eslint-plugin-jsdoc to v63.0.6 (#22410)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-21 13:08:47 +02:00
renovate[bot] edc2013073 chore(universal-xml-plugin): update dependency fast-xml-parser to v5.9.2 (#22409)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-20 17:29:10 +00:00
dependabot[bot] 979a6e4c9d chore(deps): bump undici in the npm_and_yarn group across 1 directory (#22404)
Bumps the npm_and_yarn group with 1 update in the / directory: [undici](https://github.com/nodejs/undici).


Updates `undici` from 6.24.0 to 6.27.0
- [Release notes](https://github.com/nodejs/undici/releases)
- [Commits](https://github.com/nodejs/undici/compare/v6.24.0...v6.27.0)

---
updated-dependencies:
- dependency-name: undici
  dependency-version: 6.27.0
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-20 18:59:37 +03:00
renovate[bot] 1cc4f0f2e5 chore(support): update dependency teen_process to v4.1.5 (#22408)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-20 13:56:00 +00:00
renovate[bot] 0467311286 chore(eslint-config-appium-ts): update typescript-eslint monorepo to v8.61.1 (#22407)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-20 12:37:45 +00:00
renovate[bot] cb9c476fbb chore(eslint-config-appium-ts): update dependency eslint-plugin-jsdoc to v63.0.5 (#22406)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-20 08:35:20 +00:00
github-actions[bot]andmykola-mokhnach 44278bdfc3 docs(appium): Update documentation translations (#22405)
Co-authored-by: mykola-mokhnach <7767781+mykola-mokhnach@users.noreply.github.com>
2026-06-19 20:02:12 -07:00
renovate[bot] a8418f95e9 chore(eslint-config-appium-ts): update dependency eslint-plugin-perfectionist to v5.9.1 (#22403)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-19 19:45:19 +00:00
renovate[bot] 7e67b54726 chore(eslint-config-appium-ts): update dependency eslint-plugin-jsdoc to v63.0.4 (#22402)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-19 13:49:33 +00:00
817 changed files with 52203 additions and 32570 deletions
+10
View File
@@ -0,0 +1,10 @@
<!-- CODEGRAPH_START -->
## CodeGraph
In repositories indexed by CodeGraph (a `.codegraph/` directory exists at the repo root), reach for it BEFORE grep/find or reading files when you need to understand or locate code:
- **MCP tool** (when available): `codegraph_explore` answers most code questions in one call — the relevant symbols' verbatim source plus the call paths between them, including dynamic-dispatch hops grep can't follow. Name a file or symbol in the query to read its current line-numbered source. If it's listed but deferred, load it by name via tool search.
- **Shell** (always works): `codegraph explore "<symbol names or question>"` prints the same output.
If there is no `.codegraph/` directory, skip CodeGraph entirely — indexing is the user's decision.
<!-- CODEGRAPH_END -->
+17
View File
@@ -0,0 +1,17 @@
{
"permissions": {
"allow": ["mcp__codegraph__*"]
},
"hooks": {
"UserPromptSubmit": [
{
"hooks": [
{
"type": "command",
"command": "codegraph prompt-hook"
}
]
}
]
}
}
+5
View File
@@ -0,0 +1,5 @@
# CodeGraph data files — local to each machine, not for committing.
# Ignore everything in .codegraph/ except this file itself, so transient
# files (the database, daemon.pid, sockets, logs) never show up in git.
*
!.gitignore
+9
View File
@@ -0,0 +1,9 @@
{
"mcpServers": {
"codegraph": {
"type": "stdio",
"command": "codegraph",
"args": ["serve", "--mcp", "--path", "."]
}
}
}
+2 -1
View File
@@ -1,13 +1,14 @@
# editorconfig.org
root = true
[*]
[*.{js,cjs,mjs,jsx,ts,cts,mts,tsx,json,jsonc}]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
max_line_length = 120
[*.md]
trim_trailing_whitespace = false
-2
View File
@@ -23,8 +23,6 @@ targets:
path: ./packages/appium
- type: npm
path: ./packages/base-driver
- type: npm
path: ./packages/eslint-config-appium-ts
- type: npm
path: ./packages/fake-driver
- type: npm
+8 -9
View File
@@ -3,7 +3,6 @@ description: Create a new bug report.
title: '🐞 bug: <title>'
labels: [Bug, Needs Triage]
body:
- type: markdown
attributes:
value: ':stop_sign: _Make sure you are reporting an issue. For general questions, please use [http://discuss.appium.io](http://discuss.appium.io)._'
@@ -13,9 +12,9 @@ body:
label: Is there an existing issue for this?
description: 'Please [search :mag: the issues](https://github.com/appium/appium/issues) to check if this bug has already been reported.'
options:
- label: I have searched the existing issues
required: true
- label: I have searched the existing issues
required: true
- type: input
attributes:
label: Appium Version
@@ -49,7 +48,7 @@ body:
description: |
You can retrieve the latest server/driver/plugin version number by running `npm info <package-name> version`. If you are not up to date, please try to reproduce your issue after updating all affected components.
options:
- label: I confirm the issue is still reproducible with the latest component versions
- label: I confirm the issue is still reproducible with the latest component versions
- type: textarea
attributes:
@@ -82,10 +81,10 @@ body:
:warning: _Remember to redact or remove any sensitive information!_
value: |
- Debug details (`appium --show-debug-info`):
- Last component version(s) which did _not_ exhibit the problem:
- Platform and version of device under test:
- Real device or emulator/simulator:
- Debug details (`appium --show-debug-info`):
- Last component version(s) which did _not_ exhibit the problem:
- Platform and version of device under test:
- Real device or emulator/simulator:
- type: textarea
attributes:
+1 -1
View File
@@ -1,5 +1,5 @@
blank_issues_enabled: true
contact_links:
- name: ❓ Question
- name: ❓ Question
url: https://discuss.appium.io/
about: Please use the Appium forum for any questions.
+1 -2
View File
@@ -1,9 +1,8 @@
name: 💡 Feature Request
description: Suggest an idea for this project.
title: "💡 feat: <title>"
title: '💡 feat: <title>'
labels: [Enhancement]
body:
- type: textarea
attributes:
label: Is your feature request related to a problem?
+53 -4
View File
@@ -16,15 +16,21 @@ inputs:
required: false
default: 'true'
socket-firewall:
description: Whether to install dependencies through Socket Firewall
required: false
default: 'true'
runs:
using: composite
steps:
- name: Use Node.js ${{ inputs.node-version }}
uses: actions/setup-node@v6.4.0
uses: actions/setup-node@v7.1.0
with:
node-version: ${{ inputs.node-version }}
cache: npm
- name: SocketDev Security Check
if: ${{ inputs.socket-firewall == 'true' }}
uses: SocketDev/action@v1.3.2
with:
mode: firewall-free
@@ -33,9 +39,52 @@ runs:
run: |
# Replace npm with sfw npm in the install command
install_cmd="${{ inputs.install-command }}"
install_cmd="${install_cmd//npm /sfw npm }"
$install_cmd
if [ "${{ inputs.socket-firewall }}" != "true" ]; then
$install_cmd
exit $?
fi
sfw_install_cmd="${install_cmd//npm /sfw npm }"
# Socket Firewall can fail to reach its own backend (a transient
# network error unrelated to the actual npm install) and, in that
# case, either exit non-zero or exit 0 without having actually
# completed the install, leaving node_modules incomplete. We only
# want to fall back to a plain (non-firewalled) install when we can
# positively attribute the failure to *that* Socket Firewall error —
# a real npm/package problem (bad version, checksum mismatch, failing
# postinstall script, etc.) must fail the job as-is, not be silently
# retried and potentially masked.
sfw_log="$(mktemp)"
set +e
$sfw_install_cmd 2>&1 | tee "$sfw_log"
sfw_exit="${PIPESTATUS[0]}"
set -e
sfw_infra_failure=0
if grep -qi 'Socket Firewall encountered an unexpected error' "$sfw_log"; then
sfw_infra_failure=1
fi
install_incomplete=0
if [ "$sfw_exit" -eq 0 ]; then
npm ls --depth=0 >/dev/null 2>&1 || install_incomplete=1
fi
rm -f "$sfw_log"
if [ "$sfw_infra_failure" -eq 1 ] && { [ "$sfw_exit" -ne 0 ] || [ "$install_incomplete" -eq 1 ]; }; then
echo "::warning::Socket Firewall failed to reach its backend; retrying with a plain npm install"
$install_cmd
elif [ "$sfw_exit" -ne 0 ]; then
echo "::error::npm install failed"
exit "$sfw_exit"
elif [ "$install_incomplete" -eq 1 ]; then
echo "::error::npm install appears incomplete and is not attributable to a Socket Firewall failure"
exit 1
fi
- name: Build packages
if: ${{ inputs.run-build == 'true' }}
# `npm ci` already builds through the root `prepare` script
if: ${{ inputs.run-build == 'true' && !startsWith(inputs.install-command, 'npm ci') }}
shell: bash
run: npm run build
+12 -2
View File
@@ -43,6 +43,11 @@ labels:
matcher:
files: ['packages/appium/**']
- label: '@appium/http-server'
sync: true
matcher:
files: ['packages/http-server/**']
- label: '@appium/base-driver'
sync: true
matcher:
@@ -68,10 +73,10 @@ labels:
matcher:
files: ['packages/driver-test-support/**']
- label: '@appium/eslint-config-appium-ts'
- label: '@appium/oxc-config'
sync: true
matcher:
files: ['packages/eslint-config-appium-ts/**']
files: ['packages/oxc-config/**']
- label: '@appium/execute-driver-plugin'
sync: true
@@ -118,6 +123,11 @@ labels:
matcher:
files: ['packages/schema/**']
- label: '@appium/semantic-release-config'
sync: true
matcher:
files: ['packages/semantic-release-config/**']
- label: '@appium/support'
sync: true
matcher:
+60 -42
View File
@@ -3,42 +3,46 @@ on:
push:
branches:
- master
- appium4
# this list of paths could potentially impact the build
paths:
- '**/tsconfig*.json'
- 'packages/base-driver/test-fixtures/**/*'
- 'packages/*/test/**'
- 'packages/*/bin'
- 'packages/**/*.js'
- 'packages/**/*.mjs'
- 'packages/**/*.cjs'
- 'packages/**/*.ts'
- 'packages/**/*.json'
- 'packages/**/*.js'
- 'package*.json'
- 'lerna.json'
- '.npmrc'
- '.mocharc.js'
- '.github/workflows/build.yml' # this file
- '.eslintrc'
- '.eslintignore'
- 'oxlint.config.mjs'
- 'oxfmt.config.mjs'
- '!**/sample-code/**'
- '!packages/*/docs/**'
pull_request:
branches:
- master
- appium4
paths:
- '**/tsconfig*.json'
- 'packages/base-driver/test-fixtures/**/*'
- 'packages/*/test/**'
- 'packages/*/bin'
- 'packages/**/*.js'
- 'packages/**/*.mjs'
- 'packages/**/*.cjs'
- 'packages/**/*.ts'
- 'packages/**/*.json'
- 'packages/**/*.js'
- 'package*.json'
- 'lerna.json'
- '.npmrc'
- '.mocharc.js'
- '.github/workflows/build.yml' # this file
- '.eslintrc'
- '.eslintignore'
- 'oxlint.config.mjs'
- 'oxfmt.config.mjs'
- '!**/sample-code/**'
- '!packages/*/docs/**'
@@ -57,15 +61,19 @@ jobs:
uses: appium/appium-workflows/.github/workflows/node-lts-matrix.yml@main
prepare_e2e_packages:
needs:
- prepare_matrix
runs-on: ubuntu-latest
outputs:
packages: ${{ steps.discover-packages.outputs.packages }}
matrix: ${{ steps.discover-packages.outputs.matrix }}
steps:
- uses: actions/checkout@v6.0.3
- uses: actions/checkout@v7.0.1
- name: Discover packages with e2e tests
id: discover-packages
env:
NODE_VERSIONS: ${{ needs.prepare_matrix.outputs.versions }}
run: |
packages=()
targets='[]'
for package_dir in packages/*/; do
package_json="${package_dir}package.json"
if [[ -f "$package_json" ]]; then
@@ -85,32 +93,37 @@ jobs:
# Use the directory path without packages/ prefix (remove trailing slash)
package_name="${package_dir%/}"
package_name="${package_name#packages/}"
packages+=("$package_name")
# Packages with a test:e2e:shards script print a JSON array of shard names
shards='[""]'
if node -e "process.exit(require('./$package_json').scripts?.['test:e2e:shards'] ? 0 : 1)"; then
shards=$(cd "$package_dir" && npm run --silent test:e2e:shards)
fi
targets=$(jq -c --arg pkg "$package_name" --argjson shards "$shards" \
'. + [$shards[] | {package_dir: $pkg, shard: ., name: (if . == "" then $pkg else "\($pkg) [\(.)]" end)}]' \
<<< "$targets")
fi
fi
done
# Convert array to JSON array for GitHub Actions matrix
if [[ ${#packages[@]} -eq 0 ]]; then
echo "No packages with e2e tests found"
packages_json="[]"
else
# Use jq to create a compact single-line JSON array
packages_json=$(printf '%s\n' "${packages[@]}" | jq -R '.' | jq -s '.' | jq -c '.')
fi
echo "packages=$packages_json" >> $GITHUB_OUTPUT
echo "Found packages with e2e tests:"
echo "$packages_json" | jq -r '.[]' | sed 's/^/ - /'
echo "Found e2e targets:"
echo "$targets" | jq -r '.[].name' | sed 's/^/ - /'
# All LTS versions of Node.js on Linux, the newest one on Windows
matrix=$(jq -c --argjson versions "$NODE_VERSIONS" '
[.[] as $target
| ($versions[] | {os: "ubuntu-latest", "node-version": ., target: $target}),
{os: "windows-latest", "node-version": ($versions | map(tostring | tonumber) | max), target: $target}]
' <<< "$targets")
echo "matrix=$matrix" >> $GITHUB_OUTPUT
test-smoke:
needs:
- prepare_matrix
- prepare_matrix
name: Smoke Tests
strategy:
matrix:
node-version: ${{ fromJSON(needs.prepare_matrix.outputs.versions) }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6.0.3
- uses: actions/checkout@v7.0.1
- name: Setup, install and build
uses: ./.github/actions/setup-build
with:
@@ -122,14 +135,14 @@ jobs:
test-unit:
needs:
- prepare_matrix
- prepare_matrix
name: Unit Tests
strategy:
matrix:
node-version: ${{ fromJSON(needs.prepare_matrix.outputs.versions) }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6.0.3
- uses: actions/checkout@v7.0.1
- name: Setup, install and build
uses: ./.github/actions/setup-build
with:
@@ -141,14 +154,14 @@ jobs:
test-types:
needs:
- prepare_matrix
- prepare_matrix
name: Type Tests
strategy:
matrix:
node-version: ${{ fromJSON(needs.prepare_matrix.outputs.versions) }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6.0.3
- uses: actions/checkout@v7.0.1
- name: Setup, install and build
uses: ./.github/actions/setup-build
with:
@@ -160,42 +173,47 @@ jobs:
test-e2e:
needs:
- prepare_matrix
- prepare_e2e_packages
name: E2E Tests - ${{ matrix.package_dir }} (Node ${{ matrix.node-version }})
- prepare_e2e_packages
name: E2E Tests - ${{ matrix.target.name }} (Node ${{ matrix.node-version }}, ${{ matrix.os }})
if: ${{ github.event_name == 'pull_request' }}
strategy:
fail-fast: false
matrix:
node-version: ${{ fromJSON(needs.prepare_matrix.outputs.versions) }}
package_dir: ${{ fromJSON(needs.prepare_e2e_packages.outputs.packages) }}
runs-on: ubuntu-latest
include: ${{ fromJSON(needs.prepare_e2e_packages.outputs.matrix) }}
runs-on: ${{ matrix.os }}
timeout-minutes: 30
defaults:
run:
shell: bash
steps:
- uses: actions/checkout@v6.0.3
- uses: actions/checkout@v7.0.1
- name: Setup, install and build
uses: ./.github/actions/setup-build
with:
node-version: ${{ matrix.node-version }}
install-command: npm ci --foreground-scripts
run-build: 'true'
- name: Run E2E tests for ${{ matrix.package_dir }}
socket-firewall: 'false'
- name: Run E2E tests for ${{ matrix.target.name }}
run: |
set -o pipefail
cd "packages/${{ matrix.package_dir }}"
npm run test:e2e
cd "packages/${{ matrix.target.package_dir }}"
npm run test:e2e -- ${{ matrix.target.shard }}
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6.0.3
- uses: actions/checkout@v7.0.1
- name: Setup and install (no build)
uses: ./.github/actions/setup-build
with:
node-version: lts/*
install-command: npm ci
run-build: 'false'
- name: ESLint
- name: Lint
run: npm run lint:ci
- name: Prettier
- name: Format check
run: npm run format:check
- name: Verify changelog generation
run: npm run verify:changelog-generation
+1 -1
View File
@@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.1
- name: Cleanup
run: |
+3 -3
View File
@@ -21,9 +21,9 @@ jobs:
project_name: appium-inspector
steps:
- uses: actions/checkout@v6.0.3
- uses: actions/checkout@v7.0.1
- name: Use Node.js LTS
uses: actions/setup-node@v6.4.0
uses: actions/setup-node@v7.1.0
with:
node-version: lts/*
cache: 'npm'
@@ -36,7 +36,7 @@ jobs:
CROWDIN_PROJECT_ID: ${{ vars[matrix.project_id_var] }}
CROWDIN_TOKEN: ${{ secrets.CROWDIN_DOCS_TOKEN }}
- name: Post to Slack for ${{ matrix.project_name }}
uses: slackapi/slack-github-action@v3.0.3
uses: slackapi/slack-github-action@v4.0.0
with:
errors: true
webhook: ${{ secrets.SLACK_WEBHOOK_URL }}
@@ -12,11 +12,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6.0.3
- uses: actions/checkout@v7.0.1
with:
persist-credentials: false
- name: Use Node.js LTS
uses: actions/setup-node@v6.4.0
uses: actions/setup-node@v7.1.0
with:
node-version: lts/*
cache: 'npm'
@@ -15,9 +15,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6.0.3
- uses: actions/checkout@v7.0.1
- name: Use Node.js LTS
uses: actions/setup-node@v6.4.0
uses: actions/setup-node@v7.1.0
with:
node-version: lts/*
cache: 'npm'
+3 -9
View File
@@ -10,17 +10,11 @@ permissions:
jobs:
prepare_matrix:
runs-on: ubuntu-latest
outputs:
versions: ${{ steps.generate-matrix.outputs.lts }}
steps:
- name: Select all current LTS versions of Node.js
id: generate-matrix
uses: msimerson/node-lts-versions@v2.0.2
uses: appium/appium-workflows/.github/workflows/node-lts-matrix.yml@main
check:
needs:
- prepare_matrix
- prepare_matrix
name: Check
runs-on: ${{ matrix.os }}
strategy:
@@ -29,7 +23,7 @@ jobs:
os: [ubuntu-latest, windows-latest, macos-latest]
steps:
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v6.4.0
uses: actions/setup-node@v7.1.0
with:
node-version: ${{ matrix.node-version }}
- name: Install Appium
+1 -1
View File
@@ -37,7 +37,7 @@ jobs:
name: Docs
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6.0.3
- uses: actions/checkout@v7.0.1
- name: Install dependencies (Node.js)
uses: bahmutov/npm-install@v1.12.1
with:
+1 -1
View File
@@ -13,7 +13,7 @@ jobs:
env:
FOSSA_API_KEY: ${{secrets.FOSSA_API_KEY}}
steps:
- uses: actions/checkout@v6.0.3
- uses: actions/checkout@v7.0.1
with:
ref: master
- name: Install dependencies
+3 -3
View File
@@ -14,9 +14,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6.0.3
- uses: actions/checkout@v7.0.1
- name: Use Node.js LTS
uses: actions/setup-node@v6.4.0
uses: actions/setup-node@v7.1.0
with:
node-version: lts/*
cache: 'npm'
@@ -29,7 +29,7 @@ jobs:
# GITHUB_TOKEN is optional - if not provided, uses unauthenticated requests (rate limited)
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Post to Slack
uses: slackapi/slack-github-action@v3.0.3
uses: slackapi/slack-github-action@v4.0.0
with:
errors: true
webhook: ${{ secrets.SLACK_WEBHOOK_URL }}
+143 -49
View File
@@ -8,6 +8,16 @@ on:
required: true
type: boolean
default: true
recover_missing_only:
description: 'Recovery mode: skip version bump, publish only packages missing on npm at their current package.json version'
required: true
type: boolean
default: false
allow_legacy_peer_deps:
description: 'Beta branch only: pass --legacy-peer-deps to lerna''s internal npm calls, for when a dependent''s peerDependencies range still lags a just-bumped major (causes ERESOLVE otherwise). Leave off unless you hit that failure.'
required: true
type: boolean
default: false
permissions:
contents: write
@@ -18,11 +28,15 @@ permissions:
jobs:
release:
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/master' && contains(fromJSON('["mykola-mokhnach", "jlipps", "eglitise", "KazuCocoa"]'), github.actor)
# keep this branch name in sync with env.BETA_BRANCH below - the `env` context isn't available
# to jobs.<job_id>.if (GitHub Actions resolves it before job-level env is set up)
if: (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/appium4') && contains(fromJSON('["mykola-mokhnach", "jlipps", "eglitise", "KazuCocoa"]'), github.actor)
env:
BETA_BRANCH: appium4
steps:
- name: Checkout repository
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.1
with:
fetch-depth: 0
@@ -43,15 +57,137 @@ jobs:
# for lerna createRelease
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
DRY_RUN: ${{ inputs.dry_run }}
RECOVER_MISSING_ONLY: ${{ inputs.recover_missing_only }}
ALLOW_LEGACY_PEER_DEPS: ${{ inputs.allow_legacy_peer_deps }}
run: |
set -euo pipefail
# On the beta branch ($BETA_BRANCH), every release is a beta prerelease anchored at its
# target major (see packages/semantic-release-config's "Next-major beta branches" docs
# for the equivalent semantic-release behavior this mirrors).
#
# allow_legacy_peer_deps: independent next-major beta.0 bumps can briefly outrun a
# dependent's committed peerDependencies range, aborting lerna's internal lockfile
# refresh with ERESOLVE. Opt-in only - it masks a real (if temporary) peer mismatch.
LEGACY_PEER_DEPS_FLAGS=""
if [ "$ALLOW_LEGACY_PEER_DEPS" = "true" ]; then
LEGACY_PEER_DEPS_FLAGS="--npm-client-args=--legacy-peer-deps"
fi
BETA_FLAGS=""
DIST_TAG_FLAGS=""
NPM_TAG_FLAGS=""
if [ "${{ github.ref_name }}" = "$BETA_BRANCH" ]; then
BETA_FLAGS="--conventional-prerelease --preid beta $LEGACY_PEER_DEPS_FLAGS"
DIST_TAG_FLAGS="--dist-tag beta"
# `npm publish` takes --tag; --dist-tag is lerna-only
NPM_TAG_FLAGS="--tag beta"
fi
# Prints "<pkg_name>@<pkg_version>" to stdout for each non-private workspace package
# whose current package.json version is not yet on npm.
find_missing_packages() {
for pkg_json in packages/*/package.json; do
if [ ! -f "$pkg_json" ]; then
continue
fi
if [ "$(jq -r '.private // false' "$pkg_json")" = "true" ]; then
continue
fi
pkg_name="$(jq -r '.name // empty' "$pkg_json")"
pkg_version="$(jq -r '.version // empty' "$pkg_json")"
if [ -z "$pkg_name" ] || [ -z "$pkg_version" ]; then
echo "Skipping malformed package manifest: $pkg_json" >&2
continue
fi
pkg_spec="${pkg_name}@${pkg_version}"
if ! npm view "$pkg_spec" version >/dev/null 2>&1; then
echo "$pkg_spec"
fi
done
}
# Resolves the workspace directory of a package given its npm name.
package_dir_for() {
local target="$1"
for pkg_json in packages/*/package.json; do
if [ ! -f "$pkg_json" ]; then
continue
fi
if [ "$(jq -r '.name // empty' "$pkg_json")" = "$target" ]; then
dirname "$pkg_json"
return 0
fi
done
return 1
}
# Publishes each "<pkg_name>@<pkg_version>" package spec passed as an argument.
# `lerna publish` has no per-package scope filter, so this runs `npm publish`
# directly from each package's directory instead. Returns non-zero if any
# individual publish failed; npm publish's own exit code is taken as authoritative,
# no npm registry re-scan is done to double-check it.
publish_missing_packages() {
echo "Publishing missing packages one-by-one"
local overall_status=0
for pkg_spec in "$@"; do
pkg_name="${pkg_spec%@*}"
pkg_dir="$(package_dir_for "$pkg_name")"
if [ -z "$pkg_dir" ]; then
echo "Could not resolve workspace directory for ${pkg_name}"
overall_status=1
continue
fi
echo "Publishing ${pkg_spec}"
set +e
(cd "$pkg_dir" && npm publish $NPM_TAG_FLAGS)
publish_exit_code=$?
set -e
if [ $publish_exit_code -ne 0 ]; then
echo "npm publish failed for ${pkg_spec} (exit code ${publish_exit_code})"
overall_status=1
fi
done
return $overall_status
}
if [ "$RECOVER_MISSING_ONLY" = "true" ]; then
echo "Recovery mode: scanning for packages missing on npm at their current package.json version"
mapfile -t missing < <(find_missing_packages)
if [ ${#missing[@]} -eq 0 ]; then
echo "No missing packages found; nothing to do"
exit 0
fi
echo "Missing package versions on npm:"
printf ' - %s\n' "${missing[@]}"
if [ "$DRY_RUN" = "true" ]; then
echo "Dry run: not publishing"
exit 0
fi
if ! publish_missing_packages "${missing[@]}"; then
echo "Recovery incomplete: one or more packages failed to publish"
exit 1
fi
echo "All missing packages published successfully"
exit 0
fi
if [ "$DRY_RUN" = "true" ]; then
echo "Running lerna publish in dry-run mode"
printf "n\n" | npx lerna version --exact --no-git-tag-version --no-push
printf "n\n" | npx lerna version --no-git-tag-version --no-push $BETA_FLAGS
else
echo "Running lerna publish for real"
set +e
release_output="$(npm run release -- --yes 2>&1)"
release_output="$(npm run release -- --yes $BETA_FLAGS $DIST_TAG_FLAGS 2>&1)"
release_exit_code=$?
set -e
printf '%s\n' "$release_output"
@@ -76,55 +212,13 @@ jobs:
fi
git checkout "$tagged_commit"
missing=()
for pkg_json in packages/*/package.json; do
if [ ! -f "$pkg_json" ]; then
continue
fi
if [ "$(jq -r '.private // false' "$pkg_json")" = "true" ]; then
continue
fi
pkg_name="$(jq -r '.name // empty' "$pkg_json")"
pkg_version="$(jq -r '.version // empty' "$pkg_json")"
if [ -z "$pkg_name" ] || [ -z "$pkg_version" ]; then
echo "Skipping malformed package manifest: $pkg_json"
continue
fi
pkg_spec="${pkg_name}@${pkg_version}"
if ! npm view "$pkg_spec" version >/dev/null 2>&1; then
missing+=("$pkg_spec")
fi
done
mapfile -t missing < <(find_missing_packages)
if [ ${#missing[@]} -gt 0 ]; then
echo "Missing package versions on npm before scoped recovery:"
printf ' - %s\n' "${missing[@]}"
echo "Publishing missing packages one-by-one with scope"
for pkg_spec in "${missing[@]}"; do
pkg_name="${pkg_spec%@*}"
echo "Publishing ${pkg_name}"
set +e
npx lerna publish from-package --yes --scope "$pkg_name"
scoped_exit_code=$?
set -e
if [ $scoped_exit_code -ne 0 ]; then
echo "Scoped publish exited non-zero for ${pkg_name}; will verify on npm"
fi
done
final_missing=()
for pkg_spec in "${missing[@]}"; do
if ! npm view "$pkg_spec" version >/dev/null 2>&1; then
final_missing+=("$pkg_spec")
fi
done
if [ ${#final_missing[@]} -gt 0 ]; then
echo "Recovery incomplete after scoped retries. Still missing:"
printf ' - %s\n' "${final_missing[@]}"
if ! publish_missing_packages "${missing[@]}"; then
echo "Recovery incomplete: one or more packages failed to publish"
exit $release_exit_code
fi
fi
+12 -3
View File
@@ -15,11 +15,15 @@ permissions:
jobs:
version:
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/master' && contains(fromJSON('["mykola-mokhnach", "jlipps", "eglitise", "KazuCocoa"]'), github.actor)
# keep this branch name in sync with env.BETA_BRANCH below - the `env` context isn't available
# to jobs.<job_id>.if (GitHub Actions resolves it before job-level env is set up)
if: (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/appium4') && contains(fromJSON('["mykola-mokhnach", "jlipps", "eglitise", "KazuCocoa"]'), github.actor)
env:
BETA_BRANCH: appium4
steps:
- name: Checkout repository
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.1
with:
fetch-depth: 0
@@ -38,4 +42,9 @@ jobs:
- name: Run Lerna version
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: npx lerna version --exact --yes
run: |
BETA_FLAGS=""
if [ "${{ github.ref_name }}" = "$BETA_BRANCH" ]; then
BETA_FLAGS="--conventional-prerelease --preid beta"
fi
npx lerna version --yes $BETA_FLAGS
+10 -10
View File
@@ -2,17 +2,21 @@ name: Publish docs
on:
workflow_dispatch:
workflow_run:
workflows: ["Appium Server Release"]
types:
- completed
push:
# Runs when any new Appium tag is pushed = there is a new release
# Does not trigger for tags with a slash, e.g. 1.0/beta
tags:
- appium@*
- '!appium@*beta*'
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6.0.3
- uses: actions/checkout@v7.0.1
with:
fetch-depth: 0 # ensure the docs branch is also fetched
- run: |
git config user.name github-actions
git config user.email github-actions@github.com
@@ -24,9 +28,5 @@ jobs:
run-build: 'true'
- name: Install dependencies (Python)
run: npm run install-docs-deps
- name: Fetch gh-pages
run: |
git fetch origin gh-pages:gh-pages || true
- name: Publish docs
run: |
npm run docs:publish
run: npm run docs:publish
+2 -4
View File
@@ -6,14 +6,12 @@ on:
paths:
- '.github/workflows/renovate.yml' # this file
- '.renovaterc.json'
- 'renovate/default.json'
pull_request:
branches:
- master
paths:
- '.github/workflows/renovate.yml' # this file
- '.renovaterc.json'
- 'renovate/default.json'
permissions:
contents: read
@@ -23,8 +21,8 @@ jobs:
name: Check Renovate Configs
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6.0.3
- uses: actions/checkout@v7.0.1
- name: Validate Renovate Config
uses: rinchsan/renovate-config-validator@v0.2.1
with:
pattern: '{.renovaterc.json,renovate/default.json}'
pattern: '.renovaterc.json'
+9
View File
@@ -0,0 +1,9 @@
{
"mcpServers": {
"codegraph": {
"type": "stdio",
"command": "codegraph",
"args": ["serve", "--mcp"]
}
}
}
-11
View File
@@ -1,11 +0,0 @@
// @ts-check
'use strict';
module.exports = {
require: ['ts-node/register'],
// forbids use of .only() in CI
forbidOnly: Boolean(process.env.CI),
color: true,
timeout: '5s',
};
-1
View File
@@ -1,4 +1,3 @@
# Do not disable package-lock. Disabling package-lock also disables npm-shrinkwrap
package-lock=true
save-exact=true
foreground-scripts=true
-16
View File
@@ -1,16 +0,0 @@
**/node_modules/**
**/build/**
**/fixtures/**
**/*.min.*
**/*.md
package-lock.json
package.json
**/.vscode/**
**/*.html
**/generated/**
# generated
packages/schema/lib/appium-config.schema.json
packages/types/lib/appium-config.ts
**/*.hbs
/.nx/workspace-data
+2 -5
View File
@@ -1,9 +1,6 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": ["github>appium/appium//renovate/default"],
"schedule": [
"after 10pm",
"before 5:00am"
],
"extends": ["github>appium/appium-workflows//renovate/default"],
"schedule": ["after 10pm", "before 5:00am"],
"timezone": "America/Vancouver"
}
+4
View File
@@ -0,0 +1,4 @@
# Repository guidance
Before changing code, read and follow the
[contributor style guide](packages/appium/docs/en/contributing/index.md#code-style).
+2
View File
@@ -29,8 +29,10 @@ to driver/client documentation
* [fake-plugin](packages/fake-plugin/CHANGELOG.md)
* [logger](packages/logger/CHANGELOG.md)
* [opencv](packages/opencv/CHANGELOG.md)
* [oxc-config](packages/oxc-config/CHANGELOG.md)
* [plugin-test-support](packages/plugin-test-support/CHANGELOG.md)
* [schema](packages/schema/CHANGELOG.md)
* [semantic-release-config](packages/semantic-release-config/CHANGELOG.md)
* [strongbox](packages/strongbox/CHANGELOG.md)
* [support](packages/support/CHANGELOG.md)
* [tsconfig](packages/tsconfig/CHANGELOG.md)
-41
View File
@@ -1,41 +0,0 @@
import appiumConfig from '@appium/eslint-config-appium-ts';
import {defineConfig, globalIgnores} from 'eslint/config';
// Create a modified config subset for test support files:
// Extract the test file related configs (Mocha plugin and custom rules),
// then override their 'files' property (match by name — resilient to new config entries).
const testFileConfigItems = appiumConfig.filter(
(item) => typeof item?.name === 'string' && item.name.startsWith('Test Files'),
);
const testSupportFiles = [
'packages/driver-test-support/lib/**',
'packages/plugin-test-support/lib/**',
];
const testSupportConfig = testFileConfigItems.map((item) => ({...item, files: testSupportFiles}));
export default defineConfig([
{
name: 'Base Config',
extends: [appiumConfig],
},
{
name: 'Test Support',
extends: [testSupportConfig],
},
{
name: 'Consistent type-only imports (all packages)',
files: ['**/*.{ts,tsx,mtsx}'],
rules: {
'@typescript-eslint/consistent-type-imports': [
'error',
{ fixStyle: 'separate-type-imports', prefer: 'type-imports' },
],
},
},
globalIgnores([
'**/build-fixtures/**',
'packages/appium/docs/**/assets/**',
'packages/appium/docs/**/js/**',
'packages/appium/sample-code/**',
]),
]);
+1 -5
View File
@@ -2,9 +2,6 @@
"$schema": "https://json.schemastore.org/lerna",
"changelogPreset": "conventional-changelog-conventionalcommits",
"command": {
"add": {
"exact": true
},
"publish": {
"message": "chore: publish"
},
@@ -14,8 +11,7 @@
"version": {
"conventionalCommits": true,
"createRelease": "github",
"allowBranch": ["master", "appium3"],
"exact": true
"allowBranch": ["master", "appium4"]
}
},
"ignoreChanges": ["**/test/**", "**/*.md"],
+11
View File
@@ -0,0 +1,11 @@
import appiumConfig, {defineConfig, ignorePatterns as appiumIgnorePatterns} from '@appium/oxc-config/oxfmt';
export default defineConfig({
...appiumConfig,
ignorePatterns: [
...appiumIgnorePatterns,
'packages/appium/docs/**',
'packages/schema/lib/appium-config.schema.json',
'packages/types/lib/appium-config.ts',
],
});
+11
View File
@@ -0,0 +1,11 @@
import appiumConfig, {defineConfig, ignorePatterns as appiumIgnorePatterns} from '@appium/oxc-config/oxlint';
export default defineConfig({
extends: [appiumConfig],
ignorePatterns: [
...appiumIgnorePatterns,
'packages/appium/docs/**/assets/**',
'packages/appium/docs/**/js/**',
'packages/appium/sample-code/**',
],
});
+7956 -7970
View File
File diff suppressed because it is too large Load Diff
+39 -77
View File
@@ -4,28 +4,31 @@
"private": true,
"description": "Automation for Apps.",
"keywords": [
"android",
"automation",
"firefoxos",
"ios",
"javascript",
"selenium",
"webdriver",
"ios",
"android",
"firefoxos",
"testing"
"testing",
"webdriver"
],
"homepage": "https://appium.io",
"bugs": {
"url": "https://github.com/appium/appium/issues"
},
"license": "Apache-2.0",
"author": "https://github.com/appium",
"repository": {
"type": "git",
"url": "https://github.com/appium/appium.git"
},
"license": "Apache-2.0",
"author": "https://github.com/appium",
"workspaces": [
"packages/*"
],
"publishConfig": {
"access": "public"
},
"scripts": {
"build": "run-s build:*",
"build:compile": "tsc -b",
@@ -45,13 +48,12 @@
"docs": "npm run -w packages/appium build:docs",
"docs:preview": "npm run -w packages/appium build:docs:preview",
"docs:publish": "npm run -w packages/appium publish:docs",
"doctor": "node ./scripts/doctor-stub.mjs",
"install-docs-deps": "npm run -w packages/appium install-docs-deps",
"format": "prettier -w ./packages ./scripts",
"format:check": "prettier --check ./packages ./scripts",
"lint": "eslint .",
"lint:ci": "eslint --quiet .",
"lint:fix": "run-s \"lint -- --fix\"",
"format": "oxfmt -c oxfmt.config.mjs .",
"format:check": "oxfmt -c oxfmt.config.mjs --check .",
"lint": "oxlint -c oxlint.config.mjs .",
"lint:ci": "oxlint -c oxlint.config.mjs --quiet .",
"lint:fix": "oxlint -c oxlint.config.mjs --fix .",
"prepare": "run-p rebuild",
"prepublishOnly": "run-s rebuild",
"preversion": "run-s rebuild",
@@ -59,7 +61,7 @@
"rebuild": "run-s clean build",
"reinstall": "run-s clean clean:deps ci",
"reinstall:angry": "npm run clean:dangerously && npm ci",
"release": "lerna publish --exact",
"release": "lerna publish",
"stage-synced-pkgs": "git add -A packages/appium/README.md \"packages/*/package.json\"",
"start": "appium",
"sync-pkgs": "node ./scripts/sync-monorepo-packages.mjs",
@@ -68,90 +70,46 @@
"test:e2e": "lerna run test:e2e",
"test:quick": "run-s lint test:unit test:types",
"test:slow": "run-s test:quick test:smoke test:e2e",
"test:smoke": "smoker --all test:smoke",
"test:smoke": "lerna run test:smoke",
"test:types": "lerna run test:types",
"test:unit": "lerna run test",
"upload": "gulp github-upload",
"verify:changelog-generation": "node ./scripts/verify-changelog-generation.mjs",
"zip": "zip -qr ./appium.zip ./packages/appium",
"zip-and-upload": "run-s zip upload"
},
"prettier": {
"bracketSpacing": false,
"printWidth": 100,
"singleQuote": true
},
"devDependencies": {
"@eslint/js": "10.0.1",
"@stylistic/eslint-plugin": "5.10.0",
"@tsconfig/node20": "20.1.9",
"@types/argparse": "2.0.17",
"@types/async-lock": "1.4.2",
"@types/bluebird": "3.5.42",
"@types/chai": "5.2.3",
"@types/chai-as-promised": "8.0.2",
"@appium/oxc-config": "2.0.0-beta.0",
"@tsconfig/node22": "22.0.0",
"@types/express": "5.0.6",
"@types/jsftp": "2.1.5",
"@types/json-schema": "7.0.15",
"@types/klaw": "3.0.7",
"@types/lockfile": "1.0.4",
"@types/lodash": "4.17.24",
"@types/method-override": "3.0.0",
"@types/mocha": "10.0.10",
"@types/mime-types": "3.0.1",
"@types/morgan": "1.9.10",
"@types/mv": "2.1.4",
"@types/node": "24.13.2",
"@types/plist": "3.0.5",
"@types/node": "24.19.1",
"@types/picomatch": "4.0.3",
"@types/pluralize": "0.0.33",
"@types/semver": "7.7.1",
"@types/serve-favicon": "2.5.7",
"@types/semver": "7.8.0",
"@types/shell-quote": "1.7.5",
"@types/sinon": "21.0.1",
"@types/sinon-chai": "4.0.0",
"@types/sinon": "22.0.0",
"@types/which": "3.0.4",
"@types/ws": "8.18.1",
"@types/xmldom": "0.1.34",
"@types/yargs": "17.0.35",
"asyncbox": "6.3.0",
"chai": "6.2.2",
"chai-as-promised": "8.0.2",
"conventional-changelog-conventionalcommits": "7.0.2",
"asyncbox": "6.5.0",
"conventional-changelog-conventionalcommits": "9.3.1",
"cpy-cli": "7.0.0",
"cross-env": "10.1.0",
"eslint": "10.5.0",
"eslint-config-prettier": "10.1.8",
"eslint-import-resolver-typescript": "4.4.5",
"eslint-plugin-import-x": "4.16.2",
"eslint-plugin-mocha": "11.3.0",
"eslint-plugin-n": "18.1.0",
"eslint-plugin-promise": "7.3.0",
"finalhandler": "2.1.1",
"json-schema-to-typescript": "15.0.4",
"lerna": "9.0.7",
"midnight-smoker": "8.0.0",
"mjpeg-consumer": "2.0.0",
"mjpeg-server": "0.3.1",
"mocha": "11.7.6",
"npm-run-all2": "9.0.2",
"prettier": "3.8.4",
"rewiremock": "3.14.6",
"json-schema-to-typescript": "16.0.0",
"lerna": "10.0.1",
"npm-run-all2": "9.0.3",
"rimraf": "6.1.3",
"serve-static": "2.2.1",
"sinon": "22.0.0",
"sinon-chai": "4.0.1",
"teen_process": "4.1.3",
"ts-node": "10.9.2",
"sinon": "22.1.0",
"teen_process": "4.2.1",
"tsd": "0.33.0",
"typescript": "6.0.3",
"typescript-eslint": "8.61.0",
"webdriverio": "9.28.0",
"ws": "8.21.0"
},
"engines": {
"node": "^20.19.0 || ^22.12.0 || >=24.0.0",
"npm": ">=10"
},
"publishConfig": {
"access": "public"
"webdriverio": "9.32.0",
"ws": "8.22.0"
},
"overrides": {
"@sidvind/better-ajv-errors": {
@@ -160,6 +118,10 @@
"ajv-formats": {
"ajv": "8.20.0"
},
"http-cache-semantics": "4.2.0"
"http-cache-semantics": "4.3.0"
},
"engines": {
"node": "^22.22.2 || ^24.15.0 || >=26.0.0",
"npm": ">=10"
}
}
-10
View File
@@ -1,10 +0,0 @@
// @ts-check
'use strict';
module.exports = {
require: ['ts-node/register'],
forbidOnly: Boolean(process.env.CI),
color: true,
timeout: '5s',
};
+283
View File
@@ -3,6 +3,289 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [4.0.0-beta.6](https://github.com/appium/appium/compare/appium@4.0.0-beta.5...appium@4.0.0-beta.6) (2026-10-26)
### ⚠ BREAKING CHANGES
* **support:** ExtractAllOptions.useSystemUnzip is removed.
* **support:** Non-UTF-8-flagged file names now default to utf8 instead of cp437, and cp437 decoding is no longer supported (modern archives use UTF-8). Such archives must be recreated or converted; fileNamesEncoding only accepts Node.js BufferEncodings.
* **appium:** install local extensions without global npm links (#23086)
* **base-driver:** preserve proxied command validation ordering (#23060)
* **base-driver:** The protected BaseDriver#commandsQueueGuard beyond promise-based acquire(key, fn) - it is now an asyncbox AsyncLock, so callback-style acquire() and the internal queues field are gone (use getQueueLength() / isBusy()).
* **base-driver:** validate every timeout capability candidate (#23062)
* **appium:** removed the --callback-address (-ca) server argument and the server.callback-address config property
* **appium:** removed the --callback-port (-cp) server argument and the server.callback-port config property
* **appium:** removed the --debug-log-spacing server argument and the server.debug-log-spacing config property
* **appium:** removed the --strict-caps server argument and the server.strict-caps config property
* **appium:** removed the --trace-dir server argument and the server.trace-dir config property
* **base-driver:** POST body has to be a JSON object, otherwise the server returns invalid argument error.
### Features
* **support:** Remove system unzip from extractAllTo ([#23091](https://github.com/appium/appium/issues/23091)) ([0f1a761](https://github.com/appium/appium/commit/0f1a76184b78ee3d60652b9a610c808213196f7c))
### Bug Fixes
* **appium:** install local extensions without global npm links ([#23086](https://github.com/appium/appium/issues/23086)) ([3615d6d](https://github.com/appium/appium/commit/3615d6df4c5a785524092f8c28dc338385a4468a))
* **appium:** preserve npm arguments on Windows ([#22957](https://github.com/appium/appium/issues/22957)) ([dd71f66](https://github.com/appium/appium/commit/dd71f66e4ac343bc48e78f5b661ca02b351553c1))
* **appium:** remove unused server arguments ([#23063](https://github.com/appium/appium/issues/23063)) ([16c84ea](https://github.com/appium/appium/commit/16c84ea20314f605b93070de283818a78c0ed4bb))
* **appium:** send log webhooks to ipv6 hosts ([#23061](https://github.com/appium/appium/issues/23061)) ([819905b](https://github.com/appium/appium/commit/819905beb265431446ec56776373ec15f13a0a8f))
* **base-driver:** preserve proxied command validation ordering ([#23060](https://github.com/appium/appium/issues/23060)) ([6cc4095](https://github.com/appium/appium/commit/6cc4095c59bc7ce23b47ce768707aae978794824))
* **base-driver:** reject command POST bodies that are not JSON objects ([#23007](https://github.com/appium/appium/issues/23007)) ([39e9512](https://github.com/appium/appium/commit/39e9512b4a4c936edae0d7a56bdd4bd613075322))
* **base-driver:** use AsyncLock from asyncbox instead of async-lock ([#23066](https://github.com/appium/appium/issues/23066)) ([17421ae](https://github.com/appium/appium/commit/17421ae61fa9f9e74a52b116f3ed2ee3c09fbc1f))
* **base-driver:** validate every timeout capability candidate ([#23062](https://github.com/appium/appium/issues/23062)) ([531f482](https://github.com/appium/appium/commit/531f482d8759e7c621276b212489b764a994766a))
* **docs:** correct stale instructions in .NET quickstart ([#23049](https://github.com/appium/appium/issues/23049)) ([2dd05e1](https://github.com/appium/appium/commit/2dd05e12d3e5911203856977120813ce56b69552))
* **docs:** X-Appium-Is-Sensitive also accepts the value &#x27;yes&#x27; ([#23051](https://github.com/appium/appium/issues/23051)) ([2cf7f4e](https://github.com/appium/appium/commit/2cf7f4e6b21fdf36f85986187d5361d980f7cb42))
* **support:** prevent zip extraction through symlink chains ([#23002](https://github.com/appium/appium/issues/23002)) ([8e42cbc](https://github.com/appium/appium/commit/8e42cbc82d95da690b73399782f750043464352d))
## [4.0.0-beta.5](https://github.com/appium/appium/compare/appium@4.0.0-beta.4...appium@4.0.0-beta.5) (2026-10-18)
### ⚠ BREAKING CHANGES
* **base-driver:** match standard capability names case-sensitively (#22976)
* **appium:** Drivers must use base-driver with shared command activity tracking. Custom timeout implementations must delegate timer creation to super.startNewCommandTimeout(); legacy private-counter implementations are no longer protected by method overrides.
* add fake driver/plugin example
* remove from readme
* fix(appium): release one proxied BiDi command per response
* test(appium): cover overlapping duplicate BiDi command IDs
* **appium:** Drivers must use base-driver with shared command activity tracking. Custom timeout implementations must delegate timer creation to super.startNewCommandTimeout(); legacy private-counter implementations are no longer protected by method overrides.
* add fake driver/plugin example
* remove from readme
* Remove actions needed for driver updates in migration guide
Removed the section detailing actions needed for driver updates related to shared command activity tracking.
* refactor(base-driver): simplify shutdown promise handling
* Add actions needed for custom startNewCommandTimeout
Updated migration guide to include new requirements for custom command timeout implementations.
* **types:** the locale, language, autoWebview, orientation and udid capabilities are no longer part of the standard capability constraints. They are not generic enough and must be defined by the corresponding drivers.
* **types:** the deprecated appium:autoLaunch capability is no longer part of the standard capability constraints.
### Features
* **appium:** fallback to latest compatible extension version for CLI &#x60;install&#x60; ([#22951](https://github.com/appium/appium/issues/22951)) ([0b3fe1c](https://github.com/appium/appium/commit/0b3fe1ce35fd40eea8f91a3e8f9846ee03445276))
* **types:** remove deprecated autoLaunch capability ([#22950](https://github.com/appium/appium/issues/22950)) ([8822cea](https://github.com/appium/appium/commit/8822ceaabb68a213a8244e4f955d8ee68221e329))
* **types:** remove driver-specific capabilities from base constraints ([#22952](https://github.com/appium/appium/issues/22952)) ([cce0b58](https://github.com/appium/appium/commit/cce0b5814d94157caa6a25834a469277ad1a4dcc))
### Bug Fixes
* **appium:** apply default capabilities to every firstMatch candidate ([#22988](https://github.com/appium/appium/issues/22988)) ([f820e04](https://github.com/appium/appium/commit/f820e04000f138addab57917ef92d01071596f96))
* **appium:** classify driver selection failures as session not created ([#22970](https://github.com/appium/appium/issues/22970)) ([9b1ddb8](https://github.com/appium/appium/commit/9b1ddb8bf6cb51ce93114b70c536d0db18b39bfa))
* **appium:** suspend idle timeout during BiDi commands ([#22947](https://github.com/appium/appium/issues/22947)) ([3f82e6a](https://github.com/appium/appium/commit/3f82e6a1817de5acb6584b92c98bd156295060d3))
* **appium:** track idle timeout across plugin commands ([#22946](https://github.com/appium/appium/issues/22946)) ([c10c413](https://github.com/appium/appium/commit/c10c41384cdb4276faaab1a908cf9f80a77464a5))
* **base-driver:** match standard capability names case-sensitively ([#22976](https://github.com/appium/appium/issues/22976)) ([f3edfc5](https://github.com/appium/appium/commit/f3edfc5e03deb6821599614a35cf5ec5df03a3c3)), closes [#22966](https://github.com/appium/appium/issues/22966)
* **base-driver:** preserve capability invalid argument errors ([#22977](https://github.com/appium/appium/issues/22977)) ([b452a5e](https://github.com/appium/appium/commit/b452a5eade468d6c20a62c89357f72f492441038)), closes [#22966](https://github.com/appium/appium/issues/22966)
* **base-driver:** validate and merge all capability candidates ([#22982](https://github.com/appium/appium/issues/22982)) ([aba33a2](https://github.com/appium/appium/commit/aba33a2d17621e5db28e4505773c6603b075e7f2))
* **base-driver:** validate standard timeouts and preserve null values ([#22961](https://github.com/appium/appium/issues/22961)) ([eef8246](https://github.com/appium/appium/commit/eef8246f8a6983c7818c99bfd94d5ae7ec8bfa1e))
## [4.0.0-beta.4](https://github.com/appium/appium/compare/appium@4.0.0-beta.3...appium@4.0.0-beta.4) (2026-10-09)
### ⚠ BREAKING CHANGES
* **appium:** Replace the &#x60;windows&#x60; known-driver shortcut with &#x60;wincore&#x60; pointing to appium-wincore-driver instead of the obsolete appium-windows-driver
### Features
* **appium:** replace obsolete windows driver with appium-wincore-driver ([#22917](https://github.com/appium/appium/issues/22917)) ([7544a10](https://github.com/appium/appium/commit/7544a1008d96f44f19499500314b6ae21d54d4f2))
### Bug Fixes
* **appium:** prevent recursion when logging the first BiDi event of a method ([#22897](https://github.com/appium/appium/issues/22897)) ([e230727](https://github.com/appium/appium/commit/e230727090f24a3c777c88de9d43d83ff94ea97d))
* centralize URL hostname formatting and bracket IPv6 hosts ([#22914](https://github.com/appium/appium/issues/22914)) ([c5ddcef](https://github.com/appium/appium/commit/c5ddcef8940dfa5fac8d180bed65dc80ba8fafab))
* tune wincore driver integration ([#22922](https://github.com/appium/appium/issues/22922)) ([9f8455e](https://github.com/appium/appium/commit/9f8455e1973e97c8a6451a9291de741dadfed775))
## [4.0.0-beta.3](https://github.com/appium/appium/compare/appium@4.0.0-beta.2...appium@4.0.0-beta.3) (2026-10-05)
### Features
* **universal-xml-plugin:** add execute methods to toggle translation ([#22862](https://github.com/appium/appium/issues/22862)) ([b95aaac](https://github.com/appium/appium/commit/b95aaac60bed065ce0a0d1e4f7baf9c5e60572b1)), closes [#21630](https://github.com/appium/appium/issues/21630)
### Bug Fixes
* **appium:** apply initial settings from selected capabilities ([#22882](https://github.com/appium/appium/issues/22882)) ([95c0461](https://github.com/appium/appium/commit/95c04611e7d1cd8acfa4c6ce51276899938588d3))
* **appium:** bracket IPv6 hosts in BiDi websocket URLs ([#22872](https://github.com/appium/appium/issues/22872)) ([3875344](https://github.com/appium/appium/commit/3875344e111dc23e4c99310ad918b769dbb38927))
* **appium:** close bidi sockets when a session ends unexpectedly ([#22854](https://github.com/appium/appium/issues/22854)) ([3cdc69b](https://github.com/appium/appium/commit/3cdc69b198e85607b4f35a5b3f57b542b0ae9b80))
* **appium:** detect project homes with prerelease dependencies ([#22871](https://github.com/appium/appium/issues/22871)) ([11d9904](https://github.com/appium/appium/commit/11d9904166c9e692e6fa8491acfce4ac892c1a8e))
* **appium:** do not crash when a proxied bidi socket closes without a sendable code ([#22835](https://github.com/appium/appium/issues/22835)) ([66ab396](https://github.com/appium/appium/commit/66ab396a71c419f1fb456b8a1ddf23bc3491baf8))
* **appium:** explain manual recovery after manifest lock timeouts ([#22875](https://github.com/appium/appium/issues/22875)) ([b9c020c](https://github.com/appium/appium/commit/b9c020c6ebf88643ffe6b6d5e302b43673bbb028))
* **appium:** import extensions through escaped file URLs ([#22884](https://github.com/appium/appium/issues/22884)) ([8312252](https://github.com/appium/appium/commit/83122525db72f2d6ff06e27cd3da5269318be3a8))
* **appium:** match global and module BiDi event subscriptions ([#22883](https://github.com/appium/appium/issues/22883)) ([879873b](https://github.com/appium/appium/commit/879873bad5669c9b264f3fcb60d05562f6ca3b1e))
* **appium:** resolve default extension export conditions ([#22885](https://github.com/appium/appium/issues/22885)) ([3188c9f](https://github.com/appium/appium/commit/3188c9fbab17c16192a6e75ff62f69f4d844be8f))
* **appium:** roll back sessions when initialization fails ([#22873](https://github.com/appium/appium/issues/22873)) ([f24e704](https://github.com/appium/appium/commit/f24e7046eb2815822f655a644aee9786d90fd4e8))
* **appium:** start idle timeout after session initialization ([#22881](https://github.com/appium/appium/issues/22881)) ([e37f1c1](https://github.com/appium/appium/commit/e37f1c158394617bcaa474da25f1650b9b24ebd1))
* **base-driver:** track and remove BiDi subscriptions by ID ([#22876](https://github.com/appium/appium/issues/22876)) ([b791915](https://github.com/appium/appium/commit/b791915ffcf322e1cc84dbde606be55d34f020b3))
* use native ESM entrypoint detection ([#22868](https://github.com/appium/appium/issues/22868)) ([e391bb2](https://github.com/appium/appium/commit/e391bb249fec5f700a6d63a747ff921a80689fb5))
## [4.0.0-beta.2](https://github.com/appium/appium/compare/appium@4.0.0-beta.1...appium@4.0.0-beta.2) (2026-09-27)
### ⚠ BREAKING CHANGES
* **base-driver:** @appium/base-driver no longer exports server, normalizeBasePath, configureServer, ServerOpts, ConfigureServerOpts, ConfigureHttpOpts, or StartServerOpts, and no longer depends on express/morgan/body-parser/ method-override/path-to-regexp. Use the new @appium/http-server package instead.
* **base-driver:** appium/driver.js and driver.d.ts no longer re-export server/normalizeBasePath/configureServer/ServerOpts (or their types), and no longer use a blanket export *. Import from @appium/http-server directly if you were relying on this undocumented path.
### Features
* **appium:** accept package names for installed extension commands ([#22747](https://github.com/appium/appium/issues/22747)) ([#22812](https://github.com/appium/appium/issues/22812)) ([b7356ee](https://github.com/appium/appium/commit/b7356ee53eec3e322707eef0d2bd37976a47cfd2))
### Bug Fixes
* **appium:** release the manifest lock before errAndQuit exits ([#22821](https://github.com/appium/appium/issues/22821)) ([dc0568b](https://github.com/appium/appium/commit/dc0568bc658961f5d6a28fb955d8092d51cf9523))
* **base-driver:** carry proxy req via AsyncLocalStorage instead of args ([#22799](https://github.com/appium/appium/issues/22799)) ([2596da0](https://github.com/appium/appium/commit/2596da02d201d7e5fbfd24cf9dd2801302f8abc7))
* **support:** narrow fs.glob overload for default withFileTypes/lazy ([#22795](https://github.com/appium/appium/issues/22795)) ([09a04f4](https://github.com/appium/appium/commit/09a04f48831e17b9b7f3b8dc0a72f0df5096ae51))
### Code Refactoring
* **base-driver:** extract the HTTP/Express server into @appium/http-server ([#22800](https://github.com/appium/appium/issues/22800)) ([225a1c6](https://github.com/appium/appium/commit/225a1c6d9d24fa2696699d4b61436a49b847d666))
## [4.0.0-beta.1](https://github.com/appium/appium/compare/appium@4.0.0-beta.0...appium@4.0.0-beta.1) (2026-09-19)
### Bug Fixes
* **deps:** catch up peerDependencies to published beta.0; drop bootstrap_packages ([#22793](https://github.com/appium/appium/issues/22793)) ([b09efc3](https://github.com/appium/appium/commit/b09efc31c6967bbcf2c4314db9d2a0b6454fa8fb))
## [4.0.0-beta.0](https://github.com/appium/appium/compare/appium@3.7.0...appium@4.0.0-beta.0) (2026-09-19)
### ⚠ BREAKING CHANGES
* **base-driver:** align timeouts endpoints closer to W3C standard (#22776)
* users relying on the implicit debug default for console/file logs will now see info-level output unless they explicitly pass --log-level debug.
* **support:** Any script or tool that consumes Appium CLI output (driver/plugin list/install/update, doctor, etc.) and relies on the previous stream layout will need to be updated. Previously, non-JSON human-readable output (info/success/warning messages) was written to STDERR, with STDOUT reserved almost exclusively for --json output. Now that output goes to STDOUT, and only actual errors go to STDERR.
* **support:** Scripts that discarded STDOUT (1&gt;/dev/null) to suppress &quot;chatter&quot; while relying on STDERR for status text will now see that text on STDOUT instead.
* **support:** Scripts that captured STDERR to detect/log informational or success messages will no longer see them there — only genuine errors will appear on STDERR. --json output and error behavior are unaffected: --json continues to write clean JSON to STDOUT only, and errors continue to go to STDERR.
* **support:** @appium/logger&#x27;s Log class changes its default output stream from STDERR to STDOUT for all levels except error, and adds an errorStream/stderrLevel pair of properties. Any code that set .stream on a Log instance expecting it to capture all levels (including error) will now need to also set .errorStream to the same target.
* **base-driver:** @appium/base-driver&#x27;s default export (BaseDriver) and @appium/base-driver&#x27;s basedriver/helpers.js default export are removed; use the named exports instead. The appium/driver.js and driver.d.ts re-exports of the default are removed accordingly.
* **base-driver:** BaseDriver.reset() is removed; it was unused, not part of any protocol route or the Driver interface, and had no callers in this monorepo or any driver package that was checked.
* **base-driver:** BaseDriver&#x27;s constructor is removed in favor of inherited DriverCore construction; opts is no longer a required constructor argument (it now defaults to {}, matching DriverCore).
* **support:** @appium/support&#x27;s fs.glob option shape changed from the glob package&#x27;s GlobOptions to a smaller, @appium/support-owned GlobOptions interface. Supported fields: cwd, withFileTypes, absolute, lazy. Options like nodir/ignore/nocase are no longer supported directly — callers needing directory filtering can pass withFileTypes: true and filter on .isFile()/.isDirectory(); callers needing path exclusion can filter the returned array by pattern.
* **appium:** The --allow-cors CLI flag and allow-cors config file property have been removed. Use --allow-insecure&#x3D;*:cors (or --relaxed-security, optionally overridden with --deny-insecure&#x3D;*:cors) instead.
* **base-driver:** ISessionHandler.createSession (@appium/types) and LegacyCreateSessionArgs — overload and type deleted, single-argument signature only.
* **base-driver:** BaseDriver.createSession and AppiumDriver.createSession — collapsed to the single-argument mplementation; capability validation now checks the one argument directly instead of scanning three positions for the first W3C-shaped value.
* **base-driver:** FakeDriver.createSession (and the base-driver e2e test double) — overrides updated to match.
* **base-driver:** RelaxedCapsPlugin.createSession — this plugin existed specifically to normalize capabilities across the (now-gone) three positions; its hook is simplified to a single caps argument.
* **base-driver:** POST /session wire route (w3c.ts) — payloadParams.optional no longer sends capabilities three times into the command args.
* **base-driver:** BaseDriver.getSession, ISessionHandler.getSession, and the GET /session/:sessionId JSONWP route are gone. Use getAppiumSessionCapabilities to retrieve session capabilities and EventCommands.getLogEvents to retrieve event history instead.
* **base-driver:** The appium:eventTimings capability is removed; it only ever controlled whether getSession&#x27;s response was decorated with event history, which getLogEvents already exposes unconditionally.
* **support:** move npm-related helpers into appium package (#22721)
* **logger:** @appium/logger and @appium/support no longer export a default log — import the named log export instead.
* **logger:** AppiumLogger#errorAndThrow has been removed; use errorWithException instead.
* **logger:** Logger#enableProgress, disableProgress, progressEnabled, enableUnicode, disableUnicode have been removed; they were already permanently disabled no-ops.
* **base-plugin:** Plugin#logger has been removed; use Plugin#log instead.
* **base-plugin:** @appium/base-plugin no longer exports BasePlugin as the default export; use the named BasePlugin export.
* Core.driverData, DriverData, and the driverData parameter on ISessionHandler.createSession/deleteSession are gone. Any driver overriding get driverData() or relying on the umbrella driver passing sibling-session data into createSession/deleteSession needs to migrate off this mechanism.
* FakeDriver no longer supports the uniqueApp capability.
* **support:** npm.installPackage now takes a hasAppiumDependency boolean computed by the caller instead of importing the check itself, avoiding a support -&gt; appium dependency.
* **support:** The env export is removed from @appium/support.
* the minimum supported Node.js engine is set to ^22.22.2 || ^24.15.0 || &gt;&#x3D;26.0.0
* **base-driver:** @appium/base-driver no longer exports statusCodes or getSummaryByCode (the jsonwp-status module was removed).
* **base-driver:** errorFromMJSONWPStatusCode (aliased as errorFromCode) has been removed; use errorFromW3CJsonCode for W3C error-signature mapping.
* **base-driver:** ProtocolError instances no longer have a jsonwpCode property.
* **base-driver:** WebDriverProxy (formerly JWProxy) no longer proxies to or interprets responses from downstream servers speaking the legacy MJSONWP protocol; the ProtocolConverter class, COMMAND_URLS_CONFLICTS, and the downstreamProtocol getter/setter have been removed. Downstream automation servers must speak W3C WebDriver.
* **base-driver:** Responses no longer duplicate element references under the legacy ELEMENT key; only the W3C element-6066-11e4-a52e-4f735466cecf key is present. The MJSONWP_ELEMENT_KEY constant has been removed.
* **base-driver:** determineProtocol has been removed, and drivers can no longer produce the legacy {sessionId, status, value} response shape; all responses are now W3C-shaped regardless of how the client formatted its createSession capabilities.
* **base-driver:** DriverCore#setProtocolMJSONWP() has been removed.
* **base-driver:** The deprecated JWProxy export has been removed; use WebDriverProxy instead. Its source also moved from lib/jsonwp-proxy/proxy.js to lib/wd-proxy/proxy.js.
* **base-driver:** DriverCore#isMjsonwpProtocol() has been removed.
* **support:** The following @appium/support exports have been removed: process (getProcessIds, killProcess), mkdirp, imageUtil (requireSharp, cropBase64Image), mjpeg (MJpegStream), net&#x27;s FTP upload
support (FtpUploadOptions, NotHttpUploadOptions; uploadFile only supports http/https now), util&#x27;s uuidV1/uuidV3/uuidV5/localIp/cancellableDelay/multiResolve, and fs&#x27;s readPackageJsonFrom/findRoot/ F_OK/R_OK/W_OK/X_OK.
* **appium:** The --nodeconfig server argument and the server.nodeconfig config file property no longer exist. Users who need to register with a Selenium Grid should use the Grid 4 relay feature instead (see the Grid guide).
* **base-driver:** TLS-enabled Appium servers no longer negotiate SPDY/HTTP2 (spdy/2, spdy/3, spdy/3.1, h2) via ALPN; connections are now served as plain HTTPS (HTTP/1.1) using Node&#x27;s native https module. Clients relying on SPDY or HTTP/2 multiplexing against Appium&#x27;s TLS listener must switch to HTTP/1.1.
* **appium:** &#x60;npm install -g appium --drivers&#x3D;... --plugins&#x3D;...&#x60; no longer installs extensions automatically. Use &#x60;appium driver/plugin install&#x60; or add extensions to package.json instead.
* require(&#x27;@appium/support&#x27;) no longer works; consumers must import it instead.
* require(&#x27;@appium/base-driver&#x27;) no longer works; drivers extending BaseDriver via require must switch to import.
* require(&#x27;@appium/base-plugin&#x27;) no longer works; plugins extending BasePlugin via require must switch to import.
* require(&#x27;appium&#x27;) no longer works; plugins extending the package via require must switch to import.
* require(&#x27;@appium/fake-plugin&#x27;) no longer works; consumers must import it instead.
* require(&#x27;@appium/fake-driver&#x27;) no longer works; consumers must import it instead.
* require(&#x27;@appium/types&#x27;) no longer works; consumers must import it instead.
* require(&#x27;@appium/logger&#x27;) no longer works; consumers must import it instead.
* require(&#x27;@appium/schema&#x27;) no longer works; consumers must import it instead.
### Features
* **appium:** add --app-url-rules server arg to restrict remote app URLs ([#22753](https://github.com/appium/appium/issues/22753)) ([0b104f6](https://github.com/appium/appium/commit/0b104f6bf9b7180b111df40fd6a15b26d649eabd))
* **appium:** remove postinstall autoinstall-extensions script ([#22675](https://github.com/appium/appium/issues/22675)) ([cd0d8f1](https://github.com/appium/appium/commit/cd0d8f14e9d5906ecd1b438038a7f67f74f742fc))
* **appium:** remove Selenium Grid 3 support ([#22677](https://github.com/appium/appium/issues/22677)) ([8fcdcb3](https://github.com/appium/appium/commit/8fcdcb30b07c1ee582256fbef3538d6a3ceea5f2))
* **appium:** replace --allow-cors flag with --allow-insecure&#x3D;*:cors ([#22731](https://github.com/appium/appium/issues/22731)) ([926a0a5](https://github.com/appium/appium/commit/926a0a594935a6cf2e7375eb174eeb15af964f12))
* **appium:** validate extension manifests and extensions.yaml via ajv ([#22732](https://github.com/appium/appium/issues/22732)) ([8840f9f](https://github.com/appium/appium/commit/8840f9f9322b63bc8940c390b8230f1e5306fa62))
* backport diff from master ([#22749](https://github.com/appium/appium/issues/22749)) ([6323df1](https://github.com/appium/appium/commit/6323df1d7354f85cc631508f5cb6a3e35270d4bd))
* **base-driver:** add definitions for all WebDriver BiDi commands ([#22770](https://github.com/appium/appium/issues/22770)) ([ac01aa8](https://github.com/appium/appium/commit/ac01aa83ad73982eea48f856dfc0249b4a26e395))
* **base-driver:** align timeouts endpoints closer to W3C standard ([#22776](https://github.com/appium/appium/issues/22776)) ([93115d1](https://github.com/appium/appium/commit/93115d195773b011306ffb2e6d7d6cc935df0148))
* **base-driver:** remove legacy (M)JSONWP protocol support ([#22681](https://github.com/appium/appium/issues/22681)) ([0b83e77](https://github.com/appium/appium/commit/0b83e77e7a1c24a885d9895a99db30281cf4acea))
* **base-driver:** replace spdy with native https transport ([#22676](https://github.com/appium/appium/issues/22676)) ([ec7de47](https://github.com/appium/appium/commit/ec7de4768fdd8dde9d28db6fc2dfd63be45fd4c6))
* bump minimum supported Node.js engine to ^22.22.2 || ^24.15.0 || &gt;&#x3D;26.0.0 ([#22685](https://github.com/appium/appium/issues/22685)) ([9f4a11e](https://github.com/appium/appium/commit/9f4a11e7190290986d01743557e90e6e44f87638))
* Change the default log level to &#x60;info&#x60; ([#22756](https://github.com/appium/appium/issues/22756)) ([5e644a3](https://github.com/appium/appium/commit/5e644a392fbf11e2ca985408853d7f51aa85be17))
* convert remaining monorepo packages to ESM-only ([#22674](https://github.com/appium/appium/issues/22674)) ([3516e50](https://github.com/appium/appium/commit/3516e50ce6d022f4c0dc539071c593bf2983325f))
* **support:** move env/APPIUM_HOME resolution into appium package ([#22708](https://github.com/appium/appium/issues/22708)) ([8db8e57](https://github.com/appium/appium/commit/8db8e5748e26a7150cacbea09ba3ec52c5ce3fc0))
* **support:** remove deprecated APIs ([#22678](https://github.com/appium/appium/issues/22678)) ([e8ee716](https://github.com/appium/appium/commit/e8ee71685b4c21116e61c88fd8d2f0e39fac2edb))
* **types:** check execute method params against the command signature ([#22754](https://github.com/appium/appium/issues/22754)) ([b25fc20](https://github.com/appium/appium/commit/b25fc20c51a191c33695c1c71fbf129de834b52d))
### Bug Fixes
* address code review cleanups (plist, env memoize, test reuse) ([#22740](https://github.com/appium/appium/issues/22740)) ([0df8834](https://github.com/appium/appium/commit/0df883463812d57d44a0d6d80e9b88f124361934))
* Address several TODOs ([#22762](https://github.com/appium/appium/issues/22762)) ([d6ef7e3](https://github.com/appium/appium/commit/d6ef7e3f1240f161df622c850aea06637f9d1078))
* adjust deprecated JSONWP/MJSONWP routes ([#22763](https://github.com/appium/appium/issues/22763)) ([b129cbb](https://github.com/appium/appium/commit/b129cbbcd55656ae7f33b80b4b52719eb5179509))
* **appium:** lock the extension manifest while driver/plugin commands run ([#22767](https://github.com/appium/appium/issues/22767)) ([a73644f](https://github.com/appium/appium/commit/a73644fb4f4e7b0363117ac1262d7018094b42ff))
* **base-driver:** clean up aborted session creation ([#22733](https://github.com/appium/appium/issues/22733)) ([33d866e](https://github.com/appium/appium/commit/33d866e57506e1546aa4870dde75f7209be107bc))
* declare dependencies that were only resolving via hoisting ([#22758](https://github.com/appium/appium/issues/22758)) ([bcf1c57](https://github.com/appium/appium/commit/bcf1c579997b8dbb8292b1a77583aeef40b11ba4))
* **support:** route non-error CLI console output to stdout ([#22742](https://github.com/appium/appium/issues/22742)) ([4eb4d7f](https://github.com/appium/appium/commit/4eb4d7f317a685d681b6cf4e25c15b617a8cc7df))
### Code Refactoring
* **base-driver:** remove deprecated getSession command ([#22725](https://github.com/appium/appium/issues/22725)) ([f71d980](https://github.com/appium/appium/commit/f71d980779d23e9a19d33285646fad128eae4917))
* **base-driver:** remove deprecated multi-argument createSession overload ([#22730](https://github.com/appium/appium/issues/22730)) ([db72249](https://github.com/appium/appium/commit/db722498739ae7fe1deef1b98e02d04b455a4f70))
* **base-driver:** replace command mixin with explicit named exports ([#22741](https://github.com/appium/appium/issues/22741)) ([506bfee](https://github.com/appium/appium/commit/506bfeeb9aa15c0dd1f481868d347cb9b0d37f86))
* **base-plugin:** remove deprecated legacy logger and default export ([#22718](https://github.com/appium/appium/issues/22718)) ([62db5f0](https://github.com/appium/appium/commit/62db5f0b20c496b495ce27d490453861ddefb259))
* **logger:** remove default exports and dead logging APIs ([#22719](https://github.com/appium/appium/issues/22719)) ([fe24f52](https://github.com/appium/appium/commit/fe24f527a4412ed86c7b5e5afe0c6e26dc569e6b))
* remove deprecated driverData mechanism ([#22716](https://github.com/appium/appium/issues/22716)) ([ef8a8e1](https://github.com/appium/appium/commit/ef8a8e1934bfdc9699c2b9a7eca3e2201b4b9f93))
* **support:** move npm-related helpers into appium package ([#22721](https://github.com/appium/appium/issues/22721)) ([d82d496](https://github.com/appium/appium/commit/d82d496f9e80d6628abface3b623601a6f9791f9))
* **support:** replace glob package with native node:fs glob ([#22735](https://github.com/appium/appium/issues/22735)) ([add1615](https://github.com/appium/appium/commit/add161560701ac7fb2bc372ddefd9317146fbae8))
## [3.7.0](https://github.com/appium/appium/compare/appium@3.6.0...appium@3.7.0) (2026-08-24)
### Features
* **base-driver:** add httpServer.frontRouter for global extension middleware ([#22632](https://github.com/appium/appium/issues/22632)) ([f06226b](https://github.com/appium/appium/commit/f06226b7bb2fb7ee2f3a61833f4a1d7777d4ef21))
* **storage-plugin:** Add route aliases ([#22558](https://github.com/appium/appium/issues/22558)) ([a551c38](https://github.com/appium/appium/commit/a551c3817156bbc7e06793227643a2d11c0c0259))
### Bug Fixes
* **appium:** load @appium/docutils dynamically in docs build script ([#22584](https://github.com/appium/appium/issues/22584)) ([2c0f3fd](https://github.com/appium/appium/commit/2c0f3fd9e319c3bc774bdd2a65148da11094c87c))
* **appium:** parse --webhook as a URI instead of host:port ([#22635](https://github.com/appium/appium/issues/22635)) ([c35a12a](https://github.com/appium/appium/commit/c35a12a51d30fe311ef96ebe386cd9c5540e3927))
* **support:** do not treat a sibling folder as a subpath of the root ([#22586](https://github.com/appium/appium/issues/22586)) ([3a80498](https://github.com/appium/appium/commit/3a8049887dc673a6658c3821a2882e0145c3aa86))
## [3.6.0](https://github.com/appium/appium/compare/appium@3.5.2...appium@3.6.0) (2026-07-25)
### Features
* **appium:** add --allow-unknown-args to tolerate unrecognized server CLI arguments ([#22477](https://github.com/appium/appium/issues/22477)) ([f943fab](https://github.com/appium/appium/commit/f943fab1411826212cfb174e3847e610c25f37b7))
### Bug Fixes
* **appium:** Duplicated module import ([#22519](https://github.com/appium/appium/issues/22519)) ([154ea18](https://github.com/appium/appium/commit/154ea1825281335569f0d6952db527135c75e153))
* typescript config ([#22540](https://github.com/appium/appium/issues/22540)) ([ea6a2e1](https://github.com/appium/appium/commit/ea6a2e117f56c1d7ff459378700494407c51608e))
* Typescript references ([#22458](https://github.com/appium/appium/issues/22458)) ([fd70da7](https://github.com/appium/appium/commit/fd70da7ab407bf1f6fec8a3576b9a33f0aef9b69))
## [3.5.2](https://github.com/appium/appium/compare/appium@3.5.1...appium@3.5.2) (2026-06-18)
**Note:** Version bump only for package appium
+2
View File
@@ -16,6 +16,8 @@ plugins:
blog:
enabled: true
post_excerpt: required
mike:
canonical_version: 'latest'
redirects:
redirect_maps:
'intro/requirements.md': 'quickstart/requirements.md'
@@ -17,3 +17,8 @@ authors:
avatar: 'https://avatars.githubusercontent.com/u/4045041?v=4'
slug: 'sai'
url: 'https://saikrishna.tech'
mykola:
name: 'Mykola Mokhnach'
description: 'Appium Core Maintainer'
avatar: https://avatars.githubusercontent.com/u/7767781?v=4
slug: 'mykola'
@@ -8,11 +8,13 @@ date: 2025-11-25
# Announcing LambdaTest as Appium's Newest Development Partner
As part of our ongoing effort to recognize organizations that meaningfully support the Appium project through dedicated maintainer time, we are excited to welcome our newest Development Partner: LambdaTest!
LambdaTest has long been an active participant in the automated testing ecosystem. Today, they are officially formalizing their commitment by investing people—dedicated engineers who will directly support Appium's development, maintenance, and ecosystem growth.
This kind of hands-on contribution is crucial for ensuring Appium continues to evolve. We deeply value partners who empower their engineering teams to contribute code directly to open source rather than just sponsorship.
<!-- more -->
LambdaTest has long been an active participant in the automated testing ecosystem. Today, they are officially formalizing their commitment by investing people—dedicated engineers who will directly support Appium's development, maintenance, and ecosystem growth.
This kind of hands-on contribution is crucial for ensuring Appium continues to evolve. We deeply value partners who empower their engineering teams to contribute code directly to open source rather than just sponsorship.
<div style="text-align:center;width:100%">
<a href="https://lambdatest.com/?utm_source=appium.io&utm_medium=organic&utm_campaign=june_25&utm_term=sk&utm_content=webpage">
<img src="/docs/en/latest/assets/images/sponsor-logo-lambdatest-dark.png#only-dark" width="300" />
@@ -22,15 +24,19 @@ This kind of hands-on contribution is crucial for ensuring Appium continues to e
LambdaTest engineers are already contributing actively across several key Appium repositories. Their current focus includes:
**appium-mcp** – Supporting Appium's implementation of the Model Context Protocol (MCP) to drive forward modern AI-assisted capabilities.
**appium-remote-xpc** – Contributing to the native macOS/XPC-based remote execution layer, essential for iOS automation.
**appium-tun-tap** – Assisting in the development and maintenance of network virtualization and tunneling components for advanced testing scenarios.
* [`appium-mcp`](https://github.com/appium/appium-mcp) – Supporting Appium's implementation of the Model Context Protocol (MCP) to drive forward modern AI-assisted capabilities.
* [`appium-ios-remotexpc`](https://github.com/appium/appium-ios-remotexpc) – Contributing to the native macOS/XPC-based remote execution layer, essential for iOS automation.
* [`appium-ios-tuntap`](https://github.com/appium/appium-ios-tuntap) – Assisting in the development and maintenance of network virtualization and tunneling components for advanced testing scenarios.
These contributions strengthen critical infrastructure within the Appium ecosystem and demonstrate LambdaTest's commitment to deep technical involvement in the project's evolution.
About LambdaTest
**About LambdaTest**
LambdaTest’s platform already serves a massive community of Appium users. As a GenAI-powered Quality Engineering Platform, they provide a full-stack testing cloud with 10,000+ real mobile devices and 3,000+ browsers.
Beyond standard infrastructure, they are pushing the boundaries of test execution with AI-native test management and next-generation agents like KaneAI. By supporting Appium alongside Selenium, Playwright, and other major frameworks, LambdaTest helps teams orchestrate intelligent automation at cloud scale.
LambdaTest will continue contributing people, code, and development expertise to strengthen Appium's core functionality. Their commitment represents a significant investment into the future of the framework, and we're thrilled to have them collaborating with us at this deeper level.
The Appium project is proud to recognize LambdaTest as our newest official Development Partner. We can't wait to see what we'll build together!
@@ -0,0 +1,38 @@
---
authors:
- mykola
date: 2026-09-01
---
# Announcing watchOS Simulator Support in the XCUITest Driver
We’re excited to announce that the XCUITest driver now supports automation of watchOS apps — a
platform that’s been on Appium’s requested list for a long time.
<!-- more -->
In order to automate watchOS, you'll need the following:
* XCUITest driver version 12.6.0 or later (WebDriverAgent 16.5.0 or later)
* Xcode 15.4 or later
* watchOS 10 or later
Important to note: ^^the driver currently only works with simulators — real devices are not
compatible.^^
The good news: automating a watchOS app looks just like automating any other iOS/iPadOS app. The
standard use-cases of finding and clicking on elements, retrieving the page source, using the
Appium Inspector — all work as usual. The one exception here is standard W3C gesture/touch actions
(tap/swipe via the Actions API), which are not supported.
On top of that, a few watchOS-specific extensions are available:
- `mobile: pressButton` — press the Digital Crown or Action button
- `mobile: rotateDigitalCrown` — rotate the Digital Crown (since driver version 12.7.0)
- `mobile: performHandGesture` — perform double-tap or wrist flick hand gestures (since driver
version 12.7.0)
For full setup instructions, capabilities, and limitations, check out the
[watchOS guide](https://appium.github.io/appium-xcuitest-driver/latest/guides/watchos/).
Happy testing!
@@ -103,6 +103,27 @@ export APPIUM_WORKSPACE=@appium/base-driver
npm run test:unit -w $APPIUM_WORKSPACE
```
### Code Style
#### JSDoc
Add concise JSDoc to all public class methods and exported functions.
#### Module Ordering
Place exported functions and classes before non-exported functions and classes in each module.
This rule does not apply to constants or variables.
#### Class Member Ordering
When adding or moving class methods, follow these conventions:
- Order methods by visibility: public → protected → private. Methods without an explicit visibility
modifier belong to the public group.
- Place private helper methods at the end of the class.
- Preserve existing order within each visibility group.
- Before finishing, check the ordering of methods you added or moved.
### Documentation
The documentation for this project is [available in the project repository itself](https://github.com/appium/appium/tree/master/packages/appium/docs).
@@ -222,18 +222,13 @@ So you'll probably end up overriding `createSession`. You can do so by defining
driver:
```js
async createSession(jwpCaps, reqCaps, w3cCaps, otherDriverData) {
const [sessionId, caps] = super.createSession(w3cCaps);
async createSession(w3cCaps) {
const [sessionId, caps] = await super.createSession(w3cCaps);
// do your own stuff here
return [sessionId, caps];
}
```
For legacy reasons, your function will receive old-style JSON Wire Protocol desired and required
caps as the first two arguments. Given that the old protocol isn't supported anymore and clients
have all been updated, you can instead only rely on the `w3cCaps` parameter. (For a discussion
about what `otherDriverData` is about, see the section below on concurrent drivers).
You'll want to make sure to call `super.createSession` in order to get the session ID as well as
the processed capabilities (note that capabilities are also set on `this.caps`; modifying `caps`
locally here would have no effect other than changing what the user sees in the create session
@@ -520,14 +515,13 @@ route, then the route will not be proxied and instead will be handled by your dr
example, we are avoiding proxying all `POST` routes that have the `appium` prefix.
Next, we have to set up the proxying itself. The way to do this is to use a special class from
Appium called `JWProxy`. (The name means "JSON Wire Proxy" and is related to a legacy
implementation of the protocol). You'll want to create a `JWProxy` object using the details required to
connect to the remote server:
Appium called `WebDriverProxy`. You'll want to create a `WebDriverProxy` object using the details
required to connect to the remote server:
```js
// import {JWProxy} from 'appium/driver';
// import {WebDriverProxy} from 'appium/driver';
const proxy = new JWProxy({
const proxy = new WebDriverProxy({
server: 'remote.server',
port: 1234,
base: '/',
@@ -539,8 +533,8 @@ this.proxyCommand = proxy.command.bind(proxy);
Here we are creating a proxy object and assigning some of its methods to `this` under the names
`proxyReqRes` and `proxyCommand`. This is required for Appium to use the proxy, so don't forget
this step! The `JWProxy` has a variety of other options which you can check out in the source code,
as well. (TODO: publish options as API docs and link here).
this step! The `WebDriverProxy` has a variety of other options [which you can check out in the
source code as well.](https://github.com/appium/appium/blob/master/packages/base-driver/lib/wd-proxy/proxy.ts)
Finally, we need a way to tell Appium when the proxy is active. For your driver it might always
be active, or it might only be active when in a certain context. You can define the logic as an
@@ -720,6 +714,12 @@ A couple notes about this system:
mode!
1. The `executeMethod` helper will reject with an error if a script name doesn't match one of the
script names defined as a command in `executeMethodMap`, or if there are missing parameters.
1. If your driver is written in TypeScript, declare the map `as const satisfies
ExecuteMethodMap<MyDriver>` (with `ExecuteMethodMap` imported from `@appium/types`). The
compiler then checks that each `command` names a method of your driver, and that the
`required`/`optional` params are consistent with that method's signature: the method must not
require a parameter which is listed as `optional` (or not listed at all), and must accept all
of the listed parameters.
One of the nice things about the Execute Method strategy is that methods implemented in this way
will be available via the classic or BiDi interfaces (since they will result in the same Appium
@@ -794,44 +794,32 @@ that multiple simultaneous sessions don't use the same resources:
1. Have your users specify resource IDs via capabilities (`appium:driverPort` etc)
1. Just always use free resources (find a new random port for each session)
1. Have each driver express what resources it is using, then examine currently-used resources from
other drivers when a new session begins.
1. Have each running instance of your driver publish and subscribe to messages on a shared IPC
channel, so sessions can coordinate directly with one another.
To support this third strategy, you can implement `get driverData` in your driver to return what
sorts of resources your driver is currently using, for example:
```js
get driverData() {
return {specialPort: 1234, specialFile: /path/to/file}
}
```
Now, when a new session is started on your driver, the `driverData` response from any other
simultaneously running drivers (of the same type) will also be included, as the last parameter of
the `createSession` method:
```js
async createSession(jwpCaps, reqCaps, w3cCaps, driverData)
```
You can dig into this `driverData` array to see what resources other drivers are using to help
determine which ones you want to use for this particular session.
The per-session IPC channel described in [Send messages to plugins running on the same
session](#send-messages-to-plugins-running-on-the-same-session) is isolated per session, so it
can't be used for this: it doesn't let you talk to *other* sessions of your driver. Instead,
construct and hold on to your own `AppiumIpc` instance (exported from `appium/driver.js`) at
module scope in your driver's package, for example as a memoized singleton. Since Node.js caches
modules, every instance of your driver loaded within the same Appium server process will share
that one `AppiumIpc` object, and can use it to publish and subscribe to topics across sessions —
the same way the per-session channel works, just not scoped to a single session. See
[xcuitest-driver's `session-claim-handler.ts`](https://github.com/appium/appium-xcuitest-driver/blob/master/lib/session-claim-handler.ts)
for a real example, which uses this technique to detect and resolve conflicting sessions targeting
the same device UDID.
!!! warning
Be careful here, since `driverData` is only passed between sessions of a single running Appium
server. There's nothing to stop a user from running multiple Appium servers and requesting your
driver simultaneously on each of them. In this case, you won't be able to ensure independence
of resources via `driverData`, so you might consider using file-based locking mechanisms or
something similar.
This only works for sessions running within the same Node.js process (i.e. the same Appium
server). It cannot coordinate sessions running on different Appium server processes; for that,
consider file-based locking or a similar out-of-process mechanism.
!!! warning
It's also important to note you will only receive `driverData` for other instances of *your*
driver. So unrelated drivers also running may still be using some system resources. In general
Appium doesn't provide any features for ensuring unrelated drivers don't interfere with one
another, so it's up to the drivers to allow users to specify resource locations or addresses to
avoid clashes.
Appium also doesn't provide any features for ensuring unrelated drivers don't interfere with
one another, so it's up to the drivers to allow users to specify resource locations or
addresses to avoid clashes.
### Log events to the Appium event timeline
@@ -979,10 +967,15 @@ export type IpcMessage<T> = {
#### IPC Notes
There are some important things to keep in mind when using Appium's IPC feature:
- IPC is only available once `onIpcInit` has been called, at the end of the `createSession` flow.
It cannot be used statically or in `createSession` hooks
- In sum, IPC is only for use during a session (this is also to prevent drivers/plugins from
accessing or reading data sent on IPC channels in other sessions.)
- The `ipcSubscribe` method (and the IPC channel it subscribes to) is only available once
`onIpcInit` has been called, at the end of the `createSession` flow. It cannot be used statically
or in `createSession` hooks
- The channel that `ipcSubscribe` gives you access to is scoped to the current session, and is
shared only with the plugins active in that same session (this is also to prevent drivers/plugins
from accessing or reading data sent on IPC channels in other sessions.) To coordinate across
multiple concurrent sessions of your own driver instead, see [Make itself aware of resources
other concurrent drivers are
using](#make-itself-aware-of-resources-other-concurrent-drivers-are-using).
- The default max size of an IPC message is 1MB. This can be configured by the server-admin by
using the `--max-ipc-data-size` arg (value is a number in bytes).
- If a message exceeds the configured size, any call to `publish` methods will throw, so be
@@ -257,7 +257,7 @@ throw new errors.NoSuchElementError();
### Log messages to the Appium log
You can always use `console.log`, of course, but Appium provides a nice logger for you as
`this.logger` (it has `.info`, `.debug`, `.log`, `.warn`, `.error` methods on it for differing log
`this.log` (it has `.info`, `.debug`, `.warn`, `.error` methods on it for differing log
levels). If you want to create an Appium logger outside of a plugin context (say in a script or
helper file), you can always construct your own too:
@@ -476,10 +476,10 @@ capability.
You probably don't normally need to update the Appium server object (which is an
[Express](https://expressjs.com/) server having already been
[configured](https://github.com/appium/appium/blob/master/packages/base-driver/lib/express/server.js)
in a variety of ways). But, for example, you could add new Express middleware to the server to
support your plugin's requirements. To update the server you must implement the `static async
updateServer` method in your class. This method takes three parameters:
[configured](https://github.com/appium/appium/blob/master/packages/http-server/lib/server.ts)
in a variety of ways). But, for example, you could add new routes to the server to support your
plugin's requirements. To update the server you must implement the `static async updateServer`
method in your class. This method takes three parameters:
* `expressApp`: the Express app object
* `httpServer`: the Node HTTP server object
@@ -492,6 +492,25 @@ results you'll need to test! Warning: this should be considered an advanced feat
knowledge of Express, as well as the care not to do anything that could affect the operation of
other parts of the Appium server!
`updateServer` runs *after* Appium's own routes are registered, so `expressApp.use(...)` here
won't see requests to paths Appium already owns — Express matches middleware and routes in
registration order. For middleware that must see requests to those routes too (e.g. logging or
auth), use `httpServer.frontRouter` instead: Appium mounts this
[Router](https://expressjs.com/en/5x/api/router/) before any route is registered, so anything
attached to it — even from inside `updateServer` — still runs ahead of route matching. Note this
is still *after* Appium's own baseline middleware (CORS, WebSocket upgrade handling, body
parsing, etc.), so it won't see requests Appium itself has already rejected, e.g. a CORS
preflight failure or a body that failed to parse:
```js
static async updateServer(expressApp, httpServer, cliArgs) {
httpServer.frontRouter.use((req, res, next) => {
console.log(`Incoming request: ${req.method} ${req.url}`);
next();
});
}
```
### Handle unexpected session shutdown
When developing a plugin you may want to add some cleanup logic for when a session ends. You would
@@ -42,7 +42,7 @@ In order to get some value in logs replaced by a generic mask it is necessary:
[util.format](https://nodejs.org/api/util.html#utilformatformat-args) API.
- While sending the appropriate server request, where this log line is used and should be masked,
add the custom header `X-Appium-Is-Sensitive` with its value set to `1` or `true` (case-insensitive).
add the custom header `X-Appium-Is-Sensitive` with its value set to `1`, `true` or `yes` (case-insensitive).
Without such header the above log value is not going to be masked.
This way it is possible to conditionally mask log records depending on which
request is being handled by the extension if the log expression is used in the
+27 -13
View File
@@ -69,18 +69,13 @@ appium driver install safari
appium driver install uiautomator2
```
### [Windows](https://github.com/appium/appium-windows-driver)
!!! warning
Only the Node.js-based driver part is maintained by the Appium team. The server part
(WinAppDriver executable) is provided by Microsoft, but has not been maintained since 2022.
### [WinCore](https://github.com/appium/appium-wincore-driver)
* Target: Windows applications
* Mode: Native
```sh title="Install This Driver"
appium driver install windows
appium driver install wincore
```
### [XCUITest](https://appium.github.io/appium-xcuitest-driver/)
@@ -126,7 +121,7 @@ appium driver install --source=npm appium-flutter-integration-driver
appium driver install --source=npm appium-lg-webos-driver
```
### [Linux](https://github.com/fantonglang/appium-linux-driver)
### [Linux (@fantonglang)](https://github.com/fantonglang/appium-linux-driver)
!!! warning
@@ -143,13 +138,18 @@ yarn install
node ./
```
### [Linux (@Itsmeaj)](https://github.com/Itsmeaj/appium)
* Target: Linux desktop applications on X11 and Wayland
* Mode: Native
* Supported by: `@Itsmeaj`
```sh title="Install This Driver"
appium driver install --source=npm @itsmeaj/appium-linux-driver
```
### [NovaWindows](https://github.com/AutomateThePlanet/appium-novawindows-driver)
!!! info
This driver is recommended as a drop-in replacement for the partially unmaintained
[Windows driver](#windows)
* Target: Windows applications
* Mode: Native
* Supported by: Community / Automate The Planet
@@ -192,6 +192,20 @@ npm install appium-tizen-driver
appium driver install --source=npm appium-tizen-tv-driver
```
### [Windows (legacy)](https://github.com/appium/appium-windows-driver)
!!! warning
This driver relies on WinAppDriver, which has not been maintained since 2022, and is only
compatible with Appium 1-3. Use the [WinCore](#wincore) driver with Appium 4 and newer.
* Target: Windows applications
* Mode: Native
```sh title="Install This Driver"
appium driver install --source=npm appium-windows-driver
```
### [You.i Engine](https://github.com/YOU-i-Labs/appium-youiengine-driver)
!!! warning
+1 -1
View File
@@ -95,4 +95,4 @@ and the values of any specific configuration it provides are under this. For exa
All properties are case-sensitive and will be in
[kebab-case](https://en.wikipedia.org/wiki/Naming_convention_(programming)#Delimiter-separated_words).
For example, `callback-port` is allowed, but `callbackPort` is not.
For example, `base-path` is allowed, but `basePath` is not.
@@ -6,14 +6,12 @@ title: Retrieving Event Timings
---
Appium comes with the ability to retrieve timing information about startup
information and command length. This is an advanced feature that is controlled
by the use of the `appium:eventTimings` capability (set it to `true` to log event
timings).
information and command length. This is an advanced feature; event and
command timings are collected automatically for the duration of a session.
With this capability turned on, the `POST /session/:id/appium/events` response (i.e.,
the response to `driver.logs.events` or similar, depending on client) will be
decorated with an `events` property. This is the structure of that `events`
property:
The `POST /session/:id/appium/events` response (i.e., the response to
`driver.logs.events` or similar, depending on client) has the following
structure:
```
{
-59
View File
@@ -117,62 +117,3 @@ hub, you'll be able to send all your Appium traffic via the single hub endpoint
And of course, you're able to link up Appium servers and nodes running on different machines in
your network to form a larger grid.
## Using Selenium Grid 3
It is possible to register your Appium server with a local [Selenium Grid 3](https://www.selenium.dev/documentation/legacy/selenium_3/grid_3/)
([setup docs](https://www.selenium.dev/documentation/legacy/grid_3/setting_up_your_own_grid/)) instance by using the
`--nodeconfig` server argument.
```bash
appium server --nodeconfig /path/to/nodeconfig.json --base-path=/wd/hub
```
In the referenced config file you have to define the `browserName`, `version` and `platform`
capabilities and based on these parameters the grid will re-direct your test to the right device.
You will also need to configure your host details and the Selenium Grid details. For a full list of
all parameters and descriptions see
[here](https://www.selenium.dev/documentation/legacy/selenium_3/grid_setup/).
Once you start the Appium server it will register with the grid, and you will see your device on
the grid console page:
`http://**\<grid-ip-adress\>**:**\<grid-port\>**/grid/console`
### Example Grid Node Configuration JSON
```json
{
"capabilities":
[
{
"browserName": "<e.g._iPhone5_or_iPad4>",
"version":"<version_of_iOS_e.g._7.1>",
"maxInstances": 1,
"platform":"<platform_e.g._MAC_or_ANDROID>"
}
],
"configuration":
{
"cleanUpCycle":2000,
"timeout":30000,
"proxy": "org.openqa.grid.selenium.proxy.DefaultRemoteProxy",
"url":"http://<host_name_appium_server_or_ip-address_appium_server>:<appium_port>/wd/hub",
"host": "<host_name_appium_server_or_ip-address_appium_server>",
"port": <appium_port>,
"maxSession": 1,
"register": true,
"registerCycle": 5000,
"hubPort": <grid_port>,
"hubHost": "<Grid_host_name_or_grid_ip-address>",
"hubProtocol": "<Protocol_of_Grid_defaults_to_http>"
}
}
```
If `url`, `host`, and `port` are not given, the config will be auto updated to point to
`localhost:<appium-port>`.
If your Appium server is running on a different machine to your Selenium Grid server, make sure you
use an external name/IP address in your `host` and `url` configuration; `localhost` and `127.0.0.1`
will prevent Selenium Grid from connecting correctly.
@@ -31,10 +31,6 @@ installing it only installs the core Appium server, without any drivers.
When it comes to installing Appium 2 drivers, there are several approaches you can take:
* Add the `--drivers` flag when installing Appium, for example:
```bash
npm i -g appium --drivers=xcuitest,uiautomator2
```
* Use the [Appium Extension CLI](../reference/cli/extensions.md), for example:
```bash
appium driver install uiautomator2
@@ -0,0 +1,649 @@
---
title: Migrating to Appium 4
---
<style>
.md-typeset .grid {
grid-template-columns: repeat(auto-fit,minmax(min(100%,11rem),1fr));
}
</style>
This document is a guide for those who are using Appium 3 and would like to upgrade to Appium 4.
It contains a list of breaking changes, as well as suggestions for handling them.
The [behavior fixes](#behavior-fixes) section also highlights corrections that can affect existing
tests, client error handling, and server integrations.
Appium 4 focuses on modernizing the codebase (Node.js requirements, ESM-only packages, dropping
long-deprecated protocol support and APIs) rather than introducing a large number of new
user-facing features. Appium users should check the [Node.js version](#node-2222-required),
[timeout inputs](#timeouts-endpoint-aligned-with-w3c-standard), and
[capability validation](#stricter-capability-validation). The other breaking changes mostly affect
driver and plugin developers, or users of specific removed flags/endpoints.
## Installation
Appium 4 is currently only available as a beta release, published under the `beta` npm dist-tag.
The installation method otherwise remains the same as for Appium 3. If you want to upgrade, you
can simply install Appium 4 on top of your existing installation:
```bash
# optional: `appium setup reset`
npm install -g appium@beta
```
## Breaking Changes
### Node 22.22+ Required
Appium 3 required Node `^20.19.0 || ^22.12.0 || >=24.0.0`. Appium 4 drops Node 20 support and
bumps the minimum required version to `^22.22.2 || ^24.15.0 || >=26.0.0`.
!!! info "Actions Needed"
Upgrade Node.js to `v22.22.2`/`v24.15.0` or newer
### All Packages Are Now ESM-Only
Every `@appium/*` package (and `appium` itself) is now published as `"type": "module"` with no
CommonJS build. `require()` no longer works for these packages. This affects anyone importing
Appium or a driver/plugin package directly from Node.js code, not just driver/plugin developers.
!!! info "Actions Needed"
Import Appium packages with ESM `import` syntax, or use dynamic `import()` from CommonJS code
### `--nodeconfig` Flag Removed
Support for registering with a legacy Selenium Grid 3 hub has been removed, along with the
`--nodeconfig` server argument and the `server.nodeconfig` config file property.
!!! info "Actions Needed"
If you relied on Grid 3 registration, use the [Selenium Grid 4 relay feature](./grid.md) instead
### `--allow-cors` Flag Removed
CORS is now gated behind the same [insecure feature](./security.md) mechanism used for other
server-level features (e.g. `session_discovery`), instead of its own dedicated flag.
Without CORS enabled, state-changing HTTP requests carrying an `Origin` header and WebSocket
upgrades carrying that header now receive HTTP `403`. Ordinary `GET`, `HEAD`, and `OPTIONS`
requests are exempt from the HTTP check; requests without `Origin` are unaffected.
Library callers of `@appium/http-server`'s `server()` and `configureServer()` now default to
`allowCors: false` ([#22990](https://github.com/appium/appium/pull/22990)).
!!! info "Actions Needed"
Replace `--allow-cors` with `--allow-insecure=*:cors` (or enable it via `--relaxed-security`)
for browser clients. Library callers that need CORS must explicitly pass `allowCors: true`
### Unused Server Arguments Removed
The following server arguments had no effect for a long time and have been removed, together with
their `server.*` config file properties:
* `--callback-address` (`-ca`) and `--callback-port` (`-cp`)
* `--debug-log-spacing`
* `--strict-caps`
* `--trace-dir`
Passing any of them on the command line now fails with an unrecognized argument error (unless
[`--allow-unknown-args`](../reference/cli/server.md) is set), and config files containing them
fail schema validation.
!!! info "Actions Needed"
Remove these arguments from startup scripts and config files
### Extension Auto-Install on `npm install` Removed
The `npm install -g appium --drivers=... --plugins=...` postinstall hook that automatically
installed extensions has been removed, since npm postinstall hooks are a supply-chain security
risk and newer npm versions no longer support passing custom arguments to them.
!!! info "Actions Needed"
Use `appium driver install`/`appium plugin install`, or declare extensions in your project's
`package.json`, instead of `--drivers`/`--plugins` install-time arguments
### Local Extension Installs No Longer Create Global npm Links
`appium driver install --source=local` and `appium plugin install --source=local` now install
directly from the supplied local directory, without first creating an intermediate global npm
link. Local installs still link to the source directory, so source changes do not require
reinstalling the extension (rebuild the extension first if its source needs compilation).
!!! info "Actions Needed"
Keep using `--source=local` with the extension's directory for local development. If other
tooling relied on the package being linked in npm's global prefix, manage that global link
separately; Appium no longer creates it as a side effect of a local extension install.
### `windows` Driver Shortcut Replaced by `wincore`
The `windows` shortcut for `appium driver install` has been removed, and the new `wincore` shortcut
resolves to [`appium-wincore-driver`](https://github.com/appium/appium-wincore-driver). The old
`appium-windows-driver` depends on WinAppDriver, which has not been maintained since 2022 and only
speaks the legacy JSONWP protocol, so it does not work with Appium 4.
!!! info "Actions Needed"
1. While Appium 3 is still installed, install `appium-wincore-driver` v3 next to the legacy
driver and verify that your Windows tests pass with it. The `wincore` shortcut is not
available in Appium 3, so the package must be specified explicitly:
`appium driver install --source=npm appium-wincore-driver@^3.0.0`
2. After upgrading to Appium 4, update to the Appium 4 compatible driver release,
since v3 of the driver only works with Appium 3:
`appium driver update wincore --unsafe`
3. Uninstall the legacy driver, as it does not work with Appium 4:
`appium driver uninstall appium-windows-driver`
### Default Log Level Changed to `info`
The default `--log-level` changed from `debug` to `info`. Debug-level logging is very verbose and
mostly only useful for troubleshooting.
!!! info "Actions Needed"
Pass `--log-level debug` explicitly if you rely on the previous verbosity
### CLI Output Streams Split
Appium's CLI commands (`driver`/`plugin install|list|update`, `doctor`, etc.) used to write all
non-JSON output to `STDERR`. Human-readable output (`log`/`ok`/`info`/`warn`) now goes to
`STDOUT`; only `error` output still goes to `STDERR`. The main server's log output (Winston-based)
is unaffected, since it already split streams this way.
!!! info "Actions Needed"
If you have scripts that capture Appium CLI output, make sure they read from the correct stream
### TLS Server No Longer Negotiates SPDY/HTTP2
Appium's TLS-enabled server used the unmaintained `spdy` package, which allowed negotiating
SPDY/HTTP2 over ALPN. It has been replaced with Node's native `https` module, which only serves
plain HTTPS (HTTP/1.1).
!!! info "Actions Needed"
Clients relying on SPDY or HTTP/2 multiplexing against Appium's TLS listener must switch to
plain HTTPS (HTTP/1.1)
### Timeout Errors Now Return HTTP 500
Per the W3C WebDriver standard, `TimeoutError` and `ScriptTimeoutError` now map to HTTP `500`
instead of `408`. The stale `408` mapping could cause keep-alive clients/proxies to auto-retry an
already-executed timed-out command.
!!! info "Actions Needed"
None, unless your code inspects the raw HTTP status code of a timeout response
### Timeouts Endpoint Aligned With W3C Standard
`GET/SET /timeouts` is now closer to [the W3C standard](https://w3c.github.io/webdriver/#timeouts):
* `script`, `pageLoad`, and `implicit` accept `null` or integers from `0` to `Number.MAX_SAFE_INTEGER`
milliseconds. Strings and fractions now return HTTP `400` / `invalid argument`
([#22961](https://github.com/appium/appium/pull/22961))
* `null` disables the timeout and is preserved in managed drivers and readback. These fields now
use `TimeoutValue` (`number | null`), including driver-level `timeouts()`/`ISessionHandler` types
* `GET /session/:sessionId/timeouts` now returns `script` and `pageLoad` in addition to the existing
`implicit` and Appium-specific `command` values
* `POST /session/:sessionId/timeouts` accepts `command` directly as an Appium-specific idle timeout
in milliseconds; it is not a standard W3C timeout field
* Updates without `script`, `pageLoad`, or `implicit` no longer fail: omitted fields keep their
values and `{}` succeeds without changes. Invalid updates change no values
([#22960](https://github.com/appium/appium/pull/22960),
[#22961](https://github.com/appium/appium/pull/22961))
* `command` remains an Appium-specific numeric idle timeout with legacy parsing. The `type`/`ms`
parameters are deprecated
!!! info "Actions Needed"
Replace numeric strings and `type`/`ms` with standard fields. Handle `null` in custom setters
and readback; `implicit: null` allows unlimited waiting
Initial `capabilities.timeouts` now initializes BaseDriver's local timeout state before backend
startup, using the same value checks as Set Timeouts. Partial objects are accepted and omitted
fields keep their defaults; backend handling remains driver-specific
([#22983](https://github.com/appium/appium/pull/22983)).
Timeout capability validation also runs on `alwaysMatch` and every `firstMatch` candidate before
driver selection. A malformed timeout object or invalid standard timeout value rejects the request
with HTTP `400` / `invalid argument`, even when an earlier candidate could match. Top-level
`timeouts: null` is still omitted, nested null timeout values are preserved, and unknown timeout
properties are ignored for validation.
### Command POST Bodies Must Be JSON Objects
A command `POST` body now has to be a JSON object, as required by the W3C WebDriver standard.
A request with no body, or a body that is a JSON array such as `[]`, returns HTTP `400` with
`invalid argument` (`HTTP request body must be a JSON object`) and is not forwarded to the driver.
`POST` with `{}` is unchanged, including commands that take no parameters. Routes that set
`payloadParams.wrap` can still accept a bare array.
!!! info "Actions Needed"
Send `{}` instead of an empty body. Clients that already send a JSON object, including the
Appium clients, need no change
### Plugins Intercepting Proxied Commands Receive Unvalidated Arguments
When a plugin intercepts a command that would otherwise be proxied to a downstream WebDriver
server, Appium no longer validates required payload fields or command arguments before calling
the plugin. This matches direct proxying, where the downstream remote end already owns those
checks ([#23060](https://github.com/appium/appium/pull/23060)). Plugins that previously relied on
Appium to validate these arguments can now receive missing or invalid values.
!!! info "Actions Needed"
If your plugin handles such a command without calling `next()`, validate the inputs it uses.
A pass-through plugin can call `next()` and delegate validation to the downstream remote end.
The downstream driver's validation responsibility is unchanged.
Local and proxy-avoided commands retain Appium's validation. Session-existence checks and
JSON-object request-body checks also remain in place before the plugin is called.
### BiDi Commands No Longer Accept Stray `sessionId`/`id` Params
Unlike HTTP endpoints, WebDriver BiDi commands have no URL-derived session/element id, so a stray
`sessionId` or `id` key inside BiDi command params is no longer silently accepted; it is now
validated like any other unknown parameter.
!!! info "Actions Needed"
Remove `sessionId`/`id` from the parameters of any custom BiDi command invocation
### `getStatus` No Longer Waits In the Commands Queue
`GET /status` is now exempt from the driver commands queue by default, so it can be answered while
a long-running command is still in progress. The new-command idle timer is now only restarted once
no commands are executing. Drivers can customize the exemption list by overriding the new
`queueExemptCommands` getter.
!!! info "Actions Needed"
None, unless your driver relied on `getStatus` being serialized with other commands
### Stricter Capability Validation
* Standard capability keys are case-sensitive (e.g. `platformName`, not `PlatformName`).
Value matching and namespaced extensions are unchanged
([#22976](https://github.com/appium/appium/pull/22976))
* Malformed capability containers and parser `invalid argument` errors now return HTTP `400`,
not HTTP `500` / `session not created`. Driver selection/loading failures still use
`session not created` ([#22977](https://github.com/appium/appium/pull/22977))
* Top-level `null` capabilities are omitted before validation and merging, avoiding false
duplicate-key errors. Nested nulls, such as `timeouts: {script: null}`, are preserved
([#22975](https://github.com/appium/appium/pull/22975))
* An empty `firstMatch` array in the new session request is no longer silently replaced with a
single empty entry; it is rejected with `InvalidArgumentError`, as required by the W3C standard
* Setting both `appium:noReset` and `appium:fullReset` to `true` now fails with `SessionNotCreatedError`
instead of a generic error, and no longer leaves partially initialized session state behind
* `appium:newCommandTimeout` values that are `null`, blank strings, or non-finite numeric values are now
ignored (the default idle timeout is kept) instead of silently disabling the timeout. Other invalid
types, such as nonnumeric strings, still fail capability validation
!!! info "Actions Needed"
Use exact standard key names and vendor-prefixed extensions. Send `firstMatch: [{}]` or omit it.
Update error assertions for malformed capabilities; top-level `null` values no longer reach drivers
Every `firstMatch` candidate is now validated and merged before selection. Invalid supplied values
or duplicate keys in any candidate reject the request, even when another candidate could match;
missing required driver capabilities and unsupported platforms still allow matching to continue
([#22982](https://github.com/appium/appium/pull/22982)).
### Default Capabilities Apply to Every Candidate
`--default-capabilities` now fills missing keys in each `firstMatch` candidate independently.
Client values in `alwaysMatch` or the current candidate take precedence; a value in another
candidate does not suppress the default. Duplicate keys between `alwaysMatch` and `firstMatch`
are still rejected. This is Appium-specific defaulting behavior
([#22988](https://github.com/appium/appium/pull/22988)); see
[Default Capabilities](../reference/cli/server.md#default-capabilities) for an example.
!!! info "Actions Needed"
Check defaults when sending multiple candidates: later candidates now receive missing defaults too
### Storage Plugin Routes Respect `--base-path`
The [storage plugin](https://github.com/appium/appium/tree/master/packages/storage-plugin) HTTP
routes and stream/events WebSocket paths used to be mounted at the server root, ignoring `--base-path`.
They are now prefixed with the base path, e.g. `/wd/hub/appium/storage/add`. Also, storage file names
starting with a dot, reserved partial-file names, and concurrent WebSocket uploads of the same file
are now rejected.
!!! info "Actions Needed"
If the server uses a non-default `--base-path`, update storage plugin clients to include it in the URLs
and avoid file names starting with a dot or ending in `.filepart` (case-insensitive).
Concurrent WebSocket uploads to the same case-insensitive file name now close with code `1013`;
retry with a new connection after the active upload finishes
Pending upload WebSocket routes are also preserved across retries,
and errors on upload event sockets are logged instead of potentially terminating the server.
## Behavior Fixes
These fixes are relevant when upgrading from Appium 3, even though they do not remove APIs.
Most require no changes unless your tests or integrations depend on the previous behavior.
### HTTP Responses and WebDriver Errors
* `HEAD` requests to GET command URLs now return HTTP `405` without executing the GET command.
Explicit extension HEAD handlers and custom server routes retain their behavior. Use `GET /status`
for health checks ([#22992](https://github.com/appium/appium/pull/22992)).
* Unsupported log types and unknown legacy timeout `type` values now return HTTP `400` /
`invalid argument` instead of HTTP `500` / `unknown error`
([#22986](https://github.com/appium/appium/pull/22986),
[#22987](https://github.com/appium/appium/pull/22987)).
* WebDriver HTTP responses now include `Cache-Control: no-cache`, including successful responses,
errors, and responses reconstructed by `WebDriverProxy`
([#22971](https://github.com/appium/appium/pull/22971)). Caches must revalidate these responses
before reusing them; this header does not mean `no-store`.
* When no installed driver matches the requested platform/automation combination, or the matching
driver cannot be loaded or does not export its class, New Session now returns `session not created`
instead of `unknown error`. The HTTP status remains `500`, and the diagnostic message is preserved.
Validation of missing `platformName`/`appium:automationName` is unchanged
([#22970](https://github.com/appium/appium/pull/22970)).
* A downstream `detached shadow root` error is now preserved as HTTP `404` with that WebDriver
error code instead of being converted to `unknown error` with HTTP `500`
([#22968](https://github.com/appium/appium/pull/22968)).
* Supplied WebDriver error `data` (e.g. alert text) is preserved in driver and proxy responses;
absent data remains omitted
([#22974](https://github.com/appium/appium/pull/22974)).
* With an empty base path, an unregistered `GET /` now returns a WebDriver JSON `unknown command`
response with HTTP `404` instead of Express's HTML 404 page. Extensions can still register their
own root route ([#22969](https://github.com/appium/appium/pull/22969)).
* Driver commands rejecting with `null` or `undefined` now produce a WebDriver `unknown error`
response instead of causing the error handler itself to fail
([#22869](https://github.com/appium/appium/pull/22869)).
!!! info "Actions Needed"
Update assertions or retry logic that inspect raw error codes, HTTP status codes, response
headers, or root-path response bodies. Use `GET /status` for server health checks
### Initial Settings and Session Lifecycle
* Initial settings now come from the selected `firstMatch` candidate, with defaults and
`alwaysMatch` settings applied first. Unselected candidates no longer override them, and settings
inside `appium:options` are now honored
([#22882](https://github.com/appium/appium/pull/22882)).
* The new-command idle timeout starts after initial settings and BiDi response setup finish,
preventing a session from expiring during initialization
([#22881](https://github.com/appium/appium/pull/22881)). If post-create initialization fails,
Appium now attempts to delete the newly created session and removes its registration while
preserving the original initialization error
([#22873](https://github.com/appium/appium/pull/22873)).
* Commands still waiting in the queue when their session ends now reject with `invalid session id`
instead of remaining pending indefinitely; their driver methods are not executed
([#22874](https://github.com/appium/appium/pull/22874)).
* A driver cleanup failure during idle-timeout shutdown is now logged instead of becoming an
unhandled rejection that can terminate the server and unrelated sessions
([#22964](https://github.com/appium/appium/pull/22964)).
!!! info "Actions Needed"
Put initial settings in `alwaysMatch` or the intended `firstMatch` candidate. Handle
`invalid session id` for queued requests when a session ends, and make driver cleanup safe
to call after a partially completed initialization
### BiDi Subscription Scope and Event Delivery
Subscriptions without `contexts` now receive events carrying a context, and module subscriptions
such as `events: ["log"]` now match events such as `log.entryAdded`. Overlapping subscriptions
deliver each event only once ([#22883](https://github.com/appium/appium/pull/22883)).
The base implementation of `session.subscribe` now rejects `userContexts` with `unsupported operation`
instead of silently creating an unscoped subscription. Malformed `userContexts`, or supplying both
`contexts` and `userContexts`, produce `invalid argument`. Drivers implementing user-context support
can override `bidiSubscribe` ([#22877](https://github.com/appium/appium/pull/22877)).
!!! info "Actions Needed"
Use `contexts` for browsing-context scope, or a driver that implements `userContexts` for
user-context scope. Review event-count assertions and workarounds for previously missing events
### App Downloads and IPv6 Connections
Percent-encoded credentials in `appium:app` URLs are now decoded once before Basic authentication,
and complete HTTP-compressed downloads are no longer rejected because the decompressed file size
differs from `Content-Length` ([#22887](https://github.com/appium/appium/pull/22887),
[#22886](https://github.com/appium/appium/pull/22886)). IPv6 literals are now bracketed when constructing
URLs, including advertised BiDi WebSocket URLs and execute-driver plugin connections
([#22872](https://github.com/appium/appium/pull/22872),
[#22912](https://github.com/appium/appium/pull/22912),
[#22914](https://github.com/appium/appium/pull/22914)).
!!! info "Actions Needed"
Remove any workarounds for literal percent escapes in download credentials or malformed IPv6 URLs
## Endpoint Changes
The following endpoints have been removed:
* `GET/POST /session/:sessionId/ime/*` (all IME endpoints, legacy JSONWP)
* `GET /session/:sessionId/location`, `POST /session/:sessionId/location` (legacy geolocation, legacy JSONWP)
* `GET /session/:sessionId/network_connection`, `POST /session/:sessionId/network_connection` (legacy MJSONWP)
* `POST /session/:sessionId/receive_async_response` (legacy JSONWP)
* `GET /session/:sessionId/element/:elementId` (undocumented legacy JSONWP stub)
* `GET /session/:sessionId` (deprecated `getSession`; see [below](#getsession-command-removed) for
its replacement)
The `orientation`, `context`/`contexts`, and `rotation` deprecated endpoints are unaffected and
remain available.
## Changes for Driver and Plugin Developers
The following changes primarily affect authors of Appium drivers, plugins, or tools that import
Appium's internal packages directly (`@appium/base-driver`, `@appium/base-plugin`,
`@appium/support`, `@appium/logger`). They do not affect most Appium end users.
### Remaining Legacy (M)JSONWP Support Removed From `@appium/base-driver`
Beyond the removed endpoints above, the internal machinery that translated between the legacy
(M)JSONWP protocol and W3C WebDriver has been removed:
* `ProtocolError` instances no longer carry a `jsonwpCode` property, and error responses no longer
include a legacy numeric `status` code (`errorFromMJSONWPStatusCode`, aliased as `errorFromCode`,
has been removed; use `errorFromW3CJsonCode`)
* Responses no longer duplicate element references under the legacy `ELEMENT` key; only the W3C
`element-6066-11e4-a52e-4f735466cecf` key is present
* `WebDriverProxy` (formerly `JWProxy`, now removed as a name) no longer converts responses from a
downstream automation server speaking legacy MJSONWP; downstream servers must speak W3C WebDriver
* `determineProtocol`, `DriverCore#setProtocolMJSONWP()`, and `DriverCore#isMjsonwpProtocol()` have
been removed; drivers can no longer produce the legacy `{sessionId, status, value}` response shape
* `@appium/base-driver` no longer exports `statusCodes`/`getSummaryByCode` (the `jsonwp-status`
module was removed)
!!! info "Actions Needed"
Ensure any downstream automation server a driver proxies to (via `WebDriverProxy`) speaks pure
W3C WebDriver; update code referencing the removed exports to their W3C equivalents
### `@appium/base-driver` Default Export Removed
`BaseDriver` is no longer exported as `@appium/base-driver`'s default export, and
`basedriver/helpers.js`'s default export has been removed as well. `BaseDriver`'s constructor no
longer takes command-mixin machinery into account, and the unused `BaseDriver.reset()` method has
been removed.
!!! info "Actions Needed"
Replace `import BaseDriver from '@appium/base-driver'` with `import {BaseDriver} from '@appium/base-driver'`
### Drivers Must Support Shared Command Activity Tracking
The idle timer stays suspended through HTTP/BiDi plugin chains, including work after `next()`,
and while proxied BiDi commands await a response. Drivers must use `@appium/base-driver` with
shared command activity tracking; legacy timer implementations are no longer supported
([#22946](https://github.com/appium/appium/pull/22946),
[#22947](https://github.com/appium/appium/pull/22947)).
!!! info "Actions Needed"
Custom `startNewCommandTimeout` implementations must delegate timer creation
to `super.startNewCommandTimeout()` so they respect shared activity. Custom command execution
must delegate to `super.executeCommand()` or wrap the full command in `runWithCommandTimeout`.
### HTTP Server Extracted Into `@appium/http-server`
The HTTP/Express server code was moved from `@appium/base-driver` into the new
`@appium/http-server` package. `@appium/base-driver` no longer exports `server`,
`normalizeBasePath`, `configureServer`, `ServerOpts`, `ConfigureServerOpts`, `ConfigureHttpOpts`,
or `StartServerOpts`, and no longer depends on `express` and related middleware packages.
`appium/driver.js` stopped re-exporting these symbols as well.
!!! info "Actions Needed"
Import the server helpers from `@appium/http-server` instead
### Driver-Specific Capabilities Removed From Base Constraints
The `appium:locale`, `appium:language`, `appium:autoWebview`, `appium:orientation`, and
`appium:udid` capabilities are no longer part of the standard capability constraints, since they
are not generic enough. The deprecated `appium:autoLaunch` capability has been removed as well.
The base driver no longer validates them.
!!! info "Actions Needed"
Drivers that rely on these capabilities must declare them in their own `desiredCapConstraints`
### `@appium/base-plugin` Default Export and Legacy Logger Removed
`BasePlugin` is no longer the default export of `@appium/base-plugin`. `Plugin#logger` has been
removed in favor of `Plugin#log`, and `Plugin#name`/`Plugin#cliArgs` are now read-only.
!!! info "Actions Needed"
* Replace `import BasePlugin from '@appium/base-plugin'` with `import {BasePlugin} from '@appium/base-plugin'`
* Replace `this.logger` with `this.log` in plugin code
### `@appium/logger` Default Export and Dead APIs Removed
`@appium/logger` and `@appium/support` no longer export a default `log`. `AppiumLogger#errorAndThrow`
has been removed in favor of `errorWithException`. The long-disabled, no-op progress/unicode
methods (`enableProgress`, `disableProgress`, `progressEnabled`, `enableUnicode`, `disableUnicode`)
have also been removed.
!!! info "Actions Needed"
* Import the named `log` export instead of the default export
* Replace `errorAndThrow` calls with `errorWithException`
### `driverData` Mechanism Removed
The deprecated `driverData` mechanism (`Core.driverData`, the `driverData` parameter on
`createSession`/`deleteSession`, and the `DriverData` type) has been removed, superseded by
`IAppiumIpc`.
!!! info "Actions Needed"
Drivers overriding `get driverData()` or relying on it for cross-session coordination need to
migrate to `IAppiumIpc`
### `createSession` Multi-Argument Overload Removed
`createSession` no longer accepts the legacy multi-argument form
(`createSession(jwpCaps, reqCaps, w3cCaps)`), a holdover from JSONWP. Only the single
`w3cCapabilities` argument is supported now.
!!! info "Actions Needed"
Update any driver or plugin overriding `createSession` to use the single-argument signature
### `getSession` Command Removed
`BaseDriver.getSession` and `ISessionHandler.getSession` have been removed (see [Endpoint
Changes](#endpoint-changes) for the removed route), along with the `appium:eventTimings`
capability that controlled whether its response included event history.
!!! info "Actions Needed"
Use `GET /session/:sessionId/appium/capabilities` to retrieve session capabilities, and
`POST /session/:sessionId/appium/events` to retrieve event history (now returned unconditionally)
### `@appium/support` Deprecated APIs Removed
The following `@appium/support` exports have been removed: `process` (`getProcessIds`,
`killProcess`), `mkdirp`, `imageUtil` (`requireSharp`, `cropBase64Image`), `mjpeg` (`MJpegStream`),
`net`'s FTP upload support, `util`'s `uuidV1`/`uuidV3`/`uuidV5`/`localIp`/`cancellableDelay`/`multiResolve`,
`fs`'s `readPackageJsonFrom`/`findRoot`/`F_OK`/`R_OK`/`W_OK`/`X_OK`, `npm`/`NPM` (moved into the
`appium` package), and `env` (moved into the `appium` package). `node.requirePackage()` and
`system.macOsxVersion()` have also been removed. `fs.glob()` is now backed by Node's native
`fs.promises.glob` instead of the `glob` package, with a narrower `GlobOptions` shape.
!!! info "Actions Needed"
None for typical drivers/plugins, which don't use these low-level helpers directly. If you do,
switch to the suggested replacement or a native Node.js API
### Filesystem and IPC Fixes
* `@appium/support`'s `fs.mv()` now preserves symlinks inside moved directories and moves source
symlinks themselves without moving their targets' contents. Dangling source links are supported,
and Windows case-only renames update the entry's spelling
([#22880](https://github.com/appium/appium/pull/22880),
[#22954](https://github.com/appium/appium/pull/22954),
[#22958](https://github.com/appium/appium/pull/22958)).
* `fs.walkDir()` now continues after walker errors such as `EACCES` and `ELOOP` instead of hanging.
These errors are logged and affected entries skipped; `ENOENT` still rejects the returned promise
([#22956](https://github.com/appium/appium/pull/22956)).
* Both `ipc.unsubscribe(topic, subscriber)` and `subscription.unsubscribe()` now finish waiting
async iterators and notify observers once. An observer that throws still propagates its error
to the caller, but no longer prevents waiting iterators from finishing
([#22955](https://github.com/appium/appium/pull/22955)).
!!! info "Actions Needed"
If you intend to move a symlink's target, resolve it explicitly before calling `fs.mv()`.
A completed directory walk may have skipped entries; check warnings if completeness matters.
Allow IPC consumers to finish when unsubscribed
### System Unzip Removed From `@appium/support`
`extractAllTo()` always uses the built-in JS extractor now, so the `useSystemUnzip` option has been
removed. The extractor supports only stored and deflate entries and verifies the CRC32 of every
entry. File names that are not flagged as UTF-8 by the archive are decoded as `utf8` by default, and
the legacy `cp437` decoding is no longer supported. Such names get corrupted, and different names
may collapse into the same one, so that entries overwrite each other.
!!! info "Actions Needed"
Remove `useSystemUnzip` from `extractAllTo()` calls. Archives with `cp437` file names must be
recreated with UTF-8 names (or converted) before extraction. Other single-byte encodings
supported by Node.js can be selected via `fileNamesEncoding`. Archives with encrypted entries or
other compression methods are rejected with `ZipStreamUnsupportedError`
### Legacy Test Pages Removed From `@appium/base-driver`
The built-in test fixture routes (`/welcome`, `/test/guinea-pig*`, `/produce_error`, `/crash`),
the `STATIC_DIR` export, and the `APPIUM_ENABLE_LEGACY_TEST_PAGES` environment variable have been
removed.
!!! info "Actions Needed"
Driver test suites that relied on these fixtures should host their own local test server
## New Features
* [`--app-url-rules`](./security.md#restricting-remote-app-urls) lets server operators restrict
which hosts/IPs an `appium:app` URL is allowed to be downloaded from (allow/deny lists, HTTPS-only,
credentials, redirect limits)
* Definitions for all current [WebDriver BiDi](https://w3c.github.io/webdriver-bidi/) commands are
now included for introspection/validation purposes
* `ExecuteMethodMap` type declarations now check `params` against the target method's signature at
compile time, catching mismatched required/optional execute method parameters in driver code
* The [Universal XML plugin](../reference/api/plugins.md) provides the `universalXml: setEnabled` and
`universalXml: isEnabled` execute methods to toggle the translation during a session, e.g. to
retrieve the original page source
* Extension manifests and `extensions.yaml` are now validated with `ajv`/JSON Schema; a corrupted
`extensions.yaml` now resets to an empty manifest with a warning instead of crashing later with a
confusing error
* `appium driver`/`appium plugin` CLI commands now lock the extension manifest while running,
preventing concurrent processes from silently clobbering each other's changes
* The `install` CLI command now attempts to search for and install the newest compatible extension
version, if the latest version has an incompatible Appium server requirement
+115
View File
@@ -85,3 +85,118 @@ Turn on multiple insecure features for specific drivers:
```bash
appium --allow-insecure=uiautomator2:adb_shell,xcuitest:get_server_logs
```
## Restricting Remote App URLs
Several drivers accept a URL instead of a local path in capabilities such as `appium:app`, in which
case the Appium server downloads the application package from that URL before starting the session.
By default, any `http:` or `https:` URL is accepted. Server hosts who want to control which URLs
their server is allowed to download from can do so with the `--app-url-rules` argument (or the
`server.app-url-rules` property in a [configuration file](./config.md)). Its value is a JSON object
(or a path to a JSON file) with any of the following properties:
|<div style="width:10em">Rule</div>|Description|Default|
|----------------------------------|-----------|-------|
|`allow`|List of hostname patterns, IP addresses, or CIDR subnets. If non-empty, a hostname or its resolved address must match _at least one_|`[]`|
|`deny`|List of hostname patterns, IP addresses, or CIDR subnets. A matching hostname or resolved address is rejected, even if an `allow` rule also matches|`[]`|
|`httpsOnly`|Only accept `https:` URLs|`false`|
|`allowCredentials`|Accept URLs containing a username and/or password (e.g. `https://user:pass@host/app.apk`)|`true`|
|`maxRedirects`|Maximum number of HTTP redirects to follow while downloading. Set to `0` to reject any redirect|`21`|
Hostnames are normalized (lowercased, converted to Punycode, trailing dot removed) before matching
and resolved on every download. Address rules apply to literal addresses and all dynamically
resolved IPv4/IPv6 addresses. The rules are also applied to every redirect, so redirects cannot be
used to escape them. If a URL violates any rule, the session is not created and a generic error
is returned. The violated rule is deliberately neither reported to the client nor written to the
server log, since server logs are often handed over to clients as well. URL schemes, ports, paths,
queries, and fragments are not matched.
Hostname patterns use the [picomatch](https://github.com/micromatch/picomatch#globbing-features)
glob syntax and are matched against the whole hostname:
|Pattern|Matches|
|-------|-------|
|`artifacts.example.com`|Exactly this hostname|
|`*.example.com`|Any subdomain of `example.com` (including nested ones, like `a.b.example.com`), but not `example.com` itself|
|`*example.com`|Any hostname ending with `example.com`, including `example.com` itself|
|`apps.{staging,prod}.example.com`|`apps.staging.example.com` and `apps.prod.example.com`|
|`build-?.example.com`|`build-1.example.com`, `build-a.example.com`, etc.|
|`node[0-9].example.com`|`node0.example.com` to `node9.example.com`|
IP address rules match a single address (`192.0.2.10`, `2001:db8::1`) or a whole subnet in CIDR
notation (`10.0.0.0/8`, `2001:db8::/32`). IPv4 rules also match IPv4-mapped IPv6 addresses
(e.g. `::ffff:10.1.2.3`).
!!! note
These rules only apply to URLs the server downloads via the standard app configuration helper,
which is used for `appium:app` and similar capabilities by all official drivers. Drivers may
define custom capabilities with URLs that are processed differently and thus are not covered.
!!! note
If the server downloads applications through an HTTP proxy (configured via the `HTTP_PROXY`,
`HTTPS_PROXY` and `NO_PROXY` environment variables), the proxy resolves the destination host
itself, so rules containing IP addresses or subnets cannot be enforced for proxied downloads.
Such downloads are rejected; either exclude the download hosts from proxying via `NO_PROXY`
or only use hostname-based rules.
### Examples
Only allow HTTPS downloads from an internal artifact server, without credentials in the URL and
without following redirects:
```bash
appium --app-url-rules='{"allow": ["artifacts.example.com", "10.0.0.0/8"], "httpsOnly": true, "allowCredentials": false, "maxRedirects": 0}'
```
The same configuration as part of a config file:
```yaml
server:
app-url-rules:
allow:
- 'artifacts.example.com'
- '10.0.0.0/8'
httpsOnly: true
allowCredentials: false
maxRedirects: 0
```
Allow downloads from any subdomain of `example.com` and from two IPv6 subnets, except from the
staging environment:
```yaml
server:
app-url-rules:
allow:
- '*.example.com'
- '2001:db8:1::/48'
- '2001:db8:2::/48'
deny:
- '*.staging.example.com'
```
Allow downloads from anywhere, except from the server itself, private networks, link-local
addresses (such as cloud metadata services), and any `.internal` hostname:
```yaml
server:
app-url-rules:
deny:
- 'localhost'
- '*.internal'
- '127.0.0.0/8'
- '10.0.0.0/8'
- '172.16.0.0/12'
- '192.168.0.0/16'
- '169.254.0.0/16'
- '::1/128'
- 'fc00::/7'
- 'fe80::/10'
```
The rules in the last example are applied to the addresses each hostname resolves to, so they
cannot be bypassed via DNS. Note that the `deny` list also blocks any download from the server's
own private network, so a private artifact server must be excluded from the `deny` rules in order
to work.
+6 -7
View File
@@ -2,7 +2,7 @@
hide:
- toc
title: SSL/TLS/SPDY Support
title: SSL/TLS Support
---
Appium 2.2 introduces the option to start the Appium server with SSL/TLS support.
@@ -19,15 +19,14 @@ Both arguments must be provided and should contain paths to a valid
[X509 PEM](https://www.ssl.com/guide/pem-der-crt-and-cer-x-509-encodings-and-conversions/)
certificate and its corresponding private key.
After the server is started use the `https` protocol and a client supporting SSL/TLS or
[SPDY](https://en.wikipedia.org/wiki/SPDY) to communicate to it.
After the server is started use the `https` protocol and a client supporting SSL/TLS to
communicate to it.
### Supported Features
Once a secure server socket is established it supports the following protocols:
`['h2', 'spdy/3.1', 'spdy/3', 'spdy/2', 'http/1.1', 'http/1.0']`. See
[the SPDY node module documentation](https://www.npmjs.com/package/spdy) to get more details about
its features. All insecure client connections will be rejected by the server.
Once a secure server socket is established it uses Node.js' native
[`https`](https://nodejs.org/api/https.html) module to serve connections. All insecure client
connections will be rejected by the server.
### Self-Signed Certificates
+10
View File
@@ -39,6 +39,16 @@ So, for example, the `Find Element` command corresponds to an HTTP `POST` reques
endpoint `/session/:sessionid/element` (where in this case, `:sessionid` is a placeholder for the
unique session ID generated by the server in a previous call to `Create Session`).
If the client disconnects before Appium finishes sending a `Create Session` response and the
request has no `X-Idempotency-Key`, Appium deletes the new session once driver setup completes.
Setup is not interrupted. With an accepted idempotency key, a retry that arrives before setup
finishes waits for the same session ID, or the same error if setup failed. If the original client
and all waiting retries disconnect before setup finishes, Appium deletes the new session and
discards its cached result; a later retry starts a new session. Otherwise, Appium keeps the session
and caches its response for replay while it remains in the bounded, in-memory idempotency cache.
Disconnecting after a response was sent, or during a command in an existing session, does not
automatically end that session. Use `Delete Session` when you are done.
This information is primarily useful for people developing technology that works with the WebDriver
spec. It's not particularly useful for people trying to write Appium or Selenium tests. When you
write an Appium test, you want to use a programming language you're familiar with. Luckily, there
+1 -1
View File
@@ -19,7 +19,7 @@ Appium, called `BaseDriver`. You could have something very close to a "working"
very simple lines of code:
```js
import BaseDriver from '@appium/base-driver'
import {BaseDriver} from '@appium/base-driver'
class MyNewDriver extends BaseDriver {
}
@@ -8,15 +8,14 @@ title: Write a Test (.NET)
The [Appium .NET Client](https://github.com/appium/dotnet-client/) is
an official Appium client in C#. This driver is an extension of the Selenium C# client. It has all the functionalities of the regular driver, but add Appium-specific methods on top of this. The driver is available on the public NuGet Gallery as [Appium.WebDriver](https://www.nuget.org/packages/Appium.WebDriver/).
Now, we get inside the directory and create a new [NUnit](https://nunit.org/) project. We will also add the references to the Appium.Net driver, and other dependencies.
Now, create a new [NUnit](https://nunit.org/) project. We will also add the references to the Appium.Net driver, and other dependencies.
```bash
cd dotnet-client
dotnet new nunit --name appiumtest
cd appiumtest
# This will install the latest 5.x version
# This will install the latest version
dotnet add package Appium.WebDriver --prerelease
dotnet add package Newtonsoft.Json --version 13.0.3
```
@@ -47,7 +46,7 @@ public class Tests
driverOptions.AddAdditionalAppiumOption("appPackage", "com.android.settings");
driverOptions.AddAdditionalAppiumOption("appActivity", ".Settings");
// NoReset assumes the app com.google.android is preinstalled on the emulator
// NoReset assumes the app com.android.settings is preinstalled on the emulator
driverOptions.AddAdditionalAppiumOption("noReset", true);
_driver = new AndroidDriver(serverUri, driverOptions, TimeSpan.FromSeconds(180));
@@ -106,6 +106,48 @@ Retrieves the [execute methods](../../guides/execute-methods.md) supported in th
origin (driver-specific or plugin-specific). Refer to [the type definition file](https://github.com/appium/appium/blob/master/packages/types/lib/command.ts)
for a detailed structure of this object.
### getCurrentAppiumContext
```
GET /session/:sessionId/appium/context
```
Retrieves the active application context.
#### Response
`string` - the name of the active context
### setAppiumContext
```
POST /session/:sessionId/appium/context
```
Sets an application context as the active context.
#### Parameters
|Name|Description|Type|
|--|--|--|
|`name`|Name of the context to set as the active one|string|
#### Response
`null`
### getAppiumContexts
```
GET /session/:sessionId/appium/contexts
```
Retrieves all available application contexts.
#### Response
`string[]` - the names of available contexts
### getLogEvents
```
@@ -392,3 +434,71 @@ Retrieves data from a directory on the device under test.
#### Response
`string` - the Base64-encoded zip file of the directory contents
### getAppiumRotation
```
GET /session/:sessionId/appium/device/rotation
```
Retrieves the current spatial orientation of the device under test.
#### Response
`Rotation` - an object with the following properties:
|Name|Description|Type|
|--|--|--|
|`x`|Degrees by which the device is rotated on its X axis|number|
|`y`|Degrees by which the device is rotated on its Y axis|number|
|`z`|Degrees by which the device is rotated on its Z axis|number|
### setAppiumRotation
```
POST /session/:sessionId/appium/device/rotation
```
Sets the spatial orientation of the device under test.
#### Parameters
|Name|Description|Type|
|--|--|--|
|`x`|Degrees by which the device is rotated on its X axis|number|
|`y`|Degrees by which the device is rotated on its Y axis|number|
|`z`|Degrees by which the device is rotated on its Z axis|number|
#### Response
`null`
### getAppiumOrientation
```
GET /session/:sessionId/appium/device/orientation
```
Retrieves the current orientation of the device under test.
#### Response
`string` - either `PORTRAIT` or `LANDSCAPE`
### setAppiumOrientation
```
POST /session/:sessionId/appium/device/orientation
```
Sets the orientation of the device under test.
#### Parameters
|Name|Description|Type|
|--|--|--|
|`orientation`|New device orientation. Supported values are `PORTRAIT` or `LANDSCAPE`.|string|
#### Response
`null`
+10 -5
View File
@@ -46,7 +46,7 @@ Subscribes to one or more BiDi events.
#### Response
`null`
`{subscription: string}` — a unique subscription ID that can be passed to `session.unsubscribe`.
### bidiUnsubscribe
@@ -56,15 +56,20 @@ session.unsubscribe
> WebDriver BiDi documentation: [session.unsubscribe](https://w3c.github.io/webdriver-bidi/#command-session-unsubscribe)
Unsubscribes from one or more BiDi events.
Unsubscribes using previously returned subscription IDs, or the legacy event/context form.
Removing a subscription ID preserves any overlapping subscriptions.
#### Parameters
|Name|Description|Type|Default|
|--|--|--|--|
|`contexts?`|Contexts in which to unsubscribe from the specified events. By default, the global scope is applied.|string[]|`['']`|
|`events`|Names of events to unsubscribe from|string[]||
|`subscriptions?`|Non-empty list of subscription IDs returned by `session.subscribe`. Cannot be combined with `events` or `contexts`.|string[]||
|`contexts?`|Legacy form only: contexts in which to unsubscribe from the specified events. By default, the global scope is applied.|string[]|`['']`|
|`events?`|Legacy form: non-empty list of event names to unsubscribe from. Required when `subscriptions` is omitted.|string[]||
All subscription IDs must be known to the current session. An unknown ID causes an `invalid argument`
error without removing any subscriptions. Subscription IDs are cleared when the session is deleted.
#### Response
`null`
`{}`
+8 -169
View File
@@ -10,125 +10,6 @@ title: JSON Wire Protocol
The following is a list of legacy [JSON Wire Protocol (JSONWP)](https://www.selenium.dev/documentation/legacy/json_wire_protocol/)
endpoints supported in Appium.
### getSession
```
GET /session/:sessionId
```
> JSONWP documentation: [/session/:sessionId](https://www.selenium.dev/documentation/legacy/json_wire_protocol/#sessionsessionid)
Retrieves the capabilities of the current session.
Appium implements a modified version of this endpoint. If the `appium:eventTimings` capability is
set to `true`, the returned result will additionally include the `events` key, whose value contains
the event history and timings.
!!! warning "Deprecated"
For retrieving capabilities, please use [`getAppiumSessionCapabilities`](./appium.md#getappiumsessioncapabilities).<br />
For retrieving event history, please use [`getLogEvents`](./appium.md#getlogevents).
#### Response
`Capabilities` - an object containing the session capabilities, and the event history (if applicable)
### availableIMEEngines
```
GET /session/:sessionId/ime/available_engines
```
> JSONWP documentation: [/session/:sessionId/ime/available_engines](https://www.selenium.dev/documentation/legacy/json_wire_protocol/#sessionsessionidimeavailable_engines)
Retrieves all IME (input method editor) engines available on the device under test.
!!! warning "Deprecated"
In the future, this endpoint will be moved to the [UiAutomator2 and Espresso drivers](../../ecosystem/drivers.md)
#### Response
`string[]` - a list of available IME engines
### getActiveIMEEngine
```
GET /session/:sessionId/ime/active_engine
```
> JSONWP documentation: [/session/:sessionId/ime/active_engine](https://www.selenium.dev/documentation/legacy/json_wire_protocol/#sessionsessionidimeactive_engine)
Retrieves the name of the active IME engine.
!!! warning "Deprecated"
In the future, this endpoint will be moved to the UiAutomator2 and Espresso drivers
#### Response
`string` - the name of the active IME engine
### isIMEActivated
```
GET /session/:sessionId/ime/activated
```
> JSONWP documentation: [/session/:sessionId/ime/activated](https://www.selenium.dev/documentation/legacy/json_wire_protocol/#sessionsessionidimeactivated)
Determines if IME input is available and active.
!!! warning "Deprecated"
In the future, this endpoint will be moved to the UiAutomator2 and Espresso drivers
#### Response
`boolean` - `true` if IME is active, otherwise `false`
### deactivateIMEEngine
```
POST /session/:sessionId/ime/deactivate
```
> JSONWP documentation: [/session/:sessionId/ime/deactivate](https://www.selenium.dev/documentation/legacy/json_wire_protocol/#sessionsessionidimedeactivate)
Deactivates the currently active IME engine.
!!! warning "Deprecated"
In the future, this endpoint will be moved to the UiAutomator2 and Espresso drivers
#### Response
`null`
### activateIMEEngine
```
POST /session/:sessionId/ime/activate
```
> JSONWP documentation: [/session/:sessionId/ime/activate](https://www.selenium.dev/documentation/legacy/json_wire_protocol/#sessionsessionidimeactivate)
Activates an IME engine.
!!! warning "Deprecated"
In the future, this endpoint will be moved to the UiAutomator2 and Espresso drivers
#### Parameters
|Name|Description|Type|
|--|--|--|
|`engine`|Name of the IME engine to activate|string|
#### Response
`null`
### getOrientation
```
@@ -139,6 +20,10 @@ GET /session/:sessionId/orientation
Retrieves the current orientation of the device under test.
!!! warning "Deprecated"
Please use [`getAppiumOrientation`](./appium.md#getappiumorientation) instead
#### Response
`string` - either `PORTRAIT` or `LANDSCAPE`
@@ -153,6 +38,10 @@ POST /session/:sessionId/orientation
Sets the orientation of the device under test.
!!! warning "Deprecated"
Please use [`setAppiumOrientation`](./appium.md#setappiumorientation) instead
#### Parameters
|Name|Description|Type|
@@ -162,53 +51,3 @@ Sets the orientation of the device under test.
#### Response
`null`
### getGeoLocation
```
GET /session/:sessionId/location
```
> JSONWP documentation: [/session/:sessionId/location](https://www.selenium.dev/documentation/legacy/json_wire_protocol/#sessionsessionidlocation)
Retrieves the current location of the device under test.
!!! warning "Deprecated"
Please use driver-specific extension methods such as `mobile: getGeoLocation` or
`mobile: getSimulatedLocation`
#### Response
`Location` - an object with the following properties:
|Name|Description|Type|
|--|--|--|
|`altitude`|Altitude of the device location|number|
|`latitude`|Latitude of the device location|number|
|`longitude`|Longitude of the device location|number|
### setGeoLocation
```
POST /session/:sessionId/location
```
> JSONWP documentation: [/session/:sessionId/location](https://www.selenium.dev/documentation/legacy/json_wire_protocol/#sessionsessionidlocation)
Sets the current location of the device under test.
!!! warning "Deprecated"
Please use driver-specific extension methods such as `mobile: setGeoLocation` or
`mobile: setSimulatedLocation`
#### Parameters
|Name|Description|Type|
|--|--|--|
|`location`|New device latitude, longitude and altitude|[`Location`](#response_6)|
#### Response
`null`
@@ -20,6 +20,10 @@ GET /session/:sessionId/rotation
Retrieves the current spatial orientation of the device under test.
!!! warning "Deprecated"
Please use [`getAppiumRotation`](./appium.md#getappiumrotation) instead
#### Response
`Rotation` - an object with the following properties:
@@ -40,6 +44,10 @@ POST /session/:sessionId/rotation
Sets the spatial orientation of the device under test.
!!! warning "Deprecated"
Please use [`setAppiumRotation`](./appium.md#setappiumrotation) instead
#### Parameters
|Name|Description|Type|
@@ -62,6 +70,10 @@ GET /session/:sessionId/context
Retrieves the active application context.
!!! warning "Deprecated"
Please use [`getCurrentAppiumContext`](./appium.md#getcurrentappiumcontext) instead
#### Response
`string` - the name of the active context
@@ -76,6 +88,10 @@ POST /session/:sessionId/context
Sets an application context as the active context.
!!! warning "Deprecated"
Please use [`setAppiumContext`](./appium.md#setappiumcontext) instead
#### Parameters
|Name|Description|Type|
@@ -96,56 +112,10 @@ GET /session/:sessionId/contexts
Retrieves all available application contexts.
!!! warning "Deprecated"
Please use [`getAppiumContexts`](./appium.md#getappiumcontexts) instead
#### Response
`string[]` - the names of available contexts
### getNetworkConnection
```
GET /session/:sessionId/network_connection
```
> MJSONWP documentation: [Device Modes](https://github.com/SeleniumHQ/mobile-spec/blob/master/spec-draft.md#device-modes)
Retrieves the current state of network types (data, Wi-Fi, airplane mode).
!!! warning "Deprecated"
Please use driver-specific extension methods such as `mobile: getConnectivity`
#### Response
`NetworkConnectionState` - a number indicating the current network state:
|Value|Data|Wi-Fi|Airplane Mode|
|--|--|--|--|
|`0`|OFF|OFF|OFF|
|`1`|OFF|OFF|ON|
|`2`|OFF|ON|OFF|
|`4`|ON|OFF|OFF|
|`6`|ON|ON|OFF|
### setNetworkConnection
```
POST /session/:sessionId/network_connection
```
> MJSONWP documentation: [Device Modes](https://github.com/SeleniumHQ/mobile-spec/blob/master/spec-draft.md#device-modes)
Sets the state of network types (data, Wi-Fi, airplane mode).
!!! warning "Deprecated"
Please use driver-specific extension methods such as `mobile: setConnectivity`
#### Parameters
|<div style="width:6em">Name</div>|Description|<div style="width:18em">Type</div>|
|--|--|--|
|`parameters`|Object containing the `type` key, whose value is the desired network state|`{"type": `[`NetworkConnectionState`](#response_5)`}`|
#### Response
[`NetworkConnectionState`](#response_5) - the new network state
@@ -169,10 +169,17 @@ Modifies the [`createSession`](./webdriver.md#createsession) endpoint:
All endpoints for this plugin can be invoked without creating a session, allowing you to prepare
your test environment in advance.
!!! info
Prior to plugin version 1.2.0, all endpoints below were mounted under the `/storage` prefix
(e.g. `/storage/add`) instead of `/appium/storage`. The `/storage`-prefixed routes are still
available for backward compatibility, but are deprecated and will be removed in a future
version of the plugin.
### addStorageItem
```
POST /storage/add
POST /appium/storage/add
```
Adds a new file to the storage.
@@ -198,8 +205,8 @@ Example:
```json
{
"ws": {
"stream": "/storage/add/ccc963411b2621335657963322890305ebe96186/stream",
"events": "/storage/add/ccc963411b2621335657963322890305ebe96186/events"
"stream": "/appium/storage/add/ccc963411b2621335657963322890305ebe96186/stream",
"events": "/appium/storage/add/ccc963411b2621335657963322890305ebe96186/events"
},
"ttlMs": 300000
}
@@ -208,7 +215,7 @@ Example:
### deleteStorageItem
```
POST /storage/delete
POST /appium/storage/delete
```
Deletes a file in the storage.
@@ -227,7 +234,7 @@ the storage, or if the delete request is invalid.
### listStorageItems
```
GET /storage/list
GET /appium/storage/list
```
List all files present in the storage.
@@ -245,7 +252,7 @@ List all files present in the storage.
### resetStorage
```
POST /storage/reset
POST /appium/storage/reset
```
Deletes all uploaded files and stops any incomplete uploads.
@@ -258,6 +265,43 @@ preserved, and only the incomplete uploads will be stopped.
## Universal XML Plugin
Translation is on by default. It can be turned off and on during a session with the
[execute methods](../../guides/execute-methods.md) below, and applies only to the current session.
### execute
```
POST /session/:sessionId/execute/sync
```
Modifies the [`execute`](./webdriver.md#execute) endpoint:
* Adds the `universalXml: setEnabled` and `universalXml: isEnabled` execute methods. Any other
script is passed on unchanged
#### universalXml: setEnabled
Turns translation on or off for the current session. While it is off, `findElement`,
`findElements` and `getPageSource` are passed on unchanged.
##### Parameters
|Name|Description|Type|
|--|--|--|
|`enabled`|Whether node/attribute names should be translated|boolean|
##### Response
`null`
#### universalXml: isEnabled
Returns whether translation is on for the current session.
##### Response
`boolean` - `true` if translation is on
### findElement
```
@@ -25,18 +25,11 @@ POST /session
Creates a new WebDriver session.
Appium implements a modified version of this endpoint for historical reasons. While the W3C
endpoint only accepts 1 parameter, Appium's implementation allows up to 3 parameters, as this was
required by the legacy JSON Wire Protocol (JSONWP). Since Appium 2, the JSONWP format is no longer
supported, and any of the 3 parameters can be used to specify the W3C capabilities.
#### Parameters
|Name|Description|Type|
|--|--|--|
|`w3cCapabilities1?`|Capabilities of the new session|`W3CDriverCaps`|
|`w3cCapabilities2?`|Another location for the new session capabilities (legacy)|`W3CDriverCaps`|
|`w3cCapabilities?`|Another location for the new session capabilities (legacy)|`W3CDriverCaps`|
|`capabilities?`|Capabilities of the new session|`W3CDriverCaps`|
#### Response
@@ -91,14 +84,19 @@ GET /session/:sessionId/timeouts
Retrieves the timeout values of the current session.
Appium implements a modified version of this endpoint, by also returning the value of the `command`
timeout for supported drivers and contexts.
#### Response
`GetTimeoutsResult` - an object with the following properties:
|Name|Description|Type|
|--|--|--|
|`command`|Command timeout|number|
|`implicit`|Implicit wait timeout|number|
|`script`|Script timeout|number or null|
|`pageLoad`|Page load timeout|number or null|
|`implicit`|Implicit wait timeout|number or null|
|`command`|Appium command timeout|number|
### timeouts
@@ -110,13 +108,17 @@ POST /session/:sessionId/timeouts
Sets the timeout values of the current session.
Appium implements a modified version of this endpoint by adding the optional `command` parameter
for supported drivers and contexts.
#### Parameters
|Name|Description|Type|
|--|--|--|
|`implicit?`|Implicit wait timeout (in milliseconds)|number|
|`pageLoad?`|Page load timeout (in milliseconds)|number|
|`script?`|Script timeout (in milliseconds)|number|
|`script?`|Script timeout (in milliseconds)|number or null|
|`implicit?`|Implicit wait timeout (in milliseconds)|number or null|
|`pageLoad?`|Page load timeout (in milliseconds)|number or null|
|`command?`|Appium command timeout (in milliseconds)|number|
#### Response
@@ -13,6 +13,12 @@ Provides management options for a specific extension (driver or plugin). Both th
The following sub-subcommands are supported: `doctor`, `install`, `list`, `run`, `update`,
and `uninstall`.
For `doctor`, `run`, `update`, and `uninstall`, you can identify an installed extension by its
short name or its exact npm package name. For example, `appium driver uninstall uiautomator2`
and `appium driver uninstall appium-uiautomator2-driver` refer to the same installed driver.
Short names take precedence if a name also matches another extension's package name. Package names
can include a scope, such as `@appium/fake-driver`, but must not include a version or tag suffix.
## `doctor`
Runs doctor checks for an installed extension, which validate whether the extension has its prerequisites
@@ -31,7 +37,7 @@ appium {driver|plugin} doctor <extension-name>
|Argument|Description|
|--|--|
|`extension-name`|The short name of the installed extension|
|`extension-name`|The short name or exact npm package name of the installed extension|
#### Options
@@ -158,13 +164,13 @@ extensions include scripts.
#### Usage
```
appium {driver|plugin} run <extension-name> <script-name> [script-args]
appium {driver|plugin} run <extension-name> [<script-name> [<script-args>]]
```
|Argument|Description|
|--|--|
|`extension-name`|The short name of the installed extension|
|`script-name`|The name of the script to be run|
|`extension-name`|The short name or exact npm package name of the installed extension|
|`script-name`|The name of the script to run. If not provided, a list of available scripts is returned.|
|`script-args`|Any additional arguments passed to the script|
#### Options
@@ -181,6 +187,12 @@ appium {driver|plugin} run <extension-name> <script-name> [script-args]
appium driver run uiautomator2 reset
```
- List all available scripts included in the XCUITest driver:
```
appium driver run xcuitest
```
## `update`
Updates one or more extensions. Only supported for extensions installed via `npm`. By default,
@@ -194,7 +206,7 @@ appium {driver|plugin} update <extension-name>
|Argument|Description|
|--|--|
|`extension-name`|The short name of the installed extension, or `installed` to update all installed extensions|
|`extension-name`|The short name or exact npm package name of the installed extension, or `installed` to update all installed extensions|
#### Options
@@ -229,7 +241,7 @@ appium {driver|plugin} uninstall <extension-name>
|Argument|Description|
|--|--|
|`extension-name`|The short name of the installed extension|
|`extension-name`|The short name or exact npm package name of the installed extension|
#### Options
@@ -14,6 +14,7 @@ they can only be enabled using the wildcard (`*`) prefix:
| Feature Name | Description |
|-------------------|-------------------------------------------------------------------------------|
|`session_discovery`|Allows retrieving the list of active server sessions via `GET /appium/sessions`|
|`cors` |Allows web browser connections to the server from any host |
Of course, Appium drivers and plugins are free to define additional insecure features of their own.
@@ -27,14 +27,12 @@ appium
|<div style="width:15em">Argument</div>|Description|Type|<div style="width:7em">Default</div>|
|--|--|--|--|
|`--address`, `-a`|IPv4/IPv6 address to listen on|string|`0.0.0.0`|
|`--allow-cors`|Allow web browser connections from any host|boolean|`false`|
|`--allow-insecure`|List of [insecure features](../../guides/security.md) that should be allowed in this server's sessions. Individual features can be overridden by `--deny-insecure`. Has no effect in combination with `--relaxed-security`.|array<string>|`[]`|
|`--allow-unknown-args`|Do not exit if unrecognized command-line arguments are passed to the server; ignore them instead. Useful when the Appium CLI is wrapped by external tooling that appends extra flags.|boolean|`false`|
|`--app-url-rules`|Rules that remote application URLs (e.g. in the `appium:app` capability) must satisfy before the server downloads them. See the [Restricting Remote App URLs](../../guides/security.md#restricting-remote-app-urls) guide for details. Can be a path to a JSON file or raw JSON.|object||
|`--base-path`, `-pa`|Base path to use as the prefix for all webdriver routes running on the server|string|`""`|
|`--callback-address`, `-ca`|Callback IP address|string|`0.0.0.0`|
|`--callback-port`, `-cp`|Callback port|integer|`4723`|
|`--config`|Path to an [Appium configuration JSON file](../../guides/config.md)|string||
|`--debug-log-spacing`|Add exaggerated spacing in logs to help with visual inspection|boolean|`false`|
|`--default-capabilities`, `-dc`|Capabilities that will be used for each session, unless overridden by received capabilities|object||
|`--default-capabilities`, `-dc`|Capabilities that will be used for each session, unless overridden by received capabilities. See [Default Capabilities](#default-capabilities) for how they are applied.|object||
|`--deny-insecure`|List of [insecure features](../../guides/security.md) that should be disabled in this server's sessions. Since all insecure features are disabled by default, this argument has no effect without either `--allow-insecure` or `--relaxed-security`, and is applied after both.|array<string>|`[]`|
|`--driver`|Driver-specific configuration. Keys should correspond to driver package names|object||
|`--drivers-import-chunk-size`|Maximum number of drivers that can be imported in parallel on server startup|number|`3`|
@@ -43,7 +41,7 @@ appium
|`--local-timezone`|Use local timezone for log timestamps|boolean|`false`|
|`--log`, `-g`|Path to a file where the server logs should be output. This does not affect output on the console.|string||
|`--log-filters`|List of log filtering rules. See the [log filtering guide](../../guides/log-filters.md) for details.|array||
|`--log-level`|The log level for the server logs. Supported values are `debug`, `info`, `warn`, or `error`. Combining two supported values using a colon (e.g. `warn:debug`) allows to set separate log levels for the console and file outputs, respectively.|string|`debug`|
|`--log-level`|The log level for the server logs. Supported values are `debug`, `info`, `warn`, or `error`. Combining two supported values using a colon (e.g. `warn:debug`) allows to set separate log levels for the console and file outputs, respectively.|string|`info`|
|`--log-format`|The log format of the server logs. Supported values are `text`, `json`, or `pretty_json`. Setting the value to `json` disables colors.|string|`text`|
|`--log-no-colors`|Disable colors in the server log|boolean|`false`|
|`--log-timestamp`|Show timestamps in the server log|boolean|`false`|
@@ -51,7 +49,6 @@ appium
|`--max-ipc-data-size`|Maximum byte size of IPC message objects.|integer|`1048576` (1MB)|
|`--max-ipc-topics`|Maximum number of IPC topics per session.|integer|`1000`|
|`--no-perms-check`|Skip various permission checks on the server startup|boolean|`false`|
|`--nodeconfig`|JSON configuration for registering Appium as a node with Selenium Grid 3|object||
|`--plugin`|Plugin-specific configuration. Keys should correspond to plugin package names|object||
|`--plugins-import-chunk-size`|Maximum number of plugins that can be imported in parallel on server startup|number|`7`|
|`--port`, `-p`|Port to listen on|integer|`4723`|
@@ -60,11 +57,44 @@ appium
|`--shutdown-timeout`|Timeout (in milliseconds) for waiting on all active connections to close, when shutting down the server|number|`5000`|
|`--ssl-cert-path`|Absolute path to the `.cert` file if TLS is used. Must be provided together with `--ssl-key-path`. See the [SSL/TLS/SPDY Support guide](../../guides/tls.md) for details.|string||
|`--ssl-key-path`|Absolute path to the `.key` file if TLS is used. Must be provided together with `--ssl-cert-path`. See the [SSL/TLS/SPDY Support guide](../../guides/tls.md) for details.|string||
|`--strict-caps`|Prevent creation of new client sessions that use unsupported capabilities|boolean|`false`|
|`--tmp`|Absolute path to the directory used for temporary files|string|[`os.tmpdir()`](https://nodejs.org/api/os.html#ostmpdir)|
|`--use-drivers`|List of drivers to activate. By default, all installed drivers are activated.|array<string>|`[]`|
|`--use-plugins`|List of plugins to activate. By default, no plugins are activated. Set to `["all"]` to activate all installed plugins.|array<string>|`[]`|
|`--webhook`, `-G`|URL for an HTTP listener where the server logs should be output. This does not affect output on the console.|string||
|`--webhook`, `-G`|URL for an HTTP listener where the server logs should be output. This does not affect output on the console. A bare `host:port` is also accepted, which posts the logs to the root path over plain http; a value having neither a scheme nor a port falls back to `127.0.0.1:9003`.|string||
### Default Capabilities
Capabilities set with `--default-capabilities` are added to the received capabilities before a
driver is matched. This is Appium's own defaulting behavior, not something defined by the W3C
WebDriver specification.
* A value the client sends in `alwaysMatch`, or in a given `firstMatch` candidate, takes precedence
over the server default
* Defaults fill missing keys in each `firstMatch` candidate separately. A value in one candidate
does not stop the default from being applied to another candidate
* A key sent in both `alwaysMatch` and `firstMatch` is still rejected by the usual capability
validation. Server defaults do not change that
For example, with these server defaults:
```json
{"appium:newCommandTimeout": 60}
```
and these received capabilities:
```json
{
"alwaysMatch": {"appium:deviceName": "Pixel"},
"firstMatch": [
{"platformName": "iOS", "appium:newCommandTimeout": 120},
{"platformName": "Android"}
]
}
```
the iOS candidate keeps `appium:newCommandTimeout` of `120`, and the Android candidate gets the
default `60`. The session uses the value from whichever candidate it is created with.
### Info Options
@@ -32,7 +32,7 @@ appium setup browser
Installs the following extensions for desktop application testing:
* Drivers: `mac2`[^1], `windows`[^2]
* Drivers: `mac2`[^1], `wincore`[^2]
* Plugins: `images`, `inspector`
#### Usage
@@ -32,7 +32,6 @@ default value.
| <div style="width:19em">Capability</div> | Description | Type | Default |
|--|--|--|--|
| `webSocketUrl` | Toggle support of the WebDriver BiDi protocol | `boolean` ||
| `appium:eventTimings` (deprecated) | Toggle collection of [Event Timings](../../guides/event-timing.md). This capability is deprecated - please use the [`getLogEvents`](../api/appium.md#getlogevents) endpoint instead. | `boolean` ||
| `appium:newCommandTimeout` | Number of seconds the Appium server should wait for clients to send commands before stopping the session. A value of `0` disables the timeout. | `number` | `60` |
| `appium:printPageSourceOnFindFailure` | Toggle retrieval of the the page source and its printing to the Appium log, whenever a request to find an element fails | `boolean` ||
@@ -42,15 +41,9 @@ These capabilities are not directly used in the Appium server/base driver, but a
validation checks due to their relevance in other drivers. The actual handling of these
capabilities is optional and entirely driver-dependent.
| Capability | Validation |
|-----------------------------------|---------------------------------------------------------------|
| `platformVersion` | Must be `string` |
| `appium:app` | Must be `string`; empty values are ignored |
| `appium:autoLaunch` (deprecated) | Must be `boolean` |
| `appium:autoWebview` | Must be `boolean` |
| `appium:fullReset` | Must be `boolean`; mutually exclusive with `appium:noReset` |
| `appium:language` | Must be `string` |
| `appium:locale` | Must be `string` |
| `appium:orientation` | Must be either `LANDSCAPE` or `PORTRAIT` |
| `appium:noReset` | Must be `boolean`; mutually exclusive with `appium:fullReset` |
| `appium:udid` | Must be `string` |
| Capability | Validation |
|--------------------------|---------------------------------------------------------------|
| `appium:platformVersion` | Must be `string` |
| `appium:app` | Must be `string`; empty values are ignored |
| `appium:fullReset` | Must be `boolean`; mutually exclusive with `appium:noReset` |
| `appium:noReset` | Must be `boolean`; mutually exclusive with `appium:fullReset` |
@@ -8,11 +8,6 @@ title: Additional Resources
Here you can find links to additional Appium resources around the web:
## Websites
- [Appium Pro](https://appiumpro.com) - a blog and newsletter written by one of Appium's
maintainers, Jonathan Lipps, with lots of useful guides
## Online Courses
- [Appium and Selenium Fundamentals](https://ui.headspin.io/university/learn/appium-selenium-fundamentals-2020) - a comprehensive video course on learning Python, Selenium, and Appium by Jonathan Lipps
@@ -17,3 +17,8 @@ authors:
avatar: 'https://avatars.githubusercontent.com/u/4045041?v=4'
slug: 'sai'
url: 'https://saikrishna.tech'
mykola:
name: 'Mykola Mokhnach'
description: 'Appium Core Maintainer'
avatar: https://avatars.githubusercontent.com/u/7767781?v=4
slug: 'mykola'
@@ -8,11 +8,13 @@ date: 2025-11-25
# Announcing LambdaTest as Appium's Newest Development Partner
As part of our ongoing effort to recognize organizations that meaningfully support the Appium project through dedicated maintainer time, we are excited to welcome our newest Development Partner: LambdaTest!
LambdaTest has long been an active participant in the automated testing ecosystem. Today, they are officially formalizing their commitment by investing people—dedicated engineers who will directly support Appium's development, maintenance, and ecosystem growth.
This kind of hands-on contribution is crucial for ensuring Appium continues to evolve. We deeply value partners who empower their engineering teams to contribute code directly to open source rather than just sponsorship.
<!-- more -->
LambdaTest has long been an active participant in the automated testing ecosystem. Today, they are officially formalizing their commitment by investing people—dedicated engineers who will directly support Appium's development, maintenance, and ecosystem growth.
This kind of hands-on contribution is crucial for ensuring Appium continues to evolve. We deeply value partners who empower their engineering teams to contribute code directly to open source rather than just sponsorship.
<div style="text-align:center;width:100%"><a href="https://lambdatest.com/?utm_source=appium.io&utm_medium=organic&utm_campaign=june_25&utm_term=sk&utm_content=webpage">
<img src="/docs/en/latest/assets/images/sponsor-logo-lambdatest-dark.png#only-dark" width="300" />
<img src="/docs/en/latest/assets/images/sponsor-logo-lambdatest-light.png#only-light" width="300" />
@@ -20,15 +22,19 @@ This kind of hands-on contribution is crucial for ensuring Appium continues to e
</div>
LambdaTest engineers are already contributing actively across several key Appium repositories. Their current focus includes:
**appium-mcp** – Supporting Appium's implementation of the Model Context Protocol (MCP) to drive forward modern AI-assisted capabilities.
**appium-remote-xpc** – Contributing to the native macOS/XPC-based remote execution layer, essential for iOS automation.
**appium-tun-tap** – Assisting in the development and maintenance of network virtualization and tunneling components for advanced testing scenarios.
- [`appium-mcp`](https://github.com/appium/appium-mcp) – Supporting Appium's implementation of the Model Context Protocol (MCP) to drive forward modern AI-assisted capabilities.
- [`appium-ios-remotexpc`](https://github.com/appium/appium-ios-remotexpc) – Contributing to the native macOS/XPC-based remote execution layer, essential for iOS automation.
- [`appium-ios-tuntap`](https://github.com/appium/appium-ios-tuntap) – Assisting in the development and maintenance of network virtualization and tunneling components for advanced testing scenarios.
These contributions strengthen critical infrastructure within the Appium ecosystem and demonstrate LambdaTest's commitment to deep technical involvement in the project's evolution.
About LambdaTest
**About LambdaTest**
LambdaTest’s platform already serves a massive community of Appium users. As a GenAI-powered Quality Engineering Platform, they provide a full-stack testing cloud with 10,000+ real mobile devices and 3,000+ browsers.
Beyond standard infrastructure, they are pushing the boundaries of test execution with AI-native test management and next-generation agents like KaneAI. By supporting Appium alongside Selenium, Playwright, and other major frameworks, LambdaTest helps teams orchestrate intelligent automation at cloud scale.
LambdaTest will continue contributing people, code, and development expertise to strengthen Appium's core functionality. Their commitment represents a significant investment into the future of the framework, and we're thrilled to have them collaborating with us at this deeper level.
The Appium project is proud to recognize LambdaTest as our newest official Development Partner. We can't wait to see what we'll build together!
@@ -0,0 +1,38 @@
---
authors:
- mykola
date: 2026-09-01
---
# Announcing watchOS Simulator Support in the XCUITest Driver
We’re excited to announce that the XCUITest driver now supports automation of watchOS apps — a
platform that’s been on Appium’s requested list for a long time.
<!-- more -->
In order to automate watchOS, you'll need the following:
- XCUITest driver version 12.6.0 or later (WebDriverAgent 16.5.0 or later)
- Xcode 15.4 or later
- watchOS 10 or later
Important to note: ^^the driver currently only works with simulators — real devices are not
compatible.^^
The good news: automating a watchOS app looks just like automating any other iOS/iPadOS app. The
standard use-cases of finding and clicking on elements, retrieving the page source, using the
Appium Inspector — all work as usual. The one exception here is standard W3C gesture/touch actions
(tap/swipe via the Actions API), which are not supported.
On top of that, a few watchOS-specific extensions are available:
- `mobile: pressButton` — press the Digital Crown or Action button
- `mobile: rotateDigitalCrown` — rotate the Digital Crown (since driver version 12.7.0)
- `mobile: performHandGesture` — perform double-tap or wrist flick hand gestures (since driver
version 12.7.0)
For full setup instructions, capabilities, and limitations, check out the
[watchOS guide](https://appium.github.io/appium-xcuitest-driver/latest/guides/watchos/).
Happy testing!
@@ -222,17 +222,24 @@ So you'll probably end up overriding `createSession`. You can do so by defining
driver:
```js
async createSession(jwpCaps, reqCaps, w3cCaps, otherDriverData) {
const [sessionId, caps] = super.createSession(w3cCaps);
async createSession(w3cCaps) {
const [sessionId, caps] = await super.createSession(w3cCaps);
// do your own stuff here
return [sessionId, caps];
}
```
For legacy reasons, your function will receive old-style JSON Wire Protocol desired and required
caps as the first two arguments. Given that the old protocol isn't supported anymore and clients
have all been updated, you can instead only rely on the `w3cCaps` parameter. (For a discussion
about what `otherDriverData` is about, see the section below on concurrent drivers).
!!! warning "Deprecated"
```
Older drivers may still define `createSession` with up to four parameters, e.g.
`createSession(jwpCaps, reqCaps, w3cCaps, otherDriverData)`. This shape is a holdover from the
retired JSON Wire Protocol, which required desired and required caps as the first two
arguments; only the first W3C-shaped argument was ever used. The `otherDriverData` parameter is
likewise deprecated in favor of [IPC](#send-messages-to-plugins-running-on-the-same-session) for
coordinating with other concurrently-running sessions. New drivers should use the single-argument
form shown above.
```
You'll want to make sure to call `super.createSession` in order to get the session ID as well as
the processed capabilities (note that capabilities are also set on `this.caps`; modifying `caps`
@@ -522,14 +529,13 @@ route, then the route will not be proxied and instead will be handled by your dr
example, we are avoiding proxying all `POST` routes that have the `appium` prefix.
Next, we have to set up the proxying itself. The way to do this is to use a special class from
Appium called `JWProxy`. (The name means "JSON Wire Proxy" and is related to a legacy
implementation of the protocol). You'll want to create a `JWProxy` object using the details required to
connect to the remote server:
Appium called `WebDriverProxy`. You'll want to create a `WebDriverProxy` object using the details
required to connect to the remote server:
```js
// import {JWProxy} from 'appium/driver';
// import {WebDriverProxy} from 'appium/driver';
const proxy = new JWProxy({
const proxy = new WebDriverProxy({
server: 'remote.server',
port: 1234,
base: '/',
@@ -541,8 +547,8 @@ this.proxyCommand = proxy.command.bind(proxy);
Here we are creating a proxy object and assigning some of its methods to `this` under the names
`proxyReqRes` and `proxyCommand`. This is required for Appium to use the proxy, so don't forget
this step! The `JWProxy` has a variety of other options which you can check out in the source code,
as well. (TODO: publish options as API docs and link here).
this step! The `WebDriverProxy` has a variety of other options which you can check out in the
source code as well.
Finally, we need a way to tell Appium when the proxy is active. For your driver it might always
be active, or it might only be active when in a certain context. You can define the logic as an
@@ -799,6 +805,14 @@ that multiple simultaneous sessions don't use the same resources:
3. Have each driver express what resources it is using, then examine currently-used resources from
other drivers when a new session begins.
!!! warning "Deprecated"
```
The `driverData`-based mechanism described below is deprecated. Prefer
[IPC](#send-messages-to-plugins-running-on-the-same-session) for coordinating resources across
concurrently-running sessions instead.
```
To support this third strategy, you can implement `get driverData` in your driver to return what
sorts of resources your driver is currently using, for example:
@@ -810,7 +824,7 @@ get driverData() {
Now, when a new session is started on your driver, the `driverData` response from any other
simultaneously running drivers (of the same type) will also be included, as the last parameter of
the `createSession` method:
the deprecated, multi-argument form of the `createSession` method:
```js
async createSession(jwpCaps, reqCaps, w3cCaps, driverData)
@@ -483,9 +483,9 @@ capability.
You probably don't normally need to update the Appium server object (which is an
[Express](https://expressjs.com/) server having already been
[configured](https://github.com/appium/appium/blob/master/packages/base-driver/lib/express/server.js)
in a variety of ways). But, for example, you could add new Express middleware to the server to
support your plugin's requirements. To update the server you must implement the `static async
updateServer` method in your class. This method takes three parameters:
in a variety of ways). But, for example, you could add new routes to the server to support your
plugin's requirements. To update the server you must implement the `static async updateServer`
method in your class. This method takes three parameters:
- `expressApp`: the Express app object
- `httpServer`: the Node HTTP server object
@@ -498,6 +498,25 @@ results you'll need to test! Warning: this should be considered an advanced feat
knowledge of Express, as well as the care not to do anything that could affect the operation of
other parts of the Appium server!
`updateServer` runs _after_ Appium's own routes are registered, so `expressApp.use(...)` here
won't see requests to paths Appium already owns — Express matches middleware and routes in
registration order. For middleware that must see requests to those routes too (e.g. logging or
auth), use `httpServer.frontRouter` instead: Appium mounts this
[Router](https://expressjs.com/en/5x/api/router/) before any route is registered, so anything
attached to it — even from inside `updateServer` — still runs ahead of route matching. Note this
is still _after_ Appium's own baseline middleware (CORS, WebSocket upgrade handling, body
parsing, etc.), so it won't see requests Appium itself has already rejected, e.g. a CORS
preflight failure or a body that failed to parse:
```js
static async updateServer(expressApp, httpServer, cliArgs) {
httpServer.frontRouter.use((req, res, next) => {
console.log(`Incoming request: ${req.method} ${req.url}`);
next();
});
}
```
### Handle unexpected session shutdown
When developing a plugin you may want to add some cleanup logic for when a session ends. You would
+11 -1
View File
@@ -130,7 +130,7 @@ appium driver install --source=npm appium-flutter-integration-driver
appium driver install --source=npm appium-lg-webos-driver
```
### [Linux](https://github.com/fantonglang/appium-linux-driver)
### [Linux (@fantonglang)](https://github.com/fantonglang/appium-linux-driver)
!!! warning
@@ -149,6 +149,16 @@ yarn install
node ./
```
### [Linux (@Itsmeaj)](https://github.com/Itsmeaj/appium)
- Target: Linux desktop applications on X11 and Wayland
- Mode: Native
- Supported by: `@Itsmeaj`
```sh title="Install This Driver"
appium driver install --source=npm @itsmeaj/appium-linux-driver
```
### [NovaWindows](https://github.com/AutomateThePlanet/appium-novawindows-driver)
!!! info
@@ -107,6 +107,48 @@ Retrieves the [execute methods](../../guides/execute-methods.md) supported in th
origin (driver-specific or plugin-specific). Refer to [the type definition file](https://github.com/appium/appium/blob/master/packages/types/lib/command.ts)
for a detailed structure of this object.
### getCurrentAppiumContext
```
GET /session/:sessionId/appium/context
```
Retrieves the active application context.
#### Response
`string` - the name of the active context
### setAppiumContext
```
POST /session/:sessionId/appium/context
```
Sets an application context as the active context.
#### Parameters
| Name | Description | Type |
| ------ | -------------------------------------------- | ------ |
| `name` | Name of the context to set as the active one | string |
#### Response
`null`
### getAppiumContexts
```
GET /session/:sessionId/appium/contexts
```
Retrieves all available application contexts.
#### Response
`string[]` - the names of available contexts
### getLogEvents
```
@@ -393,3 +435,71 @@ Retrieves data from a directory on the device under test.
#### Response
`string` - the Base64-encoded zip file of the directory contents
### getAppiumRotation
```
GET /session/:sessionId/appium/device/rotation
```
Retrieves the current spatial orientation of the device under test.
#### Response
`Rotation` - an object with the following properties:
| Name | Description | Type |
| ---- | ---------------------------------------------------- | ------ |
| `x` | Degrees by which the device is rotated on its X axis | number |
| `y` | Degrees by which the device is rotated on its Y axis | number |
| `z` | Degrees by which the device is rotated on its Z axis | number |
### setAppiumRotation
```
POST /session/:sessionId/appium/device/rotation
```
Sets the spatial orientation of the device under test.
#### Parameters
| Name | Description | Type |
| ---- | ---------------------------------------------------- | ------ |
| `x` | Degrees by which the device is rotated on its X axis | number |
| `y` | Degrees by which the device is rotated on its Y axis | number |
| `z` | Degrees by which the device is rotated on its Z axis | number |
#### Response
`null`
### getAppiumOrientation
```
GET /session/:sessionId/appium/device/orientation
```
Retrieves the current orientation of the device under test.
#### Response
`string` - either `PORTRAIT` or `LANDSCAPE`
### setAppiumOrientation
```
POST /session/:sessionId/appium/device/orientation
```
Sets the orientation of the device under test.
#### Parameters
| Name | Description | Type |
| ------------- | ------------------------------------------------------------------------------------------------------- | ------ |
| `orientation` | New device orientation. Supported values are `PORTRAIT` or `LANDSCAPE`. | string |
#### Response
`null`
@@ -140,6 +140,10 @@ GET /session/:sessionId/orientation
Retrieves the current orientation of the device under test.
!!! warning "Deprecated"
Please use [`getAppiumOrientation`](./appium.md#getappiumorientation) instead
#### Response
`string` - either `PORTRAIT` or `LANDSCAPE`
@@ -154,6 +158,10 @@ POST /session/:sessionId/orientation
Sets the orientation of the device under test.
!!! warning "Deprecated"
Please use [`setAppiumOrientation`](./appium.md#setappiumorientation) instead
#### Parameters
| Name | Description | Type |
@@ -21,6 +21,10 @@ GET /session/:sessionId/rotation
Retrieves the current spatial orientation of the device under test.
!!! warning "Deprecated"
Please use [`getAppiumRotation`](./appium.md#getappiumrotation) instead
#### Response
`Rotation` - an object with the following properties:
@@ -41,6 +45,10 @@ POST /session/:sessionId/rotation
Sets the spatial orientation of the device under test.
!!! warning "Deprecated"
Please use [`setAppiumRotation`](./appium.md#setappiumrotation) instead
#### Parameters
| Name | Description | Type |
@@ -63,6 +71,10 @@ GET /session/:sessionId/context
Retrieves the active application context.
!!! warning "Deprecated"
Please use [`getCurrentAppiumContext`](./appium.md#getcurrentappiumcontext) instead
#### Response
`string` - the name of the active context
@@ -77,6 +89,10 @@ POST /session/:sessionId/context
Sets an application context as the active context.
!!! warning "Deprecated"
Please use [`setAppiumContext`](./appium.md#setappiumcontext) instead
#### Parameters
| Name | Description | Type |
@@ -97,6 +113,10 @@ GET /session/:sessionId/contexts
Retrieves all available application contexts.
!!! warning "Deprecated"
Please use [`getAppiumContexts`](./appium.md#getappiumcontexts) instead
#### Response
`string[]` - the names of available contexts
@@ -170,10 +170,17 @@ Modifies the [`createSession`](./webdriver.md#createsession) endpoint:
All endpoints for this plugin can be invoked without creating a session, allowing you to prepare
your test environment in advance.
!!! info
Prior to plugin version 1.2.0, all endpoints below were mounted under the `/storage` prefix
(e.g. `/storage/add`) instead of `/appium/storage`. The `/storage`-prefixed routes are still
available for backward compatibility, but are deprecated and will be removed in a future
version of the plugin.
### addStorageItem
```
POST /storage/add
POST /appium/storage/add
```
Adds a new file to the storage.
@@ -200,8 +207,8 @@ Example:
```json
{
"ws": {
"stream": "/storage/add/ccc963411b2621335657963322890305ebe96186/stream",
"events": "/storage/add/ccc963411b2621335657963322890305ebe96186/events"
"stream": "/appium/storage/add/ccc963411b2621335657963322890305ebe96186/stream",
"events": "/appium/storage/add/ccc963411b2621335657963322890305ebe96186/events"
},
"ttlMs": 300000
}
@@ -210,7 +217,7 @@ Example:
### deleteStorageItem
```
POST /storage/delete
POST /appium/storage/delete
```
Deletes a file in the storage.
@@ -229,7 +236,7 @@ the storage, or if the delete request is invalid.
### listStorageItems
```
GET /storage/list
GET /appium/storage/list
```
List all files present in the storage.
@@ -247,7 +254,7 @@ List all files present in the storage.
### resetStorage
```
POST /storage/reset
POST /appium/storage/reset
```
Deletes all uploaded files and stops any incomplete uploads.
@@ -14,6 +14,12 @@ Provides management options for a specific extension (driver or plugin). Both th
The following sub-subcommands are supported: `doctor`, `install`, `list`, `run`, `update`,
and `uninstall`.
For `doctor`, `run`, `update`, and `uninstall`, you can identify an installed extension by its
short name or its exact npm package name. For example, `appium driver uninstall uiautomator2`
and `appium driver uninstall appium-uiautomator2-driver` refer to the same installed driver.
Short names take precedence if a name also matches another extension's package name. Package names
can include a scope, such as `@appium/fake-driver`, but must not include a version or tag suffix.
## `doctor`
Runs doctor checks for an installed extension, which validate whether the extension has its prerequisites
@@ -30,9 +36,9 @@ configured correctly. Note that not all extensions include doctor checks.
appium {driver|plugin} doctor <extension-name>
```
| Argument | Description |
| ---------------- | ----------------------------------------- |
| `extension-name` | The short name of the installed extension |
| Argument | Description |
| ---------------- | ------------------------------------------------------------------- |
| `extension-name` | The short name or exact npm package name of the installed extension |
#### Options
@@ -159,14 +165,14 @@ extensions include scripts.
#### Usage
```
appium {driver|plugin} run <extension-name> <script-name> [script-args]
appium {driver|plugin} run <extension-name> [<script-name> [<script-args>]]
```
| Argument | Description |
| ---------------- | --------------------------------------------- |
| `extension-name` | The short name of the installed extension |
| `script-name` | The name of the script to be run |
| `script-args` | Any additional arguments passed to the script |
| Argument | Description |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------ |
| `extension-name` | The short name or exact npm package name of the installed extension |
| `script-name` | The name of the script to run. If not provided, a list of available scripts is returned. |
| `script-args` | Any additional arguments passed to the script |
#### Options
@@ -182,6 +188,12 @@ appium {driver|plugin} run <extension-name> <script-name> [script-args]
appium driver run uiautomator2 reset
```
- List all available scripts included in the XCUITest driver:
```
appium driver run xcuitest
```
## `update`
Updates one or more extensions. Only supported for extensions installed via `npm`. By default,
@@ -193,9 +205,9 @@ Appium will only update minor and patch versions, in order to prevent any breaki
appium {driver|plugin} update <extension-name>
```
| Argument | Description |
| ---------------- | -------------------------------------------------------------------------------------------- |
| `extension-name` | The short name of the installed extension, or `installed` to update all installed extensions |
| Argument | Description |
| ---------------- | ---------------------------------------------------------------------------------------------------------------------- |
| `extension-name` | The short name or exact npm package name of the installed extension, or `installed` to update all installed extensions |
#### Options
@@ -228,9 +240,9 @@ Removes an installed extension.
appium {driver|plugin} uninstall <extension-name>
```
| Argument | Description |
| ---------------- | ----------------------------------------- |
| `extension-name` | The short name of the installed extension |
| Argument | Description |
| ---------------- | ------------------------------------------------------------------- |
| `extension-name` | The short name or exact npm package name of the installed extension |
#### Options
@@ -29,6 +29,7 @@ appium
| `--address`, `-a` | IPv4/IPv6 address to listen on | string | `0.0.0.0` |
| `--allow-cors` | Allow web browser connections from any host | boolean | `false` |
| `--allow-insecure` | List of [insecure features](../../guides/security.md) that should be allowed in this server's sessions. Individual features can be overridden by `--deny-insecure`. Has no effect in combination with `--relaxed-security`. | array<string> | `[]` |
| `--allow-unknown-args` | Do not exit if unrecognized command-line arguments are passed to the server; ignore them instead. Useful when the Appium CLI is wrapped by external tooling that appends extra flags. | boolean | `false` |
| `--base-path`, `-pa` | Base path to use as the prefix for all webdriver routes running on the server | string | `""` |
| `--callback-address`, `-ca` | Callback IP address | string | `0.0.0.0` |
| `--callback-port`, `-cp` | Callback port | integer | `4723` |
@@ -64,7 +65,7 @@ appium
| `--tmp` | Absolute path to the directory used for temporary files | string | [`os.tmpdir()`](https://nodejs.org/api/os.html#ostmpdir) |
| `--use-drivers` | List of drivers to activate. By default, all installed drivers are activated. | array<string> | `[]` |
| `--use-plugins` | List of plugins to activate. By default, no plugins are activated. Set to `["all"]` to activate all installed plugins. | array<string> | `[]` |
| `--webhook`, `-G` | URL for an HTTP listener where the server logs should be output. This does not affect output on the console. | string | |
| `--webhook`, `-G` | URL for an HTTP listener where the server logs should be output. This does not affect output on the console. A bare `host:port` is also accepted, which posts the logs to the root path over plain http; a value having neither a scheme nor a port falls back to `127.0.0.1:9003`. | string | |
### Info Options
@@ -46,11 +46,5 @@ capabilities is optional and entirely driver-dependent.
| --------------------------------------------------- | ------------------------------------------------------------- |
| `platformVersion` | Must be `string` |
| `appium:app` | Must be `string`; empty values are ignored |
| `appium:autoLaunch` (deprecated) | Must be `boolean` |
| `appium:autoWebview` | Must be `boolean` |
| `appium:fullReset` | Must be `boolean`; mutually exclusive with `appium:noReset` |
| `appium:language` | Must be `string` |
| `appium:locale` | Must be `string` |
| `appium:orientation` | Must be either `LANDSCAPE` or `PORTRAIT` |
| `appium:noReset` | Must be `boolean`; mutually exclusive with `appium:fullReset` |
| `appium:udid` | Must be `string` |
@@ -8,11 +8,6 @@ title: Additional Resources
Here you can find links to additional Appium resources around the web:
## Websites
- [Appium Pro](https://appiumpro.com) - a blog and newsletter written by one of Appium's
maintainers, Jonathan Lipps, with lots of useful guides
## Online Courses
- [Appium and Selenium Fundamentals](https://ui.headspin.io/university/learn/appium-selenium-fundamentals-2020) - a comprehensive video course on learning Python, Selenium, and Appium by Jonathan Lipps
+1
View File
@@ -57,6 +57,7 @@ nav:
- reference/api/plugins.md
- Guides:
- Migration:
- guides/migrating-3-to-4.md
- guides/migrating-2-to-3.md
- guides/migrating-1-to-2.md
- Server/Driver Configuration:
+12 -15
View File
@@ -10,19 +10,16 @@
/* eslint-disable promise/prefer-await-to-callbacks */
/* eslint-disable promise/prefer-await-to-then */
const {deploy, buildSite} = require('@appium/docutils');
const {
log,
LANGS,
DOCS_DIR,
DOCS_BRANCH,
DOCS_PREFIX,
DOCS_REMOTE,
LATEST_ALIAS,
} = require('./utils');
const path = require('node:path');
const semver = require('semver');
const {version} = require('../../package.json');
import {readFileSync} from 'node:fs';
import path from 'node:path';
import {fileURLToPath} from 'node:url';
import {deploy, buildSite} from '@appium/docutils';
import semver from 'semver';
import {log, LANGS, DOCS_DIR, DOCS_BRANCH, DOCS_PREFIX, DOCS_REMOTE, LATEST_ALIAS} from './utils.js';
const {version} = JSON.parse(readFileSync(new URL('../../package.json', import.meta.url), 'utf8'));
const branch = process.env.APPIUM_DOCS_BRANCH ?? DOCS_BRANCH;
const basePrefix = process.env.APPIUM_DOCS_PREFIX ?? DOCS_PREFIX;
@@ -67,10 +64,10 @@ async function main() {
}
}
if (require.main === module) {
if (process.argv[1] === fileURLToPath(import.meta.url)) {
main().catch((err) => {
throw err;
});
}
module.exports = main;
export default main;
@@ -1,98 +0,0 @@
// for simplicity this file is not transpiled and is run directly via an npm script
/* eslint-disable no-console */
/* eslint-disable promise/prefer-await-to-callbacks */
/* eslint-disable promise/prefer-await-to-then */
const path = require('node:path');
const {fs} = require('@appium/support');
const {AppiumConfigJsonSchema} = require('@appium/schema');
const DOC_PATH = path.resolve(__dirname, '..', 'en', 'cli', 'args.md');
const START_TAG = `<!-- AUTOGEN-START -->`;
const STOP_TAG = `<!-- AUTOGEN-STOP -->`;
/**
* Regenerates the CLI arguments table in docs.
*/
async function main() {
console.log(`Gathering current cli doc file contents...`);
const [preStart, postEnd] = await getDocParts();
console.log(`Generating new cli arg table...`);
const argTable = getCliArgTable();
console.log(`Writing new doc file...`);
await writeNewDoc(preStart, argTable, postEnd);
console.log(`...done!`);
}
async function getDocParts() {
if (!(await fs.exists(DOC_PATH))) {
throw new Error(`Cannot find args.md at ${DOC_PATH}`);
}
const curContents = await fs.readFile(DOC_PATH, 'utf-8');
// attempt to verify the start/stop tags
const [preStart, postStart] = curContents.split(START_TAG);
if (!postStart) {
throw new Error(`Could not find autogen start tag '${START_TAG}'. Make sure it's in place!`);
}
const [, postEnd] = postStart.split(STOP_TAG);
if (!postEnd) {
throw new Error(`Could not find autogen stop tag '${STOP_TAG}'. Make sure it's in place!`);
}
return [preStart, postEnd];
}
function getCliArgTable() {
const pad = '&nbsp;'.repeat(30);
const header = `|Argument${pad}|Description|Type|Default|Aliases|\n` + `|--|--|--|--|--|`;
const serverProps = AppiumConfigJsonSchema.properties.server.properties;
const args = Object.keys(serverProps).map((arg) => {
let {appiumCliAliases = [], description, type, items, maximum, minimum} = serverProps[arg];
let def = serverProps[arg].default;
let enums = serverProps[arg].enum;
if (type === 'array' || type === 'object' || (type === 'string' && def === '')) {
def = JSON.stringify(def);
}
if (type === 'array' && items.type) {
type = `array<${items.type}>`;
}
if (type === 'integer' && typeof minimum !== 'undefined' && typeof maximum !== 'undefined') {
description = `${description} (Value must be between \`${minimum}\` and \`${maximum}\`)`;
}
if (enums) {
const enumsFormatted = enums.map((e) => `\`${e}\``).join(', ');
description = `${description} (Value must be one of: ${enumsFormatted})`;
}
return {
arg: `--${arg}`,
aliases: appiumCliAliases.map((a) => `-${a}`),
type,
def,
description,
};
});
const body = args
.map(({arg, aliases, type, def, description}) => {
const aliasStr = aliases.map((a) => `\`${a}\``).join(', ');
def = typeof def !== 'undefined' ? `\`${def}\`` : '';
return `|\`${arg}\`|${description}|${type}|${def}|${aliasStr}|`;
})
.join('\n');
return `${header}\n${body}`;
}
async function writeNewDoc(preStart, argTable, postEnd) {
const newContents = `${preStart}${START_TAG}\n${argTable}\n${STOP_TAG}${postEnd}`;
await fs.writeFile(DOC_PATH, newContents);
}
if (require.main === module) {
main().catch((err) => {
throw err;
});
}
module.exports = main;
+5 -14
View File
@@ -2,28 +2,19 @@
// for simplicity this file is not transpiled and is run directly via an npm script
//
const {logger} = require('@appium/support');
const path = require('node:path');
import path from 'node:path';
import {logger} from '@appium/support';
const log = logger.getLogger('Docs');
const DOCS_REMOTE = 'origin';
const DOCS_BRANCH = 'gh-pages';
const DOCS_PREFIX = 'docs';
const DOCS_DIR = path.resolve(__dirname, '..');
const DOCS_DIR = path.resolve(import.meta.dirname, '..');
const LATEST_ALIAS = 'latest';
const ASSETS_DIR = path.join(DOCS_DIR, 'assets');
const LANGS = ['en', 'ja', 'zh'];
const DEFAULT_LANG = 'en';
module.exports = {
log,
DOCS_DIR,
ASSETS_DIR,
LANGS,
DEFAULT_LANG,
DOCS_BRANCH,
DOCS_PREFIX,
DOCS_REMOTE,
LATEST_ALIAS,
};
export {log, DOCS_DIR, ASSETS_DIR, LANGS, DEFAULT_LANG, DOCS_BRANCH, DOCS_PREFIX, DOCS_REMOTE, LATEST_ALIAS};
@@ -17,3 +17,8 @@ authors:
avatar: 'https://avatars.githubusercontent.com/u/4045041?v=4'
slug: 'sai'
url: 'https://saikrishna.tech'
mykola:
name: 'Mykola Mokhnach'
description: 'Appium Core Maintainer'
avatar: https://avatars.githubusercontent.com/u/7767781?v=4
slug: 'mykola'
@@ -5,9 +5,9 @@ authors:
date: 2024-07-13
---
# Appium Conf 2024: Call for Proposals Now Open!
# Appium Conf 2024: 现在开始征集提案
We're thrilled to announce that Appium Conf is back and going online for 2024! This is the official Appium conference where you can learn all about what's new in Appium 2.0 and explore the latest in automating tests for devices, mobile apps, and desktop applications.
我们很兴奋地宣布,Appium Conf 已回归并在2024年上线! 这是官方的Appium会议,您可以在会上了解Appium 2中的所有新内容。 并探索设备自动化测试、移动应用程序和桌面应用程序的最新版本。
<!-- more -->
@@ -17,29 +17,29 @@ We're thrilled to announce that Appium Conf is back and going online for 2024! T
</a>
</div>
## Mark Your Calendars: September 13, 2024
## 标记您的日历:2024年9月13日
Join us on September 13, 2024, for a full day of inspiring and practical talks from the Appium team and the world's top mobile testing and automation experts. With Appium 2.0 now well-established, this is your opportunity to dive deep and learn how to maximize its potential.
2024年9月13日,请加入我们,参加Appium团队和世界顶级移动测试和自动化专家为期一天的鼓舞人心且实用的演讲。 随着Appium 2.0的成熟,这是您深入了解并学习如何最大限度地发挥其潜力的机会。
## Why Attend Appium Conf 2024?
## 为什么参加了2024年的Appium Conf ?
1. **Gain Insights**: Hear interesting insights and inspiring case studies from global experts and the core Appium team.
2. **Improve Your Strategy**: Gain valuable knowledge to enhance your own automation strategy.
3. **Network**: Make connections with Appium users from around the globe.
4. **Learn from Specialists**: Benefit from Appium specialists' expertise on a variety of topics.
1. **获得见解**:听取全球专家和核心Appium团队的有趣见解和鼓舞人心的案例研究。
2. **改进您的战略**:获得有价值的知识,以增强您自己的自动化战略。
3. **网络**:与来自全球的Appium用户建立联系。
4. **向专家学习**:从Appium专家在各种主题上的专业知识中受益。
## Call for Proposals: Share Your Knowledge!
## 征集提案:分享您的知识!
We're looking for a diverse range of speakers from around the globe who are eager to share innovative ideas and use-cases of mobile test automation. If you have experience with Appium and insights that could benefit the community of test practitioners, we want to hear from you!
我们正在寻找来自全球各地的各种演讲者,他们渴望分享移动测试自动化的创新想法和用例。 如果您有使用Appium的经验和见解,可以使测试从业者社区受益,我们想听听您的意见!
Don't miss this chance to contribute to the Appium community and showcase your expertise on a global stage.
不要错过这个机会,为Appium社区做出贡献,并在全球舞台上展示你的专业知识。
## How to Submit Your Proposal
## 如何提交您的提案
Visit our website at [https://appiumconf.com/](https://appiumconf.com/) and [Submit a Proposal](https://confengine.com/conferences/appium-conf-2024/proposals?utm_medium=referal&utm_source=appium.io&utm_campaign=launch). We're excited to see your innovative ideas and experiences with Appium!
访问我们的网站 [https://appiumconf.com/](https://appiumconf.com/) 并[提交提案](https://confengine.com/conferences/appium-conf-2024/proposals?utm_medium=referal&utm_source=appium.io&utm_campaign=launch)。 我们很高兴看到您在 Appium 中的创新想法和经验!
## Stay Tuned
## 敬请期待
Keep an eye on our website and social media channels for updates on the conference schedule, keynote speakers, and more exciting details about Appium Conf 2024.
关注我们的网站和社交媒体频道以了解会议日程安排的更新, 主旨演讲人和2024年Appium Conf 的更令人兴奋的细节。
Join us in shaping the future of mobile and device test automation. Submit your proposal today and be part of this exciting event!
加入我们来塑造移动设备测试自动化的未来。 立即提交您的提案,成为这一激动人心的活动的一部分!
@@ -8,11 +8,13 @@ date: 2025-11-25
# Announcing LambdaTest as Appium's Newest Development Partner
As part of our ongoing effort to recognize organizations that meaningfully support the Appium project through dedicated maintainer time, we are excited to welcome our newest Development Partner: LambdaTest!
LambdaTest has long been an active participant in the automated testing ecosystem. Today, they are officially formalizing their commitment by investing people—dedicated engineers who will directly support Appium's development, maintenance, and ecosystem growth.
This kind of hands-on contribution is crucial for ensuring Appium continues to evolve. We deeply value partners who empower their engineering teams to contribute code directly to open source rather than just sponsorship.
<!-- more -->
LambdaTest has long been an active participant in the automated testing ecosystem. Today, they are officially formalizing their commitment by investing people—dedicated engineers who will directly support Appium's development, maintenance, and ecosystem growth.
This kind of hands-on contribution is crucial for ensuring Appium continues to evolve. We deeply value partners who empower their engineering teams to contribute code directly to open source rather than just sponsorship.
<div style="text-align:center;width:100%"><a href="https://lambdatest.com/?utm_source=appium.io&utm_medium=organic&utm_campaign=june_25&utm_term=sk&utm_content=webpage">
<img src="/docs/en/latest/assets/images/sponsor-logo-lambdatest-dark.png#only-dark" width="300" />
<img src="/docs/en/latest/assets/images/sponsor-logo-lambdatest-light.png#only-light" width="300" />
@@ -20,15 +22,19 @@ This kind of hands-on contribution is crucial for ensuring Appium continues to e
</div>
LambdaTest engineers are already contributing actively across several key Appium repositories. Their current focus includes:
**appium-mcp** – Supporting Appium's implementation of the Model Context Protocol (MCP) to drive forward modern AI-assisted capabilities.
**appium-remote-xpc** – Contributing to the native macOS/XPC-based remote execution layer, essential for iOS automation.
**appium-tun-tap** – Assisting in the development and maintenance of network virtualization and tunneling components for advanced testing scenarios.
- [`appium-mcp`](https://github.com/appium/appium-mcp) – Supporting Appium's implementation of the Model Context Protocol (MCP) to drive forward modern AI-assisted capabilities.
- [`appium-ios-remotexpc`](https://github.com/appium/appium-ios-remotexpc) – Contributing to the native macOS/XPC-based remote execution layer, essential for iOS automation.
- [`appium-ios-tuntap`](https://github.com/appium/appium-ios-tuntap) – Assisting in the development and maintenance of network virtualization and tunneling components for advanced testing scenarios.
These contributions strengthen critical infrastructure within the Appium ecosystem and demonstrate LambdaTest's commitment to deep technical involvement in the project's evolution.
About LambdaTest
**About LambdaTest**
LambdaTest’s platform already serves a massive community of Appium users. As a GenAI-powered Quality Engineering Platform, they provide a full-stack testing cloud with 10,000+ real mobile devices and 3,000+ browsers.
Beyond standard infrastructure, they are pushing the boundaries of test execution with AI-native test management and next-generation agents like KaneAI. By supporting Appium alongside Selenium, Playwright, and other major frameworks, LambdaTest helps teams orchestrate intelligent automation at cloud scale.
LambdaTest will continue contributing people, code, and development expertise to strengthen Appium's core functionality. Their commitment represents a significant investment into the future of the framework, and we're thrilled to have them collaborating with us at this deeper level.
The Appium project is proud to recognize LambdaTest as our newest official Development Partner. We can't wait to see what we'll build together!
@@ -0,0 +1,38 @@
---
authors:
- mykola
date: 2026-09-01
---
# Announcing watchOS Simulator Support in the XCUITest Driver
We’re excited to announce that the XCUITest driver now supports automation of watchOS apps — a
platform that’s been on Appium’s requested list for a long time.
<!-- more -->
In order to automate watchOS, you'll need the following:
- XCUITest driver version 12.6.0 or later (WebDriverAgent 16.5.0 or later)
- Xcode 15.4 or later
- watchOS 10 or later
Important to note: ^^the driver currently only works with simulators — real devices are not
compatible.^^
The good news: automating a watchOS app looks just like automating any other iOS/iPadOS app. The
standard use-cases of finding and clicking on elements, retrieving the page source, using the
Appium Inspector — all work as usual. The one exception here is standard W3C gesture/touch actions
(tap/swipe via the Actions API), which are not supported.
On top of that, a few watchOS-specific extensions are available:
- `mobile: pressButton` — press the Digital Crown or Action button
- `mobile: rotateDigitalCrown` — rotate the Digital Crown (since driver version 12.7.0)
- `mobile: performHandGesture` — perform double-tap or wrist flick hand gestures (since driver
version 12.7.0)
For full setup instructions, capabilities, and limitations, check out the
[watchOS guide](https://appium.github.io/appium-xcuitest-driver/latest/guides/watchos/).
Happy testing!
@@ -1,84 +1,66 @@
---
title: Building Documentation
title: 构建文档
---
Once you've [built a driver](./build-drivers.md) or [built a plugin](./build-plugins.md) for Appium,
you will hopefully want to document how that extension works for your users. The most basic way of
doing this is to write up a quick `README.md` and keep it in the root of your project's repository.
However, this can involve a lot of effort.
一旦你为 Appium 构建了 [驱动](./build-drivers.md) 或 [插件](./build-plugins.md),通常也会希望为用户说明这个扩展是如何工作的。 最基础的方式就是编写一个简短的 `README.md`,并将它放在项目仓库的根目录中。
不过,这样做可能会耗费不少精力。
The Appium project has built tools to help with this, and we've packaged up these tools so our
ecosystem developers building drivers and plugins can _also_ use them. The best way to get going
with these tools is probably to look at an existing Appium driver repo to see how it's done, for
example the [XCUITest driver repo](https://github.com/appium/appium-xcuitest-driver). But this guide
will outline the basic approach.
Appium 项目已经提供了辅助工具,并且我们把这些工具打包好了,以便生态系统中的驱动和插件开发者也能使用。 这些工具最好的入门方式是查看一个现有的 Appium 驱动仓库,看看它是如何组织文档的,例如 [XCUITest 驱动仓库](https://github.com/appium/appium-xcuitest-driver)。 不过,本指南会先介绍基本的实现思路。
### Conceptual architecture
### 概念架构
Appium settled on [MkDocs](https://www.mkdocs.org/) as a Markdown-based documentation site
generator. It uses a Python toolchain (and not Node.js), but it turned out to be the best option
for our purposes. You can adjust this, but by default Appium's utilities also assume that you'll be
using the [mkdocs-material](https://squidfunk.github.io/mkdocs-material/) theme/extension for
MkDocs.
Appium 采用 [MkDocs](https://www.mkdocs.org/) 作为基于 Markdown 的文档站点的生成工具。 它使用 Python 工具链(而不是 Node.js),但这恰好是我们当前需求下最合适的选择。 你可以自行调整,不过默认情况下,Appium 的工具链也会假设你使用 [mkdocs-material](https://squidfunk.github.io/mkdocs-material/) 主题与扩展来搭建 MkDocs。
In order to make different versions of your docs available (one for each minor release of your
extension, typically), we also bundle [Mike](https://github.com/jimporter/mike).
为了让你的文档能够支持不同版本(通常是每个小版本一个),我们还会捆绑 [Mike](https://github.com/jimporter/mike)。
From here, building a basic docs site is as easy as collecting your Markdown files together and
defining how you want them to be organized.
从这里开始,构建一个基础文档站点其实很简单:把 Markdown 文件收集起来,再定义你希望它们如何组织即可。
### Prerequisites
### 前置条件
To take advantage of Appium's documentation utilities, you'll need to install:
要使用 Appium 的文档工具,你需要安装以下内容:
- [Python v3+](https://www.python.org/downloads/)
- [pip](https://pip.pypa.io/en/stable/installation/) (this may be installed automatically with Python)
- The `@appium/docutils` package:
- [pip](https://pip.pypa.io/en/stable/installation/)(通常 Python 会自动附带安装)
- `@appium/docutils` 包:
```bash
npm install --save-dev @appium/docutils
```
### Initializing an Extension for Building Docs
### 初始化用于构建文档的扩展
To prepare your extension for generating documentation, run the following command:
要为你的扩展准备文档生成环境,请运行下面的命令:
```bash
npx appium-docs init
```
This will:
这一步会:
1. Create a `tsconfig.json` if one does not already exist. This is necessary even if your extension
is not written in TypeScript.
2. Create a `mkdocs.yml` with the necessary configuration for MkDocs.
1. 创建一个 `tsconfig.json`(如果当前还没有的话)。 即使你的扩展并不是 TypeScript 编写的,这也是必需的。
2. 创建一个 `mkdocs.yml`,其中包含 MkDocs 所需的配置。
### Documenting Your Extension
### 为你的扩展编写文档
At this point, you can begin documenting your extension. By default, MkDocs will look for Markdown
files in the `docs` directory. You can therefore create your Markdown documentation files, place
them in `docs`, and add links to these files in `mkdocs.yml`.
此时你就可以开始为扩展编写文档了。 默认情况下,MkDocs 会在 `docs` 目录中查找 Markdown 文件。 因此,你可以创建文档文件并放入 `docs`,然后在 `mkdocs.yml` 里添加相应链接。
Refer to the [MkDocs documentation](https://www.mkdocs.org/user-guide/writing-your-docs/) for
information on how to organize and structure your documentation.
请参考 [MkDocs 文档](https://www.mkdocs.org/user-guide/writing-your-docs/) 来了解如何组织与构建你的文档结构。
### Building the Docs
### 构建文档
At this point, you can use the `appium-docs` CLI tool. Run this tool with no arguments to get the
full help output and see all the available subcommands and parameters. Here are a few usage
examples:
在这个阶段,你可以使用 `appium-docs` CLI 工具。 运行这个工具时不传任何参数,可以看到完整的帮助信息,以及所有可用的子命令与参数。 下面是几个常见用法示例:
```bash
# Generate reference and build the mkdocs site into the site dir
# 生成参考文档并将 mkdocs 站点构建到 site 目录
npx appium-docs build
# Same as build, but host the docs on a local dev server
# and watch for changes and rebuild when files change
# 与 build 相同,但会在本地启动开发服务器
# 并监听文件变化,在内容变更时自动重建
npx appium-docs build --serve
# Build the docs and deploy them with mike versioning to the docs-site branch
# using the included commit message.
# This is particularly useful for pushing content to a GitHub pages branch!
# 构建文档并使用 mike 进行版本化部署到 docs-site 分支
# 其中包含内置提交信息。
# 这对于把内容推送到 GitHub Pages 分支特别有用!
npx appium-docs build \
--deploy \
-b docs-site \
@@ -1,45 +1,33 @@
---
title: Building Doctor Checks
title: 构建 Doctor Checks
---
The idea of Appium Doctor is to assist users with driver or plugin preconditions setup. Sometimes such
preconditions might be quite complicated and require non-trivial technical knowledge. Doctor checks,
which are vanilla Node.js class instances written by extension authors, simplify
the setup process by automating diagnostics and possible fixes for the found issues. These checks
might also be interactive to ensure better usage experience.
Appium Doctor 的目标,是帮助用户完成驱动或插件的前置条件配置。 有时这些前置条件可能相当复杂,需要一定的技术知识。 Doctor checks是由扩展作者编写的普通 Node.js 类实例,它可以自动完成诊断并尝试修复发现的问题。 为了提供更好的使用体验,这些检查项有时也会是交互式的。
This tutorial is supposed to be used by plugin or driver authors that would like to help their users
to deal with complicated setup or configuration steps.
本教程适用于希望帮助用户处理复杂配置或设置步骤的插件或驱动作者。
## Adding Doctor Checks
## 添加 Doctor Checks
### Typing Requirements
### 类型要求
The term `Doctor Check` literally describes a single javascript class instance that implements the
[IDoctorCheck interface](https://github.com/appium/appium/blob/master/packages/types/lib/doctor.ts).
The interface defines the following methods and properties:
术语 `Doctor Check` 的字面含义,是指一个实现了 [IDoctorCheck 接口](https://github.com/appium/appium/blob/master/packages/types/lib/doctor.ts) 的 JavaScript 类实例。
这个接口定义了以下方法和属性:
- `diagnose(): Promise<DoctorCheckResult>`: Contains the code to diagnose a possible issue
- `fix(): Promise<string|null>`: Either fixes the actual problem if `hasAutofix()` returns true or
returns a string description for possible manual fixes. If this method throws an exception named
`FixSkippedError` and `hasAutofix()` returns true then the result of the method invocation
is going to be ignored.
- `hasAutofix(): boolean`: Whether calling `fix()` would resolve the found issue
- `isOptional(): boolean`: Whether the found issue can be ignored and is not a showstopper
- `log: AppiumLogger`: May be used for logging. This property may be assigned
by the instance itself or by the Appium server if it is left unassigned.
- `diagnose(): Promise<DoctorCheckResult>`:包含诊断某个问题的代码
- `fix(): Promise<string|null>`:如果 `hasAutofix()` 返回 true,则修复实际问题;否则返回一段手动修复的说明。 如果此方法抛出名为 `FixSkippedError` 的异常,并且 `hasAutofix()` 返回 true,那么该调用结果将被忽略。
- `hasAutofix(): boolean`:表示调用 `fix()` 是否能解决发现的问题
- `isOptional(): boolean`:表示发现的问题是否可以被忽略且不是阻塞性问题
- `log: AppiumLogger`:可用于日志输出。 这个属性可以由实例自行赋值,也可以由 Appium 服务在未赋值时自动提供。
The `DoctorCheckResult` object returned by the `diagnose()` method must contain the following properties:
`diagnose()` 方法返回的 `DoctorCheckResult` 对象必须包含以下属性:
- `ok: boolean`: Whether the diagnosis found no issues
- `optional: boolean`: Whether the diagnosed issue is safe to ignore
- `message: string`: The text message describing the diagnostic result
- `ok: boolean`:诊断是否未发现任何问题。
- `optional: boolean`:诊断出的问题是否可以安全忽略。
- `message: string`:描述诊断结果的文本信息。
### Manifest Requirements
### 清单要求
A single extension may export multiple Doctor checks to Appium. In order for these checks to be properly
picked up by the server CLI after the corresponding extension is installed they might be listed in the
package .json manifest under the `appium.doctor.checks` section similar to the definition below:
一个扩展可以向 Appium 导出多个 Doctor checks。 为了让这些检查项在对应扩展安装后能够被服务端 CLI 正确识别,它们需要在package .json 清单文件中列在 `appium.doctor.checks` 部分,格式类似下面这样:
```json
// ...
@@ -67,12 +55,11 @@ package .json manifest under the `appium.doctor.checks` section similar to the d
// ...
```
Also, it makes sense to include the [@appium/types](https://www.npmjs.com/package/@appium/types) import
to the package dev dependencies.
此外,建议将 [@appium/types](https://www.npmjs.com/package/@appium/types) 加入包的开发依赖中。
### Implementation Example
### 实现示例
The below example is a "raw" Node.JS implementation that does not use any transpilation:
下面是一个“原生”的 Node.js 实现示例,没有使用任何转译工具:
```js
const {fs, doctor} = require('@appium/support');
@@ -123,8 +110,7 @@ module.exports = {androidHomeCheck};
*/
```
This file could be saved as `doctor/android-home-check.js` and then added to the package.json manifest
as
这个文件可以保存为 `doctor/android-home-check.js`,然后再将其加入到 package.json 清单中,例如:
```json
// ...
@@ -222,17 +222,24 @@ So you'll probably end up overriding `createSession`. You can do so by defining
driver:
```js
async createSession(jwpCaps, reqCaps, w3cCaps, otherDriverData) {
const [sessionId, caps] = super.createSession(w3cCaps);
async createSession(w3cCaps) {
const [sessionId, caps] = await super.createSession(w3cCaps);
// do your own stuff here
return [sessionId, caps];
}
```
For legacy reasons, your function will receive old-style JSON Wire Protocol desired and required
caps as the first two arguments. Given that the old protocol isn't supported anymore and clients
have all been updated, you can instead only rely on the `w3cCaps` parameter. (For a discussion
about what `otherDriverData` is about, see the section below on concurrent drivers).
!!! warning "Deprecated"
```
Older drivers may still define `createSession` with up to four parameters, e.g.
`createSession(jwpCaps, reqCaps, w3cCaps, otherDriverData)`. This shape is a holdover from the
retired JSON Wire Protocol, which required desired and required caps as the first two
arguments; only the first W3C-shaped argument was ever used. The `otherDriverData` parameter is
likewise deprecated in favor of [IPC](#send-messages-to-plugins-running-on-the-same-session) for
coordinating with other concurrently-running sessions. New drivers should use the single-argument
form shown above.
```
You'll want to make sure to call `super.createSession` in order to get the session ID as well as
the processed capabilities (note that capabilities are also set on `this.caps`; modifying `caps`
@@ -522,14 +529,13 @@ route, then the route will not be proxied and instead will be handled by your dr
example, we are avoiding proxying all `POST` routes that have the `appium` prefix.
Next, we have to set up the proxying itself. The way to do this is to use a special class from
Appium called `JWProxy`. (The name means "JSON Wire Proxy" and is related to a legacy
implementation of the protocol). You'll want to create a `JWProxy` object using the details required to
connect to the remote server:
Appium called `WebDriverProxy`. You'll want to create a `WebDriverProxy` object using the details
required to connect to the remote server:
```js
// import {JWProxy} from 'appium/driver';
// import {WebDriverProxy} from 'appium/driver';
const proxy = new JWProxy({
const proxy = new WebDriverProxy({
server: 'remote.server',
port: 1234,
base: '/',
@@ -541,8 +547,8 @@ this.proxyCommand = proxy.command.bind(proxy);
Here we are creating a proxy object and assigning some of its methods to `this` under the names
`proxyReqRes` and `proxyCommand`. This is required for Appium to use the proxy, so don't forget
this step! The `JWProxy` has a variety of other options which you can check out in the source code,
as well. (TODO: publish options as API docs and link here).
this step! The `WebDriverProxy` has a variety of other options which you can check out in the
source code as well.
Finally, we need a way to tell Appium when the proxy is active. For your driver it might always
be active, or it might only be active when in a certain context. You can define the logic as an
@@ -799,6 +805,14 @@ that multiple simultaneous sessions don't use the same resources:
3. Have each driver express what resources it is using, then examine currently-used resources from
other drivers when a new session begins.
!!! warning "Deprecated"
```
The `driverData`-based mechanism described below is deprecated. Prefer
[IPC](#send-messages-to-plugins-running-on-the-same-session) for coordinating resources across
concurrently-running sessions instead.
```
To support this third strategy, you can implement `get driverData` in your driver to return what
sorts of resources your driver is currently using, for example:
@@ -810,7 +824,7 @@ get driverData() {
Now, when a new session is started on your driver, the `driverData` response from any other
simultaneously running drivers (of the same type) will also be included, as the last parameter of
the `createSession` method:
the deprecated, multi-argument form of the `createSession` method:
```js
async createSession(jwpCaps, reqCaps, w3cCaps, driverData)
@@ -483,9 +483,9 @@ capability.
You probably don't normally need to update the Appium server object (which is an
[Express](https://expressjs.com/) server having already been
[configured](https://github.com/appium/appium/blob/master/packages/base-driver/lib/express/server.js)
in a variety of ways). But, for example, you could add new Express middleware to the server to
support your plugin's requirements. To update the server you must implement the `static async
updateServer` method in your class. This method takes three parameters:
in a variety of ways). But, for example, you could add new routes to the server to support your
plugin's requirements. To update the server you must implement the `static async updateServer`
method in your class. This method takes three parameters:
- `expressApp`: the Express app object
- `httpServer`: the Node HTTP server object
@@ -498,6 +498,25 @@ results you'll need to test! Warning: this should be considered an advanced feat
knowledge of Express, as well as the care not to do anything that could affect the operation of
other parts of the Appium server!
`updateServer` runs _after_ Appium's own routes are registered, so `expressApp.use(...)` here
won't see requests to paths Appium already owns — Express matches middleware and routes in
registration order. For middleware that must see requests to those routes too (e.g. logging or
auth), use `httpServer.frontRouter` instead: Appium mounts this
[Router](https://expressjs.com/en/5x/api/router/) before any route is registered, so anything
attached to it — even from inside `updateServer` — still runs ahead of route matching. Note this
is still _after_ Appium's own baseline middleware (CORS, WebSocket upgrade handling, body
parsing, etc.), so it won't see requests Appium itself has already rejected, e.g. a CORS
preflight failure or a body that failed to parse:
```js
static async updateServer(expressApp, httpServer, cliArgs) {
httpServer.frontRouter.use((req, res, next) => {
console.log(`Incoming request: ${req.method} ${req.url}`);
next();
});
}
```
### Handle unexpected session shutdown
When developing a plugin you may want to add some cleanup logic for when a session ends. You would
+7 -10
View File
@@ -2,17 +2,14 @@
hide:
- toc
title: Developing Appium Extensions
title: 开发 Appium 扩展
---
Appium is built with a modular structure, which means that Appium extensions (drivers and plugins)
are decoupled from the main Appium module, and you only need to install the extensions that you
want to use. This modular structure also unlocks the ability to develop entirely new extensions!
Appium 采用模块化结构,这意味着 Appium 扩展(驱动和插件)与主 Appium 模块是解耦的;你只需要安装自己想要使用的扩展即可。 这种模块化结构还使得开发全新的扩展变得成为可能。
This section of the Appium documentation is intended to help aspiring developers with creating their
own Appium extension:
Appium 文档的这一部分,旨在帮助有志于开发者创建自己的 Appium 扩展:
- For creating a driver, see the [Build Drivers](./build-drivers.md) page
- For creating a plugin, take a look at the [Build Plugins](build-plugins.md) page
- Drivers and plugins both need documentation, so check out the [Build Documentation](./build-docs.md) page
- For creating a doctor check, see the [Building Doctor Checks](./build-doctor-checks.md) page
- 想要创建驱动,请查看 [构建驱动](./build-drivers.md) 页面
- 想要创建插件,请查看 [构建插件](build-plugins.md) 页面
- 驱动和插件都需要文档,因此请查看 [构建文档](./build-docs.md) 页面
- 想要创建 Doctor check,请查看 [构建Doctor Checks](./build-doctor-checks.md) 页面
+17 -27
View File
@@ -2,48 +2,38 @@
hide:
- toc
title: Masking Sensitive Log Data
title: 屏蔽敏感日志数据
---
Since Appium server version 2.18.0 there is a possibility to mask sensitive
values in logs. The below tutorial explains how to use this feature in third-party
extensions.
自 Appium server 2.18.0 版本起,已经具备了在日志中屏蔽敏感值的能力。 下面的教程将介绍如何在第三方扩展中使用这一功能。
## Why It Might Be Useful
## 为什么这很有用
It is the right call to hide sensitive information, like passwords, tokens, etc.
from server logs, so it does not accidentally leak if these logs end up in wrong hands.
Appium server already provides a way to manipulate logs records via
[filtering](../guides/log-filters.md), although it has its own limitations.
The current approach is more sophisticated though, and requires some fine-tuning
on the driver/plugin side.
将密码、令牌等敏感信息从服务器日志中隐藏起来是合理且必要的,这样即使日志落入错误的人手中,也不会意外泄露敏感数据。
Appium server 已经提供了一种通过 [过滤](../guides/log-filters.md) 来处理日志记录的方式,不过它本身存在一些限制。
当前这种方法则更为先进,但需要驱动/插件侧进行一些细调。
## How To
## 使用方法
The assumption is that your extension uses the standard built-in
[@appium/logger](https://www.npmjs.com/package/@appium/logger).
In order to get some value in logs replaced by a generic mask it is necessary:
假设你的扩展使用的是标准内置的 [@appium/logger](https://www.npmjs.com/package/@appium/logger)。
要让日志中的某个值被替换成通用的掩码,需要执行以下步骤:
- Change the logging expression to wrap sensitive values and format them, for example:
- 将日志表达式改为包裹敏感值并进行格式化,例如:
```js
this.log.info(`Value: ${value}`);
this.log.info(`Value: ${value}`);
```
becomes
变为
```js
import {logger} from '@appium/support';
this.log.info('Value: %s', logger.markSensitive(value));
this.log.info('Value: %s', logger.markSensitive(value));
```
The formatting happens via the standard Node.js's
[util.format](https://nodejs.org/api/util.html#utilformatformat-args) API.
格式化过程会通过标准的 Node.js [util.format](https://nodejs.org/api/util.html#utilformatformat-args) API 完成。
- While sending the appropriate server request, where this log line is used and should be masked,
add the custom header `X-Appium-Is-Sensitive` with its value set to `1` or `true` (case-insensitive).
Without such header the above log value is not going to be masked.
This way it is possible to conditionally mask log records depending on which
request is being handled by the extension if the log expression is used in the
common section.
- 在发送相应的服务器请求时,如果这个日志行被使用并且应该被掩码处理,请额外添加自定义请求头 `X-Appium-Is-Sensitive`,其值为 `1` 或 `true`(大小写不敏感)。
如果没有这个请求头,上述日志值将不会被掩码处理。
这样就可以根据扩展处理的具体请求来有条件地屏蔽日志记录;如果日志表达式位于公共部分,就可以在这里统一控制。
+11 -1
View File
@@ -130,7 +130,7 @@ appium driver install --source=npm appium-flutter-integration-driver
appium driver install --source=npm appium-lg-webos-driver
```
### [Linux](https://github.com/fantonglang/appium-linux-driver)
### [Linux (@fantonglang)](https://github.com/fantonglang/appium-linux-driver)
!!! warning
@@ -149,6 +149,16 @@ yarn install
node ./
```
### [Linux (@Itsmeaj)](https://github.com/Itsmeaj/appium)
- Target: Linux desktop applications on X11 and Wayland
- Mode: Native
- Supported by: `@Itsmeaj`
```sh title="Install This Driver"
appium driver install --source=npm @itsmeaj/appium-linux-driver
```
### [NovaWindows](https://github.com/AutomateThePlanet/appium-novawindows-driver)
!!! info
+8 -8
View File
@@ -23,7 +23,7 @@ title: Appium 如何工作?
这里还有另一个更大的问题,鉴于除了 iOS 和 Android 之外还有更多应用平台:
- 我们如何为_所有_平台启用自动化?
- 我们如何&#x4E3A;_&#x6240;&#x6709;_&#x5E73;台启用自动化?
探索 Appium 对这些问题的答案可能不是学习 Appium 是什么的最快方式,但它肯定是一个好方法! 所以让我们深入了解。
@@ -38,12 +38,12 @@ Appium 的最初目标是为移动应用(iOS 和 Android)开发自动化标
[^2]: 从技术上讲,当 Appium 首次编写时,我们处理的是比 WebDriver 规范更古老的东西,称为 JSON Wire Protocol。 从那时起,Appium 继续与 W3C 规范一起演化,并完全符合 W3C 规范。
当然,Appium 希望支持用户交互从 Web 到移动或 Web 到电视确实不同的情况,因此 Appium 还利用 WebDriver 规范的内置_可扩展性_。 结果是,无论您想要自动化哪个平台,当您使用 Appium 时,您将使用标准 WebDriver 规范,但有两个注意事项:
当然,Appium 希望支持用户交互从 Web 到移动或 Web 到电视确实不同的情况,因此 Appium 还利用 WebDriver 规范的内&#x7F6E;_&#x53EF;扩展性_。 结果是,无论您想要自动化哪个平台,当您使用 Appium 时,您将使用标准 WebDriver 规范,但有两个注意事项:
- 我们可能无法在给定平台上支持特定的 WebDriver API 命令,因此某些命令可能不受支持(例如,在原生移动应用自动化世界中,获取或设置 cookie 是不可能的)。
- 我们可能支持超出 WebDriver API 命令列表的自动化行为,尽管任何此类命令将是有效的、符合规范的 WebDriver API 扩展。
您实际上如何_使用_ WebDriver API,特别是 Appium 的背景下? 我们将在下面的[部分](#universal-programming-language-access)中介绍 Appium 如何提供通用编程语言访问。 您现在只需要知道 Appium 通过实现 WebDriver 协议来引入通用 UI 自动化接口。
您实际上如&#x4F55;_&#x4F7F;用_ WebDriver API,特别是 Appium 的背景下? 我们将在下面的[部分](#universal-programming-language-access)中介绍 Appium 如何提供通用编程语言访问。 您现在只需要知道 Appium 通过实现 WebDriver 协议来引入通用 UI 自动化接口。
## 平台自动化行为
@@ -53,13 +53,13 @@ Appium 的最初目标是为移动应用(iOS 和 Android)开发自动化标
驱动程序是独立、可插拔模块的原因之一是它们彼此完全不同。 构建和使用不同平台的驱动程序的工具和要求完全不同。 因此,Appium 让您只使用您自动化任务所需的驱动程序。 选择驱动程序并安装它们以便与您的 Appium 实例一起使用非常重要,以至于 Appium 有自己的 [CLI 来管理驱动程序](../reference/cli/extensions.md)。
因此,为了回答我们最初的问题,Appium 为给定平台提供自动化功能访问的方式是 Appium 团队(或任何人[^3])为该平台编写一个_驱动程序_,实现 WebDriver 协议的尽可能多或少的部分。 然后,任何使用 Appium 的人都可以安装驱动程序。
因此,为了回答我们最初的问题,Appium 为给定平台提供自动化功能访问的方式是 Appium 团队(或任何人[^3])为该平台编写一&#x4E2A;_&#x9A71;动程序_,实现 WebDriver 协议的尽可能多或少的部分。 然后,任何使用 Appium 的人都可以安装驱动程序。
[^3]: 您可以构建和分享自己的驱动程序! 查看[构建驱动程序](../developing/build-drivers.md)以了解更多关于如何在 Node.js 中开发可与 Appium 一起使用的驱动程序的信息。
## 通用编程语言访问
但使用 Appium 意味着什么,或者看起来像什么? 由于 Appium 最终是一个 Node.js 程序,它_可以_看起来像将 Appium 及其驱动程序作为库导入到您自己的 Node.js 程序中。 但这不会满足 Appium 的目标,即为使用任何流行编程语言的人提供自动化功能。
但使用 Appium 意味着什么,或者看起来像什么? 由于 Appium 最终是一个 Node.js 程序,&#x5B83;_&#x53EF;&#x4EE5;_&#x770B;起来像将 Appium 及其驱动程序作为库导入到您自己的 Node.js 程序中。 但这不会满足 Appium 的目标,即为使用任何流行编程语言的人提供自动化功能。
幸运的是,Appium 继承 Selenium 的遗产意味着我们从一开始就有了这个问题的解决方案。 您看,WebDriver 规范实际上是一个基于 HTTP 的协议,这意味着它被设计为通过网络而不是单个程序的内存使用。
@@ -67,16 +67,16 @@ Appium 的最初目标是为移动应用(iOS 和 Android)开发自动化标
这里有几个重要的点供 Appium 用户了解:
- Appium 是一个_HTTP 服务器_。 只要您想使用它进行自动化,它必须在某台计算机上作为进程运行。 它必须在网络上可访问,以方便您想要运行自动化的任何计算机访问(无论是同一台机器还是世界另一端的机器)。
- Appium 是一&#x4E2A;_&#x48;TTP 服务器_。 只要您想使用它进行自动化,它必须在某台计算机上作为进程运行。 它必须在网络上可访问,以方便您想要运行自动化的任何计算机访问(无论是同一台机器还是世界另一端的机器)。
- 除非您想编写原始 HTTP 调用或使用 cURL,否则请使用您选择的语言的 [Appium 客户端](clients.md)进行自动化。 每个客户端的目标是将 WebDriver 协议封装,以便您不必担心协议本身,而是可以使用感觉适合您语言的对象和方法。
- Appium 服务器和 Appium 客户端_不需要_在同一台计算机上运行。 您只需能够通过某个网络从客户端向服务器发送 HTTP 请求。
- Appium 服务器和 Appium 客户&#x7AEF;_&#x4E0D;需&#x8981;_&#x5728;同一台计算机上运行。 您只需能够通过某个网络从客户端向服务器发送 HTTP 请求。
这大大促进了云提供商对 Appium 的使用,因为他们可以托管 Appium 服务器以及任何相关驱动程序和设备,您只需将客户端脚本指向他们的安全端点。
当然,这一切都不是关于“测试”本身,只是出于自动化目的使用 Appium 及其客户端库。 如果您想出于“测试”目的进行自动化,您可能希望利用测试运行器、测试框架等的帮助,这些都不需要与 Appium 相关;Appium“通用可访问性”的好处之一是它与您发现最适合您情况的任何工具集配合良好。
## Appium 的巨大范围
Appium 的愿景(在单一 API 下自动化一切)是巨大的! 当然,比开源项目的核心维护者团队大得多。 那么 Appium 如何希望实现这一目标? 基本上,通过授权社区在 Appium 之上作为_平台_开发功能。 这就是我们所谓的 Appium“生态系统”。
Appium 的愿景(在单一 API 下自动化一切)是巨大的! 当然,比开源项目的核心维护者团队大得多。 那么 Appium 如何希望实现这一目标? 基本上,通过授权社区在 Appium 之上作&#x4E3A;_&#x5E73;&#x53F0;_&#x5F00;发功能。 这就是我们所谓的 Appium“生态系统”。
Appium 团队确实自己维护了一些驱动程序(例如,我们之前讨论的 XCUITest 驱动程序)。 但它无法拥有特定于平台的专业知识或维护许多不同平台的驱动程序的能力。 但我们所做的,特别是从 Appium 2 开始,是提供工具来授权社区加入我们的愿景:

Some files were not shown because too many files have changed in this diff Show More