@appium/base-driver 8.5.3 → 8.5.6
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 +23 -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 +132 -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,30 +1,61 @@
|
|
|
1
1
|
import _ from 'lodash';
|
|
2
|
-
import {
|
|
2
|
+
import {logger, util} from '@appium/support';
|
|
3
3
|
import axios from 'axios';
|
|
4
|
-
import {
|
|
4
|
+
import {getSummaryByCode} from '../jsonwp-status/status';
|
|
5
5
|
import {
|
|
6
|
-
errors,
|
|
6
|
+
errors,
|
|
7
|
+
isErrorType,
|
|
8
|
+
errorFromMJSONWPStatusCode,
|
|
9
|
+
errorFromW3CJsonCode,
|
|
7
10
|
getResponseForW3CError,
|
|
8
11
|
} from '../protocol/errors';
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
12
|
+
import {routeToCommandName} from '../protocol';
|
|
13
|
+
import {MAX_LOG_BODY_LENGTH, DEFAULT_BASE_PATH, PROTOCOLS} from '../constants';
|
|
11
14
|
import ProtocolConverter from './protocol-converter';
|
|
12
|
-
import {
|
|
15
|
+
import {formatResponseValue, formatStatus} from '../protocol/helpers';
|
|
13
16
|
import http from 'http';
|
|
14
17
|
import https from 'https';
|
|
15
18
|
|
|
16
19
|
const DEFAULT_LOG = logger.getLogger('WD Proxy');
|
|
17
20
|
const DEFAULT_REQUEST_TIMEOUT = 240000;
|
|
18
|
-
const COMPACT_ERROR_PATTERNS = [
|
|
19
|
-
/\bECONNREFUSED\b/,
|
|
20
|
-
/socket hang up/,
|
|
21
|
-
];
|
|
21
|
+
const COMPACT_ERROR_PATTERNS = [/\bECONNREFUSED\b/, /socket hang up/];
|
|
22
22
|
|
|
23
23
|
const {MJSONWP, W3C} = PROTOCOLS;
|
|
24
24
|
|
|
25
|
+
const ALLOWED_OPTS = [
|
|
26
|
+
'scheme',
|
|
27
|
+
'server',
|
|
28
|
+
'port',
|
|
29
|
+
'base',
|
|
30
|
+
'reqBasePath',
|
|
31
|
+
'sessionId',
|
|
32
|
+
'timeout',
|
|
33
|
+
'log',
|
|
34
|
+
'keepAlive',
|
|
35
|
+
];
|
|
36
|
+
|
|
25
37
|
class JWProxy {
|
|
26
|
-
|
|
27
|
-
|
|
38
|
+
/** @type {string} */
|
|
39
|
+
scheme;
|
|
40
|
+
/** @type {string} */
|
|
41
|
+
server;
|
|
42
|
+
/** @type {number} */
|
|
43
|
+
port;
|
|
44
|
+
/** @type {string} */
|
|
45
|
+
base;
|
|
46
|
+
/** @type {string} */
|
|
47
|
+
reqBasePath;
|
|
48
|
+
/** @type {string?} */
|
|
49
|
+
sessionId;
|
|
50
|
+
/** @type {number} */
|
|
51
|
+
timeout;
|
|
52
|
+
|
|
53
|
+
constructor(opts = {}) {
|
|
54
|
+
opts = _.pick(opts, ALLOWED_OPTS);
|
|
55
|
+
|
|
56
|
+
// omit 'log' in the defaults assignment here because 'log' is a getter and we are going to set
|
|
57
|
+
// it to this._log (which lies behind the getter) further down
|
|
58
|
+
const options = _.defaults(_.omit(opts, 'log'), {
|
|
28
59
|
scheme: 'http',
|
|
29
60
|
server: 'localhost',
|
|
30
61
|
port: 4444,
|
|
@@ -33,7 +64,9 @@ class JWProxy {
|
|
|
33
64
|
sessionId: null,
|
|
34
65
|
timeout: DEFAULT_REQUEST_TIMEOUT,
|
|
35
66
|
});
|
|
36
|
-
|
|
67
|
+
options.scheme = options.scheme.toLowerCase();
|
|
68
|
+
Object.assign(this, options);
|
|
69
|
+
|
|
37
70
|
this._activeRequests = [];
|
|
38
71
|
this._downstreamProtocol = null;
|
|
39
72
|
const agentOpts = {
|
|
@@ -47,7 +80,7 @@ class JWProxy {
|
|
|
47
80
|
this._log = opts.log;
|
|
48
81
|
}
|
|
49
82
|
|
|
50
|
-
get log
|
|
83
|
+
get log() {
|
|
51
84
|
return this._log ?? DEFAULT_LOG;
|
|
52
85
|
}
|
|
53
86
|
|
|
@@ -57,10 +90,10 @@ class JWProxy {
|
|
|
57
90
|
* @private - Do not call this method directly,
|
|
58
91
|
* it uses client-specific arguments and responses!
|
|
59
92
|
*
|
|
60
|
-
* @param {AxiosRequestConfig} requestConfig
|
|
61
|
-
* @returns {AxiosResponse}
|
|
93
|
+
* @param {import('axios').AxiosRequestConfig} requestConfig
|
|
94
|
+
* @returns {Promise<import('axios').AxiosResponse>}
|
|
62
95
|
*/
|
|
63
|
-
async request
|
|
96
|
+
async request(requestConfig) {
|
|
64
97
|
const reqPromise = axios(requestConfig);
|
|
65
98
|
this._activeRequests.push(reqPromise);
|
|
66
99
|
try {
|
|
@@ -70,28 +103,28 @@ class JWProxy {
|
|
|
70
103
|
}
|
|
71
104
|
}
|
|
72
105
|
|
|
73
|
-
getActiveRequestsCount
|
|
106
|
+
getActiveRequestsCount() {
|
|
74
107
|
return this._activeRequests.length;
|
|
75
108
|
}
|
|
76
109
|
|
|
77
|
-
cancelActiveRequests
|
|
110
|
+
cancelActiveRequests() {
|
|
78
111
|
this._activeRequests = [];
|
|
79
112
|
}
|
|
80
113
|
|
|
81
|
-
endpointRequiresSessionId
|
|
114
|
+
endpointRequiresSessionId(endpoint) {
|
|
82
115
|
return !_.includes(['/session', '/sessions', '/status'], endpoint);
|
|
83
116
|
}
|
|
84
117
|
|
|
85
|
-
set downstreamProtocol
|
|
118
|
+
set downstreamProtocol(value) {
|
|
86
119
|
this._downstreamProtocol = value;
|
|
87
120
|
this.protocolConverter.downstreamProtocol = value;
|
|
88
121
|
}
|
|
89
122
|
|
|
90
|
-
get downstreamProtocol
|
|
123
|
+
get downstreamProtocol() {
|
|
91
124
|
return this._downstreamProtocol;
|
|
92
125
|
}
|
|
93
126
|
|
|
94
|
-
getUrlForProxy
|
|
127
|
+
getUrlForProxy(url) {
|
|
95
128
|
if (url === '') {
|
|
96
129
|
url = '/';
|
|
97
130
|
}
|
|
@@ -99,12 +132,12 @@ class JWProxy {
|
|
|
99
132
|
const endpointRe = '(/(session|status))';
|
|
100
133
|
let remainingUrl = '';
|
|
101
134
|
if (/^http/.test(url)) {
|
|
102
|
-
const first =
|
|
135
|
+
const first = new RegExp(`(https?://.+)${endpointRe}`).exec(url);
|
|
103
136
|
if (!first) {
|
|
104
137
|
throw new Error('Got a complete url but could not extract JWP endpoint');
|
|
105
138
|
}
|
|
106
139
|
remainingUrl = url.replace(first[1], '');
|
|
107
|
-
} else if (
|
|
140
|
+
} else if (new RegExp('^/').test(url)) {
|
|
108
141
|
remainingUrl = url;
|
|
109
142
|
} else {
|
|
110
143
|
throw new Error(`Did not know what to do with url '${url}'`);
|
|
@@ -112,10 +145,10 @@ class JWProxy {
|
|
|
112
145
|
|
|
113
146
|
const stripPrefixRe = new RegExp('^.*?(/(session|status).*)$');
|
|
114
147
|
if (stripPrefixRe.test(remainingUrl)) {
|
|
115
|
-
remainingUrl = stripPrefixRe.exec(remainingUrl)[1];
|
|
148
|
+
remainingUrl = /** @type {RegExpExecArray} */ (stripPrefixRe.exec(remainingUrl))[1];
|
|
116
149
|
}
|
|
117
150
|
|
|
118
|
-
if (!
|
|
151
|
+
if (!new RegExp(endpointRe).test(remainingUrl)) {
|
|
119
152
|
remainingUrl = `/session/${this.sessionId}${remainingUrl}`;
|
|
120
153
|
}
|
|
121
154
|
|
|
@@ -127,10 +160,20 @@ class JWProxy {
|
|
|
127
160
|
|
|
128
161
|
const sessionBaseRe = new RegExp('^/session/([^/]+)');
|
|
129
162
|
if (sessionBaseRe.test(remainingUrl)) {
|
|
163
|
+
if (this.sessionId === null) {
|
|
164
|
+
throw new ReferenceError(
|
|
165
|
+
`Session ID is not set, but saw a URL path referencing a session (${remainingUrl}). This may be a bug in your client.`
|
|
166
|
+
);
|
|
167
|
+
}
|
|
130
168
|
// we have something like /session/:id/foobar, so we need to replace
|
|
131
169
|
// the session id
|
|
132
170
|
const match = sessionBaseRe.exec(remainingUrl);
|
|
133
|
-
|
|
171
|
+
// TODO: if `requiresSessionId` is `false` and `sessionId` is `null`, this is a bug.
|
|
172
|
+
// are we sure `sessionId` is not `null`?
|
|
173
|
+
remainingUrl = remainingUrl.replace(
|
|
174
|
+
/** @type {RegExpExecArray} */ (match)[1],
|
|
175
|
+
/** @type {string} */ (this.sessionId)
|
|
176
|
+
);
|
|
134
177
|
} else if (requiresSessionId) {
|
|
135
178
|
throw new Error(`Could not find :session section for url: ${remainingUrl}`);
|
|
136
179
|
}
|
|
@@ -139,12 +182,14 @@ class JWProxy {
|
|
|
139
182
|
return proxyBase + remainingUrl;
|
|
140
183
|
}
|
|
141
184
|
|
|
142
|
-
async proxy
|
|
185
|
+
async proxy(url, method, body = null) {
|
|
143
186
|
method = method.toUpperCase();
|
|
144
187
|
const newUrl = this.getUrlForProxy(url);
|
|
145
|
-
const truncateBody = (content) =>
|
|
146
|
-
_.isString(content) ? content : JSON.stringify(content),
|
|
147
|
-
|
|
188
|
+
const truncateBody = (content) =>
|
|
189
|
+
_.truncate(_.isString(content) ? content : JSON.stringify(content), {
|
|
190
|
+
length: MAX_LOG_BODY_LENGTH,
|
|
191
|
+
});
|
|
192
|
+
/** @type {import('axios').AxiosRequestConfig} */
|
|
148
193
|
const reqOpts = {
|
|
149
194
|
url: newUrl,
|
|
150
195
|
method,
|
|
@@ -171,14 +216,16 @@ class JWProxy {
|
|
|
171
216
|
}
|
|
172
217
|
}
|
|
173
218
|
|
|
174
|
-
this.log.debug(
|
|
175
|
-
|
|
219
|
+
this.log.debug(
|
|
220
|
+
`Proxying [${method} ${url || '/'}] to [${method} ${newUrl}] ` +
|
|
221
|
+
(reqOpts.data ? `with body: ${truncateBody(reqOpts.data)}` : 'with no body')
|
|
222
|
+
);
|
|
176
223
|
|
|
177
224
|
const throwProxyError = (error) => {
|
|
178
|
-
const err = new Error(`The request to ${url} has failed`);
|
|
225
|
+
const err = /** @type {ProxyError} */ (new Error(`The request to ${url} has failed`));
|
|
179
226
|
err.response = {
|
|
180
227
|
data: error,
|
|
181
|
-
status: 500
|
|
228
|
+
status: 500,
|
|
182
229
|
};
|
|
183
230
|
throw err;
|
|
184
231
|
};
|
|
@@ -216,9 +263,11 @@ class JWProxy {
|
|
|
216
263
|
if (util.hasValue(e.response)) {
|
|
217
264
|
if (!isResponseLogged) {
|
|
218
265
|
const error = truncateBody(e.response.data);
|
|
219
|
-
this.log.info(
|
|
220
|
-
|
|
221
|
-
|
|
266
|
+
this.log.info(
|
|
267
|
+
util.hasValue(e.response.status)
|
|
268
|
+
? `Got response with status ${e.response.status}: ${error}`
|
|
269
|
+
: `Got response with unknown status: ${error}`
|
|
270
|
+
);
|
|
222
271
|
}
|
|
223
272
|
} else {
|
|
224
273
|
proxyErrorMsg = `Could not proxy command to the remote server. Original error: ${e.message}`;
|
|
@@ -232,7 +281,7 @@ class JWProxy {
|
|
|
232
281
|
}
|
|
233
282
|
}
|
|
234
283
|
|
|
235
|
-
getProtocolFromResBody
|
|
284
|
+
getProtocolFromResBody(resObj) {
|
|
236
285
|
if (_.isInteger(resObj.status)) {
|
|
237
286
|
return MJSONWP;
|
|
238
287
|
}
|
|
@@ -241,14 +290,29 @@ class JWProxy {
|
|
|
241
290
|
}
|
|
242
291
|
}
|
|
243
292
|
|
|
244
|
-
|
|
293
|
+
/**
|
|
294
|
+
*
|
|
295
|
+
* @param {string} url
|
|
296
|
+
* @param {import('@appium/types').HTTPMethod} method
|
|
297
|
+
* @returns {string|undefined}
|
|
298
|
+
*/
|
|
299
|
+
requestToCommandName(url, method) {
|
|
300
|
+
/**
|
|
301
|
+
*
|
|
302
|
+
* @param {RegExp} pattern
|
|
303
|
+
* @returns {string|undefined}
|
|
304
|
+
*/
|
|
245
305
|
const extractCommandName = (pattern) => {
|
|
246
306
|
const pathMatch = pattern.exec(url);
|
|
247
|
-
|
|
307
|
+
if (pathMatch) {
|
|
308
|
+
return routeToCommandName(pathMatch[1], method, this.reqBasePath);
|
|
309
|
+
}
|
|
248
310
|
};
|
|
249
311
|
let commandName = routeToCommandName(url, method, this.reqBasePath);
|
|
250
312
|
if (!commandName && _.includes(url, `${this.reqBasePath}/session/`)) {
|
|
251
|
-
commandName = extractCommandName(
|
|
313
|
+
commandName = extractCommandName(
|
|
314
|
+
new RegExp(`${_.escapeRegExp(this.reqBasePath)}/session/[^/]+(.+)`)
|
|
315
|
+
);
|
|
252
316
|
}
|
|
253
317
|
if (!commandName && _.includes(url, this.reqBasePath)) {
|
|
254
318
|
commandName = extractCommandName(new RegExp(`${_.escapeRegExp(this.reqBasePath)}(/.+)`));
|
|
@@ -256,7 +320,7 @@ class JWProxy {
|
|
|
256
320
|
return commandName;
|
|
257
321
|
}
|
|
258
322
|
|
|
259
|
-
async proxyCommand
|
|
323
|
+
async proxyCommand(url, method, body = null) {
|
|
260
324
|
const commandName = this.requestToCommandName(url, method);
|
|
261
325
|
if (!commandName) {
|
|
262
326
|
return await this.proxy(url, method, body);
|
|
@@ -266,7 +330,7 @@ class JWProxy {
|
|
|
266
330
|
return await this.protocolConverter.convertAndProxy(commandName, url, method, body);
|
|
267
331
|
}
|
|
268
332
|
|
|
269
|
-
async command
|
|
333
|
+
async command(url, method, body = null) {
|
|
270
334
|
let response;
|
|
271
335
|
let resBodyObj;
|
|
272
336
|
try {
|
|
@@ -289,7 +353,10 @@ class JWProxy {
|
|
|
289
353
|
if (_.has(message, 'message')) {
|
|
290
354
|
message = message.message;
|
|
291
355
|
}
|
|
292
|
-
throw errorFromMJSONWPStatusCode(
|
|
356
|
+
throw errorFromMJSONWPStatusCode(
|
|
357
|
+
status,
|
|
358
|
+
_.isEmpty(message) ? getSummaryByCode(status) : message
|
|
359
|
+
);
|
|
293
360
|
}
|
|
294
361
|
} else if (protocol === W3C) {
|
|
295
362
|
// Got response in W3C format
|
|
@@ -297,22 +364,30 @@ class JWProxy {
|
|
|
297
364
|
return resBodyObj.value;
|
|
298
365
|
}
|
|
299
366
|
if (_.isPlainObject(resBodyObj.value) && resBodyObj.value.error) {
|
|
300
|
-
throw errorFromW3CJsonCode(
|
|
367
|
+
throw errorFromW3CJsonCode(
|
|
368
|
+
resBodyObj.value.error,
|
|
369
|
+
resBodyObj.value.message,
|
|
370
|
+
resBodyObj.value.stacktrace
|
|
371
|
+
);
|
|
301
372
|
}
|
|
302
373
|
} else if (response.statusCode === 200) {
|
|
303
374
|
// Unknown protocol. Keeping it because of the backward compatibility
|
|
304
375
|
return resBodyObj;
|
|
305
376
|
}
|
|
306
|
-
throw new errors.UnknownError(
|
|
307
|
-
|
|
377
|
+
throw new errors.UnknownError(
|
|
378
|
+
`Did not know what to do with response code '${response.statusCode}' ` +
|
|
379
|
+
`and response body '${_.truncate(JSON.stringify(resBodyObj), {
|
|
380
|
+
length: 300,
|
|
381
|
+
})}'`
|
|
382
|
+
);
|
|
308
383
|
}
|
|
309
384
|
|
|
310
|
-
getSessionIdFromUrl
|
|
385
|
+
getSessionIdFromUrl(url) {
|
|
311
386
|
const match = url.match(/\/session\/([^/]+)/);
|
|
312
387
|
return match ? match[1] : null;
|
|
313
388
|
}
|
|
314
389
|
|
|
315
|
-
async proxyReqRes
|
|
390
|
+
async proxyReqRes(req, res) {
|
|
316
391
|
// ! this method must not throw any exceptions
|
|
317
392
|
// ! make sure to call res.send before return
|
|
318
393
|
let statusCode;
|
|
@@ -331,7 +406,7 @@ class JWProxy {
|
|
|
331
406
|
if (!_.isPlainObject(resBodyObj)) {
|
|
332
407
|
const error = new errors.UnknownError(
|
|
333
408
|
`The downstream server response with the status code ${statusCode} is not a valid JSON object: ` +
|
|
334
|
-
|
|
409
|
+
_.truncate(`${resBodyObj}`, {length: 300})
|
|
335
410
|
);
|
|
336
411
|
[statusCode, resBodyObj] = getResponseForW3CError(error);
|
|
337
412
|
}
|
|
@@ -354,5 +429,9 @@ class JWProxy {
|
|
|
354
429
|
}
|
|
355
430
|
}
|
|
356
431
|
|
|
357
|
-
export {
|
|
432
|
+
export {JWProxy};
|
|
358
433
|
export default JWProxy;
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* @typedef {Error & {response: {data: import('type-fest').JsonObject, status: import('http-status-codes').StatusCodes}}} ProxyError
|
|
437
|
+
*/
|
|
@@ -3,111 +3,118 @@ import _ from 'lodash';
|
|
|
3
3
|
const codes = {
|
|
4
4
|
Success: {
|
|
5
5
|
code: 0,
|
|
6
|
-
summary: 'The command executed successfully.'
|
|
6
|
+
summary: 'The command executed successfully.',
|
|
7
7
|
},
|
|
8
8
|
NoSuchDriver: {
|
|
9
9
|
code: 6,
|
|
10
|
-
summary: 'A session is either terminated or not started'
|
|
10
|
+
summary: 'A session is either terminated or not started',
|
|
11
11
|
},
|
|
12
12
|
NoSuchElement: {
|
|
13
13
|
code: 7,
|
|
14
|
-
summary: 'An element could not be located on the page using the given search parameters.'
|
|
14
|
+
summary: 'An element could not be located on the page using the given search parameters.',
|
|
15
15
|
},
|
|
16
16
|
NoSuchFrame: {
|
|
17
17
|
code: 8,
|
|
18
|
-
summary:
|
|
18
|
+
summary:
|
|
19
|
+
'A request to switch to a frame could not be satisfied because the frame could not be found.',
|
|
19
20
|
},
|
|
20
21
|
UnknownCommand: {
|
|
21
22
|
code: 9,
|
|
22
|
-
summary:
|
|
23
|
+
summary:
|
|
24
|
+
'The requested resource could not be found, or a request was received using an HTTP method that is not supported by the mapped resource.',
|
|
23
25
|
},
|
|
24
26
|
StaleElementReference: {
|
|
25
27
|
code: 10,
|
|
26
|
-
summary:
|
|
28
|
+
summary:
|
|
29
|
+
'An element command failed because the referenced element is no longer attached to the DOM.',
|
|
27
30
|
},
|
|
28
31
|
ElementNotVisible: {
|
|
29
32
|
code: 11,
|
|
30
|
-
summary:
|
|
33
|
+
summary:
|
|
34
|
+
'An element command could not be completed because the element is not visible on the page.',
|
|
31
35
|
},
|
|
32
36
|
InvalidElementState: {
|
|
33
37
|
code: 12,
|
|
34
|
-
summary:
|
|
38
|
+
summary:
|
|
39
|
+
'An element command could not be completed because the element is in an invalid state (e.g. attempting to click a disabled element).',
|
|
35
40
|
},
|
|
36
41
|
UnknownError: {
|
|
37
42
|
code: 13,
|
|
38
|
-
summary: 'An unknown server-side error occurred while processing the command.'
|
|
43
|
+
summary: 'An unknown server-side error occurred while processing the command.',
|
|
39
44
|
},
|
|
40
45
|
ElementIsNotSelectable: {
|
|
41
46
|
code: 15,
|
|
42
|
-
summary: 'An attempt was made to select an element that cannot be selected.'
|
|
47
|
+
summary: 'An attempt was made to select an element that cannot be selected.',
|
|
43
48
|
},
|
|
44
49
|
JavaScriptError: {
|
|
45
50
|
code: 17,
|
|
46
|
-
summary: 'An error occurred while executing user supplied JavaScript.'
|
|
51
|
+
summary: 'An error occurred while executing user supplied JavaScript.',
|
|
47
52
|
},
|
|
48
53
|
XPathLookupError: {
|
|
49
54
|
code: 19,
|
|
50
|
-
summary: 'An error occurred while searching for an element by XPath.'
|
|
55
|
+
summary: 'An error occurred while searching for an element by XPath.',
|
|
51
56
|
},
|
|
52
57
|
Timeout: {
|
|
53
58
|
code: 21,
|
|
54
|
-
summary: 'An operation did not complete before its timeout expired.'
|
|
59
|
+
summary: 'An operation did not complete before its timeout expired.',
|
|
55
60
|
},
|
|
56
61
|
NoSuchWindow: {
|
|
57
62
|
code: 23,
|
|
58
|
-
summary:
|
|
63
|
+
summary:
|
|
64
|
+
'A request to switch to a different window could not be satisfied because the window could not be found.',
|
|
59
65
|
},
|
|
60
66
|
InvalidCookieDomain: {
|
|
61
67
|
code: 24,
|
|
62
|
-
summary:
|
|
68
|
+
summary:
|
|
69
|
+
'An illegal attempt was made to set a cookie under a different domain than the current page.',
|
|
63
70
|
},
|
|
64
71
|
UnableToSetCookie: {
|
|
65
72
|
code: 25,
|
|
66
|
-
summary:
|
|
73
|
+
summary: "A request to set a cookie's value could not be satisfied.",
|
|
67
74
|
},
|
|
68
75
|
UnexpectedAlertOpen: {
|
|
69
76
|
code: 26,
|
|
70
|
-
summary: 'A modal dialog was open, blocking this operation'
|
|
77
|
+
summary: 'A modal dialog was open, blocking this operation',
|
|
71
78
|
},
|
|
72
79
|
NoAlertOpenError: {
|
|
73
80
|
code: 27,
|
|
74
|
-
summary: 'An attempt was made to operate on a modal dialog when one was not open.'
|
|
81
|
+
summary: 'An attempt was made to operate on a modal dialog when one was not open.',
|
|
75
82
|
},
|
|
76
83
|
ScriptTimeout: {
|
|
77
84
|
code: 28,
|
|
78
|
-
summary: 'A script did not complete before its timeout expired.'
|
|
85
|
+
summary: 'A script did not complete before its timeout expired.',
|
|
79
86
|
},
|
|
80
87
|
InvalidElementCoordinates: {
|
|
81
88
|
code: 29,
|
|
82
|
-
summary: 'The coordinates provided to an interactions operation are invalid.'
|
|
89
|
+
summary: 'The coordinates provided to an interactions operation are invalid.',
|
|
83
90
|
},
|
|
84
91
|
IMENotAvailable: {
|
|
85
92
|
code: 30,
|
|
86
|
-
summary: 'IME was not available.'
|
|
93
|
+
summary: 'IME was not available.',
|
|
87
94
|
},
|
|
88
95
|
IMEEngineActivationFailed: {
|
|
89
96
|
code: 31,
|
|
90
|
-
summary: 'An IME engine could not be started.'
|
|
97
|
+
summary: 'An IME engine could not be started.',
|
|
91
98
|
},
|
|
92
99
|
InvalidSelector: {
|
|
93
100
|
code: 32,
|
|
94
|
-
summary: 'Argument was an invalid selector (e.g. XPath/CSS).'
|
|
101
|
+
summary: 'Argument was an invalid selector (e.g. XPath/CSS).',
|
|
95
102
|
},
|
|
96
103
|
SessionNotCreatedException: {
|
|
97
104
|
code: 33,
|
|
98
|
-
summary: 'A new session could not be created.'
|
|
105
|
+
summary: 'A new session could not be created.',
|
|
99
106
|
},
|
|
100
107
|
MoveTargetOutOfBounds: {
|
|
101
108
|
code: 34,
|
|
102
|
-
summary: 'Target provided for a move action is out of bounds.'
|
|
109
|
+
summary: 'Target provided for a move action is out of bounds.',
|
|
103
110
|
},
|
|
104
111
|
NoSuchContext: {
|
|
105
112
|
code: 35,
|
|
106
|
-
summary: 'No such context found.'
|
|
107
|
-
}
|
|
113
|
+
summary: 'No such context found.',
|
|
114
|
+
},
|
|
108
115
|
};
|
|
109
116
|
|
|
110
|
-
function getSummaryByCode
|
|
117
|
+
function getSummaryByCode(code) {
|
|
111
118
|
code = parseInt(code, 10);
|
|
112
119
|
for (let obj of _.values(codes)) {
|
|
113
120
|
if (!_.isUndefined(obj.code) && obj.code === code) {
|
|
@@ -118,4 +125,4 @@ function getSummaryByCode (code) {
|
|
|
118
125
|
}
|
|
119
126
|
|
|
120
127
|
export default codes;
|
|
121
|
-
export {
|
|
128
|
+
export {codes, getSummaryByCode};
|