@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.
Files changed (189) hide show
  1. package/build/lib/basedriver/capabilities.d.ts +1 -1
  2. package/build/lib/basedriver/capabilities.d.ts.map +1 -1
  3. package/build/lib/basedriver/capabilities.js +58 -50
  4. package/build/lib/basedriver/capabilities.js.map +1 -1
  5. package/build/lib/basedriver/commands/bidi.d.ts.map +1 -1
  6. package/build/lib/basedriver/commands/bidi.js +10 -14
  7. package/build/lib/basedriver/commands/bidi.js.map +1 -1
  8. package/build/lib/basedriver/commands/event.d.ts.map +1 -1
  9. package/build/lib/basedriver/commands/event.js +4 -7
  10. package/build/lib/basedriver/commands/event.js.map +1 -1
  11. package/build/lib/basedriver/commands/execute.js +3 -6
  12. package/build/lib/basedriver/commands/execute.js.map +1 -1
  13. package/build/lib/basedriver/commands/find.d.ts.map +1 -1
  14. package/build/lib/basedriver/commands/find.js +2 -1
  15. package/build/lib/basedriver/commands/find.js.map +1 -1
  16. package/build/lib/basedriver/commands/log.d.ts.map +1 -1
  17. package/build/lib/basedriver/commands/log.js +1 -5
  18. package/build/lib/basedriver/commands/log.js.map +1 -1
  19. package/build/lib/basedriver/commands/timeout.d.ts.map +1 -1
  20. package/build/lib/basedriver/commands/timeout.js +9 -13
  21. package/build/lib/basedriver/commands/timeout.js.map +1 -1
  22. package/build/lib/basedriver/core.d.ts.map +1 -1
  23. package/build/lib/basedriver/core.js +17 -14
  24. package/build/lib/basedriver/core.js.map +1 -1
  25. package/build/lib/basedriver/device-settings.d.ts.map +1 -1
  26. package/build/lib/basedriver/device-settings.js +3 -7
  27. package/build/lib/basedriver/device-settings.js.map +1 -1
  28. package/build/lib/basedriver/driver.d.ts.map +1 -1
  29. package/build/lib/basedriver/driver.js +34 -38
  30. package/build/lib/basedriver/driver.js.map +1 -1
  31. package/build/lib/basedriver/extension-core.d.ts +4 -1
  32. package/build/lib/basedriver/extension-core.d.ts.map +1 -1
  33. package/build/lib/basedriver/extension-core.js +37 -13
  34. package/build/lib/basedriver/extension-core.js.map +1 -1
  35. package/build/lib/basedriver/helpers.d.ts.map +1 -1
  36. package/build/lib/basedriver/helpers.js +47 -33
  37. package/build/lib/basedriver/helpers.js.map +1 -1
  38. package/build/lib/basedriver/ipc.d.ts +36 -0
  39. package/build/lib/basedriver/ipc.d.ts.map +1 -0
  40. package/build/lib/basedriver/ipc.js +157 -0
  41. package/build/lib/basedriver/ipc.js.map +1 -0
  42. package/build/lib/basedriver/validation.d.ts.map +1 -1
  43. package/build/lib/basedriver/validation.js +27 -29
  44. package/build/lib/basedriver/validation.js.map +1 -1
  45. package/build/lib/express/express-logging.d.ts +0 -1
  46. package/build/lib/express/express-logging.d.ts.map +1 -1
  47. package/build/lib/express/express-logging.js +11 -11
  48. package/build/lib/express/express-logging.js.map +1 -1
  49. package/build/lib/express/idempotency.js +3 -6
  50. package/build/lib/express/idempotency.js.map +1 -1
  51. package/build/lib/express/middleware.d.ts.map +1 -1
  52. package/build/lib/express/middleware.js +6 -10
  53. package/build/lib/express/middleware.js.map +1 -1
  54. package/build/lib/express/server.d.ts +1 -1
  55. package/build/lib/express/server.d.ts.map +1 -1
  56. package/build/lib/express/server.js +82 -73
  57. package/build/lib/express/server.js.map +1 -1
  58. package/build/lib/express/websocket.d.ts.map +1 -1
  59. package/build/lib/express/websocket.js +6 -9
  60. package/build/lib/express/websocket.js.map +1 -1
  61. package/build/lib/helpers/capabilities.d.ts.map +1 -1
  62. package/build/lib/helpers/capabilities.js +14 -17
  63. package/build/lib/helpers/capabilities.js.map +1 -1
  64. package/build/lib/helpers/extension-command-name.js +2 -5
  65. package/build/lib/helpers/extension-command-name.js.map +1 -1
  66. package/build/lib/helpers/levenshtein-match.d.ts.map +1 -1
  67. package/build/lib/helpers/levenshtein-match.js +6 -7
  68. package/build/lib/helpers/levenshtein-match.js.map +1 -1
  69. package/build/lib/index.d.ts +2 -1
  70. package/build/lib/index.d.ts.map +1 -1
  71. package/build/lib/index.js +6 -16
  72. package/build/lib/index.js.map +1 -1
  73. package/build/lib/jsonwp-proxy/protocol-converter.d.ts.map +1 -1
  74. package/build/lib/jsonwp-proxy/protocol-converter.js +21 -18
  75. package/build/lib/jsonwp-proxy/protocol-converter.js.map +1 -1
  76. package/build/lib/jsonwp-proxy/proxy-request.d.ts +2 -2
  77. package/build/lib/jsonwp-proxy/proxy-request.d.ts.map +1 -1
  78. package/build/lib/jsonwp-proxy/proxy-request.js +25 -21
  79. package/build/lib/jsonwp-proxy/proxy-request.js.map +1 -1
  80. package/build/lib/jsonwp-proxy/proxy.d.ts.map +1 -1
  81. package/build/lib/jsonwp-proxy/proxy.js +45 -36
  82. package/build/lib/jsonwp-proxy/proxy.js.map +1 -1
  83. package/build/lib/protocol/errors.d.ts.map +1 -1
  84. package/build/lib/protocol/errors.js +33 -37
  85. package/build/lib/protocol/errors.js.map +1 -1
  86. package/build/lib/protocol/helpers.d.ts.map +1 -1
  87. package/build/lib/protocol/helpers.js +9 -8
  88. package/build/lib/protocol/helpers.js.map +1 -1
  89. package/build/lib/protocol/protocol.d.ts +1 -1
  90. package/build/lib/protocol/protocol.d.ts.map +1 -1
  91. package/build/lib/protocol/protocol.js +73 -61
  92. package/build/lib/protocol/protocol.js.map +1 -1
  93. package/build/lib/protocol/routes.d.ts +1 -1
  94. package/build/lib/protocol/routes.d.ts.map +1 -1
  95. package/build/lib/protocol/routes.js +16 -17
  96. package/build/lib/protocol/routes.js.map +1 -1
  97. package/build/lib/protocol/validators.d.ts.map +1 -1
  98. package/build/lib/protocol/validators.js +1 -5
  99. package/build/lib/protocol/validators.js.map +1 -1
  100. package/build/lib/test-pages/crash.d.ts.map +1 -0
  101. package/build/lib/test-pages/crash.js.map +1 -0
  102. package/build/lib/test-pages/env.d.ts +5 -0
  103. package/build/lib/test-pages/env.d.ts.map +1 -0
  104. package/build/lib/test-pages/env.js +12 -0
  105. package/build/lib/test-pages/env.js.map +1 -0
  106. package/build/lib/{express/static.d.ts → test-pages/handlers.d.ts} +1 -2
  107. package/build/lib/test-pages/handlers.d.ts.map +1 -0
  108. package/build/lib/{express/static.js → test-pages/handlers.js} +9 -12
  109. package/build/lib/test-pages/handlers.js.map +1 -0
  110. package/build/lib/test-pages/index.d.ts +6 -0
  111. package/build/lib/test-pages/index.d.ts.map +1 -0
  112. package/build/lib/test-pages/index.js +35 -0
  113. package/build/lib/test-pages/index.js.map +1 -0
  114. package/build/lib/test-pages/static-dir.d.ts +8 -0
  115. package/build/lib/test-pages/static-dir.d.ts.map +1 -0
  116. package/build/lib/test-pages/static-dir.js +24 -0
  117. package/build/lib/test-pages/static-dir.js.map +1 -0
  118. package/build/lib/test-pages/template.d.ts +3 -0
  119. package/build/lib/test-pages/template.d.ts.map +1 -0
  120. package/build/lib/test-pages/template.js +19 -0
  121. package/build/lib/test-pages/template.js.map +1 -0
  122. package/build/lib/utils.d.ts +14 -0
  123. package/build/lib/utils.d.ts.map +1 -0
  124. package/build/lib/utils.js +55 -0
  125. package/build/lib/utils.js.map +1 -0
  126. package/lib/basedriver/capabilities.ts +126 -115
  127. package/lib/basedriver/commands/bidi.ts +11 -11
  128. package/lib/basedriver/commands/event.ts +17 -11
  129. package/lib/basedriver/commands/execute.ts +15 -12
  130. package/lib/basedriver/commands/find.ts +20 -12
  131. package/lib/basedriver/commands/log.ts +4 -3
  132. package/lib/basedriver/commands/timeout.ts +22 -14
  133. package/lib/basedriver/core.ts +26 -26
  134. package/lib/basedriver/device-settings.ts +7 -12
  135. package/lib/basedriver/driver.ts +62 -50
  136. package/lib/basedriver/extension-core.ts +60 -18
  137. package/lib/basedriver/helpers.ts +81 -52
  138. package/lib/basedriver/ipc.ts +198 -0
  139. package/lib/basedriver/validation.ts +37 -30
  140. package/lib/express/express-logging.ts +16 -20
  141. package/lib/express/idempotency.ts +9 -9
  142. package/lib/express/middleware.ts +14 -18
  143. package/lib/express/server.ts +118 -120
  144. package/lib/express/websocket.ts +11 -15
  145. package/lib/helpers/capabilities.ts +21 -16
  146. package/lib/helpers/extension-command-name.ts +3 -3
  147. package/lib/helpers/levenshtein-match.ts +20 -14
  148. package/lib/index.js +3 -12
  149. package/lib/jsonwp-proxy/protocol-converter.ts +58 -35
  150. package/lib/jsonwp-proxy/proxy-request.ts +26 -26
  151. package/lib/jsonwp-proxy/proxy.ts +74 -75
  152. package/lib/protocol/errors.ts +69 -88
  153. package/lib/protocol/helpers.ts +9 -5
  154. package/lib/protocol/protocol.ts +149 -107
  155. package/lib/protocol/routes.ts +17 -17
  156. package/lib/protocol/validators.ts +1 -3
  157. package/lib/test-pages/env.ts +9 -0
  158. package/lib/{express/static.ts → test-pages/handlers.ts} +10 -22
  159. package/lib/test-pages/index.ts +34 -0
  160. package/lib/test-pages/static-dir.ts +19 -0
  161. package/lib/test-pages/template.ts +17 -0
  162. package/lib/utils.ts +65 -0
  163. package/package.json +10 -13
  164. package/tsconfig.json +1 -0
  165. package/build/lib/express/crash.d.ts.map +0 -1
  166. package/build/lib/express/crash.js.map +0 -1
  167. package/build/lib/express/static.d.ts.map +0 -1
  168. package/build/lib/express/static.js.map +0 -1
  169. /package/build/lib/{express → test-pages}/crash.d.ts +0 -0
  170. /package/build/lib/{express → test-pages}/crash.js +0 -0
  171. /package/lib/{express → test-pages}/crash.ts +0 -0
  172. /package/{static → test-fixtures/static}/appium.png +0 -0
  173. /package/{static → test-fixtures/static}/favicon.ico +0 -0
  174. /package/{static → test-fixtures/static}/js/jquery.min.js +0 -0
  175. /package/{static → test-fixtures/static}/test/frameset.html +0 -0
  176. /package/{static → test-fixtures/static}/test/guinea-pig-app-banner.html +0 -0
  177. /package/{static → test-fixtures/static}/test/guinea-pig-scrollable.html +0 -0
  178. /package/{static → test-fixtures/static}/test/guinea-pig.html +0 -0
  179. /package/{static → test-fixtures/static}/test/guinea-pig2.html +0 -0
  180. /package/{static → test-fixtures/static}/test/guinea-pig3.html +0 -0
  181. /package/{static → test-fixtures/static}/test/guinea-pig4.html +0 -0
  182. /package/{static → test-fixtures/static}/test/guinea-pig5.html +0 -0
  183. /package/{static → test-fixtures/static}/test/iframes.html +0 -0
  184. /package/{static → test-fixtures/static}/test/shadow-dom.html +0 -0
  185. /package/{static → test-fixtures/static}/test/subframe1.html +0 -0
  186. /package/{static → test-fixtures/static}/test/subframe2.html +0 -0
  187. /package/{static → test-fixtures/static}/test/subframe3.html +0 -0
  188. /package/{static → test-fixtures/static}/test/touch.html +0 -0
  189. /package/{static → test-fixtures/static}/test/welcome.html +0 -0
