@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/protocol/protocol.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import _ from 'lodash';
|
|
2
1
|
import {util, logger} from '@appium/support';
|
|
3
2
|
import {validators} from './validators';
|
|
4
3
|
import {
|
|
@@ -10,13 +9,20 @@ import {
|
|
|
10
9
|
BadParametersError,
|
|
11
10
|
} from './errors';
|
|
12
11
|
import {METHOD_MAP, NO_SESSION_ID_COMMANDS} from './routes';
|
|
13
|
-
import B from 'bluebird';
|
|
14
12
|
import {formatResponseValue, ensureW3cResponse} from './helpers';
|
|
15
13
|
import {MAX_LOG_BODY_LENGTH, PROTOCOLS, DEFAULT_BASE_PATH} from '../constants';
|
|
16
14
|
import {isW3cCaps} from '../helpers/capabilities';
|
|
17
15
|
import {log} from '../basedriver/logger';
|
|
16
|
+
import {omitKeys} from '../utils';
|
|
18
17
|
import {generateDriverLogPrefix} from '../basedriver/helpers';
|
|
19
|
-
import type {
|
|
18
|
+
import type {
|
|
19
|
+
Core,
|
|
20
|
+
AppiumLogger,
|
|
21
|
+
PayloadParams,
|
|
22
|
+
MethodMap,
|
|
23
|
+
Driver,
|
|
24
|
+
DriverMethodDef,
|
|
25
|
+
} from '@appium/types';
|
|
20
26
|
import type {BaseDriver} from '../basedriver/driver';
|
|
21
27
|
import type {Request, Response, Application} from 'express';
|
|
22
28
|
import type {MultidimensionalReadonlyArray} from 'type-fest';
|
|
@@ -35,7 +41,7 @@ export const deprecatedCommandsLogged: Set<string> = new Set();
|
|
|
35
41
|
* @param createSessionArgs - Arguments passed to the createSession command
|
|
36
42
|
*/
|
|
37
43
|
export function determineProtocol(createSessionArgs: any[]): keyof typeof PROTOCOLS {
|
|
38
|
-
return
|
|
44
|
+
return createSessionArgs.some(isW3cCaps) ? PROTOCOLS.W3C : PROTOCOLS.MJSONWP;
|
|
39
45
|
}
|
|
40
46
|
|
|
41
47
|
/**
|
|
@@ -55,10 +61,10 @@ export function getSessionId(driver: Core<any>, req: Request): string | undefine
|
|
|
55
61
|
const sessionId = req.params.sessionId[0];
|
|
56
62
|
getLogger(driver, sessionId).warn(
|
|
57
63
|
`Received malformed sessionId as array from the route: ${req.originalUrl}. ` +
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
64
|
+
`This indicates the route definition issue. The route should start with '/session/:sessionId' (named parameter) ` +
|
|
65
|
+
`instead of '/session/*sessionId' (wildcard). ` +
|
|
66
|
+
`Using the first element as session id: ${sessionId}. ` +
|
|
67
|
+
`Please fix the route definition to prevent this error.`,
|
|
62
68
|
);
|
|
63
69
|
// This is to not log the message multiple times.
|
|
64
70
|
req.params.sessionId = sessionId;
|
|
@@ -72,7 +78,7 @@ export function getSessionId(driver: Core<any>, req: Request): string | undefine
|
|
|
72
78
|
* @returns Whether the command requires a session id in the URL
|
|
73
79
|
*/
|
|
74
80
|
export function isSessionCommand(command: string): boolean {
|
|
75
|
-
return !
|
|
81
|
+
return !NO_SESSION_ID_COMMANDS.includes(command);
|
|
76
82
|
}
|
|
77
83
|
|
|
78
84
|
/**
|
|
@@ -84,25 +90,24 @@ export function isSessionCommand(command: string): boolean {
|
|
|
84
90
|
export function checkParams(
|
|
85
91
|
paramSpec: PayloadParams,
|
|
86
92
|
args: Record<string, any>,
|
|
87
|
-
protocol?: keyof typeof PROTOCOLS
|
|
93
|
+
protocol?: keyof typeof PROTOCOLS,
|
|
88
94
|
): Record<string, any> {
|
|
89
95
|
let requiredParams: string[][] = [];
|
|
90
96
|
let optionalParams: string[] = [];
|
|
91
|
-
const actualParamNames: string[] =
|
|
97
|
+
const actualParamNames: string[] = Object.keys(args);
|
|
92
98
|
|
|
93
99
|
if (paramSpec.required) {
|
|
94
100
|
// we might have an array of parameters,
|
|
95
101
|
// or an array of arrays of parameters, so standardize
|
|
96
|
-
requiredParams =
|
|
102
|
+
requiredParams = structuredClone(
|
|
97
103
|
(hasMultipleRequiredParamSets(paramSpec.required)
|
|
98
104
|
? paramSpec.required
|
|
99
|
-
: [paramSpec.required]
|
|
100
|
-
) as string[][]
|
|
105
|
+
: [paramSpec.required]) as string[][],
|
|
101
106
|
);
|
|
102
107
|
}
|
|
103
108
|
// optional parameters are just an array
|
|
104
109
|
if (paramSpec.optional) {
|
|
105
|
-
optionalParams =
|
|
110
|
+
optionalParams = structuredClone(paramSpec.optional as string[]);
|
|
106
111
|
}
|
|
107
112
|
|
|
108
113
|
// If a function was provided as the 'validate' key, it will here be called with
|
|
@@ -112,49 +117,57 @@ export function checkParams(
|
|
|
112
117
|
if (paramSpec.validate) {
|
|
113
118
|
const message = paramSpec.validate(args, protocol ?? PROTOCOLS.W3C);
|
|
114
119
|
if (message) {
|
|
115
|
-
throw new errors.InvalidArgumentError(
|
|
120
|
+
throw new errors.InvalidArgumentError(typeof message === 'string' ? message : undefined);
|
|
116
121
|
}
|
|
117
122
|
}
|
|
118
123
|
|
|
119
124
|
// some clients pass in the session id in the params
|
|
120
|
-
if (!
|
|
125
|
+
if (!optionalParams.includes('sessionId')) {
|
|
121
126
|
optionalParams.push('sessionId');
|
|
122
127
|
}
|
|
123
128
|
// some clients pass in an element id in the params
|
|
124
|
-
if (!
|
|
129
|
+
if (!optionalParams.includes('id')) {
|
|
125
130
|
optionalParams.push('id');
|
|
126
131
|
}
|
|
127
132
|
|
|
128
|
-
if (
|
|
133
|
+
if (util.isEmpty(requiredParams)) {
|
|
129
134
|
// if we don't have any required parameters, then just filter out unknown ones
|
|
130
|
-
return pickKnownParams(
|
|
135
|
+
return pickKnownParams(
|
|
136
|
+
args,
|
|
137
|
+
actualParamNames.filter((name) => !optionalParams.includes(name)),
|
|
138
|
+
);
|
|
131
139
|
}
|
|
132
140
|
|
|
133
141
|
// go through the required parameters and check against our arguments
|
|
134
142
|
let matchedReqParamSet: string[] = [];
|
|
135
143
|
for (const requiredParamsSet of requiredParams) {
|
|
136
|
-
if (!
|
|
144
|
+
if (!Array.isArray(requiredParamsSet)) {
|
|
137
145
|
throw new Error(
|
|
138
146
|
`The required parameter set item ${JSON.stringify(requiredParamsSet)} ` +
|
|
139
|
-
|
|
140
|
-
|
|
147
|
+
`in ${JSON.stringify(paramSpec)} is not an array. ` +
|
|
148
|
+
`This is a bug in the method map definition.`,
|
|
141
149
|
);
|
|
142
150
|
}
|
|
143
|
-
if (
|
|
151
|
+
if (requiredParamsSet.every((name) => actualParamNames.includes(name))) {
|
|
144
152
|
return pickKnownParams(
|
|
145
153
|
args,
|
|
146
|
-
|
|
154
|
+
actualParamNames.filter(
|
|
155
|
+
(name) => !requiredParamsSet.includes(name) && !optionalParams.includes(name),
|
|
156
|
+
),
|
|
147
157
|
);
|
|
148
158
|
}
|
|
149
|
-
if (!
|
|
159
|
+
if (!util.isEmpty(requiredParamsSet) && util.isEmpty(matchedReqParamSet)) {
|
|
150
160
|
matchedReqParamSet = requiredParamsSet;
|
|
151
161
|
}
|
|
152
162
|
}
|
|
153
|
-
throw new BadParametersError(
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
163
|
+
throw new BadParametersError(
|
|
164
|
+
{
|
|
165
|
+
...paramSpec,
|
|
166
|
+
required: matchedReqParamSet,
|
|
167
|
+
optional: optionalParams,
|
|
168
|
+
},
|
|
169
|
+
actualParamNames,
|
|
170
|
+
);
|
|
158
171
|
}
|
|
159
172
|
|
|
160
173
|
/**
|
|
@@ -163,12 +176,16 @@ export function checkParams(
|
|
|
163
176
|
* @param jsonObj - Parsed JSON request body
|
|
164
177
|
* @param payloadParams - Route payload definition (required/optional/makeArgs)
|
|
165
178
|
*/
|
|
166
|
-
export function makeArgs(
|
|
179
|
+
export function makeArgs(
|
|
180
|
+
requestParams: Record<string, string | string[] | undefined>,
|
|
181
|
+
jsonObj: any,
|
|
182
|
+
payloadParams: PayloadParams,
|
|
183
|
+
): any[] {
|
|
167
184
|
// We want to pass the "url" parameters to the commands in reverse order
|
|
168
185
|
// since the command will sometimes want to ignore, say, the sessionId.
|
|
169
186
|
// This has the effect of putting sessionId last, which means in JS we can
|
|
170
187
|
// omit it from the function signature if we're not going to use it.
|
|
171
|
-
const urlParams =
|
|
188
|
+
const urlParams = Object.keys(requestParams).reverse();
|
|
172
189
|
|
|
173
190
|
// In the simple case, the required parameters are a basic array in
|
|
174
191
|
// payloadParams.required, so start there. It's possible that there are
|
|
@@ -180,9 +197,9 @@ export function makeArgs(requestParams: PayloadParams, jsonObj: any, payloadPara
|
|
|
180
197
|
// array of arrays in payloadParams.required, so loop through each set and
|
|
181
198
|
// pick the one that matches which JSON params were actually sent. We've
|
|
182
199
|
// already been through validation so we're guaranteed to find a match.
|
|
183
|
-
const keys =
|
|
200
|
+
const keys = Object.keys(jsonObj);
|
|
184
201
|
for (const params of payloadParams.required) {
|
|
185
|
-
if (
|
|
202
|
+
if (params.filter((p) => !keys.includes(p)).length === 0) {
|
|
186
203
|
requiredParams = params;
|
|
187
204
|
break;
|
|
188
205
|
}
|
|
@@ -191,7 +208,7 @@ export function makeArgs(requestParams: PayloadParams, jsonObj: any, payloadPara
|
|
|
191
208
|
|
|
192
209
|
// Now we construct our list of arguments which will be passed to the command
|
|
193
210
|
let args;
|
|
194
|
-
if (
|
|
211
|
+
if (typeof payloadParams.makeArgs === 'function') {
|
|
195
212
|
// In the route spec, a particular route might define a 'makeArgs' function
|
|
196
213
|
// if it wants full control over how to turn JSON parameters into command
|
|
197
214
|
// arguments. So we pass it the JSON parameters and it returns an array
|
|
@@ -202,9 +219,9 @@ export function makeArgs(requestParams: PayloadParams, jsonObj: any, payloadPara
|
|
|
202
219
|
} else {
|
|
203
220
|
// Otherwise, collect all the required and optional params and flatten them
|
|
204
221
|
// into an argument array
|
|
205
|
-
args =
|
|
222
|
+
args = (requiredParams ?? []).flat().map((p) => jsonObj[p]);
|
|
206
223
|
if (payloadParams.optional) {
|
|
207
|
-
args = args.concat(
|
|
224
|
+
args = args.concat((payloadParams.optional ?? []).flat().map((p) => jsonObj[p]));
|
|
208
225
|
}
|
|
209
226
|
}
|
|
210
227
|
// Finally, get our url params (session id, element id, etc...) on the end of
|
|
@@ -222,17 +239,17 @@ export function validateExecuteMethodParams(params: any[], paramSpec?: PayloadPa
|
|
|
222
239
|
// the w3c protocol will give us an array of arguments to apply to a javascript function.
|
|
223
240
|
// that's not what we're doing. we're going to look for a JS object as the first arg, so we
|
|
224
241
|
// can perform validation on it. we'll ignore everything else.
|
|
225
|
-
if (!params || !
|
|
242
|
+
if (!params || !Array.isArray(params) || params.length > 1) {
|
|
226
243
|
throw new errors.InvalidArgumentError(
|
|
227
244
|
`Did not get correct format of arguments for execute method. Expected zero or one ` +
|
|
228
|
-
`arguments to execute script and instead received: ${JSON.stringify(params)}
|
|
245
|
+
`arguments to execute script and instead received: ${JSON.stringify(params)}`,
|
|
229
246
|
);
|
|
230
247
|
}
|
|
231
248
|
const args: Record<string, any> = params[0] ?? {};
|
|
232
|
-
if (!
|
|
249
|
+
if (!util.isPlainObject(args)) {
|
|
233
250
|
throw new errors.InvalidArgumentError(
|
|
234
251
|
`Did not receive an appropriate execute method parameters object. It needs to be ` +
|
|
235
|
-
`deserializable as a plain JS object
|
|
252
|
+
`deserializable as a plain JS object`,
|
|
236
253
|
);
|
|
237
254
|
}
|
|
238
255
|
const specToUse = {
|
|
@@ -265,18 +282,11 @@ export function routeConfiguringFunction(driver: Core<any>): RouteConfiguringFun
|
|
|
265
282
|
driver.basePath = basePath;
|
|
266
283
|
|
|
267
284
|
const allMethods: MethodMap<Driver> = {...METHOD_MAP, ...extraMethodMap};
|
|
268
|
-
for (const [path, methods] of
|
|
269
|
-
for (const [method, spec] of
|
|
285
|
+
for (const [path, methods] of Object.entries(allMethods)) {
|
|
286
|
+
for (const [method, spec] of Object.entries(methods)) {
|
|
270
287
|
const isSessCommand = spec.command ? isSessionCommand(spec.command) : false;
|
|
271
288
|
// set up the express route handler
|
|
272
|
-
buildHandler(
|
|
273
|
-
app,
|
|
274
|
-
method,
|
|
275
|
-
`${basePath}${path}`,
|
|
276
|
-
spec,
|
|
277
|
-
driver,
|
|
278
|
-
isSessCommand
|
|
279
|
-
);
|
|
289
|
+
buildHandler(app, method, `${basePath}${path}`, spec, driver, isSessCommand);
|
|
280
290
|
}
|
|
281
291
|
}
|
|
282
292
|
};
|
|
@@ -310,10 +320,14 @@ export function driverShouldDoJwpProxy(driver: Core<any>, req: Request, command:
|
|
|
310
320
|
return true;
|
|
311
321
|
}
|
|
312
322
|
|
|
313
|
-
function extractProtocol(
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
323
|
+
function extractProtocol(
|
|
324
|
+
driver: Core<any>,
|
|
325
|
+
sessionId: string | null = null,
|
|
326
|
+
): keyof typeof PROTOCOLS {
|
|
327
|
+
const dstDriver =
|
|
328
|
+
typeof driver.driverForSession === 'function' && sessionId
|
|
329
|
+
? driver.driverForSession(sessionId)
|
|
330
|
+
: driver;
|
|
317
331
|
if (dstDriver === driver) {
|
|
318
332
|
// Shortcircuit if the driver instance is not an umbrella driver
|
|
319
333
|
// or it is Fake driver instance, where `driver.driverForSession`
|
|
@@ -327,10 +341,10 @@ function extractProtocol(driver: Core<any>, sessionId: string | null = null): ke
|
|
|
327
341
|
|
|
328
342
|
function getLogger(driver: Core<any>, sessionId: string | null = null): AppiumLogger {
|
|
329
343
|
const dstDriver =
|
|
330
|
-
sessionId &&
|
|
331
|
-
? driver.driverForSession(sessionId) ?? driver
|
|
344
|
+
sessionId && typeof driver.driverForSession === 'function'
|
|
345
|
+
? (driver.driverForSession(sessionId) ?? driver)
|
|
332
346
|
: driver;
|
|
333
|
-
if (
|
|
347
|
+
if (typeof dstDriver.log?.info === 'function') {
|
|
334
348
|
return dstDriver.log;
|
|
335
349
|
}
|
|
336
350
|
|
|
@@ -338,14 +352,15 @@ function getLogger(driver: Core<any>, sessionId: string | null = null): AppiumLo
|
|
|
338
352
|
return logger.getLogger(logPrefix);
|
|
339
353
|
}
|
|
340
354
|
|
|
341
|
-
function wrapParams<T>(paramSets, jsonObj: T): T | Record<string, T> {
|
|
355
|
+
function wrapParams<T>(paramSets: PayloadParams, jsonObj: T): T | Record<string, T> {
|
|
342
356
|
/* There are commands like performTouch which take a single parameter (primitive type or array).
|
|
343
357
|
* Some drivers choose to pass this parameter as a value (eg. [action1, action2...]) while others to
|
|
344
358
|
* wrap it within an object(eg' {gesture: [action1, action2...]}), which makes it hard to validate.
|
|
345
359
|
* The wrap option in the spec enforce wrapping before validation, so that all params are wrapped at
|
|
346
360
|
* the time they are validated and later passed to the commands.
|
|
347
361
|
*/
|
|
348
|
-
return (
|
|
362
|
+
return (Array.isArray(jsonObj) || typeof jsonObj !== 'object' || jsonObj === null) &&
|
|
363
|
+
paramSets.wrap
|
|
349
364
|
? {[paramSets.wrap]: jsonObj}
|
|
350
365
|
: jsonObj;
|
|
351
366
|
}
|
|
@@ -354,25 +369,25 @@ function unwrapParams<T>(paramSets: PayloadParams, jsonObj: T): T | Record<strin
|
|
|
354
369
|
/* There are commands like setNetworkConnection which send parameters wrapped inside a key such as
|
|
355
370
|
* "parameters". This function unwraps them (eg. {"parameters": {"type": 1}} becomes {"type": 1}).
|
|
356
371
|
*/
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
372
|
+
const unwrapped =
|
|
373
|
+
typeof jsonObj === 'object' && jsonObj !== null && paramSets.unwrap
|
|
374
|
+
? (jsonObj as Record<string, T>)[paramSets.unwrap]
|
|
375
|
+
: undefined;
|
|
376
|
+
return unwrapped !== undefined ? unwrapped : jsonObj;
|
|
360
377
|
}
|
|
361
378
|
|
|
362
|
-
|
|
363
379
|
function hasMultipleRequiredParamSets(
|
|
364
|
-
required: ReadonlyArray<string> | MultidimensionalReadonlyArray<string, 2> | undefined
|
|
380
|
+
required: ReadonlyArray<string> | MultidimensionalReadonlyArray<string, 2> | undefined,
|
|
365
381
|
): required is MultidimensionalReadonlyArray<string, 2> {
|
|
366
|
-
|
|
367
|
-
return Boolean(required && _.isArray(_.first(required)));
|
|
382
|
+
return Boolean(required && Array.isArray(required?.[0]));
|
|
368
383
|
}
|
|
369
384
|
|
|
370
385
|
function pickKnownParams(args: Record<string, any>, unknownNames: string[]): Record<string, any> {
|
|
371
|
-
if (
|
|
386
|
+
if (util.isEmpty(unknownNames)) {
|
|
372
387
|
return args;
|
|
373
388
|
}
|
|
374
389
|
log.info(`The following arguments are not known and will be ignored: ${unknownNames}`);
|
|
375
|
-
return
|
|
390
|
+
return omitKeys(args, unknownNames);
|
|
376
391
|
}
|
|
377
392
|
|
|
378
393
|
function buildHandler(
|
|
@@ -381,7 +396,7 @@ function buildHandler(
|
|
|
381
396
|
path: string,
|
|
382
397
|
spec: DriverMethodDef<Driver>,
|
|
383
398
|
driver: Core<any>,
|
|
384
|
-
isSessCmd: boolean
|
|
399
|
+
isSessCmd: boolean,
|
|
385
400
|
): void {
|
|
386
401
|
const asyncHandler = async (req: Request, res: Response) => {
|
|
387
402
|
let jsonObj = req.body;
|
|
@@ -398,7 +413,7 @@ function buildHandler(
|
|
|
398
413
|
getLogger(driver, sessionId).warn(
|
|
399
414
|
`The ${method} ${path} endpoint has been deprecated and will be removed in a future ` +
|
|
400
415
|
`version of Appium or your driver/plugin. Please use a different endpoint or contact the ` +
|
|
401
|
-
`driver/plugin author to add explicit support for the endpoint before it is removed
|
|
416
|
+
`driver/plugin author to add explicit support for the endpoint before it is removed`,
|
|
402
417
|
);
|
|
403
418
|
}
|
|
404
419
|
|
|
@@ -417,9 +432,15 @@ function buildHandler(
|
|
|
417
432
|
// commands and generally would not want that command to be proxied instead of handled by the
|
|
418
433
|
// plugin)
|
|
419
434
|
let didPluginOverrideProxy = false;
|
|
420
|
-
if (
|
|
435
|
+
if (
|
|
436
|
+
isSessCmd &&
|
|
437
|
+
!spec.neverProxy &&
|
|
438
|
+
spec.command &&
|
|
439
|
+
driverShouldDoJwpProxy(driver, req, spec.command)
|
|
440
|
+
) {
|
|
421
441
|
if (
|
|
422
|
-
!('pluginsToHandleCmd' in driver) ||
|
|
442
|
+
!('pluginsToHandleCmd' in driver) ||
|
|
443
|
+
typeof driver.pluginsToHandleCmd !== 'function' ||
|
|
423
444
|
driver.pluginsToHandleCmd(spec.command, sessionId).length === 0
|
|
424
445
|
) {
|
|
425
446
|
await doJwpProxy(driver as BaseDriver<any>, req, res);
|
|
@@ -428,7 +449,7 @@ function buildHandler(
|
|
|
428
449
|
getLogger(driver, sessionId).debug(
|
|
429
450
|
`Would have proxied ` +
|
|
430
451
|
`command directly, but a plugin exists which might require its value, so will let ` +
|
|
431
|
-
`its value be collected internally and made part of plugin chain
|
|
452
|
+
`its value be collected internally and made part of plugin chain`,
|
|
432
453
|
);
|
|
433
454
|
didPluginOverrideProxy = true;
|
|
434
455
|
}
|
|
@@ -453,7 +474,7 @@ function buildHandler(
|
|
|
453
474
|
// try to determine protocol by session creation args, so we can throw a
|
|
454
475
|
// properly formatted error if arguments validation fails
|
|
455
476
|
currentProtocol = determineProtocol(
|
|
456
|
-
makeArgs(req.params, jsonObj, spec.payloadParams || {})
|
|
477
|
+
makeArgs(req.params, jsonObj, spec.payloadParams || {}),
|
|
457
478
|
);
|
|
458
479
|
}
|
|
459
480
|
|
|
@@ -467,15 +488,21 @@ function buildHandler(
|
|
|
467
488
|
const args = makeArgs(req.params, jsonObj, spec.payloadParams || {});
|
|
468
489
|
let driverRes: any;
|
|
469
490
|
// validate command args according to MJSONWP
|
|
470
|
-
|
|
471
|
-
validators[
|
|
491
|
+
const validator = (
|
|
492
|
+
validators as Record<string, ((...validatorArgs: any[]) => void) | undefined>
|
|
493
|
+
)[spec.command];
|
|
494
|
+
if (validator) {
|
|
495
|
+
validator(...args);
|
|
472
496
|
}
|
|
473
497
|
|
|
474
498
|
// run the driver command wrapped inside the argument validators
|
|
475
499
|
getLogger(driver, sessionId).debug(
|
|
476
500
|
`Calling %s.%s() with args: %s`,
|
|
477
|
-
driver.constructor.name,
|
|
478
|
-
|
|
501
|
+
driver.constructor.name,
|
|
502
|
+
spec.command,
|
|
503
|
+
logger.markSensitive(
|
|
504
|
+
util.truncateString(JSON.stringify(args), {length: MAX_LOG_BODY_LENGTH}),
|
|
505
|
+
),
|
|
479
506
|
);
|
|
480
507
|
|
|
481
508
|
if (didPluginOverrideProxy) {
|
|
@@ -492,8 +519,9 @@ function buildHandler(
|
|
|
492
519
|
|
|
493
520
|
// If `executeCommand` was overridden and the method returns an object
|
|
494
521
|
// with a protocol and value/error property, re-assign the protocol
|
|
495
|
-
if (
|
|
496
|
-
currentProtocol =
|
|
522
|
+
if (util.isPlainObject(driverRes) && Object.hasOwn(driverRes, 'protocol')) {
|
|
523
|
+
currentProtocol =
|
|
524
|
+
(driverRes as {protocol?: keyof typeof PROTOCOLS}).protocol || currentProtocol;
|
|
497
525
|
if (driverRes.error) {
|
|
498
526
|
throw driverRes.error;
|
|
499
527
|
}
|
|
@@ -504,7 +532,7 @@ function buildHandler(
|
|
|
504
532
|
if (spec.command === CREATE_SESSION_COMMAND) {
|
|
505
533
|
newSessionId = driverRes[0];
|
|
506
534
|
getLogger(driver, newSessionId).debug(
|
|
507
|
-
`Cached the protocol value '${currentProtocol}' for the new session ${newSessionId}
|
|
535
|
+
`Cached the protocol value '${currentProtocol}' for the new session ${newSessionId}`,
|
|
508
536
|
);
|
|
509
537
|
if (currentProtocol === PROTOCOLS.MJSONWP) {
|
|
510
538
|
driverRes = driverRes[1];
|
|
@@ -520,9 +548,9 @@ function buildHandler(
|
|
|
520
548
|
// delete should not return anything even if successful
|
|
521
549
|
if (spec.command === DELETE_SESSION_COMMAND) {
|
|
522
550
|
getLogger(driver, sessionId).debug(
|
|
523
|
-
`Received response: ${
|
|
551
|
+
`Received response: ${util.truncateString(JSON.stringify(driverRes), {
|
|
524
552
|
length: MAX_LOG_BODY_LENGTH,
|
|
525
|
-
})}
|
|
553
|
+
})}`,
|
|
526
554
|
);
|
|
527
555
|
getLogger(driver, sessionId).debug('But deleting session, so not returning');
|
|
528
556
|
driverRes = null;
|
|
@@ -536,11 +564,11 @@ function buildHandler(
|
|
|
536
564
|
parseInt(driverRes.status, 10) !== 0
|
|
537
565
|
) {
|
|
538
566
|
throw errorFromMJSONWPStatusCode(driverRes.status, driverRes.value);
|
|
539
|
-
} else if (
|
|
567
|
+
} else if (util.isPlainObject(driverRes.value) && driverRes.value.error) {
|
|
540
568
|
throw errorFromW3CJsonCode(
|
|
541
569
|
driverRes.value.error,
|
|
542
570
|
driverRes.value.message,
|
|
543
|
-
driverRes.value.stacktrace
|
|
571
|
+
driverRes.value.stacktrace,
|
|
544
572
|
);
|
|
545
573
|
}
|
|
546
574
|
}
|
|
@@ -548,38 +576,48 @@ function buildHandler(
|
|
|
548
576
|
httpResBody.value = driverRes;
|
|
549
577
|
getLogger(driver, sessionId || newSessionId).debug(
|
|
550
578
|
`Responding ` +
|
|
551
|
-
`to client with driver.${spec.command}() result: ${
|
|
552
|
-
|
|
553
|
-
|
|
579
|
+
`to client with driver.${spec.command}() result: ${util.truncateString(
|
|
580
|
+
JSON.stringify(driverRes),
|
|
581
|
+
{
|
|
582
|
+
length: MAX_LOG_BODY_LENGTH,
|
|
583
|
+
},
|
|
584
|
+
)}`,
|
|
554
585
|
);
|
|
555
586
|
} catch (err) {
|
|
556
587
|
// if anything goes wrong, figure out what our response should be
|
|
557
588
|
// based on the type of error that we encountered
|
|
558
|
-
let actualErr;
|
|
559
|
-
if (err instanceof Error
|
|
589
|
+
let actualErr: Error;
|
|
590
|
+
if (err instanceof Error) {
|
|
560
591
|
actualErr = err;
|
|
592
|
+
} else if (
|
|
593
|
+
typeof err === 'object' &&
|
|
594
|
+
err !== null &&
|
|
595
|
+
Object.hasOwn(err, 'stack') &&
|
|
596
|
+
Object.hasOwn(err, 'message')
|
|
597
|
+
) {
|
|
598
|
+
actualErr = err as Error;
|
|
561
599
|
} else {
|
|
562
600
|
getLogger(driver, sessionId || newSessionId).warn(
|
|
563
601
|
'The thrown error object does not seem to be a valid instance of the Error class. This ' +
|
|
564
|
-
'might be a genuine bug of a driver or a plugin.'
|
|
602
|
+
'might be a genuine bug of a driver or a plugin.',
|
|
565
603
|
);
|
|
566
604
|
actualErr = new Error(`${err ?? 'unknown'}`);
|
|
567
605
|
}
|
|
568
606
|
|
|
569
|
-
currentProtocol =
|
|
570
|
-
currentProtocol || extractProtocol(driver, sessionId || newSessionId);
|
|
607
|
+
currentProtocol = currentProtocol || extractProtocol(driver, sessionId || newSessionId);
|
|
571
608
|
|
|
572
|
-
|
|
573
|
-
|
|
609
|
+
const stacktrace = (err as {stacktrace?: string}).stacktrace;
|
|
610
|
+
let errMsg = stacktrace || actualErr.stack || '';
|
|
611
|
+
if (!errMsg.includes(actualErr.message)) {
|
|
574
612
|
// if the message has more information, add it. but often the message
|
|
575
613
|
// is the first part of the stack trace
|
|
576
|
-
errMsg = `${
|
|
614
|
+
errMsg = `${actualErr.message}${errMsg ? '\n' + errMsg : ''}`;
|
|
577
615
|
}
|
|
578
616
|
if (isErrorType(err, errors.ProxyRequestError)) {
|
|
579
617
|
actualErr = err.getActualError();
|
|
580
618
|
} else {
|
|
581
619
|
getLogger(driver, sessionId || newSessionId).debug(
|
|
582
|
-
`Encountered internal error running command: ${errMsg}
|
|
620
|
+
`Encountered internal error running command: ${errMsg}`,
|
|
583
621
|
);
|
|
584
622
|
}
|
|
585
623
|
|
|
@@ -587,8 +625,9 @@ function buildHandler(
|
|
|
587
625
|
}
|
|
588
626
|
|
|
589
627
|
// decode the response, which is either a string or json
|
|
590
|
-
if (
|
|
591
|
-
res
|
|
628
|
+
if (typeof httpResBody === 'string') {
|
|
629
|
+
res
|
|
630
|
+
.status(httpStatus)
|
|
592
631
|
.setHeader('content-type', 'application/json; charset=utf-8')
|
|
593
632
|
.send(httpResBody);
|
|
594
633
|
} else {
|
|
@@ -599,16 +638,17 @@ function buildHandler(
|
|
|
599
638
|
}
|
|
600
639
|
};
|
|
601
640
|
// add the method to the app
|
|
602
|
-
|
|
603
|
-
|
|
641
|
+
const registerRoute = (
|
|
642
|
+
app as Application & Record<string, (routePath: string, ...handlers: any[]) => void>
|
|
643
|
+
)[method.toLowerCase()].bind(app);
|
|
644
|
+
registerRoute(path, (req: Request, res: Response) => {
|
|
645
|
+
void asyncHandler(req, res);
|
|
604
646
|
});
|
|
605
647
|
}
|
|
606
648
|
|
|
607
649
|
async function doJwpProxy(driver: BaseDriver<any>, req: Request, res: Response): Promise<void> {
|
|
608
650
|
const sessionId = getSessionId(driver, req) as string;
|
|
609
|
-
getLogger(driver, sessionId).info(
|
|
610
|
-
'Driver proxy active, passing request on via HTTP proxy'
|
|
611
|
-
);
|
|
651
|
+
getLogger(driver, sessionId).info('Driver proxy active, passing request on via HTTP proxy');
|
|
612
652
|
|
|
613
653
|
// check that the inner driver has a proxy function
|
|
614
654
|
if (!driver.canProxy(sessionId)) {
|
|
@@ -619,8 +659,10 @@ async function doJwpProxy(driver: BaseDriver<any>, req: Request, res: Response):
|
|
|
619
659
|
} catch (err) {
|
|
620
660
|
if (isErrorType(err, errors.ProxyRequestError)) {
|
|
621
661
|
throw err;
|
|
622
|
-
}
|
|
662
|
+
}
|
|
663
|
+
if (err instanceof Error) {
|
|
623
664
|
throw new Error(`Could not proxy. Proxy error: ${err.message}`, {cause: err});
|
|
624
665
|
}
|
|
666
|
+
throw new Error(`Could not proxy. Proxy error: ${String(err)}`, {cause: err});
|
|
625
667
|
}
|
|
626
668
|
}
|
package/lib/protocol/routes.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type {Driver, DriverMethodDef, HTTPMethod, MethodMap} from '@appium/types';
|
|
2
|
-
import
|
|
2
|
+
import {util} from '@appium/support';
|
|
3
3
|
import {DEFAULT_BASE_PATH} from '../constants';
|
|
4
4
|
import {match} from 'path-to-regexp';
|
|
5
5
|
import {LRUCache} from 'lru-cache';
|
|
@@ -14,7 +14,6 @@ const COMMAND_NAMES_CACHE = new LRUCache<string, string>({
|
|
|
14
14
|
* `optional`.
|
|
15
15
|
*/
|
|
16
16
|
export const METHOD_MAP = {
|
|
17
|
-
|
|
18
17
|
// #region W3C WebDriver
|
|
19
18
|
// https://www.w3.org/TR/webdriver2/
|
|
20
19
|
'/session': {
|
|
@@ -244,8 +243,8 @@ export const METHOD_MAP = {
|
|
|
244
243
|
'shrinkToFit',
|
|
245
244
|
'pageRanges',
|
|
246
245
|
],
|
|
247
|
-
}
|
|
248
|
-
}
|
|
246
|
+
},
|
|
247
|
+
},
|
|
249
248
|
},
|
|
250
249
|
// #endregion
|
|
251
250
|
|
|
@@ -274,7 +273,7 @@ export const METHOD_MAP = {
|
|
|
274
273
|
GET: {command: 'getOrientation'},
|
|
275
274
|
POST: {
|
|
276
275
|
command: 'setOrientation',
|
|
277
|
-
payloadParams: {required: ['orientation']}
|
|
276
|
+
payloadParams: {required: ['orientation']},
|
|
278
277
|
},
|
|
279
278
|
},
|
|
280
279
|
'/session/:sessionId/location': {
|
|
@@ -318,7 +317,7 @@ export const METHOD_MAP = {
|
|
|
318
317
|
GET: {command: 'getAppiumSessions'},
|
|
319
318
|
},
|
|
320
319
|
'/session/:sessionId/appium/capabilities': {
|
|
321
|
-
GET: {command: 'getAppiumSessionCapabilities'}
|
|
320
|
+
GET: {command: 'getAppiumSessionCapabilities'},
|
|
322
321
|
},
|
|
323
322
|
'/session/:sessionId/appium/settings': {
|
|
324
323
|
POST: {command: 'updateSettings', payloadParams: {required: ['settings']}},
|
|
@@ -579,9 +578,9 @@ export const METHOD_MAP = {
|
|
|
579
578
|
} as const satisfies MethodMap<Driver>;
|
|
580
579
|
|
|
581
580
|
// driver command names
|
|
582
|
-
export const ALL_COMMANDS =
|
|
583
|
-
.
|
|
584
|
-
.
|
|
581
|
+
export const ALL_COMMANDS = Object.values(METHOD_MAP)
|
|
582
|
+
.flatMap((methods) => Object.values(methods) as Array<{command?: string}>)
|
|
583
|
+
.flatMap((m) => (m.command ? [m.command] : []));
|
|
585
584
|
|
|
586
585
|
/**
|
|
587
586
|
* Resolve a WebDriver URL path and HTTP method to a driver command name from {@link METHOD_MAP}.
|
|
@@ -592,13 +591,13 @@ export const ALL_COMMANDS = _.flatMap(_.values(METHOD_MAP).map(_.values))
|
|
|
592
591
|
export function routeToCommandName(
|
|
593
592
|
endpoint: string,
|
|
594
593
|
method?: HTTPMethod,
|
|
595
|
-
basePath?: string
|
|
594
|
+
basePath?: string,
|
|
596
595
|
): string | undefined {
|
|
597
596
|
const resolvedBasePath = basePath ?? DEFAULT_BASE_PATH;
|
|
598
597
|
let normalizedEndpoint = resolvedBasePath
|
|
599
|
-
? endpoint.replace(new RegExp(`^${
|
|
598
|
+
? endpoint.replace(new RegExp(`^${util.escapeRegExp(resolvedBasePath)}`), '')
|
|
600
599
|
: endpoint;
|
|
601
|
-
normalizedEndpoint = `${
|
|
600
|
+
normalizedEndpoint = `${normalizedEndpoint.startsWith('/') ? '' : '/'}${normalizedEndpoint}`;
|
|
602
601
|
let normalizedPathname: string;
|
|
603
602
|
try {
|
|
604
603
|
// we could use any prefix there as we anyway need to only extract the pathname
|
|
@@ -608,7 +607,7 @@ export function routeToCommandName(
|
|
|
608
607
|
throw new Error(`'${endpoint}' cannot be translated to a command name: ${msg}`, {cause: err});
|
|
609
608
|
}
|
|
610
609
|
|
|
611
|
-
const normalizedMethod =
|
|
610
|
+
const normalizedMethod = (method ?? '').toUpperCase();
|
|
612
611
|
const cacheKey = toCommandNameCacheKey(normalizedPathname, normalizedMethod);
|
|
613
612
|
const cached = COMMAND_NAMES_CACHE.get(cacheKey);
|
|
614
613
|
if (cached !== undefined) {
|
|
@@ -620,13 +619,14 @@ export function routeToCommandName(
|
|
|
620
619
|
possiblePathnames.push(`/session/any-session-id${normalizedPathname}`);
|
|
621
620
|
}
|
|
622
621
|
possiblePathnames.push(normalizedPathname);
|
|
623
|
-
for (const [routePath, routeSpec] of
|
|
622
|
+
for (const [routePath, routeSpec] of Object.entries(METHOD_MAP)) {
|
|
624
623
|
const routeMatcher = match(routePath);
|
|
625
624
|
if (possiblePathnames.some((pp) => routeMatcher(pp))) {
|
|
626
625
|
const spec = routeSpec as Record<string, DriverMethodDef<Driver>>;
|
|
627
|
-
const commandForAnyMethod = () =>
|
|
628
|
-
|
|
629
|
-
|
|
626
|
+
const commandForAnyMethod = () => Object.keys(spec).map((key) => spec[key]?.command)[0];
|
|
627
|
+
const commandName = normalizedMethod
|
|
628
|
+
? spec[normalizedMethod]?.command
|
|
629
|
+
: commandForAnyMethod();
|
|
630
630
|
if (commandName) {
|
|
631
631
|
COMMAND_NAMES_CACHE.set(cacheKey, commandName);
|
|
632
632
|
return commandName;
|