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.
- package/README.md +145 -44
- package/build/lib/appium.d.ts +3 -103
- package/build/lib/appium.d.ts.map +1 -1
- package/build/lib/appium.js +679 -549
- package/build/lib/appium.js.map +1 -1
- package/build/lib/cli/args.js +247 -127
- package/build/lib/cli/args.js.map +1 -1
- package/build/lib/cli/driver-command.d.ts +24 -5
- package/build/lib/cli/driver-command.d.ts.map +1 -1
- package/build/lib/cli/driver-command.js +78 -88
- package/build/lib/cli/driver-command.js.map +1 -1
- package/build/lib/cli/extension-command.d.ts +33 -24
- package/build/lib/cli/extension-command.d.ts.map +1 -1
- package/build/lib/cli/extension-command.js +729 -512
- package/build/lib/cli/extension-command.js.map +1 -1
- package/build/lib/cli/extension.d.ts +7 -6
- package/build/lib/cli/extension.d.ts.map +1 -1
- package/build/lib/cli/extension.js +68 -65
- package/build/lib/cli/extension.js.map +1 -1
- package/build/lib/cli/parser.d.ts +3 -3
- package/build/lib/cli/parser.d.ts.map +1 -1
- package/build/lib/cli/parser.js +234 -192
- package/build/lib/cli/parser.js.map +1 -1
- package/build/lib/cli/plugin-command.js +58 -87
- package/build/lib/cli/plugin-command.js.map +1 -1
- package/build/lib/cli/utils.js +66 -69
- package/build/lib/cli/utils.js.map +1 -1
- package/build/lib/config-file.d.ts.map +1 -1
- package/build/lib/config-file.js +189 -120
- package/build/lib/config-file.js.map +1 -1
- package/build/lib/config.d.ts.map +1 -1
- package/build/lib/config.js +254 -213
- package/build/lib/config.js.map +1 -1
- package/build/lib/constants.d.ts +6 -5
- package/build/lib/constants.d.ts.map +1 -1
- package/build/lib/constants.js +65 -59
- package/build/lib/constants.js.map +1 -1
- package/build/lib/extension/driver-config.js +199 -164
- package/build/lib/extension/driver-config.js.map +1 -1
- package/build/lib/extension/extension-config.d.ts +33 -26
- package/build/lib/extension/extension-config.d.ts.map +1 -1
- package/build/lib/extension/extension-config.js +541 -396
- package/build/lib/extension/extension-config.js.map +1 -1
- package/build/lib/extension/index.js +98 -68
- package/build/lib/extension/index.js.map +1 -1
- package/build/lib/extension/manifest-migrations.d.ts +27 -0
- package/build/lib/extension/manifest-migrations.d.ts.map +1 -0
- package/build/lib/extension/manifest-migrations.js +118 -0
- package/build/lib/extension/manifest-migrations.js.map +1 -0
- package/build/lib/extension/manifest.d.ts +35 -63
- package/build/lib/extension/manifest.d.ts.map +1 -1
- package/build/lib/extension/manifest.js +500 -240
- package/build/lib/extension/manifest.js.map +1 -1
- package/build/lib/extension/package-changed.js +57 -61
- package/build/lib/extension/package-changed.js.map +1 -1
- package/build/lib/extension/plugin-config.d.ts +2 -3
- package/build/lib/extension/plugin-config.d.ts.map +1 -1
- package/build/lib/extension/plugin-config.js +94 -70
- package/build/lib/extension/plugin-config.js.map +1 -1
- package/build/lib/grid-register.js +119 -137
- package/build/lib/grid-register.js.map +1 -1
- package/build/lib/logger.d.ts +1 -1
- package/build/lib/logger.d.ts.map +1 -1
- package/build/lib/logger.js +5 -15
- package/build/lib/logger.js.map +1 -1
- package/build/lib/logsink.d.ts.map +1 -1
- package/build/lib/logsink.js +189 -183
- package/build/lib/logsink.js.map +1 -1
- package/build/lib/main.d.ts +19 -12
- package/build/lib/main.d.ts.map +1 -1
- package/build/lib/main.js +330 -304
- package/build/lib/main.js.map +1 -1
- package/build/lib/schema/arg-spec.js +153 -108
- package/build/lib/schema/arg-spec.js.map +1 -1
- package/build/lib/schema/cli-args.js +203 -164
- package/build/lib/schema/cli-args.js.map +1 -1
- package/build/lib/schema/cli-transformers.js +117 -72
- package/build/lib/schema/cli-transformers.js.map +1 -1
- package/build/lib/schema/index.js +17 -32
- package/build/lib/schema/index.js.map +1 -1
- package/build/lib/schema/keywords.js +125 -67
- package/build/lib/schema/keywords.js.map +1 -1
- package/build/lib/schema/schema.d.ts.map +1 -1
- package/build/lib/schema/schema.js +582 -417
- package/build/lib/schema/schema.js.map +1 -1
- package/build/lib/utils.d.ts +41 -255
- package/build/lib/utils.d.ts.map +1 -1
- package/build/lib/utils.js +342 -193
- package/build/lib/utils.js.map +1 -1
- package/build/tsconfig.tsbuildinfo +1 -1
- package/build/types/cli.d.ts +45 -34
- package/build/types/cli.d.ts.map +1 -1
- package/build/types/cli.js +3 -0
- package/build/types/cli.js.map +1 -0
- package/build/types/index.d.ts +1 -2
- package/build/types/index.d.ts.map +1 -1
- package/build/types/index.js +19 -0
- package/build/types/index.js.map +1 -0
- package/build/types/manifest/base.d.ts +135 -0
- package/build/types/manifest/base.d.ts.map +1 -0
- package/build/types/manifest/base.js +3 -0
- package/build/types/manifest/base.js.map +1 -0
- package/build/types/manifest/index.d.ts +19 -0
- package/build/types/manifest/index.d.ts.map +1 -0
- package/build/types/manifest/index.js +40 -0
- package/build/types/manifest/index.js.map +1 -0
- package/build/types/manifest/v3.d.ts +139 -0
- package/build/types/manifest/v3.d.ts.map +1 -0
- package/build/types/manifest/v3.js +3 -0
- package/build/types/manifest/v3.js.map +1 -0
- package/lib/appium.js +1 -1
- package/lib/cli/args.js +1 -1
- package/lib/cli/driver-command.js +17 -0
- package/lib/cli/extension-command.js +119 -65
- package/lib/cli/extension.js +9 -8
- package/lib/cli/parser.js +2 -2
- package/lib/config-file.js +2 -3
- package/lib/config.js +3 -2
- package/lib/constants.js +7 -5
- package/lib/extension/extension-config.js +52 -47
- package/lib/extension/manifest-migrations.js +120 -0
- package/lib/extension/manifest.js +184 -103
- package/lib/extension/plugin-config.js +1 -2
- package/lib/logsink.js +26 -5
- package/lib/main.js +58 -50
- package/lib/schema/schema.js +6 -1
- package/lib/utils.js +62 -0
- package/package.json +24 -25
- package/scripts/autoinstall-extensions.js +78 -26
- package/types/cli.ts +81 -42
- package/types/index.ts +1 -2
- package/types/manifest/README.md +30 -0
- package/types/manifest/base.ts +158 -0
- package/types/manifest/index.ts +27 -0
- package/types/manifest/v3.ts +161 -0
- package/build/types/appium-manifest.d.ts +0 -59
- package/build/types/appium-manifest.d.ts.map +0 -1
- package/build/types/extension-manifest.d.ts +0 -55
- package/build/types/extension-manifest.d.ts.map +0 -1
- package/types/appium-manifest.ts +0 -73
- package/types/extension-manifest.ts +0 -64
|
@@ -8,15 +8,11 @@ import {env, fs} from '@appium/support';
|
|
|
8
8
|
import _ from 'lodash';
|
|
9
9
|
import path from 'path';
|
|
10
10
|
import YAML from 'yaml';
|
|
11
|
-
import {DRIVER_TYPE, PLUGIN_TYPE} from '../constants';
|
|
11
|
+
import {CURRENT_SCHEMA_REV, DRIVER_TYPE, PLUGIN_TYPE} from '../constants';
|
|
12
12
|
import log from '../logger';
|
|
13
13
|
import {INSTALL_TYPE_NPM} from './extension-config';
|
|
14
14
|
import {packageDidChange} from './package-changed';
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Current configuration schema revision!
|
|
18
|
-
*/
|
|
19
|
-
const CONFIG_SCHEMA_REV = 2;
|
|
15
|
+
import {migrate} from './manifest-migrations';
|
|
20
16
|
|
|
21
17
|
/**
|
|
22
18
|
* The name of the prop (`drivers`) used in `extensions.yaml` for drivers.
|
|
@@ -36,12 +32,14 @@ const CONFIG_DATA_PLUGIN_KEY = `${PLUGIN_TYPE}s`;
|
|
|
36
32
|
const INITIAL_MANIFEST_DATA = Object.freeze({
|
|
37
33
|
[CONFIG_DATA_DRIVER_KEY]: Object.freeze({}),
|
|
38
34
|
[CONFIG_DATA_PLUGIN_KEY]: Object.freeze({}),
|
|
39
|
-
schemaRev:
|
|
35
|
+
schemaRev: CURRENT_SCHEMA_REV,
|
|
40
36
|
});
|
|
41
37
|
|
|
42
38
|
/**
|
|
43
39
|
* Given a `package.json` return `true` if it represents an Appium Extension (either a driver or plugin).
|
|
44
40
|
*
|
|
41
|
+
* _This is a type guard; not a validator._
|
|
42
|
+
*
|
|
45
43
|
* The `package.json` must have an `appium` property which is an object.
|
|
46
44
|
* @param {any} value
|
|
47
45
|
* @returns {value is ExtPackageJson<ExtensionType>}
|
|
@@ -54,32 +52,35 @@ function isExtension(value) {
|
|
|
54
52
|
_.isString(value.version)
|
|
55
53
|
);
|
|
56
54
|
}
|
|
55
|
+
|
|
57
56
|
/**
|
|
58
57
|
* Given a `package.json`, return `true` if it represents an Appium Driver.
|
|
59
58
|
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
59
|
+
* _This is a type guard; not a validator._
|
|
60
|
+
*
|
|
61
|
+
* To be considered a driver, a `package.json` must have an `appium.driverName` field.
|
|
62
|
+
*
|
|
63
|
+
* Further validation of the `appium` property happens elsewhere.
|
|
62
64
|
* @param {any} value - Value to test
|
|
63
65
|
* @returns {value is ExtPackageJson<DriverType>}
|
|
64
66
|
*/
|
|
65
67
|
function isDriver(value) {
|
|
66
|
-
return (
|
|
67
|
-
isExtension(value) &&
|
|
68
|
-
_.isString(_.get(value, 'appium.driverName')) &&
|
|
69
|
-
_.isString(_.get(value, 'appium.automationName')) &&
|
|
70
|
-
_.isArray(_.get(value, 'appium.platformNames'))
|
|
71
|
-
);
|
|
68
|
+
return isExtension(value) && 'driverName' in value.appium && _.isString(value.appium.driverName);
|
|
72
69
|
}
|
|
73
70
|
|
|
74
71
|
/**
|
|
75
72
|
* Given a `package.json`, return `true` if it represents an Appium Plugin.
|
|
76
73
|
*
|
|
74
|
+
* _This is a type guard; not a validator._
|
|
75
|
+
*
|
|
77
76
|
* To be considered a plugin, a `package.json` must have an `appium.pluginName` field.
|
|
77
|
+
*
|
|
78
|
+
* Further validation of the `appium` property happens elsewhere.
|
|
78
79
|
* @param {any} value - Value to test
|
|
79
80
|
* @returns {value is ExtPackageJson<PluginType>}
|
|
80
81
|
*/
|
|
81
82
|
function isPlugin(value) {
|
|
82
|
-
return isExtension(value) && _.isString(
|
|
83
|
+
return isExtension(value) && 'pluginName' in value.appium && _.isString(value.appium.pluginName);
|
|
83
84
|
}
|
|
84
85
|
|
|
85
86
|
/**
|
|
@@ -93,23 +94,21 @@ export class Manifest {
|
|
|
93
94
|
*
|
|
94
95
|
* Contains proxies for automatic persistence on disk
|
|
95
96
|
* @type {ManifestData}
|
|
96
|
-
* @private
|
|
97
97
|
*/
|
|
98
|
-
|
|
98
|
+
#data;
|
|
99
99
|
|
|
100
100
|
/**
|
|
101
101
|
* Path to `APPIUM_HOME`.
|
|
102
|
-
* @private
|
|
103
102
|
* @type {Readonly<string>}
|
|
104
103
|
*/
|
|
105
|
-
|
|
104
|
+
#appiumHome;
|
|
106
105
|
|
|
107
106
|
/**
|
|
108
107
|
* Path to `extensions.yaml`
|
|
109
108
|
* @type {string}
|
|
110
109
|
* Not set until {@link Manifest.read} is called.
|
|
111
110
|
*/
|
|
112
|
-
|
|
111
|
+
#manifestPath;
|
|
113
112
|
|
|
114
113
|
/**
|
|
115
114
|
* Helps avoid writing multiple times.
|
|
@@ -118,10 +117,9 @@ export class Manifest {
|
|
|
118
117
|
* set to a `Promise`. When the call to `write()` is complete, the `Promise`
|
|
119
118
|
* will resolve and then this value will be set to `undefined`. Concurrent calls
|
|
120
119
|
* made while this value is a `Promise` will return the `Promise` itself.
|
|
121
|
-
* @private
|
|
122
120
|
* @type {Promise<boolean>|undefined}
|
|
123
121
|
*/
|
|
124
|
-
|
|
122
|
+
#writing;
|
|
125
123
|
|
|
126
124
|
/**
|
|
127
125
|
* Helps avoid reading multiple times.
|
|
@@ -130,10 +128,9 @@ export class Manifest {
|
|
|
130
128
|
* set to a `Promise`. When the call to `read()` is complete, the `Promise`
|
|
131
129
|
* will resolve and then this value will be set to `undefined`. Concurrent calls
|
|
132
130
|
* made while this value is a `Promise` will return the `Promise` itself.
|
|
133
|
-
* @private
|
|
134
131
|
* @type {Promise<void>|undefined}
|
|
135
132
|
*/
|
|
136
|
-
|
|
133
|
+
#reading;
|
|
137
134
|
|
|
138
135
|
/**
|
|
139
136
|
* Sets internal data to a fresh clone of {@link INITIAL_MANIFEST_DATA}
|
|
@@ -143,8 +140,8 @@ export class Manifest {
|
|
|
143
140
|
* @private
|
|
144
141
|
*/
|
|
145
142
|
constructor(appiumHome) {
|
|
146
|
-
this
|
|
147
|
-
this
|
|
143
|
+
this.#appiumHome = appiumHome;
|
|
144
|
+
this.#data = _.cloneDeep(INITIAL_MANIFEST_DATA);
|
|
148
145
|
}
|
|
149
146
|
|
|
150
147
|
/**
|
|
@@ -174,7 +171,23 @@ export class Manifest {
|
|
|
174
171
|
const onMatch = async (filepath) => {
|
|
175
172
|
try {
|
|
176
173
|
const pkg = JSON.parse(await fs.readFile(filepath, 'utf8'));
|
|
177
|
-
if (
|
|
174
|
+
if (isExtension(pkg)) {
|
|
175
|
+
const extType = isDriver(pkg) ? DRIVER_TYPE : PLUGIN_TYPE;
|
|
176
|
+
/**
|
|
177
|
+
* this should only be 'unknown' if the extension's `package.json` is invalid
|
|
178
|
+
* @type {string}
|
|
179
|
+
*/
|
|
180
|
+
const name = isDriver(pkg)
|
|
181
|
+
? pkg.appium.driverName
|
|
182
|
+
: isPlugin(pkg)
|
|
183
|
+
? pkg.appium.pluginName
|
|
184
|
+
: '(unknown)';
|
|
185
|
+
if (
|
|
186
|
+
(isDriver(pkg) && !this.hasDriver(name)) ||
|
|
187
|
+
(isPlugin(pkg) && !this.hasPlugin(name))
|
|
188
|
+
) {
|
|
189
|
+
log.info(`Discovered installed ${extType} "${name}"`);
|
|
190
|
+
}
|
|
178
191
|
const changed = this.addExtensionFromPackage(pkg, filepath);
|
|
179
192
|
didChange = didChange || changed;
|
|
180
193
|
}
|
|
@@ -187,14 +200,14 @@ export class Manifest {
|
|
|
187
200
|
*/
|
|
188
201
|
const queue = [
|
|
189
202
|
// look at `package.json` in `APPIUM_HOME` only
|
|
190
|
-
onMatch(path.join(this
|
|
203
|
+
onMatch(path.join(this.#appiumHome, 'package.json')),
|
|
191
204
|
];
|
|
192
205
|
|
|
193
206
|
// add dependencies to the queue
|
|
194
207
|
await new B((resolve, reject) => {
|
|
195
208
|
glob(
|
|
196
209
|
'node_modules/{*,@*/*}/package.json',
|
|
197
|
-
{cwd: this
|
|
210
|
+
{cwd: this.#appiumHome, silent: true, absolute: true},
|
|
198
211
|
// eslint-disable-next-line promise/prefer-await-to-callbacks
|
|
199
212
|
(err) => {
|
|
200
213
|
if (err) {
|
|
@@ -221,7 +234,7 @@ export class Manifest {
|
|
|
221
234
|
* @returns {boolean}
|
|
222
235
|
*/
|
|
223
236
|
hasDriver(name) {
|
|
224
|
-
return Boolean(this.
|
|
237
|
+
return Boolean(this.#data.drivers[name]);
|
|
225
238
|
}
|
|
226
239
|
|
|
227
240
|
/**
|
|
@@ -230,17 +243,16 @@ export class Manifest {
|
|
|
230
243
|
* @returns {boolean}
|
|
231
244
|
*/
|
|
232
245
|
hasPlugin(name) {
|
|
233
|
-
return Boolean(this.
|
|
246
|
+
return Boolean(this.#data.plugins[name]);
|
|
234
247
|
}
|
|
235
248
|
|
|
236
249
|
/**
|
|
237
250
|
* Given a path to a `package.json`, add it as either a driver or plugin to the manifest.
|
|
238
251
|
*
|
|
239
|
-
* Will _not_ overwrite existing entries.
|
|
240
252
|
* @template {ExtensionType} ExtType
|
|
241
253
|
* @param {ExtPackageJson<ExtType>} pkgJson
|
|
242
254
|
* @param {string} pkgPath
|
|
243
|
-
* @returns {boolean} - `true`
|
|
255
|
+
* @returns {boolean} - `true` if this method did anything.
|
|
244
256
|
*/
|
|
245
257
|
addExtensionFromPackage(pkgJson, pkgPath) {
|
|
246
258
|
const extensionPath = path.dirname(pkgPath);
|
|
@@ -254,29 +266,32 @@ export class Manifest {
|
|
|
254
266
|
appiumVersion: pkgJson.peerDependencies?.appium,
|
|
255
267
|
installType: INSTALL_TYPE_NPM,
|
|
256
268
|
installSpec: `${pkgJson.name}@${pkgJson.version}`,
|
|
269
|
+
installPath: extensionPath,
|
|
257
270
|
};
|
|
258
271
|
|
|
259
272
|
if (isDriver(pkgJson)) {
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
273
|
+
const value = {
|
|
274
|
+
..._.omit(pkgJson.appium, 'driverName'),
|
|
275
|
+
...internal,
|
|
276
|
+
};
|
|
277
|
+
if (!_.isEqual(value, this.#data.drivers[pkgJson.appium.driverName])) {
|
|
278
|
+
this.setExtension(DRIVER_TYPE, pkgJson.appium.driverName, value);
|
|
265
279
|
return true;
|
|
266
280
|
}
|
|
267
281
|
return false;
|
|
268
282
|
} else if (isPlugin(pkgJson)) {
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
283
|
+
const value = {
|
|
284
|
+
..._.omit(pkgJson.appium, 'pluginName'),
|
|
285
|
+
...internal,
|
|
286
|
+
};
|
|
287
|
+
if (!_.isEqual(value, this.#data.plugins[pkgJson.appium.pluginName])) {
|
|
288
|
+
this.setExtension(PLUGIN_TYPE, pkgJson.appium.pluginName, value);
|
|
274
289
|
return true;
|
|
275
290
|
}
|
|
276
291
|
return false;
|
|
277
292
|
} else {
|
|
278
293
|
throw new TypeError(
|
|
279
|
-
`The extension in ${extensionPath} is neither a valid
|
|
294
|
+
`The extension in ${extensionPath} is neither a valid ${DRIVER_TYPE} nor a valid ${PLUGIN_TYPE}.`
|
|
280
295
|
);
|
|
281
296
|
}
|
|
282
297
|
}
|
|
@@ -292,24 +307,48 @@ export class Manifest {
|
|
|
292
307
|
* @param {ExtManifest<ExtType>} extData - Extension metadata
|
|
293
308
|
* @returns {ExtManifest<ExtType>} A clone of `extData`, potentially with a mutated `appiumVersion` field
|
|
294
309
|
*/
|
|
295
|
-
|
|
296
|
-
const data = _.
|
|
297
|
-
this
|
|
310
|
+
setExtension(extType, extName, extData) {
|
|
311
|
+
const data = _.cloneDeep(extData);
|
|
312
|
+
this.#data[`${extType}s`][extName] = data;
|
|
298
313
|
return data;
|
|
299
314
|
}
|
|
300
315
|
|
|
301
316
|
/**
|
|
302
|
-
*
|
|
317
|
+
* Sets the schema revision
|
|
318
|
+
* @param {keyof import('./manifest-migrations').ManifestDataVersions} rev
|
|
319
|
+
*/
|
|
320
|
+
setSchemaRev(rev) {
|
|
321
|
+
this.#data.schemaRev = rev;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Remove an extension from the manifest.
|
|
326
|
+
* @param {ExtensionType} extType
|
|
327
|
+
* @param {string} extName
|
|
328
|
+
*/
|
|
329
|
+
deleteExtension(extType, extName) {
|
|
330
|
+
delete this.#data[`${extType}s`][extName];
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Returns the `APPIUM_HOME` path
|
|
303
335
|
*/
|
|
304
336
|
get appiumHome() {
|
|
305
|
-
return this
|
|
337
|
+
return this.#appiumHome;
|
|
306
338
|
}
|
|
307
339
|
|
|
308
340
|
/**
|
|
309
|
-
* Returns the path to the manifest file
|
|
341
|
+
* Returns the path to the manifest file (`extensions.yaml`)
|
|
310
342
|
*/
|
|
311
343
|
get manifestPath() {
|
|
312
|
-
return this
|
|
344
|
+
return this.#manifestPath;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Returns the schema rev of this manifest
|
|
349
|
+
*/
|
|
350
|
+
get schemaRev() {
|
|
351
|
+
return this.#data.schemaRev;
|
|
313
352
|
}
|
|
314
353
|
|
|
315
354
|
/**
|
|
@@ -317,47 +356,62 @@ export class Manifest {
|
|
|
317
356
|
*
|
|
318
357
|
* @template {ExtensionType} ExtType
|
|
319
358
|
* @param {ExtType} extType
|
|
320
|
-
* @returns {ExtRecord<ExtType
|
|
359
|
+
* @returns {Readonly<ExtRecord<ExtType>>}
|
|
321
360
|
*/
|
|
322
361
|
getExtensionData(extType) {
|
|
323
|
-
return this
|
|
362
|
+
return this.#data[/** @type {string} */ (`${extType}s`)];
|
|
324
363
|
}
|
|
325
364
|
|
|
326
365
|
/**
|
|
327
366
|
* Reads manifest from disk and _overwrites_ the internal data.
|
|
328
367
|
*
|
|
329
|
-
* If the manifest does not exist on disk, an
|
|
368
|
+
* If the manifest does not exist on disk, an
|
|
369
|
+
* {@link INITIAL_MANIFEST_DATA "empty"} manifest file will be created, as
|
|
370
|
+
* well as its directory if needed.
|
|
371
|
+
*
|
|
372
|
+
* This will also, if necessary:
|
|
373
|
+
* 1. perform a migration of the manifest data
|
|
374
|
+
* 2. sync the manifest with extensions on-disk (kind of like "auto
|
|
375
|
+
* discovery")
|
|
376
|
+
* 3. write the manifest to disk.
|
|
330
377
|
*
|
|
331
|
-
*
|
|
378
|
+
* Only one read operation can happen at a time.
|
|
332
379
|
*
|
|
333
|
-
* Only one read operation should happen at a time. This is controlled via the {@link Manifest._reading} property.
|
|
334
380
|
* @returns {Promise<ManifestData>} The data
|
|
335
381
|
*/
|
|
336
382
|
async read() {
|
|
337
|
-
if (this
|
|
338
|
-
await this
|
|
339
|
-
return this
|
|
383
|
+
if (this.#reading) {
|
|
384
|
+
await this.#reading;
|
|
385
|
+
return this.#data;
|
|
340
386
|
}
|
|
341
387
|
|
|
342
|
-
this
|
|
388
|
+
this.#reading = (async () => {
|
|
343
389
|
/** @type {ManifestData} */
|
|
344
390
|
let data;
|
|
345
|
-
|
|
346
|
-
|
|
391
|
+
/**
|
|
392
|
+
* This will be `true` if, after reading, we need to update the manifest data
|
|
393
|
+
* and write it again to disk.
|
|
394
|
+
*/
|
|
395
|
+
let shouldWrite = false;
|
|
396
|
+
await this.#setManifestPath();
|
|
347
397
|
try {
|
|
348
|
-
|
|
349
|
-
const yaml = await fs.readFile(this._manifestPath, 'utf8');
|
|
398
|
+
const yaml = await fs.readFile(this.#manifestPath, 'utf8');
|
|
350
399
|
data = YAML.parse(yaml);
|
|
351
|
-
log.debug(
|
|
400
|
+
log.debug(
|
|
401
|
+
`Parsed manifest file at ${this.#manifestPath}: ${JSON.stringify(data, null, 2)}`
|
|
402
|
+
);
|
|
352
403
|
} catch (err) {
|
|
353
404
|
if (err.code === 'ENOENT') {
|
|
405
|
+
log.debug(`No manifest file found at ${this.#manifestPath}; creating`);
|
|
354
406
|
data = _.cloneDeep(INITIAL_MANIFEST_DATA);
|
|
355
|
-
|
|
407
|
+
shouldWrite = true;
|
|
356
408
|
} else {
|
|
357
|
-
if (this
|
|
409
|
+
if (this.#manifestPath) {
|
|
358
410
|
throw new Error(
|
|
359
411
|
`Appium had trouble loading the extension installation ` +
|
|
360
|
-
`cache file (${this
|
|
412
|
+
`cache file (${this.#manifestPath}). It may be invalid YAML. Specific error: ${
|
|
413
|
+
err.message
|
|
414
|
+
}`
|
|
361
415
|
);
|
|
362
416
|
} else {
|
|
363
417
|
throw new Error(
|
|
@@ -367,47 +421,73 @@ export class Manifest {
|
|
|
367
421
|
}
|
|
368
422
|
}
|
|
369
423
|
|
|
370
|
-
this
|
|
371
|
-
|
|
424
|
+
this.#data = data;
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* the only way `shouldWrite` is `true` is if we have a new file. a new
|
|
428
|
+
* file will get the latest schema revision, so we can skip the migration.
|
|
429
|
+
*/
|
|
430
|
+
if (!shouldWrite && (data.schemaRev ?? 0) < CURRENT_SCHEMA_REV) {
|
|
431
|
+
log.debug(
|
|
432
|
+
`Updating manifest schema from rev ${data.schemaRev ?? '(none)'} to ${CURRENT_SCHEMA_REV}`
|
|
433
|
+
);
|
|
434
|
+
shouldWrite = await migrate(this);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* we still may want to sync with installed extensions even if we have a
|
|
439
|
+
* new file. right now this is limited to the following cases:
|
|
440
|
+
* 1. we have a brand new manifest file
|
|
441
|
+
* 2. we have performed a migration on a manifest file
|
|
442
|
+
* 3. `appium` is a dependency within `package.json`, and `package.json`
|
|
443
|
+
* has changed since last time we checked.
|
|
444
|
+
*
|
|
445
|
+
* It may also make sense to sync with the extensions in an arbitrary
|
|
446
|
+
* `APPIUM_HOME`, but we don't do that here.
|
|
447
|
+
*/
|
|
372
448
|
if (
|
|
373
|
-
|
|
374
|
-
(await
|
|
449
|
+
shouldWrite ||
|
|
450
|
+
((await env.hasAppiumDependency(this.appiumHome)) &&
|
|
451
|
+
(await packageDidChange(this.appiumHome)))
|
|
375
452
|
) {
|
|
376
|
-
|
|
453
|
+
log.debug('Discovering newly installed extensions...');
|
|
454
|
+
shouldWrite = (await this.syncWithInstalledExtensions()) || shouldWrite;
|
|
377
455
|
}
|
|
378
456
|
|
|
379
|
-
if (
|
|
457
|
+
if (shouldWrite) {
|
|
380
458
|
await this.write();
|
|
381
459
|
}
|
|
382
460
|
})();
|
|
461
|
+
|
|
383
462
|
try {
|
|
384
|
-
await this
|
|
385
|
-
return this
|
|
463
|
+
await this.#reading;
|
|
464
|
+
return this.#data;
|
|
386
465
|
} finally {
|
|
387
|
-
this
|
|
466
|
+
this.#reading = undefined;
|
|
388
467
|
}
|
|
389
468
|
}
|
|
390
469
|
|
|
391
470
|
/**
|
|
392
|
-
* Ensures
|
|
471
|
+
* Ensures the internal manifest path is set.
|
|
393
472
|
*
|
|
394
473
|
* Creates the directory if necessary.
|
|
395
|
-
* @private
|
|
396
474
|
* @returns {Promise<string>}
|
|
397
475
|
*/
|
|
398
|
-
async
|
|
399
|
-
if (!this
|
|
400
|
-
this
|
|
476
|
+
async #setManifestPath() {
|
|
477
|
+
if (!this.#manifestPath) {
|
|
478
|
+
this.#manifestPath = await env.resolveManifestPath(this.#appiumHome);
|
|
401
479
|
|
|
402
480
|
/* istanbul ignore if */
|
|
403
|
-
if (path.relative(this
|
|
481
|
+
if (path.relative(this.#appiumHome, this.#manifestPath).startsWith('.')) {
|
|
404
482
|
throw new Error(
|
|
405
|
-
`Mismatch between location of APPIUM_HOME and manifest file. APPIUM_HOME: ${
|
|
483
|
+
`Mismatch between location of APPIUM_HOME and manifest file. APPIUM_HOME: ${
|
|
484
|
+
this.appiumHome
|
|
485
|
+
}, manifest file: ${this.#manifestPath}`
|
|
406
486
|
);
|
|
407
487
|
}
|
|
408
488
|
}
|
|
409
489
|
|
|
410
|
-
return this
|
|
490
|
+
return this.#manifestPath;
|
|
411
491
|
}
|
|
412
492
|
|
|
413
493
|
/**
|
|
@@ -419,34 +499,35 @@ export class Manifest {
|
|
|
419
499
|
* @returns {Promise<boolean>} Whether the data was written
|
|
420
500
|
*/
|
|
421
501
|
async write() {
|
|
422
|
-
if (this
|
|
423
|
-
return this
|
|
502
|
+
if (this.#writing) {
|
|
503
|
+
return this.#writing;
|
|
424
504
|
}
|
|
425
|
-
this
|
|
426
|
-
await this
|
|
505
|
+
this.#writing = (async () => {
|
|
506
|
+
await this.#setManifestPath();
|
|
427
507
|
try {
|
|
428
|
-
await fs.mkdirp(path.dirname(this
|
|
508
|
+
await fs.mkdirp(path.dirname(this.#manifestPath));
|
|
429
509
|
} catch (err) {
|
|
430
510
|
throw new Error(
|
|
431
511
|
`Appium could not create the directory for the manifest file: ${path.dirname(
|
|
432
|
-
this
|
|
512
|
+
this.#manifestPath
|
|
433
513
|
)}. Original error: ${err.message}`
|
|
434
514
|
);
|
|
435
515
|
}
|
|
436
516
|
try {
|
|
437
|
-
await fs.writeFile(this
|
|
517
|
+
await fs.writeFile(this.#manifestPath, YAML.stringify(this.#data), 'utf8');
|
|
438
518
|
return true;
|
|
439
519
|
} catch (err) {
|
|
440
520
|
throw new Error(
|
|
441
|
-
`Appium could not write to manifest at ${this
|
|
442
|
-
|
|
521
|
+
`Appium could not write to manifest at ${this.#manifestPath} using APPIUM_HOME ${
|
|
522
|
+
this.#appiumHome
|
|
523
|
+
}. Please ensure it is writable. Original error: ${err.message}`
|
|
443
524
|
);
|
|
444
525
|
}
|
|
445
526
|
})();
|
|
446
527
|
try {
|
|
447
|
-
return await this
|
|
528
|
+
return await this.#writing;
|
|
448
529
|
} finally {
|
|
449
|
-
this
|
|
530
|
+
this.#writing = undefined;
|
|
450
531
|
}
|
|
451
532
|
}
|
|
452
533
|
}
|
|
@@ -472,18 +553,18 @@ export class Manifest {
|
|
|
472
553
|
*/
|
|
473
554
|
|
|
474
555
|
/**
|
|
475
|
-
* @template
|
|
476
|
-
* @typedef {import('appium/types').ExtPackageJson<
|
|
556
|
+
* @template {ExtensionType} ExtType
|
|
557
|
+
* @typedef {import('appium/types').ExtPackageJson<ExtType>} ExtPackageJson
|
|
477
558
|
*/
|
|
478
559
|
|
|
479
560
|
/**
|
|
480
|
-
* @template
|
|
481
|
-
* @typedef {import('appium/types').ExtManifest<
|
|
561
|
+
* @template {ExtensionType} ExtType
|
|
562
|
+
* @typedef {import('appium/types').ExtManifest<ExtType>} ExtManifest
|
|
482
563
|
*/
|
|
483
564
|
|
|
484
565
|
/**
|
|
485
|
-
* @template
|
|
486
|
-
* @typedef {import('appium/types').ExtRecord<
|
|
566
|
+
* @template {ExtensionType} ExtType
|
|
567
|
+
* @typedef {import('appium/types').ExtRecord<ExtType>} ExtRecord
|
|
487
568
|
*/
|
|
488
569
|
|
|
489
570
|
/**
|
|
@@ -72,7 +72,7 @@ export class PluginConfig extends ExtensionConfig {
|
|
|
72
72
|
|
|
73
73
|
/**
|
|
74
74
|
*
|
|
75
|
-
* @param {(keyof
|
|
75
|
+
* @param {(keyof import('appium/types').ExtRecord<PluginType>)[]} activeNames
|
|
76
76
|
* @returns {void}
|
|
77
77
|
*/
|
|
78
78
|
print(activeNames) {
|
|
@@ -106,7 +106,6 @@ export class PluginConfig extends ExtensionConfig {
|
|
|
106
106
|
*/
|
|
107
107
|
|
|
108
108
|
/**
|
|
109
|
-
* @typedef {import('appium/types').PluginRecord} PluginRecord
|
|
110
109
|
* @typedef {import('@appium/types').PluginType} PluginType
|
|
111
110
|
* @typedef {import('appium/types').ExtMetadata<PluginType>} PluginMetadata
|
|
112
111
|
* @typedef {import('./manifest').Manifest} Manifest
|
package/lib/logsink.js
CHANGED
|
@@ -33,6 +33,8 @@ const npmToWinstonLevels = {
|
|
|
33
33
|
error: 'error',
|
|
34
34
|
};
|
|
35
35
|
|
|
36
|
+
const encounteredPrefixes = [];
|
|
37
|
+
|
|
36
38
|
let log = null;
|
|
37
39
|
let useLocalTimeZone = false;
|
|
38
40
|
|
|
@@ -186,8 +188,21 @@ async function createTransports(args) {
|
|
|
186
188
|
return transports;
|
|
187
189
|
}
|
|
188
190
|
|
|
191
|
+
function getColorizedPrefix(prefix) {
|
|
192
|
+
let prefixId = prefix.split('@')[0].trim();
|
|
193
|
+
prefixId = prefixId.split(' (')[0].trim();
|
|
194
|
+
if (encounteredPrefixes.indexOf(prefixId) < 0) {
|
|
195
|
+
encounteredPrefixes.push(prefixId);
|
|
196
|
+
}
|
|
197
|
+
// using a multiple of 16 should cause 16 colors to be created
|
|
198
|
+
const colorNumber = encounteredPrefixes.indexOf(prefixId) * 16;
|
|
199
|
+
// use the modulus to cycle around color wheel
|
|
200
|
+
return `\x1b[38;5;${colorNumber % 256}m${prefix}\x1b[0m`;
|
|
201
|
+
}
|
|
202
|
+
|
|
189
203
|
async function init(args) {
|
|
190
204
|
npmlog.level = 'silent';
|
|
205
|
+
|
|
191
206
|
// set de facto param passed to timestamp function
|
|
192
207
|
useLocalTimeZone = args.localTimezone;
|
|
193
208
|
|
|
@@ -205,11 +220,17 @@ async function init(args) {
|
|
|
205
220
|
let msg = logObj.message;
|
|
206
221
|
if (logObj.prefix) {
|
|
207
222
|
const prefix = `[${logObj.prefix}]`;
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
223
|
+
if (args.logNoColors) {
|
|
224
|
+
msg = `${prefix} ${msg}`;
|
|
225
|
+
} if (prefix === '[Appium]') {
|
|
226
|
+
msg = `${prefix.magenta} ${msg}`;
|
|
227
|
+
} else {
|
|
228
|
+
msg = `${getColorizedPrefix(prefix)} ${msg}`;
|
|
229
|
+
}
|
|
230
|
+
log[winstonLevel](msg);
|
|
231
|
+
if (args.logHandler && _.isFunction(args.logHandler)) {
|
|
232
|
+
args.logHandler(logObj.level, msg);
|
|
233
|
+
}
|
|
213
234
|
}
|
|
214
235
|
});
|
|
215
236
|
}
|