appium 2.0.0-beta.24 → 2.0.0-beta.27

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 (124) hide show
  1. package/build/lib/appium-config.schema.json +278 -0
  2. package/build/lib/appium.js +45 -66
  3. package/build/lib/cli/args.js +19 -39
  4. package/build/lib/cli/driver-command.js +5 -9
  5. package/build/lib/cli/extension-command.js +73 -64
  6. package/build/lib/cli/extension.js +10 -23
  7. package/build/lib/cli/parser.js +9 -19
  8. package/build/lib/cli/plugin-command.js +5 -9
  9. package/build/lib/cli/utils.js +2 -4
  10. package/build/lib/config-file.js +5 -10
  11. package/build/lib/config.js +50 -20
  12. package/build/lib/constants.js +60 -0
  13. package/build/lib/extension/driver-config.js +190 -0
  14. package/build/lib/extension/extension-config.js +297 -0
  15. package/build/lib/extension/index.js +77 -0
  16. package/build/lib/extension/manifest.js +246 -0
  17. package/build/lib/extension/package-changed.js +68 -0
  18. package/build/lib/extension/plugin-config.js +87 -0
  19. package/build/lib/grid-register.js +2 -4
  20. package/build/lib/logger.js +2 -4
  21. package/build/lib/logsink.js +2 -4
  22. package/build/lib/main.js +42 -71
  23. package/build/lib/schema/appium-config-schema.js +2 -4
  24. package/build/lib/schema/arg-spec.js +11 -14
  25. package/build/lib/schema/cli-args.js +2 -4
  26. package/build/lib/schema/cli-transformers.js +2 -4
  27. package/build/lib/schema/index.js +2 -4
  28. package/build/lib/schema/keywords.js +2 -4
  29. package/build/lib/schema/schema.js +61 -37
  30. package/build/lib/utils.js +1 -32
  31. package/lib/appium.js +50 -68
  32. package/lib/cli/args.js +19 -23
  33. package/lib/cli/driver-command.js +10 -2
  34. package/lib/cli/extension-command.js +216 -135
  35. package/lib/cli/extension.js +7 -15
  36. package/lib/cli/parser.js +6 -14
  37. package/lib/cli/plugin-command.js +1 -2
  38. package/lib/config-file.js +12 -15
  39. package/lib/config.js +55 -24
  40. package/lib/constants.js +79 -0
  41. package/lib/extension/driver-config.js +230 -0
  42. package/lib/extension/extension-config.js +459 -0
  43. package/lib/extension/index.js +103 -0
  44. package/lib/extension/manifest.js +590 -0
  45. package/lib/extension/package-changed.js +64 -0
  46. package/lib/extension/plugin-config.js +111 -0
  47. package/lib/grid-register.js +4 -4
  48. package/lib/main.js +57 -93
  49. package/lib/schema/arg-spec.js +3 -3
  50. package/lib/schema/cli-args.js +1 -0
  51. package/lib/schema/keywords.js +1 -1
  52. package/lib/schema/schema.js +67 -29
  53. package/lib/utils.js +2 -32
  54. package/package.json +29 -21
  55. package/{postinstall.js → scripts/postinstall.js} +1 -1
  56. package/types/types.d.ts +70 -31
  57. package/bin/ios-webkit-debug-proxy-launcher.js +0 -71
  58. package/build/check-npm-pack-files.js +0 -23
  59. package/build/commands-yml/parse.js +0 -319
  60. package/build/commands-yml/validator.js +0 -130
  61. package/build/index.js +0 -19
  62. package/build/lib/cli/npm.js +0 -220
  63. package/build/lib/driver-config.js +0 -100
  64. package/build/lib/drivers.js +0 -100
  65. package/build/lib/ext-config-io.js +0 -165
  66. package/build/lib/extension-config.js +0 -320
  67. package/build/lib/plugin-config.js +0 -69
  68. package/build/lib/plugins.js +0 -18
  69. package/build/postinstall.js +0 -90
  70. package/build/test/cli/cli-e2e-specs.js +0 -221
  71. package/build/test/cli/cli-helpers.js +0 -86
  72. package/build/test/cli/cli-specs.js +0 -71
  73. package/build/test/cli/fixtures/test-driver/package.json +0 -27
  74. package/build/test/cli/schema-args-specs.js +0 -48
  75. package/build/test/cli/schema-e2e-specs.js +0 -47
  76. package/build/test/config-e2e-specs.js +0 -112
  77. package/build/test/config-file-e2e-specs.js +0 -209
  78. package/build/test/config-file-specs.js +0 -281
  79. package/build/test/config-specs.js +0 -246
  80. package/build/test/driver-e2e-specs.js +0 -435
  81. package/build/test/driver-specs.js +0 -386
  82. package/build/test/ext-config-io-specs.js +0 -181
  83. package/build/test/extension-config-specs.js +0 -365
  84. package/build/test/fixtures/allow-feat.txt +0 -5
  85. package/build/test/fixtures/caps.json +0 -3
  86. package/build/test/fixtures/config/allow-insecure.txt +0 -3
  87. package/build/test/fixtures/config/appium.config.bad-nodeconfig.json +0 -5
  88. package/build/test/fixtures/config/appium.config.bad.json +0 -32
  89. package/build/test/fixtures/config/appium.config.ext-good.json +0 -9
  90. package/build/test/fixtures/config/appium.config.ext-unknown-props.json +0 -10
  91. package/build/test/fixtures/config/appium.config.good.js +0 -40
  92. package/build/test/fixtures/config/appium.config.good.json +0 -33
  93. package/build/test/fixtures/config/appium.config.good.yaml +0 -30
  94. package/build/test/fixtures/config/appium.config.invalid.json +0 -31
  95. package/build/test/fixtures/config/appium.config.security-array.json +0 -5
  96. package/build/test/fixtures/config/appium.config.security-delimited.json +0 -5
  97. package/build/test/fixtures/config/appium.config.security-path.json +0 -5
  98. package/build/test/fixtures/config/driver-fake.config.json +0 -8
  99. package/build/test/fixtures/config/nodeconfig.json +0 -3
  100. package/build/test/fixtures/config/plugin-fake.config.json +0 -0
  101. package/build/test/fixtures/default-args.js +0 -35
  102. package/build/test/fixtures/deny-feat.txt +0 -5
  103. package/build/test/fixtures/driver.schema.js +0 -20
  104. package/build/test/fixtures/extensions.yaml +0 -27
  105. package/build/test/fixtures/flattened-schema.js +0 -532
  106. package/build/test/fixtures/plugin.schema.js +0 -20
  107. package/build/test/fixtures/schema-with-extensions.js +0 -28
  108. package/build/test/grid-register-specs.js +0 -74
  109. package/build/test/helpers.js +0 -75
  110. package/build/test/logger-specs.js +0 -76
  111. package/build/test/npm-specs.js +0 -20
  112. package/build/test/parser-specs.js +0 -319
  113. package/build/test/plugin-e2e-specs.js +0 -316
  114. package/build/test/schema/arg-spec-specs.js +0 -70
  115. package/build/test/schema/cli-args-specs.js +0 -408
  116. package/build/test/schema/schema-specs.js +0 -407
  117. package/build/test/utils-specs.js +0 -288
  118. package/lib/cli/npm.js +0 -251
  119. package/lib/driver-config.js +0 -101
  120. package/lib/drivers.js +0 -84
  121. package/lib/ext-config-io.js +0 -287
  122. package/lib/extension-config.js +0 -366
  123. package/lib/plugin-config.js +0 -63
  124. package/lib/plugins.js +0 -13
