appium 2.0.0-beta.2 → 2.0.0-beta.20
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.
- package/README.md +9 -9
- package/build/lib/appium-config.schema.json +0 -0
- package/build/lib/appium.js +157 -53
- package/build/lib/cli/argparse-actions.js +104 -0
- package/build/lib/cli/args.js +115 -279
- package/build/lib/cli/driver-command.js +11 -1
- package/build/lib/cli/extension-command.js +60 -8
- package/build/lib/cli/extension.js +30 -7
- package/build/lib/cli/npm.js +17 -14
- package/build/lib/cli/parser.js +152 -89
- package/build/lib/cli/plugin-command.js +11 -1
- package/build/lib/cli/utils.js +29 -3
- package/build/lib/config-file.js +141 -0
- package/build/lib/config.js +76 -61
- package/build/lib/driver-config.js +42 -19
- package/build/lib/drivers.js +8 -4
- package/build/lib/ext-config-io.js +165 -0
- package/build/lib/extension-config.js +130 -61
- package/build/lib/grid-register.js +22 -24
- package/build/lib/logger.js +3 -3
- package/build/lib/logsink.js +11 -13
- package/build/lib/main.js +197 -77
- package/build/lib/plugin-config.js +20 -10
- package/build/lib/plugins.js +4 -2
- package/build/lib/schema/appium-config-schema.js +252 -0
- package/build/lib/schema/arg-spec.js +120 -0
- package/build/lib/schema/cli-args.js +173 -0
- package/build/lib/schema/cli-transformers.js +76 -0
- package/build/lib/schema/index.js +36 -0
- package/build/lib/schema/keywords.js +62 -0
- package/build/lib/schema/schema.js +357 -0
- package/build/lib/utils.js +44 -99
- package/lib/appium-config.schema.json +277 -0
- package/lib/appium.js +201 -65
- package/lib/cli/argparse-actions.js +77 -0
- package/lib/cli/args.js +174 -375
- package/lib/cli/driver-command.js +4 -0
- package/lib/cli/extension-command.js +70 -5
- package/lib/cli/extension.js +25 -5
- package/lib/cli/npm.js +18 -12
- package/lib/cli/parser.js +254 -79
- package/lib/cli/plugin-command.js +4 -0
- package/lib/cli/utils.js +21 -1
- package/lib/config-file.js +227 -0
- package/lib/config.js +109 -62
- package/lib/driver-config.js +66 -11
- package/lib/drivers.js +4 -1
- package/lib/ext-config-io.js +287 -0
- package/lib/extension-config.js +225 -67
- package/lib/grid-register.js +27 -24
- package/lib/logger.js +1 -1
- package/lib/logsink.js +10 -7
- package/lib/main.js +211 -77
- package/lib/plugin-config.js +34 -5
- package/lib/plugins.js +1 -0
- package/lib/schema/appium-config-schema.js +286 -0
- package/lib/schema/arg-spec.js +218 -0
- package/lib/schema/cli-args.js +273 -0
- package/lib/schema/cli-transformers.js +123 -0
- package/lib/schema/index.js +2 -0
- package/lib/schema/keywords.js +119 -0
- package/lib/schema/schema.js +577 -0
- package/lib/utils.js +42 -88
- package/package.json +55 -80
- package/postinstall.js +71 -0
- package/types/appium-config.d.ts +197 -0
- package/types/types.d.ts +201 -0
- package/CHANGELOG.md +0 -3515
- package/build/lib/cli/parser-helpers.js +0 -82
- package/lib/cli/parser-helpers.js +0 -79
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
import _ from 'lodash';
|
|
4
4
|
import NPM from './npm';
|
|
5
5
|
import path from 'path';
|
|
6
|
-
import { fs, util } from 'appium
|
|
7
|
-
import { log, spinWith } from './utils';
|
|
6
|
+
import { fs, util } from '@appium/support';
|
|
7
|
+
import { log, spinWith, RingBuffer } from './utils';
|
|
8
|
+
import { SubProcess} from 'teen_process';
|
|
8
9
|
import { INSTALL_TYPE_NPM, INSTALL_TYPE_GIT, INSTALL_TYPE_GITHUB,
|
|
9
10
|
INSTALL_TYPE_LOCAL } from '../extension-config';
|
|
10
11
|
|
|
@@ -482,8 +483,72 @@ export default class ExtensionCommand {
|
|
|
482
483
|
*/
|
|
483
484
|
async updateExtension (ext, version) {
|
|
484
485
|
const {pkgName} = this.config.installedExtensions[ext];
|
|
485
|
-
await this.
|
|
486
|
-
this.
|
|
487
|
-
|
|
486
|
+
await fs.rimraf(this.config.getInstallPath(ext));
|
|
487
|
+
const extData = await this.installViaNpm({ext, pkgName, pkgVer: version});
|
|
488
|
+
delete extData[`${this.type}Name`];
|
|
489
|
+
await this.config.updateExtension(ext, extData);
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
/**
|
|
493
|
+
* Runs a script cached inside the "scripts" field under "appium"
|
|
494
|
+
* inside of the driver/plugins "package.json" file. Will throw
|
|
495
|
+
* an error if the driver/plugin does not contain a "scripts" field
|
|
496
|
+
* underneath the "appium" field in its package.json, if the
|
|
497
|
+
* "scripts" field is not a plain object, or if the scriptName is
|
|
498
|
+
* not found within "scripts" object.
|
|
499
|
+
*
|
|
500
|
+
* @param {string} ext - name of the extension to run a script from
|
|
501
|
+
* @param {string} scriptName - name of the script to run
|
|
502
|
+
* @return {RunOutput}
|
|
503
|
+
*/
|
|
504
|
+
async run ({ext, scriptName}) {
|
|
505
|
+
if (!_.has(this.config.installedExtensions, ext)) {
|
|
506
|
+
throw new Error(`please install the ${this.type} first`);
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
const extConfig = this.config.installedExtensions[ext];
|
|
510
|
+
|
|
511
|
+
if (!_.has(extConfig, 'scripts')) {
|
|
512
|
+
throw new Error(`The ${this.type} named '${ext}' does not contain the ` +
|
|
513
|
+
`"scripts" field underneath the "appium" field in its package.json`);
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
const extScripts = extConfig.scripts;
|
|
517
|
+
|
|
518
|
+
if (!_.isPlainObject(extScripts)) {
|
|
519
|
+
throw new Error(`The ${this.type} named '${ext}' "scripts" field must be a plain object`);
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
if (!_.has(extScripts, scriptName)) {
|
|
523
|
+
throw new Error(`The ${this.type} named '${ext}' does not support the script: '${scriptName}'`);
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
const runner = new SubProcess(process.execPath, [extScripts[scriptName]], {
|
|
527
|
+
cwd: this.config.getExtensionRequirePath(ext)
|
|
528
|
+
});
|
|
529
|
+
|
|
530
|
+
const output = new RingBuffer(50);
|
|
531
|
+
|
|
532
|
+
runner.on('stream-line', (line) => {
|
|
533
|
+
output.enqueue(line);
|
|
534
|
+
log(this.isJsonOutput, line);
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
await runner.start(0);
|
|
538
|
+
|
|
539
|
+
try {
|
|
540
|
+
await runner.join();
|
|
541
|
+
log(this.isJsonOutput, `${scriptName} successfully ran`.green);
|
|
542
|
+
return {output: output.getBuff()};
|
|
543
|
+
} catch (err) {
|
|
544
|
+
log(this.isJsonOutput, `Encountered an error when running '${scriptName}': ${err.message}`.red);
|
|
545
|
+
return {error: err.message, output: output.getBuff()};
|
|
546
|
+
}
|
|
488
547
|
}
|
|
489
548
|
}
|
|
549
|
+
|
|
550
|
+
/**
|
|
551
|
+
* @typedef {Object} RunOutput
|
|
552
|
+
* @property {string|undefined} error - error message if script ran unsuccessfully, otherwise undefined
|
|
553
|
+
* @property {string[]} output - script output
|
|
554
|
+
*/
|
package/lib/cli/extension.js
CHANGED
|
@@ -6,6 +6,7 @@ import DriverConfig from '../driver-config';
|
|
|
6
6
|
import PluginConfig from '../plugin-config';
|
|
7
7
|
import { DRIVER_TYPE } from '../extension-config';
|
|
8
8
|
import { errAndQuit, log, JSON_SPACES } from './utils';
|
|
9
|
+
import { APPIUM_HOME } from './args';
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* Run a subcommand of the 'appium driver' type. Each subcommand has its own set of arguments which
|
|
@@ -13,25 +14,44 @@ import { errAndQuit, log, JSON_SPACES } from './utils';
|
|
|
13
14
|
*
|
|
14
15
|
* @param {Object} args - JS object where the key is the parameter name (as defined in
|
|
15
16
|
* driver-parser.js)
|
|
17
|
+
* @param {import('../ext-config-io').ExtensionType} type - Extension type
|
|
18
|
+
* @oaram {ExtensionConfig} [configObject] - Extension config object, if we have one
|
|
16
19
|
*/
|
|
17
|
-
async function runExtensionCommand (args, type) {
|
|
20
|
+
async function runExtensionCommand (args, type, configObject) {
|
|
18
21
|
// TODO driver config file should be locked while any of these commands are
|
|
19
22
|
// running to prevent weird situations
|
|
20
23
|
let jsonResult = null;
|
|
21
|
-
const
|
|
24
|
+
const extCmd = args[`${type}Command`];
|
|
25
|
+
if (!extCmd) {
|
|
26
|
+
throw new TypeError(`Cannot call ${type} command without a subcommand like 'install'`);
|
|
27
|
+
}
|
|
28
|
+
let {json, suppressOutput} = args;
|
|
29
|
+
if (suppressOutput) {
|
|
30
|
+
json = true;
|
|
31
|
+
}
|
|
22
32
|
const logFn = (msg) => log(json, msg);
|
|
23
|
-
|
|
33
|
+
let config;
|
|
34
|
+
if (configObject) {
|
|
35
|
+
config = configObject;
|
|
36
|
+
config.log = logFn;
|
|
37
|
+
} else {
|
|
38
|
+
config = (type === DRIVER_TYPE ? DriverConfig : PluginConfig).getInstance(APPIUM_HOME, logFn);
|
|
39
|
+
}
|
|
24
40
|
const CommandClass = type === DRIVER_TYPE ? DriverCommand : PluginCommand;
|
|
25
|
-
const config = new ConfigClass(appiumHome, logFn);
|
|
26
41
|
const cmd = new CommandClass({config, json});
|
|
27
42
|
try {
|
|
28
43
|
await config.read();
|
|
29
44
|
jsonResult = await cmd.execute(args);
|
|
30
45
|
} catch (err) {
|
|
46
|
+
// in the suppress output case, we are calling this function internally and should
|
|
47
|
+
// just throw instead of printing an error and ending the process
|
|
48
|
+
if (suppressOutput) {
|
|
49
|
+
throw err;
|
|
50
|
+
}
|
|
31
51
|
errAndQuit(json, err);
|
|
32
52
|
}
|
|
33
53
|
|
|
34
|
-
if (json) {
|
|
54
|
+
if (json && !suppressOutput) {
|
|
35
55
|
console.log(JSON.stringify(jsonResult, null, JSON_SPACES));
|
|
36
56
|
}
|
|
37
57
|
|
package/lib/cli/npm.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import _ from 'lodash';
|
|
2
1
|
import path from 'path';
|
|
3
2
|
import semver from 'semver';
|
|
4
3
|
import { exec } from 'teen_process';
|
|
5
|
-
import { mkdirp, util } from 'appium
|
|
4
|
+
import { fs, mkdirp, util, system } from '@appium/support';
|
|
6
5
|
|
|
7
6
|
const INSTALL_LOCKFILE = '.appium.install.lock';
|
|
8
7
|
const LINK_LOCKFILE = '.appium.link.lock';
|
|
@@ -21,6 +20,14 @@ export default class NPM {
|
|
|
21
20
|
// ensure the directory we want to install inside of exists
|
|
22
21
|
await mkdirp(cwd);
|
|
23
22
|
|
|
23
|
+
// not only this, this directory needs a 'package.json' inside of it, otherwise, if any
|
|
24
|
+
// directory in the filesystem tree ABOVE cwd happens to have a package.json or a node_modules
|
|
25
|
+
// dir in it, NPM will install the module up there instead (silly NPM)
|
|
26
|
+
const dummyPkgJson = path.resolve(cwd, 'package.json');
|
|
27
|
+
if (!await fs.exists(dummyPkgJson)) {
|
|
28
|
+
await fs.writeFile(dummyPkgJson, '{}');
|
|
29
|
+
}
|
|
30
|
+
|
|
24
31
|
// make sure we perform the current operation in cwd
|
|
25
32
|
execOpts = {...execOpts, cwd};
|
|
26
33
|
|
|
@@ -28,7 +35,8 @@ export default class NPM {
|
|
|
28
35
|
if (json) {
|
|
29
36
|
args.push('-json');
|
|
30
37
|
}
|
|
31
|
-
|
|
38
|
+
const npmCmd = system.isWindows() ? 'npm.cmd' : 'npm';
|
|
39
|
+
let runner = async () => await exec(npmCmd, args, execOpts);
|
|
32
40
|
if (lockFile) {
|
|
33
41
|
const acquireLock = util.getLockFileGuard(path.resolve(cwd, lockFile));
|
|
34
42
|
const _runner = runner;
|
|
@@ -124,14 +132,6 @@ export default class NPM {
|
|
|
124
132
|
if (res.json.error) {
|
|
125
133
|
throw new Error(res.json.error);
|
|
126
134
|
}
|
|
127
|
-
|
|
128
|
-
// if we tried to install a package via git/github, we might not have been
|
|
129
|
-
// certain of the package name, so if we can retrieve it unambiguously from
|
|
130
|
-
// the json report of the install, do so
|
|
131
|
-
const names = _.uniq([...res.json.added, ...res.json.updated].map((x) => x.name));
|
|
132
|
-
if (names.length === 1) {
|
|
133
|
-
pkgName = names[0];
|
|
134
|
-
}
|
|
135
135
|
}
|
|
136
136
|
|
|
137
137
|
// Now read package data from the installed package to return, and make sure
|
|
@@ -159,8 +159,14 @@ export default class NPM {
|
|
|
159
159
|
`provided: ${pkgPath}`);
|
|
160
160
|
}
|
|
161
161
|
|
|
162
|
+
// this is added to handle commands with relative paths
|
|
163
|
+
// ie: "node . driver install --source=local ../fake-driver"
|
|
164
|
+
pkgPath = path.resolve(process.cwd(), pkgPath);
|
|
165
|
+
|
|
162
166
|
const pkgHome = path.resolve(this.appiumHome, pkgName);
|
|
163
|
-
|
|
167
|
+
|
|
168
|
+
// call link with --no-package-lock to ensure no corruption while installing local packages
|
|
169
|
+
const res = await this.exec('link', ['--no-package-lock', pkgPath], {cwd: pkgHome, lockFile: LINK_LOCKFILE});
|
|
164
170
|
if (res.json && res.json.error) {
|
|
165
171
|
throw new Error(res.json.error);
|
|
166
172
|
}
|
package/lib/cli/parser.js
CHANGED
|
@@ -1,98 +1,273 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
import { fs } from '@appium/support';
|
|
3
4
|
import { ArgumentParser } from 'argparse';
|
|
4
|
-
import
|
|
5
|
+
import B from 'bluebird';
|
|
6
|
+
import _ from 'lodash';
|
|
7
|
+
import path from 'path';
|
|
5
8
|
import { DRIVER_TYPE, PLUGIN_TYPE } from '../extension-config';
|
|
9
|
+
import { finalizeSchema, getArgSpec, hasArgSpec } from '../schema';
|
|
6
10
|
import { rootDir } from '../utils';
|
|
11
|
+
import {
|
|
12
|
+
driverConfig,
|
|
13
|
+
getExtensionArgs,
|
|
14
|
+
getServerArgs,
|
|
15
|
+
pluginConfig
|
|
16
|
+
} from './args';
|
|
7
17
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
18
|
+
export const SERVER_SUBCOMMAND = 'server';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* If the parsed args do not contain any of these values, then we
|
|
22
|
+
* will automatially inject the `server` subcommand.
|
|
23
|
+
*/
|
|
24
|
+
const NON_SERVER_ARGS = Object.freeze(
|
|
25
|
+
new Set([
|
|
26
|
+
DRIVER_TYPE,
|
|
27
|
+
PLUGIN_TYPE,
|
|
28
|
+
SERVER_SUBCOMMAND,
|
|
29
|
+
'-h',
|
|
30
|
+
'--help',
|
|
31
|
+
'-v',
|
|
32
|
+
'--version'
|
|
33
|
+
])
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
const version = fs.readPackageJsonFrom(rootDir).version;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* A wrapper around `argparse`
|
|
40
|
+
*
|
|
41
|
+
* - Handles instantiation, configuration, and monkeypatching of an
|
|
42
|
+
* `ArgumentParser` instance for Appium server and its extensions
|
|
43
|
+
* - Handles error conditions, messages, and exit behavior
|
|
44
|
+
*/
|
|
45
|
+
class ArgParser {
|
|
46
|
+
/**
|
|
47
|
+
* @param {boolean} [debug] - If true, throw instead of exit on error.
|
|
48
|
+
*/
|
|
49
|
+
constructor (debug = false) {
|
|
50
|
+
const prog = process.argv[1] ? path.basename(process.argv[1]) : 'appium';
|
|
51
|
+
const parser = new ArgumentParser({
|
|
52
|
+
add_help: true,
|
|
53
|
+
description:
|
|
54
|
+
'A webdriver-compatible server that facilitates automation of web, mobile, and other types of apps across various platforms.',
|
|
55
|
+
prog,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
ArgParser._patchExit(parser);
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Program name (typically `appium`)
|
|
62
|
+
* @type {string}
|
|
63
|
+
*/
|
|
64
|
+
this.prog = prog;
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* If `true`, throw an error on parse failure instead of printing help
|
|
68
|
+
* @type {boolean}
|
|
69
|
+
*/
|
|
70
|
+
this.debug = debug;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Wrapped `ArgumentParser` instance
|
|
74
|
+
* @type {ArgumentParser}
|
|
75
|
+
*/
|
|
76
|
+
this.parser = parser;
|
|
77
|
+
|
|
78
|
+
parser.add_argument('-v', '--version', {
|
|
79
|
+
action: 'version',
|
|
80
|
+
version,
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
const subParsers = parser.add_subparsers({dest: 'subcommand'});
|
|
84
|
+
|
|
85
|
+
// add the 'server' subcommand, and store the raw arguments on the parser
|
|
86
|
+
// object as a way for other parts of the code to work with the arguments
|
|
87
|
+
// conceptually rather than just through argparse
|
|
88
|
+
const serverArgs = ArgParser._addServerToParser(subParsers);
|
|
89
|
+
|
|
90
|
+
this.rawArgs = serverArgs;
|
|
91
|
+
|
|
92
|
+
// add the 'driver' and 'plugin' subcommands
|
|
93
|
+
ArgParser._addExtensionCommandsToParser(subParsers);
|
|
94
|
+
|
|
95
|
+
// backwards compatibility / drop-in wrapper
|
|
96
|
+
/**
|
|
97
|
+
* @type {ArgParser['parseArgs']}
|
|
98
|
+
*/
|
|
99
|
+
this.parse_args = this.parseArgs;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Parse arguments from the command line.
|
|
104
|
+
*
|
|
105
|
+
* If no subcommand is passed in, this method will inject the `server` subcommand.
|
|
106
|
+
*
|
|
107
|
+
* `ArgParser.prototype.parse_args` is an alias of this method.
|
|
108
|
+
* @param {string[]} [args] - Array of arguments, ostensibly from `process.argv`. Gathers args from `process.argv` if not provided.
|
|
109
|
+
* @returns {import('../../types/types').ParsedArgs} - The parsed arguments
|
|
110
|
+
*/
|
|
111
|
+
parseArgs (args = process.argv.slice(2)) {
|
|
112
|
+
if (!NON_SERVER_ARGS.has(args[0])) {
|
|
113
|
+
args.unshift(SERVER_SUBCOMMAND);
|
|
36
114
|
}
|
|
37
|
-
return this._parseArgs(args, namespace);
|
|
38
|
-
}.bind(parser);
|
|
39
|
-
return parser;
|
|
40
|
-
}
|
|
41
115
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
116
|
+
try {
|
|
117
|
+
const parsed = this.parser.parse_args(args);
|
|
118
|
+
return ArgParser._transformParsedArgs(parsed);
|
|
119
|
+
} catch (err) {
|
|
120
|
+
if (this.debug) {
|
|
121
|
+
throw err;
|
|
122
|
+
}
|
|
123
|
+
// this isn't tested via unit tests (we use `debug: true`) so may escape coverage.
|
|
48
124
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
125
|
+
/* istanbul ignore next */
|
|
126
|
+
{
|
|
127
|
+
// eslint-disable-next-line no-console
|
|
128
|
+
console.error(); // need an extra space since argparse prints usage.
|
|
129
|
+
// eslint-disable-next-line no-console
|
|
130
|
+
console.error(err.message);
|
|
131
|
+
process.exit(1);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
52
134
|
}
|
|
53
135
|
|
|
54
|
-
|
|
55
|
-
|
|
136
|
+
/**
|
|
137
|
+
* Given an object full of arguments as returned by `argparser.parse_args`,
|
|
138
|
+
* expand the ones for extensions into a nested object structure and rename
|
|
139
|
+
* keys to match the intended destination.
|
|
140
|
+
*
|
|
141
|
+
* E.g., `{'driver-foo-bar': baz}` becomes `{driver: {foo: {bar: 'baz'}}}`
|
|
142
|
+
* @param {object} args
|
|
143
|
+
* @returns {object}
|
|
144
|
+
*/
|
|
145
|
+
static _transformParsedArgs (args) {
|
|
146
|
+
return _.reduce(
|
|
147
|
+
args,
|
|
148
|
+
(unpacked, value, key) => {
|
|
149
|
+
if (hasArgSpec(key)) {
|
|
150
|
+
const {dest} = /** @type {import('../schema/arg-spec').ArgSpec} */(getArgSpec(key));
|
|
151
|
+
_.set(unpacked, dest, value);
|
|
152
|
+
} else {
|
|
153
|
+
// this could be anything that _isn't_ a server arg
|
|
154
|
+
unpacked[key] = value;
|
|
155
|
+
}
|
|
156
|
+
return unpacked;
|
|
157
|
+
},
|
|
158
|
+
{},
|
|
159
|
+
);
|
|
160
|
+
}
|
|
56
161
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
162
|
+
/**
|
|
163
|
+
* Patches the `exit()` method of the parser to throw an error, so we can handle it manually.
|
|
164
|
+
* @param {ArgumentParser} parser
|
|
165
|
+
*/
|
|
166
|
+
static _patchExit (parser) {
|
|
167
|
+
parser.exit = (code, msg) => {
|
|
168
|
+
throw new Error(msg);
|
|
169
|
+
};
|
|
61
170
|
}
|
|
62
|
-
return defaults;
|
|
63
|
-
}
|
|
64
171
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
debug
|
|
172
|
+
/**
|
|
173
|
+
*
|
|
174
|
+
* @param {import('argparse').SubParser} subParser
|
|
175
|
+
* @returns {import('./args').ArgumentDefinitions}
|
|
176
|
+
*/
|
|
177
|
+
static _addServerToParser (subParser) {
|
|
178
|
+
const serverParser = subParser.add_parser('server', {
|
|
179
|
+
add_help: true,
|
|
180
|
+
help: 'Run an Appium server',
|
|
75
181
|
});
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
182
|
+
|
|
183
|
+
ArgParser._patchExit(serverParser);
|
|
184
|
+
|
|
185
|
+
const serverArgs = getServerArgs();
|
|
186
|
+
for (const [flagsOrNames, opts] of serverArgs) {
|
|
187
|
+
// TS doesn't like the spread operator here.
|
|
188
|
+
// @ts-ignore
|
|
189
|
+
serverParser.add_argument(...flagsOrNames, {...opts});
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return serverArgs;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Adds extension sub-sub-commands to `driver`/`plugin` subcommands
|
|
197
|
+
* @param {import('argparse').SubParser} subParsers
|
|
198
|
+
*/
|
|
199
|
+
static _addExtensionCommandsToParser (subParsers) {
|
|
200
|
+
for (const type of [DRIVER_TYPE, PLUGIN_TYPE]) {
|
|
201
|
+
const extParser = subParsers.add_parser(type, {
|
|
202
|
+
add_help: true,
|
|
203
|
+
help: `Access the ${type} management CLI commands`,
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
ArgParser._patchExit(extParser);
|
|
207
|
+
|
|
208
|
+
const extSubParsers = extParser.add_subparsers({
|
|
209
|
+
dest: `${type}Command`,
|
|
210
|
+
});
|
|
211
|
+
const extensionArgs = getExtensionArgs();
|
|
212
|
+
const parserSpecs = [
|
|
213
|
+
{
|
|
214
|
+
command: 'list',
|
|
215
|
+
args: extensionArgs[type].list,
|
|
216
|
+
help: `List available and installed ${type}s`,
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
command: 'install',
|
|
220
|
+
args: extensionArgs[type].install,
|
|
221
|
+
help: `Install a ${type}`,
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
command: 'uninstall',
|
|
225
|
+
args: extensionArgs[type].uninstall,
|
|
226
|
+
help: `Uninstall a ${type}`,
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
command: 'update',
|
|
230
|
+
args: extensionArgs[type].update,
|
|
231
|
+
help: `Update installed ${type}s to the latest version`,
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
command: 'run',
|
|
235
|
+
args: extensionArgs[type].run,
|
|
236
|
+
help:
|
|
237
|
+
`Run a script (defined inside the ${type}'s package.json under the ` +
|
|
238
|
+
`“scripts” field inside the “appium” field) from an installed ${type}`,
|
|
239
|
+
},
|
|
240
|
+
];
|
|
241
|
+
|
|
242
|
+
for (const {command, args, help} of parserSpecs) {
|
|
243
|
+
const parser = extSubParsers.add_parser(command, {help});
|
|
244
|
+
|
|
245
|
+
ArgParser._patchExit(parser);
|
|
246
|
+
|
|
247
|
+
for (const [flagsOrNames, opts] of args) {
|
|
248
|
+
// add_argument mutates params so make sure to send in copies instead
|
|
249
|
+
// @ts-ignore
|
|
250
|
+
parser.add_argument(...flagsOrNames, {...opts});
|
|
251
|
+
}
|
|
92
252
|
}
|
|
93
253
|
}
|
|
94
254
|
}
|
|
95
255
|
}
|
|
96
256
|
|
|
257
|
+
/**
|
|
258
|
+
* Creates a {@link ArgParser} instance. Necessarily reads extension configuration
|
|
259
|
+
* beforehand, and finalizes the config schema.
|
|
260
|
+
*
|
|
261
|
+
* @constructs ArgParser
|
|
262
|
+
* @param {boolean} [debug] - If `true`, throw instead of exit upon parsing error
|
|
263
|
+
* @returns {Promise<ArgParser>}
|
|
264
|
+
*/
|
|
265
|
+
async function getParser (debug = false) {
|
|
266
|
+
await B.all([driverConfig.read(), pluginConfig.read()]);
|
|
267
|
+
finalizeSchema();
|
|
268
|
+
|
|
269
|
+
return new ArgParser(debug);
|
|
270
|
+
}
|
|
271
|
+
|
|
97
272
|
export default getParser;
|
|
98
|
-
export { getParser,
|
|
273
|
+
export { getParser, ArgParser };
|
|
@@ -24,6 +24,10 @@ export default class PluginCommand extends ExtensionCommand {
|
|
|
24
24
|
return await super.update({ext: plugin, unsafe});
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
async run ({plugin, scriptName}) {
|
|
28
|
+
return await super.run({ext: plugin, scriptName});
|
|
29
|
+
}
|
|
30
|
+
|
|
27
31
|
getPostInstallText ({extName, extData}) {
|
|
28
32
|
return `Plugin ${extName}@${extData.version} successfully installed`.green;
|
|
29
33
|
}
|
package/lib/cli/utils.js
CHANGED
|
@@ -52,9 +52,29 @@ async function spinWith (json, msg, fn) {
|
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
class RingBuffer {
|
|
56
|
+
constructor (size = 50) {
|
|
57
|
+
this.size = size;
|
|
58
|
+
this.buffer = [];
|
|
59
|
+
}
|
|
60
|
+
getBuff () {
|
|
61
|
+
return this.buffer;
|
|
62
|
+
}
|
|
63
|
+
dequeue () {
|
|
64
|
+
this.buffer.shift();
|
|
65
|
+
}
|
|
66
|
+
enqueue (item) {
|
|
67
|
+
if (this.buffer.length >= this.size) {
|
|
68
|
+
this.dequeue();
|
|
69
|
+
}
|
|
70
|
+
this.buffer.push(item);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
55
74
|
export {
|
|
56
75
|
errAndQuit,
|
|
57
76
|
log,
|
|
58
77
|
spinWith,
|
|
59
|
-
JSON_SPACES
|
|
78
|
+
JSON_SPACES,
|
|
79
|
+
RingBuffer
|
|
60
80
|
};
|