appium 3.2.2 → 3.3.1

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 (250) hide show
  1. package/build/lib/appium.d.ts +147 -205
  2. package/build/lib/appium.d.ts.map +1 -1
  3. package/build/lib/appium.js +169 -282
  4. package/build/lib/appium.js.map +1 -1
  5. package/build/lib/bidi-commands.d.ts.map +1 -1
  6. package/build/lib/bidi-commands.js +11 -11
  7. package/build/lib/bidi-commands.js.map +1 -1
  8. package/build/lib/bootstrap/appium-initializer.d.ts +21 -0
  9. package/build/lib/bootstrap/appium-initializer.d.ts.map +1 -0
  10. package/build/lib/bootstrap/appium-initializer.js +146 -0
  11. package/build/lib/bootstrap/appium-initializer.js.map +1 -0
  12. package/build/lib/bootstrap/appium-main-runner.d.ts +22 -0
  13. package/build/lib/bootstrap/appium-main-runner.d.ts.map +1 -0
  14. package/build/lib/bootstrap/appium-main-runner.js +109 -0
  15. package/build/lib/bootstrap/appium-main-runner.js.map +1 -0
  16. package/build/lib/bootstrap/config-file.d.ts +37 -0
  17. package/build/lib/bootstrap/config-file.d.ts.map +1 -0
  18. package/build/lib/{config-file.js → bootstrap/config-file.js} +62 -138
  19. package/build/lib/bootstrap/config-file.js.map +1 -0
  20. package/build/lib/bootstrap/grid-v3-register.d.ts +20 -0
  21. package/build/lib/bootstrap/grid-v3-register.d.ts.map +1 -0
  22. package/build/lib/bootstrap/grid-v3-register.js +185 -0
  23. package/build/lib/bootstrap/grid-v3-register.js.map +1 -0
  24. package/build/lib/bootstrap/init-types.d.ts +16 -0
  25. package/build/lib/bootstrap/init-types.d.ts.map +1 -0
  26. package/build/lib/bootstrap/init-types.js +3 -0
  27. package/build/lib/bootstrap/init-types.js.map +1 -0
  28. package/build/lib/bootstrap/main-helpers.d.ts +55 -0
  29. package/build/lib/bootstrap/main-helpers.d.ts.map +1 -0
  30. package/build/lib/bootstrap/main-helpers.js +187 -0
  31. package/build/lib/bootstrap/main-helpers.js.map +1 -0
  32. package/build/lib/bootstrap/node-helpers.d.ts +32 -0
  33. package/build/lib/bootstrap/node-helpers.d.ts.map +1 -0
  34. package/build/lib/bootstrap/node-helpers.js +201 -0
  35. package/build/lib/bootstrap/node-helpers.js.map +1 -0
  36. package/build/lib/bootstrap/startup-config.d.ts +22 -0
  37. package/build/lib/bootstrap/startup-config.d.ts.map +1 -0
  38. package/build/lib/bootstrap/startup-config.js +111 -0
  39. package/build/lib/bootstrap/startup-config.js.map +1 -0
  40. package/build/lib/cli/args.d.ts +16 -12
  41. package/build/lib/cli/args.d.ts.map +1 -1
  42. package/build/lib/cli/args.js +20 -40
  43. package/build/lib/cli/args.js.map +1 -1
  44. package/build/lib/cli/driver-command.d.ts +51 -93
  45. package/build/lib/cli/driver-command.d.ts.map +1 -1
  46. package/build/lib/cli/driver-command.js +11 -66
  47. package/build/lib/cli/driver-command.js.map +1 -1
  48. package/build/lib/cli/extension-command.d.ts +173 -377
  49. package/build/lib/cli/extension-command.d.ts.map +1 -1
  50. package/build/lib/cli/extension-command.js +387 -656
  51. package/build/lib/cli/extension-command.js.map +1 -1
  52. package/build/lib/cli/extension.d.ts +10 -15
  53. package/build/lib/cli/extension.d.ts.map +1 -1
  54. package/build/lib/cli/extension.js +15 -33
  55. package/build/lib/cli/extension.js.map +1 -1
  56. package/build/lib/cli/parser.d.ts +37 -66
  57. package/build/lib/cli/parser.d.ts.map +1 -1
  58. package/build/lib/cli/parser.js +69 -104
  59. package/build/lib/cli/parser.js.map +1 -1
  60. package/build/lib/cli/plugin-command.d.ts +50 -90
  61. package/build/lib/cli/plugin-command.d.ts.map +1 -1
  62. package/build/lib/cli/plugin-command.js +11 -63
  63. package/build/lib/cli/plugin-command.js.map +1 -1
  64. package/build/lib/cli/setup-command.d.ts +21 -26
  65. package/build/lib/cli/setup-command.d.ts.map +1 -1
  66. package/build/lib/cli/setup-command.js +19 -61
  67. package/build/lib/cli/setup-command.js.map +1 -1
  68. package/build/lib/cli/utils.d.ts +33 -35
  69. package/build/lib/cli/utils.d.ts.map +1 -1
  70. package/build/lib/cli/utils.js +48 -50
  71. package/build/lib/cli/utils.js.map +1 -1
  72. package/build/lib/constants.d.ts +23 -23
  73. package/build/lib/constants.d.ts.map +1 -1
  74. package/build/lib/constants.js +10 -15
  75. package/build/lib/constants.js.map +1 -1
  76. package/build/lib/doctor/doctor.d.ts +40 -57
  77. package/build/lib/doctor/doctor.d.ts.map +1 -1
  78. package/build/lib/doctor/doctor.js +31 -62
  79. package/build/lib/doctor/doctor.js.map +1 -1
  80. package/build/lib/extension/driver-config.d.ts +18 -77
  81. package/build/lib/extension/driver-config.d.ts.map +1 -1
  82. package/build/lib/extension/driver-config.js +37 -125
  83. package/build/lib/extension/driver-config.js.map +1 -1
  84. package/build/lib/extension/extension-config.d.ts +103 -210
  85. package/build/lib/extension/extension-config.d.ts.map +1 -1
  86. package/build/lib/extension/extension-config.js +180 -342
  87. package/build/lib/extension/extension-config.js.map +1 -1
  88. package/build/lib/extension/index.d.ts +12 -29
  89. package/build/lib/extension/index.d.ts.map +1 -1
  90. package/build/lib/extension/index.js +33 -75
  91. package/build/lib/extension/index.js.map +1 -1
  92. package/build/lib/extension/manifest-migrations.d.ts +3 -20
  93. package/build/lib/extension/manifest-migrations.d.ts.map +1 -1
  94. package/build/lib/extension/manifest-migrations.js +20 -101
  95. package/build/lib/extension/manifest-migrations.js.map +1 -1
  96. package/build/lib/extension/manifest.d.ts +61 -107
  97. package/build/lib/extension/manifest.d.ts.map +1 -1
  98. package/build/lib/extension/manifest.js +181 -356
  99. package/build/lib/extension/manifest.js.map +1 -1
  100. package/build/lib/extension/package-changed.d.ts +1 -3
  101. package/build/lib/extension/package-changed.d.ts.map +1 -1
  102. package/build/lib/extension/package-changed.js +8 -15
  103. package/build/lib/extension/package-changed.js.map +1 -1
  104. package/build/lib/extension/plugin-config.d.ts +10 -52
  105. package/build/lib/extension/plugin-config.d.ts.map +1 -1
  106. package/build/lib/extension/plugin-config.js +11 -63
  107. package/build/lib/extension/plugin-config.js.map +1 -1
  108. package/build/lib/helpers/build.d.ts +22 -0
  109. package/build/lib/helpers/build.d.ts.map +1 -0
  110. package/build/lib/helpers/build.js +109 -0
  111. package/build/lib/helpers/build.js.map +1 -0
  112. package/build/lib/helpers/capability.d.ts +38 -0
  113. package/build/lib/helpers/capability.d.ts.map +1 -0
  114. package/build/lib/helpers/capability.js +128 -0
  115. package/build/lib/helpers/capability.js.map +1 -0
  116. package/build/lib/helpers/network.d.ts +14 -0
  117. package/build/lib/helpers/network.d.ts.map +1 -0
  118. package/build/lib/helpers/network.js +35 -0
  119. package/build/lib/helpers/network.js.map +1 -0
  120. package/build/lib/insecure-features.js +6 -6
  121. package/build/lib/insecure-features.js.map +1 -1
  122. package/build/lib/inspector-commands.d.ts +6 -0
  123. package/build/lib/inspector-commands.d.ts.map +1 -1
  124. package/build/lib/inspector-commands.js +6 -0
  125. package/build/lib/inspector-commands.js.map +1 -1
  126. package/build/lib/logger.d.ts +2 -3
  127. package/build/lib/logger.d.ts.map +1 -1
  128. package/build/lib/logger.js +2 -3
  129. package/build/lib/logger.js.map +1 -1
  130. package/build/lib/logsink.d.ts +13 -22
  131. package/build/lib/logsink.d.ts.map +1 -1
  132. package/build/lib/logsink.js +48 -103
  133. package/build/lib/logsink.js.map +1 -1
  134. package/build/lib/main.d.ts +15 -58
  135. package/build/lib/main.d.ts.map +1 -1
  136. package/build/lib/main.js +25 -425
  137. package/build/lib/main.js.map +1 -1
  138. package/build/lib/schema/arg-spec.d.ts +32 -107
  139. package/build/lib/schema/arg-spec.d.ts.map +1 -1
  140. package/build/lib/schema/arg-spec.js +11 -107
  141. package/build/lib/schema/arg-spec.js.map +1 -1
  142. package/build/lib/schema/cli-args-guards.d.ts +34 -0
  143. package/build/lib/schema/cli-args-guards.d.ts.map +1 -0
  144. package/build/lib/schema/cli-args-guards.js +49 -0
  145. package/build/lib/schema/cli-args-guards.js.map +1 -0
  146. package/build/lib/schema/cli-args.d.ts +3 -15
  147. package/build/lib/schema/cli-args.d.ts.map +1 -1
  148. package/build/lib/schema/cli-args.js +17 -107
  149. package/build/lib/schema/cli-args.js.map +1 -1
  150. package/build/lib/schema/cli-transformers.d.ts +15 -12
  151. package/build/lib/schema/cli-transformers.d.ts.map +1 -1
  152. package/build/lib/schema/cli-transformers.js +15 -45
  153. package/build/lib/schema/cli-transformers.js.map +1 -1
  154. package/build/lib/schema/format-errors.d.ts +28 -0
  155. package/build/lib/schema/format-errors.d.ts.map +1 -0
  156. package/build/lib/schema/format-errors.js +29 -0
  157. package/build/lib/schema/format-errors.js.map +1 -0
  158. package/build/lib/schema/index.d.ts +4 -2
  159. package/build/lib/schema/index.d.ts.map +1 -1
  160. package/build/lib/schema/index.js +2 -0
  161. package/build/lib/schema/index.js.map +1 -1
  162. package/build/lib/schema/keywords.d.ts +12 -20
  163. package/build/lib/schema/keywords.d.ts.map +1 -1
  164. package/build/lib/schema/keywords.js +6 -51
  165. package/build/lib/schema/keywords.js.map +1 -1
  166. package/build/lib/schema/schema.d.ts +106 -231
  167. package/build/lib/schema/schema.d.ts.map +1 -1
  168. package/build/lib/schema/schema.js +88 -358
  169. package/build/lib/schema/schema.js.map +1 -1
  170. package/build/lib/utils.d.ts +7 -267
  171. package/build/lib/utils.d.ts.map +1 -1
  172. package/build/lib/utils.js +10 -409
  173. package/build/lib/utils.js.map +1 -1
  174. package/lib/{appium.js → appium.ts} +297 -341
  175. package/lib/bidi-commands.ts +10 -14
  176. package/lib/bootstrap/appium-initializer.ts +212 -0
  177. package/lib/bootstrap/appium-main-runner.ts +172 -0
  178. package/lib/bootstrap/config-file.ts +178 -0
  179. package/lib/bootstrap/grid-v3-register.ts +250 -0
  180. package/lib/bootstrap/init-types.ts +31 -0
  181. package/lib/bootstrap/main-helpers.ts +223 -0
  182. package/lib/bootstrap/node-helpers.ts +180 -0
  183. package/lib/bootstrap/startup-config.ts +143 -0
  184. package/lib/cli/{args.js → args.ts} +45 -56
  185. package/lib/cli/driver-command.ts +122 -0
  186. package/lib/cli/{extension-command.js → extension-command.ts} +827 -906
  187. package/lib/cli/extension.ts +65 -0
  188. package/lib/cli/{parser.js → parser.ts} +93 -116
  189. package/lib/cli/plugin-command.ts +117 -0
  190. package/lib/cli/{setup-command.js → setup-command.ts} +59 -74
  191. package/lib/cli/utils.ts +97 -0
  192. package/lib/{constants.js → constants.ts} +30 -41
  193. package/lib/doctor/{doctor.js → doctor.ts} +82 -92
  194. package/lib/extension/driver-config.ts +165 -0
  195. package/lib/extension/{extension-config.js → extension-config.ts} +291 -405
  196. package/lib/extension/index.ts +143 -0
  197. package/lib/extension/manifest-migrations.ts +57 -0
  198. package/lib/extension/manifest.ts +369 -0
  199. package/lib/extension/{package-changed.js → package-changed.ts} +9 -18
  200. package/lib/extension/plugin-config.ts +62 -0
  201. package/lib/helpers/build.ts +111 -0
  202. package/lib/helpers/capability.ts +171 -0
  203. package/lib/helpers/network.ts +30 -0
  204. package/lib/insecure-features.ts +1 -1
  205. package/lib/inspector-commands.ts +6 -1
  206. package/lib/{logger.js → logger.ts} +1 -2
  207. package/lib/{logsink.js → logsink.ts} +91 -137
  208. package/lib/main.ts +60 -0
  209. package/lib/schema/arg-spec.ts +131 -0
  210. package/lib/schema/cli-args-guards.ts +67 -0
  211. package/lib/schema/cli-args.ts +171 -0
  212. package/lib/schema/cli-transformers.ts +83 -0
  213. package/lib/schema/format-errors.ts +43 -0
  214. package/lib/schema/index.ts +4 -0
  215. package/lib/schema/keywords.ts +96 -0
  216. package/lib/schema/schema.ts +448 -0
  217. package/lib/utils.ts +73 -0
  218. package/package.json +17 -18
  219. package/scripts/autoinstall-extensions.js +3 -0
  220. package/build/lib/config-file.d.ts +0 -100
  221. package/build/lib/config-file.d.ts.map +0 -1
  222. package/build/lib/config-file.js.map +0 -1
  223. package/build/lib/config.d.ts +0 -70
  224. package/build/lib/config.d.ts.map +0 -1
  225. package/build/lib/config.js +0 -390
  226. package/build/lib/config.js.map +0 -1
  227. package/build/lib/grid-register.d.ts +0 -10
  228. package/build/lib/grid-register.d.ts.map +0 -1
  229. package/build/lib/grid-register.js +0 -134
  230. package/build/lib/grid-register.js.map +0 -1
  231. package/lib/cli/driver-command.js +0 -174
  232. package/lib/cli/extension.js +0 -74
  233. package/lib/cli/plugin-command.js +0 -164
  234. package/lib/cli/utils.js +0 -91
  235. package/lib/config-file.js +0 -228
  236. package/lib/config.js +0 -389
  237. package/lib/extension/driver-config.js +0 -245
  238. package/lib/extension/index.js +0 -169
  239. package/lib/extension/manifest-migrations.js +0 -136
  240. package/lib/extension/manifest.js +0 -550
  241. package/lib/extension/plugin-config.js +0 -112
  242. package/lib/grid-register.js +0 -146
  243. package/lib/main.js +0 -545
  244. package/lib/schema/arg-spec.js +0 -229
  245. package/lib/schema/cli-args.js +0 -254
  246. package/lib/schema/cli-transformers.js +0 -113
  247. package/lib/schema/index.js +0 -2
  248. package/lib/schema/keywords.js +0 -136
  249. package/lib/schema/schema.js +0 -725
  250. package/lib/utils.js +0 -512
