Compare commits

...
Author SHA1 Message Date
Jonathan Lipps 1d16ac6b45 chore: publish
- appium@2.13.1
 - @appium/base-driver@9.13.1
 - @appium/base-plugin@2.2.50
 - @appium/docutils@1.0.26
 - @appium/driver-test-support@0.7.1
 - @appium/fake-driver@5.5.7
 - @appium/fake-plugin@3.1.54
 - @appium/images-plugin@3.0.24
 - @appium/plugin-test-support@0.3.46
 - @appium/support@6.0.0
 - @appium/test-support@3.1.0
 - @appium/types@0.22.3
 - @appium/universal-xml-plugin@1.0.26
2024-12-05 12:22:35 -08:00
Kazuaki Matsuo 6ea0830319 fix(base-driver): add * always for non-separator for backward compatibility (#20821)
* fix(base-driver): add * always for non-separator for backward compatibility

* simplify

* revert const

* add todo comment

* revert const
2024-12-05 11:49:55 -08:00
renovate[bot] 228b091edc fix(universal-xml-plugin): update dependency @xmldom/xmldom to v0.9.6 (#20819)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-05 11:10:58 +02:00
Mykola Mokhnach 81a5b57673 feat(test-support): Remove obsolete helpers (#20817) 2024-12-04 22:12:39 +01:00
Edgars Eglītis 8d94f48bc5 chore(github): adjust newlines in bug issue form (#20818) 2024-12-04 22:00:02 +02:00
Edgars Eglītis f03468a153 chore(github): update issue templates (#20816)
* chore(github): update issue templates

* chore: add back checkbox for latest component updates
2024-12-04 21:37:17 +02:00
renovate[bot] 6a78aa9cea chore(deps): update dependency eslint-import-resolver-typescript to v3.7.0 (#20815)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-04 14:00:00 +00:00
Edgars Eglītis 9c5407ce64 ci: add workflow to validate PR title (#20813)
* ci: add workflow to validate PR title

* ci: rerun job if title was edited
2024-12-04 13:11:34 +02:00
Mykola Mokhnach 2a99e43b88 docs(appium): Fix HTML formatting (#20814) 2024-12-04 12:05:57 +01:00
Edgars Eglītis dca88624a4 docs: update aggregate changelog (#20812) 2024-12-04 13:03:42 +02:00
renovate[bot] 69f100ad3e fix(support): update dependency axios to v1.7.9 (#20811)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-04 09:16:44 +01:00
renovate[bot] f5e708ac42 chore(deps): update dependency prettier to v3.4.2 (#20810)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-04 09:16:33 +01:00
renovate[bot] c91683f50f fix(support): update dependency teen_process to v2.2.2 (#20808)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-04 08:58:50 +01:00
Mykola Mokhnach 4a2bfe9418 docs(appium): Enable the documentation to be translated via Crowdin (#20800) 2024-12-04 08:33:05 +01:00
renovate[bot] 3008c0d7be chore(deps): update dependency mocha to v11 (#20798)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-04 08:32:08 +01:00
Mykola Mokhnach 9a3f990739 feat: Remove @appium/doctor from the monorepo (#20805) 2024-12-04 08:31:32 +01:00
Kazuaki Matsuo 16c3ae25ea chore(support): revert #20797 as a backward compatibility (#20801)
BREAKING CHANGE: revert #20797 which was used for a backward compatibility
2024-12-03 09:54:58 -08:00
renovate[bot] 93fa5ccb06 fix(docutils): update dependency lilconfig to v3.1.3 (#20803)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-03 17:06:00 +00:00
renovate[bot] 8590432955 fix(types): update dependency type-fest to v4.30.0 (#20802)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-03 14:27:51 +02:00
renovate[bot] 42ef4841f9 fix(base-driver): update dependency @types/method-override to v3 (#20799)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-03 09:23:00 +02:00
Jonathan Lipps 8daf5e123a chore: publish
- appium@2.13.0
 - @appium/base-driver@9.13.0
 - @appium/base-plugin@2.2.49
 - @appium/doctor@2.1.15
 - @appium/docutils@1.0.25
 - @appium/driver-test-support@0.7.0
 - @appium/fake-plugin@3.1.53
 - @appium/images-plugin@3.0.23
 - @appium/plugin-test-support@0.3.45
 - @appium/support@5.1.8
 - @appium/test-support@3.0.53
 - @appium/types@0.22.2
2024-12-02 15:12:18 -08:00
Kazuaki Matsuo 3108a162ff fix{support}: keep backward compatibility for https://github.com/appium/appium/pull/2078 (#20797) 2024-12-02 14:52:40 -08:00
Mykola Mokhnach 942057d26c feat(base-driver): Allow to prefix feature names with automation names (#20793) 2024-12-02 10:51:41 +01:00
renovate[bot] 6ba31fe576 fix(types): update dependency type-fest to v4.29.1 (#20795)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-02 08:40:08 +02:00
renovate[bot] 7abf277ea3 chore(docutils): update dependency mkdocs-material to v9.5.47 (#20794)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-01 12:29:15 +00:00
Mykola Mokhnach 9aa9807b90 chore(driver-test-support): Remove stoppable utils (#20791) 2024-11-29 21:30:34 +01:00
94 changed files with 1559 additions and 4998 deletions
-2
View File
@@ -23,8 +23,6 @@ targets:
path: ./packages/appium
- type: npm
path: ./packages/base-driver
- type: npm
path: ./packages/doctor
- type: npm
path: ./packages/eslint-config-appium
- type: npm
+82 -63
View File
@@ -1,51 +1,97 @@
name: 🐞 Bug Report
description: File a new bug report
title: 'bug: <title>'
description: Create a new bug report.
title: '🐞 bug: <title>'
labels: [Bug, Needs Triage]
body:
- type: markdown
attributes:
value: ':stop_sign: _For support questions, please visit [http://discuss.appium.io](http://discuss.appium.io) instead._'
value: ':stop_sign: _Make sure you are reporting an issue. For general questions, please use [http://discuss.appium.io](http://discuss.appium.io)._'
- type: checkboxes
attributes:
label: 'Do I have the most recent component updates?'
description: 'Appium 1-related issues are going to be ignored as this version has reached its end of life.'
options:
- label: 'I use the most recent available driver/plugin and server versions'
required: true
- type: checkboxes
attributes:
label: 'Is the component officially supported by the Appium team?'
description: 'Make sure you report an issue for a component which is officially supported/maintained by the Appium team.'
options:
- label: 'I have verified the component repository is present under the Appium organization in GitHub'
required: true
- type: checkboxes
attributes:
label: 'Is there an existing issue for this?'
label: Is there an existing issue for this?
description: 'Please [search :mag: the issues](https://github.com/appium/appium/issues) to check if this bug has already been reported.'
options:
- label: 'I have searched the existing issues'
- label: I have searched the existing issues
required: true
- type: textarea
- type: input
attributes:
label: 'Current Behavior'
description: 'Describe the problem you are experiencing. **Please do not paste your logs here.** Screenshots are welcome.'
label: Appium Version
description: What Appium server version are you running?
placeholder: Output of `appium --version`
validations:
required: true
- type: textarea
attributes:
label: 'Expected Behavior'
description: 'Describe what you expect to happen instead.'
validations:
required: true
- type: textarea
attributes:
label: 'Minimal Reproducible Example'
description: |
Please provide a the _smallest, complete code snippet_ that Appium's maintainers can run to reproduce the issue ([read more about what this entails](https://stackoverflow.com/help/minimal-reproducible-example)). Failing this, any sort of reproduction steps are better than nothing!
An easy way to get started is to copy [sample code](https://github.com/appium/appium/tree/master/packages/appium/sample-code) and modify it accordingly.
- type: dropdown
attributes:
label: Appium Host Type
description: |
How is your Appium server being hosted? For options other than "Command Line", please add more details under "Further Information".
options:
- Command Line
- Virtual Machine/Docker
- Cloud Provider (e.g. BrowserStack/Sauce Labs)
- Other
validations:
required: true
- type: input
attributes:
label: Are you using Appium components not maintained by the Appium team?
description: Refer to the [Appium Ecosystem](http://appium.io/docs/en/latest/ecosystem/) page for officially-supported components.
placeholder: Please provide the names of any third-party components.
- type: checkboxes
attributes:
label: Is this issue reproducible using the latest components?
description: |
You can retrieve the latest server/driver/plugin version number by running `npm info <package-name> version`. If you are not up to date, please try to reproduce your issue after updating all affected components.
options:
- label: I confirm the issue is still reproducible with the latest component versions
- type: textarea
attributes:
label: Current Behavior
description: Describe the problem you are experiencing. **Please do not paste your logs here.** Screenshots are welcome.
validations:
required: true
- type: textarea
attributes:
label: Expected Behavior
description: Describe what you expect to happen instead.
validations:
required: true
- type: input
attributes:
label: Appium Log
description: |
Please create a [Gist](https://gist.github.com) with your _full_ Appium server log and link it here. Alternatively, you can directly attach your logfile in the "Further Information" field below.
:warning: _Remember to redact or remove any sensitive information!_
placeholder: 'https://gist.github.com/...'
- type: textarea
attributes:
label: Environment Details
description: |
Please provide additional information about your environment; feel free to remove any items which are not relevant.
:warning: _Remember to redact or remove any sensitive information!_
value: |
- Debug details (`appium --show-debug-info`):
- Last component version(s) which did _not_ exhibit the problem:
- Platform and version of device under test:
- Real device or emulator/simulator:
- type: textarea
attributes:
label: Minimal Reproducible Example
description: |
Please provide [the smallest, complete code snippet](https://stackoverflow.com/help/minimal-reproducible-example) that Appium's maintainers can run to reproduce the issue. An easy way to get started is to copy [sample code](https://github.com/appium/appium/tree/master/packages/appium/sample-code) and modify it accordingly. Failing this, any sort of reproduction steps are better than nothing!
If the result is more than a screenful of text _or_ requires multiple files, please:
@@ -54,39 +100,12 @@ body:
- Provide a link to a new or existing public repository exhibiting the issue
validations:
required: true
- type: textarea
attributes:
label: 'Environment'
description: 'Please provide the following information about your environment; feel free to remove any items which are not relevant.'
value: |
- Operating system:
- Appium server version (output of `appium --version`):
- Appium driver(s) and their version(s) (`appium driver list`):
- Appium plugin(s) and their version(s) (`appium plugin list`):
- Node.js version (output of `node --version`):
- `npm` version (output of `npm --version`):
- Last component(s) version which did _not_ exhibit the problem:
- Platform and version under test:
- Real device or emulator/simulator:
validations:
required: false
- type: input
attributes:
label: 'Link to Appium Logs'
description: |
Create a [Gist](https://gist.github.com)—which contains your _full_ Appium logs—and link it here. Alternatively, you can attach a logfile to this issue (drag it into the "Further Information" field below).
:warning: _Remember to redact or remove any sensitive information!_
placeholder: 'https://gist.github.com/...'
- type: textarea
attributes:
label: Further Information
description: |
Links? References? Anything that will give us more context about the issue you are encountering!
description: If needed, please provide additional information about your issue (attachments, links, references, etc.).
_Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in._
validations:
required: false
- type: markdown
attributes:
value: ':stop_sign: _For support questions, please visit [http://discuss.appium.io](http://discuss.appium.io) instead._'
value: ':stop_sign: _Make sure you are reporting an issue. For general questions, please use [http://discuss.appium.io](http://discuss.appium.io)._'
+1 -1
View File
@@ -2,4 +2,4 @@ blank_issues_enabled: true
contact_links:
- name: ❓ Question
url: https://discuss.appium.io/
about: Please use the Appium forum for questions
about: Please use the Appium forum for any questions.
-20
View File
@@ -1,20 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
title: "[Feat]:"
labels: enhancement
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
@@ -0,0 +1,33 @@
name: 💡 Feature Request
description: Suggest an idea for this project.
title: "💡 feat: <title>"
labels: [Enhancement]
body:
- type: textarea
attributes:
label: Is your feature request related to a problem?
description: A clear and concise description of what the problem is. Ex. I'm always frustrated when [...].
validations:
required: true
- type: textarea
attributes:
label: Describe the solution you'd like.
description: A clear and concise description of what you want to happen.
validations:
required: true
- type: textarea
attributes:
label: Describe alternatives you've considered.
description: A clear and concise description of any alternative solutions or features you've considered.
validations:
required: false
- type: textarea
attributes:
label: Additional context
description: Add any other context or screenshots about the feature request here.
validations:
required: false
-5
View File
@@ -53,11 +53,6 @@ labels:
matcher:
files: ['packages/base-plugin/**']
- label: '@appium/doctor'
sync: true
matcher:
files: ['packages/doctor/**']
- label: '@appium/docutils'
sync: true
matcher:
@@ -0,0 +1,38 @@
# Retrieves non-English translations from Crowdin and creates a PR with new changes
name: Sync Crowdin Docs Translations
on:
workflow_dispatch:
# TODO: Uncomment the scheduler as soon as we have existing translations
# imported to Crowdin
# schedule:
# - cron: 0 0 * * 0
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Use Node.js LTS
uses: actions/setup-node@v4
with:
node-version: lts/*
cache: 'npm'
- name: Install Dependencies
run: npm ci
- name: Crowdin Sync
run: npm run crowdin-sync-docs-translations
env:
# appium-documentation
CROWDIN_PROJECT_ID: ${{ vars.CROWDIN_DOCS_PROJECT_ID }}
CROWDIN_TOKEN: ${{ secrets.CROWDIN_DOCS_TOKEN }}
- name: Create Pull Request
uses: peter-evans/create-pull-request@v7.0.5
with:
token: ${{ github.token }}
commit-message: 'docs(appium): Update documentation translations'
title: 'docs(appium): Update documentation translations'
branch: crowdin-sync-${{ github.run_id }}
body: 'Automated Update of Documentation Translations: https://crowdin.com/project/appium-documentation'
@@ -0,0 +1,31 @@
# Updates Crowdin with any changes in document files in English
name: Update Crowdin English Docs
on:
push:
branches: [master]
paths:
- 'packages/appium/docs/en/**.md'
- 'packages/appium/docs/mkdocs-en.yml'
- '.github/workflows/crowdin-update-docs-resources.yml' # this file
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Use Node.js LTS
uses: actions/setup-node@v4
with:
node-version: lts/*
cache: 'npm'
- name: Install Dependencies
run: npm ci
- name: Crowdin Update
run: npm run crowdin-update-docs
env:
# appium-documentation
CROWDIN_PROJECT_ID: ${{ vars.CROWDIN_DOCS_PROJECT_ID }}
CROWDIN_TOKEN: ${{ secrets.CROWDIN_DOCS_TOKEN }}
+15
View File
@@ -0,0 +1,15 @@
name: Conventional Commits
on:
pull_request:
types: [opened, edited, synchronize, reopened]
jobs:
conventional:
name: PR Title
runs-on: ubuntu-latest
steps:
- uses: beemojs/conventional-pr-action@v3
with:
config-preset: angular
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+18 -10
View File
@@ -1,36 +1,44 @@
# Aggregate Changelog
Appium contains many different modules and supports a variety of drivers, plugins and clients, most of which have their own changelogs. Links to these individual changelogs are aggregated here.
The Appium monorepo contains many different modules and supports a variety of drivers, plugins and clients,
most of which have their own changelogs. Links to these individual changelogs are aggregated here.
## Main Appium Module
* ### [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
* Refer to the [Appium Ecosystem](https://appium.io/docs/en/latest/ecosystem/) page for links to specific driver/client/plugin documentation
* Changelogs for plugins maintained by Appium Team are also linked in the Other Modules section below
## Drivers / Clients
Refer to the [Appium Ecosystem](https://appium.io/docs/en/latest/ecosystem/) page for links
to driver/client documentation
## Other Appium Modules
## Plugins
* [execute-driver-plugin](packages/execute-driver-plugin/CHANGELOG.md)
* [images-plugin](packages/images-plugin/CHANGELOG.md)
* [relaxed-caps-plugin](packages/relaxed-caps-plugin/CHANGELOG.md)
* [universal-xml-plugin](packages/universal-xml-plugin/CHANGELOG.md)
## Other Modules
* [base-driver](packages/base-driver/CHANGELOG.md)
* [base-plugin](packages/base-plugin/CHANGELOG.md)
* [doctor](packages/doctor/CHANGELOG.md)
* [docutils](packages/docutils/CHANGELOG.md)
* [driver-test-support](packages/driver-test-support/CHANGELOG.md)
* [eslint-config-appium](packages/eslint-config-appium/CHANGELOG.md)
* [eslint-config-appium-ts](packages/eslint-config-appium-ts/CHANGELOG.md)
* [execute-driver-plugin](packages/execute-driver-plugin/CHANGELOG.md)
* [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)
* [schema](packages/schema/CHANGELOG.md)
* [strongbox](packages/strongbox/CHANGELOG.md)
* [support](packages/support/CHANGELOG.md)
* [test-support](packages/test-support/CHANGELOG.md)
* [tsconfig](packages/tsconfig/CHANGELOG.md)
* [types](packages/types/CHANGELOG.md)
* [universal-xml-plugin](packages/universal-xml-plugin/CHANGELOG.md)
## Removed Modules
The changelog links used for these modules lead to the last version of their changelog file.
* [doctor](https://github.com/appium/appium/blob/8daf5e123ac14c8325acf504fb33eb28e1a3dd78/packages/doctor/CHANGELOG.md) ([removal PR](https://github.com/appium/appium/pull/20805))
* [gulp-plugins](https://github.com/appium/appium/blob/0823f0b60e40395cd1dc3b72cfa3c0092bc81302/packages/gulp-plugins/CHANGELOG.md) ([removal PR](https://github.com/appium/appium/pull/17943))
* [typedoc-plugin-appium](https://github.com/appium/appium/blob/d6204b6902074210943d7bbbf72d139b9b170a20/packages/typedoc-plugin-appium/CHANGELOG.md) ([removal PR](https://github.com/appium/appium/pull/19465))
+318 -654
View File
File diff suppressed because it is too large Load Diff
+6 -4
View File
@@ -38,12 +38,14 @@
"clean:interactive": "git clean -d -x -i",
"clean:force": "git clean -d -x -f",
"clean:workspaces": "lerna run clean || true",
"crowdin-sync-docs-translations": "node ./scripts/crowdin-sync-docs-translations.mjs",
"crowdin-update-docs": "node ./scripts/crowdin-update-docs-resources.mjs",
"dev": "run-s build:compile \"build:workspaces\" \"build:compile -- --watch\"",
"dev:docs": "npm run -w packages/appium dev:docs",
"docs": "npm run -w packages/appium build:docs",
"docs:preview": "npm run -w packages/appium build:docs:preview",
"docs:publish": "npm run -w packages/appium publish:docs",
"doctor": "node ./packages/doctor",
"doctor": "node ./scripts/doctor-stub.mjs",
"install-docs-deps": "npm run -w packages/appium install-docs-deps",
"lint": "eslint .",
"lint:ci": "eslint --quiet .",
@@ -106,7 +108,7 @@
"eslint": "8.57.1",
"eslint-config-prettier": "9.1.0",
"eslint-find-rules": "4.2.0",
"eslint-import-resolver-typescript": "3.6.3",
"eslint-import-resolver-typescript": "3.7.0",
"eslint-plugin-import": "2.31.0",
"eslint-plugin-mocha": "10.5.0",
"eslint-plugin-promise": "6.6.0",
@@ -118,9 +120,9 @@
"midnight-smoker": "8.0.0",
"mjpeg-consumer": "2.0.0",
"mjpeg-server": "0.3.1",
"mocha": "10.8.2",
"mocha": "11.0.1",
"npm-run-all2": "6.2.6",
"prettier": "3.4.1",
"prettier": "3.4.2",
"rewiremock": "3.14.5",
"rimraf": "5.0.10",
"serve-static": "1.16.2",
+26
View File
@@ -3,6 +3,32 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [2.13.1](https://github.com/appium/appium/compare/appium@2.13.0...appium@2.13.1) (2024-12-05)
### Bug Fixes
* **docutils:** update dependency lilconfig to v3.1.3 ([#20803](https://github.com/appium/appium/issues/20803)) ([93fa5cc](https://github.com/appium/appium/commit/93fa5ccb06eb5089bb3e8a8d9e67dad7f4446a0a))
* **support:** update dependency axios to v1.7.9 ([#20811](https://github.com/appium/appium/issues/20811)) ([69f100a](https://github.com/appium/appium/commit/69f100ad3e12030708dee4b8a74005dd41976e37))
* **support:** update dependency teen_process to v2.2.2 ([#20808](https://github.com/appium/appium/issues/20808)) ([c91683f](https://github.com/appium/appium/commit/c91683f50feea937ff8568eab6366465a6cf9a3e))
* **types:** update dependency type-fest to v4.30.0 ([#20802](https://github.com/appium/appium/issues/20802)) ([8590432](https://github.com/appium/appium/commit/8590432955eb7663e35847db541b9ead3f845a36))
## [2.13.0](https://github.com/appium/appium/compare/appium@2.12.2...appium@2.13.0) (2024-12-02)
### Features
* **base-driver:** Allow to prefix feature names with automation names ([#20793](https://github.com/appium/appium/issues/20793)) ([942057d](https://github.com/appium/appium/commit/942057d26cbf51539f34b6b7ff8a3c1d07821687))
### Bug Fixes
* **types:** update dependency type-fest to v4.29.1 ([#20795](https://github.com/appium/appium/issues/20795)) ([6ba31fe](https://github.com/appium/appium/commit/6ba31fe5766f69cb010a4cac81233f4c3cbcf80f))
## [2.12.2](https://github.com/appium/appium/compare/appium@2.12.1...appium@2.12.2) (2024-11-29)
+27
View File
@@ -121,3 +121,30 @@ npm run dev:docs
```
You can then view the documentation at `http://127.0.0.1:8000/docs/en`.
## Translating Appium Documentation
The process of Appium documents localization into languages other than English is automated and is done via
the [Crowdin Translations Management System](https://crowdin.com). Do not edit any translated documents
directly in the GitHub Appium repository as they are going to be replaced with ones exported from Crowdin
during an upcoming sync.
### Where To Start
If you would like to contribute to the translation of Appium documents into your language then simply join
the translators group for the [Appium Documentation](https://crowdin.com/project/appium-documentation)
Crowdin project, and start translating documents there. If you see that your language is missing from
the list of available Crowdin languages then simply let us know by creating an
[issue](https://github.com/appium/appium/issues).
### Source Language Updates
Changes in documents are synchronized to Crowdin automatically via the `Update Crowdin English Docs` GitHub action.
This action is triggered automatically as soon as there are any changes under `packages/appium/docs/en/**.md`
or `packages/appium/docs/mkdocs-en.yml`.
### Fetching Translated Documents
In order to fetch translated files from Crowdin to the GitHub repository it is necessary to trigger
the `Sync Crowdin Docs Translations` action. This action should also automatically create a PR with
corresponding translated resources included.
@@ -44,6 +44,14 @@ might use. Here is an incomplete list of examples from some of Appium's official
|`record_audio`|Allow recording of host machine audio inputs|XCUITest|
|`execute_driver_script`| Allows to send a request which has multiple Appium commands.|Execute Driver Plugin|
## Driver-scope security
Since Appium server version 2.12.3 there is a possibility to prefix each feature name with the corresponding automationName where it is going to be applied. For example, if you provide the following features to allow:
`uiautomator2:adb_shell,xcuitest:get_server_logs,*:record_audio`, then the `adb_shell` would only be allowed
for the UiAutomator2 driver, `get_server` - for the XCUITest driver and the `record_audio` one - for
all installed drivers. Feature names provided without explicit automation name prefix are equivalent to
these having the `*:` prefix. The same feature naming rule applies to the list of denied features.
## Examples
To turn on the `get_server_logs` feature for my Appium server, I could start it like this:
+1 -1
View File
@@ -14,7 +14,7 @@ title: Welcome
<div style="text-align:center;width:100%;background-color:#ec366d;margin-top:-3em;margin-bottom:2em;">
<h3 style="color:#ffffff;padding: 8px;">
AppiumConf 2025 is partnering with SeleniumConf<br/> <span style="font-size:0.8em;">March 26-28 in Valencia, Spain. Join us to learn and connect with the community!<span><br/>
AppiumConf 2025 is partnering with SeleniumConf<br/> <span style="font-size:0.8em;">March 26-28 in Valencia, Spain. Join us to learn and connect with the community!</span><br/>
<a style="color:#fcba12" href="https://seleniumconf.com/" target="_blank">Register Now</a>
</h3>
</div>
+3 -2
View File
@@ -22,6 +22,7 @@ import {
isBroadcastIp,
fetchInterfaces,
V4_BROADCAST_IP,
validateFeatures,
} from './utils';
import {util, node, logger} from '@appium/support';
import {getDefaultsForExtension} from './schema';
@@ -697,13 +698,13 @@ class AppiumDriver extends DriverCore {
if (!_.isEmpty(this.args.denyInsecure)) {
this.log.info('Explicitly preventing use of insecure features:');
this.args.denyInsecure.map((a) => this.log.info(` ${a}`));
driverInstance.denyInsecure = this.args.denyInsecure;
driverInstance.denyInsecure = validateFeatures(this.args.denyInsecure);
}
if (!_.isEmpty(this.args.allowInsecure)) {
this.log.info('Explicitly enabling use of insecure features:');
this.args.allowInsecure.map((a) => this.log.info(` ${a}`));
driverInstance.allowInsecure = this.args.allowInsecure;
driverInstance.allowInsecure = validateFeatures(this.args.allowInsecure);
}
// Likewise, any driver-specific CLI args that were passed in should be assigned directly to
+39 -2
View File
@@ -1,6 +1,11 @@
import _ from 'lodash';
import logger from './logger';
import {processCapabilities, PROTOCOLS, STANDARD_CAPS, errors} from '@appium/base-driver';
import {
processCapabilities,
PROTOCOLS,
STANDARD_CAPS,
errors,
} from '@appium/base-driver';
import {inspect as dump} from 'util';
import {node, fs} from '@appium/support';
import path from 'path';
@@ -12,7 +17,8 @@ const STANDARD_CAPS_LOWERCASE = new Set([...STANDARD_CAPS].map((cap) => cap.toLo
export const V4_BROADCAST_IP = '0.0.0.0';
export const V6_BROADCAST_IP = '::';
export const npmPackage = fs.readPackageJsonFrom(__dirname);
const ALL_DRIVERS_MATCH = '*';
const FEATURE_NAME_SEPARATOR = ':';
/**
*
@@ -457,6 +463,37 @@ export function isBroadcastIp(address) {
return [V4_BROADCAST_IP, V6_BROADCAST_IP, `[${V6_BROADCAST_IP}]`].includes(address);
}
/**
* Validates the list of allowed/denied server features
*
* @param {string[]} features
* @returns {string[]}
*/
export function validateFeatures(features) {
const validator = (/** @type {string} */ fullName) => {
const separatorPos = fullName.indexOf(FEATURE_NAME_SEPARATOR);
// TODO: This is for the backward compatibility with Appium2
// TODO: In Appium3 the separator will be mandatory
if (separatorPos < 0) {
return `${ALL_DRIVERS_MATCH}${FEATURE_NAME_SEPARATOR}${fullName}`;
}
const [automationName, featureName] = [
fullName.substring(0, separatorPos),
fullName.substring(separatorPos + 1)
];
if (!automationName || !featureName) {
throw new Error(
`The full feature name must include both the destination automation name or the ` +
`'${ALL_DRIVERS_MATCH}' wildcard to apply the feature to all installed drivers, and ` +
`the feature name split by a colon, got '${fullName}' instead`
);
}
return fullName;
};
return features.map(validator);
}
/**
* @typedef {import('@appium/types').StringRecord} StringRecord
* @typedef {import('@appium/types').BaseDriverCapConstraints} BaseDriverCapConstraints
+10 -10
View File
@@ -1,6 +1,6 @@
{
"name": "appium",
"version": "2.12.2",
"version": "2.13.1",
"description": "Automation for Apps.",
"keywords": [
"automation",
@@ -60,13 +60,13 @@
"test:unit": "mocha \"./test/unit/**/*.spec.js\""
},
"dependencies": {
"@appium/base-driver": "^9.12.2",
"@appium/base-plugin": "^2.2.48",
"@appium/docutils": "^1.0.24",
"@appium/base-driver": "^9.13.1",
"@appium/base-plugin": "^2.2.50",
"@appium/docutils": "^1.0.26",
"@appium/logger": "^1.6.1",
"@appium/schema": "^0.7.0",
"@appium/support": "^5.1.7",
"@appium/types": "^0.22.1",
"@appium/support": "^6.0.0",
"@appium/types": "^0.22.3",
"@sidvind/better-ajv-errors": "3.0.1",
"@types/argparse": "2.0.17",
"@types/bluebird": "3.5.42",
@@ -78,10 +78,10 @@
"argparse": "2.0.1",
"async-lock": "1.4.1",
"asyncbox": "3.0.0",
"axios": "1.7.8",
"axios": "1.7.9",
"bluebird": "3.7.2",
"cross-env": "7.0.3",
"lilconfig": "3.1.2",
"lilconfig": "3.1.3",
"lodash": "4.17.21",
"lru-cache": "10.4.3",
"ora": "5.4.1",
@@ -89,8 +89,8 @@
"resolve-from": "5.0.0",
"semver": "7.6.3",
"source-map-support": "0.5.21",
"teen_process": "2.2.0",
"type-fest": "4.29.0",
"teen_process": "2.2.2",
"type-fest": "4.30.0",
"winston": "3.17.0",
"wrap-ansi": "7.0.0",
"ws": "8.18.0",
+26
View File
@@ -3,6 +3,32 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [9.13.1](https://github.com/appium/appium/compare/@appium/base-driver@9.13.0...@appium/base-driver@9.13.1) (2024-12-05)
### Bug Fixes
* **base-driver:** add * always for non-separator for backward compatibility ([#20821](https://github.com/appium/appium/issues/20821)) ([6ea0830](https://github.com/appium/appium/commit/6ea0830319c204097233d0659e21b3447b624431))
* **base-driver:** update dependency @types/method-override to v3 ([#20799](https://github.com/appium/appium/issues/20799)) ([42ef484](https://github.com/appium/appium/commit/42ef4841f9a379d8e9ce531eb5375eab03902e3f))
* **support:** update dependency axios to v1.7.9 ([#20811](https://github.com/appium/appium/issues/20811)) ([69f100a](https://github.com/appium/appium/commit/69f100ad3e12030708dee4b8a74005dd41976e37))
* **types:** update dependency type-fest to v4.30.0 ([#20802](https://github.com/appium/appium/issues/20802)) ([8590432](https://github.com/appium/appium/commit/8590432955eb7663e35847db541b9ead3f845a36))
## [9.13.0](https://github.com/appium/appium/compare/@appium/base-driver@9.12.2...@appium/base-driver@9.13.0) (2024-12-02)
### Features
* **base-driver:** Allow to prefix feature names with automation names ([#20793](https://github.com/appium/appium/issues/20793)) ([942057d](https://github.com/appium/appium/commit/942057d26cbf51539f34b6b7ff8a3c1d07821687))
### Bug Fixes
* **types:** update dependency type-fest to v4.29.1 ([#20795](https://github.com/appium/appium/issues/20795)) ([6ba31fe](https://github.com/appium/appium/commit/6ba31fe5766f69cb010a4cac81233f4c3cbcf80f))
## [9.12.2](https://github.com/appium/appium/compare/@appium/base-driver@9.12.1...@appium/base-driver@9.12.2) (2024-11-29)
+35 -4
View File
@@ -20,7 +20,11 @@ import AsyncLock from 'async-lock';
import _ from 'lodash';
import {EventEmitter} from 'node:events';
import os from 'node:os';
import {DEFAULT_BASE_PATH, PROTOCOLS, MAX_LOG_BODY_LENGTH} from '../constants';
import {
DEFAULT_BASE_PATH,
PROTOCOLS,
MAX_LOG_BODY_LENGTH,
} from '../constants';
import {errors} from '../protocol';
import DeviceSettings from './device-settings';
import helpers, {BASEDRIVER_VER} from './helpers';
@@ -30,6 +34,9 @@ const NEW_COMMAND_TIMEOUT_MS = 60 * 1000;
const ON_UNEXPECTED_SHUTDOWN_EVENT = 'onUnexpectedShutdown';
const ALL_DRIVERS_MATCH = '*';
const FEATURE_NAME_SEPARATOR = ':';
export class DriverCore<const C extends Constraints, Settings extends StringRecord = StringRecord>
implements Core<C, Settings>
{
@@ -258,13 +265,37 @@ export class DriverCore<const C extends Constraints, Settings extends StringReco
* @param name - name of feature/command
*/
isFeatureEnabled(name: string): boolean {
// automationName comparison is case-insensitive,
// while feature name is case-sensitive
const currentAutomationName = _.toLower(this.opts.automationName);
const parseFullName = (fullName: string) => {
const separatorPos = fullName.indexOf(FEATURE_NAME_SEPARATOR);
if (separatorPos < 0) {
// TODO: This is for the backward compatibility with Appium 2.
// TODO: We should bring back to raise an Error below for Appium 3.
// throw new Error(
// `The full feature name must include both the driver name/wildcard and the feature ` +
// `name split by a colon, got '${fullName}' instead`
// );
return [ALL_DRIVERS_MATCH, fullName];
}
return [
_.toLower(fullName.substring(0, separatorPos)),
fullName.substring(separatorPos + 1)
];
};
const parseFullNames = (fullNames: string[]) => fullNames.map(parseFullName);
const matches = ([automationName, featureName]: [string, string]) =>
[currentAutomationName, ALL_DRIVERS_MATCH].includes(automationName) && featureName === name;
// if we have explicitly denied this feature, return false immediately
if (this.denyInsecure && _.includes(this.denyInsecure, name)) {
if (!_.isEmpty(this.denyInsecure) && parseFullNames(this.denyInsecure).some(matches)) {
return false;
}
// if we specifically have allowed the feature, return true
if (this.allowInsecure && _.includes(this.allowInsecure, name)) {
if (!_.isEmpty(this.allowInsecure) && parseFullNames(this.allowInsecure).some(matches)) {
return true;
}
@@ -283,7 +314,7 @@ export class DriverCore<const C extends Constraints, Settings extends StringReco
* not
*
* @param name - name of feature/command
* @deprecated
* @deprecated Use {@link assertFeatureEnabled} instead
*/
ensureFeatureEnabled(name: string) {
this.assertFeatureEnabled(name);
+5 -7
View File
@@ -7,15 +7,13 @@ import {Protocol} from '@appium/types';
// server feature. Example rule:
// {"pattern": "(.{1,150}).*", "flags": "s", "replacer": "$1"}
// ^ cuts all log records to maximum 150 chars
const MAX_LOG_BODY_LENGTH = 1024;
const MJSONWP_ELEMENT_KEY = 'ELEMENT';
const W3C_ELEMENT_KEY = util.W3C_WEB_ELEMENT_IDENTIFIER;
const PROTOCOLS = {
export const MAX_LOG_BODY_LENGTH = 1024;
export const MJSONWP_ELEMENT_KEY = 'ELEMENT';
export const W3C_ELEMENT_KEY = util.W3C_WEB_ELEMENT_IDENTIFIER;
export const PROTOCOLS = {
W3C: 'W3C',
MJSONWP: 'MJSONWP',
} as const satisfies Record<Protocol, Protocol>;
// Before Appium 2.0, this default value was '/wd/hub' by historical reasons.
const DEFAULT_BASE_PATH = '';
export {MAX_LOG_BODY_LENGTH, MJSONWP_ELEMENT_KEY, W3C_ELEMENT_KEY, PROTOCOLS, DEFAULT_BASE_PATH};
export const DEFAULT_BASE_PATH = '';
+6 -6
View File
@@ -1,6 +1,6 @@
{
"name": "@appium/base-driver",
"version": "9.12.2",
"version": "9.13.1",
"description": "Base driver class for Appium drivers",
"keywords": [
"automation",
@@ -44,18 +44,18 @@
"test:types": "tsd"
},
"dependencies": {
"@appium/support": "^5.1.7",
"@appium/types": "^0.22.1",
"@appium/support": "^6.0.0",
"@appium/types": "^0.22.3",
"@colors/colors": "1.6.0",
"@types/async-lock": "1.4.2",
"@types/bluebird": "3.5.42",
"@types/express": "5.0.0",
"@types/lodash": "4.17.13",
"@types/method-override": "0.0.35",
"@types/method-override": "3.0.0",
"@types/serve-favicon": "2.5.7",
"async-lock": "1.4.1",
"asyncbox": "3.0.0",
"axios": "1.7.8",
"axios": "1.7.9",
"bluebird": "3.7.2",
"body-parser": "1.20.3",
"express": "4.21.1",
@@ -67,7 +67,7 @@
"path-to-regexp": "8.2.0",
"serve-favicon": "2.5.0",
"source-map-support": "0.5.21",
"type-fest": "4.29.0",
"type-fest": "4.30.0",
"validate.js": "0.13.1"
},
"optionalDependencies": {
+16
View File
@@ -3,6 +3,22 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [2.2.50](https://github.com/appium/appium/compare/@appium/base-plugin@2.2.49...@appium/base-plugin@2.2.50) (2024-12-05)
**Note:** Version bump only for package @appium/base-plugin
## [2.2.49](https://github.com/appium/appium/compare/@appium/base-plugin@2.2.48...@appium/base-plugin@2.2.49) (2024-12-02)
**Note:** Version bump only for package @appium/base-plugin
## [2.2.48](https://github.com/appium/appium/compare/@appium/base-plugin@2.2.47...@appium/base-plugin@2.2.48) (2024-11-29)
**Note:** Version bump only for package @appium/base-plugin
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@appium/base-plugin",
"version": "2.2.48",
"version": "2.2.50",
"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.12.2",
"@appium/support": "^5.1.7"
"@appium/base-driver": "^9.13.1",
"@appium/support": "^6.0.0"
},
"engines": {
"node": "^14.17.0 || ^16.13.0 || >=18.0.0",
-694
View File
@@ -1,694 +0,0 @@
# Change Log
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [2.1.14](https://github.com/appium/appium/compare/@appium/doctor@2.1.13...@appium/doctor@2.1.14) (2024-11-29)
### Bug Fixes
* **doctor:** update dependency appium-adb to v12.6.1 ([7e19c88](https://github.com/appium/appium/commit/7e19c88b79ea8c79489db54fcb3d56d9b2ab56dd))
* **doctor:** update dependency appium-adb to v12.6.2 ([#20735](https://github.com/appium/appium/issues/20735)) ([d046657](https://github.com/appium/appium/commit/d04665777a0296e5d5bbc93ddc74329887cf9677))
* **doctor:** update dependency appium-adb to v12.7.0 ([#20738](https://github.com/appium/appium/issues/20738)) ([9d21f56](https://github.com/appium/appium/commit/9d21f56edb4081f09a1255a665e589103fafe781))
## [2.1.13](https://github.com/appium/appium/compare/@appium/doctor@2.1.12...@appium/doctor@2.1.13) (2024-10-24)
**Note:** Version bump only for package @appium/doctor
## [2.1.12](https://github.com/appium/appium/compare/@appium/doctor@2.1.11...@appium/doctor@2.1.12) (2024-10-15)
**Note:** Version bump only for package @appium/doctor
## [2.1.11](https://github.com/appium/appium/compare/@appium/doctor@2.1.10...@appium/doctor@2.1.11) (2024-09-16)
### Bug Fixes
* **doctor:** update dependency appium-adb to v12.5.2 ([db28605](https://github.com/appium/appium/commit/db286055324c980459f12163ec8253c0b90c1754))
* **doctor:** update dependency appium-adb to v12.5.3 ([048ce52](https://github.com/appium/appium/commit/048ce520d83f31ceab393f5fe46588274fd2ef56))
* **doctor:** update dependency appium-adb to v12.6.0 ([42d7ed8](https://github.com/appium/appium/commit/42d7ed8afe199584cb3850b4a5826d0cb1dc8dc2))
## [2.1.10](https://github.com/appium/appium/compare/@appium/doctor@2.1.9...@appium/doctor@2.1.10) (2024-08-07)
### Bug Fixes
* **doctor:** update dependency appium-adb to v12.5.0 ([b7b97a2](https://github.com/appium/appium/commit/b7b97a24d66e5c561b4c9559a4b0aea25503f39a))
* **doctor:** update dependency appium-adb to v12.5.1 ([82b7515](https://github.com/appium/appium/commit/82b7515867c10c20843dd02c37c7a2e9014c3703))
## [2.1.9](https://github.com/appium/appium/compare/@appium/doctor@2.1.8...@appium/doctor@2.1.9) (2024-07-10)
### Bug Fixes
* **doctor:** update dependency appium-adb to v12.4.6 ([5281f5a](https://github.com/appium/appium/commit/5281f5a01a63e12454093cb296286da02adc77ee))
* **doctor:** update dependency appium-adb to v12.4.8 ([177dff4](https://github.com/appium/appium/commit/177dff4f0ed84880ce9df55f94ff62b922a80ae5))
* **support:** update dependency teen_process to v2.1.10 ([86f843b](https://github.com/appium/appium/commit/86f843b9e1fc56576d7a08bf95838a58971e98b3))
* **support:** update dependency teen_process to v2.1.7 ([5226fa8](https://github.com/appium/appium/commit/5226fa897b90e9fec9be3e79a7d48fc7b4e0016d))
* **support:** update dependency teen_process to v2.2.0 ([06607da](https://github.com/appium/appium/commit/06607da92e93d015747fd6d6d5bdabe1b3ac58d2))
## [2.1.8](https://github.com/appium/appium/compare/@appium/doctor@2.1.7...@appium/doctor@2.1.8) (2024-06-28)
**Note:** Version bump only for package @appium/doctor
## [2.1.7](https://github.com/appium/appium/compare/@appium/doctor@2.1.6...@appium/doctor@2.1.7) (2024-06-27)
### Bug Fixes
* **doctor:** update dependency appium-adb to v12.4.3 ([4345a40](https://github.com/appium/appium/commit/4345a40a15d2473bb13524593ab66ff8ae3d8a7e))
* **doctor:** update dependency appium-adb to v12.4.4 ([15234bc](https://github.com/appium/appium/commit/15234bc05502157ebf847856d38e35da4d542df7))
* **support:** update dependency teen_process to v2.1.5 ([bc388db](https://github.com/appium/appium/commit/bc388db1116f411f4b241105f5feae30418c9a27))
* **support:** update dependency teen_process to v2.1.6 ([5412461](https://github.com/appium/appium/commit/541246184dc7521c825ec1902775e681ea6f0db3))
## [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)
### Bug Fixes
* **doctor:** update dependency appium-adb to v12.0.9 ([fd63e9b](https://github.com/appium/appium/commit/fd63e9bdbd0c941453963cfb9636972129a3b57a))
* **doctor:** update dependency appium-adb to v12.2.0 ([e961a9b](https://github.com/appium/appium/commit/e961a9b9c5347ed85317de9bfc5687ad74a711ff))
* **doctor:** update dependency appium-adb to v12.3.2 ([f764c38](https://github.com/appium/appium/commit/f764c38c29c657133dec1ea3f0e3c6c2663ee7df))
* **doctor:** update dependency appium-adb to v12.4.0 ([33be2ee](https://github.com/appium/appium/commit/33be2ee45d725e6a64fc24683dfbcbff29dfbe84))
* **support:** update dependency teen_process to v2.1.3 ([b34ce3c](https://github.com/appium/appium/commit/b34ce3cff0279be5bb5dcb9228f7e058d27a5e72))
## [2.0.36](https://github.com/appium/appium/compare/@appium/doctor@2.0.35...@appium/doctor@2.0.36) (2024-04-21)
**Note:** Version bump only for package @appium/doctor
## [2.0.35](https://github.com/appium/appium/compare/@appium/doctor@2.0.34...@appium/doctor@2.0.35) (2024-04-16)
**Note:** Version bump only for package @appium/doctor
## [2.0.34](https://github.com/appium/appium/compare/@appium/doctor@2.0.33...@appium/doctor@2.0.34) (2024-04-08)
### Bug Fixes
* **doctor:** update dependency appium-adb to v12.0.4 ([df07665](https://github.com/appium/appium/commit/df076655098db68c986079037a794ce3477314bd))
* **doctor:** update dependency appium-adb to v12.0.5 ([a6d49c1](https://github.com/appium/appium/commit/a6d49c1ac84c98ebe085abf32161d2f39c172d5a))
* **doctor:** update dependency appium-adb to v12.0.6 ([26ab0ef](https://github.com/appium/appium/commit/26ab0ef7595580722f9a63a1c1e5c3bbef3b18f1))
* **doctor:** update dependency appium-adb to v12.0.7 ([3936cb4](https://github.com/appium/appium/commit/3936cb43dceb45e1386c9418453a0dd734e32215))
* **doctor:** update dependency appium-adb to v12.0.8 ([1f36882](https://github.com/appium/appium/commit/1f368828e6b3093a7317853e94437bb79955c399))
## [2.0.33](https://github.com/appium/appium/compare/@appium/doctor@2.0.32...@appium/doctor@2.0.33) (2024-02-13)
**Note:** Version bump only for package @appium/doctor
## [2.0.32](https://github.com/appium/appium/compare/@appium/doctor@2.0.31...@appium/doctor@2.0.32) (2024-02-06)
### Bug Fixes
* **doctor:** update dependency appium-adb to v11.1.0 ([cbc1f50](https://github.com/appium/appium/commit/cbc1f500bc0721edcdf7f47af949ae113c038a46))
* **doctor:** update dependency appium-adb to v12 ([#19648](https://github.com/appium/appium/issues/19648)) ([9617974](https://github.com/appium/appium/commit/961797448116b6be7b500a68b4202b969805825c))
* **doctor:** update dependency appium-adb to v12.0.1 ([b85a6b9](https://github.com/appium/appium/commit/b85a6b939f688703522ebb6ddab19aa807c64dbe))
* **doctor:** update dependency appium-adb to v12.0.2 ([5936d53](https://github.com/appium/appium/commit/5936d53d7856e6a2aa31236eaa0ea9afee737c95))
* **doctor:** update dependency appium-adb to v12.0.3 ([#19722](https://github.com/appium/appium/issues/19722)) ([6cd2c54](https://github.com/appium/appium/commit/6cd2c547def390eb5d739d6245fe0bab1f99931a))
## [2.0.31](https://github.com/appium/appium/compare/@appium/doctor@2.0.30...@appium/doctor@2.0.31) (2024-01-03)
### Bug Fixes
* **doctor:** update dependency appium-adb to v11.0.9 ([3465495](https://github.com/appium/appium/commit/34654956a3cd5a76865444ab52bd905543dfcb2c))
## [2.0.30](https://github.com/appium/appium/compare/@appium/doctor@2.0.29...@appium/doctor@2.0.30) (2023-12-18)
### Bug Fixes
* **support:** update dependency teen_process to v2.0.109 ([85dce4a](https://github.com/appium/appium/commit/85dce4a72b21e017b4661ddf997c096817e5fd7f))
* **support:** update dependency teen_process to v2.0.110 ([b602693](https://github.com/appium/appium/commit/b60269371662de4c42ccd7586512c9d685d95d52))
* **support:** update dependency teen_process to v2.0.112 ([#19491](https://github.com/appium/appium/issues/19491)) ([d2d8ea7](https://github.com/appium/appium/commit/d2d8ea7a105eb93f59793fbc4d3438a66a191cd8))
* **support:** update dependency teen_process to v2.1.1 ([b79ed2b](https://github.com/appium/appium/commit/b79ed2bed5329b904b15fa5b1fbde7885087c9ad))
## [2.0.29](https://github.com/appium/appium/compare/@appium/doctor@2.0.28...@appium/doctor@2.0.29) (2023-12-04)
### Bug Fixes
* **doctor:** update dependency appium-adb to v11.0.8 ([610be26](https://github.com/appium/appium/commit/610be26cbb177f4f3b43a9f94166fd832b56476e))
* **support:** update dependency teen_process to v2.0.100 ([e50b53f](https://github.com/appium/appium/commit/e50b53fbea4aa3709adc3df4baf4945124d6a217))
* **support:** update dependency teen_process to v2.0.101 ([b837f8c](https://github.com/appium/appium/commit/b837f8ce14bd48edb6af62bca718a708d2b65f09))
* **support:** update dependency teen_process to v2.0.87 ([4df1712](https://github.com/appium/appium/commit/4df1712d9b0c3155e95e38a1aa552f036d37dcfe))
* **support:** update dependency teen_process to v2.0.89 ([c129111](https://github.com/appium/appium/commit/c1291116989159eb7e08d303354bc00262a9e784))
* **support:** update dependency teen_process to v2.0.91 ([6cedd48](https://github.com/appium/appium/commit/6cedd485d7007455151c6efc03dd6172d4a31259))
* **support:** update teen_process-related packages ([c637b05](https://github.com/appium/appium/commit/c637b052f45e0ae7ce68d29b9739718549302756))
## [2.0.28](https://github.com/appium/appium/compare/@appium/doctor@2.0.27...@appium/doctor@2.0.28) (2023-11-14)
### Bug Fixes
* **doctor:** update dependency appium-adb to v11.0.3 ([ee1df47](https://github.com/appium/appium/commit/ee1df47b172009717b81cce7b3465219791b1f97))
* **doctor:** update dependency appium-adb to v11.0.5 ([#19362](https://github.com/appium/appium/issues/19362)) ([b82f13b](https://github.com/appium/appium/commit/b82f13b893c44bcaf9908a9ebe96d3bb51c1f6b3))
* **doctor:** update dependency appium-adb to v11.0.6 ([c2f543f](https://github.com/appium/appium/commit/c2f543fc1009067141bc6cf50a2d0e47f1a455b2))
* **doctor:** update dependency appium-adb to v11.0.7 ([261cfc8](https://github.com/appium/appium/commit/261cfc8694f9eeab774712e67bbf46e72615efa7))
* **support:** update definitelytyped ([5ae8df3](https://github.com/appium/appium/commit/5ae8df3c36c7f03fbf3420087b532086f6742348))
* **support:** update dependency teen_process to v2.0.57 ([1fbd503](https://github.com/appium/appium/commit/1fbd503a66b965a9b32bbb6702062861a8480333))
* **support:** update dependency teen_process to v2.0.63 ([fa57192](https://github.com/appium/appium/commit/fa571925b7ac980cc99c8d23f659420eacd8546c))
* **support:** update dependency teen_process to v2.0.64 ([332bb59](https://github.com/appium/appium/commit/332bb59ee3fdd14c03f6b3c4fd6587a30319d568))
* **support:** update dependency teen_process to v2.0.66 ([3205b5d](https://github.com/appium/appium/commit/3205b5d06f073f1e5c7735c6e36ad08fdf091182))
* **support:** update dependency teen_process to v2.0.67 ([3b5ae2a](https://github.com/appium/appium/commit/3b5ae2ab17c74c9890b53c081aec146152f11c7a))
* **support:** update dependency teen_process to v2.0.73 ([8e453be](https://github.com/appium/appium/commit/8e453bed3f440c1dcf82f016f7d4431365b9cdff))
* **support:** update dependency teen_process to v2.0.75 ([fbb6fd9](https://github.com/appium/appium/commit/fbb6fd9a2780eb00c15584abeaf644a1fcdecb4d))
* **support:** update teen_process-related packages ([84d96db](https://github.com/appium/appium/commit/84d96dbae45dfe56218d32330f8ff2d26b750fb7))
* **support:** update teen_process-related packages ([3d0b97c](https://github.com/appium/appium/commit/3d0b97cae76c616da0aea5b92cebcd38d59263b7))
## [2.0.27](https://github.com/appium/appium/compare/@appium/doctor@2.0.26...@appium/doctor@2.0.27) (2023-10-19)
### Bug Fixes
* **doctor:** update dependency appium-adb to v11 ([#19321](https://github.com/appium/appium/issues/19321)) ([408885a](https://github.com/appium/appium/commit/408885a5b28f8691dbbb3716f3c1a409d662275b))
* **support:** update definitelytyped ([a306ce7](https://github.com/appium/appium/commit/a306ce741a806d21bc44f3b979803b8af5da99aa))
## [2.0.26](https://github.com/appium/appium/compare/@appium/doctor@2.0.25...@appium/doctor@2.0.26) (2023-10-18)
### Bug Fixes
* **doctor:** update dependency appium-adb to v10 ([#19308](https://github.com/appium/appium/issues/19308)) ([3315671](https://github.com/appium/appium/commit/33156712b142b1cb2ffdcf1cfdf65f5b504e7751))
* **doctor:** update dependency appium-adb to v9.14.10 ([809b2da](https://github.com/appium/appium/commit/809b2daea5304942c849b7015f0a74447fb7b57c))
* **doctor:** update dependency appium-adb to v9.14.11 ([c81e99f](https://github.com/appium/appium/commit/c81e99fd80739d97b3dc2099a43cc66245035bc5))
* **doctor:** update dependency appium-adb to v9.14.12 ([0220a9f](https://github.com/appium/appium/commit/0220a9f8e9a890f84031cfeaea026fceb0a303e9))
* **doctor:** update dependency appium-adb to v9.14.7 ([#19058](https://github.com/appium/appium/issues/19058)) ([f8039a7](https://github.com/appium/appium/commit/f8039a70ca851dcc120a2b217e14e3e3b922ba21))
* **doctor:** update dependency appium-adb to v9.14.9 ([9f700bf](https://github.com/appium/appium/commit/9f700bfdf9e09de3cea9f81029fadef84b8e956b))
* **opencv:** update definitelytyped ([d2a9a99](https://github.com/appium/appium/commit/d2a9a99418af9ce9b569bb9b98ee396faab932bb))
* **support:** update definitelytyped ([b6a76ce](https://github.com/appium/appium/commit/b6a76ce91e2765c22f84e389b93f780e0b4490c0))
* **support:** update dependency @types/teen_process to v2.0.1 ([d2f19aa](https://github.com/appium/appium/commit/d2f19aab935cc6ec00a51f042b5f02674a772198))
* **support:** update dependency teen_process to v2.0.10 ([bc1b56a](https://github.com/appium/appium/commit/bc1b56a3c162ed8686d3d7956c30eb1f7682b1a7))
* **support:** update dependency teen_process to v2.0.12 ([c5d6df7](https://github.com/appium/appium/commit/c5d6df7a000d64e8591a56ce5ef65baa2071a36b))
* **support:** update dependency teen_process to v2.0.13 ([a0c4b84](https://github.com/appium/appium/commit/a0c4b84fb7d3977b7a4ca0a07fadd49ee355791a))
* **support:** update dependency teen_process to v2.0.18 ([ffeebc7](https://github.com/appium/appium/commit/ffeebc74b72e5eadf658325e67708ae6ed5b8346))
* **support:** update dependency teen_process to v2.0.19 ([2ef40fe](https://github.com/appium/appium/commit/2ef40febdc717f54ea618ecfe1058b858fe2daf9))
* **support:** update dependency teen_process to v2.0.23 ([1b0459e](https://github.com/appium/appium/commit/1b0459edd656514d8706aab8e21cd4d40de2ec5d))
* **support:** update dependency teen_process to v2.0.24 ([bc72c66](https://github.com/appium/appium/commit/bc72c6676a5412b60e74bb143a223738ce059977))
* **support:** update dependency teen_process to v2.0.27 ([03b2aa6](https://github.com/appium/appium/commit/03b2aa6b6e45fa0ea24c4691b039255bc4399733))
* **support:** update dependency teen_process to v2.0.30 ([7cdb142](https://github.com/appium/appium/commit/7cdb1429a1104ad3699547f2a2dd9b237135891a))
* **support:** update dependency teen_process to v2.0.34 ([#19218](https://github.com/appium/appium/issues/19218)) ([c28fcef](https://github.com/appium/appium/commit/c28fcefa725fbce5a0464e57946051e3cb023d85))
* **support:** update dependency teen_process to v2.0.37 ([6d894be](https://github.com/appium/appium/commit/6d894bef6f377ebfdffdeb1adde7a72624844414))
* **support:** update dependency teen_process to v2.0.38 ([a32dc11](https://github.com/appium/appium/commit/a32dc1110125d0f1fac9c66c17f1309145a634c7))
* **support:** update dependency teen_process to v2.0.40 ([2837bcd](https://github.com/appium/appium/commit/2837bcdeb19532fe973a2902db5c8d730252d962))
* **support:** update dependency teen_process to v2.0.41 ([1a34396](https://github.com/appium/appium/commit/1a34396339f71d40353d323feaf78afcf2269fc9))
* **support:** update dependency teen_process to v2.0.42 ([e2a670e](https://github.com/appium/appium/commit/e2a670e1c4a0f061b0bfb08a6b534d9deb7bdd9c))
* **support:** update dependency teen_process to v2.0.45 ([ea731bf](https://github.com/appium/appium/commit/ea731bf117d680496fce453ac42cfee7f05ada60))
* **support:** update dependency teen_process to v2.0.48 ([eebc651](https://github.com/appium/appium/commit/eebc651f31c282baa7e77a6384d3ee8efc0072d6))
* **support:** update dependency teen_process to v2.0.50 ([ca3e5d9](https://github.com/appium/appium/commit/ca3e5d90e4c51fc1dda32154e1d77e65cb44fb54))
* **support:** update dependency teen_process to v2.0.9 ([6c7f3ef](https://github.com/appium/appium/commit/6c7f3ef670d441bdc4b693948a19dcbdc54e4764))
## [2.0.25](https://github.com/appium/appium/compare/@appium/doctor@2.0.24...@appium/doctor@2.0.25) (2023-08-22)
**Note:** Version bump only for package @appium/doctor
## [2.0.24](https://github.com/appium/appium/compare/@appium/doctor@2.0.23...@appium/doctor@2.0.24) (2023-08-21)
### Bug Fixes
* **doctor:** update dependency appium-adb to v9.14.5 ([d5f3a28](https://github.com/appium/appium/commit/d5f3a288c2497d4020b7eabc667814744e69ac48))
## [2.0.23](https://github.com/appium/appium/compare/@appium/doctor@2.0.22...@appium/doctor@2.0.23) (2023-08-17)
### Bug Fixes
* **doctor:** update dependency appium-adb to v9.14.4 ([#18844](https://github.com/appium/appium/issues/18844)) ([a9e86aa](https://github.com/appium/appium/commit/a9e86aabc657bf22df8b1c5f67ab93acc0ced934))
* **doctor:** update dependency inquirer to v8.2.6 ([#18940](https://github.com/appium/appium/issues/18940)) ([a8cc551](https://github.com/appium/appium/commit/a8cc551210c31b014025f7c56b091163f4d15df1))
* **test-support:** update dependency @colors/colors to v1.6.0 ([1358937](https://github.com/appium/appium/commit/1358937db2edf08ce1ebe3dff2f70ac6b07cd373))
## [2.0.22](https://github.com/appium/appium/compare/@appium/doctor@2.0.21...@appium/doctor@2.0.22) (2023-07-24)
### Bug Fixes
* **support:** update dependency teen_process to v2.0.4 ([151c19c](https://github.com/appium/appium/commit/151c19c1c3b4c9b94aba10033c2d863f567d849b))
## [2.0.21](https://github.com/appium/appium/compare/@appium/doctor@2.0.20...@appium/doctor@2.0.21) (2023-07-03)
### Bug Fixes
* **doctor:** update dependency appium-adb to v9.11.7 ([fdd2ac0](https://github.com/appium/appium/commit/fdd2ac098d1b6e0d17b1429defa0123660d85ca9))
## [2.0.20](https://github.com/appium/appium/compare/@appium/doctor@2.0.19...@appium/doctor@2.0.20) (2023-06-29)
**Note:** Version bump only for package @appium/doctor
## [2.0.19](https://github.com/appium/appium/compare/@appium/doctor@2.0.18...@appium/doctor@2.0.19) (2023-06-15)
**Note:** Version bump only for package @appium/doctor
## [2.0.18](https://github.com/appium/appium/compare/@appium/doctor@2.0.17...@appium/doctor@2.0.18) (2023-06-14)
**Note:** Version bump only for package @appium/doctor
## [2.0.17](https://github.com/appium/appium/compare/@appium/doctor@2.0.16...@appium/doctor@2.0.17) (2023-06-14)
### Bug Fixes
* **doctor:** update dependency appium-adb to v9.11.3 ([a0b434d](https://github.com/appium/appium/commit/a0b434dff7a4e425f66915e12266a6e943d10372))
* **doctor:** update dependency appium-adb to v9.11.4 ([f34c75c](https://github.com/appium/appium/commit/f34c75c65068a299ab4d3427233e771e796fc43a))
* **doctor:** update dependency appium-adb to v9.11.5 ([78c6794](https://github.com/appium/appium/commit/78c67942534705133093aa9fa54d84efb9fe2f69))
## [2.0.16](https://github.com/appium/appium/compare/@appium/doctor@2.0.15...@appium/doctor@2.0.16) (2023-05-19)
**Note:** Version bump only for package @appium/doctor
## [2.0.15](https://github.com/appium/appium/compare/@appium/doctor@2.0.14...@appium/doctor@2.0.15) (2023-05-17)
### Bug Fixes
* **doctor:** update dependency appium-adb to v9.11.2 ([e7df6fd](https://github.com/appium/appium/commit/e7df6fd4f054d6ad6eb4125f77e7e0d58bc05ebf))
* **docutils:** update dependency yargs to v17.7.2 ([3d1e4af](https://github.com/appium/appium/commit/3d1e4af858ea21bcf3ab1e1015aa7bdf1776f3ce))
## [2.0.14](https://github.com/appium/appium/compare/@appium/doctor@2.0.13...@appium/doctor@2.0.14) (2023-04-20)
### Bug Fixes
* **doctor:** update dependency appium-adb to v9.11.1 ([45cd434](https://github.com/appium/appium/commit/45cd43488ce3e7a3c32864912d8fb104934f90d8))
## [2.0.13](https://github.com/appium/appium/compare/@appium/doctor@2.0.12...@appium/doctor@2.0.13) (2023-04-14)
**Note:** Version bump only for package @appium/doctor
## [2.0.12](https://github.com/appium/appium/compare/@appium/doctor@2.0.11...@appium/doctor@2.0.12) (2023-04-10)
**Note:** Version bump only for package @appium/doctor
## [2.0.11](https://github.com/appium/appium/compare/@appium/doctor@2.0.10...@appium/doctor@2.0.11) (2023-04-03)
### Bug Fixes
* **doctor:** update dependency appium-adb to v9.11.0 ([d8b6334](https://github.com/appium/appium/commit/d8b633406a68e88cdd945906dde3f23a2a566a71))
## [2.0.10](https://github.com/appium/appium/compare/@appium/doctor@2.0.9...@appium/doctor@2.0.10) (2023-03-28)
**Note:** Version bump only for package @appium/doctor
## [2.0.9](https://github.com/appium/appium/compare/@appium/doctor@2.0.8...@appium/doctor@2.0.9) (2023-03-08)
**Note:** Version bump only for package @appium/doctor
## [2.0.8](https://github.com/appium/appium/compare/@appium/doctor@2.0.7...@appium/doctor@2.0.8) (2023-02-24)
### Bug Fixes
* **docutils:** update dependency yargs to v17.7.0 ([70d65b7](https://github.com/appium/appium/commit/70d65b7d6230dd415f26aaba6d097a3cbe876b47))
* **docutils:** update dependency yargs to v17.7.1 ([2602016](https://github.com/appium/appium/commit/2602016eb38c73d2d9a40f94e95277faf2444305))
## [2.0.7](https://github.com/appium/appium/compare/@appium/doctor@2.0.6...@appium/doctor@2.0.7) (2023-02-09)
**Note:** Version bump only for package @appium/doctor
## [2.0.6](https://github.com/appium/appium/compare/@appium/doctor@2.0.5...@appium/doctor@2.0.6) (2023-01-23)
### Bug Fixes
* **doctor:** update dependency appium-adb to v9.10.22 ([3b213d4](https://github.com/appium/appium/commit/3b213d40e72ccec850f5efb48777b0aee2918cc4))
* **doctor:** update dependency appium-adb to v9.10.23 ([34081b4](https://github.com/appium/appium/commit/34081b44c88a4b2d820eed864c1e9ab36c217d2c))
* **doctor:** update dependency appium-adb to v9.10.24 ([f76a5a7](https://github.com/appium/appium/commit/f76a5a71c96cddac5ad8b8f12d2c03270a28e330))
## [2.0.5](https://github.com/appium/appium/compare/@appium/doctor@2.0.4...@appium/doctor@2.0.5) (2023-01-13)
**Note:** Version bump only for package @appium/doctor
## [2.0.4](https://github.com/appium/appium/compare/@appium/doctor@2.0.3...@appium/doctor@2.0.4) (2023-01-13)
**Note:** Version bump only for package @appium/doctor
## [2.0.3](https://github.com/appium/appium/compare/@appium/doctor@2.0.2...@appium/doctor@2.0.3) (2023-01-13)
**Note:** Version bump only for package @appium/doctor
## [2.0.2](https://github.com/appium/appium/compare/@appium/doctor@2.0.1...@appium/doctor@2.0.2) (2023-01-13)
### Bug Fixes
* **doctor:** update dependency appium-adb to v9.10.20 ([29bf77c](https://github.com/appium/appium/commit/29bf77cf5a623c7417d9c996814498f76cead2a3))
* **doctor:** update dependency appium-adb to v9.10.21 ([ac2c735](https://github.com/appium/appium/commit/ac2c73596810919dcd1c6d294e8c9496eb91e0b9))
## [2.0.1](https://github.com/appium/appium/compare/@appium/doctor@2.0.0...@appium/doctor@2.0.1) (2022-12-21)
### Bug Fixes
- **doctor:** update dependency appium-adb to v9.10.19 ([2cb1af4](https://github.com/appium/appium/commit/2cb1af4357e0e867c28c7045825140d45fa02393))
# [2.0.0](https://github.com/appium/appium/compare/@appium/doctor@1.16.28...@appium/doctor@2.0.0) (2022-12-14)
### Bug Fixes
- **doctor:** update dependency appium-adb to v9.10.16 ([b3e8b47](https://github.com/appium/appium/commit/b3e8b470895e09d8933ad5c10dbbe17c3ad34941))
- **doctor:** update dependency appium-adb to v9.10.17 ([3e815d2](https://github.com/appium/appium/commit/3e815d2a358720fec3b072d92f125dad47be5ca0))
- **doctor:** update dependency appium-adb to v9.10.18 ([8a285cd](https://github.com/appium/appium/commit/8a285cdeceb044d88f657ccd4d5e6939bb15f4eb))
- **doctor:** update dependency inquirer to v8 ([4dd2a46](https://github.com/appium/appium/commit/4dd2a46338a0fb26421c39418c5e20282a400eb3))
- **gulp-plugins:** update dependency yargs to v17.6.1 ([ea1b6a3](https://github.com/appium/appium/commit/ea1b6a3334ee5330abd456a7d4467fc92d94270d))
- **gulp-plugins:** update dependency yargs to v17.6.2 ([a95b2e0](https://github.com/appium/appium/commit/a95b2e0fe6fc646ad5e1fb3479b448ad3a66c880))
- **opencv:** update definitelytyped ([32557f4](https://github.com/appium/appium/commit/32557f4bca5acc2f89cfd3a70f369cebeb94c588))
- chore!: set engines to minimum Node.js v14.17.0 ([a1dbe6c](https://github.com/appium/appium/commit/a1dbe6c43efe76604943a607d402f4c8b864d652))
### Features
- **doctor:** print APPIUM_HOME ([#17625](https://github.com/appium/appium/issues/17625)) ([22d5b2f](https://github.com/appium/appium/commit/22d5b2fd9e0b1f65d5cb78b4dcd45b095537cae1))
- experimental support for typedoc generation ([4746080](https://github.com/appium/appium/commit/4746080e54ed8bb494cbc7c6ce83db503bf6bb52))
### BREAKING CHANGES
- Appium now supports version range `^14.17.0 || ^16.13.0 || >=18.0.0`
## [1.16.28](https://github.com/appium/appium/compare/@appium/doctor@1.16.27...@appium/doctor@1.16.28) (2022-10-14)
**Note:** Version bump only for package @appium/doctor
## [1.16.27](https://github.com/appium/appium/compare/@appium/doctor@1.16.26...@appium/doctor@1.16.27) (2022-10-13)
**Note:** Version bump only for package @appium/doctor
## [1.16.26](https://github.com/appium/appium/compare/@appium/doctor@1.16.25...@appium/doctor@1.16.26) (2022-09-07)
**Note:** Version bump only for package @appium/doctor
## [1.16.25](https://github.com/appium/appium/compare/@appium/doctor@1.16.24...@appium/doctor@1.16.25) (2022-08-10)
**Note:** Version bump only for package @appium/doctor
## [1.16.24](https://github.com/appium/appium/compare/@appium/doctor@1.16.23...@appium/doctor@1.16.24) (2022-08-03)
### Bug Fixes
- **appium,base-driver,base-plugin,doctor,docutils,eslint-config-appium,execute-driver-plugin,fake-driver,fake-plugin,gulp-plugins,images-plugin,opencv,relaxed-caps-plugin,schema,support,test-support,types,universal-xml-plugin:** update engines ([d8d2382](https://github.com/appium/appium/commit/d8d2382327ba7b7db8a4d1cad987c0e60184c92d))
## [1.16.23](https://github.com/appium/appium/compare/@appium/doctor@1.16.22...@appium/doctor@1.16.23) (2022-07-28)
### Bug Fixes
- **doctor:** actually build bin dir, fix dev script ([10421fa](https://github.com/appium/appium/commit/10421fa5ad8c9d2e5916e5a701fefdeb3f5b9969))
- moved type packages to deps of specific packages ([f9129df](https://github.com/appium/appium/commit/f9129dfee32fcc3f89ffcfa69fb83b7c2419c24f))
## [1.16.22](https://github.com/appium/appium/compare/@appium/doctor@1.16.21...@appium/doctor@1.16.22) (2022-06-04)
**Note:** Version bump only for package @appium/doctor
## [1.16.21](https://github.com/appium/appium/compare/@appium/doctor@1.16.20...@appium/doctor@1.16.21) (2022-05-31)
**Note:** Version bump only for package @appium/doctor
## [1.16.20](https://github.com/appium/appium/compare/@appium/doctor@1.16.19...@appium/doctor@1.16.20) (2022-05-31)
### Bug Fixes
- **appium:** fix extension autoinstall postinstall script ([3e2c05d](https://github.com/appium/appium/commit/3e2c05d8a290072484afde34fe5fd968618f6359)), closes [#16924](https://github.com/appium/appium/issues/16924)
## [1.16.19](https://github.com/appium/appium/compare/@appium/doctor@1.16.18...@appium/doctor@1.16.19) (2022-05-03)
**Note:** Version bump only for package @appium/doctor
## [1.16.18](https://github.com/appium/appium/compare/@appium/doctor@1.16.17...@appium/doctor@1.16.18) (2022-05-02)
**Note:** Version bump only for package @appium/doctor
## [1.16.17](https://github.com/appium/appium/compare/@appium/doctor@1.16.16...@appium/doctor@1.16.17) (2022-04-20)
**Note:** Version bump only for package @appium/doctor
## [1.16.16](https://github.com/appium/appium/compare/@appium/doctor@1.16.15...@appium/doctor@1.16.16) (2022-04-20)
**Note:** Version bump only for package @appium/doctor
## [1.16.15](https://github.com/appium/appium/compare/@appium/doctor@1.16.14...@appium/doctor@1.16.15) (2022-04-20)
**Note:** Version bump only for package @appium/doctor
## [1.16.14](https://github.com/appium/appium/compare/@appium/doctor@1.16.13...@appium/doctor@1.16.14) (2022-04-12)
**Note:** Version bump only for package @appium/doctor
## [1.16.13](https://github.com/appium/appium/compare/@appium/doctor@1.16.12...@appium/doctor@1.16.13) (2022-04-12)
**Note:** Version bump only for package @appium/doctor
## [1.16.12](https://github.com/appium/appium/compare/@appium/doctor@1.16.11...@appium/doctor@1.16.12) (2022-04-07)
**Note:** Version bump only for package @appium/doctor
## [1.16.11](https://github.com/appium/appium/compare/@appium/doctor@1.16.10...@appium/doctor@1.16.11) (2022-03-23)
**Note:** Version bump only for package @appium/doctor
## [1.16.10](https://github.com/appium/appium/compare/@appium/doctor@1.16.9...@appium/doctor@1.16.10) (2022-03-22)
**Note:** Version bump only for package @appium/doctor
## [1.16.9](https://github.com/appium/appium/compare/@appium/doctor@1.16.8...@appium/doctor@1.16.9) (2022-01-21)
**Note:** Version bump only for package @appium/doctor
## [1.16.8](https://github.com/appium/appium/compare/@appium/doctor@1.16.7...@appium/doctor@1.16.8) (2022-01-11)
### Bug Fixes
- **support:** allow @u4/opencv4nodejs in addition to the old one, and promote it in doctor instead ([7d8d547](https://github.com/appium/appium/commit/7d8d547fbd5f234922a38c79239391c83d48aa5b))
- Switch colors package to a non-compomised repository ([#16317](https://github.com/appium/appium/issues/16317)) ([40a6f05](https://github.com/appium/appium/commit/40a6f054dca3d94fc88773af9c6336ba12ebfb81))
## [1.16.7](https://github.com/appium/appium/compare/@appium/doctor@1.16.6...@appium/doctor@1.16.7) (2021-11-19)
### Bug Fixes
- **doctor:** move index to support cjs wrapper ([69820ea](https://github.com/appium/appium/commit/69820eaf9369969ebbad038ca53accf973d18aa4))
## [1.16.6](https://github.com/appium/appium/compare/@appium/doctor@1.16.5...@appium/doctor@1.16.6) (2021-11-15)
**Note:** Version bump only for package @appium/doctor
## [1.16.5](https://github.com/appium/appium/compare/@appium/doctor@1.16.4...@appium/doctor@1.16.5) (2021-11-09)
**Note:** Version bump only for package @appium/doctor
## [1.16.4](https://github.com/appium/appium/compare/@appium/doctor@1.16.3...@appium/doctor@1.16.4) (2021-09-16)
**Note:** Version bump only for package @appium/doctor
## [1.16.3](https://github.com/appium/appium/compare/@appium/doctor@1.16.2...@appium/doctor@1.16.3) (2021-09-14)
**Note:** Version bump only for package @appium/doctor
## [1.16.2](https://github.com/appium/appium/compare/@appium/doctor@1.16.1...@appium/doctor@1.16.2) (2021-09-14)
**Note:** Version bump only for package @appium/doctor
## 1.16.1 (2021-08-16)
# 2.0.0-beta (2021-08-13)
### Bug Fixes
- **doctor:** update reference to @appium/idb README ([c20a13e](https://github.com/appium/appium/commit/c20a13ea95070c1fb705147f65254b50af761b95))
- **doctor:** use @appium/test-support ([80c9919](https://github.com/appium/appium/commit/80c991998540faad464ba764df612a443db35ff2))
-39
View File
@@ -1,39 +0,0 @@
# @appium/doctor
> Attempts to diagnose and fix common Appium configuration issues
[![NPM version](http://img.shields.io/npm/v/@appium/doctor.svg)](https://npmjs.org/package/@appium/doctor)
[![Downloads](http://img.shields.io/npm/dm/@appium/doctor.svg)](https://npmjs.org/package/@appium/doctor)
> [!WARNING]
> This package has been deprecated since the Appium server v 2.4.0 and will be removed in the future.
> Use doctor checks (if any exist) integrated into your installed driver or plugin by
> running `appium driver doctor <driver_name>` or `appium plugin doctor <plugin_name>`.
### Install
```
npm install @appium/doctor -g
```
### Usage
```
➜ appium-doctor -h
Usage: appium-doctor.js [options, defaults: --ios --android]
Options:
--ios Check iOS setup [boolean]
--android Check Android setup [boolean]
--dev Check dev setup [boolean]
--debug Show debug messages [boolean]
--yes Always respond yes [boolean]
--no Always respond no [boolean]
--demo Run appium-doctor demo (for dev). [boolean]
-h, --help Show help [boolean]
```
## License
Apache-2.0
-3
View File
@@ -1,3 +0,0 @@
#!/usr/bin/env node
module.exports = require('./build/bin/appium-doctor');
-51
View File
@@ -1,51 +0,0 @@
// transpile:main
import yargs from 'yargs';
import newDoctor from '../lib/factory';
import {configureBinaryLog} from '../lib/utils';
import {configure as configurePrompt} from '../lib/prompt';
import {system} from '@appium/support';
yargs
.strict()
.usage('Usage: $0 [options, defaults: --ios --android]')
.boolean('ios')
.describe('ios', 'Check iOS setup')
.boolean('android')
.describe('android', 'Check Android setup')
.boolean('dev')
.describe('dev', 'Check dev setup')
.boolean('debug')
.describe('debug', 'Show debug messages')
.boolean('yes')
.describe('yes', 'Always respond yes')
.boolean('no')
.describe('no', 'Always respond no')
.boolean('demo')
.describe('demo', 'Run appium-doctor demo (for dev).')
.help('h')
.alias('h', 'help')
.check(function (argv) {
if (!argv.ios && !argv.android && !argv.demo) {
argv.ios = system.isMac();
argv.android = true;
}
return true;
});
// make sure we use the general checks for every test
let opts = Object.assign(
{
general: true,
},
yargs.argv
);
configurePrompt(opts);
configureBinaryLog(opts);
newDoctor(opts)
.run()
.catch(function (e) {
console.error(e); // eslint-disable-line no-console
process.exit(1);
});
-165
View File
@@ -1,165 +0,0 @@
import {DoctorCheck} from './doctor';
import {ok, nok, okOptional, nokOptional, resolveExecutablePath} from './utils';
import {system, fs} from '@appium/support';
import path from 'path';
import EnvVarAndPathCheck from './env';
import '@colors/colors';
import {getAndroidBinaryPath, getSdkRootFromEnv} from 'appium-adb';
import log from './logger';
/**
* @type {import('./factory').DoctorCheckList}
*/
const checks = [];
const javaHome = system.isWindows() ? '%JAVA_HOME%' : '$JAVA_HOME';
checks.push(new EnvVarAndPathCheck('ANDROID_HOME'));
checks.push(new EnvVarAndPathCheck('JAVA_HOME'));
// Check that the PATH includes the jdk's bin directory
export class JavaOnPathCheck extends DoctorCheck {
/**
* @override
*/
async diagnose() {
if (process.env.JAVA_HOME) {
const javaHomeBin = path.resolve(process.env.JAVA_HOME, 'bin');
return (await fs.exists(javaHomeBin))
? ok(`'bin' subfolder exists under '${process.env.JAVA_HOME}'`)
: nok(
`'bin' subfolder does not exist under '${process.env.JAVA_HOME}'. ` +
`Is ${javaHome} set to a proper value?`
);
}
return nok(
`Cannot check ${javaHome} requirements since the environment variable itself is not set`
);
}
/**
* @override
*/
async fix() {
return `Set ${javaHome} environment variable to the root folder path of your local JDK installation`;
}
}
// Check tools
export class AndroidToolCheck extends DoctorCheck {
constructor() {
super();
this.tools = ['adb', 'emulator', `apkanalyzer${system.isWindows() ? '.bat' : ''}`];
this.noBinaries = [];
}
/**
* @override
*/
async diagnose() {
const listOfTools = this.tools.join(', ');
const sdkRoot = getSdkRootFromEnv();
if (!sdkRoot) {
return nok(
`${listOfTools} could not be found because ANDROID_HOME is NOT set!`
);
}
log.info(` Checking ${listOfTools}`);
for (const binary of this.tools) {
try {
log.info(` '${binary}' is in ${await getAndroidBinaryPath(binary)}`);
} catch (e) {
this.noBinaries.push(binary);
}
}
if (this.noBinaries.length > 0) {
return nok(`${this.noBinaries.join(', ')} could NOT be found in ${sdkRoot}!`);
}
return ok(`${listOfTools} exist: ${sdkRoot}`);
}
/**
* @override
*/
async fix() {
if (typeof process.env.ANDROID_HOME === 'undefined') {
return `Manually configure ${'ANDROID_HOME'.bold} and run appium-doctor again.`;
}
return (
`Manually install ${this.noBinaries.join(', ').bold} and add it to ${'PATH'.bold}. ` +
'https://developer.android.com/studio#cmdline-tools and ' +
'https://developer.android.com/studio/intro/update#sdk-manager may help to setup.'
);
}
}
checks.push(new AndroidToolCheck());
checks.push(new JavaOnPathCheck());
export class OptionalAppBundleCheck extends DoctorCheck {
/**
* @override
*/
async diagnose() {
const bundletoolPath = await resolveExecutablePath('bundletool.jar');
return bundletoolPath
? okOptional(`bundletool.jar is installed at: ${bundletoolPath}`)
: nokOptional('bundletool.jar cannot be found');
}
/**
* @override
*/
async fix() {
return (
`${
'bundletool.jar'.bold
} is used to handle Android App Bundle. Please read http://appium.io/docs/en/writing-running-appium/android/android-appbundle/ to install it` +
`${
system.isWindows()
? '. Also consider adding the ".jar" extension into your PATHEXT environment variable in order to fix the problem for Windows'
: ''
}`
);
}
}
checks.push(new OptionalAppBundleCheck());
export class OptionalGstreamerCheck extends DoctorCheck {
GSTREAMER_BINARY = `gst-launch-1.0${system.isWindows() ? '.exe' : ''}`;
GST_INSPECT_BINARY = `gst-inspect-1.0${system.isWindows() ? '.exe' : ''}`;
/**
* @override
*/
async diagnose() {
const gstreamerPath = await resolveExecutablePath(this.GSTREAMER_BINARY);
const gstInspectPath = await resolveExecutablePath(this.GST_INSPECT_BINARY);
return gstreamerPath && gstInspectPath
? okOptional(
`${this.GSTREAMER_BINARY} and ${this.GST_INSPECT_BINARY} are installed at: ${gstreamerPath} and ${gstInspectPath}`
)
: nokOptional(`${this.GSTREAMER_BINARY} and/or ${this.GST_INSPECT_BINARY} cannot be found`);
}
/**
* @override
*/
async fix() {
return (
`${
`${this.GSTREAMER_BINARY} and ${this.GST_INSPECT_BINARY}`.bold
} are used to stream the screen of the device under test. ` +
'Please read https://appium.io/docs/en/writing-running-appium/android/android-screen-streaming/ to install them and for more details'
);
}
}
checks.push(new OptionalGstreamerCheck());
export {EnvVarAndPathCheck};
export default checks;
-88
View File
@@ -1,88 +0,0 @@
// demo rule to test the gui
import {ok, nok} from './utils';
import {fs} from '@appium/support';
import {exec} from 'teen_process';
import {DoctorCheck, FixSkippedError} from './doctor';
import log from './logger';
import {fixIt} from './prompt';
/**
* @type {import('./factory').DoctorCheckList}
*/
const checks = [];
export class DirCheck extends DoctorCheck {
/**
* @param {string} path
*/
constructor(path) {
super({autofix: false});
this.path = path;
}
/**
* @override
*/
async diagnose() {
if (!(await fs.exists(this.path))) {
return nok(`Could NOT find directory at '${this.path}'!`);
}
let stats = await fs.lstat(this.path);
return stats.isDirectory()
? ok(`Found directory at: ${this.path}`)
: nok(`'${this.path}' is NOT a directory!`);
}
/**
* @override
*/
async fix() {
return `Manually create a directory at: ${this.path}`;
}
}
checks.push(new DirCheck('/tmp/appium-doctor'));
checks.push(new DirCheck('/tmp/appium-doctor/demo'));
export class FileCheck extends DoctorCheck {
/**
* @param {string} path
*/
constructor(path) {
super({autofix: true});
this.path = path;
}
/**
* @override
*/
async diagnose() {
return (await fs.exists(this.path))
? ok(`Found file at: ${this.path}`)
: nok(`Could NOT find file at '${this.path}'!`);
}
/**
* @override
*/
async fix() {
log.info(`The following command need be executed: touch '${this.path}'`);
let yesno = await fixIt();
if (yesno === 'yes') {
await exec('touch', [this.path]);
} else {
log.info(`Skipping. You will need to touch '${this.path}' manually.`);
throw new FixSkippedError('bbb');
}
return null;
}
}
checks.push(
new FileCheck('/tmp/appium-doctor/demo/apple.fruit'),
new FileCheck('/tmp/appium-doctor/demo/pear.fruit'),
new FileCheck('/tmp/appium-doctor/demo/orange.fruit'),
);
export default checks;
-87
View File
@@ -1,87 +0,0 @@
import {DoctorCheck} from './doctor';
import {ok, nok, resolveExecutablePath} from './utils';
import {fs, system} from '@appium/support';
import path from 'path';
import '@colors/colors';
/**
* @type {import('./factory').DoctorCheckList}
*/
const checks = [];
// Check PATH binaries
export class BinaryIsInPathCheck extends DoctorCheck {
/**
* @param {string} binary
*/
constructor(binary) {
super();
this.binary = binary;
}
/**
* @override
*/
async diagnose() {
const resolvedPath = await resolveExecutablePath(this.binary);
if (!resolvedPath) {
return nok(`${this.binary} is MISSING in PATH: ${process.env.PATH}`);
}
return ok(`${this.binary} was found at ${resolvedPath}`);
}
/**
* @override
*/
async fix() {
return `Manually install the ${this.binary.bold} binary and add it to ${'PATH'.bold}.`;
}
}
checks.push(
new BinaryIsInPathCheck(system.isWindows() ? 'mvn.bat' : 'mvn'),
new BinaryIsInPathCheck(system.isWindows() ? 'ant.bat' : 'ant'),
new BinaryIsInPathCheck(system.isWindows() ? 'adb.exe' : 'adb'),
);
// Check Android SDKs
export class AndroidSdkExists extends DoctorCheck {
/**
* @param {string} sdk
*/
constructor(sdk) {
super();
this.sdk = sdk;
}
/**
* @override
*/
async diagnose() {
if (typeof process.env.ANDROID_HOME === 'undefined') {
return nok(`${this.sdk} could not be found because ANDROID_HOME is NOT set!`);
}
let sdkPath = path.resolve(process.env.ANDROID_HOME, path.join('platforms', this.sdk));
return (await fs.exists(sdkPath))
? ok(`${this.sdk} was found at: ${sdkPath}`)
: nok(`${this.sdk} could NOT be found at '${sdkPath}'!`);
}
/**
* @override
*/
async fix() {
if (typeof process.env.ANDROID_HOME === 'undefined') {
return `Manually configure ${'ANDROID_HOME'.bold}.`;
}
return `Manually install the ${this.sdk.bold} sdk.`;
}
}
checks.push(
new AndroidSdkExists('android-16'),
new AndroidSdkExists('android-19'),
);
export default checks;
-284
View File
@@ -1,284 +0,0 @@
import '@colors/colors';
import _ from 'lodash';
import log from './logger';
import {fs} from '@appium/support';
const {version} = fs.readPackageJsonFrom(__dirname);
class FixSkippedError extends Error {}
/**
* Create interface for other Doctors
*/
class DoctorCheck {
/**
* @param {DoctorOpts} [opts={}]
*/
constructor(opts = {}) {
this.autofix = !!opts.autofix;
}
/**
* Every doctor diagnose the symptoms
* @returns {Promise<import('./utils').CheckResult>}
* @throws {Error}
*/
async diagnose() {
throw new Error('Not Implemented!');
}
/**
* Every doctor suggest the solutions to fix the sickness
* @returns {Promise<string|null>}
* @throws {Error}
*/
async fix() {
// return string for manual fixes.
throw new Error('Not Implemented!');
}
}
class Doctor {
constructor() {
/**
* All the sub check goes here after register
* @type {DoctorCheck[]}
*/
this.checks = [];
/** @type {DoctorCheck[]} */
this.checkOptionals = [];
/** @type {DoctorIssue[]} */
this.toFix = [];
/** @type {DoctorIssue[]} */
this.toFixOptionals = [];
}
/**
* Register all the sub check and combine them together
* @param {DoctorCheck[] | DoctorCheck} checks
*/
register(checks) {
this.checks.push(...(Array.isArray(checks) ? checks : [checks]));
}
/**
* The doctor shows the report
*/
async diagnose() {
log.info(`### Diagnostic for ${'necessary'.green} dependencies starting ###`);
this.toFix = [];
for (const check of this.checks) {
const res = await check.diagnose();
if (res.optional) {
this.checkOptionals.push(check);
continue;
}
await this.diagnosticResultMessage(res, this.toFix, check);
}
log.info(
`### Diagnostic for necessary dependencies completed, ${await this.fixMessage(
this.toFix.length
)}. ###`
);
log.info('');
log.info(`### Diagnostic for ${'optional'.yellow} dependencies starting ###`);
this.toFixOptionals = [];
for (const checkOptional of this.checkOptionals) {
await this.diagnosticResultMessage(
await checkOptional.diagnose(),
this.toFixOptionals,
checkOptional
);
}
log.info(
`### Diagnostic for optional dependencies completed, ${await this.fixMessage(
this.toFixOptionals.length,
true
)}. ###`
);
log.info('');
}
/**
* @param {DoctorIssue[]} fixes
* @param {DoctorIssue[]} optionalFixes
*/
async reportManualFixes(fixes, optionalFixes) {
const manualFixes = _.filter(fixes, (f) => !f?.check?.autofix);
const manualFixesOptional = _.filter(optionalFixes, (f) => !f?.check?.autofix);
if (manualFixes.length > 0) {
log.info('### Manual Fixes Needed ###');
log.info('The configuration cannot be automatically fixed, please do the following first:');
/** @type {string[]} */
const fixMessages = [];
for (const f of manualFixes) {
const message = await f.check.fix();
if (message) {
fixMessages.push(message);
}
}
for (const m of _.uniq(fixMessages)) {
log.warn(` \u279C ${m}`);
}
log.info('');
}
if (manualFixesOptional.length > 0) {
log.info('### Optional Manual Fixes ###');
log.info('The configuration can install optionally. Please do the following manually:');
/** @type {string[]} */
const fixMessages = [];
for (const f of manualFixesOptional) {
const message = await f.check.fix();
if (message) {
fixMessages.push(message);
}
}
for (const m of _.uniq(fixMessages)) {
log.warn(` \u279C ${m}`);
}
log.info('');
}
if (manualFixes.length > 0 || manualFixesOptional.length > 0) {
log.info('###');
log.info('');
log.info('Bye! Run appium-doctor again when all manual fixes have been applied!');
log.info('');
return true;
}
return false;
}
/**
* @param {DoctorIssue} f
*/
async runAutoFix(f) {
log.info(`### Fixing: ${f.error} ###`);
try {
await f.check.fix();
} catch (err) {
if (err instanceof FixSkippedError) {
log.info(`### Skipped fix ###`);
return;
} else {
log.warn(`${err}`.replace(/\n$/g, ''));
log.info(`### Fix did not succeed ###`);
return;
}
}
log.info('Checking if this was fixed:');
const res = await f.check.diagnose();
if (res.ok) {
f.fixed = true;
log.info(` ${'\u2714'.green} ${res.message}`);
log.info(`### Fix was successfully applied ###`);
} else {
log.info(` ${'\u2716'.red} ${res.message}`);
log.info(`### Fix was applied but issue remains ###`);
}
}
async runAutoFixes() {
const autoFixes = _.filter(this.toFix, (f) => f?.check?.autofix);
for (const f of autoFixes) {
await this.runAutoFix(f);
log.info('');
}
if (_.find(autoFixes, (f) => !f.fixed)) {
// a few issues remain.
log.info('Bye! A few issues remain, fix manually and/or rerun appium-doctor!');
} else {
// nothing left to fix.
log.info('Bye! All issues have been fixed!');
}
log.info('');
}
async run() {
log.info(`Appium Doctor v.${version}`);
log.warn(`This package is deprecated since the Appium server version 2.4.0 and will be removed in the future.`);
log.warn(`Use doctor checks (if any exist) integrated into your installed driver or plugin by ` +
`running \`appium driver doctor <driver_name>\` or \`appium plugin doctor <plugin_name>\`.`);
await this.diagnose();
if (await this.reportSuccess(this.toFix.length, this.toFixOptionals.length)) {
return;
}
if (await this.reportManualFixes(this.toFix, this.toFixOptionals)) {
return;
}
await this.runAutoFixes();
}
/**
* Generating messages
*
* @param {import('./utils').CheckResult} result
* @param {DoctorIssue[]} toFixList
* @param {DoctorCheck} check
*/
async diagnosticResultMessage(result, toFixList, check) {
if (result.ok) {
log.info(` ${'\u2714'.green} ${result.message}`);
} else {
const errorMessage = result.optional
? ` ${'\u2716'.yellow} ${result.message}`
: ` ${'\u2716'.red} ${result.message}`;
log.warn(errorMessage);
toFixList.push({
error: errorMessage,
check,
});
}
}
/**
* @param {number} length
* @param {boolean} [optional=false]
* @returns {Promise<string>}
*/
async fixMessage(length, optional = false) {
let message;
switch (length) {
case 0:
message = 'no fix';
break;
case 1:
message = 'one fix';
break;
default:
message = `${length} fixes`;
}
return `${message} ${optional ? 'possible' : 'needed'}`;
}
/**
* @param {number} length
* @param {number} lengthOptional
* @returns {Promise<boolean>}
*/
async reportSuccess(length, lengthOptional) {
if (length === 0 && lengthOptional === 0) {
log.info('Everything looks good, bye!');
log.info('');
return true;
} else {
return false;
}
}
}
export {Doctor, DoctorCheck, FixSkippedError};
/**
* @typedef DoctorOpts
* @property {boolean?} [autofix]
*/
/**
* @typedef DoctorIssue
* @property {DoctorCheck} check
* @property {string} error
* @property {boolean} [fixed]
*/
-49
View File
@@ -1,49 +0,0 @@
import {fs, system} from '@appium/support';
import {DoctorCheck} from './doctor';
import {ok, nok} from './utils';
import '@colors/colors';
// Check env variables
export class EnvVarAndPathCheck extends DoctorCheck {
/**
* @param {string} varName
*/
constructor(varName) {
super();
this.varName = varName;
}
/**
* @override
*/
async diagnose() {
let varValue = process.env[this.varName];
if (typeof varValue === 'undefined') {
return nok(`${this.varName} environment variable is NOT set!`);
}
if (await fs.exists(varValue)) {
return ok(`${this.varName} is set to: ${varValue}`);
}
let err_msg = `${this.varName} is set to '${varValue}' but this is NOT a valid path!`;
// On Windows, when the env var has %LOCALAPPDATA%, fs.exists cannot resolve the path.
// Then, it would be safe to request the user to set the full path instead.
if (system.isWindows() && varValue.includes('%LOCALAPPDATA%')) {
err_msg += ` Please set '${process.env.LOCALAPPDATA}' instead of '%LOCALAPPDATA%' as the environment variable.`;
}
return nok(err_msg);
}
/**
* @override
*/
async fix() {
return (
`Make sure the environment variable ${this.varName.bold} is properly configured for the Appium process. ` +
`Refer https://github.com/appium/java-client/blob/master/docs/environment.md for more details.`
);
}
}
export default EnvVarAndPathCheck;
-37
View File
@@ -1,37 +0,0 @@
import _ from 'lodash';
import {Doctor} from './doctor';
import generalChecks from './general';
import iosChecks from './ios';
import androidChecks from './android';
import devChecks from './dev';
import demoChecks from './demo';
/**
* @type {DoctorGroup}
*/
const checks = {generalChecks, iosChecks, androidChecks, devChecks, demoChecks};
const newDoctor = (/** @type {Record<string, any>} */ opts) => {
const doctor = new Doctor();
for (const [k, v] of _.toPairs(opts)) {
if (v) {
doctor.register(checks[`${k}Checks`] || []);
}
}
return doctor;
};
export default newDoctor;
/**
* @typedef {import('./doctor').DoctorCheck[]} DoctorCheckList
*/
/**
* @typedef DoctorGroup - Contain a group of Doctors
* @property {DoctorCheckList} generalChecks - Check AppiumHome, NodeBinary, NodeVersion, ffmpeg, mjpeg-consumer
* @property {DoctorCheckList} iosChecks - Check if iOS toolchains are installed
* @property {DoctorCheckList} androidChecks - Check if Android toolchains are installed
* @property {DoctorCheckList} devChecks - Check Path Binary and Android SDKs
* @property {DoctorCheckList} demoChecks - Check /tmp/appium-doctor/demo/*
*/
-140
View File
@@ -1,140 +0,0 @@
import {ok, nok, okOptional, nokOptional, resolveExecutablePath, getNpmPackageInfo} from './utils';
import {exec} from 'teen_process';
import {DoctorCheck} from './doctor';
import NodeDetector from './node-detector';
import {util, env} from '@appium/support';
import '@colors/colors';
/**
* @type {import('./factory').DoctorCheckList}
*/
const checks = [];
export class AppiumHomeCheck extends DoctorCheck {
/**
* @override
*/
async diagnose() {
return ok(`APPIUM_HOME is ${await env.resolveAppiumHome()}`);
}
/**
* @override
*/
async fix() {
return null;
}
}
checks.push(new AppiumHomeCheck());
// Node Binary
export class NodeBinaryCheck extends DoctorCheck {
/**
* @override
*/
async diagnose() {
let nodePath = await NodeDetector.detect();
return nodePath
? ok(`The Node.js binary was found at: ${nodePath}`)
: nok('The Node.js binary was NOT found!');
}
/**
* @override
*/
async fix() {
return `Manually setup ${'Node.js'.bold}.`;
}
}
checks.push(new NodeBinaryCheck());
const REQUIRED_NODE_VERSION = '14.0.0';
// Node version
export class NodeVersionCheck extends DoctorCheck {
/**
* @override
*/
async diagnose() {
let nodePath = await NodeDetector.detect();
if (!nodePath) {
return nok('Node is not installed, so no version to check!');
}
let {stdout} = await exec(nodePath, ['--version']);
let versionString = stdout.replace('v', '').trim();
try {
return util.compareVersions(REQUIRED_NODE_VERSION, '<=', versionString)
? ok(`Node version is ${versionString}`)
: nok(`Node version should be at least ${REQUIRED_NODE_VERSION}!`);
} catch {
return nok(`Unable to find node version (version = '${versionString}')`);
}
}
/**
* @override
*/
async fix() {
return `Manually upgrade ${'Node.js'.bold}.`;
}
}
checks.push(new NodeVersionCheck());
export class OptionalFfmpegCommandCheck extends DoctorCheck {
/**
* @override
*/
async diagnose() {
const ffmpegPath = await resolveExecutablePath('ffmpeg');
return ffmpegPath
? okOptional(
`ffmpeg is installed at: ${ffmpegPath}. ${
(await exec('ffmpeg', ['-version'])).stdout.split('\n')[0]
}`
)
: nokOptional('ffmpeg cannot be found');
}
/**
* @override
*/
async fix() {
return `${
'ffmpeg'.bold
} is needed to record screen features. Please read https://www.ffmpeg.org/ to install it`;
}
}
checks.push(new OptionalFfmpegCommandCheck());
export class OptionalMjpegConsumerCommandCheck extends DoctorCheck {
/**
* @override
*/
async diagnose() {
const packageName = 'mjpeg-consumer';
const packageInfo = await getNpmPackageInfo(packageName);
if (packageInfo) {
return okOptional(
`${packageName} is installed at: ${packageInfo.path}. Installed version is: ${packageInfo.version}`
);
}
return nokOptional(`${packageName} cannot be found.`);
}
/**
* @override
*/
async fix() {
return `${
'mjpeg-consumer'.bold
} module is required to use MJPEG-over-HTTP features. Please install it with 'npm i -g mjpeg-consumer'.`;
}
}
checks.push(new OptionalMjpegConsumerCommandCheck());
export default checks;
/**
* @typedef {import('./utils').CheckResult} UtilsResult
*/
-4
View File
@@ -1,4 +0,0 @@
import newDoctor from './factory';
import {Doctor, DoctorCheck} from './doctor';
export {newDoctor, Doctor, DoctorCheck};
-224
View File
@@ -1,224 +0,0 @@
import {ok, nok, okOptional, nokOptional, resolveExecutablePath} from './utils'; // eslint-disable-line
import {fs} from '@appium/support';
import {exec} from 'teen_process';
import {DoctorCheck, FixSkippedError} from './doctor';
import log from './logger';
import {fixIt} from './prompt';
import EnvVarAndPathCheck from './env';
import '@colors/colors';
/**
* @type {import('./factory').DoctorCheckList}
*/
const checks = [];
export const fixes = {};
// Check for Xcode.
export class XcodeCheck extends DoctorCheck {
/**
* @override
*/
async diagnose() {
let xcodePath;
try {
// https://github.com/appium/appium/issues/12093#issuecomment-459358120 can happen
await exec('xcrun', ['simctl', 'help']);
} catch (err) {
return nok('Error running xcrun simctl');
}
try {
const {stdout} = await exec('xcode-select', ['-p']);
xcodePath = (stdout || '').replace('\n', '');
} catch (err) {
return nok('Xcode is NOT installed!');
}
return xcodePath && (await fs.exists(xcodePath))
? ok(`Xcode is installed at: ${xcodePath}`)
: nok(`Xcode cannot be found at '${xcodePath}'!`);
}
/**
* @override
*/
async fix() {
return `Manually install ${
'Xcode'.bold
}, and make sure 'xcode-select -p' command shows proper path like '/Applications/Xcode.app/Contents/Developer'`;
}
}
checks.push(new XcodeCheck());
// Check for Xcode Command Line Tools.
export class XcodeCmdLineToolsCheck extends DoctorCheck {
constructor() {
super({autofix: true});
}
/**
* @override
*/
async diagnose() {
const errMess = 'Xcode Command Line Tools are NOT installed!';
try {
// https://stackoverflow.com/questions/15371925/how-to-check-if-command-line-tools-is-installed
const stdout = (await exec('xcode-select', ['-p'])).stdout;
return ok(`Xcode Command Line Tools are installed in: ${stdout.trim()}`);
} catch (err) {
log.debug(err);
return nok(errMess);
}
}
/**
* @override
*/
async fix() {
log.info(`The following command need be executed: xcode-select --install`);
let yesno = await fixIt();
if (yesno === 'yes') {
await exec('xcode-select', ['--install']);
} else {
log.info(`Skipping. You will need to install ${'Xcode'.bold} manually.`);
throw new FixSkippedError();
}
return null;
}
}
checks.push(new XcodeCmdLineToolsCheck());
// Dev Tools Security
export class DevToolsSecurityCheck extends DoctorCheck {
/**
* @override
*/
async diagnose() {
const errMess = 'DevToolsSecurity is NOT enabled!';
let stdout;
try {
stdout = (await exec('DevToolsSecurity', [])).stdout;
} catch (err) {
log.debug(err);
return nok(errMess);
}
return stdout && stdout.match(/enabled/) ? ok('DevToolsSecurity is enabled.') : nok(errMess);
}
}
checks.push(
new DevToolsSecurityCheck(),
new EnvVarAndPathCheck('HOME'),
);
export class OptionalLyftCommandCheck extends DoctorCheck {
/**
* @override
*/
async diagnose() {
const lyftCmd = await resolveExecutablePath('set-simulator-location');
if (lyftCmd) {
return okOptional('set-simulator-location is installed');
}
return nokOptional('set-simulator-location is not installed');
}
/**
* @override
*/
async fix() {
return (
`${'set-simulator-location'.bold} is needed to set location for Simulator. ` +
'Please read https://github.com/lyft/set-simulator-location to install it'
);
}
}
checks.push(new OptionalLyftCommandCheck());
export class OptionalIdbCommandCheck extends DoctorCheck {
/**
* @override
*/
async diagnose() {
const fbIdbPath = await resolveExecutablePath('idb');
const fbCompanionIdbPath = await resolveExecutablePath('idb_companion');
if (fbIdbPath && fbCompanionIdbPath) {
return okOptional('idb and idb_companion are installed');
}
if (!fbIdbPath && fbCompanionIdbPath) {
return nokOptional('idb is not installed');
} else if (fbIdbPath && !fbCompanionIdbPath) {
return nokOptional('idb_companion is not installed');
}
return nokOptional('idb and idb_companion are not installed');
}
/**
* @override
*/
async fix() {
return `Why ${'idb'.bold} is needed and how to install it: ${
OptionalIdbCommandCheck.idbReadmeURL
}`;
}
}
// link to idb README.md
// https://github.com/appium/appium-ios/blob/main/packages/idb/README.md
OptionalIdbCommandCheck.idbReadmeURL = 'https://git.io/JnxQc';
checks.push(new OptionalIdbCommandCheck());
export class OptionalApplesimutilsCommandCheck extends DoctorCheck {
/**
* @override
*/
async diagnose() {
const applesimutilsPath = await resolveExecutablePath('applesimutils');
return applesimutilsPath
? okOptional(
`applesimutils is installed at: ${applesimutilsPath}. Installed versions are: ${(
await exec('brew', ['list', '--versions', 'applesimutils'])
).stdout.trim()}`
)
: nokOptional('applesimutils cannot be found');
}
/**
* @override
*/
async fix() {
return `Why ${
'applesimutils'.bold
} is needed and how to install it: ` +
`https://github.com/appium/appium-xcuitest-driver/blob/master/docs/execute-methods.md#mobile-setpermission`;
}
}
checks.push(new OptionalApplesimutilsCommandCheck());
export class OptionalIOSDeployCommandCheck extends DoctorCheck {
/**
* @override
*/
async diagnose() {
const iosDeployPath = await resolveExecutablePath('ios-deploy');
return iosDeployPath
? okOptional(
`ios-deploy is installed at: ${iosDeployPath}. Installed version is: ${(
await exec(iosDeployPath, ['-V'])
).stdout.trim()}`
)
: nokOptional('ios-deploy cannot be found');
}
/**
* @override
*/
async fix() {
return `${
'ios-deploy'.bold
} is used as a fallback command to install iOS applications to real device. ` +
`Please read https://github.com/ios-control/ios-deploy/ to install it`;
}
}
checks.push(new OptionalIOSDeployCommandCheck());
export default checks;
-5
View File
@@ -1,5 +0,0 @@
import {logger} from '@appium/support';
const log = logger.getLogger('AppiumDoctor');
export default log;
-35
View File
@@ -1,35 +0,0 @@
import log from './logger';
import {resolveExecutablePath} from './utils';
// Look for node
export class NodeDetector {
/**
* @returns {Promise<string?>}
*/
static async retrieveUsingSystemCall() {
const nodePath = await resolveExecutablePath('node');
if (!nodePath) {
log.debug(`Node binary not found in PATH: ${process.env.PATH}`);
return null;
}
log.debug(`Node binary found at: ${nodePath}`);
return nodePath;
}
/**
* @returns {Promise<string?>}
*/
static async detect() {
const nodePath = await NodeDetector.retrieveUsingSystemCall();
if (nodePath) {
return nodePath;
} else {
log.warn('The node binary could not be found.');
return null;
}
}
}
export default NodeDetector;
-53
View File
@@ -1,53 +0,0 @@
import { prompt } from './utils';
/**
* @type {string|undefined}
*/
let persistentResponse;
const fixItQuestion = {
type: 'list',
name: 'confirmation',
message: 'Fix it:',
choices: ['yes', 'no', 'always', 'never'],
/**
*
* @param {string} val
* @returns {string}
*/
filter(val) {
return val.toLowerCase();
},
};
/**
* @param {Record<string, any>} opts
*/
export function configure(opts) {
if (opts.yes) {
persistentResponse = 'yes';
}
if (opts.no) {
persistentResponse = 'no';
}
}
/**
* @returns {void}
*/
export function clear() {
persistentResponse = undefined;
}
/**
* @returns {Promise<string|undefined>}
*/
export async function fixIt() {
if (persistentResponse) {
return persistentResponse;
}
const resp = await prompt(fixItQuestion);
persistentResponse = resp.confirmation === 'always' ? 'yes' : persistentResponse;
persistentResponse = resp.confirmation === 'never' ? 'no' : persistentResponse;
return persistentResponse || resp.confirmation;
}
-135
View File
@@ -1,135 +0,0 @@
import log from '../lib/logger';
import {fs, system} from '@appium/support';
import {exec} from 'teen_process';
import {isFunction} from 'lodash';
/**
* @param {string} message
* @returns {CheckResult}
*/
export function ok(message) {
return {ok: true, optional: false, message};
}
/**
* @param {string} message
* @returns {CheckResult}
*/
export function nok(message) {
return {ok: false, optional: false, message};
}
/**
* @param {string} message
* @returns {CheckResult}
*/
export function okOptional(message) {
return {ok: true, optional: true, message};
}
/**
* @param {string} message
* @returns {CheckResult}
*/
export function nokOptional(message) {
return {ok: false, optional: true, message};
}
/**
* @param {any} question https://www.npmjs.com/package/inquirer#question
* @returns {Promise<{confirmation: string}>}
*/
export async function prompt(question) {
const {default: inquirer} = await import('inquirer');
return await inquirer.prompt(question);
}
let onLogMessageHandler = null;
export function configureBinaryLog(opts) {
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);
};
internalLogger.level = opts.debug ? 'debug' : 'info';
}
/**
* If {@link configureBinaryLog} was called, this will restore the original `log` function.
*/
export function resetLog() {
if (onLogMessageHandler) {
log.unwrap().off('log', onLogMessageHandler);
onLogMessageHandler = null;
}
}
/**
* Return an executable path of cmd
*
* @param {string} cmd Standard output by command
* @return {Promise<string?>} The full path of cmd. `null` if the cmd is not found.
*/
export async function resolveExecutablePath(cmd) {
let executablePath;
try {
executablePath = await fs.which(cmd);
if (executablePath && (await fs.exists(executablePath))) {
return executablePath;
}
} catch (err) {
if (/not found/gi.test(err.message)) {
log.debug(err);
} else {
log.warn(err);
}
}
log.debug(`No executable path of '${cmd}'.`);
if (executablePath) {
log.debug(`Does '${executablePath}' exist?`);
}
return null;
}
/**
* @typedef NpmPackageInfo
* @property {string} version - version
* @property {string} path - A path to npm root
*/
/**
* Returns the path and version of given package name
* @param {string} packageName A package name to get path and version data
* @return {Promise<NpmPackageInfo?>}
*/
export async function getNpmPackageInfo(packageName) {
const npmPath = await resolveExecutablePath(`npm${system.isWindows() ? `.cmd` : ''}`);
if (!npmPath) {
return null;
}
let pJson = {};
try {
const {stdout} = await exec(npmPath, ['list', '-g', '-l', '-j', packageName]);
pJson = JSON.parse(stdout);
} catch (err) {
log.debug(err);
return null;
}
if (pJson.dependencies && pJson.dependencies[packageName]) {
return {version: pJson.dependencies[packageName].version, path: pJson.path};
}
return null;
}
/**
* @typedef CheckResult
* @property {boolean} ok
* @property {boolean} optional
* @property {string} message
*/
-67
View File
@@ -1,67 +0,0 @@
{
"name": "@appium/doctor",
"version": "2.1.14",
"description": "Test environment for fitness to run Appium",
"keywords": [
"automation",
"javascript",
"selenium",
"webdriver",
"ios",
"android",
"firefoxos",
"testing"
],
"homepage": "https://appium.io",
"bugs": {
"url": "https://github.com/appium/appium/issues"
},
"repository": {
"type": "git",
"url": "https://github.com/appium/appium.git",
"directory": "packages/doctor"
},
"license": "Apache-2.0",
"author": "https://github.com/appium",
"main": "./appium-doctor.js",
"bin": {
"appium-doctor": "./appium-doctor.js"
},
"directories": {
"lib": "lib"
},
"files": [
"appium-doctor.js",
"lib",
"bin",
"build",
"tsconfig.json",
"!build/tsconfig.tsbuildinfo"
],
"scripts": {
"test": "npm run test:unit",
"test:smoke": "node ./build/lib/index.js",
"test:unit": "mocha \"./test/unit/**/*.spec.js\""
},
"dependencies": {
"@appium/support": "^5.1.7",
"@colors/colors": "1.6.0",
"@types/bluebird": "3.5.42",
"@types/teen_process": "2.0.4",
"appium-adb": "12.7.0",
"bluebird": "3.7.2",
"inquirer": "9.2.12",
"lodash": "4.17.21",
"source-map-support": "0.5.21",
"teen_process": "2.2.0",
"yargs": "17.7.2"
},
"engines": {
"node": "^14.17.0 || ^16.13.0 || >=18.0.0",
"npm": ">=8"
},
"publishConfig": {
"access": "public"
},
"gitHead": "8480a85ce2fa466360e0fb1a7f66628331907f02"
}
-199
View File
@@ -1,199 +0,0 @@
import path from 'node:path';
import {
EnvVarAndPathCheck,
AndroidToolCheck,
OptionalAppBundleCheck,
OptionalGstreamerCheck,
} from '../../lib/android';
import {fs, system} from '@appium/support';
import * as adb from 'appium-adb';
import * as utils from '../../lib/utils';
import * as tp from 'teen_process';
import {withMocks, stubEnv} from '@appium/test-support';
import B from 'bluebird';
import {removeColors} from './helper';
import {createSandbox} from 'sinon';
describe('android', function () {
const apkAnalyzerFilename = system.isWindows() ? 'apkanalyzer.bat' : 'apkanalyzer';
const gstLaunchFilename = system.isWindows() ? 'gst-launch-1.0.exe' : 'gst-launch-1.0';
const gstInspectFilename = system.isWindows() ? 'gst-inspect-1.0.exe' : 'gst-inspect-1.0';
before(async function () {
const chai = await import('chai');
const chaiAsPromised = await import('chai-as-promised');
chai.use(chaiAsPromised.default);
chai.should();
});
describe(
'EnvVarAndPathCheck',
withMocks({fs}, (mocks) => {
stubEnv();
let check = new EnvVarAndPathCheck('ANDROID_HOME');
it('autofix', function () {
check.autofix.should.not.be.ok;
});
it('diagnose - success', async function () {
process.env.ANDROID_HOME = '/a/b/c/d';
mocks.fs.expects('exists').once().returns(B.resolve(true));
(await check.diagnose()).should.deep.equal({
ok: true,
optional: false,
message: 'ANDROID_HOME is set to: /a/b/c/d',
});
mocks.verify();
});
it('failure - not set', async function () {
delete process.env.ANDROID_HOME;
const {ok, optional, message} = await check.diagnose();
ok.should.be.false;
optional.should.be.false;
message.should.not.be.empty;
mocks.verify();
});
it('failure - file not exists', async function () {
process.env.ANDROID_HOME = '/a/b/c/d';
mocks.fs.expects('exists').once().returns(B.resolve(false));
(await check.diagnose()).should.deep.equal({
ok: false,
optional: false,
message: "ANDROID_HOME is set to '/a/b/c/d' " + 'but this is NOT a valid path!',
});
mocks.verify();
});
it('fix', async function () {
removeColors(await check.fix()).should.contain('ANDROID_HOME');
});
})
);
describe(
'AndroidToolCheck', function () {
// withMocks won't work with ESM modules: https://github.com/sinonjs/sinon/issues/2530
/** @type {sinon.SinonSandbox} */
let sandbox;
beforeEach(function () {
sandbox = createSandbox();
});
afterEach(function () {
sandbox.restore();
});
stubEnv();
const check = new AndroidToolCheck();
it('autofix', function () {
check.autofix.should.not.be.ok;
});
it('diagnose - success', async function () {
process.env.ANDROID_HOME = '/a/b/c/d';
sandbox.stub(adb, 'getAndroidBinaryPath')
.get(() => sandbox.fake.resolves('/path/to/binary'));
(await check.diagnose()).should.deep.equal({
ok: true,
optional: false,
message: `adb, emulator, ${apkAnalyzerFilename} exist: /a/b/c/d`,
});
});
it('diagnose - failure - no ANDROID_HOME', async function () {
delete process.env.ANDROID_HOME;
delete process.env.ANDROID_SDK_ROOT;
(await check.diagnose()).should.deep.equal({
ok: false,
optional: false,
message: `adb, emulator, ${apkAnalyzerFilename} could not be found because ANDROID_HOME is NOT set!`,
});
});
it('diagnose - failure - path not valid', async function () {
process.env.ANDROID_HOME = '/a/b/c/d';
sandbox.stub(adb, 'getAndroidBinaryPath')
.get(() => sandbox.fake.throws());
(await check.diagnose()).should.deep.equal({
ok: false,
optional: false,
message: `adb, emulator, ${apkAnalyzerFilename} could NOT be found in /a/b/c/d!`,
});
});
it('fix - ANDROID_HOME', async function () {
delete process.env.ANDROID_HOME;
removeColors(await check.fix()).should.equal(
'Manually configure ANDROID_HOME ' + 'and run appium-doctor again.'
);
});
it('fix - install', async function () {
process.env.ANDROID_HOME = path.resolve('/', 'a', 'b', 'c', 'd');
removeColors(await check.fix()).should.equal(
`Manually install adb, emulator, ${apkAnalyzerFilename} and add it to PATH. https://developer.android.com/studio#cmdline-tools and https://developer.android.com/studio/intro/update#sdk-manager may help to setup.`
);
});
}
);
describe(
'OptionalAppBundleCheck',
withMocks({tp, utils}, (mocks) => {
let check = new OptionalAppBundleCheck();
it('autofix', function () {
check.autofix.should.not.be.ok;
});
it('diagnose - success', async function () {
mocks.utils.expects('resolveExecutablePath').once().returns('path/to/bundletool.jar');
(await check.diagnose()).should.deep.equal({
ok: true,
optional: true,
message: 'bundletool.jar is installed at: path/to/bundletool.jar',
});
mocks.verify();
});
it('diagnose - failure', async function () {
mocks.utils.expects('resolveExecutablePath').once().returns(false);
(await check.diagnose()).should.deep.equal({
ok: false,
optional: true,
message: 'bundletool.jar cannot be found',
});
mocks.verify();
});
it('fix', async function () {
removeColors(await check.fix()).should.match(
new RegExp(
'bundletool.jar is used to handle Android App Bundle. Please read http://appium.io/docs/en/writing-running-appium/android/android-appbundle/ to install it'
)
);
});
})
);
describe(
'OptionalGstreamerCheck',
withMocks({tp, utils}, (mocks) => {
let check = new OptionalGstreamerCheck();
it('autofix', function () {
check.autofix.should.not.be.ok;
});
it('diagnose - success', async function () {
mocks.utils.expects('resolveExecutablePath').once().returns('path/to/gst-launch');
mocks.utils.expects('resolveExecutablePath').once().returns('path/to/gst-inspect');
(await check.diagnose()).should.deep.equal({
ok: true,
optional: true,
message: `${gstLaunchFilename} and ${gstInspectFilename} are installed at: path/to/gst-launch and path/to/gst-inspect`,
});
mocks.verify();
});
it('diagnose - failure', async function () {
mocks.utils.expects('resolveExecutablePath').twice().returns(false);
(await check.diagnose()).should.deep.equal({
ok: false,
optional: true,
message: `${gstLaunchFilename} and/or ${gstInspectFilename} cannot be found`,
});
mocks.verify();
});
it('fix', async function () {
removeColors(await check.fix()).should.equal(
`${gstLaunchFilename} and ${gstInspectFilename} are used to stream the screen of the device under test. Please read https://appium.io/docs/en/writing-running-appium/android/android-screen-streaming/ to install them and for more details`
);
});
})
);
});
-133
View File
@@ -1,133 +0,0 @@
import {DirCheck, FileCheck} from '../../lib/demo';
import {fs} from '@appium/support';
import * as tp from 'teen_process';
import * as prompt from '../../lib/prompt';
import log from '../../lib/logger';
import {FixSkippedError} from '../../lib/doctor';
import {withMocks, withSandbox, stubLog} from '@appium/test-support';
import B from 'bluebird';
describe('demo', function () {
before(async function () {
const chai = await import('chai');
const chaiAsPromised = await import('chai-as-promised');
chai.use(chaiAsPromised.default);
chai.should();
});
describe(
'DirCheck',
withMocks({fs}, (mocks) => {
let check = new DirCheck('/a/b/c/d');
it('diagnose - success', async function () {
mocks.fs.expects('exists').once().returns(B.resolve(true));
mocks.fs
.expects('lstat')
.once()
.returns(
B.resolve({
isDirectory() {
return true;
},
})
);
(await check.diagnose()).should.deep.equal({
ok: true,
optional: false,
message: 'Found directory at: /a/b/c/d',
});
mocks.verify();
});
it('failure - not there', async function () {
mocks.fs.expects('exists').once().returns(B.resolve(false));
(await check.diagnose()).should.deep.equal({
ok: false,
optional: false,
message: "Could NOT find directory at '/a/b/c/d'!",
});
mocks.verify();
});
it('failure - not a dir', async function () {
mocks.fs.expects('exists').once().returns(B.resolve(true));
mocks.fs
.expects('lstat')
.once()
.returns(
B.resolve({
isDirectory() {
return false;
},
})
);
(await check.diagnose()).should.deep.equal({
ok: false,
optional: false,
message: "'/a/b/c/d' is NOT a directory!",
});
mocks.verify();
});
it('fix', async function () {
(await check.fix()).should.equal('Manually create a directory at: /a/b/c/d');
});
})
);
describe(
'FileCheck',
withSandbox({mocks: {fs, tp, prompt}}, (S) => {
let check = new FileCheck('/a/b/c/d');
it('diagnose - success', async function () {
S.mocks.fs.expects('exists').once().returns(B.resolve(true));
(await check.diagnose()).should.deep.equal({
ok: true,
optional: false,
message: 'Found file at: /a/b/c/d',
});
S.verify();
});
it('failure - not there', async function () {
S.mocks.fs.expects('exists').once().returns(B.resolve(false));
(await check.diagnose()).should.deep.equal({
ok: false,
optional: false,
message: "Could NOT find file at '/a/b/c/d'!",
});
S.verify();
});
it('fix - yes', async function () {
let logStub = stubLog(S.sandbox, log, {stripColors: true});
S.mocks.prompt.expects('fixIt').once().returns(B.resolve('yes'));
S.mocks.tp
.expects('exec')
.once()
.returns(B.resolve({stdout: '', stderr: ''}));
await check.fix();
S.verify();
logStub.output.should.equal(
"info: The following command need be executed: touch '/a/b/c/d'"
);
});
it('fix - no', async function () {
let logStub = stubLog(S.sandbox, log, {stripColors: true});
S.mocks.prompt.expects('fixIt').once().returns(B.resolve('no'));
S.mocks.tp.expects('exec').never();
await check.fix().should.be.rejectedWith(FixSkippedError);
S.verify();
logStub.output.should.equal(
[
"info: The following command need be executed: touch '/a/b/c/d'",
"info: Skipping. You will need to touch '/a/b/c/d' manually.",
].join('\n')
);
});
})
);
});
-114
View File
@@ -1,114 +0,0 @@
import path from 'node:path';
import {BinaryIsInPathCheck, AndroidSdkExists} from '../../lib/dev';
import {fs} from '@appium/support';
import * as tp from 'teen_process';
import {withMocks, stubEnv} from '@appium/test-support';
import B from 'bluebird';
import {removeColors} from './helper';
describe('dev', function () {
before(async function () {
const chai = await import('chai');
const chaiAsPromised = await import('chai-as-promised');
chai.use(chaiAsPromised.default);
chai.should();
});
describe(
'BinaryIsInPathCheck',
withMocks({tp, fs}, (mocks) => {
stubEnv();
let check = new BinaryIsInPathCheck('mvn');
it('autofix', function () {
check.autofix.should.not.be.ok;
});
it('diagnose - success', async function () {
process.env.PATH = '/a/b/c/d;/e/f/g/h';
mocks.fs.expects('which').once().returns(B.resolve('/a/b/c/d/mvn'));
mocks.fs.expects('exists').once().returns(B.resolve(true));
(await check.diagnose()).should.deep.equal({
ok: true,
optional: false,
message: 'mvn was found at /a/b/c/d/mvn',
});
mocks.verify();
});
it('diagnose - failure - not in path ', async function () {
process.env.PATH = '/a/b/c/d;/e/f/g/h';
mocks.fs
.expects('which')
.once()
.returns(B.resolve({stack: 'mvn not found'}));
(await check.diagnose()).should.deep.equal({
ok: false,
optional: false,
message: 'mvn is MISSING in PATH: /a/b/c/d;/e/f/g/h',
});
mocks.verify();
});
it('diagnose - failure - invalid path', async function () {
process.env.PATH = '/a/b/c/d;/e/f/g/h';
mocks.fs.expects('which').once().returns(B.resolve('/a/b/c/d/mvn'));
mocks.fs.expects('exists').once().returns(B.resolve(false));
(await check.diagnose()).should.deep.equal({
ok: false,
optional: false,
message: 'mvn is MISSING in PATH: /a/b/c/d;/e/f/g/h',
});
mocks.verify();
});
it('fix', async function () {
removeColors(await check.fix()).should.equal(
'Manually install the mvn binary and add it to PATH.'
);
});
})
);
describe(
'AndroidSdkExists',
withMocks({fs}, (mocks) => {
stubEnv();
let check = new AndroidSdkExists('android-16');
it('autofix', function () {
check.autofix.should.not.be.ok;
});
it('diagnose - success', async function () {
process.env.ANDROID_HOME = path.resolve('/', 'a', 'b', 'c', 'd');
mocks.fs.expects('exists').once().returns(B.resolve(true));
(await check.diagnose()).should.deep.equal({
ok: true,
optional: false,
message: `android-16 was found at: ${process.env.ANDROID_HOME}${path.sep}platforms${path.sep}android-16`,
});
mocks.verify();
});
it('failure - missing android home', async function () {
delete process.env.ANDROID_HOME;
(await check.diagnose()).should.deep.equal({
ok: false,
optional: false,
message: 'android-16 could not be found because ANDROID_HOME is NOT set!',
});
mocks.verify();
});
it('diagnose - failure - invalid path', async function () {
process.env.ANDROID_HOME = path.resolve('/', 'a', 'b', 'c', 'd');
mocks.fs.expects('exists').once().returns(B.resolve(false));
(await check.diagnose()).should.deep.equal({
ok: false,
optional: false,
message: `android-16 could NOT be found at '${process.env.ANDROID_HOME}${path.sep}platforms${path.sep}android-16'!`,
});
mocks.verify();
});
it('fix - ANDROID_HOME', async function () {
delete process.env.ANDROID_HOME;
removeColors(await check.fix()).should.equal('Manually configure ANDROID_HOME.');
});
it('fix - install', async function () {
process.env.ANDROID_HOME = '/a/b/c/d';
removeColors(await check.fix()).should.equal('Manually install the android-16 sdk.');
});
})
);
});
-438
View File
@@ -1,438 +0,0 @@
import {Doctor, DoctorCheck, FixSkippedError} from '../../lib/doctor';
import {withSandbox, stubLog} from '@appium/test-support';
import log from '../../lib/logger';
import B from 'bluebird';
describe('doctor', function () {
before(async function () {
const chai = await import('chai');
const chaiAsPromised = await import('chai-as-promised');
chai.use(chaiAsPromised.default);
chai.should();
});
it('register', function () {
let doctor = new Doctor();
doctor.checks.should.have.length(0);
doctor.register(new DoctorCheck());
doctor.checks.should.have.length(1);
doctor.register([new DoctorCheck(), new DoctorCheck()]);
doctor.checks.should.have.length(3);
});
function configure() {
let doctor = new Doctor();
let checks = [
new DoctorCheck(),
new DoctorCheck(),
new DoctorCheck(),
new DoctorCheck(),
new DoctorCheck(),
];
doctor.register(checks);
return {doctor, checks};
}
describe(
'diagnose',
withSandbox({}, (S) => {
it('should detect all issues', async function () {
let logStub = stubLog(S.sandbox, log, {stripColors: true});
let {doctor, checks} = configure();
S.mocks.checks = checks.map((check) => S.sandbox.mock(check));
S.mocks.checks[0].expects('diagnose').once().returns({ok: true, message: 'All Good!'});
S.mocks.checks[1]
.expects('diagnose')
.twice()
.returns({ok: true, optional: true, message: 'All Good Option!'});
S.mocks.checks[2].expects('diagnose').once().returns({ok: false, message: 'Oh No!'});
S.mocks.checks[3]
.expects('diagnose')
.twice()
.returns({ok: false, optional: true, message: 'Oh No Option!'});
S.mocks.checks[4].expects('diagnose').once().returns({ok: false, message: 'Oh No!'});
await doctor.diagnose();
S.verify();
doctor.toFix.should.have.length(2);
doctor.toFixOptionals.should.have.length(1);
logStub.output.should.equal(
[
'info: ### Diagnostic for necessary dependencies starting ###',
'info: ✔ All Good!',
'warn: ✖ Oh No!',
'warn: ✖ Oh No!',
'info: ### Diagnostic for necessary dependencies completed, 2 fixes needed. ###',
'info: ',
'info: ### Diagnostic for optional dependencies starting ###',
'info: ✔ All Good Option!',
'warn: ✖ Oh No Option!',
'info: ### Diagnostic for optional dependencies completed, one fix possible. ###',
'info: ',
].join('\n')
);
});
})
);
describe(
'reportSuccess',
withSandbox({}, (S) => {
let doctor = new Doctor();
it('should report success when no fixes are needed', async function () {
let logStub = stubLog(S.sandbox, log, {stripColors: true});
(
await doctor.reportSuccess(doctor.toFix.length, doctor.toFixOptionals.length)
).should.equal(true);
logStub.output.should.equal(['info: Everything looks good, bye!', 'info: '].join('\n'));
});
it('should return false when fixes are needed', async function () {
doctor.toFix = [{}];
(await doctor.reportSuccess(doctor.toFix.length)).should.equal(false);
});
})
);
describe(
'reportManualFixes',
withSandbox({}, (S) => {
let doctor = new Doctor();
it('should ask for manual fixes to be applied', async function () {
let logStub = stubLog(S.sandbox, log, {stripColors: true});
doctor.toFix = [
{
error: 'Oh no this need to be manually fixed.',
check: new DoctorCheck(),
},
{
error: 'Oh no this is an autofix.',
check: new DoctorCheck({autofix: true}),
},
{
error: 'Oh no this also need to be manually fixed.',
check: new DoctorCheck(),
},
{
error: 'Oh no this also need to be manually fixed.',
check: new DoctorCheck(),
},
];
doctor.toFixOptionals = [];
for (let i = 0; i < doctor.toFix.length; i++) {
let m = S.sandbox.mock(doctor.toFix[i].check);
if (doctor.toFix[i].check.autofix) {
m.expects('fix').never();
} else {
m.expects('fix')
.once()
.returns(B.resolve(`Manual fix for ${i} is do something.`));
}
}
(await doctor.reportManualFixes(doctor.toFix, doctor.toFixOptionals)).should.equal(true);
S.verify();
logStub.output.should.equal(
[
'info: ### Manual Fixes Needed ###',
'info: The configuration cannot be automatically fixed, please do the following first:',
'warn: ➜ Manual fix for 0 is do something.',
'warn: ➜ Manual fix for 2 is do something.',
'warn: ➜ Manual fix for 3 is do something.',
'info: ',
'info: ###',
'info: ',
'info: Bye! Run appium-doctor again when all manual fixes have been applied!',
'info: ',
].join('\n')
);
});
it('should ask for manual fixes to be applied for optional', async function () {
let logStub = stubLog(S.sandbox, log, {stripColors: true});
doctor.toFix = [];
doctor.toFixOptionals = [
{
error: 'Oh no this need to be manually fixed.',
check: new DoctorCheck(),
},
{
error: 'Oh no this also need to be manually fixed.',
check: new DoctorCheck(),
},
];
for (let i = 0; i < doctor.toFixOptionals.length; i++) {
let m = S.sandbox.mock(doctor.toFixOptionals[i].check);
if (doctor.toFixOptionals[i].check.autofix) {
m.expects('fix').never();
} else {
m.expects('fix')
.once()
.returns(B.resolve(`Manual fix for ${i} is do something.`));
}
}
(await doctor.reportManualFixes(doctor.toFix, doctor.toFixOptionals)).should.equal(true);
S.verify();
logStub.output.should.equal(
[
'info: ### Optional Manual Fixes ###',
'info: The configuration can install optionally. Please do the following manually:',
'warn: ➜ Manual fix for 0 is do something.',
'warn: ➜ Manual fix for 1 is do something.',
'info: ',
'info: ###',
'info: ',
'info: Bye! Run appium-doctor again when all manual fixes have been applied!',
'info: ',
].join('\n')
);
});
it('should ask for manual fixes to be applied for necessary and optional', async function () {
let logStub = stubLog(S.sandbox, log, {stripColors: true});
doctor.toFix = [
{
error: 'Oh no this need to be manually fixed.',
check: new DoctorCheck(),
},
];
doctor.toFixOptionals = [
{
error: 'Oh no this need to be manually fixed, but it is optional.',
check: new DoctorCheck(),
},
];
S.sandbox
.mock(doctor.toFix[0].check)
.expects('fix')
.once()
.returns(B.resolve(`Manual fix for 0 is do something.`));
S.sandbox
.mock(doctor.toFixOptionals[0].check)
.expects('fix')
.once()
.returns(B.resolve(`Manual fix for 0 is do something.`));
(await doctor.reportManualFixes(doctor.toFix, doctor.toFixOptionals)).should.equal(true);
S.verify();
logStub.output.should.equal(
[
'info: ### Manual Fixes Needed ###',
'info: The configuration cannot be automatically fixed, please do the following first:',
'warn: ➜ Manual fix for 0 is do something.',
'info: ',
'info: ### Optional Manual Fixes ###',
'info: The configuration can install optionally. Please do the following manually:',
'warn: ➜ Manual fix for 0 is do something.',
'info: ',
'info: ###',
'info: ',
'info: Bye! Run appium-doctor again when all manual fixes have been applied!',
'info: ',
].join('\n')
);
});
it('should return false when there is no manual fix', async function () {
doctor.toFix = [{error: 'Oh no!', check: new DoctorCheck({autofix: true})}];
(await doctor.reportManualFixes()).should.equal(false);
});
})
);
describe(
'runAutoFix',
withSandbox({}, (S) => {
let doctor = new Doctor();
let fix = {
error: 'Something wrong!',
check: {
fix() {},
diagnose() {},
},
};
it('fix - success', async function () {
let logStub = stubLog(S.sandbox, log, {stripColors: true});
S.mocks.check = S.sandbox.mock(fix.check);
S.mocks.check.expects('fix').once();
S.mocks.check
.expects('diagnose')
.once()
.returns(
B.resolve({
ok: true,
optional: false,
message: 'It worked',
})
);
await doctor.runAutoFix(fix);
S.verify();
logStub.output.should.equal(
[
'info: ### Fixing: Something wrong! ###',
'info: Checking if this was fixed:',
'info: ✔ It worked',
'info: ### Fix was successfully applied ###',
].join('\n')
);
});
it('fix - skipped', async function () {
let logStub = stubLog(S.sandbox, log, {stripColors: true});
S.mocks.check = S.sandbox.mock(fix.check);
S.mocks.check.expects('fix').once().throws(new FixSkippedError());
await doctor.runAutoFix(fix);
S.verify();
logStub.output.should.equal(
['info: ### Fixing: Something wrong! ###', 'info: ### Skipped fix ###'].join('\n')
);
});
it('fix - crash', async function () {
let logStub = stubLog(S.sandbox, log, {stripColors: true});
S.mocks.check = S.sandbox.mock(fix.check);
S.mocks.check.expects('fix').once().throws(new Error('Oh No!'));
await doctor.runAutoFix(fix);
S.verify();
logStub.output.should.equal(
[
'info: ### Fixing: Something wrong! ###',
'warn: Error: Oh No!',
'info: ### Fix did not succeed ###',
].join('\n')
);
});
it("fix - didn't fix", async function () {
let logStub = stubLog(S.sandbox, log, {stripColors: true});
S.mocks.check = S.sandbox.mock(fix.check);
S.mocks.check.expects('fix').once();
S.mocks.check
.expects('diagnose')
.once()
.returns(
B.resolve({
ok: false,
message: 'Still Weird!',
})
);
await doctor.runAutoFix(fix);
S.verify();
logStub.output.should.equal(
[
'info: ### Fixing: Something wrong! ###',
'info: Checking if this was fixed:',
'info: ✖ Still Weird!',
'info: ### Fix was applied but issue remains ###',
].join('\n')
);
});
})
);
describe(
'runAutoFixes',
withSandbox({}, (S) => {
let doctor = new Doctor();
it('success', async function () {
let logStub = stubLog(S.sandbox, log, {stripColors: true});
doctor.toFix = [
{error: 'Oh no.', check: new DoctorCheck({autofix: true})},
{error: 'Oh no.', check: new DoctorCheck({autofix: true})},
{error: 'Oh no.', check: new DoctorCheck({autofix: true})},
];
S.sandbox.stub(doctor, 'runAutoFix').callsFake((f) => {
log.info('Autofix log go there.');
f.fixed = true;
});
await doctor.runAutoFixes();
doctor.runAutoFix.calledThrice.should.be.ok;
logStub.output.should.equal(
[
'info: Autofix log go there.',
'info: ',
'info: Autofix log go there.',
'info: ',
'info: Autofix log go there.',
'info: ',
'info: Bye! All issues have been fixed!',
'info: ',
].join('\n')
);
});
it('failure', async function () {
let logStub = stubLog(S.sandbox, log, {stripColors: true});
doctor.toFix = [
{error: 'Oh no.', check: new DoctorCheck({autofix: true})},
{error: 'Oh no.', check: new DoctorCheck({autofix: true})},
{error: 'Oh no.', check: new DoctorCheck({autofix: true})},
];
let succeed = false;
S.sandbox.stub(doctor, 'runAutoFix').callsFake((f) => {
if (succeed) {
log.info('succeeded, Autofix log go there.');
f.fixed = true;
} else {
log.warn('failed, Autofix log go there.');
}
succeed = !succeed;
});
await doctor.runAutoFixes();
doctor.runAutoFix.calledThrice.should.be.ok;
logStub.output.should.equal(
[
'warn: failed, Autofix log go there.',
'info: ',
'info: succeeded, Autofix log go there.',
'info: ',
'warn: failed, Autofix log go there.',
'info: ',
'info: Bye! A few issues remain, fix manually and/or rerun appium-doctor!',
'info: ',
].join('\n')
);
});
})
);
describe(
'run',
withSandbox({}, (S) => {
let doctor = new Doctor();
it('should work', async function () {
try {
let doctor = new Doctor();
await doctor.run();
} catch (err) {}
});
it('should report success', async function () {
S.mocks.doctor = S.sandbox.mock(doctor);
S.mocks.doctor.expects('diagnose').once();
S.mocks.doctor.expects('reportSuccess').once().returns(true);
S.mocks.doctor.expects('reportManualFixes').never();
S.mocks.doctor.expects('runAutoFixes').never();
await doctor.run();
S.verify();
});
it('should report manual fixes', async function () {
S.mocks.doctor = S.sandbox.mock(doctor);
S.mocks.doctor.expects('diagnose').once();
S.mocks.doctor.expects('reportSuccess').once().returns(false);
S.mocks.doctor.expects('reportManualFixes').once().returns(true);
S.mocks.doctor.expects('runAutoFixes').never();
await doctor.run();
S.verify();
});
it('should run autofixes', async function () {
S.mocks.doctor = S.sandbox.mock(doctor);
S.mocks.doctor.expects('diagnose').once();
S.mocks.doctor.expects('reportSuccess').once().returns(false);
S.mocks.doctor.expects('reportManualFixes').once().returns(false);
S.mocks.doctor.expects('runAutoFixes').once();
await doctor.run();
S.verify();
});
})
);
});
-19
View File
@@ -1,19 +0,0 @@
import newDoctor from '../../lib/factory';
describe('factory', function () {
before(async function () {
const chai = await import('chai');
chai.should();
});
function getTest(config) {
return function runTest() {
let doctor = newDoctor(config);
doctor.should.exist;
doctor.checks.should.have.length.above(0);
};
}
for (let config of [{ios: true}, {android: true}, {dev: true}]) {
it(`should work for ${config}`, getTest(config));
}
});
@@ -1 +0,0 @@
WOW
-200
View File
@@ -1,200 +0,0 @@
import {
NodeBinaryCheck,
NodeVersionCheck,
OptionalFfmpegCommandCheck,
OptionalMjpegConsumerCommandCheck,
} from '../../lib/general';
import * as tp from 'teen_process';
import * as utils from '../../lib/utils';
import NodeDetector from '../../lib/node-detector';
import {withMocks} from '@appium/test-support';
import B from 'bluebird';
import {removeColors} from './helper';
describe('general', function () {
before(async function () {
const chai = await import('chai');
const chaiAsPromised = await import('chai-as-promised');
chai.use(chaiAsPromised.default);
chai.should();
});
describe(
'NodeBinaryCheck',
withMocks({NodeDetector}, (mocks) => {
let check = new NodeBinaryCheck();
it('autofix', function () {
check.autofix.should.not.be.ok;
});
it('diagnose - success', async function () {
mocks.NodeDetector.expects('detect').once().returns(B.resolve('/a/b/c/d'));
(await check.diagnose()).should.deep.equal({
ok: true,
optional: false,
message: 'The Node.js binary was found at: /a/b/c/d',
});
mocks.verify();
});
it('diagnose - failure', async function () {
mocks.NodeDetector.expects('detect').once().returns(B.resolve(null));
(await check.diagnose()).should.deep.equal({
ok: false,
optional: false,
message: 'The Node.js binary was NOT found!',
});
mocks.verify();
});
it('fix', async function () {
removeColors(await check.fix()).should.equal('Manually setup Node.js.');
});
})
);
describe(
'NodeVersionCheck',
withMocks({NodeDetector, tp}, (mocks) => {
let check = new NodeVersionCheck();
it('autofix', function () {
check.autofix.should.not.be.ok;
});
it('diagnose - success', async function () {
mocks.NodeDetector.expects('detect').once().returns(B.resolve('/a/b/c/d'));
mocks.tp
.expects('exec')
.once()
.returns(B.resolve({stdout: 'v14.0.0', stderr: ''}));
(await check.diagnose()).should.deep.equal({
ok: true,
optional: false,
message: 'Node version is 14.0.0',
});
mocks.verify();
});
it('diagnose - failure - insufficient version', async function () {
mocks.NodeDetector.expects('detect').once().returns(B.resolve('/a/b/c/d'));
mocks.tp
.expects('exec')
.once()
.returns(B.resolve({stdout: 'v0.12.18', stderr: ''}));
(await check.diagnose()).should.deep.equal({
ok: false,
optional: false,
message: 'Node version should be at least 14.0.0!',
});
mocks.verify();
});
it('diagnose - failure - bad output', async function () {
mocks.NodeDetector.expects('detect').once().returns(B.resolve('/a/b/c/d'));
mocks.tp
.expects('exec')
.once()
.returns(B.resolve({stdout: 'blahblahblah', stderr: ''}));
(await check.diagnose()).should.deep.equal({
ok: false,
optional: false,
message: `Unable to find node version (version = 'blahblahblah')`,
});
mocks.verify();
});
it('fix', async function () {
removeColors(await check.fix()).should.equal('Manually upgrade Node.js.');
});
})
);
describe(
'OptionalFfmpegCommandCheck',
withMocks({tp, utils}, (mocks) => {
let check = new OptionalFfmpegCommandCheck();
it('autofix', function () {
check.autofix.should.not.be.ok;
});
it('diagnose - success', async function () {
mocks.tp
.expects('exec')
.once()
.returns({
stdout: `ffmpeg version 4.1 Copyright (c) 2000-2018 the FFmpeg developers
built with Apple LLVM version 14.0.0 (clang-1000.11.45.5)
configuration: --prefix=/usr/local/Cellar/ffmpeg/4.1_1 --enable-shared --enable-pthreads --enable-version3 --enable-hardcoded-tables --enable-avresample --cc=clang --host-cflags= --host-ldflags= --enable-ffplay --enable-gpl --enable-libmp3lame --enable-libopus --enable-libsnappy --enable-libtheora --enable-libvorbis --enable-libvpx --enable-libx264 --enable-libx265 --enable-libxvid --enable-lzma --enable-opencl --enable-videotoolbox
libavutil 56. 22.100 / 56. 22.100
libpostproc 55. 3.100 / 55. 3.100`,
stderr: '',
});
mocks.utils.expects('resolveExecutablePath').once().returns('path/to/ffmpeg');
(await check.diagnose()).should.deep.equal({
ok: true,
optional: true,
message:
'ffmpeg is installed at: path/to/ffmpeg. ffmpeg version 4.1 Copyright (c) 2000-2018 the FFmpeg developers',
});
mocks.verify();
});
it('diagnose - failure', async function () {
mocks.utils.expects('resolveExecutablePath').once().returns(false);
(await check.diagnose()).should.deep.equal({
ok: false,
optional: true,
message: 'ffmpeg cannot be found',
});
mocks.verify();
});
it('fix', async function () {
removeColors(await check.fix()).should.equal(
'ffmpeg is needed to record screen features. Please read https://www.ffmpeg.org/ to install it'
);
});
})
);
describe(
'OptionalMjpegConsumerCommandCheck',
withMocks({tp}, (mocks) => {
let check = new OptionalMjpegConsumerCommandCheck();
it('autofix', function () {
check.autofix.should.not.be.ok;
});
it('diagnose - success', async function () {
mocks.tp
.expects('exec')
.once()
.returns({
stdout: `
{
"dependencies": {
"mjpeg-consumer": {
"version": "1.1.0",
"from": "mjpeg-consumer",
"resolved": "https://registry.npmjs.org/mjpeg-consumer/-/mjpeg-consumer-1.1.0.tgz"
}
},
"path": "/path/to/node/node/v11.4.0/lib"
}
`,
stderr: '',
});
(await check.diagnose()).should.deep.equal({
ok: true,
optional: true,
message:
'mjpeg-consumer is installed at: /path/to/node/node/v11.4.0/lib. Installed version is: 1.1.0',
});
mocks.verify();
});
it('diagnose - failure', async function () {
mocks.tp.expects('exec').once().returns({stdout: '{}', stderr: ''});
(await check.diagnose()).should.deep.equal({
ok: false,
optional: true,
message: 'mjpeg-consumer cannot be found.',
});
mocks.verify();
});
it('fix', async function () {
removeColors(await check.fix()).should.equal(
"mjpeg-consumer module is required to use MJPEG-over-HTTP features. Please install it with 'npm i -g mjpeg-consumer'."
);
});
})
);
});
-16
View File
@@ -1,16 +0,0 @@
/**
* Remove ANSI colors/styles
*
* @param {string} text
* @returns {string} The text which has no ANSI colors/styles
*/
function removeColors(text) {
// https://stackoverflow.com/questions/25245716/remove-all-ansi-colors-styles-from-strings
return text.replace(
// eslint-disable-next-line no-control-regex
/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g,
''
);
}
export {removeColors};
-14
View File
@@ -1,14 +0,0 @@
import {newDoctor, Doctor, DoctorCheck} from '../../lib';
describe('index', function () {
before(async function () {
const chai = await import('chai');
chai.should();
});
it('should work', function () {
newDoctor.should.exist;
Doctor.should.exist;
DoctorCheck.should.exist;
});
});
-395
View File
@@ -1,395 +0,0 @@
import {
fixes,
XcodeCheck,
XcodeCmdLineToolsCheck,
DevToolsSecurityCheck,
OptionalApplesimutilsCommandCheck,
OptionalIdbCommandCheck,
OptionalIOSDeployCommandCheck,
OptionalLyftCommandCheck,
} from '../../lib/ios';
import {fs, system} from '@appium/support';
import * as utils from '../../lib/utils';
import * as tp from 'teen_process';
import * as prompter from '../../lib/prompt';
import FixSkippedError from '../../lib/doctor';
import log from '../../lib/logger';
import B from 'bluebird';
import {withMocks, withSandbox, stubLog} from '@appium/test-support';
import {removeColors} from './helper';
describe('ios', function () {
before(async function () {
const chai = await import('chai');
const chaiAsPromised = await import('chai-as-promised');
chai.use(chaiAsPromised.default);
chai.should();
});
describe(
'XcodeCheck',
withMocks({tp, fs}, (mocks) => {
let check = new XcodeCheck();
it('autofix', function () {
check.autofix.should.not.be.ok;
});
it('diagnose - success', async function () {
// xcrun
mocks.tp.expects('exec').once().resolves({
stdout: 'usage: simctl [--set <path>] [--profiles <path>] <subcommand> ...',
stderr: '',
});
// xcode-select
mocks.tp
.expects('exec')
.once()
.returns(B.resolve({stdout: '/a/b/c/d\n', stderr: ''}));
mocks.fs.expects('exists').once().resolves(true);
(await check.diagnose()).should.deep.equal({
ok: true,
optional: false,
message: 'Xcode is installed at: /a/b/c/d',
});
mocks.verify();
});
it('diagnose - failure - xcode-select', async function () {
// xcrun
mocks.tp
.expects('exec')
.once()
.returns(
B.resolve({
stdout: 'usage: simctl [--set <path>] [--profiles <path>] <subcommand> ...',
stderr: '',
})
);
// xcode-select
mocks.tp.expects('exec').once().rejects(new Error('Something wrong!'));
(await check.diagnose()).should.deep.equal({
ok: false,
optional: false,
message: 'Xcode is NOT installed!',
});
mocks.verify();
});
it('diagnose - failure - path not exists', async function () {
// xcrun
mocks.tp
.expects('exec')
.once()
.returns(
B.resolve({
stdout: 'usage: simctl [--set <path>] [--profiles <path>] <subcommand> ...',
stderr: '',
})
);
// xcode-select
mocks.tp
.expects('exec')
.once()
.returns(B.resolve({stdout: '/a/b/c/d\n', stderr: ''}));
mocks.fs.expects('exists').once().resolves(false);
(await check.diagnose()).should.deep.equal({
ok: false,
optional: false,
message: "Xcode cannot be found at '/a/b/c/d'!",
});
mocks.verify();
});
it('diagnose - failure - xcrun does not work', async function () {
// xcrun
mocks.tp
.expects('exec')
.once()
.rejects(
new Error(
'xcrun: error: unable to find utility "simctl", not a developer tool or in PATH'
)
);
// no xcode-select
(await check.diagnose()).should.deep.equal({
ok: false,
optional: false,
message: 'Error running xcrun simctl',
});
mocks.verify();
});
it('fix', async function () {
removeColors(await check.fix()).should.equal(
"Manually install Xcode, and make sure 'xcode-select -p' command shows proper path like '/Applications/Xcode.app/Contents/Developer'"
);
});
})
);
describe(
'XcodeCmdLineToolsCheck',
withSandbox({mocks: {tp, utils, prompter, system}}, (S) => {
let check = new XcodeCmdLineToolsCheck();
it('autofix', function () {
check.autofix.should.be.ok;
});
it('diagnose - success', async function () {
S.mocks.tp
.expects('exec')
.once()
.returns(
B.resolve({
stdout: '/Applications/Xcode.app/Contents/Developer\n',
stderr: '',
})
);
(await check.diagnose()).should.deep.equal({
ok: true,
optional: false,
message:
'Xcode Command Line Tools are installed in: /Applications/Xcode.app/Contents/Developer',
});
S.verify();
});
it('diagnose - failure - pkgutil crash', async function () {
S.mocks.tp.expects('exec').once().throws(new Error('Something wrong!'));
(await check.diagnose()).should.deep.equal({
ok: false,
optional: false,
message: 'Xcode Command Line Tools are NOT installed!',
});
S.verify();
});
it('diagnose - failure - xcode-select -p returns status 2', async function () {
S.mocks.tp.expects('exec').once().throws(new Error());
(await check.diagnose()).should.deep.equal({
ok: false,
optional: false,
message: 'Xcode Command Line Tools are NOT installed!',
});
S.verify();
});
it('fix - yes', async function () {
let logStub = stubLog(S.sandbox, log, {stripColors: true});
S.mocks.tp
.expects('exec')
.once()
.returns(B.resolve({stdout: '', stderr: ''}));
S.mocks.prompter.expects('fixIt').once().resolves('yes');
await check.fix();
S.verify();
logStub.output.should.equal(
['info: The following command need be executed: xcode-select --install'].join('\n')
);
});
it('fix - no', async function () {
let logStub = stubLog(S.sandbox, log, {stripColors: true});
S.mocks.tp.expects('exec').never();
S.mocks.prompter.expects('fixIt').once().resolves('no');
await check.fix().should.be.rejectedWith(FixSkippedError);
S.verify();
logStub.output.should.equal(
[
'info: The following command need be executed: xcode-select --install',
'info: Skipping. You will need to install Xcode manually.',
].join('\n')
);
});
})
);
describe(
'DevToolsSecurityCheck',
withMocks({fixes, tp}, (mocks) => {
let check = new DevToolsSecurityCheck();
it('diagnose - success', async function () {
mocks.tp
.expects('exec')
.once()
.returns(B.resolve({stdout: '1234 enabled\n', stderr: ''}));
(await check.diagnose()).should.deep.equal({
ok: true,
optional: false,
message: 'DevToolsSecurity is enabled.',
});
mocks.verify();
});
it('diagnose - failure - DevToolsSecurity crash', async function () {
mocks.tp.expects('exec').once().rejects(new Error('Something wrong!'));
(await check.diagnose()).should.deep.equal({
ok: false,
optional: false,
message: 'DevToolsSecurity is NOT enabled!',
});
mocks.verify();
});
it('diagnose - failure - not enabled', async function () {
mocks.tp
.expects('exec')
.once()
.returns(B.resolve({stdout: '1234 abcd\n', stderr: ''}));
(await check.diagnose()).should.deep.equal({
ok: false,
optional: false,
message: 'DevToolsSecurity is NOT enabled!',
});
mocks.verify();
});
})
);
describe(
'OptionalLyftCommandCheck',
withMocks({tp, utils}, (mocks) => {
let check = new OptionalLyftCommandCheck();
it('autofix', function () {
check.autofix.should.not.be.ok;
});
it('diagnose - success', async function () {
mocks.utils
.expects('resolveExecutablePath')
.once()
.returns('path/to/set-simulator-location');
(await check.diagnose()).should.deep.equal({
ok: true,
optional: true,
message: 'set-simulator-location is installed',
});
mocks.verify();
});
it('diagnose - failure', async function () {
mocks.utils.expects('resolveExecutablePath').once().returns(false);
(await check.diagnose()).should.deep.equal({
ok: false,
optional: true,
message: 'set-simulator-location is not installed',
});
mocks.verify();
});
it('fix', async function () {
removeColors(await check.fix()).should.equal(
'set-simulator-location is needed to set location for Simulator. ' +
'Please read https://github.com/lyft/set-simulator-location to install it'
);
});
})
);
describe(
'OptionalIdbCommandCheck',
withMocks({tp, utils}, (mocks) => {
let check = new OptionalIdbCommandCheck();
it('autofix', function () {
check.autofix.should.not.be.ok;
});
it('diagnose - success', async function () {
mocks.utils.expects('resolveExecutablePath').once().returns('path/to/idb');
mocks.utils.expects('resolveExecutablePath').once().returns('path/to/idb_cpmpanion');
(await check.diagnose()).should.deep.equal({
ok: true,
optional: true,
message: 'idb and idb_companion are installed',
});
mocks.verify();
});
it('diagnose - failure because of no idb_companion and idb', async function () {
mocks.utils.expects('resolveExecutablePath').once().returns(false);
mocks.utils.expects('resolveExecutablePath').once().returns(false);
(await check.diagnose()).should.deep.equal({
ok: false,
optional: true,
message: 'idb and idb_companion are not installed',
});
mocks.verify();
});
it('diagnose - failure because of no idb_companion', async function () {
mocks.utils.expects('resolveExecutablePath').once().returns('path/to/idb');
mocks.utils.expects('resolveExecutablePath').once().returns(false);
(await check.diagnose()).should.deep.equal({
ok: false,
optional: true,
message: 'idb_companion is not installed',
});
mocks.verify();
});
it('diagnose - failure because of no idb', async function () {
mocks.utils.expects('resolveExecutablePath').once().returns(false);
mocks.utils.expects('resolveExecutablePath').once().returns('path/to/idb_cpmpanion');
(await check.diagnose()).should.deep.equal({
ok: false,
optional: true,
message: 'idb is not installed',
});
mocks.verify();
});
it('fix', async function () {
removeColors(await check.fix()).should.equal(
'Why idb is needed and how to install it: https://git.io/JnxQc'
);
});
})
);
describe(
'OptionalApplesimutilsCommandCheck',
withMocks({tp, utils}, (mocks) => {
let check = new OptionalApplesimutilsCommandCheck();
it('autofix', function () {
check.autofix.should.not.be.ok;
});
it('diagnose - success', async function () {
mocks.utils.expects('resolveExecutablePath').once().returns('path/to/applesimutils');
mocks.tp.expects('exec').once().returns({stdout: 'vxx.xx.xx', stderr: ''});
(await check.diagnose()).should.deep.equal({
ok: true,
optional: true,
message:
'applesimutils is installed at: path/to/applesimutils. Installed versions are: vxx.xx.xx',
});
mocks.verify();
});
it('diagnose - failure', async function () {
mocks.utils.expects('resolveExecutablePath').once().returns(false);
(await check.diagnose()).should.deep.equal({
ok: false,
optional: true,
message: 'applesimutils cannot be found',
});
mocks.verify();
});
it('fix', async function () {
removeColors(await check.fix()).should.equal(
'Why applesimutils is needed and how to install it: https://github.com/appium/appium-xcuitest-driver/blob/master/docs/execute-methods.md#mobile-setpermission'
);
});
})
);
describe(
'OptionalIOSDeployCommandCheck',
withMocks({tp, utils}, (mocks) => {
let check = new OptionalIOSDeployCommandCheck();
it('autofix', function () {
check.autofix.should.not.be.ok;
});
it('diagnose - success', async function () {
mocks.utils.expects('resolveExecutablePath').once().returns('path/to/ios-deploy');
mocks.tp.expects('exec').once().returns({stdout: '1.9.4', stderr: ''});
(await check.diagnose()).should.deep.equal({
ok: true,
optional: true,
message: 'ios-deploy is installed at: path/to/ios-deploy. Installed version is: 1.9.4',
});
mocks.verify();
});
it('diagnose - failure', async function () {
mocks.utils.expects('resolveExecutablePath').once().returns(false);
(await check.diagnose()).should.deep.equal({
ok: false,
optional: true,
message: 'ios-deploy cannot be found',
});
mocks.verify();
});
it('fix', async function () {
removeColors(await check.fix()).should.equal(
'ios-deploy is used as a fallback command to install iOS applications to real device. Please read https://github.com/ios-control/ios-deploy/ to install it'
);
});
})
);
});
@@ -1,48 +0,0 @@
import {fs, system} from '@appium/support';
import NodeDetector from '../../lib/node-detector';
import B from 'bluebird';
import {withSandbox} from '@appium/test-support';
describe(
'NodeDetector',
withSandbox({mocks: {fs, system}}, (S) => {
let expect;
before(async function () {
const chai = await import('chai');
chai.should();
expect = chai.expect;
});
it('retrieveUsingSystemCall - success - where returns multiple lines ', async function () {
S.mocks.fs.expects('which').once().returns(B.resolve('/a/b/node.exe'));
S.mocks.fs.expects('exists').once().returns(B.resolve(true));
(await NodeDetector.retrieveUsingSystemCall()).should.equal('/a/b/node.exe');
S.verify();
});
it('retrieveUsingSystemCall - success', async function () {
S.mocks.fs.expects('which').once().returns(B.resolve('/a/b/c/d/node'));
S.mocks.fs.expects('exists').once().returns(B.resolve(true));
(await NodeDetector.retrieveUsingSystemCall()).should.equal('/a/b/c/d/node');
S.verify();
});
it('retrieveUsingSystemCall - failure - path not found ', async function () {
S.mocks.fs.expects('which').once().throws(Error('not found: carthage'));
expect(await NodeDetector.retrieveUsingSystemCall()).to.be.a('null');
S.verify();
});
it('retrieveUsingSystemCall - failure - path not exist', async function () {
S.mocks.fs.expects('which').once().returns(B.resolve('/a/b/c/d'));
S.mocks.fs.expects('exists').once().returns(B.resolve(false));
expect(await NodeDetector.retrieveUsingSystemCall()).to.be.a('null');
});
it('checkForNodeBinary - failure', async function () {
S.mocks.NodeDetector = S.sandbox.mock(NodeDetector);
S.mocks.NodeDetector.expects('retrieveUsingSystemCall').once().returns(null);
expect(await NodeDetector.detect()).to.be.a('null');
S.verify();
});
})
);
-48
View File
@@ -1,48 +0,0 @@
import {fixIt, clear} from '../../lib/prompt';
import * as utils from '../../lib/utils';
import {withMocks} from '@appium/test-support';
import B from 'bluebird';
describe(
'prompt',
withMocks({utils}, (mocks) => {
before(async function () {
const chai = await import('chai');
chai.should();
});
it('fixit - yes', async function () {
clear();
mocks.utils
.expects('prompt')
.once()
.returns(B.resolve({confirmation: 'yes'}));
(await fixIt()).should.equal('yes');
mocks.verify();
});
it('fixit always ', async function () {
clear();
mocks.utils
.expects('prompt')
.once()
.returns(B.resolve({confirmation: 'always'}));
(await fixIt()).should.equal('yes');
(await fixIt()).should.equal('yes');
(await fixIt()).should.equal('yes');
mocks.verify();
});
it('fixit never ', async function () {
clear();
mocks.utils
.expects('prompt')
.once()
.returns(B.resolve({confirmation: 'never'}));
(await fixIt()).should.equal('no');
(await fixIt()).should.equal('no');
(await fixIt()).should.equal('no');
mocks.verify();
});
})
);
-36
View File
@@ -1,36 +0,0 @@
import {configureBinaryLog, resetLog} from '../../lib/utils';
import {fs} from '@appium/support';
import path from 'path';
import {Doctor} from '../../lib/doctor';
describe('utils', function () {
before(async function () {
const chai = await import('chai');
chai.should();
});
it('fs.readFile', async function () {
(await fs.readFile(path.resolve(__dirname, 'fixtures', 'wow.txt'), 'utf8')).should.include(
'WOW'
);
});
it('fs.exists', async function () {
(await fs.exists(path.resolve(__dirname, 'fixtures', 'wow.txt'))).should.be.ok;
(await fs.exists(path.resolve(__dirname, 'fixtures', 'notwow.txt'))).should.not.be.ok;
});
it('Should handle logs through onLogMessage callback', async function () {
function onLogMessage(level, prefix, msg) {
`${level} ${prefix} ${msg}`.should.include('AppiumDoctor');
}
configureBinaryLog({onLogMessage});
let doctor = new Doctor();
try {
await doctor.run();
} finally {
resetLog();
}
});
});
-12
View File
@@ -1,12 +0,0 @@
{
"extends": "@appium/tsconfig/tsconfig.json",
"compilerOptions": {
"outDir": "build",
"checkJs": true,
"paths": {
"@appium/support": ["../support"]
}
},
"include": ["lib", "bin"],
"references": [{"path": "../support"}]
}
+20
View File
@@ -3,6 +3,26 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.0.26](https://github.com/appium/appium/compare/@appium/docutils@1.0.25...@appium/docutils@1.0.26) (2024-12-05)
### Bug Fixes
* **docutils:** update dependency lilconfig to v3.1.3 ([#20803](https://github.com/appium/appium/issues/20803)) ([93fa5cc](https://github.com/appium/appium/commit/93fa5ccb06eb5089bb3e8a8d9e67dad7f4446a0a))
* **support:** update dependency teen_process to v2.2.2 ([#20808](https://github.com/appium/appium/issues/20808)) ([c91683f](https://github.com/appium/appium/commit/c91683f50feea937ff8568eab6366465a6cf9a3e))
* **types:** update dependency type-fest to v4.30.0 ([#20802](https://github.com/appium/appium/issues/20802)) ([8590432](https://github.com/appium/appium/commit/8590432955eb7663e35847db541b9ead3f845a36))
## [1.0.25](https://github.com/appium/appium/compare/@appium/docutils@1.0.24...@appium/docutils@1.0.25) (2024-12-02)
### Bug Fixes
* **types:** update dependency type-fest to v4.29.1 ([#20795](https://github.com/appium/appium/issues/20795)) ([6ba31fe](https://github.com/appium/appium/commit/6ba31fe5766f69cb010a4cac81233f4c3cbcf80f))
## [1.0.24](https://github.com/appium/appium/compare/@appium/docutils@1.0.23...@appium/docutils@1.0.24) (2024-11-29)
+5 -5
View File
@@ -1,6 +1,6 @@
{
"name": "@appium/docutils",
"version": "1.0.24",
"version": "1.0.26",
"description": "Documentation generation utilities for Appium and related projects",
"keywords": [
"automation",
@@ -48,7 +48,7 @@
"start": "node ./build/lib/cli/index.js"
},
"dependencies": {
"@appium/support": "^5.1.7",
"@appium/support": "^6.0.0",
"@appium/tsconfig": "^0.3.3",
"@sliphua/lilconfig-ts-loader": "3.2.2",
"@types/which": "3.0.4",
@@ -56,14 +56,14 @@
"consola": "3.2.3",
"diff": "7.0.0",
"json5": "2.2.3",
"lilconfig": "3.1.2",
"lilconfig": "3.1.3",
"lodash": "4.17.21",
"pkg-dir": "5.0.0",
"read-pkg": "5.2.0",
"semver": "7.6.3",
"source-map-support": "0.5.21",
"teen_process": "2.2.0",
"type-fest": "4.29.0",
"teen_process": "2.2.2",
"type-fest": "4.30.0",
"typescript": "5.7.2",
"yaml": "2.6.1",
"yargs": "17.7.2",
+1 -1
View File
@@ -1,5 +1,5 @@
mkdocs==1.6.1
mkdocs-git-revision-date-localized-plugin==1.3.0
mkdocs-material==9.5.46
mkdocs-material==9.5.47
mkdocs-redirects==1.2.2
mike==2.1.3
+25
View File
@@ -3,6 +3,31 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [0.7.1](https://github.com/appium/appium/compare/@appium/driver-test-support@0.7.0...@appium/driver-test-support@0.7.1) (2024-12-05)
### Bug Fixes
* **base-driver:** add * always for non-separator for backward compatibility ([#20821](https://github.com/appium/appium/issues/20821)) ([6ea0830](https://github.com/appium/appium/commit/6ea0830319c204097233d0659e21b3447b624431))
* **support:** update dependency axios to v1.7.9 ([#20811](https://github.com/appium/appium/issues/20811)) ([69f100a](https://github.com/appium/appium/commit/69f100ad3e12030708dee4b8a74005dd41976e37))
* **types:** update dependency type-fest to v4.30.0 ([#20802](https://github.com/appium/appium/issues/20802)) ([8590432](https://github.com/appium/appium/commit/8590432955eb7663e35847db541b9ead3f845a36))
## [0.7.0](https://github.com/appium/appium/compare/@appium/driver-test-support@0.6.12...@appium/driver-test-support@0.7.0) (2024-12-02)
### Features
* **base-driver:** Allow to prefix feature names with automation names ([#20793](https://github.com/appium/appium/issues/20793)) ([942057d](https://github.com/appium/appium/commit/942057d26cbf51539f34b6b7ff8a3c1d07821687))
### Bug Fixes
* **types:** update dependency type-fest to v4.29.1 ([#20795](https://github.com/appium/appium/issues/20795)) ([6ba31fe](https://github.com/appium/appium/commit/6ba31fe5766f69cb010a4cac81233f4c3cbcf80f))
## [0.6.12](https://github.com/appium/appium/compare/@appium/driver-test-support@0.6.11...@appium/driver-test-support@0.6.12) (2024-11-29)
@@ -2,9 +2,6 @@ export {createSessionHelpers, driverE2ETestSuite} from './e2e-suite';
export * from './unit-suite';
export * from './helpers';
// eslint-disable-next-line import/no-unresolved
export * from './stoppable';
/**
* @typedef {import('@appium/types').DriverClass} DriverClass
* @typedef {import('@appium/types').BaseNSCapabilities} BaseNSCapabilities
@@ -1,57 +0,0 @@
import type {AppiumServer} from '@appium/types';
import {main as startAppium} from 'appium';
import type {Args, CliCommandServer} from 'appium/types';
import B from 'bluebird';
import type {Server} from 'node:http';
import stoppable from 'stoppable';
import type {Asyncify} from 'type-fest';
/**
* Options for {@linkcode startStoppableAppium}
*/
export type AppiumServerOpts = Args<CliCommandServer>;
/**
* An {@linkcode AppiumServer} with a method `stop() => Promise<void>`, which closes all sockets and fully stops the server.
*
* Returned by {@linkcode startStoppableAppium}
*/
export type TestAppiumServer = Omit<NormativeAppiumServer, 'close'> & {
stop: Asyncify<stoppable.WithStop['stop']>;
close: (callback: (err?: Error) => void) => Promise<void>;
};
/**
* The {@linkcode AppiumServer} type, but with the `close` method normalized to a callback-style function.
*/
export type NormativeAppiumServer = Omit<AppiumServer, 'close'> & {
close: Server['close'];
};
/**
* Coerces {@linkcode AppiumServer} into a {@linkcode TestAppiumServer}.
* @param opts Options for {@linkcode startAppium}
* @todo This should be moved into `@appium/driver-test-support` or something
* @returns A stoppable Appium server
*/
export async function startStoppableAppium(opts: AppiumServerOpts): Promise<TestAppiumServer> {
const appiumServer = (await startAppium(opts)) as AppiumServer;
const stoppableServer = stoppable(appiumServer as unknown as NormativeAppiumServer, 0);
const originalAsyncClose = appiumServer.close;
(stoppableServer as unknown as TestAppiumServer).close = async function (
callback?: (err?: Error) => void
) {
if (callback) {
try {
await originalAsyncClose.call(this);
callback();
} catch (err) {
callback(err);
}
} else {
await originalAsyncClose.call(this);
}
};
stoppableServer.stop = B.promisify(stoppableServer.stop, {context: stoppableServer});
return stoppableServer as unknown as TestAppiumServer;
}
+28 -18
View File
@@ -580,24 +580,9 @@ export function driverUnitTestSuite(
d = new DriverClass();
});
it('should say a feature is enabled when it is explicitly allowed', function () {
d.allowInsecure = ['foo', 'bar'];
it('should handled with wild card if feature name is invalid', function () {
d.allowInsecure = ['foo'];
d.isFeatureEnabled('foo').should.be.true;
d.isFeatureEnabled('bar').should.be.true;
d.isFeatureEnabled('baz').should.be.false;
});
it('should say a feature is not enabled if it is not enabled', function () {
d.allowInsecure = [];
d.isFeatureEnabled('foo').should.be.false;
});
it('should prefer denyInsecure to allowInsecure', function () {
d.allowInsecure = ['foo', 'bar'];
d.denyInsecure = ['foo'];
d.isFeatureEnabled('foo').should.be.false;
d.isFeatureEnabled('bar').should.be.true;
d.isFeatureEnabled('baz').should.be.false;
});
it('should allow global setting for insecurity', function () {
@@ -609,11 +594,36 @@ export function driverUnitTestSuite(
it('global setting should be overrideable', function () {
d.relaxedSecurityEnabled = true;
d.denyInsecure = ['foo', 'bar'];
d.denyInsecure = ['*:foo', '*:bar'];
d.isFeatureEnabled('foo').should.be.false;
d.isFeatureEnabled('bar').should.be.false;
d.isFeatureEnabled('baz').should.be.true;
});
it('should say a feature is enabled if it is for this driver', function () {
d.opts.automationName = 'bar';
d.allowInsecure = ['bar:foo'];
d.isFeatureEnabled('foo').should.be.true;
});
it('should say a feature is enabled if it is for all drivers', function () {
d.opts.automationName = 'bar';
d.allowInsecure = ['*:foo'];
d.isFeatureEnabled('foo').should.be.true;
});
it('should say a feature is not enabled if it is not for this driver', function () {
d.opts.automationName = 'bar';
d.allowInsecure = ['baz:foo'];
d.isFeatureEnabled('foo').should.be.false;
});
it('should say a feature is not enabled if it is enabled and then disabled', function () {
d.opts.automationName = 'bar';
d.allowInsecure = ['bar:foo'];
d.denyInsecure = ['*:foo'];
d.isFeatureEnabled('foo').should.be.false;
});
});
}
+4 -6
View File
@@ -1,6 +1,6 @@
{
"name": "@appium/driver-test-support",
"version": "0.6.12",
"version": "0.7.1",
"description": "Test utilities for Appium drivers",
"keywords": [
"automation",
@@ -41,10 +41,9 @@
},
"types": "./build/lib/index.d.ts",
"dependencies": {
"@appium/types": "^0.22.1",
"@appium/types": "^0.22.3",
"@types/lodash": "4.17.13",
"@types/stoppable": "1.1.3",
"axios": "1.7.8",
"axios": "1.7.9",
"bluebird": "3.7.2",
"chai": "5.1.2",
"chai-as-promised": "8.0.1",
@@ -52,8 +51,7 @@
"lodash": "4.17.21",
"sinon": "19.0.2",
"source-map-support": "0.5.21",
"stoppable": "1.1.0",
"type-fest": "4.29.0"
"type-fest": "4.30.0"
},
"peerDependencies": {
"appium": "^2.0.0-beta.43",
@@ -1,45 +0,0 @@
import axios from 'axios';
import B from 'bluebird';
import {Agent} from 'node:http';
import {startStoppableAppium, TestAppiumServer} from '../../lib';
import getPort from 'get-port';
describe('startStoppableAppium()', function () {
let expect: any;
before(async function () {
const chai = await import('chai');
const chaiAsPromised = await import('chai-as-promised');
chai.use(chaiAsPromised.default);
chai.should();
expect = chai.expect;
});
it('should start an Appium server', async function () {
let server: TestAppiumServer | undefined;
try {
server = await startStoppableAppium({port: await getPort()});
expect(server, 'to be an object');
} finally {
if (server) {
await expect(server.stop()).to.be.fulfilled;
}
}
});
describe('when the server has connections', function () {
it('should stop the server and resolve with a boolean', async function () {
const port = await getPort();
const server = await startStoppableAppium({port});
const getConnections = B.promisify(server.getConnections, {context: server});
await axios.get(`http://127.0.0.1:${port}/status`, {
httpAgent: new Agent({keepAlive: true}),
});
try {
await expect(getConnections()).to.eventually.be.greaterThan(0);
} finally {
await expect(server.stop()).to.eventually.be.a('boolean');
}
});
});
});
+9
View File
@@ -3,6 +3,15 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [5.5.7](https://github.com/appium/appium/compare/@appium/fake-driver@5.5.6...@appium/fake-driver@5.5.7) (2024-12-05)
### Bug Fixes
* **universal-xml-plugin:** update dependency @xmldom/xmldom to v0.9.6 ([#20819](https://github.com/appium/appium/issues/20819)) ([228b091](https://github.com/appium/appium/commit/228b091edc6f16dfc081642bfc313b66d07bd370))
## [5.5.6](https://github.com/appium/appium/compare/@appium/fake-driver@5.5.5...@appium/fake-driver@5.5.6) (2024-11-29)
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@appium/fake-driver",
"version": "5.5.6",
"version": "5.5.7",
"description": "Mock driver used internally by Appium for testing. Ignore",
"keywords": [
"automation",
@@ -43,7 +43,7 @@
},
"dependencies": {
"@types/bluebird": "3.5.42",
"@xmldom/xmldom": "0.9.5",
"@xmldom/xmldom": "0.9.6",
"asyncbox": "3.0.0",
"bluebird": "3.7.2",
"lodash": "4.17.21",
+16
View File
@@ -3,6 +3,22 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [3.1.54](https://github.com/appium/appium/compare/@appium/fake-plugin@3.1.53...@appium/fake-plugin@3.1.54) (2024-12-05)
**Note:** Version bump only for package @appium/fake-plugin
## [3.1.53](https://github.com/appium/appium/compare/@appium/fake-plugin@3.1.52...@appium/fake-plugin@3.1.53) (2024-12-02)
**Note:** Version bump only for package @appium/fake-plugin
## [3.1.52](https://github.com/appium/appium/compare/@appium/fake-plugin@3.1.51...@appium/fake-plugin@3.1.52) (2024-11-29)
**Note:** Version bump only for package @appium/fake-plugin
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@appium/fake-plugin",
"version": "3.1.52",
"version": "3.1.54",
"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.48",
"@appium/support": "^5.1.7",
"@appium/base-plugin": "^2.2.50",
"@appium/support": "^6.0.0",
"@types/bluebird": "3.5.42",
"bluebird": "3.7.2",
"lodash": "4.17.21",
+16
View File
@@ -3,6 +3,22 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [3.0.24](https://github.com/appium/appium/compare/@appium/images-plugin@3.0.23...@appium/images-plugin@3.0.24) (2024-12-05)
**Note:** Version bump only for package @appium/images-plugin
## [3.0.23](https://github.com/appium/appium/compare/@appium/images-plugin@3.0.22...@appium/images-plugin@3.0.23) (2024-12-02)
**Note:** Version bump only for package @appium/images-plugin
## [3.0.22](https://github.com/appium/appium/compare/@appium/images-plugin@3.0.21...@appium/images-plugin@3.0.22) (2024-11-29)
**Note:** Version bump only for package @appium/images-plugin
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@appium/images-plugin",
"version": "3.0.22",
"version": "3.0.24",
"description": "Plugin for working with images and image elements in Appium",
"keywords": [
"automation",
@@ -40,7 +40,7 @@
},
"dependencies": {
"@appium/opencv": "^3.0.6",
"@appium/support": "^5.1.7",
"@appium/support": "^6.0.0",
"lodash": "4.17.21",
"lru-cache": "10.4.3",
"sharp": "0.33.5",
+17
View File
@@ -3,6 +3,23 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [0.3.46](https://github.com/appium/appium/compare/@appium/plugin-test-support@0.3.45...@appium/plugin-test-support@0.3.46) (2024-12-05)
### Bug Fixes
* **support:** update dependency teen_process to v2.2.2 ([#20808](https://github.com/appium/appium/issues/20808)) ([c91683f](https://github.com/appium/appium/commit/c91683f50feea937ff8568eab6366465a6cf9a3e))
## [0.3.45](https://github.com/appium/appium/compare/@appium/plugin-test-support@0.3.44...@appium/plugin-test-support@0.3.45) (2024-12-02)
**Note:** Version bump only for package @appium/plugin-test-support
## [0.3.44](https://github.com/appium/appium/compare/@appium/plugin-test-support@0.3.43...@appium/plugin-test-support@0.3.44) (2024-11-29)
**Note:** Version bump only for package @appium/plugin-test-support
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@appium/plugin-test-support",
"version": "0.3.44",
"version": "0.3.46",
"description": "Testing utilities for Appium plugins",
"keywords": [
"automation",
@@ -41,11 +41,11 @@
"test:unit": "exit 0"
},
"dependencies": {
"@appium/types": "^0.22.1",
"@appium/types": "^0.22.3",
"get-port": "5.1.1",
"log-symbols": "4.1.0",
"source-map-support": "0.5.21",
"teen_process": "2.2.0"
"teen_process": "2.2.2"
},
"peerDependencies": {
"appium": "^2.0.0-beta.43",
+29
View File
@@ -3,6 +3,35 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [6.0.0](https://github.com/appium/appium/compare/@appium/support@5.1.8...@appium/support@6.0.0) (2024-12-05)
### ⚠ BREAKING CHANGES
* **support:** revert #20797 which was used for a backward compatibility
### Bug Fixes
* **support:** update dependency axios to v1.7.9 ([#20811](https://github.com/appium/appium/issues/20811)) ([69f100a](https://github.com/appium/appium/commit/69f100ad3e12030708dee4b8a74005dd41976e37))
* **support:** update dependency teen_process to v2.2.2 ([#20808](https://github.com/appium/appium/issues/20808)) ([c91683f](https://github.com/appium/appium/commit/c91683f50feea937ff8568eab6366465a6cf9a3e))
* **types:** update dependency type-fest to v4.30.0 ([#20802](https://github.com/appium/appium/issues/20802)) ([8590432](https://github.com/appium/appium/commit/8590432955eb7663e35847db541b9ead3f845a36))
### Miscellaneous Chores
* **support:** revert [#20797](https://github.com/appium/appium/issues/20797) as a backward compatibility ([#20801](https://github.com/appium/appium/issues/20801)) ([16c3ae2](https://github.com/appium/appium/commit/16c3ae25eace19bf63ded56f5196865cfa6eca51))
## [5.1.8](https://github.com/appium/appium/compare/@appium/support@5.1.7...@appium/support@5.1.8) (2024-12-02)
### Bug Fixes
* **types:** update dependency type-fest to v4.29.1 ([#20795](https://github.com/appium/appium/issues/20795)) ([6ba31fe](https://github.com/appium/appium/commit/6ba31fe5766f69cb010a4cac81233f4c3cbcf80f))
## [5.1.7](https://github.com/appium/appium/compare/@appium/support@5.1.6...@appium/support@5.1.7) (2024-11-29)
+5 -5
View File
@@ -1,6 +1,6 @@
{
"name": "@appium/support",
"version": "5.1.7",
"version": "6.0.0",
"description": "Support libs used across appium packages",
"keywords": [
"automation",
@@ -43,7 +43,7 @@
"dependencies": {
"@appium/logger": "^1.6.1",
"@appium/tsconfig": "^0.3.3",
"@appium/types": "^0.22.1",
"@appium/types": "^0.22.3",
"@colors/colors": "1.6.0",
"@types/archiver": "6.0.3",
"@types/base64-stream": "1.0.5",
@@ -61,7 +61,7 @@
"@types/uuid": "10.0.0",
"@types/which": "3.0.4",
"archiver": "7.0.1",
"axios": "1.7.8",
"axios": "1.7.9",
"base64-stream": "1.0.0",
"bluebird": "3.7.2",
"bplist-creator": "0.1.1",
@@ -88,8 +88,8 @@
"shell-quote": "1.8.2",
"source-map-support": "0.5.21",
"supports-color": "8.1.1",
"teen_process": "2.2.0",
"type-fest": "4.29.0",
"teen_process": "2.2.2",
"type-fest": "4.30.0",
"uuid": "11.0.3",
"which": "4.0.0",
"yauzl": "3.2.0"
+17
View File
@@ -3,6 +3,23 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [3.1.0](https://github.com/appium/appium/compare/@appium/test-support@3.0.53...@appium/test-support@3.1.0) (2024-12-05)
### Features
* **test-support:** Remove obsolete helpers ([#20817](https://github.com/appium/appium/issues/20817)) ([81a5b57](https://github.com/appium/appium/commit/81a5b576739f67c1c7f311217ecc50e467f5a491))
## [3.0.53](https://github.com/appium/appium/compare/@appium/test-support@3.0.52...@appium/test-support@3.0.53) (2024-12-02)
**Note:** Version bump only for package @appium/test-support
## [3.0.52](https://github.com/appium/appium/compare/@appium/test-support@3.0.51...@appium/test-support@3.0.52) (2024-11-29)
**Note:** Version bump only for package @appium/test-support
-89
View File
@@ -13,50 +13,6 @@ npm install @appium/test-support --save-dev
## Usage
### stubEnv
```js
import { stubEnv } from '@appium/test-support';
describe('myTest', () => {
stubEnv();
it('setting env variable', () => {
// Changes to process.env will stay local
process.env.ABC = 'abc';
});
});
```
### stubLog
```js
import { stubLog } from '@appium/test-support';
describe('myTest', () => {
let sandbox;
// configure sandbox here...
it('stubbing log', () => {
let logStub = stubLog(sandbox, log);
log.info('Hello World!');
log.warn(`The ${'sun'.yellow} is shining!`);
logStub.output.should.equals([
'info: Hello World!',
`warn: The ${'sun'.yellow} is shining!`
].join('\n'));
});
it('stubbing log stripping colors', () => {
let logStub = stubLog(sandbox, log, {stripColors: true});
log.info('Hello World!');
log.warn(`The ${'sun'.yellow} is shining!`);
logStub.output.should.equals([
'info: Hello World!',
'warn: The sun is shining!'
].join('\n'));
});
});
```
### withSandbox
Use when mixing up `sinon` APIs (mocks, spies, stubs).
@@ -100,51 +56,6 @@ describe('withMocks', withMocks({api}, (mocks) => {
}));
```
### fakeTime
```js
import { fakeTime } from '@appium/test-support';
function doSomething() {
return new B.Promise((resolve) => {
let ret = '';
function appendOneByOne () {
if(ret.length >= 10) {
return resolve(ret);
}
setTimeout(() => {
ret = ret + ret.length;
appendOneByOne();
}, 1000);
}
appendOneByOne();
});
}
describe('fakeTime', () => {
let sandbox;
// create sandbox ...
it('should fake time', async () => {
let timeLord = fakeTime(sandbox);
let p = doSomething();
timeLord.speedup(200, 60); // interval=200, times=60
(await p).should.equals('0123456789');
});
});
```
## Travis Android Emu Setup
On [Travis](https://travis-ci.org/), setting up an emulator takes a lot of boilerplate.
While the configuration needs to be done on a case-by-case basis, the actual startup
can be scripted. Toward that, there are two scripts:
* `android-emu-travis-pre` - creates a device (configured with the environment variables
`ANDROID_EMU_NAME`, `ANDROID_EMU_TARGET`, and `ANDROID_EMU_ABI`) and starts it
in the background
* `android-emu-travis-post` - waits for the device to be booted, and then goes
to its home screen
## License
Apache-2.0
-16
View File
@@ -1,16 +0,0 @@
/**
* Creates Mocha "before each" and "after each" hooks to restore `process.env` after every test.
*/
function stubEnv() {
/** @type {NodeJS.ProcessEnv} */
let envBackup;
beforeEach(function beforeEach() {
envBackup = process.env;
process.env = {...process.env};
});
afterEach(function afterEach() {
process.env = envBackup;
});
}
export {stubEnv};
-2
View File
@@ -1,7 +1,5 @@
// this just needs to be imported, for the functionality to be injected
import './unhandled-rejection';
export {stubEnv} from './env-utils';
export {stubLog} from './log-utils';
export {withMocks, verifyMocks} from './mock-utils';
export {withSandbox, verifySandbox} from './sandbox-utils';
-51
View File
@@ -1,51 +0,0 @@
import '@colors/colors';
class LogStub {
/**
*
* @param {LogStubOptions} [opts]
*/
constructor(opts = {}) {
this.output = '';
this.stripColors = Boolean(opts.stripColors);
}
/**
*
* @param {string} level
* @param {any} message
*/
log(level, message) {
if (this.stripColors) {
message = message.stripColors;
}
if (this.output.length > 0) {
this.output += '\n';
}
this.output = `${this.output}${level}: ${message}`;
}
}
/**
* Instantiates a {@linkcode LogStub} object
* @param {import('sinon').SinonSandbox} sandbox
* @param {import('@appium/types').AppiumLogger} log
* @param {LogStubOptions} [opts]
* @returns {LogStub}
*/
function stubLog(sandbox, log, opts = {}) {
let logStub = new LogStub(opts);
for (let l of log.levels) {
sandbox.stub(log, l).callsFake(function doLogging(mess) {
logStub.log(l, mess);
});
}
return logStub;
}
export {stubLog};
/**
* Options for {@linkcode LogStub} constructor
* @typedef LogStubOptions
* @property {boolean} [stripColors] - If `true`, strip ANSI colors from output
*/
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@appium/test-support",
"version": "3.0.52",
"version": "3.1.0",
"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": "^5.1.7",
"@appium/support": "^6.0.0",
"@colors/colors": "1.6.0",
"bluebird": "3.7.2",
"lodash": "4.17.21",
@@ -1,23 +0,0 @@
import {stubEnv} from '../../lib';
describe('env-utils', function () {
let expect;
before(async function () {
const chai = await import('chai');
chai.should();
expect = chai.expect;
});
describe('stubEnv', function () {
stubEnv();
it('setting env variable', function () {
process.env.ABC = 'abc';
});
it('env varible should not be set', function () {
expect(process.env.ABC).not.to.exist;
});
});
});
@@ -1,30 +0,0 @@
import {stubLog} from '../../lib';
import log from '../../lib/logger';
import sinon from 'sinon';
import '@colors/colors';
describe('log-utils', function () {
describe('stubLog', function () {
let sandbox;
beforeEach(function () {
sandbox = sinon.createSandbox();
});
afterEach(function () {
sandbox.restore();
});
it('should stub log', function () {
let logStub = stubLog(sandbox, log);
log.info('Hello World!');
log.warn(`The ${'sun'.yellow} is shining!`);
logStub.output.should.equals(
['info: Hello World!', `warn: The ${'sun'.yellow} is shining!`].join('\n')
);
});
it('should stub log and strip colors', function () {
let logStub = stubLog(sandbox, log, {stripColors: true});
log.info('Hello World!');
log.warn(`The ${'sun'.yellow} is shining!`);
logStub.output.should.equals(['info: Hello World!', 'warn: The sun is shining!'].join('\n'));
});
});
});
+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.22.3](https://github.com/appium/appium/compare/@appium/types@0.22.2...@appium/types@0.22.3) (2024-12-05)
### Bug Fixes
* **types:** update dependency type-fest to v4.30.0 ([#20802](https://github.com/appium/appium/issues/20802)) ([8590432](https://github.com/appium/appium/commit/8590432955eb7663e35847db541b9ead3f845a36))
## [0.22.2](https://github.com/appium/appium/compare/@appium/types@0.22.1...@appium/types@0.22.2) (2024-12-02)
### Bug Fixes
* **types:** update dependency type-fest to v4.29.1 ([#20795](https://github.com/appium/appium/issues/20795)) ([6ba31fe](https://github.com/appium/appium/commit/6ba31fe5766f69cb010a4cac81233f4c3cbcf80f))
## [0.22.1](https://github.com/appium/appium/compare/@appium/types@0.22.0...@appium/types@0.22.1) (2024-11-29)
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@appium/types",
"version": "0.22.1",
"version": "0.22.3",
"description": "Various type declarations used across Appium",
"keywords": [
"automation",
@@ -43,7 +43,7 @@
"@appium/tsconfig": "^0.3.3",
"@types/express": "5.0.0",
"@types/ws": "8.5.13",
"type-fest": "4.29.0"
"type-fest": "4.30.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.26](https://github.com/appium/appium-plugins/compare/@appium/universal-xml-plugin@1.0.25...@appium/universal-xml-plugin@1.0.26) (2024-12-05)
### Bug Fixes
* **universal-xml-plugin:** update dependency @xmldom/xmldom to v0.9.6 ([#20819](https://github.com/appium/appium-plugins/issues/20819)) ([228b091](https://github.com/appium/appium-plugins/commit/228b091edc6f16dfc081642bfc313b66d07bd370))
## [1.0.25](https://github.com/appium/appium-plugins/compare/@appium/universal-xml-plugin@1.0.24...@appium/universal-xml-plugin@1.0.25) (2024-11-29)
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@appium/universal-xml-plugin",
"version": "1.0.25",
"version": "1.0.26",
"description": "Appium plugin for making XML source and XPath queries the same across iOS and Android",
"keywords": [
"automation",
@@ -37,7 +37,7 @@
},
"dependencies": {
"@types/xmldom": "0.1.34",
"@xmldom/xmldom": "0.9.5",
"@xmldom/xmldom": "0.9.6",
"fast-xml-parser": "4.5.0",
"lodash": "4.17.21",
"source-map-support": "0.5.21",
-1
View File
@@ -27,7 +27,6 @@
"get-stream",
"globby",
"got",
"inquirer",
"log-symbols",
"ora",
"p-retry",
+86
View File
@@ -0,0 +1,86 @@
import path from 'node:path';
import {logger, fs} from '@appium/support';
import axios from 'axios';
import _ from 'lodash';
export const log = logger.getLogger('CROWDIN');
// https://developer.crowdin.com/api/v2/
const PROJECT_ID = process.env.CROWDIN_PROJECT_ID;
const API_TOKEN = process.env.CROWDIN_TOKEN;
if (!PROJECT_ID || !API_TOKEN) {
throw new Error(`Both CROWDIN_PROJECT_ID and CROWDIN_TOKEN environment variables must be set`);
}
export const RESOURCES_ROOT = path.resolve('packages', 'appium', 'docs');
export const ORIGINAL_LANGUAGE = 'en';
export const DOCUMENTS_EXT = '.md';
export const MKDOCS_YAML = (langName) => `mkdocs-${langName}.yml`;
const USER_AGENT = 'Appium CI';
const API_ROOT = 'https://api.crowdin.com/api/v2';
/**
*
* @param {string} dir
* @param {string} ext
* @returns {Promise<string[]>}
*/
export async function walk(dir, ext) {
const itemsInDir = await fs.readdir(dir);
const result = [];
for (const itemInDir of itemsInDir) {
const fullPath = path.join(dir, itemInDir);
let stats;
try {
stats = await fs.stat(fullPath);
} catch (e) {
continue;
}
if (stats.isDirectory()) {
result.push(...(await walk(fullPath, ext)));
} else if (itemInDir.endsWith(ext)) {
result.push(fullPath);
}
}
return result;
}
/**
*
* @param {string} [suffix='']
* @param {ApiRequestOptions} [opts={}]
* @returns {any}
*/
export async function performApiRequest(suffix = '', opts = {}) {
const {method = 'GET', payload, headers, params, isProjectSpecific = true} = opts;
const url = isProjectSpecific
? `${API_ROOT}/projects/${PROJECT_ID}${suffix}`
: `${API_ROOT}${suffix}`;
log.debug(`Sending ${method} request to ${url}`);
if (_.isPlainObject(payload)) {
log.debug(`Request payload: ${JSON.stringify(payload)}`);
}
return (
await axios({
method,
headers: {
Authorization: `Bearer ${API_TOKEN}`,
'Content-Type': 'application/json',
'User-Agent': USER_AGENT,
...(headers || {}),
},
url,
params,
data: payload,
})
).data;
}
/**
* @typedef {Object} ApiRequestOptions
* @property {string} [method='GET']
* @property {any} [payload]
* @property {axios.AxiosRequestHeaders} headers
* @property {Record<string, any>} [params]
* @property {boolean} [isProjectSpecific=true]
*/
+150
View File
@@ -0,0 +1,150 @@
import path from 'node:path';
import {fs, net, tempDir, zip} from '@appium/support';
import {waitForCondition} from 'asyncbox';
import {
log,
walk,
ORIGINAL_LANGUAGE,
performApiRequest,
RESOURCES_ROOT,
DOCUMENTS_EXT,
MKDOCS_YAML,
} from './crowdin-common.mjs';
const BUILD_TIMEOUT_MS = 1000 * 60 * 10;
const BUILD_STATUS = {
finished: 'finished',
created: 'created',
inProgress: 'inProgress',
canceled: 'canceled',
failed: 'failed',
};
// Add new languages here whenever needed
const CROWDIN_TO_FS_LANGUAGES_MAP = {
ja: 'ja',
'zh-CN': 'zh',
// de,es-ES,fr,it,ja,pt-BR,uk
};
/**
*
* @returns {Promise<number>}
*/
async function buildTranslations() {
log.info('Building project translations');
const {data: buildData} = await performApiRequest('/translations/builds', {
method: 'POST',
});
return buildData.id;
}
/**
*
* @param {number} buildId
* @param {string} dstPath
* @returns {Promise<void>}
*/
async function downloadTranslations(buildId, dstPath) {
log.info(`Waiting up to ${BUILD_TIMEOUT_MS / 1000}s for the build #${buildId} to finish`);
await waitForCondition(
async () => {
const {data: buildData} = await performApiRequest(`/translations/builds/${buildId}`);
switch (buildData.status) {
case BUILD_STATUS.finished:
return true;
case BUILD_STATUS.inProgress:
case BUILD_STATUS.created:
return false;
default:
throw new Error(`The translations build got an unexpected status '${buildData.status}'`);
}
},
{
waitMs: BUILD_TIMEOUT_MS,
intervalMs: 1000,
},
);
const {data: downloadData} = await performApiRequest(`/translations/builds/${buildId}/download`);
log.info(`Downloading translations to '${dstPath}'`);
await net.downloadFile(downloadData.url, dstPath);
}
/**
*
* @param {string} srcDir
* @param {string} dstDir
* @returns {Promise<void>}
*/
async function syncTranslatedDocuments(srcDir, dstDir) {
const srcTranslatedDocs = await walk(srcDir, DOCUMENTS_EXT);
if (srcTranslatedDocs.length === 0) {
return;
}
let count = 0;
for (const srcPath of srcTranslatedDocs) {
const relativeTranslatedDocPath = path.relative(srcDir, srcPath);
const dstPath = path.join(dstDir, relativeTranslatedDocPath);
log.info(`Synchronizing '${dstPath}' (${++count} of ${srcTranslatedDocs.length})`);
await fs.mv(srcPath, dstPath, {mkdirp: true});
}
}
/**
*
* @param {string} srcDir
* @param {string} dstDir
* @param {string} dstLanguage
* @returns {Promise<void>}
*/
async function syncTranslatedConfig(srcDir, dstDir, dstLanguage) {
const configPath = path.join(srcDir, MKDOCS_YAML(ORIGINAL_LANGUAGE));
if (!await fs.exists(configPath)) {
throw new Error(`Did not find the translated MkDocs config at '${configPath}'`);
}
const dstPath = path.join(dstDir, MKDOCS_YAML(dstLanguage));
log.info(`Synchronizing '${dstPath}'`);
await fs.mv(configPath, dstPath);
}
async function main() {
const buildId = await buildTranslations();
const zipPath = await tempDir.path({prefix: 'translations', suffix: '.zip'});
try {
await downloadTranslations(buildId, zipPath);
const tmpRoot = await tempDir.openDir();
try {
await zip.extractAllTo(zipPath, tmpRoot);
const srcLanguageNames = await fs.readdir(tmpRoot);
log.info(`Available Crowdin languages: ${srcLanguageNames}`);
log.info(`Supported languages map: ${JSON.stringify(CROWDIN_TO_FS_LANGUAGES_MAP)}`);
let count = 0;
for (const name of srcLanguageNames) {
const currentPath = path.join(tmpRoot, name);
if (!(await fs.stat(currentPath)).isDirectory() || name === ORIGINAL_LANGUAGE) {
continue;
}
const dstLanguageName = CROWDIN_TO_FS_LANGUAGES_MAP[name];
if (!dstLanguageName) {
// If the target language is not present in the map we ignore it
continue;
}
await syncTranslatedDocuments(currentPath, path.join(RESOURCES_ROOT, dstLanguageName));
await syncTranslatedConfig(currentPath, RESOURCES_ROOT, dstLanguageName);
log.info(
`Successfully updated resources for the '${dstLanguageName}' ` +
`('${name}' in Crowdin) language (${++count} of ${Object.keys(CROWDIN_TO_FS_LANGUAGES_MAP).length})`
);
}
} finally {
await fs.rimraf(tmpRoot);
}
} finally {
await fs.rimraf(zipPath);
}
}
(async () => await main())();
+325
View File
@@ -0,0 +1,325 @@
import path from 'node:path';
import crypto from 'node:crypto';
import {
log,
walk,
ORIGINAL_LANGUAGE,
performApiRequest,
RESOURCES_ROOT,
DOCUMENTS_EXT,
MKDOCS_YAML,
} from './crowdin-common.mjs';
import {fs} from '@appium/support';
const LANGUAGE_ROOT = path.resolve(RESOURCES_ROOT, ORIGINAL_LANGUAGE);
const MAX_ITEMS_PER_PAGE = 300;
const DOCUMENT_CONTENT_TYPE = 'text/markdown';
const MKDOCS_CONTENT_TYPE = 'application/yaml';
/**
*
* @param {string} str
* @returns {string}
*/
function toHash(str) {
return crypto.createHash('md5').update(str).digest('hex');
}
/**
*
* @param {string} fullPath
* @returns {string}
*/
function toCrowdinPath(fullPath) {
let result = `/${path.relative(LANGUAGE_ROOT, fullPath)}`;
if (result.includes('..')) {
result = `/${path.basename(fullPath)}`;
}
return result;
}
/**
*
* @param {string} name
* @param {string|null|undefined} [parentId]
* @returns {Promise<number>}
*/
async function addDirectory(name, parentId) {
const {data: directoryData} = await performApiRequest('/directories', {
method: 'POST',
payload: {
name,
directoryId: parentId,
}
});
return directoryData.id;
}
/**
*
* @param {string} name
* @param {number} storageId
* @param {string|null|undefined} [parentDirectoryId]
* @returns {Promise<number>}
*/
async function addFile(name, storageId, parentDirectoryId) {
const {data: fileData} = await performApiRequest('/files', {
method: 'POST',
payload: {
name,
storageId,
directoryId: parentDirectoryId,
}
});
return fileData.id;
}
/**
* @returns {Promise<Record<string, any>[]>}
*/
async function listFiles() {
const {data: filesData} = await performApiRequest('/files', {
method: 'GET',
params: {
limit: MAX_ITEMS_PER_PAGE,
}
});
return filesData.map(({data}) => data);
}
/**
*
* @param {number} fileId
* @returns {Promise<void>}
*/
async function deleteFile(fileId) {
await performApiRequest(`/files/${fileId}`, {
method: 'DELETE',
});
}
/**
*
* @param {number} [parentDirectoryId]
* @param {number} [recursion]
* @return {Promise<Record<string, any>[]>}
*/
async function listDirectories(parentDirectoryId) {
const {data: directoriesData} = await performApiRequest('/directories', {
method: 'GET',
params: {
limit: MAX_ITEMS_PER_PAGE,
directoryId: parentDirectoryId,
recursion: parentDirectoryId ? 10 : undefined,
},
});
return directoriesData.map(({data}) => data);
}
/**
*
* @param {string} name Should be properly url-encoded
* @param {string} fullPath
* @param {string} contentType Should be one of https://www.iana.org/assignments/media-types/media-types.xhtml
* @returns {Promise<Record<string, any>>}
*/
async function addStorage(name, fullPath, contentType) {
const {data: storageData} = await performApiRequest('/storages', {
method: 'POST',
headers: {
'Crowdin-API-FileName': name,
'Content-Type': contentType,
},
payload: fs.createReadStream(fullPath),
isProjectSpecific: false,
});
return storageData;
}
/**
*
* @param {string[]} matchedFiles
* @returns {Promise<Record<string, string>>}
*/
async function uploadDocumentsToStorage(matchedFiles) {
const resultMap = {};
let count = 0;
for (const matchedFilePath of matchedFiles) {
const crowdinPath = toCrowdinPath(matchedFilePath);
// Hashing is used to make sure we aways create the same storage for the same file path in Crowdin
const storageName = toHash(crowdinPath);
log.info(`Uploading '${crowdinPath}' to Crowdin storage (${++count} of ${matchedFiles.length})`);
const storageData = await addStorage(storageName, matchedFilePath, DOCUMENT_CONTENT_TYPE);
resultMap[matchedFilePath] = storageData.id;
}
return resultMap;
}
/**
*
* @param {string[]} matchedFiles
* @returns {Promise<Record<string, number>>}
*/
async function ensureDirectoryStructure(matchedFiles) {
const uniqueFolderPaths = new Set();
for (const matchedFile of matchedFiles) {
const relativePath = path.relative(LANGUAGE_ROOT, path.dirname(matchedFile));
if (relativePath) {
uniqueFolderPaths.add(relativePath);
}
}
if (uniqueFolderPaths.size === 0) {
return {};
}
const splitPaths = Array.from(uniqueFolderPaths)
.map((p) => p.split(path.sep))
.sort((a, b) => a.length - b.length);
const result = {};
const topDirectories = await listDirectories();
for (const splitPath of splitPaths) {
for (const level in splitPath) {
const pathInCrowdin = `/${splitPath.slice(0, level + 1).join('/')}`;
const parentPathInCrowdin = path.dirname(pathInCrowdin);
const parentDirectoryId = level === 0 ? undefined : result[parentPathInCrowdin];
const subDirectories = level === 0 ? topDirectories : await listDirectories(parentDirectoryId);
const existingDirectoryData = subDirectories.find((data) => data.path === pathInCrowdin);
if (existingDirectoryData) {
log.info(`Crowdin directory '${pathInCrowdin}' already exists`);
result[pathInCrowdin] = existingDirectoryData.id;
} else {
log.info(`Crowdin directory '${pathInCrowdin}' does not exist yet. Adding it`);
result[pathInCrowdin] = await addDirectory(splitPath[level], parentDirectoryId);
}
}
}
return result;
}
/**
*
* @param {Record<string, number>} storageMapping
* @param {Record<string, number>} directoriesMapping
* @param {Record<string, number>[]} existingFilesData
* @returns {Promise<Record<string, number>>}
*/
async function ensureFileStructure(storageMapping, directoriesMapping, existingFilesData) {
const result = {};
let count = 0;
for (const [fullPath, storageId] of Object.entries(storageMapping)) {
const pathInCrowdin = toCrowdinPath(fullPath);
log.info(`Synchronizing '${pathInCrowdin}' (${++count} of ${Object.keys(storageMapping).length})`);
const fileData = existingFilesData.find((data) => data.path === pathInCrowdin);
if (fileData) {
result[fullPath] = fileData.id;
} else {
const parentFolderId = directoriesMapping[path.dirname(pathInCrowdin)];
try {
const fileId = await addFile(encodeURIComponent(path.basename(pathInCrowdin)), storageId, parentFolderId);
result[fullPath] = fileId;
} catch (e) {
log.warn(`Cannot add '${pathInCrowdin}'. Skipping it`);
if (e.response) {
log.info(`Crowdin API status: ${e.response.status}`);
log.info(`Crowdin API error: ${JSON.stringify(e.response.data, null, 2)}`);
} else {
log.info(e);
}
continue;
}
}
}
return result;
}
/**
* @returns {Promise<void>}
*/
async function cleanupObsoleteDocuments() {
const [existingFilesData, matchedFiles] = await Promise.all([
listFiles(),
walk(LANGUAGE_ROOT, DOCUMENTS_EXT)
]);
const matchedFilePaths = new Set(matchedFiles.map(toCrowdinPath));
let count = 0;
for (const existingFileData of existingFilesData) {
if (matchedFilePaths.has(existingFileData.path)
|| !existingFileData.name.endsWith(DOCUMENTS_EXT)) {
continue;
}
log.info(`Deleting the obsolete document '${existingFileData.path}'`);
await deleteFile(existingFileData.id);
++count;
}
if (count > 0) {
log.info(`Deleted ${count} obsolete documents`);
}
}
/**
*
* @param {Record<string, number>} filesMapping
* @param {Record<string, number>} storageMapping
* @returns {Promise<void>}
*/
async function updateFiles(filesMapping, storageMapping) {
let count = 0;
for (const [fullPath, fileId] of Object.entries(filesMapping)) {
log.info(`Updating '${toCrowdinPath(fullPath)}' (${++count} of ${Object.keys(filesMapping).length})`);
await performApiRequest(`/files/${fileId}`, {
method: 'PUT',
payload: {
storageId: storageMapping[fullPath],
},
});
}
}
/**
*
* @returns {Promise<void>}
*/
async function updateDocuments() {
const matchedFiles = await walk(LANGUAGE_ROOT, DOCUMENTS_EXT);
if (matchedFiles.length === 0) {
throw new Error(`Did not find any files matching the '*${DOCUMENTS_EXT}' extension in '${LANGUAGE_ROOT}'`);
}
log.info(`Matched ${matchedFiles.length} files from '${LANGUAGE_ROOT}' for upload...`);
const [storageMapping, directoriesMapping] = await Promise.all([
uploadDocumentsToStorage(matchedFiles),
ensureDirectoryStructure(matchedFiles),
]);
const existingFilesData = await listFiles();
const filesMapping = await ensureFileStructure(storageMapping, directoriesMapping, existingFilesData);
await updateFiles(filesMapping, storageMapping);
}
/**
* @returns {Promise<void>}
*/
async function updateMkDocsConfig() {
const configFileName = MKDOCS_YAML(ORIGINAL_LANGUAGE);
const matchedFilePath = path.join(RESOURCES_ROOT, configFileName);
if (!await fs.exists(matchedFilePath)) {
throw new Error(`Did not find the MkDocs config at '${matchedFilePath}'`);
}
const storageData = await addStorage(encodeURIComponent(configFileName), matchedFilePath, MKDOCS_CONTENT_TYPE);
const storageMapping = {[matchedFilePath]: storageData.id};
const existingFilesData = await listFiles();
const filesMapping = await ensureFileStructure(storageMapping, {}, existingFilesData);
await updateFiles(filesMapping, storageMapping);
}
async function main() {
log.info('Updating documents');
await updateDocuments();
await cleanupObsoleteDocuments();
log.info('Updating MkDocs config');
await updateMkDocsConfig();
log.info('All done');
}
(async () => await main())();
+16
View File
@@ -0,0 +1,16 @@
import {logger} from '@appium/support';
const log = logger.getLogger('Doctor CLI');
const DEV_DOC_URL = 'https://github.com/appium/appium/blob/master/packages/appium/docs/en/developing/build-doctor-checks.md';
async function main() {
log.warn(`The Appium server's 'doctor' CLI utility has been removed`);
log.warn(
`Please use the 'doctor' script defined for the specific driver or plugin, ` +
`for example 'appium driver doctor uiautomator2'`
);
log.info(`Check ${DEV_DOC_URL} if you would like to contribute to existing doctor checks`);
process.exit(1);
}
(async () => await main())();
-3
View File
@@ -62,9 +62,6 @@
{
"path": "packages/fake-driver"
},
{
"path": "packages/doctor"
},
{
"path": "packages/strongbox"
},