appium 2.0.0-beta.23 → 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 (125) hide show
  1. package/README.md +1 -2
  2. package/build/lib/appium-config.schema.json +278 -0
  3. package/build/lib/appium.js +62 -71
  4. package/build/lib/cli/args.js +31 -53
  5. package/build/lib/cli/driver-command.js +5 -9
  6. package/build/lib/cli/extension-command.js +73 -64
  7. package/build/lib/cli/extension.js +10 -23
  8. package/build/lib/cli/parser.js +10 -20
  9. package/build/lib/cli/plugin-command.js +5 -9
  10. package/build/lib/cli/utils.js +2 -4
  11. package/build/lib/config-file.js +5 -10
  12. package/build/lib/config.js +67 -30
  13. package/build/lib/constants.js +60 -0
  14. package/build/lib/extension/driver-config.js +190 -0
  15. package/build/lib/extension/extension-config.js +297 -0
  16. package/build/lib/extension/index.js +77 -0
  17. package/build/lib/extension/manifest.js +246 -0
  18. package/build/lib/extension/package-changed.js +68 -0
  19. package/build/lib/extension/plugin-config.js +87 -0
  20. package/build/lib/grid-register.js +2 -4
  21. package/build/lib/logger.js +2 -4
  22. package/build/lib/logsink.js +2 -4
  23. package/build/lib/main.js +64 -92
  24. package/build/lib/schema/appium-config-schema.js +2 -4
  25. package/build/lib/schema/arg-spec.js +14 -15
  26. package/build/lib/schema/cli-args.js +8 -16
  27. package/build/lib/schema/cli-transformers.js +2 -4
  28. package/build/lib/schema/index.js +2 -4
  29. package/build/lib/schema/keywords.js +2 -4
  30. package/build/lib/schema/schema.js +136 -41
  31. package/build/lib/utils.js +13 -64
  32. package/lib/appium.js +74 -55
  33. package/lib/cli/args.js +36 -37
  34. package/lib/cli/driver-command.js +10 -2
  35. package/lib/cli/extension-command.js +216 -135
  36. package/lib/cli/extension.js +7 -15
  37. package/lib/cli/parser.js +7 -15
  38. package/lib/cli/plugin-command.js +1 -2
  39. package/lib/config-file.js +12 -15
  40. package/lib/config.js +111 -36
  41. package/lib/constants.js +79 -0
  42. package/lib/extension/driver-config.js +230 -0
  43. package/lib/extension/extension-config.js +459 -0
  44. package/lib/extension/index.js +103 -0
  45. package/lib/extension/manifest.js +590 -0
  46. package/lib/extension/package-changed.js +64 -0
  47. package/lib/extension/plugin-config.js +111 -0
  48. package/lib/grid-register.js +4 -4
  49. package/lib/main.js +110 -96
  50. package/lib/schema/arg-spec.js +11 -5
  51. package/lib/schema/cli-args.js +7 -30
  52. package/lib/schema/keywords.js +1 -1
  53. package/lib/schema/schema.js +206 -48
  54. package/lib/utils.js +27 -58
  55. package/package.json +29 -21
  56. package/{postinstall.js → scripts/postinstall.js} +1 -1
  57. package/types/types.d.ts +72 -28
  58. package/bin/ios-webkit-debug-proxy-launcher.js +0 -71
  59. package/build/check-npm-pack-files.js +0 -23
  60. package/build/commands-yml/parse.js +0 -319
  61. package/build/commands-yml/validator.js +0 -130
  62. package/build/index.js +0 -19
  63. package/build/lib/cli/npm.js +0 -220
  64. package/build/lib/driver-config.js +0 -100
  65. package/build/lib/drivers.js +0 -100
  66. package/build/lib/ext-config-io.js +0 -165
  67. package/build/lib/extension-config.js +0 -320
  68. package/build/lib/plugin-config.js +0 -69
  69. package/build/lib/plugins.js +0 -16
  70. package/build/postinstall.js +0 -90
  71. package/build/test/cli/cli-e2e-specs.js +0 -221
  72. package/build/test/cli/cli-helpers.js +0 -86
  73. package/build/test/cli/cli-specs.js +0 -71
  74. package/build/test/cli/fixtures/test-driver/package.json +0 -27
  75. package/build/test/cli/schema-args-specs.js +0 -48
  76. package/build/test/cli/schema-e2e-specs.js +0 -47
  77. package/build/test/config-e2e-specs.js +0 -112
  78. package/build/test/config-file-e2e-specs.js +0 -209
  79. package/build/test/config-file-specs.js +0 -281
  80. package/build/test/config-specs.js +0 -159
  81. package/build/test/driver-e2e-specs.js +0 -435
  82. package/build/test/driver-specs.js +0 -321
  83. package/build/test/ext-config-io-specs.js +0 -181
  84. package/build/test/extension-config-specs.js +0 -365
  85. package/build/test/fixtures/allow-feat.txt +0 -5
  86. package/build/test/fixtures/caps.json +0 -3
  87. package/build/test/fixtures/config/allow-insecure.txt +0 -3
  88. package/build/test/fixtures/config/appium.config.bad-nodeconfig.json +0 -5
  89. package/build/test/fixtures/config/appium.config.bad.json +0 -32
  90. package/build/test/fixtures/config/appium.config.ext-good.json +0 -9
  91. package/build/test/fixtures/config/appium.config.ext-unknown-props.json +0 -10
  92. package/build/test/fixtures/config/appium.config.good.js +0 -40
  93. package/build/test/fixtures/config/appium.config.good.json +0 -33
  94. package/build/test/fixtures/config/appium.config.good.yaml +0 -30
  95. package/build/test/fixtures/config/appium.config.invalid.json +0 -31
  96. package/build/test/fixtures/config/appium.config.security-array.json +0 -5
  97. package/build/test/fixtures/config/appium.config.security-delimited.json +0 -5
  98. package/build/test/fixtures/config/appium.config.security-path.json +0 -5
  99. package/build/test/fixtures/config/driver-fake.config.json +0 -8
  100. package/build/test/fixtures/config/nodeconfig.json +0 -3
  101. package/build/test/fixtures/config/plugin-fake.config.json +0 -0
  102. package/build/test/fixtures/default-args.js +0 -35
  103. package/build/test/fixtures/deny-feat.txt +0 -5
  104. package/build/test/fixtures/driver.schema.js +0 -20
  105. package/build/test/fixtures/extensions.yaml +0 -27
  106. package/build/test/fixtures/flattened-schema.js +0 -504
  107. package/build/test/fixtures/plugin.schema.js +0 -20
  108. package/build/test/fixtures/schema-with-extensions.js +0 -28
  109. package/build/test/grid-register-specs.js +0 -74
  110. package/build/test/helpers.js +0 -75
  111. package/build/test/logger-specs.js +0 -76
  112. package/build/test/npm-specs.js +0 -20
  113. package/build/test/parser-specs.js +0 -314
  114. package/build/test/plugin-e2e-specs.js +0 -316
  115. package/build/test/schema/arg-spec-specs.js +0 -70
  116. package/build/test/schema/cli-args-specs.js +0 -431
  117. package/build/test/schema/schema-specs.js +0 -389
  118. package/build/test/utils-specs.js +0 -266
  119. package/lib/cli/npm.js +0 -251
  120. package/lib/driver-config.js +0 -101
  121. package/lib/drivers.js +0 -84
  122. package/lib/ext-config-io.js +0 -287
  123. package/lib/extension-config.js +0 -366
  124. package/lib/plugin-config.js +0 -63
  125. package/lib/plugins.js +0 -11
