mirror of
https://github.com/appium/appium.git
synced 2026-11-03 15:56:15 +08:00
Compare commits
49
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
edf2593e17 | ||
|
|
7a14f9f929 | ||
|
|
b4612e0c7a | ||
|
|
8a307a078b | ||
|
|
0ffca811ce | ||
|
|
89f2e25a6c | ||
|
|
7aac098d60 | ||
|
|
32f57500e8 | ||
|
|
1ca77c6ded | ||
|
|
92b32f8524 | ||
|
|
c1c3957cb7 | ||
|
|
885570e7ca | ||
|
|
8413ccb4fa | ||
|
|
82b7515867 | ||
|
|
2957b8344b | ||
|
|
ea86eff938 | ||
|
|
b98dc4f46f | ||
|
|
61276f2e7c | ||
|
|
9625052d05 | ||
|
|
0c755f503c | ||
|
|
a2294fa716 | ||
|
|
32fae41c03 | ||
|
|
0e8e3c7144 | ||
|
|
96b45a85cf | ||
|
|
e08e7ebec2 | ||
|
|
a0be939031 | ||
|
|
bb26085f7e | ||
|
|
75e0788784 | ||
|
|
7b3f0b5dbb | ||
|
|
9eb65f7ad4 | ||
|
|
47405dda8a | ||
|
|
5a9ab97924 | ||
|
|
4494fad3af | ||
|
|
6a97fe48ae | ||
|
|
d35ed1c8ac | ||
|
|
d5ef0f9c86 | ||
|
|
139dbaeef6 | ||
|
|
2d7b2c4b4c | ||
|
|
6488a0a4e1 | ||
|
|
6b9d844998 | ||
|
|
8ee816d0cf | ||
|
|
b1b7c33edd | ||
|
|
57dc6a045d | ||
|
|
75289711ee | ||
|
|
b7b97a24d6 | ||
|
|
715942845b | ||
|
|
8d79467da8 | ||
|
|
dcfec17d1c | ||
|
|
c38e639d52 |
Generated
+982
-1748
File diff suppressed because it is too large
Load Diff
+17
-19
@@ -42,8 +42,8 @@
|
||||
"dev:docs": "npm run -w packages/appium dev:docs",
|
||||
"docs": "npm run -w packages/appium build:docs",
|
||||
"docs:preview": "npm run -w packages/appium build:docs:preview",
|
||||
"docs:publish": "npm run -w packages/appium publish:docs",
|
||||
"doctor": "node ./packages/doctor",
|
||||
"generate-docs": "npm run --workspace=packages/appium generate-docs",
|
||||
"lint": "eslint .",
|
||||
"lint:ci": "eslint --quiet .",
|
||||
"lint:fix": "run-s \"lint -- --fix\"",
|
||||
@@ -83,20 +83,20 @@
|
||||
"devDependencies": {
|
||||
"@colors/colors": "1.6.0",
|
||||
"@tsconfig/node14": "14.1.2",
|
||||
"@types/chai": "4.3.16",
|
||||
"@types/chai": "4.3.17",
|
||||
"@types/chai-as-promised": "7.1.8",
|
||||
"@types/diff": "5.2.1",
|
||||
"@types/mocha": "10.0.7",
|
||||
"@types/node": "20.14.10",
|
||||
"@types/node": "20.14.14",
|
||||
"@types/semver": "7.5.8",
|
||||
"@types/sinon": "17.0.3",
|
||||
"@types/sinon-chai": "3.2.12",
|
||||
"@types/source-map-support": "0.5.10",
|
||||
"@types/teen_process": "2.0.4",
|
||||
"@types/ws": "8.5.10",
|
||||
"@types/ws": "8.5.12",
|
||||
"@types/yargs": "17.0.32",
|
||||
"@typescript-eslint/eslint-plugin": "7.16.0",
|
||||
"@typescript-eslint/parser": "7.16.0",
|
||||
"@typescript-eslint/eslint-plugin": "7.18.0",
|
||||
"@typescript-eslint/parser": "7.18.0",
|
||||
"asyncbox": "3.0.0",
|
||||
"chai": "5.1.1",
|
||||
"chai-as-promised": "8.0.0",
|
||||
@@ -107,31 +107,29 @@
|
||||
"eslint-find-rules": "4.1.0",
|
||||
"eslint-import-resolver-typescript": "3.6.1",
|
||||
"eslint-plugin-import": "2.29.1",
|
||||
"eslint-plugin-mocha": "10.4.3",
|
||||
"eslint-plugin-promise": "6.4.0",
|
||||
"fancy-log": "2.0.0",
|
||||
"eslint-plugin-mocha": "10.5.0",
|
||||
"eslint-plugin-promise": "6.6.0",
|
||||
"finalhandler": "1.2.0",
|
||||
"get-port": "5.1.1",
|
||||
"json-schema-to-typescript": "14.1.0",
|
||||
"lerna": "8.1.6",
|
||||
"json-schema-to-typescript": "15.0.0",
|
||||
"lerna": "8.1.7",
|
||||
"log-symbols": "4.1.0",
|
||||
"midnight-smoker": "8.0.0",
|
||||
"mjpeg-consumer": "2.0.0",
|
||||
"mjpeg-server": "0.3.1",
|
||||
"mocha": "10.6.0",
|
||||
"mocha": "10.7.0",
|
||||
"npm-run-all2": "6.2.2",
|
||||
"prettier": "3.3.2",
|
||||
"prettier": "3.3.3",
|
||||
"rewiremock": "3.14.5",
|
||||
"rimraf": "5.0.9",
|
||||
"rimraf": "5.0.10",
|
||||
"serve-static": "1.15.0",
|
||||
"sinon": "18.0.0",
|
||||
"sync-monorepo-packages": "1.0.2",
|
||||
"through2": "4.0.2",
|
||||
"ts-node": "10.9.2",
|
||||
"tsd": "0.31.1",
|
||||
"typescript": "5.5.3",
|
||||
"typescript": "5.5.4",
|
||||
"validate.js": "0.13.1",
|
||||
"webdriverio": "8.39.1",
|
||||
"webdriverio": "8.40.0",
|
||||
"ws": "8.18.0",
|
||||
"yaml-js": "0.3.1"
|
||||
},
|
||||
@@ -144,10 +142,10 @@
|
||||
},
|
||||
"overrides": {
|
||||
"@sidvind/better-ajv-errors": {
|
||||
"ajv": "8.16.0"
|
||||
"ajv": "8.17.1"
|
||||
},
|
||||
"ajv-formats": {
|
||||
"ajv": "8.16.0"
|
||||
"ajv": "8.17.1"
|
||||
},
|
||||
"http-cache-semantics": "4.1.1"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,23 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [2.11.3](https://github.com/appium/appium/compare/appium@2.11.2...appium@2.11.3) (2024-08-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **appium:** update dependency ajv to v8.17.1 ([7528971](https://github.com/appium/appium/commit/75289711ee344ac9a11a572f13ddb0471e2cbf79))
|
||||
* **appium:** update dependency winston to v3.13.1 ([dcfec17](https://github.com/appium/appium/commit/dcfec17d1ceab90939b5f199854ba630829e3d1f))
|
||||
* **docutils:** update dependency yaml to v2.5.0 ([0c755f5](https://github.com/appium/appium/commit/0c755f503c49e459315616e51be0c0776089b2bb))
|
||||
* **execute-driver-plugin:** update dependency webdriverio to v8.40.0 ([#20442](https://github.com/appium/appium/issues/20442)) ([b4612e0](https://github.com/appium/appium/commit/b4612e0c7a21902a3cab658fbdc05f3b48c55d0d))
|
||||
* **logger:** update dependency lru-cache to v10.4.3 ([#20364](https://github.com/appium/appium/issues/20364)) ([8d79467](https://github.com/appium/appium/commit/8d79467da8a0733ac3e49b9152bd6905989a57ca))
|
||||
* **support:** update dependency axios to v1.7.3 ([1ca77c6](https://github.com/appium/appium/commit/1ca77c6dedbff4552aba9c97cf5406c7552d1a01))
|
||||
* **support:** update dependency semver to v7.6.3 ([5a9ab97](https://github.com/appium/appium/commit/5a9ab9792402e31d3903fb1a3b08329a67b6a2a2))
|
||||
* **types:** update dependency type-fest to v4.22.0 ([#20387](https://github.com/appium/appium/issues/20387)) ([47405dd](https://github.com/appium/appium/commit/47405dda8a5de17c72fb721b0c043e0dd4f6b35a))
|
||||
* **types:** update dependency type-fest to v4.23.0 ([#20396](https://github.com/appium/appium/issues/20396)) ([0e8e3c7](https://github.com/appium/appium/commit/0e8e3c71441d02d22f015f08df5223909f5fbb93))
|
||||
|
||||
|
||||
|
||||
## [2.11.2](https://github.com/appium/appium/compare/appium@2.11.1...appium@2.11.2) (2024-07-10)
|
||||
|
||||
|
||||
|
||||
@@ -1,8 +1,19 @@
|
||||
authors:
|
||||
jlipps:
|
||||
name: "Jonathan Lipps"
|
||||
description: "Appium project lead. Learn more about Jonathan at https://jlipps.com"
|
||||
avatar: "https://gravatar.com/avatar/fdaadd825d92709bcf7fe76dffa12c40681c6108c67bcddf19478b2d00e3794b.jpg"
|
||||
slug: "jlipps"
|
||||
url: "https://jlipps.com"
|
||||
|
||||
name: 'Jonathan Lipps'
|
||||
description: 'Appium project lead. Learn more about Jonathan at https://jlipps.com'
|
||||
avatar: 'https://gravatar.com/avatar/fdaadd825d92709bcf7fe76dffa12c40681c6108c67bcddf19478b2d00e3794b.jpg'
|
||||
slug: 'jlipps'
|
||||
url: 'https://jlipps.com'
|
||||
srini:
|
||||
name: 'Srinivasan Sekar'
|
||||
description: 'Appium Maintainer'
|
||||
avatar: 'https://avatars.githubusercontent.com/u/8896549?v=4'
|
||||
slug: 'srini'
|
||||
url: 'https://srini.codes'
|
||||
sai:
|
||||
name: 'Sai krishna'
|
||||
description: 'Appium Maintainer'
|
||||
avatar: 'https://avatars.githubusercontent.com/u/4045041?v=4'
|
||||
slug: 'sai'
|
||||
url: 'https://saikrishna.tech'
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
---
|
||||
authors:
|
||||
- srini
|
||||
- sai
|
||||
date: 2024-07-13
|
||||
---
|
||||
|
||||
# Appium Conf 2024: Call for Proposals Now Open!
|
||||
|
||||
We're thrilled to announce that Appium Conf is back and going online for 2024! This is the official Appium conference where you can learn all about what's new in Appium 2.0 and explore the latest in automating tests for devices, mobile apps, and desktop applications.
|
||||
|
||||
<!-- more -->
|
||||
<div style="text-align:center;width:100%">
|
||||
<a href="https://confengine.com/conferences/appium-conf-2024/proposals?utm_medium=referal&utm_source=appium.io&utm_campaign=launch">
|
||||
<img id="image-36-42" alt="Appium Conf logo" src="https://appiumconf.com/wp-content/uploads/2021/06/Appium-Conf-Logo.svg" data-src="https://appiumconf.com/wp-content/uploads/2021/06/Appium-Conf-Logo.svg" decoding="async">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
||||
## Mark Your Calendars: September 13, 2024
|
||||
|
||||
Join us on September 13, 2024, for a full day of inspiring and practical talks from the Appium team and the world's top mobile testing and automation experts. With Appium 2.0 now well-established, this is your opportunity to dive deep and learn how to maximize its potential.
|
||||
|
||||
## Why Attend Appium Conf 2024?
|
||||
|
||||
1. **Gain Insights**: Hear interesting insights and inspiring case studies from global experts and the core Appium team.
|
||||
2. **Improve Your Strategy**: Gain valuable knowledge to enhance your own automation strategy.
|
||||
3. **Network**: Make connections with Appium users from around the globe.
|
||||
4. **Learn from Specialists**: Benefit from Appium specialists' expertise on a variety of topics.
|
||||
|
||||
## Call for Proposals: Share Your Knowledge!
|
||||
|
||||
We're looking for a diverse range of speakers from around the globe who are eager to share innovative ideas and use-cases of mobile test automation. If you have experience with Appium and insights that could benefit the community of test practitioners, we want to hear from you!
|
||||
|
||||
Don't miss this chance to contribute to the Appium community and showcase your expertise on a global stage.
|
||||
|
||||
## How to Submit Your Proposal
|
||||
|
||||
Visit our website at [https://appiumconf.com/](https://appiumconf.com/) and [Submit a Proposal](https://confengine.com/conferences/appium-conf-2024/proposals?utm_medium=referal&utm_source=appium.io&utm_campaign=launch). We're excited to see your innovative ideas and experiences with Appium!
|
||||
|
||||
## Stay Tuned
|
||||
|
||||
Keep an eye on our website and social media channels for updates on the conference schedule, keynote speakers, and more exciting details about Appium Conf 2024.
|
||||
|
||||
Join us in shaping the future of mobile and device test automation. Submit your proposal today and be part of this exciting event!
|
||||
@@ -37,13 +37,13 @@ These drivers are not maintained by the Appium team and can be used to target ad
|
||||
|Driver|Installation Key|Platform(s)|Mode(s)|Supported By|
|
||||
|--|--|--|--|--|
|
||||
|[Flutter](https://github.com/appium/appium-flutter-driver)|`--source=npm appium-flutter-driver`|iOS, Android|Native|Community|
|
||||
|[Flutter Integration](https://github.com/AppiumTestDistribution/appium-flutter-integration-driver)|`--source=npm appium-flutter-integration-driver`|iOS, Android|Native|Community / `@AppiumTestDistribution`|
|
||||
|[LG WebOS](https://github.com/headspinio/appium-lg-webos-driver)|`--source=npm appium-lg-webos-driver`|LG TV|Web|HeadSpin|
|
||||
|[Linux](https://github.com/fantonglang/appium-linux-driver)|`--source=npm @stdspa/appium-linux-driver`|Linux|Native|`@fantonglang`|
|
||||
|[Roku](https://github.com/headspinio/appium-roku-driver)|`--source=npm @headspinio/appium-roku-driver`|Roku|Native|HeadSpin|
|
||||
|[Tizen](https://github.com/Samsung/appium-tizen-driver)|`--source=npm appium-tizen-driver`|Android|Native|Community / Samsung|
|
||||
|[TizenTV](https://github.com/headspinio/appium-tizen-tv-driver)|`--source=npm appium-tizen-tv-driver`|Samsung TV|Web|HeadSpin|
|
||||
|[Youi](https://github.com/YOU-i-Labs/appium-youiengine-driver)|`--source=npm appium-youiengine-driver`|iOS, Android, macOS, Linux, tvOS|Native|Community / You.i|
|
||||
|[Flutter Integration](https://github.com/AppiumTestDistribution/appium-flutter-integration-driver)|`--source=npm appium-flutter-integration-driver`|Android, iOS|Native|Community / AppiumTestDistribution|
|
||||
|
||||
!!! note
|
||||
|
||||
|
||||
@@ -32,9 +32,12 @@ These plugins are not maintained by the Appium team and can provide additional f
|
||||
|<div style="width:6em">Plugin</div>|<div style="width:19em">Installation Key</div>|Description|<div style="width:13em">Supported By</div>|
|
||||
|---|---|---|---|
|
||||
|[AltUnity](https://github.com/headspinio/appium-altunity-plugin)|`--source=npm appium-altunity-plugin`|Target Unity games and apps for automation with a new context, via the AltUnityTester framework|HeadSpin|
|
||||
|[Device Farm](https://github.com/AppiumTestDistribution/appium-device-farm)|`--source=npm appium-device-farm`|Manage and create driver session on connected android devices and iOS Simulators.|`@AppiumTestDistribution`|
|
||||
|[Device Farm](https://github.com/AppiumTestDistribution/appium-device-farm)|`--source=npm appium-device-farm`|Manage and create driver sessions on connected Android devices and iOS simulators|`@AppiumTestDistribution`|
|
||||
|[Gestures](https://github.com/AppiumTestDistribution/appium-gestures-plugin)|`--source=npm appium-gestures-plugin`|Perform basic gestures using W3C Actions|`@AppiumTestDistribution`|
|
||||
|[Interceptor](https://github.com/AppiumTestDistribution/appium-interceptor-plugin)|`--source=npm appium-interceptor`|Intercept and mock API requests and responses|`@AppiumTestDistribution`|
|
||||
|[OCR](https://github.com/jlipps/appium-ocr-plugin)|`--source=npm appium-ocr-plugin`|Find elements via OCR text|`@jlipps`|
|
||||
|[Reporter](https://github.com/AppiumTestDistribution/appium-reporter-plugin)|`--source=npm appium-reporter-plugin`|Generates standalone consolidated html report with screenshots. Reports can be fetched from appium server, without worrying about heavy lifting such as screenshot capturing, report generation etc.|`@AppiumTestDistribution`|
|
||||
|[Reporter](https://github.com/AppiumTestDistribution/appium-reporter-plugin)|`--source=npm appium-reporter-plugin`|Generate standalone consolidated HTML reports with screenshots|`@AppiumTestDistribution`|
|
||||
|[Wait](https://github.com/AppiumTestDistribution/appium-wait-plugin)|`--source=npm appium-wait-plugin`|Manage global element wait timeouts|`@AppiumTestDistribution`|
|
||||
|
||||
!!! note
|
||||
|
||||
|
||||
@@ -11,6 +11,12 @@ title: Welcome
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div style="text-align:center;width:100%;background-color:#7f56c2" >
|
||||
<h3>
|
||||
Appium Conf 2024 is coming online, 13 September. Have you got something to share with the community? <a style="color:#fcba12" href="https://confengine.com/conferences/appium-conf-2024/proposals?utm_medium=referal&utm_source=appium.io&utm_campaign=launch" target="blank">Submit a proposal</a>
|
||||
</h3>
|
||||
</div>
|
||||
<div style="text-align: center">
|
||||
<img src="assets/images/appium-logo-horiz.png" style="max-width: 400px;" />
|
||||
</div>
|
||||
|
||||
@@ -40,12 +40,12 @@ but it is certainly a good one! So let's dive in.
|
||||
## Appium's choice of API
|
||||
|
||||
Appium is very fortunate to have been preceded by a technology which has been a long-standing
|
||||
pioneer in the field of UI automation, namely [Selenium](https://seleniumhq.org). The goal of the
|
||||
pioneer in the field of UI automation, namely [Selenium](https://selenium.dev). The goal of the
|
||||
Selenium project has been to support UI automation of web browsers, and in this way we can think of
|
||||
it as occupying a subset of Appium's goals. Along the way, Selenium (and, after they merged,
|
||||
another project called WebDriver) developed a relatively stable API for browser automation.
|
||||
|
||||
Over the years, Selenium worked with various web browser vendors and the [W3C](https://w3c.org)
|
||||
Over the years, Selenium worked with various web browser vendors and the [W3C](https://w3.org)
|
||||
standards group to turn its API into an official web browser standard, called the [WebDriver
|
||||
specification](https://w3c.github.io/webdriver/). All the main browsers now
|
||||
implement automation capabilities inline with the WebDriver spec, without the Selenium team having
|
||||
|
||||
@@ -8,6 +8,7 @@ theme:
|
||||
language: zh
|
||||
nav:
|
||||
- 首页: index.md
|
||||
- 博客: blog/index.md
|
||||
- 简介:
|
||||
- intro/index.md
|
||||
- 背景:
|
||||
@@ -17,40 +18,61 @@ nav:
|
||||
- intro/history.md
|
||||
- 快速开始:
|
||||
- quickstart/index.md
|
||||
- quickstart/requirements.md
|
||||
- quickstart/install.md
|
||||
- quickstart/uiauto2-driver.md
|
||||
- 编写测试:
|
||||
- Write a Test:
|
||||
- quickstart/test-js.md
|
||||
- quickstart/test-py.md
|
||||
- quickstart/test-java.md
|
||||
- quickstart/test-rb.md
|
||||
- quickstart/test-dotnet.md
|
||||
- quickstart/next-steps.md
|
||||
- 生态系统:
|
||||
- ecosystem/index.md
|
||||
- 开发 Appium 扩展:
|
||||
- ecosystem/build-drivers.md
|
||||
- ecosystem/build-plugins.md
|
||||
- ecosystem/build-docs.md
|
||||
- 命令行参考:
|
||||
- ecosystem/drivers.md
|
||||
- ecosystem/clients.md
|
||||
- ecosystem/plugins.md
|
||||
- ecosystem/tools.md
|
||||
- CLI Reference:
|
||||
- cli/index.md
|
||||
- cli/args.md
|
||||
- cli/env-vars.md
|
||||
- cli/extensions.md
|
||||
- cli/setup.md
|
||||
- Command Reference:
|
||||
- ../en/commands/base-driver.md
|
||||
- ../en/commands/execute-driver-plugin.md
|
||||
- ../en/commands/images-plugin.md
|
||||
- ../en/commands/relaxed-caps-plugin.md
|
||||
- ../en/commands/universal-xml-plugin.md
|
||||
- commands/index.md
|
||||
- commands/base-driver.md
|
||||
- commands/execute-driver-plugin.md
|
||||
- commands/images-plugin.md
|
||||
- commands/relaxed-caps-plugin.md
|
||||
- commands/universal-xml-plugin.md
|
||||
- 指南:
|
||||
- guides/migrating-1-to-2.md
|
||||
- guides/managing-exts.md
|
||||
- guides/config.md
|
||||
- guides/security.md
|
||||
- guides/caps.md
|
||||
- guides/context.md
|
||||
- guides/settings.md
|
||||
- guides/execute-script-overload.md
|
||||
- guides/event-timing.md
|
||||
- guides/log-filters.md
|
||||
- guides/grid.md
|
||||
- 更多 Appium 资源: resources.md
|
||||
- 贡献: contributing/index.md
|
||||
- Migration:
|
||||
- guides/migrating-1-to-2.md
|
||||
- Server/Driver Configuration:
|
||||
- guides/managing-exts.md
|
||||
- guides/branch-testing.md
|
||||
- guides/config.md
|
||||
- guides/security.md
|
||||
- guides/log-filters.md
|
||||
- guides/grid.md
|
||||
- guides/caching.md
|
||||
- guides/tls.md
|
||||
- Session Configuration:
|
||||
- guides/caps.md
|
||||
- guides/settings.md
|
||||
- guides/execute-methods.md
|
||||
- guides/context.md
|
||||
- guides/event-timing.md
|
||||
- 更多资源: resources.md
|
||||
- 开发:
|
||||
- developing/index.md
|
||||
- developing/build-drivers.md
|
||||
- developing/build-plugins.md
|
||||
- developing/build-docs.md
|
||||
- developing/build-doctor-checks.md
|
||||
- 贡献:
|
||||
- contributing/index.md
|
||||
- contributing/develop.md
|
||||
- contributing/config-system.md
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
authors:
|
||||
jlipps:
|
||||
name: 'Jonathan Lipps'
|
||||
description: 'Appium project lead. Learn more about Jonathan at https://jlipps.com'
|
||||
avatar: 'https://gravatar.com/avatar/fdaadd825d92709bcf7fe76dffa12c40681c6108c67bcddf19478b2d00e3794b.jpg'
|
||||
slug: 'jlipps'
|
||||
url: 'https://jlipps.com'
|
||||
srini:
|
||||
name: 'Srinivasan Sekar'
|
||||
description: 'Appium Maintainer'
|
||||
avatar: 'https://avatars.githubusercontent.com/u/8896549?v=4'
|
||||
slug: 'srini'
|
||||
url: 'https://srini.codes'
|
||||
sai:
|
||||
name: 'Sai krishna'
|
||||
description: 'Appium Maintainer'
|
||||
avatar: 'https://avatars.githubusercontent.com/u/4045041?v=4'
|
||||
slug: 'sai'
|
||||
url: 'https://saikrishna.tech'
|
||||
@@ -1,2 +1,2 @@
|
||||
# Blog
|
||||
# 博客
|
||||
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
---
|
||||
authors:
|
||||
- srini
|
||||
- sai
|
||||
date: 2024-07-13
|
||||
---
|
||||
|
||||
# Appium Conf 2024: Call for Proposals Now Open!
|
||||
|
||||
We're thrilled to announce that Appium Conf is back and going online for 2024! This is the official Appium conference where you can learn all about what's new in Appium 2.0 and explore the latest in automating tests for devices, mobile apps, and desktop applications.
|
||||
|
||||
<!-- more -->
|
||||
<div style="text-align:center;width:100%">
|
||||
<a href="https://confengine.com/conferences/appium-conf-2024/proposals?utm_medium=referal&utm_source=appium.io&utm_campaign=launch">
|
||||
<img id="image-36-42" alt="Appium Conf logo" src="https://appiumconf.com/wp-content/uploads/2021/06/Appium-Conf-Logo.svg" data-src="https://appiumconf.com/wp-content/uploads/2021/06/Appium-Conf-Logo.svg" decoding="async">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
||||
## Mark Your Calendars: September 13, 2024
|
||||
|
||||
Join us on September 13, 2024, for a full day of inspiring and practical talks from the Appium team and the world's top mobile testing and automation experts. With Appium 2.0 now well-established, this is your opportunity to dive deep and learn how to maximize its potential.
|
||||
|
||||
## Why Attend Appium Conf 2024?
|
||||
|
||||
1. **Gain Insights**: Hear interesting insights and inspiring case studies from global experts and the core Appium team.
|
||||
2. **Improve Your Strategy**: Gain valuable knowledge to enhance your own automation strategy.
|
||||
3. **Network**: Make connections with Appium users from around the globe.
|
||||
4. **Learn from Specialists**: Benefit from Appium specialists' expertise on a variety of topics.
|
||||
|
||||
## Call for Proposals: Share Your Knowledge!
|
||||
|
||||
We're looking for a diverse range of speakers from around the globe who are eager to share innovative ideas and use-cases of mobile test automation. If you have experience with Appium and insights that could benefit the community of test practitioners, we want to hear from you!
|
||||
|
||||
Don't miss this chance to contribute to the Appium community and showcase your expertise on a global stage.
|
||||
|
||||
## How to Submit Your Proposal
|
||||
|
||||
Visit our website at [https://appiumconf.com/](https://appiumconf.com/) and [Submit a Proposal](https://confengine.com/conferences/appium-conf-2024/proposals?utm_medium=referal&utm_source=appium.io&utm_campaign=launch). We're excited to see your innovative ideas and experiences with Appium!
|
||||
|
||||
## Stay Tuned
|
||||
|
||||
Keep an eye on our website and social media channels for updates on the conference schedule, keynote speakers, and more exciting details about Appium Conf 2024.
|
||||
|
||||
Join us in shaping the future of mobile and device test automation. Submit your proposal today and be part of this exciting event!
|
||||
@@ -0,0 +1,46 @@
|
||||
---
|
||||
authors:
|
||||
- jlipps
|
||||
date: 2024-06-10
|
||||
---
|
||||
|
||||
# Announcing BrowserStack as Appium's Strategic Partner
|
||||
|
||||
We have another exciting announcement for you! As part of our recently-announced [sponsorship and
|
||||
contributor compensation
|
||||
program](https://appium.io/docs/en/latest/blog/2024/05/14/announcing-appiums-sponsorship-program/),
|
||||
we are obviously in need of significant funding to support giving back to our contributor
|
||||
community. We are therefore extremely happy to announce one of our brand new exclusive Strategic Partners: <a
|
||||
href="https://www.browserstack.com/browserstack-appium?utm_campaigncode=701OW00000AoUTQYA3&utm_medium=partnered&utm_source=appium">BrowserStack</a>!
|
||||
|
||||
<!-- more -->
|
||||
|
||||
<div style="text-align:center;width:100%">
|
||||
<a href="https://www.browserstack.com/browserstack-appium?utm_campaigncode=701OW00000AoUTQYA3&utm_medium=partnered&utm_source=appium">
|
||||
<img src="/docs/en/latest/assets/images/sponsor-logo-browserstack-dark.png#only-dark" width="300" />
|
||||
<img src="/docs/en/latest/assets/images/sponsor-logo-browserstack-light.png#only-light" width="300" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
BrowserStack has long been recognized in the industry as having extensive support for Appium,
|
||||
allowing you to run functional and visual regression tests for your native and hybrid apps at scale
|
||||
across a variety of devices and platforms.
|
||||
|
||||
As a token of this collaboration, BrowserStack is extending a special premium access of
|
||||
BrowserStack App Automate product to Appium users. Discover more about this special offer at
|
||||
[browserstack.com](https://www.browserstack.com/browserstack-appium?utm_campaigncode=701OW00000AoUTQYA3&utm_medium=partnered&utm_source=appium&utm_term=partannouncement)
|
||||
|
||||
With the decision to come on board as Strategic Partner, BrowserStack is making a huge investment
|
||||
in Appium and the Appium community. BrowserStack is very interested in supporting the development
|
||||
of the Appium contributor base, and is proud to be a key part of our contributor compensation
|
||||
scheme. Without Strategic Partners like BrowserStack, we would not be able to maintain such
|
||||
a program! We welcome them with gratitude!
|
||||
|
||||
**About BrowserStack**
|
||||
|
||||
<em>BrowserStack is the world’s leading software testing platform powering over two million tests every
|
||||
day across 19 global data centers. BrowserStack helps Tesco, Shell, NVIDIA, Discovery, Wells Fargo,
|
||||
and over 50,000 customers deliver quality software at speed by moving testing to their Cloud.
|
||||
BrowserStack’s platform provides instant access to 20,000+ real mobile devices on a highly reliable
|
||||
cloud infrastructure that effortlessly scales as testing needs grow. With BrowserStack, Dev and QA
|
||||
teams can move fast while delivering an amazing experience for every customer.</em>
|
||||
@@ -0,0 +1,47 @@
|
||||
---
|
||||
authors:
|
||||
- jlipps
|
||||
date: 2024-06-06
|
||||
---
|
||||
|
||||
# Announcing HeadSpin as Appium's Development Partner
|
||||
|
||||
Recently, we announced a new [sponsorship and contributor compensation program](https://appium.io/docs/en/latest/blog/2024/05/14/announcing-appiums-sponsorship-program/) for the Appium
|
||||
project. As part of this program, we felt it was important to have a place to recognize those
|
||||
companies who make an extremely valuable kind of contribution to the project: dedicated maintainer
|
||||
time. Appium only survives because of the tireless efforts of the core maintainers, and some of us
|
||||
are only able to do this work because it is donated on behalf of our day jobs. We're therefore very
|
||||
excited to announce our first Development Partner: <a
|
||||
href="https://www.headspin.io/solutions/appium-mobile-test-automation">HeadSpin</a>!
|
||||
|
||||
<!-- more -->
|
||||
|
||||
<div style="text-align:center;width:100%">
|
||||
<a href="https://www.headspin.io/solutions/appium-mobile-test-automation"><img
|
||||
src="/docs/en/latest/assets/images/sponsor-logo-headspin.png" style="width: 300px"/></a>
|
||||
</div>
|
||||
|
||||
HeadSpin is a real device performance and UX analysis cloud, with full support for Appium. When you
|
||||
run your Appium tests on HeadSpin, you get access to comprehensive performance reports about your
|
||||
app, including potential network, system resource, or user experience issues. HeadSpin supports
|
||||
a variety of complex testing modes, including hooking up your own devices to the cloud, injecting
|
||||
and receiving video and audio to/from devices, working with media (streaming) platforms, and more.
|
||||
|
||||
HeadSpin has been one of the Appium project's biggest supporters for a long time, and currently
|
||||
employs two of Appium's core maintainers, donating a significant portion of their time towards the
|
||||
development, maintenance, support, and leadership of open source Appium repos. HeadSpin has also
|
||||
worked hard towards Appium's vision of a robust Appium ecosystem, contributing several
|
||||
HeadSpin-maintained Appium drivers and plugins to the community:
|
||||
|
||||
- A [Roku TV driver](https://github.com/headspinio/appium-roku-driver)
|
||||
- A [Tizen TV driver](https://github.com/headspinio/appium-tizen-tv-driver/)
|
||||
- A [LG webOS TV driver](https://github.com/headspinio/appium-lg-webos-driver/)
|
||||
- A [Unity game automation plugin](https://github.com/headspinio/appium-altunity-plugin)
|
||||
|
||||
These are all free and open source Appium components that HeadSpin has donated to the community
|
||||
with the goal of fostering collaboration in these areas of media app automation.
|
||||
|
||||
Looking to the future, HeadSpin plans to continue supporting Appium in significant ways, both as
|
||||
a Development Partner and a significant contributor to the Appium ecosystem. The Appium project is
|
||||
deeply indebted to HeadSpin for all of its support, and is proud to take this moment to recognize
|
||||
HeadSpin as our first official Development Partner!
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
authors:
|
||||
- jlipps
|
||||
date: 2024-06-10
|
||||
---
|
||||
|
||||
# Announcing Sauce Labs as Appium's Strategic Partner
|
||||
|
||||
The good news surrounding our new [sponsorship and contributor compensation
|
||||
program](https://appium.io/docs/en/latest/blog/2024/05/14/announcing-appiums-sponsorship-program/)
|
||||
just keeps coming! Effective immediately we are fortunate and excited to be able to welcome a new Strategic Partner to
|
||||
the Appium project: <a href="https://saucelabs.com/resources/blog/appium-strategic-partner">Sauce Labs</a>!
|
||||
|
||||
<!-- more -->
|
||||
|
||||
<div style="text-align:center;width:100%">
|
||||
<a href="https://saucelabs.com/resources/blog/appium-strategic-partner"><img src="/docs/en/latest/assets/images/sponsor-logo-sauce.png" style="width: 300px"/></a>
|
||||
</div>
|
||||
|
||||
Sauce Labs has a long history of support for the Appium project. In fact, Appium was initially
|
||||
incubated at Sauce Labs under the direction of [@jlipps](https://github.com/jlipps), in the early
|
||||
days of mobile automation. Sauce Labs always envisioned Appium as being a project for and by the
|
||||
community, and cemented this perspective in 2016 with the donation of Appium to the JS Foundation
|
||||
(now the [OpenJS Foundation](https://openjsf.org/)). To this day, developers associated with Sauce
|
||||
Labs continue to help maintain various parts of the Appium project.
|
||||
|
||||
Now, Sauce Labs is making a further commitment to support Appium financially as a way to enable
|
||||
our new contributor compensation program. We're very excited about this program as a vehicle for
|
||||
getting more people involved in Appium's development and maintenance. With the help of Strategic
|
||||
Partners like Sauce Labs, our project will be better able to meet the needs of our constantly
|
||||
growing userbase, and attract new developers (like you?) to build the Appium ecosystem together.
|
||||
Needless to say, we are extremely grateful for Sauce Labs's support!
|
||||
|
||||
**About Sauce Labs**
|
||||
|
||||
<em>Sauce Labs is the leader in continuous quality. Our Platform for Test helps organizations transform software testing from a chore to a competitive advantage. With decades of expertise and deep roots in the Selenium and Appium open source communities, high performing software teams use our platform to execute any testing workload across thousands of different devices, browsers, and operating systems—anywhere, any time, and at any scale. For more information, please visit [saucelabs.com](https://saucelabs.com).</em>
|
||||
@@ -0,0 +1,48 @@
|
||||
---
|
||||
authors:
|
||||
- jlipps
|
||||
date: 2024-05-14
|
||||
---
|
||||
|
||||
# Announcing Appium's Sponsorship Program
|
||||
|
||||
Over the last several months, the Appium team has been working on a sponsorship program that is
|
||||
designed to help give back to Appium's contributors as well as to recognize important partner
|
||||
individuals or companies in the industry. This program is now officially launching! Let's dive into
|
||||
some of the details.
|
||||
|
||||
<!-- more -->
|
||||
|
||||
First of all, the details and fine print will always be available at Appium's [Governance
|
||||
document](https://github.com/appium/appium/blob/master/GOVERNANCE.md), which outlines how the
|
||||
project is run, who makes project decisions, etc... This document will be kept up to date with the
|
||||
latest details of the program, so that's the place to bookmark for reference!
|
||||
|
||||
As of now, Appium has opened up sponsorship opportunities via our [OpenCollective
|
||||
page](https://opencollective.com/appium). OpenCollective is a service which allows people to donate
|
||||
to open source groups. We've defined a number of giving tiers for donations, within a very wide
|
||||
range, and welcome any financial amount with gratitude! Based on the donation tier, sponsorship
|
||||
comes with a variety of benefits, including recognition at different places in the Appium repo,
|
||||
docs, and website. In addition to financial sponsorship, the project has decided to recognize
|
||||
significant donations of project leadership and maintenance efforts from companies who have
|
||||
invested heavily into Appium with their employees' time (we call these "Development Partners").
|
||||
|
||||
What will the project do with the funds raised through this sponsorship program? We aim to give it
|
||||
all back to the Appium community! With the details again available at our [Governance document](https://github.com/appium/appium/blob/master/GOVERNANCE.md),
|
||||
we have implemented a scheme very much inspired by our friends at [WebdriverIO](https://webdriver.io/blog/2024/02/15/new-contributor-stipend-program) (thanks WDIO
|
||||
friends!) There are three groups of contributors to Appium that this scheme supports: (1) project
|
||||
maintainers (those who devote consistent time to leading the project), (2) project contributors
|
||||
(anyone in the wider community that contributes code or documentation), and (3) upstream projects
|
||||
(other open source projects Appium relies heavily on to work well). Each month, the funds raised
|
||||
via sponsorship will be distributed in different amounts to each of these groups. So if you
|
||||
make a valuable contribution to Appium moving forward, you'll be eligible to get some cash as
|
||||
a thank-you! (You'll need to have your own OpenCollective account in good standing to receive
|
||||
funds).
|
||||
|
||||
We're very excited about this new program, and hope that it helps existing and new contributors to
|
||||
feel even better about donating their time to making Appium better. And of course, none of this
|
||||
would be possible without the amazing Partners and Sponsors we hope to attract! On that note, stay
|
||||
tuned over the next few weeks as we unveil some of our initial program launch Partners! And if you
|
||||
or your company would like to participate by supporting Appium financially, you are most welcome.
|
||||
Just head over to our OpenCollective page and choose the tier that reflects your desired
|
||||
contribution level.
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
authors:
|
||||
- jlipps
|
||||
date: 2024-03-07
|
||||
---
|
||||
|
||||
# Hello World!
|
||||
|
||||
This is the first post in the Appium blog. There's not much to see here yet. We're creating this
|
||||
space so we can make announcements or post other news, or information about events, that don't
|
||||
really fit inside the documentation itself.
|
||||
|
||||
<!-- more -->
|
||||
|
||||
Stay tuned for more!
|
||||
@@ -1,5 +1,69 @@
|
||||
---
|
||||
title: Server CLI Args
|
||||
hide:
|
||||
- toc
|
||||
|
||||
title: Server Command-Line Arguments
|
||||
---
|
||||
|
||||
TODO
|
||||
To start the Appium server, you may either run `appium` or `appium server`. The `server` subcommand
|
||||
is considered to be the default, so if you omit it, Appium will interpret this as your request to
|
||||
run the Appium server.
|
||||
|
||||
The invocation of `appium` (or `appium server`) can take a number of arguments, which are detailed
|
||||
below.
|
||||
|
||||
!!! note
|
||||
|
||||
All of these arguments can be set via a [Configuration File](../guides/config.md) instead if
|
||||
you want. Any arguments set on the command line will override any arguments found in
|
||||
a configuration file.
|
||||
|
||||
|
||||
|<div style="width:12em">Argument</div>|Description|Type|<div style="width:8em">Default</div>|Aliases|
|
||||
|--|--|--|--|--|
|
||||
|`--address`|IP address to listen on|string|`0.0.0.0`|`-a`|
|
||||
|`--allow-cors`|Whether the Appium server should allow web browser connections from any host|boolean|`false`||
|
||||
|`--allow-insecure`|Set which insecure features are allowed to run in this server's sessions. Features are defined on a driver level; see documentation for more details. Note that features defined via `--deny-insecure` will be disabled, even if also listed here. If string, a path to a text file containing policy or a comma-delimited list.|array<string>|`[]`||
|
||||
|`--base-path`|Base path to use as the prefix for all webdriver routes running on the server|string|`""`|`-pa`|
|
||||
|`--callback-address`|Callback IP address (default: same as `--address`)|string||`-ca`|
|
||||
|`--callback-port`|Callback port (default: same as `--port`) (Value must be between `1` and `65535`)|integer|`4723`|`-cp`|
|
||||
|`--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`|Set the default desired capabilities, which will be set on each session unless overridden by received capabilities. If a string, a path to a JSON file containing the capabilities, or raw JSON.|object||`-dc`|
|
||||
|`--deny-insecure`|Set which insecure features are not allowed to run in this server's sessions. Features are defined on a driver level; see documentation for more details. Features listed here will not be enabled even if also listed in `--allow-insecure`, and even if `--relaxed-security` is enabled. If string, a path to a text file containing policy or a comma-delimited list.|array<string>|`[]`||
|
||||
|`--driver`|Driver-specific configuration. Keys should correspond to driver package names|object|||
|
||||
|`--drivers-import-chunk-size`|The maximum amount of drivers that could be imported in parallel on server startup|number|`3`||
|
||||
|`--keep-alive-timeout`|Number of seconds the Appium server should apply as both the keep-alive timeout and the connection timeout for all requests. Setting this to `0` disables the timeout.|integer|`600`|`-ka`|
|
||||
|`--local-timezone`|Use local timezone for timestamps|boolean|`false`||
|
||||
|`--log`|Also send log output to this file|string||`-g`|
|
||||
|`--log-filters`|One or more log filtering rules|array|||
|
||||
|`--log-level`|Log level (console[:file]) (Value must be one of: `info`, `info:debug`, `info:info`, `info:warn`, `info:error`, `warn`, `warn:debug`, `warn:info`, `warn:warn`, `warn:error`, `error`, `error:debug`, `error:info`, `error:warn`, `error:error`, `debug`, `debug:debug`, `debug:info`, `debug:warn`, `debug:error`)|string|`debug`||
|
||||
|`--log-format`|Log format (Value must be to one of: `text`, `json`, `pretty_json`). If logs are printed as JSON then the text coloring is always disabled.|string|`text`||
|
||||
|`--log-no-colors`|Do not use color in console output|boolean|`false`||
|
||||
|`--log-timestamp`|Show timestamps in console output|boolean|`false`||
|
||||
|`--long-stacktrace`|Add long stack traces to log entries. Recommended for debugging only.|boolean|`false`||
|
||||
|`--no-perms-check`|Skip various permission checks on the server startup|boolean|`false`||
|
||||
|`--nodeconfig`|Path to configuration JSON file to register Appium as a node with Selenium Grid 3; otherwise the configuration itself|string|||
|
||||
|`--plugin`|Plugin-specific configuration. Keys should correspond to plugin package names|object|||
|
||||
|`--plugins-import-chunk-size`|The maximum amount of plugins that could be imported in parallel on server startup|number|`7`||
|
||||
|`--port`|Port to listen on (Value must be between `1` and `65535`)|integer|`4723`|`-p`|
|
||||
|`--relaxed-security`|Disable additional security checks, so it is possible to use some advanced features, provided by drivers supporting this option. Only enable it if all the clients are in the trusted network and it's not the case if a client could potentially break out of the session sandbox. Specific features can be overridden by using `--deny-insecure`|boolean|`false`||
|
||||
|`--session-override`|Enables session override (clobbering)|boolean|`false`||
|
||||
|`--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`|Cause sessions to fail if desired caps are sent in that Appium does not recognize as valid for the selected device|boolean|`false`||
|
||||
|`--tmp`|Absolute path to directory Appium can use to manage temp files|string|Windows: `C:\Windows\Temp`<br>Others: `/tmp`||
|
||||
|`--trace-dir`|Absolute path to directory Appium can use to save iOS instrument traces|string|`<tmp>/appium-instruments`||
|
||||
|`--use-drivers`|A list of drivers to activate. By default, all installed drivers will be activated.|array<string>|`[]`||
|
||||
|`--use-plugins`|A list of plugins to activate. To activate all plugins, the value should be an array with a single item `"all"`.|array<string>|`[]`||
|
||||
|`--webhook`|Also send log output to this http listener|string||`-G`|
|
||||
|
||||
The following arguments are used for information retrieval, after which the server will automatically exit. They are therefore meant for reference or debug purposes.
|
||||
|
||||
|<div style="width:10em">Argument</div>|Description|Alias|
|
||||
|--|--|--|
|
||||
|`--help`|Print instructions on using the Appium command-line. This argument can also be used for any Appium subcommands.|`-h`|
|
||||
|`--show-build-info`|Print detailed information on the Appium server version||
|
||||
|`--show-config`|Print the current Appium server configuration details||
|
||||
|`--show-debug-info`|Print information on the current environment: details about the operating system, Node.js, and Appium itself||
|
||||
|`--version`|Print the Appium server version|`-v`|
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
---
|
||||
hide:
|
||||
- toc
|
||||
|
||||
title: Server Environment Variables
|
||||
---
|
||||
|
||||
The primary way of configuring the Appium server is via [Command-Line Arguments](./args.md). However, some more
|
||||
advanced features are toggled or configured via environment variables. To set environment
|
||||
variables, refer to the documentation for your operating system and terminal. These are the
|
||||
environment variables that the Appium server understands:
|
||||
|
||||
|Variable |Description|
|
||||
|--------|-----------|
|
||||
|`APPIUM_HOME`|By default, Appium creates a directory called `.appium` in the home directory for your system user. You can adjust the directory with this variable, as detailed in the [Managing Extensions](../guides/managing-exts.md) guide.|
|
||||
|`APPIUM_TMP_DIR`|By default, Appium uses a random temporary directory for many of its operations. If you wish to use a specific directory, you may do so by including an absolute path as the value of this variable. The behaviour is equivalent to using the `--tmp` CLI arg.|
|
||||
|`APPIUM_PREFER_SYSTEM_UNZIP`|Set to `0` or `false` to request that Appium not use the `unzip` binary included on your system for unzipping downloaded apps or other artifacts. Instead it will use a JS-based unzip library. This could help on some systems with non-existent or non-standard `unzip` commands. Note that if unzipping fails using the system library, the fallback library will be attempted in any case, so setting this env var merely saves time in the event you know the system unzip will fail.|
|
||||
|`APPIUM_HOST`|Same as the `--address` CLI arg|
|
||||
|`APPIUM_PORT`|Same as the `--port` CLI arg|
|
||||
|`APPIUM_RELOAD_EXTENSIONS`|Set to `1` to cause Appium to re-require extensions when new sessions are created. This is mostly useful for [building extensions](../developing/build-drivers.md)|
|
||||
|`APPIUM_OMIT_PEER_DEPS`|Adds `--omit=peer` to all the NPM commands run internally by Appium. Mostly an internal feature.|
|
||||
|`APPIUM_APPS_CACHE_MAX_AGE`|Allows to set the maximum age (in minutes) for [cached applications](../guides/caching.md). The default value is `60 * 24` (24 hours). Do not set it to a lower number than the duration of a single session startup.|
|
||||
|`APPIUM_APPS_CACHE_MAX_ITEMS`|Allows to set the maximum amount of [cached applications](../guides/caching.md). The default value is `1024`. Do not set it to a lower number than the amount of apps in all parallel sessions per process.|
|
||||
|`APPIUM_APPS_CACHE_IGNORE_URL_QUERY`| If the above option is enabled then the 'search' part of the app URL will be cut off from cache keys. See the corresponding
|
||||
[feature request](https://discuss.appium.io/t/regarding-app-caching-when-using-aws-s3-presigned-urls/42713)
|
||||
for more details. Disabled by default.|
|
||||
@@ -1,5 +1,226 @@
|
||||
---
|
||||
title: The Extension CLI
|
||||
title: Extension Command-Line Usage
|
||||
---
|
||||
|
||||
TODO
|
||||
Appium allows for the flexible installation and management of various _extensions_, such as _drivers_
|
||||
(which provide Appium with the capability to automate a given platform) and _plugins_ (which can
|
||||
augment or alter the way individual Appium commands work). For a conceptual understanding of these
|
||||
entities, please review the [Introduction](../intro/index.md).
|
||||
|
||||
Management of drivers and plugins is handled by Appium's Extension CLI (command-line interface).
|
||||
|
||||
!!! note
|
||||
|
||||
This reference uses placeholders to refer to various options. Anywhere you see one of these
|
||||
placeholders in the reference, ensure you replace it with the correct type of actual content.
|
||||
|
||||
|Placeholder|Meaning|
|
||||
|--|--|
|
||||
|`<ext-type>`|"Extension type". It should be either `driver` or `plugin`. All the Extension CLI commands can be used with either drivers or plugins, so you must specify which type of extension will be used|
|
||||
|`<ext-name>`|"Extension name". This is the short name of the extension found in a call to `appium <ext-type> list`. This is distinct from the NPM package name of the extension or, in general, the "install spec" of the extension.|
|
||||
|`<install-spec>`|"Install specification". This refers to the string used to indicate what extension Appium should install.|
|
||||
|`<install-source>`|This refers to the method that Appium should use to install an extension.|
|
||||
|
||||
## Commands
|
||||
|
||||
All Extension CLI commands begin with `appium <ext-type>`, i.e., either `appium driver` or `appium
|
||||
plugin`.
|
||||
|
||||
All Extension CLI commands can take an optional `--json` argument, which will return the result of
|
||||
the command as a machine-readable JSON string rather than the standard output, which is colourized
|
||||
and tuned for human consumption.
|
||||
|
||||
### `doctor`
|
||||
|
||||
Run doctor checks for the given extension, which validate whether the extension has its prerequisites
|
||||
configured correctly. Note that not all extensions include doctor checks. See the
|
||||
[Building Doctor Checks](../developing/build-doctor-checks.md) tutorial for more details on
|
||||
how to create them.
|
||||
|
||||
Usage:
|
||||
|
||||
```
|
||||
appium <ext-type> doctor <ext-name>
|
||||
```
|
||||
|
||||
Required arguments:
|
||||
|
||||
- `<ext-type>`: must be `driver` or `plugin`
|
||||
- `<ext-name>`: the name of the extension whose doctor checks you want to run
|
||||
|
||||
Optional arguments:
|
||||
|
||||
- `--json`: return the result in JSON format
|
||||
|
||||
Example (run doctor checks for the UiAutomator2 driver):
|
||||
|
||||
```
|
||||
appium driver doctor uiautomator2
|
||||
```
|
||||
|
||||
### `install`
|
||||
|
||||
Install an extension. If successful, respond with the short name of the extension which can be used
|
||||
in other invocations of the Extension CLI. If the extension is a driver, also note which platforms
|
||||
may be used with the driver.
|
||||
|
||||
Usage:
|
||||
|
||||
```
|
||||
appium <ext-type> install <install-spec> [--source=<install-source>] [--package=<package-name>] [--json]
|
||||
```
|
||||
|
||||
Required arguments:
|
||||
|
||||
- `<ext-type>`: must be `driver` or `plugin`
|
||||
- `<install-spec>`: this is the name, location, and/or version of the extension you want to
|
||||
install. Its possible values are dependent on the `<install-source>` (see below).
|
||||
|
||||
Optional arguments:
|
||||
|
||||
- `--source`: this directs Appium where to find your extension. See below for a table of possible
|
||||
source types and corresponding install specification.
|
||||
- `--package`: when `<install-source>` is `git` or `github`, `--package` is required. It should be
|
||||
the Node.js package name of the extension. Without this information, Appium will not be able to
|
||||
find the installed package.
|
||||
- `--json`: return the result in JSON format
|
||||
|
||||
|Install source type|Behaviour|
|
||||
|--|--|
|
||||
|None|This is the default behaviour when no `--source` is used. In this case, Appium will look at `<install-spec>` and match it against the name of extensions available when running `appium <ext-type> list`, i.e., against the officially recognized extension names. If found, it will install that extension at the latest version via NPM|
|
||||
|`npm`|Install an extension based on its NPM package name. Here, `<install-spec>` must be the NPM package name with any additional NPM installation modifiers, like version (see below)|
|
||||
|`github`|Install an extension via a GitHub spec of the form `<org>/<repo>`|
|
||||
|`git`|Install an extension via a Git URL (e.g., `git+ssh://git-host.com/repo.git`)|
|
||||
|`local`|Install an extension via a local path. This must be a path to the directory where the Node.js package information for the driver is located.|
|
||||
|
||||
#### NPM-based `<install-spec>`
|
||||
|
||||
When Appium is installing an extension via NPM (as is the case when `--source` is either omitted or
|
||||
set to `npm`), the `<install-spec>` can be complex, and can include any kind of information allowed
|
||||
by `npm install`:
|
||||
|
||||
- `[@scope]/<name>`
|
||||
- `[@scope]/<name>@<version>`
|
||||
- `[@scope]/<name>@<tag>`
|
||||
- `[@scope]/<name>@<version range>`
|
||||
|
||||
#### Examples
|
||||
|
||||
- Install the latest XCUITest driver:
|
||||
|
||||
```
|
||||
appium driver install xcuitest
|
||||
```
|
||||
|
||||
- Install the XCUITest driver at version 4.11.1:
|
||||
|
||||
```
|
||||
appium driver install xcuitest@4.11.1
|
||||
```
|
||||
|
||||
- Install the `beta` version of the `@appium/fake-driver` from NPM:
|
||||
|
||||
```
|
||||
appium driver install --source=npm @appium/fake-driver@beta
|
||||
```
|
||||
|
||||
- Install a locally-developed plugin:
|
||||
|
||||
```
|
||||
appium plugin install --source=local /path/to/my/plugin
|
||||
```
|
||||
|
||||
### `list`
|
||||
|
||||
List installed and available extensions. "Available" extensions include those which are officially
|
||||
recognized by the Appium team, but you are not limited to installing only the extensions displayed
|
||||
in this list.
|
||||
|
||||
Usage:
|
||||
|
||||
```
|
||||
appium <ext-type> list [--installed] [--updates] [--json]
|
||||
```
|
||||
|
||||
Required arguments:
|
||||
|
||||
- `<ext-type>`: must be `driver` or `plugin`
|
||||
|
||||
Optional arguments:
|
||||
|
||||
- `--installed`: show only installed extensions, not installed plus available extensions
|
||||
- `--updates`: for extensions installed via NPM, display a message if there are any updates
|
||||
- `--json`: return the result in JSON format
|
||||
|
||||
### `run`
|
||||
|
||||
Run a script included in an extension package. Extension authors can include runnable scripts that
|
||||
assist with setup or perform other tasks. These scripts are given names (called the `<script-name>`
|
||||
in this reference) by extension authors and will generally be documented in extension
|
||||
documentation.
|
||||
|
||||
Usage:
|
||||
|
||||
```
|
||||
appium <ext-type> run <ext-name> [--json] <script-name> [script-args]
|
||||
```
|
||||
|
||||
Required arguments:
|
||||
|
||||
- `<ext-type>`: must be `driver` or `plugin`
|
||||
- `<ext-name>`: the name of the extension whose script you want to run
|
||||
- `<script-name>`: the name of the script the extension has published
|
||||
|
||||
Optional arguments:
|
||||
|
||||
- `script-args`: any arguments that Appium does not interpret as belonging to its own set of
|
||||
arguments will be passed along to the extension script
|
||||
- `--json`: return the result in JSON format
|
||||
|
||||
Example (run the `reset` script included with the UiAutomator2 driver):
|
||||
|
||||
```
|
||||
appium driver run uiautomator2 reset
|
||||
```
|
||||
|
||||
### `update`
|
||||
|
||||
Update one or more extensions that have been installed via NPM. By default, Appium will not
|
||||
automatically update any extension past a major version boundary, so as to prevent
|
||||
unintended breaking changes.
|
||||
|
||||
Usage:
|
||||
|
||||
```
|
||||
appium <ext-type> update <ext-name> [--unsafe] [--json]
|
||||
```
|
||||
|
||||
Required arguments:
|
||||
|
||||
- `<ext-type>`: must be `driver` or `plugin`
|
||||
- `<ext-name>`: the name of the extension to update, or the string `installed` (which will update
|
||||
all installed extensions)
|
||||
|
||||
Optional arguments:
|
||||
|
||||
- `--unsafe`: direct Appium to go ahead and update past a major version boundary
|
||||
- `--json`: return the result in JSON format
|
||||
|
||||
### `uninstall`
|
||||
|
||||
Remove an installed extension.
|
||||
|
||||
Usage:
|
||||
|
||||
```
|
||||
appium <ext-type> uninstall <ext-name> [--json]
|
||||
```
|
||||
|
||||
Required arguments:
|
||||
|
||||
- `<ext-type>`: must be `driver` or `plugin`
|
||||
- `<ext-name>`: the name of the extension to uninstall
|
||||
|
||||
Optional arguments:
|
||||
|
||||
- `--json`: return the result in JSON format
|
||||
|
||||
@@ -1,5 +1,26 @@
|
||||
---
|
||||
title: 命令行介绍
|
||||
hide:
|
||||
- toc
|
||||
|
||||
title: Command-Line Overview
|
||||
---
|
||||
|
||||
TODO
|
||||
Appium provides a command-line executable (`appium`), which will likely be your main way of interacting with
|
||||
the Appium server. This section of the Appium documentation describes how to use this executable.
|
||||
|
||||
To start off, you can run `appium -v` or `appium --version` to return the installed version,
|
||||
or run `appium -h` or `appium --help` to return the help message.
|
||||
|
||||
The main `appium` executable provides the following subcommands:
|
||||
|
||||
1. `appium server` (or simply `appium`) - launch an Appium server
|
||||
- [See here for accepted arguments](./args.md)
|
||||
- For advanced features, [see here for accepted environment variables](./env-vars.md)
|
||||
2. `appium driver` - manage Appium drivers
|
||||
- [See here for details](./extensions.md)
|
||||
3. `appium plugin` - manage Appium plugins
|
||||
- [See here for details](./extensions.md)
|
||||
4. `appium setup` - batch install a preset of drivers and plugins
|
||||
- [See here for details](./setup.md)
|
||||
|
||||
Like the main command, you can also run each subcommand with the `-h` or `--help` flag to learn more about it.
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
---
|
||||
hide:
|
||||
- toc
|
||||
|
||||
title: Setup Command-Line Usage
|
||||
---
|
||||
|
||||
The `setup` command aims to simplify the initial procedure of setting up Appium. It allows to install
|
||||
multiple extensions (drivers/plugins) in one go, without the need to run
|
||||
`appium <ext-name> install <ext-name>` multiple times.
|
||||
|
||||
The command has several presets that can be used to install different sets of extensions.
|
||||
The presets are as follows:
|
||||
|
||||
|Preset|Installation Command|Included Drivers|Included Plugins|
|
||||
|--|--|--|--|
|
||||
|Mobile|`appium setup mobile` or `appium setup`|`uiautomator2`, `xcuitest`[^1], `espresso`|`images`|
|
||||
|Desktop application|`appium setup desktop`|`mac2`[^1], `windows`[^2]|`images`|
|
||||
|Desktop browser|`appium setup browser`|`safari`[^1], `gecko`, `chromium`|`images`|
|
||||
|
||||
Attempting to install a preset while already having one or more of its included extensions installed
|
||||
will only install the missing extensions.
|
||||
|
||||
Refer to the [Ecosystem documentation](../ecosystem/index.md) to learn more about the extensions
|
||||
listed above.
|
||||
|
||||
[^1]: Only installed if the host machine is running macOS.
|
||||
[^2]: Only installed if the host machine is running Windows.
|
||||
@@ -0,0 +1,542 @@
|
||||
# Driver: base-driver
|
||||
|
||||
### `createSession`
|
||||
|
||||
`POST` **`/session`**
|
||||
|
||||
Historically the first two arguments were reserved for JSONWP capabilities.
|
||||
Appium 2 has dropped the support of these, so now we only accept capability
|
||||
objects in W3C format and thus allow any of the three arguments to represent
|
||||
the latter.
|
||||
|
||||
**`See`**
|
||||
|
||||
[https://w3c.github.io/webdriver/#new-session](https://w3c.github.io/webdriver/#new-session)
|
||||
|
||||
<!-- comment source: multiple -->
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
| :------ | :------ | :------ |
|
||||
| `desiredCapabilities?` | `W3CDriverCaps`<`C`\> | the new session capabilities |
|
||||
| `requiredCapabilities?` | `W3CDriverCaps`<`C`\> | another place the new session capabilities could be sent (typically left undefined) |
|
||||
| `capabilities?` | `W3CDriverCaps`<`C`\> | another place the new session capabilities could be sent (typically left undefined) |
|
||||
|
||||
#### Response
|
||||
|
||||
`CreateResult`
|
||||
|
||||
The capabilities object representing the created session
|
||||
|
||||
### `deleteSession`
|
||||
|
||||
`DELETE` **`/session/:sessionId`**
|
||||
|
||||
Returns capabilities for the session and event history (if applicable)
|
||||
|
||||
<!-- comment source: method-signature -->
|
||||
|
||||
#### Response
|
||||
|
||||
`SingularSessionData`<`C`, `SessionData`\>
|
||||
|
||||
A session data object
|
||||
|
||||
### `getSession`
|
||||
|
||||
`GET` **`/session/:sessionId`**
|
||||
|
||||
Returns capabilities for the session and event history (if applicable)
|
||||
|
||||
<!-- comment source: multiple -->
|
||||
|
||||
#### Response
|
||||
|
||||
`SingularSessionData`<`C`, `SessionData`\>
|
||||
|
||||
A session data object
|
||||
|
||||
### `findElement`
|
||||
|
||||
`POST` **`/session/:sessionId/element`**
|
||||
|
||||
Find a UI element given a locator strategy and a selector, erroring if it can't be found
|
||||
|
||||
**`See`**
|
||||
|
||||
[https://w3c.github.io/webdriver/#find-element](https://w3c.github.io/webdriver/#find-element)
|
||||
|
||||
<!-- comment source: method-signature -->
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
| :------ | :------ | :------ |
|
||||
| `using` | `string` | the locator strategy |
|
||||
| `value` | `string` | the selector to combine with the strategy to find the specific element |
|
||||
|
||||
#### Response
|
||||
|
||||
`Element`<`string`\>
|
||||
|
||||
The element object encoding the element id which can be used in element-related
|
||||
commands
|
||||
|
||||
### `findElementFromElement`
|
||||
|
||||
`POST` **`/session/:sessionId/element/:elementId/element`**
|
||||
|
||||
Find a UI element given a locator strategy and a selector, erroring if it can't be found. Only
|
||||
look for elements among the set of descendants of a given element
|
||||
|
||||
**`See`**
|
||||
|
||||
[https://w3c.github.io/webdriver/#find-element-from-element](https://w3c.github.io/webdriver/#find-element-from-element)
|
||||
|
||||
<!-- comment source: method-signature -->
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
| :------ | :------ | :------ |
|
||||
| `using` | `string` | the locator strategy |
|
||||
| `value` | `string` | the selector to combine with the strategy to find the specific element |
|
||||
|
||||
#### Response
|
||||
|
||||
`Element`<`string`\>
|
||||
|
||||
The element object encoding the element id which can be used in element-related
|
||||
commands
|
||||
|
||||
### `findElementFromShadowRoot`
|
||||
|
||||
`POST` **`/session/:sessionId/shadow/:shadowId/element`**
|
||||
|
||||
Find an element from a shadow root
|
||||
|
||||
**`See`**
|
||||
|
||||
[https://w3c.github.io/webdriver/#find-element-from-shadow-root](https://w3c.github.io/webdriver/#find-element-from-shadow-root)
|
||||
|
||||
<!-- comment source: method-signature -->
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
| :------ | :------ | :------ |
|
||||
| `using` | `string` | the locator strategy |
|
||||
| `value` | `string` | the selector to combine with the strategy to find the specific elements |
|
||||
|
||||
#### Response
|
||||
|
||||
`Element`<`string`\>
|
||||
|
||||
The element inside the shadow root matching the selector
|
||||
|
||||
### `findElements`
|
||||
|
||||
`POST` **`/session/:sessionId/elements`**
|
||||
|
||||
Find a a list of all UI elements matching a given a locator strategy and a selector
|
||||
|
||||
**`See`**
|
||||
|
||||
[https://w3c.github.io/webdriver/#find-elements](https://w3c.github.io/webdriver/#find-elements)
|
||||
|
||||
<!-- comment source: method-signature -->
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
| :------ | :------ | :------ |
|
||||
| `using` | `string` | the locator strategy |
|
||||
| `value` | `string` | the selector to combine with the strategy to find the specific elements |
|
||||
|
||||
#### Response
|
||||
|
||||
`Element`<`string`\>[]
|
||||
|
||||
A possibly-empty list of element objects
|
||||
|
||||
### `findElementsFromElement`
|
||||
|
||||
`POST` **`/session/:sessionId/element/:elementId/elements`**
|
||||
|
||||
Find a a list of all UI elements matching a given a locator strategy and a selector. Only
|
||||
look for elements among the set of descendants of a given element
|
||||
|
||||
**`See`**
|
||||
|
||||
[https://w3c.github.io/webdriver/#find-elements-from-element](https://w3c.github.io/webdriver/#find-elements-from-element)
|
||||
|
||||
<!-- comment source: method-signature -->
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
| :------ | :------ | :------ |
|
||||
| `using` | `string` | the locator strategy |
|
||||
| `value` | `string` | the selector to combine with the strategy to find the specific elements |
|
||||
|
||||
#### Response
|
||||
|
||||
`Element`<`string`\>[]
|
||||
|
||||
A possibly-empty list of element objects
|
||||
|
||||
### `findElementsFromShadowRoot`
|
||||
|
||||
`POST` **`/session/:sessionId/shadow/:shadowId/elements`**
|
||||
|
||||
Find elements from a shadow root
|
||||
|
||||
**`See`**
|
||||
|
||||
[https://w3c.github.io/webdriver/#find-element-from-shadow-root](https://w3c.github.io/webdriver/#find-element-from-shadow-root)
|
||||
|
||||
<!-- comment source: method-signature -->
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
| :------ | :------ | :------ |
|
||||
| `using` | `string` | the locator strategy |
|
||||
| `value` | `string` | the selector to combine with the strategy to find the specific elements |
|
||||
|
||||
#### Response
|
||||
|
||||
`Element`<`string`\>[]
|
||||
|
||||
A possibly empty list of elements inside the shadow root matching the selector
|
||||
|
||||
### `getLog`
|
||||
|
||||
`POST` **`/session/:sessionId/log`**
|
||||
|
||||
Get the log for a given log type.
|
||||
|
||||
!!! warning "Deprecated"
|
||||
|
||||
Please use the `/session/:sessionId/se/log` endpoint instead
|
||||
|
||||
<!-- comment source: method-signature -->
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
| :------ | :------ | :------ |
|
||||
| `type` | `string` | Name/key of log type as defined in ILogCommands.supportedLogTypes. |
|
||||
|
||||
#### Response
|
||||
|
||||
`any`
|
||||
|
||||
### `getLog`
|
||||
|
||||
`POST` **`/session/:sessionId/se/log`**
|
||||
|
||||
Get the log for a given log type.
|
||||
|
||||
<!-- comment source: method-signature -->
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
| :------ | :------ | :------ |
|
||||
| `type` | `string` | Name/key of log type as defined in ILogCommands.supportedLogTypes. |
|
||||
|
||||
#### Response
|
||||
|
||||
`any`
|
||||
|
||||
### `getLogEvents`
|
||||
|
||||
`POST` **`/session/:sessionId/appium/events`**
|
||||
|
||||
Get a list of events that have occurred in the current session
|
||||
|
||||
<!-- comment source: method-signature -->
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
| :------ | :------ | :------ |
|
||||
| `type?` | `string` \| `string`[] | filter the returned events by including one or more types |
|
||||
|
||||
#### Response
|
||||
|
||||
`EventHistory` \| `Record`<`string`, `number`\>
|
||||
|
||||
The event history for the session
|
||||
|
||||
### `getLogTypes`
|
||||
|
||||
`GET` **`/session/:sessionId/log/types`**
|
||||
|
||||
Get available log types as a list of strings
|
||||
|
||||
!!! warning "Deprecated"
|
||||
|
||||
Please use the `/session/:sessionId/se/log/types` endpoint instead
|
||||
|
||||
<!-- comment source: method-signature -->
|
||||
|
||||
#### Response
|
||||
|
||||
`string`[]
|
||||
|
||||
### `getLogTypes`
|
||||
|
||||
`GET` **`/session/:sessionId/se/log/types`**
|
||||
|
||||
Get available log types as a list of strings
|
||||
|
||||
<!-- comment source: method-signature -->
|
||||
|
||||
#### Response
|
||||
|
||||
`string`[]
|
||||
|
||||
### `getPageSource`
|
||||
|
||||
`GET` **`/session/:sessionId/source`**
|
||||
|
||||
Get the current page/app source as HTML/XML
|
||||
|
||||
**`See`**
|
||||
|
||||
[https://w3c.github.io/webdriver/#get-page-source](https://w3c.github.io/webdriver/#get-page-source)
|
||||
|
||||
<!-- comment source: method-signature -->
|
||||
|
||||
#### Response
|
||||
|
||||
`string`
|
||||
|
||||
The UI hierarchy in a platform-appropriate format (e.g., HTML for a web page)
|
||||
|
||||
### `getSessions`
|
||||
|
||||
`GET` **`/sessions`**
|
||||
|
||||
Get data for all sessions running on an Appium server
|
||||
|
||||
<!-- comment source: method-signature -->
|
||||
|
||||
#### Response
|
||||
|
||||
A list of session data objects.
|
||||
Each session data object will be returned with `id` and the session's capabilities as `capabilities` keys like an example below:
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id":"ba30c6da-c266-4734-8ddb-c16f5bb53e16",
|
||||
"capabilities":{ "platformName":"ios","browserName":"safari","automationName":"xcuitest","platformVersion":"17.2","deviceName":"iPhone 15" }
|
||||
},
|
||||
{
|
||||
"id":"1441110c-1ece-4e45-abbf-ebf404f45f0a",
|
||||
"capabilities":{ "platformName":"ios","browserName":"safari","automationName":"xcuitest","platformVersion":"17.0","deviceName":"iPhone 14" }
|
||||
},
|
||||
...
|
||||
]
|
||||
```
|
||||
|
||||
### `getSettings`
|
||||
|
||||
`GET` **`/session/:sessionId/appium/settings`**
|
||||
|
||||
Update the session's settings dictionary with a new settings object
|
||||
|
||||
<!-- comment source: method-signature -->
|
||||
|
||||
#### Response
|
||||
|
||||
``null``
|
||||
|
||||
### `updateSettings`
|
||||
|
||||
`POST` **`/session/:sessionId/appium/settings`**
|
||||
|
||||
Update the session's settings dictionary with a new settings object
|
||||
|
||||
<!-- comment source: method-signature -->
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
| :------ | :------ | :------ |
|
||||
| `settings` | `Settings` | A key-value map of setting names to values. Settings not named in the map will not have their value adjusted. |
|
||||
|
||||
#### Response
|
||||
|
||||
``null``
|
||||
|
||||
### `getStatus`
|
||||
|
||||
`GET` **`/status`**
|
||||
|
||||
**`Summary`**
|
||||
|
||||
Retrieve the server's current status.
|
||||
|
||||
**`Description`**
|
||||
|
||||
Returns information about whether a remote end is in a state in which it can create new sessions and can additionally include arbitrary meta information that is specific to the implementation.
|
||||
|
||||
The readiness state is represented by the ready property of the body, which is false if an attempt to create a session at the current time would fail. However, the value true does not guarantee that a New Session command will succeed.
|
||||
|
||||
Implementations may optionally include additional meta information as part of the body, but the top-level properties ready and message are reserved and must not be overwritten.
|
||||
|
||||
<!-- comment source: builtin-interface -->
|
||||
|
||||
#### Examples
|
||||
|
||||
<!-- BEGIN:EXAMPLES -->
|
||||
##### JavaScript
|
||||
<!-- BEGIN:EXAMPLE lang=JavaScript -->
|
||||
|
||||
```js
|
||||
// webdriver.io example
|
||||
await driver.status();
|
||||
```
|
||||
|
||||
<!-- END:EXAMPLE -->
|
||||
##### Python
|
||||
<!-- BEGIN:EXAMPLE lang=Python -->
|
||||
|
||||
```python
|
||||
driver.get_status()
|
||||
```
|
||||
|
||||
<!-- END:EXAMPLE -->
|
||||
##### Java
|
||||
<!-- BEGIN:EXAMPLE lang=Java -->
|
||||
|
||||
```java
|
||||
driver.getStatus();
|
||||
```
|
||||
|
||||
<!-- END:EXAMPLE -->
|
||||
##### Ruby
|
||||
<!-- BEGIN:EXAMPLE lang=Ruby -->
|
||||
|
||||
```ruby
|
||||
# ruby_lib example
|
||||
remote_status
|
||||
|
||||
# ruby_lib_core example
|
||||
@driver.remote_status
|
||||
```
|
||||
|
||||
<!-- END:EXAMPLE -->
|
||||
<!-- END:EXAMPLES -->
|
||||
|
||||
#### Response
|
||||
|
||||
`Object`
|
||||
|
||||
### `getTimeouts`
|
||||
|
||||
`GET` **`/session/:sessionId/timeouts`**
|
||||
|
||||
Set the various timeouts associated with a session
|
||||
|
||||
**`See`**
|
||||
|
||||
[https://w3c.github.io/webdriver/#set-timeouts](https://w3c.github.io/webdriver/#set-timeouts)
|
||||
|
||||
<!-- comment source: method-signature -->
|
||||
|
||||
#### Response
|
||||
|
||||
``null``
|
||||
|
||||
### `timeouts`
|
||||
|
||||
`POST` **`/session/:sessionId/timeouts`**
|
||||
|
||||
Set the various timeouts associated with a session
|
||||
|
||||
**`See`**
|
||||
|
||||
[https://w3c.github.io/webdriver/#set-timeouts](https://w3c.github.io/webdriver/#set-timeouts)
|
||||
|
||||
<!-- comment source: method-signature -->
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
| :------ | :------ | :------ |
|
||||
| `type?` | `string` | used only for the old (JSONWP) command, the type of the timeout |
|
||||
| `ms?` | `string` \| `number` | used only for the old (JSONWP) command, the ms for the timeout |
|
||||
| `script?` | `number` | the number in ms for the script timeout, used for the W3C command |
|
||||
| `pageLoad?` | `number` | the number in ms for the pageLoad timeout, used for the W3C command |
|
||||
| `implicit?` | `string` \| `number` | the number in ms for the implicit wait timeout, used for the W3C command |
|
||||
|
||||
#### Response
|
||||
|
||||
``null``
|
||||
|
||||
### `implicitWait`
|
||||
|
||||
`POST` **`/session/:sessionId/timeouts/implicit_wait`**
|
||||
|
||||
Set the implicit wait timeout
|
||||
|
||||
!!! warning "Deprecated"
|
||||
|
||||
Please use the `/session/:sessionId/timeouts` endpoint instead
|
||||
|
||||
Use `timeouts` instead
|
||||
|
||||
<!-- comment source: method-signature -->
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
| :------ | :------ | :------ |
|
||||
| `ms` | `string` \| `number` | the timeout in ms |
|
||||
|
||||
#### Response
|
||||
|
||||
``null``
|
||||
|
||||
### `logCustomEvent`
|
||||
|
||||
`POST` **`/session/:sessionId/appium/log_event`**
|
||||
|
||||
Add a custom-named event to the Appium event log
|
||||
|
||||
<!-- comment source: method-signature -->
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
| :------ | :------ | :------ |
|
||||
| `vendor` | `string` | the name of the vendor or tool the event belongs to, to namespace the event |
|
||||
| `event` | `string` | the name of the event itself |
|
||||
|
||||
#### Response
|
||||
|
||||
``null``
|
||||
|
||||
### `reset`
|
||||
|
||||
`POST` **`/session/:sessionId/appium/app/reset`**
|
||||
|
||||
Reset the current session (run the delete session and create session subroutines)
|
||||
|
||||
!!! warning "Deprecated"
|
||||
|
||||
Please use each driver's launch, activate, terminate or cleanup method.
|
||||
|
||||
Use explicit session management commands instead
|
||||
|
||||
<!-- comment source: method-signature -->
|
||||
|
||||
#### Response
|
||||
|
||||
``null``
|
||||
@@ -0,0 +1,22 @@
|
||||
# Plugin: execute-driver
|
||||
|
||||
### `executeDriverScript`
|
||||
|
||||
`POST` **`/session/:sessionId/appium/execute_driver`**
|
||||
|
||||
Implementation of a command within a plugin
|
||||
|
||||
At minimum, `D` must be `ExternalDriver`, but a plugin can be more narrow about which drivers it supports.
|
||||
|
||||
<!-- comment source: method-signature -->
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type |
|
||||
| :------ | :------ |
|
||||
| `script` | [script: string, scriptType: string, timeoutMs: number] |
|
||||
| `type?` | [script: string, scriptType: string, timeoutMs: number] |
|
||||
|
||||
#### Response
|
||||
|
||||
`unknown`
|
||||
@@ -0,0 +1,67 @@
|
||||
# Plugin: images
|
||||
|
||||
### `compareImages`
|
||||
|
||||
`POST` **`/session/:sessionId/appium/compare_images`**
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type |
|
||||
| :------ | :------ |
|
||||
| `mode` | `any`[] |
|
||||
| `options?` | `any`[] |
|
||||
|
||||
#### Response
|
||||
|
||||
`ComparisonResult`
|
||||
|
||||
### `findElement`
|
||||
|
||||
`POST` **`/session/:sessionId/element`**
|
||||
|
||||
Find a UI element given a locator strategy and a selector, erroring if it can't be found
|
||||
|
||||
**`See`**
|
||||
|
||||
[https://w3c.github.io/webdriver/#find-element](https://w3c.github.io/webdriver/#find-element)
|
||||
|
||||
<!-- comment source: method-signature -->
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
| :------ | :------ | :------ |
|
||||
| `using` | `any` | the locator strategy |
|
||||
| `value` | `any` | the selector to combine with the strategy to find the specific element |
|
||||
|
||||
#### Response
|
||||
|
||||
`any`
|
||||
|
||||
The element object encoding the element id which can be used in element-related
|
||||
commands
|
||||
|
||||
### `findElements`
|
||||
|
||||
`POST` **`/session/:sessionId/elements`**
|
||||
|
||||
Find a a list of all UI elements matching a given a locator strategy and a selector
|
||||
|
||||
**`See`**
|
||||
|
||||
[https://w3c.github.io/webdriver/#find-elements](https://w3c.github.io/webdriver/#find-elements)
|
||||
|
||||
<!-- comment source: method-signature -->
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
| :------ | :------ | :------ |
|
||||
| `using` | `any` | the locator strategy |
|
||||
| `value` | `any` | the selector to combine with the strategy to find the specific elements |
|
||||
|
||||
#### Response
|
||||
|
||||
`any`
|
||||
|
||||
A possibly-empty list of element objects
|
||||
@@ -0,0 +1,24 @@
|
||||
---
|
||||
hide:
|
||||
- toc
|
||||
|
||||
title: Intro to Commands
|
||||
---
|
||||
|
||||
Here you can find various commands exposed by the main Appium module through its base driver, as
|
||||
well as the commands available in several plugins.
|
||||
|
||||
!!! note
|
||||
|
||||
The Appium base driver only exposes a few commands, as it is not meant to be used on its own.
|
||||
Please refer to the documentation of your [Appium driver](../ecosystem/drivers.md) to learn
|
||||
about the commands it exposes, and then check your [Appium client](../ecosystem/clients.md)
|
||||
documentation for the exact syntax of that command.
|
||||
|
||||
The command listings can be found here:
|
||||
|
||||
* [Base Driver](./base-driver.md)
|
||||
* [Execute Driver Plugin](./execute-driver-plugin.md)
|
||||
* [Images Plugin](./images-plugin.md)
|
||||
* [Relaxed Caps Plugin](./relaxed-caps-plugin.md)
|
||||
* [Universal XML Plugin](./universal-xml-plugin.md)
|
||||
@@ -0,0 +1,27 @@
|
||||
# Plugin: relaxed-caps
|
||||
|
||||
### `createSession`
|
||||
|
||||
`POST` **`/session`**
|
||||
|
||||
Start a new automation session
|
||||
|
||||
**`See`**
|
||||
|
||||
[https://w3c.github.io/webdriver/#new-session](https://w3c.github.io/webdriver/#new-session)
|
||||
|
||||
<!-- comment source: multiple -->
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
| :------ | :------ | :------ |
|
||||
| `desiredCapabilities?` | `any` | the new session capabilities |
|
||||
| `requiredCapabilities?` | `any` | another place the new session capabilities could be sent (typically left undefined) |
|
||||
| `capabilities?` | `any` | another place the new session capabilities could be sent (typically left undefined) |
|
||||
|
||||
#### Response
|
||||
|
||||
`any`
|
||||
|
||||
The capabilities object representing the created session
|
||||
@@ -0,0 +1,68 @@
|
||||
# Plugin: universal-xml
|
||||
|
||||
### `findElement`
|
||||
|
||||
`POST` **`/session/:sessionId/element`**
|
||||
|
||||
Find a UI element given a locator strategy and a selector, erroring if it can't be found
|
||||
|
||||
**`See`**
|
||||
|
||||
[https://w3c.github.io/webdriver/#find-element](https://w3c.github.io/webdriver/#find-element)
|
||||
|
||||
<!-- comment source: method-signature -->
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
| :------ | :------ | :------ |
|
||||
| `using` | `any`[] | the locator strategy |
|
||||
|
||||
#### Response
|
||||
|
||||
`any`
|
||||
|
||||
The element object encoding the element id which can be used in element-related
|
||||
commands
|
||||
|
||||
### `findElements`
|
||||
|
||||
`POST` **`/session/:sessionId/elements`**
|
||||
|
||||
Find a a list of all UI elements matching a given a locator strategy and a selector
|
||||
|
||||
**`See`**
|
||||
|
||||
[https://w3c.github.io/webdriver/#find-elements](https://w3c.github.io/webdriver/#find-elements)
|
||||
|
||||
<!-- comment source: method-signature -->
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
| :------ | :------ | :------ |
|
||||
| `using` | `any`[] | the locator strategy |
|
||||
|
||||
#### Response
|
||||
|
||||
`any`
|
||||
|
||||
A possibly-empty list of element objects
|
||||
|
||||
### `getPageSource`
|
||||
|
||||
`GET` **`/session/:sessionId/source`**
|
||||
|
||||
Get the current page/app source as HTML/XML
|
||||
|
||||
**`See`**
|
||||
|
||||
[https://w3c.github.io/webdriver/#get-page-source](https://w3c.github.io/webdriver/#get-page-source)
|
||||
|
||||
<!-- comment source: method-signature -->
|
||||
|
||||
#### Response
|
||||
|
||||
`string`
|
||||
|
||||
The UI hierarchy in a platform-appropriate format (e.g., HTML for a web page)
|
||||
@@ -0,0 +1,451 @@
|
||||
---
|
||||
title: Appium's Config System
|
||||
---
|
||||
|
||||
Appium 2 supports [configuration files](../guides/config.md). A configuration file is intended to
|
||||
have (nearly) 1:1 parity with command-line arguments. An end user can supply Appium 2 with
|
||||
a configuration file, CLI args, or both (the args have precedence over the config file).
|
||||
|
||||
This document will be a technical overview of how the configuration system works. It is intended
|
||||
for Appium contributors, but will also explain the system's fundamental features.
|
||||
|
||||
## Reading a Config File
|
||||
|
||||
A config file is a JSON, JavaScript, or YAML file which can be validated against a schema. By
|
||||
default, this file will be named `.appiumrc.{json,js,yaml,yml}` and should be in the root of the
|
||||
project which depends upon `appium`. Other filenames and locations are supported via the `--config
|
||||
<file>` flag. For obvious reasons, the `config` argument is disallowed within config files.
|
||||
|
||||
In lieu of a separate file, configuration can be embedded in a project's `package.json` using the
|
||||
`appiumConfig` property, e.g.,:
|
||||
|
||||
```json
|
||||
{
|
||||
"appiumConfig": {
|
||||
"server": {
|
||||
"port": 12345
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
When an Appium server is started via the `appium` executable, the `init` function in `lib/main.js`
|
||||
will call into `lib/config-file.js` to load and/or search for a configuration file and in
|
||||
`package.json`.
|
||||
|
||||
!!! note
|
||||
|
||||
It is not an error if configuration isn't found!
|
||||
|
||||
The [`lilconfig`](https://npm.im/lilconfig) package provides the search & load functionality; refer
|
||||
to its documentation for more information about the search paths. Additionally, Appium provides
|
||||
support for config files written in YAML via the package [`yaml`](https://npm.im/yaml).
|
||||
|
||||
If a config file is found and successfully [validated](#validation), the result will be merged with
|
||||
a set of defaults and any additionall CLI arguments. CLI arguments have precedence over config
|
||||
files, and config files have precedence over defaults.
|
||||
|
||||
## Validation
|
||||
|
||||
The same system is used for _both_ validation of config files _and_ command-line arguments.
|
||||
|
||||
The package [`ajv`](https://npm.im/ajv) provides validation. Of course, to make `ajv` validate
|
||||
anything, it must be provided a _schema_.
|
||||
|
||||
The _base_ schema is a [JSON Schema
|
||||
Draft-7](https://json-schema.org/draft/2020-12/json-schema-core.html)-compliant object exported by
|
||||
`lib/schema/appium-config-schema.js`. This schema defines configuration _native to Appium_, and
|
||||
only concerns its behavior as a _server_; it does not define configuration for any other
|
||||
functionality (e.g., the `plugin` or `driver` subcommands).
|
||||
|
||||
!!! warning
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
### Validating Config Files
|
||||
|
||||
When a config file is found (`lib/config-file.js`), it will call the `validate` function exported
|
||||
from `lib/schema/schema.js` with the contents of the config file. In turn, this asks `ajv` to
|
||||
validate the data against the schema that Appium has provided it.
|
||||
|
||||
If the config file is invalid, errors will be generated to be displayed to the user. Finally, the
|
||||
`init` function will detect these errors, display them, and the process will exit.
|
||||
|
||||
I hope that made sense, because this is the easy part.
|
||||
|
||||
### Validating CLI Arguments
|
||||
|
||||
As mentioned earlier, the same system is used for validating both config files and CLI arguments.
|
||||
|
||||
Totally not judging, but Appium uses [`argparse`](https://npm.im/argparse) for its CLI argument
|
||||
parsing. This package, and others like it, provides an API to define the arguments a command-line
|
||||
Node.js script accepts, and will ultimately return an object representation of the user-supplied
|
||||
arguments.
|
||||
|
||||
Just as the schema defines what's allowed in a config file, it also defines what's allowed on the
|
||||
command-line.
|
||||
|
||||
#### Defining CLI Arguments via Schema
|
||||
|
||||
CLI arguments must be _defined_ before their values can be validated.
|
||||
|
||||
A JSON schema isn't a natural fit for defining CLI args--it needs some grease to make it work--but
|
||||
it's close enough that we can do so with an adapter and some custom metadata.
|
||||
|
||||
In `lib/cli/parser.js`, there's a wrapper around `argparse`'s `ArgumentParser`; it's called (wait
|
||||
for it)... `ArgParser`. The wrapper exists because we're doing some custom things with `argparse`,
|
||||
but is has nothing to do with the schema directly.
|
||||
|
||||
An `ArgParser` instance is created and its `parseArgs()` method is called with the raw CLI
|
||||
arguments. The definition of the accepted arguments comes from `lib/cli/args.js` in part--here, all
|
||||
of the arguments _not_ intended for use with the `server` subcommand are hard-coded (e.g., the
|
||||
`driver` subcommand and _its_ subcommands). `args.js` also contains a function `getServerArgs()`,
|
||||
which in turn calls into `toParserArgs` in `lib/schema/cli-args.js`. `lib/schema/cli-args.js` can
|
||||
be considered the "adapter" layer between `argparse` and the schema.
|
||||
|
||||
`toParserArgs` uses the `flattenSchema` function exported by `lib/schema/schema.js`, which
|
||||
"squashes" the schema into a key/value representation. Then, `toParserArgs` iterates over each
|
||||
key/value pair and "converts" it into a suitable `ArgumentOption` object for final handoff to
|
||||
`ArgParser`.
|
||||
|
||||
This adapter (`cli-args.js`) is where most of the mess is hidden; let's explore this rat's nest
|
||||
a bit further.
|
||||
|
||||
##### CLI & Schema Incongruities
|
||||
|
||||
The conversion algorithm (see function `subSchemaToArgDef` in `lib/schema/cli-args.js`) is mostly
|
||||
just hacks and special cases neatly packed into a function. Things that don't cleanly map from
|
||||
`argparse` to a JSON schema include, but are not limited to:
|
||||
|
||||
- A schema cannot natively express "store the value of `--foo=<value>` in a property called `bar`" in a schema (this corresponds to the `ArgumentOption['dest']` prop).
|
||||
- A schema cannot natively express aliases; e.g., `--verbose` can also be `-v`
|
||||
- A schema `enum` is not restricted to multiple types, but `argparse`'s equivalent `ArgumentOption['choices']` prop _is_
|
||||
- A schema does not know about `argparse`'s concept of "actions" (note that Appium is not currently using custom actions--though it did, and it could again).
|
||||
- `argparse` has no native type for `email`, `hostname`, `ipv4`, `uri` etc., and the schema does
|
||||
- Schema validation only _validates_, it does not perform translation, transformation, or coercion (mostly). `argparse` allows this.
|
||||
- Schemas allow the `null` type, for whatever reason. Ever pass `null` on the CLI?
|
||||
- `argparse` does not understand anything other than primitives; no objects, arrays, etc., and certainly not arrays of a particular type.
|
||||
|
||||
All of the above cases and others are handled by the adapter.
|
||||
|
||||
!!! warning
|
||||
|
||||
Some decisions made in the adapter were arrived at via coin toss. If you are curious about why
|
||||
something is the way it is, it's likely that it had to do _something_.
|
||||
|
||||
Let's look more closely at handling types.
|
||||
|
||||
#### Argument Types via `ajv`
|
||||
|
||||
While `argparse` allows consumers, via its API, to define the _type_ of various arguments (e.g.,
|
||||
a string, number, boolean flag, etc.), Appium mostly avoids these built-in types. _Why is that?_
|
||||
Well:
|
||||
|
||||
1. We already know the type of an argument, because we've defined it in a schema.
|
||||
2. `ajv` provides validation against a schema.
|
||||
3. A schema allows for greater expression of types, allowed values, etc., than `argparse` can provide natively.
|
||||
4. The expressiveness of a schema allows for better error messaging.
|
||||
|
||||
To that end, the adapter eschews `argparse`'s built-in types (see allowed string values of
|
||||
`ArgumentOption['type']`) and instead abuses the ability to provide a _function_ as a `type`. The
|
||||
exception is _boolean_ flags, which do not have a `type`, but rather `action: 'store_true'`. The
|
||||
world may never know why.
|
||||
|
||||
##### Types as Functions
|
||||
|
||||
When a `type` is a function, the function performs both validation _and_ coercion (if necessary).
|
||||
So what are these functions?
|
||||
|
||||
> Note: `type` is _omitted_ (and thus _not_ a function) from the `ArgumentOption` if the property
|
||||
> type is `boolean`, and is instead provided an `action` property of `store_true`. Yes, this is
|
||||
> weird. No, I don't know why.
|
||||
|
||||
Well... it depends upon the schema. But generally speaking, we create a _pipeline_ of functions,
|
||||
each corresponding to a keyword in the schema. Let's take the example of the `port` argument. In
|
||||
lieu of asking the OS which ports the `appium`-running user can bind to, this argument is expected
|
||||
to be an integer between 1 and 65535. This turns out to be two functions which we combine into
|
||||
a pipeline:
|
||||
|
||||
1. Convert the value to an integer, if possible. Because _every value in `process.argv` is a string_, we must coerce if we want a number.
|
||||
2. Use `ajv` to validate the integer against the schema for `port`. A schema lets us define a range via the `minimum` and `maximum` keywords. Read more about how this works in
|
||||
|
||||
Much like the config file validation, if errors are detected, Appium nicely tells the end-user and
|
||||
the process exits w/ some help text.
|
||||
|
||||
For other arguments which are naturally of non-primitive types, things are not so straightforward.
|
||||
|
||||
##### Transformers
|
||||
|
||||
Remember how `argparse` doesn't understand arrays? What if the most ergonomic way to express
|
||||
a value is, in fact, an array?
|
||||
|
||||
Well, Appium can't accept an array on the CLI, even though it can accept one in the config file.
|
||||
But Appium _can_ accept a comma-delimited string (a CSV "line"). Or a string filepath referring to
|
||||
a file which _contains_ a delimited list. Either way: by the time the value gets out of the
|
||||
argument parser, it should be an array.
|
||||
|
||||
And as mentioned above, the native facilities of a JSON schema cannot express this. However, it's
|
||||
possible to define a _custom keyword_ which Appium can then detect and handle accordingly. So
|
||||
that's what Appium does.
|
||||
|
||||
In this case, a custom keyword `appiumCliTransformer` is registered with `ajv`. The value of
|
||||
`appiumCliTransformer` (at the time of this writing) can be `csv` or `json`. In the base schema
|
||||
file, `appium-config-schema.js`, Appium uses `appiumCliTransformer: 'csv'` if this behavior is
|
||||
desired.
|
||||
|
||||
!!! note
|
||||
|
||||
Any property defined in the schema having type `array` will _automatically_ uses the `csv`
|
||||
transformer. Likewise, a property having type `object` will use the `json` transformer. It's
|
||||
conceivable that `array` may want to use the `json` transformer, but otherwise, the presence of
|
||||
the `appiumCliTransformer` keyword on an `array`-or-`object`-typed property is not stricly
|
||||
necessary.
|
||||
|
||||
The adapter (remember the adapter?) creates a pipeline function including a special "CSV
|
||||
transformer" (transformers are defined in `lib/schema/cli-transformers.js`), and uses this function
|
||||
as the `type` property of the `ArgumentOption` passed into `argparse`. In this case, the `type:
|
||||
'array'` in the schema is ignored.
|
||||
|
||||
!!! note
|
||||
|
||||
The config file doesn't _need_ to perform any complex transformation of values, because it
|
||||
naturally allows Appium to define exactly what it expects. So Appium does no post-processing of
|
||||
config file values.
|
||||
|
||||
Properties that do not need this special treatment use `ajv` directly for validation. How this
|
||||
works requires some explanation, so that's next.
|
||||
|
||||
#### Validation of Individual Arguments via `ajv`
|
||||
|
||||
When we think of a JSON schema, we tend to think, "I have this JSON file and I want to validate it
|
||||
against the schema". That's valid, and in fact Appium does just that with config files! However,
|
||||
Appium does not do this when validating arguments.
|
||||
|
||||
!!! note
|
||||
|
||||
During implementation, I was tempted to mash all of the arguments together into
|
||||
a config-file-like data structure and then validate it all at once. I think that would have
|
||||
been _possible_, but since an object full of CLI arguments is a flat key/value structure and
|
||||
the schema is not, this seemed like trouble.
|
||||
|
||||
Instead, Appium validates a value against a specific property _within_ the schema. To do this, it
|
||||
maintains a mapping between a CLI argument definition and its corresponding property. The mapping
|
||||
itself is a `Map` with a unique identifier for the argument as the key, and an `ArgSpec`
|
||||
(`lib/schema/arg-spec.js`) object as the value.
|
||||
|
||||
An `ArgSpec` object stores the following metadata:
|
||||
|
||||
| Property Name | Description |
|
||||
| --------------- | ------------------------------------------------------------------------------------- |
|
||||
| `name` | Canonical name of the argument, corresponding to the property name in the schema. |
|
||||
| `extType?` | `driver` or `plugin`, if appropriate |
|
||||
| `extName?` | Extension name, if appropriate |
|
||||
| `ref` | Computed `$id` of the property in the schema |
|
||||
| `arg` | Argument as accepted on CLI, without leading dashes |
|
||||
| `dest` | Property name in parsed arguments object (as returned by `argparse`'s `parse_args()`) |
|
||||
| `defaultValue?` | Value of the `default` keyword in schema, if appropriate |
|
||||
|
||||
When a schema is [finalized](#schema-finalization), the `Map` is populated with `ArgSpec` objects
|
||||
for all known arguments.
|
||||
|
||||
So when the adapter is creating the pipeline of functions for the argument's `type`, it already has
|
||||
an `ArgSpec` for the argument. It creates a function which calls `validate(value, ref)` (in
|
||||
`lib/schema/schema.js`) where `value` is whatever the user provided, and `ref` is the `ref`
|
||||
property of the `ArgSpec`. The concept is that `ajv` can validate using _any_ `ref` it knows about;
|
||||
each property in a schema can be referenced by this `ref` whether it's defined or not. To help
|
||||
visualize, if a schema is:
|
||||
|
||||
```json
|
||||
{
|
||||
"$id": "my-schema.json",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"foo": {
|
||||
"type": "number"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The `ref` of `foo` would be `my-schema.json#/properties/foo`. Assuming our `Ajv` instance knows
|
||||
about this `my-schema.json`, then we can call its `getSchema(ref)` method (which has a `schema`
|
||||
property, but is a misnomer nonetheless) to get a validation function; `validate(value, ref)` in
|
||||
`schema.js` calls this validation function.
|
||||
|
||||
!!! note
|
||||
|
||||
The schema spec says a schema author can supply an explicit `$id` keyword to override this;
|
||||
it's unsupported by Appium at this time. If needed, extension authors must carefully use `$ref`
|
||||
without custom `$id`s. It's highly unlikely an extension would have a schema so complicated as
|
||||
to need this, however; Appium itself doesn't even use `$ref` to define its own properties!
|
||||
|
||||
Next, let's take a look at how Appium loads schemas. This actually happens _before_ any argument
|
||||
validation.
|
||||
|
||||
## Schema Loading
|
||||
|
||||
Let's ignore extensions for a moment, and start with the base schema.
|
||||
|
||||
When something first imports the `lib/schema/schema.js` module, an instance of an `AppiumSchema` is
|
||||
created. This is a singleton, and its methods are exported from the module (all of which are bound
|
||||
to the instance).
|
||||
|
||||
The constructor does very little; it instantiates an `Ajv` instance and configures it with Appium's
|
||||
[custom keywords](#custom-keyword-reference) and adds support for the `format` keyword via the
|
||||
[ajv-formats](https://npm.im/ajv-formats) module.
|
||||
|
||||
Otherwise, the `AppiumSchema` instance does not interact with the `Ajv` instance until its
|
||||
`finalize()` method (exported as `finalizeSchema()`) is called. When this method is called, we're
|
||||
saying "we are not going to add any more schemas; go ahead and create `ArgSpec` objects and
|
||||
register schemas with `ajv`".
|
||||
|
||||
When does finalization happen? Well:
|
||||
|
||||
1. When the `appium` executable begins, it _checks for and configures extensions_ (hand-wave) in `APPIUM_HOME`.
|
||||
2. Only then does it start to think about arguments--it instantiates an `ArgParser`, which (as you'll recall) runs the adapter to convert the schema to arguments.
|
||||
3. _Finalization happens here_--when creating the parser. Appium need the schema(s) to be registered with `ajv` in order to create validation functions for arguments.
|
||||
4. Thereafter, Appium parses the arguments with the `ArgParser`.
|
||||
5. Finally, decides what to do with the returned object.
|
||||
|
||||
Without extensions, `finalize()` still knows about the Appium base schema
|
||||
(`appium-config-schema.js`), and just registers that. However, step 1. above is doing a _lot of
|
||||
work_, so let's look at how extensions come into play.
|
||||
|
||||
## Extension Support
|
||||
|
||||
One of the design goals of this system is the following:
|
||||
|
||||
_An extension should be able to register custom CLI arguments with the Appium, and a user should be
|
||||
able to use them like any other argument_.
|
||||
|
||||
Previously, Appium 2 accepted arguments in this manner (via `--driverArgs`), but validation was
|
||||
hand-rolled and required extension implementors to use a custom API. It also required the user to
|
||||
awkwardly pass a JSON string as the configuration on the command-line. Further, no contextual help
|
||||
(via `--help`) existed for these arguments.
|
||||
|
||||
Now, by providing a schema for its options, a driver or plugin can register CLI arguments and
|
||||
config file schemas with Appium.
|
||||
|
||||
To register a schema, an extension must provide the `appium.schema` property in its `package.json`.
|
||||
The value may be a schema or a path to a schema. If the latter, the schema should be JSON or
|
||||
a CommonJS module (ESM not supported at this time, nor is YAML).
|
||||
|
||||
For any property in this schema, the property will appear as a CLI argument of the form
|
||||
`--<extension-type>-<extension-name>-<property-name>`. For example, if the `fake` driver provides
|
||||
a property `foo`, the argument will be `--driver-fake-foo`, and will show in `appium server --help`
|
||||
like any other CLI argument.
|
||||
|
||||
The corresponding property in a config file would be
|
||||
`server.<extension-type>.<extension-name>.<property-name>`, e.g.:
|
||||
|
||||
```json
|
||||
{
|
||||
"server": {
|
||||
"driver": {
|
||||
"fake": {
|
||||
"foo": "bar"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The naming convention described above avoids problems of one extension type having a name conflict
|
||||
with a different extension type.
|
||||
|
||||
!!! note
|
||||
|
||||
While an extension can provide aliases via `appiumCliAliases`, "short" flags are disallowed,
|
||||
since all arguments from extensions are prefixed with `--<extension-type>-<extension-name>-`.
|
||||
The extension name and argument name will be kebab-cased for the CLI, according to [Lodash's
|
||||
rules](https://lodash.com/docs/4.17.15#kebabCase) around kebab-casing.
|
||||
|
||||
The schema object will look much like Appium's base schema, but it will only have top-level
|
||||
properties (nested properties are currently unsupported). Example:
|
||||
|
||||
```json
|
||||
{
|
||||
"title": "my rad schema for the cowabunga driver",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"fizz": {
|
||||
"type": "string",
|
||||
"default": "buzz",
|
||||
"$comment": "corresponds to CLI --driver-cowabunga-fizz"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
As written in a user's config file, this would be the `server.driver.cowabunga.fizz` property.
|
||||
|
||||
When extensions are loaded, the `schema` property is verified and the schema is registered with the
|
||||
`AppiumSchema` (it is _not_ registered with `Ajv` until `finalize()` is called).
|
||||
|
||||
During finalization, each registered schema is added to the `Ajv` instance. The schema is assigned
|
||||
an `$id` based on the extension type and name (which overrides whatever the extension provides, if
|
||||
anything). Schemas are also forced to disallowed unknown arguments via the `additionalProperties:
|
||||
false` keyword.
|
||||
|
||||
Behind the scenes, the base schema has `driver` and `plugin` properties which are objects. When
|
||||
finalized, a property is added to each--corresponding to an extension name--and the value of this
|
||||
property is a reference to the `$id` of a property in the extension schema. For example, the
|
||||
`server.driver` property will look like this:
|
||||
|
||||
```json
|
||||
{
|
||||
"driver": {
|
||||
"cowabunga": {
|
||||
"$ref": "driver-cowabunga.json"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This is why we call it the "base" schema--it is _mutated_ when extensions provide schemas. The
|
||||
extension schemas are kept separately, but the _references_ are added to the schema before it's
|
||||
ultimately added to `ajv`. This works because an `Ajv` instance understands references _from_ any
|
||||
schema it knows about _to_ any schema it knows about.
|
||||
|
||||
!!! note
|
||||
|
||||
This makes it impossible to provide a complete static schema for Appium _and_ the installed
|
||||
extensions (as of Nov 5 2021). A static `.json` schema _is_ generated from the base (via a Gulp
|
||||
task), but it does not contain any extension schemas. The static schema also has uses beyond
|
||||
Appium; e.g., IDEs can provide contextual error-checking of config files this way. Let's solve
|
||||
this?
|
||||
|
||||
Just like how we look up the reference ID of a particular argument in the base schema, validation
|
||||
of arguments from extensions happens the exact same way. If the `cowabunga` driver has the schema
|
||||
ID `driver-cowabunga.json`, then the `fizz` property can be referenced from any schema registered
|
||||
with `ajv` via `driver-cowabunga.json#/properties/fizz`. "Base" schema arguments begin with
|
||||
`appium.json#properties/` instead.
|
||||
|
||||
## Development Environment Support
|
||||
|
||||
During the flow of development, a couple extra tasks have been automated to maintain the base
|
||||
schema:
|
||||
|
||||
- As a post-transpilation step, a `lib/appium-config.schema.json` gets generated from
|
||||
- `lib/schema/appium-config-schema.js` (in addition to its CJS counterpart generated by Babel).
|
||||
- This file is under version control. It ends up being _copied_ to
|
||||
- `build/lib/appium-config.schema.json` in this step. A pre-commit hook (see
|
||||
- `scripts/generate-schema-declarations.js` in the root monorepo) generates
|
||||
- a `types/appium-config-schema.d.ts` from the above JSON file. The types in `types/types.d.ts`
|
||||
- depend upon this file. This file is under version control.
|
||||
|
||||
## Custom Keyword Reference
|
||||
|
||||
Keywords are defined in `lib/schema/keywords.js`.
|
||||
|
||||
- `appiumCliAliases`: allows a schema to express aliases (e.g., a CLI argument can be `--verbose` or `-v`). This is an array of strings. Strings shorter than three (3) characters will begin with a single dash (`-`) instead of a double-dash (`--`). Note that any argument provided by an extension will begin with a double-dash, because these are required to have the `--<extension-type>-<extension-name>-` prefix.
|
||||
- `appiumCliDest`: allows a schema to specify a custom property name in the post-`argprase` arguments objects. If not set, this becomes a camelCased string.
|
||||
- `appiumCliDescription`: allows a schema to override the description of the argument when displayed on the command-line. This is useful paired with `appiumCliTransformer` (or `array`/`object`-typed properties), since there's a substantial difference between what a CLI-using user can provide vs. what a config-file-using user can provide.
|
||||
- `appiumCliTransformer`: currently a choice between `csv` and `json`. These are custom functions which post-process a value. They are not used when loading & validating config files, but the idea should be that they result in the same object you'd get if you used whatever the config file wanted (e.g., an array of strings). `csv` is for comma-delimited strings and CSV files; `json` is for raw JSON strings and `.json` files.
|
||||
- `appiumCliIgnore`: If `true`, do not support this property on the CLI.
|
||||
- `appiumDeprecated`: If `true`, the property is considered "deprecated", and will be displayed as such to the user (e.g., in the `--help` output). Note the JSON Schema draft-2019-09 introduces a new keyword `deprecated` which we should use instead if upgrading to this metaschema. When doing so, `appiumDeprecated` should itself be marked as `deprecated`.
|
||||
@@ -0,0 +1,90 @@
|
||||
---
|
||||
title: Contributing Code
|
||||
---
|
||||
|
||||
To contribute to the Appium code base make sure to check out the Git repository.
|
||||
|
||||
!!! 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).
|
||||
|
||||
It is advised to [fork](https://github.com/appium/appium/fork) before cloning it to your system.
|
||||
|
||||
```sh
|
||||
export GITHUB_USERNAME=<your-username>
|
||||
git clone git@github.com:$GITHUB_USERNAME/appium.git
|
||||
```
|
||||
|
||||
After cloning you can go ahead and install the project dependencies:
|
||||
|
||||
```sh
|
||||
npm install
|
||||
```
|
||||
|
||||
From here on there are several things you can do.
|
||||
|
||||
### Watch Files
|
||||
|
||||
When developing Appium code we have to watch all JavaScript and TypeScript files to re-compile them after every change. You can run this watch process via:
|
||||
|
||||
```sh
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### Start Appium in Dev-Mode
|
||||
|
||||
To test your changes you can run Appium in dev mode via:
|
||||
|
||||
```sh
|
||||
npm start
|
||||
```
|
||||
|
||||
### Run Tests
|
||||
|
||||
The project maintains a set of different test variations you can run to verify the quality of the code.
|
||||
|
||||
#### Linting
|
||||
|
||||
Appium uses [`eslint`](https://eslint.org/) for static code analysis and linting. You can run these checks via:
|
||||
|
||||
```sh
|
||||
npm run lint
|
||||
```
|
||||
|
||||
#### Unit
|
||||
|
||||
```sh
|
||||
npm run test:unit
|
||||
```
|
||||
|
||||
You can also run tests for specific workspaces, e.g.:
|
||||
|
||||
```sh
|
||||
export APPIUM_WORKSPACE=@appium/doctor
|
||||
npm run test:unit -w $APPIUM_WORKSPACE
|
||||
```
|
||||
|
||||
#### Smoke and E2E
|
||||
|
||||
```sh
|
||||
npm run test:slow
|
||||
```
|
||||
|
||||
### Deploy Docs Locally
|
||||
|
||||
Our documentation system uses [MKDocs](https://www.mkdocs.org/) and therefore requires [Python](https://www.python.org/) to be installed on your system. You can run the docs by:
|
||||
|
||||
```sh
|
||||
# installing needed Python dependencies
|
||||
pip install -r packages/docutils/requirements.txt
|
||||
# build the project
|
||||
npm run build
|
||||
# run dev server
|
||||
npm run dev:docs
|
||||
```
|
||||
|
||||
You should be able to view the page at `http://127.0.0.1:8000/docs/en`:
|
||||
|
||||
```sh
|
||||
open http://127.0.0.1:8000/docs/en
|
||||
```
|
||||
@@ -1,5 +1,38 @@
|
||||
---
|
||||
title: 为Appium做贡献
|
||||
hide:
|
||||
- toc
|
||||
|
||||
title: Contributing to Appium
|
||||
---
|
||||
|
||||
TODO
|
||||
Appium is open source, available under an Apache 2.0 license. Appium's copyright is held by the
|
||||
[OpenJS Foundation](https://openjsf.org), and Appium receives contributions from many companies
|
||||
across several software industries, regardless of their competitive status. (3rd-party drivers and
|
||||
plugins are available under the licenses provided by their authors.)
|
||||
|
||||
As such, we welcome contributions! The project moves forward in relation to the investment of
|
||||
contributions of code, documentation, maintenance, and support from companies and volunteers.
|
||||
|
||||
This section of Appium's documentation is for those who are interested in contributing to Appium's
|
||||
development. It contains developer overviews, guides, and notes. Use the navigation menu to find
|
||||
a specific topic. All the information in these guides assumes in-depth familiarity with Appium
|
||||
already.
|
||||
|
||||
!!! warning
|
||||
|
||||
Developer information may not be kept up to date as frequently as user-facing information, or
|
||||
it may be most relevant in its current form on the online repository, not in this published
|
||||
version. Always be sure to check the repo or discuss with maintainers. We're always happy to
|
||||
help new contributors get started!
|
||||
|
||||
## Ways to Contribute
|
||||
|
||||
The project offers a variety of ways to contribute, e.g.:
|
||||
|
||||
- [contributing code](./develop.md)
|
||||
- improving documentation
|
||||
- creating educational content (blog posts, tutorials, videos, etc.)
|
||||
- spreading the good word about the project (e.g. via Twitter)
|
||||
- filing bugs if you discover them while using Appium
|
||||
- making feature requests if you are missing something in the project or help triaging bugs
|
||||
- supporting users in the [Appium forum](https://discuss.appium.io/)
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
---
|
||||
title: Building Documentation
|
||||
---
|
||||
|
||||
Once you've [built a driver](./build-drivers.md) or [built a plugin](./build-plugins.md) for Appium,
|
||||
you will hopefully want to document how that extension works for your users. The most basic way of
|
||||
doing this is to write up a quick `README.md` and keep it in the root of your project's repository.
|
||||
However, this can involve a lot of effort.
|
||||
|
||||
The Appium project has built tools to help with this, and we've packaged up these tools so our
|
||||
ecosystem developers building drivers and plugins can _also_ use them. The best way to get going
|
||||
with these tools is probably to look at an existing Appium driver repo to see how it's done, for
|
||||
example the [XCUITest driver repo](https://github.com/appium/appium-xcuitest-driver). But this guide
|
||||
will outline the basic approach.
|
||||
|
||||
### Conceptual architecture
|
||||
|
||||
Appium settled on [MkDocs](https://www.mkdocs.org/) as a Markdown-based documentation site
|
||||
generator. It uses a Python toolchain (and not Node.js), but it turned out to be the best option
|
||||
for our purposes. You can adjust this, but by default Appium's utilities also assume that you'll be
|
||||
using the [mkdocs-material](https://squidfunk.github.io/mkdocs-material/) theme/extension for
|
||||
MkDocs.
|
||||
|
||||
In order to make different versions of your docs available (one for each minor release of your
|
||||
extension, typically), we also bundle [Mike](https://github.com/jimporter/mike).
|
||||
|
||||
From here, building a basic docs site is as easy as collecting your Markdown files together and
|
||||
defining how you want them to be organized.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
To take advantage of Appium's documentation utilities, you'll need to install:
|
||||
|
||||
- [Python v3+](https://www.python.org/downloads/)
|
||||
- [pip](https://pip.pypa.io/en/stable/installation/) (this may be installed automatically with Python)
|
||||
- The `@appium/docutils` package:
|
||||
|
||||
```bash
|
||||
npm install --save-dev @appium/docutils
|
||||
```
|
||||
|
||||
### Initializing an Extension for Building Docs
|
||||
|
||||
To prepare your extension for generating documentation, run the following command:
|
||||
|
||||
```bash
|
||||
npx appium-docs init
|
||||
```
|
||||
|
||||
This will:
|
||||
|
||||
1. Create a `tsconfig.json` if one does not already exist. This is necessary even if your extension
|
||||
is not written in TypeScript.
|
||||
2. Create a `mkdocs.yml` with the necessary configuration for MkDocs.
|
||||
|
||||
### Documenting Your Extension
|
||||
|
||||
At this point, you can begin documenting your extension. By default, MkDocs will look for Markdown
|
||||
files in the `docs` directory. You can therefore create your Markdown documentation files, place
|
||||
them in `docs`, and add links to these files in `mkdocs.yml`.
|
||||
|
||||
Refer to the [MkDocs documentation](https://www.mkdocs.org/user-guide/writing-your-docs/) for
|
||||
information on how to organize and structure your documentation.
|
||||
|
||||
### Building the Docs
|
||||
|
||||
At this point, you can use the `appium-docs` CLI tool. Run this tool with no arguments to get the
|
||||
full help output and see all the available subcommands and parameters. Here are a few usage
|
||||
examples:
|
||||
|
||||
```bash
|
||||
# Generate reference and build the mkdocs site into the site dir
|
||||
npx appium-docs build
|
||||
|
||||
# Same as build, but host the docs on a local dev server
|
||||
# and watch for changes and rebuild when files change
|
||||
npx appium-docs build --serve
|
||||
|
||||
# Build the docs and deploy them with mike versioning to the docs-site branch
|
||||
# using the included commit message.
|
||||
# This is particularly useful for pushing content to a GitHub pages branch!
|
||||
npx appium-docs build \
|
||||
--deploy \
|
||||
-b docs-site \
|
||||
-m 'docs: auto-build docs for appium-xcuitest-driver@%s'
|
||||
```
|
||||
@@ -0,0 +1,141 @@
|
||||
---
|
||||
title: Building Doctor Checks
|
||||
---
|
||||
|
||||
The idea of Appium Doctor is to assist users with driver or plugin preconditions setup. Sometimes such
|
||||
preconditions might be quite complicated and require non-trivial technical knowledge. Doctor checks,
|
||||
which are vanilla Node.js class instances written by extension authors, simplify
|
||||
the setup process by automating diagnostics and possible fixes for the found issues. These checks
|
||||
might also be interactive to ensure better usage experience.
|
||||
|
||||
This tutorial is supposed to be used by plugin or driver authors that would like to help their users
|
||||
to deal with complicated setup or configuration steps.
|
||||
|
||||
## Adding Doctor Checks
|
||||
|
||||
### Typing Requirements
|
||||
|
||||
The term `Doctor Check` literally describes a single javascript class instance that implements the
|
||||
[IDoctorCheck interface](https://github.com/appium/appium/blob/master/packages/types/lib/doctor.ts).
|
||||
The interface defines the following methods and properties:
|
||||
|
||||
- `diagnose(): Promise<DoctorCheckResult>`: Contains the code to diagnose a possible issue
|
||||
- `fix(): Promise<string|null>`: Either fixes the actual problem if `hasAutofix()` returns true or
|
||||
returns a string description for possible manual fixes. If this method throws an exception named
|
||||
`FixSkippedError` and `hasAutofix()` returns true then the result of the method invocation
|
||||
is going to be ignored.
|
||||
- `hasAutofix(): boolean`: Whether calling `fix()` would resolve the found issue
|
||||
- `isOptional(): boolean`: Whether the found issue can be ignored and is not a showstopper
|
||||
- `log: AppiumLogger`: May be used for logging. This property may be assigned
|
||||
by the instance itself or by the Appium server if it is left unassigned.
|
||||
|
||||
The `DoctorCheckResult` object returned by the `diagnose()` method must contain the following properties:
|
||||
|
||||
- `ok: boolean`: Whether the diagnosis found no issues
|
||||
- `optional: boolean`: Whether the diagnosed issue is safe to ignore
|
||||
- `message: string`: The text message describing the diagnostic result
|
||||
|
||||
### Manifest Requirements
|
||||
|
||||
A single extension may export multiple Doctor checks to Appium. In order for these checks to be properly
|
||||
picked up by the server CLI after the corresponding extension is installed they might be listed in the
|
||||
package .json manifest under the `appium.doctor.checks` section similar to the definition below:
|
||||
|
||||
```json
|
||||
// ...
|
||||
"appium": {
|
||||
"driverName": "fake",
|
||||
"automationName": "Fake",
|
||||
"platformNames": [
|
||||
"Fake"
|
||||
],
|
||||
"mainClass": "FakeDriver",
|
||||
"schema": "./build/lib/fake-driver-schema.js",
|
||||
"scripts": {
|
||||
"fake-error": "./build/lib/scripts/fake-error.js",
|
||||
"fake-success": "./build/lib/scripts/fake-success.js",
|
||||
"fake-stdin": "./build/lib/scripts/fake-stdin.js"
|
||||
},
|
||||
"doctor": {
|
||||
"checks": [
|
||||
"./doctor/fake1.js",
|
||||
"./doctor/fake2.js"
|
||||
// ...
|
||||
]
|
||||
}
|
||||
},
|
||||
// ...
|
||||
```
|
||||
|
||||
Also, it makes sense to include the [@appium/types](https://www.npmjs.com/package/@appium/types) import
|
||||
to the package dev dependencies.
|
||||
|
||||
### Implementation Example
|
||||
|
||||
The below example is a "raw" Node.JS implementation that does not use any transpilation:
|
||||
|
||||
```js
|
||||
const {fs, doctor} = require('@appium/support');
|
||||
|
||||
/** @satisfies {import('@appium/types').IDoctorCheck} */
|
||||
class EnvVarAndPathCheck {
|
||||
/**
|
||||
* @param {string} varName
|
||||
*/
|
||||
constructor(varName) {
|
||||
this.varName = varName;
|
||||
}
|
||||
|
||||
async diagnose() {
|
||||
const varValue = process.env[this.varName];
|
||||
if (typeof varValue === 'undefined') {
|
||||
return doctor.nok(`${this.varName} environment variable is NOT set!`);
|
||||
}
|
||||
|
||||
if (await fs.exists(varValue)) {
|
||||
return doctor.ok(`${this.varName} is set to: ${varValue}`);
|
||||
}
|
||||
|
||||
return doctor.nok(`${this.varName} is set to '${varValue}' but this is NOT a valid path!`);
|
||||
}
|
||||
|
||||
async fix() {
|
||||
return (
|
||||
`Make sure the environment variable ${this.varName} is properly configured for the Appium server process`
|
||||
);
|
||||
}
|
||||
|
||||
hasAutofix() {
|
||||
return false;
|
||||
}
|
||||
|
||||
isOptional() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const androidHomeCheck = new EnvVarAndPathCheck('ANDROID_HOME');
|
||||
|
||||
module.exports = {androidHomeCheck};
|
||||
|
||||
/**
|
||||
* @typedef {import('@appium/types').DoctorCheckResult} CheckResult
|
||||
*/
|
||||
```
|
||||
|
||||
This file could be saved as `doctor/android-home-check.js` and then added to the package.json manifest
|
||||
as
|
||||
|
||||
```json
|
||||
// ...
|
||||
"appium": {
|
||||
// ...
|
||||
"doctor": {
|
||||
"checks": [
|
||||
"./doctor/android-home-check.js",
|
||||
]
|
||||
}
|
||||
// ...
|
||||
},
|
||||
// ...
|
||||
```
|
||||
@@ -0,0 +1,460 @@
|
||||
---
|
||||
title: Building Plugins
|
||||
---
|
||||
|
||||
This is a high-level guide for developing Appium plugins, which is not something most Appium users
|
||||
need to know or care about. If you are not familiar with Appium plugins yet from a user
|
||||
perspective, check out the [list of plugins](../ecosystem/plugins.md) to play around with some and get
|
||||
an idea of the sorts of things that plugins can do. Plugins are a powerful system for augmenting
|
||||
Appium's functionality or changing the way Appium works. They can be distributed to other Appium
|
||||
users and can extend Appium's ecosystem in all kinds of interesting ways! (There is also
|
||||
a significant amount of overlap here with developing Appium drivers, so you may also want to check
|
||||
out the [building drivers](./build-drivers.md) guide for further inspiration.)
|
||||
|
||||
## Before you create your plugin
|
||||
|
||||
Before creating your plugin, it's good to have a general idea of what you want your plugin to
|
||||
accomplish and whether it will be possible to implement it given the restrictions of the Appium
|
||||
platform. Reading through this guide will help you understand what's possible. In general, Appium's
|
||||
plugin system is extremely powerful and no attempts have been made to artificially limit what's
|
||||
possible with them (which is a main reason that all plugins are opt-in by the system administrator
|
||||
responsible for starting the Appium server---plugins are powerful and should only be used when
|
||||
explicitly trusted!).
|
||||
|
||||
## Other plugins to reference
|
||||
|
||||
There are a wide variety of open source Appium plugins available for perusal. It's definitely
|
||||
recommended to explore the code for some other plugins before embarking on writing your own. The
|
||||
Appium team maintains a set of official plugins in the [Appium GitHub
|
||||
repo](https://github.com/appium/appium). Links to other open source plugins can be found in the
|
||||
[Plugin list](../ecosystem/plugins.md)
|
||||
|
||||
## Basic requirements for plugins
|
||||
|
||||
These are the things your plugin *must* do (or be), if you want it to be a valid Appium plugin.
|
||||
|
||||
### Node.js package with Appium extension metadata
|
||||
|
||||
All Appium plugins are fundamentally Node.js packages, and therefore must have a valid
|
||||
`package.json`. Your driver is not _limited_ to Node.js, but it must provide an adapter written in
|
||||
Node.js so it can be loaded by Appium.
|
||||
|
||||
Your `package.json` must include `appium` as a `peerDependency`. The requirements for the
|
||||
dependency versions should be as loose as possible (unless you happen to know your plugin will only
|
||||
work with certain versions of Appium). For Appium 2, for example, this would look something like
|
||||
`^2.0.0`, declaring that your plugin works with any version of Appium that starts with 2.x.
|
||||
|
||||
Your `package.json` must contain an `appium` field, like this (we call this the 'Appium extension
|
||||
metadata'):
|
||||
|
||||
```json
|
||||
{
|
||||
...,
|
||||
"appium": {
|
||||
"pluginName": "fake",
|
||||
"mainClass": "FakePlugin"
|
||||
},
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
The required subfields are:
|
||||
|
||||
* `pluginName`: this should be a short name for your plugin.
|
||||
* `mainClass`: this is a named export (in CommonJS style) from your `main` field. It must be a
|
||||
class which extends Appium's `BasePlugin` (see below).
|
||||
|
||||
### Extend Appium's `BasePlugin` class
|
||||
|
||||
Ultimately, your plugin is much easier to write because most of the hard work of defining patterns
|
||||
for overriding commands is done for you. This is
|
||||
all encoded up as a class which Appium exports for you to use, called `BasePlugin`. It is exported
|
||||
from `appium/plugin`, so you can use one of these styles to import it and create your *own* class
|
||||
that extends it:
|
||||
|
||||
```js
|
||||
import {BasePlugin} from 'appium/plugin';
|
||||
// or: const {BasePlugin} = require('appium/plugin');
|
||||
|
||||
export class MyPlugin extends BasePlugin {
|
||||
// class methods here
|
||||
}
|
||||
```
|
||||
|
||||
!!! note
|
||||
|
||||
In all the code samples below, whenever we reference an example method, it is assumed
|
||||
that it is defined _within_ the class, though this is not explicitly written, for the sake of
|
||||
clarity and space.
|
||||
|
||||
### Make your plugin available
|
||||
|
||||
That's basically it! With a Node.js package exporting a plugin class and with correct Appium
|
||||
extension metadata, you've got yourself an Appium plugin! Now it doesn't *do* anything, but you can
|
||||
load it up in Appium, activate it, etc...
|
||||
|
||||
To make it available to users, you could publish it via NPM. When you do so, your plugin will be
|
||||
installable via the Appium CLI:
|
||||
|
||||
```
|
||||
appium plugin install --source=npm <plugin-package-on-npm>
|
||||
```
|
||||
|
||||
It's a good idea to test your plugin first, of course. One way to see how it works within Appium is
|
||||
to install it locally first:
|
||||
|
||||
```
|
||||
appium plugin install --source=local /path/to/your/plugin
|
||||
```
|
||||
|
||||
And of course, plugins must be "activated" during Appium server start, so make sure you direct your
|
||||
users to do so:
|
||||
|
||||
```
|
||||
appium --use-plugins=plugin-name
|
||||
```
|
||||
|
||||
### Developing your plugin
|
||||
|
||||
How you develop your plugin is up to you. It is convenient, however, to run it from within Appium
|
||||
without having to do lots of publishing and installing. The most straightforward way to do this is
|
||||
to include the most recent version of Appium as a `devDependency` (although its being already
|
||||
included as a `peerDependency` is sufficient in newer versions of NPM), and then also your own plugin,
|
||||
like this:
|
||||
|
||||
```json
|
||||
{
|
||||
"devDependencies": {
|
||||
...,
|
||||
"appium": "^2.0.0",
|
||||
"your-plugin": "file:.",
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now, you can run Appium locally (`npm exec appium` or `npx appium`), and because your plugin is
|
||||
listed as a dependency alongside it, it will be automatically "installed" and available. You can
|
||||
design your e2e tests this way, or if you're writing them in Node.js, you can simply import
|
||||
Appium's start server methods to handle starting and stopping the Appium server in Node.
|
||||
|
||||
Of course, you can always install it locally as described above as well.
|
||||
|
||||
Anytime you make changes to your plugin code, you'll need to restart the Appium server to make sure
|
||||
it picks up the latest code. As with drivers, you can set the `APPIUM_RELOAD_EXTENSIONS`
|
||||
environment variable if you wish Appium to try to re-require your plugin module when a new session
|
||||
starts.
|
||||
|
||||
## Standard plugin implementation ideas
|
||||
|
||||
These are things you will probably find yourself wanting to do when creating a plugin.
|
||||
|
||||
### Set up state in a constructor
|
||||
|
||||
If you define your own constructor, you'll need to call `super` to make sure all the standard state
|
||||
is set up correctly:
|
||||
|
||||
```js
|
||||
constructor(...args) {
|
||||
super(...args);
|
||||
// now do your own thing
|
||||
}
|
||||
```
|
||||
|
||||
The `args` parameter here is the object containing all the CLI args used to start the Appium
|
||||
server.
|
||||
|
||||
### Intercept and handle specific Appium commands
|
||||
|
||||
This is the most normal behavior for Appium plugins -- to modify or replace the execution of one or
|
||||
more commands that would normally be handled by the active driver. To override the default command
|
||||
handling, you need to implement `async` methods in your class with the same name as the Appium
|
||||
commands to be handled (just exactly how [drivers themselves are
|
||||
implemented](./build-drivers.md#implement-webdriver-commands)). Curious what command names there
|
||||
are? They are defined in the Appium base driver's
|
||||
[routes.js](https://github.com/appium/appium-base-driver/blob/master/lib/protocol/routes.js) file,
|
||||
and of course you can add more as defined in the next section.
|
||||
|
||||
Each command method is sent the following arguments:
|
||||
|
||||
1. `next`: This is a reference to an `async` function which encapsulates the chain of behaviors which would take place if this plugin were not handling the command. You can choose to call the next behavior in the chain at any point in your logic (by making sure to include `await next()` somewhere), or not. If you don't, it means the default behavior (or any plugins registered after this one) won't be run.
|
||||
1. `driver`: This is the object representing the driver handling the current session. You have access to it for any work you need to do, for example calling other driver methods, checking capabilities or settings, etc...
|
||||
1. `...args`: A spread array with any arguments that have been applied to the command by the user.
|
||||
|
||||
For example, if we wanted to override the `setUrl` command to simply add some extra logging on top,
|
||||
we would implement as follows:
|
||||
|
||||
```js
|
||||
async setUrl(next, driver, url) {
|
||||
this.log(`Let's get the page source for some reason before navigating to '${url}'!`);
|
||||
await driver.getPageSource();
|
||||
const result = await next();
|
||||
this.log(`We can also log after the original behaviour`);
|
||||
return result;
|
||||
}
|
||||
```
|
||||
|
||||
### Intercept and handle _all_ Appium commands
|
||||
|
||||
You might find yourself in a position where you want to handle *all* commands, in order to inspect
|
||||
payloads to determine whether or not to act in some way. If so, you can implement `async handle`,
|
||||
and any command that is not handled by one of your named methods will be handled by this method
|
||||
instead. It takes the following parameters (with all the same semantics as above):
|
||||
|
||||
1. `next`
|
||||
1. `driver`
|
||||
1. `cmdName` - string representing the command being run
|
||||
1. `...args`
|
||||
|
||||
For example, let's say we want to log timing for all Appium commands as part of a plugin. We could
|
||||
do this by implementing `handle` in our plugin class as follows:
|
||||
|
||||
```js
|
||||
async handle(next, driver, cmdName, ...args) {
|
||||
const start = Date.now();
|
||||
try {
|
||||
const result = await next();
|
||||
} finally {
|
||||
const elapsedMs = Date.now() - start;
|
||||
this.log(`Command '${cmdName}' took ${elapsedMs}`);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
```
|
||||
|
||||
### Work around driver proxies
|
||||
|
||||
There is a bit of a gotcha with handling Appium commands. Appium drivers have the ability to turn
|
||||
on a special 'proxy' mode, wherein the Appium server process takes a look at incoming URLs, and
|
||||
decides whether to forward them on to some upstream WebDriver server. It could happen that
|
||||
a command which a plugin wants to handle is designated as a command which is being proxied to an
|
||||
upstream server. In this case, we run into a problem, because the plugin never gets a chance to
|
||||
handle that command! For this reason, plugins can implement a special member function called
|
||||
`shouldAvoidProxy`, which takes the following parameters:
|
||||
|
||||
1. `method` - string denoting HTTP method (`GET`, `POST`, etc...)
|
||||
2. `route` - string denoting the requested resource, for example `/session/8b3d9aa8-a0ca-47b9-9ab7-446e818ec4fc/source`
|
||||
3. `body` - optional value of any type representing the WebDriver request body
|
||||
|
||||
These parameters define an incoming request. If you want to handle a command in your plugin which
|
||||
would normally be proxied directly through a driver, you could disable or 'avoid' proxying the
|
||||
request, and instead have the request fall into the typical Appium command execution flow (and
|
||||
thereby your own command function). To avoid proxying a request, just return `true` from
|
||||
`shouldAvoidProxy`. Some examples of how this method is used are in the [Universal XML
|
||||
plugin](https://github.com/appium/appium/blob/master/packages/universal-xml/lib/plugin.js) (where
|
||||
we want to avoid proxying the `getPageSource` command, or in the [Images
|
||||
plugin](https://github.com/appium/appium/blob/master/packages/images-plugin/lib/plugin.js) (where
|
||||
we want to conditionally avoid proxying any command if it looks like it contains an image element).
|
||||
|
||||
|
||||
### Throw WebDriver-specific errors
|
||||
|
||||
The WebDriver spec defines a [set of error
|
||||
codes](https://github.com/jlipps/simple-wd-spec#error-codes) to accompany command responses if an
|
||||
error occurred. Appium has created error classes for each of these codes, so you can throw the
|
||||
appropriate error from inside a command, and it will do the right thing in terms of the protocol
|
||||
response to the user. To get access to these error classes, import them from `appium/driver`:
|
||||
|
||||
```js
|
||||
import {errors} from 'appium/driver';
|
||||
|
||||
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
|
||||
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:
|
||||
|
||||
```js
|
||||
import {logging} from 'appium/support';
|
||||
const log = logging.getLogger('MyPlugin');
|
||||
```
|
||||
|
||||
## Further possibilities for Appium plugins
|
||||
|
||||
These are things your plugin *can* do to take advantage of extra plugin features or do its job more
|
||||
conveniently.
|
||||
|
||||
### Add a schema for custom command line arguments
|
||||
|
||||
You can add custom CLI args if you want your plugin to receive data from the command line when the
|
||||
Appium server is started (for example, ports that a server administrator should set that should not
|
||||
be passed in as capabilities).
|
||||
|
||||
This works largely the same for plugins as it does for drivers, so for more details have a look at
|
||||
[the equivalent section in the building drivers
|
||||
doc](./build-drivers.md#add-a-schema-for-custom-command-line-arguments).
|
||||
|
||||
The only difference is that to construct the CLI argument name, you prefix it with
|
||||
`--plugin-<name>`. So for example, if you have a plugin named `pluggo` and a CLI arg defined with
|
||||
the name `electro-port`, you can set it when starting Appium via `--plugin-pluggo-electro-port`.
|
||||
|
||||
Setting args via a configuration file is also supported, as it is for drivers, but under the
|
||||
`plugin` field instead. For example:
|
||||
|
||||
```json
|
||||
{
|
||||
"server": {
|
||||
"plugin": {
|
||||
"pluggo": {
|
||||
"electro-port": 1234
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Add plugin scripts
|
||||
|
||||
Sometimes you might want users of your plugin to be able to run scripts outside the context of
|
||||
a session (for example, to run a script that pre-builds aspects of your plugin). To support this,
|
||||
you can add a map of script names and JS files to the `scripts` field within your Appium extension
|
||||
metadata. So let's say you've created a script in your project that lives in a `scripts` directory
|
||||
in your project, named `plugin-prebuild.js`. Then you could add a `scripts` field like this:
|
||||
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"prebuild": "./scripts/plugin-prebuild.js"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now, assuming your plugin is named `myplugin`, users of your plugin can run `appium plugin run
|
||||
myplugin prebuild`, and your script will execute.
|
||||
|
||||
### Add new Appium commands
|
||||
|
||||
If you want to offer functionality that doesn't map to any of the existing commands supported by
|
||||
drivers, you can create new commands in one of two ways, just as is possible for drivers:
|
||||
|
||||
1. Extending the WebDriver protocol and creating client-side plugins to access the extensions
|
||||
1. Overloading the Execute Script command by defining [Execute
|
||||
Methods](../guides/execute-methods.md)
|
||||
|
||||
If you want to follow the first path, you can direct Appium to recognize new methods and add them
|
||||
to its set of allowed HTTP routes and command names. You do this by assigning the `newMethodMap`
|
||||
static variable in your driver class to an object of the same form as Appium's `routes.js` object.
|
||||
For example, here is part of the `newMethodMap` for the `FakePlugin` example driver:
|
||||
|
||||
```js
|
||||
static newMethodMap = {
|
||||
'/session/:sessionId/fake_data': {
|
||||
GET: {command: 'getFakeSessionData', neverProxy: true},
|
||||
POST: {
|
||||
command: 'setFakeSessionData',
|
||||
payloadParams: {required: ['data']},
|
||||
neverProxy: true,
|
||||
},
|
||||
},
|
||||
'/session/:sessionId/fakepluginargs': {
|
||||
GET: {command: 'getFakePluginArgs', neverProxy: true},
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
!!! note
|
||||
|
||||
If you're using TypeScript, static member objects like these should be defined `as const`.
|
||||
|
||||
In this example we're adding a few new routes and a total of 3 new commands. For more examples of
|
||||
how to define commands in this way, it's best to have a look through `routes.js`. Now all you need
|
||||
to do is implement the command handlers in the same way you'd implement any other Appium command.
|
||||
|
||||
Note also the special `neverProxy` key for commands; this is generally a good idea to set to `true`
|
||||
for plugins, since your plugin might be active for a driver that is put into proxy mode but hasn't
|
||||
bothered to decline proxying for these (new and therefore unknown) commands. Setting `neverProxy`
|
||||
to `true` here will cause Appium to never proxy these routes and therefore ensure your plugin
|
||||
handles them, even if a driver is in proxy mode.
|
||||
|
||||
The downside of adding new commands via `newMethodMap` is that people using the standard Appium
|
||||
clients won't have nice client-side functions designed to target these endpoints. So you would need
|
||||
to create and release client-side plugins for each language you want to support (directions or
|
||||
examples can be found at the relevant client docs).
|
||||
|
||||
An alternative to this way of doing things is to overload a command which all WebDriver clients
|
||||
have access to already: Execute Script. Make sure to read the section on [adding new
|
||||
commands](./build-drivers.md#extend-the-existing-protocol-with-new-commands) in
|
||||
the Building Drivers guide to understand the way this works in general. The way it works with
|
||||
plugins is only slightly different. Let's look at an example taken from Appium's `fake-plugin`:
|
||||
|
||||
```js
|
||||
static executeMethodMap = {
|
||||
'fake: plugMeIn': {
|
||||
command: 'plugMeIn',
|
||||
params: {required: ['socket']},
|
||||
},
|
||||
};
|
||||
|
||||
async plugMeIn(next, driver, socket) {
|
||||
return `Plugged in to ${socket}`;
|
||||
}
|
||||
|
||||
async execute(next, driver, script, args) {
|
||||
return await this.executeMethod(next, driver, script, args);
|
||||
}
|
||||
```
|
||||
|
||||
We have three important components shown here which make this system work, all of which are defined
|
||||
inside the plugin class:
|
||||
|
||||
1. The `executeMethodMap`, defined in just the same way as for drivers
|
||||
1. The implementation of the command method as defined in `executeMethodMap` (in this case,
|
||||
`plugMeIn`)
|
||||
1. The overriding/handling of the `execute` command. Just like any plugin command handlers, the
|
||||
first two arguments are `next` and `driver`, followed by the script name and args. `BasePlugin`
|
||||
implements a helper method which we can simply call with all of these arguments.
|
||||
|
||||
Overriding Execute Methods from drivers works as you'd expect: if your plugin defines an Execute
|
||||
Method with the same name as that of a driver, your command (in this case `plugMeIn`) will be
|
||||
called first. You can choose to run the driver's original behaviour via `next` if you want.
|
||||
|
||||
### Build Appium Doctor checks
|
||||
|
||||
Your users can run `appium plugin doctor <pluginName>` to run installation and health checks. Visit
|
||||
the [Building Doctor Checks](./build-doctor-checks.md) guide for more information on this
|
||||
capability.
|
||||
|
||||
### Update the Appium server object
|
||||
|
||||
You probably don't normally need to update the Appium server object (which is an
|
||||
[Express](https://expressjs.com/) server having already been
|
||||
[configured](https://github.com/appium/appium/blob/master/packages/base-driver/lib/express/server.js)
|
||||
in a variety of ways). But, for example, you could add new Express middleware to the server to
|
||||
support your plugin's requirements. To update the server you must implement the `static async
|
||||
updateServer` method in your class. This method takes three parameters:
|
||||
|
||||
* `expressApp`: the Express app object
|
||||
* `httpServer`: the Node HTTP server object
|
||||
* `cliArgs`: a map of the CLI args used to start the Appium server
|
||||
|
||||
You can do whatever you want with them inside the `updateServer` method. You might want to
|
||||
reference how these objects are created and worked with in the BaseDriver code, so that you know
|
||||
you're not undoing or overriding anything standard and important. But if you insist, you can, with
|
||||
results you'll need to test! Warning: this should be considered an advanced feature and requires
|
||||
knowledge of Express, as well as the care not to do anything that could affect the operation of
|
||||
other parts of the Appium server!
|
||||
|
||||
### Handle unexpected session shutdown
|
||||
|
||||
When developing a plugin you may want to add some cleanup logic for when a session ends. You would
|
||||
naturally do this by adding a handler for `deleteSession`. This works in most cases, except when
|
||||
the session does not finish cleanly. Appium sometimes determines that a session has finished
|
||||
unexpectedly, and in these situations, Appium will look for a method called `onUnexpectedShutdown`
|
||||
in your plugin class, which will be called (passing the current session driver as the first
|
||||
parameter, and the error object representing the cause of the shutdown as the second), giving you
|
||||
an opportunity to take any steps that might be necessary to clean up from the session. For example,
|
||||
keeping in mind that the function is not `await`ed you could implement something like this:
|
||||
|
||||
```js
|
||||
async onUnexpectedShutdown(driver, cause) {
|
||||
try {
|
||||
// do some cleanup
|
||||
} catch (e) {
|
||||
// log any errors; don't allow anything to be thrown as they will be unhandled rejections
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,18 @@
|
||||
---
|
||||
hide:
|
||||
- toc
|
||||
|
||||
title: Intro to Development
|
||||
---
|
||||
|
||||
Appium 2 is built with a modular structure, which means that Appium extensions (drivers and plugins)
|
||||
are decoupled from the main Appium module, and you only need to install the extensions that you
|
||||
want to use. This modular structure also unlocks the ability to develop entirely new extensions!
|
||||
|
||||
This section of the Appium documentation is intended to help aspiring developers with creating their
|
||||
own Appium extension:
|
||||
|
||||
* For creating a driver, see the [Build Drivers](./build-drivers.md) page
|
||||
* For creating a plugin, take a look at the [Build Plugins](build-plugins.md) page
|
||||
* Drivers and plugins both need documentation, so check out the [Build Documentation](./build-docs.md) page
|
||||
* For creating a doctor check, see the [Building Doctor Checks](./build-doctor-checks.md) page
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
title: Building Docs for Appium Extensions
|
||||
---
|
||||
|
||||
TODO
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
title: Building Appium Plugins
|
||||
---
|
||||
|
||||
TODO
|
||||
@@ -0,0 +1,42 @@
|
||||
---
|
||||
hide:
|
||||
- toc
|
||||
|
||||
title: Appium Clients
|
||||
---
|
||||
|
||||
You need a client to write and run Appium scripts. You'll want to become very
|
||||
familiar with your client documentation (as well as the documentation of any Selenium client that
|
||||
the Appium client depends on) since that will be your primary interface to Appium.
|
||||
|
||||
To learn more about clients, read our [Client Intro](../intro/clients.md).
|
||||
|
||||
### Official Clients
|
||||
|
||||
These clients are currently maintained by the Appium team:
|
||||
|
||||
|Client|Language|
|
||||
|-|-|
|
||||
|[Appium Java client](https://github.com/appium/java-client)|Java|
|
||||
|[Appium Python client](https://github.com/appium/python-client)|Python|
|
||||
|[Appium Ruby Core client](https://github.com/appium/ruby_lib_core) (Recommended)<br>[Appium Ruby client](https://github.com/appium/ruby_lib)|Ruby|
|
||||
|[Appium .NET client](https://github.com/appium/dotnet-client)|C#|
|
||||
|
||||
### Other Clients
|
||||
|
||||
These clients are not maintained by the Appium team and can be used with other languages:
|
||||
|
||||
|Client|Language|
|
||||
|-|-|
|
||||
|[WebdriverIO](https://webdriver.io/docs/appium)|Node.js|
|
||||
|[Nightwatch.js](https://nightwatchjs.org/guide/mobile-app-testing/introduction.html)|Node.js|
|
||||
|[RobotFramework](https://github.com/serhatbolsu/robotframework-appiumlibrary)|DSL|
|
||||
|[multicatch's appium-client](https://github.com/multicatch/appium-client)|Rust|
|
||||
|
||||
In general, any W3C WebDriver spec-compatible client will also integrate well with Appium, though
|
||||
some Appium-specific commands may not be implemented in other clients.
|
||||
|
||||
!!! note
|
||||
|
||||
If you maintain an Appium client that you would like to be listed in the Appium docs, feel free
|
||||
to make a PR to add it to this section with a link to the documentation for the client.
|
||||
@@ -0,0 +1,51 @@
|
||||
---
|
||||
hide:
|
||||
- toc
|
||||
|
||||
title: Appium Drivers
|
||||
---
|
||||
|
||||
You can't use Appium without at least one driver! Click on the link for each driver to see the
|
||||
specific installation instructions and documentation for that driver.
|
||||
|
||||
Generally, drivers can be installed using their listed installation key, with the following command:
|
||||
```
|
||||
appium driver install <installation key>
|
||||
```
|
||||
|
||||
To learn more about drivers, check out the [Driver Intro](../intro/drivers.md).
|
||||
|
||||
### Official Drivers
|
||||
|
||||
These drivers are currently maintained by the Appium team:
|
||||
|
||||
|Driver|Installation Key|Platform(s)|Mode(s)|
|
||||
|--|--|--|--|
|
||||
|[Chromium](https://github.com/appium/appium-chromium-driver)|`chromium`|macOS, Windows, Linux|Web|
|
||||
|[Espresso](https://github.com/appium/appium-espresso-driver)|`espresso`|Android|Native|
|
||||
|[Gecko](https://github.com/appium/appium-geckodriver)|`gecko`|macOS, Windows, Linux, Android|Web|
|
||||
|[Mac2](https://github.com/appium/appium-mac2-driver)|`mac2`|macOS|Native|
|
||||
|[Safari](https://github.com/appium/appium-safari-driver)|`safari`|macOS, iOS|Web|
|
||||
|[UiAutomator2](https://github.com/appium/appium-uiautomator2-driver)|`uiautomator2`|Android|Native, Hybrid, Web|
|
||||
|[Windows](https://github.com/appium/appium-windows-driver)|`windows`|Windows|Native|
|
||||
|[XCUITest](https://github.com/appium/appium-xcuitest-driver)|`xcuitest`|iOS|Native, Hybrid, Web|
|
||||
|
||||
### Other Drivers
|
||||
|
||||
These drivers are not maintained by the Appium team and can be used to target additional platforms:
|
||||
|
||||
|Driver|Installation Key|Platform(s)|Mode(s)|Supported By|
|
||||
|--|--|--|--|--|
|
||||
|[Flutter](https://github.com/appium/appium-flutter-driver)|`--source=npm appium-flutter-driver`|iOS, Android|Native|Community|
|
||||
|[Flutter Integration](https://github.com/AppiumTestDistribution/appium-flutter-integration-driver)|`--source=npm appium-flutter-integration-driver`|iOS, Android|Native|Community / `@AppiumTestDistribution`|
|
||||
|[LG WebOS](https://github.com/headspinio/appium-lg-webos-driver)|`--source=npm appium-lg-webos-driver`|LG TV|Web|HeadSpin|
|
||||
|[Linux](https://github.com/fantonglang/appium-linux-driver)|`--source=npm @stdspa/appium-linux-driver`|Linux|Native|`@fantonglang`|
|
||||
|[Roku](https://github.com/headspinio/appium-roku-driver)|`--source=npm @headspinio/appium-roku-driver`|Roku|Native|HeadSpin|
|
||||
|[Tizen](https://github.com/Samsung/appium-tizen-driver)|`--source=npm appium-tizen-driver`|Android|Native|Community / Samsung|
|
||||
|[TizenTV](https://github.com/headspinio/appium-tizen-tv-driver)|`--source=npm appium-tizen-tv-driver`|Samsung TV|Web|HeadSpin|
|
||||
|[Youi](https://github.com/YOU-i-Labs/appium-youiengine-driver)|`--source=npm appium-youiengine-driver`|iOS, Android, macOS, Linux, tvOS|Native|Community / You.i|
|
||||
|
||||
!!! note
|
||||
|
||||
If you maintain an Appium driver that you would like to be listed in the Appium docs, feel free
|
||||
to make a PR to add it to this section with a link to the driver documentation.
|
||||
@@ -1,6 +1,45 @@
|
||||
---
|
||||
title: Appium生态系统
|
||||
hide:
|
||||
- toc
|
||||
|
||||
title: Ecosystem Overview
|
||||
---
|
||||
|
||||
Appium has a wide ecosystem of related software and tools. This section of the Appium documentation
|
||||
aims to compile a listing of various officially-supported and community-supported Appium projects:
|
||||
|
||||
TODO
|
||||
<div class="grid cards" markdown>
|
||||
|
||||
- :material-car: __Drivers__
|
||||
|
||||
---
|
||||
|
||||
Link Appium to your test device
|
||||
|
||||
[:octicons-arrow-right-24: View all drivers](./drivers.md)
|
||||
|
||||
- :octicons-code-16: __Clients__
|
||||
|
||||
---
|
||||
|
||||
Link Appium to your automation code
|
||||
|
||||
[:octicons-arrow-right-24: View all clients](./clients.md)
|
||||
|
||||
- :fontawesome-solid-plug: __Plugins__
|
||||
|
||||
---
|
||||
|
||||
Modify and extend Appium functionality
|
||||
|
||||
[:octicons-arrow-right-24: View all plugins](./plugins.md)
|
||||
|
||||
- :material-wrench: __Tools__
|
||||
|
||||
---
|
||||
|
||||
Interact with Appium in other ways
|
||||
|
||||
[:octicons-arrow-right-24: View all tools](./tools.md)
|
||||
|
||||
</div>
|
||||
@@ -0,0 +1,45 @@
|
||||
---
|
||||
hide:
|
||||
- toc
|
||||
|
||||
title: Appium Plugins
|
||||
---
|
||||
|
||||
Plugins offer various ways to extend or modify Appium's behaviour. They are _completely optional_
|
||||
and are not needed for standard automation functionality, but you may find them to be useful
|
||||
for more specialised automation workflows.
|
||||
|
||||
Generally, plugins can be installed using their listed installation key, with the following command:
|
||||
```
|
||||
appium plugin install <installation key>
|
||||
```
|
||||
|
||||
### Official Plugins
|
||||
|
||||
These plugins are are currently maintained by the Appium team:
|
||||
|
||||
|<div style="width:7em">Plugin</div>|<div style="width:8em">Installation Key</div>|Description|
|
||||
|---|---|---|
|
||||
|[Execute Driver](https://github.com/appium/appium/tree/master/packages/execute-driver-plugin)|`execute-driver`|Run entire batches of commands in a single call to the Appium server|
|
||||
|[Images](https://github.com/appium/appium/tree/master/packages/images-plugin)|`images`|Image matching and comparison features|
|
||||
|[Relaxed Caps](https://github.com/appium/appium/tree/master/packages/relaxed-caps-plugin)|`relaxed-caps`|Relax Appium's requirement for vendor prefixes on capabilities|
|
||||
|[Universal XML](https://github.com/appium/appium/tree/master/packages/universal-xml-plugin)|`universal-xml`|Instead of the standard XML format for iOS and Android, use an XML definition that is the same across both platforms|
|
||||
|
||||
### Other Plugins
|
||||
|
||||
These plugins are not maintained by the Appium team and can provide additional functionality:
|
||||
|
||||
|<div style="width:6em">Plugin</div>|<div style="width:19em">Installation Key</div>|Description|<div style="width:13em">Supported By</div>|
|
||||
|---|---|---|---|
|
||||
|[AltUnity](https://github.com/headspinio/appium-altunity-plugin)|`--source=npm appium-altunity-plugin`|Target Unity games and apps for automation with a new context, via the AltUnityTester framework|HeadSpin|
|
||||
|[Device Farm](https://github.com/AppiumTestDistribution/appium-device-farm)|`--source=npm appium-device-farm`|Manage and create driver sessions on connected Android devices and iOS simulators|`@AppiumTestDistribution`|
|
||||
|[Gestures](https://github.com/AppiumTestDistribution/appium-gestures-plugin)|`--source=npm appium-gestures-plugin`|Perform basic gestures using W3C Actions|`@AppiumTestDistribution`|
|
||||
|[Interceptor](https://github.com/AppiumTestDistribution/appium-interceptor-plugin)|`--source=npm appium-interceptor`|Intercept and mock API requests and responses|`@AppiumTestDistribution`|
|
||||
|[OCR](https://github.com/jlipps/appium-ocr-plugin)|`--source=npm appium-ocr-plugin`|Find elements via OCR text|`@jlipps`|
|
||||
|[Reporter](https://github.com/AppiumTestDistribution/appium-reporter-plugin)|`--source=npm appium-reporter-plugin`|Generate standalone consolidated HTML reports with screenshots|`@AppiumTestDistribution`|
|
||||
|[Wait](https://github.com/AppiumTestDistribution/appium-wait-plugin)|`--source=npm appium-wait-plugin`|Manage global element wait timeouts|`@AppiumTestDistribution`|
|
||||
|
||||
!!! note
|
||||
|
||||
If you maintain an Appium plugin that you would like to be listed in the Appium docs, feel free
|
||||
to make a PR to add it to this section with a link to the documentation for the plugin.
|
||||
@@ -0,0 +1,42 @@
|
||||
---
|
||||
hide:
|
||||
- toc
|
||||
|
||||
title: Appium-Related Tools
|
||||
---
|
||||
|
||||
There are several Appium tools that have been created to to assist with things not directly related
|
||||
to testing, such as Appium installation, test development, and more.
|
||||
|
||||
### [Appium Inspector](https://appium.github.io/appium-inspector/latest/)
|
||||
|
||||
Appium has a graphical client which can be used to inspect application screenshots, view the
|
||||
application hierarchy, run Appium commands, record app interactions, and more. It is very useful
|
||||
for Appium test development.
|
||||
|
||||
Find downloads and more information on its GitHub page: [Appium Inspector](https://github.com/appium/appium-inspector)
|
||||
|
||||
### Appium Doctor
|
||||
|
||||
Appium Doctor is a command-line tool built into Appium drivers and plugins.
|
||||
The command is expected to be used to validate whether a driver or plugin has all of its prerequisites and other environment details set up correctly
|
||||
if the driver/plugin author implemented the `doctor` command.
|
||||
|
||||
For example, `uiautomator2` driver provides the `doctor` command below.
|
||||
|
||||
```
|
||||
appium driver doctor uiautomator2
|
||||
```
|
||||
|
||||
It shows no result if the driver/plugin author did not implement them.
|
||||
|
||||
More information on this command can be found in the [Command-Line Usage documentation](../cli/extensions.md#doctor).
|
||||
For driver/plugin developers, please read [Building Doctor Checks](../developing/build-doctor-checks.md).
|
||||
|
||||
### Other Tools
|
||||
|
||||
These tools are not maintained by the Appium team and can be used to assist with other problems:
|
||||
|
||||
|Name|Description|Supported By|
|
||||
|---|---|---|
|
||||
|[appium-installer](https://github.com/AppiumTestDistribution/appium-installer)|Help set up an Appium environment for Android and iOS|`@AppiumTestDistribution`|
|
||||
@@ -0,0 +1,57 @@
|
||||
---
|
||||
title: Local Validation Of Extension PRs
|
||||
---
|
||||
|
||||
Sometimes it might be necessary to validate if a remote driver or a plugin PR works for the particular local
|
||||
environment before it is merged or published. This tutorial describes how to achieve that.
|
||||
|
||||
## Requirements
|
||||
|
||||
- Recent LTS version of NodeJS. Check [Node.js main page](https://nodejs.org) for the download link.
|
||||
- Recent version of the Appium server. Use the following commands to ensure you have
|
||||
the latest version installed: `npm uninstall appium` and `npm install -g appium`.
|
||||
- [Git](https://git-scm.com/) should be available locally.
|
||||
|
||||
## Installation
|
||||
|
||||
- [Check out]((https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/checking-out-pull-requests-locally)) the PR locally.
|
||||
There is also an option (although it is less flexible, because you won't be able to easily fetch
|
||||
any changes to this PR later) to simply [download](https://docs.github.com/en/repositories/working-with-files/using-files/downloading-source-code-archives) and unzip the sources
|
||||
locally. If you choose the later option then no `git` tool would be necessary.
|
||||
- Navigate to the local driver or plugin folder and run `npm i` from that folder.
|
||||
- Make sure you don't have the given driver or plugin already installed. Use the `appium driver uninstall <driver_name>`
|
||||
or `appium plugin uninstall <plugin_name>` CLI in order to delete any leftovers. The value of
|
||||
`<driver_name>`/`<plugin_name>` depends on the actual driver or plugin name the PR has been prepared for.
|
||||
If you are not sure which name you need to use then check the content of the `package.json` manifest,
|
||||
which must be always located under the root folder of the fetched sources. You should be looking for
|
||||
the `"appium" -> "driverName"` entry value there.
|
||||
- Change the current folder to the one, which is NOT the driver/plugin folder root or its subfolder.
|
||||
Also, make sure your current working folder does not contain any extra `package.json` file. If
|
||||
it does then simply navigate to any other folder that does not.
|
||||
- Run the following command in order to link the driver/plugin sources to the Appium server:
|
||||
`appium driver install --source=local <full_path_to_driver_folder_with_fetched_sources>` or
|
||||
`appium plugin install --source=local <full_path_to_plugin_folder_with_fetched_sources>`.
|
||||
- Stop the Appium server if it is running and start it again (`appium server --use-drivers=<driver_name>`)
|
||||
to check the list of loaded drivers. If the linking succeeded then you must see the driver name and the path to its
|
||||
parent folder in the server logs. In case of a plugin it is required to explicitly request
|
||||
this plugin to be loaded upon server startup: `appium server --use-plugins=<plugin_name>`.
|
||||
|
||||
## Update
|
||||
|
||||
After you have tested the PR and there are issues it might be necessary to update the local branch with
|
||||
the recent changes from Git. Follow the next steps for that:
|
||||
|
||||
- Navigate to the parent folder of your local driver/plugin and run `git pull`.
|
||||
- Stop Appium server if it is running.
|
||||
- Run `npm i` in the parent folder of your local driver/plugin to rebuild it and update any dependencies if necessary.
|
||||
- Start Appium server again similarly to how this is done in the corresponding [Installation](#installation) step above.
|
||||
|
||||
## Switching Back To a Stable Release
|
||||
|
||||
After the PR is merged there is no need to use the local plugin/driver deployment anymore, and it makes sense
|
||||
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>`.
|
||||
- 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.
|
||||
@@ -0,0 +1,76 @@
|
||||
---
|
||||
title: Caching of Application Bundles
|
||||
---
|
||||
|
||||
Appium's base driver provides a feature which enables caching of application builds provided, for example,
|
||||
as `app` capability value or to endpoints similar to the `installApp` one. This article explains common caching
|
||||
principles, so you could create more performant and efficient test suite execution strategies.
|
||||
|
||||
## Why Caching Is Necessary
|
||||
|
||||
Mobile application bundles could reach hundreds of megabytes is 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.
|
||||
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
|
||||
first before they could be actually installed on the device under test. On iOS, for example, these are `.ipa` or
|
||||
`.zip` compressed application bundles, or .`aab` on Android.
|
||||
|
||||
## Caching of Remote Application Bundles
|
||||
|
||||
In order to validate whether an app bundle downloaded from the given URL could be (re)used from the cache the following
|
||||
steps are applied:
|
||||
|
||||
1. The script checks if the given URL is already present in the cache.
|
||||
If yes then it tries to fetch previously remembered
|
||||
[Last-Modified](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Last-Modified)
|
||||
or [ETag](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag) header values for it.
|
||||
2. If `ETag` value is present then it is put into
|
||||
[If-None-Match request header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-None-Match).
|
||||
Else if `Last-Modified` header value is present then it is put into
|
||||
[If-Modified-Since request header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-Modified-Since).
|
||||
Otherwise, no caching is applied.
|
||||
3. If the response status is equal to `304` then the previously cached binary is used,
|
||||
otherwise the cached entry is reset and refreshed.
|
||||
|
||||
## Caching of Local Application Bundles
|
||||
|
||||
It only makes sense to cache application bundles if they need some preprocessing before being installed on the device under test.
|
||||
For example, on iOS `.ipa` bundles must be unzipped, because the system installer only works with `.app` folders.
|
||||
|
||||
1. The script verifies if the given bundle path is already present in the cache. If the bundle was not in the cache yet
|
||||
then it gets preprocessed and added there.
|
||||
2. The script validates the hashsum of the bundle and compares it to the previously stored one. If hash sums don't match
|
||||
then the cached item gets deleted and the preprocessing of the bundle repeats.
|
||||
|
||||
## How The Cache File System Is Configured
|
||||
|
||||
The cache where the base driver keeps all application bundles is located in the system temp folder. It is configured
|
||||
on per-process basis, so each test session initialized in scope of the same Appium server process takes advantages
|
||||
of it. It is a [LRU Cache](https://www.npmjs.com/package/lru-cache) with the following limitations:
|
||||
|
||||
- Max items: 1024. You may customize it by providing a new value to
|
||||
the [APPIUM_APPS_CACHE_MAX_ITEMS](../cli/env-vars.md) environment variable.
|
||||
Do not set it to a lower number than the amount of apps in all parallel sessions per process.
|
||||
- Max time to live (TTL) for each entry: 24 hours.
|
||||
You may customize it by providing a new value to the
|
||||
[APPIUM_APPS_CACHE_MAX_AGE](../cli/env-vars.md) environment variable.
|
||||
Do not set it to a lower number than the duration of a single session startup.
|
||||
- TTL is refreshed for each entry upon access
|
||||
- By default the full application URL is used as cache key. You may change this behavior
|
||||
by enabling the [APPIUM_APPS_CACHE_IGNORE_URL_QUERY](../cli/env-vars.md) environment variable.
|
||||
If the above option is enabled then the 'search' part of the app URL will be cut off from cache keys.
|
||||
See the corresponding [feature request](https://discuss.appium.io/t/regarding-app-caching-when-using-aws-s3-presigned-urls/42713)
|
||||
for more details.
|
||||
|
||||
!!! warning
|
||||
|
||||
Note: The cache root folder is set up for automatic deletion on Appium process termination. This would only
|
||||
work if Appium server is killed with `SIGINT` or `SIGTERM`. If `SIGKILL` is used then no cache cleanup
|
||||
would be performed.
|
||||
@@ -1,5 +1,265 @@
|
||||
---
|
||||
title: Capabilities
|
||||
title: Session Capabilities
|
||||
---
|
||||
|
||||
TODO
|
||||
"Capabilities" is the name given to the set of parameters used to start an Appium session. The
|
||||
information in the set describes what sort of "capabilities" you want your session to have, for
|
||||
example, a certain mobile operating system or a certain version of a device. Capabilities are
|
||||
represented as key-value pairs, with values allowed to be any valid JSON type, including
|
||||
other objects.
|
||||
|
||||
The W3C WebDriver spec's [section on Capabilities](https://w3c.github.io/webdriver/#capabilities)
|
||||
identifies a small set of 10 standard capabilities, including the following:
|
||||
|
||||
| Capability Name | Type | Description |
|
||||
|------------------|----------|------------------------------------------------|
|
||||
| `browserName` | `string` | The name of the browser to launch and automate |
|
||||
| `browserVersion` | `string` | The specific version of the browser |
|
||||
| `platformName` | `string` | The type of platform hosting the browser |
|
||||
|
||||
## Common Appium Capabilities
|
||||
|
||||
Appium understands these browser-focused capabilities, but introduces a number of additional
|
||||
capabilities. According to the WebDriver spec, any
|
||||
non-standard "extension capabilities" must include a namespace prefix (signifying the vendor
|
||||
introducing the capability), ending in a `:`. Appium's vendor prefix is
|
||||
`appium:`, and so any Appium-specific capabilities must include this prefix. Depending on which
|
||||
client you are using, 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.
|
||||
|
||||
Here is a list of all the globally-recognized Appium capabilities:
|
||||
|
||||
!!! info
|
||||
|
||||
Individual drivers and plugins can support other capabilities, so refer to their documentation
|
||||
for lists ofspecific capability names. Some drivers may also not support all of these capabilities
|
||||
|
||||
| <div style="width:12em">Capability</div> | Type | Required? | Description |
|
||||
|--------------------------------------------|-----------|-----------|----------------------------|
|
||||
| `platformName` | `string` | yes | The type of platform hosting the app or browser |
|
||||
| `appium:automationName` | `string` | yes | The name of the Appium driver to use |
|
||||
| `browserName` | `string` | no | The name of the browser to launch and automate, if the driver supports web browsers as a special case |
|
||||
| `appium:app` | `string` | no | The path to an installable application |
|
||||
| `appium:deviceName` | `string` | no | The name of a particular device to automate, e.g., `iPhone 14` (currently only actually useful for specifying iOS simulators, since in other situations it's typically recommended to use a specific device id via the `appium:udid` capability). |
|
||||
| `appium:platformVersion` | `string` | no | The version of a platform, e.g., for iOS, `16.0` |
|
||||
| `appium:newCommandTimeout` | `number` | no | The number of seconds the Appium server should wait for clients to send commands before deciding that the client has gone away and the session should shut down |
|
||||
| `appium:noReset` | `boolean` | no | If true, instruct an Appium driver to avoid its usual reset logic during session start and cleanup (default `false`) |
|
||||
| `appium:fullReset` | `boolean` | no | If true, instruct an Appium driver to augment its usual reset logic with additional steps to ensure maximum environmental reproducibility (default `false`) |
|
||||
| `appium:eventTimings` | `boolean` | no | If true, instruct an Appium driver to collect [Event Timings](./event-timing.md) (default `false`) |
|
||||
| `appium:printPageSourceOnFindFailure` | `boolean` | no | If true, collect the page source and print it to the Appium log whenever a request to find an element fails (default `false`) |
|
||||
|
||||
|
||||
Some drivers place more complex constraints on capabilities as a group. For example, while the
|
||||
`appium:app` and `browserName` capabilities are listed above as optional, if you want to launch
|
||||
a session with a specific app, the XCUITest driver requires that at least one of `appium:app`,
|
||||
`browserName`, or `appium:bundleId` are included in the capabilities (otherwise it will not know
|
||||
what app to install and/or launch and will simply open a session on the home screen). Each driver
|
||||
will document how it interprets these capabilities and any other platform-specific requirements.
|
||||
|
||||
!!! note
|
||||
|
||||
Capabilities are like parameters used when starting a session. After the capabilities are sent
|
||||
and the session is started, they cannot be changed. If a driver supports updating aspects of
|
||||
its behaviour in the course of a session, it will provide a [Setting](./settings.md) for this
|
||||
purpose instead of, or in addition to, a capability.
|
||||
|
||||
Each Appium client has its own way of constructing capabilities and starting a session. For
|
||||
examples of doing this in each client library, head to the [Ecosystem](../ecosystem/index.md) page
|
||||
and click through to the appropriate client documentation.
|
||||
|
||||
## Using `appium:options` to Group Capabilities
|
||||
|
||||
If you use a lot of `appium:` capabilities in your tests, it can get a little repetitive. You can
|
||||
combine all capabilities as an object value of a single `appium:options` capability instead, in
|
||||
which case you don't need to use prefixes on the capabilities inside the object. For example:
|
||||
|
||||
```json
|
||||
{
|
||||
"platformName": "iOS",
|
||||
"appium:options": {
|
||||
"automationName": "XCUITest",
|
||||
"platformVersion": "16.0",
|
||||
"app": "/path/to/your.app",
|
||||
"deviceName": "iPhone 12",
|
||||
"noReset": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Note that constructing a capability value which is itself an object differs by language; refer to
|
||||
your client documentation for further examples on how to achieve this.
|
||||
|
||||
!!! warning
|
||||
|
||||
If you include the same capabilities both inside and outside of `appium:options`, the values
|
||||
inside of `appium:options` take precedence.
|
||||
|
||||
## Always-Match and First-Match Capabilities
|
||||
|
||||
The W3C spec allows clients to give the Appium server some flexibility in the kind of session it
|
||||
creates in response to a new session request. This is through the concept of "always-match" and
|
||||
"first-match" capabilities:
|
||||
|
||||
- Always-match capabilities consist of a single set of capabilities, every member of which must
|
||||
be satisfied by the server in order for the new session request to proceed.
|
||||
- First-match capabilities consist of an array of capability sets. Each set is merged with the
|
||||
always-match capabilities, and the first set that the server knows how to handle will be the set
|
||||
that is used to start the session.
|
||||
|
||||
!!! note
|
||||
|
||||
Check out the [spec itself](https://w3c.github.io/webdriver/#processing-capabilities) or
|
||||
a [summarized version](https://github.com/jlipps/simple-wd-spec#processing-capabilities) for
|
||||
a more in-depth description of how capabilities are processed.
|
||||
|
||||
In practice, use of first-match capabilities is not necessary or recommended for use with Appium.
|
||||
Instead, we recommend that you define the explicit set of capabilities you want the Appium
|
||||
server to handle. These will be encoded as the always-match capabilities, and the array of
|
||||
first-match capabilities will be empty.
|
||||
|
||||
That being said, Appium _does_ understand always-match and first-match capabilities as
|
||||
defined in the W3C spec, so if you use these features, Appium will work as expected. The process of
|
||||
defining always-match and first-match capabilities is unique to each client library, so refer to
|
||||
the documentation for your client library to see examples of how it works.
|
||||
|
||||
## Special Notes for Cloud Providers
|
||||
|
||||
!!! warning
|
||||
|
||||
This section is not intended for end-users of Appium; it is intended for developers building
|
||||
Appium-compatible cloud services.
|
||||
|
||||
When managing an Appium cloud, your users may wish to target various independent versions of Appium
|
||||
drivers and plugins. It is of course up to each service provider how they wish to implement the
|
||||
discovery, installation, and availability of any official or third party drivers or plugins. But
|
||||
the Appium team does provide several suggestions, for consistency across the industry. _These are
|
||||
recommendations only,_ and not a standard, but adopting it will help users to navigate the increased
|
||||
complexity that working with Appium 2 in a cloud environment may bring.
|
||||
|
||||
### Suggested capabilities
|
||||
|
||||
In addition to the standard `platformName`, `appium:deviceName`, `appium:automationName`, and
|
||||
`appium:platformVersion`, we recommend adopting the capability `$cloud:appiumOptions`, where the
|
||||
label `$cloud` is not meant to be interpreted literally but instead should be replaced by your
|
||||
vendor prefix (so for HeadSpin it would be `headspin`, Sauce Labs it would be `sauce`, and
|
||||
BrowserStack it would be `browserstack`, to name just a few examples). The `$cloud:appiumOptions`
|
||||
capability would itself be a JSON object, with the following internal keys:
|
||||
|
||||
| <div style="width:10em">Capability</div> | Usage | Example |
|
||||
| ---------------------------------------- | ------ | ------- |
|
||||
| `version` | The version of the Appium server that is used to host and manage drivers. If omitted, the behavior is left up to the provider, but the recommendation would be to provide the latest official version. | `2.0.0` |
|
||||
| `automationVersion` | The version of the driver (as specified by `appium:automationName`) that should be used. | `1.55.2` |
|
||||
| `automation` | The name of a custom driver to use (see below for more info). This would override `appium:automationName` and `$cloud:automationVersion`. | `{"name": "@org/custom-driver", "source": "github", "package": "custom-driver"}` |
|
||||
| `plugins` | The list of plugins (and potentially versions of plugins) that should be activated (see below for more info). | `["images", "universal-xml"]` |
|
||||
|
||||
### Basic example
|
||||
|
||||
Appium extensions (drivers and plugins) have a set of properties that specify where they can be
|
||||
installed from. Cloud providers are obviously under no obligation to provide support for
|
||||
arbitrarily specified extensions, seeing as these may represent untrusted code running in a managed
|
||||
environment. In the case where arbitrary extensions are not supported, the `appium:automationName`,
|
||||
`$cloud:automationVersion`, and `$cloud:appiumPlugins` capabilities should be sufficient. See the
|
||||
following JSON object representing capabilities for a session:
|
||||
|
||||
```json
|
||||
{
|
||||
"platformName": "iOS",
|
||||
"appium:platformVersion": "14.4",
|
||||
"appium:deviceName": "iPhone 11",
|
||||
"appium:app": "Some-App.app.zip",
|
||||
"appium:automationName": "XCUITest",
|
||||
"$cloud:appiumOptions": {
|
||||
"version": "2.0.0",
|
||||
"automationVersion": "3.52.0",
|
||||
"plugins": ["images"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This set of capabilities requests an Appium 2+ server supporting the XCUITest driver at version
|
||||
`3.52.0`, and the `images` plugin active. This set is easy for a cloud provider to verify. The
|
||||
cloud provider can obviously do anything it wants in response to these capabilities, including
|
||||
downloading Appium and driver and plugin packages on the fly, or erroring out if the versions
|
||||
requested are not in a supported set, or if the plugin is not supported, etc...
|
||||
|
||||
### Basic example with `appium:options`
|
||||
|
||||
The previous example still looks a bit disorganized, so of course we also recommend that cloud
|
||||
providers support the `appium:options` capability as detailed above, which could turn the previous
|
||||
set of capabilities into the following:
|
||||
|
||||
```json
|
||||
{
|
||||
"platformName": "iOS",
|
||||
"appium:options": {
|
||||
"platformVersion": "14.4",
|
||||
"deviceName": "iPhone 11",
|
||||
"app": "Some-App.app.zip",
|
||||
"automationName": "XCUITest"
|
||||
},
|
||||
"$cloud:appiumOptions": {
|
||||
"version": "2.0.0",
|
||||
"automationVersion": "3.52.0",
|
||||
"plugins": ["images"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Extension objects
|
||||
|
||||
Some service providers may wish to dynamically allow access to all of the features of the Appium
|
||||
2 CLI, including downloading arbitrary drivers and plugins. To represent these extensions, we can
|
||||
define special JSON "extension objects", with the following keys:
|
||||
|
||||
- `name`: the name of the extension. This would be an `npm` package name (if downloading from `npm`),
|
||||
or a `git` or GitHub spec (if downloading from a `git` server or GitHub).
|
||||
- `version`: the version of the extension, e.g., the `npm` package version or `git` SHA.
|
||||
- (optional) `source`: a denotation of where the extension can be downloaded from. It is recommended
|
||||
to support the following values: `appium`, `npm`, `git`, `github`. Here, `appium` means "Appium's
|
||||
own official list", and should be the default value if this key is not included.
|
||||
- (optional) `package`: when downloading extensions from `git` or GitHub, the `npm` package name of
|
||||
the extension must also be provided. This is optional for non-`git` sources.
|
||||
|
||||
Since each session is handled by a single driver, the `$cloud:appiumOptions`/`$automation`
|
||||
capability could be used with an extension object value to denote this driver, for example:
|
||||
|
||||
```json
|
||||
{
|
||||
"$cloud:appiumOptions": {
|
||||
"automation": {
|
||||
"name": "git+https://some-git-host.com/custom-driver-project.git",
|
||||
"version": "some-git-sha",
|
||||
"source": "git",
|
||||
"package": "driver-npm-package-name"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
And since sessions can handle multiple plugins, each value in the list of `$cloud:appiumPlugins`
|
||||
could also be an extension object rather than a string, so that specific versions could be
|
||||
requested:
|
||||
|
||||
```json
|
||||
{
|
||||
"$cloud:appiumOptions": {
|
||||
"plugins": [{
|
||||
"name": "images",
|
||||
"version": "1.1.0"
|
||||
}, {
|
||||
"name": "my-github-org/my-custom-plugin",
|
||||
"version": "a83f2e",
|
||||
"source": "github",
|
||||
"package": "custom-plugin"
|
||||
}]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
These serve as illustrative examples for the recommendations here. Of course, it is up to the
|
||||
service providers to implement the handling of these capabilities at their front end / load
|
||||
balancer, to perform any error checking, or to actually run any of the `appium driver` or `appium
|
||||
plugin` CLI commands that support the end user's request. This section is merely a suggestion as to
|
||||
how service providers might design their user-facing capabilities API in a way which in principle
|
||||
supports all of the capabilities that Appium itself would provide to the end user if they were
|
||||
running Appium on their own.
|
||||
|
||||
@@ -2,4 +2,97 @@
|
||||
title: The Appium Config File
|
||||
---
|
||||
|
||||
TODO
|
||||
Instead of passing arguments on the command line to Appium, you may add them to a special config
|
||||
file. Appium will read values from this config file when it runs. (Please note that CLI arguments
|
||||
have _precedence_ over configuration files; if a value is set in a config file _and_ via CLI
|
||||
argument, the CLI argument is preferred.)
|
||||
|
||||
## Supported Config File Formats
|
||||
|
||||
You can store your config data in the following kinds of files:
|
||||
|
||||
- JSON
|
||||
- YAML
|
||||
- JS (a JavaScript file exporting a JS object)
|
||||
- CJS (the same as above; the extension is for common JS)
|
||||
|
||||
!!! warning
|
||||
|
||||
Note: Configuration files in ESM format are not currently supported.
|
||||
|
||||
## Supported Config File Locations
|
||||
|
||||
Configuration files can be named anything, but the following filenames will be automatically
|
||||
discovered and loaded by Appium:
|
||||
|
||||
- `.appiumrc.json` (recommended)
|
||||
- `.appiumrc.yaml`
|
||||
- `.appiumrc.yml`
|
||||
- `.appiumrc.js`
|
||||
- `.appiumrc.cjs`
|
||||
- `appium.config.js`
|
||||
- `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`
|
||||
property in your `package.json` and it will be automatically discovered.
|
||||
|
||||
### Config File Search
|
||||
|
||||
Appium will search _up_ the directory tree from the current working directory for one of these
|
||||
files. If it reaches the current user's home directory or filesystem root, it will stop looking.
|
||||
|
||||
To specify a _custom_ location for your config file, use `appium --config-file /path/to/config/file`.
|
||||
|
||||
#### Configuration File Format
|
||||
|
||||
First, you might want to look at some examples:
|
||||
|
||||
- [Appium Configuration - JSON](https://github.com/appium/appium/blob/master/packages/appium/sample-code/appium.config.sample.json)
|
||||
- [Appium Configuration - YAML](https://github.com/appium/appium/blob/master/packages/appium/sample-code/appium.config.sample.yaml)
|
||||
- [Appium Configuration - JS](https://github.com/appium/appium/blob/master/packages/appium/sample-code/appium.config.sample.js)
|
||||
|
||||
A description of the format is available, as well:
|
||||
|
||||
- [Appium Configuration File JSON Schema](https://github.com/appium/appium/blob/master/packages/schema/lib/appium-config.schema.json)
|
||||
- [TypeScript declarations for Appium Configuration](https://github.com/appium/appium/blob/master/packages/types/lib/config.ts)
|
||||
|
||||
To describe in words, the config file will have a root `server` property, and all arguments are
|
||||
child properties. For certain properties which must be supplied on the command-line as
|
||||
comma-delimited lists, JSON strings, and/or external filepaths, these instead will be of their
|
||||
"native" type. For example, `--use-plugins <value>` needs `<value>` to be comma-delimited string
|
||||
or path to a delimited file. However, the config file just wants an array, e.g.,:
|
||||
|
||||
```json
|
||||
{
|
||||
"server": {
|
||||
"use-plugins": ["my-plugin", "some-other-plugin"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Configuring extensions (drivers and plugins)
|
||||
|
||||
For `driver`-and-`plugin`-specific configuration, these live under the `server.driver` and
|
||||
`server.plugin` properties, respectively. Each driver or plugin will have its own named property,
|
||||
and the values of any specific configuration it provides are under this. For example:
|
||||
|
||||
```json
|
||||
{
|
||||
"server": {
|
||||
"driver": {
|
||||
"xcuitest": {
|
||||
"webkit-debug-proxy-port": 5400
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
!!! note
|
||||
|
||||
The above configuration corresponds to the `--driver-xcuitest-webkit-debug-proxy-port` CLI argument.
|
||||
|
||||
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.
|
||||
|
||||
@@ -1,5 +1,44 @@
|
||||
---
|
||||
title: The Context API
|
||||
hide:
|
||||
- toc
|
||||
|
||||
title: Managing Contexts
|
||||
---
|
||||
|
||||
TODO
|
||||
A common feature of many app platforms is the ability for developers to embed web content inside of
|
||||
the platform-native app frame. This allows developers to leverage web technologies or existing web
|
||||
content for some or all of the app functionality. However, the additional complexity of mixing
|
||||
"modes" within a single application can make it difficult for automation tools that are designed to
|
||||
target the "native" elements and behaviours.
|
||||
|
||||
Appium provides a set of APIs for working with different app modes, called "contexts", that Appium
|
||||
drivers can implement if they support automation commands in these different modes. There are three
|
||||
basic commands that Appium has added to the W3C WebDriver spec for this purpose:
|
||||
|
||||
| Command Name | Method/Route | Params | Description | Returns |
|
||||
|-----------------------|-----------------------------|-------------------|-----------------------------------------------|-----------------|
|
||||
| `Get Contexts` | `GET /session/:id/contexts` | | Get a list of the available contexts | `array<string>` |
|
||||
| `Get Current Context` | `GET /session/:id/context` | | Get the name of the active context | `string` |
|
||||
| `Set Context` | `POST /session/:id/context` | `name` (`string`) | Switch into the context with the given `name` | `null` |
|
||||
|
||||
This API is flexible enough to handle a variety of semantic interpretations on the part of the
|
||||
driver. For example, the XCUITest driver includes two kinds of contexts: the native app context and
|
||||
any active webviews, as one context per webview. A call to `Get Contexts` will return the list of
|
||||
names, which you as a test author can sift through and use to switch into the appropriate context.
|
||||
As another example, the [Appium Altunity
|
||||
Plugin](https://github.com/headspinio/appium-altunity-plugin) introduces the concept of a `UNITY`
|
||||
context, which encapsulates all the plugin's specific behaviour to ensure that when outside of the
|
||||
`UNITY` context, the active driver's usual command implementations are used.
|
||||
|
||||
It is important to note that a call to `Get Contexts` will always contain at least one context,
|
||||
conventionally but not necessarily named `NATIVE_APP`. This is the default active context.
|
||||
|
||||
Depending on the type of context you're in, the operation of the driver might change. The XCUITest
|
||||
driver, when targeting a webview context, will not run its typical routines for finding and
|
||||
interacting with elements. Instead, it will run a different set of routines appropriate to web
|
||||
elements. This might have a variety of consequences, like supporting a different set of locator
|
||||
strategies.
|
||||
|
||||
The command names in the table above are generic references to the commands and not code examples.
|
||||
For examples of how to access the Context API in the language-specific client libraries, please
|
||||
visit the documentation for a given library.
|
||||
|
||||
@@ -1,5 +1,74 @@
|
||||
---
|
||||
title: The Event Timing API
|
||||
hide:
|
||||
- toc
|
||||
|
||||
title: Retrieving Event Timings
|
||||
---
|
||||
|
||||
TODO
|
||||
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).
|
||||
|
||||
With this capability turned on, the `GET /session/:id` response (i.e., the
|
||||
response to `driver.getSessionDetails()` or similar, depending on client) will
|
||||
be decorated with an `events` property. This is the structure of that `events`
|
||||
property:
|
||||
|
||||
```
|
||||
{
|
||||
"<event_type>": [<occurence_timestamp_1>, ...],
|
||||
"commands": [
|
||||
{
|
||||
"cmd": "<command_name>",
|
||||
"startTime": <js_timestamp>,
|
||||
"endTime": <js_timestamp>
|
||||
},
|
||||
...
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
In other words, the `events` property has 2 kinds of properties of its own:
|
||||
|
||||
* Properties which are the names of event types
|
||||
* The `commands` property
|
||||
|
||||
Properties which are names of event types correspond to an array of timestamps
|
||||
when that event happened. It's an array because events might happen multiple
|
||||
times in the course of a session. Examples of event types include:
|
||||
|
||||
* `newSessionRequested`
|
||||
* `newSessionStarted`
|
||||
|
||||
(Individual drivers will define their own event types, so we do not have an
|
||||
exhaustive list to share here. It's best to actually get one of these responses
|
||||
from a real session to inspect the possible event types.)
|
||||
|
||||
The `commands` property is an array of objects. Each object has the name of the
|
||||
Appium-internal command (for example `click`), as well as the time the command
|
||||
started processing and the time it finished processing.
|
||||
|
||||
With this data, you can calculate the time between events, or a strict timeline
|
||||
of events, or statistical information about average length of a certain type of
|
||||
command, and so on.
|
||||
|
||||
You can only receive data about events that have happened when you make the
|
||||
call to `/session/:id`, so the best time to get data about an entire session is
|
||||
right before quitting it.
|
||||
|
||||
The Appium team maintains an event timings parser tool that can be used to
|
||||
generate various kinds of reports from event timings output:
|
||||
[appium/appium-event-parser](https://github.com/appium/appium-event-parser).
|
||||
|
||||
## Add a custom event
|
||||
|
||||
!!! warning "TODO"
|
||||
|
||||
The links to the commands in the following paragraph do not yet work since these docs are under
|
||||
construction.
|
||||
|
||||
You can add custom events that will show up in the event timings data. You can send a custom event
|
||||
name to the Appium server using the [Log Event API](#TODO), and the server will store the
|
||||
timestamp. The [Get Events](#TODO) command can be used to retrieve named events' timestamps later
|
||||
on.
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
---
|
||||
hide:
|
||||
- toc
|
||||
|
||||
title: Execute Methods
|
||||
---
|
||||
|
||||
Because the scope of commands implemented in Appium drivers is broader than the scope of commands
|
||||
defined by the W3C WebDriver spec, Appium needs a way for these "extended" commands to be accessible
|
||||
by client libraries. There are two main strategies for this:
|
||||
|
||||
1. Appium drivers define new W3C-compatible API routes, and Appium clients are updated to include
|
||||
support for those new routes.
|
||||
2. Appium drivers define so-called "Execute Methods" which provide new functionality by
|
||||
overloading the existing `Execute Script` command which is already available in any WebDriver-
|
||||
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.
|
||||
|
||||
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.
|
||||
Here's an example of how the `Execute Script` command is designed to work in the world of WebDriver
|
||||
and browser automation:
|
||||
|
||||
=== "JS (WebDriverIO)"
|
||||
|
||||
```js
|
||||
await driver.executeScript('return arguments[0] + arguments[1]', [3, 4])
|
||||
```
|
||||
|
||||
=== "Java"
|
||||
|
||||
```java
|
||||
JavascriptExecutor jsDriver = (JavascriptExecutor) driver;
|
||||
jsDriver.executeScript("return arguments[0] + arguments[1]", 3, 4);
|
||||
```
|
||||
|
||||
=== "Python"
|
||||
|
||||
```py
|
||||
driver.execute_script('return arguments[0] + arguments[1]', 3, 4)
|
||||
```
|
||||
|
||||
=== "Ruby"
|
||||
|
||||
```rb
|
||||
driver.execute_script 'return arguments[0] + arguments[1]', 3, 4
|
||||
```
|
||||
|
||||
=== "C#"
|
||||
|
||||
```dotnet
|
||||
((IJavaScriptExecutor)driver).ExecuteScript("return arguments[0] + arguments[1]", 3, 4);
|
||||
```
|
||||
|
||||
What's happening here is that we are defining a snippet of Javascript (technically,
|
||||
a function body) to be executed within the web browser. The client can send arguments which are
|
||||
serialized, sent over HTTP, and finally provided to the function as parameters. In this example,
|
||||
we are essentially defining an addition function. The return value of the `Execute Script` command
|
||||
is whatever the return value of the Javascript snippet is! In the case of this example, that value
|
||||
would be the number `7` (`3` + `4`).
|
||||
|
||||
Each client library has its own way of calling the command and providing any arguments to the script
|
||||
function, but the function itself—the snippet—is always a string and is the same across all languages.
|
||||
|
||||
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
|
||||
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_
|
||||
as defined by the driver. The only other thing a client needs to know is the set of
|
||||
parameters for the method, which are documented by the driver. In this case, we have a parameter
|
||||
named `bundleId`, whose value should be a string encoding the ID of the app to terminate. Here is
|
||||
how this Execute Method would be called:
|
||||
|
||||
=== "JS (WebDriverIO)"
|
||||
|
||||
```js
|
||||
await driver.executeScript('mobile: terminateApp', [{bundleId: 'com.my.app'}])
|
||||
```
|
||||
|
||||
=== "Java"
|
||||
|
||||
```java
|
||||
JavascriptExecutor jsDriver = (JavascriptExecutor) driver;
|
||||
jsDriver.executeScript("mobile: terminateApp", ImmutableMap.of("bundleId", "com.my.app"));
|
||||
```
|
||||
|
||||
=== "Python"
|
||||
|
||||
```py
|
||||
driver.execute_script('mobile: terminateApp', {'bundleId': 'com.my.app'})
|
||||
```
|
||||
|
||||
=== "Ruby"
|
||||
|
||||
```rb
|
||||
driver.execute_script 'mobile: terminateApp', { bundleId: 'com.my.app' }
|
||||
```
|
||||
|
||||
=== "C#"
|
||||
|
||||
```dotnet
|
||||
((IJavaScriptExecutor)driver).ExecuteScript("mobile: terminateApp",
|
||||
new Dictionary<string, string> { { "bundleId", "com.my.app" } });
|
||||
|
||||
```
|
||||
|
||||
There are two important differences in using Appium Execute Methods from vanilla Selenium
|
||||
Javascript execution:
|
||||
|
||||
1. The script string is just a command name; it will be provided by the driver documentation
|
||||
1. The standard way to provide parameters is as a *single* object with keys representing parameter
|
||||
names and values representing parameter values. So in this case, we had to specify both the
|
||||
parameter name (`bundleId`) as the key of the parameters object, and the parameter value
|
||||
(`com.my.app`) as the value for that key. A driver can define parameters as _required_ or _optional_.
|
||||
|
||||
Of course, always refer to the documentation for the particular Execute Method in case the author
|
||||
has made any alterations to the standard access method.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
title: Execute Script Overloads
|
||||
---
|
||||
|
||||
TODO
|
||||
@@ -2,4 +2,165 @@
|
||||
title: Appium and Selenium Grid
|
||||
---
|
||||
|
||||
TODO
|
||||
## Using Selenium Grid 4+
|
||||
|
||||
The
|
||||
[relay](https://www.selenium.dev/documentation/grid/configuration/toml_options/#relaying-commands-to-a-service-endpoint-that-supports-webdriver)
|
||||
feature in Grid 4 allows you to proxy Appium requests to an Appium server instance. Here is
|
||||
an example walkthrough of how you would connect two different Appium instances to a Selenium Grid.
|
||||
|
||||
### Define the Appium configs
|
||||
|
||||
Each Appium instance should have a config file that can be easily updated. It should contain any
|
||||
information which needs to be unique to that particular server (e.g., ports its drivers should use
|
||||
that others should not). We are going to have 2 Appium servers, so we will need 2 config files:
|
||||
|
||||
```yaml
|
||||
# appium1.yml
|
||||
server:
|
||||
port: 4723
|
||||
use-drivers:
|
||||
- xcuitest
|
||||
default-capabilities:
|
||||
wdaLocalPort: 8100
|
||||
mjpegServerPort: 9100
|
||||
mjpegScreenshotUrl: "http://localhost:9100"
|
||||
```
|
||||
|
||||
In the above YAML config file, we specify the Appium server port, the driver used, and parameters
|
||||
for the driver that will be sent in as default capabilities. Our goal is to ensure that any other
|
||||
drivers running on this host will not compete with system ports or other resources. The second
|
||||
config file could look like the following, where we simply adjust a few ports to prevent clashes:
|
||||
|
||||
```yaml
|
||||
# appium2.yml
|
||||
server:
|
||||
port: 4733
|
||||
use-drivers:
|
||||
- xcuitest
|
||||
default-capabilities:
|
||||
wdaLocalPort: 8110
|
||||
mjpegServerPort: 9110
|
||||
mjpegScreenshotUrl: "http://localhost:9110"
|
||||
```
|
||||
|
||||
### Define the Grid node configs
|
||||
|
||||
We will be launching one Grid "node" per Appium server, to manage relaying commands and determining
|
||||
capacity and online status, etc... So we should have one config file per Grid node as well. Each
|
||||
node config should include the address of the Appium server it will target, as well as a list of
|
||||
capability "configs" it should accept to relay a session request to Appium. Here is what the config
|
||||
could look like for the two nodes:
|
||||
|
||||
```toml
|
||||
# node1.toml
|
||||
[server]
|
||||
port = 5555
|
||||
|
||||
[node]
|
||||
detect-drivers = false
|
||||
|
||||
[relay]
|
||||
url = "http://localhost:4723"
|
||||
status-endpoint = "/status"
|
||||
configs = [
|
||||
"1", "{\"platformName\": \"iOS\", \"appium:platformVersion\": \"15.5\", \"appium:deviceName\": \"iPhone 13\", \"appium:automationName\": \"XCUITest\"}"
|
||||
]
|
||||
```
|
||||
|
||||
```toml
|
||||
# node2.toml
|
||||
[server]
|
||||
port = 5565
|
||||
|
||||
[node]
|
||||
detect-drivers = false
|
||||
|
||||
[relay]
|
||||
url = "http://localhost:4733"
|
||||
status-endpoint = "/status"
|
||||
configs = [
|
||||
"1", "{\"platformName\": \"iOS\", \"appium:platformVersion\": \"15.5\", \"appium:deviceName\": \"iPhone 12\", \"appium:automationName\": \"XCUITest\"}"
|
||||
]
|
||||
```
|
||||
|
||||
Note that each node config also specifies a different port itself for the node to run on.
|
||||
|
||||
### Putting it together
|
||||
|
||||
The Grid nodes aren't enough--you'll also want a Grid "hub" that acts as a load balancer and
|
||||
manager for all the nodes. So in the end we'll have 5 processes running at once: 2 Appium servers,
|
||||
2 Grid nodes, and 1 Grid hub. It's best to run each of these in a separate terminal window to avoid
|
||||
confusion of logs. Here is how you'd start each process:
|
||||
|
||||
0. `appium --config appium1.yml`
|
||||
0. `appium --config appium2.yml`
|
||||
0. `java -jar /path/to/selenium.jar node --config node1.toml`
|
||||
0. `java -jar /path/to/selenium.jar node --config node2.toml`
|
||||
0. `java -jar /path/to/selenium.jar hub`
|
||||
|
||||
Once you wait a few moments for the nodes to detect their Appium servers, and to register with the
|
||||
hub, you'll be able to send all your Appium traffic via the single hub endpoint (defaulting to
|
||||
`http://localhost:4444`).
|
||||
|
||||
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.
|
||||
|
||||
@@ -1,5 +1,86 @@
|
||||
---
|
||||
title: Log Filtering
|
||||
title: Filtering the Appium Log
|
||||
---
|
||||
|
||||
TODO
|
||||
Sometimes it might be necessary to hide sensitive information, like passwords, device identifiers,
|
||||
hashes, etc..., from the server log. Appium makes it possible to ensure such information is
|
||||
redacted in logs via the `--log-filters` command line argument. This argument allows you to provide
|
||||
the path to a special config file, containing one or more log obfuscation rules.
|
||||
|
||||
## Config Format
|
||||
|
||||
The filtering config must be one of:
|
||||
|
||||
- a path to a valid JSON file containing an array of filtering rules
|
||||
- a `log-filters` entry in an [Appium Config](./config.md) file, with the rules array inline
|
||||
|
||||
Each rule is an object with a set of predefined properties. The following rule properties are
|
||||
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.
|
||||
- `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.
|
||||
- `replacer`: The replacer value to use. By default it is `**SECURE**`. Empty values are allowed.
|
||||
|
||||
### Config Examples
|
||||
|
||||
Replace all occurrences of `my.magic.app` string with the default replacer:
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"text": "my.magic.app"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
Replace all occurrences of `my.magic.<any char>` string with a custom replacer (case insensitive):
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"pattern": "my\\.magic\\.\\w",
|
||||
"flags": "i",
|
||||
"replacer": "***"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
Replace all occurrences of `my.magic.<any chars>` and/or `your.magic` strings with a custom
|
||||
replacer (case insensitive):
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"pattern": "my\\.magic\\.\\w+",
|
||||
"flags": "i",
|
||||
"replacer": "***"
|
||||
},
|
||||
{
|
||||
"pattern": "your\\.magic",
|
||||
"flags": "i",
|
||||
"replacer": "***"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
Truncate all log lines to max 15 chars (advanced):
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"pattern": "(.{1,15}).*",
|
||||
"flags": "s",
|
||||
"replacer": "$1"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
### Config Errors Handling
|
||||
|
||||
If any of the config rules contains invalid items (such as empty/invalid pattern, empty rule, etc.)
|
||||
then Appium will print the detailed report about collected errors and will fail to start until
|
||||
these errors are addressed.
|
||||
|
||||
@@ -1,5 +1,87 @@
|
||||
---
|
||||
title: Managing Appium Drivers and Plugins
|
||||
title: Managing Drivers and Plugins
|
||||
---
|
||||
|
||||
TODO
|
||||
To do anything useful with Appium, you need to have at least one [Driver](../intro/drivers.md)
|
||||
installed, otherwise Appium won't know how to automate anything. There is an entire
|
||||
[Ecosystem](../ecosystem/index.md) of drivers and plugins out there!
|
||||
|
||||
This guide helps explain how to manage these drivers and plugins. There are
|
||||
two basic strategies: using Appium's extension CLI interface, or managing extensions yourself in an
|
||||
`npm`-based project.
|
||||
|
||||
!!! note
|
||||
|
||||
Other package managers are not currently supported.
|
||||
|
||||
## Using Appium's Extension CLI
|
||||
|
||||
With Appium's [Extension CLI](../cli/extensions.md), you let Appium manage drivers and plugins for
|
||||
you. You will use CLI commands to tell Appium which extensions you would like to install, update,
|
||||
or remove. Here's an example of how you might install a driver using the CLI:
|
||||
|
||||
```bash
|
||||
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
|
||||
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?
|
||||
Appium manages extensions in a directory specified by the `APPIUM_HOME` environment variable. You
|
||||
can set that variable to anything you like, and Appium will manage its extensions there. You can
|
||||
therefore also use the `APPIUM_HOME` environment variable to manage different sets of extensions,
|
||||
for example if you want to have the same driver installed at conflicting versions:
|
||||
|
||||
```bash
|
||||
APPIUM_HOME=/path/to/home1 appium driver install xcuitest@4.11.1
|
||||
APPIUM_HOME=/path/to/home2 appium driver install xcuitest@4.11.2
|
||||
```
|
||||
|
||||
Running these commands will result in two separate `APPIUM_HOME` directories being created and
|
||||
populated with the corresponding version of the XCUITest driver. You can then use the same
|
||||
environment variables to direct Appium which version to use on launch:
|
||||
|
||||
```bash
|
||||
APPIUM_HOME=/path/to/home1 appium # use xcuitest driver 4.11.1
|
||||
APPIUM_HOME=/path/to/home2 appium # use xcuitest driver 4.11.2
|
||||
```
|
||||
|
||||
You don't need to set `APPIUM_HOME` if you don't want to! By default, Appium will set `APPIUM_HOME`
|
||||
to the directory `.appium` in your user home directory.
|
||||
|
||||
These installed packages will be managed by `extensions.yaml` in `$APPIUM_HOME/node_modules/.cache/appium/extensions.yaml`.
|
||||
|
||||
## Do-It-Yourself with `npm`
|
||||
|
||||
Because Appium and Appium drivers are Node.js programs, if you are integrating your Appium scripts
|
||||
into your own Node.js project, there is an alternative way to manage drivers and plugins: via `npm`,
|
||||
like any other dependency. Basically, whenever you run Appium, if you have not explicitly set
|
||||
`APPIUM_HOME`, it will:
|
||||
|
||||
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
|
||||
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
|
||||
dev dependencies. For example, if your project has a `package.json` which includes the following:
|
||||
|
||||
```json
|
||||
{
|
||||
"devDependencies": {
|
||||
"appium": "^2.0.0",
|
||||
"appium-xcuitest-driver": "^4.11.1"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Then, if you run `npx appium` inside your project, Appium will detect that it is a dependency of
|
||||
the project, and will load the XCUITest driver which is also listed as a dev dependency for the
|
||||
project.
|
||||
|
||||
This strategy is *only* recommended if you are already using `npm` for your project.
|
||||
Otherwise, it is recommended that you use Appium's Extension CLI and, if necessary, adjust
|
||||
`APPIUM_HOME` to change the location of stored extensions.
|
||||
|
||||
@@ -1,198 +1,208 @@
|
||||
---
|
||||
title: 自 Appium 1.x 迁移到 Appium 2.x
|
||||
title: Migrating to Appium 2
|
||||
---
|
||||
本文档是针对使用 Appium 1.x 并希望迁移到 Appium 2.x 的用户的指南.
|
||||
其包含重大更改的列表, 以及如何迁移您的环境或测试套件以确保与Appium 2.0的兼容性.
|
||||
|
||||
## Appium 2.0 概述
|
||||
This document is a guide for those who are using Appium 1 and wish to migrate to Appium 2. It
|
||||
contains a list of breaking changes and how to migrate your environments or test suites to ensure
|
||||
compatibility with Appium 2.
|
||||
|
||||
Appium 2.0 是Appium近5年来最重要的新版本.
|
||||
Appium 2.0 中的更改主要与特定平台的自动化行为更改 _无_ 关.
|
||||
相反, Appium 2.0将Appium重新定义为一个 _平台_,
|
||||
其可以轻松创建和共享"驱动程序" (引入对给定平台自动化的支持的代码项目)
|
||||
和"插件" (允许覆盖, 更改, 扩展或向Appium添加行为的代码项目) .
|
||||
## Overview of Appium 2
|
||||
|
||||
与此同时, Appium项目正在借此机会删除许多陈旧以及作废的功能.
|
||||
Appium 2 is the most major new release of Appium in over 5 years. The changes in Appium 2 are _not_
|
||||
primarily related to changes in automation behaviors for specific platforms. Instead, Appium 2
|
||||
reenvisions Appium as a _platform_ where "drivers" (code projects that introduce support for
|
||||
automation of a given platform) and "plugins" (code projects that allow for overriding, altering,
|
||||
extending, or adding behaviors to Appium) can be easily created and shared.
|
||||
|
||||
借此机会介绍一些重大变更, 包括如何安装Appium、
|
||||
如何管理驱动程序和各种功能、以及协议支持等.
|
||||
以下为详细介绍.
|
||||
At the same time, the Appium project is taking the opportunity to remove many old and deprecated
|
||||
bits of functionality.
|
||||
|
||||
## 重大变更
|
||||
Together these do introduce a few breaking changes to how Appium is installed, how drivers and
|
||||
various features are managed, and protocol support. These are detailed below.
|
||||
|
||||
查看 [Appium 2.0 发行说明](https://github.com/appium/appium/releases) 以获取最全面的变更列表.
|
||||
在此, 我们指出重大变更以及您需要为它们执行的操作.
|
||||
## Breaking Changes
|
||||
|
||||
### :bangbang: 在安装过程中安装驱动程序
|
||||
### :bangbang: Default server base path
|
||||
|
||||
当您安装Appium 1.x时, 所有可用的驱动程序将与主Appium Server同时安装.
|
||||
现在情况已不再如此. 简单地安装Appium 2.0 (例如, 通过 `npm install -g appium` ) ,
|
||||
将只安装Appium Server, 但没有驱动程序.
|
||||
要安装驱动程序, 您必须改用新的 [Appium 扩展 CLI](../cli/extensions.md).
|
||||
例如, 为了获取最新版本的XCUITest和UiAutomator2驱动程序,
|
||||
您可以在安装Appium后, 运行以下命令:
|
||||
With Appium 1, the server would accept commands by default on `http://localhost:4723/wd/hub`. The
|
||||
`/wd/hub` base path was a legacy convention from the days of migrating from Selenium 1 to Selenium
|
||||
2, and is no longer relevant. As such the default base path for the server is now `/`. If you want
|
||||
to retain the old behaviour, you can set the base path via a command line argument as follows:
|
||||
|
||||
```
|
||||
appium driver install xcuitest
|
||||
appium driver install uiautomator2
|
||||
appium --base-path=/wd/hub
|
||||
```
|
||||
|
||||
至此, 您的驱动程序已安装并准备就绪.
|
||||
您可以使用此CLI做更多的事情, 因此请务必查看其文档.
|
||||
如果您在持续集成环境中运行, 或者想要在一步到位地安装 Appium 和一些驱动程序,
|
||||
则可以在安装过程中使用一些特殊标志来执行此操作, 例如:
|
||||
You can also set server arguments as [Config file](./config.md) properties.
|
||||
|
||||
```
|
||||
npm install --global appium --drivers=xcuitest,uiautomator2
|
||||
### :bangbang: Installing drivers during setup
|
||||
|
||||
When you installed Appium 1, all available drivers would be installed at the same time as the main
|
||||
Appium server. This is no longer the case. Simply installing Appium 2 (e.g., by `npm i -g appium`),
|
||||
will install the Appium server only, but no drivers. To install drivers, you must instead use the
|
||||
new [Appium extension CLI](../cli/extensions.md). For example, to install the latest versions of the
|
||||
XCUITest and UiAutomator2 drivers, after installing Appium you would run the following commands:
|
||||
|
||||
```bash
|
||||
appium driver install uiautomator2 # installs the latest driver version
|
||||
appium driver install xcuitest@4.12.2 # installs a specific driver version
|
||||
```
|
||||
|
||||
这将一次性为您安装Appium和两种驱动程序.
|
||||
At this point, your drivers are installed and ready. There's a lot more you can do with the Appium 2
|
||||
command line, so be sure to check out [its documentation](../cli/index.md). If you're running in a CI
|
||||
environment or want to install Appium along with some drivers all in one step, you can do so using
|
||||
some special flags during install, for example:
|
||||
|
||||
### :bangbang: 驱动安装路径
|
||||
```bash
|
||||
npm i -g appium --drivers=xcuitest,uiautomator2
|
||||
```
|
||||
|
||||
当您安装 Appium 1.x 时,
|
||||
所有可用的驱动程序将与主 Appium 服务器同时安装.
|
||||
路径是 `/path/to/appium/node_modules`.
|
||||
例如, 手动构建WebDriverAgent的`appium-webdriveragent`
|
||||
是 `/path/to/appium/node_modules/appium-xcuitest-driver/node_modules/appium-webdriveragent`.
|
||||
This will install Appium and the two drivers for you in one go. Please uninstall any existing Appium 1
|
||||
`npm` packages (with `npm uninstall -g appium`) if you get an installation or startup error.
|
||||
|
||||
Appium 2.0 在 `APPIUM_HOME` 环境变量中安装此类依赖项.
|
||||
默认路径为 `~/.appium`.
|
||||
因此,安装 XCUITest 驱动程序包后,
|
||||
`appium-webdriveragent` 的路径可能是 `$APPIUM_HOME/node_modules/appium-xcuitest-driver/node_modules/appium-webdriveragent` .
|
||||
### :bangbang: Drivers installation path
|
||||
|
||||
### :bangbang: Chromedriver安装标志
|
||||
When you installed Appium 1, all available drivers would be installed at the same time as the main
|
||||
Appium server, at `/path/to/appium/node_modules`. For example, `appium-webdriveragent` was located
|
||||
at `/path/to/appium/node_modules/appium-xcuitest-driver/node_modules/appium-webdriveragent`.
|
||||
|
||||
在Appium 1.x中, 可以使用以下命令行标志自定义Chromedriver的安装方式
|
||||
(例如作为UiAutomator2驱动程序的一部分) :
|
||||
Appium 2 installs such dependencies in the path defined by the `APPIUM_HOME` environment variable.
|
||||
The default path is `~/.appium`. So, `appium-webdriveragent` would now be located at
|
||||
`$APPIUM_HOME/node_modules/appium-xcuitest-driver/node_modules/appium-webdriveragent`.
|
||||
|
||||
### :bangbang: Chromedriver installation flags
|
||||
|
||||
In Appium 1, it was possible to customize the way Chromedriver was installed (as part of the
|
||||
UiAutomator2 driver for example), using the following command line flags:
|
||||
|
||||
* `--chromedriver-skip-install`
|
||||
* `--chromedriver-version`
|
||||
* `--chromedriver-cdnurl`
|
||||
|
||||
由于现在通过 Appium 2.0 为您安装驱动程序, 并且这些标志是作为 NPM 配置标志实现的,
|
||||
因此它们将不再有效. 相反地, 请在驱动程序安装过程中使用以下环境变量:
|
||||
|
||||
Because Appium 2 now installs drivers for you, and because these flags were implemented as `npm`
|
||||
config flags, they will no longer work. Instead, use the following environment variables during
|
||||
driver installation:
|
||||
|
||||
* `APPIUM_SKIP_CHROMEDRIVER_INSTALL`
|
||||
* `CHROMEDRIVER_VERSION`
|
||||
* `CHROMEDRIVER_CDNURL`
|
||||
|
||||
例如:
|
||||
For example:
|
||||
|
||||
```
|
||||
```bash
|
||||
APPIUM_SKIP_CHROMEDRIVER_INSTALL=1 appium driver install uiautomator2
|
||||
```
|
||||
|
||||
### :bangbang: 特定于驱动程序的命令行选项
|
||||
### :bangbang: Driver-specific command line options
|
||||
|
||||
在 Appium 1.x 中, 针对特定驱动程序的命令行选项都托管在主 Appium 服务器上.
|
||||
因此, 您可以与Appium一起使用诸如
|
||||
'--chromedriver-executable' 这样的CLI参数,
|
||||
来设置特定Chromedriver版本的位置,
|
||||
UiAutomator2同理.
|
||||
With Appium 1, command-line options specific to particular drivers were all hosted on the main
|
||||
Appium server. So, for example, `--chromedriver-executable` was a CLI parameter you could use with
|
||||
Appium to set the location of a specific Chromedriver version for use with, say, the UiAutomator2 driver.
|
||||
|
||||
With Appium 2, all driver- and platform-specific CLI params have been moved to the drivers themselves.
|
||||
To access the corresponding functionality, you'll need to refer to the driver/plugin documentation.
|
||||
In some cases, the extension will continue to expose CLI parameters. For example, the XCUITest driver
|
||||
used to expose a parameter `--webdriveragent-port`. Now, to access this parameter, it should be
|
||||
prefixed with `driver-xcuitest`, to differentiate it from parameters other drivers might also expose.
|
||||
To use this parameter, you thus need to start Appium with something like:
|
||||
|
||||
在 Appium 2.x 中,
|
||||
所有特定于驱动程序和平台的 CLI 参数都已移至驱动程序本身.
|
||||
要访问它们, 您现在需要在参数前面附加扩展类型 ( `driver` 或 `plugin`) 以及扩展名的名称.
|
||||
例如, `--chromedriver-executable`变为`--driver-uiautomator2-chromedriver-executable`.
|
||||
```bash
|
||||
appium --driver-xcuitest-webdriveragent-port=5000
|
||||
```
|
||||
|
||||
Some drivers have done away with CLI args entirely in favour of default capabilities. With the
|
||||
above-mentioned `--chromedriver-executable` parameter for example, you now need to take advantage
|
||||
of the `appium:chromedriverExecutable` capability supported by the UiAutomator2 driver. To set this
|
||||
capability from the command line, do the following:
|
||||
|
||||
### :bangbang: 特定于驱动程序的自动化命令
|
||||
```bash
|
||||
appium --default-capabilities '{"appium:chromedriverExecutable": "/path/to/chromedriver"}'
|
||||
```
|
||||
|
||||
仅与特定驱动程序相关的某些命令的定义已移至这些驱动程序内实现.
|
||||
例如, `pressKeyCode` 特定作用于 UiAutomator2 驱动程序,
|
||||
并且现在只有该驱动程序才能识别.
|
||||
在实践中, 这里唯一的重大变更是未安装驱动程序的那种错误.
|
||||
之前, 如果使用未实现命令的驱动程序, 则会收到 `501 Not Yet Implemented` 错误.
|
||||
现在, 您将获得`404 Not Found`错误,
|
||||
因为如果不知道该命令的驱动程序是否有效,
|
||||
主 Appium Server将不会定义与命令对应的路由.
|
||||
### :bangbang: Driver-specific automation commands
|
||||
|
||||
The definition of certain commands that pertain only to specific drivers has been moved to those
|
||||
drivers' implementations. For example, `pressKeyCode` is specific to the UiAutomator2 driver and is
|
||||
now understood only by that driver. In practice, the only breaking change here is the kind of error
|
||||
you would encounter if the appropriate driver is not installed. Previously, you would get a `501
|
||||
Not Yet Implemented` error if using a driver that didn't implement the command. Now, you will get
|
||||
a `404 Not Found` error because if a driver that doesn't know about the command is not active, the
|
||||
main Appium server will not define the route corresponding to the command.
|
||||
|
||||
### :bangbang: 驱动程序更新
|
||||
|
||||
在过去, 要获取iOS或Android驱动程序的更新,
|
||||
您只需等待新版本的Appium包含这些升级,
|
||||
然后更新您的Appium版本即可.
|
||||
在 Appium 2.x 中, Appium Server和 Appium 驱动程序是分开进行版本控制和发布的.
|
||||
这意味着驱动程序可以按照自己的节奏进行发布,
|
||||
并且可以在发生驱动程序更新时获得驱动程序更新,
|
||||
而不是等待新的Appium Server发布.
|
||||
检查驱动程序更新的方法是使用 CLI:
|
||||
### :bangbang: Driver updates
|
||||
|
||||
In the past, to get updates to your iOS or Android drivers, you'd simply wait for those updates to
|
||||
be rolled into a new release of Appium, and then update your Appium version. With Appium 2, the
|
||||
Appium server and the Appium drivers are versioned and released separately. This means that drivers
|
||||
can be on their own release cadence, and you can get the latest driver version right away, rather
|
||||
than waiting for a new Appium server release. The way to check for driver updates is with the CLI:
|
||||
|
||||
```bash
|
||||
appium driver list --updates
|
||||
```
|
||||
|
||||
如果有任何更新可用,
|
||||
则可以针对任何给定的驱动程序运行 `update` 命令:
|
||||
|
||||
If any updates are available, you can then run the `update` command for any given driver:
|
||||
|
||||
```bash
|
||||
appium driver update xcuitest
|
||||
```
|
||||
|
||||
要更新Appium Server本身, 您需要执行与过去一致的操作: `npm install -g appium`.
|
||||
现在, 安装新版本的Appium Server将无关于其他驱动程序, 因此整个过程将更加快速.
|
||||
(For a complete description of the update command, check out the
|
||||
[Extension CLI](../cli/extensions.md#update) documentation)
|
||||
|
||||
To update the Appium server itself, you do the same thing as in the past: `npm install -g appium`.
|
||||
Now, installing new versions of the Appium server will leave your drivers intact, so the whole
|
||||
process will be much more quick.
|
||||
|
||||
### :bangbang: 协议变更
|
||||
If you would like to update to a specific driver version, not the latest, please uninstall the driver
|
||||
and install the desired version using the `install` subcommand instead of `update`.
|
||||
|
||||
Appium的API基于[W3C WebDriver协议](https://www.w3.org/TR/webdriver/),
|
||||
并且已经支持该协议多年.
|
||||
在W3C WebDriver协议被设计为Web标准之前,
|
||||
还有其他几种协议用于Selenium和Appium.
|
||||
这些协议是 "JSONWP" (JSON Wire Protocol) 以及 "MJSONWP" (Mobile JSON Wire Protocol).
|
||||
W3C协议与(M)JSONWP协议在某些方面略有差异.
|
||||
```bash
|
||||
appium driver uninstall xcuitest
|
||||
appium driver install xcuitest@4.11.1
|
||||
```
|
||||
|
||||
### :bangbang: Protocol changes
|
||||
|
||||
在Appium 2.0之前, Appium支持这两种协议,
|
||||
因此之前的Selenium / Appium客户端仍然可以与较新的Appium服务器进行通信.
|
||||
长期来看, Appium将删除对遗留协议的支持.
|
||||
Appium's API is based on the [W3C WebDriver Protocol](https://www.w3.org/TR/webdriver/), and it has
|
||||
supported this protocol for years. Before the W3C WebDriver Protocol was designed as a web standard,
|
||||
several other protocols were used for both Selenium and Appium. These protocols were the "JSONWP"
|
||||
(JSON Wire Protocol) and "MJSONWP" (Mobile JSON Wire Protocol). The W3C Protocol differs from the
|
||||
(M)JSONWP protocols in a few small ways.
|
||||
|
||||
Up until Appium 2, Appium supported both protocols, so that older Selenium/Appium clients could
|
||||
still communicate with newer Appium servers. Appium 2 removes support for older protocols and is
|
||||
now only compatible with the W3C WebDriver Protocol.
|
||||
|
||||
### :bangbang: _Capabilities_
|
||||
### :bangbang: Capabilities must use vendor prefix
|
||||
|
||||
新旧协议之间的一个显着差异在于capabilities的格式.
|
||||
以前称为"desired capabilities", 现在简称为"capabilities",
|
||||
现在要求在任何非标准capabilities上使用所谓的"供应商前缀".
|
||||
标准capabilities列表在 [WebDriver 协议规范](https://www.w3.org/TR/webdriver/#capabilities)中给出,
|
||||
其中包括一些常用capabilities, 如`browserName` 以及 `platformName`.
|
||||
|
||||
这些标准capabilities继续按原样使用.
|
||||
所有其他capabilities的名称中必须包含"供应商前缀".
|
||||
供应商前缀是后跟冒号的字符串, 例如 `appium:`.
|
||||
Appium 的大多数capabilities都超出了标准的 W3C capabilities,
|
||||
因此必须包含供应商前缀 (我们建议您使用 `appium:` , 除非文档另有说明) . 例如:
|
||||
One significant difference between old and new protocols is in the format of capabilities.
|
||||
Previously called "desired capabilities", and now called simply "capabilities", there is now a
|
||||
requirement for a so-called "vendor prefix" on any non-standard capabilities. The list of standard
|
||||
capabilities is given in the [WebDriver Protocol spec](https://www.w3.org/TR/webdriver/#capabilities),
|
||||
and includes a few commonly used capabilities such as `browserName` and `platformName`.
|
||||
|
||||
These standard capabilities continue to be used as-is. All other capabilities must include a
|
||||
"vendor prefix" in their name. A vendor prefix is a string followed by a colon, such as `appium:`.
|
||||
Most of Appium's capabilities go beyond the standard W3C capabilities and must therefore include
|
||||
vendor prefixes (we recommend that you use `appium:` unless directed otherwise by documentation).
|
||||
For example:
|
||||
|
||||
- `appium:app`
|
||||
- `appium:noReset`
|
||||
- `appium:deviceName`
|
||||
|
||||
面向 Appium 2.0 时,
|
||||
此要求对于您的测试套件来说, 可能是也可能不是重大更改.
|
||||
如果您使用的是更新过的Appium客户端,
|
||||
客户端将在所有必要的功能上为您添加 `appium:` 前缀.
|
||||
新版本的Appium Inspector工具也将这样做.
|
||||
基于云的Appium提供商也可以这样做.
|
||||
因此, 只需注意, 如果您收到任何提示,
|
||||
表明您的功能缺少供应商前缀, 这便是如何您解决问题的方法.
|
||||
|
||||
在相关的说明中,
|
||||
将无法再使用不支持 W3C 协议的 WebDriver 客户端
|
||||
启动 Appium 会话 (有关 WD 库的针对此效果的注释, 请参阅下文) .
|
||||
|
||||
为了让大家更轻松一些,
|
||||
我们还引入了将所有与 Appium 相关的capabilities
|
||||
打包到一个capabilities对象`appium:options`中的选项.
|
||||
您可以将通常用于 `appium:` 前缀上的任何内容绑定到此capabilities中.
|
||||
下面是一个示例 (以原始 JSON 格式) ,
|
||||
说明如何使用`appium:options`在 Safari 浏览器上启动 iOS 会话:
|
||||
This requirement may or may not be a breaking change for your test suites when targeting Appium 2.
|
||||
If you're using an updated Appium client (at least one maintained by the Appium team), the client
|
||||
will add the `appium:` prefix for you on all necessary capabilities automatically. New versions of
|
||||
[Appium Inspector](https://github.com/appium/appium-inspector) will also do this. Cloud-based Appium
|
||||
providers may also do this. So simply be aware that if you get any messages to the effect that your
|
||||
capabilities lack a vendor prefix, this is how you solve that problem.
|
||||
|
||||
To make everyone's lives a bit easier, we've also introduced the option of grouping up all
|
||||
Appium-related capabilities into one object capability, `appium:options`. You can bundle together
|
||||
anything that you would normally put an `appium:` prefix on into this one capability. Here's an
|
||||
example (in raw JSON) of how you might start an iOS session on the Safari browser using `appium:options`:
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -206,154 +216,145 @@ Appium 的大多数capabilities都超出了标准的 W3C capabilities,
|
||||
}
|
||||
```
|
||||
|
||||
(当然, 每个客户端都会有不同的方法来创建结构化capabilities,
|
||||
诸如 `appium:options` 或其他你可能见过的capabilities, 如 `goog:chromeOptions`) .
|
||||
注意:显示在 `appium:options` 中的capabilities将覆盖显示在对象顶层的同名capabilities.
|
||||
(Of course, each client will have a different way of creating structured capabilities like
|
||||
`appium:options` or other ones that you might have seen such as `goog:chromeOptions`).
|
||||
|
||||
有关capabilities的更多信息, 请查看[Capabilities指南](caps.md).
|
||||
!!! note
|
||||
|
||||
### :bangbang: _删除的命令_
|
||||
Capabilities included in the `appium:options` object will overwrite capabilities of the same
|
||||
name that are used outside of this object. (The `appium:options` syntax support by cloud
|
||||
providers may vary.)
|
||||
|
||||
除了已移至驱动程序实现的命令外,
|
||||
作为W3C协议外的旧的JSON Wire协议命令将不再可用:
|
||||
For more information on capabilities, have a look at the [Capabilities Guide](./caps.md).
|
||||
|
||||
- TODO (这些命令正在被区分和删除, 完成后将在此处更新)
|
||||
### :bangbang: Removed Commands
|
||||
|
||||
如果您使用现代Appium或Selenium客户端,
|
||||
则无论如何都不应再使用遗留命令,
|
||||
因此任何重大变更都应首先作用于客户端方面.
|
||||
In addition to commands which have been moved to driver implementations, commands which were a part
|
||||
of the old JSON Wire Protocol and not a part of the W3C Protocol are no longer available:
|
||||
|
||||
- TODO (these commands are being identified and removed and will be updated here when complete)
|
||||
|
||||
### :bangbang: 图像分析功能移至插件
|
||||
If you use a modern Appium or Selenium client, you should no longer have access to these anyway,
|
||||
so any breaking changes should appear on the client side first and foremost.
|
||||
|
||||
Appium 2.0 的设计目标之一是
|
||||
将非核心功能迁移到名为 [plugins](../ecosystem/plugins.md)的特殊扩展中.
|
||||
这允许人们选择使用额外时间来下载或扩展系统设置的功能.
|
||||
Appium的各种图像相关功能 (图像比较, 按图像查找元素等) 已被移动到一个名为 [images](https://github.com/appium/appium/tree/master/packages/images-plugin)的官方支持的插件中.
|
||||
### :bangbang: Image analysis features moved to plugin
|
||||
|
||||
如果您使用这些与图像相关的方法,
|
||||
要继续使用它们, 您需要执行两项操作.
|
||||
One of the design goals for Appium 2 is to migrate non-core features into special extensions called
|
||||
[plugins](../ecosystem/plugins.md). This allows people to opt into features which require extra time
|
||||
to download or extra system setup. The various image-related features of Appium (image comparison,
|
||||
finding elements by image, etc...) have been moved into an officially supported plugin called
|
||||
[images](https://github.com/appium/appium/tree/master/packages/images-plugin).
|
||||
|
||||
1. 安装插件: `appium plugin install images`
|
||||
2. 确保您启动 Appium Server时具备访问运行插件的权限, 方法是将其包含在命令行上指定的插件列表中, 例如 `appium --use-plugins=images`
|
||||
If you use these image-related methods, to continue accessing them you will need to do two things:
|
||||
|
||||
与图像相关的命令也将在客户端被删除,
|
||||
这意味着您需要按照插件README上的说明安装客户端插件来使用这些功能.
|
||||
1. Install the plugin: `appium plugin install images`
|
||||
2. Ensure you start the Appium server with access to run the plugin by including it in the list of
|
||||
plugins designated on the command line, e.g., `appium --use-plugins=images`
|
||||
|
||||
Image-related commands will also be removed on the client side of things, which means you will need
|
||||
to follow the instructions on the plugin README for installing client-side plugins to access these features.
|
||||
|
||||
### :bangbang: 执行驱动程序脚本命令移至插件
|
||||
### :bangbang: Execute Driver Script command moved to plugin
|
||||
|
||||
如果您使用高级执行驱动程序脚本功能
|
||||
(它允许您发送WebdriverIO脚本以使其完全在服务器上执行,
|
||||
而不是从客户端逐个命令执行) ,
|
||||
则此功能已移至插件.
|
||||
以下是继续使用它的方法:
|
||||
If you use the advanced Execute Driver Script feature (which allows you to send in a WebdriverIO
|
||||
script to have it executed completely on the server instead of command-by-command from the client),
|
||||
this functionality has been moved to a plugin. Here's what to do to keep using it:
|
||||
|
||||
1. Install the plugin: `appium plugin install execute-driver`
|
||||
2. Ensure you start the Appium server with access to run the plugin by including it in the list of
|
||||
plugins designated on the command line, e.g., `appium --use-plugins=execute-driver`
|
||||
|
||||
1. 安装插件: `appium plugin install execute-driver`
|
||||
2. 确保您启动 Appium Server时具备访问运行插件的权限, 方法是将其包含在命令行上指定的插件列表中, 例如 `appium --use-plugins=execute-driver`
|
||||
### :bangbang: External Files No Longer Supported for `--nodeconfig`, `--default-capabilities`, `--allow-insecure` and `--deny-insecure`
|
||||
|
||||
### :bangbang: 不再支持外部文件 `--nodeconfig`, `--default-capabilities`, `--allow-insecure` 以及 `--deny-insecure`
|
||||
These options can be provided as strings on the command line (a JSON string for `--nodeconfig` and
|
||||
a comma-separated list of strings for `--allow-insecure` and `--deny-insecure`). Arguments provided
|
||||
on the command line will likely need to be quoted or escaped.
|
||||
|
||||
这些选项可以在命令行上以字符串形式提供
|
||||
( `--nodeconfig` 的JSON字符串以及
|
||||
用于`--allow-insecure`和`--deny-insecure`的逗号分隔字符串列表) .
|
||||
命令行上提供的参数可能需要用引号括起或转义.
|
||||
The recommended method to provide these options is through a [configuration file](./config.md).
|
||||
|
||||
现在, 提供这些选项的推荐方法是通过 [配置文件](#tada-configuration-files).
|
||||
In summary, if you are using a JSON Appium config file, you can simply cut-and-paste the contents
|
||||
of your "nodeconfig" JSON file into the value of the `server.nodeconfig` property. Any CSV-like
|
||||
files you had previously provided for `--allow-insecure` and `--deny-insecure` become the values
|
||||
of the `server.allow-insecure` and `server.deny-insecure` properties in the Appium config files
|
||||
(respectively); both are arrays of strings.
|
||||
|
||||
总之, 如果您使用的是 JSON Appium 配置文件,
|
||||
则只需将"nodeconfig"JSON 文件的内容剪切并粘贴到 `server.nodeconfig` 属性的值中即可.
|
||||
您之前为 `--allow-insecure` 和 `--deny-insecure` 提供的任何类似CSV的文件
|
||||
分别成为Appium配置文件中 `server.allow-insecure` 和 `server.deny-insecure` 属性的值;
|
||||
两者都是字符串数组.
|
||||
### :bangbang: Old drivers removed
|
||||
|
||||
The old iOS and Android (UiAutomator 1) drivers and related tools (e.g., `authorize-ios`) have been
|
||||
removed. They haven't been relevant for many years anyway.
|
||||
|
||||
### :bangbang: 删除的旧驱动
|
||||
### :bangbang: Server can no longer be started with `--port 0`
|
||||
|
||||
旧的iOS和Android (UiAutomator 1) 驱动程序
|
||||
和相关工具 (例如, `authorize-ios`) 已被删除.
|
||||
无论如何, 它们已经很多年都没有关联了.
|
||||
In Appium 1, it was possible to specify `--port 0` during server startup. This had the effect of
|
||||
starting Appium on a random free port. In Appium 2, port values must be `1` or higher. The random
|
||||
port assignment was never an intentional feature of Appium 1, but a consequence of how Node's
|
||||
HTTP servers work and the fact that there was no port input validation in Appium 1. If you want
|
||||
to find a random free port to start Appium on, you must now take care of this on your own prior to
|
||||
starting Appium. Starting Appium on an explicit and known port is the correct practice moving
|
||||
forward.
|
||||
|
||||
### :warning: 内部包已重命名
|
||||
### :warning: Internal packages renamed
|
||||
|
||||
一些Appium内部的NPM软件包已被重命名
|
||||
(例如, `appium-base-driver` 现在是 `@appium/base-driver`) .
|
||||
对于Appium用户来说, 这并非是重大变更,
|
||||
仅仅针对那些已经构建了直接包含Appium代码的软件的人来说.
|
||||
Some Appium-internal NPM packages have been renamed (for example, `appium-base-driver` is now
|
||||
`@appium/base-driver`). This is not a breaking change for Appium users, only for people who have
|
||||
built software that directly incorporates Appium's code.
|
||||
|
||||
### :warning: 不再支持"WD"JavaScript 客户端库
|
||||
### :warning: `wd` JavaScript client library no longer supported
|
||||
|
||||
多年来, Appium的一些作者一直维护着[WD](https://github.com/admc/wd) 客户端库.
|
||||
此库已被弃用, 并且尚未更新W3C WebDriver协议.
|
||||
因此, 如果您正在使用此库, 则需要迁移到更现代的库.
|
||||
我们推荐 [WebdriverIO](https://webdriver.io).
|
||||
For many years, some of Appium's authors maintained the [WD](https://github.com/admc/wd) client
|
||||
library. This library has been deprecated and has not been updated for use with the W3C WebDriver
|
||||
protocol. As such, if you're using this library you'll need to move to a more modern one. We
|
||||
recommend [WebdriverIO](https://webdriver.io).
|
||||
|
||||
### :warning: Appium Desktop replaced with Appium Inspector
|
||||
|
||||
### :warning: Appium Inspector 从 Appium Desktop 中分离出来
|
||||
The inspector functionality of Appium Desktop has been moved to its own app:
|
||||
[Appium Inspector](https://github.com/appium/appium-inspector). It is fully compatible with
|
||||
standalone Appium 2 servers, but also works with later versions of Appium 1 servers. Appium Desktop
|
||||
itself has been deprecated and is not compatible with Appium 2.
|
||||
|
||||
Appium Desktop的Inspector部分已移至其自己的应用程序Appium Inspector:
|
||||
[github.com/appium/appium-inspector](https://github.com/appium/appium-inspector).
|
||||
其与Appium 2.0 Server完全兼容.
|
||||
只需下载它并自行运行即可.
|
||||
您不再需要GUI Appium桌面服务器来检查应用程序.
|
||||
Appium Desktop Server将继续在其原始站点
|
||||
[github.com/appium/appium-desktop](https://github.com/appium/appium-desktop) 上受支持.
|
||||
它不再将Inspector与其捆绑在一起.
|
||||
In addition to the app, Appium Inspector also has a browser version, accessible at
|
||||
[inspector.appiumpro.com](https://inspector.appiumpro.com). Note that in order to use the
|
||||
browser version with a local Appium server, you'll need to first start the server with the
|
||||
`--allow-cors` flag.
|
||||
|
||||
您现在还可以通过访问[Web版本的Appium Inspector](https://inspector.appiumpro.com)
|
||||
来使用Appium Inspector而无需下载任何内容.
|
||||
请注意, 要针对本地服务器进行测试, 您需要使用"--allow-cors"启动服务器,
|
||||
以便基于浏览器版本的Appium Inspector可以访问您的Appium Server以启动会话.
|
||||
## Major New Features
|
||||
|
||||
Apart from the breaking changes mentioned above, in this section is a list of some of the major new
|
||||
features you may wish to take advantage of with Appium 2.
|
||||
|
||||
## 主要新功能
|
||||
### Plugins
|
||||
|
||||
除了上面提到的重大变更之外,
|
||||
本节中还列出了您可能希望在 Appium 2.0 中利用的一些主要新功能.
|
||||
#### :tada: Server Plugins
|
||||
|
||||
### 插件
|
||||
Appium extension authors can now develop their own server plugins, which can intercept and modify
|
||||
any Appium command, or even adjust the way the underlying Appium HTTP server itself works. To learn
|
||||
more about plugins, read the new [Appium Introduction](../intro/index.md). Interested in building
|
||||
a plugin? Check out the [Building Plugins](../developing/build-plugins.md) guide.
|
||||
|
||||
#### :tada: _服务器插件_
|
||||
### :tada: Install drivers and plugins from anywhere
|
||||
|
||||
Appium 扩展作者现在可以开发自己的服务器插件,
|
||||
可以拦截和修改任何Appium命令,
|
||||
甚至调整底层 Appium HTTP 服务器本身的工作方式.
|
||||
了解有关插件的更多信息,
|
||||
查询新的 [Appium 介绍](../intro/index.md).
|
||||
有兴趣构建插件?
|
||||
请查阅 [构建插件](../ecosystem/build-plugins.md) 指南.
|
||||
You're no longer limited to the drivers that come with Appium, or that the Appium team even knows
|
||||
about! Appium extension authors can now develop custom drivers, which can be downloaded or
|
||||
installed via Appium's [Extension CLI](../cli/extensions.md) from `npm`, `git`, GitHub, or even the
|
||||
local filesystem. Interested in building a driver? Check out the [Building
|
||||
Drivers](../developing/build-drivers.md) guide.
|
||||
|
||||
### :tada: Configuration Files
|
||||
|
||||
#### :tada: _客户端插件_
|
||||
Appium now supports _configuration files_ in addition to command-line arguments. In a nutshell,
|
||||
nearly all arguments which Appium 1 required to be provided on the CLI are now able to be expressed
|
||||
via a configuration file. Configuration files may be in JSON, JS, or YAML format. See the
|
||||
[Config Guide](./config.md) for a full explanation.
|
||||
|
||||
TODO
|
||||
## Special Notes for Cloud Providers
|
||||
|
||||
### :tada: 从任何地方安装驱动程序和插件
|
||||
The rest of this document has applied to Appium generally, but some of the architectural changes in
|
||||
Appium 2 will constitute breaking changes for Appium-related service providers, whether a
|
||||
cloud-based Appium host or an internal service. At the end of the day, the maintainer of the Appium
|
||||
server is responsible for installing and making available the various Appium drivers and plugins
|
||||
that end users may wish to use.
|
||||
|
||||
您不再局限于 Appium 附带的驱动程序,
|
||||
或者通过Appium团队知晓!
|
||||
Appium 扩展作者现在可以开发自定义驱动程序,
|
||||
可以通过Appium的[扩展 CLI](../cli/extensions.md)
|
||||
从NPM, Git, GitHub 乃至本地的文件系统下载或安装.
|
||||
有兴趣构建驱动程序?
|
||||
请查阅 [构建驱动](../ecosystem/build-drivers.md) 指南.
|
||||
|
||||
### :tada: 配置文件
|
||||
|
||||
Appium 现在除了命令行参数外,
|
||||
还支持 _配置文件_ .
|
||||
简而言之, Appium 1.x需要在CLI上提供的几乎所有参数现在都可以通过配置文件来表达.
|
||||
配置文件可以是 JSON、JS 或 YAML 格式.
|
||||
有关完整说明, 请参阅 [配置指南](config.md) .
|
||||
|
||||
|
||||
## 云供应商特别说明
|
||||
|
||||
本文档的其余部分已普遍适用于 Appium,
|
||||
但 Appium 2.0 中的一些架构更改将构成 Appium 相关服务供应商的重大变更,
|
||||
无论是基于云的 Appium 主机还是内部服务.
|
||||
在一天结束时, Appium Server的维护者负责安装和提供最终用户可能希望使用的各种Appium驱动程序和插件.
|
||||
|
||||
我们鼓励云供应商彻底阅读并理解我们的 [针对云供应商的建议](caps.md#special-notes-for-cloud-providers),
|
||||
以行业兼容的方式支持用户!
|
||||
We encourage cloud providers to thoroughly read and understand our [recommendation for cloud
|
||||
provider capabilities](./caps.md#special-notes-for-cloud-providers) in order to support user needs in
|
||||
an industry-compatible way!
|
||||
|
||||
@@ -2,4 +2,64 @@
|
||||
title: Appium Server Security
|
||||
---
|
||||
|
||||
TODO
|
||||
The Appium team makes every effort to ensure the security of the Appium server. This is especially
|
||||
important when Appium is run in a multitenant environment, or when multiple users are running
|
||||
sessions on the same Appium server. In general, you can only safely enable all Appium's features if
|
||||
all the following are true:
|
||||
|
||||
- You're running your own Appium server locally or within a protected internal network
|
||||
- You're not sharing it with any untrusted parties
|
||||
- You don't expose Appium's port(s) to the wider internet
|
||||
|
||||
But because many Appium users might not be able to guarantee such a safe environment, the Appium
|
||||
team puts many features behind a security protection mechanism which forces system admins (the
|
||||
people that are in charge of starting the Appium server) to _explicitly opt-in_ to these features.
|
||||
(Third-party driver and plugin authors can also [hide behaviour behind security
|
||||
flags](../developing/build-drivers.md).)
|
||||
|
||||
For security reasons, Appium client sessions can _not_ request feature enablement via capabilities;
|
||||
this is the responsibility of the server admin who configures and launches the Appium server.
|
||||
|
||||
## Security Server Args
|
||||
|
||||
The [Server CLI Args](../cli/args.md) doc outlines three relevant arguments which may be passed to
|
||||
Appium when starting it from the command line:
|
||||
|
||||
|<div style="width:10em">Parameter</div>|Description|
|
||||
|---------------------------------------|-----------|
|
||||
|`--relaxed-security` |Setting this flag turns on _all_ insecure features (unless blocked by `--deny-insecure`; see below)|
|
||||
|`--allow-insecure` |Setting this flag to a comma-separated list of feature names or a path to a file containing a feature list (each name on a separate line) will allow _only_ the features listed. For example, `--allow-insecure=adb_shell` will cause _only_ the ADB shell execution feature to be enabled. This is true _unless_ `--relaxed-security` is also used, in which case all features will still be enabled. It makes no sense to combine this flag with `--relaxed-security`.|
|
||||
|`--deny-insecure` |This flag can likewise be set to a comma-separated list of feature names, or a path to a feature file. Any features listed here will be _disabled_, regardless of whether `--relaxed-security` is set and regardless of whether the names are also listed with `--allow-insecure`.|
|
||||
|
||||
## Insecure Features
|
||||
|
||||
Each Appium driver is responsible for its own security, and can create its own feature names. Thus
|
||||
you should read through the documentation for a particular driver to know which feature names it
|
||||
might use. Here is an incomplete list of examples from some of Appium's official drivers:
|
||||
|
||||
|<div style="width:12em">Feature Name</div>|Description|Supported Extension(s)|
|
||||
|------------|-----------|-------|
|
||||
|`get_server_logs`|Allows retrieving of Appium server logs via the Webdriver log interface|IOS, XCUITest, Android, UiAutomator2, Espresso|
|
||||
|`adb_shell`|Allows execution of arbitrary shell commands via ADB, using the `mobile: shell` command|Android, UiAutomator2, Espresso|
|
||||
|`record_audio`|Allow recording of host machine audio inputs|XCUITest|
|
||||
|`execute_driver_script`| Allows to send a request which has multiple Appium commands.|Execute Driver Plugin|
|
||||
|
||||
## Examples
|
||||
|
||||
To turn on the `get_server_logs` feature for my Appium server, I could start it like this:
|
||||
|
||||
```bash
|
||||
appium --allow-insecure=get_server_logs
|
||||
```
|
||||
|
||||
To turn on multiple features:
|
||||
|
||||
```bash
|
||||
appium --allow-insecure=get_server_logs,record_audio
|
||||
```
|
||||
|
||||
To allow all features except one:
|
||||
|
||||
```bash
|
||||
appium --relaxed-security --deny-insecure=adb_shell
|
||||
```
|
||||
|
||||
@@ -1,5 +1,68 @@
|
||||
---
|
||||
title: The Settings API
|
||||
hide:
|
||||
- toc
|
||||
|
||||
title: Session Settings
|
||||
---
|
||||
|
||||
TODO
|
||||
Appium has a set of extension APIs that allow you to adjust parameters for a given session during
|
||||
the session itself. Called "Settings", these parameters are similar to [Capabilities](./caps.md),
|
||||
but while Capabilities _cannot be adjusted_ once a session has started, Settings _can be adjusted any
|
||||
number of times_ during the course of a session.
|
||||
|
||||
There are 3 important points to the concept of Settings:
|
||||
|
||||
- Settings are mutable; they can be changed during a session using the Settings API
|
||||
- Settings are only relevant during the session in which they are set. They are typically reset for
|
||||
each new session, though depending on the driver, some settings may persist between sessions
|
||||
- Settings only adjust the way the Appium server behaves during test automation. They do not affect
|
||||
the device or app under test
|
||||
|
||||
An example of a setting would be the `ignoreUnimportantViews` setting recognized by the UiAutomator2
|
||||
driver. The driver can be instructed to ignore elements in the view hierarchy which it deems
|
||||
irrelevant. Changing this setting to `true` can cause tests to run faster. But if later in the same
|
||||
session you _want_ to access elements which would be ignored under this setting, you can always
|
||||
change it back to `false`.
|
||||
|
||||
Settings are implemented via the following API endpoints:
|
||||
|
||||
| Command | <div style="width:18em">Method/Route</div> | Params | Description | Returns |
|
||||
|-------------------|---------------------------------------------|------------------------------------|------------------------------------|------------------------------------|
|
||||
| `Update Settings` | `POST /session/:id/appium/settings` | `settings` (`Record<string, any>`) | Update the provided setting values | `null` |
|
||||
| `Get Settings` | `GET /session/:id/appium/settings` | | Return the current settings | `settings` (`Record<string, any>`) |
|
||||
|
||||
The `settings` object must be a set of keys and values, where the key is the setting name, and the
|
||||
value is any documented valid value for that setting.
|
||||
|
||||
!!! info
|
||||
|
||||
Settings are driver-specific, so refer to your driver's documentation for a list of supported settings
|
||||
|
||||
## Initializing Settings via Capabilities
|
||||
|
||||
If you want to start an Appium session with a setting in a non-default state, you can do so by
|
||||
including a capability of the form `appium:settings[<name>]` with the appropriate value. So to turn
|
||||
on the `ignoreUnimportantViews` setting mentioned above from the very beginning of a session, you
|
||||
would construct a set of capabilities that includes the following in its JSON representation:
|
||||
|
||||
```json
|
||||
{
|
||||
"appium:settings[ignoreUnimportantViews]": true
|
||||
}
|
||||
```
|
||||
|
||||
Also, since Appium 2.1, there is a possibility to provide multiple settings in a single
|
||||
`appium:settings` capability value:
|
||||
|
||||
```json
|
||||
{
|
||||
"appium:settings": {
|
||||
"ignoreUnimportantViews": true,
|
||||
"allowInvisibleElements": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Of course, initializing a setting via capabilities doesn't prevent you from changing it later on
|
||||
via the Settings API. To learn more about how to use the Settings API in the context of your
|
||||
specific client library, visit the documentation for that client.
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
---
|
||||
hide:
|
||||
- toc
|
||||
|
||||
title: SSL/TLS/SPDY Support
|
||||
---
|
||||
|
||||
Appium 2.2 introduces the option to start the Appium server with SSL/TLS support.
|
||||
|
||||
## Command Line Arguments
|
||||
In order to enable secure connections to the server, you need to provide the following command
|
||||
line arguments:
|
||||
|
||||
```bash
|
||||
appium server --ssl-cert-path=/path/to/cert.pem --ssl-key-path=/path/to/key.pem
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
### 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.
|
||||
|
||||
### Self-Signed Certificates
|
||||
|
||||
Use the following command in order to generate a self-signed certificate/key pair:
|
||||
|
||||
```bash
|
||||
openssl req -nodes -new -x509 -keyout key.pem -out cert.pem -subj "/C=US/ST=State/L=City/O=company/OU=Com/CN=www.testserver.local"
|
||||
```
|
||||
|
||||
Feel free to change the value of `-subj` in the command above with your matching details. The server
|
||||
should work just fine with a self-signed certificate, although you need to take care about a proper
|
||||
client setup, e.g. make sure it does not reject unauthorized certificates.
|
||||
@@ -6,7 +6,8 @@ hide:
|
||||
title: 欢迎
|
||||
---
|
||||
<style>
|
||||
.md-typeset h1 {
|
||||
.md-typeset h1,
|
||||
.appium-sponsor-thanks {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
@@ -14,23 +15,44 @@ title: 欢迎
|
||||
<img src="assets/images/appium-logo-horiz.png" style="max-width: 400px;" />
|
||||
</div>
|
||||
|
||||
欢迎访问 Appium 文档!Appium 是一个开源项目和相关软件的生态系统。
|
||||
软件,旨在促进许多应用程序平台的用户界面自动化,包括移动平台(iOS、
|
||||
Android、Tizen)、浏览器(Chrome、Firefox、Safari)、桌面(macOS、Windows)、电视(Roku、tvOS、
|
||||
Android TV、三星)等!
|
||||
欢迎来到Appium文档!Appium是一个开源项目和相关软件生态系统,旨在促进许多应用程序平台的UI自动化,包括移动端(iOS、Android、Tizen)、浏览器端(Chrome、Firefox、Safari)、桌面端(macOS、Windows)、电视端(Roku、tvOS、Android TV、三星)等!
|
||||
|
||||
|
||||
<div style="text-align: center; margin-top: 2rem; font-style: italic;">
|
||||
Appium非常感谢主要合作伙伴的支持!(了解更多关于我们的赞助计划和贡献者补偿计划信息请<a
|
||||
href="https://github.com/appium/appium/blob/master/GOVERNANCE.md#sponsorship">点击此处</a>)
|
||||
<div class="homepageSponsors">
|
||||
<div class="homepageSponsor">
|
||||
<a href="https://www.headspin.io/solutions/appium-mobile-test-automation">
|
||||
<img src="assets/images/sponsor-logo-headspin.png" style="width: 200px;" />
|
||||
</a>
|
||||
</div>
|
||||
<div class="homepageSponsor">
|
||||
<a href="https://www.browserstack.com/browserstack-appium?utm_campaigncode=701OW00000AoUTQYA3&utm_medium=partnered&utm_source=appium">
|
||||
<img src="assets/images/sponsor-logo-browserstack-dark.png#only-dark" style="width: 220px;" />
|
||||
<img src="assets/images/sponsor-logo-browserstack-light.png#only-light" style="width: 220px;" />
|
||||
</a>
|
||||
</div>
|
||||
<div class="homepageSponsor">
|
||||
<a href="https://saucelabs.com/resources/blog/appium-strategic-partner">
|
||||
<img src="assets/images/sponsor-logo-sauce.png" style="width: 200px;" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
## 探索文档
|
||||
|
||||
<div class="grid cards" markdown>
|
||||
|
||||
- 查看 [__介绍__](./intro/index.md) 确保您理解关键概念
|
||||
- 查看 [__简介__](./intro/index.md) 以确保您了解关键概念
|
||||
- 通过 [__快速入门__](./quickstart/index.md) 来进行设置并运行基本的 Android 测试
|
||||
- 访问 [__生态系统__](./ecosystem/index.md) 页面,查看您可能需要使用的驱动程序、客户端和插件列表
|
||||
- 参考 [__CLI 参考__](./cli/index.md) 用于从命令行使用 Appium
|
||||
- 查看 [__命令说明__](./commands/index.md) Appium 和插件公开的命令列表
|
||||
- 阅读不同的 [__指南__](./guides/migrating-1-to-2.md) 来获取各种说明、技巧和窍门
|
||||
- 查阅各种第三方的 [__资源__](./resources.md) 来探索网上的Appium运用
|
||||
- 为了创建您自己的Appium拓展, 请查看 [__开发者__](./developing/index.md) 文档
|
||||
- 对 Appium 自身的贡献,请查阅 [__贡献__](./contributing/index.md) 页面
|
||||
- 访问 [__生态系统__](./ecosystem/index.md) 页面,获取您可能想要使用的驱动程序、客户端和插件列表
|
||||
- 参考 [__CLI Reference__](./cli/index.md) 以了解如何从命令行使用Appium
|
||||
- 参考 [__Command Reference__](./commands/index.md) 以了解有关Appium和插件的命令列表
|
||||
- 阅读不同的 [__指南__](./guides/migrating-1-to-2.md),了解各种说明、提示和技巧
|
||||
- 查看各种第三方 [__资源__](./resources.md) 以探索网络上的Appium
|
||||
- 要创建自己的Appium扩展,请参阅 [__开发者__](./developing/index.md) 文档
|
||||
- 有关对Appium本身的贡献,请查阅 [__贡献__](./contributing/index.md) 页面
|
||||
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
---
|
||||
title: Appium如何工作?
|
||||
---
|
||||
|
||||
正如主页上提到的那样,Appium是一个开源项目和相关软件生态系统,旨在促进许多应用程序平台的UI自动化。随着Appium 2的发布,Appium的主要目标如下:[^1]
|
||||
|
||||
- 在跨平台、标准API下提供特定于平台的自动化功能
|
||||
- 允许从任何编程语言轻松访问此API
|
||||
- 提供工具以方便社区开发Appium扩展
|
||||
|
||||
[^1]:
|
||||
为了实现这些主要目标,我们还制定了一套次要目标或方法论原则,我们也鼓励Appium扩展开发人员这样做:
|
||||
|
||||
- 尽可能依赖(并贡献)开源技术
|
||||
- 尽可能依赖供应商为给定平台提供的工具
|
||||
- 尽可能依赖允许不修改应用程序就能实现自动化的工具(不要让用户构建额外的SDK或软件,从而导致应用程序的测试版本和生产版本之间存在差异)
|
||||
- 尽可能依赖现有标准而不是创建新标准
|
||||
|
||||
因此,请选择您所知道的任何应用程序平台,例如iOS或Android。Appium希望有一种方法让开发人员和测试人员根据单一、统一的API为该平台编写UI自动化代码。根据Appium的目标,我们有很多问题需要回答才能使这一切顺利进行:
|
||||
|
||||
- “单一、统一”API应该是哪个API?
|
||||
- 我们如何将该API映射到特定平台的自动化行为?
|
||||
- 我们如何通过多种流行编程语言访问该API?
|
||||
|
||||
考虑到除了iOS和Android之外还有更多的应用程序平台,这里还隐藏着另一个更大的问题:
|
||||
|
||||
- 我们如何实现所有平台的自动化?
|
||||
|
||||
探索Appium对这些问题的答案可能不是了解Appium是什么的最快方法,但它肯定是一个很好的方法!那么让我们深入研究吧。
|
||||
|
||||
## Appium对API的选择
|
||||
|
||||
Appium非常幸运,它先于UI自动化领域的长期先驱技术 [Selenium](https://selenium.dev) 。Selenium项目的目标是支持网络浏览器的UI自动化,通过这种方式,我们可以将其视为占据Appium目标的一个子集。一路走来,Selenium(以及合并后的另一个名为WebDriver的项目)开发了一个相对稳定的浏览器自动化API。
|
||||
|
||||
多年来,Selenium与多家网络浏览器供应商和 [W3C](https://w3.org) 标准小组合作,将其API转变为官方网络浏览器标准,称为 [WebDriver规范](https://w3c.github.io/webdriver/)。所有主要浏览器现在都实现了符合WebDriver规范的自动化功能,Selenium团队无需维护任何执行实际自动化的软件;胜利的标准!
|
||||
|
||||
Appium的最初目标是为移动应用程序(iOS和Android)开发自动化标准。我们本可以创造一些新的东西,但要本着团结一致和保持标准的精神,好吧,标准,我们决定采用WebDriver规范作为Appium的API。[^2]虽然网站和移动原生应用程序中的用户交互并不完全相同(例如,一旦我们开始考虑由简单遥控器控制的电视平台,差异就会更大),实际上大多数软件UI几乎相同。这意味着WebDriver规范提供自动化API基元(查找元素、与元素交互、加载页面或屏幕等)或多或少映射到任何平台。
|
||||
|
||||
[^2]: 从技术上讲,当Appium第一次编写时,我们正在处理比WebDriver规范更早的东西,称为JSON Wire Protocol。从那时起,Appium继续随着W3C规范发展,并且完全符合W3C。
|
||||
|
||||
当然,Appium希望支持用户交互从网页到移动设备或从网页到电视的不同情况,因此Appium还利用了WebDriver规范的内置可扩展性。结果是,无论您想要实现哪个平台自动化,当您使用Appium时,您都将使用标准的WebDriver规范来执行,但有两个警告:
|
||||
|
||||
- 我们可能无法在给定的平台上支持特定的WebDriver API命令,因此某些命令可能不受支持(例如,在原生移动应用程序自动化中,获取或设置cookie是不可能的)。
|
||||
- 我们可能支持超出WebDriver API命令列表中可用范围的自动化行为,尽管任何此类命令都是WebDriver API的有效且符合规范的扩展。
|
||||
|
||||
您实际上如何使用WebDriver API,特别是在Appium的环境中?我们将在[下面](#universal-programming-language-access)关于Appium如何提供通用编程语言访问的部分中讨论这一点。目前您需要知道的是,Appium引入通用UI自动化界面的方式是通过实现WebDriver协议。
|
||||
|
||||
## 平台自动化行为
|
||||
|
||||
下一个问题是,Appium如何将该协议映射到广泛平台上的自动化行为?严格来说,Appium没有这样做!它将这一责任留给了一种名为Appium驱动程序的软件模块。接下来您可以阅读完整的[驱动程序简介](./drivers.md),因此我们目前不会详细介绍它们的工作原理。
|
||||
|
||||
目前需要了解的重要一点是,驱动程序有点像Appium的可插入模块,它使Appium能够自动化特定平台(或一组平台,具体取决于驱动程序的目标)。归根结底,驱动程序的责任只是简单地实现代表WebDriver协议的Appium内部接口。它如何实现这个接口完全取决于驱动程序,基于其在特定平台上实现自动化的策略。通常,由于细节上的复杂性和困难性要大得多,驱动程序通过依赖于平台特定的自动化技术来做到这一点。例如,苹果维护一项名为[XCUITest](https://developer.apple.com/documentation/xctest/user_interface_tests)的iOS自动化技术。支持iOS应用自动化的Appium驱动程序被称为[XCUITest
|
||||
Driver](https://github.com/appium/appium-xcuitest-driver),因为它最终的作用是将WebDriver协议转换为XCUITest库调用。
|
||||
|
||||
驱动程序是独立的可插入模块,这样做的原因之一是因为它们的工作方式完全不同。构建和使用不同平台驱动程序的工具和需求也是完全不同的。因此,Appium允许您仅使用自动化任务所需的驱动程序。选择驱动程序并安装它们,以便您可以将它们与Appium实例一起使用,这是非常重要的,因此Appium拥有自己的[CLI来管理驱动程序](../cli/extensions.md)。
|
||||
|
||||
因此,为了回答我们最初的问题,Appium为给定平台提供自动化功能访问的方式是,Appium团队(或其他任何人[^3])为该平台编写驱动程序,根据需要实现尽可能多或尽可能少的WebDriver协议。然后任何使用Appium的人都可以安装该驱动程序。
|
||||
|
||||
[^3]:您可以构建和共享自己的驱动程序!查看[构建驱动程序](../developing/build-drivers.md),了解更多关于如何在Node.js中开发可与Appium一起使用的驱动程序的信息。
|
||||
|
||||
## 通用编程语言访问
|
||||
|
||||
但使用Appium到底是什么意思,或者看起来是什么样子呢?由于Appium最终是一个Node.js程序,因此看起来就像将Appium及其驱动程序作为库导入到您自己的Node.js程序中。但这无法满足Appium为使用任何流行编程语言的人们提供自动化功能的目标。
|
||||
|
||||
幸运的是,Appium搭上了Selenium的顺风车,这意味着我们从第一天起就有了解决这个问题的办法。你看,WebDriver规范实际上是一个基于HTTP的协议,这意味着它被设计为在网络上使用,而不是在单个程序的内存中使用。
|
||||
|
||||
这种“客户端-服务器”架构的主要好处之一是,它允许自动化实现者(执行自动化的东西,这里指服务器端)与自动化运行者(定义自动化应该执行什么、在哪些步骤中等,这里指客户端)完全不同。基本上,所有“硬东西”(实际上是弄清楚如何在给定的平台上实现自动化)都可以由服务器在一个地方处理,而“瘦”客户端库可以用任何编程语言编写,这些语言只需以适当的语言对服务器的HTTP请求进行编码。换句话说,如果存在高级的HTTP库,那么通过在那种语言中编写一个基本的HTTP客户端,就可以相对容易地将基本的Appium/WebDriver功能引入到新的编程语言中。
|
||||
|
||||
对于Appium用户来说,这里有几个重要的要点:
|
||||
|
||||
- Appium是一个HTTP服务器。只要您想将其用于自动化,它就必须在某台计算机上作为进程运行。它必须可以在网络上访问您想用来运行自动化的任何计算机(无论是同一台机器还是世界各地的机器)。
|
||||
- 除非您想编写原始HTTP调用或使用cURL,否则使用Appium进行自动化需要使用您选择的语言的[Appium客户端](clients.md)。每个客户端的目标都是封装WebDriver协议,这样您就可以使用对您的语言来说习惯的对象和方法,而不用担心协议本身。
|
||||
- Appium服务器和Appium客户端不需要在同一台计算机上运行。您只需要能够通过某种网络将HTTP请求从客户端发送到服务器即可。这极大地方便了Appium云提供商的使用,因为它们可以托管Appium服务器以及任何相关驱动程序和设备,并且您需要做的就是将您的客户端脚本指向其安全端点。
|
||||
|
||||
当然,这些都不是关于“测试”本身,而纯粹是关于使用Appium及其客户端库来实现自动化目的。如果你想以“测试”为目的进行自动化,你可能会希望获得测试运行者、测试框架等的帮助,这些都不需要与Appium相关;Appium的“通用可访问性”的好处之一是,它可以很好地与你认为对你的情况最有利的任何一组工具配合使用。
|
||||
|
||||
## Appium的广阔应用范围
|
||||
|
||||
Appium的愿景(在单个API下实现一切自动化)是巨大的!当然,这比开源项目的核心维护人员团队要大得多。那么Appium希望如何实现这个目标呢?基本上,通过授权社区在Appium作为平台的基础上开发功能。这就是我们所说的Appium“生态系统”。
|
||||
|
||||
Appium团队本身确实正式维护了一些驱动程序(例如,我们之前谈到的XCUITest驱动程序)。但不能指望他们拥有特定于平台的专业知识或为许多不同平台维护驱动程序的能力。但我们所做的,特别是从Appium 2开始,是提供工具来帮助社区加入我们的愿景:
|
||||
|
||||
- 任何人都可以通过创建一个符合约定的Node.js模块来创建驱动程序,并实现WebDriver协议的任何子集或超集。创建驱动程序通常需要最少的代码,因为WebDriver协议的细节被抽象出来,并且有许多辅助库可用——这些库也为Appium团队自己的驱动程序提供动力。
|
||||
- 使用Appium驱动程序CLI可以轻松地与他人共享驱动程序。没有中央权力机构。任何人都可以公开或私下、免费或出售共享驱动程序。驱动程序可以是开源的,也可以是闭源的(尽管我们很欣赏开源!)。
|
||||
|
||||
Appium作为开发平台的愿景不仅限于支持所有应用平台的自动化。作为一种流行的自动化工具,Appium有很多机会与各种其他工具和服务集成。此外,对于Appium,无论是作为核心服务器还是通过各种驱动程序的实现,都有许多特性的想法,这些是核心团队永远没有时间来构建的。因此,在Appium 2中,Appium发布了一个插件系统,使任何人都可以构建和共享改变Appium工作方式的模块!
|
||||
|
||||
就像驱动程序可以通过Appium驱动程序CLI轻松共享和使用一样,插件也可以通过相似的[插件CLI](../cli/extensions.md)发布和使用。插件可以做各种各样的事情,例如为Appium添加根据模板图像查找屏幕区域并与屏幕区域交互的能力(如在[图像插件](https://github.com/appium/appium/tree/master/packages/images-plugin)中)。你可以用插件做什么几乎没有限制,所以你可能也有兴趣学习如何在Node.js中[构建](../developing/build-plugins.md)可以与Appium一起使用的插件。
|
||||
|
||||
这就是Appium:一个可扩展的通用界面,用于实现潜在一切的UI自动化!请继续阅读一些具体的介绍文档以获取更多详细信息,或查看各种指南以深入了解Appium的一些概念和功能。
|
||||
@@ -1,6 +1,155 @@
|
||||
---
|
||||
title: Intro to Appium Drivers
|
||||
title: Appium驱动程序简介
|
||||
---
|
||||
|
||||
正如[概述](./appium.md)所表明的那样,“驱动程序”基本上是Appium对“我们如何支持多个无关平台的自动化”这个问题的回答,在本文档中,我们将更详细地了解驱动程序的工作方式。驱动程序如何工作的具体细节对您来说可能并不太重要,除非您计划编写自己的驱动程序或为现有的驱动程序做出贡献(我们希望您这样做!)。
|
||||
|
||||
TODO
|
||||
深入了解驱动程序的工作原理的主要好处是,当您在测试中不可避免地遇到问题时,了解典型的复杂性或典型的驱动程序架构将为您的调试过程提供信息。
|
||||
|
||||
## 接口实现
|
||||
|
||||
在最基本的层面上,驱动程序只是Node.js类,它扩展了Appium中包含的一个特殊类,称为`BaseDriver`。只需一些非常简单的代码,您就可以拥有一个接近可工作的驱动程序:
|
||||
|
||||
```js
|
||||
import BaseDriver from '@appium/base-driver'
|
||||
|
||||
class MyNewDriver extends BaseDriver {
|
||||
}
|
||||
```
|
||||
|
||||
这个空驱动程序什么也不做,但您可以把它包装在一个Node.js模块中,在模块的清单(`package.json`)中添加一些与Appium相关的字段,然后使用`appium driver install`来安装它。
|
||||
|
||||
因此,从技术角度来看,Appium驱动程序只是继承自其他Appium代码的一段代码。就是这样!现在,从`BaseDriver`继承实际上给了我们很多东西,因为`BaseDriver`本质上是整个WebDriver协议的封装。因此,驱动程序需要做的有用的事情就是实现Node.js方法,其名称与WebDriver协议中定义的名称相对应。
|
||||
|
||||
假设我想用这个空驱动程序做一些事情;首先我必须决定我想实现哪个WebDriver命令。对于我们的示例,让我们实现[Navigate To](https://w3c.github.io/webdriver/#navigate-to)这个WebDriver命令。暂且不谈执行此命令时我想让驱动程序做什么。为了告诉Appium驱动程序可以处理该命令,我们所要做的就是在我们的驱动程序类中定义一个这样的方法:[^1]
|
||||
|
||||
```js
|
||||
async setUrl(url) {
|
||||
// 在这里做我们想做的事
|
||||
}
|
||||
```
|
||||
|
||||
[^1]: 您可能会注意到`setUrl`看起来一点也不像`Navigate To`,那么我们怎么知道应该使用它而不是其他随机字符串呢?Appium中WebDriver协议到方法名的映射是在`@appium/base-driver`包中名为[routes.js](https://github.com/appium/appium/blob/master/packages/base-driver/lib/protocol/routes.js)的特殊文件中定义的。因此,如果您正在编写一个驱动程序,您可以在这里弄清楚要使用什么方法名以及需要什么参数。或者您可以查看任何主要的Appium驱动程序源代码!
|
||||
|
||||
就是这样!我们如何实际实现命令完全取决于我们,取决于我们想要支持的平台。以下是该命令针对不同平台的一些不同示例实现:
|
||||
|
||||
- 浏览器: 执行一些JavaScript去设置`window.location.href`
|
||||
- iOS应用程序: 使用深度链接启动应用程序
|
||||
- Android应用程序: 使用深度链接启动应用程序
|
||||
- React应用程序: 加载特定路线
|
||||
- Unity: 转到指定的场景
|
||||
|
||||
因此,您可以看到,驱动程序在不同平台上实现相同WebDriver命令的方式存在很大差异。[^2]不过,他们表达他们能够处理协议命令的方式是相同的。
|
||||
|
||||
[^2]: 当然,我们希望语义尽可能相似,但在iOS世界中,例如,通过深度链接(一个带有特定应用程序协议的URL)启动应用程序,这几乎是我们能够实现的最接近于导航到网页URL的方式。
|
||||
|
||||
我们要深入这么多细节(顺便说一下,您不需要记住这些),重点是要强调一个观点,即Appium驱动程序本质上并不是什么特别的东西,它不过是一段能够处理WebDriver协议命令的JavaScript代码。从那里开始,你能走多远就看你了,驱动程序的作者!
|
||||
|
||||
## 自动化映射
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
So the real challenge for driver authors is not how to work with the WebDriver protocol (because
|
||||
`BaseDriver` encapsulates all that for you), but how to make the actual automation happen on the
|
||||
target platform. Every driver relies on its own set of underlying technologies here. As mentioned
|
||||
in the [Overview](index.md), the iOS driver uses an Apple technology called
|
||||
[XCUITest](https://developer.apple.com/documentation/xctest/xcuielement). These underlying
|
||||
automation technologies usually have proprietary or idiosyncratic APIs of their own. Writing
|
||||
a driver becomes the task of mapping the WebDriver protocol to this underlying API (or sometimes
|
||||
a set of different underlying APIs--for example, the UiAutomator2 driver relies not only on the
|
||||
[UiAutomator2](https://developer.android.com/training/testing/other-components/ui-automator)
|
||||
technology from Google, but also functions only available through
|
||||
[ADB](https://developer.android.com/tools/adb), as well as functions only available
|
||||
via the Android SDK inside a helper app). Tying it all together into a single, usable, WebDriver
|
||||
interface is the incredibly useful (but incredibly challenging) art of driver development!
|
||||
|
||||
## 多层架构
|
||||
|
||||
In practice, this often results in a pretty complex architecture. Let's take iOS for example again.
|
||||
The XCUITest framework (the one used by the Appium driver) expects code that calls it to be written
|
||||
in Objective-C or Swift. Furthermore, XCUITest code can only be run in a special mode triggered by
|
||||
Xcode (and directly or indirectly, the Xcode command line tools). In other words, there's no
|
||||
straightforward way to go from a Node.js function implementation (like `setUrl()` above) to
|
||||
XCUITest API calls.
|
||||
|
||||
What the XCUITest driver authors have done is instead to split the driver into two parts: one part
|
||||
written in Node.js (the part which is incorporated into Appium and which initially handles the
|
||||
WebDriver commands), and the other part written in Objective-C (the part which actually gets run on
|
||||
an iOS device and makes XCUITest API calls). This makes interfacing with XCUITest possible, but
|
||||
introduces the new problem of coordination between the two parts.
|
||||
|
||||
The driver authors could have chosen any of a number of very different strategies to model the
|
||||
communication between the Node.js side and the Objective-C side, but at the end of the day decided
|
||||
to use ... the WebDriver protocol! That's right, the Objective-C side of the XCUITest driver is
|
||||
itself a WebDriver implementation, called
|
||||
[WebDriverAgent](https://github.com/appium/webdriveragent).[^3]
|
||||
|
||||
[^3]: You could in theory, therefore, point your WebDriver client straight to WebDriverAgent and
|
||||
bypass Appium entirely. This is usually not convenient, however, for a few reasons:
|
||||
|
||||
- The Appium XCUITest driver builds and manages WebDriverAgent for you, which can be a pain and
|
||||
involves the use of Xcode.
|
||||
- The XCUITest driver does lots more than what can be done by WebDriverAgent, for example working
|
||||
with simulators or devices, installing apps, and the like.
|
||||
|
||||
The moral of the story is that driver architectures can become quite complicated and multilayered,
|
||||
due to the nature of the problem we're trying to solve. It also means it can be difficult sometimes
|
||||
to tell where in this chain of technologies something has gone wrong, if you run into a problem
|
||||
with a particular test. With the XCUITest world again, we have something like the following set of
|
||||
technologies all in play at the same time:
|
||||
|
||||
- Your test code (in its programming language) - owned by you
|
||||
- The Appium client library - owned by Appium
|
||||
- The Selenium client library - owned by Selenium
|
||||
- The network (local or Internet)
|
||||
- The Appium server - owned by Appium
|
||||
- The Appium XCUITest driver - owned by Appium
|
||||
- WebDriverAgent - owned by Appium
|
||||
- Xcode - owned by Apple
|
||||
- XCUITest - owned by Apple
|
||||
- iOS itself - owned by Apple
|
||||
- macOS (where Xcode and iOS simulators run) - owned by Apple
|
||||
|
||||
It's a pretty deep stack!
|
||||
|
||||
## 代理模式
|
||||
|
||||
There's one other important architectural aspect of drivers to understand. It can be exemplified
|
||||
again by the XCUITest driver. Recall that we just discussed how the two "halves" of the XCUITest
|
||||
driver both speak the WebDriver protocol---the Node.js half clicks right into Appium's WebDriver
|
||||
server, and the Objective-c half (WebDriverAgent) is its own WebDriver implementation.
|
||||
|
||||
This opens up the possibility of Appium taking a shortcut in certain cases. Let's imagine that the
|
||||
XCUITest driver needs to implement the `Click Element` command. The internal code of this
|
||||
implementation would look something like taking the appropriate parameters and constructing an HTTP
|
||||
request to the WebDriverAgent server. In this case, we're basically just reconstructing the
|
||||
client's original call to the Appium server![^4] So there's really no need to even write a function
|
||||
implementing the `Click Element` command. Instead, the XCUITest driver can just let Appium know
|
||||
that this command should be proxied directly to some other WebDriver server.
|
||||
|
||||
[^4]: It's not *exactly* the same call, because the Appium server and the WebDriverAgent server
|
||||
will generate different session IDs, but these differences will be handled transparently.
|
||||
|
||||
If you're not familiar with the concept of "proxying," in this case it just means that the XCUITest
|
||||
driver will not be involved at all in handling the command. Instead it will merely be repackaged
|
||||
and forwarded to WebDriverAgent at the protocol level, and WebDriverAgent's response will likewise
|
||||
be passed back directly to the client, without any XCUITest driver code seeing it or modifying it.
|
||||
|
||||
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
|
||||
example, you'll see that it implements basically no standard commands, because all of these are
|
||||
proxied directly to an underlying SafariDriver process.
|
||||
|
||||
It's important to understand that this proxying business is sometimes happening under the hood,
|
||||
because if you're ever diving into some open source driver code trying to figure out where
|
||||
a command is implemented, you might be surprised to find no implementation at all in the Node.js
|
||||
driver code itself! In that case, you'll need to figure out where commands are being proxied to so
|
||||
you can look there for the appropriate implementation.
|
||||
|
||||
OK, that's enough for this very detailed introduction to drivers!
|
||||
|
||||
@@ -2,28 +2,29 @@
|
||||
hide:
|
||||
- toc
|
||||
|
||||
title: Appium in a Nutshell
|
||||
title: Appium 简介
|
||||
---
|
||||
正如主页上提到的那样,Appium旨在支持许多不同平台(移动、网络、桌面等)的UI自动化。不仅如此,它还旨在支持用不同语言(JS、Java、Python等)编写的自动化代码。将所有这些功能结合到一个程序中是一项非常艰巨、甚至不可能的任务!
|
||||
正如主页上提到的那样,Appium旨在支持许多不同平台(移动端、网页端、桌面端等)的UI自动化。不仅如此,它还旨在支持用不同语言(JS、Java、Python等)编写的自动化代码。将所有这些功能结合到一个程序中是一项非常艰巨、甚至不可能的任务!
|
||||
|
||||
为了实现这一目标,Appium实际上被分为四个部分:
|
||||
|
||||
<div class="grid cards" markdown>
|
||||
- :material-image-filter-center-focus-strong: __Appium Core__ - 定义核心API
|
||||
- :material-car: __Drivers__ - 实现与特定平台的连接
|
||||
- :octicons-code-16: __Clients__ - 实现与特定平台的连接
|
||||
- :octicons-code-16: __Clients__ - 用特定语言实现Appium的API
|
||||
- :fontawesome-solid-plug: __Plugins__ - 更改或扩展Appium的核心功能
|
||||
</div>
|
||||
|
||||
因此,为了开始使用Appium自动化某些内容,您需要:
|
||||
因此,为了开始使用Appium自动化某些内容,你需要:
|
||||
|
||||
- 安装Appium本身
|
||||
- 为您的目标平台安装驱动程序
|
||||
- 为您的目标编程语言安装客户端库
|
||||
- 为你的目标平台安装驱动程序
|
||||
- 为你的目标编程语言安装客户端库
|
||||
- (可选)安装一个或多个插件
|
||||
|
||||
这些都是基础!如果您准备好加入,请继续[快速入门](../quickstart/index.md)!
|
||||
这些都是基础!如果你准备好加入,请继续[快速入门](../quickstart/index.md)!
|
||||
|
||||
如果您想了解有关其运作方式的更多详细信息,请参阅以下页面了解背景材料:
|
||||
如果你想了解有关其运作方式的更多详细信息,请参阅以下页面了解背景材料:
|
||||
|
||||
- [Appium Core](./appium.md)
|
||||
- [Appium Drivers](./drivers.md)
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
---
|
||||
hide:
|
||||
- toc
|
||||
|
||||
title: System Requirements
|
||||
---
|
||||
|
||||
The basic requirements for the Appium server are:
|
||||
|
||||
* A macOS, Linux, or Windows operating system
|
||||
* [Node.js](https://nodejs.org) version in the [SemVer](https://semver.org) range `^14.17.0 || ^16.13.0 || >=18.0.0`
|
||||
* LTS is recommended
|
||||
* [`npm`](https://npmjs.com) version `>=8` (`npm` is usually bundled with Node.js, but can be upgraded
|
||||
independently)
|
||||
|
||||
By itself, Appium is relatively lightweight and doesn't have significant disk space or RAM
|
||||
requirements. It can even be run in resource-constrained environments like Raspberry Pi, so long as
|
||||
Node.js is available.
|
||||
|
||||
### Driver Requirements
|
||||
|
||||
Drivers for automating specific platforms will likely have other requirements. Refer to the
|
||||
documentation of the [Appium driver(s)](../ecosystem/drivers.md) for that platform for additional
|
||||
dependencies. It is almost universally the case that Appium drivers for a given platform will
|
||||
require the developer toolchain and SDKs for that platform to be installed.
|
||||
|
||||
In order to assist with driver requirements, each (official) driver comes with the Appium Doctor tool,
|
||||
which allows to verify if all requirements have been set up. Learn more about how to use this tool in
|
||||
the [Command-Line Usage documentation](../cli/extensions.md#doctor).
|
||||
@@ -0,0 +1,105 @@
|
||||
---
|
||||
hide:
|
||||
- toc
|
||||
|
||||
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.
|
||||
|
||||
```bash
|
||||
cd dotnet-client
|
||||
dotnet new nunit --name appiumtest
|
||||
|
||||
cd appiumtest
|
||||
|
||||
# This will install the latest 5.x version
|
||||
dotnet add package Appium.WebDriver --prerelease
|
||||
dotnet add package Newtonsoft.Json --version 13.0.3
|
||||
```
|
||||
|
||||
Once this is done, your project should have a placeholder file `UnitTest1.cs`. We will replace the code to include the OpenQA namespaces, an initialization of the driver, and the actual test.
|
||||
|
||||
```C# title="UnitTest1.cs"
|
||||
using OpenQA.Selenium;
|
||||
using OpenQA.Selenium.Appium;
|
||||
using OpenQA.Selenium.Appium.Android;
|
||||
using OpenQA.Selenium.Appium.Enums;
|
||||
|
||||
namespace appiumtest;
|
||||
|
||||
public class Tests
|
||||
{
|
||||
private AndroidDriver _driver;
|
||||
|
||||
[OneTimeSetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
var serverUri = new Uri(Environment.GetEnvironmentVariable("APPIUM_HOST") ?? "http://127.0.0.1:4723/");
|
||||
var driverOptions = new AppiumOptions() {
|
||||
AutomationName = AutomationName.AndroidUIAutomator2,
|
||||
PlatformName = "Android",
|
||||
DeviceName = "Android Emulator",
|
||||
};
|
||||
|
||||
driverOptions.AddAdditionalAppiumOption("appPackage", "com.android.settings");
|
||||
driverOptions.AddAdditionalAppiumOption("appActivity", ".Settings");
|
||||
// NoReset assumes the app com.google.android is preinstalled on the emulator
|
||||
driverOptions.AddAdditionalAppiumOption("noReset", true);
|
||||
|
||||
_driver = new AndroidDriver(serverUri, driverOptions, TimeSpan.FromSeconds(180));
|
||||
_driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(10);
|
||||
}
|
||||
|
||||
[OneTimeTearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
_driver.Dispose();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestBattery()
|
||||
{
|
||||
_driver.StartActivity("com.android.settings", ".Settings");
|
||||
_driver.FindElement(By.XPath("//*[@text='Battery']")).Click();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
!!! note
|
||||
|
||||
It's not within the scope of this guide to give a complete run-down on the dotnet client
|
||||
library or everything that's happening here, so we'll leave the code itself unexplained in
|
||||
detail for now. You may want to read up particularly on Appium
|
||||
[Capabilities](../guides/caps.md) in addition to familiarizing yourself with the
|
||||
[dotnet client driver documentation](https://github.com/appium/dotnet-client/) for a fuller explanation
|
||||
of the various API commands you see and what their purpose is.
|
||||
|
||||
Basically, this code is doing the following:
|
||||
|
||||
1. Defining a set of "Capabilities" (parameters) to send to the Appium server so Appium knows what
|
||||
kind of thing you want to automate. Some of these parameters can be overriden using environment variables.
|
||||
1. Starting an Appium session on the built-in Android settings app.
|
||||
1. Finding the "Battery" list item and clicking it.
|
||||
1. Ending the Appium session.
|
||||
|
||||
That's it! Let's give it a try. Before you run the test, make sure that you have an Appium server
|
||||
running in another terminal session, otherwise you'll get an error about not being able to connect
|
||||
to one. Then, you can execute the script:
|
||||
|
||||
```bash
|
||||
dotnet test
|
||||
|
||||
# Example output:
|
||||
# Starting test execution, please wait...
|
||||
# A total of 1 test files matched the specified pattern.
|
||||
|
||||
# Passed! - Failed: 0, Passed: 1, Skipped: 0, Total: 1, Duration: 323 ms - appiumtest.dll (net7.0)
|
||||
```
|
||||
|
||||
If all goes well, you'll see the Settings app open up and navigate to the "Battery" view in the emulator before the app closes again.
|
||||
|
||||
Congratulations, you've started your Appium journey! Read on for some [next steps](./next-steps.md) to explore.
|
||||
@@ -0,0 +1,83 @@
|
||||
---
|
||||
hide:
|
||||
- toc
|
||||
|
||||
title: Write a Test (Ruby)
|
||||
---
|
||||
|
||||
|
||||
The [AppiumLib](https://github.com/appium/ruby_lib) and the [AppiumLibCore](https://github.com/appium/ruby_lib_core) (**recommended**) are official Appium client libraries in Ruby, which are available via gem under the [appium_lib](https://rubygems.org/gems/appium_lib) and the [appium_lib_core](https://rubygems.org/gems/appium_lib_core) package names. The appium_lib_core inherits from the Selenium Ruby Binding, and the appium_lib inherits from the appium_lib_core, so installing these libraries include the selenium binding. We recommend `appium_lib_core` if you need a less complex client-side solution. The `appium_lib` has some useful methods the core does not have, but for the cost of greater complexity and historical methods which may not work in the latest environment.
|
||||
|
||||
As the first step, let's initialize a Gemfile to manage the dependency:
|
||||
|
||||
```bash
|
||||
bundle init
|
||||
```
|
||||
|
||||
Then, you could add Appium Ruby Client dependency as below:
|
||||
|
||||
```bash
|
||||
bundle add appium_lib_core
|
||||
# or
|
||||
# bundle add appium_lib
|
||||
```
|
||||
|
||||
Test code example below uses `test-unit` module, thus please run:
|
||||
|
||||
```bash
|
||||
bundle add test-unit
|
||||
```
|
||||
|
||||
Once these steps has done, your `Gemfile` file should include:
|
||||
|
||||
```ruby title="Gemfile"
|
||||
--8<-- "./sample-code/quickstarts/rb/Gemfile"
|
||||
```
|
||||
|
||||
The `appium_lib_core` is the main part as an Appium client.
|
||||
`appium_lib` has various helper methods, but the driver instance was ordinary designed to be used as a global variable. It could causes an issue to handle the instance.
|
||||
`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.
|
||||
|
||||
```ruby title="test.rb"
|
||||
--8<-- "./sample-code/quickstarts/rb/test.rb"
|
||||
```
|
||||
|
||||
!!! note
|
||||
|
||||
It's not within the scope of this guide to give a complete run-down on the Ruby client
|
||||
library or everything that's happening here, so we'll leave the code itself unexplained in detail for now.
|
||||
|
||||
- You may want to read up particularly on Appium [Capabilities](../guides/caps.md).
|
||||
- [functional test code](https://github.com/appium/ruby_lib_core/tree/master/test/functional) in the appium_lib_core GitHub repository should help to find more working example.
|
||||
- Documentation [appium_lib_core](https://www.rubydoc.info/github/appium/ruby_lib_core) and [appium_lib](https://www.rubydoc.info/github/appium/ruby_lib) also helps to find available methods.
|
||||
|
||||
!!! note
|
||||
|
||||
The sample code is available from [GitHub Appium repository](https://github.com/appium/appium/tree/master/packages/appium/sample-code/quickstarts/rb).
|
||||
|
||||
|
||||
Basically, this code is doing the following:
|
||||
|
||||
1. Defining a set of "Capabilities" (parameters) to send to the Appium server so Appium knows what
|
||||
kind of thing you want to automate.
|
||||
1. Starting an Appium session on the built-in Android settings app.
|
||||
1. Finding the "Battery" list item and clicking it.
|
||||
1. Pausing for a moment purely for visual effect.
|
||||
1. Ending the Appium session.
|
||||
|
||||
That's it! Let's give it a try. Before you run the test, make sure that you have an Appium server
|
||||
running in another terminal session, otherwise you'll get an error about not being able to connect
|
||||
to one. Then, you can execute the script:
|
||||
|
||||
```bash
|
||||
# Please run "bundle install" first if your environment has not run the installation command yet.
|
||||
bundle exec ruby test.rb
|
||||
```
|
||||
|
||||
If all goes well, you'll see the Settings app open up and navigate to the "Battery" view before the
|
||||
app closes again.
|
||||
|
||||
Congratulations, you've started your Appium journey! Read on for some [next steps](./next-steps.md) to explore.
|
||||
@@ -8,7 +8,6 @@ import _ from 'lodash';
|
||||
import path from 'path';
|
||||
import YAML from 'yaml';
|
||||
import {CURRENT_SCHEMA_REV, DRIVER_TYPE, PLUGIN_TYPE} from '../constants';
|
||||
import log from '../logger';
|
||||
import {INSTALL_TYPE_NPM, INSTALL_TYPE_DEV} from './extension-config';
|
||||
import {packageDidChange} from './package-changed';
|
||||
import {migrate} from './manifest-migrations';
|
||||
@@ -177,22 +176,6 @@ export class Manifest {
|
||||
try {
|
||||
const pkg = JSON.parse(await fs.readFile(filepath, 'utf8'));
|
||||
if (isExtension(pkg)) {
|
||||
const extType = isDriver(pkg) ? DRIVER_TYPE : PLUGIN_TYPE;
|
||||
/**
|
||||
* this should only be 'unknown' if the extension's `package.json` is invalid
|
||||
* @type {string}
|
||||
*/
|
||||
const name = isDriver(pkg)
|
||||
? pkg.appium.driverName
|
||||
: isPlugin(pkg)
|
||||
? pkg.appium.pluginName
|
||||
: '(unknown)';
|
||||
if (
|
||||
(isDriver(pkg) && !this.hasDriver(name)) ||
|
||||
(isPlugin(pkg) && !this.hasPlugin(name))
|
||||
) {
|
||||
log.info(`Discovered installed ${extType} "${name}"`);
|
||||
}
|
||||
const installType = devType && hasAppiumDependency ? INSTALL_TYPE_DEV : INSTALL_TYPE_NPM;
|
||||
const changed = this.addExtensionFromPackage(pkg, filepath, installType);
|
||||
didChange = didChange || changed;
|
||||
@@ -403,12 +386,8 @@ export class Manifest {
|
||||
try {
|
||||
const yaml = await fs.readFile(this.#manifestPath, 'utf8');
|
||||
data = YAML.parse(yaml);
|
||||
log.debug(
|
||||
`Parsed manifest file at ${this.#manifestPath}: ${JSON.stringify(data, null, 2)}`
|
||||
);
|
||||
} catch (err) {
|
||||
if (err.code === 'ENOENT') {
|
||||
log.debug(`No manifest file found at ${this.#manifestPath}; creating`);
|
||||
data = _.cloneDeep(INITIAL_MANIFEST_DATA);
|
||||
shouldWrite = true;
|
||||
} else {
|
||||
@@ -434,9 +413,6 @@ export class Manifest {
|
||||
* file will get the latest schema revision, so we can skip the migration.
|
||||
*/
|
||||
if (!shouldWrite && (data.schemaRev ?? 0) < CURRENT_SCHEMA_REV) {
|
||||
log.debug(
|
||||
`Updating manifest schema from rev ${data.schemaRev ?? '(none)'} to ${CURRENT_SCHEMA_REV}`
|
||||
);
|
||||
shouldWrite = await migrate(this);
|
||||
}
|
||||
|
||||
@@ -454,7 +430,6 @@ export class Manifest {
|
||||
* `APPIUM_HOME`, but we don't do that here.
|
||||
*/
|
||||
if (shouldWrite || (hasAppiumDependency && (await packageDidChange(this.appiumHome)))) {
|
||||
log.debug('Discovering newly installed extensions...');
|
||||
shouldWrite = (await this.syncWithInstalledExtensions(hasAppiumDependency)) || shouldWrite;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "appium",
|
||||
"version": "2.11.2",
|
||||
"version": "2.11.3",
|
||||
"description": "Automation for Apps.",
|
||||
"keywords": [
|
||||
"automation",
|
||||
@@ -59,42 +59,41 @@
|
||||
"test:unit": "mocha \"./test/unit/**/*.spec.js\""
|
||||
},
|
||||
"dependencies": {
|
||||
"@appium/base-driver": "^9.11.2",
|
||||
"@appium/base-plugin": "^2.2.42",
|
||||
"@appium/docutils": "^1.0.18",
|
||||
"@appium/logger": "^1.6.0",
|
||||
"@appium/base-driver": "^9.11.3",
|
||||
"@appium/base-plugin": "^2.2.43",
|
||||
"@appium/docutils": "^1.0.19",
|
||||
"@appium/logger": "^1.6.1",
|
||||
"@appium/schema": "^0.6.1",
|
||||
"@appium/support": "^5.1.2",
|
||||
"@appium/types": "^0.21.1",
|
||||
"@appium/support": "^5.1.3",
|
||||
"@appium/types": "^0.21.2",
|
||||
"@sidvind/better-ajv-errors": "2.1.3",
|
||||
"@types/argparse": "2.0.16",
|
||||
"@types/bluebird": "3.5.42",
|
||||
"@types/fancy-log": "2.0.2",
|
||||
"@types/semver": "7.5.8",
|
||||
"@types/teen_process": "2.0.4",
|
||||
"@types/wrap-ansi": "3.0.0",
|
||||
"ajv": "8.16.0",
|
||||
"ajv": "8.17.1",
|
||||
"ajv-formats": "3.0.1",
|
||||
"argparse": "2.0.1",
|
||||
"async-lock": "1.4.1",
|
||||
"asyncbox": "3.0.0",
|
||||
"axios": "1.7.2",
|
||||
"axios": "1.7.3",
|
||||
"bluebird": "3.7.2",
|
||||
"cross-env": "7.0.3",
|
||||
"lilconfig": "3.1.2",
|
||||
"lodash": "4.17.21",
|
||||
"lru-cache": "10.3.0",
|
||||
"lru-cache": "10.4.3",
|
||||
"ora": "5.4.1",
|
||||
"package-changed": "3.0.0",
|
||||
"resolve-from": "5.0.0",
|
||||
"semver": "7.6.2",
|
||||
"semver": "7.6.3",
|
||||
"source-map-support": "0.5.21",
|
||||
"teen_process": "2.2.0",
|
||||
"type-fest": "4.21.0",
|
||||
"winston": "3.13.0",
|
||||
"type-fest": "4.23.0",
|
||||
"winston": "3.13.1",
|
||||
"wrap-ansi": "7.0.0",
|
||||
"ws": "8.18.0",
|
||||
"yaml": "2.4.5"
|
||||
"yaml": "2.5.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.17.0 || ^16.13.0 || >=18.0.0",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"devDependencies": {
|
||||
"webdriverio": "8.39.1"
|
||||
"webdriverio": "8.40.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,13 +4,14 @@
|
||||
|
||||
const path = require('path');
|
||||
const yaml = require('yaml');
|
||||
const {fs, util} = require('@appium/support');
|
||||
const {fs, util, logger} = require('@appium/support');
|
||||
const validate = require('validate.js');
|
||||
const Handlebars = require('handlebars');
|
||||
const _ = require('lodash');
|
||||
const {asyncify} = require('asyncbox');
|
||||
const url = require('url');
|
||||
const log = require('fancy-log');
|
||||
|
||||
const log = logger.getLogger('YamlParser');
|
||||
|
||||
validate.validators.array = function array(value, options, key, attributes) {
|
||||
if (attributes[key] && !validate.isArray(attributes[key])) {
|
||||
@@ -276,7 +277,7 @@ async function generateCommands() {
|
||||
await registerSpecUrlHelper();
|
||||
|
||||
const commands = path.resolve(YAML_DIR, 'commands/**/*.yml');
|
||||
log('Traversing YML files', commands);
|
||||
log.info('Traversing YML files', commands);
|
||||
await fs.rimraf(path.resolve(rootFolder, 'docs', 'en', 'commands'));
|
||||
|
||||
// get the template from which the md files will be created
|
||||
@@ -288,7 +289,7 @@ async function generateCommands() {
|
||||
let fileCount = 0;
|
||||
for (const filename of await fs.glob(commands)) {
|
||||
const relativeFilename = path.relative(YAML_DIR, filename);
|
||||
log(`Rendering file: ${filename} ${relativeFilename}`);
|
||||
log.info(`Rendering file: ${filename} ${relativeFilename}`);
|
||||
|
||||
// Translate the YML specs to JSON
|
||||
const inputYML = await fs.readFile(filename, 'utf8');
|
||||
@@ -309,13 +310,13 @@ async function generateCommands() {
|
||||
relativeFilename.length - ext.length
|
||||
)}.md`;
|
||||
const outfile = path.resolve(rootFolder, 'docs', 'en', markdownPath);
|
||||
log(` Writing to: ${outfile}`);
|
||||
log.info(` Writing to: ${outfile}`);
|
||||
await fs.mkdirp(path.dirname(outfile));
|
||||
await fs.writeFile(outfile, markdown, 'utf8');
|
||||
|
||||
fileCount++;
|
||||
}
|
||||
log(`Done writing ${fileCount} command documents`);
|
||||
log.info(`Done writing ${fileCount} command documents`);
|
||||
}
|
||||
|
||||
async function generateCommandIndex() {
|
||||
@@ -351,7 +352,7 @@ async function generateCommandIndex() {
|
||||
);
|
||||
|
||||
async function writeIndex(index, commands, indexPath) {
|
||||
log(`Creating API index '${index}'`);
|
||||
log.info(`Creating API index '${index}'`);
|
||||
const commandMarkdown = commandTemplate({
|
||||
commands,
|
||||
path: indexPath,
|
||||
@@ -361,7 +362,7 @@ async function generateCommandIndex() {
|
||||
|
||||
const apiIndex = path.resolve(rootFolder, 'docs', 'en', 'about-appium', 'api.md');
|
||||
await writeIndex(apiIndex, commands);
|
||||
log(`Done writing main API index`);
|
||||
log.info(`Done writing main API index`);
|
||||
|
||||
async function writeIndividualIndexes(command) {
|
||||
if (!util.hasValue(command.commands)) {
|
||||
|
||||
@@ -3,6 +3,23 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [9.11.3](https://github.com/appium/appium/compare/@appium/base-driver@9.11.2...@appium/base-driver@9.11.3) (2024-08-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **base-driver:** calls startNewCommandTimeout before returning NotYetImplementedError ([#20380](https://github.com/appium/appium/issues/20380)) ([6a97fe4](https://github.com/appium/appium/commit/6a97fe48aebb85424fa7e5be5de2eb5524cf0db6))
|
||||
* **base-driver:** change required to option for setWindowRect to follow w3c ([#20391](https://github.com/appium/appium/issues/20391)) ([9eb65f7](https://github.com/appium/appium/commit/9eb65f7ad4efc89bbf5af89791bb9eb81fc2248f))
|
||||
* **base-driver:** Start command timeout even if the recent command has thrown an exception ([#20379](https://github.com/appium/appium/issues/20379)) ([139dbae](https://github.com/appium/appium/commit/139dbaeef6963f79a1de38541181bae8953e00e1))
|
||||
* **base-driver:** update dependency path-to-regexp to v7.1.0 ([#20376](https://github.com/appium/appium/issues/20376)) ([6488a0a](https://github.com/appium/appium/commit/6488a0a4e1dd08f4eda6e40e33a9b31d31edda1c))
|
||||
* **driver-test-support:** update dependency @types/lodash to v4.17.7 ([#20382](https://github.com/appium/appium/issues/20382)) ([d5ef0f9](https://github.com/appium/appium/commit/d5ef0f9c8608ba545988c3016cefd0669f2400dc))
|
||||
* **logger:** update dependency lru-cache to v10.4.3 ([#20364](https://github.com/appium/appium/issues/20364)) ([8d79467](https://github.com/appium/appium/commit/8d79467da8a0733ac3e49b9152bd6905989a57ca))
|
||||
* **support:** update dependency axios to v1.7.3 ([1ca77c6](https://github.com/appium/appium/commit/1ca77c6dedbff4552aba9c97cf5406c7552d1a01))
|
||||
* **types:** update dependency type-fest to v4.22.0 ([#20387](https://github.com/appium/appium/issues/20387)) ([47405dd](https://github.com/appium/appium/commit/47405dda8a5de17c72fb721b0c043e0dd4f6b35a))
|
||||
* **types:** update dependency type-fest to v4.23.0 ([#20396](https://github.com/appium/appium/issues/20396)) ([0e8e3c7](https://github.com/appium/appium/commit/0e8e3c71441d02d22f015f08df5223909f5fbb93))
|
||||
|
||||
|
||||
|
||||
## [9.11.2](https://github.com/appium/appium/compare/@appium/base-driver@9.11.1...@appium/base-driver@9.11.2) (2024-07-10)
|
||||
|
||||
|
||||
|
||||
@@ -98,41 +98,55 @@ export class BaseDriver<
|
||||
|
||||
// If we don't have this command, it must not be implemented
|
||||
if (!this[cmd]) {
|
||||
await this.startNewCommandTimeout();
|
||||
throw new errors.NotYetImplementedError();
|
||||
}
|
||||
|
||||
let unexpectedShutdownListener;
|
||||
const commandExecutor = async () =>
|
||||
await B.race([
|
||||
this[cmd](...args),
|
||||
new B((resolve, reject) => {
|
||||
unexpectedShutdownListener = reject;
|
||||
this.eventEmitter.on(ON_UNEXPECTED_SHUTDOWN_EVENT, unexpectedShutdownListener);
|
||||
}),
|
||||
]).finally(() => {
|
||||
if (unexpectedShutdownListener) {
|
||||
const runCommandPromise = async () => {
|
||||
let unexpectedShutdownRejecter: ((error?: any) => void) | null = null;
|
||||
let unexpectedShutdownResolver: ((x?: unknown) => void) | null = null;
|
||||
let wasSessionShutdownUnexpectedly = false;
|
||||
const onUnexpectedShutdown = (e: Error) => {
|
||||
wasSessionShutdownUnexpectedly = true;
|
||||
unexpectedShutdownRejecter?.(e);
|
||||
};
|
||||
try {
|
||||
return await B.race([
|
||||
this[cmd](...args),
|
||||
// This promise is needed to monitor if the session has been
|
||||
// shut down unexpectedly while the command was running
|
||||
new B((resolve, reject) => {
|
||||
unexpectedShutdownResolver = resolve;
|
||||
unexpectedShutdownRejecter = reject;
|
||||
this.eventEmitter.once(ON_UNEXPECTED_SHUTDOWN_EVENT, onUnexpectedShutdown);
|
||||
})
|
||||
]);
|
||||
} finally {
|
||||
if (unexpectedShutdownRejecter && unexpectedShutdownResolver) {
|
||||
// This is needed to prevent memory leaks
|
||||
this.eventEmitter.removeListener(
|
||||
ON_UNEXPECTED_SHUTDOWN_EVENT,
|
||||
unexpectedShutdownListener,
|
||||
);
|
||||
unexpectedShutdownListener = null;
|
||||
this.eventEmitter.removeListener(ON_UNEXPECTED_SHUTDOWN_EVENT, onUnexpectedShutdown);
|
||||
unexpectedShutdownRejecter = null;
|
||||
// @ts-ignore typescript cannot understand this
|
||||
unexpectedShutdownResolver?.();
|
||||
unexpectedShutdownResolver = null;
|
||||
}
|
||||
});
|
||||
const res = this.isCommandsQueueEnabled
|
||||
? await this.commandsQueueGuard.acquire(BaseDriver.name, commandExecutor)
|
||||
: await commandExecutor();
|
||||
|
||||
// if we have set a new command timeout (which is the default), start a
|
||||
// timer once we've finished executing this command. If we don't clear
|
||||
// the timer (which is done when a new command comes in), we will trigger
|
||||
// automatic session deletion in this.onCommandTimeout. Of course we don't
|
||||
// want to trigger the timer when the user is shutting down the session
|
||||
// intentionally
|
||||
if (this.isCommandsQueueEnabled && cmd !== DELETE_SESSION_COMMAND) {
|
||||
// resetting existing timeout
|
||||
await this.startNewCommandTimeout();
|
||||
}
|
||||
// if we have set a new command timeout (which is the default), start a
|
||||
// timer once we've finished executing this command. If we don't clear
|
||||
// the timer (which is done when a new command comes in), we will trigger
|
||||
// automatic session deletion in this.onCommandTimeout. Of course we don't
|
||||
// want to trigger the timer when the user is shutting down the session
|
||||
// intentionally
|
||||
if (!wasSessionShutdownUnexpectedly && this.isCommandsQueueEnabled && cmd !== DELETE_SESSION_COMMAND) {
|
||||
// resetting existing timeout
|
||||
await this.startNewCommandTimeout();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const res = this.isCommandsQueueEnabled
|
||||
? await this.commandsQueueGuard.acquire(BaseDriver.name, runCommandPromise)
|
||||
: await runCommandPromise();
|
||||
|
||||
// log timing information about this command
|
||||
const endTime = Date.now();
|
||||
|
||||
@@ -824,7 +824,7 @@ export const METHOD_MAP = /** @type {const} */ ({
|
||||
GET: {command: 'getWindowRect'},
|
||||
POST: {
|
||||
command: 'setWindowRect',
|
||||
payloadParams: {required: ['x', 'y', 'width', 'height']},
|
||||
payloadParams: {optional: ['x', 'y', 'width', 'height']},
|
||||
},
|
||||
},
|
||||
'/session/:sessionId/window/maximize': {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@appium/base-driver",
|
||||
"version": "9.11.2",
|
||||
"version": "9.11.3",
|
||||
"description": "Base driver class for Appium drivers",
|
||||
"keywords": [
|
||||
"automation",
|
||||
@@ -44,30 +44,30 @@
|
||||
"test:types": "tsd"
|
||||
},
|
||||
"dependencies": {
|
||||
"@appium/support": "^5.1.2",
|
||||
"@appium/types": "^0.21.1",
|
||||
"@appium/support": "^5.1.3",
|
||||
"@appium/types": "^0.21.2",
|
||||
"@colors/colors": "1.6.0",
|
||||
"@types/async-lock": "1.4.2",
|
||||
"@types/bluebird": "3.5.42",
|
||||
"@types/express": "4.17.21",
|
||||
"@types/lodash": "4.17.6",
|
||||
"@types/lodash": "4.17.7",
|
||||
"@types/method-override": "0.0.35",
|
||||
"@types/serve-favicon": "2.5.7",
|
||||
"async-lock": "1.4.1",
|
||||
"asyncbox": "3.0.0",
|
||||
"axios": "1.7.2",
|
||||
"axios": "1.7.3",
|
||||
"bluebird": "3.7.2",
|
||||
"body-parser": "1.20.2",
|
||||
"express": "4.19.2",
|
||||
"http-status-codes": "2.3.0",
|
||||
"lodash": "4.17.21",
|
||||
"lru-cache": "10.3.0",
|
||||
"lru-cache": "10.4.3",
|
||||
"method-override": "3.0.0",
|
||||
"morgan": "1.10.0",
|
||||
"path-to-regexp": "7.0.0",
|
||||
"path-to-regexp": "7.1.0",
|
||||
"serve-favicon": "2.5.0",
|
||||
"source-map-support": "0.5.21",
|
||||
"type-fest": "4.21.0",
|
||||
"type-fest": "4.23.0",
|
||||
"validate.js": "0.13.1"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [2.2.43](https://github.com/appium/appium/compare/@appium/base-plugin@2.2.42...@appium/base-plugin@2.2.43) (2024-08-07)
|
||||
|
||||
**Note:** Version bump only for package @appium/base-plugin
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.2.42](https://github.com/appium/appium/compare/@appium/base-plugin@2.2.41...@appium/base-plugin@2.2.42) (2024-07-10)
|
||||
|
||||
**Note:** Version bump only for package @appium/base-plugin
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@appium/base-plugin",
|
||||
"version": "2.2.42",
|
||||
"version": "2.2.43",
|
||||
"description": "The base plugin used to create Appium 2.0 plugins",
|
||||
"keywords": [
|
||||
"automation",
|
||||
@@ -40,8 +40,8 @@
|
||||
"test:unit": "mocha \"./test/unit/**/*.spec.js\""
|
||||
},
|
||||
"dependencies": {
|
||||
"@appium/base-driver": "^9.11.2",
|
||||
"@appium/support": "^5.1.2"
|
||||
"@appium/base-driver": "^9.11.3",
|
||||
"@appium/support": "^5.1.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.17.0 || ^16.13.0 || >=18.0.0",
|
||||
|
||||
@@ -3,6 +3,16 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [2.1.10](https://github.com/appium/appium/compare/@appium/doctor@2.1.9...@appium/doctor@2.1.10) (2024-08-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **doctor:** update dependency appium-adb to v12.5.0 ([b7b97a2](https://github.com/appium/appium/commit/b7b97a24d66e5c561b4c9559a4b0aea25503f39a))
|
||||
* **doctor:** update dependency appium-adb to v12.5.1 ([82b7515](https://github.com/appium/appium/commit/82b7515867c10c20843dd02c37c7a2e9014c3703))
|
||||
|
||||
|
||||
|
||||
## [2.1.9](https://github.com/appium/appium/compare/@appium/doctor@2.1.8...@appium/doctor@2.1.9) (2024-07-10)
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@appium/doctor",
|
||||
"version": "2.1.9",
|
||||
"version": "2.1.10",
|
||||
"description": "Test environment for fitness to run Appium",
|
||||
"keywords": [
|
||||
"automation",
|
||||
@@ -44,11 +44,11 @@
|
||||
"test:unit": "mocha \"./test/unit/**/*.spec.js\""
|
||||
},
|
||||
"dependencies": {
|
||||
"@appium/support": "^5.1.2",
|
||||
"@appium/support": "^5.1.3",
|
||||
"@colors/colors": "1.6.0",
|
||||
"@types/bluebird": "3.5.42",
|
||||
"@types/teen_process": "2.0.4",
|
||||
"appium-adb": "12.4.8",
|
||||
"appium-adb": "12.5.1",
|
||||
"bluebird": "3.7.2",
|
||||
"inquirer": "9.2.12",
|
||||
"lodash": "4.17.21",
|
||||
|
||||
@@ -3,6 +3,19 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.0.19](https://github.com/appium/appium/compare/@appium/docutils@1.0.18...@appium/docutils@1.0.19) (2024-08-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **docutils:** update dependency typescript to v5.5.4 ([e08e7eb](https://github.com/appium/appium/commit/e08e7ebec24866611e0718f0d038e9566abd3dd6))
|
||||
* **docutils:** update dependency yaml to v2.5.0 ([0c755f5](https://github.com/appium/appium/commit/0c755f503c49e459315616e51be0c0776089b2bb))
|
||||
* **support:** update dependency semver to v7.6.3 ([5a9ab97](https://github.com/appium/appium/commit/5a9ab9792402e31d3903fb1a3b08329a67b6a2a2))
|
||||
* **types:** update dependency type-fest to v4.22.0 ([#20387](https://github.com/appium/appium/issues/20387)) ([47405dd](https://github.com/appium/appium/commit/47405dda8a5de17c72fb721b0c043e0dd4f6b35a))
|
||||
* **types:** update dependency type-fest to v4.23.0 ([#20396](https://github.com/appium/appium/issues/20396)) ([0e8e3c7](https://github.com/appium/appium/commit/0e8e3c71441d02d22f015f08df5223909f5fbb93))
|
||||
|
||||
|
||||
|
||||
## [1.0.18](https://github.com/appium/appium/compare/@appium/docutils@1.0.17...@appium/docutils@1.0.18) (2024-07-10)
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@appium/docutils",
|
||||
"version": "1.0.18",
|
||||
"version": "1.0.19",
|
||||
"description": "Documentation generation utilities for Appium and related projects",
|
||||
"keywords": [
|
||||
"automation",
|
||||
@@ -48,7 +48,7 @@
|
||||
"start": "node ./build/lib/cli/index.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@appium/support": "^5.1.2",
|
||||
"@appium/support": "^5.1.3",
|
||||
"@appium/tsconfig": "^0.3.3",
|
||||
"@sliphua/lilconfig-ts-loader": "3.2.2",
|
||||
"@types/which": "3.0.4",
|
||||
@@ -60,12 +60,12 @@
|
||||
"lodash": "4.17.21",
|
||||
"pkg-dir": "5.0.0",
|
||||
"read-pkg": "5.2.0",
|
||||
"semver": "7.6.2",
|
||||
"semver": "7.6.3",
|
||||
"source-map-support": "0.5.21",
|
||||
"teen_process": "2.2.0",
|
||||
"type-fest": "4.21.0",
|
||||
"typescript": "5.5.3",
|
||||
"yaml": "2.4.5",
|
||||
"type-fest": "4.23.0",
|
||||
"typescript": "5.5.4",
|
||||
"yaml": "2.5.0",
|
||||
"yargs": "17.7.2",
|
||||
"yargs-parser": "21.1.1"
|
||||
},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
mkdocs==1.6.0
|
||||
mkdocs-git-revision-date-localized-plugin==1.2.6
|
||||
mkdocs-material==9.5.28
|
||||
mkdocs-material==9.5.31
|
||||
mkdocs-redirects==1.2.1
|
||||
mike==2.1.2
|
||||
|
||||
@@ -3,6 +3,18 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [0.6.7](https://github.com/appium/appium/compare/@appium/driver-test-support@0.6.6...@appium/driver-test-support@0.6.7) (2024-08-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **driver-test-support:** update dependency @types/lodash to v4.17.7 ([#20382](https://github.com/appium/appium/issues/20382)) ([d5ef0f9](https://github.com/appium/appium/commit/d5ef0f9c8608ba545988c3016cefd0669f2400dc))
|
||||
* **support:** update dependency axios to v1.7.3 ([1ca77c6](https://github.com/appium/appium/commit/1ca77c6dedbff4552aba9c97cf5406c7552d1a01))
|
||||
* **types:** update dependency type-fest to v4.22.0 ([#20387](https://github.com/appium/appium/issues/20387)) ([47405dd](https://github.com/appium/appium/commit/47405dda8a5de17c72fb721b0c043e0dd4f6b35a))
|
||||
* **types:** update dependency type-fest to v4.23.0 ([#20396](https://github.com/appium/appium/issues/20396)) ([0e8e3c7](https://github.com/appium/appium/commit/0e8e3c71441d02d22f015f08df5223909f5fbb93))
|
||||
|
||||
|
||||
|
||||
## [0.6.6](https://github.com/appium/appium/compare/@appium/driver-test-support@0.6.5...@appium/driver-test-support@0.6.6) (2024-07-10)
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@appium/driver-test-support",
|
||||
"version": "0.6.6",
|
||||
"version": "0.6.7",
|
||||
"description": "Test utilities for Appium drivers",
|
||||
"keywords": [
|
||||
"automation",
|
||||
@@ -41,10 +41,10 @@
|
||||
},
|
||||
"types": "./build/lib/index.d.ts",
|
||||
"dependencies": {
|
||||
"@appium/types": "^0.21.1",
|
||||
"@types/lodash": "4.17.6",
|
||||
"@appium/types": "^0.21.2",
|
||||
"@types/lodash": "4.17.7",
|
||||
"@types/stoppable": "1.1.3",
|
||||
"axios": "1.7.2",
|
||||
"axios": "1.7.3",
|
||||
"bluebird": "3.7.2",
|
||||
"chai": "5.1.1",
|
||||
"chai-as-promised": "8.0.0",
|
||||
@@ -53,7 +53,7 @@
|
||||
"sinon": "18.0.0",
|
||||
"source-map-support": "0.5.21",
|
||||
"stoppable": "1.1.0",
|
||||
"type-fest": "4.21.0"
|
||||
"type-fest": "4.23.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"appium": "^2.0.0-beta.43",
|
||||
|
||||
@@ -3,6 +3,15 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [3.0.33](https://github.com/appium/appium/compare/@appium/execute-driver-plugin@3.0.32...@appium/execute-driver-plugin@3.0.33) (2024-08-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **execute-driver-plugin:** update dependency webdriverio to v8.40.0 ([#20442](https://github.com/appium/appium/issues/20442)) ([b4612e0](https://github.com/appium/appium/commit/b4612e0c7a21902a3cab658fbdc05f3b48c55d0d))
|
||||
|
||||
|
||||
|
||||
## [3.0.32](https://github.com/appium/appium/compare/@appium/execute-driver-plugin@3.0.31...@appium/execute-driver-plugin@3.0.32) (2024-07-10)
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@appium/execute-driver-plugin",
|
||||
"version": "3.0.32",
|
||||
"version": "3.0.33",
|
||||
"description": "Plugin for batching and executing driver commands with Appiums",
|
||||
"keywords": [
|
||||
"automation",
|
||||
@@ -43,7 +43,7 @@
|
||||
"lodash": "4.17.21",
|
||||
"source-map-support": "0.5.21",
|
||||
"vm2": "3.9.19",
|
||||
"webdriverio": "8.39.1"
|
||||
"webdriverio": "8.40.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"appium": "^2.0.0-beta.35"
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [3.1.47](https://github.com/appium/appium/compare/@appium/fake-plugin@3.1.46...@appium/fake-plugin@3.1.47) (2024-08-07)
|
||||
|
||||
**Note:** Version bump only for package @appium/fake-plugin
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.1.46](https://github.com/appium/appium/compare/@appium/fake-plugin@3.1.45...@appium/fake-plugin@3.1.46) (2024-07-10)
|
||||
|
||||
**Note:** Version bump only for package @appium/fake-plugin
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@appium/fake-plugin",
|
||||
"version": "3.1.46",
|
||||
"version": "3.1.47",
|
||||
"description": "A fake Appium 2.0 plugin",
|
||||
"keywords": [
|
||||
"automation",
|
||||
@@ -39,8 +39,8 @@
|
||||
"test:unit": "mocha \"./test/unit/**/*.spec.js\""
|
||||
},
|
||||
"dependencies": {
|
||||
"@appium/base-plugin": "^2.2.42",
|
||||
"@appium/support": "^5.1.2",
|
||||
"@appium/base-plugin": "^2.2.43",
|
||||
"@appium/support": "^5.1.3",
|
||||
"@types/bluebird": "3.5.42",
|
||||
"bluebird": "3.7.2",
|
||||
"lodash": "4.17.21",
|
||||
|
||||
@@ -3,6 +3,15 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [3.0.18](https://github.com/appium/appium/compare/@appium/images-plugin@3.0.17...@appium/images-plugin@3.0.18) (2024-08-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **logger:** update dependency lru-cache to v10.4.3 ([#20364](https://github.com/appium/appium/issues/20364)) ([8d79467](https://github.com/appium/appium/commit/8d79467da8a0733ac3e49b9152bd6905989a57ca))
|
||||
|
||||
|
||||
|
||||
## [3.0.17](https://github.com/appium/appium/compare/@appium/images-plugin@3.0.16...@appium/images-plugin@3.0.17) (2024-07-10)
|
||||
|
||||
**Note:** Version bump only for package @appium/images-plugin
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@appium/images-plugin",
|
||||
"version": "3.0.17",
|
||||
"version": "3.0.18",
|
||||
"description": "Plugin for working with images and image elements in Appium",
|
||||
"keywords": [
|
||||
"automation",
|
||||
@@ -40,9 +40,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@appium/opencv": "^3.0.5",
|
||||
"@appium/support": "^5.1.2",
|
||||
"@appium/support": "^5.1.3",
|
||||
"lodash": "4.17.21",
|
||||
"lru-cache": "10.3.0",
|
||||
"lru-cache": "10.4.3",
|
||||
"sharp": "0.33.4",
|
||||
"source-map-support": "0.5.21"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,16 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.6.1](https://github.com/appium/appium/compare/@appium/logger@1.6.0...@appium/logger@1.6.1) (2024-08-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **logger:** update dependency lru-cache to v10.4.3 ([#20364](https://github.com/appium/appium/issues/20364)) ([8d79467](https://github.com/appium/appium/commit/8d79467da8a0733ac3e49b9152bd6905989a57ca))
|
||||
* **support:** Print an empty string if no arguments are provided to a logging function ([#20424](https://github.com/appium/appium/issues/20424)) ([885570e](https://github.com/appium/appium/commit/885570e7caec486765c1baa67131f0b3adf1daf7))
|
||||
|
||||
|
||||
|
||||
## [1.6.0](https://github.com/appium/appium/compare/@appium/logger@1.5.0...@appium/logger@1.6.0) (2024-07-10)
|
||||
|
||||
|
||||
|
||||
@@ -245,9 +245,7 @@ export class Log extends EventEmitter implements Logger {
|
||||
if (stack) {
|
||||
messageArguments.unshift(`${stack}\n`);
|
||||
}
|
||||
const formattedMessage: string = messageArguments.length
|
||||
? util.format(...messageArguments)
|
||||
: '';
|
||||
const formattedMessage = util.format(...messageArguments);
|
||||
|
||||
const m: MessageObject = {
|
||||
id: this._id++,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@appium/logger",
|
||||
"version": "1.6.0",
|
||||
"version": "1.6.1",
|
||||
"author": "https://github.com/appium",
|
||||
"description": "A Universal Logger For The Appium Ecosystem",
|
||||
"repository": {
|
||||
@@ -29,7 +29,7 @@
|
||||
"dependencies": {
|
||||
"console-control-strings": "1.1.0",
|
||||
"lodash": "4.17.21",
|
||||
"lru-cache": "10.3.0",
|
||||
"lru-cache": "10.4.3",
|
||||
"set-blocking": "2.0.0"
|
||||
},
|
||||
"license": "ISC",
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [0.3.41](https://github.com/appium/appium/compare/@appium/plugin-test-support@0.3.40...@appium/plugin-test-support@0.3.41) (2024-08-07)
|
||||
|
||||
**Note:** Version bump only for package @appium/plugin-test-support
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.3.40](https://github.com/appium/appium/compare/@appium/plugin-test-support@0.3.39...@appium/plugin-test-support@0.3.40) (2024-07-10)
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@appium/plugin-test-support",
|
||||
"version": "0.3.40",
|
||||
"version": "0.3.41",
|
||||
"description": "Testing utilities for Appium plugins",
|
||||
"keywords": [
|
||||
"automation",
|
||||
@@ -41,7 +41,7 @@
|
||||
"test:unit": "exit 0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@appium/types": "^0.21.1",
|
||||
"@appium/types": "^0.21.2",
|
||||
"get-port": "5.1.1",
|
||||
"log-symbols": "4.1.0",
|
||||
"source-map-support": "0.5.21",
|
||||
|
||||
@@ -3,6 +3,21 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [5.1.3](https://github.com/appium/appium/compare/@appium/support@5.1.2...@appium/support@5.1.3) (2024-08-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **support:** Allow temp dir which do not have a parent folder ([#20440](https://github.com/appium/appium/issues/20440)) ([8a307a0](https://github.com/appium/appium/commit/8a307a078b48176dfd7cf7013cd5ca405fc6d870))
|
||||
* **support:** Print an empty string if no arguments are provided to a logging function ([#20424](https://github.com/appium/appium/issues/20424)) ([885570e](https://github.com/appium/appium/commit/885570e7caec486765c1baa67131f0b3adf1daf7))
|
||||
* **support:** update dependency axios to v1.7.3 ([1ca77c6](https://github.com/appium/appium/commit/1ca77c6dedbff4552aba9c97cf5406c7552d1a01))
|
||||
* **support:** update dependency glob to v10.4.5 ([#20365](https://github.com/appium/appium/issues/20365)) ([7159428](https://github.com/appium/appium/commit/715942845b8375f96dcd6c21f0273b2a386b19a3))
|
||||
* **support:** update dependency semver to v7.6.3 ([5a9ab97](https://github.com/appium/appium/commit/5a9ab9792402e31d3903fb1a3b08329a67b6a2a2))
|
||||
* **types:** update dependency type-fest to v4.22.0 ([#20387](https://github.com/appium/appium/issues/20387)) ([47405dd](https://github.com/appium/appium/commit/47405dda8a5de17c72fb721b0c043e0dd4f6b35a))
|
||||
* **types:** update dependency type-fest to v4.23.0 ([#20396](https://github.com/appium/appium/issues/20396)) ([0e8e3c7](https://github.com/appium/appium/commit/0e8e3c71441d02d22f015f08df5223909f5fbb93))
|
||||
|
||||
|
||||
|
||||
## [5.1.2](https://github.com/appium/appium/compare/@appium/support@5.1.1...@appium/support@5.1.2) (2024-07-10)
|
||||
|
||||
|
||||
|
||||
@@ -67,8 +67,12 @@ export function getLogger(prefix = null) {
|
||||
for (const level of LEVELS) {
|
||||
wrappedLogger[level] = /** @param {any[]} args */ function (...args) {
|
||||
const finalPrefix = getFinalPrefix(this.prefix, isDebugTimestampLoggingEnabled);
|
||||
// @ts-ignore This is OK
|
||||
logger[level](finalPrefix, ...args);
|
||||
if (args.length) {
|
||||
// @ts-ignore This is OK
|
||||
logger[level](finalPrefix, ...args);
|
||||
} else {
|
||||
logger[level](finalPrefix, '');
|
||||
}
|
||||
};
|
||||
}
|
||||
if (!usingGlobalLog) {
|
||||
|
||||
@@ -31,7 +31,7 @@ async function tempDir() {
|
||||
);
|
||||
// creates a temp directory using the date and a random string
|
||||
|
||||
await fs.mkdir(filePath);
|
||||
await fs.mkdir(filePath, {recursive: true});
|
||||
|
||||
return filePath;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@appium/support",
|
||||
"version": "5.1.2",
|
||||
"version": "5.1.3",
|
||||
"description": "Support libs used across appium packages",
|
||||
"keywords": [
|
||||
"automation",
|
||||
@@ -41,9 +41,9 @@
|
||||
"test:unit": "mocha \"./test/unit/**/*.spec.js\""
|
||||
},
|
||||
"dependencies": {
|
||||
"@appium/logger": "^1.6.0",
|
||||
"@appium/logger": "^1.6.1",
|
||||
"@appium/tsconfig": "^0.3.3",
|
||||
"@appium/types": "^0.21.1",
|
||||
"@appium/types": "^0.21.2",
|
||||
"@colors/colors": "1.6.0",
|
||||
"@types/archiver": "6.0.2",
|
||||
"@types/base64-stream": "1.0.5",
|
||||
@@ -61,14 +61,14 @@
|
||||
"@types/uuid": "10.0.0",
|
||||
"@types/which": "3.0.4",
|
||||
"archiver": "7.0.1",
|
||||
"axios": "1.7.2",
|
||||
"axios": "1.7.3",
|
||||
"base64-stream": "1.0.0",
|
||||
"bluebird": "3.7.2",
|
||||
"bplist-creator": "0.1.1",
|
||||
"bplist-parser": "0.3.2",
|
||||
"form-data": "4.0.0",
|
||||
"get-stream": "6.0.1",
|
||||
"glob": "10.4.2",
|
||||
"glob": "10.4.5",
|
||||
"jsftp": "2.1.3",
|
||||
"klaw": "4.1.0",
|
||||
"lockfile": "1.0.4",
|
||||
@@ -84,12 +84,12 @@
|
||||
"read-pkg": "5.2.0",
|
||||
"resolve-from": "5.0.0",
|
||||
"sanitize-filename": "1.6.3",
|
||||
"semver": "7.6.2",
|
||||
"semver": "7.6.3",
|
||||
"shell-quote": "1.8.1",
|
||||
"source-map-support": "0.5.21",
|
||||
"supports-color": "8.1.1",
|
||||
"teen_process": "2.2.0",
|
||||
"type-fest": "4.21.0",
|
||||
"type-fest": "4.23.0",
|
||||
"uuid": "10.0.0",
|
||||
"which": "4.0.0",
|
||||
"yauzl": "3.1.3"
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [3.0.47](https://github.com/appium/appium/compare/@appium/test-support@3.0.46...@appium/test-support@3.0.47) (2024-08-07)
|
||||
|
||||
**Note:** Version bump only for package @appium/test-support
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.0.46](https://github.com/appium/appium/compare/@appium/test-support@3.0.45...@appium/test-support@3.0.46) (2024-07-10)
|
||||
|
||||
**Note:** Version bump only for package @appium/test-support
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@appium/test-support",
|
||||
"version": "3.0.46",
|
||||
"version": "3.0.47",
|
||||
"description": "A collection of test utilities used across Appium packages",
|
||||
"keywords": [
|
||||
"automation",
|
||||
@@ -45,7 +45,7 @@
|
||||
"test:unit": "mocha \"./test/unit/**/*.spec.js\""
|
||||
},
|
||||
"dependencies": {
|
||||
"@appium/support": "^5.1.2",
|
||||
"@appium/support": "^5.1.3",
|
||||
"@colors/colors": "1.6.0",
|
||||
"bluebird": "3.7.2",
|
||||
"lodash": "4.17.21",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user