Compare commits

...
Author SHA1 Message Date
Jonathan Lipps 42c0bd370e chore: publish
- appium@2.5.1
 - @appium/base-driver@9.5.2
 - @appium/base-plugin@2.2.28
 - @appium/doctor@2.0.33
 - @appium/docutils@1.0.4
 - @appium/execute-driver-plugin@3.0.25
 - @appium/fake-driver@5.5.1
 - @appium/fake-plugin@3.1.32
 - @appium/images-plugin@3.0.3
 - @appium/support@4.2.2
 - @appium/test-support@3.0.32
2024-02-13 11:26:55 -08:00
renovate[bot] 383fee4e11 chore(workflows): update egmacke/action-check-label action to v3 (#19780)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-13 14:49:10 +02:00
renovate[bot] 5951027ce7 chore(workflows): update fuxingloh/multi-labeler action to v4 (#19781)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-13 14:44:58 +02:00
Kazuaki Matsuo 889a533eef fix(fake-driver): fix dependencies (#19782) 2024-02-13 01:08:37 -08:00
renovate[bot] d83d436d01 fix(docutils): update dependency diff to v5.2.0 2024-02-13 08:36:59 +00:00
renovate[bot] bd8a92f588 fix(support): update dependency @types/semver to v7.5.7 2024-02-12 22:48:47 +00:00
renovate[bot] db5a6323e6 chore(workflows): update rinchsan/renovate-config-validator action to v0.2.0 (#19774)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-11 12:56:49 +02:00
renovate[bot] 2f56b05619 chore(docutils): update dependency mkdocs-material to v9.5.9 2024-02-11 08:58:57 +00:00
renovate[bot] 1e5201afad chore(deps): update dependency mocha to v10.3.0 2024-02-10 05:23:07 +00:00
renovate[bot] 9ef18310da chore(deps): update dependency @types/node to v20.11.17 2024-02-09 09:37:02 +00:00
renovate[bot] db0eec05c5 fix(execute-driver-plugin): update dependency webdriverio to v8.31.1 2024-02-09 04:17:22 +00:00
renovate[bot] fc9a93fee0 chore(docutils): update dependency mkdocs-material to v9.5.8 2024-02-08 05:27:36 +00:00
renovate[bot] 7b40a3225e fix(support): update dependency semver to v7.6.0 2024-02-06 21:41:41 +00:00
Jonathan Lipps 9cc598a92c chore: publish
- appium@2.5.0
 - @appium/base-driver@9.5.1
 - @appium/base-plugin@2.2.27
 - @appium/doctor@2.0.32
 - @appium/docutils@1.0.3
 - @appium/driver-test-support@0.4.21
 - @appium/execute-driver-plugin@3.0.24
 - @appium/fake-driver@5.5.0
 - @appium/fake-plugin@3.1.31
 - @appium/images-plugin@3.0.2
 - @appium/opencv@3.0.2
 - @appium/plugin-test-support@0.3.28
 - @appium/support@4.2.1
 - @appium/test-support@3.0.31
 - @appium/types@0.16.1
 - @appium/universal-xml-plugin@1.0.17
2024-02-06 12:21:16 -08:00
renovate[bot] 91320ddd02 fix(execute-driver-plugin): update dependency webdriverio to v8.30.0 2024-02-06 19:07:45 +00:00
renovate[bot] 23dc3d56ce chore(deps): update eslint-related packages to v6.21.0 2024-02-06 15:42:15 +00:00
renovate[bot] 35742f0962 chore(deps): update dependency lerna to v8.1.2 2024-02-06 12:56:31 +00:00
renovate[bot] 52a6f96e08 fix(execute-driver-plugin): update dependency webdriverio to v8.29.7 2024-02-05 07:41:24 +00:00
renovate[bot] d064dd1e8c chore(deps): update dependency prettier to v3.2.5 2024-02-05 05:42:36 +00:00
renovate[bot] 7bf4d694d4 chore(docutils): update dependency mkdocs-git-revision-date-localized-plugin to v1.2.4 2024-02-04 11:57:15 +00:00
renovate[bot] cdc7c09421 chore(docutils): update dependency mkdocs-material to v9.5.7 2024-02-03 10:53:51 +00:00
renovate[bot] 17c7a7cb29 chore(deps): update dependency @types/node to v20.11.16 2024-02-02 04:58:11 +00:00
renovate[bot] 3032b0009c chore(deps): update dependency npm-run-all2 to v6.1.2 2024-02-01 09:38:54 +00:00
renovate[bot] 6dacb5e70c fix(execute-driver-plugin): update dependency webdriverio to v8.29.3 2024-02-01 05:29:46 +00:00
renovate[bot] 5a8af1302f chore(deps): update dependency @types/node to v20.11.13 2024-01-31 09:24:52 +00:00
renovate[bot] 569db839f1 fix(universal-xml-plugin): update dependency fast-xml-parser to v4.3.4 2024-01-31 05:12:39 +00:00
renovate[bot] 32e9191431 chore(deps): update dependency npm-run-all2 to v6 (#19736)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-30 14:25:21 +02:00
renovate[bot] 21b808d94f chore(deps): update eslint-related packages to v6.20.0 2024-01-30 09:41:29 +00:00
renovate[bot] 83cc668596 chore(deps): update dependency npm-run-all2 to v5.0.2 (#19735)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-30 09:37:44 +02:00
renovate[bot] b92ab24a25 chore(deps): replace dependency npm-run-all with npm-run-all2 5.0.0 (#19726)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-30 09:27:55 +02:00
renovate[bot] 4c7661dd13 chore(deps): update dependency @types/node to v20.11.10 2024-01-29 12:22:46 +00:00
renovate[bot] b14c03f38a chore(docutils): update dependency mkdocs-material to v9.5.6 2024-01-28 17:45:27 +00:00
renovate[bot] 0e1eb95006 chore(deps): update dependency @types/node to v20.11.9 2024-01-28 14:32:21 +00:00
Mykola Mokhnach edb490f359 chore: Remove commitlint and husky (#19724) 2024-01-27 20:53:14 +01:00
renovate[bot] 202da83449 fix(images-plugin): update dependency lru-cache to v10 (#19723)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-27 08:56:56 +01:00
Edgars Eglītis d10b4ba2c4 chore(docutils): update consola to 3.2.3 (#19655) 2024-01-27 08:56:32 +01:00
renovate[bot] 6cd2c547de fix(doctor): update dependency appium-adb to v12.0.3 (#19722)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-27 08:55:41 +01:00
renovate[bot] 95e3fd2612 chore(deps): update commitlint monorepo to v18.6.0 2024-01-26 19:57:27 +00:00
renovate[bot] 795092a97f fix(support): update dependency axios to v1.6.7 2024-01-26 17:45:56 +00:00
renovate[bot] 5936d53d78 fix(doctor): update dependency appium-adb to v12.0.2 2024-01-26 13:06:36 +00:00
renovate[bot] 5f75d8c8ce chore(deps): update definitelytyped 2024-01-26 11:01:07 +00:00
multicatch 214e922c3e docs: add Rust client (#19702) 2024-01-25 20:13:52 +02:00
两仪院水月 30425f9038 docs: Update zh quickstart next-steps.md (#19701)
docs: update Chinese translation for next-steps.md
2024-01-25 20:10:56 +02:00
renovate[bot] 6313704ee5 fix(support): update dependency axios to v1.6.6 2024-01-25 14:20:29 +00:00
renovate[bot] fb0f8f776c fix(execute-driver-plugin): update dependency webdriverio to v8.29.1 2024-01-25 10:20:45 +00:00
renovate[bot] faff8dc199 chore(docutils): update dependency mkdocs-material to v9.5.5 2024-01-24 15:36:42 +00:00
renovate[bot] 501395c948 fix(types): update dependency type-fest to v4.10.1 (#19703)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-24 15:32:57 +02:00
renovate[bot] 759e06a940 chore(deps): update dependency @types/node to v20.11.6 2024-01-24 10:19:44 +00:00
renovate[bot] 966d305e5e fix(types): update dependency type-fest to v4.10 (#19694)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-23 19:34:11 +02:00
两仪院水月 f3b212ce05 docs: Update zh quickstart install.md (#19689)
* docs: Update zh install.md

Update Chinese translation install.md

* Update install.md

delete space line

* Update install.md

rollback original code

* docs: update zh install.md
2024-01-23 19:23:16 +02:00
renovate[bot] 6f0101eb63 fix(execute-driver-plugin): update dependency webdriverio to v8.29.0 2024-01-23 12:56:28 +00:00
renovate[bot] 9e4cff1595 chore(deps): update eslint-related packages to v6.19.1 2024-01-23 11:31:12 +00:00
两仪院水月 63ef7eb2fb docs: Update zh quickstart index.md (#19688) 2024-01-22 17:26:27 +02:00
两仪院水月 3dcbd6afe7 docs: Update zh resources.md (#19687)
* docs: Update zh resources.md

update Chinese translation for resources.md

* Update resources.md

return original code

* docs: Update zh resources.md

update right update zh resources

* correct space line

* correct space line

* delete line 

delete
2024-01-22 17:23:51 +02:00
renovate[bot] 45abac485b chore(deps): update commitlint monorepo to v18.5.0 2024-01-22 14:27:38 +00:00
renovate[bot] 1fdf117c63 fix(execute-driver-plugin): update dependency webdriverio to v8.28.8 2024-01-20 09:39:20 +00:00
Edgars Eglītis d9711b4191 fix(docutils): change default alias type to symlink (#19681)
* fix(docutils): change default docs alias type to symlink

* fix(docutils): adjust order of mike arguments
2024-01-19 22:06:19 +02:00
两仪院水月 9b16f0d9ad docs: update zh Doc Introduction index.md (#19675)
* Update index.md

add China Doc introduction explanation

* docs: update new doc to lastest version

if has some question , just @unlimitedcodeG , leave my message.

* docs: update correct file index.md
2024-01-19 15:44:07 +02:00
renovate[bot] da84efc42d fix(execute-driver-plugin): update dependency webdriverio to v8.28.6 2024-01-18 11:11:19 +00:00
renovate[bot] 7733b0de3b chore(deps): update dependency tsd to v0.30.4 (#19673)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-18 09:51:49 +02:00
renovate[bot] e8e0444bd2 fix(execute-driver-plugin): update dependency webdriverio to v8.28.0 2024-01-17 18:06:36 +00:00
renovate[bot] c3bea4cf14 chore(deps): update dependency prettier to v3.2.4 2024-01-17 15:51:53 +00:00
renovate[bot] b85a6b939f fix(doctor): update dependency appium-adb to v12.0.1 2024-01-17 14:16:15 +00:00
renovate[bot] 0e94a33240 chore(deps): update dependency @types/node to v20.11.5 2024-01-17 09:16:13 +00:00
Dor Blayzer 4efd0c5cad docs(appium): Add update command in install.md (#19660) 2024-01-16 10:30:30 -08:00
renovate[bot] fcb42f0809 chore(deps): update dependency @types/node to v20.11.4 2024-01-16 13:38:51 +00:00
renovate[bot] 882bd06d1c chore(deps): update eslint-related packages to v6.19.0 2024-01-16 11:08:31 +00:00
renovate[bot] a6d6480074 chore(docutils): update dependency mkdocs-material to v9.5.4 2024-01-15 15:13:30 +00:00
renovate[bot] 45f5660b94 chore(deps): update dependency @types/node to v20.11.1 2024-01-15 11:06:31 +00:00
renovate[bot] 7aabbc3115 chore(support): update dependency sharp to v0.33.2 (#19645)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-14 17:59:57 +02:00
renovate[bot] 2ef9cc5983 chore(deps): update dependency prettier to v3.2.2 2024-01-14 13:49:46 +00:00
renovate[bot] 6ce45cf4f8 chore(deps): update dependency json-schema-to-typescript to v13.1.2 2024-01-14 11:06:36 +00:00
renovate[bot] 9a21dbacf2 chore(deps): update dependency prettier to v3.2.1 2024-01-13 12:52:42 +00:00
renovate[bot] 9617974481 fix(doctor): update dependency appium-adb to v12 (#19648)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-13 11:25:33 +01:00
renovate[bot] ca54b4f79a fix(driver-test-support): update dependency chai to v4.4.1 2024-01-13 09:29:42 +00:00
renovate[bot] 35f1dbabf6 fix(execute-driver-plugin): update dependency webdriverio to v8.27.2 2024-01-12 10:24:09 +00:00
renovate[bot] c3dfade284 chore(deps): update dependency @types/node to v20.11.0 2024-01-11 10:14:42 +00:00
renovate[bot] cbc1f500bc fix(doctor): update dependency appium-adb to v11.1.0 2024-01-10 18:14:24 +00:00
renovate[bot] 293c8031d9 chore(deps): update eslint-related packages to v6.18.1 2024-01-10 17:45:47 +00:00
renovate[bot] 43690a4c77 chore(deps): update definitelytyped 2024-01-10 13:57:08 +00:00
renovate[bot] 40873fb655 fix(universal-xml-plugin): update dependency fast-xml-parser to v4.3.3 2024-01-10 10:18:55 +00:00
Dor Blayzer 7d9065cf31 chore: grammar fix (#19627) 2024-01-08 22:50:48 +01:00
Dor Blayzer 06218c0c6a docs(types): Update README.md with Alerts (#19628) 2024-01-08 22:49:52 +01:00
Dor Blayzer 348b297578 docs(docutils): Update docutils README.md (#19626) 2024-01-08 22:48:43 +01:00
renovate[bot] 314e1b468a chore(deps): update dependency @types/node to v20.10.7 2024-01-08 10:14:33 +00:00
Dor Blayzer 16a7899154 docs: Update warning section in the doctor/README.md (#19622)
* Update Warning Section in doctor/README.md

* add space
2024-01-08 09:19:45 +02:00
Mykola Mokhnach cc24a26c6a docs(doctor): Add deprecation notice (#19621) 2024-01-07 20:00:04 +01:00
renovate[bot] 0f1c12d859 chore(deps): update eslint-related packages to v6.18.0 2024-01-07 09:44:06 +00:00
Mykola Mokhnach f4011f17d4 feat(appium): Set a proper exit code if any of required doctor checks fails (#19617) 2024-01-06 19:48:50 +01:00
renovate[bot] f7e47316c0 fix(driver-test-support): update dependency chai to v4.4.0 2024-01-06 15:17:17 +00:00
renovate[bot] d2666f9eb5 chore(deps): update dependency lerna to v8.0.2 2024-01-06 13:13:10 +00:00
renovate[bot] ac73522351 fix(support): update dependency axios to v1.6.5 (#19616)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-06 11:07:59 +01:00
Kazuaki Matsuo 5aaca9b11c docs(appium): add links to the sample repository urls (#19613) 2024-01-05 10:02:46 -08:00
Peter Schröder 9733c7715e docs(appium): fix incorrect filetype in Ruby quickstart example (#19608)
* fix filename

* via bundler test-rb.md
2024-01-05 09:16:09 -08:00
Mykola Mokhnach 5cd9ca5c21 fix(appium): Tune warning messages about installed extensions (#19612) 2024-01-05 11:27:17 +01:00
renovate[bot] 7084d7cfdc chore(deps): update commitlint monorepo to v18.4.4 2024-01-05 09:46:20 +00:00
Mykola Mokhnach b5460fc131 fix(support): Prefer semver.minVersion to semver.coerce API (#19610) 2024-01-05 08:43:11 +01:00
Jonathan Lipps fc3ea5caa2 test(appium): fix race condition in bidi test 2024-01-04 12:05:14 -08:00
renovate[bot] 332cc48a09 fix(support): update dependency axios to v1.6.4 2024-01-04 12:05:14 -08:00
renovate[bot] 83f24acd39 chore(deps): update dependency tsd to v0.30.3 (#19604)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-04 19:22:38 +02:00
Jonathan Lipps b08210a6f8 chore: publish
- appium@2.4.1
2024-01-04 08:01:04 -08:00
Mykola Mokhnach 474f3ce092 fix(appium): Properly handle version check for aliases 2024-01-04 08:00:16 -08:00
Jonathan Lipps 9ef2533256 docs(appium): make appium doctor guide more visible 2024-01-03 16:30:50 -08:00
Jonathan Lipps 76df4d600d chore: publish
- appium@2.4.0
 - @appium/base-driver@9.5.0
 - @appium/base-plugin@2.2.26
 - @appium/doctor@2.0.31
 - @appium/docutils@1.0.2
 - @appium/driver-test-support@0.4.20
 - @appium/execute-driver-plugin@3.0.23
 - @appium/fake-driver@5.4.0
 - @appium/fake-plugin@3.1.30
 - @appium/images-plugin@3.0.1
 - @appium/opencv@3.0.1
 - @appium/plugin-test-support@0.3.27
 - @appium/support@4.2.0
 - @appium/test-support@3.0.30
 - @appium/types@0.16.0
2024-01-03 13:51:42 -08:00
Mykola Mokhnach eff6d331f2 fix(support): Appium version comparison (#19602) 2024-01-03 22:45:25 +01:00
Edgars Eglītis 5070a34739 docs: update READMEs (#19595)
* docs: update universal-xml-plugin readme

* chore: fix line lengths

* docs: update relaxed-caps-plugin readme

* docs: update images-plugin readme

* docs: update fake-plugin readme

* docs: add readme for execute-driver-plugin

* docs: update base-plugin readme

* docs: update test-support readme

* docs: update support readme

* docs: update opencv readme

* docs: update fake-driver readme

* docs: update docutils readme

* docs: update doctor readme

* docs: update base-driver readme

* docs: fix badge for base-driver

* docs: slightly improve base-driver/plugin readme

* docs: fix title for eslint-config-appium-ts

* docs: add badges for non-prerelease packages

* docs: update appium readme

* docs: update some readme links

* chore: address review comments
2024-01-02 21:37:30 +02:00
renovate[bot] bd88198a9a chore(deps): update dependency tsd to v0.30.2 (#19599)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-02 14:23:22 +02:00
renovate[bot] 8e83996eb6 chore(deps): update eslint-related packages to v6.17.0 2024-01-02 11:04:27 +00:00
renovate[bot] 122a4ec111 chore(deps): update dependency @types/node to v20.10.6 2023-12-30 09:27:57 +00:00
renovate[bot] 94b358022f fix(types): update dependency type-fest to v4 (#19592)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-28 15:21:21 +02:00
renovate[bot] 34654956a3 fix(doctor): update dependency appium-adb to v11.0.9 2023-12-28 10:50:25 +00:00
renovate[bot] ea8394a888 fix(docutils): update dependency lilconfig to v3 (#19489)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-27 19:45:41 +02:00
renovate[bot] b11366c3fc fix(support): update dependency moment to v2.30.1 2023-12-27 16:48:47 +00:00
renovate[bot] 38d9f92d1a chore(deps): update dependency tsd to v0.30.1 (#19584)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-27 11:00:04 +01:00
renovate[bot] fe5245a427 fix(appium): update dependency ws to v8.16.0 (#19586)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-27 10:59:48 +01:00
renovate[bot] e77aa417a0 fix(support): update dependency moment to v2.30.0 (#19587)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-27 10:59:34 +01:00
Mykola Mokhnach 7de3c1d57a chore(support): Deprecate AppiumLogger.errorAndThrow() (#19577) 2023-12-27 10:59:15 +01:00
renovate[bot] 441b2848da fix(support): update dependency axios to v1.6.3 2023-12-27 09:10:45 +00:00
renovate[bot] 523cf6fa59 chore(deps): update eslint-related packages to v6.16.0 2023-12-26 09:31:27 +00:00
renovate[bot] f5fce7ced9 chore(docutils): update dependency mkdocs-material to v9.5.3 2023-12-24 10:48:56 +00:00
renovate[bot] a304a1f78e fix(base-driver): update dependency async-lock to v1.4.1 2023-12-23 09:35:18 +00:00
renovate[bot] 88ab563fa0 chore(support): update dependency sharp to v0.33.1 (#19547)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-23 01:02:54 +01:00
Kazuaki Matsuo 923a40b5eb chore(appium): use ~ instead of ^ to strict versions a bit (#19576) 2023-12-22 15:46:23 -08:00
Mykola Mokhnach 73bf68cc60 feat: Deny install/upgrade of packages which server dep does not meet the current Appium version (#19575) 2023-12-22 22:41:30 +01:00
Kazuaki Matsuo 8c4646f3f3 appium(support): remove "--prefer-offline" to avoid possible installation error (#19571) 2023-12-22 08:47:42 -08:00
renovate[bot] 92d1172984 fix(appium): update dependency ws to v8.15.1 2023-12-22 09:23:15 +00:00
Mykola Mokhnach dde74d98b6 chore(appium): Ditch the redundant find-up dependency (#19570) 2023-12-21 23:32:38 +01:00
Edgars Eglītis 96c19c7887 chore(support): update dependency sharp to v0.33.0 (#19569) 2023-12-21 22:47:00 +02:00
Jonathan Lipps ac9b2bd4fc test(appium): ignore lint of mkdocs generated js 2023-12-21 12:32:15 -08:00
Jonathan Lipps f88d68f295 test(appium): wrap bluebird config in error handling for tests 2023-12-21 12:32:15 -08:00
Jonathan Lipps 2b21e66891 feat: add webdriver bidi support 2023-12-21 12:32:15 -08:00
renovate[bot] 9b06b871eb fix(execute-driver-plugin): update dependency webdriverio to v8.27.0 2023-12-21 09:02:23 +00:00
Mykola Mokhnach 893b9e15d6 feat(appium,support): Add common shortcuts for doctor checks (#19562) 2023-12-21 09:24:36 +01:00
Mykola Mokhnach 9aae933581 chore(appium): Tune the error message for a non-existing extension (#19561) 2023-12-21 09:06:15 +01:00
Mykola Mokhnach dc2cedfecd feat(appium): Print the list of available extension scripts if no script name is provided (#19539) 2023-12-20 14:09:48 +01:00
Mykola Mokhnach a30286b6e7 feat(appium): Make doctor extensible (#19542) 2023-12-19 18:40:04 +01:00
renovate[bot] 3bdb8a4f4a chore(deps): update eslint-related packages to v6.15.0 2023-12-19 17:16:57 +00:00
renovate[bot] ff767b7f25 fix(execute-driver-plugin): update dependency webdriverio to v8.26.3 2023-12-19 12:21:15 +00:00
renovate[bot] cbf5e1e6de chore(docutils): update dependency mkdocs-git-revision-date-localized-plugin to v1.2.2 2023-12-19 09:27:48 +00:00
Edgars Eglītis e41935fd64 chore: Update CHANGELOG.md 2023-12-18 15:38:31 -08:00
Edgars Eglītis 73837f1063 chore: Update CHANGELOG.md 2023-12-18 15:38:31 -08:00
Edgars Eglītis b114030e94 chore(docutils): update changelog
Due to how lerna works, it is not straightforward to bump a package from 0.x to 1.x, so breaking changes for this package were labeled as fixes. This adjusts the changelog to add more appropriate labels for the changes.
2023-12-18 15:38:31 -08:00
137 changed files with 4391 additions and 4688 deletions
+1
View File
@@ -6,3 +6,4 @@ examples/javascript-wd
sample-code
**/build-fixtures/**
packages/appium/docs/**/assets/**
packages/appium/docs/**/js/**
+1 -1
View File
@@ -20,7 +20,7 @@ _Put an `x` in the boxes that apply. You can also fill these out after creating
- [ ] I have signed the CLA
- [ ] Lint and unit tests pass locally with my changes
- [ ] I have added tests that prove my fix is effective or that my feature works
- [ ] I have added necessary documentation (if appropriate)
- [ ] I have added the necessary documentation (if appropriate)
- [ ] Any dependent changes have been merged and published in downstream modules
## Further comments
+1 -1
View File
@@ -13,4 +13,4 @@ jobs:
name: Auto-Label PRs
runs-on: ubuntu-latest
steps:
- uses: fuxingloh/multi-labeler@v2
- uses: fuxingloh/multi-labeler@v4
+4
View File
@@ -45,6 +45,10 @@ on:
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
prepare_matrix:
runs-on: ubuntu-latest
+1 -1
View File
@@ -9,7 +9,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check for "On Hold" label
uses: egmacke/action-check-label@v2
uses: egmacke/action-check-label@v3
with:
label: On Hold
state: absent
+1 -1
View File
@@ -25,6 +25,6 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Validate Renovate Config
uses: rinchsan/renovate-config-validator@v0.0.12
uses: rinchsan/renovate-config-validator@v0.2.0
with:
pattern: '{.renovaterc.json,renovate/default.json}'
+2
View File
@@ -29,6 +29,8 @@ build/
.idea
*DerivedData
__pycache__
certificate.cert
certificate.key
# csharp
*.pyc
-4
View File
@@ -1,4 +0,0 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npm run lint:commit -- --edit ${1}
-4
View File
@@ -1,4 +0,0 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npm run lint:staged
+130 -159
View File
@@ -1,214 +1,185 @@
## Appium
[![Stand With Ukraine](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner2-direct.svg)](https://vshymanskyy.github.io/StandWithUkraine/)
<p align="center">
<a href="https://appium.io/">
<img alt="Appium" src="https://raw.githubusercontent.com/appium/appium/master/packages/appium/docs/assets/images/appium-logo-horiz.png" width="500">
</a>
</p>
<p align="center">
Cross-platform test automation for native, hybrid, mobile web and desktop apps.
</p>
[![NPM version](https://badge.fury.io/js/appium.svg)](https://npmjs.org/package/appium)
[![Monthly Downloads](https://img.shields.io/npm/dm/appium.svg)](https://npmjs.org/package/appium)
[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fappium%2Fappium.svg?type=shield)](https://app.fossa.io/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fappium%2Fappium?ref=badge_shield)
[![StandWithUkraine](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/badges/StandWithUkraine.svg)](https://github.com/vshymanskyy/StandWithUkraine/)
[![Stand With Ukraine](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner2-direct.svg)](https://vshymanskyy.github.io/StandWithUkraine/)
***
Appium is an open-source, cross-platform test automation tool for native,
hybrid, mobile web and desktop apps. Initially created to automate iOS and Android mobile
applications Appium has grown to a full-featured platform that provides [WebDriver](https://www.w3.org/TR/webdriver/)-based automation possibilities for the whole set of different mobile and desktop platforms.
See the full list of
[officially-supported](https://appium.io/docs/en/latest/ecosystem/#drivers) and
[third-party](https://appium.io/docs/en/latest/ecosystem/#other-drivers) drivers for more details.
<p align="center"><b>
<a href="https://appium.io">Documentation</a> |
<a href="https://appium.io/docs/en/latest/intro/">Get Started</a> |
<a href="https://appium.io/docs/en/latest/ecosystem/">Ecosystem</a> |
<a href="https://github.com/appium/appium/blob/master/packages/appium/CHANGELOG.md">Changelog</a> |
<a href="https://github.com/appium/appium/blob/master/ROADMAP.md">Roadmap</a> |
<a href="https://discuss.appium.io">Discussion Forum</a>
</b></p>
:bangbang: Appium core team does not maintain Appium 1.x anymore since the 1st of January 2022. All recent versions of officially supported platform drivers are not compatible to Appium 1.x, and require Appium 2 to run. [Please read the migration guide from 1.x to 2.0](https://appium.io/docs/en/latest/guides/migrating-1-to-2/) to manage the Appium server.
***
:bangbang: If you still use the deprecated Appium Desktop containing the built-in server then you won't be able to upgrade to Appium 2. Consider switching to [Appium Inspector](https://github.com/appium/appium-inspector) instead.
Appium is an open-source automation framework that provides
[WebDriver](https://www.w3.org/TR/webdriver/)-based automation possibilities for a wide range of
different mobile, desktop and IoT platforms. Appium is modular and extensible, and supports multiple
programming languages, which means there is an entire ecosystem of related software:
* [__Drivers__](#drivers) add support for automating specific platforms
* [__Clients__](#clients) allow writing Appium tests in your programming language of choice
* [__Plugins__](#plugins) allow to further extend Appium functionality
### Documentation
## Migrating From Appium 1 to Appium 2
The current Appium documentation is available at [__https://appium.io__](https://appium.io).
As of January 1st, 2022, the Appium team no longer maintains or supports Appium 1. All officially
supported platform drivers are only compatible with Appium 2.
### Requirements
__[Please read the Migration Guide](https://appium.io/docs/en/latest/guides/migrating-1-to-2/) if you
are still using Appium 1.__
- macOS, Linux or Windows operating system
- Node.js 16+. Any relevant [LTS release](https://nodejs.dev/en/about/releases/) should work just fine.
- NPM (Node Package Manager) 8+
> [!WARNING]
> If you use Appium Desktop or Appium Server GUI, you will not be able to upgrade to Appium 2, as
both of these tools have been deprecated. Please use [Appium Inspector](https://github.com/appium/appium-inspector)
in combination with a standalone Appium 2 server.
These are only server requirements. Each driver might have its own requirements. Consider checking the corresponding driver tutorial for more details.
## Installation
### Server
Appium can be installed using `npm` (other package managers are not currently supported). Please
check the [installation docs](http://appium.io/docs/en/latest/quickstart/install/) for the
system requirements and further information.
To install Appium 2 server using Node Package Manager (npm) run the following command:
If upgrading from Appium 1, make sure Appium 1 is fully uninstalled (`npm uninstall -g appium`).
Unexpected errors might appear if this has not been done.
```bash
npm i -g appium
```
To update Appium 2 server using Node Package Manager (npm) run the following command:
Note that this will only install the core Appium server, which cannot automate anything on its own.
Please install [drivers](#drivers) for your target platforms in order to automate them.
```bash
npm i -g appium@latest
```
## Drivers
:bangbang: Make sure Appium 1 is fully uninstalled before upgrading it to Appium 2 (`npm uninstall -g appium`). Unexpected errors might appear if this has not been done.
Appium supports app automation across a variety of platforms, like iOS, Android, macOS, Windows,
and more. Each platform is supported by one or more "drivers", which know how to automate that
particular platform. You can find a full list of officially-supported and third-party drivers in
[Appium Ecosystem's Drivers page](http://appium.io/docs/en/latest/ecosystem/drivers/).
### Drivers
Appium supports app automation across a variety of platforms, like iOS,
Android, and Windows. Each platform is supported by one or more "drivers",
which know how to automate that particular platform. Since version 2.0
all drivers have been isolated from the Appium server app and can
be managed independently using the [appium driver](https://appiumpro.com/editions/122-installing-appium-20-and-the-driver-and-plugins-cli) command line interface.
In general, the drivers management in Appium 2 is as simple as:
Driver management is done using [Appium's Extension command-line interface](http://appium.io/docs/en/latest/cli/extensions/):
```bash
# To install a new driver from npm
appium driver install --source=npm appium-xcuitest-driver[@<version>]
# To install a driver from a local folder (useful for developers)
appium driver install --source=local /Users/me/sources/appium-xcuitest-driver
# To install a new driver from github (hm, maybe it's time to publish it to NPM?)
appium driver install --source=github --package=appium-xcuitest-driver appium/appium-xcuitest-driver
# Install an official driver from npm (see documentation for a list of such drivers)
appium driver install <driver-name>
# Install any driver from npm
appium driver install --source=npm <driver-name>
# See documentation for installation from other sources
# To list already installed drivers
# List already installed drivers
appium driver list --installed
# To update a driver (it must be already installed)
appium driver update xcuitest
# To uninstall a driver (it won't last forever, wouldn't it?)
appium driver uninstall xcuitest
# Update a driver (it must be already installed)
# This will NOT update the major version, in order to prevent breaking changes
appium driver update <driver-name>
# Update a driver to the most recent version (may include breaking changes)
appium driver update <driver-name> --unsafe
# Uninstall a driver (it won't last forever, will it?)
appium driver uninstall <driver-name>
```
You can find a full list of
[officially-supported](https://appium.io/docs/en/latest/ecosystem/#drivers) and
[third-party](https://appium.io/docs/en/latest/ecosystem/#other-drivers) drivers at
the current Appium 2.0 documentation.
## Clients
### Plugins
Client libraries enable writing Appium tests in different programming languages. There are
officially-supported clients for Java, Python, Ruby, and .NET C#, as well as third-party clients
for other languages. You can find a full list of clients in
[Appium Ecosystem's Clients page](http://appium.io/docs/en/latest/ecosystem/clients/).
The concept of plugins is something new that has been added exclusively to Appium2. Plugins allow you to extend server functionality without changing the server code. Plugins could be managed similarly to drivers:
## Plugins
Plugins allow you to extend server functionality without changing the server code. The main
difference between drivers and plugins is that the latter must be explicitly enabled on
Appium server startup (all installed drivers are enabled by default):
```bash
# To install an officially supported plugin
appium plugin install images
# To install a plugin from a local folder (useful for developers)
appium plugin install --source=local /Users/me/sources/images
# To install a new plugin from npm
appium plugin install --source=npm appium-device-farm
appium --use-plugins=<plugin-name>
```
# To list already installed plugins
You can find a full list of officially-supported and third-party plugins in
[Appium Ecosystem's Plugins page](http://appium.io/docs/en/latest/ecosystem/plugins/).
Similarly to drivers, plugin management is also done using
[Appium's Extension command-line interface](http://appium.io/docs/en/latest/cli/extensions/):
```bash
# Install an official plugin from npm (see documentation for a list of such plugins)
appium plugin install <plugin-name>
# Install any plugin from npm
appium plugin install --source=npm <plugin-name>
# See documentation for installation from other sources
# List already installed plugins
appium plugin list --installed
# To update a plugins (it must be already installed)
appium plugin update appium-device-farm
# To uninstall a plugin
appium plugin uninstall appium-device-farm
# Update a plugin (it must be already installed)
# This will NOT update the major version, in order to prevent breaking changes
appium plugin update <plugin-name>
# Update a plugin to the most recent version (may include breaking changes)
appium plugin update <plugin-name> --unsafe
# Uninstall a plugin
appium plugin uninstall <plugin-name>
```
The main difference between drivers and plugins is that the latter must be explicitly enabled on server startup after it was installed (drivers are enabled by default after installation):
## Server Command Line Interface
In order to start sending commands to the Appium server, it must be running on the URL and port
where your client library expects it to listen. [Appium's command-line interface](http://appium.io/docs/en/latest/cli/args/)
is used to launch and configure the server:
```bash
appium server --use-plugins=device-farm,images
```
You can find a full list of
[officially-supported](https://appium.io/docs/en/latest/ecosystem/#plugins) and
[third-party](https://appium.io/docs/en/latest/ecosystem/#other-plugins) plugins at
the current Appium 2.0 documentation.
### Server Command Line Interface
In order to start sending commands to Appium over the wire it must be listening
on the URL where your client library expects it to listen.
Use the following commands to run and configure Appium server:
```bash
# Start the server on the default port and host (e.g. http://0.0.0.0:4723/)
# Start the server on the default host (0.0.0.0) and port (4723)
appium server
# Start the server on the given port, host and use the base path prefix (the default prefix is /)
appium server -p 9000 -a 127.0.0.1 -pa /wd/hub
# Get the list of all supported command line parameters.
# This list would also include descriptions of driver-specific
# command line arguments for all installed drivers.
# Each driver and plugin must have their command line arguments
# exposed in a special JSON schema declared as a part of the corresponding
# package.json file.
appium server --help
# You can also omit the 'server' subcommand
appium
# Start the server on the given host, port and use a custom base path prefix (the default prefix is '/')
appium --address 127.0.0.1 --port 9000 --base-path /wd/hub
```
Appium supports execution of parallel server processes as well as parallel driver sessions within
Appium supports execution of parallel server processes, as well as parallel driver sessions within a
single server process. Refer the corresponding driver documentations regarding which mode is optimal
for the particular driver or whether it supports parallel sessions.
### Why Appium?
## Why Appium?
1. You usually don't have to recompile your app or modify it in any way, due
to the use of standard automation APIs on all platforms.
2. You can write tests with your favorite dev tools using any
[WebDriver](https://w3c.github.io/webdriver/webdriver-spec.html)-compatible
language such as [Java](https://github.com/appium/java-client),
[JavaScript](https://webdriver.io/), [Python](https://github.com/appium/python-client),
[Ruby](https://github.com/appium/ruby_lib), [C#](https://github.com/appium/dotnet-client)
with the Selenium WebDriver API. There are also various third party
client implementations for other languages.
1. You usually don't have to recompile your app or modify it in any way, due to the use of standard
automation APIs on all platforms.
2. You can write tests with your favorite dev tools using any WebDriver-compatible language such as
Java, Python, Ruby and C#. There are also third party client implementations for other languages.
3. You can use any testing framework.
4. Some drivers, like xcuitest and uiautomator2 ones have built-in mobile web and
hybrid app support. Within the same script, you can switch seamlessly between native
app automation and webview automation, all using the WebDriver model that's already
the standard for web automation.
5. You can run your automated tests locally and in a cloud. There are multiple
cloud providers that support various Appium drivers (mostly
targeting iOS and Android mobile automation).
6. [Appium Inspector](https://github.com/appium/appium-inspector) allows
visual debugging of automated tests and could be extremely useful for
beginners.
4. Some drivers like `xcuitest` and `uiautomator2` have built-in mobile web and hybrid app support.
Within the same script, you can switch seamlessly between native app automation and webview
automation, all using the WebDriver model that's already the standard for web automation.
5. You can run your automated tests locally and in a cloud. There are multiple cloud providers that
support various Appium drivers (mostly targeting iOS and Android mobile automation).
6. [Appium Inspector](https://github.com/appium/appium-inspector) can be used to visually inspect
the page source of applications across different platforms, facilitating easier test development.
Investing in the
[WebDriver](https://w3c.github.io/webdriver/webdriver-spec.html) protocol means
you are betting on a single, free, and open protocol for testing that has become
a web standard. Don't lock yourself into a proprietary stack.
Investing in the [WebDriver](https://w3c.github.io/webdriver/webdriver-spec.html) protocol means you
are betting on a single, free, and open protocol for testing that has become a web standard. Don't
lock yourself into a proprietary stack.
For example, if you use Apple's XCUITest library without Appium you can only
write tests using Obj-C/Swift, and you can only run tests through Xcode.
Similarly, with Google's UiAutomator or Espresso, you can only write tests in
Java/Kotlin. Appium opens up the possibility of true cross-platform native app
automation, for mobile and beyond. Finally!
For example, if you use Apple's XCUITest library without Appium, you can only write tests using
Obj-C/Swift, and you can only run tests through Xcode. Similarly, with Google's UiAutomator or
Espresso, you can only write tests in Java/Kotlin. Appium opens up the possibility of true
cross-platform native app automation, for mobile and beyond!
If you're new to Appium or want a more comprehensive description of what this is all
about, please read our [Intro to Appium](https://appium.io/docs/en/latest/intro/).
If you are looking for a more comprehensive description of what this is all about, please read our
documentation on [How Does Appium Work?](https://appium.io/docs/en/latest/intro/appium/).
### Quickstart
## License
Check out our [Quickstart](https://appium.io/docs/en/latest/quickstart/) guide
to get going with Appium.
There is also a sample code that contains [many examples of tests in a variety
of different languages](https://github.com/appium/appium/tree/1.x/sample-code)!
### Documentation
For prettily-rendered docs, please visit [Appium Documentation](https://appium.io).
You can always find the full list of Appium doc pages at [Appium's GitHub
Repo](https://github.com/appium/appium/tree/master/packages/appium/docs) as well.
### Contributing
Please take a look at our [contribution documentation](CONTRIBUTING.md)
for instructions on how to build, test, and run Appium from the source.
### Project History, Credits & Inspiration
* [History](https://appium.io/docs/en/latest/intro/history/)
### User Forums
Announcements and debates often take place on the [Discussion Group](https://discuss.appium.io),
be sure to sign up!
### Troubleshooting
Please find the troubleshooting guide at the corresponding driver repository/documentation.
### License
[Apache-2.0](./LICENSE)
[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fappium%2Fappium.svg?type=large)](https://app.fossa.io/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fappium%2Fappium?ref=badge_large)
-11
View File
@@ -1,11 +0,0 @@
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
'body-leading-blank': [0],
'body-max-line-length': [0],
'footer-max-line-length': [0],
'header-max-length': [0],
'subject-case': [0],
'subject-full-stop': [0],
},
};
+1138 -3687
View File
File diff suppressed because it is too large Load Diff
+15 -22
View File
@@ -44,13 +44,10 @@
"docs:preview": "npm run -w packages/appium build:docs:preview",
"doctor": "node ./packages/doctor",
"generate-docs": "npm run --workspace=packages/appium generate-docs",
"husky-install": "husky install",
"lint": "eslint .",
"lint:ci": "eslint --quiet .",
"lint:commit": "commitlint",
"lint:fix": "run-s \"lint -- --fix\"",
"lint:staged": "lint-staged",
"prepare": "run-p husky-install rebuild",
"prepare": "run-p rebuild",
"prepublishOnly": "run-s rebuild",
"preversion": "run-s rebuild",
"version": "run-s sync-pkgs stage-synced-pkgs",
@@ -84,25 +81,23 @@
},
"devDependencies": {
"@colors/colors": "1.6.0",
"@commitlint/cli": "18.4.3",
"@commitlint/config-conventional": "18.4.3",
"@tsconfig/node14": "14.1.0",
"@types/chai": "4.3.11",
"@types/chai-as-promised": "7.1.8",
"@types/diff": "5.0.9",
"@types/mocha": "10.0.6",
"@types/node": "20.10.5",
"@types/semver": "7.5.6",
"@types/sinon": "17.0.2",
"@types/node": "20.11.17",
"@types/semver": "7.5.7",
"@types/sinon": "17.0.3",
"@types/sinon-chai": "3.2.12",
"@types/source-map-support": "0.5.10",
"@types/teen_process": "2.0.4",
"@types/ws": "8.5.10",
"@types/yargs": "17.0.32",
"@typescript-eslint/eslint-plugin": "6.14.0",
"@typescript-eslint/parser": "6.14.0",
"@typescript-eslint/eslint-plugin": "6.21.0",
"@typescript-eslint/parser": "6.21.0",
"asyncbox": "3.0.0",
"chai": "4.3.10",
"chai": "4.4.1",
"chai-as-promised": "7.1.1",
"chai-webdriverio-async": "2.8.0",
"conventional-changelog-conventionalcommits": "7.0.2",
@@ -117,18 +112,16 @@
"fancy-log": "2.0.0",
"finalhandler": "1.2.0",
"get-port": "5.1.1",
"husky": "8.0.3",
"json-schema-to-typescript": "13.1.1",
"lerna": "8.0.1",
"lint-staged": "15.2.0",
"json-schema-to-typescript": "13.1.2",
"lerna": "8.1.2",
"log-symbols": "4.1.0",
"midnight-smoker": "8.0.0",
"mjpeg-consumer": "2.0.0",
"mjpeg-server": "0.3.1",
"mocha": "10.2.0",
"mocha": "10.3.0",
"mocha-chai-jest-snapshot": "1.1.4",
"npm-run-all": "4.1.5",
"prettier": "3.1.1",
"npm-run-all2": "6.1.2",
"prettier": "3.2.5",
"rewiremock": "3.14.5",
"rimraf": "5.0.5",
"serve-static": "1.15.0",
@@ -137,11 +130,11 @@
"sync-monorepo-packages": "1.0.2",
"through2": "4.0.2",
"ts-node": "10.9.2",
"tsd": "0.30.0",
"tsd": "0.30.4",
"typescript": "5.2.2",
"validate.js": "0.13.1",
"webdriverio": "8.26.2",
"ws": "8.15.1",
"webdriverio": "8.31.1",
"ws": "8.16.0",
"yaml-js": "0.3.1"
},
"engines": {
+74
View File
@@ -3,6 +3,80 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [2.5.1](https://github.com/appium/appium/compare/appium@2.5.0...appium@2.5.1) (2024-02-13)
### Bug Fixes
* **execute-driver-plugin:** update dependency webdriverio to v8.31.1 ([db0eec0](https://github.com/appium/appium/commit/db0eec05c58fe970351a309a01507cad824bdad2))
* **support:** update dependency @types/semver to v7.5.7 ([bd8a92f](https://github.com/appium/appium/commit/bd8a92f588d40c97fbe51f8bd3b7835c96e9fe8f))
* **support:** update dependency semver to v7.6.0 ([7b40a32](https://github.com/appium/appium/commit/7b40a3225ebe0f5976a26d05db2f22ba7b13a130))
## [2.5.0](https://github.com/appium/appium/compare/appium@2.4.1...appium@2.5.0) (2024-02-06)
### Features
* **appium:** Set a proper exit code if any of required doctor checks fails ([#19617](https://github.com/appium/appium/issues/19617)) ([f4011f1](https://github.com/appium/appium/commit/f4011f17d4d03bc854c43462848a4c38427acb85))
### Bug Fixes
* **appium:** Tune warning messages about installed extensions ([#19612](https://github.com/appium/appium/issues/19612)) ([5cd9ca5](https://github.com/appium/appium/commit/5cd9ca5c21393c70d259f7525d2c9d35b7eeb2a2))
* **execute-driver-plugin:** update dependency webdriverio to v8.27.2 ([35f1dba](https://github.com/appium/appium/commit/35f1dbabf6e852c4ae84ec128879bc7e61321a16))
* **execute-driver-plugin:** update dependency webdriverio to v8.28.0 ([e8e0444](https://github.com/appium/appium/commit/e8e0444bd28d94963090065a813a4b10be665caa))
* **execute-driver-plugin:** update dependency webdriverio to v8.28.6 ([da84efc](https://github.com/appium/appium/commit/da84efc42dd62b2be07a52dd4379791e2cef6eb2))
* **execute-driver-plugin:** update dependency webdriverio to v8.28.8 ([1fdf117](https://github.com/appium/appium/commit/1fdf117c63f6e9871f4de3c0bd93ecbdb13a9235))
* **execute-driver-plugin:** update dependency webdriverio to v8.29.0 ([6f0101e](https://github.com/appium/appium/commit/6f0101eb6343eb8b372e00a6b71d2b2ee909404c))
* **execute-driver-plugin:** update dependency webdriverio to v8.29.1 ([fb0f8f7](https://github.com/appium/appium/commit/fb0f8f776c6a03174f4569efa60d817a930809cf))
* **execute-driver-plugin:** update dependency webdriverio to v8.29.3 ([6dacb5e](https://github.com/appium/appium/commit/6dacb5e70ca3529a080f629f5245c2b676746e2c))
* **execute-driver-plugin:** update dependency webdriverio to v8.29.7 ([52a6f96](https://github.com/appium/appium/commit/52a6f96e085ff13acdf8d5013673861b7e0da818))
* **execute-driver-plugin:** update dependency webdriverio to v8.30.0 ([91320dd](https://github.com/appium/appium/commit/91320ddd02dca89a8cec258d0ab44a49d344d71c))
* **support:** update dependency axios to v1.6.4 ([332cc48](https://github.com/appium/appium/commit/332cc48a09b5532a8d51f85f3a24785e2c754e00))
* **support:** update dependency axios to v1.6.5 ([#19616](https://github.com/appium/appium/issues/19616)) ([ac73522](https://github.com/appium/appium/commit/ac73522351b31bd6c11972c61daa8b6b8d18fb91))
* **support:** update dependency axios to v1.6.6 ([6313704](https://github.com/appium/appium/commit/6313704ee5a8ee3aee726eb512ef259b6fa1041c))
* **support:** update dependency axios to v1.6.7 ([795092a](https://github.com/appium/appium/commit/795092a97f6d7569cccc4b5c166f52fef821514b))
* **types:** update dependency type-fest to v4.10 ([#19694](https://github.com/appium/appium/issues/19694)) ([966d305](https://github.com/appium/appium/commit/966d305e5eade9369a3875243bcad951df88545c))
* **types:** update dependency type-fest to v4.10.1 ([#19703](https://github.com/appium/appium/issues/19703)) ([501395c](https://github.com/appium/appium/commit/501395c9489320b84ab49ff78af4270f66070d62))
## [2.4.1](https://github.com/appium/appium/compare/appium@2.4.0...appium@2.4.1) (2024-01-04)
### Bug Fixes
* **appium:** Properly handle version check for aliases ([474f3ce](https://github.com/appium/appium/commit/474f3ce092c68981655d9deba546a6202acea141))
## [2.4.0](https://github.com/appium/appium/compare/appium@2.3.0...appium@2.4.0) (2024-01-03)
### Features
* add webdriver bidi support ([2b21e66](https://github.com/appium/appium/commit/2b21e66891e8ab8c3929f04f32e94eb4efdba691))
* **appium,support:** Add common shortcuts for doctor checks ([#19562](https://github.com/appium/appium/issues/19562)) ([893b9e1](https://github.com/appium/appium/commit/893b9e15d64d2356ea339596e805543df51fa505))
* **appium:** Make doctor extensible ([#19542](https://github.com/appium/appium/issues/19542)) ([a30286b](https://github.com/appium/appium/commit/a30286b6e7b9753f73e55a8f9db14211b3124578))
* **appium:** Print the list of available extension scripts if no script name is provided ([#19539](https://github.com/appium/appium/issues/19539)) ([dc2cedf](https://github.com/appium/appium/commit/dc2cedfecd19581321f6947ef1c847065ba2d3d6))
* Deny install/upgrade of packages which server dep does not meet the current Appium version ([#19575](https://github.com/appium/appium/issues/19575)) ([73bf68c](https://github.com/appium/appium/commit/73bf68cc602de0999e1956110be225c3fd9087c2))
### Bug Fixes
* **appium:** update dependency ws to v8.15.1 ([92d1172](https://github.com/appium/appium/commit/92d117298442e72ae262f224733068ddf147f4c8))
* **appium:** update dependency ws to v8.16.0 ([#19586](https://github.com/appium/appium/issues/19586)) ([fe5245a](https://github.com/appium/appium/commit/fe5245a42784181a9cd7a8ff6b095a622ae40920))
* **base-driver:** update dependency async-lock to v1.4.1 ([a304a1f](https://github.com/appium/appium/commit/a304a1f78e658f6f70cbe8e1efd6d06b81d8d34e))
* **docutils:** update dependency lilconfig to v3 ([#19489](https://github.com/appium/appium/issues/19489)) ([ea8394a](https://github.com/appium/appium/commit/ea8394a888c835bc54f7f68174a4b6cae85339ea))
* **execute-driver-plugin:** update dependency webdriverio to v8.26.3 ([ff767b7](https://github.com/appium/appium/commit/ff767b7f25e97ec9643f691b57ca6ba159dcdcf9))
* **execute-driver-plugin:** update dependency webdriverio to v8.27.0 ([9b06b87](https://github.com/appium/appium/commit/9b06b871eb3d9c60428130442ea7ab433ce4df31))
* **support:** update dependency axios to v1.6.3 ([441b284](https://github.com/appium/appium/commit/441b2848dae28472356f37fc5d51ac27af7bbe29))
* **types:** update dependency type-fest to v4 ([#19592](https://github.com/appium/appium/issues/19592)) ([94b3580](https://github.com/appium/appium/commit/94b358022fdba3050ef94c1f881895f07e24fb75))
## [2.3.0](https://github.com/appium/appium/compare/appium@2.2.3...appium@2.3.0) (2023-12-18)
+130 -159
View File
@@ -1,214 +1,185 @@
## Appium
[![Stand With Ukraine](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner2-direct.svg)](https://vshymanskyy.github.io/StandWithUkraine/)
<p align="center">
<a href="https://appium.io/">
<img alt="Appium" src="https://raw.githubusercontent.com/appium/appium/master/packages/appium/docs/assets/images/appium-logo-horiz.png" width="500">
</a>
</p>
<p align="center">
Cross-platform test automation for native, hybrid, mobile web and desktop apps.
</p>
[![NPM version](https://badge.fury.io/js/appium.svg)](https://npmjs.org/package/appium)
[![Monthly Downloads](https://img.shields.io/npm/dm/appium.svg)](https://npmjs.org/package/appium)
[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fappium%2Fappium.svg?type=shield)](https://app.fossa.io/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fappium%2Fappium?ref=badge_shield)
[![StandWithUkraine](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/badges/StandWithUkraine.svg)](https://github.com/vshymanskyy/StandWithUkraine/)
[![Stand With Ukraine](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner2-direct.svg)](https://vshymanskyy.github.io/StandWithUkraine/)
***
Appium is an open-source, cross-platform test automation tool for native,
hybrid, mobile web and desktop apps. Initially created to automate iOS and Android mobile
applications Appium has grown to a full-featured platform that provides [WebDriver](https://www.w3.org/TR/webdriver/)-based automation possibilities for the whole set of different mobile and desktop platforms.
See the full list of
[officially-supported](https://appium.io/docs/en/latest/ecosystem/#drivers) and
[third-party](https://appium.io/docs/en/latest/ecosystem/#other-drivers) drivers for more details.
<p align="center"><b>
<a href="https://appium.io">Documentation</a> |
<a href="https://appium.io/docs/en/latest/intro/">Get Started</a> |
<a href="https://appium.io/docs/en/latest/ecosystem/">Ecosystem</a> |
<a href="https://github.com/appium/appium/blob/master/packages/appium/CHANGELOG.md">Changelog</a> |
<a href="https://github.com/appium/appium/blob/master/ROADMAP.md">Roadmap</a> |
<a href="https://discuss.appium.io">Discussion Forum</a>
</b></p>
:bangbang: Appium core team does not maintain Appium 1.x anymore since the 1st of January 2022. All recent versions of officially supported platform drivers are not compatible to Appium 1.x, and require Appium 2 to run. [Please read the migration guide from 1.x to 2.0](https://appium.io/docs/en/latest/guides/migrating-1-to-2/) to manage the Appium server.
***
:bangbang: If you still use the deprecated Appium Desktop containing the built-in server then you won't be able to upgrade to Appium 2. Consider switching to [Appium Inspector](https://github.com/appium/appium-inspector) instead.
Appium is an open-source automation framework that provides
[WebDriver](https://www.w3.org/TR/webdriver/)-based automation possibilities for a wide range of
different mobile, desktop and IoT platforms. Appium is modular and extensible, and supports multiple
programming languages, which means there is an entire ecosystem of related software:
* [__Drivers__](#drivers) add support for automating specific platforms
* [__Clients__](#clients) allow writing Appium tests in your programming language of choice
* [__Plugins__](#plugins) allow to further extend Appium functionality
### Documentation
## Migrating From Appium 1 to Appium 2
The current Appium documentation is available at [__https://appium.io__](https://appium.io).
As of January 1st, 2022, the Appium team no longer maintains or supports Appium 1. All officially
supported platform drivers are only compatible with Appium 2.
### Requirements
__[Please read the Migration Guide](https://appium.io/docs/en/latest/guides/migrating-1-to-2/) if you
are still using Appium 1.__
- macOS, Linux or Windows operating system
- Node.js 16+. Any relevant [LTS release](https://nodejs.dev/en/about/releases/) should work just fine.
- NPM (Node Package Manager) 8+
> [!WARNING]
> If you use Appium Desktop or Appium Server GUI, you will not be able to upgrade to Appium 2, as
both of these tools have been deprecated. Please use [Appium Inspector](https://github.com/appium/appium-inspector)
in combination with a standalone Appium 2 server.
These are only server requirements. Each driver might have its own requirements. Consider checking the corresponding driver tutorial for more details.
## Installation
### Server
Appium can be installed using `npm` (other package managers are not currently supported). Please
check the [installation docs](http://appium.io/docs/en/latest/quickstart/install/) for the
system requirements and further information.
To install Appium 2 server using Node Package Manager (npm) run the following command:
If upgrading from Appium 1, make sure Appium 1 is fully uninstalled (`npm uninstall -g appium`).
Unexpected errors might appear if this has not been done.
```bash
npm i -g appium
```
To update Appium 2 server using Node Package Manager (npm) run the following command:
Note that this will only install the core Appium server, which cannot automate anything on its own.
Please install [drivers](#drivers) for your target platforms in order to automate them.
```bash
npm i -g appium@latest
```
## Drivers
:bangbang: Make sure Appium 1 is fully uninstalled before upgrading it to Appium 2 (`npm uninstall -g appium`). Unexpected errors might appear if this has not been done.
Appium supports app automation across a variety of platforms, like iOS, Android, macOS, Windows,
and more. Each platform is supported by one or more "drivers", which know how to automate that
particular platform. You can find a full list of officially-supported and third-party drivers in
[Appium Ecosystem's Drivers page](http://appium.io/docs/en/latest/ecosystem/drivers/).
### Drivers
Appium supports app automation across a variety of platforms, like iOS,
Android, and Windows. Each platform is supported by one or more "drivers",
which know how to automate that particular platform. Since version 2.0
all drivers have been isolated from the Appium server app and can
be managed independently using the [appium driver](https://appiumpro.com/editions/122-installing-appium-20-and-the-driver-and-plugins-cli) command line interface.
In general, the drivers management in Appium 2 is as simple as:
Driver management is done using [Appium's Extension command-line interface](http://appium.io/docs/en/latest/cli/extensions/):
```bash
# To install a new driver from npm
appium driver install --source=npm appium-xcuitest-driver[@<version>]
# To install a driver from a local folder (useful for developers)
appium driver install --source=local /Users/me/sources/appium-xcuitest-driver
# To install a new driver from github (hm, maybe it's time to publish it to NPM?)
appium driver install --source=github --package=appium-xcuitest-driver appium/appium-xcuitest-driver
# Install an official driver from npm (see documentation for a list of such drivers)
appium driver install <driver-name>
# Install any driver from npm
appium driver install --source=npm <driver-name>
# See documentation for installation from other sources
# To list already installed drivers
# List already installed drivers
appium driver list --installed
# To update a driver (it must be already installed)
appium driver update xcuitest
# To uninstall a driver (it won't last forever, wouldn't it?)
appium driver uninstall xcuitest
# Update a driver (it must be already installed)
# This will NOT update the major version, in order to prevent breaking changes
appium driver update <driver-name>
# Update a driver to the most recent version (may include breaking changes)
appium driver update <driver-name> --unsafe
# Uninstall a driver (it won't last forever, will it?)
appium driver uninstall <driver-name>
```
You can find a full list of
[officially-supported](https://appium.io/docs/en/latest/ecosystem/#drivers) and
[third-party](https://appium.io/docs/en/latest/ecosystem/#other-drivers) drivers at
the current Appium 2.0 documentation.
## Clients
### Plugins
Client libraries enable writing Appium tests in different programming languages. There are
officially-supported clients for Java, Python, Ruby, and .NET C#, as well as third-party clients
for other languages. You can find a full list of clients in
[Appium Ecosystem's Clients page](http://appium.io/docs/en/latest/ecosystem/clients/).
The concept of plugins is something new that has been added exclusively to Appium2. Plugins allow you to extend server functionality without changing the server code. Plugins could be managed similarly to drivers:
## Plugins
Plugins allow you to extend server functionality without changing the server code. The main
difference between drivers and plugins is that the latter must be explicitly enabled on
Appium server startup (all installed drivers are enabled by default):
```bash
# To install an officially supported plugin
appium plugin install images
# To install a plugin from a local folder (useful for developers)
appium plugin install --source=local /Users/me/sources/images
# To install a new plugin from npm
appium plugin install --source=npm appium-device-farm
appium --use-plugins=<plugin-name>
```
# To list already installed plugins
You can find a full list of officially-supported and third-party plugins in
[Appium Ecosystem's Plugins page](http://appium.io/docs/en/latest/ecosystem/plugins/).
Similarly to drivers, plugin management is also done using
[Appium's Extension command-line interface](http://appium.io/docs/en/latest/cli/extensions/):
```bash
# Install an official plugin from npm (see documentation for a list of such plugins)
appium plugin install <plugin-name>
# Install any plugin from npm
appium plugin install --source=npm <plugin-name>
# See documentation for installation from other sources
# List already installed plugins
appium plugin list --installed
# To update a plugins (it must be already installed)
appium plugin update appium-device-farm
# To uninstall a plugin
appium plugin uninstall appium-device-farm
# Update a plugin (it must be already installed)
# This will NOT update the major version, in order to prevent breaking changes
appium plugin update <plugin-name>
# Update a plugin to the most recent version (may include breaking changes)
appium plugin update <plugin-name> --unsafe
# Uninstall a plugin
appium plugin uninstall <plugin-name>
```
The main difference between drivers and plugins is that the latter must be explicitly enabled on server startup after it was installed (drivers are enabled by default after installation):
## Server Command Line Interface
In order to start sending commands to the Appium server, it must be running on the URL and port
where your client library expects it to listen. [Appium's command-line interface](http://appium.io/docs/en/latest/cli/args/)
is used to launch and configure the server:
```bash
appium server --use-plugins=device-farm,images
```
You can find a full list of
[officially-supported](https://appium.io/docs/en/latest/ecosystem/#plugins) and
[third-party](https://appium.io/docs/en/latest/ecosystem/#other-plugins) plugins at
the current Appium 2.0 documentation.
### Server Command Line Interface
In order to start sending commands to Appium over the wire it must be listening
on the URL where your client library expects it to listen.
Use the following commands to run and configure Appium server:
```bash
# Start the server on the default port and host (e.g. http://0.0.0.0:4723/)
# Start the server on the default host (0.0.0.0) and port (4723)
appium server
# Start the server on the given port, host and use the base path prefix (the default prefix is /)
appium server -p 9000 -a 127.0.0.1 -pa /wd/hub
# Get the list of all supported command line parameters.
# This list would also include descriptions of driver-specific
# command line arguments for all installed drivers.
# Each driver and plugin must have their command line arguments
# exposed in a special JSON schema declared as a part of the corresponding
# package.json file.
appium server --help
# You can also omit the 'server' subcommand
appium
# Start the server on the given host, port and use a custom base path prefix (the default prefix is '/')
appium --address 127.0.0.1 --port 9000 --base-path /wd/hub
```
Appium supports execution of parallel server processes as well as parallel driver sessions within
Appium supports execution of parallel server processes, as well as parallel driver sessions within a
single server process. Refer the corresponding driver documentations regarding which mode is optimal
for the particular driver or whether it supports parallel sessions.
### Why Appium?
## Why Appium?
1. You usually don't have to recompile your app or modify it in any way, due
to the use of standard automation APIs on all platforms.
2. You can write tests with your favorite dev tools using any
[WebDriver](https://w3c.github.io/webdriver/webdriver-spec.html)-compatible
language such as [Java](https://github.com/appium/java-client),
[JavaScript](https://webdriver.io/), [Python](https://github.com/appium/python-client),
[Ruby](https://github.com/appium/ruby_lib), [C#](https://github.com/appium/dotnet-client)
with the Selenium WebDriver API. There are also various third party
client implementations for other languages.
1. You usually don't have to recompile your app or modify it in any way, due to the use of standard
automation APIs on all platforms.
2. You can write tests with your favorite dev tools using any WebDriver-compatible language such as
Java, Python, Ruby and C#. There are also third party client implementations for other languages.
3. You can use any testing framework.
4. Some drivers, like xcuitest and uiautomator2 ones have built-in mobile web and
hybrid app support. Within the same script, you can switch seamlessly between native
app automation and webview automation, all using the WebDriver model that's already
the standard for web automation.
5. You can run your automated tests locally and in a cloud. There are multiple
cloud providers that support various Appium drivers (mostly
targeting iOS and Android mobile automation).
6. [Appium Inspector](https://github.com/appium/appium-inspector) allows
visual debugging of automated tests and could be extremely useful for
beginners.
4. Some drivers like `xcuitest` and `uiautomator2` have built-in mobile web and hybrid app support.
Within the same script, you can switch seamlessly between native app automation and webview
automation, all using the WebDriver model that's already the standard for web automation.
5. You can run your automated tests locally and in a cloud. There are multiple cloud providers that
support various Appium drivers (mostly targeting iOS and Android mobile automation).
6. [Appium Inspector](https://github.com/appium/appium-inspector) can be used to visually inspect
the page source of applications across different platforms, facilitating easier test development.
Investing in the
[WebDriver](https://w3c.github.io/webdriver/webdriver-spec.html) protocol means
you are betting on a single, free, and open protocol for testing that has become
a web standard. Don't lock yourself into a proprietary stack.
Investing in the [WebDriver](https://w3c.github.io/webdriver/webdriver-spec.html) protocol means you
are betting on a single, free, and open protocol for testing that has become a web standard. Don't
lock yourself into a proprietary stack.
For example, if you use Apple's XCUITest library without Appium you can only
write tests using Obj-C/Swift, and you can only run tests through Xcode.
Similarly, with Google's UiAutomator or Espresso, you can only write tests in
Java/Kotlin. Appium opens up the possibility of true cross-platform native app
automation, for mobile and beyond. Finally!
For example, if you use Apple's XCUITest library without Appium, you can only write tests using
Obj-C/Swift, and you can only run tests through Xcode. Similarly, with Google's UiAutomator or
Espresso, you can only write tests in Java/Kotlin. Appium opens up the possibility of true
cross-platform native app automation, for mobile and beyond!
If you're new to Appium or want a more comprehensive description of what this is all
about, please read our [Intro to Appium](https://appium.io/docs/en/latest/intro/).
If you are looking for a more comprehensive description of what this is all about, please read our
documentation on [How Does Appium Work?](https://appium.io/docs/en/latest/intro/appium/).
### Quickstart
## License
Check out our [Quickstart](https://appium.io/docs/en/latest/quickstart/) guide
to get going with Appium.
There is also a sample code that contains [many examples of tests in a variety
of different languages](https://github.com/appium/appium/tree/1.x/sample-code)!
### Documentation
For prettily-rendered docs, please visit [Appium Documentation](https://appium.io).
You can always find the full list of Appium doc pages at [Appium's GitHub
Repo](https://github.com/appium/appium/tree/master/packages/appium/docs) as well.
### Contributing
Please take a look at our [contribution documentation](CONTRIBUTING.md)
for instructions on how to build, test, and run Appium from the source.
### Project History, Credits & Inspiration
* [History](https://appium.io/docs/en/latest/intro/history/)
### User Forums
Announcements and debates often take place on the [Discussion Group](https://discuss.appium.io),
be sure to sign up!
### Troubleshooting
Please find the troubleshooting guide at the corresponding driver repository/documentation.
### License
[Apache-2.0](./LICENSE)
[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fappium%2Fappium.svg?type=large)](https://app.fossa.io/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fappium%2Fappium?ref=badge_large)
+21
View File
@@ -196,3 +196,24 @@ Example (run the `reset` script included with the UiAutomator2 driver):
```
appium driver run uiautomator2 reset
```
### `doctor`
Run doctor checks for the given extension if it has any. Extension authors can include
runnable scripts that assist with driver or plugin preconditions validations. These scripts
must be valid Node.js sources, must be listed in the package manifest, and must export
class instances that comply to the particular requirements.
See the [Building Doctor Checks](../developing/build-doctor-checks.md) tutorial for more
details on these requirements.
Usage:
```
appium <ext-type> doctor <ext-name>
```
Example (run doctor checks for the UiAutomator2 driver):
```
appium driver doctor uiautomator2
```
@@ -0,0 +1,141 @@
---
title: Building Doctor Checks
---
The idea of Appium Doctor is to assist users with driver or plugin preconditions setup. Sometimes such
preconditions might be quite complicated and require non-trivial technical knowledge. Doctor checks,
which are vanilla Node.js class instances written by extension authors, simplify
the setup process by automating diagnostics and possible fixes for the found issues. These checks
might also be interactive to ensure better usage experience.
This tutorial is supposed to be used by plugin or driver authors that would like to help their users
to deal with complicated setup or configuration steps.
## Adding Doctor Checks
### Typing Requirements
The term `Doctor Check` literally describes a single javascript class instance that implements the
[IDoctorCheck interface](https://github.com/appium/appium/blob/master/packages/types/lib/doctor.ts).
The interface defines the following methods and properties:
- `diagnose(): Promise<DoctorCheckResult>`: Contains the code to diagnose a possible issue
- `fix(): Promise<string|null>`: Either fixes the actual problem if `hasAutofix()` returns true or
returns a string description for possible manual fixes. If this method throws an exception named
`FixSkippedError` and `hasAutofix()` returns true then the result of the method invocation
is going to be ignored.
- `hasAutofix(): boolean`: Whether calling `fix()` would resolve the found issue
- `isOptional(): boolean`: Whether the found issue can be ignored and is not a showstopper
- `log: AppiumLogger`: May be used for logging. This property may be assigned
by the instance itself or by the Appium server if it is left unassigned.
The `DoctorCheckResult` object returned by the `diagnose()` method must contain the following properties:
- `ok: boolean`: Whether the diagnosis found no issues
- `optional: boolean`: Whether the diagnosed issue is safe to ignore
- `message: string`: The text message describing the diagnostic result
### Manifest Requirements
A single extension may export multiple Doctor checks to Appium. In order for these checks to be properly
picked up by the server CLI after the corresponding extension is installed they might be listed in the
package .json manifest under the `appium.doctor.checks` section similar to the definition below:
```json
// ...
"appium": {
"driverName": "fake",
"automationName": "Fake",
"platformNames": [
"Fake"
],
"mainClass": "FakeDriver",
"schema": "./build/lib/fake-driver-schema.js",
"scripts": {
"fake-error": "./build/lib/scripts/fake-error.js",
"fake-success": "./build/lib/scripts/fake-success.js",
"fake-stdin": "./build/lib/scripts/fake-stdin.js"
},
"doctor": {
"checks": [
"./doctor/fake1.js",
"./doctor/fake2.js"
// ...
]
}
},
// ...
```
Also, it makes sense to include the [@appium/types](https://www.npmjs.com/package/@appium/types) import
to the package dev dependencies.
### Implementation Example
The below example is a "raw" Node.JS implementation that does not use any transpilation:
```js
const {fs, doctor} = require('@appium/support');
/** @satisfies {import('@appium/types').IDoctorCheck} */
class EnvVarAndPathCheck {
/**
* @param {string} varName
*/
constructor(varName) {
this.varName = varName;
}
async diagnose() {
const varValue = process.env[this.varName];
if (typeof varValue === 'undefined') {
return doctor.nok(`${this.varName} environment variable is NOT set!`);
}
if (await fs.exists(varValue)) {
return doctor.ok(`${this.varName} is set to: ${varValue}`);
}
return doctor.nok(`${this.varName} is set to '${varValue}' but this is NOT a valid path!`);
}
async fix() {
return (
`Make sure the environment variable ${this.varName} is properly configured for the Appium server process`
);
}
hasAutofix() {
return false;
}
isOptional() {
return false;
}
}
const androidHomeCheck = new EnvVarAndPathCheck('ANDROID_HOME');
module.exports = {androidHomeCheck};
/**
* @typedef {import('@appium/types').DoctorCheckResult} CheckResult
*/
```
This file could be saved as `doctor/android-home-check.js` and then added to the package.json manifest
as
```json
// ...
"appium": {
// ...
"doctor": {
"checks": [
"./doctor/android-home-check.js",
]
}
// ...
},
// ...
```
@@ -270,7 +270,7 @@ access the `this.cliArgs` and `this.caps` objects explicitly.
In all cases, the `appium:` capability prefix will have been stripped away by the time you are
accessing values here, for convenience.
### Implement WebDriver commands
### Implement WebDriver classic commands
You handle WebDriver commands by implementing functions in your driver class. Each member of the
WebDriver Protocol, plus the various Appium extensions, has a corresponding function that you
@@ -309,6 +309,21 @@ A few notes:
- if your driver doesn't implement a command, users can still try to access the command, and will
get a `501 Not Yet Implemented` response error.
### Implement WebDriver BiDi commands
[WebDriver BiDi](https://w3c.github.io/webdriver-bidi) is a newer version of the WebDriver spec
which is implemented over Websockets instead of HTTP. As an Appium driver author you can take
advantage of Appium's BiDi support without having to know anything about the BiDi protocol or
Websockets. Implementing handlers for BiDi commands works just the same as implementing handlers
for WebDriver classic commands (described in the previous section). You simply define a method on
your driver of the appropriate name, and it will be called when the BiDi command is requested by
the client. To see which specific names you should use for BiDi commands, have a look at
[bidi-commands.js](https://github.com/appium/appium/blob/master/packages/base-driver/lib/protocol/bidi-commands.js)
Currently, you also need to define a `doesSupportBidi` field on your driver instances, and ensure
it is set to `true`. Appium will not turn on its Websocket servers for your driver and set up any
handlers unless your driver says that it supports BiDi in this way.
### Implement element finding
Element finding is a special command implementation case. You don't actually want to override
@@ -540,6 +555,21 @@ proxyActive() {
With those pieces in play, you won't have to reimplement anything that's already implemented by the
remote endpoint you're proxying to. Appium will take care of all the proxying for you.
### Proxy BiDi commands to another BiDi implementation
All of the above about proxying WebDriver commands is conceptually also valid for proxying BiDi
commands specifically. In order to enable BiDi proxying, you need to:
1. Set the `doesSupportBidi` field on your driver instances to `true`.
1. Implement `get bidiProxyUrl` on your driver. This should return a Websocket URL which is the
address of the upstream socket you want BiDi commands to be proxied to.
The intended pattern here is for you to start a session on the upstream implementation, check
whether it has an active BiDi socket in the returned capabilities (e.g., the `webSocketUrl`
capability), and then to set an internal field to that value, so that it can be returned by `get
bidiProxyUrl`. Once all this is in place, Appium will proxy BiDi commands from the client straight
to the upstream connection.
### Extend the existing protocol with new commands
You may find that the existing commands don't cut it for your driver. If you want to expose
@@ -627,6 +657,13 @@ A couple notes about this system:
1. The `executeMethod` helper will reject with an error if a script name doesn't match one of the
script names defined as a command in `executeMethodMap`, or if there are missing parameters.
### Build Appium Doctor checks
Your users can run `appium driver doctor <driverName>` to run installation and health checks. Visit
the [Building Doctor Checks](./build-doctor-checks.md) guide for more information on this
capability.
### Implement handling of Appium settings
Appium users can send parameters to your driver via CLI args as well as via capabilities. But these
@@ -659,6 +696,29 @@ async onSettingsUpdate(key, value) {
}
```
### Emit BiDi events
With the WebDriver BiDi protocol, clients can subscribe to arbitrary events which can be sent
asynchronously to the client over the BiDi socket connection. As an Appium driver author you don't
need to worry about event subscription. If you want to emit an event with a certain method name and
payload, it's as easy as using the built-in event emitter with the `bidiEvent` event.
As an
example, let's say our driver wants to periodically emit CPU load information. We could define an
event called `system.cpu`, and a payload that looks like `{load: 0.97}` to signify 97% CPU usage.
Whenever we want, our driver can simply call the following code (assuming we have the current load
in `this.currentCpuLoad`):
```js
this.eventEmitter.emit('bidiEvent', {
method: 'system.cpu',
params: {load: this.currentCpuLoad},
})
```
Now, if the client has subscribed to the `system.cpu` event, it will be notified with the load
whenever the driver emits it.
### Make itself aware of resources other concurrent drivers are using
Let's say your driver uses up some system resources, like ports. There are a few ways to make sure
@@ -412,6 +412,12 @@ Overriding Execute Methods from drivers works as you'd expect: if your plugin de
Method with the same name as that of a driver, your command (in this case `plugMeIn`) will be
called first. You can choose to run the driver's original behaviour via `next` if you want.
### Build Appium Doctor checks
Your users can run `appium plugin doctor <pluginName>` to run installation and health checks. Visit
the [Building Doctor Checks](./build-doctor-checks.md) guide for more information on this
capability.
### Update the Appium server object
You probably don't normally need to update the Appium server object (which is an
@@ -15,3 +15,4 @@ own Appium extension:
* For creating a driver, see the [Build Drivers](./build-drivers.md) page
* For creating a plugin, take a look at the [Build Plugins](build-plugins.md) page
* Drivers and plugins both need documentation, so check out the [Build Documentation](./build-docs.md) page
* For creating a doctor check, see the [Building Doctor Checks](./build-doctor-checks.md) page
@@ -31,6 +31,7 @@ These clients are not maintained by the Appium team and can be used with other l
|[WebdriverIO](https://webdriver.io/docs/appium)|Node.js|
|[Nightwatch.js](https://nightwatchjs.org/guide/mobile-app-testing/introduction.html)|Node.js|
|[RobotFramework](https://github.com/serhatbolsu/robotframework-appiumlibrary)|DSL|
|[multicatch's appium-client](https://github.com/multicatch/appium-client)|Rust|
In general, any W3C WebDriver spec-compatible client will also integrate well with Appium, though
some Appium-specific commands may not be implemented in other clients.
@@ -28,7 +28,13 @@ appium
You should see some output that starts with a line like this:
```
[Appium] Welcome to Appium v2.0.0
[Appium] Welcome to Appium v2.4.1
```
In order to update Appium using `npm`:
```bash
npm update -g appium
```
That's it! If you see this, the Appium server is up and running. Go ahead and quit
@@ -47,6 +47,11 @@ contents:
[WebdriverIO documentation](https://webdriver.io/docs/gettingstarted) for a fuller explanation
of the various API commands you see and what their purpose is.
!!! note
The sample code is available from [GitHub Appium repository](https://github.com/appium/appium/tree/master/packages/appium/sample-code/quickstarts/js).
Basically, this code is doing the following:
1. Defining a set of "Capabilities" (parameters) to send to the Appium server so Appium knows what
@@ -68,4 +73,3 @@ If all goes well, you'll see the Settings app open up and navigate to the "Batte
app closes again.
Congratulations, you've started your Appium journey! Read on for some [next steps](./next-steps.md) to explore.
@@ -26,11 +26,17 @@ You usually do not need to worry about the prefix.
It's not within the scope of this guide to give a complete run-down on the Python client
library or everything that's happening here, so we'll leave the code itself unexplained in detail for now.
- You may want to read up particularly on Appium [Capabilities](../guides/caps.md).
- [functional test code](https://github.com/appium/python-client/tree/master/test/functional) in Python Client GitHub repository should help to find more working example.
- [Documentation](https://appium.github.io/python-client-sphinx/) also helps to find methods
defined in the Appium Python Client.
!!! note
The sample code is available from [GitHub Appium repository](https://github.com/appium/appium/tree/master/packages/appium/sample-code/quickstarts/py).
Basically, this code is doing the following:
1. Defining a set of "Capabilities" (parameters) to send to the Appium server so Appium knows what
+29 -5
View File
@@ -6,12 +6,32 @@ title: Write a Test (Ruby)
---
The [AppiumLib](https://github.com/appium/ruby_lib) and the [AppiumLibCore](https://github.com/appium/ruby_lib_core) (**recommended**) are official Appium client libraries in Ruby, which are available via gem under the [appium_lib](https://rubygems.org/gems/appium_lib) and the [appium_lib_core](https://rubygems.org/gems/appium_lib_core) package names. The appium_lib_core inherits from the Selenium Ruby Binding, and the appium_lib inherits from the appium_lib_core, so installing these libraries include the selenium binding. We recommend `appium_lib_core` if you need a less complex client-side solution. The `appium_lib` has some useful methods the core does not have, but for the cost of greater complexity.
The [AppiumLib](https://github.com/appium/ruby_lib) and the [AppiumLibCore](https://github.com/appium/ruby_lib_core) (**recommended**) are official Appium client libraries in Ruby, which are available via gem under the [appium_lib](https://rubygems.org/gems/appium_lib) and the [appium_lib_core](https://rubygems.org/gems/appium_lib_core) package names. The appium_lib_core inherits from the Selenium Ruby Binding, and the appium_lib inherits from the appium_lib_core, so installing these libraries include the selenium binding. We recommend `appium_lib_core` if you need a less complex client-side solution. The `appium_lib` has some useful methods the core does not have, but for the cost of greater complexity and historical methods which may not work in the latest environment.
As the first step, let's initialize a Gemfile to manage the dependency:
```bash
gem install appium_lib
bundle init
```
Then, you could add Appium Ruby Client dependency as below:
```bash
bundle add appium_lib_core
# or
gem install appium_lib_core
# bundle add appium_lib
```
Test code example below uses `test-unit` module, thus please run:
```bash
bundle add test-unit
```
Once these steps has done, your `Gemfile` file should include:
```ruby title="Gemfile"
--8<-- "./sample-code/quickstarts/rb/Gemfile"
```
The `appium_lib_core` is the main part as an Appium client.
@@ -21,7 +41,7 @@ The `appium_lib_core` is the main part as an Appium client.
This example is by the `appium_lib_core` with `test-unit` gem module.
Tes code in `appium_lib` should be similar.
```python title="test.py"
```ruby title="test.rb"
--8<-- "./sample-code/quickstarts/rb/test.rb"
```
@@ -34,6 +54,10 @@ Tes code in `appium_lib` should be similar.
- [functional test code](https://github.com/appium/ruby_lib_core/tree/master/test/functional) in the appium_lib_core GitHub repository should help to find more working example.
- Documentation [appium_lib_core](https://www.rubydoc.info/github/appium/ruby_lib_core) and [appium_lib](https://www.rubydoc.info/github/appium/ruby_lib) also helps to find available methods.
!!! note
The sample code is available from [GitHub Appium repository](https://github.com/appium/appium/tree/master/packages/appium/sample-code/quickstarts/rb).
Basically, this code is doing the following:
@@ -49,7 +73,7 @@ running in another terminal session, otherwise you'll get an error about not bei
to one. Then, you can execute the script:
```bash
bundle install
# Please run "bundle install" first if your environment has not run the installation command yet.
bundle exec ruby test.rb
```
+1
View File
@@ -68,6 +68,7 @@ nav:
- developing/build-drivers.md
- developing/build-plugins.md
- developing/build-docs.md
- developing/build-doctor-checks.md
- Contributing:
- contributing/index.md
- contributing/develop.md
+32 -2
View File
@@ -1,6 +1,36 @@
---
title: Appium文档
hide:
- navigation
- toc
title: 欢迎
---
<style>
.md-typeset h1 {
display: none;
}
</style>
<div style="text-align: center">
<img src="assets/images/appium-logo-horiz.png" style="max-width: 400px;" />
</div>
欢迎访问 Appium 文档!Appium 是一个开源项目和相关软件的生态系统。
软件,旨在促进许多应用程序平台的用户界面自动化,包括移动平台(iOS、
Android、Tizen)、浏览器(Chrome、Firefox、Safari)、桌面(macOS、Windows)、电视(Roku、tvOS、
Android TV、三星)等!
TODO
## 探索文档
<div class="grid cards" markdown>
- 查看 [__介绍__](./intro/index.md) 确保您理解关键概念
- 通过 [__快速入门__](./quickstart/index.md) 来进行设置并运行基本的 Android 测试
- 访问 [__生态系统__](./ecosystem/index.md) 页面,查看您可能需要使用的驱动程序、客户端和插件列表
- 参考 [__CLI 参考__](./cli/index.md) 用于从命令行使用 Appium
- 查看 [__命令说明__](./commands/index.md) Appium 和插件公开的命令列表
- 阅读不同的 [__指南__](./guides/migrating-1-to-2.md) 来获取各种说明、技巧和窍门
- 查阅各种第三方的 [__资源__](./resources.md) 来探索网上的Appium运用
- 为了创建您自己的Appium拓展, 请查看 [__开发者__](./developing/index.md) 文档
- 对 Appium 自身的贡献,请查阅 [__贡献__](./contributing/index.md) 页面
</div>
+25 -2
View File
@@ -1,6 +1,29 @@
---
title: 快速开始
hide:
- toc
title: 快速入门介绍
---
让我们开始使用 Appium!为了成功使用本快速入门介绍, 我们建议您
首先阅读 [介绍](../intro/index.md), 以便您了解
运行 Appium 和编写 Appium 脚本所涉及的概念。
TODO
本快速入门课程的基本计划如下:
1. 安装 Appium
1. 安装 Appium 驱动及其依赖项 (我们将使用 [UiAutomator2
driver](https://github.com/appium/appium-uiautomator2-driver) 对于这些例子)
1. 用你选择的语言安装 Appium 客户端库(本指南包含以下选项
JavaScript、Python、Java、Ruby 和 .NET 的选项)。
1. 使用示例应用程序编写并运行简单的 Appium 自动化脚本
### 要求
开始之前,请确保您的系统满足运行 Appium 服务器的
[要求](../quickstart/requirements.md).其他要求
将在安装 UiAutomator2 驱动程序时一并讨论。 本指南还假设
您已熟练掌握您的操作系统平台上的基本命令行功能,例如可以运行命令行命令, 设置
永久的系统变量, 等等...
现在你已经准备好开始了!请前往 [安装 Appium](./install.md)吧.
+37 -2
View File
@@ -1,6 +1,41 @@
---
title: Install Appium
hide:
- toc
title: 安装 Appium
---
!!! 信息
TODO
安装前,请务必检查[系统要求](./requirements.md).
您可以使用 `npm` 在全局范围内安装 Appium:
```bash
npm i -g appium
```
!!! 注意
目前不支持其他软件包管理器。
安装完成后,您应该可以从命令行运行 Appium:
```
appium
```
你应该会看到一些输出结果,开头一行是这样的:
```
[Appium] 欢迎来到 Appium v2.4.1
```
为了更新Appiums使用 `npm`:
```bash
npm update -g appium
```
就是这样!如果你看到这个,说明 Appium 服务器已经启动并运行。按 (Ctrl-C)
继续退出并跳转到到 [下一步](./uiauto2-driver.md), 在这里我们将安装一个用于自动运行 Android 应用程序的驱动程序.
@@ -1,6 +1,23 @@
---
title: Next Steps
hide:
- toc
title: 下一步
---
既然您已经成功配置了您的系统进行Android自动化并运行了一个简单的测试,
您可能希望继续探索这份文档。特别是对于初学者,
以下是一些很好的指南和参考资料:
TODO
- 在 [生态系统](../ecosystem/index.md) 页面: 浏览可用的驱动程序, 客户端, 插件, 和工具
- [管理Appium驱动程序与插件](../guides/managing-exts.md)
- [能力](../guides/caps.md)
- [设置](../guides/settings.md)
您还会发现[Appium 监测者](https://github.com/appium/appium-inspector) 编写 Appium 测试不可或缺的工具。
是编写 Appium 测试不可或缺的工具,因为它可以对应用程序进行可视化检查,并
帮助您发现可用于测试脚本的元素定位器。
您还可以利用为您提供的众多在线 Appium 课程之一。
祝你好运,玩得开心!
+16 -2
View File
@@ -1,6 +1,20 @@
---
title: 更多 Appium 资源
hide:
- navigation
- toc
title: 其他资源
---
在这里,您可以找到网络上其他 Appium 资源的链接:
TODO
## 网站
- [Appium Pro](https://appiumpro.com) - 是Appium其中一名维护者乔纳森-利普斯(Jonathan Lipps)撰写的博客和时事通讯。
撰写的博客和时事通讯,其中包含大量实用指南
## 在线课程
- [Appium 和 Selenium 基础](https://ui.headspin.io/university/learn/appium-selenium-fundamentals-2020) - Jonathan Lipps 编写的学习 Python、Selenium 和 Appium 的综合视频课程
- [使用 Appium 实现移动测试自动化](https://testautomationu.applitools.com/appium-java-tutorial/) - 由 Moataz Nabil 制作的视频课程
- [高级Appoium](https://www.linkedin.com/learning/advanced-appium) - 乔纳森-利普斯(Jonathan Lipps)的视频课程
+387 -1
View File
@@ -1,5 +1,6 @@
/* eslint-disable no-unused-vars */
import _ from 'lodash';
import B from 'bluebird';
import {getBuildInfo, updateBuildInfo, APPIUM_VER} from './config';
import {
BaseDriver,
@@ -9,6 +10,7 @@ import {
CREATE_SESSION_COMMAND,
DELETE_SESSION_COMMAND,
GET_STATUS_COMMAND,
MAX_LOG_BODY_LENGTH,
promoteAppiumOptions,
promoteAppiumOptionsForObject,
} from '@appium/base-driver';
@@ -16,7 +18,8 @@ import AsyncLock from 'async-lock';
import {parseCapsForInnerDriver, pullSettings, makeNonW3cCapsError} from './utils';
import {util, node, logger} from '@appium/support';
import {getDefaultsForExtension} from './schema';
import {DRIVER_TYPE} from './constants';
import {DRIVER_TYPE, BIDI_BASE_PATH, BIDI_EVENT_NAME} from './constants';
import WebSocket from 'ws';
const desiredCapabilityConstraints = /** @type {const} */ ({
automationName: {
@@ -83,6 +86,12 @@ class AppiumDriver extends DriverCore {
/** @type {AppiumServer} */
server;
/** @type {Record<string, import('ws').WebSocket[]>} */
bidiSockets;
/** @type {Record<string, import('ws').WebSocket>} */
bidiProxyClients;
/**
* @type {AppiumDriverConstraints}
* @readonly
@@ -113,6 +122,8 @@ class AppiumDriver extends DriverCore {
this.pluginClasses = new Map();
this.sessionPlugins = {};
this.sessionlessPlugins = [];
this.bidiSockets = {};
this.bidiProxyClients = {};
this.desiredCapConstraints = desiredCapabilityConstraints;
this._isShuttingDown = false;
@@ -241,6 +252,337 @@ class AppiumDriver extends DriverCore {
}
}
/**
* Initialize a new bidi connection and set up handlers
* @param {import('ws').WebSocket} ws The websocket connection object
* @param {import('http').IncomingMessage} req The connection pathname, which might include the session id
*/
onBidiConnection(ws, req) {
// TODO put bidi-related functionality into a mixin/helper class
// wrap all of the handler logic with exception handling so if something blows up we can log
// and close the websocket
try {
const {bidiHandlerDriver, proxyClient, send, sendToProxy, logSocketErr} = this.initBidiSocket(
ws,
req,
);
this.initBidiSocketHandlers(
ws,
proxyClient,
send,
sendToProxy,
bidiHandlerDriver,
logSocketErr,
);
this.initBidiProxyHandlers(proxyClient, ws, send);
this.initBidiEventListeners(ws, bidiHandlerDriver, send);
} catch (err) {
this.log.error(err);
try {
ws.close();
} catch (ign) {}
}
}
/**
* Initialize a new bidi connection
* @param {import('ws').WebSocket} ws The websocket connection object
* @param {import('http').IncomingMessage} req The connection pathname, which might include the session id
*/
initBidiSocket(ws, req) {
let outOfBandErrorPrefix = '';
const pathname = req.url;
if (!pathname) {
throw new Error('Invalid connection request: pathname missing from request');
}
const bidiSessionRe = new RegExp(`${BIDI_BASE_PATH}/([^/]+)$`);
const bidiNoSessionRe = new RegExp(`${BIDI_BASE_PATH}/?$`);
const sessionMatch = bidiSessionRe.exec(pathname);
const noSessionMatch = bidiNoSessionRe.exec(pathname);
if (!sessionMatch && !noSessionMatch) {
throw new Error(
`Got websocket connection for path ${pathname} but didn't know what to do with it. ` +
`Ignoring and will close the connection`,
);
}
// Let's figure out which driver is going to handle this socket connection. It's either going
// to be a driver matching a session id appended to the bidi base path, or this umbrella driver
// (if no session id is included in the bidi connection request)
/** @type {import('@appium/types').ExternalDriver | AppiumDriver} */
let bidiHandlerDriver;
/** @type {import('ws').WebSocket | null} */
let proxyClient = null;
if (sessionMatch) {
// If we found a session id, see if it matches an active session
const sessionId = sessionMatch[1];
bidiHandlerDriver = this.sessions[sessionId];
if (!bidiHandlerDriver) {
// The session ID sent in doesn't match an active session; just ignore this socket
// connection in that case
throw new Error(
`Got bidi connection request for session with id ${sessionId} which is closed ` +
`or does not exist. Closing the socket connection.`,
);
}
const driverName = bidiHandlerDriver.constructor.name;
outOfBandErrorPrefix = `[session ${sessionId}] `;
this.log.info(`Bidi websocket connection made for session ${sessionId}`);
// store this socket connection for later removal on session deletion. theoretically there
// can be multiple sockets per session
if (!this.bidiSockets[sessionId]) {
this.bidiSockets[sessionId] = [];
}
this.bidiSockets[sessionId].push(ws);
const bidiProxyUrl = bidiHandlerDriver.bidiProxyUrl;
if (bidiProxyUrl) {
try {
new URL(bidiProxyUrl);
} catch (ign) {
throw new Error(
`Got request for ${driverName} to proxy bidi connections to upstream socket with ` +
`url ${bidiProxyUrl}, but this was not a valid url`,
);
}
this.log.info(`Bidi connection for ${driverName} will be proxied to ${bidiProxyUrl}`);
proxyClient = new WebSocket(bidiProxyUrl);
this.bidiProxyClients[sessionId] = proxyClient;
}
} else {
this.log.info('Bidi websocket connection made to main server');
// no need to store the socket connection if it's to the main server since it will just
// stay open as long as the server itself is and will close when the server closes.
bidiHandlerDriver = this; // eslint-disable-line @typescript-eslint/no-this-alias
}
const logSocketErr = (/** @type {Error} */ err) =>
this.log.error(`${outOfBandErrorPrefix}${err}`);
// This is a function which wraps the 'send' method on a web socket for two reasons:
// 1. Make it async-await friendly
// 2. Do some logging if there's a send error
const sendFactory = (/** @type {import('ws').WebSocket} */ socket) => {
const socketSend = B.promisify(socket.send, {context: socket});
return async (/** @type {string|Buffer} */ data) => {
try {
await socketSend(data);
} catch (err) {
logSocketErr(err);
}
};
};
// Construct our send method for sending messages to the client
const send = sendFactory(ws);
// Construct a conditional send method for proxying messages from the client to an upstream
// bidi socket server (e.g. on a browser)
const sendToProxy = proxyClient ? sendFactory(proxyClient) : null;
return {bidiHandlerDriver, proxyClient, send, sendToProxy, logSocketErr};
}
/**
* Set up handlers on upstream bidi socket we are proxying to/from
*
* @param {import('ws').WebSocket | null} proxyClient - the websocket connection to/from the
* upstream socket (the one we're proxying to/from)
* @param {import('ws').WebSocket} ws - the websocket connection to/from the client
* @param {(data: string | Buffer) => Promise<void>} send - a method used to send data to the
* client
*/
initBidiProxyHandlers(proxyClient, ws, send) {
// Set up handlers for events that might come from the upstream bidi socket connection if
// we're in proxy mode
if (proxyClient) {
// Here we're receiving a message from the upstream socket server. We want to pass it on to
// the client
proxyClient.on('message', async (/** @type {Buffer|string} */ data) => {
const logData = _.truncate(data.toString('utf8'), {length: MAX_LOG_BODY_LENGTH});
this.log.debug(
`<-- BIDI Received data from proxied bidi socket, sending to client. Data: ${logData}`,
);
await send(data);
});
// If the upstream socket server closes the connection, should close the connection to the
// client as well
proxyClient.on('close', (code, reason) => {
this.log.debug(
`Upstream bidi socket closed connection (code ${code}, reason: '${reason}'). ` +
`Closing proxy connection to client`,
);
ws.close(code, reason);
});
proxyClient.on('error', (err) => {
this.log.error(`Got error on upstream bidi socket connection: ${err}`);
});
}
}
/**
* Set up handlers on the bidi socket connection to the client
*
* @param {import('ws').WebSocket} ws - the websocket connection to/from the client
* @param {import('ws').WebSocket | null} proxyClient - the websocket connection to/from the
* upstream socket (the one we're proxying to/from, if we're proxying)
* @param {(data: string | Buffer) => Promise<void>} send - a method used to send data to the
* client
* @param {((data: string | Buffer) => Promise<void>) | null} sendToProxy - a method used to send data to the
* upstream socket
* @param {import('@appium/types').ExternalDriver | AppiumDriver} bidiHandlerDriver - the driver
* handling the bidi commands
* @param {(err: Error) => void} logSocketErr - a special prefixed logger
*/
initBidiSocketHandlers(ws, proxyClient, send, sendToProxy, bidiHandlerDriver, logSocketErr) {
ws.on('error', (err) => {
// Can't do much with random errors on the connection other than log them
logSocketErr(err);
});
ws.on('open', () => {
this.log.info('Bidi websocket connection is now open');
});
// Now set up handlers for the various events that might happen on the websocket connection
// coming from the client
// First is incoming messages from the client
ws.on('message', async (/** @type {Buffer} */ data) => {
if (proxyClient) {
const logData = _.truncate(data.toString('utf8'), {length: MAX_LOG_BODY_LENGTH});
this.log.debug(
`--> BIDI Received data from client, sending to upstream bidi socket. Data: ${logData}`,
);
// if we're meant to proxy to an upstream bidi socket, just do that
// @ts-ignore sendToProxy is never null if proxyClient is truthy, but ts doesn't know
// that
await sendToProxy(data.toString('utf8'));
} else {
const res = await this.onBidiMessage(data, bidiHandlerDriver);
await send(JSON.stringify(res));
}
});
// Next consider if the client closes the socket connection on us
ws.on('close', (code, reason) => {
// Not sure if we need to do anything here if the client closes the websocket connection.
// Probably if a session was started via the socket, and the socket closes, we should end the
// associated session to free up resources. But otherwise, for sockets attached to existing
// sessions, doing nothing is probably right.
this.log.debug(`Bidi socket connection closed (code ${code}, reason: '${reason}')`);
// If we're proxying, might as well close the upstream connection and clean it up
if (proxyClient) {
this.log.debug('Also closing bidi proxy socket connection');
proxyClient.close(code, reason);
}
});
}
/**
* Set up bidi event listeners
*
* @param {import('ws').WebSocket} ws - the websocket connection to/from the client
* @param {import('@appium/types').ExternalDriver | AppiumDriver} bidiHandlerDriver - the driver
* handling the bidi commands
* @param {(data: string | Buffer) => Promise<void>} send - a method used to send data to the
* client
*/
initBidiEventListeners(ws, bidiHandlerDriver, send) {
// If the driver emits a bidi event that should maybe get sent to the client, check to make
// sure the client is subscribed and then pass it on
let eventListener = async ({context, method, params}) => {
// if the driver didn't specify a context, use the empty context
if (!context) {
context = '';
}
if (!method || !params) {
throw new Error(
`Driver emitted a bidi event that was malformed. Require method and params keys ` +
`(with optional context). But instead received: ${JSON.stringify({
context,
method,
params,
})}`,
);
}
if (ws.readyState !== WebSocket.OPEN) {
// if the websocket is not still 'open', then we can ignore sending these events
if (ws.readyState > WebSocket.OPEN) {
// if the websocket is closed or closing, we can remove this listener as well to avoid
// leaks
bidiHandlerDriver.eventEmitter.removeListener(BIDI_EVENT_NAME, eventListener);
}
return;
}
if (bidiHandlerDriver.bidiEventSubs[method]?.includes(context)) {
this.log.info(
`<-- BIDI EVENT ${method} (context: '${context}', params: ${JSON.stringify(params)})`,
);
// now we can send the event onto the socket
const ev = {type: 'event', context, method, params};
await send(JSON.stringify(ev));
}
};
bidiHandlerDriver.eventEmitter.on(BIDI_EVENT_NAME, eventListener);
}
/**
* @param {Buffer} data
* @param {ExternalDriver | AppiumDriver} driver
*/
async onBidiMessage(data, driver) {
let resMessage, id, method, params;
const dataTruncated = _.truncate(data.toString(), {length: 100});
try {
try {
({id, method, params} = JSON.parse(data.toString('utf8')));
} catch (err) {
throw new errors.InvalidArgumentError(
`Could not parse Bidi command '${dataTruncated}': ${err.message}`,
);
}
driver.log.info(`--> BIDI message #${id}`);
if (!method) {
throw new errors.InvalidArgumentError(
`Missing method for BiDi operation in '${dataTruncated}'`,
);
}
if (!params) {
throw new errors.InvalidArgumentError(
`Missing params for BiDi operation in '${dataTruncated}`,
);
}
const result = await driver.executeBidiCommand(method, params);
// https://w3c.github.io/webdriver-bidi/#protocol-definition
resMessage = {
id,
type: 'success',
result,
};
} catch (err) {
resMessage = err.bidiErrObject(id);
}
driver.log.info(`<-- BIDI message #${id}`);
return resMessage;
}
/**
* Log a bidi server error
* @param {Error} err
*/
onBidiServerError(err) {
this.log.error(`Error from bidi websocket server: ${err}`);
}
/**
* Create a new session
* @param {W3CAppiumDriverCaps} jsonwpCaps JSONWP formatted desired capabilities
@@ -410,6 +752,21 @@ class AppiumDriver extends DriverCore {
);
await driverInstance.updateSettings(jwpSettings);
}
// if the user has asked for bidi support, and the driver says that it supports bidi,
// send our bidi url back to the user. The inner driver will need to have already saved any
// internal bidi urls it might want to proxy to, cause we are going to overwrite that
// information here!
if (dCaps.webSocketUrl && driverInstance.doesSupportBidi) {
const {address, port, basePath} = this.args;
const isUsingSsl = this.args.sslCertificatePath && this.args.sslKeyPath;
const scheme = isUsingSsl ? 'wss' : 'ws';
const bidiUrl = `${scheme}://${address}:${port}${basePath}${BIDI_BASE_PATH}/${innerSessionId}`;
// @ts-ignore webSocketUrl gets sent by the client as a boolean, but then it is supposed
// to come back from the server as a string. TODO figure out how to express this in our
// capability constraint system
dCaps.webSocketUrl = bidiUrl;
}
} catch (error) {
return {
protocol,
@@ -515,6 +872,9 @@ class AppiumDriver extends DriverCore {
// be in otherwise
delete this.sessions[sessionId];
delete this.sessionPlugins[sessionId];
this.cleanupBidiSockets(sessionId);
return dstSession;
});
// this may not be correct, but if `dstSession` was falsy, the call to `deleteSession()` would
@@ -535,6 +895,32 @@ 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) {
+27 -2
View File
@@ -7,6 +7,7 @@ import {
EXT_SUBCOMMAND_RUN,
EXT_SUBCOMMAND_UNINSTALL,
EXT_SUBCOMMAND_UPDATE,
EXT_SUBCOMMAND_DOCTOR,
} from '../constants';
import {INSTALL_TYPES} from '../extension/extension-config';
import {toParserArgs} from '../schema/cli-args';
@@ -49,6 +50,7 @@ const getExtensionArgs = _.memoize(function getExtensionArgs() {
[EXT_SUBCOMMAND_UNINSTALL]: makeUninstallArgs(type),
[EXT_SUBCOMMAND_UPDATE]: makeUpdateArgs(type),
[EXT_SUBCOMMAND_RUN]: makeRunArgs(type),
[EXT_SUBCOMMAND_DOCTOR]: makeDoctorArgs(type),
};
}
return /** @type {Record<ExtensionType, Record<import('appium/types').CliExtensionSubcommand,ArgumentDefinitions>>} */ (
@@ -156,7 +158,28 @@ function makeUninstallArgs(type) {
{
type: 'str',
help:
'Name of the driver to uninstall, for example: ' + type === DRIVER_TYPE
`Name of the ${type} to uninstall, for example: ` + type === DRIVER_TYPE
? DRIVER_EXAMPLE
: PLUGIN_EXAMPLE,
},
],
]);
}
/**
* Makes the opts for the `doctor` subcommand for each extension type
* @param {ExtensionType} type
* @returns {ArgumentDefinitions}
*/
function makeDoctorArgs(type) {
return new Map([
...globalExtensionArgs,
[
[type],
{
type: 'str',
help:
`Name of the ${type} to run doctor checks for, for example: ` + type === DRIVER_TYPE
? DRIVER_EXAMPLE
: PLUGIN_EXAMPLE,
},
@@ -222,10 +245,12 @@ function makeRunArgs(type) {
['scriptName'],
{
default: null,
nargs: '?',
type: 'str',
help:
`Name of the script to run from the ${type}. The script name must be a key ` +
`inside the "appium.scripts" field inside the ${type}'s "package.json" file`,
`inside the "appium.scripts" field inside the ${type}'s "package.json" file. ` +
`If not provided then all available script names are going to be listed.`,
},
],
]);
+19
View File
@@ -66,6 +66,19 @@ export default class DriverCliCommand extends ExtensionCliCommand {
});
}
/**
* Runs doctor checks for the given driver.
*
* @param {DriverDoctorOptions} opts
* @returns {Promise<number>} The amount of executed doctor checks.
* @throws {Error} If any of the mandatory Doctor checks fails.
*/
async doctor({driver}) {
return await super._doctor({
installSpec: driver,
});
}
/**
*
* @param {import('./extension-command').ExtensionArgs} opts
@@ -151,3 +164,9 @@ export default class DriverCliCommand extends ExtensionCliCommand {
* @property {string} scriptName - name of the script to run
* @property {string[]} [extraArgs] - arguments to pass to the script
*/
/**
* Options for {@linkcode DriverCliCommand.doctor}.
* @typedef DriverDoctorOptions
* @property {string} driver - name of the driver to run doctor checks for
*/
+157 -2
View File
@@ -2,7 +2,7 @@
import B from 'bluebird';
import _ from 'lodash';
import path from 'path';
import {npm, util, env, console} from '@appium/support';
import {npm, util, env, console, fs, system} from '@appium/support';
import {spinWith, RingBuffer} from './utils';
import {
INSTALL_TYPE_NPM,
@@ -15,6 +15,10 @@ import {SubProcess} from 'teen_process';
import {packageDidChange} from '../extension/package-changed';
import {spawn} from 'child_process';
import {inspect} from 'node:util';
import {pathToFileURL} from 'url';
import {Doctor, EXIT_CODE as DOCTOR_EXIT_CODE} from '../doctor/doctor';
import {npmPackage} from '../utils';
import semver from 'semver';
const UPDATE_ALL = 'installed';
@@ -31,6 +35,23 @@ function receiptToManifest(receipt) {
return /** @type {ExtManifest<ExtType>} */ (_.omit(receipt, 'driverName', 'pluginName'));
}
/**
* Fetches the remote extension version requirements
*
* @param {string} pkgName Extension name
* @param {string} [pkgVer] Extension version (if not provided then the latest is assumed)
* @returns {Promise<[string, string|null]>}
*/
async function getRemoteExtensionVersionReq(pkgName, pkgVer) {
const allDeps = await npm.getPackageInfo(
`${pkgName}${pkgVer ? `@${pkgVer}` : ``}`,
['peerDependencies', 'dependencies']
);
const requiredVersionPair = _.flatMap(_.values(allDeps).map(_.toPairs))
.find(([name]) => name === 'appium');
return [npmPackage.version, requiredVersionPair ? requiredVersionPair[1] : null];
}
/**
* @template {ExtensionType} ExtType
*/
@@ -223,6 +244,29 @@ class ExtensionCliCommand {
return listData;
}
/**
* Checks whether the given extension is compatible with the currently installed server
*
* @param {InstallViaNpmArgs} installViaNpmOpts
* @returns {Promise<void>}
*/
async _checkInstallCompatibility({installSpec, pkgName, pkgVer, installType}) {
if (INSTALL_TYPE_NPM !== installType) {
return;
}
await spinWith(this.isJsonOutput, `Checking if '${pkgName}' is compatible`, async () => {
const [serverVersion, extVersionRequirement] = await getRemoteExtensionVersionReq(pkgName, pkgVer);
if (serverVersion && extVersionRequirement && !semver.satisfies(serverVersion, extVersionRequirement)) {
throw this._createFatalError(
`'${installSpec}' cannot be installed because the server version it requires (${extVersionRequirement}) ` +
`does not meet the currently installed one (${serverVersion}). Please install ` +
`a compatible server version first.`
);
}
});
}
/**
* Install an extension
*
@@ -332,6 +376,8 @@ class ExtensionCliCommand {
);
}
await this._checkInstallCompatibility(installViaNpmOpts);
receipt = await this.installViaNpm(installViaNpmOpts);
// this _should_ be the same as `probablyExtName` as the one derived above unless
@@ -695,6 +741,90 @@ class ExtensionCliCommand {
});
}
/**
* Runs doctor checks for the given extension.
*
* @param {DoctorOptions} opts
* @returns {Promise<number>} The amount of Doctor checks that were
* successfully loaded and executed for the given extension
* @throws {Error} If any of the mandatory Doctor checks fails.
*/
async _doctor({installSpec}) {
if (!this.config.isInstalled(installSpec)) {
throw this._createFatalError(`The ${this.type} "${installSpec}" is not installed`);
}
const moduleRoot = this.config.getInstallPath(installSpec);
const packageJsonPath = path.join(moduleRoot, 'package.json');
if (!await fs.exists(packageJsonPath)) {
throw this._createFatalError(
`No package.json could be found for "${installSpec}" ${this.type}`
);
}
let doctorSpec;
try {
doctorSpec = JSON.parse(await fs.readFile(packageJsonPath, 'utf8')).appium?.doctor;
} catch (e) {
throw this._createFatalError(
`The manifest at '${packageJsonPath}' cannot be parsed: ${e.message}`
);
}
if (!doctorSpec) {
this.log.info(`The ${this.type} "${installSpec}" does not export any doctor checks`);
return 0;
}
if (!_.isPlainObject(doctorSpec) || !_.isArray(doctorSpec.checks)) {
throw this._createFatalError(
`The 'doctor' entry in the package manifest '${packageJsonPath}' must be a proper object ` +
`containing the 'checks' key with the array of script paths`
);
}
const paths = doctorSpec.checks.map((/** @type {string} */ p) => {
const scriptPath = path.resolve(moduleRoot, p);
if (!path.normalize(scriptPath).startsWith(path.normalize(moduleRoot))) {
this.log.error(
`The doctor check script '${p}' from the package manifest '${packageJsonPath}' must be located ` +
`in the '${moduleRoot}' root folder. It will be skipped`
);
return null;
}
return scriptPath;
}).filter(Boolean);
/** @type {Promise[]} */
const loadChecksPromises = [];
for (const p of paths) {
const promise = (async () => {
// https://github.com/nodejs/node/issues/31710
const scriptPath = system.isWindows() ? pathToFileURL(p).href : p;
try {
return await import(scriptPath);
} catch (e) {
this.log.warn(`Unable to load doctor checks from '${p}': ${e.message}`);
}
})();
loadChecksPromises.push(promise);
}
const isDoctorCheck = (/** @type {any} */ x) =>
['diagnose', 'fix', 'hasAutofix', 'isOptional'].every((method) => _.isFunction(x?.[method]));
/** @type {import('@appium/types').IDoctorCheck[]} */
const checks = _.flatMap((await B.all(loadChecksPromises)).filter(Boolean).map(_.toPairs))
.map(([, value]) => value)
.filter(isDoctorCheck);
if (_.isEmpty(checks)) {
this.log.info(`The ${this.type} "${installSpec}" exports no valid doctor checks`);
return 0;
}
this.log.debug(
`Running ${util.pluralize('doctor check', checks.length, true)} ` +
`for the "${installSpec}" ${this.type}`
);
const exitCode = await new Doctor(checks).run();
if (exitCode !== DOCTOR_EXIT_CODE.SUCCESS) {
throw this._createFatalError('Treatment required');
}
return checks.length;
}
/**
* Runs a script cached inside the `scripts` field under `appium`
* inside of the extension's `package.json` file. Will throw
@@ -729,6 +859,24 @@ class ExtensionCliCommand {
);
}
if (!scriptName) {
const allScripts = _.toPairs(extScripts);
const root = this.config.getInstallPath(installSpec);
const existingScripts = await B.filter(
allScripts,
async ([, p]) => await fs.exists(path.join(root, p))
);
if (_.isEmpty(existingScripts)) {
this.log.info(`The ${this.type} named '${installSpec}' does not contain any scripts`);
} else {
this.log.info(`The ${this.type} named '${installSpec}' contains ` +
`${util.pluralize('script', existingScripts.length, true)}:`);
existingScripts.forEach(([name]) => this.log.info(` - ${name}`));
}
this.log.ok(`Successfully retrieved the list of scripts`.green);
return {};
}
if (!(scriptName in /** @type {Record<string,string>} */ (extScripts))) {
throw this._createFatalError(
`The ${this.type} named '${installSpec}' does not support the script: '${scriptName}'`
@@ -874,11 +1022,18 @@ export {ExtensionCliCommand as ExtensionCommand};
* Options for {@linkcode ExtensionCliCommand._run}.
* @typedef RunOptions
* @property {string} installSpec - name of the extension to run a script from
* @property {string} scriptName - name of the script to run
* @property {string} [scriptName] - name of the script to run. If not provided
* then all available script names will be printed
* @property {string[]} [extraArgs] - arguments to pass to the script
* @property {boolean} [bufferOutput] - if true, will buffer the output of the script and return it
*/
/**
* Options for {@linkcode ExtensionCliCommand.doctor}.
* @typedef DoctorOptions
* @property {string} installSpec - name of the extension to run doctor checks for
*/
/**
* Return value of {@linkcode ExtensionCliCommand._run}
*
+22 -8
View File
@@ -2,7 +2,17 @@ import {fs} from '@appium/support';
import {ArgumentParser} from 'argparse';
import _ from 'lodash';
import path from 'path';
import {DRIVER_TYPE, PLUGIN_TYPE, SERVER_SUBCOMMAND} from '../constants';
import {
DRIVER_TYPE,
EXT_SUBCOMMAND_DOCTOR,
EXT_SUBCOMMAND_INSTALL,
EXT_SUBCOMMAND_LIST,
EXT_SUBCOMMAND_RUN,
EXT_SUBCOMMAND_UNINSTALL,
EXT_SUBCOMMAND_UPDATE,
PLUGIN_TYPE,
SERVER_SUBCOMMAND
} from '../constants';
import {finalizeSchema, getArgSpec, hasArgSpec} from '../schema';
import {rootDir} from '../config';
import {getExtensionArgs, getServerArgs} from './args';
@@ -191,8 +201,7 @@ class ArgParser {
const serverArgs = getServerArgs();
for (const [flagsOrNames, opts] of serverArgs) {
// TS doesn't like the spread operator here.
// @ts-ignore
// @ts-ignore TS doesn't like the spread operator here.
serverParser.add_argument(...flagsOrNames, {...opts});
}
@@ -221,33 +230,38 @@ class ArgParser {
*/
const parserSpecs = [
{
command: 'list',
command: EXT_SUBCOMMAND_LIST,
args: extensionArgs[type].list,
help: `List available and installed ${type}s`,
aliases: ['ls'],
},
{
command: 'install',
command: EXT_SUBCOMMAND_INSTALL,
args: extensionArgs[type].install,
help: `Install a ${type}`,
},
{
command: 'uninstall',
command: EXT_SUBCOMMAND_UNINSTALL,
args: extensionArgs[type].uninstall,
help: `Uninstall a ${type}`,
},
{
command: 'update',
command: EXT_SUBCOMMAND_UPDATE,
args: extensionArgs[type].update,
help: `Update installed ${type}s to the latest version`,
},
{
command: 'run',
command: EXT_SUBCOMMAND_RUN,
args: extensionArgs[type].run,
help:
`Run a script (defined inside the ${type}'s package.json under the ` +
`“scripts” field inside the “appium” field) from an installed ${type}`,
},
{
command: EXT_SUBCOMMAND_DOCTOR,
args: extensionArgs[type].doctor,
help: `Run doctor checks (if any defined) for the given ${type}`,
},
];
for (const {command, args, help, aliases} of parserSpecs) {
+19
View File
@@ -65,6 +65,19 @@ export default class PluginCliCommand extends ExtensionCliCommand {
});
}
/**
* Runs doctor checks for the given plugin
*
* @param {PluginDoctorOptions} opts
* @returns {Promise<number>} The amount of executed doctor checks.
* @throws {Error} If any of the mandatory Doctor checks fails.
*/
async doctor({plugin}) {
return await super._doctor({
installSpec: plugin,
});
}
/**
*
* @param {import('./extension-command').ExtensionArgs} opts
@@ -141,3 +154,9 @@ export default class PluginCliCommand extends ExtensionCliCommand {
* @property {string} scriptName - name of the script to run
* @property {string[]} [extraArgs] - arguments to pass to the script
*/
/**
* Options for {@linkcode PluginCliCommand.doctor}.
* @typedef PluginDoctorOptions
* @property {string} plugin - name of the plugin to run doctor checks for
*/
+5 -7
View File
@@ -2,14 +2,14 @@
import ora from 'ora';
const JSON_SPACES = 4;
export const JSON_SPACES = 4;
/***
* Log an error to the console and exit the process.
* @param {boolean} json - whether we should log json or text
* @param {any} msg - error message, object, Error instance, etc.
*/
function errAndQuit(json, msg) {
export function errAndQuit(json, msg) {
if (json) {
console.log(JSON.stringify({error: `${msg}`}, null, JSON_SPACES));
} else {
@@ -26,7 +26,7 @@ function errAndQuit(json, msg) {
* @param {boolean} json - whether we are in json mode (and should therefore not log)
* @param {string} msg - string to log
*/
function log(json, msg) {
export function log(json, msg) {
!json && console.log(msg);
}
@@ -36,7 +36,7 @@ function log(json, msg) {
* @param {string} msg - string to log
* @param {function} fn - function to wrap with spinning
*/
async function spinWith(json, msg, fn) {
export async function spinWith(json, msg, fn) {
if (json) {
return await fn();
}
@@ -52,7 +52,7 @@ async function spinWith(json, msg, fn) {
}
}
class RingBuffer {
export class RingBuffer {
constructor(size = 50) {
this.size = size;
this.buffer = [];
@@ -70,5 +70,3 @@ class RingBuffer {
this.buffer.push(item);
}
}
export {errAndQuit, log, spinWith, JSON_SPACES, RingBuffer};
+18 -24
View File
@@ -4,17 +4,14 @@ import {system, fs} from '@appium/support';
import axios from 'axios';
import {exec} from 'teen_process';
import semver from 'semver';
import findUp from 'find-up';
import os from 'node:os';
import {npmPackage} from './utils';
import {getDefaultsForSchema, getAllArgSpecs} from './schema/schema';
const npmPackage = fs.readPackageJsonFrom(__dirname);
const APPIUM_VER = npmPackage.version;
export const APPIUM_VER = npmPackage.version;
const ENGINES = /** @type {Record<string,string>} */ (npmPackage.engines);
const MIN_NODE_VERSION = ENGINES.node;
const GIT_META_ROOT = '.git';
const GIT_BINARY = `git${system.isWindows() ? '.exe' : ''}`;
const GITHUB_API = 'https://api.github.com/repos/appium/appium';
@@ -44,27 +41,25 @@ async function updateBuildInfo(useGithubApiFallback = false) {
}
}
/**
* Finds the Git metadata dir (see `GIT_META_ROOT`)
*
* This is needed because Appium cannot assume `package.json` and `.git` are in the same
* directory. Monorepos, see?
* @returns {Promise<string|undefined>} Path to dir or `undefined` if not found
*/
async function findGitRoot() {
return await findUp(GIT_META_ROOT, {cwd: rootDir, type: 'directory'});
}
/** @type {() => Promise<string?>} */
const getFullGitPath = _.memoize(async function getFullGitPath() {
try {
return await fs.which(GIT_BINARY);
} catch {
return null;
}
});
/**
* @param {boolean} [useGithubApiFallback]
* @returns {Promise<string?>}
*/
async function getGitRev(useGithubApiFallback = false) {
const gitRoot = await findGitRoot();
if (gitRoot) {
const fullGitPath = await getFullGitPath();
if (fullGitPath) {
try {
const {stdout} = await exec(GIT_BINARY, ['rev-parse', 'HEAD'], {
cwd: gitRoot,
const {stdout} = await exec(fullGitPath, ['rev-parse', 'HEAD'], {
cwd: __dirname,
});
return stdout.trim();
} catch (ign) {}
@@ -94,11 +89,11 @@ async function getGitRev(useGithubApiFallback = false) {
* @returns {Promise<string?>}
*/
async function getGitTimestamp(commitSha, useGithubApiFallback = false) {
const gitRoot = await findGitRoot();
if (gitRoot) {
const fullGitPath = await getFullGitPath();
if (fullGitPath) {
try {
const {stdout} = await exec(GIT_BINARY, ['show', '-s', '--format=%ci', commitSha], {
cwd: gitRoot,
const {stdout} = await exec(fullGitPath, ['show', '-s', '--format=%ci', commitSha], {
cwd: __dirname,
});
return stdout.trim();
} catch (ign) {}
@@ -321,7 +316,6 @@ export {
showBuildInfo,
getNonDefaultServerArgs,
getGitRev,
APPIUM_VER,
updateBuildInfo,
showConfig,
rootDir,
+12
View File
@@ -64,6 +64,7 @@ export const EXT_SUBCOMMAND_INSTALL = 'install';
export const EXT_SUBCOMMAND_UNINSTALL = 'uninstall';
export const EXT_SUBCOMMAND_UPDATE = 'update';
export const EXT_SUBCOMMAND_RUN = 'run';
export const EXT_SUBCOMMAND_DOCTOR = 'doctor';
/**
* Current revision of the manifest (`extensions.yaml`) schema
@@ -77,3 +78,14 @@ export const CURRENT_SCHEMA_REV = 4;
* memory usage, perf, and/or log output, and higher limits may be difficult to scan.
*/
export const LONG_STACKTRACE_LIMIT = 100;
/**
* Where should the bidi websocket handler live on the server?
*/
export const BIDI_BASE_PATH = '/bidi';
/**
* The name of the event for drivers to emit when they want to send bidi events to a client over
* a bidi socket
*/
export const BIDI_EVENT_NAME = 'bidiEvent';
+230
View File
@@ -0,0 +1,230 @@
import '@colors/colors';
import _ from 'lodash';
import { util, doctor, logger } from '@appium/support';
export const EXIT_CODE = /** @type {const} */ Object.freeze({
SUCCESS: 0,
HAS_MAJOR_ISSUES: 127,
});
export class Doctor {
/**
* @param {DoctorCheck[]} [checks=[]]
*/
constructor(checks = []) {
this.log = logger.getLogger('Doctor');
/** @type {DoctorCheck[]} */
this.checks = checks;
this.checks
.filter((c) => _.isNil(c.log))
.forEach((c) => { c.log = this.log; });
/** @type {DoctorIssue[]} */
this.foundIssues = [];
}
/**
* @returns {DoctorIssue[]}
*/
get issuesRequiredToFix() {
return this.foundIssues.filter((f) => !f.check.isOptional());
}
/**
* @returns {DoctorIssue[]}
*/
get issuesOptionalToFix() {
return this.foundIssues.filter((f) => f.check.isOptional());
}
/**
* The doctor shows the report
*/
async diagnose() {
this.log.info(`### Starting doctor diagnostics ###`);
this.foundIssues = [];
for (const check of this.checks) {
const res = await check.diagnose();
const issue = this.toIssue(res, check);
if (issue) {
this.foundIssues.push(issue);
}
}
this.log.info(
`### Diagnostic completed, ${this.buildFixMessage()}. ###`
);
this.log.info('');
}
/**
* @returns {Promise<boolean>}
*/
async reportManualIssues() {
const manualIssues = _.filter(this.issuesRequiredToFix, (f) => !f.check.hasAutofix());
const manualIssuesOptional = _.filter(this.issuesOptionalToFix, (f) => !f.check.hasAutofix());
const handleIssues = async (headerLogs, issues) => {
if (_.isEmpty(issues)) {
return;
}
for (const logMsg of headerLogs) {
this.log.info(logMsg);
}
/** @type {string[]} */
const fixMessages = [];
for (const issue of issues) {
let message;
try {
message = await issue.check.fix();
} catch (e) {
message = e.message;
}
if (message) {
fixMessages.push(message);
}
}
for (const m of _.uniq(fixMessages)) {
this.log.warn(` \u279C ${m}`);
}
this.log.info('');
};
await handleIssues([
'### Manual Fixes Needed ###',
'The configuration cannot be automatically fixed, please do the following first:',
], manualIssues);
await handleIssues([
'### Optional Manual Fixes ###',
'To fix these optional issues, please do the following manually:',
], manualIssuesOptional);
if (manualIssues.length > 0) {
this.log.info('###');
this.log.info('');
this.log.info('Bye! Run doctor again when all manual fixes have been applied!');
this.log.info('');
return true;
}
return false;
}
/**
* @param {DoctorIssue} f
*/
async runAutoFix(f) {
this.log.info(`### Fixing: ${f.error} ###`);
try {
await f.check.fix();
} catch (err) {
if (err.constructor.name === doctor.FixSkippedError.name) {
this.log.info(`### Skipped fix ###`);
return;
} else {
this.log.warn(`${err}`.replace(/\n$/g, ''));
this.log.info(`### Fix did not succeed ###`);
return;
}
}
this.log.info('Checking if this was fixed:');
const res = await f.check.diagnose();
if (res.ok) {
f.fixed = true;
this.log.info(` ${'\u2714'.green} ${res.message}`);
this.log.info(`### Fix was successfully applied ###`);
} else {
this.log.info(` ${'\u2716'.red} ${res.message}`);
this.log.info(`### Fix was applied but issue remains ###`);
}
}
/**
* @returns {Promise<boolean>}
*/
async runAutoFixes() {
const autoFixes = _.filter(this.foundIssues, (f) => f.check.hasAutofix());
for (const f of autoFixes) {
await this.runAutoFix(f);
this.log.info('');
}
if (_.find(autoFixes, (f) => !f.fixed)) {
// a few issues remain.
this.log.info('Bye! A few issues remain, fix manually and/or rerun doctor!');
this.log.info('');
return false;
}
// nothing left to fix.
this.log.info('Bye! All issues have been fixed!');
this.log.info('');
return true;
}
/**
* @returns {Promise<EXIT_CODE[keyof EXIT_CODE]>}
*/
async run() {
await this.diagnose();
if (this.reportSuccess()) {
return EXIT_CODE.SUCCESS;
}
if (await this.reportManualIssues()) {
return EXIT_CODE.HAS_MAJOR_ISSUES;
}
if (!await this.runAutoFixes()) {
return EXIT_CODE.HAS_MAJOR_ISSUES;
}
return EXIT_CODE.SUCCESS;
}
/**
* @param {DoctorCheckResult} result
* @param {DoctorCheck} check
* @returns {DoctorIssue?}
*/
toIssue(result, check) {
if (result.ok) {
this.log.info(` ${'\u2714'.green} ${result.message}`);
return null;
}
const errorMessage = result.optional
? ` ${'\u2716'.yellow} ${result.message}`
: ` ${'\u2716'.red} ${result.message}`;
this.log.warn(errorMessage);
return {
error: errorMessage,
check,
};
}
/**
* @returns {string}
*/
buildFixMessage() {
return `${util.pluralize('required fix', this.issuesRequiredToFix.length, true)} needed, ` +
`${util.pluralize('optional fix', this.issuesOptionalToFix.length, true)} possible`;
}
/**
* @returns {boolean}
*/
reportSuccess() {
if (this.issuesRequiredToFix.length === 0 && this.issuesOptionalToFix.length === 0) {
this.log.info('Everything looks good, bye!');
this.log.info('');
return true;
}
return false;
}
}
/**
* @typedef DoctorIssue
* @property {DoctorCheck} check
* @property {string} error
* @property {boolean} [fixed]
*/
/**
* @typedef {import('@appium/types').IDoctorCheck} DoctorCheck
* @typedef {import('@appium/types').DoctorCheckResult} DoctorCheckResult
*/
@@ -311,7 +311,9 @@ export class ExtensionConfig {
const invalidFieldsText = invalidFields.map((field) => `"${field}"`).join(', ');
warnings.push(
`${extTypeText} "${extName}" (package \`${pkgName}\`) has ${invalidFieldsEnumerationText} (${invalidFieldsText}) in \`extensions.yaml\`; this may cause upgrades done via the \`appium\` CLI tool to fail. Please reinstall with \`appium ${this.extensionType} uninstall ${extName}\` and \`appium ${this.extensionType} install ${extName}\` to attempt a fix.`
`${extTypeText} "${extName}" (package \`${pkgName}\`) has ${invalidFieldsEnumerationText} (${invalidFieldsText}) in \`extensions.yaml\`; ` +
`this may cause upgrades done via the \`appium\` CLI tool to fail. Please reinstall with \`appium ${this.extensionType} uninstall ` +
`${extName}\` and \`appium ${this.extensionType} install ${extName}\` to attempt a fix.`
);
}
@@ -331,16 +333,16 @@ export class ExtensionConfig {
);
if (extListData?.installed) {
const {updateVersion, upToDate} = extListData;
if (!upToDate) {
if (!upToDate && updateVersion) {
warnings.push(
createPeerWarning(
`its peer dependency on older Appium v${appiumVersion}. Please upgrade \`${pkgName}\` to v${updateVersion} or newer.`
`its peer dependency on Appium ${appiumVersion}. Try to upgrade \`${pkgName}\` to v${updateVersion} or newer.`
)
);
} else {
warnings.push(
createPeerWarning(
`its peer dependency on older Appium v${appiumVersion}. Please ask the developer of \`${pkgName}\` to update the peer dependency on Appium to v${APPIUM_VER}.`
`its peer dependency on Appium ${appiumVersion}. Please install a compatible version of the ${_.toLower(extTypeText)}.`
)
);
}
@@ -354,13 +356,15 @@ export class ExtensionConfig {
if (!extListData?.upToDate && extListData?.updateVersion) {
warnings.push(
createPeerWarning(
`an invalid or missing peer dependency on Appium. A newer version of \`${pkgName}\` is available; please attempt to upgrade "${extName}" to v${extListData.updateVersion} or newer.`
`an invalid or missing peer dependency on Appium. A newer version of \`${pkgName}\` is available; ` +
`please attempt to upgrade "${extName}" to v${extListData.updateVersion} or newer.`
)
);
} else {
warnings.push(
createPeerWarning(
`an invalid or missing peer dependency on Appium. Please ask the developer of \`${pkgName}\` to add a peer dependency on \`^appium@${APPIUM_VER}\`.`
`an invalid or missing peer dependency on Appium. ` +
`Please ask the developer of \`${pkgName}\` to add a peer dependency on \`^appium@${APPIUM_VER}\`.`
)
);
}
+10 -4
View File
@@ -99,9 +99,12 @@ export async function getActivePlugins(pluginConfig, maxParallelImports, usePlug
`The reserved plugin name '${pluginName}' cannot be combined with other names.`
);
} else {
const suffix = _.isEmpty(pluginConfig.installedExtensions)
? `You don't have any plugins installed yet.`
: `Only the following ${_.size(pluginConfig.installedExtensions) === 1 ? `plugin is` : `plugins are`} ` +
`available: ${_.keys(pluginConfig.installedExtensions)}`;
throw new Error(
`Could not load the plugin '${pluginName}' because it is not installed. ` +
`Only the following plugins are available: ${_.keys(pluginConfig.installedExtensions)}`
`Could not load the plugin '${pluginName}' because it is not installed. ${suffix}`
);
}
}
@@ -131,9 +134,12 @@ export async function getActiveDrivers(driverConfig, maxParallelImports, useDriv
if (driverName in driverConfig.installedExtensions) {
filteredDriverNames.push(driverName);
} else {
const suffix = _.isEmpty(driverConfig.installedExtensions)
? `You don't have any drivers installed yet.`
: `Only the following ${_.size(driverConfig.installedExtensions) === 1 ? `driver is` : `drivers are`} ` +
`available: ${_.keys(driverConfig.installedExtensions)}`;
throw new Error(
`Could not load the driver '${driverName}' because it is not installed. ` +
`Only the following drivers are available: ${_.keys(driverConfig.installedExtensions)}`
`Could not load the driver '${driverName}' because it is not installed. ${suffix}`
);
}
}
+1 -2
View File
@@ -30,10 +30,9 @@ async function registerNode(data, addr, port, basePath) {
try {
data = JSON.parse(data);
} catch (err) {
logger.errorAndThrow(
throw logger.errorWithException(
`Syntax error in node configuration file ${configFilePath}: ${err.message}`
);
return;
}
}
+6
View File
@@ -1,5 +1,6 @@
#!/usr/bin/env node
import {WebSocketServer} from 'ws';
import {init as logsinkInit} from './logsink'; // this import needs to come first since it sets up global npmlog
import logger from './logger'; // logger needs to remain second
import {
@@ -373,8 +374,13 @@ async function main(args) {
serverOpts.keepAliveTimeout = parsedArgs.keepAliveTimeout * 1000;
}
let server;
const bidiServer = new WebSocketServer({noServer: true});
bidiServer.on('connection', appiumDriver.onBidiConnection.bind(appiumDriver));
bidiServer.on('error', appiumDriver.onBidiServerError.bind(appiumDriver));
try {
server = await baseServer(serverOpts);
server.addWebSocketHandler('/bidi', bidiServer);
server.addWebSocketHandler('/bidi/:sessionId', bidiServer);
} catch (err) {
logger.error(
`Could not configure Appium server. It's possible that a driver or plugin tried ` +
+2 -1
View File
@@ -2,7 +2,7 @@ import _ from 'lodash';
import logger from './logger';
import {processCapabilities, PROTOCOLS, STANDARD_CAPS, errors} from '@appium/base-driver';
import {inspect as dump} from 'util';
import {node} from '@appium/support';
import {node, fs} from '@appium/support';
import path from 'path';
import {SERVER_SUBCOMMAND, DRIVER_TYPE, PLUGIN_TYPE} from './constants';
import os from 'node:os';
@@ -11,6 +11,7 @@ const W3C_APPIUM_PREFIX = 'appium';
const STANDARD_CAPS_LOWERCASE = new Set([...STANDARD_CAPS].map((cap) => cap.toLowerCase()));
export const V4_BROADCAST_IP = '0.0.0.0';
export const V6_BROADCAST_IP = '::';
export const npmPackage = fs.readPackageJsonFrom(__dirname);
/**
+14 -14
View File
@@ -1,6 +1,6 @@
{
"name": "appium",
"version": "2.3.0",
"version": "2.5.1",
"description": "Automation for Apps.",
"keywords": [
"automation",
@@ -60,40 +60,40 @@
"test:unit": "mocha \"./test/unit/**/*.spec.js\""
},
"dependencies": {
"@appium/base-driver": "^9.4.4",
"@appium/base-plugin": "^2.2.25",
"@appium/docutils": "^1.0.1",
"@appium/schema": "^0.5.0",
"@appium/support": "^4.1.11",
"@appium/types": "^0.15.0",
"@appium/base-driver": "^9.5.2",
"@appium/base-plugin": "^2.2.28",
"@appium/docutils": "^1.0.4",
"@appium/schema": "~0.5.0",
"@appium/support": "^4.2.2",
"@appium/types": "^0.16.1",
"@sidvind/better-ajv-errors": "2.1.3",
"@types/argparse": "2.0.14",
"@types/bluebird": "3.5.42",
"@types/fancy-log": "2.0.2",
"@types/semver": "7.5.6",
"@types/semver": "7.5.7",
"@types/teen_process": "2.0.4",
"@types/wrap-ansi": "3.0.0",
"ajv": "8.12.0",
"ajv-formats": "2.1.1",
"argparse": "2.0.1",
"async-lock": "1.4.0",
"async-lock": "1.4.1",
"asyncbox": "3.0.0",
"axios": "1.6.2",
"axios": "1.6.7",
"bluebird": "3.7.2",
"cross-env": "7.0.3",
"find-up": "5.0.0",
"lilconfig": "2.1.0",
"lilconfig": "3.0.0",
"lodash": "4.17.21",
"npmlog": "7.0.1",
"ora": "5.4.1",
"package-changed": "3.0.0",
"resolve-from": "5.0.0",
"semver": "7.5.4",
"semver": "7.6.0",
"source-map-support": "0.5.21",
"teen_process": "2.1.1",
"type-fest": "4.8.3",
"type-fest": "4.10.1",
"winston": "3.11.0",
"wrap-ansi": "7.0.0",
"ws": "8.16.0",
"yaml": "2.3.4"
},
"engines": {
@@ -1,5 +1,5 @@
{
"devDependencies": {
"webdriverio": "8.26.2"
"webdriverio": "8.31.1"
}
}
@@ -10,6 +10,7 @@ import {
EXT_SUBCOMMAND_LIST as LIST,
EXT_SUBCOMMAND_RUN as RUN,
EXT_SUBCOMMAND_UNINSTALL as UNINSTALL,
EXT_SUBCOMMAND_DOCTOR as DOCTOR,
KNOWN_DRIVERS,
} from '../../lib/constants';
import {FAKE_DRIVER_DIR, resolveFixture} from '../helpers';
@@ -51,6 +52,11 @@ describe('Driver CLI', function () {
*/
let runUninstall;
/**
* @type {(args: string[]) => Promise<ExtRecord<DriverType>>}
*/
let runDoctor;
async function resetAppiumHome() {
await fs.rimraf(appiumHome);
await fs.mkdirp(appiumHome);
@@ -67,6 +73,8 @@ describe('Driver CLI', function () {
/** @type {ReturnType<typeof runList>} */ (await run([DRIVER_TYPE, LIST, ...args]));
runRun = async (args) =>
/** @type {ReturnType<typeof runRun>} */ (await run([DRIVER_TYPE, RUN, ...args]));
runDoctor = async (args) =>
/** @type {ReturnType<typeof runDoctor>} */ (await run([DRIVER_TYPE, DOCTOR, ...args]));
});
after(async function () {
@@ -404,6 +412,22 @@ describe('Driver CLI', function () {
});
});
});
describe('doctor', function () {
const driverName = 'fake';
before(async function () {
await resetAppiumHome();
await installLocalExtension(appiumHome, DRIVER_TYPE, FAKE_DRIVER_DIR);
});
describe('when the driver defines doctor checks', function () {
it('should load and run them', async function () {
const checksLen = await runDoctor([driverName]);
checksLen.should.eql(2);
});
});
});
});
/**
+174 -24
View File
@@ -1,6 +1,7 @@
// @ts-check
import {BaseDriver} from '@appium/base-driver';
import {exec} from 'teen_process';
import {fs, tempDir} from '@appium/support';
import axios from 'axios';
import {command} from 'webdriver';
@@ -43,6 +44,35 @@ const wdOpts = {
connectionRetryCount: 0,
};
/**
* @param {string} appiumHome
*/
async function initFakeDriver(appiumHome) {
const {driverConfig} = await loadExtensions(appiumHome);
const driverList = await runExtensionCommand(
{
driverCommand: 'list',
subcommand: DRIVER_TYPE,
suppressOutput: true,
showInstalled: true,
},
driverConfig,
);
if (!_.has(driverList, 'fake')) {
await runExtensionCommand(
{
driverCommand: 'install',
driver: FAKE_DRIVER_DIR,
installType: INSTALL_TYPE_LOCAL,
subcommand: DRIVER_TYPE,
},
driverConfig,
);
}
return await driverConfig.requireAsync('fake');
}
describe('FakeDriver via HTTP', function () {
/** @type {AppiumServer} */
let server;
@@ -64,30 +94,7 @@ describe('FakeDriver via HTTP', function () {
wdOpts.port = port = await getTestPort();
testServerBaseUrl = `http://${TEST_HOST}:${port}`;
testServerBaseSessionUrl = `${testServerBaseUrl}/session`;
// first ensure we have fakedriver installed
const {driverConfig} = await loadExtensions(appiumHome);
const driverList = await runExtensionCommand(
{
driverCommand: 'list',
subcommand: DRIVER_TYPE,
suppressOutput: true,
showInstalled: true,
},
driverConfig,
);
if (!_.has(driverList, 'fake')) {
await runExtensionCommand(
{
driverCommand: 'install',
driver: FAKE_DRIVER_DIR,
installType: INSTALL_TYPE_LOCAL,
subcommand: DRIVER_TYPE,
},
driverConfig,
);
}
FakeDriver = await driverConfig.requireAsync('fake');
FakeDriver = await initFakeDriver(appiumHome);
});
after(async function () {
@@ -536,6 +543,149 @@ describe('FakeDriver via HTTP', function () {
}
});
});
describe('Bidi protocol', function () {
withServer();
const capabilities = {...caps, webSocketUrl: true, 'appium:runClock': true};
let driver;
beforeEach(async function () {
driver = await wdio({...wdOpts, capabilities});
});
afterEach(async function () {
if (driver) {
await driver.deleteSession();
}
});
it('should respond with bidi specific capability when a driver supports it', async function () {
should.exist(driver.capabilities.webSocketUrl);
});
it('should interpret the bidi protocol and let the driver handle it by command', async function () {
should.not.exist(await driver.getUrl());
await driver.browsingContextNavigate({
context: 'foo',
url: 'https://appium.io',
wait: 'complete',
});
await driver.getUrl().should.eventually.eql('https://appium.io');
});
it('should be able to subscribe and unsubscribe to bidi events', async function () {
let collectedEvents = [];
// asyncrhonously start our listener
driver.on('clock.currentTime', ({time}) => {
collectedEvents.push(time);
});
// wait for some time to be sure clock events have happened, and assert we don't receive
// any yet
await B.delay(750);
collectedEvents.should.be.empty;
// now subscribe and wait and assert that some events have been collected
await driver.sessionSubscribe({events: ['clock.currentTime']});
await B.delay(750);
collectedEvents.should.not.be.empty;
// finally unsubscribe and wait and assert that some events have been collected
await driver.sessionUnsubscribe({events: ['clock.currentTime']});
collectedEvents = [];
await B.delay(750);
collectedEvents.should.be.empty;
});
});
});
describe('Bidi over SSL', function () {
/**
* @param {string} certPath
* @param {string} keyPath
*/
async function generateCertificate(certPath, keyPath) {
await exec('openssl', [
'req',
'-nodes',
'-new',
'-x509',
'-keyout',
keyPath,
'-out',
certPath,
'-subj',
'/C=US/ST=State/L=City/O=company/OU=Com/CN=www.testserver.local',
]);
}
/** @type {AppiumServer} */
let server;
/** @type {string} */
let appiumHome;
/** @type {import('webdriverio').Browser} */
let driver;
// since we update the FakeDriver.prototype below, make sure we update the FakeDriver which is
// actually going to be required by Appium
/** @type {import('@appium/types').DriverClass} */
let FakeDriver;
/** @type {string} */
let testServerBaseSessionUrl;
/** @type {import('sinon').SinonSandbox} */
let sandbox;
let certPath = 'certificate.cert';
let keyPath = 'certificate.key';
const capabilities = {...caps, webSocketUrl: true};
before(async function () {
try {
await generateCertificate(certPath, keyPath);
} catch (e) {
if (process.env.CI) {
throw e;
}
return this.skip();
}
sandbox = createSandbox();
appiumHome = await tempDir.openDir();
wdOpts.port = port = await getTestPort();
testServerBaseUrl = `https://${TEST_HOST}:${port}`;
FakeDriver = await initFakeDriver(appiumHome);
server = await appiumServer({
address: TEST_HOST,
port,
appiumHome,
sslCertificatePath: certPath,
sslKeyPath: keyPath,
});
});
after(async function () {
await fs.rimraf(appiumHome);
await server.close();
});
beforeEach(async function () {
driver = await wdio({...wdOpts, protocol: 'https', strictSSL: false, capabilities});
});
afterEach(async function () {
if (driver) {
await driver.deleteSession();
}
});
it('should still run bidi over ssl', async function () {
should.not.exist(await driver.getUrl());
await driver.browsingContextNavigate({
context: 'foo',
url: 'https://appium.io',
wait: 'complete',
});
await driver.getUrl().should.eventually.eql('https://appium.io');
});
});
// TODO this test only works if the log has not previously been initialized in the same process.
@@ -239,7 +239,7 @@ describe('ExtensionConfig', function () {
await expect(
config.getGenericConfigWarnings(extData, extData.pkgName)
).to.eventually.eql([
`Driver "${extData.pkgName}" (package \`${extData.pkgName}\`) may be incompatible with the current version of Appium (v${APPIUM_VER}) due to its peer dependency on older Appium v${extData.appiumVersion}. Please upgrade \`${extData.pkgName}\` to v${updateVersion} or newer.`,
`Driver "${extData.pkgName}" (package \`${extData.pkgName}\`) may be incompatible with the current version of Appium (v${APPIUM_VER}) due to its peer dependency on Appium ${extData.appiumVersion}. Try to upgrade \`${extData.pkgName}\` to v${updateVersion} or newer.`,
]);
});
});
@@ -254,7 +254,7 @@ describe('ExtensionConfig', function () {
await expect(
config.getGenericConfigWarnings(extData, extData.pkgName)
).to.eventually.eql([
`Driver "${extData.pkgName}" (package \`${extData.pkgName}\`) may be incompatible with the current version of Appium (v${APPIUM_VER}) due to its peer dependency on older Appium v${extData.appiumVersion}. Please ask the developer of \`${extData.pkgName}\` to update the peer dependency on Appium to v${APPIUM_VER}.`,
`Driver "${extData.pkgName}" (package \`${extData.pkgName}\`) may be incompatible with the current version of Appium (v${APPIUM_VER}) due to its peer dependency on Appium ${extData.appiumVersion}. Please install a compatible version of the driver.`,
]);
});
});
+15 -4
View File
@@ -1,3 +1,4 @@
import _ from 'lodash';
import {DRIVER_TYPE, PLUGIN_TYPE} from '../../lib/constants';
import {getParser} from '../../lib/cli/parser';
import {INSTALL_TYPES} from '../../lib/extension/extension-config';
@@ -349,8 +350,13 @@ describe('parser', function () {
it('should not allow an empty driver argument list', function () {
(() => p.parseArgs([DRIVER_TYPE, 'run'])).should.throw();
});
it('should not allow no driver scriptName', function () {
(() => p.parseArgs([DRIVER_TYPE, 'run', 'foo'])).should.throw();
it('should allow no driver scriptName', function () {
const args = p.parseArgs([DRIVER_TYPE, 'run', 'foo']);
args.subcommand.should.eql(DRIVER_TYPE);
args.driverCommand.should.eql('run');
args.driver.should.eql('foo');
_.isNull(args.scriptName).should.be.true;
args.json.should.eql(false);
});
it('should take a driverName and scriptName to run', function () {
const args = p.parseArgs([DRIVER_TYPE, 'run', 'foo', 'bar']);
@@ -367,8 +373,13 @@ describe('parser', function () {
it('should not allow an empty plugin argument list', function () {
(() => p.parseArgs([PLUGIN_TYPE, 'run'])).should.throw();
});
it('should not allow no plugin scriptName', function () {
(() => p.parseArgs([PLUGIN_TYPE, 'run', 'foo'])).should.throw();
it('should allow no plugin scriptName', function () {
const args = p.parseArgs([PLUGIN_TYPE, 'run', 'foo']);
args.subcommand.should.eql(PLUGIN_TYPE);
args.pluginCommand.should.eql('run');
args.plugin.should.eql('foo');
_.isNull(args.scriptName).should.be.true;
args.json.should.eql(false);
});
it('should take a pluginName and scriptName to run', function () {
const args = p.parseArgs([PLUGIN_TYPE, 'run', 'foo', 'bar']);
+1 -1
View File
@@ -19,7 +19,7 @@ export type CliCommand = CliCommandServer | CliExtensionCommand;
* Possible subcommands of {@linkcode CliCommandDriver} or
* {@linkcode CliCommandPlugin}.
*/
export type CliExtensionSubcommand = 'install' | 'list' | 'run' | 'uninstall' | 'update';
export type CliExtensionSubcommand = 'install' | 'list' | 'run' | 'uninstall' | 'update'| 'doctor';
export interface CliExtensionSubcommandListArgs {
showInstalled?: boolean;
+39
View File
@@ -3,6 +3,45 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [9.5.2](https://github.com/appium/appium/compare/@appium/base-driver@9.5.1...@appium/base-driver@9.5.2) (2024-02-13)
**Note:** Version bump only for package @appium/base-driver
## [9.5.1](https://github.com/appium/appium/compare/@appium/base-driver@9.5.0...@appium/base-driver@9.5.1) (2024-02-06)
### Bug Fixes
* **images-plugin:** update dependency lru-cache to v10 ([#19723](https://github.com/appium/appium/issues/19723)) ([202da83](https://github.com/appium/appium/commit/202da83449073ddb3da8fb2cfbcfce482a5ecd40))
* **support:** update dependency axios to v1.6.4 ([332cc48](https://github.com/appium/appium/commit/332cc48a09b5532a8d51f85f3a24785e2c754e00))
* **support:** update dependency axios to v1.6.5 ([#19616](https://github.com/appium/appium/issues/19616)) ([ac73522](https://github.com/appium/appium/commit/ac73522351b31bd6c11972c61daa8b6b8d18fb91))
* **support:** update dependency axios to v1.6.6 ([6313704](https://github.com/appium/appium/commit/6313704ee5a8ee3aee726eb512ef259b6fa1041c))
* **support:** update dependency axios to v1.6.7 ([795092a](https://github.com/appium/appium/commit/795092a97f6d7569cccc4b5c166f52fef821514b))
* **types:** update dependency type-fest to v4.10 ([#19694](https://github.com/appium/appium/issues/19694)) ([966d305](https://github.com/appium/appium/commit/966d305e5eade9369a3875243bcad951df88545c))
* **types:** update dependency type-fest to v4.10.1 ([#19703](https://github.com/appium/appium/issues/19703)) ([501395c](https://github.com/appium/appium/commit/501395c9489320b84ab49ff78af4270f66070d62))
## [9.5.0](https://github.com/appium/appium/compare/@appium/base-driver@9.4.4...@appium/base-driver@9.5.0) (2024-01-03)
### Features
* add webdriver bidi support ([2b21e66](https://github.com/appium/appium/commit/2b21e66891e8ab8c3929f04f32e94eb4efdba691))
### Bug Fixes
* **base-driver:** update dependency async-lock to v1.4.1 ([a304a1f](https://github.com/appium/appium/commit/a304a1f78e658f6f70cbe8e1efd6d06b81d8d34e))
* **support:** update dependency axios to v1.6.3 ([441b284](https://github.com/appium/appium/commit/441b2848dae28472356f37fc5d51ac27af7bbe29))
* **types:** update dependency type-fest to v4 ([#19592](https://github.com/appium/appium/issues/19592)) ([94b3580](https://github.com/appium/appium/commit/94b358022fdba3050ef94c1f881895f07e24fb75))
## [9.4.4](https://github.com/appium/appium/compare/@appium/base-driver@9.4.3...@appium/base-driver@9.4.4) (2023-12-18)
+14 -7
View File
@@ -1,16 +1,23 @@
appium-base-driver
===================
[![NPM version](http://img.shields.io/npm/v/appium-base-driver.svg)](https://npmjs.org/package/@appium/base-driver)
# @appium/base-driver
> Base class for creating other Appium drivers
[![NPM version](http://img.shields.io/npm/v/@appium/base-driver.svg)](https://npmjs.org/package/@appium/base-driver)
[![Downloads](http://img.shields.io/npm/dm/@appium/base-driver.svg)](https://npmjs.org/package/@appium/base-driver)
[![Dependency Status](https://david-dm.org/appium/@appium/base-driver.svg)](https://david-dm.org/appium/@appium/base-driver)
[![devDependency Status](https://david-dm.org/appium/@appium/base-driver/dev-status.svg)](https://david-dm.org/appium/@appium/base-driver#info=devDependencies)
[![Build Status](https://travis-ci.org/appium/@appium/base-driver.svg?branch=master)](https://travis-ci.org/appium/@appium/base-driver)
This is the parent class that all Appium drivers inherit from. This driver should not be installed
directly as it does nothing on its own. Instead, you should extend this driver when creating your
*own* Appium drivers. Check out the [Building Drivers](https://appium.io/docs/en/latest/developing/build-drivers/)
documentation for more details.
This is the parent class that all [appium](appium.io) drivers inherit from, along with a collection of globally-used Appium driver utilities. Each utility is documented in its own README in the code:
Each included utility is documented in its own README:
* [BaseDriver](lib/basedriver)
* [The Appium Express Server](lib/express)
* [The Mobile JSON Wire Protocol Encapsulation](lib/mjsonwp)
* [The JSONWP Proxy Library](lib/jsonwp-proxy)
* [The JSONWP Status Library](lib/jsonwp-status)
## License
Apache-2.0
@@ -0,0 +1,37 @@
import type {Constraints, Driver, IBidiCommands} from '@appium/types';
import type {BaseDriver} from '../driver';
import {mixin} from './mixin';
declare module '../driver' {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
interface BaseDriver<C extends Constraints> extends IBidiCommands {}
}
const BidiCommands: IBidiCommands = {
async bidiSubscribe<C extends Constraints>(
this: BaseDriver<C>,
events: string[],
contexts: string[] = [''],
) {
for (const event of events) {
this.bidiEventSubs[event] = contexts;
}
},
async bidiUnsubscribe<C extends Constraints>(
this: BaseDriver<C>,
events: string[],
contexts: string[] = [''],
) {
for (const event of events) {
if (this.bidiEventSubs[event]) {
this.bidiEventSubs[event] = this.bidiEventSubs[event].filter((c) => !contexts.includes(c));
}
if (this.bidiEventSubs[event].length === 0) {
delete this.bidiEventSubs[event];
}
}
},
};
mixin(BidiCommands);
@@ -3,3 +3,4 @@ import './find';
import './log';
import './timeout';
import './execute';
import './bidi';
+79 -3
View File
@@ -14,15 +14,17 @@ import type {
Protocol,
RouteMatcher,
StringRecord,
BidiMethodDef,
} from '@appium/types';
import AsyncLock from 'async-lock';
import _ from 'lodash';
import {EventEmitter} from 'node:events';
import os from 'node:os';
import {DEFAULT_BASE_PATH, PROTOCOLS} from '../constants';
import {DEFAULT_BASE_PATH, PROTOCOLS, MAX_LOG_BODY_LENGTH} from '../constants';
import {errors} from '../protocol';
import DeviceSettings from './device-settings';
import helpers, {BASEDRIVER_VER} from './helpers';
import {BIDI_COMMANDS} from '../protocol/bidi-commands';
const NEW_COMMAND_TIMEOUT_MS = 60 * 1000;
@@ -99,6 +101,10 @@ export class DriverCore<const C extends Constraints, Settings extends StringReco
protocol?: Protocol;
bidiEventSubs: Record<string, string[]>;
doesSupportBidi: boolean;
constructor(opts: InitialOpts = <InitialOpts>{}, shouldValidateCaps = true) {
this._log = logger.getLogger(helpers.generateDriverLogPrefix(this as Core<C>));
@@ -132,6 +138,8 @@ export class DriverCore<const C extends Constraints, Settings extends StringReco
this.shutdownUnexpectedly = false;
this.commandsQueueGuard = new AsyncLock();
this.settings = new DeviceSettings();
this.bidiEventSubs = {};
this.doesSupportBidi = false;
}
get log() {
@@ -293,7 +301,7 @@ export class DriverCore<const C extends Constraints, Settings extends StringReco
`Potentially insecure feature '${name}' has not been ` +
`enabled. If you want to enable this feature and accept ` +
`the security ramifications, please do so by following ` +
`the documented instructions at http://appium.io/docs/en/2.0/guides/security/`
`the documented instructions at http://appium.io/docs/en/2.0/guides/security/`,
);
}
}
@@ -308,11 +316,23 @@ export class DriverCore<const C extends Constraints, Settings extends StringReco
if (!_.includes(validStrategies, strategy)) {
throw new errors.InvalidSelectorError(
`Locator Strategy '${strategy}' is not supported for this session`
`Locator Strategy '${strategy}' is not supported for this session`,
);
}
}
/**
* If this driver has requested proxying of bidi connections to an upstream bidi endpoint, this
* method should be overridden to return the URL of that websocket, to indicate that bidi
* proxying is enabled. Otherwise, a null return will indicate that bidi proxying should not be
* active and bidi commands will be handled by this driver.
*
* @returns {string | null}
*/
get bidiProxyUrl(): string | null {
return null;
}
proxyActive(sessionId: string): boolean {
return false;
}
@@ -376,4 +396,60 @@ export class DriverCore<const C extends Constraints, Settings extends StringReco
this.noCommandTimer = null;
}
}
async executeBidiCommand(bidiCmd: string, bidiParams: StringRecord): Promise<any> {
const [moduleName, methodName] = bidiCmd.split('.');
// if we don't get a valid format for bidi command name, reject
if (!moduleName || !methodName) {
throw new errors.UnknownCommandError(
`Did not receive a valid BiDi module and method name ` +
`of the form moduleName.methodName. Instead received ` +
`'${moduleName}.${methodName}'`,
);
}
// if the command module isn't part of our spec, reject
if (!BIDI_COMMANDS[moduleName]) {
throw new errors.UnknownCommandError();
}
const {command, params} = BIDI_COMMANDS[moduleName][methodName] as BidiMethodDef;
// if the command method isn't part of our spec, also reject
if (!command) {
throw new errors.UnknownCommandError();
}
// If the driver doesn't have this command, it must not be implemented
if (!this[command]) {
throw new errors.NotYetImplementedError();
}
// TODO improve param parsing and error messages along the lines of what we have in the http
// handlers
const args: any[] = [];
if (params?.required?.length) {
for (const requiredParam of params.required) {
if (_.isUndefined(bidiParams[requiredParam])) {
throw new errors.InvalidArgumentError(
`The ${requiredParam} parameter was required but you omitted it`,
);
}
args.push(bidiParams[requiredParam]);
}
}
if (params?.optional?.length) {
for (const optionalParam of params.optional) {
args.push(bidiParams[optionalParam]);
}
}
const logParams = _.truncate(JSON.stringify(bidiParams), {length: MAX_LOG_BODY_LENGTH});
this.log.debug(
`Executing bidi command '${bidiCmd}' with params ${logParams} by passing to driver ` +
`method '${command}'`,
);
const res = (await this[command](...args)) ?? null;
this.log.debug(`Responding to bidi command '${bidiCmd}' with ${JSON.stringify(res)}`);
return res;
}
}
+17 -20
View File
@@ -37,7 +37,7 @@ export class BaseDriver<
Settings extends StringRecord = StringRecord,
CreateResult = DefaultCreateSessionResult<C>,
DeleteResult = DefaultDeleteSessionResult,
SessionData extends StringRecord = StringRecord
SessionData extends StringRecord = StringRecord,
>
extends DriverCore<C, Settings>
implements Driver<C, CArgs, Settings, CreateResult, DeleteResult, SessionData>
@@ -55,7 +55,6 @@ export class BaseDriver<
super(opts, shouldValidateCaps);
this.caps = {} as DriverCaps<C>;
this.cliArgs = {} as CArgs & ServerArgs;
}
@@ -114,7 +113,7 @@ export class BaseDriver<
// This is needed to prevent memory leaks
this.eventEmitter.removeListener(
ON_UNEXPECTED_SHUTDOWN_EVENT,
unexpectedShutdownListener
unexpectedShutdownListener,
);
unexpectedShutdownListener = null;
}
@@ -147,7 +146,7 @@ export class BaseDriver<
}
async startUnexpectedShutdown(
err: Error = new errors.NoSuchDriverError('The driver was unexpectedly shut down!')
err: Error = new errors.NoSuchDriverError('The driver was unexpectedly shut down!'),
) {
this.eventEmitter.emit(ON_UNEXPECTED_SHUTDOWN_EVENT, err); // allow others to listen for this
this.shutdownUnexpectedly = true;
@@ -170,7 +169,7 @@ export class BaseDriver<
this.noCommandTimer = setTimeout(async () => {
this.log.warn(
`Shutting down because we waited ` +
`${this.newCommandTimeoutMs / 1000.0} seconds for a command`
`${this.newCommandTimeoutMs / 1000.0} seconds for a command`,
);
const errorMessage =
`New Command Timeout of ` +
@@ -234,23 +233,23 @@ export class BaseDriver<
w3cCapabilities2?: W3CDriverCaps<C>,
w3cCapabilities?: W3CDriverCaps<C>,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
driverData?: DriverData[]
driverData?: DriverData[],
): Promise<CreateResult> {
if (this.sessionId !== null) {
throw new errors.SessionNotCreatedError(
'Cannot create a new session while one is in progress'
'Cannot create a new session while one is in progress',
);
}
this.log.debug();
const originalCaps = _.cloneDeep(
[w3cCapabilities, w3cCapabilities1, w3cCapabilities2].find(isW3cCaps)
[w3cCapabilities, w3cCapabilities1, w3cCapabilities2].find(isW3cCaps),
);
if (!originalCaps) {
throw new errors.SessionNotCreatedError(
'Appium only supports W3C-style capability objects. ' +
'Your client is sending an older capabilities format. Please update your client library.'
'Your client is sending an older capabilities format. Please update your client library.',
);
}
@@ -258,7 +257,7 @@ export class BaseDriver<
this.originalCaps = originalCaps;
this.log.debug(
`Creating session with W3C capabilities: ${JSON.stringify(originalCaps, null, 2)}`
`Creating session with W3C capabilities: ${JSON.stringify(originalCaps, null, 2)}`,
);
let caps: DriverCaps<C>;
@@ -266,7 +265,7 @@ export class BaseDriver<
caps = processCapabilities(
originalCaps,
this._desiredCapConstraints,
this.shouldValidateCaps
this.shouldValidateCaps,
) as DriverCaps<C>;
caps = fixCaps(caps, this._desiredCapConstraints, this.log) as DriverCaps<C>;
} catch (e) {
@@ -287,7 +286,7 @@ export class BaseDriver<
throw new Error(
"The 'noReset' and 'fullReset' capabilities are mutually " +
'exclusive and should not both be set to true. You ' +
"probably meant to just use 'fullReset' on its own"
"probably meant to just use 'fullReset' on its own",
);
}
if (this.opts.noReset === true) {
@@ -354,9 +353,7 @@ export class BaseDriver<
logExtraCaps(caps: Capabilities<C>) {
const extraCaps = _.difference(_.keys(caps), _.keys(this._desiredCapConstraints));
if (extraCaps.length) {
this.log.warn(
`The following provided capabilities were not recognized by this driver:`
);
this.log.warn(`The following provided capabilities were not recognized by this driver:`);
for (const cap of extraCaps) {
this.log.warn(` ${cap}`);
}
@@ -371,11 +368,11 @@ export class BaseDriver<
try {
validateCaps(caps, this._desiredCapConstraints);
} catch (e) {
this.log.errorAndThrow(
throw this.log.errorWithException(
new errors.SessionNotCreatedError(
`The desiredCapabilities object was not valid for the ` +
`following reason(s): ${e.message}`
)
`following reason(s): ${e.message}`,
),
);
}
@@ -386,14 +383,14 @@ export class BaseDriver<
async updateSettings(newSettings: Settings) {
if (!this.settings) {
this.log.errorAndThrow('Cannot update settings; settings object not found');
throw this.log.errorWithException('Cannot update settings; settings object not found');
}
return await this.settings.update(newSettings);
}
async getSettings() {
if (!this.settings) {
this.log.errorAndThrow('Cannot get settings; settings object not found');
throw this.log.errorWithException('Cannot get settings; settings object not found');
}
return this.settings.getSettings();
}
@@ -491,7 +491,7 @@ async function unzipApp(zipPath, dstRoot, supportedAppExtensions) {
})
).sort((a, b) => a.split(path.sep).length - b.split(path.sep).length);
if (_.isEmpty(sortedBundleItems)) {
logger.errorAndThrow(
throw logger.errorWithException(
`App unzipped OK, but we could not find any '${supportedAppExtensions}' ` +
util.pluralize('bundle', supportedAppExtensions.length, false) +
` in it. Make sure your archive contains at least one package having ` +
+9 -4
View File
@@ -1,8 +1,13 @@
import B from 'bluebird';
B.config({
cancellation: true,
});
try {
B.config({
cancellation: true,
});
} catch (ign) {
// sometimes during testing this somehow gets required twice and results in an error about
// cancellation not being able to be enabled after promise has been configured
}
// BaseDriver exports
import {BaseDriver} from './basedriver/driver';
@@ -11,11 +16,11 @@ export {DeviceSettings} from './basedriver/device-settings';
export {BaseDriver};
export default BaseDriver;
export {MAX_LOG_BODY_LENGTH, DEFAULT_BASE_PATH, PROTOCOLS, W3C_ELEMENT_KEY} from './constants';
// MJSONWP exports
export * from './protocol';
export {errorFromMJSONWPStatusCode as errorFromCode} from './protocol';
export {DEFAULT_BASE_PATH, PROTOCOLS, W3C_ELEMENT_KEY} from './constants';
// Express exports
export {STATIC_DIR} from './express/static';
@@ -0,0 +1,31 @@
const SUBSCRIPTION_REQUEST_PARAMS = /** @type {const} */ ({
required: ['events'],
optional: ['contexts'],
});
const BIDI_COMMANDS = /** @type {const} */ ({
session: {
subscribe: {
command: 'bidiSubscribe',
params: SUBSCRIPTION_REQUEST_PARAMS,
},
unsubscribe: {
command: 'bidiUnsubscribe',
params: SUBSCRIPTION_REQUEST_PARAMS,
},
},
browsingContext: {
navigate: {
command: 'bidiNavigate',
params: {
required: ['context', 'url'],
optional: ['wait'],
},
},
},
});
// TODO add definitions for all bidi commands.
// spec link: https://w3c.github.io/webdriver-bidi/
export {BIDI_COMMANDS};
@@ -28,6 +28,27 @@ export class ProtocolError extends ES6Error {
set stacktrace(value) {
this._stacktrace = value;
}
/**
* Get the Bidi protocol version of an error
* @param {string|number} id - the id used in the request for which this error forms the response
* @see https://w3c.github.io/webdriver-bidi/#protocol-definition
* @returns The object conforming to the shape of a BiDi error response
*/
bidiErrObject(id) {
// if we don't have an id, the client didn't send one, so we have nothing to send back.
// send back an empty string rather than making something up
if (_.isNil(id)) {
id = '';
}
return {
id,
type: 'error',
error: this.error,
stacktrace: this.stacktrace,
message: this.message,
};
}
}
// https://github.com/SeleniumHQ/selenium/blob/176b4a9e3082ac1926f2a436eb346760c37a5998/java/client/src/org/openqa/selenium/remote/ErrorCodes.java#L215
+7 -7
View File
@@ -1,6 +1,6 @@
{
"name": "@appium/base-driver",
"version": "9.4.4",
"version": "9.5.2",
"description": "Base driver class for Appium drivers",
"keywords": [
"automation",
@@ -44,8 +44,8 @@
"test:types": "tsd"
},
"dependencies": {
"@appium/support": "^4.1.11",
"@appium/types": "^0.15.0",
"@appium/support": "^4.2.2",
"@appium/types": "^0.16.1",
"@colors/colors": "1.6.0",
"@types/async-lock": "1.4.2",
"@types/bluebird": "3.5.42",
@@ -53,22 +53,22 @@
"@types/lodash": "4.14.202",
"@types/method-override": "0.0.35",
"@types/serve-favicon": "2.5.7",
"async-lock": "1.4.0",
"async-lock": "1.4.1",
"asyncbox": "3.0.0",
"axios": "1.6.2",
"axios": "1.6.7",
"bluebird": "3.7.2",
"body-parser": "1.20.2",
"es6-error": "4.1.1",
"express": "4.18.2",
"http-status-codes": "2.3.0",
"lodash": "4.17.21",
"lru-cache": "10.1.0",
"lru-cache": "10.2.0",
"method-override": "3.0.0",
"morgan": "1.10.0",
"path-to-regexp": "6.2.1",
"serve-favicon": "2.5.0",
"source-map-support": "0.5.21",
"type-fest": "4.8.3",
"type-fest": "4.10.1",
"validate.js": "0.13.1"
},
"optionalDependencies": {
+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.2.28](https://github.com/appium/appium/compare/@appium/base-plugin@2.2.27...@appium/base-plugin@2.2.28) (2024-02-13)
**Note:** Version bump only for package @appium/base-plugin
## [2.2.27](https://github.com/appium/appium/compare/@appium/base-plugin@2.2.26...@appium/base-plugin@2.2.27) (2024-02-06)
**Note:** Version bump only for package @appium/base-plugin
## [2.2.26](https://github.com/appium/appium/compare/@appium/base-plugin@2.2.25...@appium/base-plugin@2.2.26) (2024-01-03)
**Note:** Version bump only for package @appium/base-plugin
## [2.2.25](https://github.com/appium/appium/compare/@appium/base-plugin@2.2.24...@appium/base-plugin@2.2.25) (2023-12-18)
+14 -2
View File
@@ -1,3 +1,15 @@
# Appium Base Plugin
# @appium/base-plugin
The base class used to create Appium plugins. This plugin should not be installed directly as it does nothing. Instead, you extend this plugin when creating your *own* Appium plugins. Check out the [Building Plugins](https://appium.io/docs/en/latest/ecosystem/build-plugins/) to learn how to make use of this base class to develop Appium plugins.
> Base class for creating other Appium plugins
[![NPM version](http://img.shields.io/npm/v/@appium/base-plugin.svg)](https://npmjs.org/package/@appium/base-plugin)
[![Downloads](http://img.shields.io/npm/dm/@appium/base-plugin.svg)](https://npmjs.org/package/@appium/base-plugin)
This is the parent class that all Appium plugins inherit from. This plugin should not be installed
directly as it does nothing on its own. Instead, you should extend this plugin when creating your
*own* Appium plugins. Check out the [Building Plugins](https://appium.io/docs/en/latest/developing/build-plugins/)
documentation for more details.
## License
Apache-2.0
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@appium/base-plugin",
"version": "2.2.25",
"version": "2.2.28",
"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.4.4",
"@appium/support": "^4.1.11"
"@appium/base-driver": "^9.5.2",
"@appium/support": "^4.2.2"
},
"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.
## [2.0.33](https://github.com/appium/appium/compare/@appium/doctor@2.0.32...@appium/doctor@2.0.33) (2024-02-13)
**Note:** Version bump only for package @appium/doctor
## [2.0.32](https://github.com/appium/appium/compare/@appium/doctor@2.0.31...@appium/doctor@2.0.32) (2024-02-06)
### Bug Fixes
* **doctor:** update dependency appium-adb to v11.1.0 ([cbc1f50](https://github.com/appium/appium/commit/cbc1f500bc0721edcdf7f47af949ae113c038a46))
* **doctor:** update dependency appium-adb to v12 ([#19648](https://github.com/appium/appium/issues/19648)) ([9617974](https://github.com/appium/appium/commit/961797448116b6be7b500a68b4202b969805825c))
* **doctor:** update dependency appium-adb to v12.0.1 ([b85a6b9](https://github.com/appium/appium/commit/b85a6b939f688703522ebb6ddab19aa807c64dbe))
* **doctor:** update dependency appium-adb to v12.0.2 ([5936d53](https://github.com/appium/appium/commit/5936d53d7856e6a2aa31236eaa0ea9afee737c95))
* **doctor:** update dependency appium-adb to v12.0.3 ([#19722](https://github.com/appium/appium/issues/19722)) ([6cd2c54](https://github.com/appium/appium/commit/6cd2c547def390eb5d739d6245fe0bab1f99931a))
## [2.0.31](https://github.com/appium/appium/compare/@appium/doctor@2.0.30...@appium/doctor@2.0.31) (2024-01-03)
### Bug Fixes
* **doctor:** update dependency appium-adb to v11.0.9 ([3465495](https://github.com/appium/appium/commit/34654956a3cd5a76865444ab52bd905543dfcb2c))
## [2.0.30](https://github.com/appium/appium/compare/@appium/doctor@2.0.29...@appium/doctor@2.0.30) (2023-12-18)
+13 -23
View File
@@ -1,6 +1,14 @@
appium-doctor
===================
Attempts to diagnose and fix common Node, iOS and Android configuration issues before starting Appium.
# @appium/doctor
> Attempts to diagnose and fix common Appium configuration issues
[![NPM version](http://img.shields.io/npm/v/@appium/doctor.svg)](https://npmjs.org/package/@appium/doctor)
[![Downloads](http://img.shields.io/npm/dm/@appium/doctor.svg)](https://npmjs.org/package/@appium/doctor)
> [!WARNING]
> This package has been deprecated since the Appium server v 2.4.0 and will be removed in the future.
> Use doctor checks (if any exist) integrated into your installed driver or plugin by
> running `appium driver doctor <driver_name>` or `appium plugin doctor <plugin_name>`.
### Install
@@ -26,24 +34,6 @@ Options:
-h, --help Show help [boolean]
```
## Dev
## License
## Watch
```
npm run watch
```
## Test
```
npm test
```
## Hack Cli
Use the `--demo` option to simulate the fix process.
```
appium-doctor --demo
```
Apache-2.0
+3
View File
@@ -198,6 +198,9 @@ class Doctor {
async run() {
log.info(`Appium Doctor v.${version}`);
log.warn(`This package is deprecated since the Appium server version 2.4.0 and will be removed in the future.`);
log.warn(`Use doctor checks (if any exist) integrated into your installed driver or plugin by ` +
`running \`appium driver doctor <driver_name>\` or \`appium plugin doctor <plugin_name>\`.`);
await this.diagnose();
if (await this.reportSuccess(this.toFix.length, this.toFixOptionals.length)) {
return;
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@appium/doctor",
"version": "2.0.30",
"version": "2.0.33",
"description": "Test environment for fitness to run Appium",
"keywords": [
"automation",
@@ -44,11 +44,11 @@
"test:unit": "mocha \"./test/unit/**/*.spec.js\""
},
"dependencies": {
"@appium/support": "^4.1.11",
"@appium/support": "^4.2.2",
"@colors/colors": "1.6.0",
"@types/bluebird": "3.5.42",
"@types/teen_process": "2.0.4",
"appium-adb": "11.0.8",
"appium-adb": "12.0.3",
"bluebird": "3.7.2",
"inquirer": "9.2.12",
"lodash": "4.17.21",
+45 -1
View File
@@ -3,13 +3,57 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.0.1](https://github.com/appium/appium/compare/@appium/docutils@0.4.13...@appium/docutils@1.0.1) (2023-12-18)
## [1.0.4](https://github.com/appium/appium/compare/@appium/docutils@1.0.3...@appium/docutils@1.0.4) (2024-02-13)
### Bug Fixes
* **docutils:** update dependency diff to v5.2.0 ([d83d436](https://github.com/appium/appium/commit/d83d436d01232463a48902964e5ef97334d047f9))
* **support:** update dependency semver to v7.6.0 ([7b40a32](https://github.com/appium/appium/commit/7b40a3225ebe0f5976a26d05db2f22ba7b13a130))
## [1.0.3](https://github.com/appium/appium/compare/@appium/docutils@1.0.2...@appium/docutils@1.0.3) (2024-02-06)
### Bug Fixes
* **docutils:** change default alias type to `symlink` ([#19681](https://github.com/appium/appium/issues/19681)) ([d9711b4](https://github.com/appium/appium/commit/d9711b4191d19ed0b89944b2732a1a4f4c4d9c0b))
* **types:** update dependency type-fest to v4.10 ([#19694](https://github.com/appium/appium/issues/19694)) ([966d305](https://github.com/appium/appium/commit/966d305e5eade9369a3875243bcad951df88545c))
* **types:** update dependency type-fest to v4.10.1 ([#19703](https://github.com/appium/appium/issues/19703)) ([501395c](https://github.com/appium/appium/commit/501395c9489320b84ab49ff78af4270f66070d62))
## [1.0.2](https://github.com/appium/appium/compare/@appium/docutils@1.0.1...@appium/docutils@1.0.2) (2024-01-03)
### Bug Fixes
* **docutils:** update dependency lilconfig to v3 ([#19489](https://github.com/appium/appium/issues/19489)) ([ea8394a](https://github.com/appium/appium/commit/ea8394a888c835bc54f7f68174a4b6cae85339ea))
* **types:** update dependency type-fest to v4 ([#19592](https://github.com/appium/appium/issues/19592)) ([94b3580](https://github.com/appium/appium/commit/94b358022fdba3050ef94c1f881895f07e24fb75))
## [1.0.1](https://github.com/appium/appium/compare/@appium/docutils@0.4.13...@appium/docutils@1.0.1) (2023-12-18)
This includes all changes from 1.0.0.
## 1.0.0 [Unreleased]
### ⚠ BREAKING CHANGES
* `@appium/typedoc-plugin-appium` integration and all other uses of `typedoc` have been removed
* `--prefix` option has been renamed to `--deploy-prefix`
* `--rebase` flag has been removed
### Features
* **docutils:** enhance base mkdocs configuration ([#19493](https://github.com/appium/appium/issues/19493)) ([9de6f40](https://github.com/appium/appium/commit/9de6f409ec506017d6aa865cfafbd3ad5bc8d423))
* **docutils:** further adjust base mkdocs configuration ([#19527](https://github.com/appium/appium/issues/19527)) ([3dff112](https://github.com/appium/appium/commit/3dff11253db9a4681b39794b60f44dce01a7ddd3))
* **docutils:** add `--alias-type` option ([#19485](https://github.com/appium/appium/issues/19485)) ([0003304](https://github.com/appium/appium/commit/00033044e08f932b5daf1234cfb381c6f46c8bb8))
### Bug Fixes
* **docutils:** remove `@appium/typedoc-plugin-appium` and all other uses of `typedoc` ([#19465](https://github.com/appium/appium/issues/19465)) ([7528fcf](https://github.com/appium/appium/commit/7528fcf890f79f4017f5e718bb1952bf907ee479))
* **docutils:** update dependency mike to v2.0.0 ([#19485](https://github.com/appium/appium/issues/19485)) ([0003304](https://github.com/appium/appium/commit/00033044e08f932b5daf1234cfb381c6f46c8bb8))
* **docutils:** update dependency typescript to v5.2.2 ([#19472](https://github.com/appium/appium/issues/19472)) ([cb950df](https://github.com/appium/appium/commit/cb950dffb82e114341d2e0e0faa5ce6d551ec267))
+15 -10
View File
@@ -2,21 +2,26 @@
> Documentation-building utilities for Appium
## Prerequisites
[![NPM version](http://img.shields.io/npm/v/@appium/docutils.svg)](https://npmjs.org/package/@appium/docutils)
[![Downloads](http://img.shields.io/npm/dm/@appium/docutils.svg)](https://npmjs.org/package/@appium/docutils)
- Node.js in the [semantic version range](https://semver.org) `^14.17.0 || ^16.13.0 || >=18.0.0`
- Python v3+ if building HTML documentation
## Setup for Main Appium Docs
## Running for Main Appium Docs
This package does not need to be run directly. Instead, execute this command in the `packages/appium` folder of [this monorepo](https://github.com/appium/appium):
This package does not need to be run directly. Instead, execute this command in the
`packages/appium` directory:
```bash
npm run dev:docs:en
````
```
See the `scripts` field of the `package.json` for other languages.
> [!NOTE]
> This will only build the English docs. See the `scripts` field of the `package.json` for
other languages.
## Using in Appium Extensions
## Setup for Appium Drivers and Plugins
TODO: give an example of how this package is used in an extension outside the appium monorepo, for example when it's made a part of the XCUITest repo. Or link to the part of the Appium docs where this info will live.
See the Appium docs on [Building Documentation](http://appium.io/docs/en/latest/developing/build-docs/).
## License
Apache-2.0
+2 -8
View File
@@ -139,12 +139,6 @@ export async function deploy({
const mikeArgs = [
...argify(_.pickBy(mikeOpts, (value) => _.isNumber(value) || Boolean(value))),
];
if (alias) {
mikeArgs.push('--update-aliases', version, alias);
mikeArgs.push('--alias-type', aliasType);
} else {
mikeArgs.push(version);
}
stop(); // discard
// unsure about how SIGHUP is handled here
await doServe(mikePath, mikeArgs, serveOpts);
@@ -159,8 +153,8 @@ export async function deploy({
),
];
if (alias) {
mikeArgs.push('--update-aliases', version, alias);
mikeArgs.push('--alias-type', aliasType);
mikeArgs.push('--update-aliases', '--alias-type', aliasType);
mikeArgs.push(version, alias);
} else {
mikeArgs.push(version);
}
+2 -3
View File
@@ -4,7 +4,6 @@
*/
import loadTs from '@sliphua/lilconfig-ts-loader';
import {LogLevel} from 'consola';
import {lilconfig, Loader} from 'lilconfig';
import _ from 'lodash';
import path from 'node:path';
@@ -46,9 +45,9 @@ export async function findConfig(argv: string[] = hideBin(process.argv)) {
// if --verbose is used, set the log level to debug.
// otherwise use --log-level or the default.
let logLevel: LogLevel | keyof typeof LogLevelMap;
let logLevel: keyof typeof LogLevelMap;
if (preArgs.verbose) {
logLevel = LogLevel.Debug;
logLevel = 'debug';
} else {
// if the loglevel is valid, use it, otherwise use the default
logLevel = isLogLevelString(preArgs.logLevel) ? preArgs.logLevel : DEFAULT_LOG_LEVEL;
+8 -7
View File
@@ -2,7 +2,8 @@
* Constants used across various modules in this package
* @module
*/
import {LogLevel} from 'consola';
// eslint-disable-next-line @typescript-eslint/no-var-requires -- Consola 3 import call is ESM
const {LogLevels} = require('consola');
import {readFileSync} from 'node:fs';
import {fs} from '@appium/support';
import path from 'node:path';
@@ -108,7 +109,7 @@ export const REQUIREMENTS_TXT_PATH = path.join(PKG_ROOT_DIR, NAME_REQUIREMENTS_T
* The default alias creation strategy to pass to `mike` when deploying
* (`symlink`, `redirect` or `copy`)
*/
export const DEFAULT_DEPLOY_ALIAS_TYPE = 'redirect';
export const DEFAULT_DEPLOY_ALIAS_TYPE = 'symlink';
/**
* The default branch to deploy to
@@ -134,11 +135,11 @@ export const DEFAULT_SERVE_HOST = 'localhost';
* Mapping of `@appium/docutils`' log levels to `consola` log levels
*/
export const LogLevelMap = {
silent: LogLevel.Silent,
error: LogLevel.Error,
warn: LogLevel.Warn,
info: LogLevel.Info,
debug: LogLevel.Debug,
silent: LogLevels.silent,
error: LogLevels.error,
warn: LogLevels.warn,
info: LogLevels.info,
debug: LogLevels.debug,
} as const;
/**
+15 -128
View File
@@ -6,134 +6,17 @@
* @module
*/
import chalk, {type BackgroundColor, type ForegroundColor} from 'chalk';
import consola, {
type Consola,
type ConsolaReporterLogObject,
FancyReporter,
type FancyReporterOptions,
type LogLevel,
type logType as LogType,
} from 'consola';
import figures from 'figures';
// eslint-disable-next-line @typescript-eslint/no-var-requires -- Consola 3 import call is ESM
const {ConsolaInstance, createConsola, LogLevel} = require('consola');
import _ from 'lodash';
import logSymbols from 'log-symbols';
import {DEFAULT_LOG_LEVEL, LogLevelMap} from './constants';
/**
* This is a reporter for `consola` which uses some extra/custom icons and colors.
*
* @privateRemarks
* I did not like that the default `FancyReport` logs errors in _green_ without any sort of icon.
* Both `log-symbols` and `consola` consume `chalk`, so we do too. `consola` also depends on `figures`.
*/
class DocutilsReporter extends FancyReporter {
/**
* Mapping of log types (the name of the logging method called) to chalk fg colors
*/
static readonly TYPE_COLOR_MAP = {
info: 'cyan',
success: 'green',
error: 'red',
warn: 'yellow',
} satisfies {[k in LogType]?: typeof ForegroundColor};
/**
* Mapping of log levels to chalk fg colors
*/
static readonly LEVEL_COLORS = {
0: 'red',
1: 'yellow',
2: 'white',
3: 'green',
} satisfies {[k in LogLevel]?: typeof ForegroundColor};
/**
* Mapping of log types to icons/symbols
*/
static readonly TYPE_ICONS = {
info: logSymbols.info,
success: logSymbols.success,
error: logSymbols.error,
warn: logSymbols.warning,
debug: figures('›'),
trace: figures('›'),
} satisfies {[k in LogType]?: string};
/**
* Default color to use if we can't find a color for the log type or level
*/
static readonly DEFAULT_COLOR = 'grey';
/**
* Type guard to check if a log type has a color
* @param type A log type
*/
static hasTypeColor(type: LogType): type is keyof typeof DocutilsReporter.TYPE_COLOR_MAP {
return type in DocutilsReporter.TYPE_COLOR_MAP;
}
/**
* Type guard to check if a log level has a color
* @param level A log level
*/
static hasLevelColor(level: LogLevel): level is keyof typeof DocutilsReporter.LEVEL_COLORS {
return level in DocutilsReporter.LEVEL_COLORS;
}
/**
* Type guard to check if a log type has an icon
* @param type A log type
*/
static hasTypeIcon(type: LogType): type is keyof typeof DocutilsReporter.TYPE_ICONS {
return type in DocutilsReporter.TYPE_ICONS;
}
/**
* Prefixes the logging output with colors and symbols, depending on contents of `logObj`.
* @param logObj Consola's log object
* @param isBadge {@linkcode FancyReporter} uses this; I think it depends on the terminal width
* @returns
*/
protected override formatType(logObj: ConsolaReporterLogObject, isBadge?: boolean): string {
const {
TYPE_COLOR_MAP,
LEVEL_COLORS,
TYPE_ICONS,
hasTypeColor,
hasLevelColor,
hasTypeIcon,
DEFAULT_COLOR,
} = DocutilsReporter;
let typeColor: typeof ForegroundColor;
if (hasTypeColor(logObj.type)) {
typeColor = TYPE_COLOR_MAP[logObj.type];
} else if (hasLevelColor(logObj.level)) {
typeColor = LEVEL_COLORS[logObj.level];
} else {
typeColor = <typeof ForegroundColor>(
((this.options as FancyReporterOptions).secondaryColor ?? DEFAULT_COLOR)
);
}
if (isBadge) {
return chalk[('bg' + _.capitalize(typeColor)) as typeof BackgroundColor].black(
` ${_.toUpper(logObj.type)}`
);
}
const type = hasTypeIcon(logObj.type) ? TYPE_ICONS[logObj.type] : logObj.type;
return type ? chalk[typeColor](type) : '';
}
}
/**
* The global log level
*
* "Global" inasmuch as any logger created from the root logger will use this level.
*/
let globalLevel: LogLevel = LogLevelMap[DEFAULT_LOG_LEVEL];
let globalLevel: typeof LogLevel = LogLevelMap[DEFAULT_LOG_LEVEL];
/**
* Type guard to see if a string is a recognized log level
@@ -144,20 +27,24 @@ export function isLogLevelString(level: any): level is keyof typeof LogLevelMap
}
/**
* The logger from which all loggers are created. This one uses a unique tag and our custom reporter.
* The logger from which all loggers are created. This one uses a unique tag.
*/
const rootLogger = consola.create({
const rootLogger = createConsola({
defaults: {tag: 'docutils'},
reporters: [new DocutilsReporter()],
fancy: true,
level: globalLevel,
formatOptions: {
colors: true,
date: false,
},
});
// this prevents logging before `initLogger` is called
rootLogger.pause();
rootLogger.pauseLogs();
/**
* A map of tags to loggers
*/
const loggers: Map<string, WeakRef<Consola>> = new Map();
const loggers: Map<string, WeakRef<typeof ConsolaInstance>> = new Map();
export function getLogger(tag: string, parent = rootLogger) {
if (loggers.has(tag)) {
@@ -179,15 +66,15 @@ export function getLogger(tag: string, parent = rootLogger) {
*
* @remarks Child loggers seem to inherit the "paused" state of the parent, so when this is called, we must resume all of them.
*/
export const initLogger = _.once((level: keyof typeof LogLevelMap | LogLevel) => {
export const initLogger = _.once((level: keyof typeof LogLevelMap | typeof LogLevel) => {
globalLevel = isLogLevelString(level) ? LogLevelMap[level] : level;
rootLogger.level = globalLevel;
rootLogger.resume();
rootLogger.resumeLogs();
for (const ref of loggers.values()) {
const logger = ref.deref();
if (logger) {
logger.level = globalLevel;
logger.resume();
logger.resumeLogs();
}
}
});
+7 -9
View File
@@ -1,6 +1,6 @@
{
"name": "@appium/docutils",
"version": "1.0.1",
"version": "1.0.4",
"description": "Documentation generation utilities for Appium and related projects",
"keywords": [
"automation",
@@ -48,24 +48,22 @@
"start": "node ./build/lib/cli/index.js"
},
"dependencies": {
"@appium/support": "^4.1.11",
"@appium/support": "^4.2.2",
"@appium/tsconfig": "^0.x",
"@sliphua/lilconfig-ts-loader": "3.2.2",
"@types/which": "3.0.3",
"chalk": "4.1.2",
"consola": "2.15.3",
"diff": "5.1.0",
"figures": "3.2.0",
"consola": "3.2.3",
"diff": "5.2.0",
"json5": "2.2.3",
"lilconfig": "2.1.0",
"lilconfig": "3.0.0",
"lodash": "4.17.21",
"log-symbols": "4.1.0",
"pkg-dir": "5.0.0",
"read-pkg": "5.2.0",
"semver": "7.5.4",
"semver": "7.6.0",
"source-map-support": "0.5.21",
"teen_process": "2.1.1",
"type-fest": "4.8.3",
"type-fest": "4.10.1",
"typescript": "5.2.2",
"yaml": "2.3.4",
"yargs": "17.7.2",
+2 -2
View File
@@ -1,5 +1,5 @@
mkdocs==1.5.3
mkdocs-git-revision-date-localized-plugin==1.2.1
mkdocs-material==9.5.2
mkdocs-git-revision-date-localized-plugin==1.2.4
mkdocs-material==9.5.9
mkdocs-redirects==1.2.1
mike==2.0.0
+26
View File
@@ -3,6 +3,32 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [0.4.21](https://github.com/appium/appium/compare/@appium/driver-test-support@0.4.20...@appium/driver-test-support@0.4.21) (2024-02-06)
### Bug Fixes
* **driver-test-support:** update dependency chai to v4.4.0 ([f7e4731](https://github.com/appium/appium/commit/f7e47316c0133a5bc6b658cfb97c1caa5d4272f7))
* **driver-test-support:** update dependency chai to v4.4.1 ([ca54b4f](https://github.com/appium/appium/commit/ca54b4f79ac27f4c6bceaae5d97175afbdbea460))
* **support:** update dependency axios to v1.6.4 ([332cc48](https://github.com/appium/appium/commit/332cc48a09b5532a8d51f85f3a24785e2c754e00))
* **support:** update dependency axios to v1.6.5 ([#19616](https://github.com/appium/appium/issues/19616)) ([ac73522](https://github.com/appium/appium/commit/ac73522351b31bd6c11972c61daa8b6b8d18fb91))
* **support:** update dependency axios to v1.6.6 ([6313704](https://github.com/appium/appium/commit/6313704ee5a8ee3aee726eb512ef259b6fa1041c))
* **support:** update dependency axios to v1.6.7 ([795092a](https://github.com/appium/appium/commit/795092a97f6d7569cccc4b5c166f52fef821514b))
* **types:** update dependency type-fest to v4.10 ([#19694](https://github.com/appium/appium/issues/19694)) ([966d305](https://github.com/appium/appium/commit/966d305e5eade9369a3875243bcad951df88545c))
* **types:** update dependency type-fest to v4.10.1 ([#19703](https://github.com/appium/appium/issues/19703)) ([501395c](https://github.com/appium/appium/commit/501395c9489320b84ab49ff78af4270f66070d62))
## [0.4.20](https://github.com/appium/appium/compare/@appium/driver-test-support@0.4.19...@appium/driver-test-support@0.4.20) (2024-01-03)
### Bug Fixes
* **support:** update dependency axios to v1.6.3 ([441b284](https://github.com/appium/appium/commit/441b2848dae28472356f37fc5d51ac27af7bbe29))
* **types:** update dependency type-fest to v4 ([#19592](https://github.com/appium/appium/issues/19592)) ([94b3580](https://github.com/appium/appium/commit/94b358022fdba3050ef94c1f881895f07e24fb75))
## [0.4.19](https://github.com/appium/appium/compare/@appium/driver-test-support@0.4.18...@appium/driver-test-support@0.4.19) (2023-12-18)
+5 -5
View File
@@ -1,6 +1,6 @@
{
"name": "@appium/driver-test-support",
"version": "0.4.19",
"version": "0.4.21",
"description": "Test utilities for Appium drivers",
"keywords": [
"automation",
@@ -41,18 +41,18 @@
},
"types": "./build/lib/index.d.ts",
"dependencies": {
"@appium/types": "^0.15.0",
"@appium/types": "^0.16.1",
"@types/lodash": "4.14.202",
"@types/stoppable": "1.1.3",
"axios": "1.6.2",
"axios": "1.6.7",
"bluebird": "3.7.2",
"chai": "4.3.10",
"chai": "4.4.1",
"get-port": "5.1.1",
"lodash": "4.17.21",
"sinon": "17.0.1",
"source-map-support": "0.5.21",
"stoppable": "1.1.0",
"type-fest": "4.8.3"
"type-fest": "4.10.1"
},
"peerDependencies": {
"appium": "^2.0.0-beta.43",
+1 -1
View File
@@ -1,4 +1,4 @@
# @appium/eslint-config-appium
# @appium/eslint-config-appium-ts
> Provides a reusable [ESLint](http://eslint.org/) [shared configuration](http://eslint.org/docs/developer-guide/shareable-configs) for [Appium](https://github.com/appium/appium) and Appium-adjacent projects (for TypeScript)
+3
View File
@@ -2,6 +2,9 @@
> Provides a reusable [ESLint](http://eslint.org/) [shared configuration](http://eslint.org/docs/developer-guide/shareable-configs) for [Appium](https://github.com/appium/appium) and Appium-adjacent projects.
[![NPM version](http://img.shields.io/npm/v/@appium/eslint-config-appium.svg)](https://npmjs.org/package/@appium/eslint-config-appium)
[![Downloads](http://img.shields.io/npm/dm/@appium/eslint-config-appium.svg)](https://npmjs.org/package/@appium/eslint-config-appium)
## Usage
Install the package with **`npm` v8 or newer**:
@@ -3,6 +3,42 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [3.0.25](https://github.com/appium/appium/compare/@appium/execute-driver-plugin@3.0.24...@appium/execute-driver-plugin@3.0.25) (2024-02-13)
### Bug Fixes
* **execute-driver-plugin:** update dependency webdriverio to v8.31.1 ([db0eec0](https://github.com/appium/appium/commit/db0eec05c58fe970351a309a01507cad824bdad2))
## [3.0.24](https://github.com/appium/appium/compare/@appium/execute-driver-plugin@3.0.23...@appium/execute-driver-plugin@3.0.24) (2024-02-06)
### Bug Fixes
* **execute-driver-plugin:** update dependency webdriverio to v8.27.2 ([35f1dba](https://github.com/appium/appium/commit/35f1dbabf6e852c4ae84ec128879bc7e61321a16))
* **execute-driver-plugin:** update dependency webdriverio to v8.28.0 ([e8e0444](https://github.com/appium/appium/commit/e8e0444bd28d94963090065a813a4b10be665caa))
* **execute-driver-plugin:** update dependency webdriverio to v8.28.6 ([da84efc](https://github.com/appium/appium/commit/da84efc42dd62b2be07a52dd4379791e2cef6eb2))
* **execute-driver-plugin:** update dependency webdriverio to v8.28.8 ([1fdf117](https://github.com/appium/appium/commit/1fdf117c63f6e9871f4de3c0bd93ecbdb13a9235))
* **execute-driver-plugin:** update dependency webdriverio to v8.29.0 ([6f0101e](https://github.com/appium/appium/commit/6f0101eb6343eb8b372e00a6b71d2b2ee909404c))
* **execute-driver-plugin:** update dependency webdriverio to v8.29.1 ([fb0f8f7](https://github.com/appium/appium/commit/fb0f8f776c6a03174f4569efa60d817a930809cf))
* **execute-driver-plugin:** update dependency webdriverio to v8.29.3 ([6dacb5e](https://github.com/appium/appium/commit/6dacb5e70ca3529a080f629f5245c2b676746e2c))
* **execute-driver-plugin:** update dependency webdriverio to v8.29.7 ([52a6f96](https://github.com/appium/appium/commit/52a6f96e085ff13acdf8d5013673861b7e0da818))
* **execute-driver-plugin:** update dependency webdriverio to v8.30.0 ([91320dd](https://github.com/appium/appium/commit/91320ddd02dca89a8cec258d0ab44a49d344d71c))
## [3.0.23](https://github.com/appium/appium/compare/@appium/execute-driver-plugin@3.0.22...@appium/execute-driver-plugin@3.0.23) (2024-01-03)
### Bug Fixes
* **execute-driver-plugin:** update dependency webdriverio to v8.26.3 ([ff767b7](https://github.com/appium/appium/commit/ff767b7f25e97ec9643f691b57ca6ba159dcdcf9))
* **execute-driver-plugin:** update dependency webdriverio to v8.27.0 ([9b06b87](https://github.com/appium/appium/commit/9b06b871eb3d9c60428130442ea7ab433ce4df31))
## [3.0.22](https://github.com/appium/appium/compare/@appium/execute-driver-plugin@3.0.21...@appium/execute-driver-plugin@3.0.22) (2023-12-18)
+42
View File
@@ -0,0 +1,42 @@
# @appium/execute-driver-plugin
> Appium plugin for running a driver script in a child process
[![NPM version](http://img.shields.io/npm/v/@appium/execute-driver-plugin.svg)](https://npmjs.org/package/@appium/execute-driver-plugin)
[![Downloads](http://img.shields.io/npm/dm/@appium/execute-driver-plugin.svg)](https://npmjs.org/package/@appium/execute-driver-plugin)
This plugin adds a new driver command that allows executing scripts in a child process. Currently,
the only supported driver type is `webdriverio`, therefore the script must also be written in JS.
## Motivation
Running a driver script in a child process adds a degree of parallelisation, which may result in
faster test execution.
## Installation
```
appium plugin install execute-driver
```
The plugin must be explicitly activated when launching the Appium server. Since the input script
can be arbitrary JavaScript, this is an insecure feature, and must also be explicitly enabled:
```
appium --use-plugins=execute-driver --allow-insecure=execute_driver_script
```
## Usage
```js
const script = `return await driver.getTimeouts();`;
const {result, logs} = await driver.executeDriverScript(script);
// 'result' contains the data returned by the script (in this case, the response to 'getTimeouts')
// 'logs' contains everything logged to console during script execution
```
Refer to your Appium client documentation for the exact syntax of this command.
## License
Apache-2.0
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@appium/execute-driver-plugin",
"version": "3.0.22",
"version": "3.0.25",
"description": "Plugin for batching and executing driver commands with Appiums",
"keywords": [
"automation",
@@ -43,7 +43,7 @@
"lodash": "4.17.21",
"source-map-support": "0.5.21",
"vm2": "3.9.19",
"webdriverio": "8.26.2"
"webdriverio": "8.31.1"
},
"peerDependencies": {
"appium": "^2.0.0-beta.35"
+29
View File
@@ -3,6 +3,35 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [5.5.1](https://github.com/appium/appium/compare/@appium/fake-driver@5.5.0...@appium/fake-driver@5.5.1) (2024-02-13)
### Bug Fixes
* **fake-driver:** fix dependencies ([#19782](https://github.com/appium/appium/issues/19782)) ([889a533](https://github.com/appium/appium/commit/889a533eef8b7a0805dd786d358f5729e03f0c40))
## [5.5.0](https://github.com/appium/appium/compare/@appium/fake-driver@5.4.0...@appium/fake-driver@5.5.0) (2024-02-06)
### Features
* **appium:** Set a proper exit code if any of required doctor checks fails ([#19617](https://github.com/appium/appium/issues/19617)) ([f4011f1](https://github.com/appium/appium/commit/f4011f17d4d03bc854c43462848a4c38427acb85))
## [5.4.0](https://github.com/appium/appium/compare/@appium/fake-driver@5.3.0...@appium/fake-driver@5.4.0) (2024-01-03)
### Features
* add webdriver bidi support ([2b21e66](https://github.com/appium/appium/commit/2b21e66891e8ab8c3929f04f32e94eb4efdba691))
* **appium,support:** Add common shortcuts for doctor checks ([#19562](https://github.com/appium/appium/issues/19562)) ([893b9e1](https://github.com/appium/appium/commit/893b9e15d64d2356ea339596e805543df51fa505))
* **appium:** Make doctor extensible ([#19542](https://github.com/appium/appium/issues/19542)) ([a30286b](https://github.com/appium/appium/commit/a30286b6e7b9753f73e55a8f9db14211b3124578))
## [5.3.0](https://github.com/appium/appium/compare/@appium/fake-driver@5.2.11...@appium/fake-driver@5.3.0) (2023-12-18)
+4 -17
View File
@@ -1,20 +1,7 @@
appium-fake-driver
===================
# @appium/fake-driver
[![Greenkeeper badge](https://badges.greenkeeper.io/appium/appium-fake-driver.svg)](https://greenkeeper.io/)
> Fake Appium driver for internal testing
Issues for this repo are disabled. Log any issues at the [main Appium repo's issue tracker](https://github.com/appium/appium/issues).
## License
Work in progress, stay tuned!
## Watch
```
npm run watch
```
## Test
```
npm test
```
Apache-2.0
+32
View File
@@ -0,0 +1,32 @@
/* eslint-disable @typescript-eslint/no-var-requires */
const {doctor} = require('appium/support');
/** @satisfies {import('@appium/types').IDoctorCheck} */
class EnvVarAndPathCheck {
/**
* @param {string} varName
*/
constructor(varName) {
this.varName = varName;
}
async diagnose() {
return doctor.ok(`${this.varName} environment variable is always set because it's fake`);
}
async fix() {
return (
`Make sure the environment variable ${this.varName} is properly configured for the Appium server process`
);
}
hasAutofix() {
return false;
}
isOptional() {
return false;
}
}
module.exports = {EnvVarAndPathCheck};
+6
View File
@@ -0,0 +1,6 @@
/* eslint-disable @typescript-eslint/no-var-requires */
const {EnvVarAndPathCheck} = require('./common');
const fakeCheck1 = new EnvVarAndPathCheck('FAKE1');
module.exports = {fakeCheck1};
+6
View File
@@ -0,0 +1,6 @@
/* eslint-disable @typescript-eslint/no-var-requires */
const {EnvVarAndPathCheck} = require('./common');
const fakeCheck2 = new EnvVarAndPathCheck('FAKE2');
module.exports = {fakeCheck2};
+1 -1
View File
@@ -3,7 +3,7 @@ import {Element} from '@appium/types';
import {errors} from 'appium/driver';
import {FakeElement} from '../fake-element';
import {FakeDriver} from '../driver';
import {util} from '@appium/support';
import {util} from 'appium/support';
import {mixin} from './mixin';
const {W3C_WEB_ELEMENT_IDENTIFIER} = util;
@@ -23,6 +23,9 @@ interface FakeDriverGeneralMixin {
execute(script: string, args: any[]): Promise<any>;
fakeAddition(a: number, b: number, c?: number): Promise<number>;
getLog(type: string): Promise<any>;
getUrl(): Promise<string>;
bidiNavigate(context: string, url: string): Promise<void>;
}
declare module '../driver' {
@@ -111,6 +114,14 @@ const GeneralMixin: FakeDriverGeneralMixin = {
async fakeAddition(this: FakeDriver, num1: number, num2: number, num3 = 0) {
return num1 + num2 + (num3 ?? 0);
},
async getUrl() {
return this.url;
},
async bidiNavigate(context: string, url: string) {
this.url = url;
},
};
mixin(GeneralMixin);
+42
View File
@@ -10,6 +10,9 @@ const FAKE_DRIVER_CONSTRAINTS = /** @type {const} */ ({
uniqueApp: {
isBoolean: true,
},
runClock: {
isBoolean: true,
},
});
/**
@@ -53,6 +56,12 @@ export class FakeDriver extends BaseDriver {
/** @type {Record<string,import('./fake-element').FakeElement>} */
elMap;
/** @type {string|null} */
_bidiProxyUrl;
/** @type {boolean} */
_clockRunning = false;
constructor(
opts = /** @type {import('@appium/types').InitialOpts} */ ({}),
shouldValidateCaps = true,
@@ -67,6 +76,8 @@ export class FakeDriver extends BaseDriver {
this.shook = false;
this.appModel = new FakeApp();
this.desiredCapConstraints = FAKE_DRIVER_CONSTRAINTS;
this.doesSupportBidi = true;
this._bidiProxyUrl = null;
}
proxyActive() {
@@ -77,6 +88,10 @@ export class FakeDriver extends BaseDriver {
return true;
}
get bidiProxyUrl() {
return this._bidiProxyUrl;
}
proxyReqRes(req, res) {
// fake implementation of jwp proxy req res
res.set('content-type', 'application/json');
@@ -123,9 +138,21 @@ export class FakeDriver extends BaseDriver {
);
this.caps = caps;
await this.appModel.loadApp(caps.app);
if (this.caps.runClock) {
this.startClock();
}
return [sessionId, caps];
}
/**
* @param {string} [sessionId]
* @returns {Promise<void>}
*/
async deleteSession(sessionId) {
this.stopClock();
return await super.deleteSession(sessionId);
}
get driverData() {
return {
isUnique: !!this.caps.uniqueApp,
@@ -137,6 +164,21 @@ export class FakeDriver extends BaseDriver {
return this.fakeThing;
}
async startClock() {
this._clockRunning = true;
while (this._clockRunning) {
await B.delay(500);
this.eventEmitter.emit('bidiEvent', {
method: 'clock.currentTime',
params: {time: Date.now()},
});
}
}
stopClock() {
this._clockRunning = false;
}
/**
* Set the 'thing' value (so that it can be retrieved later)
*
+8 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@appium/fake-driver",
"version": "5.3.0",
"version": "5.5.1",
"description": "Mock driver used internally by Appium for testing. Ignore",
"keywords": [
"automation",
@@ -51,7 +51,7 @@
"xpath": "0.0.34"
},
"peerDependencies": {
"appium": "^2.0.0-beta.35"
"appium": "^2.4.1"
},
"engines": {
"node": "^14.17.0 || ^16.13.0 || >=18.0.0",
@@ -73,6 +73,12 @@
"fake-error": "./build/lib/scripts/fake-error.js",
"fake-success": "./build/lib/scripts/fake-success.js",
"fake-stdin": "./build/lib/scripts/fake-stdin.js"
},
"doctor": {
"checks": [
"./doctor/fake1.js",
"./doctor/fake2.js"
]
}
},
"main": "./build/lib/index.js",
+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.32](https://github.com/appium/appium/compare/@appium/fake-plugin@3.1.31...@appium/fake-plugin@3.1.32) (2024-02-13)
**Note:** Version bump only for package @appium/fake-plugin
## [3.1.31](https://github.com/appium/appium/compare/@appium/fake-plugin@3.1.30...@appium/fake-plugin@3.1.31) (2024-02-06)
**Note:** Version bump only for package @appium/fake-plugin
## [3.1.30](https://github.com/appium/appium/compare/@appium/fake-plugin@3.1.29...@appium/fake-plugin@3.1.30) (2024-01-03)
**Note:** Version bump only for package @appium/fake-plugin
## [3.1.29](https://github.com/appium/appium/compare/@appium/fake-plugin@3.1.28...@appium/fake-plugin@3.1.29) (2023-12-18)
+6 -2
View File
@@ -1,3 +1,7 @@
# Appium Fake Plugin
# @appium/fake-plugin
This is a "fake" Appium 2.0 plugin used for testing and examples. There's no point in documenting its functionality since it's not meant to be used. If you're here at all, it's probably because you want to peek at the source code to see how a plugin is designed. So go ahead and do that!
> Fake Appium plugin for internal testing
## License
Apache-2.0
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@appium/fake-plugin",
"version": "3.1.29",
"version": "3.1.32",
"description": "A fake Appium 2.0 plugin",
"keywords": [
"automation",
@@ -39,8 +39,8 @@
"test:unit": "mocha \"./test/unit/**/*.spec.js\""
},
"dependencies": {
"@appium/base-plugin": "^2.2.25",
"@appium/support": "^4.1.11",
"@appium/base-plugin": "^2.2.28",
"@appium/support": "^4.2.2",
"@types/bluebird": "3.5.42",
"bluebird": "3.7.2",
"lodash": "4.17.21",
+25
View File
@@ -3,6 +3,31 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [3.0.3](https://github.com/appium/appium/compare/@appium/images-plugin@3.0.2...@appium/images-plugin@3.0.3) (2024-02-13)
**Note:** Version bump only for package @appium/images-plugin
## [3.0.2](https://github.com/appium/appium/compare/@appium/images-plugin@3.0.1...@appium/images-plugin@3.0.2) (2024-02-06)
### Bug Fixes
* **images-plugin:** update dependency lru-cache to v10 ([#19723](https://github.com/appium/appium/issues/19723)) ([202da83](https://github.com/appium/appium/commit/202da83449073ddb3da8fb2cfbcfce482a5ecd40))
## [3.0.1](https://github.com/appium/appium/compare/@appium/images-plugin@3.0.0...@appium/images-plugin@3.0.1) (2024-01-03)
**Note:** Version bump only for package @appium/images-plugin
## [3.0.0](https://github.com/appium/appium/compare/@appium/images-plugin@2.1.8...@appium/images-plugin@3.0.0) (2023-12-18)
+9 -17
View File
@@ -1,35 +1,27 @@
# Appium Images Plugin
# @appium/images-plugin
This is an official Appium plugin designed to facilitate image comparison, visual testing, and image-based functional testing.
> Appium plugin for image comparison, visual testing, and image-based functional testing
[![NPM version](http://img.shields.io/npm/v/@appium/images-plugin.svg)](https://npmjs.org/package/@appium/images-plugin)
[![Downloads](http://img.shields.io/npm/dm/@appium/images-plugin.svg)](https://npmjs.org/package/@appium/images-plugin)
## Features
1. **Image Comparison** ([docs](./docs/image-comparison.md)) - A new Appium command and route that allows sending in two different images and comparing them in various ways.
2. **Finding Elements by Image** ([docs](./docs/find-by-image.md)) - Using a template image, find a matching screen region of an app and interact with it via standard Appium element semantics.
## Prerequisites
* Appium Server 2.0+
## Installation - Server
Install the plugin using Appium's plugin CLI:
## Installation
```
appium plugin install images
```
## Installation - Client
No special action is needed to make the features available in the various Appium clients, as this plugin used to be a core Appium feature and its commands are already supported in the official clients.
## Activation
The plugin will not be active unless turned on when invoking the Appium server:
The plugin must be explicitly activated when launching the Appium server:
```
appium --use-plugins=images
```
## Usage
## License
Check the docs links above for usage for each of the features provided.
Apache-2.0

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