appium 2.0.0-beta.43 → 2.0.0-beta.45
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.d.ts +137 -15
- package/build/lib/appium.d.ts.map +1 -1
- package/build/lib/appium.js +5 -5
- package/build/lib/appium.js.map +1 -0
- package/build/lib/cli/args.js +3 -3
- package/build/lib/cli/args.js.map +1 -0
- package/build/lib/cli/driver-command.js +3 -3
- package/build/lib/cli/driver-command.js.map +1 -0
- package/build/lib/cli/extension-command.d.ts.map +1 -1
- package/build/lib/cli/extension-command.js +4 -4
- package/build/lib/cli/extension-command.js.map +1 -0
- package/build/lib/cli/extension.js +3 -3
- package/build/lib/cli/extension.js.map +1 -0
- package/build/lib/cli/parser.js +3 -3
- package/build/lib/cli/parser.js.map +1 -0
- package/build/lib/cli/plugin-command.js +3 -3
- package/build/lib/cli/plugin-command.js.map +1 -0
- package/build/lib/cli/utils.js +3 -3
- package/build/lib/cli/utils.js.map +1 -0
- package/build/lib/config-file.js +3 -3
- package/build/lib/config-file.js.map +1 -0
- package/build/lib/config.js +3 -3
- package/build/lib/config.js.map +1 -0
- package/build/lib/constants.js +3 -3
- package/build/lib/constants.js.map +1 -0
- package/build/lib/extension/driver-config.d.ts +3 -2
- package/build/lib/extension/driver-config.d.ts.map +1 -1
- package/build/lib/extension/driver-config.js +3 -3
- package/build/lib/extension/driver-config.js.map +1 -0
- package/build/lib/extension/extension-config.js +3 -3
- package/build/lib/extension/extension-config.js.map +1 -0
- package/build/lib/extension/index.js +3 -3
- package/build/lib/extension/index.js.map +1 -0
- package/build/lib/extension/manifest.d.ts +1 -2
- package/build/lib/extension/manifest.d.ts.map +1 -1
- package/build/lib/extension/manifest.js +35 -30
- package/build/lib/extension/manifest.js.map +1 -0
- package/build/lib/extension/package-changed.js +3 -3
- package/build/lib/extension/package-changed.js.map +1 -0
- package/build/lib/extension/plugin-config.js +3 -3
- package/build/lib/extension/plugin-config.js.map +1 -0
- package/build/lib/grid-register.js +3 -3
- package/build/lib/grid-register.js.map +1 -0
- package/build/lib/logger.js.map +1 -0
- package/build/lib/logsink.js +3 -3
- package/build/lib/logsink.js.map +1 -0
- package/build/lib/main.d.ts.map +1 -1
- package/build/lib/main.js +4 -3
- package/build/lib/main.js.map +1 -0
- package/build/lib/schema/arg-spec.js +3 -3
- package/build/lib/schema/arg-spec.js.map +1 -0
- package/build/lib/schema/cli-args.js +3 -3
- package/build/lib/schema/cli-args.js.map +1 -0
- package/build/lib/schema/cli-transformers.d.ts +1 -1
- package/build/lib/schema/cli-transformers.d.ts.map +1 -1
- package/build/lib/schema/cli-transformers.js +23 -18
- package/build/lib/schema/cli-transformers.js.map +1 -0
- package/build/lib/schema/index.js.map +1 -0
- package/build/lib/schema/keywords.js.map +1 -0
- package/build/lib/schema/schema.js +3 -3
- package/build/lib/schema/schema.js.map +1 -0
- package/build/lib/utils.d.ts +296 -27
- package/build/lib/utils.d.ts.map +1 -1
- package/build/lib/utils.js +60 -27
- package/build/lib/utils.js.map +1 -0
- package/build/tsconfig.tsbuildinfo +1 -1
- package/lib/appium.js +32 -18
- package/lib/cli/extension-command.js +2 -1
- package/lib/extension/driver-config.js +2 -1
- package/lib/extension/manifest.js +49 -32
- package/lib/main.js +3 -1
- package/lib/schema/cli-transformers.js +27 -23
- package/lib/utils.js +143 -62
- package/package.json +17 -18
- package/test.d.ts +0 -7
- package/test.js +0 -13
package/lib/appium.js
CHANGED
|
@@ -14,13 +14,9 @@ import AsyncLock from 'async-lock';
|
|
|
14
14
|
import {parseCapsForInnerDriver, pullSettings} from './utils';
|
|
15
15
|
import {util, node, logger} from '@appium/support';
|
|
16
16
|
import {getDefaultsForExtension} from './schema';
|
|
17
|
-
import {DRIVER_TYPE
|
|
17
|
+
import {DRIVER_TYPE} from './constants';
|
|
18
18
|
|
|
19
|
-
/**
|
|
20
|
-
* Invariant set of base constraints
|
|
21
|
-
* @type {Readonly<Constraints>}
|
|
22
|
-
*/
|
|
23
|
-
const desiredCapabilityConstraints = Object.freeze({
|
|
19
|
+
const desiredCapabilityConstraints = /** @type {const} */ ({
|
|
24
20
|
automationName: {
|
|
25
21
|
presence: true,
|
|
26
22
|
isString: true,
|
|
@@ -30,6 +26,9 @@ const desiredCapabilityConstraints = Object.freeze({
|
|
|
30
26
|
isString: true,
|
|
31
27
|
},
|
|
32
28
|
});
|
|
29
|
+
/**
|
|
30
|
+
* @typedef {typeof desiredCapabilityConstraints} AppiumDriverConstraints
|
|
31
|
+
*/
|
|
33
32
|
|
|
34
33
|
const sessionsListGuard = new AsyncLock();
|
|
35
34
|
const pendingDriversGuard = new AsyncLock();
|
|
@@ -181,10 +180,10 @@ class AppiumDriver extends DriverCore {
|
|
|
181
180
|
/**
|
|
182
181
|
* Retrieves all CLI arguments for a specific plugin.
|
|
183
182
|
* @param {string} extName - Plugin name
|
|
184
|
-
* @returns {
|
|
183
|
+
* @returns {StringRecord} Arguments object. If none, an empty object.
|
|
185
184
|
*/
|
|
186
185
|
getCliArgsForPlugin(extName) {
|
|
187
|
-
return /** @type {
|
|
186
|
+
return /** @type {StringRecord} */ (this.args.plugin?.[extName] ?? {});
|
|
188
187
|
}
|
|
189
188
|
|
|
190
189
|
/**
|
|
@@ -194,12 +193,10 @@ class AppiumDriver extends DriverCore {
|
|
|
194
193
|
*
|
|
195
194
|
* _Note that this behavior currently (May 18 2022) differs from how plugins are handled_ (see {@linkcode AppiumDriver.getCliArgsForPlugin}).
|
|
196
195
|
* @param {string} extName - Driver name
|
|
197
|
-
* @returns {
|
|
196
|
+
* @returns {StringRecord|undefined} Arguments object. If none, `undefined`
|
|
198
197
|
*/
|
|
199
198
|
getCliArgsForDriver(extName) {
|
|
200
|
-
const allCliArgsForExt = /** @type {
|
|
201
|
-
this.args.driver?.[extName]
|
|
202
|
-
);
|
|
199
|
+
const allCliArgsForExt = /** @type {StringRecord|undefined} */ (this.args.driver?.[extName]);
|
|
203
200
|
|
|
204
201
|
if (!_.isEmpty(allCliArgsForExt)) {
|
|
205
202
|
const defaults = getDefaultsForExtension(DRIVER_TYPE, extName);
|
|
@@ -214,9 +211,9 @@ class AppiumDriver extends DriverCore {
|
|
|
214
211
|
|
|
215
212
|
/**
|
|
216
213
|
* Create a new session
|
|
217
|
-
* @param {W3CCapabilities} jsonwpCaps JSONWP formatted desired capabilities
|
|
218
|
-
* @param {W3CCapabilities} reqCaps Required capabilities (JSONWP standard)
|
|
219
|
-
* @param {W3CCapabilities} w3cCapabilities W3C capabilities
|
|
214
|
+
* @param {W3CCapabilities<AppiumDriverConstraints>} jsonwpCaps JSONWP formatted desired capabilities
|
|
215
|
+
* @param {W3CCapabilities<AppiumDriverConstraints>} reqCaps Required capabilities (JSONWP standard)
|
|
216
|
+
* @param {W3CCapabilities<AppiumDriverConstraints>} w3cCapabilities W3C capabilities
|
|
220
217
|
* @param {DriverData[]} [driverData]
|
|
221
218
|
*/
|
|
222
219
|
async createSession(jsonwpCaps, reqCaps, w3cCapabilities, driverData) {
|
|
@@ -249,9 +246,11 @@ class AppiumDriver extends DriverCore {
|
|
|
249
246
|
);
|
|
250
247
|
|
|
251
248
|
const {desiredCaps, processedJsonwpCapabilities, processedW3CCapabilities} =
|
|
252
|
-
/** @type {import('./utils').ParsedDriverCaps} */ (parsedCaps);
|
|
249
|
+
/** @type {import('./utils').ParsedDriverCaps<AppiumDriverConstraints>} */ (parsedCaps);
|
|
253
250
|
protocol = parsedCaps.protocol;
|
|
254
|
-
const error = /** @type {import('./utils').InvalidCaps} */ (
|
|
251
|
+
const error = /** @type {import('./utils').InvalidCaps<AppiumDriverConstraints>} */ (
|
|
252
|
+
parsedCaps
|
|
253
|
+
).error;
|
|
255
254
|
// If the parsing of the caps produced an error, throw it in here
|
|
256
255
|
if (error) {
|
|
257
256
|
throw error;
|
|
@@ -783,6 +782,11 @@ class AppiumDriver extends DriverCore {
|
|
|
783
782
|
return dstSession && _.isFunction(dstSession.proxyActive) && dstSession.proxyActive(sessionId);
|
|
784
783
|
}
|
|
785
784
|
|
|
785
|
+
/**
|
|
786
|
+
*
|
|
787
|
+
* @param {string} sessionId
|
|
788
|
+
* @returns {import('@appium/types').RouteMatcher[]}
|
|
789
|
+
*/
|
|
786
790
|
getProxyAvoidList(sessionId) {
|
|
787
791
|
const dstSession = this.sessions[sessionId];
|
|
788
792
|
return dstSession ? dstSession.getProxyAvoidList() : [];
|
|
@@ -826,7 +830,6 @@ export {AppiumDriver};
|
|
|
826
830
|
* @typedef {import('@appium/types').ExternalDriver} ExternalDriver
|
|
827
831
|
* @typedef {import('@appium/types').Driver} Driver
|
|
828
832
|
* @typedef {import('@appium/types').DriverClass} DriverClass
|
|
829
|
-
* @typedef {import('@appium/types').W3CCapabilities} W3CCapabilities
|
|
830
833
|
* @typedef {import('@appium/types').DriverData} DriverData
|
|
831
834
|
* @typedef {import('@appium/types').ServerArgs} DriverOpts
|
|
832
835
|
* @typedef {import('@appium/types').Constraints} Constraints
|
|
@@ -837,6 +840,7 @@ export {AppiumDriver};
|
|
|
837
840
|
* @typedef {import('@appium/types').PluginClass} PluginClass
|
|
838
841
|
* @typedef {import('@appium/types').PluginType} PluginType
|
|
839
842
|
* @typedef {import('@appium/types').DriverType} DriverType
|
|
843
|
+
* @typedef {import('@appium/types').StringRecord} StringRecord
|
|
840
844
|
* @typedef {import('@appium/types').SessionHandler<SessionHandlerResult<any[]>,SessionHandlerResult<void>>} SessionHandler
|
|
841
845
|
*/
|
|
842
846
|
|
|
@@ -849,3 +853,13 @@ export {AppiumDriver};
|
|
|
849
853
|
* @property {Error} [error]
|
|
850
854
|
* @property {string} [protocol]
|
|
851
855
|
*/
|
|
856
|
+
|
|
857
|
+
/**
|
|
858
|
+
* @template {Constraints} C
|
|
859
|
+
* @typedef {import('@appium/types').W3CCapabilities<C>} W3CCapabilities
|
|
860
|
+
*/
|
|
861
|
+
|
|
862
|
+
/**
|
|
863
|
+
* @template {Constraints} C
|
|
864
|
+
* @typedef {import('@appium/types').Capabilities<C>} Capabilities
|
|
865
|
+
*/
|
|
@@ -576,13 +576,14 @@ class ExtensionCommand {
|
|
|
576
576
|
// this is a helper method, 'ext' is assumed to already be installed here, and of the npm
|
|
577
577
|
// install type
|
|
578
578
|
const {version, pkgName} = this.config.installedExtensions[ext];
|
|
579
|
+
/** @type {string?} */
|
|
579
580
|
let unsafeUpdate = await npm.getLatestVersion(this.config.appiumHome, pkgName);
|
|
580
581
|
let safeUpdate = await npm.getLatestSafeUpgradeVersion(
|
|
581
582
|
this.config.appiumHome,
|
|
582
583
|
pkgName,
|
|
583
584
|
version
|
|
584
585
|
);
|
|
585
|
-
if (!util.compareVersions(unsafeUpdate, '>', version)) {
|
|
586
|
+
if (unsafeUpdate !== null && !util.compareVersions(unsafeUpdate, '>', version)) {
|
|
586
587
|
// the latest version is not greater than the current version, so there's no possible update
|
|
587
588
|
unsafeUpdate = null;
|
|
588
589
|
safeUpdate = null;
|
|
@@ -133,7 +133,8 @@ export class DriverConfig extends ExtensionConfig {
|
|
|
133
133
|
|
|
134
134
|
/**
|
|
135
135
|
* Given capabilities, find a matching driver within the config. Load its class and return it along with version and driver name.
|
|
136
|
-
* @
|
|
136
|
+
* @template {import('@appium/types').StringRecord} C
|
|
137
|
+
* @param {C} caps
|
|
137
138
|
* @returns {MatchedDriver}
|
|
138
139
|
*/
|
|
139
140
|
findMatchingDriver({automationName, platformName}) {
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
* Module containing {@link Manifest} which handles reading & writing of extension config files.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
import B from 'bluebird';
|
|
6
|
+
import glob from 'glob';
|
|
5
7
|
import {env, fs} from '@appium/support';
|
|
6
8
|
import _ from 'lodash';
|
|
7
9
|
import path from 'path';
|
|
@@ -11,23 +13,6 @@ import log from '../logger';
|
|
|
11
13
|
import {INSTALL_TYPE_NPM} from './extension-config';
|
|
12
14
|
import {packageDidChange} from './package-changed';
|
|
13
15
|
|
|
14
|
-
/**
|
|
15
|
-
* Default depth to search in directory tree for whatever it is we're looking for.
|
|
16
|
-
*
|
|
17
|
-
* It's 4 because smaller numbers didn't work.
|
|
18
|
-
*/
|
|
19
|
-
const DEFAULT_SEARCH_DEPTH = 4;
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Default options for {@link findExtensions}.
|
|
23
|
-
* @type {Readonly<import('klaw').Options>}
|
|
24
|
-
*/
|
|
25
|
-
const DEFAULT_FIND_EXTENSIONS_OPTS = Object.freeze({
|
|
26
|
-
depthLimit: DEFAULT_SEARCH_DEPTH,
|
|
27
|
-
/* istanbul ignore next */
|
|
28
|
-
filter: (filepath) => !path.basename(filepath).startsWith('.'),
|
|
29
|
-
});
|
|
30
|
-
|
|
31
16
|
/**
|
|
32
17
|
* Current configuration schema revision!
|
|
33
18
|
*/
|
|
@@ -175,26 +160,58 @@ export class Manifest {
|
|
|
175
160
|
|
|
176
161
|
/**
|
|
177
162
|
* Searches `APPIUM_HOME` for installed extensions and adds them to the manifest.
|
|
178
|
-
* @param {SyncWithInstalledExtensionsOpts} opts
|
|
179
163
|
* @returns {Promise<boolean>} `true` if any extensions were added, `false` otherwise.
|
|
180
164
|
*/
|
|
181
|
-
async syncWithInstalledExtensions(
|
|
182
|
-
const walkOpts = _.defaults({depthLimit}, DEFAULT_FIND_EXTENSIONS_OPTS);
|
|
165
|
+
async syncWithInstalledExtensions() {
|
|
183
166
|
// this could be parallelized, but we can't use fs.walk as an async iterator
|
|
184
167
|
let didChange = false;
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Listener for the `match` event of a `glob` instance
|
|
171
|
+
* @param {string} filepath - Path to a `package.json`
|
|
172
|
+
* @returns {Promise<void>}
|
|
173
|
+
*/
|
|
174
|
+
const onMatch = async (filepath) => {
|
|
175
|
+
try {
|
|
176
|
+
const pkg = JSON.parse(await fs.readFile(filepath, 'utf8'));
|
|
177
|
+
if (isDriver(pkg) || isPlugin(pkg)) {
|
|
178
|
+
const changed = this.addExtensionFromPackage(pkg, filepath);
|
|
179
|
+
didChange = didChange || changed;
|
|
180
|
+
}
|
|
181
|
+
} catch {}
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* A list of `Promise`s which read `package.json` files looking for Appium extensions.
|
|
186
|
+
* @type {Promise<void>[]}
|
|
187
|
+
*/
|
|
188
|
+
const queue = [
|
|
189
|
+
// look at `package.json` in `APPIUM_HOME` only
|
|
190
|
+
onMatch(path.join(this._appiumHome, 'package.json')),
|
|
191
|
+
];
|
|
192
|
+
|
|
193
|
+
// add dependencies to the queue
|
|
194
|
+
await new B((resolve, reject) => {
|
|
195
|
+
glob(
|
|
196
|
+
'node_modules/{*,@*/*}/package.json',
|
|
197
|
+
{cwd: this._appiumHome, silent: true, absolute: true},
|
|
198
|
+
// eslint-disable-next-line promise/prefer-await-to-callbacks
|
|
199
|
+
(err) => {
|
|
200
|
+
if (err) {
|
|
201
|
+
reject(err);
|
|
194
202
|
}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
203
|
+
resolve();
|
|
204
|
+
}
|
|
205
|
+
)
|
|
206
|
+
.on('error', reject)
|
|
207
|
+
.on('match', (filepath) => {
|
|
208
|
+
queue.push(onMatch(filepath));
|
|
209
|
+
});
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
// wait for everything to finish
|
|
213
|
+
await B.all(queue);
|
|
214
|
+
|
|
198
215
|
return didChange;
|
|
199
216
|
}
|
|
200
217
|
|
package/lib/main.js
CHANGED
|
@@ -26,7 +26,7 @@ import {loadExtensions, getActivePlugins, getActiveDrivers} from './extension';
|
|
|
26
26
|
import {DRIVER_TYPE, PLUGIN_TYPE, SERVER_SUBCOMMAND} from './constants';
|
|
27
27
|
import registerNode from './grid-register';
|
|
28
28
|
import {getDefaultsForSchema, validate} from './schema/schema';
|
|
29
|
-
import {inspect} from './utils';
|
|
29
|
+
import {inspect, adjustNodePath} from './utils';
|
|
30
30
|
|
|
31
31
|
const {resolveAppiumHome} = env;
|
|
32
32
|
|
|
@@ -172,6 +172,8 @@ function areServerCommandArgs(args) {
|
|
|
172
172
|
async function init(args) {
|
|
173
173
|
const appiumHome = args?.appiumHome ?? (await resolveAppiumHome());
|
|
174
174
|
|
|
175
|
+
adjustNodePath();
|
|
176
|
+
|
|
175
177
|
const {driverConfig, pluginConfig} = await loadExtensions(appiumHome);
|
|
176
178
|
|
|
177
179
|
const parser = getParser();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {ArgumentTypeError} from 'argparse';
|
|
2
|
-
import {readFileSync} from 'fs';
|
|
2
|
+
import {readFileSync, existsSync} from 'fs';
|
|
3
3
|
import _ from 'lodash';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -55,25 +55,30 @@ export const transformers = {
|
|
|
55
55
|
/**
|
|
56
56
|
* Given a CSV-style string or pathname, parse it into an array.
|
|
57
57
|
* The file can also be split on newlines.
|
|
58
|
-
* @param {string}
|
|
58
|
+
* @param {string} csvOrPath
|
|
59
59
|
* @returns {string[]}
|
|
60
60
|
*/
|
|
61
|
-
csv: (
|
|
62
|
-
let
|
|
61
|
+
csv: (csvOrPath) => {
|
|
62
|
+
let csv = csvOrPath;
|
|
63
|
+
let loadedFromFile = false;
|
|
63
64
|
// since this value could be a single string (no commas) _or_ a pathname, we will need
|
|
64
65
|
// to attempt to parse it as a file _first_.
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
throw new ArgumentTypeError(`Could not read file ${
|
|
66
|
+
if (existsSync(csvOrPath)) {
|
|
67
|
+
try {
|
|
68
|
+
csv = readFileSync(csvOrPath, 'utf8');
|
|
69
|
+
} catch (err) {
|
|
70
|
+
throw new ArgumentTypeError(`Could not read file '${csvOrPath}': ${err.message}`);
|
|
70
71
|
}
|
|
72
|
+
loadedFromFile = true;
|
|
71
73
|
}
|
|
72
74
|
|
|
73
75
|
try {
|
|
74
|
-
return
|
|
76
|
+
return loadedFromFile ? parseCsvFile(csv) : parseCsvLine(csv);
|
|
75
77
|
} catch (err) {
|
|
76
|
-
|
|
78
|
+
const msg = loadedFromFile
|
|
79
|
+
? `The provided value of '${csvOrPath}' must be a valid CSV`
|
|
80
|
+
: `Must be a comma-delimited string, e.g., "foo,bar,baz"`;
|
|
81
|
+
throw new TypeError(`${msg}. Original error: ${err.message}`);
|
|
77
82
|
}
|
|
78
83
|
},
|
|
79
84
|
|
|
@@ -85,19 +90,18 @@ export const transformers = {
|
|
|
85
90
|
json: (jsonOrPath) => {
|
|
86
91
|
let json = jsonOrPath;
|
|
87
92
|
let loadedFromFile = false;
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
if (err.code !== 'ENOENT') {
|
|
99
|
-
throw err;
|
|
93
|
+
if (existsSync(jsonOrPath)) {
|
|
94
|
+
try {
|
|
95
|
+
// use synchronous file access, as `argparse` provides no way of either
|
|
96
|
+
// awaiting or using callbacks. This step happens in startup, in what is
|
|
97
|
+
// effectively command-line code, so nothing is blocked in terms of
|
|
98
|
+
// sessions, so holding up the event loop does not incur the usual
|
|
99
|
+
// drawbacks.
|
|
100
|
+
json = readFileSync(jsonOrPath, 'utf8');
|
|
101
|
+
} catch (err) {
|
|
102
|
+
throw new ArgumentTypeError(`Could not read file '${jsonOrPath}': ${err.message}`);
|
|
100
103
|
}
|
|
104
|
+
loadedFromFile = true;
|
|
101
105
|
}
|
|
102
106
|
try {
|
|
103
107
|
const result = JSON.parse(json);
|
package/lib/utils.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import _ from 'lodash';
|
|
2
2
|
import logger from './logger';
|
|
3
|
-
import {processCapabilities, PROTOCOLS} from '@appium/base-driver';
|
|
3
|
+
import {processCapabilities, PROTOCOLS, STANDARD_CAPS} from '@appium/base-driver';
|
|
4
4
|
import {inspect as dump} from 'util';
|
|
5
|
+
import {node} from '@appium/support';
|
|
6
|
+
import path from 'path';
|
|
5
7
|
|
|
6
8
|
const W3C_APPIUM_PREFIX = 'appium';
|
|
9
|
+
const STANDARD_CAPS_LOWERCASE = new Set([...STANDARD_CAPS].map((cap) => cap.toLowerCase()));
|
|
7
10
|
|
|
8
11
|
/**
|
|
9
12
|
*
|
|
@@ -33,16 +36,18 @@ const inspect = _.flow(
|
|
|
33
36
|
* Takes the caps that were provided in the request and translates them
|
|
34
37
|
* into caps that can be used by the inner drivers.
|
|
35
38
|
*
|
|
36
|
-
* @
|
|
37
|
-
* @
|
|
38
|
-
* @param {
|
|
39
|
-
* @param {
|
|
40
|
-
* @
|
|
39
|
+
* @template {Constraints} C
|
|
40
|
+
* @template [J=any]
|
|
41
|
+
* @param {J} jsonwpCapabilities
|
|
42
|
+
* @param {W3CCapabilities<C>} w3cCapabilities
|
|
43
|
+
* @param {C} constraints
|
|
44
|
+
* @param {NSCapabilities<C>} [defaultCapabilities]
|
|
45
|
+
* @returns {ParsedDriverCaps<C,J>|InvalidCaps<C,J>}
|
|
41
46
|
*/
|
|
42
47
|
function parseCapsForInnerDriver(
|
|
43
48
|
jsonwpCapabilities,
|
|
44
49
|
w3cCapabilities,
|
|
45
|
-
constraints = {},
|
|
50
|
+
constraints = /** @type {C} */ ({}),
|
|
46
51
|
defaultCapabilities = {}
|
|
47
52
|
) {
|
|
48
53
|
// Check if the caller sent JSONWP caps, W3C caps, or both
|
|
@@ -50,14 +55,14 @@ function parseCapsForInnerDriver(
|
|
|
50
55
|
_.isPlainObject(w3cCapabilities) &&
|
|
51
56
|
(_.has(w3cCapabilities, 'alwaysMatch') || _.has(w3cCapabilities, 'firstMatch'));
|
|
52
57
|
const hasJSONWPCaps = _.isPlainObject(jsonwpCapabilities);
|
|
53
|
-
let desiredCaps = /** @type {ParsedDriverCaps['desiredCaps']} */ ({});
|
|
54
|
-
/** @type {ParsedDriverCaps['processedW3CCapabilities']} */
|
|
58
|
+
let desiredCaps = /** @type {ParsedDriverCaps<C>['desiredCaps']} */ ({});
|
|
59
|
+
/** @type {ParsedDriverCaps<C>['processedW3CCapabilities']} */
|
|
55
60
|
let processedW3CCapabilities;
|
|
56
|
-
/** @type {ParsedDriverCaps['processedJsonwpCapabilities']} */
|
|
61
|
+
/** @type {ParsedDriverCaps<C>['processedJsonwpCapabilities']} */
|
|
57
62
|
let processedJsonwpCapabilities;
|
|
58
63
|
|
|
59
64
|
if (!hasW3CCaps) {
|
|
60
|
-
return /** @type {InvalidCaps} */ ({
|
|
65
|
+
return /** @type {InvalidCaps<C>} */ ({
|
|
61
66
|
protocol: PROTOCOLS.W3C,
|
|
62
67
|
error: new Error('W3C capabilities should be provided'),
|
|
63
68
|
});
|
|
@@ -76,7 +81,7 @@ function parseCapsForInnerDriver(
|
|
|
76
81
|
for (const [defaultCapKey, defaultCapValue] of _.toPairs(defaultCapabilities)) {
|
|
77
82
|
let isCapAlreadySet = false;
|
|
78
83
|
// Check if the key is already present in firstMatch entries
|
|
79
|
-
for (const firstMatchEntry of w3cCapabilities.firstMatch
|
|
84
|
+
for (const firstMatchEntry of w3cCapabilities.firstMatch ?? []) {
|
|
80
85
|
if (
|
|
81
86
|
_.isPlainObject(firstMatchEntry) &&
|
|
82
87
|
_.has(removeAppiumPrefixes(firstMatchEntry), removeAppiumPrefix(defaultCapKey))
|
|
@@ -100,7 +105,9 @@ function parseCapsForInnerDriver(
|
|
|
100
105
|
|
|
101
106
|
// Only add the default capability if it is not overridden
|
|
102
107
|
if (_.isEmpty(w3cCapabilities.firstMatch)) {
|
|
103
|
-
w3cCapabilities.firstMatch =
|
|
108
|
+
w3cCapabilities.firstMatch = /** @type {W3CCapabilities<C>['firstMatch']} */ ([
|
|
109
|
+
{[defaultCapKey]: defaultCapValue},
|
|
110
|
+
]);
|
|
104
111
|
} else {
|
|
105
112
|
w3cCapabilities.firstMatch[0][defaultCapKey] = defaultCapValue;
|
|
106
113
|
}
|
|
@@ -127,7 +134,7 @@ function parseCapsForInnerDriver(
|
|
|
127
134
|
desiredCaps = processCapabilities(w3cCapabilities, constraints, true);
|
|
128
135
|
} catch (error) {
|
|
129
136
|
logger.info(`Could not parse W3C capabilities: ${error.message}`);
|
|
130
|
-
return /** @type {InvalidCaps} */ ({
|
|
137
|
+
return /** @type {InvalidCaps<C,J>} */ ({
|
|
131
138
|
desiredCaps,
|
|
132
139
|
processedJsonwpCapabilities,
|
|
133
140
|
processedW3CCapabilities,
|
|
@@ -143,7 +150,7 @@ function parseCapsForInnerDriver(
|
|
|
143
150
|
};
|
|
144
151
|
}
|
|
145
152
|
|
|
146
|
-
return /** @type {ParsedDriverCaps} */ ({
|
|
153
|
+
return /** @type {ParsedDriverCaps<C,J>} */ ({
|
|
147
154
|
desiredCaps,
|
|
148
155
|
processedJsonwpCapabilities,
|
|
149
156
|
processedW3CCapabilities,
|
|
@@ -153,62 +160,101 @@ function parseCapsForInnerDriver(
|
|
|
153
160
|
|
|
154
161
|
/**
|
|
155
162
|
* Takes a capabilities objects and prefixes capabilities with `appium:`
|
|
156
|
-
* @
|
|
157
|
-
* @
|
|
163
|
+
* @template {Constraints} [C={}]
|
|
164
|
+
* @param {Capabilities<C>} caps - Desired capabilities object
|
|
165
|
+
* @returns {NSCapabilities<C>}
|
|
158
166
|
*/
|
|
159
167
|
function insertAppiumPrefixes(caps) {
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
'proxy',
|
|
168
|
-
'setWindowRect',
|
|
169
|
-
'timeouts',
|
|
170
|
-
'unhandledPromptBehavior',
|
|
171
|
-
];
|
|
172
|
-
|
|
173
|
-
let prefixedCaps = {};
|
|
174
|
-
for (let [name, value] of _.toPairs(caps)) {
|
|
175
|
-
if (STANDARD_CAPS.includes(name) || name.includes(':')) {
|
|
176
|
-
prefixedCaps[name] = value;
|
|
177
|
-
} else {
|
|
178
|
-
prefixedCaps[`${W3C_APPIUM_PREFIX}:${name}`] = value;
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
return prefixedCaps;
|
|
168
|
+
return /** @type {NSCapabilities<C>} */ (
|
|
169
|
+
_.mapKeys(caps, (_, key) =>
|
|
170
|
+
STANDARD_CAPS_LOWERCASE.has(key.toLowerCase()) || key.includes(':')
|
|
171
|
+
? key
|
|
172
|
+
: `${W3C_APPIUM_PREFIX}:${key}`
|
|
173
|
+
)
|
|
174
|
+
);
|
|
182
175
|
}
|
|
183
176
|
|
|
184
177
|
/**
|
|
185
|
-
*
|
|
186
|
-
* @param {
|
|
187
|
-
* @returns {Capabilities}
|
|
178
|
+
* @template {Constraints} [C={}]
|
|
179
|
+
* @param {NSCapabilities<C>} caps
|
|
180
|
+
* @returns {Capabilities<C>}
|
|
188
181
|
*/
|
|
189
182
|
function removeAppiumPrefixes(caps) {
|
|
190
|
-
|
|
191
|
-
return caps;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
/** @type {Capabilities} */
|
|
195
|
-
const fixedCaps = {};
|
|
196
|
-
for (let [name, value] of _.toPairs(caps)) {
|
|
197
|
-
fixedCaps[removeAppiumPrefix(name)] = value;
|
|
198
|
-
}
|
|
199
|
-
return fixedCaps;
|
|
183
|
+
return /** @type {Capabilities<C>} */ (_.mapKeys(caps, (_, key) => removeAppiumPrefix(key)));
|
|
200
184
|
}
|
|
201
185
|
|
|
186
|
+
/**
|
|
187
|
+
* @param {string} key
|
|
188
|
+
* @returns {string}
|
|
189
|
+
*/
|
|
202
190
|
function removeAppiumPrefix(key) {
|
|
203
191
|
const prefix = `${W3C_APPIUM_PREFIX}:`;
|
|
204
192
|
return _.startsWith(key, prefix) ? key.substring(prefix.length) : key;
|
|
205
193
|
}
|
|
206
194
|
|
|
195
|
+
/**
|
|
196
|
+
*
|
|
197
|
+
* @param {string} pkgName
|
|
198
|
+
* @returns {string|undefined}
|
|
199
|
+
*/
|
|
207
200
|
function getPackageVersion(pkgName) {
|
|
208
201
|
const pkgInfo = require(`${pkgName}/package.json`) || {};
|
|
209
202
|
return pkgInfo.version;
|
|
210
203
|
}
|
|
211
204
|
|
|
205
|
+
/**
|
|
206
|
+
* Adjusts NODE_PATH environment variable,
|
|
207
|
+
* so drivers and plugins could load their peer dependencies.
|
|
208
|
+
* Read https://nodejs.org/api/modules.html#loading-from-the-global-folders
|
|
209
|
+
* for more details.
|
|
210
|
+
* @returns {void}
|
|
211
|
+
*/
|
|
212
|
+
function adjustNodePath() {
|
|
213
|
+
const selfRoot = node.getModuleRootSync('appium', __filename);
|
|
214
|
+
if (!selfRoot || path.dirname(selfRoot).length >= selfRoot.length) {
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
const nodeModulesRoot = path.dirname(selfRoot);
|
|
218
|
+
|
|
219
|
+
const refreshRequirePaths = () => {
|
|
220
|
+
try {
|
|
221
|
+
// ! This hack allows us to avoid modification of import
|
|
222
|
+
// ! statements in client modules. It uses a private API though,
|
|
223
|
+
// ! so it could break (maybe, eventually).
|
|
224
|
+
// See https://gist.github.com/branneman/8048520#7-the-hack
|
|
225
|
+
// @ts-ignore
|
|
226
|
+
require('module').Module._initPaths();
|
|
227
|
+
return true;
|
|
228
|
+
} catch (e) {
|
|
229
|
+
return false;
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
if (!process.env.NODE_PATH) {
|
|
234
|
+
process.env.NODE_PATH = nodeModulesRoot;
|
|
235
|
+
if (refreshRequirePaths()) {
|
|
236
|
+
process.env.APPIUM_OMIT_PEER_DEPS = '1';
|
|
237
|
+
} else {
|
|
238
|
+
delete process.env.NODE_PATH;
|
|
239
|
+
}
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const nodePathParts = process.env.NODE_PATH.split(path.delimiter);
|
|
244
|
+
if (nodePathParts.includes(nodeModulesRoot)) {
|
|
245
|
+
process.env.APPIUM_OMIT_PEER_DEPS = '1';
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
nodePathParts.push(nodeModulesRoot);
|
|
250
|
+
process.env.NODE_PATH = nodePathParts.join(path.delimiter);
|
|
251
|
+
if (refreshRequirePaths()) {
|
|
252
|
+
process.env.APPIUM_OMIT_PEER_DEPS = '1';
|
|
253
|
+
} else {
|
|
254
|
+
process.env.NODE_PATH = _.without(nodePathParts, nodeModulesRoot).join(path.delimiter);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
212
258
|
/**
|
|
213
259
|
* Pulls the initial values of Appium settings from the given capabilities argument.
|
|
214
260
|
* Each setting item must satisfy the following format:
|
|
@@ -250,29 +296,64 @@ export {
|
|
|
250
296
|
getPackageVersion,
|
|
251
297
|
pullSettings,
|
|
252
298
|
removeAppiumPrefixes,
|
|
299
|
+
adjustNodePath,
|
|
253
300
|
};
|
|
254
301
|
|
|
255
302
|
/**
|
|
256
|
-
* @
|
|
303
|
+
* @typedef {import('@appium/types').StringRecord} StringRecord
|
|
304
|
+
* @typedef {import('@appium/types').BaseDriverCapConstraints} BaseDriverCapConstraints
|
|
305
|
+
*/
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* @template {Constraints} [C=BaseDriverCapConstraints]
|
|
309
|
+
* @template [J=any]
|
|
257
310
|
* @typedef ParsedDriverCaps
|
|
258
|
-
* @property {Capabilities} desiredCaps
|
|
311
|
+
* @property {Capabilities<C>} desiredCaps
|
|
259
312
|
* @property {string} protocol
|
|
260
|
-
* @property {
|
|
261
|
-
* @property {W3CCapabilities} [processedW3CCapabilities]
|
|
313
|
+
* @property {J} [processedJsonwpCapabilities]
|
|
314
|
+
* @property {W3CCapabilities<C>} [processedW3CCapabilities]
|
|
262
315
|
*/
|
|
263
316
|
|
|
264
317
|
/**
|
|
265
318
|
* @todo protocol is more specific
|
|
319
|
+
* @template {Constraints} [C=BaseDriverCapConstraints]
|
|
320
|
+
* @template [J=any]
|
|
266
321
|
* @typedef InvalidCaps
|
|
267
322
|
* @property {Error} error
|
|
268
323
|
* @property {string} protocol
|
|
269
|
-
* @property {Capabilities} [desiredCaps]
|
|
270
|
-
* @property {
|
|
271
|
-
* @property {W3CCapabilities} [processedW3CCapabilities]
|
|
324
|
+
* @property {Capabilities<C>} [desiredCaps]
|
|
325
|
+
* @property {J} [processedJsonwpCapabilities]
|
|
326
|
+
* @property {W3CCapabilities<C>} [processedW3CCapabilities]
|
|
327
|
+
*/
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* @template {Constraints} [C=BaseDriverCapConstraints]
|
|
331
|
+
* @template {StringRecord|void} [Extra=void]
|
|
332
|
+
* @typedef {import('@appium/types').Capabilities<C, Extra>} Capabilities
|
|
333
|
+
*/
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* @template {Constraints} [C=BaseDriverCapConstraints]
|
|
337
|
+
* @template {StringRecord|void} [Extra=void]
|
|
338
|
+
* @typedef {import('@appium/types').W3CCapabilities<C, Extra>} W3CCapabilities
|
|
339
|
+
*/
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* @template {Constraints} [C=BaseDriverCapConstraints]
|
|
343
|
+
* @template {StringRecord|void} [Extra=void]
|
|
344
|
+
* @typedef {import('@appium/types').NSCapabilities<C, Extra>} NSCapabilities
|
|
345
|
+
*/
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* @template {Constraints} C
|
|
349
|
+
* @typedef {import('@appium/types').ConstraintsToCaps<C>} ConstraintsToCaps
|
|
350
|
+
*/
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* @template T
|
|
354
|
+
* @typedef {import('type-fest').StringKeyOf<T>} StringKeyOf
|
|
272
355
|
*/
|
|
273
356
|
|
|
274
357
|
/**
|
|
275
|
-
* @typedef {import('@appium/types').
|
|
276
|
-
* @typedef {import('@appium/types').Capabilities} Capabilities
|
|
277
|
-
* @typedef {import('@appium/types').AppiumW3CCapabilities} AppiumW3CCapabilities
|
|
358
|
+
* @typedef {import('@appium/types').Constraints} Constraints
|
|
278
359
|
*/
|