@@ -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 '../schema/cli-args-guards';
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
+ export type ExtCommand<ExtType extends ExtensionType> = ExtType extends DriverType
19
+ ? Class<DriverCliCommand>
20
+ : ExtType extends PluginType
21
+ ? Class<PluginCliCommand>
22
+ : never;
23
+
24
+ /**
25
+ * Executes a driver/plugin extension subcommand and returns the command result.
26
+ *
27
+ * When JSON output is enabled, this also prints the serialized command result
28
+ * unless output was suppressed by the caller.
29
+ */
30
+ export async function runExtensionCommand<
31
+ Cmd extends CliExtensionCommand,
32
+ SubCmd extends CliExtensionSubcommand,
33
+ >(args: Args<Cmd, SubCmd>, config: ExtensionConfig<Cmd>) {
34
+ // TODO driver config file should be locked while any of these commands are
35
+ // running to prevent weird situations
36
+ let jsonResult: Record<string, unknown> = {};
37
+ const {extensionType: type} = config; // NOTE this is the same as `args.subcommand`
38
+ if (!isExtensionCommandArgs(args)) {
39
+ throw new TypeError(`Cannot call ${type} command without a subcommand like 'install'`);
40
+ }
41
+ let {json} = args;
42
+ const {suppressOutput} = args;
43
+ json = Boolean(json);
44
+ if (suppressOutput) {
45
+ json = true;
46
+ }
47
+ const CommandClass = commandClasses[type] as ExtCommand<Cmd>;
48
+ const cmd = new CommandClass({config, json} as any);
49
+ try {
50
+ jsonResult = (await cmd.execute(args)) as Record<string, unknown>;
51
+ } catch (err) {
52
+ // in the suppress output case, we are calling this function internally and should
53
+ // just throw instead of printing an error and ending the process
54
+ if (suppressOutput) {
55
+ throw err;
56
+ }
57
+ errAndQuit(json, err);
58
+ }
59
+
60
+ if (json && !suppressOutput) {
61
+ console.log(JSON.stringify(jsonResult, null, JSON_SPACES));
62
+ }
63
+
64
+ return jsonResult;
65
+ }
@@ -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,
@@ -15,8 +18,9 @@ import {
15
18
  SETUP_SUBCOMMAND
16
19
  } from '../constants';
