@appium/base-driver 10.2.2 → 10.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 +4 -0
- package/build/lib/basedriver/capabilities.d.ts.map +1 -1
- package/build/lib/basedriver/capabilities.js +4 -0
- package/build/lib/basedriver/capabilities.js.map +1 -1
- package/build/lib/basedriver/commands/execute.js +7 -17
- package/build/lib/basedriver/commands/execute.js.map +1 -1
- package/build/lib/basedriver/core.d.ts +24 -24
- package/build/lib/basedriver/core.d.ts.map +1 -1
- package/build/lib/basedriver/core.js +29 -29
- package/build/lib/basedriver/core.js.map +1 -1
- package/build/lib/basedriver/driver.d.ts.map +1 -1
- package/build/lib/basedriver/driver.js +7 -2
- package/build/lib/basedriver/driver.js.map +1 -1
- package/build/lib/basedriver/extension-core.d.ts +1 -1
- package/build/lib/basedriver/extension-core.d.ts.map +1 -1
- package/build/lib/basedriver/extension-core.js +1 -1
- 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 +2 -2
- package/build/lib/basedriver/helpers.js.map +1 -1
- package/build/lib/helpers/levenshtein-match.d.ts +27 -0
- package/build/lib/helpers/levenshtein-match.d.ts.map +1 -0
- package/build/lib/helpers/levenshtein-match.js +61 -0
- package/build/lib/helpers/levenshtein-match.js.map +1 -0
- package/build/lib/jsonwp-proxy/protocol-converter.d.ts +1 -1
- package/build/lib/jsonwp-proxy/protocol-converter.d.ts.map +1 -1
- package/build/lib/jsonwp-proxy/protocol-converter.js +3 -3
- package/build/lib/jsonwp-proxy/protocol-converter.js.map +1 -1
- package/build/lib/jsonwp-proxy/proxy.d.ts +53 -98
- package/build/lib/jsonwp-proxy/proxy.d.ts.map +1 -1
- package/build/lib/jsonwp-proxy/proxy.js +102 -145
- package/build/lib/jsonwp-proxy/proxy.js.map +1 -1
- package/build/lib/protocol/errors.d.ts +45 -45
- package/build/lib/protocol/errors.d.ts.map +1 -1
- package/build/lib/protocol/errors.js +162 -162
- package/build/lib/protocol/errors.js.map +1 -1
- package/build/lib/protocol/protocol.d.ts +35 -0
- package/build/lib/protocol/protocol.d.ts.map +1 -1
- package/build/lib/protocol/protocol.js +105 -77
- package/build/lib/protocol/protocol.js.map +1 -1
- package/build/lib/protocol/routes.d.ts +6 -0
- package/build/lib/protocol/routes.d.ts.map +1 -1
- package/build/lib/protocol/routes.js +6 -0
- package/build/lib/protocol/routes.js.map +1 -1
- package/lib/basedriver/capabilities.ts +4 -0
- package/lib/basedriver/commands/execute.ts +7 -18
- package/lib/basedriver/core.ts +34 -34
- package/lib/basedriver/driver.ts +9 -2
- package/lib/basedriver/extension-core.ts +1 -1
- package/lib/basedriver/helpers.ts +21 -21
- package/lib/helpers/levenshtein-match.ts +74 -0
- package/lib/jsonwp-proxy/protocol-converter.ts +4 -4
- package/lib/jsonwp-proxy/proxy.ts +506 -0
- package/lib/protocol/errors.ts +281 -246
- package/lib/protocol/protocol.ts +121 -93
- package/lib/protocol/routes.ts +6 -0
- package/package.json +10 -10
- package/tsconfig.json +6 -0
- package/lib/jsonwp-proxy/proxy.js +0 -493
package/lib/protocol/protocol.ts
CHANGED
|
@@ -30,6 +30,10 @@ export const LIST_DRIVER_EXTENSIONS_COMMAND = 'listExtensions';
|
|
|
30
30
|
|
|
31
31
|
export const deprecatedCommandsLogged: Set<string> = new Set();
|
|
32
32
|
|
|
33
|
+
/**
|
|
34
|
+
* Infer W3C vs MJSONWP from new-session capability payloads.
|
|
35
|
+
* @param createSessionArgs - Arguments passed to the createSession command
|
|
36
|
+
*/
|
|
33
37
|
export function determineProtocol(createSessionArgs: any[]): keyof typeof PROTOCOLS {
|
|
34
38
|
return _.some(createSessionArgs, isW3cCaps) ? PROTOCOLS.W3C : PROTOCOLS.MJSONWP;
|
|
35
39
|
}
|
|
@@ -63,74 +67,20 @@ export function getSessionId(driver: Core<any>, req: Request): string | undefine
|
|
|
63
67
|
return req.params.sessionId;
|
|
64
68
|
}
|
|
65
69
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
if (dstDriver === driver) {
|
|
71
|
-
// Shortcircuit if the driver instance is not an umbrella driver
|
|
72
|
-
// or it is Fake driver instance, where `driver.driverForSession`
|
|
73
|
-
// always returns self instance
|
|
74
|
-
return driver.protocol ?? PROTOCOLS.W3C;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Extract the protocol for the current session if the given driver is the umbrella one
|
|
78
|
-
return dstDriver?.protocol ?? PROTOCOLS.W3C;
|
|
79
|
-
}
|
|
80
|
-
|
|
70
|
+
/**
|
|
71
|
+
* @param command - Driver command name
|
|
72
|
+
* @returns Whether the command requires a session id in the URL
|
|
73
|
+
*/
|
|
81
74
|
export function isSessionCommand(command: string): boolean {
|
|
82
75
|
return !_.includes(NO_SESSION_ID_COMMANDS, command);
|
|
83
76
|
}
|
|
84
77
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
return dstDriver.log;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
const logPrefix = generateDriverLogPrefix(dstDriver);
|
|
95
|
-
return logger.getLogger(logPrefix);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
function wrapParams<T>(paramSets, jsonObj: T): T | Record<string, T> {
|
|
99
|
-
/* There are commands like performTouch which take a single parameter (primitive type or array).
|
|
100
|
-
* Some drivers choose to pass this parameter as a value (eg. [action1, action2...]) while others to
|
|
101
|
-
* wrap it within an object(eg' {gesture: [action1, action2...]}), which makes it hard to validate.
|
|
102
|
-
* The wrap option in the spec enforce wrapping before validation, so that all params are wrapped at
|
|
103
|
-
* the time they are validated and later passed to the commands.
|
|
104
|
-
*/
|
|
105
|
-
return (_.isArray(jsonObj) || !_.isObject(jsonObj)) && paramSets.wrap
|
|
106
|
-
? {[paramSets.wrap]: jsonObj}
|
|
107
|
-
: jsonObj;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
function unwrapParams<T>(paramSets: PayloadParams, jsonObj: T): T | Record<string, T> {
|
|
111
|
-
/* There are commands like setNetworkConnection which send parameters wrapped inside a key such as
|
|
112
|
-
* "parameters". This function unwraps them (eg. {"parameters": {"type": 1}} becomes {"type": 1}).
|
|
113
|
-
*/
|
|
114
|
-
return _.isObject(jsonObj) && paramSets.unwrap && jsonObj[paramSets.unwrap]
|
|
115
|
-
? jsonObj[paramSets.unwrap]
|
|
116
|
-
: jsonObj;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
function hasMultipleRequiredParamSets(
|
|
120
|
-
required: ReadonlyArray<string> | MultidimensionalReadonlyArray<string, 2> | undefined
|
|
121
|
-
): required is MultidimensionalReadonlyArray<string, 2> {
|
|
122
|
-
//@ts-expect-error Needed to convince lodash typechecks
|
|
123
|
-
return Boolean(required && _.isArray(_.first(required)));
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
function pickKnownParams(args: Record<string, any>, unknownNames: string[]): Record<string, any> {
|
|
127
|
-
if (_.isEmpty(unknownNames)) {
|
|
128
|
-
return args;
|
|
129
|
-
}
|
|
130
|
-
log.info(`The following arguments are not known and will be ignored: ${unknownNames}`);
|
|
131
|
-
return _.pickBy(args, (v, k) => !unknownNames.includes(k));
|
|
132
|
-
}
|
|
133
|
-
|
|
78
|
+
/**
|
|
79
|
+
* Validate request arguments against a route payload spec and return filtered params.
|
|
80
|
+
* @param paramSpec - Required/optional parameter definition from the method map
|
|
81
|
+
* @param args - Raw arguments (e.g. JSON body)
|
|
82
|
+
* @param protocol - Active protocol, used when a custom validate function is present
|
|
83
|
+
*/
|
|
134
84
|
export function checkParams(
|
|
135
85
|
paramSpec: PayloadParams,
|
|
136
86
|
args: Record<string, any>,
|
|
@@ -207,12 +157,11 @@ export function checkParams(
|
|
|
207
157
|
}, actualParamNames);
|
|
208
158
|
}
|
|
209
159
|
|
|
210
|
-
|
|
211
|
-
*
|
|
212
|
-
*
|
|
213
|
-
*
|
|
214
|
-
*
|
|
215
|
-
* be applied to a command.
|
|
160
|
+
/**
|
|
161
|
+
* Build the ordered argument list for a driver command from URL params, JSON body, and route spec.
|
|
162
|
+
* @param requestParams - Express route parameters (e.g. sessionId, element id)
|
|
163
|
+
* @param jsonObj - Parsed JSON request body
|
|
164
|
+
* @param payloadParams - Route payload definition (required/optional/makeArgs)
|
|
216
165
|
*/
|
|
217
166
|
export function makeArgs(requestParams: PayloadParams, jsonObj: any, payloadParams: PayloadParams): any[] {
|
|
218
167
|
// We want to pass the "url" parameters to the commands in reverse order
|
|
@@ -264,6 +213,11 @@ export function makeArgs(requestParams: PayloadParams, jsonObj: any, payloadPara
|
|
|
264
213
|
return args;
|
|
265
214
|
}
|
|
266
215
|
|
|
216
|
+
/**
|
|
217
|
+
* Validate parameters for execute/executeAsync script endpoints.
|
|
218
|
+
* @param params - Raw execute command arguments from the client
|
|
219
|
+
* @param paramSpec - Optional payload spec for additional validation
|
|
220
|
+
*/
|
|
267
221
|
export function validateExecuteMethodParams(params: any[], paramSpec?: PayloadParams): any[] {
|
|
268
222
|
// the w3c protocol will give us an array of arguments to apply to a javascript function.
|
|
269
223
|
// that's not what we're doing. we're going to look for a JS object as the first arg, so we
|
|
@@ -290,7 +244,10 @@ export function validateExecuteMethodParams(params: any[], paramSpec?: PayloadPa
|
|
|
290
244
|
return makeArgs({}, filteredArgs, specToUse);
|
|
291
245
|
}
|
|
292
246
|
|
|
293
|
-
|
|
247
|
+
/**
|
|
248
|
+
* Returns a function that registers default (and plugin) HTTP routes on an Express app for a driver.
|
|
249
|
+
* @param driver - Driver instance used to execute commands
|
|
250
|
+
*/
|
|
294
251
|
export function routeConfiguringFunction(driver: Core<any>): RouteConfiguringFunction {
|
|
295
252
|
if (!driver.sessionExists) {
|
|
296
253
|
throw new Error('Drivers must implement `sessionExists` property');
|
|
@@ -325,6 +282,99 @@ export function routeConfiguringFunction(driver: Core<any>): RouteConfiguringFun
|
|
|
325
282
|
};
|
|
326
283
|
}
|
|
327
284
|
|
|
285
|
+
/**
|
|
286
|
+
* Whether an incoming request should be forwarded to the driver's JWProxy for the given command.
|
|
287
|
+
* @param driver - Active driver
|
|
288
|
+
* @param req - Incoming HTTP request
|
|
289
|
+
* @param command - Resolved driver command name
|
|
290
|
+
*/
|
|
291
|
+
export function driverShouldDoJwpProxy(driver: Core<any>, req: Request, command: string): boolean {
|
|
292
|
+
const sessionId = getSessionId(driver, req);
|
|
293
|
+
// drivers need to explicitly say when the proxy is active
|
|
294
|
+
if (!driver.proxyActive(sessionId)) {
|
|
295
|
+
return false;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// we should never proxy deleteSession because we need to give the containing
|
|
299
|
+
// driver an opportunity to clean itself up
|
|
300
|
+
if (command === DELETE_SESSION_COMMAND) {
|
|
301
|
+
return false;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// validate avoidance schema, and say we shouldn't proxy if anything in the
|
|
305
|
+
// avoid list matches our req
|
|
306
|
+
if (driver.proxyRouteIsAvoided(sessionId as string, req.method, req.originalUrl, req.body)) {
|
|
307
|
+
return false;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
return true;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
function extractProtocol(driver: Core<any>, sessionId: string | null = null): keyof typeof PROTOCOLS {
|
|
314
|
+
const dstDriver = _.isFunction(driver.driverForSession) && sessionId
|
|
315
|
+
? driver.driverForSession(sessionId)
|
|
316
|
+
: driver;
|
|
317
|
+
if (dstDriver === driver) {
|
|
318
|
+
// Shortcircuit if the driver instance is not an umbrella driver
|
|
319
|
+
// or it is Fake driver instance, where `driver.driverForSession`
|
|
320
|
+
// always returns self instance
|
|
321
|
+
return driver.protocol ?? PROTOCOLS.W3C;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// Extract the protocol for the current session if the given driver is the umbrella one
|
|
325
|
+
return dstDriver?.protocol ?? PROTOCOLS.W3C;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function getLogger(driver: Core<any>, sessionId: string | null = null): AppiumLogger {
|
|
329
|
+
const dstDriver =
|
|
330
|
+
sessionId && _.isFunction(driver.driverForSession)
|
|
331
|
+
? driver.driverForSession(sessionId) ?? driver
|
|
332
|
+
: driver;
|
|
333
|
+
if (_.isFunction(dstDriver.log?.info)) {
|
|
334
|
+
return dstDriver.log;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
const logPrefix = generateDriverLogPrefix(dstDriver);
|
|
338
|
+
return logger.getLogger(logPrefix);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
function wrapParams<T>(paramSets, jsonObj: T): T | Record<string, T> {
|
|
342
|
+
/* There are commands like performTouch which take a single parameter (primitive type or array).
|
|
343
|
+
* Some drivers choose to pass this parameter as a value (eg. [action1, action2...]) while others to
|
|
344
|
+
* wrap it within an object(eg' {gesture: [action1, action2...]}), which makes it hard to validate.
|
|
345
|
+
* The wrap option in the spec enforce wrapping before validation, so that all params are wrapped at
|
|
346
|
+
* the time they are validated and later passed to the commands.
|
|
347
|
+
*/
|
|
348
|
+
return (_.isArray(jsonObj) || !_.isObject(jsonObj)) && paramSets.wrap
|
|
349
|
+
? {[paramSets.wrap]: jsonObj}
|
|
350
|
+
: jsonObj;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
function unwrapParams<T>(paramSets: PayloadParams, jsonObj: T): T | Record<string, T> {
|
|
354
|
+
/* There are commands like setNetworkConnection which send parameters wrapped inside a key such as
|
|
355
|
+
* "parameters". This function unwraps them (eg. {"parameters": {"type": 1}} becomes {"type": 1}).
|
|
356
|
+
*/
|
|
357
|
+
return _.isObject(jsonObj) && paramSets.unwrap && jsonObj[paramSets.unwrap]
|
|
358
|
+
? jsonObj[paramSets.unwrap]
|
|
359
|
+
: jsonObj;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
function hasMultipleRequiredParamSets(
|
|
364
|
+
required: ReadonlyArray<string> | MultidimensionalReadonlyArray<string, 2> | undefined
|
|
365
|
+
): required is MultidimensionalReadonlyArray<string, 2> {
|
|
366
|
+
//@ts-expect-error Needed to convince lodash typechecks
|
|
367
|
+
return Boolean(required && _.isArray(_.first(required)));
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
function pickKnownParams(args: Record<string, any>, unknownNames: string[]): Record<string, any> {
|
|
371
|
+
if (_.isEmpty(unknownNames)) {
|
|
372
|
+
return args;
|
|
373
|
+
}
|
|
374
|
+
log.info(`The following arguments are not known and will be ignored: ${unknownNames}`);
|
|
375
|
+
return _.pickBy(args, (v, k) => !unknownNames.includes(k));
|
|
376
|
+
}
|
|
377
|
+
|
|
328
378
|
function buildHandler(
|
|
329
379
|
app: Application,
|
|
330
380
|
method: string,
|
|
@@ -554,28 +604,6 @@ function buildHandler(
|
|
|
554
604
|
});
|
|
555
605
|
}
|
|
556
606
|
|
|
557
|
-
export function driverShouldDoJwpProxy(driver: Core<any>, req: Request, command: string): boolean {
|
|
558
|
-
const sessionId = getSessionId(driver, req);
|
|
559
|
-
// drivers need to explicitly say when the proxy is active
|
|
560
|
-
if (!driver.proxyActive(sessionId)) {
|
|
561
|
-
return false;
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
// we should never proxy deleteSession because we need to give the containing
|
|
565
|
-
// driver an opportunity to clean itself up
|
|
566
|
-
if (command === DELETE_SESSION_COMMAND) {
|
|
567
|
-
return false;
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
// validate avoidance schema, and say we shouldn't proxy if anything in the
|
|
571
|
-
// avoid list matches our req
|
|
572
|
-
if (driver.proxyRouteIsAvoided(sessionId as string, req.method, req.originalUrl, req.body)) {
|
|
573
|
-
return false;
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
return true;
|
|
577
|
-
}
|
|
578
|
-
|
|
579
607
|
async function doJwpProxy(driver: BaseDriver<any>, req: Request, res: Response): Promise<void> {
|
|
580
608
|
const sessionId = getSessionId(driver, req) as string;
|
|
581
609
|
getLogger(driver, sessionId).info(
|
package/lib/protocol/routes.ts
CHANGED
|
@@ -572,6 +572,12 @@ export const ALL_COMMANDS = _.flatMap(_.values(METHOD_MAP).map(_.values))
|
|
|
572
572
|
.filter((m) => Boolean(m.command))
|
|
573
573
|
.map((m) => m.command);
|
|
574
574
|
|
|
575
|
+
/**
|
|
576
|
+
* Resolve a WebDriver URL path and HTTP method to a driver command name from {@link METHOD_MAP}.
|
|
577
|
+
* @param endpoint - Request URL or path (may include base path)
|
|
578
|
+
* @param method - HTTP method (used when one path maps to multiple commands)
|
|
579
|
+
* @param basePath - Optional base path prefix to strip before matching
|
|
580
|
+
*/
|
|
575
581
|
export function routeToCommandName(
|
|
576
582
|
endpoint: string,
|
|
577
583
|
method?: HTTPMethod,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@appium/base-driver",
|
|
3
|
-
"version": "10.
|
|
3
|
+
"version": "10.4.0",
|
|
4
4
|
"description": "Base driver class for Appium drivers",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"automation",
|
|
@@ -42,27 +42,27 @@
|
|
|
42
42
|
"test:e2e": "mocha --exit --timeout 20s --slow 10s \"./test/e2e/**/*.spec.ts\"",
|
|
43
43
|
"test:smoke": "node ./index.js",
|
|
44
44
|
"test:unit": "mocha \"./test/unit/**/*.spec.ts\"",
|
|
45
|
-
"test:types": "tsd
|
|
45
|
+
"test:types": "tsd"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@appium/support": "
|
|
49
|
-
"@appium/types": "
|
|
48
|
+
"@appium/support": "7.1.1",
|
|
49
|
+
"@appium/types": "1.3.1",
|
|
50
50
|
"@colors/colors": "1.6.0",
|
|
51
51
|
"async-lock": "1.4.1",
|
|
52
52
|
"asyncbox": "6.1.0",
|
|
53
|
-
"axios": "1.
|
|
53
|
+
"axios": "1.15.0",
|
|
54
54
|
"bluebird": "3.7.2",
|
|
55
55
|
"body-parser": "2.2.2",
|
|
56
56
|
"express": "5.2.1",
|
|
57
57
|
"fastest-levenshtein": "1.0.16",
|
|
58
58
|
"http-status-codes": "2.3.0",
|
|
59
|
-
"lodash": "4.
|
|
60
|
-
"lru-cache": "11.
|
|
59
|
+
"lodash": "4.18.1",
|
|
60
|
+
"lru-cache": "11.3.5",
|
|
61
61
|
"method-override": "3.0.0",
|
|
62
62
|
"morgan": "1.10.1",
|
|
63
|
-
"path-to-regexp": "8.
|
|
63
|
+
"path-to-regexp": "8.4.2",
|
|
64
64
|
"serve-favicon": "2.5.1",
|
|
65
|
-
"type-fest": "5.
|
|
65
|
+
"type-fest": "5.6.0"
|
|
66
66
|
},
|
|
67
67
|
"optionalDependencies": {
|
|
68
68
|
"spdy": "4.0.2"
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
"publishConfig": {
|
|
75
75
|
"access": "public"
|
|
76
76
|
},
|
|
77
|
-
"gitHead": "
|
|
77
|
+
"gitHead": "17f84265d10944fec06ae7684ae8ad77d1224b50",
|
|
78
78
|
"tsd": {
|
|
79
79
|
"directory": "test/types"
|
|
80
80
|
}
|