selenium-webdriver 3.3.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 +273 -0
- package/NOTICE +1 -1
- package/README.md +28 -36
- package/chrome.js +221 -146
- package/edge.js +43 -118
- package/example/chrome_android.js +8 -9
- package/example/chrome_mobile_emulation.js +9 -10
- package/example/firefox_channels.js +23 -19
- package/example/google_search.js +3 -3
- package/example/google_search_test.js +39 -27
- package/example/headless.js +55 -0
- package/example/logging.js +40 -7
- package/firefox.js +769 -0
- package/http/index.js +41 -4
- package/http/util.js +11 -6
- package/ie.js +57 -98
- package/index.js +182 -115
- package/io/index.js +59 -0
- package/io/zip.js +214 -0
- package/jasmine.json +11 -0
- package/lib/actions.js +68 -63
- package/lib/atoms/get-attribute.js +9 -0
- package/lib/atoms/is-displayed.js +73 -78
- package/lib/by.js +9 -1
- package/lib/capabilities.js +303 -201
- package/lib/command.js +28 -50
- package/lib/error.js +113 -42
- package/lib/http.js +167 -135
- package/lib/input.js +1044 -8
- package/lib/promise.js +222 -3331
- 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/click_tests/disabled_element.html +12 -0
- package/lib/test/data/content-editable.html +10 -0
- package/lib/test/data/firefox/webextension.xpi +0 -0
- package/lib/test/data/inputs.html +42 -0
- package/lib/test/data/key_logger.html +34 -0
- package/lib/test/data/nestedElements.html +10 -1
- package/lib/test/data/scrolling_tests/page_with_partially_hidden_element.html +14 -0
- package/lib/test/data/selectPage.html +12 -0
- package/lib/test/data/simpleTest.html +5 -0
- package/lib/test/data/single_text_input.html +12 -0
- package/lib/test/data/upload_invisible.html +45 -0
- package/lib/test/fileserver.js +23 -19
- package/lib/test/index.js +30 -244
- package/lib/test/resources.js +0 -1
- package/lib/webdriver.js +770 -939
- package/net/index.js +21 -25
- package/net/portprober.js +51 -68
- package/package.json +6 -4
- package/remote/index.js +18 -30
- package/safari.js +42 -129
- package/test/actions_test.js +173 -18
- package/test/builder_test.js +106 -0
- 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 +18 -1
- package/test/http/util_test.js +2 -2
- package/test/{io_test.js → io/io_test.js} +40 -1
- package/test/io/zip_test.js +127 -0
- package/test/lib/by_test.js +21 -0
- package/test/lib/error_test.js +55 -8
- package/test/lib/http_test.js +18 -130
- package/test/lib/input_test.js +1379 -0
- package/test/lib/promise_test.js +473 -908
- package/test/lib/webdriver_test.js +357 -825
- package/test/logging_test.js +37 -43
- package/test/page_loading_test.js +70 -86
- package/test/proxy_test.js +64 -72
- package/test/rect_test.js +14 -25
- package/test/remote_test.js +13 -32
- 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 -315
- package/.npmignore +0 -2
- package/example/async_await_test.js +0 -68
- package/example/google_search_generator.js +0 -50
- package/example/parallel_flows.js +0 -54
- package/firefox/binary.js +0 -344
- package/firefox/extension.js +0 -187
- package/firefox/index.js +0 -722
- package/firefox/profile.js +0 -430
- package/lib/atoms/getAttribute.js +0 -12
- package/lib/events.js +0 -210
- package/lib/firefox/webdriver.json +0 -70
- package/lib/firefox/webdriver.xpi +0 -0
- 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 -96
- package/test/firefox/firefox_test.js +0 -226
- package/test/firefox/profile_test.js +0 -185
- 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/session_test.js +0 -54
- 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,52 +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, 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');
|
|
61
|
-
* driver.findElement(By.name('btnG')).click();
|
|
62
|
-
* driver.wait(until.titleIs('webdriver - Google Search'), 1000);
|
|
63
|
-
* });
|
|
64
|
-
* });
|
|
19
|
+
* @fileoverview Provides extensions for
|
|
20
|
+
* [Jasmine](https://jasmine.github.io) and [Mocha](https://mochajs.org).
|
|
65
21
|
*
|
|
66
22
|
* You may conditionally suppress a test function using the exported
|
|
67
23
|
* "ignore" function. If the provided predicate returns true, the attached
|
|
@@ -76,352 +32,467 @@
|
|
|
76
32
|
|
|
77
33
|
'use strict';
|
|
78
34
|
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
process.env['SELENIUM_PROMISE_MANAGER'] = initial;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
})();
|
|
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');
|
|
93
45
|
|
|
94
46
|
|
|
95
47
|
/**
|
|
96
|
-
*
|
|
97
|
-
* @
|
|
98
|
-
* @return {!Function} The wrapped function.
|
|
48
|
+
* Describes a browser targetted by a {@linkplain suite test suite}.
|
|
49
|
+
* @record
|
|
99
50
|
*/
|
|
100
|
-
function
|
|
101
|
-
return function() {
|
|
102
|
-
fn();
|
|
103
|
-
};
|
|
104
|
-
}
|
|
105
|
-
|
|
51
|
+
function TargetBrowser() {}
|
|
106
52
|
|
|
107
53
|
/**
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
* continuing.
|
|
111
|
-
* @param {!Function} globalFn The function to wrap.
|
|
112
|
-
* @return {!Function} The new function.
|
|
54
|
+
* The {@linkplain Browser name} of the targetted browser.
|
|
55
|
+
* @type {string}
|
|
113
56
|
*/
|
|
114
|
-
|
|
115
|
-
return function() {
|
|
116
|
-
if (arguments.length === 1) {
|
|
117
|
-
return globalFn(wrapArgument(arguments[0]));
|
|
57
|
+
TargetBrowser.prototype.name;
|
|
118
58
|
|
|
119
|
-
|
|
120
|
-
|
|
59
|
+
/**
|
|
60
|
+
* The specific version of the targetted browser, if any.
|
|
61
|
+
* @type {(string|undefined)}
|
|
62
|
+
*/
|
|
63
|
+
TargetBrowser.prototype.version;
|
|
121
64
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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;
|
|
127
71
|
|
|
128
72
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
}
|
|
133
|
-
return value;
|
|
73
|
+
/** @suppress {checkTypes} */
|
|
74
|
+
function color(c, s) {
|
|
75
|
+
return isatty(process.stdout) ? `\u001b[${c}m${s}\u001b[0m` : s;
|
|
134
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}`); }
|
|
135
81
|
|
|
136
82
|
|
|
137
83
|
/**
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
* Should preserve the semantics of Mocha's Runnable.prototype.run (See
|
|
142
|
-
* 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.
|
|
143
86
|
*
|
|
144
|
-
* @
|
|
145
|
-
* @return {!Function}
|
|
87
|
+
* @return {!Array<!TargetBrowser>} the browsers to target.
|
|
146
88
|
*/
|
|
147
|
-
function
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
throw TypeError(
|
|
152
|
-
'generator-based tests must not take a callback; for async testing,'
|
|
153
|
-
+ ' return a promise (or yield on a promise)');
|
|
89
|
+
function getBrowsersToTestFromEnv() {
|
|
90
|
+
let browsers = process.env['SELENIUM_BROWSER'];
|
|
91
|
+
if (!browsers) {
|
|
92
|
+
return [];
|
|
154
93
|
}
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
resolve(promise.consume(fn, this));
|
|
163
|
-
} else {
|
|
164
|
-
resolve(fn.call(this));
|
|
165
|
-
}
|
|
166
|
-
} catch (ex) {
|
|
167
|
-
reject(ex);
|
|
168
|
-
}
|
|
169
|
-
};
|
|
170
|
-
|
|
171
|
-
if (!promise.USE_PROMISE_MANAGER) {
|
|
172
|
-
new Promise(runTest).then(seal(done), done);
|
|
173
|
-
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;
|
|
174
101
|
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
runnable.callback = function() {
|
|
179
|
-
flow.reset();
|
|
180
|
-
return mochaCallback.apply(this, arguments);
|
|
181
|
-
};
|
|
182
|
-
|
|
183
|
-
flow.execute(function controlFlowExecute() {
|
|
184
|
-
return new promise.Promise(function(fulfill, reject) {
|
|
185
|
-
return runTest(fulfill, reject);
|
|
186
|
-
}, flow);
|
|
187
|
-
}, runnable.fullTitle()).then(seal(done), done);
|
|
102
|
+
let version = parts[1];
|
|
103
|
+
let platform = parts[2];
|
|
104
|
+
return {name, version, platform};
|
|
188
105
|
});
|
|
189
|
-
|
|
190
|
-
ret.toString = function() {
|
|
191
|
-
return fn.toString();
|
|
192
|
-
};
|
|
193
|
-
|
|
194
|
-
return ret;
|
|
195
106
|
}
|
|
196
107
|
|
|
197
108
|
|
|
198
109
|
/**
|
|
199
|
-
*
|
|
200
|
-
*
|
|
201
|
-
* @param {function(): boolean} predicateFn A predicate to call to determine
|
|
202
|
-
* if the test should be suppressed. This function MUST be synchronous.
|
|
203
|
-
* @return {!Object} An object with wrapped versions of {@link #it()} and
|
|
204
|
-
* {@link #describe()} that ignore tests as indicated by the predicate.
|
|
110
|
+
* @return {!Array<!TargetBrowser>} the browsers available for testing on this
|
|
111
|
+
* system.
|
|
205
112
|
*/
|
|
206
|
-
function
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
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
|
+
}
|
|
217
133
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
if (predicateFn()) {
|
|
221
|
-
onSkip(title, fn);
|
|
222
|
-
} else {
|
|
223
|
-
onRun(title, fn);
|
|
224
|
-
}
|
|
225
|
-
};
|
|
134
|
+
if (availableBrowsers.length === 0) {
|
|
135
|
+
warn(`Unable to locate any WebDriver executables for testing`);
|
|
226
136
|
}
|
|
137
|
+
|
|
138
|
+
return availableBrowsers;
|
|
227
139
|
}
|
|
228
140
|
|
|
141
|
+
let wasInit;
|
|
142
|
+
let targetBrowsers;
|
|
143
|
+
let seleniumJar;
|
|
144
|
+
let seleniumUrl;
|
|
145
|
+
let seleniumServer;
|
|
229
146
|
|
|
230
147
|
/**
|
|
231
|
-
*
|
|
232
|
-
* @
|
|
233
|
-
*
|
|
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.
|
|
234
181
|
*/
|
|
235
|
-
function
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
if (type !== 'function') {
|
|
239
|
-
throw TypeError(
|
|
240
|
-
`Expected global.${name} to be a function, but is ${type}. `
|
|
241
|
-
+ 'This can happen if you try using this module when running '
|
|
242
|
-
+ 'with node directly instead of using the mocha executable');
|
|
182
|
+
function init(force = false) {
|
|
183
|
+
if (wasInit && !force) {
|
|
184
|
+
return;
|
|
243
185
|
}
|
|
244
|
-
|
|
245
|
-
}
|
|
186
|
+
wasInit = true;
|
|
246
187
|
|
|
188
|
+
// If force re-init, kill the current server if there is one.
|
|
189
|
+
if (seleniumServer) {
|
|
190
|
+
seleniumServer.kill();
|
|
191
|
+
seleniumServer = null;
|
|
192
|
+
}
|
|
247
193
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
it: null,
|
|
254
|
-
itOnly: null,
|
|
255
|
-
xit: null
|
|
256
|
-
};
|
|
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
|
+
}
|
|
257
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
|
+
}
|
|
258
209
|
|
|
259
|
-
|
|
260
|
-
if (
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
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.');
|
|
264
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
|
+
});
|
|
265
227
|
}
|
|
266
228
|
|
|
267
229
|
|
|
268
230
|
|
|
269
|
-
|
|
231
|
+
const TARGET_MAP = /** !WeakMap<!Environment, !TargetBrowser> */new WeakMap;
|
|
232
|
+
const URL_MAP =
|
|
233
|
+
/** !WeakMap<!Environment, ?(string|remote.SeleniumServer)> */new WeakMap;
|
|
270
234
|
|
|
271
235
|
|
|
272
236
|
/**
|
|
273
|
-
*
|
|
274
|
-
*
|
|
237
|
+
* Defines the environment a {@linkplain suite test suite} is running against.
|
|
238
|
+
* @final
|
|
275
239
|
*/
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
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
|
+
}
|
|
279
254
|
|
|
255
|
+
/** @return {!TargetBrowser} the target browser for this test environment. */
|
|
256
|
+
get browser() {
|
|
257
|
+
return TARGET_MAP.get(this);
|
|
258
|
+
}
|
|
280
259
|
|
|
281
|
-
/**
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
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
|
+
}
|
|
291
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
|
+
};
|
|
292
294
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
* @param {string} name The suite name.
|
|
297
|
-
* @param {function()=} opt_fn The suite function, or `undefined` to define
|
|
298
|
-
* a pending test suite.
|
|
299
|
-
*/
|
|
300
|
-
exports.describe.only = function(name, opt_fn) {
|
|
301
|
-
let desc = getMochaGlobal('describe');
|
|
302
|
-
return opt_fn ? desc.only(name, opt_fn) : desc.only(name);
|
|
303
|
-
};
|
|
295
|
+
return builder;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
304
298
|
|
|
305
299
|
|
|
306
300
|
/**
|
|
307
|
-
*
|
|
308
|
-
* @
|
|
309
|
-
* @param {function()=} opt_fn The suite function, or `undefined` to define
|
|
310
|
-
* a pending test suite.
|
|
301
|
+
* Configuration options for a {@linkplain ./index.suite test suite}.
|
|
302
|
+
* @record
|
|
311
303
|
*/
|
|
312
|
-
|
|
313
|
-
let fn = getMochaGlobal('describe');
|
|
314
|
-
return opt_fn ? fn.skip(name, opt_fn) : fn.skip(name);
|
|
315
|
-
};
|
|
316
|
-
|
|
304
|
+
function SuiteOptions() {}
|
|
317
305
|
|
|
318
306
|
/**
|
|
319
|
-
*
|
|
320
|
-
* @
|
|
321
|
-
* @param {function()=} opt_fn The suite function, or `undefined` to define
|
|
322
|
-
* a pending test suite.
|
|
307
|
+
* The browsers to run the test suite against.
|
|
308
|
+
* @type {!Array<!(Browser|TargetBrowser)>}
|
|
323
309
|
*/
|
|
324
|
-
|
|
325
|
-
let fn = getMochaGlobal('xdescribe');
|
|
326
|
-
return opt_fn ? fn(name, opt_fn) : fn(name);
|
|
327
|
-
};
|
|
310
|
+
SuiteOptions.prototype.browsers;
|
|
328
311
|
|
|
329
312
|
|
|
330
|
-
|
|
331
|
-
* Register a function to call after the current suite finishes.
|
|
332
|
-
* @param {function()} fn .
|
|
333
|
-
*/
|
|
334
|
-
exports.after = function(fn) {
|
|
335
|
-
if (!WRAPPED.after) {
|
|
336
|
-
WRAPPED.after = wrapped(getMochaGlobal('after'));
|
|
337
|
-
}
|
|
338
|
-
WRAPPED.after(fn);
|
|
339
|
-
};
|
|
313
|
+
let inSuite = false;
|
|
340
314
|
|
|
341
315
|
|
|
342
316
|
/**
|
|
343
|
-
*
|
|
344
|
-
*
|
|
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.
|
|
345
358
|
*/
|
|
346
|
-
|
|
347
|
-
if (
|
|
348
|
-
|
|
359
|
+
function suite(fn, options = undefined) {
|
|
360
|
+
if (inSuite) {
|
|
361
|
+
throw Error('Calls to suite() may not be nested');
|
|
349
362
|
}
|
|
350
|
-
|
|
351
|
-
|
|
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
|
+
}
|
|
352
382
|
|
|
383
|
+
describe(`[${browser.name}]`, function() {
|
|
384
|
+
if (!seleniumUrl && seleniumJar && !seleniumServer) {
|
|
385
|
+
seleniumServer = new remote.SeleniumServer(seleniumJar);
|
|
353
386
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
if (!WRAPPED.before) {
|
|
360
|
-
WRAPPED.before = wrapped(getMochaGlobal('before'));
|
|
361
|
-
}
|
|
362
|
-
WRAPPED.before(fn);
|
|
363
|
-
};
|
|
387
|
+
const startTimeout = 65 * 1000;
|
|
388
|
+
function startSelenium() {
|
|
389
|
+
if (typeof this.timeout === 'function') {
|
|
390
|
+
this.timeout(startTimeout); // For mocha.
|
|
391
|
+
}
|
|
364
392
|
|
|
365
|
-
|
|
366
|
-
*
|
|
367
|
-
|
|
368
|
-
*/
|
|
369
|
-
exports.beforeEach = function(fn) {
|
|
370
|
-
if (!WRAPPED.beforeEach) {
|
|
371
|
-
WRAPPED.beforeEach = wrapped(getMochaGlobal('beforeEach'));
|
|
372
|
-
}
|
|
373
|
-
WRAPPED.beforeEach(fn);
|
|
374
|
-
};
|
|
393
|
+
info(`Starting selenium server ${seleniumJar}`);
|
|
394
|
+
return seleniumServer.start(60 * 1000);
|
|
395
|
+
}
|
|
375
396
|
|
|
376
|
-
/**
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
WRAPPED.it(name, opt_fn);
|
|
386
|
-
} else {
|
|
387
|
-
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;
|
|
388
406
|
}
|
|
389
|
-
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
|
|
390
410
|
|
|
391
411
|
/**
|
|
392
|
-
*
|
|
393
|
-
*
|
|
394
|
-
*
|
|
395
|
-
*
|
|
396
|
-
*
|
|
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.
|
|
397
435
|
*/
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
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
|
+
};
|
|
404
466
|
}
|
|
405
|
-
}
|
|
467
|
+
}
|
|
406
468
|
|
|
407
469
|
|
|
408
470
|
/**
|
|
409
|
-
*
|
|
410
|
-
* @
|
|
411
|
-
* @
|
|
412
|
-
* a pending test case.
|
|
471
|
+
* @param {string} name
|
|
472
|
+
* @return {!Function}
|
|
473
|
+
* @throws {TypeError}
|
|
413
474
|
*/
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
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');
|
|
422
483
|
}
|
|
423
|
-
|
|
484
|
+
return fn;
|
|
485
|
+
}
|
|
486
|
+
|
|
424
487
|
|
|
488
|
+
// PUBLIC API
|
|
425
489
|
|
|
426
|
-
|
|
427
|
-
exports
|
|
490
|
+
|
|
491
|
+
module.exports = {
|
|
492
|
+
Environment,
|
|
493
|
+
TargetBrowser,
|
|
494
|
+
SuiteOptions,
|
|
495
|
+
init,
|
|
496
|
+
ignore,
|
|
497
|
+
suite
|
|
498
|
+
};
|