Compare commits

...
Author SHA1 Message Date
Jonathan Lipps e473b05130 chore: publish
- appium@2.16.2
 - @appium/base-driver@9.16.2
 - @appium/base-plugin@2.3.3
 - @appium/docutils@1.0.32
 - @appium/fake-plugin@3.2.3
 - @appium/images-plugin@3.0.30
 - @appium/support@6.0.6
 - @appium/test-support@3.1.6
 - @appium/universal-xml-plugin@1.0.30
2025-02-20 15:56:38 -08:00
Mykola Mokhnach dd64a48fca fix(base-driver): Restore the legacy proxy url behaviour (#21021) 2025-02-20 15:24:46 -08:00
renovate[bot] 4473ef4da8 chore(support): update dependency uuid to v11.1.0 (#21012)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-20 23:09:25 +01:00
renovate[bot] f9f88e66b6 chore(universal-xml-plugin): update dependency fast-xml-parser to v5 (#21013)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-20 23:09:09 +01:00
Jonathan Lipps 715fd03c45 chore: publish
- appium@2.16.1
 - @appium/base-driver@9.16.1
 - @appium/base-plugin@2.3.2
 - @appium/docutils@1.0.31
 - @appium/driver-test-support@0.7.6
 - @appium/fake-plugin@3.2.2
 - @appium/images-plugin@3.0.29
 - @appium/plugin-test-support@0.3.51
 - @appium/schema@0.8.1
 - @appium/support@6.0.5
 - @appium/test-support@3.1.5
 - @appium/tsconfig@0.3.4
 - @appium/types@0.25.1
 - @appium/universal-xml-plugin@1.0.29
2025-02-20 11:04:05 -08:00
Mykola Mokhnach 8a664a4eef fix(base-driver): Optimize the logic of getUrlForProxy (#21018) 2025-02-20 19:59:15 +01:00
renovate[bot] dc24864cbb chore(deps): update dependency eslint-import-resolver-typescript to v3.8.3 (#21016)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-20 12:59:27 +00:00
Edgars Eglītis c102d8fd8e fix(renovate): use chore prefix for all PRs (#21014) 2025-02-20 12:59:44 +02:00
renovate[bot] 703085bc54 chore(docutils): update dependency mkdocs-material to v9.6.5 (#21009)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-20 09:08:09 +00:00
Edgars Eglītis 47497bc8da chore(schema,tsconfig,universal-xml-plugin): fix repository field in package.json (#21011) 2025-02-20 10:04:01 +02:00
Jonathan Lipps ea00e3aa18 chore: publish
- appium@2.16.0
 - @appium/base-driver@9.16.0
 - @appium/base-plugin@2.3.1
 - @appium/docutils@1.0.30
 - @appium/driver-test-support@0.7.5
 - @appium/eslint-config-appium-ts@1.0.3
 - @appium/execute-driver-plugin@4.0.2
 - @appium/fake-driver@5.7.1
 - @appium/fake-plugin@3.2.1
 - @appium/images-plugin@3.0.28
 - @appium/opencv@3.0.8
 - @appium/plugin-test-support@0.3.50
 - @appium/schema@0.8.0
 - @appium/support@6.0.4
 - @appium/test-support@3.1.4
 - @appium/types@0.25.0
 - @appium/universal-xml-plugin@1.0.28
2025-02-19 12:15:46 -08:00
Jonathan Lipps f8ddac629d chore: remove sauce labs as project sponsor (#21008) 2025-02-19 11:58:09 -08:00
Mykola Mokhnach eb1b156146 feat(appium): Add a command line parameter to configure HTTP server request timeout (#21003) 2025-02-19 20:45:20 +01:00
renovate[bot] c8ac9c40bb fix(universal-xml-plugin): update dependency fast-xml-parser to v5 (#21006) 2025-02-19 20:44:18 +01:00
Kim Arnett c4337fc3e7 docs(appium): remove axe DevTools for Mobile drivers from Appium other drivers list (#21007) 2025-02-19 17:59:32 +01:00
renovate[bot] 686a630fbd fix(execute-driver-plugin): update dependency webdriverio to v9.9.3 (#21005)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-19 16:09:58 +00:00
renovate[bot] 5a227d23d2 chore(deps): update dependency eslint-import-resolver-typescript to v3.8.2 (#21004)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-19 12:15:51 +00:00
renovate[bot] c7900f4a16 fix(universal-xml-plugin): update dependency fast-xml-parser to v4.5.2 (#21002)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-18 16:41:40 +00:00
renovate[bot] 312a1c53f3 chore(deps): update eslint-related packages (#21001)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-18 10:27:11 +00:00
renovate[bot] ca456e5236 fix(driver-test-support): update dependency chai to v5.2.0 (#20998)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-17 09:50:41 +01:00
renovate[bot] 3dc7336b5f fix(types): update dependency type-fest to v4.35.0 (#20999)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-17 08:21:26 +01:00
renovate[bot] 224f5247b0 fix(support): update dependency form-data to v4.0.2 (#20997)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-15 13:55:17 +00:00
renovate[bot] c6a080f7ad fix(execute-driver-plugin): update dependency webdriverio to v9.9.1 (#20996)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-15 10:11:14 +00:00
renovate[bot] b3c877f66d fix(execute-driver-plugin): update dependency webdriverio to v9.9.0 (#20994)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-14 19:22:49 +00:00
Kazuaki Matsuo 419661013b chore(appium): returns this.caps for /session/:sessionId/appium/capabilities (#20979)
* return the same value of getSession

* chore: update docs and implementation

* remove event stuff

* add type in return

* add AppiumSessionCapabilities type

* update the syntax

* rename

* add test in e2e

* remove unnecessary type

* use fake driver name
2025-02-14 08:08:42 -08:00
renovate[bot] 9b08c209f9 chore(deps): update dependency eslint-import-resolver-typescript to v3.8.0 (#20995)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-14 15:27:43 +00:00
renovate[bot] ab07a0bc90 chore(deps): update dependency prettier to v3.5.1 (#20993)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-14 10:38:46 +00:00
renovate[bot] 91d9a3ef5d chore(deps): update dependency @types/node to v22.13.4 (#20992)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-14 07:15:48 +00:00
renovate[bot] 4c9a42062c chore(docutils): update dependency mkdocs-material to v9.6.4 (#20990)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-13 20:08:50 +00:00
renovate[bot] 30be097e15 chore(deps): update dependency @types/node to v22.13.2 (#20989)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-13 14:46:02 +00:00
Kazuaki Matsuo 97ae82b2a2 docs(appium): use event endpoint to get event timing in zh resource (#20985) 2025-02-12 01:33:45 -08:00
Kazuaki Matsuo aea0ccfa00 docs(appium): use event endpoint to get event timing (#20984)
* docs(appium): use event endpoint to get event timing
2025-02-12 01:32:12 -08:00
Edgars Eglītis e934b61856 chore(renovate): add @types/supports-color to blocklist (#20983) 2025-02-12 10:36:17 +02:00
renovate[bot] 99e96502b9 chore(deps): update dependency eslint to v9.20.1 (#20982)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-12 07:32:54 +00:00
Kazuaki Matsuo 848f2b2396 chore(appium): handle getAppiumSessions as non session id commands (#20981) 2025-02-11 23:16:56 -08:00
renovate[bot] a1e3890681 chore(deps): update dependency typescript-eslint to v8.24.0 (#20978)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-11 09:36:32 +00:00
Kazuaki Matsuo 21e2aabc39 fix(appium): add await for async function (#20974) 2025-02-10 10:01:24 -08:00
renovate[bot] 0a7490ed53 fix(types): update dependency type-fest to v4.34.1 (#20971)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-10 05:19:14 +00:00
renovate[bot] 1c2c119461 chore(deps): update eslint-related packages to v9.20.0 (#20968)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-10 00:57:15 +00:00
renovate[bot] 49da3c6c35 chore(deps): update dependency prettier to v3.5.0 (#20970)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-09 22:22:04 +00:00
renovate[bot] 0b27739768 chore(docutils): update dependency mkdocs-material to v9.6.3 (#20967)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-08 13:59:58 +00:00
renovate[bot] ce32226cb5 fix(execute-driver-plugin): update dependency webdriverio to v9.8.0 (#20964)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-07 00:26:28 +00:00
Mykola Mokhnach f8b57999e5 feat(base-driver): Print the closest match if the given script did not match (#20956) 2025-02-06 08:56:13 +01:00
renovate[bot] 52c6c6933b chore(deps): update dependency typescript-eslint to v8.23.0 (#20962)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-04 17:52:25 +00:00
renovate[bot] 4c089440e6 fix(support): update dependency semver to v7.7.1 (#20961)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-04 15:05:21 +00:00
renovate[bot] 94f93a1421 chore(docutils): update dependency mkdocs-material to v9.6.2 (#20958)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-04 12:25:37 +00:00
renovate[bot] b3018fcb76 chore(deps): update dependency @types/node to v22.13.1 (#20957)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-04 08:58:19 +00:00
Mykola Mokhnach 3cfe3af1c8 fix(appium): Wait for upstream web socket to open before sending data to it (#20953) 2025-02-02 22:50:08 +01:00
Edgars Eglītis 70f154da52 chore: move or remove some main dependencies (#20952) 2025-02-02 11:57:13 +02:00
renovate[bot] 8a87698cc2 chore(deps): update definitelytyped (#20950)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-01 21:28:27 +00:00
Kazuaki Matsuo 7edd3bdc11 chore: use RegExp to check session id requirement (#20947)
* chore: use regext to check session id requirement

* update docs

* add test case

* use match of path-to-regexp

* use another name to avoid conflict

* accept appium prefix for non-session id
2025-02-01 01:03:15 -08:00
renovate[bot] 98da49ff58 chore(docutils): update dependency mkdocs-material to v9.6.1 (#20951)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-01 08:47:41 +00:00
renovate[bot] 81ebd758cb fix(support): update dependency semver to v7.7.0 (#20948)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-30 19:14:31 +00:00
renovate[bot] f5e0819f50 fix(execute-driver-plugin): update dependency webdriverio to v9.7.2 (#20944)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-29 13:59:11 +00:00
Kazuaki Matsuo eeb59cab07 feat: add /appium/sessions, /session/:sessionId/appium/capabilities and deprecated marks will be removed in the future (#20936)
* feat: add /appium/sessions, /session/:sessionId/appium/capabilities and deprecated marks will be removed in the future

* add a dymmy implementation

* move entire deprecated endpoints in bottom of each section

* define GET_APPIUM_SESSION_CAPABILITIES and add it to isAppiumDriverCommand

* adds _COMMAND

* remove unnecessary method definition

* move deprecated endpoints

* add temporary implementation
2025-01-28 10:14:51 -08:00
renovate[bot] 75a0df2975 chore(deps): update dependency typescript-eslint to v8.22.0 (#20943)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-28 17:11:41 +00:00
renovate[bot] f7c63ee5d8 fix(driver-test-support): update definitelytyped (#20942)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-28 14:43:46 +00:00
Mykola Mokhnach 841a835153 chore(base-driver): Cleanup unnecessary command definitions (#20939) 2025-01-27 11:05:07 +01:00
Mykola Mokhnach a6b6077ecd feat: Add /appium/extensions API to list available extensions (#20931) 2025-01-25 17:02:41 +01:00
renovate[bot] caff2c98a9 chore(deps): update eslint-related packages to v9.19.0 (#20935)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-25 11:13:42 +01:00
renovate[bot] 50fa5d670b fix(execute-driver-plugin): update dependency webdriverio to v9.7.1 (#20934)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-25 11:13:29 +01:00
renovate[bot] 57809826a4 fix(execute-driver-plugin): update dependency webdriverio to v9.7.0 (#20930)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-24 20:04:53 +01:00
Dalton Alexandre d5ab2c1f31 docs(appium): Add Unofficial SwiftAppium Client 2025-01-24 19:51:47 +01:00
Mykola Mokhnach 2635dcb457 feat: Add BiDi commands to the listCommands API output (#20925) 2025-01-24 19:48:54 +01:00
renovate[bot] e7541f88fb chore(deps): update dependency mocha to v11.1.0 (#20929)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-24 18:17:12 +00:00
renovate[bot] 1f65cf6555 fix(deps): update definitelytyped (#20928)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-24 12:33:21 +00:00
Mykola Mokhnach 059f1cb698 feat(base-driver): Add an API to list commands (#20914) 2025-01-23 19:07:16 +01:00
renovate[bot] 2409d3223a fix(types): update dependency type-fest to v4.33.0 (#20923)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-22 13:25:44 +00:00
renovate[bot] 7e1f1e5aad fix(universal-xml-plugin): update dependency @xmldom/xmldom to v0.9.7 (#20918)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-22 11:24:37 +02:00
renovate[bot] 4180270f0b chore(deps): update dependency typescript-eslint to v8.21.0 (#20919)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-21 13:50:10 +00:00
Mykola Mokhnach fe4ffc159a feat(types): Add an optional info field to base method type definitions (#20917) 2025-01-20 14:14:07 +01:00
renovate[bot] 4cda1e48ef chore(docutils): update dependency mkdocs-material to v9.5.50 (#20915)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-19 13:06:06 +00:00
renovate[bot] c5da98a5d9 chore(eslint-config-appium-ts): update dependency eslint-config-prettier to v10 (#20911)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-17 23:06:23 +01:00
Mykola Mokhnach 3c58054a47 fix: Avoid "Unhandled rejection" error on mjpeg connection failure (#20909) 2025-01-16 17:41:29 +01:00
renovate[bot] 2d5d4bec02 chore(deps): update dependency @types/node to v22.10.7 (#20910)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-16 12:42:33 +00:00
renovate[bot] 8fb0da34bb fix(docutils): update dependency consola to v3.4.0 (#20908)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-15 17:55:57 +00:00
renovate[bot] b686178f93 chore(deps): update dependency typescript-eslint to v8.20.0 (#20907)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-15 13:26:38 +00:00
renovate[bot] 803f69726b chore(deps): update dependency json-schema-to-typescript to v15.0.4 (#20905)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-14 20:56:49 +00:00
renovate[bot] 7764e71257 chore(deps): update dependency @types/node to v22.10.6 (#20904)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-14 14:24:42 +00:00
renovate[bot] c60fdc8d84 fix(execute-driver-plugin): update dependency webdriverio to v9.5.7 (#20903)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-13 14:10:37 +00:00
renovate[bot] fbc85308a5 fix(types): update dependency type-fest to v4.32.0 (#20900)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-11 22:43:42 +00:00
renovate[bot] 26eda4775e chore(deps): update dependency @types/diff to v7 (#20901)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-11 18:44:54 +02:00
renovate[bot] e96908fcbb chore(deps): update eslint-related packages to v9.18.0 (#20899)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-11 14:01:26 +00:00
renovate[bot] 7f91d5cba8 fix(execute-driver-plugin): update dependency webdriverio to v9.5.4 (#20898)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-11 09:55:34 +00:00
renovate[bot] 561aff641a fix(support): update dependency uuid to v11.0.5 (#20896)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-10 19:13:11 +00:00
renovate[bot] 874147449a fix(execute-driver-plugin): update dependency webdriverio to v9.5.3 (#20895)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-10 13:37:25 +00:00
renovate[bot] 6706a0037e fix(docutils): update dependency typescript to v5.7.3 (#20893)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-09 18:25:56 +00:00
renovate[bot] 6d62389d26 chore(deps): update dependency typescript-eslint to v8.19.1 (#20892)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-09 14:03:13 +00:00
84 changed files with 2886 additions and 1646 deletions
-5
View File
@@ -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/).
+1011 -894
View File
File diff suppressed because it is too large Load Diff
+37 -14
View File
@@ -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"
},
+51
View File
@@ -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)
-5
View File
@@ -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/).
+1
View File
@@ -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"
-5
View File
@@ -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>
-1
View File
@@ -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"
+30 -35
View File
@@ -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
+190
View File
@@ -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,
};
}
+4 -2
View File
@@ -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});
+9 -15
View File
@@ -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();
+1
View File
@@ -8,6 +8,7 @@ export default {
denyInsecure: [],
driversImportChunkSize: 3,
keepAliveTimeout: 600,
requestTimeout: 3600,
localTimezone: false,
logFormat: 'text',
loglevel: 'debug',
+41
View File
@@ -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();
+24 -6
View File
@@ -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]
*/
/**
+3
View File
@@ -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';
/**
+59 -56
View File
@@ -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,
};
+412 -367
View File
@@ -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'];
+5 -10
View File
@@ -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');
});
});
+24
View File
@@ -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)
+3 -3
View File
@@ -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",
+32
View File
@@ -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
+7 -8
View File
@@ -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 -1
View File
@@ -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
+22
View File
@@ -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
+4 -5
View File
@@ -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)
+2 -3
View File
@@ -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"
+9
View File
@@ -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)
+2 -3
View File
@@ -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",
+24
View File
@@ -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)
+3 -4
View File
@@ -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"
+24
View File
@@ -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
+3 -3
View File
@@ -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",
+8
View File
@@ -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 -2
View File
@@ -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",
+16
View File
@@ -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
+2 -2
View File
@@ -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",
+17
View File
@@ -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)
+13 -1
View File
@@ -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",
+2 -3
View File
@@ -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"
},
+33
View File
@@ -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
+28 -27
View File
@@ -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)
+7 -23
View File
@@ -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"
},
+3 -3
View File
@@ -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;
});
});
+24
View File
@@ -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
+2 -2
View File
@@ -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",
+8
View File
@@ -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)
+6 -1
View File
@@ -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",
+30
View File
@@ -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)
+5
View File
@@ -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;
+148 -5
View File
@@ -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;
}
+20 -1
View File
@@ -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
+4 -6
View File
@@ -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 -1
View File
@@ -1,7 +1,7 @@
const {main, UniversalXMLPlugin} = require('./build/lib');
if (require.main === module) {
main();
(async () => await main())();
}
module.exports = {UniversalXMLPlugin};
+5 -8
View File
@@ -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 -3
View File
@@ -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,
});
+24 -20
View File
@@ -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
*/
+5 -5
View File
@@ -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
View File
@@ -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
+2 -1
View File
@@ -10,11 +10,12 @@
":enableVulnerabilityAlerts",
":rebaseStalePrs",
":semanticCommits",
":semanticPrefixChore"
":semanticCommitTypeAll(chore)"
],
"packageRules": [
{
"matchPackageNames": [
"@types/supports-color",
"@types/wrap-ansi",
"chalk",
"conf",