mirror of
https://github.com/appium/appium.git
synced 2026-11-10 20:05:42 +08:00
Compare commits
17
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d7e0ef76a3 | ||
|
|
b8e78c8449 | ||
|
|
97ed6be4d9 | ||
|
|
3cdfecae7d | ||
|
|
43992824a4 | ||
|
|
db036e9ea8 | ||
|
|
51eee6e4f2 | ||
|
|
7c0b9cf861 | ||
|
|
9137040334 | ||
|
|
9d4606cb21 | ||
|
|
299cc6ce7a | ||
|
|
b8af2e11d1 | ||
|
|
d6e1abdb2a | ||
|
|
486c0e7448 | ||
|
|
ccfb631e21 | ||
|
|
14663fbbd3 | ||
|
|
1f5c6a7010 |
@@ -1,13 +0,0 @@
|
||||
# editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
-54
@@ -1,54 +0,0 @@
|
||||
# Generated by FOSSA CLI (https://github.com/fossas/fossa-cli)
|
||||
# Visit https://fossa.com to learn more
|
||||
|
||||
version: 3
|
||||
|
||||
project:
|
||||
id: appium
|
||||
name: appium
|
||||
|
||||
paths:
|
||||
exclude:
|
||||
- ./sample-code
|
||||
- '**/fixtures/**'
|
||||
- '**/demo/**'
|
||||
- '**/example/**'
|
||||
- '**/examples/**'
|
||||
|
||||
targets:
|
||||
only:
|
||||
- type: npm
|
||||
path: .
|
||||
- type: npm
|
||||
path: ./packages/appium
|
||||
- type: npm
|
||||
path: ./packages/base-driver
|
||||
- type: npm
|
||||
path: ./packages/eslint-config-appium-ts
|
||||
- type: npm
|
||||
path: ./packages/fake-driver
|
||||
- type: npm
|
||||
path: ./packages/support
|
||||
- type: npm
|
||||
path: ./packages/test-support
|
||||
- type: npm
|
||||
path: ./packages/types
|
||||
- type: npm
|
||||
path: ./packages/schema
|
||||
- type: npm
|
||||
path: ./packages/universal-xmlplugin
|
||||
- type: npm
|
||||
path: ./packages/opencv
|
||||
- type: npm
|
||||
path: ./packages/images-plugin
|
||||
- type: npm
|
||||
path: ./packages/base-plugin
|
||||
- type: npm
|
||||
path: ./packages/relaxed-caps-plugin
|
||||
- type: npm
|
||||
path: ./packages/fake-plugin
|
||||
- type: npm
|
||||
path: ./packages/docutils
|
||||
|
||||
revision:
|
||||
branch: master
|
||||
@@ -1,4 +0,0 @@
|
||||
* text=auto eol=lf
|
||||
|
||||
# Handle as a text file but merge as binary.
|
||||
package-lock.json merge=binary
|
||||
@@ -1,111 +0,0 @@
|
||||
name: 🐞 Bug Report
|
||||
description: Create a new bug report.
|
||||
title: '🐞 bug: <title>'
|
||||
labels: [Bug, Needs Triage]
|
||||
body:
|
||||
|
||||
- type: markdown
|
||||
attributes:
|
||||
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: 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
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
attributes:
|
||||
label: Appium Version
|
||||
description: What Appium server version are you running?
|
||||
placeholder: Output of `appium --version`
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- 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:
|
||||
|
||||
- _Attach_ (do not paste) it to this textarea, _or_
|
||||
- Put it in a [Gist](https://gist.github.com) and paste the link, _or_
|
||||
- Provide a link to a new or existing public repository exhibiting the issue
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Further Information
|
||||
description: If needed, please provide additional information about your issue (attachments, links, references, etc.).
|
||||
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: ':stop_sign: _Make sure you are reporting an issue. For general questions, please use [http://discuss.appium.io](http://discuss.appium.io)._'
|
||||
@@ -1,5 +0,0 @@
|
||||
blank_issues_enabled: true
|
||||
contact_links:
|
||||
- name: ❓ Question
|
||||
url: https://discuss.appium.io/
|
||||
about: Please use the Appium forum for any questions.
|
||||
@@ -1,33 +0,0 @@
|
||||
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
|
||||
@@ -1,28 +0,0 @@
|
||||
## Proposed changes
|
||||
|
||||
Describe the big picture of your changes here to communicate to the maintainers why we should accept this pull request. If it fixes a bug or resolves a feature request, be sure to link to that issue.
|
||||
|
||||
## Types of changes
|
||||
|
||||
What types of changes does your code introduce to Appium?
|
||||
_Put an `x` in the boxes that apply_
|
||||
|
||||
- [ ] Bugfix (non-breaking change which fixes an issue)
|
||||
- [ ] New feature (non-breaking change which adds functionality)
|
||||
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
|
||||
- [ ] Documentation Update (if none of the other choices apply)
|
||||
|
||||
## Checklist
|
||||
|
||||
_Put an `x` in the boxes that apply. You can also fill these out after creating the PR. If you're unsure about any of them, don't hesitate to ask. We're here to help! This is simply a reminder of what we are going to look for before merging your code._
|
||||
|
||||
- [ ] I have read the [Contributing Guide](https://appium.io/docs/en/latest/contributing/)
|
||||
- [ ] I have signed the CLA
|
||||
- [ ] Lint and unit tests pass locally with my changes
|
||||
- [ ] I have added tests that prove my fix is effective or that my feature works
|
||||
- [ ] I have added the necessary documentation (if appropriate)
|
||||
- [ ] Any dependent changes have been merged and published in downstream modules
|
||||
|
||||
## Further comments
|
||||
|
||||
If this is a relatively large or complex change, kick off the discussion by explaining why you chose the solution you did and what alternatives you considered, etc...
|
||||
@@ -1,149 +0,0 @@
|
||||
version: v1
|
||||
|
||||
labels:
|
||||
- label: 'Enhancement'
|
||||
sync: true
|
||||
matcher:
|
||||
commits: '^feat.*?:'
|
||||
title: '^feat.*?:'
|
||||
|
||||
- label: 'Bug'
|
||||
sync: true
|
||||
matcher:
|
||||
commits: '^fix.*?:'
|
||||
title: '^fix.*?:'
|
||||
|
||||
- label: 'Documentation'
|
||||
sync: true
|
||||
matcher:
|
||||
commits: '^docs.*?:'
|
||||
title: '^docs.*?:'
|
||||
files: ['packages/docutils/**', 'packages/appium/docs/**', '**/*.md']
|
||||
|
||||
- label: 'chore'
|
||||
sync: true
|
||||
matcher:
|
||||
commits: '^chore.*?:'
|
||||
title: '^chore.*?:'
|
||||
files: ['.github/**', '.husky/**', '.vscode/**', './.*', './*.js', '**/test/**']
|
||||
|
||||
- label: 'SemVer:Breaking'
|
||||
sync: true
|
||||
matcher:
|
||||
commits: '(^(.+?)!:)|(BREAKING CHANGE:)'
|
||||
|
||||
- label: 'Dependencies'
|
||||
sync: true
|
||||
matcher:
|
||||
files: ['**/package.json', 'package-lock.json', '**/requirements.txt']
|
||||
author: 'renovate'
|
||||
|
||||
- label: 'appium core'
|
||||
sync: true
|
||||
matcher:
|
||||
files: ['packages/appium/**']
|
||||
|
||||
- label: '@appium/base-driver'
|
||||
sync: true
|
||||
matcher:
|
||||
files: ['packages/base-driver/**']
|
||||
|
||||
- label: '@appium/base-plugin'
|
||||
sync: true
|
||||
matcher:
|
||||
files: ['packages/base-plugin/**']
|
||||
|
||||
- label: '@appium/storage-plugin'
|
||||
sync: true
|
||||
matcher:
|
||||
files: ['packages/storage-plugin/**']
|
||||
|
||||
- label: '@appium/docutils'
|
||||
sync: true
|
||||
matcher:
|
||||
files: ['packages/docutils/**']
|
||||
|
||||
- label: '@appium/driver-test-support'
|
||||
sync: true
|
||||
matcher:
|
||||
files: ['packages/driver-test-support/**']
|
||||
|
||||
- label: '@appium/eslint-config-appium-ts'
|
||||
sync: true
|
||||
matcher:
|
||||
files: ['packages/eslint-config-appium-ts/**']
|
||||
|
||||
- label: '@appium/execute-driver-plugin'
|
||||
sync: true
|
||||
matcher:
|
||||
files: ['packages/execute-driver-plugin/**']
|
||||
|
||||
- label: '@appium/fake-driver'
|
||||
sync: true
|
||||
matcher:
|
||||
files: ['packages/fake-driver/**']
|
||||
|
||||
- label: '@appium/fake-plugin'
|
||||
sync: true
|
||||
matcher:
|
||||
files: ['packages/fake-plugin/**']
|
||||
|
||||
- label: '@appium/images-plugin'
|
||||
sync: true
|
||||
matcher:
|
||||
files: ['packages/images-plugin/**']
|
||||
|
||||
- label: '@appium/logger'
|
||||
sync: true
|
||||
matcher:
|
||||
files: ['packages/logger/**']
|
||||
|
||||
- label: '@appium/opencv'
|
||||
sync: true
|
||||
matcher:
|
||||
files: ['packages/opencv/**']
|
||||
|
||||
- label: '@appium/plugin-test-support'
|
||||
sync: true
|
||||
matcher:
|
||||
files: ['packages/plugin-test-support/**']
|
||||
|
||||
- label: '@appium/relaxed-caps-plugin'
|
||||
sync: true
|
||||
matcher:
|
||||
files: ['packages/relaxed-caps-plugin/**']
|
||||
|
||||
- label: '@appium/schema'
|
||||
sync: true
|
||||
matcher:
|
||||
files: ['packages/schema/**']
|
||||
|
||||
- label: '@appium/support'
|
||||
sync: true
|
||||
matcher:
|
||||
files: ['packages/support/**']
|
||||
|
||||
- label: '@appium/strongbox'
|
||||
sync: true
|
||||
matcher:
|
||||
files: ['packages/strongbox/**']
|
||||
|
||||
- label: '@appium/test-support'
|
||||
sync: true
|
||||
matcher:
|
||||
files: ['packages/test-support/**']
|
||||
|
||||
- label: '@appium/tsconfig'
|
||||
sync: true
|
||||
matcher:
|
||||
files: ['packages/tsconfig/**']
|
||||
|
||||
- label: '@appium/types'
|
||||
sync: true
|
||||
matcher:
|
||||
files: ['packages/types/**']
|
||||
|
||||
- label: '@appium/universal-xml-plugin'
|
||||
sync: true
|
||||
matcher:
|
||||
files: ['packages/universal-xml-plugin/**']
|
||||
@@ -1,26 +0,0 @@
|
||||
# Configuration for Lock Threads - https://github.com/dessant/lock-threads
|
||||
|
||||
# Number of days of inactivity before a closed issue or pull request is locked
|
||||
daysUntilLock: 365
|
||||
|
||||
# Skip issues and pull requests created before a given timestamp. Timestamp must
|
||||
# follow ISO 8601 (`YYYY-MM-DD`). Set to `false` to disable
|
||||
skipCreatedBefore: false
|
||||
|
||||
# Issues and pull requests with these labels will be ignored. Set to `[]` to disable
|
||||
exemptLabels: []
|
||||
|
||||
# Label to add before locking, such as `outdated`. Set to `false` to disable
|
||||
lockLabel: false
|
||||
|
||||
# Comment to post before locking. Set to `false` to disable
|
||||
lockComment: >
|
||||
This thread has been automatically locked since there has not been
|
||||
any recent activity after it was closed. Please open a new issue for
|
||||
related bugs.
|
||||
|
||||
# Assign `resolved` as the reason for locking. Set to `false` to disable
|
||||
setLockReason: false
|
||||
|
||||
# Limit to only `issues` or `pulls`
|
||||
only: issues
|
||||
@@ -1,16 +0,0 @@
|
||||
name: Labeler
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened, synchronize]
|
||||
branches: [master]
|
||||
|
||||
permissions: read-all
|
||||
|
||||
jobs:
|
||||
labeler:
|
||||
permissions:
|
||||
pull-requests: write
|
||||
name: Auto-Label PRs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: fuxingloh/multi-labeler@v4
|
||||
@@ -1,105 +0,0 @@
|
||||
name: Appium Build
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
# this list of paths could potentially impact the build
|
||||
paths:
|
||||
- '**/tsconfig*.json'
|
||||
- 'packages/base-driver/static/**/*'
|
||||
- 'packages/*/test/**'
|
||||
- 'packages/*/bin'
|
||||
- 'packages/**/*.ts'
|
||||
- 'packages/**/*.json'
|
||||
- 'packages/**/*.js'
|
||||
- 'package*.json'
|
||||
- 'lerna.json'
|
||||
- '.npmrc'
|
||||
- '.mocharc.js'
|
||||
- '.github/workflows/build.yml' # this file
|
||||
- '.eslintrc'
|
||||
- '.eslintignore'
|
||||
- '!**/sample-code/**'
|
||||
- '!packages/*/docs/**'
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- '**/tsconfig*.json'
|
||||
- 'packages/base-driver/static/**/*'
|
||||
- 'packages/*/test/**'
|
||||
- 'packages/*/bin'
|
||||
- 'packages/**/*.ts'
|
||||
- 'packages/**/*.json'
|
||||
- 'packages/**/*.js'
|
||||
- 'package*.json'
|
||||
- 'lerna.json'
|
||||
- '.npmrc'
|
||||
- '.mocharc.js'
|
||||
- '.github/workflows/build.yml' # this file
|
||||
- '.eslintrc'
|
||||
- '.eslintignore'
|
||||
- '!**/sample-code/**'
|
||||
- '!packages/*/docs/**'
|
||||
|
||||
env:
|
||||
CI: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
prepare_matrix:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
versions: ${{ steps.generate-matrix.outputs.lts }}
|
||||
steps:
|
||||
- name: Select all current LTS versions of Node.js
|
||||
id: generate-matrix
|
||||
uses: msimerson/node-lts-versions@v1
|
||||
|
||||
test:
|
||||
needs:
|
||||
- prepare_matrix
|
||||
name: Tests
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: ${{ fromJSON(needs.prepare_matrix.outputs.versions) }}
|
||||
os:
|
||||
- ubuntu-latest
|
||||
# TODO: Windows is not stable and slow to run
|
||||
# - windows-latest
|
||||
# TODO: Enable below envs after all tests have been verified green
|
||||
# - macos-latest
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
cache: 'npm'
|
||||
- name: Install dependencies
|
||||
uses: bahmutov/npm-install@v1
|
||||
with:
|
||||
useRollingCache: true
|
||||
install-command: npm ci --foreground-scripts
|
||||
- name: Run smoke, unit & E2E tests
|
||||
run: npm run test:ci
|
||||
|
||||
lint:
|
||||
name: Lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install dependencies
|
||||
uses: bahmutov/npm-install@v1
|
||||
with:
|
||||
useRollingCache: true
|
||||
install-command: npm ci
|
||||
- name: ESLint
|
||||
run: npm run lint:ci
|
||||
@@ -1,36 +0,0 @@
|
||||
name: Cache Cleanup
|
||||
|
||||
# from https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#usage-limits-and-eviction-policy
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- closed
|
||||
|
||||
jobs:
|
||||
cleanup:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Cleanup
|
||||
run: |
|
||||
gh extension install actions/gh-actions-cache
|
||||
|
||||
REPO=${{ github.repository }}
|
||||
BRANCH="refs/pull/${{ github.event.pull_request.number }}/merge"
|
||||
|
||||
echo "Fetching list of cache keys"
|
||||
cacheKeysForPR=$(gh actions-cache list -R $REPO -B $BRANCH | cut -f 1 )
|
||||
|
||||
## Setting this to not fail the workflow while deleting cache keys.
|
||||
set +e
|
||||
echo "Deleting caches..."
|
||||
for cacheKey in $cacheKeysForPR
|
||||
do
|
||||
gh actions-cache delete $cacheKey -R $REPO -B $BRANCH --confirm
|
||||
done
|
||||
echo "Done"
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -1,38 +0,0 @@
|
||||
# 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.8
|
||||
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'
|
||||
@@ -1,31 +0,0 @@
|
||||
# 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 }}
|
||||
@@ -1,68 +0,0 @@
|
||||
name: Appium Deploy
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- master
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
prepare_matrix:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
versions: ${{ steps.generate-matrix.outputs.lts }}
|
||||
steps:
|
||||
- name: Select all current LTS versions of Node.js
|
||||
id: generate-matrix
|
||||
uses: msimerson/node-lts-versions@v1
|
||||
|
||||
check:
|
||||
needs:
|
||||
- prepare_matrix
|
||||
name: Check
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: ${{ fromJSON(needs.prepare_matrix.outputs.versions) }}
|
||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||
steps:
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- name: Install Appium
|
||||
run: |
|
||||
if [[ "$GITHUB_REF_TYPE" == tag ]]; then
|
||||
npm i --location=global $GITHUB_REF_NAME
|
||||
else
|
||||
npm i --location=global appium
|
||||
fi
|
||||
shell: bash
|
||||
- name: Install drivers and plugins
|
||||
run: |
|
||||
if grep -q macos <<< ${{ matrix.os }}; then
|
||||
appium driver install xcuitest
|
||||
fi
|
||||
appium driver install uiautomator2
|
||||
appium plugin install images
|
||||
shell: bash
|
||||
- name: Verify installation
|
||||
run: |
|
||||
installed_drivers=$(appium driver list --installed 2>&1)
|
||||
if grep -q macos <<< ${{ matrix.os }}; then
|
||||
grep -q xcuitest <<< $installed_drivers
|
||||
fi
|
||||
grep -q uiautomator2 <<< $installed_drivers
|
||||
installed_plugins=$(appium plugin list --installed 2>&1)
|
||||
grep -q images <<< $installed_plugins
|
||||
shell: bash
|
||||
- name: Verify Appium could be executed
|
||||
run: |
|
||||
appium --use-plugins=images &
|
||||
pid=$(echo $!)
|
||||
sleep 5
|
||||
ps -p $pid || exit 1
|
||||
kill -2 $pid
|
||||
shell: bash
|
||||
@@ -1,53 +0,0 @@
|
||||
name: Build Docs
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- '**/tsconfig*.json'
|
||||
- '**/package*.json'
|
||||
- '**/*mkdocs*.ya?ml'
|
||||
- 'packages/docutils/requirements.txt'
|
||||
- 'packages/*/README.md'
|
||||
- 'packages/*/docs/**'
|
||||
- 'packages/*/**/*.ts'
|
||||
- 'packages/*/**/*.js'
|
||||
- '.github/workflows/docs.yml' # this file
|
||||
- '!**/test/**'
|
||||
- '!**/sample-code/**'
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- '**/tsconfig*.json'
|
||||
- '**/package*.json'
|
||||
- '**/*mkdocs*.ya?ml'
|
||||
- 'packages/docutils/requirements.txt'
|
||||
- 'packages/*/README.md'
|
||||
- 'packages/*/docs/**'
|
||||
- 'packages/*/**/*.ts'
|
||||
- 'packages/*/**/*.js'
|
||||
- '.github/workflows/docs.yml' # this file
|
||||
- '!**/test/**'
|
||||
- '!**/sample-code/**'
|
||||
|
||||
jobs:
|
||||
docs:
|
||||
name: Docs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install dependencies (Node.js)
|
||||
uses: bahmutov/npm-install@v1
|
||||
with:
|
||||
useRollingCache: true
|
||||
install-command: npm ci
|
||||
- name: Install dependencies (Python)
|
||||
run: PIP_BREAK_SYSTEM_PACKAGES=1 pip install -r packages/docutils/requirements.txt
|
||||
- name: Configure Git User
|
||||
run: |
|
||||
git config --local user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git config --local user.name "github-actions[bot]"
|
||||
- name: Build Docs
|
||||
run: npm run docs:preview
|
||||
@@ -1,31 +0,0 @@
|
||||
name: License Checks
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
fossa-check:
|
||||
name: Run FOSSA Analysis
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
FOSSA_API_KEY: ${{secrets.FOSSA_API_KEY}}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: master
|
||||
- name: Install dependencies
|
||||
uses: bahmutov/npm-install@v1
|
||||
- name: Install fossa-cli
|
||||
run: |
|
||||
curl -H 'Cache-Control: no-cache' https://raw.githubusercontent.com/fossas/fossa-cli/master/install-latest.sh | bash
|
||||
- name: Run FOSSA analysis
|
||||
run: |
|
||||
if [ -n "$FOSSA_API_KEY" ]; then
|
||||
fossa analyze
|
||||
fossa test
|
||||
else
|
||||
echo "License checks did not run. FOSSA_API_KEY is not defined for this fork."
|
||||
fi
|
||||
@@ -1,15 +0,0 @@
|
||||
name: Disallow "On Hold" PRs
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened, labeled, unlabeled, synchronize]
|
||||
branches: [master]
|
||||
|
||||
jobs:
|
||||
label-check:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check for "On Hold" label
|
||||
uses: egmacke/action-check-label@v4
|
||||
with:
|
||||
label: On Hold
|
||||
state: absent
|
||||
@@ -1,15 +0,0 @@
|
||||
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 }}
|
||||
@@ -1,38 +0,0 @@
|
||||
# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node
|
||||
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs
|
||||
|
||||
name: Publish docs
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [18.x]
|
||||
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- run: |
|
||||
git config user.name github-actions
|
||||
git config user.email github-actions@github.com
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- name: Install dependencies (Node.js)
|
||||
uses: bahmutov/npm-install@v1
|
||||
with:
|
||||
useRollingCache: true
|
||||
install-command: npm ci
|
||||
- name: Install dependencies (Python)
|
||||
run: PIP_BREAK_SYSTEM_PACKAGES=1 pip install -r packages/docutils/requirements.txt
|
||||
- name: Publish docs
|
||||
run: |
|
||||
cd packages/appium
|
||||
npm run publish:docs
|
||||
@@ -1,30 +0,0 @@
|
||||
name: Validate Renovate Configs
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- '.github/workflows/renovate.yml' # this file
|
||||
- '.renovaterc.json'
|
||||
- 'renovate/default.json'
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- '.github/workflows/renovate.yml' # this file
|
||||
- '.renovaterc.json'
|
||||
- 'renovate/default.json'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
renovate:
|
||||
name: Check Renovate Configs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Validate Renovate Config
|
||||
uses: rinchsan/renovate-config-validator@v0.2.0
|
||||
with:
|
||||
pattern: '{.renovaterc.json,renovate/default.json}'
|
||||
+3
-65
@@ -1,68 +1,6 @@
|
||||
.irb-history
|
||||
*.swp
|
||||
.DS_Store
|
||||
node_modules
|
||||
*.trace
|
||||
xcuserdata
|
||||
*.sublime-*
|
||||
test/functional/_joined
|
||||
*.iml
|
||||
!examples/java/*.iml
|
||||
*.autosave
|
||||
.idea/
|
||||
!examples/java/.idea/
|
||||
build
|
||||
*.log
|
||||
*.t
|
||||
artifacts
|
||||
input
|
||||
./output
|
||||
lib/devices/android/bootstrap/bin/*
|
||||
lib/devices/android/bootstrap/local.properties
|
||||
lib/devices/android/bootstrap/project.properties
|
||||
lib/devices/android/bootstrap/.classpath
|
||||
lib/devices/android/bootstrap/target/
|
||||
.*~
|
||||
*~
|
||||
org.eclipse.ltk.core.refactoring.prefs
|
||||
.appiumconfig*
|
||||
build/
|
||||
.idea
|
||||
*DerivedData
|
||||
__pycache__
|
||||
certificate.cert
|
||||
certificate.key
|
||||
|
||||
# csharp
|
||||
*.pyc
|
||||
*.pdb
|
||||
.vs
|
||||
|
||||
#vscode
|
||||
.history
|
||||
|
||||
old
|
||||
.tern-project
|
||||
working
|
||||
.DS_Store
|
||||
coverage
|
||||
docs/mkdocs.yml
|
||||
examples/javascript-wd/node_modules
|
||||
examples/php/vendor
|
||||
examples/php/composer.lock
|
||||
examples/java/out/
|
||||
examples/ruby/Gemfile.lock
|
||||
examples/python/test/results/
|
||||
examples/python/test/input
|
||||
examples/python/.pytest_cache/
|
||||
examples/csharp/test/bin
|
||||
examples/csharp/test/obj
|
||||
examples/csharp/test/bin
|
||||
examples/csharp/packages
|
||||
appium.zip
|
||||
.vscode/settings.json
|
||||
.nyc_output
|
||||
*.lerna_backup
|
||||
**/build-fixtures/**
|
||||
*.tsbuildinfo
|
||||
packages/*/LICENSE
|
||||
\!packages/*/LICENSE
|
||||
local_appium_home
|
||||
*.tgz
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"excludeFiles": ["node_modules/**"],
|
||||
"requireCurlyBraces": ["for", "while", "do", "try", "catch"],
|
||||
"requireSpaceAfterKeywords": ["if", "else", "for", "while", "do", "switch",
|
||||
"return", "try", "catch", "function"],
|
||||
"disallowMixedSpacesAndTabs": true,
|
||||
"disallowTrailingWhitespace": true,
|
||||
"requireSpacesInFunctionExpression": {
|
||||
"beforeOpeningCurlyBrace": true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"laxcomma": true,
|
||||
"undef": true,
|
||||
"unused": true,
|
||||
"node": true,
|
||||
"eqeqeq": true,
|
||||
"trailing": true,
|
||||
"indent": 2,
|
||||
"esnext": true,
|
||||
"experimental": ["asyncawait"]
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
module.exports = {
|
||||
'*.(js|ts)': ['eslint --fix', 'prettier --write'],
|
||||
'appium-config-schema.js': () => [
|
||||
'npm run --workspace=./packages/schema build',
|
||||
'git add -A packages/schema/lib/appium-config.schema.json',
|
||||
'npm run --workspace=./packages/types build',
|
||||
'git add -A packages/types/lib/appium-config.ts',
|
||||
],
|
||||
'!(package|package-lock)*.json': ['prettier --write'],
|
||||
};
|
||||
-11
@@ -1,11 +0,0 @@
|
||||
// @ts-check
|
||||
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
require: ['ts-node/register'],
|
||||
// forbids use of .only() in CI
|
||||
forbidOnly: Boolean(process.env.CI),
|
||||
color: true,
|
||||
timeout: '5s',
|
||||
};
|
||||
@@ -0,0 +1,2 @@
|
||||
node_modules
|
||||
*.log
|
||||
@@ -1,4 +0,0 @@
|
||||
# Do not disable package-lock. Disabling package-lock also disables npm-shrinkwrap
|
||||
package-lock=true
|
||||
save-exact=true
|
||||
foreground-scripts=true
|
||||
@@ -1,13 +0,0 @@
|
||||
**/node_modules/**
|
||||
**/build/**
|
||||
**/fixtures/**
|
||||
**/*.min.*
|
||||
**/*.md
|
||||
package-lock.json
|
||||
package.json
|
||||
**/.vscode/**
|
||||
**/*.html
|
||||
**/generated/**
|
||||
# generated
|
||||
packages/types/lib/appium-config.ts
|
||||
**/*.hbs
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": ["github>appium/appium//renovate/default"],
|
||||
"schedule": [
|
||||
"after 10pm",
|
||||
"before 5:00am"
|
||||
],
|
||||
"timezone": "America/Vancouver"
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- "0.12"
|
||||
after_success:
|
||||
- gulp coveralls
|
||||
Vendored
-5
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"stateful.runme"
|
||||
]
|
||||
}
|
||||
Vendored
-50
@@ -1,50 +0,0 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"args": [
|
||||
"--log-level=debug"
|
||||
],
|
||||
"console": "integratedTerminal",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"env": {
|
||||
"_FORCE_LOGS": "1"
|
||||
},
|
||||
"internalConsoleOptions": "openOnSessionStart",
|
||||
"name": "Run Appium",
|
||||
"program": "${workspaceFolder}/node_modules/.bin/appium",
|
||||
"request": "launch",
|
||||
"skipFiles": [
|
||||
"<node_internals>/**"
|
||||
],
|
||||
"sourceMaps": true,
|
||||
"type": "node"
|
||||
},
|
||||
{
|
||||
"args": [
|
||||
"--colors",
|
||||
"--no-timeout",
|
||||
"${file}"
|
||||
],
|
||||
"autoAttachChildProcesses": true,
|
||||
"console": "integratedTerminal",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"internalConsoleOptions": "openOnSessionStart",
|
||||
"name": "Test Current File w/ Mocha",
|
||||
"program": "${workspaceFolder}/node_modules/.bin/mocha",
|
||||
"request": "launch",
|
||||
"runtimeArgs": [
|
||||
"--require",
|
||||
"ts-node/register"
|
||||
],
|
||||
"skipFiles": [
|
||||
"<node_internals>/**",
|
||||
"**/node_modules/mocha/**",
|
||||
"**/node_modules/lodash/**",
|
||||
"**/node_modules/ts-node/**"
|
||||
],
|
||||
"sourceMaps": true,
|
||||
"type": "node"
|
||||
}
|
||||
],
|
||||
"version": "0.2.0"
|
||||
}
|
||||
Vendored
-19
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "dev",
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
},
|
||||
"problemMatcher": [
|
||||
"$tsc-watch"
|
||||
],
|
||||
"label": "npm: dev",
|
||||
"detail": "npm run dev",
|
||||
"isBackground": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
Authors ordered by first contribution
|
||||
|
||||
Dan Cuellar <dancuellar@me.com>
|
||||
E. James Infusino <jinfusino@gmail.com>
|
||||
Jason Huggins <hugs@saucelabs.com>
|
||||
Jason Carr <jcarr@saucelabs.com>
|
||||
Jayme Deffenbaugh <jdeffenbaugh@me.com>
|
||||
Roman Salvador <roman.salvador@gmail.com>
|
||||
Luke Inman-Semerau <luke.semerau@gmail.com>
|
||||
Pradeep Bishnoi <pradeepbishnoi@gmail.com>
|
||||
Charles Nowacek <charlie.nowacek@gmail.com>
|
||||
Jayakumar Chinnappan <jayakumareee21@gmail.com>
|
||||
Robin Keller <robinthekeller@gmail.com>
|
||||
Adam Christian <adam.christian@gmail.com>
|
||||
Jonathan Lipps <jlipps@gmail.com>
|
||||
Sebastian Tiedtke <sebastiantiedtke@gmail.com>
|
||||
Christian Bromann <mail@christian-bromann.com>
|
||||
Jeremy Avnet <>
|
||||
Bernard Kobos <bkobos@extensa.pl>
|
||||
Santiago Suarez Ordoñez <santiycr@gmail.com>
|
||||
Joe Mathes <>
|
||||
@@ -1,45 +0,0 @@
|
||||
# Aggregate Changelog
|
||||
|
||||
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)
|
||||
|
||||
## Drivers / Clients
|
||||
Refer to the [Appium Ecosystem](https://appium.io/docs/en/latest/ecosystem/) page for links
|
||||
to driver/client documentation
|
||||
|
||||
## 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)
|
||||
* [docutils](packages/docutils/CHANGELOG.md)
|
||||
* [driver-test-support](packages/driver-test-support/CHANGELOG.md)
|
||||
* [eslint-config-appium-ts](packages/eslint-config-appium-ts/CHANGELOG.md)
|
||||
* [fake-driver](packages/fake-driver/CHANGELOG.md)
|
||||
* [fake-plugin](packages/fake-plugin/CHANGELOG.md)
|
||||
* [logger](packages/logger/CHANGELOG.md)
|
||||
* [opencv](packages/opencv/CHANGELOG.md)
|
||||
* [plugin-test-support](packages/plugin-test-support/CHANGELOG.md)
|
||||
* [schema](packages/schema/CHANGELOG.md)
|
||||
* [storage-plugin](packages/storage-plugin/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)
|
||||
|
||||
## 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))
|
||||
* [eslint-config-appium](https://github.com/appium/appium/blob/4b3a8c2197a587ec5a2606b662e4e7667a7852cb/packages/eslint-config-appium/CHANGELOG.md) ([removal PR](https://github.com/appium/appium/pull/20856))
|
||||
* [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))
|
||||
-48
@@ -1,48 +0,0 @@
|
||||
## Code of Conduct
|
||||
|
||||
### What is this code of conduct for?
|
||||
|
||||
Appium is a piece of technology, but **the core of the Appium community is the people in it**. As such, we are committed to providing a friendly, safe and welcoming environment for all, regardless of gender, gender identity and expression, sexual orientation, ability, physical appearance, body size, race, age, socioeconomic status, religion (or lack thereof), or other marginalized aspect of community members. We expect all members of the Appium community to abide by this Code of Conduct whenever interacting in Appium venues (pull requests, GitHub issues, 1-1 or group chat, meetups, conferences, etc...)
|
||||
|
||||
### Examples of inappropriate behavior
|
||||
|
||||
Because we come from a variety of backgrounds, we don't want to assume that everyone has the same assumptions about what is and isn't appropriate. Here are some examples of inappropriate behavior that are incompatible with our community's ethos:
|
||||
|
||||
* Spamming, trolling, intentionally disrupting conversations, or irrelevant solicitation or advertisement
|
||||
* Making demeaning or discriminatory comments
|
||||
* Making negative assumptions about someone's background, abilities, or intentions
|
||||
* Harassing or stalking individuals (online or in person)
|
||||
* Giving someone unwelcome sexual attention or making unwelcome physical contact (in the case of an IRL event)
|
||||
* Sharing sexual images or using sexually explicit language
|
||||
|
||||
In general: treat others how you would like to be treated, were you in their place. Don't be a jerk. _Do_ ask questions. _Do_ keep conflicts productively focused on technical issues. _Do_ think before you speak; remember that what is perceived as a funny witticism in your group of friends might be hurtful or reinforce hurtful stereotypes in the context of our diverse online community. _Do_ remember that we are all people, not robots, and all equally deserving of sensitivity and respect. (If and when robots join our community, let's treat them with respect too!)
|
||||
|
||||
|
||||
### What will organizers do about inappropriate behavior?
|
||||
|
||||
If we notice you doing or saying something inappropriate, an organizer will explain why it's inappropriate and ask you to stop. We won't demonize or vilify you. But please do stop the inappropriate behavior so we can get back to writing and discussing code in a safe environment. If you have philosophical disagreements about what's actually inappropriate, please take them to a separate public or private conversation with an Appium maintainer so we don't turn pull requests into an ethics debate.
|
||||
|
||||
If you keep doing unacceptable things, we'll likely ban you, report you to GitHub, or take other appropriate action.
|
||||
|
||||
### What if I see or am subject to what feels like inappropriate behavior?
|
||||
|
||||
Let us know! Please notify a community organizer as soon as possible. Full contact information is listed in the [Contact Info](#contact-info) section of this document. All communications will be kept strictly confidential, unless otherwise required by law. No issue will be considered too inconsequential or unimportant for us to have a conversation about.
|
||||
|
||||
### Contact Info
|
||||
|
||||
If you need to report an incident, please contact any of the following organizers directly:
|
||||
|
||||
* Isaac Murchie [email](mailto:isaac@saucelabs.com) [twitter](https://twitter.com/imurchie)
|
||||
* Jonathan Lipps [email](mailto:jlipps@saucelabs.com) [twitter](https://twitter.com/jlipps)
|
||||
|
||||
### Credit, License, and Attribution
|
||||
|
||||
This Code of Conduct is distributed under a [Creative Commons Attribution-ShareAlike license](http://creativecommons.org/licenses/by-sa/3.0/).
|
||||
|
||||
It's inspired among other things by:
|
||||
* [Citizen Code of Conduct](http://citizencodeofconduct.org/)
|
||||
* [npmjs](https://www.npmjs.com/policies/conduct)
|
||||
* [Geek Feminism](http://geekfeminism.wikia.com/wiki/Conference_anti-harassment/Policy)
|
||||
* [Ashe Dryden](http://www.ashedryden.com/blog/codes-of-conduct-101-faq)
|
||||
* [Model View Culture](https://modelviewculture.com/issues/events)
|
||||
* [Open Source & Feelings](http://osfeels.com/conduct).
|
||||
-185
@@ -1,185 +0,0 @@
|
||||
# Appium Project Governance
|
||||
|
||||
The Appium Project wants as much as possible to operate using procedures that
|
||||
are fair, open, inviting, and ultimately good for the community. For that
|
||||
reason we find it valuable to codify some of the ways that the Project goes
|
||||
about its day-to-day business. We want to make sure that no matter who you are,
|
||||
you have the opportunity to contribute to Appium. We want to make sure that no
|
||||
one corporation can exert undue influence on the community or hold the Project
|
||||
hostage. And likewise we want to make sure that corporations which benefit from
|
||||
Appium are also incentivized to give back.
|
||||
|
||||
### The Technical Committee
|
||||
|
||||
The project is officially led by a Technical Committee (TC), currently
|
||||
consisting of:
|
||||
|
||||
* [@jlipps](https://github.com/jlipps)
|
||||
* [@mykola-mokhnach](https://github.com/mykola-mokhnach)
|
||||
* [@eglitise](https://github.com/eglitise)
|
||||
* [@KazuCocoa](https://github.com/KazuCocoa)
|
||||
|
||||
They all represent different companies and different types of contribution to
|
||||
the Appium Project. The TC is responsible for high-level decisions (like
|
||||
establishing a feature roadmap, accepting sponsorship, organizing conferences,
|
||||
etc...), adjudicating conflicts between members of the community, and making
|
||||
changes to the project's governance processes.
|
||||
|
||||
* Membership in the TC is granted by unanimous vote of current TC members.
|
||||
* TC members can step down at any time.
|
||||
* No more than 1/3 (or 2, whichever is higher) of TC members may belong to the same company.
|
||||
* Changes to project governance require unanimous vote of the TC.
|
||||
|
||||
### Project Committers
|
||||
|
||||
Most of the project maintenance is handled by committers, community members who
|
||||
have been granted commit access to the Appium organization on GitHub. The
|
||||
responsibilities of committers include: reviewing and merging pull requests,
|
||||
walking through changes with developers, weighing in on architectural issues,
|
||||
and so on.
|
||||
|
||||
* Any two TC members can decide to make someone a project committer, providing that person has shown an appropriate understanding of the Appium codebase through prior contributions of code.
|
||||
* In general any committer can review and merge a PR. In general committers should only merge code they are qualified to review, which might entail pinging another committer who has greater ownership over a specific code area.
|
||||
* Debates between committers about whether code should be merged should happen in GitHub pull requests.
|
||||
* Proposals for large changes to the project's code (architectural changes, etc...) should be brought forward as a GitHub issue (with the label `Proposal`), and all committers should be pinged so they can weigh in on the discussion if desired. Substantial changes, whether in proposal stage or in pull request stage should be signed off on by 1 TC member and at least 2 other committers. To assist in the discussion, a small proof of concept can be undertaken on a subset of the Appium ecosystem and raised as a strawman PR, to give flesh to the proposal and make discussion more helpfully concrete.
|
||||
* In general disputes about code should be resolved by discussion, not votes. If there is substantial disagreement, aim for consensus. If that is not possible, committers can bring the dispute to the TC for a vote, with the outcome determined by a majority.
|
||||
* Any committer can decide to close a PR or issue if they determine the change doesn't suit the project.
|
||||
* Appium has a large ecosystem of repositories. Some are 'core' in the sense of being central subpackages with a lot of traffic like `appium/appium` or `appium/appium-ios-driver`. Others are 'peripheral' in the sense of receiving few changes, being maintained by 1 person or no one, etc..., like `appium/ruby_lib` or `appium/appium_thor` For 'core' repos, committers should not merge their own code straightaway. Like all contributors they should open a PR and get a +1 from another committer. For 'peripheral' repos for which they are the sole maintainer or for which they have a good understanding, it is obtuse to insist on another contributor's +1 and this rule should be relaxed in that case. In sum: use good judgment and don't ram code through without a review when you can help it.
|
||||
|
||||
The current committers are (along with their primary areas(s) of ownership or maintenance):
|
||||
|
||||
* [@dan-maor](https://github.com/Dan-Maor) - XCUITest driver and iOS internals
|
||||
* [@dor-bl](https://github.com/Dor-bl) - DotNet client
|
||||
* [@eglitise](https://github.com/eglitise) - Appium server, Inspector
|
||||
* [@jlipps](https://github.com/jlipps) - Appium server, Inspector
|
||||
* [@kazucocoa](https://github.com/KazuCocoa) - all Appium projects
|
||||
* [@laolubenson](https://github.com/laolubenson) - DotNet client
|
||||
* [@mwakizaka](https://github.com/mwakizaka) - XCUITest driver
|
||||
* [@mykola-mokhnach](https://github.com/mykola-mokhnach) - all Appium projects
|
||||
* [@rerorero](https://github.com/rerorero) - XCUITest/UIA2 driver
|
||||
* [@saikrishna321](https://github.com/saikrishna321) - Java client
|
||||
* [@srinivasantarget](https://github.com/SrinivasanTarget) - Java client
|
||||
* [@tomriddly](https://github.com/tomriddly) - appium-ios-device
|
||||
* [@valfirst](https://github.com/valfirst) - Java client
|
||||
|
||||
See [Emeritus Contributors](#emeritus-contributors) below for a list of people who formerly made
|
||||
significant contributions to the project but are no longer actively committing.
|
||||
|
||||
### Other Contributors
|
||||
|
||||
Other, less formal, kinds of contribution are outlined in our [Contributing Guide](https://appium.io/docs/en/latest/contributing/).
|
||||
We also very much appreciate those who spend their valuable time assisting with the [Appium forums](https://discuss.appium.io).
|
||||
Current high-level forum contributors and mods include:
|
||||
|
||||
* [aleksei](https://discuss.appium.io/u/aleksei/summary)
|
||||
* [wreed](https://discuss.appium.io/u/wreed/summary)
|
||||
|
||||
### Sponsorship
|
||||
|
||||
The Appium project welcomes the sponsorship of individuals and organizations. There are two types
|
||||
of sponsorship: those related to contributions of Appium maintenance and leadership, and those
|
||||
related to contributions of funds. Both are extremely important to the ongoing health of the
|
||||
project. Financial sponsorship is mediated through our [OpenCollective page](https://opencollective.com/appium).
|
||||
Sponsorship tiers and benefits are described below:
|
||||
|
||||
- **Development Partners**: The primary need of any open source project is for sustained maintenance and contributorship. Companies that officially sponsor such development with dedicated employee time are recognized as Development Partners, and receive the same benefits as the highest tier of financial sponsorship. To be considered a Development Partner, a company must devote at least 50% of a full-time employee's time to Appium maintenance. This means that the contributors designated by the company must have been appointed as "committers" in good standing of one or more core Appium repositories. (See above for a description of how to become a committer).
|
||||
- **Strategic Partners**: Representing the highest tier of ongoing financial support for the project, Strategic Partners are what make our compensation scheme for contributors possible, and we thank them for their significant investment into the project! More than financial contribution, Strategic Partners help elevate Appium's position in the industry through marketing and evangelism efforts, and partner with the Appium team to help make Appium better. Along with the Development Partners, Strategic Partners have their logos displayed prominently on our website, and have the ability to link to their Appium-related products in a few relevant places in our documentation.
|
||||
- **Gold, Silver, and Bronze Sponsors**: these sponsorship levels correspond to different amounts of recurring financial contribution that generously support our contributor compensation scheme. Sponsors at these levels also have the opportunity to display their logo on our website.
|
||||
- **Backers**: individuals who benefit from the Appium project can also donate on a one-time or recurring basis, at any amount, and will be recognized as Backers for their contribution!
|
||||
|
||||
To become a sponsor at one of these levels, follow the process indicated at the [OpenCollective
|
||||
page](https://opencollective.com/appium).
|
||||
|
||||
### Compensation Scheme
|
||||
|
||||
With funds received through sponsorship, the Appium project wants to incentivize contributions of
|
||||
code, documentation efforts, maintenance, and project leadership. We have developed a scheme
|
||||
(inspired by one developed at the [WebdriverIO](https://github.com/webdriverio/webdriverio)
|
||||
project) for disbursing funds on a monthly basis to those who contribute to the project, as well as
|
||||
some other "upstream" open source projects.
|
||||
|
||||
Each month, 15% of sponsorship funds received will be donated to upstream projects, based on the
|
||||
discretion of the current TC. At this point, no attempt will be made to formalize quantitative
|
||||
notions of relevance. If you manage a project upstream of Appium and would like to be considered as
|
||||
part of this donation group, give us a shout!
|
||||
|
||||
Of the remaining 85% of sponsorship funds, 70% (roughly 60% of the original total) will go to
|
||||
project Committers (i.e., those in a role of maintenance), and 30% (roughly 25% of the original
|
||||
total) will go to Contributors (i.e., those making one-off or periodic contributions). The way that
|
||||
Committers and Contributors are paid for their efforts differs.
|
||||
|
||||
For Committers, each Committer will keep a monthly time sheet of hours contributed to the project.
|
||||
At the end of each month, the hours tracked from all Committers will be totalled, and then the
|
||||
Committer-allocated funds will be disbursed (via OpenCollective) on a pro rata basis. Within this
|
||||
scheme, Committers agree not to submit hours worked as part of their employment with a Development
|
||||
Partner (since this is part of the "sponsorship" contribution of the Development Partner).
|
||||
|
||||
For Contributors, when they make contributions which are successfully merged into the project,
|
||||
a Committer can discretionarily assign a "value tier" to the contribution. The "value tiers" and
|
||||
corresponding payouts are as follows:
|
||||
|
||||
- XS: a trivial change (e.g. a typo change or applying of an automatically suggested security
|
||||
patch). \$0.
|
||||
- S: a small but useful change. \$25.
|
||||
- M: a medium-small or slightly more complex change. \$50.
|
||||
- L: a very valuable or sizable change. \$100.
|
||||
- XL: a massive change or set of changes. \$500.
|
||||
|
||||
At the end of each month, Committers will total up the number of changes receiving potential
|
||||
payout. If the total amount of funds allocated for Contributor payout exceeds the total amount to
|
||||
be paid, then each Contributor will receive an OpenCollective link to claim their funds. If not
|
||||
enough funds exist to cover all payouts, then the payout amounts will be scaled down in a pro rata
|
||||
fashion.
|
||||
|
||||
The scope for Committer or Contributor payouts is work completed within any "core" Appium
|
||||
repository (i.e., those officially maintained by the Appium team, residing within the Appium
|
||||
organization on GitHub, under `https://github.com/appium`).
|
||||
|
||||
Some disclaimers: Payments will happen exclusively via OpenCollective. Recipients of funds are
|
||||
responsible for maintaining their OpenCollective account in a fashion where funds can be disbursed,
|
||||
and are responsible for any and all tax, financial, or legal consequences of receiving money in
|
||||
this way. Receipt of funds from this scheme holds the Appium project and the OpenJS Foundation free
|
||||
of any claim or obligation. While this document exists to clearly and publicly lay out the terms
|
||||
under which payments are generally made, the TC retains the right to grant or withhold funding on
|
||||
a discretionary of special basis. Basically, this scheme is run at the project's discretion, and is
|
||||
not a public service. Making contributions to the project under this scheme does not obligate the
|
||||
project to pay you for such work, or otherwise compensate you in any way.
|
||||
|
||||
### Raising Issues Related to Governance
|
||||
|
||||
This governance model necessarily leaves many situations unspecified. If
|
||||
questions arise as to how a given situation should proceed according to the
|
||||
overall goals of the project, the best thing to do is to open a GitHub issue
|
||||
and ping the TC members.
|
||||
|
||||
### Emeritus Contributors
|
||||
|
||||
* [@aluedeke](https://github.com/aluedeke)
|
||||
* [@astro03](https://github.com/Astro03)
|
||||
* [@bayandin](https://github.com/bayandin)
|
||||
* [@boneskull](https://github.com/boneskull)
|
||||
* [@bootstraponline](https://github.com/bootstraponline)
|
||||
* [@dandoveralba](https://github.com/dandoveralba)
|
||||
* [@ddkjin](https://github.com/ddkjin)
|
||||
* [@drpy](https://github.com/drpy)
|
||||
* [@dylanLacey](https://github.com/DylanLacey)
|
||||
* [@filmaj](https://github.com/filmaj)
|
||||
* [@gempesaw](https://github.com/gempesaw)
|
||||
* [@hugs](https://github.com/hugs)
|
||||
* [@imurchie](https://github.com/imurchie)
|
||||
* [@jamieEdge](https://github.com/JamieEdge)
|
||||
* [@jdeff](https://github.com/jdeff)
|
||||
* [@jonahss](https://github.com/Jonahss)
|
||||
* [@maudineormsby](https://github.com/maudineormsby)
|
||||
* [@moizjv](https://github.com/moizjv)
|
||||
* [@peepa](https://github.com/peepa)
|
||||
* [@penguinho](https://github.com/penguinho)
|
||||
* [@sbonebrake](https://github.com/sbonebrake)
|
||||
* [@scottdixon](https://github.com/scottdixon)
|
||||
* [@sebv](https://github.com/sebv)
|
||||
* [@sravanmedarapu](https://github.com/sravanmedarapu)
|
||||
* [@stuartbrussell-intuit](https://github.com/stuartbrussell-intuit)
|
||||
* [@tikhomirovSergey](https://github.com/TikhomirovSergey)
|
||||
* [@titusfortner](https://github.com/titusfortner)
|
||||
* [@vgrigoruk](https://github.com/vgrigoruk)
|
||||
* [@vmaxim](https://github.com/vmaxim)
|
||||
@@ -1,16 +0,0 @@
|
||||
Google Summer of Code Ideas
|
||||
=====
|
||||
|
||||
Appium is excited to participate as a [Google Summer of Code](https://summerofcode.withgoogle.com/) sponsor project. Here's a fun list of ideas that a Summer of Code student participant can use as a foundation for thinking about what to contribute to Appium!
|
||||
|
||||
|Idea|Description|
|
||||
|----|-----------|
|
||||
|Apple tvOS Support|Our users have been asking for an Appium driver for Apple tvOS apps. Is this possible? We're not entirely sure! Why not be awesome and build it?|
|
||||
|Test App revamp|Appium uses a number of poorly-designed mobile applications in its own testing. We have to use a variety of apps because we need to test that Appium can automate all the various controls and platform features that mobile vendors provide. Really what we need is one cross-platform app (or one app per platform) that exhibit all and only the features we need to test. This would be a fun project to build some mobile apps used for testing and also demoing Appium at events and in training videos, etc...|
|
||||
|Tooling and Performance|We haven't instrumented Appium with very many performance metrics, and don't really know if we're missing some low-hanging fruit in terms of performance. Maybe you can help us do that?|
|
||||
|Babel|We have a lot of repos, and they're all on a really old version of Babel. They all need to stay in sync. How can we make this situation better, and upgrade to the latest and greatest?|
|
||||
|Unity3d Driver|Historically, Appium hasn't been great at automating games. We don't have any particular support for the frameworks used to create games. Unity is a popular one, for example. What if we could write an Appium driver that talked to an embedded process in a test version of a Unity game, that allowed us to find and interact with 3d elements? It would be amazing! You can also be amazing and lead Appium into this new world.|
|
||||
|Testing the Internet of Things|Appium's architecture makes it dead simple to integrate drivers for automating new technologies. What about _really_ new technologies like IoT devices? Help to redefine the semantics of the WebDriver protocol for use with IoT, and build a driver for an IoT device to prove it out.|
|
||||
|Driver for X|Have an idea of something else Appium could automate that it doesn't already? Build a driver for it! This would involve some awesome Node.js code, plus whatever else is needed to integrate with the platform you have in mind.|
|
||||
|
||||
Hopefully this gives you some ideas of fun projects that would be substantial and long-lasting contributions to the Appium community.
|
||||
@@ -1,192 +1,4 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright OpenJS Foundation and other contributors, https://openjsf.org/
|
||||
Copyright 2012-2013 Appium Committers
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,217 +1,52 @@
|
||||
[](https://vshymanskyy.github.io/StandWithUkraine/)
|
||||
appium-doctor
|
||||
===================
|
||||
|
||||
<p align="center">
|
||||
<a href="https://appium.io/">
|
||||
<img alt="Appium" src="https://raw.githubusercontent.com/appium/appium/master/packages/appium/docs/overrides/assets/images/appium-logo-horiz.png" width="500">
|
||||
</a>
|
||||
</p>
|
||||
<p align="center">
|
||||
Cross-platform test automation for native, hybrid, mobile web and desktop apps.
|
||||
</p>
|
||||
[](https://travis-ci.org/appium/appium-doctor)
|
||||
[](https://coveralls.io/r/appium/appium-doctor?branch=sebv-doctor-who)
|
||||
|
||||
<div align="center">
|
||||
### Install
|
||||
|
||||
[](https://npmjs.org/package/appium)
|
||||
[](https://npmjs.org/package/appium)
|
||||
[](https://app.fossa.io/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fappium%2Fappium?ref=badge_shield)
|
||||
[](https://github.com/vshymanskyy/StandWithUkraine/)
|
||||
|
||||
</div>
|
||||
|
||||
***
|
||||
|
||||
<p align="center"><b>
|
||||
<a href="https://appium.io">Documentation</a> |
|
||||
<a href="https://appium.io/docs/en/latest/intro/">Get Started</a> |
|
||||
<a href="https://appium.io/docs/en/latest/ecosystem/">Ecosystem</a> |
|
||||
<a href="https://github.com/appium/appium/blob/master/packages/appium/CHANGELOG.md">Changelog</a> |
|
||||
<a href="https://appium.io/docs/en/latest/contributing/">Contributing Guide</a> |
|
||||
<a href="https://discuss.appium.io">Discussion Forum</a>
|
||||
</b></p>
|
||||
|
||||
***
|
||||
|
||||
Appium is an open-source automation framework that provides
|
||||
[WebDriver](https://www.w3.org/TR/webdriver/)-based automation possibilities for a wide range of
|
||||
different mobile, desktop and IoT platforms. Appium is modular and extensible, and supports multiple
|
||||
programming languages, which means there is an entire ecosystem of related software:
|
||||
* [__Drivers__](#drivers) add support for automating specific platforms
|
||||
* [__Clients__](#clients) allow writing Appium tests in your programming language of choice
|
||||
* [__Plugins__](#plugins) allow to further extend Appium functionality
|
||||
|
||||
## Migrating From Appium 1 to Appium 2
|
||||
|
||||
As of January 1st, 2022, the Appium team no longer maintains or supports Appium 1. All officially
|
||||
supported platform drivers are only compatible with Appium 2.
|
||||
|
||||
__[Please read the Migration Guide](https://appium.io/docs/en/latest/guides/migrating-1-to-2/) if you
|
||||
are still using Appium 1.__
|
||||
|
||||
> [!WARNING]
|
||||
> If you use Appium Desktop or Appium Server GUI, you will not be able to upgrade to Appium 2, as
|
||||
both of these tools have been deprecated. Please use [Appium Inspector](https://github.com/appium/appium-inspector)
|
||||
in combination with a standalone Appium 2 server.
|
||||
|
||||
## Installation
|
||||
|
||||
Appium can be installed using `npm` (other package managers are not currently supported). Please
|
||||
check the [installation docs](http://appium.io/docs/en/latest/quickstart/install/) for the
|
||||
system requirements and further information.
|
||||
|
||||
If upgrading from Appium 1, make sure Appium 1 is fully uninstalled (`npm uninstall -g appium`).
|
||||
Unexpected errors might appear if this has not been done.
|
||||
|
||||
```bash
|
||||
npm i -g appium
|
||||
```
|
||||
npm install appium-doctor -g
|
||||
```
|
||||
|
||||
Note that this will only install the core Appium server, which cannot automate anything on its own.
|
||||
Please install [drivers](#drivers) for your target platforms in order to automate them.
|
||||
### Usage
|
||||
|
||||
## Drivers
|
||||
```
|
||||
➜ appium-doctor -h
|
||||
|
||||
Appium supports app automation across a variety of platforms, like iOS, Android, macOS, Windows,
|
||||
and more. Each platform is supported by one or more "drivers", which know how to automate that
|
||||
particular platform. You can find a full list of officially-supported and third-party drivers in
|
||||
[Appium Ecosystem's Drivers page](http://appium.io/docs/en/latest/ecosystem/drivers/).
|
||||
Usage: appium-doctor.js [options, defaults: --ios --android]
|
||||
|
||||
Driver management is done using [Appium's Extension command-line interface](http://appium.io/docs/en/latest/cli/extensions/):
|
||||
|
||||
```bash
|
||||
# Install an official driver from npm (see documentation for a list of such drivers)
|
||||
appium driver install <driver-name>
|
||||
# Install any driver from npm
|
||||
appium driver install --source=npm <driver-name>
|
||||
# See documentation for installation from other sources
|
||||
|
||||
# List already installed drivers
|
||||
appium driver list --installed
|
||||
# Update a driver (it must be already installed)
|
||||
# This will NOT update the major version, in order to prevent breaking changes
|
||||
appium driver update <driver-name>
|
||||
# Update a driver to the most recent version (may include breaking changes)
|
||||
appium driver update <driver-name> --unsafe
|
||||
# Uninstall a driver (it won't last forever, will it?)
|
||||
appium driver uninstall <driver-name>
|
||||
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]
|
||||
```
|
||||
|
||||
## Clients
|
||||
## Dev
|
||||
|
||||
Client libraries enable writing Appium tests in different programming languages. There are
|
||||
officially-supported clients for Java, Python, Ruby, and .NET C#, as well as third-party clients
|
||||
for other languages. You can find a full list of clients in
|
||||
[Appium Ecosystem's Clients page](http://appium.io/docs/en/latest/ecosystem/clients/).
|
||||
## Watch
|
||||
|
||||
## Plugins
|
||||
|
||||
Plugins allow you to extend server functionality without changing the server code. The main
|
||||
difference between drivers and plugins is that the latter must be explicitly enabled on
|
||||
Appium server startup (all installed drivers are enabled by default):
|
||||
|
||||
```bash
|
||||
appium --use-plugins=<plugin-name>
|
||||
```
|
||||
npm run watch
|
||||
```
|
||||
|
||||
You can find a full list of officially-supported and third-party plugins in
|
||||
[Appium Ecosystem's Plugins page](http://appium.io/docs/en/latest/ecosystem/plugins/).
|
||||
## Test
|
||||
|
||||
Similarly to drivers, plugin management is also done using
|
||||
[Appium's Extension command-line interface](http://appium.io/docs/en/latest/cli/extensions/):
|
||||
|
||||
```bash
|
||||
# Install an official plugin from npm (see documentation for a list of such plugins)
|
||||
appium plugin install <plugin-name>
|
||||
# Install any plugin from npm
|
||||
appium plugin install --source=npm <plugin-name>
|
||||
# See documentation for installation from other sources
|
||||
|
||||
# List already installed plugins
|
||||
appium plugin list --installed
|
||||
# Update a plugin (it must be already installed)
|
||||
# This will NOT update the major version, in order to prevent breaking changes
|
||||
appium plugin update <plugin-name>
|
||||
# Update a plugin to the most recent version (may include breaking changes)
|
||||
appium plugin update <plugin-name> --unsafe
|
||||
# Uninstall a plugin
|
||||
appium plugin uninstall <plugin-name>
|
||||
```
|
||||
npm test
|
||||
```
|
||||
|
||||
## Server Command Line Interface
|
||||
## Hack Cli
|
||||
|
||||
In order to start sending commands to the Appium server, it must be running on the URL and port
|
||||
where your client library expects it to listen. [Appium's command-line interface](http://appium.io/docs/en/latest/cli/args/)
|
||||
is used to launch and configure the server:
|
||||
Use the `--demo` option to simulate the fix process.
|
||||
|
||||
```bash
|
||||
# Start the server on the default host (0.0.0.0) and port (4723)
|
||||
appium server
|
||||
# You can also omit the 'server' subcommand
|
||||
appium
|
||||
# Start the server on the given host, port and use a custom base path prefix (the default prefix is '/')
|
||||
appium --address 127.0.0.1 --port 9000 --base-path /wd/hub
|
||||
```
|
||||
appium-doctor --demo
|
||||
```
|
||||
|
||||
Appium supports execution of parallel server processes, as well as parallel driver sessions within a
|
||||
single server process. Refer the corresponding driver documentations regarding which mode is optimal
|
||||
for the particular driver or whether it supports parallel sessions.
|
||||
|
||||
## Why Appium?
|
||||
|
||||
1. You usually don't have to recompile your app or modify it in any way, due to the use of standard
|
||||
automation APIs on all platforms.
|
||||
2. You can write tests with your favorite dev tools using any WebDriver-compatible language such as
|
||||
Java, Python, Ruby and C#. There are also third party client implementations for other languages.
|
||||
3. You can use any testing framework.
|
||||
4. Some drivers like `xcuitest` and `uiautomator2` have built-in mobile web and hybrid app support.
|
||||
Within the same script, you can switch seamlessly between native app automation and webview
|
||||
automation, all using the WebDriver model that's already the standard for web automation.
|
||||
5. You can run your automated tests locally and in a cloud. There are multiple cloud providers that
|
||||
support various Appium drivers (mostly targeting iOS and Android mobile automation).
|
||||
6. [Appium Inspector](https://github.com/appium/appium-inspector) can be used to visually inspect
|
||||
the page source of applications across different platforms, facilitating easier test development.
|
||||
|
||||
Investing in the [WebDriver](https://w3c.github.io/webdriver/webdriver-spec.html) protocol means you
|
||||
are betting on a single, free, and open protocol for testing that has become a web standard. Don't
|
||||
lock yourself into a proprietary stack.
|
||||
|
||||
For example, if you use Apple's XCUITest library without Appium, you can only write tests using
|
||||
Obj-C/Swift, and you can only run tests through Xcode. Similarly, with Google's UiAutomator or
|
||||
Espresso, you can only write tests in Java/Kotlin. Appium opens up the possibility of true
|
||||
cross-platform native app automation, for mobile and beyond!
|
||||
|
||||
If you are looking for a more comprehensive description of what this is all about, please read our
|
||||
documentation on [How Does Appium Work?](https://appium.io/docs/en/latest/intro/appium/).
|
||||
|
||||
## Sponsors
|
||||
|
||||
Appium has a [Sponsorship Program](GOVERNANCE.md#sponsorship)! If you or your company uses Appium
|
||||
and wants to give back financially to the project, we use these funds to [encourage development and
|
||||
contributions](GOVERNANCE.md#compensation-scheme), as well as support other open source projects we
|
||||
rely on. [Become a sponsor](https://opencollective.com/appium) via our OpenCollective page.
|
||||
|
||||
### Development and Strategic Partners
|
||||
|
||||
Appium is incredibly grateful to our Development and Strategic Partners for their sustained
|
||||
contribution of project development and leadership!
|
||||
|
||||
<p align="center">
|
||||
<a href="https://www.browserstack.com/browserstack-appium?utm_campaigncode=701OW00000AoUTQYA3&utm_medium=partnered&utm_source=appium">
|
||||
<picture>
|
||||
<source srcset="packages/appium/docs/overrides/assets/images/sponsor-logo-browserstack-dark.png" media="(prefers-color-scheme: dark)"/>
|
||||
<source srcset="packages/appium/docs/overrides/assets/images/sponsor-logo-browserstack-light.png" media="(prefers-color-scheme: light)"/>
|
||||
<img src="packages/appium/docs/overrides/assets/images/sponsor-logo-browserstack-dark.png" width="300" alt="Browserstack"/>
|
||||
</picture>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
### Other Sponsors
|
||||
|
||||
A full list of sponsors is available at our [Sponsors page](https://appium.io/docs/en/latest/sponsors/).
|
||||
|
||||
## License
|
||||
|
||||
[Apache-2.0](./LICENSE)
|
||||
|
||||
[](https://app.fossa.io/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fappium%2Fappium?ref=badge_large)
|
||||
|
||||
`@appium/logger` package is under [ISC](./packages/logger/LICENSE) License.
|
||||
|
||||
BIN
Binary file not shown.
-30
@@ -1,30 +0,0 @@
|
||||
# Roadmap
|
||||
|
||||
This is not a traditional roadmap. We don't have a typical product team with a typical product
|
||||
manager. The fact that the items below exist on a roadmap is no indication that we will ever have
|
||||
the opportunity to work on them. Instead this is more like a wishlist of outcomes that the project
|
||||
thinks would be valuable. If and when we get additional contributions of development work, this is
|
||||
a set of items that would be great for someone to work on! Ideally, interested devs would find
|
||||
something on this list and own it from start to finish! Let anyone on the project know if you're
|
||||
interested and we can help get you going.
|
||||
|
||||
### Core projects
|
||||
- **WebDriver BiDi support in the Appium server**: the WebDriver BiDi spec is underway, which details a different method for communicating with drivers. We should implement handling for these methods in our server so that Appium drivers can take advantage of the BiDi spec without additional work on their part.
|
||||
- **Redevelop automatically generated command docs**: due to changing TypeDoc support and the departure of one of our team, we were forced to remove our TypeDoc-based automatic docs generation tool. It would be nice to bring this back in a more maintainable form!
|
||||
|
||||
### Driver/platform-specific projects
|
||||
- **Better maintenance for our Android drivers**: we're looking for Android specialists to help maintain our Android drivers (UiAutomator2 and Espresso)
|
||||
- **Better maintenance for our iOS driver**: we're looking for iOS specialists to help maintain our XCUITest driver
|
||||
- **Better maintenance for [appium-ios-device](https://github.com/appium/appium-ios-device)**: this project is using outdated approaches and libraries. If it could be improved it would have a big impact on the performance of the XCUITest driver
|
||||
|
||||
### Client library projects
|
||||
- **Selenium compatibility client updates**: we'd like to rearchitect our flagship Appium clients to extend rather than wrap Selenium clients, reducing the overall surface area of the Appium clients and making them work more closely with existing Selenium client functionality.
|
||||
|
||||
### Appium Inspector projects
|
||||
- **Full support for web inspection**: currently the Appium Inspector has poor to no support for inspecing web pages (either mobile browsers or web content embedded in webviews). It's a common requirement to inspect web elements as part of writing Appium tests, and at the present time users are required to leave the Inspector and open up a browser's devtools.
|
||||
- **Record/Playback**: we have a basic record-to-code feature already. It would be nice to be able to save recorded steps and play them back with a little player/editor.
|
||||
- **Support for more platforms**: currently there is a fair bit of specialized handling for iOS and Android in the Appium inspector. Other platforms either work poorly or not at all with the Inspector. It would be great to establish a way for drivers to report all the information about how they work to the Inspector, so it can support any platform without needing special-cased code for it.
|
||||
- **Locator analysis and comparison**: it would be nice for users to get more information about the relative suitability of using different locators to find elements, as poor locator performance is a leading cause of Appium test instability.
|
||||
- **Dark theme**: for true hackers!
|
||||
- **Export/import of saved gestures**: it's currently not possible to port your saved gestures to other instances of the Inspector (i.e., to share with team members).
|
||||
- **Bulk export/import saved sessions**: would be nice to take your sessions and capabilities with you or to share with colleagues.
|
||||
@@ -1,3 +0,0 @@
|
||||
# Sponsors & Backers
|
||||
|
||||
Please check [Sponsors & Backers](https://appium.io/docs/en/latest/sponsors/)
|
||||
Executable
+3
@@ -0,0 +1,3 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
require('./build/bin/appium-doctor');
|
||||
Executable
+39
@@ -0,0 +1,39 @@
|
||||
// transpile:main
|
||||
|
||||
import yargs from 'yargs';
|
||||
//import _ from 'lodash';
|
||||
import newDoctor from '../lib/factory';
|
||||
import { configureBinaryLog } from '../lib/utils';
|
||||
import { configure as configurePrompt } from '../lib/prompt';
|
||||
|
||||
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 = true;
|
||||
argv.android = true;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
let opts = yargs.argv;
|
||||
|
||||
configurePrompt(opts);
|
||||
configureBinaryLog(opts);
|
||||
newDoctor(opts).run();
|
||||
@@ -1,6 +0,0 @@
|
||||
# Docs moved
|
||||
|
||||
The old Appium 1.x docs used to be in this directory. Now, they are available only on the [1.x
|
||||
branch](https://github.com/appium/appium/tree/1.x/docs).
|
||||
|
||||
Appium 2.x docs are located in [packages/appium/docs](../packages/appium/docs) in this repo.
|
||||
@@ -1,28 +0,0 @@
|
||||
This documentation is a note for the opencollective payout.
|
||||
|
||||
1. Select `Submit expense`
|
||||
|
||||
<img width="200" alt="Screenshot 2024-09-17 at 11 04 32 PM" src="https://github.com/user-attachments/assets/5d598a46-8aa3-41e0-aadc-ce3f34b10f6b">
|
||||
|
||||
2. Select `Invoice`
|
||||
3. Type proper account name in `Who is being paid for this expense?`
|
||||
- The form depends on the target user
|
||||
4. Select `Next`
|
||||
5. Input expense info and complete the form. Then, the user will receive the expense info.
|
||||
- Expense title: `<year-month> contribution payout`
|
||||
- e.g. `2024-07 contribution payout`
|
||||
- Description in "Set invoice details"
|
||||
- for TCs:
|
||||
- `<year-month> contribution as a TC https://github.com/appium/appium/blob/master/GOVERNANCE.md#the-technical-committee and maintaining Appium project itself (development, review etc)`
|
||||
- i.e. `2024-07 contribution as a TC https://github.com/appium/appium/blob/master/GOVERNANCE.md#the-technical-committee and maintaining Appium project itself (development, review etc)`
|
||||
- for individuals
|
||||
- `PR contribution: <PRs>` for PRs. `Community contribution in Q&A forum at https://discuss.appium.io/` for appium forums
|
||||
- E.g.
|
||||
- `PR contribution: https://github.com/appium/dotnet-client/pull/810, https://github.com/appium/dotnet-client/pull/808`
|
||||
- `PR contribution: https://github.com/appium/java-client/pull/2206, https://github.com/appium/java-client/pull/2203 and a few PRs`
|
||||
- `Community contribution in Q&A forum at https://discuss.appium.io/`
|
||||
- Amount
|
||||
- Type the amount in `USD`
|
||||
6. Approve each expense after getting response for each expense
|
||||
|
||||
<img width="500" alt="Screenshot 2024-09-11 at 8 40 35 PM" src="https://github.com/user-attachments/assets/462cda69-1ce7-458c-b319-dda87cf10d65">
|
||||
@@ -1,24 +0,0 @@
|
||||
import tsConfig from '@appium/eslint-config-appium-ts';
|
||||
|
||||
export default [
|
||||
...tsConfig,
|
||||
{
|
||||
...tsConfig.find(({name}) => name === 'Test Files'),
|
||||
name: 'Test Support',
|
||||
files: [
|
||||
'packages/test-support/lib/**',
|
||||
'packages/driver-test-support/lib/**',
|
||||
'packages/plugin-test-support/lib/**',
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Ignores',
|
||||
ignores: [
|
||||
'**/build-fixtures/**',
|
||||
'packages/appium/docs/**/assets/**',
|
||||
'packages/appium/docs/**/js/**',
|
||||
'packages/appium/sample-code/**',
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
"use strict";
|
||||
|
||||
var gulp = require('gulp'),
|
||||
boilerplate = require('appium-gulp-plugins').boilerplate.use(gulp),
|
||||
DEFAULTS = require('appium-gulp-plugins').boilerplate.DEFAULTS;
|
||||
|
||||
boilerplate({
|
||||
build: 'appium-doctor',
|
||||
files: DEFAULTS.files.concat('bin/**/*.js'),
|
||||
jscs: false,
|
||||
});
|
||||
@@ -0,0 +1,4 @@
|
||||
import newDoctor from './lib/factory';
|
||||
import { Doctor, DoctorCheck } from './lib/doctor';
|
||||
|
||||
export { newDoctor, Doctor, DoctorCheck };
|
||||
-23
@@ -1,23 +0,0 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/lerna",
|
||||
"changelogPreset": "conventional-changelog-conventionalcommits",
|
||||
"command": {
|
||||
"add": {
|
||||
"exact": true
|
||||
},
|
||||
"publish": {
|
||||
"message": "chore: publish"
|
||||
},
|
||||
"run": {
|
||||
"concurrency": 8
|
||||
},
|
||||
"version": {
|
||||
"conventionalCommits": true,
|
||||
"createRelease": "github",
|
||||
"allowBranch": "master"
|
||||
}
|
||||
},
|
||||
"ignoreChanges": ["**/test/**", "**/*.md"],
|
||||
"useNx": false,
|
||||
"version": "independent"
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
import { fs } from './utils';
|
||||
import { DoctorCheck } from './doctor';
|
||||
import { ok, nok } from './utils';
|
||||
import { system } from 'appium-support';
|
||||
import path from 'path';
|
||||
let checks = [];
|
||||
|
||||
// Check env variables
|
||||
|
||||
class EnvVarAndPathCheck extends DoctorCheck {
|
||||
constructor (varName) {
|
||||
super();
|
||||
this.varName = varName;
|
||||
}
|
||||
|
||||
async diagnose() {
|
||||
let varValue = process.env[this.varName];
|
||||
if (typeof varValue === "undefined") {
|
||||
return nok(`${this.varName} is NOT set!`);
|
||||
}
|
||||
return await fs.exists(varValue) ? ok(`${this.varName} is set to: ${varValue}`) :
|
||||
nok(`${this.varName} is set to \'${varValue}\' but this is NOT a valid path!`);
|
||||
}
|
||||
|
||||
fix() {
|
||||
return `Manually configure ${this.varName}.`;
|
||||
}
|
||||
}
|
||||
|
||||
checks.push(new EnvVarAndPathCheck('ANDROID_HOME'));
|
||||
checks.push(new EnvVarAndPathCheck('JAVA_HOME'));
|
||||
|
||||
// Check tools
|
||||
|
||||
class AndroidToolCheck extends DoctorCheck {
|
||||
constructor(toolName, toolPath) {
|
||||
super();
|
||||
this.toolName = toolName;
|
||||
this.toolPath = toolPath;
|
||||
}
|
||||
async diagnose () {
|
||||
if (typeof process.env.ANDROID_HOME === "undefined") {
|
||||
return nok(`${this.toolName} could not be found because ANDROID_HOME is NOT set!`);
|
||||
}
|
||||
let fullPath = path.resolve(process.env.ANDROID_HOME, this.toolPath);
|
||||
return await fs.exists(fullPath) ? ok(`${this.toolName} exists at: ${fullPath}`) :
|
||||
nok(`${this.toolName} could NOT be found at \'${fullPath}\'!`);
|
||||
}
|
||||
|
||||
fix() {
|
||||
if (typeof process.env.ANDROID_HOME === "undefined") {
|
||||
return 'Manually configure ANDROID_HOME and run appium-doctor again.';
|
||||
}
|
||||
return `Manually install ${this.toolName} and add it to PATH.`;
|
||||
}
|
||||
}
|
||||
checks.push(new AndroidToolCheck('adb',
|
||||
path.join("platform-tools", system.isWindows() ? 'adb.exe' : 'adb')));
|
||||
checks.push(new AndroidToolCheck('android',
|
||||
path.join("tools", system.isWindows() ? 'android.bat' : 'android')));
|
||||
checks.push(new AndroidToolCheck('emulator',
|
||||
path.join("tools", system.isWindows() ? 'emulator.exe' : 'emulator')));
|
||||
|
||||
export { EnvVarAndPathCheck, AndroidToolCheck };
|
||||
export default checks;
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
// demo rule to test the gui
|
||||
|
||||
import { fs, ok, nok } from './utils';
|
||||
import { exec } from 'teen_process';
|
||||
import { DoctorCheck } from './doctor';
|
||||
import log from './logger';
|
||||
import { fixIt } from './prompt';
|
||||
import { FixSkippedError } from './doctor';
|
||||
|
||||
let checks = [];
|
||||
|
||||
class DirCheck extends DoctorCheck {
|
||||
constructor (path) {
|
||||
super({autofix: false});
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
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!`);
|
||||
}
|
||||
|
||||
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'));
|
||||
|
||||
class FileCheck extends DoctorCheck {
|
||||
constructor(path) {
|
||||
super({autofix: true});
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
async diagnose() {
|
||||
return await fs.exists(this.path) ?
|
||||
ok(`Found file at: ${this.path}`) : nok(`Could NOT find file at \'${this.path}\'!`);
|
||||
}
|
||||
|
||||
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');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
checks.push(new FileCheck('/tmp/appium-doctor/demo/apple.fruit'));
|
||||
checks.push(new FileCheck('/tmp/appium-doctor/demo/pear.fruit'));
|
||||
checks.push(new FileCheck('/tmp/appium-doctor/demo/orange.fruit'));
|
||||
|
||||
export { DirCheck, FileCheck };
|
||||
export default checks;
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
import { fs } from './utils';
|
||||
import { exec } from 'teen_process';
|
||||
import { DoctorCheck } from './doctor';
|
||||
import { ok, nok } from './utils';
|
||||
import { system } from 'appium-support';
|
||||
import path from 'path';
|
||||
import { EOL } from 'os';
|
||||
|
||||
let checks = [];
|
||||
|
||||
// Check PATH binaries
|
||||
|
||||
class BinaryIsInPathCheck extends DoctorCheck {
|
||||
constructor (binary) {
|
||||
super();
|
||||
this.binary = binary;
|
||||
}
|
||||
|
||||
async diagnose () {
|
||||
let resolvedPath;
|
||||
try {
|
||||
let executable = system.isWindows() ? 'where' : 'which';
|
||||
let {stdout} = await exec(executable, [this.binary]);
|
||||
if (stdout.match(/not found/gi)) {
|
||||
throw new Error('Not Found');
|
||||
}
|
||||
resolvedPath = system.isWindows() ? stdout.split(EOL)[0] : stdout.replace(EOL, "");
|
||||
} catch (err) {
|
||||
return nok(`${this.binary} is MISSING in PATH!`);
|
||||
}
|
||||
return await fs.exists(resolvedPath) ? ok(`${this.binary} was found at ${resolvedPath}`) :
|
||||
nok(`${this.binary} was found in PATH at '${resolvedPath}', but this is NOT a valid path!`);
|
||||
}
|
||||
fix() {
|
||||
return `Manually install the ${this.binary} binary and add it to PATH.`;
|
||||
}
|
||||
}
|
||||
|
||||
checks.push(new BinaryIsInPathCheck(system.isWindows() ? "mvn.bat" : "mvn"));
|
||||
checks.push(new BinaryIsInPathCheck(system.isWindows() ? "ant.bat" : "ant"));
|
||||
checks.push(new BinaryIsInPathCheck(system.isWindows() ? "adb.exe" : "adb"));
|
||||
|
||||
// Check Android SDKs
|
||||
|
||||
class AndroidSdkExists extends DoctorCheck {
|
||||
constructor (sdk) {
|
||||
super();
|
||||
this.sdk = sdk;
|
||||
}
|
||||
|
||||
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}\'!`);
|
||||
}
|
||||
fix() {
|
||||
if (typeof process.env.ANDROID_HOME === "undefined") {
|
||||
return 'Manually configure ANDROID_HOME.';
|
||||
}
|
||||
return `Manually install the ${this.sdk} sdk.`;
|
||||
}
|
||||
}
|
||||
|
||||
checks.push(new AndroidSdkExists('android-16'));
|
||||
checks.push(new AndroidSdkExists('android-19'));
|
||||
|
||||
export { BinaryIsInPathCheck, AndroidSdkExists };
|
||||
export default checks;
|
||||
+149
@@ -0,0 +1,149 @@
|
||||
import 'colors';
|
||||
import _ from 'lodash';
|
||||
import log from './logger';
|
||||
|
||||
class FixSkippedError extends Error {
|
||||
}
|
||||
|
||||
class DoctorCheck {
|
||||
constructor(opts={}) {
|
||||
this.autofix = !!opts.autofix;
|
||||
}
|
||||
diagnose () { throw new Error('Not Implemented!'); }
|
||||
fix () {
|
||||
// return string for manual fixes.
|
||||
throw new Error('Not Implemented!');
|
||||
}
|
||||
}
|
||||
|
||||
class Doctor {
|
||||
constructor() {
|
||||
this.checks = [];
|
||||
this.toFix = [];
|
||||
}
|
||||
|
||||
register (checks) {
|
||||
checks = Array.isArray(checks) ? checks : [checks];
|
||||
this.checks = this.checks.concat(checks);
|
||||
}
|
||||
|
||||
async diagnose() {
|
||||
log.info('### Diagnostic starting ###');
|
||||
this.toFix = [];
|
||||
for (let check of this.checks) {
|
||||
let res = await check.diagnose();
|
||||
if (res.ok) {
|
||||
log.info(` ${'\u2714'.green} ${res.message}`);
|
||||
} else {
|
||||
let errorMessage = ` ${'\u2716'.red} ${res.message}`;
|
||||
log.warn(errorMessage);
|
||||
this.toFix.push({
|
||||
error: errorMessage,
|
||||
check: check
|
||||
});
|
||||
}
|
||||
}
|
||||
let fixMessage;
|
||||
switch (this.toFix.length) {
|
||||
case 0:
|
||||
fixMessage = "no fix needed";
|
||||
break;
|
||||
case 1:
|
||||
fixMessage = "one fix needed";
|
||||
break;
|
||||
default:
|
||||
fixMessage = `${this.toFix.length} fixes needed`;
|
||||
}
|
||||
log.info(`### Diagnostic completed, ${fixMessage}. ###`);
|
||||
log.info('');
|
||||
}
|
||||
|
||||
async reportSuccess () {
|
||||
if (this.toFix.length === 0) {
|
||||
log.info('Everything looks good, bye!');
|
||||
log.info('');
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async reportManualFixes () {
|
||||
let manualFixes = _.filter(this.toFix, (f) => {return !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:');
|
||||
// for manual fixes, the fix method always return a string
|
||||
let fixMessages = [];
|
||||
for (let f of manualFixes) {
|
||||
fixMessages.push(await f.check.fix());
|
||||
}
|
||||
for (let m of _.uniq(fixMessages)) {
|
||||
log.warn(`- ${m}`);
|
||||
}
|
||||
log.info('###');
|
||||
log.info('');
|
||||
log.info('Bye, run appium-doctor again when the all the manual fixes have been applied!');
|
||||
log.info('');
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
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:');
|
||||
let 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 () {
|
||||
let autoFixes = _.filter(this.toFix, (f) => {return f.check.autofix;});
|
||||
for (let f of autoFixes) {
|
||||
await this.runAutoFix(f);
|
||||
log.info('');
|
||||
}
|
||||
if(_.find(autoFixes, (f) => { return !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() {
|
||||
await this.diagnose();
|
||||
if (await this.reportSuccess()) {
|
||||
return;
|
||||
}
|
||||
if (await this.reportManualFixes()) {
|
||||
return;
|
||||
}
|
||||
await this.runAutoFixes();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { Doctor, DoctorCheck, FixSkippedError };
|
||||
@@ -0,0 +1,20 @@
|
||||
import _ from 'lodash';
|
||||
import { Doctor } from './doctor.js';
|
||||
import iosChecks from './ios';
|
||||
import androidChecks from './android';
|
||||
import devChecks from './dev';
|
||||
import demoChecks from './demo';
|
||||
|
||||
let checks = { iosChecks, androidChecks, devChecks, demoChecks };
|
||||
|
||||
let newDoctor = (opts) => {
|
||||
let doctor = new Doctor();
|
||||
for (let [k, v] of _.pairs(opts)) {
|
||||
if(v) {
|
||||
doctor.register(checks[`${k}Checks`] || []);
|
||||
}
|
||||
}
|
||||
return doctor;
|
||||
};
|
||||
|
||||
export default newDoctor;
|
||||
+152
@@ -0,0 +1,152 @@
|
||||
import { fs, macOsxVersion, ok, nok, authorize } from './utils';
|
||||
import { exec } from 'teen_process';
|
||||
import { DoctorCheck } from './doctor';
|
||||
import log from './logger';
|
||||
import { FixSkippedError } from './doctor';
|
||||
import NodeDetector from './node-detector';
|
||||
import { fixIt } from './prompt';
|
||||
|
||||
let checks = [];
|
||||
let fixes = {};
|
||||
|
||||
// Check for Xcode.
|
||||
class XcodeCheck extends DoctorCheck {
|
||||
async diagnose() {
|
||||
let xcodePath;
|
||||
try {
|
||||
let {stdout} = await exec('xcode-select', ['--print-path']);
|
||||
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}\'!`);
|
||||
}
|
||||
|
||||
async fix () {
|
||||
return 'Manually install Xcode.';
|
||||
}
|
||||
}
|
||||
checks.push(new XcodeCheck());
|
||||
|
||||
// Check for Xcode Command Line Tools.
|
||||
class XcodeCmdLineToolsCheck extends DoctorCheck {
|
||||
constructor() {
|
||||
super({autofix: true});
|
||||
}
|
||||
|
||||
async diagnose () {
|
||||
const errMess = 'Xcode Command Line Tools are NOT installed!';
|
||||
let pkgName = await macOsxVersion() === '10.8' ? "com.apple.pkg.DeveloperToolsCLI" : "com.apple.pkg.CLTools_Executables";
|
||||
let stdout;
|
||||
try {
|
||||
stdout = (await exec('pkgutil', [`--pkg-info=${pkgName}`])).stdout;
|
||||
} catch (err) {
|
||||
log.debug(err);
|
||||
return nok(errMess);
|
||||
}
|
||||
return stdout.match(/install-time/) ? ok('Xcode Command Line Tools are installed.') :
|
||||
nok(errMess);
|
||||
}
|
||||
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 manually.');
|
||||
throw new FixSkippedError();
|
||||
}
|
||||
}
|
||||
}
|
||||
checks.push(new XcodeCmdLineToolsCheck());
|
||||
|
||||
fixes.authorizeIosFix = async function () {
|
||||
log.info(`The authorize iOS script need to be run.`);
|
||||
let yesno = await fixIt();
|
||||
if (yesno === 'yes') {
|
||||
await authorize();
|
||||
} else {
|
||||
log.info('Skipping you will need to run the authorize iOS manually.');
|
||||
throw new FixSkippedError();
|
||||
}
|
||||
};
|
||||
|
||||
// Dev Tools Security
|
||||
class DevToolsSecurityCheck extends DoctorCheck {
|
||||
constructor() {
|
||||
super({autofix: true});
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
async fix () {
|
||||
return await fixes.authorizeIosFix();
|
||||
}
|
||||
}
|
||||
checks.push(new DevToolsSecurityCheck());
|
||||
|
||||
// Authorization DB
|
||||
class AuthorizationDbCheck extends DoctorCheck {
|
||||
constructor() {
|
||||
super({autofix: true});
|
||||
}
|
||||
|
||||
async diagnose () {
|
||||
const successMess = 'The Authorization DB is set up properly.';
|
||||
const errMess = 'The Authorization DB is NOT set up properly.';
|
||||
let stdout;
|
||||
try {
|
||||
stdout = (await exec('security', ['authorizationdb', 'read', 'system.privilege.taskport'])).stdout;
|
||||
} catch (err) {
|
||||
if (await macOsxVersion() === '10.8') {
|
||||
let data;
|
||||
try {
|
||||
data = await fs.readFile('/etc/authorization', 'utf8');
|
||||
} catch (err) {
|
||||
log.debug(err);
|
||||
return nok(errMess);
|
||||
}
|
||||
let rg =/<key>system.privilege.taskport<\/key>\s*\n\s*<dict>\n\s*<key>allow-root<\/key>\n\s*(<true\/>)/;
|
||||
return data && data.match(rg) ? ok(successMess) : nok(errMess);
|
||||
} else {
|
||||
log.debug(err);
|
||||
return nok(errMess);
|
||||
}
|
||||
}
|
||||
return stdout && (stdout.match(/is-developer/) || stdout.match(/allow/)) ?
|
||||
ok(successMess) : nok(errMess);
|
||||
}
|
||||
async fix () {
|
||||
return await fixes.authorizeIosFix();
|
||||
}
|
||||
}
|
||||
checks.push(new AuthorizationDbCheck());
|
||||
|
||||
// Node Binary
|
||||
class NodeBinaryCheck extends DoctorCheck {
|
||||
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!');
|
||||
}
|
||||
|
||||
fix() {
|
||||
return `Manually setup Node.js.`;
|
||||
}
|
||||
}
|
||||
checks.push(new NodeBinaryCheck());
|
||||
|
||||
export { fixes, XcodeCheck, XcodeCmdLineToolsCheck, DevToolsSecurityCheck,
|
||||
AuthorizationDbCheck, NodeBinaryCheck };
|
||||
export default checks;
|
||||
@@ -0,0 +1,5 @@
|
||||
import { getLogger } from 'appium-logger';
|
||||
|
||||
const logger = getLogger('AppiumDoctor');
|
||||
|
||||
export default logger;
|
||||
@@ -0,0 +1,97 @@
|
||||
import { fs } from './utils';
|
||||
import { exec } from 'teen_process';
|
||||
import log from './logger';
|
||||
import path from 'path';
|
||||
|
||||
const NODE_COMMON_PATHS = [
|
||||
process.env.NODE_BIN,
|
||||
'/usr/local/bin/node',
|
||||
'/opt/local/bin/node',
|
||||
];
|
||||
|
||||
class NodeDetector {
|
||||
static async retrieveInCommonPlaces () {
|
||||
for (let p of NODE_COMMON_PATHS) {
|
||||
if (p && await fs.exists(p)) {
|
||||
log.debug(`Node binary found at common place: ${p}`);
|
||||
return p;
|
||||
}
|
||||
}
|
||||
log.debug('Node binary wasn\'t found at common places.');
|
||||
return null;
|
||||
}
|
||||
|
||||
static async retrieveUsingWhichCommand () {
|
||||
let stdout;
|
||||
try {
|
||||
stdout = (await exec('which', ['node'])).stdout;
|
||||
} catch (ign) {}
|
||||
let nodePath = stdout.replace("\n", "");
|
||||
if (await fs.exists(nodePath)) {
|
||||
log.debug("Node binary found using which command at: " + nodePath);
|
||||
return nodePath;
|
||||
} else {
|
||||
log.debug('Node binary not found using the which command.');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static async retrieveUsingAppleScript () {
|
||||
var appScript = [
|
||||
'try'
|
||||
, ' set appiumIsRunning to false'
|
||||
, ' tell application "System Events"'
|
||||
, ' set appiumIsRunning to name of every process contains "Appium"'
|
||||
, ' end tell'
|
||||
, ' if appiumIsRunning then'
|
||||
, ' tell application "Appium" to return node path'
|
||||
, ' end if'
|
||||
, 'end try'
|
||||
, 'return "NULL"'
|
||||
].join("\n");
|
||||
let stdout;
|
||||
try {
|
||||
stdout = (await exec('osascript', ['-e', appScript ])).stdout;
|
||||
} catch(ign) {}
|
||||
let nodePath = stdout.replace("\n", "");
|
||||
if (await fs.exists(nodePath)) {
|
||||
log.debug("Node binary found using AppleScript at: " + nodePath);
|
||||
return nodePath;
|
||||
} else {
|
||||
log.debug('Node binary not found using AppleScript.');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static async retrieveUsingAppiumConfigFile () {
|
||||
let jsonobj;
|
||||
try {
|
||||
var appiumConfigPath = path.resolve(__dirname, "../..", ".appiumconfig.json");
|
||||
if (await fs.exists(appiumConfigPath)) {
|
||||
jsonobj = JSON.parse(await fs.readFile(appiumConfigPath, 'utf8'));
|
||||
}
|
||||
} catch (ign) {}
|
||||
if (jsonobj && jsonobj.node_bin && await fs.exists(jsonobj.node_bin) ) {
|
||||
log.debug("Node binary found using .appiumconfig.json at: " + jsonobj.node_bin);
|
||||
return jsonobj.node_bin;
|
||||
} else {
|
||||
log.debug('Node binary not found in the .appiumconfig.json file.');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static async detect () {
|
||||
let nodePath = await this.retrieveInCommonPlaces() ||
|
||||
await NodeDetector.retrieveUsingWhichCommand() ||
|
||||
await NodeDetector.retrieveUsingAppleScript() ||
|
||||
await NodeDetector.retrieveUsingAppiumConfigFile();
|
||||
if (nodePath) {
|
||||
return nodePath;
|
||||
} else {
|
||||
log.warn('The node binary could not be found.');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default NodeDetector;
|
||||
@@ -0,0 +1,37 @@
|
||||
import { inquirer } from './utils';
|
||||
|
||||
let persistentResponse;
|
||||
|
||||
const fixItQuestion = {
|
||||
type: "list",
|
||||
name: "confirmation",
|
||||
message: "Fix it:",
|
||||
choices: [ "yes", "no", "always", "never"],
|
||||
filter: function ( val ) { return val.toLowerCase(); }
|
||||
};
|
||||
|
||||
function configure (opts) {
|
||||
if(opts.yes) {
|
||||
persistentResponse = 'yes';
|
||||
}
|
||||
if(opts.no) {
|
||||
persistentResponse = 'no';
|
||||
}
|
||||
}
|
||||
|
||||
function clear () {
|
||||
persistentResponse = undefined;
|
||||
}
|
||||
|
||||
async function fixIt () {
|
||||
if (persistentResponse) {
|
||||
return persistentResponse;
|
||||
}
|
||||
let resp = await inquirer.prompt(fixItQuestion);
|
||||
persistentResponse = resp.confirmation === 'always' ? 'yes' : persistentResponse;
|
||||
persistentResponse = resp.confirmation === 'never' ? 'no' : persistentResponse;
|
||||
return persistentResponse || resp.confirmation;
|
||||
}
|
||||
|
||||
export { configure, fixIt, clear };
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
import B from 'bluebird';
|
||||
import _fs from 'fs';
|
||||
import path from 'path';
|
||||
import _inquirer from 'inquirer';
|
||||
import { exec } from 'teen_process';
|
||||
import log from '../lib/logger';
|
||||
import authorize from 'authorize-ios';
|
||||
|
||||
let pkgRoot = process.env.NO_PRECOMPILE ?
|
||||
path.resolve(__dirname, '..') : path.resolve(__dirname, '..', '..');
|
||||
|
||||
let fs = {
|
||||
readFile: B.promisify(_fs.readFile),
|
||||
exists: B.promisify((f, cb) => {
|
||||
_fs.exists(f, function (res) { cb(null, res);});
|
||||
}),
|
||||
lstat: B.promisify(_fs.lstat)
|
||||
};
|
||||
|
||||
async function macOsxVersion () {
|
||||
// TODO: move this to appium-support
|
||||
let stdout;
|
||||
try {
|
||||
stdout = (await exec('sw_vers', ['-productVersion'])).stdout;
|
||||
} catch (err) {
|
||||
throw new Error("Unknown SW Version Command: " + err);
|
||||
}
|
||||
for (let v of ['10.8', '10.9', '10.10', '10.11']) {
|
||||
if (stdout.indexOf(v) === 0) { return v; }
|
||||
}
|
||||
throw new Error("Could not detect Mac OS X Version");
|
||||
}
|
||||
|
||||
let ok = (message) => { return {ok: true, message: message }; };
|
||||
let nok = (message) => { return {ok: false, message: message }; };
|
||||
|
||||
let inquirer = {
|
||||
prompt: B.promisify(function(question, cb) {
|
||||
_inquirer.prompt(question, function(resp) { cb(null, resp); });
|
||||
})
|
||||
};
|
||||
|
||||
function configureBinaryLog(opts) {
|
||||
log.unwrap().log = function (level, prefix, message) {
|
||||
let l = this.levels[level];
|
||||
if (l < this.levels[this.level]) return;
|
||||
console.log(message);
|
||||
};
|
||||
log.level = opts.debug ? 'debug' : 'info';
|
||||
}
|
||||
|
||||
export { pkgRoot, fs, macOsxVersion, ok, nok, inquirer, configureBinaryLog, authorize};
|
||||
Generated
-33126
File diff suppressed because it is too large
Load Diff
+43
-164
@@ -1,176 +1,55 @@
|
||||
{
|
||||
"name": "appium-monorepo",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"description": "Automation for Apps.",
|
||||
"name": "appium-doctor",
|
||||
"description": "Description goes here.",
|
||||
"keywords": [
|
||||
"automation",
|
||||
"javascript",
|
||||
"selenium",
|
||||
"webdriver",
|
||||
"ios",
|
||||
"android",
|
||||
"firefoxos",
|
||||
"testing"
|
||||
"appium"
|
||||
],
|
||||
"homepage": "https://appium.io",
|
||||
"bugs": {
|
||||
"url": "https://github.com/appium/appium/issues"
|
||||
},
|
||||
"version": "0.0.6",
|
||||
"author": "appium",
|
||||
"license": "Apache-2.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/appium/appium.git"
|
||||
"url": "https://github.com/appium/appium-doctor.git"
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"author": "https://github.com/appium",
|
||||
"workspaces": [
|
||||
"packages/*"
|
||||
"bugs": {
|
||||
"url": "https://github.com/appium/appium-doctor/issues"
|
||||
},
|
||||
"engines": [
|
||||
"node"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "run-s build:*",
|
||||
"build:compile": "tsc -b",
|
||||
"build:workspaces": "lerna run build",
|
||||
"ci": "npm ci",
|
||||
"clean": "run-s clean:distfiles clean:workspaces",
|
||||
"clean:dangerously": "run-s clean:workspaces clean:force",
|
||||
"clean:deps": "lerna clean --yes",
|
||||
"clean:distfiles": "npx rimraf --glob \"./packages/*/build\" \"./packages/*/**/.tsconfig.tsbuildinfo\"; npx nx reset",
|
||||
"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 ./scripts/doctor-stub.mjs",
|
||||
"install-docs-deps": "npm run -w packages/appium install-docs-deps",
|
||||
"lint": "eslint .",
|
||||
"lint:ci": "eslint --quiet .",
|
||||
"lint:fix": "run-s \"lint -- --fix\"",
|
||||
"prepare": "run-p rebuild",
|
||||
"prepublishOnly": "run-s rebuild",
|
||||
"preversion": "run-s rebuild",
|
||||
"version": "run-s sync-pkgs stage-synced-pkgs",
|
||||
"rebuild": "run-s clean build",
|
||||
"reinstall": "run-s clean clean:deps ci",
|
||||
"reinstall:angry": "npm run clean:dangerously && npm ci",
|
||||
"release": "lerna publish",
|
||||
"stage-synced-pkgs": "git add -A packages/appium/README.md \"packages/*/package.json\"",
|
||||
"start": "appium",
|
||||
"sync-pkgs": "run-p sync-pkgs:*",
|
||||
"sync-pkgs:appium-readme": "sync-monorepo-packages --force --no-package-json --packages=packages/appium README.md",
|
||||
"sync-pkgs:common-fields:default": "sync-monorepo-packages --packages=\"!packages/logger\" --fields=author,license,bugs,homepage,engines",
|
||||
"sync-pkgs:common-fields:logger": "sync-monorepo-packages --packages=\"packages/logger\" --fields=author,bugs,homepage,engines",
|
||||
"sync-pkgs:keywords": "sync-monorepo-packages --field=keywords --packages=\"packages/*\" --packages=\"!packages/types\" --packages=\"!packages/eslint-config-appium-ts\" ",
|
||||
"sync-pkgs:license": "sync-monorepo-packages --force --no-package-json --packages=\"!packages/logger\" LICENSE",
|
||||
"test": "run-s test:quick",
|
||||
"test:ci": "run-s test:smoke test:unit test:types test:e2e",
|
||||
"test:e2e": "lerna run test:e2e",
|
||||
"test:quick": "run-s lint test:unit test:types",
|
||||
"test:slow": "run-s test:quick test:smoke test:e2e",
|
||||
"test:smoke": "smoker --all test:smoke",
|
||||
"test:types": "lerna run test:types",
|
||||
"test:unit": "lerna run test",
|
||||
"upload": "gulp github-upload",
|
||||
"zip": "zip -qr ./appium.zip ./packages/appium",
|
||||
"zip-and-upload": "run-s zip upload"
|
||||
"main": "./build/index.js",
|
||||
"bin": {
|
||||
"appium-doctor": "./appium-doctor.js"
|
||||
},
|
||||
"prettier": {
|
||||
"bracketSpacing": false,
|
||||
"printWidth": 100,
|
||||
"singleQuote": true
|
||||
"directories": {
|
||||
"lib": "lib"
|
||||
},
|
||||
"dependencies": {
|
||||
"appium-logger": "^1.1.5",
|
||||
"appium-support": "^1.1.1",
|
||||
"authorize-ios": "^1.0.0",
|
||||
"babel-runtime": "=5.6.16",
|
||||
"bluebird": "^2.9.30",
|
||||
"colors": "^1.1.2",
|
||||
"inquirer": "^0.9.0",
|
||||
"lodash": "^3.9.3",
|
||||
"source-map-support": "^0.3.1",
|
||||
"teen_process": "^1.2.2",
|
||||
"yargs": "^3.14.0"
|
||||
},
|
||||
"scripts": {
|
||||
"prepublish": "./node_modules/.bin/gulp prepublish",
|
||||
"test": "./node_modules/.bin/gulp once",
|
||||
"watch": "./node_modules/.bin/gulp"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@colors/colors": "1.6.0",
|
||||
"@eslint/js": "9.28.0",
|
||||
"@tsconfig/node14": "14.1.3",
|
||||
"@types/argparse": "2.0.17",
|
||||
"@types/archiver": "6.0.3",
|
||||
"@types/async-lock": "1.4.2",
|
||||
"@types/base64-stream": "1.0.5",
|
||||
"@types/bluebird": "3.5.42",
|
||||
"@types/chai": "5.2.1",
|
||||
"@types/chai-as-promised": "8.0.2",
|
||||
"@types/express": "5.0.1",
|
||||
"@types/jsftp": "2.1.5",
|
||||
"@types/json-schema": "7.0.15",
|
||||
"@types/klaw": "3.0.6",
|
||||
"@types/lockfile": "1.0.4",
|
||||
"@types/lodash": "4.17.16",
|
||||
"@types/method-override": "3.0.0",
|
||||
"@types/mocha": "10.0.10",
|
||||
"@types/mv": "2.1.4",
|
||||
"@types/ncp": "2.0.8",
|
||||
"@types/node": "22.15.2",
|
||||
"@types/pluralize": "0.0.33",
|
||||
"@types/semver": "7.7.0",
|
||||
"@types/serve-favicon": "2.5.7",
|
||||
"@types/shell-quote": "1.7.5",
|
||||
"@types/sinon": "17.0.4",
|
||||
"@types/sinon-chai": "4.0.0",
|
||||
"@types/source-map-support": "0.5.10",
|
||||
"@types/supports-color": "8.1.3",
|
||||
"@types/teen_process": "2.0.4",
|
||||
"@types/uuid": "10.0.0",
|
||||
"@types/which": "3.0.4",
|
||||
"@types/wrap-ansi": "3.0.0",
|
||||
"@types/ws": "8.18.1",
|
||||
"@types/xmldom": "0.1.34",
|
||||
"@types/yargs": "17.0.33",
|
||||
"asyncbox": "3.0.0",
|
||||
"chai": "5.2.0",
|
||||
"chai-as-promised": "8.0.1",
|
||||
"conventional-changelog-conventionalcommits": "7.0.2",
|
||||
"cpy-cli": "5.0.0",
|
||||
"cross-env": "7.0.3",
|
||||
"eslint": "9.28.0",
|
||||
"eslint-config-prettier": "10.1.5",
|
||||
"eslint-import-resolver-typescript": "4.4.2",
|
||||
"eslint-plugin-import": "2.31.0",
|
||||
"eslint-plugin-mocha": "10.5.0",
|
||||
"eslint-plugin-promise": "7.2.1",
|
||||
"finalhandler": "2.1.0",
|
||||
"get-port": "5.1.1",
|
||||
"json-schema-to-typescript": "15.0.4",
|
||||
"lerna": "8.2.2",
|
||||
"log-symbols": "4.1.0",
|
||||
"midnight-smoker": "8.0.0",
|
||||
"mjpeg-consumer": "2.0.0",
|
||||
"mjpeg-server": "0.3.1",
|
||||
"mocha": "11.5.0",
|
||||
"npm-run-all2": "6.2.6",
|
||||
"prettier": "3.5.3",
|
||||
"rewiremock": "3.14.6",
|
||||
"rimraf": "5.0.10",
|
||||
"serve-static": "1.16.2",
|
||||
"sinon": "20.0.0",
|
||||
"sync-monorepo-packages": "1.0.2",
|
||||
"ts-node": "10.9.2",
|
||||
"tsd": "0.32.0",
|
||||
"typescript": "5.8.3",
|
||||
"typescript-eslint": "8.33.0",
|
||||
"validate.js": "0.13.1",
|
||||
"webdriverio": "9.15.0",
|
||||
"ws": "8.18.2",
|
||||
"yaml-js": "0.3.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.17.0 || ^16.13.0 || >=18.0.0",
|
||||
"npm": ">=8"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"overrides": {
|
||||
"@sidvind/better-ajv-errors": {
|
||||
"ajv": "8.17.1"
|
||||
},
|
||||
"ajv-formats": {
|
||||
"ajv": "8.17.1"
|
||||
},
|
||||
"http-cache-semantics": "4.2.0"
|
||||
"appium-gulp-plugins": "^1.2.12",
|
||||
"appium-test-support": "^0.0.5",
|
||||
"chai": "^3.0.0",
|
||||
"chai-as-promised": "^5.1.0",
|
||||
"gulp": "^3.8.11",
|
||||
"mochawait": "^2.0.0",
|
||||
"sinon": "^1.15.4",
|
||||
"uncolor": "^0.3.0"
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,217 +0,0 @@
|
||||
[](https://vshymanskyy.github.io/StandWithUkraine/)
|
||||
|
||||
<p align="center">
|
||||
<a href="https://appium.io/">
|
||||
<img alt="Appium" src="https://raw.githubusercontent.com/appium/appium/master/packages/appium/docs/overrides/assets/images/appium-logo-horiz.png" width="500">
|
||||
</a>
|
||||
</p>
|
||||
<p align="center">
|
||||
Cross-platform test automation for native, hybrid, mobile web and desktop apps.
|
||||
</p>
|
||||
|
||||
<div align="center">
|
||||
|
||||
[](https://npmjs.org/package/appium)
|
||||
[](https://npmjs.org/package/appium)
|
||||
[](https://app.fossa.io/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fappium%2Fappium?ref=badge_shield)
|
||||
[](https://github.com/vshymanskyy/StandWithUkraine/)
|
||||
|
||||
</div>
|
||||
|
||||
***
|
||||
|
||||
<p align="center"><b>
|
||||
<a href="https://appium.io">Documentation</a> |
|
||||
<a href="https://appium.io/docs/en/latest/intro/">Get Started</a> |
|
||||
<a href="https://appium.io/docs/en/latest/ecosystem/">Ecosystem</a> |
|
||||
<a href="https://github.com/appium/appium/blob/master/packages/appium/CHANGELOG.md">Changelog</a> |
|
||||
<a href="https://appium.io/docs/en/latest/contributing/">Contributing Guide</a> |
|
||||
<a href="https://discuss.appium.io">Discussion Forum</a>
|
||||
</b></p>
|
||||
|
||||
***
|
||||
|
||||
Appium is an open-source automation framework that provides
|
||||
[WebDriver](https://www.w3.org/TR/webdriver/)-based automation possibilities for a wide range of
|
||||
different mobile, desktop and IoT platforms. Appium is modular and extensible, and supports multiple
|
||||
programming languages, which means there is an entire ecosystem of related software:
|
||||
* [__Drivers__](#drivers) add support for automating specific platforms
|
||||
* [__Clients__](#clients) allow writing Appium tests in your programming language of choice
|
||||
* [__Plugins__](#plugins) allow to further extend Appium functionality
|
||||
|
||||
## Migrating From Appium 1 to Appium 2
|
||||
|
||||
As of January 1st, 2022, the Appium team no longer maintains or supports Appium 1. All officially
|
||||
supported platform drivers are only compatible with Appium 2.
|
||||
|
||||
__[Please read the Migration Guide](https://appium.io/docs/en/latest/guides/migrating-1-to-2/) if you
|
||||
are still using Appium 1.__
|
||||
|
||||
> [!WARNING]
|
||||
> If you use Appium Desktop or Appium Server GUI, you will not be able to upgrade to Appium 2, as
|
||||
both of these tools have been deprecated. Please use [Appium Inspector](https://github.com/appium/appium-inspector)
|
||||
in combination with a standalone Appium 2 server.
|
||||
|
||||
## Installation
|
||||
|
||||
Appium can be installed using `npm` (other package managers are not currently supported). Please
|
||||
check the [installation docs](http://appium.io/docs/en/latest/quickstart/install/) for the
|
||||
system requirements and further information.
|
||||
|
||||
If upgrading from Appium 1, make sure Appium 1 is fully uninstalled (`npm uninstall -g appium`).
|
||||
Unexpected errors might appear if this has not been done.
|
||||
|
||||
```bash
|
||||
npm i -g appium
|
||||
```
|
||||
|
||||
Note that this will only install the core Appium server, which cannot automate anything on its own.
|
||||
Please install [drivers](#drivers) for your target platforms in order to automate them.
|
||||
|
||||
## Drivers
|
||||
|
||||
Appium supports app automation across a variety of platforms, like iOS, Android, macOS, Windows,
|
||||
and more. Each platform is supported by one or more "drivers", which know how to automate that
|
||||
particular platform. You can find a full list of officially-supported and third-party drivers in
|
||||
[Appium Ecosystem's Drivers page](http://appium.io/docs/en/latest/ecosystem/drivers/).
|
||||
|
||||
Driver management is done using [Appium's Extension command-line interface](http://appium.io/docs/en/latest/cli/extensions/):
|
||||
|
||||
```bash
|
||||
# Install an official driver from npm (see documentation for a list of such drivers)
|
||||
appium driver install <driver-name>
|
||||
# Install any driver from npm
|
||||
appium driver install --source=npm <driver-name>
|
||||
# See documentation for installation from other sources
|
||||
|
||||
# List already installed drivers
|
||||
appium driver list --installed
|
||||
# Update a driver (it must be already installed)
|
||||
# This will NOT update the major version, in order to prevent breaking changes
|
||||
appium driver update <driver-name>
|
||||
# Update a driver to the most recent version (may include breaking changes)
|
||||
appium driver update <driver-name> --unsafe
|
||||
# Uninstall a driver (it won't last forever, will it?)
|
||||
appium driver uninstall <driver-name>
|
||||
```
|
||||
|
||||
## Clients
|
||||
|
||||
Client libraries enable writing Appium tests in different programming languages. There are
|
||||
officially-supported clients for Java, Python, Ruby, and .NET C#, as well as third-party clients
|
||||
for other languages. You can find a full list of clients in
|
||||
[Appium Ecosystem's Clients page](http://appium.io/docs/en/latest/ecosystem/clients/).
|
||||
|
||||
## Plugins
|
||||
|
||||
Plugins allow you to extend server functionality without changing the server code. The main
|
||||
difference between drivers and plugins is that the latter must be explicitly enabled on
|
||||
Appium server startup (all installed drivers are enabled by default):
|
||||
|
||||
```bash
|
||||
appium --use-plugins=<plugin-name>
|
||||
```
|
||||
|
||||
You can find a full list of officially-supported and third-party plugins in
|
||||
[Appium Ecosystem's Plugins page](http://appium.io/docs/en/latest/ecosystem/plugins/).
|
||||
|
||||
Similarly to drivers, plugin management is also done using
|
||||
[Appium's Extension command-line interface](http://appium.io/docs/en/latest/cli/extensions/):
|
||||
|
||||
```bash
|
||||
# Install an official plugin from npm (see documentation for a list of such plugins)
|
||||
appium plugin install <plugin-name>
|
||||
# Install any plugin from npm
|
||||
appium plugin install --source=npm <plugin-name>
|
||||
# See documentation for installation from other sources
|
||||
|
||||
# List already installed plugins
|
||||
appium plugin list --installed
|
||||
# Update a plugin (it must be already installed)
|
||||
# This will NOT update the major version, in order to prevent breaking changes
|
||||
appium plugin update <plugin-name>
|
||||
# Update a plugin to the most recent version (may include breaking changes)
|
||||
appium plugin update <plugin-name> --unsafe
|
||||
# Uninstall a plugin
|
||||
appium plugin uninstall <plugin-name>
|
||||
```
|
||||
|
||||
## Server Command Line Interface
|
||||
|
||||
In order to start sending commands to the Appium server, it must be running on the URL and port
|
||||
where your client library expects it to listen. [Appium's command-line interface](http://appium.io/docs/en/latest/cli/args/)
|
||||
is used to launch and configure the server:
|
||||
|
||||
```bash
|
||||
# Start the server on the default host (0.0.0.0) and port (4723)
|
||||
appium server
|
||||
# You can also omit the 'server' subcommand
|
||||
appium
|
||||
# Start the server on the given host, port and use a custom base path prefix (the default prefix is '/')
|
||||
appium --address 127.0.0.1 --port 9000 --base-path /wd/hub
|
||||
```
|
||||
|
||||
Appium supports execution of parallel server processes, as well as parallel driver sessions within a
|
||||
single server process. Refer the corresponding driver documentations regarding which mode is optimal
|
||||
for the particular driver or whether it supports parallel sessions.
|
||||
|
||||
## Why Appium?
|
||||
|
||||
1. You usually don't have to recompile your app or modify it in any way, due to the use of standard
|
||||
automation APIs on all platforms.
|
||||
2. You can write tests with your favorite dev tools using any WebDriver-compatible language such as
|
||||
Java, Python, Ruby and C#. There are also third party client implementations for other languages.
|
||||
3. You can use any testing framework.
|
||||
4. Some drivers like `xcuitest` and `uiautomator2` have built-in mobile web and hybrid app support.
|
||||
Within the same script, you can switch seamlessly between native app automation and webview
|
||||
automation, all using the WebDriver model that's already the standard for web automation.
|
||||
5. You can run your automated tests locally and in a cloud. There are multiple cloud providers that
|
||||
support various Appium drivers (mostly targeting iOS and Android mobile automation).
|
||||
6. [Appium Inspector](https://github.com/appium/appium-inspector) can be used to visually inspect
|
||||
the page source of applications across different platforms, facilitating easier test development.
|
||||
|
||||
Investing in the [WebDriver](https://w3c.github.io/webdriver/webdriver-spec.html) protocol means you
|
||||
are betting on a single, free, and open protocol for testing that has become a web standard. Don't
|
||||
lock yourself into a proprietary stack.
|
||||
|
||||
For example, if you use Apple's XCUITest library without Appium, you can only write tests using
|
||||
Obj-C/Swift, and you can only run tests through Xcode. Similarly, with Google's UiAutomator or
|
||||
Espresso, you can only write tests in Java/Kotlin. Appium opens up the possibility of true
|
||||
cross-platform native app automation, for mobile and beyond!
|
||||
|
||||
If you are looking for a more comprehensive description of what this is all about, please read our
|
||||
documentation on [How Does Appium Work?](https://appium.io/docs/en/latest/intro/appium/).
|
||||
|
||||
## Sponsors
|
||||
|
||||
Appium has a [Sponsorship Program](GOVERNANCE.md#sponsorship)! If you or your company uses Appium
|
||||
and wants to give back financially to the project, we use these funds to [encourage development and
|
||||
contributions](GOVERNANCE.md#compensation-scheme), as well as support other open source projects we
|
||||
rely on. [Become a sponsor](https://opencollective.com/appium) via our OpenCollective page.
|
||||
|
||||
### Development and Strategic Partners
|
||||
|
||||
Appium is incredibly grateful to our Development and Strategic Partners for their sustained
|
||||
contribution of project development and leadership!
|
||||
|
||||
<p align="center">
|
||||
<a href="https://www.browserstack.com/browserstack-appium?utm_campaigncode=701OW00000AoUTQYA3&utm_medium=partnered&utm_source=appium">
|
||||
<picture>
|
||||
<source srcset="packages/appium/docs/overrides/assets/images/sponsor-logo-browserstack-dark.png" media="(prefers-color-scheme: dark)"/>
|
||||
<source srcset="packages/appium/docs/overrides/assets/images/sponsor-logo-browserstack-light.png" media="(prefers-color-scheme: light)"/>
|
||||
<img src="packages/appium/docs/overrides/assets/images/sponsor-logo-browserstack-dark.png" width="300" alt="Browserstack"/>
|
||||
</picture>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
### Other Sponsors
|
||||
|
||||
A full list of sponsors is available at our [Sponsors page](https://appium.io/docs/en/latest/sponsors/).
|
||||
|
||||
## License
|
||||
|
||||
[Apache-2.0](./LICENSE)
|
||||
|
||||
[](https://app.fossa.io/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fappium%2Fappium?ref=badge_large)
|
||||
|
||||
`@appium/logger` package is under [ISC](./packages/logger/LICENSE) License.
|
||||
@@ -1,5 +0,0 @@
|
||||
en/latest
|
||||
en/assets
|
||||
ja/assets
|
||||
zh/assets
|
||||
site
|
||||
@@ -1,50 +0,0 @@
|
||||
INHERIT: ../../docutils/base-mkdocs.yml
|
||||
site_name: Appium Documentation
|
||||
repo_url: https://github.com/appium/appium
|
||||
repo_name: appium/appium
|
||||
edit_uri: edit/master/packages/appium/docs
|
||||
copyright: Copyright © 2012 OpenJS Foundation - <a href="#__consent">Change cookie settings</a>
|
||||
|
||||
theme:
|
||||
logo: assets/images/appium-logo-white.png
|
||||
favicon: assets/images/appium-logo.png
|
||||
custom_dir: overrides
|
||||
|
||||
plugins:
|
||||
blog:
|
||||
enabled: true
|
||||
post_excerpt: required
|
||||
redirects:
|
||||
redirect_maps:
|
||||
'intro/requirements.md': 'quickstart/requirements.md'
|
||||
'ecosystem/build-drivers.md': 'developing/build-drivers.md'
|
||||
'ecosystem/build-plugins.md': 'developing/build-plugins.md'
|
||||
'ecosystem/build-docs.md': 'developing/build-docs.md'
|
||||
'reference/commands/base-driver.md': 'commands/base-driver.md'
|
||||
'reference/commands/execute-driver-plugin.md': 'commands/execute-driver-plugin.md'
|
||||
'reference/commands/images-plugin.md': 'commands/images-plugin.md'
|
||||
'reference/commands/relaxed-caps-plugin.md': 'commands/relaxed-caps-plugin.md'
|
||||
'reference/commands/universal-xml-plugin.md': 'commands/universal-xml-plugin.md'
|
||||
'contributing/config-system.md': 'developing/config-system.md'
|
||||
'contributing/develop.md': 'contributing.md'
|
||||
|
||||
extra_css:
|
||||
- assets/stylesheets/extra.css
|
||||
|
||||
extra:
|
||||
homepage: /
|
||||
version:
|
||||
provider: mike
|
||||
social:
|
||||
- icon: fontawesome/brands/twitter
|
||||
link: https://twitter.com/AppiumDevs
|
||||
alternate:
|
||||
- name: English
|
||||
link: /docs/en/
|
||||
lang: en
|
||||
- name: 日本
|
||||
link: /docs/ja/
|
||||
lang: ja
|
||||
- name: 中文简体
|
||||
link: /docs/zh/
|
||||
lang: zh
|
||||
@@ -1,19 +0,0 @@
|
||||
authors:
|
||||
jlipps:
|
||||
name: 'Jonathan Lipps'
|
||||
description: 'Appium project lead. Learn more about Jonathan at https://jlipps.com'
|
||||
avatar: 'https://gravatar.com/avatar/fdaadd825d92709bcf7fe76dffa12c40681c6108c67bcddf19478b2d00e3794b.jpg'
|
||||
slug: 'jlipps'
|
||||
url: 'https://jlipps.com'
|
||||
srini:
|
||||
name: 'Srinivasan Sekar'
|
||||
description: 'Appium Maintainer'
|
||||
avatar: 'https://avatars.githubusercontent.com/u/8896549?v=4'
|
||||
slug: 'srini'
|
||||
url: 'https://srini.codes'
|
||||
sai:
|
||||
name: 'Sai krishna'
|
||||
description: 'Appium Maintainer'
|
||||
avatar: 'https://avatars.githubusercontent.com/u/4045041?v=4'
|
||||
slug: 'sai'
|
||||
url: 'https://saikrishna.tech'
|
||||
@@ -1,2 +0,0 @@
|
||||
# Appium Blog
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
---
|
||||
authors:
|
||||
- srini
|
||||
- sai
|
||||
date: 2024-07-13
|
||||
---
|
||||
|
||||
# Appium Conf 2024: Call for Proposals Now Open!
|
||||
|
||||
We're thrilled to announce that Appium Conf is back and going online for 2024! This is the official Appium conference where you can learn all about what's new in Appium 2.0 and explore the latest in automating tests for devices, mobile apps, and desktop applications.
|
||||
|
||||
<!-- more -->
|
||||
<div style="text-align:center;width:100%">
|
||||
<a href="https://confengine.com/conferences/appium-conf-2024/proposals?utm_medium=referal&utm_source=appium.io&utm_campaign=launch">
|
||||
<img id="image-36-42" alt="Appium Conf logo" src="https://appiumconf.com/wp-content/uploads/2021/06/Appium-Conf-Logo.svg" data-src="https://appiumconf.com/wp-content/uploads/2021/06/Appium-Conf-Logo.svg" decoding="async">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
||||
## Mark Your Calendars: September 13, 2024
|
||||
|
||||
Join us on September 13, 2024, for a full day of inspiring and practical talks from the Appium team and the world's top mobile testing and automation experts. With Appium 2.0 now well-established, this is your opportunity to dive deep and learn how to maximize its potential.
|
||||
|
||||
## Why Attend Appium Conf 2024?
|
||||
|
||||
1. **Gain Insights**: Hear interesting insights and inspiring case studies from global experts and the core Appium team.
|
||||
2. **Improve Your Strategy**: Gain valuable knowledge to enhance your own automation strategy.
|
||||
3. **Network**: Make connections with Appium users from around the globe.
|
||||
4. **Learn from Specialists**: Benefit from Appium specialists' expertise on a variety of topics.
|
||||
|
||||
## Call for Proposals: Share Your Knowledge!
|
||||
|
||||
We're looking for a diverse range of speakers from around the globe who are eager to share innovative ideas and use-cases of mobile test automation. If you have experience with Appium and insights that could benefit the community of test practitioners, we want to hear from you!
|
||||
|
||||
Don't miss this chance to contribute to the Appium community and showcase your expertise on a global stage.
|
||||
|
||||
## How to Submit Your Proposal
|
||||
|
||||
Visit our website at [https://appiumconf.com/](https://appiumconf.com/) and [Submit a Proposal](https://confengine.com/conferences/appium-conf-2024/proposals?utm_medium=referal&utm_source=appium.io&utm_campaign=launch). We're excited to see your innovative ideas and experiences with Appium!
|
||||
|
||||
## Stay Tuned
|
||||
|
||||
Keep an eye on our website and social media channels for updates on the conference schedule, keynote speakers, and more exciting details about Appium Conf 2024.
|
||||
|
||||
Join us in shaping the future of mobile and device test automation. Submit your proposal today and be part of this exciting event!
|
||||
@@ -1,46 +0,0 @@
|
||||
---
|
||||
authors:
|
||||
- jlipps
|
||||
date: 2024-06-10
|
||||
---
|
||||
|
||||
# Announcing BrowserStack as Appium's Strategic Partner
|
||||
|
||||
We have another exciting announcement for you! As part of our recently-announced [sponsorship and
|
||||
contributor compensation
|
||||
program](https://appium.io/docs/en/latest/blog/2024/05/14/announcing-appiums-sponsorship-program/),
|
||||
we are obviously in need of significant funding to support giving back to our contributor
|
||||
community. We are therefore extremely happy to announce one of our brand new exclusive Strategic Partners: <a
|
||||
href="https://www.browserstack.com/browserstack-appium?utm_campaigncode=701OW00000AoUTQYA3&utm_medium=partnered&utm_source=appium">BrowserStack</a>!
|
||||
|
||||
<!-- more -->
|
||||
|
||||
<div style="text-align:center;width:100%">
|
||||
<a href="https://www.browserstack.com/browserstack-appium?utm_campaigncode=701OW00000AoUTQYA3&utm_medium=partnered&utm_source=appium">
|
||||
<img src="/docs/en/latest/assets/images/sponsor-logo-browserstack-dark.png#only-dark" width="300" />
|
||||
<img src="/docs/en/latest/assets/images/sponsor-logo-browserstack-light.png#only-light" width="300" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
BrowserStack has long been recognized in the industry as having extensive support for Appium,
|
||||
allowing you to run functional and visual regression tests for your native and hybrid apps at scale
|
||||
across a variety of devices and platforms.
|
||||
|
||||
As a token of this collaboration, BrowserStack is extending a special premium access of
|
||||
BrowserStack App Automate product to Appium users. Discover more about this special offer at
|
||||
[browserstack.com](https://www.browserstack.com/browserstack-appium?utm_campaigncode=701OW00000AoUTQYA3&utm_medium=partnered&utm_source=appium&utm_term=partannouncement)
|
||||
|
||||
With the decision to come on board as Strategic Partner, BrowserStack is making a huge investment
|
||||
in Appium and the Appium community. BrowserStack is very interested in supporting the development
|
||||
of the Appium contributor base, and is proud to be a key part of our contributor compensation
|
||||
scheme. Without Strategic Partners like BrowserStack, we would not be able to maintain such
|
||||
a program! We welcome them with gratitude!
|
||||
|
||||
**About BrowserStack**
|
||||
|
||||
<em>BrowserStack is the world’s leading software testing platform powering over two million tests every
|
||||
day across 19 global data centers. BrowserStack helps Tesco, Shell, NVIDIA, Discovery, Wells Fargo,
|
||||
and over 50,000 customers deliver quality software at speed by moving testing to their Cloud.
|
||||
BrowserStack’s platform provides instant access to 20,000+ real mobile devices on a highly reliable
|
||||
cloud infrastructure that effortlessly scales as testing needs grow. With BrowserStack, Dev and QA
|
||||
teams can move fast while delivering an amazing experience for every customer.</em>
|
||||
@@ -1,47 +0,0 @@
|
||||
---
|
||||
authors:
|
||||
- jlipps
|
||||
date: 2024-06-06
|
||||
---
|
||||
|
||||
# Announcing HeadSpin as Appium's Development Partner
|
||||
|
||||
Recently, we announced a new [sponsorship and contributor compensation program](https://appium.io/docs/en/latest/blog/2024/05/14/announcing-appiums-sponsorship-program/) for the Appium
|
||||
project. As part of this program, we felt it was important to have a place to recognize those
|
||||
companies who make an extremely valuable kind of contribution to the project: dedicated maintainer
|
||||
time. Appium only survives because of the tireless efforts of the core maintainers, and some of us
|
||||
are only able to do this work because it is donated on behalf of our day jobs. We're therefore very
|
||||
excited to announce our first Development Partner: <a
|
||||
href="https://www.headspin.io/solutions/appium-mobile-test-automation">HeadSpin</a>!
|
||||
|
||||
<!-- more -->
|
||||
|
||||
<div style="text-align:center;width:100%">
|
||||
<a href="https://www.headspin.io/solutions/appium-mobile-test-automation"><img
|
||||
src="/docs/en/latest/assets/images/sponsor-logo-headspin.png" style="width: 300px"/></a>
|
||||
</div>
|
||||
|
||||
HeadSpin is a real device performance and UX analysis cloud, with full support for Appium. When you
|
||||
run your Appium tests on HeadSpin, you get access to comprehensive performance reports about your
|
||||
app, including potential network, system resource, or user experience issues. HeadSpin supports
|
||||
a variety of complex testing modes, including hooking up your own devices to the cloud, injecting
|
||||
and receiving video and audio to/from devices, working with media (streaming) platforms, and more.
|
||||
|
||||
HeadSpin has been one of the Appium project's biggest supporters for a long time, and currently
|
||||
employs two of Appium's core maintainers, donating a significant portion of their time towards the
|
||||
development, maintenance, support, and leadership of open source Appium repos. HeadSpin has also
|
||||
worked hard towards Appium's vision of a robust Appium ecosystem, contributing several
|
||||
HeadSpin-maintained Appium drivers and plugins to the community:
|
||||
|
||||
- A [Roku TV driver](https://github.com/headspinio/appium-roku-driver)
|
||||
- A [Tizen TV driver](https://github.com/headspinio/appium-tizen-tv-driver/)
|
||||
- A [LG webOS TV driver](https://github.com/headspinio/appium-lg-webos-driver/)
|
||||
- A [Unity game automation plugin](https://github.com/headspinio/appium-altunity-plugin)
|
||||
|
||||
These are all free and open source Appium components that HeadSpin has donated to the community
|
||||
with the goal of fostering collaboration in these areas of media app automation.
|
||||
|
||||
Looking to the future, HeadSpin plans to continue supporting Appium in significant ways, both as
|
||||
a Development Partner and a significant contributor to the Appium ecosystem. The Appium project is
|
||||
deeply indebted to HeadSpin for all of its support, and is proud to take this moment to recognize
|
||||
HeadSpin as our first official Development Partner!
|
||||
@@ -1,36 +0,0 @@
|
||||
---
|
||||
authors:
|
||||
- jlipps
|
||||
date: 2024-06-10
|
||||
---
|
||||
|
||||
# Announcing Sauce Labs as Appium's Strategic Partner
|
||||
|
||||
The good news surrounding our new [sponsorship and contributor compensation
|
||||
program](https://appium.io/docs/en/latest/blog/2024/05/14/announcing-appiums-sponsorship-program/)
|
||||
just keeps coming! Effective immediately we are fortunate and excited to be able to welcome a new Strategic Partner to
|
||||
the Appium project: <a href="https://saucelabs.com/resources/blog/appium-strategic-partner">Sauce Labs</a>!
|
||||
|
||||
<!-- more -->
|
||||
|
||||
<div style="text-align:center;width:100%">
|
||||
<a href="https://saucelabs.com/resources/blog/appium-strategic-partner"><img src="/docs/en/latest/assets/images/sponsor-logo-sauce.png" style="width: 300px"/></a>
|
||||
</div>
|
||||
|
||||
Sauce Labs has a long history of support for the Appium project. In fact, Appium was initially
|
||||
incubated at Sauce Labs under the direction of [@jlipps](https://github.com/jlipps), in the early
|
||||
days of mobile automation. Sauce Labs always envisioned Appium as being a project for and by the
|
||||
community, and cemented this perspective in 2016 with the donation of Appium to the JS Foundation
|
||||
(now the [OpenJS Foundation](https://openjsf.org/)). To this day, developers associated with Sauce
|
||||
Labs continue to help maintain various parts of the Appium project.
|
||||
|
||||
Now, Sauce Labs is making a further commitment to support Appium financially as a way to enable
|
||||
our new contributor compensation program. We're very excited about this program as a vehicle for
|
||||
getting more people involved in Appium's development and maintenance. With the help of Strategic
|
||||
Partners like Sauce Labs, our project will be better able to meet the needs of our constantly
|
||||
growing userbase, and attract new developers (like you?) to build the Appium ecosystem together.
|
||||
Needless to say, we are extremely grateful for Sauce Labs's support!
|
||||
|
||||
**About Sauce Labs**
|
||||
|
||||
<em>Sauce Labs is the leader in continuous quality. Our Platform for Test helps organizations transform software testing from a chore to a competitive advantage. With decades of expertise and deep roots in the Selenium and Appium open source communities, high performing software teams use our platform to execute any testing workload across thousands of different devices, browsers, and operating systems—anywhere, any time, and at any scale. For more information, please visit [saucelabs.com](https://saucelabs.com).</em>
|
||||
@@ -1,48 +0,0 @@
|
||||
---
|
||||
authors:
|
||||
- jlipps
|
||||
date: 2024-05-14
|
||||
---
|
||||
|
||||
# Announcing Appium's Sponsorship Program
|
||||
|
||||
Over the last several months, the Appium team has been working on a sponsorship program that is
|
||||
designed to help give back to Appium's contributors as well as to recognize important partner
|
||||
individuals or companies in the industry. This program is now officially launching! Let's dive into
|
||||
some of the details.
|
||||
|
||||
<!-- more -->
|
||||
|
||||
First of all, the details and fine print will always be available at Appium's [Governance
|
||||
document](https://github.com/appium/appium/blob/master/GOVERNANCE.md), which outlines how the
|
||||
project is run, who makes project decisions, etc... This document will be kept up to date with the
|
||||
latest details of the program, so that's the place to bookmark for reference!
|
||||
|
||||
As of now, Appium has opened up sponsorship opportunities via our [OpenCollective
|
||||
page](https://opencollective.com/appium). OpenCollective is a service which allows people to donate
|
||||
to open source groups. We've defined a number of giving tiers for donations, within a very wide
|
||||
range, and welcome any financial amount with gratitude! Based on the donation tier, sponsorship
|
||||
comes with a variety of benefits, including recognition at different places in the Appium repo,
|
||||
docs, and website. In addition to financial sponsorship, the project has decided to recognize
|
||||
significant donations of project leadership and maintenance efforts from companies who have
|
||||
invested heavily into Appium with their employees' time (we call these "Development Partners").
|
||||
|
||||
What will the project do with the funds raised through this sponsorship program? We aim to give it
|
||||
all back to the Appium community! With the details again available at our [Governance document](https://github.com/appium/appium/blob/master/GOVERNANCE.md),
|
||||
we have implemented a scheme very much inspired by our friends at [WebdriverIO](https://webdriver.io/blog/2024/02/15/new-contributor-stipend-program) (thanks WDIO
|
||||
friends!) There are three groups of contributors to Appium that this scheme supports: (1) project
|
||||
maintainers (those who devote consistent time to leading the project), (2) project contributors
|
||||
(anyone in the wider community that contributes code or documentation), and (3) upstream projects
|
||||
(other open source projects Appium relies heavily on to work well). Each month, the funds raised
|
||||
via sponsorship will be distributed in different amounts to each of these groups. So if you
|
||||
make a valuable contribution to Appium moving forward, you'll be eligible to get some cash as
|
||||
a thank-you! (You'll need to have your own OpenCollective account in good standing to receive
|
||||
funds).
|
||||
|
||||
We're very excited about this new program, and hope that it helps existing and new contributors to
|
||||
feel even better about donating their time to making Appium better. And of course, none of this
|
||||
would be possible without the amazing Partners and Sponsors we hope to attract! On that note, stay
|
||||
tuned over the next few weeks as we unveil some of our initial program launch Partners! And if you
|
||||
or your company would like to participate by supporting Appium financially, you are most welcome.
|
||||
Just head over to our OpenCollective page and choose the tier that reflects your desired
|
||||
contribution level.
|
||||
@@ -1,15 +0,0 @@
|
||||
---
|
||||
authors:
|
||||
- jlipps
|
||||
date: 2024-03-07
|
||||
---
|
||||
|
||||
# Hello World!
|
||||
|
||||
This is the first post in the Appium blog. There's not much to see here yet. We're creating this
|
||||
space so we can make announcements or post other news, or information about events, that don't
|
||||
really fit inside the documentation itself.
|
||||
|
||||
<!-- more -->
|
||||
|
||||
Stay tuned for more!
|
||||
@@ -1,70 +0,0 @@
|
||||
---
|
||||
hide:
|
||||
- toc
|
||||
|
||||
title: Server Command-Line Arguments
|
||||
---
|
||||
|
||||
To start the Appium server, you may either run `appium` or `appium server`. The `server` subcommand
|
||||
is considered to be the default, so if you omit it, Appium will interpret this as your request to
|
||||
run the Appium server.
|
||||
|
||||
The invocation of `appium` (or `appium server`) can take a number of arguments, which are detailed
|
||||
below.
|
||||
|
||||
!!! note
|
||||
|
||||
All of these arguments can be set via a [Configuration File](../guides/config.md) instead if
|
||||
you want. Any arguments set on the command line will override any arguments found in
|
||||
a configuration file.
|
||||
|
||||
|
||||
|<div style="width:12em">Argument</div>|Description|Type|<div style="width:8em">Default</div>|Aliases|
|
||||
|--|--|--|--|--|
|
||||
|`--address`|IP address to listen on|string|`0.0.0.0`|`-a`|
|
||||
|`--allow-cors`|Whether the Appium server should allow web browser connections from any host|boolean|`false`||
|
||||
|`--allow-insecure`|Set which [insecure features](../guides/security.md) are allowed to run in this server's sessions. Most features are defined on a driver level; see driver documentation for more details. Individual features can be overridden by `--deny-insecure`. Has no effect in combination with `--relaxed-security`.|array<string>|`[]`||
|
||||
|`--base-path`|Base path to use as the prefix for all webdriver routes running on the server|string|`""`|`-pa`|
|
||||
|`--callback-address`|Callback IP address (default: same as `--address`)|string||`-ca`|
|
||||
|`--callback-port`|Callback port (default: same as `--port`) (Value must be between `1` and `65535`)|integer|`4723`|`-cp`|
|
||||
|`--config`|Path to an [Appium configuration JSON file](../guides/config.md)|string|||
|
||||
|`--debug-log-spacing`|Add exaggerated spacing in logs to help with visual inspection|boolean|`false`||
|
||||
|`--default-capabilities`|Set the default desired capabilities, which will be set on each session unless overridden by received capabilities. If a string, a path to a JSON file containing the capabilities, or raw JSON.|object||`-dc`|
|
||||
|`--deny-insecure`|Set which [insecure features](../guides/security.md) are not allowed to run in this server's sessions. Most features are defined on a driver level; see driver documentation for more details. Since all insecure features are disabled by default, this argument has no effect without either `--allow-insecure` or `--relaxed-security`, and is applied after both.|array<string>|`[]`||
|
||||
|`--driver`|Driver-specific configuration. Keys should correspond to driver package names|object|||
|
||||
|`--drivers-import-chunk-size`|The maximum amount of drivers that could be imported in parallel on server startup|number|`3`||
|
||||
|`--keep-alive-timeout`|Number of seconds the Appium server should apply as both the keep-alive timeout and the connection timeout for all requests. Setting this to `0` disables the timeout.|integer|`600`|`-ka`|
|
||||
|`--request-timeout`|Number of seconds the Appium server should apply for receiving the entire HTTP request from the client. A value of 0 disables the timeout. Set it to a non-zero value to protect against potential Denial-of-Service attacks in case the server is deployed without a reverse proxy in front. HTTP requests that are running longer than allowed by this timeout would be rejected with the status code 408.|integer|`3600`||
|
||||
|`--local-timezone`|Use local timezone for timestamps|boolean|`false`||
|
||||
|`--log`|Also send log output to this file|string||`-g`|
|
||||
|`--log-filters`|One or more log filtering rules|array|||
|
||||
|`--log-level`|Log level (console[:file]) (Value must be one of: `info`, `info:debug`, `info:info`, `info:warn`, `info:error`, `warn`, `warn:debug`, `warn:info`, `warn:warn`, `warn:error`, `error`, `error:debug`, `error:info`, `error:warn`, `error:error`, `debug`, `debug:debug`, `debug:info`, `debug:warn`, `debug:error`)|string|`debug`||
|
||||
|`--log-format`|Log format (Value must be to one of: `text`, `json`, `pretty_json`). If logs are printed as JSON then the text coloring is always disabled.|string|`text`||
|
||||
|`--log-no-colors`|Do not use color in console output|boolean|`false`||
|
||||
|`--log-timestamp`|Show timestamps in console output|boolean|`false`||
|
||||
|`--long-stacktrace`|Add long stack traces to log entries. Recommended for debugging only.|boolean|`false`||
|
||||
|`--no-perms-check`|Skip various permission checks on the server startup|boolean|`false`||
|
||||
|`--nodeconfig`|Path to configuration JSON file to register Appium as a node with Selenium Grid 3; otherwise the configuration itself|string|||
|
||||
|`--plugin`|Plugin-specific configuration. Keys should correspond to plugin package names|object|||
|
||||
|`--plugins-import-chunk-size`|The maximum amount of plugins that could be imported in parallel on server startup|number|`7`||
|
||||
|`--port`|Port to listen on (Value must be between `1` and `65535`)|integer|`4723`|`-p`|
|
||||
|`--relaxed-security`|Allow all [insecure features](../guides/security.md). Only use this if all clients are in a trusted network and could not potentially break out of the session sandbox. Specific features can be overridden by using `--deny-insecure`.|boolean|`false`||
|
||||
|`--session-override`|Enables session override (clobbering)|boolean|`false`||
|
||||
|`--ssl-cert-path`|Absolute path to the `.cert` file if TLS is used. Must be provided together with `--ssl-key-path`. See the [SSL/TLS/SPDY Support guide](../guides/tls.md) for details|string|||
|
||||
|`--ssl-key-path`|Absolute path to the `.key` file if TLS is used. Must be provided together with `--ssl-cert-path`. See the [SSL/TLS/SPDY Support guide](../guides/tls.md) for details|string|||
|
||||
|`--strict-caps`|Cause sessions to fail if desired caps are sent in that Appium does not recognize as valid for the selected device|boolean|`false`||
|
||||
|`--tmp`|Absolute path to directory Appium can use to manage temp files|string|Windows: `C:\Windows\Temp`<br>Others: `/tmp`||
|
||||
|`--trace-dir`|Absolute path to directory Appium can use to save iOS instrument traces|string|`<tmp>/appium-instruments`||
|
||||
|`--use-drivers`|A list of drivers to activate. By default, all installed drivers will be activated.|array<string>|`[]`||
|
||||
|`--use-plugins`|A list of plugins to activate. To activate all plugins, the value should be an array with a single item `"all"`.|array<string>|`[]`||
|
||||
|`--webhook`|Also send log output to this http listener|string||`-G`|
|
||||
|
||||
The following arguments are used for information retrieval, after which the server will automatically exit. They are therefore meant for reference or debug purposes.
|
||||
|
||||
|<div style="width:10em">Argument</div>|Description|Alias|
|
||||
|--|--|--|
|
||||
|`--help`|Print instructions on using the Appium command-line. This argument can also be used for any Appium subcommands.|`-h`|
|
||||
|`--show-build-info`|Print detailed information on the Appium server version||
|
||||
|`--show-config`|Print the current Appium server configuration details||
|
||||
|`--show-debug-info`|Print information on the current environment: details about the operating system, Node.js, and Appium itself||
|
||||
|`--version`|Print the Appium server version|`-v`|
|
||||
@@ -1,24 +0,0 @@
|
||||
---
|
||||
hide:
|
||||
- toc
|
||||
|
||||
title: Server Environment Variables
|
||||
---
|
||||
|
||||
The primary way of configuring the Appium server is via [Command-Line Arguments](./args.md). However, some more
|
||||
advanced features are toggled or configured via environment variables. To set environment
|
||||
variables, refer to the documentation for your operating system and terminal. These are the
|
||||
environment variables that the Appium server understands:
|
||||
|
||||
|Variable |Description|
|
||||
|--------|-----------|
|
||||
|`APPIUM_HOME`|By default, Appium creates a directory called `.appium` in the home directory for your system user. You can adjust the directory with this variable, as detailed in the [Managing Extensions](../guides/managing-exts.md) guide.|
|
||||
|`APPIUM_TMP_DIR`|By default, Appium uses a random temporary directory for many of its operations. If you wish to use a specific directory, you may do so by including an absolute path as the value of this variable. The behaviour is equivalent to using the `--tmp` CLI arg.|
|
||||
|`APPIUM_PREFER_SYSTEM_UNZIP`|Set to `0` or `false` to request that Appium not use the `unzip` binary included on your system for unzipping downloaded apps or other artifacts. Instead it will use a JS-based unzip library. This could help on some systems with non-existent or non-standard `unzip` commands. Note that if unzipping fails using the system library, the fallback library will be attempted in any case, so setting this env var merely saves time in the event you know the system unzip will fail.|
|
||||
|`APPIUM_HOST`|Same as the `--address` CLI arg|
|
||||
|`APPIUM_PORT`|Same as the `--port` CLI arg|
|
||||
|`APPIUM_RELOAD_EXTENSIONS`|Set to `1` to cause Appium to re-require extensions when new sessions are created. This is mostly useful for [building extensions](../developing/build-drivers.md)|
|
||||
|`APPIUM_OMIT_PEER_DEPS`|Adds `--omit=peer` to all the NPM commands run internally by Appium. Mostly an internal feature.|
|
||||
|`APPIUM_APPS_CACHE_MAX_AGE`|Allows to set the maximum age (in minutes) for [cached applications](../guides/caching.md). The default value is `60 * 24` (24 hours). Do not set it to a lower number than the duration of a single session startup.|
|
||||
|`APPIUM_APPS_CACHE_MAX_ITEMS`|Allows to set the maximum amount of [cached applications](../guides/caching.md). The default value is `1024`. Do not set it to a lower number than the amount of apps in all parallel sessions per process.|
|
||||
|`APPIUM_APPS_CACHE_IGNORE_URL_QUERY`| If the above option is enabled then the 'search' part of the app URL will be cut off from cache keys. See the corresponding [feature request](https://discuss.appium.io/t/regarding-app-caching-when-using-aws-s3-presigned-urls/42713) for more details. Disabled by default.|
|
||||
@@ -1,226 +0,0 @@
|
||||
---
|
||||
title: Extension Command-Line Usage
|
||||
---
|
||||
|
||||
Appium allows for the flexible installation and management of various _extensions_, such as _drivers_
|
||||
(which provide Appium with the capability to automate a given platform) and _plugins_ (which can
|
||||
augment or alter the way individual Appium commands work). For a conceptual understanding of these
|
||||
entities, please review the [Introduction](../intro/index.md).
|
||||
|
||||
Management of drivers and plugins is handled by Appium's Extension CLI (command-line interface).
|
||||
|
||||
!!! note
|
||||
|
||||
This reference uses placeholders to refer to various options. Anywhere you see one of these
|
||||
placeholders in the reference, ensure you replace it with the correct type of actual content.
|
||||
|
||||
|Placeholder|Meaning|
|
||||
|--|--|
|
||||
|`<ext-type>`|"Extension type". It should be either `driver` or `plugin`. All the Extension CLI commands can be used with either drivers or plugins, so you must specify which type of extension will be used|
|
||||
|`<ext-name>`|"Extension name". This is the short name of the extension found in a call to `appium <ext-type> list`. This is distinct from the NPM package name of the extension or, in general, the "install spec" of the extension.|
|
||||
|`<install-spec>`|"Install specification". This refers to the string used to indicate what extension Appium should install.|
|
||||
|`<install-source>`|This refers to the method that Appium should use to install an extension.|
|
||||
|
||||
## Commands
|
||||
|
||||
All Extension CLI commands begin with `appium <ext-type>`, i.e., either `appium driver` or `appium
|
||||
plugin`.
|
||||
|
||||
All Extension CLI commands can take an optional `--json` argument, which will return the result of
|
||||
the command as a machine-readable JSON string rather than the standard output, which is colourized
|
||||
and tuned for human consumption.
|
||||
|
||||
### `doctor`
|
||||
|
||||
Run doctor checks for the given extension, which validate whether the extension has its prerequisites
|
||||
configured correctly. Note that not all extensions include doctor checks. See the
|
||||
[Building Doctor Checks](../developing/build-doctor-checks.md) tutorial for more details on
|
||||
how to create them.
|
||||
|
||||
Usage:
|
||||
|
||||
```
|
||||
appium <ext-type> doctor <ext-name>
|
||||
```
|
||||
|
||||
Required arguments:
|
||||
|
||||
- `<ext-type>`: must be `driver` or `plugin`
|
||||
- `<ext-name>`: the name of the extension whose doctor checks you want to run
|
||||
|
||||
Optional arguments:
|
||||
|
||||
- `--json`: return the result in JSON format
|
||||
|
||||
Example (run doctor checks for the UiAutomator2 driver):
|
||||
|
||||
```
|
||||
appium driver doctor uiautomator2
|
||||
```
|
||||
|
||||
### `install`
|
||||
|
||||
Install an extension. If successful, respond with the short name of the extension which can be used
|
||||
in other invocations of the Extension CLI. If the extension is a driver, also note which platforms
|
||||
may be used with the driver.
|
||||
|
||||
Usage:
|
||||
|
||||
```
|
||||
appium <ext-type> install <install-spec> [--source=<install-source>] [--package=<package-name>] [--json]
|
||||
```
|
||||
|
||||
Required arguments:
|
||||
|
||||
- `<ext-type>`: must be `driver` or `plugin`
|
||||
- `<install-spec>`: this is the name, location, and/or version of the extension you want to
|
||||
install. Its possible values are dependent on the `<install-source>` (see below).
|
||||
|
||||
Optional arguments:
|
||||
|
||||
- `--source`: this directs Appium where to find your extension. See below for a table of possible
|
||||
source types and corresponding install specification.
|
||||
- `--package`: when `<install-source>` is `git` or `github`, `--package` is required. It should be
|
||||
the Node.js package name of the extension. Without this information, Appium will not be able to
|
||||
find the installed package.
|
||||
- `--json`: return the result in JSON format
|
||||
|
||||
|Install source type|Behaviour|
|
||||
|--|--|
|
||||
|None|This is the default behaviour when no `--source` is used. In this case, Appium will look at `<install-spec>` and match it against the name of extensions available when running `appium <ext-type> list`, i.e., against the officially recognized extension names. If found, it will install that extension at the latest version via NPM|
|
||||
|`npm`|Install an extension based on its NPM package name. Here, `<install-spec>` must be the NPM package name with any additional NPM installation modifiers, like version (see below)|
|
||||
|`github`|Install an extension via a GitHub spec of the form `<org>/<repo>`|
|
||||
|`git`|Install an extension via a Git URL (e.g., `git+ssh://git-host.com/repo.git`)|
|
||||
|`local`|Install an extension via a local path. This must be a path to the directory where the Node.js package information for the driver is located.|
|
||||
|
||||
#### NPM-based `<install-spec>`
|
||||
|
||||
When Appium is installing an extension via NPM (as is the case when `--source` is either omitted or
|
||||
set to `npm`), the `<install-spec>` can be complex, and can include any kind of information allowed
|
||||
by `npm install`:
|
||||
|
||||
- `[@scope]/<name>`
|
||||
- `[@scope]/<name>@<version>`
|
||||
- `[@scope]/<name>@<tag>`
|
||||
- `[@scope]/<name>@<version range>`
|
||||
|
||||
#### Examples
|
||||
|
||||
- Install the latest XCUITest driver:
|
||||
|
||||
```
|
||||
appium driver install xcuitest
|
||||
```
|
||||
|
||||
- Install the XCUITest driver at version 4.11.1:
|
||||
|
||||
```
|
||||
appium driver install xcuitest@4.11.1
|
||||
```
|
||||
|
||||
- Install the `beta` version of the `@appium/fake-driver` from NPM:
|
||||
|
||||
```
|
||||
appium driver install --source=npm @appium/fake-driver@beta
|
||||
```
|
||||
|
||||
- Install a locally-developed plugin:
|
||||
|
||||
```
|
||||
appium plugin install --source=local /path/to/my/plugin
|
||||
```
|
||||
|
||||
### `list`
|
||||
|
||||
List installed and available extensions. "Available" extensions include those which are officially
|
||||
recognized by the Appium team, but you are not limited to installing only the extensions displayed
|
||||
in this list.
|
||||
|
||||
Usage:
|
||||
|
||||
```
|
||||
appium <ext-type> list [--installed] [--updates] [--json]
|
||||
```
|
||||
|
||||
Required arguments:
|
||||
|
||||
- `<ext-type>`: must be `driver` or `plugin`
|
||||
|
||||
Optional arguments:
|
||||
|
||||
- `--installed`: show only installed extensions, not installed plus available extensions
|
||||
- `--updates`: for extensions installed via NPM, display a message if there are any updates
|
||||
- `--json`: return the result in JSON format
|
||||
|
||||
### `run`
|
||||
|
||||
Run a script included in an extension package. Extension authors can include runnable scripts that
|
||||
assist with setup or perform other tasks. These scripts are given names (called the `<script-name>`
|
||||
in this reference) by extension authors and will generally be documented in extension
|
||||
documentation.
|
||||
|
||||
Usage:
|
||||
|
||||
```
|
||||
appium <ext-type> run <ext-name> [--json] <script-name> [script-args]
|
||||
```
|
||||
|
||||
Required arguments:
|
||||
|
||||
- `<ext-type>`: must be `driver` or `plugin`
|
||||
- `<ext-name>`: the name of the extension whose script you want to run
|
||||
- `<script-name>`: the name of the script the extension has published
|
||||
|
||||
Optional arguments:
|
||||
|
||||
- `script-args`: any arguments that Appium does not interpret as belonging to its own set of
|
||||
arguments will be passed along to the extension script
|
||||
- `--json`: return the result in JSON format
|
||||
|
||||
Example (run the `reset` script included with the UiAutomator2 driver):
|
||||
|
||||
```
|
||||
appium driver run uiautomator2 reset
|
||||
```
|
||||
|
||||
### `update`
|
||||
|
||||
Update one or more extensions that have been installed via NPM. By default, Appium will not
|
||||
automatically update any extension past a major version boundary, so as to prevent
|
||||
unintended breaking changes.
|
||||
|
||||
Usage:
|
||||
|
||||
```
|
||||
appium <ext-type> update <ext-name> [--unsafe] [--json]
|
||||
```
|
||||
|
||||
Required arguments:
|
||||
|
||||
- `<ext-type>`: must be `driver` or `plugin`
|
||||
- `<ext-name>`: the name of the extension to update, or the string `installed` (which will update
|
||||
all installed extensions)
|
||||
|
||||
Optional arguments:
|
||||
|
||||
- `--unsafe`: direct Appium to go ahead and update past a major version boundary
|
||||
- `--json`: return the result in JSON format
|
||||
|
||||
### `uninstall`
|
||||
|
||||
Remove an installed extension.
|
||||
|
||||
Usage:
|
||||
|
||||
```
|
||||
appium <ext-type> uninstall <ext-name> [--json]
|
||||
```
|
||||
|
||||
Required arguments:
|
||||
|
||||
- `<ext-type>`: must be `driver` or `plugin`
|
||||
- `<ext-name>`: the name of the extension to uninstall
|
||||
|
||||
Optional arguments:
|
||||
|
||||
- `--json`: return the result in JSON format
|
||||
@@ -1,26 +0,0 @@
|
||||
---
|
||||
hide:
|
||||
- toc
|
||||
|
||||
title: Command-Line Overview
|
||||
---
|
||||
|
||||
Appium provides a command-line executable (`appium`), which will likely be your main way of interacting with
|
||||
the Appium server. This section of the Appium documentation describes how to use this executable.
|
||||
|
||||
To start off, you can run `appium -v` or `appium --version` to return the installed version,
|
||||
or run `appium -h` or `appium --help` to return the help message.
|
||||
|
||||
The main `appium` executable provides the following subcommands:
|
||||
|
||||
1. `appium server` (or simply `appium`) - launch an Appium server
|
||||
- [See here for accepted arguments](./args.md)
|
||||
- For advanced features, [see here for accepted environment variables](./env-vars.md)
|
||||
2. `appium driver` - manage Appium drivers
|
||||
- [See here for details](./extensions.md)
|
||||
3. `appium plugin` - manage Appium plugins
|
||||
- [See here for details](./extensions.md)
|
||||
4. `appium setup` - batch install a preset of drivers and plugins
|
||||
- [See here for details](./setup.md)
|
||||
|
||||
Like the main command, you can also run each subcommand with the `-h` or `--help` flag to learn more about it.
|
||||
@@ -1,33 +0,0 @@
|
||||
---
|
||||
hide:
|
||||
- toc
|
||||
|
||||
title: Setup Command-Line Usage
|
||||
---
|
||||
|
||||
The `setup` command aims to simplify the initial procedure of setting up Appium. It allows to install
|
||||
multiple extensions (drivers/plugins) in one go, without the need to run
|
||||
`appium <ext-name> install <ext-name>` multiple times.
|
||||
|
||||
The command has several presets that can be used to install different sets of extensions.
|
||||
The presets are as follows:
|
||||
|
||||
|Preset|Installation Command|Included Drivers|Included Plugins|
|
||||
|--|--|--|--|
|
||||
|Mobile|`appium setup mobile` or `appium setup`|`uiautomator2`, `xcuitest`[^1], `espresso`|`images`|
|
||||
|Desktop application|`appium setup desktop`|`mac2`[^1], `windows`[^2]|`images`|
|
||||
|Desktop browser|`appium setup browser`|`safari`[^1], `gecko`, `chromium`|`images`|
|
||||
|
||||
Attempting to install a preset while already having one or more of its included extensions installed
|
||||
will only install the missing extensions.
|
||||
|
||||
Refer to the [Ecosystem documentation](../ecosystem/index.md) to learn more about the extensions
|
||||
listed above.
|
||||
|
||||
This commands also supports the functionality that allows to fully reset your Appium server
|
||||
deployment if you experience various configuration issues,
|
||||
for example, due to a failed upgrade attempt from an older Appium version, on server startup.
|
||||
By running `appium setup reset` the server would uninstall all installed drivers, plugins and their related manifest files from the currently used Appium home folder.
|
||||
|
||||
[^1]: Only installed if the host machine is running macOS.
|
||||
[^2]: Only installed if the host machine is running Windows.
|
||||
@@ -1,562 +0,0 @@
|
||||
# Driver: base-driver
|
||||
|
||||
### `createSession`
|
||||
|
||||
`POST` **`/session`**
|
||||
|
||||
Historically the first two arguments were reserved for JSONWP capabilities.
|
||||
Appium 2 has dropped the support of these, so now we only accept capability
|
||||
objects in W3C format and thus allow any of the three arguments to represent
|
||||
the latter.
|
||||
|
||||
**`See`**
|
||||
|
||||
[https://w3c.github.io/webdriver/#new-session](https://w3c.github.io/webdriver/#new-session)
|
||||
|
||||
<!-- comment source: multiple -->
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
| :------ | :------ | :------ |
|
||||
| `desiredCapabilities?` | `W3CDriverCaps`<`C`\> | the new session capabilities |
|
||||
| `requiredCapabilities?` | `W3CDriverCaps`<`C`\> | another place the new session capabilities could be sent (typically left undefined) |
|
||||
| `capabilities?` | `W3CDriverCaps`<`C`\> | another place the new session capabilities could be sent (typically left undefined) |
|
||||
|
||||
#### Response
|
||||
|
||||
`CreateResult`
|
||||
|
||||
The capabilities object representing the created session
|
||||
|
||||
### `deleteSession`
|
||||
|
||||
`DELETE` **`/session/:sessionId`**
|
||||
|
||||
Returns capabilities for the session and event history (if applicable)
|
||||
|
||||
<!-- comment source: method-signature -->
|
||||
|
||||
#### Response
|
||||
|
||||
`SingularSessionData`<`C`, `SessionData`\>
|
||||
|
||||
A session data object
|
||||
|
||||
### `getSession`
|
||||
|
||||
`GET` **`/session/:sessionId`**
|
||||
|
||||
Returns capabilities for the session and event history (if applicable).
|
||||
|
||||
!!! warning "Deprecated"
|
||||
|
||||
Please use `getAppiumSessionCapabilities` to get the session capabilities.
|
||||
Please use `getLogEvents` to get event history.
|
||||
|
||||
|
||||
<!-- comment source: multiple -->
|
||||
|
||||
#### Response
|
||||
|
||||
`SingularSessionData`<`C`, `SessionData`\>
|
||||
|
||||
A session data object
|
||||
|
||||
### `getAppiumSessionCapabilities`
|
||||
|
||||
`GET` **`/session/:sessionId/appium/capabilities`**
|
||||
|
||||
Returns capabilities for the session.
|
||||
|
||||
<!-- comment source: multiple -->
|
||||
|
||||
#### Response
|
||||
|
||||
`AppiumSessionCapabilities`
|
||||
|
||||
A session data object
|
||||
|
||||
### `findElement`
|
||||
|
||||
`POST` **`/session/:sessionId/element`**
|
||||
|
||||
Find a UI element given a locator strategy and a selector, erroring if it can't be found
|
||||
|
||||
**`See`**
|
||||
|
||||
[https://w3c.github.io/webdriver/#find-element](https://w3c.github.io/webdriver/#find-element)
|
||||
|
||||
<!-- comment source: method-signature -->
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
| :------ | :------ | :------ |
|
||||
| `using` | `string` | the locator strategy |
|
||||
| `value` | `string` | the selector to combine with the strategy to find the specific element |
|
||||
|
||||
#### Response
|
||||
|
||||
`Element`<`string`\>
|
||||
|
||||
The element object encoding the element id which can be used in element-related
|
||||
commands
|
||||
|
||||
### `findElementFromElement`
|
||||
|
||||
`POST` **`/session/:sessionId/element/:elementId/element`**
|
||||
|
||||
Find a UI element given a locator strategy and a selector, erroring if it can't be found. Only
|
||||
look for elements among the set of descendants of a given element
|
||||
|
||||
**`See`**
|
||||
|
||||
[https://w3c.github.io/webdriver/#find-element-from-element](https://w3c.github.io/webdriver/#find-element-from-element)
|
||||
|
||||
<!-- comment source: method-signature -->
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
| :------ | :------ | :------ |
|
||||
| `using` | `string` | the locator strategy |
|
||||
| `value` | `string` | the selector to combine with the strategy to find the specific element |
|
||||
|
||||
#### Response
|
||||
|
||||
`Element`<`string`\>
|
||||
|
||||
The element object encoding the element id which can be used in element-related
|
||||
commands
|
||||
|
||||
### `findElementFromShadowRoot`
|
||||
|
||||
`POST` **`/session/:sessionId/shadow/:shadowId/element`**
|
||||
|
||||
Find an element from a shadow root
|
||||
|
||||
**`See`**
|
||||
|
||||
[https://w3c.github.io/webdriver/#find-element-from-shadow-root](https://w3c.github.io/webdriver/#find-element-from-shadow-root)
|
||||
|
||||
<!-- comment source: method-signature -->
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
| :------ | :------ | :------ |
|
||||
| `using` | `string` | the locator strategy |
|
||||
| `value` | `string` | the selector to combine with the strategy to find the specific elements |
|
||||
|
||||
#### Response
|
||||
|
||||
`Element`<`string`\>
|
||||
|
||||
The element inside the shadow root matching the selector
|
||||
|
||||
### `findElements`
|
||||
|
||||
`POST` **`/session/:sessionId/elements`**
|
||||
|
||||
Find a a list of all UI elements matching a given a locator strategy and a selector
|
||||
|
||||
**`See`**
|
||||
|
||||
[https://w3c.github.io/webdriver/#find-elements](https://w3c.github.io/webdriver/#find-elements)
|
||||
|
||||
<!-- comment source: method-signature -->
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
| :------ | :------ | :------ |
|
||||
| `using` | `string` | the locator strategy |
|
||||
| `value` | `string` | the selector to combine with the strategy to find the specific elements |
|
||||
|
||||
#### Response
|
||||
|
||||
`Element`<`string`\>[]
|
||||
|
||||
A possibly-empty list of element objects
|
||||
|
||||
### `findElementsFromElement`
|
||||
|
||||
`POST` **`/session/:sessionId/element/:elementId/elements`**
|
||||
|
||||
Find a a list of all UI elements matching a given a locator strategy and a selector. Only
|
||||
look for elements among the set of descendants of a given element
|
||||
|
||||
**`See`**
|
||||
|
||||
[https://w3c.github.io/webdriver/#find-elements-from-element](https://w3c.github.io/webdriver/#find-elements-from-element)
|
||||
|
||||
<!-- comment source: method-signature -->
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
| :------ | :------ | :------ |
|
||||
| `using` | `string` | the locator strategy |
|
||||
| `value` | `string` | the selector to combine with the strategy to find the specific elements |
|
||||
|
||||
#### Response
|
||||
|
||||
`Element`<`string`\>[]
|
||||
|
||||
A possibly-empty list of element objects
|
||||
|
||||
### `findElementsFromShadowRoot`
|
||||
|
||||
`POST` **`/session/:sessionId/shadow/:shadowId/elements`**
|
||||
|
||||
Find elements from a shadow root
|
||||
|
||||
**`See`**
|
||||
|
||||
[https://w3c.github.io/webdriver/#find-element-from-shadow-root](https://w3c.github.io/webdriver/#find-element-from-shadow-root)
|
||||
|
||||
<!-- comment source: method-signature -->
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
| :------ | :------ | :------ |
|
||||
| `using` | `string` | the locator strategy |
|
||||
| `value` | `string` | the selector to combine with the strategy to find the specific elements |
|
||||
|
||||
#### Response
|
||||
|
||||
`Element`<`string`\>[]
|
||||
|
||||
A possibly empty list of elements inside the shadow root matching the selector
|
||||
|
||||
### `getLog`
|
||||
|
||||
`POST` **`/session/:sessionId/log`**
|
||||
|
||||
Get the log for a given log type.
|
||||
|
||||
!!! warning "Deprecated"
|
||||
|
||||
Please use the `/session/:sessionId/se/log` endpoint instead
|
||||
|
||||
<!-- comment source: method-signature -->
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
| :------ | :------ | :------ |
|
||||
| `type` | `string` | Name/key of log type as defined in ILogCommands.supportedLogTypes. |
|
||||
|
||||
#### Response
|
||||
|
||||
`any`
|
||||
|
||||
### `getLog`
|
||||
|
||||
`POST` **`/session/:sessionId/se/log`**
|
||||
|
||||
Get the log for a given log type.
|
||||
|
||||
<!-- comment source: method-signature -->
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
| :------ | :------ | :------ |
|
||||
| `type` | `string` | Name/key of log type as defined in ILogCommands.supportedLogTypes. |
|
||||
|
||||
#### Response
|
||||
|
||||
`any`
|
||||
|
||||
### `getLogEvents`
|
||||
|
||||
`POST` **`/session/:sessionId/appium/events`**
|
||||
|
||||
Get a list of events that have occurred in the current session
|
||||
|
||||
<!-- comment source: method-signature -->
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
| :------ | :------ | :------ |
|
||||
| `type?` | `string` \| `string`[] | filter the returned events by including one or more types |
|
||||
|
||||
#### Response
|
||||
|
||||
`EventHistory` \| `Record`<`string`, `number`\>
|
||||
|
||||
The event history for the session
|
||||
|
||||
### `getLogTypes`
|
||||
|
||||
`GET` **`/session/:sessionId/log/types`**
|
||||
|
||||
Get available log types as a list of strings
|
||||
|
||||
!!! warning "Deprecated"
|
||||
|
||||
Please use the `/session/:sessionId/se/log/types` endpoint instead
|
||||
|
||||
<!-- comment source: method-signature -->
|
||||
|
||||
#### Response
|
||||
|
||||
`string`[]
|
||||
|
||||
### `getLogTypes`
|
||||
|
||||
`GET` **`/session/:sessionId/se/log/types`**
|
||||
|
||||
Get available log types as a list of strings
|
||||
|
||||
<!-- comment source: method-signature -->
|
||||
|
||||
#### Response
|
||||
|
||||
`string`[]
|
||||
|
||||
### `getPageSource`
|
||||
|
||||
`GET` **`/session/:sessionId/source`**
|
||||
|
||||
Get the current page/app source as HTML/XML
|
||||
|
||||
**`See`**
|
||||
|
||||
[https://w3c.github.io/webdriver/#get-page-source](https://w3c.github.io/webdriver/#get-page-source)
|
||||
|
||||
<!-- comment source: method-signature -->
|
||||
|
||||
#### Response
|
||||
|
||||
`string`
|
||||
|
||||
The UI hierarchy in a platform-appropriate format (e.g., HTML for a web page)
|
||||
|
||||
### `getSessions`
|
||||
|
||||
`GET` **`/sessions`**
|
||||
|
||||
Get data for all sessions running on an Appium server
|
||||
|
||||
<!-- comment source: method-signature -->
|
||||
|
||||
#### Response
|
||||
|
||||
A list of session data objects.
|
||||
Each session data object will be returned with `id` and the session's capabilities as `capabilities` keys like an example below:
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id":"ba30c6da-c266-4734-8ddb-c16f5bb53e16",
|
||||
"capabilities":{ "platformName":"ios","browserName":"safari","automationName":"xcuitest","platformVersion":"17.2","deviceName":"iPhone 15" }
|
||||
},
|
||||
{
|
||||
"id":"1441110c-1ece-4e45-abbf-ebf404f45f0a",
|
||||
"capabilities":{ "platformName":"ios","browserName":"safari","automationName":"xcuitest","platformVersion":"17.0","deviceName":"iPhone 14" }
|
||||
},
|
||||
...
|
||||
]
|
||||
```
|
||||
|
||||
### `getSettings`
|
||||
|
||||
`GET` **`/session/:sessionId/appium/settings`**
|
||||
|
||||
Update the session's settings dictionary with a new settings object
|
||||
|
||||
<!-- comment source: method-signature -->
|
||||
|
||||
#### Response
|
||||
|
||||
``null``
|
||||
|
||||
### `updateSettings`
|
||||
|
||||
`POST` **`/session/:sessionId/appium/settings`**
|
||||
|
||||
Update the session's settings dictionary with a new settings object
|
||||
|
||||
<!-- comment source: method-signature -->
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
| :------ | :------ | :------ |
|
||||
| `settings` | `Settings` | A key-value map of setting names to values. Settings not named in the map will not have their value adjusted. |
|
||||
|
||||
#### Response
|
||||
|
||||
``null``
|
||||
|
||||
### `getStatus`
|
||||
|
||||
`GET` **`/status`**
|
||||
|
||||
**`Summary`**
|
||||
|
||||
Retrieve the server's current status.
|
||||
|
||||
**`Description`**
|
||||
|
||||
Returns information about whether a remote end is in a state in which it can create new sessions and can additionally include arbitrary meta information that is specific to the implementation.
|
||||
|
||||
The readiness state is represented by the ready property of the body, which is false if an attempt to create a session at the current time would fail. However, the value true does not guarantee that a New Session command will succeed.
|
||||
|
||||
Implementations may optionally include additional meta information as part of the body, but the top-level properties ready and message are reserved and must not be overwritten.
|
||||
|
||||
<!-- comment source: builtin-interface -->
|
||||
|
||||
#### Examples
|
||||
|
||||
<!-- BEGIN:EXAMPLES -->
|
||||
##### JavaScript
|
||||
<!-- BEGIN:EXAMPLE lang=JavaScript -->
|
||||
|
||||
```js
|
||||
// webdriver.io example
|
||||
await driver.status();
|
||||
```
|
||||
|
||||
<!-- END:EXAMPLE -->
|
||||
##### Python
|
||||
<!-- BEGIN:EXAMPLE lang=Python -->
|
||||
|
||||
```python
|
||||
driver.get_status()
|
||||
```
|
||||
|
||||
<!-- END:EXAMPLE -->
|
||||
##### Java
|
||||
<!-- BEGIN:EXAMPLE lang=Java -->
|
||||
|
||||
```java
|
||||
driver.getStatus();
|
||||
```
|
||||
|
||||
<!-- END:EXAMPLE -->
|
||||
##### Ruby
|
||||
<!-- BEGIN:EXAMPLE lang=Ruby -->
|
||||
|
||||
```ruby
|
||||
# ruby_lib example
|
||||
remote_status
|
||||
|
||||
# ruby_lib_core example
|
||||
@driver.remote_status
|
||||
```
|
||||
|
||||
<!-- END:EXAMPLE -->
|
||||
<!-- END:EXAMPLES -->
|
||||
|
||||
#### Response
|
||||
|
||||
`Object`
|
||||
|
||||
### `getTimeouts`
|
||||
|
||||
`GET` **`/session/:sessionId/timeouts`**
|
||||
|
||||
Set the various timeouts associated with a session
|
||||
|
||||
**`See`**
|
||||
|
||||
[https://w3c.github.io/webdriver/#set-timeouts](https://w3c.github.io/webdriver/#set-timeouts)
|
||||
|
||||
<!-- comment source: method-signature -->
|
||||
|
||||
#### Response
|
||||
|
||||
``null``
|
||||
|
||||
### `timeouts`
|
||||
|
||||
`POST` **`/session/:sessionId/timeouts`**
|
||||
|
||||
Set the various timeouts associated with a session
|
||||
|
||||
**`See`**
|
||||
|
||||
[https://w3c.github.io/webdriver/#set-timeouts](https://w3c.github.io/webdriver/#set-timeouts)
|
||||
|
||||
<!-- comment source: method-signature -->
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
| :------ | :------ | :------ |
|
||||
| `type?` | `string` | used only for the old (JSONWP) command, the type of the timeout |
|
||||
| `ms?` | `string` \| `number` | used only for the old (JSONWP) command, the ms for the timeout |
|
||||
| `script?` | `number` | the number in ms for the script timeout, used for the W3C command |
|
||||
| `pageLoad?` | `number` | the number in ms for the pageLoad timeout, used for the W3C command |
|
||||
| `implicit?` | `string` \| `number` | the number in ms for the implicit wait timeout, used for the W3C command |
|
||||
|
||||
#### Response
|
||||
|
||||
``null``
|
||||
|
||||
### `implicitWait`
|
||||
|
||||
`POST` **`/session/:sessionId/timeouts/implicit_wait`**
|
||||
|
||||
Set the implicit wait timeout
|
||||
|
||||
!!! warning "Deprecated"
|
||||
|
||||
Please use the `/session/:sessionId/timeouts` endpoint instead
|
||||
|
||||
Use `timeouts` instead
|
||||
|
||||
<!-- comment source: method-signature -->
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
| :------ | :------ | :------ |
|
||||
| `ms` | `string` \| `number` | the timeout in ms |
|
||||
|
||||
#### Response
|
||||
|
||||
``null``
|
||||
|
||||
### `logCustomEvent`
|
||||
|
||||
`POST` **`/session/:sessionId/appium/log_event`**
|
||||
|
||||
Add a custom-named event to the Appium event log
|
||||
|
||||
<!-- comment source: method-signature -->
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
| :------ | :------ | :------ |
|
||||
| `vendor` | `string` | the name of the vendor or tool the event belongs to, to namespace the event |
|
||||
| `event` | `string` | the name of the event itself |
|
||||
|
||||
#### Response
|
||||
|
||||
``null``
|
||||
|
||||
### `reset`
|
||||
|
||||
`POST` **`/session/:sessionId/appium/app/reset`**
|
||||
|
||||
Reset the current session (run the delete session and create session subroutines)
|
||||
|
||||
!!! warning "Deprecated"
|
||||
|
||||
Please use each driver's launch, activate, terminate or cleanup method.
|
||||
|
||||
Use explicit session management commands instead
|
||||
|
||||
<!-- comment source: method-signature -->
|
||||
|
||||
#### Response
|
||||
|
||||
``null``
|
||||
@@ -1,22 +0,0 @@
|
||||
# Plugin: execute-driver
|
||||
|
||||
### `executeDriverScript`
|
||||
|
||||
`POST` **`/session/:sessionId/appium/execute_driver`**
|
||||
|
||||
Implementation of a command within a plugin
|
||||
|
||||
At minimum, `D` must be `ExternalDriver`, but a plugin can be more narrow about which drivers it supports.
|
||||
|
||||
<!-- comment source: method-signature -->
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type |
|
||||
| :------ | :------ |
|
||||
| `script` | [script: string, scriptType: string, timeoutMs: number] |
|
||||
| `type?` | [script: string, scriptType: string, timeoutMs: number] |
|
||||
|
||||
#### Response
|
||||
|
||||
`unknown`
|
||||
@@ -1,89 +0,0 @@
|
||||
# Plugin: images
|
||||
|
||||
### `compareImages`
|
||||
|
||||
`POST` **`/session/:sessionId/appium/compare_images`**
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type |
|
||||
| :------ | :------ |
|
||||
| `mode` | `any`[] |
|
||||
| `options?` | `any`[] |
|
||||
|
||||
#### Response
|
||||
|
||||
`ComparisonResult`
|
||||
|
||||
### `findElement`
|
||||
|
||||
`POST` **`/session/:sessionId/element`**
|
||||
|
||||
Find a UI element given a locator strategy and a selector, erroring if it can't be found
|
||||
|
||||
**`See`**
|
||||
|
||||
[https://w3c.github.io/webdriver/#find-element](https://w3c.github.io/webdriver/#find-element)
|
||||
|
||||
<!-- comment source: method-signature -->
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
| :------ | :------ | :------ |
|
||||
| `using` | `any` | the locator strategy |
|
||||
| `value` | `any` | the selector to combine with the strategy to find the specific element |
|
||||
|
||||
#### Response
|
||||
|
||||
`any`
|
||||
|
||||
The element object encoding the element id which can be used in element-related
|
||||
commands
|
||||
|
||||
### `findElements`
|
||||
|
||||
`POST` **`/session/:sessionId/elements`**
|
||||
|
||||
Find a a list of all UI elements matching a given a locator strategy and a selector
|
||||
|
||||
**`See`**
|
||||
|
||||
[https://w3c.github.io/webdriver/#find-elements](https://w3c.github.io/webdriver/#find-elements)
|
||||
|
||||
<!-- comment source: method-signature -->
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
| :------ | :------ | :------ |
|
||||
| `using` | `any` | the locator strategy |
|
||||
| `value` | `any` | the selector to combine with the strategy to find the specific elements |
|
||||
|
||||
#### Response
|
||||
|
||||
`any`
|
||||
|
||||
A possibly-empty list of element objects
|
||||
|
||||
### `performActions`
|
||||
|
||||
`POST` **`/session/:sessionId/actions`**
|
||||
|
||||
If the actions contains image elements as origin, convert them to viewport coordinates before sending it to the external driver
|
||||
|
||||
**`See`**
|
||||
|
||||
[https://w3c.github.io/webdriver/#perform-actions](https://w3c.github.io/webdriver/#perform-actions)
|
||||
|
||||
<!-- comment source: method-signature -->
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
| :------ | :------ | :------ |
|
||||
| `actions` | `ActionSequence[]` | an array of action sequences |
|
||||
|
||||
#### Response
|
||||
|
||||
`null`
|
||||
@@ -1,25 +0,0 @@
|
||||
---
|
||||
hide:
|
||||
- toc
|
||||
|
||||
title: Intro to Commands
|
||||
---
|
||||
|
||||
Here you can find various commands exposed by the main Appium module through its base driver, as
|
||||
well as the commands available in several plugins.
|
||||
|
||||
!!! note
|
||||
|
||||
The Appium base driver only exposes a few commands, as it is not meant to be used on its own.
|
||||
Please refer to the documentation of your [Appium driver](../ecosystem/drivers.md) to learn
|
||||
about the commands it exposes, and then check your [Appium client](../ecosystem/clients.md)
|
||||
documentation for the exact syntax of that command.
|
||||
|
||||
The command listings can be found here:
|
||||
|
||||
* [Base Driver](./base-driver.md)
|
||||
* [Execute Driver Plugin](./execute-driver-plugin.md)
|
||||
* [Images Plugin](./images-plugin.md)
|
||||
* [Relaxed Caps Plugin](./relaxed-caps-plugin.md)
|
||||
* [Storage Plugin](./storage-plugin.md)
|
||||
* [Universal XML Plugin](./universal-xml-plugin.md)
|
||||
@@ -1,27 +0,0 @@
|
||||
# Plugin: relaxed-caps
|
||||
|
||||
### `createSession`
|
||||
|
||||
`POST` **`/session`**
|
||||
|
||||
Start a new automation session
|
||||
|
||||
**`See`**
|
||||
|
||||
[https://w3c.github.io/webdriver/#new-session](https://w3c.github.io/webdriver/#new-session)
|
||||
|
||||
<!-- comment source: multiple -->
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
| :------ | :------ | :------ |
|
||||
| `desiredCapabilities?` | `any` | the new session capabilities |
|
||||
| `requiredCapabilities?` | `any` | another place the new session capabilities could be sent (typically left undefined) |
|
||||
| `capabilities?` | `any` | another place the new session capabilities could be sent (typically left undefined) |
|
||||
|
||||
#### Response
|
||||
|
||||
`any`
|
||||
|
||||
The capabilities object representing the created session
|
||||
@@ -1,113 +0,0 @@
|
||||
# Plugin: storage
|
||||
|
||||
!!! tip
|
||||
|
||||
All these commands can be invoked without creating a session, allowing you to
|
||||
prepare your test environment in advance.
|
||||
|
||||
### `addStorageItem`
|
||||
|
||||
`POST` **`/storage/add`**
|
||||
|
||||
Add a new file to the storage
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
| :------ | :------ | :------ |
|
||||
| `name` | `string` | the name used to save the file (must not include path separator characters) |
|
||||
| `sha1` | `string` | SHA1 hash of the file to be uploaded |
|
||||
|
||||
#### Example
|
||||
|
||||
```bash
|
||||
curl -X POST --header "Content-Type: application/json" --data '{"name":"app.ipa","sha1":"ccc963411b2621335657963322890305ebe96186"}' http://127.0.0.1:4723/storage/add
|
||||
```
|
||||
|
||||
#### Response
|
||||
|
||||
`AddRequestResult`
|
||||
|
||||
A JSON object in the following format:
|
||||
```json
|
||||
{
|
||||
"ws": {
|
||||
"stream": "/storage/add/ccc963411b2621335657963322890305ebe96186/stream",
|
||||
"events": "/storage/add/ccc963411b2621335657963322890305ebe96186/events"
|
||||
},
|
||||
"ttlMs": 300000
|
||||
}
|
||||
```
|
||||
|
||||
| Name | Type | Description |
|
||||
| :------ | :------ | :------ |
|
||||
| `ws.stream` | `string` | the pathname of the streaming web socket used to upload the file content |
|
||||
| `ws.events` | `string` | the pathname of the events web socket used to notify about upload success or a failure |
|
||||
| `ttlMs` | `number` | the amount of milliseconds both web sockets will be kept active before they expire, or a file payload would be successfully uploaded |
|
||||
|
||||
|
||||
### `listStorageItems`
|
||||
|
||||
`GET` **`/storage/list`**
|
||||
|
||||
List all files present in the storage
|
||||
|
||||
#### Example
|
||||
|
||||
```bash
|
||||
curl http://127.0.0.1:4723/storage/list
|
||||
```
|
||||
|
||||
#### Response
|
||||
|
||||
`List<StorageItem>`
|
||||
|
||||
A list of items, where each item has the following properties:
|
||||
|
||||
| Name | Type | Description |
|
||||
| :------ | :------ | :------ |
|
||||
| `name` | `string` | the name of the file in the storage |
|
||||
| `path` | `string` | full path to the file on the remote file system |
|
||||
| `size` | `number` | file size in bytes |
|
||||
|
||||
### `deleteStorageItem`
|
||||
|
||||
`POST` **`/storage/delete`**
|
||||
|
||||
Deletes a file in the storage with the specified name
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
| :------ | :------ | :------ |
|
||||
| `name` | `string` | the name of the file to be deleted |
|
||||
|
||||
#### Example
|
||||
|
||||
```bash
|
||||
curl -X POST --header "Content-Type: application/json" --data '{"name":"app.ipa"}' http://127.0.0.1:4723/storage/delete
|
||||
```
|
||||
|
||||
#### Response
|
||||
|
||||
`boolean`
|
||||
|
||||
`false` if the file does not exist in the storage, or `true` upon successful file deletion
|
||||
|
||||
### `resetStorage`
|
||||
|
||||
`POST` **`/storage/reset`**
|
||||
|
||||
Deletes all uploaded files and stops any incomplete uploads.
|
||||
If the `APPIUM_STORAGE_KEEP_ALL` flag is enabled, all uploaded files will be preserved,
|
||||
and only the incomplete uploads will be stopped.
|
||||
|
||||
#### Example
|
||||
|
||||
```bash
|
||||
curl -X POST http://127.0.0.1:4723/storage/reset
|
||||
```
|
||||
|
||||
#### Response
|
||||
|
||||
`undefined`
|
||||
@@ -1,68 +0,0 @@
|
||||
# Plugin: universal-xml
|
||||
|
||||
### `findElement`
|
||||
|
||||
`POST` **`/session/:sessionId/element`**
|
||||
|
||||
Find a UI element given a locator strategy and a selector, erroring if it can't be found
|
||||
|
||||
**`See`**
|
||||
|
||||
[https://w3c.github.io/webdriver/#find-element](https://w3c.github.io/webdriver/#find-element)
|
||||
|
||||
<!-- comment source: method-signature -->
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
| :------ | :------ | :------ |
|
||||
| `using` | `any`[] | the locator strategy |
|
||||
|
||||
#### Response
|
||||
|
||||
`any`
|
||||
|
||||
The element object encoding the element id which can be used in element-related
|
||||
commands
|
||||
|
||||
### `findElements`
|
||||
|
||||
`POST` **`/session/:sessionId/elements`**
|
||||
|
||||
Find a a list of all UI elements matching a given a locator strategy and a selector
|
||||
|
||||
**`See`**
|
||||
|
||||
[https://w3c.github.io/webdriver/#find-elements](https://w3c.github.io/webdriver/#find-elements)
|
||||
|
||||
<!-- comment source: method-signature -->
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
| :------ | :------ | :------ |
|
||||
| `using` | `any`[] | the locator strategy |
|
||||
|
||||
#### Response
|
||||
|
||||
`any`
|
||||
|
||||
A possibly-empty list of element objects
|
||||
|
||||
### `getPageSource`
|
||||
|
||||
`GET` **`/session/:sessionId/source`**
|
||||
|
||||
Get the current page/app source as HTML/XML
|
||||
|
||||
**`See`**
|
||||
|
||||
[https://w3c.github.io/webdriver/#get-page-source](https://w3c.github.io/webdriver/#get-page-source)
|
||||
|
||||
<!-- comment source: method-signature -->
|
||||
|
||||
#### Response
|
||||
|
||||
`string`
|
||||
|
||||
The UI hierarchy in a platform-appropriate format (e.g., HTML for a web page)
|
||||
@@ -1,150 +0,0 @@
|
||||
---
|
||||
hide:
|
||||
- navigation
|
||||
|
||||
title: Contributing to Appium
|
||||
---
|
||||
|
||||
The Appium project would not exist without the many contributions of code, documentation,
|
||||
maintenance, and support from companies and volunteers. As such, we welcome contributions!
|
||||
|
||||
There are a lot of different ways to help the project - see below for everything you can do and the
|
||||
processes to follow for each contribution method. Note that no matter how you contribute, your
|
||||
participation is governed by our [Code of Conduct](https://github.com/appium/appium/blob/master/CONDUCT.md).
|
||||
|
||||
## Join the Discussion Forum
|
||||
|
||||
You don't need to know the internals of Appium to be able to contribute! If you have experience with
|
||||
using Appium and feel like sharing your knowledge with others, consider helping out users on the
|
||||
Appium forums at [discuss.appium.io](https://discuss.appium.io/). Hop on over and see if there are
|
||||
any questions that you can answer.
|
||||
|
||||
## Report Bugs or Feature Requests
|
||||
|
||||
If you've encountered a bug, or have a cool feature in mind that you think Appium should support,
|
||||
make sure to let us know at our [GitHub issue tracker](https://github.com/appium/appium/issues).
|
||||
Please use the appropriate issue form template when creating your issue.
|
||||
|
||||
## Triage Issues
|
||||
|
||||
In addition to creating issues, you can also help us investigate already reported issues. All you
|
||||
need is enough familiarity with Appium to try and reproduce bugs.
|
||||
|
||||
You can get started by checking our [GitHub issue tracker](https://github.com/appium/appium/issues)
|
||||
for issues with labels such as `Needs Triage` or `Needs Info`, and leaving relevant comments:
|
||||
|
||||
- If the issue is a duplicate, drop a link to the original issue
|
||||
- If the user has not provided enough information (such as Appium logs), ask them for more details
|
||||
- If you can reproduce the problem on your own environment, provide all the information that you think
|
||||
would help us track down the cause of the issue
|
||||
|
||||
For further information on triaging Appium issues (for any Appium project repository), please contact
|
||||
any member of the [Technical Committee](https://github.com/appium/appium/blob/master/GOVERNANCE.md#the-technical-committee).
|
||||
|
||||
## Contribute Code
|
||||
|
||||
We are always open to pull requests for improving the Appium code or documentation!
|
||||
|
||||
!!! info
|
||||
|
||||
Developer information may not be kept up to date as frequently as user-facing information, or
|
||||
it may be most relevant in its current form on the online repository, not in this published
|
||||
version. Make sure to check the repo or discuss with maintainers. We would be glad to help
|
||||
new contributors get started!
|
||||
|
||||
Start by cloning the repository (we recommend [forking](https://github.com/appium/appium/fork)
|
||||
it first):
|
||||
```sh
|
||||
git clone https://github.com/appium/appium.git
|
||||
cd appium
|
||||
```
|
||||
|
||||
!!! info
|
||||
|
||||
If you are VS Code user, you can easily check out the project using [Runme](https://runme.dev/api/runme?repository=https%3A%2F%2Fgithub.com%2Fappium%2Fappium.git&fileToOpen=packages%2Fappium%2Fdocs%2Fen%2Fcontributing%2Findex.md).
|
||||
|
||||
Install dependencies:
|
||||
```sh
|
||||
npm install
|
||||
```
|
||||
|
||||
From here on there are several things you can do.
|
||||
|
||||
Build the project:
|
||||
```sh
|
||||
npm run build
|
||||
```
|
||||
|
||||
Build the project and watch for changes:
|
||||
```sh
|
||||
npm run dev
|
||||
```
|
||||
|
||||
Start the locally built Appium server:
|
||||
```sh
|
||||
npm start
|
||||
```
|
||||
|
||||
Run various tests:
|
||||
```sh
|
||||
npm run lint
|
||||
npm run test:unit
|
||||
npm run test:types
|
||||
npm run test:smoke
|
||||
npm run test:e2e
|
||||
npm run test:quick # unit and types
|
||||
npm run test:slow # everything
|
||||
```
|
||||
|
||||
You can also run tests for specific workspaces, e.g.:
|
||||
|
||||
```sh
|
||||
export APPIUM_WORKSPACE=@appium/base-driver
|
||||
npm run test:unit -w $APPIUM_WORKSPACE
|
||||
```
|
||||
|
||||
### Documentation
|
||||
|
||||
The documentation for this project is [available in the project repository itself](https://github.com/appium/appium/tree/master/packages/appium/docs).
|
||||
It is contained in Markdown files, which are built by our documentation system in the
|
||||
`@appium/docutils` module. This module is based on [MkDocs](https://www.mkdocs.org/) and therefore
|
||||
requires [Python](https://www.python.org/) to be installed on your system.
|
||||
|
||||
Install Python dependencies:
|
||||
```sh
|
||||
npm run install-docs-deps
|
||||
```
|
||||
|
||||
After making your changes, you can run the documentation server in dev mode:
|
||||
```sh
|
||||
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.
|
||||
@@ -1,86 +0,0 @@
|
||||
---
|
||||
title: Building Documentation
|
||||
---
|
||||
|
||||
Once you've [built a driver](./build-drivers.md) or [built a plugin](./build-plugins.md) for Appium,
|
||||
you will hopefully want to document how that extension works for your users. The most basic way of
|
||||
doing this is to write up a quick `README.md` and keep it in the root of your project's repository.
|
||||
However, this can involve a lot of effort.
|
||||
|
||||
The Appium project has built tools to help with this, and we've packaged up these tools so our
|
||||
ecosystem developers building drivers and plugins can _also_ use them. The best way to get going
|
||||
with these tools is probably to look at an existing Appium driver repo to see how it's done, for
|
||||
example the [XCUITest driver repo](https://github.com/appium/appium-xcuitest-driver). But this guide
|
||||
will outline the basic approach.
|
||||
|
||||
### Conceptual architecture
|
||||
|
||||
Appium settled on [MkDocs](https://www.mkdocs.org/) as a Markdown-based documentation site
|
||||
generator. It uses a Python toolchain (and not Node.js), but it turned out to be the best option
|
||||
for our purposes. You can adjust this, but by default Appium's utilities also assume that you'll be
|
||||
using the [mkdocs-material](https://squidfunk.github.io/mkdocs-material/) theme/extension for
|
||||
MkDocs.
|
||||
|
||||
In order to make different versions of your docs available (one for each minor release of your
|
||||
extension, typically), we also bundle [Mike](https://github.com/jimporter/mike).
|
||||
|
||||
From here, building a basic docs site is as easy as collecting your Markdown files together and
|
||||
defining how you want them to be organized.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
To take advantage of Appium's documentation utilities, you'll need to install:
|
||||
|
||||
- [Python v3+](https://www.python.org/downloads/)
|
||||
- [pip](https://pip.pypa.io/en/stable/installation/) (this may be installed automatically with Python)
|
||||
- The `@appium/docutils` package:
|
||||
|
||||
```bash
|
||||
npm install --save-dev @appium/docutils
|
||||
```
|
||||
|
||||
### Initializing an Extension for Building Docs
|
||||
|
||||
To prepare your extension for generating documentation, run the following command:
|
||||
|
||||
```bash
|
||||
npx appium-docs init
|
||||
```
|
||||
|
||||
This will:
|
||||
|
||||
1. Create a `tsconfig.json` if one does not already exist. This is necessary even if your extension
|
||||
is not written in TypeScript.
|
||||
2. Create a `mkdocs.yml` with the necessary configuration for MkDocs.
|
||||
|
||||
### Documenting Your Extension
|
||||
|
||||
At this point, you can begin documenting your extension. By default, MkDocs will look for Markdown
|
||||
files in the `docs` directory. You can therefore create your Markdown documentation files, place
|
||||
them in `docs`, and add links to these files in `mkdocs.yml`.
|
||||
|
||||
Refer to the [MkDocs documentation](https://www.mkdocs.org/user-guide/writing-your-docs/) for
|
||||
information on how to organize and structure your documentation.
|
||||
|
||||
### Building the Docs
|
||||
|
||||
At this point, you can use the `appium-docs` CLI tool. Run this tool with no arguments to get the
|
||||
full help output and see all the available subcommands and parameters. Here are a few usage
|
||||
examples:
|
||||
|
||||
```bash
|
||||
# Generate reference and build the mkdocs site into the site dir
|
||||
npx appium-docs build
|
||||
|
||||
# Same as build, but host the docs on a local dev server
|
||||
# and watch for changes and rebuild when files change
|
||||
npx appium-docs build --serve
|
||||
|
||||
# Build the docs and deploy them with mike versioning to the docs-site branch
|
||||
# using the included commit message.
|
||||
# This is particularly useful for pushing content to a GitHub pages branch!
|
||||
npx appium-docs build \
|
||||
--deploy \
|
||||
-b docs-site \
|
||||
-m 'docs: auto-build docs for appium-xcuitest-driver@%s'
|
||||
```
|
||||
@@ -1,141 +0,0 @@
|
||||
---
|
||||
title: Building Doctor Checks
|
||||
---
|
||||
|
||||
The idea of Appium Doctor is to assist users with driver or plugin preconditions setup. Sometimes such
|
||||
preconditions might be quite complicated and require non-trivial technical knowledge. Doctor checks,
|
||||
which are vanilla Node.js class instances written by extension authors, simplify
|
||||
the setup process by automating diagnostics and possible fixes for the found issues. These checks
|
||||
might also be interactive to ensure better usage experience.
|
||||
|
||||
This tutorial is supposed to be used by plugin or driver authors that would like to help their users
|
||||
to deal with complicated setup or configuration steps.
|
||||
|
||||
## Adding Doctor Checks
|
||||
|
||||
### Typing Requirements
|
||||
|
||||
The term `Doctor Check` literally describes a single javascript class instance that implements the
|
||||
[IDoctorCheck interface](https://github.com/appium/appium/blob/master/packages/types/lib/doctor.ts).
|
||||
The interface defines the following methods and properties:
|
||||
|
||||
- `diagnose(): Promise<DoctorCheckResult>`: Contains the code to diagnose a possible issue
|
||||
- `fix(): Promise<string|null>`: Either fixes the actual problem if `hasAutofix()` returns true or
|
||||
returns a string description for possible manual fixes. If this method throws an exception named
|
||||
`FixSkippedError` and `hasAutofix()` returns true then the result of the method invocation
|
||||
is going to be ignored.
|
||||
- `hasAutofix(): boolean`: Whether calling `fix()` would resolve the found issue
|
||||
- `isOptional(): boolean`: Whether the found issue can be ignored and is not a showstopper
|
||||
- `log: AppiumLogger`: May be used for logging. This property may be assigned
|
||||
by the instance itself or by the Appium server if it is left unassigned.
|
||||
|
||||
The `DoctorCheckResult` object returned by the `diagnose()` method must contain the following properties:
|
||||
|
||||
- `ok: boolean`: Whether the diagnosis found no issues
|
||||
- `optional: boolean`: Whether the diagnosed issue is safe to ignore
|
||||
- `message: string`: The text message describing the diagnostic result
|
||||
|
||||
### Manifest Requirements
|
||||
|
||||
A single extension may export multiple Doctor checks to Appium. In order for these checks to be properly
|
||||
picked up by the server CLI after the corresponding extension is installed they might be listed in the
|
||||
package .json manifest under the `appium.doctor.checks` section similar to the definition below:
|
||||
|
||||
```json
|
||||
// ...
|
||||
"appium": {
|
||||
"driverName": "fake",
|
||||
"automationName": "Fake",
|
||||
"platformNames": [
|
||||
"Fake"
|
||||
],
|
||||
"mainClass": "FakeDriver",
|
||||
"schema": "./build/lib/fake-driver-schema.js",
|
||||
"scripts": {
|
||||
"fake-error": "./build/lib/scripts/fake-error.js",
|
||||
"fake-success": "./build/lib/scripts/fake-success.js",
|
||||
"fake-stdin": "./build/lib/scripts/fake-stdin.js"
|
||||
},
|
||||
"doctor": {
|
||||
"checks": [
|
||||
"./doctor/fake1.js",
|
||||
"./doctor/fake2.js"
|
||||
// ...
|
||||
]
|
||||
}
|
||||
},
|
||||
// ...
|
||||
```
|
||||
|
||||
Also, it makes sense to include the [@appium/types](https://www.npmjs.com/package/@appium/types) import
|
||||
to the package dev dependencies.
|
||||
|
||||
### Implementation Example
|
||||
|
||||
The below example is a "raw" Node.JS implementation that does not use any transpilation:
|
||||
|
||||
```js
|
||||
const {fs, doctor} = require('@appium/support');
|
||||
|
||||
/** @satisfies {import('@appium/types').IDoctorCheck} */
|
||||
class EnvVarAndPathCheck {
|
||||
/**
|
||||
* @param {string} varName
|
||||
*/
|
||||
constructor(varName) {
|
||||
this.varName = varName;
|
||||
}
|
||||
|
||||
async diagnose() {
|
||||
const varValue = process.env[this.varName];
|
||||
if (typeof varValue === 'undefined') {
|
||||
return doctor.nok(`${this.varName} environment variable is NOT set!`);
|
||||
}
|
||||
|
||||
if (await fs.exists(varValue)) {
|
||||
return doctor.ok(`${this.varName} is set to: ${varValue}`);
|
||||
}
|
||||
|
||||
return doctor.nok(`${this.varName} is set to '${varValue}' but this is NOT a valid path!`);
|
||||
}
|
||||
|
||||
async fix() {
|
||||
return (
|
||||
`Make sure the environment variable ${this.varName} is properly configured for the Appium server process`
|
||||
);
|
||||
}
|
||||
|
||||
hasAutofix() {
|
||||
return false;
|
||||
}
|
||||
|
||||
isOptional() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const androidHomeCheck = new EnvVarAndPathCheck('ANDROID_HOME');
|
||||
|
||||
module.exports = {androidHomeCheck};
|
||||
|
||||
/**
|
||||
* @typedef {import('@appium/types').DoctorCheckResult} CheckResult
|
||||
*/
|
||||
```
|
||||
|
||||
This file could be saved as `doctor/android-home-check.js` and then added to the package.json manifest
|
||||
as
|
||||
|
||||
```json
|
||||
// ...
|
||||
"appium": {
|
||||
// ...
|
||||
"doctor": {
|
||||
"checks": [
|
||||
"./doctor/android-home-check.js",
|
||||
]
|
||||
}
|
||||
// ...
|
||||
},
|
||||
// ...
|
||||
```
|
||||
@@ -1,860 +0,0 @@
|
||||
---
|
||||
title: Building Drivers
|
||||
---
|
||||
|
||||
Appium wants to make it easy for anyone to develop their own automation drivers as part of the
|
||||
Appium ecosystem. This guide will explain what's involved and how you can accomplish various driver
|
||||
development tasks using the tools Appium provides. This guide assumes you (1) are a competent user of
|
||||
Appium, (2) are a competent Node.js developer, and (3) that you have read and understood the
|
||||
[Driver Intro](../intro/drivers.md).
|
||||
|
||||
If that describes you, great! This guide will get you started.
|
||||
|
||||
## Before you create your driver
|
||||
|
||||
Before you get to work implementing your driver, it's important to have a few things sorted out.
|
||||
For example, you need to know what your driver will do. Which platform is it trying to expose
|
||||
WebDriver automation for?
|
||||
|
||||
Appium doesn't magically give you the power to automate any platform. All it does is give you a set
|
||||
of convenient tools for implementing the WebDriver Protocol. So if you want to create, for example,
|
||||
a driver for a new app platform, you'll need to know how to automate apps on that platform *without Appium*.
|
||||
|
||||
This usually means that you need to be very familiar with app development for a given platform. And
|
||||
it usually means that you will rely on tools or SDKs provided by the platform vendor.
|
||||
|
||||
Basically, if you can't answer the question **"how would I launch, remotely trigger behaviours, and
|
||||
read state from an app on this platform?" then you're not quite ready to write an Appium driver**.
|
||||
Make sure you do the research to feel comfortable that there *is* a path forward. Once there is,
|
||||
coding it up and making it available as an Appium driver should be the easy part!
|
||||
|
||||
## Other drivers to reference
|
||||
|
||||
One of the greatest things about building an Appium driver is that there are already a number of
|
||||
open source Appium drivers which you can look at for reference. There is
|
||||
a [fake-driver](https://github.com/appium/appium/tree/master/packages/fake-driver) sample driver which
|
||||
does basically nothing other than showcase some of the things described in this guide.
|
||||
|
||||
And of course, all of Appium's official drivers are open source and available in repositories at
|
||||
the project's GitHub organization. So if you ever find yourself asking, "how does a driver do X?",
|
||||
read the code for these drivers! Also don't be afraid to ask questions of the Appium developers if
|
||||
you get stuck; we're always happy to help make sure the driver development experience is a good
|
||||
one!
|
||||
|
||||
## Basic requirements for Appium drivers
|
||||
|
||||
These are the things your driver *must* do (or be), if you want it to be a valid Appium driver.
|
||||
|
||||
### Node.js package with Appium extension metadata
|
||||
|
||||
All Appium drivers are fundamentally Node.js packages, and therefore must have a valid
|
||||
`package.json`. Your driver is not _limited_ to Node.js, but it must provide an adapter written in Node.js so it can be loaded by Appium.
|
||||
|
||||
Your `package.json` must include `appium` as a `peerDependency`. The requirements for the
|
||||
dependency versions should be as loose as possible (unless you happen to know your driver will only
|
||||
work with certain versions of Appium). For Appium 2, for example, this would look something like
|
||||
`^2.0.0`, declaring that your driver works with any version of Appium that starts with 2.x.
|
||||
|
||||
Your `package.json` must contain an `appium` field, like this (we call this the 'Appium extension
|
||||
metadata'):
|
||||
|
||||
```json
|
||||
{
|
||||
...,
|
||||
"appium": {
|
||||
"driverName": "fake",
|
||||
"automationName": "Fake",
|
||||
"platformNames": [
|
||||
"Fake"
|
||||
],
|
||||
"mainClass": "FakeDriver"
|
||||
},
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
The required subfields are:
|
||||
|
||||
* `driverName`: this should be a short name for your driver.
|
||||
* `automationName`: this should be the string users will use for their `appium:automationName`
|
||||
capability to tell Appium to use *your* driver.
|
||||
* `platformNames`: this is an array of one or more platform names considered valid for your driver.
|
||||
When a user sends in the `platformName` capability to start a session, it must be included in
|
||||
this list for your driver to handle the session. Known platform name strings include: `iOS`,
|
||||
`tvOS`, `macOS`, `Windows`, `Android`.
|
||||
* `mainClass`: this is a named export (in CommonJS style) from your `main` field. It must be a
|
||||
class which extends Appium's `BaseDriver` (see below).
|
||||
|
||||
### Extend Appium's `BaseDriver` class
|
||||
|
||||
Ultimately, your driver is much easier to write because most of the hard work of implementing the
|
||||
WebDriver protocol and handling certain common logic is taken care of already by Appium. This is
|
||||
all encoded up as a class which Appium exports for you to use, called `BaseDriver`. It is exported
|
||||
from `appium/driver`, so you can use one of these styles to import it and create your *own* class
|
||||
that extends it:
|
||||
|
||||
```js
|
||||
import {BaseDriver} from 'appium/driver';
|
||||
// or: const {BaseDriver} = require('appium/driver');
|
||||
|
||||
export class MyDriver extends BaseDriver {
|
||||
}
|
||||
```
|
||||
|
||||
### Make your driver available
|
||||
|
||||
That's basically it! With a Node.js package exporting a driver class and with correct Appium
|
||||
extension metadata, you've got yourself an Appium driver! Now it doesn't *do* anything, but you can
|
||||
load it up in Appium, start and stop sessions with it, etc...
|
||||
|
||||
To make it available to users, you could publish it via NPM. When you do so, your driver will be
|
||||
installable via the Appium CLI:
|
||||
|
||||
```
|
||||
appium driver install --source=npm <driver-package-on-npm>
|
||||
```
|
||||
|
||||
It's a good idea to test your driver first, of course. One way to see how it works within Appium is
|
||||
to install it locally first:
|
||||
|
||||
```
|
||||
appium driver install --source=local /path/to/your/driver
|
||||
```
|
||||
|
||||
### Developing your driver
|
||||
|
||||
How you develop your driver is up to you. It is convenient, however, to run it from within Appium
|
||||
without having to do lots of publishing and installing. The most straightforward way to do this is
|
||||
to include the most recent version of Appium as a `devDependency`, and then also your own driver,
|
||||
like this:
|
||||
|
||||
```json
|
||||
{
|
||||
"devDependencies": {
|
||||
...,
|
||||
"appium": "^2.0.0",
|
||||
"your-driver": "file:.",
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now, you can run Appium locally (`npm exec appium` or `npx appium`), and because your driver is
|
||||
listed as a dependency alongside it, it will be automatically "installed" and available. You can
|
||||
design your e2e tests this way, or if you're writing them in Node.js, you can simply import
|
||||
Appium's start server methods to handle starting and stopping the Appium server in Node. (TODO:
|
||||
reference an implementation of this in one of the open source drivers when ready).
|
||||
|
||||
Another way to do local development with an existing Appium server install is to simply install
|
||||
your driver locally:
|
||||
|
||||
```
|
||||
appium driver install --source=local /path/to/your/driver/dev/dir
|
||||
```
|
||||
|
||||
### Refreshing your driver during development
|
||||
|
||||
When the Appium server starts, it loads your driver into memory. Changes to your driver code will
|
||||
not take effect until the next time the Appium server starts. Simply starting a new session is not
|
||||
sufficient to cause your driver's code to be reloaded.
|
||||
|
||||
However, you can set the `APPIUM_RELOAD_EXTENSIONS` environment variable to `1` to request that
|
||||
Appium clear its module cache and reload extensions whenever a new session is requested. This may
|
||||
obviate the need to restart the server when you make code changes to your driver.
|
||||
|
||||
## Standard driver implementation ideas
|
||||
|
||||
These are things you will probably find yourself wanting to do when creating a driver.
|
||||
|
||||
### Set up state in a constructor
|
||||
|
||||
If you define your own constructor, you'll need to call `super` to make sure all the standard state
|
||||
is set up correctly:
|
||||
|
||||
```js
|
||||
constructor(...args) {
|
||||
super(...args);
|
||||
// now do your own thing
|
||||
}
|
||||
```
|
||||
|
||||
The `args` parameter here is the object containing all the CLI args used to start the Appium
|
||||
server.
|
||||
|
||||
### Define and validate accepted capabilities
|
||||
|
||||
You can define your own capabilities and basic validation for them. Users will always be able to
|
||||
send in capabilities that you don't define, but if they send in capabilities you have explicitly
|
||||
defined, then Appium will validate that they are of the correct type (and will check for the
|
||||
presence of required capabilities).
|
||||
|
||||
If you want to turn capability validation off entirely, set `this.shouldValidateCaps` to `false` in
|
||||
your constructor.
|
||||
|
||||
To give Appium your validation constraints, set `this.desiredCapConstraints` to a validation object
|
||||
in your constructor. Validation objects can be somewhat complex. Here's an example from the
|
||||
UiAutomator2 driver:
|
||||
|
||||
```js
|
||||
{
|
||||
app: {
|
||||
presence: true,
|
||||
isString: true
|
||||
},
|
||||
automationName: {
|
||||
isString: true
|
||||
},
|
||||
browserName: {
|
||||
isString: true
|
||||
},
|
||||
launchTimeout: {
|
||||
isNumber: true
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
### Start a session and read capabilities
|
||||
|
||||
Appium's `BaseDriver` already implements the `createSession` command, so you don't have to. However
|
||||
it's very common to need to perform your own startup actions (launching an app, running some
|
||||
platform code, or doing different things based on capabilities you have defined for your driver).
|
||||
So you'll probably end up overriding `createSession`. You can do so by defining the method in your
|
||||
driver:
|
||||
|
||||
```js
|
||||
async createSession(jwpCaps, reqCaps, w3cCaps, otherDriverData) {
|
||||
const [sessionId, caps] = super.createSession(w3cCaps);
|
||||
// do your own stuff here
|
||||
return [sessionId, caps];
|
||||
}
|
||||
```
|
||||
|
||||
For legacy reasons, your function will receive old-style JSON Wire Protocol desired and required
|
||||
caps as the first two arguments. Given that the old protocol isn't supported anymore and clients
|
||||
have all been updated, you can instead only rely on the `w3cCaps` parameter. (For a discussion
|
||||
about what `otherDriverData` is about, see the section below on concurrent drivers).
|
||||
|
||||
You'll want to make sure to call `super.createSession` in order to get the session ID as well as
|
||||
the processed capabilities (note that capabilities are also set on `this.caps`; modifying `caps`
|
||||
locally here would have no effect other than changing what the user sees in the create session
|
||||
response).
|
||||
|
||||
So that's it! You can fill out the middle section with whatever startup logic your driver requires.
|
||||
|
||||
### End a session
|
||||
|
||||
If your driver requires any cleanup or shutdown logic, it's best to do it as part of overriding the
|
||||
implementation of `deleteSession`:
|
||||
|
||||
```js
|
||||
async deleteSession() {
|
||||
// do your own cleanup here
|
||||
// don't forget to call super!
|
||||
await super.deleteSession();
|
||||
}
|
||||
```
|
||||
|
||||
It's very important not to throw any errors here if possible so that all parts of session cleanup
|
||||
can succeed!
|
||||
|
||||
### Access capabilities and CLI args
|
||||
|
||||
You'll often want to read parameters the user has set for the session, whether as CLI args or as
|
||||
capabilities. The easiest way to do this is to access `this.opts`, which is a merge of all options,
|
||||
from the CLI or from capabilities. So for example to access the `appium:app` capability, you could
|
||||
simply get the value of `this.opts.app`.
|
||||
|
||||
If you care about knowing whether something was sent in as a CLI arg *or* a capability, you can
|
||||
access the `this.cliArgs` and `this.caps` objects explicitly.
|
||||
|
||||
In all cases, the `appium:` capability prefix will have been stripped away by the time you are
|
||||
accessing values here, for convenience.
|
||||
|
||||
### Implement WebDriver classic commands
|
||||
|
||||
You handle WebDriver commands by implementing functions in your driver class. Each member of the
|
||||
WebDriver Protocol, plus the various Appium extensions, has a corresponding function that you
|
||||
implement if you want to support that command in your driver. The best way to see which commands
|
||||
Appium supports and which method you need to implement for each command is to look at Appium's
|
||||
[routes.js](https://github.com/appium/appium/blob/master/packages/base-driver/lib/protocol/routes.js).
|
||||
Each route object in this file tells you the command name as well as the parameters you'd expect to
|
||||
receive for that command.
|
||||
|
||||
Let's take this block for example:
|
||||
```js
|
||||
'/session/:sessionId/url': {
|
||||
GET: {command: 'getUrl'},
|
||||
POST: {command: 'setUrl', payloadParams: {required: ['url']}},
|
||||
}
|
||||
```
|
||||
|
||||
Here we see that the route `/session/:sessionId/url` is mapped to two commands, one for a `GET`
|
||||
request and one for a `POST` request. If we want to allow our driver to change the "url" (or
|
||||
whatever that might mean for our driver), we can therefore implement the `setUrl` command, knowing
|
||||
it will take the `url` parameter:
|
||||
|
||||
```js
|
||||
async setUrl(url) {
|
||||
// your implementation here
|
||||
}
|
||||
```
|
||||
|
||||
A few notes:
|
||||
- all command methods should be `async` functions or otherwise return a `Promise`
|
||||
- you don't need to worry about protocol encoding/decoding. You will get JS objects as params, and
|
||||
can return JSON-serializable objects in response. Appium will take care of wrapping it up in the
|
||||
WebDriver protocol response format, turning it into JSON, etc...
|
||||
- all session-based commands receive the `sessionId` parameter as the last parameter
|
||||
- all element-based commands receive the `elementId` parameter as the second-to-last parameter
|
||||
- if your driver doesn't implement a command, users can still try to access the command, and will
|
||||
get a `501 Not Yet Implemented` response error.
|
||||
|
||||
### Implement WebDriver BiDi commands
|
||||
|
||||
[WebDriver BiDi](https://w3c.github.io/webdriver-bidi) is a newer version of the WebDriver spec
|
||||
which is implemented over Websockets instead of HTTP. As an Appium driver author you can take
|
||||
advantage of Appium's BiDi support without having to know anything about the BiDi protocol or
|
||||
Websockets. Implementing handlers for BiDi commands works just the same as implementing handlers
|
||||
for WebDriver classic commands (described in the previous section). You simply define a method on
|
||||
your driver of the appropriate name, and it will be called when the BiDi command is requested by
|
||||
the client. To see which specific names you should use for BiDi commands, have a look at
|
||||
[bidi-commands.js](https://github.com/appium/appium/blob/master/packages/base-driver/lib/protocol/bidi-commands.js)
|
||||
|
||||
You are not limited to BiDi commands that are defined in the official BiDi specification. If you
|
||||
wish to define new commands, you may do so; you just need to tell Appium about them! See
|
||||
[below](#extend-the-existing-protocol-with-new-commands) for more information.
|
||||
|
||||
### Implement element finding
|
||||
|
||||
Element finding is a special command implementation case. You don't actually want to override
|
||||
`findElement` or `findElements`, even though those are what are listed in `routes.js`. Appium does
|
||||
a lot of work for you if instead you implement this function:
|
||||
|
||||
```js
|
||||
async findElOrEls(strategy, selector, mult, context) {
|
||||
// find your element here
|
||||
}
|
||||
```
|
||||
|
||||
Here's what gets passed in:
|
||||
|
||||
- `strategy` - a string, the locator strategy being used
|
||||
- `selector` - a string, the selector
|
||||
- `mult` - boolean, whether the user has requested one element or all elements matching the
|
||||
selector
|
||||
- `context` - (optional) if defined, will be a W3C Element (i.e., a JS object with the W3C element
|
||||
identifier as the key and the element ID as the value)
|
||||
|
||||
And you need to return one of the following:
|
||||
|
||||
- a single W3C element (an object as described above)
|
||||
- an array of W3C elements
|
||||
|
||||
Note that you can import that W3C web element identifier from `appium/support`:
|
||||
|
||||
```js
|
||||
import {util} from 'appium/support';
|
||||
const { W3C_WEB_ELEMENT_IDENTIFIER } = util;
|
||||
```
|
||||
|
||||
What you do with elements is up to you! Usually you end up keeping a cache map of IDs to actual
|
||||
element "objects" or whatever the equivalent is for your platform.
|
||||
|
||||
### Define valid locator strategies
|
||||
|
||||
Your driver might only support a subset of the standard WebDriver locator strategies, or it might
|
||||
add its own custom locator strategies. To tell Appium which strategies are considered valid for
|
||||
your driver, create an array of strategies and assign it to `this.locatorStrategies`:
|
||||
|
||||
```js
|
||||
this.locatorStrategies = ['xpath', 'custom-strategy'];
|
||||
```
|
||||
|
||||
Appium will throw an error if the user attempts to use any strategies other than the allowed ones,
|
||||
which enables you to keep your element finding code clean and deal with only the strategies you
|
||||
know about.
|
||||
|
||||
By default, the list of valid strategies is empty, so if your driver isn't simply proxying to
|
||||
another WebDriver endpoint, you'll need to define some. The protocol-standard locator strategies
|
||||
are defined [here](https://www.w3.org/TR/webdriver/#locator-strategies).
|
||||
|
||||
### Throw WebDriver-specific errors
|
||||
|
||||
The WebDriver spec defines a [set of error
|
||||
codes](https://github.com/jlipps/simple-wd-spec#error-codes) to accompany command responses if an
|
||||
error occurred. Appium has created error classes for each of these codes, so you can throw the
|
||||
appropriate error from inside a command, and it will do the right thing in terms of the protocol
|
||||
response to the user. To get access to these error classes, import them from `appium/driver`:
|
||||
|
||||
```
|
||||
import {errors} from 'appium/driver';
|
||||
|
||||
throw new errors.NoSuchElementError();
|
||||
```
|
||||
|
||||
### Log messages to the Appium log
|
||||
|
||||
You can always use `console.log`, of course, but Appium provides a nice logger for you as
|
||||
`this.log` (it has `.info`, `.debug`, `.log`, `.warn`, `.error` methods on it for differing log
|
||||
levels). If you want to create an Appium logger outside of a driver context (say in a script or
|
||||
helper file), you can always construct your own too:
|
||||
|
||||
```js
|
||||
import {logging} from 'appium/support';
|
||||
const log = logging.getLogger('MyDriver');
|
||||
```
|
||||
|
||||
## Further possibilities for Appium drivers
|
||||
|
||||
These are things your driver *can* do to take advantage of extra driver features or do its job more
|
||||
conveniently.
|
||||
|
||||
### Add a schema for custom command line arguments
|
||||
|
||||
You can add custom CLI args if you want your driver to receive data from the command line when the
|
||||
Appium server is started (for example, ports that a server administrator should set that should not
|
||||
be passed in as capabilities.
|
||||
|
||||
To define CLI arguments (or configuration properties) for the Appium server, your extension must provide a _schema_. In
|
||||
the `appium` property of your extension's `package.json`, add a `schema` property. This will either
|
||||
a) be a schema itself, or b) be a path to a schema file.
|
||||
|
||||
The rules for these schemas:
|
||||
|
||||
- Schemas must conform to [JSON Schema Draft-07](https://ajv.js.org/json-schema.html#draft-07).
|
||||
- If the `schema` property is a path to a schema file, the file must be in JSON or JS (CommonJS) format.
|
||||
- Custom `$id` values are unsupported. To use `$ref`, provide a value relative to the schema root, e.g., `/properties/foo`.
|
||||
- Known values of the `format` keyword are likely supported, but various other keywords may be unsupported. If you find a keyword that is unsupported which you need to use, please [ask for support](https://github.com/appium/appium/issues/new) or send a PR!
|
||||
- The schema must be of type `object` (`{"type": "object"}`), containing the arguments in a `properties` keyword. Nested properties are unsupported.
|
||||
|
||||
Example:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"test-web-server-port": {
|
||||
"type": "integer",
|
||||
"minimum": 1,
|
||||
"maximum": 65535,
|
||||
"description": "The port to use for the test web server"
|
||||
},
|
||||
"test-web-server-host": {
|
||||
"type": "string",
|
||||
"description": "The host to use for the test web server",
|
||||
"default": "sillyhost"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The above schema defines two properties which can be set via CLI argument or configuration file. If
|
||||
this extension is a _driver_ and its name is "horace", the CLI args would be
|
||||
`--driver-horace-test-web-server-port` and `--driver-horace-test-web-server-host`, respectively.
|
||||
Alternatively, a user could provide a configuration file containing:
|
||||
|
||||
```json
|
||||
{
|
||||
"server": {
|
||||
"driver": {
|
||||
"horace": {
|
||||
"test-web-server-port": 1234,
|
||||
"test-web-server-host": "localhorse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Add driver scripts
|
||||
|
||||
Sometimes you might want users of your driver to be able to run scripts outside the context of
|
||||
a session (for example, to run a script that pre-builds aspects of your driver). To support this,
|
||||
you can add a map of script names and JS files to the `scripts` field within your Appium extension
|
||||
metadata. So let's say you've created a script in your project that lives in a `scripts` directory
|
||||
in your project, named `driver-prebuild.js`. Then you could add a `scripts` field like this:
|
||||
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"prebuild": "./scripts/driver-prebuild.js"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now, assuming your driver is named `mydriver`, users of your driver can run `appium driver run
|
||||
mydriver prebuild`, and your script will execute.
|
||||
|
||||
### Proxy commands to another WebDriver implementation
|
||||
|
||||
A very common design architecture for Appium drivers is to have some kind of platform-specific
|
||||
WebDriver implementation that the Appium driver interfaces with. For example, the Appium
|
||||
UiAutomator2 driver interfaces with a special (Java-based) server running on the Android device. In
|
||||
webview mode, it also interfaces with Chromedriver.
|
||||
|
||||
If you find yourself in this situation, it is extremely easy to tell Appium that your driver is
|
||||
just going to be proxying WebDriver commands straight to another endpoint.
|
||||
|
||||
First, let Appium know that your driver *can* proxy by implementing the `canProxy` method:
|
||||
|
||||
```js
|
||||
canProxy() {
|
||||
return true;
|
||||
}
|
||||
```
|
||||
|
||||
Next, tell Appium which WebDriver routes it should *not* attempt to proxy (there often end up being
|
||||
certain routes that you don't want to forward on):
|
||||
|
||||
```js
|
||||
getProxyAvoidList() {
|
||||
return [
|
||||
['POST', new RegExp('^/session/[^/]+/appium')]
|
||||
];
|
||||
}
|
||||
```
|
||||
|
||||
The proxy avoidance list should be an array of arrays, where each inner array has an HTTP method as
|
||||
its first member, and a regular expression as its second. If the regular expression matches the
|
||||
route, then the route will not be proxied and instead will be handled by your driver. In this
|
||||
example, we are avoiding proxying all `POST` routes that have the `appium` prefix.
|
||||
|
||||
Next, we have to set up the proxying itself. The way to do this is to use a special class from
|
||||
Appium called `JWProxy`. (The name means "JSON Wire Proxy" and is related to a legacy
|
||||
implementation of the protocol). You'll want to create a `JWProxy` object using the details required to
|
||||
connect to the remote server:
|
||||
|
||||
```js
|
||||
// import {JWProxy} from 'appium/driver';
|
||||
|
||||
const proxy = new JWProxy({
|
||||
server: 'remote.server',
|
||||
port: 1234,
|
||||
base: '/',
|
||||
});
|
||||
|
||||
this.proxyReqRes = proxy.proxyReqRes.bind(proxy);
|
||||
this.proxyCommand = proxy.command.bind(proxy);
|
||||
```
|
||||
|
||||
Here we are creating a proxy object and assigning some of its methods to `this` under the names
|
||||
`proxyReqRes` and `proxyCommand`. This is required for Appium to use the proxy, so don't forget
|
||||
this step! The `JWProxy` has a variety of other options which you can check out in the source code,
|
||||
as well. (TODO: publish options as API docs and link here).
|
||||
|
||||
Finally, we need a way to tell Appium when the proxy is active. For your driver it might always
|
||||
be active, or it might only be active when in a certain context. You can define the logic as an
|
||||
implementation of `proxyActive`:
|
||||
|
||||
```js
|
||||
proxyActive() {
|
||||
return true; // or use custom logic
|
||||
}
|
||||
```
|
||||
|
||||
With those pieces in play, you won't have to reimplement anything that's already implemented by the
|
||||
remote endpoint you're proxying to. Appium will take care of all the proxying for you.
|
||||
|
||||
### Proxy BiDi commands to another BiDi implementation
|
||||
|
||||
All of the above about proxying WebDriver commands is conceptually also valid for proxying BiDi
|
||||
commands specifically. In order to enable BiDi proxying, you need to implement `get bidiProxyUrl`
|
||||
on your driver. This should return a Websocket URL which is the address of the upstream socket you
|
||||
want BiDi commands to be proxied to.
|
||||
|
||||
The intended pattern here is for you to start a session on the upstream implementation, check
|
||||
whether it has an active BiDi socket in the returned capabilities (e.g., the `webSocketUrl`
|
||||
capability), and then to set an internal field to that value, so that it can be returned by `get
|
||||
bidiProxyUrl`. Once all this is in place, Appium will proxy BiDi commands from the client straight
|
||||
to the upstream connection.
|
||||
|
||||
### Extend the existing protocol with new commands
|
||||
|
||||
You may find that the existing commands don't cut it for your driver. If you want to expose
|
||||
behaviours that don't map to any of the existing commands, you can create new commands in one of
|
||||
three ways:
|
||||
|
||||
1. Extending the classic WebDriver protocol and creating client-side plugins to access the extensions via the classic HTTP interface
|
||||
1. Extending the WebDriver BiDi protocol with new modules and methods, accessed from a client via the BiDi interface
|
||||
1. Overloading the Execute Script command by defining [Execute
|
||||
Methods](../guides/execute-methods.md)
|
||||
|
||||
If you want to follow the first path, you can direct Appium to recognize new methods and add them
|
||||
to its set of allowed HTTP routes and command names. You do this by assigning the `newMethodMap`
|
||||
static variable in your driver class to an object of the same form as Appium's `routes.js` object.
|
||||
For example, here is the `newMethodMap` for the `FakeDriver` example driver:
|
||||
|
||||
```js
|
||||
static newMethodMap = {
|
||||
'/session/:sessionId/fakedriver': {
|
||||
GET: {command: 'getFakeThing'},
|
||||
POST: {command: 'setFakeThing', payloadParams: {required: ['thing']}},
|
||||
},
|
||||
'/session/:sessionId/fakedriverargs': {
|
||||
GET: {command: 'getFakeDriverArgs'},
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
In this example we're adding a few new routes and a total of 3 new commands. For more examples of
|
||||
how to define commands in this way, it's best to have a look through `routes.js`. Now all you need
|
||||
to do is implement the command handlers in the same way you'd implement any other Appium command.
|
||||
|
||||
The downside of this way of adding new commands is that people using the standard Appium clients
|
||||
won't have nice client-side functions designed to target these endpoints. So you would need to
|
||||
create and release client-side plugins for each language you want to support (directions or
|
||||
examples can be found at the relevant client docs).
|
||||
|
||||
The second way of adding new commands is adding them as BiDi commands (accessed via the BiDi
|
||||
websocket interface, rather than the classic HTTP interface). BiDi commands come in two parts:
|
||||
a "module", which is basically a container or namespace, and a "command", which is the name of your
|
||||
new command.
|
||||
|
||||
As with the first method, you teach Appium to recognize your new BiDi commands by adding a static
|
||||
field to your driver class, called `newBidiCommands`. It has a format similar to `newMethodMap`.
|
||||
Basically it encapsulates information about the BiDi module, BiDi command name, reference to your
|
||||
driver instance method that will handle the command, and required and optional parameters. Here's
|
||||
an example of a `newBidiCommands` as implemented on an imaginary driver:
|
||||
|
||||
```js
|
||||
static newBidiCommands = {
|
||||
'appium:video': {
|
||||
startFramerateCapture: {
|
||||
command: 'startFrameCap',
|
||||
params: {
|
||||
required: ['videoSource'],
|
||||
optional: ['showOnScreen'],
|
||||
}
|
||||
},
|
||||
stopFramerateCapture: {
|
||||
command: 'stopFrameCap',
|
||||
},
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
In this imaginary example, we have defined two new BiDi commands:
|
||||
`appium:video.startFramerateCapture` and `appium:video.stopFramerateCapture`. Note first of all
|
||||
that, because we are defining a custom BiDi command, we should include a 'vendor prefix' (in this
|
||||
case, `appium:`, though you should pick something that represents your project). The first command
|
||||
takes a required and an optional parameter, and the second does not. When combined with generic
|
||||
BiDi support in your driver (see [the section on BiDi](#implement-webdriver-bidi-commands) above),
|
||||
and given an implementation of the appropriate methods on your driver (e.g. `startFrameCap` and
|
||||
`stopFrameCap` in this example), clients would be able to send these BiDi commands using whatever
|
||||
mechanism normally exists for doing so in the client library.
|
||||
|
||||
An alternative to these other ways of doing things is to overload a command which all WebDriver clients
|
||||
have access to already: Execute Script. Appium provides some a convenient tool for making this
|
||||
easy. Let's say you are building a driver for stereo system called `soundz`, and you wanted to
|
||||
create a command for playing a song by name. You could expose this to your users in such a way that
|
||||
they call something like:
|
||||
|
||||
```js
|
||||
// webdriverio example. Calling webdriverio's `executeScript` command is what trigger's Appium's
|
||||
// Execute Script command handler
|
||||
driver.executeScript('soundz: playSong', [{song: 'Stairway to Heaven', artist: 'Led Zeppelin'}]);
|
||||
```
|
||||
|
||||
Then in your driver code you can define the static property `executeMethodMap` as a mapping of
|
||||
script names to methods on your driver. It has the same basic form as `newMethodMap`, described
|
||||
above. Once `executeMethodMap` is defined, you'll also need to implement the Execute Script command
|
||||
handler, which according to Appium's routes mapping is called `execute`. The implementation can
|
||||
call a single helper function, `this.executeMethod`, which takes care of looking at the script and
|
||||
arguments the user sent in and routing it to the correct custom handler you've defined. Here's an
|
||||
example:
|
||||
|
||||
```js
|
||||
static executeMethodMap = {
|
||||
'soundz: playSong', {
|
||||
command: 'soundzPlaySong',
|
||||
params: {required: ['song', 'artist'], optional: []},
|
||||
}
|
||||
}
|
||||
|
||||
async soundzPlaySong(song, artist) {
|
||||
// play the song based on song and artist details
|
||||
}
|
||||
|
||||
async execute(script, args) {
|
||||
return await this.executeMethod(script, args);
|
||||
}
|
||||
```
|
||||
|
||||
A couple notes about this system:
|
||||
1. The arguments array sent via the call to Execute Script must contain only zero or one element(s). The
|
||||
first item in the list is considered to be the parameters object for your method. These parameters
|
||||
will be parsed, validated, and then applied to your overload method in the order specified in
|
||||
`executeMethodMap` (the order specified in the `required` parameters list, followed by the
|
||||
`optional` parameters list). I.e., this framework assumes only a single actual argument sent in via
|
||||
Execute Script (and this argument should be an object with keys/values representing the
|
||||
parameters your execute method expects).
|
||||
1. Appium does not automatically implement `execute` (the Execute Script handler) for you. You may
|
||||
wish, for example, to only call the `executeMethod` helper function when you're not in proxy
|
||||
mode!
|
||||
1. The `executeMethod` helper will reject with an error if a script name doesn't match one of the
|
||||
script names defined as a command in `executeMethodMap`, or if there are missing parameters.
|
||||
|
||||
One of the nice things about the Execute Method strategy is that methods implemented in this way
|
||||
will be available via the classic or BiDi interfaces (since they will result in the same Appium
|
||||
handlers being called).
|
||||
|
||||
|
||||
### Build Appium Doctor checks
|
||||
|
||||
Your users can run `appium driver doctor <driverName>` to run installation and health checks. Visit
|
||||
the [Building Doctor Checks](./build-doctor-checks.md) guide for more information on this
|
||||
capability.
|
||||
|
||||
### Implement handling of Appium settings
|
||||
|
||||
Appium users can send parameters to your driver via CLI args as well as via capabilities. But these
|
||||
cannot change during the course of a test, and sometimes users want to adjust parameters mid-test.
|
||||
Appium has a [Settings](../guides/settings.md) API for this purpose.
|
||||
|
||||
To support settings in your own driver, first of all define `this.settings` to be an instance of
|
||||
the appropriate class, in your constructor:
|
||||
|
||||
```js
|
||||
// import {DeviceSettings} from 'appium/driver';
|
||||
|
||||
this.settings = new DeviceSettings();
|
||||
```
|
||||
|
||||
Now, you can read user settings any time simply by calling `this.settings.getSettings()`. This will
|
||||
return a JS object where the settings names are keys and have their corresponding values.
|
||||
|
||||
If you want to assign some default settings, or run some code on your end whenever settings are
|
||||
updated, you can do both of these things as well.
|
||||
|
||||
```js
|
||||
constructor() {
|
||||
const defaults = {setting1: 'value1'};
|
||||
this.settings = new DeviceSettings(defaults, this.onSettingsUpdate.bind(this));
|
||||
}
|
||||
|
||||
async onSettingsUpdate(key, value) {
|
||||
// do anything you want here with key and value
|
||||
}
|
||||
```
|
||||
|
||||
### Emit BiDi events
|
||||
|
||||
With the WebDriver BiDi protocol, clients can subscribe to arbitrary events which can be sent
|
||||
asynchronously to the client over the BiDi socket connection. As an Appium driver author you don't
|
||||
need to worry about event subscription. If you want to emit an event with a certain method name and
|
||||
payload, it's as easy as using the built-in event emitter with the `bidiEvent` event.
|
||||
|
||||
As an
|
||||
example, let's say our driver wants to periodically emit CPU load information. We could define an
|
||||
event called `system.cpu`, and a payload that looks like `{load: 0.97}` to signify 97% CPU usage.
|
||||
Whenever we want, our driver can simply call the following code (assuming we have the current load
|
||||
in `this.currentCpuLoad`):
|
||||
|
||||
```js
|
||||
this.eventEmitter.emit('bidiEvent', {
|
||||
method: 'appium:system.cpu',
|
||||
params: {load: this.currentCpuLoad},
|
||||
})
|
||||
```
|
||||
|
||||
Now, if the client has subscribed to the `system.cpu` event, it will be notified with the load
|
||||
whenever the driver emits it.
|
||||
|
||||
### Make itself aware of resources other concurrent drivers are using
|
||||
|
||||
Let's say your driver uses up some system resources, like ports. There are a few ways to make sure
|
||||
that multiple simultaneous sessions don't use the same resources:
|
||||
|
||||
1. Have your users specify resource IDs via capabilities (`appium:driverPort` etc)
|
||||
1. Just always use free resources (find a new random port for each session)
|
||||
1. Have each driver express what resources it is using, then examine currently-used resources from
|
||||
other drivers when a new session begins.
|
||||
|
||||
To support this third strategy, you can implement `get driverData` in your driver to return what
|
||||
sorts of resources your driver is currently using, for example:
|
||||
|
||||
```js
|
||||
get driverData() {
|
||||
return {specialPort: 1234, specialFile: /path/to/file}
|
||||
}
|
||||
```
|
||||
|
||||
Now, when a new session is started on your driver, the `driverData` response from any other
|
||||
simultaneously running drivers (of the same type) will also be included, as the last parameter of
|
||||
the `createSession` method:
|
||||
|
||||
```js
|
||||
async createSession(jwpCaps, reqCaps, w3cCaps, driverData)
|
||||
```
|
||||
|
||||
You can dig into this `driverData` array to see what resources other drivers are using to help
|
||||
determine which ones you want to use for this particular session.
|
||||
|
||||
!!! warning
|
||||
|
||||
Be careful here, since `driverData` is only passed between sessions of a single running Appium
|
||||
server. There's nothing to stop a user from running multiple Appium servers and requesting your
|
||||
driver simultaneously on each of them. In this case, you won't be able to ensure independence
|
||||
of resources via `driverData`, so you might consider using file-based locking mechanisms or
|
||||
something similar.
|
||||
|
||||
!!! warning
|
||||
|
||||
It's also important to note you will only receive `driverData` for other instances of *your*
|
||||
driver. So unrelated drivers also running may still be using some system resources. In general
|
||||
Appium doesn't provide any features for ensuring unrelated drivers don't interfere with one
|
||||
another, so it's up to the drivers to allow users to specify resource locations or addresses to
|
||||
avoid clashes.
|
||||
|
||||
### Log events to the Appium event timeline
|
||||
|
||||
Appium has an [Event Timing API](../guides/event-timing.md) which allows users to get timestamps
|
||||
for certain server-side events (like commands, startup milestones, etc...) and display them on
|
||||
a timeline. The feature basically exists to allow introspection of timing for internal events to
|
||||
help with debugging or running analysis on Appium driver internals. You can add your own events to
|
||||
the event log:
|
||||
|
||||
```js
|
||||
this.logEvent(name);
|
||||
```
|
||||
|
||||
Simply provide a name for the event and it will be added at the current time, and made accessible
|
||||
as part of the event log for users.
|
||||
|
||||
### Hide behaviour behind security flags
|
||||
|
||||
Appium has a feature-flag based [security model](../guides/security.md) that allows driver authors
|
||||
to hide certain features behind security flags. What this means is that if you have a feature you
|
||||
deem insecure and want to require server admins to opt in to it, you can require that they enable
|
||||
the feature by adding it to the `--allow-insecure` list or turning off server security entirely.
|
||||
|
||||
To support the check within your own driver, you can call `this.isFeatureEnabled(featureName)` to
|
||||
determine whether a feature of the given name has been enabled. Or, if you want to simply
|
||||
short-circuit and throw an error if the feature isn't enabled, you can call
|
||||
`this.assertFeatureEnabled(featureName)`.
|
||||
|
||||
### Use a temp dir for files
|
||||
|
||||
If you want to use a temporary directory for files your driver creates that are not important to
|
||||
keep around between computer or server restarts, you can simply read from `this.opts.tmpDir`. This
|
||||
reads the temporary directory location from `@appium/support`, potentially overridden by a CLI
|
||||
flag. I.e., it's safer than writing to your own temporary directory because the location here plays
|
||||
nicely with possible user configuration. `this.opts.tmpDir` is a string, the path to the dir.
|
||||
|
||||
### Deal with unexpected shutdowns or crashes
|
||||
|
||||
Your driver might run into a situation where it can't continue operating normally. For example, it
|
||||
might detect that some external service has crashed and nothing will work anymore. In this case, it
|
||||
can call `this.startUnexpectedShutdown(err)` with an error object including any details, and Appium
|
||||
will attempt to gracefully handle any remaining requests before shutting down the session.
|
||||
|
||||
If you want to perform some of your own cleanup logic when you encounter this condition, you can
|
||||
either do so immediately before calling `this.startUnexpectedShutdown`, or you can attach a handler
|
||||
to the unexpected shutdown event and run your cleanup logic "out of band" so to speak:
|
||||
|
||||
```js
|
||||
this.onUnexpectedShutdown(handler)
|
||||
```
|
||||
|
||||
`handler` should be a function which receives an error object (representing the reason for the
|
||||
unexpected shutdown).
|
||||
@@ -1,514 +0,0 @@
|
||||
---
|
||||
title: Building Plugins
|
||||
---
|
||||
|
||||
This is a high-level guide for developing Appium plugins, which is not something most Appium users
|
||||
need to know or care about. If you are not familiar with Appium plugins yet from a user
|
||||
perspective, check out the [list of plugins](../ecosystem/plugins.md) to play around with some and get
|
||||
an idea of the sorts of things that plugins can do. Plugins are a powerful system for augmenting
|
||||
Appium's functionality or changing the way Appium works. They can be distributed to other Appium
|
||||
users and can extend Appium's ecosystem in all kinds of interesting ways! (There is also
|
||||
a significant amount of overlap here with developing Appium drivers, so you may also want to check
|
||||
out the [building drivers](./build-drivers.md) guide for further inspiration.)
|
||||
|
||||
## Before you create your plugin
|
||||
|
||||
Before creating your plugin, it's good to have a general idea of what you want your plugin to
|
||||
accomplish and whether it will be possible to implement it given the restrictions of the Appium
|
||||
platform. Reading through this guide will help you understand what's possible. In general, Appium's
|
||||
plugin system is extremely powerful and no attempts have been made to artificially limit what's
|
||||
possible with them (which is a main reason that all plugins are opt-in by the system administrator
|
||||
responsible for starting the Appium server---plugins are powerful and should only be used when
|
||||
explicitly trusted!).
|
||||
|
||||
## Other plugins to reference
|
||||
|
||||
There are a wide variety of open source Appium plugins available for perusal. It's definitely
|
||||
recommended to explore the code for some other plugins before embarking on writing your own. The
|
||||
Appium team maintains a set of official plugins in the [Appium GitHub
|
||||
repo](https://github.com/appium/appium). Links to other open source plugins can be found in the
|
||||
[Plugin list](../ecosystem/plugins.md)
|
||||
|
||||
## Basic requirements for plugins
|
||||
|
||||
These are the things your plugin *must* do (or be), if you want it to be a valid Appium plugin.
|
||||
|
||||
### Node.js package with Appium extension metadata
|
||||
|
||||
All Appium plugins are fundamentally Node.js packages, and therefore must have a valid
|
||||
`package.json`. Your driver is not _limited_ to Node.js, but it must provide an adapter written in
|
||||
Node.js so it can be loaded by Appium.
|
||||
|
||||
Your `package.json` must include `appium` as a `peerDependency`. The requirements for the
|
||||
dependency versions should be as loose as possible (unless you happen to know your plugin will only
|
||||
work with certain versions of Appium). For Appium 2, for example, this would look something like
|
||||
`^2.0.0`, declaring that your plugin works with any version of Appium that starts with 2.x.
|
||||
|
||||
Your `package.json` must contain an `appium` field, like this (we call this the 'Appium extension
|
||||
metadata'):
|
||||
|
||||
```json
|
||||
{
|
||||
...,
|
||||
"appium": {
|
||||
"pluginName": "fake",
|
||||
"mainClass": "FakePlugin"
|
||||
},
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
The required subfields are:
|
||||
|
||||
* `pluginName`: this should be a short name for your plugin.
|
||||
* `mainClass`: this is a named export (in CommonJS style) from your `main` field. It must be a
|
||||
class which extends Appium's `BasePlugin` (see below).
|
||||
|
||||
### Extend Appium's `BasePlugin` class
|
||||
|
||||
Ultimately, your plugin is much easier to write because most of the hard work of defining patterns
|
||||
for overriding commands is done for you. This is
|
||||
all encoded up as a class which Appium exports for you to use, called `BasePlugin`. It is exported
|
||||
from `appium/plugin`, so you can use one of these styles to import it and create your *own* class
|
||||
that extends it:
|
||||
|
||||
```js
|
||||
import {BasePlugin} from 'appium/plugin';
|
||||
// or: const {BasePlugin} = require('appium/plugin');
|
||||
|
||||
export class MyPlugin extends BasePlugin {
|
||||
// class methods here
|
||||
}
|
||||
```
|
||||
|
||||
!!! note
|
||||
|
||||
In all the code samples below, whenever we reference an example method, it is assumed
|
||||
that it is defined _within_ the class, though this is not explicitly written, for the sake of
|
||||
clarity and space.
|
||||
|
||||
### Make your plugin available
|
||||
|
||||
That's basically it! With a Node.js package exporting a plugin class and with correct Appium
|
||||
extension metadata, you've got yourself an Appium plugin! Now it doesn't *do* anything, but you can
|
||||
load it up in Appium, activate it, etc...
|
||||
|
||||
To make it available to users, you could publish it via NPM. When you do so, your plugin will be
|
||||
installable via the Appium CLI:
|
||||
|
||||
```
|
||||
appium plugin install --source=npm <plugin-package-on-npm>
|
||||
```
|
||||
|
||||
It's a good idea to test your plugin first, of course. One way to see how it works within Appium is
|
||||
to install it locally first:
|
||||
|
||||
```
|
||||
appium plugin install --source=local /path/to/your/plugin
|
||||
```
|
||||
|
||||
And of course, plugins must be "activated" during Appium server start, so make sure you direct your
|
||||
users to do so:
|
||||
|
||||
```
|
||||
appium --use-plugins=plugin-name
|
||||
```
|
||||
|
||||
### Developing your plugin
|
||||
|
||||
How you develop your plugin is up to you. It is convenient, however, to run it from within Appium
|
||||
without having to do lots of publishing and installing. The most straightforward way to do this is
|
||||
to include the most recent version of Appium as a `devDependency` (although its being already
|
||||
included as a `peerDependency` is sufficient in newer versions of NPM), and then also your own plugin,
|
||||
like this:
|
||||
|
||||
```json
|
||||
{
|
||||
"devDependencies": {
|
||||
...,
|
||||
"appium": "^2.0.0",
|
||||
"your-plugin": "file:.",
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now, you can run Appium locally (`npm exec appium` or `npx appium`), and because your plugin is
|
||||
listed as a dependency alongside it, it will be automatically "installed" and available. You can
|
||||
design your e2e tests this way, or if you're writing them in Node.js, you can simply import
|
||||
Appium's start server methods to handle starting and stopping the Appium server in Node.
|
||||
|
||||
Of course, you can always install it locally as described above as well.
|
||||
|
||||
Anytime you make changes to your plugin code, you'll need to restart the Appium server to make sure
|
||||
it picks up the latest code. As with drivers, you can set the `APPIUM_RELOAD_EXTENSIONS`
|
||||
environment variable if you wish Appium to try to re-require your plugin module when a new session
|
||||
starts.
|
||||
|
||||
## Standard plugin implementation ideas
|
||||
|
||||
These are things you will probably find yourself wanting to do when creating a plugin.
|
||||
|
||||
### Set up state in a constructor
|
||||
|
||||
If you define your own constructor, you'll need to call `super` to make sure all the standard state
|
||||
is set up correctly:
|
||||
|
||||
```js
|
||||
constructor(...args) {
|
||||
super(...args);
|
||||
// now do your own thing
|
||||
}
|
||||
```
|
||||
|
||||
The `args` parameter here is the object containing all the CLI args used to start the Appium
|
||||
server.
|
||||
|
||||
### Intercept and handle specific Appium commands
|
||||
|
||||
This is the most normal behavior for Appium plugins -- to modify or replace the execution of one or
|
||||
more commands that would normally be handled by the active driver. To override the default command
|
||||
handling, you need to implement `async` methods in your class with the same name as the Appium
|
||||
commands to be handled (just exactly how [drivers themselves are
|
||||
implemented](./build-drivers.md#implement-webdriver-commands)). Curious what command names there
|
||||
are? They are defined in the Appium base driver's
|
||||
[routes.js](https://github.com/appium/appium-base-driver/blob/master/lib/protocol/routes.js) file,
|
||||
and of course you can add more as defined in the next section.
|
||||
|
||||
Each command method is sent the following arguments:
|
||||
|
||||
1. `next`: This is a reference to an `async` function which encapsulates the chain of behaviors which would take place if this plugin were not handling the command. You can choose to call the next behavior in the chain at any point in your logic (by making sure to include `await next()` somewhere), or not. If you don't, it means the default behavior (or any plugins registered after this one) won't be run.
|
||||
1. `driver`: This is the object representing the driver handling the current session. You have access to it for any work you need to do, for example calling other driver methods, checking capabilities or settings, etc...
|
||||
1. `...args`: A spread array with any arguments that have been applied to the command by the user.
|
||||
|
||||
For example, if we wanted to override the `setUrl` command to simply add some extra logging on top,
|
||||
we would implement as follows:
|
||||
|
||||
```js
|
||||
async setUrl(next, driver, url) {
|
||||
this.log(`Let's get the page source for some reason before navigating to '${url}'!`);
|
||||
await driver.getPageSource();
|
||||
const result = await next();
|
||||
this.log(`We can also log after the original behaviour`);
|
||||
return result;
|
||||
}
|
||||
```
|
||||
|
||||
### Intercept and handle _all_ Appium commands
|
||||
|
||||
You might find yourself in a position where you want to handle *all* commands, in order to inspect
|
||||
payloads to determine whether or not to act in some way. If so, you can implement `async handle`,
|
||||
and any command that is not handled by one of your named methods will be handled by this method
|
||||
instead. It takes the following parameters (with all the same semantics as above):
|
||||
|
||||
1. `next`
|
||||
1. `driver`
|
||||
1. `cmdName` - string representing the command being run
|
||||
1. `...args`
|
||||
|
||||
For example, let's say we want to log timing for all Appium commands as part of a plugin. We could
|
||||
do this by implementing `handle` in our plugin class as follows:
|
||||
|
||||
```js
|
||||
async handle(next, driver, cmdName, ...args) {
|
||||
const start = Date.now();
|
||||
try {
|
||||
const result = await next();
|
||||
} finally {
|
||||
const elapsedMs = Date.now() - start;
|
||||
this.log(`Command '${cmdName}' took ${elapsedMs}`);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
```
|
||||
|
||||
### Working with driver proxies
|
||||
|
||||
There is a bit of a gotcha with handling Appium commands. Appium drivers have the ability to turn
|
||||
on a special 'proxy' mode, wherein the Appium server process takes a look at incoming URLs, and
|
||||
decides whether to forward them on to some upstream WebDriver server. It could happen that
|
||||
a command which a plugin wants to handle is designated as a command which is being proxied to an
|
||||
upstream server. In this case, we might run into a problem, because the plugin never gets a chance to
|
||||
handle that command! The way Appium handles this is as follows:
|
||||
|
||||
1. When a command comes in, before deciding whether to proxy the command, the main protocol handler
|
||||
checks to see whether a plugin would handle the command.
|
||||
2. If a plugin would *not* handle the command--all proceeds as normal, and the request is either
|
||||
proxied or not, based on the driver's proxy mode.
|
||||
3. If a plugin *would* handle the command, then the proxy behavior is skipped for the time being,
|
||||
and wrapped up as the `next` function sent to the plugin. So if you have a command handler in
|
||||
your plugin, and you want to be sure that the default driver proxying *does* take place, then
|
||||
simply `await next()` instead of (or in addition to) whatever your plugin handler is doing.
|
||||
|
||||
### Throw WebDriver-specific errors
|
||||
|
||||
The WebDriver spec defines a [set of error
|
||||
codes](https://github.com/jlipps/simple-wd-spec#error-codes) to accompany command responses if an
|
||||
error occurred. Appium has created error classes for each of these codes, so you can throw the
|
||||
appropriate error from inside a command, and it will do the right thing in terms of the protocol
|
||||
response to the user. To get access to these error classes, import them from `appium/driver`:
|
||||
|
||||
```js
|
||||
import {errors} from 'appium/driver';
|
||||
|
||||
throw new errors.NoSuchElementError();
|
||||
```
|
||||
|
||||
### Log messages to the Appium log
|
||||
|
||||
You can always use `console.log`, of course, but Appium provides a nice logger for you as
|
||||
`this.logger` (it has `.info`, `.debug`, `.log`, `.warn`, `.error` methods on it for differing log
|
||||
levels). If you want to create an Appium logger outside of a plugin context (say in a script or
|
||||
helper file), you can always construct your own too:
|
||||
|
||||
```js
|
||||
import {logging} from 'appium/support';
|
||||
const log = logging.getLogger('MyPlugin');
|
||||
```
|
||||
|
||||
## Further possibilities for Appium plugins
|
||||
|
||||
These are things your plugin *can* do to take advantage of extra plugin features or do its job more
|
||||
conveniently.
|
||||
|
||||
### Add a schema for custom command line arguments
|
||||
|
||||
You can add custom CLI args if you want your plugin to receive data from the command line when the
|
||||
Appium server is started (for example, ports that a server administrator should set that should not
|
||||
be passed in as capabilities).
|
||||
|
||||
This works largely the same for plugins as it does for drivers, so for more details have a look at
|
||||
[the equivalent section in the building drivers
|
||||
doc](./build-drivers.md#add-a-schema-for-custom-command-line-arguments).
|
||||
|
||||
The only difference is that to construct the CLI argument name, you prefix it with
|
||||
`--plugin-<name>`. So for example, if you have a plugin named `pluggo` and a CLI arg defined with
|
||||
the name `electro-port`, you can set it when starting Appium via `--plugin-pluggo-electro-port`.
|
||||
|
||||
Setting args via a configuration file is also supported, as it is for drivers, but under the
|
||||
`plugin` field instead. For example:
|
||||
|
||||
```json
|
||||
{
|
||||
"server": {
|
||||
"plugin": {
|
||||
"pluggo": {
|
||||
"electro-port": 1234
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Add plugin scripts
|
||||
|
||||
Sometimes you might want users of your plugin to be able to run scripts outside the context of
|
||||
a session (for example, to run a script that pre-builds aspects of your plugin). To support this,
|
||||
you can add a map of script names and JS files to the `scripts` field within your Appium extension
|
||||
metadata. So let's say you've created a script in your project that lives in a `scripts` directory
|
||||
in your project, named `plugin-prebuild.js`. Then you could add a `scripts` field like this:
|
||||
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"prebuild": "./scripts/plugin-prebuild.js"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now, assuming your plugin is named `myplugin`, users of your plugin can run `appium plugin run
|
||||
myplugin prebuild`, and your script will execute.
|
||||
|
||||
### Add new Appium commands
|
||||
|
||||
If you want to offer functionality that doesn't map to any of the existing commands supported by
|
||||
drivers, you can create new commands in one of two ways, just as is possible for drivers:
|
||||
|
||||
1. Extending the WebDriver protocol and creating client-side plugins to access the extensions
|
||||
1. Extending the WebDriver BiDi protocol with new modules and methods, accessed from a client via the BiDi interface
|
||||
1. Overloading the Execute Script command by defining [Execute
|
||||
Methods](../guides/execute-methods.md)
|
||||
|
||||
#### Extending the HTTP Protocol
|
||||
|
||||
If you want to follow the first path, you can direct Appium to recognize new methods and add them
|
||||
to its set of allowed HTTP routes and command names. You do this by assigning the `newMethodMap`
|
||||
static variable in your driver class to an object of the same form as Appium's `routes.js` object.
|
||||
For example, here is part of the `newMethodMap` for the `FakePlugin` example driver:
|
||||
|
||||
```js
|
||||
static newMethodMap = {
|
||||
'/session/:sessionId/fake_data': {
|
||||
GET: {command: 'getFakeSessionData', neverProxy: true},
|
||||
POST: {
|
||||
command: 'setFakeSessionData',
|
||||
payloadParams: {required: ['data']},
|
||||
neverProxy: true,
|
||||
},
|
||||
},
|
||||
'/session/:sessionId/fakepluginargs': {
|
||||
GET: {command: 'getFakePluginArgs', neverProxy: true},
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
!!! note
|
||||
|
||||
If you're using TypeScript, static member objects like these should be defined `as const`.
|
||||
|
||||
In this example we're adding a few new routes and a total of 3 new commands. For more examples of
|
||||
how to define commands in this way, it's best to have a look through `routes.js`. Now all you need
|
||||
to do is implement the command handlers in the same way you'd implement any other Appium command.
|
||||
|
||||
Note also the special `neverProxy` key for commands; this is generally a good idea to set to `true`
|
||||
for plugins, since your plugin might be active for a driver that is put into proxy mode but hasn't
|
||||
bothered to decline proxying for these (new and therefore unknown) commands. Setting `neverProxy`
|
||||
to `true` here will cause Appium to never proxy these routes and therefore ensure your plugin
|
||||
handles them, even if a driver is in proxy mode.
|
||||
|
||||
The downside of adding new commands via `newMethodMap` is that people using the standard Appium
|
||||
clients won't have nice client-side functions designed to target these endpoints. So you would need
|
||||
to create and release client-side plugins for each language you want to support (directions or
|
||||
examples can be found at the relevant client docs).
|
||||
|
||||
#### Extending the BiDi Protocol
|
||||
|
||||
You can also make new commands accessible via the WebDriver BiDi (WebSocket-based) protocol. BiDi
|
||||
commands come in two parts: a "module", which is basically a container or namespace, and
|
||||
a "command", which is the name of your new command.
|
||||
|
||||
As with the first method, you teach Appium to recognize your new BiDi commands by adding a static
|
||||
field to your driver class, called `newBidiCommands`. It has a format similar to `newMethodMap`.
|
||||
Basically it encapsulates information about the BiDi module, BiDi command name, reference to your
|
||||
driver instance method that will handle the command, and required and optional parameters. Here's
|
||||
an example of a `newBidiCommands` as implemented on an imaginary driver:
|
||||
|
||||
```js
|
||||
static newBidiCommands = {
|
||||
'appium:video': {
|
||||
startFramerateCapture: {
|
||||
command: 'startFrameCap',
|
||||
params: {
|
||||
required: ['videoSource'],
|
||||
optional: ['showOnScreen'],
|
||||
}
|
||||
},
|
||||
stopFramerateCapture: {
|
||||
command: 'stopFrameCap',
|
||||
},
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
In this imaginary example, we have defined two new BiDi commands: `appium:video.startFramerateCapture` and
|
||||
`appium:video.stopFramerateCapture`. The first command takes a required and an optional parameter, and the
|
||||
second does not. When you have implemented the `startFrameCap` and `stopFrameCap` methods on your
|
||||
plugin class, they will be called whenever the BiDi commands are triggered by a client. The
|
||||
signatures for these methods would look as follows:
|
||||
|
||||
```js
|
||||
async startFrameCap(next: () => Promise<any>, driver: DriverClass, videoSource: string, showOnScreen: boolean): Promise<any>;
|
||||
async stopFrameCap(next: () => Promise<any>, driver: DriverClass): Promise<any>;
|
||||
```
|
||||
|
||||
As with adding or overriding existing HTTP protocol commands, these methods are injected with
|
||||
a `next` parameter and a `driver` parameter. The `driver` object represents the driver currently
|
||||
owning the session, and calling `await next()` will execute/return the behavior that would execute
|
||||
were the plugin not active (i.e., the driver's own handling of that method, or the chain of
|
||||
commands executed with the same name by other active plugins).
|
||||
|
||||
Note that, currently, if a driver has BiDi proxying turned on, plugins will not be able to override
|
||||
BiDi methods handled by the proxy. Also note that since these are custom BiDi commands, our module
|
||||
name should include a vendor prefix (we chose `appium:`, but you can/should choose something that
|
||||
makes sense for your extension).
|
||||
|
||||
#### Overloading Execute Script
|
||||
|
||||
An alternative to these ways of doing things is to overload a command which all WebDriver clients
|
||||
have access to already: Execute Script. Make sure to read the section on [adding new
|
||||
commands](./build-drivers.md#extend-the-existing-protocol-with-new-commands) in
|
||||
the Building Drivers guide to understand the way this works in general. The way it works with
|
||||
plugins is only slightly different. Let's look at an example taken from Appium's `fake-plugin`:
|
||||
|
||||
```js
|
||||
static executeMethodMap = {
|
||||
'fake: plugMeIn': {
|
||||
command: 'plugMeIn',
|
||||
params: {required: ['socket']},
|
||||
},
|
||||
};
|
||||
|
||||
async plugMeIn(next, driver, socket) {
|
||||
return `Plugged in to ${socket}`;
|
||||
}
|
||||
|
||||
async execute(next, driver, script, args) {
|
||||
return await this.executeMethod(next, driver, script, args);
|
||||
}
|
||||
```
|
||||
|
||||
We have three important components shown here which make this system work, all of which are defined
|
||||
inside the plugin class:
|
||||
|
||||
1. The `executeMethodMap`, defined in just the same way as for drivers
|
||||
1. The implementation of the command method as defined in `executeMethodMap` (in this case,
|
||||
`plugMeIn`)
|
||||
1. The overriding/handling of the `execute` command. Just like any plugin command handlers, the
|
||||
first two arguments are `next` and `driver`, followed by the script name and args. `BasePlugin`
|
||||
implements a helper method which we can simply call with all of these arguments.
|
||||
|
||||
Overriding Execute Methods from drivers works as you'd expect: if your plugin defines an Execute
|
||||
Method with the same name as that of a driver, your command (in this case `plugMeIn`) will be
|
||||
called first. You can choose to run the driver's original behaviour via `next` if you want.
|
||||
|
||||
### Emit BiDi Events
|
||||
|
||||
Your plugin can emit custom BiDi events in [exactly the same way as Appium
|
||||
drivers](./build-drivers.md#emit-bidi-events).
|
||||
|
||||
### Build Appium Doctor checks
|
||||
|
||||
Your users can run `appium plugin doctor <pluginName>` to run installation and health checks. Visit
|
||||
the [Building Doctor Checks](./build-doctor-checks.md) guide for more information on this
|
||||
capability.
|
||||
|
||||
### Update the Appium server object
|
||||
|
||||
You probably don't normally need to update the Appium server object (which is an
|
||||
[Express](https://expressjs.com/) server having already been
|
||||
[configured](https://github.com/appium/appium/blob/master/packages/base-driver/lib/express/server.js)
|
||||
in a variety of ways). But, for example, you could add new Express middleware to the server to
|
||||
support your plugin's requirements. To update the server you must implement the `static async
|
||||
updateServer` method in your class. This method takes three parameters:
|
||||
|
||||
* `expressApp`: the Express app object
|
||||
* `httpServer`: the Node HTTP server object
|
||||
* `cliArgs`: a map of the CLI args used to start the Appium server
|
||||
|
||||
You can do whatever you want with them inside the `updateServer` method. You might want to
|
||||
reference how these objects are created and worked with in the BaseDriver code, so that you know
|
||||
you're not undoing or overriding anything standard and important. But if you insist, you can, with
|
||||
results you'll need to test! Warning: this should be considered an advanced feature and requires
|
||||
knowledge of Express, as well as the care not to do anything that could affect the operation of
|
||||
other parts of the Appium server!
|
||||
|
||||
### Handle unexpected session shutdown
|
||||
|
||||
When developing a plugin you may want to add some cleanup logic for when a session ends. You would
|
||||
naturally do this by adding a handler for `deleteSession`. This works in most cases, except when
|
||||
the session does not finish cleanly. Appium sometimes determines that a session has finished
|
||||
unexpectedly, and in these situations, Appium will look for a method called `onUnexpectedShutdown`
|
||||
in your plugin class, which will be called (passing the current session driver as the first
|
||||
parameter, and the error object representing the cause of the shutdown as the second), giving you
|
||||
an opportunity to take any steps that might be necessary to clean up from the session. For example,
|
||||
keeping in mind that the function is not `await`ed you could implement something like this:
|
||||
|
||||
```js
|
||||
async onUnexpectedShutdown(driver, cause) {
|
||||
try {
|
||||
// do some cleanup
|
||||
} catch (e) {
|
||||
// log any errors; don't allow anything to be thrown as they will be unhandled rejections
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -1,451 +0,0 @@
|
||||
---
|
||||
title: Appium's Config System
|
||||
---
|
||||
|
||||
Appium 2 supports [configuration files](../guides/config.md). A configuration file is intended to
|
||||
have (nearly) 1:1 parity with command-line arguments. An end user can supply Appium 2 with
|
||||
a configuration file, CLI args, or both (the args have precedence over the config file).
|
||||
|
||||
This document will be a technical overview of how the configuration system works. It is intended
|
||||
for Appium contributors, but will also explain the system's fundamental features.
|
||||
|
||||
## Reading a Config File
|
||||
|
||||
A config file is a JSON, JavaScript, or YAML file which can be validated against a schema. By
|
||||
default, this file will be named `.appiumrc.{json,js,yaml,yml}` and should be in the root of the
|
||||
project which depends upon `appium`. Other filenames and locations are supported via the `--config
|
||||
<file>` flag. For obvious reasons, the `config` argument is disallowed within config files.
|
||||
|
||||
In lieu of a separate file, configuration can be embedded in a project's `package.json` using the
|
||||
`appiumConfig` property, e.g.,:
|
||||
|
||||
```json
|
||||
{
|
||||
"appiumConfig": {
|
||||
"server": {
|
||||
"port": 12345
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
When an Appium server is started via the `appium` executable, the `init` function in `lib/main.js`
|
||||
will call into `lib/config-file.js` to load and/or search for a configuration file and in
|
||||
`package.json`.
|
||||
|
||||
!!! note
|
||||
|
||||
It is not an error if configuration isn't found!
|
||||
|
||||
The [`lilconfig`](https://npm.im/lilconfig) package provides the search & load functionality; refer
|
||||
to its documentation for more information about the search paths. Additionally, Appium provides
|
||||
support for config files written in YAML via the package [`yaml`](https://npm.im/yaml).
|
||||
|
||||
If a config file is found and successfully [validated](#validation), the result will be merged with
|
||||
a set of defaults and any additional CLI arguments. CLI arguments have precedence over config
|
||||
files, and config files have precedence over defaults.
|
||||
|
||||
## Validation
|
||||
|
||||
The same system is used for _both_ validation of config files _and_ command-line arguments.
|
||||
|
||||
The package [`ajv`](https://npm.im/ajv) provides validation. Of course, to make `ajv` validate
|
||||
anything, it must be provided a _schema_.
|
||||
|
||||
The _base_ schema is a [JSON Schema
|
||||
Draft-7](https://json-schema.org/draft/2020-12/json-schema-core.html)-compliant object exported by
|
||||
`lib/schema/appium-config-schema.js`. This schema defines configuration _native to Appium_, and
|
||||
only concerns its behavior as a _server_; it does not define configuration for any other
|
||||
functionality (e.g., the `plugin` or `driver` subcommands).
|
||||
|
||||
!!! warning
|
||||
|
||||
Note that this file is the _base_ schema; this will become painfully relevant.
|
||||
|
||||
This file is is _not_ a JSON file, because a) JSON is painful to work with for humans, b) is
|
||||
especially reviled by @jlipps, and c) `ajv` accepts objects, not JSON files.
|
||||
|
||||
It is more straightforward to explain how config files are validated, so we'll start there.
|
||||
|
||||
### Validating Config Files
|
||||
|
||||
When a config file is found (`lib/config-file.js`), it will call the `validate` function exported
|
||||
from `lib/schema/schema.js` with the contents of the config file. In turn, this asks `ajv` to
|
||||
validate the data against the schema that Appium has provided it.
|
||||
|
||||
If the config file is invalid, errors will be generated to be displayed to the user. Finally, the
|
||||
`init` function will detect these errors, display them, and the process will exit.
|
||||
|
||||
I hope that made sense, because this is the easy part.
|
||||
|
||||
### Validating CLI Arguments
|
||||
|
||||
As mentioned earlier, the same system is used for validating both config files and CLI arguments.
|
||||
|
||||
Totally not judging, but Appium uses [`argparse`](https://npm.im/argparse) for its CLI argument
|
||||
parsing. This package, and others like it, provides an API to define the arguments a command-line
|
||||
Node.js script accepts, and will ultimately return an object representation of the user-supplied
|
||||
arguments.
|
||||
|
||||
Just as the schema defines what's allowed in a config file, it also defines what's allowed on the
|
||||
command-line.
|
||||
|
||||
#### Defining CLI Arguments via Schema
|
||||
|
||||
CLI arguments must be _defined_ before their values can be validated.
|
||||
|
||||
A JSON schema isn't a natural fit for defining CLI args--it needs some grease to make it work--but
|
||||
it's close enough that we can do so with an adapter and some custom metadata.
|
||||
|
||||
In `lib/cli/parser.js`, there's a wrapper around `argparse`'s `ArgumentParser`; it's called (wait
|
||||
for it)... `ArgParser`. The wrapper exists because we're doing some custom things with `argparse`,
|
||||
but is has nothing to do with the schema directly.
|
||||
|
||||
An `ArgParser` instance is created and its `parseArgs()` method is called with the raw CLI
|
||||
arguments. The definition of the accepted arguments comes from `lib/cli/args.js` in part--here, all
|
||||
of the arguments _not_ intended for use with the `server` subcommand are hard-coded (e.g., the
|
||||
`driver` subcommand and _its_ subcommands). `args.js` also contains a function `getServerArgs()`,
|
||||
which in turn calls into `toParserArgs` in `lib/schema/cli-args.js`. `lib/schema/cli-args.js` can
|
||||
be considered the "adapter" layer between `argparse` and the schema.
|
||||
|
||||
`toParserArgs` uses the `flattenSchema` function exported by `lib/schema/schema.js`, which
|
||||
"squashes" the schema into a key/value representation. Then, `toParserArgs` iterates over each
|
||||
key/value pair and "converts" it into a suitable `ArgumentOption` object for final handoff to
|
||||
`ArgParser`.
|
||||
|
||||
This adapter (`cli-args.js`) is where most of the mess is hidden; let's explore this rat's nest
|
||||
a bit further.
|
||||
|
||||
##### CLI & Schema Incongruities
|
||||
|
||||
The conversion algorithm (see function `subSchemaToArgDef` in `lib/schema/cli-args.js`) is mostly
|
||||
just hacks and special cases neatly packed into a function. Things that don't cleanly map from
|
||||
`argparse` to a JSON schema include, but are not limited to:
|
||||
|
||||
- A schema cannot natively express "store the value of `--foo=<value>` in a property called `bar`" in a schema (this corresponds to the `ArgumentOption['dest']` prop).
|
||||
- A schema cannot natively express aliases; e.g., `--verbose` can also be `-v`
|
||||
- A schema `enum` is not restricted to multiple types, but `argparse`'s equivalent `ArgumentOption['choices']` prop _is_
|
||||
- A schema does not know about `argparse`'s concept of "actions" (note that Appium is not currently using custom actions--though it did, and it could again).
|
||||
- `argparse` has no native type for `email`, `hostname`, `ipv4`, `uri` etc., and the schema does
|
||||
- Schema validation only _validates_, it does not perform translation, transformation, or coercion (mostly). `argparse` allows this.
|
||||
- Schemas allow the `null` type, for whatever reason. Ever pass `null` on the CLI?
|
||||
- `argparse` does not understand anything other than primitives; no objects, arrays, etc., and certainly not arrays of a particular type.
|
||||
|
||||
All of the above cases and others are handled by the adapter.
|
||||
|
||||
!!! warning
|
||||
|
||||
Some decisions made in the adapter were arrived at via coin toss. If you are curious about why
|
||||
something is the way it is, it's likely that it had to do _something_.
|
||||
|
||||
Let's look more closely at handling types.
|
||||
|
||||
#### Argument Types via `ajv`
|
||||
|
||||
While `argparse` allows consumers, via its API, to define the _type_ of various arguments (e.g.,
|
||||
a string, number, boolean flag, etc.), Appium mostly avoids these built-in types. _Why is that?_
|
||||
Well:
|
||||
|
||||
1. We already know the type of an argument, because we've defined it in a schema.
|
||||
2. `ajv` provides validation against a schema.
|
||||
3. A schema allows for greater expression of types, allowed values, etc., than `argparse` can provide natively.
|
||||
4. The expressiveness of a schema allows for better error messaging.
|
||||
|
||||
To that end, the adapter eschews `argparse`'s built-in types (see allowed string values of
|
||||
`ArgumentOption['type']`) and instead abuses the ability to provide a _function_ as a `type`. The
|
||||
exception is _boolean_ flags, which do not have a `type`, but rather `action: 'store_true'`. The
|
||||
world may never know why.
|
||||
|
||||
##### Types as Functions
|
||||
|
||||
When a `type` is a function, the function performs both validation _and_ coercion (if necessary).
|
||||
So what are these functions?
|
||||
|
||||
> Note: `type` is _omitted_ (and thus _not_ a function) from the `ArgumentOption` if the property
|
||||
> type is `boolean`, and is instead provided an `action` property of `store_true`. Yes, this is
|
||||
> weird. No, I don't know why.
|
||||
|
||||
Well... it depends upon the schema. But generally speaking, we create a _pipeline_ of functions,
|
||||
each corresponding to a keyword in the schema. Let's take the example of the `port` argument. In
|
||||
lieu of asking the OS which ports the `appium`-running user can bind to, this argument is expected
|
||||
to be an integer between 1 and 65535. This turns out to be two functions which we combine into
|
||||
a pipeline:
|
||||
|
||||
1. Convert the value to an integer, if possible. Because _every value in `process.argv` is a string_, we must coerce if we want a number.
|
||||
2. Use `ajv` to validate the integer against the schema for `port`. A schema lets us define a range via the `minimum` and `maximum` keywords. Read more about how this works in
|
||||
|
||||
Much like the config file validation, if errors are detected, Appium nicely tells the end-user and
|
||||
the process exits w/ some help text.
|
||||
|
||||
For other arguments which are naturally of non-primitive types, things are not so straightforward.
|
||||
|
||||
##### Transformers
|
||||
|
||||
Remember how `argparse` doesn't understand arrays? What if the most ergonomic way to express
|
||||
a value is, in fact, an array?
|
||||
|
||||
Well, Appium can't accept an array on the CLI, even though it can accept one in the config file.
|
||||
But Appium _can_ accept a comma-delimited string (a CSV "line"). Or a string filepath referring to
|
||||
a file which _contains_ a delimited list. Either way: by the time the value gets out of the
|
||||
argument parser, it should be an array.
|
||||
|
||||
And as mentioned above, the native facilities of a JSON schema cannot express this. However, it's
|
||||
possible to define a _custom keyword_ which Appium can then detect and handle accordingly. So
|
||||
that's what Appium does.
|
||||
|
||||
In this case, a custom keyword `appiumCliTransformer` is registered with `ajv`. The value of
|
||||
`appiumCliTransformer` (at the time of this writing) can be `csv` or `json`. In the base schema
|
||||
file, `appium-config-schema.js`, Appium uses `appiumCliTransformer: 'csv'` if this behavior is
|
||||
desired.
|
||||
|
||||
!!! note
|
||||
|
||||
Any property defined in the schema having type `array` will _automatically_ uses the `csv`
|
||||
transformer. Likewise, a property having type `object` will use the `json` transformer. It's
|
||||
conceivable that `array` may want to use the `json` transformer, but otherwise, the presence of
|
||||
the `appiumCliTransformer` keyword on an `array`-or-`object`-typed property is not strictly
|
||||
necessary.
|
||||
|
||||
The adapter (remember the adapter?) creates a pipeline function including a special "CSV
|
||||
transformer" (transformers are defined in `lib/schema/cli-transformers.js`), and uses this function
|
||||
as the `type` property of the `ArgumentOption` passed into `argparse`. In this case, the `type:
|
||||
'array'` in the schema is ignored.
|
||||
|
||||
!!! note
|
||||
|
||||
The config file doesn't _need_ to perform any complex transformation of values, because it
|
||||
naturally allows Appium to define exactly what it expects. So Appium does no post-processing of
|
||||
config file values.
|
||||
|
||||
Properties that do not need this special treatment use `ajv` directly for validation. How this
|
||||
works requires some explanation, so that's next.
|
||||
|
||||
#### Validation of Individual Arguments via `ajv`
|
||||
|
||||
When we think of a JSON schema, we tend to think, "I have this JSON file and I want to validate it
|
||||
against the schema". That's valid, and in fact Appium does just that with config files! However,
|
||||
Appium does not do this when validating arguments.
|
||||
|
||||
!!! note
|
||||
|
||||
During implementation, I was tempted to mash all of the arguments together into
|
||||
a config-file-like data structure and then validate it all at once. I think that would have
|
||||
been _possible_, but since an object full of CLI arguments is a flat key/value structure and
|
||||
the schema is not, this seemed like trouble.
|
||||
|
||||
Instead, Appium validates a value against a specific property _within_ the schema. To do this, it
|
||||
maintains a mapping between a CLI argument definition and its corresponding property. The mapping
|
||||
itself is a `Map` with a unique identifier for the argument as the key, and an `ArgSpec`
|
||||
(`lib/schema/arg-spec.js`) object as the value.
|
||||
|
||||
An `ArgSpec` object stores the following metadata:
|
||||
|
||||
| Property Name | Description |
|
||||
| --------------- | ------------------------------------------------------------------------------------- |
|
||||
| `name` | Canonical name of the argument, corresponding to the property name in the schema. |
|
||||
| `extType?` | `driver` or `plugin`, if appropriate |
|
||||
| `extName?` | Extension name, if appropriate |
|
||||
| `ref` | Computed `$id` of the property in the schema |
|
||||
| `arg` | Argument as accepted on CLI, without leading dashes |
|
||||
| `dest` | Property name in parsed arguments object (as returned by `argparse`'s `parse_args()`) |
|
||||
| `defaultValue?` | Value of the `default` keyword in schema, if appropriate |
|
||||
|
||||
When a schema is [finalized](#schema-finalization), the `Map` is populated with `ArgSpec` objects
|
||||
for all known arguments.
|
||||
|
||||
So when the adapter is creating the pipeline of functions for the argument's `type`, it already has
|
||||
an `ArgSpec` for the argument. It creates a function which calls `validate(value, ref)` (in
|
||||
`lib/schema/schema.js`) where `value` is whatever the user provided, and `ref` is the `ref`
|
||||
property of the `ArgSpec`. The concept is that `ajv` can validate using _any_ `ref` it knows about;
|
||||
each property in a schema can be referenced by this `ref` whether it's defined or not. To help
|
||||
visualize, if a schema is:
|
||||
|
||||
```json
|
||||
{
|
||||
"$id": "my-schema.json",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"foo": {
|
||||
"type": "number"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The `ref` of `foo` would be `my-schema.json#/properties/foo`. Assuming our `Ajv` instance knows
|
||||
about this `my-schema.json`, then we can call its `getSchema(ref)` method (which has a `schema`
|
||||
property, but is a misnomer nonetheless) to get a validation function; `validate(value, ref)` in
|
||||
`schema.js` calls this validation function.
|
||||
|
||||
!!! note
|
||||
|
||||
The schema spec says a schema author can supply an explicit `$id` keyword to override this;
|
||||
it's unsupported by Appium at this time. If needed, extension authors must carefully use `$ref`
|
||||
without custom `$id`s. It's highly unlikely an extension would have a schema so complicated as
|
||||
to need this, however; Appium itself doesn't even use `$ref` to define its own properties!
|
||||
|
||||
Next, let's take a look at how Appium loads schemas. This actually happens _before_ any argument
|
||||
validation.
|
||||
|
||||
## Schema Loading
|
||||
|
||||
Let's ignore extensions for a moment, and start with the base schema.
|
||||
|
||||
When something first imports the `lib/schema/schema.js` module, an instance of an `AppiumSchema` is
|
||||
created. This is a singleton, and its methods are exported from the module (all of which are bound
|
||||
to the instance).
|
||||
|
||||
The constructor does very little; it instantiates an `Ajv` instance and configures it with Appium's
|
||||
[custom keywords](#custom-keyword-reference) and adds support for the `format` keyword via the
|
||||
[ajv-formats](https://npm.im/ajv-formats) module.
|
||||
|
||||
Otherwise, the `AppiumSchema` instance does not interact with the `Ajv` instance until its
|
||||
`finalize()` method (exported as `finalizeSchema()`) is called. When this method is called, we're
|
||||
saying "we are not going to add any more schemas; go ahead and create `ArgSpec` objects and
|
||||
register schemas with `ajv`".
|
||||
|
||||
When does finalization happen? Well:
|
||||
|
||||
1. When the `appium` executable begins, it _checks for and configures extensions_ (hand-wave) in `APPIUM_HOME`.
|
||||
2. Only then does it start to think about arguments--it instantiates an `ArgParser`, which (as you'll recall) runs the adapter to convert the schema to arguments.
|
||||
3. _Finalization happens here_--when creating the parser. Appium need the schema(s) to be registered with `ajv` in order to create validation functions for arguments.
|
||||
4. Thereafter, Appium parses the arguments with the `ArgParser`.
|
||||
5. Finally, decides what to do with the returned object.
|
||||
|
||||
Without extensions, `finalize()` still knows about the Appium base schema
|
||||
(`appium-config-schema.js`), and just registers that. However, step 1. above is doing a _lot of
|
||||
work_, so let's look at how extensions come into play.
|
||||
|
||||
## Extension Support
|
||||
|
||||
One of the design goals of this system is the following:
|
||||
|
||||
_An extension should be able to register custom CLI arguments with the Appium, and a user should be
|
||||
able to use them like any other argument_.
|
||||
|
||||
Previously, Appium 2 accepted arguments in this manner (via `--driverArgs`), but validation was
|
||||
hand-rolled and required extension implementors to use a custom API. It also required the user to
|
||||
awkwardly pass a JSON string as the configuration on the command-line. Further, no contextual help
|
||||
(via `--help`) existed for these arguments.
|
||||
|
||||
Now, by providing a schema for its options, a driver or plugin can register CLI arguments and
|
||||
config file schemas with Appium.
|
||||
|
||||
To register a schema, an extension must provide the `appium.schema` property in its `package.json`.
|
||||
The value may be a schema or a path to a schema. If the latter, the schema should be JSON or
|
||||
a CommonJS module (ESM not supported at this time, nor is YAML).
|
||||
|
||||
For any property in this schema, the property will appear as a CLI argument of the form
|
||||
`--<extension-type>-<extension-name>-<property-name>`. For example, if the `fake` driver provides
|
||||
a property `foo`, the argument will be `--driver-fake-foo`, and will show in `appium server --help`
|
||||
like any other CLI argument.
|
||||
|
||||
The corresponding property in a config file would be
|
||||
`server.<extension-type>.<extension-name>.<property-name>`, e.g.:
|
||||
|
||||
```json
|
||||
{
|
||||
"server": {
|
||||
"driver": {
|
||||
"fake": {
|
||||
"foo": "bar"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The naming convention described above avoids problems of one extension type having a name conflict
|
||||
with a different extension type.
|
||||
|
||||
!!! note
|
||||
|
||||
While an extension can provide aliases via `appiumCliAliases`, "short" flags are disallowed,
|
||||
since all arguments from extensions are prefixed with `--<extension-type>-<extension-name>-`.
|
||||
The extension name and argument name will be kebab-cased for the CLI, according to [Lodash's
|
||||
rules](https://lodash.com/docs/4.17.15#kebabCase) around kebab-casing.
|
||||
|
||||
The schema object will look much like Appium's base schema, but it will only have top-level
|
||||
properties (nested properties are currently unsupported). Example:
|
||||
|
||||
```json
|
||||
{
|
||||
"title": "my rad schema for the cowabunga driver",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"fizz": {
|
||||
"type": "string",
|
||||
"default": "buzz",
|
||||
"$comment": "corresponds to CLI --driver-cowabunga-fizz"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
As written in a user's config file, this would be the `server.driver.cowabunga.fizz` property.
|
||||
|
||||
When extensions are loaded, the `schema` property is verified and the schema is registered with the
|
||||
`AppiumSchema` (it is _not_ registered with `Ajv` until `finalize()` is called).
|
||||
|
||||
During finalization, each registered schema is added to the `Ajv` instance. The schema is assigned
|
||||
an `$id` based on the extension type and name (which overrides whatever the extension provides, if
|
||||
anything). Schemas are also forced to disallowed unknown arguments via the `additionalProperties:
|
||||
false` keyword.
|
||||
|
||||
Behind the scenes, the base schema has `driver` and `plugin` properties which are objects. When
|
||||
finalized, a property is added to each--corresponding to an extension name--and the value of this
|
||||
property is a reference to the `$id` of a property in the extension schema. For example, the
|
||||
`server.driver` property will look like this:
|
||||
|
||||
```json
|
||||
{
|
||||
"driver": {
|
||||
"cowabunga": {
|
||||
"$ref": "driver-cowabunga.json"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This is why we call it the "base" schema--it is _mutated_ when extensions provide schemas. The
|
||||
extension schemas are kept separately, but the _references_ are added to the schema before it's
|
||||
ultimately added to `ajv`. This works because an `Ajv` instance understands references _from_ any
|
||||
schema it knows about _to_ any schema it knows about.
|
||||
|
||||
!!! note
|
||||
|
||||
This makes it impossible to provide a complete static schema for Appium _and_ the installed
|
||||
extensions (as of Nov 5 2021). A static `.json` schema _is_ generated from the base (via a Gulp
|
||||
task), but it does not contain any extension schemas. The static schema also has uses beyond
|
||||
Appium; e.g., IDEs can provide contextual error-checking of config files this way. Let's solve
|
||||
this?
|
||||
|
||||
Just like how we look up the reference ID of a particular argument in the base schema, validation
|
||||
of arguments from extensions happens the exact same way. If the `cowabunga` driver has the schema
|
||||
ID `driver-cowabunga.json`, then the `fizz` property can be referenced from any schema registered
|
||||
with `ajv` via `driver-cowabunga.json#/properties/fizz`. "Base" schema arguments begin with
|
||||
`appium.json#properties/` instead.
|
||||
|
||||
## Development Environment Support
|
||||
|
||||
During the flow of development, a couple extra tasks have been automated to maintain the base
|
||||
schema:
|
||||
|
||||
- As a post-transpilation step, a `lib/appium-config.schema.json` gets generated from
|
||||
- `lib/schema/appium-config-schema.js` (in addition to its CJS counterpart generated by Babel).
|
||||
- This file is under version control. It ends up being _copied_ to
|
||||
- `build/lib/appium-config.schema.json` in this step. A pre-commit hook (see
|
||||
- `scripts/generate-schema-declarations.js` in the root monorepo) generates
|
||||
- a `types/appium-config-schema.d.ts` from the above JSON file. The types in `types/types.d.ts`
|
||||
- depend upon this file. This file is under version control.
|
||||
|
||||
## Custom Keyword Reference
|
||||
|
||||
Keywords are defined in `lib/schema/keywords.js`.
|
||||
|
||||
- `appiumCliAliases`: allows a schema to express aliases (e.g., a CLI argument can be `--verbose` or `-v`). This is an array of strings. Strings shorter than three (3) characters will begin with a single dash (`-`) instead of a double-dash (`--`). Note that any argument provided by an extension will begin with a double-dash, because these are required to have the `--<extension-type>-<extension-name>-` prefix.
|
||||
- `appiumCliDest`: allows a schema to specify a custom property name in the post-`argprase` arguments objects. If not set, this becomes a camelCased string.
|
||||
- `appiumCliDescription`: allows a schema to override the description of the argument when displayed on the command-line. This is useful paired with `appiumCliTransformer` (or `array`/`object`-typed properties), since there's a substantial difference between what a CLI-using user can provide vs. what a config-file-using user can provide.
|
||||
- `appiumCliTransformer`: currently a choice between `csv` and `json`. These are custom functions which post-process a value. They are not used when loading & validating config files, but the idea should be that they result in the same object you'd get if you used whatever the config file wanted (e.g., an array of strings). `csv` is for comma-delimited strings and CSV files; `json` is for raw JSON strings and `.json` files.
|
||||
- `appiumCliIgnore`: If `true`, do not support this property on the CLI.
|
||||
- `appiumDeprecated`: If `true`, the property is considered "deprecated", and will be displayed as such to the user (e.g., in the `--help` output). Note the JSON Schema draft-2019-09 introduces a new keyword `deprecated` which we should use instead if upgrading to this metaschema. When doing so, `appiumDeprecated` should itself be marked as `deprecated`.
|
||||
@@ -1,18 +0,0 @@
|
||||
---
|
||||
hide:
|
||||
- toc
|
||||
|
||||
title: Intro to Development
|
||||
---
|
||||
|
||||
Appium 2 is built with a modular structure, which means that Appium extensions (drivers and plugins)
|
||||
are decoupled from the main Appium module, and you only need to install the extensions that you
|
||||
want to use. This modular structure also unlocks the ability to develop entirely new extensions!
|
||||
|
||||
This section of the Appium documentation is intended to help aspiring developers with creating their
|
||||
own Appium extension:
|
||||
|
||||
* For creating a driver, see the [Build Drivers](./build-drivers.md) page
|
||||
* For creating a plugin, take a look at the [Build Plugins](build-plugins.md) page
|
||||
* Drivers and plugins both need documentation, so check out the [Build Documentation](./build-docs.md) page
|
||||
* For creating a doctor check, see the [Building Doctor Checks](./build-doctor-checks.md) page
|
||||
@@ -1,49 +0,0 @@
|
||||
---
|
||||
hide:
|
||||
- toc
|
||||
|
||||
title: Masking Sensitive Log Data
|
||||
---
|
||||
|
||||
Since Appium server version 2.18.0 there is a possibility to mask sensitive
|
||||
values in logs. The below tutorial explains how to use this feature in third-party
|
||||
extensions.
|
||||
|
||||
## Why It Might Be Useful
|
||||
|
||||
It is the right call to hide sensitive information, like passwords, tokens, etc.
|
||||
from server logs, so it does not accidentally leak if these logs end up in wrong hands.
|
||||
Appium server already provides a way to manipulate logs records via
|
||||
[filtering](../guides/log-filters.md), although it has its own limitations.
|
||||
The current approach is more sophisticated though, and requires some fine-tuning
|
||||
on the driver/plugin side.
|
||||
|
||||
## How To
|
||||
|
||||
The assumption is that your extension uses the standard built-in
|
||||
[@appium/logger](https://www.npmjs.com/package/@appium/logger).
|
||||
In order to get some value in logs replaced by a generic mask it is necessary:
|
||||
|
||||
- Change the logging expression to wrap sensitive values and format them, for example:
|
||||
|
||||
```js
|
||||
this.log.info(`Value: ${value}`);
|
||||
```
|
||||
|
||||
becomes
|
||||
|
||||
```js
|
||||
import {logger} from '@appium/support';
|
||||
|
||||
this.log.info('Value: %s', logger.markSensitive(value));
|
||||
```
|
||||
|
||||
The formatting happens via the standard Node.js's
|
||||
[util.format](https://nodejs.org/api/util.html#utilformatformat-args) API.
|
||||
|
||||
- While sending the appropriate server request, where this log line is used and should be masked,
|
||||
add the custom header `X-Appium-Is-Sensitive` with its value set to `1` or `true` (case-insensitive).
|
||||
Without such header the above log value is not going to be masked.
|
||||
This way it is possible to conditionally mask log records depending on which
|
||||
request is being handled by the extension if the log expression is used in the
|
||||
common section.
|
||||
@@ -1,43 +0,0 @@
|
||||
---
|
||||
hide:
|
||||
- toc
|
||||
|
||||
title: Appium Clients
|
||||
---
|
||||
|
||||
You need a client to write and run Appium scripts. You'll want to become very
|
||||
familiar with your client documentation (as well as the documentation of any Selenium client that
|
||||
the Appium client depends on) since that will be your primary interface to Appium.
|
||||
|
||||
To learn more about clients, read our [Client Intro](../intro/clients.md).
|
||||
|
||||
### Official Clients
|
||||
|
||||
These clients are currently maintained by the Appium team:
|
||||
|
||||
|Client|Language|
|
||||
|-|-|
|
||||
|[Appium Java client](https://github.com/appium/java-client)|Java|
|
||||
|[Appium Python client](https://github.com/appium/python-client)|Python|
|
||||
|[Appium Ruby Core client](https://github.com/appium/ruby_lib_core) (Recommended)<br>[Appium Ruby client](https://github.com/appium/ruby_lib)|Ruby|
|
||||
|[Appium .NET client](https://github.com/appium/dotnet-client)|C#|
|
||||
|
||||
### Other Clients
|
||||
|
||||
These clients are not maintained by the Appium team and can be used with other languages:
|
||||
|
||||
|Client|Language|
|
||||
|-|-|
|
||||
|[WebdriverIO](https://webdriver.io/docs/appium)|Node.js|
|
||||
|[Nightwatch.js](https://nightwatchjs.org/guide/mobile-app-testing/introduction.html)|Node.js|
|
||||
|[RobotFramework](https://github.com/serhatbolsu/robotframework-appiumlibrary)|DSL|
|
||||
|[multicatch's appium-client](https://github.com/multicatch/appium-client)|Rust|
|
||||
|[SwiftAppium](https://github.com/milcgroup/swiftappium)|Swift|
|
||||
|
||||
In general, any W3C WebDriver spec-compatible client will also integrate well with Appium, though
|
||||
some Appium-specific commands may not be implemented in other clients.
|
||||
|
||||
!!! note
|
||||
|
||||
If you maintain an Appium client that you would like to be listed in the Appium docs, feel free
|
||||
to make a PR to add it to this section with a link to the documentation for the client.
|
||||
@@ -1,52 +0,0 @@
|
||||
---
|
||||
hide:
|
||||
- toc
|
||||
|
||||
title: Appium Drivers
|
||||
---
|
||||
|
||||
You can't use Appium without at least one driver! Click on the link for each driver to see the
|
||||
specific installation instructions and documentation for that driver.
|
||||
|
||||
Generally, drivers can be installed using their listed installation key, with the following command:
|
||||
```
|
||||
appium driver install <installation key>
|
||||
```
|
||||
|
||||
To learn more about drivers, check out the [Driver Intro](../intro/drivers.md).
|
||||
|
||||
### Official Drivers
|
||||
|
||||
These drivers are currently maintained by the Appium team:
|
||||
|
||||
|Driver|Installation Key|Platform(s)|Mode(s)|
|
||||
|--|--|--|--|
|
||||
|[Chromium](https://github.com/appium/appium-chromium-driver)|`chromium`|macOS, Windows, Linux|Web|
|
||||
|[Espresso](https://github.com/appium/appium-espresso-driver)|`espresso`|Android|Native|
|
||||
|[Gecko](https://github.com/appium/appium-geckodriver)|`gecko`|macOS, Windows, Linux, Android|Web|
|
||||
|[Mac2](https://github.com/appium/appium-mac2-driver)|`mac2`|macOS|Native|
|
||||
|[Safari](https://github.com/appium/appium-safari-driver)|`safari`|macOS, iOS|Web|
|
||||
|[UiAutomator2](https://github.com/appium/appium-uiautomator2-driver)|`uiautomator2`|Android|Native, Hybrid, Web|
|
||||
|[Windows](https://github.com/appium/appium-windows-driver)|`windows`|Windows|Native|
|
||||
|[XCUITest](https://github.com/appium/appium-xcuitest-driver)|`xcuitest`|iOS|Native, Hybrid, Web|
|
||||
|
||||
### Other Drivers
|
||||
|
||||
These drivers are not maintained by the Appium team and can be used to target additional platforms:
|
||||
|
||||
|Driver|Installation Key|Platform(s)|Mode(s)|Supported By|
|
||||
|--|--|--|--|--|
|
||||
|[Flutter](https://github.com/appium/appium-flutter-driver)|`--source=npm appium-flutter-driver`|iOS, Android|Native|Community|
|
||||
|[Flutter Integration](https://github.com/AppiumTestDistribution/appium-flutter-integration-driver)|`--source=npm appium-flutter-integration-driver`|iOS, Android|Native|Community / `@AppiumTestDistribution`|
|
||||
|[LG WebOS](https://github.com/headspinio/appium-lg-webos-driver)|`--source=npm appium-lg-webos-driver`|LG TV|Web|HeadSpin|
|
||||
|[Linux](https://github.com/fantonglang/appium-linux-driver)|`--source=npm @stdspa/appium-linux-driver`|Linux|Native|`@fantonglang`|
|
||||
|[Roku](https://github.com/headspinio/appium-roku-driver)|`--source=npm @headspinio/appium-roku-driver`|Roku|Native|HeadSpin|
|
||||
|[Tizen](https://github.com/Samsung/appium-tizen-driver)|`--source=npm appium-tizen-driver`|Android|Native|Community / Samsung|
|
||||
|[TizenTV](https://github.com/headspinio/appium-tizen-tv-driver)|`--source=npm appium-tizen-tv-driver`|Samsung TV|Web|HeadSpin|
|
||||
|[Youi](https://github.com/YOU-i-Labs/appium-youiengine-driver)|`--source=npm appium-youiengine-driver`|iOS, Android, macOS, Linux, tvOS|Native|Community / You.i|
|
||||
|[NovaWindows](https://github.com/AutomateThePlanet/appium-novawindows-driver)|`--source=npm appium-novawindows-driver`|Windows|Native|Community / Automate The Planet|
|
||||
|
||||
!!! note
|
||||
|
||||
If you maintain an Appium driver that you would like to be listed in the Appium docs, feel free
|
||||
to make a PR to add it to this section with a link to the driver documentation.
|
||||
@@ -1,45 +0,0 @@
|
||||
---
|
||||
hide:
|
||||
- toc
|
||||
|
||||
title: Ecosystem Overview
|
||||
---
|
||||
|
||||
Appium has a wide ecosystem of related software and tools. This section of the Appium documentation
|
||||
aims to compile a listing of various officially-supported and community-supported Appium projects:
|
||||
|
||||
<div class="grid cards" markdown>
|
||||
|
||||
- :material-car: __Drivers__
|
||||
|
||||
---
|
||||
|
||||
Link Appium to your test device
|
||||
|
||||
[:octicons-arrow-right-24: View all drivers](./drivers.md)
|
||||
|
||||
- :octicons-code-16: __Clients__
|
||||
|
||||
---
|
||||
|
||||
Link Appium to your automation code
|
||||
|
||||
[:octicons-arrow-right-24: View all clients](./clients.md)
|
||||
|
||||
- :fontawesome-solid-plug: __Plugins__
|
||||
|
||||
---
|
||||
|
||||
Modify and extend Appium functionality
|
||||
|
||||
[:octicons-arrow-right-24: View all plugins](./plugins.md)
|
||||
|
||||
- :material-wrench: __Tools__
|
||||
|
||||
---
|
||||
|
||||
Interact with Appium in other ways
|
||||
|
||||
[:octicons-arrow-right-24: View all tools](./tools.md)
|
||||
|
||||
</div>
|
||||
@@ -1,46 +0,0 @@
|
||||
---
|
||||
hide:
|
||||
- toc
|
||||
|
||||
title: Appium Plugins
|
||||
---
|
||||
|
||||
Plugins offer various ways to extend or modify Appium's behaviour. They are _completely optional_
|
||||
and are not needed for standard automation functionality, but you may find them to be useful
|
||||
for more specialised automation workflows.
|
||||
|
||||
Generally, plugins can be installed using their listed installation key, with the following command:
|
||||
```
|
||||
appium plugin install <installation key>
|
||||
```
|
||||
|
||||
### Official Plugins
|
||||
|
||||
These plugins are are currently maintained by the Appium team:
|
||||
|
||||
|<div style="width:7em">Plugin</div>|<div style="width:8em">Installation Key</div>|Description|
|
||||
|---|---|---|
|
||||
|[Execute Driver](https://github.com/appium/appium/tree/master/packages/execute-driver-plugin)|`execute-driver`|Run entire batches of commands in a single call to the Appium server|
|
||||
|[Images](https://github.com/appium/appium/tree/master/packages/images-plugin)|`images`|Image matching and comparison features|
|
||||
|[Relaxed Caps](https://github.com/appium/appium/tree/master/packages/relaxed-caps-plugin)|`relaxed-caps`|Relax Appium's requirement for vendor prefixes on capabilities|
|
||||
|[Storage](https://github.com/appium/appium/tree/master/packages/storage-plugin)|`storage`|Server-side storage with client-side management|
|
||||
|[Universal XML](https://github.com/appium/appium/tree/master/packages/universal-xml-plugin)|`universal-xml`|Instead of the standard XML format for iOS and Android, use an XML definition that is the same across both platforms|
|
||||
|
||||
### Other Plugins
|
||||
|
||||
These plugins are not maintained by the Appium team and can provide additional functionality:
|
||||
|
||||
|<div style="width:6em">Plugin</div>|<div style="width:19em">Installation Key</div>|Description|<div style="width:13em">Supported By</div>|
|
||||
|---|---|---|---|
|
||||
|[AltUnity](https://github.com/headspinio/appium-altunity-plugin)|`--source=npm appium-altunity-plugin`|Target Unity games and apps for automation with a new context, via the AltUnityTester framework|HeadSpin|
|
||||
|[Device Farm](https://github.com/AppiumTestDistribution/appium-device-farm)|`--source=npm appium-device-farm`|Manage and create driver sessions on connected Android devices and iOS simulators|`@AppiumTestDistribution`|
|
||||
|[Gestures](https://github.com/AppiumTestDistribution/appium-gestures-plugin)|`--source=npm appium-gestures-plugin`|Perform basic gestures using W3C Actions|`@AppiumTestDistribution`|
|
||||
|[Interceptor](https://github.com/AppiumTestDistribution/appium-interceptor-plugin)|`--source=npm appium-interceptor`|Intercept and mock API requests and responses|`@AppiumTestDistribution`|
|
||||
|[OCR](https://github.com/jlipps/appium-ocr-plugin)|`--source=npm appium-ocr-plugin`|Find elements via OCR text|`@jlipps`|
|
||||
|[Reporter](https://github.com/AppiumTestDistribution/appium-reporter-plugin)|`--source=npm appium-reporter-plugin`|Generate standalone consolidated HTML reports with screenshots|`@AppiumTestDistribution`|
|
||||
|[Wait](https://github.com/AppiumTestDistribution/appium-wait-plugin)|`--source=npm appium-wait-plugin`|Manage global element wait timeouts|`@AppiumTestDistribution`|
|
||||
|
||||
!!! note
|
||||
|
||||
If you maintain an Appium plugin that you would like to be listed in the Appium docs, feel free
|
||||
to make a PR to add it to this section with a link to the documentation for the plugin.
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user