appium 2.0.0-beta.3 → 2.0.0-beta.34
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 +10 -11
- package/build/lib/appium.d.ts +215 -0
- package/build/lib/appium.d.ts.map +1 -0
- package/build/lib/appium.js +241 -132
- package/build/lib/cli/args.d.ts +20 -0
- package/build/lib/cli/args.d.ts.map +1 -0
- package/build/lib/cli/args.js +96 -282
- package/build/lib/cli/driver-command.d.ts +36 -0
- package/build/lib/cli/driver-command.d.ts.map +1 -0
- package/build/lib/cli/driver-command.js +19 -12
- package/build/lib/cli/extension-command.d.ts +345 -0
- package/build/lib/cli/extension-command.d.ts.map +1 -0
- package/build/lib/cli/extension-command.js +171 -96
- package/build/lib/cli/extension.d.ts +14 -0
- package/build/lib/cli/extension.d.ts.map +1 -0
- package/build/lib/cli/extension.js +31 -16
- package/build/lib/cli/parser.d.ts +80 -0
- package/build/lib/cli/parser.d.ts.map +1 -0
- package/build/lib/cli/parser.js +152 -95
- package/build/lib/cli/plugin-command.d.ts +39 -0
- package/build/lib/cli/plugin-command.d.ts.map +1 -0
- package/build/lib/cli/plugin-command.js +18 -13
- package/build/lib/cli/utils.d.ts +29 -0
- package/build/lib/cli/utils.d.ts.map +1 -0
- package/build/lib/cli/utils.js +27 -3
- package/build/lib/config-file.d.ts +100 -0
- package/build/lib/config-file.d.ts.map +1 -0
- package/build/lib/config-file.js +136 -0
- package/build/lib/config.d.ts +41 -0
- package/build/lib/config.d.ts.map +1 -0
- package/build/lib/config.js +92 -67
- package/build/lib/constants.d.ts +48 -0
- package/build/lib/constants.d.ts.map +1 -0
- package/build/lib/constants.js +60 -0
- package/build/lib/extension/driver-config.d.ts +84 -0
- package/build/lib/extension/driver-config.d.ts.map +1 -0
- package/build/lib/extension/driver-config.js +190 -0
- package/build/lib/extension/extension-config.d.ts +170 -0
- package/build/lib/extension/extension-config.d.ts.map +1 -0
- package/build/lib/extension/extension-config.js +297 -0
- package/build/lib/extension/index.d.ts +39 -0
- package/build/lib/extension/index.d.ts.map +1 -0
- package/build/lib/extension/index.js +77 -0
- package/build/lib/extension/manifest.d.ts +174 -0
- package/build/lib/extension/manifest.d.ts.map +1 -0
- package/build/lib/extension/manifest.js +246 -0
- package/build/lib/extension/package-changed.d.ts +11 -0
- package/build/lib/extension/package-changed.d.ts.map +1 -0
- package/build/lib/extension/package-changed.js +68 -0
- package/build/lib/extension/plugin-config.d.ts +62 -0
- package/build/lib/extension/plugin-config.d.ts.map +1 -0
- package/build/lib/extension/plugin-config.js +87 -0
- package/build/lib/grid-register.d.ts +10 -0
- package/build/lib/grid-register.d.ts.map +1 -0
- package/build/lib/grid-register.js +21 -25
- package/build/lib/logger.d.ts +3 -0
- package/build/lib/logger.d.ts.map +1 -0
- package/build/lib/logger.js +4 -6
- package/build/lib/logsink.d.ts +4 -0
- package/build/lib/logsink.d.ts.map +1 -0
- package/build/lib/logsink.js +12 -16
- package/build/lib/main.d.ts +54 -0
- package/build/lib/main.d.ts.map +1 -0
- package/build/lib/main.js +189 -90
- package/build/lib/schema/arg-spec.d.ts +143 -0
- package/build/lib/schema/arg-spec.d.ts.map +1 -0
- package/build/lib/schema/arg-spec.js +119 -0
- package/build/lib/schema/cli-args.d.ts +19 -0
- package/build/lib/schema/cli-args.d.ts.map +1 -0
- package/build/lib/schema/cli-args.js +180 -0
- package/build/lib/schema/cli-transformers.d.ts +5 -0
- package/build/lib/schema/cli-transformers.d.ts.map +1 -0
- package/build/lib/schema/cli-transformers.js +74 -0
- package/build/lib/schema/index.d.ts +3 -0
- package/build/lib/schema/index.d.ts.map +1 -0
- package/build/lib/schema/index.js +34 -0
- package/build/lib/schema/keywords.d.ts +24 -0
- package/build/lib/schema/keywords.d.ts.map +1 -0
- package/build/lib/schema/keywords.js +70 -0
- package/build/lib/schema/schema.d.ts +259 -0
- package/build/lib/schema/schema.d.ts.map +1 -0
- package/build/lib/schema/schema.js +452 -0
- package/build/lib/utils.d.ts +66 -0
- package/build/lib/utils.d.ts.map +1 -0
- package/build/lib/utils.js +35 -139
- package/build/tsconfig.tsbuildinfo +1 -0
- package/build/types/appium-manifest.d.ts +40 -0
- package/build/types/appium-manifest.d.ts.map +1 -0
- package/build/types/cli.d.ts +112 -0
- package/build/types/cli.d.ts.map +1 -0
- package/build/types/extension.d.ts +43 -0
- package/build/types/extension.d.ts.map +1 -0
- package/build/types/external-manifest.d.ts +47 -0
- package/build/types/external-manifest.d.ts.map +1 -0
- package/build/types/index.d.ts +15 -0
- package/build/types/index.d.ts.map +1 -0
- package/index.js +11 -0
- package/lib/appium-config.schema.json +278 -0
- package/lib/appium.js +402 -155
- package/lib/cli/args.js +174 -377
- package/lib/cli/driver-command.js +22 -7
- package/lib/cli/extension-command.js +372 -177
- package/lib/cli/extension.js +32 -10
- package/lib/cli/parser.js +253 -83
- package/lib/cli/plugin-command.js +19 -6
- package/lib/cli/utils.js +22 -2
- package/lib/config-file.js +223 -0
- package/lib/config.js +170 -69
- package/lib/constants.js +78 -0
- package/lib/extension/driver-config.js +249 -0
- package/lib/extension/extension-config.js +458 -0
- package/lib/extension/index.js +102 -0
- package/lib/extension/manifest.js +484 -0
- package/lib/extension/package-changed.js +63 -0
- package/lib/extension/plugin-config.js +113 -0
- package/lib/grid-register.js +25 -22
- package/lib/logger.js +1 -1
- package/lib/logsink.js +14 -7
- package/lib/main.js +255 -90
- package/lib/schema/arg-spec.js +232 -0
- package/lib/schema/cli-args.js +261 -0
- package/lib/schema/cli-transformers.js +122 -0
- package/lib/schema/index.js +2 -0
- package/lib/schema/keywords.js +134 -0
- package/lib/schema/schema.js +734 -0
- package/lib/utils.js +85 -129
- package/package.json +68 -85
- package/scripts/postinstall.js +71 -0
- package/types/appium-manifest.ts +61 -0
- package/types/cli.ts +153 -0
- package/types/extension.ts +56 -0
- package/types/external-manifest.ts +58 -0
- package/types/index.ts +14 -0
- package/CHANGELOG.md +0 -3515
- package/bin/ios-webkit-debug-proxy-launcher.js +0 -71
- package/build/lib/cli/npm.js +0 -206
- package/build/lib/cli/parser-helpers.js +0 -82
- package/build/lib/driver-config.js +0 -77
- package/build/lib/drivers.js +0 -96
- package/build/lib/extension-config.js +0 -251
- package/build/lib/plugin-config.js +0 -59
- package/build/lib/plugins.js +0 -14
- package/lib/cli/npm.js +0 -183
- package/lib/cli/parser-helpers.js +0 -79
- package/lib/driver-config.js +0 -46
- package/lib/drivers.js +0 -81
- package/lib/extension-config.js +0 -208
- package/lib/plugin-config.js +0 -34
- package/lib/plugins.js +0 -10
package/lib/appium.js
CHANGED
|
@@ -1,14 +1,19 @@
|
|
|
1
|
+
/* eslint-disable no-unused-vars */
|
|
1
2
|
import _ from 'lodash';
|
|
2
|
-
import log from './logger';
|
|
3
3
|
import { getBuildInfo, updateBuildInfo, APPIUM_VER } from './config';
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
import { BaseDriver, DriverCore, errors, isSessionCommand,
|
|
5
|
+
CREATE_SESSION_COMMAND, DELETE_SESSION_COMMAND, GET_STATUS_COMMAND
|
|
6
|
+
} from '@appium/base-driver';
|
|
7
7
|
import AsyncLock from 'async-lock';
|
|
8
8
|
import { parseCapsForInnerDriver, pullSettings } from './utils';
|
|
9
|
-
import { util } from 'appium
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
import { util, node, logger } from '@appium/support';
|
|
10
|
+
import { getDefaultsForExtension } from './schema';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Invariant set of base constraints
|
|
14
|
+
* @type {Readonly<Constraints>}
|
|
15
|
+
*/
|
|
16
|
+
const desiredCapabilityConstraints = Object.freeze({
|
|
12
17
|
automationName: {
|
|
13
18
|
presence: true,
|
|
14
19
|
isString: true,
|
|
@@ -17,44 +22,97 @@ const desiredCapabilityConstraints = {
|
|
|
17
22
|
presence: true,
|
|
18
23
|
isString: true,
|
|
19
24
|
},
|
|
20
|
-
};
|
|
25
|
+
});
|
|
21
26
|
|
|
22
27
|
const sessionsListGuard = new AsyncLock();
|
|
23
28
|
const pendingDriversGuard = new AsyncLock();
|
|
24
29
|
|
|
25
|
-
|
|
26
|
-
|
|
30
|
+
/**
|
|
31
|
+
* @implements {SessionHandler}
|
|
32
|
+
*/
|
|
33
|
+
class AppiumDriver extends DriverCore {
|
|
34
|
+
/**
|
|
35
|
+
* Access to sessions list must be guarded with a Semaphore, because
|
|
36
|
+
* it might be changed by other async calls at any time
|
|
37
|
+
* It is not recommended to access this property directly from the outside
|
|
38
|
+
* @type {Record<string,ExternalDriver>}
|
|
39
|
+
*/
|
|
40
|
+
sessions = {};
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Access to pending drivers list must be guarded with a Semaphore, because
|
|
44
|
+
* it might be changed by other async calls at any time
|
|
45
|
+
* It is not recommended to access this property directly from the outside
|
|
46
|
+
* @type {Record<string,ExternalDriver[]>}
|
|
47
|
+
*/
|
|
48
|
+
pendingDrivers = {};
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Note that {@linkcode AppiumDriver} has no `newCommandTimeout` method.
|
|
52
|
+
* `AppiumDriver` does not set and observe its own timeouts; individual
|
|
53
|
+
* sessions (managed drivers) do instead.
|
|
54
|
+
*/
|
|
55
|
+
newCommandTimeoutMs = 0;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* List of active plugins
|
|
59
|
+
* @type {PluginClass[]}
|
|
60
|
+
*/
|
|
61
|
+
pluginClasses = [];
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* map of sessions to actual plugin instances per session
|
|
65
|
+
* @type {Record<string,InstanceType<PluginClass>[]>}
|
|
66
|
+
*/
|
|
67
|
+
sessionPlugins = {};
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* some commands are sessionless, so we need a set of plugins for them
|
|
71
|
+
* @type {InstanceType<PluginClass>[]}
|
|
72
|
+
*/
|
|
73
|
+
sessionlessPlugins = [];;
|
|
74
|
+
|
|
75
|
+
/** @type {DriverConfig} */
|
|
76
|
+
driverConfig;
|
|
77
|
+
|
|
78
|
+
/** @type {AppiumServer} */
|
|
79
|
+
server;
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* @param {DriverOpts} opts
|
|
83
|
+
*/
|
|
84
|
+
constructor (opts) {
|
|
27
85
|
// It is necessary to set `--tmp` here since it should be set to
|
|
28
86
|
// process.env.APPIUM_TMP_DIR once at an initial point in the Appium lifecycle.
|
|
29
87
|
// The process argument will be referenced by BaseDriver.
|
|
30
|
-
// Please call appium
|
|
31
|
-
if (
|
|
32
|
-
process.env.APPIUM_TMP_DIR =
|
|
88
|
+
// Please call @appium/support.tempDir module to apply this benefit.
|
|
89
|
+
if (opts.tmpDir) {
|
|
90
|
+
process.env.APPIUM_TMP_DIR = opts.tmpDir;
|
|
33
91
|
}
|
|
34
92
|
|
|
35
|
-
super(
|
|
93
|
+
super(opts);
|
|
36
94
|
|
|
37
95
|
this.desiredCapConstraints = desiredCapabilityConstraints;
|
|
38
96
|
|
|
39
|
-
|
|
40
|
-
this.newCommandTimeoutMs = 0;
|
|
41
|
-
|
|
42
|
-
this.args = Object.assign({}, args);
|
|
43
|
-
|
|
44
|
-
// Access to sessions list must be guarded with a Semaphore, because
|
|
45
|
-
// it might be changed by other async calls at any time
|
|
46
|
-
// It is not recommended to access this property directly from the outside
|
|
47
|
-
this.sessions = {};
|
|
48
|
-
|
|
49
|
-
// Access to pending drivers list must be guarded with a Semaphore, because
|
|
50
|
-
// it might be changed by other async calls at any time
|
|
51
|
-
// It is not recommended to access this property directly from the outside
|
|
52
|
-
this.pendingDrivers = {};
|
|
53
|
-
|
|
54
|
-
this.plugins = [];
|
|
97
|
+
this.args = {...opts};
|
|
55
98
|
|
|
56
99
|
// allow this to happen in the background, so no `await`
|
|
57
|
-
|
|
100
|
+
// catch this to avoid an unhandled rejection
|
|
101
|
+
// eslint-disable-next-line promise/prefer-await-to-then,promise/prefer-await-to-callbacks
|
|
102
|
+
updateBuildInfo().catch((err) => {
|
|
103
|
+
this.log.debug(err);
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Retrieves logger instance for the current umbrella driver instance
|
|
109
|
+
*/
|
|
110
|
+
get log () {
|
|
111
|
+
if (!this._log) {
|
|
112
|
+
const instanceName = `${this.constructor.name}@${node.getObjectId(this).substring(0, 4)}`;
|
|
113
|
+
this._log = logger.getLogger(instanceName);
|
|
114
|
+
}
|
|
115
|
+
return this._log;
|
|
58
116
|
}
|
|
59
117
|
|
|
60
118
|
/**
|
|
@@ -79,47 +137,72 @@ class AppiumDriver extends BaseDriver {
|
|
|
79
137
|
};
|
|
80
138
|
}
|
|
81
139
|
|
|
82
|
-
async getSessions () {
|
|
83
|
-
|
|
84
|
-
return _.toPairs(sessions)
|
|
140
|
+
async getSessions () { // eslint-disable-line require-await
|
|
141
|
+
return _.toPairs(this.sessions)
|
|
85
142
|
.map(([id, driver]) => ({id, capabilities: driver.caps}));
|
|
86
143
|
}
|
|
87
144
|
|
|
88
|
-
printNewSessionAnnouncement (driverName, driverVersion) {
|
|
89
|
-
|
|
145
|
+
printNewSessionAnnouncement (driverName, driverVersion, driverBaseVersion) {
|
|
146
|
+
this.log.info(driverVersion
|
|
90
147
|
? `Appium v${APPIUM_VER} creating new ${driverName} (v${driverVersion}) session`
|
|
91
|
-
: `Appium v${APPIUM_VER} creating new ${driverName} session
|
|
92
|
-
|
|
148
|
+
: `Appium v${APPIUM_VER} creating new ${driverName} session`
|
|
149
|
+
);
|
|
150
|
+
this.log.info(`Checking BaseDriver versions for Appium and ${driverName}`);
|
|
151
|
+
this.log.info(AppiumDriver.baseVersion
|
|
152
|
+
? `Appium's BaseDriver version is ${AppiumDriver.baseVersion}`
|
|
153
|
+
: `Could not determine Appium's BaseDriver version`
|
|
154
|
+
);
|
|
155
|
+
this.log.info(driverBaseVersion
|
|
156
|
+
? `${driverName}'s BaseDriver version is ${driverBaseVersion}`
|
|
157
|
+
: `Could not determine ${driverName}'s BaseDriver version`
|
|
158
|
+
);
|
|
93
159
|
}
|
|
94
160
|
|
|
95
161
|
/**
|
|
96
|
-
*
|
|
97
|
-
*
|
|
162
|
+
* Validate and assign CLI args for a driver or plugin
|
|
163
|
+
*
|
|
164
|
+
* If the extension has provided a schema, validation has already happened.
|
|
165
|
+
*
|
|
166
|
+
* Any arg which is equal to its default value will not be assigned to the extension.
|
|
167
|
+
* @param {ExtensionType} extType 'driver' or 'plugin'
|
|
168
|
+
* @param {string} extName the name of the extension
|
|
169
|
+
* @param {Object} extInstance the driver or plugin instance
|
|
98
170
|
*/
|
|
99
|
-
|
|
100
|
-
|
|
171
|
+
assignCliArgsToExtension (extType, extName, extInstance) {
|
|
172
|
+
const allCliArgsForExt = /** @type {Record<string,unknown>|undefined} */(this.args[extType]?.[extName]);
|
|
173
|
+
if (!_.isEmpty(allCliArgsForExt)) {
|
|
174
|
+
const defaults = getDefaultsForExtension(extType, extName);
|
|
175
|
+
const cliArgs = _.isEmpty(defaults)
|
|
176
|
+
? allCliArgsForExt
|
|
177
|
+
: _.omitBy(allCliArgsForExt, (value, key) => _.isEqual(defaults[key], value));
|
|
178
|
+
if (!_.isEmpty(cliArgs)) {
|
|
179
|
+
extInstance.cliArgs = cliArgs;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
101
182
|
}
|
|
102
183
|
|
|
103
184
|
/**
|
|
104
185
|
* Create a new session
|
|
105
|
-
* @param {
|
|
106
|
-
* @param {
|
|
107
|
-
* @param {
|
|
108
|
-
* @
|
|
186
|
+
* @param {W3CCapabilities} jsonwpCaps JSONWP formatted desired capabilities
|
|
187
|
+
* @param {W3CCapabilities} reqCaps Required capabilities (JSONWP standard)
|
|
188
|
+
* @param {W3CCapabilities} w3cCapabilities W3C capabilities
|
|
189
|
+
* @param {import('@appium/types').DriverData[]} [driverData]
|
|
109
190
|
*/
|
|
110
|
-
async createSession (jsonwpCaps, reqCaps, w3cCapabilities) {
|
|
191
|
+
async createSession (jsonwpCaps, reqCaps, w3cCapabilities, driverData) {
|
|
111
192
|
const defaultCapabilities = _.cloneDeep(this.args.defaultCapabilities);
|
|
112
193
|
const defaultSettings = pullSettings(defaultCapabilities);
|
|
113
194
|
jsonwpCaps = _.cloneDeep(jsonwpCaps);
|
|
114
|
-
const jwpSettings =
|
|
195
|
+
const jwpSettings = {...defaultSettings, ...pullSettings(jsonwpCaps)};
|
|
115
196
|
w3cCapabilities = _.cloneDeep(w3cCapabilities);
|
|
116
197
|
// It is possible that the client only provides caps using JSONWP standard,
|
|
117
198
|
// although firstMatch/alwaysMatch properties are still present.
|
|
118
199
|
// In such case we assume the client understands W3C protocol and merge the given
|
|
119
200
|
// JSONWP caps to W3C caps
|
|
120
|
-
const w3cSettings =
|
|
121
|
-
|
|
122
|
-
|
|
201
|
+
const w3cSettings = {
|
|
202
|
+
...jwpSettings,
|
|
203
|
+
...pullSettings((w3cCapabilities ?? {}).alwaysMatch ?? {})
|
|
204
|
+
};
|
|
205
|
+
for (const firstMatchEntry of ((w3cCapabilities ?? {}).firstMatch ?? [])) {
|
|
123
206
|
Object.assign(w3cSettings, pullSettings(firstMatchEntry));
|
|
124
207
|
}
|
|
125
208
|
|
|
@@ -134,9 +217,9 @@ class AppiumDriver extends BaseDriver {
|
|
|
134
217
|
defaultCapabilities
|
|
135
218
|
);
|
|
136
219
|
|
|
137
|
-
const {desiredCaps, processedJsonwpCapabilities, processedW3CCapabilities
|
|
220
|
+
const {desiredCaps, processedJsonwpCapabilities, processedW3CCapabilities} = /** @type {import('./utils').ParsedDriverCaps} */(parsedCaps);
|
|
138
221
|
protocol = parsedCaps.protocol;
|
|
139
|
-
|
|
222
|
+
const error = /** @type {import('./utils').InvalidCaps} */(parsedCaps).error;
|
|
140
223
|
// If the parsing of the caps produced an error, throw it in here
|
|
141
224
|
if (error) {
|
|
142
225
|
throw error;
|
|
@@ -144,87 +227,105 @@ class AppiumDriver extends BaseDriver {
|
|
|
144
227
|
|
|
145
228
|
const {
|
|
146
229
|
driver: InnerDriver,
|
|
147
|
-
version: driverVersion
|
|
148
|
-
|
|
149
|
-
this.
|
|
230
|
+
version: driverVersion,
|
|
231
|
+
driverName
|
|
232
|
+
} = this.driverConfig.findMatchingDriver(desiredCaps);
|
|
233
|
+
this.printNewSessionAnnouncement(InnerDriver.name, driverVersion, InnerDriver.baseVersion);
|
|
150
234
|
|
|
151
235
|
if (this.args.sessionOverride) {
|
|
152
236
|
await this.deleteAllSessions();
|
|
153
237
|
}
|
|
154
238
|
|
|
155
|
-
|
|
156
|
-
|
|
239
|
+
/**
|
|
240
|
+
* @type {DriverData[]}
|
|
241
|
+
*/
|
|
242
|
+
let runningDriversData = [];
|
|
243
|
+
/**
|
|
244
|
+
* @type {DriverData[]}
|
|
245
|
+
*/
|
|
246
|
+
let otherPendingDriversData = [];
|
|
247
|
+
|
|
248
|
+
const driverInstance = new InnerDriver(this.args, true);
|
|
157
249
|
|
|
158
250
|
// We want to assign security values directly on the driver. The driver
|
|
159
251
|
// should not read security values from `this.opts` because those values
|
|
160
252
|
// could have been set by a malicious user via capabilities, whereas we
|
|
161
253
|
// want a guarantee the values were set by the appium server admin
|
|
162
254
|
if (this.args.relaxedSecurityEnabled) {
|
|
163
|
-
log.info(`Applying relaxed security to '${InnerDriver.name}' as per ` +
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
255
|
+
this.log.info(`Applying relaxed security to '${InnerDriver.name}' as per ` +
|
|
256
|
+
`server command line argument. All insecure features will be ` +
|
|
257
|
+
`enabled unless explicitly disabled by --deny-insecure`);
|
|
258
|
+
driverInstance.relaxedSecurityEnabled = true;
|
|
167
259
|
}
|
|
168
260
|
|
|
169
261
|
if (!_.isEmpty(this.args.denyInsecure)) {
|
|
170
|
-
log.info('Explicitly preventing use of insecure features:');
|
|
171
|
-
this.args.denyInsecure.map((a) => log.info(` ${a}`));
|
|
172
|
-
|
|
262
|
+
this.log.info('Explicitly preventing use of insecure features:');
|
|
263
|
+
this.args.denyInsecure.map((a) => this.log.info(` ${a}`));
|
|
264
|
+
driverInstance.denyInsecure = this.args.denyInsecure;
|
|
173
265
|
}
|
|
174
266
|
|
|
175
267
|
if (!_.isEmpty(this.args.allowInsecure)) {
|
|
176
|
-
log.info('Explicitly enabling use of insecure features:');
|
|
177
|
-
this.args.allowInsecure.map((a) => log.info(` ${a}`));
|
|
178
|
-
|
|
268
|
+
this.log.info('Explicitly enabling use of insecure features:');
|
|
269
|
+
this.args.allowInsecure.map((a) => this.log.info(` ${a}`));
|
|
270
|
+
driverInstance.allowInsecure = this.args.allowInsecure;
|
|
179
271
|
}
|
|
180
272
|
|
|
273
|
+
// Likewise, any driver-specific CLI args that were passed in should be assigned directly to
|
|
274
|
+
// the driver so that they cannot be mimicked by a malicious user sending in capabilities
|
|
275
|
+
this.assignCliArgsToExtension('driver', driverName, driverInstance);
|
|
276
|
+
|
|
181
277
|
// This assignment is required for correct web sockets functionality inside the driver
|
|
182
|
-
|
|
278
|
+
// Drivers/plugins might also want to know where they are hosted
|
|
279
|
+
|
|
280
|
+
// XXX: temporary hack to work around #16747
|
|
281
|
+
driverInstance.server = this.server;
|
|
282
|
+
driverInstance.serverHost = this.args.address;
|
|
283
|
+
driverInstance.serverPort = this.args.port;
|
|
284
|
+
driverInstance.serverPath = this.args.basePath;
|
|
285
|
+
|
|
183
286
|
try {
|
|
184
|
-
runningDriversData = await this.curSessionDataForDriver(InnerDriver);
|
|
287
|
+
runningDriversData = await this.curSessionDataForDriver(InnerDriver) ?? [];
|
|
185
288
|
} catch (e) {
|
|
186
289
|
throw new errors.SessionNotCreatedError(e.message);
|
|
187
290
|
}
|
|
188
291
|
await pendingDriversGuard.acquire(AppiumDriver.name, () => {
|
|
189
292
|
this.pendingDrivers[InnerDriver.name] = this.pendingDrivers[InnerDriver.name] || [];
|
|
190
|
-
otherPendingDriversData = this.pendingDrivers[InnerDriver.name].map((drv) => drv.driverData);
|
|
191
|
-
this.pendingDrivers[InnerDriver.name].push(
|
|
293
|
+
otherPendingDriversData = _.compact(this.pendingDrivers[InnerDriver.name].map((drv) => drv.driverData));
|
|
294
|
+
this.pendingDrivers[InnerDriver.name].push(driverInstance);
|
|
192
295
|
});
|
|
193
296
|
|
|
194
297
|
try {
|
|
195
|
-
[innerSessionId, dCaps] = await
|
|
298
|
+
[innerSessionId, dCaps] = await driverInstance.createSession(
|
|
196
299
|
processedJsonwpCapabilities,
|
|
197
300
|
reqCaps,
|
|
198
301
|
processedW3CCapabilities,
|
|
199
302
|
[...runningDriversData, ...otherPendingDriversData]
|
|
200
303
|
);
|
|
201
|
-
protocol =
|
|
202
|
-
|
|
203
|
-
this.sessions[innerSessionId] = d;
|
|
204
|
-
});
|
|
304
|
+
protocol = driverInstance.protocol;
|
|
305
|
+
this.sessions[innerSessionId] = driverInstance;
|
|
205
306
|
} finally {
|
|
206
307
|
await pendingDriversGuard.acquire(AppiumDriver.name, () => {
|
|
207
|
-
_.pull(this.pendingDrivers[InnerDriver.name],
|
|
308
|
+
_.pull(this.pendingDrivers[InnerDriver.name], driverInstance);
|
|
208
309
|
});
|
|
209
310
|
}
|
|
210
311
|
|
|
211
|
-
this.attachUnexpectedShutdownHandler(
|
|
312
|
+
this.attachUnexpectedShutdownHandler(driverInstance, innerSessionId);
|
|
212
313
|
|
|
213
|
-
log.info(`New ${InnerDriver.name} session created successfully, session ` +
|
|
214
|
-
|
|
314
|
+
this.log.info(`New ${InnerDriver.name} session created successfully, session ` +
|
|
315
|
+
`${innerSessionId} added to master session list`);
|
|
215
316
|
|
|
216
317
|
// set the New Command Timeout for the inner driver
|
|
217
|
-
|
|
318
|
+
driverInstance.startNewCommandTimeout();
|
|
218
319
|
|
|
219
320
|
// apply initial values to Appium settings (if provided)
|
|
220
|
-
if (
|
|
221
|
-
log.info(`Applying the initial values to Appium settings parsed from W3C caps: ` +
|
|
321
|
+
if (driverInstance.isW3CProtocol() && !_.isEmpty(w3cSettings)) {
|
|
322
|
+
this.log.info(`Applying the initial values to Appium settings parsed from W3C caps: ` +
|
|
222
323
|
JSON.stringify(w3cSettings));
|
|
223
|
-
await
|
|
224
|
-
} else if (
|
|
225
|
-
log.info(`Applying the initial values to Appium settings parsed from MJSONWP caps: ` +
|
|
324
|
+
await driverInstance.updateSettings(w3cSettings);
|
|
325
|
+
} else if (driverInstance.isMjsonwpProtocol() && !_.isEmpty(jwpSettings)) {
|
|
326
|
+
this.log.info(`Applying the initial values to Appium settings parsed from MJSONWP caps: ` +
|
|
226
327
|
JSON.stringify(jwpSettings));
|
|
227
|
-
await
|
|
328
|
+
await driverInstance.updateSettings(jwpSettings);
|
|
228
329
|
}
|
|
229
330
|
} catch (error) {
|
|
230
331
|
return {
|
|
@@ -240,60 +341,63 @@ class AppiumDriver extends BaseDriver {
|
|
|
240
341
|
}
|
|
241
342
|
|
|
242
343
|
attachUnexpectedShutdownHandler (driver, innerSessionId) {
|
|
243
|
-
const
|
|
244
|
-
log.warn(`
|
|
245
|
-
|
|
344
|
+
const onShutdown = (cause = new Error('Unknown error')) => {
|
|
345
|
+
this.log.warn(`Ending session, cause was '${cause.message}'`);
|
|
346
|
+
|
|
347
|
+
if (this.sessionPlugins[innerSessionId]) {
|
|
348
|
+
for (const plugin of this.sessionPlugins[innerSessionId]) {
|
|
349
|
+
if (_.isFunction(plugin.onUnexpectedShutdown)) {
|
|
350
|
+
this.log.debug(`Plugin ${plugin.name} defines an unexpected shutdown handler; calling it now`);
|
|
351
|
+
try {
|
|
352
|
+
plugin.onUnexpectedShutdown(driver, cause);
|
|
353
|
+
} catch (e) {
|
|
354
|
+
this.log.warn(`Got an error when running plugin ${plugin.name} shutdown handler: ${e}`);
|
|
355
|
+
}
|
|
356
|
+
} else {
|
|
357
|
+
this.log.debug(`Plugin ${plugin.name} does not define an unexpected shutdown handler`);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
this.log.info(`Removing session '${innerSessionId}' from our master session list`);
|
|
246
363
|
delete this.sessions[innerSessionId];
|
|
364
|
+
delete this.sessionPlugins[innerSessionId];
|
|
247
365
|
};
|
|
248
366
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
// TODO: Remove this block after all the drivers use base driver above v 5.0.0
|
|
252
|
-
// Remove the session on unexpected shutdown, so that we are in a position
|
|
253
|
-
// to open another session later on.
|
|
254
|
-
driver.onUnexpectedShutdown
|
|
255
|
-
// eslint-disable-next-line promise/prefer-await-to-then
|
|
256
|
-
.then(() => {
|
|
257
|
-
// if we get here, we've had an unexpected shutdown, so error
|
|
258
|
-
throw new Error('Unexpected shutdown');
|
|
259
|
-
})
|
|
260
|
-
.catch((e) => {
|
|
261
|
-
// if we cancelled the unexpected shutdown promise, that means we
|
|
262
|
-
// no longer care about it, and can safely ignore it
|
|
263
|
-
if (!(e instanceof B.CancellationError)) {
|
|
264
|
-
removeSessionFromMasterList(e);
|
|
265
|
-
}
|
|
266
|
-
}); // this is a cancellable promise
|
|
267
|
-
} else if (_.isFunction(driver.onUnexpectedShutdown)) {
|
|
268
|
-
// since base driver v 5.0.0
|
|
269
|
-
driver.onUnexpectedShutdown(removeSessionFromMasterList);
|
|
367
|
+
if (_.isFunction(driver.onUnexpectedShutdown)) {
|
|
368
|
+
driver.onUnexpectedShutdown(onShutdown);
|
|
270
369
|
} else {
|
|
271
|
-
log.warn(`Failed to attach the unexpected shutdown listener. ` +
|
|
370
|
+
this.log.warn(`Failed to attach the unexpected shutdown listener. ` +
|
|
272
371
|
`Is 'onUnexpectedShutdown' method available for '${driver.constructor.name}'?`);
|
|
273
372
|
}
|
|
274
373
|
}
|
|
275
374
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
375
|
+
/**
|
|
376
|
+
*
|
|
377
|
+
* @param {import('../types/extension').DriverClass} InnerDriver
|
|
378
|
+
* @returns {Promise<DriverData[]>}}
|
|
379
|
+
*/
|
|
380
|
+
async curSessionDataForDriver (InnerDriver) { // eslint-disable-line require-await
|
|
381
|
+
const data = _.compact(_.values(this.sessions)
|
|
382
|
+
.filter((s) => s.constructor.name === InnerDriver.name)
|
|
383
|
+
.map((s) => s.driverData));
|
|
384
|
+
for (const datum of data) {
|
|
282
385
|
if (!datum) {
|
|
283
386
|
throw new Error(`Problem getting session data for driver type ` +
|
|
284
|
-
|
|
285
|
-
`driverData'?`);
|
|
387
|
+
`${InnerDriver.name}; does it implement 'get driverData'?`);
|
|
286
388
|
}
|
|
287
389
|
}
|
|
288
390
|
return data;
|
|
289
391
|
}
|
|
290
392
|
|
|
393
|
+
/**
|
|
394
|
+
* @param {string} sessionId
|
|
395
|
+
*/
|
|
291
396
|
async deleteSession (sessionId) {
|
|
292
397
|
let protocol;
|
|
293
398
|
try {
|
|
294
|
-
let otherSessionsData
|
|
295
|
-
|
|
296
|
-
await sessionsListGuard.acquire(AppiumDriver.name, () => {
|
|
399
|
+
let otherSessionsData;
|
|
400
|
+
const dstSession = await sessionsListGuard.acquire(AppiumDriver.name, () => {
|
|
297
401
|
if (!this.sessions[sessionId]) {
|
|
298
402
|
return;
|
|
299
403
|
}
|
|
@@ -301,20 +405,27 @@ class AppiumDriver extends BaseDriver {
|
|
|
301
405
|
otherSessionsData = _.toPairs(this.sessions)
|
|
302
406
|
.filter(([key, value]) => value.constructor.name === curConstructorName && key !== sessionId)
|
|
303
407
|
.map(([, value]) => value.driverData);
|
|
304
|
-
dstSession = this.sessions[sessionId];
|
|
408
|
+
const dstSession = this.sessions[sessionId];
|
|
305
409
|
protocol = dstSession.protocol;
|
|
306
|
-
log.info(`Removing session ${sessionId} from our master session list`);
|
|
410
|
+
this.log.info(`Removing session ${sessionId} from our master session list`);
|
|
307
411
|
// regardless of whether the deleteSession completes successfully or not
|
|
308
412
|
// make the session unavailable, because who knows what state it might
|
|
309
413
|
// be in otherwise
|
|
310
414
|
delete this.sessions[sessionId];
|
|
415
|
+
delete this.sessionPlugins[sessionId];
|
|
416
|
+
return dstSession;
|
|
311
417
|
});
|
|
418
|
+
// this may not be correct, but if `dstSession` was falsy, the call to `deleteSession()` would
|
|
419
|
+
// throw anyway.
|
|
420
|
+
if (!dstSession) {
|
|
421
|
+
throw new Error('Session not found');
|
|
422
|
+
}
|
|
312
423
|
return {
|
|
313
424
|
protocol,
|
|
314
425
|
value: await dstSession.deleteSession(sessionId, otherSessionsData),
|
|
315
426
|
};
|
|
316
427
|
} catch (e) {
|
|
317
|
-
log.error(`Had trouble ending session ${sessionId}: ${e.message}`);
|
|
428
|
+
this.log.error(`Had trouble ending session ${sessionId}: ${e.message}`);
|
|
318
429
|
return {
|
|
319
430
|
protocol,
|
|
320
431
|
error: e,
|
|
@@ -325,7 +436,7 @@ class AppiumDriver extends BaseDriver {
|
|
|
325
436
|
async deleteAllSessions (opts = {}) {
|
|
326
437
|
const sessionsCount = _.size(this.sessions);
|
|
327
438
|
if (0 === sessionsCount) {
|
|
328
|
-
log.debug('There are no active sessions for cleanup');
|
|
439
|
+
this.log.debug('There are no active sessions for cleanup');
|
|
329
440
|
return;
|
|
330
441
|
}
|
|
331
442
|
|
|
@@ -333,7 +444,7 @@ class AppiumDriver extends BaseDriver {
|
|
|
333
444
|
force = false,
|
|
334
445
|
reason,
|
|
335
446
|
} = opts;
|
|
336
|
-
log.debug(`Cleaning up ${util.pluralize('active session', sessionsCount, true)}`);
|
|
447
|
+
this.log.debug(`Cleaning up ${util.pluralize('active session', sessionsCount, true)}`);
|
|
337
448
|
const cleanupPromises = force
|
|
338
449
|
? _.values(this.sessions).map((drv) => drv.startUnexpectedShutdown(reason && new Error(reason)))
|
|
339
450
|
: _.keys(this.sessions).map((id) => this.deleteSession(id));
|
|
@@ -341,18 +452,63 @@ class AppiumDriver extends BaseDriver {
|
|
|
341
452
|
try {
|
|
342
453
|
await cleanupPromise;
|
|
343
454
|
} catch (e) {
|
|
344
|
-
log.debug(e);
|
|
455
|
+
this.log.debug(e);
|
|
345
456
|
}
|
|
346
457
|
}
|
|
347
458
|
}
|
|
348
459
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
460
|
+
/**
|
|
461
|
+
* Get the appropriate plugins for a session (or sessionless plugins)
|
|
462
|
+
*
|
|
463
|
+
* @param {?string} sessionId - the sessionId (or null) to use to find plugins
|
|
464
|
+
* @returns {Array} - array of plugin instances
|
|
465
|
+
*/
|
|
466
|
+
pluginsForSession (sessionId = null) {
|
|
467
|
+
if (sessionId) {
|
|
468
|
+
if (!this.sessionPlugins[sessionId]) {
|
|
469
|
+
this.sessionPlugins[sessionId] = this.createPluginInstances();
|
|
470
|
+
}
|
|
471
|
+
return this.sessionPlugins[sessionId];
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
if (_.isEmpty(this.sessionlessPlugins)) {
|
|
475
|
+
this.sessionlessPlugins = this.createPluginInstances();
|
|
476
|
+
}
|
|
477
|
+
return this.sessionlessPlugins;
|
|
354
478
|
}
|
|
355
479
|
|
|
480
|
+
/**
|
|
481
|
+
* To get plugins for a command, we either get the plugin instances associated with the
|
|
482
|
+
* particular command's session, or in the case of sessionless plugins, pull from the set of
|
|
483
|
+
* plugin instances reserved for sessionless commands (and we lazily create plugin instances on
|
|
484
|
+
* first use)
|
|
485
|
+
*
|
|
486
|
+
* @param {string} cmd - the name of the command to find a plugin to handle
|
|
487
|
+
* @param {?string} sessionId - the particular session for which to find a plugin, or null if
|
|
488
|
+
* sessionless
|
|
489
|
+
*/
|
|
490
|
+
pluginsToHandleCmd (cmd, sessionId = null) {
|
|
491
|
+
// to handle a given command, a plugin should either implement that command as a plugin
|
|
492
|
+
// instance method or it should implement a generic 'handle' method
|
|
493
|
+
return this.pluginsForSession(sessionId)
|
|
494
|
+
.filter((p) => _.isFunction(p[cmd]) || _.isFunction(p.handle));
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
createPluginInstances () {
|
|
498
|
+
return this.pluginClasses.map((PluginClass) => {
|
|
499
|
+
const name = PluginClass.pluginName;
|
|
500
|
+
const plugin = new PluginClass(name);
|
|
501
|
+
this.assignCliArgsToExtension('plugin', name, plugin);
|
|
502
|
+
return plugin;
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
/**
|
|
507
|
+
*
|
|
508
|
+
* @param {string} cmd
|
|
509
|
+
* @param {...any} args
|
|
510
|
+
* @returns {Promise<{value: any, error?: Error, protocol: string} | import('type-fest').AsyncReturnType<import('@appium/types').Driver['executeCommand']>>}
|
|
511
|
+
*/
|
|
356
512
|
async executeCommand (cmd, ...args) {
|
|
357
513
|
// We have basically three cases for how to handle commands:
|
|
358
514
|
// 1. handle getStatus (we do this as a special out of band case so it doesn't get added to an
|
|
@@ -363,28 +519,42 @@ class AppiumDriver extends BaseDriver {
|
|
|
363
519
|
// The tricky part is that because we support command plugins, we need to wrap any of these
|
|
364
520
|
// cases with plugin handling.
|
|
365
521
|
|
|
366
|
-
const isGetStatus = cmd ===
|
|
367
|
-
const isUmbrellaCmd =
|
|
368
|
-
const isSessionCmd =
|
|
522
|
+
const isGetStatus = cmd === GET_STATUS_COMMAND;
|
|
523
|
+
const isUmbrellaCmd = isAppiumDriverCommand(cmd);
|
|
524
|
+
const isSessionCmd = isSessionCommand(cmd);
|
|
525
|
+
|
|
526
|
+
// if a plugin override proxying for this command and that is why we are here instead of just
|
|
527
|
+
// letting the protocol proxy the command entirely, determine that, get the request object for
|
|
528
|
+
// use later on, then clean up the args
|
|
529
|
+
const reqForProxy = _.last(args)?.reqForProxy;
|
|
530
|
+
if (reqForProxy) {
|
|
531
|
+
args.pop();
|
|
532
|
+
}
|
|
369
533
|
|
|
370
|
-
// get any plugins which are registered as handling this command
|
|
371
|
-
const plugins = this.pluginsToHandleCmd(cmd);
|
|
372
534
|
|
|
373
535
|
// first do some error checking. If we're requesting a session command execution, then make
|
|
374
536
|
// sure that session actually exists on the session driver, and set the session driver itself
|
|
375
537
|
let sessionId = null;
|
|
376
538
|
let dstSession = null;
|
|
377
539
|
let protocol = null;
|
|
540
|
+
/** @type {this | ExternalDriver} */
|
|
541
|
+
let driver = this;
|
|
378
542
|
if (isSessionCmd) {
|
|
379
543
|
sessionId = _.last(args);
|
|
380
|
-
dstSession =
|
|
544
|
+
dstSession = this.sessions[sessionId];
|
|
381
545
|
if (!dstSession) {
|
|
382
546
|
throw new Error(`The session with id '${sessionId}' does not exist`);
|
|
383
547
|
}
|
|
384
548
|
// now save the response protocol given that the session driver's protocol might differ
|
|
385
549
|
protocol = dstSession.protocol;
|
|
550
|
+
if (!isUmbrellaCmd) {
|
|
551
|
+
driver = dstSession;
|
|
552
|
+
}
|
|
386
553
|
}
|
|
387
554
|
|
|
555
|
+
// get any plugins which are registered as handling this command
|
|
556
|
+
const plugins = this.pluginsToHandleCmd(cmd, sessionId);
|
|
557
|
+
|
|
388
558
|
// now we define a 'cmdHandledBy' object which will keep track of which plugins have handled this
|
|
389
559
|
// command. we care about this because (a) multiple plugins can handle the same command, and
|
|
390
560
|
// (b) there's no guarantee that a plugin will actually call the next() method which runs the
|
|
@@ -401,11 +571,23 @@ class AppiumDriver extends BaseDriver {
|
|
|
401
571
|
// if we're running with plugins, make sure we log that the default behavior is actually
|
|
402
572
|
// happening so we can tell when the plugin call chain is unwrapping to the default behavior
|
|
403
573
|
// if that's what happens
|
|
404
|
-
plugins.length && log.info(`Executing default handling behavior for command '${cmd}'`);
|
|
574
|
+
plugins.length && this.log.info(`Executing default handling behavior for command '${cmd}'`);
|
|
405
575
|
|
|
406
576
|
// if we make it here, we know that the default behavior is handled
|
|
407
577
|
cmdHandledBy.default = true;
|
|
408
578
|
|
|
579
|
+
if (reqForProxy) {
|
|
580
|
+
// we would have proxied this command had a plugin not handled it, so the default behavior
|
|
581
|
+
// is to do the proxy and retrieve the result internally so it can be passed to the plugin
|
|
582
|
+
// in case it calls 'await next()'. This requires that the driver have defined
|
|
583
|
+
// 'proxyCommand' and not just 'proxyReqRes'.
|
|
584
|
+
if (!dstSession.proxyCommand) {
|
|
585
|
+
throw new NoDriverProxyCommandError();
|
|
586
|
+
}
|
|
587
|
+
return await dstSession.proxyCommand(reqForProxy.originalUrl, reqForProxy.method,
|
|
588
|
+
reqForProxy.body);
|
|
589
|
+
}
|
|
590
|
+
|
|
409
591
|
if (isGetStatus) {
|
|
410
592
|
return await this.getStatus();
|
|
411
593
|
}
|
|
@@ -413,7 +595,7 @@ class AppiumDriver extends BaseDriver {
|
|
|
413
595
|
if (isUmbrellaCmd) {
|
|
414
596
|
// some commands, like deleteSession, we want to make sure to handle on *this* driver,
|
|
415
597
|
// not the platform driver
|
|
416
|
-
return await
|
|
598
|
+
return await BaseDriver.prototype.executeCommand.call(this, cmd, ...args);
|
|
417
599
|
}
|
|
418
600
|
|
|
419
601
|
// here we know that we are executing a session command, and have a valid session driver
|
|
@@ -421,18 +603,31 @@ class AppiumDriver extends BaseDriver {
|
|
|
421
603
|
};
|
|
422
604
|
|
|
423
605
|
// now take our default behavior, wrap it with any number of plugin behaviors, and run it
|
|
424
|
-
const wrappedCmd = this.wrapCommandWithPlugins({
|
|
606
|
+
const wrappedCmd = this.wrapCommandWithPlugins({
|
|
607
|
+
driver, cmd, args, plugins, cmdHandledBy, next: defaultBehavior
|
|
608
|
+
});
|
|
425
609
|
const res = await this.executeWrappedCommand({wrappedCmd, protocol});
|
|
426
610
|
|
|
427
611
|
// if we had plugins, make sure to log out the helpful report about which plugins ended up
|
|
428
612
|
// handling the command and which didn't
|
|
429
|
-
|
|
613
|
+
this.logPluginHandlerReport(plugins, {cmd, cmdHandledBy});
|
|
614
|
+
|
|
615
|
+
// And finally, if the command was createSession, we want to migrate any plugins which were
|
|
616
|
+
// previously sessionless to use the new sessionId, so that plugins can share state between
|
|
617
|
+
// their createSession method and other instance methods
|
|
618
|
+
if (cmd === CREATE_SESSION_COMMAND && this.sessionlessPlugins.length && !res.error) {
|
|
619
|
+
const sessionId = _.first(res.value);
|
|
620
|
+
this.log.info(`Promoting ${this.sessionlessPlugins.length} sessionless plugins to be attached ` +
|
|
621
|
+
`to session ID ${sessionId}`);
|
|
622
|
+
this.sessionPlugins[sessionId] = this.sessionlessPlugins;
|
|
623
|
+
this.sessionlessPlugins = [];
|
|
624
|
+
}
|
|
430
625
|
|
|
431
626
|
return res;
|
|
432
627
|
}
|
|
433
628
|
|
|
434
|
-
wrapCommandWithPlugins ({cmd, args, next, cmdHandledBy, plugins}) {
|
|
435
|
-
plugins.length && log.info(`Plugins which can handle cmd '${cmd}': ${plugins.map((p) => p.name)}`);
|
|
629
|
+
wrapCommandWithPlugins ({driver, cmd, args, next, cmdHandledBy, plugins}) {
|
|
630
|
+
plugins.length && this.log.info(`Plugins which can handle cmd '${cmd}': ${plugins.map((p) => p.name)}`);
|
|
436
631
|
|
|
437
632
|
// now we can go through each plugin and wrap `next` around its own handler, passing the *old*
|
|
438
633
|
// next in so that it can call it if it wants to
|
|
@@ -442,16 +637,25 @@ class AppiumDriver extends BaseDriver {
|
|
|
442
637
|
// evaluated, otherwise we end up with infinite recursion of the last `next` to be defined.
|
|
443
638
|
cmdHandledBy[plugin.name] = false; // we see a new plugin, so add it to the 'cmdHandledBy' object
|
|
444
639
|
next = ((_next) => async () => {
|
|
445
|
-
log.info(`Plugin ${plugin.name} is now handling cmd '${cmd}'`);
|
|
640
|
+
this.log.info(`Plugin ${plugin.name} is now handling cmd '${cmd}'`);
|
|
446
641
|
cmdHandledBy[plugin.name] = true; // if we make it here, this plugin has attempted to handle cmd
|
|
447
|
-
|
|
642
|
+
// first attempt to handle the command via a command-specific handler on the plugin
|
|
643
|
+
if (plugin[cmd]) {
|
|
644
|
+
return await plugin[cmd](_next, driver, ...args);
|
|
645
|
+
}
|
|
646
|
+
// otherwise, call the generic 'handle' method
|
|
647
|
+
return await plugin.handle(_next, driver, cmd, ...args);
|
|
448
648
|
})(next);
|
|
449
649
|
}
|
|
450
650
|
|
|
451
651
|
return next;
|
|
452
652
|
}
|
|
453
653
|
|
|
454
|
-
logPluginHandlerReport ({cmd, cmdHandledBy}) {
|
|
654
|
+
logPluginHandlerReport (plugins, {cmd, cmdHandledBy}) {
|
|
655
|
+
if (!plugins.length) {
|
|
656
|
+
return;
|
|
657
|
+
}
|
|
658
|
+
|
|
455
659
|
// at the end of the day, we have an object representing which plugins ended up getting
|
|
456
660
|
// their code run as part of handling this command. Because plugins can choose *not* to
|
|
457
661
|
// pass control to other plugins or to the default driver behavior, this is information
|
|
@@ -461,9 +665,9 @@ class AppiumDriver extends BaseDriver {
|
|
|
461
665
|
const didHandle = Object.keys(cmdHandledBy).filter((k) => cmdHandledBy[k]);
|
|
462
666
|
const didntHandle = Object.keys(cmdHandledBy).filter((k) => !cmdHandledBy[k]);
|
|
463
667
|
if (didntHandle.length > 0) {
|
|
464
|
-
log.info(`Command '${cmd}' was not handled by the following
|
|
465
|
-
|
|
466
|
-
|
|
668
|
+
this.log.info(`Command '${cmd}' was *not* handled by the following behaviours or plugins, even ` +
|
|
669
|
+
`though they were registered to handle it: ${JSON.stringify(didntHandle)}. The ` +
|
|
670
|
+
`command *was* handled by these: ${JSON.stringify(didHandle)}.`);
|
|
467
671
|
}
|
|
468
672
|
}
|
|
469
673
|
|
|
@@ -491,7 +695,6 @@ class AppiumDriver extends BaseDriver {
|
|
|
491
695
|
return res;
|
|
492
696
|
}
|
|
493
697
|
|
|
494
|
-
|
|
495
698
|
proxyActive (sessionId) {
|
|
496
699
|
const dstSession = this.sessions[sessionId];
|
|
497
700
|
return dstSession && _.isFunction(dstSession.proxyActive) && dstSession.proxyActive(sessionId);
|
|
@@ -511,7 +714,51 @@ class AppiumDriver extends BaseDriver {
|
|
|
511
714
|
// help decide which commands should be proxied to sub-drivers and which
|
|
512
715
|
// should be handled by this, our umbrella driver
|
|
513
716
|
function isAppiumDriverCommand (cmd) {
|
|
514
|
-
return !isSessionCommand(cmd) || cmd ===
|
|
717
|
+
return !isSessionCommand(cmd) || cmd === DELETE_SESSION_COMMAND;
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
/**
|
|
721
|
+
* Thrown when Appium tried to proxy a command using a driver's `proxyCommand` method but the
|
|
722
|
+
* method did not exist
|
|
723
|
+
*/
|
|
724
|
+
export class NoDriverProxyCommandError extends Error {
|
|
725
|
+
/**
|
|
726
|
+
* @type {Readonly<string>}
|
|
727
|
+
*/
|
|
728
|
+
code = 'APPIUMERR_NO_DRIVER_PROXYCOMMAND';
|
|
729
|
+
|
|
730
|
+
constructor () {
|
|
731
|
+
super(`The default behavior for this command was to proxy, but the driver ` +
|
|
732
|
+
`did not have the 'proxyCommand' method defined. To fully support ` +
|
|
733
|
+
`plugins, drivers should have 'proxyCommand' set to a jwpProxy object's ` +
|
|
734
|
+
`'command()' method, in addition to the normal 'proxyReqRes'`);
|
|
735
|
+
}
|
|
515
736
|
}
|
|
516
737
|
|
|
517
738
|
export { AppiumDriver };
|
|
739
|
+
|
|
740
|
+
/**
|
|
741
|
+
* @typedef {import('@appium/types').ExternalDriver} ExternalDriver
|
|
742
|
+
* @typedef {import('@appium/types').W3CCapabilities} W3CCapabilities
|
|
743
|
+
* @typedef {import('@appium/types').DriverData} DriverData
|
|
744
|
+
* @typedef {import('@appium/types').ServerArgs} DriverOpts
|
|
745
|
+
* @typedef {import('@appium/types').Constraints} Constraints
|
|
746
|
+
* @typedef {import('@appium/types').AppiumServer} AppiumServer
|
|
747
|
+
* @typedef {import('../types').ExtensionType} ExtensionType
|
|
748
|
+
* @typedef {import('../types/extension').PluginClass} PluginClass
|
|
749
|
+
* @typedef {import('./extension/driver-config').DriverConfig} DriverConfig
|
|
750
|
+
*/
|
|
751
|
+
|
|
752
|
+
/**
|
|
753
|
+
* Used by {@linkcode AppiumDriver.createSession} and {@linkcode AppiumDriver.deleteSession} to describe
|
|
754
|
+
* result.
|
|
755
|
+
* @template V
|
|
756
|
+
* @typedef SessionHandlerResult
|
|
757
|
+
* @property {V} [value]
|
|
758
|
+
* @property {Error} [error]
|
|
759
|
+
* @property {string} [protocol]
|
|
760
|
+
*/
|
|
761
|
+
|
|
762
|
+
/**
|
|
763
|
+
* @typedef {import('@appium/types').SessionHandler<SessionHandlerResult<any[]>,SessionHandlerResult<void>>} SessionHandler
|
|
764
|
+
*/
|