@appium/base-driver 8.5.3 → 8.5.4
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.map +1 -1
- package/build/lib/basedriver/capabilities.js +1 -1
- package/build/lib/basedriver/commands/event.js +1 -1
- package/build/lib/basedriver/commands/find.d.ts.map +1 -1
- package/build/lib/basedriver/commands/find.js +1 -1
- package/build/lib/basedriver/commands/index.js +1 -1
- package/build/lib/basedriver/commands/log.d.ts.map +1 -1
- package/build/lib/basedriver/commands/log.js +1 -1
- package/build/lib/basedriver/commands/session.js +1 -1
- package/build/lib/basedriver/commands/settings.d.ts.map +1 -1
- package/build/lib/basedriver/commands/settings.js +1 -1
- package/build/lib/basedriver/commands/timeout.d.ts.map +1 -1
- package/build/lib/basedriver/commands/timeout.js +1 -1
- package/build/lib/basedriver/core.d.ts +120 -139
- package/build/lib/basedriver/core.d.ts.map +1 -1
- package/build/lib/basedriver/core.js +1 -49
- package/build/lib/basedriver/desired-caps.d.ts +5 -2
- package/build/lib/basedriver/desired-caps.d.ts.map +1 -1
- package/build/lib/basedriver/desired-caps.js +14 -18
- package/build/lib/basedriver/device-settings.d.ts +9 -9
- package/build/lib/basedriver/device-settings.d.ts.map +1 -1
- package/build/lib/basedriver/device-settings.js +4 -4
- package/build/lib/basedriver/driver.d.ts +43 -38
- package/build/lib/basedriver/driver.d.ts.map +1 -1
- package/build/lib/basedriver/driver.js +58 -11
- package/build/lib/basedriver/helpers.d.ts +8 -3
- package/build/lib/basedriver/helpers.d.ts.map +1 -1
- package/build/lib/basedriver/helpers.js +4 -6
- package/build/lib/basedriver/logger.d.ts +1 -1
- package/build/lib/basedriver/logger.d.ts.map +1 -1
- package/build/lib/basedriver/logger.js +1 -1
- package/build/lib/constants.js +1 -1
- package/build/lib/express/crash.d.ts.map +1 -1
- package/build/lib/express/crash.js +1 -1
- package/build/lib/express/express-logging.d.ts.map +1 -1
- package/build/lib/express/express-logging.js +1 -1
- package/build/lib/express/idempotency.js +1 -1
- package/build/lib/express/logger.d.ts +1 -1
- package/build/lib/express/logger.d.ts.map +1 -1
- package/build/lib/express/logger.js +1 -1
- package/build/lib/express/middleware.d.ts.map +1 -1
- package/build/lib/express/middleware.js +1 -1
- package/build/lib/express/server.d.ts +21 -0
- package/build/lib/express/server.d.ts.map +1 -1
- package/build/lib/express/server.js +4 -9
- package/build/lib/express/static.d.ts.map +1 -1
- package/build/lib/express/static.js +2 -2
- package/build/lib/express/websocket.d.ts +14 -11
- package/build/lib/express/websocket.d.ts.map +1 -1
- package/build/lib/express/websocket.js +2 -2
- package/build/lib/helpers/capabilities.d.ts.map +1 -1
- package/build/lib/helpers/capabilities.js +1 -1
- package/build/lib/index.d.ts +2 -1
- package/build/lib/index.js +7 -1
- package/build/lib/jsonwp-proxy/protocol-converter.d.ts.map +1 -1
- package/build/lib/jsonwp-proxy/protocol-converter.js +2 -2
- package/build/lib/jsonwp-proxy/proxy.d.ts +30 -5
- package/build/lib/jsonwp-proxy/proxy.d.ts.map +1 -1
- package/build/lib/jsonwp-proxy/proxy.js +20 -4
- package/build/lib/jsonwp-status/status.d.ts.map +1 -1
- package/build/lib/jsonwp-status/status.js +2 -2
- package/build/lib/protocol/errors.d.ts +17 -8
- package/build/lib/protocol/errors.d.ts.map +1 -1
- package/build/lib/protocol/errors.js +9 -5
- package/build/lib/protocol/helpers.js +1 -1
- package/build/lib/protocol/index.js +1 -1
- package/build/lib/protocol/protocol.d.ts.map +1 -1
- package/build/lib/protocol/protocol.js +1 -1
- package/build/lib/protocol/routes.d.ts +17 -3
- package/build/lib/protocol/routes.d.ts.map +1 -1
- package/build/lib/protocol/routes.js +1 -1
- package/build/lib/protocol/validators.js +1 -1
- package/build/test/basedriver/driver-e2e-tests.js +1 -1
- package/build/test/basedriver/driver-tests.js +1 -1
- package/build/test/basedriver/index.js +1 -1
- package/build/test/e2e/basedriver/driver.e2e.spec.js +1 -1
- package/build/test/e2e/basedriver/helpers.e2e.spec.js +1 -1
- package/build/test/e2e/basedriver/websockets.e2e.spec.js +1 -1
- package/build/test/e2e/express/server.e2e.spec.js +1 -1
- package/build/test/e2e/jsonwp-proxy/proxy.e2e.spec.js +1 -1
- package/build/test/e2e/protocol/fake-driver.js +1 -1
- package/build/test/e2e/protocol/helpers.js +1 -1
- package/build/test/e2e/protocol/protocol.e2e.spec.js +13 -13
- package/build/test/helpers.js +1 -1
- package/build/test/unit/basedriver/capabilities.spec.js +12 -12
- package/build/test/unit/basedriver/capability.spec.js +15 -15
- package/build/test/unit/basedriver/commands/event.spec.js +1 -1
- package/build/test/unit/basedriver/commands/log.spec.js +1 -1
- package/build/test/unit/basedriver/device-settings.spec.js +1 -1
- package/build/test/unit/basedriver/driver.spec.js +1 -1
- package/build/test/unit/basedriver/helpers.spec.js +33 -33
- package/build/test/unit/basedriver/timeout.spec.js +1 -1
- package/build/test/unit/express/server.spec.js +1 -1
- package/build/test/unit/express/static.spec.js +2 -2
- package/build/test/unit/jsonwp-proxy/mock-request.js +1 -1
- package/build/test/unit/jsonwp-proxy/protocol-converter.spec.js +1 -1
- package/build/test/unit/jsonwp-proxy/proxy.spec.js +2 -2
- package/build/test/unit/jsonwp-proxy/url.spec.js +1 -1
- package/build/test/unit/jsonwp-status/status.spec.js +1 -1
- package/build/test/unit/protocol/errors.spec.js +1 -1
- package/build/test/unit/protocol/routes.spec.js +1 -1
- package/build/test/unit/protocol/validator.spec.js +1 -1
- package/build/tsconfig.tsbuildinfo +1 -1
- package/lib/basedriver/capabilities.js +95 -47
- package/lib/basedriver/commands/event.js +4 -4
- package/lib/basedriver/commands/find.js +12 -26
- package/lib/basedriver/commands/index.js +7 -7
- package/lib/basedriver/commands/log.js +5 -7
- package/lib/basedriver/commands/session.js +3 -3
- package/lib/basedriver/commands/settings.js +3 -5
- package/lib/basedriver/commands/timeout.js +18 -23
- package/lib/basedriver/core.js +150 -229
- package/lib/basedriver/desired-caps.js +30 -29
- package/lib/basedriver/device-settings.js +21 -20
- package/lib/basedriver/driver.js +131 -96
- package/lib/basedriver/helpers.js +124 -81
- package/lib/basedriver/logger.js +1 -1
- package/lib/constants.js +2 -6
- package/lib/express/crash.js +4 -6
- package/lib/express/express-logging.js +26 -24
- package/lib/express/idempotency.js +16 -16
- package/lib/express/logger.js +1 -1
- package/lib/express/middleware.js +49 -33
- package/lib/express/server.js +68 -44
- package/lib/express/static.js +11 -12
- package/lib/express/websocket.js +26 -16
- package/lib/helpers/capabilities.js +11 -16
- package/lib/index.js +50 -33
- package/lib/jsonwp-proxy/protocol-converter.js +85 -69
- package/lib/jsonwp-proxy/proxy.js +116 -53
- package/lib/jsonwp-status/status.js +36 -29
- package/lib/protocol/errors.js +469 -292
- package/lib/protocol/helpers.js +5 -8
- package/lib/protocol/index.js +22 -15
- package/lib/protocol/protocol.js +103 -55
- package/lib/protocol/routes.js +430 -273
- package/lib/protocol/validators.js +5 -5
- package/package.json +7 -6
- package/test/basedriver/driver-e2e-tests.js +92 -66
- package/test/basedriver/driver-tests.js +90 -33
- package/test/basedriver/index.js +1 -1
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import _ from 'lodash';
|
|
2
2
|
import log from './logger';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import {errors} from '../protocol';
|
|
4
|
+
import {handleIdempotency} from './idempotency';
|
|
5
5
|
|
|
6
|
-
function allowCrossDomain
|
|
6
|
+
function allowCrossDomain(req, res, next) {
|
|
7
7
|
try {
|
|
8
8
|
res.header('Access-Control-Allow-Origin', '*');
|
|
9
9
|
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, OPTIONS, DELETE');
|
|
10
|
-
res.header(
|
|
10
|
+
res.header(
|
|
11
|
+
'Access-Control-Allow-Headers',
|
|
12
|
+
'Cache-Control, Pragma, Origin, X-Requested-With, Content-Type, Accept, User-Agent'
|
|
13
|
+
);
|
|
11
14
|
|
|
12
15
|
// need to respond 200 to OPTIONS
|
|
13
16
|
if ('OPTIONS' === req.method) {
|
|
@@ -19,11 +22,13 @@ function allowCrossDomain (req, res, next) {
|
|
|
19
22
|
next();
|
|
20
23
|
}
|
|
21
24
|
|
|
22
|
-
function allowCrossDomainAsyncExecute
|
|
25
|
+
function allowCrossDomainAsyncExecute(basePath) {
|
|
23
26
|
return (req, res, next) => {
|
|
24
27
|
// there are two paths for async responses, so cover both
|
|
25
28
|
// https://regex101.com/r/txYiEz/1
|
|
26
|
-
const receiveAsyncResponseRegExp = new RegExp(
|
|
29
|
+
const receiveAsyncResponseRegExp = new RegExp(
|
|
30
|
+
`${_.escapeRegExp(basePath)}/session/[a-f0-9-]+/(appium/)?receive_async_response`
|
|
31
|
+
);
|
|
27
32
|
if (!receiveAsyncResponseRegExp.test(req.url)) {
|
|
28
33
|
return next();
|
|
29
34
|
}
|
|
@@ -31,10 +36,13 @@ function allowCrossDomainAsyncExecute (basePath) {
|
|
|
31
36
|
};
|
|
32
37
|
}
|
|
33
38
|
|
|
34
|
-
function fixPythonContentType
|
|
39
|
+
function fixPythonContentType(basePath) {
|
|
35
40
|
return (req, res, next) => {
|
|
36
41
|
// hack because python client library gives us wrong content-type
|
|
37
|
-
if (
|
|
42
|
+
if (
|
|
43
|
+
new RegExp(`^${_.escapeRegExp(basePath)}`).test(req.path) &&
|
|
44
|
+
/^Python/.test(req.headers['user-agent'])
|
|
45
|
+
) {
|
|
38
46
|
if (req.headers['content-type'] === 'application/x-www-form-urlencoded') {
|
|
39
47
|
req.headers['content-type'] = 'application/json; charset=utf-8';
|
|
40
48
|
}
|
|
@@ -43,14 +51,14 @@ function fixPythonContentType (basePath) {
|
|
|
43
51
|
};
|
|
44
52
|
}
|
|
45
53
|
|
|
46
|
-
function defaultToJSONContentType
|
|
54
|
+
function defaultToJSONContentType(req, res, next) {
|
|
47
55
|
if (!req.headers['content-type']) {
|
|
48
56
|
req.headers['content-type'] = 'application/json; charset=utf-8';
|
|
49
57
|
}
|
|
50
58
|
next();
|
|
51
59
|
}
|
|
52
60
|
|
|
53
|
-
function catchAllHandler
|
|
61
|
+
function catchAllHandler(err, req, res, next) {
|
|
54
62
|
if (res.headersSent) {
|
|
55
63
|
return next(err);
|
|
56
64
|
}
|
|
@@ -58,36 +66,40 @@ function catchAllHandler (err, req, res, next) {
|
|
|
58
66
|
log.error(`Uncaught error: ${err.message}`);
|
|
59
67
|
log.error('Sending generic error response');
|
|
60
68
|
const error = errors.UnknownError;
|
|
61
|
-
res.status(error.w3cStatus()).json(
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
+
res.status(error.w3cStatus()).json(
|
|
70
|
+
patchWithSessionId(req, {
|
|
71
|
+
status: error.code(),
|
|
72
|
+
value: {
|
|
73
|
+
error: error.error(),
|
|
74
|
+
message: `An unknown server-side error occurred while processing the command: ${err.message}`,
|
|
75
|
+
stacktrace: err.stack,
|
|
76
|
+
},
|
|
77
|
+
})
|
|
78
|
+
);
|
|
69
79
|
log.error(err);
|
|
70
80
|
}
|
|
71
81
|
|
|
72
|
-
function catch404Handler
|
|
82
|
+
function catch404Handler(req, res) {
|
|
73
83
|
log.debug(`No route found for ${req.url}`);
|
|
74
84
|
const error = errors.UnknownCommandError;
|
|
75
|
-
res.status(error.w3cStatus()).json(
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
+
res.status(error.w3cStatus()).json(
|
|
86
|
+
patchWithSessionId(req, {
|
|
87
|
+
status: error.code(),
|
|
88
|
+
value: {
|
|
89
|
+
error: error.error(),
|
|
90
|
+
message:
|
|
91
|
+
'The requested resource could not be found, or a request was ' +
|
|
92
|
+
'received using an HTTP method that is not supported by the mapped ' +
|
|
93
|
+
'resource',
|
|
94
|
+
stacktrace: '',
|
|
95
|
+
},
|
|
96
|
+
})
|
|
97
|
+
);
|
|
85
98
|
}
|
|
86
99
|
|
|
87
|
-
|
|
88
100
|
const SESSION_ID_PATTERN = /\/session\/([^/]+)/;
|
|
89
101
|
|
|
90
|
-
function patchWithSessionId
|
|
102
|
+
function patchWithSessionId(req, body) {
|
|
91
103
|
const match = SESSION_ID_PATTERN.exec(req.url);
|
|
92
104
|
if (match) {
|
|
93
105
|
body.sessionId = match[1];
|
|
@@ -96,7 +108,11 @@ function patchWithSessionId (req, body) {
|
|
|
96
108
|
}
|
|
97
109
|
|
|
98
110
|
export {
|
|
99
|
-
allowCrossDomain,
|
|
100
|
-
|
|
111
|
+
allowCrossDomain,
|
|
112
|
+
fixPythonContentType,
|
|
113
|
+
defaultToJSONContentType,
|
|
114
|
+
catchAllHandler,
|
|
115
|
+
allowCrossDomainAsyncExecute,
|
|
116
|
+
handleIdempotency,
|
|
101
117
|
catch404Handler,
|
|
102
118
|
};
|
package/lib/express/server.js
CHANGED
|
@@ -6,27 +6,31 @@ import favicon from 'serve-favicon';
|
|
|
6
6
|
import bodyParser from 'body-parser';
|
|
7
7
|
import methodOverride from 'method-override';
|
|
8
8
|
import log from './logger';
|
|
9
|
-
import {
|
|
9
|
+
import {startLogFormatter, endLogFormatter} from './express-logging';
|
|
10
10
|
import {
|
|
11
|
-
allowCrossDomain,
|
|
12
|
-
|
|
11
|
+
allowCrossDomain,
|
|
12
|
+
fixPythonContentType,
|
|
13
|
+
defaultToJSONContentType,
|
|
14
|
+
catchAllHandler,
|
|
15
|
+
allowCrossDomainAsyncExecute,
|
|
16
|
+
handleIdempotency,
|
|
13
17
|
catch404Handler,
|
|
14
18
|
} from './middleware';
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
19
|
+
import {guineaPig, guineaPigScrollable, guineaPigAppBanner, welcome, STATIC_DIR} from './static';
|
|
20
|
+
import {produceError, produceCrash} from './crash';
|
|
17
21
|
import {
|
|
18
|
-
addWebSocketHandler,
|
|
19
|
-
|
|
22
|
+
addWebSocketHandler,
|
|
23
|
+
removeWebSocketHandler,
|
|
24
|
+
removeAllWebSocketHandlers,
|
|
25
|
+
getWebSocketHandlers,
|
|
20
26
|
} from './websocket';
|
|
21
27
|
import B from 'bluebird';
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
24
|
-
|
|
28
|
+
import {DEFAULT_BASE_PATH} from '../constants';
|
|
29
|
+
import {EventEmitter} from 'events';
|
|
25
30
|
|
|
26
31
|
const KEEP_ALIVE_TIMEOUT_MS = 10 * 60 * 1000; // 10 minutes
|
|
27
32
|
|
|
28
|
-
|
|
29
|
-
async function server (opts = {}) {
|
|
33
|
+
async function server(opts = {}) {
|
|
30
34
|
const {
|
|
31
35
|
routeConfiguringFunction,
|
|
32
36
|
port,
|
|
@@ -49,7 +53,13 @@ async function server (opts = {}) {
|
|
|
49
53
|
// try/catch so any errors can be passed to reject.
|
|
50
54
|
try {
|
|
51
55
|
configureHttp({httpServer, reject, keepAliveTimeout});
|
|
52
|
-
configureServer({
|
|
56
|
+
configureServer({
|
|
57
|
+
app,
|
|
58
|
+
addRoutes: routeConfiguringFunction,
|
|
59
|
+
allowCors,
|
|
60
|
+
basePath,
|
|
61
|
+
extraMethodMap,
|
|
62
|
+
});
|
|
53
63
|
// allow extensions to update the app and http server objects
|
|
54
64
|
for (const updater of serverUpdaters) {
|
|
55
65
|
await updater(app, httpServer);
|
|
@@ -66,10 +76,9 @@ async function server (opts = {}) {
|
|
|
66
76
|
reject(err);
|
|
67
77
|
}
|
|
68
78
|
});
|
|
69
|
-
|
|
70
79
|
}
|
|
71
80
|
|
|
72
|
-
function configureServer
|
|
81
|
+
function configureServer({
|
|
73
82
|
app,
|
|
74
83
|
addRoutes,
|
|
75
84
|
allowCors = true,
|
|
@@ -116,7 +125,7 @@ function configureServer ({
|
|
|
116
125
|
app.all('/test/guinea-pig-app-banner', guineaPigAppBanner);
|
|
117
126
|
}
|
|
118
127
|
|
|
119
|
-
function configureHttp
|
|
128
|
+
function configureHttp({httpServer, reject, keepAliveTimeout}) {
|
|
120
129
|
const serverState = {
|
|
121
130
|
notifier: new EventEmitter(),
|
|
122
131
|
closed: false,
|
|
@@ -129,28 +138,32 @@ function configureHttp ({httpServer, reject, keepAliveTimeout}) {
|
|
|
129
138
|
// http.Server.close() only stops new connections, but we need to wait until
|
|
130
139
|
// all connections are closed and the `close` event is emitted
|
|
131
140
|
const close = httpServer.close.bind(httpServer);
|
|
132
|
-
httpServer.close = async () =>
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
141
|
+
httpServer.close = async () =>
|
|
142
|
+
await new B((resolve, reject) => {
|
|
143
|
+
// https://github.com/nodejs/node-v0.x-archive/issues/9066#issuecomment-124210576
|
|
144
|
+
serverState.closed = true;
|
|
145
|
+
serverState.notifier.emit('shutdown');
|
|
146
|
+
log.info('Waiting until the server is closed');
|
|
147
|
+
httpServer.on('close', () => {
|
|
148
|
+
log.info('Received server close event');
|
|
149
|
+
resolve();
|
|
150
|
+
});
|
|
151
|
+
close((err) => {
|
|
152
|
+
if (err) reject(err); // eslint-disable-line curly
|
|
153
|
+
});
|
|
143
154
|
});
|
|
144
|
-
});
|
|
145
155
|
|
|
146
156
|
httpServer.on('error', (err) => {
|
|
147
157
|
if (err.code === 'EADDRNOTAVAIL') {
|
|
148
|
-
log.error(
|
|
149
|
-
|
|
158
|
+
log.error(
|
|
159
|
+
'Could not start REST http interface listener. ' + 'Requested address is not available.'
|
|
160
|
+
);
|
|
150
161
|
} else {
|
|
151
|
-
log.error(
|
|
152
|
-
|
|
153
|
-
|
|
162
|
+
log.error(
|
|
163
|
+
'Could not start REST http interface listener. The requested ' +
|
|
164
|
+
'port may already be in use. Please make sure there is no ' +
|
|
165
|
+
'other instance of this server running already.'
|
|
166
|
+
);
|
|
154
167
|
}
|
|
155
168
|
reject(err);
|
|
156
169
|
});
|
|
@@ -159,7 +172,7 @@ function configureHttp ({httpServer, reject, keepAliveTimeout}) {
|
|
|
159
172
|
socket.setTimeout(keepAliveTimeout);
|
|
160
173
|
socket.on('error', reject);
|
|
161
174
|
|
|
162
|
-
function destroy
|
|
175
|
+
function destroy() {
|
|
163
176
|
socket.destroy();
|
|
164
177
|
}
|
|
165
178
|
socket._openReqCount = 0;
|
|
@@ -179,21 +192,23 @@ function configureHttp ({httpServer, reject, keepAliveTimeout}) {
|
|
|
179
192
|
});
|
|
180
193
|
}
|
|
181
194
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
195
|
+
/**
|
|
196
|
+
*
|
|
197
|
+
* @param {StartServerOpts} opts
|
|
198
|
+
*/
|
|
199
|
+
async function startServer({httpServer, port, hostname, keepAliveTimeout}) {
|
|
200
|
+
// If the hostname is omitted, the server will accept
|
|
201
|
+
// connections on any IP address
|
|
202
|
+
/** @type {(port: number, hostname?: string) => B<http.Server>} */
|
|
203
|
+
const start = B.promisify(httpServer.listen, {context: httpServer});
|
|
204
|
+
const startPromise = start(port, hostname);
|
|
190
205
|
httpServer.keepAliveTimeout = keepAliveTimeout;
|
|
191
206
|
// headers timeout must be greater than keepAliveTimeout
|
|
192
207
|
httpServer.headersTimeout = keepAliveTimeout + 5 * 1000;
|
|
193
208
|
await startPromise;
|
|
194
209
|
}
|
|
195
210
|
|
|
196
|
-
function normalizeBasePath
|
|
211
|
+
function normalizeBasePath(basePath) {
|
|
197
212
|
if (!_.isString(basePath)) {
|
|
198
213
|
throw new Error(`Invalid path prefix ${basePath}`);
|
|
199
214
|
}
|
|
@@ -211,4 +226,13 @@ function normalizeBasePath (basePath) {
|
|
|
211
226
|
return basePath;
|
|
212
227
|
}
|
|
213
228
|
|
|
214
|
-
export {
|
|
229
|
+
export {server, configureServer, normalizeBasePath};
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Options for {@linkcode startServer}.
|
|
233
|
+
* @typedef StartServerOpts
|
|
234
|
+
* @property {import('http').Server} httpServer - HTTP server instance
|
|
235
|
+
* @property {number} port - Port to run on
|
|
236
|
+
* @property {number} keepAliveTimeout - Keep-alive timeout in milliseconds
|
|
237
|
+
* @property {string} [hostname] - Optional hostname
|
|
238
|
+
*/
|
package/lib/express/static.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
2
|
import log from './logger';
|
|
3
3
|
import _ from 'lodash';
|
|
4
|
-
import {
|
|
4
|
+
import {fs} from '@appium/support';
|
|
5
5
|
import B from 'bluebird';
|
|
6
6
|
|
|
7
|
-
|
|
8
7
|
let STATIC_DIR = path.resolve(__dirname, '..', '..', '..', 'static');
|
|
9
8
|
if (_.isNull(path.resolve(__dirname).match(/build[/\\]lib[/\\]express$/))) {
|
|
10
9
|
// in some contexts we are not in the build directory,
|
|
@@ -12,14 +11,14 @@ if (_.isNull(path.resolve(__dirname).match(/build[/\\]lib[/\\]express$/))) {
|
|
|
12
11
|
STATIC_DIR = path.resolve(__dirname, '..', '..', 'static');
|
|
13
12
|
}
|
|
14
13
|
|
|
15
|
-
async function guineaPigTemplate
|
|
14
|
+
async function guineaPigTemplate(req, res, page) {
|
|
16
15
|
const delay = parseInt(req.params.delay || req.query.delay || 0, 10);
|
|
17
16
|
const throwError = req.params.throwError || req.query.throwError || '';
|
|
18
17
|
let params = {
|
|
19
18
|
throwError,
|
|
20
19
|
serverTime: new Date(),
|
|
21
20
|
userAgent: req.headers['user-agent'],
|
|
22
|
-
comment: 'None'
|
|
21
|
+
comment: 'None',
|
|
23
22
|
};
|
|
24
23
|
if (req.method === 'POST') {
|
|
25
24
|
params.comment = req.body.comments || params.comment;
|
|
@@ -34,7 +33,7 @@ async function guineaPigTemplate (req, res, page) {
|
|
|
34
33
|
res.cookie('guineacookie2', 'cookié2', {path: '/'});
|
|
35
34
|
res.cookie('guineacookie3', 'cant access this', {
|
|
36
35
|
domain: '.blargimarg.com',
|
|
37
|
-
path: '/'
|
|
36
|
+
path: '/',
|
|
38
37
|
});
|
|
39
38
|
res.send((await getTemplate(page))(params));
|
|
40
39
|
}
|
|
@@ -42,36 +41,36 @@ async function guineaPigTemplate (req, res, page) {
|
|
|
42
41
|
/*
|
|
43
42
|
* Dynamic page mapped to /test/guinea-pig
|
|
44
43
|
*/
|
|
45
|
-
async function guineaPig
|
|
44
|
+
async function guineaPig(req, res) {
|
|
46
45
|
return await guineaPigTemplate(req, res, 'guinea-pig.html');
|
|
47
46
|
}
|
|
48
47
|
|
|
49
48
|
/*
|
|
50
49
|
* Dynamic page mapped to /test/guinea-pig-scrollable
|
|
51
50
|
*/
|
|
52
|
-
async function guineaPigScrollable
|
|
51
|
+
async function guineaPigScrollable(req, res) {
|
|
53
52
|
return await guineaPigTemplate(req, res, 'guinea-pig-scrollable.html');
|
|
54
53
|
}
|
|
55
54
|
|
|
56
55
|
/*
|
|
57
56
|
* Dynamic page mapped to /test/guinea-pig-app-banner
|
|
58
57
|
*/
|
|
59
|
-
async function guineaPigAppBanner
|
|
58
|
+
async function guineaPigAppBanner(req, res) {
|
|
60
59
|
return await guineaPigTemplate(req, res, 'guinea-pig-app-banner.html');
|
|
61
60
|
}
|
|
62
61
|
|
|
63
62
|
/*
|
|
64
63
|
* Dynamic page mapped to /welcome
|
|
65
64
|
*/
|
|
66
|
-
async function welcome
|
|
67
|
-
let params = {message:
|
|
65
|
+
async function welcome(req, res) {
|
|
66
|
+
let params = {message: "Let's browse!"};
|
|
68
67
|
log.debug(`Sending welcome response with params: ${JSON.stringify(params)}`);
|
|
69
68
|
res.send((await getTemplate('welcome.html'))(params));
|
|
70
69
|
}
|
|
71
70
|
|
|
72
|
-
async function getTemplate
|
|
71
|
+
async function getTemplate(templateName) {
|
|
73
72
|
let content = await fs.readFile(path.resolve(STATIC_DIR, 'test', templateName));
|
|
74
73
|
return _.template(content.toString());
|
|
75
74
|
}
|
|
76
75
|
|
|
77
|
-
export {
|
|
76
|
+
export {guineaPig, guineaPigScrollable, guineaPigAppBanner, welcome, STATIC_DIR};
|
package/lib/express/websocket.js
CHANGED
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
import _ from 'lodash';
|
|
2
|
-
import {
|
|
2
|
+
import {URL} from 'url';
|
|
3
3
|
import B from 'bluebird';
|
|
4
4
|
|
|
5
5
|
const DEFAULT_WS_PATHNAME_PREFIX = '/ws';
|
|
6
6
|
|
|
7
|
-
|
|
8
7
|
/**
|
|
9
8
|
* Adds websocket handler to express server instance.
|
|
10
9
|
* It is expected this function is called in Express
|
|
11
10
|
* server instance context.
|
|
12
11
|
*
|
|
13
|
-
* @
|
|
12
|
+
* @this {WebSocketServer} - An instance of express HTTP server.
|
|
14
13
|
* @param {string} handlerPathname - Web socket endpoint path starting with
|
|
15
14
|
* a single slash character. It is recommended to always add
|
|
16
15
|
* DEFAULT_WS_PATHNAME_PREFIX to all web socket pathnames.
|
|
@@ -18,15 +17,17 @@ const DEFAULT_WS_PATHNAME_PREFIX = '/ws';
|
|
|
18
17
|
* https://github.com/websockets/ws/pull/885 for more details
|
|
19
18
|
* on how to configure the handler properly.
|
|
20
19
|
*/
|
|
21
|
-
|
|
20
|
+
// eslint-disable-next-line require-await
|
|
21
|
+
async function addWebSocketHandler(handlerPathname, handlerServer) {
|
|
22
22
|
if (_.isUndefined(this.webSocketsMapping)) {
|
|
23
23
|
this.webSocketsMapping = {};
|
|
24
24
|
// https://github.com/websockets/ws/pull/885
|
|
25
25
|
this.on('upgrade', (request, socket, head) => {
|
|
26
26
|
let currentPathname;
|
|
27
27
|
try {
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
// @ts-expect-error
|
|
29
|
+
currentPathname = new URL(request.url).pathname;
|
|
30
|
+
} catch {
|
|
30
31
|
currentPathname = request.url;
|
|
31
32
|
}
|
|
32
33
|
for (const [pathname, wsServer] of _.toPairs(this.webSocketsMapping)) {
|
|
@@ -49,12 +50,13 @@ async function addWebSocketHandler (handlerPathname, handlerServer) { // eslint-
|
|
|
49
50
|
* It is expected this function is called in Express
|
|
50
51
|
* server instance context.
|
|
51
52
|
*
|
|
52
|
-
* @param {?
|
|
53
|
+
* @param {string?} [keysFilter] - Only include pathnames with given
|
|
53
54
|
* `keysFilter` value if set. All pairs will be included by default.
|
|
54
|
-
* @returns {Object} pathnames to websocket server isntances mapping
|
|
55
|
+
* @returns {Promise<Object>} pathnames to websocket server isntances mapping
|
|
55
56
|
* matching the search criteria or an empty object otherwise.
|
|
56
57
|
*/
|
|
57
|
-
|
|
58
|
+
// eslint-disable-next-line require-await
|
|
59
|
+
async function getWebSocketHandlers(keysFilter = null) {
|
|
58
60
|
if (_.isEmpty(this.webSocketsMapping)) {
|
|
59
61
|
return {};
|
|
60
62
|
}
|
|
@@ -75,9 +77,10 @@ async function getWebSocketHandlers (keysFilter = null) { // eslint-disable-line
|
|
|
75
77
|
* server instance context.
|
|
76
78
|
*
|
|
77
79
|
* @param {string} handlerPathname - Websocket endpoint path.
|
|
78
|
-
* @returns {boolean} true if the handlerPathname was found and deleted
|
|
80
|
+
* @returns {Promise<boolean>} true if the handlerPathname was found and deleted
|
|
79
81
|
*/
|
|
80
|
-
|
|
82
|
+
// eslint-disable-next-line require-await
|
|
83
|
+
async function removeWebSocketHandler(handlerPathname) {
|
|
81
84
|
const wsServer = this.webSocketsMapping?.[handlerPathname];
|
|
82
85
|
if (!wsServer) {
|
|
83
86
|
return false;
|
|
@@ -85,7 +88,7 @@ async function removeWebSocketHandler (handlerPathname) { // eslint-disable-line
|
|
|
85
88
|
|
|
86
89
|
try {
|
|
87
90
|
wsServer.close();
|
|
88
|
-
for (const client of
|
|
91
|
+
for (const client of wsServer.clients || []) {
|
|
89
92
|
client.terminate();
|
|
90
93
|
}
|
|
91
94
|
return true;
|
|
@@ -102,9 +105,9 @@ async function removeWebSocketHandler (handlerPathname) { // eslint-disable-line
|
|
|
102
105
|
* It is expected this function is called in Express
|
|
103
106
|
* server instance context.
|
|
104
107
|
*
|
|
105
|
-
* @returns {boolean} true if at least one handler has been deleted
|
|
108
|
+
* @returns {Promise<boolean>} true if at least one handler has been deleted
|
|
106
109
|
*/
|
|
107
|
-
async function removeAllWebSocketHandlers
|
|
110
|
+
async function removeAllWebSocketHandlers() {
|
|
108
111
|
if (_.isEmpty(this.webSocketsMapping)) {
|
|
109
112
|
return false;
|
|
110
113
|
}
|
|
@@ -117,6 +120,13 @@ async function removeAllWebSocketHandlers () {
|
|
|
117
120
|
}
|
|
118
121
|
|
|
119
122
|
export {
|
|
120
|
-
addWebSocketHandler,
|
|
121
|
-
|
|
123
|
+
addWebSocketHandler,
|
|
124
|
+
removeWebSocketHandler,
|
|
125
|
+
removeAllWebSocketHandlers,
|
|
126
|
+
getWebSocketHandlers,
|
|
127
|
+
DEFAULT_WS_PATHNAME_PREFIX,
|
|
122
128
|
};
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* @typedef {import('http').Server & {webSocketsMapping: Record<string,import('ws').Server>}} WebSocketServer
|
|
132
|
+
*/
|
|
@@ -2,13 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
import _ from 'lodash';
|
|
4
4
|
|
|
5
|
-
function isW3cCaps
|
|
5
|
+
function isW3cCaps(caps) {
|
|
6
6
|
if (!_.isPlainObject(caps)) {
|
|
7
7
|
return false;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
const isFirstMatchValid = () =>
|
|
11
|
-
|
|
10
|
+
const isFirstMatchValid = () =>
|
|
11
|
+
_.isArray(caps.firstMatch) &&
|
|
12
|
+
!_.isEmpty(caps.firstMatch) &&
|
|
13
|
+
_.every(caps.firstMatch, _.isPlainObject);
|
|
12
14
|
const isAlwaysMatchValid = () => _.isPlainObject(caps.alwaysMatch);
|
|
13
15
|
if (_.has(caps, 'firstMatch') && _.has(caps, 'alwaysMatch')) {
|
|
14
16
|
return isFirstMatchValid() && isAlwaysMatchValid();
|
|
@@ -29,21 +31,19 @@ function isW3cCaps (caps) {
|
|
|
29
31
|
* @param {AppiumLogger} log
|
|
30
32
|
* @returns {Capabilities}
|
|
31
33
|
*/
|
|
32
|
-
function fixCaps
|
|
34
|
+
function fixCaps(originalCaps, desiredCapConstraints, log) {
|
|
33
35
|
let caps = _.clone(originalCaps);
|
|
34
36
|
|
|
35
37
|
// boolean capabilities can be passed in as strings 'false' and 'true'
|
|
36
38
|
// which we want to translate into boolean values
|
|
37
|
-
let booleanCaps = _.keys(
|
|
38
|
-
_.pickBy(desiredCapConstraints, (k) => k.isBoolean === true),
|
|
39
|
-
);
|
|
39
|
+
let booleanCaps = _.keys(_.pickBy(desiredCapConstraints, (k) => k.isBoolean === true));
|
|
40
40
|
for (let cap of booleanCaps) {
|
|
41
41
|
let value = originalCaps[cap];
|
|
42
42
|
if (_.isString(value)) {
|
|
43
43
|
value = value.toLowerCase();
|
|
44
44
|
if (value === 'true' || value === 'false') {
|
|
45
45
|
log.warn(
|
|
46
|
-
`Capability '${cap}' changed from string to boolean. This may cause unexpected behavior
|
|
46
|
+
`Capability '${cap}' changed from string to boolean. This may cause unexpected behavior`
|
|
47
47
|
);
|
|
48
48
|
caps[cap] = value === 'true';
|
|
49
49
|
}
|
|
@@ -51,9 +51,7 @@ function fixCaps (originalCaps, desiredCapConstraints, log) {
|
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
// int capabilities are often sent in as strings by frameworks
|
|
54
|
-
let intCaps = _.keys(
|
|
55
|
-
_.pickBy(desiredCapConstraints, (k) => k.isNumber === true),
|
|
56
|
-
);
|
|
54
|
+
let intCaps = _.keys(_.pickBy(desiredCapConstraints, (k) => k.isNumber === true));
|
|
57
55
|
for (let cap of intCaps) {
|
|
58
56
|
let value = originalCaps[cap];
|
|
59
57
|
if (_.isString(value)) {
|
|
@@ -63,7 +61,7 @@ function fixCaps (originalCaps, desiredCapConstraints, log) {
|
|
|
63
61
|
newValue = parseFloat(value);
|
|
64
62
|
}
|
|
65
63
|
log.warn(
|
|
66
|
-
`Capability '${cap}' changed from string ('${value}') to integer (${newValue}). This may cause unexpected behavior
|
|
64
|
+
`Capability '${cap}' changed from string ('${value}') to integer (${newValue}). This may cause unexpected behavior`
|
|
67
65
|
);
|
|
68
66
|
caps[cap] = newValue;
|
|
69
67
|
}
|
|
@@ -72,10 +70,7 @@ function fixCaps (originalCaps, desiredCapConstraints, log) {
|
|
|
72
70
|
return caps;
|
|
73
71
|
}
|
|
74
72
|
|
|
75
|
-
export {
|
|
76
|
-
isW3cCaps,
|
|
77
|
-
fixCaps
|
|
78
|
-
};
|
|
73
|
+
export {isW3cCaps, fixCaps};
|
|
79
74
|
|
|
80
75
|
/**
|
|
81
76
|
* @typedef {import('@appium/types').Capabilities} Capabilities
|