appium 2.0.0-beta.25 → 2.0.0-beta.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/lib/appium-config.schema.json +278 -0
- package/build/lib/appium.js +45 -66
- package/build/lib/cli/args.js +19 -39
- package/build/lib/cli/driver-command.js +5 -9
- package/build/lib/cli/extension-command.js +73 -64
- package/build/lib/cli/extension.js +10 -23
- package/build/lib/cli/parser.js +9 -19
- package/build/lib/cli/plugin-command.js +5 -9
- package/build/lib/cli/utils.js +2 -4
- package/build/lib/config-file.js +2 -4
- package/build/lib/config.js +7 -6
- package/build/lib/constants.js +60 -0
- package/build/lib/extension/driver-config.js +190 -0
- package/build/lib/extension/extension-config.js +297 -0
- package/build/lib/extension/index.js +77 -0
- package/build/lib/extension/manifest.js +246 -0
- package/build/lib/extension/package-changed.js +68 -0
- package/build/lib/extension/plugin-config.js +87 -0
- package/build/lib/grid-register.js +2 -4
- package/build/lib/logger.js +2 -4
- package/build/lib/logsink.js +2 -4
- package/build/lib/main.js +40 -68
- package/build/lib/schema/appium-config-schema.js +2 -4
- package/build/lib/schema/arg-spec.js +11 -14
- package/build/lib/schema/cli-args.js +2 -4
- package/build/lib/schema/cli-transformers.js +2 -4
- package/build/lib/schema/index.js +2 -4
- package/build/lib/schema/keywords.js +2 -4
- package/build/lib/schema/schema.js +55 -37
- package/build/lib/utils.js +1 -32
- package/lib/appium.js +50 -68
- package/lib/cli/args.js +19 -23
- package/lib/cli/driver-command.js +10 -2
- package/lib/cli/extension-command.js +216 -135
- package/lib/cli/extension.js +7 -15
- package/lib/cli/parser.js +6 -14
- package/lib/cli/plugin-command.js +1 -2
- package/lib/config-file.js +3 -3
- package/lib/config.js +5 -4
- package/lib/constants.js +79 -0
- package/lib/extension/driver-config.js +230 -0
- package/lib/extension/extension-config.js +459 -0
- package/lib/extension/index.js +103 -0
- package/lib/extension/manifest.js +590 -0
- package/lib/extension/package-changed.js +64 -0
- package/lib/extension/plugin-config.js +111 -0
- package/lib/grid-register.js +4 -4
- package/lib/main.js +51 -88
- package/lib/schema/arg-spec.js +2 -2
- package/lib/schema/cli-args.js +1 -0
- package/lib/schema/keywords.js +1 -1
- package/lib/schema/schema.js +60 -28
- package/lib/utils.js +2 -32
- package/package.json +29 -21
- package/{postinstall.js → scripts/postinstall.js} +1 -1
- package/types/types.d.ts +70 -31
- package/bin/ios-webkit-debug-proxy-launcher.js +0 -71
- package/build/check-npm-pack-files.js +0 -23
- package/build/commands-yml/parse.js +0 -319
- package/build/commands-yml/validator.js +0 -130
- package/build/index.js +0 -19
- package/build/lib/cli/npm.js +0 -220
- package/build/lib/driver-config.js +0 -100
- package/build/lib/drivers.js +0 -100
- package/build/lib/ext-config-io.js +0 -165
- package/build/lib/extension-config.js +0 -320
- package/build/lib/plugin-config.js +0 -69
- package/build/lib/plugins.js +0 -18
- package/build/postinstall.js +0 -90
- package/build/test/cli/cli-e2e-specs.js +0 -221
- package/build/test/cli/cli-helpers.js +0 -86
- package/build/test/cli/cli-specs.js +0 -71
- package/build/test/cli/fixtures/test-driver/package.json +0 -27
- package/build/test/cli/schema-args-specs.js +0 -48
- package/build/test/cli/schema-e2e-specs.js +0 -47
- package/build/test/config-e2e-specs.js +0 -112
- package/build/test/config-file-e2e-specs.js +0 -191
- package/build/test/config-file-specs.js +0 -281
- package/build/test/config-specs.js +0 -258
- package/build/test/driver-e2e-specs.js +0 -435
- package/build/test/driver-specs.js +0 -386
- package/build/test/ext-config-io-specs.js +0 -181
- package/build/test/extension-config-specs.js +0 -365
- package/build/test/fixtures/allow-feat.txt +0 -5
- package/build/test/fixtures/caps.json +0 -3
- package/build/test/fixtures/config/allow-insecure.txt +0 -3
- package/build/test/fixtures/config/appium.config.bad-nodeconfig.json +0 -5
- package/build/test/fixtures/config/appium.config.bad.json +0 -32
- package/build/test/fixtures/config/appium.config.ext-good.json +0 -9
- package/build/test/fixtures/config/appium.config.ext-unknown-props.json +0 -10
- package/build/test/fixtures/config/appium.config.good.js +0 -40
- package/build/test/fixtures/config/appium.config.good.json +0 -33
- package/build/test/fixtures/config/appium.config.good.yaml +0 -30
- package/build/test/fixtures/config/appium.config.invalid.json +0 -31
- package/build/test/fixtures/config/appium.config.security-array.json +0 -5
- package/build/test/fixtures/config/appium.config.security-delimited.json +0 -5
- package/build/test/fixtures/config/appium.config.security-path.json +0 -5
- package/build/test/fixtures/config/driver-fake.config.json +0 -8
- package/build/test/fixtures/config/nodeconfig.json +0 -3
- package/build/test/fixtures/config/plugin-fake.config.json +0 -0
- package/build/test/fixtures/default-args.js +0 -35
- package/build/test/fixtures/deny-feat.txt +0 -5
- package/build/test/fixtures/driver.schema.js +0 -20
- package/build/test/fixtures/extensions.yaml +0 -27
- package/build/test/fixtures/flattened-schema.js +0 -532
- package/build/test/fixtures/plugin.schema.js +0 -20
- package/build/test/fixtures/schema-with-extensions.js +0 -28
- package/build/test/grid-register-specs.js +0 -74
- package/build/test/helpers.js +0 -75
- package/build/test/logger-specs.js +0 -76
- package/build/test/npm-specs.js +0 -20
- package/build/test/parser-specs.js +0 -319
- package/build/test/plugin-e2e-specs.js +0 -316
- package/build/test/schema/arg-spec-specs.js +0 -70
- package/build/test/schema/cli-args-specs.js +0 -408
- package/build/test/schema/schema-specs.js +0 -407
- package/build/test/utils-specs.js +0 -288
- package/lib/cli/npm.js +0 -251
- package/lib/driver-config.js +0 -101
- package/lib/drivers.js +0 -84
- package/lib/ext-config-io.js +0 -287
- package/lib/extension-config.js +0 -366
- package/lib/plugin-config.js +0 -63
- package/lib/plugins.js +0 -13
|
@@ -1,47 +1,61 @@
|
|
|
1
|
+
// @ts-check
|
|
1
2
|
/* eslint-disable no-console */
|
|
2
3
|
|
|
3
4
|
import _ from 'lodash';
|
|
4
|
-
import
|
|
5
|
-
import path from 'path';
|
|
6
|
-
import { fs, util } from '@appium/support';
|
|
5
|
+
import { npm, fs, util } from '@appium/support';
|
|
7
6
|
import { log, spinWith, RingBuffer } from './utils';
|
|
8
|
-
import { SubProcess} from 'teen_process';
|
|
7
|
+
import { SubProcess } from 'teen_process';
|
|
9
8
|
import { INSTALL_TYPE_NPM, INSTALL_TYPE_GIT, INSTALL_TYPE_GITHUB,
|
|
10
|
-
INSTALL_TYPE_LOCAL } from '../extension-config';
|
|
9
|
+
INSTALL_TYPE_LOCAL } from '../extension/extension-config';
|
|
11
10
|
|
|
12
11
|
const UPDATE_ALL = 'installed';
|
|
13
12
|
|
|
14
13
|
class NotUpdatableError extends Error {}
|
|
15
14
|
class NoUpdatesAvailableError extends Error {}
|
|
16
15
|
|
|
16
|
+
/**
|
|
17
|
+
* @template {ExtensionType} ExtType
|
|
18
|
+
*/
|
|
17
19
|
export default class ExtensionCommand {
|
|
20
|
+
/**
|
|
21
|
+
* This is the `DriverConfig` or `PluginConfig`, depending on `ExtType`.
|
|
22
|
+
* @type {ExtensionConfig<ExtType>}
|
|
23
|
+
*/
|
|
24
|
+
config;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* {@linkcode Record} of official plugins or drivers.
|
|
28
|
+
* @type {ExtType extends import('../extension/manifest').DriverType ? typeof import('../constants').KNOWN_DRIVERS : typeof import('../constants').KNOWN_PLUGINS}
|
|
29
|
+
*/
|
|
30
|
+
knownExtensions;
|
|
18
31
|
|
|
19
32
|
/**
|
|
20
|
-
*
|
|
21
|
-
* @
|
|
22
|
-
* @property {boolean} json - whether the output of this command should be JSON or text
|
|
23
|
-
* @property {string} type - DRIVER_TYPE or PLUGIN_TYPE
|
|
33
|
+
* If `true`, command output has been requested as JSON.
|
|
34
|
+
* @type {boolean}
|
|
24
35
|
*/
|
|
36
|
+
isJsonOutput;
|
|
25
37
|
|
|
26
38
|
/**
|
|
27
39
|
* Build an ExtensionCommand
|
|
28
|
-
*
|
|
29
|
-
* @param {ExtensionCommandConstructor} opts
|
|
30
|
-
* @return {ExtensionCommand}
|
|
40
|
+
* @param {ExtensionCommandOptions<ExtType>} opts
|
|
31
41
|
*/
|
|
32
|
-
constructor ({config, json
|
|
42
|
+
constructor ({config, json}) {
|
|
33
43
|
this.config = config;
|
|
34
|
-
this.type = type;
|
|
35
44
|
this.isJsonOutput = json;
|
|
36
|
-
|
|
37
|
-
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* `driver` or `plugin`, depending on the `ExtensionConfig`.
|
|
49
|
+
*/
|
|
50
|
+
get type () {
|
|
51
|
+
return this.config.extensionType;
|
|
38
52
|
}
|
|
39
53
|
|
|
40
54
|
/**
|
|
41
55
|
* Take a CLI parse and run an extension command based on its type
|
|
42
56
|
*
|
|
43
57
|
* @param {object} args - a key/value object with CLI flags and values
|
|
44
|
-
* @return {object} the result of the specific command which is executed
|
|
58
|
+
* @return {Promise<object>} the result of the specific command which is executed
|
|
45
59
|
*/
|
|
46
60
|
async execute (args) {
|
|
47
61
|
const cmd = args[`${this.type}Command`];
|
|
@@ -53,7 +67,7 @@ export default class ExtensionCommand {
|
|
|
53
67
|
}
|
|
54
68
|
|
|
55
69
|
/**
|
|
56
|
-
* @typedef
|
|
70
|
+
* @typedef ListOptions
|
|
57
71
|
* @property {boolean} showInstalled - whether should show only installed extensions
|
|
58
72
|
* @property {boolean} showUpdates - whether should show available updates
|
|
59
73
|
*/
|
|
@@ -61,8 +75,8 @@ export default class ExtensionCommand {
|
|
|
61
75
|
/**
|
|
62
76
|
* List extensions
|
|
63
77
|
*
|
|
64
|
-
* @param {
|
|
65
|
-
* @return {
|
|
78
|
+
* @param {ListOptions} opts
|
|
79
|
+
* @return {Promise<ExtensionListData>} map of extension names to extension data
|
|
66
80
|
*/
|
|
67
81
|
async list ({showInstalled, showUpdates}) {
|
|
68
82
|
const lsMsg = `Listing ${showInstalled ? 'installed' : 'available'} ${this.type}s`;
|
|
@@ -77,7 +91,7 @@ export default class ExtensionCommand {
|
|
|
77
91
|
}
|
|
78
92
|
}
|
|
79
93
|
return acc;
|
|
80
|
-
}, {});
|
|
94
|
+
}, /** @type {ExtensionListData} */({}));
|
|
81
95
|
|
|
82
96
|
// if we want to show whether updates are available, put that behind a spinner
|
|
83
97
|
await spinWith(this.isJsonOutput, lsMsg, async () => {
|
|
@@ -85,8 +99,7 @@ export default class ExtensionCommand {
|
|
|
85
99
|
return;
|
|
86
100
|
}
|
|
87
101
|
for (const [ext, data] of _.toPairs(exts)) {
|
|
88
|
-
|
|
89
|
-
if (!installed || installType !== INSTALL_TYPE_NPM) {
|
|
102
|
+
if (!data.installed || data.installType !== INSTALL_TYPE_NPM) {
|
|
90
103
|
// don't need to check for updates on exts that aren't installed
|
|
91
104
|
// also don't need to check for updates on non-npm exts
|
|
92
105
|
continue;
|
|
@@ -106,32 +119,40 @@ export default class ExtensionCommand {
|
|
|
106
119
|
|
|
107
120
|
for (const [
|
|
108
121
|
name,
|
|
109
|
-
|
|
122
|
+
data
|
|
110
123
|
] of _.toPairs(exts)) {
|
|
111
|
-
let
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
124
|
+
let installTxt = ' [not installed]'.grey;
|
|
125
|
+
let updateTxt = '';
|
|
126
|
+
let upToDateTxt = '';
|
|
127
|
+
let unsafeUpdateTxt = '';
|
|
128
|
+
if (data.installed) {
|
|
129
|
+
const {installType, installSpec, updateVersion, unsafeUpdateVersion, version, upToDate} = data;
|
|
130
|
+
let typeTxt;
|
|
131
|
+
switch (installType) {
|
|
132
|
+
case INSTALL_TYPE_GIT:
|
|
133
|
+
case INSTALL_TYPE_GITHUB:
|
|
134
|
+
typeTxt = `(cloned from ${installSpec})`.yellow;
|
|
135
|
+
break;
|
|
136
|
+
case INSTALL_TYPE_LOCAL:
|
|
137
|
+
typeTxt = `(linked from ${installSpec})`.magenta;
|
|
138
|
+
break;
|
|
139
|
+
default:
|
|
140
|
+
typeTxt = '(NPM)';
|
|
141
|
+
}
|
|
142
|
+
installTxt = `@${version.yellow} ${('[installed ' + typeTxt + ']').green}`;
|
|
143
|
+
|
|
144
|
+
if (showUpdates) {
|
|
145
|
+
if (updateVersion) {
|
|
146
|
+
updateTxt = ` [${updateVersion} available]`.magenta;
|
|
147
|
+
}
|
|
148
|
+
if (upToDate) {
|
|
149
|
+
upToDateTxt = ` [Up to date]`.green;
|
|
150
|
+
}
|
|
151
|
+
if (unsafeUpdateVersion) {
|
|
152
|
+
unsafeUpdateTxt = ` [${unsafeUpdateVersion} available (potentially unsafe)]`.cyan;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
122
155
|
}
|
|
123
|
-
const installTxt = installed ?
|
|
124
|
-
`@${version.yellow} ${('[installed ' + typeTxt + ']').green}` :
|
|
125
|
-
' [not installed]'.grey;
|
|
126
|
-
const updateTxt = showUpdates && updateVersion ?
|
|
127
|
-
` [${updateVersion} available]`.magenta :
|
|
128
|
-
'';
|
|
129
|
-
const upToDateTxt = showUpdates && upToDate ?
|
|
130
|
-
` [Up to date]`.green :
|
|
131
|
-
'';
|
|
132
|
-
const unsafeUpdateTxt = showUpdates && unsafeUpdateVersion ?
|
|
133
|
-
` [${unsafeUpdateVersion} available (potentially unsafe)]`.cyan :
|
|
134
|
-
'';
|
|
135
156
|
|
|
136
157
|
console.log(`- ${name.yellow}${installTxt}${updateTxt}${upToDateTxt}${unsafeUpdateTxt}`);
|
|
137
158
|
}
|
|
@@ -139,22 +160,14 @@ export default class ExtensionCommand {
|
|
|
139
160
|
return exts;
|
|
140
161
|
}
|
|
141
162
|
|
|
142
|
-
/**
|
|
143
|
-
* @typedef {Object} InstallArgs
|
|
144
|
-
* @property {string} ext - the name or spec of an extension to install
|
|
145
|
-
* @property {string} installType - how to install this extension. One of the INSTALL_TYPES
|
|
146
|
-
* @property {string} [packageName] - for git/github installs, the extension node package name
|
|
147
|
-
*/
|
|
148
|
-
|
|
149
163
|
/**
|
|
150
164
|
* Install an extension
|
|
151
165
|
*
|
|
152
166
|
* @param {InstallArgs} args
|
|
153
|
-
* @return {
|
|
167
|
+
* @return {Promise<ExtRecord<ExtType>>} map of all installed extension names to extension data
|
|
154
168
|
*/
|
|
155
169
|
async install ({ext, installType, packageName}) {
|
|
156
|
-
|
|
157
|
-
|
|
170
|
+
/** @type {ExtData<ExtType>} */
|
|
158
171
|
let extData;
|
|
159
172
|
let installSpec = ext;
|
|
160
173
|
|
|
@@ -167,23 +180,23 @@ export default class ExtensionCommand {
|
|
|
167
180
|
}
|
|
168
181
|
|
|
169
182
|
if (installType === INSTALL_TYPE_LOCAL) {
|
|
170
|
-
const msg = `Linking ${this.type} from local path`;
|
|
183
|
+
const msg = `Linking ${this.type} from local path into ${this.config.appiumHome}`;
|
|
171
184
|
const pkgJsonData = await spinWith(this.isJsonOutput, msg, async () => (
|
|
172
|
-
await
|
|
185
|
+
await npm.linkPackage(this.config.appiumHome, installSpec))
|
|
173
186
|
);
|
|
174
187
|
extData = this.getExtensionFields(pkgJsonData);
|
|
175
|
-
|
|
188
|
+
log(this.isJsonOutput, `Successfully linked ${extData.pkgName} into ${this.config.appiumHome}`);
|
|
176
189
|
} else if (installType === INSTALL_TYPE_GITHUB) {
|
|
177
190
|
if (installSpec.split('/').length !== 2) {
|
|
178
191
|
throw new Error(`Github ${this.type} spec ${installSpec} appeared to be invalid; ` +
|
|
179
192
|
'it should be of the form <org>/<repo>');
|
|
180
193
|
}
|
|
181
|
-
extData = await this.installViaNpm({ext: installSpec, pkgName: packageName});
|
|
194
|
+
extData = await this.installViaNpm({ext: installSpec, pkgName: /** @type {string} */(packageName)});
|
|
182
195
|
} else if (installType === INSTALL_TYPE_GIT) {
|
|
183
196
|
// git urls can have '.git' at the end, but this is not necessary and would complicate the
|
|
184
197
|
// way we download and name directories, so we can just remove it
|
|
185
198
|
installSpec = installSpec.replace(/\.git$/, '');
|
|
186
|
-
extData = await this.installViaNpm({ext: installSpec, pkgName: packageName});
|
|
199
|
+
extData = await this.installViaNpm({ext: installSpec, pkgName: /** @type {string} */(packageName)});
|
|
187
200
|
} else {
|
|
188
201
|
// at this point we have either an npm package or an appium verified extension
|
|
189
202
|
// name. both of which will be installed via npm.
|
|
@@ -224,8 +237,8 @@ export default class ExtensionCommand {
|
|
|
224
237
|
extData = await this.installViaNpm({ext, pkgName, pkgVer});
|
|
225
238
|
}
|
|
226
239
|
|
|
227
|
-
const extName = extData[`${this.type}Name`];
|
|
228
|
-
delete extData[`${this.type}Name`];
|
|
240
|
+
const extName = extData[/** @type {string} */(`${this.type}Name`)];
|
|
241
|
+
delete extData[/** @type {string} */(`${this.type}Name`)];
|
|
229
242
|
|
|
230
243
|
if (this.config.isInstalled(extName)) {
|
|
231
244
|
throw new Error(`A ${this.type} named '${extName}' is already installed. ` +
|
|
@@ -243,17 +256,11 @@ export default class ExtensionCommand {
|
|
|
243
256
|
return this.config.installedExtensions;
|
|
244
257
|
}
|
|
245
258
|
|
|
246
|
-
/**
|
|
247
|
-
* @typedef {Object} InstallViaNpmArgs
|
|
248
|
-
* @property {string} ext - the name or spec of an extension to install
|
|
249
|
-
* @property {string} pkgName - the NPM package name of the extension
|
|
250
|
-
* @property {string} [pkgVer] - the specific version of the NPM package
|
|
251
|
-
*/
|
|
252
|
-
|
|
253
259
|
/**
|
|
254
260
|
* Install an extension via NPM
|
|
255
261
|
*
|
|
256
262
|
* @param {InstallViaNpmArgs} args
|
|
263
|
+
* @returns {Promise<ExtData<ExtType>>}
|
|
257
264
|
*/
|
|
258
265
|
async installViaNpm ({ext, pkgName, pkgVer}) {
|
|
259
266
|
const npmSpec = `${pkgName}${pkgVer ? '@' + pkgVer : ''}`;
|
|
@@ -261,33 +268,25 @@ export default class ExtensionCommand {
|
|
|
261
268
|
const msg = `Installing '${ext}'${specMsg}`;
|
|
262
269
|
try {
|
|
263
270
|
const pkgJsonData = await spinWith(this.isJsonOutput, msg, async () => (
|
|
264
|
-
await
|
|
265
|
-
pkgDir: path.resolve(this.config.appiumHome, pkgName),
|
|
266
|
-
pkgName,
|
|
271
|
+
await npm.installPackage(this.config.appiumHome, pkgName, {
|
|
267
272
|
pkgVer
|
|
268
273
|
})
|
|
269
274
|
));
|
|
270
|
-
|
|
271
|
-
extData.installPath = pkgName;
|
|
272
|
-
return extData;
|
|
275
|
+
return this.getExtensionFields(pkgJsonData);
|
|
273
276
|
} catch (err) {
|
|
274
277
|
throw new Error(`Encountered an error when installing package: ${err.message}`);
|
|
275
278
|
}
|
|
276
279
|
}
|
|
277
280
|
|
|
278
|
-
/**
|
|
279
|
-
* @typedef {Object} ExtensionArgs
|
|
280
|
-
* @property {string} extName - the name of an extension
|
|
281
|
-
* @property {object} extData - the data for an installed extension
|
|
282
|
-
*/
|
|
283
|
-
|
|
284
281
|
/**
|
|
285
282
|
* Get the text which should be displayed to the user after an extension has been installed. This
|
|
286
283
|
* is designed to be overridden by drivers/plugins with their own particular text.
|
|
287
284
|
*
|
|
288
285
|
* @param {ExtensionArgs} args
|
|
286
|
+
* @returns {string}
|
|
289
287
|
*/
|
|
290
|
-
|
|
288
|
+
// eslint-disable-next-line no-unused-vars
|
|
289
|
+
getPostInstallText (args) {
|
|
291
290
|
throw new Error('Must be implemented in final class');
|
|
292
291
|
}
|
|
293
292
|
|
|
@@ -316,30 +315,25 @@ export default class ExtensionCommand {
|
|
|
316
315
|
* presence and form of those fields on the package.json data, throwing an error if anything is
|
|
317
316
|
* amiss.
|
|
318
317
|
*
|
|
319
|
-
* @param {
|
|
320
|
-
* extension
|
|
318
|
+
* @param {import('../extension/manifest').ExternalData<ExtType>} appiumPkgData - the data in the "appium" field of package.json for an extension
|
|
321
319
|
*/
|
|
322
|
-
|
|
320
|
+
// eslint-disable-next-line no-unused-vars
|
|
321
|
+
validateExtensionFields (appiumPkgData) {
|
|
323
322
|
throw new Error('Must be implemented in final class');
|
|
324
323
|
}
|
|
325
|
-
|
|
326
|
-
/**
|
|
327
|
-
* @typedef {Object} UninstallArgs
|
|
328
|
-
* @property {string} ext - the name or spec of an extension to uninstall
|
|
329
|
-
*/
|
|
330
|
-
|
|
331
324
|
/**
|
|
332
325
|
* Uninstall an extension
|
|
333
326
|
*
|
|
334
|
-
* @param {
|
|
335
|
-
* @return {
|
|
327
|
+
* @param {UninstallOpts} opts
|
|
328
|
+
* @return {Promise<ExtRecord<ExtType>>} map of all installed extension names to extension data
|
|
336
329
|
*/
|
|
337
330
|
async uninstall ({ext}) {
|
|
338
331
|
if (!this.config.isInstalled(ext)) {
|
|
339
332
|
throw new Error(`Can't uninstall ${this.type} '${ext}'; it is not installed`);
|
|
340
333
|
}
|
|
334
|
+
const installPath = this.config.getInstallPath(ext);
|
|
341
335
|
try {
|
|
342
|
-
await fs.rimraf(
|
|
336
|
+
await fs.rimraf(installPath);
|
|
343
337
|
} finally {
|
|
344
338
|
await this.config.removeExtension(ext);
|
|
345
339
|
}
|
|
@@ -347,30 +341,11 @@ export default class ExtensionCommand {
|
|
|
347
341
|
return this.config.installedExtensions;
|
|
348
342
|
}
|
|
349
343
|
|
|
350
|
-
/**
|
|
351
|
-
* @typedef {Object} ExtensionUpdateOpts
|
|
352
|
-
* @property {string} ext - the name of the extension to update
|
|
353
|
-
* @property {boolean} unsafe - if true, will perform unsafe updates past major revision
|
|
354
|
-
* boundaries
|
|
355
|
-
*/
|
|
356
|
-
|
|
357
|
-
/**
|
|
358
|
-
* @typedef {Object} UpdateReport
|
|
359
|
-
* @property {string} from - version updated from
|
|
360
|
-
* @property {string} to - version updated to
|
|
361
|
-
*/
|
|
362
|
-
|
|
363
|
-
/**
|
|
364
|
-
* @typedef {Object} ExtensionUpdateResult
|
|
365
|
-
* @property {Object} errors - map of ext names to error objects
|
|
366
|
-
* @property {Object} updates - map of ext names to {@link UpdateReport}s
|
|
367
|
-
*/
|
|
368
|
-
|
|
369
344
|
/**
|
|
370
345
|
* Attempt to update one or more drivers using NPM
|
|
371
346
|
*
|
|
372
347
|
* @param {ExtensionUpdateOpts} updateSpec
|
|
373
|
-
* @return {ExtensionUpdateResult}
|
|
348
|
+
* @return {Promise<ExtensionUpdateResult>}
|
|
374
349
|
*/
|
|
375
350
|
async update ({ext, unsafe}) {
|
|
376
351
|
const shouldUpdateAll = ext === UPDATE_ALL;
|
|
@@ -381,10 +356,12 @@ export default class ExtensionCommand {
|
|
|
381
356
|
const extsToUpdate = shouldUpdateAll ? Object.keys(this.config.installedExtensions) : [ext];
|
|
382
357
|
|
|
383
358
|
// 'errors' will have ext names as keys and error objects as values
|
|
359
|
+
/** @type {Record<string,Error>} */
|
|
384
360
|
const errors = {};
|
|
385
361
|
|
|
386
362
|
// 'updates' will have ext names as keys and update objects as values, where an update
|
|
387
363
|
// object is of the form {from: versionString, to: versionString}
|
|
364
|
+
/** @type {Record<string,UpdateReport>} */
|
|
388
365
|
const updates = {};
|
|
389
366
|
|
|
390
367
|
for (const e of extsToUpdate) {
|
|
@@ -437,27 +414,20 @@ export default class ExtensionCommand {
|
|
|
437
414
|
return {updates, errors};
|
|
438
415
|
}
|
|
439
416
|
|
|
440
|
-
/**
|
|
441
|
-
* @typedef PossibleUpdates
|
|
442
|
-
* @property {string} current - current version
|
|
443
|
-
* @property {string|null} safeUpdate - version we can safely update to if it exists, or null
|
|
444
|
-
* @property {string|null} unsafeUpdate - version we can unsafely update to if it exists, or null
|
|
445
|
-
*/
|
|
446
|
-
|
|
447
417
|
/**
|
|
448
418
|
* Given an extension name, figure out what its highest possible version upgrade is, and also the
|
|
449
419
|
* highest possible safe upgrade.
|
|
450
420
|
*
|
|
451
421
|
* @param {string} ext - name of extension
|
|
452
|
-
* @return {PossibleUpdates}
|
|
422
|
+
* @return {Promise<PossibleUpdates>}
|
|
453
423
|
*/
|
|
454
424
|
async checkForExtensionUpdate (ext) {
|
|
455
425
|
// TODO decide how we want to handle beta versions?
|
|
456
426
|
// this is a helper method, 'ext' is assumed to already be installed here, and of the npm
|
|
457
427
|
// install type
|
|
458
428
|
const {version, pkgName} = this.config.installedExtensions[ext];
|
|
459
|
-
let unsafeUpdate = await
|
|
460
|
-
let safeUpdate = await
|
|
429
|
+
let unsafeUpdate = await npm.getLatestVersion(this.config.appiumHome, pkgName);
|
|
430
|
+
let safeUpdate = await npm.getLatestSafeUpgradeVersion(this.config.appiumHome, pkgName, version);
|
|
461
431
|
if (!util.compareVersions(unsafeUpdate, '>', version)) {
|
|
462
432
|
// the latest version is not greater than the current version, so there's no possible update
|
|
463
433
|
unsafeUpdate = null;
|
|
@@ -485,7 +455,7 @@ export default class ExtensionCommand {
|
|
|
485
455
|
const {pkgName} = this.config.installedExtensions[ext];
|
|
486
456
|
await fs.rimraf(this.config.getInstallPath(ext));
|
|
487
457
|
const extData = await this.installViaNpm({ext, pkgName, pkgVer: version});
|
|
488
|
-
delete extData[`${this.type}Name`];
|
|
458
|
+
delete extData[/** @type {string} */(`${this.type}Name`)];
|
|
489
459
|
await this.config.updateExtension(ext, extData);
|
|
490
460
|
}
|
|
491
461
|
|
|
@@ -497,9 +467,8 @@ export default class ExtensionCommand {
|
|
|
497
467
|
* "scripts" field is not a plain object, or if the scriptName is
|
|
498
468
|
* not found within "scripts" object.
|
|
499
469
|
*
|
|
500
|
-
* @param {
|
|
501
|
-
* @
|
|
502
|
-
* @return {RunOutput}
|
|
470
|
+
* @param {RunOptions} opts
|
|
471
|
+
* @return {Promise<RunOutput>}
|
|
503
472
|
*/
|
|
504
473
|
async run ({ext, scriptName}) {
|
|
505
474
|
if (!_.has(this.config.installedExtensions, ext)) {
|
|
@@ -508,7 +477,8 @@ export default class ExtensionCommand {
|
|
|
508
477
|
|
|
509
478
|
const extConfig = this.config.installedExtensions[ext];
|
|
510
479
|
|
|
511
|
-
|
|
480
|
+
// note: TS cannot understand that _.has() is a type guard
|
|
481
|
+
if (!extConfig.scripts) {
|
|
512
482
|
throw new Error(`The ${this.type} named '${ext}' does not contain the ` +
|
|
513
483
|
`"scripts" field underneath the "appium" field in its package.json`);
|
|
514
484
|
}
|
|
@@ -523,8 +493,10 @@ export default class ExtensionCommand {
|
|
|
523
493
|
throw new Error(`The ${this.type} named '${ext}' does not support the script: '${scriptName}'`);
|
|
524
494
|
}
|
|
525
495
|
|
|
526
|
-
const runner = new SubProcess(process.execPath, [
|
|
527
|
-
|
|
496
|
+
const runner = new SubProcess(process.execPath, [
|
|
497
|
+
extScripts[scriptName]
|
|
498
|
+
], {
|
|
499
|
+
cwd: this.config.getInstallPath(ext)
|
|
528
500
|
});
|
|
529
501
|
|
|
530
502
|
const output = new RingBuffer(50);
|
|
@@ -548,7 +520,116 @@ export default class ExtensionCommand {
|
|
|
548
520
|
}
|
|
549
521
|
|
|
550
522
|
/**
|
|
551
|
-
* @
|
|
552
|
-
* @
|
|
523
|
+
* @template {ExtensionType} ExtType
|
|
524
|
+
* @typedef {import('../extension/extension-config').ExtensionConfig<ExtType>} ExtensionConfig
|
|
525
|
+
*/
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* Options for the {@linkcode ExtensionCommand} constructor
|
|
529
|
+
* @template {ExtensionType} ExtType
|
|
530
|
+
* @typedef ExtensionCommandOptions
|
|
531
|
+
* @property {ExtensionConfig<ExtType>} config - the `DriverConfig` or `PluginConfig` instance used for this command
|
|
532
|
+
* @property {boolean} json - whether the output of this command should be JSON or text
|
|
533
|
+
*/
|
|
534
|
+
|
|
535
|
+
/**
|
|
536
|
+
* Extra stuff about extensions; used indirectly by {@linkcode ExtensionCommand.list}.
|
|
537
|
+
*
|
|
538
|
+
* @typedef ExtensionMetadata
|
|
539
|
+
* @property {boolean} installed - If `true`, the extension is installed
|
|
540
|
+
* @property {string|null} [updateVersion] - If the extension is installed, the version it can be updated to
|
|
541
|
+
* @property {string|null} [unsafeUpdateVersion] - Same as above, but a major version bump
|
|
542
|
+
* @property {boolean} [upToDate] - If the extension is installed and the latest
|
|
543
|
+
*/
|
|
544
|
+
|
|
545
|
+
/**
|
|
546
|
+
* @typedef {import('../extension/manifest').ExtensionType} ExtensionType
|
|
547
|
+
*/
|
|
548
|
+
|
|
549
|
+
/**
|
|
550
|
+
* @template {ExtensionType} ExtType
|
|
551
|
+
* @typedef {import('../extension/manifest').ExtRecord<ExtType>} ExtRecord
|
|
552
|
+
*/
|
|
553
|
+
|
|
554
|
+
/**
|
|
555
|
+
* @template {ExtensionType} ExtType
|
|
556
|
+
* @typedef {import('../extension/manifest').ExtData<ExtType>} ExtData
|
|
557
|
+
*/
|
|
558
|
+
|
|
559
|
+
/**
|
|
560
|
+
* Return value of {@linkcode ExtensionCommand.list}.
|
|
561
|
+
* @typedef {Record<string, (import('../extension/manifest').InternalData & ExtensionMetadata) | { pkgName: string, installed: false }>} ExtensionListData
|
|
562
|
+
*/
|
|
563
|
+
|
|
564
|
+
/**
|
|
565
|
+
* Options for {@linkcode ExtensionCommand.run}.
|
|
566
|
+
* @typedef RunOptions
|
|
567
|
+
* @property {string} ext - name of the extension to run a script from
|
|
568
|
+
* @property {string} scriptName - name of the script to run
|
|
569
|
+
*/
|
|
570
|
+
|
|
571
|
+
/**
|
|
572
|
+
* Return value of {@linkcode ExtensionCommand.run}
|
|
573
|
+
*
|
|
574
|
+
* @typedef RunOutput
|
|
575
|
+
* @property {string} [error] - error message if script ran unsuccessfully, otherwise undefined
|
|
553
576
|
* @property {string[]} output - script output
|
|
554
577
|
*/
|
|
578
|
+
|
|
579
|
+
/**
|
|
580
|
+
* Options for {@linkcode ExtensionCommand.update}.
|
|
581
|
+
* @typedef ExtensionUpdateOpts
|
|
582
|
+
* @property {string} ext - the name of the extension to update
|
|
583
|
+
* @property {boolean} unsafe - if true, will perform unsafe updates past major revision boundaries
|
|
584
|
+
*/
|
|
585
|
+
|
|
586
|
+
/**
|
|
587
|
+
* Return value of {@linkcode ExtensionCommand.update}.
|
|
588
|
+
* @typedef ExtensionUpdateResult
|
|
589
|
+
* @property {Record<string,Error>} errors - map of ext names to error objects
|
|
590
|
+
* @property {Record<string,UpdateReport>} updates - map of ext names to {@linkcode UpdateReport}s
|
|
591
|
+
*/
|
|
592
|
+
|
|
593
|
+
/**
|
|
594
|
+
* Part of result of {@linkcode ExtensionCommand.update}.
|
|
595
|
+
* @typedef UpdateReport
|
|
596
|
+
* @property {string} from - version the extension was updated from
|
|
597
|
+
* @property {string} to - version the extension was updated to
|
|
598
|
+
*/
|
|
599
|
+
|
|
600
|
+
/**
|
|
601
|
+
* Options for {@linkcode ExtensionCommand.uninstall}.
|
|
602
|
+
* @typedef UninstallOpts
|
|
603
|
+
* @property {string} ext - the name or spec of an extension to uninstall
|
|
604
|
+
*/
|
|
605
|
+
|
|
606
|
+
/**
|
|
607
|
+
* Used by {@linkcode ExtensionCommand.getPostInstallText}
|
|
608
|
+
* @typedef ExtensionArgs
|
|
609
|
+
* @property {string} extName - the name of an extension
|
|
610
|
+
* @property {object} extData - the data for an installed extension
|
|
611
|
+
*/
|
|
612
|
+
|
|
613
|
+
/**
|
|
614
|
+
* Options for {@linkcode ExtensionCommand.installViaNpm}
|
|
615
|
+
* @typedef InstallViaNpmArgs
|
|
616
|
+
* @property {string} ext - the name or spec of an extension to install
|
|
617
|
+
* @property {string} pkgName - the NPM package name of the extension
|
|
618
|
+
* @property {string} [pkgVer] - the specific version of the NPM package
|
|
619
|
+
*/
|
|
620
|
+
|
|
621
|
+
/**
|
|
622
|
+
* Object returned by {@linkcode ExtensionCommand.checkForExtensionUpdate}
|
|
623
|
+
* @typedef PossibleUpdates
|
|
624
|
+
* @property {string} current - current version
|
|
625
|
+
* @property {string?} safeUpdate - version we can safely update to if it exists, or null
|
|
626
|
+
* @property {string?} unsafeUpdate - version we can unsafely update to if it exists, or null
|
|
627
|
+
*/
|
|
628
|
+
|
|
629
|
+
/**
|
|
630
|
+
* Options for {@linkcode ExtensionCommand.install}
|
|
631
|
+
* @typedef InstallArgs
|
|
632
|
+
* @property {string} ext - the name or spec of an extension to install
|
|
633
|
+
* @property {import('../extension/extension-config').InstallType} installType - how to install this extension. One of the INSTALL_TYPES
|
|
634
|
+
* @property {string} [packageName] - for git/github installs, the extension node package name
|
|
635
|
+
*/
|
package/lib/cli/extension.js
CHANGED
|
@@ -2,11 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
import DriverCommand from './driver-command';
|
|
4
4
|
import PluginCommand from './plugin-command';
|
|
5
|
-
import
|
|
6
|
-
import PluginConfig from '../plugin-config';
|
|
7
|
-
import { DRIVER_TYPE } from '../extension-config';
|
|
5
|
+
import { DRIVER_TYPE } from '../constants';
|
|
8
6
|
import { errAndQuit, log, JSON_SPACES } from './utils';
|
|
9
|
-
import { APPIUM_HOME } from './args';
|
|
10
7
|
|
|
11
8
|
/**
|
|
12
9
|
* Run a subcommand of the 'appium driver' type. Each subcommand has its own set of arguments which
|
|
@@ -14,13 +11,14 @@ import { APPIUM_HOME } from './args';
|
|
|
14
11
|
*
|
|
15
12
|
* @param {Object} args - JS object where the key is the parameter name (as defined in
|
|
16
13
|
* driver-parser.js)
|
|
17
|
-
* @
|
|
18
|
-
* @
|
|
14
|
+
* @template {ExtensionType} ExtType
|
|
15
|
+
* @param {import('../extension/extension-config').ExtensionConfig<ExtType>} configObject - Extension config object
|
|
19
16
|
*/
|
|
20
|
-
async function runExtensionCommand (args,
|
|
17
|
+
async function runExtensionCommand (args, configObject) {
|
|
21
18
|
// TODO driver config file should be locked while any of these commands are
|
|
22
19
|
// running to prevent weird situations
|
|
23
20
|
let jsonResult = null;
|
|
21
|
+
const {extensionType: type} = configObject;
|
|
24
22
|
const extCmd = args[`${type}Command`];
|
|
25
23
|
if (!extCmd) {
|
|
26
24
|
throw new TypeError(`Cannot call ${type} command without a subcommand like 'install'`);
|
|
@@ -30,17 +28,11 @@ async function runExtensionCommand (args, type, configObject) {
|
|
|
30
28
|
json = true;
|
|
31
29
|
}
|
|
32
30
|
const logFn = (msg) => log(json, msg);
|
|
33
|
-
let config;
|
|
34
|
-
|
|
35
|
-
config = configObject;
|
|
36
|
-
config.log = logFn;
|
|
37
|
-
} else {
|
|
38
|
-
config = (type === DRIVER_TYPE ? DriverConfig : PluginConfig).getInstance(APPIUM_HOME, logFn);
|
|
39
|
-
}
|
|
31
|
+
let config = configObject;
|
|
32
|
+
config.log = logFn;
|
|
40
33
|
const CommandClass = type === DRIVER_TYPE ? DriverCommand : PluginCommand;
|
|
41
34
|
const cmd = new CommandClass({config, json});
|
|
42
35
|
try {
|
|
43
|
-
await config.read();
|
|
44
36
|
jsonResult = await cmd.execute(args);
|
|
45
37
|
} catch (err) {
|
|
46
38
|
// in the suppress output case, we are calling this function internally and should
|
package/lib/cli/parser.js
CHANGED
|
@@ -2,21 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
import { fs } from '@appium/support';
|
|
4
4
|
import { ArgumentParser } from 'argparse';
|
|
5
|
-
import B from 'bluebird';
|
|
6
5
|
import _ from 'lodash';
|
|
7
6
|
import path from 'path';
|
|
8
|
-
import { DRIVER_TYPE, PLUGIN_TYPE } from '../
|
|
7
|
+
import { DRIVER_TYPE, PLUGIN_TYPE, SERVER_SUBCOMMAND } from '../constants';
|
|
9
8
|
import { finalizeSchema, getArgSpec, hasArgSpec } from '../schema';
|
|
10
|
-
import { rootDir } from '../
|
|
9
|
+
import { rootDir } from '../config';
|
|
11
10
|
import {
|
|
12
|
-
driverConfig,
|
|
13
11
|
getExtensionArgs,
|
|
14
|
-
getServerArgs
|
|
15
|
-
pluginConfig
|
|
12
|
+
getServerArgs
|
|
16
13
|
} from './args';
|
|
17
14
|
|
|
18
|
-
export const SERVER_SUBCOMMAND = 'server';
|
|
19
|
-
|
|
20
15
|
/**
|
|
21
16
|
* If the parsed args do not contain any of these values, then we
|
|
22
17
|
* will automatially inject the `server` subcommand.
|
|
@@ -258,19 +253,16 @@ class ArgParser {
|
|
|
258
253
|
}
|
|
259
254
|
|
|
260
255
|
/**
|
|
261
|
-
* Creates a {@link ArgParser} instance
|
|
262
|
-
* beforehand, and finalizes the config schema.
|
|
256
|
+
* Creates a {@link ArgParser} instance; finalizes the config schema.
|
|
263
257
|
*
|
|
264
258
|
* @constructs ArgParser
|
|
265
259
|
* @param {boolean} [debug] - If `true`, throw instead of exit upon parsing error
|
|
266
|
-
* @returns {
|
|
260
|
+
* @returns {ArgParser}
|
|
267
261
|
*/
|
|
268
|
-
|
|
269
|
-
await B.all([driverConfig.read(), pluginConfig.read()]);
|
|
262
|
+
function getParser (debug) {
|
|
270
263
|
finalizeSchema();
|
|
271
264
|
|
|
272
265
|
return new ArgParser(debug);
|
|
273
266
|
}
|
|
274
267
|
|
|
275
|
-
export default getParser;
|
|
276
268
|
export { getParser, ArgParser };
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import _ from 'lodash';
|
|
2
2
|
import ExtensionCommand from './extension-command';
|
|
3
|
-
import { PLUGIN_TYPE } from '../
|
|
4
|
-
import { KNOWN_PLUGINS } from '../plugins';
|
|
3
|
+
import { PLUGIN_TYPE, KNOWN_PLUGINS } from '../constants';
|
|
5
4
|
|
|
6
5
|
const REQ_PLUGIN_FIELDS = ['pluginName', 'mainClass'];
|
|
7
6
|
|