selenium-webdriver 3.0.0-beta-1 → 3.0.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.
Files changed (77) hide show
  1. package/CHANGES.md +109 -0
  2. package/LICENSE +1 -1
  3. package/README.md +7 -6
  4. package/chrome.js +31 -128
  5. package/edge.js +13 -91
  6. package/example/chrome_android.js +18 -13
  7. package/example/chrome_mobile_emulation.js +19 -15
  8. package/example/google_search.js +11 -11
  9. package/example/google_search_generator.js +23 -18
  10. package/example/google_search_test.js +31 -17
  11. package/example/logging.js +9 -17
  12. package/example/parallel_flows.js +3 -0
  13. package/firefox/binary.js +17 -0
  14. package/firefox/index.js +198 -101
  15. package/firefox/profile.js +24 -3
  16. package/http/util.js +95 -59
  17. package/ie.js +6 -10
  18. package/index.js +636 -2
  19. package/lib/actions.js +18 -10
  20. package/lib/atoms/getAttribute.js +11 -0
  21. package/lib/atoms/isDisplayed.js +106 -0
  22. package/lib/firefox/amd64/libnoblur64.so +0 -0
  23. package/lib/firefox/i386/libnoblur.so +0 -0
  24. package/lib/firefox/webdriver.xpi +0 -0
  25. package/lib/http.js +159 -46
  26. package/lib/logging.js +7 -1
  27. package/lib/promise.js +602 -358
  28. package/lib/proxy.js +28 -0
  29. package/lib/session.js +1 -1
  30. package/lib/test/build.js +26 -28
  31. package/lib/test/data/formPage.html +1 -0
  32. package/lib/test/fileserver.js +12 -10
  33. package/lib/test/httpserver.js +4 -6
  34. package/lib/test/index.js +18 -11
  35. package/lib/test/promise.js +79 -0
  36. package/lib/webdriver.js +587 -479
  37. package/opera.js +12 -88
  38. package/package.json +13 -14
  39. package/phantomjs.js +7 -14
  40. package/remote/index.js +201 -30
  41. package/safari.js +62 -416
  42. package/test/actions_test.js +9 -11
  43. package/test/chrome/options_test.js +8 -8
  44. package/test/chrome/service_test.js +2 -2
  45. package/test/cookie_test.js +57 -57
  46. package/test/element_finding_test.js +201 -185
  47. package/test/execute_script_test.js +108 -111
  48. package/test/fingerprint_test.js +16 -11
  49. package/test/firefox/firefox_test.js +75 -49
  50. package/test/firefox/profile_test.js +1 -3
  51. package/test/http/util_test.js +21 -27
  52. package/test/io_test.js +2 -5
  53. package/test/lib/http_test.js +8 -6
  54. package/test/lib/promise_aplus_test.js +48 -44
  55. package/test/lib/promise_error_test.js +687 -611
  56. package/test/lib/promise_flow_test.js +1810 -1806
  57. package/test/lib/promise_generator_test.js +227 -225
  58. package/test/lib/promise_test.js +864 -835
  59. package/test/lib/until_test.js +1 -1
  60. package/test/lib/webdriver_test.js +520 -424
  61. package/test/logging_test.js +43 -45
  62. package/test/net/portprober_test.js +23 -24
  63. package/test/page_loading_test.js +84 -78
  64. package/test/phantomjs/execute_phantomjs_test.js +11 -25
  65. package/test/proxy_test.js +23 -23
  66. package/test/remote_test.js +22 -22
  67. package/test/safari_test.js +108 -0
  68. package/test/session_test.js +11 -10
  69. package/test/stale_element_test.js +18 -16
  70. package/test/tag_name_test.js +7 -5
  71. package/test/testing/assert_test.js +2 -3
  72. package/test/testing/index_test.js +163 -117
  73. package/test/upload_test.js +13 -13
  74. package/test/window_test.js +94 -70
  75. package/testing/index.js +176 -37
  76. package/builder.js +0 -553
  77. package/lib/safari/client.js +0 -56
