appium 2.0.0-beta.46 → 2.0.0-beta.48

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 (141) hide show
  1. package/README.md +145 -44
  2. package/build/lib/appium.d.ts +3 -103
  3. package/build/lib/appium.d.ts.map +1 -1
  4. package/build/lib/appium.js +679 -549
  5. package/build/lib/appium.js.map +1 -1
  6. package/build/lib/cli/args.js +247 -127
  7. package/build/lib/cli/args.js.map +1 -1
  8. package/build/lib/cli/driver-command.d.ts +24 -5
  9. package/build/lib/cli/driver-command.d.ts.map +1 -1
  10. package/build/lib/cli/driver-command.js +78 -88
  11. package/build/lib/cli/driver-command.js.map +1 -1
  12. package/build/lib/cli/extension-command.d.ts +33 -24
  13. package/build/lib/cli/extension-command.d.ts.map +1 -1
  14. package/build/lib/cli/extension-command.js +729 -512
  15. package/build/lib/cli/extension-command.js.map +1 -1
  16. package/build/lib/cli/extension.d.ts +7 -6
  17. package/build/lib/cli/extension.d.ts.map +1 -1
  18. package/build/lib/cli/extension.js +68 -65
  19. package/build/lib/cli/extension.js.map +1 -1
  20. package/build/lib/cli/parser.d.ts +3 -3
  21. package/build/lib/cli/parser.d.ts.map +1 -1
  22. package/build/lib/cli/parser.js +234 -192
  23. package/build/lib/cli/parser.js.map +1 -1
  24. package/build/lib/cli/plugin-command.js +58 -87
  25. package/build/lib/cli/plugin-command.js.map +1 -1
  26. package/build/lib/cli/utils.js +66 -69
  27. package/build/lib/cli/utils.js.map +1 -1
  28. package/build/lib/config-file.d.ts.map +1 -1
  29. package/build/lib/config-file.js +189 -120
  30. package/build/lib/config-file.js.map +1 -1
  31. package/build/lib/config.d.ts.map +1 -1
  32. package/build/lib/config.js +254 -213
  33. package/build/lib/config.js.map +1 -1
  34. package/build/lib/constants.d.ts +6 -5
  35. package/build/lib/constants.d.ts.map +1 -1
  36. package/build/lib/constants.js +65 -59
  37. package/build/lib/constants.js.map +1 -1
  38. package/build/lib/extension/driver-config.js +199 -164
  39. package/build/lib/extension/driver-config.js.map +1 -1
  40. package/build/lib/extension/extension-config.d.ts +33 -26
  41. package/build/lib/extension/extension-config.d.ts.map +1 -1
  42. package/build/lib/extension/extension-config.js +541 -396
  43. package/build/lib/extension/extension-config.js.map +1 -1
  44. package/build/lib/extension/index.js +98 -68
  45. package/build/lib/extension/index.js.map +1 -1
  46. package/build/lib/extension/manifest-migrations.d.ts +27 -0
  47. package/build/lib/extension/manifest-migrations.d.ts.map +1 -0
  48. package/build/lib/extension/manifest-migrations.js +118 -0
  49. package/build/lib/extension/manifest-migrations.js.map +1 -0
  50. package/build/lib/extension/manifest.d.ts +35 -63
  51. package/build/lib/extension/manifest.d.ts.map +1 -1
  52. package/build/lib/extension/manifest.js +500 -240
  53. package/build/lib/extension/manifest.js.map +1 -1
  54. package/build/lib/extension/package-changed.js +57 -61
  55. package/build/lib/extension/package-changed.js.map +1 -1
  56. package/build/lib/extension/plugin-config.d.ts +2 -3
  57. package/build/lib/extension/plugin-config.d.ts.map +1 -1
  58. package/build/lib/extension/plugin-config.js +94 -70
  59. package/build/lib/extension/plugin-config.js.map +1 -1
  60. package/build/lib/grid-register.js +119 -137
  61. package/build/lib/grid-register.js.map +1 -1
  62. package/build/lib/logger.d.ts +1 -1
  63. package/build/lib/logger.d.ts.map +1 -1
  64. package/build/lib/logger.js +5 -15
  65. package/build/lib/logger.js.map +1 -1
  66. package/build/lib/logsink.d.ts.map +1 -1
  67. package/build/lib/logsink.js +189 -183
  68. package/build/lib/logsink.js.map +1 -1
  69. package/build/lib/main.d.ts +19 -12
  70. package/build/lib/main.d.ts.map +1 -1
  71. package/build/lib/main.js +330 -304
  72. package/build/lib/main.js.map +1 -1
  73. package/build/lib/schema/arg-spec.js +153 -108
  74. package/build/lib/schema/arg-spec.js.map +1 -1
  75. package/build/lib/schema/cli-args.js +203 -164
  76. package/build/lib/schema/cli-args.js.map +1 -1
  77. package/build/lib/schema/cli-transformers.js +117 -72
  78. package/build/lib/schema/cli-transformers.js.map +1 -1
  79. package/build/lib/schema/index.js +17 -32
  80. package/build/lib/schema/index.js.map +1 -1
  81. package/build/lib/schema/keywords.js +125 -67
  82. package/build/lib/schema/keywords.js.map +1 -1
  83. package/build/lib/schema/schema.d.ts.map +1 -1
  84. package/build/lib/schema/schema.js +582 -417
  85. package/build/lib/schema/schema.js.map +1 -1
  86. package/build/lib/utils.d.ts +41 -255
  87. package/build/lib/utils.d.ts.map +1 -1
  88. package/build/lib/utils.js +342 -193
  89. package/build/lib/utils.js.map +1 -1
  90. package/build/tsconfig.tsbuildinfo +1 -1
  91. package/build/types/cli.d.ts +45 -34
  92. package/build/types/cli.d.ts.map +1 -1
  93. package/build/types/cli.js +3 -0
  94. package/build/types/cli.js.map +1 -0
  95. package/build/types/index.d.ts +1 -2
  96. package/build/types/index.d.ts.map +1 -1
  97. package/build/types/index.js +19 -0
  98. package/build/types/index.js.map +1 -0
  99. package/build/types/manifest/base.d.ts +135 -0
  100. package/build/types/manifest/base.d.ts.map +1 -0
  101. package/build/types/manifest/base.js +3 -0
  102. package/build/types/manifest/base.js.map +1 -0
  103. package/build/types/manifest/index.d.ts +19 -0
  104. package/build/types/manifest/index.d.ts.map +1 -0
  105. package/build/types/manifest/index.js +40 -0
  106. package/build/types/manifest/index.js.map +1 -0
  107. package/build/types/manifest/v3.d.ts +139 -0
  108. package/build/types/manifest/v3.d.ts.map +1 -0
  109. package/build/types/manifest/v3.js +3 -0
  110. package/build/types/manifest/v3.js.map +1 -0
  111. package/lib/appium.js +1 -1
  112. package/lib/cli/args.js +1 -1
  113. package/lib/cli/driver-command.js +17 -0
  114. package/lib/cli/extension-command.js +119 -65
  115. package/lib/cli/extension.js +9 -8
  116. package/lib/cli/parser.js +2 -2
  117. package/lib/config-file.js +2 -3
  118. package/lib/config.js +3 -2
  119. package/lib/constants.js +7 -5
  120. package/lib/extension/extension-config.js +52 -47
  121. package/lib/extension/manifest-migrations.js +120 -0
  122. package/lib/extension/manifest.js +184 -103
  123. package/lib/extension/plugin-config.js +1 -2
  124. package/lib/logsink.js +26 -5
  125. package/lib/main.js +58 -50
  126. package/lib/schema/schema.js +6 -1
  127. package/lib/utils.js +62 -0
  128. package/package.json +24 -25
  129. package/scripts/autoinstall-extensions.js +78 -26
  130. package/types/cli.ts +81 -42
  131. package/types/index.ts +1 -2
  132. package/types/manifest/README.md +30 -0
  133. package/types/manifest/base.ts +158 -0
  134. package/types/manifest/index.ts +27 -0
  135. package/types/manifest/v3.ts +161 -0
  136. package/build/types/appium-manifest.d.ts +0 -59
  137. package/build/types/appium-manifest.d.ts.map +0 -1
  138. package/build/types/extension-manifest.d.ts +0 -55
  139. package/build/types/extension-manifest.d.ts.map +0 -1
  140. package/types/appium-manifest.ts +0 -73
  141. package/types/extension-manifest.ts +0 -64
