appium 2.0.0-beta.25 → 2.0.0-beta.26

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 +2 -4
  11. package/build/lib/config.js +7 -6
  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 +40 -68
  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 +55 -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 +3 -3
  39. package/lib/config.js +5 -4
  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 +51 -88
  49. package/lib/schema/arg-spec.js +2 -2
  50. package/lib/schema/cli-args.js +1 -0
  51. package/lib/schema/keywords.js +1 -1
  52. package/lib/schema/schema.js +60 -28
  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 -191
  78. package/build/test/config-file-specs.js +0 -281
  79. package/build/test/config-specs.js +0 -258
  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
@@ -256,11 +210,11 @@ async function init (args) {
256
210
  // if the user has requested the 'driver' CLI, don't run the normal server,
257
211
  // but instead pass control to the driver CLI
258
212
  if (parsedArgs.subcommand === DRIVER_TYPE) {
259
- await runExtensionCommand(parsedArgs, parsedArgs.subcommand, driverConfig);
213
+ await runExtensionCommand(parsedArgs, driverConfig);
260
214
  return {};
261
215
  }
262
216
  if (parsedArgs.subcommand === PLUGIN_TYPE) {
263
- await runExtensionCommand(parsedArgs, parsedArgs.subcommand, pluginConfig);
217
+ await runExtensionCommand(parsedArgs, pluginConfig);
264
218
  return {};
265
219
  }
266
220
 
@@ -282,32 +236,32 @@ async function init (args) {
282
236
  appiumDriver.driverConfig = driverConfig;
283
237
  await preflightChecks(parsedArgs, throwInsteadOfExit);
284
238
 
285
- return {appiumDriver, parsedArgs};
239
+ return /** @type {ServerInitResult} */({appiumDriver, parsedArgs, driverConfig, pluginConfig});
286
240
  }
287
241
 
288
242
  /**
289
243
  * Initializes Appium's config. Starts server if appropriate and resolves the
290
244
  * server instance if so; otherwise resolves w/ `undefined`.
291
- * @param {ParsedArgs} [args] - Arguments from CLI or otherwise
292
- * @returns {Promise<import('express').Express|undefined>}
245
+ * @param {PartialArgs} [args] - Arguments from CLI or otherwise
246
+ * @returns {Promise<import('http').Server|undefined>}
293
247
  */
294
248
  async function main (args) {
295
- const {appiumDriver, parsedArgs} = await init(args);
249
+ const {appiumDriver, parsedArgs, pluginConfig, driverConfig} = /** @type {ServerInitResult} */(await init(args));
296
250
 
297
- if (!appiumDriver || !parsedArgs) {
251
+ if (!appiumDriver || !parsedArgs || !pluginConfig || !driverConfig) {
298
252
  // if this branch is taken, we've run a different subcommand, so there's nothing
299
253
  // left to do here.
300
254
  return;
301
255
  }
302
256
 
303
- const pluginClasses = getActivePlugins(parsedArgs, pluginConfig);
257
+ const pluginClasses = getActivePlugins(pluginConfig, parsedArgs.usePlugins);
304
258
  // set the active plugins on the umbrella driver so it can use them for commands
305
259
  appiumDriver.pluginClasses = pluginClasses;
306
260
 
307
261
  await logStartupInfo(parsedArgs);
308
262
  let routeConfiguringFunction = makeRouter(appiumDriver);
309
263
 
310
- const driverClasses = getActiveDrivers(parsedArgs, driverConfig);
264
+ const driverClasses = getActiveDrivers(driverConfig, parsedArgs.useDrivers);
311
265
  const serverUpdaters = getServerUpdaters(driverClasses, pluginClasses);
312
266
  const extraMethodMap = getExtraMethodMap(driverClasses, pluginClasses);
313
267
 
@@ -382,23 +336,32 @@ if (require.main === module) {
382
336
  }
383
337
 
384
338
  // everything below here is intended to be a public API.
385
- export { main, init };
386
- export { APPIUM_HOME } from './extension-config';
387
- export { getSchema, validate, finalizeSchema } from './schema/schema';
388
339
  export { readConfigFile } from './config-file';
340
+ export { finalizeSchema, getSchema, validate } from './schema/schema';
341
+ export { main, init, resolveAppiumHome };
389
342
 
390
343
  /**
391
344
  * @typedef {import('../types/types').ParsedArgs} ParsedArgs
392
345
  */
393
346
 
394
347
  /**
395
- * @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
396
357
  */
397
358
 
398
359
  /**
399
- * @typedef {import('./appium').DriverExtensionClass} DriverExtensionClass
360
+ * @typedef ServerInitData
361
+ * @property {AppiumDriver} appiumDriver - The Appium driver
362
+ * @property {ParsedArgs} parsedArgs - The parsed arguments
400
363
  */
401
364
 
402
365
  /**
403
- * @typedef {import('./appium').StaticExtMembers} StaticExtMembers
366
+ * @typedef {ServerInitData & import('./extension').ExtensionConfigs} ServerInitResult
404
367
  */
@@ -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,25 +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
- 'getAllArgSpecs',
93
- 'getArgSpec',
94
- 'getDefaults',
95
- 'getDefaultsForExtension',
96
- 'getSchema',
97
- 'hasArgSpec',
98
- 'isFinalized',
99
- 'registerSchema',
100
- 'reset',
101
- 'validate',
102
- ]);
103
-
104
- 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;
105
137
  }
106
138
 
107
139
  /**
@@ -150,7 +182,7 @@ class AppiumSchema {
150
182
  */
151
183
  finalize () {
152
184
  if (this.isFinalized()) {
153
- return /** @type {Record<string,StrictSchemaObject>} */ (
185
+ return /** @type {NonNullable<typeof this._finalizedSchemas>} */ (
154
186
  this._finalizedSchemas
155
187
  );
156
188
  }
@@ -259,7 +291,7 @@ class AppiumSchema {
259
291
  for (const schemaId of Object.keys(this._finalizedSchemas ?? {})) {
260
292
  this._ajv.removeSchema(schemaId);
261
293
  }
262
- this._argSpecs = new ReadonlyMap();
294
+ this._argSpecs = new RoachHotelMap();
263
295
  this._registeredSchemas = {
264
296
  [DRIVER_TYPE]: new Map(),
265
297
  [PLUGIN_TYPE]: new Map(),
@@ -276,7 +308,7 @@ class AppiumSchema {
276
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.
277
309
  *
278
310
  * Does _not_ add the schema to the `ajv` instance (this is done by {@link AppiumSchema.finalize}).
279
- * @param {import('../ext-config-io').ExtensionType} extType - Extension type
311
+ * @param {ExtensionType} extType - Extension type
280
312
  * @param {string} extName - Unique extension name for `type`
281
313
  * @param {SchemaObject} schema - Schema object
282
314
  * @throws {SchemaNameConflictError} If the schema is an invalid
@@ -666,12 +698,12 @@ export const {isAllowedSchemaFileExtension} = AppiumSchema;
666
698
  */
667
699
 
668
700
  /**
669
- * @typedef {import('../ext-config-io').ExtensionType} ExtensionType
701
+ * @typedef {import('../extension/manifest').ExtensionType} ExtensionType
670
702
  */
671
703
 
672
704
  /**
673
705
  * An object having property `additionalProperties: false`
674
- * @typedef {Object} StrictProp
706
+ * @typedef StrictProp
675
707
  * @property {false} additionalProperties
676
708
  */
677
709
 
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
  };