@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,10 +1,8 @@
1
- import _ from 'lodash';
2
- import path from 'node:path';
3
1
  import express from 'express';
4
2
  import type {Express, RequestHandler} from 'express';
5
3
  import http from 'node:http';
6
4
  import type {Server as HttpServer} from 'node:http';
7
- import favicon from 'serve-favicon';
5
+ import {createRequire} from 'node:module';
8
6
  import bodyParser from 'body-parser';
9
7
  import methodOverride from 'method-override';
10
8
  import {log} from './logger';
@@ -20,21 +18,15 @@ import {
20
18
  catch404Handler,
21
19
  handleLogContext,
22
20
  } from './middleware';
23
- import {
24
- guineaPig,
25
- guineaPigScrollable,
26
- guineaPigAppBanner,
27
- welcome,
28
- STATIC_DIR,
29
- } from './static';
30
- import {produceError, produceCrash} from './crash';
21
+ // Import env helper directly — not from the test-pages barrel — so Express handlers and
22
+ // fixture code stay unloaded unless APPIUM_ENABLE_LEGACY_TEST_PAGES is set.
23
+ import {isLegacyTestPagesEnabled} from '../test-pages/env';
31
24
  import {
32
25
  addWebSocketHandler,
33
26
  removeWebSocketHandler,
34
27
  removeAllWebSocketHandlers,
35
28
  getWebSocketHandlers,
36
29
  } from './websocket';
37
- import B from 'bluebird';
38
30
  import {DEFAULT_BASE_PATH} from '../constants';
39
31
  import {fs, timing} from '@appium/support';