@@ -33,13 +33,18 @@ const INSTALL_TYPES = new Set([
33
33
  * @template {ExtensionType} ExtType
34
34
  */
35
35
  export class ExtensionConfig {
36
- /** @type {ExtType} */
36
+ /**
37
+ * The type of extension this class is responsible for.
38
+ * @type {ExtType}
39
+ */
37
40
  extensionType;
38
41
 
39
- /** @type {`${ExtType}s`} */
40
- configKey;
41
-
42
- /** @type {ExtRecord<ExtType>} */
42
+ /**
43
+ * Manifest data for the extensions of this type.
44
+ *
45
+ * This data should _not_ be written to by anything but {@linkcode Manifest}.
46
+ * @type {Readonly<ExtRecord<ExtType>>}
47
+ */
43
48
  installedExtensions;
44
49
 
45
50
  /** @type {import('@appium/types').AppiumLogger} */
@@ -49,9 +54,9 @@ export class ExtensionConfig {
49
54
  manifest;
50
55
 
51
56
  /**
52
- * @type {ExtensionListData}
57
+ * @type {ExtensionListData|undefined}
53
58
  */
54
- _listDataCache;
59
+ #listDataCache;
55
60
 
56
61
  /**
57
62
  * @protected
@@ -60,7 +65,6 @@ export class ExtensionConfig {
60
65
  */
61
66
  constructor(extensionType, manifest) {
62
67
  this.extensionType = extensionType;
63
- this.configKey = `${extensionType}s`;
64
68
  this.installedExtensions = manifest.getExtensionData(extensionType);
65
69
  this.manifest = manifest;
66
70
  }
@@ -204,8 +208,8 @@ export class ExtensionConfig {
204
208
  if (!_.isEmpty(errorSummaries)) {
205
209
  log.error(
206
210
  `Appium encountered ${util.pluralize('error', errorMap.size, true)} while validating ${
207
- this.configKey
208
- } found in manifest ${this.manifestPath}`
211
+ this.extensionType
212
+ }s found in manifest ${this.manifestPath}`
209
213
  );
210
214
  for (const summary of errorSummaries) {
211
215
  log.error(summary);
@@ -219,7 +223,7 @@ export class ExtensionConfig {
219
223
  'warning',
220
224
  warningMap.size,
221
225
  true
222
- )} while validating ${this.configKey} found in manifest ${this.manifestPath}`
226
+ )} while validating ${this.extensionType}s found in manifest ${this.manifestPath}`
223
227
  );
224
228
  for (const summary of warningSummaries) {
225
229
  log.warn(summary);
@@ -231,18 +235,20 @@ export class ExtensionConfig {
231
235
 
232
236
  /**
233
237
  * Retrieves listing data for extensions via command class.
234
- * Caches the result in {@linkcode ExtensionConfig._listDataCache}
238
+ *
239
+ * This is an expensive operation, so the result is cached. Currently, there is no
240
+ * use case for invalidating the cache.
235
241
  * @protected
236
242
  * @returns {Promise<ExtensionListData>}
237
243
  */
238
244
  async getListData() {
239
- if (this._listDataCache) {
240
- return this._listDataCache;
245
+ if (this.#listDataCache) {
246
+ return this.#listDataCache;
241
247
  }
242
248
  const CommandClass = /** @type {ExtCommand<ExtType>} */ (commandClasses[this.extensionType]);
243
249
  const cmd = new CommandClass({config: this, json: true});
244
250
  const listData = await cmd.list({showInstalled: true, showUpdates: true});
245
- this._listDataCache = listData;
251
+ this.#listDataCache = listData;
246
252
  return listData;
247
253
  }
248
254
 
@@ -433,7 +439,7 @@ export class ExtensionConfig {
433
439
  * @returns {Promise<void>}
434
440
  */
435
441
  async addExtension(extName, extManifest, {write = true} = {}) {
436
- this.manifest.addExtension(this.extensionType, extName, extManifest);
442
+ this.manifest.setExtension(this.extensionType, extName, extManifest);
437
443
  if (write) {
438
444
  await this.manifest.write();
439
445
  }
@@ -441,15 +447,15 @@ export class ExtensionConfig {
441
447
 
442
448
  /**
443
449
  * @param {ExtName<ExtType>} extName
444
- * @param {ExtManifest<ExtType>|import('../cli/extension-command').ExtensionFields<ExtType>} extManifest
450
+ * @param {ExtManifest<ExtType>} extManifest
445
451
  * @param {ExtensionConfigMutationOpts} [opts]
446
452
  * @returns {Promise<void>}
447
453
  */
448
454
  async updateExtension(extName, extManifest, {write = true} = {}) {
449
- this.installedExtensions[extName] = {
455
+ this.manifest.setExtension(this.extensionType, extName, {
450
456
  ...this.installedExtensions[extName],
451
457
  ...extManifest,
452
- };
458
+ });
453
459
  if (write) {
454
460
  await this.manifest.write();
455
461
  }
@@ -463,7 +469,7 @@ export class ExtensionConfig {
463
469
  * @returns {Promise<void>}
464
470
  */
465
471
  async removeExtension(extName, {write = true} = {}) {
466
- delete this.installedExtensions[extName];
472
+ this.manifest.deleteExtension(this.extensionType, extName);
467
473
  if (write) {
468
474
  await this.manifest.write();
469
475
  }
@@ -477,13 +483,13 @@ export class ExtensionConfig {
477
483
  print(activeNames) {
478
484
  if (_.isEmpty(this.installedExtensions)) {
479
485
  log.info(
480
- `No ${this.configKey} have been installed in ${this.appiumHome}. Use the "appium ${this.extensionType}" ` +
486
+ `No ${this.extensionType}s have been installed in ${this.appiumHome}. Use the "appium ${this.extensionType}" ` +
481
487
  'command to install the one(s) you want to use.'
482
488
  );
483
489
  return;
484
490
  }
485
491
 
486
- log.info(`Available ${this.configKey}:`);
492
+ log.info(`Available ${this.extensionType}s:`);
487
493
  for (const [extName, extManifest] of /** @type {[string, ExtManifest<ExtType>][]} */ (
488
494
  _.toPairs(this.installedExtensions)
489
495
  )) {
@@ -504,11 +510,17 @@ export class ExtensionConfig {
504
510
  }
505
511
 
506
512
  /**
507
- * @param {string} extName
513
+ * Returns--with reasonable accuracy--the path on disk to the extension.
514
+ *
515
+ * If `installPath` is present in the manifest, then it is used; otherwise we just guess.
516
+ * @param {keyof typeof this.installedExtensions} extName
508
517
  * @returns {string}
509
518
  */
510
519
  getInstallPath(extName) {
511
- return path.join(this.appiumHome, 'node_modules', this.installedExtensions[extName].pkgName);
520
+ return (
521
+ this.installedExtensions[extName]?.installPath ??
522
+ path.join(this.appiumHome, 'node_modules', this.installedExtensions[extName].pkgName)
523
+ );
512
524
  }
513
525
 
514
526
  /**
@@ -546,7 +558,7 @@ export class ExtensionConfig {
546
558
  * @returns {boolean}
547
559
  */
548
560
  isInstalled(extName) {
549
- return _.includes(Object.keys(this.installedExtensions), extName);
561
+ return extName in this.installedExtensions;
550
562
  }
551
563
 
552
564
  /**
@@ -628,36 +640,39 @@ export {INSTALL_TYPE_NPM, INSTALL_TYPE_GIT, INSTALL_TYPE_LOCAL, INSTALL_TYPE_GIT
628
640
  /**
629
641
  * @typedef {import('@appium/types').ExtensionType} ExtensionType
630
642
  * @typedef {import('./manifest').Manifest} Manifest
643
+ * @typedef {import('../cli/extension-command').ExtensionListData} ExtensionListData
644
+ * @typedef {import('../cli/extension-command').InstalledExtensionListData} InstalledExtensionListData
645
+ * @typedef {import('appium/types').InstallType} InstallType
631
646
  */
632
647
 
633
648
  /**
634
- * @template T
635
- * @typedef {import('appium/types').ExtManifest<T>} ExtManifest
649
+ * @template {ExtensionType} ExtType
650
+ * @typedef {import('appium/types').ExtManifest<ExtType>} ExtManifest
636
651
  */
637
652
 
638
653
  /**
639
- * @template T
640
- * @typedef {import('appium/types').ExtManifestWithSchema<T>} ExtManifestWithSchema
654
+ * @template {ExtensionType} ExtType
655
+ * @typedef {ExtManifest<ExtType> & {schema: NonNullable<ExtManifest<ExtType>['schema']>}} ExtManifestWithSchema
641
656
  */
642
657
 
643
658
  /**
644
- * @template T
645
- * @typedef {import('appium/types').ExtName<T>} ExtName
659
+ * @template {ExtensionType} ExtType
660
+ * @typedef {import('appium/types').ExtName<ExtType>} ExtName
646
661
  */
647
662
 
648
663
  /**
649
- * @template T
650
- * @typedef {import('appium/types').ExtClass<T>} ExtClass
664
+ * @template {ExtensionType} ExtType
665
+ * @typedef {import('appium/types').ExtClass<ExtType>} ExtClass
651
666
  */
652
667
 
653
668
  /**
654
- * @template T
655
- * @typedef {import('appium/types').ExtRecord<T>} ExtRecord
669
+ * @template {ExtensionType} ExtType
670
+ * @typedef {import('appium/types').ExtRecord<ExtType>} ExtRecord
656
671
  */
657
672
 
658
673
  /**
659
- * @template T
660
- * @typedef {import('../cli/extension').ExtCommand<T>} ExtCommand
674
+ * @template {ExtensionType} ExtType
675
+ * @typedef {import('../cli/extension').ExtCommand<ExtType>} ExtCommand
661
676
  */
662
677
 
663
678
  /**
@@ -665,13 +680,3 @@ export {INSTALL_TYPE_NPM, INSTALL_TYPE_GIT, INSTALL_TYPE_LOCAL, INSTALL_TYPE_GIT
665
680
  * @typedef ExtensionConfigMutationOpts
666
681
  * @property {boolean} [write=true] Whether or not to write the manifest to disk after a mutation operation
667
682
  */
668
-
669
- /**
670
- * A valid install type
671
- * @typedef {typeof INSTALL_TYPE_NPM | typeof INSTALL_TYPE_GIT | typeof INSTALL_TYPE_LOCAL | typeof INSTALL_TYPE_GITHUB} InstallType
672
- */
673
-
674
- /**
675
- * @typedef {import('../cli/extension-command').ExtensionListData} ExtensionListData
676
- * @typedef {import('../cli/extension-command').InstalledExtensionListData} InstalledExtensionListData
677
- */
@@ -0,0 +1,120 @@
1
+ import {DRIVER_TYPE, PLUGIN_TYPE} from '../constants';
2
+ import log from '../logger';
3
+
4
+ /**
5
+ * This contains logic to migrate old versions of `extensions.yaml` to new versions.
6
+ *
7
+ * @module
8
+ */
9
+
10
+ /**
11
+ * Constant for v3 of the schema rev.
12
+ */
13
+ const SCHEMA_REV_3 = 3;
14
+
15
+ /**
16
+ * Collection of functions to migrate from one version to another.
17
+ *
18
+ * These functions _should not actually perform the migration_; rather, they
19
+ * should return `true` if the migration should be performed. The migration
20
+ * itself will happen within {@linkcode Manifest.syncWithInstalledExtensions}; the extensions
21
+ * will be checked and the manifest file updated per the state of the filesystem.
22
+ *
23
+ * @type {{[P in keyof ManifestDataVersions]?: Migration}}
24
+ */
25
+ const Migrations = {
26
+ /**
27
+ * If `installPath` is missing from the `ExtManifest` for any extension, return `true`.
28
+ *
29
+ * We cannot easily determine the path of any given extension here, but returning `true`
30
+ * will cause the `Manifest` to sync with the filesystem, which will populate the missing field.
31
+ *
32
+ * @type {Migration}
33
+ */
34
+ [SCHEMA_REV_3]: (manifest) => {
35
+ let shouldSync = false;
36
+ /** @type {Array<ExtManifest<PluginType>|ExtManifest<DriverType>>} */
37
+ const allExtData = [
38
+ ...Object.values(manifest.getExtensionData(DRIVER_TYPE)),
39
+ ...Object.values(manifest.getExtensionData(PLUGIN_TYPE)),
40
+ ];
41
+ for (const metadata of allExtData) {
42
+ if (!('installPath' in metadata)) {
43
+ shouldSync = true;
44
+ break;
45
+ }
46
+ }
47
+ return shouldSync;
48
+ },
49
+ };
50
+
51
+ /**
52
+ * Set `schemaRev` to a specific version.
53
+ *
54
+ * This _does_ mutate `data` in-place, unlike functions in
55
+ * {@linkcode Migrations}.
56
+ *
57
+ * Again, returning `true` means that the manifest should be--at
58
+ * minimum--persisted to disk, since we changed it.
59
+ * @param {Readonly<Manifest>} manifest
60
+ * @param {keyof ManifestDataVersions} version
61
+ * @returns {boolean} Whether the data was modified
62
+ */
63
+ function setSchemaRev(manifest, version) {
64
+ if (manifest.schemaRev ?? 0 < version) {
65
+ manifest.setSchemaRev(version);
66
+ return true;
67
+ }
68
+ return false;
69
+ }
70
+
71
+ /**
72
+ * Applies a series of migration functions to a manifest to update its manifest schema version.
73
+ *
74
+ * `data` is modified in-place.
75
+ *
76
+ * @param {Readonly<Manifest>} manifest
77
+ * @returns {Promise<boolean>} If `true` existing packages should be synced from disk and the manifest should be persisted.
78
+ */
79
+ export async function migrate(manifest) {
80
+ let didChange = false;
81
+ for await (const [v, migration] of Object.entries(Migrations)) {
82
+ const version = /** @type {keyof ManifestDataVersions} */ (Number(v));
83
+ didChange = (await migration(manifest)) || didChange;
84
+ didChange = setSchemaRev(manifest, version) || didChange;
85
+ }
86
+
87
+ if (didChange) {
88
+ // this is not _technically_ true, since we don't actually write the file here.
89
+ log.info(`Upgraded extension manifest to schema v${manifest.schemaRev}`);
90
+ }
91
+
92
+ return didChange;
93
+ }
94
+
95
+ /**
96
+ * A migration function. It will return `true` if a change _should be made_.
97
+ *
98
+ * A migration function should not modify `schemaRev`, as this is done automatically.
99
+ *
100
+ * Note that at the time of writing, we're not able to determine the version of the _current_ manifest file if there is no `schemaRev` prop present (and we may not want to trust it anyway).
101
+ * @callback Migration
102
+ * @param {Readonly<Manifest>} manifest - The `Manifest` instance
103
+ * @returns {boolean|Promise<boolean>} If `true`, then something changed
104
+ */
105
+
106
+ /**
107
+ * @typedef {import('appium/types').ManifestData} ManifestData
108
+ * @typedef {import('@appium/types').DriverType} DriverType
109
+ * @typedef {import('@appium/types').PluginType} PluginType
110
+ * @typedef {import('appium/types').AnyManifestDataVersion} AnyManifestDataVersion
111
+ * @typedef {import('appium/types').ManifestDataVersions} ManifestDataVersions
112
+ * @typedef {import('@appium/types').ExtensionType} ExtensionType
113
+ * @typedef {import('appium/types').ManifestV2.ManifestData} ManifestDataV2
114
+ * @typedef {import('./manifest').Manifest} Manifest
115
+ */
116
+
117
+ /**
118
+ * @template {ExtensionType} ExtType
119
+ * @typedef {import('appium/types').ExtManifest<ExtType>} ExtManifest
120
+ */