webdriver 5.11.0 → 5.11.12

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/command.js CHANGED
@@ -23,62 +23,37 @@ function _default(method, endpointUri, commandInfo) {
23
23
  variables = [],
24
24
  isHubCommand = false
25
25
  } = commandInfo;
26
- return function (...args) {
27
- let endpoint = endpointUri; // clone endpointUri in case we change it
28
-
26
+ return function protocolCommand(...args) {
27
+ let endpoint = endpointUri;
29
28
  const commandParams = [...variables.map(v => Object.assign(v, {
30
- /**
31
- * url variables are:
32
- */
33
29
  required: true,
34
- // always required as they are part of the endpoint
35
- type: 'string' // have to be always type of string
36
-
30
+ type: 'string'
37
31
  })), ...parameters];
38
32
  const commandUsage = `${command}(${commandParams.map(p => p.name).join(', ')})`;
39
33
  const moreInfo = `\n\nFor more info see ${ref}\n`;
40
34
  const body = {};
41
- /**
42
- * parameter check
43
- */
44
-
45
35
  const minAllowedParams = commandParams.filter(param => param.required).length;
46
36
 
47
37
  if (args.length < minAllowedParams || args.length > commandParams.length) {
48
38
  const parameterDescription = commandParams.length ? `\n\nProperty Description:\n${commandParams.map(p => ` "${p.name}" (${p.type}): ${p.description}`).join('\n')}` : '';
49
39
  throw new Error(`Wrong parameters applied for ${command}\n` + `Usage: ${commandUsage}` + parameterDescription + moreInfo);
50
40
  }
51
- /**
52
- * parameter type check
53
- */
54
-
55
41
 
56
42
  for (const [i, arg] of Object.entries(args)) {
57
43
  const commandParam = commandParams[i];
58
44
 
59
45
  if (!(0, _utils.isValidParameter)(arg, commandParam.type)) {
60
- /**
61
- * ignore if argument is not required
62
- */
63
46
  if (typeof arg === 'undefined' && !commandParam.required) {
64
47
  continue;
65
48
  }
66
49
 
67
50
  throw new Error(`Malformed type for "${commandParam.name}" parameter of command ${command}\n` + `Expected: ${commandParam.type}\n` + `Actual: ${(0, _utils.getArgumentType)(arg)}` + moreInfo);
68
51
  }
69
- /**
70
- * inject url variables
71
- */
72
-
73
52
 
74
53
  if (i < variables.length) {
75
- endpoint = endpoint.replace(`:${commandParams[i].name}`, arg);
54
+ endpoint = endpoint.replace(`:${commandParams[i].name}`, encodeURIComponent(encodeURIComponent(arg)));
76
55
  continue;
77
56
  }
78
- /**
79
- * rest of args are part of body payload
80
- */
81
-
82
57
 
83
58
  body[commandParams[i].name] = arg;
84
59
  }
@@ -92,7 +67,7 @@ function _default(method, endpointUri, commandInfo) {
92
67
  log.info('COMMAND', (0, _utils.commandCallStructure)(command, args));
93
68
  return request.makeRequest(this.options, this.sessionId).then(result => {
94
69
  if (result.value != null) {
95
- log.info('RESULT', command.toLowerCase().includes('screenshot') && typeof result.value === 'string' && result.value.length > 64 ? `${result.value.substr(0, 61)}...` : result.value);
70
+ log.info('RESULT', /screenshot|recording/i.test(command) && typeof result.value === 'string' && result.value.length > 64 ? `${result.value.substr(0, 61)}...` : result.value);
96
71
  }
97
72
 
98
73
  this.emit('result', {
@@ -5,103 +5,52 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.DEFAULTS = void 0;
7
7
  const DEFAULTS = {
8
- /**
9
- * protocol of automation driver
10
- */
11
8
  protocol: {
12
9
  type: 'string',
13
10
  default: 'http',
14
11
  match: /(http|https)/
15
12
  },
16
-
17
- /**
18
- * hostname of automation driver
19
- */
20
13
  hostname: {
21
14
  type: 'string',
22
15
  default: 'localhost'
23
16
  },
24
-
25
- /**
26
- * port of automation driver
27
- */
28
17
  port: {
29
18
  type: 'number',
30
19
  default: 4444
31
20
  },
32
-
33
- /**
34
- * path to WebDriver endpoints
35
- */
36
21
  path: {
37
22
  type: 'string',
38
23
  default: '/wd/hub'
39
24
  },
40
-
41
- /**
42
- * A key-value store of query parameters to be added to every selenium request
43
- */
44
25
  queryParams: {
45
26
  type: 'object'
46
27
  },
47
-
48
- /**
49
- * capability of WebDriver session
50
- */
51
28
  capabilities: {
52
29
  type: 'object',
53
30
  required: true
54
31
  },
55
-
56
- /**
57
- * Level of logging verbosity
58
- */
59
32
  logLevel: {
60
33
  type: 'string',
61
34
  default: 'info',
62
35
  match: /(trace|debug|info|warn|error|silent)/
63
36
  },
64
-
65
- /**
66
- * Timeout for any request to the Selenium server
67
- */
68
37
  connectionRetryTimeout: {
69
38
  type: 'number',
70
39
  default: 90000
71
40
  },
72
-
73
- /**
74
- * Count of request retries to the Selenium server
75
- */
76
41
  connectionRetryCount: {
77
42
  type: 'number',
78
43
  default: 3
79
44
  },
80
-
81
- /**
82
- * cloud user if applicable
83
- */
84
45
  user: {
85
46
  type: 'string'
86
47
  },
87
-
88
- /**
89
- * access key to user
90
- */
91
48
  key: {
92
49
  type: 'string'
93
50
  },
94
-
95
- /**
96
- * Override default agent
97
- */
98
51
  agent: {
99
52
  type: 'object'
100
53
  },
101
-
102
- /**
103
- * Pass custom headers
104
- */
105
54
  headers: {
106
55
  type: 'object'
107
56
  }
package/build/index.js CHANGED
@@ -19,8 +19,6 @@ exports.default = void 0;
19
19
 
20
20
  var _logger = _interopRequireDefault(require("@wdio/logger"));
21
21
 
22
- var _lodash = _interopRequireDefault(require("lodash.merge"));
23
-
24
22
  var _config = require("@wdio/config");
25
23
 
26
24
  var _monad = _interopRequireDefault(require("./monad"));
@@ -43,6 +41,12 @@ var _chromium = _interopRequireDefault(require("../protocol/chromium.json"));
43
41
 
44
42
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
45
43
 
44
+ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
45
+
46
+ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(source, true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(source).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
47
+
48
+ function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
49
+
46
50
  class WebDriver {
47
51
  static async newSession(options = {}, modifier, userPrototype = {}, commandWrapper) {
48
52
  const params = (0, _config.validateConfig)(_constants.DEFAULTS, options);
@@ -50,64 +54,35 @@ class WebDriver {
50
54
  if (!options.logLevels || !options.logLevels['webdriver']) {
51
55
  _logger.default.setLevel('webdriver', params.logLevel);
52
56
  }
53
- /**
54
- * the user could have passed in either w3c style or jsonwp style caps
55
- * and we want to pass both styles to the server, which means we need
56
- * to check what style the user sent in so we know how to construct the
57
- * object for the other style
58
- */
59
-
60
-
61
- const [w3cCaps, jsonwpCaps] = params.capabilities && params.capabilities.alwaysMatch
62
- /**
63
- * in case W3C compliant capabilities are provided
64
- */
65
- ? [params.capabilities, params.capabilities.alwaysMatch]
66
- /**
67
- * otherwise assume they passed in jsonwp-style caps (flat object)
68
- */
69
- : [{
57
+
58
+ const [w3cCaps, jsonwpCaps] = params.capabilities && params.capabilities.alwaysMatch ? [params.capabilities, params.capabilities.alwaysMatch] : [{
70
59
  alwaysMatch: params.capabilities,
71
60
  firstMatch: [{}]
72
61
  }, params.capabilities];
73
62
  const sessionRequest = new _request.default('POST', '/session', {
74
63
  capabilities: w3cCaps,
75
- // W3C compliant
76
- desiredCapabilities: jsonwpCaps // JSONWP compliant
77
-
64
+ desiredCapabilities: jsonwpCaps
78
65
  });
79
66
  const response = await sessionRequest.makeRequest(params);
80
- /**
81
- * save original set of capabilities to allow to request the same session again
82
- * (e.g. for reloadSession command in WebdriverIO)
83
- */
84
-
85
67
  params.requestedCapabilities = {
86
68
  w3cCaps,
87
69
  jsonwpCaps
88
- /**
89
- * save actual receveived session details
90
- */
91
-
92
70
  };
93
71
  params.capabilities = response.value.capabilities || response.value;
94
72
  const environment = (0, _utils.environmentDetector)(params);
95
73
  const environmentPrototype = (0, _utils.getEnvironmentVars)(environment);
96
74
  const protocolCommands = (0, _utils.getPrototype)(environment);
97
- const prototype = (0, _lodash.default)(protocolCommands, environmentPrototype, userPrototype);
75
+
76
+ const prototype = _objectSpread({}, protocolCommands, {}, environmentPrototype, {}, userPrototype);
77
+
98
78
  const monad = (0, _monad.default)(params, modifier, prototype);
99
79
  return monad(response.value.sessionId || response.sessionId, commandWrapper);
100
80
  }
101
- /**
102
- * allows user to attach to existing sessions
103
- */
104
-
105
81
 
106
82
  static attachToSession(options = {}, modifier, userPrototype = {}, commandWrapper) {
107
83
  if (typeof options.sessionId !== 'string') {
108
84
  throw new Error('sessionId is required to attach to existing session');
109
- } // logLevel can be undefined in watch mode when SIGINT is called
110
-
85
+ }
111
86
 
112
87
  if (options.logLevel !== undefined) {
113
88
  _logger.default.setLevel('webdriver', options.logLevel);
@@ -117,7 +92,9 @@ class WebDriver {
117
92
  options.isW3C = options.isW3C === false ? false : true;
118
93
  const environmentPrototype = (0, _utils.getEnvironmentVars)(options);
119
94
  const protocolCommands = (0, _utils.getPrototype)(options);
120
- const prototype = (0, _lodash.default)(protocolCommands, environmentPrototype, userPrototype);
95
+
96
+ const prototype = _objectSpread({}, protocolCommands, {}, environmentPrototype, {}, userPrototype);
97
+
121
98
  const monad = (0, _monad.default)(options, modifier, prototype);
122
99
  return monad(options.sessionId, commandWrapper);
123
100
  }
@@ -129,10 +106,6 @@ class WebDriver {
129
106
  static get DEFAULTS() {
130
107
  return _constants.DEFAULTS;
131
108
  }
132
- /**
133
- * Protocols
134
- */
135
-
136
109
 
137
110
  static get WebDriverProtocol() {
138
111
  return _webdriver.default;
@@ -155,9 +128,5 @@ class WebDriver {
155
128
  }
156
129
 
157
130
  }
158
- /**
159
- * Helper methods consumed by webdriverio package
160
- */
161
-
162
131
 
163
132
  exports.default = WebDriver;
package/build/monad.js CHANGED
@@ -14,29 +14,17 @@ var _utils = require("./utils");
14
14
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
15
15
 
16
16
  const SCOPE_TYPES = {
17
- 'browser':
18
- /* istanbul ignore next */
19
- function Browser() {},
20
- 'element':
21
- /* istanbul ignore next */
22
- function Element() {}
17
+ 'browser': function Browser() {},
18
+ 'element': function Element() {}
23
19
  };
24
20
 
25
21
  function WebDriver(options, modifier, propertiesObject = {}) {
26
- /**
27
- * In order to allow named scopes for elements we have to propagate that
28
- * info within the `propertiesObject` object. This doesn't have any functional
29
- * advantages just provides better description of objects when debugging them
30
- */
31
22
  const scopeType = SCOPE_TYPES[propertiesObject.scope] || SCOPE_TYPES['browser'];
32
23
  delete propertiesObject.scope;
33
24
  const prototype = Object.create(scopeType.prototype);
34
25
  const log = (0, _logger.default)('webdriver');
35
26
  const eventHandler = new _events.EventEmitter();
36
27
  const EVENTHANDLER_FUNCTIONS = Object.getPrototypeOf(eventHandler);
37
- /**
38
- * WebDriver monad
39
- */
40
28
 
41
29
  function unit(sessionId, commandWrapper) {
42
30
  propertiesObject.commandList = {
@@ -44,11 +32,6 @@ function WebDriver(options, modifier, propertiesObject = {}) {
44
32
  };
45
33
  propertiesObject.options = {
46
34
  value: options
47
- /**
48
- * allow to wrap commands if necessary
49
- * e.g. in wdio-cli to make them synchronous
50
- */
51
-
52
35
  };
53
36
 
54
37
  if (typeof commandWrapper === 'function') {
@@ -63,25 +46,14 @@ function WebDriver(options, modifier, propertiesObject = {}) {
63
46
  propertiesObject[commandName].configurable = true;
64
47
  }
65
48
  }
66
- /**
67
- * overwrite native element commands with user defined
68
- */
69
-
70
49
 
71
50
  _utils.overwriteElementCommands.call(this, propertiesObject);
72
- /**
73
- * assign propertiesObject to itself so the client can be recreated
74
- */
75
-
76
51
 
77
52
  propertiesObject['__propertiesObject__'] = {
78
53
  value: propertiesObject
79
54
  };
80
55
  let client = Object.create(prototype, propertiesObject);
81
56
  client.sessionId = sessionId;
82
- /**
83
- * register capabilities only to browser scope
84
- */
85
57
 
86
58
  if (scopeType.name === 'Browser') {
87
59
  client.capabilities = options.capabilities;
@@ -95,9 +67,6 @@ function WebDriver(options, modifier, propertiesObject = {}) {
95
67
  const customCommand = typeof commandWrapper === 'function' ? commandWrapper(name, func) : func;
96
68
 
97
69
  if (attachToElement) {
98
- /**
99
- * add command to every multiremote instance
100
- */
101
70
  if (instances) {
102
71
  Object.values(instances).forEach(instance => {
103
72
  instance.__propertiesObject__[name] = {
@@ -113,32 +82,16 @@ function WebDriver(options, modifier, propertiesObject = {}) {
113
82
  unit.lift(name, customCommand, proto);
114
83
  }
115
84
  };
116
- /**
117
- * overwriteCommand
118
- * @param {String} name command name to be overwritten
119
- * @param {Function} func function to replace original command with;
120
- * takes original function as first argument.
121
- * @param {boolean=} attachToElement overwrite browser command (false) or element command (true)
122
- * @param {Object=} proto prototype to add function to (optional)
123
- * @param {Object=} instances multiremote instances
124
- */
125
-
126
85
 
127
86
  client.overwriteCommand = function (name, func, attachToElement = false, proto, instances) {
128
87
  let customCommand = typeof commandWrapper === 'function' ? commandWrapper(name, func) : func;
129
88
 
130
89
  if (attachToElement) {
131
90
  if (instances) {
132
- /**
133
- * add command to every multiremote instance
134
- */
135
91
  Object.values(instances).forEach(instance => {
136
92
  instance.__propertiesObject__.__elementOverrides__.value[name] = customCommand;
137
93
  });
138
94
  } else {
139
- /**
140
- * regular mode
141
- */
142
95
  this.__propertiesObject__.__elementOverrides__.value[name] = customCommand;
143
96
  }
144
97
  } else if (client[name]) {
@@ -152,32 +105,15 @@ function WebDriver(options, modifier, propertiesObject = {}) {
152
105
 
153
106
  return client;
154
107
  }
155
- /**
156
- * Enhance monad prototype with function
157
- * @param {String} name name of function to attach to prototype
158
- * @param {Function} func function to be added to prototype
159
- * @param {Object} proto prototype to add function to (optional)
160
- * @param {Function} origCommand original command to be passed to custom command as first argument
161
- */
162
-
163
108
 
164
109
  unit.lift = function (name, func, proto, origCommand) {
165
110
  (proto || prototype)[name] = function next(...args) {
166
111
  log.info('COMMAND', (0, _utils.commandCallStructure)(name, args));
167
- /**
168
- * set name of function for better error stack
169
- */
170
-
171
112
  Object.defineProperty(func, 'name', {
172
113
  value: name,
173
114
  writable: false
174
115
  });
175
116
  const result = func.apply(this, origCommand ? [origCommand, ...args] : args);
176
- /**
177
- * always transform result into promise as we don't know whether or not
178
- * the user is running tests with wdio-sync or not
179
- */
180
-
181
117
  Promise.resolve(result).then(res => {
182
118
  log.info('RESULT', res);
183
119
  this.emit('result', {
@@ -188,10 +124,6 @@ function WebDriver(options, modifier, propertiesObject = {}) {
188
124
  return result;
189
125
  };
190
126
  };
191
- /**
192
- * register event emitter
193
- */
194
-
195
127
 
196
128
  for (let eventCommand in EVENTHANDLER_FUNCTIONS) {
197
129
  prototype[eventCommand] = function (...args) {
package/build/request.js CHANGED
@@ -13,8 +13,6 @@ var _path = _interopRequireDefault(require("path"));
13
13
 
14
14
  var _https = _interopRequireDefault(require("https"));
15
15
 
16
- var _lodash = _interopRequireDefault(require("lodash.merge"));
17
-
18
16
  var _request = _interopRequireDefault(require("request"));
19
17
 
20
18
  var _events = _interopRequireDefault(require("events"));
@@ -58,7 +56,7 @@ class WebDriverRequest extends _events.default {
58
56
  }
59
57
 
60
58
  makeRequest(options, sessionId) {
61
- const fullRequestOptions = (0, _lodash.default)({}, this.defaultOptions, this._createOptions(options, sessionId));
59
+ const fullRequestOptions = Object.assign({}, this.defaultOptions, this._createOptions(options, sessionId));
62
60
  this.emit('request', fullRequestOptions);
63
61
  return this._request(fullRequestOptions, options.connectionRetryCount);
64
62
  }
@@ -68,33 +66,20 @@ class WebDriverRequest extends _events.default {
68
66
  agent: options.agent || agents[options.protocol],
69
67
  headers: typeof options.headers === 'object' ? options.headers : {},
70
68
  qs: typeof options.queryParams === 'object' ? options.queryParams : {}
71
- /**
72
- * only apply body property if existing
73
- */
74
-
75
69
  };
76
70
 
77
71
  if (this.body && (Object.keys(this.body).length || this.method === 'POST')) {
78
72
  requestOptions.body = this.body;
79
- requestOptions.headers = (0, _lodash.default)({}, requestOptions.headers, {
73
+ requestOptions.headers = Object.assign({}, requestOptions.headers, {
80
74
  'Content-Length': Buffer.byteLength(JSON.stringify(requestOptions.body), 'UTF-8')
81
75
  });
82
76
  }
83
- /**
84
- * if we don't have a session id we set it here, unless we call commands that don't require session ids, for
85
- * example /sessions. The call to /sessions is not connected to a session itself and it therefore doesn't
86
- * require it
87
- */
88
-
89
77
 
90
78
  if (this.requiresSessionId && !sessionId) {
91
79
  throw new Error('A sessionId is required for this command');
92
80
  }
93
81
 
94
82
  requestOptions.uri = _url.default.parse(`${options.protocol}://` + `${options.hostname}:${options.port}` + (this.isHubCommand ? this.endpoint : _path.default.join(options.path, this.endpoint.replace(':sessionId', sessionId))));
95
- /**
96
- * send authentication credentials only when creating new session
97
- */
98
83
 
99
84
  if (this.endpoint === '/session' && options.user && options.key) {
100
85
  requestOptions.auth = {
@@ -102,10 +87,6 @@ class WebDriverRequest extends _events.default {
102
87
  pass: options.key
103
88
  };
104
89
  }
105
- /**
106
- * if the environment variable "STRICT_SSL" is defined as "false", it doesn't require SSL certificates to be valid.
107
- */
108
-
109
90
 
110
91
  requestOptions.strictSSL = !(process.env.STRICT_SSL === 'false' || process.env.strict_ssl === 'false');
111
92
  return requestOptions;
@@ -120,16 +101,8 @@ class WebDriverRequest extends _events.default {
120
101
 
121
102
  return new Promise((resolve, reject) => (0, _request.default)(fullRequestOptions, (err, response, body) => {
122
103
  const error = err || (0, _utils.getErrorFromResponseBody)(body);
123
- /**
124
- * hub commands don't follow standard response formats
125
- * and can have empty bodies
126
- */
127
104
 
128
105
  if (this.isHubCommand) {
129
- /**
130
- * if body contains HTML the command was called on a node
131
- * directly without using a hub, therefor throw
132
- */
133
106
  if (typeof body === 'string' && body.startsWith('<!DOCTYPE html>')) {
134
107
  return reject(new Error('Command can only be called to a Selenium Hub'));
135
108
  }
@@ -138,10 +111,6 @@ class WebDriverRequest extends _events.default {
138
111
  value: body || null
139
112
  };
140
113
  }
141
- /**
142
- * Resolve only if successful response
143
- */
144
-
145
114
 
146
115
  if (!err && (0, _utils.isSuccessfulResponse)(response.statusCode, body)) {
147
116
  this.emit('response', {
@@ -149,11 +118,6 @@ class WebDriverRequest extends _events.default {
149
118
  });
150
119
  return resolve(body);
151
120
  }
152
- /**
153
- * stop retrying as this will never be successful.
154
- * we will handle this at the elementErrorHandler
155
- */
156
-
157
121
 
158
122
  if (error.name === 'stale element reference') {
159
123
  log.warn('Request encountered a stale element - terminating request');
@@ -162,11 +126,6 @@ class WebDriverRequest extends _events.default {
162
126
  });
163
127
  return reject(error);
164
128
  }
165
- /**
166
- * stop retrying if totalRetryCount was exceeded or there is no reason to
167
- * retry, e.g. if sessionId is invalid
168
- */
169
-
170
129
 
171
130
  if (retryCount >= totalRetryCount || error.message.includes('invalid session id')) {
172
131
  log.error('Request failed due to', error);
package/build/utils.js CHANGED
@@ -44,35 +44,16 @@ var _selenium = _interopRequireDefault(require("../protocol/selenium.json"));
44
44
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
45
45
 
46
46
  const log = (0, _logger.default)('webdriver');
47
- /**
48
- * check if WebDriver requests was successful
49
- * @param {Object} body body payload of response
50
- * @return {Boolean} true if request was successful
51
- */
52
47
 
53
48
  function isSuccessfulResponse(statusCode, body) {
54
- /**
55
- * response contains a body
56
- */
57
49
  if (!body || typeof body.value === 'undefined') {
58
50
  log.debug('request failed due to missing body');
59
51
  return false;
60
52
  }
61
- /**
62
- * ignore failing element request to enable lazy loading capability
63
- */
64
53
 
65
-
66
- if (body.status === 7 && body.value && body.value.message && (body.value.message.toLowerCase().startsWith('no such element') || // Appium
67
- body.value.message === 'An element could not be located on the page using the given search parameters.' || // Internet Explorter
68
- body.value.message.toLowerCase().startsWith('unable to find element'))) {
54
+ if (body.status === 7 && body.value && body.value.message && (body.value.message.toLowerCase().startsWith('no such element') || body.value.message === 'An element could not be located on the page using the given search parameters.' || body.value.message.toLowerCase().startsWith('unable to find element'))) {
69
55
  return true;
70
56
  }
71
- /**
72
- * if it has a status property, it should be 0
73
- * (just here to stay backwards compatible to the jsonwire protocol)
74
- */
75
-
76
57
 
77
58
  if (body.status && body.status !== 0) {
78
59
  log.debug(`request failed due to status ${body.status}`);
@@ -80,26 +61,14 @@ function isSuccessfulResponse(statusCode, body) {
80
61
  }
81
62
 
82
63
  const hasErrorResponse = body.value && (body.value.error || body.value.stackTrace || body.value.stacktrace);
83
- /**
84
- * check status code
85
- */
86
64
 
87
65
  if (statusCode === 200 && !hasErrorResponse) {
88
66
  return true;
89
67
  }
90
- /**
91
- * if an element was not found we don't flag it as failed request because
92
- * we lazy load it
93
- */
94
-
95
68
 
96
69
  if (statusCode === 404 && body.value && body.value.error === 'no such element') {
97
70
  return true;
98
71
  }
99
- /**
100
- * that has no error property (Appium only)
101
- */
102
-
103
72
 
104
73
  if (hasErrorResponse) {
105
74
  log.debug('request failed due to response error:', body.value.error);
@@ -108,14 +77,6 @@ function isSuccessfulResponse(statusCode, body) {
108
77
 
109
78
  return true;
110
79
  }
111
- /**
112
- * checks if command argument is valid according to specificiation
113
- *
114
- * @param {*} arg command argument
115
- * @param {Object} expectedType parameter type (e.g. `number`, `string[]` or `(number|string)`)
116
- * @return {Boolean} true if argument is valid
117
- */
118
-
119
80
 
120
81
  function isValidParameter(arg, expectedType) {
121
82
  let shouldBeArray = false;
@@ -124,19 +85,12 @@ function isValidParameter(arg, expectedType) {
124
85
  expectedType = expectedType.slice(0, -2);
125
86
  shouldBeArray = true;
126
87
  }
127
- /**
128
- * check type of each individual array element
129
- */
130
-
131
88
 
132
89
  if (shouldBeArray) {
133
90
  if (!Array.isArray(arg)) {
134
91
  return false;
135
92
  }
136
93
  } else {
137
- /**
138
- * transform to array to have a unified check
139
- */
140
94
  arg = [arg];
141
95
  }
142
96
 
@@ -150,18 +104,10 @@ function isValidParameter(arg, expectedType) {
150
104
 
151
105
  return true;
152
106
  }
153
- /**
154
- * get type of command argument
155
- */
156
-
157
107
 
158
108
  function getArgumentType(arg) {
159
109
  return arg === null ? 'null' : typeof arg;
160
110
  }
161
- /**
162
- * creates the base prototype for the webdriver monad
163
- */
164
-
165
111
 
166
112
  function getPrototype({
167
113
  isW3C,
@@ -171,30 +117,7 @@ function getPrototype({
171
117
  isSeleniumStandalone
172
118
  }) {
173
119
  const prototype = {};
174
- const ProtocolCommands = (0, _lodash.default)(
175
- /**
176
- * if mobile apply JSONWire and WebDriver protocol because
177
- * some legacy JSONWire commands are still used in Appium
178
- * (e.g. set/get geolocation)
179
- */
180
- isMobile ? (0, _lodash.default)({}, _jsonwp.default, _webdriver.default) : isW3C ? _webdriver.default : _jsonwp.default,
181
- /**
182
- * only apply mobile protocol if session is actually for mobile
183
- */
184
- isMobile ? (0, _lodash.default)({}, _mjsonwp.default, _appium.default) : {},
185
- /**
186
- * only apply special Chrome commands if session is using Chrome
187
- */
188
- isChrome ? _chromium.default : {},
189
- /**
190
- * only Sauce Labs specific vendor commands
191
- */
192
- isSauce ? _saucelabs.default : {},
193
- /**
194
- * only apply special commands when running tests using
195
- * Selenium Grid or Selenium Standalone server
196
- */
197
- isSeleniumStandalone ? _selenium.default : {});
120
+ const ProtocolCommands = (0, _lodash.default)(isMobile ? (0, _lodash.default)({}, _jsonwp.default, _webdriver.default) : isW3C ? _webdriver.default : _jsonwp.default, isMobile ? (0, _lodash.default)({}, _mjsonwp.default, _appium.default) : {}, isChrome ? _chromium.default : {}, isSauce ? _saucelabs.default : {}, isSeleniumStandalone ? _selenium.default : {});
198
121
 
199
122
  for (const [endpoint, methods] of Object.entries(ProtocolCommands)) {
200
123
  for (const [method, commandData] of Object.entries(methods)) {
@@ -206,11 +129,6 @@ function getPrototype({
206
129
 
207
130
  return prototype;
208
131
  }
209
- /**
210
- * get command call structure
211
- * (for logging purposes)
212
- */
213
-
214
132
 
215
133
  function commandCallStructure(commandName, args) {
216
134
  const callArgs = args.map(arg => {
@@ -230,108 +148,40 @@ function commandCallStructure(commandName, args) {
230
148
  }).join(', ');
231
149
  return `${commandName}(${callArgs})`;
232
150
  }
233
- /**
234
- * check if session is based on W3C protocol based on the /session response
235
- * @param {Object} capabilities caps of session response
236
- * @return {Boolean} true if W3C (browser)
237
- */
238
-
239
151
 
240
152
  function isW3C(capabilities) {
241
- /**
242
- * JSONWire protocol doesn't return a property `capabilities`.
243
- * Also check for Appium response as it is using JSONWire protocol for most of the part.
244
- */
245
153
  if (!capabilities) {
246
154
  return false;
247
155
  }
248
- /**
249
- * assume session to be a WebDriver session when
250
- * - capabilities are returned
251
- * (https://w3c.github.io/webdriver/#dfn-new-sessions)
252
- * - it is an Appium session (since Appium is full W3C compliant)
253
- */
254
-
255
156
 
256
157
  const isAppium = capabilities.automationName || capabilities.deviceName;
257
- const hasW3CCaps = capabilities.platformName && capabilities.browserVersion && (
258
- /**
259
- * local safari and BrowserStack don't provide platformVersion therefor
260
- * check also if setWindowRect is provided
261
- */
262
- capabilities.platformVersion || Object.prototype.hasOwnProperty.call(capabilities, 'setWindowRect'));
158
+ const hasW3CCaps = capabilities.platformName && capabilities.browserVersion && (capabilities.platformVersion || Object.prototype.hasOwnProperty.call(capabilities, 'setWindowRect'));
263
159
  return Boolean(hasW3CCaps || isAppium);
264
160
  }
265
- /**
266
- * check if session is run by Chromedriver
267
- * @param {Object} capabilities caps of session response
268
- * @return {Boolean} true if run by Chromedriver
269
- */
270
-
271
161
 
272
162
  function isChrome(caps) {
273
163
  return Boolean(caps.chrome) || Boolean(caps['goog:chromeOptions']);
274
164
  }
275
- /**
276
- * check if current platform is mobile device
277
- *
278
- * @param {Object} caps capabilities
279
- * @return {Boolean} true if platform is mobile device
280
- */
281
-
282
165
 
283
166
  function isMobile(caps) {
284
- return Boolean(typeof caps['appium-version'] !== 'undefined' || typeof caps['device-type'] !== 'undefined' || typeof caps['deviceType'] !== 'undefined' || typeof caps['device-orientation'] !== 'undefined' || typeof caps['deviceOrientation'] !== 'undefined' || typeof caps.deviceName !== 'undefined' || // Check browserName for specific values
285
- caps.browserName === '' || caps.browserName !== undefined && (caps.browserName.toLowerCase() === 'ipad' || caps.browserName.toLowerCase() === 'iphone' || caps.browserName.toLowerCase() === 'android'));
167
+ return Boolean(typeof caps['appium-version'] !== 'undefined' || typeof caps['device-type'] !== 'undefined' || typeof caps['deviceType'] !== 'undefined' || typeof caps['device-orientation'] !== 'undefined' || typeof caps['deviceOrientation'] !== 'undefined' || typeof caps.deviceName !== 'undefined' || caps.browserName === '' || caps.browserName !== undefined && (caps.browserName.toLowerCase() === 'ipad' || caps.browserName.toLowerCase() === 'iphone' || caps.browserName.toLowerCase() === 'android'));
286
168
  }
287
- /**
288
- * check if session is run on iOS device
289
- * @param {Object} capabilities caps of session response
290
- * @return {Boolean} true if run on iOS device
291
- */
292
-
293
169
 
294
170
  function isIOS(caps) {
295
171
  return Boolean(caps.platformName && caps.platformName.match(/iOS/i) || caps.deviceName && caps.deviceName.match(/(iPad|iPhone)/i));
296
172
  }
297
- /**
298
- * check if session is run on Android device
299
- * @param {Object} capabilities caps of session response
300
- * @return {Boolean} true if run on Android device
301
- */
302
-
303
173
 
304
174
  function isAndroid(caps) {
305
175
  return Boolean(caps.platformName && caps.platformName.match(/Android/i) || caps.browserName && caps.browserName.match(/Android/i));
306
176
  }
307
- /**
308
- * detects if session is run on Sauce with extended debugging enabled
309
- * @param {string} hostname hostname of session request
310
- * @param {object} capabilities session capabilities
311
- * @return {Boolean} true if session is running on Sauce with extended debugging enabled
312
- */
313
-
314
177
 
315
178
  function isSauce(hostname, caps) {
316
- return Boolean(hostname && hostname.includes('saucelabs') && (caps.extendedDebugging || caps['sauce:options'] && caps['sauce:options'].extendedDebugging));
179
+ return Boolean(caps.extendedDebugging || caps['sauce:options'] && caps['sauce:options'].extendedDebugging);
317
180
  }
318
- /**
319
- * detects if session is run using Selenium Standalone server
320
- * @param {object} capabilities session capabilities
321
- * @return {Boolean} true if session is run with Selenium Standalone Server
322
- */
323
-
324
181
 
325
182
  function isSeleniumStandalone(caps) {
326
183
  return Boolean(caps['webdriver.remote.sessionid']);
327
184
  }
328
- /**
329
- * returns information about the environment
330
- * @param {Object} hostname name of the host to run the session against
331
- * @param {Object} capabilities caps of session response
332
- * @return {Object} object with environment flags
333
- */
334
-
335
185
 
336
186
  function environmentDetector({
337
187
  hostname,
@@ -348,12 +198,6 @@ function environmentDetector({
348
198
  isSeleniumStandalone: isSeleniumStandalone(capabilities)
349
199
  };
350
200
  }
351
- /**
352
- * helper method to determine the error from webdriver response
353
- * @param {Object} body body object
354
- * @return {Object} error
355
- */
356
-
357
201
 
358
202
  function getErrorFromResponseBody(body) {
359
203
  if (!body) {
@@ -369,8 +213,7 @@ function getErrorFromResponseBody(body) {
369
213
  }
370
214
 
371
215
  return new CustomRequestError(body);
372
- } //Exporting for testability
373
-
216
+ }
374
217
 
375
218
  class CustomRequestError extends Error {
376
219
  constructor(body) {
@@ -384,11 +227,6 @@ class CustomRequestError extends Error {
384
227
  }
385
228
 
386
229
  }
387
- /**
388
- * overwrite native element commands with user defined
389
- * @param {object} propertiesObject propertiesObject
390
- */
391
-
392
230
 
393
231
  exports.CustomRequestError = CustomRequestError;
394
232
 
@@ -412,7 +250,11 @@ function overwriteElementCommands(propertiesObject) {
412
250
  delete propertiesObject[commandName];
413
251
 
414
252
  const newCommand = function (...args) {
415
- return userDefinedCommand.apply(this, [origCommand.bind(this), ...args]);
253
+ const element = this;
254
+ return userDefinedCommand.apply(element, [function origCommandFunction() {
255
+ const context = this || element;
256
+ return origCommand.apply(context, arguments);
257
+ }, ...args]);
416
258
  };
417
259
 
418
260
  propertiesObject[commandName] = {
@@ -426,13 +268,6 @@ function overwriteElementCommands(propertiesObject) {
426
268
  value: {}
427
269
  };
428
270
  }
429
- /**
430
- * return all supported flags and return them in a format so we can attach them
431
- * to the instance protocol
432
- * @param {Object} options driver instance or option object containing these flags
433
- * @return {Object} prototype object
434
- */
435
-
436
271
 
437
272
  function getEnvironmentVars({
438
273
  isW3C,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "webdriver",
3
- "version": "5.11.0",
3
+ "version": "5.11.12",
4
4
  "description": "A Node.js bindings implementation for the W3C WebDriver and Mobile JSONWire Protocol",
5
5
  "author": "Christian Bromann <christian@saucelabs.com>",
6
6
  "homepage": "https://github.com/webdriverio/webdriverio/tree/master/packages/webdriver",
@@ -36,5 +36,5 @@
36
36
  "lodash.merge": "^4.6.1",
37
37
  "request": "^2.83.0"
38
38
  },
39
- "gitHead": "f235de6d9876a1a81bc1007430346d493ba7f579"
39
+ "gitHead": "32d9805f8dee10f0665e31eed14f774aaa48c914"
40
40
  }
@@ -81,7 +81,7 @@
81
81
  }
82
82
  }
83
83
  },
84
- "/session/:sessionId/sauce/ondemand/throttle": {
84
+ "/session/:sessionId/sauce/ondemand/throttle/network": {
85
85
  "POST": {
86
86
  "command": "throttleNetwork",
87
87
  "description": "With network conditioning you can test your site on a variety of network connections, including Edge, 3G, and even offline. You can throttle the data throughput, including the maximum download and upload throughput, and use latency manipulation to enforce a minimum delay in connection round-trip time (RTT).",
@@ -108,6 +108,29 @@
108
108
  }]
109
109
  }
110
110
  },
111
+ "/session/:sessionId/sauce/ondemand/throttle/cpu": {
112
+ "POST": {
113
+ "command": "throttleCPU",
114
+ "description": "You can throttle the CPU in DevTools to understand how your page performs under that constraint.",
115
+ "ref": "https://wiki.saucelabs.com/display/DOCS/Custom+Sauce+Labs+WebDriver+Extensions+for+Network+and+Log+Commands#CustomSauceLabsWebDriverExtensionsforNetworkandLogCommands-ThrottleCPUCapabilities",
116
+ "examples": [
117
+ [
118
+ "// throttle CPU and make it run 4x slower",
119
+ "browser.throttleCPU(4)"
120
+ ],
121
+ [
122
+ "// reset CPU throttling",
123
+ "browser.throttleCPU(0)"
124
+ ]
125
+ ],
126
+ "parameters": [{
127
+ "name": "rate",
128
+ "type": "number",
129
+ "description": "Rate on how much the CPU should get throttled.",
130
+ "required": true
131
+ }]
132
+ }
133
+ },
111
134
  "/session/:sessionId/sauce/ondemand/intercept": {
112
135
  "POST": {
113
136
  "command": "interceptRequest",
package/webdriver.d.ts CHANGED
@@ -66,6 +66,7 @@ declare namespace WebDriver {
66
66
  perfLoggingPrefs?: {
67
67
  [name: string]: any;
68
68
  };
69
+ prefs?: string[];
69
70
  windowTypes?: string[];
70
71
  }
71
72
 
@@ -78,7 +79,7 @@ declare namespace WebDriver {
78
79
  args?: string[],
79
80
  profile?: string,
80
81
  log?: FirefoxLogObject,
81
- prefs: {
82
+ prefs?: {
82
83
  [name: string]: string | number | boolean;
83
84
  }
84
85
  }
@@ -368,6 +369,7 @@ declare namespace WebDriver {
368
369
  interface ClientOptions {
369
370
  capabilities: DesiredCapabilities;
370
371
  isW3C: boolean;
372
+ isChrome: boolean;
371
373
  isAndroid: boolean;
372
374
  isMobile: boolean;
373
375
  isIOS: boolean;
@@ -639,6 +641,7 @@ declare namespace WebDriver {
639
641
  interface Client {
640
642
  getPageLogs(type: string): object;
641
643
  throttleNetwork(condition: (string|object)): void;
644
+ throttleCPU(rate: number): void;
642
645
  interceptRequest(rule: object): void;
643
646
  assertPerformance(name: string, metrics?: string[]): boolean;
644
647
  }