appium 2.0.0-beta.41 → 2.0.0-beta.42

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -15,9 +15,9 @@ export const commandClasses = Object.freeze(
15
15
  * Run a subcommand of the 'appium driver' type. Each subcommand has its own set of arguments which
16
16
  * can be represented as a JS object.
17
17
  *
18
- * @param {Object} args - JS object where the key is the parameter name (as defined in
18
+ * @param {import('appium/types').Args<import('appium/types').WithExtSubcommand>} args - JS object where the key is the parameter name (as defined in
19
19
  * driver-parser.js)
20
- * @template {import('../extension/manifest').ExtensionType} ExtType
20
+ * @template {ExtensionType} ExtType
21
21
  * @param {import('../extension/extension-config').ExtensionConfig<ExtType>} config - Extension config object
22
22
  */
23
23
  async function runExtensionCommand(args, config) {
@@ -30,6 +30,7 @@ async function runExtensionCommand(args, config) {
30
30
  throw new TypeError(`Cannot call ${type} command without a subcommand like 'install'`);
31
31
  }
32
32
  let {json, suppressOutput} = args;
33
+ json = Boolean(json);
33
34
  if (suppressOutput) {
34
35
  json = true;
35
36
  }
@@ -56,6 +57,17 @@ async function runExtensionCommand(args, config) {
56
57
  export {runExtensionCommand};
57
58
 
58
59
  /**
59
- * @template {import('@appium/types').ExtensionType} ExtType
60
- * @typedef {ExtType extends import('@appium/types').DriverType ? import('@appium/types').Class<DriverCommand> : ExtType extends import('@appium/types').PluginType ? import('@appium/types').Class<PluginCommand> : never} ExtCommand
60
+ * @template {ExtensionType} ExtType
61
+ * @typedef {ExtType extends DriverType ? Class<DriverCommand> : ExtType extends PluginType ? Class<PluginCommand> : never} ExtCommand
62
+ */
63
+
64
+ /**
65
+ * @typedef {import('@appium/types').ExtensionType} ExtensionType
66
+ * @typedef {import('@appium/types').DriverType} DriverType
67
+ * @typedef {import('@appium/types').PluginType} PluginType
68
+ */
69
+
70
+ /**
71
+ * @template T
72
+ * @typedef {import('@appium/types').Class<T>} Class
61
73
  */
package/lib/cli/parser.js CHANGED
@@ -7,6 +7,8 @@ import {finalizeSchema, getArgSpec, hasArgSpec} from '../schema';
7
7
  import {rootDir} from '../config';
8
8
  import {getExtensionArgs, getServerArgs} from './args';
9
9
 
10
+ export const EXTRA_ARGS = 'extraArgs';
11
+
10
12
  /**
11
13
  * If the parsed args do not contain any of these values, then we
12
14
  * will automatially inject the `server` subcommand.
@@ -97,8 +99,17 @@ class ArgParser {
97
99
  }
98
100
 
99
101
  try {
100
- const parsed = this.parser.parse_args(args);
101
- return ArgParser._transformParsedArgs(parsed);
102
+ const parsed = this.parser.parse_known_args(args);
103
+ const [knownArgs, unknownArgs] = parsed;
104
+ if (
105
+ unknownArgs?.length &&
106
+ (knownArgs.driverCommand === 'run' || knownArgs.pluginCommand === 'run')
107
+ ) {
108
+ return ArgParser._transformParsedArgs(knownArgs, unknownArgs);
109
+ } else if (unknownArgs?.length) {
110
+ throw new Error(`[ERROR] Unrecognized arguments: ${unknownArgs.join(' ')}`);
111
+ }
112
+ return ArgParser._transformParsedArgs(knownArgs);
102
113
  } catch (err) {
103
114
  if (this.debug) {
104
115
  throw err;
@@ -123,10 +134,11 @@ class ArgParser {
123
134
  *
124
135
  * E.g., `{'driver-foo-bar': baz}` becomes `{driver: {foo: {bar: 'baz'}}}`
125
136
  * @param {object} args
137
+ * @param {string[]} [unknownArgs]
126
138
  * @returns {object}
127
139
  */
128
- static _transformParsedArgs(args) {
129
- return _.reduce(
140
+ static _transformParsedArgs(args, unknownArgs = []) {
141
+ const result = _.reduce(
130
142
  args,
131
143
  (unpacked, value, key) => {
132
144
  if (!_.isUndefined(value) && hasArgSpec(key)) {
@@ -140,6 +152,8 @@ class ArgParser {
140
152
  },
141
153
  {}
142
154
  );
155
+ result[EXTRA_ARGS] = unknownArgs;
156
+ return result;
143
157
  }
144
158
 
145
159
  /**
@@ -183,7 +197,7 @@ class ArgParser {
183
197
  * @param {import('argparse').SubParser} subParsers
184
198
  */
185
199
  static _addExtensionCommandsToParser(subParsers) {
186
- for (const type of [DRIVER_TYPE, PLUGIN_TYPE]) {
200
+ for (const type of /** @type {[DriverType, PluginType]} */ ([DRIVER_TYPE, PLUGIN_TYPE])) {
187
201
  const extParser = subParsers.add_parser(type, {
188
202
  add_help: true,
189
203
  help: `Access the ${type} management CLI commands`,
@@ -195,6 +209,9 @@ class ArgParser {
195
209
  dest: `${type}Command`,
196
210
  });
197
211
  const extensionArgs = getExtensionArgs();
212
+ /**
213
+ * @type { {command: import('appium/types').CliExtensionSubcommand, args: import('./args').ArgumentDefinitions, help: string}[] }
214
+ */
198
215
  const parserSpecs = [
199
216
  {
200
217
  command: 'list',
@@ -254,3 +271,8 @@ function getParser(debug) {
254
271
  }
255
272
 
256
273
  export {getParser, ArgParser};
274
+
275
+ /**
276
+ * @typedef {import('@appium/types').DriverType} DriverType
277
+ * @typedef {import('@appium/types').PluginType} PluginType
278
+ */
@@ -33,8 +33,8 @@ export default class PluginCommand extends ExtensionCommand {
33
33
  return await super._update({installSpec: plugin, unsafe});
34
34
  }
35
35
 
36
- async run({plugin, scriptName}) {
37
- return await super._run({installSpec: plugin, scriptName});
36
+ async run({plugin, scriptName, extraArgs}) {
37
+ return await super._run({installSpec: plugin, scriptName, extraArgs});
38
38
  }
39
39
 
40
40
  getPostInstallText({extName, extData}) {
package/lib/config.js CHANGED
@@ -3,7 +3,6 @@ import _ from 'lodash';
3
3
  import {system, fs} from '@appium/support';
4
4
  import axios from 'axios';
5
5
  import {exec} from 'teen_process';
6
- import logger from './logger';
7
6
  import semver from 'semver';
8
7
  import findUp from 'find-up';
9
8
  import {getDefaultsForSchema, getAllArgSpecs} from './schema/schema';
@@ -12,6 +11,7 @@ const npmPackage = fs.readPackageJsonFrom(__dirname);
12
11
 
13
12
  const APPIUM_VER = npmPackage.version;
14
13
  const MIN_NODE_VERSION = npmPackage.engines.node;
14
+ const MIN_NPM_VERSION = npmPackage.engines.npm;
15
15
 
16
16
  const GIT_META_ROOT = '.git';
17
17
  const GIT_BINARY = `git${system.isWindows() ? '.exe' : ''}`;
@@ -28,6 +28,15 @@ function getNodeVersion() {
28
28
  return /** @type {import('semver').SemVer} */ (semver.coerce(process.version));
29
29
  }
30
30
 
31
+ /**
32
+ * Returns version of `npm`
33
+ * @returns {Promise<string>}
34
+ */
35
+ async function getNpmVersion() {
36
+ const {stdout} = await exec(system.isWindows() ? 'npm.cmd' : 'npm', ['--version']);
37
+ return stdout.trim();
38
+ }
39
+
31
40
  /**
32
41
  * @param {boolean} [useGithubApiFallback]
33
42
  */
@@ -133,7 +142,18 @@ function getBuildInfo() {
133
142
  function checkNodeOk() {
134
143
  const version = getNodeVersion();
135
144
  if (!semver.satisfies(version, MIN_NODE_VERSION)) {
136
- logger.errorAndThrow(`Node version must be ${MIN_NODE_VERSION}. Currently ${version.version}`);
145
+ throw new Error(
146
+ `Node version must be at least ${MIN_NODE_VERSION}; current is ${version.version}`
147
+ );
148
+ }
149
+ }
150
+
151
+ export async function checkNpmOk() {
152
+ const npmVersion = await getNpmVersion();
153
+ if (!semver.satisfies(npmVersion, MIN_NPM_VERSION)) {
154
+ throw new Error(
155
+ `npm version must be at least ${MIN_NPM_VERSION}; current is ${npmVersion}. Run "npm install -g npm" to upgrade.`
156
+ );
137
157
  }
138
158
  }
139
159
 
package/lib/main.js CHANGED
@@ -4,7 +4,7 @@ import {init as logsinkInit} from './logsink'; // this import needs to come firs
4
4
  import logger from './logger'; // logger needs to remain second
5
5
  // @ts-ignore
6
6
  import {routeConfiguringFunction as makeRouter, server as baseServer} from '@appium/base-driver';
7
- import {logger as logFactory, util, env, fs} from '@appium/support';
7
+ import {logger as logFactory, util, env} from '@appium/support';
8
8
  import {asyncify} from 'asyncbox';
9
9
  import _ from 'lodash';
10
10
  import {AppiumDriver} from './appium';
@@ -15,11 +15,11 @@ import {
15
15
  checkNodeOk,
16
16
  getGitRev,
17
17
  getNonDefaultServerArgs,
18
- rootDir,
19
18
  showConfig,
20
19
  showBuildInfo,
21
20
  validateTmpDir,
22
21
  warnNodeDeprecations,
22
+ checkNpmOk,
23
23
  } from './config';
24
24
  import {readConfigFile} from './config-file';
25
25
  import {loadExtensions, getActivePlugins, getActiveDrivers} from './extension';
@@ -27,9 +27,8 @@ import {DRIVER_TYPE, PLUGIN_TYPE, SERVER_SUBCOMMAND} from './constants';
27
27
  import registerNode from './grid-register';
28
28
  import {getDefaultsForSchema, validate} from './schema/schema';
29
29
  import {inspect} from './utils';
30
- import path from 'path';
31
30
 
32
- const {resolveAppiumHome, hasAppiumDependency} = env;
31
+ const {resolveAppiumHome} = env;
33
32
 
34
33
  /**
35
34
  *
@@ -39,6 +38,7 @@ const {resolveAppiumHome, hasAppiumDependency} = env;
39
38
  async function preflightChecks(args, throwInsteadOfExit = false) {
40
39
  try {
41
40
  checkNodeOk();
41
+ await checkNpmOk();
42
42
  if (args.longStacktrace) {
43
43
  require('longjohn').async_trace_limit = -1;
44
44
  }
@@ -172,19 +172,6 @@ function areServerCommandArgs(args) {
172
172
  async function init(args) {
173
173
  const appiumHome = args?.appiumHome ?? (await resolveAppiumHome());
174
174
 
175
- // this is here so extensions installed in e.g., `~/.appium` can find the peer dependency
176
- // of `appium`, which lives wherever it lives (see `rootDir`).
177
- if (!(await hasAppiumDependency(appiumHome))) {
178
- try {
179
- await fs.mkdirp(path.join(appiumHome, 'node_modules'));
180
- await fs.symlink(rootDir, path.join(appiumHome, 'node_modules', 'appium'), 'junction');
181
- } catch (err) {
182
- if (err.code !== 'EEXIST') {
183
- throw new Error(`Unable to create symlink to appium: ${err.message}`);
184
- }
185
- }
186
- }
187
-
188
175
  const {driverConfig, pluginConfig} = await loadExtensions(appiumHome);
189
176
 
190
177
  const parser = getParser();
@@ -222,20 +209,7 @@ async function init(args) {
222
209
  // 1. command line args
223
210
  // 2. config file
224
211
  // 3. defaults from config file.
225
- if (!areServerCommandArgs(preConfigArgs)) {
226
- // if the user has requested the 'driver' CLI, don't run the normal server,
227
- // but instead pass control to the driver CLI
228
- if (preConfigArgs.subcommand === DRIVER_TYPE) {
229
- await runExtensionCommand(preConfigArgs, driverConfig);
230
- return {};
231
- }
232
- if (preConfigArgs.subcommand === PLUGIN_TYPE) {
233
- await runExtensionCommand(preConfigArgs, pluginConfig);
234
- return {};
235
- }
236
- /* istanbul ignore next */
237
- return {}; // should never happen
238
- } else {
212
+ if (areServerCommandArgs(preConfigArgs)) {
239
213
  const defaults = getDefaultsForSchema(false);
240
214
 
241
215
  /** @type {ParsedArgs} */
@@ -282,6 +256,22 @@ async function init(args) {
282
256
  driverConfig,
283
257
  pluginConfig,
284
258
  });
259
+ } else {
260
+ const extensionCommandArgs = /** @type {Args<import('appium/types').WithExtSubcommand>} */ (
261
+ preConfigArgs
262
+ );
263
+ // if the user has requested the 'driver' CLI, don't run the normal server,
264
+ // but instead pass control to the driver CLI
265
+ if (preConfigArgs.subcommand === DRIVER_TYPE) {
266
+ await runExtensionCommand(extensionCommandArgs, driverConfig);
267
+ return {};
268
+ }
269
+ if (preConfigArgs.subcommand === PLUGIN_TYPE) {
270
+ await runExtensionCommand(extensionCommandArgs, pluginConfig);
271
+ return {};
272
+ }
273
+ /* istanbul ignore next */
274
+ return {}; // should never happen
285
275
  }
286
276
  }
287
277
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "appium",
3
- "version": "2.0.0-beta.41",
3
+ "version": "2.0.0-beta.42",
4
4
  "description": "Automation for Apps.",
5
5
  "keywords": [
6
6
  "automation",
@@ -54,20 +54,20 @@
54
54
  "postinstall": "node ./scripts/autoinstall-extensions.js",
55
55
  "lint": "eslint -c ../../.eslintrc --ignore-path ../../.eslintignore .",
56
56
  "prepare": "npm run build",
57
- "publish:docs": "APPIUM_DOCS_PUBLISH=1 npm run build:docs",
57
+ "publish:docs": "cross-env APPIUM_DOCS_PUBLISH=1 npm run build:docs",
58
58
  "test": "npm run test:unit",
59
59
  "test:e2e": "mocha --timeout 1m --slow 30s \"./test/e2e/**/*.spec.js\"",
60
- "test:smoke": "node ./index.js --show-build-info",
60
+ "test:smoke": "cross-env APPIUM_HOME=./local_appium_home node ./index.js driver install uiautomator2 && cross-env APPIUM_HOME=./local_appium_home node ./index.js driver list",
61
61
  "test:unit": "mocha \"./test/unit/**/*.spec.js\""
62
62
  },
63
63
  "dependencies": {
64
- "@appium/base-driver": "^8.6.0",
65
- "@appium/base-plugin": "^1.10.0",
66
- "@appium/docutils": "^0.0.8",
67
- "@appium/schema": "^0.0.8",
68
- "@appium/support": "^2.59.3",
69
- "@appium/test-support": "^1.4.0",
70
- "@appium/types": "^0.3.0",
64
+ "@appium/base-driver": "^8.6.1",
65
+ "@appium/base-plugin": "^1.10.1",
66
+ "@appium/docutils": "^0.0.9",
67
+ "@appium/schema": "^0.0.9",
68
+ "@appium/support": "^2.59.4",
69
+ "@appium/test-support": "^1.4.1",
70
+ "@appium/types": "^0.3.1",
71
71
  "@babel/runtime": "7.18.9",
72
72
  "@sidvind/better-ajv-errors": "2.0.0",
73
73
  "@types/argparse": "2.0.10",
@@ -83,6 +83,7 @@
83
83
  "asyncbox": "2.9.2",
84
84
  "axios": "0.27.2",
85
85
  "bluebird": "3.7.2",
86
+ "cross-env": "7.0.3",
86
87
  "find-up": "5.0.0",
87
88
  "lilconfig": "2.0.6",
88
89
  "lodash": "4.17.21",
@@ -94,18 +95,18 @@
94
95
  "semver": "7.3.7",
95
96
  "source-map-support": "0.5.21",
96
97
  "teen_process": "1.16.0",
97
- "type-fest": "2.17.0",
98
+ "type-fest": "2.18.0",
98
99
  "winston": "3.8.1",
99
100
  "wrap-ansi": "7.0.0",
100
101
  "yaml": "2.1.1"
101
102
  },
102
103
  "engines": {
103
104
  "node": ">=14",
104
- "npm": ">=6"
105
+ "npm": ">=8"
105
106
  },
106
107
  "publishConfig": {
107
108
  "access": "public",
108
109
  "tag": "next"
109
110
  },
110
- "gitHead": "f6e6d7234e7fe92f989f4e37a71007044985dedf"
111
+ "gitHead": "500c08f3929b1dd531d119872d11cea59d417eb7"
111
112
  }
package/types/cli.ts CHANGED
@@ -92,6 +92,21 @@ export interface ExtArgs extends WithExtSubcommand {
92
92
  * Subcommands of `plugin` subcommand
93
93
  */
94
94
  pluginCommand?: CliExtensionSubcommand;
95
+
96
+ /**
97
+ * Output JSON instead of human-readable text
98
+ */
99
+ json?: boolean;
100
+
101
+ /**
102
+ * Output nothing
103
+ */
104
+ suppressOutput?: boolean;
105
+
106
+ /**
107
+ * Extra args to pass to extension scripts
108
+ */
109
+ extraArgs?: string[];
95
110
  }
96
111
 
97
112
  /**