@appium/base-driver 10.5.2 → 10.7.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 +1 -1
- package/build/lib/basedriver/capabilities.d.ts.map +1 -1
- package/build/lib/basedriver/capabilities.js +58 -50
- package/build/lib/basedriver/capabilities.js.map +1 -1
- package/build/lib/basedriver/commands/bidi.d.ts.map +1 -1
- package/build/lib/basedriver/commands/bidi.js +10 -14
- package/build/lib/basedriver/commands/bidi.js.map +1 -1
- package/build/lib/basedriver/commands/event.d.ts.map +1 -1
- package/build/lib/basedriver/commands/event.js +4 -7
- package/build/lib/basedriver/commands/event.js.map +1 -1
- package/build/lib/basedriver/commands/execute.js +3 -6
- package/build/lib/basedriver/commands/execute.js.map +1 -1
- package/build/lib/basedriver/commands/find.d.ts.map +1 -1
- package/build/lib/basedriver/commands/find.js +2 -1
- package/build/lib/basedriver/commands/find.js.map +1 -1
- package/build/lib/basedriver/commands/log.d.ts.map +1 -1
- package/build/lib/basedriver/commands/log.js +1 -5
- package/build/lib/basedriver/commands/log.js.map +1 -1
- package/build/lib/basedriver/commands/timeout.d.ts.map +1 -1
- package/build/lib/basedriver/commands/timeout.js +9 -13
- package/build/lib/basedriver/commands/timeout.js.map +1 -1
- package/build/lib/basedriver/core.d.ts.map +1 -1
- package/build/lib/basedriver/core.js +17 -14
- package/build/lib/basedriver/core.js.map +1 -1
- package/build/lib/basedriver/device-settings.d.ts.map +1 -1
- package/build/lib/basedriver/device-settings.js +3 -7
- package/build/lib/basedriver/device-settings.js.map +1 -1
- package/build/lib/basedriver/driver.d.ts.map +1 -1
- package/build/lib/basedriver/driver.js +34 -38
- package/build/lib/basedriver/driver.js.map +1 -1
- package/build/lib/basedriver/extension-core.d.ts +4 -1
- package/build/lib/basedriver/extension-core.d.ts.map +1 -1
- package/build/lib/basedriver/extension-core.js +37 -13
- package/build/lib/basedriver/extension-core.js.map +1 -1
- package/build/lib/basedriver/helpers.d.ts.map +1 -1
- package/build/lib/basedriver/helpers.js +47 -33
- package/build/lib/basedriver/helpers.js.map +1 -1
- package/build/lib/basedriver/ipc.d.ts +36 -0
- package/build/lib/basedriver/ipc.d.ts.map +1 -0
- package/build/lib/basedriver/ipc.js +157 -0
- package/build/lib/basedriver/ipc.js.map +1 -0
- package/build/lib/basedriver/validation.d.ts.map +1 -1
- package/build/lib/basedriver/validation.js +27 -29
- package/build/lib/basedriver/validation.js.map +1 -1
- package/build/lib/express/express-logging.d.ts +0 -1
- package/build/lib/express/express-logging.d.ts.map +1 -1
- package/build/lib/express/express-logging.js +11 -11
- package/build/lib/express/express-logging.js.map +1 -1
- package/build/lib/express/idempotency.js +3 -6
- package/build/lib/express/idempotency.js.map +1 -1
- package/build/lib/express/middleware.d.ts.map +1 -1
- package/build/lib/express/middleware.js +6 -10
- package/build/lib/express/middleware.js.map +1 -1
- package/build/lib/express/server.d.ts +1 -1
- package/build/lib/express/server.d.ts.map +1 -1
- package/build/lib/express/server.js +82 -73
- package/build/lib/express/server.js.map +1 -1
- package/build/lib/express/websocket.d.ts.map +1 -1
- package/build/lib/express/websocket.js +6 -9
- package/build/lib/express/websocket.js.map +1 -1
- package/build/lib/helpers/capabilities.d.ts.map +1 -1
- package/build/lib/helpers/capabilities.js +14 -17
- package/build/lib/helpers/capabilities.js.map +1 -1
- package/build/lib/helpers/extension-command-name.js +2 -5
- package/build/lib/helpers/extension-command-name.js.map +1 -1
- package/build/lib/helpers/levenshtein-match.d.ts.map +1 -1
- package/build/lib/helpers/levenshtein-match.js +6 -7
- package/build/lib/helpers/levenshtein-match.js.map +1 -1
- package/build/lib/index.d.ts +2 -1
- package/build/lib/index.d.ts.map +1 -1
- package/build/lib/index.js +6 -16
- package/build/lib/index.js.map +1 -1
- package/build/lib/jsonwp-proxy/protocol-converter.d.ts.map +1 -1
- package/build/lib/jsonwp-proxy/protocol-converter.js +21 -18
- package/build/lib/jsonwp-proxy/protocol-converter.js.map +1 -1
- package/build/lib/jsonwp-proxy/proxy-request.d.ts +2 -2
- package/build/lib/jsonwp-proxy/proxy-request.d.ts.map +1 -1
- package/build/lib/jsonwp-proxy/proxy-request.js +25 -21
- package/build/lib/jsonwp-proxy/proxy-request.js.map +1 -1
- package/build/lib/jsonwp-proxy/proxy.d.ts.map +1 -1
- package/build/lib/jsonwp-proxy/proxy.js +45 -36
- package/build/lib/jsonwp-proxy/proxy.js.map +1 -1
- package/build/lib/protocol/errors.d.ts.map +1 -1
- package/build/lib/protocol/errors.js +33 -37
- package/build/lib/protocol/errors.js.map +1 -1
- package/build/lib/protocol/helpers.d.ts.map +1 -1
- package/build/lib/protocol/helpers.js +9 -8
- package/build/lib/protocol/helpers.js.map +1 -1
- package/build/lib/protocol/protocol.d.ts +1 -1
- package/build/lib/protocol/protocol.d.ts.map +1 -1
- package/build/lib/protocol/protocol.js +73 -61
- package/build/lib/protocol/protocol.js.map +1 -1
- package/build/lib/protocol/routes.d.ts +1 -1
- package/build/lib/protocol/routes.d.ts.map +1 -1
- package/build/lib/protocol/routes.js +16 -17
- package/build/lib/protocol/routes.js.map +1 -1
- package/build/lib/protocol/validators.d.ts.map +1 -1
- package/build/lib/protocol/validators.js +1 -5
- package/build/lib/protocol/validators.js.map +1 -1
- package/build/lib/test-pages/crash.d.ts.map +1 -0
- package/build/lib/test-pages/crash.js.map +1 -0
- package/build/lib/test-pages/env.d.ts +5 -0
- package/build/lib/test-pages/env.d.ts.map +1 -0
- package/build/lib/test-pages/env.js +12 -0
- package/build/lib/test-pages/env.js.map +1 -0
- package/build/lib/{express/static.d.ts → test-pages/handlers.d.ts} +1 -2
- package/build/lib/test-pages/handlers.d.ts.map +1 -0
- package/build/lib/{express/static.js → test-pages/handlers.js} +9 -12
- package/build/lib/test-pages/handlers.js.map +1 -0
- package/build/lib/test-pages/index.d.ts +6 -0
- package/build/lib/test-pages/index.d.ts.map +1 -0
- package/build/lib/test-pages/index.js +35 -0
- package/build/lib/test-pages/index.js.map +1 -0
- package/build/lib/test-pages/static-dir.d.ts +8 -0
- package/build/lib/test-pages/static-dir.d.ts.map +1 -0
- package/build/lib/test-pages/static-dir.js +24 -0
- package/build/lib/test-pages/static-dir.js.map +1 -0
- package/build/lib/test-pages/template.d.ts +3 -0
- package/build/lib/test-pages/template.d.ts.map +1 -0
- package/build/lib/test-pages/template.js +19 -0
- package/build/lib/test-pages/template.js.map +1 -0
- package/build/lib/utils.d.ts +14 -0
- package/build/lib/utils.d.ts.map +1 -0
- package/build/lib/utils.js +55 -0
- package/build/lib/utils.js.map +1 -0
- package/lib/basedriver/capabilities.ts +126 -115
- package/lib/basedriver/commands/bidi.ts +11 -11
- package/lib/basedriver/commands/event.ts +17 -11
- package/lib/basedriver/commands/execute.ts +15 -12
- package/lib/basedriver/commands/find.ts +20 -12
- package/lib/basedriver/commands/log.ts +4 -3
- package/lib/basedriver/commands/timeout.ts +22 -14
- package/lib/basedriver/core.ts +26 -26
- package/lib/basedriver/device-settings.ts +7 -12
- package/lib/basedriver/driver.ts +62 -50
- package/lib/basedriver/extension-core.ts +60 -18
- package/lib/basedriver/helpers.ts +81 -52
- package/lib/basedriver/ipc.ts +198 -0
- package/lib/basedriver/validation.ts +37 -30
- package/lib/express/express-logging.ts +16 -20
- package/lib/express/idempotency.ts +9 -9
- package/lib/express/middleware.ts +14 -18
- package/lib/express/server.ts +118 -120
- package/lib/express/websocket.ts +11 -15
- package/lib/helpers/capabilities.ts +21 -16
- package/lib/helpers/extension-command-name.ts +3 -3
- package/lib/helpers/levenshtein-match.ts +20 -14
- package/lib/index.js +3 -12
- package/lib/jsonwp-proxy/protocol-converter.ts +58 -35
- package/lib/jsonwp-proxy/proxy-request.ts +26 -26
- package/lib/jsonwp-proxy/proxy.ts +74 -75
- package/lib/protocol/errors.ts +69 -88
- package/lib/protocol/helpers.ts +9 -5
- package/lib/protocol/protocol.ts +149 -107
- package/lib/protocol/routes.ts +17 -17
- package/lib/protocol/validators.ts +1 -3
- package/lib/test-pages/env.ts +9 -0
- package/lib/{express/static.ts → test-pages/handlers.ts} +10 -22
- package/lib/test-pages/index.ts +34 -0
- package/lib/test-pages/static-dir.ts +19 -0
- package/lib/test-pages/template.ts +17 -0
- package/lib/utils.ts +65 -0
- package/package.json +10 -13
- package/tsconfig.json +1 -0
- package/build/lib/express/crash.d.ts.map +0 -1
- package/build/lib/express/crash.js.map +0 -1
- package/build/lib/express/static.d.ts.map +0 -1
- package/build/lib/express/static.js.map +0 -1
- /package/build/lib/{express → test-pages}/crash.d.ts +0 -0
- /package/build/lib/{express → test-pages}/crash.js +0 -0
- /package/lib/{express → test-pages}/crash.ts +0 -0
- /package/{static → test-fixtures/static}/appium.png +0 -0
- /package/{static → test-fixtures/static}/favicon.ico +0 -0
- /package/{static → test-fixtures/static}/js/jquery.min.js +0 -0
- /package/{static → test-fixtures/static}/test/frameset.html +0 -0
- /package/{static → test-fixtures/static}/test/guinea-pig-app-banner.html +0 -0
- /package/{static → test-fixtures/static}/test/guinea-pig-scrollable.html +0 -0
- /package/{static → test-fixtures/static}/test/guinea-pig.html +0 -0
- /package/{static → test-fixtures/static}/test/guinea-pig2.html +0 -0
- /package/{static → test-fixtures/static}/test/guinea-pig3.html +0 -0
- /package/{static → test-fixtures/static}/test/guinea-pig4.html +0 -0
- /package/{static → test-fixtures/static}/test/guinea-pig5.html +0 -0
- /package/{static → test-fixtures/static}/test/iframes.html +0 -0
- /package/{static → test-fixtures/static}/test/shadow-dom.html +0 -0
- /package/{static → test-fixtures/static}/test/subframe1.html +0 -0
- /package/{static → test-fixtures/static}/test/subframe2.html +0 -0
- /package/{static → test-fixtures/static}/test/subframe3.html +0 -0
- /package/{static → test-fixtures/static}/test/touch.html +0 -0
- /package/{static → test-fixtures/static}/test/welcome.html +0 -0
package/lib/basedriver/driver.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type AsyncLock from 'async-lock';
|
|
1
2
|
import {util} from '@appium/support';
|
|
2
3
|
import {
|
|
3
4
|
BASE_DESIRED_CAP_CONSTRAINTS,
|
|
@@ -17,8 +18,6 @@ import {
|
|
|
17
18
|
type SingularSessionData,
|
|
18
19
|
type SessionCapabilities,
|
|
19
20
|
} from '@appium/types';
|
|
20
|
-
import B from 'bluebird';
|
|
21
|
-
import _ from 'lodash';
|
|
22
21
|
import {fixCaps, isW3cCaps} from '../helpers/capabilities';
|
|
23
22
|
import {getLevenshteinSuggestion} from '../helpers/levenshtein-match';
|
|
24
23
|
import {calcSignature} from '../helpers/session';
|
|
@@ -26,8 +25,12 @@ import {DELETE_SESSION_COMMAND, determineProtocol, errors} from '../protocol';
|
|
|
26
25
|
import {processCapabilities, validateCaps} from './capabilities';
|
|
27
26
|
import {DriverCore} from './core';
|
|
28
27
|
import * as helpers from './helpers';
|
|
28
|
+
import {mergePlainObjects} from '../utils';
|
|
29
29
|
import {resolveExecuteExtensionName} from '../helpers/extension-command-name';
|
|
30
30
|
|
|
31
|
+
type CommandInvoker<C extends Constraints> = BaseDriver<C> &
|
|
32
|
+
Record<string, ((...args: any[]) => any) | undefined>;
|
|
33
|
+
|
|
31
34
|
const EVENT_SESSION_INIT = 'newSessionRequested';
|
|
32
35
|
const EVENT_SESSION_START = 'newSessionStarted';
|
|
33
36
|
const EVENT_SESSION_QUIT_START = 'quitSessionRequested';
|
|
@@ -35,20 +38,20 @@ const EVENT_SESSION_QUIT_DONE = 'quitSessionFinished';
|
|
|
35
38
|
const ON_UNEXPECTED_SHUTDOWN_EVENT = 'onUnexpectedShutdown';
|
|
36
39
|
|
|
37
40
|
export class BaseDriver<
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
41
|
+
const C extends Constraints,
|
|
42
|
+
CArgs extends StringRecord = StringRecord,
|
|
43
|
+
Settings extends StringRecord = StringRecord,
|
|
44
|
+
CreateResult = DefaultCreateSessionResult<C>,
|
|
45
|
+
DeleteResult = DefaultDeleteSessionResult,
|
|
46
|
+
SessionData extends StringRecord = StringRecord,
|
|
47
|
+
>
|
|
45
48
|
extends DriverCore<C, Settings>
|
|
46
49
|
implements Driver<C, CArgs, Settings, CreateResult, DeleteResult, SessionData>
|
|
47
50
|
{
|
|
48
51
|
cliArgs: CArgs & ServerArgs;
|
|
49
52
|
caps: DriverCaps<C>;
|
|
50
|
-
originalCaps
|
|
51
|
-
desiredCapConstraints
|
|
53
|
+
originalCaps!: W3CDriverCaps<C>;
|
|
54
|
+
desiredCapConstraints!: C;
|
|
52
55
|
server?: AppiumServer;
|
|
53
56
|
serverHost?: string;
|
|
54
57
|
serverPort?: number;
|
|
@@ -69,7 +72,13 @@ export class BaseDriver<
|
|
|
69
72
|
* @see {@link https://github.com/appium/appium/issues/new}
|
|
70
73
|
*/
|
|
71
74
|
protected get _desiredCapConstraints(): Readonly<BaseDriverCapConstraints & C> {
|
|
72
|
-
return Object.freeze(
|
|
75
|
+
return Object.freeze(
|
|
76
|
+
mergePlainObjects(
|
|
77
|
+
{},
|
|
78
|
+
BASE_DESIRED_CAP_CONSTRAINTS,
|
|
79
|
+
this.desiredCapConstraints,
|
|
80
|
+
) as BaseDriverCapConstraints & C,
|
|
81
|
+
);
|
|
73
82
|
}
|
|
74
83
|
|
|
75
84
|
/**
|
|
@@ -98,8 +107,10 @@ export class BaseDriver<
|
|
|
98
107
|
throw new errors.NoSuchDriverError('The driver was unexpectedly shut down!');
|
|
99
108
|
}
|
|
100
109
|
|
|
110
|
+
const invoker = this as unknown as CommandInvoker<C>;
|
|
111
|
+
const command = invoker[cmd];
|
|
101
112
|
// If we don't have this command, it must not be implemented
|
|
102
|
-
if (!
|
|
113
|
+
if (!command) {
|
|
103
114
|
await this.startNewCommandTimeout();
|
|
104
115
|
throw new errors.NotYetImplementedError();
|
|
105
116
|
}
|
|
@@ -113,15 +124,15 @@ export class BaseDriver<
|
|
|
113
124
|
unexpectedShutdownRejecter?.(e);
|
|
114
125
|
};
|
|
115
126
|
try {
|
|
116
|
-
return await
|
|
117
|
-
this
|
|
127
|
+
return await Promise.race([
|
|
128
|
+
command.call(this, ...args),
|
|
118
129
|
// This promise is needed to monitor if the session has been
|
|
119
130
|
// shut down unexpectedly while the command was running
|
|
120
|
-
new
|
|
131
|
+
new Promise((resolve, reject) => {
|
|
121
132
|
unexpectedShutdownResolver = resolve;
|
|
122
133
|
unexpectedShutdownRejecter = reject;
|
|
123
134
|
this.eventEmitter.once(ON_UNEXPECTED_SHUTDOWN_EVENT, onUnexpectedShutdown);
|
|
124
|
-
})
|
|
135
|
+
}),
|
|
125
136
|
]);
|
|
126
137
|
} finally {
|
|
127
138
|
if (unexpectedShutdownRejecter && unexpectedShutdownResolver) {
|
|
@@ -138,7 +149,11 @@ export class BaseDriver<
|
|
|
138
149
|
// automatic session deletion in this.onCommandTimeout. Of course we don't
|
|
139
150
|
// want to trigger the timer when the user is shutting down the session
|
|
140
151
|
// intentionally
|
|
141
|
-
if (
|
|
152
|
+
if (
|
|
153
|
+
!wasSessionShutdownUnexpectedly &&
|
|
154
|
+
this.isCommandsQueueEnabled &&
|
|
155
|
+
cmd !== DELETE_SESSION_COMMAND
|
|
156
|
+
) {
|
|
142
157
|
// resetting existing timeout
|
|
143
158
|
await this.startNewCommandTimeout();
|
|
144
159
|
}
|
|
@@ -146,13 +161,15 @@ export class BaseDriver<
|
|
|
146
161
|
};
|
|
147
162
|
|
|
148
163
|
const synchronizationKey = BaseDriver.name;
|
|
149
|
-
|
|
150
|
-
|
|
164
|
+
const commandsQueueGuard = this.commandsQueueGuard as AsyncLock & {
|
|
165
|
+
queues?: Record<string, unknown[]>;
|
|
166
|
+
};
|
|
167
|
+
const commandsQueueLen: number = commandsQueueGuard.queues?.[synchronizationKey]?.length ?? 0;
|
|
151
168
|
if (this.isCommandsQueueEnabled && commandsQueueLen > 0) {
|
|
152
169
|
this.log.debug(
|
|
153
170
|
`Scheduling the '${cmd}' command to the ${this.constructor.name} commands queue. ` +
|
|
154
|
-
|
|
155
|
-
|
|
171
|
+
`${util.pluralize('queue item', commandsQueueLen, true)} ${commandsQueueLen === 1 ? 'is' : 'are'} ` +
|
|
172
|
+
`already waiting for execution.`,
|
|
156
173
|
);
|
|
157
174
|
}
|
|
158
175
|
|
|
@@ -180,8 +197,8 @@ export class BaseDriver<
|
|
|
180
197
|
clarifyCommandName(cmd: string, args: string[]): string {
|
|
181
198
|
if (cmd === 'execute') {
|
|
182
199
|
const firstArg = args?.[0];
|
|
183
|
-
if (
|
|
184
|
-
return resolveExecuteExtensionName.call(this
|
|
200
|
+
if (typeof firstArg === 'string' && firstArg.trim().length > 0) {
|
|
201
|
+
return resolveExecuteExtensionName.call(this as BaseDriver<Constraints>, firstArg);
|
|
185
202
|
}
|
|
186
203
|
}
|
|
187
204
|
|
|
@@ -239,15 +256,12 @@ export class BaseDriver<
|
|
|
239
256
|
this.log.debug('Running generic full reset');
|
|
240
257
|
|
|
241
258
|
// preserving state
|
|
242
|
-
const currentConfig = {
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
]) {
|
|
249
|
-
currentConfig[property] = this[property];
|
|
250
|
-
}
|
|
259
|
+
const currentConfig = {
|
|
260
|
+
implicitWaitMs: this.implicitWaitMs,
|
|
261
|
+
newCommandTimeoutMs: this.newCommandTimeoutMs,
|
|
262
|
+
sessionId: this.sessionId,
|
|
263
|
+
shutdownUnexpectedly: this.shutdownUnexpectedly,
|
|
264
|
+
};
|
|
251
265
|
|
|
252
266
|
try {
|
|
253
267
|
if (this.sessionId !== null) {
|
|
@@ -257,9 +271,7 @@ export class BaseDriver<
|
|
|
257
271
|
await this.createSession(this.originalCaps);
|
|
258
272
|
} finally {
|
|
259
273
|
// always restore state.
|
|
260
|
-
|
|
261
|
-
this[key] = value;
|
|
262
|
-
}
|
|
274
|
+
Object.assign(this, currentConfig);
|
|
263
275
|
}
|
|
264
276
|
await this.clearNewCommandTimeout();
|
|
265
277
|
}
|
|
@@ -286,7 +298,7 @@ export class BaseDriver<
|
|
|
286
298
|
|
|
287
299
|
this.log.debug();
|
|
288
300
|
|
|
289
|
-
const originalCaps =
|
|
301
|
+
const originalCaps = structuredClone(
|
|
290
302
|
[w3cCapabilities, w3cCapabilities1, w3cCapabilities2].find(isW3cCaps),
|
|
291
303
|
);
|
|
292
304
|
if (!originalCaps) {
|
|
@@ -312,7 +324,8 @@ export class BaseDriver<
|
|
|
312
324
|
) as DriverCaps<C>;
|
|
313
325
|
caps = fixCaps(caps, this._desiredCapConstraints, this.log) as DriverCaps<C>;
|
|
314
326
|
} catch (e) {
|
|
315
|
-
|
|
327
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
328
|
+
throw new errors.SessionNotCreatedError(message);
|
|
316
329
|
}
|
|
317
330
|
|
|
318
331
|
this.validateDesiredCaps(caps);
|
|
@@ -321,7 +334,7 @@ export class BaseDriver<
|
|
|
321
334
|
this.sessionCreationTimestampMs = Date.now();
|
|
322
335
|
this.caps = caps;
|
|
323
336
|
// merge caps onto opts so we don't need to worry about what's where
|
|
324
|
-
this.opts = {...
|
|
337
|
+
this.opts = {...structuredClone(this.initialOpts), ...this.caps};
|
|
325
338
|
|
|
326
339
|
// deal with resets
|
|
327
340
|
// some people like to do weird things by setting noReset and fullReset
|
|
@@ -347,7 +360,7 @@ export class BaseDriver<
|
|
|
347
360
|
delete this.opts.app;
|
|
348
361
|
}
|
|
349
362
|
|
|
350
|
-
if (
|
|
363
|
+
if (this.caps.newCommandTimeout !== undefined) {
|
|
351
364
|
this.newCommandTimeoutMs = (this.caps.newCommandTimeout as number) * 1000;
|
|
352
365
|
}
|
|
353
366
|
|
|
@@ -388,7 +401,7 @@ export class BaseDriver<
|
|
|
388
401
|
// simple hack to release pending commands if they exist
|
|
389
402
|
// @ts-expect-error private API
|
|
390
403
|
const queues = this.commandsQueueGuard.queues;
|
|
391
|
-
for (const key of
|
|
404
|
+
for (const key of Object.keys(queues)) {
|
|
392
405
|
queues[key] = [];
|
|
393
406
|
}
|
|
394
407
|
}
|
|
@@ -396,17 +409,14 @@ export class BaseDriver<
|
|
|
396
409
|
}
|
|
397
410
|
|
|
398
411
|
logExtraCaps(caps: Capabilities<C>) {
|
|
399
|
-
const knownCaps =
|
|
400
|
-
const
|
|
412
|
+
const knownCaps = Object.keys(this._desiredCapConstraints);
|
|
413
|
+
const knownCapsSet = new Set(knownCaps);
|
|
414
|
+
const extraCaps = Object.keys(caps).filter((cap) => !knownCapsSet.has(cap));
|
|
401
415
|
if (extraCaps.length) {
|
|
402
416
|
this.log.warn(`The following provided capabilities were not recognized by this driver:`);
|
|
403
417
|
for (const cap of extraCaps) {
|
|
404
418
|
const suggestion = getLevenshteinSuggestion(cap, knownCaps);
|
|
405
|
-
this.log.warn(
|
|
406
|
-
suggestion
|
|
407
|
-
? ` ${cap} (did you mean '${suggestion}'?)`
|
|
408
|
-
: ` ${cap}`,
|
|
409
|
-
);
|
|
419
|
+
this.log.warn(suggestion ? ` ${cap} (did you mean '${suggestion}'?)` : ` ${cap}`);
|
|
410
420
|
}
|
|
411
421
|
}
|
|
412
422
|
}
|
|
@@ -419,11 +429,13 @@ export class BaseDriver<
|
|
|
419
429
|
try {
|
|
420
430
|
validateCaps(caps, this._desiredCapConstraints);
|
|
421
431
|
} catch (e) {
|
|
432
|
+
const capError = e instanceof Error ? e : new Error(String(e));
|
|
422
433
|
throw this.log.errorWithException(
|
|
423
434
|
new errors.SessionNotCreatedError(
|
|
424
435
|
`Session capabilities were not valid for the ` +
|
|
425
|
-
|
|
426
|
-
|
|
436
|
+
`following reason(s): ${capError.message}`,
|
|
437
|
+
capError,
|
|
438
|
+
),
|
|
427
439
|
);
|
|
428
440
|
}
|
|
429
441
|
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import {logger} from '@appium/support';
|
|
1
|
+
import {logger, util} from '@appium/support';
|
|
2
2
|
import {EventEmitter} from 'node:events';
|
|
3
3
|
import type {
|
|
4
4
|
AppiumLogger,
|
|
5
5
|
BidiModuleMap,
|
|
6
6
|
BiDiResultData,
|
|
7
|
+
IAppiumIpc,
|
|
8
|
+
IIpcSubscription,
|
|
9
|
+
IpcData,
|
|
7
10
|
StringRecord,
|
|
8
11
|
} from '@appium/types';
|
|
9
|
-
import {
|
|
10
|
-
MAX_LOG_BODY_LENGTH,
|
|
11
|
-
} from '../constants';
|
|
12
|
+
import {MAX_LOG_BODY_LENGTH} from '../constants';
|
|
12
13
|
import {errors} from '../protocol';
|
|
13
14
|
import {BIDI_COMMANDS} from '../protocol/bidi-commands';
|
|
14
|
-
import _ from 'lodash';
|
|
15
15
|
import {generateDriverLogPrefix} from './helpers';
|
|
16
16
|
|
|
17
17
|
export class ExtensionCore {
|
|
@@ -20,8 +20,8 @@ export class ExtensionCore {
|
|
|
20
20
|
_logPrefix?: string;
|
|
21
21
|
// used to handle driver events
|
|
22
22
|
readonly eventEmitter: NodeJS.EventEmitter;
|
|
23
|
-
protected _log
|
|
24
|
-
|
|
23
|
+
protected _log!: AppiumLogger;
|
|
24
|
+
private ipc?: IAppiumIpc;
|
|
25
25
|
|
|
26
26
|
constructor(logPrefix?: string) {
|
|
27
27
|
this._logPrefix = logPrefix;
|
|
@@ -41,9 +41,11 @@ export class ExtensionCore {
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
updateBidiCommands(cmds: BidiModuleMap): void {
|
|
44
|
-
const overlappingKeys =
|
|
44
|
+
const overlappingKeys = Object.keys(cmds).filter((key) => key in this.bidiCommands);
|
|
45
45
|
if (overlappingKeys.length) {
|
|
46
|
-
this.log.warn(
|
|
46
|
+
this.log.warn(
|
|
47
|
+
`Overwriting existing bidi modules: ${JSON.stringify(overlappingKeys)}. This may not be intended!`,
|
|
48
|
+
);
|
|
47
49
|
}
|
|
48
50
|
this.bidiCommands = {
|
|
49
51
|
...this.bidiCommands,
|
|
@@ -71,7 +73,7 @@ export class ExtensionCore {
|
|
|
71
73
|
}
|
|
72
74
|
|
|
73
75
|
// if the command module or method isn't part of our spec, reject
|
|
74
|
-
if (!
|
|
76
|
+
if (!this.bidiCommands[moduleName]?.[methodName]) {
|
|
75
77
|
throw new errors.UnknownCommandError();
|
|
76
78
|
}
|
|
77
79
|
|
|
@@ -82,13 +84,19 @@ export class ExtensionCore {
|
|
|
82
84
|
}
|
|
83
85
|
|
|
84
86
|
// If the driver doesn't have this command, it must not be implemented
|
|
85
|
-
|
|
87
|
+
const handler = (this as ExtensionCore & Record<string, unknown>)[command];
|
|
88
|
+
if (typeof handler !== 'function') {
|
|
86
89
|
throw new errors.NotYetImplementedError();
|
|
87
90
|
}
|
|
88
91
|
}
|
|
89
92
|
|
|
90
|
-
async executeBidiCommand(
|
|
91
|
-
|
|
93
|
+
async executeBidiCommand(
|
|
94
|
+
bidiCmd: string,
|
|
95
|
+
bidiParams: StringRecord,
|
|
96
|
+
next?: () => Promise<any>,
|
|
97
|
+
driver?: ExtensionCore,
|
|
98
|
+
): Promise<BiDiResultData> {
|
|
99
|
+
const handlerType = next && driver ? 'plugin' : 'driver';
|
|
92
100
|
const [moduleName, methodName] = bidiCmd.split('.');
|
|
93
101
|
this.ensureBidiCommandExists(moduleName, methodName);
|
|
94
102
|
const {command, params} = this.bidiCommands[moduleName][methodName];
|
|
@@ -98,7 +106,7 @@ export class ExtensionCore {
|
|
|
98
106
|
const args: any[] = [];
|
|
99
107
|
if (params?.required?.length) {
|
|
100
108
|
for (const requiredParam of params.required) {
|
|
101
|
-
if (
|
|
109
|
+
if (bidiParams[requiredParam] === undefined) {
|
|
102
110
|
throw new errors.InvalidArgumentError(
|
|
103
111
|
`The ${requiredParam} parameter was required but you omitted it`,
|
|
104
112
|
);
|
|
@@ -111,18 +119,52 @@ export class ExtensionCore {
|
|
|
111
119
|
args.push(bidiParams[optionalParam]);
|
|
112
120
|
}
|
|
113
121
|
}
|
|
114
|
-
const logParams =
|
|
122
|
+
const logParams = util.truncateString(JSON.stringify(bidiParams), {
|
|
123
|
+
length: MAX_LOG_BODY_LENGTH,
|
|
124
|
+
});
|
|
115
125
|
this.log.debug(
|
|
116
126
|
`Executing bidi command '${bidiCmd}' with params ${logParams} by passing to ${handlerType} ` +
|
|
117
127
|
`method '${command}'`,
|
|
118
128
|
);
|
|
119
129
|
// call the handler with the signature appropriate to extension type (plugin or driver)
|
|
120
|
-
const
|
|
121
|
-
|
|
130
|
+
const commandHandler = (
|
|
131
|
+
this as unknown as Record<string, (...handlerArgs: any[]) => Promise<unknown>>
|
|
132
|
+
)[command];
|
|
133
|
+
const response =
|
|
134
|
+
next && driver
|
|
135
|
+
? await commandHandler.call(this, next, driver, ...args)
|
|
136
|
+
: await commandHandler.call(this, ...args);
|
|
137
|
+
const finalResponse: BiDiResultData =
|
|
138
|
+
response === undefined ? {} : (response as BiDiResultData);
|
|
122
139
|
this.log.debug(
|
|
123
140
|
`Responding to bidi command '${bidiCmd}' with ` +
|
|
124
|
-
|
|
141
|
+
`${util.truncateString(JSON.stringify(finalResponse), {length: MAX_LOG_BODY_LENGTH})}`,
|
|
125
142
|
);
|
|
126
143
|
return finalResponse;
|
|
127
144
|
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* @internal Used by AppiumDriver to wire session IPC; extension authors should use {@link onIpcInit} instead.
|
|
148
|
+
*/
|
|
149
|
+
async assignIpc(ipc: IAppiumIpc): Promise<void> {
|
|
150
|
+
this.ipc = ipc;
|
|
151
|
+
try {
|
|
152
|
+
await this.onIpcInit();
|
|
153
|
+
} catch (e) {
|
|
154
|
+
this.log.error(`Error running onIpcInit: `, e);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
async onIpcInit(): Promise<void> {}
|
|
159
|
+
|
|
160
|
+
ipcSubscribe<T extends IpcData>(topic: string): IIpcSubscription<T> {
|
|
161
|
+
if (!this.ipc) {
|
|
162
|
+
throw new Error(
|
|
163
|
+
`Cannot subscribe to an IPC topic without an IPC object assigned. ` +
|
|
164
|
+
`This is likely a programming error. ipcSubscribe should be called in the ` +
|
|
165
|
+
`onIpcInit handler or after you are certain that createSession has completed successfully.`,
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
return this.ipc.subscribe<T>(topic, generateDriverLogPrefix(this));
|
|
169
|
+
}
|
|
128
170
|
}
|