mirror of
https://github.com/appium/appium.git
synced 2026-11-12 21:43:19 +08:00
Compare commits
26
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1d16ac6b45 | ||
|
|
6ea0830319 | ||
|
|
228b091edc | ||
|
|
81a5b57673 | ||
|
|
8d94f48bc5 | ||
|
|
f03468a153 | ||
|
|
6a78aa9cea | ||
|
|
9c5407ce64 | ||
|
|
2a99e43b88 | ||
|
|
dca88624a4 | ||
|
|
69f100ad3e | ||
|
|
f5e708ac42 | ||
|
|
c91683f50f | ||
|
|
4a2bfe9418 | ||
|
|
3008c0d7be | ||
|
|
9a3f990739 | ||
|
|
16c3ae25ea | ||
|
|
93fa5ccb06 | ||
|
|
8590432955 | ||
|
|
42ef4841f9 | ||
|
|
8daf5e123a | ||
|
|
3108a162ff | ||
|
|
942057d26c | ||
|
|
6ba31fe576 | ||
|
|
7abf277ea3 | ||
|
|
9aa9807b90 |
@@ -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
|
||||
|
||||
@@ -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)._'
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
@@ -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 }}
|
||||
@@ -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
@@ -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))
|
||||
|
||||
Generated
+318
-654
File diff suppressed because it is too large
Load Diff
+6
-4
@@ -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",
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 = '';
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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))
|
||||
@@ -1,39 +0,0 @@
|
||||
# @appium/doctor
|
||||
|
||||
> Attempts to diagnose and fix common Appium configuration issues
|
||||
|
||||
[](https://npmjs.org/package/@appium/doctor)
|
||||
[](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
|
||||
@@ -1,3 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
module.exports = require('./build/bin/appium-doctor');
|
||||
@@ -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);
|
||||
});
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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]
|
||||
*/
|
||||
@@ -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;
|
||||
@@ -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/*
|
||||
*/
|
||||
@@ -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
|
||||
*/
|
||||
@@ -1,4 +0,0 @@
|
||||
import newDoctor from './factory';
|
||||
import {Doctor, DoctorCheck} from './doctor';
|
||||
|
||||
export {newDoctor, Doctor, DoctorCheck};
|
||||
@@ -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;
|
||||
@@ -1,5 +0,0 @@
|
||||
import {logger} from '@appium/support';
|
||||
|
||||
const log = logger.getLogger('AppiumDoctor');
|
||||
|
||||
export default log;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
*/
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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`
|
||||
);
|
||||
});
|
||||
})
|
||||
);
|
||||
});
|
||||
@@ -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')
|
||||
);
|
||||
});
|
||||
})
|
||||
);
|
||||
});
|
||||
@@ -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.');
|
||||
});
|
||||
})
|
||||
);
|
||||
});
|
||||
@@ -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();
|
||||
});
|
||||
})
|
||||
);
|
||||
});
|
||||
@@ -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
|
||||
@@ -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'."
|
||||
);
|
||||
});
|
||||
})
|
||||
);
|
||||
});
|
||||
@@ -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};
|
||||
@@ -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;
|
||||
});
|
||||
});
|
||||
@@ -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();
|
||||
});
|
||||
})
|
||||
);
|
||||
@@ -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();
|
||||
});
|
||||
})
|
||||
);
|
||||
@@ -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();
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"extends": "@appium/tsconfig/tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "build",
|
||||
"checkJs": true,
|
||||
"paths": {
|
||||
"@appium/support": ["../support"]
|
||||
}
|
||||
},
|
||||
"include": ["lib", "bin"],
|
||||
"references": [{"path": "../support"}]
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
@@ -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,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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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};
|
||||
@@ -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';
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
@@ -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'));
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
"get-stream",
|
||||
"globby",
|
||||
"got",
|
||||
"inquirer",
|
||||
"log-symbols",
|
||||
"ora",
|
||||
"p-retry",
|
||||
|
||||
@@ -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]
|
||||
*/
|
||||
@@ -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())();
|
||||
@@ -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())();
|
||||
@@ -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())();
|
||||
@@ -62,9 +62,6 @@
|
||||
{
|
||||
"path": "packages/fake-driver"
|
||||
},
|
||||
{
|
||||
"path": "packages/doctor"
|
||||
},
|
||||
{
|
||||
"path": "packages/strongbox"
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user