Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
74292e2292 | ||
|
|
8ccaf09877 | ||
|
|
d3d77a38df | ||
|
|
0149490522 | ||
|
|
16015de1b5 | ||
|
|
b7999aed03 | ||
|
|
1fa8c54765 | ||
|
|
8565209fda | ||
|
|
f8819836b5 | ||
|
|
ac912606e7 | ||
|
|
dcac55711c | ||
|
|
821ed01ae2 | ||
|
|
62fe09d563 | ||
|
|
8894b9adf7 | ||
|
|
44e423110a | ||
|
|
41a18f3b68 | ||
|
|
c2f3a618e4 | ||
|
|
5ce3b0c1aa | ||
|
|
177dff4f0e | ||
|
|
e7665d1cd9 | ||
|
|
34b636082f | ||
|
|
06607da92e | ||
|
|
9d6bbdb13e | ||
|
|
234ad59798 | ||
|
|
fe1b926886 | ||
|
|
5281f5a01a | ||
|
|
86f843b9e1 | ||
|
|
5226fa897b | ||
|
|
19928e9b90 | ||
|
|
e43c7a4dce | ||
|
|
82e40c1400 | ||
|
|
f3d959ef91 | ||
|
|
e8a838417a | ||
|
|
9773a23b26 | ||
|
|
cc10b65ee5 | ||
|
|
530045ec53 | ||
|
|
8e0fa2c1b3 | ||
|
|
d0d7548576 | ||
|
|
c7198b13e4 | ||
|
|
4279cc6cee | ||
|
|
a746eefbbb | ||
|
|
b6de85359d | ||
|
|
f2c8b69608 | ||
|
|
87fc50c2af | ||
|
|
f25079354b | ||
|
|
189bce88a0 | ||
|
|
15234bc055 | ||
|
|
073447ae4a | ||
|
|
dd070dc51d | ||
|
|
2882e71631 | ||
|
|
c106326169 | ||
|
|
5203480df6 | ||
|
|
52ae52a90a | ||
|
|
d5e5186cb7 | ||
|
|
541246184d | ||
|
|
faf9623a65 | ||
|
|
bc388db111 | ||
|
|
bdea13130c | ||
|
|
cc23c22b60 | ||
|
|
f675abc27b | ||
|
|
6fa0ed57ff | ||
|
|
57bbb7f46d | ||
|
|
d2b24daba0 | ||
|
|
14d27fe2a4 | ||
|
|
19845531f5 | ||
|
|
3cc878d835 | ||
|
|
4345a40a15 | ||
|
|
89e5f7e8db | ||
|
|
9469aaef0e | ||
|
|
2b12d5974a | ||
|
|
45cc948671 | ||
|
|
192f1f3793 | ||
|
|
5baf93672d | ||
|
|
c670010ec7 | ||
|
|
4a07f82cec | ||
|
|
b28b524957 | ||
|
|
f40598161c | ||
|
|
0ddedec0c4 | ||
|
|
def3474b05 | ||
|
|
1b9c1e96a3 | ||
|
|
e60e840316 | ||
|
|
6505d7921a | ||
|
|
def655cb39 | ||
|
|
1828501c66 | ||
|
|
6a08c95fc9 | ||
|
|
a156bc5bf5 | ||
|
|
dbc3b668a0 | ||
|
|
69bbb5370e | ||
|
|
ea00626091 | ||
|
|
1e86537e94 | ||
|
|
93c271ec5d | ||
|
|
0363aab8ba | ||
|
|
dd421b397b | ||
|
|
8ee7d07af4 | ||
|
|
1c19a8dbba | ||
|
|
1ed646cc8f | ||
|
|
d4ce2a82d9 | ||
|
|
40def9dbdb | ||
|
|
b3f8a47c2d |
@@ -63,6 +63,6 @@ appium.zip
|
||||
**/build-fixtures/**
|
||||
*.tsbuildinfo
|
||||
packages/*/LICENSE
|
||||
\!packages/*/LICENSE
|
||||
local_appium_home
|
||||
*.tgz
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
require: [require.resolve('./test/setup.js')],
|
||||
require: ['ts-node/register'],
|
||||
// forbids use of .only() in CI
|
||||
forbidOnly: Boolean(process.env.CI),
|
||||
color: true,
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
<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">
|
||||
<img alt="Appium" src="https://raw.githubusercontent.com/appium/appium/master/packages/appium/docs/overrides/assets/images/appium-logo-horiz.png" width="500">
|
||||
</a>
|
||||
</p>
|
||||
<p align="center">
|
||||
@@ -185,14 +185,29 @@ and wants to give back financially to the project, we use these funds to [encour
|
||||
contributions](GOVERNANCE.md#compensation-scheme), as well as support other open source projects we
|
||||
rely on. [Become a sponsor](https://opencollective.com/appium) via our OpenCollective page.
|
||||
|
||||
### Development Partners
|
||||
### Development and Strategic Partners
|
||||
|
||||
Appium is incredibly grateful to our Development Partners for their sustained contribution of
|
||||
project development and leadership!
|
||||
Appium is incredibly grateful to our Development and Strategic Partners for their sustained
|
||||
contribution of project development and leadership!
|
||||
|
||||
<p align="center">
|
||||
<a href="https://www.headspin.io/solutions/appium-mobile-test-automation"><img src="packages/appium/docs/assets/images/sponsor-logo-headspin.png"
|
||||
width="300" alt="HeadSpin" /></a>
|
||||
<a href="https://www.headspin.io/solutions/appium-mobile-test-automation"><img src="packages/appium/docs/overrides/assets/images/sponsor-logo-headspin.png"
|
||||
width="275" alt="HeadSpin" /></a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://www.browserstack.com/browserstack-appium?utm_campaigncode=701OW00000AoUTQYA3&utm_medium=partnered&utm_source=appium">
|
||||
<picture>
|
||||
<source srcset="packages/appium/docs/overrides/assets/images/sponsor-logo-browserstack-dark.png" media="(prefers-color-scheme: dark)"/>
|
||||
<source srcset="packages/appium/docs/overrides/assets/images/sponsor-logo-browserstack-light.png" media="(prefers-color-scheme: light)"/>
|
||||
<img src="packages/appium/docs/overrides/assets/images/sponsor-logo-browserstack-dark.png" width="300" alt="Browserstack"/>
|
||||
</picture>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://saucelabs.com/resources/blog/appium-strategic-partner"><img src="packages/appium/docs/overrides/assets/images/sponsor-logo-sauce.png"
|
||||
width="300" alt="Sauce Labs" /></a>
|
||||
</p>
|
||||
|
||||
### Other Sponsors
|
||||
|
||||
@@ -12,7 +12,7 @@ We are very grateful for the support of our Development Partners, who donate App
|
||||
maintenance as part of their employees' core job duties!
|
||||
|
||||
<a href="https://www.headspin.io/solutions/appium-mobile-test-automation" target="_blank"><img
|
||||
src="packages/appium/docs/assets/images/sponsor-logo-headspin.png" width="250"/></a>
|
||||
src="packages/appium/docs/overrides/assets/images/sponsor-logo-headspin.png" width="250"/></a>
|
||||
|
||||
## Strategic Partners
|
||||
|
||||
@@ -20,7 +20,14 @@ We are very grateful for the financial and marketing support of our exclusive St
|
||||
who contribute a significant ongoing investment of funds to help the project attract and reward
|
||||
contributors!
|
||||
|
||||
* *Coming Soon!*
|
||||
<a href="https://www.browserstack.com/browserstack-appium?utm_campaigncode=701OW00000AoUTQYA3&utm_medium=partnered&utm_source=appium">
|
||||
<picture>
|
||||
<source srcset="packages/appium/docs/overrides/assets/images/sponsor-logo-browserstack-dark.png" media="(prefers-color-scheme: dark)"/>
|
||||
<source srcset="packages/appium/docs/overrides/assets/images/sponsor-logo-browserstack-light.png" media="(prefers-color-scheme: light)"/>
|
||||
<img src="packages/appium/docs/overrides/assets/images/sponsor-logo-browserstack-dark.png" width="250" alt="Browserstack"/>
|
||||
</picture>
|
||||
</a>
|
||||
<a style="padding-left: 1em" href="https://saucelabs.com/resources/blog/appium-strategic-partner" target="_blank"><img src="packages/appium/docs/overrides/assets/images/sponsor-logo-sauce.png" width="250"/></a>
|
||||
|
||||
## Gold Sponsors
|
||||
|
||||
|
||||
@@ -59,12 +59,13 @@
|
||||
"start": "appium",
|
||||
"sync-pkgs": "run-p sync-pkgs:*",
|
||||
"sync-pkgs:appium-readme": "sync-monorepo-packages --force --no-package-json --packages=packages/appium README.md",
|
||||
"sync-pkgs:common-fields": "sync-monorepo-packages --fields=author,license,bugs,homepage,engines",
|
||||
"sync-pkgs:common-fields:default": "sync-monorepo-packages --packages=\"!packages/logger\" --fields=author,license,bugs,homepage,engines",
|
||||
"sync-pkgs:common-fields:logger": "sync-monorepo-packages --packages=\"packages/logger\" --fields=author,bugs,homepage,engines",
|
||||
"sync-pkgs:keywords": "sync-monorepo-packages --field=keywords --packages=\"packages/*\" --packages=\"!packages/eslint-config-appium\" --packages=\"!packages/types\" --packages=\"!packages/eslint-config-appium-ts\" ",
|
||||
"sync-pkgs:license": "sync-monorepo-packages --force --no-package-json LICENSE",
|
||||
"sync-pkgs:license": "sync-monorepo-packages --force --no-package-json --packages=\"!packages/logger\" LICENSE",
|
||||
"test": "run-s test:quick",
|
||||
"test:ci": "run-s test:smoke test:unit test:types test:e2e",
|
||||
"test:e2e": "lerna run --concurrency=2 test:e2e",
|
||||
"test:e2e": "lerna run test:e2e",
|
||||
"test:quick": "run-s lint test:unit test:types",
|
||||
"test:slow": "run-s test:quick test:smoke test:e2e",
|
||||
"test:smoke": "smoker --all test:smoke",
|
||||
@@ -85,8 +86,8 @@
|
||||
"@types/chai": "4.3.16",
|
||||
"@types/chai-as-promised": "7.1.8",
|
||||
"@types/diff": "5.2.1",
|
||||
"@types/mocha": "10.0.6",
|
||||
"@types/node": "20.14.2",
|
||||
"@types/mocha": "10.0.7",
|
||||
"@types/node": "20.14.10",
|
||||
"@types/semver": "7.5.8",
|
||||
"@types/sinon": "17.0.3",
|
||||
"@types/sinon-chai": "3.2.12",
|
||||
@@ -94,12 +95,11 @@
|
||||
"@types/teen_process": "2.0.4",
|
||||
"@types/ws": "8.5.10",
|
||||
"@types/yargs": "17.0.32",
|
||||
"@typescript-eslint/eslint-plugin": "7.12.0",
|
||||
"@typescript-eslint/parser": "7.12.0",
|
||||
"@typescript-eslint/eslint-plugin": "7.16.0",
|
||||
"@typescript-eslint/parser": "7.16.0",
|
||||
"asyncbox": "3.0.0",
|
||||
"chai": "4.4.1",
|
||||
"chai-as-promised": "7.1.2",
|
||||
"chai-webdriverio-async": "3.0.0",
|
||||
"chai": "5.1.1",
|
||||
"chai-as-promised": "8.0.0",
|
||||
"conventional-changelog-conventionalcommits": "7.0.2",
|
||||
"cpy-cli": "5.0.0",
|
||||
"eslint": "8.57.0",
|
||||
@@ -108,33 +108,31 @@
|
||||
"eslint-import-resolver-typescript": "3.6.1",
|
||||
"eslint-plugin-import": "2.29.1",
|
||||
"eslint-plugin-mocha": "10.4.3",
|
||||
"eslint-plugin-promise": "6.2.0",
|
||||
"eslint-plugin-promise": "6.4.0",
|
||||
"fancy-log": "2.0.0",
|
||||
"finalhandler": "1.2.0",
|
||||
"get-port": "5.1.1",
|
||||
"json-schema-to-typescript": "14.0.5",
|
||||
"lerna": "8.1.3",
|
||||
"json-schema-to-typescript": "14.1.0",
|
||||
"lerna": "8.1.6",
|
||||
"log-symbols": "4.1.0",
|
||||
"midnight-smoker": "8.0.0",
|
||||
"mjpeg-consumer": "2.0.0",
|
||||
"mjpeg-server": "0.3.1",
|
||||
"mocha": "10.4.0",
|
||||
"mocha-chai-jest-snapshot": "1.1.4",
|
||||
"npm-run-all2": "6.2.0",
|
||||
"prettier": "3.3.1",
|
||||
"mocha": "10.6.0",
|
||||
"npm-run-all2": "6.2.2",
|
||||
"prettier": "3.3.2",
|
||||
"rewiremock": "3.14.5",
|
||||
"rimraf": "5.0.7",
|
||||
"rimraf": "5.0.9",
|
||||
"serve-static": "1.15.0",
|
||||
"sinon": "18.0.0",
|
||||
"sinon-chai": "3.7.0",
|
||||
"sync-monorepo-packages": "1.0.2",
|
||||
"through2": "4.0.2",
|
||||
"ts-node": "10.9.2",
|
||||
"tsd": "0.31.0",
|
||||
"typescript": "5.4.5",
|
||||
"tsd": "0.31.1",
|
||||
"typescript": "5.5.3",
|
||||
"validate.js": "0.13.1",
|
||||
"webdriverio": "8.38.2",
|
||||
"ws": "8.17.0",
|
||||
"webdriverio": "8.39.1",
|
||||
"ws": "8.18.0",
|
||||
"yaml-js": "0.3.1"
|
||||
},
|
||||
"engines": {
|
||||
|
||||
@@ -3,6 +3,106 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [2.11.2](https://github.com/appium/appium/compare/appium@2.11.1...appium@2.11.2) (2024-07-10)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **appium:** update dependency ws to v8.18.0 ([44e4231](https://github.com/appium/appium/commit/44e423110ad6c8bd7d846cc334fafe931fea43f9))
|
||||
* **execute-driver-plugin:** update dependency webdriverio to v8.39.1 ([c2f3a61](https://github.com/appium/appium/commit/c2f3a618e413c7f21057ba6d9002dc1fe4507e6f))
|
||||
* **support:** update dependency teen_process to v2.1.10 ([86f843b](https://github.com/appium/appium/commit/86f843b9e1fc56576d7a08bf95838a58971e98b3))
|
||||
* **support:** update dependency teen_process to v2.1.7 ([5226fa8](https://github.com/appium/appium/commit/5226fa897b90e9fec9be3e79a7d48fc7b4e0016d))
|
||||
* **support:** update dependency teen_process to v2.2.0 ([06607da](https://github.com/appium/appium/commit/06607da92e93d015747fd6d6d5bdabe1b3ac58d2))
|
||||
* **types:** update dependency type-fest to v4.21.0 ([#20335](https://github.com/appium/appium/issues/20335)) ([8894b9a](https://github.com/appium/appium/commit/8894b9adf709646108cc8d6426bbb690550609f2))
|
||||
|
||||
|
||||
|
||||
## [2.11.1](https://github.com/appium/appium/compare/appium@2.11.0...appium@2.11.1) (2024-06-28)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **images-plugin:** update dependency lru-cache to v10.3.0 ([#20306](https://github.com/appium/appium/issues/20306)) ([e43c7a4](https://github.com/appium/appium/commit/e43c7a4dce1f50fbd4d028cbeac5677422210b20))
|
||||
|
||||
|
||||
|
||||
## [2.11.0](https://github.com/appium/appium/compare/appium@2.10.3...appium@2.11.0) (2024-06-27)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **appium:** Improve context logging ([#20250](https://github.com/appium/appium/issues/20250)) ([f675abc](https://github.com/appium/appium/commit/f675abc27b3e6beac2431cc71afb5fc2c2f70534))
|
||||
* **appium:** show extension update info message for newer major versions ([#20283](https://github.com/appium/appium/issues/20283)) ([f2c8b69](https://github.com/appium/appium/commit/f2c8b696085ba862553476f23509cac0674a447c))
|
||||
* **appium:** show spinner during extension uninstall ([#20282](https://github.com/appium/appium/issues/20282)) ([f250793](https://github.com/appium/appium/commit/f25079354bfd646810813ae47244a1e2e962dbfb))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **appium:** update dependency ws to v8.17.1 ([d2b24da](https://github.com/appium/appium/commit/d2b24daba056373160d680ec5f017b28e9cbde1a))
|
||||
* **execute-driver-plugin:** update dependency webdriverio to v8.39.0 ([faf9623](https://github.com/appium/appium/commit/faf9623a65868a6330d062076074da7c1c3acb2a))
|
||||
* **support:** update dependency teen_process to v2.1.5 ([bc388db](https://github.com/appium/appium/commit/bc388db1116f411f4b241105f5feae30418c9a27))
|
||||
* **support:** update dependency teen_process to v2.1.6 ([5412461](https://github.com/appium/appium/commit/541246184dc7521c825ec1902775e681ea6f0db3))
|
||||
* **types:** update dependency type-fest to v4.20.1 ([#20255](https://github.com/appium/appium/issues/20255)) ([1984553](https://github.com/appium/appium/commit/19845531f558e2b16dfae807c768e1b9f2cab25d))
|
||||
|
||||
|
||||
|
||||
## [2.10.3](https://github.com/appium/appium/compare/appium@2.10.2...appium@2.10.3) (2024-06-11)
|
||||
|
||||
**Note:** Version bump only for package appium
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.10.2](https://github.com/appium/appium/compare/appium@2.10.1...appium@2.10.2) (2024-06-11)
|
||||
|
||||
**Note:** Version bump only for package appium
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.10.1](https://github.com/appium/appium/compare/appium@2.10.0...appium@2.10.1) (2024-06-11)
|
||||
|
||||
**Note:** Version bump only for package appium
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.10.0](https://github.com/appium/appium/compare/appium@2.9.0...appium@2.10.0) (2024-06-10)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **appium:** Add a possibility to print logs in json format ([#20224](https://github.com/appium/appium/issues/20224)) ([1e86537](https://github.com/appium/appium/commit/1e86537e944f6beb7dbe72b35c267ae1dd9ebd36))
|
||||
* **appium:** Add session signature to all logs ([#20202](https://github.com/appium/appium/issues/20202)) ([#20214](https://github.com/appium/appium/issues/20214)) ([0363aab](https://github.com/appium/appium/commit/0363aab8ba4fe0ec49845db2f493001aa873578b)), closes [#20222](https://github.com/appium/appium/issues/20222)
|
||||
* **logger:** Add the debug level to the default logger ([#20219](https://github.com/appium/appium/issues/20219)) ([8ee7d07](https://github.com/appium/appium/commit/8ee7d07af4e2375d2eb7c23badaaac34685bc59c))
|
||||
* **support:** Move SecureValuesPreprocessor to @appum/logger ([#20228](https://github.com/appium/appium/issues/20228)) ([dbc3b66](https://github.com/appium/appium/commit/dbc3b668a0a7a815d23f1cae4207d435fc09034d))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **docutils:** update dependency lilconfig to v3.1.2 ([1828501](https://github.com/appium/appium/commit/1828501c66d0c5f8f295c67cd12d6691f8020780))
|
||||
* **docutils:** update dependency yaml to v2.4.5 ([6a08c95](https://github.com/appium/appium/commit/6a08c95fc9bda5cf39b8a6d98e5394658355a38e))
|
||||
* **types:** update dependency type-fest to v4.20.0 ([#20227](https://github.com/appium/appium/issues/20227)) ([ea00626](https://github.com/appium/appium/commit/ea00626091e3ce87f6e3ed7a61003295272835e3))
|
||||
|
||||
|
||||
|
||||
## [2.9.0](https://github.com/appium/appium/compare/appium@2.8.0...appium@2.9.0) (2024-06-06)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **appium:** Add session signature to all logs ([#20202](https://github.com/appium/appium/issues/20202)) ([b3f8a47](https://github.com/appium/appium/commit/b3f8a47c2d3fa029bdb5592d7130c6d1664e53b5))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **appium:** Revert changes in 20203 and 20202 ([#20209](https://github.com/appium/appium/issues/20209)) ([40def9d](https://github.com/appium/appium/commit/40def9dbdbde64706111900967d66735257b7404)), closes [#20202](https://github.com/appium/appium/issues/20202) [#20203](https://github.com/appium/appium/issues/20203)
|
||||
|
||||
|
||||
|
||||
## [2.8.0](https://github.com/appium/appium/compare/appium@2.7.0...appium@2.8.0) (2024-06-06)
|
||||
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
<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">
|
||||
<img alt="Appium" src="https://raw.githubusercontent.com/appium/appium/master/packages/appium/docs/overrides/assets/images/appium-logo-horiz.png" width="500">
|
||||
</a>
|
||||
</p>
|
||||
<p align="center">
|
||||
@@ -185,14 +185,29 @@ and wants to give back financially to the project, we use these funds to [encour
|
||||
contributions](GOVERNANCE.md#compensation-scheme), as well as support other open source projects we
|
||||
rely on. [Become a sponsor](https://opencollective.com/appium) via our OpenCollective page.
|
||||
|
||||
### Development Partners
|
||||
### Development and Strategic Partners
|
||||
|
||||
Appium is incredibly grateful to our Development Partners for their sustained contribution of
|
||||
project development and leadership!
|
||||
Appium is incredibly grateful to our Development and Strategic Partners for their sustained
|
||||
contribution of project development and leadership!
|
||||
|
||||
<p align="center">
|
||||
<a href="https://www.headspin.io/solutions/appium-mobile-test-automation"><img src="packages/appium/docs/assets/images/sponsor-logo-headspin.png"
|
||||
width="300" alt="HeadSpin" /></a>
|
||||
<a href="https://www.headspin.io/solutions/appium-mobile-test-automation"><img src="packages/appium/docs/overrides/assets/images/sponsor-logo-headspin.png"
|
||||
width="275" alt="HeadSpin" /></a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://www.browserstack.com/browserstack-appium?utm_campaigncode=701OW00000AoUTQYA3&utm_medium=partnered&utm_source=appium">
|
||||
<picture>
|
||||
<source srcset="packages/appium/docs/overrides/assets/images/sponsor-logo-browserstack-dark.png" media="(prefers-color-scheme: dark)"/>
|
||||
<source srcset="packages/appium/docs/overrides/assets/images/sponsor-logo-browserstack-light.png" media="(prefers-color-scheme: light)"/>
|
||||
<img src="packages/appium/docs/overrides/assets/images/sponsor-logo-browserstack-dark.png" width="300" alt="Browserstack"/>
|
||||
</picture>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://saucelabs.com/resources/blog/appium-strategic-partner"><img src="packages/appium/docs/overrides/assets/images/sponsor-logo-sauce.png"
|
||||
width="300" alt="Sauce Labs" /></a>
|
||||
</p>
|
||||
|
||||
### Other Sponsors
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
en/latest
|
||||
en/assets
|
||||
ja/assets
|
||||
zh/assets
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
---
|
||||
authors:
|
||||
- jlipps
|
||||
date: 2024-06-10
|
||||
---
|
||||
|
||||
# Announcing BrowserStack as Appium's Strategic Partner
|
||||
|
||||
We have another exciting announcement for you! As part of our recently-announced [sponsorship and
|
||||
contributor compensation
|
||||
program](https://appium.io/docs/en/latest/blog/2024/05/14/announcing-appiums-sponsorship-program/),
|
||||
we are obviously in need of significant funding to support giving back to our contributor
|
||||
community. We are therefore extremely happy to announce one of our brand new exclusive Strategic Partners: <a
|
||||
href="https://www.browserstack.com/browserstack-appium?utm_campaigncode=701OW00000AoUTQYA3&utm_medium=partnered&utm_source=appium">BrowserStack</a>!
|
||||
|
||||
<!-- more -->
|
||||
|
||||
<div style="text-align:center;width:100%">
|
||||
<a href="https://www.browserstack.com/browserstack-appium?utm_campaigncode=701OW00000AoUTQYA3&utm_medium=partnered&utm_source=appium">
|
||||
<img src="/docs/en/latest/assets/images/sponsor-logo-browserstack-dark.png#only-dark" width="300" />
|
||||
<img src="/docs/en/latest/assets/images/sponsor-logo-browserstack-light.png#only-light" width="300" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
BrowserStack has long been recognized in the industry as having extensive support for Appium,
|
||||
allowing you to run functional and visual regression tests for your native and hybrid apps at scale
|
||||
across a variety of devices and platforms.
|
||||
|
||||
As a token of this collaboration, BrowserStack is extending a special premium access of
|
||||
BrowserStack App Automate product to Appium users. Discover more about this special offer at
|
||||
[browserstack.com](https://www.browserstack.com/browserstack-appium?utm_campaigncode=701OW00000AoUTQYA3&utm_medium=partnered&utm_source=appium&utm_term=partannouncement)
|
||||
|
||||
With the decision to come on board as Strategic Partner, BrowserStack is making a huge investment
|
||||
in Appium and the Appium community. BrowserStack is very interested in supporting the development
|
||||
of the Appium contributor base, and is proud to be a key part of our contributor compensation
|
||||
scheme. Without Strategic Partners like BrowserStack, we would not be able to maintain such
|
||||
a program! We welcome them with gratitude!
|
||||
|
||||
**About BrowserStack**
|
||||
|
||||
<em>BrowserStack is the world’s leading software testing platform powering over two million tests every
|
||||
day across 19 global data centers. BrowserStack helps Tesco, Shell, NVIDIA, Discovery, Wells Fargo,
|
||||
and over 50,000 customers deliver quality software at speed by moving testing to their Cloud.
|
||||
BrowserStack’s platform provides instant access to 20,000+ real mobile devices on a highly reliable
|
||||
cloud infrastructure that effortlessly scales as testing needs grow. With BrowserStack, Dev and QA
|
||||
teams can move fast while delivering an amazing experience for every customer.</em>
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
authors:
|
||||
- jlipps
|
||||
date: 2024-06-10
|
||||
---
|
||||
|
||||
# Announcing Sauce Labs as Appium's Strategic Partner
|
||||
|
||||
The good news surrounding our new [sponsorship and contributor compensation
|
||||
program](https://appium.io/docs/en/latest/blog/2024/05/14/announcing-appiums-sponsorship-program/)
|
||||
just keeps coming! Effective immediately we are fortunate and excited to be able to welcome a new Strategic Partner to
|
||||
the Appium project: <a href="https://saucelabs.com/resources/blog/appium-strategic-partner">Sauce Labs</a>!
|
||||
|
||||
<!-- more -->
|
||||
|
||||
<div style="text-align:center;width:100%">
|
||||
<a href="https://saucelabs.com/resources/blog/appium-strategic-partner"><img src="/docs/en/latest/assets/images/sponsor-logo-sauce.png" style="width: 300px"/></a>
|
||||
</div>
|
||||
|
||||
Sauce Labs has a long history of support for the Appium project. In fact, Appium was initially
|
||||
incubated at Sauce Labs under the direction of [@jlipps](https://github.com/jlipps), in the early
|
||||
days of mobile automation. Sauce Labs always envisioned Appium as being a project for and by the
|
||||
community, and cemented this perspective in 2016 with the donation of Appium to the JS Foundation
|
||||
(now the [OpenJS Foundation](https://openjsf.org/)). To this day, developers associated with Sauce
|
||||
Labs continue to help maintain various parts of the Appium project.
|
||||
|
||||
Now, Sauce Labs is making a further commitment to support Appium financially as a way to enable
|
||||
our new contributor compensation program. We're very excited about this program as a vehicle for
|
||||
getting more people involved in Appium's development and maintenance. With the help of Strategic
|
||||
Partners like Sauce Labs, our project will be better able to meet the needs of our constantly
|
||||
growing userbase, and attract new developers (like you?) to build the Appium ecosystem together.
|
||||
Needless to say, we are extremely grateful for Sauce Labs's support!
|
||||
|
||||
**About Sauce Labs**
|
||||
|
||||
<em>Sauce Labs is the leader in continuous quality. Our Platform for Test helps organizations transform software testing from a chore to a competitive advantage. With decades of expertise and deep roots in the Selenium and Appium open source communities, high performing software teams use our platform to execute any testing workload across thousands of different devices, browsers, and operating systems—anywhere, any time, and at any scale. For more information, please visit [saucelabs.com](https://saucelabs.com).</em>
|
||||
@@ -38,6 +38,7 @@ below.
|
||||
|`--log`|Also send log output to this file|string||`-g`|
|
||||
|`--log-filters`|One or more log filtering rules|array|||
|
||||
|`--log-level`|Log level (console[:file]) (Value must be one of: `info`, `info:debug`, `info:info`, `info:warn`, `info:error`, `warn`, `warn:debug`, `warn:info`, `warn:warn`, `warn:error`, `error`, `error:debug`, `error:info`, `error:warn`, `error:error`, `debug`, `debug:debug`, `debug:info`, `debug:warn`, `debug:error`)|string|`debug`||
|
||||
|`--log-format`|Log format (Value must be to one of: `text`, `json`, `pretty_json`). If logs are printed as JSON then the text coloring is always disabled.|string|`text`||
|
||||
|`--log-no-colors`|Do not use color in console output|boolean|`false`||
|
||||
|`--log-timestamp`|Show timestamps in console output|boolean|`false`||
|
||||
|`--long-stacktrace`|Add long stack traces to log entries. Recommended for debugging only.|boolean|`false`||
|
||||
|
||||
@@ -43,6 +43,7 @@ These drivers are not maintained by the Appium team and can be used to target ad
|
||||
|[Tizen](https://github.com/Samsung/appium-tizen-driver)|`--source=npm appium-tizen-driver`|Android|Native|Community / Samsung|
|
||||
|[TizenTV](https://github.com/headspinio/appium-tizen-tv-driver)|`--source=npm appium-tizen-tv-driver`|Samsung TV|Web|HeadSpin|
|
||||
|[Youi](https://github.com/YOU-i-Labs/appium-youiengine-driver)|`--source=npm appium-youiengine-driver`|iOS, Android, macOS, Linux, tvOS|Native|Community / You.i|
|
||||
|[Flutter Integration](https://github.com/AppiumTestDistribution/appium-flutter-integration-driver)|`--source=npm appium-flutter-integration-driver`|Android, iOS|Native|Community / AppiumTestDistribution|
|
||||
|
||||
!!! note
|
||||
|
||||
|
||||
@@ -21,14 +21,28 @@ Android, Tizen), browser (Chrome, Firefox, Safari), desktop (macOS, Windows), TV
|
||||
Android TV, Samsung), and more!
|
||||
|
||||
|
||||
<div style="text-align: center">
|
||||
<div style="text-align: center; margin-top: 2rem; font-style: italic;">
|
||||
Appium is extremely grateful for the support of its key partners! (Learn more about our
|
||||
sponsorship program and contributor compensation scheme
|
||||
<a href="https://github.com/appium/appium/blob/master/GOVERNANCE.md#sponsorship">here</a>)
|
||||
<br/>
|
||||
<a href="https://www.headspin.io/solutions/appium-mobile-test-automation">
|
||||
<img src="assets/images/sponsor-logo-headspin.png" style="width: 200px;" />
|
||||
</a>
|
||||
sponsorship program and contributor compensation scheme <a
|
||||
href="https://github.com/appium/appium/blob/master/GOVERNANCE.md#sponsorship">here</a>)
|
||||
<div class="homepageSponsors">
|
||||
<div class="homepageSponsor">
|
||||
<a href="https://www.headspin.io/solutions/appium-mobile-test-automation">
|
||||
<img src="assets/images/sponsor-logo-headspin.png" style="width: 200px;" />
|
||||
</a>
|
||||
</div>
|
||||
<div class="homepageSponsor">
|
||||
<a href="https://www.browserstack.com/browserstack-appium?utm_campaigncode=701OW00000AoUTQYA3&utm_medium=partnered&utm_source=appium">
|
||||
<img src="assets/images/sponsor-logo-browserstack-dark.png#only-dark" style="width: 220px;" />
|
||||
<img src="assets/images/sponsor-logo-browserstack-light.png#only-light" style="width: 220px;" />
|
||||
</a>
|
||||
</div>
|
||||
<div class="homepageSponsor">
|
||||
<a href="https://saucelabs.com/resources/blog/appium-strategic-partner">
|
||||
<img src="assets/images/sponsor-logo-sauce.png" style="width: 200px;" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
## Explore the Documentation
|
||||
|
||||
@@ -47,7 +47,7 @@ another project called WebDriver) developed a relatively stable API for browser
|
||||
|
||||
Over the years, Selenium worked with various web browser vendors and the [W3C](https://w3c.org)
|
||||
standards group to turn its API into an official web browser standard, called the [WebDriver
|
||||
specification](https://w3c.github.io/webdriver/webdriver-spec.html). All the main browsers now
|
||||
specification](https://w3c.github.io/webdriver/). All the main browsers now
|
||||
implement automation capabilities inline with the WebDriver spec, without the Selenium team having
|
||||
to maintain any software that performs actual automation; standards for the win!
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ title: Intro to Appium Clients
|
||||
---
|
||||
|
||||
For all the reasons discussed in the [main Overview](./appium.md), Appium is based on the [W3C
|
||||
WebDriver specification](https://w3c.github.io/webdriver/webdriver-spec.html). This means that
|
||||
WebDriver specification](https://w3c.github.io/webdriver/). This means that
|
||||
Appium implements a client-server architecture. The server (consisting of Appium itself along with
|
||||
any drivers or plugins you are using for automation) is connected to the devices under test, and
|
||||
is actually responsible for making automation happen on those devices. The client (driven by *you*,
|
||||
|
||||
@@ -37,7 +37,7 @@ their WebDriver protocol equivalents.
|
||||
|
||||
So let's say I wanted to do something with this empty driver; first I have to decide which
|
||||
WebDriver command I want to implement. For our example, let's take the [Navigate
|
||||
To](https://www.w3.org/TR/webdriver1/#navigate-to) WebDriver command. Leave aside for the moment
|
||||
To](https://w3c.github.io/webdriver/#navigate-to) WebDriver command. Leave aside for the moment
|
||||
what I want to have the driver *do* when this command is executed. To tell Appium the driver can
|
||||
handle the command, all we have to do is define a method like this in our driver class:[^1]
|
||||
|
||||
@@ -96,7 +96,7 @@ a driver becomes the task of mapping the WebDriver protocol to this underlying A
|
||||
a set of different underlying APIs--for example, the UiAutomator2 driver relies not only on the
|
||||
[UiAutomator2](https://developer.android.com/training/testing/other-components/ui-automator)
|
||||
technology from Google, but also functions only available through
|
||||
[ADB](https://developer.android.com/studio/command-line/adb), as well as functions only available
|
||||
[ADB](https://developer.android.com/tools/adb), as well as functions only available
|
||||
via the Android SDK inside a helper app). Tying it all together into a single, usable, WebDriver
|
||||
interface is the incredibly useful (but incredibly challenging) art of driver development!
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 9.9 KiB |
@@ -7,12 +7,25 @@
|
||||
|
||||
.appium-sponsor-thanks {
|
||||
width: 100%;
|
||||
margin: auto 0.6rem;
|
||||
margin: 0.5rem 0.6rem auto 0.6rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.appium-sponsor-thanks img {
|
||||
margin: auto 6px;
|
||||
height: 50px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.homepageSponsors {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin: 10px auto;
|
||||
max-width: 800px;
|
||||
}
|
||||
|
||||
.homepageSponsor {
|
||||
flex: 1;
|
||||
}
|
||||
@@ -1,6 +1,15 @@
|
||||
<div class="appium-sponsor-thanks">
|
||||
Supported by
|
||||
|
||||
<a href="https://www.headspin.io/solutions/appium-mobile-test-automation">
|
||||
<img src="/docs/en/latest/assets/images/sponsor-logo-headspin.png" />
|
||||
</a>
|
||||
|
||||
<a href="https://www.browserstack.com/browserstack-appium?utm_campaigncode=701OW00000AoUTQYA3&utm_medium=partnered&utm_source=appium">
|
||||
<img src="/docs/en/latest/assets/images/sponsor-logo-browserstack-dark.png" />
|
||||
</a>
|
||||
|
||||
<a href="https://saucelabs.com/resources/blog/appium-strategic-partner">
|
||||
<img src="/docs/en/latest/assets/images/sponsor-logo-sauce.png" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -20,7 +20,6 @@ const {
|
||||
DOCS_REMOTE,
|
||||
LATEST_ALIAS,
|
||||
} = require('./utils');
|
||||
const copyAssets = require('./copy-assets');
|
||||
const path = require('path');
|
||||
const semver = require('semver');
|
||||
const {version} = require('../../package.json');
|
||||
@@ -35,8 +34,6 @@ const push = Boolean(process.env.APPIUM_DOCS_PUBLISH);
|
||||
async function main() {
|
||||
log.info(`Building Appium docs and committing to ${DOCS_BRANCH}`);
|
||||
|
||||
await copyAssets();
|
||||
|
||||
const semVersion = semver.parse(version);
|
||||
if (!semVersion) {
|
||||
throw new Error(`Could not parse Appium version "${version}" as semver`);
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
/**
|
||||
* This script copies generic assets from `ASSETS_DIR` to each docs language dir.
|
||||
*/
|
||||
|
||||
// @ts-check
|
||||
|
||||
// for simplicity this file is not transpiled and is run directly via an npm script
|
||||
/* eslint-disable promise/prefer-await-to-callbacks */
|
||||
/* eslint-disable promise/prefer-await-to-then */
|
||||
|
||||
const {fs} = require('@appium/support');
|
||||
const {log, LANGS, DOCS_DIR, ASSETS_DIR} = require('./utils');
|
||||
const path = require('path');
|
||||
|
||||
async function main() {
|
||||
log.info('Copying generic assets to docs language dirs');
|
||||
|
||||
for (const lang of LANGS) {
|
||||
const langPath = path.join(DOCS_DIR, lang);
|
||||
const langAssetPath = path.join(langPath, 'assets');
|
||||
log.info(`Deleting ${langAssetPath}`);
|
||||
await fs.rimraf(langAssetPath);
|
||||
log.info(`Copying assets into ${langAssetPath}`);
|
||||
await fs.copyFile(ASSETS_DIR, langAssetPath);
|
||||
}
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
main().catch((err) => {
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = main;
|
||||
@@ -578,7 +578,9 @@ class ExtensionCliCommand {
|
||||
return this.config.installedExtensions;
|
||||
}
|
||||
const pkgName = extRecord.pkgName;
|
||||
await npm.uninstallPackage(this.config.appiumHome, pkgName);
|
||||
await spinWith(this.isJsonOutput, `Uninstalling ${this.type} '${installSpec}'`, async () => {
|
||||
await npm.uninstallPackage(this.config.appiumHome, pkgName);
|
||||
});
|
||||
await this.config.removeExtension(installSpec);
|
||||
this.log.ok(`Successfully uninstalled ${this.type} '${installSpec}'`.green);
|
||||
return this.config.installedExtensions;
|
||||
@@ -639,9 +641,16 @@ class ExtensionCliCommand {
|
||||
const updateVer = unsafe && update.unsafeUpdate ? update.unsafeUpdate : update.safeUpdate;
|
||||
await spinWith(
|
||||
this.isJsonOutput,
|
||||
`Updating driver '${e}' from ${update.current} to ${updateVer}`,
|
||||
`Updating ${this.type} '${e}' from ${update.current} to ${updateVer}`,
|
||||
async () => await this.updateExtension(e, updateVer)
|
||||
);
|
||||
// if we're doing a safe update, but an unsafe update is also available, let the user know
|
||||
if (!unsafe && update.unsafeUpdate) {
|
||||
const newMajorUpdateMsg = `A newer major version ${update.unsafeUpdate} ` +
|
||||
`is available for ${this.type} '${e}', which could include breaking changes. ` +
|
||||
`If you want to apply this update, re-run with --unsafe`;
|
||||
this.log.info(newMajorUpdateMsg.yellow);
|
||||
}
|
||||
updates[e] = {from: update.current, to: updateVer};
|
||||
} catch (err) {
|
||||
errors[e] = err;
|
||||
|
||||
@@ -1,29 +1,27 @@
|
||||
import globalLog from '@appium/logger';
|
||||
import {createLogger, format, transports} from 'winston';
|
||||
import {fs} from '@appium/support';
|
||||
import { APPIUM_LOGGER_NAME } from './logger';
|
||||
import _ from 'lodash';
|
||||
import { adler32 } from './utils';
|
||||
import { LRUCache } from 'lru-cache';
|
||||
|
||||
// set up distributed logging before everything else
|
||||
global._global_npmlog = globalLog;
|
||||
|
||||
// npmlog is used only for emitting, we use winston for output
|
||||
globalLog.level = 'info';
|
||||
const levels = {
|
||||
const LEVELS_MAP = {
|
||||
debug: 4,
|
||||
info: 3,
|
||||
warn: 2,
|
||||
error: 1,
|
||||
};
|
||||
|
||||
const colors = {
|
||||
const COLORS_MAP = {
|
||||
info: 'cyan',
|
||||
debug: 'grey',
|
||||
warn: 'yellow',
|
||||
error: 'red',
|
||||
};
|
||||
|
||||
const npmToWinstonLevels = {
|
||||
const TO_WINSTON_LEVELS_MAP = {
|
||||
silly: 'debug',
|
||||
verbose: 'debug',
|
||||
debug: 'debug',
|
||||
@@ -32,36 +30,106 @@ const npmToWinstonLevels = {
|
||||
warn: 'warn',
|
||||
error: 'error',
|
||||
};
|
||||
const COLOR_CODE_PATTERN = /\u001b\[(\d+(;\d+)*)?m/g; // eslint-disable-line no-control-regex
|
||||
|
||||
const encounteredPrefixes = [];
|
||||
|
||||
let log = null;
|
||||
let useLocalTimeZone = false;
|
||||
|
||||
// add the timestamp in the correct format to the log info object
|
||||
const timestampFormat = format.timestamp({
|
||||
format() {
|
||||
let date = new Date();
|
||||
if (useLocalTimeZone) {
|
||||
date = new Date(date.valueOf() - date.getTimezoneOffset() * 60000);
|
||||
}
|
||||
// '2012-11-04T14:51:06.157Z' -> '2012-11-04 14:51:06:157'
|
||||
return date.toISOString().replace(/[TZ]/g, ' ').replace(/\./g, ':').trim();
|
||||
},
|
||||
// https://www.ditig.com/publications/256-colors-cheat-sheet
|
||||
const MIN_COLOR = 17;
|
||||
const MAX_COLOR = 231;
|
||||
/** @type {LRUCache<string, number>} */
|
||||
const COLORS_CACHE = new LRUCache({
|
||||
max: 1024,
|
||||
ttl: 1000 * 60 * 60 * 24, // expire after 24 hours
|
||||
updateAgeOnGet: true,
|
||||
});
|
||||
|
||||
/** @type {import('winston').Logger?} */
|
||||
let log = null;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {import('../types').ParsedArgs} args
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export async function init(args) {
|
||||
globalLog.level = 'silent';
|
||||
|
||||
// clean up in case we have initiated before since npmlog is a global object
|
||||
clear();
|
||||
|
||||
const transports = await createTransports(args);
|
||||
const transportNames = new Set(transports.map((tr) => tr.constructor.name));
|
||||
log = createLogger({
|
||||
transports,
|
||||
levels: LEVELS_MAP,
|
||||
});
|
||||
|
||||
const reportedLoggerErrors = new Set();
|
||||
// Capture logs emitted via npmlog and pass them through winston
|
||||
globalLog.on('log', (/** @type {MessageObject} */{level, message, prefix}) => {
|
||||
const {sessionSignature} = globalLog.asyncStorage.getStore() ?? {};
|
||||
/** @type {string[]} */
|
||||
const prefixes = [];
|
||||
if (sessionSignature) {
|
||||
prefixes.push(sessionSignature);
|
||||
}
|
||||
if (prefix) {
|
||||
prefixes.push(prefix);
|
||||
}
|
||||
let msg = message;
|
||||
if (!_.isEmpty(prefixes)) {
|
||||
const finalPrefix = prefixes
|
||||
.map(toDecoratedPrefix)
|
||||
.map((pfx) => isLogColorEnabled(args) ? colorizePrefix(pfx) : pfx)
|
||||
.join('');
|
||||
msg = `${finalPrefix} ${msg}`;
|
||||
}
|
||||
const winstonLevel = TO_WINSTON_LEVELS_MAP[level] || 'info';
|
||||
try {
|
||||
/** @type {import('winston').Logger} */(log)[winstonLevel](msg);
|
||||
if (_.isFunction(args.logHandler)) {
|
||||
args.logHandler(level, msg);
|
||||
}
|
||||
} catch (e) {
|
||||
if (!reportedLoggerErrors.has(e.message) && process.stderr.writable) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(
|
||||
`The log message '${_.truncate(msg, {length: 30})}' cannot be written into ` +
|
||||
`one or more requested destinations: ${transportNames}. Original error: ${e.message}`
|
||||
);
|
||||
reportedLoggerErrors.add(e.message);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {void}
|
||||
*/
|
||||
export function clear() {
|
||||
log?.clear();
|
||||
globalLog.removeAllListeners('log');
|
||||
}
|
||||
|
||||
// set the custom colors
|
||||
const colorizeFormat = format.colorize({
|
||||
colors,
|
||||
colors: COLORS_MAP,
|
||||
});
|
||||
|
||||
// Strip the color marking within messages
|
||||
const stripColorFormat = format(function stripColor(info) {
|
||||
const code = /\u001b\[(\d+(;\d+)*)?m/g; // eslint-disable-line no-control-regex
|
||||
info.message = info.message.replace(code, '');
|
||||
return info;
|
||||
return {
|
||||
...info,
|
||||
level: stripColorCodes(info.level),
|
||||
message: stripColorCodes(info.message),
|
||||
};
|
||||
})();
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {ParsedArgs} args
|
||||
* @param {string} logLvl
|
||||
* @returns {transports.ConsoleTransportInstance}
|
||||
*/
|
||||
function createConsoleTransport(args, logLvl) {
|
||||
return new transports.Console({
|
||||
// @ts-expect-error The 'name' property should exist
|
||||
@@ -72,15 +140,19 @@ function createConsoleTransport(args, logLvl) {
|
||||
level: logLvl,
|
||||
stderrLevels: ['error'],
|
||||
format: format.combine(
|
||||
timestampFormat,
|
||||
args.logNoColors ? stripColorFormat : colorizeFormat,
|
||||
format.printf(function printInfo(info) {
|
||||
return `${args.logTimestamp ? `${info.timestamp} - ` : ''}${info.message}`;
|
||||
})
|
||||
formatTimestamp(args),
|
||||
isLogColorEnabled(args) ? colorizeFormat : stripColorFormat,
|
||||
formatLog(args, true),
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {ParsedArgs} args
|
||||
* @param {string} logLvl
|
||||
* @returns {transports.FileTransportInstance}
|
||||
*/
|
||||
function createFileTransport(args, logLvl) {
|
||||
return new transports.File({
|
||||
// @ts-expect-error The 'name' property should exist
|
||||
@@ -93,19 +165,23 @@ function createFileTransport(args, logLvl) {
|
||||
level: logLvl,
|
||||
format: format.combine(
|
||||
stripColorFormat,
|
||||
timestampFormat,
|
||||
format.printf(function printInfo(info) {
|
||||
return `${info.timestamp} ${info.message}`;
|
||||
})
|
||||
formatTimestamp(args),
|
||||
formatLog(args, false),
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {ParsedArgs} args
|
||||
* @param {string} logLvl
|
||||
* @returns {transports.HttpTransportInstance}
|
||||
*/
|
||||
function createHttpTransport(args, logLvl) {
|
||||
let host = '127.0.0.1';
|
||||
let port = 9003;
|
||||
|
||||
if (args.webhook.match(':')) {
|
||||
if (args.webhook?.match(':')) {
|
||||
const hostAndPort = args.webhook.split(':');
|
||||
host = hostAndPort[0];
|
||||
port = parseInt(hostAndPort[1], 10);
|
||||
@@ -123,28 +199,26 @@ function createHttpTransport(args, logLvl) {
|
||||
level: logLvl,
|
||||
format: format.combine(
|
||||
stripColorFormat,
|
||||
format.printf(function printInfo(info) {
|
||||
return `${info.timestamp} ${info.message}`;
|
||||
})
|
||||
formatLog(args, false),
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {import('@appium/types').StringRecord} args
|
||||
* @param {ParsedArgs} args
|
||||
* @returns {Promise<import('winston-transport')[]>}
|
||||
*/
|
||||
async function createTransports(args) {
|
||||
const transports = [];
|
||||
let consoleLogLevel = null;
|
||||
let fileLogLevel = null;
|
||||
|
||||
/** @type {string} */
|
||||
let consoleLogLevel;
|
||||
/** @type {string} */
|
||||
let fileLogLevel;
|
||||
if (args.loglevel && args.loglevel.match(':')) {
|
||||
// --log-level arg can optionally provide diff logging levels for console and file, separated by a colon
|
||||
const lvlPair = args.loglevel.split(':');
|
||||
consoleLogLevel = lvlPair[0] || consoleLogLevel;
|
||||
fileLogLevel = lvlPair[1] || fileLogLevel;
|
||||
[consoleLogLevel, fileLogLevel] = lvlPair;
|
||||
} else {
|
||||
consoleLogLevel = fileLogLevel = args.loglevel;
|
||||
}
|
||||
@@ -184,72 +258,108 @@ async function createTransports(args) {
|
||||
return transports;
|
||||
}
|
||||
|
||||
function getColorizedPrefix(prefix) {
|
||||
let prefixId = prefix.split('@')[0].trim();
|
||||
prefixId = prefixId.split(' (')[0].trim();
|
||||
if (encounteredPrefixes.indexOf(prefixId) < 0) {
|
||||
encounteredPrefixes.push(prefixId);
|
||||
}
|
||||
// using a multiple of 16 should cause 16 colors to be created
|
||||
const colorNumber = encounteredPrefixes.indexOf(prefixId) * 16;
|
||||
// use the modulus to cycle around color wheel
|
||||
return `\x1b[38;5;${colorNumber % 256}m${prefix}\x1b[0m`;
|
||||
/**
|
||||
*
|
||||
* @param {string} text
|
||||
* @returns {string}
|
||||
*/
|
||||
function toDecoratedPrefix(text) {
|
||||
return `[${text}]`;
|
||||
}
|
||||
|
||||
async function init(args) {
|
||||
globalLog.level = 'silent';
|
||||
/**
|
||||
* Selects the color of the text in terminal from the MIN_COLOR..MAX_COLOR
|
||||
* range. We use adler32 hashing to ensure that equal prefixes would always have
|
||||
* same colors.
|
||||
*
|
||||
* @param {string} text Initial text
|
||||
* @returns {string} Colorized text (with pseudocode cchars added)
|
||||
*/
|
||||
function colorizePrefix(text) {
|
||||
let colorIndex = COLORS_CACHE.get(text);
|
||||
if (!colorIndex) {
|
||||
const hash = adler32(text);
|
||||
colorIndex = MIN_COLOR + hash % (MAX_COLOR - MIN_COLOR);
|
||||
COLORS_CACHE.set(text, colorIndex);
|
||||
}
|
||||
return `\x1b[38;5;${colorIndex}m${text}\x1b[0m`;
|
||||
}
|
||||
|
||||
// set de facto param passed to timestamp function
|
||||
useLocalTimeZone = args.localTimezone;
|
||||
/**
|
||||
* @param {ParsedArgs} args
|
||||
* @param {boolean} targetConsole
|
||||
* @returns {import('logform').Format}
|
||||
*/
|
||||
function formatLog(args, targetConsole) {
|
||||
if (['json', 'pretty_json'].includes(args.logFormat)) {
|
||||
return format.combine(
|
||||
format((info) => {
|
||||
const infoCopy = {...info};
|
||||
const contextInfo = globalLog.asyncStorage.getStore() ?? {};
|
||||
|
||||
// clean up in case we have initiated before since npmlog is a global object
|
||||
clear();
|
||||
if (targetConsole && !args.logTimestamp) {
|
||||
delete infoCopy.timestamp;
|
||||
}
|
||||
|
||||
const transports = await createTransports(args);
|
||||
const transportNames = new Set(transports.map((tr) => tr.constructor.name));
|
||||
log = createLogger({
|
||||
transports,
|
||||
levels,
|
||||
});
|
||||
if (!_.isEmpty(contextInfo)) {
|
||||
infoCopy.context = {...contextInfo};
|
||||
}
|
||||
|
||||
const reportedLoggerErrors = new Set();
|
||||
// Capture logs emitted via npmlog and pass them through winston
|
||||
globalLog.on('log', ({level, message, prefix}) => {
|
||||
const winstonLevel = npmToWinstonLevels[level] || 'info';
|
||||
let msg = message;
|
||||
if (prefix) {
|
||||
const decoratedPrefix = `[${prefix}]`;
|
||||
const toColorizedDecoratedPrefix = () => prefix === APPIUM_LOGGER_NAME
|
||||
? decoratedPrefix.magenta
|
||||
: getColorizedPrefix(decoratedPrefix);
|
||||
msg = `${args.logNoColors ? decoratedPrefix : toColorizedDecoratedPrefix()} ${msg}`;
|
||||
}
|
||||
try {
|
||||
log[winstonLevel](msg);
|
||||
if (_.isFunction(args.logHandler)) {
|
||||
args.logHandler(level, msg);
|
||||
}
|
||||
} catch (e) {
|
||||
if (!reportedLoggerErrors.has(e.message) && process.stderr.writable) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(
|
||||
`The log message '${_.truncate(msg, {length: 30})}' cannot be written into ` +
|
||||
`one or more requested destinations: ${transportNames}. Original error: ${e.message}`
|
||||
);
|
||||
reportedLoggerErrors.add(e.message);
|
||||
}
|
||||
return infoCopy;
|
||||
})(),
|
||||
format.json({space: args.logFormat === 'pretty_json' ? 2 : undefined}),
|
||||
);
|
||||
}
|
||||
|
||||
return format.printf((info) => {
|
||||
if (targetConsole) {
|
||||
return `${args.logTimestamp ? `${info.timestamp} - ` : ''}${info.message}`;
|
||||
}
|
||||
return `${info.timestamp} ${info.message}`;
|
||||
});
|
||||
}
|
||||
|
||||
function clear() {
|
||||
if (log) {
|
||||
for (let transport of _.keys(log.transports)) {
|
||||
log.remove(transport);
|
||||
}
|
||||
}
|
||||
globalLog.removeAllListeners('log');
|
||||
/**
|
||||
* add the timestamp in the correct format to the log info object
|
||||
*
|
||||
* @param {ParsedArgs} args
|
||||
* @returns {import('logform').Format}
|
||||
*/
|
||||
function formatTimestamp(args) {
|
||||
return format.timestamp({
|
||||
format() {
|
||||
let date = new Date();
|
||||
if (args.localTimezone) {
|
||||
date = new Date(date.valueOf() - date.getTimezoneOffset() * 60000);
|
||||
}
|
||||
// '2012-11-04T14:51:06.157Z' -> '2012-11-04 14:51:06:157'
|
||||
return date.toISOString().replace(/[TZ]/g, ' ').replace(/\./g, ':').trim();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Strips color control codes from the given string
|
||||
*
|
||||
* @param {string} text
|
||||
* @returns {string}
|
||||
*/
|
||||
function stripColorCodes(text) {
|
||||
return text.replace(COLOR_CODE_PATTERN, '');
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {ParsedArgs} args
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function isLogColorEnabled(args) {
|
||||
return !args.logNoColors && args.logFormat === 'text';
|
||||
}
|
||||
|
||||
export {init, clear};
|
||||
export default init;
|
||||
|
||||
/**
|
||||
* @typedef {import('appium/types').ParsedArgs} ParsedArgs
|
||||
* @typedef {import('@appium/logger').MessageObject} MessageObject
|
||||
*/
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
server as baseServer,
|
||||
normalizeBasePath,
|
||||
} from '@appium/base-driver';
|
||||
import {logger as logFactory, util, env} from '@appium/support';
|
||||
import {util, env} from '@appium/support';
|
||||
import {asyncify} from 'asyncbox';
|
||||
import _ from 'lodash';
|
||||
import {AppiumDriver} from './appium';
|
||||
@@ -246,18 +246,22 @@ async function init(args) {
|
||||
await logsinkInit(serverArgs);
|
||||
|
||||
if (serverArgs.logFilters) {
|
||||
const {issues, rules} = await logFactory.loadSecureValuesPreprocessingRules(
|
||||
const {issues, rules} = await logger.unwrap().loadSecureValuesPreprocessingRules(
|
||||
serverArgs.logFilters,
|
||||
);
|
||||
const argToLog = _.truncate(JSON.stringify(serverArgs.logFilters), {
|
||||
length: 150
|
||||
});
|
||||
if (!_.isEmpty(issues)) {
|
||||
throw new Error(
|
||||
`The log filtering rules config '${serverArgs.logFilters}' has issues: ` +
|
||||
`The log filtering rules config ${argToLog} has issues: ` +
|
||||
JSON.stringify(issues, null, 2),
|
||||
);
|
||||
}
|
||||
if (_.isEmpty(rules)) {
|
||||
logger.warn(
|
||||
`Found no log filtering rules in '${serverArgs.logFilters}'. Is that expected?`,
|
||||
`Found no log filtering rules in the ${argToLog} config. ` +
|
||||
`Is that expected?`,
|
||||
);
|
||||
} else {
|
||||
// Filtering aims to "hide" these values from the log,
|
||||
|
||||
@@ -391,6 +391,62 @@ export function fetchInterfaces (family = null) {
|
||||
.filter(({family}) => !familyValue || familyValue && familyValue.includes(family));
|
||||
}
|
||||
|
||||
/**
|
||||
* https://github.com/SheetJS/js-adler32
|
||||
*
|
||||
* @param {string} str
|
||||
* @param {number?} [seed]
|
||||
* @returns {number}
|
||||
*/
|
||||
export function adler32(str, seed = null) {
|
||||
let a = 1, b = 0, L = str.length, M = 0, c = 0, d = 0;
|
||||
if (typeof seed === 'number') {
|
||||
a = seed & 0xFFFF;
|
||||
b = seed >>> 16;
|
||||
}
|
||||
for (let i = 0; i < L;) {
|
||||
M = Math.min(L - i, 2918);
|
||||
while (M > 0) {
|
||||
c = str.charCodeAt(i++);
|
||||
if (c < 0x80) {
|
||||
a += c;
|
||||
}
|
||||
else if (c < 0x800) {
|
||||
a += 192 | ((c >> 6) & 31);
|
||||
b += a;
|
||||
--M;
|
||||
a += 128 | (c & 63);
|
||||
} else if (c >= 0xD800 && c < 0xE000) {
|
||||
c = (c & 1023) + 64;
|
||||
d = str.charCodeAt(i++) & 1023;
|
||||
a += 240 | ((c >> 8) & 7);
|
||||
b += a;
|
||||
--M;
|
||||
a += 128 | ((c >> 2) & 63);
|
||||
b += a;
|
||||
--M;
|
||||
a += 128 | ((d >> 6) & 15) | ((c & 3) << 4);
|
||||
b += a;
|
||||
--M;
|
||||
a += 128 | (d & 63);
|
||||
} else {
|
||||
a += 224 | ((c >> 12) & 15);
|
||||
b += a;
|
||||
--M;
|
||||
a += 128 | ((c >> 6) & 63);
|
||||
b += a;
|
||||
--M;
|
||||
a += 128 | (c & 63);
|
||||
}
|
||||
b += a;
|
||||
--M;
|
||||
}
|
||||
a = (15 * (a >>> 16) + (a & 65535));
|
||||
b = (15 * (b >>> 16) + (b & 65535));
|
||||
}
|
||||
return ((b % 65521) << 16) | (a % 65521);
|
||||
}
|
||||
|
||||
export {
|
||||
inspect,
|
||||
parseCapsForInnerDriver,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "appium",
|
||||
"version": "2.8.0",
|
||||
"version": "2.11.2",
|
||||
"description": "Automation for Apps.",
|
||||
"keywords": [
|
||||
"automation",
|
||||
@@ -45,28 +45,27 @@
|
||||
"scripts": {
|
||||
"build:docs": "node docs/scripts/build-docs.js",
|
||||
"build:docs:preview": "cross-env APPIUM_DOCS_PREVIEW=1 npm run build:docs",
|
||||
"build:docs:assets": "node docs/scripts/copy-assets.js",
|
||||
"build:docs:cli": "node docs/scripts/gen-cli-args-docs.js",
|
||||
"build:docs:reference": "node docs/scripts/build-reference.js",
|
||||
"dev:docs": "npm run build:docs:assets && npm run dev:docs:en",
|
||||
"dev:docs": "npm run dev:docs:en",
|
||||
"dev:docs:en": "appium-docs build --serve --mkdocs-yml ./docs/mkdocs-en.yml",
|
||||
"dev:docs:ja": "appium-docs build --serve --mkdocs-yml ./docs/mkdocs-ja.yml",
|
||||
"dev:docs:zh": "appium-docs build --serve --mkdocs-yml ./docs/mkdocs-zh.yml",
|
||||
"postinstall": "node ./scripts/autoinstall-extensions.js",
|
||||
"publish:docs": "cross-env APPIUM_DOCS_PUBLISH=1 npm run build:docs",
|
||||
"test": "npm run test:unit",
|
||||
"test:e2e": "mocha -p --timeout 1m --slow 30s \"./test/e2e/**/*.spec.js\"",
|
||||
"test:e2e": "mocha --timeout 1m --slow 30s \"./test/e2e/**/*.spec.js\"",
|
||||
"test:smoke": "cross-env APPIUM_HOME=./local_appium_home node ./index.js driver install uiautomator2 && cross-env APPIUM_HOME=./local_appium_home node ./index.js driver list",
|
||||
"test:unit": "mocha \"./test/unit/**/*.spec.js\""
|
||||
},
|
||||
"dependencies": {
|
||||
"@appium/base-driver": "^9.8.1",
|
||||
"@appium/base-plugin": "^2.2.34",
|
||||
"@appium/docutils": "^1.0.10",
|
||||
"@appium/logger": "^1.2.0",
|
||||
"@appium/schema": "~0.5.0",
|
||||
"@appium/support": "^4.4.0",
|
||||
"@appium/types": "^0.19.1",
|
||||
"@appium/base-driver": "^9.11.2",
|
||||
"@appium/base-plugin": "^2.2.42",
|
||||
"@appium/docutils": "^1.0.18",
|
||||
"@appium/logger": "^1.6.0",
|
||||
"@appium/schema": "^0.6.1",
|
||||
"@appium/support": "^5.1.2",
|
||||
"@appium/types": "^0.21.1",
|
||||
"@sidvind/better-ajv-errors": "2.1.3",
|
||||
"@types/argparse": "2.0.16",
|
||||
"@types/bluebird": "3.5.42",
|
||||
@@ -82,19 +81,20 @@
|
||||
"axios": "1.7.2",
|
||||
"bluebird": "3.7.2",
|
||||
"cross-env": "7.0.3",
|
||||
"lilconfig": "3.1.1",
|
||||
"lilconfig": "3.1.2",
|
||||
"lodash": "4.17.21",
|
||||
"lru-cache": "10.3.0",
|
||||
"ora": "5.4.1",
|
||||
"package-changed": "3.0.0",
|
||||
"resolve-from": "5.0.0",
|
||||
"semver": "7.6.2",
|
||||
"source-map-support": "0.5.21",
|
||||
"teen_process": "2.1.4",
|
||||
"type-fest": "4.19.0",
|
||||
"teen_process": "2.2.0",
|
||||
"type-fest": "4.21.0",
|
||||
"winston": "3.13.0",
|
||||
"wrap-ansi": "7.0.0",
|
||||
"ws": "8.17.0",
|
||||
"yaml": "2.4.3"
|
||||
"ws": "8.18.0",
|
||||
"yaml": "2.4.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.17.0 || ^16.13.0 || >=18.0.0",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"devDependencies": {
|
||||
"webdriverio": "8.38.2"
|
||||
"webdriverio": "8.39.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,15 +10,20 @@ import {
|
||||
runAppiumRaw,
|
||||
} from './e2e-helpers';
|
||||
|
||||
const {expect} = chai;
|
||||
|
||||
describe('argument parsing', function () {
|
||||
/**
|
||||
* @type {string}
|
||||
*/
|
||||
let appiumHome;
|
||||
let expect;
|
||||
|
||||
before(async function () {
|
||||
const chai = await import('chai');
|
||||
const chaiAsPromised = await import('chai-as-promised');
|
||||
chai.use(chaiAsPromised.default);
|
||||
chai.should();
|
||||
expect = chai.expect;
|
||||
|
||||
appiumHome = await tempDir.openDir();
|
||||
});
|
||||
|
||||
|
||||
@@ -16,8 +16,6 @@ import {
|
||||
import {FAKE_DRIVER_DIR, resolveFixture} from '../helpers';
|
||||
import {installLocalExtension, runAppiumJson, runAppiumRaw} from './e2e-helpers';
|
||||
|
||||
const {expect} = chai;
|
||||
|
||||
const TEST_DRIVER_DIR = path.dirname(resolveFixture('test-driver/package.json'));
|
||||
|
||||
const TEST_DRIVER_INVALID_PEERS_DIR = path.dirname(
|
||||
@@ -56,6 +54,7 @@ describe('Driver CLI', function () {
|
||||
* @type {(args: string[]) => Promise<ExtRecord<DriverType>>}
|
||||
*/
|
||||
let runDoctor;
|
||||
let expect;
|
||||
|
||||
async function resetAppiumHome() {
|
||||
await fs.rimraf(appiumHome);
|
||||
@@ -63,6 +62,12 @@ describe('Driver CLI', function () {
|
||||
}
|
||||
|
||||
before(async function () {
|
||||
const chai = await import('chai');
|
||||
const chaiAsPromised = await import('chai-as-promised');
|
||||
chai.use(chaiAsPromised.default);
|
||||
chai.should();
|
||||
expect = chai.expect;
|
||||
|
||||
appiumHome = await tempDir.openDir();
|
||||
const run = runAppiumJson(appiumHome);
|
||||
runInstall = async (args) =>
|
||||
|
||||
@@ -5,8 +5,6 @@ import {EXT_SUBCOMMAND_RUN as RUN, PLUGIN_TYPE} from '../../lib/constants';
|
||||
import {FAKE_PLUGIN_DIR} from '../helpers';
|
||||
import {installLocalExtension, runAppiumJson} from './e2e-helpers';
|
||||
|
||||
const {expect} = chai;
|
||||
|
||||
describe('Plugin CLI', function () {
|
||||
/**
|
||||
* @type {string}
|
||||
@@ -17,8 +15,15 @@ describe('Plugin CLI', function () {
|
||||
* @type {(args: string[]) => Promise<{ output: string, error?: string }>}
|
||||
*/
|
||||
let runRun;
|
||||
let expect;
|
||||
|
||||
before(async function () {
|
||||
const chai = await import('chai');
|
||||
const chaiAsPromised = await import('chai-as-promised');
|
||||
chai.use(chaiAsPromised.default);
|
||||
chai.should();
|
||||
expect = chai.expect;
|
||||
|
||||
appiumHome = await tempDir.openDir();
|
||||
const run = runAppiumJson(appiumHome);
|
||||
runRun = async (args) =>
|
||||
|
||||
@@ -22,6 +22,13 @@ describe('config file behavior', function () {
|
||||
const EXT_PROPS_FILEPATH = resolveConfigFixture('appium-config-ext-good.json');
|
||||
const LOG_FILTERS_FILEPATH = resolveConfigFixture('appium-config-log-filters.json');
|
||||
|
||||
before(async function () {
|
||||
const chai = await import('chai');
|
||||
const chaiAsPromised = await import('chai-as-promised');
|
||||
chai.use(chaiAsPromised.default);
|
||||
chai.should();
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
finalizeSchema();
|
||||
});
|
||||
|
||||
@@ -6,6 +6,7 @@ import * as teenProcess from 'teen_process';
|
||||
|
||||
describe('Config', function () {
|
||||
let sandbox;
|
||||
let should;
|
||||
|
||||
beforeEach(function () {
|
||||
sandbox = createSandbox();
|
||||
@@ -15,6 +16,13 @@ describe('Config', function () {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
before(async function () {
|
||||
const chai = await import('chai');
|
||||
const chaiAsPromised = await import('chai-as-promised');
|
||||
chai.use(chaiAsPromised.default);
|
||||
should = chai.should();
|
||||
});
|
||||
|
||||
describe('getGitRev', function () {
|
||||
it('should get a reasonable git revision', async function () {
|
||||
let rev = await getGitRev();
|
||||
|
||||
@@ -23,8 +23,6 @@ import {
|
||||
W3C_PREFIXED_CAPS,
|
||||
} from '../helpers';
|
||||
|
||||
const should = chai.should();
|
||||
|
||||
/** @type {string} */
|
||||
let testServerBaseUrl;
|
||||
|
||||
@@ -87,8 +85,14 @@ describe('FakeDriver via HTTP', function () {
|
||||
|
||||
/** @type {import('sinon').SinonSandbox} */
|
||||
let sandbox;
|
||||
let should;
|
||||
|
||||
before(async function () {
|
||||
const chai = await import('chai');
|
||||
const chaiAsPromised = await import('chai-as-promised');
|
||||
chai.use(chaiAsPromised.default);
|
||||
should = chai.should();
|
||||
|
||||
sandbox = createSandbox();
|
||||
appiumHome = await tempDir.openDir();
|
||||
wdOpts.port = port = await getTestPort();
|
||||
@@ -637,8 +641,14 @@ describe('Bidi over SSL', function () {
|
||||
let certPath = 'certificate.cert';
|
||||
let keyPath = 'certificate.key';
|
||||
const capabilities = {...caps, webSocketUrl: true};
|
||||
let should;
|
||||
|
||||
before(async function () {
|
||||
const chai = await import('chai');
|
||||
const chaiAsPromised = await import('chai-as-promised');
|
||||
chai.use(chaiAsPromised.default);
|
||||
should = chai.should();
|
||||
|
||||
try {
|
||||
await generateCertificate(certPath, keyPath);
|
||||
} catch (e) {
|
||||
|
||||
@@ -14,7 +14,6 @@ import {FAKE_DRIVER_DIR, resolveFixture} from '../helpers';
|
||||
import {installLocalExtension, runAppiumJson} from './e2e-helpers';
|
||||
|
||||
const {MANIFEST_RELATIVE_PATH} = env;
|
||||
const {expect} = chai;
|
||||
const testDriverPath = path.dirname(resolveFixture('test-driver/package.json'));
|
||||
|
||||
describe('when Appium is a dependency of the current project', function () {
|
||||
@@ -26,8 +25,15 @@ describe('when Appium is a dependency of the current project', function () {
|
||||
let appiumHomePkgPath;
|
||||
/** @type {string} */
|
||||
let appiumHome;
|
||||
let expect;
|
||||
|
||||
before(async function () {
|
||||
const chai = await import('chai');
|
||||
const chaiAsPromised = await import('chai-as-promised');
|
||||
chai.use(chaiAsPromised.default);
|
||||
chai.should();
|
||||
expect = chai.expect;
|
||||
|
||||
appiumHome = await tempDir.openDir();
|
||||
});
|
||||
|
||||
|
||||
@@ -10,8 +10,6 @@ import {
|
||||
import {FAKE_DRIVER_DIR, resolveFixture} from '../helpers';
|
||||
import {installLocalExtension, runAppiumJson} from './e2e-helpers';
|
||||
|
||||
const {expect} = chai;
|
||||
|
||||
describe('manifest handling', function () {
|
||||
/**
|
||||
* @type {string}
|
||||
@@ -27,6 +25,7 @@ describe('manifest handling', function () {
|
||||
* @type {(args?: string[]) => Promise<ExtensionListData>}
|
||||
*/
|
||||
let runList;
|
||||
let expect;
|
||||
|
||||
async function resetAppiumHome() {
|
||||
await fs.rimraf(appiumHome);
|
||||
@@ -34,6 +33,12 @@ describe('manifest handling', function () {
|
||||
}
|
||||
|
||||
before(async function () {
|
||||
const chai = await import('chai');
|
||||
const chaiAsPromised = await import('chai-as-promised');
|
||||
chai.use(chaiAsPromised.default);
|
||||
chai.should();
|
||||
expect = chai.expect;
|
||||
|
||||
appiumHome = await tempDir.openDir();
|
||||
manifestPath = path.join(appiumHome, CACHE_DIR_RELATIVE_PATH, 'extensions.yaml');
|
||||
const run = runAppiumJson(appiumHome);
|
||||
|
||||
@@ -21,8 +21,6 @@ import {
|
||||
const FAKE_ARGS = {sillyWebServerPort: 1234, host: 'hey'};
|
||||
const FAKE_PLUGIN_ARGS = {fake: FAKE_ARGS};
|
||||
|
||||
const should = chai.should();
|
||||
|
||||
/** @type {import('webdriverio').RemoteOptions} */
|
||||
const wdOpts = {
|
||||
hostname: TEST_HOST,
|
||||
@@ -41,8 +39,14 @@ describe('FakePlugin w/ FakeDriver via HTTP', function () {
|
||||
let port;
|
||||
/** @type {string} */
|
||||
let testServerBaseSessionUrl;
|
||||
let should;
|
||||
|
||||
before(async function () {
|
||||
const chai = await import('chai');
|
||||
const chaiAsPromised = await import('chai-as-promised');
|
||||
chai.use(chaiAsPromised.default);
|
||||
should = chai.should();
|
||||
|
||||
resetSchema();
|
||||
appiumHome = await tempDir.openDir();
|
||||
wdOpts.port = port = await getTestPort();
|
||||
|
||||
@@ -5,15 +5,20 @@ import {DRIVER_TYPE} from '../../lib/constants';
|
||||
import {resolveFixture} from '../helpers';
|
||||
import {installLocalExtension, runAppium} from './e2e-helpers';
|
||||
|
||||
const {expect} = chai;
|
||||
|
||||
describe('CLI behavior controlled by schema', function () {
|
||||
/**
|
||||
* @type {string}
|
||||
*/
|
||||
let appiumHome;
|
||||
let expect;
|
||||
|
||||
before(async function () {
|
||||
const chai = await import('chai');
|
||||
const chaiAsPromised = await import('chai-as-promised');
|
||||
chai.use(chaiAsPromised.default);
|
||||
chai.should();
|
||||
expect = chai.expect;
|
||||
|
||||
appiumHome = await tempDir.openDir();
|
||||
});
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ export default {
|
||||
driversImportChunkSize: 3,
|
||||
keepAliveTimeout: 600,
|
||||
localTimezone: false,
|
||||
logFormat: 'text',
|
||||
loglevel: 'debug',
|
||||
logNoColors: false,
|
||||
logTimestamp: false,
|
||||
|
||||
@@ -14,8 +14,6 @@ import BasePlugin from '@appium/base-plugin';
|
||||
|
||||
const SESSION_ID = '1';
|
||||
|
||||
const {expect} = chai;
|
||||
|
||||
describe('AppiumDriver', function () {
|
||||
/** @type {import('sinon').SinonSandbox} */
|
||||
let sandbox;
|
||||
@@ -25,6 +23,15 @@ describe('AppiumDriver', function () {
|
||||
|
||||
/** @type {MockConfig} */
|
||||
let MockConfig;
|
||||
let expect;
|
||||
|
||||
before(async function () {
|
||||
const chai = await import('chai');
|
||||
const chaiAsPromised = await import('chai-as-promised');
|
||||
chai.use(chaiAsPromised.default);
|
||||
chai.should();
|
||||
expect = chai.expect;
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
sandbox = createSandbox();
|
||||
@@ -64,10 +71,10 @@ describe('AppiumDriver', function () {
|
||||
// triggers the `log` getter to set `_log`
|
||||
ad.log;
|
||||
// now we can stub `_log`, since it exists
|
||||
sandbox.stub(ad._log, 'debug');
|
||||
const debugStrub = sandbox.stub(ad._log, 'debug');
|
||||
// finally, wait for `updateBuildInfo()` to finish up
|
||||
await promise;
|
||||
ad._log.debug.should.have.been.calledOnce;
|
||||
debugStrub.calledOnce.should.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -403,7 +410,7 @@ describe('AppiumDriver', function () {
|
||||
[ArgsPlugin, 'args'],
|
||||
]);
|
||||
for (const plugin of appium.createPluginInstances()) {
|
||||
chai.expect(plugin.cliArgs).to.eql({});
|
||||
expect(plugin.cliArgs).to.eql({});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -17,6 +17,11 @@ describe('DriverCommand', function () {
|
||||
|
||||
let sandbox;
|
||||
|
||||
before(async function () {
|
||||
const chai = await import('chai');
|
||||
chai.should();
|
||||
});
|
||||
|
||||
beforeEach(async function () {
|
||||
sandbox = createSandbox();
|
||||
appiumHome = await tempDir.openDir();
|
||||
|
||||
@@ -2,8 +2,6 @@ import {createSandbox} from 'sinon';
|
||||
import {finalizeSchema, resetSchema, SchemaFinalizationError} from '../../../lib/schema/schema';
|
||||
import {rewiremock} from '../../helpers';
|
||||
|
||||
const expect = chai.expect;
|
||||
|
||||
describe('cli/schema-args', function () {
|
||||
/** @type {import('appium/lib/schema/cli-args').toParserArgs} */
|
||||
let toParserArgs;
|
||||
@@ -12,6 +10,15 @@ describe('cli/schema-args', function () {
|
||||
* @type {sinon.SinonSandbox}
|
||||
*/
|
||||
let sandbox;
|
||||
let expect;
|
||||
|
||||
before(async function () {
|
||||
const chai = await import('chai');
|
||||
const chaiAsPromised = await import('chai-as-promised');
|
||||
chai.use(chaiAsPromised.default);
|
||||
chai.should();
|
||||
expect = chai.expect;
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
sandbox = createSandbox();
|
||||
|
||||
@@ -2,10 +2,17 @@ import {createSandbox} from 'sinon';
|
||||
import {system} from '@appium/support';
|
||||
import {getPresetDrivers} from '../../../lib/cli/setup-command';
|
||||
|
||||
const expect = chai.expect;
|
||||
|
||||
describe('SetupCommand', function () {
|
||||
let sandbox;
|
||||
let expect;
|
||||
|
||||
before(async function () {
|
||||
const chai = await import('chai');
|
||||
const chaiAsPromised = await import('chai-as-promised');
|
||||
chai.use(chaiAsPromised.default);
|
||||
chai.should();
|
||||
expect = chai.expect;
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
sandbox = createSandbox();
|
||||
|
||||
@@ -6,8 +6,6 @@ import YAML from 'yaml';
|
||||
import * as schema from '../../lib/schema/schema';
|
||||
import {resolveFixture, rewiremock} from '../helpers';
|
||||
|
||||
const expect = chai.expect;
|
||||
|
||||
describe('config-file', function () {
|
||||
const GOOD_YAML_CONFIG_FILEPATH = resolveFixture('config', 'appium-config-good.yaml');
|
||||
const GOOD_JSON_CONFIG_FILEPATH = resolveFixture('config', 'appium-config-good.json');
|
||||
@@ -47,8 +45,15 @@ describe('config-file', function () {
|
||||
let lc;
|
||||
|
||||
let mocks;
|
||||
let expect;
|
||||
|
||||
before(async function () {
|
||||
const chai = await import('chai');
|
||||
const chaiAsPromised = await import('chai-as-promised');
|
||||
chai.use(chaiAsPromised.default);
|
||||
chai.should();
|
||||
expect = chai.expect;
|
||||
|
||||
before(function () {
|
||||
// generally called via the CLI parser, this needs to be done manually in tests.
|
||||
// we don't need to do this before _each_ test, because we're not changing the schema.
|
||||
// if we did change the schema, this would need to be in `beforeEach()` and `afterEach()`
|
||||
@@ -120,19 +125,22 @@ describe('config-file', function () {
|
||||
it('should support yaml', async function () {
|
||||
const {config} = await readConfigFile(GOOD_YAML_CONFIG_FILEPATH);
|
||||
expect(config).to.eql(normalizeConfig(GOOD_JSON_CONFIG));
|
||||
expect(schema.validate).to.have.been.calledOnce;
|
||||
// @ts-ignore
|
||||
schema.validate.calledOnce.should.be.true;
|
||||
});
|
||||
|
||||
it('should support json', async function () {
|
||||
const {config} = await readConfigFile(GOOD_JSON_CONFIG_FILEPATH);
|
||||
expect(config).to.eql(normalizeConfig(GOOD_JSON_CONFIG));
|
||||
expect(schema.validate).to.have.been.calledOnce;
|
||||
// @ts-ignore
|
||||
schema.validate.calledOnce.should.be.true;
|
||||
});
|
||||
|
||||
it('should support js', async function () {
|
||||
const {config} = await readConfigFile(GOOD_JS_CONFIG_FILEPATH);
|
||||
expect(config).to.eql(normalizeConfig(GOOD_JSON_CONFIG));
|
||||
expect(schema.validate).to.have.been.calledOnce;
|
||||
// @ts-ignore
|
||||
schema.validate.calledOnce.should.be.true;
|
||||
});
|
||||
|
||||
describe('when no filepath provided', function () {
|
||||
@@ -141,12 +149,13 @@ describe('config-file', function () {
|
||||
});
|
||||
|
||||
it('should search for a config file', function () {
|
||||
expect(lc.search).to.have.been.calledOnce;
|
||||
expect(schema.validate).to.have.been.calledOnce;
|
||||
lc.search.calledOnce.should.be.true;
|
||||
// @ts-ignore
|
||||
schema.validate.calledOnce.should.be.true;
|
||||
});
|
||||
|
||||
it('should not try to load a config file directly', function () {
|
||||
expect(lc.load).to.not.have.been.called;
|
||||
lc.load.called.should.be.false;
|
||||
});
|
||||
|
||||
describe('when no config file is found', function () {
|
||||
@@ -160,7 +169,8 @@ describe('config-file', function () {
|
||||
|
||||
it('should resolve with an empty object', function () {
|
||||
expect(result).to.be.an('object').that.is.empty;
|
||||
expect(schema.validate).not.to.have.been.called;
|
||||
// @ts-ignore
|
||||
schema.validate.calledOnce.should.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -183,7 +193,8 @@ describe('config-file', function () {
|
||||
|
||||
describe('when the config file is not empty', function () {
|
||||
it('should validate the config against a schema', function () {
|
||||
expect(schema.validate).to.have.been.calledOnceWith(GOOD_JSON_CONFIG);
|
||||
// @ts-ignore
|
||||
schema.validate.calledOnceWith(GOOD_JSON_CONFIG).should.be.true;
|
||||
});
|
||||
|
||||
describe('when the config file is valid', function () {
|
||||
@@ -226,11 +237,11 @@ describe('config-file', function () {
|
||||
});
|
||||
|
||||
it('should not attempt to find a config file', function () {
|
||||
expect(lc.search).to.not.have.been.called;
|
||||
lc.search.called.should.be.false;
|
||||
});
|
||||
|
||||
it('should try to load a config file directly', function () {
|
||||
expect(lc.load).to.have.been.calledOnce;
|
||||
lc.load.calledOnce.should.be.true;
|
||||
});
|
||||
|
||||
describe('when no config file exists at path', function () {
|
||||
@@ -285,7 +296,8 @@ describe('config-file', function () {
|
||||
|
||||
describe('when the config file is not empty', function () {
|
||||
it('should validate the config against a schema', function () {
|
||||
expect(schema.validate).to.have.been.calledOnceWith(GOOD_JSON_CONFIG);
|
||||
// @ts-ignore
|
||||
schema.validate.calledOnceWith(GOOD_JSON_CONFIG).should.be.true;
|
||||
});
|
||||
|
||||
describe('when the config file is valid', function () {
|
||||
@@ -340,12 +352,12 @@ describe('config-file', function () {
|
||||
it('should call `betterAjvErrors()` with option `json: opts.json`', function () {
|
||||
// @ts-expect-error
|
||||
formatErrors([{}], {}, {json: '{"foo": "bar"}'});
|
||||
expect(mocks['@sidvind/better-ajv-errors']).to.have.been.calledWith(
|
||||
mocks['@sidvind/better-ajv-errors'].calledWith(
|
||||
schema.getSchema(),
|
||||
{},
|
||||
[{}],
|
||||
{format: 'cli', json: '{"foo": "bar"}'}
|
||||
);
|
||||
).should.be.true;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -23,6 +23,13 @@ describe('Config', function () {
|
||||
/** @type {sinon.SinonSandbox} */
|
||||
let sandbox;
|
||||
|
||||
before(async function () {
|
||||
const chai = await import('chai');
|
||||
const chaiAsPromised = await import('chai-as-promised');
|
||||
chai.use(chaiAsPromised.default);
|
||||
chai.should();
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
sandbox = createSandbox();
|
||||
});
|
||||
@@ -45,7 +52,7 @@ describe('Config', function () {
|
||||
it('should log build info to console', async function () {
|
||||
const config = getBuildInfo();
|
||||
await showBuildInfo();
|
||||
log.should.have.been.calledOnce;
|
||||
log.calledOnce.should.be.true;
|
||||
log.firstCall.args.should.contain(JSON.stringify(config));
|
||||
});
|
||||
});
|
||||
@@ -64,7 +71,7 @@ describe('Config', function () {
|
||||
{port: 1234},
|
||||
{allowCors: false}
|
||||
);
|
||||
log.should.have.been.calledWith('Appium Configuration\n');
|
||||
log.calledWith('Appium Configuration\n').should.be.true;
|
||||
});
|
||||
|
||||
it('should skip empty objects', function () {
|
||||
@@ -75,7 +82,7 @@ describe('Config', function () {
|
||||
{spam: 'food'},
|
||||
{}
|
||||
);
|
||||
dir.should.have.been.calledWith({foo: 'bar', sheep: 0, ducks: false});
|
||||
dir.calledWith({foo: 'bar', sheep: 0, ducks: false}).should.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -88,7 +95,7 @@ describe('Config', function () {
|
||||
{spam: 'food'},
|
||||
{}
|
||||
);
|
||||
log.should.have.been.calledWith('\n(no configuration file loaded)');
|
||||
log.calledWith('\n(no configuration file loaded)').should.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -96,7 +103,7 @@ describe('Config', function () {
|
||||
it('should not dump CLI args', function () {
|
||||
// @ts-expect-error
|
||||
showConfig({}, {}, {}, {});
|
||||
log.should.have.been.calledWith('\n(no CLI parameters provided)');
|
||||
log.calledWith('\n(no CLI parameters provided)').should.be.true;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,8 +6,6 @@ import {resetSchema} from '../../../lib/schema';
|
||||
import {resolveFixture, rewiremock} from '../../helpers';
|
||||
import {initMocks} from './mocks';
|
||||
|
||||
const {expect} = chai;
|
||||
|
||||
describe('DriverConfig', function () {
|
||||
/** @type {string} */
|
||||
let yamlFixture;
|
||||
@@ -31,7 +29,15 @@ describe('DriverConfig', function () {
|
||||
*/
|
||||
let DriverConfig;
|
||||
|
||||
let expect;
|
||||
|
||||
before(async function () {
|
||||
const chai = await import('chai');
|
||||
const chaiAsPromised = await import('chai-as-promised');
|
||||
chai.use(chaiAsPromised.default);
|
||||
chai.should();
|
||||
expect = chai.expect;
|
||||
|
||||
yamlFixture = await fs.readFile(resolveFixture('manifest', 'v3.yaml'), 'utf8');
|
||||
});
|
||||
|
||||
@@ -338,7 +344,7 @@ describe('DriverConfig', function () {
|
||||
driverConfig.readExtensionSchema(extName, extData);
|
||||
|
||||
// we don't have access to the schema registration cache directly, so this is as close as we can get.
|
||||
expect(MockResolveFrom).to.have.been.calledOnce;
|
||||
MockResolveFrom.calledOnce.should.be.true;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5,8 +5,6 @@ import sinon from 'sinon';
|
||||
import {FAKE_DRIVER_DIR} from '../../helpers';
|
||||
import {Manifest} from '../../../lib/extension/manifest';
|
||||
|
||||
const {expect} = chai;
|
||||
|
||||
/**
|
||||
* Relative path from actual `package.json` of `FakeDriver` for the `fake-stdin` script
|
||||
*/
|
||||
@@ -20,6 +18,16 @@ describe('ExtensionCommand', function () {
|
||||
/** @type {sinon.SinonSandbox} */
|
||||
let sandbox;
|
||||
|
||||
let expect;
|
||||
|
||||
before(async function () {
|
||||
const chai = await import('chai');
|
||||
const chaiAsPromised = await import('chai-as-promised');
|
||||
chai.use(chaiAsPromised.default);
|
||||
chai.should();
|
||||
expect = chai.expect;
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
sandbox = sinon.createSandbox();
|
||||
const driverConfig = DriverConfig.create(sandbox.createStubInstance(Manifest));
|
||||
|
||||
@@ -4,8 +4,6 @@ import {version as APPIUM_VER} from '../../../package.json';
|
||||
import {FAKE_DRIVER_DIR, PROJECT_ROOT, rewiremock} from '../../helpers';
|
||||
import {initMocks} from './mocks';
|
||||
|
||||
const {expect} = chai;
|
||||
|
||||
describe('ExtensionConfig', function () {
|
||||
/** @type {import('sinon').SinonSandbox} */
|
||||
let sandbox;
|
||||
@@ -19,6 +17,16 @@ describe('ExtensionConfig', function () {
|
||||
/** @type {import('./mocks').MockAppiumSupport} */
|
||||
let MockAppiumSupport;
|
||||
|
||||
let expect;
|
||||
|
||||
before(async function () {
|
||||
const chai = await import('chai');
|
||||
const chaiAsPromised = await import('chai-as-promised');
|
||||
chai.use(chaiAsPromised.default);
|
||||
chai.should();
|
||||
expect = chai.expect;
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
let overrides;
|
||||
({MockAppiumSupport, overrides, sandbox} = initMocks());
|
||||
@@ -270,9 +278,9 @@ describe('ExtensionConfig', function () {
|
||||
|
||||
it('should display a warning count of 1', async function () {
|
||||
await config._validate({foo: {}});
|
||||
expect(MockAppiumSupport.logger.__logger.warn).to.be.calledWith(
|
||||
MockAppiumSupport.logger.__logger.warn.calledWith(
|
||||
'Appium encountered 1 warning while validating drivers found in manifest /some/path/extensions.yaml'
|
||||
);
|
||||
).should.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -284,9 +292,9 @@ describe('ExtensionConfig', function () {
|
||||
|
||||
it('should display an error count of 1', async function () {
|
||||
await config._validate({foo: {}});
|
||||
expect(MockAppiumSupport.logger.__logger.error).to.be.calledWith(
|
||||
MockAppiumSupport.logger.__logger.error.calledWith(
|
||||
'Appium encountered 1 error while validating drivers found in manifest /some/path/extensions.yaml'
|
||||
);
|
||||
).should.be.true;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,9 +2,17 @@ import {Manifest} from '../../../lib/extension/manifest';
|
||||
import {migrate} from '../../../lib/extension/manifest-migrations';
|
||||
import {DRIVER_TYPE} from '../../../lib/constants';
|
||||
|
||||
const {expect} = chai;
|
||||
|
||||
describe('manifest-migrations', function () {
|
||||
let expect;
|
||||
|
||||
before(async function () {
|
||||
const chai = await import('chai');
|
||||
const chaiAsPromised = await import('chai-as-promised');
|
||||
chai.use(chaiAsPromised.default);
|
||||
chai.should();
|
||||
expect = chai.expect;
|
||||
});
|
||||
|
||||
describe('when no installPath property present in manifest', function () {
|
||||
it('should trigger refresh', async function () {
|
||||
const manifest = Manifest.getInstance(process.cwd());
|
||||
|
||||
@@ -7,8 +7,6 @@ import {initMocks} from './mocks';
|
||||
import {version as APPIUM_VER} from '../../../package.json';
|
||||
import EventEmitter from 'events';
|
||||
|
||||
const {expect} = chai;
|
||||
|
||||
describe('Manifest', function () {
|
||||
/**
|
||||
* @type {sinon.SinonSandbox}
|
||||
@@ -27,7 +25,15 @@ describe('Manifest', function () {
|
||||
/** @type {import('./mocks').MockGlob} */
|
||||
let MockGlob;
|
||||
|
||||
let expect;
|
||||
|
||||
before(async function () {
|
||||
const chai = await import('chai');
|
||||
const chaiAsPromised = await import('chai-as-promised');
|
||||
chai.use(chaiAsPromised.default);
|
||||
chai.should();
|
||||
expect = chai.expect;
|
||||
|
||||
yamlFixture = await fs.readFile(resolveFixture('manifest', 'v3.yaml'), 'utf8');
|
||||
});
|
||||
|
||||
@@ -143,7 +149,7 @@ describe('Manifest', function () {
|
||||
});
|
||||
|
||||
it('should create a new file', function () {
|
||||
expect(MockAppiumSupport.fs.writeFile).to.be.calledOnce;
|
||||
MockAppiumSupport.fs.writeFile.calledOnce.should.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -179,10 +185,10 @@ describe('Manifest', function () {
|
||||
await B.all([manifest.read(), manifest.read()]);
|
||||
});
|
||||
it('should not read the file twice', function () {
|
||||
expect(MockAppiumSupport.fs.readFile).to.have.been.calledOnceWith(
|
||||
MockAppiumSupport.fs.readFile.calledOnceWith(
|
||||
'/some/path/extensions.yaml',
|
||||
'utf8'
|
||||
);
|
||||
).should.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -193,15 +199,16 @@ describe('Manifest', function () {
|
||||
});
|
||||
|
||||
it('should attempt to read the file at `filepath`', function () {
|
||||
expect(MockAppiumSupport.fs.readFile).to.have.been.calledOnceWith(
|
||||
MockAppiumSupport.fs.readFile.calledOnceWith(
|
||||
'/some/path/extensions.yaml',
|
||||
'utf8'
|
||||
);
|
||||
).should.be.true;
|
||||
});
|
||||
|
||||
describe('when the data has not changed', function () {
|
||||
it('should not write the data', function () {
|
||||
expect(manifest.write).not.to.be.called;
|
||||
// @ts-ignore
|
||||
manifest.write.called.should.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -218,12 +225,13 @@ describe('Manifest', function () {
|
||||
|
||||
it('should synchronize manifest with installed extensions', async function () {
|
||||
await manifest.read();
|
||||
expect(manifest.syncWithInstalledExtensions).to.be.calledOnce;
|
||||
// @ts-ignore
|
||||
manifest.syncWithInstalledExtensions.calledOnce.should.be.true;
|
||||
});
|
||||
|
||||
it('should check if the `package.json` has changed', async function () {
|
||||
await manifest.read();
|
||||
expect(MockPackageChanged.isPackageChanged).to.be.calledOnce;
|
||||
MockPackageChanged.isPackageChanged.calledOnce.should.be.true;
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -258,7 +266,7 @@ describe('Manifest', function () {
|
||||
describe('when called again before the first call resolves', function () {
|
||||
it('should not write the file twice', async function () {
|
||||
await B.all([manifest.write(), manifest.write()]);
|
||||
expect(MockAppiumSupport.fs.writeFile).to.have.been.calledOnce;
|
||||
MockAppiumSupport.fs.writeFile.calledOnce.should.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -4,8 +4,6 @@ import {PKG_HASHFILE_RELATIVE_PATH} from '../../../lib/constants';
|
||||
import {rewiremock} from '../../helpers';
|
||||
import {initMocks} from './mocks';
|
||||
|
||||
const {expect} = chai;
|
||||
|
||||
describe('package-changed', function () {
|
||||
/** @type {typeof import('appium/lib/extension/package-changed').packageDidChange} */
|
||||
let packageDidChange;
|
||||
@@ -19,6 +17,16 @@ describe('package-changed', function () {
|
||||
/** @type {import('./mocks').MockAppiumSupport} */
|
||||
let MockAppiumSupport;
|
||||
|
||||
let expect;
|
||||
|
||||
before(async function () {
|
||||
const chai = await import('chai');
|
||||
const chaiAsPromised = await import('chai-as-promised');
|
||||
chai.use(chaiAsPromised.default);
|
||||
chai.should();
|
||||
expect = chai.expect;
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
({MockPackageChanged, MockAppiumSupport, sandbox} = initMocks());
|
||||
({packageDidChange} = rewiremock.proxy(
|
||||
@@ -46,17 +54,17 @@ describe('package-changed', function () {
|
||||
|
||||
it('it should attempt to create the parent dir for the hash file', async function () {
|
||||
await packageDidChange('/some/path');
|
||||
expect(MockAppiumSupport.fs.mkdirp).to.have.been.calledWith(
|
||||
MockAppiumSupport.fs.mkdirp.calledWith(
|
||||
path.dirname(path.join('/some/path', PKG_HASHFILE_RELATIVE_PATH))
|
||||
);
|
||||
).should.be.true;
|
||||
});
|
||||
|
||||
it('should call `package-changed` with a cwd and relative path to hash file', async function () {
|
||||
await packageDidChange('/some/path');
|
||||
expect(MockPackageChanged.isPackageChanged).to.have.been.calledWith({
|
||||
MockPackageChanged.isPackageChanged.calledWith({
|
||||
cwd: '/some/path',
|
||||
hashFilename: PKG_HASHFILE_RELATIVE_PATH,
|
||||
});
|
||||
}).should.be.true;
|
||||
});
|
||||
|
||||
describe('when it cannot create the parent dir', function () {
|
||||
@@ -85,14 +93,14 @@ describe('package-changed', function () {
|
||||
|
||||
it('should not write the hash file', async function () {
|
||||
await packageDidChange('/some/where');
|
||||
expect(MockPackageChanged.__writeHash).not.to.have.been.called;
|
||||
MockPackageChanged.__writeHash.called.should.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the package has changed per `package-changed`', function () {
|
||||
it('should write the hash file', async function () {
|
||||
await packageDidChange('/some/where');
|
||||
expect(MockPackageChanged.__writeHash).to.have.been.calledOnce;
|
||||
MockPackageChanged.__writeHash.calledOnce.should.be.true;
|
||||
});
|
||||
|
||||
it('should resolve `true`', async function () {
|
||||
|
||||
@@ -6,8 +6,6 @@ import {resetSchema} from '../../../lib/schema';
|
||||
import {resolveFixture, rewiremock} from '../../helpers';
|
||||
import {initMocks} from './mocks';
|
||||
|
||||
const {expect} = chai;
|
||||
|
||||
describe('PluginConfig', function () {
|
||||
/** @type {string} */
|
||||
let yamlFixture;
|
||||
@@ -31,7 +29,15 @@ describe('PluginConfig', function () {
|
||||
*/
|
||||
let PluginConfig;
|
||||
|
||||
let expect;
|
||||
|
||||
before(async function () {
|
||||
const chai = await import('chai');
|
||||
const chaiAsPromised = await import('chai-as-promised');
|
||||
chai.use(chaiAsPromised.default);
|
||||
chai.should();
|
||||
expect = chai.expect;
|
||||
|
||||
yamlFixture = await fs.readFile(resolveFixture('manifest', 'v3.yaml'), 'utf8');
|
||||
});
|
||||
|
||||
@@ -337,7 +343,7 @@ describe('PluginConfig', function () {
|
||||
describe('when the extension schema has not yet been registered', function () {
|
||||
it('should resolve and load the extension schema file', function () {
|
||||
pluginConfig.readExtensionSchema(extName, extData);
|
||||
expect(MockResolveFrom).to.have.been.calledOnce;
|
||||
MockResolveFrom.calledOnce.should.be.true;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,6 +6,13 @@ import {rewiremock} from '../helpers';
|
||||
describe('grid-register', function () {
|
||||
let sandbox;
|
||||
|
||||
before(async function () {
|
||||
const chai = await import('chai');
|
||||
const chaiAsPromised = await import('chai-as-promised');
|
||||
chai.use(chaiAsPromised.default);
|
||||
chai.should();
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
sandbox = createSandbox();
|
||||
});
|
||||
@@ -38,18 +45,18 @@ describe('grid-register', function () {
|
||||
describe('when provided a path to a config file', function () {
|
||||
it('should read the config file', async function () {
|
||||
await registerNode('/path/to/config-file.json');
|
||||
mocks['@appium/support'].fs.readFile.should.have.been.calledOnceWith(
|
||||
mocks['@appium/support'].fs.readFile.calledOnceWith(
|
||||
'/path/to/config-file.json',
|
||||
'utf-8'
|
||||
);
|
||||
).should.be.true;
|
||||
});
|
||||
|
||||
it('should parse the config file as JSON', async function () {
|
||||
sandbox.spy(JSON, 'parse');
|
||||
const parseSpy = sandbox.spy(JSON, 'parse');
|
||||
await registerNode('/path/to/config-file.json');
|
||||
JSON.parse.should.have.been.calledOnceWith(
|
||||
parseSpy.calledOnceWith(
|
||||
await mocks['@appium/support'].fs.readFile.firstCall.returnValue
|
||||
);
|
||||
).should.be.true;
|
||||
});
|
||||
|
||||
describe('when the config file is invalid', function () {
|
||||
@@ -65,13 +72,13 @@ describe('grid-register', function () {
|
||||
describe('when provided a config object', function () {
|
||||
it('should not attempt to read the object as a config file', async function () {
|
||||
await registerNode({my: 'config'});
|
||||
mocks['@appium/support'].fs.readFile.should.not.have.been.called;
|
||||
mocks['@appium/support'].fs.readFile.called.should.be.false;
|
||||
});
|
||||
|
||||
it('should not attempt to parse any JSON', async function () {
|
||||
sandbox.spy(JSON, 'parse');
|
||||
const parseSpy = sandbox.spy(JSON, 'parse');
|
||||
await registerNode({my: 'config'});
|
||||
JSON.parse.should.not.have.been.called;
|
||||
parseSpy.called.should.be.false;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -11,7 +11,12 @@ describe('logging', function () {
|
||||
let sandbox;
|
||||
let stderrSpy;
|
||||
let stdoutSpy;
|
||||
beforeEach(function () {
|
||||
beforeEach(async function () {
|
||||
const chai = await import('chai');
|
||||
const chaiAsPromised = await import('chai-as-promised');
|
||||
chai.use(chaiAsPromised.default);
|
||||
chai.should();
|
||||
|
||||
sandbox = createSandbox();
|
||||
stderrSpy = sandbox.spy(process.stderr, 'write');
|
||||
stdoutSpy = sandbox.spy(process.stdout, 'write');
|
||||
|
||||
@@ -14,6 +14,14 @@ const LOG_FILTERS_FIXTURE = resolveFixture('log-filters.json');
|
||||
|
||||
describe('parser', function () {
|
||||
let p;
|
||||
let should;
|
||||
|
||||
beforeEach(async function () {
|
||||
const chai = await import('chai');
|
||||
const chaiAsPromised = await import('chai-as-promised');
|
||||
chai.use(chaiAsPromised.default);
|
||||
should = chai.should();
|
||||
});
|
||||
|
||||
describe('Main Parser', function () {
|
||||
beforeEach(function () {
|
||||
|
||||
@@ -3,9 +3,15 @@
|
||||
import {DRIVER_TYPE} from '../../../lib/constants';
|
||||
import {ArgSpec} from '../../../lib/schema/arg-spec';
|
||||
|
||||
const {expect} = chai;
|
||||
|
||||
describe('ArgSpec', function () {
|
||||
let expect;
|
||||
|
||||
before(async function () {
|
||||
const chai = await import('chai');
|
||||
chai.should();
|
||||
expect = chai.expect;
|
||||
});
|
||||
|
||||
describe('class method', function () {
|
||||
describe('create()', function () {
|
||||
it('should return a new ArgSpec', function () {
|
||||
@@ -69,9 +75,11 @@ describe('ArgSpec', function () {
|
||||
});
|
||||
|
||||
describe('when provided the schema ID of an extension schema', function () {
|
||||
expect(ArgSpec.extensionInfoFromRootSchemaId('driver-stuff.json')).to.eql({
|
||||
extType: DRIVER_TYPE,
|
||||
normalizedExtName: 'stuff',
|
||||
it('should return an proper object', function () {
|
||||
expect(ArgSpec.extensionInfoFromRootSchemaId('driver-stuff.json')).to.eql({
|
||||
extType: DRIVER_TYPE,
|
||||
normalizedExtName: 'stuff',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5,9 +5,17 @@ import {PLUGIN_TYPE} from '../../../lib/constants';
|
||||
import {finalizeSchema, registerSchema, resetSchema} from '../../../lib/schema';
|
||||
import {toParserArgs} from '../../../lib/schema/cli-args';
|
||||
|
||||
const {expect} = chai;
|
||||
|
||||
describe('cli-args', function () {
|
||||
let expect;
|
||||
|
||||
before(async function () {
|
||||
const chai = await import('chai');
|
||||
const chaiAsPromised = await import('chai-as-promised');
|
||||
chai.use(chaiAsPromised.default);
|
||||
chai.should();
|
||||
expect = chai.expect;
|
||||
});
|
||||
|
||||
describe('toParserArgs()', function () {
|
||||
/**
|
||||
*
|
||||
|
||||
@@ -10,12 +10,12 @@ import DRIVER_SCHEMA_FIXTURE from '../../fixtures/driver-schema';
|
||||
import flattenedSchemaFixture from '../../fixtures/flattened-schema';
|
||||
import {rewiremock} from '../../helpers';
|
||||
|
||||
const {expect} = chai;
|
||||
|
||||
describe('schema', function () {
|
||||
/** @type {sinon.SinonSandbox} */
|
||||
let sandbox;
|
||||
|
||||
let expect;
|
||||
|
||||
/**
|
||||
* @type {import('@appium/types').Class<import('appium/lib/schema/schema').SchemaFinalizationError>}
|
||||
*/
|
||||
@@ -78,6 +78,14 @@ describe('schema', function () {
|
||||
*/
|
||||
let RoachHotelMap;
|
||||
|
||||
before(async function () {
|
||||
const chai = await import('chai');
|
||||
const chaiAsPromised = await import('chai-as-promised');
|
||||
chai.use(chaiAsPromised.default);
|
||||
chai.should();
|
||||
expect = chai.expect;
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
sandbox = createSandbox();
|
||||
|
||||
|
||||
@@ -15,6 +15,15 @@ import logger from '../../lib/logger';
|
||||
import {fs} from '@appium/support';
|
||||
|
||||
describe('utils', function () {
|
||||
let should;
|
||||
|
||||
beforeEach(async function () {
|
||||
const chai = await import('chai');
|
||||
const chaiAsPromised = await import('chai-as-promised');
|
||||
chai.use(chaiAsPromised.default);
|
||||
should = chai.should();
|
||||
});
|
||||
|
||||
describe('parseCapsForInnerDriver()', function () {
|
||||
it('should return an error if only JSONWP provided', function () {
|
||||
let {error, protocol} = parseCapsForInnerDriver(BASE_CAPS);
|
||||
|
||||
@@ -3,6 +3,94 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [9.11.2](https://github.com/appium/appium/compare/@appium/base-driver@9.11.1...@appium/base-driver@9.11.2) (2024-07-10)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **types:** update dependency type-fest to v4.21.0 ([#20335](https://github.com/appium/appium/issues/20335)) ([8894b9a](https://github.com/appium/appium/commit/8894b9adf709646108cc8d6426bbb690550609f2))
|
||||
|
||||
|
||||
|
||||
## [9.11.1](https://github.com/appium/appium/compare/@appium/base-driver@9.11.0...@appium/base-driver@9.11.1) (2024-06-28)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **driver-test-support:** update dependency @types/lodash to v4.17.6 ([82e40c1](https://github.com/appium/appium/commit/82e40c1400e23f341c45d788982ccc5905827d7c))
|
||||
* **images-plugin:** update dependency lru-cache to v10.3.0 ([#20306](https://github.com/appium/appium/issues/20306)) ([e43c7a4](https://github.com/appium/appium/commit/e43c7a4dce1f50fbd4d028cbeac5677422210b20))
|
||||
|
||||
|
||||
|
||||
## [9.11.0](https://github.com/appium/appium/compare/@appium/base-driver@9.10.3...@appium/base-driver@9.11.0) (2024-06-27)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **appium:** Improve context logging ([#20250](https://github.com/appium/appium/issues/20250)) ([f675abc](https://github.com/appium/appium/commit/f675abc27b3e6beac2431cc71afb5fc2c2f70534))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **base-driver:** Check if app path is relative earlier ([#20276](https://github.com/appium/appium/issues/20276)) ([2882e71](https://github.com/appium/appium/commit/2882e7163159b7918d54cc4521105a1fdf55da9a))
|
||||
* **base-driver:** update dependency path-to-regexp to v7 ([#20279](https://github.com/appium/appium/issues/20279)) ([a746eef](https://github.com/appium/appium/commit/a746eefbbb627c7f7dd9d1ce13226a88d9527129))
|
||||
* **types:** update dependency type-fest to v4.20.1 ([#20255](https://github.com/appium/appium/issues/20255)) ([1984553](https://github.com/appium/appium/commit/19845531f558e2b16dfae807c768e1b9f2cab25d))
|
||||
|
||||
|
||||
|
||||
## [9.10.3](https://github.com/appium/appium/compare/@appium/base-driver@9.10.2...@appium/base-driver@9.10.3) (2024-06-11)
|
||||
|
||||
**Note:** Version bump only for package @appium/base-driver
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [9.10.2](https://github.com/appium/appium/compare/@appium/base-driver@9.10.1...@appium/base-driver@9.10.2) (2024-06-11)
|
||||
|
||||
**Note:** Version bump only for package @appium/base-driver
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [9.10.1](https://github.com/appium/appium/compare/@appium/base-driver@9.10.0...@appium/base-driver@9.10.1) (2024-06-11)
|
||||
|
||||
**Note:** Version bump only for package @appium/base-driver
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [9.10.0](https://github.com/appium/appium/compare/@appium/base-driver@9.9.0...@appium/base-driver@9.10.0) (2024-06-10)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **appium:** Add session signature to all logs ([#20202](https://github.com/appium/appium/issues/20202)) ([#20214](https://github.com/appium/appium/issues/20214)) ([0363aab](https://github.com/appium/appium/commit/0363aab8ba4fe0ec49845db2f493001aa873578b)), closes [#20222](https://github.com/appium/appium/issues/20222)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **driver-test-support:** update dependency @types/lodash to v4.17.5 ([69bbb53](https://github.com/appium/appium/commit/69bbb5370e671d2809a5bf997936b0b3cc1a0a0b))
|
||||
* **types:** update dependency type-fest to v4.20.0 ([#20227](https://github.com/appium/appium/issues/20227)) ([ea00626](https://github.com/appium/appium/commit/ea00626091e3ce87f6e3ed7a61003295272835e3))
|
||||
|
||||
|
||||
|
||||
## [9.9.0](https://github.com/appium/appium/compare/@appium/base-driver@9.8.1...@appium/base-driver@9.9.0) (2024-06-06)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **appium:** Add session signature to all logs ([#20202](https://github.com/appium/appium/issues/20202)) ([b3f8a47](https://github.com/appium/appium/commit/b3f8a47c2d3fa029bdb5592d7130c6d1664e53b5))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **appium:** Revert changes in 20203 and 20202 ([#20209](https://github.com/appium/appium/issues/20209)) ([40def9d](https://github.com/appium/appium/commit/40def9dbdbde64706111900967d66735257b7404)), closes [#20202](https://github.com/appium/appium/issues/20202) [#20203](https://github.com/appium/appium/issues/20203)
|
||||
|
||||
|
||||
|
||||
## [9.8.1](https://github.com/appium/appium/compare/@appium/base-driver@9.8.0...@appium/base-driver@9.8.1) (2024-06-06)
|
||||
|
||||
**Note:** Version bump only for package @appium/base-driver
|
||||
|
||||
@@ -20,6 +20,7 @@ import {
|
||||
import B from 'bluebird';
|
||||
import _ from 'lodash';
|
||||
import {fixCaps, isW3cCaps} from '../helpers/capabilities';
|
||||
import {calcSignature} from '../helpers/session';
|
||||
import {DELETE_SESSION_COMMAND, determineProtocol, errors} from '../protocol';
|
||||
import {processCapabilities, validateCaps} from './capabilities';
|
||||
import {DriverCore} from './core';
|
||||
@@ -307,7 +308,12 @@ export class BaseDriver<
|
||||
this.newCommandTimeoutMs = (this.caps.newCommandTimeout as number) * 1000;
|
||||
}
|
||||
|
||||
this._log.prefix = helpers.generateDriverLogPrefix(this, this.sessionId);
|
||||
this._log.prefix = helpers.generateDriverLogPrefix(this);
|
||||
|
||||
this.log.updateAsyncContext({
|
||||
sessionId: this.sessionId,
|
||||
sessionSignature: calcSignature(this.sessionId),
|
||||
});
|
||||
|
||||
this.log.info(`Session created with session id: ${this.sessionId}`);
|
||||
|
||||
@@ -347,7 +353,6 @@ export class BaseDriver<
|
||||
}
|
||||
}
|
||||
this.sessionId = null;
|
||||
this._log.prefix = helpers.generateDriverLogPrefix(this);
|
||||
}
|
||||
|
||||
logExtraCaps(caps: Capabilities<C>) {
|
||||
|
||||
@@ -105,6 +105,14 @@ export async function configureApp(
|
||||
};
|
||||
const {protocol, pathname} = parseAppLink(app);
|
||||
const isUrl = isSupportedUrl(app);
|
||||
if (!isUrl && !path.isAbsolute(newApp)) {
|
||||
newApp = path.resolve(process.cwd(), newApp);
|
||||
logger.warn(
|
||||
`The current application path '${app}' is not absolute ` +
|
||||
`and has been rewritten to '${newApp}'. Consider using absolute paths rather than relative`
|
||||
);
|
||||
app = newApp;
|
||||
}
|
||||
const appCacheKey = toCacheKey(app);
|
||||
|
||||
const cachedAppInfo = APPLICATIONS_CACHE.get(appCacheKey);
|
||||
@@ -276,13 +284,6 @@ export async function configureApp(
|
||||
}
|
||||
}
|
||||
logger.info(`Unzipped local app to '${newApp}'`);
|
||||
} else if (!path.isAbsolute(newApp)) {
|
||||
newApp = path.resolve(process.cwd(), newApp);
|
||||
logger.warn(
|
||||
`The current application path '${app}' is not absolute ` +
|
||||
`and has been rewritten to '${newApp}'. Consider using absolute paths rather than relative`
|
||||
);
|
||||
app = newApp;
|
||||
}
|
||||
|
||||
const storeAppInCache = async (appPathToCache) => {
|
||||
@@ -401,15 +402,14 @@ export function parseCapsArray(cap) {
|
||||
* Generate a string that uniquely describes driver instance
|
||||
*
|
||||
* @param {import('@appium/types').Core} obj driver instance
|
||||
* @param {string?} sessionId session identifier (if exists)
|
||||
* @param {string?} [sessionId=null] session identifier (if exists).
|
||||
* This parameter is deprecated and is not used.
|
||||
* @returns {string}
|
||||
*/
|
||||
export function generateDriverLogPrefix(obj, sessionId = null) {
|
||||
const instanceName = `${obj.constructor.name}@${node.getObjectId(obj).substring(0, 4)}`;
|
||||
return sessionId ? `${instanceName} (${sessionId.substring(0, 8)})` : instanceName;
|
||||
return `${obj.constructor.name}@${node.getObjectId(obj).substring(0, 4)}`;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sends a HTTP GET query to fetch the app with caching enabled.
|
||||
* Follows https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching
|
||||
|
||||
@@ -144,6 +144,9 @@ async function handleIdempotency(req, res, next) {
|
||||
}
|
||||
|
||||
const key = _.isArray(keyOrArr) ? keyOrArr[0] : keyOrArr;
|
||||
|
||||
log.updateAsyncContext({idempotencyKey: key});
|
||||
|
||||
if (!MONITORED_METHODS.includes(req.method)) {
|
||||
// GET, DELETE, etc. requests are idempotent by default
|
||||
// there is no need to cache them
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import _ from 'lodash';
|
||||
import log from './logger';
|
||||
import {errors} from '../protocol';
|
||||
import {handleIdempotency} from './idempotency';
|
||||
export {handleIdempotency} from './idempotency';
|
||||
import {pathToRegexp} from 'path-to-regexp';
|
||||
import {util} from '@appium/support';
|
||||
import {calcSignature} from '../helpers/session';
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -68,6 +70,24 @@ export function fixPythonContentType(basePath) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {import('express').Request} req
|
||||
* @param {import('express').Response} res
|
||||
* @param {import('express').NextFunction} next
|
||||
* @returns {any}
|
||||
*/
|
||||
export function handleLogContext(req, res, next) {
|
||||
const requestId = util.uuidV4();
|
||||
|
||||
const sessionId = SESSION_ID_PATTERN.exec(req.url)?.[1];
|
||||
const sessionInfo = sessionId ? {sessionId, sessionSignature: calcSignature(sessionId)} : {};
|
||||
|
||||
log.updateAsyncContext({requestId, ...sessionInfo}, true);
|
||||
|
||||
return next();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {import('express').Request} req
|
||||
@@ -168,5 +188,3 @@ function patchWithSessionId(req, body) {
|
||||
}
|
||||
return body;
|
||||
}
|
||||
|
||||
export { handleIdempotency };
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
handleIdempotency,
|
||||
handleUpgrade,
|
||||
catch404Handler,
|
||||
handleLogContext,
|
||||
} from './middleware';
|
||||
import {guineaPig, guineaPigScrollable, guineaPigAppBanner, welcome, STATIC_DIR} from './static';
|
||||
import {produceError, produceCrash} from './crash';
|
||||
@@ -27,10 +28,10 @@ import {
|
||||
} from './websocket';
|
||||
import B from 'bluebird';
|
||||
import {DEFAULT_BASE_PATH} from '../constants';
|
||||
import {EventEmitter} from 'events';
|
||||
import {fs} from '@appium/support';
|
||||
import {fs, timing} from '@appium/support';
|
||||
|
||||
const KEEP_ALIVE_TIMEOUT_MS = 10 * 60 * 1000; // 10 minutes
|
||||
const SERVER_CLOSE_TIMEOUT_MS = 5000;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -146,6 +147,7 @@ function configureServer({
|
||||
basePath = normalizeBasePath(basePath);
|
||||
|
||||
app.use(endLogFormatter);
|
||||
app.use(handleLogContext);
|
||||
|
||||
// set up static assets
|
||||
app.use(favicon(path.resolve(STATIC_DIR, 'favicon.ico')));
|
||||
@@ -190,10 +192,6 @@ function configureServer({
|
||||
* @returns {AppiumServer}
|
||||
*/
|
||||
function configureHttp({httpServer, reject, keepAliveTimeout}) {
|
||||
const serverState = {
|
||||
notifier: new EventEmitter(),
|
||||
closed: false,
|
||||
};
|
||||
/**
|
||||
* @type {AppiumServer}
|
||||
*/
|
||||
@@ -206,23 +204,34 @@ function configureHttp({httpServer, reject, keepAliveTimeout}) {
|
||||
|
||||
// http.Server.close() only stops new connections, but we need to wait until
|
||||
// all connections are closed and the `close` event is emitted
|
||||
const close = appiumServer.close.bind(appiumServer);
|
||||
const originalClose = appiumServer.close.bind(appiumServer);
|
||||
appiumServer.close = async () =>
|
||||
await new B((resolve, reject) => {
|
||||
// https://github.com/nodejs/node-v0.x-archive/issues/9066#issuecomment-124210576
|
||||
serverState.closed = true;
|
||||
serverState.notifier.emit('shutdown');
|
||||
log.info('Waiting until the server is closed');
|
||||
httpServer.on('close', () => {
|
||||
log.info('Received server close event');
|
||||
resolve();
|
||||
await new B((_resolve, _reject) => {
|
||||
log.info('Closing Appium HTTP server');
|
||||
const timer = new timing.Timer().start();
|
||||
const onTimeout = setTimeout(() => {
|
||||
log.info(
|
||||
`Not all active connections have been closed within ` +
|
||||
`${timer.getDuration().asMilliSeconds.toFixed(0)}ms. Exiting anyway.`
|
||||
);
|
||||
process.exit(process.exitCode ?? 0);
|
||||
}, SERVER_CLOSE_TIMEOUT_MS);
|
||||
httpServer.once('close', () => {
|
||||
log.info(
|
||||
`Appium HTTP server has been succesfully closed after ` +
|
||||
`${timer.getDuration().asMilliSeconds.toFixed(0)}ms`
|
||||
);
|
||||
clearTimeout(onTimeout);
|
||||
_resolve();
|
||||
});
|
||||
close((err) => {
|
||||
if (err) reject(err); // eslint-disable-line curly
|
||||
originalClose((/** @type {Error|undefined} */ err) => {
|
||||
if (err) {
|
||||
_reject(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
appiumServer.on(
|
||||
appiumServer.once(
|
||||
'error',
|
||||
/** @param {NodeJS.ErrnoException} err */ (err) => {
|
||||
if (err.code === 'EADDRNOTAVAIL') {
|
||||
@@ -240,31 +249,7 @@ function configureHttp({httpServer, reject, keepAliveTimeout}) {
|
||||
}
|
||||
);
|
||||
|
||||
appiumServer.on(
|
||||
'connection',
|
||||
/** @param {AppiumServerSocket} socket */ (socket) => {
|
||||
socket.setTimeout(keepAliveTimeout);
|
||||
socket.on('error', reject);
|
||||
|
||||
function destroy() {
|
||||
socket.destroy();
|
||||
}
|
||||
socket._openReqCount = 0;
|
||||
socket.once('close', () => serverState.notifier.removeListener('shutdown', destroy));
|
||||
serverState.notifier.once('shutdown', destroy);
|
||||
}
|
||||
);
|
||||
|
||||
appiumServer.on('request', function (req, res) {
|
||||
const socket = /** @type {AppiumServerSocket} */ (req.connection || req.socket);
|
||||
socket._openReqCount++;
|
||||
res.on('finish', function () {
|
||||
socket._openReqCount--;
|
||||
if (serverState.closed && socket._openReqCount === 0) {
|
||||
socket.destroy();
|
||||
}
|
||||
});
|
||||
});
|
||||
appiumServer.on('connection', (socket) => socket.setTimeout(keepAliveTimeout));
|
||||
|
||||
return appiumServer;
|
||||
}
|
||||
@@ -322,7 +307,6 @@ export {server, configureServer, normalizeBasePath};
|
||||
|
||||
/**
|
||||
* @typedef {import('@appium/types').AppiumServer} AppiumServer
|
||||
* @typedef {import('@appium/types').AppiumServerSocket} AppiumServerSocket
|
||||
*/
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* Extract the first 8 characters of session ID to prefix the log.
|
||||
*
|
||||
* @param sessionId session identifier
|
||||
*/
|
||||
export function calcSignature(sessionId: string): string {
|
||||
return sessionId.substring(0, 8);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import _ from 'lodash';
|
||||
import {util, logger, node} from '@appium/support';
|
||||
import {util, logger} from '@appium/support';
|
||||
import {validators} from './validators';
|
||||
import {
|
||||
errors,
|
||||
@@ -14,6 +14,7 @@ import {formatResponseValue, formatStatus} from './helpers';
|
||||
import {MAX_LOG_BODY_LENGTH, PROTOCOLS, DEFAULT_BASE_PATH} from '../constants';
|
||||
import {isW3cCaps} from '../helpers/capabilities';
|
||||
import log from '../basedriver/logger';
|
||||
import { generateDriverLogPrefix } from '../basedriver/helpers';
|
||||
|
||||
const CREATE_SESSION_COMMAND = 'createSession';
|
||||
const DELETE_SESSION_COMMAND = 'deleteSession';
|
||||
@@ -60,12 +61,7 @@ function getLogger(driver, sessionId = null) {
|
||||
return dstDriver.log;
|
||||
}
|
||||
|
||||
let logPrefix = dstDriver.constructor
|
||||
? `${dstDriver.constructor.name}@${node.getObjectId(dstDriver).substring(0, 8)}`
|
||||
: 'AppiumDriver';
|
||||
if (sessionId) {
|
||||
logPrefix += ` (${sessionId.substring(0, 8)})`;
|
||||
}
|
||||
const logPrefix = generateDriverLogPrefix(dstDriver);
|
||||
return logger.getLogger(logPrefix);
|
||||
}
|
||||
|
||||
@@ -259,9 +255,7 @@ function routeConfiguringFunction(driver) {
|
||||
throw new Error('Drivers must implement `sessionExists` property');
|
||||
}
|
||||
|
||||
// "execute" isn't defined anywhere
|
||||
// @ts-expect-error
|
||||
if (!(driver.executeCommand || driver.execute)) {
|
||||
if (!(/** @type {any} */ (driver).executeCommand || /** @type {any} */ (driver).execute)) {
|
||||
throw new Error('Drivers must implement `executeCommand` or `execute` method');
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import _ from 'lodash';
|
||||
import {util} from '@appium/support';
|
||||
import {PROTOCOLS, DEFAULT_BASE_PATH} from '../constants';
|
||||
import {pathToRegexp} from 'path-to-regexp';
|
||||
|
||||
const SET_ALERT_TEXT_PAYLOAD_PARAMS = {
|
||||
validate: (jsonObj) =>
|
||||
@@ -20,7 +21,7 @@ const SET_ALERT_TEXT_PAYLOAD_PARAMS = {
|
||||
* `optional`
|
||||
* @satisfies {import('@appium/types').MethodMap<import('../basedriver/driver').BaseDriver>}
|
||||
*/
|
||||
const METHOD_MAP = /** @type {const} */ ({
|
||||
export const METHOD_MAP = /** @type {const} */ ({
|
||||
'/status': {
|
||||
GET: {command: 'getStatus'},
|
||||
},
|
||||
@@ -918,90 +919,48 @@ const METHOD_MAP = /** @type {const} */ ({
|
||||
});
|
||||
|
||||
// driver command names
|
||||
let ALL_COMMANDS = [];
|
||||
for (let v of _.values(METHOD_MAP)) {
|
||||
for (let m of _.values(v)) {
|
||||
if ('command' in m && m.command) {
|
||||
ALL_COMMANDS.push(m.command);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const RE_ESCAPE = /[-[\]{}()+?.,\\^$|#\s]/g;
|
||||
const RE_PARAM = /([:*])(\w+)/g;
|
||||
|
||||
class Route {
|
||||
constructor(route) {
|
||||
this.paramNames = [];
|
||||
|
||||
let reStr = route.replace(RE_ESCAPE, '\\$&');
|
||||
reStr = reStr.replace(RE_PARAM, (_, mode, name) => {
|
||||
this.paramNames.push(name);
|
||||
return mode === ':' ? '([^/]*)' : '(.*)';
|
||||
});
|
||||
this.routeRegexp = new RegExp(`^${reStr}$`);
|
||||
}
|
||||
|
||||
parse(url) {
|
||||
//if (url.indexOf('timeouts') !== -1 && this.routeRegexp.toString().indexOf('timeouts') !== -1) {
|
||||
//debugger;
|
||||
//}
|
||||
let matches = url.match(this.routeRegexp);
|
||||
if (!matches) return; // eslint-disable-line curly
|
||||
let i = 0;
|
||||
let params = {};
|
||||
while (i < this.paramNames.length) {
|
||||
const paramName = this.paramNames[i++];
|
||||
params[paramName] = matches[i];
|
||||
}
|
||||
return params;
|
||||
}
|
||||
}
|
||||
export const ALL_COMMANDS = _.flatMap(_.values(METHOD_MAP).map(_.values))
|
||||
.filter((m) => Boolean(m.command))
|
||||
.map((m) => m.command);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} endpoint
|
||||
* @param {import('@appium/types').HTTPMethod} method
|
||||
* @param {string} [basePath]
|
||||
* @param {string} [basePath=DEFAULT_BASE_PATH]
|
||||
* @returns {string|undefined}
|
||||
*/
|
||||
function routeToCommandName(endpoint, method, basePath = DEFAULT_BASE_PATH) {
|
||||
let dstRoute = null;
|
||||
|
||||
// remove any query string
|
||||
// TODO: would this be better handled as a `URL` object?
|
||||
if (endpoint.includes('?')) {
|
||||
endpoint = endpoint.slice(0, endpoint.indexOf('?'));
|
||||
export function routeToCommandName(endpoint, method, basePath = DEFAULT_BASE_PATH) {
|
||||
let normalizedEndpoint = basePath
|
||||
? endpoint.replace(new RegExp(`^${_.escapeRegExp(basePath)}`), '')
|
||||
: endpoint;
|
||||
normalizedEndpoint = `${_.startsWith(normalizedEndpoint, '/') ? '' : '/'}${normalizedEndpoint}`;
|
||||
/** @type {string} */
|
||||
let normalizedPathname;
|
||||
try {
|
||||
// we could use any prefix there as we anyway need to only extract the pathname
|
||||
normalizedPathname = new URL(`https://appium.io${normalizedEndpoint}`).pathname;
|
||||
} catch (err) {
|
||||
throw new Error(`'${endpoint}' cannot be translated to a command name: ${err.message}`);
|
||||
}
|
||||
|
||||
const actualEndpoint =
|
||||
endpoint === '/' ? '' : _.startsWith(endpoint, '/') ? endpoint : `/${endpoint}`;
|
||||
|
||||
for (let currentRoute of _.keys(METHOD_MAP)) {
|
||||
const route = new Route(`${basePath}${currentRoute}`);
|
||||
// we don't care about the actual session id for matching
|
||||
if (
|
||||
route.parse(`${basePath}/session/ignored-session-id${actualEndpoint}`) ||
|
||||
route.parse(`${basePath}${actualEndpoint}`) ||
|
||||
route.parse(actualEndpoint)
|
||||
) {
|
||||
dstRoute = currentRoute;
|
||||
break;
|
||||
}
|
||||
/** @type {string[]} */
|
||||
const possiblePathnames = [];
|
||||
if (!normalizedPathname.startsWith('/session/')) {
|
||||
possiblePathnames.push(`/session/any-session-id${normalizedPathname}`);
|
||||
}
|
||||
if (!dstRoute) return; // eslint-disable-line curly
|
||||
|
||||
const methods = METHOD_MAP[dstRoute];
|
||||
method = /** @type {Uppercase<typeof method>} */ (_.toUpper(method));
|
||||
if (method in methods) {
|
||||
const dstMethod = _.get(methods, method);
|
||||
if (dstMethod.command) {
|
||||
return dstMethod.command;
|
||||
possiblePathnames.push(normalizedPathname);
|
||||
const normalizedMethod = _.toUpper(method);
|
||||
for (const [routePath, routeSpec] of _.toPairs(METHOD_MAP)) {
|
||||
const routeRegexp = pathToRegexp(routePath);
|
||||
if (possiblePathnames.some((pp) => routeRegexp.test(pp))) {
|
||||
const commandName = routeSpec?.[normalizedMethod]?.command;
|
||||
if (commandName) {
|
||||
return commandName;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// driver commands that do not require a session to already exist
|
||||
const NO_SESSION_ID_COMMANDS = ['createSession', 'getStatus', 'getSessions'];
|
||||
|
||||
export {METHOD_MAP, ALL_COMMANDS, NO_SESSION_ID_COMMANDS, routeToCommandName};
|
||||
export const NO_SESSION_ID_COMMANDS = ['createSession', 'getStatus', 'getSessions'];
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@appium/base-driver",
|
||||
"version": "9.8.1",
|
||||
"version": "9.11.2",
|
||||
"description": "Base driver class for Appium drivers",
|
||||
"keywords": [
|
||||
"automation",
|
||||
@@ -44,13 +44,13 @@
|
||||
"test:types": "tsd"
|
||||
},
|
||||
"dependencies": {
|
||||
"@appium/support": "^4.4.0",
|
||||
"@appium/types": "^0.19.1",
|
||||
"@appium/support": "^5.1.2",
|
||||
"@appium/types": "^0.21.1",
|
||||
"@colors/colors": "1.6.0",
|
||||
"@types/async-lock": "1.4.2",
|
||||
"@types/bluebird": "3.5.42",
|
||||
"@types/express": "4.17.21",
|
||||
"@types/lodash": "4.17.4",
|
||||
"@types/lodash": "4.17.6",
|
||||
"@types/method-override": "0.0.35",
|
||||
"@types/serve-favicon": "2.5.7",
|
||||
"async-lock": "1.4.1",
|
||||
@@ -61,13 +61,13 @@
|
||||
"express": "4.19.2",
|
||||
"http-status-codes": "2.3.0",
|
||||
"lodash": "4.17.21",
|
||||
"lru-cache": "10.2.2",
|
||||
"lru-cache": "10.3.0",
|
||||
"method-override": "3.0.0",
|
||||
"morgan": "1.10.0",
|
||||
"path-to-regexp": "6.2.2",
|
||||
"path-to-regexp": "7.0.0",
|
||||
"serve-favicon": "2.5.0",
|
||||
"source-map-support": "0.5.21",
|
||||
"type-fest": "4.19.0",
|
||||
"type-fest": "4.21.0",
|
||||
"validate.js": "0.13.1"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
|
||||
@@ -15,6 +15,13 @@ function getFixture(file) {
|
||||
}
|
||||
|
||||
describe('app download and configuration', function () {
|
||||
before(async function () {
|
||||
const chai = await import('chai');
|
||||
const chaisAsPromised = await import('chai-as-promised');
|
||||
chai.use(chaisAsPromised.default);
|
||||
chai.should();
|
||||
});
|
||||
|
||||
describe('configureApp', function () {
|
||||
it('should get the path for a local .app', async function () {
|
||||
let newAppPath = await configureApp(getFixture('FakeIOSApp.app'), '.app');
|
||||
|
||||
@@ -13,6 +13,11 @@ describe('Websockets (e2e)', function () {
|
||||
const WS_DATA = 'Hello';
|
||||
|
||||
before(async function () {
|
||||
const chai = await import('chai');
|
||||
const chaisAsPromised = await import('chai-as-promised');
|
||||
chai.use(chaisAsPromised.default);
|
||||
chai.should();
|
||||
|
||||
driver = new FakeDriver();
|
||||
driver.sessionId = SESSION_ID;
|
||||
port = await getTestPort();
|
||||
|
||||
@@ -21,6 +21,11 @@ describe('server', function () {
|
||||
let port;
|
||||
let sandbox;
|
||||
before(async function () {
|
||||
const chai = await import('chai');
|
||||
const chaisAsPromised = await import('chai-as-promised');
|
||||
chai.use(chaisAsPromised.default);
|
||||
chai.should();
|
||||
|
||||
port = await getTestPort(true);
|
||||
|
||||
function configureRoutes(app) {
|
||||
@@ -119,6 +124,11 @@ describe('tls server', function () {
|
||||
});
|
||||
|
||||
before(async function () {
|
||||
const chai = await import('chai');
|
||||
const chaisAsPromised = await import('chai-as-promised');
|
||||
chai.use(chaisAsPromised.default);
|
||||
chai.should();
|
||||
|
||||
try {
|
||||
await generateCertificate(certPath, keyPath);
|
||||
} catch (e) {
|
||||
@@ -167,6 +177,11 @@ describe('server plugins', function () {
|
||||
let port;
|
||||
|
||||
before(async function () {
|
||||
const chai = await import('chai');
|
||||
const chaisAsPromised = await import('chai-as-promised');
|
||||
chai.use(chaisAsPromised.default);
|
||||
chai.should();
|
||||
|
||||
port = await getTestPort(true);
|
||||
});
|
||||
|
||||
|
||||
@@ -4,7 +4,14 @@ import {FakeDriver} from '../protocol/fake-driver';
|
||||
describe('proxy', function () {
|
||||
const jwproxy = new JWProxy();
|
||||
let baseServer;
|
||||
let should;
|
||||
|
||||
before(async function () {
|
||||
const chai = await import('chai');
|
||||
const chaisAsPromised = await import('chai-as-promised');
|
||||
chai.use(chaisAsPromised.default);
|
||||
should = chai.should();
|
||||
|
||||
baseServer = await server({
|
||||
routeConfiguringFunction: routeConfiguringFunction(new FakeDriver()),
|
||||
port: 4444,
|
||||
|
||||
@@ -12,6 +12,7 @@ let baseUrl;
|
||||
|
||||
describe('Protocol', function () {
|
||||
let sandbox;
|
||||
let should;
|
||||
|
||||
beforeEach(function () {
|
||||
sandbox = createSandbox();
|
||||
@@ -22,6 +23,11 @@ describe('Protocol', function () {
|
||||
});
|
||||
|
||||
before(async function () {
|
||||
const chai = await import('chai');
|
||||
const chaisAsPromised = await import('chai-as-promised');
|
||||
chai.use(chaisAsPromised.default);
|
||||
should = chai.should();
|
||||
|
||||
port = await getTestPort();
|
||||
baseUrl = `http://${TEST_HOST}:${port}`;
|
||||
});
|
||||
|
||||
@@ -14,6 +14,15 @@ import {BASE_DESIRED_CAP_CONSTRAINTS} from '@appium/types';
|
||||
import {isW3cCaps} from '../../../lib/helpers/capabilities';
|
||||
|
||||
describe('caps', function () {
|
||||
let should;
|
||||
|
||||
before(async function () {
|
||||
const chai = await import('chai');
|
||||
const chaiAsPromised = await import('chai-as-promised');
|
||||
chai.use(chaiAsPromised.default);
|
||||
should = chai.should();
|
||||
});
|
||||
|
||||
// Tests based on: https://www.w3.org/TR/webdriver/#dfn-validate-caps
|
||||
describe('#validateCaps', function () {
|
||||
it('returns invalid argument error if "capability" is not a JSON object (1)', function () {
|
||||
|
||||
@@ -11,12 +11,24 @@ describe('Desired Capabilities', function () {
|
||||
/** @type {BaseDriver} */
|
||||
let d;
|
||||
let sandbox;
|
||||
/** @type {import('sinon').SinonSpy} */
|
||||
let logWarnSpy;
|
||||
/** @type {import('sinon').SinonStub} */
|
||||
let deprecatedStub;
|
||||
|
||||
before(async function () {
|
||||
const chai = await import('chai');
|
||||
const chaiAsPromised = await import('chai-as-promised');
|
||||
chai.use(chaiAsPromised.default);
|
||||
chai.should();
|
||||
});
|
||||
|
||||
|
||||
beforeEach(function () {
|
||||
d = new BaseDriver();
|
||||
sandbox = createSandbox();
|
||||
sandbox.spy(d.log, 'warn');
|
||||
sandbox.stub(validator.validators, 'deprecated');
|
||||
logWarnSpy = sandbox.spy(d.log, 'warn');
|
||||
deprecatedStub = sandbox.stub(validator.validators, 'deprecated');
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
@@ -118,7 +130,7 @@ describe('Desired Capabilities', function () {
|
||||
firstMatch: [{}],
|
||||
});
|
||||
|
||||
d.log.warn.should.have.been.called;
|
||||
logWarnSpy.called.should.be.true;
|
||||
});
|
||||
|
||||
it('should be sensitive to the case of caps', async function () {
|
||||
@@ -143,7 +155,7 @@ describe('Desired Capabilities', function () {
|
||||
},
|
||||
firstMatch: [{}],
|
||||
});
|
||||
d.log.warn.should.have.been.called;
|
||||
logWarnSpy.called.should.be.true;
|
||||
|
||||
let sessions = await d.getSessions();
|
||||
sessions[0].capabilities.noReset.should.eql(false);
|
||||
@@ -158,7 +170,7 @@ describe('Desired Capabilities', function () {
|
||||
},
|
||||
firstMatch: [{}],
|
||||
});
|
||||
d.log.warn.should.have.been.called;
|
||||
logWarnSpy.called.should.be.true;
|
||||
|
||||
let sessions = await d.getSessions();
|
||||
sessions[0].capabilities.noReset.should.eql(true);
|
||||
@@ -172,7 +184,7 @@ describe('Desired Capabilities', function () {
|
||||
},
|
||||
firstMatch: [{}],
|
||||
});
|
||||
d.log.warn.should.not.have.been.called;
|
||||
logWarnSpy.called.should.be.false;
|
||||
|
||||
let sessions = await d.getSessions();
|
||||
sessions[0].capabilities.language.should.eql('true');
|
||||
@@ -189,7 +201,7 @@ describe('Desired Capabilities', function () {
|
||||
},
|
||||
firstMatch: [{}],
|
||||
});
|
||||
d.log.warn.should.have.been.called;
|
||||
logWarnSpy.called.should.be.true;
|
||||
|
||||
let sessions = await d.getSessions();
|
||||
sessions[0].capabilities.newCommandTimeout.should.eql(1);
|
||||
@@ -204,7 +216,7 @@ describe('Desired Capabilities', function () {
|
||||
},
|
||||
firstMatch: [{}],
|
||||
});
|
||||
d.log.warn.should.have.been.called;
|
||||
logWarnSpy.called.should.be.true;
|
||||
|
||||
let sessions = await d.getSessions();
|
||||
sessions[0].capabilities.newCommandTimeout.should.eql(1.1);
|
||||
@@ -218,7 +230,7 @@ describe('Desired Capabilities', function () {
|
||||
},
|
||||
firstMatch: [{}],
|
||||
});
|
||||
d.log.warn.should.not.have.been.called;
|
||||
logWarnSpy.called.should.be.false;
|
||||
|
||||
let sessions = await d.getSessions();
|
||||
sessions[0].capabilities.language.should.eql('1');
|
||||
@@ -254,7 +266,7 @@ describe('Desired Capabilities', function () {
|
||||
firstMatch: [{}],
|
||||
});
|
||||
|
||||
validator.validators.deprecated.should.have.been.calledWith(5, true, 'lynx-version');
|
||||
(deprecatedStub.calledWith(5, true, 'lynx-version')).should.be.true;
|
||||
});
|
||||
|
||||
it('should not warn if deprecated=false', async function () {
|
||||
@@ -274,7 +286,7 @@ describe('Desired Capabilities', function () {
|
||||
firstMatch: [{}],
|
||||
});
|
||||
|
||||
d.log.warn.should.not.have.been.called;
|
||||
logWarnSpy.called.should.be.false;
|
||||
});
|
||||
|
||||
it('should not validate against null/undefined caps', async function () {
|
||||
|
||||
@@ -2,6 +2,13 @@ import _ from 'lodash';
|
||||
import {BaseDriver} from '../../../../lib';
|
||||
|
||||
describe('logging custom events', function () {
|
||||
before(async function () {
|
||||
const chai = await import('chai');
|
||||
const chaiAsPromised = await import('chai-as-promised');
|
||||
chai.use(chaiAsPromised.default);
|
||||
chai.should();
|
||||
});
|
||||
|
||||
it('should allow logging of events', async function () {
|
||||
const d = new BaseDriver();
|
||||
d._eventHistory.should.eql({commands: []});
|
||||
@@ -19,6 +26,13 @@ describe('logging custom events', function () {
|
||||
});
|
||||
|
||||
describe('#getLogEvents', function () {
|
||||
before(async function () {
|
||||
const chai = await import('chai');
|
||||
const chaiAsPromised = await import('chai-as-promised');
|
||||
chai.use(chaiAsPromised.default);
|
||||
chai.should();
|
||||
});
|
||||
|
||||
it('should allow to get all events', async function () {
|
||||
const d = new BaseDriver();
|
||||
d._eventHistory.should.eql({commands: []});
|
||||
|
||||
@@ -3,8 +3,6 @@ import {createSandbox} from 'sinon';
|
||||
import _ from 'lodash';
|
||||
import BaseDriver from '../../../../lib';
|
||||
|
||||
const expect = chai.expect;
|
||||
|
||||
const FIRST_LOGS = ['first', 'logs'];
|
||||
const SECOND_LOGS = ['second', 'logs'];
|
||||
const SUPPORTED_LOG_TYPES = {
|
||||
@@ -20,8 +18,14 @@ const SUPPORTED_LOG_TYPES = {
|
||||
|
||||
describe('log commands -', function () {
|
||||
let sandbox;
|
||||
|
||||
let driver;
|
||||
let expect;
|
||||
|
||||
before(async function () {
|
||||
const chai = await import('chai');
|
||||
chai.should();
|
||||
expect = chai.expect;
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
sandbox = createSandbox();
|
||||
|
||||
@@ -3,10 +3,17 @@ import sinon from 'sinon';
|
||||
import {DeviceSettings, MAX_SETTINGS_SIZE} from '../../../lib/basedriver/device-settings';
|
||||
import {InvalidArgumentError} from '../../../lib/protocol/errors';
|
||||
|
||||
const {expect} = chai;
|
||||
|
||||
describe('DeviceSettings', function () {
|
||||
let sandbox;
|
||||
let expect;
|
||||
|
||||
before(async function () {
|
||||
const chai = await import('chai');
|
||||
const chaiAsPromised = await import('chai-as-promised');
|
||||
chai.use(chaiAsPromised.default);
|
||||
chai.should();
|
||||
expect = chai.expect;
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
sandbox = sinon.createSandbox();
|
||||
@@ -91,7 +98,7 @@ describe('DeviceSettings', function () {
|
||||
it('should not call the `_onSettingsUpdate` listener', async function () {
|
||||
const deviceSettings = new DeviceSettings({stuff: 'things'}, onSettingsUpdate);
|
||||
await deviceSettings.update({stuff: 'things'});
|
||||
expect(onSettingsUpdate).not.to.have.been.called;
|
||||
onSettingsUpdate.called.should.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -99,11 +106,11 @@ describe('DeviceSettings', function () {
|
||||
it('should call the `_onSettingsUpdate` listener', async function () {
|
||||
const deviceSettings = new DeviceSettings({}, onSettingsUpdate);
|
||||
await deviceSettings.update({stuff: 'things'});
|
||||
expect(onSettingsUpdate).to.have.been.calledOnceWithExactly(
|
||||
onSettingsUpdate.calledOnceWithExactly(
|
||||
'stuff',
|
||||
'things',
|
||||
undefined
|
||||
);
|
||||
).should.be.true;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
import BaseDriver from '../../../lib';
|
||||
import {driverUnitTestSuite} from '@appium/driver-test-support';
|
||||
|
||||
const {expect} = chai;
|
||||
|
||||
driverUnitTestSuite(BaseDriver, {
|
||||
platformName: 'iOS',
|
||||
'appium:deviceName': 'Delorean',
|
||||
});
|
||||
|
||||
describe('BaseDriver', function () {
|
||||
let expect;
|
||||
|
||||
before(async function () {
|
||||
const chai = await import('chai');
|
||||
chai.should();
|
||||
expect = chai.expect;
|
||||
});
|
||||
|
||||
describe('constructor', function () {
|
||||
it('should initialize "opts"', function () {
|
||||
const driver = new BaseDriver();
|
||||
|
||||
@@ -8,6 +8,15 @@ import {
|
||||
import sinon from 'sinon';
|
||||
|
||||
describe('helpers', function () {
|
||||
let should;
|
||||
|
||||
before(async function () {
|
||||
const chai = await import('chai');
|
||||
const chaiAsPromised = await import('chai-as-promised');
|
||||
chai.use(chaiAsPromised.default);
|
||||
should = chai.should();
|
||||
});
|
||||
|
||||
describe('#isPackageOrBundle', function () {
|
||||
it('should accept packages and bundles', function () {
|
||||
isPackageOrBundle('io.appium.testapp').should.be.true;
|
||||
|
||||
@@ -4,6 +4,13 @@ import BaseDriver from '../../../lib';
|
||||
import {createSandbox} from 'sinon';
|
||||
|
||||
describe('timeout', function () {
|
||||
before(async function () {
|
||||
const chai = await import('chai');
|
||||
const chaiAsPromised = await import('chai-as-promised');
|
||||
chai.use(chaiAsPromised.default);
|
||||
chai.should();
|
||||
});
|
||||
|
||||
let driver = new BaseDriver();
|
||||
let implicitWaitSpy;
|
||||
let sandbox;
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
import {pathToRegexp} from 'path-to-regexp';
|
||||
|
||||
describe('middleware', function () {
|
||||
before(async function () {
|
||||
const chai = await import('chai');
|
||||
chai.should();
|
||||
});
|
||||
|
||||
describe('pathToRegexp', function () {
|
||||
it('should match static path pattern', function () {
|
||||
const pathname = '/ws/session/1234/appium/device/syslog';
|
||||
const url = 'ws://127.0.0.1:8000/ws/session/1234/appium/device/syslog';
|
||||
const currentPathname = new URL(url).pathname;
|
||||
pathToRegexp(pathname).test(currentPathname).should.be.true;
|
||||
});
|
||||
|
||||
it('should match dynamic path pattern', function () {
|
||||
const pathname = '/ws/session/:sessionId/appium/device/syslog';
|
||||
const url = 'ws://127.0.0.1:8000/ws/session/1234/appium/device/syslog';
|
||||
const currentPathname = new URL(url).pathname;
|
||||
pathToRegexp(pathname).test(currentPathname).should.be.true;
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -23,8 +23,15 @@ function fakeDriver() {
|
||||
|
||||
describe('server configuration', function () {
|
||||
let port;
|
||||
|
||||
let sandbox;
|
||||
let should;
|
||||
|
||||
before(async function () {
|
||||
const chai = await import('chai');
|
||||
const chaiAsPromised = await import('chai-as-promised');
|
||||
chai.use(chaiAsPromised.default);
|
||||
should = chai.should();
|
||||
});
|
||||
|
||||
function fakeApp() {
|
||||
const app = {
|
||||
@@ -59,7 +66,7 @@ describe('server configuration', function () {
|
||||
const app = fakeApp();
|
||||
const configureRoutes = () => {};
|
||||
configureServer({app, addRoutes: configureRoutes});
|
||||
app.use.callCount.should.equal(15);
|
||||
app.use.callCount.should.equal(16);
|
||||
app.all.callCount.should.equal(4);
|
||||
});
|
||||
|
||||
|
||||
@@ -6,6 +6,11 @@ import {createSandbox} from 'sinon';
|
||||
describe('welcome', function () {
|
||||
let sandbox;
|
||||
|
||||
before(async function () {
|
||||
const chai = await import('chai');
|
||||
chai.should();
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
sandbox = createSandbox();
|
||||
});
|
||||
|
||||
@@ -7,6 +7,13 @@ import ProtocolConverter, {
|
||||
const {MJSONWP, W3C} = PROTOCOLS;
|
||||
|
||||
describe('Protocol Converter', function () {
|
||||
before(async function () {
|
||||
const chai = await import('chai');
|
||||
const chaiAsPromised = await import('chai-as-promised');
|
||||
chai.use(chaiAsPromised.default);
|
||||
chai.should();
|
||||
});
|
||||
|
||||
describe('getTimeoutRequestObjects', function () {
|
||||
let converter;
|
||||
before(function () {
|
||||
|
||||
@@ -25,6 +25,14 @@ function buildReqRes(url, method, body) {
|
||||
|
||||
describe('proxy', function () {
|
||||
let port;
|
||||
let should;
|
||||
|
||||
before(async function () {
|
||||
const chai = await import('chai');
|
||||
const chaiAsPromised = await import('chai-as-promised');
|
||||
chai.use(chaiAsPromised.default);
|
||||
should = chai.should();
|
||||
});
|
||||
|
||||
function mockProxy(opts = {}) {
|
||||
// sets default server/port
|
||||
|
||||
@@ -21,6 +21,11 @@ describe('JWProxy', function () {
|
||||
}
|
||||
|
||||
before(async function () {
|
||||
const chai = await import('chai');
|
||||
const chaiAsPromised = await import('chai-as-promised');
|
||||
chai.use(chaiAsPromised.default);
|
||||
chai.should();
|
||||
|
||||
port = await getTestPort();
|
||||
createTestURL = createAppiumURL(TEST_HOST, port);
|
||||
testStatusURL = createTestURL('', 'status');
|
||||
|
||||
@@ -2,6 +2,15 @@ import _ from 'lodash';
|
||||
import {statusCodes, getSummaryByCode} from '../../../lib';
|
||||
|
||||
describe('jsonwp-status', function () {
|
||||
let should;
|
||||
|
||||
before(async function () {
|
||||
const chai = await import('chai');
|
||||
const chaiAsPromised = await import('chai-as-promised');
|
||||
chai.use(chaiAsPromised.default);
|
||||
should = chai.should();
|
||||
});
|
||||
|
||||
describe('codes', function () {
|
||||
it('should export code numbers and summaries', function () {
|
||||
for (let obj of _.values(statusCodes)) {
|
||||
|
||||
@@ -227,6 +227,11 @@ let errorsList = [
|
||||
];
|
||||
|
||||
describe('errors', function () {
|
||||
before(async function () {
|
||||
const chai = await import('chai');
|
||||
chai.should();
|
||||
});
|
||||
|
||||
for (let error of errorsList) {
|
||||
it(error.errorName + ' should have a JSONWP code or W3C code and message', function () {
|
||||
if (error.errorCode) {
|
||||
@@ -247,6 +252,11 @@ describe('errors', function () {
|
||||
});
|
||||
});
|
||||
describe('errorFromMJSONWPStatusCode', function () {
|
||||
before(async function () {
|
||||
const chai = await import('chai');
|
||||
chai.should();
|
||||
});
|
||||
|
||||
for (let error of errorsList) {
|
||||
if (error.errorName !== 'NotYetImplementedError') {
|
||||
it(error.errorCode + ' should return correct error', function () {
|
||||
@@ -285,6 +295,11 @@ describe('errorFromMJSONWPStatusCode', function () {
|
||||
});
|
||||
});
|
||||
describe('errorFromW3CJsonCode', function () {
|
||||
before(async function () {
|
||||
const chai = await import('chai');
|
||||
chai.should();
|
||||
});
|
||||
|
||||
for (let error of errorsList) {
|
||||
if (error.errorName !== 'NotYetImplementedError') {
|
||||
it(error.errorName + ' should return correct error', function () {
|
||||
@@ -307,6 +322,11 @@ describe('errorFromW3CJsonCode', function () {
|
||||
});
|
||||
});
|
||||
describe('w3c Status Codes', function () {
|
||||
before(async function () {
|
||||
const chai = await import('chai');
|
||||
chai.should();
|
||||
});
|
||||
|
||||
it('should match the correct error codes', function () {
|
||||
let non400Errors = [
|
||||
['NoSuchDriverError', 404],
|
||||
@@ -341,6 +361,11 @@ describe('w3c Status Codes', function () {
|
||||
});
|
||||
});
|
||||
describe('.getResponseForW3CError', function () {
|
||||
before(async function () {
|
||||
const chai = await import('chai');
|
||||
chai.should();
|
||||
});
|
||||
|
||||
it('should return an error, message and stacktrace for just a generic exception', function () {
|
||||
try {
|
||||
throw new Error('Some random error');
|
||||
@@ -388,6 +413,11 @@ describe('.getResponseForW3CError', function () {
|
||||
});
|
||||
});
|
||||
describe('.getActualError', function () {
|
||||
before(async function () {
|
||||
const chai = await import('chai');
|
||||
chai.should();
|
||||
});
|
||||
|
||||
describe('MJSONWP', function () {
|
||||
it('should map a status code 7 no such element error as a NoSuchElementError', function () {
|
||||
const actualError = new errors.ProxyRequestError('Error message does not matter', {
|
||||
|
||||
@@ -7,6 +7,14 @@ import crypto from 'crypto';
|
||||
describe('Protocol', function () {
|
||||
// TODO test against an explicit protocol rather than a hash of a previous
|
||||
// protocol
|
||||
let chai;
|
||||
|
||||
before(async function () {
|
||||
chai = await import('chai');
|
||||
const chaiAsPromised = await import('chai-as-promised');
|
||||
chai.use(chaiAsPromised.default);
|
||||
chai.should();
|
||||
});
|
||||
|
||||
describe('ensure protocol consistency', function () {
|
||||
it('should not change protocol between patch versions', function () {
|
||||
@@ -55,6 +63,16 @@ describe('Protocol', function () {
|
||||
cmdName.should.equal('getStatus');
|
||||
});
|
||||
|
||||
it('should properly lookup correct command name for endpoint with query params', function () {
|
||||
const cmdName = routeToCommandName('/status?foo=1&bar=2', 'GET');
|
||||
cmdName.should.equal('getStatus');
|
||||
});
|
||||
|
||||
it('should properly lookup correct command name with custom base path', function () {
|
||||
const cmdName = routeToCommandName('/wd/hub/status?foo=1&bar=2', 'GET', '/wd/hub');
|
||||
cmdName.should.equal('getStatus');
|
||||
});
|
||||
|
||||
it('should properly lookup correct command name for endpoint without leading slash', function () {
|
||||
const cmdName = routeToCommandName('status', 'GET');
|
||||
cmdName.should.equal('getStatus');
|
||||
|
||||
@@ -3,6 +3,11 @@
|
||||
import {validators} from '../../../lib/protocol/validators';
|
||||
|
||||
describe('Protocol', function () {
|
||||
before(async function () {
|
||||
const chai = await import('chai');
|
||||
chai.should();
|
||||
});
|
||||
|
||||
describe('direct to driver', function () {
|
||||
describe('setUrl', function () {
|
||||
it('should fail when no url passed', function () {
|
||||
|
||||
@@ -3,6 +3,70 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [2.2.42](https://github.com/appium/appium/compare/@appium/base-plugin@2.2.41...@appium/base-plugin@2.2.42) (2024-07-10)
|
||||
|
||||
**Note:** Version bump only for package @appium/base-plugin
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.2.41](https://github.com/appium/appium/compare/@appium/base-plugin@2.2.40...@appium/base-plugin@2.2.41) (2024-06-28)
|
||||
|
||||
**Note:** Version bump only for package @appium/base-plugin
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.2.40](https://github.com/appium/appium/compare/@appium/base-plugin@2.2.39...@appium/base-plugin@2.2.40) (2024-06-27)
|
||||
|
||||
**Note:** Version bump only for package @appium/base-plugin
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.2.39](https://github.com/appium/appium/compare/@appium/base-plugin@2.2.38...@appium/base-plugin@2.2.39) (2024-06-11)
|
||||
|
||||
**Note:** Version bump only for package @appium/base-plugin
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.2.38](https://github.com/appium/appium/compare/@appium/base-plugin@2.2.37...@appium/base-plugin@2.2.38) (2024-06-11)
|
||||
|
||||
**Note:** Version bump only for package @appium/base-plugin
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.2.37](https://github.com/appium/appium/compare/@appium/base-plugin@2.2.36...@appium/base-plugin@2.2.37) (2024-06-11)
|
||||
|
||||
**Note:** Version bump only for package @appium/base-plugin
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.2.36](https://github.com/appium/appium/compare/@appium/base-plugin@2.2.35...@appium/base-plugin@2.2.36) (2024-06-10)
|
||||
|
||||
**Note:** Version bump only for package @appium/base-plugin
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.2.35](https://github.com/appium/appium/compare/@appium/base-plugin@2.2.34...@appium/base-plugin@2.2.35) (2024-06-06)
|
||||
|
||||
**Note:** Version bump only for package @appium/base-plugin
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.2.34](https://github.com/appium/appium/compare/@appium/base-plugin@2.2.33...@appium/base-plugin@2.2.34) (2024-06-06)
|
||||
|
||||
**Note:** Version bump only for package @appium/base-plugin
|
||||
|
||||