Compare commits

...
Author SHA1 Message Date
Jonathan Lipps 89e5f7e8db chore: publish
- appium@2.10.3
 - @appium/base-driver@9.10.3
 - @appium/base-plugin@2.2.39
 - @appium/doctor@2.1.6
 - @appium/docutils@1.0.15
 - @appium/driver-test-support@0.6.3
 - @appium/fake-plugin@3.1.43
 - @appium/images-plugin@3.0.14
 - @appium/logger@1.4.2
 - @appium/plugin-test-support@0.3.38
 - @appium/support@5.0.3
 - @appium/test-support@3.0.43
 - @appium/types@0.20.3
2024-06-11 13:02:33 -07:00
Corneliu Duplachi 9469aaef0e fix(logger): use index.d.ts for types instead of index.ts (#20247) 2024-06-11 21:18:52 +02:00
Jonathan Lipps 2b12d5974a chore: publish
- appium@2.10.2
 - @appium/base-driver@9.10.2
 - @appium/base-plugin@2.2.38
 - @appium/doctor@2.1.5
 - @appium/docutils@1.0.14
 - @appium/driver-test-support@0.6.2
 - @appium/fake-plugin@3.1.42
 - @appium/images-plugin@3.0.13
 - @appium/plugin-test-support@0.3.37
 - @appium/schema@0.6.1
 - @appium/support@5.0.2
 - @appium/test-support@3.0.42
 - @appium/types@0.20.2
2024-06-11 12:06:28 -07:00
Jonathan Lipps 45cc948671 fix(schema): edit a comment in order to trigger a publish 2024-06-11 12:05:50 -07:00
Jonathan Lipps 192f1f3793 chore: publish
- appium@2.10.1
 - @appium/base-driver@9.10.1
 - @appium/base-plugin@2.2.37
 - @appium/doctor@2.1.4
 - @appium/docutils@1.0.13
 - @appium/driver-test-support@0.6.1
 - @appium/fake-plugin@3.1.41
 - @appium/images-plugin@3.0.12
 - @appium/logger@1.4.1
 - @appium/plugin-test-support@0.3.36
 - @appium/support@5.0.1
 - @appium/test-support@3.0.41
 - @appium/types@0.20.1
2024-06-11 11:55:29 -07:00
Edgars Eglītis 5baf93672d chore(support): update changelog (#20246)
* chore: update support package changelog

* chore: update wording for changelog
2024-06-11 20:31:07 +03:00
Mykola Mokhnach c670010ec7 fix(logger): Type declaration for DEFAULT_LOG_LEVELS (#20244) 2024-06-11 18:07:59 +02:00
renovate[bot] 4a07f82cec chore(deps): update eslint-related packages to v7.13.0 2024-06-11 13:01:06 +00:00
renovate[bot] b28b524957 chore(deps): update dependency prettier to v3.3.2 2024-06-11 09:03:31 +00:00
Jonathan Lipps f40598161c chore: publish
- appium@2.10.0
 - @appium/base-driver@9.10.0
 - @appium/base-plugin@2.2.36
 - @appium/doctor@2.1.3
 - @appium/docutils@1.0.12
 - @appium/driver-test-support@0.6.0
 - @appium/fake-plugin@3.1.40
 - @appium/images-plugin@3.0.11
 - @appium/logger@1.4.0
 - @appium/plugin-test-support@0.3.35
 - @appium/schema@0.6.0
 - @appium/support@5.0.0
 - @appium/test-support@3.0.40
 - @appium/types@0.20.0
2024-06-10 16:29:53 -07:00
Jonathan LippsandEdgars Eglītis 0ddedec0c4 docs(appium): add sauce labs as strategic sponsor (#20240)
* docs(appium): add sauce labs as strategic sponsor

* Update SPONSORS.md

Co-authored-by: Edgars Eglītis <37242620+eglitise@users.noreply.github.com>

---------

Co-authored-by: Edgars Eglītis <37242620+eglitise@users.noreply.github.com>
2024-06-10 13:36:54 -07:00
Edgars Eglītis def3474b05 docs(appium): fix more image links (#20238) 2024-06-10 10:29:41 -07:00
Jonathan Lipps 1b9c1e96a3 docs(appium): fix sponsor logo image urls again 2024-06-10 09:49:27 -07:00
Jonathan Lipps e60e840316 docs(appium): fix sponsor logo image urls 2024-06-10 08:20:37 -07:00
Jonathan Lipps 6505d7921a docs(appium): fix sponsor logo image urls 2024-06-10 08:17:37 -07:00
Jonathan LippsandEdgars Eglītis def655cb39 docs(appium): add strategic partner browserstack (#20232)
* docs(appium): add strategic partner browserstack

* Partner announce (#20237)

* docs: move assets to override dir

* docs: remove copy-assets script

* docs: fix browserstack color-specific logo

---------

Co-authored-by: Edgars Eglītis <edgars.eglitis@outlook.com>

---------

Co-authored-by: Edgars Eglītis <edgars.eglitis@outlook.com>
2024-06-10 08:05:51 -07:00
renovate[bot] 1828501c66 fix(docutils): update dependency lilconfig to v3.1.2 2024-06-10 10:52:58 +00:00
renovate[bot] 6a08c95fc9 fix(docutils): update dependency yaml to v2.4.5 2024-06-09 09:03:13 +00:00
Mykola Mokhnach a156bc5bf5 chore(support): Simplify the logic for the AppiumLogger instance retrieval (#20230) 2024-06-09 08:07:52 +02:00
Mykola Mokhnach dbc3b668a0 feat(support): Move SecureValuesPreprocessor to @appum/logger (#20228)
The SecureValuesPreprocessor has been moved directly to the @appum/logger module and set as a private logger instance property.
The loadSecureValuesPreprocessingRules method is now public instance method of the Logger class. Unit tests were ported to the @appium/logger module as well.
2024-06-08 14:38:53 +02:00
renovate[bot] 69bbb5370e fix(driver-test-support): update dependency @types/lodash to v4.17.5 2024-06-08 12:03:35 +00:00
renovate[bot] ea00626091 fix(types): update dependency type-fest to v4.20.0 (#20227)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-06-08 13:38:47 +03:00
Mykola Mokhnach 1e86537e94 feat(appium): Add a possibility to print logs in json format (#20224) 2024-06-08 08:08:37 +02:00
Kazuaki Matsuo 93c271ec5d feat(support): remove unleakString as no usage (#20221)
BREAKING CHANGE: Remove unleakString since it is now in appium/logger
2024-06-07 17:57:13 -07:00
0363aab8ba feat(appium): Add session signature to all logs (#20202) (#20214)
* feat(appium): Add session signature to all logs (#20202) (#20214)

* test(logger): add tests for unleakString (#20222)

---------

Co-authored-by: Mykola Mokhnach <mokhnach@gmail.com>
Co-authored-by: Edgars Eglītis <37242620+eglitise@users.noreply.github.com>
2024-06-07 13:13:39 -07:00
Kazuaki Matsuo dd421b397b feat(support): Remove patchLogger from logger (#20220)
BREAKING CHANGE: Remove patchLogger exported method from logger
2024-06-07 09:44:08 -07:00
Kazuaki Matsuo 8ee7d07af4 feat(logger): Add the debug level to the default logger (#20219)
* feat(logger): Add the debug level to the default logger
2024-06-07 09:43:49 -07:00
Kazuaki Matsuo 1c19a8dbba chore(logger): update lerna to keep ISC as the license (#20217)
* chore(logger): update lerna to keep ISC as the license

* tweak sync
2024-06-07 09:43:23 -07:00
renovate[bot] 1ed646cc8f chore(docutils): update dependency mkdocs-material to v9.5.26 2024-06-07 05:50:25 +00:00
Jonathan Lipps d4ce2a82d9 chore: publish
- appium@2.9.0
 - @appium/base-driver@9.9.0
 - @appium/base-plugin@2.2.35
 - @appium/doctor@2.1.2
 - @appium/docutils@1.0.11
 - @appium/driver-test-support@0.5.0
 - @appium/fake-plugin@3.1.39
 - @appium/images-plugin@3.0.10
 - @appium/logger@1.3.0
 - @appium/plugin-test-support@0.3.34
 - @appium/support@4.5.0
 - @appium/test-support@3.0.39
 - @appium/types@0.19.2
2024-06-06 16:25:41 -07:00
Kazuaki Matsuo 40def9dbdb fix(appium): Revert changes in 20203 and 20202 (#20209)
* Revert "feat(appium): Add session signature to all logs (#20202)"

This reverts commit b3f8a47c2d.

* Revert "feat(logger): Add the 'debug' level to the default logger (#20203)"

This reverts commit 7fd9d5f626.
2024-06-06 16:17:57 -07:00
Mykola Mokhnach b3f8a47c2d feat(appium): Add session signature to all logs (#20202) 2024-06-06 22:46:52 +02:00
Jonathan Lipps 5b043cc1ab chore: publish
- appium@2.8.0
 - @appium/base-driver@9.8.1
 - @appium/base-plugin@2.2.34
 - @appium/doctor@2.1.1
 - @appium/docutils@1.0.10
 - @appium/driver-test-support@0.4.26
 - @appium/fake-plugin@3.1.38
 - @appium/images-plugin@3.0.9
 - @appium/logger@1.2.0
 - @appium/plugin-test-support@0.3.33
 - @appium/support@4.4.0
 - @appium/test-support@3.0.38
 - @appium/types@0.19.1
 - @appium/universal-xml-plugin@1.0.20
2024-06-06 13:20:24 -07:00
Edgars Eglītis 3fbc4f1fe0 fix: add logger dependencies in package.json (#20205) 2024-06-06 23:06:01 +03:00
Sudharsan Selvaraj 7492c3357e fix(universal-xml-plugin): add checks to disable the plguin in webview (#20179)
* fix(universal-xml-plugin): add checks to prevent transforming elements in webview

* Add missing tests

* Make context check to short circuit after selector validation
2024-06-06 10:25:29 -07:00
Mykola Mokhnach 7fd9d5f626 feat(logger): Add the 'debug' level to the default logger (#20203) 2024-06-06 19:21:54 +02:00
Jonathan Lipps 5f25351c9d docs(appium): fix sponsor logo image urls 2024-06-06 10:05:28 -07:00
Jonathan Lipps 339acd345f chore: publish
- appium@2.7.0
 - @appium/base-driver@9.8.0
 - @appium/base-plugin@2.2.33
 - @appium/doctor@2.1.0
 - @appium/docutils@1.0.9
 - @appium/driver-test-support@0.4.25
 - @appium/execute-driver-plugin@3.0.30
 - @appium/fake-plugin@3.1.37
 - @appium/images-plugin@3.0.8
 - @appium/logger@1.1.0
 - @appium/plugin-test-support@0.3.32
 - @appium/support@4.3.0
 - @appium/test-support@3.0.37
 - @appium/types@0.19.0
2024-06-06 09:54:55 -07:00
Mykola Mokhnach 21316e94d0 feat(base-driver): Make addition of search query params to cache optional (#20195) 2024-06-06 12:04:36 +02:00
Jonathan LippsandEdgars Eglītis 43fcc17b31 docs(appium): recognize headspin as development partner (#20191)
* docs(appium): recognize headspin as development partner

* update partner announcement branch (#20200)

* docs: make sponsor placement prettier

* docs: add margin to sponsor footer

* docs: add margin around individual sponsor images in footer

* docs: hide sponsor footer on landing page

---------

Co-authored-by: Edgars Eglītis <edgars.eglitis@outlook.com>

* partners plural

---------

Co-authored-by: Edgars Eglītis <edgars.eglitis@outlook.com>
2024-06-05 12:47:07 -07:00
renovate[bot] b1a4045bbd chore(deps): update dependency prettier to v3.3.1 2024-06-05 15:09:29 +00:00
renovate[bot] d2a8edbb7a chore(deps): update dependency @types/node to v20.14.2 2024-06-05 14:54:14 +00:00
Edgars Eglītis 6d67177e2f chore(logger): tidy up logger integration (#20194) 2024-06-05 10:27:19 +03:00
renovate[bot] db62358dba fix(types): update dependency type-fest to v4.19.0 (#20193)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-06-05 09:26:39 +03:00
Mykola Mokhnach 4fd6893146 fix(support): Make sure response stream is defined while stopping it (#20189) 2024-06-05 08:13:40 +02:00
Mykola Mokhnach 8915934270 feat(appium): Replace npmlog with the local fork (#20190) 2024-06-05 08:12:07 +02:00
renovate[bot] ae7f74a014 fix(appium): update dependency ajv to v8.16.0 2024-06-04 21:15:20 +00:00
renovate[bot] ea9cf8d910 chore(deps): update eslint-related packages to v7.12.0 2024-06-04 18:53:20 +00:00
Kazuaki Matsuo 70449cd077 feat(logger): add packages/logger package from npmlog (#20161)
* feat: move logger into appium/logger

* add logger in tsconfig.json

* add smoke with loading the index.js

* modify files to fit other modules

* refer to @appium/logger

* update with npm run sync-pkgs and a few

* cleanup changelog and package-lock.json

* update smoke

* pin deps

* adjust tsconfig.json with others

* Update README.md

* update license

* update license

* Update LICENSE

* fix review
2024-06-04 09:12:27 -07:00
Jonathan Lipps 65c47acf59 fix(appium): ensure ws close code is valid (#20178) 2024-06-04 07:46:16 -07:00
renovate[bot] 96a7a3f23b fix(support): update dependency teen_process to v2.1.4 2024-06-04 14:02:01 +00:00
renovate[bot] c8f997973c fix(execute-driver-plugin): update dependency webdriverio to v8.38.2 2024-06-04 11:27:38 +00:00
renovate[bot] bea47aacca fix(doctor): update dependency appium-adb to v12.4.2 2024-06-04 06:22:57 +00:00
renovate[bot] 10e5b37211 chore(deps): update dependency @types/node to v20.14.1 2024-06-04 06:05:50 +00:00
Kazuaki Matsuo 459f0a34b2 docs(appium): add icons by opencollevtive in sponsors (#20170)
* docs: add svg by opencollevtive to add icons

* Update SPONSORS.md
2024-06-03 14:11:00 -07:00
renovate[bot] 4c15dc6e5d chore(deps): update dependency @types/node to v20.14.0 2024-06-03 15:05:34 +00:00
renovate[bot] 848eb00afb fix(docutils): update dependency yaml to v2.4.3 2024-06-03 13:14:51 +00:00
renovate[bot] de8b1c97dc chore(deps): update dependency json-schema-to-typescript to v14.0.5 2024-06-03 11:00:57 +00:00
renovate[bot] 092ebb2a69 chore(deps): update dependency prettier to v3.3.0 2024-06-02 09:13:49 +00:00
renovate[bot] dddc4aa8c0 chore(deps): update dependency @types/node to v20.13.0 2024-06-01 07:26:37 +00:00
renovate[bot] 425d3eac1d chore(docutils): update dependency mkdocs-git-revision-date-localized-plugin to v1.2.6 2024-06-01 05:35:41 +00:00
renovate[bot] 57b0406a33 fix(support): update dependency @types/which to v3 (#20164)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-05-31 13:28:10 +02:00
renovate[bot] e3e1757c56 fix(doctor): update dependency appium-adb to v12.4.1 2024-05-31 11:07:08 +00:00
renovate[bot] 17e271afff chore(deps): update dependency @types/node to v20.12.13 2024-05-30 07:53:34 +00:00
renovate[bot] 7718cd01d1 chore(deps): update eslint-related packages to v7.11.0 2024-05-28 12:21:51 +00:00
105 changed files with 3756 additions and 1360 deletions
+20 -15
View File
@@ -48,16 +48,6 @@ labels:
matcher:
files: ['packages/base-driver/**']
- label: '@appium/eslint-config-appium'
sync: true
matcher:
files: ['packages/eslint-config-appium/**']
- label: '@appium/eslint-config-appium-ts'
sync: true
matcher:
files: ['packages/eslint-config-appium-ts/**']
- label: '@appium/base-plugin'
sync: true
matcher:
@@ -78,6 +68,16 @@ labels:
matcher:
files: ['packages/driver-test-support/**']
- label: '@appium/eslint-config-appium'
sync: true
matcher:
files: ['packages/eslint-config-appium/**']
- label: '@appium/eslint-config-appium-ts'
sync: true
matcher:
files: ['packages/eslint-config-appium-ts/**']
- label: '@appium/execute-driver-plugin'
sync: true
matcher:
@@ -98,6 +98,11 @@ labels:
matcher:
files: ['packages/images-plugin/**']
- label: '@appium/logger'
sync: true
matcher:
files: ['packages/logger/**']
- label: '@appium/opencv'
sync: true
matcher:
@@ -123,6 +128,11 @@ labels:
matcher:
files: ['packages/support/**']
- label: '@appium/strongbox'
sync: true
matcher:
files: ['packages/strongbox/**']
- label: '@appium/test-support'
sync: true
matcher:
@@ -142,8 +152,3 @@ labels:
sync: true
matcher:
files: ['packages/universal-xml-plugin/**']
- label: '@appium/strongbox'
sync: true
matcher:
files: ['packages/strongbox/**']
+4
View File
@@ -37,6 +37,9 @@ certificate.key
*.pdb
.vs
#vscode
.history
old
.tern-project
coverage
@@ -60,5 +63,6 @@ appium.zip
**/build-fixtures/**
*.tsbuildinfo
packages/*/LICENSE
\!packages/*/LICENSE
local_appium_home
*.tgz
+2 -2
View File
@@ -4,7 +4,7 @@ Appium contains many different modules and supports a variety of drivers, plugin
## Main Appium Module
* ### [Appium 2.0 Changelog](packages/appium/CHANGELOG.md)
* ### [Appium 2 Changelog](packages/appium/CHANGELOG.md)
* [Appium 1.x Changelog](https://github.com/appium/appium/blob/1.x/CHANGELOG.md)
## Appium Drivers / Clients / Plugins
@@ -23,6 +23,7 @@ Appium contains many different modules and supports a variety of drivers, plugin
* [fake-driver](packages/fake-driver/CHANGELOG.md)
* [fake-plugin](packages/fake-plugin/CHANGELOG.md)
* [images-plugin](packages/images-plugin/CHANGELOG.md)
* [logger](packages/logger/CHANGELOG.md)
* [opencv](packages/opencv/CHANGELOG.md)
* [plugin-test-support](packages/plugin-test-support/CHANGELOG.md)
* [relaxed-caps-plugin](packages/relaxed-caps-plugin/CHANGELOG.md)
@@ -31,6 +32,5 @@ Appium contains many different modules and supports a variety of drivers, plugin
* [support](packages/support/CHANGELOG.md)
* [test-support](packages/test-support/CHANGELOG.md)
* [tsconfig](packages/tsconfig/CHANGELOG.md)
* [typedoc-plugin-appium](packages/typedoc-plugin-appium/CHANGELOG.md)
* [types](packages/types/CHANGELOG.md)
* [universal-xml-plugin](packages/universal-xml-plugin/CHANGELOG.md)
+2 -2
View File
@@ -83,7 +83,7 @@ related to contributions of funds. Both are extremely important to the ongoing h
project. Financial sponsorship is mediated through our [OpenCollective page](https://opencollective.com/appium).
Sponsorship tiers and benefits are described below:
- **Development Partners**: The primary need of any open source project is for sustained maintenance and contributorship. Companies that officially sponsor such development with dedicated employee time are recognized as Development Partners, and receive the same benefits as the highest tier of financial sponsorship. To be considered a Development Partner, a company must devote at least 50% of a full-time employee's time to Appium maintenance.
- **Development Partners**: The primary need of any open source project is for sustained maintenance and contributorship. Companies that officially sponsor such development with dedicated employee time are recognized as Development Partners, and receive the same benefits as the highest tier of financial sponsorship. To be considered a Development Partner, a company must devote at least 50% of a full-time employee's time to Appium maintenance. This means that the contributors designated by the company must have been appointed as "committers" in good standing of one or more core Appium repositories. (See above for a description of how to become a committer).
- **Strategic Partners**: Representing the highest tier of ongoing financial support for the project, Strategic Partners are what make our compensation scheme for contributors possible, and we thank them for their significant investment into the project! More than financial contribution, Strategic Partners help elevate Appium's position in the industry through marketing and evangelism efforts, and partner with the Appium team to help make Appium better. Along with the Development Partners, Strategic Partners have their logos displayed prominently on our website, and have the ability to link to their Appium-related products in a few relevant places in our documentation.
- **Gold, Silver, and Bronze Sponsors**: these sponsorship levels correspond to different amounts of recurring financial contribution that generously support our contributor compensation scheme. Sponsors at these levels also have the opportunity to display their logo on our website.
- **Backers**: individuals who benefit from the Appium project can also donate on a one-time or recurring basis, at any amount, and will be recognized as Backers for their contribution!
@@ -91,7 +91,7 @@ Sponsorship tiers and benefits are described below:
To become a sponsor at one of these levels, follow the process indicated at the [OpenCollective
page](https://opencollective.com/appium).
### Compensation for TC Members, Committers, Contributors, and upstream projects
### Compensation Scheme
With funds received through sponsorship, the Appium project wants to incentivize contributions of
code, documentation efforts, maintenance, and project leadership. We have developed a scheme
+39 -1
View File
@@ -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">
@@ -178,8 +178,46 @@ cross-platform native app automation, for mobile and beyond!
If you are looking for a more comprehensive description of what this is all about, please read our
documentation on [How Does Appium Work?](https://appium.io/docs/en/latest/intro/appium/).
## Sponsors
Appium has a [Sponsorship Program](GOVERNANCE.md#sponsorship)! If you or your company uses Appium
and wants to give back financially to the project, we use these funds to [encourage development and
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 and Strategic Partners
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/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
A full list of sponsors is available at our [Sponsors page](SPONSORS.md).
## License
[Apache-2.0](./LICENSE)
[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fappium%2Fappium.svg?type=large)](https://app.fossa.io/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fappium%2Fappium?ref=badge_large)
`@appium/logger` package is under [ISC](./packages/logger/LICENSE) License.
+19 -6
View File
@@ -11,7 +11,8 @@ Hub](https://opencollective.com/appium).
We are very grateful for the support of our Development Partners, who donate Appium development and
maintenance as part of their employees' core job duties!
* *Coming Soon!*
<a href="https://www.headspin.io/solutions/appium-mobile-test-automation" target="_blank"><img
src="packages/appium/docs/overrides/assets/images/sponsor-logo-headspin.png" width="250"/></a>
## Strategic Partners
@@ -19,17 +20,29 @@ 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
* [Become a Gold Sponsor](https://opencollective.com/appium/contribute/gold-sponsor-72877/checkout?interval=month&amount=500&contributeAs=me)
<a href="https://opencollective.com/appium" target="_blank"><img src="https://opencollective.com/appium/tiers/gold-sponsor.svg?avatarHeight=100&button=false"/></a>
[Become a Gold Sponsor](https://opencollective.com/appium/contribute/gold-sponsor-72877/checkout?interval=month&amount=500&contributeAs=me)
## Silver Sponsors
* [Become a Silver Sponsor](https://opencollective.com/appium/contribute/silver-sponsor-72876/checkout?interval=month&amount=250&contributeAs=me)
<a href="https://opencollective.com/appium" target="_blank"><img src="https://opencollective.com/appium/tiers/silver-sponsor.svg?avatarHeight=80&button=false"/></a>
[Become a Silver Sponsor](https://opencollective.com/appium/contribute/silver-sponsor-72876/checkout?interval=month&amount=250&contributeAs=me)
## Bronze Sponsors
- [MagicPod](https://magicpod.com/en/)
* [Become a Bronze Sponsor](https://opencollective.com/appium/contribute/sponsors-70690/checkout?interval=month&amount=100&contributeAs=me)
<a href="https://opencollective.com/appium" target="_blank"><img src="https://opencollective.com/appium/tiers/sponsors.svg?avatarHeight=60&button=false"/></a>
[Become a Bronze Sponsor](https://opencollective.com/appium/contribute/sponsors-70690/checkout?interval=month&amount=100&contributeAs=me)
+517 -709
View File
File diff suppressed because it is too large Load Diff
+11 -10
View File
@@ -59,9 +59,10 @@
"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",
@@ -86,7 +87,7 @@
"@types/chai-as-promised": "7.1.8",
"@types/diff": "5.2.1",
"@types/mocha": "10.0.6",
"@types/node": "20.12.12",
"@types/node": "20.14.2",
"@types/semver": "7.5.8",
"@types/sinon": "17.0.3",
"@types/sinon-chai": "3.2.12",
@@ -94,8 +95,8 @@
"@types/teen_process": "2.0.4",
"@types/ws": "8.5.10",
"@types/yargs": "17.0.32",
"@typescript-eslint/eslint-plugin": "7.10.0",
"@typescript-eslint/parser": "7.10.0",
"@typescript-eslint/eslint-plugin": "7.13.0",
"@typescript-eslint/parser": "7.13.0",
"asyncbox": "3.0.0",
"chai": "4.4.1",
"chai-as-promised": "7.1.2",
@@ -112,7 +113,7 @@
"fancy-log": "2.0.0",
"finalhandler": "1.2.0",
"get-port": "5.1.1",
"json-schema-to-typescript": "14.0.4",
"json-schema-to-typescript": "14.0.5",
"lerna": "8.1.3",
"log-symbols": "4.1.0",
"midnight-smoker": "8.0.0",
@@ -121,7 +122,7 @@
"mocha": "10.4.0",
"mocha-chai-jest-snapshot": "1.1.4",
"npm-run-all2": "6.2.0",
"prettier": "3.2.5",
"prettier": "3.3.2",
"rewiremock": "3.14.5",
"rimraf": "5.0.7",
"serve-static": "1.15.0",
@@ -133,7 +134,7 @@
"tsd": "0.31.0",
"typescript": "5.4.5",
"validate.js": "0.13.1",
"webdriverio": "8.38.0",
"webdriverio": "8.38.2",
"ws": "8.17.0",
"yaml-js": "0.3.1"
},
@@ -146,10 +147,10 @@
},
"overrides": {
"@sidvind/better-ajv-errors": {
"ajv": "8.14.0"
"ajv": "8.16.0"
},
"ajv-formats": {
"ajv": "8.14.0"
"ajv": "8.16.0"
},
"http-cache-semantics": "4.1.1"
}
+91
View File
@@ -3,6 +3,97 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [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)
### Features
* **logger:** Add the 'debug' level to the default logger ([#20203](https://github.com/appium/appium/issues/20203)) ([7fd9d5f](https://github.com/appium/appium/commit/7fd9d5f6261b385c234580c2bfee4d576905458b))
### Bug Fixes
* add logger dependencies in package.json ([#20205](https://github.com/appium/appium/issues/20205)) ([3fbc4f1](https://github.com/appium/appium/commit/3fbc4f1fe07eadf9c7a2ef8fc9f4ba78dc3486a3))
## [2.7.0](https://github.com/appium/appium/compare/appium@2.6.0...appium@2.7.0) (2024-06-06)
### Features
* **appium:** Replace npmlog with the local fork ([#20190](https://github.com/appium/appium/issues/20190)) ([8915934](https://github.com/appium/appium/commit/8915934270243bfb46c4d104a098ce1cc481b0ff))
* **base-driver:** Make addition of search query params to cache optional ([#20195](https://github.com/appium/appium/issues/20195)) ([21316e9](https://github.com/appium/appium/commit/21316e94d05b2f97aa0349a82da229713e92446c))
### Bug Fixes
* **appium:** ensure ws close code is valid ([#20178](https://github.com/appium/appium/issues/20178)) ([65c47ac](https://github.com/appium/appium/commit/65c47acf5954bbf1947e896ef79c81a9b3c712a5))
* **appium:** update dependency ajv to v8.16.0 ([ae7f74a](https://github.com/appium/appium/commit/ae7f74a0144d233653443622a84d49b1cb4e6222))
* **docutils:** update dependency yaml to v2.4.3 ([848eb00](https://github.com/appium/appium/commit/848eb00afbd1cab76a6a1c3d0f4a2a0b1acee0d0))
* **execute-driver-plugin:** update dependency webdriverio to v8.38.2 ([c8f9979](https://github.com/appium/appium/commit/c8f997973cc21673f95d02c92d43a2981b8713b8))
* **support:** update dependency teen_process to v2.1.4 ([96a7a3f](https://github.com/appium/appium/commit/96a7a3f23bead7c1d18700550138cd2d70ce5e3c))
* **types:** update dependency type-fest to v4.19.0 ([#20193](https://github.com/appium/appium/issues/20193)) ([db62358](https://github.com/appium/appium/commit/db62358dbac25de2a75fdc7493338d98b1422c84))
## [2.6.0](https://github.com/appium/appium/compare/appium@2.5.4...appium@2.6.0) (2024-05-27)
+39 -1
View File
@@ -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">
@@ -178,8 +178,46 @@ cross-platform native app automation, for mobile and beyond!
If you are looking for a more comprehensive description of what this is all about, please read our
documentation on [How Does Appium Work?](https://appium.io/docs/en/latest/intro/appium/).
## Sponsors
Appium has a [Sponsorship Program](GOVERNANCE.md#sponsorship)! If you or your company uses Appium
and wants to give back financially to the project, we use these funds to [encourage development and
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 and Strategic Partners
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/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
A full list of sponsors is available at our [Sponsors page](SPONSORS.md).
## License
[Apache-2.0](./LICENSE)
[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fappium%2Fappium.svg?type=large)](https://app.fossa.io/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fappium%2Fappium?ref=badge_large)
`@appium/logger` package is under [ISC](./packages/logger/LICENSE) License.
+1
View File
@@ -1,3 +1,4 @@
en/latest
en/assets
ja/assets
zh/assets
@@ -1,6 +0,0 @@
.md-source__fact--version {
display: none;
}
.md-source__fact:nth-child(1n + 2):before {
margin-left: 0 !important;
}
+1
View File
@@ -8,6 +8,7 @@ copyright: Copyright &copy; 2012 OpenJS Foundation - <a href="#__consent">Change
theme:
logo: assets/images/appium-logo-white.png
favicon: assets/images/appium-logo.png
custom_dir: overrides
plugins:
blog:
@@ -0,0 +1,46 @@
---
authors:
- jlipps
date: 2024-06-10
---
# Announcing BrowserStack as Appium's Strategic Partner
We have another exciting announcement for you! As part of our recently-announced [sponsorship and
contributor compensation
program](https://appium.io/docs/en/latest/blog/2024/05/14/announcing-appiums-sponsorship-program/),
we are obviously in need of significant funding to support giving back to our contributor
community. We are therefore extremely happy to announce one of our brand new exclusive Strategic Partners: <a
href="https://www.browserstack.com/browserstack-appium?utm_campaigncode=701OW00000AoUTQYA3&utm_medium=partnered&utm_source=appium">BrowserStack</a>!
<!-- more -->
<div style="text-align:center;width:100%">
<a href="https://www.browserstack.com/browserstack-appium?utm_campaigncode=701OW00000AoUTQYA3&utm_medium=partnered&utm_source=appium">
<img src="/docs/en/latest/assets/images/sponsor-logo-browserstack-dark.png#only-dark" width="300" />
<img src="/docs/en/latest/assets/images/sponsor-logo-browserstack-light.png#only-light" width="300" />
</a>
</div>
BrowserStack has long been recognized in the industry as having extensive support for Appium,
allowing you to run functional and visual regression tests for your native and hybrid apps at scale
across a variety of devices and platforms.
As a token of this collaboration, BrowserStack is extending a special premium access of
BrowserStack App Automate product to Appium users. Discover more about this special offer at
[browserstack.com](https://www.browserstack.com/browserstack-appium?utm_campaigncode=701OW00000AoUTQYA3&utm_medium=partnered&utm_source=appium&utm_term=partannouncement)
With the decision to come on board as Strategic Partner, BrowserStack is making a huge investment
in Appium and the Appium community. BrowserStack is very interested in supporting the development
of the Appium contributor base, and is proud to be a key part of our contributor compensation
scheme. Without Strategic Partners like BrowserStack, we would not be able to maintain such
a program! We welcome them with gratitude!
**About BrowserStack**
<em>BrowserStack is the world’s leading software testing platform powering over two million tests every
day across 19 global data centers. BrowserStack helps Tesco, Shell, NVIDIA, Discovery, Wells Fargo,
and over 50,000 customers deliver quality software at speed by moving testing to their Cloud.
BrowserStack’s platform provides instant access to 20,000+ real mobile devices on a highly reliable
cloud infrastructure that effortlessly scales as testing needs grow. With BrowserStack, Dev and QA
teams can move fast while delivering an amazing experience for every customer.</em>
@@ -0,0 +1,47 @@
---
authors:
- jlipps
date: 2024-06-06
---
# Announcing HeadSpin as Appium's Development Partner
Recently, we announced a new [sponsorship and contributor compensation program](https://appium.io/docs/en/latest/blog/2024/05/14/announcing-appiums-sponsorship-program/) for the Appium
project. As part of this program, we felt it was important to have a place to recognize those
companies who make an extremely valuable kind of contribution to the project: dedicated maintainer
time. Appium only survives because of the tireless efforts of the core maintainers, and some of us
are only able to do this work because it is donated on behalf of our day jobs. We're therefore very
excited to announce our first Development Partner: <a
href="https://www.headspin.io/solutions/appium-mobile-test-automation">HeadSpin</a>!
<!-- more -->
<div style="text-align:center;width:100%">
<a href="https://www.headspin.io/solutions/appium-mobile-test-automation"><img
src="/docs/en/latest/assets/images/sponsor-logo-headspin.png" style="width: 300px"/></a>
</div>
HeadSpin is a real device performance and UX analysis cloud, with full support for Appium. When you
run your Appium tests on HeadSpin, you get access to comprehensive performance reports about your
app, including potential network, system resource, or user experience issues. HeadSpin supports
a variety of complex testing modes, including hooking up your own devices to the cloud, injecting
and receiving video and audio to/from devices, working with media (streaming) platforms, and more.
HeadSpin has been one of the Appium project's biggest supporters for a long time, and currently
employs two of Appium's core maintainers, donating a significant portion of their time towards the
development, maintenance, support, and leadership of open source Appium repos. HeadSpin has also
worked hard towards Appium's vision of a robust Appium ecosystem, contributing several
HeadSpin-maintained Appium drivers and plugins to the community:
- A [Roku TV driver](https://github.com/headspinio/appium-roku-driver)
- A [Tizen TV driver](https://github.com/headspinio/appium-tizen-tv-driver/)
- A [LG webOS TV driver](https://github.com/headspinio/appium-lg-webos-driver/)
- A [Unity game automation plugin](https://github.com/headspinio/appium-altunity-plugin)
These are all free and open source Appium components that HeadSpin has donated to the community
with the goal of fostering collaboration in these areas of media app automation.
Looking to the future, HeadSpin plans to continue supporting Appium in significant ways, both as
a Development Partner and a significant contributor to the Appium ecosystem. The Appium project is
deeply indebted to HeadSpin for all of its support, and is proud to take this moment to recognize
HeadSpin as our first official Development Partner!
@@ -0,0 +1,36 @@
---
authors:
- jlipps
date: 2024-06-10
---
# Announcing Sauce Labs as Appium's Strategic Partner
The good news surrounding our new [sponsorship and contributor compensation
program](https://appium.io/docs/en/latest/blog/2024/05/14/announcing-appiums-sponsorship-program/)
just keeps coming! Effective immediately we are fortunate and excited to be able to welcome a new Strategic Partner to
the Appium project: <a href="https://saucelabs.com/resources/blog/appium-strategic-partner">Sauce Labs</a>!
<!-- more -->
<div style="text-align:center;width:100%">
<a href="https://saucelabs.com/resources/blog/appium-strategic-partner"><img src="/docs/en/latest/assets/images/sponsor-logo-sauce.png" style="width: 300px"/></a>
</div>
Sauce Labs has a long history of support for the Appium project. In fact, Appium was initially
incubated at Sauce Labs under the direction of [@jlipps](https://github.com/jlipps), in the early
days of mobile automation. Sauce Labs always envisioned Appium as being a project for and by the
community, and cemented this perspective in 2016 with the donation of Appium to the JS Foundation
(now the [OpenJS Foundation](https://openjsf.org/)). To this day, developers associated with Sauce
Labs continue to help maintain various parts of the Appium project.
Now, Sauce Labs is making a further commitment to support Appium financially as a way to enable
our new contributor compensation program. We're very excited about this program as a vehicle for
getting more people involved in Appium's development and maintenance. With the help of Strategic
Partners like Sauce Labs, our project will be better able to meet the needs of our constantly
growing userbase, and attract new developers (like you?) to build the Appium ecosystem together.
Needless to say, we are extremely grateful for Sauce Labs's support!
**About Sauce Labs**
<em>Sauce Labs is the leader in continuous quality. Our Platform for Test helps organizations transform software testing from a chore to a competitive advantage. With decades of expertise and deep roots in the Selenium and Appium open source communities, high performing software teams use our platform to execute any testing workload across thousands of different devices, browsers, and operating systems—anywhere, any time, and at any scale. For more information, please visit [saucelabs.com](https://saucelabs.com).</em>
+1
View File
@@ -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`||
+3
View File
@@ -21,3 +21,6 @@ environment variables that the Appium server understands:
|`APPIUM_OMIT_PEER_DEPS`|Adds `--omit=peer` to all the NPM commands run internally by Appium. Mostly an internal feature.|
|`APPIUM_APPS_CACHE_MAX_AGE`|Allows to set the maximum age (in minutes) for [cached applications](../guides/caching.md). The default value is `60 * 24` (24 hours). Do not set it to a lower number than the duration of a single session startup.|
|`APPIUM_APPS_CACHE_MAX_ITEMS`|Allows to set the maximum amount of [cached applications](../guides/caching.md). The default value is `1024`. Do not set it to a lower number than the amount of apps in all parallel sessions per process.|
|`APPIUM_APPS_CACHE_IGNORE_URL_QUERY`| If the above option is enabled then the 'search' part of the app URL will be cut off from cache keys. See the corresponding
[feature request](https://discuss.appium.io/t/regarding-app-caching-when-using-aws-s3-presigned-urls/42713)
for more details. Disabled by default.|
@@ -63,6 +63,11 @@ of it. It is a [LRU Cache](https://www.npmjs.com/package/lru-cache) with the fol
[APPIUM_APPS_CACHE_MAX_AGE](../cli/env-vars.md) environment variable.
Do not set it to a lower number than the duration of a single session startup.
- TTL is refreshed for each entry upon access
- By default the full application URL is used as cache key. You may change this behavior
by enabling the [APPIUM_APPS_CACHE_IGNORE_URL_QUERY](../cli/env-vars.md) environment variable.
If the above option is enabled then the 'search' part of the app URL will be cut off from cache keys.
See the corresponding [feature request](https://discuss.appium.io/t/regarding-app-caching-when-using-aws-s3-presigned-urls/42713)
for more details.
!!! warning
+27 -1
View File
@@ -6,7 +6,8 @@ hide:
title: Welcome
---
<style>
.md-typeset h1 {
.md-typeset h1,
.appium-sponsor-thanks {
display: none;
}
</style>
@@ -19,6 +20,31 @@ software, designed to facilitate UI automation of many app platforms, including
Android, Tizen), browser (Chrome, Firefox, Safari), desktop (macOS, Windows), TV (Roku, tvOS,
Android TV, Samsung), and more!
<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>)
<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
<div class="grid cards" markdown>

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

@@ -0,0 +1,31 @@
.md-source__fact--version {
display: none;
}
.md-source__fact:nth-child(1n + 2):before {
margin-left: 0 !important;
}
.appium-sponsor-thanks {
width: 100%;
margin: 0.5rem 0.6rem auto 0.6rem;
display: flex;
align-items: center;
}
.appium-sponsor-thanks img {
margin: auto 6px;
height: 40px;
}
.homepageSponsors {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
margin: 10px auto;
max-width: 800px;
}
.homepageSponsor {
flex: 1;
}
@@ -0,0 +1,79 @@
<!-- Footer -->
<footer class="md-footer">
<!-- Link to previous and/or next page -->
{% if "navigation.footer" in features %}
{% if page.previous_page or page.next_page %}
{% if page.meta and page.meta.hide %}
{% set hidden = "hidden" if "footer" in page.meta.hide %}
{% endif %}
<nav
class="md-footer__inner md-grid"
aria-label="{{ lang.t('footer') }}"
{{ hidden }}
>
<!-- Link to previous page -->
{% if page.previous_page %}
{% set direction = lang.t("footer.previous") %}
<a
href="{{ page.previous_page.url | url }}"
class="md-footer__link md-footer__link--prev"
aria-label="{{ direction }}: {{ page.previous_page.title | e }}"
>
<div class="md-footer__button md-icon">
{% set icon = config.theme.icon.previous or "material/arrow-left" %}
{% include ".icons/" ~ icon ~ ".svg" %}
</div>
<div class="md-footer__title">
<span class="md-footer__direction">
{{ direction }}
</span>
<div class="md-ellipsis">
{{ page.previous_page.title }}
</div>
</div>
</a>
{% endif %}
<!-- Link to next page -->
{% if page.next_page %}
{% set direction = lang.t("footer.next") %}
<a
href="{{ page.next_page.url | url }}"
class="md-footer__link md-footer__link--next"
aria-label="{{ direction }}: {{ page.next_page.title | e }}"
>
<div class="md-footer__title">
<span class="md-footer__direction">
{{ direction }}
</span>
<div class="md-ellipsis">
{{ page.next_page.title }}
</div>
</div>
<div class="md-footer__button md-icon">
{% set icon = config.theme.icon.next or "material/arrow-right" %}
{% include ".icons/" ~ icon ~ ".svg" %}
</div>
</a>
{% endif %}
</nav>
{% endif %}
{% endif %}
<!-- Further information -->
<div class="md-footer-meta md-typeset">
<div class="md-footer-meta__inner md-grid">
<!-- Sponsor thanks -->
{% include "partials/sponsors.html" %}
{% include "partials/copyright.html" %}
<!-- Social links -->
{% if config.extra.social %}
{% include "partials/social.html" %}
{% endif %}
</div>
</div>
</footer>
@@ -0,0 +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;
+15 -1
View File
@@ -21,6 +21,10 @@ import {getDefaultsForExtension} from './schema';
import {DRIVER_TYPE, BIDI_BASE_PATH, BIDI_EVENT_NAME} from './constants';
import WebSocket from 'ws';
const MIN_WS_CODE_VAL = 1000;
const MAX_WS_CODE_VAL = 1015;
const WS_FALLBACK_CODE = 1011; // server encountered an error while fulfilling request
const desiredCapabilityConstraints = /** @type {const} */ ({
automationName: {
presence: true,
@@ -418,6 +422,16 @@ class AppiumDriver extends DriverCore {
`Upstream bidi socket closed connection (code ${code}, reason: '${reason}'). ` +
`Closing proxy connection to client`,
);
if (!_.isNumber(code)) {
code = parseInt(code, 10);
}
if (_.isNaN(code) || code < MIN_WS_CODE_VAL || code > MAX_WS_CODE_VAL) {
this.log.warn(
`Received code ${code} from upstream socket, but this is not a valid ` +
`websocket code. Rewriting to ${WS_FALLBACK_CODE} for ws compatibility`,
);
code = WS_FALLBACK_CODE;
}
ws.close(code, reason);
});
@@ -1106,7 +1120,7 @@ class AppiumDriver extends DriverCore {
}
// here we know that we are executing a session command, and have a valid session driver
return await (/** @type {any} */ (dstSession)).executeCommand(cmd, ...args);
return await /** @type {any} */ (dstSession).executeCommand(cmd, ...args);
};
// now take our default behavior, wrap it with any number of plugin behaviors, and run it
+204 -108
View File
@@ -1,30 +1,27 @@
import npmlog from 'npmlog';
import globalLog from '@appium/logger';
import {createLogger, format, transports} from 'winston';
import {fs, logger} from '@appium/support';
import { APPIUM_LOGGER_NAME } from './logger';
import {fs} from '@appium/support';
import _ from 'lodash';
import { adler32 } from './utils';
import { LRUCache } from 'lru-cache';
// set up distributed logging before everything else
logger.patchLogger(npmlog);
global._global_npmlog = npmlog;
global._global_npmlog = globalLog;
// npmlog is used only for emitting, we use winston for output
npmlog.level = 'info';
const levels = {
globalLog.level = 'info';
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',
@@ -33,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
@@ -73,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,
format.printf(formatLogLine(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
@@ -94,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),
format.printf(formatLogLine(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);
@@ -124,28 +199,26 @@ function createHttpTransport(args, logLvl) {
level: logLvl,
format: format.combine(
stripColorFormat,
format.printf(function printInfo(info) {
return `${info.timestamp} ${info.message}`;
})
format.printf(formatLogLine(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;
}
@@ -185,72 +258,95 @@ 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) {
npmlog.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;
// 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,
});
const reportedLoggerErrors = new Set();
// Capture logs emitted via npmlog and pass them through winston
npmlog.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);
/**
* @param {ParsedArgs} args
* @param {boolean} targetConsole
* @returns {(info: import('logform').TransformableInfo) => string}
*/
function formatLogLine(args, targetConsole) {
return (info) => {
if (['json', 'pretty_json'].includes(args.logFormat)) {
let infoCopy = {...info};
if (targetConsole && !args.logTimestamp) {
delete infoCopy.timestamp;
}
return JSON.stringify(infoCopy, null, args.logFormat === 'pretty_json' ? 2 : undefined);
}
if (targetConsole) {
return `${args.logTimestamp ? `${info.timestamp} - ` : ''}${info.message}`;
}
return `${info.timestamp} ${info.message}`;
};
}
/**
* 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();
},
});
}
function clear() {
if (log) {
for (let transport of _.keys(log.transports)) {
log.remove(transport);
}
}
npmlog.removeAllListeners('log');
/**
* 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
*/
+2 -2
View File
@@ -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,7 +246,7 @@ 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,
);
if (!_.isEmpty(issues)) {
+56
View File
@@ -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,
+15 -15
View File
@@ -1,6 +1,6 @@
{
"name": "appium",
"version": "2.6.0",
"version": "2.10.3",
"description": "Automation for Apps.",
"keywords": [
"automation",
@@ -45,10 +45,9 @@
"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",
@@ -60,12 +59,13 @@
"test:unit": "mocha \"./test/unit/**/*.spec.js\""
},
"dependencies": {
"@appium/base-driver": "^9.7.0",
"@appium/base-plugin": "^2.2.32",
"@appium/docutils": "^1.0.8",
"@appium/schema": "~0.5.0",
"@appium/support": "^4.2.6",
"@appium/types": "^0.18.0",
"@appium/base-driver": "^9.10.3",
"@appium/base-plugin": "^2.2.39",
"@appium/docutils": "^1.0.15",
"@appium/logger": "^1.4.2",
"@appium/schema": "^0.6.1",
"@appium/support": "^5.0.3",
"@appium/types": "^0.20.3",
"@sidvind/better-ajv-errors": "2.1.3",
"@types/argparse": "2.0.16",
"@types/bluebird": "3.5.42",
@@ -73,7 +73,7 @@
"@types/semver": "7.5.8",
"@types/teen_process": "2.0.4",
"@types/wrap-ansi": "3.0.0",
"ajv": "8.14.0",
"ajv": "8.16.0",
"ajv-formats": "3.0.1",
"argparse": "2.0.1",
"async-lock": "1.4.1",
@@ -81,20 +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",
"npmlog": "7.0.1",
"lru-cache": "10.2.2",
"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.3",
"type-fest": "4.18.3",
"teen_process": "2.1.4",
"type-fest": "4.20.0",
"winston": "3.13.0",
"wrap-ansi": "7.0.0",
"ws": "8.17.0",
"yaml": "2.4.2"
"yaml": "2.4.5"
},
"engines": {
"node": "^14.17.0 || ^16.13.0 || >=18.0.0",
@@ -1,5 +1,5 @@
{
"devDependencies": {
"webdriverio": "8.38.0"
"webdriverio": "8.38.2"
}
}
+1
View File
@@ -9,6 +9,7 @@ export default {
driversImportChunkSize: 3,
keepAliveTimeout: 600,
localTimezone: false,
logFormat: 'text',
loglevel: 'debug',
logNoColors: false,
logTimestamp: false,
+75
View File
@@ -3,6 +3,81 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [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
## [9.8.0](https://github.com/appium/appium/compare/@appium/base-driver@9.7.0...@appium/base-driver@9.8.0) (2024-06-06)
### Features
* **base-driver:** Make addition of search query params to cache optional ([#20195](https://github.com/appium/appium/issues/20195)) ([21316e9](https://github.com/appium/appium/commit/21316e94d05b2f97aa0349a82da229713e92446c))
### Bug Fixes
* **types:** update dependency type-fest to v4.19.0 ([#20193](https://github.com/appium/appium/issues/20193)) ([db62358](https://github.com/appium/appium/commit/db62358dbac25de2a75fdc7493338d98b1422c84))
## [9.7.0](https://github.com/appium/appium/compare/@appium/base-driver@9.6.0...@appium/base-driver@9.7.0) (2024-05-27)
@@ -307,7 +307,7 @@ 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.info(`Session created with session id: ${this.sessionId}`);
@@ -347,7 +347,6 @@ export class BaseDriver<
}
}
this.sessionId = null;
this._log.prefix = helpers.generateDriverLogPrefix(this);
}
logExtraCaps(caps: Capabilities<C>) {
+218 -146
View File
@@ -61,74 +61,6 @@ process.on('exit', () => {
}
});
/**
*
* @param {string} [envVarName]
* @param {number} defaultValue
* @returns {number}
*/
function toNaturalNumber(defaultValue, envVarName) {
if (!envVarName || _.isUndefined(process.env[envVarName])) {
return defaultValue;
}
const num = parseInt(`${process.env[envVarName]}`, 10);
return num > 0 ? num : defaultValue;
}
/**
* @param {string} app
* @param {string[]} supportedAppExtensions
* @returns {string}
*/
function verifyAppExtension(app, supportedAppExtensions) {
if (supportedAppExtensions.map(_.toLower).includes(_.toLower(path.extname(app)))) {
return app;
}
throw new Error(
`New app path '${app}' did not have ` +
`${util.pluralize('extension', supportedAppExtensions.length, false)}: ` +
supportedAppExtensions
);
}
/**
* @param {string} folderPath
* @returns {Promise<number>}
*/
async function calculateFolderIntegrity(folderPath) {
return (await fs.glob('**/*', {cwd: folderPath})).length;
}
/**
* @param {string} filePath
* @returns {Promise<string>}
*/
async function calculateFileIntegrity(filePath) {
return await fs.hash(filePath);
}
/**
* @param {string} currentPath
* @param {import('@appium/types').StringRecord} expectedIntegrity
* @returns {Promise<boolean>}
*/
async function isAppIntegrityOk(currentPath, expectedIntegrity = {}) {
if (!(await fs.exists(currentPath))) {
return false;
}
// Folder integrity check is simple:
// Verify the previous amount of files is not greater than the current one.
// We don't want to use equality comparison because of an assumption that the OS might
// create some unwanted service files/cached inside of that folder or its subfolders.
// Ofc, validating the hash sum of each file (or at least of file path) would be much
// more precise, but we don't need to be very precise here and also don't want to
// overuse RAM and have a performance drop.
return (await fs.stat(currentPath)).isDirectory()
? (await calculateFolderIntegrity(currentPath)) >= expectedIntegrity?.folder
: (await calculateFileIntegrity(currentPath)) === expectedIntegrity?.file;
}
/**
*
* @param {string} app
@@ -171,15 +103,16 @@ export async function configureApp(
maxAge: null,
etag: null,
};
const {protocol, pathname} = url.parse(newApp);
const isUrl = protocol === null ? false : ['http:', 'https:'].includes(protocol);
const {protocol, pathname} = parseAppLink(app);
const isUrl = isSupportedUrl(app);
const appCacheKey = toCacheKey(app);
const cachedAppInfo = APPLICATIONS_CACHE.get(app);
const cachedAppInfo = APPLICATIONS_CACHE.get(appCacheKey);
if (cachedAppInfo) {
logger.debug(`Cached app data: ${JSON.stringify(cachedAppInfo, null, 2)}`);
}
return await APPLICATIONS_CACHE_GUARD.acquire(app, async () => {
return await APPLICATIONS_CACHE_GUARD.acquire(appCacheKey, async () => {
if (isUrl) {
// Use the app from remote URL
logger.info(`Using downloadable app '${newApp}'`);
@@ -223,7 +156,7 @@ export async function configureApp(
`The application at '${cachedAppInfo.fullPath}' does not exist anymore ` +
`or its integrity has been damaged. Deleting it from the internal cache`
);
APPLICATIONS_CACHE.delete(app);
APPLICATIONS_CACHE.delete(appCacheKey);
if (!stream.closed) {
stream.destroy();
@@ -332,7 +265,7 @@ export async function configureApp(
`The application at '${fullPath}' does not exist anymore ` +
`or its integrity has been damaged. Deleting it from the cache`
);
APPLICATIONS_CACHE.delete(app);
APPLICATIONS_CACHE.delete(appCacheKey);
}
const tmpRoot = await tempDir.openDir();
try {
@@ -363,7 +296,7 @@ export async function configureApp(
} else {
integrity.file = await calculateFileIntegrity(appPathToCache);
}
APPLICATIONS_CACHE.set(app, {
APPLICATIONS_CACHE.set(appCacheKey, {
...remoteAppProps,
timestamp: Date.now(),
packageHash,
@@ -389,12 +322,93 @@ export async function configureApp(
}
verifyAppExtension(newApp, supportedAppExtensions);
return app !== newApp && (packageHash || _.values(remoteAppProps).some(Boolean))
return appCacheKey !== toCacheKey(newApp) && (packageHash || _.values(remoteAppProps).some(Boolean))
? await storeAppInCache(newApp)
: newApp;
});
}
/**
* @param {string} app
* @returns {boolean}
*/
export function isPackageOrBundle(app) {
return /^([a-zA-Z0-9\-_]+\.[a-zA-Z0-9\-_]+)+$/.test(app);
}
/**
* Finds all instances 'firstKey' and create a duplicate with the key 'secondKey',
* Do the same thing in reverse. If we find 'secondKey', create a duplicate with the key 'firstKey'.
*
* This will cause keys to be overwritten if the object contains 'firstKey' and 'secondKey'.
* @param {*} input Any type of input
* @param {String} firstKey The first key to duplicate
* @param {String} secondKey The second key to duplicate
*/
export function duplicateKeys(input, firstKey, secondKey) {
// If array provided, recursively call on all elements
if (_.isArray(input)) {
return input.map((item) => duplicateKeys(item, firstKey, secondKey));
}
// If object, create duplicates for keys and then recursively call on values
if (_.isPlainObject(input)) {
const resultObj = {};
for (let [key, value] of _.toPairs(input)) {
const recursivelyCalledValue = duplicateKeys(value, firstKey, secondKey);
if (key === firstKey) {
resultObj[secondKey] = recursivelyCalledValue;
} else if (key === secondKey) {
resultObj[firstKey] = recursivelyCalledValue;
}
resultObj[key] = recursivelyCalledValue;
}
return resultObj;
}
// Base case. Return primitives without doing anything.
return input;
}
/**
* Takes a desired capability and tries to JSON.parse it as an array,
* and either returns the parsed array or a singleton array.
*
* @param {string|Array<String>} cap A desired capability
*/
export function parseCapsArray(cap) {
if (_.isArray(cap)) {
return cap;
}
let parsedCaps;
try {
parsedCaps = JSON.parse(cap);
if (_.isArray(parsedCaps)) {
return parsedCaps;
}
} catch (ign) {
logger.warn(`Failed to parse capability as JSON array`);
}
if (_.isString(cap)) {
return [cap];
}
throw new Error(`must provide a string or JSON Array; received ${cap}`);
}
/**
* Generate a string that uniquely describes driver instance
*
* @param {import('@appium/types').Core} obj driver instance
* @param {string?} [sessionId=null] session identifier (if exists).
* This parameter is deprecated and is not used.
* @returns {string}
*/
export function generateDriverLogPrefix(obj, sessionId = null) {
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
@@ -500,9 +514,7 @@ async function unzipApp(zipPath, dstRoot, supportedAppExtensions) {
try {
logger.debug(`Unzipping '${zipPath}'`);
const timer = new timing.Timer().start();
const useSystemUnzipEnv = process.env.APPIUM_PREFER_SYSTEM_UNZIP;
const useSystemUnzip =
_.isEmpty(useSystemUnzipEnv) || !['0', 'false'].includes(_.toLower(useSystemUnzipEnv));
const useSystemUnzip = isEnvOptionEnabled('APPIUM_PREFER_SYSTEM_UNZIP', true);
/**
* Attempt to use use the system `unzip` (e.g., `/usr/bin/unzip`) due
* to the significant performance improvement it provides over the native
@@ -556,84 +568,144 @@ async function unzipApp(zipPath, dstRoot, supportedAppExtensions) {
}
/**
* @param {string} app
* @returns {boolean}
*/
export function isPackageOrBundle(app) {
return /^([a-zA-Z0-9\-_]+\.[a-zA-Z0-9\-_]+)+$/.test(app);
}
/**
* Finds all instances 'firstKey' and create a duplicate with the key 'secondKey',
* Do the same thing in reverse. If we find 'secondKey', create a duplicate with the key 'firstKey'.
* Transforms the given app link to the cache key.
* This is necessary to properly cache apps
* having the same address, but different query strings,
* for example ones stored in S3 using presigned URLs.
*
* This will cause keys to be overwritten if the object contains 'firstKey' and 'secondKey'.
* @param {*} input Any type of input
* @param {String} firstKey The first key to duplicate
* @param {String} secondKey The second key to duplicate
* @param {string} app App link.
* @returns {string} Transformed app link or the original arg if
* no transfromation is needed.
*/
export function duplicateKeys(input, firstKey, secondKey) {
// If array provided, recursively call on all elements
if (_.isArray(input)) {
return input.map((item) => duplicateKeys(item, firstKey, secondKey));
function toCacheKey(app) {
if (!isEnvOptionEnabled('APPIUM_APPS_CACHE_IGNORE_URL_QUERY') || !isSupportedUrl(app)) {
return app;
}
// If object, create duplicates for keys and then recursively call on values
if (_.isPlainObject(input)) {
const resultObj = {};
for (let [key, value] of _.toPairs(input)) {
const recursivelyCalledValue = duplicateKeys(value, firstKey, secondKey);
if (key === firstKey) {
resultObj[secondKey] = recursivelyCalledValue;
} else if (key === secondKey) {
resultObj[firstKey] = recursivelyCalledValue;
}
resultObj[key] = recursivelyCalledValue;
}
return resultObj;
}
// Base case. Return primitives without doing anything.
return input;
}
/**
* Takes a desired capability and tries to JSON.parse it as an array,
* and either returns the parsed array or a singleton array.
*
* @param {string|Array<String>} cap A desired capability
*/
export function parseCapsArray(cap) {
if (_.isArray(cap)) {
return cap;
}
let parsedCaps;
try {
parsedCaps = JSON.parse(cap);
if (_.isArray(parsedCaps)) {
return parsedCaps;
const {href, search} = parseAppLink(app);
if (href && search) {
return href.replace(search, '');
}
} catch (ign) {
logger.warn(`Failed to parse capability as JSON array`);
}
if (_.isString(cap)) {
return [cap];
}
throw new Error(`must provide a string or JSON Array; received ${cap}`);
if (href) {
return href;
}
} catch {}
return app;
}
/**
* Generate a string that uniquely describes driver instance
* Safely parses the given app link to a URL object
*
* @param {import('@appium/types').Core} obj driver instance
* @param {string?} sessionId session identifier (if exists)
* @param {string} appLink
* @returns {URL|import('@appium/types').StringRecord} Parsed URL object
* or an empty object if the parsing has failed
*/
function parseAppLink(appLink) {
try {
return new URL(appLink);
} catch {
return {};
}
}
/**
* Checks whether we can threat the given app link
* as a URL,
*
* @param {string} app
* @returns {boolean} True if app is a supported URL
*/
function isSupportedUrl(app) {
try {
const {protocol} = parseAppLink(app);
return ['http:', 'https:'].includes(protocol);
} catch {
return false;
}
}
/**
* Check if the given environment option is enabled
*
* @param {string} optionName Option name
* @param {boolean|null} [defaultValue=null] The value to return if the given env value
* is not set explcitly
* @returns {boolean} True if the option is enabled
*/
function isEnvOptionEnabled(optionName, defaultValue = null) {
const value = process.env[optionName];
if (!_.isNull(defaultValue) && _.isEmpty(value)) {
return defaultValue;
}
return !_.isEmpty(value) && !['0', 'false', 'no'].includes(_.toLower(value));
}
/**
*
* @param {string} [envVarName]
* @param {number} defaultValue
* @returns {number}
*/
function toNaturalNumber(defaultValue, envVarName) {
if (!envVarName || _.isUndefined(process.env[envVarName])) {
return defaultValue;
}
const num = parseInt(`${process.env[envVarName]}`, 10);
return num > 0 ? num : defaultValue;
}
/**
* @param {string} app
* @param {string[]} supportedAppExtensions
* @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;
function verifyAppExtension(app, supportedAppExtensions) {
if (supportedAppExtensions.map(_.toLower).includes(_.toLower(path.extname(app)))) {
return app;
}
throw new Error(
`New app path '${app}' did not have ` +
`${util.pluralize('extension', supportedAppExtensions.length, false)}: ` +
supportedAppExtensions
);
}
/**
* @param {string} folderPath
* @returns {Promise<number>}
*/
async function calculateFolderIntegrity(folderPath) {
return (await fs.glob('**/*', {cwd: folderPath})).length;
}
/**
* @param {string} filePath
* @returns {Promise<string>}
*/
async function calculateFileIntegrity(filePath) {
return await fs.hash(filePath);
}
/**
* @param {string} currentPath
* @param {import('@appium/types').StringRecord} expectedIntegrity
* @returns {Promise<boolean>}
*/
async function isAppIntegrityOk(currentPath, expectedIntegrity = {}) {
if (!(await fs.exists(currentPath))) {
return false;
}
// Folder integrity check is simple:
// Verify the previous amount of files is not greater than the current one.
// We don't want to use equality comparison because of an assumption that the OS might
// create some unwanted service files/cached inside of that folder or its subfolders.
// Ofc, validating the hash sum of each file (or at least of file path) would be much
// more precise, but we don't need to be very precise here and also don't want to
// overuse RAM and have a performance drop.
return (await fs.stat(currentPath)).isDirectory()
? (await calculateFolderIntegrity(currentPath)) >= expectedIntegrity?.folder
: (await calculateFileIntegrity(currentPath)) === expectedIntegrity?.file;
}
/** @type {import('@appium/types').DriverHelpers} */
+21 -3
View File
@@ -1,7 +1,7 @@
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';
/**
@@ -68,6 +68,26 @@ 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 contextStorage = global._global_npmlog?.asyncStorage;
if (!contextStorage) {
return next();
}
const sessionIdMatch = SESSION_ID_PATTERN.exec(req.url);
contextStorage.enterWith(
sessionIdMatch ? {sessionSignature: sessionIdMatch[1].substring(0, 8)} : {}
);
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';
@@ -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')));
+4 -10
View File
@@ -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');
}
+5 -5
View File
@@ -1,6 +1,6 @@
{
"name": "@appium/base-driver",
"version": "9.7.0",
"version": "9.10.3",
"description": "Base driver class for Appium drivers",
"keywords": [
"automation",
@@ -44,13 +44,13 @@
"test:types": "tsd"
},
"dependencies": {
"@appium/support": "^4.2.6",
"@appium/types": "^0.18.0",
"@appium/support": "^5.0.3",
"@appium/types": "^0.20.3",
"@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.5",
"@types/method-override": "0.0.35",
"@types/serve-favicon": "2.5.7",
"async-lock": "1.4.1",
@@ -67,7 +67,7 @@
"path-to-regexp": "6.2.2",
"serve-favicon": "2.5.0",
"source-map-support": "0.5.21",
"type-fest": "4.18.3",
"type-fest": "4.20.0",
"validate.js": "0.13.1"
},
"optionalDependencies": {
@@ -59,7 +59,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);
});
+56
View File
@@ -3,6 +3,62 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [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
## [2.2.33](https://github.com/appium/appium/compare/@appium/base-plugin@2.2.32...@appium/base-plugin@2.2.33) (2024-06-06)
**Note:** Version bump only for package @appium/base-plugin
## [2.2.32](https://github.com/appium/appium/compare/@appium/base-plugin@2.2.31...@appium/base-plugin@2.2.32) (2024-05-27)
**Note:** Version bump only for package @appium/base-plugin
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@appium/base-plugin",
"version": "2.2.32",
"version": "2.2.39",
"description": "The base plugin used to create Appium 2.0 plugins",
"keywords": [
"automation",
@@ -40,8 +40,8 @@
"test:unit": "mocha \"./test/unit/**/*.spec.js\""
},
"dependencies": {
"@appium/base-driver": "^9.7.0",
"@appium/support": "^4.2.6"
"@appium/base-driver": "^9.10.3",
"@appium/support": "^5.0.3"
},
"engines": {
"node": "^14.17.0 || ^16.13.0 || >=18.0.0",
+64
View File
@@ -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.1.6](https://github.com/appium/appium/compare/@appium/doctor@2.1.5...@appium/doctor@2.1.6) (2024-06-11)
**Note:** Version bump only for package @appium/doctor
## [2.1.5](https://github.com/appium/appium/compare/@appium/doctor@2.1.4...@appium/doctor@2.1.5) (2024-06-11)
**Note:** Version bump only for package @appium/doctor
## [2.1.4](https://github.com/appium/appium/compare/@appium/doctor@2.1.3...@appium/doctor@2.1.4) (2024-06-11)
**Note:** Version bump only for package @appium/doctor
## [2.1.3](https://github.com/appium/appium/compare/@appium/doctor@2.1.2...@appium/doctor@2.1.3) (2024-06-10)
**Note:** Version bump only for package @appium/doctor
## [2.1.2](https://github.com/appium/appium/compare/@appium/doctor@2.1.1...@appium/doctor@2.1.2) (2024-06-06)
**Note:** Version bump only for package @appium/doctor
## [2.1.1](https://github.com/appium/appium/compare/@appium/doctor@2.1.0...@appium/doctor@2.1.1) (2024-06-06)
**Note:** Version bump only for package @appium/doctor
## [2.1.0](https://github.com/appium/appium/compare/@appium/doctor@2.0.37...@appium/doctor@2.1.0) (2024-06-06)
### Features
* **appium:** Replace npmlog with the local fork ([#20190](https://github.com/appium/appium/issues/20190)) ([8915934](https://github.com/appium/appium/commit/8915934270243bfb46c4d104a098ce1cc481b0ff))
### Bug Fixes
* **doctor:** update dependency appium-adb to v12.4.1 ([e3e1757](https://github.com/appium/appium/commit/e3e1757c564cffd467f3e9931f9aaa5a973fa825))
* **doctor:** update dependency appium-adb to v12.4.2 ([bea47aa](https://github.com/appium/appium/commit/bea47aacca1b1a27cce48b027246513e674c2bb7))
* **support:** update dependency teen_process to v2.1.4 ([96a7a3f](https://github.com/appium/appium/commit/96a7a3f23bead7c1d18700550138cd2d70ce5e3c))
## [2.0.37](https://github.com/appium/appium/compare/@appium/doctor@2.0.36...@appium/doctor@2.0.37) (2024-05-27)
+11 -13
View File
@@ -44,28 +44,26 @@ export async function prompt(question) {
return await inquirer.prompt(question);
}
let actualLog;
let onLogMessageHandler = null;
export function configureBinaryLog(opts) {
actualLog = log.unwrap().log;
log.unwrap().log = function (level, prefix, msg) {
let l = this.levels[level];
if (l < this.levels[this.level]) return; // eslint-disable-line curly
actualLog(level, prefix, msg);
if (isFunction(opts.onLogMessage)) {
opts.onLogMessage(level, prefix, msg);
}
const internalLogger = log.unwrap();
if (isFunction(opts.onLogMessage) && !onLogMessageHandler) {
onLogMessageHandler = (/** @type {import('@appium/logger').MessageObject} */ msg) => opts.onLogMessage(
msg.level, msg.prefix, msg.message
);
internalLogger.on('log', onLogMessageHandler);
};
log.level = opts.debug ? 'debug' : 'info';
internalLogger.level = opts.debug ? 'debug' : 'info';
}
/**
* If {@link configureBinaryLog} was called, this will restore the original `log` function.
*/
export function resetLog() {
if (actualLog) {
log.unwrap().log = actualLog;
if (onLogMessageHandler) {
log.unwrap().off('log', onLogMessageHandler);
onLogMessageHandler = null;
}
}
+4 -4
View File
@@ -1,6 +1,6 @@
{
"name": "@appium/doctor",
"version": "2.0.37",
"version": "2.1.6",
"description": "Test environment for fitness to run Appium",
"keywords": [
"automation",
@@ -44,16 +44,16 @@
"test:unit": "mocha \"./test/unit/**/*.spec.js\""
},
"dependencies": {
"@appium/support": "^4.2.6",
"@appium/support": "^5.0.3",
"@colors/colors": "1.6.0",
"@types/bluebird": "3.5.42",
"@types/teen_process": "2.0.4",
"appium-adb": "12.4.0",
"appium-adb": "12.4.2",
"bluebird": "3.7.2",
"inquirer": "9.2.12",
"lodash": "4.17.21",
"source-map-support": "0.5.21",
"teen_process": "2.1.3",
"teen_process": "2.1.4",
"yargs": "17.7.2"
},
"engines": {
+63
View File
@@ -3,6 +3,69 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.0.15](https://github.com/appium/appium/compare/@appium/docutils@1.0.14...@appium/docutils@1.0.15) (2024-06-11)
**Note:** Version bump only for package @appium/docutils
## [1.0.14](https://github.com/appium/appium/compare/@appium/docutils@1.0.13...@appium/docutils@1.0.14) (2024-06-11)
**Note:** Version bump only for package @appium/docutils
## [1.0.13](https://github.com/appium/appium/compare/@appium/docutils@1.0.12...@appium/docutils@1.0.13) (2024-06-11)
**Note:** Version bump only for package @appium/docutils
## [1.0.12](https://github.com/appium/appium/compare/@appium/docutils@1.0.11...@appium/docutils@1.0.12) (2024-06-10)
### 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))
## [1.0.11](https://github.com/appium/appium/compare/@appium/docutils@1.0.10...@appium/docutils@1.0.11) (2024-06-06)
**Note:** Version bump only for package @appium/docutils
## [1.0.10](https://github.com/appium/appium/compare/@appium/docutils@1.0.9...@appium/docutils@1.0.10) (2024-06-06)
**Note:** Version bump only for package @appium/docutils
## [1.0.9](https://github.com/appium/appium/compare/@appium/docutils@1.0.8...@appium/docutils@1.0.9) (2024-06-06)
### Bug Fixes
* **docutils:** update dependency yaml to v2.4.3 ([848eb00](https://github.com/appium/appium/commit/848eb00afbd1cab76a6a1c3d0f4a2a0b1acee0d0))
* **support:** update dependency @types/which to v3 ([#20164](https://github.com/appium/appium/issues/20164)) ([57b0406](https://github.com/appium/appium/commit/57b0406a33bc687c731f9e7fedd56e1b07c3cc67))
* **support:** update dependency teen_process to v2.1.4 ([96a7a3f](https://github.com/appium/appium/commit/96a7a3f23bead7c1d18700550138cd2d70ce5e3c))
* **types:** update dependency type-fest to v4.19.0 ([#20193](https://github.com/appium/appium/issues/20193)) ([db62358](https://github.com/appium/appium/commit/db62358dbac25de2a75fdc7493338d98b1422c84))
## [1.0.8](https://github.com/appium/appium/compare/@appium/docutils@1.0.7...@appium/docutils@1.0.8) (2024-05-27)
+7 -7
View File
@@ -1,6 +1,6 @@
{
"name": "@appium/docutils",
"version": "1.0.8",
"version": "1.0.15",
"description": "Documentation generation utilities for Appium and related projects",
"keywords": [
"automation",
@@ -48,24 +48,24 @@
"start": "node ./build/lib/cli/index.js"
},
"dependencies": {
"@appium/support": "^4.2.6",
"@appium/support": "^5.0.3",
"@appium/tsconfig": "^0.3.3",
"@sliphua/lilconfig-ts-loader": "3.2.2",
"@types/which": "3.0.3",
"@types/which": "3.0.4",
"chalk": "4.1.2",
"consola": "3.2.3",
"diff": "5.2.0",
"json5": "2.2.3",
"lilconfig": "3.1.1",
"lilconfig": "3.1.2",
"lodash": "4.17.21",
"pkg-dir": "5.0.0",
"read-pkg": "5.2.0",
"semver": "7.6.2",
"source-map-support": "0.5.21",
"teen_process": "2.1.3",
"type-fest": "4.18.3",
"teen_process": "2.1.4",
"type-fest": "4.20.0",
"typescript": "5.4.5",
"yaml": "2.4.2",
"yaml": "2.4.5",
"yargs": "17.7.2",
"yargs-parser": "21.1.1"
},
+2 -2
View File
@@ -1,5 +1,5 @@
mkdocs==1.6.0
mkdocs-git-revision-date-localized-plugin==1.2.5
mkdocs-material==9.5.25
mkdocs-git-revision-date-localized-plugin==1.2.6
mkdocs-material==9.5.26
mkdocs-redirects==1.2.1
mike==2.1.1
+70
View File
@@ -3,6 +3,76 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [0.6.3](https://github.com/appium/appium/compare/@appium/driver-test-support@0.6.2...@appium/driver-test-support@0.6.3) (2024-06-11)
**Note:** Version bump only for package @appium/driver-test-support
## [0.6.2](https://github.com/appium/appium/compare/@appium/driver-test-support@0.6.1...@appium/driver-test-support@0.6.2) (2024-06-11)
**Note:** Version bump only for package @appium/driver-test-support
## [0.6.1](https://github.com/appium/appium/compare/@appium/driver-test-support@0.6.0...@appium/driver-test-support@0.6.1) (2024-06-11)
**Note:** Version bump only for package @appium/driver-test-support
## [0.6.0](https://github.com/appium/appium/compare/@appium/driver-test-support@0.5.0...@appium/driver-test-support@0.6.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))
## [0.5.0](https://github.com/appium/appium/compare/@appium/driver-test-support@0.4.26...@appium/driver-test-support@0.5.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)
## [0.4.26](https://github.com/appium/appium/compare/@appium/driver-test-support@0.4.25...@appium/driver-test-support@0.4.26) (2024-06-06)
**Note:** Version bump only for package @appium/driver-test-support
## [0.4.25](https://github.com/appium/appium/compare/@appium/driver-test-support@0.4.24...@appium/driver-test-support@0.4.25) (2024-06-06)
### Bug Fixes
* **types:** update dependency type-fest to v4.19.0 ([#20193](https://github.com/appium/appium/issues/20193)) ([db62358](https://github.com/appium/appium/commit/db62358dbac25de2a75fdc7493338d98b1422c84))
## [0.4.24](https://github.com/appium/appium/compare/@appium/driver-test-support@0.4.23...@appium/driver-test-support@0.4.24) (2024-05-27)
@@ -57,23 +57,6 @@ export function driverUnitTestSuite(
});
});
describe('Log prefix', function () {
it('should setup log prefix', async function () {
const d = new DriverClass();
const previousPrefix = d.log.prefix;
await d.createSession({
alwaysMatch: {...defaultCaps, platformName: 'Fake', 'appium:deviceName': 'Commodore 64'},
firstMatch: [{}],
});
try {
expect(previousPrefix).not.to.eql(d.log.prefix);
} finally {
await d.deleteSession();
expect(previousPrefix).to.eql(d.log.prefix);
}
});
});
it('should return an empty status object', async function () {
let status = await d.getStatus();
status.should.eql({});
+4 -4
View File
@@ -1,6 +1,6 @@
{
"name": "@appium/driver-test-support",
"version": "0.4.24",
"version": "0.6.3",
"description": "Test utilities for Appium drivers",
"keywords": [
"automation",
@@ -41,8 +41,8 @@
},
"types": "./build/lib/index.d.ts",
"dependencies": {
"@appium/types": "^0.18.0",
"@types/lodash": "4.17.4",
"@appium/types": "^0.20.3",
"@types/lodash": "4.17.5",
"@types/stoppable": "1.1.3",
"axios": "1.7.2",
"bluebird": "3.7.2",
@@ -52,7 +52,7 @@
"sinon": "18.0.0",
"source-map-support": "0.5.21",
"stoppable": "1.1.0",
"type-fest": "4.18.3"
"type-fest": "4.20.0"
},
"peerDependencies": {
"appium": "^2.0.0-beta.43",
@@ -3,6 +3,15 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [3.0.30](https://github.com/appium/appium/compare/@appium/execute-driver-plugin@3.0.29...@appium/execute-driver-plugin@3.0.30) (2024-06-06)
### Bug Fixes
* **execute-driver-plugin:** update dependency webdriverio to v8.38.2 ([c8f9979](https://github.com/appium/appium/commit/c8f997973cc21673f95d02c92d43a2981b8713b8))
## [3.0.29](https://github.com/appium/appium/compare/@appium/execute-driver-plugin@3.0.28...@appium/execute-driver-plugin@3.0.29) (2024-05-27)
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@appium/execute-driver-plugin",
"version": "3.0.29",
"version": "3.0.30",
"description": "Plugin for batching and executing driver commands with Appiums",
"keywords": [
"automation",
@@ -43,7 +43,7 @@
"lodash": "4.17.21",
"source-map-support": "0.5.21",
"vm2": "3.9.19",
"webdriverio": "8.38.0"
"webdriverio": "8.38.2"
},
"peerDependencies": {
"appium": "^2.0.0-beta.35"
+56
View File
@@ -3,6 +3,62 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [3.1.43](https://github.com/appium/appium/compare/@appium/fake-plugin@3.1.42...@appium/fake-plugin@3.1.43) (2024-06-11)
**Note:** Version bump only for package @appium/fake-plugin
## [3.1.42](https://github.com/appium/appium/compare/@appium/fake-plugin@3.1.41...@appium/fake-plugin@3.1.42) (2024-06-11)
**Note:** Version bump only for package @appium/fake-plugin
## [3.1.41](https://github.com/appium/appium/compare/@appium/fake-plugin@3.1.40...@appium/fake-plugin@3.1.41) (2024-06-11)
**Note:** Version bump only for package @appium/fake-plugin
## [3.1.40](https://github.com/appium/appium/compare/@appium/fake-plugin@3.1.39...@appium/fake-plugin@3.1.40) (2024-06-10)
**Note:** Version bump only for package @appium/fake-plugin
## [3.1.39](https://github.com/appium/appium/compare/@appium/fake-plugin@3.1.38...@appium/fake-plugin@3.1.39) (2024-06-06)
**Note:** Version bump only for package @appium/fake-plugin
## [3.1.38](https://github.com/appium/appium/compare/@appium/fake-plugin@3.1.37...@appium/fake-plugin@3.1.38) (2024-06-06)
**Note:** Version bump only for package @appium/fake-plugin
## [3.1.37](https://github.com/appium/appium/compare/@appium/fake-plugin@3.1.36...@appium/fake-plugin@3.1.37) (2024-06-06)
**Note:** Version bump only for package @appium/fake-plugin
## [3.1.36](https://github.com/appium/appium/compare/@appium/fake-plugin@3.1.35...@appium/fake-plugin@3.1.36) (2024-05-27)
**Note:** Version bump only for package @appium/fake-plugin
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@appium/fake-plugin",
"version": "3.1.36",
"version": "3.1.43",
"description": "A fake Appium 2.0 plugin",
"keywords": [
"automation",
@@ -39,8 +39,8 @@
"test:unit": "mocha \"./test/unit/**/*.spec.js\""
},
"dependencies": {
"@appium/base-plugin": "^2.2.32",
"@appium/support": "^4.2.6",
"@appium/base-plugin": "^2.2.39",
"@appium/support": "^5.0.3",
"@types/bluebird": "3.5.42",
"bluebird": "3.7.2",
"lodash": "4.17.21",
+56
View File
@@ -3,6 +3,62 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [3.0.14](https://github.com/appium/appium/compare/@appium/images-plugin@3.0.13...@appium/images-plugin@3.0.14) (2024-06-11)
**Note:** Version bump only for package @appium/images-plugin
## [3.0.13](https://github.com/appium/appium/compare/@appium/images-plugin@3.0.12...@appium/images-plugin@3.0.13) (2024-06-11)
**Note:** Version bump only for package @appium/images-plugin
## [3.0.12](https://github.com/appium/appium/compare/@appium/images-plugin@3.0.11...@appium/images-plugin@3.0.12) (2024-06-11)
**Note:** Version bump only for package @appium/images-plugin
## [3.0.11](https://github.com/appium/appium/compare/@appium/images-plugin@3.0.10...@appium/images-plugin@3.0.11) (2024-06-10)
**Note:** Version bump only for package @appium/images-plugin
## [3.0.10](https://github.com/appium/appium/compare/@appium/images-plugin@3.0.9...@appium/images-plugin@3.0.10) (2024-06-06)
**Note:** Version bump only for package @appium/images-plugin
## [3.0.9](https://github.com/appium/appium/compare/@appium/images-plugin@3.0.8...@appium/images-plugin@3.0.9) (2024-06-06)
**Note:** Version bump only for package @appium/images-plugin
## [3.0.8](https://github.com/appium/appium/compare/@appium/images-plugin@3.0.7...@appium/images-plugin@3.0.8) (2024-06-06)
**Note:** Version bump only for package @appium/images-plugin
## [3.0.7](https://github.com/appium/appium/compare/@appium/images-plugin@3.0.6...@appium/images-plugin@3.0.7) (2024-05-27)
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@appium/images-plugin",
"version": "3.0.7",
"version": "3.0.14",
"description": "Plugin for working with images and image elements in Appium",
"keywords": [
"automation",
@@ -40,7 +40,7 @@
},
"dependencies": {
"@appium/opencv": "^3.0.4",
"@appium/support": "^4.2.6",
"@appium/support": "^5.0.3",
"lodash": "4.17.21",
"lru-cache": "10.2.2",
"sharp": "0.33.4",
+64
View File
@@ -0,0 +1,64 @@
# Change Log
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.4.2](https://github.com/appium/appium/compare/@appium/logger@1.4.1...@appium/logger@1.4.2) (2024-06-11)
### Bug Fixes
* **logger:** use `index.d.ts` for types instead of `index.ts` ([#20247](https://github.com/appium/appium/issues/20247)) ([9469aae](https://github.com/appium/appium/commit/9469aaef0e31d27b4814bc14763b0abeb6e11bf7))
## [1.4.1](https://github.com/appium/appium/compare/@appium/logger@1.4.0...@appium/logger@1.4.1) (2024-06-11)
### Bug Fixes
* **logger:** Type declaration for DEFAULT_LOG_LEVELS ([#20244](https://github.com/appium/appium/issues/20244)) ([c670010](https://github.com/appium/appium/commit/c670010ec7ea1c2730839e86b308837a83fc026e))
## [1.4.0](https://github.com/appium/appium/compare/@appium/logger@1.3.0...@appium/logger@1.4.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)
* **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))
## [1.3.0](https://github.com/appium/appium/compare/@appium/logger@1.2.0...@appium/logger@1.3.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)
## [1.2.0](https://github.com/appium/appium/compare/@appium/logger@1.1.0...@appium/logger@1.2.0) (2024-06-06)
### Features
* **logger:** Add the 'debug' level to the default logger ([#20203](https://github.com/appium/appium/issues/20203)) ([7fd9d5f](https://github.com/appium/appium/commit/7fd9d5f6261b385c234580c2bfee4d576905458b))
## 1.1.0 (2024-06-06)
### Features
* **appium:** Replace npmlog with the local fork ([#20190](https://github.com/appium/appium/issues/20190)) ([8915934](https://github.com/appium/appium/commit/8915934270243bfb46c4d104a098ce1cc481b0ff))
* **logger:** add packages/logger package from npmlog ([#20161](https://github.com/appium/appium/issues/20161)) ([70449cd](https://github.com/appium/appium/commit/70449cd077d7efc3dbc8aa498ee2072cc2dc0f22))
+18
View File
@@ -0,0 +1,18 @@
ISC License
Copyright npm, Inc.
Permission to use, copy, modify, and/or distribute this
software for any purpose with or without fee is hereby
granted, provided that the above copyright notice and this
permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND NPM DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO
EVENT SHALL NPM BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
USE OR PERFORMANCE OF THIS SOFTWARE.
+31
View File
@@ -0,0 +1,31 @@
# @appium/logger
> Appium's logging functionality
## Installation
```console
npm install @appium/logger --save
```
## Basic Usage
```js
import log from '@appium/logger';
// additional stuff ---------------------------+
// message ----------+ |
// prefix ----+ | |
// level -+ | | |
// v v v v
log.info('fyi', 'I have a kitty cat: %j', myKittyCat);
```
## History
This module is forked from [npmlog](https://github.com/npm/npmlog) under ISC License because the original project has been archived.
Please check [the npmlog changelog](https://github.com/npm/npmlog/blob/main/CHANGELOG.md) to see the list of former module updates before it was forked.
## License
ISC License
+5
View File
@@ -0,0 +1,5 @@
import log from './lib/log';
export type * from './lib/types';
export {log};
export default log;
+359
View File
@@ -0,0 +1,359 @@
import _ from 'lodash';
import {EventEmitter} from 'node:events';
// @ts-ignore This module does not provide type definitons
import setBlocking from 'set-blocking';
// @ts-ignore This module does not provide type definitons
import consoleControl from 'console-control-strings';
import * as util from 'node:util';
import type {
MessageObject,
StyleObject,
Logger,
LogLevel,
PreprocessingRulesLoadResult,
LogFiltersConfig
} from './types';
import type {Writable} from 'node:stream';
import {AsyncLocalStorage} from 'node:async_hooks';
import { unleakString } from './utils';
import { SecureValuesPreprocessor } from './secure-values-preprocessor';
const DEFAULT_LOG_LEVELS = [
['silly', -Infinity, {inverse: true}, 'sill'],
['verbose', 1000, {fg: 'cyan', bg: 'black'}, 'verb'],
['debug', 1500, {fg: 'cyan', bg: 'black'}, 'dbug'],
['info', 2000, {fg: 'green'}],
['timing', 2500, {fg: 'green', bg: 'black'}],
['http', 3000, {fg: 'green', bg: 'black'}],
['notice', 3500, {fg: 'cyan', bg: 'black'}],
['warn', 4000, {fg: 'black', bg: 'yellow'}, 'WARN'],
['error', 5000, {fg: 'red', bg: 'black'}, 'ERR!'],
['silent', Infinity],
] as const;
setBlocking(true);
export class Log extends EventEmitter implements Logger {
level: LogLevel | string;
record: MessageObject[];
maxRecordSize: number;
prefixStyle: StyleObject;
headingStyle: StyleObject;
heading: string;
stream: Writable; // Defaults to process.stderr
_asyncStorage: AsyncLocalStorage<Record<string, any>>;
_colorEnabled?: boolean;
_buffer: MessageObject[];
_style: Record<LogLevel | string, StyleObject | undefined>;
_levels: Record<LogLevel | string, number>;
_disp: Record<LogLevel | string, number | string>;
_id: number;
_paused: boolean;
_secureValuesPreprocessor: SecureValuesPreprocessor;
constructor() {
super();
this.level = 'info';
this._buffer = [];
this.record = [];
this.maxRecordSize = 10000;
this.stream = process.stderr;
this.heading = '';
this.prefixStyle = {fg: 'magenta'};
this.headingStyle = {fg: 'white', bg: 'black'};
this._id = 0;
this._paused = false;
this._asyncStorage = new AsyncLocalStorage();
this._secureValuesPreprocessor = new SecureValuesPreprocessor();
this._style = {};
this._levels = {};
this._disp = {};
this.initDefaultLevels();
// allow 'error' prefix
this.on('error', () => {});
}
private useColor(): boolean {
// by default, decide based on tty-ness.
return (
this._colorEnabled ?? Boolean(this.stream && 'isTTY' in this.stream && this.stream.isTTY)
);
}
get asyncStorage(): AsyncLocalStorage<Record<string, any>> {
return this._asyncStorage;
}
enableColor(): void {
this._colorEnabled = true;
}
disableColor(): void {
this._colorEnabled = false;
}
// this functionality has been deliberately disabled
enableUnicode(): void {}
disableUnicode(): void {}
enableProgress(): void {}
disableProgress(): void {}
progressEnabled(): boolean {
return false;
}
/**
* Temporarily stop emitting, but don't drop
*/
pause(): void {
this._paused = true;
}
resume(): void {
if (!this._paused) {
return;
}
this._paused = false;
const b = this._buffer;
this._buffer = [];
for (const m of b) {
this.emitLog(m);
}
}
silly(prefix: string, message: any, ...args: any[]): void {
this.log('silly', prefix, message, ...args);
}
verbose(prefix: string, message: any, ...args: any[]): void {
this.log('verbose', prefix, message, ...args);
}
debug(prefix: string, message: any, ...args: any[]): void {
this.log('debug', prefix, message, ...args);
}
info(prefix: string, message: any, ...args: any[]): void {
this.log('info', prefix, message, ...args);
}
timing(prefix: string, message: any, ...args: any[]): void {
this.log('timing', prefix, message, ...args);
}
http(prefix: string, message: any, ...args: any[]): void {
this.log('http', prefix, message, ...args);
}
notice(prefix: string, message: any, ...args: any[]): void {
this.log('notice', prefix, message, ...args);
}
warn(prefix: string, message: any, ...args: any[]): void {
this.log('warn', prefix, message, ...args);
}
error(prefix: string, message: any, ...args: any[]): void {
this.log('error', prefix, message, ...args);
}
silent(prefix: string, message: any, ...args: any[]): void {
this.log('silent', prefix, message, ...args);
}
addLevel(level: string, n: number, style?: StyleObject, disp?: string): void {
this._levels[level] = n;
this._style[level] = style;
if (!(this as any)[level]) {
(this as any)[level] = (prefix: string, message: any, ...args: any[]) => {
this.log(level, prefix, message, ...args);
};
}
// If 'disp' is null or undefined, use the level as a default
this._disp[level] = disp ?? level;
}
/**
* Creates a log message
* @param level
* @param prefix
* @param message message of the log which will be formatted using utils.format()
* @param args additional arguments appended to the log message also formatted using utils.format()
*/
log(level: LogLevel | string, prefix: string, message: any, ...args: any[]): void {
const l = this._levels[level];
if (l === undefined) {
this.emit('error', new Error(util.format('Undefined log level: %j', level)));
return;
}
const messageArguments: any[] = [];
let stack: string | null = null;
for (const formatArg of [message, ...args]) {
messageArguments.push(formatArg);
// resolve stack traces to a plain string.
if (_.isError(formatArg) && formatArg.stack) {
Object.defineProperty(formatArg, 'stack', {
value: (stack = formatArg.stack + ''),
enumerable: true,
writable: true,
});
}
}
if (stack) {
messageArguments.unshift(`${stack}\n`);
}
const formattedMessage: string = util.format(...messageArguments);
const m: MessageObject = {
id: this._id++,
timestamp: Date.now(),
level,
prefix: this._secureValuesPreprocessor.preprocess(unleakString(prefix || '')),
message: this._secureValuesPreprocessor.preprocess(unleakString(formattedMessage)),
};
this.emit('log', m);
this.emit('log.' + level, m);
if (m.prefix) {
this.emit(m.prefix, m);
}
this.record.push(m);
const mrs = this.maxRecordSize;
if (this.record.length > mrs) {
this.record.shift();
}
this.emitLog(m);
}
/**
* Loads the JSON file containing secure values replacement rules.
* This might be necessary to hide sensitive values that may possibly
* appear in Appium logs.
* Each call to this method replaces the previously loaded rules if any existed.
*
* @param {string|string[]|LogFiltersConfig} rulesJsonPath The full path to the JSON file containing
* the replacement rules. Each rule could either be a string to be replaced
* or an object with predefined properties.
* @throws {Error} If the given file cannot be loaded
* @returns {Promise<PreprocessingRulesLoadResult>}
*/
async loadSecureValuesPreprocessingRules(
rulesJsonPath: string | string[] | LogFiltersConfig
): Promise<PreprocessingRulesLoadResult> {
const issues = await this._secureValuesPreprocessor.loadRules(rulesJsonPath);
return {
issues,
rules: _.cloneDeep(this._secureValuesPreprocessor.rules),
};
}
private emitLog(m: MessageObject): void {
if (this._paused) {
this._buffer.push(m);
return;
}
const l = this._levels[m.level];
if (l === undefined) {
return;
}
if (l < this._levels[this.level]) {
return;
}
if (l > 0 && !isFinite(l)) {
return;
}
// If 'disp' is null or undefined, use the lvl as a default
// Allows: '', 0 as valid disp
const disp = this._disp[m.level];
this.clearProgress();
for (const line of m.message.split(/\r?\n/)) {
const heading = this.heading;
if (heading) {
this.write(heading, this.headingStyle);
this.write(' ');
}
this.write(String(disp), this._style[m.level]);
const p = m.prefix || '';
if (p) {
this.write(' ');
}
this.write(p, this.prefixStyle);
this.write(` ${line}\n`);
}
this.showProgress();
}
private _format(msg: string, style: StyleObject = {}): string | undefined {
if (!this.stream) {
return;
}
let output = '';
if (this.useColor()) {
const settings: string[] = [];
if (style.fg) {
settings.push(style.fg);
}
if (style.bg) {
settings.push('bg' + style.bg[0].toUpperCase() + style.bg.slice(1));
}
if (style.bold) {
settings.push('bold');
}
if (style.underline) {
settings.push('underline');
}
if (style.inverse) {
settings.push('inverse');
}
if (settings.length) {
output += consoleControl.color(settings);
}
if (style.bell) {
output += consoleControl.beep();
}
}
output += msg;
if (this.useColor()) {
output += consoleControl.color('reset');
}
return output;
}
private write(msg: string, style: StyleObject = {}): void {
if (!this.stream) {
return;
}
const formatted = this._format(msg, style);
if (formatted !== undefined) {
this.stream.write(formatted);
}
}
private initDefaultLevels(): void {
for (const [level, index, style, disp] of DEFAULT_LOG_LEVELS) {
this._levels[level] = index;
this._style[level] = style;
this._disp[level] = disp ?? level;
}
}
// this functionality has been deliberately disabled
private clearProgress(): void {}
private showProgress(): void {}
}
export const GLOBAL_LOG = new Log();
export default GLOBAL_LOG;
@@ -1,17 +1,25 @@
import _ from 'lodash';
import type {
SecureValuePreprocessingRule,
LogFilterRegex,
LogFiltersConfig,
LogFilter,
} from './types';
const DEFAULT_REPLACER = '**SECURE**';
/**
* Type guard for log filter type
* @param {object} value
* @returns {value is import('@appium/types').LogFilterRegex}
* @returns {value is LogFilterRegex}
*/
function isLogFilterRegex(value) {
function isLogFilterRegex(value: object): value is LogFilterRegex {
return 'pattern' in value;
}
class SecureValuesPreprocessor {
export class SecureValuesPreprocessor {
_rules: SecureValuePreprocessingRule[];
constructor() {
this._rules = [];
}
@@ -20,20 +28,20 @@ class SecureValuesPreprocessor {
* @returns {Array<SecureValuePreprocessingRule>} The list of successfully
* parsed preprocessing rules
*/
get rules() {
get rules(): Array<SecureValuePreprocessingRule> {
return this._rules;
}
/**
* Parses single rule from the given JSON file
*
* @param {string|import('@appium/types').LogFilter} rule The rule might
* @param {string|LogFilter} rule The rule might
* either be represented as a single string or a configuration object
* @throws {Error} If there was an error while parsing the rule
* @returns {SecureValuePreprocessingRule} The parsed rule
*/
parseRule(rule) {
let pattern;
parseRule(rule: string | LogFilter): SecureValuePreprocessingRule {
let pattern: string | undefined;
let replacer = DEFAULT_REPLACER;
let flags = ['g'];
if (_.isString(rule)) {
@@ -89,23 +97,21 @@ class SecureValuesPreprocessor {
/**
* Loads rules from the given JSON file
*
* @param {string|string[]|import('@appium/types').LogFiltersConfig} filters
* @param {string|string[]|LogFiltersConfig} filters
* One or more log parsing rules
* @throws {Error} If the format of the source file is invalid or
* it does not exist
* @returns {Promise<string[]>} The list of issues found while parsing each rule.
* An empty list is returned if no rule parsing issues were found
*/
async loadRules(filters) {
/** @type {string[]} */
const issues = [];
/** @type {(import('@appium/types').LogFilter|string)[]} */
const rawRules = [];
async loadRules(filters: string | string[] | LogFiltersConfig): Promise<string[]> {
const issues: string[] = [];
const rawRules: (LogFilter | string)[] = [];
for (const source of (_.isArray(filters) ? filters : [filters])) {
if (_.isPlainObject(source)) {
rawRules.push(/** @type {import('@appium/types').LogFilter} */ (source));
rawRules.push(source as LogFilter);
} else if (_.isString(source)) {
rawRules.push(source);
rawRules.push(String(source));
} else {
issues.push(`'${source}' must be a valid log filtering rule`);
}
@@ -115,7 +121,7 @@ class SecureValuesPreprocessor {
try {
this._rules.push(this.parseRule(rawRule));
} catch (e) {
issues.push(e.message);
issues.push((e as Error).message);
}
}
return issues;
@@ -129,27 +135,15 @@ class SecureValuesPreprocessor {
* @param {string} str The string to make replacements in
* @returns {string} The string with replacements made
*/
preprocess(str) {
if (this._rules.length === 0 || !_.isString(str)) {
preprocess(str: string): string {
if (this._rules.length === 0 || !str || !_.isString(str)) {
return str;
}
let result = str;
for (const rule of this._rules) {
result = result.replace(rule.pattern, rule.replacer);
result = result.replace(rule.pattern, rule.replacer ?? DEFAULT_REPLACER);
}
return result;
}
}
const SECURE_VALUES_PREPROCESSOR = new SecureValuesPreprocessor();
export {SECURE_VALUES_PREPROCESSOR, SecureValuesPreprocessor};
export default SECURE_VALUES_PREPROCESSOR;
/**
* @typedef SecureValuePreprocessingRule
* @property {RegExp} pattern The parsed pattern which is going to be used for replacement
* @property {string} [replacer] The replacer value to use. By default
* equals to `DEFAULT_SECURE_REPLACER`
*/
+146
View File
@@ -0,0 +1,146 @@
import type {EventEmitter} from 'node:events';
import type {AsyncLocalStorage} from 'node:async_hooks';
export interface Logger extends EventEmitter {
level: string;
record: MessageObject[];
maxRecordSize: number;
prefixStyle: StyleObject;
headingStyle: StyleObject;
heading: string;
stream: any; // Defaults to process.stderr
/**
* Creates a log message
* @param level
* @param prefix
* @param message message of the log which will be formatted using utils.format()
* @param args additional arguments appended to the log message also formatted using utils.format()
*/
log(level: LogLevel | string, prefix: string, message: any, ...args: any[]): void;
/**
* @param prefix
* @param message message of the log which will be formatted using utils.format()
* @param args additional arguments appended to the log message also formatted using utils.format()
*/
silly(prefix: string, message: any, ...args: any[]): void;
verbose(prefix: string, message: any, ...args: any[]): void;
debug(prefix: string, message: any, ...args: any[]): void;
info(prefix: string, message: any, ...args: any[]): void;
timing(prefix: string, message: any, ...args: any[]): void;
http(prefix: string, message: any, ...args: any[]): void;
notice(prefix: string, message: any, ...args: any[]): void;
warn(prefix: string, message: any, ...args: any[]): void;
error(prefix: string, message: any, ...args: any[]): void;
silent(prefix: string, message: any, ...args: any[]): void;
loadSecureValuesPreprocessingRules(
rulesJsonPath: string | string[] | LogFiltersConfig
): Promise<PreprocessingRulesLoadResult>;
enableColor(): void;
disableColor(): void;
enableProgress(): void;
disableProgress(): void;
progressEnabled(): boolean;
enableUnicode(): void;
disableUnicode(): void;
pause(): void;
resume(): void;
addLevel(level: string, n: number, style?: StyleObject, disp?: string): void;
get asyncStorage(): AsyncLocalStorage<Record<string, any>>;
// Allows for custom log levels
// log.addLevel("custom", level)
// log.custom(prefix, message)
[key: string]: any;
}
export type LogLevel =
| 'silly'
| 'verbose'
| 'debug'
| 'info'
| 'timing'
| 'http'
| 'notice'
| 'warn'
| 'error'
| 'silent';
export interface StyleObject {
fg?: string | undefined;
bg?: string | undefined;
bold?: boolean | undefined;
inverse?: boolean | undefined;
underline?: boolean | undefined;
bell?: boolean | undefined;
}
export interface MessageObject {
id: number;
timestamp: number;
level: string;
prefix: string;
message: string;
}
export interface SecureValuePreprocessingRule {
/** The parsed pattern which is going to be used for replacement */
pattern: RegExp;
/** The replacer value to use. By default equals to `DEFAULT_SECURE_REPLACER` */
replacer?: string;
}
export interface PreprocessingRulesLoadResult {
/**
* The list of rule parsing issues (one item per rule).
* Rules with issues are skipped. An empty list is returned if no parsing issues exist.
*/
issues: string[];
/**
* The list of successfully loaded
* replacement rules. The list could be empty if no rules were loaded.
*/
rules: SecureValuePreprocessingRule[];
}
export type LogFilter = {
/**
* Replacement string for matched text
*/
replacer?: string;
/**
* Matching flags; see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#advanced_searching_with_flags
*/
flags?: string;
[k: string]: unknown;
} & (LogFilterText | LogFilterRegex);
/**
* One or more log filtering rules
*/
export type LogFiltersConfig = LogFilter[];
export interface LogFilterText {
/**
* Text to match
*/
text: string;
[k: string]: unknown;
}
/**
* Log filter with regular expression
*/
export interface LogFilterRegex {
/**
* Regex pattern to match
*/
pattern: string;
[k: string]: unknown;
}
+11
View File
@@ -0,0 +1,11 @@
/**
* This function is necessary to workaround unexpected memory leaks
* caused by NodeJS string interning
* behavior described in https://bugs.chromium.org/p/v8/issues/detail?id=2869
*
* @param {any} s - The string to unleak
* @return {string} Either the unleaked string or the original object converted to string
*/
export function unleakString(s: any): string {
return ` ${s}`.substring(1);
}
+57
View File
@@ -0,0 +1,57 @@
{
"name": "@appium/logger",
"version": "1.4.2",
"author": "https://github.com/appium",
"description": "A Universal Logger For The Appium Ecosystem",
"repository": {
"type": "git",
"url": "https://github.com/appium/appium.git",
"directory": "packages/logger"
},
"main": "build/index.js",
"types": "build/index.d.ts",
"files": [
"index.ts",
"lib",
"build",
"tsconfig.json",
"!build/tsconfig.tsbuildinfo",
"CHANGELOG.md"
],
"directories": {
"lib": "./lib"
},
"scripts": {
"test": "npm run test:unit",
"test:smoke": "node ./build/index.js",
"test:unit": "mocha --exit --timeout 1m \"./test/unit/**/*-specs.js\""
},
"dependencies": {
"console-control-strings": "1.1.0",
"lodash": "4.17.21",
"set-blocking": "2.0.0"
},
"license": "ISC",
"engines": {
"node": "^14.17.0 || ^16.13.0 || >=18.0.0",
"npm": ">=8"
},
"bugs": {
"url": "https://github.com/appium/appium/issues"
},
"homepage": "https://appium.io",
"publishConfig": {
"access": "public"
},
"gitHead": "8480a85ce2fa466360e0fb1a7f66628331907f02",
"keywords": [
"automation",
"javascript",
"selenium",
"webdriver",
"ios",
"android",
"firefoxos",
"testing"
]
}
+440
View File
@@ -0,0 +1,440 @@
/* eslint-disable no-console */
import { Log } from '../../lib/log';
import { unleakString } from '../../lib/utils';
import {Stream} from 'node:stream';
describe('basic', async function () {
const chai = await import('chai');
chai.should();
let log;
describe('logging', function () {
let s;
let result = [];
let logEvents = [];
let logInfoEvents = [];
let logPrefixEvents = [];
const resultExpect = [
// eslint-disable-next-line max-len
'\u001b[37;40mnpm\u001b[0m \u001b[0m\u001b[7msill\u001b[0m \u001b[0m\u001b[35msilly prefix\u001b[0m x = {"foo":{"bar":"baz"}}\n',
// eslint-disable-next-line max-len
'\u001b[0m\u001b[37;40mnpm\u001b[0m \u001b[0m\u001b[36;40mverb\u001b[0m \u001b[0m\u001b[35mverbose prefix\u001b[0m x = {"foo":{"bar":"baz"}}\n',
// eslint-disable-next-line max-len
'\u001b[0m\u001b[37;40mnpm\u001b[0m \u001b[0m\u001b[32minfo\u001b[0m \u001b[0m\u001b[35minfo prefix\u001b[0m x = {"foo":{"bar":"baz"}}\n',
// eslint-disable-next-line max-len
'\u001b[0m\u001b[37;40mnpm\u001b[0m \u001b[0m\u001b[32;40mtiming\u001b[0m \u001b[0m\u001b[35mtiming prefix\u001b[0m x = {"foo":{"bar":"baz"}}\n',
// eslint-disable-next-line max-len
'\u001b[0m\u001b[37;40mnpm\u001b[0m \u001b[0m\u001b[32;40mhttp\u001b[0m \u001b[0m\u001b[35mhttp prefix\u001b[0m x = {"foo":{"bar":"baz"}}\n',
// eslint-disable-next-line max-len
'\u001b[0m\u001b[37;40mnpm\u001b[0m \u001b[0m\u001b[36;40mnotice\u001b[0m \u001b[0m\u001b[35mnotice prefix\u001b[0m x = {"foo":{"bar":"baz"}}\n',
// eslint-disable-next-line max-len
'\u001b[0m\u001b[37;40mnpm\u001b[0m \u001b[0m\u001b[30;43mWARN\u001b[0m \u001b[0m\u001b[35mwarn prefix\u001b[0m x = {"foo":{"bar":"baz"}}\n',
// eslint-disable-next-line max-len
'\u001b[0m\u001b[37;40mnpm\u001b[0m \u001b[0m\u001b[31;40mERR!\u001b[0m \u001b[0m\u001b[35merror prefix\u001b[0m x = {"foo":{"bar":"baz"}}\n',
// eslint-disable-next-line max-len
'\u001b[0m\u001b[37;40mnpm\u001b[0m \u001b[0m\u001b[32minfo\u001b[0m \u001b[0m\u001b[35minfo prefix\u001b[0m x = {"foo":{"bar":"baz"}}\n',
// eslint-disable-next-line max-len
'\u001b[0m\u001b[37;40mnpm\u001b[0m \u001b[0m\u001b[32;40mtiming\u001b[0m \u001b[0m\u001b[35mtiming prefix\u001b[0m x = {"foo":{"bar":"baz"}}\n',
// eslint-disable-next-line max-len
'\u001b[0m\u001b[37;40mnpm\u001b[0m \u001b[0m\u001b[32;40mhttp\u001b[0m \u001b[0m\u001b[35mhttp prefix\u001b[0m x = {"foo":{"bar":"baz"}}\n',
// eslint-disable-next-line max-len
'\u001b[0m\u001b[37;40mnpm\u001b[0m \u001b[0m\u001b[36;40mnotice\u001b[0m \u001b[0m\u001b[35mnotice prefix\u001b[0m x = {"foo":{"bar":"baz"}}\n',
// eslint-disable-next-line max-len
'\u001b[0m\u001b[37;40mnpm\u001b[0m \u001b[0m\u001b[30;43mWARN\u001b[0m \u001b[0m\u001b[35mwarn prefix\u001b[0m x = {"foo":{"bar":"baz"}}\n',
// eslint-disable-next-line max-len
'\u001b[0m\u001b[37;40mnpm\u001b[0m \u001b[0m\u001b[31;40mERR!\u001b[0m \u001b[0m\u001b[35merror prefix\u001b[0m x = {"foo":{"bar":"baz"}}\n',
// eslint-disable-next-line max-len
'\u001b[0m\u001b[37;40mnpm\u001b[0m \u001b[0m\u001b[31;40mERR!\u001b[0m \u001b[0m\u001b[35m404\u001b[0m This is a longer\n',
// eslint-disable-next-line max-len
'\u001b[0m\u001b[37;40mnpm\u001b[0m \u001b[0m\u001b[31;40mERR!\u001b[0m \u001b[0m\u001b[35m404\u001b[0m message, with some details\n',
// eslint-disable-next-line max-len
'\u001b[0m\u001b[37;40mnpm\u001b[0m \u001b[0m\u001b[31;40mERR!\u001b[0m \u001b[0m\u001b[35m404\u001b[0m and maybe a stack.\n',
// eslint-disable-next-line max-len
'\u001b[0m\u001b[37;40mnpm\u001b[0m \u001b[0m\u001b[31;40mERR!\u001b[0m \u001b[0m\u001b[35m404\u001b[0m \n',
// eslint-disable-next-line max-len
'\u001b[0m\u001b[37;40mnpm\u001b[0m \u001b[0m\u0007noise\u001b[0m\u001b[35m\u001b[0m LOUD NOISES\n',
// eslint-disable-next-line max-len
'\u001b[0m\u001b[37;40mnpm\u001b[0m \u001b[0m\u0007noise\u001b[0m \u001b[0m\u001b[35merror\u001b[0m erroring\n',
'\u001b[0m',
];
const logPrefixEventsExpect = [
{ id: 2,
level: 'info',
prefix: 'info prefix',
message: 'x = {"foo":{"bar":"baz"}}',
},
{ id: 11,
level: 'info',
prefix: 'info prefix',
message: 'x = {"foo":{"bar":"baz"}}',
},
{ id: 20,
level: 'info',
prefix: 'info prefix',
message: 'x = {"foo":{"bar":"baz"}}',
},
];
// should be the same.
const logInfoEventsExpect = logPrefixEventsExpect;
const logEventsExpect = [
{ id: 0,
level: 'silly',
prefix: 'silly prefix',
message: 'x = {"foo":{"bar":"baz"}}',
},
{ id: 1,
level: 'verbose',
prefix: 'verbose prefix',
message: 'x = {"foo":{"bar":"baz"}}',
},
{ id: 2,
level: 'info',
prefix: 'info prefix',
message: 'x = {"foo":{"bar":"baz"}}',
},
{ id: 3,
level: 'timing',
prefix: 'timing prefix',
message: 'x = {"foo":{"bar":"baz"}}',
},
{ id: 4,
level: 'http',
prefix: 'http prefix',
message: 'x = {"foo":{"bar":"baz"}}',
},
{ id: 5,
level: 'notice',
prefix: 'notice prefix',
message: 'x = {"foo":{"bar":"baz"}}',
},
{ id: 6,
level: 'warn',
prefix: 'warn prefix',
message: 'x = {"foo":{"bar":"baz"}}',
},
{ id: 7,
level: 'error',
prefix: 'error prefix',
message: 'x = {"foo":{"bar":"baz"}}',
},
{ id: 8,
level: 'silent',
prefix: 'silent prefix',
message: 'x = {"foo":{"bar":"baz"}}',
},
{ id: 9,
level: 'silly',
prefix: 'silly prefix',
message: 'x = {"foo":{"bar":"baz"}}',
},
{ id: 10,
level: 'verbose',
prefix: 'verbose prefix',
message: 'x = {"foo":{"bar":"baz"}}',
},
{ id: 11,
level: 'info',
prefix: 'info prefix',
message: 'x = {"foo":{"bar":"baz"}}',
},
{ id: 12,
level: 'timing',
prefix: 'timing prefix',
message: 'x = {"foo":{"bar":"baz"}}',
},
{ id: 13,
level: 'http',
prefix: 'http prefix',
message: 'x = {"foo":{"bar":"baz"}}',
},
{ id: 14,
level: 'notice',
prefix: 'notice prefix',
message: 'x = {"foo":{"bar":"baz"}}',
},
{ id: 15,
level: 'warn',
prefix: 'warn prefix',
message: 'x = {"foo":{"bar":"baz"}}',
},
{ id: 16,
level: 'error',
prefix: 'error prefix',
message: 'x = {"foo":{"bar":"baz"}}',
},
{ id: 17,
level: 'silent',
prefix: 'silent prefix',
message: 'x = {"foo":{"bar":"baz"}}',
},
{ id: 18,
level: 'silly',
prefix: 'silly prefix',
message: 'x = {"foo":{"bar":"baz"}}',
},
{ id: 19,
level: 'verbose',
prefix: 'verbose prefix',
message: 'x = {"foo":{"bar":"baz"}}',
},
{ id: 20,
level: 'info',
prefix: 'info prefix',
message: 'x = {"foo":{"bar":"baz"}}',
},
{ id: 21,
level: 'timing',
prefix: 'timing prefix',
message: 'x = {"foo":{"bar":"baz"}}',
},
{ id: 22,
level: 'http',
prefix: 'http prefix',
message: 'x = {"foo":{"bar":"baz"}}',
},
{ id: 23,
level: 'notice',
prefix: 'notice prefix',
message: 'x = {"foo":{"bar":"baz"}}',
},
{ id: 24,
level: 'warn',
prefix: 'warn prefix',
message: 'x = {"foo":{"bar":"baz"}}',
},
{ id: 25,
level: 'error',
prefix: 'error prefix',
message: 'x = {"foo":{"bar":"baz"}}',
},
{ id: 26,
level: 'silent',
prefix: 'silent prefix',
message: 'x = {"foo":{"bar":"baz"}}',
},
{ id: 27,
level: 'error',
prefix: '404',
message: 'This is a longer\nmessage, with some details\nand maybe a stack.\n',
},
{ id: 28,
level: 'noise',
prefix: '',
message: 'LOUD NOISES',
},
{ id: 29,
level: 'noise',
prefix: 'error',
message: 'erroring',
},
];
this.beforeEach(function () {
result = [];
logEvents = [];
logInfoEvents = [];
logPrefixEvents = [];
log = new Log();
s = new Stream();
s.write = (m) => result.push(m);
s.writable = true;
s.isTTY = true;
s.end = () => {};
log.stream = s;
log.heading = 'npm';
});
it('should work', function () {
log.stream.should.equal(s);
log.on('log', logEvents.push.bind(logEvents));
log.on('log.info', logInfoEvents.push.bind(logInfoEvents));
log.on('info prefix', logPrefixEvents.push.bind(logPrefixEvents));
console.error('log.level=silly');
log.level = 'silly';
log.silly('silly prefix', 'x = %j', { foo: { bar: 'baz' } });
log.verbose('verbose prefix', 'x = %j', { foo: { bar: 'baz' } });
log.info('info prefix', 'x = %j', { foo: { bar: 'baz' } });
log.timing('timing prefix', 'x = %j', { foo: { bar: 'baz' } });
log.http('http prefix', 'x = %j', { foo: { bar: 'baz' } });
log.notice('notice prefix', 'x = %j', { foo: { bar: 'baz' } });
log.warn('warn prefix', 'x = %j', { foo: { bar: 'baz' } });
log.error('error prefix', 'x = %j', { foo: { bar: 'baz' } });
log.silent('silent prefix', 'x = %j', { foo: { bar: 'baz' } });
console.error('log.level=silent');
log.level = 'silent';
log.silly('silly prefix', 'x = %j', { foo: { bar: 'baz' } });
log.verbose('verbose prefix', 'x = %j', { foo: { bar: 'baz' } });
log.info('info prefix', 'x = %j', { foo: { bar: 'baz' } });
log.timing('timing prefix', 'x = %j', { foo: { bar: 'baz' } });
log.http('http prefix', 'x = %j', { foo: { bar: 'baz' } });
log.notice('notice prefix', 'x = %j', { foo: { bar: 'baz' } });
log.warn('warn prefix', 'x = %j', { foo: { bar: 'baz' } });
log.error('error prefix', 'x = %j', { foo: { bar: 'baz' } });
log.silent('silent prefix', 'x = %j', { foo: { bar: 'baz' } });
console.error('log.level=info');
log.level = 'info';
log.silly('silly prefix', 'x = %j', { foo: { bar: 'baz' } });
log.verbose('verbose prefix', 'x = %j', { foo: { bar: 'baz' } });
log.info('info prefix', 'x = %j', { foo: { bar: 'baz' } });
log.timing('timing prefix', 'x = %j', { foo: { bar: 'baz' } });
log.http('http prefix', 'x = %j', { foo: { bar: 'baz' } });
log.notice('notice prefix', 'x = %j', { foo: { bar: 'baz' } });
log.warn('warn prefix', 'x = %j', { foo: { bar: 'baz' } });
log.error('error prefix', 'x = %j', { foo: { bar: 'baz' } });
log.silent('silent prefix', 'x = %j', { foo: { bar: 'baz' } });
log.error('404', 'This is a longer\n' +
'message, with some details\n' +
'and maybe a stack.\n');
log.addLevel('noise', 10000, { bell: true });
log.noise(false, 'LOUD NOISES');
log.noise('error', 'erroring');
result.join('').trim().should.equal(resultExpect.join('').trim());
const withoutTimestamps = (x) => x.map((m) => {
Boolean(m.timestamp).should.be.true;
const copy = JSON.parse(JSON.stringify(m));
delete copy.timestamp;
return copy;
});
withoutTimestamps(log.record).should.eql(logEventsExpect);
withoutTimestamps(logEvents).should.eql(logEventsExpect);
withoutTimestamps(logInfoEvents).should.eql(logInfoEventsExpect);
withoutTimestamps(logPrefixEvents).should.eql(logPrefixEventsExpect);
});
});
describe('utils', function () {
it('enableColor', function () {
log.enableColor();
log.useColor().should.be.true;
});
it('disableColor', function () {
log.disableColor();
log.useColor().should.be.false;
});
it('_buffer while paused', function () {
log.pause();
log.log('verbose', 'test', 'test log');
log._buffer.length.should.equal(1);
log.resume();
log._buffer.length.should.equal(0);
});
});
describe('log.log', function () {
beforeEach(function () {
log = new Log();
});
it('emits error on bad loglevel', async function() {
await new Promise((resolve, reject) => {
log.once('error', (err) => {
/Undefined log level: "asdf"/.test(err).should.be.true;
resolve();
});
log.log('asdf', 'bad loglevel');
setTimeout(reject, 1000);
});
});
it('resolves stack traces to a plain string', async function() {
await new Promise((resolve, reject) => {
log.once('log', (m) => {
/Error: with a stack trace/.test(m.message).should.be.true;
/at Test/.test(m.message).should.be.true;
resolve();
});
const err = new Error('with a stack trace');
log.log('verbose', 'oops', err);
setTimeout(reject, 1000);
});
});
it('max record size', function() {
log.maxRecordSize = 3;
log.log('verbose', 'test', 'log 1');
log.log('verbose', 'test', 'log 2');
log.log('verbose', 'test', 'log 3');
log.log('verbose', 'test', 'log 4');
log.record.length.should.equal(3);
});
});
describe('stream', function () {
beforeEach(function () {
log = new Log();
});
it('write with no stream', function() {
log.stream = null;
log.write('message');
});
});
describe('emitLog', function () {
beforeEach(function () {
log = new Log();
});
it('to nonexistant level', function() {
log.emitLog({ prefix: 'test', level: 'asdf' });
});
});
describe('format', function () {
beforeEach(function () {
log = new Log();
});
it('with nonexistant stream', function() {
log.stream = null;
(log._format('message') === undefined).should.be.true;
});
it('fg', function () {
log.enableColor();
const o = log._format('test message', { bg: 'blue' });
o.includes('\u001b[44mtest message\u001b[0m').should.be.true;
});
it('bg', function () {
log.enableColor();
const o = log._format('test message', { bg: 'white' });
o.includes('\u001b[47mtest message\u001b[0m').should.be.true;
});
it('bold', function () {
log.enableColor();
const o = log._format('test message', { bold: true });
o.includes('\u001b[1mtest message\u001b[0m').should.be.true;
});
it('underline', function () {
log.enableColor();
const o = log._format('test message', { underline: true });
o.includes('\u001b[4mtest message\u001b[0m').should.be.true;
});
it('inverse', function () {
log.enableColor();
const o = log._format('test message', { inverse: true });
o.includes('\u001b[7mtest message\u001b[0m').should.be.true;
});
});
describe('unleakString', function () {
it('should unleak a string', function () {
unleakString('yolo').should.eql('yolo');
});
it('should unleak a multiline string', function () {
unleakString(' yolo\nbolo ').should.eql(' yolo\nbolo ');
});
it('should convert an object to a string', function () {
for (const obj of [{}, null, undefined, [], 0]) {
unleakString(obj).should.eql(`${obj}`);
}
});
});
});
@@ -0,0 +1,34 @@
import { Log } from '../../lib/log';
import { waitForCondition } from 'asyncbox';
describe('display', function () {
let log;
describe('explicitly set new log level display to empty string', function () {
let actual;
beforeEach(function () {
actual = '';
log = new Log();
log.write = (msg) => {
actual += msg;
};
});
it('explicitly set new log level display to empty string', async function () {
log.addLevel('explicitNoLevelDisplayed', 20000, {}, '');
log.explicitNoLevelDisplayed('1', '2');
await waitForCondition(() => actual.trim() === '1 2', {waitMs: 1000, intervalMs: 50});
actual = '';
log.explicitNoLevelDisplayed('', '1');
await waitForCondition(() => actual.trim() === '1', {waitMs: 1000, intervalMs: 50});
});
it('explicitly set new log level display to 0', async function () {
log.addLevel('explicitNoLevelDisplayed', 20000, {}, 0);
log.explicitNoLevelDisplayed('', '1');
await waitForCondition(() => actual.trim() === '0 1', {waitMs: 1000, intervalMs: 50});
});
});
});
@@ -1,7 +1,7 @@
import {SecureValuesPreprocessor} from '../../lib/log-internal';
import {SecureValuesPreprocessor} from '../../lib/secure-values-preprocessor';
describe('Log Internals', function () {
/** @type {import('../../lib/log-internal').SecureValuesPreprocessor} */
/** @type {import('../../lib/secure-values-prepreocessor').SecureValuesPreprocessor} */
let preprocessor;
beforeEach(function () {
+14
View File
@@ -0,0 +1,14 @@
{
"extends": "@appium/tsconfig/tsconfig.json",
"compilerOptions": {
"esModuleInterop": true,
"strict": false,
"outDir": "build",
"types": ["node"],
"checkJs": true
},
"include": [
"index.ts",
"lib"
]
}
+57
View File
@@ -3,6 +3,63 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [0.3.38](https://github.com/appium/appium/compare/@appium/plugin-test-support@0.3.37...@appium/plugin-test-support@0.3.38) (2024-06-11)
**Note:** Version bump only for package @appium/plugin-test-support
## [0.3.37](https://github.com/appium/appium/compare/@appium/plugin-test-support@0.3.36...@appium/plugin-test-support@0.3.37) (2024-06-11)
**Note:** Version bump only for package @appium/plugin-test-support
## [0.3.36](https://github.com/appium/appium/compare/@appium/plugin-test-support@0.3.35...@appium/plugin-test-support@0.3.36) (2024-06-11)
**Note:** Version bump only for package @appium/plugin-test-support
## [0.3.35](https://github.com/appium/appium/compare/@appium/plugin-test-support@0.3.34...@appium/plugin-test-support@0.3.35) (2024-06-10)
**Note:** Version bump only for package @appium/plugin-test-support
## [0.3.34](https://github.com/appium/appium/compare/@appium/plugin-test-support@0.3.33...@appium/plugin-test-support@0.3.34) (2024-06-06)
**Note:** Version bump only for package @appium/plugin-test-support
## [0.3.33](https://github.com/appium/appium/compare/@appium/plugin-test-support@0.3.32...@appium/plugin-test-support@0.3.33) (2024-06-06)
**Note:** Version bump only for package @appium/plugin-test-support
## [0.3.32](https://github.com/appium/appium/compare/@appium/plugin-test-support@0.3.31...@appium/plugin-test-support@0.3.32) (2024-06-06)
### Bug Fixes
* **support:** update dependency teen_process to v2.1.4 ([96a7a3f](https://github.com/appium/appium/commit/96a7a3f23bead7c1d18700550138cd2d70ce5e3c))
## [0.3.31](https://github.com/appium/appium/compare/@appium/plugin-test-support@0.3.30...@appium/plugin-test-support@0.3.31) (2024-05-27)
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@appium/plugin-test-support",
"version": "0.3.31",
"version": "0.3.38",
"description": "Testing utilities for Appium plugins",
"keywords": [
"automation",
@@ -41,11 +41,11 @@
"test:unit": "exit 0"
},
"dependencies": {
"@appium/types": "^0.18.0",
"@appium/types": "^0.20.3",
"get-port": "5.1.1",
"log-symbols": "4.1.0",
"source-map-support": "0.5.21",
"teen_process": "2.1.3"
"teen_process": "2.1.4"
},
"peerDependencies": {
"appium": "^2.0.0-beta.43",
+18
View File
@@ -3,6 +3,24 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [0.6.1](https://github.com/appium/appium/compare/@appium/schema@0.6.0...@appium/schema@0.6.1) (2024-06-11)
### Bug Fixes
* **schema:** edit a comment in order to trigger a publish ([45cc948](https://github.com/appium/appium/commit/45cc9486711b6100e78317c9dd7fb4f65a41d1eb))
## [0.6.0](https://github.com/appium/appium/compare/@appium/schema@0.5.0...@appium/schema@0.6.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))
## [0.5.0](https://github.com/appium/appium/compare/@appium/schema@0.4.2...@appium/schema@0.5.0) (2023-12-18)
+17 -5
View File
@@ -1,5 +1,5 @@
/**
* The single-source-of-truth of the Appium server configuration
* The single-source-of-truth of the Appium server configuration.
*
* This defines _both_ what the CLI supports and what the config files support.
*/
@@ -171,6 +171,14 @@ export const AppiumConfigJsonSchema = /** @type {const} */ ({
title: 'log-level config',
type: 'string',
},
'log-format': {
appiumCliDest: 'logFormat',
default: 'text',
description: 'Log format (text|json|pretty_json)',
enum: ['text', 'json', 'pretty_json'],
title: 'log-format config',
type: 'string',
},
'log-no-colors': {
default: false,
description: 'Do not use color in console output',
@@ -185,13 +193,15 @@ export const AppiumConfigJsonSchema = /** @type {const} */ ({
},
'plugins-import-chunk-size': {
default: 7,
description: 'The maximum amount of plugins that could be imported in parallel on server startup',
description:
'The maximum amount of plugins that could be imported in parallel on server startup',
title: 'plugins-import-chunk-size config',
type: 'number',
},
'drivers-import-chunk-size': {
default: 3,
description: 'The maximum amount of drivers that could be imported in parallel on server startup',
description:
'The maximum amount of drivers that could be imported in parallel on server startup',
title: 'drivers-import-chunk-size config',
type: 'number',
},
@@ -301,13 +311,15 @@ export const AppiumConfigJsonSchema = /** @type {const} */ ({
type: 'string',
},
'ssl-cert-path': {
description: 'Full path to the .cert file if TLS is used. Must be provided together with "ssl-key-path"',
description:
'Full path to the .cert file if TLS is used. Must be provided together with "ssl-key-path"',
title: '.cert file path',
appiumCliDest: 'sslCertificatePath',
type: 'string',
},
'ssl-key-path': {
description: 'Full path to the .key file if TLS is used. Must be provided together with "ssl-cert-path"',
description:
'Full path to the .key file if TLS is used. Must be provided together with "ssl-cert-path"',
title: '.key file path',
appiumCliDest: 'sslKeyPath',
type: 'string',
@@ -173,6 +173,18 @@
"title": "log-level config",
"type": "string"
},
"log-format": {
"appiumCliDest": "logFormat",
"default": "text",
"description": "Log format (text|json|pretty_json)",
"enum": [
"text",
"json",
"pretty_json"
],
"title": "log-format config",
"type": "string"
},
"log-no-colors": {
"default": false,
"description": "Do not use color in console output",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@appium/schema",
"version": "0.5.0",
"version": "0.6.1",
"description": "Appium Configuration Schema",
"keywords": [
"automation",
+93
View File
@@ -3,6 +3,99 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [5.0.3](https://github.com/appium/appium/compare/@appium/support@5.0.2...@appium/support@5.0.3) (2024-06-11)
**Note:** Version bump only for package @appium/support
## [5.0.2](https://github.com/appium/appium/compare/@appium/support@5.0.1...@appium/support@5.0.2) (2024-06-11)
**Note:** Version bump only for package @appium/support
## [5.0.1](https://github.com/appium/appium/compare/@appium/support@5.0.0...@appium/support@5.0.1) (2024-06-11)
**Note:** Version bump only for package @appium/support
## [5.0.0](https://github.com/appium/appium/compare/@appium/support@4.5.0...@appium/support@5.0.0) (2024-06-10)
### ⚠ BREAKING CHANGES
* **support:** Remove unleakString since it is now in appium/logger
* **support:** Remove patchLogger exported method from logger
* **support:** Removed the 'log-internals' module
* **support:** Moved the 'loadSecureValuesPreprocessingRules' API to the '@appium/logger' package
### 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)
* **support:** Move SecureValuesPreprocessor to @appum/logger ([#20228](https://github.com/appium/appium/issues/20228)) ([dbc3b66](https://github.com/appium/appium/commit/dbc3b668a0a7a815d23f1cae4207d435fc09034d))
* **support:** Remove patchLogger from logger ([#20220](https://github.com/appium/appium/issues/20220)) ([dd421b3](https://github.com/appium/appium/commit/dd421b397b41cf4d5ed5a434edf72701f5936bb1))
* **support:** remove unleakString as no usage ([#20221](https://github.com/appium/appium/issues/20221)) ([93c271e](https://github.com/appium/appium/commit/93c271ec5d7ffc3f68b3ced3d15170387f9ab6c9))
### Bug Fixes
* **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))
## [4.5.0](https://github.com/appium/appium/compare/@appium/support@4.4.0...@appium/support@4.5.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)
## [4.4.0](https://github.com/appium/appium/compare/@appium/support@4.3.0...@appium/support@4.4.0) (2024-06-06)
### Features
* **logger:** Add the 'debug' level to the default logger ([#20203](https://github.com/appium/appium/issues/20203)) ([7fd9d5f](https://github.com/appium/appium/commit/7fd9d5f6261b385c234580c2bfee4d576905458b))
### Bug Fixes
* add logger dependencies in package.json ([#20205](https://github.com/appium/appium/issues/20205)) ([3fbc4f1](https://github.com/appium/appium/commit/3fbc4f1fe07eadf9c7a2ef8fc9f4ba78dc3486a3))
## [4.3.0](https://github.com/appium/appium/compare/@appium/support@4.2.6...@appium/support@4.3.0) (2024-06-06)
### Features
* **appium:** Replace npmlog with the local fork ([#20190](https://github.com/appium/appium/issues/20190)) ([8915934](https://github.com/appium/appium/commit/8915934270243bfb46c4d104a098ce1cc481b0ff))
### Bug Fixes
* **support:** Make sure response stream is defined while stopping it ([#20189](https://github.com/appium/appium/issues/20189)) ([4fd6893](https://github.com/appium/appium/commit/4fd68931465dd2ed3490bcebad7ccff3dacc9b3a))
* **support:** update dependency @types/which to v3 ([#20164](https://github.com/appium/appium/issues/20164)) ([57b0406](https://github.com/appium/appium/commit/57b0406a33bc687c731f9e7fedd56e1b07c3cc67))
* **support:** update dependency teen_process to v2.1.4 ([96a7a3f](https://github.com/appium/appium/commit/96a7a3f23bead7c1d18700550138cd2d70ce5e3c))
* **types:** update dependency type-fest to v4.19.0 ([#20193](https://github.com/appium/appium/issues/20193)) ([db62358](https://github.com/appium/appium/commit/db62358dbac25de2a75fdc7493338d98b1422c84))
## [4.2.6](https://github.com/appium/appium/compare/@appium/support@4.2.5...@appium/support@4.2.6) (2024-05-27)
-1
View File
@@ -51,7 +51,6 @@ All utility functions are split into a bunch of different categories. Each categ
|env|Several helpers needed by the server to cope with internal dependencies and manifests|
|fs|Most of the functions here are just thin wrappers over utility functions available in [Promises API](https://nodejs.org/api/fs.html#promises-api)|
|image-util|Utilities to work with images. Use [sharp](https://github.com/lovell/sharp) under the hood.<br>:bangbang: Node >=18.17 is required to use these utilities|
|log-internal|Utilities needed for internal Appium log config assistance|
|logging|See [the logging section below](#logging)|
|mjpeg|Helpers needed to implement [MJPEG streaming](https://en.wikipedia.org/wiki/Motion_JPEG#Video_streaming)|
|net|Helpers needed for network interactions, for example, upload and download of files|
-1
View File
@@ -80,4 +80,3 @@ export type {
ZipCompressionOptions,
ZipSourceOptions,
} from './zip';
export type {SecureValuePreprocessingRule} from './log-internal';
+74 -114
View File
@@ -1,84 +1,61 @@
// @ts-check
import npmlog from 'npmlog';
import globalLog from '@appium/logger';
import _ from 'lodash';
import {unleakString} from './util';
import moment from 'moment';
import SECURE_VALUES_PREPROCESSOR from './log-internal';
/** @type {import('@appium/types').AppiumLoggerLevel[]} */
export const LEVELS = ['silly', 'verbose', 'debug', 'info', 'http', 'warn', 'error'];
const MAX_LOG_RECORDS_COUNT = 3000;
const PREFIX_TIMESTAMP_FORMAT = 'HH-mm-ss:SSS';
// mock log object used in testing mode
let mockLog = {};
for (let level of LEVELS) {
mockLog[level] = () => {};
}
// export a default logger with no prefix
export const log = getLogger();
// mock log object is used in testing mode to silence the output
const MOCK_LOG = {
unwrap: () => ({
loadSecureValuesPreprocessingRules: () => ({
issues: [],
rules: [],
}),
level: 'verbose',
prefix: '',
log: _.noop,
}),
...(_.fromPairs(LEVELS.map((l) => [l, _.noop]))),
};
/**
*
* @param {import('npmlog').Logger} logger
*/
function patchLogger(logger) {
if (!logger.debug) {
logger.addLevel('debug', 1000, {fg: 'blue', bg: 'black'}, 'dbug');
}
}
/**
*
* @returns {[import('npmlog').Logger, boolean]}
*/
function _getLogger() {
// check if the user set the `_TESTING` or `_FORCE_LOGS` flag
const testingMode = process.env._TESTING === '1';
const forceLogMode = process.env._FORCE_LOGS === '1';
// if is possible that there is a logger instance that is already around,
// in which case we want t o use that
const usingGlobalLog = !!global._global_npmlog;
let logger;
if (testingMode && !forceLogMode) {
// in testing mode, use a mock logger object that we can query
logger = mockLog;
} else {
// otherwise, either use the global, or a new `npmlog` object
logger = global._global_npmlog || npmlog;
// The default value is 10000, which causes excessive memory usage
logger.maxRecordSize = MAX_LOG_RECORDS_COUNT;
}
patchLogger(logger);
return [logger, usingGlobalLog];
}
/**
* @param {AppiumLoggerPrefix?} prefix
* @param {boolean} logTimestamp whether to include timestamps into log prefixes
* @returns {string}
*/
function getActualPrefix(prefix, logTimestamp = false) {
const result = (_.isFunction(prefix) ? prefix() : prefix) ?? '';
return logTimestamp ? `[${moment().format(PREFIX_TIMESTAMP_FORMAT)}] ${result}` : result;
}
/**
*
* @param {AppiumLoggerPrefix?} prefix
* @param {AppiumLoggerPrefix?} [prefix=null]
* @returns {AppiumLogger}
*/
function getLogger(prefix = null) {
let [logger, usingGlobalLog] = _getLogger();
export function getLogger(prefix = null) {
const [logger, usingGlobalLog] = _getLogger();
// wrap the logger so that we can catch and modify any logging
let wrappedLogger = {
const wrappedLogger = {
unwrap: () => logger,
levels: LEVELS,
prefix,
/**
* Logs given arguments at the error level and returns
* the error object.
*
* @param {...any} args
* @returns {Error}
*/
errorWithException (/** @type {any[]} */ ...args) {
this.error(...args);
// make sure we have an `Error` object. Wrap if necessary
return _.isError(args[0]) ? args[0] : new Error(args.join('\n'));
},
/**
* @deprecated Use {@link errorWithException} instead
* @param {...any} args
* @throws {Error}
*/
errorAndThrow (/** @type {any[]} */ ...args) {
throw this.errorWithException(args);
},
};
// allow access to the level of the underlying logger
Object.defineProperty(wrappedLogger, 'level', {
get() {
@@ -90,35 +67,15 @@ function getLogger(prefix = null) {
enumerable: true,
configurable: true,
});
const logTimestamp = process.env._LOG_TIMESTAMP === '1';
const isDebugTimestampLoggingEnabled = process.env._LOG_TIMESTAMP === '1';
// add all the levels from `npmlog`, and map to the underlying logger
for (const level of LEVELS) {
wrappedLogger[level] = /** @param {...any} args */ function (...args) {
const actualPrefix = getActualPrefix(this.prefix, logTimestamp);
for (const arg of args) {
const out = _.isError(arg) && arg.stack ? arg.stack : `${arg}`;
for (const line of out.split('\n')) {
// it is necessary to unleak each line because `split` call
// creates "views" to the original string as well as the `substring` one
const unleakedLine = unleakString(line);
logger[level](actualPrefix, SECURE_VALUES_PREPROCESSOR.preprocess(unleakedLine));
}
}
wrappedLogger[level] = /** @param {any[]} args */ function (...args) {
const finalPrefix = getFinalPrefix(this.prefix, isDebugTimestampLoggingEnabled);
// @ts-ignore This is OK
logger[level](finalPrefix, ...args);
};
}
wrappedLogger.errorWithException = function (/** @type {any[]} */ ...args) {
this.error(...args);
// make sure we have an `Error` object. Wrap if necessary
return _.isError(args[0]) ? args[0] : new Error(args.map(unleakString).join('\n'));
};
/**
* @deprecated Use {@link errorWithException} instead
*/
wrappedLogger.errorAndThrow = function (/** @type {any[]} */ ...args) {
throw this.errorWithException(args);
};
if (!usingGlobalLog) {
// if we're not using a global log specified from some top-level package,
// set the log level to a default of verbose. Otherwise, let the top-level
@@ -129,37 +86,40 @@ function getLogger(prefix = null) {
}
/**
* @typedef LoadResult
* @property {string[]} issues The list of rule parsing issues (one item per rule).
* Rules with issues are skipped. An empty list is returned if no parsing issues exist.
* @property {import('./log-internal').SecureValuePreprocessingRule[]} rules The list of successfully loaded
* replacement rules. The list could be empty if no rules were loaded.
*/
/**
* Loads the JSON file containing secure values replacement rules.
* This might be necessary to hide sensitive values that may possibly
* appear in Appium logs.
* Each call to this method replaces the previously loaded rules if any existed.
*
* @param {string|string[]|import('@appium/types').LogFiltersConfig} rulesJsonPath The full path to the JSON file containing
* the replacement rules. Each rule could either be a string to be replaced
* or an object with predefined properties.
* @throws {Error} If the given file cannot be loaded
* @returns {Promise<LoadResult>}
* @returns {[import('@appium/logger').Logger, boolean]}
*/
async function loadSecureValuesPreprocessingRules(rulesJsonPath) {
const issues = await SECURE_VALUES_PREPROCESSOR.loadRules(rulesJsonPath);
return {
issues,
rules: _.cloneDeep(SECURE_VALUES_PREPROCESSOR.rules),
};
function _getLogger() {
// check if the user set the `_TESTING` or `_FORCE_LOGS` flag
const testingMode = process.env._TESTING === '1';
const forceLogMode = process.env._FORCE_LOGS === '1';
// if is possible that there is a logger instance that is already around,
// in which case we want t o use that
const useGlobalLog = !!global._global_npmlog;
const logger = testingMode && !forceLogMode
// in testing mode, use a mock logger object that we can query
? MOCK_LOG
// otherwise, either use the global, or a new `npmlog` object
: (global._global_npmlog || globalLog);
// The default value is 10000, which causes excessive memory usage
logger.maxRecordSize = MAX_LOG_RECORDS_COUNT;
return [logger, useGlobalLog];
}
// export a default logger with no prefix
const log = getLogger();
/**
* @param {AppiumLoggerPrefix?} prefix
* @param {boolean} [shouldLogTimestamp=false] whether to include timestamps into log prefixes
* @returns {string}
*/
function getFinalPrefix(prefix, shouldLogTimestamp = false) {
const result = (_.isFunction(prefix) ? prefix() : prefix) ?? '';
if (!shouldLogTimestamp) {
return result;
}
const formattedTimestamp = `[${moment().format(PREFIX_TIMESTAMP_FORMAT)}]`;
return result ? `${formattedTimestamp} ${result}` : formattedTimestamp;
}
export {log, patchLogger, getLogger, loadSecureValuesPreprocessingRules};
export default log;
/**
+8 -6
View File
@@ -172,13 +172,15 @@ class MJpegStream extends Writable {
* the HTTP request itself. Then reset the state.
*/
stop() {
if (!this.consumer) {
return;
if (this.consumer) {
this.consumer.unpipe(this);
}
if (this.responseStream) {
if (this.consumer) {
this.responseStream.unpipe(this.consumer);
}
this.responseStream.destroy();
}
this.responseStream.unpipe(this.consumer);
this.consumer.unpipe(this);
this.responseStream.destroy();
this.clear();
}
-13
View File
@@ -358,18 +358,6 @@ function quote(args) {
return shellQuote(_.castArray(args));
}
/**
* This function is necessary to workaround unexpected memory leaks
* caused by NodeJS string interning
* behavior described in https://bugs.chromium.org/p/v8/issues/detail?id=2869
*
* @param {any} s - The string to unleak
* @return {string} Either the unleaked string or the original object converted to string
*/
function unleakString(s) {
return ` ${s}`.substring(1);
}
/**
* @typedef PluralizeOptions
* @property {boolean} [inclusive=false] - Whether to prefix with the number (e.g., 3 ducks)
@@ -555,7 +543,6 @@ export {
compareVersions,
coerceVersion,
quote,
unleakString,
jsonStringify,
pluralize,
GiB,
+6 -7
View File
@@ -1,6 +1,6 @@
{
"name": "@appium/support",
"version": "4.2.6",
"version": "5.0.3",
"description": "Support libs used across appium packages",
"keywords": [
"automation",
@@ -41,8 +41,9 @@
"test:unit": "mocha \"./test/unit/**/*.spec.js\""
},
"dependencies": {
"@appium/logger": "^1.4.2",
"@appium/tsconfig": "^0.3.3",
"@appium/types": "^0.18.0",
"@appium/types": "^0.20.3",
"@colors/colors": "1.6.0",
"@types/archiver": "6.0.2",
"@types/base64-stream": "1.0.5",
@@ -52,14 +53,13 @@
"@types/lockfile": "1.0.4",
"@types/mv": "2.1.4",
"@types/ncp": "2.0.8",
"@types/npmlog": "7.0.0",
"@types/pluralize": "0.0.33",
"@types/semver": "7.5.8",
"@types/shell-quote": "1.7.5",
"@types/supports-color": "8.1.3",
"@types/teen_process": "2.0.4",
"@types/uuid": "9.0.8",
"@types/which": "3.0.3",
"@types/which": "3.0.4",
"archiver": "7.0.1",
"axios": "1.7.2",
"base64-stream": "1.0.0",
@@ -77,7 +77,6 @@
"moment": "2.30.1",
"mv": "2.1.1",
"ncp": "2.0.0",
"npmlog": "7.0.1",
"opencv-bindings": "4.5.5",
"pkg-dir": "5.0.0",
"plist": "3.1.0",
@@ -89,8 +88,8 @@
"shell-quote": "1.8.1",
"source-map-support": "0.5.21",
"supports-color": "8.1.1",
"teen_process": "2.1.3",
"type-fest": "4.18.3",
"teen_process": "2.1.4",
"type-fest": "4.20.0",
"uuid": "9.0.1",
"which": "4.0.0",
"yauzl": "3.1.3"
-14
View File
@@ -503,20 +503,6 @@ describe('util', function () {
});
});
describe('unleakString', function () {
it('should unleak a string', function () {
util.unleakString('yolo').should.eql('yolo');
});
it('should unleak a multiline string', function () {
util.unleakString(' yolo\nbolo ').should.eql(' yolo\nbolo ');
});
it('should convert an object to a string', function () {
for (const obj of [{}, null, undefined, [], 0]) {
util.unleakString(obj).should.eql(`${obj}`);
}
});
});
describe('pluralize', function () {
/*
* The pluralize library (https://github.com/blakeembrey/pluralize)
+56
View File
@@ -3,6 +3,62 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [3.0.43](https://github.com/appium/appium/compare/@appium/test-support@3.0.42...@appium/test-support@3.0.43) (2024-06-11)
**Note:** Version bump only for package @appium/test-support
## [3.0.42](https://github.com/appium/appium/compare/@appium/test-support@3.0.41...@appium/test-support@3.0.42) (2024-06-11)
**Note:** Version bump only for package @appium/test-support
## [3.0.41](https://github.com/appium/appium/compare/@appium/test-support@3.0.40...@appium/test-support@3.0.41) (2024-06-11)
**Note:** Version bump only for package @appium/test-support
## [3.0.40](https://github.com/appium/appium/compare/@appium/test-support@3.0.39...@appium/test-support@3.0.40) (2024-06-10)
**Note:** Version bump only for package @appium/test-support
## [3.0.39](https://github.com/appium/appium/compare/@appium/test-support@3.0.38...@appium/test-support@3.0.39) (2024-06-06)
**Note:** Version bump only for package @appium/test-support
## [3.0.38](https://github.com/appium/appium/compare/@appium/test-support@3.0.37...@appium/test-support@3.0.38) (2024-06-06)
**Note:** Version bump only for package @appium/test-support
## [3.0.37](https://github.com/appium/appium/compare/@appium/test-support@3.0.36...@appium/test-support@3.0.37) (2024-06-06)
**Note:** Version bump only for package @appium/test-support
## [3.0.36](https://github.com/appium/appium/compare/@appium/test-support@3.0.35...@appium/test-support@3.0.36) (2024-05-27)
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@appium/test-support",
"version": "3.0.36",
"version": "3.0.43",
"description": "A collection of test utilities used across Appium packages",
"keywords": [
"automation",
@@ -45,7 +45,7 @@
"test:unit": "mocha \"./test/unit/**/*.spec.js\""
},
"dependencies": {
"@appium/support": "^4.2.6",
"@appium/support": "^5.0.3",
"@colors/colors": "1.6.0",
"bluebird": "3.7.2",
"lodash": "4.17.21",
+69
View File
@@ -3,6 +3,75 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [0.20.3](https://github.com/appium/appium/compare/@appium/types@0.20.2...@appium/types@0.20.3) (2024-06-11)
**Note:** Version bump only for package @appium/types
## [0.20.2](https://github.com/appium/appium/compare/@appium/types@0.20.1...@appium/types@0.20.2) (2024-06-11)
**Note:** Version bump only for package @appium/types
## [0.20.1](https://github.com/appium/appium/compare/@appium/types@0.20.0...@appium/types@0.20.1) (2024-06-11)
**Note:** Version bump only for package @appium/types
## [0.20.0](https://github.com/appium/appium/compare/@appium/types@0.19.2...@appium/types@0.20.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))
### Bug Fixes
* **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))
## [0.19.2](https://github.com/appium/appium/compare/@appium/types@0.19.1...@appium/types@0.19.2) (2024-06-06)
**Note:** Version bump only for package @appium/types
## [0.19.1](https://github.com/appium/appium/compare/@appium/types@0.19.0...@appium/types@0.19.1) (2024-06-06)
### Bug Fixes
* add logger dependencies in package.json ([#20205](https://github.com/appium/appium/issues/20205)) ([3fbc4f1](https://github.com/appium/appium/commit/3fbc4f1fe07eadf9c7a2ef8fc9f4ba78dc3486a3))
## [0.19.0](https://github.com/appium/appium/compare/@appium/types@0.18.0...@appium/types@0.19.0) (2024-06-06)
### Features
* **appium:** Replace npmlog with the local fork ([#20190](https://github.com/appium/appium/issues/20190)) ([8915934](https://github.com/appium/appium/commit/8915934270243bfb46c4d104a098ce1cc481b0ff))
### Bug Fixes
* **types:** update dependency type-fest to v4.19.0 ([#20193](https://github.com/appium/appium/issues/20193)) ([db62358](https://github.com/appium/appium/commit/db62358dbac25de2a75fdc7493338d98b1422c84))
## [0.18.0](https://github.com/appium/appium/compare/@appium/types@0.17.0...@appium/types@0.18.0) (2024-05-27)
+5
View File
@@ -93,6 +93,10 @@ export type LogLevelConfig =
| "debug:info"
| "debug:warn"
| "debug:error";
/**
* Log format (text|json|pretty_json)
*/
export type LogFormatConfig = "text" | "json" | "pretty_json";
/**
* Do not use color in console output
*/
@@ -191,6 +195,7 @@ export interface ServerConfig {
log?: LogConfig;
"log-filters"?: LogFiltersConfig;
"log-level"?: LogLevelConfig;
"log-format"?: LogFormatConfig;
"log-no-colors"?: LogNoColorsConfig;
"log-timestamp"?: LogTimestampConfig;
"plugins-import-chunk-size"?: PluginsImportChunkSizeConfig;
+4 -7
View File
@@ -1,4 +1,4 @@
import type {Logger} from 'npmlog';
import type {Logger} from '@appium/logger';
/**
* A log prefix for {@linkcode AppiumLogger}
@@ -9,19 +9,16 @@ export type AppiumLoggerPrefix = string | (() => string);
/**
* Possible "log levels" for {@linkcode AppiumLogger}.
*
* Extracted from `npmlog`.
*/
export type AppiumLoggerLevel = 'silly' | 'verbose' | 'debug' | 'info' | 'http' | 'warn' | 'error';
/**
* Describes the `npmlog`-based internal logger.
*
* @see https://npm.im/npmlog
* Describes the internal logger.
*/
export interface AppiumLogger {
/**
* Returns the underlying `npmlog` {@link Logger}.
* Returns the underlying `logger` {@link Logger}.
* ! This method is designed for private usage.
*/
unwrap(): Logger;
level: AppiumLoggerLevel;
+4 -4
View File
@@ -1,6 +1,6 @@
{
"name": "@appium/types",
"version": "0.18.0",
"version": "0.20.3",
"description": "Various type declarations used across Appium",
"keywords": [
"automation",
@@ -38,12 +38,12 @@
"test:types": "tsd"
},
"dependencies": {
"@appium/schema": "^0.5.0",
"@appium/logger": "^1.4.2",
"@appium/schema": "^0.6.1",
"@appium/tsconfig": "^0.3.3",
"@types/express": "4.17.21",
"@types/npmlog": "7.0.0",
"@types/ws": "8.5.10",
"type-fest": "4.18.3"
"type-fest": "4.20.0"
},
"engines": {
"node": "^14.17.0 || ^16.13.0 || >=18.0.0",
@@ -3,6 +3,15 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.0.20](https://github.com/appium/appium-plugins/compare/@appium/universal-xml-plugin@1.0.19...@appium/universal-xml-plugin@1.0.20) (2024-06-06)
### Bug Fixes
* **universal-xml-plugin:** add checks to disable the plguin in webview ([#20179](https://github.com/appium/appium-plugins/issues/20179)) ([7492c33](https://github.com/appium/appium-plugins/commit/7492c3357e72b76d301924fdf35a42d228c09175))
## [1.0.19](https://github.com/appium/appium-plugins/compare/@appium/universal-xml-plugin@1.0.18...@appium/universal-xml-plugin@1.0.19) (2024-05-27)
+4 -4
View File
@@ -23,7 +23,7 @@ export default class UniversalXMLPlugin extends BasePlugin {
`The XML mapper found ${unknowns.nodes.length} node(s) / ` +
`tag name(s) that it didn't know about. These should be ` +
`reported to improve the quality of the plugin: ` +
unknowns.nodes.join(', ')
unknowns.nodes.join(', '),
);
}
if (unknowns.attrs.length) {
@@ -31,7 +31,7 @@ export default class UniversalXMLPlugin extends BasePlugin {
`The XML mapper found ${unknowns.attrs.length} attributes ` +
`that it didn't know about. These should be reported to ` +
`improve the quality of the plugin: ` +
unknowns.attrs.join(', ')
unknowns.attrs.join(', '),
);
}
return xml;
@@ -47,7 +47,7 @@ export default class UniversalXMLPlugin extends BasePlugin {
async _find(multiple, next, driver, strategy, selector) {
const {platformName} = driver.caps;
if (strategy.toLowerCase() !== 'xpath') {
if (strategy.toLowerCase() !== 'xpath' || (await driver.getCurrentContext()) !== 'NATIVE_APP') {
return await next();
}
const xml = await this.getPageSource(null, driver, null, true);
@@ -58,7 +58,7 @@ export default class UniversalXMLPlugin extends BasePlugin {
if (newSelector === null) {
log.warn(
`Selector was not able to be translated to underlying XML. Either the requested ` +
`element does not exist or there was an error in translation`
`element does not exist or there was an error in translation`,
);
if (multiple) {
return [];

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