40
32
  import type {
@@ -55,10 +47,7 @@ export interface RouteConfiguringFunctionOpts {
55
47
  }
56
48
 
57
49
  /** A function which configures routes */
58
- export type RouteConfiguringFunction = (
59
- app: Express,
60
- opts?: RouteConfiguringFunctionOpts
61
- ) => void;
50
+ export type RouteConfiguringFunction = (app: Express, opts?: RouteConfiguringFunctionOpts) => void;
62
51
 
63
52
  /** Options for {@linkcode server} */
64
53
  export interface ServerOpts {
@@ -85,6 +74,12 @@ export interface ConfigureServerOpts {
85
74
  useLegacyUpgradeHandler?: boolean;
86
75
  }
87
76
 
77
+ /** @internal */
78
+ export interface ConfigureServerInternalOpts extends ConfigureServerOpts {
79
+ /** @deprecated Appium 4 */
80
+ registerTestPages?: (app: Express, opts: {basePath: string}) => void;
81
+ }
82
+
88
83
  /** Options for {@linkcode configureHttp} */
89
84
  export interface ConfigureHttpOpts {
90
85
  httpServer: HttpServer;
@@ -123,51 +118,57 @@ export async function server(opts: ServerOpts): Promise<AppiumServer> {
123
118
  const app = express();
124
119
  const httpServer = await createServer(app, cliArgs);
125
120
 
126
- return await new B<AppiumServer>(async (resolve, reject) => {
127
- // we put an async function as the promise constructor because we want some things to happen in
128
- // serial (application of plugin updates, for example). But we still need to use a promise here
129
- // because some elements of server start failure only happen in httpServer listeners. So the
130
- // way we resolve it is to use an async function here but to wrap all the inner logic in
131
- // try/catch so any errors can be passed to reject.
132
- try {
133
- const appiumServer = configureHttp({
134
- httpServer,
135
- reject,
136
- keepAliveTimeout,
137
- gracefulShutdownTimeout: cliArgs.shutdownTimeout,
138
- });
139
- const useLegacyUpgradeHandler = !hasShouldUpgradeCallback(httpServer);
140
- configureServer({
141
- app,
142
- addRoutes: routeConfiguringFunction,
143
- allowCors,
144
- basePath,
145
- extraMethodMap,
146
- webSocketsMapping: appiumServer.webSocketsMapping,
147
- useLegacyUpgradeHandler,
148
- });
149
- // allow extensions to update the app and http server objects
150
- for (const updater of serverUpdaters) {
151
- await updater(app, appiumServer, cliArgs);
152
- }
153
-
154
- // once all configurations and updaters have been applied, make sure to set up a catchall
155
- // handler so that anything unknown 404s. But do this after everything else since we don't
156
- // want to block extensions' ability to add routes if they want.
157
- app.all('/*all', catch404Handler);
158
-
159
- await startServer({
160
- httpServer,
161
- hostname,
162
- port,
163
- keepAliveTimeout,
164
- requestTimeout,
165
- });
121
+ return await new Promise<AppiumServer>((resolve, reject) => {
122
+ // we use a promise here because some elements of server start failure only happen in
123
+ // httpServer listeners. The async IIFE runs setup serially (e.g. plugin updates) while
124
+ // configureHttp can still reject via the listener registered below.
125
+ void (async () => {
126
+ try {
127
+ const appiumServer = configureHttp({
128
+ httpServer,
129
+ reject,
130
+ keepAliveTimeout,
131
+ gracefulShutdownTimeout: cliArgs.shutdownTimeout,
132
+ });
133
+ const useLegacyUpgradeHandler = !hasShouldUpgradeCallback(httpServer);
134
+ let registerTestPages: ConfigureServerInternalOpts['registerTestPages'];
135
+ if (isLegacyTestPagesEnabled()) {
136
+ const require = createRequire(__filename);
137
+ registerTestPages = require('../test-pages').registerTestPages;
138
+ }
139
+ configureServer({
140
+ app,
141
+ addRoutes: routeConfiguringFunction,
142
+ allowCors,
143
+ basePath,
144
+ extraMethodMap,
145
+ webSocketsMapping: appiumServer.webSocketsMapping,
146
+ useLegacyUpgradeHandler,
147
+ registerTestPages,
148
+ } as ConfigureServerInternalOpts);
149
+ // allow extensions to update the app and http server objects
150
+ for (const updater of serverUpdaters) {
151
+ await updater(app, appiumServer, cliArgs);
152
+ }
166
153
 
167
- resolve(appiumServer);
168
- } catch (err) {
169
- reject(err);
170
- }
154
+ // once all configurations and updaters have been applied, make sure to set up a catchall
155
+ // handler so that anything unknown 404s. But do this after everything else since we don't
156
+ // want to block extensions' ability to add routes if they want.
157
+ app.all('/*all', catch404Handler);
158
+
159
+ await startServer({
160
+ httpServer,
161
+ hostname,
162
+ port,
163
+ keepAliveTimeout,
164
+ requestTimeout,
165
+ });
166
+
167
+ resolve(appiumServer);
168
+ } catch (err) {
169
+ reject(err);
170
+ }
171
+ })();
171
172
  });
172
173
  }
173
174
 
@@ -176,27 +177,25 @@ export async function server(opts: ServerOpts): Promise<AppiumServer> {
176
177
  *
177
178
  * @param opts - Configuration options
178
179
  */
179
- export function configureServer({
180
- app,
181
- addRoutes,
182
- allowCors = true,
183
- basePath = DEFAULT_BASE_PATH,
184
- extraMethodMap = {},
185
- webSocketsMapping = {},
186
- useLegacyUpgradeHandler = true,
187
- }: ConfigureServerOpts): void {
188
- basePath = normalizeBasePath(basePath);
180
+ export function configureServer(opts: ConfigureServerOpts): void {
181
+ const {
182
+ app,
183
+ addRoutes,
184
+ allowCors = true,
185
+ basePath: rawBasePath = DEFAULT_BASE_PATH,
186
+ extraMethodMap = {},
187
+ webSocketsMapping = {},
188
+ useLegacyUpgradeHandler = true,
189
+ } = opts;
190
+ const {registerTestPages} = opts as ConfigureServerInternalOpts;
191
+ const basePath = normalizeBasePath(rawBasePath);
189
192
 
190
193
  app.use(endLogFormatter);
191
194
  app.use(handleLogContext);
192
195
 
193
- // set up static assets
194
- app.use(favicon(path.resolve(STATIC_DIR, 'favicon.ico')));
195
- app.use(express.static(STATIC_DIR));
196
-
197
- // crash routes, for testing
198
- app.use(`${basePath}/produce_error`, produceError);
199
- app.use(`${basePath}/crash`, produceCrash);
196
+ if (registerTestPages) {
197
+ registerTestPages(app, {basePath});
198
+ }
200
199
 
201
200
  // Only use legacy Express middleware for WebSocket upgrades if shouldUpgradeCallback is not available.
202
201
  // When shouldUpgradeCallback is available, upgrades are handled directly on the HTTP server
@@ -222,12 +221,6 @@ export function configureServer({
222
221
  app.use(startLogFormatter);
223
222
 
224
223
  addRoutes(app, {basePath, extraMethodMap});
225
-
226
- // dynamic routes for testing, etc.
227
- app.all('/welcome', welcome);
228
- app.all('/test/guinea-pig', guineaPig);
229
- app.all('/test/guinea-pig-scrollable', guineaPigScrollable);
230
- app.all('/test/guinea-pig-app-banner', guineaPigAppBanner);
231
224
  }
232
225
 
233
226
  /**
@@ -237,7 +230,7 @@ export function configureServer({
237
230
  * @returns Normalized base path
238
231
  */
239
232
  export function normalizeBasePath(basePath: string): string {
240
- if (!_.isString(basePath)) {
233
+ if (typeof basePath !== 'string') {
241
234
  throw new Error(`Invalid path prefix ${basePath}`);
242
235
  }
243
236
 
@@ -253,42 +246,35 @@ export function normalizeBasePath(basePath: string): string {
253
246
  return basePath;
254
247
  }
255
248
 
256
- async function createServer(
257
- app: Express,
258
- cliArgs?: Partial<ServerArgs>
259
- ): Promise<HttpServer> {
249
+ async function createServer(app: Express, cliArgs?: Partial<ServerArgs>): Promise<HttpServer> {
260
250
  const {sslCertificatePath, sslKeyPath} = cliArgs ?? {};
261
251
  if (!sslCertificatePath && !sslKeyPath) {
262
252
  return http.createServer(app);
263
253
  }
264
254
  if (!sslCertificatePath || !sslKeyPath) {
265
- throw new Error(
266
- `Both certificate path and key path must be provided to enable TLS`
267
- );
255
+ throw new Error(`Both certificate path and key path must be provided to enable TLS`);
268
256
  }
269
257
 
270
258
  const certKey = [sslCertificatePath, sslKeyPath];
271
- const zipped = _.zip(
272
- await B.all(certKey.map((p) => fs.exists(p))),
273
- ['certificate', 'key'],
274
- certKey
275
- ) as [boolean, string, string][];
276
- for (const [exists, desc, p] of zipped) {
259
+ const [certExists, keyExists] = await Promise.all(certKey.map((p) => fs.exists(p)));
260
+ for (const [exists, desc, p] of [
261
+ [certExists, 'certificate', sslCertificatePath],
262
+ [keyExists, 'key', sslKeyPath],
263
+ ]) {
277
264
  if (!exists) {
278
- throw new Error(
279
- `The provided SSL ${desc} at '${p}' does not exist or is not accessible`
280
- );
265
+ throw new Error(`The provided SSL ${desc} at '${p}' does not exist or is not accessible`);
281
266
  }
282
267
  }
283
- const [cert, key] = await B.all(
284
- certKey.map((p) => fs.readFile(p, 'utf8'))
285
- ) as [string, string];
268
+ const [cert, key] = (await Promise.all(certKey.map((p) => fs.readFile(p, 'utf8')))) as [
269
+ string,
270
+ string,
271
+ ];
286
272
  log.debug('Enabling TLS/SPDY on the server using the provided certificate');
287
273
 
288
274
  const spdy = require('spdy') as {
289
275
  createServer: (
290
276
  options: {cert: string; key: string; spdy: {plain: boolean; ssl: boolean}},
291
- requestListener: RequestHandler
277
+ requestListener: RequestHandler,
292
278
  ) => HttpServer;
293
279
  };
294
280
  return spdy.createServer(
@@ -300,7 +286,7 @@ async function createServer(
300
286
  ssl: true,
301
287
  },
302
288
  },
303
- app
289
+ app,
304
290
  );
305
291
  }
306
292
 
@@ -332,8 +318,10 @@ function configureHttp({
332
318
  // See: https://github.com/nodejs/node/pull/59824
333
319
  if (hasShouldUpgradeCallback(httpServer)) {
334
320
  // shouldUpgradeCallback only returns a boolean to indicate if the upgrade should proceed
335
- (appiumServer as unknown as {shouldUpgradeCallback?: (req: http.IncomingMessage) => boolean}).shouldUpgradeCallback = (req) =>
336
- _.toLower(req.headers?.upgrade) === 'websocket';
321
+ (
322
+ appiumServer as unknown as {shouldUpgradeCallback?: (req: http.IncomingMessage) => boolean}
323
+ ).shouldUpgradeCallback = (req) =>
324
+ String(req.headers?.upgrade ?? '').toLowerCase() === 'websocket';
337
325
  appiumServer.on('upgrade', (req, socket, head) => {
338
326
  if (!tryHandleWebSocketUpgrade(req, socket, head, appiumServer.webSocketsMapping)) {
339
327
  socket.destroy();
@@ -343,9 +331,11 @@ function configureHttp({
343
331
 
344
332
  // http.Server.close() only stops new connections, but we need to wait until
345
333
  // all connections are closed and the `close` event is emitted
346
- const originalClose = appiumServer.close.bind(appiumServer);
334
+ const originalClose = appiumServer.close.bind(appiumServer) as (
335
+ callback?: (err?: Error | null) => void,
336
+ ) => void;
347
337
  appiumServer.close = async () =>
348
- await new B<void>((_resolve, _reject) => {
338
+ await new Promise<void>((_resolve, _reject) => {
349
339
  log.info('Closing Appium HTTP server');
350
340
  const timer = new timing.Timer().start();
351
341
  const onTimeout = setTimeout(() => {
@@ -353,7 +343,7 @@ function configureHttp({
353
343
  log.info(
354
344
  `Not all active connections have been closed within ${gracefulShutdownTimeout}ms. ` +
355
345
  `This timeout might be customized by the --shutdown-timeout command line ` +
356
- `argument. Closing the server anyway.`
346
+ `argument. Closing the server anyway.`,
357
347
  );
358
348
  }
359
349
  process.exit(process.exitCode ?? 0);
@@ -361,13 +351,13 @@ function configureHttp({
361
351
  const onClose = () => {
362
352
  log.info(
363
353
  `Appium HTTP server has been successfully closed after ` +
364
- `${timer.getDuration().asMilliSeconds.toFixed(0)}ms`
354
+ `${timer.getDuration().asMilliSeconds.toFixed(0)}ms`,
365
355
  );
366
356
  clearTimeout(onTimeout);
367
357
  _resolve();
368
358
  };
369
359
  httpServer.once('close', onClose);
370
- originalClose((err?: Error) => {
360
+ originalClose((err?: Error | null) => {
371
361
  if (err) {
372
362
  clearTimeout(onTimeout);
373
363
  httpServer.removeListener('close', onClose);
@@ -379,14 +369,13 @@ function configureHttp({
379
369
  appiumServer.once('error', (err: NodeJS.ErrnoException) => {
380
370
  if (err.code === 'EADDRNOTAVAIL') {
381
371
  log.error(
382
- 'Could not start REST http interface listener. ' +
383
- 'Requested address is not available.'
372
+ 'Could not start REST http interface listener. ' + 'Requested address is not available.',
384
373
  );
385
374
  } else {
386
375
  log.error(
387
376
  'Could not start REST http interface listener. The requested ' +
388
377
  'port may already be in use. Please make sure there is no ' +
389
- 'other instance of this server running already.'
378
+ 'other instance of this server running already.',
390
379
  );
391
380
  }
392
381
  reject(err);
@@ -405,12 +394,21 @@ async function startServer({
405
394
  requestTimeout,
406
395
  }: StartServerOpts): Promise<void> {
407
396
  // If the hostname is omitted, the server will accept connections on any IP address
408
- const start = B.promisify(httpServer.listen, {
409
- context: httpServer,
410
- }) as (port: number, hostname?: string) => B<HttpServer>;
411
- const startPromise = start(port, hostname);
397
+ const startPromise = new Promise<void>((resolve, reject) => {
398
+ const onError = (err: Error) => {
399
+ httpServer.removeListener('listening', onListening);
400
+ reject(err);
401
+ };
402
+ const onListening = () => {
403
+ httpServer.removeListener('error', onError);
404
+ resolve();
405
+ };
406
+ httpServer.once('error', onError);
407
+ httpServer.once('listening', onListening);
408
+ httpServer.listen(port, hostname);
409
+ });
412
410
  httpServer.keepAliveTimeout = keepAliveTimeout;
413
- if (_.isInteger(requestTimeout)) {
411
+ if (Number.isInteger(requestTimeout)) {
414
412
  httpServer.requestTimeout = Number(requestTimeout);
415
413
  }
416
414
  // headers timeout must be greater than keepAliveTimeout
@@ -1,5 +1,4 @@
1
- import _ from 'lodash';
2
- import B from 'bluebird';
1
+ import {util} from '@appium/support';
3
2
  import type {AppiumServer, WSServer} from '@appium/types';
4
3
 
5
4
  export const DEFAULT_WS_PATHNAME_PREFIX = '/ws';
@@ -11,7 +10,7 @@ export const DEFAULT_WS_PATHNAME_PREFIX = '/ws';
11
10
  export async function addWebSocketHandler(
12
11
  this: AppiumServer,
13
12
  handlerPathname: string,
14
- handlerServer: WSServer
13
+ handlerServer: WSServer,
15
14
  ): Promise<void> {
16
15
  this.webSocketsMapping[handlerPathname] = handlerServer;
17
16
  }
@@ -22,16 +21,16 @@ export async function addWebSocketHandler(
22
21
  */
23
22
  export async function getWebSocketHandlers(
24
23
  this: AppiumServer,
25
- keysFilter: string | null = null
24
+ keysFilter: string | null = null,
26
25
  ): Promise<Record<string, WSServer>> {
27
- return _.toPairs(this.webSocketsMapping).reduce<Record<string, WSServer>>(
26
+ return Object.entries(this.webSocketsMapping).reduce<Record<string, WSServer>>(
28
27
  (acc, [pathname, wsServer]) => {
29
- if (!_.isString(keysFilter) || pathname.includes(keysFilter)) {
28
+ if (typeof keysFilter !== 'string' || pathname.includes(keysFilter)) {
30
29
  acc[pathname] = wsServer;
31
30
  }
32
31
  return acc;
33
32
  },
34
- {}
33
+ {},
35
34
  );
36
35
  }
37
36
 
@@ -41,7 +40,7 @@ export async function getWebSocketHandlers(
41
40
  */
42
41
  export async function removeWebSocketHandler(
43
42
  this: AppiumServer,
44
- handlerPathname: string
43
+ handlerPathname: string,
45
44
  ): Promise<boolean> {
46
45
  const wsServer = this.webSocketsMapping?.[handlerPathname];
47
46
  if (!wsServer) {
@@ -67,15 +66,12 @@ export async function removeWebSocketHandler(
67
66
  * @see AppiumServerExtension.removeAllWebSocketHandlers
68
67
  */
69
68
  export async function removeAllWebSocketHandlers(this: AppiumServer): Promise<boolean> {
70
- if (_.isEmpty(this.webSocketsMapping)) {
69
+ if (util.isEmpty(this.webSocketsMapping)) {
71
70
  return false;
72
71
  }
73
72
 
74
- return _.some(
75
- await B.all(
76
- _.keys(this.webSocketsMapping).map((pathname) =>
77
- this.removeWebSocketHandler(pathname)
78
- )
79
- )
73
+ const results = await Promise.all(
74
+ Object.keys(this.webSocketsMapping).map((pathname) => this.removeWebSocketHandler(pathname)),
80
75
  );
76
+ return results.some(Boolean);
81
77
  }
@@ -1,28 +1,28 @@
1
1
  import type {Constraints, W3CCapabilities, Capabilities, AppiumLogger} from '@appium/types';
2
- import _ from 'lodash';
2
+ import {util} from '@appium/support';
3
3
 
4
4
  /**
5
5
  * Determine whether the given argument is valid
6
6
  * W3C capabilities instance.
7
7
  */
8
8
  export function isW3cCaps(caps: unknown): caps is W3CCapabilities<Constraints> {
9
- if (!_.isPlainObject(caps)) {
9
+ if (!util.isPlainObject(caps)) {
10
10
  return false;
11
11
  }
12
12
 
13
13
  const c = caps as Record<string, unknown>;
14
14
  const isFirstMatchValid = () =>
15
- _.isArray(c.firstMatch) &&
16
- !_.isEmpty(c.firstMatch) &&
17
- _.every(c.firstMatch, _.isPlainObject);
18
- const isAlwaysMatchValid = () => _.isPlainObject(c.alwaysMatch);
19
- if (_.has(c, 'firstMatch') && _.has(c, 'alwaysMatch')) {
15
+ Array.isArray(c.firstMatch) &&
16
+ !util.isEmpty(c.firstMatch) &&
17
+ c.firstMatch.every((item) => util.isPlainObject(item));
18
+ const isAlwaysMatchValid = () => util.isPlainObject(c.alwaysMatch);
19
+ if (Object.hasOwn(c, 'firstMatch') && Object.hasOwn(c, 'alwaysMatch')) {
20
20
  return isFirstMatchValid() && isAlwaysMatchValid();
21
21
  }
22
- if (_.has(c, 'firstMatch')) {
22
+ if (Object.hasOwn(c, 'firstMatch')) {
23
23
  return isFirstMatchValid();
24
24
  }
25
- if (_.has(c, 'alwaysMatch')) {
25
+ if (Object.hasOwn(c, 'alwaysMatch')) {
26
26
  return isAlwaysMatchValid();
27
27
  }
28
28
  return false;
@@ -34,18 +34,21 @@ export function isW3cCaps(caps: unknown): caps is W3CCapabilities<Constraints> {
34
34
  export function fixCaps<C extends Constraints>(
35
35
  oldCaps: Record<string, unknown>,
36
36
  desiredCapConstraints: C,
37
- log: AppiumLogger
37
+ log: AppiumLogger,
38
38
  ): Capabilities<C> {
39
- const caps = _.clone(oldCaps) as Record<string, unknown>;
39
+ const caps = {...oldCaps} as Record<string, unknown>;
40
40
 
41
- const logCastWarning = (prefix: string) => log.warn(`${prefix}. This may cause unexpected behavior`);
41
+ const logCastWarning = (prefix: string) =>
42
+ log.warn(`${prefix}. This may cause unexpected behavior`);
42
43
 
43
44
  // boolean capabilities can be passed in as strings 'false' and 'true'
44
45
  // which we want to translate into boolean values
45
- const booleanCaps = _.keys(_.pickBy(desiredCapConstraints, (k) => k.isBoolean === true));
46
+ const booleanCaps = Object.keys(desiredCapConstraints).filter(
47
+ (key) => desiredCapConstraints[key as keyof C]?.isBoolean === true,
48
+ );
46
49
  for (const cap of booleanCaps) {
47
50
  const value = oldCaps[cap];
48
- if (!_.isString(value)) {
51
+ if (typeof value !== 'string') {
49
52
  continue;
50
53
  }
51
54
 
@@ -59,10 +62,12 @@ export function fixCaps<C extends Constraints>(
59
62
  }
60
63
 
61
64
  // int capabilities are often sent in as strings by frameworks
62
- const intCaps = _.keys(_.pickBy(desiredCapConstraints, (k) => k.isNumber === true));
65
+ const intCaps = Object.keys(desiredCapConstraints).filter(
66
+ (key) => desiredCapConstraints[key as keyof C]?.isNumber === true,
67
+ );
63
68
  for (const cap of intCaps) {
64
69
  const value = oldCaps[cap];
65
- if (!_.isString(value)) {
70
+ if (typeof value !== 'string') {
66
71
  continue;
67
72
  }
68
73
 
@@ -1,4 +1,4 @@
1
- import _ from 'lodash';
1
+ import {util} from '@appium/support';
2
2
  import type {Constraints, Driver, DriverClass} from '@appium/types';
3
3
  import type {BaseDriver} from '../basedriver/driver';
4
4
 
@@ -11,12 +11,12 @@ import type {BaseDriver} from '../basedriver/driver';
11
11
  */
12
12
  export function resolveExecuteExtensionName<C extends Constraints>(
13
13
  this: BaseDriver<C>,
14
- commandName: string
14
+ commandName: string,
15
15
  ): string {
16
16
  const Driver = this.constructor as DriverClass<Driver<C>>;
17
17
  const methodMap = Driver.executeMethodMap;
18
18
 
19
- if (methodMap && _.isPlainObject(methodMap) && commandName in methodMap) {
19
+ if (methodMap && util.isPlainObject(methodMap) && commandName in methodMap) {
20
20
  const command = methodMap[commandName]?.command;
21
21
  if (typeof command === 'string') {
22
22
  return command;
@@ -1,6 +1,5 @@
1
1
  import type {StringRecord} from '@appium/types';
2
2
  import {distance} from 'fastest-levenshtein';
3
- import _ from 'lodash';
4
3
 
5
4
  /**
6
5
  * Inclusive maximum Levenshtein edit distance for offering a "did you mean" hint.
@@ -30,24 +29,31 @@ export function rankLevenshteinCandidates(
30
29
 
31
30
  const matchesMap: StringRecord<string[]> = candidates
32
31
  .map((name) => [distance(target, name), name] as const)
33
- .reduce((acc, [dist, name]) => {
34
- const key = String(dist);
35
- if (key in acc) {
36
- acc[key].push(name);
37
- } else {
38
- acc[key] = [name];
39
- }
40
- return acc;
41
- }, {});
42
- const sortedDistanceKeys = _.keys(matchesMap).sort((a, b) => parseInt(a, 10) - parseInt(b, 10));
43
- const sorted = _.flatten(
44
- sortedDistanceKeys.map((k) => (matchesMap[k] ?? []).sort()),
32
+ .reduce(
33
+ (acc, [dist, name]) => {
34
+ const key = String(dist);
35
+ if (key in acc) {
36
+ acc[key].push(name);
37
+ } else {
38
+ acc[key] = [name];
39
+ }
40
+ return acc;
41
+ },
42
+ {} as StringRecord<string[]>,
43
+ );
44
+ const sortedDistanceKeys = Object.keys(matchesMap).sort(
45
+ (a, b) => parseInt(a, 10) - parseInt(b, 10),
45
46
  );
47
+ const sorted = sortedDistanceKeys.flatMap((k) => (matchesMap[k] ?? []).sort());
46
48
 
47
49
  const best = sorted[0];
48
50
  const firstDistanceKey = sortedDistanceKeys[0];
49
51
  const minDist = firstDistanceKey !== undefined ? parseInt(firstDistanceKey, 10) : NaN;
50
- const suggestion = maxEditDistance >= 0 && best !== undefined && !Number.isNaN(minDist) && minDist <= maxEditDistance
52
+ const suggestion =
53
+ maxEditDistance >= 0 &&
54
+ best !== undefined &&
55
+ !Number.isNaN(minDist) &&
56
+ minDist <= maxEditDistance
51
57
  ? best
52
58
  : undefined;
53
59
  return {sorted, suggestion};
package/lib/index.js CHANGED
@@ -1,19 +1,9 @@
1
- import B from 'bluebird';
2
-
3
- try {
4
- B.config({
5
- cancellation: true,
6
- });
7
- } catch {
8
- // sometimes during testing this somehow gets required twice and results in an error about
9
- // cancellation not being able to be enabled after promise has been configured
10
- }
11
-
12
1
  // BaseDriver exports
13
2
  export {ExtensionCore} from './basedriver/extension-core';
14
3
  import {BaseDriver} from './basedriver/driver';
15
4
  export {DriverCore} from './basedriver/core';
16
5
  export {DeviceSettings} from './basedriver/device-settings';
6
+ export {AppiumIpc} from './basedriver/ipc';
17
7
 
18
8
  export {BaseDriver};
19
9
  export default BaseDriver;
@@ -24,7 +14,8 @@ export * from './protocol';
24
14
  export {errorFromMJSONWPStatusCode as errorFromCode} from './protocol';
25
15
 
26
16
  // Express exports
27
- export {STATIC_DIR} from './express/static';
17
+ /** @deprecated Removed in Appium 4. Use hard-copied test fixtures in driver CI instead. */
18
+ export {TEST_FIXTURES_DIR as STATIC_DIR} from './test-pages';
28
19
  export {server, normalizeBasePath} from './express/server';
29
20
 
30
21
  // jsonwp-proxy exports