Skip to content

Commit 43e84db

Browse files
committed
Workaround for WinAppDriver
0 parents  commit 43e84db

16 files changed

+6225
-0
lines changed

README.md

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
WebDriver
2+
=========
3+
4+
> A lightweight, non-opinionated implementation of the [WebDriver specification](https://w3c.github.io/webdriver/webdriver-spec.html) including mobile commands supported by [Appium](http://appium.io/)
5+
6+
There are [tons](https://github.com/christian-bromann/awesome-selenium#javascript) of Selenium and WebDriver binding implementations in the Node.js world. Every one of them have an opinionated API and recommended way to use. This binding is the most non-opinionated you will find as it just represents the [WebDriver specification](https://w3c.github.io/webdriver/webdriver-spec.html) and doesn't come with any extra or higher level abstraction. It is lightweight and comes with support for the [WebDriver specification](https://w3c.github.io/webdriver/webdriver-spec.html) and Appiums [Mobile JSONWire Protocol](https://github.com/appium/appium-base-driver/blob/master/docs/mjsonwp/protocol-methods.md).
7+
8+
## Example
9+
10+
The following example demonstrates a simple Google Search scenario:
11+
12+
```js
13+
import WebDriver from 'webdriver'
14+
15+
;(async () => {
16+
const client = await WebDriver.newSession({
17+
path: '/',
18+
capabilities: { browserName: 'firefox' }
19+
})
20+
21+
await client.navigateTo('https://www.google.com/ncr')
22+
23+
const searchInput = await client.findElement('css selector', '#lst-ib')
24+
await client.elementSendKeys(searchInput['element-6066-11e4-a52e-4f735466cecf'], 'WebDriver')
25+
26+
const searchBtn = await client.findElement('css selector', 'input[value="Google Search"]')
27+
await client.elementClick(searchBtn['element-6066-11e4-a52e-4f735466cecf'])
28+
29+
console.log(await client.getTitle()) // outputs "WebDriver - Google Search"
30+
31+
await client.deleteSession()
32+
})()
33+
```
34+
35+
# Configuration
36+
37+
To create a WebDriver session call the `newSession` method on the `WebDriver` class and pass in your configurations:
38+
39+
```js
40+
import WebDriver from 'webdriver'
41+
const client = await WebDriver.newSession(options)
42+
```
43+
44+
The following options are available:
45+
46+
### capabilities
47+
Defines the [capabilities](https://w3c.github.io/webdriver/webdriver-spec.html#capabilities) you want to run in your Selenium session.
48+
49+
Type: `Object`<br>
50+
Required: `true`
51+
52+
### logLevel
53+
Level of logging verbosity.
54+
55+
Type: `String`<br>
56+
Default: *info*<br>
57+
Options: *trace* | *debug* | *info* | *warn* | *error* | *silent*
58+
59+
### protocol
60+
Protocol to use when communicating with the Selenium standalone server (or driver).
61+
62+
Type: `String`<br>
63+
Default: *http*
64+
Options: *http* | *https*
65+
66+
### hostname
67+
Host of your WebDriver server.
68+
69+
Type: `String`<br>
70+
Default: *localhost*
71+
72+
### port
73+
Port your WebDriver server is on.
74+
75+
Type: `Number`<br>
76+
Default: *4444*
77+
78+
### path
79+
Path to WebDriver server.
80+
81+
Type: `String`<br>
82+
Default: */wd/hub*
83+
84+
### baseUrl
85+
Shorten `url` command calls by setting a base url.
86+
87+
Type: `String`<br>
88+
Default: *null*
89+
90+
### connectionRetryTimeout
91+
Timeout for any request to the Selenium server.
92+
93+
Type: `Number`<br>
94+
Default: *90000*
95+
96+
### connectionRetryCount
97+
Count of request retries to the Selenium server.
98+
99+
Type: `Number`<br>
100+
Default: *2*

build/command.js

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
"use strict";
2+
3+
Object.defineProperty(exports, "__esModule", {
4+
value: true
5+
});
6+
exports.default = _default;
7+
8+
var _logger = _interopRequireDefault(require("@wdio/logger"));
9+
10+
var _request = _interopRequireDefault(require("./request"));
11+
12+
var _utils = require("./utils");
13+
14+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
15+
16+
const log = (0, _logger.default)('webdriver');
17+
18+
function _default(method, endpointUri, commandInfo) {
19+
const {
20+
command,
21+
ref,
22+
parameters,
23+
variables = [],
24+
isHubCommand = false
25+
} = commandInfo;
26+
return function protocolCommand(...args) {
27+
let endpoint = endpointUri;
28+
const commandParams = [...variables.map(v => Object.assign(v, {
29+
required: true,
30+
type: 'string'
31+
})), ...parameters];
32+
const commandUsage = `${command}(${commandParams.map(p => p.name).join(', ')})`;
33+
const moreInfo = `\n\nFor more info see ${ref}\n`;
34+
const body = {};
35+
const minAllowedParams = commandParams.filter(param => param.required).length;
36+
37+
if (args.length < minAllowedParams || args.length > commandParams.length) {
38+
const parameterDescription = commandParams.length ? `\n\nProperty Description:\n${commandParams.map(p => ` "${p.name}" (${p.type}): ${p.description}`).join('\n')}` : '';
39+
throw new Error(`Wrong parameters applied for ${command}\n` + `Usage: ${commandUsage}` + parameterDescription + moreInfo);
40+
}
41+
42+
for (const [i, arg] of Object.entries(args)) {
43+
const commandParam = commandParams[i];
44+
45+
if (!(0, _utils.isValidParameter)(arg, commandParam.type)) {
46+
if (typeof arg === 'undefined' && !commandParam.required) {
47+
continue;
48+
}
49+
50+
throw new Error(`Malformed type for "${commandParam.name}" parameter of command ${command}\n` + `Expected: ${commandParam.type}\n` + `Actual: ${(0, _utils.getArgumentType)(arg)}` + moreInfo);
51+
}
52+
53+
if (i < variables.length) {
54+
endpoint = endpoint.replace(`:${commandParams[i].name}`, encodeURIComponent(encodeURIComponent(arg)));
55+
continue;
56+
}
57+
58+
body[commandParams[i].name] = arg;
59+
}
60+
61+
const request = new _request.default(method, endpoint, body, isHubCommand);
62+
this.emit('command', {
63+
method,
64+
endpoint,
65+
body
66+
});
67+
log.info('COMMAND', (0, _utils.commandCallStructure)(command, args));
68+
return request.makeRequest(this.options, this.sessionId).then(result => {
69+
if (result.value != null) {
70+
log.info('RESULT', /screenshot|recording/i.test(command) && typeof result.value === 'string' && result.value.length > 64 ? `${result.value.substr(0, 61)}...` : result.value);
71+
}
72+
73+
this.emit('result', {
74+
method,
75+
endpoint,
76+
body,
77+
result
78+
});
79+
return result.value;
80+
});
81+
};
82+
}

build/constants.js

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
"use strict";
2+
3+
Object.defineProperty(exports, "__esModule", {
4+
value: true
5+
});
6+
exports.DEFAULTS = void 0;
7+
const DEFAULTS = {
8+
protocol: {
9+
type: 'string',
10+
default: 'http',
11+
match: /(http|https)/
12+
},
13+
hostname: {
14+
type: 'string',
15+
default: 'localhost'
16+
},
17+
port: {
18+
type: 'number',
19+
default: 4444
20+
},
21+
path: {
22+
type: 'string',
23+
default: '/wd/hub'
24+
},
25+
queryParams: {
26+
type: 'object'
27+
},
28+
capabilities: {
29+
type: 'object',
30+
required: true
31+
},
32+
logLevel: {
33+
type: 'string',
34+
default: 'info',
35+
match: /(trace|debug|info|warn|error|silent)/
36+
},
37+
connectionRetryTimeout: {
38+
type: 'number',
39+
default: 90000
40+
},
41+
connectionRetryCount: {
42+
type: 'number',
43+
default: 3
44+
},
45+
user: {
46+
type: 'string'
47+
},
48+
key: {
49+
type: 'string'
50+
},
51+
agent: {
52+
type: 'object'
53+
},
54+
headers: {
55+
type: 'object'
56+
}
57+
};
58+
exports.DEFAULTS = DEFAULTS;

build/index.js

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
"use strict";
2+
3+
Object.defineProperty(exports, "__esModule", {
4+
value: true
5+
});
6+
Object.defineProperty(exports, "webdriverMonad", {
7+
enumerable: true,
8+
get: function () {
9+
return _monad.default;
10+
}
11+
});
12+
Object.defineProperty(exports, "getPrototype", {
13+
enumerable: true,
14+
get: function () {
15+
return _utils.getPrototype;
16+
}
17+
});
18+
exports.default = void 0;
19+
20+
var _logger = _interopRequireDefault(require("@wdio/logger"));
21+
22+
var _config = require("@wdio/config");
23+
24+
var _monad = _interopRequireDefault(require("./monad"));
25+
26+
var _request = _interopRequireDefault(require("./request"));
27+
28+
var _constants = require("./constants");
29+
30+
var _utils = require("./utils");
31+
32+
var _webdriver = _interopRequireDefault(require("../protocol/webdriver.json"));
33+
34+
var _jsonwp = _interopRequireDefault(require("../protocol/jsonwp.json"));
35+
36+
var _mjsonwp = _interopRequireDefault(require("../protocol/mjsonwp.json"));
37+
38+
var _appium = _interopRequireDefault(require("../protocol/appium.json"));
39+
40+
var _chromium = _interopRequireDefault(require("../protocol/chromium.json"));
41+
42+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
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+
50+
class WebDriver {
51+
static async newSession(options = {}, modifier, userPrototype = {}, commandWrapper) {
52+
const params = (0, _config.validateConfig)(_constants.DEFAULTS, options);
53+
54+
if (!options.logLevels || !options.logLevels['webdriver']) {
55+
_logger.default.setLevel('webdriver', params.logLevel);
56+
}
57+
58+
const [w3cCaps, jsonwpCaps] = params.capabilities && params.capabilities.alwaysMatch ? [params.capabilities, params.capabilities.alwaysMatch] : [{
59+
alwaysMatch: params.capabilities,
60+
firstMatch: [{}]
61+
}, params.capabilities];
62+
const sessionRequest = new _request.default('POST', '/session', {
63+
capabilities: w3cCaps,
64+
desiredCapabilities: jsonwpCaps
65+
});
66+
const response = await sessionRequest.makeRequest(params);
67+
params.requestedCapabilities = {
68+
w3cCaps,
69+
jsonwpCaps
70+
};
71+
params.capabilities = response.value.capabilities || response.value;
72+
const environment = (0, _utils.environmentDetector)(params);
73+
const environmentPrototype = (0, _utils.getEnvironmentVars)(environment);
74+
const protocolCommands = (0, _utils.getPrototype)(environment);
75+
76+
const prototype = _objectSpread({}, protocolCommands, {}, environmentPrototype, {}, userPrototype);
77+
78+
const monad = (0, _monad.default)(params, modifier, prototype);
79+
return monad(response.value.sessionId || response.sessionId, commandWrapper);
80+
}
81+
82+
static attachToSession(options = {}, modifier, userPrototype = {}, commandWrapper) {
83+
if (typeof options.sessionId !== 'string') {
84+
throw new Error('sessionId is required to attach to existing session');
85+
}
86+
87+
if (options.logLevel !== undefined) {
88+
_logger.default.setLevel('webdriver', options.logLevel);
89+
}
90+
91+
options.capabilities = options.capabilities || {};
92+
options.isW3C = options.isW3C === false ? false : true;
93+
const environmentPrototype = (0, _utils.getEnvironmentVars)(options);
94+
const protocolCommands = (0, _utils.getPrototype)(options);
95+
96+
const prototype = _objectSpread({}, protocolCommands, {}, environmentPrototype, {}, userPrototype);
97+
98+
const monad = (0, _monad.default)(options, modifier, prototype);
99+
return monad(options.sessionId, commandWrapper);
100+
}
101+
102+
static get WebDriver() {
103+
return WebDriver;
104+
}
105+
106+
static get DEFAULTS() {
107+
return _constants.DEFAULTS;
108+
}
109+
110+
static get WebDriverProtocol() {
111+
return _webdriver.default;
112+
}
113+
114+
static get JsonWProtocol() {
115+
return _jsonwp.default;
116+
}
117+
118+
static get MJsonWProtocol() {
119+
return _mjsonwp.default;
120+
}
121+
122+
static get AppiumProtocol() {
123+
return _appium.default;
124+
}
125+
126+
static get ChromiumProtocol() {
127+
return _chromium.default;
128+
}
129+
130+
}
131+
132+
exports.default = WebDriver;

0 commit comments

Comments
 (0)