From 163b34dc2fcd4e7bfb7e1e6acefd60ac44db98c5 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 2 Sep 2020 22:01:20 +0200 Subject: [PATCH 01/44] chore(deps): bump argparse from 1.0.10 to 2.0.1 (#14687) Bumps [argparse](https://github.com/nodeca/argparse) from 1.0.10 to 2.0.1. - [Release notes](https://github.com/nodeca/argparse/releases) - [Changelog](https://github.com/nodeca/argparse/blob/master/CHANGELOG.md) - [Commits](https://github.com/nodeca/argparse/compare/1.0.10...2.0.1) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> Co-authored-by: Mykola Mokhnach --- gulpfile.js | 10 +- lib/appium.js | 4 +- lib/argsparse-actions.js | 77 ++++++ lib/config.js | 42 +-- lib/main.js | 2 +- lib/parser.js | 574 +++++++++++++++++---------------------- package.json | 2 +- test/config-specs.js | 4 +- test/driver-specs.js | 2 +- test/parser-specs.js | 42 +-- 10 files changed, 382 insertions(+), 377 deletions(-) create mode 100644 lib/argsparse-actions.js diff --git a/gulpfile.js b/gulpfile.js index a6f7e93b6..47935ba7a 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -70,20 +70,20 @@ gulp.task('docs', gulp.series(['transpile']), function parseDocs () { const exampleArg = typeof arg[0][1] === 'undefined' ? arg[0][0] : arg[0][1]; const argOpts = arg[1]; - // --keystore-path defaultValue contains a user-specific path, + // --keystore-path default contains a user-specific path, // let's replace it with /... if (arg[0][0] === '--keystore-path') { const userPath = process.env.HOME || process.env.USERPROFILE; - argOpts.defaultValue = argOpts.defaultValue.replace(userPath, '<user>'); + argOpts.default = argOpts.default.replace(userPath, '<user>'); } // handle empty objects - if (JSON.stringify(argOpts.defaultValue) === '{}') { - argOpts.defaultValue = '{}'; + if (JSON.stringify(argOpts.default) === '{}') { + argOpts.default = '{}'; } md += '|`' + argNames.join('`, `') + '`'; - md += '|' + ((typeof argOpts.defaultValue === 'undefined') ? '' : argOpts.defaultValue); + md += '|' + ((typeof argOpts.default === 'undefined') ? '' : argOpts.default); md += '|' + argOpts.help; md += '|' + ((typeof argOpts.example === 'undefined') ? '' : '`' + exampleArg + ' ' + argOpts.example + '`'); md += '|\n'; diff --git a/lib/appium.js b/lib/appium.js index 4a4c93d83..3424696c5 100644 --- a/lib/appium.js +++ b/lib/appium.js @@ -254,9 +254,7 @@ class AppiumDriver extends BaseDriver { async getSessions () { const sessions = await sessionsListGuard.acquire(AppiumDriver.name, () => this.sessions); return _.toPairs(sessions) - .map(([id, driver]) => { - return {id, capabilities: driver.caps}; - }); + .map(([id, driver]) => ({id, capabilities: driver.caps})); } printNewSessionAnnouncement (driverName, driverVersion) { diff --git a/lib/argsparse-actions.js b/lib/argsparse-actions.js new file mode 100644 index 000000000..7a0c0f6c9 --- /dev/null +++ b/lib/argsparse-actions.js @@ -0,0 +1,77 @@ +import { Action } from 'argparse'; + + +const DEFAULT_CAPS_ARG = '--default-capabilities'; + + +class StoreDeprecatedAction extends Action { + constructor (options = {}) { + const opts = Object.assign({}, options); + let helpPrefix = '[DEPRECATED]'; + if (opts.deprecated_for) { + helpPrefix = `[DEPRECATED, use ${opts.deprecated_for} instead]`; + delete opts.deprecated_for; + } + if (opts.help) { + opts.help = `${helpPrefix} - ${opts.help}`; + } else { + opts.help = helpPrefix; + } + super(opts); + } + + call (parser, namespace, values) { + namespace[this.dest] = values; + } +} + + +class StoreDeprecatedTrueAction extends StoreDeprecatedAction { + constructor (options = {}) { + super(Object.assign({}, options, {const: true, nargs: 0})); + } + + call (parser, namespace) { + namespace[this.dest] = this.const; + } +} + + +class StoreDeprecatedDefaultCapabilityAction extends StoreDeprecatedAction { + constructor (options = {}) { + super(Object.assign({}, options, {deprecated_for: DEFAULT_CAPS_ARG})); + } + + _writeDefaultCap (namespace, value) { + namespace[this.dest] = value; + if (value === this.default) { + return; + } + + if (!namespace.defaultCapabilities) { + namespace.defaultCapabilities = {}; + } + namespace.defaultCapabilities[this.dest] = value; + } + + call (parser, namespace, values) { + this._writeDefaultCap(namespace, values); + } +} + + +class StoreDeprecatedDefaultCapabilityTrueAction extends StoreDeprecatedDefaultCapabilityAction { + constructor (options = {}) { + super(Object.assign({}, options, {const: true, nargs: 0})); + } + + call (parser, namespace) { + this._writeDefaultCap(namespace, this.const); + } +} + +export { + StoreDeprecatedAction, StoreDeprecatedTrueAction, + StoreDeprecatedDefaultCapabilityAction, StoreDeprecatedDefaultCapabilityTrueAction, + DEFAULT_CAPS_ARG, +}; diff --git a/lib/config.js b/lib/config.js index 445da4a1e..63374f159 100644 --- a/lib/config.js +++ b/lib/config.js @@ -6,6 +6,9 @@ import { exec } from 'teen_process'; import { rootDir } from './utils'; import logger from './logger'; import semver from 'semver'; +import { + StoreDeprecatedDefaultCapabilityAction, DEFAULT_CAPS_ARG, +} from './argsparse-actions'; const npmPackage = require(path.resolve(rootDir, 'package.json')); @@ -24,6 +27,11 @@ function getNodeVersion () { return semver.coerce(process.version); } +function isSubClass (candidateClass, superClass) { + return _.isFunction(superClass) && _.isFunction(candidateClass) + && (candidateClass.prototype instanceof superClass || candidateClass === superClass); +} + async function updateBuildInfo (useGithubApiFallback = false) { const sha = await getGitRev(useGithubApiFallback); if (!sha) { @@ -136,29 +144,29 @@ async function showConfig () { } function getNonDefaultArgs (parser, args) { - let nonDefaults = {}; - for (let rawArg of parser.rawArgs) { - let arg = rawArg[1].dest; - if (args[arg] && args[arg] !== rawArg[1].defaultValue) { - nonDefaults[arg] = args[arg]; + return parser.rawArgs.reduce((acc, [, {dest, default: defaultValue}]) => { + if (args[dest] && args[dest] !== defaultValue) { + acc[dest] = args[dest]; } - } - return nonDefaults; + return acc; + }, {}); } function getDeprecatedArgs (parser, args) { // go through the server command line arguments and figure // out which of the ones used are deprecated - let deprecated = {}; - for (let rawArg of parser.rawArgs) { - let arg = rawArg[1].dest; - let defaultValue = rawArg[1].defaultValue; - let isDeprecated = !!rawArg[1].deprecatedFor; - if (args[arg] && args[arg] !== defaultValue && isDeprecated) { - deprecated[rawArg[0]] = rawArg[1].deprecatedFor; + return parser.rawArgs.reduce((acc, [[name], {dest, default: defaultValue, action}]) => { + if (!args[dest] || args[dest] === defaultValue) { + return acc; } - } - return deprecated; + + if (action?.deprecated_for) { + acc[name] = action.deprecated_for; + } else if (isSubClass(action, StoreDeprecatedDefaultCapabilityAction)) { + acc[name] = DEFAULT_CAPS_ARG; + } + return acc; + }, {}); } function checkValidPort (port, portName) { @@ -197,7 +205,7 @@ function validateServerArgs (parser, args) { bootstrapPort: checkValidPort, chromedriverPort: checkValidPort, robotPort: checkValidPort, - backendRetries: (r) => { return r >= 0; } + backendRetries: (r) => r >= 0, }; const nonDefaultArgs = getNonDefaultArgs(parser, args); diff --git a/lib/main.js b/lib/main.js index e5ad58404..542401796 100755 --- a/lib/main.js +++ b/lib/main.js @@ -111,7 +111,7 @@ async function main (args = null) { } } else { // otherwise parse from CLI - args = parser.parseArgs(); + args = parser.parse_args(); } await logsinkInit(args); if (args.logFilters) { diff --git a/lib/parser.js b/lib/parser.js index f07136329..0c79fd6cb 100644 --- a/lib/parser.js +++ b/lib/parser.js @@ -4,128 +4,121 @@ import _ from 'lodash'; import { ArgumentParser } from 'argparse'; import { rootDir } from './utils'; import { DEFAULT_BASE_PATH } from 'appium-base-driver'; +import { + StoreDeprecatedAction, StoreDeprecatedTrueAction, + StoreDeprecatedDefaultCapabilityAction, StoreDeprecatedDefaultCapabilityTrueAction, + DEFAULT_CAPS_ARG, +} from './argsparse-actions'; + const args = [ [['--shell'], { required: false, - defaultValue: null, + default: false, + action: 'store_true', help: 'Enter REPL mode', - nargs: 0, dest: 'shell', }], [['--allow-cors'], { required: false, - defaultValue: false, - action: 'storeTrue', + default: false, + action: 'store_true', help: 'Whether the Appium server should allow web browser connections from any host', - nargs: 0, dest: 'allowCors', }], [['--reboot'], { - defaultValue: false, + default: false, dest: 'reboot', - action: 'storeTrue', + action: 'store_true', required: false, help: '(Android-only) reboot emulator after each session and kill it at the end', - nargs: 0, }], [['--ipa'], { required: false, - defaultValue: null, + default: null, help: '(IOS-only) abs path to compiled .ipa file', - example: '/abs/path/to/my.ipa', dest: 'ipa', }], [['-a', '--address'], { - defaultValue: '0.0.0.0', + default: '0.0.0.0', required: false, - example: '0.0.0.0', help: 'IP Address to listen on', dest: 'address', }], [['-p', '--port'], { - defaultValue: 4723, + default: 4723, required: false, type: 'int', - example: '4723', help: 'port to listen on', dest: 'port', }], [['-pa', '--base-path'], { required: false, - defaultValue: DEFAULT_BASE_PATH, + default: DEFAULT_BASE_PATH, dest: 'basePath', - example: '/path/prefix', help: 'Base path to use as the prefix for all webdriver routes running' + - `on this server (default: ${DEFAULT_BASE_PATH})` + 'on this server' }], [['-ca', '--callback-address'], { required: false, dest: 'callbackAddress', - defaultValue: null, - example: '127.0.0.1', + default: null, help: 'callback IP Address (default: same as --address)', }], [['-cp', '--callback-port'], { required: false, dest: 'callbackPort', - defaultValue: null, + default: null, type: 'int', - example: '4723', help: 'callback port (default: same as port)', }], [['-bp', '--bootstrap-port'], { - defaultValue: 4724, + default: 4724, dest: 'bootstrapPort', required: false, type: 'int', - example: '4724', help: '(Android-only) port to use on device to talk to Appium', }], [['-r', '--backend-retries'], { - defaultValue: 3, + default: 3, dest: 'backendRetries', required: false, type: 'int', - example: '3', help: '(iOS-only) How many times to retry launching Instruments ' + 'before saying it crashed or timed out', }], [['--session-override'], { - defaultValue: false, + default: false, dest: 'sessionOverride', - action: 'storeTrue', + action: 'store_true', required: false, help: 'Enables session override (clobbering)', - nargs: 0, }], [['-l', '--pre-launch'], { - defaultValue: false, + default: false, dest: 'launch', - action: 'storeTrue', + action: 'store_true', required: false, help: 'Pre-launch the application before allowing the first session ' + '(Requires --app and, for Android, --app-pkg and --app-activity)', - nargs: 0, }], [['-g', '--log'], { - defaultValue: null, + default: null, dest: 'logFile', required: false, - example: '/path/to/appium.log', help: 'Also send log output to this file', }], @@ -136,162 +129,148 @@ const args = [ 'error', 'error:debug', 'error:info', 'error:warn', 'error:error', 'debug', 'debug:debug', 'debug:info', 'debug:warn', 'debug:error', ], - defaultValue: 'debug', + default: 'debug', dest: 'loglevel', required: false, - example: 'debug', help: 'log level; default (console[:file]): debug[:debug]', }], [['--log-timestamp'], { - defaultValue: false, + default: false, required: false, help: 'Show timestamps in console output', - nargs: 0, - action: 'storeTrue', + action: 'store_true', dest: 'logTimestamp', }], [['--local-timezone'], { - defaultValue: false, + default: false, required: false, help: 'Use local timezone for timestamps', - nargs: 0, - action: 'storeTrue', + action: 'store_true', dest: 'localTimezone', }], [['--log-no-colors'], { - defaultValue: false, + default: false, required: false, help: 'Do not use colors in console output', - nargs: 0, - action: 'storeTrue', + action: 'store_true', dest: 'logNoColors', }], [['-G', '--webhook'], { - defaultValue: null, + default: null, required: false, - example: 'localhost:9876', dest: 'webhook', - help: 'Also send log output to this HTTP listener', + help: 'Also send log output to this HTTP listener, for example localhost:9876', }], [['--safari'], { - defaultValue: false, - action: 'storeTrue', + default: false, + action: 'store_true', dest: 'safari', required: false, help: '(IOS-Only) Use the safari app', - nargs: 0, }], [['--default-device', '-dd'], { dest: 'defaultDevice', - defaultValue: false, - action: 'storeTrue', + default: false, + action: 'store_true', required: false, help: '(IOS-Simulator-only) use the default simulator that instruments ' + 'launches on its own', }], [['--force-iphone'], { - defaultValue: false, + default: false, dest: 'forceIphone', - action: 'storeTrue', + action: 'store_true', required: false, help: '(IOS-only) Use the iPhone Simulator no matter what the app wants', - nargs: 0, }], [['--force-ipad'], { - defaultValue: false, + default: false, dest: 'forceIpad', - action: 'storeTrue', + action: 'store_true', required: false, help: '(IOS-only) Use the iPad Simulator no matter what the app wants', - nargs: 0, }], [['--tracetemplate'], { - defaultValue: null, + default: null, dest: 'automationTraceTemplatePath', required: false, - example: '/Users/me/Automation.tracetemplate', help: '(IOS-only) .tracetemplate file to use with Instruments', }], [['--instruments'], { - defaultValue: null, + default: null, dest: 'instrumentsPath', - require: false, - example: '/path/to/instruments', + required: false, help: '(IOS-only) path to instruments binary', }], [['--nodeconfig'], { required: false, - defaultValue: null, + default: null, dest: 'nodeconfig', help: 'Configuration JSON file to register appium with selenium grid', - example: '/abs/path/to/nodeconfig.json', }], [['-ra', '--robot-address'], { - defaultValue: '0.0.0.0', + default: '0.0.0.0', dest: 'robotAddress', required: false, - example: '0.0.0.0', help: 'IP Address of robot', }], [['-rp', '--robot-port'], { - defaultValue: -1, + default: -1, dest: 'robotPort', required: false, type: 'int', - example: '4242', help: 'port for robot', }], [['--chromedriver-executable'], { - defaultValue: null, + default: null, dest: 'chromedriverExecutable', required: false, help: 'ChromeDriver executable full path', }], [['--show-config'], { - defaultValue: false, + default: false, dest: 'showConfig', - action: 'storeTrue', + action: 'store_true', required: false, help: 'Show info about the appium server configuration and exit', }], [['--no-perms-check'], { - defaultValue: false, + default: false, dest: 'noPermsCheck', - action: 'storeTrue', + action: 'store_true', required: false, help: 'Bypass Appium\'s checks to ensure we can read/write necessary files', }], [['--strict-caps'], { - defaultValue: false, + default: false, dest: 'enforceStrictCaps', - action: 'storeTrue', + action: 'store_true', required: false, help: 'Cause sessions to fail if desired caps are sent in that Appium ' + 'does not recognize as valid for the selected device', - nargs: 0, }], [['--isolate-sim-device'], { - defaultValue: false, + default: false, dest: 'isolateSimDevice', - action: 'storeTrue', + action: 'store_true', required: false, help: 'Xcode 6 has a bug on some platforms where a certain simulator ' + 'can only be launched without error if all other simulator devices ' + @@ -299,11 +278,10 @@ const args = [ 'devices other than the one being used by Appium. Note that this ' + 'is a permanent deletion, and you are responsible for using simctl ' + 'or xcode to manage the categories of devices used with Appium.', - nargs: 0, }], [['--tmp'], { - defaultValue: null, + default: null, dest: 'tmpDir', required: false, help: 'Absolute path to directory Appium can use to manage temporary ' + @@ -312,7 +290,7 @@ const args = [ }], [['--trace-dir'], { - defaultValue: null, + default: null, dest: 'traceDir', required: false, help: 'Absolute path to directory Appium use to save ios instruments ' + @@ -321,521 +299,456 @@ const args = [ [['--debug-log-spacing'], { dest: 'debugLogSpacing', - defaultValue: false, - action: 'storeTrue', + default: false, + action: 'store_true', required: false, help: 'Add exaggerated spacing in logs to help with visual inspection', }], [['--suppress-adb-kill-server'], { dest: 'suppressKillServer', - defaultValue: false, - action: 'storeTrue', + default: false, + action: 'store_true', required: false, help: '(Android-only) If set, prevents Appium from killing the adb server instance', - nargs: 0, }], [['--long-stacktrace'], { dest: 'longStacktrace', - defaultValue: false, + default: false, required: false, - action: 'storeTrue', + action: 'store_true', help: 'Add long stack traces to log entries. Recommended for debugging only.', }], [['--webkit-debug-proxy-port'], { - defaultValue: 27753, + default: 27753, dest: 'webkitDebugProxyPort', required: false, type: 'int', - example: '27753', help: '(IOS-only) Local port used for communication with ios-webkit-debug-proxy' }], [['--webdriveragent-port'], { - defaultValue: 8100, + default: 8100, dest: 'wdaLocalPort', required: false, type: 'int', - example: '8100', help: '(IOS-only, XCUITest-only) Local port used for communication with WebDriverAgent' }], - [['-dc', '--default-capabilities'], { + [['-dc', DEFAULT_CAPS_ARG], { dest: 'defaultCapabilities', - defaultValue: {}, + default: {}, type: parseDefaultCaps, required: false, - example: '[ \'{"app": "myapp.app", "deviceName": "iPhone Simulator"}\' ' + - '| /path/to/caps.json ]', help: 'Set the default desired capabilities, which will be set on each ' + - 'session unless overridden by received capabilities.' + 'session unless overridden by received capabilities. For example: ' + + '[ \'{"app": "myapp.app", "deviceName": "iPhone Simulator"}\' ' + + '| /path/to/caps.json ]' }], [['--relaxed-security'], { - defaultValue: false, + default: false, dest: 'relaxedSecurityEnabled', - action: 'storeTrue', + action: 'store_true', required: false, help: 'Disable additional security checks, so it is possible to use some advanced features, provided ' + 'by drivers supporting this option. Only enable it if all the ' + 'clients are in the trusted network and it\'s not the case if a client could potentially ' + 'break out of the session sandbox. Specific features can be overridden by ' + 'using the --deny-insecure flag', - nargs: 0 }], [['--allow-insecure'], { dest: 'allowInsecure', - defaultValue: [], + default: [], type: parseSecurityFeatures, required: false, - example: 'execute_driver_script,adb_shell', help: 'Set which insecure features are allowed to run in this server\'s sessions. ' + 'Features are defined on a driver level; see documentation for more details. ' + 'This should be either a comma-separated list of feature names, or a path to ' + 'a file where each feature name is on a line. Note that features defined via ' + - '--deny-insecure will be disabled, even if also listed here.', + '--deny-insecure will be disabled, even if also listed here. For example: ' + + 'execute_driver_script,adb_shell', }], [['--deny-insecure'], { dest: 'denyInsecure', - defaultValue: [], + default: [], type: parseSecurityFeatures, required: false, - example: 'execute_driver_script,adb_shell', help: 'Set which insecure features are not allowed to run in this server\'s sessions. ' + 'Features are defined on a driver level; see documentation for more details. ' + 'This should be either a comma-separated list of feature names, or a path to ' + 'a file where each feature name is on a line. Features listed here will not be ' + 'enabled even if also listed in --allow-insecure, and even if --relaxed-security ' + - 'is turned on.', + 'is turned on. For example: execute_driver_script,adb_shell', }], -]; -const deprecatedArgs = [ [['--command-timeout'], { - defaultValue: 60, + default: 60, dest: 'defaultCommandTimeout', type: 'int', required: false, - help: '[DEPRECATED] No effect. This used to be the default command ' + + deprecated_for: 'newCommandTimeout capability', + action: StoreDeprecatedAction, + help: 'No effect. This used to be the default command ' + 'timeout for the server to use for all sessions (in seconds and ' + 'should be less than 2147483). Use newCommandTimeout cap instead' }], [['-k', '--keep-artifacts'], { - defaultValue: false, + default: false, dest: 'keepArtifacts', - action: 'storeTrue', + action: StoreDeprecatedTrueAction, required: false, - help: '[DEPRECATED] - no effect, trace is now in tmp dir by default and is ' + + help: 'No effect, trace is now in tmp dir by default and is ' + 'cleared before each run. Please also refer to the --trace-dir flag.', - nargs: 0, }], [['--platform-name'], { dest: 'platformName', - defaultValue: null, + default: null, required: false, - deprecatedFor: '--default-capabilities', - example: 'iOS', - help: '[DEPRECATED] - Name of the mobile platform: iOS, Android, or FirefoxOS', + action: StoreDeprecatedDefaultCapabilityAction, + help: 'Name of the mobile platform: iOS, Android, or FirefoxOS', }], [['--platform-version'], { dest: 'platformVersion', - defaultValue: null, + default: null, required: false, - deprecatedFor: '--default-capabilities', - example: '7.1', - help: '[DEPRECATED] - Version of the mobile platform', + action: StoreDeprecatedDefaultCapabilityAction, + help: 'Version of the mobile platform', }], [['--automation-name'], { dest: 'automationName', - defaultValue: null, + default: null, required: false, - deprecatedFor: '--default-capabilities', - example: 'Appium', - help: '[DEPRECATED] - Name of the automation tool: Appium, XCUITest, etc.', + action: StoreDeprecatedDefaultCapabilityAction, + help: 'Name of the automation tool: Appium, XCUITest, etc.', }], [['--device-name'], { dest: 'deviceName', - defaultValue: null, + default: null, required: false, - deprecatedFor: '--default-capabilities', - example: 'iPhone Retina (4-inch), Android Emulator', - help: '[DEPRECATED] - Name of the mobile device to use', + action: StoreDeprecatedDefaultCapabilityAction, + help: 'Name of the mobile device to use, for example: ' + + 'iPhone Retina (4-inch), Android Emulator', }], [['--browser-name'], { dest: 'browserName', - defaultValue: null, + default: null, required: false, - deprecatedFor: '--default-capabilities', - example: 'Safari', - help: '[DEPRECATED] - Name of the mobile browser: Safari or Chrome', + action: StoreDeprecatedDefaultCapabilityAction, + help: 'Name of the mobile browser: Safari or Chrome', }], [['--app'], { dest: 'app', required: false, - defaultValue: null, - deprecatedFor: '--default-capabilities', - help: '[DEPRECATED] - IOS: abs path to simulator-compiled .app file or the bundle_id of the desired target on device; Android: abs path to .apk file', - example: '/abs/path/to/my.app', + default: null, + action: StoreDeprecatedDefaultCapabilityAction, + help: 'IOS: abs path to simulator-compiled .app file or the ' + + 'bundle_id of the desired target on device; Android: abs path to .apk file', }], [['-lt', '--launch-timeout'], { - defaultValue: 90000, + default: 90000, dest: 'launchTimeout', type: 'int', required: false, - deprecatedFor: '--default-capabilities', - help: '[DEPRECATED] - (iOS-only) how long in ms to wait for Instruments to launch', + action: StoreDeprecatedDefaultCapabilityAction, + help: '(iOS-only) how long in ms to wait for Instruments to launch', }], [['--language'], { - defaultValue: null, + default: null, dest: 'language', required: false, - example: 'en', - deprecatedFor: '--default-capabilities', - help: '[DEPRECATED] - Language for the iOS simulator / Android Emulator', + action: StoreDeprecatedDefaultCapabilityAction, + help: 'Language for the iOS simulator / Android Emulator, like: en, es', }], [['--locale'], { - defaultValue: null, + default: null, dest: 'locale', required: false, - example: 'en_US', - deprecatedFor: '--default-capabilities', - help: '[DEPRECATED] - Locale for the iOS simulator / Android Emulator', + action: StoreDeprecatedDefaultCapabilityAction, + help: 'Locale for the iOS simulator / Android Emulator, like en_US, de_DE', }], [['-U', '--udid'], { dest: 'udid', required: false, - defaultValue: null, - example: '1adsf-sdfas-asdf-123sdf', - deprecatedFor: '--default-capabilities', - help: '[DEPRECATED] - Unique device identifier of the connected physical device', + default: null, + action: StoreDeprecatedDefaultCapabilityAction, + help: 'Unique device identifier of the connected physical device', }], [['--orientation'], { dest: 'orientation', - defaultValue: null, + default: null, required: false, - example: 'LANDSCAPE', - deprecatedFor: '--default-capabilities', - help: '[DEPRECATED] - (IOS-only) use LANDSCAPE or PORTRAIT to initialize all requests ' + + action: StoreDeprecatedDefaultCapabilityAction, + help: '(IOS-only) use LANDSCAPE or PORTRAIT to initialize all requests ' + 'to this orientation', }], [['--no-reset'], { - defaultValue: false, + default: false, dest: 'noReset', - action: 'storeTrue', + action: StoreDeprecatedDefaultCapabilityTrueAction, required: false, - deprecatedFor: '--default-capabilities', - help: '[DEPRECATED] - Do not reset app state between sessions (IOS: do not delete app ' + + help: 'Do not reset app state between sessions (IOS: do not delete app ' + 'plist files; Android: do not uninstall app before new session)', - nargs: 0, }], [['--full-reset'], { - defaultValue: false, + default: false, dest: 'fullReset', - action: 'storeTrue', + action: StoreDeprecatedDefaultCapabilityTrueAction, required: false, - deprecatedFor: '--default-capabilities', - help: '[DEPRECATED] - (iOS) Delete the entire simulator folder. (Android) Reset app ' + + help: '(iOS) Delete the entire simulator folder. (Android) Reset app ' + 'state by uninstalling app instead of clearing app data. On ' + 'Android, this will also remove the app after the session is complete.', - nargs: 0, }], [['--app-pkg'], { dest: 'appPackage', - defaultValue: null, + default: null, required: false, - deprecatedFor: '--default-capabilities', - example: 'com.example.android.myApp', - help: '[DEPRECATED] - (Android-only) Java package of the Android app you want to run ' + + action: StoreDeprecatedDefaultCapabilityAction, + help: '(Android-only) Java package of the Android app you want to run ' + '(e.g., com.example.android.myApp)', }], [['--app-activity'], { dest: 'appActivity', - defaultValue: null, + default: null, required: false, - example: 'MainActivity', - deprecatedFor: '--default-capabilities', - help: '[DEPRECATED] - (Android-only) Activity name for the Android activity you want ' + + action: StoreDeprecatedDefaultCapabilityAction, + help: '(Android-only) Activity name for the Android activity you want ' + 'to launch from your package (e.g., MainActivity)', }], [['--app-wait-package'], { dest: 'appWaitPackage', - defaultValue: false, + default: false, required: false, - example: 'com.example.android.myApp', - deprecatedFor: '--default-capabilities', - help: '[DEPRECATED] - (Android-only) Package name for the Android activity you want ' + + action: StoreDeprecatedDefaultCapabilityAction, + help: '(Android-only) Package name for the Android activity you want ' + 'to wait for (e.g., com.example.android.myApp)', }], [['--app-wait-activity'], { dest: 'appWaitActivity', - defaultValue: false, + default: false, required: false, - example: 'SplashActivity', - deprecatedFor: '--default-capabilities', - help: '[DEPRECATED] - (Android-only) Activity name for the Android activity you want ' + + action: StoreDeprecatedDefaultCapabilityAction, + help: '(Android-only) Activity name for the Android activity you want ' + 'to wait for (e.g., SplashActivity)', }], [['--device-ready-timeout'], { dest: 'deviceReadyTimeout', - defaultValue: 5, + default: 5, required: false, type: 'int', - example: '5', - deprecatedFor: '--default-capabilities', - help: '[DEPRECATED] - (Android-only) Timeout in seconds while waiting for device to become ready', + action: StoreDeprecatedDefaultCapabilityAction, + help: '(Android-only) Timeout in seconds while waiting for device to become ready', }], [['--android-coverage'], { dest: 'androidCoverage', - defaultValue: false, + default: false, required: false, - example: 'com.my.Pkg/com.my.Pkg.instrumentation.MyInstrumentation', - deprecatedFor: '--default-capabilities', - help: '[DEPRECATED] - (Android-only) Fully qualified instrumentation class. Passed to -w in ' + - 'adb shell am instrument -e coverage true -w ', + action: StoreDeprecatedDefaultCapabilityAction, + help: '(Android-only) Fully qualified instrumentation class. Passed to -w in ' + + 'adb shell am instrument -e coverage true -w ' + + '(e.g. com.my.Pkg/com.my.Pkg.instrumentation.MyInstrumentation)', }], [['--avd'], { dest: 'avd', - defaultValue: null, + default: null, required: false, - example: '@default', - deprecatedFor: '--default-capabilities', - help: '[DEPRECATED] - (Android-only) Name of the avd to launch', + action: StoreDeprecatedDefaultCapabilityAction, + help: '(Android-only) Name of the avd to launch (e.g. @Nexus_5)', }], [['--avd-args'], { dest: 'avdArgs', - defaultValue: null, + default: null, required: false, - example: '-no-snapshot-load', - deprecatedFor: '--default-capabilities', - help: '[DEPRECATED] - (Android-only) Additional emulator arguments to launch the avd', + action: StoreDeprecatedDefaultCapabilityAction, + help: '(Android-only) Additional emulator arguments to launch the avd (e.g. -no-snapshot-load)', }], [['--use-keystore'], { - defaultValue: false, + default: false, dest: 'useKeystore', - action: 'storeTrue', + action: StoreDeprecatedDefaultCapabilityTrueAction, required: false, - deprecatedFor: '--default-capabilities', - help: '[DEPRECATED] - (Android-only) When set the keystore will be used to sign apks.', + help: '(Android-only) When set the keystore will be used to sign apks.', }], [['--keystore-path'], { - defaultValue: path.resolve(process.env.HOME || process.env.USERPROFILE || '', '.android', 'debug.keystore'), + default: path.resolve(process.env.HOME || process.env.USERPROFILE || '', '.android', 'debug.keystore'), dest: 'keystorePath', required: false, - deprecatedFor: '--default-capabilities', - help: '[DEPRECATED] - (Android-only) Path to keystore', + action: StoreDeprecatedDefaultCapabilityAction, + help: '(Android-only) Path to keystore', }], [['--keystore-password'], { - defaultValue: 'android', + default: 'android', dest: 'keystorePassword', required: false, - deprecatedFor: '--default-capabilities', - help: '[DEPRECATED] - (Android-only) Password to keystore', + action: StoreDeprecatedDefaultCapabilityAction, + help: '(Android-only) Password to keystore', }], [['--key-alias'], { - defaultValue: 'androiddebugkey', + default: 'androiddebugkey', dest: 'keyAlias', required: false, - deprecatedFor: '--default-capabilities', - help: '[DEPRECATED] - (Android-only) Key alias', + action: StoreDeprecatedDefaultCapabilityAction, + help: '(Android-only) Key alias', }], [['--key-password'], { - defaultValue: 'android', + default: 'android', dest: 'keyPassword', required: false, - deprecatedFor: '--default-capabilities', - help: '[DEPRECATED] - (Android-only) Key password', + action: StoreDeprecatedDefaultCapabilityAction, + help: '(Android-only) Key password', }], [['--intent-action'], { dest: 'intentAction', - defaultValue: 'android.intent.action.MAIN', + default: 'android.intent.action.MAIN', required: false, - example: 'android.intent.action.MAIN', - deprecatedFor: '--default-capabilities', - help: '[DEPRECATED] - (Android-only) Intent action which will be used to start activity', + action: StoreDeprecatedDefaultCapabilityAction, + help: '(Android-only) Intent action which will be used to start activity (e.g. android.intent.action.MAIN)', }], [['--intent-category'], { dest: 'intentCategory', - defaultValue: 'android.intent.category.LAUNCHER', + default: 'android.intent.category.LAUNCHER', required: false, - example: 'android.intent.category.APP_CONTACTS', - deprecatedFor: '--default-capabilities', - help: '[DEPRECATED] - (Android-only) Intent category which will be used to start activity', + action: StoreDeprecatedDefaultCapabilityAction, + help: '(Android-only) Intent category which will be used to start activity ' + + '(e.g. android.intent.category.APP_CONTACTS)', }], [['--intent-flags'], { dest: 'intentFlags', - defaultValue: '0x10200000', + default: '0x10200000', required: false, - example: '0x10200000', - deprecatedFor: '--default-capabilities', - help: '[DEPRECATED] - (Android-only) Flags that will be used to start activity', + action: StoreDeprecatedDefaultCapabilityAction, + help: '(Android-only) Flags that will be used to start activity (e.g. 0x10200000)', }], [['--intent-args'], { dest: 'optionalIntentArguments', - defaultValue: null, + default: null, required: false, - example: '0x10200000', - deprecatedFor: '--default-capabilities', - help: '[DEPRECATED] - (Android-only) Additional intent arguments that will be used to ' + - 'start activity', + action: StoreDeprecatedDefaultCapabilityAction, + help: '(Android-only) Additional intent arguments that will be used to start activity (e.g. 0x10200000)', }], [['--dont-stop-app-on-reset'], { dest: 'dontStopAppOnReset', - defaultValue: false, - action: 'storeTrue', + default: false, required: false, - deprecatedFor: '--default-capabilities', - help: '[DEPRECATED] - (Android-only) When included, refrains from stopping the app before restart', + action: StoreDeprecatedDefaultCapabilityTrueAction, + help: '(Android-only) When included, refrains from stopping the app before restart', }], [['--calendar-format'], { - defaultValue: null, + default: null, dest: 'calendarFormat', required: false, - example: 'gregorian', - deprecatedFor: '--default-capabilities', - help: '[DEPRECATED] - (IOS-only) calendar format for the iOS simulator', + action: StoreDeprecatedDefaultCapabilityAction, + help: '(IOS-only) calendar format for the iOS simulator (e.g. gregorian)', }], [['--native-instruments-lib'], { - defaultValue: false, + default: false, dest: 'nativeInstrumentsLib', - action: 'storeTrue', + action: StoreDeprecatedDefaultCapabilityTrueAction, required: false, - deprecatedFor: '--default-capabilities', - help: '[DEPRECATED] - (IOS-only) IOS has a weird built-in unavoidable ' + - 'delay. We patch this in appium. If you do not want it patched, ' + - 'pass in this flag.', - nargs: 0, + help: '(IOS-only) IOS has a weird built-in unavoidable ' + + 'delay. We patch this in appium. If you do not want it patched, pass in this flag.', }], [['--keep-keychains'], { - defaultValue: false, + default: false, dest: 'keepKeyChains', - action: 'storeTrue', + action: StoreDeprecatedDefaultCapabilityTrueAction, required: false, - deprecatedFor: '--default-capabilities', - help: '[DEPRECATED] - (iOS-only) Whether to keep keychains (Library/Keychains) when reset app between sessions', - nargs: 0, + help: '(iOS-only) Whether to keep keychains ' + + '(Library/Keychains) when reset app between sessions', }], [['--localizable-strings-dir'], { required: false, dest: 'localizableStringsDir', - defaultValue: 'en.lproj', - deprecatedFor: '--default-capabilities', - help: '[DEPRECATED] - (IOS-only) the relative path of the dir where Localizable.strings file resides ', - example: 'en.lproj', + default: 'en.lproj', + action: StoreDeprecatedDefaultCapabilityAction, + help: '(IOS-only) the relative path of the dir where Localizable.strings file resides (e.g. en.lproj)', }], [['--show-ios-log'], { - defaultValue: false, + default: false, dest: 'showIOSLog', - action: 'storeTrue', + action: StoreDeprecatedDefaultCapabilityTrueAction, required: false, - deprecatedFor: '--default-capabilities', - help: '[DEPRECATED] - (IOS-only) if set, the iOS system log will be written to the console', - nargs: 0, + help: '(IOS-only) if set, the iOS system log will be written to the console', }], [['--async-trace'], { dest: 'longStacktrace', - defaultValue: false, + default: false, required: false, - action: 'storeTrue', - deprecatedFor: '--long-stacktrace', - help: '[DEPRECATED] - Add long stack traces to log entries. Recommended for debugging only.', + action: StoreDeprecatedDefaultCapabilityTrueAction, + help: 'Add long stack traces to log entries. Recommended for debugging only.', }], [['--chromedriver-port'], { - defaultValue: null, + default: null, dest: 'chromedriverPort', required: false, type: 'int', - example: '9515', - deprecatedFor: '--default-capabilities', - help: '[DEPRECATED] - Port upon which ChromeDriver will run. If not given, Android driver will pick a random available port.', + action: StoreDeprecatedDefaultCapabilityAction, + help: 'Port upon which ChromeDriver will run. If not given, ' + + 'Android driver will pick a random available port.', }], [['--log-filters'], { dest: 'logFilters', - defaultValue: null, + default: null, required: false, help: 'Set the full path to a JSON file containing one or more log filtering rules', - example: '/home/rules.json', }], ]; -function updateParseArgsForDefaultCapabilities (parser) { - // here we want to update the parser.parseArgs() function - // in order to bring together all the args that are actually - // default caps. - // once those deprecated args are actually removed, this - // can also be removed - parser._parseArgs = parser.parseArgs; - parser.parseArgs = function parseArgs (args) { - let parsedArgs = parser._parseArgs(args); - parsedArgs.defaultCapabilities = parsedArgs.defaultCapabilities || {}; - for (let argEntry of deprecatedArgs) { - let arg = argEntry[1].dest; - if (argEntry[1].deprecatedFor === '--default-capabilities') { - if (arg in parsedArgs && parsedArgs[arg] !== argEntry[1].defaultValue) { - parsedArgs.defaultCapabilities[arg] = parsedArgs[arg]; - // j s h i n t can't handle complex interpolated strings - let capDict = {[arg]: parsedArgs[arg]}; - argEntry[1].deprecatedFor = `--default-capabilities ` + - `'${JSON.stringify(capDict)}'`; - } - } - } - return parsedArgs; - }; -} - function parseSecurityFeatures (features) { - const splitter = (splitOn, str) => `${str}`.split(splitOn).map(s => s.trim()).filter(Boolean); + const splitter = (splitOn, str) => `${str}`.split(splitOn) + .map((s) => s.trim()) + .filter(Boolean); let parsedFeatures; try { parsedFeatures = splitter(',', features); } catch (err) { - throw new Error('Could not parse value of --allow/deny-insecure. Should be ' + - 'a list of strings separated by commas, or a path to a file ' + - 'listing one feature name per line.'); + throw new TypeError('Could not parse value of --allow/deny-insecure. Should be ' + + 'a list of strings separated by commas, or a path to a file ' + + 'listing one feature name per line.'); } if (parsedFeatures.length === 1 && fs.existsSync(parsedFeatures[0])) { @@ -844,57 +757,66 @@ function parseSecurityFeatures (features) { const fileFeatures = fs.readFileSync(parsedFeatures[0], 'utf8'); parsedFeatures = splitter('\n', fileFeatures); } catch (err) { - throw new Error(`Attempted to read --allow/deny-insecure feature names ` + - `from file ${parsedFeatures[0]} but got error: ${err.message}`); + throw new TypeError(`Attempted to read --allow/deny-insecure feature names ` + + `from file ${parsedFeatures[0]} but got error: ${err.message}`); } } return parsedFeatures; } -function parseDefaultCaps (caps) { +function parseDefaultCaps (capsOrPath) { + let caps = capsOrPath; + let loadedFromFile = false; try { // use synchronous file access, as `argparse` provides no way of either // awaiting or using callbacks. This step happens in startup, in what is // effectively command-line code, so nothing is blocked in terms of // sessions, so holding up the event loop does not incur the usual // drawbacks. - if (fs.statSync(caps).isFile()) { - caps = fs.readFileSync(caps, 'utf8'); + if (_.isString(capsOrPath) && fs.statSync(capsOrPath).isFile()) { + caps = fs.readFileSync(capsOrPath, 'utf8'); + loadedFromFile = true; } } catch (err) { // not a file, or not readable } - caps = JSON.parse(caps); - if (!_.isPlainObject(caps)) { - throw 'Invalid format for default capabilities'; + try { + const result = JSON.parse(caps); + if (!_.isPlainObject(result)) { + throw new Error(`'${_.truncate(result, {length: 100})}' is not an object`); + } + return result; + } catch (e) { + const msg = loadedFromFile + ? `Default capabilities in '${capsOrPath}' must be a valid JSON` + : `Default capabilities must be a valid JSON`; + throw new TypeError(`${msg}. Original error: ${e.message}`); } - return caps; } function getParser () { - let parser = new ArgumentParser({ - version: require(path.resolve(rootDir, 'package.json')).version, - addHelp: true, + const parser = new ArgumentParser({ + add_help: true, description: 'A webdriver-compatible server for use with native and hybrid iOS and Android applications.', prog: process.argv[1] || 'Appium' }); - let allArgs = _.union(args, deprecatedArgs); - parser.rawArgs = allArgs; - for (let arg of allArgs) { - parser.addArgument(arg[0], arg[1]); + parser.rawArgs = args; + for (const [flagsOrNames, options] of args) { + parser.add_argument(...flagsOrNames, options); } - updateParseArgsForDefaultCapabilities(parser); - + parser.add_argument('-v', '--version', { + action: 'version', + version: require(path.resolve(rootDir, 'package.json')).version, + }); return parser; } function getDefaultArgs () { - let defaults = {}; - for (let [, arg] of args) { - defaults[arg.dest] = arg.defaultValue; - } - return defaults; + return args.reduce((acc, [, {dest, default: defaultValue}]) => { + acc[dest] = defaultValue; + return acc; + }, {}); } export default getParser; diff --git a/package.json b/package.json index 474e1a0fc..39ff19898 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,7 @@ "appium-windows-driver": "1.x", "appium-xcuitest-driver": "^3.22.0", "appium-youiengine-driver": "^1.2.0", - "argparse": "^1.0.10", + "argparse": "^2.0.1", "async-lock": "^1.0.0", "asyncbox": "2.x", "axios": "^0.20.0", diff --git a/test/config-specs.js b/test/config-specs.js index 87d676e8e..71eddff74 100644 --- a/test/config-specs.js +++ b/test/config-specs.js @@ -203,7 +203,7 @@ describe('Config', function () { beforeEach(function () { // give all the defaults for (let rawArg of parser.rawArgs) { - args[rawArg[1].dest] = rawArg[1].defaultValue; + args[rawArg[1].dest] = rawArg[1].default; } }); describe('getNonDefaultArgs', function () { @@ -296,7 +296,7 @@ describe('Config', function () { const defaultArgs = {}; // give all the defaults for (let rawArg of parser.rawArgs) { - defaultArgs[rawArg[1].dest] = rawArg[1].defaultValue; + defaultArgs[rawArg[1].dest] = rawArg[1].default; } let args = {}; beforeEach(function () { diff --git a/test/driver-specs.js b/test/driver-specs.js index 0c8532a03..9e8384bb0 100644 --- a/test/driver-specs.js +++ b/test/driver-specs.js @@ -82,7 +82,7 @@ describe('AppiumDriver', function () { new FakeDriver(), new FakeDriver(), ]; - let mockFakeDrivers = _.map(fakeDrivers, (fd) => {return sinon.mock(fd);}); + let mockFakeDrivers = _.map(fakeDrivers, (fd) => sinon.mock(fd)); mockFakeDrivers[0].expects('deleteSession') .once(); mockFakeDrivers[1].expects('deleteSession') diff --git a/test/parser-specs.js b/test/parser-specs.js index c13f2f8bc..df1cd2da4 100644 --- a/test/parser-specs.js +++ b/test/parser-specs.js @@ -12,8 +12,8 @@ describe('Parser', function () { let p = getParser(); p.debug = true; // throw instead of exit on error; pass as option instead? it('should return an arg parser', function () { - should.exist(p.parseArgs); - p.parseArgs([]).should.have.property('port'); + should.exist(p.parse_args); + p.parse_args([]).should.have.property('port'); }); it('should keep the raw server flags array', function () { should.exist(p.rawArgs); @@ -24,45 +24,45 @@ describe('Parser', function () { } }); it('should throw an error with unknown argument', function () { - (() => {p.parseArgs(['--apple']);}).should.throw(); + (() => {p.parse_args(['--apple']);}).should.throw(); }); it('should parse default capabilities correctly from a string', function () { let defaultCapabilities = {a: 'b'}; - let args = p.parseArgs(['--default-capabilities', JSON.stringify(defaultCapabilities)]); + let args = p.parse_args(['--default-capabilities', JSON.stringify(defaultCapabilities)]); args.defaultCapabilities.should.eql(defaultCapabilities); }); it('should parse default capabilities correctly from a file', function () { let defaultCapabilities = {a: 'b'}; - let args = p.parseArgs(['--default-capabilities', 'test/fixtures/caps.json']); + let args = p.parse_args(['--default-capabilities', 'test/fixtures/caps.json']); args.defaultCapabilities.should.eql(defaultCapabilities); }); it('should throw an error with invalid arg to default capabilities', function () { - (() => {p.parseArgs(['-dc', '42']);}).should.throw(); - (() => {p.parseArgs(['-dc', 'false']);}).should.throw(); - (() => {p.parseArgs(['-dc', 'null']);}).should.throw(); - (() => {p.parseArgs(['-dc', 'does/not/exist.json']);}).should.throw(); + (() => {p.parse_args(['-dc', '42']);}).should.throw(); + (() => {p.parse_args(['-dc', 'false']);}).should.throw(); + (() => {p.parse_args(['-dc', 'null']);}).should.throw(); + (() => {p.parse_args(['-dc', 'does/not/exist.json']);}).should.throw(); }); it('should parse args that are caps into default capabilities', function () { let defaultCapabilities = {localizableStringsDir: '/my/dir'}; - let args = p.parseArgs(['--localizable-strings-dir', '/my/dir']); + let args = p.parse_args(['--localizable-strings-dir', '/my/dir']); args.defaultCapabilities.should.eql(defaultCapabilities); }); it('should parse --allow-insecure correctly', function () { - p.parseArgs([]).allowInsecure.should.eql([]); - p.parseArgs(['--allow-insecure', '']).allowInsecure.should.eql([]); - p.parseArgs(['--allow-insecure', 'foo']).allowInsecure.should.eql(['foo']); - p.parseArgs(['--allow-insecure', 'foo,bar']).allowInsecure.should.eql(['foo', 'bar']); - p.parseArgs(['--allow-insecure', 'foo ,bar']).allowInsecure.should.eql(['foo', 'bar']); + p.parse_args([]).allowInsecure.should.eql([]); + p.parse_args(['--allow-insecure', '']).allowInsecure.should.eql([]); + p.parse_args(['--allow-insecure', 'foo']).allowInsecure.should.eql(['foo']); + p.parse_args(['--allow-insecure', 'foo,bar']).allowInsecure.should.eql(['foo', 'bar']); + p.parse_args(['--allow-insecure', 'foo ,bar']).allowInsecure.should.eql(['foo', 'bar']); }); it('should parse --deny-insecure correctly', function () { - p.parseArgs([]).denyInsecure.should.eql([]); - p.parseArgs(['--deny-insecure', '']).denyInsecure.should.eql([]); - p.parseArgs(['--deny-insecure', 'foo']).denyInsecure.should.eql(['foo']); - p.parseArgs(['--deny-insecure', 'foo,bar']).denyInsecure.should.eql(['foo', 'bar']); - p.parseArgs(['--deny-insecure', 'foo ,bar']).denyInsecure.should.eql(['foo', 'bar']); + p.parse_args([]).denyInsecure.should.eql([]); + p.parse_args(['--deny-insecure', '']).denyInsecure.should.eql([]); + p.parse_args(['--deny-insecure', 'foo']).denyInsecure.should.eql(['foo']); + p.parse_args(['--deny-insecure', 'foo,bar']).denyInsecure.should.eql(['foo', 'bar']); + p.parse_args(['--deny-insecure', 'foo ,bar']).denyInsecure.should.eql(['foo', 'bar']); }); it('should parse --allow and --deny insecure from files', function () { - const parsed = p.parseArgs([ + const parsed = p.parse_args([ '--allow-insecure', ALLOW_FIXTURE, '--deny-insecure', DENY_FIXTURE ]); parsed.allowInsecure.should.eql(['feature1', 'feature2', 'feature3']); From 0a9dab7314c873e721afc9ec18033e407dfc6879 Mon Sep 17 00:00:00 2001 From: Blue Ye Date: Thu, 3 Sep 2020 19:39:35 +0800 Subject: [PATCH 02/44] Add example about how to dump heap (#14694) --- .../en/advanced-concepts/memory-collection.md | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/docs/en/advanced-concepts/memory-collection.md b/docs/en/advanced-concepts/memory-collection.md index dd0a30aab..704aa9cfd 100644 --- a/docs/en/advanced-concepts/memory-collection.md +++ b/docs/en/advanced-concepts/memory-collection.md @@ -22,6 +22,28 @@ Dump files are created in the same folder where the main Appium script has been They have the `.heapsnapshot` extension, and can be loaded into the Chrome Inspector for further investigation. +#### Examples +```shell +# 1. Go to the directory where appium is installed via NPM using one of the two cd command below, depending on appium +# is installed globally or locally +## if your appium is globally installed via NPM with command "npm install -g appium": +cd "$(npm -g root)/appium/" +## else if your appium is locally installed via NPM: +cd "$(npm root)/appium/" + +# 2. Start appium server with heapsnapshot signal +# "&" at the end puts the process at background, so we can continue working on the same terminal +node --heapsnapshot-signal=SIGUSR2 . & + +# 3. Get the PID of previous node process +pid=$! + +# 4. When it's time to dump the heap, issue a SIGUSR2 signal to the PID got above +kill -SIGUSR2 $pid +# Then the heap dump file is created in current directory +``` + + ### Dump file analysis Read the [Rising Stack article](https://blog.risingstack.com/finding-a-memory-leak-in-node-js/) for more details. From bafa8ef757848cf5d430663467cba904f6c92ef7 Mon Sep 17 00:00:00 2001 From: Appium Triager Date: Thu, 3 Sep 2020 23:42:16 -0700 Subject: [PATCH 03/44] docs: Update generated docs [ci skip] (#14705) Co-authored-by: runner --- docs/en/commands/session/execute-driver.md | 23 +++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/docs/en/commands/session/execute-driver.md b/docs/en/commands/session/execute-driver.md index 6515d5d2b..d30c1635f 100644 --- a/docs/en/commands/session/execute-driver.md +++ b/docs/en/commands/session/execute-driver.md @@ -15,7 +15,12 @@ driver.executeDriverScript(script, new ScriptOptions().withTimeout(200)); ```python # Python -# TODO fill out once client code is written +import textwrap +script = """ + const el = await driver.$('~foo'); + await el.click(); +""" +response = driver.execute_driver(script=textwrap.dedent(script)) ``` @@ -40,10 +45,18 @@ await driver.executeDriver(script, {timeout: 200}); ```ruby # Ruby # ruby_lib example -# TODO fill out once client code is written +script = <<-SCRIPT +const status = await driver.status(); +return status; +SCRIPT +driver.execute_driver script: script # ruby_lib_core example -# TODO fill out once client code is written +script = <<-SCRIPT +const status = await driver.status(); +return status; +SCRIPT +@driver.execute_driver script: script ``` @@ -118,10 +131,10 @@ The advantage of this approach of using WebdriverIO code is that you have access |Language|Support|Documentation| |--------|-------|-------------| |[Java](https://github.com/appium/java-client/releases/latest)| All | [javadoc.io](https://javadoc.io/page/io.appium/java-client/latest/io/appium/java_client/ExecutesDriverScript.html#executeDriverScript-java.lang.String-io.appium.java_client.driverscripts.ScriptOptions-) | -|[Python](https://github.com/appium/python-client/releases/latest)| None | | +|[Python](https://github.com/appium/python-client/releases/latest)| None | [appium.github.io](https://appium.github.io/python-client-sphinx/webdriver.extensions.html#module-webdriver.extensions.execute_driver) | |[Javascript (WebdriverIO)](http://webdriver.io/index.html)| None | | |[Javascript (WD)](https://github.com/admc/wd/releases/latest)| None | | -|[Ruby](https://github.com/appium/ruby_lib/releases/latest)| None | | +|[Ruby](https://github.com/appium/ruby_lib/releases/latest)| None | [www.rubydoc.info](https://www.rubydoc.info/github/appium/ruby_lib/master/Appium/Driver#execute_driver-instance_method) | |[PHP](https://github.com/appium/php-client/releases/latest)| None | | |[C#](https://github.com/appium/appium-dotnet-driver/releases/latest)| None | | From 502938d6f125b5eab3b4f260016ba026f5e2dddc Mon Sep 17 00:00:00 2001 From: Kazuaki Matsuo Date: Fri, 4 Sep 2020 22:39:55 +0900 Subject: [PATCH 04/44] chore: bump beta version (#14702) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 39ff19898..740744e9c 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "firefoxos", "testing" ], - "version": "1.18.0-beta.1", + "version": "1.19.0-beta.0", "author": "https://github.com/appium", "license": "Apache-2.0", "repository": { From c440fdc58dd3920a7ae1789765a079d97b97a746 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sun, 6 Sep 2020 19:16:04 +0900 Subject: [PATCH 05/44] chore: Update Dependabot config file (#14712) Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- .dependabot/config.yml | 11 ----------- .github/dependabot.yml | 20 ++++++++++++++++++++ 2 files changed, 20 insertions(+), 11 deletions(-) delete mode 100644 .dependabot/config.yml create mode 100644 .github/dependabot.yml diff --git a/.dependabot/config.yml b/.dependabot/config.yml deleted file mode 100644 index 624326d97..000000000 --- a/.dependabot/config.yml +++ /dev/null @@ -1,11 +0,0 @@ -version: 1 -update_configs: - - package_manager: "javascript" - directory: "/" - update_schedule: "live" - - package_manager: "javascript" - directory: "/sample-code/javascript-wd" - update_schedule: "live" - - package_manager: "javascript" - directory: "/sample-code/javascript-webdriverio" - update_schedule: "live" diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..9438fb991 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,20 @@ +version: 2 +updates: +- package-ecosystem: npm + directory: "/sample-code/javascript-webdriverio" + schedule: + interval: daily + time: "11:00" + open-pull-requests-limit: 10 +- package-ecosystem: npm + directory: "/sample-code/javascript-wd" + schedule: + interval: daily + time: "11:00" + open-pull-requests-limit: 10 +- package-ecosystem: npm + directory: "/" + schedule: + interval: daily + time: "11:00" + open-pull-requests-limit: 10 From 4c8a4798253247e7ed6a8c05e6db51bb9ac308f4 Mon Sep 17 00:00:00 2001 From: Kazuaki Matsuo Date: Tue, 8 Sep 2020 08:55:03 +0900 Subject: [PATCH 06/44] docs: update python get (#14716) --- commands-yml/commands/session/get.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/commands-yml/commands/session/get.yml b/commands-yml/commands/session/get.yml index 6a8c03c6a..0df8f0909 100644 --- a/commands-yml/commands/session/get.yml +++ b/commands-yml/commands/session/get.yml @@ -8,7 +8,7 @@ example_usage: Map caps = driver.getSessionDetails(); python: | - desired_caps = self.driver.desired_capabilities + desired_caps = self.driver.session javascript_wd: | let caps = await driver.sessionCapabilities(); @@ -30,7 +30,7 @@ example_usage: client_docs: java: "https://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/remote/server/ActiveSessions.html#get-org.openqa.selenium.remote.SessionId-" - python: "http://selenium-python.readthedocs.io/api.html#selenium.webdriver.remote.webdriver.WebDriver.desired_capabilities" + python: "https://appium.github.io/python-client-sphinx/webdriver.extensions.html#webdriver.extensions.session.Session.session" javascript_wdio: "https://webdriver.io/docs/api/jsonwp.html#getsession" javascript_wd: "https://github.com/admc/wd/blob/master/lib/commands.js#L227" ruby: "https://www.rubydoc.info/gems/selenium-webdriver/Selenium%2FWebDriver%2FRemote%2FOSS%2FBridge:session_capabilities" From bfe01e13f3301ee7c26ec31c6e11587a6f3cc3ac Mon Sep 17 00:00:00 2001 From: Mykola Mokhnach Date: Tue, 8 Sep 2020 19:55:31 +0200 Subject: [PATCH 07/44] docs: Add documentation about idling resources endpoints (#14718) --- commands-yml/commands/mobile-command.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/commands-yml/commands/mobile-command.yml b/commands-yml/commands/mobile-command.yml index f3500c35a..c1e84134e 100644 --- a/commands-yml/commands/mobile-command.yml +++ b/commands-yml/commands/mobile-command.yml @@ -117,6 +117,9 @@ | mobile:webAtoms | Runs a chain of [webatoms](https://developer.android.com/training/testing/espresso/web). `withElement` and `perform` are basic chain items. Please refer [issue](https://github.com/appium/appium-espresso-driver/pull/380) and [Sample test code](https://github.com/appium/ruby_lib_core/blob/c9062c4b744263d790c7de17263cbd4645cdefc6/test/functional/android/android/mobile_commands_test.rb#L196-L239) as references. | `{webviewElement, forceJavascriptEnabled, methodChain}` | (webdriverio) `{ webviewElement: webviewEl.value, forceJavascriptEnabled: true, methodChain: [...]}` | | mobile:dismissAutofill | Disable autofill dialog in Android O+ | `{element}` | `{element: elementId}` | | mobile:deviceInfo | Gets device information like manufacturer, model, timezone and locale. Read [GetDeviceInfo](https://github.com/appium/appium-espresso-driver/blob/master/espresso-server/app/src/androidTest/java/io/appium/espressoserver/lib/handlers/GetDeviceInfo.kt) for more details. | | | + | mobile:registerIdlingResources | Registers one or more idling resources. Read [idling-resources.js](https://github.com/appium/appium-espresso-driver/blob/master/lib/commands/idling-resources.js) for more details. | `{classNames}` | `{classNames: 'com.package.MyIdlingResource1, com.package.MyIdlingResource2'}` | + | mobile:unregisterIdlingResources | Unregisters one or more idling resources. Read [idling-resources.js](https://github.com/appium/appium-espresso-driver/blob/master/lib/commands/idling-resources.js) for more details. | `{classNames}` | `{classNames: 'com.package.MyIdlingResource1, com.package.MyIdlingResource2'}` | + | mobile:listIdlingResources | Returns the list of canonical class names of currently registered idling resources. Read [idling-resources.js](https://github.com/appium/appium-espresso-driver/blob/master/lib/commands/idling-resources.js) for more details. | | | example_usage: From c8846abf18e0e67e9c7002e706c0696841b1fad9 Mon Sep 17 00:00:00 2001 From: Scott Babcock Date: Sun, 13 Sep 2020 16:36:00 -0700 Subject: [PATCH 08/44] docs: Add white space to work issues of MkDocs tool (#14728) https://github.com/mkdocs/mkdocs/issues/2163 These changes appear to enable to formatting of this document to be rendered correctly. --- docs/en/writing-running-appium/android/android-appbundle.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/en/writing-running-appium/android/android-appbundle.md b/docs/en/writing-running-appium/android/android-appbundle.md index 9e9243b6e..c1ac6d692 100644 --- a/docs/en/writing-running-appium/android/android-appbundle.md +++ b/docs/en/writing-running-appium/android/android-appbundle.md @@ -12,6 +12,7 @@ We can get distributed apk files from the `.aab` file via the CLI. Using the gen 2. Generate the `.apks` file from the `.aab` file - The `.aab` is available over Android Studio 3.2 - You must sign correctly when you generate `.apks` from `.aab`. This step requires data signing. + ```bash $ java -jar apks/bundletool.jar build-apks \ --bundle apks/release/release/app.aab \ # A generated aab file @@ -21,7 +22,9 @@ We can get distributed apk files from the `.aab` file via the CLI. Using the gen --ks-pass pass:kazucocoa \ # Password of the keystore --overwrite # Overwrite any existing apks files ``` + 3. Use the path to the `.apks` file as your `app` capability. + ```ruby desired_capability = caps: { platformName: :android, @@ -42,7 +45,7 @@ You can find another way to get test APKs in https://developer.android.com/guide ## Tips ### Make `bundletool.jar` executable -Make sure the bundletool is executable. +Make sure the bundletool is executable. `$ chmod 655 /path/to/bundletool.jar` can make it executable, for example. ### Test with different languages From f60df166b54627f41cebbcbd4b2a1ed63a0a93aa Mon Sep 17 00:00:00 2001 From: Appium Triager Date: Sun, 13 Sep 2020 16:40:10 -0700 Subject: [PATCH 09/44] docs: Update generated docs [ci skip] (#14729) Co-authored-by: runner --- docs/en/commands/mobile-command.md | 3 +++ docs/en/commands/session/get.md | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/en/commands/mobile-command.md b/docs/en/commands/mobile-command.md index 0bc46460a..89034001b 100644 --- a/docs/en/commands/mobile-command.md +++ b/docs/en/commands/mobile-command.md @@ -165,6 +165,9 @@ List of available commands: | mobile:webAtoms | Runs a chain of [webatoms](https://developer.android.com/training/testing/espresso/web). `withElement` and `perform` are basic chain items. Please refer [issue](https://github.com/appium/appium-espresso-driver/pull/380) and [Sample test code](https://github.com/appium/ruby_lib_core/blob/c9062c4b744263d790c7de17263cbd4645cdefc6/test/functional/android/android/mobile_commands_test.rb#L196-L239) as references. | `{webviewElement, forceJavascriptEnabled, methodChain}` | (webdriverio) `{ webviewElement: webviewEl.value, forceJavascriptEnabled: true, methodChain: [...]}` | | mobile:dismissAutofill | Disable autofill dialog in Android O+ | `{element}` | `{element: elementId}` | | mobile:deviceInfo | Gets device information like manufacturer, model, timezone and locale. Read [GetDeviceInfo](https://github.com/appium/appium-espresso-driver/blob/master/espresso-server/app/src/androidTest/java/io/appium/espressoserver/lib/handlers/GetDeviceInfo.kt) for more details. | | | +| mobile:registerIdlingResources | Registers one or more idling resources. Read [idling-resources.js](https://github.com/appium/appium-espresso-driver/blob/master/lib/commands/idling-resources.js) for more details. | `{classNames}` | `{classNames: 'com.package.MyIdlingResource1, com.package.MyIdlingResource2'}` | +| mobile:unregisterIdlingResources | Unregisters one or more idling resources. Read [idling-resources.js](https://github.com/appium/appium-espresso-driver/blob/master/lib/commands/idling-resources.js) for more details. | `{classNames}` | `{classNames: 'com.package.MyIdlingResource1, com.package.MyIdlingResource2'}` | +| mobile:listIdlingResources | Returns the list of canonical class names of currently registered idling resources. Read [idling-resources.js](https://github.com/appium/appium-espresso-driver/blob/master/lib/commands/idling-resources.js) for more details. | | | [//]: # (DO NOT EDIT THIS FILE! This is an auto-generated file. Editing for this document happens in /commands-yml/commands/mobile-command.yml) diff --git a/docs/en/commands/session/get.md b/docs/en/commands/session/get.md index c6d73b5ac..7bfd24ed3 100644 --- a/docs/en/commands/session/get.md +++ b/docs/en/commands/session/get.md @@ -13,7 +13,7 @@ Map caps = driver.getSessionDetails(); ```python # Python -desired_caps = self.driver.desired_capabilities +desired_caps = self.driver.session ``` @@ -73,7 +73,7 @@ ICapabilities caps = driver.Capabilities; |Language|Support|Documentation| |--------|-------|-------------| |[Java](https://github.com/appium/java-client/releases/latest)| All | [seleniumhq.github.io](https://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/remote/server/ActiveSessions.html#get-org.openqa.selenium.remote.SessionId-) | -|[Python](https://github.com/appium/python-client/releases/latest)| All | [selenium-python.readthedocs.io](http://selenium-python.readthedocs.io/api.html#selenium.webdriver.remote.webdriver.WebDriver.desired_capabilities) | +|[Python](https://github.com/appium/python-client/releases/latest)| All | [appium.github.io](https://appium.github.io/python-client-sphinx/webdriver.extensions.html#webdriver.extensions.session.Session.session) | |[Javascript (WebdriverIO)](http://webdriver.io/index.html)| All | | |[Javascript (WD)](https://github.com/admc/wd/releases/latest)| All | [github.com](https://github.com/admc/wd/blob/master/lib/commands.js#L227) | |[Ruby](https://github.com/appium/ruby_lib/releases/latest)| All | [www.rubydoc.info](https://www.rubydoc.info/gems/selenium-webdriver/Selenium%2FWebDriver%2FRemote%2FOSS%2FBridge:session_capabilities) | From 42f0118c547fd72eb01caefc956c3b882c0f639e Mon Sep 17 00:00:00 2001 From: Dan Date: Wed, 16 Sep 2020 09:46:43 -0700 Subject: [PATCH 10/44] chore: explicitly update appium-xcuitest-driver to 3.27.6 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 740744e9c..a0d2cd4fe 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "appium-tizen-driver": "^1.1.1-beta.4", "appium-uiautomator2-driver": "^1.37.1", "appium-windows-driver": "1.x", - "appium-xcuitest-driver": "^3.22.0", + "appium-xcuitest-driver": "^3.27.6", "appium-youiengine-driver": "^1.2.0", "argparse": "^2.0.1", "async-lock": "^1.0.0", From 05869f7841d3c569f400aae6081aac2a2b1b0309 Mon Sep 17 00:00:00 2001 From: "ian.zhang" Date: Thu, 17 Sep 2020 21:52:05 +0800 Subject: [PATCH 11/44] chore: Replace expired Setup Docs Link (#14739) Pre: The link had been expired since last year http://docs.seleniumhq.org/docs/07_selenium_grid.jsp Post: Latest available docs based on Selenium 3 https://www.selenium.dev/documentation/en/grid/grid_3/setting_up_your_own_grid/ Thanks. --- docs/en/advanced-concepts/grid.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/advanced-concepts/grid.md b/docs/en/advanced-concepts/grid.md index b32778020..5e0cce1cc 100644 --- a/docs/en/advanced-concepts/grid.md +++ b/docs/en/advanced-concepts/grid.md @@ -1,6 +1,6 @@ ## Selenium Grid -You are able to register your appium server with a local [Selenium grid](https://github.com/SeleniumHQ/selenium/wiki/Grid2) ([setup docs](http://docs.seleniumhq.org/docs/07_selenium_grid.jsp)) by using the +You are able to register your appium server with a local [Selenium grid](https://github.com/SeleniumHQ/selenium/wiki/Grid2) ([setup docs](https://www.selenium.dev/documentation/en/grid/grid_3/setting_up_your_own_grid/)) by using the `--nodeconfig` server parameter. ```center From ae3a02634ad4e27354ee9331745853ca6d58cc53 Mon Sep 17 00:00:00 2001 From: Kazuaki Matsuo Date: Mon, 21 Sep 2020 10:53:12 +0900 Subject: [PATCH 12/44] docs: remove broken links (#14746) --- docs/cn/README.md | 2 +- docs/cn/about-appium/platform-support.md | 2 +- docs/cn/advanced-concepts/migrating-to-1-0.md | 4 ++-- docs/cn/advanced-concepts/migrating-to-xcuitest.md | 4 ++-- docs/cn/advanced-concepts/parallel-tests.md | 2 +- docs/cn/advanced-concepts/wda-custom-server.md | 8 ++++---- docs/cn/contributing-to-appium/appium-from-source.md | 2 +- docs/cn/contributing-to-appium/appium-packages.md | 2 +- docs/cn/contributing-to-appium/version-branch-release.md | 6 ++---- .../cn/writing-running-appium/default-capabilities-arg.md | 2 +- docs/cn/writing-running-appium/finding-elements.md | 2 +- .../ios/ios-xctest-mobile-gestures.md | 4 ++-- docs/cn/writing-running-appium/safari-launcher.md | 2 +- .../writing-running-appium/web/ios-webkit-debug-proxy.md | 2 +- 14 files changed, 21 insertions(+), 23 deletions(-) diff --git a/docs/cn/README.md b/docs/cn/README.md index 09b85d123..a9c1ea349 100644 --- a/docs/cn/README.md +++ b/docs/cn/README.md @@ -1 +1 @@ -The latest doc in Chinese, maintained by [TesterHome Appium 中文文档小组](https://testerhome.com/appium-doc-cn). +The latest doc in Chinese, maintained by TesterHome Appium 中文文档小组. diff --git a/docs/cn/about-appium/platform-support.md b/docs/cn/about-appium/platform-support.md index 80f1ab60c..b3fb6642b 100644 --- a/docs/cn/about-appium/platform-support.md +++ b/docs/cn/about-appium/platform-support.md @@ -36,4 +36,4 @@ Appium 支持多种平台以及各种测试方式(native,hybrid,web,真 * [Windows App Testing](/docs/en/drivers/windows.md)(English) -本文由 [thanksdanny](https://testerhome.com/thanksdanny) 翻译,由 [lihuazhang](https://github.com/lihuazhang) 校验。 +本文由 thanksdanny 翻译,由 lihuazhang 校验。 diff --git a/docs/cn/advanced-concepts/migrating-to-1-0.md b/docs/cn/advanced-concepts/migrating-to-1-0.md index 6996b1e41..f9a93279e 100644 --- a/docs/cn/advanced-concepts/migrating-to-1-0.md +++ b/docs/cn/advanced-concepts/migrating-to-1-0.md @@ -21,7 +21,7 @@ from appium import webdriver ```center from selenium import webdriver ``` - + ### 新的 desired capabilities 不再使用以下的 capabilities: @@ -165,4 +165,4 @@ set_context "WEBVIEW" 就是这样!快乐的迁移! -本文由 [高鹏](https://testerhome.com/026) 翻译,由 [lihuazhang](https://github.com/lihuazhang) 校验。 +本文由 高鹏 翻译,由 lihuazhang 校验。 diff --git a/docs/cn/advanced-concepts/migrating-to-xcuitest.md b/docs/cn/advanced-concepts/migrating-to-xcuitest.md index c8e604589..c4292c0a1 100644 --- a/docs/cn/advanced-concepts/migrating-to-xcuitest.md +++ b/docs/cn/advanced-concepts/migrating-to-xcuitest.md @@ -122,5 +122,5 @@ Finally, a list of known issues with the initial 1.6 release (we'll strike throu 我们将尽可能添加缺失的功能,并在以后的 Appium 版本中修复其他已知问题。 - -本文由 [校长](https://testerhome.com/xushizhao) 翻译,由 [lihuazhang](https://github.com/lihuazhang) 校验。 \ No newline at end of file + +本文由 校长 翻译,由 lihuazhang 校验。 diff --git a/docs/cn/advanced-concepts/parallel-tests.md b/docs/cn/advanced-concepts/parallel-tests.md index da232ba21..98b765991 100644 --- a/docs/cn/advanced-concepts/parallel-tests.md +++ b/docs/cn/advanced-concepts/parallel-tests.md @@ -32,4 +32,4 @@ Appium 给用户提供了在一个机器上启动多个 Android sessions 的方 如果你想运行 iOS 的并发测试,你需要使用 Sauce 上传你的 Appium 测试脚本,然后就可以运行多台 iOS 和 Android 的并发测试,只要你的账号允许。查看更多相关信息可以查看 [这里](https://docs.saucelabs.com/tutorials/appium/)。 -本文由 [thanksdanny](https://testerhome.com/thanksdanny) 翻译,由 [lihuazhang](https://github.com/lihuazhang) 校验。 \ No newline at end of file +本文由 thanksdanny 翻译,由 lihuazhang 校验。 diff --git a/docs/cn/advanced-concepts/wda-custom-server.md b/docs/cn/advanced-concepts/wda-custom-server.md index 9efc3fd9c..ebb06e39f 100644 --- a/docs/cn/advanced-concepts/wda-custom-server.md +++ b/docs/cn/advanced-concepts/wda-custom-server.md @@ -6,7 +6,7 @@ Appium 的 iOS 版本的后端用的是[Facebook's WebDriverAgent](https://githu * 如果使用了Appium的默认设置,则不需要如下的步骤。服务器将为您搞定一切,当然你也不能对WDA做太多控制。 * 对连接的被测设备必须有SSH或物理访问权限。 -### 安装WDA +### 安装WDA Appium 会自动下载 WebDriverAgent 源码。如果使用 npm 命令(`npm install -g appium`) 安装Appium的话,通常情况下会保存在/usr/local/lib/node_modules/appium/node_modules/appium-xcuitest-driver/WebDriverAgent 目录下。 如果是首次安装的话,还需要下载一些第三方依赖("carthage"工具就是为此准备的: `brew install carthage`): @@ -33,7 +33,7 @@ npm install -g iproxy Xcode 会成功构建项目并安装到真机/模拟器上,所以您将在苹果系统的桌面上看到 WebDriverAgentRunner 应用程序的图标。 -### 启动WDA +### 启动WDA WebDriverAgent 应用程序扮演一个 REST 服务的角色,接收外部 API 请求,然后传递给被测应用的原生 XCTest 调用。如果在模拟器上运行你的测试,REST 服务的地址将是localhost,如果在有实际的 IP 地址的真实设备上运行,REST 服务的地址将是实际的 ip 地址。我们使用 iproxy 将网络请求路由到通过 USB 连接的真实设备上,这意味着可以使用这个工具将模拟器和真实设备上的 WDA 网络地址统一。 @@ -282,5 +282,5 @@ public class WDAServer { * 模拟器ID可以从 `xcrun simctl list` 输出中解析 * UrlChecker 类是从 org.openqa.selenium.net 包导入的 - -本文由 [simple](https://testerhome.com/simple) 翻译,由 [lihuazhang](https://github.com/lihuazhang) 校验。 \ No newline at end of file + +本文由 simple 翻译,由 lihuazhang 校验。 diff --git a/docs/cn/contributing-to-appium/appium-from-source.md b/docs/cn/contributing-to-appium/appium-from-source.md index cb305c4c0..fa750486d 100644 --- a/docs/cn/contributing-to-appium/appium-from-source.md +++ b/docs/cn/contributing-to-appium/appium-from-source.md @@ -146,4 +146,4 @@ npm run e2e-test 在提交代码之前,请运行 `npm run test` 一些基本测试,并根据代码质量标准检查您的更改。 -本文由 [校长](https://testerhome.com/xushizhao) 翻译,由 [lihuazhang](https://github.com/lihuazhang) 校验。 +本文由 校长 翻译,由 lihuazhang 校验。 diff --git a/docs/cn/contributing-to-appium/appium-packages.md b/docs/cn/contributing-to-appium/appium-packages.md index 4ba873b65..77c68d9e2 100644 --- a/docs/cn/contributing-to-appium/appium-packages.md +++ b/docs/cn/contributing-to-appium/appium-packages.md @@ -253,4 +253,4 @@ - 一堆fs方法 - plist帮助解析和更新plist文件 -本文由 [校长](https://testerhome.com/xushizhao) 翻译,由 [lihuazhang](https://github.com/lihuazhang) 校验。 +本文由 校长 翻译,由 lihuazhang 校验。 diff --git a/docs/cn/contributing-to-appium/version-branch-release.md b/docs/cn/contributing-to-appium/version-branch-release.md index d36c4f02a..488fffedf 100644 --- a/docs/cn/contributing-to-appium/version-branch-release.md +++ b/docs/cn/contributing-to-appium/version-branch-release.md @@ -42,7 +42,7 @@ Appium 使用 [Trunk Based Development](http://paulhammant.com/2013/04/05/what-i 2. 在接下来的六个星期里,这个团队将他们的代码提交到 `master`. 3. 7月15日,执行 RE 创建 20.1-branch。第一个节点被标记为 “20.1.0 Beta”。 4. 一个团队成员开始修复测试版中的错误。这些修复会被提交到 `master`. - 5. 其他贡献者开始按计划提交代码到 `20.2` 中去。这些内容也会被提交到 `master`。 + 5. 其他贡献者开始按计划提交代码到 `20.2` 中去。这些内容也会被提交到 `master`。 6. RE把修复的内容cherry picks到 `20.1-branch`, 并保留 `master` 的其他变更。 7. 该团队庆祝 8 月 1 日发布的所有测试版本都已修复。 8. RE 标签的 HEAD 20.1-branch 为 20.1.0 发布版本。 @@ -50,6 +50,4 @@ Appium 使用 [Trunk Based Development](http://paulhammant.com/2013/04/05/what-i 10. 执行 RE 将主机的崩溃修复程序拉入 20.1-branch,将 HEAD 标记为 20.1.1 并发布修补程序。 11. 一旦 20.2 发布完毕,循环就会重复。 -本文由 [大东](https://testerhome.com/Anikikun) 翻译,由 [lihuazhang](https://github.com/lihuazhang) 校验。 - - +本文由 大东 翻译,由 lihuazhang 校验。 diff --git a/docs/cn/writing-running-appium/default-capabilities-arg.md b/docs/cn/writing-running-appium/default-capabilities-arg.md index 6bb920aba..69e74d7d4 100644 --- a/docs/cn/writing-running-appium/default-capabilities-arg.md +++ b/docs/cn/writing-running-appium/default-capabilities-arg.md @@ -56,6 +56,6 @@ Appium 1.5 移除了大部分旧版本遗留的 CLI 标识;其余部分可转 --- EOF. -本文由 [thanksdanny](https://testerhome.com/thanksdanny) 翻译 +本文由 thanksdanny 翻译 Last english version: a4dd79b8144864cbc034eb97a8f0b5d744e3435c, Oct 24, 2017 diff --git a/docs/cn/writing-running-appium/finding-elements.md b/docs/cn/writing-running-appium/finding-elements.md index cf16de244..a7fa34316 100644 --- a/docs/cn/writing-running-appium/finding-elements.md +++ b/docs/cn/writing-running-appium/finding-elements.md @@ -48,7 +48,7 @@ Appium Desktop 有一个简洁的布局,由源代码树、屏幕截图、记 --- EOF. -本文由 [thanksdanny](https://testerhome.com/thanksdanny) 翻译。由 [lihuazhang](https://github.com/lihuazhang) 校验。 +本文由 thanksdanny 翻译。由 lihuazhang 校验。 翻译:@[Pandorym](https://github.com/Pandorym) Last english version: 438d6c3b38e785edc701354cf660aa9f76baceaf, Apr 11, 2019 diff --git a/docs/cn/writing-running-appium/ios/ios-xctest-mobile-gestures.md b/docs/cn/writing-running-appium/ios/ios-xctest-mobile-gestures.md index 76e7fe6f3..95dd55962 100644 --- a/docs/cn/writing-running-appium/ios/ios-xctest-mobile-gestures.md +++ b/docs/cn/writing-running-appium/ios/ios-xctest-mobile-gestures.md @@ -94,7 +94,7 @@ driver.execute('mobile: doubleTap', {element: element.value.ELEMENT}); * _duration_: 长按的持续时间(秒),浮点型。强制性参数 * _x_: 屏幕x轴坐标点,浮点型. 仅当`element`未设置时才是强制参数 * _y_: 屏幕y轴坐标点,浮点型. 仅当`element`未设置时才是强制参数 - + #### 用法示例 ```csharp @@ -217,4 +217,4 @@ driver.execute_script('mobile: alert', {'action': 'accept', 'buttonLabel': 'My C 查看 [WDA Element Commands API](https://github.com/facebook/WebDriverAgent/blob/master/WebDriverAgentLib/Commands/FBElementCommands.m) 以获取有关在Facebook WebDriverAgent中实现的手势的信息。 -本文由 [大东](https://testerhome.com/Anikikun) 翻译,由 [lihuazhang](https://github.com/lihuazhang) 校验。 \ No newline at end of file +本文由 大东 翻译,由 lihuazhang 校验。 diff --git a/docs/cn/writing-running-appium/safari-launcher.md b/docs/cn/writing-running-appium/safari-launcher.md index 54b27c5ea..553401abe 100644 --- a/docs/cn/writing-running-appium/safari-launcher.md +++ b/docs/cn/writing-running-appium/safari-launcher.md @@ -91,4 +91,4 @@ git clone https://github.com/snevesbarros/SafariLauncher.git } ``` -本文由 [高鹏](https://testerhome.com/026) 翻译,由 [lihuazhang](https://github.com/lihuazhang) 校验。 \ No newline at end of file +本文由 高鹏 翻译,由 lihuazhang 校验。 diff --git a/docs/cn/writing-running-appium/web/ios-webkit-debug-proxy.md b/docs/cn/writing-running-appium/web/ios-webkit-debug-proxy.md index 88bbb9c49..cc2ee63f7 100644 --- a/docs/cn/writing-running-appium/web/ios-webkit-debug-proxy.md +++ b/docs/cn/writing-running-appium/web/ios-webkit-debug-proxy.md @@ -70,4 +70,4 @@ $ sudo make install **注意:** 为了允许建立连接,设备上需要打开 **"Web 检查器"**。在 **设置 > safari > 高级** 中打开。Web 检查器作为 iOS6 的一部分被添加,不在之前的版本中提供,请知悉。 -本文由 [sanlengjingvv](https://testerhome.com/sanlengjingvv) 翻译,由 [lihuazhang](https://github.com/lihuazhang) 校验。 +本文由 sanlengjingvv 翻译,由 lihuazhang 校验。 From 7a6c058e28264e0a9e17344df97182d42f93d771 Mon Sep 17 00:00:00 2001 From: Conrad Magnus Kirschner <35291593+conradkirschner@users.noreply.github.com> Date: Thu, 24 Sep 2020 05:09:16 +0200 Subject: [PATCH 13/44] docs: Update background-app.yml (#14754) Fixing example to reduce confusion --- commands-yml/commands/device/app/background-app.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commands-yml/commands/device/app/background-app.yml b/commands-yml/commands/device/app/background-app.yml index 1c646c778..748d82a6a 100644 --- a/commands-yml/commands/device/app/background-app.yml +++ b/commands-yml/commands/device/app/background-app.yml @@ -9,7 +9,7 @@ description: There are 3 types of parameters which may be passed to this method: - 1. An object that looks like `{"timeout": secs}`, where `secs` is an + 1. An object that looks like `{"seconds": secs}`, where `secs` is an integer designating how long, in seconds, to background the app for. -1 means to deactivate the app entirely. From 939d8d0a18df89e969012a8f071ad392289838ab Mon Sep 17 00:00:00 2001 From: Kazuaki Matsuo Date: Thu, 24 Sep 2020 11:39:57 -0700 Subject: [PATCH 14/44] docs: tweak layout of code block (#14756) --- .../android/android-appbundle.md | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/docs/en/writing-running-appium/android/android-appbundle.md b/docs/en/writing-running-appium/android/android-appbundle.md index c1ac6d692..e50c8f6e6 100644 --- a/docs/en/writing-running-appium/android/android-appbundle.md +++ b/docs/en/writing-running-appium/android/android-appbundle.md @@ -13,39 +13,39 @@ We can get distributed apk files from the `.aab` file via the CLI. Using the gen - The `.aab` is available over Android Studio 3.2 - You must sign correctly when you generate `.apks` from `.aab`. This step requires data signing. - ```bash - $ java -jar apks/bundletool.jar build-apks \ - --bundle apks/release/release/app.aab \ # A generated aab file - --output apks/AppBundleSample.apks \ # An apks file you'd like to out put to - --ks apks/sign \ # Signing keystore - --ks-key-alias key0 \ # Alias of the keytstore - --ks-pass pass:kazucocoa \ # Password of the keystore - --overwrite # Overwrite any existing apks files - ``` +```bash +$ java -jar apks/bundletool.jar build-apks \ + --bundle apks/release/release/app.aab \ # A generated aab file + --output apks/AppBundleSample.apks \ # An apks file you'd like to out put to + --ks apks/sign \ # Signing keystore + --ks-key-alias key0 \ # Alias of the keytstore + --ks-pass pass:kazucocoa \ # Password of the keystore + --overwrite # Overwrite any existing apks files +``` 3. Use the path to the `.apks` file as your `app` capability. - ```ruby - desired_capability = caps: { - platformName: :android, - automationName: 'uiautomator2', - platformVersion: '8.1', - deviceName: 'Android Emulator', - app: "path/to/your.apks", # This line is important - fullReset: true, - ... - } +```ruby +desired_capability = caps: { + platformName: :android, + automationName: 'uiautomator2', + platformVersion: '8.1', + deviceName: 'Android Emulator', + app: "path/to/your.apks", # This line is important + fullReset: true, + ... +} - core = ::Appium::Core.for(desired_capability) - driver = core.start_driver - ``` +core = ::Appium::Core.for(desired_capability) +driver = core.start_driver +``` You can find another way to get test APKs in https://developer.android.com/guide/app-bundle/ ## Tips ### Make `bundletool.jar` executable -Make sure the bundletool is executable. +Make sure the bundletool is executable. `$ chmod 655 /path/to/bundletool.jar` can make it executable, for example. ### Test with different languages From 7a357176b34816bea2c767e81a52d24309750a2f Mon Sep 17 00:00:00 2001 From: Appium Triager Date: Thu, 24 Sep 2020 11:43:54 -0700 Subject: [PATCH 15/44] docs: Update generated docs [ci skip] (#14758) Co-authored-by: runner --- docs/en/commands/device/app/background-app.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/commands/device/app/background-app.md b/docs/en/commands/device/app/background-app.md index ea629b503..c7c9937af 100644 --- a/docs/en/commands/device/app/background-app.md +++ b/docs/en/commands/device/app/background-app.md @@ -55,7 +55,7 @@ driver.BackgroundApp(10) Send the currently active app to the background, and either return after a certain amount of time, or leave the app deactivated. There are 3 types of parameters which may be passed to this method: -1. An object that looks like `{"timeout": secs}`, where `secs` is an +1. An object that looks like `{"seconds": secs}`, where `secs` is an integer designating how long, in seconds, to background the app for. -1 means to deactivate the app entirely. From 45c801956167ceec088af2b2c38346d4cb5fe3c1 Mon Sep 17 00:00:00 2001 From: Mykola Mokhnach Date: Thu, 24 Sep 2020 21:22:44 +0200 Subject: [PATCH 16/44] docs: Document Android mobile gestures (#14757) --- .../android/android-mobile-gestures.md | 259 ++++++++++++++++++ docs/toc.js | 1 + 2 files changed, 260 insertions(+) create mode 100644 docs/en/writing-running-appium/android/android-mobile-gestures.md diff --git a/docs/en/writing-running-appium/android/android-mobile-gestures.md b/docs/en/writing-running-appium/android/android-mobile-gestures.md new file mode 100644 index 000000000..0d05928b0 --- /dev/null +++ b/docs/en/writing-running-appium/android/android-mobile-gestures.md @@ -0,0 +1,259 @@ +## Automating Mobile Gestures With UiAutomator2 Backend + +Touch actions are the most advanced and the most complicated way to +implement any Android gesture. Although, there is a couple of basic +gestures, like swipe, fling or pinch, which are commonly used in +Android applications and for which it makes sense to have shortcuts, +where only high-level options are configurable. + + +### mobile: longClickGesture + +This gesture performs long click action on the given element/coordinates. +Available since Appium v1.19 + +#### Supported arguments + + * _elementId_: The id of the element to be clicked. + If the element is missing then both click offset coordinates must be provided. + If both the element id and offset are provided then the coordinates + are parsed as relative offsets from the top left corner of the element. + * _x_: The x-offset coordinate + * _y_: The y-offset coordinate + * _duration_: Click duration in milliseconds. `500` by default. The value must not be negative + +#### Usage examples + +```java +// Java +((JavascriptExecutor) driver).executeScript("mobile: longClickGesture", ImmutableMap.of( + "elementId", ((RemoteWebElement) element).getId() +)); +``` + +```python +# Python +driver.execute_script('mobile: longClickGesture', {'x': 100, 'y': 100, 'duration': 1000}) +``` + + +### mobile: dragGesture + +This gesture performs drag action from the given element/coordinates to the given point. +Available since Appium v1.19 + +#### Supported arguments + + * _elementId_: The id of the element to be dragged. + If the element id is missing then both start coordinates must be provided. + If both the element id and the start coordinates are provided then these + coordinates are considered as offsets from the top left element corner. + * _startX_: The x-start coordinate + * _startY_: The y-start coordinate + * _endX_: The x-end coordinate. Mandatory argument + * _endY_: The y-end coordinate. Mandatory argument + * _speed_: The speed at which to perform this gesture in pixels per second. + The value must not be negative. The default value is `2500 * displayDensity` + +#### Usage examples + +```java +// Java +((JavascriptExecutor) driver).executeScript("mobile: dragGesture", ImmutableMap.of( + "elementId", ((RemoteWebElement) element).getId(), + "endX", 100, + "endY", 100 +)); +``` + + +### mobile: flingGesture + +This gesture performs fling gesture on the given element/area. +Available since Appium v1.19 + +#### Supported arguments + + * _elementId_: The id of the element to be flinged. + If the element id is missing then fling bounding area must be provided. + If both the element id and the fling bounding area are provided then this + area is effectively ignored. + * _left_: The left coordinate of the fling bounding area + * _top_: The top coordinate of the fling bounding area + * _width_: The width of the fling bounding area + * _height_: The height of the fling bounding area + * _direction_: Direction of the fling. Mandatory value. + Acceptable values are: `up`, `down`, `left` and `right` (case insensitive) + * _speed_: The speed at which to perform this + gesture in pixels per second. The value must be greater than the minimum fling + velocity for the given view (50 by default). The default value is `7500 * displayDensity` + +#### Returned value + +The returned value is a boolean one and equals to `true` if the object can still scroll in the given direction + +#### Usage examples + +```java +// Java +boolean canScrollMore = (Boolean) ((JavascriptExecutor) driver).executeScript("mobile: flingGesture", ImmutableMap.of( + "elementId", ((RemoteWebElement) element).getId(), + "direction", "down", + "speed", 500 +)); +``` + + +### mobile: pinchOpenGesture + +This gesture performs pinch-open gesture on the given element/area. +Available since Appium v1.19 + +#### Supported arguments + + * _elementId_: The id of the element to be pinched. + If the element id is missing then pinch bounding area must be provided. + If both the element id and the pinch bounding area are provided then the + area is effectively ignored. + * _left_: The left coordinate of the pinch bounding area + * _top_: The top coordinate of the pinch bounding area + * _width_: The width of the pinch bounding area + * _height_: The height of the pinch bounding area + * _percent_: The size of the pinch as a percentage of the pinch area size. + Valid values must be float numbers in range 0..1, where 1.0 is 100%. + Mandatory value. + * _speed_: The speed at which to perform this gesture in pixels per second. + The value must not be negative. The default value is `2500 * displayDensity` + +#### Usage examples + +```java +// Java +((JavascriptExecutor) driver).executeScript("mobile: pinchOpenGesture", ImmutableMap.of( + "elementId", ((RemoteWebElement) element).getId(), + "percent", 0.75 +)); +``` + + +### mobile: pinchCloseGesture + +This gesture performs pinch-close gesture on the given element/area. +Available since Appium v1.19 + +#### Supported arguments + + * _elementId_: The id of the element to be pinched. + If the element id is missing then pinch bounding area must be provided. + If both the element id and the pinch bounding area are provided then the + area is effectively ignored. + * _left_: The left coordinate of the pinch bounding area + * _top_: The top coordinate of the pinch bounding area + * _width_: The width of the pinch bounding area + * _height_: The height of the pinch bounding area + * _percent_: The size of the pinch as a percentage of the pinch area size. + Valid values must be float numbers in range 0..1, where 1.0 is 100%. + Mandatory value. + * _speed_: The speed at which to perform this gesture in pixels per second. + The value must not be negative. The default value is `2500 * displayDensity` + +#### Usage examples + +```java +// Java +((JavascriptExecutor) driver).executeScript("mobile: pinchCloseGesture", ImmutableMap.of( + "elementId", ((RemoteWebElement) element).getId(), + "percent", 0.75 +)); +``` + +```python +# Python +can_scroll_more = driver.execute_script('mobile: pinchCloseGesture', { + 'elementId': element.id, + 'percent': 0.75 +}) +``` + + +### mobile: swipeGesture + +This gesture performs swipe gesture on the given element/area. +Available since Appium v1.19 + +#### Supported arguments + + * _elementId_: The id of the element to be swiped. + If the element id is missing then swipe bounding area must be provided. + If both the element id and the swipe bounding area are provided then the + area is effectively ignored. + * _left_: The left coordinate of the swipe bounding area + * _top_: The top coordinate of the swipe bounding area + * _width_: The width of the swipe bounding area + * _height_: The height of the swipe bounding area + * _direction_: Swipe direction. Mandatory value. + Acceptable values are: `up`, `down`, `left` and `right` (case insensitive) + * _percent_: The size of the swipe as a percentage of the swipe area size. + Valid values must be float numbers in range 0..1, where 1.0 is 100%. + Mandatory value. + * _speed_: The speed at which to perform this gesture in pixels per second. + The value must not be negative. The default value is `5000 * displayDensity` + +#### Usage examples + +```java +// Java +((JavascriptExecutor) driver).executeScript("mobile: swipeGesture", ImmutableMap.of( + "left", 100, "top", 100, "width", 200, "height", 200, + "direction", "left", + "percent", 0.75 +)); +``` + + +### mobile: scrollGesture + +This gesture performs scroll gesture on the given element/area. +Available since Appium v1.19 + +#### Supported arguments + + * _elementId_: The id of the element to be scrolled. + If the element id is missing then scroll bounding area must be provided. + If both the element id and the scroll bounding area are provided then this + area is effectively ignored. + * _left_: The left coordinate of the scroll bounding area + * _top_: The top coordinate of the scroll bounding area + * _width_: The width of the scroll bounding area + * _height_: The height of the scroll bounding area + * _direction_: Scrolling direction. Mandatory value. + Acceptable values are: `up`, `down`, `left` and `right` (case insensitive) + * _percent_: The size of the scroll as a percentage of the scrolling area size. + Valid values must be float numbers greater than zero, where 1.0 is 100%. + Mandatory value. + * _speed_: The speed at which to perform this gesture in pixels per second. + The value must not be negative. The default value is `5000 * displayDensity` + +#### Returned value + +The returned value is a boolean one and equals to `true` if the object can still scroll in the given direction + +#### Usage examples + +```java +// Java +boolean canScrollMore = (Boolean) ((JavascriptExecutor) driver).executeScript("mobile: scroll", ImmutableMap.of( + "left", 100, "top", 100, "width", 200, "height", 200, + "direction", "down", + "percent", 3.0 +)); +``` + +```python +# Python +can_scroll_more = driver.execute_script('mobile: scroll', { + 'left': 100, 'top': 100, 'width': 200, 'height': 200, + 'direction': 'down', + 'percent': 3.0 +}) +``` diff --git a/docs/toc.js b/docs/toc.js index 357c66103..57b59d3fe 100644 --- a/docs/toc.js +++ b/docs/toc.js @@ -258,6 +258,7 @@ module.exports = { ['Activities Startup Troubleshooting Guide', 'activity-startup.md'], ['How To Execute Shell Commands On The Remote Device', 'android-shell.md'], ['Android Device Screen Streaming', 'android-screen-streaming.md'], + ['Automating Mobile Gestures With UiAutomator2 Backend', 'android-mobile-gestures.md'], ['How To Emulate IME Actions Generation', 'android-ime.md'], ['How To Test Android App Bundle', 'android-appbundle.md'] ]], From a29f545a78b5d27c63d69486efad9b9b8530e283 Mon Sep 17 00:00:00 2001 From: Roubal Sehgal <44202409+roubalsehgal@users.noreply.github.com> Date: Sun, 27 Sep 2020 12:00:00 +0530 Subject: [PATCH 17/44] docs: Update README.MD (#14762) --- README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 27eb0f4e8..edd50fbea 100644 --- a/README.md +++ b/README.md @@ -8,8 +8,8 @@ [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fappium%2Fappium.svg?type=shield)](https://app.fossa.io/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fappium%2Fappium?ref=badge_shield) -Appium is an open source, cross-platform test automation tool for native, -hybrid and mobile web and desktop apps. We support simulators (iOS), emulators +Appium is an open-source, cross-platform test automation tool for native, +hybrid, and mobile web and desktop apps. We support simulators (iOS), emulators (Android), and real devices (iOS, Android, Windows, Mac). Want to skip straight to the action? Check out our [getting @@ -35,7 +35,7 @@ specific information about how that driver works and how to set it up: ### Why Appium? 1. You don't have to recompile your app or modify it in any way, due - to use of standard automation APIs on all platforms. + to the use of standard automation APIs on all platforms. 2. You can write tests with your favorite dev tools using any [WebDriver](https://w3c.github.io/webdriver/webdriver-spec.html)-compatible language such as Java, Objective-C, JavaScript (Node), PHP, Python, Ruby, @@ -43,22 +43,22 @@ specific information about how that driver works and how to set it up: client libraries](/docs/en/about-appium/appium-clients.md). 3. You can use any testing framework. 4. Appium has built-in mobile web and hybrid app support. Within the same - script you can switch seamlessly between native app automation and webview + script, you can switch seamlessly between native app automation and webview automation, all using the WebDriver model that's already the standard for web automation. Investing in the [WebDriver](https://w3c.github.io/webdriver/webdriver-spec.html) protocol means -you are betting on a single, free and open protocol for testing that has become +you are betting on a single, free, and open protocol for testing that has become a web standard. Don't lock yourself into a proprietary stack. For example, if you use Apple's XCUITest library without Appium you can only write tests using Obj-C/Swift, and you can only run tests through Xcode. -Similarly, with Google's UiAutomator or Espresso you can only write tests in +Similarly, with Google's UiAutomator or Espresso, you can only write tests in Java/Kotlin. Appium opens up the possibility of true cross-platform native app automation, for mobile and beyond. Finally! -If you're new to Appium, or want a more comprehensive description of what this is all +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). @@ -74,7 +74,7 @@ Node.js 10+. Check out our [Getting Started](/docs/en/about-appium/getting-started.md) guide to get going with Appium. -There is also sample code that contains [many examples of tests in a variety +There is also a sample code that contains [many examples of tests in a variety of different languages](https://github.com/appium/appium/tree/master/sample-code)! ### Documentation @@ -92,7 +92,7 @@ Once the PR has been merged, the latest documentation will be in [appium.io](htt ### Contributing Please take a look at our [contribution documentation](CONTRIBUTING.md) -for instructions on how to build, test and run Appium from source. +for instructions on how to build, test, and run Appium from the source. ### Roadmap From bd20249ff5d21e09e0fe7bcf1ba2c80229dea10b Mon Sep 17 00:00:00 2001 From: mwakizaka Date: Thu, 1 Oct 2020 16:31:20 +0900 Subject: [PATCH 18/44] docs: mobile:getContexts for Android (#14765) --- commands-yml/commands/context/get-contexts.yml | 9 ++++----- commands-yml/commands/mobile-command.yml | 1 + 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/commands-yml/commands/context/get-contexts.yml b/commands-yml/commands/context/get-contexts.yml index 7cdaaa1a9..7e6efb2d5 100644 --- a/commands-yml/commands/context/get-contexts.yml +++ b/commands-yml/commands/context/get-contexts.yml @@ -8,11 +8,10 @@ description: For information on the format of the context names, see the [get context documentation](/docs/en/commands/context/get-context.md). - On iOS, using the XCUITest driver, one can use the `mobile: getContexts` - [mobile command](/docs/en/commands/mobile-command.md) as an alternative to - the standard method in order to get the title and url associated with each - context as additional metadata. - + [mobile command](/docs/en/commands/mobile-command.md) + `mobile: getContexts` is available on iOS (XCUITest) and Android + (UIAutomator2 and Espresso) to get more detailed contexts. + For information on contexts, see Appium's [hybrid automation docs](/docs/en/writing-running-appium/web/hybrid.md). example_usage: diff --git a/commands-yml/commands/mobile-command.yml b/commands-yml/commands/mobile-command.yml index c1e84134e..b85e2fcab 100644 --- a/commands-yml/commands/mobile-command.yml +++ b/commands-yml/commands/mobile-command.yml @@ -83,6 +83,7 @@ | mobile:deleteFile | Delete a file on the device under test. The remote path value should be the full path or a file inside an application bundle. | `{remotePath}` | `{remotePath: '@io.appium.example/path/in/bundle'}`, `{remotePath: '/tmp/data/file'}` | | mobile:startService | Starts the given service by calling `am start-service` or `am start-foreground-service` under the hood since Appium 1.18.0. The `intent` argument is mandatory. `user` is the ID of the user the service should be started for (the current user ID is used by default). If `foreground` is set to `true` then the service will be started in foreground. | `{intent, user, foreground}` | `{intent: 'my.app/my.activity', foreground: true}` | | mobile:stopService | Stops the given service by calling `am stop-service` under the hood since Appium 1.18.0. The `intent` argument is mandatory. `user` is the ID of the user the service should be stopped for (the current user ID is used by default). | `{intent, user}` | `{intent: 'my.app/my.activity'}` | + | mobile:getContexts | Retrieve available contexts, along with details associated with each webview (see [get contexts](/docs/en/commands/context/get-contexts.md)) in JSON structure. The details of each webview includes such as android package name and its window handles details (see [get handles](/docs/en/commands/web/window/get-handles.md)). The details of each window handle (which donated as `pages` in the JSON) contains such as url, title and id. The id is part of a window handle name. Therefore, by concatenating a string like "CDwindow-" + id, you can use the string for switching to window (see [set window](/docs/en/commands/web/window/set-window.md).) | | | ### Android (UiAutomator2 only) | Command | Description | Argument | Argument Example | From 45a384075b02a8cc7f8a6d757ba5b3bf3e598d09 Mon Sep 17 00:00:00 2001 From: Mykola Mokhnach Date: Thu, 1 Oct 2020 09:56:20 +0200 Subject: [PATCH 19/44] docs: Add a hint about espressoBuildConfig change (#14776) --- docs/en/writing-running-appium/caps.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/en/writing-running-appium/caps.md b/docs/en/writing-running-appium/caps.md index ef5258d49..459a6e426 100644 --- a/docs/en/writing-running-appium/caps.md +++ b/docs/en/writing-running-appium/caps.md @@ -173,7 +173,7 @@ These Capabilities are available only on the [Espresso Driver](/docs/en/drivers/ |Capability|Description|Values| |----|-----------|-------| |`espressoServerLaunchTimeout`|Timeout in milliseconds used to wait for the Espresso server to launch. Defaults to `30000` |e.g., `50000`| -|`espressoBuildConfig`|Path to the Espresso server build configuration JSON (see below)|e.g., `/projects/myapp-tests/buildconfig.json`| +|`espressoBuildConfig`|Path to the Espresso server build configuration JSON (see below) or a stringified JSON itself (only supported since server version 1.19)|e.g., `/projects/myapp-tests/buildconfig.json`| |`showGradleLog`|Whether to pipe Gradle build log for the Espresso server to the Appium log. Defaults to `false`|e.g., `true`| |`skipServerInstallation`|Skip Espresso server build and apk installation. This option could break proper Espresso server setup for the particular Appium version, but it can improve startup performance when the proper Espresso server and the proper app under test are already installed on the device. Please, make sure not to enable this option if the Espresso server or the application under test needs an update. Defaults to `false` | `true` or `false`| |`intentOptions`|Intent options which will be used to start the application under test. It can set intent options such as `action`, `categories` and `component` as JSON format. Please read [#538](https://github.com/appium/appium-espresso-driver/issues/538) as an example usage. | e.g. `{"action": "android.intent.action.MAIN", "categories": "android.intent.category.LAUNCHER", "component": "com.appium/.launcher.MainActivity"` | @@ -281,6 +281,6 @@ Driver](/docs/en/drivers/ios-uiautomation.md). ### WinAppDriver Only (For WinAppDriver specific capabilities, please refer to the documentation on the [Appium Windows Driver repo](https://github.com/appium/appium-windows-driver#windowsdriver-specific-capabilities) itself.) -### Flutter driver only +### Flutter driver only (For FlutterDriver specific capabilities, examples please refer to the documentation on the [Flutter Driver repo](https://github.com/truongsinh/appium-flutter-driver#desired-capabilities-for-flutter-driver-only) itself.) From 2b57b762a4cae95329d45fc7082ef08fdffc7fcb Mon Sep 17 00:00:00 2001 From: Appium Triager Date: Thu, 1 Oct 2020 01:20:20 -0700 Subject: [PATCH 20/44] docs: Update generated docs [ci skip] (#14777) Co-authored-by: runner --- docs/en/commands/context/get-contexts.md | 2 +- docs/en/commands/mobile-command.md | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/en/commands/context/get-contexts.md b/docs/en/commands/context/get-contexts.md index 78810f2d7..f8a5802f8 100644 --- a/docs/en/commands/context/get-contexts.md +++ b/docs/en/commands/context/get-contexts.md @@ -57,7 +57,7 @@ List AllContexts = new List(); ## Description Retrieve all the contexts available to be automated. This will include, at least, the native context. There can also be zero or more web view contexts. For information on the format of the context names, see the [get context documentation](/docs/en/commands/context/get-context.md). -On iOS, using the XCUITest driver, one can use the `mobile: getContexts` [mobile command](/docs/en/commands/mobile-command.md) as an alternative to the standard method in order to get the title and url associated with each context as additional metadata. +[mobile command](/docs/en/commands/mobile-command.md) `mobile: getContexts` is available on iOS (XCUITest) and Android (UIAutomator2 and Espresso) to get more detailed contexts. For information on contexts, see Appium's [hybrid automation docs](/docs/en/writing-running-appium/web/hybrid.md). diff --git a/docs/en/commands/mobile-command.md b/docs/en/commands/mobile-command.md index 89034001b..2848adba5 100644 --- a/docs/en/commands/mobile-command.md +++ b/docs/en/commands/mobile-command.md @@ -131,6 +131,7 @@ List of available commands: | mobile:deleteFile | Delete a file on the device under test. The remote path value should be the full path or a file inside an application bundle. | `{remotePath}` | `{remotePath: '@io.appium.example/path/in/bundle'}`, `{remotePath: '/tmp/data/file'}` | | mobile:startService | Starts the given service by calling `am start-service` or `am start-foreground-service` under the hood since Appium 1.18.0. The `intent` argument is mandatory. `user` is the ID of the user the service should be started for (the current user ID is used by default). If `foreground` is set to `true` then the service will be started in foreground. | `{intent, user, foreground}` | `{intent: 'my.app/my.activity', foreground: true}` | | mobile:stopService | Stops the given service by calling `am stop-service` under the hood since Appium 1.18.0. The `intent` argument is mandatory. `user` is the ID of the user the service should be stopped for (the current user ID is used by default). | `{intent, user}` | `{intent: 'my.app/my.activity'}` | +| mobile:getContexts | Retrieve available contexts, along with details associated with each webview (see [get contexts](/docs/en/commands/context/get-contexts.md)) in JSON structure. The details of each webview includes such as android package name and its window handles details (see [get handles](/docs/en/commands/web/window/get-handles.md)). The details of each window handle (which donated as `pages` in the JSON) contains such as url, title and id. The id is part of a window handle name. Therefore, by concatenating a string like "CDwindow-" + id, you can use the string for switching to window (see [set window](/docs/en/commands/web/window/set-window.md).) | | | ### Android (UiAutomator2 only) | Command | Description | Argument | Argument Example | From be9baab52d1900ee5b6c323ddc14196e3e7cd31d Mon Sep 17 00:00:00 2001 From: Victor Gladkikh Date: Fri, 2 Oct 2020 19:39:05 +0300 Subject: [PATCH 21/44] docs: Just a small typo fix (#14774) --- docs/en/writing-running-appium/security.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/writing-running-appium/security.md b/docs/en/writing-running-appium/security.md index 0e9db346b..523f47760 100644 --- a/docs/en/writing-running-appium/security.md +++ b/docs/en/writing-running-appium/security.md @@ -25,7 +25,7 @@ Each Appium driver is responsible for its own security, and can create its own f |`shutdown_other_sims`|Allow any session to use a capability to shutdown any running simulators on the host|XCUITest| |`perf_record`|Allow recording the system performance and other metrics of the simulator|XCUITest| |`record_audio`|Allow recording of host machine audio inputs|XCUITest| -|`chromedriver_autodownload`|Allow to downalod ChromeDriver automatically if Appium does not have proper the version |Android, UiAutomator2, Espresso| +|`chromedriver_autodownload`|Allow to download ChromeDriver automatically if Appium does not have proper the version |Android, UiAutomator2, Espresso| |`execute_driver_script`| Allows to send a request which has multiple Appium commands. Read [documentation](https://github.com/appium/appium/blob/master/docs/en/commands/session/execute-driver.md) for more details|All| Please see also below links. They might have additional flags. From 6014b96ba30f500df1bbe5495ef3b5c2953825a9 Mon Sep 17 00:00:00 2001 From: Kazuaki Matsuo Date: Thu, 8 Oct 2020 01:00:17 -0700 Subject: [PATCH 22/44] docs: add description about com.google.android.material:material in navigateTo (#14787) * docs: add description about com.google.android.material:material in navigateTo * update caps, too * tweak * update for example * Update caps.md --- commands-yml/commands/mobile-command.yml | 2 +- docs/en/writing-running-appium/caps.md | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/commands-yml/commands/mobile-command.yml b/commands-yml/commands/mobile-command.yml index b85e2fcab..73d564b30 100644 --- a/commands-yml/commands/mobile-command.yml +++ b/commands-yml/commands/mobile-command.yml @@ -110,7 +110,7 @@ | mobile:closeDrawer | Close drawer by [DrawerAction](https://developer.android.com/reference/android/support/test/espresso/contrib/DrawerActions) with [gravity](https://developer.android.com/reference/android/view/Gravity). `gravity` is option. The default is [GravityCompat.START](https://developer.android.com/reference/android/support/v4/view/GravityCompat.html#START). This method blocks until the drawer is fully closed. No operation if the drawer is already closed. | `{element, gravity}` | `{ element: element_id }`, `{ element: elementId, gravity: 3 }` | | mobile:setDate | Set date by [PickerActions#setDate](https://developer.android.com/reference/android/support/test/espresso/contrib/PickerActions.html#setDate(int,%20int,%20int)) for [DataPicker](https://developer.android.com/reference/android/widget/DatePicker). | `{element, year, monthOfYear, dayOfMonth}` | `{element: elementId, year: 2018, monthOfYear: 12, dayOfMonth: 1}` | | mobile:setTime | Set time by [PickerActions#setTime](https://developer.android.com/reference/android/support/test/espresso/contrib/PickerActions.html#setTime(int,%20int)) for [TimePicker](https://developer.android.com/reference/android/widget/TimePicker). | `{element, hours, minutes}` | `{element: elementId, hours: 12, minutes: 10}` | - | mobile:navigateTo | Action to [NavigationView](https://developer.android.com/reference/android/support/design/widget/NavigationView) by [NavigationViewActions#navigateTo](https://developer.android.com/reference/android/support/test/espresso/contrib/NavigationViewActions.html#navigateTo(int)). The view must be a child of a DrawerLayout, of type NavigationView, visible on screen and displayed on screen. | `{element, menuItemId}` | `{element: elementId, menuItemId: 1}` | + | mobile:navigateTo | Action to [NavigationView](https://developer.android.com/reference/android/support/design/widget/NavigationView) by [NavigationViewActions#navigateTo](https://developer.android.com/reference/android/support/test/espresso/contrib/NavigationViewActions.html#navigateTo(int)). The view must be a child of a DrawerLayout, of type NavigationView, visible on screen and displayed on screen. `com.google.android.material:material` as `additionalAndroidTestDependencies` capability is necessary. | `{element, menuItemId}` | `{element: elementId, menuItemId: 1}` | | mobile:scrollToPage | Action to [ViewPager](https://developer.android.com/reference/android/support/v4/view/ViewPager) by [ViewPagerActions](https://developer.android.com/reference/android/support/test/espresso/contrib/ViewPagerActions). `scrollTo` must be one of `first`, `last`, `left`, `right`. `scrollTo` is used by default if `scrollTo` and `scrollToPage` are provided. | `{scrollTo, scrollToPage, smoothScroll}` | `{element: elementId, scrollTo: 'left', smoothScroll: true}`, `{element: elementId, scrollToPage: 2}`| | mobile:backdoor | Invoke arbitrary methods defined in Android app. The methods must be `public` in Java and must be `open` in Kotlin. `target` is `activity`, `application` or `element`. `methods` are methods you would like to invoke. `element` is mandatory if `target` is `element`. Read docstring of _mobileBackdoor_ method [here](https://github.com/appium/appium-espresso-driver/blob/master/lib/commands/general.js) for more details | `{target, methods, element}` | `{target: 'activity', methods: [{name: 'method1'}, {name: 'method2', args: [{value: 'hello appium', type: 'java.lang.String'}] }] }`, `{target: 'element', element: elementId, [{name: 'getTypeface'}, {name: 'isItalic'}]}` | | mobile:flashElement | Flashes an element on the screen to visually confirm you are operating on the correct element. Can optionally set `durationMillis` (in ms) and `repeatCount` of animation| `{element, durationMillis, repeatCount}` | `{element: "1234-abcd-5678", durationMillis: 1000, repeatCount: 10}` | diff --git a/docs/en/writing-running-appium/caps.md b/docs/en/writing-running-appium/caps.md index 459a6e426..4fb971c30 100644 --- a/docs/en/writing-running-appium/caps.md +++ b/docs/en/writing-running-appium/caps.md @@ -221,9 +221,10 @@ The module versions enumerated under `toolsVersion` are only used to build the s ***Application dependencies*** -`additionalAppDependencies` array specifies additional dependencies of the application under test that build tools should know about when building the Espresso server. For example: `[ "com.google.android.material:material:1.0.0" ]`. +`additionalAppDependencies` and `additionalAndroidTestDependencies` array specify additional dependencies of the application under test that build tools should know about when building the Espresso server. +For example: `"additionalAndroidTestDependencies": [ "com.google.android.material:material:1.0.0" ]`. -Items belonging to this array are translated to `implementation` lines in Gradle build files of the Espresso server. +Items belonging to `additionalAppDependencies` array are translated to `implementation` lines in Gradle build files of the Espresso server. `additionalAndroidTestDependencies` are translated to `androidTestImplementation`. ### iOS Only @@ -283,4 +284,3 @@ Driver](/docs/en/drivers/ios-uiautomation.md). ### Flutter driver only (For FlutterDriver specific capabilities, examples please refer to the documentation on the [Flutter Driver repo](https://github.com/truongsinh/appium-flutter-driver#desired-capabilities-for-flutter-driver-only) itself.) - From 047670313704ab1fc97d5a9cd5aac3f1168745b8 Mon Sep 17 00:00:00 2001 From: Mykola Mokhnach Date: Thu, 8 Oct 2020 20:09:36 +0200 Subject: [PATCH 23/44] docs: Add the description of velocity argument (#14793) * docs: Add the description of velocity argument * tune line breaks --- .../ios/ios-xctest-mobile-gestures.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/docs/en/writing-running-appium/ios/ios-xctest-mobile-gestures.md b/docs/en/writing-running-appium/ios/ios-xctest-mobile-gestures.md index 9338367fb..2376ee4db 100644 --- a/docs/en/writing-running-appium/ios/ios-xctest-mobile-gestures.md +++ b/docs/en/writing-running-appium/ios/ios-xctest-mobile-gestures.md @@ -21,9 +21,13 @@ coordinates and duration. #### Supported arguments - * _direction_: Either 'up', 'down', 'left' or 'right'. The parameter is mandatory + * _direction_: Either 'up', 'down', 'left' or 'right'. The argument is mandatory + * _velocity_: This argument is optional and is only supported since Appium server version 1.19 and Xcode SDK version 11.4+. + The value is measured in pixels per second and same values could behave differently on different devices depending on their display density. Higher values make + swipe gesture faster (which usually scrolls larger areas if we apply it to a list) and lower + values slow it down. Only values greater than zero have effect. * _element_: The internal element identifier (as hexadecimal hash string) to swipe on. - Application element will be used instead if this parameter is not provided + Application element will be used instead if this argument is not provided #### Usage examples @@ -32,6 +36,7 @@ coordinates and duration. JavascriptExecutor js = (JavascriptExecutor) driver; Map params = new HashMap<>(); params.put("direction", "down"); +params.put("velocity", 2500); params.put("element", ((RemoteWebElement) element).getId()); js.executeScript("mobile: swipe", params); ``` From 3195a6b958dbd53144c8f9160cb206b06c72d29b Mon Sep 17 00:00:00 2001 From: Appium Triager Date: Thu, 8 Oct 2020 11:17:53 -0700 Subject: [PATCH 24/44] docs: Update generated docs [ci skip] (#14791) Co-authored-by: runner --- docs/en/commands/mobile-command.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/commands/mobile-command.md b/docs/en/commands/mobile-command.md index 2848adba5..8c961c3e4 100644 --- a/docs/en/commands/mobile-command.md +++ b/docs/en/commands/mobile-command.md @@ -158,7 +158,7 @@ List of available commands: | mobile:closeDrawer | Close drawer by [DrawerAction](https://developer.android.com/reference/android/support/test/espresso/contrib/DrawerActions) with [gravity](https://developer.android.com/reference/android/view/Gravity). `gravity` is option. The default is [GravityCompat.START](https://developer.android.com/reference/android/support/v4/view/GravityCompat.html#START). This method blocks until the drawer is fully closed. No operation if the drawer is already closed. | `{element, gravity}` | `{ element: element_id }`, `{ element: elementId, gravity: 3 }` | | mobile:setDate | Set date by [PickerActions#setDate](https://developer.android.com/reference/android/support/test/espresso/contrib/PickerActions.html#setDate(int,%20int,%20int)) for [DataPicker](https://developer.android.com/reference/android/widget/DatePicker). | `{element, year, monthOfYear, dayOfMonth}` | `{element: elementId, year: 2018, monthOfYear: 12, dayOfMonth: 1}` | | mobile:setTime | Set time by [PickerActions#setTime](https://developer.android.com/reference/android/support/test/espresso/contrib/PickerActions.html#setTime(int,%20int)) for [TimePicker](https://developer.android.com/reference/android/widget/TimePicker). | `{element, hours, minutes}` | `{element: elementId, hours: 12, minutes: 10}` | -| mobile:navigateTo | Action to [NavigationView](https://developer.android.com/reference/android/support/design/widget/NavigationView) by [NavigationViewActions#navigateTo](https://developer.android.com/reference/android/support/test/espresso/contrib/NavigationViewActions.html#navigateTo(int)). The view must be a child of a DrawerLayout, of type NavigationView, visible on screen and displayed on screen. | `{element, menuItemId}` | `{element: elementId, menuItemId: 1}` | +| mobile:navigateTo | Action to [NavigationView](https://developer.android.com/reference/android/support/design/widget/NavigationView) by [NavigationViewActions#navigateTo](https://developer.android.com/reference/android/support/test/espresso/contrib/NavigationViewActions.html#navigateTo(int)). The view must be a child of a DrawerLayout, of type NavigationView, visible on screen and displayed on screen. `com.google.android.material:material` as `additionalAndroidTestDependencies` capability is necessary. | `{element, menuItemId}` | `{element: elementId, menuItemId: 1}` | | mobile:scrollToPage | Action to [ViewPager](https://developer.android.com/reference/android/support/v4/view/ViewPager) by [ViewPagerActions](https://developer.android.com/reference/android/support/test/espresso/contrib/ViewPagerActions). `scrollTo` must be one of `first`, `last`, `left`, `right`. `scrollTo` is used by default if `scrollTo` and `scrollToPage` are provided. | `{scrollTo, scrollToPage, smoothScroll}` | `{element: elementId, scrollTo: 'left', smoothScroll: true}`, `{element: elementId, scrollToPage: 2}`| | mobile:backdoor | Invoke arbitrary methods defined in Android app. The methods must be `public` in Java and must be `open` in Kotlin. `target` is `activity`, `application` or `element`. `methods` are methods you would like to invoke. `element` is mandatory if `target` is `element`. Read docstring of _mobileBackdoor_ method [here](https://github.com/appium/appium-espresso-driver/blob/master/lib/commands/general.js) for more details | `{target, methods, element}` | `{target: 'activity', methods: [{name: 'method1'}, {name: 'method2', args: [{value: 'hello appium', type: 'java.lang.String'}] }] }`, `{target: 'element', element: elementId, [{name: 'getTypeface'}, {name: 'isItalic'}]}` | | mobile:flashElement | Flashes an element on the screen to visually confirm you are operating on the correct element. Can optionally set `durationMillis` (in ms) and `repeatCount` of animation| `{element, durationMillis, repeatCount}` | `{element: "1234-abcd-5678", durationMillis: 1000, repeatCount: 10}` | From b500f13a2a2d476a8215356ce354065f48d919aa Mon Sep 17 00:00:00 2001 From: Mihir Somani Date: Fri, 9 Oct 2020 22:41:05 -0400 Subject: [PATCH 25/44] docs: fixed APK name conflict in getting-started.md (#14798) The downloaded file from https://github.com/appium/appium/raw/master/sample-code/apps/ApiDemos-debug.apk is named `ApiDemos-debug.apk` but the docs use `ApiDemos.apk` for the path which might create unnecessary confusion for developers. --- docs/en/about-appium/getting-started.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/en/about-appium/getting-started.md b/docs/en/about-appium/getting-started.md index 7ec2195e3..1698fd344 100644 --- a/docs/en/about-appium/getting-started.md +++ b/docs/en/about-appium/getting-started.md @@ -177,7 +177,7 @@ const opts = { platformName: "Android", platformVersion: "8", deviceName: "Android Emulator", - app: "/path/to/the/downloaded/ApiDemos.apk", + app: "/path/to/the/downloaded/ApiDemos-debug.apk", appPackage: "io.appium.android.apis", appActivity: ".view.TextFields", automationName: "UiAutomator2" @@ -232,7 +232,7 @@ const opts = { platformName: "Android", platformVersion: "8", deviceName: "Android Emulator", - app: "/path/to/the/downloaded/ApiDemos.apk", + app: "/path/to/the/downloaded/ApiDemos-debug.apk", appPackage: "io.appium.android.apis", appActivity: ".view.TextFields", automationName: "UiAutomator2" From f9d20882565dd668f00a7978e9ef2e0204369a5d Mon Sep 17 00:00:00 2001 From: Wim Selles Date: Sun, 11 Oct 2020 14:38:41 +0200 Subject: [PATCH 26/44] fix: Update examples (#14799) The current example for `mobile: scrollGesture` was showing an incorrect example which referred to the iOS `mobile: scroll` --- .../writing-running-appium/android/android-mobile-gestures.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/en/writing-running-appium/android/android-mobile-gestures.md b/docs/en/writing-running-appium/android/android-mobile-gestures.md index 0d05928b0..38e0aeb5b 100644 --- a/docs/en/writing-running-appium/android/android-mobile-gestures.md +++ b/docs/en/writing-running-appium/android/android-mobile-gestures.md @@ -242,7 +242,7 @@ The returned value is a boolean one and equals to `true` if the object can still ```java // Java -boolean canScrollMore = (Boolean) ((JavascriptExecutor) driver).executeScript("mobile: scroll", ImmutableMap.of( +boolean canScrollMore = (Boolean) ((JavascriptExecutor) driver).executeScript("mobile: scrollGesture", ImmutableMap.of( "left", 100, "top", 100, "width", 200, "height", 200, "direction", "down", "percent", 3.0 @@ -251,7 +251,7 @@ boolean canScrollMore = (Boolean) ((JavascriptExecutor) driver).executeScript("m ```python # Python -can_scroll_more = driver.execute_script('mobile: scroll', { +can_scroll_more = driver.execute_script('mobile: scrollGesture', { 'left': 100, 'top': 100, 'width': 200, 'height': 200, 'direction': 'down', 'percent': 3.0 From 2c8ecc13db65dd0a6368496b731e35f2b29e47df Mon Sep 17 00:00:00 2001 From: Kazuaki Matsuo Date: Tue, 20 Oct 2020 09:51:00 -0700 Subject: [PATCH 27/44] docs: add apks via install app (#14824) * docs: add apks via install app * Update android-appbundle.md --- docs/en/writing-running-appium/android/android-appbundle.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/en/writing-running-appium/android/android-appbundle.md b/docs/en/writing-running-appium/android/android-appbundle.md index e50c8f6e6..a9be32e83 100644 --- a/docs/en/writing-running-appium/android/android-appbundle.md +++ b/docs/en/writing-running-appium/android/android-appbundle.md @@ -42,6 +42,12 @@ driver = core.start_driver You can find another way to get test APKs in https://developer.android.com/guide/app-bundle/ +You could also install `.apks` bundles via [Install App](/docs/en/commands/device/app/install-app.md) command like below. + +```ruby +driver.install_app 'path/to/your.apks' +``` + ## Tips ### Make `bundletool.jar` executable From ebe0a2525f180b75205a9ebf09980c1d3980e9b2 Mon Sep 17 00:00:00 2001 From: Imperixal Date: Wed, 21 Oct 2020 17:22:36 +0200 Subject: [PATCH 28/44] docs: In python window_handles is a property (#14831) In python, the method can't be called with '()'. It can be called only as self.driver.window_handles --- commands-yml/commands/web/window/get-handles.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commands-yml/commands/web/window/get-handles.yml b/commands-yml/commands/web/window/get-handles.yml index 8ce428178..c60a44dc2 100644 --- a/commands-yml/commands/web/window/get-handles.yml +++ b/commands-yml/commands/web/window/get-handles.yml @@ -8,7 +8,7 @@ example_usage: Set windowHandles = driver.getWindowHandles(); python: | - window_handles = self.driver.window_handles() + window_handles = self.driver.window_handles javascript_wd: | let windowHandle = await driver.windowHandles(); From ed025c8cf91b97edf7a0f870c1251620906cf8e9 Mon Sep 17 00:00:00 2001 From: Imperixal Date: Thu, 22 Oct 2020 01:37:55 +0200 Subject: [PATCH 29/44] docs: Calling method with '()' causes Error (#14832) Calling method with '()' causes Error: Traceback (most recent call last): File "", line 1, in print(driver.current_window_handle()) TypeError: 'str' object is not callable --- commands-yml/commands/web/window/get-handle.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commands-yml/commands/web/window/get-handle.yml b/commands-yml/commands/web/window/get-handle.yml index 275d145f3..6e36e5742 100644 --- a/commands-yml/commands/web/window/get-handle.yml +++ b/commands-yml/commands/web/window/get-handle.yml @@ -8,7 +8,7 @@ example_usage: String windowHandle = driver.getWindowHandle(); python: | - window_handle = self.driver.current_window_handle() + window_handle = self.driver.current_window_handle javascript_wd: | let windowHandle = await driver.windowHandle(); From 2b4cc6b8a9eec5b474330ae36f1a2419fbdf118a Mon Sep 17 00:00:00 2001 From: Mykola Mokhnach Date: Sat, 24 Oct 2020 10:42:15 +0200 Subject: [PATCH 30/44] docs: Add comments to java samples (#14838) --- .../src/AndroidBasicInteractionsTest.java | 55 ++++++++++++++++- sample-code/java/src/BaseTest.java | 4 +- .../java/src/IOSBasicInteractionsTest.java | 60 ++++++++++++++++++- 3 files changed, 115 insertions(+), 4 deletions(-) diff --git a/sample-code/java/src/AndroidBasicInteractionsTest.java b/sample-code/java/src/AndroidBasicInteractionsTest.java index 510d9a6ed..6814009fb 100644 --- a/sample-code/java/src/AndroidBasicInteractionsTest.java +++ b/sample-code/java/src/AndroidBasicInteractionsTest.java @@ -26,14 +26,65 @@ public class AndroidBasicInteractionsTest extends BaseTest { File appDir = new File(classpathRoot, "../apps"); File app = new File(appDir.getCanonicalPath(), "ApiDemos-debug.apk"); DesiredCapabilities capabilities = new DesiredCapabilities(); - capabilities.setCapability("deviceName", "Android Emulator"); + /* + 'deviceName' capability only affects device selection if you run the test in a cloud + environment. It has no effect if the test is executed on a local machine. + */ + // capabilities.setCapability("deviceName", "Android Emulator"); + + /* + It makes sense to set device udid if there are multiple devices/emulators + connected to the local machine. Run `adb devices -l` to check which devices + are online and what are their identifiers. + If only one device is connected then this capability might be omitted + */ + // capabilities.setCapability("udid", "ABCD123456789"); + + /* + It is recommended to set a full path to the app being tested. + Appium for Android supports application .apk and .apks bundles. + If this capability is not set then your test starts on Dashboard view. + It is also possible to provide an URL where the app is located. + */ capabilities.setCapability("app", app.getAbsolutePath()); + + /* + By default Appium tries to autodetect the main application activity, + but if you app's very first activity is not the main one then + it is necessary to provide its name explicitly. Check + https://github.com/appium/appium/blob/master/docs/en/writing-running-appium/android/activity-startup.md + for more details on activities selection topic. + */ + // capabilities.setCapability("appActivity", "com.myapp.SplashActivity")); + // capabilities.setCapability("appPackage", "com.myapp")); + // capabilities.setCapability("appWaitActivity", "com.myapp.MainActivity")); + + /* + Appium for Android supports multiple automation backends, where + each of them has its own pros and cons. The default one is UIAutomator2. + */ + // capabilities.setCapability("automationName", "UIAutomator2"); + // capabilities.setCapability("automationName", "Espresso"); + + /* + There are much more capabilities and driver settings, that allow + you to customize and tune your test to achieve the best automation + experience. Read http://appium.io/docs/en/writing-running-appium/caps/ + and http://appium.io/docs/en/advanced-concepts/settings/ + for more details. + + Feel free to visit our forum at https://discuss.appium.io/ + if you have more questions. + */ + driver = new AndroidDriver(getServiceUrl(), capabilities); } @AfterClass public void tearDown() { - driver.quit(); + if (driver != null) { + driver.quit(); + } } diff --git a/sample-code/java/src/BaseTest.java b/sample-code/java/src/BaseTest.java index 0869df8af..c9c052c3d 100644 --- a/sample-code/java/src/BaseTest.java +++ b/sample-code/java/src/BaseTest.java @@ -17,7 +17,9 @@ public abstract class BaseTest { @AfterSuite public void globalTearDown () { - service.stop(); + if (service != null) { + service.stop(); + } } public URL getServiceUrl () { diff --git a/sample-code/java/src/IOSBasicInteractionsTest.java b/sample-code/java/src/IOSBasicInteractionsTest.java index e4d3556a0..5d8487bac 100644 --- a/sample-code/java/src/IOSBasicInteractionsTest.java +++ b/sample-code/java/src/IOSBasicInteractionsTest.java @@ -23,16 +23,74 @@ public class IOSBasicInteractionsTest extends BaseTest { String deviceName = System.getenv("IOS_DEVICE_NAME"); String platformVersion = System.getenv("IOS_PLATFORM_VERSION"); DesiredCapabilities capabilities = new DesiredCapabilities(); + /* + 'deviceName' capability only affects device selection if you run the test in a cloud + environment or your run your test on a Simulator device. This combination of this value + plus `platformVersion` capability value + is used to select a proper Simulator if it already exists. Use `xcrun simctl list` command + to list available Simulator devices. + */ capabilities.setCapability("deviceName", deviceName == null ? "iPhone 6s" : deviceName); + + /* + udid value must be set if you run your test on a real iOS device. + The udid of your real device could be retrieved from Xcode->Windows->Devices and Simulators + dialog. + Usually, it is not enough to simply provide udid itself in order to automate apps + on real iOS devices. You must also verify the target device is included into + your Apple developer profile and the WebDriverAgent is signed with a proper signature. + Refer https://github.com/appium/appium-xcuitest-driver/blob/master/docs/real-device-config.md + for more details. + */ + // capabilities.setCapability("udid", "ABCD123456789"); + + /* + Platform version is required to be set. Only the major and minor version numbers have effect. + Check `xcrun simctl list` to see which platform versions are available if the test is going + to run on a Simulator. + */ capabilities.setCapability("platformVersion", platformVersion == null ? "11.1" : platformVersion); + + /* + It is recommended to set a full path to the app being tested. + Appium for iOS supports .app and .ipa application bundles. + It is also possible to pass zipped .app packages (they will be extracted automatically). + + Make sure the application is built for correct architecture (either + real device or Simulator) before running your tests, as there are not interchangeable. + If the test is going to run on a real device then make sure your app + is signed with correct development signature (as described in the above + Real Device Config document) + + If this capability is not set then your test starts on Springboard view. + It is also possible to provide an URL where the app is located. + */ capabilities.setCapability("app", app.getAbsolutePath()); + + /* + This is the only supported automation backend for iOS + */ capabilities.setCapability("automationName", "XCUITest"); + + /* + There are much more capabilities and driver settings, that allow + you to customize and tune your test to achieve the best automation + experience. Read http://appium.io/docs/en/writing-running-appium/caps/ + and http://appium.io/docs/en/advanced-concepts/settings/ + for more details. + + Feel free to visit our forum at https://discuss.appium.io/ + if you have more questions. + */ + driver = new IOSDriver(getServiceUrl(), capabilities); } @AfterTest public void tearDown() { - driver.quit(); + if (driver != null) { + driver.quit(); + } } @Test From 05be431a549a844c007663e9f7a0cb397eee55d1 Mon Sep 17 00:00:00 2001 From: Mykola Mokhnach Date: Sat, 24 Oct 2020 19:25:53 +0200 Subject: [PATCH 31/44] docs: Add new settings (#14839) --- docs/en/advanced-concepts/settings.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/en/advanced-concepts/settings.md b/docs/en/advanced-concepts/settings.md index acf6356c4..aa62b4124 100644 --- a/docs/en/advanced-concepts/settings.md +++ b/docs/en/advanced-concepts/settings.md @@ -77,6 +77,7 @@ Settings are implemented via the following API endpoints: | `waitForIdleTimeout` | Same as: [setWaitForIdleTimeout](https://developer.android.com/reference/android/support/test/uiautomator/Configurator.html#setWaitForIdleTimeout(long)). If a negative value is given, it would set to default (10 * 1000 milliseconds). Handled by [UiAutomator Configurator](https://developer.android.com/reference/android/support/test/uiautomator/Configurator.html) in Android API 18 and above. | e.g. `10000` | | `waitForSelectorTimeout` | Same as: [setWaitForSelectorTimeout](https://developer.android.com/reference/android/support/test/uiautomator/Configurator.html#setWaitForSelectorTimeout(long)). If a negative value is given, it would set to default (10 * 1000 milliseconds). Handled by [UiAutomator Configurator](https://developer.android.com/reference/android/support/test/uiautomator/Configurator.html) in Android API 18 and above. | e.g. `10000` | | `wakeLockTimeout` | Controls the timeout of the acquired wake lock. The lock is acquired on server startup and is held until the UIAutomator2 server is killed or the timeout expires. Setting this value to zero or a negative number will release the lock immediately if it is held. Defaults to '24 * 60 * 60 * 1000' milliseconds. | e.g. `0`, `60000` (1 min) | +|`useResourcesForOrientationDetection`| Whether to use application resource properties to figure out the current device orientation. By default the orientation value is based on the current rotation, which might be incorrect for some particular device types (usually tablets). `false` by default. Available since Appium 1.19.1 | e.g. `true` | ### iOS Only @@ -91,7 +92,9 @@ Settings are implemented via the following API endpoints: |`mjpegScalingFactor`| Changes the scale of screenshots. Defaults to `100`, no scaling. Integer between `1` and `100` are available. | e.g. `1`, `50`, `100` |1.12.0+| |`keyboardAutocorrection`| Changes the 'Auto-Correction' preference in _Keyboards_ setting. Defaults to `false` when WDA starts as xctest. | `true`, `false` |1.14.0+| |`keyboardPrediction`| Changes the 'Predictive' preference in _Keyboards_ setting. Defaults to `false` when WDA starts as xctest. | `true`, `false` |1.14.0+| -|`snapshotTimeout` | Changes the accessibility snapshots resolution timeout. _Snapshots_ are mainly used for page source generation, XML lookup and attributes retrieval. It might be necessary to increase this value if the actual page source is very large and contains hundreds of UI elements. Defaults to 15 seconds. | e.g. `10`, `100` (seconds) |1.15.0+| +|`customSnapshotTimeout` (`snapshotTimeout` before 1.19.1) | Set how much time is allowed to resolve a single accessibility snapshot with custom attributes. _Snapshots_ are mainly used for page source generation, XML lookup and custom attributes retrieval (these are visibility and accessibility ones). It might be necessary to increase this value if the actual page source is very large and contains hundreds of UI elements. Defaults to 15 seconds. Since Appium 1.19.1 if this timeout expires and no custom snapshot could be made then WDA tries to calculate the missing attributes using its own algorithms, so setting this value to zero might speed up, for example, page source retrieval, but for the cost of some element attribute preciseness. | e.g. `10`, `100` (seconds) |1.15.0+| +|`waitForIdleTimeout`|The amount of time in float seconds to wait until the application under test is idling. XCTest requires the app's main thread to be idling in order to execute any actions on it, so each action, like single click, might take a lot of time in case your app is constantly hogging the main thread. The default value is `10` (seconds). Setting it to zero disables idling checks completely (not recommended).|e.g. `2.5`|1.19.1+| +|`animationCoolOffTimeout`|The amount of time in float seconds to wait until the application under test does not have any active animations. This check is usually applied after each automation action that is supposed to change the state of the application under test, like `click` one, and blocks XCTest until the transition of the tested application to a new state completes or the cool off timeout occurs. The default value is `2` (seconds). Setting it to zero disables animation checks completely.|e.g. `1.5`, `0`|1.19.1+| |`snapshotMaxDepth`| Changes the value of maximum depth for traversing elements source tree. It may help to prevent out of memory or timeout errors while getting the elements source tree, but it might restrict the depth of source tree. Please consider restricting this value if you observed an error like _Timed out snapshotting com.apple.testmanagerd..._ message or _Cannot get 'xml' source of the current application_ in your Appium log since they are possibly timeout related. A part of elements source tree might be lost if the value was too small. Defaults to `50` | e.g. `100` | 1.17.0+ | |`useFirstMatch` | Enabling this setting makes single element lookups faster, but there is the known [problem](https://github.com/appium/appium/issues/10101) related to nested elements lookup. Defaults to `false`. |`true`, `false` |1.15.0+| |`reduceMotion`| Changes the 'reduce motion' preference of accessibility feature. | `true`, `false` |1.15.0+| From a7af094aa42d46033fffe8f51e70830159da96e1 Mon Sep 17 00:00:00 2001 From: Appium Triager Date: Sat, 24 Oct 2020 11:47:19 -0700 Subject: [PATCH 32/44] docs: Update generated docs [ci skip] (#14842) Co-authored-by: runner --- docs/en/commands/web/window/get-handle.md | 2 +- docs/en/commands/web/window/get-handles.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/en/commands/web/window/get-handle.md b/docs/en/commands/web/window/get-handle.md index bb6796572..6b863229c 100644 --- a/docs/en/commands/web/window/get-handle.md +++ b/docs/en/commands/web/window/get-handle.md @@ -13,7 +13,7 @@ String windowHandle = driver.getWindowHandle(); ```python # Python -window_handle = self.driver.current_window_handle() +window_handle = self.driver.current_window_handle ``` diff --git a/docs/en/commands/web/window/get-handles.md b/docs/en/commands/web/window/get-handles.md index ff8b14387..46c16a36b 100644 --- a/docs/en/commands/web/window/get-handles.md +++ b/docs/en/commands/web/window/get-handles.md @@ -13,7 +13,7 @@ Set windowHandles = driver.getWindowHandles(); ```python # Python -window_handles = self.driver.window_handles() +window_handles = self.driver.window_handles ``` From 6ae2834b23fed9755737aa4e63ca278f82d7217a Mon Sep 17 00:00:00 2001 From: Kazuaki Matsuo Date: Mon, 26 Oct 2020 09:19:14 -0700 Subject: [PATCH 33/44] docs: add a link to appium-windows-driver (#14844) --- docs/en/writing-running-appium/security.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/writing-running-appium/security.md b/docs/en/writing-running-appium/security.md index 523f47760..afc277dfb 100644 --- a/docs/en/writing-running-appium/security.md +++ b/docs/en/writing-running-appium/security.md @@ -33,6 +33,7 @@ Please see also below links. They might have additional flags. - [appium-android-driver](https://github.com/appium/appium-android-driver#opt-in-features-with-security-risk) - [appium-xcuitest-driver](https://github.com/appium/appium-xcuitest-driver#opt-in-features-with-security-risk) - [appium-mac-driver](https://github.com/appium/appium-mac-driver#opt-in-features-with-security-risk) +- [appium-windows-driver](https://github.com/appium/appium-windows-driver#power-shell-commands-execution) ## For Driver Developers From 7d6c5167f3118d98ac97a31681a4adb604f429da Mon Sep 17 00:00:00 2001 From: Mykola Mokhnach Date: Fri, 6 Nov 2020 08:27:15 +0100 Subject: [PATCH 34/44] feat: Add Safari driver to the list of available automation names (#14866) --- lib/appium.js | 5 +++++ package.json | 1 + 2 files changed, 6 insertions(+) diff --git a/lib/appium.js b/lib/appium.js index 3424696c5..8528678ab 100644 --- a/lib/appium.js +++ b/lib/appium.js @@ -34,6 +34,7 @@ const AUTOMATION_NAMES = { WINDOWS: 'Windows', MAC: 'Mac', FLUTTER: 'Flutter', + SAFARI: 'Safari', }; const DRIVER_MAP = { [AUTOMATION_NAMES.UIAUTOMATOR2.toLowerCase()]: { @@ -80,6 +81,10 @@ const DRIVER_MAP = { driverClassName: 'FlutterDriver', driverPackage: 'appium-flutter-driver' }, + [AUTOMATION_NAMES.SAFARI.toLowerCase()]: { + driverClassName: 'SafariDriver', + driverPackage: 'appium-safari-driver' + }, }; const PLATFORMS_MAP = { diff --git a/package.json b/package.json index a0d2cd4fe..88ef1806a 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,7 @@ "appium-flutter-driver": "^0", "appium-ios-driver": "4.x", "appium-mac-driver": "1.x", + "appium-safari-driver": "^2.1.0", "appium-support": "2.x", "appium-tizen-driver": "^1.1.1-beta.4", "appium-uiautomator2-driver": "^1.37.1", From 2fab8a48748f07d12e0e02b0aa4029c779163fbb Mon Sep 17 00:00:00 2001 From: Mykola Mokhnach Date: Sat, 7 Nov 2020 07:49:07 +0100 Subject: [PATCH 35/44] feat: Add Gecko driver (#14871) --- lib/appium.js | 5 +++++ package.json | 1 + 2 files changed, 6 insertions(+) diff --git a/lib/appium.js b/lib/appium.js index 8528678ab..32691180b 100644 --- a/lib/appium.js +++ b/lib/appium.js @@ -35,6 +35,7 @@ const AUTOMATION_NAMES = { MAC: 'Mac', FLUTTER: 'Flutter', SAFARI: 'Safari', + GECKO: 'Gecko', }; const DRIVER_MAP = { [AUTOMATION_NAMES.UIAUTOMATOR2.toLowerCase()]: { @@ -85,6 +86,10 @@ const DRIVER_MAP = { driverClassName: 'SafariDriver', driverPackage: 'appium-safari-driver' }, + [AUTOMATION_NAMES.GECKO.toLowerCase()]: { + driverClassName: 'GeckoDriver', + driverPackage: 'appium-geckodriver' + }, }; const PLATFORMS_MAP = { diff --git a/package.json b/package.json index 88ef1806a..975cfc4c5 100644 --- a/package.json +++ b/package.json @@ -47,6 +47,7 @@ "appium-espresso-driver": "^1.0.0", "appium-fake-driver": "^1.x", "appium-flutter-driver": "^0", + "appium-geckodriver": "^0.2.0", "appium-ios-driver": "4.x", "appium-mac-driver": "1.x", "appium-safari-driver": "^2.1.0", From 4ef1254ecab90ec07a9c2187793928ab4ead65a6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 7 Nov 2020 17:15:27 +0900 Subject: [PATCH 36/44] chore: Bump axios from 0.20.0 to 0.21.0 (#14845) Bumps [axios](https://github.com/axios/axios) from 0.20.0 to 0.21.0. - [Release notes](https://github.com/axios/axios/releases) - [Changelog](https://github.com/axios/axios/blob/master/CHANGELOG.md) - [Commits](https://github.com/axios/axios/compare/v0.20.0...v0.21.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 975cfc4c5..8e669cee4 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ "argparse": "^2.0.1", "async-lock": "^1.0.0", "asyncbox": "2.x", - "axios": "^0.20.0", + "axios": "^0.21.0", "bluebird": "3.x", "continuation-local-storage": "3.x", "dateformat": "^3.0.3", From b8f5af82ce720e0ad8c9aecb9a56dc797575c50d Mon Sep 17 00:00:00 2001 From: Mykola Mokhnach Date: Sun, 8 Nov 2020 10:00:51 +0100 Subject: [PATCH 37/44] docs: Add Safari driver documentation (#14873) --- docs/en/drivers/android-espresso.md | 2 +- docs/en/drivers/safari.md | 47 +++++++++++++++++++++++++++++ docs/toc.js | 1 + 3 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 docs/en/drivers/safari.md diff --git a/docs/en/drivers/android-espresso.md b/docs/en/drivers/android-espresso.md index 93aa317a7..5ce6fe8c2 100644 --- a/docs/en/drivers/android-espresso.md +++ b/docs/en/drivers/android-espresso.md @@ -18,7 +18,7 @@ Appium also supports Android automation using the In addition to Appium's general requirements: -* Java 7 installed and configured correctly for your platform +* Java 8 installed and configured correctly for your platform * Mac, Windows, or Linux OS with the ability to run the Android SDK ### Usage diff --git a/docs/en/drivers/safari.md b/docs/en/drivers/safari.md new file mode 100644 index 000000000..8f83c8408 --- /dev/null +++ b/docs/en/drivers/safari.md @@ -0,0 +1,47 @@ +## The Safari Driver + +Safari driver has been added to Appium since version 1.20. This driver +is a wrapper over Apple's [safaridriver](https://developer.apple.com/documentation/webkit/testing_with_webdriver_in_safari?language=objc) +binary, which is included to the standard macOS distribution and implements communication with either +desktop or mobile Safari browser via [W3C WebDriver protocol](https://www.w3.org/TR/webdriver/). + +Development of the Safari driver happens at the +[appium-safari-driver](https://github.com/appium/appium-safari-driver) +repo. + +Appium also supports mobile Safari automation and Safari web views automation using the +[appium-remote-debugger](https://github.com/appium/appium-remote-debugger). This module is +used to implement context switching feature with XCUITest automation name. It is more universal +and flexible than the current Safari driver, however it is using the undocumented WebKit communication +protocol under the hood. Which means the development of it and the effort to keep it in working state +is comparably high while the list of supported features is smaller. On the other hand `safaridriver` +is maintained by Apple, which means it is always in sync with the most recent browser +and communication protocol requirements. + +Long story short, it makes sense to prefer this driver if it is necessary to only automate +a web application that only works in Safari browser (either mobile or desktop). In case it is necessary to interact with native context or switch between different applications/contexts while automating your scenario +then the obvious choice would be either [XCUITest Driver](/docs/en/drivers/ios-xcuitest.md) +(for the mobile platform) or [Mac driver](/docs/en/drivers/mac.md) (for the desktop platform). + +### Requirements and Support + +In addition to Appium's general requirements: + +* Run the `safaridriver --enable` command from the macOS terminal and provide your administrator password before any automated session will be executed. This only should be done once. +* In order to automate Safari on real devices it is necessary to enable Remote Automation switch in `Settings → Safari → Advanced → Remote Automation` for these particular devices and trust them on the target host. The device's screen must not be locked while starting tests. +* Only macOS (High Sierra or newer) is supported as the host platform. +* Only iOS 13 and newer is supported for mobile browser automation. As it is stated in the WebKit blog [article](https://webkit.org/blog/9395/webdriver-is-coming-to-safari-in-ios-13/), Safari for iOS does not allow WebDriver-initiated navigations to be handled outside of Safari. In other words, clicking a tel:// link will not offer to place a phone call, and clicking an app store link will not redirect the user to the App Store. Similarly, non-Safari content displayed by the system–such as update dialogs, app notifications, incoming calls, etc.–are suppressed while a WebDriver session is active. + +### Usage + +The way to start a session using the Safari driver is to include the +`automationName` capability in your new session request, with +the value `Safari`. Of course, you must also include appropriate +`platformName` (`Mac` or `iOS`). Read +https://github.com/appium/appium-safari-driver/blob/master/README.md for +more details. + +### Capabilities + +The list of available driver capabilities could be found at +https://github.com/appium/appium-safari-driver/blob/master/README.md diff --git a/docs/toc.js b/docs/toc.js index 57b59d3fe..d36aeef11 100644 --- a/docs/toc.js +++ b/docs/toc.js @@ -17,6 +17,7 @@ module.exports = { ['UIAutomator2 (Android)', 'android-uiautomator2.md'], ['Espresso (Android)', 'android-espresso.md'], ['Windows', 'windows.md'], + ['Safari (Mac/iOS)', 'safari.md'], ['Mac', 'mac.md'], ]], ['Commands', ['commands', From dbf9170d2dec18f7072281fc95b8d7a1119b3ef4 Mon Sep 17 00:00:00 2001 From: Mykola Mokhnach Date: Sun, 8 Nov 2020 20:04:08 +0100 Subject: [PATCH 38/44] docs: Add Gecko Driver documentation (#14874) --- docs/en/drivers/gecko.md | 36 ++++++++++++++++++++++++++++++++++++ docs/toc.js | 1 + 2 files changed, 37 insertions(+) create mode 100644 docs/en/drivers/gecko.md diff --git a/docs/en/drivers/gecko.md b/docs/en/drivers/gecko.md new file mode 100644 index 000000000..38489786d --- /dev/null +++ b/docs/en/drivers/gecko.md @@ -0,0 +1,36 @@ +## The Gecko Driver + +Gecko driver has been added to Appium since version 1.20. This driver +is a wrapper over Mozilla's [geckodriver](https://firefox-source-docs.mozilla.org/testing/geckodriver/) +binary, which implements communication with either +desktop/mobile Gecko-based browsers like Firefox or Gecko-based web views (on mobile) +via [W3C WebDriver protocol](https://www.w3.org/TR/webdriver/). + +Development of the Gecko driver happens at the +[appium-geckodriver](https://github.com/appium/appium-geckodriver) +repo. + + +### Requirements and Support + +In addition to Appium's general requirements: + +* A supported browser must be installed on the destination platform. The recent browser releases (both desktop and mobile) could be retrieved from the official [download page](https://www.mozilla.org/en-GB/firefox/all/). +* The corresponding executable driver binary for the target platform must be available in `PATH` under `geckodriver` (`geckodriver.exe` in Windows) name. Geckodriver build for different supported platforms could be retrieved from the GitHub [Releases page](https://github.com/mozilla/geckodriver/releases). +* Windows (32/64 bit), Linux (32/64 bit) or macOS are supported as the host platforms. +* [Android SDK](https://developer.android.com/studio) must be installed if it is necessary to communicate with the browser or a web view on mobile devices. Also, the Emulator SDK is needed if automated tests are going to use Android emulators. For real Android devices it is necessary to make sure they have the `online` status to in the `adb devices -l` output. The device's screen must not be locked. + + +### Usage + +The way to start a session using the Gecko driver is to include the +`automationName` capability in your new session request, with +the value `Gecko`. Of course, you must also include appropriate +`platformName` (`Mac`/`Android`/`Windows`/`Linux`). Read +https://github.com/appium/appium-geckodriver/blob/master/README.md for +more details. + +### Capabilities + +The list of available driver capabilities could be found at +https://github.com/appium/appium-geckodriver/blob/master/README.md diff --git a/docs/toc.js b/docs/toc.js index d36aeef11..0bb93e296 100644 --- a/docs/toc.js +++ b/docs/toc.js @@ -18,6 +18,7 @@ module.exports = { ['Espresso (Android)', 'android-espresso.md'], ['Windows', 'windows.md'], ['Safari (Mac/iOS)', 'safari.md'], + ['Gecko (Firefox)', 'gecko.md'], ['Mac', 'mac.md'], ]], ['Commands', ['commands', From bd362d72a1c9615792cae7eaf230a875a6d4d25e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 10 Nov 2020 13:17:07 +0100 Subject: [PATCH 39/44] chore: Bump appium-geckodriver from 0.2.0 to 0.3.0 (#14878) Bumps [appium-geckodriver](https://github.com/appium/appium-geckodriver) from 0.2.0 to 0.3.0. - [Release notes](https://github.com/appium/appium-geckodriver/releases) - [Commits](https://github.com/appium/appium-geckodriver/compare/v0.2.0...v0.3.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8e669cee4..2140ad1a5 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "appium-espresso-driver": "^1.0.0", "appium-fake-driver": "^1.x", "appium-flutter-driver": "^0", - "appium-geckodriver": "^0.2.0", + "appium-geckodriver": "^0.3.0", "appium-ios-driver": "4.x", "appium-mac-driver": "1.x", "appium-safari-driver": "^2.1.0", From 15172a9c298d2f307c87aa2a8746b02b51d215ab Mon Sep 17 00:00:00 2001 From: Kazuaki Matsuo Date: Mon, 16 Nov 2020 11:23:32 -0800 Subject: [PATCH 40/44] chore: changelog 1.19.0 (#14840) * Update changelog * append espresso driver * append ios simulator * append a few xcuitest-driver * add ios * update * Update CHANGELOG.md * udpate * add links in mobile commands * update changelog --- CHANGELOG.md | 81 +++++++++++++++++++++++- commands-yml/commands/mobile-command.yml | 7 ++ docs/en/writing-running-appium/caps.md | 1 + 3 files changed, 88 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 298db5acd..0a94b2918 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,82 @@ +CHANGES IN VERSION 1.19.0 (FROM 1.18.3) +=================================== + +Appium 1.19.0 is a minor release + +### Android General + +* Add capabilities: + * `allowDelayAdb` to prevent `-delay-adb` command line option to detect emulator startup [#14773](https://github.com/appium/appium/issues/14773) +* Add `mobile:` functions: + * `mobile:getContexts` to get WebView details [appium-android-driver#662](https://github.com/appium/appium-android-driver/pull/662) +* Accept Web Authentication routes (they are part of W3C) [appium-base-driver#433](https://github.com/appium/appium-base-driver/pull/433) +* Change to enable `ensureWebviewsHavePages` by default [appium-android-driver#652](https://github.com/appium/appium-android-driver/pull/652) +* Change to close pending requests on the server side if the server is terminated [appium-base-driver#424](https://github.com/appium/appium-base-driver/pull/424) +* Fix CPU performance data parsing [appium-android-driver#659](https://github.com/appium/appium-android-driver/pull/659) +* Fix possible memory leak [appium-base-driver#430](https://github.com/appium/appium-base-driver/pull/430) +* Fix responses to unexpected server errors [appium-base-driver#432](https://github.com/appium/appium-base-driver/pull/432) + +### Android(UIAutomator2) + +* Add `mobile:` functions: + * `mobile:dragGesture`, `mobile:flingGesture`, `mobile:longClickGesture`, `mobile:pinchCloseGesture`, `mobile:pinchOpenGesture`, `mobile:swipeGesture` and `mobile:scrollGesture`: [Automating Mobile Gestures With UiAutomator2 Backend](https://appium.io/docs/en/writing-running-appium/android/android-mobile-gestures/) +* Add css selector support [appium-uiautomator2-driver#410](https://github.com/appium/appium-uiautomator2-driver/pull/410) + * `css selector` will be converted to `-android uiautomator` selector in UIAutomator2. Read [appium-uiautomator2-driver#410](https://github.com/appium/appium-uiautomator2-driver/pull/410) for more details. +* Enhance XPath lookup performance [appium-uiautomator2-server#386](https://github.com/appium/appium-uiautomator2-server/pull/386) +* Fix port guard logic to avoid port conflict [appium-uiautomator2-driver#409](https://github.com/appium/appium-uiautomator2-driver/pull/409) + +### Android(Espresso) + +* Raise `InvalidContextError` error when the app under test stops because of instrumentation process crashes (appium-espresso-driver#591)[https://github.com/appium/appium-espresso-driver/pull/591] +* Add a possibility to allow JSON formatted string as `espressoBuildConfig` capability [appium-espresso-driver#609](https://github.com/appium/appium-espresso-driver/pull/609) +* Add `mobile:` functions: + * `mobile:registerIdlingResources`, `mobile:unregisterIdlingResources` and `mobile:listIdlingResources` to customize idling resources [appium-espresso-driver#597](https://github.com/appium/appium-espresso-driver/pull/597) +* Add a custom exception description, which helps to debug issues when the application under test is not idling long enough [appium-espresso-driver#589](https://github.com/appium/appium-espresso-driver/pull/589) +* The `com.google.android.material` dependency became optional [appium-espresso-driver#616](https://github.com/appium/appium-espresso-driver/pull/616) + * `espressoBuildConfig` now allows to prevent custom dependencies for both categories: `additionalAppDependencies` and `additionalAndroidTestDependencies`. Please check [caps](https://appium.io/docs/en/writing-running-appium/caps/). + * `mobile:navigateTo` requires `com.google.android.material`, so please add `"espressoBuildConfig": "{\"additionalAndroidTestDependencies\":[\"com.google.android.material:material:1.2.1\"]}"` as your capabilities if needed +* Fix to exclude transitive dependencies of espresso-contrib[appium-espresso-driver#596](https://github.com/appium/appium-espresso-driver/pull/596) +* Fix to be able to build with SDK 29 [appium-espresso-driver#604](https://github.com/appium/appium-espresso-driver/pull/604) + +### iOS(general) + +* Improve the performane of simulator state state checks [appium-ios-simulator#284](https://github.com/appium/appium-ios-simulator/pull/284) +* Fix granting access to Calendar on Xcode 11.4 and later [appium-ios-simulator#288](https://github.com/appium/appium-ios-simulator/pull/288) +* Adjust the decimal separator in the Simulator location setting script according to system locale settings [appium-ios-simulator#295](https://github.com/appium/appium-ios-simulator/pull/295) + +### iOS(XCUITest) + +* Add capabilities: + * `simulatorDevicesSetPath` allows to set an alternative path to a Simulator devices set [appium-ios-simulator#290](https://github.com/appium/appium-ios-simulator/pull/290) + * `allowProvisioningDeviceRegistration` adds `-allowProvisioningUpdates` and `-allowProvisioningDeviceRegistration` flag to the list of xcodebuild arguments [appium-xcuitest-driver#1241](https://github.com/appium/appium-xcuitest-driver/pull/1241) +* Add mobile functions: + * `mobile:resetPermission` to reset all previous allowed or denied permissions for the application under test. It requires Xcode 11.4 and later [https://github.com/appium/appium-xcuitest-driver#1239](https://github.com/appium/appium-xcuitest-driver/pull/1239), [WebDriverAgent#392](https://github.com/appium/WebDriverAgent/pull/392) +* Add `velocity` argument for over Xcode 11.4 in `mobile:swipe` [appium#14793](https://github.com/appium/appium/pull/14793) +* Enhance performance + * e.g. improve taking snapshot to get elements [WebDriverAgent#393](https://github.com/appium/WebDriverAgent/pull/393) +* Fix alert handling in some cases [WebDriverAgent#400](https://github.com/appium/WebDriverAgent/pull/400) + +### Flutter + +The version is `0.0.25` + +### You.i Engine Driver + +The version is `1.2.7` + + +CHANGES IN VERSION 1.18.2 and 1.18.3 (FROM 1.18.1) +=================================== + +Appium 1.18.2 and 1.18.3 are patch releases + +* Supports Xcode 12 + iOS 14 + +### iOS(XCUITest) +* Allow to input text by send keys API without focus check for newer Xcode versions [WebDriverAgent#379](https://github.com/appium/WebDriverAgent/pull/379) +* Add supports `xcrun xctrace` instead of `instruments` command for Xcode 12+ [appium-xcuitest-driver#1223](https://github.com/appium/appium-xcuitest-driver/pull/1223) + + CHANGES IN VERSION 1.18.1 (FROM 1.18.0) =================================== @@ -8,7 +87,7 @@ Appium 1.18.1 is a patch release * Fix to avoid a redundant Chromedriver download operation if a matching driver is already present [appium-chromedriver#186](https://github.com/appium/appium-chromedriver/pull/186) #### Android (UiAutomator2) -* Fix to return a proper response for missing route [appium/appium-uiautomator2-server#373](https://github.com/appium/appium-uiautomator2-server/pull/373) +* Fix to return a proper response for missing route [appium/appium-uiautomator2-server#373](https://github.com/appium/appium-uiautomator2-server/pull/373) * Fix to allow double values as touch action coordinates [ appium/appium-uiautomator2-server#372](https://github.com/appium/appium-uiautomator2-server/pull/372) * Fix [#14586](https://github.com/appium/appium/issues/14586) which might affect XPath locators executed on elemetns, retrieved from nested lookup requests [appium/appium-uiautomator2-server#372](https://github.com/appium/appium-uiautomator2-server/pull/371) * A known issue in 1.18.0 diff --git a/commands-yml/commands/mobile-command.yml b/commands-yml/commands/mobile-command.yml index 73d564b30..48787598d 100644 --- a/commands-yml/commands/mobile-command.yml +++ b/commands-yml/commands/mobile-command.yml @@ -98,6 +98,13 @@ | mobile:deepLink | Opens a deep-link URL for testing [Instant Apps](https://support.google.com/googleplay/answer/7240211?hl=en) | `{url, package}` | `{url: "https://www.site.com/", package: "com.site.SomeAndroidPackage"}` | | mobile:deviceInfo | Gets device information like manufacturer, model, timezone and locale. Read [GetDeviceInfo](https://github.com/appium/appium-uiautomator2-server/blob/master/app/src/main/java/io/appium/uiautomator2/handler/GetDeviceInfo.java) for more details. | | | | mobile:type | Types the given Unicode string into focused field. The combination of `unicodeKeyboard` capability and the [send keys](https://appium.io/docs/en/commands/element/actions/send-keys/) works as _replacing_ the text field content. The send key in W3C action works only for ASCII. This command helps such cases to append unicode text content against the focused field. | `{text}` | `{text: 'happy testing'}` | + | mobile:longClickGesture | Refer to [Automating Mobile Gestures With UiAutomator2 Backend](/docs/en/writing-running-appium/android/android-mobile-gestures.md##mobile-longclickgesture) since Appium 1.19.0 | | | + | mobile:dragGesture | Refer to [Automating Mobile Gestures With UiAutomator2 Backend](/docs/en/writing-running-appium/android/android-mobile-gestures.md##mobile-draggesture) since Appium 1.19.0 | | | + | mobile:flingGesture | Refer to [Automating Mobile Gestures With UiAutomator2 Backend](/docs/en/writing-running-appium/android/android-mobile-gestures.md##mobile-flinggesture) since Appium 1.19.0 | | | + | mobile:pinchOpenGesture | Refer to [Automating Mobile Gestures With UiAutomator2 Backend](/docs/en/writing-running-appium/android/android-mobile-gestures.md##mobile-pinchopengesture) since Appium 1.19.0 | | | + | mobile:pinchCloseGesture | Refer to [Automating Mobile Gestures With UiAutomator2 Backend](/docs/en/writing-running-appium/android/android-mobile-gestures.md##mobile-pinchclosegesture) since Appium 1.19.0 | | | + | mobile:swipeGesture | Refer to [Automating Mobile Gestures With UiAutomator2 Backend](/docs/en/writing-running-appium/android/android-mobile-gestures.md##mobile-swipegesture) since Appium 1.19.0 | | | + | mobile:scrollGesture | Refer to [Automating Mobile Gestures With UiAutomator2 Backend](/docs/en/writing-running-appium/android/android-mobile-gestures.md##mobile-scrollgesture) since Appium 1.19.0 | | | ### Android (Espresso only) | Command | Description | Argument | Argument Example | diff --git a/docs/en/writing-running-appium/caps.md b/docs/en/writing-running-appium/caps.md index 4fb971c30..89cb86985 100644 --- a/docs/en/writing-running-appium/caps.md +++ b/docs/en/writing-running-appium/caps.md @@ -140,6 +140,7 @@ These Capabilities are available only on Android-based drivers (like | `mockLocationApp` | Sets the package identifier of the app, which is used as a system mock location provider since Appium 1.18.0+. This capability has no effect on emulators. If the value is set to `null` or an empty string, then Appium will skip the mocked location provider setup procedure. Defaults to Appium Setting package identifier (`io.appium.settings`). | e.g., `null`, `io.appium.settings`, `example.your.app` | | logcatFormat | Set the output format for logcat messages since Appium 1.18.0. Supported formats are listed in [here](https://github.com/appium/appium-adb/blob/master/lib/logcat.js). Please read [logcat#outputFormat](https://developer.android.com/studio/command-line/logcat#outputFormat) for more details about each format. Defaults to `threadtime`. | e.g., `process`| | logcatFilterSpecs | Set the output filter rule for logcat messages since Appium 1.18.0. Please read [logcat#filteringOutput](https://developer.android.com/studio/command-line/logcat#filteringOutput) for more details about the rule. [Write and View Logs with Logcat](https://developer.android.com/studio/debug/am-logcat) is also helpful. | e.g., `['*:W', 'MyActivity:D']` (`MyActivity` is a tag)| +| allowDelayAdb | Whether enable `-delay-adb` on emulator startup. Defaults to `true` | `true`, `false` | #### UIAutomator (1 & 2) From c083af52fde28ba4c84e2f1113cff5cc538ef3ec Mon Sep 17 00:00:00 2001 From: Appium Triager Date: Mon, 16 Nov 2020 12:36:09 -0800 Subject: [PATCH 41/44] docs: Update generated docs [ci skip] (#14890) Co-authored-by: runner --- docs/en/commands/mobile-command.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/en/commands/mobile-command.md b/docs/en/commands/mobile-command.md index 8c961c3e4..10cd35866 100644 --- a/docs/en/commands/mobile-command.md +++ b/docs/en/commands/mobile-command.md @@ -146,6 +146,13 @@ List of available commands: | mobile:deepLink | Opens a deep-link URL for testing [Instant Apps](https://support.google.com/googleplay/answer/7240211?hl=en) | `{url, package}` | `{url: "https://www.site.com/", package: "com.site.SomeAndroidPackage"}` | | mobile:deviceInfo | Gets device information like manufacturer, model, timezone and locale. Read [GetDeviceInfo](https://github.com/appium/appium-uiautomator2-server/blob/master/app/src/main/java/io/appium/uiautomator2/handler/GetDeviceInfo.java) for more details. | | | | mobile:type | Types the given Unicode string into focused field. The combination of `unicodeKeyboard` capability and the [send keys](https://appium.io/docs/en/commands/element/actions/send-keys/) works as _replacing_ the text field content. The send key in W3C action works only for ASCII. This command helps such cases to append unicode text content against the focused field. | `{text}` | `{text: 'happy testing'}` | +| mobile:longClickGesture | Refer to [Automating Mobile Gestures With UiAutomator2 Backend](/docs/en/writing-running-appium/android/android-mobile-gestures.md##mobile-longclickgesture) since Appium 1.19.0 | | | +| mobile:dragGesture | Refer to [Automating Mobile Gestures With UiAutomator2 Backend](/docs/en/writing-running-appium/android/android-mobile-gestures.md##mobile-draggesture) since Appium 1.19.0 | | | +| mobile:flingGesture | Refer to [Automating Mobile Gestures With UiAutomator2 Backend](/docs/en/writing-running-appium/android/android-mobile-gestures.md##mobile-flinggesture) since Appium 1.19.0 | | | +| mobile:pinchOpenGesture | Refer to [Automating Mobile Gestures With UiAutomator2 Backend](/docs/en/writing-running-appium/android/android-mobile-gestures.md##mobile-pinchopengesture) since Appium 1.19.0 | | | +| mobile:pinchCloseGesture | Refer to [Automating Mobile Gestures With UiAutomator2 Backend](/docs/en/writing-running-appium/android/android-mobile-gestures.md##mobile-pinchclosegesture) since Appium 1.19.0 | | | +| mobile:swipeGesture | Refer to [Automating Mobile Gestures With UiAutomator2 Backend](/docs/en/writing-running-appium/android/android-mobile-gestures.md##mobile-swipegesture) since Appium 1.19.0 | | | +| mobile:scrollGesture | Refer to [Automating Mobile Gestures With UiAutomator2 Backend](/docs/en/writing-running-appium/android/android-mobile-gestures.md##mobile-scrollgesture) since Appium 1.19.0 | | | ### Android (Espresso only) | Command | Description | Argument | Argument Example | From 8ed7bae02f8b84c5f9a7686df077e7297afe24b2 Mon Sep 17 00:00:00 2001 From: Mykola Mokhnach Date: Tue, 17 Nov 2020 12:14:08 +0100 Subject: [PATCH 42/44] fix: Properly format log timestamps (#14889) --- lib/logsink.js | 15 +++++++++------ package.json | 1 - 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/lib/logsink.js b/lib/logsink.js index c066d41e5..238689411 100644 --- a/lib/logsink.js +++ b/lib/logsink.js @@ -1,7 +1,6 @@ import npmlog from 'npmlog'; import { createLogger, format, transports } from 'winston'; import { fs, logger } from 'appium-support'; -import dateformat from 'dateformat'; import _ from 'lodash'; @@ -36,16 +35,20 @@ const npmToWinstonLevels = { }; let log = null; -let timeZone = null; +let useLocalTimeZone = false; // add the timestamp in the correct format to the log info object const timestampFormat = format.timestamp({ format () { let date = new Date(); - if (!timeZone) { - date = new Date(date.valueOf() + date.getTimezoneOffset() * 60000); + if (useLocalTimeZone) { + date = new Date(date.valueOf() - date.getTimezoneOffset() * 60000); } - return dateformat(date, 'yyyy-mm-dd HH:MM:ss:l'); + // '2012-11-04T14:51:06.157Z' -> '2012-11-04 14:51:06:157' + return date.toISOString() + .replace(/[TZ]/g, ' ') + .replace(/\./g, ':') + .trim(); }, }); @@ -182,7 +185,7 @@ async function createTransports (args) { async function init (args) { // set de facto param passed to timestamp function - timeZone = args.localTimezone; + useLocalTimeZone = args.localTimezone; // clean up in case we have initiated before since npmlog is a global object clear(); diff --git a/package.json b/package.json index 2140ad1a5..7a54ddad8 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,6 @@ "axios": "^0.21.0", "bluebird": "3.x", "continuation-local-storage": "3.x", - "dateformat": "^3.0.3", "find-root": "^1.1.0", "lodash": "^4.17.11", "longjohn": "^0.2.12", From 4f4ed25930a50422a7e5909e596bab45cad42bfb Mon Sep 17 00:00:00 2001 From: Dan Date: Tue, 17 Nov 2020 10:58:48 -0800 Subject: [PATCH 43/44] 1.20.0-beta.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7a54ddad8..e53571e05 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "firefoxos", "testing" ], - "version": "1.19.0-beta.0", + "version": "1.20.0-beta.0", "author": "https://github.com/appium", "license": "Apache-2.0", "repository": { From 498fe63845e3f624abf76159f034f94bd5e98834 Mon Sep 17 00:00:00 2001 From: Jonathan Lipps Date: Fri, 20 Nov 2020 09:18:52 -0800 Subject: [PATCH 44/44] fix: prevent potential problems with an unbound parser.parse_args --- lib/cli/parser.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/cli/parser.js b/lib/cli/parser.js index bb056ae32..712424f1f 100644 --- a/lib/cli/parser.js +++ b/lib/cli/parser.js @@ -38,7 +38,7 @@ function getParser (debug = false) { // modify the parse_args function to insert the 'server' subcommand if the // user hasn't specified a subcommand or the global help command - parser._parse_args = parser.parse_args; + parser._parse_args = parser.parse_args.bind(parser); parser.parse_args = function (args, namespace) { if (_.isUndefined(args)) { args = [...process.argv.slice(2)];