webdriver 7.16.13 → 7.17.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/package.json +6 -6
- package/build/command.d.ts +0 -5
- package/build/command.d.ts.map +0 -1
- package/build/command.js +0 -88
- package/build/constants.d.ts +0 -8
- package/build/constants.d.ts.map +0 -1
- package/build/constants.js +0 -157
- package/build/index.d.ts +0 -27
- package/build/index.d.ts.map +0 -1
- package/build/index.js +0 -111
- package/build/request/browser.d.ts +0 -14
- package/build/request/browser.d.ts.map +0 -1
- package/build/request/browser.js +0 -48
- package/build/request/factory.d.ts +0 -10
- package/build/request/factory.d.ts.map +0 -1
- package/build/request/factory.js +0 -29
- package/build/request/index.d.ts +0 -44
- package/build/request/index.d.ts.map +0 -1
- package/build/request/index.js +0 -198
- package/build/request/node.d.ts +0 -9
- package/build/request/node.d.ts.map +0 -1
- package/build/request/node.js +0 -50
- package/build/types.d.ts +0 -36
- package/build/types.d.ts.map +0 -1
- package/build/types.js +0 -2
- package/build/utils.d.ts +0 -72
- package/build/utils.d.ts.map +0 -1
- package/build/utils.js +0 -318
package/build/utils.js
DELETED
|
@@ -1,318 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.getTimeoutError = exports.getSessionError = exports.getEnvironmentVars = exports.CustomRequestError = exports.getErrorFromResponseBody = exports.getPrototype = exports.isSuccessfulResponse = exports.startWebDriverSession = void 0;
|
|
7
|
-
const lodash_merge_1 = __importDefault(require("lodash.merge"));
|
|
8
|
-
const logger_1 = __importDefault(require("@wdio/logger"));
|
|
9
|
-
const protocols_1 = require("@wdio/protocols");
|
|
10
|
-
const factory_1 = __importDefault(require("./request/factory"));
|
|
11
|
-
const command_1 = __importDefault(require("./command"));
|
|
12
|
-
const utils_1 = require("@wdio/utils");
|
|
13
|
-
const constants_1 = require("./constants");
|
|
14
|
-
const log = (0, logger_1.default)('webdriver');
|
|
15
|
-
const BROWSER_DRIVER_ERRORS = [
|
|
16
|
-
'unknown command: wd/hub/session',
|
|
17
|
-
'HTTP method not allowed',
|
|
18
|
-
"'POST /wd/hub/session' was not found.",
|
|
19
|
-
'Command not found' // iedriver
|
|
20
|
-
];
|
|
21
|
-
/**
|
|
22
|
-
* start browser session with WebDriver protocol
|
|
23
|
-
*/
|
|
24
|
-
async function startWebDriverSession(params) {
|
|
25
|
-
/**
|
|
26
|
-
* validate capabilities to check if there are no obvious mix between
|
|
27
|
-
* JSONWireProtocol and WebDriver protoocol, e.g.
|
|
28
|
-
*/
|
|
29
|
-
if (params.capabilities) {
|
|
30
|
-
const extensionCaps = Object.keys(params.capabilities).filter((cap) => cap.includes(':'));
|
|
31
|
-
const invalidWebDriverCaps = Object.keys(params.capabilities)
|
|
32
|
-
.filter((cap) => !constants_1.VALID_CAPS.includes(cap) && !cap.includes(':'));
|
|
33
|
-
/**
|
|
34
|
-
* if there are vendor extensions, e.g. sauce:options or appium:app
|
|
35
|
-
* used (only WebDriver compatible) and caps that aren't defined
|
|
36
|
-
* in the WebDriver spec
|
|
37
|
-
*/
|
|
38
|
-
if (extensionCaps.length && invalidWebDriverCaps.length) {
|
|
39
|
-
throw new Error(`Invalid or unsupported WebDriver capabilities found ("${invalidWebDriverCaps.join('", "')}"). ` +
|
|
40
|
-
'Ensure to only use valid W3C WebDriver capabilities (see https://w3c.github.io/webdriver/#capabilities).' +
|
|
41
|
-
'If you run your tests on a remote vendor, like Sauce Labs or BrowserStack, make sure that you put them ' +
|
|
42
|
-
'into vendor specific capabilities, e.g. "sauce:options" or "bstack:options". Please reach out to ' +
|
|
43
|
-
'to your vendor support team if you have further questions.');
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
/**
|
|
47
|
-
* the user could have passed in either w3c style or jsonwp style caps
|
|
48
|
-
* and we want to pass both styles to the server, which means we need
|
|
49
|
-
* to check what style the user sent in so we know how to construct the
|
|
50
|
-
* object for the other style
|
|
51
|
-
*/
|
|
52
|
-
const [w3cCaps, jsonwpCaps] = params.capabilities && params.capabilities.alwaysMatch
|
|
53
|
-
/**
|
|
54
|
-
* in case W3C compliant capabilities are provided
|
|
55
|
-
*/
|
|
56
|
-
? [params.capabilities, params.capabilities.alwaysMatch]
|
|
57
|
-
/**
|
|
58
|
-
* otherwise assume they passed in jsonwp-style caps (flat object)
|
|
59
|
-
*/
|
|
60
|
-
: [{ alwaysMatch: params.capabilities, firstMatch: [{}] }, params.capabilities];
|
|
61
|
-
const sessionRequest = factory_1.default.getInstance('POST', '/session', {
|
|
62
|
-
capabilities: w3cCaps,
|
|
63
|
-
desiredCapabilities: jsonwpCaps // JSONWP compliant
|
|
64
|
-
});
|
|
65
|
-
let response;
|
|
66
|
-
try {
|
|
67
|
-
response = await sessionRequest.makeRequest(params);
|
|
68
|
-
}
|
|
69
|
-
catch (err) {
|
|
70
|
-
log.error(err);
|
|
71
|
-
const message = (0, exports.getSessionError)(err, params);
|
|
72
|
-
throw new Error('Failed to create session.\n' + message);
|
|
73
|
-
}
|
|
74
|
-
const sessionId = response.value.sessionId || response.sessionId;
|
|
75
|
-
/**
|
|
76
|
-
* save actual receveived session details
|
|
77
|
-
*/
|
|
78
|
-
params.capabilities = response.value.capabilities || response.value;
|
|
79
|
-
return { sessionId, capabilities: params.capabilities };
|
|
80
|
-
}
|
|
81
|
-
exports.startWebDriverSession = startWebDriverSession;
|
|
82
|
-
/**
|
|
83
|
-
* check if WebDriver requests was successful
|
|
84
|
-
* @param {Number} statusCode status code of request
|
|
85
|
-
* @param {Object} body body payload of response
|
|
86
|
-
* @return {Boolean} true if request was successful
|
|
87
|
-
*/
|
|
88
|
-
function isSuccessfulResponse(statusCode, body) {
|
|
89
|
-
/**
|
|
90
|
-
* response contains a body
|
|
91
|
-
*/
|
|
92
|
-
if (!body || typeof body.value === 'undefined') {
|
|
93
|
-
log.debug('request failed due to missing body');
|
|
94
|
-
return false;
|
|
95
|
-
}
|
|
96
|
-
/**
|
|
97
|
-
* ignore failing element request to enable lazy loading capability
|
|
98
|
-
*/
|
|
99
|
-
if (body.status === 7 && body.value && body.value.message &&
|
|
100
|
-
(body.value.message.toLowerCase().startsWith('no such element') ||
|
|
101
|
-
// Appium
|
|
102
|
-
body.value.message === 'An element could not be located on the page using the given search parameters.' ||
|
|
103
|
-
// Internet Explorter
|
|
104
|
-
body.value.message.toLowerCase().startsWith('unable to find element'))) {
|
|
105
|
-
return true;
|
|
106
|
-
}
|
|
107
|
-
/**
|
|
108
|
-
* if it has a status property, it should be 0
|
|
109
|
-
* (just here to stay backwards compatible to the jsonwire protocol)
|
|
110
|
-
*/
|
|
111
|
-
if (body.status && body.status !== 0) {
|
|
112
|
-
log.debug(`request failed due to status ${body.status}`);
|
|
113
|
-
return false;
|
|
114
|
-
}
|
|
115
|
-
const hasErrorResponse = body.value && (body.value.error || body.value.stackTrace || body.value.stacktrace);
|
|
116
|
-
/**
|
|
117
|
-
* check status code
|
|
118
|
-
*/
|
|
119
|
-
if (statusCode === 200 && !hasErrorResponse) {
|
|
120
|
-
return true;
|
|
121
|
-
}
|
|
122
|
-
/**
|
|
123
|
-
* if an element was not found we don't flag it as failed request because
|
|
124
|
-
* we lazy load it
|
|
125
|
-
*/
|
|
126
|
-
if (statusCode === 404 && body.value && body.value.error === 'no such element') {
|
|
127
|
-
return true;
|
|
128
|
-
}
|
|
129
|
-
/**
|
|
130
|
-
* that has no error property (Appium only)
|
|
131
|
-
*/
|
|
132
|
-
if (hasErrorResponse) {
|
|
133
|
-
log.debug('request failed due to response error:', body.value.error);
|
|
134
|
-
return false;
|
|
135
|
-
}
|
|
136
|
-
return true;
|
|
137
|
-
}
|
|
138
|
-
exports.isSuccessfulResponse = isSuccessfulResponse;
|
|
139
|
-
/**
|
|
140
|
-
* creates the base prototype for the webdriver monad
|
|
141
|
-
*/
|
|
142
|
-
function getPrototype({ isW3C, isChrome, isFirefox, isMobile, isSauce, isSeleniumStandalone }) {
|
|
143
|
-
const prototype = {};
|
|
144
|
-
const ProtocolCommands = (0, lodash_merge_1.default)(
|
|
145
|
-
/**
|
|
146
|
-
* if mobile apply JSONWire and WebDriver protocol because
|
|
147
|
-
* some legacy JSONWire commands are still used in Appium
|
|
148
|
-
* (e.g. set/get geolocation)
|
|
149
|
-
*/
|
|
150
|
-
isMobile
|
|
151
|
-
? (0, lodash_merge_1.default)({}, protocols_1.JsonWProtocol, protocols_1.WebDriverProtocol)
|
|
152
|
-
: isW3C ? protocols_1.WebDriverProtocol : protocols_1.JsonWProtocol,
|
|
153
|
-
/**
|
|
154
|
-
* only apply mobile protocol if session is actually for mobile
|
|
155
|
-
*/
|
|
156
|
-
isMobile ? (0, lodash_merge_1.default)({}, protocols_1.MJsonWProtocol, protocols_1.AppiumProtocol) : {},
|
|
157
|
-
/**
|
|
158
|
-
* only apply special Chrome commands if session is using Chrome
|
|
159
|
-
*/
|
|
160
|
-
isChrome ? protocols_1.ChromiumProtocol : {},
|
|
161
|
-
/**
|
|
162
|
-
* only apply special Firefox commands if session is using Firefox
|
|
163
|
-
*/
|
|
164
|
-
isFirefox ? protocols_1.GeckoProtocol : {},
|
|
165
|
-
/**
|
|
166
|
-
* only Sauce Labs specific vendor commands
|
|
167
|
-
*/
|
|
168
|
-
isSauce ? protocols_1.SauceLabsProtocol : {},
|
|
169
|
-
/**
|
|
170
|
-
* only apply special commands when running tests using
|
|
171
|
-
* Selenium Grid or Selenium Standalone server
|
|
172
|
-
*/
|
|
173
|
-
isSeleniumStandalone ? protocols_1.SeleniumProtocol : {});
|
|
174
|
-
for (const [endpoint, methods] of Object.entries(ProtocolCommands)) {
|
|
175
|
-
for (const [method, commandData] of Object.entries(methods)) {
|
|
176
|
-
prototype[commandData.command] = { value: (0, command_1.default)(method, endpoint, commandData, isSeleniumStandalone) };
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
return prototype;
|
|
180
|
-
}
|
|
181
|
-
exports.getPrototype = getPrototype;
|
|
182
|
-
/**
|
|
183
|
-
* helper method to determine the error from webdriver response
|
|
184
|
-
* @param {Object} body body object
|
|
185
|
-
* @return {Object} error
|
|
186
|
-
*/
|
|
187
|
-
function getErrorFromResponseBody(body) {
|
|
188
|
-
if (!body) {
|
|
189
|
-
return new Error('Response has empty body');
|
|
190
|
-
}
|
|
191
|
-
if (typeof body === 'string' && body.length) {
|
|
192
|
-
return new Error(body);
|
|
193
|
-
}
|
|
194
|
-
if (typeof body !== 'object') {
|
|
195
|
-
return new Error('Unknown error');
|
|
196
|
-
}
|
|
197
|
-
return new CustomRequestError(body);
|
|
198
|
-
}
|
|
199
|
-
exports.getErrorFromResponseBody = getErrorFromResponseBody;
|
|
200
|
-
//Exporting for testability
|
|
201
|
-
class CustomRequestError extends Error {
|
|
202
|
-
constructor(body) {
|
|
203
|
-
const errorObj = body.value || body;
|
|
204
|
-
super(errorObj.message || errorObj.class || 'unknown error');
|
|
205
|
-
if (errorObj.error) {
|
|
206
|
-
this.name = errorObj.error;
|
|
207
|
-
}
|
|
208
|
-
else if (errorObj.message && errorObj.message.includes('stale element reference')) {
|
|
209
|
-
this.name = 'stale element reference';
|
|
210
|
-
}
|
|
211
|
-
else {
|
|
212
|
-
this.name = errorObj.name || 'WebDriver Error';
|
|
213
|
-
}
|
|
214
|
-
if (errorObj.stacktrace) {
|
|
215
|
-
this.stack = errorObj.stacktrace;
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
exports.CustomRequestError = CustomRequestError;
|
|
220
|
-
/**
|
|
221
|
-
* return all supported flags and return them in a format so we can attach them
|
|
222
|
-
* to the instance protocol
|
|
223
|
-
* @param {Object} options driver instance or option object containing these flags
|
|
224
|
-
* @return {Object} prototype object
|
|
225
|
-
*/
|
|
226
|
-
function getEnvironmentVars({ isW3C, isMobile, isIOS, isAndroid, isChrome, isFirefox, isSauce, isSeleniumStandalone }) {
|
|
227
|
-
return {
|
|
228
|
-
isW3C: { value: isW3C },
|
|
229
|
-
isMobile: { value: isMobile },
|
|
230
|
-
isIOS: { value: isIOS },
|
|
231
|
-
isAndroid: { value: isAndroid },
|
|
232
|
-
isFirefox: { value: isFirefox },
|
|
233
|
-
isChrome: { value: isChrome },
|
|
234
|
-
isSauce: { value: isSauce },
|
|
235
|
-
isSeleniumStandalone: { value: isSeleniumStandalone }
|
|
236
|
-
};
|
|
237
|
-
}
|
|
238
|
-
exports.getEnvironmentVars = getEnvironmentVars;
|
|
239
|
-
/**
|
|
240
|
-
* get human readable message from response error
|
|
241
|
-
* @param {Error} err response error
|
|
242
|
-
*/
|
|
243
|
-
const getSessionError = (err, params = {}) => {
|
|
244
|
-
var _a;
|
|
245
|
-
// browser driver / service is not started
|
|
246
|
-
if (err.code === 'ECONNREFUSED') {
|
|
247
|
-
return `Unable to connect to "${params.protocol}://${params.hostname}:${params.port}${params.path}", make sure browser driver is running on that address.` +
|
|
248
|
-
'\nIf you use services like chromedriver see initialiseServices logs above or in wdio.log file as the service might had problems to start the driver.';
|
|
249
|
-
}
|
|
250
|
-
if (err.message === 'unhandled request') {
|
|
251
|
-
return 'The browser driver couldn\'t start the session. Make sure you have set the "path" correctly!';
|
|
252
|
-
}
|
|
253
|
-
if (!err.message) {
|
|
254
|
-
return 'See wdio.* logs for more information.';
|
|
255
|
-
}
|
|
256
|
-
// wrong path: selenium-standalone
|
|
257
|
-
if (err.message.includes('Whoops! The URL specified routes to this help page.')) {
|
|
258
|
-
return "It seems you are running a Selenium Standalone server and point to a wrong path. Please set `path: '/wd/hub'` in your wdio.conf.js!";
|
|
259
|
-
}
|
|
260
|
-
// wrong path: chromedriver, geckodriver, etc
|
|
261
|
-
if (BROWSER_DRIVER_ERRORS.some(m => err && err.message && err.message.includes(m))) {
|
|
262
|
-
return "Make sure to set `path: '/'` in your wdio.conf.js!";
|
|
263
|
-
}
|
|
264
|
-
// edge driver on localhost
|
|
265
|
-
if (err.message.includes('Bad Request - Invalid Hostname') && err.message.includes('HTTP Error 400')) {
|
|
266
|
-
return "Run edge driver on 127.0.0.1 instead of localhost, ex: --host=127.0.0.1, or set `hostname: 'localhost'` in your wdio.conf.js";
|
|
267
|
-
}
|
|
268
|
-
const w3cCapMessage = '\nMake sure to add vendor prefix like "goog:", "appium:", "moz:", etc to non W3C capabilities.' +
|
|
269
|
-
'\nSee more https://www.w3.org/TR/webdriver/#capabilities';
|
|
270
|
-
// Illegal w3c capability passed to selenium standalone
|
|
271
|
-
if (err.message.includes('Illegal key values seen in w3c capabilities')) {
|
|
272
|
-
return err.message + w3cCapMessage;
|
|
273
|
-
}
|
|
274
|
-
// wrong host/port, port in use, illegal w3c capability passed to selenium grid
|
|
275
|
-
if (err.message === 'Response has empty body') {
|
|
276
|
-
return 'Make sure to connect to valid hostname:port or the port is not in use.' +
|
|
277
|
-
'\nIf you use a grid server ' + w3cCapMessage;
|
|
278
|
-
}
|
|
279
|
-
if (err.message.includes('failed serving request POST /wd/hub/session: Unauthorized') && ((_a = params.hostname) === null || _a === void 0 ? void 0 : _a.endsWith('saucelabs.com'))) {
|
|
280
|
-
return 'Session request was not authorized because you either did provide a wrong access key or tried to run ' +
|
|
281
|
-
'in a region that has not been enabled for your user. If have registered a free trial account it is connected ' +
|
|
282
|
-
'to a specific region. Ensure this region is set in your configuration (https://webdriver.io/docs/options.html#region).';
|
|
283
|
-
}
|
|
284
|
-
return err.message;
|
|
285
|
-
};
|
|
286
|
-
exports.getSessionError = getSessionError;
|
|
287
|
-
/**
|
|
288
|
-
* return timeout error with information about the executing command on which the test hangs
|
|
289
|
-
*/
|
|
290
|
-
const getTimeoutError = (error, requestOptions) => {
|
|
291
|
-
const cmdName = getExecCmdName(requestOptions);
|
|
292
|
-
const cmdArgs = getExecCmdArgs(requestOptions);
|
|
293
|
-
const cmdInfoMsg = `when running "${cmdName}" with method "${requestOptions.method}"`;
|
|
294
|
-
const cmdArgsMsg = cmdArgs ? ` and args ${cmdArgs}` : '';
|
|
295
|
-
const timeoutErr = new Error(`${error.message} ${cmdInfoMsg}${cmdArgsMsg}`);
|
|
296
|
-
return Object.assign(timeoutErr, error);
|
|
297
|
-
};
|
|
298
|
-
exports.getTimeoutError = getTimeoutError;
|
|
299
|
-
function getExecCmdName(requestOptions) {
|
|
300
|
-
const { href } = requestOptions.url;
|
|
301
|
-
const res = href.match(constants_1.REG_EXPS.commandName) || [];
|
|
302
|
-
return res[1] || href;
|
|
303
|
-
}
|
|
304
|
-
function getExecCmdArgs(requestOptions) {
|
|
305
|
-
const { json: cmdJson } = requestOptions;
|
|
306
|
-
if (typeof cmdJson !== 'object') {
|
|
307
|
-
return '';
|
|
308
|
-
}
|
|
309
|
-
const transformedRes = (0, utils_1.transformCommandLogResult)(cmdJson);
|
|
310
|
-
if (typeof transformedRes === 'string') {
|
|
311
|
-
return transformedRes;
|
|
312
|
-
}
|
|
313
|
-
if (typeof cmdJson.script === 'string') {
|
|
314
|
-
const scriptRes = cmdJson.script.match(constants_1.REG_EXPS.execFn) || [];
|
|
315
|
-
return `"${scriptRes[1] || cmdJson.script}"`;
|
|
316
|
-
}
|
|
317
|
-
return Object.keys(cmdJson).length ? `"${JSON.stringify(cmdJson)}"` : '';
|
|
318
|
-
}
|