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.
Files changed (120) hide show
  1. package/build/lib/cli/args.d.ts +16 -12
  2. package/build/lib/cli/args.d.ts.map +1 -1
  3. package/build/lib/cli/args.js +15 -35
  4. package/build/lib/cli/args.js.map +1 -1
  5. package/build/lib/cli/driver-command.d.ts +51 -93
  6. package/build/lib/cli/driver-command.d.ts.map +1 -1
  7. package/build/lib/cli/driver-command.js +11 -66
  8. package/build/lib/cli/driver-command.js.map +1 -1
  9. package/build/lib/cli/extension-command.d.ts +211 -415
  10. package/build/lib/cli/extension-command.d.ts.map +1 -1
  11. package/build/lib/cli/extension-command.js +384 -653
  12. package/build/lib/cli/extension-command.js.map +1 -1
  13. package/build/lib/cli/extension.d.ts +11 -16
  14. package/build/lib/cli/extension.d.ts.map +1 -1
  15. package/build/lib/cli/extension.js +10 -28
  16. package/build/lib/cli/extension.js.map +1 -1
  17. package/build/lib/cli/parser.d.ts +40 -69
  18. package/build/lib/cli/parser.d.ts.map +1 -1
  19. package/build/lib/cli/parser.js +24 -59
  20. package/build/lib/cli/parser.js.map +1 -1
  21. package/build/lib/cli/plugin-command.d.ts +50 -90
  22. package/build/lib/cli/plugin-command.d.ts.map +1 -1
  23. package/build/lib/cli/plugin-command.js +11 -63
  24. package/build/lib/cli/plugin-command.js.map +1 -1
  25. package/build/lib/cli/setup-command.d.ts +21 -26
  26. package/build/lib/cli/setup-command.d.ts.map +1 -1
  27. package/build/lib/cli/setup-command.js +13 -55
  28. package/build/lib/cli/setup-command.js.map +1 -1
  29. package/build/lib/cli/utils.d.ts +27 -29
  30. package/build/lib/cli/utils.d.ts.map +1 -1
  31. package/build/lib/cli/utils.js +29 -31
  32. package/build/lib/cli/utils.js.map +1 -1
  33. package/build/lib/config-file.d.ts +24 -67
  34. package/build/lib/config-file.d.ts.map +1 -1
  35. package/build/lib/config-file.js +56 -115
  36. package/build/lib/config-file.js.map +1 -1
  37. package/build/lib/config.d.ts +42 -44
  38. package/build/lib/config.d.ts.map +1 -1
  39. package/build/lib/config.js +75 -107
  40. package/build/lib/config.js.map +1 -1
  41. package/build/lib/constants.d.ts +23 -23
  42. package/build/lib/constants.d.ts.map +1 -1
  43. package/build/lib/constants.js +10 -15
  44. package/build/lib/constants.js.map +1 -1
  45. package/build/lib/doctor/doctor.d.ts +40 -57
  46. package/build/lib/doctor/doctor.d.ts.map +1 -1
  47. package/build/lib/doctor/doctor.js +29 -60
  48. package/build/lib/doctor/doctor.js.map +1 -1
  49. package/build/lib/grid-register.d.ts +32 -7
  50. package/build/lib/grid-register.d.ts.map +1 -1
  51. package/build/lib/grid-register.js +84 -48
  52. package/build/lib/grid-register.js.map +1 -1
  53. package/build/lib/logsink.d.ts +13 -22
  54. package/build/lib/logsink.d.ts.map +1 -1
  55. package/build/lib/logsink.js +48 -103
  56. package/build/lib/logsink.js.map +1 -1
  57. package/build/lib/main.js +1 -1
  58. package/build/lib/main.js.map +1 -1
  59. package/build/lib/schema/arg-spec.d.ts +32 -107
  60. package/build/lib/schema/arg-spec.d.ts.map +1 -1
  61. package/build/lib/schema/arg-spec.js +11 -107
  62. package/build/lib/schema/arg-spec.js.map +1 -1
  63. package/build/lib/schema/cli-args.d.ts +3 -15
  64. package/build/lib/schema/cli-args.d.ts.map +1 -1
  65. package/build/lib/schema/cli-args.js +15 -105
  66. package/build/lib/schema/cli-args.js.map +1 -1
  67. package/build/lib/schema/cli-transformers.d.ts +15 -12
  68. package/build/lib/schema/cli-transformers.d.ts.map +1 -1
  69. package/build/lib/schema/cli-transformers.js +15 -45
  70. package/build/lib/schema/cli-transformers.js.map +1 -1
  71. package/build/lib/schema/index.d.ts +2 -2
  72. package/build/lib/schema/index.d.ts.map +1 -1
  73. package/build/lib/schema/index.js.map +1 -1
  74. package/build/lib/schema/keywords.d.ts +12 -20
  75. package/build/lib/schema/keywords.d.ts.map +1 -1
  76. package/build/lib/schema/keywords.js +6 -51
  77. package/build/lib/schema/keywords.js.map +1 -1
  78. package/build/lib/schema/schema.d.ts +106 -231
  79. package/build/lib/schema/schema.d.ts.map +1 -1
  80. package/build/lib/schema/schema.js +75 -345
  81. package/build/lib/schema/schema.js.map +1 -1
  82. package/build/lib/utils.d.ts +59 -238
  83. package/build/lib/utils.d.ts.map +1 -1
  84. package/build/lib/utils.js +55 -207
  85. package/build/lib/utils.js.map +1 -1
  86. package/lib/cli/{args.js → args.ts} +40 -51
  87. package/lib/cli/driver-command.ts +122 -0
  88. package/lib/cli/{extension-command.js → extension-command.ts} +610 -689
  89. package/lib/cli/extension.ts +65 -0
  90. package/lib/cli/{parser.js → parser.ts} +48 -71
  91. package/lib/cli/plugin-command.ts +117 -0
  92. package/lib/cli/{setup-command.js → setup-command.ts} +57 -72
  93. package/lib/cli/utils.ts +97 -0
  94. package/lib/config-file.ts +212 -0
  95. package/lib/{config.js → config.ts} +129 -141
  96. package/lib/{constants.js → constants.ts} +30 -41
  97. package/lib/doctor/{doctor.js → doctor.ts} +81 -91
  98. package/lib/grid-register.ts +250 -0
  99. package/lib/{logsink.js → logsink.ts} +91 -137
  100. package/lib/main.js +1 -1
  101. package/lib/schema/arg-spec.ts +131 -0
  102. package/lib/schema/cli-args.ts +171 -0
  103. package/lib/schema/cli-transformers.ts +83 -0
  104. package/lib/schema/keywords.ts +96 -0
  105. package/lib/schema/schema.ts +449 -0
  106. package/lib/utils.ts +404 -0
  107. package/package.json +16 -16
  108. package/lib/cli/driver-command.js +0 -174
  109. package/lib/cli/extension.js +0 -74
  110. package/lib/cli/plugin-command.js +0 -164
  111. package/lib/cli/utils.js +0 -91
  112. package/lib/config-file.js +0 -228
  113. package/lib/grid-register.js +0 -146
  114. package/lib/schema/arg-spec.js +0 -229
  115. package/lib/schema/cli-args.js +0 -254
  116. package/lib/schema/cli-transformers.js +0 -113
  117. package/lib/schema/keywords.js +0 -136
  118. package/lib/schema/schema.js +0 -725
  119. package/lib/utils.js +0 -512
  120. /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 {boolean} [debug] - If true, throw instead of exit on error.
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
- * Parse arguments from the command line.
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
- * `ArgParser.prototype.parse_args` is an alias of this method.
116
- * @template {import('appium/types').CliCommand} [Cmd=import('appium/types').CliCommandServer]
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
- * Normalize hyphenated server arg keys (e.g. "log-level") to dest form (e.g. "loglevel").
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
- * @param {object} obj - Object that may contain server args with schema property names
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 && obj[spec.name] !== undefined && spec.rawDest !== spec.name) {
173
- obj[spec.rawDest] = obj[spec.name] ?? obj[spec.rawDest];
174
- delete obj[spec.name];
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(args, unknownArgs = []) {
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
- if (!_.isUndefined(value) && hasArgSpec(key)) {
195
- const {dest} = /** @type {import('../schema/arg-spec').ArgSpec} */ (getArgSpec(key));
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(subParsers) {
250
- for (const type of /** @type {[DriverType, PluginType]} */ ([DRIVER_TYPE, PLUGIN_TYPE])) {
251
- const extParser = subParsers.add_parser(type, {
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
- * @type { {command: import('appium/types').CliExtensionSubcommand, args: import('./args').ArgumentDefinitions, help: string, aliases?: import('argparse').SubArgumentParserOptions['aliases']}[] }
265
- */
266
- const parserSpecs = [
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 a {@link ArgParser} instance; finalizes the config schema.
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
- /** @type {const} */ ({
25
- mobile: _.keys(MOBILE_DRIVERS),
26
- desktop: _.keys(DESKTOP_DRIVERS),
27
- browser: _.keys(DESKTOP_BROWSERS)
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
- * Run 'setup' command to install drivers/plugins into the given appium home.
74
- * @template {import('appium/types').CliCommandSetup} SetupCmd
75
- * @param {import('appium/types').Args<SetupCmd>} preConfigArgs
76
- * @param {DriverConfig} driverConfig
77
- * @param {PluginConfig} pluginConfig
78
- * @returns {Promise<void>}
79
- */
80
- export async function runSetupCommand(preConfigArgs, driverConfig, pluginConfig) {
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
- for (const [command, config] of [
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
- for (const extensionName of _.keys(/** @type {DriverConfig|PluginConfig} */ (config).installedExtensions)) {
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(/** @type {CliExtensionCommand} */ (command), extensionName, 'uninstall'),
117
- /** @type {DriverConfig|PluginConfig} */ (config)
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 = /** @type {DriverConfig|PluginConfig} */ (config).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(extensionName, extensionConfigArgs, extensionConfig) {
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(extensionName, extensionConfigArgs, extensionConfig) {
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(extensionCommand, extensionName, command) {
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
- */