@@ -18,6 +18,7 @@
18
18
  'use strict';
19
19
 
20
20
  var Browser = require('..').Browser,
21
+ By = require('..').By,
21
22
  assert = require('../testing/assert'),
22
23
  test = require('../lib/test');
23
24
 
@@ -25,93 +26,99 @@ var Browser = require('..').Browser,
25
26
  test.suite(function(env) {
26
27
  var driver;
27
28
 
28
- test.before(function() { driver = env.builder().build(); });
29
- test.after(function() { driver.quit(); });
29
+ test.before(function*() { driver = yield env.builder().build(); });
30
+ test.after(function() { return driver.quit(); });
30
31
 
31
32
  test.beforeEach(function() {
32
- driver.switchTo().defaultContent();
33
+ return driver.switchTo().defaultContent();
33
34
  });
34
35
 
35
- test.it('can set size of the current window', function() {
36
- driver.get(test.Pages.echoPage);
37
- changeSizeBy(-20, -20);
36
+ test.it('can set size of the current window', function*() {
37
+ yield driver.get(test.Pages.echoPage);
38
+ yield changeSizeBy(-20, -20);
38
39
  });
39
40
 
40
- test.it('can set size of the current window from frame', function() {
41
- driver.get(test.Pages.framesetPage);
41
+ test.it('can set size of the current window from frame', function*() {
42
+ yield driver.get(test.Pages.framesetPage);
42
43
 
43
- var frame = driver.findElement({css: 'frame[name="fourth"]'});
44
- driver.switchTo().frame(frame);
45
- changeSizeBy(-20, -20);
44
+ var frame = yield driver.findElement({css: 'frame[name="fourth"]'});
45
+ yield driver.switchTo().frame(frame);
46
+ yield changeSizeBy(-20, -20);
46
47
  });
47
48
 
48
- test.it('can set size of the current window from iframe', function() {
49
- driver.get(test.Pages.iframePage);
49
+ test.it('can set size of the current window from iframe', function*() {
50
+ yield driver.get(test.Pages.iframePage);
50
51
 
51
- var frame = driver.findElement({css: 'iframe[name="iframe1-name"]'});
52
- driver.switchTo().frame(frame);
53
- changeSizeBy(-20, -20);
52
+ var frame = yield driver.findElement({css: 'iframe[name="iframe1-name"]'});
53
+ yield driver.switchTo().frame(frame);
54
+ yield changeSizeBy(-20, -20);
54
55
  });
55
56
 
56
- // See https://github.com/mozilla/geckodriver/issues/113
57
- test.ignore(env.browsers(Browser.FIREFOX)).
58
- it('can set the window position of the current window', function() {
59
- driver.manage().window().getPosition().then(function(position) {
60
- driver.manage().window().setSize(640, 480);
61
- driver.manage().window().setPosition(position.x + 10, position.y + 10);
62
-
63
- // For phantomjs, setPosition is a no-op and the "window" stays at (0, 0)
64
- if (env.currentBrowser() === Browser.PHANTOM_JS) {
65
- driver.manage().window().getPosition().then(function(position) {
66
- assert(position.x).equalTo(0);
67
- assert(position.y).equalTo(0);
68
- });
69
- } else {
70
- var dx = position.x + 10;
71
- var dy = position.y + 10;
72
- // On OSX, Safari position's the window relative to below the menubar
73
- // at the top of the screen, which is 23 pixels tall.
74
- if (env.currentBrowser() === Browser.SAFARI &&
75
- process.platform === 'darwin') {
76
- dy += 23;
77
- }
78
- }
79
- });
57
+ test.it('can switch to a new window', function*() {
58
+ yield driver.get(test.Pages.xhtmlTestPage);
59
+
60
+ let handle = yield driver.getWindowHandle();
61
+ let originalHandles = yield driver.getAllWindowHandles();
62
+
63
+ yield driver.findElement(By.linkText("Open new window")).click();
64
+ yield driver.wait(forNewWindowToBeOpened(originalHandles), 2000);
65
+ yield assert(driver.getTitle()).equalTo("XHTML Test Page");
66
+
67
+ let newHandle = yield getNewWindowHandle(originalHandles);
68
+
69
+ yield driver.switchTo().window(newHandle);
70
+ yield assert(driver.getTitle()).equalTo("We Arrive Here");
80
71
  });
81
72
 
82
- // See https://github.com/mozilla/geckodriver/issues/113
83
- test.ignore(env.browsers(Browser.FIREFOX)).
84
- it('can set the window position from a frame', function() {
85
- driver.get(test.Pages.iframePage);
86
- driver.switchTo().frame('iframe1-name');
87
- driver.manage().window().getPosition().then(function(position) {
88
- driver.manage().window().setSize(640, 480);
89
- driver.manage().window().setPosition(position.x + 10, position.y + 10);
90
-
91
- // For phantomjs, setPosition is a no-op and the "window" stays at (0, 0)
92
- if (env.currentBrowser() === Browser.PHANTOM_JS) {
93
- driver.manage().window().getPosition().then(function(position) {
94
- assert(position.x).equalTo(0);
95
- assert(position.y).equalTo(0);
96
- });
97
- } else {
98
- var dx = position.x + 10;
99
- var dy = position.y + 10;
100
- // On OSX, Safari position's the window relative to below the menubar
101
- // at the top of the screen, which is 23 pixels tall.
102
- if (env.currentBrowser() === Browser.SAFARI &&
103
- process.platform === 'darwin') {
104
- dy += 23;
105
- }
106
- driver.wait(forPositionToBe(dx, dy), 1000);
107
- }
108
- });
73
+ test.it('can set the window position of the current window', function*() {
74
+ let position = yield driver.manage().window().getPosition();
75
+
76
+ yield driver.manage().window().setSize(640, 480);
77
+ yield driver.manage().window().setPosition(position.x + 10, position.y + 10);
78
+
79
+ // For phantomjs, setPosition is a no-op and the "window" stays at (0, 0)
80
+ if (env.currentBrowser() === Browser.PHANTOM_JS) {
81
+ position = yield driver.manage().window().getPosition();
82
+ assert(position.x).equalTo(0);
83
+ assert(position.y).equalTo(0);
84
+ } else {
85
+ var dx = position.x + 10;
86
+ var dy = position.y + 10;
87
+ return driver.wait(forPositionToBe(dx, dy), 1000);
88
+ }
89
+ });
90
+
91
+ test.it('can set the window position from a frame', function*() {
92
+ yield driver.get(test.Pages.iframePage);
93
+
94
+ let frame = yield driver.findElement(By.name('iframe1-name'));
95
+ yield driver.switchTo().frame(frame);
96
+
97
+ let position = yield driver.manage().window().getPosition();
98
+ yield driver.manage().window().setSize(640, 480);
99
+ yield driver.manage().window().setPosition(position.x + 10, position.y + 10);
100
+
101
+ // For phantomjs, setPosition is a no-op and the "window" stays at (0, 0)
102
+ if (env.currentBrowser() === Browser.PHANTOM_JS) {
103
+ return driver.manage().window().getPosition().then(function(position) {
104
+ assert(position.x).equalTo(0);
105
+ assert(position.y).equalTo(0);
106
+ });
107
+ } else {
108
+ var dx = position.x + 10;
109
+ var dy = position.y + 10;
110
+ return driver.wait(forPositionToBe(dx, dy), 1000);
111
+ }
109
112
  });
110
113
 
111
114
  function changeSizeBy(dx, dy) {
112
- driver.manage().window().getSize().then(function(size) {
113
- driver.manage().window().setSize(size.width + dx, size.height + dy);
114
- driver.wait(forSizeToBe(size.width + dx, size.height + dy), 1000);
115
+ return driver.manage().window().getSize().then(function(size) {
116
+ return driver.manage().window()
117
+ .setSize(size.width + dx, size.height + dy)
118
+ .then(_ => {
119
+ return driver.wait(
120
+ forSizeToBe(size.width + dx, size.height + dy), 1000);
121
+ });
115
122
  });
116
123
  }
117
124
 
@@ -134,4 +141,21 @@ test.suite(function(env) {
134
141
  });
135
142
  };
136
143
  }
144
+
145
+ function forNewWindowToBeOpened(originalHandles) {
146
+ return function() {
147
+ return driver.getAllWindowHandles().then(function(currentHandles) {
148
+ return currentHandles.length > originalHandles.length;
149
+ });
150
+ };
151
+ }
152
+
153
+ function getNewWindowHandle(originalHandles) {
154
+ // Note: this assumes there's just one new window.
155
+ return driver.getAllWindowHandles().then(function(currentHandles) {
156
+ return currentHandles.filter(function(i) {
157
+ return originalHandles.indexOf(i) < 0;
158
+ })[0];
159
+ });
160
+ }
137
161
  });
package/testing/index.js CHANGED
@@ -28,6 +28,16 @@
28
28
  * - it.skip
29
29
  * - xit
30
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
+ *
31
41
  * The provided wrappers leverage the {@link webdriver.promise.ControlFlow}
32
42
  * to simplify writing asynchronous tests:
33
43
  *
@@ -66,8 +76,22 @@
66
76
  * function maybe() { return Math.random() < 0.5; }
67
77
  */
68
78
 
69
- var promise = require('..').promise;
70
- var flow = promise.controlFlow();
79
+ 'use strict';
80
+
81
+ const promise = require('..').promise;
82
+ const flow = (function() {
83
+ const initial = process.env['SELENIUM_PROMISE_MANAGER'];
84
+ try {
85
+ process.env['SELENIUM_PROMISE_MANAGER'] = '1';
86
+ return promise.controlFlow();
87
+ } finally {
88
+ if (initial === undefined) {
89
+ delete process.env['SELENIUM_PROMISE_MANAGER'];
90
+ } else {
91
+ process.env['SELENIUM_PROMISE_MANAGER'] = initial;
92
+ }
93
+ }
94
+ })();
71
95
 
72
96
 
73
97
  /**
@@ -119,13 +143,38 @@ function wrapArgument(value) {
119
143
  * Should preserve the semantics of Mocha's Runnable.prototype.run (See
120
144
  * https://github.com/mochajs/mocha/blob/master/lib/runnable.js#L192)
121
145
  *
122
- * @param {Function} fn
123
- * @return {Function}
146
+ * @param {!Function} fn
147
+ * @return {!Function}
124
148
  */
125
149
  function makeAsyncTestFn(fn) {
126
- var async = fn.length > 0; // if test function expects a callback, its "async"
150
+ const isAsync = fn.length > 0;
151
+ const isGenerator = promise.isGenerator(fn);
152
+ if (isAsync && isGenerator) {
153
+ throw TypeError(
154
+ 'generator-based tests must not take a callback; for async testing,'
155
+ + ' return a promise (or yield on a promise)');
156
+ }
127
157
 
128
158
  var ret = /** @type {function(this: mocha.Context)}*/ (function(done) {
159
+ const runTest = (resolve, reject) => {
160
+ try {
161
+ if (isAsync) {
162
+ fn.call(this, err => err ? reject(err) : resolve());
163
+ } else if (isGenerator) {
164
+ resolve(promise.consume(fn, this));
165
+ } else {
166
+ resolve(fn.call(this));
167
+ }
168
+ } catch (ex) {
169
+ reject(ex);
170
+ }
171
+ };
172
+
173
+ if (!promise.USE_PROMISE_MANAGER) {
174
+ new Promise(runTest).then(seal(done), done);
175
+ return;
176
+ }
177
+
129
178
  var runnable = this.runnable();
130
179
  var mochaCallback = runnable.callback;
131
180
  runnable.callback = function() {
@@ -133,25 +182,9 @@ function makeAsyncTestFn(fn) {
133
182
  return mochaCallback.apply(this, arguments);
134
183
  };
135
184
 
136
- var testFn = fn.bind(this);
137
185
  flow.execute(function controlFlowExecute() {
138
186
  return new promise.Promise(function(fulfill, reject) {
139
- if (async) {
140
- // If testFn is async (it expects a done callback), resolve the promise of this
141
- // test whenever that callback says to. Any promises returned from testFn are
142
- // ignored.
143
- testFn(function testFnDoneCallback(err) {
144
- if (err) {
145
- reject(err);
146
- } else {
147
- fulfill();
148
- }
149
- });
150
- } else {
151
- // Without a callback, testFn can return a promise, or it will
152
- // be assumed to have completed synchronously
153
- fulfill(testFn());
154
- }
187
+ return runTest(fulfill, reject);
155
188
  }, flow);
156
189
  }, runnable.fullTitle()).then(seal(done), done);
157
190
  });
@@ -196,6 +229,45 @@ function ignore(predicateFn) {
196
229
  }
197
230
 
198
231
 
232
+ /**
233
+ * @param {string} name
234
+ * @return {!Function}
235
+ * @throws {TypeError}
236
+ */
237
+ function getMochaGlobal(name) {
238
+ let fn = global[name];
239
+ let type = typeof fn;
240
+ if (type !== 'function') {
241
+ throw TypeError(
242
+ `Expected global.${name} to be a function, but is ${type}. `
243
+ + 'This can happen if you try using this module when running '
244
+ + 'with node directly instead of using the mocha executable');
245
+ }
246
+ return fn;
247
+ }
248
+
249
+
250
+ const WRAPPED = {
251
+ after: null,
252
+ afterEach: null,
253
+ before: null,
254
+ beforeEach: null,
255
+ it: null,
256
+ itOnly: null,
257
+ xit: null
258
+ };
259
+
260
+
261
+ function wrapIt() {
262
+ if (!WRAPPED.it) {
263
+ let it = getMochaGlobal('it');
264
+ WRAPPED.it = wrapped(it);
265
+ WRAPPED.itOnly = wrapped(it.only);
266
+ }
267
+ }
268
+
269
+
270
+
199
271
  // PUBLIC API
200
272
 
201
273
 
@@ -211,67 +283,134 @@ exports.controlFlow = function(){
211
283
  /**
212
284
  * Registers a new test suite.
213
285
  * @param {string} name The suite name.
214
- * @param {function()=} fn The suite function, or {@code undefined} to define
286
+ * @param {function()=} opt_fn The suite function, or `undefined` to define
215
287
  * a pending test suite.
216
288
  */
217
- exports.describe = global.describe;
289
+ exports.describe = function(name, opt_fn) {
290
+ let fn = getMochaGlobal('describe');
291
+ return opt_fn ? fn(name, opt_fn) : fn(name);
292
+ };
293
+
218
294
 
219
295
  /**
220
296
  * Defines a suppressed test suite.
221
297
  * @param {string} name The suite name.
222
- * @param {function()=} fn The suite function, or {@code undefined} to define
298
+ * @param {function()=} opt_fn The suite function, or `undefined` to define
223
299
  * a pending test suite.
224
300
  */
225
- exports.xdescribe = global.xdescribe;
226
- exports.describe.skip = global.describe.skip;
301
+ exports.describe.skip = function(name, opt_fn) {
302
+ let fn = getMochaGlobal('describe');
303
+ return opt_fn ? fn.skip(name, opt_fn) : fn.skip(name);
304
+ };
305
+
306
+
307
+ /**
308
+ * Defines a suppressed test suite.
309
+ * @param {string} name The suite name.
310
+ * @param {function()=} opt_fn The suite function, or `undefined` to define
311
+ * a pending test suite.
312
+ */
313
+ exports.xdescribe = function(name, opt_fn) {
314
+ let fn = getMochaGlobal('xdescribe');
315
+ return opt_fn ? fn(name, opt_fn) : fn(name);
316
+ };
317
+
227
318
 
228
319
  /**
229
320
  * Register a function to call after the current suite finishes.
230
321
  * @param {function()} fn .
231
322
  */
232
- exports.after = wrapped(global.after);
323
+ exports.after = function(fn) {
324
+ if (!WRAPPED.after) {
325
+ WRAPPED.after = wrapped(getMochaGlobal('after'));
326
+ }
327
+ WRAPPED.after(fn);
328
+ };
329
+
233
330
 
234
331
  /**
235
332
  * Register a function to call after each test in a suite.
236
333
  * @param {function()} fn .
237
334
  */
238
- exports.afterEach = wrapped(global.afterEach);
335
+ exports.afterEach = function(fn) {
336
+ if (!WRAPPED.afterEach) {
337
+ WRAPPED.afterEach = wrapped(getMochaGlobal('afterEach'));
338
+ }
339
+ WRAPPED.afterEach(fn);
340
+ };
341
+
239
342
 
240
343
  /**
241
344
  * Register a function to call before the current suite starts.
242
345
  * @param {function()} fn .
243
346
  */
244
- exports.before = wrapped(global.before);
347
+ exports.before = function(fn) {
348
+ if (!WRAPPED.before) {
349
+ WRAPPED.before = wrapped(getMochaGlobal('before'));
350
+ }
351
+ WRAPPED.before(fn);
352
+ };
245
353
 
246
354
  /**
247
355
  * Register a function to call before each test in a suite.
248
356
  * @param {function()} fn .
249
357
  */
250
- exports.beforeEach = wrapped(global.beforeEach);
358
+ exports.beforeEach = function(fn) {
359
+ if (!WRAPPED.beforeEach) {
360
+ WRAPPED.beforeEach = wrapped(getMochaGlobal('beforeEach'));
361
+ }
362
+ WRAPPED.beforeEach(fn);
363
+ };
251
364
 
252
365
  /**
253
366
  * Add a test to the current suite.
254
367
  * @param {string} name The test name.
255
- * @param {function()=} fn The test function, or {@code undefined} to define
368
+ * @param {function()=} opt_fn The test function, or `undefined` to define
256
369
  * a pending test case.
257
370
  */
258
- exports.it = wrapped(global.it);
371
+ exports.it = function(name, opt_fn) {
372
+ wrapIt();
373
+ if (opt_fn) {
374
+ WRAPPED.it(name, opt_fn);
375
+ } else {
376
+ WRAPPED.it(name);
377
+ }
378
+ };
259
379
 
260
380
  /**
261
381
  * An alias for {@link #it()} that flags the test as the only one that should
262
382
  * be run within the current suite.
263
383
  * @param {string} name The test name.
264
- * @param {function()=} fn The test function, or {@code undefined} to define
384
+ * @param {function()=} opt_fn The test function, or `undefined` to define
265
385
  * a pending test case.
266
386
  */
267
- exports.iit = exports.it.only = wrapped(global.it.only);
387
+ exports.it.only = function(name, opt_fn) {
388
+ wrapIt();
389
+ if (opt_fn) {
390
+ WRAPPED.itOnly(name, opt_fn);
391
+ } else {
392
+ WRAPPED.itOnly(name);
393
+ }
394
+ };
395
+
268
396
 
269
397
  /**
270
398
  * Adds a test to the current suite while suppressing it so it is not run.
271
399
  * @param {string} name The test name.
272
- * @param {function()=} fn The test function, or {@code undefined} to define
400
+ * @param {function()=} opt_fn The test function, or `undefined` to define
273
401
  * a pending test case.
274
402
  */
275
- exports.xit = exports.it.skip = wrapped(global.xit);
403
+ exports.xit = function(name, opt_fn) {
404
+ if (!WRAPPED.xit) {
405
+ WRAPPED.xit = wrapped(getMochaGlobal('xit'));
406
+ }
407
+ if (opt_fn) {
408
+ WRAPPED.xit(name, opt_fn);
409
+ } else {
410
+ WRAPPED.xit(name);
411
+ }
412
+ };
413
+
276
414
 
415
+ exports.it.skip = exports.xit;
277
416
  exports.ignore = ignore;