17
20
  import {finalizeSchema, getAllArgSpecs, getArgSpec, hasArgSpec} from '../schema';
18
- import {rootDir} from '../config';
21
+ import {rootDir} from '../helpers/build';
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,77 +101,20 @@ 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
+ * Normalizes server arg keys from schema names to parser destination names.
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
+ * This mutates and returns the same object.
119
111
  */
120
- parseArgs(args = process.argv.slice(2)) {
121
- if (!NON_SERVER_ARGS.has(args[0])) {
122
- args.unshift(SERVER_SUBCOMMAND);
123
- }
124
-
125
- try {
126
- const parsed = this.parser.parse_known_args(args);
127
- const [knownArgs, unknownArgs] = parsed;
128
- // XXX: you'd think that argparse, when given an alias for a subcommand,
129
- // would set this value to the original subcommand name, but it doesn't.
130
- if (knownArgs?.driverCommand === 'ls') {
131
- knownArgs.driverCommand = 'list';
132
- } else if (knownArgs?.pluginCommand === 'ls') {
133
- knownArgs.pluginCommand = 'list';
134
- }
135
- if (
136
- unknownArgs?.length &&
137
- (knownArgs.driverCommand === 'run' || knownArgs.pluginCommand === 'run')
138
- ) {
139
- return ArgParser._transformParsedArgs(knownArgs, unknownArgs);
140
- } else if (unknownArgs?.length) {
141
- throw new Error(`[ERROR] Unrecognized arguments: ${unknownArgs.join(' ')}`);
142
- }
143
- return ArgParser._transformParsedArgs(knownArgs);
144
- } catch (err) {
145
- if (this.debug) {
146
- throw err;
147
- }
148
- // this isn't tested via unit tests (we use `debug: true`) so may escape coverage.
149
-
150
- /* istanbul ignore next */
151
- {
152
- // eslint-disable-next-line no-console
153
- console.error(); // need an extra space since argparse prints usage.
154
- // eslint-disable-next-line no-console
155
- console.error(err.message);
156
- process.exit(1);
157
- }
158
- }
159
- }
160
-
161
- /**
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.
166
- *
167
- * @param {object} obj - Object that may contain server args with schema property names
168
- * @returns {object} The same object with keys normalized
169
- */
170
- static normalizeServerArgs(obj) {
112
+ static normalizeServerArgs<T extends LooseArgsMap>(obj: T): T {
113
+ const mutableObj = obj as LooseArgsMap;
171
114
  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];
115
+ if (!spec.extType && mutableObj[spec.name] !== undefined && spec.rawDest !== spec.name) {
116
+ mutableObj[spec.rawDest] = mutableObj[spec.name] ?? mutableObj[spec.rawDest];
117
+ delete mutableObj[spec.name];
175
118
  }
176
119
  }