@@ -0,0 +1,111 @@
1
+ // @ts-check
2
+
3
+ import _ from 'lodash';
4
+ import {ExtensionConfig} from './extension-config';
5
+ import log from '../logger';
6
+ import {PLUGIN_TYPE} from '../constants';
7
+
8
+ /**
9
+ * @extends {ExtensionConfig<PluginType>}
10
+ */
11
+ export class PluginConfig extends ExtensionConfig {
12
+
13
+ /**
14
+ * A mapping of {@link Manifest} instances to {@link PluginConfig} instances.
15
+ *
16
+ * `Manifest` and {@link ExtensionConfig} have a one-to-many relationship; each `Manifest` should be associated with a `DriverConfig` and a `PluginConfig`; no more, no less.
17
+ *
18
+ * This variable tracks the `Manifest`-to-`PluginConfig` portion.
19
+ *
20
+ * @type {WeakMap<Manifest,PluginConfig>}
21
+ * @private
22
+ */
23
+ static _instances = new WeakMap();
24
+
25
+ /**
26
+ * Call {@link PluginConfig.create} instead.
27
+ *
28
+ * Just calls the superclass' constructor with the correct extension type
29
+ * @private
30
+ * @param {import('./manifest').Manifest} manifest - IO object
31
+ * @param {PluginConfigOptions} [opts]
32
+ */
33
+ constructor (manifest, {extData, logFn} = {}) {
34
+ super(PLUGIN_TYPE, manifest, logFn);
35
+
36
+ if (extData) {
37
+ this.validate(extData);
38
+ }
39
+ }
40
+
41
+ /**
42
+ * Creates a new {@link PluginConfig} instance for a {@link Manifest} instance.
43
+ *
44
+ * @param {Manifest} manifest
45
+ * @param {PluginConfigOptions} [opts]
46
+ * @throws If `manifest` already associated with a `PluginConfig`
47
+ * @returns {PluginConfig}
48
+ */
49
+ static create (manifest, {extData, logFn} = {}) {
50
+ const instance = new PluginConfig(manifest, {logFn, extData});
51
+ if (PluginConfig.getInstance(manifest)) {
52
+ throw new Error(`Manifest with APPIUM_HOME ${manifest.appiumHome} already has a PluginConfig; use PluginConfig.getInstance() to retrieve it.`);
53
+ }
54
+ PluginConfig._instances.set(manifest, instance);
55
+ return instance;
56
+ }
57
+
58
+ /**
59
+ * Returns a PluginConfig associated with a Manifest
60
+ * @param {Manifest} manifest
61
+ * @returns {PluginConfig|undefined}
62
+ */
63
+ static getInstance (manifest) {
64
+ return PluginConfig._instances.get(manifest);
65
+ }
66
+ /**
67
+ * @param {string} pluginName
68
+ * @param {PluginData} pluginData
69
+ * @returns {string}
70
+ */
71
+ extensionDesc (pluginName, {version}) {
72
+ return `${pluginName}@${version}`;
73
+ }
74
+
75
+ /**
76
+ *
77
+ * @param {(keyof import('./manifest').ExtRecord<PluginType>)[]} activeNames
78
+ * @returns {void}
79
+ */
80
+ print (activeNames) {
81
+ const pluginNames = Object.keys(this.installedExtensions);
82
+
83
+ if (_.isEmpty(pluginNames)) {
84
+ log.info(`No plugins have been installed. Use the "appium plugin" ` +
85
+ 'command to install the one(s) you want to use.');
86
+ return;
87
+ }
88
+
89
+ log.info(`Available plugins:`);
90
+ for (const [pluginName, pluginData] of _.toPairs(this.installedExtensions)) {
91
+ const activeTxt = _.includes(activeNames, pluginName) ? ' (ACTIVE)' : '';
92
+ log.info(` - ${this.extensionDesc(pluginName, pluginData)}${activeTxt}`);
93
+ }
94
+
95
+ if (_.isEmpty(activeNames)) {
96
+ log.info('No plugins activated. Use the --use-plugins flag with names of plugins to activate');
97
+ }
98
+ }
99
+ }
100
+
101
+ /**
102
+ * @typedef PluginConfigOptions
103
+ * @property {import('./extension-config').ExtensionLogFn} [logFn] - Optional logging function
104
+ * @property {import('./manifest').ExtRecord<PluginType>} [extData] - Extension data
105
+ */
106
+
107
+ /**
108
+ * @typedef {import('./manifest').PluginType} PluginType
109
+ * @typedef {import('./manifest').ExtData<PluginType>} PluginData
110
+ * @typedef {import('./manifest').Manifest} Manifest
111
+ */
@@ -11,10 +11,10 @@ const hubUri = (config) => {
11
11
 
12
12
  /**
13
13
  * Registers a new node with a selenium grid
14
- * @param {string|object} data - Path or object representing selenium grid node config file
15
- * @param {string} addr - Bind to this address
16
- * @param {number} port - Bind to this port
17
- * @param {string} basePath - Base path for the grid
14
+ * @param {string|object} data - Path or object representing selenium grid node config file. If a `string`, all subsequent arguments are required!
15
+ * @param {string} [addr] - Bind to this address
16
+ * @param {number} [port] - Bind to this port
17
+ * @param {string} [basePath] - Base path for the grid
18
18
  */
19
19
  async function registerNode (data, addr, port, basePath) {
20
20
  let configFilePath;
package/lib/main.js CHANGED
@@ -3,24 +3,26 @@
3
3
  // transpile:main
4
4
  // @ts-check
5
5
 
6
- import logger from './logger'; // logger needs to remain first of imports
6
+ import { init as logsinkInit } from './logsink'; // this import needs to come first since it sets up global npmlog
7
+ import logger from './logger'; // logger needs to remain second
7
8
  // @ts-ignore
8
9
  import { routeConfiguringFunction as makeRouter, server as baseServer } from '@appium/base-driver';
9
- import { logger as logFactory, util } from '@appium/support';
10
+ import { logger as logFactory, util, env } from '@appium/support';
10
11
  import { asyncify } from 'asyncbox';
11
12
  import _ from 'lodash';
12
13
  import { AppiumDriver } from './appium';
13
- import { driverConfig, pluginConfig, USE_ALL_PLUGINS } from './cli/args';
14
14
  import { runExtensionCommand } from './cli/extension';
15
- import { default as getParser, SERVER_SUBCOMMAND } from './cli/parser';
16
- import { APPIUM_VER, checkNodeOk, getGitRev, getNonDefaultServerArgs, showBuildInfo, validateTmpDir, warnNodeDeprecations, showConfig } from './config';
15
+ import { getParser } from './cli/parser';
16
+ import { APPIUM_VER, checkNodeOk, getGitRev, getNonDefaultServerArgs, showConfig, showBuildInfo, validateTmpDir, warnNodeDeprecations } from './config';
17
17
  import { readConfigFile } from './config-file';
18
- import { DRIVER_TYPE, PLUGIN_TYPE } from './extension-config';
18
+ import { loadExtensions, getActivePlugins, getActiveDrivers } from './extension';
19
+ import { DRIVER_TYPE, PLUGIN_TYPE, SERVER_SUBCOMMAND } from './constants';
19
20
  import registerNode from './grid-register';
20
- import { init as logsinkInit } from './logsink';
21
- import { getDefaultsForSchema } from './schema/schema';
21
+ import { getDefaultsForSchema, validate } from './schema/schema';
22
22
  import { inspect } from './utils';
23
23
 
24
+ const {resolveAppiumHome} = env;
25
+
24
26
  /**
25
27
  *
26
28
  * @param {ParsedArgs} args
@@ -38,6 +40,8 @@ async function preflightChecks (args, throwInsteadOfExit = false) {
38
40
  }
39
41
  warnNodeDeprecations();
40
42
 
43
+ validate(args);
44
+
41
45
  if (args.tmpDir) {
42
46
  await validateTmpDir(args.tmpDir);
43
47
  }
@@ -102,63 +106,11 @@ function logServerPort (address, port) {
102
106
  logger.info(logMessage);
103
107
  }
104
108
 
105
- /**
106
- * Find any plugin name which has been installed, and which has been requested for activation by
107
- * using the --use-plugins flag, and turn each one into its class, so we can send them as objects
108
- * to the server init. We also want to send/assign them to the umbrella driver so it can use them
109
- * to wrap command execution
110
- *
111
- * @param {Object} args - argparser parsed dict
112
- * @param {import('./plugin-config').default} pluginConfig - a plugin extension config
113
- * @returns {PluginExtensionClass[]}
114
- */
115
- function getActivePlugins (args, pluginConfig) {
116
- return _.compact(Object.keys(pluginConfig.installedExtensions).filter((pluginName) =>
117
- _.includes(args.usePlugins, pluginName) ||
118
- (args.usePlugins.length === 1 && args.usePlugins[0] === USE_ALL_PLUGINS)
119
- ).map((pluginName) => {
120
- try {
121
- logger.info(`Attempting to load plugin ${pluginName}...`);
122
- const PluginClass = /** @type {PluginExtensionClass} */(pluginConfig.require(pluginName));
123
-
124
- PluginClass.pluginName = pluginName; // store the plugin name on the class so it can be used later
125
- return PluginClass;
126
- } catch (err) {
127
- logger.error(`Could not load plugin '${pluginName}', so it will not be available. Error ` +
128
- `in loading the plugin was: ${err.message}`);
129
- logger.debug(err.stack);
130
- }
131
- }));
132
- }
133
-
134
- /**
135
- * Find any driver name which has been installed, and turn each one into its class, so we can send
136
- * them as objects to the server init in case they need to add methods/routes or update the server.
137
- * If the --drivers flag was given, this method only loads the given drivers.
138
- *
139
- * @param {Object} args - argparser parsed dict
140
- * @param {import('./driver-config').default} driverConfig - a driver extension config
141
- */
142
- function getActiveDrivers (args, driverConfig) {
143
- return _.compact(Object.keys(driverConfig.installedExtensions).filter((driverName) =>
144
- _.includes(args.useDrivers, driverName) || args.useDrivers.length === 0
145
- ).map((driverName) => {
146
- try {
147
- logger.info(`Attempting to load driver ${driverName}...`);
148
- return driverConfig.require(driverName);
149
- } catch (err) {
150
- logger.error(`Could not load driver '${driverName}', so it will not be available. Error ` +
151
- `in loading the driver was: ${err.message}`);
152
- logger.debug(err.stack);
153
- }
154
- }));
155
- }
156
-
157
109
  /**
158
110
  * Gets a list of `updateServer` functions from all extensions
159
- * @param {DriverExtensionClass[]} driverClasses
160
- * @param {PluginExtensionClass[]} pluginClasses
161
- * @returns {StaticExtMembers['updateServer'][]}
111
+ * @param {DriverClass[]} driverClasses
112
+ * @param {PluginClass[]} pluginClasses
113
+ * @returns {import('./extension/manifest').UpdateServerFn[]}
162
114
  */
163
115
  function getServerUpdaters (driverClasses, pluginClasses) {
164
116
  return _.compact(_.map([...driverClasses, ...pluginClasses], 'updateServer'));
@@ -166,8 +118,8 @@ function getServerUpdaters (driverClasses, pluginClasses) {
166
118
 
167
119
  /**
168
120
  * Makes a big `MethodMap` from all the little `MethodMap`s in the extensions
169
- * @param {DriverExtensionClass[]} driverClasses
170
- * @param {PluginExtensionClass[]} pluginClasses
121
+ * @param {DriverClass[]} driverClasses
122
+ * @param {PluginClass[]} pluginClasses
171
123
  * @returns {import('@appium/base-driver').MethodMap}
172
124
  */
173
125
  function getExtraMethodMap (driverClasses, pluginClasses) {
@@ -182,11 +134,10 @@ function getExtraMethodMap (driverClasses, pluginClasses) {
182
134
  *
183
135
  * Use this to get at the configuration schema.
184
136
  *
185
- * If `args` contains a non-empty `subcommand` which is not `server`, this function
186
- * will resolve with an empty object.
137
+ * If `args` contains a non-empty `subcommand` which is not `server`, this function will return an empty object.
187
138
  *
188
- * @param {ParsedArgs} [args] - Parsed args
189
- * @returns {Promise<Partial<{appiumDriver: AppiumDriver, parsedArgs: ParsedArgs}>>}
139
+ * @param {PartialArgs} [args] - Partial args (progammatic usage only)
140
+ * @returns {Promise<ServerInitResult | ExtCommandInitResult>}
190
141
  * @example
191
142
  * import {init, getSchema} from 'appium';
192
143
  * const options = {}; // config object
@@ -194,11 +145,15 @@ function getExtraMethodMap (driverClasses, pluginClasses) {
194
145
  * const schema = getSchema(); // entire config schema including plugins and drivers
195
146
  */
196
147
  async function init (args) {
197
- const parser = await getParser();
148
+ const appiumHome = args?.appiumHome ?? await resolveAppiumHome();
149
+
150
+ const {driverConfig, pluginConfig} = await loadExtensions(appiumHome);
151
+
152
+ const parser = getParser();
198
153
  let throwInsteadOfExit = false;
199
154
  /** @type {ParsedArgs} */
200
155
  let preConfigParsedArgs;
201
- /** @type {typeof preConfigParsedArgs & import('type-fest').AsyncReturnType<readConfigFile>['config']} */
156
+ /** @type {ParsedArgs} */
202
157
  let parsedArgs;
203
158
  /**
204
159
  * This is a definition (instead of declaration) because TS can't figure out
@@ -215,7 +170,7 @@ async function init (args) {
215
170
  // but remove it since it's not a real server arg per se
216
171
  delete args.throwInsteadOfExit;
217
172
  }
218
- preConfigParsedArgs = {...args, subcommand: args.subcommand ?? SERVER_SUBCOMMAND};
173
+ preConfigParsedArgs = /** @type {ParsedArgs} */({...args, subcommand: args.subcommand ?? SERVER_SUBCOMMAND});
219
174
  } else {
220
175
  // otherwise parse from CLI
221
176
  preConfigParsedArgs = parser.parseArgs();
@@ -227,7 +182,6 @@ async function init (args) {
227
182
  throw new Error(`Errors in config file ${configResult.filepath}:\n ${configResult.reason ?? configResult.errors}`);
228
183
  }
229
184
 
230
-
231
185
  // merge config and apply defaults.
232
186
  // the order of precendece is:
233
187
  // 1. command line args
@@ -236,16 +190,17 @@ async function init (args) {
236
190
  if (preConfigParsedArgs.subcommand === SERVER_SUBCOMMAND) {
237
191
  defaults = getDefaultsForSchema(false);
238
192
 
239
- if (preConfigParsedArgs.showConfig) {
240
- showConfig(preConfigParsedArgs, configResult, defaults);
241
- return {};
242
- }
243
-
244
193
  parsedArgs = _.defaultsDeep(
245
194
  preConfigParsedArgs,
246
195
  configResult.config?.server,
247
196
  defaults
248
197
  );
198
+
199
+ if (preConfigParsedArgs.showConfig) {
200
+ showConfig(getNonDefaultServerArgs(preConfigParsedArgs), configResult, defaults, parsedArgs);
201
+ return {};
202
+ }
203
+
249
204
  } else {
250
205
  parsedArgs = preConfigParsedArgs;
251
206
  }
@@ -255,11 +210,11 @@ async function init (args) {
255
210
  // if the user has requested the 'driver' CLI, don't run the normal server,
256
211
  // but instead pass control to the driver CLI
257
212
  if (parsedArgs.subcommand === DRIVER_TYPE) {
258
- await runExtensionCommand(parsedArgs, parsedArgs.subcommand, driverConfig);
213
+ await runExtensionCommand(parsedArgs, driverConfig);
259
214
  return {};
260
215
  }
261
216
  if (parsedArgs.subcommand === PLUGIN_TYPE) {
262
- await runExtensionCommand(parsedArgs, parsedArgs.subcommand, pluginConfig);
217
+ await runExtensionCommand(parsedArgs, pluginConfig);
263
218
  return {};
264
219
  }
265
220
 
@@ -281,32 +236,32 @@ async function init (args) {
281
236
  appiumDriver.driverConfig = driverConfig;
282
237
  await preflightChecks(parsedArgs, throwInsteadOfExit);
283
238
 
284
- return {appiumDriver, parsedArgs};
239
+ return /** @type {ServerInitResult} */({appiumDriver, parsedArgs, driverConfig, pluginConfig});
285
240
  }
286
241
 
287
242
  /**
288
243
  * Initializes Appium's config. Starts server if appropriate and resolves the
289
244
  * server instance if so; otherwise resolves w/ `undefined`.
290
- * @param {ParsedArgs} [args] - Arguments from CLI or otherwise
291
- * @returns {Promise<import('express').Express|undefined>}
245
+ * @param {PartialArgs} [args] - Arguments from CLI or otherwise
246
+ * @returns {Promise<import('http').Server|undefined>}
292
247
  */
293
248
  async function main (args) {
294
- const {appiumDriver, parsedArgs} = await init(args);
249
+ const {appiumDriver, parsedArgs, pluginConfig, driverConfig} = /** @type {ServerInitResult} */(await init(args));
295
250
 
296
- if (!appiumDriver || !parsedArgs) {
251
+ if (!appiumDriver || !parsedArgs || !pluginConfig || !driverConfig) {
297
252
  // if this branch is taken, we've run a different subcommand, so there's nothing
298
253
  // left to do here.
299
254
  return;
300
255
  }
301
256
 
302
- const pluginClasses = getActivePlugins(parsedArgs, pluginConfig);
257
+ const pluginClasses = getActivePlugins(pluginConfig, parsedArgs.usePlugins);
303
258
  // set the active plugins on the umbrella driver so it can use them for commands
304
259
  appiumDriver.pluginClasses = pluginClasses;
305
260
 
306
261
  await logStartupInfo(parsedArgs);
307
262
  let routeConfiguringFunction = makeRouter(appiumDriver);
308
263
 
309
- const driverClasses = getActiveDrivers(parsedArgs, driverConfig);
264
+ const driverClasses = getActiveDrivers(driverConfig, parsedArgs.useDrivers);
310
265
  const serverUpdaters = getServerUpdaters(driverClasses, pluginClasses);
311
266
  const extraMethodMap = getExtraMethodMap(driverClasses, pluginClasses);
312
267
 
@@ -381,23 +336,32 @@ if (require.main === module) {
381
336
  }
382
337
 
383
338
  // everything below here is intended to be a public API.
384
- export { main, init };
385
- export { APPIUM_HOME } from './extension-config';
386
- export { getSchema, validate, finalizeSchema } from './schema/schema';
387
339
  export { readConfigFile } from './config-file';
340
+ export { finalizeSchema, getSchema, validate } from './schema/schema';
341
+ export { main, init, resolveAppiumHome };
388
342
 
389
343
  /**
390
344
  * @typedef {import('../types/types').ParsedArgs} ParsedArgs
391
345
  */
392
346
 
393
347
  /**
394
- * @typedef {import('./appium').PluginExtensionClass} PluginExtensionClass
348
+ * @typedef {import('../types/types').PartialArgs} PartialArgs
349
+ * @typedef {import('./extension/manifest').DriverType} DriverType
350
+ * @typedef {import('./extension/manifest').PluginType} PluginType
351
+ * @typedef {import('./extension/manifest').DriverClass} DriverClass
352
+ * @typedef {import('./extension/manifest').PluginClass} PluginClass
353
+ */
354
+
355
+ /**
356
+ * @typedef { {} } ExtCommandInitResult
395
357
  */
396
358
 
397
359
  /**
398
- * @typedef {import('./appium').DriverExtensionClass} DriverExtensionClass
360
+ * @typedef ServerInitData
361
+ * @property {AppiumDriver} appiumDriver - The Appium driver
362
+ * @property {ParsedArgs} parsedArgs - The parsed arguments
399
363
  */
400
364
 
401
365
  /**
402
- * @typedef {import('./appium').StaticExtMembers} StaticExtMembers
366
+ * @typedef {ServerInitData & import('./extension').ExtensionConfigs} ServerInitResult
403
367
  */
@@ -176,7 +176,7 @@ export class ArgSpec {
176
176
  /**
177
177
  * When given the root ID of a schema for an extension (`<extType>-<normalizedExtName>.json`) Returns an object containing the extension type and the _normalized_ extension name.
178
178
  * @param {string} schemaId - Root schema ID
179
- * @returns {{extType: ExtensionType|undefined, normalizedExtName: string|undefined}}
179
+ * @returns { {extType: ExtensionType|undefined, normalizedExtName: string|undefined} }
180
180
  */
181
181
  static extensionInfoFromRootSchemaId (schemaId) {
182
182
  const matches = schemaId.match(SCHEMA_ID_REGEXP);
@@ -216,7 +216,7 @@ export class ArgSpec {
216
216
  /**
217
217
  * Options for {@link ArgSpec.create}
218
218
  * @template D
219
- * @typedef {Object} ArgSpecOptions
219
+ * @typedef ArgSpecOptions
220
220
  * @property {string} [extName]
221
221
  * @property {ExtensionType} [extType]
222
222
  * @property {string} [dest]
@@ -224,5 +224,5 @@ export class ArgSpec {
224
224
  */
225
225
 
226
226
  /**
227
- * @typedef {import('../ext-config-io').ExtensionType} ExtensionType
227
+ * @typedef {import('../manifest').ExtensionType} ExtensionType
228
228
  */
@@ -139,6 +139,7 @@ function subSchemaToArgDef (subSchema, argSpec) {
139
139
  // handle special cases for various types
140
140
  switch (type) {
141
141
  // booleans do not have a type per `ArgumentOptions`, just an "action"
142
+ // NOTE: due to limitations of `argparse`, we cannot provide fancy help text, and must rely on its internal error messaging.
142
143
  case TYPENAMES.BOOLEAN: {
143
144
  argOpts.action = 'store_const';
144
145
  argOpts.const = true;
@@ -120,7 +120,7 @@ export const keywords = {
120
120
  /**
121
121
  * These are the custom keywords that Appium recognizes.
122
122
  *
123
- * @typedef {Object} AppiumJSONSchemaKeywords
123
+ * @typedef AppiumJSONSchemaKeywords
124
124
  * @property {string} [appiumCliDest]
125
125
  * @property {string} [appiumCliDescription]
126
126
  * @property {string[]} [appiumCliAliases]
@@ -4,12 +4,42 @@ import Ajv from 'ajv';
4
4
  import addFormats from 'ajv-formats';
5
5
  import _ from 'lodash';
6
6
  import path from 'path';
7
- import { DRIVER_TYPE, PLUGIN_TYPE } from '../extension-config';
8
- import { ReadonlyMap } from '../utils';
7
+ import { DRIVER_TYPE, PLUGIN_TYPE } from '../constants';
9
8
  import appiumConfigSchema from './appium-config-schema';
10
9
  import { APPIUM_CONFIG_SCHEMA_ID, ArgSpec, SERVER_PROP_NAME } from './arg-spec';
11
10
  import { keywords } from './keywords';
12
11
 
12
+ /**
13
+ * Key/value pairs go in... but they don't come out.
14
+ *
15
+ * @template K,V
16
+ * @extends {Map<K,V>}
17
+ */
18
+ export class RoachHotelMap extends Map {
19
+ /**
20
+ * @param {K} key
21
+ * @param {V} value
22
+ */
23
+ set (key, value) {
24
+ if (this.has(key)) {
25
+ throw new Error(`${key} is already set`);
26
+ }
27
+ return super.set(key, value);
28
+ }
29
+
30
+ /**
31
+ * @param {K} key
32
+ */
33
+ // eslint-disable-next-line no-unused-vars
34
+ delete (key) {
35
+ return false;
36
+ }
37
+
38
+ clear () {
39
+ throw new Error(`Cannot clear RoachHotelMap`);
40
+ }
41
+ }
42
+
13
43
  /**
14
44
  * Extensions that an extension schema file can have.
15
45
  */
@@ -28,9 +58,9 @@ class AppiumSchema {
28
58
  *
29
59
  * Used to provide easy lookups of argument metadata when converting between different representations of those arguments.
30
60
  * @private
31
- * @type {ReadonlyMap<string,ArgSpec>}
61
+ * @type {RoachHotelMap<string,ArgSpec>}
32
62
  */
33
- _argSpecs = new ReadonlyMap();
63
+ _argSpecs = new RoachHotelMap();
34
64
 
35
65
  /**
36
66
  * A map of extension types to extension names to schema objects.
@@ -83,24 +113,27 @@ class AppiumSchema {
83
113
  * @returns {AppiumSchema}
84
114
  */
85
115
  static create () {
86
- const instance = AppiumSchema._instance ?? new AppiumSchema();
87
- AppiumSchema._instance = instance;
88
-
89
- _.bindAll(instance, [
90
- 'finalize',
91
- 'flatten',
92
- 'getArgSpec',
93
- 'getDefaults',
94
- 'getDefaultsForExtension',
95
- 'getSchema',
96
- 'hasArgSpec',
97
- 'isFinalized',
98
- 'registerSchema',
99
- 'reset',
100
- 'validate',
101
- ]);
102
-
103
- return instance;
116
+ if (!AppiumSchema._instance) {
117
+ const instance = new AppiumSchema();
118
+ AppiumSchema._instance = instance;
119
+ _.bindAll(instance, [
120
+ 'finalize',
121
+ 'flatten',
122
+ 'getAllArgSpecs',
123
+ 'getArgSpec',
124
+ 'getDefaults',
125
+ 'getDefaultsForExtension',
126
+ 'getSchema',
127
+ 'hasArgSpec',
128
+ 'isFinalized',
129
+ 'registerSchema',
130
+ 'hasRegisteredSchema',
131
+ 'reset',
132
+ 'validate',
133
+ ]);
134
+ }
135
+
136
+ return AppiumSchema._instance;
104
137
  }
105
138
 
106
139
  /**
@@ -124,6 +157,10 @@ class AppiumSchema {
124
157
  return Boolean(this._finalizedSchemas);
125
158
  }
126
159
 
160
+ getAllArgSpecs () {
161
+ return this._argSpecs;
162
+ }
163
+
127
164
  /**
128
165
  * Call this when no more schemas will be registered.
129
166
  *
@@ -145,7 +182,7 @@ class AppiumSchema {
145
182
  */
146
183
  finalize () {
147
184
  if (this.isFinalized()) {
148
- return /** @type {Record<string,StrictSchemaObject>} */ (
185
+ return /** @type {NonNullable<typeof this._finalizedSchemas>} */ (
149
186
  this._finalizedSchemas
150
187
  );
151
188
  }
@@ -254,7 +291,7 @@ class AppiumSchema {
254
291
  for (const schemaId of Object.keys(this._finalizedSchemas ?? {})) {
255
292
  this._ajv.removeSchema(schemaId);
256
293
  }
257
- this._argSpecs = new ReadonlyMap();
294
+ this._argSpecs = new RoachHotelMap();
258
295
  this._registeredSchemas = {
259
296
  [DRIVER_TYPE]: new Map(),
260
297
  [PLUGIN_TYPE]: new Map(),
@@ -271,7 +308,7 @@ class AppiumSchema {
271
308
  * This is "fail-fast" in that the schema will immediately be validated against JSON schema draft-07 _or_ whatever the value of the schema's `$schema` prop is.
272
309
  *
273
310
  * Does _not_ add the schema to the `ajv` instance (this is done by {@link AppiumSchema.finalize}).
274
- * @param {import('../ext-config-io').ExtensionType} extType - Extension type
311
+ * @param {ExtensionType} extType - Extension type
275
312
  * @param {string} extName - Unique extension name for `type`
276
313
  * @param {SchemaObject} schema - Schema object
277
314
  * @throws {SchemaNameConflictError} If the schema is an invalid
@@ -403,7 +440,7 @@ class AppiumSchema {
403
440
  flatten () {
404
441
  const schema = this.getSchema();
405
442
 
406
- /** @type {{properties: SchemaObject, prefix: string[]}[]} */
443
+ /** @type { {properties: SchemaObject, prefix: string[]}[] } */
407
444
  const stack = [{properties: schema.properties, prefix: []}];
408
445
  /** @type {FlattenedSchema} */
409
446
  const flattened = [];
@@ -641,6 +678,7 @@ const appiumSchema = AppiumSchema.create();
641
678
 
642
679
  export const {
643
680
  registerSchema,
681
+ getAllArgSpecs,
644
682
  getArgSpec,
645
683
  hasArgSpec,
646
684
  isFinalized,
@@ -660,12 +698,12 @@ export const {isAllowedSchemaFileExtension} = AppiumSchema;
660
698
  */
661
699
 
662
700
  /**
663
- * @typedef {import('../ext-config-io').ExtensionType} ExtensionType
701
+ * @typedef {import('../extension/manifest').ExtensionType} ExtensionType
664
702
  */
665
703
 
666
704
  /**
667
705
  * An object having property `additionalProperties: false`
668
- * @typedef {Object} StrictProp
706
+ * @typedef StrictProp
669
707
  * @property {false} additionalProperties
670
708
  */
671
709
 
@@ -678,7 +716,7 @@ export const {isAllowedSchemaFileExtension} = AppiumSchema;
678
716
  * A list of schemas associated with properties and their corresponding {@link ArgSpec} objects.
679
717
  *
680
718
  * Intermediate data structure used when converting the entire schema down to CLI arguments.
681
- * @typedef {{schema: SchemaObject, argSpec: ArgSpec}[]} FlattenedSchema
719
+ * @typedef { {schema: SchemaObject, argSpec: ArgSpec}[] } FlattenedSchema
682
720
  */
683
721
 
684
722
  /**
package/lib/utils.js CHANGED
@@ -4,7 +4,6 @@ import _ from 'lodash';
4
4
  import logger from './logger';
5
5
  // @ts-ignore
6
6
  import { processCapabilities, PROTOCOLS } from '@appium/base-driver';
7
- import { fs } from '@appium/support';
8
7
  import { inspect as dump } from 'util';
9
8
 
10
9
  const W3C_APPIUM_PREFIX = 'appium';
@@ -215,36 +214,7 @@ function pullSettings (caps) {
215
214
  return result;
216
215
  }
217
216
 
218
- const rootDir = fs.findRoot(__dirname);
219
-
220
-
221
- /**
222
- * A Map where you can set properties, but only once. And you can't remove anything. So there.
223
- * @template K,V
224
- * @extends {Map<K,V>}
225
- */
226
- class ReadonlyMap extends Map {
227
- /**
228
- * @param {K} key
229
- * @param {V} value
230
- */
231
- set (key, value) {
232
- if (this.has(key)) {
233
- throw new Error(`${key} is already set`);
234
- }
235
- return super.set(key, value);
236
- }
237
-
238
- delete () {
239
- return false;
240
- }
241
-
242
- clear () {
243
- throw new Error(`Cannot clear ReadonlyMap`);
244
- }
245
- }
246
-
247
217
  export {
248
- inspect, parseCapsForInnerDriver, insertAppiumPrefixes, rootDir,
249
- getPackageVersion, pullSettings, removeAppiumPrefixes, ReadonlyMap
218
+ inspect, parseCapsForInnerDriver, insertAppiumPrefixes,
219
+ getPackageVersion, pullSettings, removeAppiumPrefixes
250
220
  };