webdriver 9.0.0-alpha.78 → 9.0.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.
Files changed (43) hide show
  1. package/README.md +3 -3
  2. package/build/bidi/core.d.ts +6 -1
  3. package/build/bidi/core.d.ts.map +1 -1
  4. package/build/bidi/handler.d.ts +49 -0
  5. package/build/bidi/handler.d.ts.map +1 -1
  6. package/build/bidi/localTypes.d.ts +134 -47
  7. package/build/bidi/localTypes.d.ts.map +1 -1
  8. package/build/bidi/remoteTypes.d.ts +135 -28
  9. package/build/bidi/remoteTypes.d.ts.map +1 -1
  10. package/build/command.d.ts.map +1 -1
  11. package/build/constants.d.ts +2 -1
  12. package/build/constants.d.ts.map +1 -1
  13. package/build/index.cjs +233 -0
  14. package/build/index.d.cts +2 -0
  15. package/build/index.d.cts.map +1 -0
  16. package/build/index.d.ts +2 -2
  17. package/build/index.d.ts.map +1 -1
  18. package/build/index.js +1615 -141
  19. package/build/request/index.d.ts +1 -2
  20. package/build/request/index.d.ts.map +1 -1
  21. package/build/request/request.d.ts +0 -1
  22. package/build/request/request.d.ts.map +1 -1
  23. package/build/types.d.ts +9 -7
  24. package/build/types.d.ts.map +1 -1
  25. package/build/utils.d.ts +4 -6
  26. package/build/utils.d.ts.map +1 -1
  27. package/package.json +15 -18
  28. package/build/bidi/core.js +0 -73
  29. package/build/bidi/handler.js +0 -446
  30. package/build/bidi/localTypes.js +0 -15
  31. package/build/bidi/remoteTypes.js +0 -15
  32. package/build/bidi/socket.js +0 -49
  33. package/build/cjs/index.d.ts +0 -2
  34. package/build/cjs/index.d.ts.map +0 -1
  35. package/build/cjs/index.js +0 -40
  36. package/build/cjs/package.json +0 -5
  37. package/build/command.js +0 -140
  38. package/build/constants.js +0 -146
  39. package/build/request/index.js +0 -215
  40. package/build/request/request.js +0 -38
  41. package/build/types.js +0 -1
  42. package/build/utils.js +0 -384
  43. /package/{LICENSE-MIT → LICENSE} +0 -0