@@ -0,0 +1,459 @@
1
+ // @ts-check
2
+
3
+ import _ from 'lodash';
4
+ import path from 'path';
5
+ import resolveFrom from 'resolve-from';
6
+ import log from '../logger';
7
+ import {
8
+ ALLOWED_SCHEMA_EXTENSIONS,
9
+ isAllowedSchemaFileExtension,
10
+ registerSchema,
11
+ } from '../schema/schema';
12
+
13
+ const INSTALL_TYPE_NPM = 'npm';
14
+ const INSTALL_TYPE_LOCAL = 'local';
15
+ const INSTALL_TYPE_GITHUB = 'github';
16
+ const INSTALL_TYPE_GIT = 'git';
17
+
18
+ /** @type {Set<InstallType>} */
19
+ const INSTALL_TYPES = new Set([
20
+ INSTALL_TYPE_GIT,
21
+ INSTALL_TYPE_GITHUB,
22
+ INSTALL_TYPE_LOCAL,
23
+ INSTALL_TYPE_NPM,
24
+ ]);
25
+
26
+ /**
27
+ * This class is abstract. It should not be instantiated directly.
28
+ *
29
+ * Subclasses should provide the generic parameter to implement.
30
+ * @template {ExtensionType} ExtType
31
+ */
32
+ export class ExtensionConfig {
33
+ /** @type {ExtType} */
34
+ extensionType;
35
+
36
+ /** @type {`${ExtType}s`} */
37
+ configKey;
38
+
39
+ /** @type {ExtRecord<ExtType>} */
40
+ installedExtensions;
41
+
42
+ /** @type {ExtensionLogFn} */
43
+ log;
44
+
45
+ /** @type {Manifest} */
46
+ manifest;
47
+
48
+ /**
49
+ * @protected
50
+ * @param {ExtType} extensionType - Type of extension
51
+ * @param {Manifest} manifest - `Manifest` instance
52
+ * @param {ExtensionLogFn} [logFn]
53
+ */
54
+ constructor (extensionType, manifest, logFn) {
55
+ const logger = _.isFunction(logFn) ? logFn : log.error.bind(log);
56
+ this.extensionType = extensionType;
57
+ this.configKey = `${extensionType}s`;
58
+ this.installedExtensions = manifest.getExtensionData(extensionType);
59
+ this.log = logger;
60
+ this.manifest = manifest;
61
+ }
62
+
63
+ get manifestPath () {
64
+ return this.manifest.manifestPath;
65
+ }
66
+
67
+ get appiumHome () {
68
+ return this.manifest.appiumHome;
69
+ }
70
+
71
+ /**
72
+ * Checks extensions for problems
73
+ * @param {ExtRecord<ExtType>} exts - Extension data
74
+ */
75
+ validate (exts) {
76
+ const foundProblems =
77
+ /** @type {Record<ExtName<ExtType>,Problem[]>} */ ({});
78
+ for (const [
79
+ extName,
80
+ extData,
81
+ ] of /** @type {[ExtName<ExtType>, ExtData<ExtType>][]} */ (
82
+ _.toPairs(exts)
83
+ )) {
84
+ foundProblems[extName] = [
85
+ ...this.getGenericConfigProblems(extData, extName),
86
+ ...this.getConfigProblems(extData),
87
+ ...this.getSchemaProblems(extData, extName),
88
+ ];
89
+ }
90
+
91
+ const problemSummaries = [];
92
+ for (const [extName, problems] of _.toPairs(foundProblems)) {
93
+ if (_.isEmpty(problems)) {
94
+ continue;
95
+ }
96
+ // remove this extension from the list since it's not valid
97
+ delete exts[extName];
98
+ problemSummaries.push(
99
+ `${this.extensionType} ${extName} had errors and will not ` +
100
+ `be available. Errors:`,
101
+ );
102
+ for (const problem of problems) {
103
+ problemSummaries.push(
104
+ ` - ${problem.err} (Actual value: ` +
105
+ `${JSON.stringify(problem.val)})`,
106
+ );
107
+ }
108
+ }
109
+
110
+ if (!_.isEmpty(problemSummaries)) {
111
+ this.log(
112
+ `Appium encountered one or more errors while validating ` +
113
+ `the ${this.configKey} extension file (${this.manifestPath}):`,
114
+ );
115
+ for (const summary of problemSummaries) {
116
+ this.log(summary);
117
+ }
118
+ }
119
+
120
+ return exts;
121
+ }
122
+
123
+ /**
124
+ * @param {ExtData<ExtType>} extData
125
+ * @param {ExtName<ExtType>} extName
126
+ * @returns {Problem[]}
127
+ */
128
+ getSchemaProblems (extData, extName) {
129
+ const problems = [];
130
+ const {schema: argSchemaPath} = extData;
131
+ if (ExtensionConfig.extDataHasSchema(extData)) {
132
+ if (_.isString(argSchemaPath)) {
133
+ if (isAllowedSchemaFileExtension(argSchemaPath)) {
134
+ try {
135
+ this.readExtensionSchema(extName, extData);
136
+ } catch (err) {
137
+ problems.push({
138
+ err: `Unable to register schema at path ${argSchemaPath}; ${err.message}`,
139
+ val: argSchemaPath,
140
+ });
141
+ }
142
+ } else {
143
+ problems.push({
144
+ err: `Schema file has unsupported extension. Allowed: ${[
145
+ ...ALLOWED_SCHEMA_EXTENSIONS,
146
+ ].join(', ')}`,
147
+ val: argSchemaPath,
148
+ });
149
+ }
150
+ } else if (_.isPlainObject(argSchemaPath)) {
151
+ try {
152
+ this.readExtensionSchema(extName, extData);
153
+ } catch (err) {
154
+ problems.push({
155
+ err: `Unable to register embedded schema; ${err.message}`,
156
+ val: argSchemaPath,
157
+ });
158
+ }
159
+ } else {
160
+ problems.push({
161
+ err: 'Incorrectly formatted schema field; must be a path to a schema file or a schema object.',
162
+ val: argSchemaPath,
163
+ });
164
+ }
165
+ }
166
+ return problems;
167
+ }
168
+
169
+ /**
170
+ * @param {ExtData<ExtType>} extData
171
+ * @param {ExtName<ExtType>} extName
172
+ * @returns {Problem[]}
173
+ */
174
+ // eslint-disable-next-line no-unused-vars
175
+ getGenericConfigProblems (extData, extName) {
176
+ const {version, pkgName, installSpec, installType, mainClass} =
177
+ extData;
178
+ const problems = [];
179
+
180
+ if (!_.isString(version)) {
181
+ problems.push({err: 'Missing or incorrect version', val: version});
182
+ }
183
+
184
+ if (!_.isString(pkgName)) {
185
+ problems.push({
186
+ err: 'Missing or incorrect NPM package name',
187
+ val: pkgName,
188
+ });
189
+ }
190
+
191
+ if (!_.isString(installSpec)) {
192
+ problems.push({
193
+ err: 'Missing or incorrect installation spec',
194
+ val: installSpec,
195
+ });
196
+ }
197
+
198
+ if (!INSTALL_TYPES.has(installType)) {
199
+ problems.push({
200
+ err: 'Missing or incorrect install type',
201
+ val: installType,
202
+ });
203
+ }
204
+
205
+ if (!_.isString(mainClass)) {
206
+ problems.push({
207
+ err: 'Missing or incorrect driver class name',
208
+ val: mainClass,
209
+ });
210
+ }
211
+
212
+ return problems;
213
+ }
214
+
215
+ /**
216
+ * @abstract
217
+ * @param {ExtData<ExtType>} extData
218
+ * @returns {Problem[]}
219
+ */
220
+ // eslint-disable-next-line no-unused-vars
221
+ getConfigProblems (extData) {
222
+ // shoud override this method if special validation is necessary for this extension type
223
+ return [];
224
+ }
225
+
226
+ /**
227
+ * @param {string} extName
228
+ * @param {ExtData<ExtType>} extData
229
+ * @param {ExtensionConfigMutationOpts} [opts]
230
+ * @returns {Promise<void>}
231
+ */
232
+ async addExtension (extName, extData, {write = true} = {}) {
233
+ await this.manifest.addExtension(this.extensionType, extName, extData);
234
+ if (write) {
235
+ await this.manifest.write();
236
+ }
237
+ }
238
+
239
+ /**
240
+ * @param {ExtName<ExtType>} extName
241
+ * @param {ExtData<ExtType>} extData
242
+ * @param {ExtensionConfigMutationOpts} [opts]
243
+ * @returns {Promise<void>}
244
+ */
245
+ async updateExtension (extName, extData, {write = true} = {}) {
246
+ this.installedExtensions[extName] = {
247
+ ...this.installedExtensions[extName],
248
+ ...extData,
249
+ };
250
+ if (write) {
251
+ await this.manifest.write();
252
+ }
253
+ }
254
+
255
+ /**
256
+ * @param {ExtName<ExtType>} extName
257
+ * @param {ExtensionConfigMutationOpts} [opts]
258
+ * @returns {Promise<void>}
259
+ */
260
+ async removeExtension (extName, {write = true} = {}) {
261
+ delete this.installedExtensions[extName];
262
+ if (write) {
263
+ await this.manifest.write();
264
+ }
265
+ }
266
+
267
+ /**
268
+ * @param {ExtName<ExtType>[]} [activeNames]
269
+ * @returns {void}
270
+ */
271
+ // eslint-disable-next-line no-unused-vars
272
+ print (activeNames) {
273
+ if (_.isEmpty(this.installedExtensions)) {
274
+ log.info(
275
+ `No ${this.configKey} have been installed in ${this.appiumHome}. Use the "appium ${this.extensionType}" ` +
276
+ 'command to install the one(s) you want to use.',
277
+ );
278
+ return;
279
+ }
280
+
281
+ log.info(`Available ${this.configKey}:`);
282
+ for (const [
283
+ extName,
284
+ extData,
285
+ ] of /** @type {[string, ExtData<ExtType>][]} */ (
286
+ _.toPairs(this.installedExtensions)
287
+ )) {
288
+ log.info(` - ${this.extensionDesc(extName, extData)}`);
289
+ }
290
+ }
291
+
292
+ /**
293
+ * Returns a string describing the extension. Subclasses must implement.
294
+ * @param {ExtName<ExtType>} extName - Extension name
295
+ * @param {ExtData<ExtType>} extData - Extension data
296
+ * @returns {string}
297
+ * @abstract
298
+ */
299
+ // eslint-disable-next-line no-unused-vars
300
+ extensionDesc (extName, extData) {
301
+ throw new Error('This must be implemented in a subclass');
302
+ }
303
+
304
+ /**
305
+ * @param {string} extName
306
+ * @returns {string}
307
+ */
308
+ getInstallPath (extName) {
309
+ return path.join(this.appiumHome, 'node_modules', this.installedExtensions[extName].pkgName);
310
+ }
311
+
312
+ /**
313
+ * Loads extension and returns its main class (constructor)
314
+ * @param {ExtName<ExtType>} extName
315
+ * @returns {ExtClass<ExtType>}
316
+ */
317
+ require (extName) {
318
+ const {mainClass} = this.installedExtensions[extName];
319
+ const reqPath = this.getInstallPath(extName);
320
+ const reqResolved = require.resolve(reqPath);
321
+ // note: this will only reload the entry point
322
+ if (process.env.APPIUM_RELOAD_EXTENSIONS && require.cache[reqResolved]) {
323
+ log.debug(`Removing ${reqResolved} from require cache`);
324
+ delete require.cache[reqResolved];
325
+ }
326
+ log.debug(`Requiring ${this.extensionType} at ${reqPath}`);
327
+ return require(reqPath)[mainClass];
328
+ }
329
+
330
+ /**
331
+ * @param {string} extName
332
+ * @returns {boolean}
333
+ */
334
+ isInstalled (extName) {
335
+ return _.includes(Object.keys(this.installedExtensions), extName);
336
+ }
337
+
338
+ /**
339
+ * Intended to be called by corresponding instance methods of subclass.
340
+ * @private
341
+ * @template {ExtensionType} ExtType
342
+ * @param {string} appiumHome
343
+ * @param {ExtType} extType
344
+ * @param {ExtName<ExtType>} extName - Extension name (unique to its type)
345
+ * @param {ExtDataWithSchema<ExtType>} extData - Extension config
346
+ * @returns {import('ajv').SchemaObject|undefined}
347
+ */
348
+ static _readExtensionSchema (appiumHome, extType, extName, extData) {
349
+ const {pkgName, schema: argSchemaPath} = extData;
350
+ if (!argSchemaPath) {
351
+ throw new TypeError(
352
+ `No \`schema\` property found in config for ${extType} ${pkgName} -- why is this function being called?`,
353
+ );
354
+ }
355
+ let moduleObject;
356
+ if (_.isString(argSchemaPath)) {
357
+ const schemaPath = resolveFrom(appiumHome, path.join(pkgName, argSchemaPath));
358
+ moduleObject = require(schemaPath);
359
+ } else {
360
+ moduleObject = argSchemaPath;
361
+ }
362
+ // this sucks. default exports should be destroyed
363
+ const schema = moduleObject.__esModule
364
+ ? moduleObject.default
365
+ : moduleObject;
366
+ registerSchema(extType, extName, schema);
367
+ return schema;
368
+ }
369
+
370
+ /**
371
+ * Returns `true` if a specific {@link ExtData} object has a `schema` prop.
372
+ * The {@link ExtData} object becomes a {@link ExtDataWithSchema} object.
373
+ * @template {ExtensionType} ExtType
374
+ * @param {ExtData<ExtType>} extData
375
+ * @returns {extData is ExtDataWithSchema<ExtType>}
376
+ */
377
+ static extDataHasSchema (extData) {
378
+ return _.isString(extData?.schema) || _.isObject(extData?.schema);
379
+ }
380
+
381
+ /**
382
+ * If an extension provides a schema, this will load the schema and attempt to
383
+ * register it with the schema registrar.
384
+ * @param {ExtName<ExtType>} extName - Name of extension
385
+ * @param {ExtDataWithSchema<ExtType>} extData - Extension data
386
+ * @returns {import('ajv').SchemaObject|undefined}
387
+ */
388
+ readExtensionSchema (extName, extData) {
389
+ return ExtensionConfig._readExtensionSchema(
390
+ this.appiumHome,
391
+ this.extensionType,
392
+ extName,
393
+ extData,
394
+ );
395
+ }
396
+ }
397
+
398
+ export {
399
+ INSTALL_TYPE_NPM,
400
+ INSTALL_TYPE_GIT,
401
+ INSTALL_TYPE_LOCAL,
402
+ INSTALL_TYPE_GITHUB,
403
+ INSTALL_TYPES,
404
+ };
405
+
406
+ /**
407
+ * Config problem
408
+ * @typedef Problem
409
+ * @property {string} err - Error message
410
+ * @property {any} val - Associated value
411
+ */
412
+
413
+ /**
414
+ * An optional logging function provided to an {@link ExtensionConfig} subclass.
415
+ * @callback ExtensionLogFn
416
+ * @param {...any} args
417
+ * @returns {void}
418
+ */
419
+
420
+ /**
421
+ * @typedef {import('./manifest').ExtensionType} ExtensionType
422
+ * @typedef {import('./manifest').Manifest} Manifest
423
+ */
424
+
425
+ /**
426
+ * @template T
427
+ * @typedef {import('./manifest').ExtData<T>} ExtData
428
+ */
429
+
430
+ /**
431
+ * @template T
432
+ * @typedef {import('./manifest').ExtDataWithSchema<T>} ExtDataWithSchema
433
+ */
434
+
435
+ /**
436
+ * @template T
437
+ * @typedef {import('./manifest').ExtName<T>} ExtName
438
+ */
439
+
440
+ /**
441
+ * @template T
442
+ * @typedef {import('./manifest').ExtClass<T>} ExtClass
443
+ */
444
+
445
+ /**
446
+ * @template T
447
+ * @typedef {import('./manifest').ExtRecord<T>} ExtRecord
448
+ */
449
+
450
+ /**
451
+ * Options for various methods in {@link ExtensionConfig}
452
+ * @typedef ExtensionConfigMutationOpts
453
+ * @property {boolean} [write=true] Whether or not to write the manifest to disk after a mutation operation
454
+ */
455
+
456
+ /**
457
+ * A valid install type
458
+ * @typedef {typeof INSTALL_TYPE_NPM | typeof INSTALL_TYPE_GIT | typeof INSTALL_TYPE_LOCAL | typeof INSTALL_TYPE_GITHUB} InstallType
459
+ */
@@ -0,0 +1,103 @@
1
+ // @ts-check
2
+
3
+ import _ from 'lodash';
4
+ import { USE_ALL_PLUGINS } from '../constants';
5
+ import log from '../logger';
6
+ import { DriverConfig } from './driver-config';
7
+ import { Manifest } from './manifest';
8
+ import { PluginConfig } from './plugin-config';
9
+
10
+ /**
11
+ * Loads extensions and creates `ExtensionConfig` instances.
12
+ *
13
+ * - Reads the manifest file, creating if necessary
14
+ * - Using the parsed extension data, creates/gets the `ExtensionConfig` subclass instances
15
+ * - Returns these instances
16
+ *
17
+ * If `appiumHome` is needed, use `resolveAppiumHome` from the `env` module in `@appium/support`.
18
+ * @param {string} appiumHome
19
+ * @returns {Promise<ExtensionConfigs>}
20
+ */
21
+ export async function loadExtensions (appiumHome) {
22
+ const manifest = Manifest.getInstance(appiumHome);
23
+ const {drivers, plugins} = await manifest.read();
24
+ const driverConfig =
25
+ DriverConfig.getInstance(manifest) ??
26
+ DriverConfig.create(manifest, {extData: drivers});
27
+ const pluginConfig =
28
+ PluginConfig.getInstance(manifest) ??
29
+ PluginConfig.create(manifest, {extData: plugins});
30
+ return {driverConfig, pluginConfig};
31
+ }
32
+
33
+ /**
34
+ * Find any plugin name which has been installed, and which has been requested for activation by
35
+ * using the --use-plugins flag, and turn each one into its class, so we can send them as objects
36
+ * to the server init. We also want to send/assign them to the umbrella driver so it can use them
37
+ * to wrap command execution
38
+ *
39
+ * @param {import('./plugin-config').PluginConfig} pluginConfig - a plugin extension config
40
+ * @param {string[]} usePlugins
41
+ * @returns {import('./manifest').PluginClass[]}
42
+ */
43
+ export function getActivePlugins (pluginConfig, usePlugins = []) {
44
+ return _.compact(
45
+ Object.keys(pluginConfig.installedExtensions)
46
+ .filter(
47
+ (pluginName) =>
48
+ _.includes(usePlugins, pluginName) ||
49
+ (usePlugins.length === 1 && usePlugins[0] === USE_ALL_PLUGINS),
50
+ )
51
+ .map((pluginName) => {
52
+ try {
53
+ log.info(`Attempting to load plugin ${pluginName}...`);
54
+ const PluginClass = pluginConfig.require(pluginName);
55
+
56
+ PluginClass.pluginName = pluginName; // store the plugin name on the class so it can be used later
57
+ return PluginClass;
58
+ } catch (err) {
59
+ log.error(
60
+ `Could not load plugin '${pluginName}', so it will not be available. Error ` +
61
+ `in loading the plugin was: ${err.message}`,
62
+ );
63
+ log.debug(err.stack);
64
+ }
65
+ }),
66
+ );
67
+ }
68
+
69
+ /**
70
+ * Find any driver name which has been installed, and turn each one into its class, so we can send
71
+ * them as objects to the server init in case they need to add methods/routes or update the server.
72
+ * If the --drivers flag was given, this method only loads the given drivers.
73
+ *
74
+ * @param {import('./driver-config').DriverConfig} driverConfig - a driver extension config
75
+ * @param {string[]} [useDrivers] - optional list of drivers to load
76
+ */
77
+ export function getActiveDrivers (driverConfig, useDrivers = []) {
78
+ return _.compact(
79
+ Object.keys(driverConfig.installedExtensions)
80
+ .filter(
81
+ (driverName) =>
82
+ _.includes(useDrivers, driverName) || useDrivers.length === 0,
83
+ )
84
+ .map((driverName) => {
85
+ try {
86
+ log.info(`Attempting to load driver ${driverName}...`);
87
+ return driverConfig.require(driverName);
88
+ } catch (err) {
89
+ log.error(
90
+ `Could not load driver '${driverName}', so it will not be available. Error ` +
91
+ `in loading the driver was: ${err.message}`,
92
+ );
93
+ log.debug(err.stack);
94
+ }
95
+ }),
96
+ );
97
+ }
98
+
99
+ /**
100
+ * @typedef ExtensionConfigs
101
+ * @property {DriverConfig} driverConfig
102
+ * @property {PluginConfig} pluginConfig
103
+ */