@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,30 +1,45 @@
|
|
|
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
25
|
class JWProxy {
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
/** @type {string} */
|
|
27
|
+
scheme;
|
|
28
|
+
/** @type {string} */
|
|
29
|
+
server;
|
|
30
|
+
/** @type {number} */
|
|
31
|
+
port;
|
|
32
|
+
/** @type {string} */
|
|
33
|
+
base;
|
|
34
|
+
/** @type {string} */
|
|
35
|
+
reqBasePath;
|
|
36
|
+
/** @type {string?} */
|
|
37
|
+
sessionId;
|
|
38
|
+
/** @type {number} */
|
|
39
|
+
timeout;
|
|
40
|
+
|
|
41
|
+
constructor(opts = {}) {
|
|
42
|
+
const options = _.defaults(opts, {
|
|
28
43
|
scheme: 'http',
|
|
29
44
|
server: 'localhost',
|
|
30
45
|
port: 4444,
|
|
@@ -33,7 +48,9 @@ class JWProxy {
|
|
|
33
48
|
sessionId: null,
|
|
34
49
|
timeout: DEFAULT_REQUEST_TIMEOUT,
|
|
35
50
|
});
|
|
36
|
-
|
|
51
|
+
options.scheme = options.scheme.toLowerCase();
|
|
52
|
+
Object.assign(this, options);
|
|
53
|
+
|
|
37
54
|
this._activeRequests = [];
|
|
38
55
|
this._downstreamProtocol = null;
|
|
39
56
|
const agentOpts = {
|
|
@@ -47,7 +64,7 @@ class JWProxy {
|
|
|
47
64
|
this._log = opts.log;
|
|
48
65
|
}
|
|
49
66
|
|
|
50
|
-
get log
|
|
67
|
+
get log() {
|
|
51
68
|
return this._log ?? DEFAULT_LOG;
|
|
52
69
|
}
|
|
53
70
|
|
|
@@ -57,10 +74,10 @@ class JWProxy {
|
|
|
57
74
|
* @private - Do not call this method directly,
|
|
58
75
|
* it uses client-specific arguments and responses!
|
|
59
76
|
*
|
|
60
|
-
* @param {AxiosRequestConfig} requestConfig
|
|
61
|
-
* @returns {AxiosResponse}
|
|
77
|
+
* @param {import('axios').AxiosRequestConfig} requestConfig
|
|
78
|
+
* @returns {Promise<import('axios').AxiosResponse>}
|
|
62
79
|
*/
|
|
63
|
-
async request
|
|
80
|
+
async request(requestConfig) {
|
|
64
81
|
const reqPromise = axios(requestConfig);
|
|
65
82
|
this._activeRequests.push(reqPromise);
|
|
66
83
|
try {
|
|
@@ -70,28 +87,28 @@ class JWProxy {
|
|
|
70
87
|
}
|
|
71
88
|
}
|
|
72
89
|
|
|
73
|
-
getActiveRequestsCount
|
|
90
|
+
getActiveRequestsCount() {
|
|
74
91
|
return this._activeRequests.length;
|
|
75
92
|
}
|
|
76
93
|
|
|
77
|
-
cancelActiveRequests
|
|
94
|
+
cancelActiveRequests() {
|
|
78
95
|
this._activeRequests = [];
|
|
79
96
|
}
|
|
80
97
|
|
|
81
|
-
endpointRequiresSessionId
|
|
98
|
+
endpointRequiresSessionId(endpoint) {
|
|
82
99
|
return !_.includes(['/session', '/sessions', '/status'], endpoint);
|
|
83
100
|
}
|
|
84
101
|
|
|
85
|
-
set downstreamProtocol
|
|
102
|
+
set downstreamProtocol(value) {
|
|
86
103
|
this._downstreamProtocol = value;
|
|
87
104
|
this.protocolConverter.downstreamProtocol = value;
|
|
88
105
|
}
|
|
89
106
|
|
|
90
|
-
get downstreamProtocol
|
|
107
|
+
get downstreamProtocol() {
|
|
91
108
|
return this._downstreamProtocol;
|
|
92
109
|
}
|
|
93
110
|
|
|
94
|
-
getUrlForProxy
|
|
111
|
+
getUrlForProxy(url) {
|
|
95
112
|
if (url === '') {
|
|
96
113
|
url = '/';
|
|
97
114
|
}
|
|
@@ -99,12 +116,12 @@ class JWProxy {
|
|
|
99
116
|
const endpointRe = '(/(session|status))';
|
|
100
117
|
let remainingUrl = '';
|
|
101
118
|
if (/^http/.test(url)) {
|
|
102
|
-
const first =
|
|
119
|
+
const first = new RegExp(`(https?://.+)${endpointRe}`).exec(url);
|
|
103
120
|
if (!first) {
|
|
104
121
|
throw new Error('Got a complete url but could not extract JWP endpoint');
|
|
105
122
|
}
|
|
106
123
|
remainingUrl = url.replace(first[1], '');
|
|
107
|
-
} else if (
|
|
124
|
+
} else if (new RegExp('^/').test(url)) {
|
|
108
125
|
remainingUrl = url;
|
|
109
126
|
} else {
|
|
110
127
|
throw new Error(`Did not know what to do with url '${url}'`);
|
|
@@ -112,10 +129,10 @@ class JWProxy {
|
|
|
112
129
|
|
|
113
130
|
const stripPrefixRe = new RegExp('^.*?(/(session|status).*)$');
|
|
114
131
|
if (stripPrefixRe.test(remainingUrl)) {
|
|
115
|
-
remainingUrl = stripPrefixRe.exec(remainingUrl)[1];
|
|
132
|
+
remainingUrl = /** @type {RegExpExecArray} */ (stripPrefixRe.exec(remainingUrl))[1];
|
|
116
133
|
}
|
|
117
134
|
|
|
118
|
-
if (!
|
|
135
|
+
if (!new RegExp(endpointRe).test(remainingUrl)) {
|
|
119
136
|
remainingUrl = `/session/${this.sessionId}${remainingUrl}`;
|
|
120
137
|
}
|
|
121
138
|
|
|
@@ -127,10 +144,20 @@ class JWProxy {
|
|
|
127
144
|
|
|
128
145
|
const sessionBaseRe = new RegExp('^/session/([^/]+)');
|
|
129
146
|
if (sessionBaseRe.test(remainingUrl)) {
|
|
147
|
+
if (this.sessionId === null) {
|
|
148
|
+
throw new ReferenceError(
|
|
149
|
+
`Session ID is not set, but saw a URL path referencing a session (${remainingUrl}). This may be a bug in your client.`
|
|
150
|
+
);
|
|
151
|
+
}
|
|
130
152
|
// we have something like /session/:id/foobar, so we need to replace
|
|
131
153
|
// the session id
|
|
132
154
|
const match = sessionBaseRe.exec(remainingUrl);
|
|
133
|
-
|
|
155
|
+
// TODO: if `requiresSessionId` is `false` and `sessionId` is `null`, this is a bug.
|
|
156
|
+
// are we sure `sessionId` is not `null`?
|
|
157
|
+
remainingUrl = remainingUrl.replace(
|
|
158
|
+
/** @type {RegExpExecArray} */ (match)[1],
|
|
159
|
+
/** @type {string} */ (this.sessionId)
|
|
160
|
+
);
|
|
134
161
|
} else if (requiresSessionId) {
|
|
135
162
|
throw new Error(`Could not find :session section for url: ${remainingUrl}`);
|
|
136
163
|
}
|
|
@@ -139,12 +166,14 @@ class JWProxy {
|
|
|
139
166
|
return proxyBase + remainingUrl;
|
|
140
167
|
}
|
|
141
168
|
|
|
142
|
-
async proxy
|
|
169
|
+
async proxy(url, method, body = null) {
|
|
143
170
|
method = method.toUpperCase();
|
|
144
171
|
const newUrl = this.getUrlForProxy(url);
|
|
145
|
-
const truncateBody = (content) =>
|
|
146
|
-
_.isString(content) ? content : JSON.stringify(content),
|
|
147
|
-
|
|
172
|
+
const truncateBody = (content) =>
|
|
173
|
+
_.truncate(_.isString(content) ? content : JSON.stringify(content), {
|
|
174
|
+
length: MAX_LOG_BODY_LENGTH,
|
|
175
|
+
});
|
|
176
|
+
/** @type {import('axios').AxiosRequestConfig} */
|
|
148
177
|
const reqOpts = {
|
|
149
178
|
url: newUrl,
|
|
150
179
|
method,
|
|
@@ -171,14 +200,16 @@ class JWProxy {
|
|
|
171
200
|
}
|
|
172
201
|
}
|
|
173
202
|
|
|
174
|
-
this.log.debug(
|
|
175
|
-
|
|
203
|
+
this.log.debug(
|
|
204
|
+
`Proxying [${method} ${url || '/'}] to [${method} ${newUrl}] ` +
|
|
205
|
+
(reqOpts.data ? `with body: ${truncateBody(reqOpts.data)}` : 'with no body')
|
|
206
|
+
);
|
|
176
207
|
|
|
177
208
|
const throwProxyError = (error) => {
|
|
178
|
-
const err = new Error(`The request to ${url} has failed`);
|
|
209
|
+
const err = /** @type {ProxyError} */ (new Error(`The request to ${url} has failed`));
|
|
179
210
|
err.response = {
|
|
180
211
|
data: error,
|
|
181
|
-
status: 500
|
|
212
|
+
status: 500,
|
|
182
213
|
};
|
|
183
214
|
throw err;
|
|
184
215
|
};
|
|
@@ -216,9 +247,11 @@ class JWProxy {
|
|
|
216
247
|
if (util.hasValue(e.response)) {
|
|
217
248
|
if (!isResponseLogged) {
|
|
218
249
|
const error = truncateBody(e.response.data);
|
|
219
|
-
this.log.info(
|
|
220
|
-
|
|
221
|
-
|
|
250
|
+
this.log.info(
|
|
251
|
+
util.hasValue(e.response.status)
|
|
252
|
+
? `Got response with status ${e.response.status}: ${error}`
|
|
253
|
+
: `Got response with unknown status: ${error}`
|
|
254
|
+
);
|
|
222
255
|
}
|
|
223
256
|
} else {
|
|
224
257
|
proxyErrorMsg = `Could not proxy command to the remote server. Original error: ${e.message}`;
|
|
@@ -232,7 +265,7 @@ class JWProxy {
|
|
|
232
265
|
}
|
|
233
266
|
}
|
|
234
267
|
|
|
235
|
-
getProtocolFromResBody
|
|
268
|
+
getProtocolFromResBody(resObj) {
|
|
236
269
|
if (_.isInteger(resObj.status)) {
|
|
237
270
|
return MJSONWP;
|
|
238
271
|
}
|
|
@@ -241,14 +274,29 @@ class JWProxy {
|
|
|
241
274
|
}
|
|
242
275
|
}
|
|
243
276
|
|
|
244
|
-
|
|
277
|
+
/**
|
|
278
|
+
*
|
|
279
|
+
* @param {string} url
|
|
280
|
+
* @param {import('@appium/types').HTTPMethod} method
|
|
281
|
+
* @returns {string|undefined}
|
|
282
|
+
*/
|
|
283
|
+
requestToCommandName(url, method) {
|
|
284
|
+
/**
|
|
285
|
+
*
|
|
286
|
+
* @param {RegExp} pattern
|
|
287
|
+
* @returns {string|undefined}
|
|
288
|
+
*/
|
|
245
289
|
const extractCommandName = (pattern) => {
|
|
246
290
|
const pathMatch = pattern.exec(url);
|
|
247
|
-
|
|
291
|
+
if (pathMatch) {
|
|
292
|
+
return routeToCommandName(pathMatch[1], method, this.reqBasePath);
|
|
293
|
+
}
|
|
248
294
|
};
|
|
249
295
|
let commandName = routeToCommandName(url, method, this.reqBasePath);
|
|
250
296
|
if (!commandName && _.includes(url, `${this.reqBasePath}/session/`)) {
|
|
251
|
-
commandName = extractCommandName(
|
|
297
|
+
commandName = extractCommandName(
|
|
298
|
+
new RegExp(`${_.escapeRegExp(this.reqBasePath)}/session/[^/]+(.+)`)
|
|
299
|
+
);
|
|
252
300
|
}
|
|
253
301
|
if (!commandName && _.includes(url, this.reqBasePath)) {
|
|
254
302
|
commandName = extractCommandName(new RegExp(`${_.escapeRegExp(this.reqBasePath)}(/.+)`));
|
|
@@ -256,7 +304,7 @@ class JWProxy {
|
|
|
256
304
|
return commandName;
|
|
257
305
|
}
|
|
258
306
|
|
|
259
|
-
async proxyCommand
|
|
307
|
+
async proxyCommand(url, method, body = null) {
|
|
260
308
|
const commandName = this.requestToCommandName(url, method);
|
|
261
309
|
if (!commandName) {
|
|
262
310
|
return await this.proxy(url, method, body);
|
|
@@ -266,7 +314,7 @@ class JWProxy {
|
|
|
266
314
|
return await this.protocolConverter.convertAndProxy(commandName, url, method, body);
|
|
267
315
|
}
|
|
268
316
|
|
|
269
|
-
async command
|
|
317
|
+
async command(url, method, body = null) {
|
|
270
318
|
let response;
|
|
271
319
|
let resBodyObj;
|
|
272
320
|
try {
|
|
@@ -289,7 +337,10 @@ class JWProxy {
|
|
|
289
337
|
if (_.has(message, 'message')) {
|
|
290
338
|
message = message.message;
|
|
291
339
|
}
|
|
292
|
-
throw errorFromMJSONWPStatusCode(
|
|
340
|
+
throw errorFromMJSONWPStatusCode(
|
|
341
|
+
status,
|
|
342
|
+
_.isEmpty(message) ? getSummaryByCode(status) : message
|
|
343
|
+
);
|
|
293
344
|
}
|
|
294
345
|
} else if (protocol === W3C) {
|
|
295
346
|
// Got response in W3C format
|
|
@@ -297,22 +348,30 @@ class JWProxy {
|
|
|
297
348
|
return resBodyObj.value;
|
|
298
349
|
}
|
|
299
350
|
if (_.isPlainObject(resBodyObj.value) && resBodyObj.value.error) {
|
|
300
|
-
throw errorFromW3CJsonCode(
|
|
351
|
+
throw errorFromW3CJsonCode(
|
|
352
|
+
resBodyObj.value.error,
|
|
353
|
+
resBodyObj.value.message,
|
|
354
|
+
resBodyObj.value.stacktrace
|
|
355
|
+
);
|
|
301
356
|
}
|
|
302
357
|
} else if (response.statusCode === 200) {
|
|
303
358
|
// Unknown protocol. Keeping it because of the backward compatibility
|
|
304
359
|
return resBodyObj;
|
|
305
360
|
}
|
|
306
|
-
throw new errors.UnknownError(
|
|
307
|
-
|
|
361
|
+
throw new errors.UnknownError(
|
|
362
|
+
`Did not know what to do with response code '${response.statusCode}' ` +
|
|
363
|
+
`and response body '${_.truncate(JSON.stringify(resBodyObj), {
|
|
364
|
+
length: 300,
|
|
365
|
+
})}'`
|
|
366
|
+
);
|
|
308
367
|
}
|
|
309
368
|
|
|
310
|
-
getSessionIdFromUrl
|
|
369
|
+
getSessionIdFromUrl(url) {
|
|
311
370
|
const match = url.match(/\/session\/([^/]+)/);
|
|
312
371
|
return match ? match[1] : null;
|
|
313
372
|
}
|
|
314
373
|
|
|
315
|
-
async proxyReqRes
|
|
374
|
+
async proxyReqRes(req, res) {
|
|
316
375
|
// ! this method must not throw any exceptions
|
|
317
376
|
// ! make sure to call res.send before return
|
|
318
377
|
let statusCode;
|
|
@@ -331,7 +390,7 @@ class JWProxy {
|
|
|
331
390
|
if (!_.isPlainObject(resBodyObj)) {
|
|
332
391
|
const error = new errors.UnknownError(
|
|
333
392
|
`The downstream server response with the status code ${statusCode} is not a valid JSON object: ` +
|
|
334
|
-
|
|
393
|
+
_.truncate(`${resBodyObj}`, {length: 300})
|
|
335
394
|
);
|
|
336
395
|
[statusCode, resBodyObj] = getResponseForW3CError(error);
|
|
337
396
|
}
|
|
@@ -354,5 +413,9 @@ class JWProxy {
|
|
|
354
413
|
}
|
|
355
414
|
}
|
|
356
415
|
|
|
357
|
-
export {
|
|
416
|
+
export {JWProxy};
|
|
358
417
|
export default JWProxy;
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* @typedef {Error & {response: {data: import('type-fest').JsonObject, status: import('http-status-codes').StatusCodes}}} ProxyError
|
|
421
|
+
*/
|
|
@@ -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};
|