@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/express/server.ts
CHANGED
|
@@ -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
|
|
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
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
|
127
|
-
// we
|
|
128
|
-
//
|
|
129
|
-
//
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
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
|
-
|
|
168
|
-
|
|
169
|
-
|
|
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
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
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
|
-
|
|
194
|
-
|
|
195
|
-
|
|
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 (
|
|
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
|
|
272
|
-
|
|
273
|
-
['certificate',
|
|
274
|
-
|
|
275
|
-
)
|
|
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
|
|
284
|
-
|
|
285
|
-
|
|
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
|
-
(
|
|
336
|
-
|
|
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
|
|
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
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
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 (
|
|
411
|
+
if (Number.isInteger(requestTimeout)) {
|
|
414
412
|
httpServer.requestTimeout = Number(requestTimeout);
|
|
415
413
|
}
|
|
416
414
|
// headers timeout must be greater than keepAliveTimeout
|
package/lib/express/websocket.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import
|
|
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
|
|
26
|
+
return Object.entries(this.webSocketsMapping).reduce<Record<string, WSServer>>(
|
|
28
27
|
(acc, [pathname, wsServer]) => {
|
|
29
|
-
if (
|
|
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 (
|
|
69
|
+
if (util.isEmpty(this.webSocketsMapping)) {
|
|
71
70
|
return false;
|
|
72
71
|
}
|
|
73
72
|
|
|
74
|
-
|
|
75
|
-
|
|
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
|
|
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 (!
|
|
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
|
-
|
|
16
|
-
!
|
|
17
|
-
|
|
18
|
-
const isAlwaysMatchValid = () =>
|
|
19
|
-
if (
|
|
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 (
|
|
22
|
+
if (Object.hasOwn(c, 'firstMatch')) {
|
|
23
23
|
return isFirstMatchValid();
|
|
24
24
|
}
|
|
25
|
-
if (
|
|
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 =
|
|
39
|
+
const caps = {...oldCaps} as Record<string, unknown>;
|
|
40
40
|
|
|
41
|
-
const logCastWarning = (prefix: string) =>
|
|
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 =
|
|
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 (
|
|
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 =
|
|
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 (
|
|
70
|
+
if (typeof value !== 'string') {
|
|
66
71
|
continue;
|
|
67
72
|
}
|
|
68
73
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
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 &&
|
|
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(
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
acc
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|