selenium-webdriver 3.6.0 → 4.0.0-alpha.1
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/CHANGES.md +181 -0
- package/README.md +25 -27
- package/chrome.js +113 -127
- package/edge.js +42 -117
- package/example/chrome_android.js +8 -8
- package/example/chrome_mobile_emulation.js +8 -8
- package/example/firefox_channels.js +22 -18
- package/example/google_search_test.js +39 -26
- package/example/logging.js +39 -6
- package/firefox.js +769 -0
- package/http/index.js +41 -4
- package/http/util.js +11 -6
- package/ie.js +56 -97
- package/index.js +178 -114
- package/jasmine.json +11 -0
- package/lib/actions.js +68 -63
- package/lib/by.js +1 -1
- package/lib/capabilities.js +255 -192
- package/lib/command.js +26 -50
- package/lib/error.js +85 -44
- package/lib/http.js +133 -110
- package/lib/input.js +1044 -8
- package/lib/promise.js +222 -3323
- package/lib/proxy.js +134 -39
- package/lib/test/bootstrap_jasmine.js +28 -0
- package/lib/test/data/actions/click.html +24 -0
- package/lib/test/data/actions/drag.html +77 -0
- package/lib/test/data/actions/record_click.html +21 -0
- package/lib/test/data/chrome/download.html +2 -0
- package/lib/test/data/inputs.html +42 -0
- package/lib/test/data/selectPage.html +12 -0
- package/lib/test/data/simpleTest.html +5 -0
- package/lib/test/data/upload_invisible.html +45 -0
- package/lib/test/fileserver.js +23 -19
- package/lib/test/index.js +30 -212
- package/lib/test/resources.js +0 -1
- package/lib/webdriver.js +675 -936
- package/net/index.js +21 -25
- package/net/portprober.js +51 -68
- package/package.json +5 -3
- package/remote/index.js +5 -19
- package/safari.js +41 -128
- package/test/actions_test.js +173 -18
- package/test/{session_test.js → builder_test.js} +44 -37
- package/test/chrome/devtools_test.js +93 -0
- package/test/chrome/options_test.js +48 -154
- package/test/chrome/service_test.js +7 -7
- package/test/cookie_test.js +77 -63
- package/test/element_finding_test.js +206 -201
- package/test/execute_script_test.js +115 -114
- package/test/fingerprint_test.js +16 -15
- package/test/firefox_test.js +251 -0
- package/test/http/http_test.js +0 -1
- package/test/http/util_test.js +2 -2
- package/test/io/zip_test.js +6 -7
- package/test/lib/error_test.js +34 -7
- package/test/lib/http_test.js +8 -128
- package/test/lib/input_test.js +1379 -0
- package/test/lib/promise_test.js +473 -908
- package/test/lib/webdriver_test.js +296 -868
- package/test/logging_test.js +37 -43
- package/test/page_loading_test.js +71 -81
- package/test/proxy_test.js +64 -72
- package/test/rect_test.js +14 -25
- package/test/remote_test.js +10 -29
- package/test/safari_test.js +7 -71
- package/test/stale_element_test.js +20 -23
- package/test/tag_name_test.js +10 -9
- package/test/upload_test.js +27 -35
- package/test/window_test.js +72 -85
- package/testing/index.js +386 -314
- package/.npmignore +0 -2
- package/example/async_await_test.js +0 -69
- package/example/google_search_generator.js +0 -47
- package/example/parallel_flows.js +0 -51
- package/firefox/binary.js +0 -347
- package/firefox/extension.js +0 -224
- package/firefox/index.js +0 -576
- package/firefox/profile.js +0 -311
- package/lib/events.js +0 -210
- package/lib/test/data/firefox/jetpack-sample.xpi +0 -0
- package/lib/test/data/firefox/sample.xpi +0 -0
- package/lib/test/promise.js +0 -79
- package/opera.js +0 -405
- package/phantomjs.js +0 -282
- package/test/firefox/extension_test.js +0 -120
- package/test/firefox/firefox_test.js +0 -244
- package/test/firefox/profile_test.js +0 -140
- package/test/lib/events_test.js +0 -177
- package/test/lib/promise_aplus_test.js +0 -78
- package/test/lib/promise_error_test.js +0 -884
- package/test/lib/promise_flow_test.js +0 -2288
- package/test/lib/promise_generator_test.js +0 -310
- package/test/phantomjs/execute_phantomjs_test.js +0 -59
- package/test/testing/assert_test.js +0 -373
- package/test/testing/index_test.js +0 -224
- package/testing/assert.js +0 -378
package/testing/index.js
CHANGED
|
@@ -16,51 +16,8 @@
|
|
|
16
16
|
// under the License.
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
|
-
* @fileoverview Provides
|
|
20
|
-
* [
|
|
21
|
-
*
|
|
22
|
-
* - after
|
|
23
|
-
* - afterEach
|
|
24
|
-
* - before
|
|
25
|
-
* - beforeEach
|
|
26
|
-
* - it
|
|
27
|
-
* - it.only
|
|
28
|
-
* - it.skip
|
|
29
|
-
* - xit
|
|
30
|
-
*
|
|
31
|
-
* Each of the wrapped functions support generator functions. If the generator
|
|
32
|
-
* {@linkplain ../lib/promise.consume yields a promise}, the test will wait
|
|
33
|
-
* for that promise to resolve before invoking the next iteration of the
|
|
34
|
-
* generator:
|
|
35
|
-
*
|
|
36
|
-
* test.it('generators', function*() {
|
|
37
|
-
* let x = yield Promise.resolve(1);
|
|
38
|
-
* assert.equal(x, 1);
|
|
39
|
-
* });
|
|
40
|
-
*
|
|
41
|
-
* The provided wrappers leverage the {@link webdriver.promise.ControlFlow}
|
|
42
|
-
* to simplify writing asynchronous tests:
|
|
43
|
-
*
|
|
44
|
-
* var {Builder, By, Key, until} = require('selenium-webdriver');
|
|
45
|
-
* var test = require('selenium-webdriver/testing');
|
|
46
|
-
*
|
|
47
|
-
* test.describe('Google Search', function() {
|
|
48
|
-
* var driver;
|
|
49
|
-
*
|
|
50
|
-
* test.before(function() {
|
|
51
|
-
* driver = new Builder().forBrowser('firefox').build();
|
|
52
|
-
* });
|
|
53
|
-
*
|
|
54
|
-
* test.after(function() {
|
|
55
|
-
* driver.quit();
|
|
56
|
-
* });
|
|
57
|
-
*
|
|
58
|
-
* test.it('should append query to title', function() {
|
|
59
|
-
* driver.get('http://www.google.com/ncr');
|
|
60
|
-
* driver.findElement(By.name('q')).sendKeys('webdriver', Key.RETURN);
|
|
61
|
-
* driver.wait(until.titleIs('webdriver - Google Search'), 1000);
|
|
62
|
-
* });
|
|
63
|
-
* });
|
|
19
|
+
* @fileoverview Provides extensions for
|
|
20
|
+
* [Jasmine](https://jasmine.github.io) and [Mocha](https://mochajs.org).
|
|
64
21
|
*
|
|
65
22
|
* You may conditionally suppress a test function using the exported
|
|
66
23
|
* "ignore" function. If the provided predicate returns true, the attached
|
|
@@ -75,352 +32,467 @@
|
|
|
75
32
|
|
|
76
33
|
'use strict';
|
|
77
34
|
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
process.env['SELENIUM_PROMISE_MANAGER'] = initial;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
})();
|
|
35
|
+
const {isatty} = require('tty');
|
|
36
|
+
|
|
37
|
+
const chrome = require('../chrome');
|
|
38
|
+
const edge = require('../edge');
|
|
39
|
+
const firefox = require('../firefox');
|
|
40
|
+
const ie = require('../ie');
|
|
41
|
+
const remote = require('../remote');
|
|
42
|
+
const safari = require('../safari');
|
|
43
|
+
const {Browser} = require('../lib/capabilities');
|
|
44
|
+
const {Builder} = require('../index');
|
|
92
45
|
|
|
93
46
|
|
|
94
47
|
/**
|
|
95
|
-
*
|
|
96
|
-
* @
|
|
97
|
-
* @return {!Function} The wrapped function.
|
|
48
|
+
* Describes a browser targetted by a {@linkplain suite test suite}.
|
|
49
|
+
* @record
|
|
98
50
|
*/
|
|
99
|
-
function
|
|
100
|
-
return function() {
|
|
101
|
-
fn();
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
|
|
51
|
+
function TargetBrowser() {}
|
|
105
52
|
|
|
106
53
|
/**
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
* continuing.
|
|
110
|
-
* @param {!Function} globalFn The function to wrap.
|
|
111
|
-
* @return {!Function} The new function.
|
|
54
|
+
* The {@linkplain Browser name} of the targetted browser.
|
|
55
|
+
* @type {string}
|
|
112
56
|
*/
|
|
113
|
-
|
|
114
|
-
return function() {
|
|
115
|
-
if (arguments.length === 1) {
|
|
116
|
-
return globalFn(wrapArgument(arguments[0]));
|
|
57
|
+
TargetBrowser.prototype.name;
|
|
117
58
|
|
|
118
|
-
|
|
119
|
-
|
|
59
|
+
/**
|
|
60
|
+
* The specific version of the targetted browser, if any.
|
|
61
|
+
* @type {(string|undefined)}
|
|
62
|
+
*/
|
|
63
|
+
TargetBrowser.prototype.version;
|
|
120
64
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
65
|
+
/**
|
|
66
|
+
* The specific {@linkplain ../lib/capabilities.Platform platform} for the
|
|
67
|
+
* targetted browser, if any.
|
|
68
|
+
* @type {(string|undefined)}.
|
|
69
|
+
*/
|
|
70
|
+
TargetBrowser.prototype.platform;
|
|
126
71
|
|
|
127
72
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
}
|
|
132
|
-
return value;
|
|
73
|
+
/** @suppress {checkTypes} */
|
|
74
|
+
function color(c, s) {
|
|
75
|
+
return isatty(process.stdout) ? `\u001b[${c}m${s}\u001b[0m` : s;
|
|
133
76
|
}
|
|
77
|
+
function green(s) { return color(32, s); }
|
|
78
|
+
function cyan(s) { return color(36, s); }
|
|
79
|
+
function info(msg) { console.info(`${green('[INFO]')} ${msg}`); }
|
|
80
|
+
function warn(msg) { console.warn(`${cyan('[WARNING]')} ${msg}`); }
|
|
134
81
|
|
|
135
82
|
|
|
136
83
|
/**
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
* Should preserve the semantics of Mocha's Runnable.prototype.run (See
|
|
141
|
-
* https://github.com/mochajs/mocha/blob/master/lib/runnable.js#L192)
|
|
84
|
+
* Extracts the browsers for a test suite to target from the `SELENIUM_BROWSER`
|
|
85
|
+
* environment variable.
|
|
142
86
|
*
|
|
143
|
-
* @
|
|
144
|
-
* @return {!Function}
|
|
87
|
+
* @return {!Array<!TargetBrowser>} the browsers to target.
|
|
145
88
|
*/
|
|
146
|
-
function
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
throw TypeError(
|
|
151
|
-
'generator-based tests must not take a callback; for async testing,'
|
|
152
|
-
+ ' return a promise (or yield on a promise)');
|
|
89
|
+
function getBrowsersToTestFromEnv() {
|
|
90
|
+
let browsers = process.env['SELENIUM_BROWSER'];
|
|
91
|
+
if (!browsers) {
|
|
92
|
+
return [];
|
|
153
93
|
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
resolve(promise.consume(fn, this));
|
|
162
|
-
} else {
|
|
163
|
-
resolve(fn.call(this));
|
|
164
|
-
}
|
|
165
|
-
} catch (ex) {
|
|
166
|
-
reject(ex);
|
|
167
|
-
}
|
|
168
|
-
};
|
|
169
|
-
|
|
170
|
-
if (!promise.USE_PROMISE_MANAGER) {
|
|
171
|
-
new Promise(runTest).then(seal(done), done);
|
|
172
|
-
return;
|
|
94
|
+
return browsers.split(',').map(spec => {
|
|
95
|
+
const parts = spec.split(/:/, 3);
|
|
96
|
+
let name = parts[0];
|
|
97
|
+
if (name === 'ie') {
|
|
98
|
+
name = Browser.IE;
|
|
99
|
+
} else if (name === 'edge') {
|
|
100
|
+
name = Browser.EDGE;
|
|
173
101
|
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
runnable.callback = function() {
|
|
178
|
-
flow.reset();
|
|
179
|
-
return mochaCallback.apply(this, arguments);
|
|
180
|
-
};
|
|
181
|
-
|
|
182
|
-
flow.execute(function controlFlowExecute() {
|
|
183
|
-
return new promise.Promise(function(fulfill, reject) {
|
|
184
|
-
return runTest(fulfill, reject);
|
|
185
|
-
}, flow);
|
|
186
|
-
}, runnable.fullTitle()).then(seal(done), done);
|
|
102
|
+
let version = parts[1];
|
|
103
|
+
let platform = parts[2];
|
|
104
|
+
return {name, version, platform};
|
|
187
105
|
});
|
|
188
|
-
|
|
189
|
-
ret.toString = function() {
|
|
190
|
-
return fn.toString();
|
|
191
|
-
};
|
|
192
|
-
|
|
193
|
-
return ret;
|
|
194
106
|
}
|
|
195
107
|
|
|
196
108
|
|
|
197
109
|
/**
|
|
198
|
-
*
|
|
199
|
-
*
|
|
200
|
-
* @param {function(): boolean} predicateFn A predicate to call to determine
|
|
201
|
-
* if the test should be suppressed. This function MUST be synchronous.
|
|
202
|
-
* @return {!Object} An object with wrapped versions of {@link #it()} and
|
|
203
|
-
* {@link #describe()} that ignore tests as indicated by the predicate.
|
|
110
|
+
* @return {!Array<!TargetBrowser>} the browsers available for testing on this
|
|
111
|
+
* system.
|
|
204
112
|
*/
|
|
205
|
-
function
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
113
|
+
function getAvailableBrowsers() {
|
|
114
|
+
info(`Searching for WebDriver executables installed on the current system...`);
|
|
115
|
+
|
|
116
|
+
let targets = [
|
|
117
|
+
[chrome.locateSynchronously, Browser.CHROME],
|
|
118
|
+
[edge.locateSynchronously, Browser.EDGE],
|
|
119
|
+
[firefox.locateSynchronously, Browser.FIREFOX],
|
|
120
|
+
[ie.locateSynchronously, Browser.IE],
|
|
121
|
+
[safari.locateSynchronously, Browser.SAFARI],
|
|
122
|
+
];
|
|
123
|
+
|
|
124
|
+
let availableBrowsers = [];
|
|
125
|
+
for (let pair of targets) {
|
|
126
|
+
const fn = pair[0];
|
|
127
|
+
const name = pair[1];
|
|
128
|
+
if (fn()) {
|
|
129
|
+
info(`... located ${name}`);
|
|
130
|
+
availableBrowsers.push({name});
|
|
131
|
+
}
|
|
132
|
+
}
|
|
216
133
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
if (predicateFn()) {
|
|
220
|
-
onSkip(title, fn);
|
|
221
|
-
} else {
|
|
222
|
-
onRun(title, fn);
|
|
223
|
-
}
|
|
224
|
-
};
|
|
134
|
+
if (availableBrowsers.length === 0) {
|
|
135
|
+
warn(`Unable to locate any WebDriver executables for testing`);
|
|
225
136
|
}
|
|
137
|
+
|
|
138
|
+
return availableBrowsers;
|
|
226
139
|
}
|
|
227
140
|
|
|
141
|
+
let wasInit;
|
|
142
|
+
let targetBrowsers;
|
|
143
|
+
let seleniumJar;
|
|
144
|
+
let seleniumUrl;
|
|
145
|
+
let seleniumServer;
|
|
228
146
|
|
|
229
147
|
/**
|
|
230
|
-
*
|
|
231
|
-
* @
|
|
232
|
-
*
|
|
148
|
+
* Initializes this module by determining which browsers a
|
|
149
|
+
* {@linkplain ./index.suite test suite} should run against. The default
|
|
150
|
+
* behavior is to run tests against every browser with a WebDriver executables
|
|
151
|
+
* (chromedriver, firefoxdriver, etc.) are installed on the system by `PATH`.
|
|
152
|
+
*
|
|
153
|
+
* Specific browsers can be selected at runtime by setting the
|
|
154
|
+
* `SELENIUM_BROWSER` environment variable. This environment variable has the
|
|
155
|
+
* same semantics as with the WebDriver {@link ../index.Builder Builder},
|
|
156
|
+
* except you may use a comma-delimited list to run against multiple browsers:
|
|
157
|
+
*
|
|
158
|
+
* SELENIUM_BROWSER=chrome,firefox mocha --recursive tests/
|
|
159
|
+
*
|
|
160
|
+
* The `SELENIUM_REMOTE_URL` environment variable may be set to configure tests
|
|
161
|
+
* to run againt an externally managed (usually remote) Selenium server. When
|
|
162
|
+
* set, the WebDriver builder provided by each
|
|
163
|
+
* {@linkplain TestEnvironment#builder TestEnvironment} will automatically be
|
|
164
|
+
* configured to use this server instead of starting a browser drively locally.
|
|
165
|
+
*
|
|
166
|
+
* The `SELENIUM_SERVER_JAR` environment variable may be set to the path of a
|
|
167
|
+
* standalone Selenium server on the local machine that should be used for
|
|
168
|
+
* WebDriver sessions. When set, the WebDriver builder provided by each
|
|
169
|
+
* {@linkplain TestEnvironment} will automatically be configured to use the
|
|
170
|
+
* started server instead of using a browser driver directly. It should only be
|
|
171
|
+
* necessary to set the `SELENIUM_SERVER_JAR` when testing locally against
|
|
172
|
+
* browsers not natively supported by the WebDriver
|
|
173
|
+
* {@link ../index.Builder Builder}.
|
|
174
|
+
*
|
|
175
|
+
* When either of the `SELENIUM_REMOTE_URL` or `SELENIUM_SERVER_JAR` environment
|
|
176
|
+
* variables are set, the `SELENIUM_BROWSER` variable must also be set.
|
|
177
|
+
*
|
|
178
|
+
* @param {boolean=} force whether to force this module to re-initialize and
|
|
179
|
+
* scan `process.env` again to determine which browsers to run tests
|
|
180
|
+
* against.
|
|
233
181
|
*/
|
|
234
|
-
function
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
if (type !== 'function') {
|
|
238
|
-
throw TypeError(
|
|
239
|
-
`Expected global.${name} to be a function, but is ${type}. `
|
|
240
|
-
+ 'This can happen if you try using this module when running '
|
|
241
|
-
+ 'with node directly instead of using the mocha executable');
|
|
182
|
+
function init(force = false) {
|
|
183
|
+
if (wasInit && !force) {
|
|
184
|
+
return;
|
|
242
185
|
}
|
|
243
|
-
|
|
244
|
-
}
|
|
186
|
+
wasInit = true;
|
|
245
187
|
|
|
188
|
+
// If force re-init, kill the current server if there is one.
|
|
189
|
+
if (seleniumServer) {
|
|
190
|
+
seleniumServer.kill();
|
|
191
|
+
seleniumServer = null;
|
|
192
|
+
}
|
|
246
193
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
it: null,
|
|
253
|
-
itOnly: null,
|
|
254
|
-
xit: null
|
|
255
|
-
};
|
|
194
|
+
seleniumJar = process.env['SELENIUM_SERVER_JAR'];
|
|
195
|
+
seleniumUrl = process.env['SELENIUM_REMOTE_URL'];
|
|
196
|
+
if (seleniumJar) {
|
|
197
|
+
info(`Using Selenium server jar: ${seleniumJar}`);
|
|
198
|
+
}
|
|
256
199
|
|
|
200
|
+
if (seleniumUrl) {
|
|
201
|
+
info(`Using Selenium remote end: ${seleniumUrl}`);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (seleniumJar && seleniumUrl) {
|
|
205
|
+
throw Error(
|
|
206
|
+
'Ambiguous test configuration: both SELENIUM_REMOTE_URL'
|
|
207
|
+
+ ' && SELENIUM_SERVER_JAR environment variables are set');
|
|
208
|
+
}
|
|
257
209
|
|
|
258
|
-
|
|
259
|
-
if (
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
210
|
+
const envBrowsers = getBrowsersToTestFromEnv();
|
|
211
|
+
if ((seleniumJar || seleniumUrl) && envBrowsers.length === 0) {
|
|
212
|
+
throw Error(
|
|
213
|
+
'Ambiguous test configuration: when either the SELENIUM_REMOTE_URL or'
|
|
214
|
+
+ ' SELENIUM_SERVER_JAR environment variable is set, the'
|
|
215
|
+
+ ' SELENIUM_BROWSER variable must also be set.');
|
|
263
216
|
}
|
|
217
|
+
|
|
218
|
+
targetBrowsers =
|
|
219
|
+
envBrowsers.length > 0 ? envBrowsers : getAvailableBrowsers();
|
|
220
|
+
info(`Running tests against [${targetBrowsers.map(b => b.name).join(', ')}]`);
|
|
221
|
+
|
|
222
|
+
after(function() {
|
|
223
|
+
if (seleniumServer) {
|
|
224
|
+
return seleniumServer.kill();
|
|
225
|
+
}
|
|
226
|
+
});
|
|
264
227
|
}
|
|
265
228
|
|
|
266
229
|
|
|
267
230
|
|
|
268
|
-
|
|
231
|
+
const TARGET_MAP = /** !WeakMap<!Environment, !TargetBrowser> */new WeakMap;
|
|
232
|
+
const URL_MAP =
|
|
233
|
+
/** !WeakMap<!Environment, ?(string|remote.SeleniumServer)> */new WeakMap;
|
|
269
234
|
|
|
270
235
|
|
|
271
236
|
/**
|
|
272
|
-
*
|
|
273
|
-
*
|
|
237
|
+
* Defines the environment a {@linkplain suite test suite} is running against.
|
|
238
|
+
* @final
|
|
274
239
|
*/
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
240
|
+
class Environment {
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* @param {!TargetBrowser} browser the browser targetted in this environment.
|
|
244
|
+
* @param {?(string|remote.SeleniumServer)=} url remote URL of an existing
|
|
245
|
+
* Selenium server to test against.
|
|
246
|
+
*/
|
|
247
|
+
constructor(browser, url = undefined) {
|
|
248
|
+
browser =
|
|
249
|
+
/** @type {!TargetBrowser} */(Object.seal(Object.assign({}, browser)));
|
|
250
|
+
|
|
251
|
+
TARGET_MAP.set(this, browser);
|
|
252
|
+
URL_MAP.set(this, url || null);
|
|
253
|
+
}
|
|
278
254
|
|
|
255
|
+
/** @return {!TargetBrowser} the target browser for this test environment. */
|
|
256
|
+
get browser() {
|
|
257
|
+
return TARGET_MAP.get(this);
|
|
258
|
+
}
|
|
279
259
|
|
|
280
|
-
/**
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
260
|
+
/**
|
|
261
|
+
* Returns a predicate function that will suppress tests in this environment
|
|
262
|
+
* if the {@linkplain #browser current browser} is in the list of
|
|
263
|
+
* `browsersToIgnore`.
|
|
264
|
+
*
|
|
265
|
+
* @param {...(string|!Browser)} browsersToIgnore the browsers that should
|
|
266
|
+
* be ignored.
|
|
267
|
+
* @return {function(): boolean} a new predicate function.
|
|
268
|
+
*/
|
|
269
|
+
browsers(...browsersToIgnore) {
|
|
270
|
+
return () => browsersToIgnore.indexOf(this.browser.name) != -1;
|
|
271
|
+
}
|
|
290
272
|
|
|
273
|
+
/**
|
|
274
|
+
* @return {!Builder} a new WebDriver builder configured to target this
|
|
275
|
+
* environment's {@linkplain #browser browser}.
|
|
276
|
+
*/
|
|
277
|
+
builder() {
|
|
278
|
+
const browser = this.browser;
|
|
279
|
+
const urlOrServer = URL_MAP.get(this);
|
|
280
|
+
|
|
281
|
+
const builder = new Builder();
|
|
282
|
+
builder.disableEnvironmentOverrides();
|
|
283
|
+
|
|
284
|
+
const realBuild = builder.build;
|
|
285
|
+
builder.build = function() {
|
|
286
|
+
builder.forBrowser(browser.name, browser.version, browser.platform);
|
|
287
|
+
if (typeof urlOrServer === 'string') {
|
|
288
|
+
builder.usingServer(urlOrServer);
|
|
289
|
+
} else if (urlOrServer) {
|
|
290
|
+
builder.usingServer(urlOrServer.address());
|
|
291
|
+
}
|
|
292
|
+
return realBuild.call(builder);
|
|
293
|
+
};
|
|
291
294
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
* @param {string} name The suite name.
|
|
296
|
-
* @param {function()=} opt_fn The suite function, or `undefined` to define
|
|
297
|
-
* a pending test suite.
|
|
298
|
-
*/
|
|
299
|
-
exports.describe.only = function(name, opt_fn) {
|
|
300
|
-
let desc = getMochaGlobal('describe');
|
|
301
|
-
return opt_fn ? desc.only(name, opt_fn) : desc.only(name);
|
|
302
|
-
};
|
|
295
|
+
return builder;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
303
298
|
|
|
304
299
|
|
|
305
300
|
/**
|
|
306
|
-
*
|
|
307
|
-
* @
|
|
308
|
-
* @param {function()=} opt_fn The suite function, or `undefined` to define
|
|
309
|
-
* a pending test suite.
|
|
301
|
+
* Configuration options for a {@linkplain ./index.suite test suite}.
|
|
302
|
+
* @record
|
|
310
303
|
*/
|
|
311
|
-
|
|
312
|
-
let fn = getMochaGlobal('describe');
|
|
313
|
-
return opt_fn ? fn.skip(name, opt_fn) : fn.skip(name);
|
|
314
|
-
};
|
|
315
|
-
|
|
304
|
+
function SuiteOptions() {}
|
|
316
305
|
|
|
317
306
|
/**
|
|
318
|
-
*
|
|
319
|
-
* @
|
|
320
|
-
* @param {function()=} opt_fn The suite function, or `undefined` to define
|
|
321
|
-
* a pending test suite.
|
|
307
|
+
* The browsers to run the test suite against.
|
|
308
|
+
* @type {!Array<!(Browser|TargetBrowser)>}
|
|
322
309
|
*/
|
|
323
|
-
|
|
324
|
-
let fn = getMochaGlobal('xdescribe');
|
|
325
|
-
return opt_fn ? fn(name, opt_fn) : fn(name);
|
|
326
|
-
};
|
|
310
|
+
SuiteOptions.prototype.browsers;
|
|
327
311
|
|
|
328
312
|
|
|
329
|
-
|
|
330
|
-
* Register a function to call after the current suite finishes.
|
|
331
|
-
* @param {function()} fn .
|
|
332
|
-
*/
|
|
333
|
-
exports.after = function(fn) {
|
|
334
|
-
if (!WRAPPED.after) {
|
|
335
|
-
WRAPPED.after = wrapped(getMochaGlobal('after'));
|
|
336
|
-
}
|
|
337
|
-
WRAPPED.after(fn);
|
|
338
|
-
};
|
|
313
|
+
let inSuite = false;
|
|
339
314
|
|
|
340
315
|
|
|
341
316
|
/**
|
|
342
|
-
*
|
|
343
|
-
*
|
|
317
|
+
* Defines a test suite by calling the provided function once for each of the
|
|
318
|
+
* target browsers. If a suite is not limited to a specific set of browsers in
|
|
319
|
+
* the provided {@linkplain ./index.SuiteOptions suite options}, the suite will
|
|
320
|
+
* be configured to run against each of the {@linkplain ./index.init runtime
|
|
321
|
+
* target browsers}.
|
|
322
|
+
*
|
|
323
|
+
* Sample usage:
|
|
324
|
+
*
|
|
325
|
+
* const {By, Key, until} = require('selenium-webdriver');
|
|
326
|
+
* const {suite} = require('selenium-webdriver/testing');
|
|
327
|
+
*
|
|
328
|
+
* suite(function(env) {
|
|
329
|
+
* describe('Google Search', function() {
|
|
330
|
+
* let driver;
|
|
331
|
+
*
|
|
332
|
+
* before(async function() {
|
|
333
|
+
* driver = await env.builder().build();
|
|
334
|
+
* });
|
|
335
|
+
*
|
|
336
|
+
* after(() => driver.quit());
|
|
337
|
+
*
|
|
338
|
+
* it('demo', async function() {
|
|
339
|
+
* await driver.get('http://www.google.com/ncr');
|
|
340
|
+
*
|
|
341
|
+
* let q = await driver.findElement(By.name('q'));
|
|
342
|
+
* await q.sendKeys('webdriver', Key.RETURN);
|
|
343
|
+
* await driver.wait(
|
|
344
|
+
* until.titleIs('webdriver - Google Search'), 1000);
|
|
345
|
+
* });
|
|
346
|
+
* });
|
|
347
|
+
* });
|
|
348
|
+
*
|
|
349
|
+
* By default, this example suite will run against every WebDriver-enabled
|
|
350
|
+
* browser on the current system. Alternatively, the `SELENIUM_BROWSER`
|
|
351
|
+
* environment variable may be used to run against a specific browser:
|
|
352
|
+
*
|
|
353
|
+
* SELENIUM_BROWSER=firefox mocha -t 120000 example_test.js
|
|
354
|
+
*
|
|
355
|
+
* @param {function(!Environment)} fn the function to call to build the test
|
|
356
|
+
* suite.
|
|
357
|
+
* @param {SuiteOptions=} options configuration options.
|
|
344
358
|
*/
|
|
345
|
-
|
|
346
|
-
if (
|
|
347
|
-
|
|
359
|
+
function suite(fn, options = undefined) {
|
|
360
|
+
if (inSuite) {
|
|
361
|
+
throw Error('Calls to suite() may not be nested');
|
|
348
362
|
}
|
|
349
|
-
|
|
350
|
-
|
|
363
|
+
try {
|
|
364
|
+
init();
|
|
365
|
+
inSuite = true;
|
|
366
|
+
|
|
367
|
+
const suiteBrowsers = new Map;
|
|
368
|
+
if (options && options.browsers) {
|
|
369
|
+
for (let browser of options.browsers) {
|
|
370
|
+
if (typeof browser === 'string') {
|
|
371
|
+
suiteBrowsers.set(browser, {name: browser});
|
|
372
|
+
} else {
|
|
373
|
+
suiteBrowsers.set(browser.name, browser);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
for (let browser of targetBrowsers) {
|
|
379
|
+
if (suiteBrowsers.size > 0 && !suiteBrowsers.has(browser.name)) {
|
|
380
|
+
continue;
|
|
381
|
+
}
|
|
351
382
|
|
|
383
|
+
describe(`[${browser.name}]`, function() {
|
|
384
|
+
if (!seleniumUrl && seleniumJar && !seleniumServer) {
|
|
385
|
+
seleniumServer = new remote.SeleniumServer(seleniumJar);
|
|
352
386
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
if (!WRAPPED.before) {
|
|
359
|
-
WRAPPED.before = wrapped(getMochaGlobal('before'));
|
|
360
|
-
}
|
|
361
|
-
WRAPPED.before(fn);
|
|
362
|
-
};
|
|
387
|
+
const startTimeout = 65 * 1000;
|
|
388
|
+
function startSelenium() {
|
|
389
|
+
if (typeof this.timeout === 'function') {
|
|
390
|
+
this.timeout(startTimeout); // For mocha.
|
|
391
|
+
}
|
|
363
392
|
|
|
364
|
-
|
|
365
|
-
*
|
|
366
|
-
|
|
367
|
-
*/
|
|
368
|
-
exports.beforeEach = function(fn) {
|
|
369
|
-
if (!WRAPPED.beforeEach) {
|
|
370
|
-
WRAPPED.beforeEach = wrapped(getMochaGlobal('beforeEach'));
|
|
371
|
-
}
|
|
372
|
-
WRAPPED.beforeEach(fn);
|
|
373
|
-
};
|
|
393
|
+
info(`Starting selenium server ${seleniumJar}`);
|
|
394
|
+
return seleniumServer.start(60 * 1000);
|
|
395
|
+
}
|
|
374
396
|
|
|
375
|
-
/**
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
WRAPPED.it(name, opt_fn);
|
|
385
|
-
} else {
|
|
386
|
-
WRAPPED.it(name);
|
|
397
|
+
const /** !Function */beforeHook = global.beforeAll || global.before;
|
|
398
|
+
beforeHook(startSelenium, startTimeout);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
fn(new Environment(browser, seleniumUrl || seleniumServer));
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
} finally {
|
|
405
|
+
inSuite = false;
|
|
387
406
|
}
|
|
388
|
-
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
|
|
389
410
|
|
|
390
411
|
/**
|
|
391
|
-
*
|
|
392
|
-
*
|
|
393
|
-
*
|
|
394
|
-
*
|
|
395
|
-
*
|
|
412
|
+
* Returns an object with wrappers for the standard mocha/jasmine test
|
|
413
|
+
* functions: `describe` and `it`, which will redirect to `xdescribe` and `xit`,
|
|
414
|
+
* respectively, if provided predicate function returns false.
|
|
415
|
+
*
|
|
416
|
+
* Sample usage:
|
|
417
|
+
*
|
|
418
|
+
* const {Browser} = require('selenium-webdriver');
|
|
419
|
+
* const {suite, ignore} = require('selenium-webdriver/testing');
|
|
420
|
+
*
|
|
421
|
+
* suite(function(env) {
|
|
422
|
+
*
|
|
423
|
+
* // Skip tests the current environment targets Chrome.
|
|
424
|
+
* ignore(env.browsers(Browser.CHROME)).
|
|
425
|
+
* describe('something', async function() {
|
|
426
|
+
* let driver = await env.builder().build();
|
|
427
|
+
* // etc.
|
|
428
|
+
* });
|
|
429
|
+
* });
|
|
430
|
+
*
|
|
431
|
+
* @param {function(): boolean} predicateFn A predicate to call to determine
|
|
432
|
+
* if the test should be suppressed. This function MUST be synchronous.
|
|
433
|
+
* @return {{describe: !Function, it: !Function}} an object with wrapped
|
|
434
|
+
* versions of the `describe` and `it` wtest functions.
|
|
396
435
|
*/
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
436
|
+
function ignore(predicateFn) {
|
|
437
|
+
const isJasmine = global.jasmine && typeof global.jasmine === 'object';
|
|
438
|
+
|
|
439
|
+
const hooks = {
|
|
440
|
+
describe: getTestHook('describe'),
|
|
441
|
+
xdescribe: getTestHook('xdescribe'),
|
|
442
|
+
it: getTestHook('it'),
|
|
443
|
+
xit: getTestHook('xit')
|
|
444
|
+
};
|
|
445
|
+
hooks.fdescribe = isJasmine ? getTestHook('fdescribe') : hooks.describe.only;
|
|
446
|
+
hooks.fit = isJasmine ? getTestHook('fit') : hooks.it.only;
|
|
447
|
+
|
|
448
|
+
let describe = wrap(hooks.xdescribe, hooks.describe);
|
|
449
|
+
let fdescribe = wrap(hooks.xdescribe, hooks.fdescribe);
|
|
450
|
+
describe.only = fdescribe;
|
|
451
|
+
|
|
452
|
+
let it = wrap(hooks.xit, hooks.it);
|
|
453
|
+
let fit = wrap(hooks.xit, hooks.fit);
|
|
454
|
+
it.only = fit;
|
|
455
|
+
|
|
456
|
+
return {describe, it};
|
|
457
|
+
|
|
458
|
+
function wrap(onSkip, onRun) {
|
|
459
|
+
return function(...args) {
|
|
460
|
+
if (predicateFn()) {
|
|
461
|
+
onSkip(...args);
|
|
462
|
+
} else {
|
|
463
|
+
onRun(...args);
|
|
464
|
+
}
|
|
465
|
+
};
|
|
403
466
|
}
|
|
404
|
-
}
|
|
467
|
+
}
|
|
405
468
|
|
|
406
469
|
|
|
407
470
|
/**
|
|
408
|
-
*
|
|
409
|
-
* @
|
|
410
|
-
* @
|
|
411
|
-
* a pending test case.
|
|
471
|
+
* @param {string} name
|
|
472
|
+
* @return {!Function}
|
|
473
|
+
* @throws {TypeError}
|
|
412
474
|
*/
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
475
|
+
function getTestHook(name) {
|
|
476
|
+
let fn = global[name];
|
|
477
|
+
let type = typeof fn;
|
|
478
|
+
if (type !== 'function') {
|
|
479
|
+
throw TypeError(
|
|
480
|
+
`Expected global.${name} to be a function, but is ${type}.`
|
|
481
|
+
+ ' This can happen if you try using this module when running with'
|
|
482
|
+
+ ' node directly instead of using jasmine or mocha');
|
|
421
483
|
}
|
|
422
|
-
|
|
484
|
+
return fn;
|
|
485
|
+
}
|
|
486
|
+
|
|
423
487
|
|
|
488
|
+
// PUBLIC API
|
|
424
489
|
|
|
425
|
-
|
|
426
|
-
exports
|
|
490
|
+
|
|
491
|
+
module.exports = {
|
|
492
|
+
Environment,
|
|
493
|
+
TargetBrowser,
|
|
494
|
+
SuiteOptions,
|
|
495
|
+
init,
|
|
496
|
+
ignore,
|
|
497
|
+
suite
|
|
498
|
+
};
|