@appium/base-driver 8.2.4 → 8.4.0
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/basedriver/capabilities.d.ts +80 -0
- package/build/lib/basedriver/capabilities.d.ts.map +1 -0
- package/build/lib/basedriver/capabilities.js +12 -13
- package/build/lib/basedriver/commands/event.d.ts +9 -0
- package/build/lib/basedriver/commands/event.d.ts.map +1 -0
- package/build/lib/basedriver/commands/event.js +21 -23
- package/build/lib/basedriver/commands/find.d.ts +11 -0
- package/build/lib/basedriver/commands/find.d.ts.map +1 -0
- package/build/lib/basedriver/commands/find.js +41 -43
- package/build/lib/basedriver/commands/index.d.ts +8 -0
- package/build/lib/basedriver/commands/index.d.ts.map +1 -0
- package/build/lib/basedriver/commands/index.js +17 -16
- package/build/lib/basedriver/commands/log.d.ts +12 -0
- package/build/lib/basedriver/commands/log.d.ts.map +1 -0
- package/build/lib/basedriver/commands/log.js +23 -30
- package/build/lib/basedriver/commands/session.d.ts +11 -0
- package/build/lib/basedriver/commands/session.d.ts.map +1 -0
- package/build/lib/basedriver/commands/session.js +21 -158
- package/build/lib/basedriver/commands/settings.d.ts +10 -0
- package/build/lib/basedriver/commands/settings.d.ts.map +1 -0
- package/build/lib/basedriver/commands/settings.js +18 -22
- package/build/lib/basedriver/commands/timeout.d.ts +8 -0
- package/build/lib/basedriver/commands/timeout.d.ts.map +1 -0
- package/build/lib/basedriver/commands/timeout.js +118 -144
- package/build/lib/basedriver/core.d.ts +235 -0
- package/build/lib/basedriver/core.d.ts.map +1 -0
- package/build/lib/basedriver/core.js +283 -0
- package/build/lib/basedriver/desired-caps.d.ts +5 -0
- package/build/lib/basedriver/desired-caps.d.ts.map +1 -0
- package/build/lib/basedriver/desired-caps.js +2 -4
- package/build/lib/basedriver/device-settings.d.ts +32 -0
- package/build/lib/basedriver/device-settings.d.ts.map +1 -0
- package/build/lib/basedriver/device-settings.js +33 -15
- package/build/lib/basedriver/driver.d.ts +83 -0
- package/build/lib/basedriver/driver.d.ts.map +1 -0
- package/build/lib/basedriver/driver.js +104 -257
- package/build/lib/basedriver/helpers.d.ts +132 -0
- package/build/lib/basedriver/helpers.d.ts.map +1 -0
- package/build/lib/basedriver/helpers.js +24 -10
- package/build/lib/basedriver/logger.d.ts +3 -0
- package/build/lib/basedriver/logger.d.ts.map +1 -0
- package/build/lib/basedriver/logger.js +2 -4
- package/build/lib/constants.d.ts +9 -0
- package/build/lib/constants.d.ts.map +1 -0
- package/build/lib/constants.js +2 -4
- package/build/lib/express/crash.d.ts +3 -0
- package/build/lib/express/crash.d.ts.map +1 -0
- package/build/lib/express/crash.js +2 -4
- package/build/lib/express/express-logging.d.ts +3 -0
- package/build/lib/express/express-logging.d.ts.map +1 -0
- package/build/lib/express/express-logging.js +3 -5
- package/build/lib/express/idempotency.d.ts +2 -0
- package/build/lib/express/idempotency.d.ts.map +1 -0
- package/build/lib/express/idempotency.js +3 -5
- package/build/lib/express/logger.d.ts +3 -0
- package/build/lib/express/logger.d.ts.map +1 -0
- package/build/lib/express/logger.js +2 -4
- package/build/lib/express/middleware.d.ts +9 -0
- package/build/lib/express/middleware.d.ts.map +1 -0
- package/build/lib/express/middleware.js +2 -4
- package/build/lib/express/server.d.ts +10 -0
- package/build/lib/express/server.d.ts.map +1 -0
- package/build/lib/express/server.js +2 -4
- package/build/lib/express/static.d.ts +6 -0
- package/build/lib/express/static.d.ts.map +1 -0
- package/build/lib/express/static.js +2 -4
- package/build/lib/express/websocket.d.ts +64 -0
- package/build/lib/express/websocket.d.ts.map +1 -0
- package/build/lib/express/websocket.js +2 -4
- package/build/lib/helpers/capabilities.d.ts +13 -0
- package/build/lib/helpers/capabilities.d.ts.map +1 -0
- package/build/lib/helpers/capabilities.js +77 -0
- package/build/lib/index.d.ts +183 -0
- package/build/lib/index.d.ts.map +1 -0
- package/build/lib/index.js +41 -25
- package/build/lib/jsonwp-proxy/protocol-converter.d.ts +48 -0
- package/build/lib/jsonwp-proxy/protocol-converter.d.ts.map +1 -0
- package/build/lib/jsonwp-proxy/protocol-converter.js +19 -15
- package/build/lib/jsonwp-proxy/proxy.d.ts +41 -0
- package/build/lib/jsonwp-proxy/proxy.d.ts.map +1 -0
- package/build/lib/jsonwp-proxy/proxy.js +20 -15
- package/build/lib/jsonwp-status/status.d.ts +159 -0
- package/build/lib/jsonwp-status/status.d.ts.map +1 -0
- package/build/lib/jsonwp-status/status.js +2 -4
- package/build/lib/protocol/errors.d.ts +310 -0
- package/build/lib/protocol/errors.d.ts.map +1 -0
- package/build/lib/protocol/errors.js +82 -5
- package/build/lib/protocol/helpers.d.ts +22 -0
- package/build/lib/protocol/helpers.d.ts.map +1 -0
- package/build/lib/protocol/helpers.js +2 -4
- package/build/lib/protocol/index.d.ts +16 -0
- package/build/lib/protocol/index.d.ts.map +1 -0
- package/build/lib/protocol/index.js +8 -10
- package/build/lib/protocol/protocol.d.ts +11 -0
- package/build/lib/protocol/protocol.d.ts.map +1 -0
- package/build/lib/protocol/protocol.js +38 -35
- package/build/lib/protocol/routes.d.ts +6 -0
- package/build/lib/protocol/routes.d.ts.map +1 -0
- package/build/lib/protocol/routes.js +84 -4
- package/build/lib/protocol/validators.d.ts +8 -0
- package/build/lib/protocol/validators.d.ts.map +1 -0
- package/build/lib/protocol/validators.js +2 -4
- package/build/test/basedriver/README.md +5 -0
- package/build/test/basedriver/driver-e2e-tests.js +2 -4
- package/build/test/basedriver/driver-tests.js +41 -19
- package/build/test/basedriver/index.js +2 -4
- package/build/test/e2e/basedriver/driver.e2e.spec.js +15 -0
- package/build/test/e2e/basedriver/helpers.e2e.spec.js +192 -0
- package/build/test/e2e/basedriver/websockets.e2e.spec.js +82 -0
- package/build/test/e2e/express/server.e2e.spec.js +159 -0
- package/build/test/e2e/jsonwp-proxy/proxy.e2e.spec.js +59 -0
- package/build/test/e2e/protocol/fake-driver.js +163 -0
- package/build/test/e2e/protocol/helpers.js +25 -0
- package/build/test/e2e/protocol/protocol.e2e.spec.js +1186 -0
- package/build/test/helpers.js +2 -4
- package/build/test/unit/basedriver/capabilities.spec.js +672 -0
- package/build/test/unit/basedriver/capability.spec.js +353 -0
- package/build/test/unit/basedriver/commands/event.spec.js +110 -0
- package/build/test/unit/basedriver/commands/log.spec.js +92 -0
- package/build/test/unit/basedriver/driver.spec.js +15 -0
- package/build/test/unit/basedriver/helpers.spec.js +151 -0
- package/build/test/unit/basedriver/timeout.spec.js +135 -0
- package/build/test/unit/express/server.spec.js +155 -0
- package/build/test/unit/express/static.spec.js +26 -0
- package/build/test/unit/jsonwp-proxy/mock-request.js +91 -0
- package/build/test/unit/jsonwp-proxy/protocol-converter.spec.js +171 -0
- package/build/test/unit/jsonwp-proxy/proxy.spec.js +292 -0
- package/build/test/unit/jsonwp-proxy/url.spec.js +165 -0
- package/build/test/unit/jsonwp-status/status.spec.js +34 -0
- package/build/test/unit/protocol/errors.spec.js +390 -0
- package/build/test/unit/protocol/routes.spec.js +80 -0
- package/build/test/unit/protocol/validator.spec.js +149 -0
- package/build/tsconfig.tsbuildinfo +1 -0
- package/lib/basedriver/capabilities.js +49 -10
- package/lib/basedriver/commands/event.js +49 -31
- package/lib/basedriver/commands/find.js +107 -45
- package/lib/basedriver/commands/index.js +25 -19
- package/lib/basedriver/commands/log.js +59 -34
- package/lib/basedriver/commands/session.js +39 -142
- package/lib/basedriver/commands/settings.js +32 -14
- package/lib/basedriver/commands/timeout.js +153 -154
- package/lib/basedriver/core.js +497 -0
- package/lib/basedriver/desired-caps.js +1 -1
- package/lib/basedriver/device-settings.js +57 -13
- package/lib/basedriver/driver.js +277 -375
- package/lib/basedriver/helpers.js +31 -13
- package/lib/express/express-logging.js +1 -1
- package/lib/express/idempotency.js +1 -1
- package/lib/helpers/capabilities.js +84 -0
- package/lib/index.js +17 -13
- package/lib/jsonwp-proxy/protocol-converter.js +14 -13
- package/lib/jsonwp-proxy/proxy.js +16 -12
- package/lib/protocol/errors.js +42 -42
- package/lib/protocol/index.js +4 -4
- package/lib/protocol/protocol.js +35 -32
- package/lib/protocol/routes.js +69 -1
- package/package.json +37 -24
- package/test/basedriver/README.md +5 -0
- package/test/basedriver/driver-e2e-tests.js +1 -1
- package/test/basedriver/driver-tests.js +43 -9
- package/build/lib/protocol/sessions-cache.js +0 -88
- package/build/test/basedriver/capabilities-specs.js +0 -632
- package/build/test/basedriver/capability-specs.js +0 -396
- package/build/test/basedriver/commands/event-specs.js +0 -112
- package/build/test/basedriver/commands/log-specs.js +0 -80
- package/build/test/basedriver/driver-e2e-specs.js +0 -17
- package/build/test/basedriver/driver-specs.js +0 -17
- package/build/test/basedriver/helpers-e2e-specs.js +0 -194
- package/build/test/basedriver/helpers-specs.js +0 -153
- package/build/test/basedriver/timeout-specs.js +0 -139
- package/build/test/basedriver/websockets-e2e-specs.js +0 -84
- package/build/test/express/server-e2e-specs.js +0 -156
- package/build/test/express/server-specs.js +0 -151
- package/build/test/express/static-specs.js +0 -23
- package/build/test/jsonwp-proxy/mock-request.js +0 -93
- package/build/test/jsonwp-proxy/protocol-converter-specs.js +0 -173
- package/build/test/jsonwp-proxy/proxy-e2e-specs.js +0 -61
- package/build/test/jsonwp-proxy/proxy-specs.js +0 -294
- package/build/test/jsonwp-proxy/url-specs.js +0 -167
- package/build/test/jsonwp-status/status-specs.js +0 -36
- package/build/test/protocol/errors-specs.js +0 -388
- package/build/test/protocol/fake-driver.js +0 -168
- package/build/test/protocol/helpers.js +0 -27
- package/build/test/protocol/protocol-e2e-specs.js +0 -1182
- package/build/test/protocol/routes-specs.js +0 -82
- package/build/test/protocol/validator-specs.js +0 -151
- package/index.d.ts +0 -386
- package/lib/protocol/sessions-cache.js +0 -74
- package/test/basedriver/capabilities-specs.js +0 -505
- package/test/basedriver/capability-specs.js +0 -409
- package/test/basedriver/commands/event-specs.js +0 -74
- package/test/basedriver/commands/log-specs.js +0 -70
- package/test/basedriver/driver-e2e-specs.js +0 -8
- package/test/basedriver/driver-specs.js +0 -8
- package/test/basedriver/fixtures/BadZippedApp.zip +0 -1
- package/test/basedriver/fixtures/FakeAndroidApp.apk +0 -1
- package/test/basedriver/fixtures/FakeAndroidApp.asd +0 -0
- package/test/basedriver/fixtures/FakeIOSApp.app +0 -1
- package/test/basedriver/fixtures/FakeIOSApp.app.zip +0 -0
- package/test/basedriver/fixtures/FakeIOSApp.ipa +0 -0
- package/test/basedriver/fixtures/custom-element-finder-bad.js +0 -5
- package/test/basedriver/fixtures/custom-element-finder.js +0 -29
- package/test/basedriver/helpers-e2e-specs.js +0 -187
- package/test/basedriver/helpers-specs.js +0 -137
- package/test/basedriver/timeout-specs.js +0 -133
- package/test/basedriver/websockets-e2e-specs.js +0 -75
package/lib/basedriver/driver.js
CHANGED
|
@@ -1,28 +1,21 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
import
|
|
7
|
-
import commands from './commands';
|
|
8
|
-
import * as helpers from './helpers';
|
|
9
|
-
import log from './logger';
|
|
10
|
-
import DeviceSettings from './device-settings';
|
|
11
|
-
import { desiredCapabilityConstraints } from './desired-caps';
|
|
12
|
-
import { validateCaps } from './capabilities';
|
|
1
|
+
// @ts-check
|
|
2
|
+
/* eslint-disable require-await */
|
|
3
|
+
/* eslint-disable no-unused-vars */
|
|
4
|
+
|
|
5
|
+
import { DriverCore } from './core';
|
|
6
|
+
import { util } from '@appium/support';
|
|
13
7
|
import B from 'bluebird';
|
|
14
8
|
import _ from 'lodash';
|
|
15
|
-
import
|
|
16
|
-
import {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const NEW_COMMAND_TIMEOUT_MS = 60 * 1000;
|
|
9
|
+
import { fixCaps, isW3cCaps } from '../helpers/capabilities';
|
|
10
|
+
import { DELETE_SESSION_COMMAND, determineProtocol, errors } from '../protocol';
|
|
11
|
+
import {
|
|
12
|
+
APPIUM_OPTS_CAP,
|
|
13
|
+
PREFIXED_APPIUM_OPTS_CAP,
|
|
14
|
+
processCapabilities,
|
|
15
|
+
promoteAppiumOptions
|
|
16
|
+
} from './capabilities';
|
|
17
|
+
import { createBaseDriverClass } from './commands';
|
|
18
|
+
import helpers from './helpers';
|
|
26
19
|
|
|
27
20
|
const EVENT_SESSION_INIT = 'newSessionRequested';
|
|
28
21
|
const EVENT_SESSION_START = 'newSessionStarted';
|
|
@@ -30,286 +23,31 @@ const EVENT_SESSION_QUIT_START = 'quitSessionRequested';
|
|
|
30
23
|
const EVENT_SESSION_QUIT_DONE = 'quitSessionFinished';
|
|
31
24
|
const ON_UNEXPECTED_SHUTDOWN_EVENT = 'onUnexpectedShutdown';
|
|
32
25
|
|
|
33
|
-
class BaseDriver extends Protocol {
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Make the basedriver version available so for any driver which inherits from this package, we
|
|
37
|
-
* know which version of basedriver it inherited from
|
|
38
|
-
*/
|
|
39
|
-
static baseVersion = BASEDRIVER_VER;
|
|
40
|
-
|
|
41
|
-
constructor (opts = {}, shouldValidateCaps = true) {
|
|
42
|
-
super();
|
|
43
|
-
|
|
44
|
-
// setup state
|
|
45
|
-
this.sessionId = null;
|
|
46
|
-
this.opts = opts;
|
|
47
|
-
this.caps = null;
|
|
48
|
-
this.originalCaps = null; // To give the original capabilities to reset
|
|
49
|
-
this.helpers = helpers;
|
|
50
|
-
|
|
51
|
-
// basePath is used for several purposes, for example in setting up
|
|
52
|
-
// proxying to other drivers, since we need to know what the base path
|
|
53
|
-
// of any incoming request might look like. We set it to the default
|
|
54
|
-
// initially but it is automatically updated during any actual program
|
|
55
|
-
// execution by the routeConfiguringFunction, which is necessarily run as
|
|
56
|
-
// the entrypoint for any Appium server
|
|
57
|
-
this.basePath = DEFAULT_BASE_PATH;
|
|
58
|
-
|
|
59
|
-
// initialize security modes
|
|
60
|
-
this.relaxedSecurityEnabled = false;
|
|
61
|
-
this.allowInsecure = [];
|
|
62
|
-
this.denyInsecure = [];
|
|
63
|
-
|
|
64
|
-
// timeout initialization
|
|
65
|
-
this.newCommandTimeoutMs = NEW_COMMAND_TIMEOUT_MS;
|
|
66
|
-
this.implicitWaitMs = 0;
|
|
67
|
-
|
|
68
|
-
this._constraints = _.cloneDeep(desiredCapabilityConstraints);
|
|
69
|
-
this.locatorStrategies = [];
|
|
70
|
-
this.webLocatorStrategies = [];
|
|
71
|
-
|
|
72
|
-
// use a custom tmp dir to avoid losing data and app when computer is
|
|
73
|
-
// restarted
|
|
74
|
-
this.opts.tmpDir = this.opts.tmpDir ||
|
|
75
|
-
process.env.APPIUM_TMP_DIR ||
|
|
76
|
-
os.tmpdir();
|
|
77
|
-
|
|
78
|
-
// base-driver internals
|
|
79
|
-
this.shutdownUnexpectedly = false;
|
|
80
|
-
this.noCommandTimer = null;
|
|
81
|
-
this.shouldValidateCaps = shouldValidateCaps;
|
|
82
|
-
this.commandsQueueGuard = new AsyncLock();
|
|
83
|
-
|
|
84
|
-
// settings should be instantiated by drivers which extend BaseDriver, but
|
|
85
|
-
// we set it to an empty DeviceSettings instance here to make sure that the
|
|
86
|
-
// default settings are applied even if an extending driver doesn't utilize
|
|
87
|
-
// the settings functionality itself
|
|
88
|
-
this.settings = new DeviceSettings({}, _.noop);
|
|
89
|
-
|
|
90
|
-
// keeping track of initial opts
|
|
91
|
-
this.initialOpts = _.cloneDeep(this.opts);
|
|
92
|
-
|
|
93
|
-
// allow subclasses to have internal drivers
|
|
94
|
-
this.managedDrivers = [];
|
|
95
|
-
|
|
96
|
-
// store event timings
|
|
97
|
-
this._eventHistory = {
|
|
98
|
-
commands: [] // commands get a special place
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
// used to handle driver events
|
|
102
|
-
this.eventEmitter = new EventEmitter();
|
|
103
|
-
|
|
104
|
-
this.protocol = null;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Set a callback handler if needed to execute a custom piece of code
|
|
109
|
-
* when the driver is shut down unexpectedly. Multiple calls to this method
|
|
110
|
-
* will cause the handler to be executed mutiple times
|
|
111
|
-
*
|
|
112
|
-
* @param {Function} handler The code to be executed on unexpected shutdown.
|
|
113
|
-
* The function may accept one argument, which is the actual error instance, which
|
|
114
|
-
* caused the driver to shut down.
|
|
115
|
-
*/
|
|
116
|
-
onUnexpectedShutdown (handler) {
|
|
117
|
-
this.eventEmitter.on(ON_UNEXPECTED_SHUTDOWN_EVENT, handler);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* This property is used by AppiumDriver to store the data of the
|
|
122
|
-
* specific driver sessions. This data can be later used to adjust
|
|
123
|
-
* properties for driver instances running in parallel.
|
|
124
|
-
* Override it in inherited driver classes if necessary.
|
|
125
|
-
*
|
|
126
|
-
* @return {object} Driver properties mapping
|
|
127
|
-
*/
|
|
128
|
-
get driverData () {
|
|
129
|
-
return {};
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* This property controls the way {#executeCommand} method
|
|
134
|
-
* handles new driver commands received from the client.
|
|
135
|
-
* Override it for inherited classes only in special cases.
|
|
136
|
-
*
|
|
137
|
-
* @return {boolean} If the returned value is true (default) then all the commands
|
|
138
|
-
* received by the particular driver instance are going to be put into the queue,
|
|
139
|
-
* so each following command will not be executed until the previous command
|
|
140
|
-
* execution is completed. False value disables that queue, so each driver command
|
|
141
|
-
* is executed independently and does not wait for anything.
|
|
142
|
-
*/
|
|
143
|
-
get isCommandsQueueEnabled () {
|
|
144
|
-
return true;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/*
|
|
148
|
-
* make eventHistory a property and return a cloned object so a consumer can't
|
|
149
|
-
* inadvertently change data outside of logEvent
|
|
150
|
-
*/
|
|
151
|
-
get eventHistory () {
|
|
152
|
-
return _.cloneDeep(this._eventHistory);
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
/*
|
|
156
|
-
* API method for driver developers to log timings for important events
|
|
157
|
-
*/
|
|
158
|
-
logEvent (eventName) {
|
|
159
|
-
if (eventName === 'commands') {
|
|
160
|
-
throw new Error('Cannot log commands directly');
|
|
161
|
-
}
|
|
162
|
-
if (typeof eventName !== 'string') {
|
|
163
|
-
throw new Error(`Invalid eventName ${eventName}`);
|
|
164
|
-
}
|
|
165
|
-
if (!this._eventHistory[eventName]) {
|
|
166
|
-
this._eventHistory[eventName] = [];
|
|
167
|
-
}
|
|
168
|
-
const ts = Date.now();
|
|
169
|
-
const logTime = (new Date(ts)).toTimeString();
|
|
170
|
-
this._eventHistory[eventName].push(ts);
|
|
171
|
-
log.debug(`Event '${eventName}' logged at ${ts} (${logTime})`);
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
/*
|
|
175
|
-
* Overridden in appium driver, but here so that individual drivers can be
|
|
176
|
-
* tested with clients that poll
|
|
177
|
-
*/
|
|
178
|
-
async getStatus () { // eslint-disable-line require-await
|
|
179
|
-
return {};
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
// we only want subclasses to ever extend the contraints
|
|
183
|
-
set desiredCapConstraints (constraints) {
|
|
184
|
-
this._constraints = Object.assign(this._constraints, constraints);
|
|
185
|
-
// 'presence' means different things in different versions of the validator,
|
|
186
|
-
// when we say 'true' we mean that it should not be able to be empty
|
|
187
|
-
for (const [, value] of _.toPairs(this._constraints)) {
|
|
188
|
-
if (value && value.presence === true) {
|
|
189
|
-
value.presence = {
|
|
190
|
-
allowEmpty: false,
|
|
191
|
-
};
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
get desiredCapConstraints () {
|
|
197
|
-
return this._constraints;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
// method required by MJSONWP in order to determine whether it should
|
|
201
|
-
// respond with an invalid session response
|
|
202
|
-
sessionExists (sessionId) {
|
|
203
|
-
if (!sessionId) return false; // eslint-disable-line curly
|
|
204
|
-
return sessionId === this.sessionId;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
// method required by MJSONWP in order to determine if the command should
|
|
208
|
-
// be proxied directly to the driver
|
|
209
|
-
driverForSession (/*sessionId*/) {
|
|
210
|
-
return this;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
logExtraCaps (caps) {
|
|
214
|
-
let extraCaps = _.difference(_.keys(caps),
|
|
215
|
-
_.keys(this._constraints));
|
|
216
|
-
if (extraCaps.length) {
|
|
217
|
-
log.warn(`The following capabilities were provided, but are not ` +
|
|
218
|
-
`recognized by Appium:`);
|
|
219
|
-
for (const cap of extraCaps) {
|
|
220
|
-
log.warn(` ${cap}`);
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
validateDesiredCaps (caps) {
|
|
226
|
-
if (!this.shouldValidateCaps) {
|
|
227
|
-
return true;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
try {
|
|
231
|
-
validateCaps(caps, this._constraints);
|
|
232
|
-
} catch (e) {
|
|
233
|
-
log.errorAndThrow(new errors.SessionNotCreatedError(`The desiredCapabilities object was not valid for the ` +
|
|
234
|
-
`following reason(s): ${e.message}`));
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
this.logExtraCaps(caps);
|
|
238
|
-
|
|
239
|
-
return true;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
isMjsonwpProtocol () {
|
|
243
|
-
return this.protocol === PROTOCOLS.MJSONWP;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
isW3CProtocol () {
|
|
247
|
-
return this.protocol === PROTOCOLS.W3C;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
setProtocolMJSONWP () {
|
|
251
|
-
this.protocol = PROTOCOLS.MJSONWP;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
setProtocolW3C () {
|
|
255
|
-
this.protocol = PROTOCOLS.W3C;
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
/**
|
|
259
|
-
* Check whether a given feature is enabled via its name
|
|
260
|
-
*
|
|
261
|
-
* @param {string} name - name of feature/command
|
|
262
|
-
*
|
|
263
|
-
* @returns {Boolean}
|
|
264
|
-
*/
|
|
265
|
-
isFeatureEnabled (name) {
|
|
266
|
-
// if we have explicitly denied this feature, return false immediately
|
|
267
|
-
if (this.denyInsecure && _.includes(this.denyInsecure, name)) {
|
|
268
|
-
return false;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
// if we specifically have allowed the feature, return true
|
|
272
|
-
if (this.allowInsecure && _.includes(this.allowInsecure, name)) {
|
|
273
|
-
return true;
|
|
274
|
-
}
|
|
275
26
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
}
|
|
27
|
+
/**
|
|
28
|
+
* @implements {SessionHandler}
|
|
29
|
+
*/
|
|
30
|
+
export class BaseDriverCore extends DriverCore {
|
|
281
31
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
/**
|
|
287
|
-
* Assert that a given feature is enabled and throw a helpful error if it's
|
|
288
|
-
* not
|
|
289
|
-
*
|
|
290
|
-
* @param {string} name - name of feature/command
|
|
291
|
-
*/
|
|
292
|
-
ensureFeatureEnabled (name) {
|
|
293
|
-
if (!this.isFeatureEnabled(name)) {
|
|
294
|
-
throw new Error(`Potentially insecure feature '${name}' has not been ` +
|
|
295
|
-
`enabled. If you want to enable this feature and accept ` +
|
|
296
|
-
`the security ramifications, please do so by following ` +
|
|
297
|
-
`the documented instructions at https://github.com/appium` +
|
|
298
|
-
`/appium/blob/master/docs/en/writing-running-appium/security.md`);
|
|
299
|
-
}
|
|
300
|
-
}
|
|
32
|
+
/** @type {Record<string,any>|undefined} */
|
|
33
|
+
cliArgs;
|
|
301
34
|
|
|
302
35
|
// This is the main command handler for the driver. It wraps command
|
|
303
36
|
// execution with timeout logic, checking that we have a valid session,
|
|
304
37
|
// and ensuring that we execute commands one at a time. This method is called
|
|
305
38
|
// by MJSONWP's express router.
|
|
39
|
+
/**
|
|
40
|
+
* @param {string} cmd
|
|
41
|
+
* @param {...any[]} args
|
|
42
|
+
* @returns {Promise<any>}
|
|
43
|
+
*/
|
|
306
44
|
async executeCommand (cmd, ...args) {
|
|
307
45
|
// get start time for this command, and log in special cases
|
|
308
46
|
let startTime = Date.now();
|
|
309
47
|
|
|
310
48
|
if (cmd === 'createSession') {
|
|
311
49
|
// If creating a session determine if W3C or MJSONWP protocol was requested and remember the choice
|
|
312
|
-
this.protocol = determineProtocol(
|
|
50
|
+
this.protocol = determineProtocol(args);
|
|
313
51
|
this.logEvent(EVENT_SESSION_INIT);
|
|
314
52
|
} else if (cmd === DELETE_SESSION_COMMAND) {
|
|
315
53
|
this.logEvent(EVENT_SESSION_QUIT_START);
|
|
@@ -317,10 +55,12 @@ class BaseDriver extends Protocol {
|
|
|
317
55
|
|
|
318
56
|
// if we had a command timer running, clear it now that we're starting
|
|
319
57
|
// a new command and so don't want to time out
|
|
320
|
-
this.clearNewCommandTimeout();
|
|
58
|
+
await this.clearNewCommandTimeout();
|
|
321
59
|
|
|
322
60
|
if (this.shutdownUnexpectedly) {
|
|
323
|
-
throw new errors.NoSuchDriverError(
|
|
61
|
+
throw new errors.NoSuchDriverError(
|
|
62
|
+
'The driver was unexpectedly shut down!',
|
|
63
|
+
);
|
|
324
64
|
}
|
|
325
65
|
|
|
326
66
|
// If we don't have this command, it must not be implemented
|
|
@@ -329,19 +69,26 @@ class BaseDriver extends Protocol {
|
|
|
329
69
|
}
|
|
330
70
|
|
|
331
71
|
let unexpectedShutdownListener;
|
|
332
|
-
const commandExecutor = async () =>
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
72
|
+
const commandExecutor = async () =>
|
|
73
|
+
await B.race([
|
|
74
|
+
this[cmd](...args),
|
|
75
|
+
new B((resolve, reject) => {
|
|
76
|
+
unexpectedShutdownListener = reject;
|
|
77
|
+
this.eventEmitter.on(
|
|
78
|
+
ON_UNEXPECTED_SHUTDOWN_EVENT,
|
|
79
|
+
unexpectedShutdownListener,
|
|
80
|
+
);
|
|
81
|
+
}),
|
|
82
|
+
]).finally(() => {
|
|
83
|
+
if (unexpectedShutdownListener) {
|
|
84
|
+
// This is needed to prevent memory leaks
|
|
85
|
+
this.eventEmitter.removeListener(
|
|
86
|
+
ON_UNEXPECTED_SHUTDOWN_EVENT,
|
|
87
|
+
unexpectedShutdownListener,
|
|
88
|
+
);
|
|
89
|
+
unexpectedShutdownListener = null;
|
|
90
|
+
}
|
|
91
|
+
});
|
|
345
92
|
const res = this.isCommandsQueueEnabled
|
|
346
93
|
? await this.commandsQueueGuard.acquire(BaseDriver.name, commandExecutor)
|
|
347
94
|
: await commandExecutor();
|
|
@@ -354,7 +101,7 @@ class BaseDriver extends Protocol {
|
|
|
354
101
|
// intentionally
|
|
355
102
|
if (this.isCommandsQueueEnabled && cmd !== DELETE_SESSION_COMMAND) {
|
|
356
103
|
// resetting existing timeout
|
|
357
|
-
this.startNewCommandTimeout();
|
|
104
|
+
await this.startNewCommandTimeout();
|
|
358
105
|
}
|
|
359
106
|
|
|
360
107
|
// log timing information about this command
|
|
@@ -369,40 +116,77 @@ class BaseDriver extends Protocol {
|
|
|
369
116
|
return res;
|
|
370
117
|
}
|
|
371
118
|
|
|
372
|
-
|
|
119
|
+
/**
|
|
120
|
+
*
|
|
121
|
+
* @param {Error} err
|
|
122
|
+
*/
|
|
123
|
+
async startUnexpectedShutdown (
|
|
124
|
+
err = new errors.NoSuchDriverError(
|
|
125
|
+
'The driver was unexpectedly shut down!',
|
|
126
|
+
),
|
|
127
|
+
) {
|
|
373
128
|
this.eventEmitter.emit(ON_UNEXPECTED_SHUTDOWN_EVENT, err); // allow others to listen for this
|
|
374
129
|
this.shutdownUnexpectedly = true;
|
|
375
130
|
try {
|
|
376
|
-
|
|
131
|
+
if (this.sessionId !== null) {
|
|
132
|
+
await this.deleteSession(this.sessionId);
|
|
133
|
+
}
|
|
377
134
|
} finally {
|
|
378
135
|
this.shutdownUnexpectedly = false;
|
|
379
136
|
}
|
|
380
137
|
}
|
|
381
138
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
if
|
|
387
|
-
|
|
388
|
-
|
|
139
|
+
async startNewCommandTimeout () {
|
|
140
|
+
// make sure there are no rogue timeouts
|
|
141
|
+
await this.clearNewCommandTimeout();
|
|
142
|
+
|
|
143
|
+
// if command timeout is 0, it is disabled
|
|
144
|
+
if (!this.newCommandTimeoutMs) return; // eslint-disable-line curly
|
|
145
|
+
|
|
146
|
+
this.noCommandTimer = setTimeout(async () => {
|
|
147
|
+
this.log.warn(
|
|
148
|
+
`Shutting down because we waited ` +
|
|
149
|
+
`${this.newCommandTimeoutMs / 1000.0} seconds for a command`,
|
|
150
|
+
);
|
|
151
|
+
const errorMessage =
|
|
152
|
+
`New Command Timeout of ` +
|
|
153
|
+
`${this.newCommandTimeoutMs / 1000.0} seconds ` +
|
|
154
|
+
`expired. Try customizing the timeout using the ` +
|
|
155
|
+
`'newCommandTimeout' desired capability`;
|
|
156
|
+
await this.startUnexpectedShutdown(new Error(errorMessage));
|
|
157
|
+
}, this.newCommandTimeoutMs);
|
|
158
|
+
}
|
|
389
159
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
160
|
+
/**
|
|
161
|
+
*
|
|
162
|
+
* @param {import('@appium/types').AppiumServer} server
|
|
163
|
+
* @param {string} host
|
|
164
|
+
* @param {number} port
|
|
165
|
+
* @param {string} path
|
|
166
|
+
*/
|
|
167
|
+
assignServer (server, host, port, path) {
|
|
168
|
+
this.server = server;
|
|
169
|
+
this.serverHost = host;
|
|
170
|
+
this.serverPort = port;
|
|
171
|
+
this.serverPath = path;
|
|
393
172
|
}
|
|
394
173
|
|
|
395
174
|
/*
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
175
|
+
* Restart the session with the original caps,
|
|
176
|
+
* preserving the timeout config.
|
|
177
|
+
*/
|
|
399
178
|
async reset () {
|
|
400
|
-
log.debug('Resetting app mid-session');
|
|
401
|
-
log.debug('Running generic full reset');
|
|
179
|
+
this.log.debug('Resetting app mid-session');
|
|
180
|
+
this.log.debug('Running generic full reset');
|
|
402
181
|
|
|
403
182
|
// preserving state
|
|
404
183
|
let currentConfig = {};
|
|
405
|
-
for (let property of [
|
|
184
|
+
for (let property of [
|
|
185
|
+
'implicitWaitMs',
|
|
186
|
+
'newCommandTimeoutMs',
|
|
187
|
+
'sessionId',
|
|
188
|
+
'resetOnUnexpectedShutdown',
|
|
189
|
+
]) {
|
|
406
190
|
currentConfig[property] = this[property];
|
|
407
191
|
}
|
|
408
192
|
|
|
@@ -410,75 +194,193 @@ class BaseDriver extends Protocol {
|
|
|
410
194
|
this.resetOnUnexpectedShutdown = () => {};
|
|
411
195
|
|
|
412
196
|
try {
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
197
|
+
if (this.sessionId !== null) {
|
|
198
|
+
await this.deleteSession(this.sessionId);
|
|
199
|
+
}
|
|
200
|
+
this.log.debug('Restarting app');
|
|
201
|
+
await this.createSession(this.originalCaps);
|
|
416
202
|
} finally {
|
|
417
203
|
// always restore state.
|
|
418
204
|
for (let [key, value] of _.toPairs(currentConfig)) {
|
|
419
205
|
this[key] = value;
|
|
420
206
|
}
|
|
421
207
|
}
|
|
422
|
-
this.clearNewCommandTimeout();
|
|
208
|
+
await this.clearNewCommandTimeout();
|
|
423
209
|
}
|
|
424
210
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
211
|
+
/**
|
|
212
|
+
*
|
|
213
|
+
* Historically the first two arguments were reserved for JSONWP capabilities.
|
|
214
|
+
* Appium 2 has dropped the support of these, so now we only accept capability
|
|
215
|
+
* objects in W3C format and thus allow any of the three arguments to represent
|
|
216
|
+
* the latter.
|
|
217
|
+
* @param {W3CCapabilities} w3cCapabilities1
|
|
218
|
+
* @param {W3CCapabilities} [w3cCapabilities2]
|
|
219
|
+
* @param {W3CCapabilities} [w3cCapabilities]
|
|
220
|
+
* @param {DriverData[]} [driverData]
|
|
221
|
+
* @returns {Promise<[string,object]>}
|
|
222
|
+
*/
|
|
223
|
+
async createSession (
|
|
224
|
+
w3cCapabilities1,
|
|
225
|
+
w3cCapabilities2,
|
|
226
|
+
w3cCapabilities,
|
|
227
|
+
driverData,
|
|
228
|
+
) {
|
|
229
|
+
if (this.sessionId !== null) {
|
|
230
|
+
throw new errors.SessionNotCreatedError(
|
|
231
|
+
'Cannot create a new session while one is in progress',
|
|
232
|
+
);
|
|
233
|
+
}
|
|
428
234
|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
235
|
+
this.log.debug();
|
|
236
|
+
|
|
237
|
+
const originalCaps = _.cloneDeep([
|
|
238
|
+
w3cCapabilities,
|
|
239
|
+
w3cCapabilities1,
|
|
240
|
+
w3cCapabilities2,
|
|
241
|
+
].find(isW3cCaps));
|
|
242
|
+
if (!originalCaps) {
|
|
243
|
+
throw new errors.SessionNotCreatedError(
|
|
244
|
+
'Appium only supports W3C-style capability objects. ' +
|
|
245
|
+
'Your client is sending an older capabilities format. Please update your client library.',
|
|
246
|
+
);
|
|
247
|
+
}
|
|
432
248
|
|
|
433
|
-
|
|
434
|
-
return false;
|
|
435
|
-
}
|
|
249
|
+
this.setProtocolW3C();
|
|
436
250
|
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
}
|
|
459
|
-
if (!_.isRegExp(avoidPathRegex)) {
|
|
460
|
-
throw new Error('Proxy avoidance path must be a regular expression');
|
|
461
|
-
}
|
|
462
|
-
let normalizedUrl = url.replace(new RegExp(`^${_.escapeRegExp(this.basePath)}`), '');
|
|
463
|
-
if (avoidMethod === method && avoidPathRegex.test(normalizedUrl)) {
|
|
464
|
-
return true;
|
|
251
|
+
this.originalCaps = _.cloneDeep(originalCaps);
|
|
252
|
+
this.log.debug(
|
|
253
|
+
`Creating session with W3C capabilities: ${JSON.stringify(
|
|
254
|
+
originalCaps,
|
|
255
|
+
null,
|
|
256
|
+
2,
|
|
257
|
+
)}`,
|
|
258
|
+
);
|
|
259
|
+
|
|
260
|
+
let caps;
|
|
261
|
+
try {
|
|
262
|
+
caps = processCapabilities(
|
|
263
|
+
originalCaps,
|
|
264
|
+
this.desiredCapConstraints,
|
|
265
|
+
this.shouldValidateCaps,
|
|
266
|
+
);
|
|
267
|
+
if (caps[APPIUM_OPTS_CAP]) {
|
|
268
|
+
this.log.debug(
|
|
269
|
+
`Found ${PREFIXED_APPIUM_OPTS_CAP} capability present; will promote items inside to caps`,
|
|
270
|
+
);
|
|
271
|
+
caps = promoteAppiumOptions(caps);
|
|
465
272
|
}
|
|
273
|
+
caps = fixCaps(caps, this.desiredCapConstraints, this.log);
|
|
274
|
+
} catch (e) {
|
|
275
|
+
throw new errors.SessionNotCreatedError(e.message);
|
|
466
276
|
}
|
|
467
|
-
return false;
|
|
468
|
-
}
|
|
469
277
|
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
278
|
+
this.validateDesiredCaps(caps);
|
|
279
|
+
|
|
280
|
+
this.sessionId = util.uuidV4();
|
|
281
|
+
this.caps = caps;
|
|
282
|
+
this.opts = _.cloneDeep(this.initialOpts);
|
|
283
|
+
|
|
284
|
+
// merge caps onto opts so we don't need to worry about what's where
|
|
285
|
+
Object.assign(this.opts, this.caps);
|
|
473
286
|
|
|
474
|
-
|
|
475
|
-
|
|
287
|
+
// deal with resets
|
|
288
|
+
// some people like to do weird things by setting noReset and fullReset
|
|
289
|
+
// both to true, but this is misguided and strange, so error here instead
|
|
290
|
+
if (this.opts.noReset && this.opts.fullReset) {
|
|
291
|
+
throw new Error(
|
|
292
|
+
"The 'noReset' and 'fullReset' capabilities are mutually " +
|
|
293
|
+
'exclusive and should not both be set to true. You ' +
|
|
294
|
+
"probably meant to just use 'fullReset' on its own",
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
if (this.opts.noReset === true) {
|
|
298
|
+
this.opts.fullReset = false;
|
|
299
|
+
}
|
|
300
|
+
if (this.opts.fullReset === true) {
|
|
301
|
+
this.opts.noReset = false;
|
|
302
|
+
}
|
|
303
|
+
this.opts.fastReset = !this.opts.fullReset && !this.opts.noReset;
|
|
304
|
+
this.opts.skipUninstall = this.opts.fastReset || this.opts.noReset;
|
|
305
|
+
|
|
306
|
+
// Prevents empty string caps so we don't need to test it everywhere
|
|
307
|
+
if (typeof this.opts.app === 'string' && this.opts.app.trim() === '') {
|
|
308
|
+
delete this.opts.app;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
if (!_.isUndefined(this.caps.newCommandTimeout)) {
|
|
312
|
+
this.newCommandTimeoutMs = this.caps.newCommandTimeout * 1000;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
this._log.prefix = helpers.generateDriverLogPrefix(this, this.sessionId);
|
|
316
|
+
|
|
317
|
+
this.log.info(`Session created with session id: ${this.sessionId}`);
|
|
318
|
+
|
|
319
|
+
return [this.sessionId, caps];
|
|
476
320
|
}
|
|
477
|
-
}
|
|
478
321
|
|
|
479
|
-
|
|
480
|
-
|
|
322
|
+
/**
|
|
323
|
+
*
|
|
324
|
+
* @param {string} [sessionId]
|
|
325
|
+
* @param {DriverData[]} [driverData]
|
|
326
|
+
* @returns {Promise<void>}
|
|
327
|
+
*/
|
|
328
|
+
async deleteSession (sessionId, driverData) {
|
|
329
|
+
await this.clearNewCommandTimeout();
|
|
330
|
+
if (this.isCommandsQueueEnabled && this.commandsQueueGuard.isBusy()) {
|
|
331
|
+
// simple hack to release pending commands if they exist
|
|
332
|
+
// @ts-ignore
|
|
333
|
+
for (const key of _.keys(this.commandsQueueGuard.queues)) {
|
|
334
|
+
// @ts-ignore
|
|
335
|
+
this.commandsQueueGuard.queues[key] = [];
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
this.sessionId = null;
|
|
339
|
+
this._log.prefix = helpers.generateDriverLogPrefix(this);
|
|
340
|
+
}
|
|
481
341
|
}
|
|
482
342
|
|
|
343
|
+
/**
|
|
344
|
+
* This ensures that all of the mixins correctly implement the interface described in {@linkcode Driver}.
|
|
345
|
+
* @implements {Driver}
|
|
346
|
+
*/
|
|
347
|
+
class BaseDriver extends createBaseDriverClass(BaseDriverCore) {}
|
|
483
348
|
export { BaseDriver };
|
|
484
349
|
export default BaseDriver;
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* @typedef {import('@appium/types').HTTPMethod} HTTPMethod
|
|
353
|
+
* @typedef {import('@appium/types').Driver} Driver
|
|
354
|
+
* @typedef {import('@appium/types').ExternalDriver} ExternalDriver
|
|
355
|
+
* @typedef {import('@appium/types').Capabilities} Capabilities
|
|
356
|
+
* @typedef {import('@appium/types').W3CCapabilities} W3CCapabilities
|
|
357
|
+
* @typedef {import('@appium/types').DriverData} DriverData
|
|
358
|
+
*/
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* @callback UpdateServerCallback
|
|
363
|
+
* @param {import('express').Express} app - Express app
|
|
364
|
+
* @param {import('@appium/types').AppiumServer} httpServer - HTTP server
|
|
365
|
+
* @returns {import('type-fest').Promisable<void>}
|
|
366
|
+
*/
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* This is used to extend {@linkcode BaseDriverCore} by the mixins and also external drivers.
|
|
370
|
+
* @template [Proto={}]
|
|
371
|
+
* @template [Static={}]
|
|
372
|
+
* @typedef {import('@appium/types').Class<BaseDriverCore & Proto,BaseDriverStatic & Static>} BaseDriverBase
|
|
373
|
+
*/
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* Static properties of `BaseDriver` and optional properties for subclasses.
|
|
377
|
+
* @template {ExternalDriver} [T=ExternalDriver]
|
|
378
|
+
* @typedef BaseDriverStatic
|
|
379
|
+
* @property {string} baseVersion
|
|
380
|
+
* @property {UpdateServerCallback} [updateServer]
|
|
381
|
+
* @property {import('@appium/types').MethodMap<T>} [newMethodMap]
|
|
382
|
+
*/
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* @typedef {import('@appium/types').SessionHandler<[string, object],void>} SessionHandler
|
|
386
|
+
*/
|