appium 2.0.0-beta.23 → 2.0.0-beta.24
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 +1 -2
- package/build/lib/appium.js +19 -7
- package/build/lib/cli/args.js +13 -15
- package/build/lib/cli/parser.js +2 -2
- package/build/lib/config.js +31 -24
- package/build/lib/extension-config.js +1 -1
- package/build/lib/main.js +29 -28
- package/build/lib/plugins.js +4 -2
- package/build/lib/schema/arg-spec.js +5 -3
- package/build/lib/schema/cli-args.js +7 -13
- package/build/lib/schema/schema.js +80 -9
- package/build/lib/utils.js +16 -36
- package/build/test/cli/cli-helpers.js +1 -1
- package/build/test/config-specs.js +122 -35
- package/build/test/driver-specs.js +96 -31
- package/build/test/fixtures/flattened-schema.js +31 -3
- package/build/test/parser-specs.js +69 -64
- package/build/test/schema/cli-args-specs.js +4 -27
- package/build/test/schema/schema-specs.js +56 -38
- package/build/test/utils-specs.js +24 -2
- package/lib/appium.js +45 -8
- package/lib/cli/args.js +17 -14
- package/lib/cli/parser.js +1 -1
- package/lib/config.js +75 -31
- package/lib/extension-config.js +1 -1
- package/lib/main.js +87 -37
- package/lib/plugins.js +2 -0
- package/lib/schema/arg-spec.js +8 -2
- package/lib/schema/cli-args.js +6 -30
- package/lib/schema/schema.js +142 -22
- package/lib/utils.js +28 -29
- package/package.json +4 -4
- package/types/types.d.ts +5 -0
package/lib/appium.js
CHANGED
|
@@ -3,10 +3,12 @@ import log from './logger';
|
|
|
3
3
|
import { getBuildInfo, updateBuildInfo, APPIUM_VER } from './config';
|
|
4
4
|
import { findMatchingDriver } from './drivers';
|
|
5
5
|
import { BaseDriver, errors, isSessionCommand,
|
|
6
|
-
CREATE_SESSION_COMMAND
|
|
6
|
+
CREATE_SESSION_COMMAND, DELETE_SESSION_COMMAND, GET_STATUS_COMMAND
|
|
7
|
+
} from '@appium/base-driver';
|
|
7
8
|
import AsyncLock from 'async-lock';
|
|
8
9
|
import { parseCapsForInnerDriver, pullSettings } from './utils';
|
|
9
10
|
import { util } from '@appium/support';
|
|
11
|
+
import { getDefaultsForExtension } from './schema';
|
|
10
12
|
|
|
11
13
|
const desiredCapabilityConstraints = {
|
|
12
14
|
automationName: {
|
|
@@ -51,6 +53,7 @@ class AppiumDriver extends BaseDriver {
|
|
|
51
53
|
// It is not recommended to access this property directly from the outside
|
|
52
54
|
this.pendingDrivers = {};
|
|
53
55
|
|
|
56
|
+
/** @type {PluginExtensionClass[]} */
|
|
54
57
|
this.pluginClasses = []; // list of which plugins are active
|
|
55
58
|
this.sessionPlugins = {}; // map of sessions to actual plugin instances per session
|
|
56
59
|
this.sessionlessPlugins = []; // some commands are sessionless, so we need a set of plugins for them
|
|
@@ -59,9 +62,12 @@ class AppiumDriver extends BaseDriver {
|
|
|
59
62
|
updateBuildInfo();
|
|
60
63
|
}
|
|
61
64
|
|
|
62
|
-
/** @type {
|
|
65
|
+
/** @type {import('./driver-config').default|undefined} */
|
|
63
66
|
driverConfig;
|
|
64
67
|
|
|
68
|
+
/** @type {import('express').Express|undefined} */
|
|
69
|
+
server;
|
|
70
|
+
|
|
65
71
|
/**
|
|
66
72
|
* Cancel commands queueing for the umbrella Appium driver
|
|
67
73
|
*/
|
|
@@ -118,14 +124,22 @@ class AppiumDriver extends BaseDriver {
|
|
|
118
124
|
* Validate and assign CLI args for a driver or plugin
|
|
119
125
|
*
|
|
120
126
|
* If the extension has provided a schema, validation has already happened.
|
|
127
|
+
*
|
|
128
|
+
* Any arg which is equal to its default value will not be assigned to the extension.
|
|
121
129
|
* @param {import('./ext-config-io').ExtensionType} extType 'driver' or 'plugin'
|
|
122
130
|
* @param {string} extName the name of the extension
|
|
123
131
|
* @param {Object} extInstance the driver or plugin instance
|
|
124
132
|
*/
|
|
125
133
|
assignCliArgsToExtension (extType, extName, extInstance) {
|
|
126
|
-
const
|
|
127
|
-
if (!_.isEmpty(
|
|
128
|
-
|
|
134
|
+
const allCliArgsForExt = this.args[extType]?.[extName];
|
|
135
|
+
if (!_.isEmpty(allCliArgsForExt)) {
|
|
136
|
+
const defaults = getDefaultsForExtension(extType, extName);
|
|
137
|
+
const cliArgs = _.isEmpty(defaults)
|
|
138
|
+
? allCliArgsForExt
|
|
139
|
+
: _.omitBy(allCliArgsForExt, (value, key) => _.isEqual(defaults[key], value));
|
|
140
|
+
if (!_.isEmpty(cliArgs)) {
|
|
141
|
+
extInstance.cliArgs = cliArgs;
|
|
142
|
+
}
|
|
129
143
|
}
|
|
130
144
|
}
|
|
131
145
|
|
|
@@ -443,9 +457,10 @@ class AppiumDriver extends BaseDriver {
|
|
|
443
457
|
// The tricky part is that because we support command plugins, we need to wrap any of these
|
|
444
458
|
// cases with plugin handling.
|
|
445
459
|
|
|
446
|
-
const isGetStatus = cmd ===
|
|
460
|
+
const isGetStatus = cmd === GET_STATUS_COMMAND;
|
|
461
|
+
const isDeleteSession = cmd === DELETE_SESSION_COMMAND;
|
|
447
462
|
const isUmbrellaCmd = !isGetStatus && isAppiumDriverCommand(cmd);
|
|
448
|
-
const isSessionCmd = !
|
|
463
|
+
const isSessionCmd = !isUmbrellaCmd || isDeleteSession;
|
|
449
464
|
|
|
450
465
|
// if a plugin override proxying for this command and that is why we are here instead of just
|
|
451
466
|
// letting the protocol proxy the command entirely, determine that, get the request object for
|
|
@@ -470,7 +485,9 @@ class AppiumDriver extends BaseDriver {
|
|
|
470
485
|
}
|
|
471
486
|
// now save the response protocol given that the session driver's protocol might differ
|
|
472
487
|
protocol = dstSession.protocol;
|
|
473
|
-
|
|
488
|
+
if (!isUmbrellaCmd) {
|
|
489
|
+
driver = dstSession;
|
|
490
|
+
}
|
|
474
491
|
}
|
|
475
492
|
|
|
476
493
|
// get any plugins which are registered as handling this command
|
|
@@ -657,3 +674,23 @@ export class NoDriverProxyCommandError extends Error {
|
|
|
657
674
|
}
|
|
658
675
|
|
|
659
676
|
export { AppiumDriver };
|
|
677
|
+
|
|
678
|
+
|
|
679
|
+
/**
|
|
680
|
+
* @typedef {Object} StaticExtMembers
|
|
681
|
+
* @property {(app: import('express').Express, httpServer: import('http').Server) => import('type-fest').Promisable<void>} [updateServer]
|
|
682
|
+
* @property {import('@appium/base-driver').MethodMap} [newMethodMap]
|
|
683
|
+
*/
|
|
684
|
+
|
|
685
|
+
/**
|
|
686
|
+
* @typedef {Object} StaticPluginMembers
|
|
687
|
+
* @property {string} pluginName
|
|
688
|
+
*/
|
|
689
|
+
|
|
690
|
+
/**
|
|
691
|
+
* @typedef {import('type-fest').Class<unknown> & StaticPluginMembers & StaticExtMembers} PluginExtensionClass
|
|
692
|
+
*/
|
|
693
|
+
|
|
694
|
+
/**
|
|
695
|
+
* @typedef {import('type-fest').Class<unknown> & StaticExtMembers} DriverExtensionClass
|
|
696
|
+
*/
|
package/lib/cli/args.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
|
|
3
3
|
// @ts-ignore
|
|
4
|
-
import { DEFAULT_BASE_PATH } from '@appium/base-driver';
|
|
5
4
|
import _ from 'lodash';
|
|
6
5
|
import DriverConfig from '../driver-config';
|
|
7
6
|
import { APPIUM_HOME, DRIVER_TYPE, INSTALL_TYPES, PLUGIN_TYPE } from '../extension-config';
|
|
@@ -175,13 +174,7 @@ function makeRunArgs (type) {
|
|
|
175
174
|
*/
|
|
176
175
|
function getServerArgs () {
|
|
177
176
|
return new Map([
|
|
178
|
-
...toParserArgs(
|
|
179
|
-
overrides: {
|
|
180
|
-
basePath: {
|
|
181
|
-
default: DEFAULT_BASE_PATH
|
|
182
|
-
},
|
|
183
|
-
}
|
|
184
|
-
}),
|
|
177
|
+
...toParserArgs(),
|
|
185
178
|
...serverArgsDisallowedInConfig,
|
|
186
179
|
]);
|
|
187
180
|
}
|
|
@@ -195,21 +188,31 @@ const serverArgsDisallowedInConfig = new Map([
|
|
|
195
188
|
['--shell'],
|
|
196
189
|
{
|
|
197
190
|
required: false,
|
|
198
|
-
default: null,
|
|
199
191
|
help: 'Enter REPL mode',
|
|
200
|
-
action: '
|
|
192
|
+
action: 'store_const',
|
|
193
|
+
const: true,
|
|
201
194
|
dest: 'shell',
|
|
202
195
|
},
|
|
203
196
|
],
|
|
197
|
+
[
|
|
198
|
+
['--show-build-info'],
|
|
199
|
+
{
|
|
200
|
+
dest: 'showBuildInfo',
|
|
201
|
+
action: 'store_const',
|
|
202
|
+
const: true,
|
|
203
|
+
required: false,
|
|
204
|
+
help: 'Show info about the Appium build and exit',
|
|
205
|
+
},
|
|
206
|
+
],
|
|
204
207
|
[
|
|
205
208
|
['--show-config'],
|
|
206
209
|
{
|
|
207
|
-
default: false,
|
|
208
210
|
dest: 'showConfig',
|
|
209
|
-
action: '
|
|
211
|
+
action: 'store_const',
|
|
212
|
+
const: true,
|
|
210
213
|
required: false,
|
|
211
|
-
help: 'Show
|
|
212
|
-
}
|
|
214
|
+
help: 'Show the current Appium configuration and exit',
|
|
215
|
+
}
|
|
213
216
|
],
|
|
214
217
|
[
|
|
215
218
|
['--config'],
|
package/lib/cli/parser.js
CHANGED
|
@@ -146,7 +146,7 @@ class ArgParser {
|
|
|
146
146
|
return _.reduce(
|
|
147
147
|
args,
|
|
148
148
|
(unpacked, value, key) => {
|
|
149
|
-
if (hasArgSpec(key)) {
|
|
149
|
+
if (!_.isUndefined(value) && hasArgSpec(key)) {
|
|
150
150
|
const {dest} = /** @type {import('../schema/arg-spec').ArgSpec} */(getArgSpec(key));
|
|
151
151
|
_.set(unpacked, dest, value);
|
|
152
152
|
} else {
|
package/lib/config.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
/* eslint-disable no-console */
|
|
1
4
|
import _ from 'lodash';
|
|
2
5
|
import { mkdirp, system, fs } from '@appium/support';
|
|
3
6
|
import axios from 'axios';
|
|
@@ -6,7 +9,7 @@ import { rootDir } from './utils';
|
|
|
6
9
|
import logger from './logger';
|
|
7
10
|
import semver from 'semver';
|
|
8
11
|
import findUp from 'find-up';
|
|
9
|
-
import {
|
|
12
|
+
import { getDefaultsForSchema } from './schema/schema';
|
|
10
13
|
|
|
11
14
|
const npmPackage = fs.readPackageJsonFrom(__dirname);
|
|
12
15
|
|
|
@@ -22,7 +25,7 @@ const BUILD_INFO = {
|
|
|
22
25
|
};
|
|
23
26
|
|
|
24
27
|
function getNodeVersion () {
|
|
25
|
-
return semver.coerce(process.version);
|
|
28
|
+
return /** @type {import('semver').SemVer} */(semver.coerce(process.version));
|
|
26
29
|
}
|
|
27
30
|
|
|
28
31
|
async function updateBuildInfo (useGithubApiFallback = false) {
|
|
@@ -42,7 +45,7 @@ async function updateBuildInfo (useGithubApiFallback = false) {
|
|
|
42
45
|
*
|
|
43
46
|
* This is needed because Appium cannot assume `package.json` and `.git` are in the same
|
|
44
47
|
* directory. Monorepos, see?
|
|
45
|
-
* @returns {string|
|
|
48
|
+
* @returns {Promise<string|undefined>} Path to dir or `undefined` if not found
|
|
46
49
|
*/
|
|
47
50
|
async function findGitRoot () {
|
|
48
51
|
return await findUp(GIT_META_ROOT, {cwd: rootDir, type: 'directory'});
|
|
@@ -80,6 +83,11 @@ async function getGitRev (useGithubApiFallback = false) {
|
|
|
80
83
|
return null;
|
|
81
84
|
}
|
|
82
85
|
|
|
86
|
+
/**
|
|
87
|
+
* @param {string} commitSha
|
|
88
|
+
* @param {boolean} [useGithubApiFallback]
|
|
89
|
+
* @returns {Promise<number?>}
|
|
90
|
+
*/
|
|
83
91
|
async function getGitTimestamp (commitSha, useGithubApiFallback = false) {
|
|
84
92
|
const gitRoot = await findGitRoot();
|
|
85
93
|
if (gitRoot) {
|
|
@@ -144,37 +152,30 @@ function warnNodeDeprecations () {
|
|
|
144
152
|
// }
|
|
145
153
|
}
|
|
146
154
|
|
|
147
|
-
async function
|
|
155
|
+
async function showBuildInfo () {
|
|
148
156
|
await updateBuildInfo(true);
|
|
149
157
|
console.log(JSON.stringify(getBuildInfo())); // eslint-disable-line no-console
|
|
150
158
|
}
|
|
151
159
|
|
|
152
|
-
|
|
160
|
+
/**
|
|
161
|
+
* Returns k/v pairs of server arguments which are _not_ the defaults.
|
|
162
|
+
* @param {ParsedArgs} args
|
|
163
|
+
* @returns {Partial<ParsedArgs>}
|
|
164
|
+
*/
|
|
165
|
+
function getNonDefaultServerArgs (args) {
|
|
153
166
|
// hopefully these function names are descriptive enough
|
|
154
167
|
|
|
155
|
-
|
|
156
|
-
return typeof args[dest] !== typeof defaultsFromSchema[dest];
|
|
157
|
-
}
|
|
168
|
+
const typesDiffer = /** @param {string} dest */(dest) => typeof args[dest] !== typeof defaultsFromSchema[dest];
|
|
158
169
|
|
|
159
|
-
|
|
160
|
-
return _.isArray(defaultsFromSchema[dest]);
|
|
161
|
-
}
|
|
170
|
+
const defaultValueIsArray = /** @param {string} dest */(dest) => _.isArray(defaultsFromSchema[dest]);
|
|
162
171
|
|
|
163
|
-
|
|
164
|
-
return _.isArray(args[dest]);
|
|
165
|
-
}
|
|
172
|
+
const argsValueIsArray = /** @param {string} dest */(dest) => _.isArray(args[dest]);
|
|
166
173
|
|
|
167
|
-
|
|
168
|
-
return _.difference(args[dest], defaultsFromSchema[dest]).length > 0;
|
|
169
|
-
}
|
|
174
|
+
const arraysDiffer = /** @param {string} dest */(dest) => _.gt(_.size(_.difference(args[dest], defaultsFromSchema[dest])), 0);
|
|
170
175
|
|
|
171
|
-
|
|
172
|
-
return args[dest] !== defaultsFromSchema[dest];
|
|
173
|
-
}
|
|
176
|
+
const valuesDiffer = /** @param {string} dest */(dest) => args[dest] !== defaultsFromSchema[dest];
|
|
174
177
|
|
|
175
|
-
|
|
176
|
-
return !_.isUndefined(defaultsFromSchema[dest]);
|
|
177
|
-
}
|
|
178
|
+
const defaultIsDefined = /** @param {string} dest */(dest) => !_.isUndefined(defaultsFromSchema[dest]);
|
|
178
179
|
|
|
179
180
|
// note that `_.overEvery` is like an "AND", and `_.overSome` is like an "OR"
|
|
180
181
|
|
|
@@ -183,8 +184,8 @@ function getNonDefaultServerArgs (parser, args) {
|
|
|
183
184
|
arraysDiffer
|
|
184
185
|
]);
|
|
185
186
|
|
|
186
|
-
const
|
|
187
|
-
_.negate(defaultValueIsArray),
|
|
187
|
+
const defaultValueNotArrayAndValuesDiffer = _.overEvery([
|
|
188
|
+
_.negate(defaultValueIsArray), valuesDiffer
|
|
188
189
|
]);
|
|
189
190
|
|
|
190
191
|
/**
|
|
@@ -197,8 +198,7 @@ function getNonDefaultServerArgs (parser, args) {
|
|
|
197
198
|
* - if so, and the default is an array:
|
|
198
199
|
* - ensures the args value is an array
|
|
199
200
|
* - ensures the args values do not differ from the default values
|
|
200
|
-
* @
|
|
201
|
-
* @returns {boolean}
|
|
201
|
+
* @type {(dest: string) => boolean}
|
|
202
202
|
*/
|
|
203
203
|
const isNotDefault = _.overEvery([
|
|
204
204
|
defaultIsDefined,
|
|
@@ -208,15 +208,55 @@ function getNonDefaultServerArgs (parser, args) {
|
|
|
208
208
|
defaultValueIsArray,
|
|
209
209
|
argValueNotArrayOrArraysDiffer
|
|
210
210
|
]),
|
|
211
|
-
|
|
211
|
+
defaultValueNotArrayAndValuesDiffer
|
|
212
212
|
])
|
|
213
213
|
]);
|
|
214
214
|
|
|
215
|
-
const defaultsFromSchema =
|
|
215
|
+
const defaultsFromSchema = getDefaultsForSchema();
|
|
216
216
|
|
|
217
217
|
return _.pickBy(args, (__, key) => isNotDefault(key));
|
|
218
218
|
}
|
|
219
219
|
|
|
220
|
+
/**
|
|
221
|
+
* Compacts an object for {@link showConfig}:
|
|
222
|
+
* 1. Removes `subcommand` key/value
|
|
223
|
+
* 2. Removes `undefined` values
|
|
224
|
+
* 3. Removes empty objects (but not `false` values)
|
|
225
|
+
* Does not operate recursively.
|
|
226
|
+
*/
|
|
227
|
+
const compactConfig = _.partial(
|
|
228
|
+
_.omitBy,
|
|
229
|
+
_,
|
|
230
|
+
(value, key) => key === 'subcommand' || _.isUndefined(value) || (_.isObject(value) && _.isEmpty(value))
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Shows a breakdown of the current config after CLI params, config file loaded & defaults applied.
|
|
235
|
+
*
|
|
236
|
+
* The actual shape of `preConfigParsedArgs` and `defaults` does not matter for the purposes of this function,
|
|
237
|
+
* but it's intended to be called with values of type {@link ParsedArgs} and `DefaultValues<true>`, respectively.
|
|
238
|
+
*
|
|
239
|
+
* @param {object} preConfigParsedArgs - Parsed CLI args (or param to `init()`) before config & defaults applied
|
|
240
|
+
* @param {import('./config-file').ReadConfigFileResult} configResult - Result of attempting to load a config file
|
|
241
|
+
* @param {object} defaults - Configuration defaults from schemas
|
|
242
|
+
*/
|
|
243
|
+
function showConfig (preConfigParsedArgs, configResult, defaults) {
|
|
244
|
+
console.log('Appium Configuration\n');
|
|
245
|
+
if (configResult.config) {
|
|
246
|
+
console.log(`via config file at ${configResult.filepath}:\n`);
|
|
247
|
+
console.dir(compactConfig(configResult.config));
|
|
248
|
+
} else {
|
|
249
|
+
console.log(`(no configuration file loaded)\n`);
|
|
250
|
+
}
|
|
251
|
+
console.log('via CLI or function call:\n');
|
|
252
|
+
console.dir(compactConfig(preConfigParsedArgs));
|
|
253
|
+
console.log('\nvia defaults:\n');
|
|
254
|
+
console.dir(compactConfig(defaults));
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* @param {string} tmpDir
|
|
259
|
+
*/
|
|
220
260
|
async function validateTmpDir (tmpDir) {
|
|
221
261
|
try {
|
|
222
262
|
await mkdirp(tmpDir);
|
|
@@ -227,7 +267,11 @@ async function validateTmpDir (tmpDir) {
|
|
|
227
267
|
}
|
|
228
268
|
|
|
229
269
|
export {
|
|
230
|
-
getBuildInfo, checkNodeOk,
|
|
270
|
+
getBuildInfo, checkNodeOk, showBuildInfo,
|
|
231
271
|
warnNodeDeprecations, validateTmpDir, getNonDefaultServerArgs,
|
|
232
|
-
getGitRev, APPIUM_VER, updateBuildInfo
|
|
272
|
+
getGitRev, APPIUM_VER, updateBuildInfo, showConfig
|
|
233
273
|
};
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* @typedef {import('../types/types').ParsedArgs} ParsedArgs
|
|
277
|
+
*/
|
package/lib/extension-config.js
CHANGED
|
@@ -270,7 +270,7 @@ export default class ExtensionConfig {
|
|
|
270
270
|
/**
|
|
271
271
|
* Loads extension and returns its main class
|
|
272
272
|
* @param {string} extName
|
|
273
|
-
* @returns {(
|
|
273
|
+
* @returns {import('type-fest').Class<unknown>}
|
|
274
274
|
*/
|
|
275
275
|
require (extName) {
|
|
276
276
|
const {mainClass} = this.installedExtensions[extName];
|
package/lib/main.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
// transpile:main
|
|
4
4
|
// @ts-check
|
|
5
5
|
|
|
6
|
+
import logger from './logger'; // logger needs to remain first of imports
|
|
6
7
|
// @ts-ignore
|
|
7
8
|
import { routeConfiguringFunction as makeRouter, server as baseServer } from '@appium/base-driver';
|
|
8
9
|
import { logger as logFactory, util } from '@appium/support';
|
|
@@ -12,14 +13,13 @@ import { AppiumDriver } from './appium';
|
|
|
12
13
|
import { driverConfig, pluginConfig, USE_ALL_PLUGINS } from './cli/args';
|
|
13
14
|
import { runExtensionCommand } from './cli/extension';
|
|
14
15
|
import { default as getParser, SERVER_SUBCOMMAND } from './cli/parser';
|
|
15
|
-
import { APPIUM_VER, checkNodeOk, getGitRev, getNonDefaultServerArgs,
|
|
16
|
+
import { APPIUM_VER, checkNodeOk, getGitRev, getNonDefaultServerArgs, showBuildInfo, validateTmpDir, warnNodeDeprecations, showConfig } from './config';
|
|
16
17
|
import { readConfigFile } from './config-file';
|
|
17
18
|
import { DRIVER_TYPE, PLUGIN_TYPE } from './extension-config';
|
|
18
19
|
import registerNode from './grid-register';
|
|
19
|
-
import logger from './logger'; // logger needs to remain first of imports
|
|
20
20
|
import { init as logsinkInit } from './logsink';
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
21
|
+
import { getDefaultsForSchema } from './schema/schema';
|
|
22
|
+
import { inspect } from './utils';
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
25
|
*
|
|
@@ -32,14 +32,12 @@ async function preflightChecks (args, throwInsteadOfExit = false) {
|
|
|
32
32
|
if (args.longStacktrace) {
|
|
33
33
|
require('longjohn').async_trace_limit = -1;
|
|
34
34
|
}
|
|
35
|
-
if (args.
|
|
36
|
-
await
|
|
35
|
+
if (args.showBuildInfo) {
|
|
36
|
+
await showBuildInfo();
|
|
37
37
|
process.exit(0);
|
|
38
38
|
}
|
|
39
39
|
warnNodeDeprecations();
|
|
40
40
|
|
|
41
|
-
validate(args);
|
|
42
|
-
|
|
43
41
|
if (args.tmpDir) {
|
|
44
42
|
await validateTmpDir(args.tmpDir);
|
|
45
43
|
}
|
|
@@ -53,18 +51,24 @@ async function preflightChecks (args, throwInsteadOfExit = false) {
|
|
|
53
51
|
}
|
|
54
52
|
}
|
|
55
53
|
|
|
54
|
+
/**
|
|
55
|
+
* @param {Partial<ParsedArgs>} args
|
|
56
|
+
*/
|
|
56
57
|
function logNonDefaultArgsWarning (args) {
|
|
57
58
|
logger.info('Non-default server args:');
|
|
58
|
-
|
|
59
|
+
inspect(args);
|
|
59
60
|
}
|
|
60
61
|
|
|
61
62
|
function logDefaultCapabilitiesWarning (caps) {
|
|
62
63
|
logger.info('Default capabilities, which will be added to each request ' +
|
|
63
64
|
'unless overridden by desired capabilities:');
|
|
64
|
-
|
|
65
|
+
inspect(caps);
|
|
65
66
|
}
|
|
66
67
|
|
|
67
|
-
|
|
68
|
+
/**
|
|
69
|
+
* @param {ParsedArgs} args
|
|
70
|
+
*/
|
|
71
|
+
async function logStartupInfo (args) {
|
|
68
72
|
let welcome = `Welcome to Appium v${APPIUM_VER}`;
|
|
69
73
|
let appiumRev = await getGitRev();
|
|
70
74
|
if (appiumRev) {
|
|
@@ -72,7 +76,7 @@ async function logStartupInfo (parser, args) {
|
|
|
72
76
|
}
|
|
73
77
|
logger.info(welcome);
|
|
74
78
|
|
|
75
|
-
let showArgs = getNonDefaultServerArgs(
|
|
79
|
+
let showArgs = getNonDefaultServerArgs(args);
|
|
76
80
|
if (_.size(showArgs)) {
|
|
77
81
|
logNonDefaultArgsWarning(showArgs);
|
|
78
82
|
}
|
|
@@ -86,6 +90,12 @@ async function logStartupInfo (parser, args) {
|
|
|
86
90
|
// }
|
|
87
91
|
}
|
|
88
92
|
|
|
93
|
+
/**
|
|
94
|
+
* Logs the address and port the server is listening on
|
|
95
|
+
* @param {string} address - Address
|
|
96
|
+
* @param {number} port - Port
|
|
97
|
+
* @returns {void}
|
|
98
|
+
*/
|
|
89
99
|
function logServerPort (address, port) {
|
|
90
100
|
let logMessage = `Appium REST http interface listener started on ` +
|
|
91
101
|
`${address}:${port}`;
|
|
@@ -100,7 +110,7 @@ function logServerPort (address, port) {
|
|
|
100
110
|
*
|
|
101
111
|
* @param {Object} args - argparser parsed dict
|
|
102
112
|
* @param {import('./plugin-config').default} pluginConfig - a plugin extension config
|
|
103
|
-
* @returns {
|
|
113
|
+
* @returns {PluginExtensionClass[]}
|
|
104
114
|
*/
|
|
105
115
|
function getActivePlugins (args, pluginConfig) {
|
|
106
116
|
return _.compact(Object.keys(pluginConfig.installedExtensions).filter((pluginName) =>
|
|
@@ -109,7 +119,7 @@ function getActivePlugins (args, pluginConfig) {
|
|
|
109
119
|
).map((pluginName) => {
|
|
110
120
|
try {
|
|
111
121
|
logger.info(`Attempting to load plugin ${pluginName}...`);
|
|
112
|
-
const PluginClass = /** @type {
|
|
122
|
+
const PluginClass = /** @type {PluginExtensionClass} */(pluginConfig.require(pluginName));
|
|
113
123
|
|
|
114
124
|
PluginClass.pluginName = pluginName; // store the plugin name on the class so it can be used later
|
|
115
125
|
return PluginClass;
|
|
@@ -144,10 +154,22 @@ function getActiveDrivers (args, driverConfig) {
|
|
|
144
154
|
}));
|
|
145
155
|
}
|
|
146
156
|
|
|
157
|
+
/**
|
|
158
|
+
* Gets a list of `updateServer` functions from all extensions
|
|
159
|
+
* @param {DriverExtensionClass[]} driverClasses
|
|
160
|
+
* @param {PluginExtensionClass[]} pluginClasses
|
|
161
|
+
* @returns {StaticExtMembers['updateServer'][]}
|
|
162
|
+
*/
|
|
147
163
|
function getServerUpdaters (driverClasses, pluginClasses) {
|
|
148
|
-
return [...driverClasses, ...pluginClasses]
|
|
164
|
+
return _.compact(_.map([...driverClasses, ...pluginClasses], 'updateServer'));
|
|
149
165
|
}
|
|
150
166
|
|
|
167
|
+
/**
|
|
168
|
+
* Makes a big `MethodMap` from all the little `MethodMap`s in the extensions
|
|
169
|
+
* @param {DriverExtensionClass[]} driverClasses
|
|
170
|
+
* @param {PluginExtensionClass[]} pluginClasses
|
|
171
|
+
* @returns {import('@appium/base-driver').MethodMap}
|
|
172
|
+
*/
|
|
151
173
|
function getExtraMethodMap (driverClasses, pluginClasses) {
|
|
152
174
|
return [...driverClasses, ...pluginClasses].reduce(
|
|
153
175
|
(map, klass) => ({...map, ...klass.newMethodMap}),
|
|
@@ -160,19 +182,30 @@ function getExtraMethodMap (driverClasses, pluginClasses) {
|
|
|
160
182
|
*
|
|
161
183
|
* Use this to get at the configuration schema.
|
|
162
184
|
*
|
|
185
|
+
* If `args` contains a non-empty `subcommand` which is not `server`, this function
|
|
186
|
+
* will resolve with an empty object.
|
|
187
|
+
*
|
|
188
|
+
* @param {ParsedArgs} [args] - Parsed args
|
|
189
|
+
* @returns {Promise<Partial<{appiumDriver: AppiumDriver, parsedArgs: ParsedArgs}>>}
|
|
163
190
|
* @example
|
|
164
191
|
* import {init, getSchema} from 'appium';
|
|
165
192
|
* const options = {}; // config object
|
|
166
193
|
* await init(options);
|
|
167
194
|
* const schema = getSchema(); // entire config schema including plugins and drivers
|
|
168
|
-
* @param {ParsedArgs} [args] - Parsed args
|
|
169
|
-
* @returns {Promise<{parser: import('./cli/parser').ArgParser} & Partial<{appiumDriver: AppiumDriver, parsedArgs: ParsedArgs}>>}
|
|
170
195
|
*/
|
|
171
196
|
async function init (args) {
|
|
172
197
|
const parser = await getParser();
|
|
173
198
|
let throwInsteadOfExit = false;
|
|
174
199
|
/** @type {ParsedArgs} */
|
|
200
|
+
let preConfigParsedArgs;
|
|
201
|
+
/** @type {typeof preConfigParsedArgs & import('type-fest').AsyncReturnType<readConfigFile>['config']} */
|
|
175
202
|
let parsedArgs;
|
|
203
|
+
/**
|
|
204
|
+
* This is a definition (instead of declaration) because TS can't figure out
|
|
205
|
+
* the value will be defined when it's used.
|
|
206
|
+
* @type {ReturnType<getDefaultsForSchema>}
|
|
207
|
+
*/
|
|
208
|
+
let defaults = {};
|
|
176
209
|
if (args) {
|
|
177
210
|
// if we have a containing package instead of running as a CLI process,
|
|
178
211
|
// that package might not appreciate us calling 'process.exit' willy-
|
|
@@ -182,47 +215,52 @@ async function init (args) {
|
|
|
182
215
|
// but remove it since it's not a real server arg per se
|
|
183
216
|
delete args.throwInsteadOfExit;
|
|
184
217
|
}
|
|
185
|
-
|
|
218
|
+
preConfigParsedArgs = {...args, subcommand: args.subcommand ?? SERVER_SUBCOMMAND};
|
|
186
219
|
} else {
|
|
187
220
|
// otherwise parse from CLI
|
|
188
|
-
|
|
221
|
+
preConfigParsedArgs = parser.parseArgs();
|
|
189
222
|
}
|
|
190
223
|
|
|
191
|
-
const configResult = await readConfigFile(
|
|
224
|
+
const configResult = await readConfigFile(preConfigParsedArgs.configFile);
|
|
192
225
|
|
|
193
226
|
if (!_.isEmpty(configResult.errors)) {
|
|
194
227
|
throw new Error(`Errors in config file ${configResult.filepath}:\n ${configResult.reason ?? configResult.errors}`);
|
|
195
228
|
}
|
|
196
229
|
|
|
230
|
+
|
|
197
231
|
// merge config and apply defaults.
|
|
198
232
|
// the order of precendece is:
|
|
199
233
|
// 1. command line args
|
|
200
234
|
// 2. config file
|
|
201
235
|
// 3. defaults from config file.
|
|
202
|
-
if (
|
|
236
|
+
if (preConfigParsedArgs.subcommand === SERVER_SUBCOMMAND) {
|
|
237
|
+
defaults = getDefaultsForSchema(false);
|
|
238
|
+
|
|
239
|
+
if (preConfigParsedArgs.showConfig) {
|
|
240
|
+
showConfig(preConfigParsedArgs, configResult, defaults);
|
|
241
|
+
return {};
|
|
242
|
+
}
|
|
243
|
+
|
|
203
244
|
parsedArgs = _.defaultsDeep(
|
|
204
|
-
|
|
245
|
+
preConfigParsedArgs,
|
|
205
246
|
configResult.config?.server,
|
|
206
|
-
|
|
247
|
+
defaults
|
|
207
248
|
);
|
|
249
|
+
} else {
|
|
250
|
+
parsedArgs = preConfigParsedArgs;
|
|
208
251
|
}
|
|
209
252
|
|
|
210
|
-
parsedArgs = _.defaultsDeep(
|
|
211
|
-
parsedArgs,
|
|
212
|
-
configResult.config ?? {},
|
|
213
|
-
);
|
|
214
|
-
|
|
215
253
|
await logsinkInit(parsedArgs);
|
|
216
254
|
|
|
217
255
|
// if the user has requested the 'driver' CLI, don't run the normal server,
|
|
218
256
|
// but instead pass control to the driver CLI
|
|
219
257
|
if (parsedArgs.subcommand === DRIVER_TYPE) {
|
|
220
258
|
await runExtensionCommand(parsedArgs, parsedArgs.subcommand, driverConfig);
|
|
221
|
-
return {
|
|
259
|
+
return {};
|
|
222
260
|
}
|
|
223
261
|
if (parsedArgs.subcommand === PLUGIN_TYPE) {
|
|
224
262
|
await runExtensionCommand(parsedArgs, parsedArgs.subcommand, pluginConfig);
|
|
225
|
-
return {
|
|
263
|
+
return {};
|
|
226
264
|
}
|
|
227
265
|
|
|
228
266
|
if (parsedArgs.logFilters) {
|
|
@@ -243,16 +281,17 @@ async function init (args) {
|
|
|
243
281
|
appiumDriver.driverConfig = driverConfig;
|
|
244
282
|
await preflightChecks(parsedArgs, throwInsteadOfExit);
|
|
245
283
|
|
|
246
|
-
return {
|
|
284
|
+
return {appiumDriver, parsedArgs};
|
|
247
285
|
}
|
|
248
286
|
|
|
249
287
|
/**
|
|
250
|
-
*
|
|
251
|
-
*
|
|
252
|
-
* @
|
|
288
|
+
* Initializes Appium's config. Starts server if appropriate and resolves the
|
|
289
|
+
* server instance if so; otherwise resolves w/ `undefined`.
|
|
290
|
+
* @param {ParsedArgs} [args] - Arguments from CLI or otherwise
|
|
291
|
+
* @returns {Promise<import('express').Express|undefined>}
|
|
253
292
|
*/
|
|
254
293
|
async function main (args) {
|
|
255
|
-
const {
|
|
294
|
+
const {appiumDriver, parsedArgs} = await init(args);
|
|
256
295
|
|
|
257
296
|
if (!appiumDriver || !parsedArgs) {
|
|
258
297
|
// if this branch is taken, we've run a different subcommand, so there's nothing
|
|
@@ -264,7 +303,7 @@ async function main (args) {
|
|
|
264
303
|
// set the active plugins on the umbrella driver so it can use them for commands
|
|
265
304
|
appiumDriver.pluginClasses = pluginClasses;
|
|
266
305
|
|
|
267
|
-
await logStartupInfo(
|
|
306
|
+
await logStartupInfo(parsedArgs);
|
|
268
307
|
let routeConfiguringFunction = makeRouter(appiumDriver);
|
|
269
308
|
|
|
270
309
|
const driverClasses = getActiveDrivers(parsedArgs, driverConfig);
|
|
@@ -298,7 +337,6 @@ async function main (args) {
|
|
|
298
337
|
'to visit sites which could maliciously try to start Appium ' +
|
|
299
338
|
'sessions on your machine');
|
|
300
339
|
}
|
|
301
|
-
// @ts-ignore
|
|
302
340
|
appiumDriver.server = server;
|
|
303
341
|
try {
|
|
304
342
|
// configure as node on grid, if necessary
|
|
@@ -351,3 +389,15 @@ export { readConfigFile } from './config-file';
|
|
|
351
389
|
/**
|
|
352
390
|
* @typedef {import('../types/types').ParsedArgs} ParsedArgs
|
|
353
391
|
*/
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* @typedef {import('./appium').PluginExtensionClass} PluginExtensionClass
|
|
395
|
+
*/
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* @typedef {import('./appium').DriverExtensionClass} DriverExtensionClass
|
|
399
|
+
*/
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* @typedef {import('./appium').StaticExtMembers} StaticExtMembers
|
|
403
|
+
*/
|
package/lib/plugins.js
CHANGED