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,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
+ */