Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e06729c114 | ||
|
|
9fc35d3ba7 | ||
|
|
7ca35a3373 | ||
|
|
704fec138d |
@@ -1,10 +0,0 @@
|
||||
<!-- CODEGRAPH_START -->
|
||||
## CodeGraph
|
||||
|
||||
In repositories indexed by CodeGraph (a `.codegraph/` directory exists at the repo root), reach for it BEFORE grep/find or reading files when you need to understand or locate code:
|
||||
|
||||
- **MCP tool** (when available): `codegraph_explore` answers most code questions in one call — the relevant symbols' verbatim source plus the call paths between them, including dynamic-dispatch hops grep can't follow. Name a file or symbol in the query to read its current line-numbered source. If it's listed but deferred, load it by name via tool search.
|
||||
- **Shell** (always works): `codegraph explore "<symbol names or question>"` prints the same output.
|
||||
|
||||
If there is no `.codegraph/` directory, skip CodeGraph entirely — indexing is the user's decision.
|
||||
<!-- CODEGRAPH_END -->
|
||||
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"permissions": {
|
||||
"allow": ["mcp__codegraph__*"]
|
||||
},
|
||||
"hooks": {
|
||||
"UserPromptSubmit": [
|
||||
{
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "codegraph prompt-hook"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
# CodeGraph data files — local to each machine, not for committing.
|
||||
# Ignore everything in .codegraph/ except this file itself, so transient
|
||||
# files (the database, daemon.pid, sockets, logs) never show up in git.
|
||||
*
|
||||
!.gitignore
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"mcpServers": {
|
||||
"codegraph": {
|
||||
"type": "stdio",
|
||||
"command": "codegraph",
|
||||
"args": ["serve", "--mcp", "--path", "."]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +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
|
||||
max_line_length = 120
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
@@ -0,0 +1,2 @@
|
||||
sample-code
|
||||
coverage
|
||||
@@ -1,52 +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/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
|
||||
@@ -0,0 +1,32 @@
|
||||
## The problem
|
||||
|
||||
Briefly describe the issue you are experiencing (or the feature you want to see added to Appium). Tell us what you were trying to do and what happened instead. Remember, this is _not_ a place to ask questions. For that, go to http://discuss.appium.io!
|
||||
|
||||
## Environment
|
||||
|
||||
* Appium version (or git revision) that exhibits the issue:
|
||||
* Last Appium version that did not exhibit the issue (if applicable):
|
||||
* Desktop OS/version used to run Appium:
|
||||
* Node.js version (unless using Appium.app|exe):
|
||||
* Mobile platform/version under test:
|
||||
* Real device or emulator/simulator:
|
||||
* Appium CLI or Appium.app|exe:
|
||||
|
||||
## Details
|
||||
|
||||
If necessary, describe the problem you have been experiencing in more detail.
|
||||
|
||||
## Link to Appium logs
|
||||
|
||||
Create a [GIST](https://gist.github.com) which is a paste of your _full_ Appium logs, and link them here.
|
||||
Do _NOT_ paste your full Appium logs here, as it will make this issue very long and hard to read!
|
||||
If you are reporting a bug, _always_ include Appium logs!
|
||||
|
||||
|
||||
## Code To Reproduce Issue [ Good To Have ]
|
||||
|
||||
Please remember that with sample code it's easier to reproduce the bug and it's much faster to fix it.
|
||||
|
||||
Please git clone https://github.com/appium/sample-code and use one of your favourite languages and sample apps to reproduce the issue.
|
||||
|
||||
In case a similar scenario is missing in sample-code, please submit a PR with one of the sample apps provided.
|
||||
@@ -1,110 +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,32 +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
|
||||
@@ -10,17 +10,16 @@ _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
|
||||
- [ ] I have read the [CONTRIBUTING](/CONTRIBUTING.md) doc
|
||||
- [ ] I have signed the [CLA](https://cla.js.foundation/appium/appium)
|
||||
- [ ] 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)
|
||||
- [ ] I have added necessary documentation (if appropriate)
|
||||
- [ ] Any dependent changes have been merged and published in downstream modules
|
||||
|
||||
## Further comments
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
name: Setup Node, Install, Build
|
||||
|
||||
description: |
|
||||
Common setup for Appium CI jobs: checkout, setup Node, install deps, optional build, optional V8 cache clear.
|
||||
|
||||
inputs:
|
||||
node-version:
|
||||
description: Node.js version to setup
|
||||
required: true
|
||||
install-command:
|
||||
description: npm install command
|
||||
required: false
|
||||
default: npm ci --foreground-scripts
|
||||
run-build:
|
||||
description: Whether to run npm run build
|
||||
required: false
|
||||
default: 'true'
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Use Node.js ${{ inputs.node-version }}
|
||||
uses: actions/setup-node@v7.0.0
|
||||
with:
|
||||
node-version: ${{ inputs.node-version }}
|
||||
cache: npm
|
||||
- name: SocketDev Security Check
|
||||
uses: SocketDev/action@v1.3.2
|
||||
with:
|
||||
mode: firewall-free
|
||||
- name: Install dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
# Replace npm with sfw npm in the install command
|
||||
install_cmd="${{ inputs.install-command }}"
|
||||
install_cmd="${install_cmd//npm /sfw npm }"
|
||||
$install_cmd
|
||||
- name: Build packages
|
||||
if: ${{ inputs.run-build == 'true' }}
|
||||
shell: bash
|
||||
run: npm run build
|
||||
@@ -1,9 +0,0 @@
|
||||
# Copilot Instructions
|
||||
|
||||
## Code Review
|
||||
|
||||
When reviewing JavaScript or TypeScript changes, check new uses of Node.js built-in APIs against the supported Node.js versions declared in package.json#engines.node. The current supported range is ^20.19.0 || ^22.12.0 || >=24.0.0.
|
||||
|
||||
For each new Node.js built-in API or option, verify that it exists and is stable across all supported Node.js versions. If any supported version lacks the API, only supports it experimentally, or has different behavior, leave a review comment and suggest using the existing dependency, adding a fallback, avoiding the API, or raising the engine requirement.
|
||||
|
||||
Example: util.styleText exists in Node.js 20.12+, but only became stable in Node.js 22.13.0 and 23.5.0. Because this repository supports Node.js 20.19.0 and 22.12.0, replacing a color library with util.styleText should be flagged unless the project explicitly accepts relying on a non-stable API for those supported versions.
|
||||
@@ -1,154 +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/oxc-config'
|
||||
sync: true
|
||||
matcher:
|
||||
files: ['packages/oxc-config/**']
|
||||
|
||||
- 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/semantic-release-config'
|
||||
sync: true
|
||||
matcher:
|
||||
files: ['packages/semantic-release-config/**']
|
||||
|
||||
- label: '@appium/support'
|
||||
sync: true
|
||||
matcher:
|
||||
files: ['packages/support/**']
|
||||
|
||||
- label: '@appium/strongbox'
|
||||
sync: true
|
||||
matcher:
|
||||
files: ['packages/strongbox/**']
|
||||
|
||||
- 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@v5.0.0
|
||||
@@ -1,199 +0,0 @@
|
||||
name: Appium Build
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
# this list of paths could potentially impact the build
|
||||
paths:
|
||||
- '**/tsconfig*.json'
|
||||
- 'packages/base-driver/test-fixtures/**/*'
|
||||
- 'packages/*/test/**'
|
||||
- 'packages/*/bin'
|
||||
- 'packages/**/*.ts'
|
||||
- 'packages/**/*.json'
|
||||
- 'packages/**/*.js'
|
||||
- 'package*.json'
|
||||
- 'lerna.json'
|
||||
- '.npmrc'
|
||||
- '.github/workflows/build.yml' # this file
|
||||
- 'oxlint.config.mjs'
|
||||
- 'oxfmt.config.mjs'
|
||||
- '!**/sample-code/**'
|
||||
- '!packages/*/docs/**'
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- '**/tsconfig*.json'
|
||||
- 'packages/base-driver/test-fixtures/**/*'
|
||||
- 'packages/*/test/**'
|
||||
- 'packages/*/bin'
|
||||
- 'packages/**/*.ts'
|
||||
- 'packages/**/*.json'
|
||||
- 'packages/**/*.js'
|
||||
- 'package*.json'
|
||||
- 'lerna.json'
|
||||
- '.npmrc'
|
||||
- '.github/workflows/build.yml' # this file
|
||||
- 'oxlint.config.mjs'
|
||||
- 'oxfmt.config.mjs'
|
||||
- '!**/sample-code/**'
|
||||
- '!packages/*/docs/**'
|
||||
|
||||
env:
|
||||
CI: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
jobs:
|
||||
prepare_matrix:
|
||||
uses: appium/appium-workflows/.github/workflows/node-lts-matrix.yml@main
|
||||
|
||||
prepare_e2e_packages:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
packages: ${{ steps.discover-packages.outputs.packages }}
|
||||
steps:
|
||||
- uses: actions/checkout@v7.0.1
|
||||
- name: Discover packages with e2e tests
|
||||
id: discover-packages
|
||||
run: |
|
||||
packages=()
|
||||
for package_dir in packages/*/; do
|
||||
package_json="${package_dir}package.json"
|
||||
if [[ -f "$package_json" ]]; then
|
||||
# Check if package.json has test:e2e script and it's not a no-op
|
||||
has_e2e=$(node -e "
|
||||
try {
|
||||
const pkg = require('./$package_json');
|
||||
const script = pkg.scripts && pkg.scripts['test:e2e'];
|
||||
if (script && !script.includes('echo') && !script.includes('No e2e tests')) {
|
||||
console.log('true');
|
||||
}
|
||||
} catch(e) {
|
||||
// Ignore errors
|
||||
}
|
||||
")
|
||||
if [[ "$has_e2e" == "true" ]]; then
|
||||
# Use the directory path without packages/ prefix (remove trailing slash)
|
||||
package_name="${package_dir%/}"
|
||||
package_name="${package_name#packages/}"
|
||||
packages+=("$package_name")
|
||||
fi
|
||||
fi
|
||||
done
|
||||
# Convert array to JSON array for GitHub Actions matrix
|
||||
if [[ ${#packages[@]} -eq 0 ]]; then
|
||||
echo "No packages with e2e tests found"
|
||||
packages_json="[]"
|
||||
else
|
||||
# Use jq to create a compact single-line JSON array
|
||||
packages_json=$(printf '%s\n' "${packages[@]}" | jq -R '.' | jq -s '.' | jq -c '.')
|
||||
fi
|
||||
echo "packages=$packages_json" >> $GITHUB_OUTPUT
|
||||
echo "Found packages with e2e tests:"
|
||||
echo "$packages_json" | jq -r '.[]' | sed 's/^/ - /'
|
||||
|
||||
test-smoke:
|
||||
needs:
|
||||
- prepare_matrix
|
||||
name: Smoke Tests
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: ${{ fromJSON(needs.prepare_matrix.outputs.versions) }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v7.0.1
|
||||
- name: Setup, install and build
|
||||
uses: ./.github/actions/setup-build
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
install-command: npm ci --foreground-scripts
|
||||
run-build: 'true'
|
||||
- name: Run smoke tests
|
||||
run: npm run test:smoke
|
||||
|
||||
test-unit:
|
||||
needs:
|
||||
- prepare_matrix
|
||||
name: Unit Tests
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: ${{ fromJSON(needs.prepare_matrix.outputs.versions) }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v7.0.1
|
||||
- name: Setup, install and build
|
||||
uses: ./.github/actions/setup-build
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
install-command: npm ci --foreground-scripts
|
||||
run-build: 'true'
|
||||
- name: Run unit tests
|
||||
run: npm run test:unit
|
||||
|
||||
test-types:
|
||||
needs:
|
||||
- prepare_matrix
|
||||
name: Type Tests
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: ${{ fromJSON(needs.prepare_matrix.outputs.versions) }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v7.0.1
|
||||
- name: Setup, install and build
|
||||
uses: ./.github/actions/setup-build
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
install-command: npm ci --foreground-scripts
|
||||
run-build: 'true'
|
||||
- name: Run type tests
|
||||
run: npm run test:types
|
||||
|
||||
test-e2e:
|
||||
needs:
|
||||
- prepare_matrix
|
||||
- prepare_e2e_packages
|
||||
name: E2E Tests - ${{ matrix.package_dir }} (Node ${{ matrix.node-version }})
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
node-version: ${{ fromJSON(needs.prepare_matrix.outputs.versions) }}
|
||||
package_dir: ${{ fromJSON(needs.prepare_e2e_packages.outputs.packages) }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v7.0.1
|
||||
- name: Setup, install and build
|
||||
uses: ./.github/actions/setup-build
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
install-command: npm ci --foreground-scripts
|
||||
run-build: 'true'
|
||||
- name: Run E2E tests for ${{ matrix.package_dir }}
|
||||
run: |
|
||||
set -o pipefail
|
||||
cd "packages/${{ matrix.package_dir }}"
|
||||
npm run test:e2e
|
||||
|
||||
lint:
|
||||
name: Lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v7.0.1
|
||||
- name: Setup and install (no build)
|
||||
uses: ./.github/actions/setup-build
|
||||
with:
|
||||
node-version: lts/*
|
||||
install-command: npm ci
|
||||
run-build: 'false'
|
||||
- name: Lint
|
||||
run: npm run lint:ci
|
||||
- name: Format check
|
||||
run: npm run format:check
|
||||
@@ -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@v7.0.1
|
||||
|
||||
- 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,44 +0,0 @@
|
||||
# Collects translation statistics from Crowdin for the last calendar month
|
||||
# and posts them to Slack grouped by project, language, and translator
|
||||
|
||||
name: Crowdin Translation Statistics
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
# Run on the first day of each month at 9:00 AM UTC
|
||||
- cron: 0 9 1 * *
|
||||
|
||||
jobs:
|
||||
collect-stats:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- project_id_var: CROWDIN_DOCS_PROJECT_ID
|
||||
project_name: appium-documentation
|
||||
- project_id_var: CROWDIN_INSPECTOR_PROJECT_ID
|
||||
project_name: appium-inspector
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v7.0.1
|
||||
- name: Use Node.js LTS
|
||||
uses: actions/setup-node@v7.0.0
|
||||
with:
|
||||
node-version: lts/*
|
||||
cache: 'npm'
|
||||
- name: Install Dependencies
|
||||
run: npm ci
|
||||
- name: Collect Crowdin Stats for ${{ matrix.project_name }}
|
||||
id: crowdin-stats
|
||||
run: node ./scripts/crowdin-stats.mjs > crowdin-stats-${{ matrix.project_name }}.json
|
||||
env:
|
||||
CROWDIN_PROJECT_ID: ${{ vars[matrix.project_id_var] }}
|
||||
CROWDIN_TOKEN: ${{ secrets.CROWDIN_DOCS_TOKEN }}
|
||||
- name: Post to Slack for ${{ matrix.project_name }}
|
||||
uses: slackapi/slack-github-action@v4.0.0
|
||||
with:
|
||||
errors: true
|
||||
webhook: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
webhook-type: incoming-webhook
|
||||
payload-file-path: crowdin-stats-${{ matrix.project_name }}.json
|
||||
@@ -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:
|
||||
schedule:
|
||||
- cron: 0 0 * * *
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Use Node.js LTS
|
||||
uses: actions/setup-node@v7.0.0
|
||||
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@v8.1.1
|
||||
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@v7.0.1
|
||||
- name: Use Node.js LTS
|
||||
uses: actions/setup-node@v7.0.0
|
||||
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@v2.0.2
|
||||
|
||||
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@v7.0.0
|
||||
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@v7.0.1
|
||||
- name: Install dependencies (Node.js)
|
||||
uses: bahmutov/npm-install@v1.12.1
|
||||
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@v7.0.1
|
||||
with:
|
||||
ref: master
|
||||
- name: Install dependencies
|
||||
uses: bahmutov/npm-install@v1.12.1
|
||||
- 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,37 +0,0 @@
|
||||
# Collects GitHub contribution statistics from the Appium organization for the last calendar month
|
||||
# and posts it to Slack
|
||||
|
||||
name: GitHub Contribution Statistics
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
# Run on the first day of each month at 09:00 AM UTC
|
||||
- cron: 0 9 1 * *
|
||||
|
||||
jobs:
|
||||
collect-contrib-stats:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v7.0.1
|
||||
- name: Use Node.js LTS
|
||||
uses: actions/setup-node@v7.0.0
|
||||
with:
|
||||
node-version: lts/*
|
||||
cache: 'npm'
|
||||
- name: Install Dependencies
|
||||
run: npm ci
|
||||
- name: Collect GitHub Contribution Stats
|
||||
id: github-stats
|
||||
run: node ./scripts/github-contrib-stats.mjs > github-contrib-stats.json
|
||||
env:
|
||||
# GITHUB_TOKEN is optional - if not provided, uses unauthenticated requests (rate limited)
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Post to Slack
|
||||
uses: slackapi/slack-github-action@v4.0.0
|
||||
with:
|
||||
errors: true
|
||||
webhook: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
webhook-type: incoming-webhook
|
||||
payload-file-path: github-contrib-stats.json
|
||||
@@ -1,137 +0,0 @@
|
||||
name: Appium Server Release
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
dry_run:
|
||||
description: 'Dry run (no publishing or tagging)'
|
||||
required: true
|
||||
type: boolean
|
||||
default: true
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
issues: write
|
||||
id-token: write
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.ref == 'refs/heads/master' && contains(fromJSON('["mykola-mokhnach", "jlipps", "eglitise", "KazuCocoa"]'), github.actor)
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v7.0.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Configure git user
|
||||
run: |
|
||||
git config user.name "github-actions"
|
||||
git config user.email "github-actions@github.com"
|
||||
|
||||
- name: Setup Node and install deps (LTS)
|
||||
uses: ./.github/actions/setup-build
|
||||
with:
|
||||
node-version: lts/*
|
||||
install-command: npm ci
|
||||
run-build: 'true'
|
||||
|
||||
- name: Run Lerna publish
|
||||
env:
|
||||
# for lerna createRelease
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
DRY_RUN: ${{ inputs.dry_run }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if [ "$DRY_RUN" = "true" ]; then
|
||||
echo "Running lerna publish in dry-run mode"
|
||||
printf "n\n" | npx lerna version --exact --no-git-tag-version --no-push
|
||||
else
|
||||
echo "Running lerna publish for real"
|
||||
set +e
|
||||
release_output="$(npm run release -- --yes 2>&1)"
|
||||
release_exit_code=$?
|
||||
set -e
|
||||
printf '%s\n' "$release_output"
|
||||
|
||||
if [ $release_exit_code -eq 0 ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ "$release_output" == *"fatal: tag '"*"' already exists"* ]]; then
|
||||
existing_tag="$(printf '%s\n' "$release_output" | sed -n "s/.*fatal: tag '\\([^']*\\)' already exists.*/\\1/p" | head -n 1)"
|
||||
if [ -z "$existing_tag" ]; then
|
||||
echo "Detected existing tag failure, but could not parse tag name"
|
||||
exit $release_exit_code
|
||||
fi
|
||||
|
||||
echo "Detected existing tag failure for ${existing_tag}; attempting scoped publish recovery from tagged commit"
|
||||
git fetch --tags --force origin
|
||||
tagged_commit="$(git rev-list -n 1 "$existing_tag")"
|
||||
if [ -z "$tagged_commit" ]; then
|
||||
echo "Tag ${existing_tag} was not found after fetching tags"
|
||||
exit $release_exit_code
|
||||
fi
|
||||
|
||||
git checkout "$tagged_commit"
|
||||
missing=()
|
||||
for pkg_json in packages/*/package.json; do
|
||||
if [ ! -f "$pkg_json" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
if [ "$(jq -r '.private // false' "$pkg_json")" = "true" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
pkg_name="$(jq -r '.name // empty' "$pkg_json")"
|
||||
pkg_version="$(jq -r '.version // empty' "$pkg_json")"
|
||||
if [ -z "$pkg_name" ] || [ -z "$pkg_version" ]; then
|
||||
echo "Skipping malformed package manifest: $pkg_json"
|
||||
continue
|
||||
fi
|
||||
|
||||
pkg_spec="${pkg_name}@${pkg_version}"
|
||||
if ! npm view "$pkg_spec" version >/dev/null 2>&1; then
|
||||
missing+=("$pkg_spec")
|
||||
fi
|
||||
done
|
||||
|
||||
if [ ${#missing[@]} -gt 0 ]; then
|
||||
echo "Missing package versions on npm before scoped recovery:"
|
||||
printf ' - %s\n' "${missing[@]}"
|
||||
echo "Publishing missing packages one-by-one with scope"
|
||||
for pkg_spec in "${missing[@]}"; do
|
||||
pkg_name="${pkg_spec%@*}"
|
||||
echo "Publishing ${pkg_name}"
|
||||
set +e
|
||||
npx lerna publish from-package --yes --scope "$pkg_name"
|
||||
scoped_exit_code=$?
|
||||
set -e
|
||||
if [ $scoped_exit_code -ne 0 ]; then
|
||||
echo "Scoped publish exited non-zero for ${pkg_name}; will verify on npm"
|
||||
fi
|
||||
done
|
||||
|
||||
final_missing=()
|
||||
for pkg_spec in "${missing[@]}"; do
|
||||
if ! npm view "$pkg_spec" version >/dev/null 2>&1; then
|
||||
final_missing+=("$pkg_spec")
|
||||
fi
|
||||
done
|
||||
|
||||
if [ ${#final_missing[@]} -gt 0 ]; then
|
||||
echo "Recovery incomplete after scoped retries. Still missing:"
|
||||
printf ' - %s\n' "${final_missing[@]}"
|
||||
exit $release_exit_code
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "All publishable workspace package versions are present on npm; treating recovery as successful."
|
||||
else
|
||||
echo "Release failed and no known recovery condition matched"
|
||||
exit $release_exit_code
|
||||
fi
|
||||
fi
|
||||
@@ -1,41 +0,0 @@
|
||||
# This job only publishes new releases on GitHub and updates the changelog, without publishing to NPM.
|
||||
# It can be used if the standard release workflow failed and *only* published to NPM.
|
||||
|
||||
name: Appium Server Version (Without NPM Publish)
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
issues: write
|
||||
id-token: write
|
||||
|
||||
jobs:
|
||||
version:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.ref == 'refs/heads/master' && contains(fromJSON('["mykola-mokhnach", "jlipps", "eglitise", "KazuCocoa"]'), github.actor)
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v7.0.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Configure git user
|
||||
run: |
|
||||
git config user.name "github-actions"
|
||||
git config user.email "github-actions@github.com"
|
||||
|
||||
- name: Setup Node and install deps (LTS)
|
||||
uses: ./.github/actions/setup-build
|
||||
with:
|
||||
node-version: lts/*
|
||||
install-command: npm ci
|
||||
run-build: 'true'
|
||||
|
||||
- name: Run Lerna version
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: npx lerna version --exact --yes
|
||||
@@ -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@v5
|
||||
with:
|
||||
label: On Hold
|
||||
state: absent
|
||||
@@ -1,10 +0,0 @@
|
||||
name: Conventional Commits
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, edited, synchronize, reopened]
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
uses: appium/appium-workflows/.github/workflows/pr-title.yml@main
|
||||
with:
|
||||
config-preset: angular
|
||||
@@ -1,32 +0,0 @@
|
||||
name: Publish docs
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
workflow_run:
|
||||
workflows: ['Appium Server Release']
|
||||
types:
|
||||
- completed
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v7.0.1
|
||||
- run: |
|
||||
git config user.name github-actions
|
||||
git config user.email github-actions@github.com
|
||||
- name: Setup Node and install deps (LTS)
|
||||
uses: ./.github/actions/setup-build
|
||||
with:
|
||||
node-version: lts/*
|
||||
install-command: npm ci
|
||||
run-build: 'true'
|
||||
- name: Install dependencies (Python)
|
||||
run: npm run install-docs-deps
|
||||
- name: Fetch gh-pages
|
||||
run: |
|
||||
git fetch origin gh-pages:gh-pages || true
|
||||
- name: Publish docs
|
||||
run: |
|
||||
npm run docs:publish
|
||||
@@ -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@v7.0.1
|
||||
- name: Validate Renovate Config
|
||||
uses: rinchsan/renovate-config-validator@v0.2.1
|
||||
with:
|
||||
pattern: '{.renovaterc.json,renovate/default.json}'
|
||||
@@ -2,20 +2,19 @@
|
||||
*.swp
|
||||
.DS_Store
|
||||
node_modules
|
||||
sample-code
|
||||
*.trace
|
||||
xcuserdata
|
||||
*.sublime-*
|
||||
test/functional/_joined
|
||||
*.iml
|
||||
!examples/java/*.iml
|
||||
*.autosave
|
||||
.idea/
|
||||
!examples/java/.idea/
|
||||
*.log
|
||||
*.t
|
||||
artifacts
|
||||
input
|
||||
./output
|
||||
output
|
||||
lib/devices/android/bootstrap/bin/*
|
||||
lib/devices/android/bootstrap/local.properties
|
||||
lib/devices/android/bootstrap/project.properties
|
||||
@@ -24,45 +23,12 @@ lib/devices/android/bootstrap/target/
|
||||
.*~
|
||||
*~
|
||||
org.eclipse.ltk.core.refactoring.prefs
|
||||
/selendroid
|
||||
.appiumconfig*
|
||||
build/
|
||||
.idea
|
||||
*DerivedData
|
||||
__pycache__
|
||||
certificate.cert
|
||||
certificate.key
|
||||
|
||||
# csharp
|
||||
*.pyc
|
||||
*.pdb
|
||||
.vs
|
||||
|
||||
#vscode
|
||||
.history
|
||||
|
||||
old
|
||||
.tern-project
|
||||
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
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"mcpServers": {
|
||||
"codegraph": {
|
||||
"type": "stdio",
|
||||
"command": "codegraph",
|
||||
"args": ["serve", "--mcp"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
assets/
|
||||
submodules/
|
||||
test/
|
||||
_vimrc_local.vim
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
.*~
|
||||
.DS_Store
|
||||
.travis.yml
|
||||
*.log
|
||||
*.trace
|
||||
*.sublime-
|
||||
*.prefs
|
||||
./.npmignore
|
||||
coverage
|
||||
@@ -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,6 +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,15 @@
|
||||
language: node_js
|
||||
sudo: false
|
||||
node_js:
|
||||
- "7"
|
||||
- "6"
|
||||
env:
|
||||
- CXX=g++-4.8
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- g++-4.8
|
||||
script:
|
||||
- npm run test
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"stateful.runme"
|
||||
]
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
## 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 comunity 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).
|
||||
@@ -0,0 +1,63 @@
|
||||
# Contributing to Appium
|
||||
|
||||
There are a lot of different ways to contribute to Appium. 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](CONDUCT.md).
|
||||
|
||||
### Make changes to the Appium code or docs
|
||||
|
||||
Fork the project, make a change, and send a pull request! Please have a look at
|
||||
our [Style Guide](/docs/en/contributing-to-appium/style-guide-2.0.md) before
|
||||
getting to work. Please make sure the unit and functional tests pass before
|
||||
sending a pull request; for more information on how to run tests, keep reading!
|
||||
|
||||
Make sure you read and follow the setup instructions in the README first. And note
|
||||
that all participation in the Appium community (including code submissions) is
|
||||
governed by our [Code of Conduct](CONDUCT.md). Then, have a look at our
|
||||
[running from source doc](/docs/en/contributing-to-appium/appium-from-source.md) for more
|
||||
information on how to get set up to run tests and start slinging code.
|
||||
|
||||
You will probably also want to have a look at this more in-depth [Developer's
|
||||
Overview](/docs/en/contributing-to-appium/developers-overview.md) of the Appium
|
||||
project, how Appium is architected, how to make changes to modules, and so on.
|
||||
|
||||
Finally, before we accept your code, you will need to have signed our
|
||||
[Contributor License
|
||||
Agreement](https://cla.js.foundation/appium/appium).
|
||||
|
||||
### Become an Appium Triager
|
||||
|
||||
You can help us manage our issue tracker! All you need is enough familiarity
|
||||
with Appium to try and reproduce bugs. We triage issues by having conversations
|
||||
with the issue creator and then either closing the issue or moving it into the
|
||||
[Bugs](https://github.com/appium/appium/milestones/Bugs) or
|
||||
[Features](https://github.com/appium/appium/milestones/Features) milestone. We
|
||||
use these buckets as the backlogs for our upcoming releases.
|
||||
|
||||
We use this "algorithm" for triaging issues, which anyone can jump in and follow:
|
||||
|
||||
1. If issue is a feature request, discuss amongst Appium devs.
|
||||
* If we decide to build it, add to the Features milestone.
|
||||
* If we decide not to build it, just close.
|
||||
2. If issue is a bug report, attempt to verify
|
||||
* If it's a duplicate, close with link to bug already reported.
|
||||
* If it ultimately can't be verified, close until we have enough information to verify (e.g., in most cases we need a full set of Appium logs to help diagnose an issue).
|
||||
* If the user hasn't provided enough information, keep asking for enough information to verify.
|
||||
* If it's a verified new bug, add it to the Bugs milestone.
|
||||
* If we have been waiting for a reply from the user for over 3 weeks, close.
|
||||
|
||||
If you're interested in joining our triage rotation, contact
|
||||
[@jlipps](https://github.com/jlipps) or
|
||||
[@imurchie](https://github.com/imurchie).
|
||||
|
||||
### Help out on our forums
|
||||
|
||||
We can always use help on our forums at
|
||||
[discuss.appium.io](https://discuss.appium.io)! Hop on over and see if there
|
||||
are any questions that you can answer.
|
||||
|
||||
### Submit bug reports or feature requests
|
||||
|
||||
Just use the GitHub issue tracker to submit your bug reports and feature
|
||||
requests. If you are submitting a bug report, please follow the [issue template](https://github.com/appium/appium/issues/new).
|
||||
@@ -15,8 +15,12 @@ The project is officially led by a Technical Committee (TC), currently
|
||||
consisting of:
|
||||
|
||||
* [@jlipps](https://github.com/jlipps)
|
||||
* [@bootstraponline](https://github.com/bootstraponline)
|
||||
* [@penguinho](https://github.com/penguinho)
|
||||
* [@dandoveralba](https://github.com/dandoveralba)
|
||||
* [@mykola-mokhnach](https://github.com/mykola-mokhnach)
|
||||
* [@eglitise](https://github.com/eglitise)
|
||||
* [@imurchie](https://github.com/imurchie)
|
||||
* [@TikhomirovSergey](https://github.com/TikhomirovSergey)
|
||||
* [@KazuCocoa](https://github.com/KazuCocoa)
|
||||
|
||||
They all represent different companies and different types of contribution to
|
||||
@@ -27,7 +31,7 @@ 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.
|
||||
* No more than 1/3 of TC members may belong to the same company.
|
||||
* Changes to project governance require unanimous vote of the TC.
|
||||
|
||||
### Project Committers
|
||||
@@ -46,108 +50,30 @@ and so on.
|
||||
* 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):
|
||||
The current committers are (in addition to the TC members above):
|
||||
|
||||
* [@dan-maor](https://github.com/Dan-Maor) - XCUITest driver and iOS internals
|
||||
* [@dor-bl](https://github.com/Dor-bl) - DotNet client
|
||||
* [@Delta456](https://github.com/Delta456) - appium-mcp, iOS support libraries
|
||||
* [@eglitise](https://github.com/eglitise) - Appium server, Inspector
|
||||
* [@harsha509](https://github.com/harsha509) - appium-ios-tuntap
|
||||
* [@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
|
||||
* [@navin772](https://github.com/navin772) - appium-mcp, iOS support libraries
|
||||
* [@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
|
||||
* [@dpgraham](https://github.com/dpgraham)
|
||||
* [@sravanmedarapu](https://github.com/sravanmedarapu)
|
||||
* [@moizjv](https://github.com/moizjv)
|
||||
* [@SrinivasanTarget](https://github.com/SrinivasanTarget)
|
||||
* [@christian-bromann](https://github.com/christian-bromann)
|
||||
* [@vrunoa](https://github.com/vrunoa)
|
||||
* [@saikrishna321](https://github.com/saikrishna321)
|
||||
* [@butomo1989](https://github.com/butomo1989)
|
||||
* [@vikramvi](https://github.com/vikramvi)
|
||||
|
||||
### Contributors
|
||||
|
||||
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)
|
||||
Other, less formal, kinds of contribution are outlined in our
|
||||
[CONTRIBUTING](/CONTRIBUTING.md) doc.
|
||||
|
||||
### 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.
|
||||
The Appium project welcomes sponsorship of various organizations. Specific
|
||||
details are not yet available but in general we will accept monetary donations,
|
||||
contribution of code, contribution of dedicated committers, or donations of
|
||||
other goods or service (internet hosting, etc...). To begin the conversation,
|
||||
reach out to one of the TC members.
|
||||
|
||||
### Raising Issues Related to Governance
|
||||
|
||||
@@ -155,35 +81,3 @@ 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)
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
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.
|
||||
@@ -186,7 +186,7 @@
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright OpenJS Foundation and other contributors, https://openjsf.org/
|
||||
Copyright JS Foundation and other contributors, https://js.foundation
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,221 +1,178 @@
|
||||
<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">
|
||||
## Appium
|
||||
|
||||
[](https://npmjs.org/package/appium)
|
||||
[](https://david-dm.org/appium/appium)
|
||||
[](https://david-dm.org/appium/appium#info=devDependencies)
|
||||
|
||||
[](https://npmjs.org/package/appium)
|
||||
[](http://issuestats.com/github/appium/appium)
|
||||
[](http://issuestats.com/github/appium/appium)
|
||||
|
||||
[](https://team-appium.ci.cloudbees.com/job/Appium/)
|
||||
[](https://app.fossa.io/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fappium%2Fappium?ref=badge_shield)
|
||||
[](https://github.com/vshymanskyy/StandWithUkraine/)
|
||||
[](https://www.unrwa.org/)
|
||||
|
||||
</div>
|
||||
Appium is an open source, cross-platform test automation tool for native, hybrid and mobile web and desktop apps, tested on simulators (iOS), emulators (Android), and real devices (iOS, Android, Windows, Mac).
|
||||
|
||||
***
|
||||
|
||||
<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>
|
||||
### Supported Platforms
|
||||
|
||||
***
|
||||
* iOS
|
||||
* Android
|
||||
* Windows
|
||||
* Mac
|
||||
|
||||
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
|
||||
See the [platform support doc](/docs/en/appium-setup/platform-support.md) for more detailed information.
|
||||
|
||||
## Upgrading From Older Versions
|
||||
### Why Appium?
|
||||
|
||||
The Appium team only provides support for the most recent version of Appium. If you wish to upgrade from
|
||||
an older major Appium version, please refer to the migration guides:
|
||||
|
||||
* [Appium v2 to v3](https://appium.io/docs/en/latest/guides/migrating-2-to-3/)
|
||||
* [Appium v1 to v2](https://appium.io/docs/en/latest/guides/migrating-1-to-2/)
|
||||
|
||||
## 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/server/)
|
||||
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.
|
||||
1. You don't have to recompile your app or modify it in any way, due
|
||||
to use of standard automation APIs on all platforms.
|
||||
2. You can write tests with your favorite dev tools using any [WebDriver](https://w3c.github.io/webdriver/webdriver-spec.html)-compatible
|
||||
language such as Java, [Objective-C](https://github.com/appium/selenium-objective-c),
|
||||
JavaScript with Node.js (in [promise, callback](https://github.com/admc/wd) or [generator](https://github.com/jlipps/yiewd) flavors),
|
||||
PHP, Python, [Ruby](https://github.com/appium/ruby_lib), C#, Clojure, or Perl
|
||||
with the Selenium WebDriver API and language-specific client libraries.
|
||||
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.
|
||||
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 defacto 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 use Apple's UIAutomation library without Appium you can only write tests
|
||||
using JavaScript and you can only run tests through the Instruments application.
|
||||
Similarly, with Google's UiAutomator you can only write tests in Java. Appium
|
||||
opens up the possibility of true cross-platform native mobile automation. Finally!
|
||||
|
||||
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/).
|
||||
### I don't get it yet...
|
||||
|
||||
## Sponsors
|
||||
If you're new to Appium, or want a fuller description of what this is all about, please read our [Introduction to Appium Concepts](/docs/en/about-appium/intro.md).
|
||||
|
||||
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.
|
||||
### Requirements
|
||||
|
||||
### Development and Strategic Partners
|
||||
Your environment needs to be setup for the particular mobile platforms that you
|
||||
want to run tests on. See below for particular platform requirements.
|
||||
|
||||
Appium is incredibly grateful to our Development and Strategic Partners for their sustained
|
||||
contribution of project development and leadership!
|
||||
If you want to run Appium via an `npm install`, hack with or contribute to Appium, you will need
|
||||
[node.js and npm](http://nodejs.org) 4 or greater (use [n](https://github.com/visionmedia/n) or
|
||||
`brew install node` to install Node.js. Make sure you have not installed Node or Appium with `sudo`,
|
||||
otherwise you'll run into problems). We recommend the latest stable version.
|
||||
|
||||
<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>
|
||||
To verify that all of Appium's dependencies are met you can use
|
||||
`appium-doctor`. Install it with `npm install -g appium-doctor` (or run it
|
||||
from [source](https://github.com/appium/appium-doctor)), then run
|
||||
`appium-doctor` and supply the `--ios` or `--android` flags to verify that all
|
||||
of the dependencies are set up correctly.
|
||||
|
||||
<p align="center">
|
||||
<a href="https://www.testmuai.com">
|
||||
<picture>
|
||||
<source srcset="packages/appium/docs/overrides/assets/images/sponsor-logo-testmuai-dark.png" media="(prefers-color-scheme: dark)"/>
|
||||
<source srcset="packages/appium/docs/overrides/assets/images/sponsor-logo-testmuai-light.png" media="(prefers-color-scheme: light)"/>
|
||||
<img src="packages/appium/docs/overrides/assets/images/sponsor-logo-testmuai-dark.png" width="300" alt="TestMu AI (formerly LambdaTest)"/>
|
||||
</picture>
|
||||
</a>
|
||||
</p>
|
||||
You also need to download the Appium client for your language so you can write tests. The Appium clients are simple extensions to the WebDriver clients. You can see the list of clients and links to download instructions at the [Appium clients list](/docs/en/about-appium/appium-clients.md).
|
||||
|
||||
### Other Sponsors
|
||||
#### iOS Requirements
|
||||
|
||||
A full list of sponsors is available at our [Sponsors page](https://appium.io/docs/en/latest/sponsors/).
|
||||
* Mac OS X 10.10 or higher, 10.11.1 recommended
|
||||
* XCode >= 6.0, 7.1.1 recommended
|
||||
* Apple Developer Tools (iPhone simulator SDK, command line tools)
|
||||
* [Ensure you read our documentation on setting yourself up for iOS testing!](/docs/en/appium-setup/running-on-osx.md)
|
||||
|
||||
## License
|
||||
#### Android Requirements
|
||||
|
||||
[Apache-2.0](./LICENSE)
|
||||
* [Android SDK](http://developer.android.com) API >= 17 (Additional features require 18/19)
|
||||
* Appium supports Android on OS X, Linux and Windows. Make sure you follow the
|
||||
directions for setting up your environment properly for testing on different OSes:
|
||||
* [linux](/docs/en/appium-setup/running-on-linux.md)
|
||||
* [osx](/docs/en/appium-setup/running-on-osx.md)
|
||||
* [windows](/docs/en/appium-setup/running-on-windows.md)
|
||||
|
||||
#### Windows Requirements
|
||||
|
||||
* Windows 10
|
||||
* [Documentation](/docs/en/appium-setup/running-on-windows.md)
|
||||
|
||||
#### Mac Requirements
|
||||
|
||||
* Mac OS X 10.7 +
|
||||
* [Documentation](https://github.com/penguinho/appium/blob/mac-driver/docs/en/appium-setup/running-on-osx.md#testing-mac-apps)
|
||||
|
||||
### Quick Start
|
||||
|
||||
Kick up an Appium server, and then run a test written in your favorite [WebDriver](https://w3c.github.io/webdriver/webdriver-spec.html)-compatible language!
|
||||
You can run an Appium server using node.js or using the application, see below.
|
||||
|
||||
#### Using Node.js
|
||||
|
||||
```
|
||||
$ npm install -g appium
|
||||
$ appium
|
||||
```
|
||||
|
||||
As we said above, you may want to run `appium-doctor` to ensure your system is set up properly:
|
||||
|
||||
```
|
||||
$ npm install -g appium-doctor
|
||||
$ appium-doctor
|
||||
```
|
||||
|
||||
#### Using the Appium Desktop App
|
||||
|
||||
* [Download the Appium app](https://www.github.com/appium/appium-desktop/releases/latest/)
|
||||
* Run it!
|
||||
|
||||
### Writing Tests for Appium
|
||||
|
||||
The main guide for getting started writing and running tests is [the running tests](/docs/en/writing-running-appium/running-tests.md) doc, which includes explanations for iOS, Android, and Android older devices. If you're interested in testing on physical hardware, you might be interested in our [real devices guide](/docs/en/appium-setup/real-devices.md).
|
||||
|
||||
(*Note*: If you're automating iOS 10+, be sure to check out our [XCUITest Migration Guide](/docs/en/advanced-concepts/migrating-to-xcuitest.md) since Apple's automation support has changed significantly since iOS 10, with corresponding changes in Appium).
|
||||
|
||||
Essentially, we support a subset of the [Selenium WebDriver JSON Wire Protocol](https://w3c.github.io/webdriver/webdriver-spec.html), and extend it so that you can specify mobile-targeted [desired capabilities](/docs/en/writing-running-appium/caps.md) to run your test through Appium.
|
||||
|
||||
You find elements by using a subset of WebDriver's element-finding strategies.
|
||||
See [finding elements](/docs/en/writing-running-appium/finding-elements.md) for detailed information. We also have several extensions to the JSON Wire Protocol for [automating mobile gestures](/docs/en/writing-running-appium/touch-actions.md) like tap, flick, and swipe.
|
||||
|
||||
You can also automate web views in hybrid apps! See the [hybrid app guide](/docs/en/advanced-concepts/hybrid.md)
|
||||
|
||||
This repository contains [many examples of tests in a variety of different languages](https://github.com/appium/sample-code)!
|
||||
|
||||
For the full list of Appium doc pages, visit [this directory](/docs/en/).
|
||||
|
||||
### How It Works
|
||||
|
||||
Appium drives various native automation frameworks and provides an API based on
|
||||
Selenium's [WebDriver JSON wire protocol](https://w3c.github.io/webdriver/webdriver-spec.html).
|
||||
|
||||
For new iOS versions (9.3 and up), Appium drives Apple's XCUITest library. Our support for XCUITest utilizes Facebook's [WebDriverAgent](https://github.com/facebook/webdriveragent) project.
|
||||
|
||||
For older iOS versions (9.3 and below), Appium drives Apple's UIAutomation library, using a strategy which is based on [Dan Cuellar's](http://github.com/penguinho) work on iOS Auto.
|
||||
|
||||
Android support uses the UiAutomator framework for newer platforms and
|
||||
[Selendroid](http://github.com/DominikDary/selendroid) for older Android platforms.
|
||||
|
||||
Windows support uses Microsoft's [WinAppDriver](https://github.com/Microsoft/WinAppDriver)
|
||||
|
||||
### Contributing
|
||||
|
||||
Please take a look at our [contribution documentation](CONTRIBUTING.md)
|
||||
for instructions on how to build, test and run Appium from source.
|
||||
|
||||
### Roadmap
|
||||
|
||||
Interested in where Appium is heading in the future? Check out the [Roadmap](ROADMAP.md)
|
||||
|
||||
### Project History, Credits & Inspiration
|
||||
|
||||
* [History](http://appium.io/history)
|
||||
* [Credits](/docs/en/contributing-to-appium/credits.md)
|
||||
|
||||
### User Forums
|
||||
|
||||
Announcements and debates often take place on the [Discussion Group](https://discuss.appium.io), be sure to sign up!
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
We put together a [troubleshooting guide](/docs/en/appium-setup/troubleshooting.md).
|
||||
Please have a look here first if you run into any problems. It contains instructions for checking a lot
|
||||
of common errors and how to get in touch with the community if you're stumped.
|
||||
|
||||
### Using Robots
|
||||
|
||||
Using Appium with [Tapster](https://github.com/hugs/tapsterbot) and other robots is possible,
|
||||
check out the [Appium Robots](https://github.com/appium/robots) project!
|
||||
|
||||
### 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,30 +1,59 @@
|
||||
# Roadmap
|
||||
Appium 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.
|
||||
This document is designed to be a living record of the current and projected
|
||||
priorities for the Appium project. It is, of course, always subject to change
|
||||
and exists solely to give the community an idea of where we're going. The
|
||||
roadmap is set by the Appium core team. If you have feature suggestions for
|
||||
Appium, please submit a [GitHub issue](https://github.com/appium/appium/issues)
|
||||
and, if of sufficient size and approved by the team, it will be added here. If
|
||||
you have concerns about the roadmap, you can e-mail the Appium devs at
|
||||
`appium-developers@googlegroups.com` or raise a GitHub issue. Note that
|
||||
bugfixes and miscellaneous features are not considered "roadmap projects", and
|
||||
will be addressed in the due course of normal Appium development. This page
|
||||
exists to outline large-scale future plans for Appium.
|
||||
|
||||
### 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!
|
||||
## Current Roadmap Projects
|
||||
|
||||
### 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
|
||||
| Project | Description | ETD | Projected Appium Release |
|
||||
|---------|-------------|-----|--------------------------|
|
||||
|[Appium CI + Metrics Improvements](https://github.com/appium/appium/issues/7168)|We have CI servers running via Cloudbees that we don't understand and which break. Let's document and own them so they work reliably. Also, let's implement gathering various metrics about driver performance so we can track this over time and ensure we're not releasing a driver which has decreased in performance or reliability.|Q2 2017|N/A|
|
||||
|
||||
### 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.
|
||||
## Upcoming Projects (in rough order of priority)
|
||||
|
||||
### 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.
|
||||
| Project | Description |
|
||||
|---------|-------------|
|
||||
|[Onboarding Overhaul](https://github.com/appium/appium/issues/7169)|The current Appium docs leave a lot to be desired. The format is confusing (it would be better for API docs), the content is outdated, and many things are still undocumented. This project would go back to the drawing board in all of these areas and put together something genuinely helpful for the community, which hopefully reduces the kind of support issues we're currently flooded by.|
|
||||
|[Appium Client Library Updates](https://github.com/appium/appium/issues/7170)|It's been too long since we ensured that Appium's client libraries were up to date with the server|
|
||||
|[Sample Code Overhaul](https://github.com/appium/appium/issues/7171)|Our sample code repo has grown tangled and broken. Let's make a new one with minimal examples which are the same across every language, and actually maintain the repo so as not to let it become overgrown.|
|
||||
|[Test App Overhaul](https://github.com/appium/appium/issues/7172)|Our current test applications (the ones used to test Appium's iOS and Android support) are not ideal. We use too many of them and the ones we use have a random hodgepodge of app SDK features, sometimes not enough to sufficiently test automation support. It'd be great to find a way to ensure our test app(s) have all the features we need to test, and keep in sync with our requirements.|
|
||||
|[iOS Test Startup Time Improvement](https://github.com/appium/appium/issues/7173)|iOS startup times have gotten progressively longer with each iOS release. In part this is Apple's fault and there's not anything we can do about it. But we should investigate and see if we can be more efficient in how we start, especially given that we can now focus on XCUITest and leave Instruments behind|
|
||||
|[Appium Dearchitecture](https://github.com/appium/appium/issues/7174)|OK, we went a bit overboard in Appium 1.5 by exploding Appium into 40+ repos and NPM packages. Let's think a little more critically about it and consolidate into the minimal number of repos/packages that make sense for our code and community patterns.|
|
||||
|[Appium 2.0](https://github.com/appium/appium/issues/7175)|Appium, rather than bundling all the separate drivers by default, becomes a driver management interface (in addition to continuing to play the role of automation frontend server). Appium gets a set of CLI tools for users to pick and choose which drivers and versions of drivers they want to use via Appium|
|
||||
|
||||
## Projects Under Consideration (unplanned and unprioritized)
|
||||
|
||||
| Project | Description |
|
||||
|---------|-------------|
|
||||
|Windows Phone Support|There has been some work done on automating Windows Phone apps. We could take this all the way to integration with Appium in the form of a WindowsDriver.|
|
||||
|Apple Watch support|Appium should support Apple Watch apps via the Watch simulator|
|
||||
|Unity3d backend POC|This is a research project to determine whether we can add some kind of support for 3d game automation, probably based on the Unity3d game development platform.|
|
||||
|
||||
## Completed Roadmap Projects
|
||||
|
||||
| Project | Description | Completed | Appium Release | Notes |
|
||||
|---------|-------------|-----------|----------------|-------|
|
||||
|[New Appium GUIs](https://github.com/appium/appium/issues/7167)|The current Appium GUIs (Appium.app and Appium.exe) have a number of issues. They aren't maintained by the core Appium team. They're written in Objective-C and C# respectively: two languages, and specifically, two languages other than Appium's core language. Because of this they lag behind the Appium server and are often broken. However, users love them primarily because of the Inspector feature (the thing that lets them graphically navigate their app), so we have to field a ton of complaints about these projects. There's an opportunity to start over. With [Electron](http://electron.atom.io/), we'd be able to take the new version of Appium we're working on and embed it directly inside a Node-based GUI app written using the languages and technologies the core team is experienced with. We'd have one codebase and could distribute the app on Mac, Windows, and Linux. The GUIs would become an official part of Appium's release cycle so they wouldn't be broken.|3/2017|N/A|[Appium Desktop website](https://github.com/appium/appium-desktop)|
|
||||
|[Android 7.1.1 Support](https://github.com/appium/appium/issues/7165)|Google has released Android 7.1.1 (API Level 25). Ensure it works with UiAutomator 1/2|2/2017|1.6.4||
|
||||
|[Android 7.0 Support](https://github.com/appium/appium/issues/7156)|Support Android 7 (Nougat)|11/2016|1.6.3||
|
||||
|[iOS 10.2 Support](https://github.com/appium/appium/issues/7166)|Support iOS 10.2|11/2016|1.6.1||
|
||||
|[iOS 10.1 Support](https://github.com/appium/appium/issues/7105)|Support iOS 10.1|11/2016|1.6.1||
|
||||
|UiAutomator2 integration|Google has released [UiAutomator 2](http://developer.android.com/reference/android/support/test/uiautomator/package-summary.html), and it promises to fix some issues and limitations with the previous UiAutomator support. We will build this inside of Selendroid since it requires an instrumentation context.|10/2016?|1.6.0|
|
||||
|XCUITest integration / iOS 10 support|Instruments + UIAutomation has been working well for many Appium users, but requires crazy workarounds to deal with bugs which remain unfixed on the Apple side. Also, UIAutomation has been removed in favor of XCUITest for iOS 10+. This new backend would exist alongside the UIAutomation backend.|10/2016|1.6.0||
|
||||
|iOS 9.3 support|Appium should support iOS 9.3 / Xcode 7.3|3/2016|1.5.1||
|
||||
|[Architecture Overhaul](https://github.com/appium/appium/issues/5169)|Appium's architecture has grown unwieldy enough that adding new features and preventing regressions has become difficult. A rearchitecture is in order to solve these issues as well as migrate the codebase to a form of JS much easier to work in and understand.|2/2015|1.5.0||
|
||||
|iOS 9.2 support|Appium should support iOS 9.2 / Xcode 7.2|11/2015|1.4.14||
|
||||
|iOS 9.1 support|Appium should support iOS 9.1 / Xcode 7.1|10/2015|1.4.12||
|
||||
|iOS 9.0 support|Appium should support iOS 9.0 / Xcode 7.0|10/2015|1.4.11||
|
||||
|iOS 8.4 support|Appium should support iOS 8.4 / Xcode 6.2|5/2015|1.4.0||
|
||||
|Appium CI|Appium has unique needs for Continuous Integration because of the highly specialized environments required (iOS simulators, Xcode, Android emulators, the Android SDK, etc...). We therefore need to create a custom CI solution (perhaps sitting underneath Cloudbees or some other OSS-friendly CI server) that can be used for all Appium projects which need integration tests.|5/2015|_N/A_|Usage instructions at [appium/appium-ci](https://github.com/appium/appium-ci)|
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
# Sponsors & Backers
|
||||
|
||||
Please check [Sponsors & Backers](https://appium.io/docs/en/latest/sponsors/)
|
||||
@@ -0,0 +1,71 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/* eslint no-console:0 */
|
||||
|
||||
/*
|
||||
* Small tool, launching and monitoring ios-web-kit-proxy, and relauching
|
||||
* on predefined errors.
|
||||
*
|
||||
* Usage:
|
||||
* ./bin/ios-webkit-debug-proxy-launcher.js [args]
|
||||
* args: ios-webkit-debug-proxy args (they will be passed over)
|
||||
*
|
||||
* Example:
|
||||
* ./bin/ios-webkit-debug-proxy-launcher.js -c <UDID>:27753 -d
|
||||
*
|
||||
* Note:
|
||||
* For iOS8.1 try this first:
|
||||
* brew install --HEAD ideviceinstaller
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
const spawn = require('child_process').spawn;
|
||||
const _ = require('lodash');
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
const RESTART_ON_MESSAGES = [
|
||||
'Invalid message _rpc_applicationUpdated',
|
||||
'Invalid message _rpc_applicationSentListing'];
|
||||
|
||||
const PROXY_CMD = 'ios_webkit_debug_proxy';
|
||||
let proxy;
|
||||
|
||||
const handleKillProcess = function (exitCode) {
|
||||
console.log('\nKilling proxy process!');
|
||||
proxy.kill('SIGTERM');
|
||||
process.exit((exitCode || 0));
|
||||
};
|
||||
|
||||
const startProxy = function () {
|
||||
console.log('RUNNING:', PROXY_CMD, args.join(' '));
|
||||
|
||||
proxy = spawn(PROXY_CMD, args);
|
||||
|
||||
proxy.stdout.on('data', function (data) {
|
||||
console.log('stdout: ' + data);
|
||||
});
|
||||
|
||||
proxy.stderr.on('data', function (data) {
|
||||
console.log('stderr: ' + data);
|
||||
const restartMessage = _(RESTART_ON_MESSAGES).find(function (message) {
|
||||
return ('' + data).indexOf(message) >= 0;
|
||||
});
|
||||
if (restartMessage) {
|
||||
console.log('Detected error message:', restartMessage);
|
||||
console.log('Killing proxy!');
|
||||
proxy.kill('SIGTERM');
|
||||
process.nextTick(startProxy);
|
||||
}
|
||||
});
|
||||
|
||||
proxy.on('close', function (code) {
|
||||
console.log('child process exited with code ' + code);
|
||||
});
|
||||
};
|
||||
|
||||
process.on('SIGINT', handleKillProcess);
|
||||
process.on('SIGTERM', handleKillProcess);
|
||||
|
||||
startProxy();
|
||||
@@ -1,6 +1,11 @@
|
||||
# Docs moved
|
||||
# docs
|
||||
|
||||
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).
|
||||
## doc site
|
||||
|
||||
Current Appium docs are located in [packages/appium/docs](../packages/appium/docs) in this repo.
|
||||
Formatted doc is [here](http://appium.io/documentation.html?lang=en).
|
||||
|
||||
## doc translation
|
||||
|
||||
At the moment we are focusing on improving the english documentation. When it
|
||||
is done we are planning to setup proper i18n l10n processes so that it is
|
||||
easier for people to help in translating the documentation.
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
The latest doc in Chinese, maintained by [TesterHome Appium 中文文档小组](https://testerhome.com/appium-doc-cn).
|
||||
@@ -0,0 +1,22 @@
|
||||
## 支持 Appium 服务器的客户端程序库列表
|
||||
|
||||
这些库封装了标准的 Selenium 客户端,提供了所有 [JSON Wire protocol](https://w3c.github.io/webdriver/webdriver-spec.html) 指定的常规 selenium 命令,额外添加操控移动设备相关的命令,例如 **多点触控手势** 和 **屏幕方向**。
|
||||
|
||||
Appium 客户端程序库实现了 [Mobile JSON Wire Protocol](https://github.com/SeleniumHQ/mobile-spec/blob/master/spec-draft.md)(一个基于标准官方协议的扩展草案)和 [W3C Webdriver 规格](https://dvcs.w3.org/hg/webdriver/raw-file/default/webdriver-spec.html)(一个无关传输的自动化规格;定义 MultiAction API 的地方)中的元素。
|
||||
|
||||
Appium 服务端自定义了基于官方协议的扩展,帮助 Appium 用户使用各种设备操作(例如在测试会话过程中安装/卸载应用)。这就是为什么我们需要 Appium 特定的客户端,而不只是“原味”的 Selenium 客户端。当然,Appium 客户端程序库只增加功能(其实它们只是扩展了标准 Selenium 客户端),所以它们仍然适用于常规的 Selenium 会话。
|
||||
|
||||
语言/框架 | Github 仓库和安装指南 |
|
||||
----- | ----- |
|
||||
Ruby | [https://github.com/appium/ruby_lib](https://github.com/appium/ruby_lib)
|
||||
Python | [https://github.com/appium/python-client](https://github.com/appium/python-client)
|
||||
Java | [https://github.com/appium/java-client](https://github.com/appium/java-client)
|
||||
JavaScript (Node.js) | [https://github.com/admc/wd](https://github.com/admc/wd)
|
||||
Objective C | [https://github.com/appium/selenium-objective-c](https://github.com/appium/selenium-objective-c)
|
||||
PHP | [https://github.com/appium/php-client](https://github.com/appium/php-client)
|
||||
C# (.NET) | [https://github.com/appium/appium-dotnet-driver](https://github.com/appium/appium-dotnet-driver)
|
||||
RobotFramework | [https://github.com/jollychang/robotframework-appiumlibrary](https://github.com/jollychang/robotframework-appiumlibrary)
|
||||
|
||||
由 @黑水 翻译,TesterHome 社区 id:sanlengjingvv
|
||||
|
||||
@lihuazhang 校验
|
||||
@@ -0,0 +1,64 @@
|
||||
## Appium 介绍
|
||||
|
||||
Appium 是一个开源工具,用于自动化 iOS 手机 、 Android 手机和 Windows 桌面平台上的原生、移动 Web 和混合应用。“原生应用”指那些用 iOS 、 Android 或者 Windows SDK 编写的应用。“移动 web 应用”是用移动端浏览器访问的应用(Appium 支持 iOS 上的 Safari 、Chrome 和 Android 上的内置浏览器)。“混合应用”带有一个 "webview" 的包装器——用来和 Web 内容交互的原生控件。类似 [Phonegap](http://phonegap.com/) 的项目,让用 Web 技术开发然后打包进原生包装器创建一个混合应用变得容易了。
|
||||
|
||||
重要的是,Appium 是跨平台的:它允许你用同样的 API 对多平台写测试,做到在 iOS 、Android 和 Windows 测试套件之间复用代码。
|
||||
|
||||
了解 Appium “支持”这些平台意味着什么、有哪些自动化方式的详细信息,请参见[ Appium 支持的平台](/docs/cn/appium-setup/platform-support.md)。
|
||||
|
||||
### Appium 的理念
|
||||
|
||||
Appium 旨在满足移动端自动化需求的理念,概述为以下四个原则:
|
||||
1. 你没有必要为了自动化而重新编译你的应用或者以任何方式修改它。
|
||||
2. 你不应该被限制在特定的语言或框架上来编写运行测试。
|
||||
3. 移动端自动化框架在自动化接口方面不应该重造轮子。
|
||||
4. 移动端自动化框架应该开源,不但在名义上而且在精神和实践上都要实至名归。
|
||||
|
||||
### Appium 的设计
|
||||
|
||||
那么 Appium 项目的架构如何实现这一理念呢?为了实现第一点要求,我们其实使用了系统自带的自动化框架。这样,我们不需要把 Appium 特定的或者第三方的代码编译进你的应用。这意味着你测试使用的应用与最终发布的应用并无二致。我们使用以下系统自带的自动化框架:
|
||||
|
||||
* iOS 9.3 及以上:苹果的 [XCUITest](https://developer.apple.com/reference/xctest)
|
||||
* iOS 9.3 及以下:苹果的 [UIAutomation](https://developer.apple.com/library/ios/documentation/DeveloperTools/Reference/UIAutomationRef/)
|
||||
* Android 4.2+: 谷歌的 [UiAutomator](http://developer.android.com/tools/help/uiautomator/index.html)
|
||||
* Android 2.3+: 谷歌的 [Instrumentation](http://developer.android.com/reference/android/app/Instrumentation.html)(通过绑定另外的项目——[ Selendroid ](http://selendroid.io)提供 Instrumentation 的支持)
|
||||
* Windows: 微软的 [WinAppDriver](http://github.com/microsoft/winappdriver)
|
||||
|
||||
为了实现第二点要求,我们把这些(系统本身的)供应商提供的框架包装进一套 API —— [WebDriver](http://docs.seleniumhq.org/projects/webdriver/) API 中。WebDriver(也叫 "Selenium WebDriver")规定了一个客户端-服务器协议(称为 [JSON Wire Protocol](https://w3c.github.io/webdriver/webdriver-spec.html)),按照这种客户端-服务器架构,可以使用任何语言编写的客户端向服务器发送适当的 HTTP 请求。已经有[各个流行编程语言编写的客户端](http://appium.io/downloads)了。这也意味着你可以自由使用任何你想要的测试运行器和测试框架;客户端程序库不过是 HTTP 客户端,可以以任何你喜欢的方式混入你的代码。换句话说,Appium 和 WebDriver 客户端不是严格意义上的“测试框架”,而是“自动化程序库”。你可以以任何你喜欢的方式管理你的测试环境!
|
||||
|
||||
我们以同样的方式实现第三点要求:WebDriver 已经成为 Web 浏览器自动化事实上的标准,并且是一个[ W3C 工作草案](https://dvcs.w3.org/hg/webdriver/raw-file/tip/webdriver-spec.html)。何必在移动端做完全不同的尝试?我们通过附加可用于移动端自动化的 API 方法[扩展了协议](https://github.com/SeleniumHQ/mobile-spec/blob/master/spec-draft.md)。
|
||||
|
||||
显然第 4 点是你在阅读的前提——[ Appium 是开源的](https://github.com/appium/appium)
|
||||
|
||||
### Appium 概念
|
||||
|
||||
**客户端/服务器架构**<br/>
|
||||
Appium 的核心是暴露 REST API 的网络服务器。它接受来自客户端的连接,监听命令并在移动设备上执行,答复表示执行结果的 HTTP 响应。客户端/服务器架构实际给予了许多可能性:我们可以使用任何有 http 客户端 API 的语言编写我们的测试代码,不过选一个[ Appium 客户端程序库
|
||||
](http://appium.io/downloads)用更容易。我们可以把服务器放在另一台机器上,而不是执行测试的机器。我们可以编写测试代码,并依靠类似 [Sauce Labs](https://saucelabs.com/mobile) 的云服务接收和解释命令。
|
||||
|
||||
**会话(session)**<br/>
|
||||
自动化始终在一个会话的上下文中执行,这些客户端程序库以各自的方式发起与服务器的会话,但都以发给服务器一个 `POST /session` 请求结束,请求中包含一个被称作 'desired capabilities' 的 JSON 对象。这时服务器就会开启这个自动化会话,并返回一个用于发送后续命令的会话 ID。
|
||||
|
||||
**Desired Capabilities**<br/>
|
||||
Desired capabilities 是一些发送给 Appium 服务器的键值对集合 (比如 map 或 hash),告诉服务器我们想要启动什么类型的自动化会话。也有各种可以在自动化运行时修改服务器行为的 capabilities。例如,我们可以把 `platformName` capability 设置为 `iOS`,告诉 Appium 我们想要 iOS 会话,而不是 Android 或者 Windows 会话。我们也可以设置 `safariAllowPopups` capability 为 `true` ,确保我们在 Safari 自动化会话中可以使用 javascript 打开新窗口。有关 Appium capabilities 的完整列表,请参阅 [capabilities doc](/docs/cn/writing-running-appium/caps.md) 。
|
||||
|
||||
**Appium 服务器**<br/>
|
||||
Appium 是用 Node.js 写的服务器。它可以从[源码](https://github.com/appium/appium/blob/master/docs/en/contributing-to-appium/appium-from-source.md)构建安装或者从 NPM 直接安装:
|
||||
```
|
||||
$ npm install -g appium
|
||||
$ appium
|
||||
```
|
||||
|
||||
**Appium 客户端**<br/>
|
||||
有多个客户端程序库(Java、Ruby、Python、PHP,、JavaScript 和 C# 的)支持 Appium 对 WebDriver 协议的扩展,你需要用这些客户端程序库代替通常的 WebDriver 客户端。在[这里](appium-clients.md)浏览所有程序库的列表。
|
||||
|
||||
**[Appium.app](https://github.com/appium/appium-dot-app), [Appium.exe](https://github.com/appium/appium-dot-exe)**<br/>
|
||||
有 Appium 服务器的图形界面包装器可以下载。它们打包了 Appium 服务器运行需要的所有东西,所以你不需要为 Node 而烦恼。它们还提供一个 Inspector 使你可以查看你应用的层级结构,这在写测试时很方便。
|
||||
|
||||
### 入门指南
|
||||
|
||||
恭喜!你现在有足够的知识来使用 Appium 了。 为什么不前往 [入门指南](/docs/cn/README.md) 了解更多详细的要求和指南呢?
|
||||
|
||||
由 @黑水 翻译,TesterHome 社区 id:sanlengjingvv
|
||||
|
||||
@lihuazhang 校验
|
||||
@@ -0,0 +1,77 @@
|
||||
## Chromedriver
|
||||
|
||||
Appium 支持对基于 Chrome 内核的 H5 应用(混合应用)或者网页(Chrome 中的网页或者内建的浏览器中的网页)进行自动化。Appium 管理维护着一个 [Chromedriver](https://sites.google.com/a/chromium.org/chromedriver/) 实例,当需要的时候,使用代理模式,将命令传递给这个实例。这和[最新版本的 Chromedriver](https://chromedriver.storage.googleapis.com/LATEST_RELEASE)是绑定的。我们可以通过 npm 包来安装[appium-chromedriver](https://www.npmjs.com/package/appium-chromedriver)。(Github: [appium-chromedriver](https://github.com/appium/appium-chromedriver))
|
||||
|
||||
不幸的是,每次 Chromedriver 升级,支持的 Chrome 的最小版本都会升级,这就导致了老设备上经常无法在绑定的版本上自动化。在 Appium 的服务端就会有这样的错误日志:
|
||||
```
|
||||
An unknown server-side error occurred while processing the command.
|
||||
Original error: unknown error: Chrome version must be >= 55.0.2883.0
|
||||
```
|
||||
|
||||
为了解决这个问题,Appium 可以指定特定的 Chromedriver 版本。或者在安装的时候指定版本,比如 `npm install appium --chromedriver_version="2.16"`
|
||||
或者 `CHROMEDRIVER_VERSION=2.20 npm install appium`,也可以使用 `LATEST` 作为 `CHROMEDRIVER_VERSION` 的版本号,表示使用最新的 Chromedriver。或者可以在 Appium 运行的时候指定 `--chromedriver-executable` 为你自己下载的版本的路径,比如 `appium --chromedriver-executable /path/to/my/chromedriver`。
|
||||
|
||||
以下就是 Chromedriver 版本和支持的 Chrome 的最小版本的信息:
|
||||
|
||||
|
||||
| Version | Minimum Chrome Version | Link to Chromedriver |
|
||||
|---------|------------------------|---------------------------------------------------------------------------|
|
||||
| 2.29 | 57.0.2987.0 | [link](https://chromedriver.storage.googleapis.com/index.html?path=2.29/) |
|
||||
| 2.28 | 55.0.2883.0 | [link](https://chromedriver.storage.googleapis.com/index.html?path=2.28/) |
|
||||
| 2.27 | 54.0.2840.0 | [link](https://chromedriver.storage.googleapis.com/index.html?path=2.27/) |
|
||||
| 2.26 | 53.0.2785.0 | [link](https://chromedriver.storage.googleapis.com/index.html?path=2.26/) |
|
||||
| 2.25 | 53.0.2785.0 | [link](https://chromedriver.storage.googleapis.com/index.html?path=2.25/) |
|
||||
| 2.24 | 52.0.2743.0 | [link](https://chromedriver.storage.googleapis.com/index.html?path=2.24/) |
|
||||
| 2.23 | 51.0.2704.0 | [link](https://chromedriver.storage.googleapis.com/index.html?path=2.23/) |
|
||||
| 2.22 | 49.0.2623.0 | [link](https://chromedriver.storage.googleapis.com/index.html?path=2.22/) |
|
||||
| 2.21 | 46.0.2490.0 | [link](https://chromedriver.storage.googleapis.com/index.html?path=2.21/) |
|
||||
| 2.20 | 43.0.2357.0 | [link](https://chromedriver.storage.googleapis.com/index.html?path=2.20/) |
|
||||
| 2.19 | 43.0.2357.0 | [link](https://chromedriver.storage.googleapis.com/index.html?path=2.19/) |
|
||||
| 2.18 | 43.0.2357.0 | [link](https://chromedriver.storage.googleapis.com/index.html?path=2.18/) |
|
||||
| 2.17 | 42.0.2311.0 | [link](https://chromedriver.storage.googleapis.com/index.html?path=2.17/) |
|
||||
| 2.16 | 42.0.2311.0 | [link](https://chromedriver.storage.googleapis.com/index.html?path=2.16/) |
|
||||
| 2.15 | 40.0.2214.0 | [link](https://chromedriver.storage.googleapis.com/index.html?path=2.15/) |
|
||||
| 2.14 | 39.0.2171.0 | [link](https://chromedriver.storage.googleapis.com/index.html?path=2.14/) |
|
||||
| 2.13 | 38.0.2125.0 | [link](https://chromedriver.storage.googleapis.com/index.html?path=2.13/) |
|
||||
| 2.12 | 36.0.1985.0 | [link](https://chromedriver.storage.googleapis.com/index.html?path=2.12/) |
|
||||
| 2.11 | 36.0.1985.0 | [link](https://chromedriver.storage.googleapis.com/index.html?path=2.11/) |
|
||||
| 2.10 | 33.0.1751.0 | [link](https://chromedriver.storage.googleapis.com/index.html?path=2.10/) |
|
||||
| 2.9 | 31.0.1650.59 | [link](https://chromedriver.storage.googleapis.com/index.html?path=2.9/) |
|
||||
| 2.8 | 30.0.1573.2 | [link](https://chromedriver.storage.googleapis.com/index.html?path=2.8/) |
|
||||
| 2.7 | 30.0.1573.2 | [link](https://chromedriver.storage.googleapis.com/index.html?path=2.7/) |
|
||||
| 2.6 | | [link](https://chromedriver.storage.googleapis.com/index.html?path=2.6/) |
|
||||
| 2.5 | | [link](https://chromedriver.storage.googleapis.com/index.html?path=2.5/) |
|
||||
| 2.4 | 29.0.1545.0 | [link](https://chromedriver.storage.googleapis.com/index.html?path=2.4/) |
|
||||
| 2.3 | 28.0.1500.0 | [link](https://chromedriver.storage.googleapis.com/index.html?path=2.3/) |
|
||||
| 2.2 | 27.0.1453.0 | [link](https://chromedriver.storage.googleapis.com/index.html?path=2.2/) |
|
||||
| 2.1 | 27.0.1453.0 | [link](https://chromedriver.storage.googleapis.com/index.html?path=2.1/) |
|
||||
| 2.0 | 27.0.1453.0 | [link](https://chromedriver.storage.googleapis.com/index.html?path=2.0/) |
|
||||
|
||||
|
||||
我们可以在[这里](https://chromedriver.storage.googleapis.com/index.html) 可以找到完整的 Chromedriver release 列表。任何一个 Chromedriver 支持的 Chrome 最小版本都可以在 [Chromium](https://www.chromium.org/Home) 项目的[源码](https://chromium.googlesource.com/chromium/src/+/master/docs/get_the_code.md)中找到。把发布那个提交给拉出来,然后在 `src/chrome/test/chromedriver/chrome/version.cc` 里找到 `kMinimumSupportedChromeVersion` 这个变量。
|
||||
|
||||
### 安装遇到的网络问题
|
||||
|
||||
Appium 安装的时候需要下载 Chromedriver,所以经常会遇到网络问题,尤其在有长城防火墙的中国。
|
||||
|
||||
Chromedriver 默认是从 `https://chromedriver.storage.googleapis.com/` 下载。如果要使用镜像的话,需要配置npm的参数 `chromedriver_cdnurl`
|
||||
|
||||
```bash
|
||||
npm install appium-chromedriver --chromedriver_cdnurl=http://npm.taobao.org/mirrors/chromedriver
|
||||
```
|
||||
|
||||
或者把这个参数加到你的 [`.npmrc`](https://docs.npmjs.com/files/npmrc) 文件中去.
|
||||
|
||||
```bash
|
||||
chromedriver_cdnurl=http://npm.taobao.org/mirrors/chromedriver
|
||||
```
|
||||
|
||||
也可以使用环境变量 `CHROMEDRIVER_CDNURL`.
|
||||
|
||||
```bash
|
||||
CHROMEDRIVER_CDNURL=http://npm.taobao.org/mirrors/chromedriver npm install appium-chromedriver
|
||||
```
|
||||
|
||||
当然最好开着代理或者vpn来下载更好。
|
||||
|
||||
本文由 [lihuazhang](https://github.com/lihuazhang) 翻译,由 [lihuazhang](https://github.com/lihuazhang) 校验。
|
||||
@@ -0,0 +1,36 @@
|
||||
## 跨域 iFrame 的自动化
|
||||
|
||||
[同源策略](https://en.wikipedia.org/wiki/Same-origin_policy) 会阻止 Appium 对和父页面不同域的 iFrame 进行自动化测试。
|
||||
|
||||
### 子域名的解决方案
|
||||
如果父页面和 iFrame 使用同一个 domain (比如 `site.com` 和 `shop.site.com`),你可以在父页面和 iFrame 里将 `document.domain` 设置为一个共同的domain。这就解决了同源问题。
|
||||
|
||||
父页面:
|
||||
```
|
||||
<html>
|
||||
<head>
|
||||
<script>
|
||||
document.domain = 'site.com';
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<iframe src="http://shop.site.com" width="200" height="200"></iframe>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
子 iFrame:
|
||||
```
|
||||
<html>
|
||||
<head>
|
||||
<script>
|
||||
document.domain = 'site.com';
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<p>This is an iFrame!</p>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
本文由 [lihuazhang](https://github.com/lihuazhang) 翻译,由 [lihuazhang](https://github.com/lihuazhang) 校验。
|
||||
@@ -0,0 +1,45 @@
|
||||
|
||||
## Appium 事件计时
|
||||
|
||||
Appium 提供了一个能力,可以获取关于启动信息和命令执行时间的计时信息。这是由 `eventTimings` 这个初始化参数控制的高级功能(把这个参数设置为 `true` 来记录事件的计时信息)
|
||||
|
||||
打开这个参数,`GET /session/:id` 这个接口的响应结果(也就是,`driver.getSessionDetails()` 的响应结果,或者类似的,取决于客户端实现)中会包含 `events` 属性。下面是 `events` 属性的结构
|
||||
|
||||
```json
|
||||
{
|
||||
"<event_type>": [<occurence_timestamp_1>, ...],
|
||||
"commands": [
|
||||
{
|
||||
"cmd": "<command_name>",
|
||||
"startTime": <js_timestamp>,
|
||||
"endTime": <js_timestamp>
|
||||
},
|
||||
...
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
换句话说,`events` 属性包含两种类别的属性。
|
||||
|
||||
* 事件类型的名字属性
|
||||
* `commands` 属性
|
||||
|
||||
事件类型的名字属性对应的是事件发生时的时间戳列表。因为在一个会话周期内,事件可能发生多次,所以时间戳是个列表。事件的类型包含:
|
||||
|
||||
* `newSessionRequested`
|
||||
* `newSessionStarted`
|
||||
|
||||
(个别 driver 会定义属于它们自己的事件类型,所以在这里我们没法列出一个完整的事件列表。最好是从一个会话得到响应结果之后去检查各种可能的事件类型。)
|
||||
|
||||
`commands` 属性是一个对象列表。每个对象都包括一个 appium 的内部命令的名字(例如 `click`),也包括这个命令执行的开始时间和结束时间。
|
||||
|
||||
通过这些数据,你可以计算出时间之间的间隔,或者事件的精确时间轴,或者某个事件的平均时间的统计信息等等。
|
||||
|
||||
你只能获得调用 `/session/:id` 这个接口期间发生的事件数据,所以获取会话周期数据的最佳时间是在你正好要退出会话之前。
|
||||
|
||||
Appium团队维护了一个事件计时解析工具,这个工具可以解析事件计时的输出,然后生成各种报告:
|
||||
[appium/appium-event-parser](https://github.com/appium/appium-event-parser).
|
||||
|
||||
|
||||
|
||||
本文由 yanqiang@douban.com 翻译,由 [lihuazhang](https://github.com/lihuazhang) 校验。
|
||||
@@ -0,0 +1,55 @@
|
||||
# Selenium Grid
|
||||
|
||||
通过 `--nodeconfig` 这个服务端参数,可以将 appium server 注册到本地的 [Selenium grid](https://code.google.com/p/selenium/wiki/Grid2) ([Grid 的配置文档](http://docs.seleniumhq.org/docs/07_selenium_grid.jsp))
|
||||
|
||||
```center
|
||||
> appium --nodeconfig /path/to/nodeconfig.json
|
||||
# 或者使用源文件执行
|
||||
> node . --nodeconfig /path/to/nodeconfig.json
|
||||
```
|
||||
|
||||
在 node 的配置文件里,你需要定义 `browserName`,`version` 和 `platform`。
|
||||
通过这些参数,selenium grid 会将你的测试定向到正确的设备上去。你还需要配置你的 `host` 详细信息和
|
||||
`selenium grid` 的详细信息。你可以在 <a href="http://code.google.com/p/selenium/source/browse/java/server/src/org/openqa/grid/common/defaults/GridParameters.properties">这里</a> 找到详细的参数列表和描述信息。
|
||||
|
||||
一旦你启动了 appium 服务器并且在 grid 里注册了信息,就可以在 grid 控制台发现你的设备:
|
||||
|
||||
"http://**\<grid-ip-adress\>**:**\<grid-port\>**/grid/console"
|
||||
|
||||
### Grid node的配置文件例子
|
||||
|
||||
```xml
|
||||
{
|
||||
"capabilities":
|
||||
[
|
||||
{
|
||||
"browserName": "<e.g._iPhone5_or_iPad4>",
|
||||
"version":"<version_of_iOS_e.g._7.1>",
|
||||
"maxInstances": 1,
|
||||
"platform":"<platform_e.g._MAC_or_ANDROID>"
|
||||
}
|
||||
],
|
||||
"configuration":
|
||||
{
|
||||
"cleanUpCycle":2000,
|
||||
"timeout":30000,
|
||||
"proxy": "org.openqa.grid.selenium.proxy.DefaultRemoteProxy",
|
||||
"url":"http://<host_name_appium_server_or_ip-address_appium_server>:<appium_port>/wd/hub",
|
||||
"host": <host_name_appium_server_or_ip-address_appium_server>,
|
||||
"port": <appium_port>,
|
||||
"maxSession": 1,
|
||||
"register": true,
|
||||
"registerCycle": 5000,
|
||||
"hubPort": <grid_port>,
|
||||
"hubHost": "<Grid_host_name_or_grid_ip-address>"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
可以在 <a href="http://selenium.googlecode.com/git/docs/api/java/org/openqa/selenium/Platform.html">这里</a>查看有效的 platform 参数。
|
||||
|
||||
如果没有给出 `url`、`host` 和 `port`,配置会自动指向 `localhost:whatever-port-Appium-started-on`。
|
||||
|
||||
如果你的 Appium Server 和 Selenium Grid 没有运行在同一台机器上,为确保 Selenium Grid 连接正常,请在你的 `host` & `url` 上使用外部域名或 IP 地址,而不是 localhost 和 127.0.0.1
|
||||
|
||||
本文由 [tobecrazy](https://github.com/tobecrazy) 翻译,由 [lihuazhang](https://github.com/lihuazhang) 校验。
|
||||
@@ -0,0 +1,143 @@
|
||||
# 自动化混合应用
|
||||
|
||||
Appium 其中一个理念就是你不能为了测试应用而修改应用。为了符合这个方法学,我们可以使用 Selenium 测试传统 web 应用的方法来测试混合 web 应用 (比如,iOS 应用里的元素 "[UIAWebView](https://developer.apple.com/library/ios/documentation/ToolsLanguages/Reference/UIAWebViewClassReference/)") ,这是有可能的。这里会有一些技术性的复杂,Appium 需要知道你是想测试原生部分呢还是web部分。幸运的是,我们还能遵守 WebDriver 的协议。
|
||||
|
||||
在 Appium 测试里,你需要以下几步来和 web 页面交涉:
|
||||
|
||||
1. 切到 webview 部分
|
||||
1. 调用 [GET session/:sessionId/contexts](https://github.com/SeleniumHQ/mobile-spec/blob/master/spec-draft.md)
|
||||
1. 这会返回一个我们能访问的 context 的列表,比如 'NATIVE_APP' 或者 'WEBVIEW_1'
|
||||
1. 用 context 的 id 作为参数,调用[POST session/:sessionId/context](https://github.com/SeleniumHQ/mobile-spec/blob/master/spec-draft.md)方法,切换到 webview 中去。
|
||||
1. (这会将你的 Appium session 放入一个模式, 在这个模式下,所有的命令都会被解释成自动化 web 视图而不是原生的部分。比如,当你运行 getElementByTagName,它会在 web 视图的 DOM 上操作,而不是返回 UIAElements。当然,一个 Webdriver 的方法只能在一个上下文中有意义,所以如果在错误的上下文,你会收到错误信息。)
|
||||
1. 如果你想停止 web 视图的自动化,回到原生部分,你可以简单地用 `context` 调用来离开 web 层,参数传原生 context id 即可。
|
||||
|
||||
```javascript
|
||||
// javascript
|
||||
// 假定你已经初始化driver
|
||||
driver
|
||||
.contexts().then(function (contexts) { // 获取view列表,返回数组格式: ["NATIVE_APP","WEBVIEW_1"]
|
||||
return driver.context(contexts[1]); // 选择 webview context
|
||||
})
|
||||
|
||||
// 执行web测试
|
||||
.elementsByCss('.green_button').click()
|
||||
|
||||
.context('NATIVE_APP') // leave webview context
|
||||
|
||||
//把native操作放这里
|
||||
|
||||
.quit() // 退出driver
|
||||
```
|
||||
|
||||
```java
|
||||
// java
|
||||
// 假定我们设置了capabilities
|
||||
driver = new AppiumDriver(new URL("http://127.0.0.1:4723/wd/hub"), capabilities);
|
||||
|
||||
Set<String> contextNames = driver.getContextHandles();
|
||||
for (String contextName : contextNames) {
|
||||
System.out.println(contextNames); //输出 NATIVE_APP \n WEBVIEW_1
|
||||
}
|
||||
driver.context(contextNames.toArray()[1]); // 设置当前 context 为 WEBVIEW_1
|
||||
|
||||
//执行web测试
|
||||
String myText = driver.findElement(By.cssSelector(".green_button")).click();
|
||||
|
||||
driver.context("NATIVE_APP");
|
||||
|
||||
//如果需要此次放置native测试脚本
|
||||
|
||||
driver.quit();
|
||||
```
|
||||
|
||||
```ruby
|
||||
# ruby
|
||||
# 假定我们设置了capabilities
|
||||
@driver = Selenium::WebDriver.for(:remote, :desired_capabilities => capabilities, :url => SERVER_URL)
|
||||
|
||||
# I switch to the last context because its always the webview in our case, in other cases you may need to specify a context
|
||||
# View the appium logs while running @driver.contexts to figure out which context is the one you want and find the associated ID
|
||||
# Then switch to it using @driver.switch_to.context("WEBVIEW_6")
|
||||
|
||||
Given(/^I switch to webview$/) do
|
||||
webview = @driver.contexts.last
|
||||
@driver.switch_to.context(webview)
|
||||
end
|
||||
|
||||
Given(/^I switch out of webview$/) do
|
||||
@driver.switch_to.context(@driver.contexts.first)
|
||||
end
|
||||
|
||||
# Now you can use CSS to select an element inside your webview
|
||||
|
||||
And(/^I click a webview button $/) do
|
||||
@driver.find_element(:css, ".green_button").click
|
||||
end
|
||||
```
|
||||
|
||||
```python
|
||||
# python
|
||||
# assuming we have an initialized `driver` object for an app
|
||||
|
||||
# switch to webview
|
||||
webview = driver.contexts.last
|
||||
driver.switch_to.context(webview)
|
||||
|
||||
# do some webby stuff
|
||||
driver.find_element(:css, ".green_button").click
|
||||
|
||||
# switch back to native view
|
||||
driver.switch_to.context(driver.contexts.first)
|
||||
|
||||
# do more native testing if we want
|
||||
|
||||
driver.quit()
|
||||
```
|
||||
|
||||
```php
|
||||
// php
|
||||
// assuming we have an initialized `driver` object in an AppiumTestCase
|
||||
|
||||
public function testThings()
|
||||
{
|
||||
$expected_contexts = array(
|
||||
0 => 'NATIVE_APP',
|
||||
1 => 'WEBVIEW_1'
|
||||
);
|
||||
|
||||
$contexts = $this->contexts();
|
||||
$this->assertEquals($expected_contexts, $contexts);
|
||||
|
||||
$this->context($contexts[1]);
|
||||
$context = $this->context();
|
||||
$this->assertEquals('WEBVIEW_1', $context);
|
||||
|
||||
// do webby stuff
|
||||
|
||||
$this->context('NATIVE_APP');
|
||||
|
||||
// do mobile stuff
|
||||
}
|
||||
```
|
||||
## 自动化 Android 的混合应用
|
||||
|
||||
Appium 通过 Chromedriver 内建混合应用支持。Appium 也可以使用 Selendroid 支持 4.4 之前的设备的 webview 测试。(你需要在 desired capability 里指定 `"device": "selendroid"`)。
|
||||
|
||||
请确保
|
||||
[setWebContentsDebuggingEnabled](http://developer.android.com/reference/android/webkit/WebView.html#setWebContentsDebuggingEnabled(boolean)) 设置为 true。具体见[remote debugging docs](https://developer.chrome.com/devtools/docs/remote-debugging#configure-webview).
|
||||
|
||||
一旦你设置了 desired capabilities,开始一个 appium 会话,遵循上面的教程。
|
||||
|
||||
## 自动化 iOS 混合应用
|
||||
|
||||
Appium 使用 remote debugger 创建和 webview 的交互连接。当在模拟器上执行下面例子的时候,我们可以直接建立连接,因为模拟器和 appium 服务器在同一台机器上。
|
||||
|
||||
一旦你设置了 desired capabilities,开始一个 appium 会话,遵循上面的教程。
|
||||
|
||||
## 在 iOS 真机上运行
|
||||
|
||||
当在真机上运行用例时,appium 无法直接访问 web 视图,所以我们需要通过 USB 线缆来建立连接。我们使用 [ios-webkit-debugger-proxy](https://github.com/google/ios-webkit-debug-proxy)建立连接。
|
||||
|
||||
如何安装和使用 iOS webkit debug proxy,请参考 [iOS webkit debug proxy](/docs/cn/advanced-concepts/ios-webkit-debug-proxy.md)
|
||||
|
||||
本文由 [tobecrazy](https://github.com/tobecrazy) 翻译,由 [lihuazhang](https://github.com/lihuazhang) 校验。
|
||||
@@ -0,0 +1,73 @@
|
||||
## iOS WebKit 调试代理
|
||||
|
||||
借助 [ios_webkit_debug_proxy](https://github.com/google/ios-webkit-debug-proxy),Appium 可以在 iOS 真机上访问 webview。
|
||||
|
||||
### 安装
|
||||
|
||||
#### 使用 Homebrew
|
||||
|
||||
在终端执行以下命令使用 Homebrew 安装最新版本的 ios-webkit-debug-proxy:
|
||||
|
||||
``` center
|
||||
# 没有安装 brew 时才需要第一条命令。
|
||||
> ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
|
||||
> brew update
|
||||
> brew install ios-webkit-debug-proxy
|
||||
```
|
||||
|
||||
#### 从源码构建 ios-webkit-debug-proxy
|
||||
|
||||
在你的 mac 上打开终端。你可以通过喜欢的搜索引擎查找如何打开终端的操作指南。打开后确认你已经安装了 [Homebew](http://brew.sh/):
|
||||
|
||||
```shell
|
||||
$ brew -v
|
||||
```
|
||||
|
||||
确认 Homebrew 已安装后,按下面的做( $ 是命令行提示符,不用输入):
|
||||
|
||||
```shell
|
||||
$ cd ~
|
||||
$ sudo apt-get install autoconf automake libusb-dev libusb-1.0-0-dev libplist-dev libplist++-dev usbmuxd libtool libimobiledevice-dev
|
||||
$ git clone https://github.com/google/ios-webkit-debug-proxy.git
|
||||
$ cd ios-webkit-debug-proxy
|
||||
$ ./autogen.sh
|
||||
$ make
|
||||
$ sudo make install
|
||||
```
|
||||
|
||||
#### 运行 ios-webkit-debug-proxy
|
||||
|
||||
安装后使用以下命令启动代理:
|
||||
|
||||
``` center
|
||||
# 修改 udid 为目标设备的 udid 并确认 remote-debugger 使用 27753 端口。
|
||||
# 你可以从苹果开发者资源平台学习如何获取 UDID 。
|
||||
> ios_webkit_debug_proxy -c 0e4b2f612b65e98c1d07d22ee08678130d345429:27753 -d
|
||||
```
|
||||
|
||||
你也可以设置 'startIWDP' desired capability 为 true (https://github.com/appium/appium/blob/master/docs/cn/writing-running-appium/caps.md)。Appium 会开启一个子进程运行上述命令并设置 udid,所以你不再需要自己运行 ios_webkit_debug_proxy 。Appium 会监控 ios-webkit-debug-proxy,一旦崩溃就会重启。
|
||||
|
||||
``` center
|
||||
// desired capabilities 示例
|
||||
{
|
||||
"browserName": "Safari",
|
||||
"platformName": "iOS",
|
||||
"deviceName": "iPhone 7",
|
||||
"automationName": "XCUITest",
|
||||
"startIWDP": true,
|
||||
"udid": "auto"
|
||||
}
|
||||
```
|
||||
|
||||
你也可以使用 `ios-webkit-debug-proxy-launcher` —— Appium 代码库中的一个小脚本 —— 来启动代理。它会监控日志中的错误,并在需要时重启代理。
|
||||
|
||||
``` center
|
||||
# 修改 udid
|
||||
# 注意,在 Appium 仓库中运行
|
||||
> ./bin/ios-webkit-debug-proxy-launcher.js -c 0e4b2f612b65e98c1d07d22ee08678130d345429:27753 -d
|
||||
```
|
||||
|
||||
**注意:** 为了允许建立连接,设备上需要打开 **"Web 检查器"**。在 **设置 >
|
||||
safari > 高级** 中打开。Web 检查器作为 iOS6 的一部分被添加,不在之前的版本中提供,请知悉。
|
||||
|
||||
本文由 [sanlengjingvv](https://testerhome.com/sanlengjingvv) 翻译,由 [lihuazhang](https://github.com/lihuazhang) 校验。
|
||||
@@ -0,0 +1,17 @@
|
||||
## 为 xcode 7 和 iOS >= 9.0 配置无延迟 instruments(iwd)
|
||||
|
||||
iOS >= 9.0 时,需要手动通过命令行传递二进制文件来配置无延迟的 instruments (在 xcode < 7 时 appium 会自动加上 ),参阅 [iwd](https://github.com/lawrencelomax/instruments-without-delay/tree/xcode7-quirks#xcode-7--ios-9-support)
|
||||
|
||||
在 xcode >= 7 时启用 iwd :
|
||||
- 下载 [appium-instruments](https://github.com/appium/appium-instruments)
|
||||
- 在 `<appium-instruments>/bin/` 下根据以下指示的参数运行 `xcode-iwd.sh` :
|
||||
|
||||
```
|
||||
sh <appium-instruments>/bin/xcode-iwd.sh <path to xcode> <path to appium-instruments>
|
||||
```
|
||||
比如: `sh ./bin/xcode-iwd.sh /Applications/Xcode.app /Users/xyz/appium-instruments/`
|
||||
|
||||
注意:Xcode 7 的 iwd 只在 iOS >= 9.0 时有效,你可以在 iOS < 9.0 时切换一个旧版本的 Xcode
|
||||
|
||||
由 @黑水 翻译,TesterHome 社区 id:sanlengjingvv
|
||||
由 [@chenhengjie123](https://github.com/chenhengjie123) 校验
|
||||
@@ -0,0 +1,168 @@
|
||||
*这是一个旧文档,只有从早期版本迁移到 Appium 1.x 的用户才有兴趣。*
|
||||
|
||||
## 将您的测试从 Appium 0.18.x 迁移到 Appium 1.x
|
||||
|
||||
Appium 1.0 中删除了许多之前版本就不推荐使用的功能。 本指南将帮助您了解在测试套件中需要做哪些修改才可以很好利用Appium 1.0。
|
||||
|
||||
### 新的客户端库
|
||||
|
||||
The biggest thing you need to worry about is using the new Appium client libraries instead of the vanilla WebDriver clients you are currently using. Visit the [Appium client list](/docs/cn/about-appium/appium-clients.md) to find the client for your language. Downloads and instructions for integrating into your code are available on the individual client websites.
|
||||
|
||||
您最需要担心的是使用新的 Appium 客户端库,而不是您正在使用的普通 WebDriver 客户端。 访问[Appium客户端列表](/docs/cn/about-appium/appium-clients.md)以找到匹配您的语言的客户端。 你可以在各个客户端网站上找到下载地址和如何集成到你的测试代码的教程。
|
||||
|
||||
最终,你会做以下一些事情(以Python为例):
|
||||
|
||||
```center
|
||||
from appium import webdriver
|
||||
```
|
||||
|
||||
代替:
|
||||
|
||||
```center
|
||||
from selenium import webdriver
|
||||
```
|
||||
|
||||
### 新的 desired capabilities
|
||||
|
||||
不再使用以下的 capabilities:
|
||||
|
||||
* `device`
|
||||
* `version`
|
||||
|
||||
使用这些功能替换:
|
||||
|
||||
* `platformName`(“iOS”或“Android”)
|
||||
* `platformVersion`(你想要的手机操作系统版本)
|
||||
* `deviceName`(你想要的那种设备,就像“iPhone模拟器”)
|
||||
* `automationName`(“Selendroid”如果你想使用,可以使用Selendroid,除此之外,可以省略这一点)
|
||||
|
||||
`app` capability 保持不变,但现在专指非浏览器 app。 要使用 Safari 或 Chrome 等浏览器,请使用标准的 `browserName` capability。这意味着 `app` 和 `browserName` 是互斥的。
|
||||
|
||||
针对 Appium 服务端的 capability,我们统一了驼峰式风格。这意味着像 `app-package` 或 `app-wait-activity` 这样的上限现在分别是 `appPackage` 和 `appWaitActivity`。当然,由于 Android 应用的包名和 activity 现在已被自动检测到,在大多数情况下,您应该可以完全省略它们。
|
||||
|
||||
### 新的定位策略
|
||||
|
||||
我们删除了以下定位器策略:
|
||||
|
||||
* `name`
|
||||
* `tag name`
|
||||
|
||||
我们添加了 `accessibility_id` 策略来替代 `name` 曾经的作用。具体细节将与您的 Appium 客户端相关。
|
||||
|
||||
`tag name` 已被 `class name`替换。 所以要通过 UI 类型找到一个元素,在你的客户脚本里使用类名定位器策略。
|
||||
|
||||
Note about `class name` and `xpath` strategies: these now require the fully-qualified class name for your element. This means that if you had an xpath selector that looked like this:
|
||||
|
||||
关于 `class name` 和 `xpath` 策略的注意事项:这些策略现在需要元素完全限定类名。 这意味着如果你有一个如下所示的 xpath 选择器:
|
||||
|
||||
```center
|
||||
//table/cell/button
|
||||
```
|
||||
|
||||
现在需要:
|
||||
|
||||
```center
|
||||
//UIATableView/UIATableCell/UIAButton
|
||||
```
|
||||
|
||||
(同样对于Android:`button` 现在需要是 `android.widget.Button`)
|
||||
|
||||
我们还添加了以下定位器策略:
|
||||
|
||||
* `-ios uiautomation`
|
||||
* `-android uiautomator`
|
||||
|
||||
参考你的客户端了解如何使用这些新的定位策略。
|
||||
|
||||
### XML, not JSON
|
||||
|
||||
App source methods, which previously returned JSON, now return XML, so if you have code that relies on parsing the app source, it will need to be updated.
|
||||
|
||||
以前返回 JSON 的 app source 方法现在返回 XML,因此如果您有代码依赖于解析 app source,则需要更新。
|
||||
|
||||
### Hybrid 通过 context 支持,而不是 window
|
||||
|
||||
以前通过在 "windows" 之间切换支持 hybrid app
|
||||
|
||||
* `window_handles`
|
||||
* `window`
|
||||
* `switch_to.window`
|
||||
|
||||
现在 Appium 使用概念上更加贴切的 "context"。要获得所有可用的 context,或应用程序特定的 context,您可以使用以下方法
|
||||
|
||||
```python
|
||||
# python
|
||||
driver.contexts
|
||||
current = driver.context
|
||||
```
|
||||
|
||||
```javascript
|
||||
// javascript
|
||||
driver.contexts().then(function (contexts) { /*...*/ })
|
||||
```
|
||||
|
||||
```c#
|
||||
// c#
|
||||
driver.GetContexts ()
|
||||
driver.GetContext ()
|
||||
```
|
||||
|
||||
```java
|
||||
// java
|
||||
Set<String> contextNames = driver.getContextHandles();
|
||||
String context = driver.getContext();
|
||||
```
|
||||
|
||||
```php
|
||||
// php
|
||||
$contexts = $this->contexts();
|
||||
$context = $this->context();
|
||||
```
|
||||
|
||||
```ruby
|
||||
# ruby
|
||||
contexts = available_contexts
|
||||
context = current_context
|
||||
```
|
||||
|
||||
在他们之间切换,你使用以下方法
|
||||
|
||||
```python
|
||||
# python
|
||||
driver.switch_to.context("WEBVIEW")
|
||||
```
|
||||
|
||||
```javascript
|
||||
// javascript
|
||||
driver.currentContext().then(function (context) { /*...*/ })
|
||||
```
|
||||
|
||||
```c#
|
||||
// c#
|
||||
driver.SetContext ("WEBVIEW");
|
||||
```
|
||||
|
||||
```java
|
||||
java
|
||||
driver.context(contextName);
|
||||
```
|
||||
|
||||
```php
|
||||
// php
|
||||
$this->context('WEBVIEW');
|
||||
```
|
||||
|
||||
```ruby
|
||||
# ruby
|
||||
set_context "WEBVIEW"
|
||||
```
|
||||
|
||||
### 不再有 `execute_script("mobile: xxx")`
|
||||
|
||||
所有 `mobile: ` 方法已被删除,并已被 Appium 客户端库中的原生方法所替代。这意味着像 `driver.execute("mobile: lock", [5])` 的方法调用,现在可能会是 `driver.lock(5)`(其中 `lock` 已经变成原生的客户端方法)。 当然,调用这些方法的细节会因客户端而异。
|
||||
|
||||
特别值得注意的是,手势方法已被新的 TouchAction / MultiAction API 所取代,该 API 允许一种更强大更普遍的方法将手势自动化放在一起。有关TouchAction / MultiAction 的使用说明,请参考您的Appium客户端。
|
||||
|
||||
就是这样!快乐的迁移!
|
||||
|
||||
本文由 [高鹏](https://testerhome.com/026) 翻译,由 [lihuazhang](https://github.com/lihuazhang) 校验。
|
||||
@@ -0,0 +1,126 @@
|
||||
## 将你的iOS测试从UIAutomation(iOS 9.3及更低版本)迁移到XCUITest(iOS 9.3及更高版本)
|
||||
|
||||
对于 iOS 自动化,Appium 依赖苹果提供的系统框架。对于 iOS 9.2 及更低版本,苹果唯一的自动化技术被称为UIAutomation,它运行在 “Instruments” 中。从 iOS 10 开始,苹果已经完全删除了 UIAutomation 工具,因此 Appium 不可能按照以前的方式进行测试。同时,苹果推出了一款名为 XCUITest 的新型自动化技术,从 iOS 9.3 到 iOS 10 及以上版本,这将是苹果唯一支持的自动化框架。
|
||||
|
||||
Appium 从 Appium 1.6 开始支持 XCUITest。在大多数情况下,XCUITest 的功能与 UIAutomation 的功能相匹配,因此,Appium 团队能够确保测试行为保持不变。这是使用 Appium 的好处之一!即使苹果完全改变了测试使用的技术,你的脚本可以保持大致相同!话虽如此,如果您想在 XCUITest 运行它们,还是存在些许差异,您需要关注测试脚本中需要修改的部分。本文将帮助您解决这些差异。
|
||||
|
||||
### 元素类名称模式
|
||||
|
||||
在 XCUITest 中,苹果已经为构成视图层次结构的 UI 元素提供了不同的类名。例如,`UIAButton` 现在为`XCUIElementTypeButton`。在很多情况下,这两个类之间有直接映射。如果您使用 `class name` 定位器策略来查找元素,Appium 1.6 将为您重写选择器。同样,如果你使用xpath定位器策略,Appium 1.6 将在 XPath 字符串中找到所有 `UIA*` 元素,并适当地重写它们。
|
||||
|
||||
但是,这并不能保证你的测试可以完全相同地运行,原因有两个:
|
||||
|
||||
1. Appium 通过 XCUITest 和 UIAutomation 看到的应用的层次结构不一定是相同的。如果你有基于路径的 XPath 选择器,则可能需要进行调整。
|
||||
|
||||
2. 类名列表也不完全一样。许多由 XCUITest 返回的元素属于`XCUIElementTypeOther类`,这是一种全新的容器。
|
||||
|
||||
### 页面源码
|
||||
|
||||
如上述,如果你依赖 `page source` 命令返回的 app 源 XML,那么这个输出的 XML 会与基于 UIAutomation 的结果会有显著不同。
|
||||
|
||||
### `-ios uiautomation` 定位策略
|
||||
|
||||
此定位器策略专门用于 UIAutomation,因此它不包括在 XCUITest 中。 在即将发布的版本中,我们将致力于类似的 “native” 型定位策略。
|
||||
|
||||
### `xpath` 定位策略
|
||||
|
||||
1. 尽量不要使用 XPath 定位器,除非你完全没有其他选择。 通常,xpath 定位器可能比其他类型的定位器慢,比如accessibility id,类名和谓词(在某些特殊情况下可减缓100倍)。 它们太慢了,因为 xpath 的位置不是苹果的XCTest 框架本身所支持的。
|
||||
|
||||
2. 使用
|
||||
|
||||
```
|
||||
driver.findElement(x)
|
||||
```
|
||||
|
||||
而不是使用
|
||||
|
||||
```
|
||||
driver.findElements(x)[0]
|
||||
```
|
||||
|
||||
通过xpath查找单个元素。 您的定位器匹配的UI元素越多,你的脚本越慢。
|
||||
|
||||
3. 在通过 xpath 定位元素时,一定要使用非常具体的xpath。 像这样的定位器
|
||||
|
||||
```
|
||||
//*
|
||||
```
|
||||
|
||||
可能需要几分钟才能完成,具体取决于您的应用程序有多少用户界面元素(例如,
|
||||
|
||||
```
|
||||
driver.findElement(By.xpath("//XCUIElementTypeButton[@value='blabla']"))
|
||||
```
|
||||
|
||||
比
|
||||
|
||||
```
|
||||
driver.findElement(By.xpath("//*[@value='blabla']"))
|
||||
```
|
||||
|
||||
或
|
||||
|
||||
```
|
||||
driver.findElement(By.xpath("//XCUIElementTypeButton")))
|
||||
```
|
||||
|
||||
要快很多。
|
||||
|
||||
4. 在大多数情况下,执行多个嵌套的 findElement 调用比执行 xpath 单个调用更快(例如,
|
||||
|
||||
```
|
||||
driver.findElement(x).findElement(y)
|
||||
```
|
||||
通常比
|
||||
|
||||
```
|
||||
driver.findElement(z)
|
||||
```
|
||||
|
||||
要快
|
||||
|
||||
其中x和y不是xpath定位符,z是xpath定位符)。
|
||||
|
||||
### 系统依赖
|
||||
|
||||
除了升级 XCode 会带来(与Appium无关)许多问题之外,Appium 对 XCUITest 的支持还需要一个新的系统依赖:[Carthage](https://github.com/Carthage/Carthage)。Appium Doctor 现已更新,以确保`carthage`二进制在你的系统路径里。
|
||||
|
||||
### API差异
|
||||
|
||||
Unfortunately, the XCUITest API and the UIAutomation API are not equivalent. In many cases (like with `tap/click`), the behavior is identical. But some features that were available in the UIAutomation backend are not yet available in the new XCUITest backend. These known lacking features include:
|
||||
|
||||
不幸的是,XCUITest API 和 UIAutomation API 还是有差别的。在许多情况下(比如 tap/click),行为是相同的,但在 UIAutomation 作为底层驱动时,可用的某些功能在新的 XCUITest 时尚不可用。下面是已知的缺乏的功能:
|
||||
|
||||
* 地理位置支持(例如: `driver.location`)
|
||||
* 振动设备
|
||||
* 锁定设备
|
||||
* 旋转设备(device _orientation_ 是支持的)
|
||||
|
||||
|
||||
We will endeavor to add these features back in future releases of Appium.
|
||||
|
||||
我们在努力把这些功能加到 Appium 的未来版本里。
|
||||
|
||||
#### 滚动和点击
|
||||
|
||||
在之前基于 UIAutomation 的驱动中,如果您尝试单击不在视图中的元素,UIAutomation 将自动滚动到该元素,然后点击它。 使用 XCUITest,不是这样。 点击之前,你需要确保元素可见(与用户看到才能点击的行为一致)。
|
||||
|
||||
### 其他已知问题
|
||||
|
||||
Finally, a list of known issues with the initial 1.6 release (we'll strike through issues which have been resolved):
|
||||
|
||||
最后,列出了初始 1.6 版本的已知问题(已解决的问题会被横线划去):
|
||||
|
||||
* ~~无法以横向模式与设备上的元素进行交互(https://github.com/appium/appium/issues/6994)~~
|
||||
* `shake` 苹果不支持所以我们没有实现
|
||||
* `lock` 苹果不支持所以我们没有实现
|
||||
* 设置地理位置不被苹果支持,我们也不支持
|
||||
* 通过TouchAction / MultiAction API,`zoom` 手势支持,因为苹果的一个bug,`pinch` 手势不支持。
|
||||
* ~~通过TouchAction / MultiAction API,`swipe`手势目前不受支持,应该很快解决(https://github.com/appium/appium/issues/7573)~~
|
||||
* `autoAcceptAlerts`, `autoDismissAlerts`目前还不能工作,而且我们是否能够在将来实施这些,存在争议。
|
||||
* iOS SDK 有一个问题,因此使用某些 API 方法构建的 PickerWheels 不能由 XCUITest 自动执行。有关解决方法,请参阅https://github.com/appium/appium/issues/6962,以确保您的 PickerWheels 正确构建。
|
||||
|
||||
我们将尽可能添加缺失的功能,并在以后的 Appium 版本中修复其他已知问题。
|
||||
|
||||
|
||||
本文由 [校长](https://testerhome.com/xushizhao) 翻译,由 [lihuazhang](https://github.com/lihuazhang) 校验。
|
||||
@@ -0,0 +1,58 @@
|
||||
## Settings
|
||||
Settings是appium引入的一个新概念。 它们目前不是Mobile JSON Wire协议或Webdriver规范的一部分。
|
||||
|
||||
Settings是用来指定appium server的工作方式。
|
||||
|
||||
Settings有以下特点:
|
||||
- 可变性,Settings在一个会话中是可以被修改的。
|
||||
- 临时性,Settings只对当前会话生效,新建立的会话会被重置。
|
||||
- 局限性,Settings只用来控制appium server,不能用于控制被测应用或设备。
|
||||
|
||||
以Android的`ignoreUnimportantViews`为例。Android中可以设置`ignoreUnimportantViews`用来忽略所有与当前视图无关的元素,这样可以让用例执行的更快。注意,当用户需要访问这些被忽略的元素时,需要禁用`ignoreUnimportantViews`后并重新启用。
|
||||
|
||||
另外一个例子是Settings让appium忽略当前不可见的元素。
|
||||
|
||||
Settings通过下面的API实现:
|
||||
|
||||
**POST** /session/:sessionId/appium/settings
|
||||
|
||||
>Settings使用键值对(name:value)的JSON格式,name为setting的名字,value是setting的取值。
|
||||
```
|
||||
{
|
||||
settings: {
|
||||
ignoreUnimportantViews : true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**GET** /session/:sessionId/appium/settings
|
||||
|
||||
>以JSON格式返回当前所有配置。
|
||||
```
|
||||
{
|
||||
ignoreUnimportantViews : true
|
||||
}
|
||||
```
|
||||
|
||||
注意,实际的命令因您的脚本所使用的语言而异; 请参阅具体的Appium客户端文档以获取更多信息。
|
||||
|
||||
### 支持的Settings
|
||||
|
||||
**"ignoreUnimportantViews"** - 布尔类型。设置为false时,Android设备不会忽略任何视图;被设置为true时,会使用`setCompressedLayoutHeirarchy()`忽略标记了IMPORTANT_FOR_ACCESSIBILITY_NO或IMPORTANT_FOR_ACCESSIBILITY_AUTO(以及被系统认为不重要的)的视图,从而尽量让脚本变得简单或执行的更快。
|
||||
|
||||
#### Android UiAutomator Configurator
|
||||
|
||||
设置[UiAutomator Configurator](https://developer.android.com/reference/android/support/test/uiautomator/Configurator.html) 的超时时间和延迟. 只支持Android API 18 及以上版本。
|
||||
|
||||
**"actionAcknowledgmentTimeout"** - Int类型,与[setActionAcknowledgmentTimeout](https://developer.android.com/reference/android/support/test/uiautomator/Configurator.html#setActionAcknowledgmentTimeout(long))相同。被设置为负数时将取默认值(3 * 1000 毫秒)
|
||||
|
||||
**"keyInjectionDelay"** - Int类型,与[setKeyInjectionDelay](https://developer.android.com/reference/android/support/test/uiautomator/Configurator.html#setKeyInjectionDelay(long))相同。被设置为负数时将取默认值(0 毫秒)
|
||||
|
||||
**"scrollAcknowledgmentTimeout"** - Int类型,与[setScrollAcknowledgmentTimeout](https://developer.android.com/reference/android/support/test/uiautomator/Configurator.html#setScrollAcknowledgmentTimeout(long))相同。被设置为负数时将取默认值(200 毫秒)
|
||||
|
||||
**"waitForIdleTimeout"** - Int类型,与[setWaitForIdleTimeout](https://developer.android.com/reference/android/support/test/uiautomator/Configurator.html#setWaitForIdleTimeout(long))相同。被设置为负数时将取默认值(10 * 1000 毫秒)
|
||||
|
||||
**"waitForSelectorTimeout"** - Int类型,与[setWaitForSelectorTimeout](https://developer.android.com/reference/android/support/test/uiautomator/Configurator.html#setWaitForSelectorTimeout(long))相同。被设置为负数时将取默认值(10 * 1000 毫秒)
|
||||
|
||||
|
||||
本文由 [ghost62184](https://github.com/ghost62184) 翻译, 由 [chenhengjie123](https://github.com/chenhengjie123) 校验
|
||||
@@ -0,0 +1,286 @@
|
||||
## 如何搭建并定制 WebDriverAgent Server
|
||||
|
||||
Appium 的 iOS 版本的后端用的是[Facebook's WebDriverAgent](https://github.com/facebook/WebDriverAgent)。该后端是基于苹果公司的 XCTest 框架,所以也有所有XCTest 框架已知的问题。其中有些问题我们正在设法解决,有一些在现阶段可能无法解决。本文中描述的方法已经能够使您完全掌握在设备上如何构建、管理和运行WDA。通过这种方式,您可以在CI环境中对您的自动化测试进行微调,并使其在长期运行的情况下更加稳定。
|
||||
|
||||
重点:
|
||||
* 如果使用了Appium的默认设置,则不需要如下的步骤。服务器将为您搞定一切,当然你也不能对WDA做太多控制。
|
||||
* 对连接的被测设备必须有SSH或物理访问权限。
|
||||
|
||||
### 安装WDA
|
||||
|
||||
Appium 会自动下载 WebDriverAgent 源码。如果使用 npm 命令(`npm install -g appium`) 安装Appium的话,通常情况下会保存在/usr/local/lib/node_modules/appium/node_modules/appium-xcuitest-driver/WebDriverAgent 目录下。
|
||||
如果是首次安装的话,还需要下载一些第三方依赖("carthage"工具就是为此准备的: `brew install carthage`):
|
||||
|
||||
```bash
|
||||
cd /usr/local/lib/node_modules/appium/node_modules/appium-xcuitest-driver/WebDriverAgent
|
||||
./Scripts/bootstrap.sh -d
|
||||
```
|
||||
|
||||
不需要进一步的配置步骤,你就可以在iOS模拟器上执行自动化测试。
|
||||
|
||||
如果是在真机上进行测试的话,则需要做更多的设置。参考[real device configuration documentation](https://github.com/appium/appium-xcuitest-driver/blob/master/docs/real-device-config.md) 设置代码签名。另外,你还需要安装iproxy工具。
|
||||
|
||||
```bash
|
||||
npm install -g iproxy
|
||||
```
|
||||
|
||||
为了确保 WDA 源代码配置正确,请执行以下操作:
|
||||
|
||||
* 用Xcode打开/usr/local/lib/node_modules/appium/node_modules/appium-xcuitest-driver/WebDriverAgent/WebDriverAgent.xcodeproj
|
||||
* 选择 "WebDriverAgentRunner" 工程
|
||||
* 选择要运行自动化测试的真机/模拟器作为构建目标机
|
||||
* 在主菜单中选择 Product -> Test
|
||||
|
||||
Xcode 会成功构建项目并安装到真机/模拟器上,所以您将在苹果系统的桌面上看到 WebDriverAgentRunner 应用程序的图标。
|
||||
|
||||
### 启动WDA
|
||||
|
||||
WebDriverAgent 应用程序扮演一个 REST 服务的角色,接收外部 API 请求,然后传递给被测应用的原生 XCTest 调用。如果在模拟器上运行你的测试,REST 服务的地址将是localhost,如果在有实际的 IP 地址的真实设备上运行,REST 服务的地址将是实际的 ip 地址。我们使用 iproxy 将网络请求路由到通过 USB 连接的真实设备上,这意味着可以使用这个工具将模拟器和真实设备上的 WDA 网络地址统一。
|
||||
|
||||
这个用Java编写的助手类说明了主要的实现细节:
|
||||
|
||||
```java
|
||||
public class WDAServer {
|
||||
private static final Logger log = ZLogger.getLog(WDAServer.class.getSimpleName());
|
||||
|
||||
private static final int MAX_REAL_DEVICE_RESTART_RETRIES = 1;
|
||||
private static final Timedelta REAL_DEVICE_RUNNING_TIMEOUT = Timedelta.ofMinutes(4);
|
||||
private static final Timedelta RESTART_TIMEOUT = Timedelta.ofMinutes(1);
|
||||
|
||||
// These settings are needed to properly sign WDA for real device tests
|
||||
// See https://github.com/appium/appium-xcuitest-driver for more details
|
||||
private static final File KEYCHAIN = new File(String.format("%s/%s",
|
||||
System.getProperty("user.home"), "/Library/Keychains/MyKeychain.keychain"));
|
||||
private static final String KEYCHAIN_PASSWORD = "******";
|
||||
|
||||
private static final File IPROXY_EXECUTABLE = new File("/usr/local/bin/iproxy");
|
||||
private static final File XCODEBUILD_EXECUTABLE = new File("/usr/bin/xcodebuild");
|
||||
private static final File WDA_PROJECT =
|
||||
new File("/usr/local/lib/node_modules/appium/node_modules/appium-xcuitest-driver/" +
|
||||
"WebDriverAgent/WebDriverAgent.xcodeproj");
|
||||
private static final String WDA_SCHEME = "WebDriverAgentRunner";
|
||||
private static final String WDA_CONFIGURATION = "Debug";
|
||||
private static final File XCODEBUILD_LOG = new File("/usr/local/var/log/appium/build.log");
|
||||
private static final File IPROXY_LOG = new File("/usr/local/var/log/appium/iproxy.log");
|
||||
|
||||
private static final int PORT = 8100;
|
||||
public static final String SERVER_URL = String.format("http://127.0.0.1:%d", PORT);
|
||||
|
||||
private static final String[] IPROXY_CMDLINE = new String[]{
|
||||
IPROXY_EXECUTABLE.getAbsolutePath(),
|
||||
Integer.toString(PORT),
|
||||
Integer.toString(PORT),
|
||||
String.format("> %s 2>&1 &", IPROXY_LOG.getAbsolutePath())
|
||||
};
|
||||
|
||||
private static WDAServer instance = null;
|
||||
private final boolean isRealDevice;
|
||||
private final String deviceId;
|
||||
private final String platformVersion;
|
||||
private int failedRestartRetriesCount = 0;
|
||||
|
||||
private WDAServer() {
|
||||
try {
|
||||
this.isRealDevice = !getIsSimulatorFromConfig(getClass());
|
||||
final String udid;
|
||||
if (isRealDevice) {
|
||||
udid = IOSRealDeviceHelpers.getUDID();
|
||||
} else {
|
||||
udid = IOSSimulatorHelpers.getId();
|
||||
}
|
||||
this.deviceId = udid;
|
||||
this.platformVersion = getPlatformVersionFromConfig(getClass());
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
ensureToolsExistence();
|
||||
ensureParentDirExistence();
|
||||
}
|
||||
|
||||
public synchronized static WDAServer getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new WDAServer();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
private boolean waitUntilIsRunning(Timedelta timeout) throws Exception {
|
||||
final URL status = new URL(SERVER_URL + "/status");
|
||||
try {
|
||||
if (timeout.asSeconds() > 5) {
|
||||
log.debug(String.format("Waiting max %s until WDA server starts responding...", timeout));
|
||||
}
|
||||
new UrlChecker().waitUntilAvailable(timeout.asMillis(), TimeUnit.MILLISECONDS, status);
|
||||
return true;
|
||||
} catch (UrlChecker.TimeoutException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static void ensureParentDirExistence() {
|
||||
if (!XCODEBUILD_LOG.getParentFile().exists()) {
|
||||
if (!XCODEBUILD_LOG.getParentFile().mkdirs()) {
|
||||
throw new IllegalStateException(String.format(
|
||||
"The script has failed to create '%s' folder for Appium logs. " +
|
||||
"Please make sure your account has correct access permissions on the parent folder(s)",
|
||||
XCODEBUILD_LOG.getParentFile().getAbsolutePath()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ensureToolsExistence() {
|
||||
if (isRealDevice && !IPROXY_EXECUTABLE.exists()) {
|
||||
throw new IllegalStateException(String.format("%s tool is expected to be installed (`npm install -g iproxy`)",
|
||||
IPROXY_EXECUTABLE.getAbsolutePath()));
|
||||
}
|
||||
if (!XCODEBUILD_EXECUTABLE.exists()) {
|
||||
throw new IllegalStateException(String.format("xcodebuild tool is not detected on the current system at %s",
|
||||
XCODEBUILD_EXECUTABLE.getAbsolutePath()));
|
||||
}
|
||||
if (!WDA_PROJECT.exists()) {
|
||||
throw new IllegalStateException(String.format("WDA project is expected to exist at %s",
|
||||
WDA_PROJECT.getAbsolutePath()));
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> generateXcodebuildCmdline() {
|
||||
final List<String> result = new ArrayList<>();
|
||||
result.add(XCODEBUILD_EXECUTABLE.getAbsolutePath());
|
||||
result.add("clean build test");
|
||||
result.add(String.format("-project %s", WDA_PROJECT.getAbsolutePath()));
|
||||
result.add(String.format("-scheme %s", WDA_SCHEME));
|
||||
result.add(String.format("-destination id=%s", deviceId));
|
||||
result.add(String.format("-configuration %s", WDA_CONFIGURATION));
|
||||
result.add(String.format("IPHONEOS_DEPLOYMENT_TARGET=%s", platformVersion));
|
||||
result.add(String.format("> %s 2>&1 &", XCODEBUILD_LOG.getAbsolutePath()));
|
||||
return result;
|
||||
}
|
||||
|
||||
private static List<String> generateKeychainUnlockCmdlines() throws Exception {
|
||||
final List<String> result = new ArrayList<>();
|
||||
result.add(String.format("/usr/bin/security -v list-keychains -s %s", KEYCHAIN.getAbsolutePath()));
|
||||
result.add(String.format("/usr/bin/security -v unlock-keychain -p %s %s",
|
||||
KEYCHAIN_PASSWORD, KEYCHAIN.getAbsolutePath()));
|
||||
result.add(String.format("/usr/bin/security set-keychain-settings -t 3600 %s", KEYCHAIN.getAbsolutePath()));
|
||||
return result;
|
||||
}
|
||||
|
||||
public synchronized void restart() throws Exception {
|
||||
if (isRealDevice && failedRestartRetriesCount >= MAX_REAL_DEVICE_RESTART_RETRIES) {
|
||||
throw new IllegalStateException(String.format(
|
||||
"WDA server cannot start on the connected device with udid %s after %s retries. " +
|
||||
"Reboot the device manually and try again", deviceId, MAX_REAL_DEVICE_RESTART_RETRIES));
|
||||
}
|
||||
|
||||
final String hostname = InetAddress.getLocalHost().getHostName();
|
||||
log.info(String.format("Trying to (re)start WDA server on %s:%s...", hostname, PORT));
|
||||
UnixProcessHelpers.killProcessesGracefully(IPROXY_EXECUTABLE.getName(), XCODEBUILD_EXECUTABLE.getName());
|
||||
|
||||
final File scriptFile = File.createTempFile("script", ".sh");
|
||||
try {
|
||||
final List<String> scriptContent = new ArrayList<>();
|
||||
scriptContent.add("#!/bin/bash");
|
||||
if (isRealDevice && isRunningInJenkinsNetwork()) {
|
||||
scriptContent.add(String.join("\n", generateKeychainUnlockCmdlines()));
|
||||
}
|
||||
if (isRealDevice) {
|
||||
scriptContent.add(String.join(" ", IPROXY_CMDLINE));
|
||||
}
|
||||
final String wdaBuildCmdline = String.join(" ", generateXcodebuildCmdline());
|
||||
log.debug(String.format("Building WDA with command line:\n%s\n", wdaBuildCmdline));
|
||||
scriptContent.add(wdaBuildCmdline);
|
||||
try (Writer output = new BufferedWriter(new FileWriter(scriptFile))) {
|
||||
output.write(String.join("\n", scriptContent));
|
||||
}
|
||||
new ProcessBuilder("/bin/chmod", "u+x", scriptFile.getCanonicalPath())
|
||||
.redirectErrorStream(true).start().waitFor(5, TimeUnit.SECONDS);
|
||||
final ProcessBuilder pb = new ProcessBuilder("/bin/bash", scriptFile.getCanonicalPath());
|
||||
final Map<String, String> env = pb.environment();
|
||||
// This is needed for Jenkins
|
||||
env.put("BUILD_ID", "dontKillMe");
|
||||
log.info(String.format("Waiting max %s for WDA to be (re)started on %s:%s...", RESTART_TIMEOUT.toString(),
|
||||
hostname, PORT));
|
||||
final Timedelta started = Timedelta.now();
|
||||
pb.redirectErrorStream(true).start().waitFor(RESTART_TIMEOUT.asMillis(), TimeUnit.MILLISECONDS);
|
||||
if (!waitUntilIsRunning(RESTART_TIMEOUT)) {
|
||||
++failedRestartRetriesCount;
|
||||
throw new IllegalStateException(
|
||||
String.format("WDA server has failed to start after %s timeout on server '%s'.\n"
|
||||
+ "Please make sure that iDevice is properly connected and you can build "
|
||||
+ "WDA manually from XCode.\n"
|
||||
+ "Xcodebuild logs:\n\n%s\n\n\niproxy logs:\n\n%s\n\n\n",
|
||||
RESTART_TIMEOUT, hostname,
|
||||
getLog(XCODEBUILD_LOG).orElse("EMPTY"), getLog(IPROXY_LOG).orElse("EMPTY"))
|
||||
);
|
||||
}
|
||||
|
||||
log.info(String.format("WDA server has been successfully (re)started after %s " +
|
||||
"and now is listening on %s:%s", Timedelta.now().diff(started).toString(), hostname, PORT));
|
||||
} finally {
|
||||
scriptFile.delete();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isRunning() throws Exception {
|
||||
if (!isProcessRunning(XCODEBUILD_EXECUTABLE.getName())
|
||||
|| (isRealDevice && !isProcessRunning(IPROXY_EXECUTABLE.getName()))) {
|
||||
return false;
|
||||
}
|
||||
return waitUntilIsRunning(isRealDevice ? REAL_DEVICE_RUNNING_TIMEOUT : Timedelta.ofSeconds(3));
|
||||
}
|
||||
|
||||
public Optional<String> getLog(File logFile) {
|
||||
if (logFile.exists()) {
|
||||
try {
|
||||
return Optional.of(new String(Files.readAllBytes(logFile.toPath()), Charset.forName("UTF-8")));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
public void resetLogs() {
|
||||
for (File logFile : new File[]{XCODEBUILD_LOG, IPROXY_LOG}) {
|
||||
if (logFile.exists()) {
|
||||
try {
|
||||
final PrintWriter writer = new PrintWriter(logFile);
|
||||
writer.print("");
|
||||
writer.close();
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
之前应该调用这段代码来启动 Appium iOS 驱动,例如,在 setUp 方法中:
|
||||
|
||||
```java
|
||||
if (!WDAServer.getInstance().isRunning()) {
|
||||
WDAServer.getInstance().restart();
|
||||
}
|
||||
```
|
||||
|
||||
为 Appium 驱动程序设置 webDriverAgentUrl 非常重要,让它知道我们的 WDA 驱动程序可以使用:
|
||||
|
||||
```java
|
||||
capabilities.setCapability("webDriverAgentUrl", WDAServer.SERVER_URL);
|
||||
```
|
||||
|
||||
### 重要注释
|
||||
|
||||
* 如果是 jenkins agent 执行的,该进程不能直接访问钥匙串(Keychain),所以我们需要在为真实设备编译 WDA 之前准备钥匙串,否则编码将失败。
|
||||
* 如果 xcodebuild 和 iproxy 进程已经被冻结,我们在重新启动之前杀死这些进程,以确保编译成功,
|
||||
* 我们准备一个单独的 bash 脚本并独立于 iproxy / xcodebuild 进程,所以即使在实际的代码执行完成后,它们也可以在后台继续运行。如果在自动化实验室中的同一机器/节点上执行多个测试/套件,最少的人工干预是非常重要的。
|
||||
* 更改 BUILD_ID 环境变量的值以避免在作业完成后由 Jenkins agent 程序杀死后台进程。
|
||||
* isRunning 检查是通过验证实际的网络终端来完成的.
|
||||
* 守护进程的输出会存入日志,因此可以跟踪错误和意外的故障。如果服务器无法启动/重启,日志文件的内容会自动添加到实际的错误消息中。
|
||||
* 真机设备ID可以从 `system_profiler SPUSBDataType` 输出中解析
|
||||
* 模拟器ID可以从 `xcrun simctl list` 输出中解析
|
||||
* UrlChecker 类是从 org.openqa.selenium.net 包导入的
|
||||
|
||||
|
||||
本文由 [simple](https://testerhome.com/simple) 翻译,由 [lihuazhang](https://github.com/lihuazhang) 校验。
|
||||
@@ -0,0 +1,13 @@
|
||||
## Intel® 硬件加速执行管理器
|
||||
|
||||
如果你发现你的 android 模拟器运行速度太缓慢,而你的系统又运行在 Intel® 的 cpu 下,你可以试试 HAXM。 HAXM 可以让你利用你的硬件虚拟化技术,为你的模拟器加速。
|
||||
|
||||
* 使用 Android SDK Manager 安装 HAXM ,你会在 Extras 文件夹里找到这个包;
|
||||
* 你可以在 [Intel® 的网站][1] 找到所有相关的文档;
|
||||
* 这需要 x86 架构的模拟器镜像;
|
||||
* 使用 Intel 的安装包去安装 HAXM; 根据你已经安装过的版本不同,Android SDK Manager 可能会安装不成功(译者注:如果原来已经有一个旧版本,此时有可能会自动安装失败。建议在 [Intel® 的网站][1] 安装官方安装包手动安装)。
|
||||
|
||||
[1]: http://software.intel.com/en-us/articles/intel-hardware-accelerated-execution-manager/
|
||||
|
||||
|
||||
翻译 by [thanksdanny](https://github.com/thanksdanny) ,由 [chenhengjie123](https://github.com/chenhengjie123) 校验
|
||||
@@ -0,0 +1,43 @@
|
||||
## Android 配置
|
||||
|
||||
开始配置之前,你需要安装 Node.js(v4 或更高版本)。具体请参考 [instructions for your flavor of linux](https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager)。
|
||||
|
||||
在你安装完 Node.js 后,下一步就是安装 [Android SDK](http://developer.android.com/sdk/index.html).
|
||||
因为你需要运行 `android` tool(包含 SDK, 在 'tools' 目录下)。
|
||||
|
||||
|
||||
运行 `android` tool,并使用他来安装 Level 17 或更高级的 API。
|
||||
|
||||
(如果想通过源码运行 Appium,需要先用 [Apache Ant](http://ant.apache.org/) 去构建出 bootstrap jar包,Appium 会在 Android 模拟器或真机上运行该 jar 包。)
|
||||
|
||||
|
||||
最后,将环境变量 `$ANDROID_HOME` 配置为你的 Android SDK 路径。举个例子,假如你解压了 Android SDK 到 /usr/local/adt/,你应该在添加这行命令到你的 shell 环境变量中:
|
||||
|
||||
export ANDROID_HOME="/usr/local/adt/sdk"
|
||||
|
||||
|
||||
现在你已经配置好可以运行 Appium 的环境了!(如果你是通过源码运行的 Appium,应该确保你已经在 Appium 的代码目录下,运行过 `npm install` 安装所有的依赖。)
|
||||
|
||||
|
||||
### 对于 Android 老版本的一些额外配置
|
||||
|
||||
Appium 是用 [Selendroid](https://selendroid.io) 去运行Android 2.3到4.1版本。当 Appium 检测到当前正在运行旧版本,它就会自动地切换 Selendroid 去运行(这里不太对,如果要使用 Selendroid 需要指定),但假如你是使用源码去运行,你还需要一些额外的配置。
|
||||
|
||||
* 确保你已经安装 [Maven 3.1.1](http://maven.apache.org/download.cgi) 或已更新到 `mvn` 的最新版本
|
||||
|
||||
|
||||
### 运行 Appium Android 测试
|
||||
|
||||
在 Linux 环境上运行测试,你需要启动并运行一个 API 为 Level 17 或更高版本的 AVD。如果你是通过 NPM 安装的话,则直接在命令行输入 `appium` 去运行 Appium。如果是通过源码运行,在目录底下运行 `node .`。
|
||||
|
||||
阅读 [server documentation](/docs/cn/writing-running-appium/server-args.md) 你能查看所有命令行参数。
|
||||
|
||||
|
||||
### 注意
|
||||
|
||||
* 现在有一些 android 的硬件加速模拟器,他有自己的一些局限性。更多的信息你可以查看这份 [page](/docs/cn/appium-setup/android-hax-emulator.md).
|
||||
* 如果你想运行任何 Appium 的测试或使用任何电量相关的命令,需要确保在你的 AVD 的 `config.ini` 里 `hw.battery=yes` 。若为 Android 5.0, 则这是默认的
|
||||
* Selendroid 需要检测你的应用是否拥有以下权限:
|
||||
`<uses-permission android:name="android.**permission.INTERNET"/>`,假如你在使用 selendroid 或者更老的Android版本(也就是2.3到4.1)时,你应确保你的应用设置了 internet 权限。
|
||||
|
||||
本文由 [thanksdanny](https://testerhome.com/thanksdanny) 翻译,由 [lihuazhang](https://github.com/lihuazhang) 校验。
|
||||
@@ -0,0 +1,51 @@
|
||||
## 在真机上部署 iOS 应用
|
||||
|
||||
准备在真机上运行 Appium 之前,你需要做好以下几件事:
|
||||
|
||||
1. 使用特定设备相关的参数去构建你的 app
|
||||
2. 使用第三方工具 [ideviceinstaller](https://github.com/libimobiledevice/ideviceinstaller) 将你构建的包部署到你的设备上
|
||||
|
||||
### Xcodebuild 的参数:
|
||||
|
||||
现在新版的 xcodebuild 允许使用的指定设置。请参考[developer.apple.com](https://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man1/xcodebuild.1.html):
|
||||
|
||||
```center
|
||||
xcodebuild [-project projectname] [-target targetname ...]
|
||||
[-configuration configurationname] [-sdk [sdkfullpath | sdkname]]
|
||||
[buildaction ...] [setting=value ...] [-userdefault=value ...]
|
||||
```
|
||||
|
||||
在这资料中你会发现一些有用的 [设置](https://developer.apple.com/library/mac/#documentation/DeveloperTools/Reference/XcodeBuildSettingRef/1-Build_Setting_Reference/build_setting_ref.html#//apple_ref/doc/uid/TP40003931-CH3-DontLinkElementID_10)
|
||||
|
||||
```center
|
||||
CODE_SIGN_IDENTITY (代码签名标识)
|
||||
描述: 指定代码签名标识的名称.
|
||||
示例值: iPhone Developer
|
||||
|
||||
```
|
||||
|
||||
PROVISIONING_PROFILE 虽然在可用的命令列表中移除了,但还是有必要设置的。
|
||||
|
||||
在 xcodebuild 命令中指定 "CODE_SIGN_IDENTITY" 与 "PROVISIONING_PROFILE" 的设置:
|
||||
|
||||
```center
|
||||
xcodebuild -sdk <iphoneos> -target <target_name> -configuration <Debug> CODE_SIGN_IDENTITY="iPhone Developer: Mister Smith" PROVISIONING_PROFILE="XXXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXX"
|
||||
```
|
||||
|
||||
一旦成功,app 就会被构建到 `<app_dir>/build/<configuration>-iphoneos/<app_name>.app` 目录下。
|
||||
|
||||
### 使用 ideviceinstaller 部署
|
||||
|
||||
使用 Homebrew 去安装被标记为最新版本的 ideviceinstaller 工具,在终端运行以下命令:
|
||||
|
||||
``` center
|
||||
# 第一行命令只是在你没有安装好 brew 的情况下才需要执行
|
||||
> ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
|
||||
> brew update
|
||||
> brew install ideviceinstaller
|
||||
> ideviceinstaller -u <UDID of device> -i <path of .app/.ipa>
|
||||
```
|
||||
|
||||
下一部分:[在真机上运行 Appium](real-devices.md)
|
||||
|
||||
本文由 [thanksdanny](https://testerhome.com/thanksdanny) 翻译,由 [lihuazhang](https://github.com/lihuazhang) 校验。
|
||||
@@ -0,0 +1,35 @@
|
||||
## Android 并发测试
|
||||
|
||||
Appium 给用户提供了在一个机器上启动多个 Android sessions 的方案。该方案只需要通过不同参数来启动的多个 Appium 服务。
|
||||
|
||||
以下是启动多个 Android 会话的一些重要参数:
|
||||
|
||||
- `-p` Appium 主要端口
|
||||
- `-U` 设备 id
|
||||
- `-bp` Appium bootstrap 端口
|
||||
- `--chromedriver-port` chromedriver 端口 (若是在使用 webviews 或 chrome)
|
||||
- `--selendroid-port` selendroid 端口 (若是在使用 selendroid)
|
||||
|
||||
更多相关参数的信息可以参考 [这里](../writing-running-appium/caps.md) 。
|
||||
|
||||
如果我们有两台设备,且他们的设备 id 分别为 43364 和 32456,我们可以通过以下命令启动两个不同的 Appium 服务器:
|
||||
|
||||
`node . -p 4492 -bp 2251 -U 32456`
|
||||
|
||||
`node . -p 4491 -bp 2252 -U 43364`
|
||||
|
||||
只要你的 Appium 与 Appium bootstrap 的端口在 0 到 65536 之间,且端口号并不相同,这样两个 Appium 服务器就不会去监听同一个端口。确保通过 -u 参数标志的 id 与对应的设备 id 是一致的。这就是 Appium 能知道设备之间是如何通信的原因,因此必须保证参数准确无误。
|
||||
|
||||
如果你使用 chromedriver 或 selendroid,记得确保服务器的端口号是独一无二的。
|
||||
|
||||
如果你使用 [appium-uiautomator2-driver](https://github.com/appium/appium-uiautomator2-driver),
|
||||
需要给 systemPort 这个 capability 配置为不同的系统端口。因为有时候如果没有使用不同的端口,会出现冲突,比如[这个问题](https://github.com/appium/appium/issues/7745).
|
||||
|
||||
### iOS 并发测试
|
||||
|
||||
十分不幸,目前并不能在本地运行 iOS 的并发测试。iOS 同一时间只能启动一个模拟器,不像 Andoid 可以同时多个模拟器去运行测试。
|
||||
|
||||
如果你想运行 iOS 的并发测试,你需要使用 Sauce 上传你的 Appium 测试脚本,然后就可以运行多台 iOS 和 Android 的并发测试,只要你的账号允许。查看更多相关信息可以查看 [这里](https://docs.saucelabs.com/tutorials/appium/)。
|
||||
|
||||
|
||||
本文由 [thanksdanny](https://testerhome.com/thanksdanny) 翻译,由 [lihuazhang](https://github.com/lihuazhang) 校验。
|
||||
@@ -0,0 +1,45 @@
|
||||
## Appium 支持的平台
|
||||
|
||||
Appium 支持多种平台以及各种测试方式(native,hybrid,web,真机,模拟器,等等...)。这份文档的设计初衷就是为了搞清楚所支持平台的版本,以及所需的条件。
|
||||
|
||||
### iOS 平台支持
|
||||
|
||||
获取 iOS 平台下所需的必备条件和安装说明,请查阅 [Running on OS X: iOS](running-on-osx.md)
|
||||
|
||||
* 版本:7.1 及以上版本
|
||||
* 设备:iPhone 模拟器,iPad 模拟器,以及 iPhone 和 iPad 的真机
|
||||
* 是否支持 Native 应用:支持。如在模拟器执行,需要 debug 版本的 .app 包,在真机上运行则需要已签名的 .ipa 包。底层的框架是由苹果的 [XCUITest](https://developer.apple.com/reference/xctest) (或 [UIAutomation](https://developer.apple.com/library/ios/documentation/DeveloperTools/Reference/UIAutomationRef/) 支持更旧的版本) 所提供支持
|
||||
* 是否支持移动端浏览器:支持。我们通过移动端的 Safari 进行自动化测试。对于真机,`ios-webkit-remote-debugger` 工具是必须的。可惜的是对于 Safari 的 native 部分的自动化目前还不支持。更多介绍请查看 [mobile web doc](/docs/cn/writing-running-appium/mobile-web.md)。
|
||||
* 是否支持 Hybrid 应用:支持。如使用真机,ios-webkit-remote-debugger 工具也是必须的。更多介绍请查看 [hybrid doc](/docs/cn/advanced-concepts/hybrid.md)。
|
||||
* 是否支持在一个 session 中多个应用的自动化:不支持
|
||||
* 是否支持多设备同时执行自动化:不支持
|
||||
* 是否支持第三方应用的自动化:仅支持在模拟器上仅有的第三方应用(设置,地图,等等...)。若在 iOS 10 及以上的版本,你同样可以在 home 界面做自动化。
|
||||
* 是否支持自定义的、非标准的 UI 控件的自动化:只支持小部分。你需要在控件设置可识别的信息,从而对一些元素进行一些基础的自动化操作。
|
||||
|
||||
### Android 平台支持
|
||||
|
||||
查阅[Running on OS X: Android](running-on-osx.md),[Running on Windows](running-on-windows.md) 或者 [Running on Linux](running-on-linux.md) 查看更多的设备信息以及安装说明。
|
||||
|
||||
* 版本:2.3 及以上版本
|
||||
* 2.3 至 4.2 版本是通过 Appium 绑定的基于 [Instrumentation](http://developer.android.com/reference/android/app/Instrumentation.html)框架的 [Selendroid](http://selendroid.io)实现的自动化。Selendroid 的命令设置与默认的 Appium 有点不同, 支持的配置文件也同样不同。要获得在后台运行自动化的权限,需要环境配置中将 `automationName` 的值为 `Selendroid`。
|
||||
* 4.2 以及更高的版本是通过 Appium 自己的 [UiAutomator](http://developer.android.com/tools/testing-support-library/index.html#UIAutomator) 库实现。这是默认的自动化后台。
|
||||
* 设备:Android 模拟器以及 Android 真机
|
||||
* 是否支持 Native 应用:支持
|
||||
* 是否支持移动端浏览器:支持(除了使用 Selendroid 后台的时候)。Appium 绑定了一个 [Chromedriver](https://code.google.com/p/selenium/wiki/ChromeDriver) 服务,使用这个代理服务进行自动化测试。在 4.2 和 4.3 版本,只能在官方的 Chrome 浏览器或者 Chromium 执行自动化测试。在 4.4 及更高版本,可以在内置的 “浏览器” 应用上进行自动化了。更多介绍请查看 [mobile web doc](/docs/cn/writing-running-appium/mobile-web.md)。
|
||||
* 是否支持 Hybrid 应用:支持。更多介绍请查阅 [hybrid doc](/docs/cn/advanced-concepts/hybrid.md)。
|
||||
* 默认的 Appium 自动化后台:支持 4.4 以及更高版本
|
||||
* Selendroid 自动化后台:支持 2.3 以及更高版本
|
||||
* 是否支持多个 app 在同一个 session 中自动化:支持(除了使用 Selendroid 后台的时候)
|
||||
* 是否支持多个设备同时进行自动化:支持,即使 Appium 在开始的时候,使用不同的端口号作为服务器参数,`--port`, `--bootstrap-port` (或者 `--selendroid-port`) 还有 `--chromedriver-port`. 更多介绍请查看 [server args doc](/docs/cn/writing-running-appium/server-args.md)。
|
||||
* 支持第三方引用:支持(除了使用 Selendroid 后台的时候)
|
||||
* 支持自定义的、非标准的 UI 控件的自动化:不支持
|
||||
|
||||
### Windows 桌面支持
|
||||
|
||||
查看以下文档获取更多信息:
|
||||
|
||||
* [Running on Windows](running-on-windows.md)
|
||||
* [Windows App Testing](/docs/cn/writing-running-appium/windows-app-testing.md)
|
||||
|
||||
|
||||
本文由 [thanksdanny](https://testerhome.com/thanksdanny) 翻译,由 [lihuazhang](https://github.com/lihuazhang) 校验。
|
||||
@@ -0,0 +1,7 @@
|
||||
### 在 Android 真机上运行 Appium
|
||||
|
||||
谢天谢地!在 Android 真机上运行是没有额外需要注意的地方:在模拟器上能运行的测试同样适用在真机上。确保你的设备可以连接 ADB 和开启开发者模式。在真机上测试 Chrome,你只需负责确认 Chrome 已安装上一个合适的版本。
|
||||
|
||||
同样,你需要在手机上关闭“验证应用”,否则他会阻止 Appium 的辅助应用启动和正常运行。
|
||||
|
||||
本文由 [thanksdanny](https://testerhome.com/thanksdanny) 翻译,由 [lihuazhang](https://github.com/lihuazhang) 校验。
|
||||
|
After Width: | Height: | Size: 832 KiB |
|
After Width: | Height: | Size: 2.5 MiB |
|
After Width: | Height: | Size: 765 KiB |
|
After Width: | Height: | Size: 928 KiB |
|
After Width: | Height: | Size: 65 KiB |
|
After Width: | Height: | Size: 650 KiB |
|
After Width: | Height: | Size: 150 KiB |
|
After Width: | Height: | Size: 1.6 MiB |
|
After Width: | Height: | Size: 84 KiB |
|
After Width: | Height: | Size: 147 KiB |
|
After Width: | Height: | Size: 849 KiB |
@@ -0,0 +1,207 @@
|
||||
## 在 iOS 真机上运行 Appium
|
||||
|
||||
Appium 已支持真机的测试。
|
||||
|
||||
### 使用 Appium 运行你的测试
|
||||
|
||||
把设备的 udid 传给 `udid` 这个 capability。把应用的 bundle ID 传给 `app` 这个 capability,如果你的应用已经在手机上安装好了。如果应用未安装,那么需要将 `.ipa` 或者 `.app` 文件的路径传给 `app` 这个 capability。一旦你的设备和应用已经配置好,你就可以在那个设备上运行测试了。
|
||||
|
||||
### Desired Capabilities
|
||||
|
||||
通过在测试里使用以下的 capability,你可以在某个设备加载这个应用:
|
||||
|
||||
* `app` 或者 `bundleId` - 指定应用,或者如果应用已经安装到手机上了,只需简单地指定应用的 bundleID。这样 Appium 就可以加载该应用了。
|
||||
* `udid` - 被测设备的 id。如果只有一台设备连接的时候,可以设置为 `auto`,Appium 能自动识别设备 id 并使用它。
|
||||
|
||||
更多的细节参考 [Appium server capabilities](/docs/cn/writing-running-appium/caps.md)
|
||||
|
||||
### 必备的软件
|
||||
|
||||
Appium iOS 真机支持依赖一个重要的第三方软件套件 [libimobiledevice](http://www.libimobiledevice.org/),使用 [Homebrew](http://brew.sh/) 可以轻易地安装好。
|
||||
```
|
||||
brew install libimobiledevice
|
||||
```
|
||||
|
||||
## 在 Xcode 7 和 iOS 9.3 或者以下,配置 iOS 真机设备测试
|
||||
|
||||
在 iOS 9.3 或者以下,使用 Xcode 7.3 或者以下对真机进行自动化 (比如,基于 Instruments 的测试) 需要相对较少的配置。主要就是需要你的应用是使用开发者证书签名的。 不幸的是,苹果移除了所有使用老版本 Xcode 的文档,不过你可以从[这里](https://medium.com/ios-os-x-development/ios-code-signing-provisioning-in-a-nutshell-d5b247760bef#.5hirl92tn),[这里](https://engineering.nodesagency.com/articles/iOS/Understanding-code-signing-for-iOS-apps/)找到如何操作。
|
||||
|
||||
更详细地说, 在真机上开始测试, 你需要如下步骤:
|
||||
|
||||
* 一个 [Apple Developer ID](https://developer.apple.com/programs/ios/) 和一个有效的配置好开发者证书和 provisioning profile 的开发者账号。
|
||||
* 要在真机上测试,一个有效的 iOS 开发证书和 Provisioning Profile 是必须的。被测应用也必须被签名。更多信息可以在 [Apple documentation](https://developer.apple.com/library/ios/documentation/IDEs/Conceptual/AppDistributionGuide/TestingYouriOSApp/TestingYouriOSApp.html) 找到。
|
||||
* 一个苹果设备: iPad 或者 iPhone 都可以。确保已经在 Xcode 里已经配置好了开发机。
|
||||
* 一个已经签名的 `.app` 或则 `.ipa` 文件,或者使用源码编一个。
|
||||
* 一台 Mac 电脑,装好了 [Xcode](https://developer.apple.com/xcode/) 和 Xcode Command
|
||||
Line Developer Tools。
|
||||
|
||||
Appium 使用 `ideviceinstaller` (`libimobiledevice` 套件的一部分)来处理应用安装问题。但是有时候使用 Xcode 提前安装好应用确保没有问题更加容易。(更多参考 [iOS deploy](ios-deploy.md))。
|
||||
|
||||
### 排障思路
|
||||
|
||||
0. 确保 UDID 是无误的,可以通过 Xcode Organizer 或者 iTunew 查看。留意 UDID 是很长的字符串(20+ 字符)。
|
||||
0. 确保你可以在模拟器运行你的测试。
|
||||
0. 重复确认通过 Instrumens 是否可以启动你的自动化。
|
||||
0. 确保 Instruments 不在运行状态中。
|
||||
0. 确保 UI Automation 在你的设备已经打开。设置 -> 开发者 -> 使用 UI Automation。
|
||||
|
||||
## 在 Xcode 8 和 iOS 9.3 以上配置 iOS 真机设备测试
|
||||
|
||||
作为对 `libimobiledevice` 依赖的补充,Appium 对 iOS 9.3 及以上和 Xcode 8 及以上的支持依赖 `ios-deploy`,
|
||||
通过 [npm](https://www.npmjs.com/package/ios-deploy) 可以方便地安装:
|
||||
```
|
||||
npm install -g ios-deploy
|
||||
```
|
||||
|
||||
不幸地是,苹果改变了以往的测试方法。新的方法需要安装一个帮助应用到设备上去,通过这个应用,自动化测试才能进行。说起来挺简单,但是代码签名和生成供开发和测试的应用,让人有一点头疼。
|
||||
|
||||
Appium 安装的应用叫 `WebDriverAgent-Runner` (可以在[这里](https://github.com/facebook/WebDriverAgent/))找到,要想安装这个应用,Appium需要能配置这个构建。
|
||||
|
||||
### 基本 (自动化) 配置
|
||||
|
||||
在 iOS 真机上设置和运行 Appium XCUItest,最方便的方法是使用自动化配置策略。有两种方式:
|
||||
|
||||
* 使用 `xcodeOrgId` 和 `xcodeSigningId` desired capabilities:
|
||||
```json
|
||||
{
|
||||
"xcodeOrgId": "<Team ID>",
|
||||
"xcodeSigningId": "iPhone Developer"
|
||||
}
|
||||
```
|
||||
* 在你的文件系统里创建一个 `.xcconfig` 文件,内容是:
|
||||
```
|
||||
DEVELOPMENT_TEAM = <Team ID>
|
||||
CODE_SIGN_IDENTITY = iPhone Developer
|
||||
```
|
||||
以上任何一个方式,Team ID 是一个 10 个字符长的字符串,苹果生成这个字符串,分配给你的团队。你可以在你的开发者账号里找到你的 Team ID。登录 [developer.apple.com/account](http://developer.apple.com/account),然后点击边栏上的 Membership。你的 Team ID 在 Membership
|
||||
Information 部分的团队名字下面。你也可以在 keychain 里的 iPhone 开发者证书里的 "Organizational Unit" 属性找到你的 Team ID。
|
||||
|
||||
注意这是互斥的策略;要么用 `xcodeConfigFile` capability, 要么用 `xcodeOrgId` and `xcodeSigningId` 的组合。
|
||||
|
||||
当以上的配置搞定后,只需再指定 `udid` capability 就可以在真机上开始测试了。
|
||||
|
||||
如果还是不工作的话,通常会在 Appium server 的日志里显示一些错误如:`info XCUITest xcodebuild exited with code '65' and signal 'null'`。这通常表示必要的代码签名没配置正确。 去看下 [Basic (manual) configuration](https://github.com/imurchie/appium-xcuitest-driver/blob/isaac-rs/docs/real-device-config.md#basic-manual-configuration)
|
||||
这个文档,参试修复下。
|
||||
|
||||
如果 `WebDriverAgentRunner` 成功的在设备上安装好,但是在 Appium server 日志里还是有如下错误信息:
|
||||
```
|
||||
2017-01-24 09:02:18.358 xcodebuild[30385:339674] Error Domain=com.apple.platform.iphoneos Code=-12 "Unable to launch com.apple.test.WebDriverAgentRunner-Runner" UserInfo={NSLocalizedDescription=Unable to launch com.apple.test.WebDriverAgentRunner-Runner, NSUnderlyingError=0x7fa839cadc60 {Error Domain=DTXMessage Code=1 "(null)" UserInfo={DTXExceptionKey=The operation couldn’t be completed. Unable to launch com.apple.test.WebDriverAgentRunner-Runner because it has an invalid code signature, inadequate entitlements or its profile has not been explicitly trusted by the user. : Failed to launch process with bundle identifier 'com.apple.test.WebDriverAgentRunner-Runner'}}}
|
||||
2017-01-24 09:02:18.358 xcodebuild[30385:339674] Error Domain=IDETestOperationsObserverErrorDomain Code=5 "Early unexpected exit, operation never finished bootstrapping - no restart will be attempted" UserInfo={NSLocalizedDescription=Early unexpected exit, operation never finished bootstrapping - no restart will be attempted}
|
||||
|
||||
Testing failed:
|
||||
Test target WebDriverAgentRunner encountered an error (Early unexpected exit, operation never finished bootstrapping - no restart will be attempted)
|
||||
```
|
||||
这个问题是因为开发者在这台设备上不被信任。如果你在设备上手动运行 `WebDriverAgentRunner`,你会看到一个错误页面弹出来:
|
||||
|
||||

|
||||
|
||||
你可以去设备中的 Settings => General => Device Management 信任这个开发者,允许 `WebDriverAgentRunner` 运行 (参考 [Apple
|
||||
documentation for more information](https://support.apple.com/en-us/HT204460)).
|
||||
|
||||
|
||||
### 基本 (手动) 配置
|
||||
|
||||
很多情况,基本的自动配置满足不了需求。这通常和真机运行上的代码签名和项目配置有关。这常常发生在免费的开发者账号上,免费的开发者账号不能创建 wildcard provisioning profile,也不能为应用创建默认的配置文件。
|
||||
|
||||
Xcode **创建 provisioning profile 失败** 的错误如下:
|
||||
|
||||

|
||||
|
||||
最容易的解决方案就是打开 [Xcode](https://developer.apple.com/xcode/)创建一个新工程:
|
||||
|
||||

|
||||
|
||||
类型不重要,只要是 "iOS"。用最简单的 "Single View Application" 即可:
|
||||
|
||||

|
||||
|
||||
重要的地方是要用唯一的 "Product Name" 和 "Organization Name". 然后,在这里,指定你的 "Team"。
|
||||
|
||||

|
||||
|
||||
你可以在 "Project" tab 确认 provisioning profile 是否创建成功。
|
||||
|
||||

|
||||
|
||||
或者去你的账号首选项里看看 provisioning profile:
|
||||
|
||||

|
||||
|
||||
现在你就有了一个有效的 provisioning profile。 记住你关联这个 profile 的 bundle id,然后放到 `updatedWDABundleId` 这个 desired capability 里。然后跟着[前面的自动配置教程](#basic-automatic-configuration)操作即可。
|
||||
|
||||
|
||||
### 全部手动配置
|
||||
|
||||
另外,可以手动把 provisioning profile 和项目关联在一起。 (记住如果使用这个方法,每次 WebDriverAgent 更新,都要做一次。所以我们不推荐这个方法):
|
||||
|
||||
* 找到你的 Appium 的安装目录
|
||||
```
|
||||
$ which appium
|
||||
/path/where/installed/bin/appium
|
||||
```
|
||||
* 假设安装目录是: `/path/where/installed/bin/appium`,那么 `WebDriverAgent`
|
||||
在 `/path/where/installed/lib/node_modules/appium/node_modules/appium-xcuitest-driver/WebDriverAgent`。
|
||||
打开终端到那个目录,然后按以下命令操作:
|
||||
```
|
||||
mkdir -p Resources/WebDriverAgent.bundle
|
||||
./Scripts/bootstrap.sh -d
|
||||
```
|
||||
* 在 Xcode 里打开 `WebDriverAgent.xcodeproj`。 对于 `WebDriverAgentLib`
|
||||
和 `WebDriverAgentRunner` 两个 target,都选择 "General" tab 里的 "Automatically manage signing"
|
||||
然后选择你的 `Development Team`。这也会自动选择 `Signing Ceritificate`。大概的样子如下:
|
||||
|
||||

|
||||
|
||||
* Xcode 可能会创建不了 `WebDriverAgentRunner` 的 provisioning profile:
|
||||
|
||||

|
||||
|
||||
* 这时候需要在 "Build Settings" tab 里手动改 bundle id。把 "Product Bundle Identifier"
|
||||
从 `com.facebook.WebDriverAgentRunner` 改成 Xcode 能采用的:
|
||||
|
||||

|
||||
|
||||
* 在回到 "General" tab 里的 `WebDriverAgentRunner` target,现在你可以看到一个 provisioning profile 创建成果了:
|
||||
|
||||

|
||||
|
||||
* 最后,你可以验证一切都 OK 了。 构建这个项目:
|
||||
```
|
||||
xcodebuild -project WebDriverAgent.xcodeproj -scheme WebDriverAgentRunner -destination 'id=<udid>' test
|
||||
```
|
||||
如果成果了,最后的输出会像下面这样:
|
||||
```
|
||||
Test Suite 'All tests' started at 2017-01-23 15:49:12.585
|
||||
Test Suite 'WebDriverAgentRunner.xctest' started at 2017-01-23 15:49:12.586
|
||||
Test Suite 'UITestingUITests' started at 2017-01-23 15:49:12.587
|
||||
Test Case '-[UITestingUITests testRunner]' started.
|
||||
t = 0.00s Start Test at 2017-01-23 15:49:12.588
|
||||
t = 0.00s Set Up
|
||||
```
|
||||
* 要完整验证的话,你可以试试看访问 WebDriverAgent server 的状态 (**注意:** 你必须和手机设备同一个网段,你可以从 Settings => Wi-Fi => Current Network 找到手机当前的 IP。):
|
||||
```
|
||||
export DEVICE_URL='http://<device IP>:8100'
|
||||
export JSON_HEADER='-H "Content-Type: application/json;charset=UTF-8, accept: application/json"'
|
||||
curl -X GET $JSON_HEADER $DEVICE_URL/status
|
||||
```
|
||||
你会得到如下输出:
|
||||
```
|
||||
{
|
||||
"value" : {
|
||||
"state" : "success",
|
||||
"os" : {
|
||||
"name" : "iOS",
|
||||
"version" : "10.2"
|
||||
},
|
||||
"ios" : {
|
||||
"simulatorVersion" : "10.2",
|
||||
"ip" : "192.168.0.7"
|
||||
},
|
||||
"build" : {
|
||||
"time" : "Jan 23 2017 14:59:57"
|
||||
}
|
||||
},
|
||||
"sessionId" : "8951A6DD-F3AD-410E-A5DB-D042F42F68A7",
|
||||
"status" : 0
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,6 @@
|
||||
## Appium 上对真机测试的支持
|
||||
|
||||
Appium 已支持真机的测试。
|
||||
|
||||
* **Android** 测试, 参考 [android real device documentation](real-devices-android.md)
|
||||
* **iOS** 测试, 参考 [iOS real device documentation](real-devices-ios.md)
|
||||
@@ -0,0 +1,11 @@
|
||||
## 在 Linux 上运行 Appium
|
||||
|
||||
### 局限性
|
||||
|
||||
如果你要在 Linux 上运行 Appium,是不能使用 '.app' 的预构建包的,因为它只能在 OS X 上进行构建。其次,iOS 的应用测试也是无法进行,因为 Appium 也是依赖在 OS X 上特有的库才能支持 iOS 的测试。
|
||||
|
||||
### 配置(Android)
|
||||
|
||||
Android 上的一些设置,与在 linux 上运行测试的方式,都是跟 Mac OS X 上的操作方式是相同的。 请查阅 [Android 设置文档](/docs/cn/appium-setup/android-setup.md).
|
||||
|
||||
本文由 [thanksdanny](https://testerhome.com/thanksdanny) 翻译,由 [lihuazhang](https://github.com/lihuazhang) 校验。
|
||||
@@ -0,0 +1,150 @@
|
||||
## 在 Mac OS Xcode 上运行 Appium
|
||||
|
||||
在 OS X 上,Appium 支持 iOS 与 Android 测试。
|
||||
|
||||
### 系统配置(iOS)
|
||||
|
||||
* Appium 要求 Mac OS X 10.10 或更高的系统版本。
|
||||
* 确保你已经安装 Xcode 与 iOS 的 SDK。推荐使用 Xcode 7.1版本,因为早期版本的 Xcode 的版本对于可测试的 iOS 版本是受限的。查看下面的章节了解更多信息。
|
||||
* 你需要给 iOS 模拟器授权使用。请查看[下面章节](#authorizing-ios-on-the-computer)。
|
||||
* 如果你使用 Xcode 7.x 及以上版本,Instruments Without Delay(IWD) 已经失效了。你通过[这个方法](/docs/cn/advanced-concepts/iwd_xcode7.md)去使用 IWD (它会使你的测试速度显著提升)。
|
||||
* 如果你使用 Xcode 6,使用 Appium 前你得提前启动模拟器。假如你想发送文本信息,你得改变他的默认设置,去开启虚拟键盘。开启键盘后,你就可以通过点击输入框,或使用快捷键(comand + K)去调出键盘。
|
||||
* 如果你使用 Xcode 6,Xcode 上有个模块叫 Devices(快捷键:comand-shift-2)。使用 Appium 的时候,你只需要在 capabilities 中的 devicesName 参数填上你的设备名字,每一个 sdk 版本都会对应一个设备。换句话说,如果你在 capabilities 里设置了 devicesName 为 "iPhone 5s" 以及 platformVersion 为 "8.0",你就得保证在设备列表里,这是唯一一个使用 8.0 sdk 且名为"iPhone 5s"的设备。否则,Appium 就不会知道你想用的哪台设备。
|
||||
* 在 iOS 8,可以在`设置`里开启或者关闭 UIAutomation。该设置就在手机的设置里一个叫"Developer"页面。在使用模拟器或者真机去做自动化前,你需要去该页面确保 UIautomation 开关已经开启。
|
||||
|
||||
### 授权 iOS 设备给你的电脑
|
||||
|
||||
你要运行 `authorize-ios` 工具进行授权,可以通过 `npm` 去安装该工具,运行以下命令进行安装
|
||||
|
||||
```
|
||||
npm install -g authorize-ios
|
||||
```
|
||||
|
||||
然后运行以下命令去调起该工具
|
||||
|
||||
```
|
||||
sudo authorize-ios
|
||||
```
|
||||
|
||||
如果你是使用 [Appium.app](https://github.com/appium/appium-dot-app),你可以在 GUI 界面进行授权。
|
||||
|
||||
每次更新 Xcode 版本的时候,你都需要重复以上步骤哦!
|
||||
|
||||
### 使用多种 iOS SDK 测试
|
||||
|
||||
Xcode 7.1 版本允许使用 iOS 7.1 以及更高级的系统版本去做自动化测试。
|
||||
|
||||
如果你使用多个 Xcode 版本,你使用以下命令去切换版本:
|
||||
|
||||
sudo xcode-select --switch <path to required xcode>
|
||||
|
||||
### 在 Xcode 8(包括 iOS 10)下使用 XCUITest 进行测试
|
||||
|
||||
为了在 Xcode 8(其中包括所有 iOS 10+ 的测试)使用 iOS 真机做自动化,你要安装 [Carthage](https://github.com/Carthage/Carthage) 去做依赖管理:
|
||||
```
|
||||
brew install carthage
|
||||
```
|
||||
|
||||
### 测试 Mac 应用
|
||||
|
||||
目前为止,Mac 对应的 appium driver 在 AppiumForMac 的二进制文件还没发布,也就是说如果你想进行 Mac 应用的自动化,你就得手动去安装 AppiumForMac 应用,并且对 OS X 授予可访问权限。
|
||||
|
||||
|
||||
如何安装 Appium for Mac:
|
||||
1. [下载该发布版本](https://github.com/appium/appium-for-mac/releases/tag/0.2.0),将他解压缩到 `/Applications` 文件夹中
|
||||
2. 查看 [简要补充安装说明](https://github.com/appium/appium-for-mac#installation),确保 appium 放访问到 OS X 的 Accessibility APIs
|
||||
|
||||
更多相关如何使用 Appium for mac 的相关信息,请查阅该[文档](https://github.com/appium/appium-for-mac#appium-for-mac)。
|
||||
|
||||
|
||||
### 系统配置(Android)
|
||||
|
||||
Android 的设置操作指南与在 Mac OS X 上的设置,大致与 Linux 上的设置相似,可以参考 [Android 设置文档](/docs/en/appium-setup/android-setup.md)。
|
||||
|
||||
### 使用 Jenkins 在 OS X 上运行 iOS 测试
|
||||
|
||||
第一步就是下载 jenkins-cli.jar,以及验证你的 Mac 是否成功的连接上 Jenkins 主机。确保已经运行了 `authorize-ios` 等相关的命令。
|
||||
|
||||
`wget https://jenkins.ci.cloudbees.com/jnlpJars/jenkins-cli.jar`
|
||||
|
||||
```
|
||||
java -jar jenkins-cli.jar \
|
||||
-s https://team-appium.ci.cloudbees.com \
|
||||
-i ~/.ssh/id_rsa \
|
||||
on-premise-executor \
|
||||
-fsroot ~/jenkins \
|
||||
-labels osx \
|
||||
-name mac_appium
|
||||
```
|
||||
|
||||
接下来为了在登录的时候可以自动启动,定义一个 Jenkins 的 LaunchAgent。LaunchDaemon 将会停止工作,因为 deamons 没有获取 GUI 的访问权限。确保 plist 文件不会包含`SessionCreate` 或 `User` 这两个键值,就防止测试在运行时启动。如果漏了配置,你会看到该报错: `Failed to authorize rights`。
|
||||
|
||||
|
||||
```
|
||||
$ sudo nano /Library/LaunchAgents/com.jenkins.ci.plist
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string>com.jenkins.ci</string>
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>java</string>
|
||||
<string>-Djava.awt.headless=true</string>
|
||||
<string>-jar</string>
|
||||
<string>/Users/appium/jenkins/jenkins-cli.jar</string>
|
||||
<string>-s</string>
|
||||
<string>https://instructure.ci.cloudbees.com</string>
|
||||
<string>on-premise-executor</string>
|
||||
<string>-fsroot</string>
|
||||
<string>/Users/appium/jenkins</string>
|
||||
<string>-executors</string>
|
||||
<string>1</string>
|
||||
<string>-labels</string>
|
||||
<string>mac</string>
|
||||
<string>-name</string>
|
||||
<string>mac_appium</string>
|
||||
<string>-persistent</string>
|
||||
</array>
|
||||
<key>KeepAlive</key>
|
||||
<true/>
|
||||
<key>StandardOutPath</key>
|
||||
<string>/Users/appium/jenkins/stdout.log</string>
|
||||
<key>StandardErrorPath</key>
|
||||
<string>/Users/appium/jenkins/error.log</string>
|
||||
</dict>
|
||||
</plist>
|
||||
```
|
||||
|
||||
最后设置 owner,权限,再开启代理。
|
||||
|
||||
```
|
||||
sudo chown root:wheel /Library/LaunchAgents/com.jenkins.ci.plist
|
||||
sudo chmod 644 /Library/LaunchAgents/com.jenkins.ci.plist
|
||||
|
||||
launchctl load /Library/LaunchAgents/com.jenkins.ci.plist
|
||||
launchctl start com.jenkins.ci
|
||||
```
|
||||
|
||||
### 运行 iOS 测试时生成的文件
|
||||
|
||||
测试 iOS 的过程中生成的文件有时可能会过大。这些文件包含日志,临时文件,还有 Xcode 运行时产生的数据。一般来说,下面的这些地址都是这些文件保存的地方,可以删除他们节省空间:
|
||||
|
||||
```
|
||||
$HOME/Library/Logs/CoreSimulator/*
|
||||
```
|
||||
|
||||
基于 Instruments 的测试 (iOS 不是使用 `XCUITest` 作为 `automationName`):
|
||||
|
||||
```
|
||||
/Library/Caches/com.apple.dt.instruments/*
|
||||
```
|
||||
|
||||
基于 XCUITest 的测试:
|
||||
|
||||
```
|
||||
$HOME/Library/Developer/Xcode/DerivedData/*
|
||||
```
|
||||
|
||||
本文由 [thanksdanny](https://testerhome.com/thanksdanny) 翻译,由 [lihuazhang](https://github.com/lihuazhang) 校验。
|
||||
@@ -0,0 +1,43 @@
|
||||
#Windows 设置
|
||||
|
||||
在 Windows 上,Appium 支持 Windows 与 Android 应用的自动化测试!
|
||||
|
||||
获取更多信息请查阅 [Windows 应用测试](/docs/cn/writing-running-appium/windows-app-testing.md)。
|
||||
|
||||
## 在 Windows 上运行 Appium
|
||||
|
||||
## 配置
|
||||
|
||||
开始安装:
|
||||
|
||||
1. 下载最新版本的 [node 与 npm 工具](https://nodejs.org/download/release/v6.3.0/node-v6.3.0-x64.msi) 的 MSI (版本 >= 6.0) `npm` 和 `nodejs` 两个命令应该在你的 PATH 系统变量里。
|
||||
2. 打开你的 cmd 终端
|
||||
3. 运行 `npm install -g appium` 这条命令后,就会通过 NPM 去安装 Appium。
|
||||
4. 在命令行简单地输入 `appium` 就可以快速开启 Appium。
|
||||
5. 按照下面的只是去设置 Android 或者 Windows 应用的测试。
|
||||
6. 使用任意 Appium 客户端去运行一个测试。
|
||||
|
||||
## Android 应用测试的一些额外配置
|
||||
|
||||
1. 下载最新版的 Java JDK[这里](http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html)(记得先接受协议的许可)。设置 'JAVA_HOME' 为你的 JDK 路径。目录下的 `bin` 文件夹应该添加到你的 PATH 变量中。
|
||||
2. 安装 [Android SDK](http://developer.android.com/sdk/index.html)。将环境变量`ANDROID_HOME`设置为你的 Android SDK 路径,并且将 `tools` 和 `platform-tools` 这两个文件夹添加到你的 PATH 变量中去。
|
||||
3. 安装 [Apache Ant](http://ant.apache.org/bindownload.cgi) 或使用 eclipse 插件目录中的 Android Windows SDK。确保添加的文件夹包含 Ant 在你的 PATH 变量中。
|
||||
4. 安装 [Apache Maven](http://maven.apache.org/download.cgi) ,并设置 M2HOME 与 M2 的环境变量。设置 `M2_HOME` 为 maven 所在的安装路径,设置 `M2` 为 `bin` 目录的路径。将 `M2` 路径添加到你的 PATH 变量中。
|
||||
5. 在 Windows 上运行测试,你需要启动 Android 的虚拟机或者一台已经连接电脑的 Android 真机,并且 AVD 要求 API Level 17 或更高版本,才能在命令行中运行 Appium(通过 `appium` 命令)
|
||||
6. 你的测试脚本应该确保 `platformVersion` 参数是与你的模拟器或者模拟器的版本是一一对应的,以及`app`参数是你的需要测试的应用的绝对路径。
|
||||
|
||||
## Windows 应用测试的额外配置
|
||||
|
||||
1. 测试 Windows 应用,最基本的要求就是要打开 [开发者模式](https://msdn.microsoft.com/en-us/windows/uwp/get-started/enable-your-device-for-development)。
|
||||
|
||||
(查看[Windows app testing](/docs/cn/writing-running-appium/windows-app-testing.md),了解更多测试 Windows 应用测试的说明)
|
||||
|
||||
## 运行 Appium
|
||||
|
||||
查看 [server documentation](/docs/cn/writing-running-appium/server-args.md) 可以查看所有的命令参数。
|
||||
|
||||
* 在 Windows 上运行 Appium.exe 需要管理员权限,或者当你以源码方式运行,你要在 cmd 运行时有管理员权限。
|
||||
* 在 Windows 上运行 Andoid 测试时,务必记得添加上`--no-reset` 或 `--full-reset` 参数。
|
||||
* Android 有硬件加速仿真器;但他也是有限制的,更多信息请查阅该[文章](/docs/cn/appium-setup/android-hax-emulator.md).
|
||||
|
||||
本文由 [thanksdanny](https://testerhome.com/thanksdanny) 翻译,由 [lihuazhang](https://github.com/lihuazhang) 校验。
|
||||
@@ -0,0 +1,150 @@
|
||||
## Appium 的故障排查
|
||||
|
||||
当你在使用过程中遇到了问题,先别急着到 github 上提交反馈,或者到 [appium-discuss discussion group](https://discuss.appium.io) 提问。可以先试试在本文中能否找到解决的办法。
|
||||
|
||||
### 常见问题
|
||||
|
||||
* 确保你是跟着 [README](/README.md) 中的每一步来做。
|
||||
* 确保你的系统已经配置好所需环境(例如. Xcode 已更至最新,Android SDK 已经安装好,而且`ANDROID_HOME`也设置无误)。
|
||||
* 确保你应用的存放路径是正确的。
|
||||
* 在 windows 上运行 appium.app 要使用管理员权限,假如你在 cmd 中运行,也得确保是在管理员权限下。
|
||||
|
||||
### 如果你是通过 Appium.app 运行
|
||||
|
||||
* 升级应用并重启。如果你被告知应用无法升级,请到 [appium.io](http://appium.io) 重新下载。
|
||||
|
||||
### 如果你是通过 Appium 的源码运行
|
||||
|
||||
* 通过 `git pull` 命令拉取代码,确保当前的代码是最新的
|
||||
* 移除旧的依赖:`rm -rf node_modules`
|
||||
* 重新安装依赖:`npm install`
|
||||
* 代码 Re-transpile: `gulp transpile`
|
||||
|
||||
* 你可以使用 [Appium Doctor](https://github.com/appium/appium-doctor) 去检测 Appium 环境是否已经配置好了。
|
||||
* 如果你升级到 Android SDK 22 后出现如下报错:
|
||||
`{ANDROID_HOME}/tools/ant/uibuild.xml:155: SDK does not have any Build Tools installed.`
|
||||
在 Android SDK 22 中,platform 与 build tools 分别被拆分到各自的 SDK 管理包中去了。你需要确保已经正确安装了 build-tools 与 platform-tools。
|
||||
|
||||
### Android
|
||||
|
||||
* 确保 Android 模拟器已经开启并在运行中。
|
||||
* 出现设备连接问题时, `adb kill-server && adb devices` 这行命令非常有用。它可以重置你的 Android 设备的连接。
|
||||
* 确保你已经设置了 ANDROID_HOME 已经指向了 Android SDK 路径
|
||||
|
||||
|
||||
### Windows
|
||||
|
||||
* 确保已经开启了开发者模式。
|
||||
* 确保 command prompt 已经是管理员权限。
|
||||
* 检查 Appium 服务器正在监听的 URL 是否与你测试脚本中的 URL 匹配的上。
|
||||
|
||||
### IOS
|
||||
|
||||
* 确保 Instruments.app 没有被开启。
|
||||
* 如果你使用模拟器时,记得不要让真机连上你的电脑。
|
||||
* 确保在手机的设置中的 accessibility 辅助功能是关闭的。
|
||||
* 确保应用是变异在当前运行的模拟器上。
|
||||
* 确保应用已编译在合适的模拟器(或真机)上(例如. 在模拟器上运行需要 debug 模式的包),否则你会出现`posix spawn`报错。
|
||||
* 如果你曾经用 sudo 运行过 Appium,你可能需要运行 `sudo rm /tmp/instruments_sock` 该命令,而且记住以后尽量别带上 sudo。
|
||||
* 如果你是第一次运行 Appium,记得对 Instruments 进行授权。 查阅 [running on OSX documentation](./running-on-osx.md#authorizing-ios-on-the-computer) 了解更多。
|
||||
* 如果在真机上运行 Instruments 出现了崩溃("exited with code 253"),确保 Xcode 已经下载了设备的符号文件。到 Window -> Devices,然后他就会自动的开始下载。每次 iOS 版本升级后都需要做这步。
|
||||
* 如果你看到 `iOS Simulator failed to install the application.` 这样的报错,并且确定路径没有设置错误的话,那你可以尝试去重启你的电脑。
|
||||
* 确保你的 macOS 上的 keychain 已经保存了用于构建你的应用的证书,并且 WebDeriverAgent 是已签名的。特别是你在使用 ssh 的情况下。通常失败的话会显示`签名`报错。
|
||||
* 如果你的应用中还有自定义的元素,他们或许不能通过默认的方式去使用 UIAutomaion(and therefore Appuim)进行自动化。你需要将 accessibility status 设置为'enabled'。在代码中设置的方式如下:
|
||||
|
||||
```center
|
||||
[myCustomView setAccessibilityEnabled:YES];
|
||||
```
|
||||
|
||||
* 在 iOS 上测试可能会出现类似内存泄露(包括性能不佳、程序挂起)的状况。如果你出现了类似的问题,这很可能是由于一个 NSLog 的已知问题所导致的。其中的一个解决办法就是将所有的 NSLog 代码移除。然而,还是有一些巧妙的处理方法,可以不重构就能解决。
|
||||
|
||||
### 解决办法 1
|
||||
NSLog 是一个宏且可以被定义的。例如:
|
||||
```objectivec
|
||||
// *You'll need to define TEST or TEST2 and then recompile.*
|
||||
|
||||
#ifdef TEST
|
||||
#define NSLog(...) _BlackHoleTestLogger(__VA_ARGS__);
|
||||
#endif // TEST
|
||||
#ifdef TEST2
|
||||
#define NSLog(...) _StdoutTestLogger(__VA_ARGS__);
|
||||
#endif // TEST2
|
||||
|
||||
void _BlackHoleTestLogger(NSString *format, ...) {
|
||||
//
|
||||
}
|
||||
|
||||
void _StdoutTestLogger(NSString *format, ...) {
|
||||
va_list argumentList;
|
||||
va_start(argumentList, format);
|
||||
NSMutableString * message = [[NSMutableString alloc] initWithFormat:format
|
||||
arguments:argumentList];
|
||||
|
||||
printf(message);
|
||||
|
||||
va_end(argumentList);
|
||||
[message release];
|
||||
}
|
||||
```
|
||||
|
||||
### 解决办法 2
|
||||
手动去替换掉 NSLog 封装的底层功能。该方法被 [Apple in a similar context.](https://support.apple.com/kb/TA45403?locale=en_US&viewlocale=en_US) 所推荐
|
||||
```objectivec
|
||||
extern void _NSSetLogCStringFunction(void(*)(const char *, unsigned, BOOL));
|
||||
|
||||
static void _GarbageFreeLogCString(const char *message, unsigned length, BOOL withSyslogBanner) {
|
||||
fprintf(stderr, "%s\\n", message);
|
||||
}
|
||||
|
||||
int main (int argc, const char *argv[]) {
|
||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
||||
int exitCode;
|
||||
|
||||
setbuf(stderr, NULL);
|
||||
|
||||
_NSSetLogCStringFunction(_GarbageFreeLogCString);
|
||||
exitCode = WOApplicationMain(@"Application", argc, argv);
|
||||
[pool release];
|
||||
return exitCode;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### Webview/Hybrid/Safari 应用的支持
|
||||
|
||||
* 确保真机上的 'Web Inspector' 为打开状态。
|
||||
* 确保你已经打开 Safari 的开发者模式(Safari - Advance Preferences- Developer menu for simulators)。
|
||||
* 确保你客户端的库提供的 appium 命令 `context` 可以让你正确地切换 contexts。
|
||||
* 当你尝试打开代理的时候,出现了这个报错:select_port() failed,请查阅该[文档](https://groups.google.com/forum/#!topic/appium-discuss/tw2GaSN8WX0)。
|
||||
* 在 Safari session 中,如果日志记录到不能输入初始 url 的问题,先确保你的软键盘是否已被开启,详情请查阅该[文档](https://github.com/appium/appium/issues/6440)。
|
||||
|
||||
### 到社区寻求帮助
|
||||
|
||||
如果上述步骤还没解决你的问题,那你可以通过以下方式获得帮助:
|
||||
|
||||
当你在使用 Appium 的过程中有任何问题,而且 Appium 提供的报错信息不够清晰的话,欢迎加入[讨论组](https://discuss.appium.io)与大家进行讨论。提问时请附带上如下信息:
|
||||
|
||||
* 你是通过什么方式运行 Appium(Appium.app, npm, source)。
|
||||
* 你使用什么操作系统。
|
||||
* 你是针对什么设备和版本去做测试的(例如. Android 4.4, 或者 iOS 7.1)。
|
||||
* 你是使用真机还是模拟器去做测试。
|
||||
* 提供客户端和服务端给出的的错误(例如. “在运行我的 Python 测试脚本时候出现了异常,Appium 服务器的报错信息如链接中所示”)。
|
||||
* 除了上述,在提问的时候希望可以附带上 Appium 服务器输出的内容(特别是在 verbose 模式下),这样我们就可以更好地分析并跟进问题。
|
||||
|
||||
如果你确信你发现的是一个 bug,请直接到 [issue tracker](https://github.com/appium/appium/issues) 去提交一个 issue 去描述 bug 的信息以及重现步骤。
|
||||
|
||||
### 已知问题
|
||||
|
||||
* 如果你已在官网下载并安装 Node,你需要使用 sudo 去运行 `npm`。可这么做这并不理想。可以尝试通过 [nvm](https://github.com/creationix/nvm), [n](https://github.com/visionmedia/n) 或者 `brew install node` 这几种方式去安装!
|
||||
* 通过设置代理,iOS 真机可以支持 Webview 了,详情可查看[讨论](https://groups.google.com/d/msg/appium-discuss/u1ropm4OEbY/uJ3y422a5_kJ)。
|
||||
* 有时候 iOS 的 UI 元素在被定位到后的几毫秒间会失效,这会导致一个类似 `(null) cannot be tapped` 的报错。唯一的解决办法就是把 finding-and-clicking 的代码放进一个 retry block 中。
|
||||
* 如果你是通过 MacPorts 去安装 Node 与 npm,Appium 可能很难找到可执行的 `node`。你必须确保 MacoPorts 的 bin 文件夹(默认是 `/opt/local/bin`)已经添加到你的 `~/.profile`, `~/.bash_profile` 或者 `~/.bashrc` 中的 `PATH` 环境变量中。
|
||||
|
||||
### 特定的错误
|
||||
|
||||
|Action|Error|Resolution|
|
||||
|------|-----|----------|
|
||||
|Running ios test|`[INST STDERR] posix spawn failure; aborting launch`|你的应用没有分别对应模拟器或者真机去编译对应版本.|
|
||||
|Running mobile safari test|`error: Could not prepare mobile safari with version '7.1'`|你可能需要再次运行授权的脚本以确保 iOS SDK 文件是可写状态。详情请查阅 [running on OSX documentation](./running-on-osx.md#authorizing-ios-on-the-computer)|
|
||||
|
||||
本文由 [thanksdanny](https://testerhome.com/thanksdanny) 翻译,由 [lihuazhang](https://github.com/lihuazhang) 校验。
|
||||
@@ -0,0 +1,155 @@
|
||||
## 从源码运行Appium
|
||||
|
||||
你想从源码运行Appium并帮助修复BUG和添加功能吗?
|
||||
真棒!只需要fork工程,添加一个修改,然后发送pull请求即可!
|
||||
在开始之前请阅读我们的代码风格指南([Style Guide](style-guide-2.0.md)。
|
||||
在发送pull请求前请确保通过单元和功能测试;关于如何运行测试等更多信息,请继续阅读!
|
||||
|
||||
首先,确保你阅读README文件且按照设置说明走。
|
||||
|
||||
### 从源码配置 Appium
|
||||
|
||||
Appium 的配置涉及:
|
||||
|
||||
1. Appium Server —— 在你的测试代码和设备或模拟器之间通过 Appium Server 来回发送消息
|
||||
2. 测试脚本 —— 任何客户端语言都可以,只要和 Appium 兼容
|
||||
|
||||
运行 Appium Server,然后运行你的测试。
|
||||
|
||||
快速开始:
|
||||
|
||||
```center
|
||||
git clone https://github.com/appium/appium.git
|
||||
cd appium
|
||||
npm install
|
||||
gulp transpile # requires gulp, see below
|
||||
npm install -g authorize-ios # for ios only
|
||||
authorize-ios # for ios only
|
||||
node .
|
||||
```
|
||||
```center
|
||||
git clone https://github.com/appium/appium.git
|
||||
cd appium
|
||||
npm install
|
||||
gulp transpile # 需要gulp,往下看
|
||||
npm install -g authorize-ios # 仅iOS
|
||||
authorize-ios # 仅iOS
|
||||
node .
|
||||
```
|
||||
|
||||
### 捣鼓改造 Appium
|
||||
|
||||
Make sure you have `ant`, `maven`, `adb` installed and added to system `PATH`, also you
|
||||
would need the android-16 sdk (for Selendroid) and android-19 sdk installed.
|
||||
From your local repo's command prompt, install the following packages using the
|
||||
following commands (if you didn't install `node` using Homebrew, you might have
|
||||
to run `npm` with sudo privileges):
|
||||
|
||||
|
||||
确保已安装 `ant`、 `maven`、 `adb` 且已添加到 `PATH` 环境变量,还需要安装 android-16 的 sdk(Selendroid需要使用)和 android-19 的 sdk。在 Appium 本地仓库打开命令行,使用以下命令安装以下包(如果你没有使用Homebrew安装过 node,可能需要使用 sudo 运行 npm):
|
||||
|
||||
```center
|
||||
npm install -g mocha
|
||||
npm install -g gulp
|
||||
npm install -g gulp-cli
|
||||
npm install -g appium-doctor && appium-doctor --dev
|
||||
npm install
|
||||
gulp transpile
|
||||
```
|
||||
|
||||
前两个命令安装测试和构建工具(如果您通过 Homebrew 安装了 nodejs,则 `sudo` 可能不是必需的)。 第三个命令验证所有依赖关系是否正确设置(因为构建 Appium 的依赖关系不同于简单运行 Appium 的依赖项),而第四个命令将安装所有应用程序依赖关系并构建支持二进制文件和测试应用程序。 最终的命令将转换所有代码,以便 nodejs 可以运行它。
|
||||
|
||||
|
||||
When pulling new code from GitHub, if there are changes to `package.json` it
|
||||
is necessary to remove the old dependencies and re-run `npm install`:
|
||||
|
||||
当从 GitHub 中拉出新的代码时,如果 `package.json` 有更改,需要删除旧的依赖关系并重新运行 `npm install`:
|
||||
|
||||
```center
|
||||
rm -rf node_modules
|
||||
npm install
|
||||
gulp transpile
|
||||
```
|
||||
|
||||
此时,您将可以启动 Appium Server:
|
||||
|
||||
```center
|
||||
node .
|
||||
```
|
||||
|
||||
完整的参数列表,请参考[the server documentation](/docs/cn/writing-running-appium/server-args.md)
|
||||
|
||||
|
||||
#### Hacking with Appium for iOS
|
||||
|
||||
#### 鼓捣 iOS 上的Appium
|
||||
|
||||
为了避免启动iOS应用程序时可能出现的安全对话框,您必须通过以下两种方式之一修改 `/etc/authorization` 文件:
|
||||
|
||||
1. 手动修改 `/etc/authorization` 文件中的 `<key>system.privilege.taskport</key>` 下的 `<allow-root>`的值为 `<true/>`。
|
||||
2. 运行以下命令,为您自动修改 `/etc/authorization` 文件:
|
||||
|
||||
```center
|
||||
npm install -g authorize-ios
|
||||
sudo authorize-ios
|
||||
```
|
||||
|
||||
|
||||
此时,运行:
|
||||
|
||||
```center
|
||||
rm -rf node-modules
|
||||
npm install
|
||||
gulp transpile
|
||||
```
|
||||
|
||||
现在你的 Appium 实例已经准备好了。运行 `node .` 以启动Appium服务器。
|
||||
|
||||
#### 鼓捣 Android 上的Appium
|
||||
|
||||
通过运行以下命令配置Appium:
|
||||
|
||||
```center
|
||||
rm -rf node-modules
|
||||
npm install
|
||||
gulp transpile
|
||||
```
|
||||
|
||||
确保您只有一个 Android 模拟器或设备运行,例如,通过在另一个进程中运行此命令(假设 `emulator` 命令在您的路径上):
|
||||
|
||||
```center
|
||||
emulator -avd <MyAvdName>
|
||||
```
|
||||
|
||||
现在,您可以通过 `node .` 运行Appium服务器了。
|
||||
|
||||
#### 确保你的代码是最新的
|
||||
|
||||
由于Appium使用某些软件包的开发版本,因此通常需要安装新 `npm` 软件包或更新各种软件。运行 `npm install` 将更新所需的一切。当 Appium 升级版本时,您还需要执行此操作。在运行 `npm install` 之前,建议先删除 `node_modules` 目录中的所有旧依赖项:
|
||||
|
||||
```center
|
||||
rm -rf node-modules
|
||||
npm install
|
||||
gulp transpile
|
||||
```
|
||||
|
||||
### 运行测试
|
||||
|
||||
首先,请查看我们关于[running tests in
|
||||
general](/docs/cn/writing-running-appium/running-tests.md) 的文档,确保您的系统正确设置为您希望测试的平台。
|
||||
|
||||
一旦您的系统配置完毕,您的代码是最新的,您可以运行单元测试:
|
||||
|
||||
```center
|
||||
gulp once
|
||||
```
|
||||
|
||||
您可以对所有受支持的平台运行功能测试(确保在另一个窗口中运行Appium `node .`)与:
|
||||
|
||||
```center
|
||||
gulp e2e-test
|
||||
```
|
||||
|
||||
在提交代码之前,请运行 `gulp once` 一些基本测试,并根据代码质量标准检查您的更改。
|
||||
|
||||
本文由 [校长](https://testerhome.com/xushizhao) 翻译,由 [lihuazhang](https://github.com/lihuazhang) 校验。
|
||||
@@ -0,0 +1,262 @@
|
||||
# Appium 简述
|
||||
|
||||

|
||||
|
||||
## appium
|
||||
- 运行时的基本检查
|
||||
- 节点版本(>= 4)
|
||||
- CLI 参数检查
|
||||
- 包含所有可用和支持的CLI参数
|
||||
- 检是否有冲突和弃用
|
||||
- 统一存放日志
|
||||
- 统一存放npmlog, winston和appium-logger
|
||||
- 启动AppiumDriver(继承Basedriver)
|
||||
- 为iOS/Android/Selendroid/Fake Driver 建立会话
|
||||
- 创建/删除Appium会话
|
||||
- 启动baseServer (appium-express)
|
||||
- 通过驱动给出的路径
|
||||
|
||||
## appium-express (部分appium basedriver)
|
||||
- 启动express server(允许x-domain-origin)
|
||||
- 从AppiumDriver初始化
|
||||
- 超时处理
|
||||
- 提供一个用于测试的静态页面
|
||||
- 将req/res事件记入日志
|
||||
|
||||
## mobile-json-wire-protocol (部分appium basedriver)
|
||||
- 提供了Appium命令列表
|
||||
- 通过协议的驱动子类
|
||||
-客户端和驱动程序之间的中间件
|
||||
- 处理驱动程序的jwp代理
|
||||
- 包含所有类型错误的错误类
|
||||
- 消除错误响应
|
||||
- 将参数封装/解封到命令中
|
||||
- 检查所需参数,验证参数
|
||||
|
||||
## appium basedriver
|
||||
- 每个实例化都有一个单独的测试会话
|
||||
- 包含对caps的约束 (platformName必须存在等)
|
||||
- 验证功能
|
||||
- 运行具有单个并发性的命令
|
||||
- 处理会话重启
|
||||
- 处理滑动选项
|
||||
- DeviceSettings负责设备的get/update管理
|
||||
- 包含基本命令
|
||||
- 找到元素
|
||||
- 创建/删除会话
|
||||
- 处理超时
|
||||
- 设备的set/update设置
|
||||
- 为命令提供帮助方法
|
||||
|
||||
## appiium iosdriver
|
||||
- 也可以作为独立服务器运行(从appium-express继承了小部分服务)
|
||||
- 支持的策略:“name”,“xpath”,“id”,“-ios uiautomation”,“class name”,“accessibility id”
|
||||
- 可以启动一个模拟器或一个真实的设备(需要标明udid)
|
||||
- 每个类型的说明
|
||||
- removeInstrumentsSocket
|
||||
- setBundleIdFromApp
|
||||
- createInstruments
|
||||
- runSimReset, isolateSimDevice, setLocale || runRealDeviceReset
|
||||
- setPreferences || runRealDeviceReset
|
||||
- startLogCapture
|
||||
- prelaunchSimulator || (noop)
|
||||
- startInstruments
|
||||
- onInstrumentsLaunch
|
||||
- configureBootstrap
|
||||
- setBundleId
|
||||
- setInitialOrientation
|
||||
- initAutoWebview
|
||||
- waitForAppLaunched
|
||||
- 所有帮助说明都在driver.js中
|
||||
- 包含一组更具体的功能约束
|
||||
- 有使用appium-xcode构建和运行Safari(safari-launcher)的逻辑
|
||||
- 实现iOSdriver的命令
|
||||
- Selenium命令被编译为ui-automator命令
|
||||
- 命令将会发送到uiAutoClient(appium-uiauto)
|
||||
- 连接到appium远程调试器
|
||||
|
||||
## appium-xcode
|
||||
- 运行shell命令从Xcode返回有用的数据,比如
|
||||
- getPath
|
||||
- getVersion
|
||||
- getAutomationTraceTemplatePath
|
||||
- getMaxIOSSDK
|
||||
- …
|
||||
- 内置自动重试
|
||||
|
||||
## appium-uiauto
|
||||
- 封装 iOS UI Automation 框架的
|
||||
- 通过socket连接进行通信
|
||||
- 运行由sendCommand函数填充的命令队列
|
||||
- 处理来自ui-automation的响应(作为缓冲区)
|
||||
- 使用osascript旋转屏幕截图
|
||||
- 提供bootstrap simulator/realdevice (dynamic-bootstrap)的方法
|
||||
- 在iOS UI自动化上下文(非节点)中运行的JavaScript文件
|
||||
- 负责执行实际的iOS UI Automation命令
|
||||
- 参考 [UIAutomation docs](https://developer.apple.com/library/ios/documentation/DeveloperTools/Reference/UIAutomationRef/)
|
||||
- 命令流程就像
|
||||
- Webdriver 命令-> iOS driver 命令 -> uiauto-命令
|
||||
|
||||
## appium-instruments
|
||||
- 封装运行instruments的命令
|
||||
- 很多exec调用与instruments二进制义互
|
||||
- 所有这些都采取回调来获取结果
|
||||
- 必须使用“iwd”包编译软件包
|
||||
- 特殊的Instruments包不存在命令延迟
|
||||
- 还包含较旧版本的iwd instrument(v4 - v7)
|
||||
|
||||
## appium-ios日志
|
||||
- 捕获iOS模拟器或实际设备的控制台,性能和崩溃日志
|
||||
- 通过调用tail来从系统路径获取日志(模拟器设备)
|
||||
- 或通过调用deviceconsole(实际设备)
|
||||
- 使用远程调试器来抓取性能日志
|
||||
- 崩溃日志保留在系统上的“.crash”文件中
|
||||
|
||||
## appium-iOS-模拟器
|
||||
- 封装iOS模拟器应用程序
|
||||
- 启动和关闭(杀死所有)模拟器
|
||||
- 更新设置和本地
|
||||
- 更新/清除 safari
|
||||
- 获取有关模拟器设备的元数据
|
||||
- 使用simctl与模拟器通信
|
||||
- 适用于Xcode 6和7
|
||||
|
||||
## authorize-iOS
|
||||
- 程序预先授权Instruments针对iOS设备运行UIAutomation脚本
|
||||
- 通过调用“DevToolsSecurity -enable”来启用开发人员工具
|
||||
- 授权用户作为开发人员调用“authorizationdb”
|
||||
- 改变模拟器目录的所有权
|
||||
|
||||
## node-simctl
|
||||
- 封装simctl二进制(cli实用程序来控制iOS模拟器)
|
||||
- 作为xcrun的子命令执行(从命令行查找或调用开发人员工具)
|
||||
- 包含功能
|
||||
- 安装/删除应用程序
|
||||
- 启动和关闭模拟器
|
||||
- 创建/清除/删除设备
|
||||
- 获取设备列表
|
||||
|
||||
## appium-cookies
|
||||
- 简单的包来创建和接收cookie
|
||||
- 在appium-iosdriver中使用,以在Web上下文中实现jswonwire cookie命令
|
||||
|
||||
## appium-chromedriver
|
||||
- 封装 chromedriver
|
||||
- 下载并安装chromedriver二进制文件
|
||||
- 启动,重新启动并停止(或杀死所有)chrome实例
|
||||
- 使用appium-jsonwp-proxy向驱动程序发送json wire protocol命令
|
||||
|
||||
## jsonwp-proxy (部分appium basedriver)
|
||||
- 允许将json wire协议命令发送到了解它的服务器(浏览器驱动程序)
|
||||
- 解析json的响应
|
||||
- 允许代理服务器的请求
|
||||
- 用于在Chromium-Chromedriver和appium-selendroiddriver中进行通信
|
||||
|
||||
## appium-androiddriver
|
||||
- 类似于appium-iosdriver,它可以作为独立服务运行
|
||||
- 自动化模拟器和实际设备上的本地,混合和移动Web应用程序
|
||||
- 负责安装Android软件包到设备
|
||||
- 如果需要,运行chromedriver会话
|
||||
- 包含一组更具体的功能约束
|
||||
- 使用appium-adb与emulator/simulator/realdevice进行交互
|
||||
- 和appium-android-bootstrap来执行实际的命令
|
||||
- 包含帮忙找出哪个网页视图属于哪个应用程序包,反之亦然
|
||||
|
||||
|
||||
## appium-adb
|
||||
- 封装 Android Debug Bridge(adb)
|
||||
- 包含一些基本的rpc到adb二进制的命令
|
||||
- 容纳jar文件来运行特殊用例,例如签名,验证应用程序或移动清单
|
||||
- 允许与webdriver协议无关的特殊(移动专用)模拟器命令
|
||||
- 锁定屏幕
|
||||
- 按返回按钮
|
||||
- 按home按钮
|
||||
- 设置/获取飞行模式
|
||||
- 设置/获取wifi状态
|
||||
- 捕获logcat
|
||||
- 处理模拟器/模拟器动作(例如重启)
|
||||
|
||||
## appium-androidbootstrap
|
||||
- JavaScript界面和Java代码,用于与Android UI Automator进行交互
|
||||
- 构建包含执行命令的逻辑的AppiumBootstrap.jar
|
||||
- 对应的 iOS 上的 appium-uiauto
|
||||
- 一旦启动,就会创建一个到设备的web scoket连接
|
||||
- 应用程序提供启动/关闭/发送命令接口
|
||||
- 命令流程如下:
|
||||
- Selenium 命令 - > appium-adb - > appium-androidbootstrap - > 使用Android UI Automator 框架的 Java 代码
|
||||
|
||||
|
||||
## appium-uiautomator
|
||||
- 启动和关闭uiautomator服务器由appium-android-bootstrap jar建立
|
||||
- 命令流程就像
|
||||
- appium-android-bootstrap:start -> appium-uiautomator:start -> appium-adb:install bootstrap
|
||||
|
||||
## appium-selendroiddriver
|
||||
- 类似于appium-androiddriver,它可以作为独立服务运行
|
||||
- 使用appium-selendroid-installer下载并安装Selendroid
|
||||
- 包含几个Selendroid特定的逻辑,以确保无缝集成
|
||||
- 包含一组更具体的功能约束
|
||||
- 使用jsonwp-proxy与服务器交互
|
||||
- 使用appium-adb启用在Selendroid中未实现的命令
|
||||
|
||||
## appium-selendroid 安装程序
|
||||
- 包含并导出设置逻辑
|
||||
- 下载Selendroid
|
||||
- 确定AndroidManifest的位置
|
||||
- 确定Server APK的位置
|
||||
- 提取这两个文件
|
||||
- 复制和清理文件
|
||||
|
||||
## appium-android-ime
|
||||
- 允许从 Android 设备发送和接收 unicode 字符
|
||||
- 将文本编码为UTF-7将其发送到设备并将其重新编码为 Unicode
|
||||
- 由 appium-androiddriver 和 appium-selendroiddriver 使用
|
||||
|
||||
## appium-doctor
|
||||
- 在启动Appium之前诊断,报告和修复常见的Node,iOS和Android配置问题
|
||||
- 暴露cli命令“appium-doctor”
|
||||
- 它检查
|
||||
- Android:
|
||||
- android sdk存在并正确配置
|
||||
- env变量和路径检查
|
||||
- iOS:
|
||||
- 安装了xcode(使用命令行工具)
|
||||
- 开发工具安全检查
|
||||
- auth检查
|
||||
- nodejs 检查
|
||||
|
||||
## appium-gulp-plugins
|
||||
- 具有定制插件的开发包使用交叉应用模块(仅适用于Appium开发)
|
||||
- 包含任务
|
||||
- e2e和单元测试(覆盖率报告)
|
||||
- 将ES2016 转换成 ES5
|
||||
- 静态代码分析(jshint)
|
||||
- 开发人员的任务
|
||||
|
||||
## appium-remote-debugger
|
||||
- RPC客户端将Appium连接到iOS网页浏览
|
||||
- 可以连接到WebKit devtools
|
||||
- 仅适用于iOS
|
||||
- 有两个rpc客户端类
|
||||
- remote-debugger-rpc-client:使用连接到localhost的tcp6:27753
|
||||
- webkit-rpc-client:使用WebSockets连接到ws:// localhost:27753 / devtools / page / $ {pageId}
|
||||
|
||||
## node-teen_process
|
||||
- 助手模块暴露:
|
||||
- exec:在引擎盖下使用spawn的exec的ES7(async / await)实现
|
||||
- SubProcess:在使用spawn时减少样板(特别是在async / await上下文中使用时)
|
||||
|
||||
## appium 日志
|
||||
- 运行时,日志器默认为 npmlog,
|
||||
- 暴露了几乎所有的Appium软件包使用的getLogger函数
|
||||
- 如果有一个已经运行的记录器,那么所有的东西都会记录
|
||||
|
||||
## appium-support
|
||||
- 用于支持跨应用程序包的库的实用程序函数。
|
||||
- 为一些常见的操作提供封装,如
|
||||
- 系统方法(isWindows,isLinux ...)
|
||||
- 实用程序方法,如hasValue,escapeSpace
|
||||
- 一堆fs方法
|
||||
- plist帮助解析和更新plist文件
|
||||
|
||||
本文由 [校长](https://testerhome.com/xushizhao) 翻译,由 [lihuazhang](https://github.com/lihuazhang) 校验。
|
||||
|
After Width: | Height: | Size: 152 KiB |
@@ -0,0 +1,19 @@
|
||||
## 鸣谢
|
||||
|
||||
### 以下是在Appium的开发过程中予我们以启示,或是为Appium锦上添花的项目
|
||||
|
||||
* [Apple UIAutomation](https://developer.apple.com/library/ios/documentation/DeveloperTools/Reference/UIAutomationRef/)
|
||||
* [GhostDriver](https://github.com/detro/ghostdriver)
|
||||
* [IOS Auto](https://github.com/penguinho/applecart)
|
||||
* [IOS Driver](https://github.com/ios-driver/ios-driver)
|
||||
* [Mechanic.js](https://github.com/jaykz52/mechanic)
|
||||
* [node-webkit](https://github.com/rogerwang/node-webkit)
|
||||
* [Remote Debug](https://github.com/leftlogic/remote-debug)
|
||||
* [Selenium Project](http://code.google.com/p/selenium/)
|
||||
* [ios-webkit-debug-proxy](https://github.com/google/ios-webkit-debug-proxy)
|
||||
* [instruments-without-delay](https://github.com/facebook/instruments-without-delay)
|
||||
* [deviceconsole](https://github.com/rpetrich/deviceconsole)
|
||||
* [WinAppDriver](http://github.com/Microsoft/WinAppDriver)
|
||||
* [WebDriverAgent](https://github.com/facebook/webdriveragent)
|
||||
|
||||
本文由 [ZhaoC](https://github.com/ZhaoC) 翻译,由 [oscarxie](https://github.com/oscarxie) 校验。
|
||||
@@ -0,0 +1,140 @@
|
||||
## 一个开发人员对于Appium的概述
|
||||
|
||||
在阅读这个此文档之前,请确保你已经阅读并理解了关于[Appium的基本概念](/docs/cn/about-appium/intro.md),
|
||||
以及关于如何参与[Appium贡献说明](/CONTRIBUTING.md)
|
||||
|
||||
### 技术愿景
|
||||
|
||||
Appium致力于成为一个移动JSONWP前端,实现最佳的应用自动化技术。仅此而已。我们想要采用所有不同的
|
||||
但自带亮点的自动化引擎,通过制作Appium驱动程序来平滑它们的差异和瑕疵,同时将它们纳入Appium。这与
|
||||
Selenium项目有异曲同工之妙。对于我们而言,我们想要每一个驱动都是一个独立的实体(单独的repo, 测试,
|
||||
等等)即便它们使用共享库,这一举措会使Appium驱动程序的开发变得尽可能简单,而且不拘泥于样板。我们之所以
|
||||
使用现今流行的JavaScript,因为它们无处不在,而且对于很多开发人员而言容易理解并易于贡献。
|
||||
|
||||
### 开发者社区
|
||||
|
||||
欢迎任何人成为Appium开发人员,你仅需要阅读此指南并把你的一些代码合并进来,那么你就成为了我们一员。
|
||||
如果你坚持不懈并且广施善缘,我们将会把你升级为一名提交者,这样你能继续帮助社区发展而且省去很多纷繁复杂。
|
||||
如果你在开发Appium的时候遇到疑问,请前往我们的开发人员社区`appium-developers@googlegroups.com`.
|
||||
请注意,这是一个为_开发_疑问而设的邮件列表,而不是关于_使用_疑问或bug提交。 有关使用问题请前往[discuss.appium.io](https://discuss.appium.io)
|
||||
Github问题跟踪只是用于bug提交和功能需求。
|
||||
|
||||
### 敏捷开发工作流程
|
||||
|
||||
Appium团队根据轻量化的scrum版本来运行开发。每两个星期我们开始一个新的“sprint”,或者每一段时间,我们
|
||||
会决定团队下一阶段需要完成的事情。我们欢迎任何熟悉Appium代码库的人参加我们的sprint计划,并将视作SCRUM参与团队成员。
|
||||
参与者无需是代码长期提交者。sprint期间,我们将在[Appium slack 团队](https://appium.slack.com)的
|
||||
`#standup` 聊天室更新我们每个人的进程(没有实时的每天站立会议)。sprint结束时,我们将举行“回顾”活动,以此来
|
||||
庆祝我们取得的成就并总结活动进展如何,这可能会生成一个关于如何进行不同尝试又或者如何做的更好的总结列表。
|
||||
|
||||
最终,我们的目标是在每一个sprint结束时发布一个Appium版本,也就是说每两个星期一个周期。我们还没有实现这个目标,但希望
|
||||
我们能很快达成。
|
||||
|
||||
当前会议时间:
|
||||
* sprint计划: 每两个周一 10:00 AM - 10:45 AM (太平洋时间)
|
||||
* sprint回顾: 每三个周五 1:00 PM - 1:30 PM(太平洋时间)
|
||||
|
||||
我们使用[Zoom](https://zoom.us)视讯来举行会议
|
||||
|
||||
关于项目管理,我们使用[ZenHub](http://zenhub.com) 浏览器插件,它向GitHub接口提供了例如
|
||||
Kanban boards 和 Epics 这些功能。要完全参与到Appium SCRUM,你需要安装上述的这些浏览器插件。
|
||||
|
||||
如果你有意参加sprint,你可以在Appium Slack群组里联系`@jlipps` 或者 `@isaac`,又或者直接在推特
|
||||
上联系`@jlipps`,我们将会在下一次sprint里分享如何加入视频聊天。
|
||||
|
||||
### 构造
|
||||
|
||||
Appium主要是一个[node.js](http://nodejs.org)包的集合,以此组合形成一个运行的node.js服务器。
|
||||
这些包都被单独维护并拥有各自的GitHub repo, CI和发布流程。一些软件包(比如 `appium-ios-driver`)
|
||||
很大,并且给Appinum添加了很重要的功能。相比而言,其他软件包则扮演着一个辅助的角色,而且高度曝光其某个
|
||||
特定并被反复使用的功能。
|
||||
|
||||
有关软件包的层次结构和每一个包所起的作用,请前往我们[包的概述](/docs/cn/contributing-to-appium/appium-packages.md)文档。
|
||||
|
||||
### 转化
|
||||
|
||||
Appium是用JavaScript的新形式编写的,称为ES6(或现在的ES2015)。因为这个版本的语言尚未被老版本
|
||||
node.js的原生支持,Appium代码是_被移植_到ES5(JS更为广泛支持的版本)。这个转化过程必须发生在所有
|
||||
代码运行之前。除了ES6的新语言特性,我们也从JS的_后续_版本中采用了很重要的关键词,`async` 和 `await`,
|
||||
这有助于编写整洁的异步代码。因为这个代换的步骤,Appium软件包囊括了观察代码变化和重新编排代码的工具。
|
||||
通常而言,同样的工具会自动运行单元测试,同时确保没有任何小的纰漏。大多Appium软件包在运行`gulp`的时候
|
||||
把上述过程当做默认行为。
|
||||
|
||||
### 排查和代码风格
|
||||
|
||||
对于所有Appium的JS而言,代码外观和使用感觉同样重要。这包括样式常规,编码模式以及我们解决各种问题时使用的
|
||||
库。你应该熟悉我们新的[ES2015 风格指南](/docs/cn/contributing-to-appium/style-guide-2.0.md)。
|
||||
当转化时,Appium包将自动运行JSHint或其他lint工具,并在代码不符合我们规范的时候提供警告或错误反馈。
|
||||
这些工具不一定能顾全我们关心的种种风格问题,所以我们在review代码的时候也应该注意代码规范问题。这不是
|
||||
吹毛求疵,而是为了有一个整洁,一致并且可读的代码库。
|
||||
|
||||
### 提交代码
|
||||
|
||||
将你的代码提交到Appium很容易:你只需要提交一个pull请求到我们的repos并在代码review的过程中与我们的
|
||||
维护者进行互动。我们对代码提交有一系列的要求(但是不用担心!如果下面所列的太过繁复,我们将会为你一一道来)。
|
||||
那我们就从你提交PR的那一步开始吧:
|
||||
|
||||
* 参考其他代码的样式和我们的样式指南
|
||||
* Atomic 提交 -- 每个逻辑变化进行一次提交(例如:确保你的提交不用进入组论即可以正常运行,而且它在任何指定提交下都应该正常运行)这通常意味着每个PR一个提交。你将想要非常熟悉的`git rebase -i` 和压缩。
|
||||
* 没有合并提交:在提交你的PR之前,通常在最新的master之上进行rebase(或者其它你想要merge进去的branch)。
|
||||
* 几乎所有的更改都应该有测试。关于漏洞修复,至少应该有单元测试以证明这个bug已被修复。新功能应该有单元测试,并且在大多数情况下进行e2e测试,以证明该功能正常工作。我们将乐意带你浏览测试的创建过程。阅读其他的测试代码会是一个很好的开始。我们的CI系统通常运行测试覆盖率统计,而且我们不大可能去合并那些会降低测试覆盖率的代码。
|
||||
|
||||
如果你在提交之前做了所有这些事情,你的代码将可能很快被接受。当然,如果你打算对Appium进行大量的改造工作,你可以联系开发人员,以确保这些改变符合我们的理念,这样在你开动之前确保我们会接受这些改动。
|
||||
|
||||
### 测试
|
||||
|
||||
始终确保你的更改已经被测试过! 除了单测试和e2e测试,请确保在你开始进行更改和提交review之前,所有现有测试都被运行。
|
||||
我们有为每一个Appium repo设置CI作为安全网,这样便于审阅者知晓代码是否已经通过测试。在任何Appium包运行测试都很容易!
|
||||
以下是你可以做的事情(除非README另有说明):
|
||||
|
||||
```
|
||||
gulp # 监测目录下重编译代码更改,以及运行单元测试
|
||||
gulp once # 同上,当不提供监测
|
||||
gulp unit-test # 转化和运行单元测试
|
||||
gulp e2e-test # 转化以及运行端到端/功能测试
|
||||
_FORCE_LOGS=1 <command> # 显示测试运行期间的模块日志输出
|
||||
```
|
||||
|
||||
请注意,单元测试的文件后缀通常为 `-specs.js` ,而e2e测试文件后缀则为`-e2e-specs.js`.
|
||||
|
||||
### 发布
|
||||
|
||||
任何非Appium主包的发布流程都是非常简洁明了的(请注意:如果你想要发布它,你需要成为一个NPM的所有者。
|
||||
所有权由Appium提交者管理; 如果你对所有者有任何疑问,请联系@jlipps 或者 @imurchie)。
|
||||
|
||||
0. `rm -rf node_modules && npm install` 并运行测试以确保全新安装正常工作
|
||||
0. 根据[SemVer](http://semver.org/) 规则决定我们是否需要发布一个补丁(漏洞修复),微调(功能)或者是主要(迭代)(请参考 [how SemVer works with NPM](https://docs.npmjs.com/getting-started/semantic-versioning).
|
||||
0. 通过任何适当的更改和提交来更新CHANGELOG以README文件。大多数子包没有CHANGELOG。
|
||||
0. 通过适当的版本类型运行 `npm version <version-type>`
|
||||
0. 将适当的分支推送到GitHub, 不要忘记加入`--tags` 来标记刚由 `npm version`创建的标志.
|
||||
0. 运行 `npm publish` (如果不是正式版, 请使用`--tag beta`).
|
||||
|
||||
对于Appium的主包发布,上述步骤必须执行,但有以下改变。一个原因是对于主包,我们使用NPM收缩包装
|
||||
以确保依赖在安装的时候不更改。另一个原因是我们在master上开发和各种分支上发布。它的工作方式
|
||||
如下:我们经常在master上开发和增加新的代码。 当我们准备好了做一个新的次要或主要版本(例如`1.5.0`
|
||||
或`2.0.0`),我们创建一个发布分支(分别为`1.5`或`2.0`)。然后我们发布该分支。 一旦我们认为需要修复补丁,
|
||||
我们首先将补丁拉到master中,然后将单个补丁挑选到发布分支(甚至是多个发布分支)。 然后我们再次
|
||||
从这些分支发布更新的补丁版本(例如`1.5.1`或`2.0.1`)。
|
||||
|
||||
**关于 `npm shrinkwrap`的注释:** 我们使用[npm shrinkwrap](https://docs.npmjs.com/cli/shrinkwrap)是为了在发布时锁定依赖关系。
|
||||
没有它,任何依赖包上的开发将在安装Appium时反应出来,这可能会导致问题。 由于配置文件`npm-shrinkwrap.json`仅存在于发布分支上,
|
||||
因此有必要在发布过程中手动管理它。 它需要与对`package.json`的更改一起提交到GitHub。
|
||||
|
||||
0. 如果 NPM shrinkwrap JSON 文件存在,请移除.
|
||||
0. `rm -rf node_modules && npm install` 并运行测试以确保全新安装正常工作
|
||||
0. `rm -rf node_modules && npm install --production` 以获取仅production部分.
|
||||
0. `npm shrinkwrap` 来编写新的 NPM shrinkwrap JSON 文件.
|
||||
0. 根据SemVer来决定我们是否需要发布一个补丁(漏洞修复),微调(功能)或者是主要(迭代)
|
||||
0. 用合适的新版本信息来更新`package.json`
|
||||
0. 对CHANGELOG/README进行合适的更改,同shrinkwrap 和 `package.json`的改变一起以PR的形式进行提交审核。待它被合并之后,把它`pull`进`release`分支。
|
||||
0. 在发布分支(通常是一个小分支,如`1.5`或`1.4`)上创建一个形式为`v <version>`的标签:`git tag -av <version>`,例如`git tag -a 1.5.0`。 这对测试版本不是必需的。
|
||||
0. 把标签推送到上游分支: `git push --tags <remote> <branch>`
|
||||
0. 安装dev依赖 (或者至少`gulp` 和 `appium-gulp-plugins`)
|
||||
0. 运行`npm publish`(如果这不是正式版本,请使用`--tag beta` )。
|
||||
0. 在appium.io更新文档。 从github的check out appium.io repo,check out`gh-pages`分支和并更新到最新版本。 然后运行`rake publish`。
|
||||
0. 在GitHub上创建一个新版本:转到`https://github.com/appium/appium/releases/tag/v <VERSION>`并点击“编辑标签”。 输入发布名称为`<VERSION>`(例如,`2.0.5`),然后粘贴到更改日志(但不是此版本的changelog标题)。 如果是试用版,请标记为预发布。
|
||||
0. 在 discuss.appium.io 创建新的帖子来宣布release. 请创建于 "News"类别. 粘贴在changelog和任何可选的评论。 置顶当前帖子并取消置顶上一个release帖子。
|
||||
0. 开始发布`appium-desktop`。
|
||||
0. 请告知 @jlipps,以便他可以发布一个链接到讨论帖子。
|
||||
|
||||
本文由 [ZhaoC](https://github.com/ZhaoC) 翻译,由 [oscarxie](https://github.com/oscarxie) 校验。
|
||||
@@ -0,0 +1,17 @@
|
||||
## Appium gulp命令
|
||||
|
||||
[Gulp](http://gulpjs.com)就像Node.js的`make`一样!我们使用它来自动化各种应用开发任务。
|
||||
以下是你可以执行的操作:
|
||||
|
||||
|任务|说明|
|
||||
| ---- | ----------- |
|
||||
| gulp once |清理,验错,转译以及运行单元测试|
|
||||
| gulp watch |当代码更改时,自动运行`gulp once`|
|
||||
| gulp lint |运行 JSLint |
|
||||
| gulp jshint |运行 JSHint |
|
||||
| gulp transpile |将我们的ES7/ES2015代码转译为ES5,生成`/build`目录+内容|
|
||||
| gulp unit-test |运行单元测试|
|
||||
| gulp e2e-test |运行e2e 测试 |
|
||||
| gulp docs |生成 docs/en/writing-running-appium/server-args.md 文档|
|
||||
|
||||
本文由 [ZhaoC](https://github.com/ZhaoC) 翻译,由 [oscarxie](https://github.com/oscarxie) 校验。
|
||||
@@ -0,0 +1,56 @@
|
||||
## 如何编写文档
|
||||
|
||||
`##`用于写一个h2头。 每个文档必须以h2开头。这是为了支持appium.io文档生成。不要使用`---`下划线方法创建标题。
|
||||
不要对标题使用h1`#`或`===`,因为它不支持目录(文件夹名称用作h1)。
|
||||
|
||||
### 副标题
|
||||
|
||||
`###` 用于编写副标题
|
||||
|
||||
### 常规标题
|
||||
|
||||
`####` 用于不出现在目录里的标题。
|
||||
不要使用h5 `#####`或者h6`######`
|
||||
|
||||
### 换行符
|
||||
|
||||
不要使用 `--` 或者 `---`这样的换行符。 这样会混淆版面。
|
||||
|
||||
### 链接
|
||||
|
||||
链接到 readme :
|
||||
|
||||
`[readme](../../README.md)`
|
||||
|
||||
链接到贡献榜:
|
||||
|
||||
`[contributing](../../CONTRIBUTING.md)`
|
||||
|
||||
链接到另一个文件
|
||||
|
||||
`[link text](filename.md)`
|
||||
|
||||
如果想链接文件内部,请使用`#` 作为版面URL
|
||||
|
||||
`[go direct to json](filename.md#json-wire-protocol-server-extensions)`
|
||||
|
||||
请注意,当标题改变时,哈希链接会被破坏掉。所以更好的选择是链接到文件的开头(用`other.md` 取代 `other.md#something`)
|
||||
|
||||
### 与appium.io的兼容性
|
||||
|
||||
### 在appium.io里中居对齐代码
|
||||
|
||||
Appium.io文档使用[slate](https://github.com/tripit/slate)获取文档。
|
||||
如果文档中的代码段不是特定的语言,或者你希望代码段保留在appium.io文档的居中部分,请使用`center`作为代码区的后缀。
|
||||
|
||||
例如:
|
||||
|
||||
```center
|
||||
代码段放置于此。
|
||||
```
|
||||
|
||||
#### 出版
|
||||
|
||||
要在appium.io上发布文档,请参阅[api-docs](https://github.com/appium/api-docs) 和 [appium.io](https://github.com/appium/appium.io).
|
||||
|
||||
本文由 [ZhaoC](https://github.com/ZhaoC) 翻译,由 [oscarxie](https://github.com/oscarxie) 校验。
|
||||
@@ -0,0 +1,257 @@
|
||||
## 给贡献者的风格指南
|
||||
|
||||
感谢您对Appium的贡献!以下是我们编写javascript代码时需要遵守的准则,请确认你的提交能符合这些规范,这有利于我们合并你的代码时能保持良好的编码风格。其中最核心的准则是:*使你的代码与其他代码的编码风格保持一致*。
|
||||
|
||||
### 衍合(Rebasing)
|
||||
|
||||
每个 pull 请求中的提交(commits)都应该包含[逻辑变更(logical changes)](https://github.com/appium/appium/pull/920#issuecomment-21588553)。
|
||||
如果有多位贡献者,请确保他们各自都有自己的提交记录,修改作者信息不是一个好主意。合并(merge)提交必须从 pull 请求中 rebase 。
|
||||
|
||||
### 检错(Linting)
|
||||
|
||||
所有的代码(除了使用了 Apple 私有方法的`bootstrap.js`代码)必须通过 JSLint 的检错测试。你可以在 Appium 存储目录下,运行`grunt lint`来检查你的代码。如果你已创建一个新的 .js 文件,请确认它在`grunt.js`中被通配符覆盖,或者被专门添加。
|
||||
|
||||
编辑器的即时检错集成很简单,并且这会使得整个编码过程更加顺利。 我们喜欢 [jshint](http://www.jshint.com), 因为它已经与许多源代码编辑器集成了。将文件`.jshintrc`加入到仓库中,当通过添加jshint到你的编辑器时,你将大受即时检错的裨益。
|
||||
|
||||
因为jshint不再强制检查代码风格,我们也使用 [jscs](https://github.com/mdevils/node-jscs),它也集成在一些源代码编辑器中。
|
||||
|
||||
这些配置文件定义了哪些警告类型将会出现在你的编辑器中。 查看 [this page for jshint](http://www.jshint.com/platforms/) 和 [this page for jscs](https://github.com/mdevils/node-jscs#friendly-packages) ,找到它们各自支持的编辑器和平台的列表,以及如何设置你的编辑器使之能自动化检错。
|
||||
|
||||
### 风格注释(Style notes)
|
||||
|
||||
我们使用前沿版本的 JavaScript,并利用Babel将其渲染到支持当前版本的 Node.js。我们使用 [ES2015](https://babeljs.io/)(以前称为ES6)中的一些非标准特性,如[`async / await`](http://babeljs.io/docs/plugins/syntax-async-functions/)。在所有Appium的贡献中必须严格遵循如下编码风格!
|
||||
|
||||
* 使用两个空格来缩进, *不要使用 tabs*
|
||||
* 在运算符两边,分别添加一个空格:
|
||||
|
||||
```javascript
|
||||
var x = 1;
|
||||
```
|
||||
|
||||
而不是
|
||||
|
||||
```javascript
|
||||
var x=1;
|
||||
```
|
||||
|
||||
* 在列表(lists),对象(objects),函数调用(function calls)等语句块中,逗号和冒号后面需要添加一个空格:
|
||||
|
||||
```javascript
|
||||
var x = myFunc("lol", {foo: bar, baz: boo});
|
||||
```
|
||||
|
||||
而不是
|
||||
|
||||
```javascript
|
||||
var x = myFunc("lol",{foo:bar,baz:boo});
|
||||
```
|
||||
|
||||
* 代码始终以分号结尾
|
||||
* 以逗号开头
|
||||
|
||||
```javascript
|
||||
var x = {
|
||||
foo: 'bar'
|
||||
, baz: 'boo'
|
||||
, wuz: 'foz'
|
||||
};
|
||||
```
|
||||
|
||||
* 左花括号应该和`function`,`if`等写在同一行,`else`应该被夹在两个花括号中间:
|
||||
|
||||
|
||||
```javascript
|
||||
if (foo === bar) {
|
||||
// do something
|
||||
} else {
|
||||
// do something else
|
||||
}
|
||||
```
|
||||
|
||||
* `if`,`for`, 和`function`之后需要添加空格:
|
||||
|
||||
```javascript
|
||||
if (foo === bar) {
|
||||
```
|
||||
```javascript
|
||||
for (var i = 0; i < 10; i ++) {
|
||||
```
|
||||
```javascript
|
||||
var lol = function (foo) {
|
||||
```
|
||||
|
||||
而不是
|
||||
|
||||
```javascript
|
||||
if(foo === bar) {
|
||||
```
|
||||
```javascript
|
||||
for(var i = 0; i < 10; i ++) {
|
||||
```
|
||||
```javascript
|
||||
var lol = function(foo) {
|
||||
```
|
||||
|
||||
* 只有一行代码时,`if`语句块的花括号也应该添加上:
|
||||
|
||||
```javascript
|
||||
if (foo === bar) {
|
||||
foo++;
|
||||
}
|
||||
```
|
||||
|
||||
而不是
|
||||
|
||||
```javascript
|
||||
if (foo === bar)
|
||||
foo++;
|
||||
```
|
||||
|
||||
除了出错后直接调用回调函数(callback)处理错误(error)的语句
|
||||
|
||||
```javascript
|
||||
if (err) return cb(err);
|
||||
```
|
||||
|
||||
* 一般情况下,使用`===`, 而不是`==`;使用`!==`, 而不是`!=`;
|
||||
* 单行长度不应超过79个字符;
|
||||
* 截断长字符串使用如下方法:
|
||||
|
||||
```javascript
|
||||
myFunc("This is a really long string that's longer " +
|
||||
"than 79 characters so I broke it up, woo");
|
||||
```
|
||||
|
||||
* 注释需要和上一行代码左对齐:
|
||||
|
||||
```javascript
|
||||
if (foo === 5) {
|
||||
myFunc(foo);
|
||||
// foo++;
|
||||
}
|
||||
```
|
||||
|
||||
not
|
||||
而不是
|
||||
|
||||
```javascript
|
||||
if (foo === 5) {
|
||||
myFunc(foo);
|
||||
//foo++;
|
||||
}
|
||||
```
|
||||
|
||||
* 通过拓展原型,来创建子类:
|
||||
|
||||
```javascript
|
||||
var _ = require('underscore');
|
||||
|
||||
var SuperClass = function () {
|
||||
this.init();
|
||||
};
|
||||
|
||||
SuperClass.prototype.init = function () {
|
||||
// initialize
|
||||
};
|
||||
|
||||
// Create a subclass
|
||||
|
||||
var SubClass = function () {
|
||||
this.init();
|
||||
};
|
||||
|
||||
_.extend(SubClass.prototype, SuperClass.prototype);
|
||||
```
|
||||
|
||||
* 函数定义中,最后使用回调函数:
|
||||
|
||||
```javascript
|
||||
var foo = function (arg1, arg2, cb) {
|
||||
...
|
||||
};
|
||||
```
|
||||
|
||||
* 使用变量来定义函数:
|
||||
|
||||
```javascript
|
||||
var myFunc = function (a, b, c) {};
|
||||
```
|
||||
|
||||
而不是
|
||||
|
||||
```javascript
|
||||
function myFunc (a, b, c) {}
|
||||
```
|
||||
|
||||
* 变量名应该是驼峰式大小写风格:
|
||||
|
||||
```javascript
|
||||
var myVariable = 42;
|
||||
```
|
||||
|
||||
而不是
|
||||
|
||||
```javascript
|
||||
var my_variable = 42;
|
||||
```
|
||||
|
||||
* 检查是否有未定义的变量:
|
||||
|
||||
```javascript
|
||||
typeof myVariable === "undefined"
|
||||
```
|
||||
|
||||
而不是
|
||||
|
||||
```javascript
|
||||
myVariable === undefined
|
||||
```
|
||||
|
||||
* 给变量定义默认值:
|
||||
|
||||
```javascript
|
||||
var x = y || z;
|
||||
```
|
||||
|
||||
而不是
|
||||
|
||||
```javascript
|
||||
var x = y ? y : z;
|
||||
```
|
||||
|
||||
### 测试代码风格(Test Style):
|
||||
|
||||
在代码语义通顺和长度许可下,可以保持在同一行:
|
||||
|
||||
样例:
|
||||
|
||||
```javascript
|
||||
driver.elementByTagName('el1').should.become("123")
|
||||
.nodeify(done);
|
||||
|
||||
driver
|
||||
.elementsByTagName('el1').should.eventually.have.length(0)
|
||||
.nodeify(done);
|
||||
```
|
||||
|
||||
或者使用缩进来提高代码的可读性:
|
||||
|
||||
```javascript
|
||||
h.driver
|
||||
.elementById('comments')
|
||||
.clear()
|
||||
.click()
|
||||
.keys("hello world")
|
||||
.getValue()
|
||||
.should.become("hello world")
|
||||
.elementById('comments')
|
||||
.getValue().should.become("hello world")
|
||||
.nodeify(done);
|
||||
|
||||
h.driver
|
||||
.execute("'nan'--")
|
||||
.should.be.rejectedWith("status: 13")
|
||||
.nodeify(done);
|
||||
```
|
||||
|
||||
本文由 [wanyukang](https://github.com/wanyukang) 翻译,由 [oscarxie](https://github.com/oscarxie) 校验。
|
||||
@@ -0,0 +1,284 @@
|
||||
## 给贡献者的风格指南
|
||||
|
||||
感谢您对Appium的贡献!以下是我们编写javascript代码时需要遵守的准则,请确认你的提交能符合这些规范,这有利于我们合并你的代码时能保持良好的编码风格。其中最核心的准则是:*使你的代码与其他代码的编码风格保持一致*。
|
||||
|
||||
|
||||
### 衍合(Rebasing)
|
||||
|
||||
每个 pull 请求中的提交(commits)都应该包含[逻辑变更(logical changes)](https://github.com/appium/appium/pull/920#issuecomment-21588553)。
|
||||
如果有多位贡献者,请确保他们各自都有自己的提交记录,修改作者信息不是一个好主意。合并(merge)提交必须从 pull 请求中 rebase 。
|
||||
|
||||
### 检错(Linting)
|
||||
|
||||
所有的代码(除了使用了 Apple 私有方法的`bootstrap.js`代码)必须通过 JSLint 的检错测试。你可以在 Appium 存储目录下,运行`grunt lint`来检查你的代码。如果你已创建一个新的 .js 文件,请确认它在`grunt.js`中被通配符覆盖,或者被专门添加。
|
||||
|
||||
编辑器的即时检错集成很简单,并且这会使得整个编码过程更加顺利。 我们喜欢 [jshint](http://www.jshint.com), 因为它已经与许多源代码编辑器集成了。将文件`.jshintrc`加入到仓库中,它的内容是:
|
||||
|
||||
```json
|
||||
{
|
||||
"laxcomma": true,
|
||||
"strict": true,
|
||||
"undef": true,
|
||||
"unused": true,
|
||||
"node": true,
|
||||
"eqeqeq": true,
|
||||
"trailing": true,
|
||||
"indent": 2
|
||||
}
|
||||
```
|
||||
|
||||
因为jshint不再强制检查代码风格,我们也使用 [jscs](https://github.com/mdevils/node-jscs),它也集成在一些源代码编辑器中。配置文件为:
|
||||
|
||||
```json
|
||||
{
|
||||
"excludeFiles": ["submodules/**", "node_modules/**",
|
||||
"./lib/server/static/**", "./lib/devices/firefoxos/atoms/*.js",
|
||||
"./test/harmony/**/*.js", "./sample-code/examples/node/**/*-yiewd.js",
|
||||
"./sample-code/apps/**", "./sample-code/examples/php/vendor/**"],
|
||||
"requireCurlyBraces": ["for", "while", "do", "try", "catch"],
|
||||
"requireSpaceAfterKeywords": ["if", "else", "for", "while", "do", "switch",
|
||||
"return", "try", "catch", "function"],
|
||||
"disallowMixedSpacesAndTabs": true,
|
||||
"disallowTrailingWhitespace": true,
|
||||
"requireSpacesInFunctionExpression": {
|
||||
"beforeOpeningCurlyBrace": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
这些配置文件定义了哪些警告类型将会出现在你的编辑器中。 查看 [this page for jshint](http://www.jshint.com/platforms/) 和 [this page for jscs](https://github.com/mdevils/node-jscs#friendly-packages) ,找到它们各自支持的编辑器和平台的列表,以及如何设置你的编辑器使之能自动化检错。
|
||||
|
||||
### 风格注释(Style notes)
|
||||
|
||||
* 使用两个空格来缩进, *不要使用 tabs*
|
||||
* 在运算符两边,分别添加一个空格:
|
||||
|
||||
```javascript
|
||||
var x = 1;
|
||||
```
|
||||
|
||||
而不是
|
||||
|
||||
```javascript
|
||||
var x=1;
|
||||
```
|
||||
|
||||
* 在列表(lists),对象(objects),函数调用(function calls)等语句块中,逗号和冒号后面需要添加一个空格:
|
||||
|
||||
```javascript
|
||||
var x = myFunc("lol", {foo: bar, baz: boo});
|
||||
```
|
||||
|
||||
而不是
|
||||
|
||||
```javascript
|
||||
var x = myFunc("lol",{foo:bar,baz:boo});
|
||||
```
|
||||
|
||||
* 代码始终以分号结尾
|
||||
× 以逗号开头
|
||||
|
||||
```javascript
|
||||
var x = {
|
||||
foo: 'bar'
|
||||
, baz: 'boo'
|
||||
, wuz: 'foz'
|
||||
};
|
||||
```
|
||||
|
||||
* 左花括号应该和`function`,`if`等写在同一行,`else`应该被夹在两个花括号中间:
|
||||
|
||||
```javascript
|
||||
if (foo === bar) {
|
||||
// do something
|
||||
} else {
|
||||
// do something else
|
||||
}
|
||||
```
|
||||
|
||||
* `if`,`for`, 和`function`之后需要添加空格:
|
||||
|
||||
```javascript
|
||||
if (foo === bar) {
|
||||
```
|
||||
```javascript
|
||||
for (var i = 0; i < 10; i ++) {
|
||||
```
|
||||
```javascript
|
||||
var lol = function (foo) {
|
||||
```
|
||||
|
||||
而不是
|
||||
|
||||
```javascript
|
||||
if(foo === bar) {
|
||||
```
|
||||
```javascript
|
||||
for(var i = 0; i < 10; i ++) {
|
||||
```
|
||||
```javascript
|
||||
var lol = function(foo) {
|
||||
```
|
||||
|
||||
* 只有一行代码时,`if`语句块的花括号也应该添加上:
|
||||
|
||||
```javascript
|
||||
if (foo === bar) {
|
||||
foo++;
|
||||
}
|
||||
```
|
||||
|
||||
而不是
|
||||
|
||||
```javascript
|
||||
if (foo === bar)
|
||||
foo++;
|
||||
```
|
||||
|
||||
除了出错后直接调用回调函数(callback)处理错误(error)的语句
|
||||
|
||||
```javascript
|
||||
if (err) return cb(err);
|
||||
```
|
||||
|
||||
* 一般情况下,使用`===`, 而不是`==`;使用`!==`, 而不是`!=`;
|
||||
* 单行长度不应超过79个字符;
|
||||
* 截断长字符串使用如下方法:
|
||||
|
||||
```javascript
|
||||
myFunc("This is a really long string that's longer " +
|
||||
"than 79 characters so I broke it up, woo");
|
||||
```
|
||||
|
||||
* 注释需要和上一行代码左对齐:
|
||||
|
||||
```javascript
|
||||
if (foo === 5) {
|
||||
myFunc(foo);
|
||||
// foo++;
|
||||
}
|
||||
```
|
||||
|
||||
而不是
|
||||
|
||||
```javascript
|
||||
if (foo === 5) {
|
||||
myFunc(foo);
|
||||
//foo++;
|
||||
}
|
||||
```
|
||||
|
||||
* 通过拓展原型,来创建子类:
|
||||
|
||||
```javascript
|
||||
var _ = require('underscore');
|
||||
|
||||
var SuperClass = function () {
|
||||
this.init();
|
||||
};
|
||||
|
||||
SuperClass.prototype.init = function () {
|
||||
// initialize
|
||||
};
|
||||
|
||||
// Create a subclass
|
||||
|
||||
var SubClass = function () {
|
||||
this.init();
|
||||
};
|
||||
|
||||
_.extend(SubClass.prototype, SuperClass.prototype);
|
||||
```
|
||||
|
||||
* 函数定义中,最后使用回调函数:
|
||||
|
||||
```javascript
|
||||
var foo = function (arg1, arg2, cb) {
|
||||
...
|
||||
};
|
||||
```
|
||||
|
||||
* 使用变量来定义函数:
|
||||
|
||||
```javascript
|
||||
var myFunc = function (a, b, c) {};
|
||||
```
|
||||
|
||||
而不是
|
||||
|
||||
```javascript
|
||||
function myFunc (a, b, c) {}
|
||||
```
|
||||
|
||||
* 变量名应该是驼峰式大小写风格:
|
||||
|
||||
```javascript
|
||||
var myVariable = 42;
|
||||
```
|
||||
|
||||
而不是
|
||||
|
||||
```javascript
|
||||
var my_variable = 42;
|
||||
```
|
||||
|
||||
* 检查是否有未定义的变量:
|
||||
|
||||
```javascript
|
||||
typeof myVariable === "undefined"
|
||||
```
|
||||
|
||||
而不是
|
||||
|
||||
```javascript
|
||||
myVariable === undefined
|
||||
```
|
||||
|
||||
* 给变量定义默认值:
|
||||
|
||||
```javascript
|
||||
var x = y || z;
|
||||
```
|
||||
|
||||
而不是
|
||||
|
||||
```javascript
|
||||
var x = y ? y : z;
|
||||
```
|
||||
|
||||
### 测试代码风格(Test Style):
|
||||
|
||||
在代码语义通顺和长度许可下,可以保持在同一行:
|
||||
|
||||
样例:
|
||||
|
||||
```javascript
|
||||
driver.elementByTagName('el1').should.become("123")
|
||||
.nodeify(done);
|
||||
|
||||
driver
|
||||
.elementsByTagName('el1').should.eventually.have.length(0)
|
||||
.nodeify(done);
|
||||
```
|
||||
|
||||
或者使用缩进来提高代码的可读性:
|
||||
|
||||
```javascript
|
||||
h.driver
|
||||
.elementById('comments')
|
||||
.clear()
|
||||
.click()
|
||||
.keys("hello world")
|
||||
.getValue()
|
||||
.should.become("hello world")
|
||||
.elementById('comments')
|
||||
.getValue().should.become("hello world")
|
||||
.nodeify(done);
|
||||
|
||||
h.driver
|
||||
.execute("'nan'--")
|
||||
.should.be.rejectedWith("status: 13")
|
||||
.nodeify(done);
|
||||
```
|
||||
|
||||
本文由 [wanyukang](https://github.com/wanyukang) 翻译,由 [oscarxie](https://github.com/oscarxie) 校验。
|
||||