mirror of
https://github.com/appium/appium.git
synced 2026-10-31 22:32:24 +08:00
Compare commits
88
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e473b05130 | ||
|
|
dd64a48fca | ||
|
|
4473ef4da8 | ||
|
|
f9f88e66b6 | ||
|
|
715fd03c45 | ||
|
|
8a664a4eef | ||
|
|
dc24864cbb | ||
|
|
c102d8fd8e | ||
|
|
703085bc54 | ||
|
|
47497bc8da | ||
|
|
ea00e3aa18 | ||
|
|
f8ddac629d | ||
|
|
eb1b156146 | ||
|
|
c8ac9c40bb | ||
|
|
c4337fc3e7 | ||
|
|
686a630fbd | ||
|
|
5a227d23d2 | ||
|
|
c7900f4a16 | ||
|
|
312a1c53f3 | ||
|
|
ca456e5236 | ||
|
|
3dc7336b5f | ||
|
|
224f5247b0 | ||
|
|
c6a080f7ad | ||
|
|
b3c877f66d | ||
|
|
419661013b | ||
|
|
9b08c209f9 | ||
|
|
ab07a0bc90 | ||
|
|
91d9a3ef5d | ||
|
|
4c9a42062c | ||
|
|
30be097e15 | ||
|
|
97ae82b2a2 | ||
|
|
aea0ccfa00 | ||
|
|
e934b61856 | ||
|
|
99e96502b9 | ||
|
|
848f2b2396 | ||
|
|
a1e3890681 | ||
|
|
21e2aabc39 | ||
|
|
0a7490ed53 | ||
|
|
1c2c119461 | ||
|
|
49da3c6c35 | ||
|
|
0b27739768 | ||
|
|
ce32226cb5 | ||
|
|
f8b57999e5 | ||
|
|
52c6c6933b | ||
|
|
4c089440e6 | ||
|
|
94f93a1421 | ||
|
|
b3018fcb76 | ||
|
|
3cfe3af1c8 | ||
|
|
70f154da52 | ||
|
|
8a87698cc2 | ||
|
|
7edd3bdc11 | ||
|
|
98da49ff58 | ||
|
|
81ebd758cb | ||
|
|
f5e0819f50 | ||
|
|
eeb59cab07 | ||
|
|
75a0df2975 | ||
|
|
f7c63ee5d8 | ||
|
|
841a835153 | ||
|
|
a6b6077ecd | ||
|
|
caff2c98a9 | ||
|
|
50fa5d670b | ||
|
|
57809826a4 | ||
|
|
d5ab2c1f31 | ||
|
|
2635dcb457 | ||
|
|
e7541f88fb | ||
|
|
1f65cf6555 | ||
|
|
059f1cb698 | ||
|
|
2409d3223a | ||
|
|
7e1f1e5aad | ||
|
|
4180270f0b | ||
|
|
fe4ffc159a | ||
|
|
4cda1e48ef | ||
|
|
c5da98a5d9 | ||
|
|
3c58054a47 | ||
|
|
2d5d4bec02 | ||
|
|
8fb0da34bb | ||
|
|
b686178f93 | ||
|
|
803f69726b | ||
|
|
7764e71257 | ||
|
|
c60fdc8d84 | ||
|
|
fbc85308a5 | ||
|
|
26eda4775e | ||
|
|
e96908fcbb | ||
|
|
7f91d5cba8 | ||
|
|
561aff641a | ||
|
|
874147449a | ||
|
|
6706a0037e | ||
|
|
6d62389d26 |
@@ -204,11 +204,6 @@ contribution of project development and leadership!
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://saucelabs.com/resources/blog/appium-strategic-partner"><img src="packages/appium/docs/overrides/assets/images/sponsor-logo-sauce.png"
|
||||
width="300" alt="Sauce Labs" /></a>
|
||||
</p>
|
||||
|
||||
### Other Sponsors
|
||||
|
||||
A full list of sponsors is available at our [Sponsors page](https://appium.io/docs/en/latest/sponsors/).
|
||||
|
||||
Generated
+1011
-894
File diff suppressed because it is too large
Load Diff
+37
-14
@@ -85,42 +85,65 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@colors/colors": "1.6.0",
|
||||
"@eslint/js": "9.17.0",
|
||||
"@eslint/js": "9.20.0",
|
||||
"@tsconfig/node14": "14.1.2",
|
||||
"@types/argparse": "2.0.17",
|
||||
"@types/archiver": "6.0.3",
|
||||
"@types/async-lock": "1.4.2",
|
||||
"@types/base64-stream": "1.0.5",
|
||||
"@types/bluebird": "3.5.42",
|
||||
"@types/chai": "5.0.1",
|
||||
"@types/chai-as-promised": "8.0.1",
|
||||
"@types/diff": "6.0.0",
|
||||
"@types/diff": "7.0.1",
|
||||
"@types/express": "5.0.0",
|
||||
"@types/jsftp": "2.1.5",
|
||||
"@types/json-schema": "7.0.15",
|
||||
"@types/klaw": "3.0.6",
|
||||
"@types/lockfile": "1.0.4",
|
||||
"@types/lodash": "4.17.15",
|
||||
"@types/method-override": "3.0.0",
|
||||
"@types/mocha": "10.0.10",
|
||||
"@types/node": "22.10.5",
|
||||
"@types/mv": "2.1.4",
|
||||
"@types/ncp": "2.0.8",
|
||||
"@types/node": "22.13.4",
|
||||
"@types/pluralize": "0.0.33",
|
||||
"@types/semver": "7.5.8",
|
||||
"@types/serve-favicon": "2.5.7",
|
||||
"@types/shell-quote": "1.7.5",
|
||||
"@types/sinon": "17.0.3",
|
||||
"@types/sinon-chai": "4.0.0",
|
||||
"@types/source-map-support": "0.5.10",
|
||||
"@types/supports-color": "8.1.3",
|
||||
"@types/teen_process": "2.0.4",
|
||||
"@types/ws": "8.5.13",
|
||||
"@types/uuid": "10.0.0",
|
||||
"@types/which": "3.0.4",
|
||||
"@types/wrap-ansi": "3.0.0",
|
||||
"@types/ws": "8.5.14",
|
||||
"@types/xmldom": "0.1.34",
|
||||
"@types/yargs": "17.0.33",
|
||||
"asyncbox": "3.0.0",
|
||||
"chai": "5.1.2",
|
||||
"chai": "5.2.0",
|
||||
"chai-as-promised": "8.0.1",
|
||||
"conventional-changelog-conventionalcommits": "7.0.2",
|
||||
"cpy-cli": "5.0.0",
|
||||
"eslint": "9.17.0",
|
||||
"eslint-config-prettier": "9.1.0",
|
||||
"eslint-import-resolver-typescript": "3.7.0",
|
||||
"cross-env": "7.0.3",
|
||||
"eslint": "9.20.1",
|
||||
"eslint-config-prettier": "10.0.1",
|
||||
"eslint-import-resolver-typescript": "3.8.3",
|
||||
"eslint-plugin-import": "2.31.0",
|
||||
"eslint-plugin-mocha": "10.5.0",
|
||||
"eslint-plugin-promise": "7.2.1",
|
||||
"finalhandler": "1.3.1",
|
||||
"get-port": "5.1.1",
|
||||
"json-schema-to-typescript": "15.0.3",
|
||||
"json-schema-to-typescript": "15.0.4",
|
||||
"lerna": "8.1.9",
|
||||
"log-symbols": "4.1.0",
|
||||
"midnight-smoker": "8.0.0",
|
||||
"mjpeg-consumer": "2.0.0",
|
||||
"mjpeg-server": "0.3.1",
|
||||
"mocha": "11.0.1",
|
||||
"mocha": "11.1.0",
|
||||
"npm-run-all2": "6.2.6",
|
||||
"prettier": "3.4.2",
|
||||
"prettier": "3.5.1",
|
||||
"rewiremock": "3.14.5",
|
||||
"rimraf": "5.0.10",
|
||||
"serve-static": "1.16.2",
|
||||
@@ -128,10 +151,10 @@
|
||||
"sync-monorepo-packages": "1.0.2",
|
||||
"ts-node": "10.9.2",
|
||||
"tsd": "0.31.2",
|
||||
"typescript": "5.7.2",
|
||||
"typescript-eslint": "8.19.0",
|
||||
"typescript": "5.7.3",
|
||||
"typescript-eslint": "8.24.1",
|
||||
"validate.js": "0.13.1",
|
||||
"webdriverio": "9.5.1",
|
||||
"webdriverio": "9.9.3",
|
||||
"ws": "8.18.0",
|
||||
"yaml-js": "0.3.1"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,57 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [2.16.2](https://github.com/appium/appium/compare/appium@2.16.1...appium@2.16.2) (2025-02-20)
|
||||
|
||||
**Note:** Version bump only for package appium
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.16.1](https://github.com/appium/appium/compare/appium@2.16.0...appium@2.16.1) (2025-02-20)
|
||||
|
||||
**Note:** Version bump only for package appium
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.16.0](https://github.com/appium/appium/compare/appium@2.15.0...appium@2.16.0) (2025-02-19)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Add /appium/extensions API to list available extensions ([#20931](https://github.com/appium/appium/issues/20931)) ([a6b6077](https://github.com/appium/appium/commit/a6b6077ecd0749598f52d9f29b3220f47d7ad636))
|
||||
* add /appium/sessions, /session/:sessionId/appium/capabilities and deprecated marks will be removed in the future ([#20936](https://github.com/appium/appium/issues/20936)) ([eeb59ca](https://github.com/appium/appium/commit/eeb59cab071fdafa44f091e9d0e2676414c85c5d))
|
||||
* Add BiDi commands to the listCommands API output ([#20925](https://github.com/appium/appium/issues/20925)) ([2635dcb](https://github.com/appium/appium/commit/2635dcb457be2dc02dfbee5ad4c6ab132f5af8de))
|
||||
* **appium:** Add a command line parameter to configure HTTP server request timeout ([#21003](https://github.com/appium/appium/issues/21003)) ([eb1b156](https://github.com/appium/appium/commit/eb1b156146bc338da9c6ded5a2c5beab22ac0ed8))
|
||||
* **base-driver:** Add an API to list commands ([#20914](https://github.com/appium/appium/issues/20914)) ([059f1cb](https://github.com/appium/appium/commit/059f1cb698ccdbc58494af9303c5bf264a1893d9))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **appium:** add await for async function ([#20974](https://github.com/appium/appium/issues/20974)) ([21e2aab](https://github.com/appium/appium/commit/21e2aabc391fefe0dff8e29fe1cd6be908d5b3d0))
|
||||
* **appium:** Wait for upstream web socket to open before sending data to it ([#20953](https://github.com/appium/appium/issues/20953)) ([3cfe3af](https://github.com/appium/appium/commit/3cfe3af1c8d88c8ec79d2d89c7e76d697205c86b))
|
||||
* **execute-driver-plugin:** update dependency webdriverio to v9.5.3 ([#20895](https://github.com/appium/appium/issues/20895)) ([8741474](https://github.com/appium/appium/commit/874147449a26bbec03b08e7e95fb9e4c6b5881af))
|
||||
* **execute-driver-plugin:** update dependency webdriverio to v9.5.4 ([#20898](https://github.com/appium/appium/issues/20898)) ([7f91d5c](https://github.com/appium/appium/commit/7f91d5cba850100124088d774ba8704e3765863f))
|
||||
* **execute-driver-plugin:** update dependency webdriverio to v9.5.7 ([#20903](https://github.com/appium/appium/issues/20903)) ([c60fdc8](https://github.com/appium/appium/commit/c60fdc8d847b4aeed2d8b8307180194ee32e5ca9))
|
||||
* **execute-driver-plugin:** update dependency webdriverio to v9.7.0 ([#20930](https://github.com/appium/appium/issues/20930)) ([5780982](https://github.com/appium/appium/commit/57809826a410b71dca155f59e081f77dcaf671ed))
|
||||
* **execute-driver-plugin:** update dependency webdriverio to v9.7.1 ([#20934](https://github.com/appium/appium/issues/20934)) ([50fa5d6](https://github.com/appium/appium/commit/50fa5d670bd967200a78e5b4b8a54d5c9b85901b))
|
||||
* **execute-driver-plugin:** update dependency webdriverio to v9.7.2 ([#20944](https://github.com/appium/appium/issues/20944)) ([f5e0819](https://github.com/appium/appium/commit/f5e0819f505b55421b7d260f69a41ae980390a91))
|
||||
* **execute-driver-plugin:** update dependency webdriverio to v9.8.0 ([#20964](https://github.com/appium/appium/issues/20964)) ([ce32226](https://github.com/appium/appium/commit/ce32226cb57fb3f07bf4862dbbfcba227ff1230c))
|
||||
* **execute-driver-plugin:** update dependency webdriverio to v9.9.0 ([#20994](https://github.com/appium/appium/issues/20994)) ([b3c877f](https://github.com/appium/appium/commit/b3c877f66ddf3ccf7a25ca1f7a3952c660bd7dda))
|
||||
* **execute-driver-plugin:** update dependency webdriverio to v9.9.1 ([#20996](https://github.com/appium/appium/issues/20996)) ([c6a080f](https://github.com/appium/appium/commit/c6a080f7ade03a49a274ce6fbc84629406fdc2a7))
|
||||
* **execute-driver-plugin:** update dependency webdriverio to v9.9.3 ([#21005](https://github.com/appium/appium/issues/21005)) ([686a630](https://github.com/appium/appium/commit/686a630fbdf525db16b42fdfc1bbdaf6426b41d0))
|
||||
* **support:** update dependency semver to v7.7.0 ([#20948](https://github.com/appium/appium/issues/20948)) ([81ebd75](https://github.com/appium/appium/commit/81ebd758cb8a87d388124036e5158207041a6b2f))
|
||||
* **support:** update dependency semver to v7.7.1 ([#20961](https://github.com/appium/appium/issues/20961)) ([4c08944](https://github.com/appium/appium/commit/4c089440e655fbe3c8da919f8f625b7d7179f00a))
|
||||
* **types:** update dependency type-fest to v4.32.0 ([#20900](https://github.com/appium/appium/issues/20900)) ([fbc8530](https://github.com/appium/appium/commit/fbc85308a5398e7c9966792da713e60e47ed7f00))
|
||||
* **types:** update dependency type-fest to v4.33.0 ([#20923](https://github.com/appium/appium/issues/20923)) ([2409d32](https://github.com/appium/appium/commit/2409d3223a77aa7e84e0cb05a70be3bfa0c69157))
|
||||
* **types:** update dependency type-fest to v4.34.1 ([#20971](https://github.com/appium/appium/issues/20971)) ([0a7490e](https://github.com/appium/appium/commit/0a7490ed53ccfa9243df779e74bafadfc8415c87))
|
||||
* **types:** update dependency type-fest to v4.35.0 ([#20999](https://github.com/appium/appium/issues/20999)) ([3dc7336](https://github.com/appium/appium/commit/3dc7336b5fce10b9c1b095cd7a8a1841dbc3de12))
|
||||
|
||||
|
||||
|
||||
## [2.15.0](https://github.com/appium/appium/compare/appium@2.14.1...appium@2.15.0) (2025-01-08)
|
||||
|
||||
|
||||
|
||||
@@ -204,11 +204,6 @@ contribution of project development and leadership!
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://saucelabs.com/resources/blog/appium-strategic-partner"><img src="packages/appium/docs/overrides/assets/images/sponsor-logo-sauce.png"
|
||||
width="300" alt="Sauce Labs" /></a>
|
||||
</p>
|
||||
|
||||
### Other Sponsors
|
||||
|
||||
A full list of sponsors is available at our [Sponsors page](https://appium.io/docs/en/latest/sponsors/).
|
||||
|
||||
@@ -34,6 +34,7 @@ below.
|
||||
|`--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`|
|
||||
|`--request-timeout`|Number of seconds the Appium server should apply for receiving the entire HTTP request from the client. A value of 0 disables the timeout. Set it to a non-zero value to protect against potential Denial-of-Service attacks in case the server is deployed without a reverse proxy in front. HTTP requests that are running longer than allowed by this timeout would be rejected with the status code 408.|integer|`3600`||
|
||||
|`--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|||
|
||||
|
||||
@@ -47,7 +47,13 @@ A session data object
|
||||
|
||||
`GET` **`/session/:sessionId`**
|
||||
|
||||
Returns capabilities for the session and event history (if applicable)
|
||||
Returns capabilities for the session and event history (if applicable).
|
||||
|
||||
!!! warning "Deprecated"
|
||||
|
||||
Please use `getAppiumSessionCapabilities` to get the session capabilities.
|
||||
Please use `getLogEvents` to get event history.
|
||||
|
||||
|
||||
<!-- comment source: multiple -->
|
||||
|
||||
@@ -57,6 +63,20 @@ Returns capabilities for the session and event history (if applicable)
|
||||
|
||||
A session data object
|
||||
|
||||
### `getAppiumSessionCapabilities`
|
||||
|
||||
`GET` **`/session/:sessionId/appium/capabilities`**
|
||||
|
||||
Returns capabilities for the session.
|
||||
|
||||
<!-- comment source: multiple -->
|
||||
|
||||
#### Response
|
||||
|
||||
`AppiumSessionCapabilities`
|
||||
|
||||
A session data object
|
||||
|
||||
### `findElement`
|
||||
|
||||
`POST` **`/session/:sessionId/element`**
|
||||
|
||||
@@ -32,6 +32,7 @@ These clients are not maintained by the Appium team and can be used with other l
|
||||
|[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|
|
||||
|[SwiftAppium](https://github.com/milcgroup/swiftappium)|Swift|
|
||||
|
||||
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.
|
||||
|
||||
@@ -44,8 +44,6 @@ These drivers are not maintained by the Appium team and can be used to target ad
|
||||
|[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|
|
||||
|[axe UiAutomator2](https://docs.deque.com/devtools-mobile/appium-setup)|`--source=npm @axe-devtools/axe-appium-uiautomator2-driver`|Android|Native|[Deque](https://www.deque.com)|
|
||||
|[axe XCUITest](https://docs.deque.com/devtools-mobile/appium-setup)|`--source=npm @axe-devtools/axe-appium-xcuitest-driver`|iOS|Native|[Deque](https://www.deque.com)|
|
||||
|
||||
!!! note
|
||||
|
||||
|
||||
@@ -10,9 +10,9 @@ 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`
|
||||
With this capability turned on, the `POST /session/:id/appium/events` response (i.e.,
|
||||
the response to `driver.logs.events` or similar, depending on client) will be
|
||||
decorated with an `events` property. This is the structure of that `events`
|
||||
property:
|
||||
|
||||
```
|
||||
@@ -54,13 +54,17 @@ 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
|
||||
call to `/session/:id/appium/events`, 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).
|
||||
|
||||
!!! note
|
||||
|
||||
In the past, events were available as a part of `GET /session/:id` response
|
||||
|
||||
## Add a custom event
|
||||
|
||||
!!! warning "TODO"
|
||||
|
||||
@@ -40,11 +40,6 @@ Android TV, Samsung), and more!
|
||||
<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>
|
||||
|
||||
|
||||
@@ -35,7 +35,6 @@ contributors!
|
||||
<img src="../assets/images/sponsor-logo-browserstack-dark.png" width="250" alt="Browserstack"/>
|
||||
</picture>
|
||||
</a>
|
||||
<a style="padding-left: 1em" href="https://saucelabs.com/resources/blog/appium-strategic-partner" target="_blank"><img src="../assets/images/sponsor-logo-sauce.png" width="250"/></a>
|
||||
|
||||
## Gold Sponsors
|
||||
|
||||
|
||||
@@ -10,9 +10,9 @@ 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`
|
||||
With this capability turned on, the `POST /session/:id/appium/events` response (i.e.,
|
||||
the response to `driver.logs.events` or similar, depending on client) will be
|
||||
decorated with an `events` property. This is the structure of that `events`
|
||||
property:
|
||||
|
||||
```
|
||||
@@ -54,13 +54,17 @@ 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
|
||||
call to `/session/:id/appium/events`, 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).
|
||||
|
||||
!!! note
|
||||
|
||||
In the past, events were available as a part of `GET /session/:id` response
|
||||
|
||||
## Add a custom event
|
||||
|
||||
!!! warning "TODO"
|
||||
|
||||
@@ -8,6 +8,8 @@ import {
|
||||
CREATE_SESSION_COMMAND,
|
||||
DELETE_SESSION_COMMAND,
|
||||
GET_STATUS_COMMAND,
|
||||
LIST_DRIVER_COMMANDS_COMMAND,
|
||||
LIST_DRIVER_EXTENSIONS_COMMAND,
|
||||
promoteAppiumOptions,
|
||||
promoteAppiumOptionsForObject,
|
||||
generateDriverLogPrefix,
|
||||
@@ -22,7 +24,8 @@ import {
|
||||
import {util} from '@appium/support';
|
||||
import {getDefaultsForExtension} from './schema';
|
||||
import {DRIVER_TYPE, BIDI_BASE_PATH} from './constants';
|
||||
import * as bidiHelpers from './bidi';
|
||||
import * as bidiCommands from './bidi-commands';
|
||||
import * as inspectorCommands from './inspector-commands';
|
||||
|
||||
const desiredCapabilityConstraints = /** @type {const} */ ({
|
||||
automationName: {
|
||||
@@ -192,6 +195,11 @@ class AppiumDriver extends DriverCore {
|
||||
}));
|
||||
}
|
||||
|
||||
async getAppiumSessions () {
|
||||
throw new errors.NotImplementedError('Not implemented yet. ' +
|
||||
'Please check https://github.com/appium/appium/issues/20880 for more details.');
|
||||
}
|
||||
|
||||
printNewSessionAnnouncement(driverName, driverVersion, driverBaseVersion) {
|
||||
this.log.info(
|
||||
driverVersion
|
||||
@@ -403,7 +411,7 @@ class AppiumDriver extends DriverCore {
|
||||
);
|
||||
|
||||
// set the New Command Timeout for the inner driver
|
||||
driverInstance.startNewCommandTimeout();
|
||||
await driverInstance.startNewCommandTimeout();
|
||||
|
||||
// apply initial values to Appium settings (if provided)
|
||||
if (driverInstance.isW3CProtocol() && !_.isEmpty(w3cSettings)) {
|
||||
@@ -426,7 +434,7 @@ class AppiumDriver extends DriverCore {
|
||||
if (dCaps.webSocketUrl) {
|
||||
const {address, port, basePath} = this.args;
|
||||
const scheme = `ws${this.server.isSecure() ? 's' : ''}`;
|
||||
const host = bidiHelpers.determineBiDiHost(address);
|
||||
const host = bidiCommands.determineBiDiHost(address);
|
||||
const bidiUrl = `${scheme}://${host}:${port}${basePath}${BIDI_BASE_PATH}/${innerSessionId}`;
|
||||
this.log.info(
|
||||
`Upstream driver responded with webSocketUrl ${dCaps.webSocketUrl}, will rewrite to ` +
|
||||
@@ -565,32 +573,6 @@ class AppiumDriver extends DriverCore {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} sessionId
|
||||
*/
|
||||
cleanupBidiSockets(sessionId) {
|
||||
// clean up any bidi sockets associated with session
|
||||
if (this.bidiSockets[sessionId]) {
|
||||
try {
|
||||
this.log.debug(`Closing bidi socket(s) associated with session ${sessionId}`);
|
||||
for (const ws of this.bidiSockets[sessionId]) {
|
||||
// 1001 means server is going away
|
||||
ws.close(1001, 'Appium session is closing');
|
||||
}
|
||||
} catch {}
|
||||
delete this.bidiSockets[sessionId];
|
||||
const proxyClient = this.bidiProxyClients[sessionId];
|
||||
if (proxyClient) {
|
||||
this.log.debug(`Also closing proxy connection to upstream bidi server`);
|
||||
try {
|
||||
// 1000 means normal closure, which seems correct when Appium is acting as the client
|
||||
proxyClient.close(1000);
|
||||
} catch {}
|
||||
delete this.bidiProxyClients[sessionId];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async deleteAllSessions(opts = {}) {
|
||||
const sessionsCount = _.size(this.sessions);
|
||||
if (0 === sessionsCount) {
|
||||
@@ -935,15 +917,28 @@ class AppiumDriver extends DriverCore {
|
||||
return dstSession && dstSession.canProxy(sessionId);
|
||||
}
|
||||
|
||||
onBidiConnection = bidiHelpers.onBidiConnection;
|
||||
onBidiMessage = bidiHelpers.onBidiMessage;
|
||||
onBidiServerError = bidiHelpers.onBidiServerError;
|
||||
onBidiConnection = bidiCommands.onBidiConnection;
|
||||
onBidiMessage = bidiCommands.onBidiMessage;
|
||||
onBidiServerError = bidiCommands.onBidiServerError;
|
||||
cleanupBidiSockets = bidiCommands.cleanupBidiSockets;
|
||||
|
||||
listCommands = inspectorCommands.listCommands;
|
||||
listExtensions = inspectorCommands.listExtensions;
|
||||
}
|
||||
|
||||
// help decide which commands should be proxied to sub-drivers and which
|
||||
// should be handled by this, our umbrella driver
|
||||
/**
|
||||
* Help decide which commands should be proxied to sub-drivers and which
|
||||
* should be handled by this, our umbrella driver
|
||||
* @param {string} cmd
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function isAppiumDriverCommand(cmd) {
|
||||
return !isSessionCommand(cmd) || cmd === DELETE_SESSION_COMMAND;
|
||||
return !isSessionCommand(cmd)
|
||||
|| _.includes([
|
||||
DELETE_SESSION_COMMAND,
|
||||
LIST_DRIVER_COMMANDS_COMMAND,
|
||||
LIST_DRIVER_EXTENSIONS_COMMAND,
|
||||
], cmd);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -176,6 +176,38 @@ export function onBidiServerError(this: AppiumDriver, err: Error): void {
|
||||
this.log.warn(`Error from bidi websocket server: ${err}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up any bidi sockets associated with session
|
||||
*
|
||||
* @param sessionId
|
||||
*/
|
||||
export function cleanupBidiSockets(this: AppiumDriver, sessionId: string): void {
|
||||
if (!this.bidiSockets[sessionId]) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
this.log.debug(`Closing bidi socket(s) associated with session ${sessionId}`);
|
||||
for (const ws of this.bidiSockets[sessionId]) {
|
||||
// 1001 means server is going away
|
||||
ws.close(1001, 'Appium session is closing');
|
||||
}
|
||||
} catch {}
|
||||
delete this.bidiSockets[sessionId];
|
||||
|
||||
const proxyClient = this.bidiProxyClients[sessionId];
|
||||
if (!proxyClient) {
|
||||
return;
|
||||
}
|
||||
this.log.debug(`Also closing proxy connection to upstream bidi server`);
|
||||
try {
|
||||
// 1000 means normal closure, which seems correct when Appium is acting as the client
|
||||
proxyClient.close(1000);
|
||||
} catch {}
|
||||
delete this.bidiProxyClients[sessionId];
|
||||
}
|
||||
|
||||
// #region Private functions
|
||||
|
||||
/**
|
||||
* Initialize a new bidi connection
|
||||
* @param ws The websocket connection object
|
||||
@@ -263,6 +295,7 @@ function initBidiSocket(this: AppiumDriver, ws: WebSocket, req: IncomingMessage)
|
||||
const socketSend = B.promisify(socket.send, {context: socket});
|
||||
return async (data: string | Buffer) => {
|
||||
try {
|
||||
await assertIsOpen(socket);
|
||||
await socketSend(data);
|
||||
} catch (err) {
|
||||
logSocketErr(err);
|
||||
@@ -468,3 +501,43 @@ function initBidiEventListeners(
|
||||
plugin.eventEmitter?.on(BIDI_EVENT_NAME, eventListenerFactory('plugin', plugin));
|
||||
}
|
||||
}
|
||||
|
||||
async function assertIsOpen(
|
||||
ws: WebSocket,
|
||||
timeoutMs: number = 5000,
|
||||
): Promise<WebSocket> {
|
||||
if (ws.readyState === ws.OPEN) {
|
||||
return ws;
|
||||
}
|
||||
if (ws.readyState > ws.OPEN) {
|
||||
throw new Error(`The BiDi web socket at ${ws.url} is not open`);
|
||||
}
|
||||
|
||||
let errorListener;
|
||||
let openListener;
|
||||
// The socket is in CONNECTING state. Wait up to `timeoutMs` until it is open
|
||||
try {
|
||||
await new Promise((resolve, reject) => {
|
||||
setTimeout(() => reject(
|
||||
new Error(
|
||||
`The BiDi web socket at ${ws.url} did not ` +
|
||||
`open after ${timeoutMs}ms timeout`
|
||||
)
|
||||
), timeoutMs);
|
||||
ws.once('error', reject);
|
||||
errorListener = reject;
|
||||
ws.once('open', resolve);
|
||||
openListener = resolve;
|
||||
});
|
||||
} finally {
|
||||
if (errorListener) {
|
||||
ws.off('error', errorListener);
|
||||
}
|
||||
if (openListener) {
|
||||
ws.off('open', openListener);
|
||||
}
|
||||
}
|
||||
return ws;
|
||||
}
|
||||
|
||||
// #endregion
|
||||
@@ -0,0 +1,190 @@
|
||||
import _ from 'lodash';
|
||||
import {
|
||||
METHOD_MAP as BASE_METHOD_MAP,
|
||||
BIDI_COMMANDS as BASE_BIDI_COMMANDS,
|
||||
} from '@appium/base-driver';
|
||||
import type {
|
||||
ListCommandsResponse,
|
||||
MethodMap,
|
||||
BidiModuleMap,
|
||||
DriverClass,
|
||||
PluginClass,
|
||||
ListExtensionsResponse,
|
||||
PayloadParams,
|
||||
RestCommandItemParam,
|
||||
RestMethodsToCommandsMap,
|
||||
BiDiCommandsMap,
|
||||
BidiMethodParams,
|
||||
BiDiCommandItemParam,
|
||||
BiDiCommandNamesToInfosMap,
|
||||
ExecuteMethodMap,
|
||||
} from '@appium/types';
|
||||
import type { AppiumDriver } from './appium';
|
||||
|
||||
|
||||
export async function listCommands(this: AppiumDriver, sessionId?: string): Promise<ListCommandsResponse> {
|
||||
let driverRestMethodMap: MethodMap<any> = {};
|
||||
let driverBiDiCommands: BidiModuleMap = {};
|
||||
let pluginRestMethodMaps: Record<string, MethodMap<any>> = {};
|
||||
let pluginBiDiCommands: Record<string, BidiModuleMap> = {};
|
||||
if (sessionId) {
|
||||
const driverClass = this.driverForSession(sessionId)?.constructor as (DriverClass | undefined);
|
||||
driverRestMethodMap = driverClass?.newMethodMap ?? {};
|
||||
driverBiDiCommands = driverClass?.newBidiCommands ?? {};
|
||||
const pluginClasses = this.pluginsForSession(sessionId)
|
||||
.map((p) => p.constructor as PluginClass);
|
||||
pluginRestMethodMaps = _.fromPairs(pluginClasses.map((c) => [c.name, c.newMethodMap ?? {}]));
|
||||
pluginBiDiCommands = _.fromPairs(pluginClasses.map((c) => [c.name, c.newBidiCommands ?? {}]));
|
||||
}
|
||||
return {
|
||||
rest: {
|
||||
base: methodMapToRestCommandsInfo(BASE_METHOD_MAP),
|
||||
driver: methodMapToRestCommandsInfo(driverRestMethodMap),
|
||||
plugins: pluginRestMethodMaps ? _.mapValues(pluginRestMethodMaps, methodMapToRestCommandsInfo) : undefined,
|
||||
},
|
||||
bidi: toBiDiCommandsMap(BASE_BIDI_COMMANDS, driverBiDiCommands, pluginBiDiCommands),
|
||||
};
|
||||
}
|
||||
|
||||
export async function listExtensions(this: AppiumDriver, sessionId?: string): Promise<ListExtensionsResponse> {
|
||||
let driverExecuteMethodMap: ExecuteMethodMap<any> = {};
|
||||
let pluginExecuteMethodMaps: Record<string, ExecuteMethodMap<any>> = {};
|
||||
if (sessionId) {
|
||||
const driverClass = this.driverForSession(sessionId)?.constructor as (DriverClass | undefined);
|
||||
driverExecuteMethodMap = driverClass?.executeMethodMap ?? {};
|
||||
const pluginClasses = this.pluginsForSession(sessionId)
|
||||
.map((p) => p.constructor as PluginClass);
|
||||
pluginExecuteMethodMaps = _.fromPairs(pluginClasses.map((c) => [c.name, c.executeMethodMap ?? {}]));
|
||||
}
|
||||
return {
|
||||
rest: {
|
||||
driver: executeMethodMapToCommandsInfo(driverExecuteMethodMap),
|
||||
plugins: pluginExecuteMethodMaps ? _.mapValues(pluginExecuteMethodMaps, executeMethodMapToCommandsInfo) : undefined,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function toRestCommandParams(params: PayloadParams | undefined): RestCommandItemParam[] | undefined {
|
||||
if (!params) {
|
||||
return;
|
||||
}
|
||||
|
||||
const toRestCommandItemParam = (x: any, isRequired: boolean): RestCommandItemParam | undefined => {
|
||||
const isNameAnArray = _.isArray(x);
|
||||
const name = isNameAnArray ? x[0] : x;
|
||||
if (!_.isString(name)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If parameter names are arrays then this means
|
||||
// either of them is required.
|
||||
// Not sure we could reflect that in here.
|
||||
const required = isRequired && !isNameAnArray;
|
||||
return {
|
||||
name,
|
||||
required,
|
||||
};
|
||||
};
|
||||
|
||||
const requiredParams: RestCommandItemParam[] = (params.required ?? [])
|
||||
.map((name: any) => toRestCommandItemParam(name, true))
|
||||
.filter((x) => !_.isUndefined(x));
|
||||
const optionalParams: RestCommandItemParam[] = (params.optional ?? [])
|
||||
.map((name: any) => toRestCommandItemParam(name, false))
|
||||
.filter((x) => !_.isUndefined(x));
|
||||
return requiredParams.length || optionalParams.length
|
||||
? [...requiredParams, ...optionalParams]
|
||||
: undefined;
|
||||
}
|
||||
|
||||
function methodMapToRestCommandsInfo (mm: MethodMap<any>): Record<string, RestMethodsToCommandsMap> {
|
||||
const res: Record<string, RestMethodsToCommandsMap> = {};
|
||||
for (const [uriPath, methods] of _.toPairs(mm)) {
|
||||
const methodsMap = {};
|
||||
for (const [method, spec] of _.toPairs(methods)) {
|
||||
methodsMap[method] = {
|
||||
command: spec.command,
|
||||
deprecated: spec.deprecated,
|
||||
info: spec.info,
|
||||
params: toRestCommandParams(spec.payloadParams),
|
||||
};
|
||||
}
|
||||
res[uriPath] = methodsMap;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
function executeMethodMapToCommandsInfo(emm: ExecuteMethodMap<any>): RestMethodsToCommandsMap {
|
||||
const result: RestMethodsToCommandsMap = {};
|
||||
for (const [name, info] of _.toPairs(emm)) {
|
||||
result[name] = {
|
||||
command: info.command,
|
||||
deprecated: info.deprecated,
|
||||
info: info.info,
|
||||
params: toRestCommandParams(info.params),
|
||||
};
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function toBiDiCommandsMap(
|
||||
baseModuleMap: BidiModuleMap,
|
||||
driverModuleMap: BidiModuleMap,
|
||||
pluginModuleMaps: Record<string, BidiModuleMap>
|
||||
): BiDiCommandsMap {
|
||||
const toBiDiCommandParams = (params: BidiMethodParams | undefined): BiDiCommandItemParam[] | undefined => {
|
||||
if (!params) {
|
||||
return;
|
||||
}
|
||||
|
||||
const toBiDiCommandItemParam = (x: any, isRequired: boolean): BiDiCommandItemParam | undefined => {
|
||||
const isNameAnArray = _.isArray(x);
|
||||
const name = isNameAnArray ? x[0] : x;
|
||||
if (!_.isString(name)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If parameter names are arrays then this means
|
||||
// either of them is required.
|
||||
// Not sure we could reflect that in here.
|
||||
const required = isRequired && !isNameAnArray;
|
||||
return {
|
||||
name,
|
||||
required,
|
||||
};
|
||||
};
|
||||
|
||||
const requiredParams: BiDiCommandItemParam[] = (params.required ?? [])
|
||||
.map((name) => toBiDiCommandItemParam(name, true))
|
||||
.filter((x) => !_.isUndefined(x));
|
||||
const optionalParams: BiDiCommandItemParam[] = (params.optional ?? [])
|
||||
.map((name) => toBiDiCommandItemParam(name, false))
|
||||
.filter((x) => !_.isUndefined(x));
|
||||
return requiredParams.length || optionalParams.length
|
||||
? [...requiredParams, ...optionalParams]
|
||||
: undefined;
|
||||
};
|
||||
|
||||
const moduleMapToBiDiCommandsInfo = (mm: BidiModuleMap): Record<string, BiDiCommandNamesToInfosMap> => {
|
||||
const res: Record<string, BiDiCommandNamesToInfosMap> = {};
|
||||
for (const [domain, commands] of _.toPairs(mm)) {
|
||||
const commandsMap = {};
|
||||
for (const [name, spec] of _.toPairs(commands)) {
|
||||
commandsMap[name] = {
|
||||
command: spec.command,
|
||||
deprecated: spec.deprecated,
|
||||
info: spec.info,
|
||||
params: toBiDiCommandParams(spec.params),
|
||||
};
|
||||
}
|
||||
res[domain] = commandsMap;
|
||||
}
|
||||
return res;
|
||||
};
|
||||
|
||||
return {
|
||||
base: moduleMapToBiDiCommandsInfo(baseModuleMap),
|
||||
driver: moduleMapToBiDiCommandsInfo(driverModuleMap),
|
||||
plugins: pluginModuleMaps ? _.mapValues(pluginModuleMaps, moduleMapToBiDiCommandsInfo) : undefined,
|
||||
};
|
||||
}
|
||||
@@ -397,8 +397,10 @@ async function main(args) {
|
||||
extraMethodMap,
|
||||
cliArgs: parsedArgs,
|
||||
};
|
||||
if (parsedArgs.keepAliveTimeout) {
|
||||
serverOpts.keepAliveTimeout = parsedArgs.keepAliveTimeout * 1000;
|
||||
for (const timeoutArgName of ['keepAliveTimeout', 'requestTimeout']) {
|
||||
if (_.isInteger(parsedArgs[timeoutArgName])) {
|
||||
serverOpts[timeoutArgName] = parsedArgs[timeoutArgName] * 1000;
|
||||
}
|
||||
}
|
||||
let server;
|
||||
const bidiServer = new WebSocketServer({noServer: true});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "appium",
|
||||
"version": "2.15.0",
|
||||
"version": "2.16.2",
|
||||
"description": "Automation for Apps.",
|
||||
"keywords": [
|
||||
"automation",
|
||||
@@ -60,19 +60,14 @@
|
||||
"test:unit": "mocha \"./test/unit/**/*.spec.js\""
|
||||
},
|
||||
"dependencies": {
|
||||
"@appium/base-driver": "^9.15.0",
|
||||
"@appium/base-plugin": "^2.3.0",
|
||||
"@appium/docutils": "^1.0.29",
|
||||
"@appium/base-driver": "^9.16.2",
|
||||
"@appium/base-plugin": "^2.3.3",
|
||||
"@appium/docutils": "^1.0.32",
|
||||
"@appium/logger": "^1.6.1",
|
||||
"@appium/schema": "^0.7.1",
|
||||
"@appium/support": "^6.0.3",
|
||||
"@appium/types": "^0.24.0",
|
||||
"@appium/schema": "^0.8.1",
|
||||
"@appium/support": "^6.0.6",
|
||||
"@appium/types": "^0.25.1",
|
||||
"@sidvind/better-ajv-errors": "3.0.1",
|
||||
"@types/argparse": "2.0.17",
|
||||
"@types/bluebird": "3.5.42",
|
||||
"@types/semver": "7.5.8",
|
||||
"@types/teen_process": "2.0.4",
|
||||
"@types/wrap-ansi": "3.0.0",
|
||||
"ajv": "8.17.1",
|
||||
"ajv-formats": "3.0.1",
|
||||
"argparse": "2.0.1",
|
||||
@@ -80,17 +75,16 @@
|
||||
"asyncbox": "3.0.0",
|
||||
"axios": "1.7.9",
|
||||
"bluebird": "3.7.2",
|
||||
"cross-env": "7.0.3",
|
||||
"lilconfig": "3.1.3",
|
||||
"lodash": "4.17.21",
|
||||
"lru-cache": "10.4.3",
|
||||
"ora": "5.4.1",
|
||||
"package-changed": "3.0.0",
|
||||
"resolve-from": "5.0.0",
|
||||
"semver": "7.6.3",
|
||||
"semver": "7.7.1",
|
||||
"source-map-support": "0.5.21",
|
||||
"teen_process": "2.2.3",
|
||||
"type-fest": "4.31.0",
|
||||
"type-fest": "4.35.0",
|
||||
"winston": "3.17.0",
|
||||
"wrap-ansi": "7.0.0",
|
||||
"ws": "8.18.0",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"devDependencies": {
|
||||
"webdriverio": "9.5.1"
|
||||
"webdriverio": "9.9.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,6 +208,77 @@ describe('FakeDriver via HTTP', function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe('inspector commands', function () {
|
||||
withServer();
|
||||
let driver;
|
||||
|
||||
beforeEach(async function () {
|
||||
driver = await wdio({...wdOpts, capabilities: caps});
|
||||
});
|
||||
afterEach(async function () {
|
||||
if (driver) {
|
||||
await driver.deleteSession();
|
||||
driver = null;
|
||||
}
|
||||
});
|
||||
|
||||
it('should list available driver commands', async function () {
|
||||
driver.addCommand(
|
||||
'listCommands',
|
||||
async () => (await axios.get(
|
||||
`${testServerBaseSessionUrl}/${driver.sessionId}/appium/commands`
|
||||
)).data.value
|
||||
);
|
||||
|
||||
const commands = await driver.listCommands();
|
||||
|
||||
JSON.stringify(commands.rest.base['/session/:sessionId/frame'])
|
||||
.should.eql(JSON.stringify({POST: {command: 'setFrame', params: [
|
||||
{name: 'id', required: true}
|
||||
]}}));
|
||||
_.size(commands.rest.driver).should.be.greaterThan(1);
|
||||
|
||||
JSON.stringify(commands.bidi.base.session.subscribe).should.eql(
|
||||
JSON.stringify({
|
||||
command: 'bidiSubscribe',
|
||||
'params': [
|
||||
{
|
||||
name: 'events',
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: 'contexts',
|
||||
required: false
|
||||
}
|
||||
]
|
||||
})
|
||||
);
|
||||
_.size(commands.bidi.base).should.be.greaterThan(1);
|
||||
_.size(commands.bidi.driver).should.be.greaterThan(0);
|
||||
});
|
||||
|
||||
it('should list available driver extensions', async function () {
|
||||
driver.addCommand(
|
||||
'listExtensions',
|
||||
async () => (await axios.get(
|
||||
`${testServerBaseSessionUrl}/${driver.sessionId}/appium/extensions`
|
||||
)).data.value
|
||||
);
|
||||
|
||||
const extensions = await driver.listExtensions();
|
||||
JSON.stringify(extensions.rest.driver['fake: setThing']).should.eql(
|
||||
JSON.stringify({
|
||||
command: 'setFakeThing',
|
||||
params: [{
|
||||
name: 'thing',
|
||||
required: true
|
||||
}]
|
||||
})
|
||||
);
|
||||
_.size(extensions.rest.driver).should.be.greaterThan(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('session handling', function () {
|
||||
withServer();
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ export default {
|
||||
denyInsecure: [],
|
||||
driversImportChunkSize: 3,
|
||||
keepAliveTimeout: 600,
|
||||
requestTimeout: 3600,
|
||||
localTimezone: false,
|
||||
logFormat: 'text',
|
||||
loglevel: 'debug',
|
||||
|
||||
@@ -3,6 +3,47 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [9.16.2](https://github.com/appium/appium/compare/@appium/base-driver@9.16.1...@appium/base-driver@9.16.2) (2025-02-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **base-driver:** Restore the legacy proxy url behaviour ([#21021](https://github.com/appium/appium/issues/21021)) ([dd64a48](https://github.com/appium/appium/commit/dd64a48fca047a728ad109d525bc4787daa75edc))
|
||||
|
||||
|
||||
|
||||
## [9.16.1](https://github.com/appium/appium/compare/@appium/base-driver@9.16.0...@appium/base-driver@9.16.1) (2025-02-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **base-driver:** Optimize the logic of getUrlForProxy ([#21018](https://github.com/appium/appium/issues/21018)) ([8a664a4](https://github.com/appium/appium/commit/8a664a4eef97f45c5bdf7f19708149488b63d7fc))
|
||||
|
||||
|
||||
|
||||
## [9.16.0](https://github.com/appium/appium/compare/@appium/base-driver@9.15.0...@appium/base-driver@9.16.0) (2025-02-19)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Add /appium/extensions API to list available extensions ([#20931](https://github.com/appium/appium/issues/20931)) ([a6b6077](https://github.com/appium/appium/commit/a6b6077ecd0749598f52d9f29b3220f47d7ad636))
|
||||
* add /appium/sessions, /session/:sessionId/appium/capabilities and deprecated marks will be removed in the future ([#20936](https://github.com/appium/appium/issues/20936)) ([eeb59ca](https://github.com/appium/appium/commit/eeb59cab071fdafa44f091e9d0e2676414c85c5d))
|
||||
* Add BiDi commands to the listCommands API output ([#20925](https://github.com/appium/appium/issues/20925)) ([2635dcb](https://github.com/appium/appium/commit/2635dcb457be2dc02dfbee5ad4c6ab132f5af8de))
|
||||
* **appium:** Add a command line parameter to configure HTTP server request timeout ([#21003](https://github.com/appium/appium/issues/21003)) ([eb1b156](https://github.com/appium/appium/commit/eb1b156146bc338da9c6ded5a2c5beab22ac0ed8))
|
||||
* **base-driver:** Add an API to list commands ([#20914](https://github.com/appium/appium/issues/20914)) ([059f1cb](https://github.com/appium/appium/commit/059f1cb698ccdbc58494af9303c5bf264a1893d9))
|
||||
* **base-driver:** Print the closest match if the given script did not match ([#20956](https://github.com/appium/appium/issues/20956)) ([f8b5799](https://github.com/appium/appium/commit/f8b57999e5a4a13a089cff12954de5d99c8a35fc))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **driver-test-support:** update definitelytyped ([#20942](https://github.com/appium/appium/issues/20942)) ([f7c63ee](https://github.com/appium/appium/commit/f7c63ee5d87be21eba577a4cfd0b0a08a050afeb))
|
||||
* **types:** update dependency type-fest to v4.32.0 ([#20900](https://github.com/appium/appium/issues/20900)) ([fbc8530](https://github.com/appium/appium/commit/fbc85308a5398e7c9966792da713e60e47ed7f00))
|
||||
* **types:** update dependency type-fest to v4.33.0 ([#20923](https://github.com/appium/appium/issues/20923)) ([2409d32](https://github.com/appium/appium/commit/2409d3223a77aa7e84e0cb05a70be3bfa0c69157))
|
||||
* **types:** update dependency type-fest to v4.34.1 ([#20971](https://github.com/appium/appium/issues/20971)) ([0a7490e](https://github.com/appium/appium/commit/0a7490ed53ccfa9243df779e74bafadfc8415c87))
|
||||
* **types:** update dependency type-fest to v4.35.0 ([#20999](https://github.com/appium/appium/issues/20999)) ([3dc7336](https://github.com/appium/appium/commit/3dc7336b5fce10b9c1b095cd7a8a1841dbc3de12))
|
||||
|
||||
|
||||
|
||||
## [9.15.0](https://github.com/appium/appium/compare/@appium/base-driver@9.14.1...@appium/base-driver@9.15.0) (2025-01-08)
|
||||
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
} from '@appium/types';
|
||||
import {mixin} from './mixin';
|
||||
import {BaseDriver} from '../driver';
|
||||
import {distance} from 'fastest-levenshtein';
|
||||
|
||||
declare module '../driver' {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
@@ -26,9 +27,33 @@ const ExecuteCommands: IExecuteCommands = {
|
||||
const commandMetadata = {...Driver.executeMethodMap?.[script]};
|
||||
if (!commandMetadata.command) {
|
||||
const availableScripts = _.keys(Driver.executeMethodMap);
|
||||
if (_.isEmpty(availableScripts)) {
|
||||
throw new errors.UnsupportedOperationError(
|
||||
`Unsupported execute method '${script}'. ` +
|
||||
`Make sure the installed ${Driver.name} is up-to-date. ` +
|
||||
`The current driver version does not define any execute methods.`
|
||||
);
|
||||
}
|
||||
const matchesMap: StringRecord<string[]> = availableScripts
|
||||
.map((name) => [distance(script, name), name])
|
||||
.reduce((acc, [key, value]) => {
|
||||
if (key in acc) {
|
||||
acc[key].push(value);
|
||||
} else {
|
||||
acc[key] = [value];
|
||||
}
|
||||
return acc;
|
||||
}, {});
|
||||
const sortedMatches = _.flatten(
|
||||
_.keys(matchesMap)
|
||||
.sort((a, b) => parseInt(a, 10) - parseInt(b, 10))
|
||||
.map((x) => matchesMap[x])
|
||||
);
|
||||
throw new errors.UnsupportedOperationError(
|
||||
`Unsupported execute method '${script}'. Available methods ` +
|
||||
`are: ${availableScripts.join(', ')}`
|
||||
`Unsupported execute method '${script}', did you mean '${sortedMatches[0]}'? ` +
|
||||
`Make sure the installed ${Driver.name} is up-to-date. ` +
|
||||
`Execute methods available in the current driver version are: ` +
|
||||
sortedMatches.join(', ')
|
||||
);
|
||||
}
|
||||
const args = validateExecuteMethodParams(protoArgs, commandMetadata.params);
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
type InitialOpts,
|
||||
type DefaultDeleteSessionResult,
|
||||
type SingularSessionData,
|
||||
type SessionCapabilities,
|
||||
} from '@appium/types';
|
||||
import B from 'bluebird';
|
||||
import _ from 'lodash';
|
||||
@@ -359,6 +360,8 @@ export class BaseDriver<
|
||||
|
||||
/**
|
||||
* Returns capabilities for the session and event history (if applicable)
|
||||
* @deprecated Use {@linkcode ISessionCommands.getAppiumSessionCapabilities} instead for getting the capabilities.
|
||||
* Use {@linkcode EventCommands.getLogEvents} instead to get the event history.
|
||||
*/
|
||||
async getSession() {
|
||||
return (
|
||||
@@ -366,6 +369,13 @@ export class BaseDriver<
|
||||
) as SingularSessionData<C, SessionData>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns capabilities for the session
|
||||
*/
|
||||
async getAppiumSessionCapabilities(): Promise<SessionCapabilities<C>> {
|
||||
return {capabilities: this.caps};
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
async deleteSession(sessionId?: string | null) {
|
||||
await this.clearNewCommandTimeout();
|
||||
|
||||
@@ -76,7 +76,7 @@ async function createServer (app, cliArgs) {
|
||||
* @param {ServerOpts} opts
|
||||
* @returns {Promise<AppiumServer>}
|
||||
*/
|
||||
async function server(opts) {
|
||||
export async function server(opts) {
|
||||
const {
|
||||
routeConfiguringFunction,
|
||||
port,
|
||||
@@ -87,6 +87,7 @@ async function server(opts) {
|
||||
extraMethodMap = {},
|
||||
serverUpdaters = [],
|
||||
keepAliveTimeout = KEEP_ALIVE_TIMEOUT_MS,
|
||||
requestTimeout,
|
||||
} = opts;
|
||||
|
||||
const app = express();
|
||||
@@ -123,7 +124,13 @@ async function server(opts) {
|
||||
// want to block extensions' ability to add routes if they want.
|
||||
app.all('*', catch404Handler);
|
||||
|
||||
await startServer({httpServer, hostname, port, keepAliveTimeout});
|
||||
await startServer({
|
||||
httpServer,
|
||||
hostname,
|
||||
port,
|
||||
keepAliveTimeout,
|
||||
requestTimeout,
|
||||
});
|
||||
|
||||
resolve(appiumServer);
|
||||
} catch (err) {
|
||||
@@ -136,7 +143,7 @@ async function server(opts) {
|
||||
* Sets up some Express middleware and stuff
|
||||
* @param {ConfigureServerOpts} opts
|
||||
*/
|
||||
function configureServer({
|
||||
export function configureServer({
|
||||
app,
|
||||
addRoutes,
|
||||
allowCors = true,
|
||||
@@ -267,13 +274,22 @@ function configureHttp({httpServer, reject, keepAliveTimeout, gracefulShutdownTi
|
||||
* @param {StartServerOpts} opts
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function startServer({httpServer, port, hostname, keepAliveTimeout}) {
|
||||
async function startServer({
|
||||
httpServer,
|
||||
port,
|
||||
hostname,
|
||||
keepAliveTimeout,
|
||||
requestTimeout,
|
||||
}) {
|
||||
// If the hostname is omitted, the server will accept
|
||||
// connections on any IP address
|
||||
/** @type {(port: number, hostname?: string) => B<http.Server>} */
|
||||
const start = B.promisify(httpServer.listen, {context: httpServer});
|
||||
const startPromise = start(port, hostname);
|
||||
httpServer.keepAliveTimeout = keepAliveTimeout;
|
||||
if (_.isInteger(requestTimeout)) {
|
||||
httpServer.requestTimeout = Number(requestTimeout);
|
||||
}
|
||||
// headers timeout must be greater than keepAliveTimeout
|
||||
httpServer.headersTimeout = keepAliveTimeout + 5 * 1000;
|
||||
await startPromise;
|
||||
@@ -284,7 +300,7 @@ async function startServer({httpServer, port, hostname, keepAliveTimeout}) {
|
||||
* @param {string} basePath
|
||||
* @returns {string}
|
||||
*/
|
||||
function normalizeBasePath(basePath) {
|
||||
export function normalizeBasePath(basePath) {
|
||||
if (!_.isString(basePath)) {
|
||||
throw new Error(`Invalid path prefix ${basePath}`);
|
||||
}
|
||||
@@ -302,7 +318,6 @@ function normalizeBasePath(basePath) {
|
||||
return basePath;
|
||||
}
|
||||
|
||||
export {server, configureServer, normalizeBasePath};
|
||||
|
||||
/**
|
||||
* Options for {@linkcode startServer}.
|
||||
@@ -311,6 +326,8 @@ export {server, configureServer, normalizeBasePath};
|
||||
* @property {number} port - Port to run on
|
||||
* @property {number} keepAliveTimeout - Keep-alive timeout in milliseconds
|
||||
* @property {string} [hostname] - Optional hostname
|
||||
* @property {number} [requestTimeout] - The timeout value in milliseconds for
|
||||
* receiving the entire request from the client
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -344,6 +361,7 @@ export {server, configureServer, normalizeBasePath};
|
||||
* @property {MethodMap} [extraMethodMap]
|
||||
* @property {import('@appium/types').UpdateServerCallback[]} [serverUpdaters]
|
||||
* @property {number} [keepAliveTimeout]
|
||||
* @property {number} [requestTimeout]
|
||||
*/
|
||||
|
||||
/**
|
||||
|
||||
@@ -49,6 +49,9 @@ export {
|
||||
// Web socket helpers
|
||||
export {DEFAULT_WS_PATHNAME_PREFIX} from './express/websocket';
|
||||
|
||||
// BiDi exports
|
||||
export {BIDI_COMMANDS} from './protocol/bidi-commands';
|
||||
|
||||
export {generateDriverLogPrefix} from './basedriver/helpers';
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,15 +9,19 @@ import {
|
||||
errorFromW3CJsonCode,
|
||||
getResponseForW3CError,
|
||||
} from '../protocol/errors';
|
||||
import {routeToCommandName} from '../protocol';
|
||||
import {isSessionCommand, routeToCommandName} from '../protocol';
|
||||
import {MAX_LOG_BODY_LENGTH, DEFAULT_BASE_PATH, PROTOCOLS} from '../constants';
|
||||
import ProtocolConverter from './protocol-converter';
|
||||
import {formatResponseValue, formatStatus} from '../protocol/helpers';
|
||||
import http from 'http';
|
||||
import https from 'https';
|
||||
import { match as pathToRegexMatch } from 'path-to-regexp';
|
||||
import nodeUrl from 'node:url';
|
||||
|
||||
|
||||
const DEFAULT_LOG = logger.getLogger('WD Proxy');
|
||||
const DEFAULT_REQUEST_TIMEOUT = 240000;
|
||||
const COMMAND_WITH_SESSION_ID_MATCHER = pathToRegexMatch('{/*prefix}/session/:sessionId{/*command}');
|
||||
|
||||
const {MJSONWP, W3C} = PROTOCOLS;
|
||||
|
||||
@@ -110,10 +114,6 @@ class JWProxy {
|
||||
this._activeRequests = [];
|
||||
}
|
||||
|
||||
endpointRequiresSessionId(endpoint) {
|
||||
return !_.includes(['/session', '/sessions', '/status'], endpoint);
|
||||
}
|
||||
|
||||
set downstreamProtocol(value) {
|
||||
this._downstreamProtocol = value;
|
||||
this.protocolConverter.downstreamProtocol = value;
|
||||
@@ -123,67 +123,70 @@ class JWProxy {
|
||||
return this._downstreamProtocol;
|
||||
}
|
||||
|
||||
getUrlForProxy(url) {
|
||||
if (url === '') {
|
||||
url = '/';
|
||||
/**
|
||||
*
|
||||
* @param {string} url
|
||||
* @param {string} [method]
|
||||
* @returns {string}
|
||||
*/
|
||||
getUrlForProxy(url, method) {
|
||||
const parsedUrl = nodeUrl.parse(url || '/');
|
||||
if (
|
||||
!parsedUrl.href || !parsedUrl.pathname
|
||||
|| (parsedUrl.protocol && !['http:', 'https:'].includes(parsedUrl.protocol))
|
||||
) {
|
||||
throw new Error(`Did not know how to proxy the url '${url}'`);
|
||||
}
|
||||
const proxyBase = `${this.scheme}://${this.server}:${this.port}${this.base}`;
|
||||
const endpointRe = '(/(session|status))';
|
||||
let remainingUrl = '';
|
||||
if (/^http/.test(url)) {
|
||||
const first = new RegExp(`(https?://.+)${endpointRe}`).exec(url);
|
||||
if (!first) {
|
||||
throw new Error('Got a complete url but could not extract JWP endpoint');
|
||||
let pathname = this.reqBasePath && parsedUrl.pathname.startsWith(this.reqBasePath)
|
||||
? parsedUrl.pathname.replace(this.reqBasePath, '')
|
||||
: parsedUrl.pathname;
|
||||
const match = COMMAND_WITH_SESSION_ID_MATCHER(pathname);
|
||||
// This is needed for the backward compatibility
|
||||
// if drivers don't set reqBasePath properly
|
||||
if (!this.reqBasePath) {
|
||||
if (match && _.isArray(match.params?.prefix)) {
|
||||
pathname = pathname.replace(`/${match.params?.prefix.join('/')}`, '');
|
||||
} else if (_.startsWith(pathname, '/wd/hub')) {
|
||||
pathname = pathname.replace('/wd/hub', '');
|
||||
}
|
||||
remainingUrl = url.replace(first[1], '');
|
||||
} else if (new RegExp('^/').test(url)) {
|
||||
remainingUrl = url;
|
||||
} else {
|
||||
throw new Error(`Did not know what to do with url '${url}'`);
|
||||
}
|
||||
|
||||
const stripPrefixRe = new RegExp('^.*?(/(session|status).*)$');
|
||||
if (stripPrefixRe.test(remainingUrl)) {
|
||||
remainingUrl = /** @type {RegExpExecArray} */ (stripPrefixRe.exec(remainingUrl))[1];
|
||||
const normalizedPathname = _.trimEnd(
|
||||
match && _.isArray(match.params?.command)
|
||||
? `/${match.params.command.join('/')}`
|
||||
: pathname,
|
||||
'/'
|
||||
);
|
||||
const commandName = normalizedPathname
|
||||
? routeToCommandName(
|
||||
normalizedPathname,
|
||||
/** @type {import('@appium/types').HTTPMethod | undefined} */ (method)
|
||||
)
|
||||
: '';
|
||||
const requiresSessionId = !commandName || (commandName && isSessionCommand(commandName));
|
||||
const proxyPrefix = `${this.scheme}://${this.server}:${this.port}${this.base}`;
|
||||
let proxySuffix = normalizedPathname ? `/${_.trimStart(normalizedPathname, '/')}` : '';
|
||||
if (parsedUrl.search) {
|
||||
proxySuffix += parsedUrl.search;
|
||||
}
|
||||
|
||||
if (!new RegExp(endpointRe).test(remainingUrl)) {
|
||||
remainingUrl = `/session/${this.sessionId}${remainingUrl}`;
|
||||
if (!requiresSessionId) {
|
||||
return `${proxyPrefix}${proxySuffix}`;
|
||||
}
|
||||
|
||||
const requiresSessionId = this.endpointRequiresSessionId(remainingUrl);
|
||||
|
||||
if (requiresSessionId && this.sessionId === null) {
|
||||
throw new Error('Trying to proxy a session command without session id');
|
||||
if (!this.sessionId) {
|
||||
throw new ReferenceError(`Session ID is not set, but saw a URL that requires it (${url})`);
|
||||
}
|
||||
|
||||
const sessionBaseRe = new RegExp('^/session/([^/]+)');
|
||||
if (sessionBaseRe.test(remainingUrl)) {
|
||||
if (this.sessionId === null) {
|
||||
throw new ReferenceError(
|
||||
`Session ID is not set, but saw a URL path referencing a session (${remainingUrl}). This may be a bug in your client.`
|
||||
);
|
||||
}
|
||||
// we have something like /session/:id/foobar, so we need to replace
|
||||
// the session id
|
||||
const match = sessionBaseRe.exec(remainingUrl);
|
||||
// TODO: if `requiresSessionId` is `false` and `sessionId` is `null`, this is a bug.
|
||||
// are we sure `sessionId` is not `null`?
|
||||
remainingUrl = remainingUrl.replace(
|
||||
/** @type {RegExpExecArray} */ (match)[1],
|
||||
/** @type {string} */ (this.sessionId)
|
||||
);
|
||||
} else if (requiresSessionId) {
|
||||
throw new Error(`Could not find :session section for url: ${remainingUrl}`);
|
||||
}
|
||||
remainingUrl = remainingUrl.replace(/\/$/, ''); // can't have trailing slashes
|
||||
|
||||
return proxyBase + remainingUrl;
|
||||
return `${proxyPrefix}/session/${this.sessionId}${proxySuffix}`;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} url
|
||||
* @param {string} method
|
||||
* @param {any} body
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
async proxy(url, method, body = null) {
|
||||
method = method.toUpperCase();
|
||||
const newUrl = this.getUrlForProxy(url);
|
||||
const newUrl = this.getUrlForProxy(url, method);
|
||||
const truncateBody = (content) =>
|
||||
_.truncate(_.isString(content) ? content : JSON.stringify(content), {
|
||||
length: MAX_LOG_BODY_LENGTH,
|
||||
|
||||
@@ -3,7 +3,7 @@ const SUBSCRIPTION_REQUEST_PARAMS = /** @type {const} */ ({
|
||||
optional: ['contexts'],
|
||||
});
|
||||
|
||||
const BIDI_COMMANDS = /** @type {const} */ ({
|
||||
export const BIDI_COMMANDS = /** @type {const} */ ({
|
||||
session: {
|
||||
subscribe: {
|
||||
command: 'bidiSubscribe',
|
||||
@@ -31,5 +31,3 @@ const BIDI_COMMANDS = /** @type {const} */ ({
|
||||
|
||||
// TODO add definitions for all bidi commands.
|
||||
// spec link: https://w3c.github.io/webdriver-bidi/
|
||||
|
||||
export {BIDI_COMMANDS};
|
||||
|
||||
@@ -5,6 +5,8 @@ import {
|
||||
CREATE_SESSION_COMMAND,
|
||||
DELETE_SESSION_COMMAND,
|
||||
GET_STATUS_COMMAND,
|
||||
LIST_DRIVER_COMMANDS_COMMAND,
|
||||
LIST_DRIVER_EXTENSIONS_COMMAND,
|
||||
makeArgs,
|
||||
checkParams,
|
||||
validateExecuteMethodParams,
|
||||
@@ -30,4 +32,6 @@ export {
|
||||
CREATE_SESSION_COMMAND,
|
||||
DELETE_SESSION_COMMAND,
|
||||
GET_STATUS_COMMAND,
|
||||
LIST_DRIVER_COMMANDS_COMMAND,
|
||||
LIST_DRIVER_EXTENSIONS_COMMAND,
|
||||
};
|
||||
|
||||
@@ -16,9 +16,11 @@ import {isW3cCaps} from '../helpers/capabilities';
|
||||
import log from '../basedriver/logger';
|
||||
import { generateDriverLogPrefix } from '../basedriver/helpers';
|
||||
|
||||
const CREATE_SESSION_COMMAND = 'createSession';
|
||||
const DELETE_SESSION_COMMAND = 'deleteSession';
|
||||
const GET_STATUS_COMMAND = 'getStatus';
|
||||
export const CREATE_SESSION_COMMAND = 'createSession';
|
||||
export const DELETE_SESSION_COMMAND = 'deleteSession';
|
||||
export const GET_STATUS_COMMAND = 'getStatus';
|
||||
export const LIST_DRIVER_COMMANDS_COMMAND = 'listCommands';
|
||||
export const LIST_DRIVER_EXTENSIONS_COMMAND = 'listExtensions';
|
||||
|
||||
/** @type {Set<string>} */
|
||||
const deprecatedCommandsLogged = new Set();
|
||||
@@ -559,9 +561,6 @@ export {
|
||||
isSessionCommand,
|
||||
driverShouldDoJwpProxy,
|
||||
determineProtocol,
|
||||
CREATE_SESSION_COMMAND,
|
||||
DELETE_SESSION_COMMAND,
|
||||
GET_STATUS_COMMAND,
|
||||
deprecatedCommandsLogged,
|
||||
validateExecuteMethodParams,
|
||||
};
|
||||
|
||||
@@ -4,6 +4,12 @@ import _ from 'lodash';
|
||||
import {util} from '@appium/support';
|
||||
import {PROTOCOLS, DEFAULT_BASE_PATH} from '../constants';
|
||||
import {match} from 'path-to-regexp';
|
||||
import { LRUCache } from 'lru-cache';
|
||||
|
||||
/** @type {LRUCache<string, string>} */
|
||||
const COMMAND_NAMES_CACHE = new LRUCache({
|
||||
max: 1024,
|
||||
});
|
||||
|
||||
const SET_ALERT_TEXT_PAYLOAD_PARAMS = {
|
||||
validate: (jsonObj) =>
|
||||
@@ -22,6 +28,12 @@ const SET_ALERT_TEXT_PAYLOAD_PARAMS = {
|
||||
* @satisfies {import('@appium/types').MethodMap<import('../basedriver/driver').BaseDriver>}
|
||||
*/
|
||||
export const METHOD_MAP = /** @type {const} */ ({
|
||||
|
||||
//
|
||||
// W3C WebDriver (and deprecated MJSONWP that will be removed in Appium 3)
|
||||
// https://www.w3.org/TR/webdriver1/
|
||||
// https://www.w3.org/TR/webdriver2/
|
||||
//
|
||||
'/status': {
|
||||
GET: {command: 'getStatus'},
|
||||
},
|
||||
@@ -29,6 +41,7 @@ export const METHOD_MAP = /** @type {const} */ ({
|
||||
POST: {
|
||||
command: 'createSession',
|
||||
payloadParams: {
|
||||
// TODO: Appium 3 will accept only 'capabilities'.
|
||||
validate: (jsonObj) =>
|
||||
!jsonObj.capabilities &&
|
||||
!jsonObj.desiredCapabilities &&
|
||||
@@ -37,11 +50,8 @@ export const METHOD_MAP = /** @type {const} */ ({
|
||||
},
|
||||
},
|
||||
},
|
||||
'/sessions': {
|
||||
GET: {command: 'getSessions'},
|
||||
},
|
||||
'/session/:sessionId': {
|
||||
GET: {command: 'getSession'},
|
||||
GET: {command: 'getSession', deprecated: true},
|
||||
DELETE: {command: 'deleteSession'},
|
||||
},
|
||||
'/session/:sessionId/timeouts': {
|
||||
@@ -59,7 +69,7 @@ export const METHOD_MAP = /** @type {const} */ ({
|
||||
return 'W3C protocol expects any of script, pageLoad or implicit to be set';
|
||||
}
|
||||
} else {
|
||||
// MJSONWP
|
||||
// TODO: Remove in Appium 3
|
||||
if (!util.hasValue(jsonObj.type) || !util.hasValue(jsonObj.ms)) {
|
||||
return 'MJSONWP protocol requires type and ms';
|
||||
}
|
||||
@@ -69,25 +79,6 @@ export const METHOD_MAP = /** @type {const} */ ({
|
||||
},
|
||||
},
|
||||
},
|
||||
'/session/:sessionId/timeouts/async_script': {
|
||||
POST: {command: 'asyncScriptTimeout', payloadParams: {required: ['ms']}, deprecated: true},
|
||||
},
|
||||
'/session/:sessionId/timeouts/implicit_wait': {
|
||||
POST: {command: 'implicitWait', payloadParams: {required: ['ms']}, deprecated: true},
|
||||
},
|
||||
// JSONWP
|
||||
'/session/:sessionId/window_handle': {
|
||||
GET: {command: 'getWindowHandle'},
|
||||
},
|
||||
// W3C
|
||||
'/session/:sessionId/window/handle': {
|
||||
GET: {command: 'getWindowHandle'},
|
||||
},
|
||||
// JSONWP
|
||||
'/session/:sessionId/window_handles': {
|
||||
GET: {command: 'getWindowHandles'},
|
||||
},
|
||||
// W3C
|
||||
'/session/:sessionId/window/handles': {
|
||||
GET: {command: 'getWindowHandles'},
|
||||
},
|
||||
@@ -104,39 +95,10 @@ export const METHOD_MAP = /** @type {const} */ ({
|
||||
'/session/:sessionId/refresh': {
|
||||
POST: {command: 'refresh'},
|
||||
},
|
||||
// MJSONWP
|
||||
'/session/:sessionId/execute': {
|
||||
POST: {command: 'execute', payloadParams: {required: ['script', 'args']}},
|
||||
},
|
||||
// MJSONWP
|
||||
'/session/:sessionId/execute_async': {
|
||||
POST: {
|
||||
command: 'executeAsync',
|
||||
payloadParams: {required: ['script', 'args']},
|
||||
},
|
||||
},
|
||||
|
||||
'/session/:sessionId/screenshot': {
|
||||
GET: {command: 'getScreenshot'},
|
||||
},
|
||||
'/session/:sessionId/ime/available_engines': {
|
||||
GET: {command: 'availableIMEEngines', deprecated: true},
|
||||
},
|
||||
'/session/:sessionId/ime/active_engine': {
|
||||
GET: {command: 'getActiveIMEEngine', deprecated: true},
|
||||
},
|
||||
'/session/:sessionId/ime/activated': {
|
||||
GET: {command: 'isIMEActivated', deprecated: true},
|
||||
},
|
||||
'/session/:sessionId/ime/deactivate': {
|
||||
POST: {command: 'deactivateIMEEngine', deprecated: true},
|
||||
},
|
||||
'/session/:sessionId/ime/activate': {
|
||||
POST: {
|
||||
command: 'activateIMEEngine',
|
||||
payloadParams: {required: ['engine']},
|
||||
deprecated: true,
|
||||
},
|
||||
},
|
||||
'/session/:sessionId/frame': {
|
||||
POST: {command: 'setFrame', payloadParams: {required: ['id']}},
|
||||
},
|
||||
@@ -148,6 +110,7 @@ export const METHOD_MAP = /** @type {const} */ ({
|
||||
POST: {
|
||||
command: 'setWindow',
|
||||
payloadParams: {
|
||||
// TODO: Appium 3 will only accept 'handle'. 'name' will be ginored.
|
||||
optional: ['name', 'handle'],
|
||||
// Return both values to match W3C and JSONWP protocols
|
||||
makeArgs: (jsonObj) => {
|
||||
@@ -167,16 +130,18 @@ export const METHOD_MAP = /** @type {const} */ ({
|
||||
},
|
||||
DELETE: {command: 'closeWindow'},
|
||||
},
|
||||
'/session/:sessionId/window/:windowhandle/size': {
|
||||
GET: {command: 'getWindowSize', deprecated: true},
|
||||
},
|
||||
'/session/:sessionId/window/:windowhandle/position': {
|
||||
POST: {deprecated: true},
|
||||
GET: {deprecated: true},
|
||||
},
|
||||
'/session/:sessionId/window/:windowhandle/maximize': {
|
||||
'/session/:sessionId/window/maximize': {
|
||||
POST: {command: 'maximizeWindow'},
|
||||
},
|
||||
'/session/:sessionId/window/minimize': {
|
||||
POST: {command: 'minimizeWindow'},
|
||||
},
|
||||
'/session/:sessionId/window/fullscreen': {
|
||||
POST: {command: 'fullScreenWindow'},
|
||||
},
|
||||
'/session/:sessionId/window/new': {
|
||||
POST: {command: 'createNewWindow', payloadParams: {optional: ['type']}},
|
||||
},
|
||||
'/session/:sessionId/cookie': {
|
||||
GET: {command: 'getCookies'},
|
||||
POST: {command: 'setCookie', payloadParams: {required: ['cookie']}},
|
||||
@@ -206,7 +171,7 @@ export const METHOD_MAP = /** @type {const} */ ({
|
||||
},
|
||||
'/session/:sessionId/element/active': {
|
||||
GET: {command: 'active'}, // W3C: https://w3c.github.io/webdriver/webdriver-spec.html#dfn-get-active-element
|
||||
POST: {command: 'active'},
|
||||
POST: {command: 'active', deprecated: true},
|
||||
},
|
||||
'/session/:sessionId/element/:elementId': {
|
||||
GET: {},
|
||||
@@ -226,9 +191,6 @@ export const METHOD_MAP = /** @type {const} */ ({
|
||||
'/session/:sessionId/element/:elementId/click': {
|
||||
POST: {command: 'click'},
|
||||
},
|
||||
'/session/:sessionId/element/:elementId/submit': {
|
||||
POST: {command: 'submit', deprecated: true},
|
||||
},
|
||||
'/session/:sessionId/element/:elementId/text': {
|
||||
GET: {command: 'getText'},
|
||||
},
|
||||
@@ -240,6 +202,7 @@ export const METHOD_MAP = /** @type {const} */ ({
|
||||
!util.hasValue(jsonObj.value) &&
|
||||
!util.hasValue(jsonObj.text) &&
|
||||
'we require one of "text" or "value" params',
|
||||
// TODO: Appium 3 will accept only 'value'.
|
||||
optional: ['value', 'text'],
|
||||
// override the default argument constructor because of the special
|
||||
// logic here. Basically we want to accept either a value (old JSONWP)
|
||||
@@ -250,9 +213,6 @@ export const METHOD_MAP = /** @type {const} */ ({
|
||||
},
|
||||
},
|
||||
},
|
||||
'/session/:sessionId/keys': {
|
||||
POST: {command: 'keys', payloadParams: {required: ['value']}, deprecated: true},
|
||||
},
|
||||
'/session/:sessionId/element/:elementId/name': {
|
||||
GET: {command: 'getName'},
|
||||
},
|
||||
@@ -268,21 +228,9 @@ export const METHOD_MAP = /** @type {const} */ ({
|
||||
'/session/:sessionId/element/:elementId/attribute/:name': {
|
||||
GET: {command: 'getAttribute'},
|
||||
},
|
||||
'/session/:sessionId/element/:elementId/equals/:otherId': {
|
||||
GET: {command: 'equalsElement', deprecated: true},
|
||||
},
|
||||
'/session/:sessionId/element/:elementId/displayed': {
|
||||
GET: {command: 'elementDisplayed'},
|
||||
},
|
||||
'/session/:sessionId/element/:elementId/location': {
|
||||
GET: {command: 'getLocation', deprecated: true},
|
||||
},
|
||||
'/session/:sessionId/element/:elementId/location_in_view': {
|
||||
GET: {command: 'getLocationInView', deprecated: true},
|
||||
},
|
||||
'/session/:sessionId/element/:elementId/size': {
|
||||
GET: {command: 'getSize', deprecated: true},
|
||||
},
|
||||
'/session/:sessionId/element/:elementId/shadow': {
|
||||
GET: {command: 'elementShadowRoot'},
|
||||
},
|
||||
@@ -301,12 +249,8 @@ export const METHOD_MAP = /** @type {const} */ ({
|
||||
'/session/:sessionId/element/:elementId/css/:propertyName': {
|
||||
GET: {command: 'getCssProperty'},
|
||||
},
|
||||
'/session/:sessionId/orientation': {
|
||||
GET: {command: 'getOrientation'},
|
||||
POST: {
|
||||
command: 'setOrientation',
|
||||
payloadParams: {required: ['orientation']},
|
||||
},
|
||||
'/session/:sessionId/element/:elementId/property/:name': {
|
||||
GET: {command: 'getProperty'},
|
||||
},
|
||||
// w3c v2 https://www.w3.org/TR/webdriver2/#get-computed-role
|
||||
'session/:sessionId/element/:elementId/computedrole': {
|
||||
@@ -316,10 +260,336 @@ export const METHOD_MAP = /** @type {const} */ ({
|
||||
'session/:sessionId/element/:elementId/computedlabel': {
|
||||
GET: {command: 'getComputedLabel'},
|
||||
},
|
||||
'/session/:sessionId/actions': {
|
||||
POST: {command: 'performActions', payloadParams: {required: ['actions']}},
|
||||
DELETE: {command: 'releaseActions'},
|
||||
},
|
||||
'/session/:sessionId/alert/text': {
|
||||
GET: {command: 'getAlertText'},
|
||||
POST: {
|
||||
command: 'setAlertText',
|
||||
payloadParams: SET_ALERT_TEXT_PAYLOAD_PARAMS,
|
||||
},
|
||||
},
|
||||
'/session/:sessionId/alert/accept': {
|
||||
POST: {command: 'postAcceptAlert'},
|
||||
},
|
||||
'/session/:sessionId/alert/dismiss': {
|
||||
POST: {command: 'postDismissAlert'},
|
||||
},
|
||||
'/session/:sessionId/element/:elementId/rect': {
|
||||
GET: {command: 'getElementRect'},
|
||||
},
|
||||
'/session/:sessionId/execute/sync': {
|
||||
POST: {command: 'execute', payloadParams: {required: ['script', 'args']}},
|
||||
},
|
||||
'/session/:sessionId/execute/async': {
|
||||
POST: {
|
||||
command: 'executeAsync',
|
||||
payloadParams: {required: ['script', 'args']},
|
||||
},
|
||||
},
|
||||
'/session/:sessionId/element/:elementId/screenshot': {
|
||||
GET: {command: 'getElementScreenshot'},
|
||||
},
|
||||
'/session/:sessionId/window/rect': {
|
||||
GET: {command: 'getWindowRect'},
|
||||
POST: {
|
||||
command: 'setWindowRect',
|
||||
payloadParams: {optional: ['x', 'y', 'width', 'height']},
|
||||
},
|
||||
},
|
||||
|
||||
//
|
||||
// Appium specific
|
||||
//
|
||||
'/session/:sessionId/ime/available_engines': {
|
||||
GET: {command: 'availableIMEEngines'},
|
||||
},
|
||||
'/session/:sessionId/ime/active_engine': {
|
||||
GET: {command: 'getActiveIMEEngine'},
|
||||
},
|
||||
'/session/:sessionId/ime/activated': {
|
||||
GET: {command: 'isIMEActivated'},
|
||||
},
|
||||
'/session/:sessionId/ime/deactivate': {
|
||||
POST: {command: 'deactivateIMEEngine'},
|
||||
},
|
||||
'/session/:sessionId/ime/activate': {
|
||||
POST: {command: 'activateIMEEngine', payloadParams: {required: ['engine']}},
|
||||
},
|
||||
'/session/:sessionId/rotation': {
|
||||
GET: {command: 'getRotation'},
|
||||
POST: {command: 'setRotation', payloadParams: {required: ['x', 'y', 'z']}},
|
||||
},
|
||||
'/session/:sessionId/location': {
|
||||
GET: {command: 'getGeoLocation'},
|
||||
POST: {command: 'setGeoLocation', payloadParams: {required: ['location']}},
|
||||
},
|
||||
'/session/:sessionId/orientation': {
|
||||
GET: {command: 'getOrientation'},
|
||||
POST: {
|
||||
command: 'setOrientation',
|
||||
payloadParams: {required: ['orientation']}
|
||||
},
|
||||
},
|
||||
'/session/:sessionId/context': {
|
||||
GET: {command: 'getCurrentContext'},
|
||||
POST: {command: 'setContext', payloadParams: {required: ['name']}},
|
||||
},
|
||||
'/session/:sessionId/contexts': {
|
||||
GET: {command: 'getContexts'},
|
||||
},
|
||||
'/session/:sessionId/network_connection': {
|
||||
GET: {command: 'getNetworkConnection'},
|
||||
POST: {
|
||||
command: 'setNetworkConnection',
|
||||
payloadParams: {unwrap: 'parameters', required: ['type']},
|
||||
},
|
||||
},
|
||||
'/session/:sessionId/receive_async_response': {
|
||||
POST: {
|
||||
command: 'receiveAsyncResponse',
|
||||
payloadParams: {required: ['status', 'value']},
|
||||
},
|
||||
},
|
||||
'/appium/sessions': {
|
||||
GET: {command: 'getAppiumSessions'},
|
||||
},
|
||||
'/session/:sessionId/appium/capabilities': {
|
||||
GET: {command: 'getAppiumSessionCapabilities'}
|
||||
},
|
||||
'/session/:sessionId/appium/device/system_time': {
|
||||
GET: {command: 'getDeviceTime', payloadParams: {optional: ['format']}},
|
||||
POST: {command: 'getDeviceTime', payloadParams: {optional: ['format']}},
|
||||
},
|
||||
// #region Applications Management
|
||||
'/session/:sessionId/appium/device/install_app': {
|
||||
POST: {
|
||||
command: 'installApp',
|
||||
payloadParams: {
|
||||
required: ['appPath'],
|
||||
optional: ['options'],
|
||||
},
|
||||
},
|
||||
},
|
||||
'/session/:sessionId/appium/device/activate_app': {
|
||||
POST: {
|
||||
command: 'activateApp',
|
||||
payloadParams: {
|
||||
required: [['appId'], ['bundleId']],
|
||||
optional: ['options'],
|
||||
},
|
||||
},
|
||||
},
|
||||
'/session/:sessionId/appium/device/remove_app': {
|
||||
POST: {
|
||||
command: 'removeApp',
|
||||
payloadParams: {
|
||||
required: [['appId'], ['bundleId']],
|
||||
optional: ['options'],
|
||||
},
|
||||
},
|
||||
},
|
||||
'/session/:sessionId/appium/device/terminate_app': {
|
||||
POST: {
|
||||
command: 'terminateApp',
|
||||
payloadParams: {
|
||||
required: [['appId'], ['bundleId']],
|
||||
optional: ['options'],
|
||||
},
|
||||
},
|
||||
},
|
||||
'/session/:sessionId/appium/device/app_installed': {
|
||||
POST: {
|
||||
command: 'isAppInstalled',
|
||||
payloadParams: {
|
||||
required: [['appId'], ['bundleId']],
|
||||
},
|
||||
},
|
||||
},
|
||||
'/session/:sessionId/appium/device/app_state': {
|
||||
GET: {
|
||||
command: 'queryAppState',
|
||||
payloadParams: {
|
||||
required: [['appId'], ['bundleId']],
|
||||
},
|
||||
},
|
||||
POST: {
|
||||
command: 'queryAppState',
|
||||
payloadParams: {
|
||||
required: [['appId'], ['bundleId']],
|
||||
},
|
||||
deprecated: true,
|
||||
},
|
||||
},
|
||||
// #endregion
|
||||
'/session/:sessionId/appium/device/hide_keyboard': {
|
||||
POST: {
|
||||
command: 'hideKeyboard',
|
||||
payloadParams: {optional: ['strategy', 'key', 'keyCode', 'keyName']},
|
||||
},
|
||||
},
|
||||
'/session/:sessionId/appium/device/is_keyboard_shown': {
|
||||
GET: {command: 'isKeyboardShown'},
|
||||
},
|
||||
'/session/:sessionId/appium/device/push_file': {
|
||||
POST: {command: 'pushFile', payloadParams: {required: ['path', 'data']}},
|
||||
},
|
||||
'/session/:sessionId/appium/device/pull_file': {
|
||||
POST: {command: 'pullFile', payloadParams: {required: ['path']}},
|
||||
},
|
||||
'/session/:sessionId/appium/device/pull_folder': {
|
||||
POST: {command: 'pullFolder', payloadParams: {required: ['path']}},
|
||||
},
|
||||
'/session/:sessionId/appium/settings': {
|
||||
POST: {command: 'updateSettings', payloadParams: {required: ['settings']}},
|
||||
GET: {command: 'getSettings'},
|
||||
},
|
||||
'/session/:sessionId/appium/events': {
|
||||
POST: {command: 'getLogEvents', payloadParams: {optional: ['type']}},
|
||||
},
|
||||
'/session/:sessionId/appium/log_event': {
|
||||
POST: {
|
||||
command: 'logCustomEvent',
|
||||
payloadParams: {required: ['vendor', 'event']},
|
||||
},
|
||||
},
|
||||
// #region Inspector
|
||||
'/session/:sessionId/appium/commands': {
|
||||
GET: {command: 'listCommands'},
|
||||
},
|
||||
'/session/:sessionId/appium/extensions': {
|
||||
GET: {command: 'listExtensions'},
|
||||
},
|
||||
// #endregion
|
||||
|
||||
//
|
||||
// 3rd party vendor/protcol support
|
||||
//
|
||||
// #region Selenium/Chromium browsers
|
||||
'/session/:sessionId/se/log': {
|
||||
POST: {command: 'getLog', payloadParams: {required: ['type']}},
|
||||
},
|
||||
'/session/:sessionId/se/log/types': {
|
||||
GET: {command: 'getLogTypes'},
|
||||
},
|
||||
// #endregion
|
||||
|
||||
// #region chromium devtools
|
||||
// https://chromium.googlesource.com/chromium/src/+/master/chrome/test/chromedriver/server/http_handler.cc
|
||||
'/session/:sessionId/:vendor/cdp/execute': {
|
||||
POST: {command: 'executeCdp', payloadParams: {required: ['cmd', 'params']}},
|
||||
},
|
||||
// #endregion
|
||||
|
||||
// #region Webauthn
|
||||
// https://www.w3.org/TR/webauthn-2/#sctn-automation-add-virtual-authenticator
|
||||
'/session/:sessionId/webauthn/authenticator': {
|
||||
POST: {
|
||||
command: 'addVirtualAuthenticator',
|
||||
payloadParams: {
|
||||
required: ['protocol', 'transport'],
|
||||
optional: ['hasResidentKey', 'hasUserVerification', 'isUserConsenting', 'isUserVerified'],
|
||||
},
|
||||
},
|
||||
},
|
||||
'/session/:sessionId/webauthn/authenticator/:authenticatorId': {
|
||||
DELETE: {
|
||||
command: 'removeVirtualAuthenticator',
|
||||
},
|
||||
},
|
||||
'/session/:sessionId/webauthn/authenticator/:authenticatorId/credential': {
|
||||
POST: {
|
||||
command: 'addAuthCredential',
|
||||
payloadParams: {
|
||||
required: ['credentialId', 'isResidentCredential', 'rpId', 'privateKey'],
|
||||
optional: ['userHandle', 'signCount'],
|
||||
},
|
||||
},
|
||||
},
|
||||
'/session/:sessionId/webauthn/authenticator/:authenticatorId/credentials': {
|
||||
GET: {command: 'getAuthCredential'},
|
||||
DELETE: {command: 'removeAllAuthCredentials'},
|
||||
},
|
||||
'/session/:sessionId/webauthn/authenticator/:authenticatorId/credentials/:credentialId': {
|
||||
DELETE: {command: 'removeAuthCredential'},
|
||||
},
|
||||
'/session/:sessionId/webauthn/authenticator/:authenticatorId/uv': {
|
||||
POST: {
|
||||
command: 'setUserAuthVerified',
|
||||
payloadParams: {
|
||||
required: ['isUserVerified'],
|
||||
},
|
||||
},
|
||||
},
|
||||
// #endregion
|
||||
|
||||
//
|
||||
// Endpoints deprecated entirely
|
||||
//
|
||||
// #region MJSONWP
|
||||
'/sessions': {
|
||||
GET: {command: 'getSessions', deprecated: true},
|
||||
},
|
||||
'/session/:sessionId/timeouts/async_script': {
|
||||
POST: {command: 'asyncScriptTimeout', payloadParams: {required: ['ms']}, deprecated: true},
|
||||
},
|
||||
'/session/:sessionId/timeouts/implicit_wait': {
|
||||
POST: {command: 'implicitWait', payloadParams: {required: ['ms']}, deprecated: true},
|
||||
},
|
||||
'/session/:sessionId/window_handle': {
|
||||
GET: {command: 'getWindowHandle', deprecated: true},
|
||||
},
|
||||
// Only 'window/handles' exists in W3C WebDriver spec.
|
||||
'/session/:sessionId/window/handle': {
|
||||
GET: {command: 'getWindowHandle', deprecated: true},
|
||||
},
|
||||
'/session/:sessionId/window_handles': {
|
||||
GET: {command: 'getWindowHandles', deprecated: true},
|
||||
},
|
||||
'/session/:sessionId/execute': {
|
||||
POST: {
|
||||
command: 'execute',
|
||||
payloadParams: {required: ['script', 'args']},
|
||||
deprecated: true
|
||||
},
|
||||
},
|
||||
'/session/:sessionId/execute_async': {
|
||||
POST: {
|
||||
command: 'executeAsync',
|
||||
payloadParams: {required: ['script', 'args']},
|
||||
deprecated: true
|
||||
},
|
||||
},
|
||||
'/session/:sessionId/window/:windowhandle/size': {
|
||||
GET: {command: 'getWindowSize', deprecated: true},
|
||||
},
|
||||
'/session/:sessionId/window/:windowhandle/position': {
|
||||
POST: {deprecated: true},
|
||||
GET: {deprecated: true},
|
||||
},
|
||||
'/session/:sessionId/window/:windowhandle/maximize': {
|
||||
POST: {command: 'maximizeWindow', deprecated: true},
|
||||
},
|
||||
'/session/:sessionId/element/:elementId/submit': {
|
||||
POST: {command: 'submit', deprecated: true},
|
||||
},
|
||||
'/session/:sessionId/keys': {
|
||||
POST: {command: 'keys', payloadParams: {required: ['value']}, deprecated: true},
|
||||
},
|
||||
'/session/:sessionId/element/:elementId/equals/:otherId': {
|
||||
GET: {command: 'equalsElement', deprecated: true},
|
||||
},
|
||||
'/session/:sessionId/element/:elementId/location': {
|
||||
GET: {command: 'getLocation', deprecated: true},
|
||||
},
|
||||
'/session/:sessionId/element/:elementId/location_in_view': {
|
||||
GET: {command: 'getLocationInView', deprecated: true},
|
||||
},
|
||||
'/session/:sessionId/element/:elementId/size': {
|
||||
GET: {command: 'getSize', deprecated: true},
|
||||
},
|
||||
'/session/:sessionId/moveto': {
|
||||
POST: {
|
||||
command: 'moveTo',
|
||||
@@ -355,11 +625,7 @@ export const METHOD_MAP = /** @type {const} */ ({
|
||||
POST: {deprecated: true},
|
||||
},
|
||||
'/session/:sessionId/touch/doubleclick': {
|
||||
POST: {},
|
||||
},
|
||||
'/session/:sessionId/actions': {
|
||||
POST: {command: 'performActions', payloadParams: {required: ['actions']}},
|
||||
DELETE: {command: 'releaseActions'},
|
||||
POST: {deprecated: true},
|
||||
},
|
||||
'/session/:sessionId/touch/longclick': {
|
||||
POST: {
|
||||
@@ -377,10 +643,6 @@ export const METHOD_MAP = /** @type {const} */ ({
|
||||
deprecated: true,
|
||||
},
|
||||
},
|
||||
'/session/:sessionId/location': {
|
||||
GET: {command: 'getGeoLocation'},
|
||||
POST: {command: 'setGeoLocation', payloadParams: {required: ['location']}},
|
||||
},
|
||||
'/session/:sessionId/local_storage': {
|
||||
GET: {deprecated: true},
|
||||
POST: {deprecated: true},
|
||||
@@ -405,46 +667,32 @@ export const METHOD_MAP = /** @type {const} */ ({
|
||||
'/session/:sessionId/session_storage/size': {
|
||||
GET: {deprecated: true},
|
||||
},
|
||||
// Selenium 4 clients
|
||||
'/session/:sessionId/se/log': {
|
||||
POST: {command: 'getLog', payloadParams: {required: ['type']}},
|
||||
},
|
||||
// Selenium 4 clients
|
||||
'/session/:sessionId/se/log/types': {
|
||||
GET: {command: 'getLogTypes'},
|
||||
},
|
||||
// mjsonwire, appium clients
|
||||
'/session/:sessionId/log': {
|
||||
POST: {command: 'getLog', payloadParams: {required: ['type']}},
|
||||
},
|
||||
// mjsonwire, appium clients
|
||||
'/session/:sessionId/log/types': {
|
||||
GET: {command: 'getLogTypes'},
|
||||
},
|
||||
'/session/:sessionId/application_cache/status': {
|
||||
GET: {},
|
||||
GET: {deprecated: true},
|
||||
},
|
||||
|
||||
//
|
||||
// mjsonwire
|
||||
//
|
||||
'/session/:sessionId/context': {
|
||||
GET: {command: 'getCurrentContext'},
|
||||
POST: {command: 'setContext', payloadParams: {required: ['name']}},
|
||||
'/session/:sessionId/alert_text': {
|
||||
GET: {command: 'getAlertText', deprecated: true},
|
||||
POST: {
|
||||
command: 'setAlertText',
|
||||
payloadParams: SET_ALERT_TEXT_PAYLOAD_PARAMS,
|
||||
deprecated: true
|
||||
},
|
||||
},
|
||||
'/session/:sessionId/contexts': {
|
||||
GET: {command: 'getContexts'},
|
||||
'/session/:sessionId/accept_alert': {
|
||||
POST: {command: 'postAcceptAlert', deprecated: true},
|
||||
},
|
||||
'/session/:sessionId/dismiss_alert': {
|
||||
POST: {command: 'postDismissAlert', deprecated: true},
|
||||
},
|
||||
// Pre-W3C endpoint for element screenshot
|
||||
'/session/:sessionId/screenshot/:elementId': {
|
||||
GET: {command: 'getElementScreenshot', deprecated: true},
|
||||
},
|
||||
// #endregion
|
||||
// #region Appium specific
|
||||
'/session/:sessionId/element/:elementId/pageIndex': {
|
||||
GET: {command: 'getPageIndex', deprecated: true},
|
||||
},
|
||||
'/session/:sessionId/network_connection': {
|
||||
GET: {command: 'getNetworkConnection'},
|
||||
POST: {
|
||||
command: 'setNetworkConnection',
|
||||
payloadParams: {unwrap: 'parameters', required: ['type']},
|
||||
},
|
||||
},
|
||||
'/session/:sessionId/touch/perform': {
|
||||
POST: {
|
||||
command: 'performTouch',
|
||||
@@ -459,19 +707,9 @@ export const METHOD_MAP = /** @type {const} */ ({
|
||||
deprecated: true,
|
||||
},
|
||||
},
|
||||
'/session/:sessionId/receive_async_response': {
|
||||
POST: {
|
||||
command: 'receiveAsyncResponse',
|
||||
payloadParams: {required: ['status', 'value']},
|
||||
},
|
||||
},
|
||||
'/session/:sessionId/appium/device/shake': {
|
||||
POST: {command: 'mobileShake', deprecated: true},
|
||||
},
|
||||
'/session/:sessionId/appium/device/system_time': {
|
||||
GET: {command: 'getDeviceTime', payloadParams: {optional: ['format']}},
|
||||
POST: {command: 'getDeviceTime', payloadParams: {optional: ['format']}},
|
||||
},
|
||||
'/session/:sessionId/appium/device/lock': {
|
||||
POST: {command: 'lock', payloadParams: {optional: ['seconds']}, deprecated: true},
|
||||
},
|
||||
@@ -575,85 +813,6 @@ export const METHOD_MAP = /** @type {const} */ ({
|
||||
'/session/:sessionId/appium/device/current_package': {
|
||||
GET: {command: 'getCurrentPackage', deprecated: true},
|
||||
},
|
||||
//region Applications Management
|
||||
'/session/:sessionId/appium/device/install_app': {
|
||||
POST: {
|
||||
command: 'installApp',
|
||||
payloadParams: {
|
||||
required: ['appPath'],
|
||||
optional: ['options'],
|
||||
},
|
||||
},
|
||||
},
|
||||
'/session/:sessionId/appium/device/activate_app': {
|
||||
POST: {
|
||||
command: 'activateApp',
|
||||
payloadParams: {
|
||||
required: [['appId'], ['bundleId']],
|
||||
optional: ['options'],
|
||||
},
|
||||
},
|
||||
},
|
||||
'/session/:sessionId/appium/device/remove_app': {
|
||||
POST: {
|
||||
command: 'removeApp',
|
||||
payloadParams: {
|
||||
required: [['appId'], ['bundleId']],
|
||||
optional: ['options'],
|
||||
},
|
||||
},
|
||||
},
|
||||
'/session/:sessionId/appium/device/terminate_app': {
|
||||
POST: {
|
||||
command: 'terminateApp',
|
||||
payloadParams: {
|
||||
required: [['appId'], ['bundleId']],
|
||||
optional: ['options'],
|
||||
},
|
||||
},
|
||||
},
|
||||
'/session/:sessionId/appium/device/app_installed': {
|
||||
POST: {
|
||||
command: 'isAppInstalled',
|
||||
payloadParams: {
|
||||
required: [['appId'], ['bundleId']],
|
||||
},
|
||||
},
|
||||
},
|
||||
'/session/:sessionId/appium/device/app_state': {
|
||||
GET: {
|
||||
command: 'queryAppState',
|
||||
payloadParams: {
|
||||
required: [['appId'], ['bundleId']],
|
||||
},
|
||||
},
|
||||
POST: {
|
||||
command: 'queryAppState',
|
||||
payloadParams: {
|
||||
required: [['appId'], ['bundleId']],
|
||||
},
|
||||
deprecated: true,
|
||||
},
|
||||
},
|
||||
//endregion
|
||||
'/session/:sessionId/appium/device/hide_keyboard': {
|
||||
POST: {
|
||||
command: 'hideKeyboard',
|
||||
payloadParams: {optional: ['strategy', 'key', 'keyCode', 'keyName']},
|
||||
},
|
||||
},
|
||||
'/session/:sessionId/appium/device/is_keyboard_shown': {
|
||||
GET: {command: 'isKeyboardShown'},
|
||||
},
|
||||
'/session/:sessionId/appium/device/push_file': {
|
||||
POST: {command: 'pushFile', payloadParams: {required: ['path', 'data']}},
|
||||
},
|
||||
'/session/:sessionId/appium/device/pull_file': {
|
||||
POST: {command: 'pullFile', payloadParams: {required: ['path']}},
|
||||
},
|
||||
'/session/:sessionId/appium/device/pull_folder': {
|
||||
POST: {command: 'pullFolder', payloadParams: {required: ['path']}},
|
||||
},
|
||||
'/session/:sessionId/appium/device/toggle_airplane_mode': {
|
||||
POST: {command: 'toggleFlightMode', deprecated: true},
|
||||
},
|
||||
@@ -743,10 +902,6 @@ export const METHOD_MAP = /** @type {const} */ ({
|
||||
deprecated: true,
|
||||
},
|
||||
},
|
||||
'/session/:sessionId/appium/settings': {
|
||||
POST: {command: 'updateSettings', payloadParams: {required: ['settings']}},
|
||||
GET: {command: 'getSettings'},
|
||||
},
|
||||
'/session/:sessionId/appium/receive_async_response': {
|
||||
POST: {
|
||||
command: 'receiveAsyncResponse',
|
||||
@@ -754,94 +909,6 @@ export const METHOD_MAP = /** @type {const} */ ({
|
||||
deprecated: true,
|
||||
},
|
||||
},
|
||||
'/session/:sessionId/appium/events': {
|
||||
POST: {command: 'getLogEvents', payloadParams: {optional: ['type']}},
|
||||
},
|
||||
'/session/:sessionId/appium/log_event': {
|
||||
POST: {
|
||||
command: 'logCustomEvent',
|
||||
payloadParams: {required: ['vendor', 'event']},
|
||||
},
|
||||
},
|
||||
|
||||
/*
|
||||
* The W3C spec has some changes to the wire protocol.
|
||||
* https://w3c.github.io/webdriver/webdriver-spec.html
|
||||
* Begin to add those changes here, keeping the old version
|
||||
* since clients still implement them.
|
||||
*/
|
||||
// MJSONWP
|
||||
'/session/:sessionId/alert_text': {
|
||||
GET: {command: 'getAlertText'},
|
||||
POST: {
|
||||
command: 'setAlertText',
|
||||
payloadParams: SET_ALERT_TEXT_PAYLOAD_PARAMS,
|
||||
},
|
||||
},
|
||||
// MJSONWP
|
||||
'/session/:sessionId/accept_alert': {
|
||||
POST: {command: 'postAcceptAlert'},
|
||||
},
|
||||
// MJSONWP
|
||||
'/session/:sessionId/dismiss_alert': {
|
||||
POST: {command: 'postDismissAlert'},
|
||||
},
|
||||
// https://w3c.github.io/webdriver/webdriver-spec.html#user-prompts
|
||||
'/session/:sessionId/alert/text': {
|
||||
GET: {command: 'getAlertText'},
|
||||
POST: {
|
||||
command: 'setAlertText',
|
||||
payloadParams: SET_ALERT_TEXT_PAYLOAD_PARAMS,
|
||||
},
|
||||
},
|
||||
'/session/:sessionId/alert/accept': {
|
||||
POST: {command: 'postAcceptAlert'},
|
||||
},
|
||||
'/session/:sessionId/alert/dismiss': {
|
||||
POST: {command: 'postDismissAlert'},
|
||||
},
|
||||
// https://w3c.github.io/webdriver/webdriver-spec.html#get-element-rect
|
||||
'/session/:sessionId/element/:elementId/rect': {
|
||||
GET: {command: 'getElementRect'},
|
||||
},
|
||||
'/session/:sessionId/execute/sync': {
|
||||
POST: {command: 'execute', payloadParams: {required: ['script', 'args']}},
|
||||
},
|
||||
'/session/:sessionId/execute/async': {
|
||||
POST: {
|
||||
command: 'executeAsync',
|
||||
payloadParams: {required: ['script', 'args']},
|
||||
},
|
||||
},
|
||||
// Pre-W3C endpoint for element screenshot
|
||||
'/session/:sessionId/screenshot/:elementId': {
|
||||
GET: {command: 'getElementScreenshot'},
|
||||
},
|
||||
'/session/:sessionId/element/:elementId/screenshot': {
|
||||
GET: {command: 'getElementScreenshot'},
|
||||
},
|
||||
'/session/:sessionId/window/rect': {
|
||||
GET: {command: 'getWindowRect'},
|
||||
POST: {
|
||||
command: 'setWindowRect',
|
||||
payloadParams: {optional: ['x', 'y', 'width', 'height']},
|
||||
},
|
||||
},
|
||||
'/session/:sessionId/window/maximize': {
|
||||
POST: {command: 'maximizeWindow'},
|
||||
},
|
||||
'/session/:sessionId/window/minimize': {
|
||||
POST: {command: 'minimizeWindow'},
|
||||
},
|
||||
'/session/:sessionId/window/fullscreen': {
|
||||
POST: {command: 'fullScreenWindow'},
|
||||
},
|
||||
'/session/:sessionId/window/new': {
|
||||
POST: {command: 'createNewWindow', payloadParams: {optional: ['type']}},
|
||||
},
|
||||
'/session/:sessionId/element/:elementId/property/:name': {
|
||||
GET: {command: 'getProperty'},
|
||||
},
|
||||
'/session/:sessionId/appium/device/set_clipboard': {
|
||||
POST: {
|
||||
command: 'setClipboard',
|
||||
@@ -861,61 +928,15 @@ export const METHOD_MAP = /** @type {const} */ ({
|
||||
deprecated: true,
|
||||
},
|
||||
},
|
||||
|
||||
// chromium devtools
|
||||
// https://chromium.googlesource.com/chromium/src/+/master/chrome/test/chromedriver/server/http_handler.cc
|
||||
'/session/:sessionId/:vendor/cdp/execute': {
|
||||
POST: {command: 'executeCdp', payloadParams: {required: ['cmd', 'params']}},
|
||||
// #endregion
|
||||
// #region JSONWP
|
||||
'/session/:sessionId/log': {
|
||||
POST: {command: 'getLog', payloadParams: {required: ['type']}, deprecated: true},
|
||||
},
|
||||
|
||||
//region Webauthn
|
||||
// https://www.w3.org/TR/webauthn-2/#sctn-automation-add-virtual-authenticator
|
||||
|
||||
'/session/:sessionId/webauthn/authenticator': {
|
||||
POST: {
|
||||
command: 'addVirtualAuthenticator',
|
||||
payloadParams: {
|
||||
required: ['protocol', 'transport'],
|
||||
optional: ['hasResidentKey', 'hasUserVerification', 'isUserConsenting', 'isUserVerified'],
|
||||
},
|
||||
},
|
||||
'/session/:sessionId/log/types': {
|
||||
GET: {command: 'getLogTypes', deprecated: true},
|
||||
},
|
||||
|
||||
'/session/:sessionId/webauthn/authenticator/:authenticatorId': {
|
||||
DELETE: {
|
||||
command: 'removeVirtualAuthenticator',
|
||||
},
|
||||
},
|
||||
|
||||
'/session/:sessionId/webauthn/authenticator/:authenticatorId/credential': {
|
||||
POST: {
|
||||
command: 'addAuthCredential',
|
||||
payloadParams: {
|
||||
required: ['credentialId', 'isResidentCredential', 'rpId', 'privateKey'],
|
||||
optional: ['userHandle', 'signCount'],
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
'/session/:sessionId/webauthn/authenticator/:authenticatorId/credentials': {
|
||||
GET: {command: 'getAuthCredential'},
|
||||
DELETE: {command: 'removeAllAuthCredentials'},
|
||||
},
|
||||
|
||||
'/session/:sessionId/webauthn/authenticator/:authenticatorId/credentials/:credentialId': {
|
||||
DELETE: {command: 'removeAuthCredential'},
|
||||
},
|
||||
|
||||
'/session/:sessionId/webauthn/authenticator/:authenticatorId/uv': {
|
||||
POST: {
|
||||
command: 'setUserAuthVerified',
|
||||
payloadParams: {
|
||||
required: ['isUserVerified'],
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
//endregion
|
||||
// #endregion
|
||||
});
|
||||
|
||||
// driver command names
|
||||
@@ -926,7 +947,7 @@ export const ALL_COMMANDS = _.flatMap(_.values(METHOD_MAP).map(_.values))
|
||||
/**
|
||||
*
|
||||
* @param {string} endpoint
|
||||
* @param {import('@appium/types').HTTPMethod} method
|
||||
* @param {import('@appium/types').HTTPMethod} [method]
|
||||
* @param {string} [basePath=DEFAULT_BASE_PATH]
|
||||
* @returns {string|undefined}
|
||||
*/
|
||||
@@ -944,23 +965,47 @@ export function routeToCommandName(endpoint, method, basePath = DEFAULT_BASE_PAT
|
||||
throw new Error(`'${endpoint}' cannot be translated to a command name: ${err.message}`);
|
||||
}
|
||||
|
||||
const normalizedMethod = _.toUpper(method);
|
||||
const cacheKey = toCommandNameCacheKey(normalizedPathname, normalizedMethod);
|
||||
if (COMMAND_NAMES_CACHE.has(cacheKey)) {
|
||||
return COMMAND_NAMES_CACHE.get(cacheKey) || undefined;
|
||||
}
|
||||
|
||||
/** @type {string[]} */
|
||||
const possiblePathnames = [];
|
||||
if (!normalizedPathname.startsWith('/session/')) {
|
||||
possiblePathnames.push(`/session/any-session-id${normalizedPathname}`);
|
||||
}
|
||||
possiblePathnames.push(normalizedPathname);
|
||||
const normalizedMethod = _.toUpper(method);
|
||||
for (const [routePath, routeSpec] of _.toPairs(METHOD_MAP)) {
|
||||
const routeMatcher = match(routePath);
|
||||
if (possiblePathnames.some((pp) => routeMatcher(pp))) {
|
||||
const commandName = routeSpec?.[normalizedMethod]?.command;
|
||||
const commandForAnyMethod = () => _.first(
|
||||
(_.keys(routeSpec) ?? []).map((key) => routeSpec[key]?.command)
|
||||
);
|
||||
const commandName = normalizedMethod
|
||||
? routeSpec?.[normalizedMethod]?.command
|
||||
: commandForAnyMethod();
|
||||
if (commandName) {
|
||||
COMMAND_NAMES_CACHE.set(cacheKey, commandName);
|
||||
return commandName;
|
||||
}
|
||||
}
|
||||
}
|
||||
// storing an empty string means we did not find any match for this set of arguments
|
||||
// and we want to cache this result
|
||||
COMMAND_NAMES_CACHE.set(cacheKey, '');
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} endpoint
|
||||
* @param {string} [method]
|
||||
* @returns {string}
|
||||
*/
|
||||
function toCommandNameCacheKey(endpoint, method) {
|
||||
return `${endpoint}:${method ?? ''}`;
|
||||
}
|
||||
|
||||
// driver commands that do not require a session to already exist
|
||||
export const NO_SESSION_ID_COMMANDS = ['createSession', 'getStatus', 'getSessions'];
|
||||
export const NO_SESSION_ID_COMMANDS = ['createSession', 'getStatus', 'getSessions', 'getAppiumSessions'];
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@appium/base-driver",
|
||||
"version": "9.15.0",
|
||||
"version": "9.16.2",
|
||||
"description": "Base driver class for Appium drivers",
|
||||
"keywords": [
|
||||
"automation",
|
||||
@@ -44,21 +44,16 @@
|
||||
"test:types": "tsd"
|
||||
},
|
||||
"dependencies": {
|
||||
"@appium/support": "^6.0.3",
|
||||
"@appium/types": "^0.24.0",
|
||||
"@appium/support": "^6.0.6",
|
||||
"@appium/types": "^0.25.1",
|
||||
"@colors/colors": "1.6.0",
|
||||
"@types/async-lock": "1.4.2",
|
||||
"@types/bluebird": "3.5.42",
|
||||
"@types/express": "5.0.0",
|
||||
"@types/lodash": "4.17.14",
|
||||
"@types/method-override": "3.0.0",
|
||||
"@types/serve-favicon": "2.5.7",
|
||||
"async-lock": "1.4.1",
|
||||
"asyncbox": "3.0.0",
|
||||
"axios": "1.7.9",
|
||||
"bluebird": "3.7.2",
|
||||
"body-parser": "1.20.3",
|
||||
"express": "4.21.2",
|
||||
"fastest-levenshtein": "1.0.16",
|
||||
"http-status-codes": "2.3.0",
|
||||
"lodash": "4.17.21",
|
||||
"lru-cache": "10.4.3",
|
||||
@@ -67,7 +62,7 @@
|
||||
"path-to-regexp": "8.2.0",
|
||||
"serve-favicon": "2.5.0",
|
||||
"source-map-support": "0.5.21",
|
||||
"type-fest": "4.31.0",
|
||||
"type-fest": "4.35.0",
|
||||
"validate.js": "0.13.1"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
|
||||
@@ -1,13 +1,63 @@
|
||||
import {BaseDriver} from '../../../lib';
|
||||
import {driverE2ETestSuite} from '@appium/driver-test-support';
|
||||
import { BaseDriver, server, routeConfiguringFunction } from '../../../lib';
|
||||
import { FakeDriver } from '../protocol/fake-driver';
|
||||
import axios from 'axios';
|
||||
import {TEST_HOST, getTestPort, driverE2ETestSuite} from '@appium/driver-test-support';
|
||||
|
||||
const DEFAULT_CAPS = {
|
||||
platformName: 'iOS',
|
||||
'appium:deviceName': 'Delorean',
|
||||
platformName: 'fake',
|
||||
'appium:automationNAme': 'fake',
|
||||
};
|
||||
|
||||
driverE2ETestSuite(BaseDriver, DEFAULT_CAPS);
|
||||
driverE2ETestSuite(BaseDriver, {
|
||||
platformName: 'iOS',
|
||||
'appium:deviceName': 'Delorean',
|
||||
});
|
||||
|
||||
/**
|
||||
* @typedef {import('@appium/driver-test-support').SessionHelpers} SessionHelpers
|
||||
*/
|
||||
describe('BaseDriver', function () {
|
||||
let port;
|
||||
let baseUrl;
|
||||
|
||||
before(async function () {
|
||||
const chai = await import('chai');
|
||||
const chaisAsPromised = await import('chai-as-promised');
|
||||
chai.use(chaisAsPromised.default);
|
||||
chai.should();
|
||||
|
||||
port = await getTestPort();
|
||||
baseUrl = `http://${TEST_HOST}:${port}`;
|
||||
});
|
||||
|
||||
describe('get appium capabilities', function () {
|
||||
let driver;
|
||||
let sessionId = 'foo';
|
||||
let mjsonwpServer;
|
||||
|
||||
before(async function () {
|
||||
driver = new FakeDriver();
|
||||
driver.sessionId = sessionId;
|
||||
mjsonwpServer = await server({
|
||||
routeConfiguringFunction: routeConfiguringFunction(driver),
|
||||
port,
|
||||
});
|
||||
});
|
||||
|
||||
after(async function () {
|
||||
await mjsonwpServer.close();
|
||||
});
|
||||
|
||||
it('should return capabilities', async function () {
|
||||
const capabilities = DEFAULT_CAPS;
|
||||
driver.caps = capabilities;
|
||||
const {data} = await axios({
|
||||
url: `${baseUrl}/session/${sessionId}/appium/capabilities`,
|
||||
method: 'GET',
|
||||
});
|
||||
data.should.eql({
|
||||
value: {
|
||||
capabilities: DEFAULT_CAPS
|
||||
},
|
||||
sessionId
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -61,52 +61,81 @@ describe('proxy', function () {
|
||||
});
|
||||
describe('getUrlForProxy', function () {
|
||||
it('should modify session id, host, and port', function () {
|
||||
let j = mockProxy({sessionId: '123'});
|
||||
j.getUrlForProxy('http://host.com:1234/session/456/element/200/value').should.eql(
|
||||
`http://${TEST_HOST}:${port}/session/123/element/200/value`
|
||||
);
|
||||
mockProxy({sessionId: '123'})
|
||||
.getUrlForProxy('http://host.com:1234/session/456/element/200/value', 'POST').should.eql(
|
||||
`http://${TEST_HOST}:${port}/session/123/element/200/value`
|
||||
);
|
||||
});
|
||||
it('should prepend scheme, host and port if not provided', function () {
|
||||
let j = mockProxy({sessionId: '123'});
|
||||
j.getUrlForProxy('/session/456/element/200/value').should.eql(
|
||||
j.getUrlForProxy('/session/456/element/200/value', 'POST').should.eql(
|
||||
`http://${TEST_HOST}:${port}/session/123/element/200/value`
|
||||
);
|
||||
j.getUrlForProxy('/session/456/appium/settings', 'POST').should.eql(
|
||||
`http://${TEST_HOST}:${port}/session/123/appium/settings`
|
||||
);
|
||||
});
|
||||
it('should prepend scheme, host, port and session if not provided', function () {
|
||||
mockProxy({sessionId: '123'})
|
||||
.getUrlForProxy('/element/200/value', 'POST').should.eql(
|
||||
`http://${TEST_HOST}:${port}/session/123/element/200/value`
|
||||
);
|
||||
});
|
||||
it('should keep query parameters', function () {
|
||||
mockProxy({sessionId: '123'})
|
||||
.getUrlForProxy('/element/200/value?foo=1&bar=2', 'POST').should.eql(
|
||||
`http://${TEST_HOST}:${port}/session/123/element/200/value?foo=1&bar=2`
|
||||
);
|
||||
});
|
||||
it('should fix legacy proxy urls if reqBasePath is unset', function () {
|
||||
const j = mockProxy({sessionId: '123', reqBasePath: ''});
|
||||
j.getUrlForProxy('/wd/hub/session/456/element/200/value', 'POST').should.eql(
|
||||
`http://${TEST_HOST}:${port}/session/123/element/200/value`
|
||||
);
|
||||
j.getUrlForProxy('/yolo/session/456/element/200/value', 'POST').should.eql(
|
||||
`http://${TEST_HOST}:${port}/session/123/element/200/value`
|
||||
);
|
||||
});
|
||||
it('should respect nonstandard incoming request base path', function () {
|
||||
let j = mockProxy({sessionId: '123', reqBasePath: ''});
|
||||
j.getUrlForProxy('/session/456/element/200/value').should.eql(
|
||||
`http://${TEST_HOST}:${port}/session/123/element/200/value`
|
||||
);
|
||||
mockProxy({sessionId: '123', reqBasePath: ''})
|
||||
.getUrlForProxy('/session/456/element/200/value', 'POST').should.eql(
|
||||
`http://${TEST_HOST}:${port}/session/123/element/200/value`
|
||||
);
|
||||
|
||||
j = mockProxy({sessionId: '123', reqBasePath: '/my/base/path'});
|
||||
j.getUrlForProxy('/my/base/path/session/456/element/200/value').should.eql(
|
||||
`http://${TEST_HOST}:${port}/session/123/element/200/value`
|
||||
);
|
||||
mockProxy({sessionId: '123', reqBasePath: '/my/base/path'})
|
||||
.getUrlForProxy('/my/base/path/session/456/element/200/value', 'POST').should.eql(
|
||||
`http://${TEST_HOST}:${port}/session/123/element/200/value`
|
||||
);
|
||||
|
||||
mockProxy({reqBasePath: '/my/base/path'})
|
||||
.getUrlForProxy('/my/base/path/session', 'POST').should.eql(
|
||||
`http://${TEST_HOST}:${port}/session`
|
||||
);
|
||||
});
|
||||
it('should work with urls which do not have session ids', function () {
|
||||
let j = mockProxy({sessionId: '123'});
|
||||
j.getUrlForProxy('http://host.com:1234/session').should.eql(
|
||||
j.getUrlForProxy('http://host.com:1234/session', 'POST').should.eql(
|
||||
`http://${TEST_HOST}:${port}/session`
|
||||
);
|
||||
|
||||
let newUrl = j.getUrlForProxy('/session');
|
||||
newUrl.should.eql(`http://${TEST_HOST}:${port}/session`);
|
||||
j.getUrlForProxy('/session', 'POST')
|
||||
.should.eql(`http://${TEST_HOST}:${port}/session`);
|
||||
j.getUrlForProxy('/appium/sessions', 'GET')
|
||||
.should.eql(`http://${TEST_HOST}:${port}/appium/sessions`);
|
||||
});
|
||||
it('should throw an error if url requires a sessionId but its null', function () {
|
||||
let j = mockProxy();
|
||||
let e;
|
||||
try {
|
||||
j.getUrlForProxy('/session/456/element/200/value');
|
||||
j.getUrlForProxy('/session/456/element/200/value', 'POST');
|
||||
} catch (err) {
|
||||
e = err;
|
||||
}
|
||||
should.exist(e);
|
||||
e.message.should.contain('without session id');
|
||||
e.message.should.contain('not set');
|
||||
});
|
||||
it('should not throw an error if url does not require a session id and its null', function () {
|
||||
let j = mockProxy();
|
||||
let newUrl = j.getUrlForProxy('/status');
|
||||
|
||||
let newUrl = mockProxy().getUrlForProxy('/status', 'GET');
|
||||
should.exist(newUrl);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -37,7 +37,7 @@ describe('JWProxy', function () {
|
||||
it('should translate host and port', function () {
|
||||
let incomingUrl = PROXY_STATUS_URL;
|
||||
let j = createJWProxy();
|
||||
let proxyUrl = j.getUrlForProxy(incomingUrl);
|
||||
let proxyUrl = j.getUrlForProxy(incomingUrl, 'GET');
|
||||
proxyUrl.should.equal(testStatusURL);
|
||||
});
|
||||
it('should translate the scheme', function () {
|
||||
@@ -49,21 +49,21 @@ describe('JWProxy', function () {
|
||||
it('should translate the base', function () {
|
||||
let incomingUrl = PROXY_STATUS_URL;
|
||||
let j = createJWProxy({base: ''});
|
||||
let proxyUrl = j.getUrlForProxy(incomingUrl);
|
||||
let proxyUrl = j.getUrlForProxy(incomingUrl, 'GET');
|
||||
proxyUrl.should.equal(testStatusURL);
|
||||
});
|
||||
it('should translate the session id', function () {
|
||||
let incomingUrl = createProxyURL('foobar', 'element');
|
||||
let j = createJWProxy({sessionId: 'barbaz'});
|
||||
let proxyUrl = j.getUrlForProxy(incomingUrl);
|
||||
let proxyUrl = j.getUrlForProxy(incomingUrl, 'POST');
|
||||
proxyUrl.should.equal(createTestURL('barbaz', 'element'));
|
||||
});
|
||||
it('should error when translating session commands without session id', function () {
|
||||
let incomingUrl = createProxyURL('foobar', 'element');
|
||||
let j = createJWProxy();
|
||||
(() => {
|
||||
j.getUrlForProxy(incomingUrl);
|
||||
}).should.throw('session id');
|
||||
j.getUrlForProxy(incomingUrl, 'POST');
|
||||
}).should.throw('not set');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -71,7 +71,7 @@ describe('JWProxy', function () {
|
||||
it('should proxy /status', function () {
|
||||
let incomingUrl = '/status';
|
||||
let j = createJWProxy();
|
||||
let proxyUrl = j.getUrlForProxy(incomingUrl);
|
||||
let proxyUrl = j.getUrlForProxy(incomingUrl, 'GET');
|
||||
proxyUrl.should.equal(testStatusURL);
|
||||
});
|
||||
it('should proxy /session', function () {
|
||||
@@ -83,64 +83,64 @@ describe('JWProxy', function () {
|
||||
it('should proxy /sessions', function () {
|
||||
let incomingUrl = '/sessions';
|
||||
let j = createJWProxy();
|
||||
let proxyUrl = j.getUrlForProxy(incomingUrl);
|
||||
let proxyUrl = j.getUrlForProxy(incomingUrl, 'GET');
|
||||
proxyUrl.should.equal(createTestURL('', 'sessions'));
|
||||
});
|
||||
it('should proxy session commands based off /session', function () {
|
||||
let incomingUrl = '/session/foobar/element';
|
||||
let j = createJWProxy({sessionId: 'barbaz'});
|
||||
let proxyUrl = j.getUrlForProxy(incomingUrl);
|
||||
let proxyUrl = j.getUrlForProxy(incomingUrl, 'POST');
|
||||
proxyUrl.should.equal(createTestURL('barbaz', 'element'));
|
||||
});
|
||||
it('should error session commands based off /session without session id', function () {
|
||||
let incomingUrl = '/session/foobar/element';
|
||||
let j = createJWProxy();
|
||||
(() => {
|
||||
j.getUrlForProxy(incomingUrl);
|
||||
}).should.throw('session id');
|
||||
j.getUrlForProxy(incomingUrl, 'POST');
|
||||
}).should.throw('not set');
|
||||
});
|
||||
it('should proxy session commands based off ', function () {
|
||||
let incomingUrl = '/session/3d001db2-7987-42a7-975d-8d5d5304083f/timeouts/implicit_wait';
|
||||
let j = createJWProxy({sessionId: '123'});
|
||||
let proxyUrl = j.getUrlForProxy(incomingUrl);
|
||||
let proxyUrl = j.getUrlForProxy(incomingUrl, 'POST');
|
||||
proxyUrl.should.equal(createTestURL('123', 'timeouts/implicit_wait'));
|
||||
});
|
||||
it('should proxy session commands based off /session as ""', function () {
|
||||
let incomingUrl = '';
|
||||
let j = createJWProxy();
|
||||
(() => {
|
||||
j.getUrlForProxy(incomingUrl);
|
||||
}).should.throw('session id');
|
||||
j.getUrlForProxy(incomingUrl, 'GET');
|
||||
}).should.throw('not set');
|
||||
j = createJWProxy({sessionId: '123'});
|
||||
let proxyUrl = j.getUrlForProxy(incomingUrl);
|
||||
let proxyUrl = j.getUrlForProxy(incomingUrl, 'GET');
|
||||
proxyUrl.should.equal(createTestSessionURL('123'));
|
||||
});
|
||||
it('should proxy session commands without /session', function () {
|
||||
let incomingUrl = '/element';
|
||||
let j = createJWProxy({sessionId: 'barbaz'});
|
||||
let proxyUrl = j.getUrlForProxy(incomingUrl);
|
||||
let proxyUrl = j.getUrlForProxy(incomingUrl, 'POST');
|
||||
proxyUrl.should.equal(createTestURL('barbaz', 'element'));
|
||||
});
|
||||
it(`should proxy session commands when '/session' is in the url`, function () {
|
||||
let incomingUrl =
|
||||
'/session/82a9b7da-faaf-4a1d-8ef3-5e4fb5812200/cookie/session-something-or-other';
|
||||
let j = createJWProxy({sessionId: 'barbaz'});
|
||||
let proxyUrl = j.getUrlForProxy(incomingUrl);
|
||||
let proxyUrl = j.getUrlForProxy(incomingUrl, 'POST');
|
||||
proxyUrl.should.equal(createTestURL('barbaz', 'cookie/session-something-or-other'));
|
||||
});
|
||||
it(`should proxy session commands when '/session' is in the url and not base on the original url`, function () {
|
||||
let incomingUrl =
|
||||
'/session/82a9b7da-faaf-4a1d-8ef3-5e4fb5812200/cookie/session-something-or-other';
|
||||
let j = createJWProxy({sessionId: 'barbaz'});
|
||||
let proxyUrl = j.getUrlForProxy(incomingUrl);
|
||||
let proxyUrl = j.getUrlForProxy(incomingUrl, 'POST');
|
||||
proxyUrl.should.equal(createTestURL('barbaz', 'cookie/session-something-or-other'));
|
||||
});
|
||||
it('should error session commands without /session without session id', function () {
|
||||
let incomingUrl = '/element';
|
||||
let j = createJWProxy();
|
||||
(() => {
|
||||
j.getUrlForProxy(incomingUrl);
|
||||
}).should.throw('session id');
|
||||
j.getUrlForProxy(incomingUrl, 'POST');
|
||||
}).should.throw('not set');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -41,7 +41,7 @@ describe('Protocol', function () {
|
||||
}
|
||||
let hash = shasum.digest('hex').substring(0, 8);
|
||||
// Modify the hash whenever the protocol has intentionally been modified.
|
||||
hash.should.equal('6e93ad90');
|
||||
hash.should.equal('00fcb083');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -3,6 +3,30 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [2.3.3](https://github.com/appium/appium/compare/@appium/base-plugin@2.3.2...@appium/base-plugin@2.3.3) (2025-02-20)
|
||||
|
||||
**Note:** Version bump only for package @appium/base-plugin
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.3.2](https://github.com/appium/appium/compare/@appium/base-plugin@2.3.1...@appium/base-plugin@2.3.2) (2025-02-20)
|
||||
|
||||
**Note:** Version bump only for package @appium/base-plugin
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.3.1](https://github.com/appium/appium/compare/@appium/base-plugin@2.3.0...@appium/base-plugin@2.3.1) (2025-02-19)
|
||||
|
||||
**Note:** Version bump only for package @appium/base-plugin
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.3.0](https://github.com/appium/appium/compare/@appium/base-plugin@2.2.52...@appium/base-plugin@2.3.0) (2025-01-08)
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@appium/base-plugin",
|
||||
"version": "2.3.0",
|
||||
"version": "2.3.3",
|
||||
"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.15.0",
|
||||
"@appium/support": "^6.0.3"
|
||||
"@appium/base-driver": "^9.16.2",
|
||||
"@appium/support": "^6.0.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.17.0 || ^16.13.0 || >=18.0.0",
|
||||
|
||||
@@ -3,6 +3,38 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.0.32](https://github.com/appium/appium/compare/@appium/docutils@1.0.31...@appium/docutils@1.0.32) (2025-02-20)
|
||||
|
||||
**Note:** Version bump only for package @appium/docutils
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.0.31](https://github.com/appium/appium/compare/@appium/docutils@1.0.30...@appium/docutils@1.0.31) (2025-02-20)
|
||||
|
||||
**Note:** Version bump only for package @appium/docutils
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.0.30](https://github.com/appium/appium/compare/@appium/docutils@1.0.29...@appium/docutils@1.0.30) (2025-02-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **docutils:** update dependency consola to v3.4.0 ([#20908](https://github.com/appium/appium/issues/20908)) ([8fb0da3](https://github.com/appium/appium/commit/8fb0da34bb58c44c30ac8900a62510f1fcb663bc))
|
||||
* **docutils:** update dependency typescript to v5.7.3 ([#20893](https://github.com/appium/appium/issues/20893)) ([6706a00](https://github.com/appium/appium/commit/6706a0037eda5f7eb9eb2a4b0fb49888618c12c2))
|
||||
* **support:** update dependency semver to v7.7.0 ([#20948](https://github.com/appium/appium/issues/20948)) ([81ebd75](https://github.com/appium/appium/commit/81ebd758cb8a87d388124036e5158207041a6b2f))
|
||||
* **support:** update dependency semver to v7.7.1 ([#20961](https://github.com/appium/appium/issues/20961)) ([4c08944](https://github.com/appium/appium/commit/4c089440e655fbe3c8da919f8f625b7d7179f00a))
|
||||
* **types:** update dependency type-fest to v4.32.0 ([#20900](https://github.com/appium/appium/issues/20900)) ([fbc8530](https://github.com/appium/appium/commit/fbc85308a5398e7c9966792da713e60e47ed7f00))
|
||||
* **types:** update dependency type-fest to v4.33.0 ([#20923](https://github.com/appium/appium/issues/20923)) ([2409d32](https://github.com/appium/appium/commit/2409d3223a77aa7e84e0cb05a70be3bfa0c69157))
|
||||
* **types:** update dependency type-fest to v4.34.1 ([#20971](https://github.com/appium/appium/issues/20971)) ([0a7490e](https://github.com/appium/appium/commit/0a7490ed53ccfa9243df779e74bafadfc8415c87))
|
||||
* **types:** update dependency type-fest to v4.35.0 ([#20999](https://github.com/appium/appium/issues/20999)) ([3dc7336](https://github.com/appium/appium/commit/3dc7336b5fce10b9c1b095cd7a8a1841dbc3de12))
|
||||
|
||||
|
||||
|
||||
## [1.0.29](https://github.com/appium/appium/compare/@appium/docutils@1.0.28...@appium/docutils@1.0.29) (2025-01-08)
|
||||
|
||||
**Note:** Version bump only for package @appium/docutils
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@appium/docutils",
|
||||
"version": "1.0.29",
|
||||
"version": "1.0.32",
|
||||
"description": "Documentation generation utilities for Appium and related projects",
|
||||
"keywords": [
|
||||
"automation",
|
||||
@@ -48,23 +48,22 @@
|
||||
"start": "node ./build/lib/cli/index.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@appium/support": "^6.0.3",
|
||||
"@appium/tsconfig": "^0.3.3",
|
||||
"@appium/support": "^6.0.6",
|
||||
"@appium/tsconfig": "^0.3.4",
|
||||
"@sliphua/lilconfig-ts-loader": "3.2.2",
|
||||
"@types/which": "3.0.4",
|
||||
"chalk": "4.1.2",
|
||||
"consola": "3.3.3",
|
||||
"consola": "3.4.0",
|
||||
"diff": "7.0.0",
|
||||
"json5": "2.2.3",
|
||||
"lilconfig": "3.1.3",
|
||||
"lodash": "4.17.21",
|
||||
"pkg-dir": "5.0.0",
|
||||
"read-pkg": "5.2.0",
|
||||
"semver": "7.6.3",
|
||||
"semver": "7.7.1",
|
||||
"source-map-support": "0.5.21",
|
||||
"teen_process": "2.2.3",
|
||||
"type-fest": "4.31.0",
|
||||
"typescript": "5.7.2",
|
||||
"type-fest": "4.35.0",
|
||||
"typescript": "5.7.3",
|
||||
"yaml": "2.7.0",
|
||||
"yargs": "17.7.2",
|
||||
"yargs-parser": "21.1.1"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
mkdocs==1.6.1
|
||||
mkdocs-git-revision-date-localized-plugin==1.3.0
|
||||
mkdocs-material==9.5.49
|
||||
mkdocs-material==9.6.5
|
||||
mkdocs-redirects==1.2.2
|
||||
mike==2.1.3
|
||||
|
||||
@@ -3,6 +3,28 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [0.7.6](https://github.com/appium/appium/compare/@appium/driver-test-support@0.7.5...@appium/driver-test-support@0.7.6) (2025-02-20)
|
||||
|
||||
**Note:** Version bump only for package @appium/driver-test-support
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.7.5](https://github.com/appium/appium/compare/@appium/driver-test-support@0.7.4...@appium/driver-test-support@0.7.5) (2025-02-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **driver-test-support:** update definitelytyped ([#20942](https://github.com/appium/appium/issues/20942)) ([f7c63ee](https://github.com/appium/appium/commit/f7c63ee5d87be21eba577a4cfd0b0a08a050afeb))
|
||||
* **driver-test-support:** update dependency chai to v5.2.0 ([#20998](https://github.com/appium/appium/issues/20998)) ([ca456e5](https://github.com/appium/appium/commit/ca456e52365a9e621b4473263cc78bb2a3a855e2))
|
||||
* **types:** update dependency type-fest to v4.32.0 ([#20900](https://github.com/appium/appium/issues/20900)) ([fbc8530](https://github.com/appium/appium/commit/fbc85308a5398e7c9966792da713e60e47ed7f00))
|
||||
* **types:** update dependency type-fest to v4.33.0 ([#20923](https://github.com/appium/appium/issues/20923)) ([2409d32](https://github.com/appium/appium/commit/2409d3223a77aa7e84e0cb05a70be3bfa0c69157))
|
||||
* **types:** update dependency type-fest to v4.34.1 ([#20971](https://github.com/appium/appium/issues/20971)) ([0a7490e](https://github.com/appium/appium/commit/0a7490ed53ccfa9243df779e74bafadfc8415c87))
|
||||
* **types:** update dependency type-fest to v4.35.0 ([#20999](https://github.com/appium/appium/issues/20999)) ([3dc7336](https://github.com/appium/appium/commit/3dc7336b5fce10b9c1b095cd7a8a1841dbc3de12))
|
||||
|
||||
|
||||
|
||||
## [0.7.4](https://github.com/appium/appium/compare/@appium/driver-test-support@0.7.3...@appium/driver-test-support@0.7.4) (2025-01-08)
|
||||
|
||||
**Note:** Version bump only for package @appium/driver-test-support
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@appium/driver-test-support",
|
||||
"version": "0.7.4",
|
||||
"version": "0.7.6",
|
||||
"description": "Test utilities for Appium drivers",
|
||||
"keywords": [
|
||||
"automation",
|
||||
@@ -41,17 +41,16 @@
|
||||
},
|
||||
"types": "./build/lib/index.d.ts",
|
||||
"dependencies": {
|
||||
"@appium/types": "^0.24.0",
|
||||
"@types/lodash": "4.17.14",
|
||||
"@appium/types": "^0.25.1",
|
||||
"axios": "1.7.9",
|
||||
"bluebird": "3.7.2",
|
||||
"chai": "5.1.2",
|
||||
"chai": "5.2.0",
|
||||
"chai-as-promised": "8.0.1",
|
||||
"get-port": "5.1.1",
|
||||
"lodash": "4.17.21",
|
||||
"sinon": "19.0.2",
|
||||
"source-map-support": "0.5.21",
|
||||
"type-fest": "4.31.0"
|
||||
"type-fest": "4.35.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"appium": "^2.0.0-beta.43",
|
||||
|
||||
@@ -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.
|
||||
|
||||
## [1.0.3](https://github.com/appium/appium/compare/@appium/eslint-config-appium-ts@1.0.2...@appium/eslint-config-appium-ts@1.0.3) (2025-02-19)
|
||||
|
||||
**Note:** Version bump only for package @appium/eslint-config-appium-ts
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.0.2](https://github.com/appium/appium/compare/@appium/eslint-config-appium-ts@1.0.1...@appium/eslint-config-appium-ts@1.0.2) (2025-01-08)
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@appium/eslint-config-appium-ts",
|
||||
"type": "module",
|
||||
"version": "1.0.2",
|
||||
"version": "1.0.3",
|
||||
"description": "Shared ESLint config for Appium projects (TypeScript version)",
|
||||
"keywords": [
|
||||
"eslint",
|
||||
@@ -32,7 +32,7 @@
|
||||
"@eslint/compat": "^1.1.0",
|
||||
"@eslint/js": "^9.0.0",
|
||||
"eslint": "^9.0.0",
|
||||
"eslint-config-prettier": "^9.0.0",
|
||||
"eslint-config-prettier": "^9.0.0 || ^10.0.0",
|
||||
"eslint-import-resolver-typescript": "^3.5.0",
|
||||
"eslint-plugin-import": "^2.30.0",
|
||||
"eslint-plugin-mocha": "^10.4.0",
|
||||
|
||||
@@ -3,6 +3,24 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [4.0.2](https://github.com/appium/appium/compare/@appium/execute-driver-plugin@4.0.1...@appium/execute-driver-plugin@4.0.2) (2025-02-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **execute-driver-plugin:** update dependency webdriverio to v9.5.3 ([#20895](https://github.com/appium/appium/issues/20895)) ([8741474](https://github.com/appium/appium/commit/874147449a26bbec03b08e7e95fb9e4c6b5881af))
|
||||
* **execute-driver-plugin:** update dependency webdriverio to v9.5.4 ([#20898](https://github.com/appium/appium/issues/20898)) ([7f91d5c](https://github.com/appium/appium/commit/7f91d5cba850100124088d774ba8704e3765863f))
|
||||
* **execute-driver-plugin:** update dependency webdriverio to v9.5.7 ([#20903](https://github.com/appium/appium/issues/20903)) ([c60fdc8](https://github.com/appium/appium/commit/c60fdc8d847b4aeed2d8b8307180194ee32e5ca9))
|
||||
* **execute-driver-plugin:** update dependency webdriverio to v9.7.0 ([#20930](https://github.com/appium/appium/issues/20930)) ([5780982](https://github.com/appium/appium/commit/57809826a410b71dca155f59e081f77dcaf671ed))
|
||||
* **execute-driver-plugin:** update dependency webdriverio to v9.7.1 ([#20934](https://github.com/appium/appium/issues/20934)) ([50fa5d6](https://github.com/appium/appium/commit/50fa5d670bd967200a78e5b4b8a54d5c9b85901b))
|
||||
* **execute-driver-plugin:** update dependency webdriverio to v9.7.2 ([#20944](https://github.com/appium/appium/issues/20944)) ([f5e0819](https://github.com/appium/appium/commit/f5e0819f505b55421b7d260f69a41ae980390a91))
|
||||
* **execute-driver-plugin:** update dependency webdriverio to v9.8.0 ([#20964](https://github.com/appium/appium/issues/20964)) ([ce32226](https://github.com/appium/appium/commit/ce32226cb57fb3f07bf4862dbbfcba227ff1230c))
|
||||
* **execute-driver-plugin:** update dependency webdriverio to v9.9.0 ([#20994](https://github.com/appium/appium/issues/20994)) ([b3c877f](https://github.com/appium/appium/commit/b3c877f66ddf3ccf7a25ca1f7a3952c660bd7dda))
|
||||
* **execute-driver-plugin:** update dependency webdriverio to v9.9.1 ([#20996](https://github.com/appium/appium/issues/20996)) ([c6a080f](https://github.com/appium/appium/commit/c6a080f7ade03a49a274ce6fbc84629406fdc2a7))
|
||||
* **execute-driver-plugin:** update dependency webdriverio to v9.9.3 ([#21005](https://github.com/appium/appium/issues/21005)) ([686a630](https://github.com/appium/appium/commit/686a630fbdf525db16b42fdfc1bbdaf6426b41d0))
|
||||
|
||||
|
||||
|
||||
## [4.0.1](https://github.com/appium/appium/compare/@appium/execute-driver-plugin@4.0.0...@appium/execute-driver-plugin@4.0.1) (2025-01-06)
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@appium/execute-driver-plugin",
|
||||
"version": "4.0.1",
|
||||
"version": "4.0.2",
|
||||
"description": "Plugin for batching and executing driver commands with Appiums",
|
||||
"keywords": [
|
||||
"automation",
|
||||
@@ -38,11 +38,10 @@
|
||||
"test:unit": "mocha \"./test/unit/**/*.spec.js\""
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/bluebird": "3.5.42",
|
||||
"bluebird": "3.7.2",
|
||||
"lodash": "4.17.21",
|
||||
"source-map-support": "0.5.21",
|
||||
"webdriverio": "9.5.1"
|
||||
"webdriverio": "9.9.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"appium": "^2.0.0-beta.35"
|
||||
|
||||
@@ -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.
|
||||
|
||||
## [5.7.1](https://github.com/appium/appium/compare/@appium/fake-driver@5.7.0...@appium/fake-driver@5.7.1) (2025-02-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **universal-xml-plugin:** update dependency @xmldom/xmldom to v0.9.7 ([#20918](https://github.com/appium/appium/issues/20918)) ([7e1f1e5](https://github.com/appium/appium/commit/7e1f1e5aad19d709a48cd4d06196fa4db792400f))
|
||||
|
||||
|
||||
|
||||
## [5.7.0](https://github.com/appium/appium/compare/@appium/fake-driver@5.6.0...@appium/fake-driver@5.7.0) (2025-01-08)
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@appium/fake-driver",
|
||||
"version": "5.7.0",
|
||||
"version": "5.7.1",
|
||||
"description": "Mock driver used internally by Appium for testing. Ignore",
|
||||
"keywords": [
|
||||
"automation",
|
||||
@@ -42,8 +42,7 @@
|
||||
"test:unit": "mocha --timeout 10s --slow 5s \"./test/unit/**/*.spec.js\""
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/bluebird": "3.5.42",
|
||||
"@xmldom/xmldom": "0.9.6",
|
||||
"@xmldom/xmldom": "0.9.7",
|
||||
"asyncbox": "3.0.0",
|
||||
"bluebird": "3.7.2",
|
||||
"lodash": "4.17.21",
|
||||
|
||||
@@ -3,6 +3,30 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [3.2.3](https://github.com/appium/appium/compare/@appium/fake-plugin@3.2.2...@appium/fake-plugin@3.2.3) (2025-02-20)
|
||||
|
||||
**Note:** Version bump only for package @appium/fake-plugin
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.2.2](https://github.com/appium/appium/compare/@appium/fake-plugin@3.2.1...@appium/fake-plugin@3.2.2) (2025-02-20)
|
||||
|
||||
**Note:** Version bump only for package @appium/fake-plugin
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.2.1](https://github.com/appium/appium/compare/@appium/fake-plugin@3.2.0...@appium/fake-plugin@3.2.1) (2025-02-19)
|
||||
|
||||
**Note:** Version bump only for package @appium/fake-plugin
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.2.0](https://github.com/appium/appium/compare/@appium/fake-plugin@3.1.56...@appium/fake-plugin@3.2.0) (2025-01-08)
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@appium/fake-plugin",
|
||||
"version": "3.2.0",
|
||||
"version": "3.2.3",
|
||||
"description": "A fake Appium 2.0 plugin",
|
||||
"keywords": [
|
||||
"automation",
|
||||
@@ -39,9 +39,8 @@
|
||||
"test:unit": "mocha \"./test/unit/**/*.spec.js\""
|
||||
},
|
||||
"dependencies": {
|
||||
"@appium/base-plugin": "^2.3.0",
|
||||
"@appium/support": "^6.0.3",
|
||||
"@types/bluebird": "3.5.42",
|
||||
"@appium/base-plugin": "^2.3.3",
|
||||
"@appium/support": "^6.0.6",
|
||||
"bluebird": "3.7.2",
|
||||
"lodash": "4.17.21",
|
||||
"source-map-support": "0.5.21"
|
||||
|
||||
@@ -3,6 +3,30 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [3.0.30](https://github.com/appium/appium/compare/@appium/images-plugin@3.0.29...@appium/images-plugin@3.0.30) (2025-02-20)
|
||||
|
||||
**Note:** Version bump only for package @appium/images-plugin
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.0.29](https://github.com/appium/appium/compare/@appium/images-plugin@3.0.28...@appium/images-plugin@3.0.29) (2025-02-20)
|
||||
|
||||
**Note:** Version bump only for package @appium/images-plugin
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.0.28](https://github.com/appium/appium/compare/@appium/images-plugin@3.0.27...@appium/images-plugin@3.0.28) (2025-02-19)
|
||||
|
||||
**Note:** Version bump only for package @appium/images-plugin
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.0.27](https://github.com/appium/appium/compare/@appium/images-plugin@3.0.26...@appium/images-plugin@3.0.27) (2025-01-08)
|
||||
|
||||
**Note:** Version bump only for package @appium/images-plugin
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@appium/images-plugin",
|
||||
"version": "3.0.27",
|
||||
"version": "3.0.30",
|
||||
"description": "Plugin for working with images and image elements in Appium",
|
||||
"keywords": [
|
||||
"automation",
|
||||
@@ -39,8 +39,8 @@
|
||||
"test:unit": "mocha \"./test/unit/**/*.spec.js\""
|
||||
},
|
||||
"dependencies": {
|
||||
"@appium/opencv": "^3.0.7",
|
||||
"@appium/support": "^6.0.3",
|
||||
"@appium/opencv": "^3.0.8",
|
||||
"@appium/support": "^6.0.6",
|
||||
"lodash": "4.17.21",
|
||||
"lru-cache": "10.4.3",
|
||||
"sharp": "0.33.5",
|
||||
|
||||
@@ -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.8](https://github.com/appium/appium/compare/@appium/opencv@3.0.7...@appium/opencv@3.0.8) (2025-02-19)
|
||||
|
||||
**Note:** Version bump only for package @appium/opencv
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.0.7](https://github.com/appium/appium/compare/@appium/opencv@3.0.6...@appium/opencv@3.0.7) (2025-01-02)
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@appium/opencv",
|
||||
"version": "3.0.7",
|
||||
"version": "3.0.8",
|
||||
"description": "OpenCV-related helper library",
|
||||
"keywords": [
|
||||
"automation",
|
||||
@@ -41,7 +41,6 @@
|
||||
"test:unit": "mocha \"./test/unit/**/*.spec.js\""
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/bluebird": "3.5.42",
|
||||
"bluebird": "3.7.2",
|
||||
"lodash": "4.17.21",
|
||||
"opencv-bindings": "4.5.5",
|
||||
|
||||
@@ -3,6 +3,22 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [0.3.51](https://github.com/appium/appium/compare/@appium/plugin-test-support@0.3.50...@appium/plugin-test-support@0.3.51) (2025-02-20)
|
||||
|
||||
**Note:** Version bump only for package @appium/plugin-test-support
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.3.50](https://github.com/appium/appium/compare/@appium/plugin-test-support@0.3.49...@appium/plugin-test-support@0.3.50) (2025-02-19)
|
||||
|
||||
**Note:** Version bump only for package @appium/plugin-test-support
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.3.49](https://github.com/appium/appium/compare/@appium/plugin-test-support@0.3.48...@appium/plugin-test-support@0.3.49) (2025-01-08)
|
||||
|
||||
**Note:** Version bump only for package @appium/plugin-test-support
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@appium/plugin-test-support",
|
||||
"version": "0.3.49",
|
||||
"version": "0.3.51",
|
||||
"description": "Testing utilities for Appium plugins",
|
||||
"keywords": [
|
||||
"automation",
|
||||
@@ -41,7 +41,7 @@
|
||||
"test:unit": "exit 0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@appium/types": "^0.24.0",
|
||||
"@appium/types": "^0.25.1",
|
||||
"get-port": "5.1.1",
|
||||
"log-symbols": "4.1.0",
|
||||
"source-map-support": "0.5.21",
|
||||
|
||||
@@ -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.
|
||||
|
||||
## [0.8.1](https://github.com/appium/appium/compare/@appium/schema@0.8.0...@appium/schema@0.8.1) (2025-02-20)
|
||||
|
||||
**Note:** Version bump only for package @appium/schema
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.8.0](https://github.com/appium/appium/compare/@appium/schema@0.7.1...@appium/schema@0.8.0) (2025-02-19)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **appium:** Add a command line parameter to configure HTTP server request timeout ([#21003](https://github.com/appium/appium/issues/21003)) ([eb1b156](https://github.com/appium/appium/commit/eb1b156146bc338da9c6ded5a2c5beab22ac0ed8))
|
||||
|
||||
|
||||
|
||||
## [0.7.1](https://github.com/appium/appium/compare/@appium/schema@0.7.0...@appium/schema@0.7.1) (2025-01-02)
|
||||
|
||||
|
||||
|
||||
@@ -117,11 +117,23 @@ export const AppiumConfigJsonSchema = /** @type {const} */ ({
|
||||
appiumCliAliases: ['ka'],
|
||||
default: 600,
|
||||
description:
|
||||
'Number of seconds the Appium server should apply as both the keep-alive timeout and the connection timeout for all requests. A value of 0 disables the timeout.',
|
||||
'Number of seconds the Appium server should apply as both the keep-alive timeout and the connection timeout ' +
|
||||
'for all requests. A value of 0 disables the timeout.',
|
||||
minimum: 0,
|
||||
title: 'keep-alive-timeout config',
|
||||
type: 'integer',
|
||||
},
|
||||
'request-timeout': {
|
||||
default: 3600,
|
||||
description:
|
||||
'Number of seconds the Appium server should apply for receiving the entire HTTP request from the client. ' +
|
||||
'A value of 0 disables the timeout. Set it to a non-zero value to protect against ' +
|
||||
'potential Denial-of-Service attacks in case the server is deployed without a reverse proxy in front. ' +
|
||||
'HTTP requests that are running longer than allowed by this timeout would be rejected with the status code 408.',
|
||||
minimum: 0,
|
||||
title: 'request-timeout config',
|
||||
type: 'integer',
|
||||
},
|
||||
'local-timezone': {
|
||||
default: false,
|
||||
description: 'Use local timezone for timestamps',
|
||||
|
||||
@@ -120,6 +120,13 @@
|
||||
"title": "keep-alive-timeout config",
|
||||
"type": "integer"
|
||||
},
|
||||
"request-timeout": {
|
||||
"default": 3600,
|
||||
"description": "Number of seconds the Appium server should apply for receiving the entire HTTP request from the client. A value of 0 disables the timeout. Set it to a non-zero value to protect against potential Denial-of-Service attacks in case the server is deployed without a reverse proxy in front. HTTP requests that are running longer than allowed by this timeout would be rejected with the status code 408.",
|
||||
"minimum": 0,
|
||||
"title": "request-timeout config",
|
||||
"type": "integer"
|
||||
},
|
||||
"local-timezone": {
|
||||
"default": false,
|
||||
"description": "Use local timezone for timestamps",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@appium/schema",
|
||||
"version": "0.7.1",
|
||||
"version": "0.8.1",
|
||||
"description": "Appium Configuration Schema",
|
||||
"keywords": [
|
||||
"automation",
|
||||
@@ -19,7 +19,7 @@
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/appium/appium.git",
|
||||
"directory": "schema"
|
||||
"directory": "packages/schema"
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"author": "https://github.com/appium",
|
||||
@@ -38,7 +38,6 @@
|
||||
"test": "exit 0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/json-schema": "7.0.15",
|
||||
"json-schema": "0.4.0",
|
||||
"source-map-support": "0.5.21"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,39 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [6.0.6](https://github.com/appium/appium/compare/@appium/support@6.0.5...@appium/support@6.0.6) (2025-02-20)
|
||||
|
||||
**Note:** Version bump only for package @appium/support
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [6.0.5](https://github.com/appium/appium/compare/@appium/support@6.0.4...@appium/support@6.0.5) (2025-02-20)
|
||||
|
||||
**Note:** Version bump only for package @appium/support
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [6.0.4](https://github.com/appium/appium/compare/@appium/support@6.0.3...@appium/support@6.0.4) (2025-02-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Avoid "Unhandled rejection" error on mjpeg connection failure ([#20909](https://github.com/appium/appium/issues/20909)) ([3c58054](https://github.com/appium/appium/commit/3c58054a476fbb92927ffa54732d1995e1b956c3))
|
||||
* **support:** update dependency form-data to v4.0.2 ([#20997](https://github.com/appium/appium/issues/20997)) ([224f524](https://github.com/appium/appium/commit/224f5247b0cd4b7dcc392567b90d53b9b57a8b88))
|
||||
* **support:** update dependency semver to v7.7.0 ([#20948](https://github.com/appium/appium/issues/20948)) ([81ebd75](https://github.com/appium/appium/commit/81ebd758cb8a87d388124036e5158207041a6b2f))
|
||||
* **support:** update dependency semver to v7.7.1 ([#20961](https://github.com/appium/appium/issues/20961)) ([4c08944](https://github.com/appium/appium/commit/4c089440e655fbe3c8da919f8f625b7d7179f00a))
|
||||
* **support:** update dependency uuid to v11.0.5 ([#20896](https://github.com/appium/appium/issues/20896)) ([561aff6](https://github.com/appium/appium/commit/561aff641a4f62c82865ea036b6e1ca1bb89fb44))
|
||||
* **types:** update dependency type-fest to v4.32.0 ([#20900](https://github.com/appium/appium/issues/20900)) ([fbc8530](https://github.com/appium/appium/commit/fbc85308a5398e7c9966792da713e60e47ed7f00))
|
||||
* **types:** update dependency type-fest to v4.33.0 ([#20923](https://github.com/appium/appium/issues/20923)) ([2409d32](https://github.com/appium/appium/commit/2409d3223a77aa7e84e0cb05a70be3bfa0c69157))
|
||||
* **types:** update dependency type-fest to v4.34.1 ([#20971](https://github.com/appium/appium/issues/20971)) ([0a7490e](https://github.com/appium/appium/commit/0a7490ed53ccfa9243df779e74bafadfc8415c87))
|
||||
* **types:** update dependency type-fest to v4.35.0 ([#20999](https://github.com/appium/appium/issues/20999)) ([3dc7336](https://github.com/appium/appium/commit/3dc7336b5fce10b9c1b095cd7a8a1841dbc3de12))
|
||||
|
||||
|
||||
|
||||
## [6.0.3](https://github.com/appium/appium/compare/@appium/support@6.0.2...@appium/support@6.0.3) (2025-01-08)
|
||||
|
||||
**Note:** Version bump only for package @appium/support
|
||||
|
||||
@@ -116,22 +116,23 @@ class MJpegStream extends Writable {
|
||||
await initMJpegConsumer();
|
||||
|
||||
this.consumer = new MJpegConsumer();
|
||||
|
||||
// use the deferred pattern so we can wait for the start of the stream
|
||||
// based on what comes in from an external pipe
|
||||
const startPromise = new B((res, rej) => {
|
||||
this.registerStartSuccess = res;
|
||||
this.registerStartFailure = rej;
|
||||
})
|
||||
// start a timeout so that if the server does not return data, we don't
|
||||
// block forever.
|
||||
.timeout(
|
||||
serverTimeout,
|
||||
`Waited ${serverTimeout}ms but the MJPEG server never sent any images`
|
||||
);
|
||||
|
||||
const url = this.url;
|
||||
const onErr = (err) => {
|
||||
try {
|
||||
this.responseStream = (
|
||||
await axios({
|
||||
url,
|
||||
responseType: 'stream',
|
||||
timeout: serverTimeout,
|
||||
})
|
||||
).data;
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
`Cannot connect to the MJPEG stream at ${url}. ` +
|
||||
`Original error: ${_.has(e, 'response') ? JSON.stringify(e.response) : /** @type {Error} */ (e).message}`
|
||||
);
|
||||
}
|
||||
|
||||
const onErr = (/** @type {Error} */ err) => {
|
||||
// Make sure we don't get an outdated screenshot if there was an error
|
||||
this.lastChunk = null;
|
||||
|
||||
@@ -145,18 +146,18 @@ class MJpegStream extends Writable {
|
||||
log.debug(`The connection to MJPEG server at ${url} has been closed`);
|
||||
this.lastChunk = null;
|
||||
};
|
||||
|
||||
try {
|
||||
this.responseStream = (
|
||||
await axios({
|
||||
url,
|
||||
responseType: 'stream',
|
||||
timeout: serverTimeout,
|
||||
})
|
||||
).data;
|
||||
} catch (e) {
|
||||
return onErr(e);
|
||||
}
|
||||
// use the deferred pattern so we can wait for the start of the stream
|
||||
// based on what comes in from an external pipe
|
||||
const startPromise = new B((res, rej) => {
|
||||
this.registerStartSuccess = res;
|
||||
this.registerStartFailure = rej;
|
||||
})
|
||||
// start a timeout so that if the server does not return data, we don't
|
||||
// block forever.
|
||||
.timeout(
|
||||
serverTimeout,
|
||||
`Waited ${serverTimeout}ms but the MJPEG server never sent any images`
|
||||
);
|
||||
|
||||
this.responseStream
|
||||
.once('close', onClose)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@appium/support",
|
||||
"version": "6.0.3",
|
||||
"version": "6.0.6",
|
||||
"description": "Support libs used across appium packages",
|
||||
"keywords": [
|
||||
"automation",
|
||||
@@ -42,31 +42,16 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@appium/logger": "^1.6.1",
|
||||
"@appium/tsconfig": "^0.3.3",
|
||||
"@appium/types": "^0.24.0",
|
||||
"@appium/tsconfig": "^0.3.4",
|
||||
"@appium/types": "^0.25.1",
|
||||
"@colors/colors": "1.6.0",
|
||||
"@types/archiver": "6.0.3",
|
||||
"@types/base64-stream": "1.0.5",
|
||||
"@types/find-root": "1.1.4",
|
||||
"@types/jsftp": "2.1.5",
|
||||
"@types/klaw": "3.0.6",
|
||||
"@types/lockfile": "1.0.4",
|
||||
"@types/mv": "2.1.4",
|
||||
"@types/ncp": "2.0.8",
|
||||
"@types/pluralize": "0.0.33",
|
||||
"@types/semver": "7.5.8",
|
||||
"@types/shell-quote": "1.7.5",
|
||||
"@types/supports-color": "8.1.3",
|
||||
"@types/teen_process": "2.0.4",
|
||||
"@types/uuid": "10.0.0",
|
||||
"@types/which": "3.0.4",
|
||||
"archiver": "7.0.1",
|
||||
"axios": "1.7.9",
|
||||
"base64-stream": "1.0.0",
|
||||
"bluebird": "3.7.2",
|
||||
"bplist-creator": "0.1.1",
|
||||
"bplist-parser": "0.3.2",
|
||||
"form-data": "4.0.1",
|
||||
"form-data": "4.0.2",
|
||||
"get-stream": "6.0.1",
|
||||
"glob": "10.4.5",
|
||||
"jsftp": "2.1.3",
|
||||
@@ -77,20 +62,19 @@
|
||||
"moment": "2.30.1",
|
||||
"mv": "2.1.1",
|
||||
"ncp": "2.0.0",
|
||||
"opencv-bindings": "4.5.5",
|
||||
"pkg-dir": "5.0.0",
|
||||
"plist": "3.1.0",
|
||||
"pluralize": "8.0.0",
|
||||
"read-pkg": "5.2.0",
|
||||
"resolve-from": "5.0.0",
|
||||
"sanitize-filename": "1.6.3",
|
||||
"semver": "7.6.3",
|
||||
"semver": "7.7.1",
|
||||
"shell-quote": "1.8.2",
|
||||
"source-map-support": "0.5.21",
|
||||
"supports-color": "8.1.1",
|
||||
"teen_process": "2.2.3",
|
||||
"type-fest": "4.31.0",
|
||||
"uuid": "11.0.4",
|
||||
"type-fest": "4.35.0",
|
||||
"uuid": "11.1.0",
|
||||
"which": "4.0.0",
|
||||
"yauzl": "3.2.0"
|
||||
},
|
||||
|
||||
@@ -103,8 +103,8 @@ describe('MJpeg Stream (e2e)', function () {
|
||||
stream.updateCount.should.eql(0);
|
||||
});
|
||||
|
||||
it('should error out if the server does not send any images before a timeout', async function () {
|
||||
stream = new MJpegStream(serverUrl, _.noop);
|
||||
await stream.start(0).should.eventually.be.rejectedWith(/never sent/);
|
||||
it('should error out if the server cannot be connected', async function () {
|
||||
stream = new MJpegStream('http://localhost', _.noop);
|
||||
await stream.start().should.eventually.be.rejected;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,6 +3,30 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [3.1.6](https://github.com/appium/appium/compare/@appium/test-support@3.1.5...@appium/test-support@3.1.6) (2025-02-20)
|
||||
|
||||
**Note:** Version bump only for package @appium/test-support
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.1.5](https://github.com/appium/appium/compare/@appium/test-support@3.1.4...@appium/test-support@3.1.5) (2025-02-20)
|
||||
|
||||
**Note:** Version bump only for package @appium/test-support
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.1.4](https://github.com/appium/appium/compare/@appium/test-support@3.1.3...@appium/test-support@3.1.4) (2025-02-19)
|
||||
|
||||
**Note:** Version bump only for package @appium/test-support
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.1.3](https://github.com/appium/appium/compare/@appium/test-support@3.1.2...@appium/test-support@3.1.3) (2025-01-08)
|
||||
|
||||
**Note:** Version bump only for package @appium/test-support
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@appium/test-support",
|
||||
"version": "3.1.3",
|
||||
"version": "3.1.6",
|
||||
"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": "^6.0.3",
|
||||
"@appium/support": "^6.0.6",
|
||||
"@colors/colors": "1.6.0",
|
||||
"bluebird": "3.7.2",
|
||||
"lodash": "4.17.21",
|
||||
|
||||
@@ -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.4](https://github.com/appium/appium/compare/@appium/tsconfig@0.3.3...@appium/tsconfig@0.3.4) (2025-02-20)
|
||||
|
||||
**Note:** Version bump only for package @appium/tsconfig
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.3.3](https://github.com/appium/appium/compare/@appium/tsconfig@0.3.2...@appium/tsconfig@0.3.3) (2024-04-08)
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@appium/tsconfig",
|
||||
"version": "0.3.3",
|
||||
"version": "0.3.4",
|
||||
"description": "Shared TypeScript Config for Appium",
|
||||
"main": "tsconfig.json",
|
||||
"keywords": [
|
||||
@@ -21,6 +21,11 @@
|
||||
"bugs": {
|
||||
"url": "https://github.com/appium/appium/issues"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/appium/appium.git",
|
||||
"directory": "packages/tsconfig"
|
||||
},
|
||||
"homepage": "https://appium.io",
|
||||
"engines": {
|
||||
"node": "^14.17.0 || ^16.13.0 || >=18.0.0",
|
||||
|
||||
@@ -3,6 +3,36 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [0.25.1](https://github.com/appium/appium/compare/@appium/types@0.25.0...@appium/types@0.25.1) (2025-02-20)
|
||||
|
||||
**Note:** Version bump only for package @appium/types
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.25.0](https://github.com/appium/appium/compare/@appium/types@0.24.0...@appium/types@0.25.0) (2025-02-19)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Add /appium/extensions API to list available extensions ([#20931](https://github.com/appium/appium/issues/20931)) ([a6b6077](https://github.com/appium/appium/commit/a6b6077ecd0749598f52d9f29b3220f47d7ad636))
|
||||
* Add BiDi commands to the listCommands API output ([#20925](https://github.com/appium/appium/issues/20925)) ([2635dcb](https://github.com/appium/appium/commit/2635dcb457be2dc02dfbee5ad4c6ab132f5af8de))
|
||||
* **appium:** Add a command line parameter to configure HTTP server request timeout ([#21003](https://github.com/appium/appium/issues/21003)) ([eb1b156](https://github.com/appium/appium/commit/eb1b156146bc338da9c6ded5a2c5beab22ac0ed8))
|
||||
* **base-driver:** Add an API to list commands ([#20914](https://github.com/appium/appium/issues/20914)) ([059f1cb](https://github.com/appium/appium/commit/059f1cb698ccdbc58494af9303c5bf264a1893d9))
|
||||
* **types:** Add an optional `info` field to base method type definitions ([#20917](https://github.com/appium/appium/issues/20917)) ([fe4ffc1](https://github.com/appium/appium/commit/fe4ffc159a87cb7566775203efe4659d1508b5b0))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **deps:** update definitelytyped ([#20928](https://github.com/appium/appium/issues/20928)) ([1f65cf6](https://github.com/appium/appium/commit/1f65cf6555a18d3ba02b4506942b4217839618ff))
|
||||
* **types:** update dependency type-fest to v4.32.0 ([#20900](https://github.com/appium/appium/issues/20900)) ([fbc8530](https://github.com/appium/appium/commit/fbc85308a5398e7c9966792da713e60e47ed7f00))
|
||||
* **types:** update dependency type-fest to v4.33.0 ([#20923](https://github.com/appium/appium/issues/20923)) ([2409d32](https://github.com/appium/appium/commit/2409d3223a77aa7e84e0cb05a70be3bfa0c69157))
|
||||
* **types:** update dependency type-fest to v4.34.1 ([#20971](https://github.com/appium/appium/issues/20971)) ([0a7490e](https://github.com/appium/appium/commit/0a7490ed53ccfa9243df779e74bafadfc8415c87))
|
||||
* **types:** update dependency type-fest to v4.35.0 ([#20999](https://github.com/appium/appium/issues/20999)) ([3dc7336](https://github.com/appium/appium/commit/3dc7336b5fce10b9c1b095cd7a8a1841dbc3de12))
|
||||
|
||||
|
||||
|
||||
## [0.24.0](https://github.com/appium/appium/compare/@appium/types@0.23.0...@appium/types@0.24.0) (2025-01-08)
|
||||
|
||||
|
||||
|
||||
@@ -41,6 +41,10 @@ export type DenyInsecureConfig = string[];
|
||||
* Number of seconds the Appium server should apply as both the keep-alive timeout and the connection timeout for all requests. A value of 0 disables the timeout.
|
||||
*/
|
||||
export type KeepAliveTimeoutConfig = number;
|
||||
/**
|
||||
* Number of seconds the Appium server should apply for receiving the entire HTTP request from the client. A value of 0 disables the timeout. Set it to a non-zero value to protect against potential Denial-of-Service attacks in case the server is deployed without a reverse proxy in front. HTTP requests that are running longer than allowed by this timeout would be rejected with the status code 408.
|
||||
*/
|
||||
export type RequestTimeoutConfig = number;
|
||||
/**
|
||||
* Use local timezone for timestamps
|
||||
*/
|
||||
@@ -193,6 +197,7 @@ export interface ServerConfig {
|
||||
"deny-insecure"?: DenyInsecureConfig;
|
||||
driver?: DriverConfig;
|
||||
"keep-alive-timeout"?: KeepAliveTimeoutConfig;
|
||||
"request-timeout"?: RequestTimeoutConfig;
|
||||
"local-timezone"?: LocalTimezoneConfig;
|
||||
log?: LogConfig;
|
||||
"log-filters"?: LogFiltersConfig;
|
||||
|
||||
@@ -59,6 +59,14 @@ export interface BaseMethodDef {
|
||||
* Specifies shape of payload
|
||||
*/
|
||||
readonly payloadParams?: PayloadParams;
|
||||
/**
|
||||
* If true, this `Method` will be removed and should not be used by clients
|
||||
*/
|
||||
readonly deprecated?: boolean;
|
||||
/**
|
||||
* Any additional info string or comments to this command.
|
||||
*/
|
||||
readonly info?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,11 +78,6 @@ export interface DriverMethodDef<T extends Driver, D extends boolean = boolean>
|
||||
* Name of the command.
|
||||
*/
|
||||
readonly command?: D extends true ? string : keyof ConditionalPick<Required<T>, DriverCommand>;
|
||||
|
||||
/**
|
||||
* If this is `true`, we do not validate `command`, because it may not exist in `ExternalDriver`.
|
||||
*/
|
||||
readonly deprecated?: D;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -113,6 +116,16 @@ export interface BaseExecuteMethodDef {
|
||||
required?: ReadonlyArray<string>;
|
||||
optional?: ReadonlyArray<string>;
|
||||
};
|
||||
|
||||
/**
|
||||
* If this is `true`, then the method is marked for deprecation.
|
||||
*/
|
||||
readonly deprecated?: boolean;
|
||||
|
||||
/**
|
||||
* Any additional info string or comments to this execute method.
|
||||
*/
|
||||
readonly info?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -177,3 +190,133 @@ export interface ErrorBiDiCommandResponse extends GenericBiDiCommandResponse {
|
||||
message: string;
|
||||
stacktrace?: string;
|
||||
}
|
||||
|
||||
export interface RestCommandItemParam {
|
||||
/**
|
||||
* Command paremeter name
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* True if the paramter is required for the given command
|
||||
*/
|
||||
required: boolean;
|
||||
}
|
||||
|
||||
export interface RestCommandItem {
|
||||
/**
|
||||
* Command name
|
||||
*/
|
||||
command?: string;
|
||||
/**
|
||||
* Whether the command is marked for deprecation
|
||||
*/
|
||||
deprecated?: boolean;
|
||||
/**
|
||||
* Optinal infostring about the command's purpose or a comment
|
||||
*/
|
||||
info?: string;
|
||||
/**
|
||||
* List of command parameters
|
||||
*/
|
||||
params?: RestCommandItemParam[];
|
||||
}
|
||||
|
||||
export interface RestMethodsToCommandsMap {
|
||||
/**
|
||||
* Method name to command info mapping
|
||||
*/
|
||||
[method: string]: RestCommandItem;
|
||||
}
|
||||
|
||||
export interface RestCommandsMap {
|
||||
/**
|
||||
* Command paths to methods map in the base driver
|
||||
*/
|
||||
base: Record<string, RestMethodsToCommandsMap>;
|
||||
/**
|
||||
* Command paths to methods map in the session-specific driver
|
||||
*/
|
||||
driver: Record<string, RestMethodsToCommandsMap>;
|
||||
/**
|
||||
* Plugin name to command paths to methods map
|
||||
*/
|
||||
plugins?: Record<string, Record<string, RestMethodsToCommandsMap>>;
|
||||
}
|
||||
|
||||
export interface BiDiCommandItemParam {
|
||||
/**
|
||||
* Command paremeter name
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* True if the paramter is required for the given command
|
||||
*/
|
||||
required: boolean;
|
||||
}
|
||||
|
||||
export interface BiDiCommandItem {
|
||||
/**
|
||||
* Command name
|
||||
*/
|
||||
command?: string;
|
||||
/**
|
||||
* Whether the command is marked for deprecation
|
||||
*/
|
||||
deprecated?: boolean;
|
||||
/**
|
||||
* Optinal infostring about the command's purpose or a comment
|
||||
*/
|
||||
info?: string;
|
||||
/**
|
||||
* List of command parameters
|
||||
*/
|
||||
params?: BiDiCommandItemParam[];
|
||||
}
|
||||
|
||||
export interface BiDiCommandNamesToInfosMap {
|
||||
[name: string]: BiDiCommandItem;
|
||||
}
|
||||
|
||||
export interface BiDiCommandsMap {
|
||||
/**
|
||||
* Domains to BiDi commands mapping in the base driver
|
||||
*/
|
||||
base: Record<string, BiDiCommandNamesToInfosMap>;
|
||||
/**
|
||||
* Domains to BiDi commands mapping in the session-specific driver
|
||||
*/
|
||||
driver: Record<string, BiDiCommandNamesToInfosMap>;
|
||||
/**
|
||||
* Plugin name to domains to BiDi commands mapping
|
||||
*/
|
||||
plugins?: Record<string, Record<string, BiDiCommandNamesToInfosMap>>;
|
||||
}
|
||||
|
||||
export interface ListCommandsResponse {
|
||||
/**
|
||||
* REST APIs mapping
|
||||
*/
|
||||
rest?: RestCommandsMap;
|
||||
/**
|
||||
* BiDi APIs mapping
|
||||
*/
|
||||
bidi?: BiDiCommandsMap;
|
||||
}
|
||||
|
||||
export interface RestExtensionsMap {
|
||||
/**
|
||||
* Driver execute methods mapping
|
||||
*/
|
||||
driver: RestMethodsToCommandsMap;
|
||||
/**
|
||||
* Plugins execute methods mapping
|
||||
*/
|
||||
plugins?: Record<string, RestMethodsToCommandsMap>;
|
||||
}
|
||||
|
||||
export interface ListExtensionsResponse {
|
||||
/**
|
||||
* Rest extensions mapping
|
||||
*/
|
||||
rest?: RestExtensionsMap;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,12 @@ import type {EventEmitter} from 'node:events';
|
||||
import type {Merge} from 'type-fest';
|
||||
import type {ActionSequence} from './action';
|
||||
import type {Capabilities, DriverCaps, W3CCapabilities, W3CDriverCaps} from './capabilities';
|
||||
import type {BidiModuleMap, BiDiResultData, ExecuteMethodMap, MethodMap} from './command';
|
||||
import type {
|
||||
BidiModuleMap,
|
||||
BiDiResultData,
|
||||
ExecuteMethodMap,
|
||||
MethodMap,
|
||||
} from './command';
|
||||
import type {ServerArgs} from './config';
|
||||
import type {HTTPHeaders, HTTPMethod} from './http';
|
||||
import type {AppiumLogger} from './logger';
|
||||
@@ -17,6 +22,7 @@ export interface IDeviceSettings<T extends StringRecord> {
|
||||
update(newSettings: T): Promise<void>;
|
||||
getSettings(): T;
|
||||
}
|
||||
|
||||
export interface ITimeoutCommands {
|
||||
/**
|
||||
* Set the various timeouts associated with a session
|
||||
@@ -197,6 +203,19 @@ export type SingularSessionData<
|
||||
error?: string;
|
||||
} & T;
|
||||
|
||||
/**
|
||||
* Data returned by {@linkcode ISessionCommands.getAppiumSessionCapabilities}.
|
||||
*
|
||||
* @typeParam C - The driver's constraints
|
||||
* @typeParam T - Any extra data the driver stuffs in here
|
||||
*/
|
||||
export type SessionCapabilities<
|
||||
C extends Constraints = Constraints,
|
||||
T extends StringRecord = StringRecord,
|
||||
> = {
|
||||
capabilities: DriverCaps<C>;
|
||||
} & T;
|
||||
|
||||
export interface IFindCommands {
|
||||
/**
|
||||
* Find a UI element given a locator strategy and a selector, erroring if it can't be found
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@appium/types",
|
||||
"version": "0.24.0",
|
||||
"version": "0.25.1",
|
||||
"description": "Various type declarations used across Appium",
|
||||
"keywords": [
|
||||
"automation",
|
||||
@@ -39,11 +39,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@appium/logger": "^1.6.1",
|
||||
"@appium/schema": "^0.7.1",
|
||||
"@appium/tsconfig": "^0.3.3",
|
||||
"@types/express": "5.0.0",
|
||||
"@types/ws": "8.5.13",
|
||||
"type-fest": "4.31.0"
|
||||
"@appium/schema": "^0.8.1",
|
||||
"@appium/tsconfig": "^0.3.4",
|
||||
"type-fest": "4.35.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.17.0 || ^16.13.0 || >=18.0.0",
|
||||
|
||||
@@ -3,6 +3,33 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.0.30](https://github.com/appium/appium/compare/@appium/universal-xml-plugin@1.0.29...@appium/universal-xml-plugin@1.0.30) (2025-02-20)
|
||||
|
||||
**Note:** Version bump only for package @appium/universal-xml-plugin
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.0.29](https://github.com/appium/appium/compare/@appium/universal-xml-plugin@1.0.28...@appium/universal-xml-plugin@1.0.29) (2025-02-20)
|
||||
|
||||
**Note:** Version bump only for package @appium/universal-xml-plugin
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.0.28](https://github.com/appium/appium-plugins/compare/@appium/universal-xml-plugin@1.0.27...@appium/universal-xml-plugin@1.0.28) (2025-02-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **universal-xml-plugin:** update dependency @xmldom/xmldom to v0.9.7 ([#20918](https://github.com/appium/appium-plugins/issues/20918)) ([7e1f1e5](https://github.com/appium/appium-plugins/commit/7e1f1e5aad19d709a48cd4d06196fa4db792400f))
|
||||
* **universal-xml-plugin:** update dependency fast-xml-parser to v4.5.2 ([#21002](https://github.com/appium/appium-plugins/issues/21002)) ([c7900f4](https://github.com/appium/appium-plugins/commit/c7900f4a164aea9b3e654119522925973fcb727e))
|
||||
* **universal-xml-plugin:** update dependency fast-xml-parser to v5 ([#21006](https://github.com/appium/appium-plugins/issues/21006)) ([c8ac9c4](https://github.com/appium/appium-plugins/commit/c8ac9c40bb09d0ecc3990c9948a4683c2d413ebd))
|
||||
|
||||
|
||||
|
||||
## [1.0.27](https://github.com/appium/appium-plugins/compare/@appium/universal-xml-plugin@1.0.26...@appium/universal-xml-plugin@1.0.27) (2025-01-02)
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const {main, UniversalXMLPlugin} = require('./build/lib');
|
||||
|
||||
if (require.main === module) {
|
||||
main();
|
||||
(async () => await main())();
|
||||
}
|
||||
|
||||
module.exports = {UniversalXMLPlugin};
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
import {UniversalXMLPlugin} from './plugin';
|
||||
import {transformSourceXml} from './source';
|
||||
import fs from 'fs';
|
||||
import fs from 'node:fs/promises';
|
||||
export default UniversalXMLPlugin;
|
||||
export {UniversalXMLPlugin};
|
||||
|
||||
export function main() {
|
||||
export async function main() {
|
||||
const [, , xmlDataPath, platform, optsJson] = process.argv;
|
||||
const xmlData = fs.readFileSync(xmlDataPath, 'utf8');
|
||||
let opts = {};
|
||||
if (optsJson) {
|
||||
opts = JSON.parse(optsJson);
|
||||
}
|
||||
const {xml, unknowns} = transformSourceXml(xmlData, platform, opts);
|
||||
const xmlData = await fs.readFile(xmlDataPath, 'utf8');
|
||||
const opts = optsJson ? JSON.parse(optsJson) : {};
|
||||
const {xml, unknowns} = await transformSourceXml(xmlData, platform, opts);
|
||||
console.log(xml); // eslint-disable-line no-console
|
||||
if (unknowns.nodes.length || unknowns.attrs.length) {
|
||||
console.error(unknowns); // eslint-disable-line no-console
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
|
||||
|
||||
import {BasePlugin} from 'appium/plugin';
|
||||
import {errors} from 'appium/driver';
|
||||
import {transformSourceXml} from './source';
|
||||
@@ -14,7 +12,7 @@ export default class UniversalXMLPlugin extends BasePlugin {
|
||||
if (platformName.toLowerCase() === 'android') {
|
||||
metadata.appPackage = driver.opts.appPackage;
|
||||
}
|
||||
const {xml, unknowns} = transformSourceXml(source, platformName.toLowerCase(), {
|
||||
const {xml, unknowns} = await transformSourceXml(source, platformName.toLowerCase(), {
|
||||
metadata,
|
||||
addIndexPath,
|
||||
});
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import _ from 'lodash';
|
||||
import {XMLParser, XMLBuilder} from 'fast-xml-parser';
|
||||
import NODE_MAP from './node-map';
|
||||
import {ATTR_MAP, REMOVE_ATTRS} from './attr-map';
|
||||
import TRANSFORMS from './transformers';
|
||||
@@ -8,21 +7,6 @@ export const ATTR_PREFIX = '@_';
|
||||
export const IDX_PATH_PREFIX = `${ATTR_PREFIX}indexPath`;
|
||||
export const IDX_PREFIX = `${ATTR_PREFIX}index`;
|
||||
|
||||
const PARSE_OPTS = {
|
||||
ignoreAttributes: false,
|
||||
ignoreDeclaration: true,
|
||||
attributeNamePrefix: ATTR_PREFIX,
|
||||
isArray: (name, jPath, isLeafNode, isAttribute) => !isAttribute,
|
||||
};
|
||||
|
||||
const GEN_OPTS = {
|
||||
ignoreAttributes: false,
|
||||
attributeNamePrefix: ATTR_PREFIX,
|
||||
allowBooleanAttributes: true,
|
||||
suppressBooleanAttributes: false,
|
||||
format: true,
|
||||
};
|
||||
|
||||
const isAttr = (/** @type {string} */ k) => k.startsWith(ATTR_PREFIX);
|
||||
const isNode = (/** @type {string} */ k) => !isAttr(k);
|
||||
|
||||
@@ -31,19 +15,19 @@ const isNode = (/** @type {string} */ k) => !isAttr(k);
|
||||
* @param {string} xmlStr
|
||||
* @param {string} platform
|
||||
* @param {{metadata?: Object, addIndexPath?: boolean}} opts
|
||||
* @returns {{xml: string, unknowns: NodesAndAttributes}}
|
||||
* @returns {Promise<{xml: string, unknowns: NodesAndAttributes}>}
|
||||
*/
|
||||
export function transformSourceXml(xmlStr, platform, {metadata = {}, addIndexPath = false} = {}) {
|
||||
export async function transformSourceXml(xmlStr, platform, {metadata = {}, addIndexPath = false} = {}) {
|
||||
// first thing we want to do is modify the ios source root node, because it doesn't include the
|
||||
// necessary index attribute, so we add it if it's not there
|
||||
xmlStr = xmlStr.replace('<AppiumAUT>', '<AppiumAUT index="0">');
|
||||
const xmlObj = new XMLParser(PARSE_OPTS).parse(xmlStr);
|
||||
const xmlObj = (await singletonXmlParser()).parse(xmlStr);
|
||||
const unknowns = transformNode(xmlObj, platform, {
|
||||
metadata,
|
||||
addIndexPath,
|
||||
parentPath: '',
|
||||
});
|
||||
let transformedXml = new XMLBuilder(GEN_OPTS).build(xmlObj).trim();
|
||||
let transformedXml = (await singletonXmlBuilder()).build(xmlObj).trim();
|
||||
transformedXml = `<?xml version="1.0" encoding="UTF-8"?>\n${transformedXml}`;
|
||||
return {xml: transformedXml, unknowns};
|
||||
}
|
||||
@@ -217,6 +201,26 @@ export function transformAttrs(nodeObj, attrs, platform) {
|
||||
return unknownAttrs;
|
||||
}
|
||||
|
||||
const singletonXmlBuilder = _.memoize(async function makeXmlBuilder() {
|
||||
const { XMLBuilder } = await import('fast-xml-parser');
|
||||
return new XMLBuilder({
|
||||
ignoreAttributes: false,
|
||||
attributeNamePrefix: ATTR_PREFIX,
|
||||
suppressBooleanAttributes: false,
|
||||
format: true,
|
||||
});
|
||||
});
|
||||
|
||||
const singletonXmlParser = _.memoize(async function makeXmlParser() {
|
||||
const { XMLParser } = await import('fast-xml-parser');
|
||||
return new XMLParser({
|
||||
ignoreAttributes: false,
|
||||
ignoreDeclaration: true,
|
||||
attributeNamePrefix: ATTR_PREFIX,
|
||||
isArray: (name, jPath, isLeafNode, isAttribute) => !isAttribute,
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* @typedef {{nodes: string[], attrs: string[]}} NodesAndAttributes
|
||||
*/
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@appium/universal-xml-plugin",
|
||||
"version": "1.0.27",
|
||||
"version": "1.0.30",
|
||||
"description": "Appium plugin for making XML source and XPath queries the same across iOS and Android",
|
||||
"keywords": [
|
||||
"automation",
|
||||
@@ -18,7 +18,8 @@
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/appium/appium-plugins.git"
|
||||
"url": "https://github.com/appium/appium.git",
|
||||
"directory": "packages/universal-xml-plugin"
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"author": "https://github.com/appium",
|
||||
@@ -36,9 +37,8 @@
|
||||
"test:unit": "mocha \"./test/unit/**/*.spec.js\""
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/xmldom": "0.1.34",
|
||||
"@xmldom/xmldom": "0.9.6",
|
||||
"fast-xml-parser": "4.5.1",
|
||||
"@xmldom/xmldom": "0.9.7",
|
||||
"fast-xml-parser": "5.0.6",
|
||||
"lodash": "4.17.21",
|
||||
"source-map-support": "0.5.21",
|
||||
"xpath": "0.0.34"
|
||||
|
||||
@@ -14,29 +14,29 @@ describe('source functions', function () {
|
||||
});
|
||||
|
||||
describe('transformSourceXml', function () {
|
||||
it('should transform an xml doc based on platform', function () {
|
||||
it('should transform an xml doc based on platform', async function () {
|
||||
const {
|
||||
xml,
|
||||
unknowns: {nodes, attrs},
|
||||
} = transformSourceXml(XML_IOS, 'ios');
|
||||
} = await transformSourceXml(XML_IOS, 'ios');
|
||||
xml.should.eql(XML_IOS_TRANSFORMED);
|
||||
nodes.should.eql([]);
|
||||
attrs.should.eql([]);
|
||||
});
|
||||
it('should transform an xml doc and include index path', function () {
|
||||
it('should transform an xml doc and include index path', async function () {
|
||||
const {
|
||||
xml,
|
||||
unknowns: {nodes, attrs},
|
||||
} = transformSourceXml(XML_IOS, 'ios', {addIndexPath: true});
|
||||
} = await transformSourceXml(XML_IOS, 'ios', {addIndexPath: true});
|
||||
xml.should.eql(XML_IOS_TRANSFORMED_INDEX_PATH);
|
||||
nodes.should.eql([]);
|
||||
attrs.should.eql([]);
|
||||
});
|
||||
it('should transform an xml doc and return any unknown nodes or attrs', function () {
|
||||
it('should transform an xml doc and return any unknown nodes or attrs', async function () {
|
||||
const {
|
||||
xml,
|
||||
unknowns: {nodes, attrs},
|
||||
} = transformSourceXml(XML_IOS_EDGE, 'ios');
|
||||
} = await transformSourceXml(XML_IOS_EDGE, 'ios');
|
||||
xml.should.eql(XML_IOS_EDGE_TRANSFORMED);
|
||||
nodes.should.eql(['SomeRandoElement']);
|
||||
attrs.should.eql(['oddAttribute']);
|
||||
|
||||
@@ -16,18 +16,18 @@ describe('xpath functions', function () {
|
||||
});
|
||||
});
|
||||
describe('transformQuery', function () {
|
||||
it('should transform a query into a single new query', function () {
|
||||
const {xml} = transformSourceXml(XML_IOS, 'ios', {addIndexPath: true});
|
||||
it('should transform a query into a single new query', async function () {
|
||||
const {xml} = await transformSourceXml(XML_IOS, 'ios', {addIndexPath: true});
|
||||
transformQuery('//TextInput', xml, false).should.eql(
|
||||
'/*[1]/*[1]/*[1]/*[1]/*[2]/*[1]/*[1]/*[1]/*[1]/*[1]/*[1]/*[2]/*[1]/*[1]/*[1]'
|
||||
);
|
||||
});
|
||||
it('should transform a query into a multiple new queries if asked', function () {
|
||||
const {xml} = transformSourceXml(XML_IOS, 'ios', {addIndexPath: true});
|
||||
it('should transform a query into a multiple new queries if asked', async function () {
|
||||
const {xml} = await transformSourceXml(XML_IOS, 'ios', {addIndexPath: true});
|
||||
transformQuery('//Window', xml, true).split('|').should.have.length(2);
|
||||
});
|
||||
it('should return null for queries that dont find anything', function () {
|
||||
const {xml} = transformSourceXml(XML_IOS, 'ios', {addIndexPath: true});
|
||||
it('should return null for queries that dont find anything', async function () {
|
||||
const {xml} = await transformSourceXml(XML_IOS, 'ios', {addIndexPath: true});
|
||||
should.not.exist(transformQuery('//blah', xml, false));
|
||||
});
|
||||
});
|
||||
|
||||
+1
-1
@@ -42,7 +42,7 @@ Appium extension authors--or anyone else--may use this config as well.
|
||||
- `:enableVulnerabilityAlerts` - For "security" purposes
|
||||
- `:rebaseStalePrs` - Renovate will automatically rebase its PRs
|
||||
- `:semanticCommits` - Renovate will use semantic commit messages
|
||||
- `:semanticPrefixChore` - Renovate's PRs have the `chore` prefix in its semantic commit message
|
||||
- `:semanticCommitTypeAll(chore)` - Renovate's PRs have the `chore` prefix in its semantic commit message
|
||||
|
||||
### Custom Rules
|
||||
|
||||
|
||||
@@ -10,11 +10,12 @@
|
||||
":enableVulnerabilityAlerts",
|
||||
":rebaseStalePrs",
|
||||
":semanticCommits",
|
||||
":semanticPrefixChore"
|
||||
":semanticCommitTypeAll(chore)"
|
||||
],
|
||||
"packageRules": [
|
||||
{
|
||||
"matchPackageNames": [
|
||||
"@types/supports-color",
|
||||
"@types/wrap-ansi",
|
||||
"chalk",
|
||||
"conf",
|
||||
|
||||
Reference in New Issue
Block a user