Compare commits

...
Author SHA1 Message Date
github-actions 8fead4e46c chore: publish
- appium@4.0.0-beta.8
 - @appium/base-driver@11.0.0-beta.7
 - @appium/base-plugin@4.0.0-beta.7
 - @appium/docutils@4.0.0-beta.7
 - @appium/driver-test-support@4.0.0-beta.7
 - @appium/execute-driver-plugin@8.0.0-beta.8
 - @appium/fake-driver@7.0.0-beta.8
 - @appium/fake-plugin@5.0.0-beta.8
 - @appium/http-server@1.0.0-beta.7
 - @appium/images-plugin@6.0.0-beta.8
 - @appium/opencv@6.0.0-beta.7
 - @appium/oxc-config@2.0.0-beta.3
 - @appium/plugin-test-support@4.0.0-beta.7
 - @appium/relaxed-caps-plugin@4.0.0-beta.7
 - @appium/schema@2.0.0-beta.4
 - @appium/storage-plugin@3.0.0-beta.8
 - @appium/support@8.0.0-beta.7
 - @appium/tsconfig@2.0.0-beta.2
 - @appium/types@2.0.0-beta.6
 - @appium/universal-xml-plugin@4.0.0-beta.8
2026-11-13 07:52:04 +00:00
Mykola Mokhnach ba7dcbbda0 feat(appium): add flow control for BiDi event delivery (#23146) 2026-11-13 08:50:02 +01:00
Mykola Mokhnach 880ab3a97c refactor(support): merge findAPortNotInUse into getFreePort (#23157) 2026-11-13 08:49:20 +01:00
Mykola Mokhnach d5d90c5d93 feat(support): add port helpers to net (#23156) 2026-11-13 08:07:04 +01:00
Mykola Mokhnach a558133f88 chore: drop rimraf dependency (#23154) 2026-11-12 15:56:26 +01:00
Mykola Mokhnach 3251261e82 chore: drop cpy-cli dependency (#23155)
Replace cpy in the fake-driver build script with native fs.cpSync and
remove the cpy-cli devDependency.
2026-11-12 15:52:06 +01:00
Mykola Mokhnach 243073851c test(appium): retry fixture cleanup on EBUSY in npm path tests (#23150) 2026-11-12 08:02:57 +01:00
Mykola Mokhnach 3bbb2037e6 chore: fix oxlint warnings (#23148)
Use the named ws export, replace promise chaining with async/await, and suppress the unavoidable body-parser callback.
2026-11-12 07:52:05 +01:00
Edgars Eglītis ae97c6b460 docs: update llms.txt (#23145) 2026-11-09 23:59:47 +02:00
Edgars Eglītis 37588201e1 docs: rewrite Appium 4 migration guide (#23144)
* docs: begin tuning migration guide

* docs: update new features section

* docs: tune

* docs: tune more

* docs: tune fixes section

* docs: tune developer section

* docs: minor tuning

* docs: add PR links for all fixes

* docs: add PR links for all dev changes

* docs: address comments
2026-11-09 20:58:50 +02:00
Mohamed Mostafa bf7b97bd36 fix(base-driver)!: keep command results that contain a protocol field (#23121)
BREAKING CHANGE: a command result shaped like {protocol, value} is no longer unwrapped. It is returned as the command value. {protocol, error} is still thrown when error is an Error and protocol is W3C or MJSONWP.
2026-11-09 12:03:04 +01:00
Mohamed Mostafa 082b4ce92e fix(images-plugin): find an image element inside its container when a better match is outside (#23135) 2026-11-09 08:27:00 +01:00
Mykola Mokhnach ddc1d555db feat!: enable noImplicitOverride in the shared tsconfig (#23143)
BREAKING CHANGE: @appium/tsconfig now sets noImplicitOverride: true. Extensions extending this config must mark overriding members with override, otherwise tsc fails with TS4114.
2026-11-09 07:59:59 +01:00
Mykola Mokhnach 6ca592d857 refactor: use typed errors for asyncbox and opencv failures (#23141) 2026-11-07 17:41:06 +01:00
Edgars Eglītis 7225f6b8b7 chore: port dependency bumps (#23138)
* chore: port dependency bumps

* chore: explicitly add axios to dev deps
2026-11-05 19:39:20 +02:00
Edgars Eglītis 8659d72bf2 docs: port sponsor updates (#23137) 2026-11-05 15:26:44 +02:00
Edgars Eglītis 1cf5711a69 docs: port recent translations (#23133) 2026-11-04 21:43:54 +02:00
Edgars Eglītis 87c4b6f795 docs: mention removal of --shell argument (#23129) 2026-11-03 17:47:54 +02:00
Edgars Eglītis 7f4bca6758 docs: update BrowserStack logo (#23128) 2026-11-03 17:41:32 +02:00
bob 282d1f48b9 chore(appium): remove dead --shell server argument (#23038)
The --shell flag is advertised in 'appium server --help' as 'Enter REPL mode' and declared in the MoreArgs type, but no code in the repo ever consumes it. Passing the flag silently does nothing; removing it makes argparse report an unknown-argument error instead.

Signed-off-by: NUAAwqy <146524024+NUAAwqy@users.noreply.github.com>
2026-11-03 15:26:16 +09:00
Edgars Eglītis 42b03172f2 fix(appium): avoid overlapping CLI spinners (#23125) 2026-11-01 22:01:32 +02:00
Kazuaki Matsuo 370a40410c fix(appium): align insecure feature logging with automationName scope (#23122)
* fix(appium): align insecure feature logging with automationName scope

* test(appium): parameterize insecure feature scope checks

* fix(appium): warn about install-name security scopes and test driver resolution

* refactor(appium): derive insecure feature CLI flags from option names
2026-11-02 01:09:03 +09:00
github-actions 3d31d20e9e chore: publish
- appium@4.0.0-beta.7
 - @appium/base-driver@11.0.0-beta.6
 - @appium/base-plugin@4.0.0-beta.6
 - @appium/docutils@4.0.0-beta.6
 - @appium/driver-test-support@4.0.0-beta.6
 - @appium/execute-driver-plugin@8.0.0-beta.7
 - @appium/fake-driver@7.0.0-beta.7
 - @appium/fake-plugin@5.0.0-beta.7
 - @appium/http-server@1.0.0-beta.6
 - @appium/images-plugin@6.0.0-beta.7
 - @appium/opencv@6.0.0-beta.6
 - @appium/storage-plugin@3.0.0-beta.7
 - @appium/support@8.0.0-beta.6
 - @appium/universal-xml-plugin@4.0.0-beta.7
2026-10-31 21:15:05 +00:00
Mohamed Mostafa 4a04100b3b fix(opencv): bound every matched point in getImagesMatches rectangles (#23120) 2026-10-31 20:27:48 +01:00
Kazuaki Matsuo ed13ec7c9b feat(support): accept removal options in rimraf helpers (#23105)
* feat(support): accept removal options in rimraf helpers

* fix(support): preserve rimraf recursive and force semantics

* refactor(support): clarify rimraf options and removal tests
2026-10-31 14:35:30 +09:00
Mykola Mokhnach 80f69e32d8 fix(appium): respect server compatibility in extension update and warn on non-npm install (#23119) 2026-10-30 17:46:49 +01:00
Mohamed Mostafa 9bf6c13163 fix(images-plugin): rethrow feature comparison errors besides a missing match (#23107) 2026-10-29 19:11:10 +01:00
Qingyuan Wang 96c275f841 fix(support)!: compare prerelease versions by semantic precedence (#23116)
BREAKING CHANGE: compareVersions() in @appium/support now orders prerelease versions correctly (2.0.0-beta.1 > 1.9.9, 2.0.0-beta.1 < 2.0.0). Before, it returned false for these.
2026-10-29 19:09:51 +01:00
Qingyuan Wang e61d420d15 fix(support): distinguish destinations on different devices (#23115) 2026-10-29 18:47:37 +01:00
Edgars Eglītis 3d400bb6fb docs: port documentation changes to beta (#23117) 2026-10-28 19:21:47 +02:00
PointerL 971fa8b58c fix(support): preserve non-string values in safeJsonParse (#23110) 2026-10-28 17:34:07 +01:00
PointerL efb52173dd fix(support): handle circular arrays in getObjectSize (#23111) 2026-10-28 17:32:49 +01:00
PointerL 236e116871 test(support): exercise fs.mv cross-device fallback (#23112) 2026-10-28 17:32:14 +01:00
fizzbeom bb93a07c1c fix(universal-xml-plugin): report a malformed XPath as invalid selector (#23108) 2026-10-28 14:13:21 +01:00
Nacy Dev b36eb13709 docs(appium): document gestures using W3C Actions API (#23094)
* docs(appium): document gestures using W3C Actions API

* docs(appium): apply review suggestions in gestures guide
2026-10-28 11:46:34 +02:00
Nacy Dev 84ad4014ba fix(universal-xml-plugin): skip XML transformation when context is not NATIVE_APP (#23095) 2026-10-28 08:27:46 +01:00
Mohamed Mostafa e85fe990d5 fix(base-driver): reject a negative newCommandTimeout (#23103) 2026-10-27 19:55:42 +01:00
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
752 changed files with 40407 additions and 23560 deletions
-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
+15 -2
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@v7.0.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,6 +39,12 @@ runs:
run: |
# Replace npm with sfw npm in the install command
install_cmd="${{ inputs.install-command }}"
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
@@ -72,6 +84,7 @@ runs:
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
+5 -5
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,11 +73,6 @@ labels:
matcher:
files: ['packages/driver-test-support/**']
- label: '@appium/eslint-config-appium-ts'
sync: true
matcher:
files: ['packages/eslint-config-appium-ts/**']
- label: '@appium/oxc-config'
sync: true
matcher:
+42 -24
View File
@@ -3,15 +3,18 @@ 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'
@@ -23,14 +26,17 @@ on:
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'
@@ -55,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@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
@@ -83,21 +93,26 @@ 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:
@@ -158,16 +173,18 @@ jobs:
test-e2e:
needs:
- prepare_matrix
- prepare_e2e_packages
name: E2E Tests - ${{ matrix.package_dir }} (Node ${{ matrix.node-version }})
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@v7.0.1
- name: Setup, install and build
@@ -176,11 +193,12 @@ jobs:
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
+1 -1
View File
@@ -23,7 +23,7 @@ jobs:
steps:
- uses: actions/checkout@v7.0.1
- name: Use Node.js LTS
uses: actions/setup-node@v7.0.0
uses: actions/setup-node@v7.1.0
with:
node-version: lts/*
cache: 'npm'
@@ -16,7 +16,7 @@ jobs:
with:
persist-credentials: false
- name: Use Node.js LTS
uses: actions/setup-node@v7.0.0
uses: actions/setup-node@v7.1.0
with:
node-version: lts/*
cache: 'npm'
@@ -17,7 +17,7 @@ jobs:
steps:
- uses: actions/checkout@v7.0.1
- name: Use Node.js LTS
uses: actions/setup-node@v7.0.0
uses: actions/setup-node@v7.1.0
with:
node-version: lts/*
cache: 'npm'
+2 -8
View File
@@ -10,13 +10,7 @@ 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:
@@ -29,7 +23,7 @@ jobs:
os: [ubuntu-latest, windows-latest, macos-latest]
steps:
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v7.0.0
uses: actions/setup-node@v7.1.0
with:
node-version: ${{ matrix.node-version }}
- name: Install Appium
+4
View File
@@ -49,5 +49,9 @@ jobs:
run: |
git config --local user.email "github-actions[bot]@users.noreply.github.com"
git config --local user.name "github-actions[bot]"
- name: Check llms.txt
run: npm run docs:check-llmstxt
- name: Build Docs
run: npm run docs:preview
- name: Verify llms.txt was published to the site
run: test -s packages/appium/docs/site/en/llms.txt
+1 -1
View File
@@ -16,7 +16,7 @@ jobs:
steps:
- uses: actions/checkout@v7.0.1
- name: Use Node.js LTS
uses: actions/setup-node@v7.0.0
uses: actions/setup-node@v7.1.0
with:
node-version: lts/*
cache: 'npm'
+23 -34
View File
@@ -13,11 +13,11 @@ on:
required: true
type: boolean
default: false
bootstrap_packages:
description: 'One-time beta-branch bootstrap only: comma-separated package names (e.g. @appium/base-driver,@appium/support) to force-bump to their next-major beta (premajor --preid beta), bypassing conventional-commit detection. Leave empty for a normal release.'
required: false
type: string
default: ''
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
@@ -58,41 +58,30 @@ jobs:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
DRY_RUN: ${{ inputs.dry_run }}
RECOVER_MISSING_ONLY: ${{ inputs.recover_missing_only }}
BOOTSTRAP_PACKAGES: ${{ inputs.bootstrap_packages }}
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).
BETA_FLAGS=""
DIST_TAG_FLAGS=""
if [ "${{ github.ref_name }}" = "$BETA_BRANCH" ]; then
BETA_FLAGS="--conventional-prerelease --preid beta"
DIST_TAG_FLAGS="--dist-tag beta"
#
# 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
# One-time bootstrap for packages just starting their beta train: force them
# straight to <next-major>.0.0-beta.0 instead of waiting for/relying on a commit that
# happens to be marked as breaking. Not part of the normal release flow below - this
# exits early on its own.
if [ -n "$BOOTSTRAP_PACKAGES" ]; then
if [ "${{ github.ref_name }}" != "$BETA_BRANCH" ]; then
echo "bootstrap_packages is only meaningful on the $BETA_BRANCH branch" >&2
exit 1
fi
if [ "$RECOVER_MISSING_ONLY" = "true" ]; then
echo "bootstrap_packages cannot be combined with recover_missing_only" >&2
exit 1
fi
echo "Bootstrapping ${BOOTSTRAP_PACKAGES} to their next-major beta (premajor --preid beta), bypassing conventional-commit detection"
if [ "$DRY_RUN" = "true" ]; then
printf "n\n" | npx lerna version premajor --preid beta --exact --no-git-tag-version --no-push --force-publish="$BOOTSTRAP_PACKAGES"
else
npx lerna publish premajor --preid beta --exact --force-publish="$BOOTSTRAP_PACKAGES" --dist-tag beta --yes
fi
exit 0
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
@@ -155,7 +144,7 @@ jobs:
echo "Publishing ${pkg_spec}"
set +e
(cd "$pkg_dir" && npm publish $DIST_TAG_FLAGS)
(cd "$pkg_dir" && npm publish $NPM_TAG_FLAGS)
publish_exit_code=$?
set -e
if [ $publish_exit_code -ne 0 ]; then
@@ -194,7 +183,7 @@ jobs:
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 $BETA_FLAGS
printf "n\n" | npx lerna version --no-git-tag-version --no-push $BETA_FLAGS
else
echo "Running lerna publish for real"
set +e
+1 -1
View File
@@ -47,4 +47,4 @@ jobs:
if [ "${{ github.ref_name }}" = "$BETA_BRANCH" ]; then
BETA_FLAGS="--conventional-prerelease --preid beta"
fi
npx lerna version --exact --yes $BETA_FLAGS
npx lerna version --yes $BETA_FLAGS
+15
View File
@@ -30,3 +30,18 @@ jobs:
run: npm run install-docs-deps
- name: Publish docs
run: npm run docs:publish
# The docs are deployed by mike into docs/<lang>/<version>, but crawlers look for
# /llms.txt at the domain root. The file's links are absolute, so the same file serves
# both locations.
- name: Publish llms.txt to the site root
run: |
cp packages/appium/docs/en/llms.txt "${RUNNER_TEMP}/llms.txt"
git checkout -f gh-pages
cp "${RUNNER_TEMP}/llms.txt" llms.txt
git add llms.txt
if git diff --cached --quiet -- llms.txt; then
echo "Root llms.txt is already up to date"
else
git commit -m "docs: update root llms.txt"
git push origin gh-pages
fi
+1 -3
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
@@ -27,4 +25,4 @@ jobs:
- name: Validate Renovate Config
uses: rinchsan/renovate-config-validator@v0.2.1
with:
pattern: '{.renovaterc.json,renovate/default.json}'
pattern: '.renovaterc.json'
-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
+1 -1
View File
@@ -1,6 +1,6 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": ["github>appium/appium//renovate/default"],
"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).
+1 -1
View File
@@ -161,7 +161,7 @@ for the particular driver or whether it supports parallel sessions.
automation, all using the WebDriver model that's already the standard for web automation.
5. You can run your automated tests locally and in a cloud. There are multiple cloud providers that
support various Appium drivers (mostly targeting iOS and Android mobile automation).
6. [Appium Inspector](https://github.com/appium/appium-inspector) can be used to visually inspect
6. [Appium Inspector](https://appium.github.io/appium-inspector/) can be used to visually inspect
the page source of applications across different platforms, facilitating easier test development.
Investing in the [WebDriver](https://w3c.github.io/webdriver/webdriver-spec.html) protocol means you
-38
View File
@@ -1,38 +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", "appium4"],
"exact": true
"allowBranch": ["master", "appium4"]
}
},
"ignoreChanges": ["**/test/**", "**/*.md"],
+2861 -7937
View File
File diff suppressed because it is too large Load Diff
+18 -30
View File
@@ -37,7 +37,7 @@
"clean": "run-s clean:distfiles clean:workspaces",
"clean:dangerously": "run-s clean:workspaces clean:force",
"clean:deps": "lerna clean --yes",
"clean:distfiles": "npx rimraf --glob \"./packages/*/build\" \"./packages/*/**/.tsconfig.tsbuildinfo\"; npx nx reset",
"clean:distfiles": "node ./scripts/clean-distfiles.mjs && npx nx reset",
"clean:interactive": "git clean -d -x -i",
"clean:force": "git clean -d -x -f",
"clean:workspaces": "lerna run clean || true",
@@ -46,9 +46,9 @@
"dev": "run-s build:compile \"build:workspaces\" \"build:compile -- --watch\"",
"dev:docs": "npm run -w packages/appium dev:docs",
"docs": "npm run -w packages/appium build:docs",
"docs:check-llmstxt": "npm run -w packages/appium check:docs:llmstxt",
"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": "oxfmt -c oxfmt.config.mjs .",
"format:check": "oxfmt -c oxfmt.config.mjs --check .",
@@ -62,7 +62,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",
@@ -80,48 +80,36 @@
"zip-and-upload": "run-s zip upload"
},
"devDependencies": {
"@appium/oxc-config": "1.0.0",
"@tsconfig/node20": "20.1.10",
"@types/async-lock": "1.4.2",
"@types/bluebird": "3.5.42",
"@types/content-disposition": "0.5.9",
"@appium/oxc-config": "2.0.0-beta.0",
"@tsconfig/node22": "22.0.0",
"@types/express": "5.0.6",
"@types/finalhandler": "1.2.4",
"@types/jsftp": "2.1.5",
"@types/json-schema": "7.0.15",
"@types/klaw": "3.0.7",
"@types/lockfile": "1.0.4",
"@types/method-override": "3.0.0",
"@types/mime-types": "3.0.1",
"@types/morgan": "1.9.10",
"@types/node": "24.13.3",
"@types/plist": "3.0.5",
"@types/node": "24.19.2",
"@types/picomatch": "4.0.3",
"@types/pluralize": "0.0.33",
"@types/semver": "7.8.0",
"@types/serve-favicon": "2.5.7",
"@types/shell-quote": "1.7.5",
"@types/sinon": "22.0.0",
"@types/sinon": "22.0.2",
"@types/which": "3.0.4",
"@types/ws": "8.18.1",
"@types/yargs": "17.0.35",
"asyncbox": "6.4.2",
"asyncbox": "6.6.0",
"axios": "1.20.0",
"conventional-changelog-conventionalcommits": "9.3.1",
"cpy-cli": "7.0.0",
"cross-env": "10.1.0",
"finalhandler": "2.1.1",
"json-schema-to-typescript": "15.0.4",
"lerna": "10.0.1",
"mjpeg-consumer": "2.0.0",
"mjpeg-server": "0.3.1",
"npm-run-all2": "9.0.3",
"rewiremock": "3.14.6",
"rimraf": "6.1.3",
"serve-static": "2.2.1",
"json-schema-to-typescript": "16.0.0",
"lerna": "10.1.1",
"npm-run-all2": "9.0.4",
"sinon": "22.1.0",
"teen_process": "4.2.1",
"tsd": "0.33.0",
"typescript": "6.0.3",
"webdriverio": "9.31.2",
"ws": "8.21.3"
"webdriverio": "9.32.0",
"ws": "8.22.0"
},
"overrides": {
"@sidvind/better-ajv-errors": {
@@ -130,10 +118,10 @@
"ajv-formats": {
"ajv": "8.20.0"
},
"http-cache-semantics": "4.2.0"
"http-cache-semantics": "4.3.0"
},
"engines": {
"node": "^20.19.0 || ^22.12.0 || >=24.0.0",
"node": "^22.22.2 || ^24.15.0 || >=26.0.0",
"npm": ">=10"
}
}
+280
View File
@@ -3,6 +3,286 @@
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.8](https://github.com/appium/appium/compare/appium@4.0.0-beta.7...appium@4.0.0-beta.8) (2026-11-13)
### ⚠ BREAKING CHANGES
* **base-driver:** a command result shaped like {protocol, value} is no longer unwrapped. It is returned as the command value. {protocol, error} is still thrown when error is an Error and protocol is W3C or MJSONWP.
* @appium/tsconfig now sets noImplicitOverride: true. Extensions extending this config must mark overriding members with override, otherwise tsc fails with TS4114.
### Features
* **appium:** add flow control for BiDi event delivery ([#23146](https://github.com/appium/appium/issues/23146)) ([ba7dcbb](https://github.com/appium/appium/commit/ba7dcbbda0720303c119cb1b121465cc2a471e65))
* enable noImplicitOverride in the shared tsconfig ([#23143](https://github.com/appium/appium/issues/23143)) ([ddc1d55](https://github.com/appium/appium/commit/ddc1d555dbf04d1cdd4dfd79139e48b1b077817b))
* **support:** add port helpers to net ([#23156](https://github.com/appium/appium/issues/23156)) ([d5d90c5](https://github.com/appium/appium/commit/d5d90c5d9373eedb96525105c25069b85f13dcbc))
### Bug Fixes
* **appium:** align insecure feature logging with automationName scope ([#23122](https://github.com/appium/appium/issues/23122)) ([370a404](https://github.com/appium/appium/commit/370a40410c5de3b6a196fb6c1c0157db75794ecf))
* **appium:** avoid overlapping CLI spinners ([#23125](https://github.com/appium/appium/issues/23125)) ([42b0317](https://github.com/appium/appium/commit/42b03172f2eeb4d787987623c6e2e3a8525b35d6))
* **base-driver:** keep command results that contain a protocol field ([#23121](https://github.com/appium/appium/issues/23121)) ([bf7b97b](https://github.com/appium/appium/commit/bf7b97bd3605cab1c88d64732a89dc68b47520a2))
## [4.0.0-beta.7](https://github.com/appium/appium/compare/appium@4.0.0-beta.6...appium@4.0.0-beta.7) (2026-10-31)
### Bug Fixes
* **appium:** respect server compatibility in extension update and warn on non-npm install ([#23119](https://github.com/appium/appium/issues/23119)) ([80f69e3](https://github.com/appium/appium/commit/80f69e32d87172eaf838819845e8017517acbaf2))
## [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
+1 -1
View File
@@ -161,7 +161,7 @@ for the particular driver or whether it supports parallel sessions.
automation, all using the WebDriver model that's already the standard for web automation.
5. You can run your automated tests locally and in a cloud. There are multiple cloud providers that
support various Appium drivers (mostly targeting iOS and Android mobile automation).
6. [Appium Inspector](https://github.com/appium/appium-inspector) can be used to visually inspect
6. [Appium Inspector](https://appium.github.io/appium-inspector/) can be used to visually inspect
the page source of applications across different platforms, facilitating easier test development.
Investing in the [WebDriver](https://w3c.github.io/webdriver/webdriver-spec.html) protocol means you
@@ -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'
@@ -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!
+22 -1
View File
@@ -61,7 +61,7 @@ cd appium
!!! info
If you are VS Code user, you can easily check out the project using [Runme](https://runme.dev/api/runme?repository=https%3A%2F%2Fgithub.com%2Fappium%2Fappium.git&fileToOpen=packages%2Fappium%2Fdocs%2Fen%2Fcontributing%2Findex.md).
If you are VS Code user, you can easily check out the project using [Runme](https://runme.dev/).
Install dependencies:
```sh
@@ -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).
@@ -62,6 +62,33 @@ them in `docs`, and add links to these files in `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.
### Helping LLMs Use Your Documentation
The [`/llms.txt` convention](https://llmstxt.org/) is a Markdown file at the root of a
documentation site, listing its most useful pages so that large language models can navigate the
site without crawling it. Appium's own documentation provides one at
[appium.io/docs/en/latest/llms.txt](https://appium.io/docs/en/latest/llms.txt), and you may want to
do the same for your extension.
MkDocs copies any non-Markdown file in your `docs` directory to the built site as-is, so all you
need to do is write an `llms.txt` and place it alongside your Markdown files. No plugin or build
configuration is required.
The value of this file lies in the parts a crawler could not have worked out on its own, so it is
worth writing by hand rather than generating it from your navigation:
- A short description of what your extension automates, and which platforms it targets
- The handful of things models reliably get wrong about your extension - removed capabilities,
renamed commands, patterns from an older major version that no longer apply
- Links to the pages that matter, with a description of each
Because links in `llms.txt` are read out of context, they must be absolute URLs. If you use Mike
for versioning, point them at your version alias (for example `latest`) rather than at a pinned
version, so that the file does not go stale. Note that a hand-written file can drift as pages are
renamed; Appium validates its own with a
[script](https://github.com/appium/appium/blob/master/packages/appium/docs/scripts/check-llmstxt.js)
that runs in CI, which you are welcome to adapt.
### Building the Docs
At this point, you can use the `appium-docs` CLI tool. Run this tool with no arguments to get the
@@ -80,8 +80,9 @@ The required subfields are:
capability to tell Appium to use *your* driver.
* `platformNames`: this is an array of one or more platform names considered valid for your driver.
When a user sends in the `platformName` capability to start a session, it must be included in
this list for your driver to handle the session. Known platform name strings include: `iOS`,
`tvOS`, `macOS`, `Windows`, `Android`.
this list for your driver to handle the session. These names should generally include the
operating systems your driver is targeting - for example, a driver targeting iOS would include
`iOS`, while a driver targeting the Chrome browser on Windows would include `Windows`.
* `mainClass`: this is a named export (in CommonJS style) from your `main` field. It must be a
class which extends Appium's `BaseDriver` (see below).
@@ -229,16 +230,6 @@ async createSession(w3cCaps) {
}
```
!!! 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`
locally here would have no effect other than changing what the user sees in the create session
@@ -281,8 +272,8 @@ You handle WebDriver commands by implementing functions in your driver class. Ea
WebDriver Protocol, plus the various Appium extensions, has a corresponding function that you
implement if you want to support that command in your driver. The best way to see which commands
Appium supports and which method you need to implement for each command is to look at Appium's
[routes.js](https://github.com/appium/appium/blob/master/packages/base-driver/lib/protocol/routes.js).
Each route object in this file tells you the command name as well as the parameters you'd expect to
[routes/](https://github.com/appium/appium/tree/master/packages/base-driver/lib/protocol/routes).
Each route object in this directory's files tells you the command name as well as the parameters you'd expect to
receive for that command.
Let's take this block for example:
@@ -323,7 +314,7 @@ Websockets. Implementing handlers for BiDi commands works just the same as imple
for WebDriver classic commands (described in the previous section). You simply define a method on
your driver of the appropriate name, and it will be called when the BiDi command is requested by
the client. To see which specific names you should use for BiDi commands, have a look at
[bidi-commands.js](https://github.com/appium/appium/blob/master/packages/base-driver/lib/protocol/bidi-commands.js)
[bidi-commands.ts](https://github.com/appium/appium/blob/master/packages/base-driver/lib/protocol/bidi-commands.ts)
You are not limited to BiDi commands that are defined in the official BiDi specification. If you
wish to define new commands, you may do so; you just need to tell Appium about them! See
@@ -544,7 +535,7 @@ 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 `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/jsonwp-proxy/proxy.ts)
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
@@ -724,6 +715,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
@@ -791,6 +788,36 @@ this.eventEmitter.emit('bidiEvent', {
Now, if the client has subscribed to the `system.cpu` event, it will be notified with the load
whenever the driver emits it.
#### Slow clients and high-rate events
Appium does not buffer events indefinitely for a client that does not read them. A connection
that keeps more than `--bidi-max-buffered-bytes` unsent for longer than `--bidi-max-buffered-ms`
is closed with the code `1013`, and connections that do not answer WebSocket pings (see
`--bidi-heartbeat-interval`) are terminated. Both only affect the offending connection.
Drivers that emit events at a high rate (e.g. media streams) can let Appium skip events for a
connection that is falling behind, instead of waiting for it to be closed. Add a `delivery` hint
to the event (it is never sent to clients; older Appium versions ignore it):
```js
this.eventEmitter.emit('bidiEvent', {
method: 'appium:media.units',
params: {track: 'video', units},
delivery: {policy: 'droppable', chain: 'video', syncPoint: startsWithKeyframe},
})
```
- `policy`: `reliable` (default) events are always sent, `droppable` ones may be skipped.
- `chain`: events of the same chain depend on each other. After one is skipped for a connection,
the following ones are skipped for it too, until an event with `syncPoint: true` (e.g. a keyframe)
arrives and the connection has caught up. Every connection resynchronizes independently.
Subscriptions are per session, not per connection, so Appium decides by the state of each
connection's buffer. To react to dropped events (e.g. to request a keyframe or lower the bitrate),
listen to `bidiBackpressure` on the same emitter. Its payload is `{method, chain?, state, dropped}`,
where `state` is `dropping` once the first connection starts skipping events (and again whenever a
sync point is skipped, so that you can request a new one) and `recovered` once the last one stops, and `dropped` is the largest number of events skipped for a single connection.
### Make itself aware of resources other concurrent drivers are using
Let's say your driver uses up some system resources, like ports. There are a few ways to make sure
@@ -798,50 +825,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.
!!! 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:
```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 deprecated, multi-argument form 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
@@ -989,10 +998,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,7 +476,7 @@ 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)
[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:
@@ -62,7 +62,7 @@ functionality (e.g., the `plugin` or `driver` subcommands).
Note that this file is the _base_ schema; this will become painfully relevant.
This file is is _not_ a JSON file, because a) JSON is painful to work with for humans, b) is
This file is _not_ a JSON file, because a) JSON is painful to work with for humans, b) is
especially reviled by @jlipps, and c) `ajv` accepts objects, not JSON files.
It is more straightforward to explain how config files are validated, so we'll start there.
@@ -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
+29 -25
View File
@@ -15,34 +15,34 @@ To learn more about drivers, check out the [Driver Intro](../intro/drivers.md).
These drivers are currently maintained by the Appium team:
### [Chromium](https://github.com/appium/appium-chromium-driver)
### [Chromium](https://appium.github.io/appium-chromium-driver)
* Target: Desktop and mobile Chromium browsers (Chrome, Microsoft Edge, etc.)
* Target: Desktop Chromium-based browsers (Chrome, Microsoft Edge, etc.)
* Mode: Web
```sh title="Install This Driver"
appium driver install chromium
```
### [Espresso](https://github.com/appium/appium-espresso-driver)
### [Espresso](https://appium.github.io/appium-espresso-driver)
* Target: Android applications
* Mode: Native
* Target: Android applications (mobile, TV, Wear, XR, Automotive)
* Mode: Native, Hybrid, Web
```sh title="Install This Driver"
appium driver install espresso
```
### [Gecko](https://github.com/appium/appium-geckodriver)
### [Gecko](https://appium.github.io/appium-geckodriver)
* Target: Desktop and mobile Gecko browsers (Firefox)
* Target: Desktop and mobile Gecko-based browsers (Firefox, etc.)
* Mode: Web
```sh title="Install This Driver"
appium driver install gecko
```
### [Mac2](https://github.com/appium/appium-mac2-driver)
### [Mac2](https://appium.github.io/appium-mac2-driver)
* Target: macOS applications
* Mode: Native
@@ -51,7 +51,7 @@ appium driver install gecko
appium driver install mac2
```
### [Safari](https://github.com/appium/appium-safari-driver)
### [Safari](https://appium.github.io/appium-safari-driver)
* Target: Desktop and mobile Safari browsers
* Mode: Web
@@ -60,32 +60,27 @@ appium driver install mac2
appium driver install safari
```
### [UiAutomator2](https://github.com/appium/appium-uiautomator2-driver)
### [UiAutomator2](https://appium.github.io/appium-uiautomator2-driver)
* Target: Android, Android TV, Android Wear applications
* Target: Android applications (mobile, TV, Wear, XR, Automotive)
* Modes: Native, Hybrid, Web
```sh title="Install This Driver"
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://appium.github.io/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/)
* Target: iOS, iPadOS, tvOS applications
* Target: iOS, iPadOS, tvOS, watchOS applications
* Modes: Native, Hybrid, Web
```sh title="Install This Driver"
@@ -155,13 +150,8 @@ 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
* Mode: Native, Hybrid
* Supported by: Community / Automate The Planet
```sh title="Install This Driver"
@@ -202,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
@@ -12,7 +12,7 @@ for more specialised automation workflows.
## Official Plugins
These plugins are are currently maintained by the Appium team:
These plugins are currently maintained by the Appium team:
### [Execute Driver](https://github.com/appium/appium/tree/master/packages/execute-driver-plugin)
@@ -104,3 +104,44 @@ Supported by: `@AppiumTestDistribution`
```sh title="Install This Tool"
npm install -g appium-installer
```
### [Optics](https://github.com/mozarkai/optics-framework)
Optics is an open-source test automation framework that uses Appium to drive Android, iOS,
Android TV, Samsung Tizen and LG webOS apps (and Selenium or Playwright for web). Tests are written
as CSV or YAML keyword steps, and the same keywords are available as a Python library, a Robot
Framework library, an HTTP API and an MCP server. Element lookup falls back from XPath to visible
text, OCR and image matching.
Supported by: `@chinmayajha`
```sh title="Install This Tool"
pip install "optics-framework[mobile]"
```
### [XPath to Class Chain](https://github.com/domovou69/xpath-to-class-chain)
XPath to Class Chain is a command-line tool for migrating iOS XPath locators in test code to
`-ios class chain` selectors. It converts the XPaths that have an exact class chain equivalent,
converts with a warning those that only have a close one (such as grouped positions `(//X)[n]`),
and skips the rest with a reason (for example, sibling or parent axes).
Supported by: `@domovou69`
```sh title="Install This Tool"
npm install -g xpath-to-class-chain
```
### [XPath to UiSelector](https://github.com/domovou69/xpath-to-uiselector)
XPath to UiSelector is a command-line tool for migrating Android XPath locators in test code to
`-android uiautomator` selectors. It converts the XPaths that have an exact `UiSelector`
equivalent, converts with a warning those that only have a close one (such as `//A//B`), and skips
the rest with a reason. The strategy call is rewritten together with the string, for example
`AppiumBy.xpath` becomes `AppiumBy.androidUIAutomator`.
Supported by: `@domovou69`
```sh title="Install This Tool"
npm install -g xpath-to-uiselector
```
@@ -52,6 +52,6 @@ After the PR is merged there is no need to use the local plugin/driver deploymen
to switch back to the package managed by NPM. Follow the next steps for that:
- Unlink the installed driver/plugin from the server by running `appium driver uninstall <driver_name>` or
`appium plugin uninstall <driver_name>`.
`appium plugin uninstall <plugin_name>`.
- Delete the local source folder (`rm -rf <full_path_to_plugin_or_driver_folder_with_fetched_sources>`).
- Install the driver or the plugin from NPM. Check the component README in order to find a proper command for that.
+2 -2
View File
@@ -8,14 +8,14 @@ principles, so you could create more performant and efficient test suite executi
## Why Caching Is Necessary
Mobile application bundles could reach hundreds of megabytes is size. This could become a serious
Mobile application bundles could reach hundreds of megabytes in size. This could become a serious
performance issue if a test suite is executed, and it is necessary to fetch/extract the same application
bundle for each test.
## What Is Cached
Caching could be applied to application bundles generated by
[`configureApp`](https://github.com/appium/appium/blob/master/packages/base-driver/lib/basedriver/helpers.js#L107) helper call.
[`configureApp`](https://github.com/appium/appium/blob/master/packages/base-driver/lib/basedriver/helpers.js#L99) helper call.
Inherited drivers can customize their caching logic by providing own `onPostProcess`
(or both `inDownload` and `onPostProcess`) property definition, but the general
rule of thumb is that we need to cache locally all application bundles need to be downloaded and/or extracted
+1 -1
View File
@@ -24,7 +24,7 @@ to launch. In order to achieve this, Appium defines its own [extension capabilit
## Common Appium Capabilities
According to the WebDriver spec, extension capabilities must include a namespace prefix (signifying
the vendor introducing the capability), and the namespace must end a colon (`:`). Appium's vendor
the vendor introducing the capability), and the namespace must end in a colon (`:`). Appium's vendor
prefix is `appium:`, and so any Appium-specific capabilities must include this prefix. Depending on
your Appium client, the prefix may be added automatically or in conjunction with certain interfaces,
but it is always a good practice to explicitly include it for clarity.
+2 -2
View File
@@ -34,7 +34,7 @@ discovered and loaded by Appium:
- `appium.config.cjs`
- `.appiumrc` (which is considered to be JSON)
Further, _if your project uses Node.js,_ you can use store the configuration inside an `appium`
Further, _if your project uses Node.js,_ you can store the configuration inside an `appiumConfig`
property in your `package.json` and it will be automatically discovered.
### Config File Search
@@ -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.
+9 -10
View File
@@ -6,18 +6,16 @@ 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:
```
{
"<event_type>": [<occurence_timestamp_1>, ...],
"<event_type>": [<occurrence_timestamp_1>, ...],
"commands": [
{
"cmd": "<command_name>",
@@ -29,7 +27,7 @@ property:
}
```
In other words, the `events` property has 2 kinds of properties of its own:
In other words, the event history object has 2 kinds of properties of its own:
* Properties which are the names of event types
* The `commands` property
@@ -63,7 +61,8 @@ generate various kinds of reports from event timings output:
!!! note
In the past, events were available as a part of `GET /session/:id` response
In the past, when the `appium:eventTimings` capability was set to `true`, the event history
was also returned as an `events` property of the `GET /session/:id` response
## Add a custom event
@@ -16,7 +16,7 @@ by client libraries. There are two main strategies for this:
based client library (including all Selenium and Appium clients).
There are pros and cons for each strategy, but it is ultimately up to the extension author to
decide how they wish implement new commands.
decide how they wish to implement new commands.
This guide is designed to specifically help you understand the "Execute Method" strategy. This
pattern is commonly used in official Appium drivers and other third-party extensions.
@@ -67,7 +67,7 @@ function, but the function itself—the snippet—is always a string and is the
In the world of Appium, we are usually not automating a web browser, which means this command is
not particularly useful. But it *is* useful as a way to encode the name of an arbitrary command and
to provide parameters. For example, the [XCUITest
Driver](https://github.com/appium/appium-xcuitest-driver) has implemented a command that lets a client
Driver](https://appium.github.io/appium-xcuitest-driver/) has implemented a command that lets a client
terminate a running application if you know the ID (the `bundleId`) of the app. The way that the
driver makes this command available is via the Execute Method `mobile: terminateApp`. Instead of
providing a JavaScript function to the "Execute Script" command, the user provides a _known string_
+343
View File
@@ -0,0 +1,343 @@
---
title: Automating Gestures
---
Appium supports automating complex pointer, touch, key, and wheel interactions through the
W3C WebDriver [Perform Actions](../reference/api/webdriver.md#performactions) API
([W3C Specification](https://w3c.github.io/webdriver/#actions)). Alternatively, many drivers provide
platform-native gesture [Execute Methods](./execute-methods.md) for common interactions.
In the W3C Actions API, interactions are defined by creating one or more virtual input sources
(such as `pointer` with `pointerType: "touch"`, `key`, `wheel`, or `none`) and queuing a sequence
of synchronized ticks (`pointerMove`, `pointerDown`, `pointerUp`, `pause`). See the
[Selenium Actions API documentation](https://www.selenium.dev/documentation/webdriver/actions_api/)
for a general overview of input sources and tick synchronization.
Below are examples of common single-finger and multi-touch gestures implemented using the W3C
Actions API across client languages.
## Double Tap
Performs two quick touch taps at `(x, y)` with a short pause between them:
=== "JS (WebDriverIO)"
```js
await driver
.action('pointer', {parameters: {pointerType: 'touch'}})
.move({x: 200, y: 300, duration: 0})
.down()
.up()
.pause(100)
.down()
.up()
.perform();
```
=== "Java"
```java
PointerInput finger = new PointerInput(PointerInput.Kind.TOUCH, "finger");
Sequence doubleTap = new Sequence(finger, 1)
.addAction(finger.createPointerMove(Duration.ZERO, PointerInput.Origin.viewport(), 200, 300))
.addAction(finger.createPointerDown(PointerInput.MouseButton.LEFT.asArg()))
.addAction(finger.createPointerUp(PointerInput.MouseButton.LEFT.asArg()))
.addAction(new Pause(finger, Duration.ofMillis(100)))
.addAction(finger.createPointerDown(PointerInput.MouseButton.LEFT.asArg()))
.addAction(finger.createPointerUp(PointerInput.MouseButton.LEFT.asArg()));
driver.perform(Collections.singletonList(doubleTap));
```
=== "Python"
```py
actions = ActionChains(driver)
actions.w3c_actions = ActionBuilder(driver, mouse=PointerInput(interaction.POINTER_TOUCH, "finger"))
actions.w3c_actions.pointer_action.move_to_location(200, 300)
actions.w3c_actions.pointer_action.pointer_down()
actions.w3c_actions.pointer_action.pointer_up()
actions.w3c_actions.pointer_action.pause(0.1)
actions.w3c_actions.pointer_action.pointer_down()
actions.w3c_actions.pointer_action.pointer_up()
actions.perform()
```
=== "Ruby"
```rb
f1 = driver.action.add_pointer_input(:touch, 'finger')
f1.create_pointer_move(duration: 0, x: 200, y: 300, origin: :viewport)
f1.create_pointer_down(:left)
f1.create_pointer_up(:left)
f1.create_pause(0.1)
f1.create_pointer_down(:left)
f1.create_pointer_up(:left)
driver.perform_actions([f1])
```
=== "C#"
```csharp
var finger = new PointerInputDevice(PointerKind.Touch, "finger");
var doubleTap = new ActionSequence(finger, 0);
doubleTap.AddAction(finger.CreatePointerMove(CoordinateOrigin.Viewport, 200, 300, TimeSpan.Zero));
doubleTap.AddAction(finger.CreatePointerDown(MouseButton.Touch));
doubleTap.AddAction(finger.CreatePointerUp(MouseButton.Touch));
doubleTap.AddAction(finger.CreatePause(TimeSpan.FromMilliseconds(100)));
doubleTap.AddAction(finger.CreatePointerDown(MouseButton.Touch));
doubleTap.AddAction(finger.CreatePointerUp(MouseButton.Touch));
driver.PerformActions(new List<ActionSequence> { doubleTap });
```
## Long Press
Presses and holds a touch pointer at `(x, y)` for 1000ms before releasing:
=== "JS (WebDriverIO)"
```js
await driver
.action('pointer', {parameters: {pointerType: 'touch'}})
.move({x: 200, y: 300, duration: 0})
.down()
.pause(1000)
.up()
.perform();
```
=== "Java"
```java
PointerInput finger = new PointerInput(PointerInput.Kind.TOUCH, "finger");
Sequence longPress = new Sequence(finger, 1)
.addAction(finger.createPointerMove(Duration.ZERO, PointerInput.Origin.viewport(), 200, 300))
.addAction(finger.createPointerDown(PointerInput.MouseButton.LEFT.asArg()))
.addAction(new Pause(finger, Duration.ofMillis(1000)))
.addAction(finger.createPointerUp(PointerInput.MouseButton.LEFT.asArg()));
driver.perform(Collections.singletonList(longPress));
```
=== "Python"
```py
actions = ActionChains(driver)
actions.w3c_actions = ActionBuilder(driver, mouse=PointerInput(interaction.POINTER_TOUCH, "finger"))
actions.w3c_actions.pointer_action.move_to_location(200, 300)
actions.w3c_actions.pointer_action.pointer_down()
actions.w3c_actions.pointer_action.pause(1.0)
actions.w3c_actions.pointer_action.pointer_up()
actions.perform()
```
=== "Ruby"
```rb
f1 = driver.action.add_pointer_input(:touch, 'finger')
f1.create_pointer_move(duration: 0, x: 200, y: 300, origin: :viewport)
f1.create_pointer_down(:left)
f1.create_pause(1.0)
f1.create_pointer_up(:left)
driver.perform_actions([f1])
```
=== "C#"
```csharp
var finger = new PointerInputDevice(PointerKind.Touch, "finger");
var longPress = new ActionSequence(finger, 0);
longPress.AddAction(finger.CreatePointerMove(CoordinateOrigin.Viewport, 200, 300, TimeSpan.Zero));
longPress.AddAction(finger.CreatePointerDown(MouseButton.Touch));
longPress.AddAction(finger.CreatePause(TimeSpan.FromMilliseconds(1000)));
longPress.AddAction(finger.CreatePointerUp(MouseButton.Touch));
driver.PerformActions(new List<ActionSequence> { longPress });
```
## Drag and Drop / Swipe
Moves a touch pointer from `(200, 600)` to `(200, 200)` over 600ms:
=== "JS (WebDriverIO)"
```js
await driver
.action('pointer', {parameters: {pointerType: 'touch'}})
.move({x: 200, y: 600, duration: 0})
.down()
.pause(100)
.move({x: 200, y: 200, duration: 600})
.up()
.perform();
```
=== "Java"
```java
PointerInput finger = new PointerInput(PointerInput.Kind.TOUCH, "finger");
Sequence dragAndDrop = new Sequence(finger, 1)
.addAction(finger.createPointerMove(Duration.ZERO, PointerInput.Origin.viewport(), 200, 600))
.addAction(finger.createPointerDown(PointerInput.MouseButton.LEFT.asArg()))
.addAction(new Pause(finger, Duration.ofMillis(100)))
.addAction(finger.createPointerMove(Duration.ofMillis(600), PointerInput.Origin.viewport(), 200, 200))
.addAction(finger.createPointerUp(PointerInput.MouseButton.LEFT.asArg()));
driver.perform(Collections.singletonList(dragAndDrop));
```
=== "Python"
```py
actions = ActionChains(driver)
actions.w3c_actions = ActionBuilder(driver, mouse=PointerInput(interaction.POINTER_TOUCH, "finger"))
actions.w3c_actions.pointer_action.move_to_location(200, 600)
actions.w3c_actions.pointer_action.pointer_down()
actions.w3c_actions.pointer_action.pause(0.1)
actions.w3c_actions.pointer_action.move_to_location(200, 200)
actions.w3c_actions.pointer_action.pointer_up()
actions.perform()
```
=== "Ruby"
```rb
f1 = driver.action.add_pointer_input(:touch, 'finger')
f1.create_pointer_move(duration: 0, x: 200, y: 600, origin: :viewport)
f1.create_pointer_down(:left)
f1.create_pause(0.1)
f1.create_pointer_move(duration: 0.6, x: 200, y: 200, origin: :viewport)
f1.create_pointer_up(:left)
driver.perform_actions([f1])
```
=== "C#"
```csharp
var finger = new PointerInputDevice(PointerKind.Touch, "finger");
var dragAndDrop = new ActionSequence(finger, 0);
dragAndDrop.AddAction(finger.CreatePointerMove(CoordinateOrigin.Viewport, 200, 600, TimeSpan.Zero));
dragAndDrop.AddAction(finger.CreatePointerDown(MouseButton.Touch));
dragAndDrop.AddAction(finger.CreatePause(TimeSpan.FromMilliseconds(100)));
dragAndDrop.AddAction(finger.CreatePointerMove(CoordinateOrigin.Viewport, 200, 200, TimeSpan.FromMilliseconds(600)));
dragAndDrop.AddAction(finger.CreatePointerUp(MouseButton.Touch));
driver.PerformActions(new List<ActionSequence> { dragAndDrop });
```
## Pinch and Zoom (Multi-Touch)
Uses two concurrent `touch` pointer sequences (`finger1` and `finger2`) moving apart to zoom in (or toward each other to pinch):
=== "JS (WebDriverIO)"
```js
await driver.performActions([
{
type: 'pointer',
id: 'finger1',
parameters: {pointerType: 'touch'},
actions: [
{type: 'pointerMove', duration: 0, x: 200, y: 400},
{type: 'pointerDown', button: 0},
{type: 'pause', duration: 100},
{type: 'pointerMove', duration: 500, x: 200, y: 200},
{type: 'pointerUp', button: 0},
],
},
{
type: 'pointer',
id: 'finger2',
parameters: {pointerType: 'touch'},
actions: [
{type: 'pointerMove', duration: 0, x: 200, y: 450},
{type: 'pointerDown', button: 0},
{type: 'pause', duration: 100},
{type: 'pointerMove', duration: 500, x: 200, y: 650},
{type: 'pointerUp', button: 0},
],
},
]);
```
=== "Java"
```java
PointerInput finger1 = new PointerInput(PointerInput.Kind.TOUCH, "finger1");
Sequence seq1 = new Sequence(finger1, 1)
.addAction(finger1.createPointerMove(Duration.ZERO, PointerInput.Origin.viewport(), 200, 400))
.addAction(finger1.createPointerDown(PointerInput.MouseButton.LEFT.asArg()))
.addAction(new Pause(finger1, Duration.ofMillis(100)))
.addAction(finger1.createPointerMove(Duration.ofMillis(500), PointerInput.Origin.viewport(), 200, 200))
.addAction(finger1.createPointerUp(PointerInput.MouseButton.LEFT.asArg()));
PointerInput finger2 = new PointerInput(PointerInput.Kind.TOUCH, "finger2");
Sequence seq2 = new Sequence(finger2, 1)
.addAction(finger2.createPointerMove(Duration.ZERO, PointerInput.Origin.viewport(), 200, 450))
.addAction(finger2.createPointerDown(PointerInput.MouseButton.LEFT.asArg()))
.addAction(new Pause(finger2, Duration.ofMillis(100)))
.addAction(finger2.createPointerMove(Duration.ofMillis(500), PointerInput.Origin.viewport(), 200, 650))
.addAction(finger2.createPointerUp(PointerInput.MouseButton.LEFT.asArg()));
driver.perform(Arrays.asList(seq1, seq2));
```
=== "Python"
```py
action_builder = ActionBuilder(driver)
finger1 = action_builder.add_pointer_input(interaction.POINTER_TOUCH, "finger1")
finger2 = action_builder.add_pointer_input(interaction.POINTER_TOUCH, "finger2")
finger1.create_pointer_move(duration=0, x=200, y=400)
finger1.create_pointer_down(button=MouseButton.LEFT)
finger1.create_pause(0.1)
finger1.create_pointer_move(duration=500, x=200, y=200)
finger1.create_pointer_up(button=MouseButton.LEFT)
finger2.create_pointer_move(duration=0, x=200, y=450)
finger2.create_pointer_down(button=MouseButton.LEFT)
finger2.create_pause(0.1)
finger2.create_pointer_move(duration=500, x=200, y=650)
finger2.create_pointer_up(button=MouseButton.LEFT)
action_builder.perform()
```
=== "Ruby"
```rb
f1 = driver.action.add_pointer_input(:touch, 'finger1')
f1.create_pointer_move(duration: 0, x: 200, y: 400, origin: :viewport)
f1.create_pointer_down(:left)
f1.create_pause(0.1)
f1.create_pointer_move(duration: 0.5, x: 200, y: 200, origin: :viewport)
f1.create_pointer_up(:left)
f2 = driver.action.add_pointer_input(:touch, 'finger2')
f2.create_pointer_move(duration: 0, x: 200, y: 450, origin: :viewport)
f2.create_pointer_down(:left)
f2.create_pause(0.1)
f2.create_pointer_move(duration: 0.5, x: 200, y: 650, origin: :viewport)
f2.create_pointer_up(:left)
driver.perform_actions([f1, f2])
```
=== "C#"
```csharp
var finger1 = new PointerInputDevice(PointerKind.Touch, "finger1");
var seq1 = new ActionSequence(finger1, 0);
seq1.AddAction(finger1.CreatePointerMove(CoordinateOrigin.Viewport, 200, 400, TimeSpan.Zero));
seq1.AddAction(finger1.CreatePointerDown(MouseButton.Touch));
seq1.AddAction(finger1.CreatePause(TimeSpan.FromMilliseconds(100)));
seq1.AddAction(finger1.CreatePointerMove(CoordinateOrigin.Viewport, 200, 200, TimeSpan.FromMilliseconds(500)));
seq1.AddAction(finger1.CreatePointerUp(MouseButton.Touch));
var finger2 = new PointerInputDevice(PointerKind.Touch, "finger2");
var seq2 = new ActionSequence(finger2, 0);
seq2.AddAction(finger2.CreatePointerMove(CoordinateOrigin.Viewport, 200, 450, TimeSpan.Zero));
seq2.AddAction(finger2.CreatePointerDown(MouseButton.Touch));
seq2.AddAction(finger2.CreatePause(TimeSpan.FromMilliseconds(100)));
seq2.AddAction(finger2.CreatePointerMove(CoordinateOrigin.Viewport, 200, 650, TimeSpan.FromMilliseconds(500)));
seq2.AddAction(finger2.CreatePointerUp(MouseButton.Touch));
driver.PerformActions(new List<ActionSequence> { seq1, seq2 });
```
-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.
@@ -18,8 +18,11 @@ Each rule is an object with a set of predefined properties. The following rule p
supported:
- `pattern`: A valid Javascript RegExp pattern to replace. Must be a valid non-empty pattern.
- `text`: A simple non-empty exact text match to replace. Either this property or the above one must
be provided. `pattern` has priority over `text` if both are provided.
- `text`: A simple non-empty exact text to replace. Either this property or the above one must
be provided. `pattern` has priority over `text` if both are provided. Note that the text is
matched as a whole word: special characters are escaped and word boundaries (`\b`) are added on
the sides where the value begins or ends with a word character. For example, a rule with text
`pass` replaces `pass` in `my pass here`, but does not match inside `password`.
- `flags`: Regular expression flags for the given pattern. Supported flags are the same as for the
standard JavaScript [RegExp constructor](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#Advanced_searching_with_flags_2).
Note that the `g` (global matching) flag is always enabled.
@@ -25,7 +25,7 @@ appium driver install xcuitest
```
This command will install the latest version of the
[XCUITest Driver](https://github.com/appium/appium-xcuitest-driver). The Extension CLI comes with a variety
[XCUITest Driver](https://appium.github.io/appium-xcuitest-driver/). The Extension CLI comes with a variety
of commands and parameters; see the documentation for that command for all the specifics.
The all-important question when Appium is managing your extensions for you is: where are they installed?
@@ -63,7 +63,7 @@ like any other dependency. Basically, whenever you run Appium, if you have not e
1. Try to determine whether the _current directory_ is inside an `npm` package.
1. If so, it will check whether `appium` is a dependency (dev, prod, or peer) in the project's
`package.json`
1. If so, _unless you have specified `APPIUM_HOME` in your environment_, Appium will ignore load
1. If so, _unless you have specified `APPIUM_HOME` in your environment_, Appium will load
drivers and plugins defined in that `package.json` file instead.
This means you are freely able to add Appium drivers and plugins as regular package dependencies or
@@ -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
@@ -106,10 +102,10 @@ and it is recommended to migrate to the following replacements:
|Appium 1 Package|Replacement in Appium 2|
|--|--|
|iOS Driver|[XCUITest Driver](https://appium.github.io/appium-xcuitest-driver/latest/)|
|UiAutomator Driver|[UiAutomator2](https://github.com/appium/appium-uiautomator2-driver/)|
|iOS Driver|[XCUITest Driver](https://appium.github.io/appium-xcuitest-driver/)|
|UiAutomator Driver|[UiAutomator2](https://appium.github.io/appium-uiautomator2-driver/)|
|`wd` Client|[WebdriverIO Client](https://webdriver.io/)|
|Appium Desktop|[Appium Inspector](https://github.com/appium/appium-inspector)|
|Appium Desktop|[Appium Inspector](https://appium.github.io/appium-inspector/)|
!!! info "Actions Needed"
@@ -201,8 +197,12 @@ and [Mobile JSON Wire Protocol](https://github.com/SeleniumHQ/mobile-spec/blob/m
(MJSONWP) were used instead.
In Appium 1, all of these protocols were supported, so that older Selenium/Appium clients could
still communicate with newer Appium servers. Appium 2 removes support for JSONWP/MJSONWP and is now
only compatible with the W3C WebDriver Protocol.
still communicate with newer Appium servers. Appium 2 removes support for JSONWP/MJSONWP (including
legacy `TouchAction` and `MultiAction` endpoints) and is now only compatible with the W3C WebDriver
Protocol. Multi-touch and pointer gestures should be performed using the W3C
[Perform Actions](../reference/api/webdriver.md#performactions) API (see the
[Automating Gestures](./gestures.md) guide) or driver-specific
[Execute Methods](./execute-methods.md).
!!! info "Actions Needed"
@@ -148,9 +148,9 @@ Icons are used to indicate endpoint support in either certain drivers, or in the
- :simple-appium:{ .lg } - Appium server
- :material-apple:{ .lg } - [XCUITest driver](https://appium.github.io/appium-xcuitest-driver/latest/)
- :material-android:{ .lg } - [UiAutomator2 driver](https://github.com/appium/appium-uiautomator2-driver/)
- :material-coffee:{ .lg } - [Espresso driver](https://github.com/appium/appium-espresso-driver)
- :material-apple-finder:{ .lg } - [Mac2 driver](https://github.com/appium/appium-mac2-driver)
- :material-android:{ .lg } - [UiAutomator2 driver](https://appium.github.io/appium-uiautomator2-driver/)
- :material-coffee:{ .lg } - [Espresso driver](https://appium.github.io/appium-espresso-driver/)
- :material-apple-finder:{ .lg } - [Mac2 driver](https://appium.github.io/appium-mac2-driver/)
- :material-microsoft-windows:{ .lg } - [Windows driver](https://github.com/appium/appium-windows-driver)
</div>
@@ -454,9 +454,6 @@ longer accepts, as well as the parameters it continues to accept in Appium 3.
* `POST /session/:sessionId/element/:elementId/value`
* :octicons-x-24: `value`
* :octicons-check-24: `text`
* `POST /session/:sessionId/timeouts`
* :octicons-x-24: `type`, `ms`
* :octicons-check-24: `script`, `pageLoad`, `implicit`
* `POST /session/:sessionId/window`
* :octicons-x-24: `name`
* :octicons-check-24: `handle`
@@ -0,0 +1,650 @@
---
title: Migrating to Appium 4
---
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 (including behavioral fixes), as well as suggestions for
handling them.
Appium 3 was largely focused on cleaning up old endpoints, whereas Appium 4 includes a wider range
of changes, including closer alignment to W3C WebDriver protocol requirements, removal of
deprecated items not limited to endpoints, and general modernizing of the codebase.
## 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 a Node.js version in the range `^20.19.0 || ^22.12.0 || >=24.0.0`. Appium 4 drops
Node 20 and bumps the required range to `^22.22.2 || ^24.15.0 || >=26.0.0`. The minimum `npm`
version remains unchanged.
!!! info "Actions Needed"
Upgrade Node.js to `v22.22.2` or newer
### ESM-Only
Every package in the Appium monorepo (`appium` and its dependents) is now published as ESM-only,
with no CommonJS build. `require()` no longer works for these packages.
!!! info "Actions Needed"
Import Appium packages using ESM `import` syntax, or use dynamic `import()` from CommonJS code
### JSONWP/MJSONWP Internals Removed
The release of Appium 2 removed general support for the obsolete JSONWP and MJSONWP protocols,
though some Appium-related packages using JSONWP/MJSONWP under the hood could still continue to
function.
Appium 4 removes most of the remaining internal code that supported these protocols, which means
that drivers that still relied on these protocols are no longer compatible with Appium 4. Most
notably, this includes the Windows driver ([see dedicated section below](#windows-driver-replaced-by-wincore-driver)). All other official drivers rely on the W3C protocol and are not affected by this change.
Despite this, Appium 4 does retain temporary support for a select few JSONWP/MJSONWP endpoints (see
[Deprecated Endpoints Removed](#deprecated-endpoints-removed)).
For extension developers, a more detailed breakdown is available [in the Developer-Facing Changes
section below](#jsonwpmjsonwp-removal).
!!! info "Actions Needed"
Migrate to drivers that support the W3C WebDriver protocol
### Windows Driver Replaced by WinCore Driver
As part of [dropping support for the deprecated JSONWP and MJSONWP protocols](#jsonwpmjsonwp-support-fully-removed),
Appium 4 is no longer compatible with the Windows driver ([`appium-windows-driver`](https://github.com/appium/appium-windows-driver)),
since the driver cannot support the standard W3C WebDriver protocol. As a result, the
`appium driver install windows` CLI command also no longer works.
Its official replacement is the WinCore driver ([`appium-wincore-driver`](https://github.com/appium/appium-wincore-driver)),
which can be installed using the new `appium driver install wincore` command. Since the WinCore
driver is also compatible with Appium 3, you can safely migrate from the Windows driver while
remaining on Appium 3 - though note that the installation shortcut is only available since Appium 4.
!!! info "Actions Needed"
If you currently use the Windows driver with Appium 3, you can migrate as follows:
1. Install `appium-wincore-driver` v3: `appium driver install --source=npm appium-wincore-driver@3`
2. Migrate your tests to use the WinCore driver
3. Uninstall the Windows driver: `appium driver uninstall windows`
4. Upgrade to Appium 4
5. Update the WinCore driver to its Appium 4 version: `appium driver update wincore --unsafe`
### Deprecated Endpoints Removed
Similarly to Appium 3, Appium 4 also removes several previously deprecated server endpoints, all of
which belong to the obsolete JSONWP/MJSONWP protocols. Compared to Appium 3, this cleanup is much
smaller (12 removed endpoints total, compared to 98 for Appium 3).
All removed endpoints are listed below, along with their replacement endpoints or driver-specific
execute methods:
* `GET /session/:sessionId`
* :octicons-arrow-right-24: [`GET /session/:sessionId/appium/capabilities`](../reference/api/appium.md#getappiumsessioncapabilities) (for capabilities)
* :octicons-arrow-right-24: [`GET /session/:sessionId/appium/events`](../reference/api/appium.md#getlogevents) (for event timings)
* `POST /session/:sessionId/ime/activate`
* :octicons-check-24: Moved to [UiAutomator2](https://appium.github.io/appium-uiautomator2-driver/)/[Espresso](https://appium.github.io/appium-espresso-driver/) drivers
* `GET /session/:sessionId/ime/activated`
* :octicons-check-24: Moved to UiAutomator2/Espresso drivers
* `GET /session/:sessionId/ime/active_engine`
* :octicons-check-24: Moved to UiAutomator2/Espresso drivers
* `GET /session/:sessionId/ime/available_engines`
* :octicons-check-24: Moved to UiAutomator2/Espresso drivers
* `POST /session/:sessionId/ime/deactivate`
* :octicons-check-24: Moved to UiAutomator2/Espresso drivers
* `GET /session/:sessionId/location`
* :octicons-check-24: Moved to [XCUITest](https://appium.github.io/appium-xcuitest-driver/) driver
* :octicons-arrow-right-24: `mobile: getGeolocation` (UiAutomator2/Espresso drivers)
* :octicons-arrow-right-24: `mobile: getSimulatedLocation` (XCUITest driver)
* `POST /session/:sessionId/location`
* :octicons-check-24: Moved to UiAutomator2/Espresso/XCUITest drivers
* :octicons-arrow-right-24: `mobile: setGeolocation` (UiAutomator2/Espresso drivers)
* :octicons-arrow-right-24: `mobile: setSimulatedLocation` (XCUITest driver)
* `GET /session/:sessionId/network_connection`
* :octicons-check-24: Moved to UiAutomator2/Espresso drivers
* :octicons-arrow-right-24: `mobile: getConnectivity` (UiAutomator2/Espresso drivers)
* `POST /session/:sessionId/network_connection`
* :octicons-check-24: Moved to UiAutomator2/Espresso drivers
* :octicons-arrow-right-24: `mobile: setConnectivity` (UiAutomator2/Espresso drivers)
* `POST /session/:sessionId/receive_async_response`
* :octicons-arrow-right-24: [`POST /session/:sessionId/execute/async`](../reference/api/webdriver.md#executeasync)
* `GET /session/:sessionId/element/:elementId`
* :octicons-no-entry-24: JSONWP protocol command stub with no direct replacement
#### Remaining Deprecations
There are 7 more deprecated endpoints that are intentionally _not_ removed in Appium 4. They are
the last remaining JSONWP/MJSONWP endpoints supported by Appium, and are listed in the
[JSONWP](../reference/api/jsonwp.md) and [MJSONWP](../reference/api/mjsonwp.md) reference pages.
These endpoints are kept due to their common usage (for context management), and their deprecation
being somewhat recent. All of them are therefore planned to be removed in the next major Appium
version, in order to give users time to migrate to their replacements.
!!! info "Actions Needed"
Check your Appium client documentation for the affected methods, and adjust your code to use
their replacements
### `--allow-cors` Flag Renamed
In Appium 3, the `--allow-cors` server argument was the only feature flag not integrated with
Appium's default [insecure feature flag mechanism](./security.md). Appium 4 removes this argument
and replaces it with the `cors` insecure feature name. This means the feature can now be controlled
using the `--allow-insecure`, `--deny-insecure` and `--relaxed-security` flags.
Similarly to the `session_discovery` feature, the `cors` feature must also be prefixed with the
wildcard (`*`) symbol.
!!! info "Actions Needed"
Replace `--allow-cors` with `--allow-insecure=*:cors` or `--relaxed-security`
### Default Log Level Set to Info
The default logging level for server logs has been 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 Command Output Streams Split
In Appium 3, all non-JSON output of Appium's CLI commands (`driver`/`plugin`/`setup`, but not
`server`) was written to `stderr`. Appium 4 now writes `log`/`ok`/`info`/`warn` level messages of
these commands to `stdout`, while `error` level output remains in `stderr`.
The log output of the server itself (the `server` command) is unaffected, since it already split
streams this way.
!!! info "Actions Needed"
If you use scripts that capture non-JSON output of `driver`/`plugin`/`setup` commands, make
sure they read from the correct stream
### SPDY/HTTP2 No Longer Supported
In Appium 3, the server could be launched with SSL/TLS, which enabled support for the SPDY and
HTTP/2 protocols. This functionality relied on the unmaintained `spdy` package. Appium 4 replaces
this package with Node's native `https` module, which only supports 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) or use a proxy
### Selenium Grid 3 Support Removed
Support for Selenium Grid 3 hub has been removed, along with its `--nodeconfig` server
argument/config file property. Selenium Grid 4 remains supported.
!!! info "Actions Needed"
If you relied on Grid 3 registration, use the [Selenium Grid 4 relay feature](./grid.md) instead
### Post-Install Script Removed
Appium 2 and 3 supported a postinstall hook that allowed for installation of drivers/plugins
together with the Appium server itself:
```
npm install -g appium --drivers=... --plugins=...
```
Since postinstall hooks are a supply-chain security risk, and newer versions of `npm` disable them
by default, this hook has been removed in Appium 4.
!!! info "Actions Needed"
If you install drivers/plugins using the postinstall hook, migrate to Appium's
[`install`](../reference/cli/extensions.md#install) command, or declare extensions in your
project's `package.json` file
### Event Timings Capability Removed
In Appium 3, the `appium:eventTimings` capability could be set to `true` in order to include event
timing information in the response of the `GET /session/:sessionId` endpoint. Since Appium 4
removes this endpoint (see [Deprecated Endpoints Removed](#deprecated-endpoints-removed)), the
capability became obsolete, and has also been removed.
!!! info "Actions Needed"
Remove any instances of the `appium:eventTimings` capability, and instead use the [getLogEvents](../reference/api/appium.md#getlogevents) endpoint to retrieve event timings
### Unused Server Arguments Removed
The following server arguments have had no effect for a long time and have been removed, along with
their equivalents in the configuration file:
* `--callback-address` (`-ca`)
* `--callback-port` (`-cp`)
* `--debug-log-spacing`
* `--shell`
* `--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
## Behavior Fixes
This is a list of smaller changes that are positioned as fixes, rather than removal of existing
functionality, and may or may not be breaking depending on your usecase. The majority of changes
are related to alignment with the W3C WebDriver specification, and should not generally require
any user action.
### Session Lifecycle
* If a successfully created session fails initialization, 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))
* The new command idle timeout (`appium:newCommandTimeout`) now only starts after finishing the
setup of initial settings and BiDi responses, preventing a session from expiring during
initialization ([#22881](https://github.com/appium/appium/pull/22881))
* If the new command idle timeout expires and the session deletion process starts, any driver
cleanup failures are now logged, instead of becoming an unhandled rejection that can terminate
the server and unrelated sessions ([#22964](https://github.com/appium/appium/pull/22964))
* Upon session deletion, any commands still waiting in the command queue are now rejected with HTTP
`404` / `invalid session id`, instead of remaining pending indefinitely
([#22874](https://github.com/appium/appium/pull/22874))
### Capabilities
* Names of standard WebDriver capabilities must now be case-sensitive (e.g. `platformName`, not
`PlatformName`). Handling of the _values_ of these capabilities, as well as names of prefixed
capabilities (e.g. `appium:app`), remains unchanged
([#22976](https://github.com/appium/appium/pull/22976))
* Malformed capability containers and parse failures now return HTTP `400` / `invalid argument`,
instead of HTTP `500` / `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))
* Session settings provided inside `appium:options` are now honored
([#22882](https://github.com/appium/appium/pull/22882))
* The standard `timeouts` capability now correctly initializes BaseDriver's timeout state using
the same value checks as the [Set Timeouts](../reference/api/webdriver.md#timeouts) endpoint.
Backend handling remains driver-specific ([#22983](https://github.com/appium/appium/pull/22983))
* Setting both `appium:noReset` and `appium:fullReset` to `true` now returns HTTP `500` /
`session not created` instead of a generic error, and no longer leaves partially initialized
session state behind ([#22861](https://github.com/appium/appium/pull/22861),
[#22870](https://github.com/appium/appium/pull/22870))
* Setting `appium:newCommandTimeout` to `null`, a blank string, or a non-finite numeric value is
now ignored (keeping the default timeout value), instead of causing the timeout to be silently
disabled ([#22815](https://github.com/appium/appium/pull/22815),
[#23103](https://github.com/appium/appium/pull/23103)). Other invalid types, such as negative
numbers or nonnumeric strings, still fail capability validation
* Percent-encoded credentials in `appium:app` URLs are now decoded once before Basic authentication,
and complete HTTP-compressed downloads are no longer rejected if the decompressed file size
differs from `Content-Length` ([#22887](https://github.com/appium/appium/pull/22887),
[#22886](https://github.com/appium/appium/pull/22886))
* Setting `firstMatch` to an empty array (`[]`) is now rejected with HTTP `400` /
`invalid argument`, instead of being silently replaced with a single empty entry
([#22860](https://github.com/appium/appium/pull/22860))
* Every `firstMatch` candidate is now validated and merged before selection. Invalid supplied values
or duplicate keys in any candidate reject the request, even if 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 (which can be set using `--default-capabilities`) are now applied to each
individual `firstMatch` candidate separately. Values set in `alwaysMatch` or the current
candidate still take precedence ([#22988](https://github.com/appium/appium/pull/22988)). See
[Default Capabilities](../reference/cli/server.md#default-capabilities) for an example.
* Session settings are now applied from the selected `firstMatch` candidate, instead of potentially
being overridden by later unselected candidates. Defaults and `alwaysMatch` settings are still
applied first ([#22882](https://github.com/appium/appium/pull/22882))
* Values of the standard `timeouts` capability are now also validated in `alwaysMatch` and every
`firstMatch` candidate ([#23062](https://github.com/appium/appium/pull/23062)). A malformed
timeout object or invalid standard timeout value is rejected with HTTP `400` /
`invalid argument`, even if an earlier `firstMatch` candidate could match. Top-level
`timeouts: null` is still omitted, nested null timeout values are preserved, and unknown timeout
properties are ignored for validation
### HTTP Responses
* All WebDriver HTTP responses now include the `Cache-Control: no-cache` header. Caches must
revalidate these responses before reusing them
([#22971](https://github.com/appium/appium/pull/22971))
* Any WebDriver error `data` (e.g. alert text) is now preserved in driver and proxy responses
([#22974](https://github.com/appium/appium/pull/22974))
* The body of any `POST` command now has to be a JSON object; requests where the body is empty or
set to an empty JSON array (`[]`) return HTTP `400` with `invalid argument`
([#23007](https://github.com/appium/appium/pull/23007)). A value of `{}` is still accepted,
including commands that take no parameters. Routes that set `payloadParams.wrap` may still accept
an empty array
* `GET` requests to the server root route (e.g. `GET /` for a default server base path) now return
HTTP `404` / `unknown command`, instead of Express's HTML 404 page. Extensions can still register
their own root route ([#22969](https://github.com/appium/appium/pull/22969))
* `HEAD` requests to any `GET` command URL now return HTTP `405` without executing the `GET`
command. Custom server routes and `HEAD` request handlers retain their behavior
([#22992](https://github.com/appium/appium/pull/22992))
* Driver commands rejecting with `null` or `undefined` now return HTTP `500` / `unknown error`,
instead of causing the error handler itself to fail
([#22869](https://github.com/appium/appium/pull/22869))
* `TimeoutError` and `ScriptTimeoutError` now map to HTTP `500` instead of HTTP `408`
([#22686](https://github.com/appium/appium/pull/22686))
* A `detached shadow root` error is now preserved as HTTP `404`, instead of being converted to HTTP
`500` / `unknown error` ([#22968](https://github.com/appium/appium/pull/22968))
### Specific Endpoints
* [New Session](../reference/api/webdriver.md#createsession): now raises the `session not created`
error instead of `unknown error` if no installed driver matches the requested platform/automation
combination, or the matching driver cannot be loaded or does not export its class. Validation of
missing `platformName`/`appium:automationName` is unchanged
([#22970](https://github.com/appium/appium/pull/22970))
* [Get Timeouts](../reference/api/webdriver.md#gettimeouts): now returns `script` and `pageLoad` in
addition to the existing `implicit` and Appium-specific `command` values
([#22776](https://github.com/appium/appium/pull/22776))
* [Set Timeouts](../reference/api/webdriver.md#timeouts): `script`, `pageLoad`, and `implicit` now
only accept null or integer values; strings and fractions return HTTP `400` / `invalid argument`
([#22961](https://github.com/appium/appium/pull/22961))
* Set Timeouts: `null` values now correctly disable the specified timeout
([#22961](https://github.com/appium/appium/pull/22961))
* Set Timeouts: omitting `script`, `pageLoad`, or `implicit` no longer fails: 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))
* Set Timeouts: unknown values for the legacy `type` argument now return HTTP `400` /
`invalid argument` instead of HTTP `500` / `unknown error`
([#22987](https://github.com/appium/appium/pull/22987))
* [getLog](../reference/api/others.md#getlog): unsupported `type` values now return HTTP `400` /
`invalid argument` instead of HTTP `500` / `unknown error`
([#22986](https://github.com/appium/appium/pull/22986))
### BiDi Protocol
* Subscriptions without `contexts` now receive events carrying a context, so that 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 implementation of [`session.subscribe`](../reference/api/bidi.md#bidisubscribe) (which does
not yet support `userContexts`) now rejects `userContexts` with HTTP `500` /
`unsupported operation`, instead of silently creating an unscoped subscription. Malformed
`userContexts`, or supplying both `contexts` and `userContexts`, return HTTP `400` /
`invalid argument`. Drivers implementing user-context support can override `bidiSubscribe`
([#22877](https://github.com/appium/appium/pull/22877)).
* Any `sessionId` or `id` keys inside BiDi command parameters are now validated like any other
unknown parameter, instead of being silently accepted
([#22771](https://github.com/appium/appium/pull/22771))
### [Storage Plugin](https://github.com/appium/appium/tree/master/packages/storage-plugin)
* All [endpoints](../reference/api/plugins.md#storage-plugin) and stream/events WebSocket paths now
respect custom server `--base-path` values, instead of always being mounted at the server root
([#22906](https://github.com/appium/appium/pull/22906))
* File names starting with a dot or ending with `.filepart` are now rejected
([#22863](https://github.com/appium/appium/pull/22863),
[#22959](https://github.com/appium/appium/pull/22959))
* Concurrent WebSocket uploads of the same file are now rejected
([#22962](https://github.com/appium/appium/pull/22962))
* Pending upload WebSocket routes are now preserved across retries
([#22948](https://github.com/appium/appium/pull/22948))
* Errors on upload event sockets are now logged instead of potentially terminating the server
([#22963](https://github.com/appium/appium/pull/22963))
### Other Fixes
* The `appium driver`/`appium plugin` CLI commands now lock the extension manifest while running,
preventing any concurrent CLI commands from silently clobbering each other's changes
([#22767](https://github.com/appium/appium/pull/22767))
* IPv6 literals are now correctly bracketed during URL assembly, including BiDi WebSocket URLs and
[Execute Driver Plugin](https://github.com/appium/appium/tree/master/packages/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))
## Developer-Facing Changes
The following changes primarily affect developers of Appium clients, drivers, plugins, or tools
that import Appium's internal packages directly. They do not affect most Appium end users.
### Extension Development
* When installing a locally available driver/plugin using Appium's CLI `install` command with the
`--source=local` flag, Appium now installs the extension directly from the supplied local
directory, without first creating an intermediate global `npm` link
([#23086](https://github.com/appium/appium/pull/23086)). Local installs still link to the source
directory, so source changes do not require reinstalling the extension. If you use tooling that
relies on the package being linked in `npm`'s global prefix, manage that global link separately.
* 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 ([#22732](https://github.com/appium/appium/pull/22732))
* `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
([#22754](https://github.com/appium/appium/pull/22754))
* When a plugin intercepts a command that would otherwise be proxied downstream, Appium no longer
validates its fields or values ([#23060](https://github.com/appium/appium/pull/23060)). This
matches direct proxying, where the downstream remote end owns these checks. Plugins that
previously relied on Appium to validate these arguments can now receive missing or invalid
values (JSON-object request-body checks still remain in place before the plugin is called). If
your plugin handles such a command without calling `next()`, validate the inputs it uses.
Pass-through plugins can still call `next()` and delegate validation to the downstream remote end.
### `@appium/base-driver`
#### JSONWP/MJSONWP Removal
As listed [in the Breaking Changes section above](#jsonwpmjsonwp-internals-removed), the internal
code responsible for JSONWP/MJSONWP protocol support has been removed
([#22681](https://github.com/appium/appium/pull/22681)). This includes the following items:
* `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
* `statusCodes` and `getSummaryByCode` are no longer available (the `jsonwp-status` module has been
removed)
* A command result shaped like `{protocol, value}` is no longer unwrapped. It is sent to the
client as the command value, so a script returning `{protocol: 'W3C', value: 5}` gets that object
back. A result of `{protocol, error}` whose `protocol` is `W3C` or `MJSONWP` and whose `error`
is an `Error` is still thrown
#### HTTP Server Moved to Separate Package
The HTTP/Express server code has been extracted from `@appium/base-driver` into the new
`@appium/http-server` package ([#22800](https://github.com/appium/appium/pull/22800)). The
following items are therefore no longer exported from `@appium/base-driver` or `appium/driver.js`:
* Methods: `server`, `normalizeBasePath`, `configureServer`
* Types: `ServerOpts`, `ConfigureServerOpts`, `ConfigureHttpOpts`, `StartServerOpts`
If you use these items, import them from `@appium/http-server` instead.
#### Other Changes
* The default class export has been removed
([#22741](https://github.com/appium/appium/pull/22741)). Use the named `{BaseDriver}` export
instead
* The default export of `basedriver/helpers.js` has been removed
([#22741](https://github.com/appium/appium/pull/22741)). Use named exports instead
* `BaseDriver`'s constructor no longer takes command-mixin machinery into account
([#22741](https://github.com/appium/appium/pull/22741))
* `createSession()` no longer accepts the legacy multi-argument form
(`jwpCaps, reqCaps, w3cCaps`); only the single `w3cCapabilities` argument is supported now
([#22730](https://github.com/appium/appium/pull/22730))
* The deprecated `driverData` mechanism (`Core.driverData`, the `driverData` parameter on
`createSession`/`deleteSession`, and the `DriverData` type) has been removed
([#22716](https://github.com/appium/appium/pull/22716)). Migrate to `IAppiumIpc` instead
* The `server()` and `configureServer()` methods (now exported by `@appium/http-server`) now
default to `allowCors: false` ([#22990](https://github.com/appium/appium/pull/22990))
* Both `ipc.unsubscribe(topic, subscriber)` and `subscription.unsubscribe()` now finish waiting on
async iterators and notify observers once ([#22955](https://github.com/appium/appium/pull/22955)).
An observer that throws still propagates its error to the caller, but no longer prevents waiting
iterators from finishing
* The unused `reset()` method has been removed
([#22741](https://github.com/appium/appium/pull/22741))
* The `generateDriverLogPrefix()` helper no longer supports the deprecated `sessionId` argument
([#22764](https://github.com/appium/appium/pull/22764))
* The new command idle timeout now stays suspended through HTTP/BiDi plugin chains, including work
after `next()`, and while proxied BiDi commands await a response
([#22946](https://github.com/appium/appium/pull/22946),
[#22947](https://github.com/appium/appium/pull/22947)). Custom `startNewCommandTimeout`
implementations must delegate timer creation to `super.startNewCommandTimeout()` so they respect
shared activity, while custom command execution must delegate to `super.executeCommand()` or wrap
the full command in `runWithCommandTimeout`
* The `appium:autoLaunch`, `appium:locale`, `appium:language`, `appium:autoWebview`,
`appium:orientation`, and `appium:udid` capabilities are no longer validated as part of the
standard capability constraints ([#22950](https://github.com/appium/appium/pull/22950),
[#22952](https://github.com/appium/appium/pull/22952)). If you use them, declare them in your own
driver constraints
* 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 ([#22680](https://github.com/appium/appium/pull/22680)). If you relied on these fixtures,
migrate to use your own local test server
### `@appium/base-plugin`
* The default class export has been removed ([#22718](https://github.com/appium/appium/pull/22718)).
Use the named `{BasePlugin}` export instead
* `Plugin#logger` has been removed in favor of `Plugin#log`
([#22718](https://github.com/appium/appium/pull/22718))
* `Plugin#name` and `Plugin#cliArgs` are now read-only
([#22718](https://github.com/appium/appium/pull/22718))
### `@appium/logger`
* The default class export has been removed ([#22719](https://github.com/appium/appium/pull/22719)).
Use the named `{log}` export instead
* The long-disabled, no-op progress/unicode methods (`enableProgress`, `disableProgress`,
`progressEnabled`, `enableUnicode`, `disableUnicode`) have been removed
([#22719](https://github.com/appium/appium/pull/22719))
### `@appium/support`
* `env`: the module has been integrated into the `appium` package as internal helpers
([#22708](https://github.com/appium/appium/pull/22708))
* `imageUtil`: the module has been removed ([#22678](https://github.com/appium/appium/pull/22678))
* `fs`: the following exports have been removed: `readPackageJsonFrom` and `findRoot` methods,
`F_OK`/`R_OK`/`W_OK`/`X_OK` constants, `CopyFileDescriptor` and `ReadFn` types
([#22678](https://github.com/appium/appium/pull/22678))
* `fs`: `glob()` now relies on Node's `fs.promises.glob` instead of the `glob` package, with a
narrower `GlobOptions` shape ([#22735](https://github.com/appium/appium/pull/22735))
* `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 are skipped; `ENOENT` still rejects the
returned promise ([#22956](https://github.com/appium/appium/pull/22956))
* `fs`: `write()` now relies on Node's `promisify`, which resolves to an object
(`{bytesWritten, buffer}`), instead of only `bytesWritten`
([#22704](https://github.com/appium/appium/pull/22704))
* `logger`: the default export has been removed. Use the named `{log}` export instead
([#22719](https://github.com/appium/appium/pull/22719))
* `logger`: `errorAndThrow` has been removed in favor of `errorWithException`
([#22719](https://github.com/appium/appium/pull/22719))
* `net`: support for FTP-based file uploads has been removed, along with the `FtpUploadOptions` and
`NotHttpUploadOptions` types ([#22678](https://github.com/appium/appium/pull/22678))
* `net`: the new `checkPortStatus()` and `getFreePort()` helpers replace the `portscanner` package
and custom free port lookups in drivers and plugins
([#23156](https://github.com/appium/appium/pull/23156)). Unlike `portscanner`, they accept an
options object instead of positional arguments. `getFreePort()` also covers `findAPortNotInUse()`:
pass `port` (a single port or a `[startPort, endPort]` range) to probe specific ports, in which
case it throws `NoFreePortError` if all of them are busy instead of resolving with `false`
* `mjpeg`: the module has been removed ([#22678](https://github.com/appium/appium/pull/22678))
* `mkdirp`: the module has been removed ([#22678](https://github.com/appium/appium/pull/22678)).
Import `mkdirp()` from the `fs` module instead
* `node`: `requirePackage()` has been removed
([#22684](https://github.com/appium/appium/pull/22684))
* `npm`: the module has been integrated into the `appium` package as internal helpers
([#22721](https://github.com/appium/appium/pull/22721))
* `process`: the module has been removed ([#22678](https://github.com/appium/appium/pull/22678))
* `system`: `macOsxVersion()` has been removed
([#22684](https://github.com/appium/appium/pull/22684))
* `util`: the following methods have been removed: `uuidV1`, `uuidV3`, `uuidV5`, `localIp`,
`cancellableDelay`, and `multiResolve` ([#22678](https://github.com/appium/appium/pull/22678))
* `zip`: `extractAllTo()` always uses the built-in JS extractor now, so the `useSystemUnzip` option
has been removed ([#23091](https://github.com/appium/appium/pull/23091)). 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. 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`
### `@appium/tsconfig`
* `noImplicitOverride` is now set to `true`, which means that TypeScript drivers and plugins that
extend the config must mark every member overriding a base class member (e.g. `BaseDriver` or
`BasePlugin` methods, and `static` fields such as `newMethodMap`) with the `override` modifier
([#23143](https://github.com/appium/appium/pull/23143)). Opt-out is also possible by setting
`noImplicitOverride` to `false` in your own `tsconfig.json`.
### Client Development
* State-changing HTTP requests with an `Origin` header now return HTTP `403`, unless the CORS
insecure feature is enabled on the Appium server
([#23102](https://github.com/appium/appium/pull/23102)). Remove the `Origin` header or instruct
users to enable CORS.
## New Features
### App URL Rules
The new `--app-url-rules` server CLI flag (and its equivalent configuration file property) allows
server hosts to restrict the host/IP values allowed in capabilities like `appium:app`. Configurable
properties include allow/deny lists, HTTPS-only, credentials, and redirect limits.
Refer to [the Security guide](./security.md#restricting-remote-app-urls) for more details.
### CLI Update Command Checks Server Requirement
As a follow-up to Appium 3.9, which changed the default behavior of the Appium CLI
[`install`](../reference/cli/extensions.md#install) command to install the newest extension version
that is compatible with the current server (as opposed to throwing an error if the server
requirement of the latest extension version is incompatible), Appium 4 adjusts the
[`update`](../reference/cli/extensions.md#update) command to now implement this behavior as well.
This makes default usage of both `install` and `update` safe even when future major extension
versions raise their server requirements.
### Commands Can Bypass Queue
Driver commands (endpoints) are now allowed to bypass the default command queue, which enables the
command to execute while another long-running command is in progress.
By default, only the standard [Status](../reference/api/webdriver.md#getstatus) endpoint is exempt
from the queue, but driver developers can also customize the list of exempt commands by overriding
the `queueExemptCommands` getter.
The behavior of the idle timer (set by `appium:newCommandTimeout`) has also been adjusted to only
reset once no commands are executing.
### New Set Timeouts Parameter
The standard [Set Timeouts](../reference/api/webdriver.md#timeouts) endpoint has been extended to
additionally accept the Appium-specific `command` parameter. This aligns its handling with
[Get Timeouts](../reference/api/webdriver.md#gettimeouts), which already returned this parameter.
The parameter itself is mapped to the `appium:newCommandTimeout` capability.
The non-standard `type`/`ms` parameters, which were previously the only way to change this timeout
mid-session, have been deprecated as a result.
### Universal XML Plugin Can Be Toggled
The [Universal XML plugin](https://github.com/appium/appium/tree/master/packages/universal-xml-plugin)
now provides the [`universalXml: setEnabled` and `universalXml: isEnabled` execute methods](../reference/api/plugins.md#universal-xml-plugin),
which allow toggling the translation during a session, e.g. to retrieve the original page source.
+119
View File
@@ -37,6 +37,10 @@ sessions the feature should apply. The prefix can be either the driver's `automa
wildcard (`*`) symbol, if the feature should be applied to all drivers. The prefix and feature name
are separated using the colon character (`:`).
Use the driver's `automationName`, which may differ from its install name. An install-name-only
prefix does not enable or disable a feature. When configuring a session, Appium warns about such
entries for that driver unless the prefix is another installed driver's `automationName`.
For example, `first:foo` refers to the `foo` feature for the `first` driver, whereas `*:bar` refers
to the `bar` feature for all drivers.
@@ -85,3 +89,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
+1 -1
View File
@@ -99,7 +99,7 @@ a driver does this by relying on platform-specific automation technologies. For
maintains an iOS automation technology called
[XCUITest](https://developer.apple.com/documentation/xctest/user_interface_tests). The Appium
driver that supports iOS app automation is called the [XCUITest
Driver](https://github.com/appium/appium-xcuitest-driver) because ultimately what it does is
Driver](https://appium.github.io/appium-xcuitest-driver/) because ultimately what it does is
convert the WebDriver protocol to XCUITest library calls.
One of the reasons that drivers are independent, pluggable modules is that they work completely
+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
+5 -5
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 {
}
@@ -49,8 +49,8 @@ async setUrl(url) {
[^1]: You might notice that `setUrl` doesn't look anything like `Navigate To`, so how did we know
to use it rather than some other random string? Well, Appium's WebDriver-protocol-to-method-name
mapping is defined in a special file within the `@appium/base-driver` package called
[routes.js](https://github.com/appium/appium/blob/master/packages/base-driver/lib/protocol/routes.js).
mappings are defined in special files within the `@appium/base-driver` package's
[routes/](https://github.com/appium/appium/tree/master/packages/base-driver/lib/protocol/routes) directory.
So if you're writing a driver, this is where you would go to figure out what method names to use
and what parameters to expect. Or you could look at the source for any of the main Appium
drivers!
@@ -81,7 +81,7 @@ from there is up to you, the driver author!
## Automation mapping
But *typically* what driver authors want to do is to provide automation behaviours for a given
platform(s) that are semantically very similar to the the WebDriver spec implementations for
platform(s) that are semantically very similar to the WebDriver spec implementations for
browsers. When you want to find an element, you should get a reference to a UI element. When you
want to click or tap that element, the resulting behaviour should be the same as if a person were
to click or tap on the element. And so on.
@@ -175,7 +175,7 @@ be passed back directly to the client, without any XCUITest driver code seeing i
This architectural pattern provides a nice bonus for driver authors who choose to deal with the
WebDriver protocol everywhere, rather than constructing bespoke protocols. It also means that
Appium can create wrapper drivers for any other existing WebDriver implementation very easily. If
you look at the [Appium Safari driver](https://github.com/appium/appium-safari-driver) code, for
you look at the [Appium Safari driver](https://appium.github.io/appium-safari-driver/) code, for
example, you'll see that it implements basically no standard commands, because all of these are
proxied directly to an underlying SafariDriver process.
+149
View File
@@ -0,0 +1,149 @@
# Appium
Appium is an open-source framework for UI automation of many app platforms - mobile (iOS,
Android, Tizen), browser (Chrome, Firefox, Safari), desktop (macOS, Windows), TV (Roku, tvOS,
Android TV, Samsung), smartwatch (watchOS, Android Wear) and more - driven over the W3C WebDriver
protocol.
Appium is split into four parts: the Appium server, which defines the core APIs; drivers, which
implement connectivity to a specific platform; clients, which implement Appium's API in a specific
programming language; and plugins, which change or extend the server's behaviour. To automate
anything you need the server, at least one driver, and a client.
Notes for tools generating Appium code from this documentation:
- Appium 4 is the current major version, and these pages describe Appium 4. Older Appium versions
are not supported, but the documentation for Appium 2 and 3 is still available under their own
versions of this site, reachable from the version selector.
- Drivers are not bundled with the server. Install them explicitly, for example
`appium driver install uiautomator2` or `appium driver install xcuitest`.
- Appium 4 requires Node.js in the range `^22.22.2 || ^24.15.0 || >=26.0.0`, and npm 10 or newer.
- Standard W3C capabilities are written without a prefix. `platformName`, `browserName` and
`browserVersion` are the ones used most often with Appium, and the rest of the specification's
set - `acceptInsecureCerts`, `pageLoadStrategy`, `timeouts`, `webSocketUrl` and so on - is also
accepted, mostly by the browser drivers. Every Appium-specific capability needs the `appium:`
vendor prefix - `appium:automationName`, `appium:app`, `appium:udid` - or must be nested,
unprefixed, inside an `appium:options` object.
- Capabilities cannot be changed once a session has started. Parameters that are adjustable during
a session are exposed through the Settings API instead.
- Driver-specific commands are usually Execute Methods rather than new client methods: call the
standard `Execute Script` command with a known command name such as `mobile: terminateApp`, and
pass a *single* object whose keys are the parameter names. Do not invent client-side method names
for these.
- Beyond the small common set documented here, capabilities, execute methods, settings and locator
strategies are defined by each driver and documented on that driver's own site. Refer users to
the driver's documentation rather than guessing names.
- If upgrading to a new major version of Appium, always check all relevant migration guides. Every
major version has its own dedicated migration guide.
- Appium 4 moves to ESM-only packaging and no longer supports the Windows driver. Refer users to
the WinCore driver instead.
- Appium 4 replaces the `--allow-cors` server flag with `--allow-insecure=*:cors`. Do not use the
former flag anymore.
- Appium 3 and 4 removed various deprecated endpoints. Check the respective migration guides before
using an endpoint carried over from older code.
- In Appium 3, every `--allow-insecure` feature name must carry a scope prefix: either a driver
name (`uiautomator2:adb_shell`) or the wildcard (`*:adb_shell`).
- WebDriver BiDi support is opt-in per session, via the standard `webSocketUrl` capability.
- Contexts model the difference between native and webview modes. `Get Contexts` always returns at
least one context, conventionally named `NATIVE_APP`.
## Getting started
- [Appium Documentation](https://appium.io/docs/en/latest/): documentation home for the Appium server and its ecosystem.
- [Appium in a Nutshell](https://appium.io/docs/en/latest/intro/): how the server, drivers, clients and plugins fit together, and what you need to install.
- [How Does Appium Work?](https://appium.io/docs/en/latest/intro/appium/): the goals and architecture of the Appium project.
- [Intro to Appium Drivers](https://appium.io/docs/en/latest/intro/drivers/): what a driver is, and how Appium supports unrelated platforms through them.
- [Intro to Appium Clients](https://appium.io/docs/en/latest/intro/clients/): Appium's client-server architecture and its relationship to the W3C WebDriver specification.
- [Getting Started](https://appium.io/docs/en/latest/quickstart/): the quickstart path - install Appium, install a driver, install a client, run a first script.
- [System Requirements](https://appium.io/docs/en/latest/quickstart/requirements/): supported operating systems and the required Node.js and npm versions.
- [Install Appium](https://appium.io/docs/en/latest/quickstart/install/): installing the Appium server with npm.
- [Install the UiAutomator2 Driver](https://appium.io/docs/en/latest/quickstart/uiauto2-driver/): installing an Android driver and its Android SDK prerequisites.
- [Write a Test (JS)](https://appium.io/docs/en/latest/quickstart/test-js/): a first Appium script in JavaScript with WebdriverIO.
- [Write a Test (Python)](https://appium.io/docs/en/latest/quickstart/test-py/): a first Appium script in Python.
- [Write a Test (Java)](https://appium.io/docs/en/latest/quickstart/test-java/): a first Appium script in Java.
- [Write a Test (Ruby)](https://appium.io/docs/en/latest/quickstart/test-rb/): a first Appium script in Ruby.
- [Write a Test (.NET)](https://appium.io/docs/en/latest/quickstart/test-dotnet/): a first Appium script in C#.
- [Next Steps](https://appium.io/docs/en/latest/quickstart/next-steps/): where to go after the quickstart.
## Drivers, clients and plugins
Which driver you install determines which platform you can automate, and the driver's own
documentation is the authoritative source for its capabilities and commands.
- [The Appium Ecosystem](https://appium.io/docs/en/latest/ecosystem/): index of officially-supported and community-supported Appium projects.
- [Appium Drivers](https://appium.io/docs/en/latest/ecosystem/drivers/): every known driver with its install command and documentation link. Official drivers include UiAutomator2 and Espresso (Android), XCUITest (iOS, iPadOS, tvOS, watchOS), Mac2 (macOS), WinCore, Chromium, Gecko and Safari. Appium 2 and 3 feature the Windows driver instead of WinCore.
- [Appium Clients](https://appium.io/docs/en/latest/ecosystem/clients/): official clients for Java, Python, Ruby and .NET, plus community clients including WebdriverIO, Nightwatch.js, Robot Framework, Rust and Swift.
- [Appium Plugins](https://appium.io/docs/en/latest/ecosystem/plugins/): optional extensions that modify or extend server behaviour. Not required for standard automation.
- [Appium-Related Tools](https://appium.io/docs/en/latest/ecosystem/tools/): tools for installation, test development and inspection, such as Appium Inspector, and tools for driving Appium from an AI agent, such as the Appium MCP server and the Appium agent skills.
- [Managing Drivers and Plugins](https://appium.io/docs/en/latest/guides/managing-exts/): installing, listing, updating and removing extensions, and installing from sources other than the official index.
## Sessions and capabilities
- [Session Capabilities](https://appium.io/docs/en/latest/guides/caps/): how capabilities work, the `appium:` vendor prefix, `appium:options` grouping, BiDi opt-in via `webSocketUrl`, and always-match versus first-match.
- [Session Properties](https://appium.io/docs/en/latest/reference/session/): the key properties of a session - capabilities, execute methods, settings and locator strategies - and which are defined by the server versus the driver.
- [Capabilities](https://appium.io/docs/en/latest/reference/session/caps/): the full list of capabilities recognized by the Appium server and base driver, and therefore supported by every driver.
- [Session Settings](https://appium.io/docs/en/latest/guides/settings/): parameters that, unlike capabilities, can be adjusted while a session is running.
- [Execute Methods](https://appium.io/docs/en/latest/guides/execute-methods/): the `mobile:` command convention that drivers use to expose commands beyond the WebDriver spec, with examples in every official client language.
- [Automating Gestures](https://appium.io/docs/en/latest/guides/gestures/): approaches for automating gestures, including several examples for various touch gestures using the W3C Actions API.
- [Managing Contexts](https://appium.io/docs/en/latest/guides/context/): switching between native and webview modes with the Get Contexts, Get Current Context and Set Context commands.
- [Caching of Application Bundles](https://appium.io/docs/en/latest/guides/caching/): how the base driver caches application builds, and how to use it for faster suite execution.
- [Retrieving Event Timings](https://appium.io/docs/en/latest/guides/event-timing/): startup and command timing information, retrieved from the `POST /session/:sessionId/appium/events` endpoint.
## Server configuration and the command line
- [Command Line Interface](https://appium.io/docs/en/latest/reference/cli/): the `appium` executable and its `server`, `driver`, `plugin` and `setup` subcommands.
- [appium server](https://appium.io/docs/en/latest/reference/cli/server/): the complete set of server command-line arguments.
- [appium driver/plugin](https://appium.io/docs/en/latest/reference/cli/extensions/): the extension management subcommands - `install`, `list`, `update`, `uninstall`, `run` and `doctor`.
- [appium setup](https://appium.io/docs/en/latest/reference/cli/setup/): installing a preset bundle of extensions for mobile, browser or desktop automation.
- [Environment Variables](https://appium.io/docs/en/latest/reference/cli/env-vars/): advanced behaviour toggled through the environment rather than CLI arguments.
- [The Appium Config File](https://appium.io/docs/en/latest/guides/config/): supplying server arguments through a config file instead of the command line. CLI arguments take precedence.
- [Filtering the Appium Log](https://appium.io/docs/en/latest/guides/log-filters/): redacting passwords, device identifiers and other sensitive values from server logs with `--log-filters`.
- [Header Handling](https://appium.io/docs/en/latest/guides/headers/): request ID tracking through the `x-request-id` header.
- [SSL/TLS Support](https://appium.io/docs/en/latest/guides/tls/): starting the server with secure connections enabled.
- [Appium and Selenium Grid](https://appium.io/docs/en/latest/guides/grid/): relaying commands from a Selenium Grid to an Appium server.
## Security
- [Appium Server Security](https://appium.io/docs/en/latest/guides/security/): the insecure-feature mechanism with the `--allow-insecure`, `--deny-insecure` and `--relaxed-security` flags, and configuration options for application URL rules.
- [Insecure Features](https://appium.io/docs/en/latest/reference/cli/insecure-features/): the features defined at the server and official-plugin level, including `session_discovery`, `cors`, and `execute_driver_script`. Drivers define their own in their own documentation.
## API endpoints
- [Reference](https://appium.io/docs/en/latest/reference/): index of the core interfaces supported by the Appium server.
- [API Endpoints](https://appium.io/docs/en/latest/reference/api/): endpoints exposed by the base driver, and therefore inherited by every driver.
- [WebDriver Protocol](https://appium.io/docs/en/latest/reference/api/webdriver/): the W3C WebDriver endpoints Appium supports.
- [WebDriver BiDi Protocol](https://appium.io/docs/en/latest/reference/api/bidi/): the W3C WebDriver BiDi commands Appium supports. These are commands over a WebSocket, not URL endpoints, and require the `webSocketUrl` capability.
- [Appium Protocol](https://appium.io/docs/en/latest/reference/api/appium/): endpoints defined by Appium's own extension of the WebDriver protocol.
- [Plugin Endpoints](https://appium.io/docs/en/latest/reference/api/plugins/): endpoints added or modified by the official plugins.
- [Other Protocols](https://appium.io/docs/en/latest/reference/api/others/): additional supported endpoints, including the Chromedriver protocol extension.
## Migrating between major versions
- [Migrating to Appium 4](https://appium.io/docs/en/latest/guides/migrating-3-to-4/): breaking changes from Appium 3 to 4 - the Node 22 minimum, ESM-only requirement, removed endpoints, the Windows driver replacement, and more.
- [Migrating to Appium 3](https://appium.io/docs/en/latest/guides/migrating-2-to-3/): every breaking change from Appium 2 to 3 - the Node 20 minimum, removed and modified endpoints, the mandatory `--allow-insecure` scope prefix, and the move from `GET /sessions` to `GET /appium/sessions`.
- [Migrating to Appium 2](https://appium.io/docs/en/latest/guides/migrating-1-to-2/): the Appium 1 to 2 changes, including drivers becoming separately installed extensions and the default base path changing from `/wd/hub` to `/`.
## Building drivers and plugins
- [Developing Appium Extensions](https://appium.io/docs/en/latest/developing/): overview of building your own driver or plugin against Appium's modular structure.
- [Building Drivers](https://appium.io/docs/en/latest/developing/build-drivers/): writing a driver, including command definitions, capability constraints and execute methods.
- [Building Plugins](https://appium.io/docs/en/latest/developing/build-plugins/): writing a plugin that modifies or extends server behaviour.
- [Building Doctor Checks](https://appium.io/docs/en/latest/developing/build-doctor-checks/): shipping preconditions checks that `appium driver doctor` can run for your extension.
- [Building Documentation](https://appium.io/docs/en/latest/developing/build-docs/): using `@appium/docutils` to build an MkDocs site for your extension.
- [Masking Sensitive Log Data](https://appium.io/docs/en/latest/developing/sensitive/): marking values as sensitive so they are redacted from extension logs.
- [Appium's Config System](https://appium.io/docs/en/latest/developing/config-system/): how the schema-driven config file and CLI argument system works internally.
- [Local Validation Of Extension PRs](https://appium.io/docs/en/latest/guides/branch-testing/): installing a driver or plugin from an unmerged branch to test it locally.
## Contributing
- [Contributing to Appium](https://appium.io/docs/en/latest/contributing/): how to contribute code, documentation, maintenance and support to the project.
## Optional
- [Appium Blog](https://appium.io/docs/en/latest/blog/): release announcements and project news.
- [Additional Resources](https://appium.io/docs/en/latest/resources/): third-party courses, books, talks and tutorials.
- [Appium Project History](https://appium.io/docs/en/latest/intro/history/): how the project evolved since 2012, of interest mostly for background.
- [Sponsors & Backers](https://appium.io/docs/en/latest/sponsors/): the project's sponsorship program.
- [Source repository](https://github.com/appium/appium): the Appium server implementation.
- [JSON Wire Protocol](https://appium.io/docs/en/latest/reference/api/jsonwp/): legacy pre-W3C protocol, listed for historical reference. Do not base new code on it.
- [Mobile JSON Wire Protocol](https://appium.io/docs/en/latest/reference/api/mjsonwp/): legacy pre-W3C mobile protocol extension, listed for historical reference. Do not base new code on it.
+1 -1
View File
@@ -13,7 +13,7 @@ The basic plan for this quickstart is as follows:
1. Install Appium
1. Install an Appium driver and its dependencies
- This guide provides instructions for the [UiAutomator2 driver](https://github.com/appium/appium-uiautomator2-driver)
- This guide provides instructions for the [UiAutomator2 driver](https://appium.github.io/appium-uiautomator2-driver)
1. Install an Appium client library in your language of choice
- This guide contains options for JavaScript, Python, Java, Ruby, and .NET
1. Write and run a simple Appium automation script using a sample application
@@ -14,7 +14,7 @@ reference materials especially for beginners:
- [Capabilities](../guides/caps.md)
- [Settings](../guides/settings.md)
You'll also find that the [Appium Inspector](https://github.com/appium/appium-inspector) is an
You'll also find that the [Appium Inspector](https://appium.github.io/appium-inspector/) is an
indispensable tool for writing Appium tests, as it enables visual inspection of apps and
helps you to discover element locators for use in your test scripts.
@@ -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));
@@ -39,7 +39,7 @@ The `appium_lib_core` is the main part as an Appium client.
`appium_lib_core` does not have such a global variable.
This example is by the `appium_lib_core` with `test-unit` gem module.
Tes code in `appium_lib` should be similar.
Test code in `appium_lib` should be similar.
```ruby title="test.rb"
--8<-- "./sample-code/quickstarts/rb/test.rb"
@@ -12,7 +12,7 @@ interface that allows Appium to automate a particular platform.
(whereas the iOS driver, for example, requires you to be using macOS).
The driver we're going to use is called the [UiAutomator2
Driver](https://github.com/appium/appium-uiautomator2-driver). It's worth visiting that driver's
Driver](https://appium.github.io/appium-uiautomator2-driver/). It's worth visiting that driver's
documentation and bookmarking it, because it will be an invaluable reference down the line.
## Set up Android automation requirements
@@ -105,7 +105,7 @@ appium setup
```
Running this will install Appium's mobile-specific drivers: UiAutomator2, [XCUITest](https://appium.github.io/appium-xcuitest-driver/)
(only if running macOS), and [Espresso](https://github.com/appium/appium-espresso-driver).
(only if running macOS), and [Espresso](https://appium.github.io/appium-espresso-driver/).
You can also use this command to batch install drivers for desktop applications or desktop browsers.
For more details on this, refer to the [Setup command documentation](../reference/cli/setup.md).
@@ -89,7 +89,7 @@ Retrieves the URL endpoints and WebDriver BiDi commands supported in the current
#### Response
`ListCommandsResponse` - an object containing all supported endpoints and BiDi commands, grouped by
their origin (base Appium, driver-specific, or plugin-specific). Refer to [the type definition file](https://github.com/appium/appium/blob/master/packages/types/lib/command.ts)
their origin (base Appium, driver-specific, or plugin-specific). Refer to [the type definition file](https://github.com/appium/appium/blob/master/packages/types/lib/command-maps.ts)
for a detailed structure of this object.
### listExtensions
@@ -103,7 +103,7 @@ Retrieves the [execute methods](../../guides/execute-methods.md) supported in th
#### Response
`ListExtensionsResponse` - an object containing all supported execute methods, grouped by their
origin (driver-specific or plugin-specific). Refer to [the type definition file](https://github.com/appium/appium/blob/master/packages/types/lib/command.ts)
origin (driver-specific or plugin-specific). Refer to [the type definition file](https://github.com/appium/appium/blob/master/packages/types/lib/command-maps.ts)
for a detailed structure of this object.
### getCurrentAppiumContext
+11 -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,21 @@ session.unsubscribe
> WebDriver BiDi documentation: [session.unsubscribe](https://w3c.github.io/webdriver-bidi/#command-session-unsubscribe)
Unsubscribes from one or more BiDi events.
Unsubscribes from one or more BiDi events using subscription IDs or event names. Event names can
also be scoped to contexts (legacy parameter). 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[]||
|`events?`|Non-empty list of event names to unsubscribe from. Required when `subscriptions` is omitted.|string[]||
|`contexts?`|Legacy parameter: contexts in which to unsubscribe from events specified in `events`. By default, the global scope is applied.|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`
`{}`
@@ -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
```
@@ -170,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`
@@ -119,53 +119,3 @@ Retrieves all available application contexts.
#### 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
@@ -265,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
@@ -954,7 +956,7 @@ POST /session/:sessionId/actions
> WebDriver documentation: [Perform Actions](https://w3c.github.io/webdriver/#perform-actions)
Performs a sequence of [actions](https://w3c.github.io/webdriver/#actions).
Performs a sequence of [actions](https://w3c.github.io/webdriver/#actions). See the [Automating Gestures](../../guides/gestures.md) guide for multi-language examples.
#### Parameters
@@ -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
@@ -75,7 +81,7 @@ appium {driver|plugin} install <install-spec>
|--|--|
|None|The short name of an official extension, with optional modifiers as supported by `npm install` (e.g. version or tag)|
|`git`|The Git URL of the extension|
|`github`|The GitHub repository URL of the extension|
|`github`|The GitHub repository slug of the extension, in the form `<org>/<repo>`|
|`local`|The local path to the extension containing its `package.json` file|
|`npm`|The name of the `npm` package, with optional modifiers as supported by `npm install` (e.g. version or tag)|
@@ -108,7 +114,7 @@ appium {driver|plugin} install <install-spec>
- Install the XCUITest driver from GitHub:
```
appium driver install https://github.com/appium/appium-xcuitest-driver --source=github --package=appium-xcuitest-driver
appium driver install appium/appium-xcuitest-driver --source=github --package=appium-xcuitest-driver
```
- Install the XCUITest driver using a Git URL:
@@ -163,7 +169,7 @@ appium {driver|plugin} run <extension-name> [<script-name> [<script-args>]]
|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|
|`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|
@@ -200,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
@@ -235,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.
@@ -26,16 +26,16 @@ 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`|
|`--address`, `-a`|IPv4/IPv6 address or a hostname or a hostname or a hostname or a hostname to listen on|string|`0.0.0.0`|
|`--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||
|`--bidi-heartbeat-interval`|Interval (in seconds) between WebSocket pings sent to each [BiDi](../api/bidi.md) client. A connection that misses two consecutive pongs is terminated. Disabled if set to `0`.|integer|`15`|
|`--bidi-max-buffered-bytes`|Maximum number of bytes the server may keep buffered for a BiDi connection whose client is not reading fast enough. A connection that stays above the limit for longer than `--bidi-max-buffered-ms`, or exceeds twice the limit, is closed with the code `1013`. Disabled if set to `0`, which also turns off skipping of droppable events.|integer|`33554432`|
|`--bidi-max-buffered-ms`|Time (in milliseconds) a BiDi connection may stay above `--bidi-max-buffered-bytes` before it is closed. If set to `0`, the connection is closed as soon as the limit is exceeded.|integer|`30000`|
|`--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`|
@@ -44,7 +44,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`|
@@ -52,7 +52,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`|
@@ -61,12 +60,45 @@ 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. 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
The following options are used for reference or debug purposes. They are only supported for the base `appium` command (not `appium server`), and will not launch the server.
@@ -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,9 +32,8 @@ 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` ||
| `appium:printPageSourceOnFindFailure` | Toggle retrieval of the page source and its printing to the Appium log, whenever a request to find an element fails | `boolean` ||
### Validated Capabilities
@@ -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` |
+1 -1
View File
@@ -46,7 +46,7 @@ contributors!
## Gold Sponsors
<div class="sponsor-section">
<a href="https://jetify.com/" target="_blank"><img src="https://images.opencollective.com/jetify/415824c/avatar/64.png" alt="Jetify" /></a>
<a href="https://device.io/" target="_blank"><img src="https://images.opencollective.com/device-io/cc7e949/avatar/64.png" alt="Device.io" /></a>
</div>
[Become a Gold Sponsor](https://opencollective.com/appium/contribute/gold-sponsor-72877/checkout?interval=month&amount=500&contributeAs=me)
@@ -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'
@@ -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!
+22 -1
View File
@@ -62,7 +62,7 @@ cd appium
!!! info
If you are VS Code user, you can easily check out the project using [Runme](https://runme.dev/api/runme?repository=https%3A%2F%2Fgithub.com%2Fappium%2Fappium.git&fileToOpen=packages%2Fappium%2Fdocs%2Fen%2Fcontributing%2Findex.md).
If you are VS Code user, you can easily check out the project using [Runme](https://runme.dev/).
Install dependencies:
@@ -109,6 +109,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).
@@ -62,6 +62,33 @@ them in `docs`, and add links to these files in `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.
### Helping LLMs Use Your Documentation
The [`/llms.txt` convention](https://llmstxt.org/) is a Markdown file at the root of a
documentation site, listing its most useful pages so that large language models can navigate the
site without crawling it. Appium's own documentation provides one at
[appium.io/docs/en/latest/llms.txt](https://appium.io/docs/en/latest/llms.txt), and you may want to
do the same for your extension.
MkDocs copies any non-Markdown file in your `docs` directory to the built site as-is, so all you
need to do is write an `llms.txt` and place it alongside your Markdown files. No plugin or build
configuration is required.
The value of this file lies in the parts a crawler could not have worked out on its own, so it is
worth writing by hand rather than generating it from your navigation:
- A short description of what your extension automates, and which platforms it targets
- The handful of things models reliably get wrong about your extension - removed capabilities,
renamed commands, patterns from an older major version that no longer apply
- Links to the pages that matter, with a description of each
Because links in `llms.txt` are read out of context, they must be absolute URLs. If you use Mike
for versioning, point them at your version alias (for example `latest`) rather than at a pinned
version, so that the file does not go stale. Note that a hand-written file can drift as pages are
renamed; Appium validates its own with a
[script](https://github.com/appium/appium/blob/master/packages/appium/docs/scripts/check-llmstxt.js)
that runs in CI, which you are welcome to adapt.
### Building the Docs
At this point, you can use the `appium-docs` CLI tool. Run this tool with no arguments to get the
@@ -80,8 +80,9 @@ The required subfields are:
capability to tell Appium to use _your_ driver.
- `platformNames`: this is an array of one or more platform names considered valid for your driver.
When a user sends in the `platformName` capability to start a session, it must be included in
this list for your driver to handle the session. Known platform name strings include: `iOS`,
`tvOS`, `macOS`, `Windows`, `Android`.
this list for your driver to handle the session. These names should generally include the
operating systems your driver is targeting - for example, a driver targeting iOS would include
`iOS`, while a driver targeting the Chrome browser on Windows would include `Windows`.
- `mainClass`: this is a named export (in CommonJS style) from your `main` field. It must be a
class which extends Appium's `BaseDriver` (see below).
@@ -283,8 +284,8 @@ You handle WebDriver commands by implementing functions in your driver class. Ea
WebDriver Protocol, plus the various Appium extensions, has a corresponding function that you
implement if you want to support that command in your driver. The best way to see which commands
Appium supports and which method you need to implement for each command is to look at Appium's
[routes.js](https://github.com/appium/appium/blob/master/packages/base-driver/lib/protocol/routes.js).
Each route object in this file tells you the command name as well as the parameters you'd expect to
[routes/](https://github.com/appium/appium/tree/master/packages/base-driver/lib/protocol/routes).
Each route object in this directory's files tells you the command name as well as the parameters you'd expect to
receive for that command.
Let's take this block for example:
@@ -327,7 +328,7 @@ Websockets. Implementing handlers for BiDi commands works just the same as imple
for WebDriver classic commands (described in the previous section). You simply define a method on
your driver of the appropriate name, and it will be called when the BiDi command is requested by
the client. To see which specific names you should use for BiDi commands, have a look at
[bidi-commands.js](https://github.com/appium/appium/blob/master/packages/base-driver/lib/protocol/bidi-commands.js)
[bidi-commands.ts](https://github.com/appium/appium/blob/master/packages/base-driver/lib/protocol/bidi-commands.ts)
You are not limited to BiDi commands that are defined in the official BiDi specification. If you
wish to define new commands, you may do so; you just need to tell Appium about them! See
@@ -482,7 +482,7 @@ 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)
[configured](https://github.com/appium/appium/blob/master/packages/base-driver/lib/express/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:
@@ -65,7 +65,7 @@ functionality (e.g., the `plugin` or `driver` subcommands).
Note that this file is the _base_ schema; this will become painfully relevant.
```
This file is is _not_ a JSON file, because a) JSON is painful to work with for humans, b) is
This file is _not_ a JSON file, because a) JSON is painful to work with for humans, b) is
especially reviled by @jlipps, and c) `ajv` accepts objects, not JSON files.
It is more straightforward to explain how config files are validated, so we'll start there.
+13 -13
View File
@@ -17,34 +17,34 @@ If you maintain an Appium driver that you would like to be listed here, feel fre
These drivers are currently maintained by the Appium team:
### [Chromium](https://github.com/appium/appium-chromium-driver)
### [Chromium](https://appium.github.io/appium-chromium-driver)
- Target: Desktop and mobile Chromium browsers (Chrome, Microsoft Edge, etc.)
- Target: Desktop Chromium-based browsers (Chrome, Microsoft Edge, etc.)
- Mode: Web
```sh title="Install This Driver"
appium driver install chromium
```
### [Espresso](https://github.com/appium/appium-espresso-driver)
### [Espresso](https://appium.github.io/appium-espresso-driver)
- Target: Android applications
- Mode: Native
- Target: Android applications (mobile, TV, Wear, XR, Automotive)
- Mode: Native, Hybrid, Web
```sh title="Install This Driver"
appium driver install espresso
```
### [Gecko](https://github.com/appium/appium-geckodriver)
### [Gecko](https://appium.github.io/appium-geckodriver)
- Target: Desktop and mobile Gecko browsers (Firefox)
- Target: Desktop and mobile Gecko-based browsers (Firefox, etc.)
- Mode: Web
```sh title="Install This Driver"
appium driver install gecko
```
### [Mac2](https://github.com/appium/appium-mac2-driver)
### [Mac2](https://appium.github.io/appium-mac2-driver)
- Target: macOS applications
- Mode: Native
@@ -53,7 +53,7 @@ appium driver install gecko
appium driver install mac2
```
### [Safari](https://github.com/appium/appium-safari-driver)
### [Safari](https://appium.github.io/appium-safari-driver)
- Target: Desktop and mobile Safari browsers
- Mode: Web
@@ -62,9 +62,9 @@ appium driver install mac2
appium driver install safari
```
### [UiAutomator2](https://github.com/appium/appium-uiautomator2-driver)
### [UiAutomator2](https://appium.github.io/appium-uiautomator2-driver)
- Target: Android, Android TV, Android Wear applications
- Target: Android applications (mobile, TV, Wear, XR, Automotive)
- Modes: Native, Hybrid, Web
```sh title="Install This Driver"
@@ -89,7 +89,7 @@ appium driver install windows
### [XCUITest](https://appium.github.io/appium-xcuitest-driver/)
- Target: iOS, iPadOS, tvOS applications
- Target: iOS, iPadOS, tvOS, watchOS applications
- Modes: Native, Hybrid, Web
```sh title="Install This Driver"
@@ -169,7 +169,7 @@ This driver is recommended as a drop-in replacement for the partially unmaintain
```
- Target: Windows applications
- Mode: Native
- Mode: Native, Hybrid
- Supported by: Community / Automate The Planet
```sh title="Install This Driver"
+1 -1
View File
@@ -14,7 +14,7 @@ If you maintain an Appium plugin that you would like to be listed here, feel fre
## Official Plugins
These plugins are are currently maintained by the Appium team:
These plugins are currently maintained by the Appium team:
### [Execute Driver](https://github.com/appium/appium/tree/master/packages/execute-driver-plugin)
@@ -113,3 +113,44 @@ Supported by: `@AppiumTestDistribution`
```sh title="Install This Tool"
npm install -g appium-installer
```
### [Optics](https://github.com/mozarkai/optics-framework)
Optics is an open-source test automation framework that uses Appium to drive Android, iOS,
Android TV, Samsung Tizen and LG webOS apps (and Selenium or Playwright for web). Tests are written
as CSV or YAML keyword steps, and the same keywords are available as a Python library, a Robot
Framework library, an HTTP API and an MCP server. Element lookup falls back from XPath to visible
text, OCR and image matching.
Supported by: `@chinmayajha`
```sh title="Install This Tool"
pip install "optics-framework[mobile]"
```
### [XPath to Class Chain](https://github.com/domovou69/xpath-to-class-chain)
XPath to Class Chain is a command-line tool for migrating iOS XPath locators in test code to
`-ios class chain` selectors. It converts the XPaths that have an exact class chain equivalent,
converts with a warning those that only have a close one (such as grouped positions `(//X)[n]`),
and skips the rest with a reason (for example, sibling or parent axes).
Supported by: `@domovou69`
```sh title="Install This Tool"
npm install -g xpath-to-class-chain
```
### [XPath to UiSelector](https://github.com/domovou69/xpath-to-uiselector)
XPath to UiSelector is a command-line tool for migrating Android XPath locators in test code to
`-android uiautomator` selectors. It converts the XPaths that have an exact `UiSelector`
equivalent, converts with a warning those that only have a close one (such as `//A//B`), and skips
the rest with a reason. The strategy call is rewritten together with the string, for example
`AppiumBy.xpath` becomes `AppiumBy.androidUIAutomator`.
Supported by: `@domovou69`
```sh title="Install This Tool"
npm install -g xpath-to-uiselector
```
@@ -52,6 +52,6 @@ After the PR is merged there is no need to use the local plugin/driver deploymen
to switch back to the package managed by NPM. Follow the next steps for that:
- Unlink the installed driver/plugin from the server by running `appium driver uninstall <driver_name>` or
`appium plugin uninstall <driver_name>`.
`appium plugin uninstall <plugin_name>`.
- Delete the local source folder (`rm -rf <full_path_to_plugin_or_driver_folder_with_fetched_sources>`).
- Install the driver or the plugin from NPM. Check the component README in order to find a proper command for that.
+2 -2
View File
@@ -8,14 +8,14 @@ principles, so you could create more performant and efficient test suite executi
## Why Caching Is Necessary
Mobile application bundles could reach hundreds of megabytes is size. This could become a serious
Mobile application bundles could reach hundreds of megabytes in size. This could become a serious
performance issue if a test suite is executed, and it is necessary to fetch/extract the same application
bundle for each test.
## What Is Cached
Caching could be applied to application bundles generated by
[`configureApp`](https://github.com/appium/appium/blob/master/packages/base-driver/lib/basedriver/helpers.js#L107) helper call.
[`configureApp`](https://github.com/appium/appium/blob/master/packages/base-driver/lib/basedriver/helpers.js#L99) helper call.
Inherited drivers can customize their caching logic by providing own `onPostProcess`
(or both `inDownload` and `onPostProcess`) property definition, but the general
rule of thumb is that we need to cache locally all application bundles need to be downloaded and/or extracted
+1 -1
View File
@@ -24,7 +24,7 @@ to launch. In order to achieve this, Appium defines its own [extension capabilit
## 一般的なAppiumのCapabilities
According to the WebDriver spec, extension capabilities must include a namespace prefix (signifying
the vendor introducing the capability), and the namespace must end a colon (`:`). Appium's vendor
the vendor introducing the capability), and the namespace must end in a colon (`:`). Appium's vendor
prefix is `appium:`, and so any Appium-specific capabilities must include this prefix. Depending on
your Appium client, the prefix may be added automatically or in conjunction with certain interfaces,
but it is always a good practice to explicitly include it for clarity.
+1 -1
View File
@@ -36,7 +36,7 @@ discovered and loaded by Appium:
- `appium.config.cjs`
- `.appiumrc` (which is considered to be JSON)
Further, _if your project uses Node.js,_ you can use store the configuration inside an `appium`
Further, _if your project uses Node.js,_ you can store the configuration inside an `appiumConfig`
property in your `package.json` and it will be automatically discovered.
### Config File Search
@@ -17,7 +17,7 @@ property:
```
{
"<event_type>": [<occurence_timestamp_1>, ...],
"<event_type>": [<occurrence_timestamp_1>, ...],
"commands": [
{
"cmd": "<command_name>",
@@ -16,7 +16,7 @@ by client libraries. There are two main strategies for this:
based client library (including all Selenium and Appium clients).
There are pros and cons for each strategy, but it is ultimately up to the extension author to
decide how they wish implement new commands.
decide how they wish to implement new commands.
This guide is designed to specifically help you understand the "Execute Method" strategy. This
pattern is commonly used in official Appium drivers and other third-party extensions.
@@ -18,8 +18,11 @@ Each rule is an object with a set of predefined properties. The following rule p
supported:
- `pattern`: A valid Javascript RegExp pattern to replace. Must be a valid non-empty pattern.
- `text`: A simple non-empty exact text match to replace. Either this property or the above one must
be provided. `pattern` has priority over `text` if both are provided.
- `text`: A simple non-empty exact text to replace. Either this property or the above one must
be provided. `pattern` has priority over `text` if both are provided. Note that the text is
matched as a whole word: special characters are escaped and word boundaries (`\b`) are added on
the sides where the value begins or ends with a word character. For example, a rule with text
`pass` replaces `pass` in `my pass here`, but does not match inside `password`.
- `flags`: Regular expression flags for the given pattern. Supported flags are the same as for the
standard JavaScript [RegExp constructor](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#Advanced_searching_with_flags_2).
Note that the `g` (global matching) flag is always enabled.
@@ -27,7 +27,7 @@ appium driver install xcuitest
```
This command will install the latest version of the
[XCUITest Driver](https://github.com/appium/appium-xcuitest-driver). The Extension CLI comes with a variety
[XCUITest Driver](https://appium.github.io/appium-xcuitest-driver/). The Extension CLI comes with a variety
of commands and parameters; see the documentation for that command for all the specifics.
The all-important question when Appium is managing your extensions for you is: where are they installed?
@@ -65,7 +65,7 @@ like any other dependency. Basically, whenever you run Appium, if you have not e
1. Try to determine whether the _current directory_ is inside an `npm` package.
2. If so, it will check whether `appium` is a dependency (dev, prod, or peer) in the project's
`package.json`
3. If so, _unless you have specified `APPIUM_HOME` in your environment_, Appium will ignore load
3. If so, _unless you have specified `APPIUM_HOME` in your environment_, Appium will load
drivers and plugins defined in that `package.json` file instead.
This means you are freely able to add Appium drivers and plugins as regular package dependencies or
@@ -111,12 +111,12 @@ The Appium 1 ecosystem included several drivers, clients and other packages that
deprecated and replaced with newer packages. Appium 2 no longer includes support for these packages,
and it is recommended to migrate to the following replacements:
| Appium 1 Package | Replacement in Appium 2 |
| ------------------ | -------------------------------------------------------------------------- |
| iOS Driver | [XCUITest Driver](https://appium.github.io/appium-xcuitest-driver/latest/) |
| UiAutomator Driver | [UiAutomator2](https://github.com/appium/appium-uiautomator2-driver/) |
| `wd` Client | [WebdriverIO Client](https://webdriver.io/) |
| Appium Desktop | [Appium Inspector](https://github.com/appium/appium-inspector) |
| Appium 1 Package | Replacement in Appium 2 |
| ------------------ | -------------------------------------------------------------------- |
| iOS Driver | [XCUITest Driver](https://appium.github.io/appium-xcuitest-driver/) |
| UiAutomator Driver | [UiAutomator2](https://appium.github.io/appium-uiautomator2-driver/) |
| `wd` Client | [WebdriverIO Client](https://webdriver.io/) |
| Appium Desktop | [Appium Inspector](https://appium.github.io/appium-inspector/) |
!!! info "Actions Needed"
@@ -142,9 +142,9 @@ Icons are used to indicate endpoint support in either certain drivers, or in the
- :simple-appium:{ .lg } - Appium server
- :material-apple:{ .lg } - [XCUITest driver](https://appium.github.io/appium-xcuitest-driver/latest/)
- :material-android:{ .lg } - [UiAutomator2 driver](https://github.com/appium/appium-uiautomator2-driver/)
- :material-coffee:{ .lg } - [Espresso driver](https://github.com/appium/appium-espresso-driver)
- :material-apple-finder:{ .lg } - [Mac2 driver](https://github.com/appium/appium-mac2-driver)
- :material-android:{ .lg } - [UiAutomator2 driver](https://appium.github.io/appium-uiautomator2-driver/)
- :material-coffee:{ .lg } - [Espresso driver](https://appium.github.io/appium-espresso-driver/)
- :material-apple-finder:{ .lg } - [Mac2 driver](https://appium.github.io/appium-mac2-driver/)
- :material-microsoft-windows:{ .lg } - [Windows driver](https://github.com/appium/appium-windows-driver)
</div>
@@ -448,9 +448,6 @@ longer accepts, as well as the parameters it continues to accept in Appium 3.
- `POST /session/:sessionId/element/:elementId/value`
- :octicons-x-24: `value`
- :octicons-check-24: `text`
- `POST /session/:sessionId/timeouts`
- :octicons-x-24: `type`, `ms`
- :octicons-check-24: `script`, `pageLoad`, `implicit`
- `POST /session/:sessionId/window`
- :octicons-x-24: `name`
- :octicons-check-24: `handle`
+4 -4
View File
@@ -49,8 +49,8 @@ async setUrl(url) {
[^1]: You might notice that `setUrl` doesn't look anything like `Navigate To`, so how did we know
to use it rather than some other random string? Well, Appium's WebDriver-protocol-to-method-name
mapping is defined in a special file within the `@appium/base-driver` package called
[routes.js](https://github.com/appium/appium/blob/master/packages/base-driver/lib/protocol/routes.js).
mappings are defined in special files within the `@appium/base-driver` package's
[routes/](https://github.com/appium/appium/tree/master/packages/base-driver/lib/protocol/routes) directory.
So if you're writing a driver, this is where you would go to figure out what method names to use
and what parameters to expect. Or you could look at the source for any of the main Appium
drivers!
@@ -81,7 +81,7 @@ from there is up to you, the driver author!
## Automation mapping
But _typically_ what driver authors want to do is to provide automation behaviours for a given
platform(s) that are semantically very similar to the the WebDriver spec implementations for
platform(s) that are semantically very similar to the WebDriver spec implementations for
browsers. When you want to find an element, you should get a reference to a UI element. When you
want to click or tap that element, the resulting behaviour should be the same as if a person were
to click or tap on the element. And so on.
@@ -175,7 +175,7 @@ be passed back directly to the client, without any XCUITest driver code seeing i
This architectural pattern provides a nice bonus for driver authors who choose to deal with the
WebDriver protocol everywhere, rather than constructing bespoke protocols. It also means that
Appium can create wrapper drivers for any other existing WebDriver implementation very easily. If
you look at the [Appium Safari driver](https://github.com/appium/appium-safari-driver) code, for
you look at the [Appium Safari driver](https://appium.github.io/appium-safari-driver/) code, for
example, you'll see that it implements basically no standard commands, because all of these are
proxied directly to an underlying SafariDriver process.
+1 -1
View File
@@ -11,7 +11,7 @@ title: Getting Started
1. Appiumをインストールする
2. Appiumドライバーとその依存関係をインストールする
- このガイドでは、[UiAutomator2 driver](https://github.com/appium/appium-uiautomator2-driver) について説明します
- This guide provides instructions for the [UiAutomator2 driver](https://appium.github.io/appium-uiautomator2-driver/)
3. お好みの言語でAppiumクライアント・ライブラリをインストールする
- このガイドでは、JavaScript、Python、Java、Ruby、.NETの例で説明します
4. サンプルアプリケーションを使用した簡単なAppiumでの自動化スクリプトの作成と実行
@@ -12,7 +12,9 @@ Android自動化用のシステムの設定と簡単なテストの実行が完
- [Capabilities](../guides/caps.md)
- [Settings](../guides/settings.md)
また、[Appium Inspector](https://github.com/appium/appium-inspector) は、アプリの視覚的な検査を可能にし、テストスクリプトで使用する要素ロケーターを見つけるのに役立つ、Appium テストの作成に不可欠なツールです。
You'll also find that the [Appium Inspector](https://appium.github.io/appium-inspector/) is an
indispensable tool for writing Appium tests, as it enables visual inspection of apps and
helps you to discover element locators for use in your test scripts.
また、多くのオンライン Appium コースもご活用いただけます。
@@ -38,7 +38,7 @@ The `appium_lib_core` is the main part as an Appium client.
`appium_lib_core` does not have such a global variable.
This example is by the `appium_lib_core` with `test-unit` gem module.
Tes code in `appium_lib` should be similar.
Test code in `appium_lib` should be similar.
```ruby title="test.rb"
--8<-- "./sample-code/quickstarts/rb/test.rb"
@@ -109,7 +109,7 @@ appium setup
```
Running this will install Appium's mobile-specific drivers: UiAutomator2, [XCUITest](https://appium.github.io/appium-xcuitest-driver/)
(only if running macOS), and [Espresso](https://github.com/appium/appium-espresso-driver).
(only if running macOS), and [Espresso](https://appium.github.io/appium-espresso-driver/).
You can also use this command to batch install drivers for desktop applications or desktop browsers.
For more details on this, refer to the [Setup command documentation](../reference/cli/setup.md).
@@ -90,7 +90,7 @@ Retrieves the URL endpoints and WebDriver BiDi commands supported in the current
#### Response
`ListCommandsResponse` - an object containing all supported endpoints and BiDi commands, grouped by
their origin (base Appium, driver-specific, or plugin-specific). Refer to [the type definition file](https://github.com/appium/appium/blob/master/packages/types/lib/command.ts)
their origin (base Appium, driver-specific, or plugin-specific). Refer to [the type definition file](https://github.com/appium/appium/blob/master/packages/types/lib/command-maps.ts)
for a detailed structure of this object.
### listExtensions
@@ -104,7 +104,7 @@ Retrieves the [execute methods](../../guides/execute-methods.md) supported in th
#### Response
`ListExtensionsResponse` - an object containing all supported execute methods, grouped by their
origin (driver-specific or plugin-specific). Refer to [the type definition file](https://github.com/appium/appium/blob/master/packages/types/lib/command.ts)
origin (driver-specific or plugin-specific). Refer to [the type definition file](https://github.com/appium/appium/blob/master/packages/types/lib/command-maps.ts)
for a detailed structure of this object.
### getCurrentAppiumContext
+12 -7
View File
@@ -47,7 +47,7 @@ Subscribes to one or more BiDi events.
#### Response
`null`
`{subscription: string}` — a unique subscription ID that can be passed to `session.unsubscribe`.
### bidiUnsubscribe
@@ -57,15 +57,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[] | |
| Name | Description | Type | Default |
| ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ | ------- |
| `subscriptions?` | Non-empty list of subscription IDs returned by `session.subscribe`. Cannot be combined with `events` or `contexts`. | string[] | |
| `contexts?` | Non-standard, only together with `events`: contexts in which to unsubscribe from the specified events. By default, the global scope is applied. | string[] | `['']` |
| `events?` | 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`
`{}`
@@ -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
@@ -76,7 +82,7 @@ appium {driver|plugin} install <install-spec>
| -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| None | The short name of an official extension, with optional modifiers as supported by `npm install` (e.g. version or tag) |
| `git` | The Git URL of the extension |
| `github` | The GitHub repository URL of the extension |
| `github` | The GitHub repository slug of the extension, in the form `<org>/<repo>` |
| `local` | The local path to the extension containing its `package.json` file |
| `npm` | The name of the `npm` package, with optional modifiers as supported by `npm install` (e.g. version or tag) |
@@ -109,7 +115,7 @@ appium {driver|plugin} install <install-spec>
- Install the XCUITest driver from GitHub:
```
appium driver install https://github.com/appium/appium-xcuitest-driver --source=github --package=appium-xcuitest-driver
appium driver install appium/appium-xcuitest-driver --source=github --package=appium-xcuitest-driver
```
- Install the XCUITest driver using a Git URL:
@@ -164,7 +170,7 @@ appium {driver|plugin} run <extension-name> [<script-name> [<script-args>]]
| 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 |
| `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 |
@@ -199,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
@@ -234,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
@@ -26,7 +26,7 @@ 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` |
| `--address`, `-a` | IPv4/IPv6 address or a hostname 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` |

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