@@ -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 {Core, AppiumLogger, PayloadParams, MethodMap, Driver, DriverMethodDef} from '@appium/types';
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 _.some(createSessionArgs, isW3cCaps) ? PROTOCOLS.W3C : PROTOCOLS.MJSONWP;
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
- `This indicates the route definition issue. The route should start with '/session/:sessionId' (named parameter) ` +
59
- `instead of '/session/*sessionId' (wildcard). ` +
60
- `Using the first element as session id: ${sessionId}. ` +
61
- `Please fix the route definition to prevent this error.`
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 !_.includes(NO_SESSION_ID_COMMANDS, command);
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[] = _.keys(args);
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 = _.cloneDeep(
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 = _.cloneDeep(paramSpec.optional as string[]);
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(_.isString(message) ? message : undefined);
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 (!_.includes(optionalParams, 'sessionId')) {
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 (!_.includes(optionalParams, 'id')) {
129
+ if (!optionalParams.includes('id')) {
125
130
  optionalParams.push('id');
126
131
  }
127
132
 
128
- if (_.isEmpty(requiredParams)) {
133
+ if (util.isEmpty(requiredParams)) {
129
134
  // if we don't have any required parameters, then just filter out unknown ones
130
- return pickKnownParams(args, _.difference(actualParamNames, optionalParams));
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 (!_.isArray(requiredParamsSet)) {
144
+ if (!Array.isArray(requiredParamsSet)) {
137
145
  throw new Error(
138
146
  `The required parameter set item ${JSON.stringify(requiredParamsSet)} ` +
139
- `in ${JSON.stringify(paramSpec)} is not an array. ` +
140
- `This is a bug in the method map definition.`
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 (_.isEmpty(_.difference(requiredParamsSet, actualParamNames))) {
151
+ if (requiredParamsSet.every((name) => actualParamNames.includes(name))) {
144
152
  return pickKnownParams(
145
153
  args,
146
- _.difference(actualParamNames, requiredParamsSet, optionalParams)
154
+ actualParamNames.filter(
155
+ (name) => !requiredParamsSet.includes(name) && !optionalParams.includes(name),
156
+ ),
147
157
  );
148
158
  }
149
- if (!_.isEmpty(requiredParamsSet) && _.isEmpty(matchedReqParamSet)) {
159
+ if (!util.isEmpty(requiredParamsSet) && util.isEmpty(matchedReqParamSet)) {
150
160
  matchedReqParamSet = requiredParamsSet;
151
161
  }
152
162
  }
153
- throw new BadParametersError({
154
- ...paramSpec,
155
- required: matchedReqParamSet,
156
- optional: optionalParams,
157
- }, actualParamNames);
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(requestParams: PayloadParams, jsonObj: any, payloadParams: PayloadParams): any[] {
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 = _.keys(requestParams).reverse();
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 = _.keys(jsonObj);
200
+ const keys = Object.keys(jsonObj);
184
201
  for (const params of payloadParams.required) {
185
- if (_.without(params, ...keys).length === 0) {
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 (_.isFunction(payloadParams.makeArgs)) {
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 = _.flatten(requiredParams).map((p) => jsonObj[p]);
222
+ args = (requiredParams ?? []).flat().map((p) => jsonObj[p]);
206
223
  if (payloadParams.optional) {
207
- args = args.concat(_.flatten(payloadParams.optional).map((p) => jsonObj[p]));
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 || !_.isArray(params) || params.length > 1) {
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 (!_.isPlainObject(args)) {
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 _.toPairs(allMethods)) {
269
- for (const [method, spec] of _.toPairs(methods)) {
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(driver: Core<any>, sessionId: string | null = null): keyof typeof PROTOCOLS {
314
- const dstDriver = _.isFunction(driver.driverForSession) && sessionId
315
- ? driver.driverForSession(sessionId)
316
- : driver;
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 && _.isFunction(driver.driverForSession)
331
- ? driver.driverForSession(sessionId) ?? driver
344
+ sessionId && typeof driver.driverForSession === 'function'
345
+ ? (driver.driverForSession(sessionId) ?? driver)
332
346
  : driver;
333
- if (_.isFunction(dstDriver.log?.info)) {
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 (_.isArray(jsonObj) || !_.isObject(jsonObj)) && paramSets.wrap
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
- return _.isObject(jsonObj) && paramSets.unwrap && jsonObj[paramSets.unwrap]
358
- ? jsonObj[paramSets.unwrap]
359
- : jsonObj;
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
- //@ts-expect-error Needed to convince lodash typechecks
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 (_.isEmpty(unknownNames)) {
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 _.pickBy(args, (v, k) => !unknownNames.includes(k));
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 (isSessCmd && !spec.neverProxy && spec.command && driverShouldDoJwpProxy(driver, req, spec.command)) {
435
+ if (
436
+ isSessCmd &&
437
+ !spec.neverProxy &&
438
+ spec.command &&
439
+ driverShouldDoJwpProxy(driver, req, spec.command)
440
+ ) {
421
441
  if (
422
- !('pluginsToHandleCmd' in driver) || !_.isFunction(driver.pluginsToHandleCmd) ||
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
- if (validators[spec.command]) {
471
- validators[spec.command](...args);
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, spec.command,
478
- logger.markSensitive(_.truncate(JSON.stringify(args), {length: MAX_LOG_BODY_LENGTH}))
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 (_.isPlainObject(driverRes) && _.has(driverRes, 'protocol')) {
496
- currentProtocol = driverRes.protocol || 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: ${_.truncate(JSON.stringify(driverRes), {
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 (_.isPlainObject(driverRes.value) && driverRes.value.error) {
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: ${_.truncate(JSON.stringify(driverRes), {
552
- length: MAX_LOG_BODY_LENGTH,
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 || (_.has(err, 'stack') && _.has(err, 'message'))) {
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
- let errMsg = err.stacktrace || err.stack;
573
- if (!_.includes(errMsg, err.message)) {
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 = `${err.message}${errMsg ? '\n' + 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 (_.isString(httpResBody)) {
591
- res.status(httpStatus)
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
- app[method.toLowerCase()](path, (req, res) => {
603
- B.resolve(asyncHandler(req, res)).done();
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
- } else {
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
  }
@@ -1,5 +1,5 @@
1
1
  import type {Driver, DriverMethodDef, HTTPMethod, MethodMap} from '@appium/types';
2
- import _ from 'lodash';
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 = _.flatMap(_.values(METHOD_MAP).map(_.values))
583
- .filter((m) => Boolean(m.command))
584
- .map((m) => m.command);
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(`^${_.escapeRegExp(resolvedBasePath)}`), '')
598
+ ? endpoint.replace(new RegExp(`^${util.escapeRegExp(resolvedBasePath)}`), '')
600
599
  : endpoint;
601
- normalizedEndpoint = `${_.startsWith(normalizedEndpoint, '/') ? '' : '/'}${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 = _.toUpper(method ?? '');
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 _.toPairs(METHOD_MAP)) {
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
- _.first(_.keys(spec).map((key) => spec[key]?.command));
629
- const commandName = normalizedMethod ? spec[normalizedMethod]?.command : commandForAnyMethod();
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;