appium 3.2.2 → 3.3.0
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/build/lib/cli/args.d.ts +16 -12
- package/build/lib/cli/args.d.ts.map +1 -1
- package/build/lib/cli/args.js +15 -35
- package/build/lib/cli/args.js.map +1 -1
- package/build/lib/cli/driver-command.d.ts +51 -93
- package/build/lib/cli/driver-command.d.ts.map +1 -1
- package/build/lib/cli/driver-command.js +11 -66
- package/build/lib/cli/driver-command.js.map +1 -1
- package/build/lib/cli/extension-command.d.ts +211 -415
- package/build/lib/cli/extension-command.d.ts.map +1 -1
- package/build/lib/cli/extension-command.js +384 -653
- package/build/lib/cli/extension-command.js.map +1 -1
- package/build/lib/cli/extension.d.ts +11 -16
- package/build/lib/cli/extension.d.ts.map +1 -1
- package/build/lib/cli/extension.js +10 -28
- package/build/lib/cli/extension.js.map +1 -1
- package/build/lib/cli/parser.d.ts +40 -69
- package/build/lib/cli/parser.d.ts.map +1 -1
- package/build/lib/cli/parser.js +24 -59
- package/build/lib/cli/parser.js.map +1 -1
- package/build/lib/cli/plugin-command.d.ts +50 -90
- package/build/lib/cli/plugin-command.d.ts.map +1 -1
- package/build/lib/cli/plugin-command.js +11 -63
- package/build/lib/cli/plugin-command.js.map +1 -1
- package/build/lib/cli/setup-command.d.ts +21 -26
- package/build/lib/cli/setup-command.d.ts.map +1 -1
- package/build/lib/cli/setup-command.js +13 -55
- package/build/lib/cli/setup-command.js.map +1 -1
- package/build/lib/cli/utils.d.ts +27 -29
- package/build/lib/cli/utils.d.ts.map +1 -1
- package/build/lib/cli/utils.js +29 -31
- package/build/lib/cli/utils.js.map +1 -1
- package/build/lib/config-file.d.ts +24 -67
- package/build/lib/config-file.d.ts.map +1 -1
- package/build/lib/config-file.js +56 -115
- package/build/lib/config-file.js.map +1 -1
- package/build/lib/config.d.ts +42 -44
- package/build/lib/config.d.ts.map +1 -1
- package/build/lib/config.js +75 -107
- package/build/lib/config.js.map +1 -1
- package/build/lib/constants.d.ts +23 -23
- package/build/lib/constants.d.ts.map +1 -1
- package/build/lib/constants.js +10 -15
- package/build/lib/constants.js.map +1 -1
- package/build/lib/doctor/doctor.d.ts +40 -57
- package/build/lib/doctor/doctor.d.ts.map +1 -1
- package/build/lib/doctor/doctor.js +29 -60
- package/build/lib/doctor/doctor.js.map +1 -1
- package/build/lib/grid-register.d.ts +32 -7
- package/build/lib/grid-register.d.ts.map +1 -1
- package/build/lib/grid-register.js +84 -48
- package/build/lib/grid-register.js.map +1 -1
- package/build/lib/logsink.d.ts +13 -22
- package/build/lib/logsink.d.ts.map +1 -1
- package/build/lib/logsink.js +48 -103
- package/build/lib/logsink.js.map +1 -1
- package/build/lib/main.js +1 -1
- package/build/lib/main.js.map +1 -1
- package/build/lib/schema/arg-spec.d.ts +32 -107
- package/build/lib/schema/arg-spec.d.ts.map +1 -1
- package/build/lib/schema/arg-spec.js +11 -107
- package/build/lib/schema/arg-spec.js.map +1 -1
- package/build/lib/schema/cli-args.d.ts +3 -15
- package/build/lib/schema/cli-args.d.ts.map +1 -1
- package/build/lib/schema/cli-args.js +15 -105
- package/build/lib/schema/cli-args.js.map +1 -1
- package/build/lib/schema/cli-transformers.d.ts +15 -12
- package/build/lib/schema/cli-transformers.d.ts.map +1 -1
- package/build/lib/schema/cli-transformers.js +15 -45
- package/build/lib/schema/cli-transformers.js.map +1 -1
- package/build/lib/schema/index.d.ts +2 -2
- package/build/lib/schema/index.d.ts.map +1 -1
- package/build/lib/schema/index.js.map +1 -1
- package/build/lib/schema/keywords.d.ts +12 -20
- package/build/lib/schema/keywords.d.ts.map +1 -1
- package/build/lib/schema/keywords.js +6 -51
- package/build/lib/schema/keywords.js.map +1 -1
- package/build/lib/schema/schema.d.ts +106 -231
- package/build/lib/schema/schema.d.ts.map +1 -1
- package/build/lib/schema/schema.js +75 -345
- package/build/lib/schema/schema.js.map +1 -1
- package/build/lib/utils.d.ts +59 -238
- package/build/lib/utils.d.ts.map +1 -1
- package/build/lib/utils.js +55 -207
- package/build/lib/utils.js.map +1 -1
- package/lib/cli/{args.js → args.ts} +40 -51
- package/lib/cli/driver-command.ts +122 -0
- package/lib/cli/{extension-command.js → extension-command.ts} +610 -689
- package/lib/cli/extension.ts +65 -0
- package/lib/cli/{parser.js → parser.ts} +48 -71
- package/lib/cli/plugin-command.ts +117 -0
- package/lib/cli/{setup-command.js → setup-command.ts} +57 -72
- package/lib/cli/utils.ts +97 -0
- package/lib/config-file.ts +212 -0
- package/lib/{config.js → config.ts} +129 -141
- package/lib/{constants.js → constants.ts} +30 -41
- package/lib/doctor/{doctor.js → doctor.ts} +81 -91
- package/lib/grid-register.ts +250 -0
- package/lib/{logsink.js → logsink.ts} +91 -137
- package/lib/main.js +1 -1
- package/lib/schema/arg-spec.ts +131 -0
- package/lib/schema/cli-args.ts +171 -0
- package/lib/schema/cli-transformers.ts +83 -0
- package/lib/schema/keywords.ts +96 -0
- package/lib/schema/schema.ts +449 -0
- package/lib/utils.ts +404 -0
- package/package.json +16 -16
- package/lib/cli/driver-command.js +0 -174
- package/lib/cli/extension.js +0 -74
- package/lib/cli/plugin-command.js +0 -164
- package/lib/cli/utils.js +0 -91
- package/lib/config-file.js +0 -228
- package/lib/grid-register.js +0 -146
- package/lib/schema/arg-spec.js +0 -229
- package/lib/schema/cli-args.js +0 -254
- package/lib/schema/cli-transformers.js +0 -113
- package/lib/schema/keywords.js +0 -136
- package/lib/schema/schema.js +0 -725
- package/lib/utils.js +0 -512
- /package/lib/schema/{index.js → index.ts} +0 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
import type {Class, DriverType, ExtensionType, PluginType} from '@appium/types';
|
|
3
|
+
import type {Args, CliExtensionCommand, CliExtensionSubcommand} from 'appium/types';
|
|
4
|
+
import type {ExtensionConfig} from '../extension/extension-config';
|
|
5
|
+
import {DRIVER_TYPE, PLUGIN_TYPE} from '../constants';
|
|
6
|
+
import {isExtensionCommandArgs} from '../utils';
|
|
7
|
+
import DriverCliCommand from './driver-command';
|
|
8
|
+
import PluginCliCommand from './plugin-command';
|
|
9
|
+
import {errAndQuit, JSON_SPACES} from './utils';
|
|
10
|
+
|
|
11
|
+
export const commandClasses = Object.freeze(
|
|
12
|
+
{
|
|
13
|
+
[DRIVER_TYPE]: DriverCliCommand,
|
|
14
|
+
[PLUGIN_TYPE]: PluginCliCommand,
|
|
15
|
+
} as const
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Executes a driver/plugin extension subcommand and returns the command result.
|
|
20
|
+
*
|
|
21
|
+
* When JSON output is enabled, this also prints the serialized command result
|
|
22
|
+
* unless output was suppressed by the caller.
|
|
23
|
+
*/
|
|
24
|
+
export async function runExtensionCommand<
|
|
25
|
+
Cmd extends CliExtensionCommand,
|
|
26
|
+
SubCmd extends CliExtensionSubcommand,
|
|
27
|
+
>(args: Args<Cmd, SubCmd>, config: ExtensionConfig<Cmd>) {
|
|
28
|
+
// TODO driver config file should be locked while any of these commands are
|
|
29
|
+
// running to prevent weird situations
|
|
30
|
+
let jsonResult: Record<string, unknown> = {};
|
|
31
|
+
const {extensionType: type} = config; // NOTE this is the same as `args.subcommand`
|
|
32
|
+
if (!isExtensionCommandArgs(args)) {
|
|
33
|
+
throw new TypeError(`Cannot call ${type} command without a subcommand like 'install'`);
|
|
34
|
+
}
|
|
35
|
+
let {json} = args;
|
|
36
|
+
const {suppressOutput} = args;
|
|
37
|
+
json = Boolean(json);
|
|
38
|
+
if (suppressOutput) {
|
|
39
|
+
json = true;
|
|
40
|
+
}
|
|
41
|
+
const CommandClass = commandClasses[type] as ExtCommand<Cmd>;
|
|
42
|
+
const cmd = new CommandClass({config, json} as any);
|
|
43
|
+
try {
|
|
44
|
+
jsonResult = (await cmd.execute(args)) as Record<string, unknown>;
|
|
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
|
+
}
|
|
51
|
+
errAndQuit(json, err);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (json && !suppressOutput) {
|
|
55
|
+
console.log(JSON.stringify(jsonResult, null, JSON_SPACES));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return jsonResult;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export type ExtCommand<ExtType extends ExtensionType> = ExtType extends DriverType
|
|
62
|
+
? Class<DriverCliCommand>
|
|
63
|
+
: ExtType extends PluginType
|
|
64
|
+
? Class<PluginCliCommand>
|
|
65
|
+
: never;
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import {fs} from '@appium/support';
|
|
2
2
|
import {ArgumentParser} from 'argparse';
|
|
3
|
+
import type {SubArgumentParserOptions, SubParser} from 'argparse';
|
|
3
4
|
import _ from 'lodash';
|
|
4
5
|
import path from 'node:path';
|
|
6
|
+
import type {DriverType, PluginType} from '@appium/types';
|
|
7
|
+
import type {CliExtensionSubcommand} from 'appium/types';
|
|
5
8
|
import {
|
|
6
9
|
DRIVER_TYPE,
|
|
7
10
|
EXT_SUBCOMMAND_DOCTOR,
|
|
@@ -17,6 +20,7 @@ import {
|
|
|
17
20
|
import {finalizeSchema, getAllArgSpecs, getArgSpec, hasArgSpec} from '../schema';
|
|
18
21
|
import {rootDir} from '../config';
|
|
19
22
|
import {getExtensionArgs, getServerArgs} from './args';
|
|
23
|
+
import type {ArgumentDefinitions} from './args';
|
|
20
24
|
import {
|
|
21
25
|
DEFAULT_PLUGINS,
|
|
22
26
|
SUBCOMMAND_MOBILE,
|
|
@@ -38,6 +42,8 @@ const NON_SERVER_ARGS = Object.freeze(
|
|
|
38
42
|
);
|
|
39
43
|
|
|
40
44
|
const version = fs.readPackageJsonFrom(rootDir).version;
|
|
45
|
+
type LooseArgsMap = {[key: string]: any};
|
|
46
|
+
type TransformedArgsMap = LooseArgsMap & {[EXTRA_ARGS]: string[]};
|
|
41
47
|
|
|
42
48
|
/**
|
|
43
49
|
* A wrapper around `argparse`
|
|
@@ -46,9 +52,15 @@ const version = fs.readPackageJsonFrom(rootDir).version;
|
|
|
46
52
|
* `ArgumentParser` instance for Appium server and its extensions
|
|
47
53
|
* - Handles error conditions, messages, and exit behavior
|
|
48
54
|
*/
|
|
49
|
-
class ArgParser {
|
|
55
|
+
export class ArgParser {
|
|
56
|
+
readonly prog: string;
|
|
57
|
+
readonly debug: boolean;
|
|
58
|
+
readonly parser: ArgumentParser;
|
|
59
|
+
readonly rawArgs: ArgumentDefinitions;
|
|
60
|
+
readonly parse_args: ArgParser['parseArgs'];
|
|
61
|
+
|
|
50
62
|
/**
|
|
51
|
-
* @param
|
|
63
|
+
* @param debug - if true, throw instead of exiting on parse errors
|
|
52
64
|
*/
|
|
53
65
|
constructor(debug = false) {
|
|
54
66
|
const prog = process.argv[1] ? path.basename(process.argv[1]) : 'appium';
|
|
@@ -62,22 +74,10 @@ class ArgParser {
|
|
|
62
74
|
|
|
63
75
|
ArgParser._patchExit(parser);
|
|
64
76
|
|
|
65
|
-
/**
|
|
66
|
-
* Program name (typically `appium`)
|
|
67
|
-
* @type {string}
|
|
68
|
-
*/
|
|
69
77
|
this.prog = prog;
|
|
70
78
|
|
|
71
|
-
/**
|
|
72
|
-
* If `true`, throw an error on parse failure instead of printing help
|
|
73
|
-
* @type {boolean}
|
|
74
|
-
*/
|
|
75
79
|
this.debug = debug;
|
|
76
80
|
|
|
77
|
-
/**
|
|
78
|
-
* Wrapped `ArgumentParser` instance
|
|
79
|
-
* @type {ArgumentParser}
|
|
80
|
-
*/
|
|
81
81
|
this.parser = parser;
|
|
82
82
|
|
|
83
83
|
parser.add_argument('-v', '--version', {
|
|
@@ -101,23 +101,16 @@ class ArgParser {
|
|
|
101
101
|
ArgParser._addSetupToParser(subParsers);
|
|
102
102
|
|
|
103
103
|
// backwards compatibility / drop-in wrapper
|
|
104
|
-
/**
|
|
105
|
-
* @type {ArgParser['parseArgs']}
|
|
106
|
-
*/
|
|
107
104
|
this.parse_args = this.parseArgs;
|
|
108
105
|
}
|
|
109
106
|
|
|
110
107
|
/**
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
* If no subcommand is passed in, this method will inject the `server` subcommand.
|
|
108
|
+
* Parses CLI args and returns Appium's normalized argument object.
|
|
114
109
|
*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
* @param {string[]} [args] - Array of arguments, ostensibly from `process.argv`. Gathers args from `process.argv` if not provided.
|
|
118
|
-
* @returns {import('appium/types').Args<Cmd>} - The parsed arguments
|
|
110
|
+
* If no explicit subcommand is provided, this injects `server`.
|
|
111
|
+
* `parse_args` is a backwards-compatible alias of this method.
|
|
119
112
|
*/
|
|
120
|
-
parseArgs(args = process.argv.slice(2)) {
|
|
113
|
+
parseArgs(args: string[] = process.argv.slice(2)): TransformedArgsMap {
|
|
121
114
|
if (!NON_SERVER_ARGS.has(args[0])) {
|
|
122
115
|
args.unshift(SERVER_SUBCOMMAND);
|
|
123
116
|
}
|
|
@@ -159,19 +152,16 @@ class ArgParser {
|
|
|
159
152
|
}
|
|
160
153
|
|
|
161
154
|
/**
|
|
162
|
-
*
|
|
163
|
-
* Use when server args come from programmatic init rather than the CLI, so they match
|
|
164
|
-
* the shape produced by parseArgs() / _transformParsedArgs().
|
|
165
|
-
* Mutates the given object.
|
|
155
|
+
* Normalizes server arg keys from schema names to parser destination names.
|
|
166
156
|
*
|
|
167
|
-
*
|
|
168
|
-
* @returns {object} The same object with keys normalized
|
|
157
|
+
* This mutates and returns the same object.
|
|
169
158
|
*/
|
|
170
|
-
static normalizeServerArgs(obj) {
|
|
159
|
+
static normalizeServerArgs<T extends LooseArgsMap>(obj: T): T {
|
|
160
|
+
const mutableObj = obj as LooseArgsMap;
|
|
171
161
|
for (const spec of getAllArgSpecs().values()) {
|
|
172
|
-
if (!spec.extType &&
|
|
173
|
-
|
|
174
|
-
delete
|
|
162
|
+
if (!spec.extType && mutableObj[spec.name] !== undefined && spec.rawDest !== spec.name) {
|
|
163
|
+
mutableObj[spec.rawDest] = mutableObj[spec.name] ?? mutableObj[spec.rawDest];
|
|
164
|
+
delete mutableObj[spec.name];
|
|
175
165
|
}
|
|
176
166
|
}
|
|
177
167
|
return obj;
|
|
@@ -183,16 +173,17 @@ class ArgParser {
|
|
|
183
173
|
* keys to match the intended destination.
|
|
184
174
|
*
|
|
185
175
|
* E.g., `{'driver-foo-bar': baz}` becomes `{driver: {foo: {bar: 'baz'}}}`
|
|
186
|
-
* @param {object} args
|
|
187
|
-
* @param {string[]} [unknownArgs]
|
|
188
|
-
* @returns {object}
|
|
189
176
|
*/
|
|
190
|
-
static _transformParsedArgs(
|
|
177
|
+
private static _transformParsedArgs(
|
|
178
|
+
args: LooseArgsMap,
|
|
179
|
+
unknownArgs: string[] = []
|
|
180
|
+
): TransformedArgsMap {
|
|
191
181
|
const result = _.reduce(
|
|
192
182
|
args,
|
|
193
183
|
(unpacked, value, key) => {
|
|
194
|
-
|
|
195
|
-
|
|
184
|
+
const spec = hasArgSpec(key) ? getArgSpec(key) : undefined;
|
|
185
|
+
if (!_.isUndefined(value) && spec) {
|
|
186
|
+
const {dest} = spec;
|
|
196
187
|
_.set(unpacked, dest, value);
|
|
197
188
|
} else {
|
|
198
189
|
// this could be anything that _isn't_ a server arg
|
|
@@ -203,14 +194,13 @@ class ArgParser {
|
|
|
203
194
|
{}
|
|
204
195
|
);
|
|
205
196
|
result[EXTRA_ARGS] = unknownArgs;
|
|
206
|
-
return result;
|
|
197
|
+
return result as TransformedArgsMap;
|
|
207
198
|
}
|
|
208
199
|
|
|
209
200
|
/**
|
|
210
201
|
* Patches the `exit()` method of the parser to throw an error, so we can handle it manually.
|
|
211
|
-
* @param {ArgumentParser} parser
|
|
212
202
|
*/
|
|
213
|
-
static _patchExit(parser) {
|
|
203
|
+
private static _patchExit(parser: ArgumentParser): void {
|
|
214
204
|
parser.exit = (code, msg) => {
|
|
215
205
|
if (code) {
|
|
216
206
|
throw new Error(msg);
|
|
@@ -220,11 +210,9 @@ class ArgParser {
|
|
|
220
210
|
}
|
|
221
211
|
|
|
222
212
|
/**
|
|
223
|
-
*
|
|
224
|
-
* @param {import('argparse').SubParser} subParser
|
|
225
|
-
* @returns {import('./args').ArgumentDefinitions}
|
|
213
|
+
* Adds the `server` subcommand parser and returns its argument definitions.
|
|
226
214
|
*/
|
|
227
|
-
static _addServerToParser(subParser) {
|
|
215
|
+
private static _addServerToParser(subParser: SubParser): ArgumentDefinitions {
|
|
228
216
|
const serverParser = subParser.add_parser('server', {
|
|
229
217
|
add_help: true,
|
|
230
218
|
help: 'Start an Appium server',
|
|
@@ -244,11 +232,10 @@ class ArgParser {
|
|
|
244
232
|
|
|
245
233
|
/**
|
|
246
234
|
* Adds extension sub-sub-commands to `driver`/`plugin` subcommands
|
|
247
|
-
* @param {import('argparse').SubParser} subParsers
|
|
248
235
|
*/
|
|
249
|
-
static _addExtensionCommandsToParser(
|
|
250
|
-
for (const type of
|
|
251
|
-
const extParser =
|
|
236
|
+
private static _addExtensionCommandsToParser(subParser: SubParser): void {
|
|
237
|
+
for (const type of [DRIVER_TYPE, PLUGIN_TYPE] as [DriverType, PluginType]) {
|
|
238
|
+
const extParser = subParser.add_parser(type, {
|
|
252
239
|
add_help: true,
|
|
253
240
|
help: `Manage Appium ${type}s`,
|
|
254
241
|
description: `Manage Appium ${type}s using various subcommands`,
|
|
@@ -260,10 +247,12 @@ class ArgParser {
|
|
|
260
247
|
dest: `${type}Command`,
|
|
261
248
|
});
|
|
262
249
|
const extensionArgs = getExtensionArgs();
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
250
|
+
const parserSpecs: {
|
|
251
|
+
command: CliExtensionSubcommand;
|
|
252
|
+
args: ArgumentDefinitions;
|
|
253
|
+
help: string;
|
|
254
|
+
aliases?: SubArgumentParserOptions['aliases'];
|
|
255
|
+
}[] = [
|
|
267
256
|
{
|
|
268
257
|
command: EXT_SUBCOMMAND_LIST,
|
|
269
258
|
args: extensionArgs[type].list,
|
|
@@ -316,9 +305,8 @@ class ArgParser {
|
|
|
316
305
|
|
|
317
306
|
/**
|
|
318
307
|
* Add subcommand and sub-sub commands for 'setup' subcommand.
|
|
319
|
-
* @param {import('argparse').SubParser} subParser
|
|
320
308
|
*/
|
|
321
|
-
static _addSetupToParser(subParser) {
|
|
309
|
+
private static _addSetupToParser(subParser: SubParser): void {
|
|
322
310
|
const setupParser = subParser.add_parser('setup', {
|
|
323
311
|
add_help: true,
|
|
324
312
|
help: 'Batch install or uninstall Appium drivers and plugins',
|
|
@@ -368,21 +356,10 @@ class ArgParser {
|
|
|
368
356
|
}
|
|
369
357
|
|
|
370
358
|
/**
|
|
371
|
-
* Creates
|
|
372
|
-
*
|
|
373
|
-
* @constructs ArgParser
|
|
374
|
-
* @param {boolean} [debug] - If `true`, throw instead of exit upon parsing error
|
|
375
|
-
* @returns {ArgParser}
|
|
359
|
+
* Creates and returns an `ArgParser` after finalizing schema state.
|
|
376
360
|
*/
|
|
377
|
-
function getParser(debug) {
|
|
361
|
+
export function getParser(debug = false) {
|
|
378
362
|
finalizeSchema();
|
|
379
363
|
|
|
380
364
|
return new ArgParser(debug);
|
|
381
365
|
}
|
|
382
|
-
|
|
383
|
-
export {getParser, ArgParser};
|
|
384
|
-
|
|
385
|
-
/**
|
|
386
|
-
* @typedef {import('@appium/types').DriverType} DriverType
|
|
387
|
-
* @typedef {import('@appium/types').PluginType} PluginType
|
|
388
|
-
*/
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import _ from 'lodash';
|
|
2
|
+
import type {ExtMetadata, ExtRecord, InstallType} from 'appium/types';
|
|
3
|
+
import ExtensionCliCommand from './extension-command';
|
|
4
|
+
import type {
|
|
5
|
+
ExtensionArgs,
|
|
6
|
+
ExtensionCommandOptions,
|
|
7
|
+
ExtensionUpdateResult,
|
|
8
|
+
PostInstallText,
|
|
9
|
+
RunOutput,
|
|
10
|
+
} from './extension-command';
|
|
11
|
+
import {KNOWN_PLUGINS} from '../constants';
|
|
12
|
+
|
|
13
|
+
const REQ_PLUGIN_FIELDS = ['pluginName', 'mainClass'];
|
|
14
|
+
type PluginInstallOpts = {plugin: string; installType: InstallType; packageName?: string};
|
|
15
|
+
type PluginUninstallOpts = {plugin: string};
|
|
16
|
+
type PluginUpdateOpts = {plugin: string; unsafe: boolean};
|
|
17
|
+
type PluginRunOptions = {plugin: string; scriptName: string; extraArgs?: string[]};
|
|
18
|
+
type PluginDoctorOptions = {plugin: string};
|
|
19
|
+
|
|
20
|
+
export default class PluginCliCommand extends ExtensionCliCommand<'plugin'> {
|
|
21
|
+
constructor({config, json}: ExtensionCommandOptions<'plugin'>) {
|
|
22
|
+
super({config, json});
|
|
23
|
+
this.knownExtensions = KNOWN_PLUGINS;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Install a plugin
|
|
28
|
+
*
|
|
29
|
+
* @param opts - install options
|
|
30
|
+
*/
|
|
31
|
+
async install({plugin, installType, packageName}: PluginInstallOpts): Promise<ExtRecord<'plugin'>> {
|
|
32
|
+
return await super._install({
|
|
33
|
+
installSpec: plugin,
|
|
34
|
+
installType,
|
|
35
|
+
packageName,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Uninstall a plugin
|
|
41
|
+
*
|
|
42
|
+
* @param opts - uninstall options
|
|
43
|
+
*/
|
|
44
|
+
async uninstall({plugin}: PluginUninstallOpts): Promise<ExtRecord<'plugin'>> {
|
|
45
|
+
return await super._uninstall({installSpec: plugin});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Update a plugin
|
|
50
|
+
*
|
|
51
|
+
* @param opts - update options
|
|
52
|
+
*/
|
|
53
|
+
async update({plugin, unsafe}: PluginUpdateOpts): Promise<ExtensionUpdateResult> {
|
|
54
|
+
return await super._update({installSpec: plugin, unsafe});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Run a script from a plugin
|
|
59
|
+
*
|
|
60
|
+
* @param opts - script execution options
|
|
61
|
+
* @throws {Error} if the script fails to run
|
|
62
|
+
*/
|
|
63
|
+
async run({plugin, scriptName, extraArgs}: PluginRunOptions): Promise<RunOutput> {
|
|
64
|
+
return await super._run({
|
|
65
|
+
installSpec: plugin,
|
|
66
|
+
scriptName,
|
|
67
|
+
extraArgs,
|
|
68
|
+
bufferOutput: this.isJsonOutput,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Runs doctor checks for the given plugin
|
|
74
|
+
*
|
|
75
|
+
* @param opts - doctor command options
|
|
76
|
+
* @returns The amount of executed doctor checks.
|
|
77
|
+
* @throws {Error} If any of the mandatory Doctor checks fails.
|
|
78
|
+
*/
|
|
79
|
+
async doctor({plugin}: PluginDoctorOptions): Promise<number> {
|
|
80
|
+
return await super._doctor({
|
|
81
|
+
installSpec: plugin,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Builds the success message displayed after a plugin installation.
|
|
87
|
+
*
|
|
88
|
+
* @param args - installed extension name and metadata
|
|
89
|
+
* @returns formatted success text
|
|
90
|
+
*/
|
|
91
|
+
override getPostInstallText({extName, extData}: ExtensionArgs<'plugin'>): PostInstallText {
|
|
92
|
+
return `Plugin ${extName}@${extData.version} successfully installed`.green;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Validates fields in `appium` field of `pluginMetadata`
|
|
97
|
+
*
|
|
98
|
+
* For any `package.json` fields which a plugin requires, validate the type of
|
|
99
|
+
* those fields on the `package.json` data, throwing an error if anything is
|
|
100
|
+
* amiss.
|
|
101
|
+
* @param pluginMetadata - `appium` metadata from extension package
|
|
102
|
+
* @param installSpec - install spec from CLI
|
|
103
|
+
*/
|
|
104
|
+
override validateExtensionFields(pluginMetadata: ExtMetadata<'plugin'>, installSpec: string): void {
|
|
105
|
+
const missingFields = REQ_PLUGIN_FIELDS.reduce(
|
|
106
|
+
(acc, field) => (pluginMetadata[field] ? acc : [...acc, field]),
|
|
107
|
+
[]
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
if (!_.isEmpty(missingFields)) {
|
|
111
|
+
throw new Error(
|
|
112
|
+
`Installed plugin "${installSpec}" did not expose correct fields for compatibility ` +
|
|
113
|
+
`with Appium. Missing fields: ${JSON.stringify(missingFields)}`
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
import _ from 'lodash';
|
|
2
|
+
import type {
|
|
3
|
+
Args,
|
|
4
|
+
CliCommandSetup,
|
|
5
|
+
CliCommandSetupSubcommand,
|
|
6
|
+
CliExtensionCommand,
|
|
7
|
+
CliExtensionSubcommand,
|
|
8
|
+
} from 'appium/types';
|
|
2
9
|
import {
|
|
3
10
|
DESKTOP_BROWSERS,
|
|
4
11
|
DESKTOP_DRIVERS,
|
|
@@ -7,6 +14,7 @@ import {
|
|
|
7
14
|
import {runExtensionCommand} from './extension';
|
|
8
15
|
import { system, fs } from '@appium/support';
|
|
9
16
|
import log from '../logger';
|
|
17
|
+
import type {ExtensionConfig} from '../extension/extension-config';
|
|
10
18
|
|
|
11
19
|
/**
|
|
12
20
|
* Subcommands of preset for setup
|
|
@@ -20,13 +28,11 @@ export const SUBCOMMAND_RESET = 'reset';
|
|
|
20
28
|
* Pairs of preset subcommand and driver candidates.
|
|
21
29
|
* Driver names listed in KNOWN_DRIVERS to install by default
|
|
22
30
|
*/
|
|
23
|
-
const PRESET_PAIRS = Object.freeze(
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}),
|
|
29
|
-
);
|
|
31
|
+
const PRESET_PAIRS = Object.freeze({
|
|
32
|
+
mobile: _.keys(MOBILE_DRIVERS),
|
|
33
|
+
desktop: _.keys(DESKTOP_DRIVERS),
|
|
34
|
+
browser: _.keys(DESKTOP_BROWSERS),
|
|
35
|
+
} as const);
|
|
30
36
|
const DRIVERS_ONLY_MACOS = ['xcuitest', 'safari', 'mac2'];
|
|
31
37
|
|
|
32
38
|
const DRIVERS_ONLY_WINDOWS = ['windows'];
|
|
@@ -36,12 +42,14 @@ const DRIVERS_ONLY_WINDOWS = ['windows'];
|
|
|
36
42
|
*/
|
|
37
43
|
export const DEFAULT_PLUGINS = ['images', 'inspector'];
|
|
38
44
|
|
|
45
|
+
type DriverConfig = ExtensionConfig<'driver'>;
|
|
46
|
+
type PluginConfig = ExtensionConfig<'plugin'>;
|
|
47
|
+
type CliExtArgs = Args<CliExtensionCommand, CliExtensionSubcommand>;
|
|
48
|
+
|
|
39
49
|
/**
|
|
40
50
|
* Return a list of drivers available for current host platform.
|
|
41
|
-
* @param {import('appium/types').CliCommandSetupSubcommand} presetName
|
|
42
|
-
* @returns {Array<string>}
|
|
43
51
|
*/
|
|
44
|
-
export function getPresetDrivers(presetName) {
|
|
52
|
+
export function getPresetDrivers(presetName: CliCommandSetupSubcommand): string[] {
|
|
45
53
|
return _.filter(PRESET_PAIRS[presetName], (driver) => {
|
|
46
54
|
if (_.includes(DRIVERS_ONLY_MACOS, driver)) {
|
|
47
55
|
return system.isMac();
|
|
@@ -58,9 +66,8 @@ export function getPresetDrivers(presetName) {
|
|
|
58
66
|
|
|
59
67
|
/**
|
|
60
68
|
* Return desktop platform name for setup command description.
|
|
61
|
-
* @returns {string}
|
|
62
69
|
*/
|
|
63
|
-
export function determinePlatformName() {
|
|
70
|
+
export function determinePlatformName(): string {
|
|
64
71
|
if (system.isMac()) {
|
|
65
72
|
return 'macOS';
|
|
66
73
|
} else if (system.isWindows()) {
|
|
@@ -70,14 +77,16 @@ export function determinePlatformName() {
|
|
|
70
77
|
}
|
|
71
78
|
|
|
72
79
|
/**
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
80
|
+
* Runs the `setup` command and applies the selected preset.
|
|
81
|
+
*
|
|
82
|
+
* Depending on the subcommand, this installs mobile/desktop/browser presets or
|
|
83
|
+
* removes all installed extensions and manifests via `reset`.
|
|
84
|
+
*/
|
|
85
|
+
export async function runSetupCommand(
|
|
86
|
+
preConfigArgs: Args<CliCommandSetup>,
|
|
87
|
+
driverConfig: DriverConfig,
|
|
88
|
+
pluginConfig: PluginConfig
|
|
89
|
+
): Promise<void> {
|
|
81
90
|
switch (preConfigArgs.setupCommand) {
|
|
82
91
|
case SUBCOMMAND_DESKTOP:
|
|
83
92
|
await setupDesktopAppDrivers(driverConfig);
|
|
@@ -99,22 +108,19 @@ export async function runSetupCommand(preConfigArgs, driverConfig, pluginConfig)
|
|
|
99
108
|
|
|
100
109
|
/**
|
|
101
110
|
* Resets all installed drivers and extensions
|
|
102
|
-
*
|
|
103
|
-
* @param {DriverConfig} driverConfig
|
|
104
|
-
* @param {PluginConfig} pluginConfig
|
|
105
|
-
* @returns {Promise<void>}
|
|
106
111
|
*/
|
|
107
|
-
async function resetAllExtensions(driverConfig, pluginConfig) {
|
|
108
|
-
|
|
112
|
+
async function resetAllExtensions(driverConfig: DriverConfig, pluginConfig: PluginConfig): Promise<void> {
|
|
113
|
+
const commandConfigs: [CliExtensionCommand, DriverConfig | PluginConfig][] = [
|
|
109
114
|
['driver', driverConfig],
|
|
110
115
|
['plugin', pluginConfig],
|
|
111
|
-
]
|
|
112
|
-
|
|
116
|
+
];
|
|
117
|
+
for (const [command, config] of commandConfigs) {
|
|
118
|
+
for (const extensionName of _.keys(config.installedExtensions)) {
|
|
113
119
|
try {
|
|
114
120
|
await uninstallExtension(
|
|
115
121
|
extensionName,
|
|
116
|
-
extensionCommandArgs(
|
|
117
|
-
|
|
122
|
+
extensionCommandArgs(command, extensionName, 'uninstall'),
|
|
123
|
+
config
|
|
118
124
|
);
|
|
119
125
|
} catch (e) {
|
|
120
126
|
log.warn(
|
|
@@ -124,7 +130,7 @@ async function resetAllExtensions(driverConfig, pluginConfig) {
|
|
|
124
130
|
}
|
|
125
131
|
}
|
|
126
132
|
|
|
127
|
-
const manifestPath =
|
|
133
|
+
const manifestPath = config.manifestPath;
|
|
128
134
|
if (!await fs.exists(manifestPath)) {
|
|
129
135
|
continue;
|
|
130
136
|
}
|
|
@@ -140,38 +146,29 @@ async function resetAllExtensions(driverConfig, pluginConfig) {
|
|
|
140
146
|
|
|
141
147
|
/**
|
|
142
148
|
* Install drivers listed in DEFAULT_DRIVERS.
|
|
143
|
-
* @param {DriverConfig} driverConfig
|
|
144
|
-
* @returns {Promise<void>}
|
|
145
149
|
*/
|
|
146
|
-
async function setupMobileDrivers(driverConfig) {
|
|
150
|
+
async function setupMobileDrivers(driverConfig: DriverConfig): Promise<void> {
|
|
147
151
|
await installDrivers(SUBCOMMAND_MOBILE, driverConfig);
|
|
148
152
|
}
|
|
149
153
|
|
|
150
154
|
/**
|
|
151
155
|
* Install all of known drivers listed in BROWSER_DRIVERS.
|
|
152
|
-
* @param {DriverConfig} driverConfig
|
|
153
|
-
* @returns {Promise<void>}
|
|
154
156
|
*/
|
|
155
|
-
async function setupBrowserDrivers(driverConfig) {
|
|
157
|
+
async function setupBrowserDrivers(driverConfig: DriverConfig): Promise<void> {
|
|
156
158
|
await installDrivers(SUBCOMMAND_BROWSER, driverConfig);
|
|
157
159
|
}
|
|
158
160
|
|
|
159
161
|
/**
|
|
160
162
|
* Install all of known drivers listed in DESKTOP_APP_DRIVERS.
|
|
161
|
-
* @param {DriverConfig} driverConfig
|
|
162
|
-
* @returns {Promise<void>}
|
|
163
163
|
*/
|
|
164
|
-
async function setupDesktopAppDrivers(driverConfig) {
|
|
164
|
+
async function setupDesktopAppDrivers(driverConfig: DriverConfig): Promise<void> {
|
|
165
165
|
await installDrivers(SUBCOMMAND_DESKTOP, driverConfig);
|
|
166
166
|
}
|
|
167
167
|
|
|
168
168
|
/**
|
|
169
169
|
* Install the given driver name. It skips the installation if the given driver name was already installed.
|
|
170
|
-
* @param {import('appium/types').CliCommandSetupSubcommand} subcommand
|
|
171
|
-
* @param {DriverConfig} driverConfig
|
|
172
|
-
* @returns {Promise<void>}
|
|
173
170
|
*/
|
|
174
|
-
async function installDrivers(subcommand, driverConfig) {
|
|
171
|
+
async function installDrivers(subcommand: CliCommandSetupSubcommand, driverConfig: DriverConfig): Promise<void> {
|
|
175
172
|
for (const driverName of getPresetDrivers(subcommand)) {
|
|
176
173
|
await installExtension(driverName, extensionCommandArgs('driver', driverName, 'install'), driverConfig);
|
|
177
174
|
}
|
|
@@ -179,10 +176,8 @@ async function installDrivers(subcommand, driverConfig) {
|
|
|
179
176
|
|
|
180
177
|
/**
|
|
181
178
|
* Install plugins listed in DEFAULT_PLUGINS.
|
|
182
|
-
* @param {PluginConfig} pluginConfig
|
|
183
|
-
* @returns {Promise<void>}
|
|
184
179
|
*/
|
|
185
|
-
async function setupDefaultPlugins(pluginConfig) {
|
|
180
|
+
async function setupDefaultPlugins(pluginConfig: PluginConfig): Promise<void> {
|
|
186
181
|
for (const pluginName of DEFAULT_PLUGINS) {
|
|
187
182
|
await installExtension(pluginName, extensionCommandArgs('plugin', pluginName, 'install'), pluginConfig);
|
|
188
183
|
}
|
|
@@ -190,12 +185,12 @@ async function setupDefaultPlugins(pluginConfig) {
|
|
|
190
185
|
|
|
191
186
|
/**
|
|
192
187
|
* Run the given extensionConfigArgs command after checking if the given extensionName was already installed.
|
|
193
|
-
* @param {string} extensionName
|
|
194
|
-
* @param {Args} extensionConfigArgs
|
|
195
|
-
* @param {DriverConfig|PluginConfig} extensionConfig
|
|
196
|
-
* @returns {Promise<void>}
|
|
197
188
|
*/
|
|
198
|
-
async function installExtension(
|
|
189
|
+
async function installExtension(
|
|
190
|
+
extensionName: string,
|
|
191
|
+
extensionConfigArgs: CliExtArgs,
|
|
192
|
+
extensionConfig: DriverConfig | PluginConfig
|
|
193
|
+
): Promise<void> {
|
|
199
194
|
if (_.keys(extensionConfig.installedExtensions).includes(extensionName)) {
|
|
200
195
|
log.info(`${extensionName} (${extensionConfig.installedExtensions[extensionName].version}) is already installed. ` +
|
|
201
196
|
`Skipping the installation.`);
|
|
@@ -206,12 +201,12 @@ async function installExtension(extensionName, extensionConfigArgs, extensionCon
|
|
|
206
201
|
|
|
207
202
|
/**
|
|
208
203
|
* Run the given extensionConfigArgs command after checking if the given extensionName was already installed.
|
|
209
|
-
* @param {string} extensionName
|
|
210
|
-
* @param {Args} extensionConfigArgs
|
|
211
|
-
* @param {DriverConfig|PluginConfig} extensionConfig
|
|
212
|
-
* @returns {Promise<void>}
|
|
213
204
|
*/
|
|
214
|
-
async function uninstallExtension(
|
|
205
|
+
async function uninstallExtension(
|
|
206
|
+
extensionName: string,
|
|
207
|
+
extensionConfigArgs: CliExtArgs,
|
|
208
|
+
extensionConfig: DriverConfig | PluginConfig
|
|
209
|
+
): Promise<void> {
|
|
215
210
|
if (!_.keys(extensionConfig.installedExtensions).includes(extensionName)) {
|
|
216
211
|
log.info(`${extensionName} (${extensionConfig.installedExtensions[extensionName].version}) is not installed. ` +
|
|
217
212
|
`Skipping its uninstall.`);
|
|
@@ -222,24 +217,14 @@ async function uninstallExtension(extensionName, extensionConfigArgs, extensionC
|
|
|
222
217
|
|
|
223
218
|
/**
|
|
224
219
|
* Return the command config for driver or plugin.
|
|
225
|
-
* @param {CliExtensionCommand} extensionCommand
|
|
226
|
-
* @param {string} extensionName
|
|
227
|
-
* @param {CliExtensionSubcommand} command
|
|
228
|
-
* @returns {Args}
|
|
229
220
|
*/
|
|
230
|
-
function extensionCommandArgs(
|
|
221
|
+
function extensionCommandArgs(
|
|
222
|
+
extensionCommand: CliExtensionCommand,
|
|
223
|
+
extensionName: string,
|
|
224
|
+
command: CliExtensionSubcommand
|
|
225
|
+
): CliExtArgs {
|
|
231
226
|
return (extensionCommand === 'plugin')
|
|
232
227
|
? {'subcommand': 'plugin', 'pluginCommand': command, 'plugin': extensionName}
|
|
233
228
|
: {'subcommand': 'driver', 'driverCommand': command, 'driver': extensionName};
|
|
234
229
|
}
|
|
235
230
|
|
|
236
|
-
/**
|
|
237
|
-
* @typedef {import('appium/types').CliExtensionCommand} CliExtensionCommand
|
|
238
|
-
* @typedef {import('appium/types').CliExtensionSubcommand} CliExtensionSubcommand
|
|
239
|
-
* @typedef {import('../extension/extension-config').ExtensionConfig<CliExtensionCommand>} PluginConfig
|
|
240
|
-
* @typedef {import('../extension/extension-config').ExtensionConfig<CliExtensionCommand>} DriverConfig
|
|
241
|
-
*/
|
|
242
|
-
|
|
243
|
-
/**
|
|
244
|
-
* @typedef {import('appium/types').Args<CliExtensionCommand, CliExtensionSubcommand>} Args
|
|
245
|
-
*/
|