package/build/utils.js DELETED
@@ -1,384 +0,0 @@
1
- import { deepmergeCustom } from 'deepmerge-ts';
2
- import logger from '@wdio/logger';
3
- import { WebDriverProtocol, MJsonWProtocol, AppiumProtocol, ChromiumProtocol, SauceLabsProtocol, SeleniumProtocol, GeckoProtocol, WebDriverBidiProtocol } from '@wdio/protocols';
4
- import { transformCommandLogResult } from '@wdio/utils';
5
- import { CAPABILITY_KEYS } from '@wdio/protocols';
6
- import Request from './request/request.js';
7
- import command from './command.js';
8
- import { BidiHandler } from './bidi/handler.js';
9
- import { REG_EXPS } from './constants.js';
10
- const log = logger('webdriver');
11
- const deepmerge = deepmergeCustom({ mergeArrays: false });
12
- const BROWSER_DRIVER_ERRORS = [
13
- 'unknown command: wd/hub/session', // chromedriver
14
- 'HTTP method not allowed', // geckodriver
15
- "'POST /wd/hub/session' was not found.", // safaridriver
16
- 'Command not found' // iedriver
17
- ];
18
- /**
19
- * start browser session with WebDriver protocol
20
- */
21
- export async function startWebDriverSession(params) {
22
- /**
23
- * validate capabilities to check if there are no obvious mix between
24
- * JSONWireProtocol and WebDriver protocol, e.g.
25
- */
26
- if (params.capabilities) {
27
- const extensionCaps = Object.keys(params.capabilities).filter((cap) => cap.includes(':'));
28
- const invalidWebDriverCaps = Object.keys(params.capabilities)
29
- .filter((cap) => !CAPABILITY_KEYS.includes(cap) && !cap.includes(':'));
30
- /**
31
- * if there are vendor extensions, e.g. sauce:options or appium:app
32
- * used (only WebDriver compatible) and caps that aren't defined
33
- * in the WebDriver spec
34
- */
35
- if (extensionCaps.length && invalidWebDriverCaps.length) {
36
- throw new Error(`Invalid or unsupported WebDriver capabilities found ("${invalidWebDriverCaps.join('", "')}"). ` +
37
- 'Ensure to only use valid W3C WebDriver capabilities (see https://w3c.github.io/webdriver/#capabilities).' +
38
- 'If you run your tests on a remote vendor, like Sauce Labs or BrowserStack, make sure that you put them ' +
39
- 'into vendor specific capabilities, e.g. "sauce:options" or "bstack:options". Please reach out ' +
40
- 'to your vendor support team if you have further questions.');
41
- }
42
- }
43
- /**
44
- * the user could have passed in either w3c style or jsonwp style caps
45
- * and we want to pass both styles to the server, which means we need
46
- * to check what style the user sent in so we know how to construct the
47
- * object for the other style
48
- */
49
- const [w3cCaps, jsonwpCaps] = params.capabilities && params.capabilities.alwaysMatch
50
- /**
51
- * in case W3C compliant capabilities are provided
52
- */
53
- ? [params.capabilities, params.capabilities.alwaysMatch]
54
- /**
55
- * otherwise assume they passed in jsonwp-style caps (flat object)
56
- */
57
- : [{ alwaysMatch: params.capabilities, firstMatch: [{}] }, params.capabilities];
58
- const sessionRequest = new Request('POST', '/session', {
59
- capabilities: w3cCaps, // W3C compliant
60
- desiredCapabilities: jsonwpCaps // JSONWP compliant
61
- });
62
- let response;
63
- try {
64
- response = await sessionRequest.makeRequest(params);
65
- }
66
- catch (err) {
67
- log.error(err);
68
- const message = getSessionError(err, params);
69
- throw new Error('Failed to create session.\n' + message);
70
- }
71
- const sessionId = response.value.sessionId || response.sessionId;
72
- /**
73
- * save actual received session details
74
- */
75
- params.capabilities = response.value.capabilities || response.value;
76
- return { sessionId, capabilities: params.capabilities };
77
- }
78
- /**
79
- * check if WebDriver requests was successful
80
- * @param {number} statusCode status code of request
81
- * @param {Object} body body payload of response
82
- * @return {Boolean} true if request was successful
83
- */
84
- export function isSuccessfulResponse(statusCode, body) {
85
- /**
86
- * response contains a body
87
- */
88
- if (!body || typeof body.value === 'undefined') {
89
- log.debug('request failed due to missing body');
90
- return false;
91
- }
92
- /**
93
- * ignore failing element request to enable lazy loading capability
94
- */
95
- if (body.status === 7 && body.value && body.value.message &&
96
- (body.value.message.toLowerCase().startsWith('no such element') ||
97
- // Appium
98
- body.value.message === 'An element could not be located on the page using the given search parameters.' ||
99
- // Internet Explorer
100
- body.value.message.toLowerCase().startsWith('unable to find element'))) {
101
- return true;
102
- }
103
- /**
104
- * if it has a status property, it should be 0
105
- * (just here to stay backwards compatible to the jsonwire protocol)
106
- */
107
- if (body.status && body.status !== 0) {
108
- log.debug(`request failed due to status ${body.status}`);
109
- return false;
110
- }
111
- const hasErrorResponse = body.value && (body.value.error || body.value.stackTrace || body.value.stacktrace);
112
- /**
113
- * check status code
114
- */
115
- if (statusCode === 200 && !hasErrorResponse) {
116
- return true;
117
- }
118
- /**
119
- * if an element was not found we don't flag it as failed request because
120
- * we lazy load it
121
- */
122
- if (statusCode === 404 && body.value && body.value.error === 'no such element') {
123
- return true;
124
- }
125
- /**
126
- * that has no error property (Appium only)
127
- */
128
- if (hasErrorResponse) {
129
- log.debug('request failed due to response error:', body.value.error);
130
- return false;
131
- }
132
- return true;
133
- }
134
- /**
135
- * creates the base prototype for the webdriver monad
136
- */
137
- export function getPrototype({ isW3C, isChromium, isFirefox, isMobile, isSauce, isSeleniumStandalone }) {
138
- const prototype = {};
139
- const ProtocolCommands = deepmerge(
140
- /**
141
- * if mobile apply JSONWire and WebDriver protocol because
142
- * some legacy JSONWire commands are still used in Appium
143
- * (e.g. set/get geolocation)
144
- */
145
- isMobile
146
- ? deepmerge(AppiumProtocol, WebDriverProtocol)
147
- : WebDriverProtocol,
148
- /**
149
- * enable Bidi protocol for W3C sessions
150
- */
151
- isW3C ? WebDriverBidiProtocol : {},
152
- /**
153
- * only apply mobile protocol if session is actually for mobile
154
- */
155
- isMobile ? deepmerge(MJsonWProtocol, AppiumProtocol) : {},
156
- /**
157
- * only apply special Chromium commands if session is using Chrome or Edge
158
- */
159
- isChromium ? ChromiumProtocol : {},
160
- /**
161
- * only apply special Firefox commands if session is using Firefox
162
- */
163
- isFirefox ? GeckoProtocol : {},
164
- /**
165
- * only Sauce Labs specific vendor commands
166
- */
167
- isSauce ? SauceLabsProtocol : {},
168
- /**
169
- * only apply special commands when running tests using
170
- * Selenium Grid or Selenium Standalone server
171
- */
172
- isSeleniumStandalone ? SeleniumProtocol : {}, {});
173
- for (const [endpoint, methods] of Object.entries(ProtocolCommands)) {
174
- for (const [method, commandData] of Object.entries(methods)) {
175
- prototype[commandData.command] = { value: command(method, endpoint, commandData, isSeleniumStandalone) };
176
- }
177
- }
178
- return prototype;
179
- }
180
- /**
181
- * helper method to determine the error from webdriver response
182
- * @param {Object} body body object
183
- * @return {Object} error
184
- */
185
- export function getErrorFromResponseBody(body, requestOptions) {
186
- if (!body) {
187
- return new Error('Response has empty body');
188
- }
189
- if (typeof body === 'string' && body.length) {
190
- return new Error(body);
191
- }
192
- if (typeof body !== 'object') {
193
- return new Error('Unknown error');
194
- }
195
- return new CustomRequestError(body, requestOptions);
196
- }
197
- //Exporting for testability
198
- export class CustomRequestError extends Error {
199
- constructor(body, requestOptions) {
200
- const errorObj = body.value || body;
201
- let errorMessage = errorObj.message || errorObj.class || 'unknown error';
202
- /**
203
- * Improve Chromedriver's error message for an invalid selector
204
- *
205
- * Chrome:
206
- * error: 'invalid argument'
207
- * message: 'invalid argument: invalid locator\n (Session info: chrome=122.0.6261.94)'
208
- * Firefox:
209
- * error: 'invalid selector'
210
- * message: 'Given xpath expression "//button" is invalid: NotSupportedError: Operation is not supported'
211
- * Safari:
212
- * error: 'timeout'
213
- * message: ''
214
- */
215
- if (typeof errorObj.message === 'string' && errorObj.message.includes('invalid locator')) {
216
- errorMessage = (`The selector "${requestOptions.value}" used with strategy "${requestOptions.using}" is invalid!`);
217
- }
218
- super(errorMessage);
219
- if (errorObj.error) {
220
- this.name = errorObj.error;
221
- }
222
- else if (errorObj.message && errorObj.message.includes('stale element reference')) {
223
- this.name = 'stale element reference';
224
- }
225
- else {
226
- this.name = errorObj.name || 'WebDriver Error';
227
- }
228
- Error.captureStackTrace(this, CustomRequestError);
229
- }
230
- }
231
- /**
232
- * return all supported flags and return them in a format so we can attach them
233
- * to the instance protocol
234
- * @param {Object} options driver instance or option object containing these flags
235
- * @return {Object} prototype object
236
- */
237
- export function getEnvironmentVars({ isW3C, isMobile, isIOS, isAndroid, isFirefox, isSauce, isSeleniumStandalone, isBidi, isChromium }) {
238
- return {
239
- isW3C: { value: isW3C },
240
- isMobile: { value: isMobile },
241
- isIOS: { value: isIOS },
242
- isAndroid: { value: isAndroid },
243
- isFirefox: { value: isFirefox },
244
- isSauce: { value: isSauce },
245
- isSeleniumStandalone: { value: isSeleniumStandalone },
246
- isBidi: { value: isBidi },
247
- isChromium: { value: isChromium },
248
- };
249
- }
250
- /**
251
- * Decorate the client's options object with host updates based on the presence of
252
- * directConnect capabilities in the new session response. Note that this
253
- * mutates the object.
254
- * @param {Client} params post-new-session client
255
- */
256
- export function setupDirectConnect(client) {
257
- const capabilities = client.capabilities;
258
- const directConnectProtocol = capabilities['appium:directConnectProtocol'];
259
- const directConnectHost = capabilities['appium:directConnectHost'];
260
- const directConnectPath = capabilities['appium:directConnectPath'];
261
- const directConnectPort = capabilities['appium:directConnectPort'];
262
- if (directConnectProtocol && directConnectHost && directConnectPort &&
263
- (directConnectPath || directConnectPath === '')) {
264
- log.info('Found direct connect information in new session response. ' +
265
- `Will connect to server at ${directConnectProtocol}://` +
266
- `${directConnectHost}:${directConnectPort}${directConnectPath}`);
267
- client.options.protocol = directConnectProtocol;
268
- client.options.hostname = directConnectHost;
269
- client.options.port = directConnectPort;
270
- client.options.path = directConnectPath;
271
- }
272
- }
273
- /**
274
- * get human readable message from response error
275
- * @param {Error} err response error
276
- */
277
- export const getSessionError = (err, params = {}) => {
278
- // browser driver / service is not started
279
- if (err.code === 'ECONNREFUSED') {
280
- return `Unable to connect to "${params.protocol}://${params.hostname}:${params.port}${params.path}", make sure browser driver is running on that address.` +
281
- '\nIt seems like the service failed to start or is rejecting any connections.';
282
- }
283
- if (err.message === 'unhandled request') {
284
- return 'The browser driver couldn\'t start the session. Make sure you have set the "path" correctly!';
285
- }
286
- if (!err.message) {
287
- return 'See wdio.* logs for more information.';
288
- }
289
- // wrong path: selenium-standalone
290
- if (err.message.includes('Whoops! The URL specified routes to this help page.')) {
291
- 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!";
292
- }
293
- // wrong path: chromedriver, geckodriver, etc
294
- if (BROWSER_DRIVER_ERRORS.some(m => err && err.message && err.message.includes(m))) {
295
- return "Make sure to set `path: '/'` in your wdio.conf.js!";
296
- }
297
- // edge driver on localhost
298
- if (err.message.includes('Bad Request - Invalid Hostname') && err.message.includes('HTTP Error 400')) {
299
- 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";
300
- }
301
- const w3cCapMessage = '\nMake sure to add vendor prefix like "goog:", "appium:", "moz:", etc to non W3C capabilities.' +
302
- '\nSee more https://www.w3.org/TR/webdriver/#capabilities';
303
- // Illegal w3c capability passed to selenium standalone
304
- if (err.message.includes('Illegal key values seen in w3c capabilities')) {
305
- return err.message + w3cCapMessage;
306
- }
307
- // wrong host/port, port in use, illegal w3c capability passed to selenium grid
308
- if (err.message === 'Response has empty body') {
309
- return 'Make sure to connect to valid hostname:port or the port is not in use.' +
310
- '\nIf you use a grid server ' + w3cCapMessage;
311
- }
312
- if (err.message.includes('failed serving request POST /wd/hub/session: Unauthorized') && params.hostname?.endsWith('saucelabs.com')) {
313
- return 'Session request was not authorized because you either did provide a wrong access key or tried to run ' +
314
- 'in a region that has not been enabled for your user. If have registered a free trial account it is connected ' +
315
- 'to a specific region. Ensure this region is set in your configuration (https://webdriver.io/docs/options.html#region).';
316
- }
317
- return err.message;
318
- };
319
- /**
320
- * return timeout error with information about the executing command on which the test hangs
321
- */
322
- export function getTimeoutError(error, requestOptions, url) {
323
- const cmdName = getExecCmdName(url);
324
- const cmdArgs = getExecCmdArgs(requestOptions);
325
- const cmdInfoMsg = `when running "${cmdName}" with method "${requestOptions.method}"`;
326
- const cmdArgsMsg = cmdArgs ? ` and args ${cmdArgs}` : '';
327
- const timeoutErr = new Error(`${error.message} ${cmdInfoMsg}${cmdArgsMsg}`);
328
- return Object.assign(timeoutErr, error);
329
- }
330
- function getExecCmdName(url) {
331
- const { href } = url;
332
- const res = href.match(REG_EXPS.commandName) || [];
333
- return res[1] || href;
334
- }
335
- function getExecCmdArgs(requestOptions) {
336
- const { body: cmdJson } = requestOptions;
337
- if (typeof cmdJson !== 'object') {
338
- return '';
339
- }
340
- const transformedRes = transformCommandLogResult(cmdJson);
341
- if (typeof transformedRes === 'string') {
342
- return transformedRes;
343
- }
344
- if (typeof cmdJson.script === 'string') {
345
- const scriptRes = cmdJson.script.match(REG_EXPS.execFn) || [];
346
- return `"${scriptRes[1] || cmdJson.script}"`;
347
- }
348
- return Object.keys(cmdJson).length ? `"${JSON.stringify(cmdJson)}"` : '';
349
- }
350
- /**
351
- * Enhance the monad with WebDriver Bidi primitives if a connection can be established successfully
352
- * @param socketUrl url to bidi interface
353
- * @returns prototype with interface for bidi primitives
354
- */
355
- export function initiateBidi(socketUrl, strictSSL = true) {
356
- socketUrl = socketUrl.replace('localhost', '127.0.0.1');
357
- const bidiReqOpts = strictSSL ? {} : { rejectUnauthorized: false };
358
- const handler = new BidiHandler(socketUrl, bidiReqOpts);
359
- handler.connect().then(() => log.info(`Connected to WebDriver Bidi interface at ${socketUrl}`));
360
- return {
361
- _bidiHandler: { value: handler },
362
- ...Object.values(WebDriverBidiProtocol).map((def) => def.socket).reduce((acc, cur) => {
363
- acc[cur.command] = {
364
- value: handler[cur.command]?.bind(handler)
365
- };
366
- return acc;
367
- }, {})
368
- };
369
- }
370
- export function parseBidiMessage(data) {
371
- try {
372
- // keep backwards compatibility
373
- // ToDo(Christian): remove in v9
374
- this.emit('message', data);
375
- const payload = JSON.parse(data.toString());
376
- if (payload.type !== 'event') {
377
- return;
378
- }
379
- this.emit(payload.method, payload.params);
380
- }
381
- catch (err) {
382
- log.error(`Failed parse WebDriver Bidi message: ${err.message}`);
383
- }
384
- }
File without changes