177
120
  return obj;
@@ -183,16 +126,17 @@ class ArgParser {
183
126
  * keys to match the intended destination.
184
127
  *
185
128
  * E.g., `{'driver-foo-bar': baz}` becomes `{driver: {foo: {bar: 'baz'}}}`
186
- * @param {object} args
187
- * @param {string[]} [unknownArgs]
188
- * @returns {object}
189
129
  */
190
- static _transformParsedArgs(args, unknownArgs = []) {
130
+ private static _transformParsedArgs(
131
+ args: LooseArgsMap,
132
+ unknownArgs: string[] = []
133
+ ): TransformedArgsMap {
191
134
  const result = _.reduce(
192
135
  args,
193
136
  (unpacked, value, key) => {
194
- if (!_.isUndefined(value) && hasArgSpec(key)) {
195
- const {dest} = /** @type {import('../schema/arg-spec').ArgSpec} */ (getArgSpec(key));
137
+ const spec = hasArgSpec(key) ? getArgSpec(key) : undefined;
138
+ if (!_.isUndefined(value) && spec) {
139
+ const {dest} = spec;
196
140
  _.set(unpacked, dest, value);
197
141
  } else {
198
142
  // this could be anything that _isn't_ a server arg
@@ -203,14 +147,13 @@ class ArgParser {
203
147
  {}
204
148
  );
205
149
  result[EXTRA_ARGS] = unknownArgs;
206
- return result;
150
+ return result as TransformedArgsMap;
207
151
  }
208
152
 
209
153
  /**
210
154
  * Patches the `exit()` method of the parser to throw an error, so we can handle it manually.
211
- * @param {ArgumentParser} parser
212
155
  */
213
- static _patchExit(parser) {
156
+ private static _patchExit(parser: ArgumentParser): void {
214
157
  parser.exit = (code, msg) => {
215
158
  if (code) {
216
159
  throw new Error(msg);
@@ -220,11 +163,9 @@ class ArgParser {
220
163
  }
221
164
 
222
165
  /**
223
- *
224
- * @param {import('argparse').SubParser} subParser
225
- * @returns {import('./args').ArgumentDefinitions}
166
+ * Adds the `server` subcommand parser and returns its argument definitions.
226
167
  */
227
- static _addServerToParser(subParser) {
168
+ private static _addServerToParser(subParser: SubParser): ArgumentDefinitions {
228
169
  const serverParser = subParser.add_parser('server', {
229
170
  add_help: true,
230
171
  help: 'Start an Appium server',
@@ -244,11 +185,10 @@ class ArgParser {
244
185
 
245
186
  /**
246
187
  * Adds extension sub-sub-commands to `driver`/`plugin` subcommands
247
- * @param {import('argparse').SubParser} subParsers
248
188
  */
249
- static _addExtensionCommandsToParser(subParsers) {
250
- for (const type of /** @type {[DriverType, PluginType]} */ ([DRIVER_TYPE, PLUGIN_TYPE])) {
251
- const extParser = subParsers.add_parser(type, {
189
+ private static _addExtensionCommandsToParser(subParser: SubParser): void {
190
+ for (const type of [DRIVER_TYPE, PLUGIN_TYPE] as [DriverType, PluginType]) {
191
+ const extParser = subParser.add_parser(type, {
252
192
  add_help: true,
253
193
  help: `Manage Appium ${type}s`,
254
194
  description: `Manage Appium ${type}s using various subcommands`,
@@ -260,10 +200,12 @@ class ArgParser {
260
200
  dest: `${type}Command`,
261
201
  });
262
202
  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 = [
203
+ const parserSpecs: {
204
+ command: CliExtensionSubcommand;
205
+ args: ArgumentDefinitions;
206
+ help: string;
207
+ aliases?: SubArgumentParserOptions['aliases'];
208
+ }[] = [
267
209
  {
268
210
  command: EXT_SUBCOMMAND_LIST,
269
211
  args: extensionArgs[type].list,
@@ -316,9 +258,8 @@ class ArgParser {
316
258
 
317
259
  /**
318
260
  * Add subcommand and sub-sub commands for 'setup' subcommand.
319
- * @param {import('argparse').SubParser} subParser
320
261
  */
321
- static _addSetupToParser(subParser) {
262
+ private static _addSetupToParser(subParser: SubParser): void {
322
263
  const setupParser = subParser.add_parser('setup', {
323
264
  add_help: true,
324
265
  help: 'Batch install or uninstall Appium drivers and plugins',
@@ -365,24 +306,60 @@ class ArgParser {
365
306
  ArgParser._patchExit(parser);
366
307
  }
367
308
  }
309
+
310
+ /**
311
+ * Parses CLI args and returns Appium's normalized argument object.
312
+ *
313
+ * If no explicit subcommand is provided, this injects `server`.
314
+ * `parse_args` is a backwards-compatible alias of this method.
315
+ */
316
+ parseArgs(args: string[] = process.argv.slice(2)): TransformedArgsMap {
317
+ if (!NON_SERVER_ARGS.has(args[0])) {
318
+ args.unshift(SERVER_SUBCOMMAND);
319
+ }
320
+
321
+ try {
322
+ const parsed = this.parser.parse_known_args(args);
323
+ const [knownArgs, unknownArgs] = parsed;
324
+ // XXX: you'd think that argparse, when given an alias for a subcommand,
325
+ // would set this value to the original subcommand name, but it doesn't.
326
+ if (knownArgs?.driverCommand === 'ls') {
327
+ knownArgs.driverCommand = 'list';
328
+ } else if (knownArgs?.pluginCommand === 'ls') {
329
+ knownArgs.pluginCommand = 'list';
330
+ }
331
+ if (
332
+ unknownArgs?.length &&
333
+ (knownArgs.driverCommand === 'run' || knownArgs.pluginCommand === 'run')
334
+ ) {
335
+ return ArgParser._transformParsedArgs(knownArgs, unknownArgs);
336
+ } else if (unknownArgs?.length) {
337
+ throw new Error(`[ERROR] Unrecognized arguments: ${unknownArgs.join(' ')}`);
338
+ }
339
+ return ArgParser._transformParsedArgs(knownArgs);
340
+ } catch (err) {
341
+ if (this.debug) {
342
+ throw err;
343
+ }
344
+ // this isn't tested via unit tests (we use `debug: true`) so may escape coverage.
345
+
346
+ /* istanbul ignore next */
347
+ {
348
+ // eslint-disable-next-line no-console
349
+ console.error(); // need an extra space since argparse prints usage.
350
+ // eslint-disable-next-line no-console
351
+ console.error(err.message);
352
+ process.exit(1);
353
+ }
354
+ }
355
+ }
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) {
378
- finalizeSchema();
361
+ export async function getParser(debug = false): Promise<ArgParser> {
362
+ await 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
+ }