mirror of
https://github.com/appium/appium.git
synced 2026-10-31 22:32:24 +08:00
Compare commits
123
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
872b924a97 | ||
|
|
0e164ba03d | ||
|
|
2359a2ba3b | ||
|
|
49694f50a4 | ||
|
|
a2cfb832ad | ||
|
|
4cfad004db | ||
|
|
b159780fd6 | ||
|
|
e2f7dd7154 | ||
|
|
e9dae3f6d0 | ||
|
|
58b4790b1d | ||
|
|
63eddb670f | ||
|
|
b7d86c0d04 | ||
|
|
dfe02ca98c | ||
|
|
10e6815a39 | ||
|
|
cd76622aa9 | ||
|
|
296cb2436c | ||
|
|
01bff49297 | ||
|
|
48c331157f | ||
|
|
9f8daf385b | ||
|
|
7bae977ca9 | ||
|
|
4ed745a95f | ||
|
|
85e4bd8925 | ||
|
|
cce27fcf88 | ||
|
|
b30b2cfb84 | ||
|
|
9ccbab5dc0 | ||
|
|
760bb5b8f3 | ||
|
|
cb14faae33 | ||
|
|
2a6a13ac89 | ||
|
|
4b43af0709 | ||
|
|
504415d0cd | ||
|
|
75defa7b48 | ||
|
|
be86627595 | ||
|
|
080eb8aa1b | ||
|
|
1b70551055 | ||
|
|
d262271b26 | ||
|
|
45b026de67 | ||
|
|
843b797750 | ||
|
|
b4c1403c22 | ||
|
|
e2aa3d3b32 | ||
|
|
b151e4c9bf | ||
|
|
5684c4e286 | ||
|
|
189e39d03c | ||
|
|
694f9c6a63 | ||
|
|
f80f4cb2bc | ||
|
|
908041c525 | ||
|
|
18c0b4b230 | ||
|
|
4e2423d0f0 | ||
|
|
ed1d3aae61 | ||
|
|
d1d5ecef87 | ||
|
|
b88f6e3e86 | ||
|
|
471a4b57e6 | ||
|
|
feebf72eb4 | ||
|
|
0f196bad4a | ||
|
|
431092dc87 | ||
|
|
935c70683f | ||
|
|
f8099dafa7 | ||
|
|
25c9728f07 | ||
|
|
ba6b312244 | ||
|
|
188ed161e4 | ||
|
|
96898c87d5 | ||
|
|
7b49356322 | ||
|
|
bfc3134968 | ||
|
|
dbaf8d4cae | ||
|
|
eb73be8303 | ||
|
|
998514fcce | ||
|
|
49f157d63e | ||
|
|
43b115bb3d | ||
|
|
56cf504a2b | ||
|
|
2ebb16b077 | ||
|
|
c321441451 | ||
|
|
6be7488eef | ||
|
|
ab5b285714 | ||
|
|
8dc47ae922 | ||
|
|
91831cf82d | ||
|
|
4b10322b80 | ||
|
|
2602016eb3 | ||
|
|
1cb01a5e85 | ||
|
|
e78448dc65 | ||
|
|
34e0b70982 | ||
|
|
ebe9477a3c | ||
|
|
3b82c9f1a1 | ||
|
|
a9476a67ff | ||
|
|
24ad9f8741 | ||
|
|
639d475627 | ||
|
|
2fa6bf1e11 | ||
|
|
d3dc9ce733 | ||
|
|
08a6f3a308 | ||
|
|
24fb515a52 | ||
|
|
893bd888cf | ||
|
|
d54901a9c3 | ||
|
|
70d65b7d62 | ||
|
|
33442bd61b | ||
|
|
4cf8dbc0d3 | ||
|
|
943dce5eec | ||
|
|
04633e3e6d | ||
|
|
d171219938 | ||
|
|
61a0275e29 | ||
|
|
8f9de63e4a | ||
|
|
90c0d36f2a | ||
|
|
988c088873 | ||
|
|
7c2c642ce7 | ||
|
|
6b8eb2cbfe | ||
|
|
242c2ccbdc | ||
|
|
c5dadbbd36 | ||
|
|
74800cdf61 | ||
|
|
70c98ea12f | ||
|
|
592f9adcb1 | ||
|
|
fac31a3d68 | ||
|
|
2bbf42ff6b | ||
|
|
1f65161e56 | ||
|
|
355bf8e5da | ||
|
|
0ca5e7e21b | ||
|
|
11e7ad0cd4 | ||
|
|
c69b602d1b | ||
|
|
4cf13c5bfa | ||
|
|
a4c58cc561 | ||
|
|
64098c5f1a | ||
|
|
b4416c5c0f | ||
|
|
547b8a45c4 | ||
|
|
1bf841736b | ||
|
|
29a5bdac9a | ||
|
|
ab366f3a1f | ||
|
|
5446e5c7a7 |
@@ -4,8 +4,5 @@ examples/javascript-wd
|
||||
**/build/**
|
||||
**/*.min.js
|
||||
sample-code
|
||||
**/*.d.ts
|
||||
**/build-fixtures/**
|
||||
**/*.ts
|
||||
packages/appium/docs/**/assets/**
|
||||
**/*.tsx
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"extends": "@appium/eslint-config-appium",
|
||||
"extends": ["@appium/eslint-config-appium", "@appium/eslint-config-appium-ts"],
|
||||
"overrides": [
|
||||
{
|
||||
"files": "packages/fake-driver/**/*",
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
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.*?:'
|
||||
|
||||
- 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/eslint-config-appium'
|
||||
sync: true
|
||||
matcher:
|
||||
files: ['packages/eslint-config-appium/**']
|
||||
|
||||
- label: '@appium/base-plugin'
|
||||
sync: true
|
||||
matcher:
|
||||
files: ['packages/base-plugin/**']
|
||||
|
||||
- label: '@appium/doctor'
|
||||
sync: true
|
||||
matcher:
|
||||
files: ['packages/doctor/**']
|
||||
|
||||
- label: '@appium/docutils'
|
||||
sync: true
|
||||
matcher:
|
||||
files: ['packages/docutils/**']
|
||||
|
||||
- label: '@appium/driver-test-support'
|
||||
sync: true
|
||||
matcher:
|
||||
files: ['packages/driver-test-support/**']
|
||||
|
||||
- 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/opencv'
|
||||
sync: true
|
||||
matcher:
|
||||
files: ['packages/opencv/**']
|
||||
|
||||
- label: '@appium/plugin-test-support'
|
||||
sync: true
|
||||
matcher:
|
||||
files: ['packages/plugin-test-support/**']
|
||||
|
||||
- label: '@appium/relaxed-caps-plugin'
|
||||
sync: true
|
||||
matcher:
|
||||
files: ['packages/relaxed-caps-plugin/**']
|
||||
|
||||
- label: '@appium/schema'
|
||||
sync: true
|
||||
matcher:
|
||||
files: ['packages/schema/**']
|
||||
|
||||
- label: '@appium/support'
|
||||
sync: true
|
||||
matcher:
|
||||
files: ['packages/support/**']
|
||||
|
||||
- label: '@appium/test-support'
|
||||
sync: true
|
||||
matcher:
|
||||
files: ['packages/test-support/**']
|
||||
|
||||
- label: '@appium/tsconfig'
|
||||
sync: true
|
||||
matcher:
|
||||
files: ['packages/tsconfig/**']
|
||||
|
||||
- label: '@appium/typedoc-plugin-appium'
|
||||
sync: true
|
||||
matcher:
|
||||
files: ['packages/typedoc-plugin-appium/**']
|
||||
|
||||
- label: '@appium/types'
|
||||
sync: true
|
||||
matcher:
|
||||
files: ['packages/types/**']
|
||||
|
||||
- label: '@appium/universal-xml-plugin'
|
||||
sync: true
|
||||
matcher:
|
||||
files: ['packages/universal-xml-plugin/**']
|
||||
@@ -0,0 +1,36 @@
|
||||
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@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3
|
||||
|
||||
- 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 }}
|
||||
@@ -0,0 +1,22 @@
|
||||
name: 'Labeler'
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize]
|
||||
|
||||
permissions:
|
||||
# Setting up permissions in the workflow to limit the scope of what it can do. Optional!
|
||||
contents: read
|
||||
issues: read # change this if/when we want to label issues
|
||||
pull-requests: write
|
||||
statuses: read
|
||||
checks: read
|
||||
|
||||
jobs:
|
||||
labeler:
|
||||
name: Labeler
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
# follows semantic versioning. Lock to different version: v1, v1.5, v1.5.0 or use a commit hash.
|
||||
- uses: fuxingloh/multi-labeler@6704db0bcba106d07482efabbc79d3092af74fa2 # v2
|
||||
with:
|
||||
github-token: ${{secrets.GITHUB_TOKEN}} # optional, default to '${{ github.token }}'
|
||||
Vendored
+4
-1
@@ -39,6 +39,7 @@
|
||||
"skipFiles": [
|
||||
"<node_internals>/**",
|
||||
"**/node_modules/mocha/**",
|
||||
"**/node_modules/lodash/**",
|
||||
"**/node_modules/ts-node/**"
|
||||
],
|
||||
"sourceMaps": true,
|
||||
@@ -53,7 +54,9 @@
|
||||
"args": ["--logLevel", "Verbose"],
|
||||
"request": "launch",
|
||||
"skipFiles": [
|
||||
"<node_internals>/**"
|
||||
"<node_internals>/**",
|
||||
"**/node_modules/lodash/**",
|
||||
"**/node_modules/**/handlebars/**"
|
||||
],
|
||||
"sourceMaps": true,
|
||||
"type": "node"
|
||||
|
||||
@@ -78,6 +78,12 @@ The current committers are (in addition to the TC members above):
|
||||
* [@ddkjin](https://github.com/ddkjin)
|
||||
* [@stuartbrussell-intuit](https://github.com/stuartbrussell-intuit)
|
||||
* [@vmaxim](https://github.com/vmaxim)
|
||||
* [@Dan-Maor](https://github.com/Dan-Maor)
|
||||
* [@laolubenson](https://github.com/laolubenson)
|
||||
* [@Dor-bl](https://github.com/Dor-bl)
|
||||
* [@boneskull](https://github.com/boneskull)
|
||||
* [@mwakizaka](https://github.com/mwakizaka)
|
||||
* [@rerorero](https://github.com/rerorero)
|
||||
|
||||
### Contributors
|
||||
|
||||
|
||||
@@ -186,7 +186,7 @@
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright JS Foundation and other contributors, https://js.foundation
|
||||
Copyright OpenJS Foundation and other contributors, https://openjsf.org/
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -57,7 +57,7 @@ appium driver install --source=npm appium-xcuitest-driver[@<version>]
|
||||
# To install a driver from a local folder (useful for developers)
|
||||
appium driver install --source=local /Users/me/sources/appium-xcuitest-driver
|
||||
# To install a new driver from github (hm, maybe it's time to publish it to NPM?)
|
||||
appium driver install --source=github appium/appium-xcuitest-driver
|
||||
appium driver install --source=github --package=appium-xcuitest-driver appium/appium-xcuitest-driver
|
||||
|
||||
# To list already installed drivers
|
||||
appium driver list --installed
|
||||
@@ -168,7 +168,7 @@ Java/Kotlin. Appium opens up the possibility of true cross-platform native app
|
||||
automation, for mobile and beyond. Finally!
|
||||
|
||||
If you're new to Appium or want a more comprehensive description of what this is all
|
||||
about, please read our [Introduction to Appium Concepts](/docs/en/about-appium/intro.md).
|
||||
about, please read our [Intro to Appium](https://appium.github.io/appium/docs/en/latest/intro/).
|
||||
|
||||
### Quickstart
|
||||
|
||||
@@ -204,10 +204,7 @@ be sure to sign up!
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
We put together a [troubleshooting guide](/docs/en/writing-running-appium/other/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.
|
||||
Please find the troubleshooting guide at the corresponding driver repository/documentation.
|
||||
|
||||
### License
|
||||
|
||||
|
||||
Generated
+2320
-2797
File diff suppressed because it is too large
Load Diff
+23
-21
@@ -30,9 +30,9 @@
|
||||
"build": "tsc -b",
|
||||
"build:all": "run-s build build:packages",
|
||||
"build:packages": "lerna run build",
|
||||
"clean": "run-s \"clean:artifacts:*\" clean:workspaces",
|
||||
"clean": "run-s clean:artifacts clean:workspaces",
|
||||
"clean:artifacts": "run-p \"clean:artifacts:*\"",
|
||||
"clean:artifacts:distfiles": "run-s \"build -- --clean\"",
|
||||
"clean:artifacts:distfiles": "npx rimraf \"./packages/*/build\" \"./packages/*/.tsconfig.tsbuildinfo\"",
|
||||
"clean:artifacts:packages": "lerna run clean || true",
|
||||
"clean:workspaces": "lerna clean -y",
|
||||
"ci": "npm ci",
|
||||
@@ -40,12 +40,11 @@
|
||||
"doctor": "node ./packages/doctor",
|
||||
"husky-install": "husky install",
|
||||
"generate-docs": "npm run --workspace=packages/appium generate-docs",
|
||||
"prepare": "run-p husky-install build:all || true",
|
||||
"prepare": "run-p husky-install build:all",
|
||||
"lint": "eslint .",
|
||||
"lint:fix": "run-s \"lint -- --fix\"",
|
||||
"lint:commit": "commitlint",
|
||||
"lint:staged": "lint-staged",
|
||||
"pack-all": "npm pack --workspaces --include-workspace-root",
|
||||
"prepublishOnly": "run-s rebuild",
|
||||
"reinstall": "run-s clean ci",
|
||||
"rebuild": "run-s clean:artifacts build:all",
|
||||
@@ -53,7 +52,7 @@
|
||||
"sync-pkgs": "run-p sync-pkgs:*",
|
||||
"sync-pkgs:appium-readme": "sync-monorepo-packages --force --no-package-json --packages=packages/appium README.md",
|
||||
"sync-pkgs:common-fields": "sync-monorepo-packages --fields=author,license,bugs,homepage,engines",
|
||||
"sync-pkgs:keywords": "sync-monorepo-packages --field=keywords --packages=\"packages/*\" --packages=\"!packages/eslint-config-appium\" --packages=\"!packages/types\" --packages=\"!packages/typedoc-plugin-appium\"",
|
||||
"sync-pkgs:keywords": "sync-monorepo-packages --field=keywords --packages=\"packages/*\" --packages=\"!packages/eslint-config-appium\" --packages=\"!packages/types\" --packages=\"!packages/typedoc-plugin-appium\" --packages=\"!packages/eslint-config-appium-ts\" ",
|
||||
"sync-pkgs:license": "sync-monorepo-packages --force --no-package-json LICENSE",
|
||||
"stage-synced-pkgs": "git add -A packages/appium/README.md \"packages/*/package.json\"",
|
||||
"test": "npm-run-all -p lint build -s test:unit",
|
||||
@@ -63,7 +62,8 @@
|
||||
"test:unit": "lerna run test",
|
||||
"typedoc": "typedoc --logLevel Verbose",
|
||||
"upload": "gulp github-upload",
|
||||
"preversion": "run-s rebuild sync-pkgs stage-synced-pkgs",
|
||||
"preversion": "run-s sync-pkgs stage-synced-pkgs",
|
||||
"prepack": "npm run rebuild",
|
||||
"zip": "zip -qr ./appium.zip ./packages/appium",
|
||||
"zip-and-upload": "run-s zip upload"
|
||||
},
|
||||
@@ -74,27 +74,29 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@colors/colors": "1.5.0",
|
||||
"@commitlint/cli": "17.4.2",
|
||||
"@commitlint/config-conventional": "17.4.2",
|
||||
"@commitlint/cli": "17.4.4",
|
||||
"@commitlint/config-conventional": "17.4.4",
|
||||
"@tsconfig/node14": "1.0.3",
|
||||
"@types/chai": "4.3.4",
|
||||
"@types/chai-as-promised": "7.1.5",
|
||||
"@types/diff": "5.0.2",
|
||||
"@types/mocha": "10.0.1",
|
||||
"@types/node": "18.13.0",
|
||||
"@types/node": "18.14.6",
|
||||
"@types/semver": "7.3.13",
|
||||
"@types/sinon": "10.0.13",
|
||||
"@types/sinon-chai": "3.2.9",
|
||||
"@types/teen_process": "2.0.0",
|
||||
"@types/ws": "8.5.4",
|
||||
"@types/yargs": "17.0.22",
|
||||
"@typescript-eslint/eslint-plugin": "5.54.1",
|
||||
"@typescript-eslint/parser": "5.54.1",
|
||||
"asyncbox": "2.9.4",
|
||||
"chai": "4.3.7",
|
||||
"chai-as-promised": "7.1.1",
|
||||
"chai-webdriverio-async": "2.7.0",
|
||||
"cpy-cli": "4.2.0",
|
||||
"eslint": "8.33.0",
|
||||
"eslint-config-prettier": "8.6.0",
|
||||
"eslint": "8.35.0",
|
||||
"eslint-config-prettier": "8.7.0",
|
||||
"eslint-find-rules": "4.1.0",
|
||||
"eslint-plugin-import": "2.27.5",
|
||||
"eslint-plugin-mocha": "10.1.0",
|
||||
@@ -104,9 +106,9 @@
|
||||
"get-port": "5.1.1",
|
||||
"handlebars": "4.7.7",
|
||||
"husky": "8.0.3",
|
||||
"json-schema-to-typescript": "11.0.3",
|
||||
"lerna": "6.4.1",
|
||||
"lint-staged": "13.1.1",
|
||||
"json-schema-to-typescript": "12.0.0",
|
||||
"lerna": "6.5.1",
|
||||
"lint-staged": "13.1.2",
|
||||
"log-symbols": "4.1.0",
|
||||
"midnight-smoker": "2.0.1",
|
||||
"mjpeg-consumer": "2.0.0",
|
||||
@@ -114,7 +116,7 @@
|
||||
"mocha": "10.2.0",
|
||||
"npm-run-all": "4.1.5",
|
||||
"prettier": "2.8.4",
|
||||
"rewiremock": "3.14.4",
|
||||
"rewiremock": "3.14.5",
|
||||
"rimraf": "3.0.2",
|
||||
"serve-static": "1.15.0",
|
||||
"sinon": "15.0.1",
|
||||
@@ -122,13 +124,13 @@
|
||||
"sync-monorepo-packages": "1.0.2",
|
||||
"through2": "4.0.2",
|
||||
"ts-node": "10.9.1",
|
||||
"typedoc": "0.23.24",
|
||||
"typedoc": "0.23.26",
|
||||
"typedoc-plugin-markdown": "3.14.0",
|
||||
"typedoc-plugin-resolve-crossmodule-references": "0.3.3",
|
||||
"typescript": "4.7.4",
|
||||
"typescript": "4.9.5",
|
||||
"validate.js": "0.13.1",
|
||||
"webdriverio": "7.30.0",
|
||||
"ws": "8.12.0",
|
||||
"webdriverio": "7.30.2",
|
||||
"ws": "8.12.1",
|
||||
"yaml-js": "0.3.1"
|
||||
},
|
||||
"engines": {
|
||||
@@ -139,12 +141,12 @@
|
||||
"access": "public"
|
||||
},
|
||||
"overrides": {
|
||||
"jpeg-js": "0.4.4",
|
||||
"@sidvind/better-ajv-errors": {
|
||||
"ajv": "8.12.0"
|
||||
},
|
||||
"ajv-formats": {
|
||||
"ajv": "8.12.0"
|
||||
}
|
||||
},
|
||||
"http-cache-semantics": "4.1.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,48 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [2.0.0-beta.57](https://github.com/appium/appium/compare/appium@2.0.0-beta.56...appium@2.0.0-beta.57) (2023-03-08)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **appium,fake-driver:** expose child process when running an extension script ([e9dae3f](https://github.com/appium/appium/commit/e9dae3f6d006dcf89b6c0b6fb491be15acfed98b))
|
||||
* **appium,support:** fs.readPackageJsonFrom() returns proper type ([9ccbab5](https://github.com/appium/appium/commit/9ccbab5dc02d0019b0cad903772cda872d9fd5fb))
|
||||
* **docutils:** update dependency lilconfig to v2.1.0 ([4ed745a](https://github.com/appium/appium/commit/4ed745a95ffc6a43f76003eba62019c16a4c6cf2))
|
||||
* **execute-driver-plugin:** update dependency webdriverio to v7.30.2 ([49694f5](https://github.com/appium/appium/commit/49694f50a4680138cc92aba1eee294c927d712cf))
|
||||
* **types:** update dependency type-fest to v3.6.1 ([471a4b5](https://github.com/appium/appium/commit/471a4b57e622ff077d59f577a78341268700c48d))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [2.0.0-beta.56](https://github.com/appium/appium/compare/appium@2.0.0-beta.55...appium@2.0.0-beta.56) (2023-02-24)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* appium:options should work via --default-capabilities ([11e7ad0](https://github.com/appium/appium/commit/11e7ad0cd403ab1dc100f581cdf93772e3449db3)), closes [#18191](https://github.com/appium/appium/issues/18191)
|
||||
* **execute-driver-plugin:** update dependency webdriverio to v7.30.1 ([547b8a4](https://github.com/appium/appium/commit/547b8a45c4b5629720b0f4bdb0485a861aecbebf))
|
||||
* **support:** update dependency axios to v1.3.4 ([49f157d](https://github.com/appium/appium/commit/49f157d63e3bdbd205527a5dc8f997df68540546))
|
||||
* **types:** update dependency type-fest to v3.5.7 ([b4416c5](https://github.com/appium/appium/commit/b4416c5c0f40200b36909a1fbb492d8c4a212108))
|
||||
* **types:** update dependency type-fest to v3.6.0 ([08a6f3a](https://github.com/appium/appium/commit/08a6f3a308c7ee162e992629888557b31e50a26e))
|
||||
* update axios to v1.3.3 ([8f9de63](https://github.com/appium/appium/commit/8f9de63e4a622712db545ab63f9f4ce6654e4a91))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [2.0.0-beta.55](https://github.com/appium/appium/compare/appium@2.0.0-beta.54...appium@2.0.0-beta.55) (2023-02-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **docutils:** fix bad option name and ignore most falsy args ([5446e5c](https://github.com/appium/appium/commit/5446e5c7a755be081f46f1ed1ca8c13665d9a772))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [2.0.0-beta.54](https://github.com/appium/appium/compare/appium@2.0.0-beta.53...appium@2.0.0-beta.54) (2023-02-09)
|
||||
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ appium driver install --source=npm appium-xcuitest-driver[@<version>]
|
||||
# To install a driver from a local folder (useful for developers)
|
||||
appium driver install --source=local /Users/me/sources/appium-xcuitest-driver
|
||||
# To install a new driver from github (hm, maybe it's time to publish it to NPM?)
|
||||
appium driver install --source=github appium/appium-xcuitest-driver
|
||||
appium driver install --source=github --package=appium-xcuitest-driver appium/appium-xcuitest-driver
|
||||
|
||||
# To list already installed drivers
|
||||
appium driver list --installed
|
||||
@@ -168,7 +168,7 @@ Java/Kotlin. Appium opens up the possibility of true cross-platform native app
|
||||
automation, for mobile and beyond. Finally!
|
||||
|
||||
If you're new to Appium or want a more comprehensive description of what this is all
|
||||
about, please read our [Introduction to Appium Concepts](/docs/en/about-appium/intro.md).
|
||||
about, please read our [Intro to Appium](https://appium.github.io/appium/docs/en/latest/intro/).
|
||||
|
||||
### Quickstart
|
||||
|
||||
@@ -204,10 +204,7 @@ be sure to sign up!
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
We put together a [troubleshooting guide](/docs/en/writing-running-appium/other/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.
|
||||
Please find the troubleshooting guide at the corresponding driver repository/documentation.
|
||||
|
||||
### License
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
<!---Current latest commit hash in the original English documentation is e4dfa52 --->
|
||||
|
||||
|
||||
---
|
||||
title: Appium 1.xからAppium 2.xへ移行する
|
||||
---
|
||||
@@ -39,15 +42,39 @@ Together these do introduce a few breaking changes to how Appium is installed, h
|
||||
## 破壊的変更
|
||||
|
||||
<!---
|
||||
Have a look at the [Appium 2.0 release notes](https://github.com/appium/appium/releases) for the most comprehensive lists of changes. Here we call out the breaking changes and what you need to do to account for them.
|
||||
Here we call out the breaking changes and what you need to do to account for them.
|
||||
--->
|
||||
|
||||
リリース毎の変更一覧に関しては[Appium 2.0リリースノート](https://github.com/appium/appium/releases)を確認してください。ここでは破壊的変更と何をする必要があるのかを示します。
|
||||
以下では破壊的変更と、何をする必要があるのかを示します。
|
||||
|
||||
<!---
|
||||
### :bangbang: Installing drivers during setup
|
||||
--->
|
||||
|
||||
|
||||
### :bangbang: 初期設定のbase path
|
||||
|
||||
|
||||
<!---
|
||||
With Appium 1.x, the server would accept commands by default on `http://localhost:4723/wd/hub`. The
|
||||
`/wd/hub` base path was a legacy convention from the days of migrating from Selenium 1 to Selenium
|
||||
2, and is no longer relevant. As such the default base path for the server is now `/`. If you want
|
||||
to retain the old behaviour, you can set the base path via a command line argument as follows:
|
||||
--->
|
||||
|
||||
Appium 1.xでは、AppiumサーバーのURLは`http://localhost:4723/wd/hub`でした。この`/wd/hub`であるbase pathはSelenium 1からSelenium 2へ移行した時の名残であり、今となっては何ら意味を持ちません。そのため、初期設定のbase pathは`/`となります。もしあたなが以前の挙動を保持したいのであれば、以下の通りにAppiumサーバを起動してください。
|
||||
|
||||
```
|
||||
appium --base-path=/wd/hub
|
||||
```
|
||||
|
||||
<!---
|
||||
You can also set server arguments as [Config file](./config.md) properties.
|
||||
--->
|
||||
|
||||
この設定は[Config file](./config.md)からでも可能です。
|
||||
|
||||
|
||||
### :bangbang: ドライバーのインストール
|
||||
|
||||
<!---
|
||||
@@ -72,10 +99,10 @@ npm install --global appium --drivers=xcuitest,uiautomator2
|
||||
```
|
||||
|
||||
<!---
|
||||
This will install Appium and the two drivers for you in one go.
|
||||
This will install Appium and the two drivers for you in one go. Please uninstall any existing Appium 1.x npm packages (with `npm uninstall -g appium`) if you get an installation or startup error.
|
||||
--->
|
||||
|
||||
これはAppiumと2つのドライバーを一つのコマンドでインストールします。
|
||||
これはAppiumと2つのドライバーを一つのコマンドでインストールします。もしセットアップで何らかの問題が発生した場合、既存のAppium 1.xを`npm uninstall -g appium`で削除してみてください。
|
||||
|
||||
### :bangbang: Drivers installation path
|
||||
|
||||
@@ -224,6 +251,17 @@ In summary, if you are using a JSON Appium config file, you can simply cut-and-p
|
||||
|
||||
The old iOS and Android (UiAutomator 1) drivers and related tools (e.g., `authorize-ios`) have been removed. They haven't been relevant for many years anyway.
|
||||
|
||||
|
||||
### :bangbang: Server can no longer be started with `--port 0`
|
||||
|
||||
In Appium 1.x, it was possible to specify `--port 0` during server startup. This had the effect of
|
||||
starting Appium on a random free port. In Appium 2.0, port values must be `1` or higher. The random
|
||||
port assignment was never an intentional feature of Appium 1.x, but a consequence of how Node's
|
||||
HTTP servers work and the fact that there was no port input validation in Appium 1.x. If you want
|
||||
to find a random free port to start Appium on, you must now take care of this on your own prior to
|
||||
starting Appium. Starting Appium on an explicit and known port is the correct practice moving
|
||||
forward.
|
||||
|
||||
### :warning: Internal packages renamed
|
||||
|
||||
Some Appium-internal NPM packages have been renamed (for example, `appium-base-driver` is now `@appium/base-driver`). This is not a breaking change for Appium users, only for people who have built software that directly incorporates Appium's code.
|
||||
|
||||
@@ -52,7 +52,7 @@ async function main() {
|
||||
log.info(`Building docs for language '${lang}' and version ${majMinVer}`);
|
||||
const configFile = path.join(DOCS_DIR, `mkdocs-${lang}.yml`);
|
||||
await deploy({
|
||||
mkDocsYml: configFile,
|
||||
mkdocsYml: configFile,
|
||||
branch,
|
||||
prefix: path.join(basePrefix, lang),
|
||||
remote,
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
DELETE_SESSION_COMMAND,
|
||||
GET_STATUS_COMMAND,
|
||||
promoteAppiumOptions,
|
||||
promoteAppiumOptionsForObject,
|
||||
} from '@appium/base-driver';
|
||||
import AsyncLock from 'async-lock';
|
||||
import {parseCapsForInnerDriver, pullSettings} from './utils';
|
||||
@@ -44,7 +45,7 @@ class AppiumDriver extends DriverCore {
|
||||
* It is not recommended to access this property directly from the outside
|
||||
* @type {Record<string,ExternalDriver>}
|
||||
*/
|
||||
sessions = {};
|
||||
sessions;
|
||||
|
||||
/**
|
||||
* Access to pending drivers list must be guarded with a Semaphore, because
|
||||
@@ -52,14 +53,14 @@ class AppiumDriver extends DriverCore {
|
||||
* It is not recommended to access this property directly from the outside
|
||||
* @type {Record<string,ExternalDriver[]>}
|
||||
*/
|
||||
pendingDrivers = {};
|
||||
pendingDrivers;
|
||||
|
||||
/**
|
||||
* Note that {@linkcode AppiumDriver} has no `newCommandTimeout` method.
|
||||
* `AppiumDriver` does not set and observe its own timeouts; individual
|
||||
* sessions (managed drivers) do instead.
|
||||
*/
|
||||
newCommandTimeoutMs = 0;
|
||||
newCommandTimeoutMs;
|
||||
|
||||
/**
|
||||
* List of active plugins
|
||||
@@ -71,13 +72,13 @@ class AppiumDriver extends DriverCore {
|
||||
* map of sessions to actual plugin instances per session
|
||||
* @type {Record<string,InstanceType<PluginClass>[]>}
|
||||
*/
|
||||
sessionPlugins = {};
|
||||
sessionPlugins;
|
||||
|
||||
/**
|
||||
* some commands are sessionless, so we need a set of plugins for them
|
||||
* @type {InstanceType<PluginClass>[]}
|
||||
*/
|
||||
sessionlessPlugins = [];
|
||||
sessionlessPlugins;
|
||||
|
||||
/** @type {DriverConfig} */
|
||||
driverConfig;
|
||||
@@ -85,7 +86,11 @@ class AppiumDriver extends DriverCore {
|
||||
/** @type {AppiumServer} */
|
||||
server;
|
||||
|
||||
desiredCapConstraints = desiredCapabilityConstraints;
|
||||
/**
|
||||
* @type {AppiumDriverConstraints}
|
||||
* @readonly
|
||||
*/
|
||||
desiredCapConstraints;
|
||||
|
||||
/** @type {DriverOpts} */
|
||||
args;
|
||||
@@ -105,6 +110,13 @@ class AppiumDriver extends DriverCore {
|
||||
super(opts);
|
||||
|
||||
this.args = {...opts};
|
||||
this.sessions = {};
|
||||
this.pendingDrivers = {};
|
||||
this.newCommandTimeoutMs = 0;
|
||||
this.pluginClasses = new Map();
|
||||
this.sessionPlugins = {};
|
||||
this.sessionlessPlugins = [];
|
||||
this.desiredCapConstraints = desiredCapabilityConstraints;
|
||||
|
||||
// allow this to happen in the background, so no `await`
|
||||
(async () => {
|
||||
@@ -243,7 +255,7 @@ class AppiumDriver extends DriverCore {
|
||||
jsonwpCaps,
|
||||
promoteAppiumOptions(w3cCapabilities),
|
||||
this.desiredCapConstraints,
|
||||
defaultCapabilities
|
||||
defaultCapabilities ? promoteAppiumOptionsForObject(defaultCapabilities) : undefined
|
||||
);
|
||||
|
||||
const {desiredCaps, processedJsonwpCapabilities, processedW3CCapabilities} =
|
||||
@@ -277,7 +289,7 @@ class AppiumDriver extends DriverCore {
|
||||
*/
|
||||
let otherPendingDriversData = [];
|
||||
|
||||
const driverInstance = new InnerDriver(this.args, true);
|
||||
const driverInstance = /** @type {ExternalDriver} */ (new InnerDriver(this.args, true));
|
||||
|
||||
// We want to assign security values directly on the driver. The driver
|
||||
// should not read security values from `this.opts` because those values
|
||||
|
||||
@@ -58,7 +58,12 @@ export default class DriverCommand extends ExtensionCommand {
|
||||
* @return {Promise<import('./extension-command').RunOutput>}
|
||||
*/
|
||||
async run({driver, scriptName, extraArgs}) {
|
||||
return await super._run({installSpec: driver, scriptName, extraArgs});
|
||||
return await super._run({
|
||||
installSpec: driver,
|
||||
scriptName,
|
||||
extraArgs,
|
||||
bufferOutput: this.isJsonOutput,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,14 +4,15 @@ import _ from 'lodash';
|
||||
import path from 'path';
|
||||
import {npm, util, env, console} from '@appium/support';
|
||||
import {spinWith, RingBuffer} from './utils';
|
||||
import {SubProcess} from 'teen_process';
|
||||
import {
|
||||
INSTALL_TYPE_NPM,
|
||||
INSTALL_TYPE_GIT,
|
||||
INSTALL_TYPE_GITHUB,
|
||||
INSTALL_TYPE_LOCAL,
|
||||
} from '../extension/extension-config';
|
||||
import {SubProcess} from 'teen_process';
|
||||
import {packageDidChange} from '../extension/package-changed';
|
||||
import {spawn} from 'child_process';
|
||||
|
||||
const UPDATE_ALL = 'installed';
|
||||
|
||||
@@ -660,17 +661,34 @@ class ExtensionCommand {
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a script cached inside the "scripts" field under "appium"
|
||||
* inside of the driver/plugins "package.json" file. Will throw
|
||||
* an error if the driver/plugin does not contain a "scripts" field
|
||||
* underneath the "appium" field in its package.json, if the
|
||||
* "scripts" field is not a plain object, or if the scriptName is
|
||||
* not found within "scripts" object.
|
||||
* Just wraps {@linkcode child_process.spawn} with some default options
|
||||
*
|
||||
* @param {string} cwd - CWD
|
||||
* @param {string} script - Path to script
|
||||
* @param {string[]} args - Extra args for script
|
||||
* @param {import('child_process').SpawnOptions} opts - Options
|
||||
* @returns {import('node:child_process').ChildProcess}
|
||||
*/
|
||||
_runUnbuffered(cwd, script, args = [], opts = {}) {
|
||||
return spawn(process.execPath, [script, ...args], {
|
||||
cwd,
|
||||
stdio: 'inherit',
|
||||
...opts,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a script cached inside the `scripts` field under `appium`
|
||||
* inside of the extension's `package.json` file. Will throw
|
||||
* an error if the driver/plugin does not contain a `scripts` field
|
||||
* underneath the `appium` field in its `package.json`, if the
|
||||
* `scripts` field is not a plain object, or if the `scriptName` is
|
||||
* not found within `scripts` object.
|
||||
*
|
||||
* @param {RunOptions} opts
|
||||
* @return {Promise<RunOutput>}
|
||||
*/
|
||||
async _run({installSpec, scriptName, extraArgs = []}) {
|
||||
async _run({installSpec, scriptName, extraArgs = [], bufferOutput = false}) {
|
||||
if (!this.config.isInstalled(installSpec)) {
|
||||
throw this._createFatalError(`The ${this.type} "${installSpec}" is not installed`);
|
||||
}
|
||||
@@ -693,32 +711,62 @@ class ExtensionCommand {
|
||||
);
|
||||
}
|
||||
|
||||
if (!(scriptName in extScripts)) {
|
||||
if (!(scriptName in /** @type {Record<string,string>} */ (extScripts))) {
|
||||
throw this._createFatalError(
|
||||
`The ${this.type} named '${installSpec}' does not support the script: '${scriptName}'`
|
||||
);
|
||||
}
|
||||
|
||||
const runner = new SubProcess(process.execPath, [extScripts[scriptName], ...extraArgs], {
|
||||
cwd: this.config.getInstallPath(installSpec),
|
||||
});
|
||||
if (bufferOutput) {
|
||||
const runner = new SubProcess(process.execPath, [extScripts[scriptName], ...extraArgs], {
|
||||
cwd: this.config.getInstallPath(installSpec),
|
||||
});
|
||||
|
||||
const output = new RingBuffer(50);
|
||||
const output = new RingBuffer(50);
|
||||
|
||||
runner.on('stream-line', (line) => {
|
||||
output.enqueue(line);
|
||||
this.log.log(line);
|
||||
});
|
||||
runner.on('stream-line', (line) => {
|
||||
output.enqueue(line);
|
||||
this.log.log(line);
|
||||
});
|
||||
|
||||
await runner.start(0);
|
||||
await runner.start(0);
|
||||
|
||||
try {
|
||||
await runner.join();
|
||||
this.log.ok(`${scriptName} successfully ran`.green);
|
||||
return {output: output.getBuff()};
|
||||
} catch (err) {
|
||||
this.log.error(`Encountered an error when running '${scriptName}': ${err.message}`.red);
|
||||
return {error: err.message, output: output.getBuff()};
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await runner.join();
|
||||
await new B((resolve, reject) => {
|
||||
this._runUnbuffered(
|
||||
this.config.getInstallPath(installSpec),
|
||||
extScripts[scriptName],
|
||||
extraArgs
|
||||
)
|
||||
.on('error', (err) => {
|
||||
// generally this is of the "I can't find the script" variety.
|
||||
// this is a developer bug: the extension is pointing to a script that is not where the
|
||||
// developer said it would be (in `appium.scripts` of the extension's `package.json`)
|
||||
reject(err);
|
||||
})
|
||||
.on('close', (code) => {
|
||||
if (code === 0) {
|
||||
resolve();
|
||||
} else {
|
||||
reject(new Error(`Script "${scriptName}" exited with code ${code}`));
|
||||
}
|
||||
});
|
||||
});
|
||||
this.log.ok(`${scriptName} successfully ran`.green);
|
||||
return {output: output.getBuff()};
|
||||
return {};
|
||||
} catch (err) {
|
||||
this.log.error(`Encountered an error when running '${scriptName}': ${err.message}`.red);
|
||||
return {error: err.message, output: output.getBuff()};
|
||||
return {error: err.message};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -802,6 +850,7 @@ export {ExtensionCommand};
|
||||
* @property {string} installSpec - name of the extension to run a script from
|
||||
* @property {string} scriptName - name of the script to run
|
||||
* @property {string[]} [extraArgs] - arguments to pass to the script
|
||||
* @property {boolean} [bufferOutput] - if true, will buffer the output of the script and return it
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -809,7 +858,7 @@ export {ExtensionCommand};
|
||||
*
|
||||
* @typedef RunOutput
|
||||
* @property {string} [error] - error message if script ran unsuccessfully, otherwise undefined
|
||||
* @property {string[]} output - script output
|
||||
* @property {string[]} [output] - script output if `bufferOutput` was `true` in {@linkcode RunOptions}
|
||||
*/
|
||||
|
||||
/**
|
||||
|
||||
@@ -57,7 +57,12 @@ export default class PluginCommand extends ExtensionCommand {
|
||||
* @returns {Promise<import('./extension-command').RunOutput>}
|
||||
*/
|
||||
async run({plugin, scriptName, extraArgs}) {
|
||||
return await super._run({installSpec: plugin, scriptName, extraArgs});
|
||||
return await super._run({
|
||||
installSpec: plugin,
|
||||
scriptName,
|
||||
extraArgs,
|
||||
bufferOutput: this.isJsonOutput,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -10,8 +10,9 @@ import {getDefaultsForSchema, getAllArgSpecs} from './schema/schema';
|
||||
const npmPackage = fs.readPackageJsonFrom(__dirname);
|
||||
|
||||
const APPIUM_VER = npmPackage.version;
|
||||
const MIN_NODE_VERSION = npmPackage.engines.node;
|
||||
const MIN_NPM_VERSION = npmPackage.engines.npm;
|
||||
const ENGINES = /** @type {Record<string,string>} */ (npmPackage.engines);
|
||||
const MIN_NODE_VERSION = ENGINES.node;
|
||||
const MIN_NPM_VERSION = ENGINES.npm;
|
||||
|
||||
const GIT_META_ROOT = '.git';
|
||||
const GIT_BINARY = `git${system.isWindows() ? '.exe' : ''}`;
|
||||
|
||||
@@ -435,7 +435,7 @@ export class ExtensionConfig {
|
||||
/**
|
||||
* @param {string} extName
|
||||
* @param {ExtManifest<ExtType>} extManifest
|
||||
* @param {ExtensionConfigMutationOpts} [opts]
|
||||
* @param {ExtensionConfigMutationOpts} opts
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async addExtension(extName, extManifest, {write = true} = {}) {
|
||||
@@ -448,7 +448,7 @@ export class ExtensionConfig {
|
||||
/**
|
||||
* @param {ExtName<ExtType>} extName
|
||||
* @param {ExtManifest<ExtType>} extManifest
|
||||
* @param {ExtensionConfigMutationOpts} [opts]
|
||||
* @param {ExtensionConfigMutationOpts} opts
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async updateExtension(extName, extManifest, {write = true} = {}) {
|
||||
@@ -465,7 +465,7 @@ export class ExtensionConfig {
|
||||
* Remove an extension from the list of installed extensions, and optionally avoid a write to the manifest file.
|
||||
*
|
||||
* @param {ExtName<ExtType>} extName
|
||||
* @param {ExtensionConfigMutationOpts} [opts]
|
||||
* @param {ExtensionConfigMutationOpts} opts
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async removeExtension(extName, {write = true} = {}) {
|
||||
|
||||
@@ -94,7 +94,7 @@ export class ArgSpec {
|
||||
* The _constructor_ is private. Use {@link ArgSpec.create} instead.
|
||||
* @private
|
||||
* @param {string} name
|
||||
* @param {ArgSpecOptions<D>} [opts]
|
||||
* @param {ArgSpecOptions<D>} opts
|
||||
*/
|
||||
constructor(name, {extType, extName, dest, defaultValue} = {}) {
|
||||
// we must normalize the extension name to fit into our convention for CLI
|
||||
|
||||
@@ -41,7 +41,9 @@ export class RoachHotelMap extends Map {
|
||||
/**
|
||||
* Extensions that an extension schema file can have.
|
||||
*/
|
||||
export const ALLOWED_SCHEMA_EXTENSIONS = new Set(['.json', '.js', '.cjs']);
|
||||
export const ALLOWED_SCHEMA_EXTENSIONS = Object.freeze(
|
||||
new Set(/** @type {AllowedSchemaExtension[]} */ (['.json', '.js', '.cjs']))
|
||||
);
|
||||
|
||||
const SCHEMA_KEY = '$schema';
|
||||
|
||||
@@ -57,44 +59,39 @@ class AppiumSchema {
|
||||
* An "argument" is a CLI argument or a config property.
|
||||
*
|
||||
* Used to provide easy lookups of argument metadata when converting between different representations of those arguments.
|
||||
* @private
|
||||
* @type {RoachHotelMap<string,ArgSpec>}
|
||||
*/
|
||||
_argSpecs = new RoachHotelMap();
|
||||
#argSpecs = new RoachHotelMap();
|
||||
|
||||
/**
|
||||
* A map of extension types to extension names to schema objects.
|
||||
*
|
||||
* This data structure is used to ensure there are no naming conflicts. The schemas
|
||||
* are stored here in memory until the instance is _finalized_.
|
||||
* @private
|
||||
* @type {Record<ExtensionType,Map<string,SchemaObject>>}
|
||||
*/
|
||||
_registeredSchemas = {[DRIVER_TYPE]: new Map(), [PLUGIN_TYPE]: new Map()};
|
||||
#registeredSchemas = {[DRIVER_TYPE]: new Map(), [PLUGIN_TYPE]: new Map()};
|
||||
|
||||
/**
|
||||
* Ajv instance
|
||||
*
|
||||
* @private
|
||||
* @type {Ajv}
|
||||
*/
|
||||
_ajv;
|
||||
#ajv;
|
||||
|
||||
/**
|
||||
* Singleton instance.
|
||||
* @private
|
||||
* @type {AppiumSchema}
|
||||
*/
|
||||
static _instance;
|
||||
static #instance;
|
||||
|
||||
/**
|
||||
* Lookup of schema IDs to finalized schemas.
|
||||
*
|
||||
* This does not include references, but rather the root schemas themselves.
|
||||
* @private
|
||||
* @type {Record<string,StrictSchemaObject>?}
|
||||
*/
|
||||
_finalizedSchemas = null;
|
||||
#finalizedSchemas = null;
|
||||
|
||||
/**
|
||||
* Initializes Ajv, adds standard formats and our custom keywords.
|
||||
@@ -102,7 +99,7 @@ class AppiumSchema {
|
||||
* @private
|
||||
*/
|
||||
constructor() {
|
||||
this._ajv = AppiumSchema._instantiateAjv();
|
||||
this.#ajv = AppiumSchema._instantiateAjv();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -113,9 +110,9 @@ class AppiumSchema {
|
||||
* @returns {AppiumSchema}
|
||||
*/
|
||||
static create() {
|
||||
if (!AppiumSchema._instance) {
|
||||
if (!AppiumSchema.#instance) {
|
||||
const instance = new AppiumSchema();
|
||||
AppiumSchema._instance = instance;
|
||||
AppiumSchema.#instance = instance;
|
||||
_.bindAll(instance, [
|
||||
'finalize',
|
||||
'flatten',
|
||||
@@ -133,7 +130,7 @@ class AppiumSchema {
|
||||
]);
|
||||
}
|
||||
|
||||
return AppiumSchema._instance;
|
||||
return AppiumSchema.#instance;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -145,7 +142,7 @@ class AppiumSchema {
|
||||
* @returns {boolean} If registered
|
||||
*/
|
||||
hasRegisteredSchema(extType, extName) {
|
||||
return this._registeredSchemas[extType].has(extName);
|
||||
return this.#registeredSchemas[extType].has(extName);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -154,11 +151,11 @@ class AppiumSchema {
|
||||
* @returns {boolean} If finalized
|
||||
*/
|
||||
isFinalized() {
|
||||
return Boolean(this._finalizedSchemas);
|
||||
return Boolean(this.#finalizedSchemas);
|
||||
}
|
||||
|
||||
getAllArgSpecs() {
|
||||
return this._argSpecs;
|
||||
return this.#argSpecs;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -182,10 +179,10 @@ class AppiumSchema {
|
||||
*/
|
||||
finalize() {
|
||||
if (this.isFinalized()) {
|
||||
return /** @type {NonNullable<typeof this._finalizedSchemas>} */ (this._finalizedSchemas);
|
||||
return /** @type {Record<string,StrictSchemaObject>} */ (this.#finalizedSchemas);
|
||||
}
|
||||
|
||||
const ajv = this._ajv;
|
||||
const ajv = this.#ajv;
|
||||
|
||||
// Ajv will _mutate_ the schema, so we need to clone it.
|
||||
const baseSchema = _.cloneDeep(AppiumConfigJsonSchema);
|
||||
@@ -205,7 +202,7 @@ class AppiumSchema {
|
||||
extName,
|
||||
});
|
||||
const {arg} = argSpec;
|
||||
this._argSpecs.set(arg, argSpec);
|
||||
this.#argSpecs.set(arg, argSpec);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -217,7 +214,7 @@ class AppiumSchema {
|
||||
const finalizedSchemas = {};
|
||||
|
||||
const finalSchema = _.reduce(
|
||||
this._registeredSchemas,
|
||||
this.#registeredSchemas,
|
||||
/**
|
||||
* @param {typeof baseSchema} baseSchema
|
||||
* @param {Map<string,SchemaObject>} extensionSchemas
|
||||
@@ -246,7 +243,7 @@ class AppiumSchema {
|
||||
finalizedSchemas[APPIUM_CONFIG_SCHEMA_ID] = finalSchema;
|
||||
ajv.validateSchema(finalSchema, true);
|
||||
|
||||
this._finalizedSchemas = finalizedSchemas;
|
||||
this.#finalizedSchemas = finalizedSchemas;
|
||||
return Object.freeze(finalizedSchemas);
|
||||
}
|
||||
|
||||
@@ -283,18 +280,18 @@ class AppiumSchema {
|
||||
* @returns {void}
|
||||
*/
|
||||
reset() {
|
||||
for (const schemaId of Object.keys(this._finalizedSchemas ?? {})) {
|
||||
this._ajv.removeSchema(schemaId);
|
||||
for (const schemaId of Object.keys(this.#finalizedSchemas ?? {})) {
|
||||
this.#ajv.removeSchema(schemaId);
|
||||
}
|
||||
this._argSpecs = new RoachHotelMap();
|
||||
this._registeredSchemas = {
|
||||
this.#argSpecs = new RoachHotelMap();
|
||||
this.#registeredSchemas = {
|
||||
[DRIVER_TYPE]: new Map(),
|
||||
[PLUGIN_TYPE]: new Map(),
|
||||
};
|
||||
this._finalizedSchemas = null;
|
||||
this.#finalizedSchemas = null;
|
||||
|
||||
// Ajv seems to have an over-eager cache, so we have to dump the object entirely.
|
||||
this._ajv = AppiumSchema._instantiateAjv();
|
||||
this.#ajv = AppiumSchema._instantiateAjv();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -318,14 +315,14 @@ class AppiumSchema {
|
||||
}
|
||||
const normalizedExtName = _.kebabCase(extName);
|
||||
if (this.hasRegisteredSchema(extType, normalizedExtName)) {
|
||||
if (_.isEqual(this._registeredSchemas[extType].get(normalizedExtName), schema)) {
|
||||
if (_.isEqual(this.#registeredSchemas[extType].get(normalizedExtName), schema)) {
|
||||
return;
|
||||
}
|
||||
throw new SchemaNameConflictError(extType, extName);
|
||||
}
|
||||
this._ajv.validateSchema(schema, true);
|
||||
this.#ajv.validateSchema(schema, true);
|
||||
|
||||
this._registeredSchemas[extType].set(normalizedExtName, schema);
|
||||
this.#registeredSchemas[extType].set(normalizedExtName, schema);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -336,7 +333,7 @@ class AppiumSchema {
|
||||
* @returns {ArgSpec|undefined} ArgSpec or `undefined` if not found
|
||||
*/
|
||||
getArgSpec(name, extType, extName) {
|
||||
return this._argSpecs.get(ArgSpec.toArg(name, extType, extName));
|
||||
return this.#argSpecs.get(ArgSpec.toArg(name, extType, extName));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -347,7 +344,7 @@ class AppiumSchema {
|
||||
* @returns {boolean} `true` if such an {@link ArgSpec} exists
|
||||
*/
|
||||
hasArgSpec(name, extType, extName) {
|
||||
return this._argSpecs.has(ArgSpec.toArg(name, extType, extName));
|
||||
return this.#argSpecs.has(ArgSpec.toArg(name, extType, extName));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -391,7 +388,7 @@ class AppiumSchema {
|
||||
|
||||
/** @type {DefaultValues<Flattened>} */
|
||||
const retval = {};
|
||||
return [...this._argSpecs.values()].reduce(reducer, retval);
|
||||
return [...this.#argSpecs.values()].reduce(reducer, retval);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -405,7 +402,7 @@ class AppiumSchema {
|
||||
if (!this.isFinalized()) {
|
||||
throw new SchemaFinalizationError();
|
||||
}
|
||||
const specs = [...this._argSpecs.values()].filter(
|
||||
const specs = [...this.#argSpecs.values()].filter(
|
||||
(spec) => spec.extType === extType && spec.extName === extName
|
||||
);
|
||||
return specs.reduce((defaults, {defaultValue, rawDest}) => {
|
||||
@@ -506,7 +503,7 @@ class AppiumSchema {
|
||||
* @returns {import('ajv').ValidateFunction}
|
||||
*/
|
||||
_getValidator(id = APPIUM_CONFIG_SCHEMA_ID) {
|
||||
const validator = this._ajv.getSchema(id);
|
||||
const validator = this.#ajv.getSchema(id);
|
||||
if (!validator) {
|
||||
if (id === APPIUM_CONFIG_SCHEMA_ID) {
|
||||
throw new SchemaFinalizationError();
|
||||
@@ -532,11 +529,13 @@ class AppiumSchema {
|
||||
|
||||
/**
|
||||
* Returns `true` if `filename`'s file extension is allowed (in {@link ALLOWED_SCHEMA_EXTENSIONS}).
|
||||
* @param {string} filename
|
||||
* @param {import('type-fest').LiteralUnion<AllowedSchemaExtension, string>} filename
|
||||
* @returns {boolean}
|
||||
*/
|
||||
static isAllowedSchemaFileExtension(filename) {
|
||||
return ALLOWED_SCHEMA_EXTENSIONS.has(path.extname(filename));
|
||||
return ALLOWED_SCHEMA_EXTENSIONS.has(
|
||||
/** @type {AllowedSchemaExtension} */ (path.extname(filename))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -720,3 +719,7 @@ export const {isAllowedSchemaFileExtension} = AppiumSchema;
|
||||
* @template {boolean|undefined} Flattened
|
||||
* @typedef {Record<string,Flattened extends true ? ArgSpecDefaultValue : ArgSpecDefaultValue | NestedArgSpecDefaultValue>} DefaultValues
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {'.json'|'.js'|'.cjs'} AllowedSchemaExtension
|
||||
*/
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "appium",
|
||||
"version": "2.0.0-beta.54",
|
||||
"version": "2.0.0-beta.57",
|
||||
"description": "Automation for Apps.",
|
||||
"keywords": [
|
||||
"automation",
|
||||
@@ -59,12 +59,12 @@
|
||||
"test:unit": "mocha \"./test/unit/**/*.spec.js\""
|
||||
},
|
||||
"dependencies": {
|
||||
"@appium/base-driver": "^9.3.1",
|
||||
"@appium/base-plugin": "^2.2.1",
|
||||
"@appium/docutils": "^0.2.0",
|
||||
"@appium/schema": "^0.2.4",
|
||||
"@appium/support": "^3.1.5",
|
||||
"@appium/types": "^0.9.1",
|
||||
"@appium/base-driver": "^9.3.3",
|
||||
"@appium/base-plugin": "^2.2.3",
|
||||
"@appium/docutils": "^0.3.0",
|
||||
"@appium/schema": "^0.2.6",
|
||||
"@appium/support": "^3.1.7",
|
||||
"@appium/types": "^0.10.1",
|
||||
"@sidvind/better-ajv-errors": "2.1.0",
|
||||
"@types/argparse": "2.0.10",
|
||||
"@types/bluebird": "3.5.38",
|
||||
@@ -77,12 +77,12 @@
|
||||
"argparse": "2.0.1",
|
||||
"async-lock": "1.4.0",
|
||||
"asyncbox": "2.9.4",
|
||||
"axios": "1.2.3",
|
||||
"axios": "1.3.4",
|
||||
"bluebird": "3.7.2",
|
||||
"cross-env": "7.0.3",
|
||||
"find-up": "5.0.0",
|
||||
"glob": "8.1.0",
|
||||
"lilconfig": "2.0.6",
|
||||
"lilconfig": "2.1.0",
|
||||
"lodash": "4.17.21",
|
||||
"longjohn": "0.2.12",
|
||||
"npmlog": "7.0.1",
|
||||
@@ -92,7 +92,7 @@
|
||||
"semver": "7.3.8",
|
||||
"source-map-support": "0.5.21",
|
||||
"teen_process": "2.0.2",
|
||||
"type-fest": "3.5.6",
|
||||
"type-fest": "3.6.1",
|
||||
"winston": "3.8.2",
|
||||
"wrap-ansi": "7.0.0",
|
||||
"yaml": "2.2.1"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"devDependencies": {
|
||||
"webdriverio": "7.30.0"
|
||||
"webdriverio": "7.30.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
*/
|
||||
|
||||
const B = require('bluebird');
|
||||
const path = require('node:path');
|
||||
const {realpath} = require('node:fs/promises');
|
||||
|
||||
B.config({
|
||||
cancellation: true,
|
||||
@@ -55,11 +57,32 @@ function log(message) {
|
||||
console.error(wrap(`[Appium] ${message}`));
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a naive attempt at determining whether or not we are in a dev environment; in other
|
||||
* words, is `postinstall` being run from within the `appium` monorepo?
|
||||
*
|
||||
* When we're in the monorepo, `npm_config_local_prefix` will be set to the root of the monorepo root
|
||||
* dir when running this lifecycle script from an `npm install` in the monorepo root.
|
||||
*
|
||||
* `realpath` is necessary due to macOS omitting `/private` from paths
|
||||
*/
|
||||
async function isDevEnvironment() {
|
||||
return (
|
||||
process.env.npm_config_local_prefix &&
|
||||
path.join(process.env.npm_config_local_prefix, 'packages', 'appium') ===
|
||||
(await realpath(path.join(__dirname, '..')))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup / check environment if we should do anything here
|
||||
* @returns {Promise<boolean>} `true` if Appium is built and ready to go
|
||||
*/
|
||||
async function init() {
|
||||
if (await isDevEnvironment()) {
|
||||
log('Dev environment likely; skipping automatic installation of extensions');
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
({env, util, logger} = require('@appium/support'));
|
||||
// @ts-ignore
|
||||
|
||||
@@ -88,35 +88,32 @@ describe('FakeDriver via HTTP', function () {
|
||||
}
|
||||
|
||||
FakeDriver = driverConfig.require('fake');
|
||||
// then start server if we need to
|
||||
await serverStart(port, {appiumHome});
|
||||
});
|
||||
|
||||
after(async function () {
|
||||
await serverClose();
|
||||
await fs.rimraf(appiumHome);
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {number} port
|
||||
* @param {Partial<import('appium/types').ParsedArgs>} [args]
|
||||
*/
|
||||
async function serverStart(port, args = {}) {
|
||||
args = {...args, port, address: TEST_HOST};
|
||||
if (shouldStartServer) {
|
||||
server = await appiumServer(args);
|
||||
}
|
||||
}
|
||||
|
||||
async function serverClose() {
|
||||
if (server) {
|
||||
await server.close();
|
||||
}
|
||||
function withServer(args = {}) {
|
||||
before(async function () {
|
||||
args = {...args, appiumHome, port, address: TEST_HOST};
|
||||
if (shouldStartServer) {
|
||||
server = await appiumServer(args);
|
||||
}
|
||||
});
|
||||
after(async function () {
|
||||
if (server) {
|
||||
await server.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
describe('server updating', function () {
|
||||
withServer();
|
||||
it('should allow drivers to update the server in arbitrary ways', async function () {
|
||||
const {data} = await axios.get(`${testServerBaseUrl}/fakedriver`);
|
||||
data.should.eql({fakedriver: 'fakeResponse'});
|
||||
@@ -132,6 +129,7 @@ describe('FakeDriver via HTTP', function () {
|
||||
});
|
||||
|
||||
describe('cli args handling for empty args', function () {
|
||||
withServer();
|
||||
it('should not receive user cli args if none passed in', async function () {
|
||||
let driver = await wdio({...wdOpts, capabilities: caps});
|
||||
const {sessionId} = driver;
|
||||
@@ -146,16 +144,7 @@ describe('FakeDriver via HTTP', function () {
|
||||
});
|
||||
|
||||
describe('cli args handling for passed in args', function () {
|
||||
before(async function () {
|
||||
await serverClose();
|
||||
await serverStart(port, {appiumHome, ...FAKE_DRIVER_ARGS});
|
||||
});
|
||||
after(async function () {
|
||||
await serverClose();
|
||||
// this weirdness here is to restart the same server which was originally started in the parent suite's
|
||||
// "before all" hook. another way of doing this would be to just...not nest suites this way.
|
||||
await serverStart(port, {appiumHome});
|
||||
});
|
||||
withServer(FAKE_DRIVER_ARGS);
|
||||
it('should receive user cli args from a driver if arguments were passed in', async function () {
|
||||
let driver = await wdio({...wdOpts, capabilities: caps});
|
||||
const {sessionId} = driver;
|
||||
@@ -169,7 +158,46 @@ describe('FakeDriver via HTTP', function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe('default capabilities via cli', function () {
|
||||
withServer({
|
||||
defaultCapabilities: {
|
||||
'appium:options': {
|
||||
automationName: 'Fake',
|
||||
deviceName: 'Fake',
|
||||
app: TEST_FAKE_APP,
|
||||
},
|
||||
platformName: 'Fake',
|
||||
},
|
||||
});
|
||||
it('should allow appium-prefixed caps sent via appium:options through --default-capabilities', async function () {
|
||||
const appiumOptsCaps = {
|
||||
capabilities: {
|
||||
alwaysMatch: {},
|
||||
firstMatch: [{}],
|
||||
},
|
||||
};
|
||||
|
||||
// Create the session
|
||||
const {value} = (await axios.post(testServerBaseSessionUrl, appiumOptsCaps)).data;
|
||||
try {
|
||||
value.sessionId.should.be.a.string;
|
||||
value.should.exist;
|
||||
value.capabilities.should.deep.equal({
|
||||
automationName: 'Fake',
|
||||
platformName: 'Fake',
|
||||
deviceName: 'Fake',
|
||||
app: TEST_FAKE_APP,
|
||||
});
|
||||
} finally {
|
||||
// End session
|
||||
await axios.delete(`${testServerBaseSessionUrl}/${value.sessionId}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('session handling', function () {
|
||||
withServer();
|
||||
|
||||
it('should start and stop a session and not allow commands after session stopped', async function () {
|
||||
let driver = await wdio({...wdOpts, capabilities: caps});
|
||||
should.exist(driver.sessionId);
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
// @ts-check
|
||||
import {DriverConfig} from '../../../lib/extension/driver-config';
|
||||
import {ExtensionCommand} from '../../../lib/cli/extension-command';
|
||||
import sinon from 'sinon';
|
||||
import {FAKE_DRIVER_DIR} from '../../helpers';
|
||||
import {Manifest} from '../../../lib/extension/manifest';
|
||||
|
||||
const {expect} = chai;
|
||||
|
||||
/**
|
||||
* Relative path from actual `package.json` of `FakeDriver` for the `fake-stdin` script
|
||||
*/
|
||||
const FAKE_STDIN_SCRIPT = require(`${FAKE_DRIVER_DIR}/package.json`).appium.scripts['fake-stdin'];
|
||||
|
||||
describe('ExtensionCommand', function () {
|
||||
describe('method', function () {
|
||||
/** @type {ExtensionCommand} */
|
||||
let ec;
|
||||
|
||||
/** @type {sinon.SinonSandbox} */
|
||||
let sandbox;
|
||||
|
||||
beforeEach(function () {
|
||||
sandbox = sinon.createSandbox();
|
||||
const driverConfig = DriverConfig.create(sandbox.createStubInstance(Manifest));
|
||||
ec = new ExtensionCommand({config: driverConfig, json: false});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
describe('_runUnbuffered()', function () {
|
||||
// this test is low value and mostly just asserts that `child_process.spawn()` works.
|
||||
// the problem is that because `_run()` returns a `Promise`, a caller cannot reach the
|
||||
// underlying `ChildProcess` instance.
|
||||
// something like `execa` could work around this because it returns a frankenstein of a
|
||||
// `Promise` + `ChildProcess`, but I didn't want to add the dep.
|
||||
it('should respond to stdin', function (done) {
|
||||
// we have to fake writing to STDIN because this is an automated test, after all.
|
||||
const proc = ec
|
||||
._runUnbuffered(FAKE_DRIVER_DIR, FAKE_STDIN_SCRIPT, [], {
|
||||
stdio: ['pipe', 'inherit', 'inherit'],
|
||||
})
|
||||
.once('exit', (code) => {
|
||||
try {
|
||||
expect(code).to.equal(0);
|
||||
done();
|
||||
} catch (err) {
|
||||
done(err);
|
||||
}
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
// TS does not understand that `proc.stdin` is not `null`, because it is only a `Writable`
|
||||
// if STDIN is piped from the parent.
|
||||
const stdin = /** @type {import('node:stream').Writable} */ (proc.stdin);
|
||||
stdin.write('\n');
|
||||
stdin.end();
|
||||
}, 200);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -3,6 +3,45 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [9.3.3](https://github.com/appium/appium/compare/@appium/base-driver@9.3.2...@appium/base-driver@9.3.3) (2023-03-08)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **base-driver:** remove needless static prop breaking the build ([1b70551](https://github.com/appium/appium/commit/1b70551055444cad97144e539da7be872b9f70f8))
|
||||
* **base-driver:** use new mixin strategy ([be86627](https://github.com/appium/appium/commit/be8662759586bb3c24c7635b60ed8c574f5e2fd4))
|
||||
* **images-plugin:** update dependency lru-cache to v7.17.2 ([d1d5ece](https://github.com/appium/appium/commit/d1d5ecef87e70afea6d9d59f69cae465460e930b))
|
||||
* **images-plugin:** update dependency lru-cache to v7.18.1 ([ed1d3aa](https://github.com/appium/appium/commit/ed1d3aae61539bc136b3f266911331387397b88a))
|
||||
* **images-plugin:** update dependency lru-cache to v7.18.2 ([48c3311](https://github.com/appium/appium/commit/48c331157f74b238a3fc8dc92775d0af3f9f3134))
|
||||
* **images-plugin:** update dependency lru-cache to v7.18.3 ([01bff49](https://github.com/appium/appium/commit/01bff49297ca5a1c92259f1f8f11f5944ef15e4d))
|
||||
* **types:** update dependency type-fest to v3.6.1 ([471a4b5](https://github.com/appium/appium/commit/471a4b57e622ff077d59f577a78341268700c48d))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [9.3.2](https://github.com/appium/appium/compare/@appium/base-driver@9.3.1...@appium/base-driver@9.3.2) (2023-02-24)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* appium:options should work via --default-capabilities ([11e7ad0](https://github.com/appium/appium/commit/11e7ad0cd403ab1dc100f581cdf93772e3449db3)), closes [#18191](https://github.com/appium/appium/issues/18191)
|
||||
* **base-driver:** fix type problem w/r/t axios headers ([61a0275](https://github.com/appium/appium/commit/61a0275e29e7a705785282415fd670a3ad617f18))
|
||||
* **base-driver:** update dependency body-parser to v1.20.2 ([1cb01a5](https://github.com/appium/appium/commit/1cb01a5e85e7aee54ce1edc751e104138852d59a))
|
||||
* **images-plugin:** update dependency lru-cache to v7.15.0 ([4cf8dbc](https://github.com/appium/appium/commit/4cf8dbc0d34769e3167bceed65facabe71b9cbde))
|
||||
* **images-plugin:** update dependency lru-cache to v7.16.0 ([d54901a](https://github.com/appium/appium/commit/d54901a9c3982dd5595ffd54010e5029b60c4754))
|
||||
* **images-plugin:** update dependency lru-cache to v7.16.1 ([2fa6bf1](https://github.com/appium/appium/commit/2fa6bf1e11e66ba8256d0641e345359bd108bc7d))
|
||||
* **images-plugin:** update dependency lru-cache to v7.16.2 ([4b10322](https://github.com/appium/appium/commit/4b10322b80e0032dd4585ac7766edd1ddf798139))
|
||||
* **images-plugin:** update dependency lru-cache to v7.17.0 ([eb73be8](https://github.com/appium/appium/commit/eb73be8303c517dbaa965ae99bac3381522d939a))
|
||||
* **support:** update dependency axios to v1.3.4 ([49f157d](https://github.com/appium/appium/commit/49f157d63e3bdbd205527a5dc8f997df68540546))
|
||||
* **types:** update dependency type-fest to v3.5.7 ([b4416c5](https://github.com/appium/appium/commit/b4416c5c0f40200b36909a1fbb492d8c4a212108))
|
||||
* **types:** update dependency type-fest to v3.6.0 ([08a6f3a](https://github.com/appium/appium/commit/08a6f3a308c7ee162e992629888557b31e50a26e))
|
||||
* update axios to v1.3.3 ([8f9de63](https://github.com/appium/appium/commit/8f9de63e4a622712db545ab63f9f4ce6654e4a91))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [9.3.1](https://github.com/appium/appium/compare/@appium/base-driver@9.3.0...@appium/base-driver@9.3.1) (2023-02-09)
|
||||
|
||||
|
||||
|
||||
@@ -333,6 +333,68 @@ function processCapabilities(
|
||||
return /** @type {Capabilities<C>} */ (matchedCaps ?? {});
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a copy of a "bare" (single-level, non-W3C) capabilities object which has taken everything
|
||||
* within the 'appium:options' capability and promoted it to the top level.
|
||||
*
|
||||
* @template {Constraints} C
|
||||
* @param {NSCapabilities<C>} obj
|
||||
* @return {NSCapabilities<C>} the capabilities with 'options' promoted if necessary
|
||||
*/
|
||||
function promoteAppiumOptionsForObject(obj) {
|
||||
const appiumOptions = obj[PREFIXED_APPIUM_OPTS_CAP];
|
||||
if (!appiumOptions) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
if (!_.isPlainObject(appiumOptions)) {
|
||||
throw new errors.SessionNotCreatedError(
|
||||
`The ${PREFIXED_APPIUM_OPTS_CAP} capability must be an object`
|
||||
);
|
||||
}
|
||||
if (_.isEmpty(appiumOptions)) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
log.debug(
|
||||
`Found ${PREFIXED_APPIUM_OPTS_CAP} capability present; will promote items inside to caps`
|
||||
);
|
||||
|
||||
/**
|
||||
* @param {string} capName
|
||||
*/
|
||||
const shouldAddVendorPrefix = (capName) => !capName.startsWith(APPIUM_VENDOR_PREFIX);
|
||||
const verifyIfAcceptable = (/** @type {string} */ capName) => {
|
||||
if (!_.isString(capName)) {
|
||||
throw new errors.SessionNotCreatedError(
|
||||
`Capability names in ${PREFIXED_APPIUM_OPTS_CAP} must be strings. '${capName}' is unexpected`
|
||||
);
|
||||
}
|
||||
if (isStandardCap(capName)) {
|
||||
throw new errors.SessionNotCreatedError(
|
||||
`${PREFIXED_APPIUM_OPTS_CAP} must only contain vendor-specific capabilties. '${capName}' is unexpected`
|
||||
);
|
||||
}
|
||||
return capName;
|
||||
};
|
||||
const preprocessedOptions = _(appiumOptions)
|
||||
.mapKeys((value, /** @type {string} */ key) => verifyIfAcceptable(key))
|
||||
.mapKeys((value, key) => (shouldAddVendorPrefix(key) ? `${APPIUM_VENDOR_PREFIX}${key}` : key))
|
||||
.value();
|
||||
// warn if we are going to overwrite any keys on the base caps object
|
||||
const overwrittenKeys = _.intersection(Object.keys(obj), Object.keys(preprocessedOptions));
|
||||
if (overwrittenKeys.length > 0) {
|
||||
log.warn(
|
||||
`Found capabilities inside ${PREFIXED_APPIUM_OPTS_CAP} that will overwrite ` +
|
||||
`capabilities at the top level: ${JSON.stringify(overwrittenKeys)}`
|
||||
);
|
||||
}
|
||||
return _.cloneDeep({
|
||||
.../** @type {NSCapabilities<C>} */ (_.omit(obj, PREFIXED_APPIUM_OPTS_CAP)),
|
||||
...preprocessedOptions,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a copy of a capabilities object which has taken everything within the 'options'
|
||||
* capability and promoted it to the top level.
|
||||
@@ -342,69 +404,15 @@ function processCapabilities(
|
||||
* @return {import('@appium/types').W3CCapabilities<C>} the capabilities with 'options' promoted if necessary
|
||||
*/
|
||||
function promoteAppiumOptions(originalCaps) {
|
||||
const promoteForObject = (obj) => {
|
||||
const appiumOptions = obj[PREFIXED_APPIUM_OPTS_CAP];
|
||||
if (!appiumOptions) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
if (!_.isPlainObject(appiumOptions)) {
|
||||
throw new errors.SessionNotCreatedError(
|
||||
`The ${PREFIXED_APPIUM_OPTS_CAP} capability must be an object`
|
||||
);
|
||||
}
|
||||
if (_.isEmpty(appiumOptions)) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
log.debug(
|
||||
`Found ${PREFIXED_APPIUM_OPTS_CAP} capability present; will promote items inside to caps`
|
||||
);
|
||||
|
||||
/**
|
||||
* @param {string} capName
|
||||
*/
|
||||
const shouldAddVendorPrefix = (capName) => !capName.startsWith(APPIUM_VENDOR_PREFIX);
|
||||
const verifyIfAcceptable = (capName) => {
|
||||
if (!_.isString(capName)) {
|
||||
throw new errors.SessionNotCreatedError(
|
||||
`Capability names in ${PREFIXED_APPIUM_OPTS_CAP} must be strings. '${capName}' is unexpected`
|
||||
);
|
||||
}
|
||||
if (isStandardCap(capName)) {
|
||||
throw new errors.SessionNotCreatedError(
|
||||
`${PREFIXED_APPIUM_OPTS_CAP} must only contain vendor-specific capabilties. '${capName}' is unexpected`
|
||||
);
|
||||
}
|
||||
return capName;
|
||||
};
|
||||
const preprocessedOptions = _(appiumOptions)
|
||||
.mapKeys((value, key) => verifyIfAcceptable(key))
|
||||
.mapKeys((value, key) => (shouldAddVendorPrefix(key) ? `${APPIUM_VENDOR_PREFIX}${key}` : key))
|
||||
.value();
|
||||
// warn if we are going to overwrite any keys on the base caps object
|
||||
const overwrittenKeys = _.intersection(Object.keys(obj), Object.keys(preprocessedOptions));
|
||||
if (overwrittenKeys.length > 0) {
|
||||
log.warn(
|
||||
`Found capabilities inside ${PREFIXED_APPIUM_OPTS_CAP} that will overwrite ` +
|
||||
`capabilities at the top level: ${JSON.stringify(overwrittenKeys)}`
|
||||
);
|
||||
}
|
||||
return _.cloneDeep({
|
||||
..._.omit(obj, PREFIXED_APPIUM_OPTS_CAP),
|
||||
...preprocessedOptions,
|
||||
});
|
||||
};
|
||||
|
||||
const {alwaysMatch, firstMatch} = originalCaps;
|
||||
const result = {};
|
||||
const {alwaysMatch, firstMatch} = originalCaps;
|
||||
if (_.isPlainObject(alwaysMatch)) {
|
||||
result.alwaysMatch = promoteForObject(alwaysMatch);
|
||||
result.alwaysMatch = promoteAppiumOptionsForObject(alwaysMatch);
|
||||
} else if ('alwaysMatch' in originalCaps) {
|
||||
result.alwaysMatch = alwaysMatch;
|
||||
}
|
||||
if (_.isArray(firstMatch)) {
|
||||
result.firstMatch = firstMatch.map(promoteForObject);
|
||||
result.firstMatch = firstMatch.map(promoteAppiumOptionsForObject);
|
||||
} else if ('firstMatch' in originalCaps) {
|
||||
result.firstMatch = firstMatch;
|
||||
}
|
||||
@@ -421,6 +429,7 @@ export {
|
||||
isStandardCap,
|
||||
stripAppiumPrefixes,
|
||||
promoteAppiumOptions,
|
||||
promoteAppiumOptionsForObject,
|
||||
PREFIXED_APPIUM_OPTS_CAP,
|
||||
};
|
||||
|
||||
@@ -431,6 +440,11 @@ export {
|
||||
* @typedef {import('@appium/types').BaseDriverCapConstraints} BaseDriverCapConstraints
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template {Constraints} C
|
||||
* @typedef {import('@appium/types').ConstraintsToCaps<C>} ConstraintsToCaps
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef ValidateCapsOpts
|
||||
* @property {boolean} [skipPresenceConstraint] - if true, skip the presence constraint
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
/* eslint-disable require-await */
|
||||
// @ts-check
|
||||
import _ from 'lodash';
|
||||
|
||||
/**
|
||||
* @template {Constraints} C
|
||||
* @param {import('./timeout').TimeoutBase<C>} Base
|
||||
* @returns {EventBase<C>}
|
||||
*/
|
||||
export function EventMixin(Base) {
|
||||
/**
|
||||
* @implements {IEventCommands}
|
||||
*/
|
||||
class EventCommands extends Base {
|
||||
/**
|
||||
* Log a user-defined event in the event log.
|
||||
*
|
||||
* @param {string} vendor - a vendor prefix for the user, to ensure namespace
|
||||
* separation
|
||||
* @param {string} event - the event name
|
||||
*/
|
||||
async logCustomEvent(vendor, event) {
|
||||
this.logEvent(`${vendor}:${event}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the event log
|
||||
* @param {string|string[]} [type] - the event type to filter with.
|
||||
* It returns all events if the type is not provided or empty string/array.
|
||||
* @returns {Promise<import('@appium/types').EventHistory|Record<string,number>>} - the event history log object
|
||||
*/
|
||||
async getLogEvents(type) {
|
||||
if (_.isEmpty(type)) {
|
||||
return this.eventHistory;
|
||||
}
|
||||
|
||||
const typeList = _.castArray(type);
|
||||
|
||||
return _.reduce(
|
||||
this.eventHistory,
|
||||
(acc, eventTimes, eventType) => {
|
||||
if (typeList.includes(eventType)) {
|
||||
acc[eventType] = eventTimes;
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
{}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return EventCommands;
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {import('@appium/types').Constraints} Constraints
|
||||
* @typedef {import('@appium/types').IEventCommands} IEventCommands
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template {Constraints} C
|
||||
* @typedef {import('../driver').BaseDriverBase<C, import('@appium/types').ITimeoutCommands & IEventCommands>} EventBase
|
||||
*/
|
||||
@@ -0,0 +1,48 @@
|
||||
import _ from 'lodash';
|
||||
import {Constraints, IEventCommands} from '@appium/types';
|
||||
import {mixin} from './mixin';
|
||||
import {BaseDriver} from '../driver';
|
||||
|
||||
declare module '../driver' {
|
||||
interface BaseDriver<C extends Constraints> extends IEventCommands {}
|
||||
}
|
||||
|
||||
const EventCommands: IEventCommands = {
|
||||
/**
|
||||
* Log a user-defined event in the event log.
|
||||
*
|
||||
* @param vendor - a vendor prefix for the user, to ensure namespace
|
||||
* separation
|
||||
* @param event - the event name
|
||||
*/
|
||||
async logCustomEvent<C extends Constraints>(this: BaseDriver<C>, vendor: string, event: string) {
|
||||
this.logEvent(`${vendor}:${event}`);
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the event log
|
||||
* @param type - the event type to filter with.
|
||||
* It returns all events if the type is not provided or empty string/array.
|
||||
* @returns the event history log object
|
||||
*/
|
||||
async getLogEvents<C extends Constraints>(this: BaseDriver<C>, type: string | string[]) {
|
||||
if (_.isEmpty(type)) {
|
||||
return this.eventHistory;
|
||||
}
|
||||
|
||||
const typeList = _.castArray(type);
|
||||
|
||||
return _.reduce(
|
||||
this.eventHistory,
|
||||
(acc, eventTimes, eventType) => {
|
||||
if (typeList.includes(eventType)) {
|
||||
acc[eventType] = eventTimes;
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
{}
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
mixin(EventCommands);
|
||||
@@ -1,45 +0,0 @@
|
||||
import _ from 'lodash';
|
||||
import {errors, validateExecuteMethodParams} from '../../protocol';
|
||||
|
||||
/**
|
||||
* @template {Constraints} C
|
||||
* @param {import('./session').SessionBase<C>} Base
|
||||
* @returns {ExecuteBase<C>}
|
||||
*/
|
||||
export function ExecuteMixin(Base) {
|
||||
/**
|
||||
* @implements {IExecuteCommands}
|
||||
*/
|
||||
class ExecuteCommands extends Base {
|
||||
/**
|
||||
* @param {string} script
|
||||
* @param {[Record<string, any>]|[]} protoArgs
|
||||
*/
|
||||
async executeMethod(script, protoArgs) {
|
||||
const Driver = /** @type {DriverClass} */ (this.constructor);
|
||||
const commandMetadata = {...Driver.executeMethodMap?.[script]};
|
||||
if (!commandMetadata.command) {
|
||||
const availableScripts = _.keys(Driver.executeMethodMap);
|
||||
throw new errors.UnsupportedOperationError(
|
||||
`Unsupported execute method '${script}'. Available methods ` +
|
||||
`are: ${availableScripts.join(', ')}`
|
||||
);
|
||||
}
|
||||
const args = validateExecuteMethodParams(protoArgs, commandMetadata.params);
|
||||
return await this[commandMetadata.command](...args);
|
||||
}
|
||||
}
|
||||
return ExecuteCommands;
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {import('@appium/types').IExecuteCommands} IExecuteCommands
|
||||
* @typedef {import('@appium/types').Driver} Driver
|
||||
* @typedef {import('@appium/types').DriverClass} DriverClass
|
||||
* @typedef {import('@appium/types').Constraints} Constraints
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template {Constraints} C
|
||||
* @typedef {import('../driver').BaseDriverBase<C, import('@appium/types').ITimeoutCommands & import('@appium/types').IEventCommands & import('@appium/types').IFindCommands & import('@appium/types').ILogCommands<C> & import('@appium/types').ISettingsCommands & import('@appium/types').ISessionCommands & IExecuteCommands>} ExecuteBase
|
||||
*/
|
||||
@@ -0,0 +1,39 @@
|
||||
import _ from 'lodash';
|
||||
import {errors, validateExecuteMethodParams} from '../../protocol';
|
||||
import {
|
||||
Constraints,
|
||||
Driver,
|
||||
DriverClass,
|
||||
DriverCommand,
|
||||
IExecuteCommands,
|
||||
StringRecord,
|
||||
} from '@appium/types';
|
||||
import {mixin} from './mixin';
|
||||
import {BaseDriver} from '../driver';
|
||||
|
||||
declare module '../driver' {
|
||||
interface BaseDriver<C extends Constraints> extends IExecuteCommands {}
|
||||
}
|
||||
|
||||
const ExecuteCommands: IExecuteCommands = {
|
||||
async executeMethod<C extends Constraints>(
|
||||
this: BaseDriver<C>,
|
||||
script: string,
|
||||
protoArgs: [StringRecord] | any[]
|
||||
) {
|
||||
const Driver = this.constructor as DriverClass<Driver<C>>;
|
||||
const commandMetadata = {...Driver.executeMethodMap?.[script]};
|
||||
if (!commandMetadata.command) {
|
||||
const availableScripts = _.keys(Driver.executeMethodMap);
|
||||
throw new errors.UnsupportedOperationError(
|
||||
`Unsupported execute method '${script}'. Available methods ` +
|
||||
`are: ${availableScripts.join(', ')}`
|
||||
);
|
||||
}
|
||||
const args = validateExecuteMethodParams(protoArgs, commandMetadata.params);
|
||||
const command = this[commandMetadata.command] as DriverCommand;
|
||||
return await command.call(this, ...args);
|
||||
},
|
||||
};
|
||||
|
||||
mixin(ExecuteCommands);
|
||||
@@ -1,108 +0,0 @@
|
||||
/* eslint-disable no-unused-vars */
|
||||
/* eslint-disable require-await */
|
||||
// @ts-check
|
||||
import {errors} from '../../protocol';
|
||||
|
||||
/**
|
||||
* @template {Constraints} C
|
||||
* @param {import('./event').EventBase<C>} Base
|
||||
* @returns {FindBase<C>}
|
||||
*/
|
||||
export function FindMixin(Base) {
|
||||
/**
|
||||
* @implements {IFindCommands}
|
||||
*/
|
||||
class FindCommands extends Base {
|
||||
/**
|
||||
*
|
||||
* @returns {Promise<Element>}
|
||||
*/
|
||||
async findElement(strategy, selector) {
|
||||
return await this.findElOrElsWithProcessing(strategy, selector, false);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {Promise<Element[]>}
|
||||
*/
|
||||
async findElements(strategy, selector) {
|
||||
return await this.findElOrElsWithProcessing(strategy, selector, true);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {Promise<Element>}
|
||||
*/
|
||||
async findElementFromElement(strategy, selector, elementId) {
|
||||
return await this.findElOrElsWithProcessing(strategy, selector, false, elementId);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {Promise<Element[]>}
|
||||
*/
|
||||
async findElementsFromElement(strategy, selector, elementId) {
|
||||
return await this.findElOrElsWithProcessing(strategy, selector, true, elementId);
|
||||
}
|
||||
/**
|
||||
* Returns an object which adheres to the way the JSON Wire Protocol represents elements:
|
||||
*
|
||||
* Override this for your own driver!
|
||||
* @template {boolean} Mult
|
||||
* @template [Ctx=any]
|
||||
* @param {string} strategy
|
||||
* @param {string} selector
|
||||
* @param {Mult} mult
|
||||
* @param {Ctx} [context]
|
||||
* @returns {Promise<Mult extends true ? Element[] : Element>}
|
||||
*/
|
||||
async findElOrEls(strategy, selector, mult, context) {
|
||||
throw new errors.NotImplementedError('Not implemented yet for find.');
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
async getPageSource() {
|
||||
throw new errors.NotImplementedError('Not implemented yet for find.');
|
||||
}
|
||||
/**
|
||||
* @template {boolean} Mult
|
||||
* @template [Ctx=any]
|
||||
* @param {string} strategy
|
||||
* @param {string} selector
|
||||
* @param {Mult} mult
|
||||
* @param {Ctx} [context]
|
||||
* @returns {Promise<Mult extends true ? Element[] : Element>}
|
||||
*/
|
||||
async findElOrElsWithProcessing(strategy, selector, mult, context) {
|
||||
this.validateLocatorStrategy(strategy);
|
||||
try {
|
||||
return await this.findElOrEls(strategy, selector, mult, context);
|
||||
} catch (err) {
|
||||
if (this.opts.printPageSourceOnFindFailure) {
|
||||
const src = await this.getPageSource();
|
||||
this.log.debug(`Error finding element${mult ? 's' : ''}: ${err.message}`);
|
||||
this.log.debug(`Page source requested through 'printPageSourceOnFindFailure':`);
|
||||
this.log.debug(src);
|
||||
}
|
||||
// still want the error to occur
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return FindCommands;
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {import('@appium/types').Element} Element
|
||||
* @typedef {import('@appium/types').Constraints} Constraints
|
||||
* @typedef {import('@appium/types').IFindCommands} IFindCommands
|
||||
* @typedef {import('@appium/types').ITimeoutCommands} ITimeoutCommands
|
||||
* @typedef {import('@appium/types').IEventCommands} IEventCommands
|
||||
*/
|
||||
/**
|
||||
* @template {Constraints} C
|
||||
* @typedef {import('../driver').BaseDriverBase<C, ITimeoutCommands & IEventCommands & IFindCommands>} FindBase
|
||||
*/
|
||||
@@ -0,0 +1,79 @@
|
||||
import {Constraints, IFindCommands} from '@appium/types';
|
||||
import {errors} from '../../protocol';
|
||||
import {BaseDriver} from '../driver';
|
||||
import {mixin} from './mixin';
|
||||
|
||||
declare module '../driver' {
|
||||
interface BaseDriver<C extends Constraints> extends IFindCommands {}
|
||||
}
|
||||
|
||||
const FindCommands: IFindCommands = {
|
||||
async findElement<C extends Constraints>(this: BaseDriver<C>, strategy, selector) {
|
||||
return await this.findElOrElsWithProcessing(strategy, selector, false);
|
||||
},
|
||||
|
||||
async findElements<C extends Constraints>(this: BaseDriver<C>, strategy, selector) {
|
||||
return await this.findElOrElsWithProcessing(strategy, selector, true);
|
||||
},
|
||||
|
||||
async findElementFromElement<C extends Constraints>(
|
||||
this: BaseDriver<C>,
|
||||
strategy: string,
|
||||
selector: string,
|
||||
elementId: string
|
||||
) {
|
||||
return await this.findElOrElsWithProcessing(strategy, selector, false, elementId);
|
||||
},
|
||||
|
||||
async findElementsFromElement<C extends Constraints>(
|
||||
this: BaseDriver<C>,
|
||||
strategy: string,
|
||||
selector: string,
|
||||
elementId: string
|
||||
) {
|
||||
return await this.findElOrElsWithProcessing(strategy, selector, true, elementId);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns an object which adheres to the way the JSON Wire Protocol represents elements:
|
||||
*
|
||||
* Override this for your own driver!
|
||||
*/
|
||||
async findElOrEls<C extends Constraints, Mult extends boolean, Ctx = any>(
|
||||
this: BaseDriver<C>,
|
||||
strategy: string,
|
||||
selector: string,
|
||||
mult: Mult,
|
||||
context: Ctx
|
||||
) {
|
||||
throw new errors.NotImplementedError('Not implemented yet for find.');
|
||||
},
|
||||
|
||||
async getPageSource<C extends Constraints>(this: BaseDriver<C>) {
|
||||
throw new errors.NotImplementedError('Not implemented yet for find.');
|
||||
},
|
||||
|
||||
async findElOrElsWithProcessing<C extends Constraints, Mult extends boolean, Ctx = any>(
|
||||
this: BaseDriver<C>,
|
||||
strategy: string,
|
||||
selector: string,
|
||||
mult: Mult,
|
||||
context?: Ctx
|
||||
) {
|
||||
this.validateLocatorStrategy(strategy);
|
||||
try {
|
||||
return await this.findElOrEls(strategy, selector, mult, context);
|
||||
} catch (err) {
|
||||
if (this.opts.printPageSourceOnFindFailure) {
|
||||
const src = await this.getPageSource();
|
||||
this.log.debug(`Error finding element${mult ? 's' : ''}: ${err.message}`);
|
||||
this.log.debug(`Page source requested through 'printPageSourceOnFindFailure':`);
|
||||
this.log.debug(src);
|
||||
}
|
||||
// still want the error to occur
|
||||
throw err;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
mixin(FindCommands);
|
||||
@@ -1,35 +0,0 @@
|
||||
// @ts-check
|
||||
import _ from 'lodash';
|
||||
import {EventMixin} from './event';
|
||||
import {FindMixin} from './find';
|
||||
import {LogMixin} from './log';
|
||||
import {SessionMixin} from './session';
|
||||
import {SettingsMixin} from './settings';
|
||||
import {TimeoutMixin} from './timeout';
|
||||
import {ExecuteMixin} from './execute';
|
||||
|
||||
/**
|
||||
* Applies all the mixins to the `BaseDriverBase` class; returns a `BaseDriver` class definition.
|
||||
* Each mixin is applied in the order it is listed here, and each type is a union with the previous.
|
||||
*
|
||||
* @template {Constraints} C
|
||||
* @param {BaseDriverBase<C>} Base
|
||||
*/
|
||||
export const createBaseDriverClass = _.flow(
|
||||
TimeoutMixin,
|
||||
EventMixin,
|
||||
FindMixin,
|
||||
LogMixin,
|
||||
SettingsMixin,
|
||||
SessionMixin,
|
||||
ExecuteMixin
|
||||
);
|
||||
|
||||
/**
|
||||
* @template {Constraints} C
|
||||
* @typedef {import('../driver').BaseDriverBase<C>} BaseDriverBase
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {import('@appium/types').Constraints} Constraints
|
||||
*/
|
||||
@@ -0,0 +1,7 @@
|
||||
export * from './event';
|
||||
export * from './find';
|
||||
export * from './log';
|
||||
export * from './session';
|
||||
export * from './settings';
|
||||
export * from './timeout';
|
||||
export * from './execute';
|
||||
@@ -1,64 +0,0 @@
|
||||
/* eslint-disable require-await */
|
||||
// @ts-check
|
||||
|
||||
import _ from 'lodash';
|
||||
|
||||
/**
|
||||
* @template {Constraints} C
|
||||
* @param {import('./find').FindBase<C>} Base
|
||||
* @returns {LogBase<C>}
|
||||
*/
|
||||
export function LogMixin(Base) {
|
||||
/**
|
||||
* @implements {ILogCommands<C>}
|
||||
*/
|
||||
class LogCommands extends Base {
|
||||
/** @type {Readonly<import('@appium/types').LogDefRecord<C>>} */
|
||||
supportedLogTypes;
|
||||
|
||||
constructor(...args) {
|
||||
super(...args);
|
||||
this.supportedLogTypes ??= {};
|
||||
}
|
||||
|
||||
async getLogTypes() {
|
||||
this.log.debug('Retrieving supported log types');
|
||||
return Object.keys(this.supportedLogTypes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @this {import('@appium/types').Driver<C>}
|
||||
* @param {keyof typeof this.supportedLogTypes} logType
|
||||
* @returns {Promise<import('type-fest').AsyncReturnType<typeof this.supportedLogTypes[keyof typeof this.supportedLogTypes]['getter']>>}
|
||||
*/
|
||||
async getLog(logType) {
|
||||
this.log.debug(`Retrieving '${String(logType)}' logs`);
|
||||
|
||||
if (!(logType in this.supportedLogTypes)) {
|
||||
const logsTypesWithDescriptions = _.mapValues(this.supportedLogTypes, 'description');
|
||||
throw new Error(
|
||||
`Unsupported log type '${String(logType)}'. ` +
|
||||
`Supported types: ${JSON.stringify(logsTypesWithDescriptions)}`
|
||||
);
|
||||
}
|
||||
|
||||
return await this.supportedLogTypes[logType].getter(this);
|
||||
}
|
||||
}
|
||||
return LogCommands;
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {import('@appium/types').Constraints} Constraints
|
||||
* @typedef {import('@appium/types').StringRecord} StringRecord
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template {Constraints} C
|
||||
* @typedef {import('@appium/types').ILogCommands<C>} ILogCommands
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template {Constraints} C
|
||||
* @typedef {import('../driver').BaseDriverBase<C, import('@appium/types').ITimeoutCommands & import('@appium/types').IEventCommands & import('@appium/types').IFindCommands & ILogCommands<C>>} LogBase
|
||||
*/
|
||||
@@ -0,0 +1,36 @@
|
||||
import {Constraints, Driver, ILogCommands, LogDefRecord} from '@appium/types';
|
||||
import _ from 'lodash';
|
||||
import {BaseDriver} from '../driver';
|
||||
import {mixin} from './mixin';
|
||||
|
||||
declare module '../driver' {
|
||||
interface BaseDriver<C extends Constraints> extends ILogCommands {}
|
||||
}
|
||||
|
||||
const LogCommands: ILogCommands = {
|
||||
supportedLogTypes: <LogDefRecord>{},
|
||||
|
||||
async getLogTypes<C extends Constraints>(this: BaseDriver<C>) {
|
||||
this.log.debug('Retrieving supported log types');
|
||||
return Object.keys(this.supportedLogTypes);
|
||||
},
|
||||
|
||||
async getLog<C extends Constraints>(
|
||||
this: Driver<C>,
|
||||
logType: keyof ILogCommands['supportedLogTypes']
|
||||
) {
|
||||
this.log.debug(`Retrieving '${String(logType)}' logs`);
|
||||
|
||||
if (!(logType in this.supportedLogTypes)) {
|
||||
const logsTypesWithDescriptions = _.mapValues(this.supportedLogTypes, 'description');
|
||||
throw new Error(
|
||||
`Unsupported log type '${String(logType)}'. ` +
|
||||
`Supported types: ${JSON.stringify(logsTypesWithDescriptions)}`
|
||||
);
|
||||
}
|
||||
|
||||
return await this.supportedLogTypes[logType].getter(this);
|
||||
},
|
||||
};
|
||||
|
||||
mixin(LogCommands);
|
||||
@@ -0,0 +1,14 @@
|
||||
import {Constraints} from '@appium/types';
|
||||
import {BaseDriver} from '../driver';
|
||||
|
||||
/**
|
||||
* This function assigns a mixin `T` to the `FakeDriver` class' prototype.
|
||||
* While each mixin has its own interface which is (in isolation) unrelated to `FakeDriver`, the constraint
|
||||
* on this generic type `T` is that it must be a partial of `FakeDriver`'s interface. This enforces
|
||||
* that it does not conflict with the existing interface of `FakeDriver`. In that way, you can
|
||||
* think of it as a type guard.
|
||||
* @param mixin Mixin implementation
|
||||
*/
|
||||
export function mixin<C extends Constraints, T extends Partial<BaseDriver<C>>>(mixin: T): void {
|
||||
Object.assign(BaseDriver.prototype, mixin);
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
/* eslint-disable no-unused-vars */
|
||||
/* eslint-disable require-await */
|
||||
// @ts-check
|
||||
import _ from 'lodash';
|
||||
|
||||
/**
|
||||
* @template {Constraints} C
|
||||
* @param {import('./settings').SettingsBase<C>} Base
|
||||
* @returns {SessionBase<C>}
|
||||
*/
|
||||
export function SessionMixin(Base) {
|
||||
/**
|
||||
* @implements {ISessionCommands}
|
||||
*/
|
||||
class SessionCommands extends Base {
|
||||
/**
|
||||
* @returns {Promise<MultiSessionData[]>}
|
||||
*/
|
||||
async getSessions() {
|
||||
let ret = [];
|
||||
|
||||
if (this.sessionId) {
|
||||
ret.push({
|
||||
id: this.sessionId,
|
||||
capabilities: this.caps,
|
||||
});
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns capabilities for the session and event history (if applicable)
|
||||
* @returns {Promise<SingularSessionData>}
|
||||
*/
|
||||
async getSession() {
|
||||
if (this.caps.eventTimings) {
|
||||
return {...this.caps, events: this.eventHistory};
|
||||
}
|
||||
return this.caps;
|
||||
}
|
||||
}
|
||||
|
||||
return SessionCommands;
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {import('@appium/types').ISessionCommands} ISessionCommands
|
||||
* @typedef {import('@appium/types').SingularSessionData} SingularSessionData
|
||||
* @typedef {import('@appium/types').MultiSessionData} MultiSessionData
|
||||
* @typedef {import('@appium/types').Constraints} Constraints
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template {Constraints} C
|
||||
* @typedef {import('../driver').BaseDriverBase<C, import('@appium/types').ITimeoutCommands & import('@appium/types').IEventCommands & import('@appium/types').IFindCommands & import('@appium/types').ILogCommands<C> & import('@appium/types').ISettingsCommands & ISessionCommands>} SessionBase
|
||||
*/
|
||||
@@ -0,0 +1,34 @@
|
||||
import {Constraints, ISessionCommands, MultiSessionData} from '@appium/types';
|
||||
import {BaseDriver} from '../driver';
|
||||
import {mixin} from './mixin';
|
||||
|
||||
declare module '../driver' {
|
||||
interface BaseDriver<C extends Constraints> extends ISessionCommands {}
|
||||
}
|
||||
|
||||
const SessionCommands: ISessionCommands = {
|
||||
async getSessions<C extends Constraints>(this: BaseDriver<C>) {
|
||||
const ret: MultiSessionData[] = [];
|
||||
|
||||
if (this.sessionId) {
|
||||
ret.push({
|
||||
id: this.sessionId,
|
||||
capabilities: this.caps,
|
||||
});
|
||||
}
|
||||
|
||||
return ret;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns capabilities for the session and event history (if applicable)
|
||||
*/
|
||||
async getSession<C extends Constraints>(this: BaseDriver<C>) {
|
||||
if (this.caps.eventTimings) {
|
||||
return {...this.caps, events: this.eventHistory};
|
||||
}
|
||||
return this.caps;
|
||||
},
|
||||
};
|
||||
|
||||
mixin(SessionCommands);
|
||||
@@ -1,38 +0,0 @@
|
||||
// @ts-check
|
||||
|
||||
/**
|
||||
* @template {Constraints} C
|
||||
* @param {import('./log').LogBase<C>} Base
|
||||
* @returns {SettingsBase<C>}
|
||||
*/
|
||||
export function SettingsMixin(Base) {
|
||||
/**
|
||||
* @implements {ISettingsCommands}
|
||||
*/
|
||||
class SettingsCommands extends Base {
|
||||
async updateSettings(newSettings) {
|
||||
if (!this.settings) {
|
||||
this.log.errorAndThrow('Cannot update settings; settings object not found');
|
||||
}
|
||||
return await this.settings.update(newSettings);
|
||||
}
|
||||
|
||||
async getSettings() {
|
||||
if (!this.settings) {
|
||||
this.log.errorAndThrow('Cannot get settings; settings object not found');
|
||||
}
|
||||
return await this.settings.getSettings();
|
||||
}
|
||||
}
|
||||
|
||||
return SettingsCommands;
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {import('@appium/types').Constraints} Constraints
|
||||
* @typedef {import('@appium/types').ISettingsCommands} ISettingsCommands
|
||||
*/
|
||||
/**
|
||||
* @template {Constraints} C
|
||||
* @typedef {import('../driver').BaseDriverBase<C, import('@appium/types').ITimeoutCommands & import('@appium/types').IEventCommands & import('@appium/types').IFindCommands & import('@appium/types').ILogCommands<C> & ISettingsCommands>} SettingsBase
|
||||
*/
|
||||
@@ -0,0 +1,25 @@
|
||||
import {BaseDriver} from '../driver';
|
||||
import {Constraints, ISettingsCommands, StringRecord} from '@appium/types';
|
||||
import {mixin} from './mixin';
|
||||
|
||||
declare module '../driver' {
|
||||
interface BaseDriver<C extends Constraints> extends ISettingsCommands {}
|
||||
}
|
||||
|
||||
const SettingsCommands: ISettingsCommands = {
|
||||
async updateSettings<C extends Constraints>(this: BaseDriver<C>, newSettings: StringRecord) {
|
||||
if (!this.settings) {
|
||||
this.log.errorAndThrow('Cannot update settings; settings object not found');
|
||||
}
|
||||
return await this.settings.update(newSettings);
|
||||
},
|
||||
|
||||
async getSettings<C extends Constraints>(this: BaseDriver<C>) {
|
||||
if (!this.settings) {
|
||||
this.log.errorAndThrow('Cannot get settings; settings object not found');
|
||||
}
|
||||
return this.settings.getSettings();
|
||||
},
|
||||
};
|
||||
|
||||
mixin(SettingsCommands);
|
||||
@@ -1,168 +0,0 @@
|
||||
// @ts-check
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
/* eslint-disable require-await */
|
||||
import {waitForCondition} from 'asyncbox';
|
||||
import _ from 'lodash';
|
||||
import {util} from '@appium/support';
|
||||
import {errors} from '../../protocol';
|
||||
|
||||
const MIN_TIMEOUT = 0;
|
||||
|
||||
/**
|
||||
* @template {Constraints} C
|
||||
* @param {import('../driver').BaseDriverBase<C>} Base
|
||||
* @returns {TimeoutBase<C>}
|
||||
*/
|
||||
export function TimeoutMixin(Base) {
|
||||
/**
|
||||
* @implements {ITimeoutCommands}
|
||||
*/
|
||||
class TimeoutCommands extends Base {
|
||||
async timeouts(type, ms, script, pageLoad, implicit) {
|
||||
if (util.hasValue(type) && util.hasValue(ms)) {
|
||||
this.log.debug(`MJSONWP timeout arguments: ${JSON.stringify({type, ms})}}`);
|
||||
|
||||
switch (type) {
|
||||
case 'command':
|
||||
await this.newCommandTimeout(ms);
|
||||
return;
|
||||
case 'implicit':
|
||||
await this.implicitWaitMJSONWP(ms);
|
||||
return;
|
||||
case 'page load':
|
||||
await this.pageLoadTimeoutMJSONWP(ms);
|
||||
return;
|
||||
case 'script':
|
||||
await this.scriptTimeoutMJSONWP(ms);
|
||||
return;
|
||||
default:
|
||||
throw new Error(`'${type}' type is not supported for MJSONWP timeout`);
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise assume it is W3C protocol
|
||||
this.log.debug(
|
||||
`W3C timeout argument: ${JSON.stringify({
|
||||
script,
|
||||
pageLoad,
|
||||
implicit,
|
||||
})}}`
|
||||
);
|
||||
if (util.hasValue(script)) {
|
||||
await this.scriptTimeoutW3C(script);
|
||||
}
|
||||
if (util.hasValue(pageLoad)) {
|
||||
await this.pageLoadTimeoutW3C(pageLoad);
|
||||
}
|
||||
if (util.hasValue(implicit)) {
|
||||
await this.implicitWaitW3C(implicit);
|
||||
}
|
||||
}
|
||||
|
||||
async getTimeouts() {
|
||||
return {
|
||||
command: this.newCommandTimeoutMs,
|
||||
implicit: this.implicitWaitMs,
|
||||
};
|
||||
}
|
||||
|
||||
// implicit
|
||||
async implicitWaitW3C(ms) {
|
||||
await this.implicitWait(ms);
|
||||
}
|
||||
|
||||
async implicitWaitMJSONWP(ms) {
|
||||
await this.implicitWait(ms);
|
||||
}
|
||||
|
||||
async implicitWait(ms) {
|
||||
await this.setImplicitWait(this.parseTimeoutArgument(ms));
|
||||
}
|
||||
|
||||
// pageLoad
|
||||
async pageLoadTimeoutW3C(ms) {
|
||||
throw new errors.NotImplementedError('Not implemented yet for pageLoad.');
|
||||
}
|
||||
|
||||
async pageLoadTimeoutMJSONWP(ms) {
|
||||
throw new errors.NotImplementedError('Not implemented yet for pageLoad.');
|
||||
}
|
||||
|
||||
// script
|
||||
async scriptTimeoutW3C(ms) {
|
||||
throw new errors.NotImplementedError('Not implemented yet for script.');
|
||||
}
|
||||
|
||||
async scriptTimeoutMJSONWP(ms) {
|
||||
throw new errors.NotImplementedError('Not implemented yet for script.');
|
||||
}
|
||||
|
||||
// command
|
||||
async newCommandTimeout(ms) {
|
||||
this.setNewCommandTimeout(this.parseTimeoutArgument(ms));
|
||||
}
|
||||
|
||||
setImplicitWait(ms) {
|
||||
// eslint-disable-line require-await
|
||||
this.implicitWaitMs = ms;
|
||||
this.log.debug(`Set implicit wait to ${ms}ms`);
|
||||
if (this.managedDrivers && this.managedDrivers.length) {
|
||||
this.log.debug('Setting implicit wait on managed drivers');
|
||||
for (let driver of this.managedDrivers) {
|
||||
if (_.isFunction(driver.setImplicitWait)) {
|
||||
driver.setImplicitWait(ms);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setNewCommandTimeout(ms) {
|
||||
this.newCommandTimeoutMs = ms;
|
||||
this.log.debug(`Set new command timeout to ${ms}ms`);
|
||||
if (this.managedDrivers && this.managedDrivers.length) {
|
||||
this.log.debug('Setting new command timeout on managed drivers');
|
||||
for (let driver of this.managedDrivers) {
|
||||
if (_.isFunction(driver.setNewCommandTimeout)) {
|
||||
driver.setNewCommandTimeout(ms);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async implicitWaitForCondition(condFn) {
|
||||
this.log.debug(`Waiting up to ${this.implicitWaitMs} ms for condition`);
|
||||
let wrappedCondFn = async (...args) => {
|
||||
// reset command timeout
|
||||
await this.clearNewCommandTimeout();
|
||||
|
||||
return await condFn(...args);
|
||||
};
|
||||
return await waitForCondition(wrappedCondFn, {
|
||||
waitMs: this.implicitWaitMs,
|
||||
intervalMs: 500,
|
||||
logger: this.log,
|
||||
});
|
||||
}
|
||||
|
||||
parseTimeoutArgument(ms) {
|
||||
let duration = parseInt(ms, 10);
|
||||
if (_.isNaN(duration) || duration < MIN_TIMEOUT) {
|
||||
throw new errors.UnknownError(`Invalid timeout value '${ms}'`);
|
||||
}
|
||||
return duration;
|
||||
}
|
||||
}
|
||||
|
||||
return TimeoutCommands;
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {import('@appium/types').ITimeoutCommands} ITimeoutCommands
|
||||
* @typedef {import('@appium/types').Constraints} Constraints
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template {Constraints} C
|
||||
* @typedef {import('../driver').BaseDriverBase<C, ITimeoutCommands>} TimeoutBase
|
||||
*/
|
||||
@@ -0,0 +1,155 @@
|
||||
// @ts-check
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
/* eslint-disable require-await */
|
||||
import {waitForCondition} from 'asyncbox';
|
||||
import _ from 'lodash';
|
||||
import {util} from '@appium/support';
|
||||
import {errors} from '../../protocol';
|
||||
import {BaseDriver} from '../driver';
|
||||
import {Constraints, ITimeoutCommands} from '@appium/types';
|
||||
import {mixin} from './mixin';
|
||||
|
||||
declare module '../driver' {
|
||||
interface BaseDriver<C extends Constraints> extends ITimeoutCommands {}
|
||||
}
|
||||
|
||||
const MIN_TIMEOUT = 0;
|
||||
|
||||
const TimeoutCommands: ITimeoutCommands = {
|
||||
async timeouts(type, ms, script, pageLoad, implicit) {
|
||||
if (util.hasValue(type) && util.hasValue(ms)) {
|
||||
this.log.debug(`MJSONWP timeout arguments: ${JSON.stringify({type, ms})}}`);
|
||||
|
||||
switch (type) {
|
||||
case 'command':
|
||||
await this.newCommandTimeout(ms);
|
||||
return;
|
||||
case 'implicit':
|
||||
await this.implicitWaitMJSONWP(ms);
|
||||
return;
|
||||
case 'page load':
|
||||
await this.pageLoadTimeoutMJSONWP(ms);
|
||||
return;
|
||||
case 'script':
|
||||
await this.scriptTimeoutMJSONWP(ms);
|
||||
return;
|
||||
default:
|
||||
throw new Error(`'${type}' type is not supported for MJSONWP timeout`);
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise assume it is W3C protocol
|
||||
this.log.debug(
|
||||
`W3C timeout argument: ${JSON.stringify({
|
||||
script,
|
||||
pageLoad,
|
||||
implicit,
|
||||
})}}`
|
||||
);
|
||||
if (util.hasValue(script)) {
|
||||
await this.scriptTimeoutW3C(script);
|
||||
}
|
||||
if (util.hasValue(pageLoad)) {
|
||||
await this.pageLoadTimeoutW3C(pageLoad);
|
||||
}
|
||||
if (util.hasValue(implicit)) {
|
||||
await this.implicitWaitW3C(implicit);
|
||||
}
|
||||
},
|
||||
|
||||
async getTimeouts() {
|
||||
return {
|
||||
command: this.newCommandTimeoutMs,
|
||||
implicit: this.implicitWaitMs,
|
||||
};
|
||||
},
|
||||
|
||||
// implicit
|
||||
async implicitWaitW3C(ms) {
|
||||
await this.implicitWait(ms);
|
||||
},
|
||||
|
||||
async implicitWaitMJSONWP(ms) {
|
||||
await this.implicitWait(ms);
|
||||
},
|
||||
|
||||
async implicitWait(ms) {
|
||||
await this.setImplicitWait(this.parseTimeoutArgument(ms));
|
||||
},
|
||||
|
||||
// pageLoad
|
||||
async pageLoadTimeoutW3C(ms) {
|
||||
throw new errors.NotImplementedError('Not implemented yet for pageLoad.');
|
||||
},
|
||||
|
||||
async pageLoadTimeoutMJSONWP(ms) {
|
||||
throw new errors.NotImplementedError('Not implemented yet for pageLoad.');
|
||||
},
|
||||
|
||||
// script
|
||||
async scriptTimeoutW3C(ms) {
|
||||
throw new errors.NotImplementedError('Not implemented yet for script.');
|
||||
},
|
||||
|
||||
async scriptTimeoutMJSONWP(ms) {
|
||||
throw new errors.NotImplementedError('Not implemented yet for script.');
|
||||
},
|
||||
|
||||
// command
|
||||
async newCommandTimeout(ms) {
|
||||
this.setNewCommandTimeout(this.parseTimeoutArgument(ms));
|
||||
},
|
||||
|
||||
setImplicitWait(ms) {
|
||||
// eslint-disable-line require-await
|
||||
this.implicitWaitMs = ms;
|
||||
this.log.debug(`Set implicit wait to ${ms}ms`);
|
||||
if (this.managedDrivers && this.managedDrivers.length) {
|
||||
this.log.debug('Setting implicit wait on managed drivers');
|
||||
for (const driver of this.managedDrivers) {
|
||||
if (_.isFunction(driver.setImplicitWait)) {
|
||||
driver.setImplicitWait(ms);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
setNewCommandTimeout(ms) {
|
||||
this.newCommandTimeoutMs = ms;
|
||||
this.log.debug(`Set new command timeout to ${ms}ms`);
|
||||
if (this.managedDrivers && this.managedDrivers.length) {
|
||||
this.log.debug('Setting new command timeout on managed drivers');
|
||||
for (const driver of this.managedDrivers) {
|
||||
if (_.isFunction(driver.setNewCommandTimeout)) {
|
||||
driver.setNewCommandTimeout(ms);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
async implicitWaitForCondition(condFn) {
|
||||
this.log.debug(`Waiting up to ${this.implicitWaitMs} ms for condition`);
|
||||
const wrappedCondFn = async (...args: any[]) => {
|
||||
// reset command timeout
|
||||
await this.clearNewCommandTimeout();
|
||||
|
||||
return await condFn(...args);
|
||||
};
|
||||
return await waitForCondition(wrappedCondFn, {
|
||||
waitMs: this.implicitWaitMs,
|
||||
intervalMs: 500,
|
||||
logger: this.log,
|
||||
});
|
||||
},
|
||||
|
||||
parseTimeoutArgument<C extends Constraints>(this: BaseDriver<C>, ms: number | string) {
|
||||
const duration = parseInt(String(ms), 10);
|
||||
if (_.isNaN(duration) || duration < MIN_TIMEOUT) {
|
||||
throw new errors.UnknownError(`Invalid timeout value '${ms}'`);
|
||||
}
|
||||
return duration;
|
||||
},
|
||||
};
|
||||
|
||||
mixin(TimeoutCommands);
|
||||
@@ -29,13 +29,10 @@ class DriverCore {
|
||||
*/
|
||||
static baseVersion = BASEDRIVER_VER;
|
||||
|
||||
/** @type {import('@appium/types').ExecuteMethodMap<DriverCore>} */
|
||||
static executeMethodMap = {};
|
||||
|
||||
/**
|
||||
* @type {string?}
|
||||
*/
|
||||
sessionId = null;
|
||||
sessionId;
|
||||
|
||||
/**
|
||||
* @type {import('@appium/types').DriverOpts<C>}
|
||||
@@ -47,7 +44,8 @@ class DriverCore {
|
||||
*/
|
||||
initialOpts;
|
||||
|
||||
helpers = helpers;
|
||||
/** @type {typeof helpers} */
|
||||
helpers;
|
||||
|
||||
/**
|
||||
* basePath is used for several purposes, for example in setting up
|
||||
@@ -56,39 +54,43 @@ class DriverCore {
|
||||
* initially but it is automatically updated during any actual program
|
||||
* execution by the routeConfiguringFunction, which is necessarily run as
|
||||
* the entrypoint for any Appium server
|
||||
* @type {string}
|
||||
*/
|
||||
basePath = DEFAULT_BASE_PATH;
|
||||
basePath;
|
||||
|
||||
relaxedSecurityEnabled = false;
|
||||
/** @type {boolean} */
|
||||
relaxedSecurityEnabled;
|
||||
|
||||
/** @type {string[]} */
|
||||
allowInsecure = [];
|
||||
allowInsecure;
|
||||
|
||||
/** @type {string[]} */
|
||||
denyInsecure = [];
|
||||
denyInsecure;
|
||||
|
||||
newCommandTimeoutMs = NEW_COMMAND_TIMEOUT_MS;
|
||||
/** @type {number} */
|
||||
newCommandTimeoutMs;
|
||||
|
||||
implicitWaitMs = 0;
|
||||
/** @type {number} */
|
||||
implicitWaitMs;
|
||||
|
||||
/** @type {string[]} */
|
||||
locatorStrategies = [];
|
||||
locatorStrategies;
|
||||
|
||||
/** @type {string[]} */
|
||||
webLocatorStrategies = [];
|
||||
webLocatorStrategies;
|
||||
|
||||
/** @type {Driver[]} */
|
||||
managedDrivers = [];
|
||||
managedDrivers;
|
||||
|
||||
/** @type {NodeJS.Timeout?} */
|
||||
noCommandTimer = null;
|
||||
noCommandTimer;
|
||||
|
||||
/** @type {EventHistory} */
|
||||
_eventHistory = {commands: []};
|
||||
_eventHistory;
|
||||
|
||||
// used to handle driver events
|
||||
/** @type {NodeJS.EventEmitter} */
|
||||
eventEmitter = new EventEmitter();
|
||||
eventEmitter;
|
||||
|
||||
/**
|
||||
* @type {AppiumLogger}
|
||||
@@ -97,8 +99,9 @@ class DriverCore {
|
||||
|
||||
/**
|
||||
* @protected
|
||||
* @type {boolean}
|
||||
*/
|
||||
shutdownUnexpectedly = false;
|
||||
shutdownUnexpectedly;
|
||||
|
||||
/**
|
||||
* @type {boolean}
|
||||
@@ -107,16 +110,18 @@ class DriverCore {
|
||||
|
||||
/**
|
||||
* @protected
|
||||
* @type {AsyncLock}
|
||||
*/
|
||||
commandsQueueGuard = new AsyncLock();
|
||||
commandsQueueGuard;
|
||||
|
||||
/**
|
||||
* settings should be instantiated by drivers which extend BaseDriver, but
|
||||
* we set it to an empty DeviceSettings instance here to make sure that the
|
||||
* default settings are applied even if an extending driver doesn't utilize
|
||||
* the settings functionality itself
|
||||
* @type {DeviceSettings}
|
||||
*/
|
||||
settings = new DeviceSettings();
|
||||
settings;
|
||||
|
||||
/**
|
||||
* @param {DriverOpts<C>} opts
|
||||
@@ -139,6 +144,22 @@ class DriverCore {
|
||||
this.initialOpts = _.cloneDeep(opts);
|
||||
|
||||
this.sessionId = null;
|
||||
this.helpers = helpers;
|
||||
this.basePath = DEFAULT_BASE_PATH;
|
||||
this.relaxedSecurityEnabled = false;
|
||||
this.allowInsecure = [];
|
||||
this.denyInsecure = [];
|
||||
this.newCommandTimeoutMs = NEW_COMMAND_TIMEOUT_MS;
|
||||
this.implicitWaitMs = 0;
|
||||
this.locatorStrategies = [];
|
||||
this.webLocatorStrategies = [];
|
||||
this.managedDrivers = [];
|
||||
this.noCommandTimer = null;
|
||||
this._eventHistory = {commands: []};
|
||||
this.eventEmitter = new EventEmitter();
|
||||
this.shutdownUnexpectedly = false;
|
||||
this.commandsQueueGuard = new AsyncLock();
|
||||
this.settings = new DeviceSettings();
|
||||
}
|
||||
|
||||
get log() {
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
/* eslint-disable require-await */
|
||||
/* eslint-disable no-unused-vars */
|
||||
|
||||
import {validateCaps, PREFIXED_APPIUM_OPTS_CAP, processCapabilities} from './capabilities';
|
||||
import {validateCaps, processCapabilities} from './capabilities';
|
||||
import {DriverCore} from './core';
|
||||
import {util} from '@appium/support';
|
||||
import B from 'bluebird';
|
||||
import _ from 'lodash';
|
||||
import {fixCaps, isW3cCaps} from '../helpers/capabilities';
|
||||
import {DELETE_SESSION_COMMAND, determineProtocol, errors} from '../protocol';
|
||||
import {createBaseDriverClass} from './commands';
|
||||
import helpers from './helpers';
|
||||
import {BASE_DESIRED_CAP_CONSTRAINTS} from '@appium/types';
|
||||
|
||||
@@ -22,9 +21,10 @@ const ON_UNEXPECTED_SHUTDOWN_EVENT = 'onUnexpectedShutdown';
|
||||
* @implements {SessionHandler<C>}
|
||||
* @template {Constraints} C
|
||||
* @template {StringRecord} [CArgs=StringRecord]
|
||||
* @implements {Driver<C, CArgs>}
|
||||
* @extends {DriverCore<C>}
|
||||
*/
|
||||
export class BaseDriverCore extends DriverCore {
|
||||
export class BaseDriver extends DriverCore {
|
||||
/**
|
||||
* @type {CArgs & ServerArgs}
|
||||
*/
|
||||
@@ -59,7 +59,7 @@ export class BaseDriverCore extends DriverCore {
|
||||
super(opts, shouldValidateCaps);
|
||||
|
||||
/**
|
||||
* This must be assigned here because the declaration of {@linkcode BaseDriverCore.opts} above
|
||||
* This must be assigned here because the declaration of {@linkcode BaseDriver.opts} above
|
||||
* blows away {@linkcode DriverCore.opts}.
|
||||
*/
|
||||
this.opts = opts;
|
||||
@@ -407,12 +407,9 @@ export class BaseDriverCore extends DriverCore {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This ensures that all of the mixins correctly implement the interface described in {@linkcode Driver}.
|
||||
* @template {Constraints} [C={}]
|
||||
* @implements {Driver<C>}
|
||||
*/
|
||||
export class BaseDriver extends createBaseDriverClass(BaseDriverCore) {}
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
export * from './commands';
|
||||
|
||||
export default BaseDriver;
|
||||
|
||||
/**
|
||||
@@ -433,11 +430,11 @@ export default BaseDriver;
|
||||
*/
|
||||
|
||||
/**
|
||||
* This is used to extend {@linkcode BaseDriverCore} by the mixins and also external drivers.
|
||||
* This is used to extend {@linkcode BaseDriver} by the mixins and also external drivers.
|
||||
* @template {Constraints} C
|
||||
* @template [Proto={}]
|
||||
* @template [Static={}]
|
||||
* @typedef {import('@appium/types').Class<BaseDriverCore<C> & Proto,import('@appium/types').DriverStatic & Static>} BaseDriverBase
|
||||
* @typedef {import('@appium/types').Class<BaseDriver<C> & Proto,import('@appium/types').DriverStatic & Static>} BaseDriverBase
|
||||
*/
|
||||
|
||||
/**
|
||||
|
||||
@@ -52,17 +52,22 @@ process.on('exit', () => {
|
||||
}
|
||||
});
|
||||
|
||||
async function retrieveHeaders(link) {
|
||||
/**
|
||||
*
|
||||
* @param {string} url
|
||||
* @returns {Promise<import('axios').AxiosResponse['headers']>}
|
||||
*/
|
||||
async function retrieveHeaders(url) {
|
||||
try {
|
||||
return (
|
||||
await axios({
|
||||
url: link,
|
||||
url,
|
||||
method: 'HEAD',
|
||||
timeout: 5000,
|
||||
})
|
||||
).headers;
|
||||
} catch (e) {
|
||||
logger.info(`Cannot send HEAD request to '${link}'. Original error: ${e.message}`);
|
||||
logger.info(`Cannot send HEAD request to '${url}'. Original error: ${e.message}`);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
@@ -228,6 +233,7 @@ async function configureApp(app, options = /** @type {ConfigureAppOptions} */ ({
|
||||
let newApp = app;
|
||||
let shouldUnzipApp = false;
|
||||
let packageHash = null;
|
||||
/** @type {import('axios').AxiosResponse['headers']?} */
|
||||
let headers = null;
|
||||
/** @type {RemoteAppProps} */
|
||||
const remoteAppProps = {
|
||||
|
||||
@@ -35,6 +35,7 @@ export {
|
||||
isStandardCap,
|
||||
validateCaps,
|
||||
promoteAppiumOptions,
|
||||
promoteAppiumOptionsForObject,
|
||||
} from './basedriver/capabilities';
|
||||
|
||||
// Web socket helpers
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@appium/base-driver",
|
||||
"version": "9.3.1",
|
||||
"version": "9.3.3",
|
||||
"description": "Base driver class for Appium drivers",
|
||||
"keywords": [
|
||||
"automation",
|
||||
@@ -43,8 +43,8 @@
|
||||
"test:unit": "mocha \"./test/unit/**/*.spec.js\""
|
||||
},
|
||||
"dependencies": {
|
||||
"@appium/support": "^3.1.5",
|
||||
"@appium/types": "^0.9.1",
|
||||
"@appium/support": "^3.1.7",
|
||||
"@appium/types": "^0.10.1",
|
||||
"@colors/colors": "1.5.0",
|
||||
"@types/async-lock": "1.4.0",
|
||||
"@types/bluebird": "3.5.38",
|
||||
@@ -53,19 +53,19 @@
|
||||
"@types/serve-favicon": "2.5.3",
|
||||
"async-lock": "1.4.0",
|
||||
"asyncbox": "2.9.4",
|
||||
"axios": "1.2.3",
|
||||
"axios": "1.3.4",
|
||||
"bluebird": "3.7.2",
|
||||
"body-parser": "1.20.1",
|
||||
"body-parser": "1.20.2",
|
||||
"es6-error": "4.1.1",
|
||||
"express": "4.18.2",
|
||||
"http-status-codes": "2.2.0",
|
||||
"lodash": "4.17.21",
|
||||
"lru-cache": "7.14.1",
|
||||
"lru-cache": "7.18.3",
|
||||
"method-override": "3.0.0",
|
||||
"morgan": "1.10.0",
|
||||
"serve-favicon": "2.5.0",
|
||||
"source-map-support": "0.5.21",
|
||||
"type-fest": "3.5.6",
|
||||
"type-fest": "3.6.1",
|
||||
"validate.js": "0.13.1"
|
||||
},
|
||||
"engines": {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// @ts-check
|
||||
import {LogMixin} from '../../../../lib/basedriver/commands/log';
|
||||
import {createSandbox} from 'sinon';
|
||||
import _ from 'lodash';
|
||||
import BaseDriver from '../../../../lib';
|
||||
|
||||
const expect = chai.expect;
|
||||
|
||||
@@ -20,25 +20,15 @@ const SUPPORTED_LOG_TYPES = {
|
||||
|
||||
describe('log commands -', function () {
|
||||
let sandbox;
|
||||
/** @type {ReturnType<typeof LogMixin>} */
|
||||
let LogCommands;
|
||||
|
||||
let logCommands;
|
||||
let driver;
|
||||
|
||||
beforeEach(function () {
|
||||
sandbox = createSandbox();
|
||||
// @ts-expect-error
|
||||
LogCommands = LogMixin(
|
||||
class {
|
||||
get log() {
|
||||
return this._log;
|
||||
}
|
||||
}
|
||||
);
|
||||
logCommands = new LogCommands();
|
||||
driver = new BaseDriver();
|
||||
// reset the supported log types
|
||||
logCommands.supportedLogTypes = {};
|
||||
logCommands._log = /** @type {import('@appium/types').AppiumLogger} */ ({
|
||||
driver.supportedLogTypes = {};
|
||||
driver._log = /** @type {import('@appium/types').AppiumLogger} */ ({
|
||||
debug: _.noop,
|
||||
});
|
||||
});
|
||||
@@ -49,11 +39,11 @@ describe('log commands -', function () {
|
||||
|
||||
describe('getLogTypes', function () {
|
||||
it('should return empty array when no supported log types', async function () {
|
||||
(await logCommands.getLogTypes()).should.eql([]);
|
||||
(await driver.getLogTypes()).should.eql([]);
|
||||
});
|
||||
it('should return keys to log type object', async function () {
|
||||
logCommands.supportedLogTypes = SUPPORTED_LOG_TYPES;
|
||||
(await logCommands.getLogTypes()).should.eql(['one', 'two']);
|
||||
driver.supportedLogTypes = SUPPORTED_LOG_TYPES;
|
||||
(await driver.getLogTypes()).should.eql(['one', 'two']);
|
||||
});
|
||||
});
|
||||
describe('getLog', function () {
|
||||
@@ -66,15 +56,15 @@ describe('log commands -', function () {
|
||||
two = sandbox.spy(SUPPORTED_LOG_TYPES.two, 'getter');
|
||||
});
|
||||
it('should throw error if log type not supported', async function () {
|
||||
await logCommands.getLog('one').should.eventually.be.rejected;
|
||||
await driver.getLog('one').should.eventually.be.rejected;
|
||||
one.called.should.be.false;
|
||||
two.called.should.be.false;
|
||||
});
|
||||
it('should throw an error with available log types if log type not supported', async function () {
|
||||
logCommands.supportedLogTypes = SUPPORTED_LOG_TYPES;
|
||||
driver.supportedLogTypes = SUPPORTED_LOG_TYPES;
|
||||
let err;
|
||||
try {
|
||||
await logCommands.getLog('three');
|
||||
await driver.getLog('three');
|
||||
} catch (_err) {
|
||||
err = _err;
|
||||
}
|
||||
@@ -86,8 +76,8 @@ describe('log commands -', function () {
|
||||
two.called.should.be.false;
|
||||
});
|
||||
it('should call getter on appropriate log when found', async function () {
|
||||
logCommands.supportedLogTypes = SUPPORTED_LOG_TYPES;
|
||||
let logs = await logCommands.getLog('one');
|
||||
driver.supportedLogTypes = SUPPORTED_LOG_TYPES;
|
||||
let logs = await driver.getLog('one');
|
||||
logs.should.eql(FIRST_LOGS);
|
||||
one.called.should.be.true;
|
||||
two.called.should.be.false;
|
||||
|
||||
@@ -3,6 +3,22 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [2.2.3](https://github.com/appium/appium/compare/@appium/base-plugin@2.2.2...@appium/base-plugin@2.2.3) (2023-03-08)
|
||||
|
||||
**Note:** Version bump only for package @appium/base-plugin
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.2.2](https://github.com/appium/appium/compare/@appium/base-plugin@2.2.1...@appium/base-plugin@2.2.2) (2023-02-24)
|
||||
|
||||
**Note:** Version bump only for package @appium/base-plugin
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.2.1](https://github.com/appium/appium/compare/@appium/base-plugin@2.2.0...@appium/base-plugin@2.2.1) (2023-02-09)
|
||||
|
||||
**Note:** Version bump only for package @appium/base-plugin
|
||||
|
||||
@@ -56,26 +56,26 @@ class BasePlugin {
|
||||
* `next` and `driver` objects since naturally we'd want to make sure to trigger the driver's own
|
||||
* `executeMethod` call if an execute method is not found on the plugin itself.
|
||||
*
|
||||
* @template {Driver} D
|
||||
* @template {Constraints} C
|
||||
* @param {NextPluginCallback} next
|
||||
* @param {D} driver
|
||||
* @param {Driver<C>} driver
|
||||
* @param {string} script
|
||||
* @param {[Record<string, any>]|[]} protoArgs
|
||||
* @this {Plugin}
|
||||
*/
|
||||
async executeMethod(next, driver, script, protoArgs) {
|
||||
const Plugin = /** @type {PluginClass} */ (this.constructor);
|
||||
const Plugin = /** @type {import('@appium/types').PluginClass<Plugin>} */ (this.constructor);
|
||||
const commandMetadata = {...Plugin.executeMethodMap?.[script]};
|
||||
|
||||
/** @type {import('@appium/types').PluginCommand<D>|undefined} */
|
||||
let command;
|
||||
|
||||
if (!commandMetadata.command || !(command = this[commandMetadata.command])) {
|
||||
if (!commandMetadata.command || !(commandMetadata.command in this)) {
|
||||
this.logger.info(
|
||||
`Plugin did not know how to handle method '${script}'. Passing control to next`
|
||||
);
|
||||
return await next();
|
||||
}
|
||||
|
||||
const command = /** @type {import('@appium/types').PluginCommand<Driver<C>>} */ (
|
||||
this[commandMetadata.command]
|
||||
);
|
||||
const args = validateExecuteMethodParams(protoArgs, commandMetadata.params);
|
||||
return await command.call(this, next, driver, ...args);
|
||||
}
|
||||
@@ -87,6 +87,10 @@ export {BasePlugin};
|
||||
/**
|
||||
* @typedef {import('@appium/types').Plugin} Plugin
|
||||
* @typedef {import('@appium/types').NextPluginCallback} NextPluginCallback
|
||||
* @typedef {import('@appium/types').Driver} Driver
|
||||
* @typedef {import('@appium/types').PluginClass} PluginClass
|
||||
* @typedef {import('@appium/types').Constraints} Constraints
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template {Constraints} C
|
||||
* @typedef {import('@appium/types').Driver<C>} Driver
|
||||
*/
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@appium/base-plugin",
|
||||
"version": "2.2.1",
|
||||
"version": "2.2.3",
|
||||
"description": "The base plugin used to create Appium 2.0 plugins",
|
||||
"keywords": [
|
||||
"automation",
|
||||
@@ -40,8 +40,8 @@
|
||||
"test:unit": "mocha \"./test/unit/**/*.spec.js\""
|
||||
},
|
||||
"dependencies": {
|
||||
"@appium/base-driver": "^9.3.1",
|
||||
"@appium/support": "^3.1.5"
|
||||
"@appium/base-driver": "^9.3.3",
|
||||
"@appium/support": "^3.1.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.17.0 || ^16.13.0 || >=18.0.0",
|
||||
|
||||
@@ -3,6 +3,26 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [2.0.9](https://github.com/appium/appium/compare/@appium/doctor@2.0.8...@appium/doctor@2.0.9) (2023-03-08)
|
||||
|
||||
**Note:** Version bump only for package @appium/doctor
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.0.8](https://github.com/appium/appium/compare/@appium/doctor@2.0.7...@appium/doctor@2.0.8) (2023-02-24)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **docutils:** update dependency yargs to v17.7.0 ([70d65b7](https://github.com/appium/appium/commit/70d65b7d6230dd415f26aaba6d097a3cbe876b47))
|
||||
* **docutils:** update dependency yargs to v17.7.1 ([2602016](https://github.com/appium/appium/commit/2602016eb38c73d2d9a40f94e95277faf2444305))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.0.7](https://github.com/appium/appium/compare/@appium/doctor@2.0.6...@appium/doctor@2.0.7) (2023-02-09)
|
||||
|
||||
**Note:** Version bump only for package @appium/doctor
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@appium/doctor",
|
||||
"version": "2.0.7",
|
||||
"version": "2.0.9",
|
||||
"description": "Test environment for fitness to run Appium",
|
||||
"keywords": [
|
||||
"automation",
|
||||
@@ -44,7 +44,7 @@
|
||||
"test:unit": "mocha \"./test/unit/**/*.spec.js\""
|
||||
},
|
||||
"dependencies": {
|
||||
"@appium/support": "^3.1.5",
|
||||
"@appium/support": "^3.1.7",
|
||||
"@colors/colors": "1.5.0",
|
||||
"@types/bluebird": "3.5.38",
|
||||
"@types/teen_process": "2.0.0",
|
||||
@@ -54,7 +54,7 @@
|
||||
"lodash": "4.17.21",
|
||||
"source-map-support": "0.5.21",
|
||||
"teen_process": "2.0.2",
|
||||
"yargs": "17.6.2"
|
||||
"yargs": "17.7.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.17.0 || ^16.13.0 || >=18.0.0",
|
||||
|
||||
@@ -3,6 +3,55 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [0.3.0](https://github.com/appium/appium/compare/@appium/docutils@0.2.2...@appium/docutils@0.3.0) (2023-03-08)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **docutils:** update dependency lilconfig to v2.1.0 ([4ed745a](https://github.com/appium/appium/commit/4ed745a95ffc6a43f76003eba62019c16a4c6cf2))
|
||||
* **docutils:** update dependency typedoc to v0.23.26 ([431092d](https://github.com/appium/appium/commit/431092dc87c3146bfd3cd274ae5c279b7582ff55))
|
||||
* **docutils:** use material theme ([189e39d](https://github.com/appium/appium/commit/189e39d03c16551272d6c489aedc0d9fbb08a3a9))
|
||||
* **types:** update dependency type-fest to v3.6.1 ([471a4b5](https://github.com/appium/appium/commit/471a4b57e622ff077d59f577a78341268700c48d))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **docutils:** add --upgrade flag to init ([4cfad00](https://github.com/appium/appium/commit/4cfad004dbdff4ae04ed7c87f23a3c9e913e4d4e))
|
||||
* **docutils:** allow "%s" as version number in commit msg arg ([f80f4cb](https://github.com/appium/appium/commit/f80f4cb2bca40347913a6ce39e3de58b485492a8))
|
||||
* **docutils:** better path validation ([b30b2cf](https://github.com/appium/appium/commit/b30b2cfb84b0f208d932d60082847d0d7323e108))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.2.2](https://github.com/appium/appium/compare/@appium/docutils@0.2.1...@appium/docutils@0.2.2) (2023-02-24)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **docutils:** fix typescript version ([6b8eb2c](https://github.com/appium/appium/commit/6b8eb2cbfec39185d8b81e2f4e2ce3f646ffd65f))
|
||||
* **docutils:** update dependency typedoc to v0.23.25 ([a4c58cc](https://github.com/appium/appium/commit/a4c58cc5618a9c83de4c044bf8bc59f1ddb80122))
|
||||
* **docutils:** update dependency yargs to v17.7.0 ([70d65b7](https://github.com/appium/appium/commit/70d65b7d6230dd415f26aaba6d097a3cbe876b47))
|
||||
* **docutils:** update dependency yargs to v17.7.1 ([2602016](https://github.com/appium/appium/commit/2602016eb38c73d2d9a40f94e95277faf2444305))
|
||||
* **types:** update dependency type-fest to v3.5.7 ([b4416c5](https://github.com/appium/appium/commit/b4416c5c0f40200b36909a1fbb492d8c4a212108))
|
||||
* **types:** update dependency type-fest to v3.6.0 ([08a6f3a](https://github.com/appium/appium/commit/08a6f3a308c7ee162e992629888557b31e50a26e))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.2.1](https://github.com/appium/appium/compare/@appium/docutils@0.2.0...@appium/docutils@0.2.1) (2023-02-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **docutils:** fix bad offset in nav updates ([ab366f3](https://github.com/appium/appium/commit/ab366f3a1f9109f7bd75ae5ac32b862d5e6dd1bf))
|
||||
* **docutils:** fix bad option name and ignore most falsy args ([5446e5c](https://github.com/appium/appium/commit/5446e5c7a755be081f46f1ed1ca8c13665d9a772))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [0.2.0](https://github.com/appium/appium/compare/@appium/docutils@0.1.6...@appium/docutils@0.2.0) (2023-02-09)
|
||||
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
*/
|
||||
|
||||
import _ from 'lodash';
|
||||
import {exec, SubProcess, TeenProcessExecOptions} from 'teen_process';
|
||||
import {exec, SubProcess, SubProcessOptions, TeenProcessExecOptions} from 'teen_process';
|
||||
import path from 'node:path';
|
||||
import {
|
||||
DEFAULT_DEPLOY_BRANCH,
|
||||
@@ -35,8 +35,9 @@ async function doServe(
|
||||
) {
|
||||
mikePath = mikePath ?? (await whichMike());
|
||||
const finalArgs = ['serve', ...args];
|
||||
log.debug('Launching %s with args: %O', mikePath, finalArgs);
|
||||
const proc = new SubProcess(mikePath, finalArgs);
|
||||
const opts: SubProcessOptions = {stdio: 'inherit'};
|
||||
log.debug('Launching %s with args: %s', mikePath, finalArgs);
|
||||
const proc = new SubProcess(mikePath, finalArgs, opts);
|
||||
return await proc.start(startDetector, detach, timeoutMs);
|
||||
}
|
||||
|
||||
@@ -74,7 +75,7 @@ async function findDeployVersion(packageJsonPath?: string, cwd = process.cwd()):
|
||||
* @param opts Options
|
||||
*/
|
||||
export async function deploy({
|
||||
mkDocsYml: mkDocsYmlPath,
|
||||
mkdocsYml: mkDocsYmlPath,
|
||||
packageJson: packageJsonPath,
|
||||
deployVersion: version,
|
||||
cwd = process.cwd(),
|
||||
@@ -99,6 +100,10 @@ export async function deploy({
|
||||
);
|
||||
}
|
||||
version = version ?? (await findDeployVersion(packageJsonPath, cwd));
|
||||
|
||||
// substitute %s in message with version
|
||||
message = message?.replace('%s', version);
|
||||
|
||||
const mikeOpts = {
|
||||
'config-file': mkDocsYmlPath,
|
||||
push,
|
||||
@@ -111,16 +116,23 @@ export async function deploy({
|
||||
host,
|
||||
};
|
||||
if (serve) {
|
||||
const mikeArgs = [...argify(_.pickBy(mikeOpts, Boolean)), version];
|
||||
const mikeArgs = [
|
||||
...argify(_.pickBy(mikeOpts, (value) => _.isNumber(value) || Boolean(value))),
|
||||
version,
|
||||
];
|
||||
if (alias) {
|
||||
mikeArgs.push(alias);
|
||||
}
|
||||
stop(); // discard
|
||||
// unsure about how SIGHUP is handled here
|
||||
await doServe(mikeArgs, serveOpts);
|
||||
} else {
|
||||
const mikeArgs = [
|
||||
...argify(
|
||||
_.omitBy(mikeOpts, (value, key) => _.includes(['port', 'host'], key) || value === false)
|
||||
_.omitBy(
|
||||
mikeOpts,
|
||||
(value, key) => _.includes(['port', 'host'], key) || (!_.isNumber(value) && !value)
|
||||
)
|
||||
),
|
||||
version,
|
||||
];
|
||||
@@ -140,7 +152,7 @@ export interface DeployOpts {
|
||||
/**
|
||||
* Path to `mike.yml`
|
||||
*/
|
||||
mkDocsYml?: string;
|
||||
mkdocsYml?: string;
|
||||
|
||||
/**
|
||||
* Current working directory
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
/**
|
||||
* Exports APIs used by build-related commands
|
||||
* @module
|
||||
*/
|
||||
|
||||
export * from './deploy';
|
||||
export * from './site';
|
||||
export * from './reference';
|
||||
|
||||
@@ -170,7 +170,6 @@ function getRootHeaderKeypath(headerItems: ParsedNavData[], header: string) {
|
||||
* @param mkDocsDocsDir Configured `docs_dir` or via options
|
||||
* @param navData Nav data parsed by {@linkcode parseNav}
|
||||
* @param all If `true`, process all markdown files, not just commands
|
||||
* @returns
|
||||
*/
|
||||
async function getNavItemsForDir(
|
||||
dir: string,
|
||||
@@ -180,45 +179,52 @@ async function getNavItemsForDir(
|
||||
all = false
|
||||
) {
|
||||
let dataChanged = false;
|
||||
const newHeaderItems: Omit<ParsedNavData, 'name'>[] = [];
|
||||
const newNavHeaderItems: Omit<ParsedNavData, 'name'>[] = [];
|
||||
const referenceOutputFilepaths = await findRelativeMarkdownFiles(dir, mkDocsDocsDir);
|
||||
if (!referenceOutputFilepaths.length) {
|
||||
log.warn('No markdown files found in %s; did TypeDoc run?', dir);
|
||||
return {data: [], changed: false};
|
||||
}
|
||||
|
||||
const newRefFilepaths = await findRelativeMarkdownFiles(dir, mkDocsDocsDir);
|
||||
|
||||
const header = all ? _.startCase(path.basename(dir)) : DEFAULT_NAV_HEADER;
|
||||
|
||||
const headerItems = filterHeaderItems(navData, header);
|
||||
const navHeader = all ? _.startCase(path.basename(dir)) : DEFAULT_NAV_HEADER;
|
||||
const navHeaderItems = filterHeaderItems(navData, navHeader);
|
||||
|
||||
// if we found items with this header already, we are going
|
||||
// to replace them all wholesale
|
||||
if (headerItems.length) {
|
||||
if (navHeaderItems.length) {
|
||||
log.debug('Found %d item(s) in header %s', navHeaderItems.length, navHeader);
|
||||
// we append indices or names to this keypath
|
||||
const rootHeaderKeypath = getRootHeaderKeypath(headerItems, header);
|
||||
const rootHeaderKeypath = getRootHeaderKeypath(navHeaderItems, navHeader);
|
||||
|
||||
for (const [offset, fileOrUrl] of newRefFilepaths.entries()) {
|
||||
const data = headerItems.find((item) => item.fileOrUrl === fileOrUrl);
|
||||
const keypath = getKeypathForHeaderItem(rootHeaderKeypath, offset, data);
|
||||
newHeaderItems.push({keypath, fileOrUrl});
|
||||
for (const fileOrUrl of referenceOutputFilepaths) {
|
||||
const offset = navHeaderItems.findIndex((item) => item.fileOrUrl === fileOrUrl);
|
||||
const newOffset = offset >= 0 ? offset : navHeaderItems.length;
|
||||
const data = navHeaderItems[offset];
|
||||
log.warn(rootHeaderKeypath, newOffset, data);
|
||||
const keypath = getKeypathForHeaderItem(rootHeaderKeypath, newOffset, data);
|
||||
newNavHeaderItems.push({keypath, fileOrUrl});
|
||||
}
|
||||
|
||||
// look for any differences between what we have and what's in the file
|
||||
if (navDataDidChange(newHeaderItems, headerItems)) {
|
||||
log.debug('Will write new nav data for header %s', header);
|
||||
if (navDataDidChange(newNavHeaderItems, navHeaderItems)) {
|
||||
log.debug('Will write new nav data for header %s: %O', navHeader, newNavHeaderItems);
|
||||
dataChanged = true;
|
||||
} else {
|
||||
log.debug('No changes for header %s', header);
|
||||
log.debug('No changes for header %s', navHeader);
|
||||
}
|
||||
} else {
|
||||
let navOffset = nav.length;
|
||||
for (const [idx, newRefFilepath] of newRefFilepaths.entries()) {
|
||||
newHeaderItems.push({
|
||||
keypath: `${navOffset}.${header}.${idx}`,
|
||||
log.debug('No items found in header %s', navHeader);
|
||||
const navOffset = nav.length;
|
||||
for (const [idx, newRefFilepath] of referenceOutputFilepaths.entries()) {
|
||||
newNavHeaderItems.push({
|
||||
keypath: `${navOffset}.${navHeader}.${idx}`,
|
||||
fileOrUrl: newRefFilepath,
|
||||
});
|
||||
}
|
||||
log.debug('Will create nav data for header %s', header);
|
||||
log.debug('Will create nav data for header %s', navHeader);
|
||||
dataChanged = true;
|
||||
}
|
||||
return {data: newHeaderItems, changed: dataChanged};
|
||||
return {data: newNavHeaderItems, changed: dataChanged};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -248,7 +254,6 @@ export async function updateNav({
|
||||
mkdocsYml: mkDocsYmlPath,
|
||||
typedocJson: typeDocJsonPath,
|
||||
all = false,
|
||||
dryRun = false,
|
||||
}: UpdateNavOpts = {}) {
|
||||
// we need `mkdocs.yml` to update
|
||||
// and we need `typedoc.json` to know where to look for the command docs
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
*/
|
||||
|
||||
import {fs} from '@appium/support';
|
||||
import glob from 'glob';
|
||||
import _ from 'lodash';
|
||||
import path from 'node:path';
|
||||
import {Application, ArgumentsReader, TypeDocOptions, TypeDocReader} from 'typedoc';
|
||||
@@ -16,28 +15,12 @@ import {
|
||||
NAME_TYPEDOC_JSON,
|
||||
} from '../constants';
|
||||
import {DocutilsError} from '../error';
|
||||
import {findPkgDir, findTypeDocJsonPath, readTypedocJson} from '../fs';
|
||||
import {findTypeDocJsonPath, readTypedocJson} from '../fs';
|
||||
import logger from '../logger';
|
||||
import {argify, relative, stopwatch} from '../util';
|
||||
|
||||
const log = logger.withTag('builder:reference');
|
||||
|
||||
/**
|
||||
* Replaces TypeDoc's homebrew "glob" implementation with a real one
|
||||
*
|
||||
* This cannot be done via `require('typedoc')` or `import` due to the file being excluded
|
||||
* from the export map in its `package.json`.
|
||||
* @see https://github.com/TypeStrong/typedoc/issues/2151
|
||||
*/
|
||||
const monkeyPatchTypedoc = _.once(async () => {
|
||||
const typedocDir = await findPkgDir(require.resolve('typedoc'));
|
||||
if (!typedocDir) {
|
||||
throw new DocutilsError('Could not find TypeDoc package directory; is it installed?');
|
||||
}
|
||||
const tdFs = require(path.join(typedocDir, 'dist', 'lib', 'utils', 'fs.js'));
|
||||
tdFs.glob = glob.sync;
|
||||
});
|
||||
|
||||
/**
|
||||
* Executes TypeDoc _in the current process_
|
||||
*
|
||||
@@ -48,9 +31,6 @@ const monkeyPatchTypedoc = _.once(async () => {
|
||||
* @param opts - TypeDoc options
|
||||
*/
|
||||
export async function runTypedoc(typeDocJsonPath: string, opts: Record<string, string>) {
|
||||
await monkeyPatchTypedoc();
|
||||
log.debug('Monkeypatched TypeDoc');
|
||||
|
||||
const args = argify(opts);
|
||||
log.debug('TypeDoc args:', args);
|
||||
const app = new Application();
|
||||
@@ -179,7 +159,7 @@ export async function buildReferenceDocs({
|
||||
|
||||
try {
|
||||
await runTypedoc(typeDocJsonPath, extraTypedocOpts);
|
||||
let finalOut = (typeDocJson.out ?? out) as string;
|
||||
const finalOut = (typeDocJson.out ?? out) as string;
|
||||
log.success(
|
||||
'Reference docs built at %s (%dms)',
|
||||
path.isAbsolute(finalOut) ? relativePath(finalOut) : finalOut,
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
*/
|
||||
|
||||
import path from 'node:path';
|
||||
import {exec, SubProcess, TeenProcessExecOptions} from 'teen_process';
|
||||
import {NAME_BIN, NAME_MKDOCS, NAME_MKDOCS_YML} from '../constants';
|
||||
import {exec, SubProcess, SubProcessOptions, TeenProcessExecOptions} from 'teen_process';
|
||||
import {NAME_BIN, NAME_THEME, NAME_MKDOCS_YML} from '../constants';
|
||||
import {DocutilsError} from '../error';
|
||||
import {findMkDocsYml, readMkDocsYml, whichMkDocs} from '../fs';
|
||||
import logger from '../logger';
|
||||
@@ -28,8 +28,9 @@ async function doServe(
|
||||
) {
|
||||
mkDocsPath = mkDocsPath ?? (await whichMkDocs());
|
||||
const finalArgs = ['serve', ...args];
|
||||
log.debug('Launching %s with args: %O', mkDocsPath, finalArgs);
|
||||
const proc = new SubProcess(mkDocsPath, finalArgs);
|
||||
const opts: SubProcessOptions = {stdio: 'inherit'};
|
||||
log.debug('Launching %s with args: %s', mkDocsPath, finalArgs);
|
||||
const proc = new SubProcess(mkDocsPath, finalArgs, opts);
|
||||
return await proc.start(startDetector, detach, timeoutMs);
|
||||
}
|
||||
|
||||
@@ -57,7 +58,7 @@ async function doBuild(
|
||||
export async function buildSite({
|
||||
mkdocsYml: mkDocsYmlPath,
|
||||
siteDir,
|
||||
theme = NAME_MKDOCS,
|
||||
theme = NAME_THEME,
|
||||
cwd = process.cwd(),
|
||||
serve = false,
|
||||
serveOpts,
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
* Provides functions to validate user-provided options as part of Yargs' post-parsing "check" phase
|
||||
* @module
|
||||
*/
|
||||
|
||||
import {fs, util} from '@appium/support';
|
||||
import _ from 'lodash';
|
||||
import type {Options} from 'yargs';
|
||||
import logger from '../logger';
|
||||
|
||||
const log = logger.withTag('check');
|
||||
|
||||
/**
|
||||
* Given a list of objects with `id` and `path` props, filters out the ones that do not exist
|
||||
* @param paths Filepaths
|
||||
* @returns Missing files
|
||||
*/
|
||||
async function filterMissing(paths: MissingFileData[]): Promise<MissingFileData[]> {
|
||||
const exists = await Promise.all(
|
||||
paths.map(async ({id, path}) => ({id, path, exists: await fs.exists(path)}))
|
||||
);
|
||||
const results = _.reject(exists, 'exists');
|
||||
return _.map(results, (result) => _.omit(result, 'exists'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Data structure describing a missing file; returned by {@linkcode filterMissing}
|
||||
*/
|
||||
interface MissingFileData {
|
||||
/**
|
||||
* Arbitrary identifier; intent was to map to an option name
|
||||
*/
|
||||
id: string;
|
||||
/**
|
||||
* Normalized filepath
|
||||
*/
|
||||
path: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes user-provided paths and checks for existence.
|
||||
*
|
||||
* Filters options to consider based on group name only.
|
||||
*
|
||||
* Meant to be used as a "fail-fast" strategy on the CLI, so we don't go all the way through some
|
||||
* expensive behavior before realizing we're missing a path.
|
||||
* @param opts Options object for a yargs command
|
||||
* @param group Group name to filter on
|
||||
* @param argv User-provided args
|
||||
* @returns `true` if all paths exist or otherwise an error message
|
||||
*/
|
||||
export async function checkMissingPaths<T extends Record<string, Options>>(
|
||||
opts: T,
|
||||
group: string,
|
||||
argv: Record<string, unknown>
|
||||
): Promise<true | string> {
|
||||
const argsToCheck = _.keys(
|
||||
_.pickBy(opts, (opt: Options, id: string) => opt?.group === group && id in argv) as Partial<T>
|
||||
);
|
||||
|
||||
// yargs is pretty loose about allowing CLI args multiple times, and the value could potentially
|
||||
// be a `string[]` instead of `string`; we don't want to allow more than one path per arg.
|
||||
if (!argsToCheck.every((id) => _.isString(argv[id]))) {
|
||||
return 'Paths may only be provided once each';
|
||||
}
|
||||
|
||||
const pathsToCheck: MissingFileData[] = _.map(argsToCheck, (id) => ({
|
||||
id,
|
||||
path: String(argv[id]), // this must be a string per the above check
|
||||
}));
|
||||
|
||||
log.debug(
|
||||
'Checking for existence of %s: %s',
|
||||
util.pluralize('path', pathsToCheck.length),
|
||||
_.map(pathsToCheck, 'path')
|
||||
);
|
||||
|
||||
const missingPaths = await filterMissing(pathsToCheck);
|
||||
|
||||
if (missingPaths.length) {
|
||||
return missingPaths
|
||||
.map(({id, path}) => `Path specified using --${_.kebabCase(id)} (${path}) does not exist`)
|
||||
.join('\n');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1,53 +1,64 @@
|
||||
import {CommandModule, InferredOptionTypes, Options} from 'yargs';
|
||||
/**
|
||||
* Yargs command module for the `build` command.
|
||||
* @module
|
||||
*/
|
||||
|
||||
import path from 'node:path';
|
||||
import type {CommandModule, InferredOptionTypes, Options} from 'yargs';
|
||||
import {buildReferenceDocs, buildSite, deploy, updateNav} from '../../builder';
|
||||
import {NAME_BIN} from '../../constants';
|
||||
import logger from '../../logger';
|
||||
import {stopwatch} from '../../util';
|
||||
import {checkMissingPaths} from '../check';
|
||||
|
||||
const log = logger.withTag('build');
|
||||
|
||||
const NAME_GROUP_BUILD = 'Build Options:';
|
||||
const NAME_GROUP_DEPLOY = 'Deployment Options:';
|
||||
const NAME_GROUP_SERVE = 'Serve Options:';
|
||||
const NAME_GROUP_BUILD_PATHS = 'Paths:';
|
||||
enum BuildCommandGroup {
|
||||
Build = 'Build Config:',
|
||||
Deploy = 'Deployment Config:',
|
||||
Serve = 'Dev Server Config:',
|
||||
BuildPaths = 'Custom Paths:',
|
||||
}
|
||||
|
||||
const opts = {
|
||||
reference: {
|
||||
describe: 'Run TypeDoc command API reference build (Markdown)',
|
||||
group: NAME_GROUP_BUILD,
|
||||
group: BuildCommandGroup.Build,
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
},
|
||||
site: {
|
||||
describe: 'Run MkDocs build (HTML)',
|
||||
group: NAME_GROUP_BUILD,
|
||||
group: BuildCommandGroup.Build,
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
},
|
||||
'site-dir': {
|
||||
alias: 'd',
|
||||
describe: 'HTML output directory',
|
||||
group: NAME_GROUP_BUILD_PATHS,
|
||||
group: BuildCommandGroup.Build,
|
||||
nargs: 1,
|
||||
requiresArg: true,
|
||||
type: 'string',
|
||||
normalize: true,
|
||||
coerce: path.resolve,
|
||||
implies: 'site',
|
||||
defaultDescription: '(from mkdocs.yml)',
|
||||
},
|
||||
'package-json': {
|
||||
defaultDescription: './package.json',
|
||||
describe: 'Path to package.json',
|
||||
group: NAME_GROUP_BUILD_PATHS,
|
||||
group: BuildCommandGroup.BuildPaths,
|
||||
nargs: 1,
|
||||
normalize: true,
|
||||
coerce: path.resolve,
|
||||
requiresArg: true,
|
||||
type: 'string',
|
||||
},
|
||||
title: {
|
||||
defaultDescription: '(extension package name)',
|
||||
describe: 'Title of the API reference',
|
||||
group: NAME_GROUP_BUILD,
|
||||
group: BuildCommandGroup.Build,
|
||||
nargs: 1,
|
||||
requiresArg: true,
|
||||
type: 'string',
|
||||
@@ -55,45 +66,48 @@ const opts = {
|
||||
'tsconfig-json': {
|
||||
defaultDescription: './tsconfig.json',
|
||||
describe: 'Path to tsconfig.json',
|
||||
group: NAME_GROUP_BUILD_PATHS,
|
||||
group: BuildCommandGroup.BuildPaths,
|
||||
nargs: 1,
|
||||
normalize: true,
|
||||
requiresArg: true,
|
||||
coerce: path.resolve,
|
||||
type: 'string',
|
||||
},
|
||||
'mkdocs-yml': {
|
||||
defaultDescription: './mkdocs.yml',
|
||||
description: 'Path to mkdocs.yml',
|
||||
group: NAME_GROUP_BUILD_PATHS,
|
||||
group: BuildCommandGroup.BuildPaths,
|
||||
nargs: 1,
|
||||
normalize: true,
|
||||
requiresArg: true,
|
||||
coerce: path.resolve,
|
||||
type: 'string',
|
||||
},
|
||||
'typedoc-json': {
|
||||
defaultDescription: './typedoc.json',
|
||||
describe: 'Path to typedoc.json',
|
||||
group: NAME_GROUP_BUILD_PATHS,
|
||||
group: BuildCommandGroup.BuildPaths,
|
||||
nargs: 1,
|
||||
normalize: true,
|
||||
requiresArg: true,
|
||||
coerce: path.resolve,
|
||||
type: 'string',
|
||||
},
|
||||
all: {
|
||||
describe: 'Output all reference docs (not just Appium comands)',
|
||||
group: NAME_GROUP_BUILD,
|
||||
group: BuildCommandGroup.Build,
|
||||
implies: 'site',
|
||||
type: 'boolean',
|
||||
},
|
||||
deploy: {
|
||||
describe: 'Commit HTML output',
|
||||
group: NAME_GROUP_DEPLOY,
|
||||
describe: 'Commit HTML output to a branch using mike',
|
||||
group: BuildCommandGroup.Deploy,
|
||||
type: 'boolean',
|
||||
implies: 'site',
|
||||
},
|
||||
push: {
|
||||
describe: 'Push after deploy',
|
||||
group: NAME_GROUP_DEPLOY,
|
||||
group: BuildCommandGroup.Deploy,
|
||||
type: 'boolean',
|
||||
implies: 'deploy',
|
||||
},
|
||||
@@ -101,7 +115,7 @@ const opts = {
|
||||
alias: 'b',
|
||||
describe: 'Branch to commit to',
|
||||
implies: 'deploy',
|
||||
group: NAME_GROUP_DEPLOY,
|
||||
group: BuildCommandGroup.Deploy,
|
||||
type: 'string',
|
||||
requiresArg: true,
|
||||
nargs: 1,
|
||||
@@ -111,7 +125,7 @@ const opts = {
|
||||
alias: 'r',
|
||||
describe: 'Remote to push to',
|
||||
implies: ['deploy', 'push'],
|
||||
group: NAME_GROUP_DEPLOY,
|
||||
group: BuildCommandGroup.Deploy,
|
||||
type: 'string',
|
||||
requiresArg: true,
|
||||
nargs: 1,
|
||||
@@ -120,16 +134,16 @@ const opts = {
|
||||
prefix: {
|
||||
describe: 'Subdirectory within <branch> to commit to',
|
||||
implies: ['deploy', 'branch'],
|
||||
group: NAME_GROUP_DEPLOY,
|
||||
group: BuildCommandGroup.Deploy,
|
||||
type: 'string',
|
||||
nargs: 1,
|
||||
requiresArg: true,
|
||||
},
|
||||
message: {
|
||||
alias: 'm',
|
||||
describe: 'Commit message',
|
||||
describe: 'Commit message. Use "%s" for version placeholder',
|
||||
implies: 'deploy',
|
||||
group: NAME_GROUP_DEPLOY,
|
||||
group: BuildCommandGroup.Deploy,
|
||||
type: 'string',
|
||||
nargs: 1,
|
||||
requiresArg: true,
|
||||
@@ -137,7 +151,7 @@ const opts = {
|
||||
'deploy-version': {
|
||||
describe: 'Version (directory) to deploy build to',
|
||||
implies: 'deploy',
|
||||
group: NAME_GROUP_DEPLOY,
|
||||
group: BuildCommandGroup.Deploy,
|
||||
type: 'string',
|
||||
nargs: 1,
|
||||
requiresArg: true,
|
||||
@@ -146,7 +160,7 @@ const opts = {
|
||||
alias: {
|
||||
describe: 'Alias for the build (e.g., "latest"); triggers alias update',
|
||||
implies: 'deploy',
|
||||
group: NAME_GROUP_DEPLOY,
|
||||
group: BuildCommandGroup.Deploy,
|
||||
type: 'string',
|
||||
nargs: 1,
|
||||
requiresArg: true,
|
||||
@@ -155,18 +169,18 @@ const opts = {
|
||||
rebase: {
|
||||
describe: 'Rebase <branch> with remote before deploy',
|
||||
implies: ['deploy', 'branch', 'remote'],
|
||||
group: NAME_GROUP_DEPLOY,
|
||||
group: BuildCommandGroup.Deploy,
|
||||
type: 'boolean',
|
||||
},
|
||||
serve: {
|
||||
describe: 'Start development server',
|
||||
group: NAME_GROUP_SERVE,
|
||||
group: BuildCommandGroup.Serve,
|
||||
type: 'boolean',
|
||||
},
|
||||
port: {
|
||||
alias: 'p',
|
||||
describe: 'Development server port',
|
||||
group: NAME_GROUP_SERVE,
|
||||
group: BuildCommandGroup.Serve,
|
||||
type: 'number',
|
||||
defaultDescription: '8000',
|
||||
implies: 'serve',
|
||||
@@ -176,32 +190,35 @@ const opts = {
|
||||
host: {
|
||||
alias: 'h',
|
||||
describe: 'Development server host',
|
||||
group: NAME_GROUP_SERVE,
|
||||
group: BuildCommandGroup.Serve,
|
||||
type: 'string',
|
||||
nargs: 1,
|
||||
requiresArg: true,
|
||||
implies: 'serve',
|
||||
defaultDescription: 'localhost',
|
||||
},
|
||||
} as const;
|
||||
} as const satisfies Record<string, Options>;
|
||||
|
||||
opts as Record<string, Options>;
|
||||
type BuildOptions = InferredOptionTypes<typeof opts>;
|
||||
|
||||
const buildCommand: CommandModule<{}, BuildOptions> = {
|
||||
export default {
|
||||
command: 'build',
|
||||
describe: 'Build Appium extension documentation',
|
||||
builder: (yargs) =>
|
||||
yargs.options(opts).check((argv) => {
|
||||
// either this method doesn't provide camel-cased props, or the types are wrong.
|
||||
if (argv.deploy === true && argv['site-dir']) {
|
||||
log.error(
|
||||
`--site-dir is unsupported when running "${NAME_BIN} deploy"; use --prefix if needd, but remember that the default behavior is to deploy to the root of the branch (${argv.branch}) instead of a subdirectory`
|
||||
);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}),
|
||||
describe: 'Build Appium extension documentation using TypeDoc & MkDocs',
|
||||
builder(yargs) {
|
||||
return yargs
|
||||
.options(opts)
|
||||
.check(async (argv) => {
|
||||
// either this method doesn't provide camel-cased props, or the types are wrong.
|
||||
if (argv.deploy === true && argv['site-dir']) {
|
||||
return `--site-dir is unsupported when running "${NAME_BIN} deploy"; use --prefix if needed, but remember that the default behavior is to deploy to the root of the branch (${argv.branch}) instead of a subdirectory`;
|
||||
}
|
||||
|
||||
return await checkMissingPaths(opts, BuildCommandGroup.BuildPaths, argv);
|
||||
})
|
||||
.epilog(
|
||||
'For help with further configuration, see:\n - MkDocs: https://www.mkdocs.org\n - TypeDoc: https://typedoc.org\n - Mike: https://github.com/jimporter/mike'
|
||||
);
|
||||
},
|
||||
async handler(args) {
|
||||
const stop = stopwatch('build');
|
||||
log.debug('Build command called with args: %O', args);
|
||||
@@ -224,6 +241,4 @@ const buildCommand: CommandModule<{}, BuildOptions> = {
|
||||
}
|
||||
log.success('Done! (total: %dms)', stop());
|
||||
},
|
||||
};
|
||||
|
||||
export default buildCommand;
|
||||
} as CommandModule<object, BuildOptions>;
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
/**
|
||||
* Exports all command modules
|
||||
* @module
|
||||
*/
|
||||
|
||||
export {default as init} from './init';
|
||||
export {default as validate} from './validate';
|
||||
export {default as build} from './build';
|
||||
|
||||
@@ -1,19 +1,32 @@
|
||||
/**
|
||||
* Yargs command module for the `init` command.
|
||||
* @module
|
||||
*/
|
||||
|
||||
import _ from 'lodash';
|
||||
import {CommandModule, InferredOptionTypes, Options} from 'yargs';
|
||||
import type {CommandModule, InferredOptionTypes, Options} from 'yargs';
|
||||
import {init} from '../../init';
|
||||
import logger from '../../logger';
|
||||
import {stopwatch} from '../../util';
|
||||
import {checkMissingPaths} from '../check';
|
||||
|
||||
const log = logger.withTag('init');
|
||||
|
||||
const NAME_GROUP_INIT_MKDOCS = 'MkDocs Config:';
|
||||
const NAME_GROUP_INIT_PATHS = 'Paths:';
|
||||
const NAME_GROUP_INIT_BEHAVIOR = 'Initialization Behavior:';
|
||||
enum InitCommandGroup {
|
||||
MkDocs = 'MkDocs Config:',
|
||||
Paths = 'Custom Paths:',
|
||||
Behavior = 'Initialization Behavior:',
|
||||
}
|
||||
|
||||
/**
|
||||
* Note the groups here; _some_ opts are paths and would usually be checked via
|
||||
* {@linkcode checkMissingPaths}, but in this case we do not care if the path exists or not, because
|
||||
* we may create it.
|
||||
*/
|
||||
const opts = {
|
||||
copyright: {
|
||||
description: 'Copyright notice',
|
||||
group: NAME_GROUP_INIT_MKDOCS,
|
||||
group: InitCommandGroup.MkDocs,
|
||||
nargs: 1,
|
||||
requiresArg: true,
|
||||
type: 'string',
|
||||
@@ -22,19 +35,19 @@ const opts = {
|
||||
default: '.',
|
||||
defaultDescription: '(current directory)',
|
||||
description: 'Directory of package',
|
||||
group: NAME_GROUP_INIT_PATHS,
|
||||
group: InitCommandGroup.Paths,
|
||||
normalize: true,
|
||||
type: 'string',
|
||||
},
|
||||
'dry-run': {
|
||||
describe: 'Do not write any files; show what would be done',
|
||||
group: NAME_GROUP_INIT_BEHAVIOR,
|
||||
group: InitCommandGroup.Behavior,
|
||||
type: 'boolean',
|
||||
},
|
||||
force: {
|
||||
alias: 'f',
|
||||
describe: 'Overwrite existing configurations',
|
||||
group: NAME_GROUP_INIT_BEHAVIOR,
|
||||
group: InitCommandGroup.Behavior,
|
||||
type: 'boolean',
|
||||
},
|
||||
include: {
|
||||
@@ -43,19 +56,20 @@ const opts = {
|
||||
coerce: (value: string | string[]) => _.castArray(value),
|
||||
description: 'Files to include in compilation (globs OK)',
|
||||
nargs: 1,
|
||||
group: InitCommandGroup.MkDocs,
|
||||
requiresArg: true,
|
||||
type: 'string',
|
||||
},
|
||||
mkdocs: {
|
||||
default: true,
|
||||
description: 'Create mkdocs.yml if needed',
|
||||
group: NAME_GROUP_INIT_BEHAVIOR,
|
||||
group: InitCommandGroup.Behavior,
|
||||
type: 'boolean',
|
||||
},
|
||||
'mkdocs-yml': {
|
||||
defaultDescription: './mkdocs.yml',
|
||||
description: 'Path to mkdocs.yml',
|
||||
group: NAME_GROUP_INIT_PATHS,
|
||||
description: 'Path to new or existing mkdocs.yml',
|
||||
group: InitCommandGroup.MkDocs,
|
||||
nargs: 1,
|
||||
normalize: true,
|
||||
requiresArg: true,
|
||||
@@ -63,8 +77,8 @@ const opts = {
|
||||
},
|
||||
'package-json': {
|
||||
defaultDescription: './package.json',
|
||||
describe: 'Path to package.json',
|
||||
group: NAME_GROUP_INIT_PATHS,
|
||||
describe: 'Path to existing package.json',
|
||||
group: InitCommandGroup.Paths,
|
||||
nargs: 1,
|
||||
normalize: true,
|
||||
requiresArg: true,
|
||||
@@ -73,13 +87,13 @@ const opts = {
|
||||
python: {
|
||||
default: true,
|
||||
description: 'Install Python dependencies if needed',
|
||||
group: NAME_GROUP_INIT_BEHAVIOR,
|
||||
group: InitCommandGroup.Behavior,
|
||||
type: 'boolean',
|
||||
},
|
||||
'python-path': {
|
||||
defaultDescription: '(derived from shell)',
|
||||
description: 'Path to python 3 executable',
|
||||
group: NAME_GROUP_INIT_PATHS,
|
||||
group: InitCommandGroup.Paths,
|
||||
nargs: 1,
|
||||
normalize: true,
|
||||
requiresArg: true,
|
||||
@@ -88,7 +102,7 @@ const opts = {
|
||||
'repo-name': {
|
||||
defaultDescription: '(derived from --repo-url)',
|
||||
description: 'Name of extension repository',
|
||||
group: NAME_GROUP_INIT_MKDOCS,
|
||||
group: InitCommandGroup.MkDocs,
|
||||
nargs: 1,
|
||||
requiresArg: true,
|
||||
type: 'string',
|
||||
@@ -96,7 +110,7 @@ const opts = {
|
||||
'repo-url': {
|
||||
defaultDescription: '(from package.json)',
|
||||
description: 'URL of extension repository',
|
||||
group: NAME_GROUP_INIT_MKDOCS,
|
||||
group: InitCommandGroup.MkDocs,
|
||||
nargs: 1,
|
||||
requiresArg: true,
|
||||
type: 'string',
|
||||
@@ -104,7 +118,7 @@ const opts = {
|
||||
'site-description': {
|
||||
defaultDescription: '(from package.json)',
|
||||
description: 'Site description',
|
||||
group: NAME_GROUP_INIT_MKDOCS,
|
||||
group: InitCommandGroup.MkDocs,
|
||||
nargs: 1,
|
||||
requiresArg: true,
|
||||
type: 'string',
|
||||
@@ -112,15 +126,15 @@ const opts = {
|
||||
'site-name': {
|
||||
defaultDescription: '(extension package name)',
|
||||
description: 'Name of site',
|
||||
group: NAME_GROUP_INIT_MKDOCS,
|
||||
group: InitCommandGroup.MkDocs,
|
||||
nargs: 1,
|
||||
requiresArg: true,
|
||||
type: 'string',
|
||||
},
|
||||
'tsconfig-json': {
|
||||
defaultDescription: './tsconfig.json',
|
||||
describe: 'Path to tsconfig.json',
|
||||
group: NAME_GROUP_INIT_PATHS,
|
||||
describe: 'Path to new or existing tsconfig.json',
|
||||
group: InitCommandGroup.Behavior,
|
||||
nargs: 1,
|
||||
normalize: true,
|
||||
requiresArg: true,
|
||||
@@ -129,13 +143,13 @@ const opts = {
|
||||
typedoc: {
|
||||
default: true,
|
||||
description: 'Create typedoc.json if needed',
|
||||
group: NAME_GROUP_INIT_BEHAVIOR,
|
||||
group: InitCommandGroup.Behavior,
|
||||
type: 'boolean',
|
||||
},
|
||||
'typedoc-json': {
|
||||
defaultDescription: './typedoc.json',
|
||||
describe: 'Path to typedoc.json',
|
||||
group: NAME_GROUP_INIT_PATHS,
|
||||
describe: 'Path to new or existing typedoc.json',
|
||||
group: InitCommandGroup.Behavior,
|
||||
nargs: 1,
|
||||
normalize: true,
|
||||
requiresArg: true,
|
||||
@@ -144,23 +158,31 @@ const opts = {
|
||||
typescript: {
|
||||
default: true,
|
||||
description: 'Create tsconfig.json if needed',
|
||||
group: NAME_GROUP_INIT_BEHAVIOR,
|
||||
group: InitCommandGroup.Behavior,
|
||||
type: 'boolean',
|
||||
},
|
||||
} as const;
|
||||
opts as Record<string, Options>; // type check
|
||||
upgrade: {
|
||||
alias: 'up',
|
||||
describe: 'Only upgrade Python dependencies if out-of-date',
|
||||
group: InitCommandGroup.Behavior,
|
||||
type: 'boolean',
|
||||
conflicts: 'force',
|
||||
},
|
||||
} as const satisfies Record<string, Options>;
|
||||
|
||||
type InitOptions = InferredOptionTypes<typeof opts>;
|
||||
|
||||
const initCommand: CommandModule<{}, InitOptions> = {
|
||||
export default {
|
||||
command: 'init',
|
||||
describe: 'Initialize package for doc generation',
|
||||
builder: opts,
|
||||
builder(yargs) {
|
||||
return yargs
|
||||
.options(opts)
|
||||
.check(async (argv) => checkMissingPaths(opts, InitCommandGroup.Paths, argv));
|
||||
},
|
||||
async handler(args) {
|
||||
const done = stopwatch('init');
|
||||
await init({...args, overwrite: args.force, cwd: args.dir});
|
||||
log.success('Done (%dms)', done());
|
||||
},
|
||||
};
|
||||
|
||||
export default initCommand;
|
||||
} as CommandModule<object, InitOptions>;
|
||||
|
||||
@@ -1,25 +1,33 @@
|
||||
import pluralize from 'pluralize';
|
||||
import {CommandModule, InferredOptionTypes, Options} from 'yargs';
|
||||
/**
|
||||
* Yargs command module for the `validate` command.
|
||||
* @module
|
||||
*/
|
||||
|
||||
import {util} from '@appium/support';
|
||||
import type {CommandModule, InferredOptionTypes, Options} from 'yargs';
|
||||
import {DocutilsError} from '../../error';
|
||||
import {DocutilsValidator, ValidationKind} from '../../validate';
|
||||
import logger from '../../logger';
|
||||
import {checkMissingPaths} from '../check';
|
||||
|
||||
const log = logger.withTag('validate');
|
||||
|
||||
const NAME_GROUP_VALIDATE = 'Validation Behavior:';
|
||||
const NAME_GROUP_VALIDATE_PATHS = 'Paths:';
|
||||
enum ValidateCommandGroup {
|
||||
Behavior = 'Validation Behavior:',
|
||||
Paths = 'Custom Paths:',
|
||||
}
|
||||
|
||||
const opts = {
|
||||
mkdocs: {
|
||||
default: true,
|
||||
description: 'Validate MkDocs environment',
|
||||
group: NAME_GROUP_VALIDATE,
|
||||
group: ValidateCommandGroup.Behavior,
|
||||
type: 'boolean',
|
||||
},
|
||||
'mkdocs-yml': {
|
||||
defaultDescription: './mkdocs.yml',
|
||||
description: 'Path to mkdocs.yml',
|
||||
group: NAME_GROUP_VALIDATE_PATHS,
|
||||
group: ValidateCommandGroup.Paths,
|
||||
nargs: 1,
|
||||
normalize: true,
|
||||
requiresArg: true,
|
||||
@@ -28,7 +36,7 @@ const opts = {
|
||||
'npm-path': {
|
||||
defaultDescription: '(derived from shell)',
|
||||
description: 'Path to npm executable',
|
||||
group: NAME_GROUP_VALIDATE_PATHS,
|
||||
group: ValidateCommandGroup.Paths,
|
||||
nargs: 1,
|
||||
normalize: true,
|
||||
requiresArg: true,
|
||||
@@ -37,13 +45,13 @@ const opts = {
|
||||
python: {
|
||||
default: true,
|
||||
description: 'Validate Python 3 environment',
|
||||
group: NAME_GROUP_VALIDATE,
|
||||
group: ValidateCommandGroup.Behavior,
|
||||
type: 'boolean',
|
||||
},
|
||||
'python-path': {
|
||||
defaultDescription: '(derived from shell)',
|
||||
description: 'Path to python 3 executable',
|
||||
group: NAME_GROUP_VALIDATE_PATHS,
|
||||
group: ValidateCommandGroup.Paths,
|
||||
nargs: 1,
|
||||
normalize: true,
|
||||
requiresArg: true,
|
||||
@@ -52,7 +60,7 @@ const opts = {
|
||||
'tsconfig-json': {
|
||||
defaultDescription: './tsconfig.json',
|
||||
describe: 'Path to tsconfig.json',
|
||||
group: NAME_GROUP_VALIDATE_PATHS,
|
||||
group: ValidateCommandGroup.Paths,
|
||||
nargs: 1,
|
||||
normalize: true,
|
||||
requiresArg: true,
|
||||
@@ -61,13 +69,13 @@ const opts = {
|
||||
typedoc: {
|
||||
default: true,
|
||||
description: 'Validate TypoDoc environment',
|
||||
group: NAME_GROUP_VALIDATE,
|
||||
group: ValidateCommandGroup.Behavior,
|
||||
type: 'boolean',
|
||||
},
|
||||
'typedoc-json': {
|
||||
defaultDescription: './typedoc.json',
|
||||
describe: 'Path to typedoc.json',
|
||||
group: NAME_GROUP_VALIDATE_PATHS,
|
||||
group: ValidateCommandGroup.Paths,
|
||||
nargs: 1,
|
||||
normalize: true,
|
||||
requiresArg: true,
|
||||
@@ -76,24 +84,25 @@ const opts = {
|
||||
typescript: {
|
||||
default: true,
|
||||
description: 'Validate TypeScript environment',
|
||||
group: NAME_GROUP_VALIDATE,
|
||||
group: ValidateCommandGroup.Behavior,
|
||||
type: 'boolean',
|
||||
},
|
||||
} as const;
|
||||
opts as Record<string, Options>;
|
||||
} as const satisfies Record<string, Options>;
|
||||
|
||||
type ValidateOptions = InferredOptionTypes<typeof opts>;
|
||||
const validateCommand: CommandModule<{}, ValidateOptions> = {
|
||||
|
||||
export default {
|
||||
command: 'validate',
|
||||
describe: 'Validate Environment',
|
||||
builder: opts,
|
||||
builder(yargs) {
|
||||
return yargs.options(opts).check(async (argv) => {
|
||||
if (!argv.python && !argv.typedoc && !argv.typescript && !argv.mkdocs) {
|
||||
return 'No validation targets specified; one or more of --python, --typescript, --typedoc or --mkdocs must be provided';
|
||||
}
|
||||
return checkMissingPaths(opts, ValidateCommandGroup.Paths, argv);
|
||||
});
|
||||
},
|
||||
async handler(args) {
|
||||
if (!args.python && !args.typedoc && !args.typescript && !args.mkdocs) {
|
||||
// specifically not a DocutilsError
|
||||
throw new Error(
|
||||
'No validation targets specified; one or more of --python, --typescript, --typedoc or --mkdocs must be provided'
|
||||
);
|
||||
}
|
||||
|
||||
let errorCount = 0;
|
||||
const validator = new DocutilsValidator(args)
|
||||
.once(DocutilsValidator.BEGIN, (kinds: ValidationKind[]) => {
|
||||
@@ -113,10 +122,8 @@ const validateCommand: CommandModule<{}, ValidateOptions> = {
|
||||
|
||||
if (errorCount) {
|
||||
throw new DocutilsError(
|
||||
`Validation failed with ${errorCount} ${pluralize('error', errorCount)}`
|
||||
`Validation failed with ${errorCount} ${util.pluralize('error', errorCount)}`
|
||||
);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export default validateCommand;
|
||||
} as CommandModule<object, ValidateOptions>;
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
#!/usr/bin/env node
|
||||
/* eslint-disable no-console */
|
||||
|
||||
/**
|
||||
* Main CLI entry point for `@appium/docutils`
|
||||
* @module
|
||||
*/
|
||||
|
||||
import logger from '../logger';
|
||||
|
||||
import _ from 'lodash';
|
||||
@@ -8,6 +15,10 @@ import {DEFAULT_LOG_LEVEL, LogLevelMap, NAME_BIN} from '../constants';
|
||||
import {DocutilsError} from '../error';
|
||||
import {build, init, validate} from './command';
|
||||
import {findConfig} from './config';
|
||||
import {fs} from '@appium/support';
|
||||
import {sync as readPkg} from 'read-pkg';
|
||||
|
||||
const pkg = readPkg({cwd: fs.findRoot(__dirname)});
|
||||
|
||||
const log = logger.withTag('cli');
|
||||
export async function main(argv = hideBin(process.argv)) {
|
||||
@@ -45,7 +56,7 @@ export async function main(argv = hideBin(process.argv)) {
|
||||
describe: 'Disable config file discovery',
|
||||
},
|
||||
})
|
||||
.middleware(
|
||||
.middleware([
|
||||
/**
|
||||
* Configures logging; `--verbose` implies `--log-level=debug`
|
||||
*/
|
||||
@@ -55,8 +66,15 @@ export async function main(argv = hideBin(process.argv)) {
|
||||
log.debug('Debug logging enabled via --verbose');
|
||||
}
|
||||
log.level = LogLevelMap[argv.logLevel];
|
||||
}
|
||||
)
|
||||
},
|
||||
/**
|
||||
* Writes a startup message, if logging is enabled
|
||||
*/
|
||||
async () => {
|
||||
log.info(`${pkg.name} @ v${pkg.version} (Node.js ${process.version})`);
|
||||
},
|
||||
])
|
||||
.epilog(`Please report bugs at ${pkg.bugs?.url}`)
|
||||
.fail(
|
||||
/**
|
||||
* Custom failure handler so we can log nicely.
|
||||
@@ -67,6 +85,7 @@ export async function main(argv = hideBin(process.argv)) {
|
||||
log.error(error.message);
|
||||
} else {
|
||||
y.showHelp();
|
||||
console.log();
|
||||
log.error(msg ?? error.message);
|
||||
}
|
||||
y.exit(1, error);
|
||||
@@ -81,7 +100,7 @@ export async function main(argv = hideBin(process.argv)) {
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
// eslint-disable-next-line promise/prefer-await-to-then
|
||||
// eslint-disable-next-line promise/prefer-await-to-then, promise/prefer-await-to-callbacks
|
||||
main().catch((err) => {
|
||||
log.error('Caught otherwise-unhandled rejection (this is probably a bug):', err);
|
||||
});
|
||||
|
||||
@@ -86,6 +86,11 @@ export const NAME_ERR_ENOENT = 'ENOENT';
|
||||
*/
|
||||
export const NAME_ERR_EEXIST = 'EEXIST';
|
||||
|
||||
/**
|
||||
* Name of the default theme
|
||||
*/
|
||||
export const NAME_THEME = 'material';
|
||||
|
||||
/**
|
||||
* Default log level
|
||||
*/
|
||||
|
||||
@@ -1 +1,7 @@
|
||||
/**
|
||||
* A custom error class. This exists so we can use `instanceof` to differentiate between "expected"
|
||||
* exceptions and unexpected ones.
|
||||
* @module
|
||||
*/
|
||||
|
||||
export class DocutilsError extends Error {}
|
||||
|
||||
@@ -127,7 +127,10 @@ async function _readPkgJson(
|
||||
cwd: string,
|
||||
normalize: true
|
||||
): Promise<{pkgPath: string; pkg: NormalizedPackageJson}>;
|
||||
async function _readPkgJson(cwd: string): Promise<{pkgPath: string; pkg: PackageJson}>;
|
||||
async function _readPkgJson(
|
||||
cwd: string,
|
||||
normalize?: false
|
||||
): Promise<{pkgPath: string; pkg: PackageJson}>;
|
||||
async function _readPkgJson(
|
||||
cwd: string,
|
||||
normalize?: boolean
|
||||
@@ -236,14 +239,14 @@ export const whichMike = _.partial(cachedWhich, NAME_MIKE);
|
||||
*/
|
||||
export const readMkDocsYml = _.memoize(
|
||||
async (filepath: string, cwd = process.cwd()): Promise<MkDocsYml> => {
|
||||
let mkDocsYml = <MkDocsYml>await readYaml(filepath);
|
||||
let mkDocsYml = (await readYaml(filepath)) as MkDocsYml;
|
||||
if (mkDocsYml.site_dir) {
|
||||
mkDocsYml.site_dir = path.resolve(cwd, path.dirname(filepath), mkDocsYml.site_dir);
|
||||
}
|
||||
if (mkDocsYml.INHERIT) {
|
||||
let inheritPath: string | undefined = path.resolve(path.dirname(filepath), mkDocsYml.INHERIT);
|
||||
while (inheritPath) {
|
||||
const inheritYml = <MkDocsYml>await readYaml(inheritPath);
|
||||
const inheritYml = (await readYaml(inheritPath)) as MkDocsYml;
|
||||
if (inheritYml.site_dir) {
|
||||
inheritYml.site_dir = path.resolve(path.dirname(inheritPath), inheritYml.site_dir);
|
||||
log.debug('Resolved site_dir to %s', inheritYml.site_dir);
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
/**
|
||||
* Main entry point
|
||||
* @module
|
||||
*/
|
||||
|
||||
export * from './mike';
|
||||
export * from './builder';
|
||||
export * from './validate';
|
||||
|
||||
@@ -126,7 +126,9 @@ export const initMkDocs = createScaffoldTask<InitMkDocsOptions, MkDocsYml>(
|
||||
if (repoUrl && !repoName) {
|
||||
let {pathname} = new URL(repoUrl);
|
||||
pathname = pathname.slice(1);
|
||||
let [owner, repo] = pathname.split('/');
|
||||
const pathparts = pathname.split('/');
|
||||
const owner = pathparts[0];
|
||||
let repo = pathparts[1];
|
||||
repo = repo.replace(/\.git$/, '');
|
||||
repoName = [owner, repo].join('/');
|
||||
if (repoName) {
|
||||
@@ -158,8 +160,12 @@ export const initMkDocs = createScaffoldTask<InitMkDocsOptions, MkDocsYml>(
|
||||
export async function initPython({
|
||||
pythonPath = NAME_PYTHON,
|
||||
dryRun = false,
|
||||
upgrade = false,
|
||||
}: InitPythonOptions = {}): Promise<void> {
|
||||
const args = ['-m', 'pip', 'install', '-r', REQUIREMENTS_TXT_PATH];
|
||||
if (upgrade) {
|
||||
args.push('--upgrade');
|
||||
}
|
||||
if (dryRun) {
|
||||
dryRunLog.info('Would execute command: %s %s', pythonPath, args.join(' '));
|
||||
} else {
|
||||
@@ -214,6 +220,7 @@ export async function init({
|
||||
dryRun,
|
||||
cwd,
|
||||
pythonPath,
|
||||
upgrade,
|
||||
typedocJson: typeDocJsonPath,
|
||||
}: InitOptions = {}): Promise<void> {
|
||||
if (!typescript && typedoc) {
|
||||
@@ -222,7 +229,7 @@ export async function init({
|
||||
);
|
||||
}
|
||||
|
||||
if (typescript) {
|
||||
if (typescript && !upgrade) {
|
||||
await initTsConfigJson({
|
||||
dest: tsconfigJsonPath,
|
||||
packageJson: packageJsonPath,
|
||||
@@ -233,7 +240,7 @@ export async function init({
|
||||
});
|
||||
}
|
||||
|
||||
if (typedoc) {
|
||||
if (typedoc && !upgrade) {
|
||||
await initTypeDocJson({
|
||||
dest: typeDocJsonPath,
|
||||
packageJson: packageJsonPath,
|
||||
@@ -244,10 +251,10 @@ export async function init({
|
||||
}
|
||||
|
||||
if (python) {
|
||||
await initPython({pythonPath, dryRun});
|
||||
await initPython({pythonPath, dryRun, upgrade});
|
||||
}
|
||||
|
||||
if (mkdocs) {
|
||||
if (mkdocs && !upgrade) {
|
||||
await initMkDocs({
|
||||
dest: mkdocsYmlPath,
|
||||
cwd,
|
||||
@@ -262,7 +269,7 @@ export async function init({
|
||||
}
|
||||
}
|
||||
|
||||
export interface InitTypeDocOptions extends ScaffoldTaskOptions {}
|
||||
export type InitTypeDocOptions = ScaffoldTaskOptions;
|
||||
export interface InitTsConfigOptions extends ScaffoldTaskOptions {
|
||||
/**
|
||||
* List of source files (globs supported); typically `src` or `lib`
|
||||
@@ -274,6 +281,11 @@ export interface InitPythonOptions extends ScaffoldTaskOptions {
|
||||
* Path to `python` (v3.x) executable
|
||||
*/
|
||||
pythonPath?: string;
|
||||
|
||||
/**
|
||||
* If true, upgrade only
|
||||
*/
|
||||
upgrade?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -315,5 +327,10 @@ export type InitOptions = Simplify<
|
||||
* Path to new or existing `mkdocs.yml` file
|
||||
*/
|
||||
mkdocsYml?: string;
|
||||
|
||||
/**
|
||||
* If `true`, upgrade only
|
||||
*/
|
||||
upgrade?: boolean;
|
||||
}
|
||||
>;
|
||||
|
||||
@@ -32,34 +32,34 @@ class DocutilsReporter extends FancyReporter {
|
||||
/**
|
||||
* Mapping of log types (the name of the logging method called) to chalk fg colors
|
||||
*/
|
||||
static TYPE_COLOR_MAP = {
|
||||
static readonly TYPE_COLOR_MAP = {
|
||||
info: 'cyan',
|
||||
success: 'green',
|
||||
error: 'red',
|
||||
warn: 'yellow',
|
||||
} as const;
|
||||
} satisfies {[k in LogType]?: typeof ForegroundColor};
|
||||
|
||||
/**
|
||||
* Mapping of log levels to chalk fg colors
|
||||
*/
|
||||
static LEVEL_COLORS = {
|
||||
static readonly LEVEL_COLORS = {
|
||||
0: 'red',
|
||||
1: 'yellow',
|
||||
2: 'white',
|
||||
3: 'green',
|
||||
} as const;
|
||||
} satisfies {[k in LogLevel]?: typeof ForegroundColor};
|
||||
|
||||
/**
|
||||
* Mapping of log types to icons/symbols
|
||||
*/
|
||||
static TYPE_ICONS = {
|
||||
static readonly TYPE_ICONS = {
|
||||
info: logSymbols.info,
|
||||
success: logSymbols.success,
|
||||
error: logSymbols.error,
|
||||
warn: logSymbols.warning,
|
||||
debug: figures('›'),
|
||||
trace: figures('›'),
|
||||
} as const;
|
||||
} satisfies {[k in LogType]?: string};
|
||||
|
||||
/**
|
||||
* Default color to use if we can't find a color for the log type or level
|
||||
@@ -191,8 +191,3 @@ function createLogProxy(logger: Consola): Consola {
|
||||
* @see {createLogProxy}
|
||||
*/
|
||||
export default rootLogger;
|
||||
|
||||
// these are just type-sanity checks
|
||||
<{[k in LogType]?: typeof ForegroundColor}>DocutilsReporter.TYPE_COLOR_MAP;
|
||||
<{[k in LogLevel]?: typeof ForegroundColor}>DocutilsReporter.LEVEL_COLORS;
|
||||
<{[k in LogType]?: string}>DocutilsReporter.TYPE_ICONS;
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
/**
|
||||
* API around `mike`, a tool for deploying multiple versions of MkDocs-built sites to GitHub Pages
|
||||
* @module
|
||||
*/
|
||||
|
||||
import {exec} from 'teen_process';
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
import log from './logger';
|
||||
|
||||
@@ -9,16 +9,10 @@ import type {TypeDocOptions} from 'typedoc';
|
||||
|
||||
/**
|
||||
* A `tsconfig.json` file w/ `$schema` prop
|
||||
*
|
||||
* Due to some `unknown` types in {@linkcode type-fest.TsConfigJson}, we cannot use that type
|
||||
* directly and need to use `Jsonify`.
|
||||
*
|
||||
*/
|
||||
export type TsConfigJson = Jsonify<
|
||||
TsConfigJsonBase & {
|
||||
$schema?: string;
|
||||
}
|
||||
>;
|
||||
export type TsConfigJson = TsConfigJsonBase & {
|
||||
$schema?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* A `typedoc.json` file w/ `$schema` and `extends` props
|
||||
|
||||
@@ -32,6 +32,9 @@ export function stopwatch(id: string) {
|
||||
}
|
||||
stopwatch.cache = new Map<string, number>();
|
||||
|
||||
/**
|
||||
* Converts a tuple to an object; use for extracting parameter types from a function signature
|
||||
*/
|
||||
export type TupleToObject<
|
||||
T extends readonly any[],
|
||||
M extends Record<Exclude<keyof T, keyof any[]>, PropertyKey>
|
||||
@@ -69,7 +72,7 @@ export const argify: (obj: Record<string, string | number | boolean | undefined>
|
||||
);
|
||||
|
||||
/**
|
||||
* Conversion of the parameters of {@linkcode Subprocess.start} to an object.
|
||||
* Conversion of the parameters of {@linkcode SubProcess.start} to an object.
|
||||
*/
|
||||
export type TeenProcessSubprocessStartOpts = Partial<
|
||||
TupleToObject<Parameters<SubProcess['start']>, ['startDetector', 'detach', 'timeoutMs']>
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
/**
|
||||
* Validates an environment for building documentation
|
||||
* Validates an environment for building documentation; used by `validate` command
|
||||
*
|
||||
* @module
|
||||
*/
|
||||
|
||||
import {fs} from '@appium/support';
|
||||
import {fs, util} from '@appium/support';
|
||||
import chalk from 'chalk';
|
||||
import _ from 'lodash';
|
||||
import {EventEmitter} from 'node:events';
|
||||
import path from 'node:path';
|
||||
import pluralize from 'pluralize';
|
||||
import {satisfies} from 'semver';
|
||||
import {exec} from 'teen_process';
|
||||
import {
|
||||
@@ -36,7 +35,6 @@ import {
|
||||
findMkDocsYml,
|
||||
readJson5,
|
||||
readTypedocJson,
|
||||
readYaml,
|
||||
whichMkDocs,
|
||||
whichNpm,
|
||||
whichPython,
|
||||
@@ -287,7 +285,7 @@ export class DocutilsValidator extends EventEmitter {
|
||||
return this.requirementsTxt;
|
||||
}
|
||||
|
||||
let requiredPackages: PipPackage[] = [];
|
||||
const requiredPackages: PipPackage[] = [];
|
||||
|
||||
try {
|
||||
let requirementsTxt = await fs.readFile(REQUIREMENTS_TXT_PATH, 'utf8');
|
||||
@@ -312,6 +310,9 @@ export class DocutilsValidator extends EventEmitter {
|
||||
this.emittedErrors.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that the correct version of `mkdocs` is installed
|
||||
*/
|
||||
protected async validateMkDocs() {
|
||||
let mkDocsPath: string | undefined;
|
||||
try {
|
||||
@@ -466,7 +467,7 @@ export class DocutilsValidator extends EventEmitter {
|
||||
const msgParts = [];
|
||||
if (missingPackages.length) {
|
||||
msgParts.push(
|
||||
`The following required ${pluralize(
|
||||
`The following required ${util.pluralize(
|
||||
'package',
|
||||
missingPackages.length
|
||||
)} could not be found:\n${missingPackages
|
||||
@@ -476,7 +477,7 @@ export class DocutilsValidator extends EventEmitter {
|
||||
}
|
||||
if (invalidVersionPackages.length) {
|
||||
msgParts.push(
|
||||
`The following required ${pluralize(
|
||||
`The following required ${util.pluralize(
|
||||
'package',
|
||||
invalidVersionPackages.length
|
||||
)} are installed, but at the wrong version:\n${invalidVersionPackages
|
||||
@@ -533,7 +534,7 @@ export class DocutilsValidator extends EventEmitter {
|
||||
}
|
||||
|
||||
if (rawTypeDocVersion) {
|
||||
let match = rawTypeDocVersion.match(TYPEDOC_VERSION_REGEX);
|
||||
const match = rawTypeDocVersion.match(TYPEDOC_VERSION_REGEX);
|
||||
if (match) {
|
||||
typeDocVersion = match[1];
|
||||
} else {
|
||||
@@ -621,7 +622,7 @@ export class DocutilsValidator extends EventEmitter {
|
||||
return this.fail(`Could not find TypeScript compiler ("tsc") from ${pkgDir}`);
|
||||
}
|
||||
|
||||
let match = rawTypeScriptVersion.match(TYPESCRIPT_VERSION_REGEX);
|
||||
const match = rawTypeScriptVersion.match(TYPESCRIPT_VERSION_REGEX);
|
||||
if (match) {
|
||||
typeScriptVersion = match[1];
|
||||
} else {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@appium/docutils",
|
||||
"version": "0.2.0",
|
||||
"version": "0.3.0",
|
||||
"description": "Documentation generation utilities for Appium and related projects",
|
||||
"keywords": [
|
||||
"automation",
|
||||
@@ -46,36 +46,34 @@
|
||||
"test:e2e": "echo \"No e2e tests for this package\"",
|
||||
"test:smoke": "node ./index.js",
|
||||
"test:unit": "mocha \"./test/unit/**/*.spec.js\"",
|
||||
"start": "node ./build/lib/cli.js"
|
||||
"start": "node ./build/lib/cli/index.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@appium/support": "^3.1.5",
|
||||
"@appium/support": "^3.1.7",
|
||||
"@appium/tsconfig": "^0.2.4",
|
||||
"@appium/typedoc-plugin-appium": "^0.4.0",
|
||||
"@appium/typedoc-plugin-appium": "^0.5.1",
|
||||
"@sliphua/lilconfig-ts-loader": "3.2.2",
|
||||
"chalk": "4.1.2",
|
||||
"consola": "2.15.3",
|
||||
"diff": "5.1.0",
|
||||
"figures": "3.2.0",
|
||||
"find-up": "5.0.0",
|
||||
"glob": "8.1.0",
|
||||
"json5": "2.2.3",
|
||||
"lilconfig": "2.0.6",
|
||||
"lilconfig": "2.1.0",
|
||||
"lodash": "4.17.21",
|
||||
"log-symbols": "4.1.0",
|
||||
"pkg-dir": "5.0.0",
|
||||
"pluralize": "8.0.0",
|
||||
"read-pkg": "5.2.0",
|
||||
"semver": "7.3.8",
|
||||
"source-map-support": "0.5.21",
|
||||
"teen_process": "2.0.2",
|
||||
"type-fest": "3.5.6",
|
||||
"typedoc": "0.23.24",
|
||||
"type-fest": "3.6.1",
|
||||
"typedoc": "0.23.26",
|
||||
"typedoc-plugin-markdown": "3.14.0",
|
||||
"typedoc-plugin-resolve-crossmodule-references": "0.3.3",
|
||||
"typescript": "4.7.4",
|
||||
"typescript": "4.9.5",
|
||||
"yaml": "2.2.1",
|
||||
"yargs": "17.6.2",
|
||||
"yargs": "17.7.1",
|
||||
"yargs-parser": "21.1.1"
|
||||
},
|
||||
"engines": {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
mkdocs==1.4.2
|
||||
mkdocs-material==9.0.11
|
||||
mkdocs-material==9.1.1
|
||||
mkdocs-material-extensions==1.1.1
|
||||
mike==1.1.2
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {Mike} from '../..';
|
||||
import {Mike} from '../../lib/mike';
|
||||
import {expect} from 'chai';
|
||||
|
||||
describe('Mike', function () {
|
||||
|
||||
@@ -3,6 +3,36 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [0.4.1](https://github.com/appium/appium/compare/@appium/driver-test-support@0.4.0...@appium/driver-test-support@0.4.1) (2023-03-08)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **types:** update dependency type-fest to v3.6.1 ([471a4b5](https://github.com/appium/appium/commit/471a4b57e622ff077d59f577a78341268700c48d))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [0.4.0](https://github.com/appium/appium/compare/@appium/driver-test-support@0.3.7...@appium/driver-test-support@0.4.0) (2023-02-24)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **support:** update dependency axios to v1.3.4 ([49f157d](https://github.com/appium/appium/commit/49f157d63e3bdbd205527a5dc8f997df68540546))
|
||||
* **types:** update dependency type-fest to v3.5.7 ([b4416c5](https://github.com/appium/appium/commit/b4416c5c0f40200b36909a1fbb492d8c4a212108))
|
||||
* **types:** update dependency type-fest to v3.6.0 ([08a6f3a](https://github.com/appium/appium/commit/08a6f3a308c7ee162e992629888557b31e50a26e))
|
||||
* update axios to v1.3.3 ([8f9de63](https://github.com/appium/appium/commit/8f9de63e4a622712db545ab63f9f4ce6654e4a91))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **driver-test-support:** add startStoppableAppium() ([893bd88](https://github.com/appium/appium/commit/893bd888cf4e7b43e07c83e8ce71d71a30a8428e))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.3.7](https://github.com/appium/appium/compare/@appium/driver-test-support@0.3.6...@appium/driver-test-support@0.3.7) (2023-02-09)
|
||||
|
||||
|
||||
|
||||
@@ -99,8 +99,7 @@ export function createSessionHelpers(port, address = TEST_HOST) {
|
||||
|
||||
/**
|
||||
* Creates E2E test suites for a driver.
|
||||
* @template {Driver} P
|
||||
* @param {DriverClass<P>} DriverClass
|
||||
* @param {DriverClass} DriverClass
|
||||
* @param {Partial<BaseNSCapabilities>} [defaultCaps]
|
||||
*/
|
||||
export function driverE2ETestSuite(DriverClass, defaultCaps = {}) {
|
||||
@@ -110,7 +109,7 @@ export function driverE2ETestSuite(DriverClass, defaultCaps = {}) {
|
||||
|
||||
describe(`BaseDriver E2E (as ${className})`, function () {
|
||||
let baseServer;
|
||||
/** @type {P} */
|
||||
/** @type {Driver} */
|
||||
let d;
|
||||
/**
|
||||
* This URL creates a new session
|
||||
@@ -419,16 +418,9 @@ export function driverE2ETestSuite(DriverClass, defaultCaps = {}) {
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@linkcode DriverClass}, except using the base {@linkcode Driver} type instead of `ExternalDriver`.
|
||||
* This allows the suite to work for `BaseDriver`.
|
||||
* @template {Driver} P
|
||||
* @typedef {import('@appium/types').DriverClass<P>} DriverClass
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {import('@appium/types').DriverClass} DriverClass
|
||||
* @typedef {import('@appium/types').Driver} Driver
|
||||
* @typedef {import('@appium/types').Constraints} Constraints
|
||||
* @typedef {import('@appium/types').DriverStatic} DriverStatic
|
||||
* @typedef {import('@appium/types').StringRecord} StringRecord
|
||||
* @typedef {import('@appium/types').BaseDriverCapConstraints} BaseDriverCapConstraints
|
||||
* @typedef {import('@appium/types').BaseNSCapabilities} BaseNSCapabilities
|
||||
|
||||
@@ -2,6 +2,9 @@ export {createSessionHelpers, driverE2ETestSuite} from './e2e-suite';
|
||||
export * from './unit-suite';
|
||||
export * from './helpers';
|
||||
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
export * from './stoppable';
|
||||
|
||||
/**
|
||||
* @typedef {import('@appium/types').DriverClass} DriverClass
|
||||
* @typedef {import('@appium/types').BaseNSCapabilities} BaseNSCapabilities
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
import type {AppiumServer} from '@appium/types';
|
||||
import {main as startAppium} from 'appium';
|
||||
import type {Args, CliCommandServer} from 'appium/types';
|
||||
import B from 'bluebird';
|
||||
import type {Server} from 'node:http';
|
||||
import stoppable from 'stoppable';
|
||||
import type {Asyncify} from 'type-fest';
|
||||
|
||||
/**
|
||||
* Options for {@linkcode startStoppableAppium}
|
||||
*/
|
||||
export type AppiumServerOpts = Args<CliCommandServer>;
|
||||
|
||||
/**
|
||||
* An {@linkcode AppiumServer} with a method `stop() => Promise<void>`, which closes all sockets and fully stops the server.
|
||||
*
|
||||
* Returned by {@linkcode startStoppableAppium}
|
||||
*/
|
||||
export type TestAppiumServer = Omit<NormativeAppiumServer, 'close'> & {
|
||||
stop: Asyncify<stoppable.WithStop['stop']>;
|
||||
close: (callback: (err?: Error) => void) => Promise<void>;
|
||||
};
|
||||
|
||||
/**
|
||||
* The {@linkcode AppiumServer} type, but with the `close` method normalized to a callback-style function.
|
||||
*/
|
||||
export type NormativeAppiumServer = Omit<AppiumServer, 'close'> & {
|
||||
close: Server['close'];
|
||||
};
|
||||
|
||||
/**
|
||||
* Coerces {@linkcode AppiumServer} into a {@linkcode TestAppiumServer}.
|
||||
* @param opts Options for {@linkcode startAppium}
|
||||
* @todo This should be moved into `@appium/driver-test-support` or something
|
||||
* @returns A stoppable Appium server
|
||||
*/
|
||||
export async function startStoppableAppium(opts: AppiumServerOpts): Promise<TestAppiumServer> {
|
||||
const appiumServer = (await startAppium(opts)) as AppiumServer;
|
||||
const stoppableServer = stoppable(appiumServer as unknown as NormativeAppiumServer, 0);
|
||||
const originalAsyncClose = appiumServer.close;
|
||||
(stoppableServer as unknown as TestAppiumServer).close = async function (
|
||||
callback?: (err?: Error) => void
|
||||
) {
|
||||
if (callback) {
|
||||
try {
|
||||
await originalAsyncClose.call(this);
|
||||
callback();
|
||||
} catch (err) {
|
||||
callback(err);
|
||||
}
|
||||
} else {
|
||||
await originalAsyncClose.call(this);
|
||||
}
|
||||
};
|
||||
stoppableServer.stop = B.promisify(stoppableServer.stop, {context: stoppableServer});
|
||||
return stoppableServer as unknown as TestAppiumServer;
|
||||
}
|
||||
@@ -626,12 +626,22 @@ export function driverUnitTestSuite(DriverClass, defaultCaps = {}) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {import('@appium/types').DriverClass} DriverClass
|
||||
* @typedef {import('@appium/types').BaseNSCapabilities} BaseNSCapabilities
|
||||
* @typedef {import('@appium/types').Constraints} Constraints
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template {import('@appium/types').Constraints} [C=import('@appium/types').BaseDriverCapConstraints]
|
||||
* @template {Constraints} [C=import('@appium/types').BaseDriverCapConstraints]
|
||||
* @typedef {import('@appium/types').DriverClass<Driver<C>>} DriverClass
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template {Constraints} C
|
||||
* @typedef {import('@appium/types').Driver<C>} Driver
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template {Constraints} [C=import('@appium/types').BaseDriverCapConstraints]
|
||||
* @template {import('@appium/types').StringRecord|void} [Extra=void]
|
||||
* @typedef {import('@appium/types').W3CCapabilities<C, Extra>} W3CCapabilities
|
||||
*/
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@appium/driver-test-support",
|
||||
"version": "0.3.7",
|
||||
"version": "0.4.1",
|
||||
"description": "Test utilities for Appium drivers",
|
||||
"keywords": [
|
||||
"automation",
|
||||
@@ -41,16 +41,18 @@
|
||||
},
|
||||
"types": "./build/lib/index.d.ts",
|
||||
"dependencies": {
|
||||
"@appium/types": "^0.9.1",
|
||||
"@appium/types": "^0.10.1",
|
||||
"@types/lodash": "4.14.191",
|
||||
"axios": "1.2.3",
|
||||
"@types/stoppable": "1.1.1",
|
||||
"axios": "1.3.4",
|
||||
"bluebird": "3.7.2",
|
||||
"chai": "4.3.7",
|
||||
"get-port": "5.1.1",
|
||||
"lodash": "4.17.21",
|
||||
"sinon": "15.0.1",
|
||||
"source-map-support": "0.5.21",
|
||||
"type-fest": "3.5.6"
|
||||
"stoppable": "1.1.0",
|
||||
"type-fest": "3.6.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"appium": "^2.0.0-beta.43",
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
import axios from 'axios';
|
||||
import B from 'bluebird';
|
||||
import {Agent} from 'node:http';
|
||||
import {startStoppableAppium, TestAppiumServer} from '../../lib';
|
||||
import getPort from 'get-port';
|
||||
|
||||
const {expect} = chai;
|
||||
|
||||
describe('startStoppableAppium()', function () {
|
||||
it('should start an Appium server', async function () {
|
||||
let server: TestAppiumServer | undefined;
|
||||
try {
|
||||
server = await startStoppableAppium({port: await getPort()});
|
||||
expect(server, 'to be an object');
|
||||
} finally {
|
||||
if (server) {
|
||||
await expect(server.stop()).to.be.fulfilled;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
describe('when the server has connections', function () {
|
||||
it('should stop the server and resolve with a boolean', async function () {
|
||||
const port = await getPort();
|
||||
const server = await startStoppableAppium({port});
|
||||
const getConnections = B.promisify(server.getConnections, {context: server});
|
||||
await axios.get(`http://127.0.0.1:${port}/status`, {
|
||||
httpAgent: new Agent({keepAlive: true}),
|
||||
});
|
||||
try {
|
||||
await expect(getConnections()).to.eventually.be.greaterThan(0);
|
||||
} finally {
|
||||
await expect(server.stop()).to.eventually.be.a('boolean');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -10,6 +10,6 @@
|
||||
},
|
||||
"types": ["mocha", "chai", "chai-as-promised"]
|
||||
},
|
||||
"include": ["./lib"],
|
||||
"include": ["lib", "test"],
|
||||
"references": [{"path": "../types"}, {"path": "../base-driver"}]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
# Change Log
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# 0.2.0 (2023-03-08)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **eslint-config-appium-ts:** create new pkg @appium/eslint-config-appium-ts ([dfe02ca](https://github.com/appium/appium/commit/dfe02ca98c73c0cf9863a7c7441d6a1a5d37ac33))
|
||||
@@ -0,0 +1,44 @@
|
||||
# @appium/eslint-config-appium
|
||||
|
||||
> Provides a reusable [ESLint](http://eslint.org/) [shared configuration](http://eslint.org/docs/developer-guide/shareable-configs) for [Appium](https://github.com/appium/appium) and Appium-adjacent projects (for TypeScript)
|
||||
|
||||
## Motivation
|
||||
|
||||
**If your package has no TypeScript sources, you don't need this.** However, if your package _does_ have TypeScript sources, you can't lint those files without this.
|
||||
|
||||
## Usage
|
||||
|
||||
Install the package with **`npm` v8 or newer**:
|
||||
|
||||
```bash
|
||||
npm install @appium/eslint-config-appium-ts --save-dev
|
||||
```
|
||||
|
||||
And then, in your `.eslintrc` file, extend the configuration:
|
||||
|
||||
```json
|
||||
{
|
||||
"extends": "@appium/eslint-config-appium-ts"
|
||||
}
|
||||
```
|
||||
|
||||
## Peer Dependencies
|
||||
|
||||
This config requires the following packages be installed (as peer dependencies) in your project. See the `package.json` for the required versions.
|
||||
|
||||
- [eslint](https://www.npmjs.com/package/eslint)
|
||||
- [eslint-config-prettier](https://www.npmjs.com/package/eslint-config-prettier)
|
||||
- [eslint-plugin-import](https://www.npmjs.com/package/eslint-plugin-import)
|
||||
- [eslint-plugin-mocha](https://www.npmjs.com/package/eslint-plugin-mocha)
|
||||
- [eslint-plugin-promise](https://www.npmjs.com/package/eslint-plugin-promise)
|
||||
- [@appium/eslint-config-appium](https://www.npmjs.com/package/@appium/eslint-config-appium)
|
||||
- [@typescript-eslint/eslint-plugin](https://www.npmjs.com/package/@typescript-eslint/eslint-plugin)
|
||||
- [@typescript-eslint/parser](https://www.npmjs.com/package/@typescript-eslint/parser)
|
||||
|
||||
## Notes
|
||||
|
||||
- This configuration is intended to be used alongside [Prettier](https://www.npmjs.com/package/prettier).
|
||||
|
||||
## License
|
||||
|
||||
Copyright © 2023 OpenJS Foundation. Licensed Apache-2.0
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"$schema": "http://json.schemastore.org/eslintrc",
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.ts", "*.tsx"],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"extends": ["@appium/eslint-config-appium", "plugin:@typescript-eslint/recommended"],
|
||||
"rules": {
|
||||
"@typescript-eslint/no-empty-function": 1,
|
||||
"@typescript-eslint/no-empty-interface": 0,
|
||||
"@typescript-eslint/no-explicit-any": 0,
|
||||
"@typescript-eslint/no-non-null-assertion": 0,
|
||||
"@typescript-eslint/no-var-requires": 1,
|
||||
"import/no-duplicates": 0,
|
||||
"import/no-unresolved": 0,
|
||||
"no-unused-vars": 0,
|
||||
"promise/no-native": 0,
|
||||
"require-await": 0
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"name": "@appium/eslint-config-appium-ts",
|
||||
"version": "0.2.0",
|
||||
"description": "Shared ESLint config for Appium projects (TypeScript version)",
|
||||
"keywords": [
|
||||
"eslint",
|
||||
"eslintconfig",
|
||||
"appium",
|
||||
"typescript",
|
||||
"typescript-eslint"
|
||||
],
|
||||
"homepage": "https://appium.io",
|
||||
"bugs": {
|
||||
"url": "https://github.com/appium/appium/issues"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/appium/appium.git",
|
||||
"directory": "packages/eslint-config-appium-ts"
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"author": "https://github.com/appium",
|
||||
"main": "index.json",
|
||||
"files": [
|
||||
"index.json"
|
||||
],
|
||||
"scripts": {
|
||||
"test:smoke": "exit 0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@appium/eslint-config-appium": "^8.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.5.0",
|
||||
"@typescript-eslint/parser": "^5.5.0",
|
||||
"eslint": "^8.21.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-mocha": "^10.1.0",
|
||||
"eslint-plugin-promise": "^6.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.17.0 || ^16.13.0 || >=18.0.0",
|
||||
"npm": ">=8"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [8.0.1](https://github.com/appium/appium/compare/@appium/eslint-config-appium@8.0.0...@appium/eslint-config-appium@8.0.1) (2023-03-08)
|
||||
|
||||
**Note:** Version bump only for package @appium/eslint-config-appium
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [8.0.0](https://github.com/appium/appium/compare/@appium/eslint-config-appium@7.0.0...@appium/eslint-config-appium@8.0.0) (2022-12-14)
|
||||
|
||||
- chore!: set engines to minimum Node.js v14.17.0 ([a1dbe6c](https://github.com/appium/appium/commit/a1dbe6c43efe76604943a607d402f4c8b864d652))
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
## Usage
|
||||
|
||||
Install the package with **`npm` v7 or newer** (for automatic installation of peer dependencies):
|
||||
Install the package with **`npm` v8 or newer**:
|
||||
|
||||
```bash
|
||||
npm install @appium/eslint-config-appium --save-dev
|
||||
@@ -20,7 +20,7 @@ And then, in your `.eslintrc` file, extend the configuration:
|
||||
|
||||
## Peer Dependencies
|
||||
|
||||
This config requires the following packages be installed (as dev dependencies) in your project. See the `package.json` for the required versions.
|
||||
This config requires the following packages be installed (as peer dependencies) in your project. See the `package.json` for the required versions.
|
||||
|
||||
- [eslint](https://www.npmjs.com/package/eslint)
|
||||
- [eslint-config-prettier](https://www.npmjs.com/package/eslint-config-prettier)
|
||||
|
||||
@@ -1,16 +1,11 @@
|
||||
{
|
||||
"name": "@appium/eslint-config-appium",
|
||||
"version": "8.0.0",
|
||||
"version": "8.0.1",
|
||||
"description": "Shared ESLint config for Appium projects",
|
||||
"keywords": [
|
||||
"automation",
|
||||
"javascript",
|
||||
"selenium",
|
||||
"webdriver",
|
||||
"ios",
|
||||
"android",
|
||||
"firefoxos",
|
||||
"testing"
|
||||
"eslint",
|
||||
"eslintconfig",
|
||||
"appium"
|
||||
],
|
||||
"homepage": "https://appium.io",
|
||||
"bugs": {
|
||||
|
||||
@@ -3,6 +3,28 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [3.0.8](https://github.com/appium/appium/compare/@appium/execute-driver-plugin@3.0.7...@appium/execute-driver-plugin@3.0.8) (2023-03-08)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **execute-driver-plugin:** update dependency webdriverio to v7.30.2 ([49694f5](https://github.com/appium/appium/commit/49694f50a4680138cc92aba1eee294c927d712cf))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.0.7](https://github.com/appium/appium/compare/@appium/execute-driver-plugin@3.0.6...@appium/execute-driver-plugin@3.0.7) (2023-02-24)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **execute-driver-plugin:** update dependency webdriverio to v7.30.1 ([547b8a4](https://github.com/appium/appium/commit/547b8a45c4b5629720b0f4bdb0485a861aecbebf))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.0.6](https://github.com/appium/appium/compare/@appium/execute-driver-plugin@3.0.5...@appium/execute-driver-plugin@3.0.6) (2023-02-09)
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@appium/execute-driver-plugin",
|
||||
"version": "3.0.6",
|
||||
"version": "3.0.8",
|
||||
"description": "Plugin for batching and executing driver commands with Appiums",
|
||||
"keywords": [
|
||||
"automation",
|
||||
@@ -43,7 +43,7 @@
|
||||
"lodash": "4.17.21",
|
||||
"source-map-support": "0.5.21",
|
||||
"vm2": "3.9.14",
|
||||
"webdriverio": "7.30.0"
|
||||
"webdriverio": "7.30.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"appium": "^2.0.0-beta.35"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user