selenium-webdriver 3.0.0 → 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.
- package/CHANGES.md +13 -0
- package/lib/actions.js +6 -4
- package/lib/firefox/webdriver.xpi +0 -0
- package/lib/promise.js +39 -19
- package/lib/webdriver.js +7 -0
- package/package.json +1 -1
- package/test/lib/promise_flow_test.js +3 -0
- package/test/lib/webdriver_test.js +102 -38
package/CHANGES.md
CHANGED
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
## v3.0.1
|
|
2
|
+
|
|
3
|
+
* More API adjustments to align with native Promises
|
|
4
|
+
- Deprecated `promise.fulfilled(value)`, use `promise.Promise#resolve(value)`
|
|
5
|
+
- Deprecated `promise.rejected(reason)`, use `promise.Promise#reject(reason)`
|
|
6
|
+
* When a `wait()` condition times out, the returned promise will now be
|
|
7
|
+
rejected with an `error.TimeoutError` instead of a generic `Error` object.
|
|
8
|
+
* `WebDriver#wait()` will now throw a TypeError if an invalid wait condition is
|
|
9
|
+
provided.
|
|
10
|
+
* Properly catch unhandled promise rejections with an action sequence (only
|
|
11
|
+
impacts when the promise manager is disabled).
|
|
12
|
+
|
|
13
|
+
|
|
1
14
|
## v3.0.0
|
|
2
15
|
|
|
3
16
|
* (__NOTICE__) The minimum supported version of Node is now 6.9.0 LTS
|
package/lib/actions.js
CHANGED
|
@@ -120,9 +120,10 @@ class ActionSequence {
|
|
|
120
120
|
let actions = this.actions_.concat();
|
|
121
121
|
let driver = this.driver_;
|
|
122
122
|
return driver.controlFlow().execute(function() {
|
|
123
|
-
actions.
|
|
124
|
-
driver.schedule(action.command, action.description);
|
|
123
|
+
let results = actions.map(action => {
|
|
124
|
+
return driver.schedule(action.command, action.description);
|
|
125
125
|
});
|
|
126
|
+
return Promise.all(results);
|
|
126
127
|
}, 'ActionSequence.perform');
|
|
127
128
|
}
|
|
128
129
|
|
|
@@ -431,9 +432,10 @@ class TouchSequence {
|
|
|
431
432
|
let actions = this.actions_.concat();
|
|
432
433
|
let driver = this.driver_;
|
|
433
434
|
return driver.controlFlow().execute(function() {
|
|
434
|
-
actions.
|
|
435
|
-
driver.schedule(action.command, action.description);
|
|
435
|
+
let results = actions.map(action => {
|
|
436
|
+
return driver.schedule(action.command, action.description);
|
|
436
437
|
});
|
|
438
|
+
return Promise.all(results);
|
|
437
439
|
}, 'TouchSequence.perform');
|
|
438
440
|
}
|
|
439
441
|
|
|
Binary file
|
package/lib/promise.js
CHANGED
|
@@ -628,6 +628,7 @@
|
|
|
628
628
|
|
|
629
629
|
'use strict';
|
|
630
630
|
|
|
631
|
+
const error = require('./error');
|
|
631
632
|
const events = require('./events');
|
|
632
633
|
const logging = require('./logging');
|
|
633
634
|
|
|
@@ -1073,6 +1074,30 @@ class ManagedPromise {
|
|
|
1073
1074
|
}
|
|
1074
1075
|
}
|
|
1075
1076
|
|
|
1077
|
+
/**
|
|
1078
|
+
* Creates a promise that is immediately resolved with the given value.
|
|
1079
|
+
*
|
|
1080
|
+
* @param {T=} opt_value The value to resolve.
|
|
1081
|
+
* @return {!ManagedPromise<T>} A promise resolved with the given value.
|
|
1082
|
+
* @template T
|
|
1083
|
+
*/
|
|
1084
|
+
static resolve(opt_value) {
|
|
1085
|
+
if (opt_value instanceof ManagedPromise) {
|
|
1086
|
+
return opt_value;
|
|
1087
|
+
}
|
|
1088
|
+
return new ManagedPromise(resolve => resolve(opt_value));
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
/**
|
|
1092
|
+
* Creates a promise that is immediately rejected with the given reason.
|
|
1093
|
+
*
|
|
1094
|
+
* @param {*=} opt_reason The rejection reason.
|
|
1095
|
+
* @return {!ManagedPromise<?>} A new rejected promise.
|
|
1096
|
+
*/
|
|
1097
|
+
static reject(opt_reason) {
|
|
1098
|
+
return new ManagedPromise((_, reject) => reject(opt_reason));
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1076
1101
|
/** @override */
|
|
1077
1102
|
toString() {
|
|
1078
1103
|
return 'ManagedPromise::' + getUid(this) +
|
|
@@ -1475,15 +1500,11 @@ function defer() {
|
|
|
1475
1500
|
* Creates a promise that has been resolved with the given value.
|
|
1476
1501
|
* @param {T=} opt_value The resolved value.
|
|
1477
1502
|
* @return {!ManagedPromise<T>} The resolved promise.
|
|
1503
|
+
* @deprecated Use {@link ManagedPromise#resolve Promise.resolve(value)}.
|
|
1478
1504
|
* @template T
|
|
1479
1505
|
*/
|
|
1480
1506
|
function fulfilled(opt_value) {
|
|
1481
|
-
|
|
1482
|
-
return opt_value;
|
|
1483
|
-
}
|
|
1484
|
-
return new ManagedPromise(function(fulfill) {
|
|
1485
|
-
fulfill(opt_value);
|
|
1486
|
-
});
|
|
1507
|
+
return ManagedPromise.resolve(opt_value);
|
|
1487
1508
|
}
|
|
1488
1509
|
|
|
1489
1510
|
|
|
@@ -1491,16 +1512,11 @@ function fulfilled(opt_value) {
|
|
|
1491
1512
|
* Creates a promise that has been rejected with the given reason.
|
|
1492
1513
|
* @param {*=} opt_reason The rejection reason; may be any value, but is
|
|
1493
1514
|
* usually an Error or a string.
|
|
1494
|
-
* @return {!ManagedPromise
|
|
1495
|
-
* @
|
|
1515
|
+
* @return {!ManagedPromise<?>} The rejected promise.
|
|
1516
|
+
* @deprecated Use {@link ManagedPromise#reject Promise.reject(reason)}.
|
|
1496
1517
|
*/
|
|
1497
1518
|
function rejected(opt_reason) {
|
|
1498
|
-
|
|
1499
|
-
return opt_reason;
|
|
1500
|
-
}
|
|
1501
|
-
return new ManagedPromise(function(_, reject) {
|
|
1502
|
-
reject(opt_reason);
|
|
1503
|
-
});
|
|
1519
|
+
return ManagedPromise.reject(opt_reason);
|
|
1504
1520
|
}
|
|
1505
1521
|
|
|
1506
1522
|
|
|
@@ -2045,9 +2061,11 @@ function scheduleWait(scheduler, condition, opt_timeout, opt_message) {
|
|
|
2045
2061
|
let start = Date.now();
|
|
2046
2062
|
let timer = setTimeout(function() {
|
|
2047
2063
|
timer = null;
|
|
2048
|
-
reject(
|
|
2049
|
-
|
|
2050
|
-
|
|
2064
|
+
reject(
|
|
2065
|
+
new error.TimeoutError(
|
|
2066
|
+
(opt_message ? opt_message + '\n' : '')
|
|
2067
|
+
+ 'Timed out waiting for promise to resolve after '
|
|
2068
|
+
+ (Date.now() - start) + 'ms'));
|
|
2051
2069
|
}, timeout);
|
|
2052
2070
|
|
|
2053
2071
|
/** @type {Thenable} */(condition).then(
|
|
@@ -2085,8 +2103,10 @@ function scheduleWait(scheduler, condition, opt_timeout, opt_message) {
|
|
|
2085
2103
|
if (!!value) {
|
|
2086
2104
|
fulfill(value);
|
|
2087
2105
|
} else if (timeout && elapsed >= timeout) {
|
|
2088
|
-
reject(
|
|
2089
|
-
|
|
2106
|
+
reject(
|
|
2107
|
+
new error.TimeoutError(
|
|
2108
|
+
(opt_message ? opt_message + '\n' : '')
|
|
2109
|
+
+ `Wait timed out after ${elapsed}ms`));
|
|
2090
2110
|
} else {
|
|
2091
2111
|
// Do not use asyncRun here because we need a non-micro yield
|
|
2092
2112
|
// here so the UI thread is given a chance when running in a
|
package/lib/webdriver.js
CHANGED
|
@@ -497,6 +497,7 @@ class IWebDriver {
|
|
|
497
497
|
* function, or rejected if the condition times out. If the input
|
|
498
498
|
* input condition is an instance of a {@link WebElementCondition},
|
|
499
499
|
* the returned value will be a {@link WebElementPromise}.
|
|
500
|
+
* @throws {TypeError} if the provided `condition` is not a valid type.
|
|
500
501
|
* @template T
|
|
501
502
|
*/
|
|
502
503
|
wait(condition, opt_timeout, opt_message) {}
|
|
@@ -932,6 +933,12 @@ class WebDriver {
|
|
|
932
933
|
fn = condition.fn;
|
|
933
934
|
}
|
|
934
935
|
|
|
936
|
+
if (typeof fn !== 'function') {
|
|
937
|
+
throw TypeError(
|
|
938
|
+
'Wait condition must be a promise-like object, function, or a '
|
|
939
|
+
+ 'Condition object');
|
|
940
|
+
}
|
|
941
|
+
|
|
935
942
|
var driver = this;
|
|
936
943
|
var result = this.flow_.wait(function() {
|
|
937
944
|
if (promise.isGenerator(fn)) {
|
package/package.json
CHANGED
|
@@ -22,6 +22,7 @@ const fail = assert.fail;
|
|
|
22
22
|
const sinon = require('sinon');
|
|
23
23
|
|
|
24
24
|
const testutil = require('./testutil');
|
|
25
|
+
const {TimeoutError} = require('../../lib/error');
|
|
25
26
|
const promise = require('../../lib/promise');
|
|
26
27
|
const {enablePromiseManager} = require('../../lib/test/promise');
|
|
27
28
|
|
|
@@ -1133,6 +1134,7 @@ describe('promise control flow', function() {
|
|
|
1133
1134
|
default:
|
|
1134
1135
|
fail('unexpected polling count: ' + count);
|
|
1135
1136
|
}
|
|
1137
|
+
assert.ok(e instanceof TimeoutError, 'Unexpected error: ' + e);
|
|
1136
1138
|
assert.ok(
|
|
1137
1139
|
/^counting to 3\nWait timed out after \d+ms$/.test(e.message));
|
|
1138
1140
|
});
|
|
@@ -1190,6 +1192,7 @@ describe('promise control flow', function() {
|
|
|
1190
1192
|
it('promiseThatDoesNotResolveBeforeTimeout', function() {
|
|
1191
1193
|
var d = promise.defer();
|
|
1192
1194
|
flow.wait(d.promise, 5).then(fail, function(e) {
|
|
1195
|
+
assert.ok(e instanceof TimeoutError, 'Unexpected error: ' + e);
|
|
1193
1196
|
assert.ok(
|
|
1194
1197
|
/Timed out waiting for promise to resolve after \d+ms/
|
|
1195
1198
|
.test(e.message),
|
|
@@ -30,6 +30,7 @@ const logging = require('../../lib/logging');
|
|
|
30
30
|
const Session = require('../../lib/session').Session;
|
|
31
31
|
const promise = require('../../lib/promise');
|
|
32
32
|
const {enablePromiseManager, promiseManagerSuite} = require('../../lib/test/promise');
|
|
33
|
+
const until = require('../../lib/until');
|
|
33
34
|
const Alert = require('../../lib/webdriver').Alert;
|
|
34
35
|
const AlertPromise = require('../../lib/webdriver').AlertPromise;
|
|
35
36
|
const UnhandledAlertError = require('../../lib/webdriver').UnhandledAlertError;
|
|
@@ -1549,43 +1550,26 @@ describe('WebDriver', function() {
|
|
|
1549
1550
|
});
|
|
1550
1551
|
|
|
1551
1552
|
describe('waiting', function() {
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
var driver = executor.createDriver();
|
|
1564
|
-
driver.wait(function() {
|
|
1565
|
-
return driver.findElements(By.id('foo')).then(els => els.length > 0);
|
|
1566
|
-
}, 200);
|
|
1567
|
-
return waitForIdle();
|
|
1568
|
-
});
|
|
1569
|
-
|
|
1570
|
-
it('waitTimesout_timeoutCaught', function() {
|
|
1571
|
-
let executor = new FakeExecutor().
|
|
1572
|
-
expect(CName.FIND_ELEMENTS,
|
|
1573
|
-
{using: 'css selector', value: '*[id="foo"]'}).
|
|
1574
|
-
andReturnSuccess([]).
|
|
1575
|
-
anyTimes().
|
|
1576
|
-
end();
|
|
1553
|
+
describe('supports custom wait functions', function() {
|
|
1554
|
+
it('waitSucceeds', function() {
|
|
1555
|
+
let executor = new FakeExecutor().
|
|
1556
|
+
expect(CName.FIND_ELEMENTS,
|
|
1557
|
+
{using: 'css selector', value: '*[id="foo"]'}).
|
|
1558
|
+
andReturnSuccess([]).
|
|
1559
|
+
times(2).
|
|
1560
|
+
expect(CName.FIND_ELEMENTS,
|
|
1561
|
+
{using: 'css selector', value: '*[id="foo"]'}).
|
|
1562
|
+
andReturnSuccess([WebElement.buildId('bar')]).
|
|
1563
|
+
end();
|
|
1577
1564
|
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
e.message.substring(0, 'Wait timed out after '.length));
|
|
1565
|
+
var driver = executor.createDriver();
|
|
1566
|
+
driver.wait(function() {
|
|
1567
|
+
return driver.findElements(By.id('foo')).then(els => els.length > 0);
|
|
1568
|
+
}, 200);
|
|
1569
|
+
return waitForIdle();
|
|
1584
1570
|
});
|
|
1585
|
-
});
|
|
1586
1571
|
|
|
1587
|
-
|
|
1588
|
-
it('waitTimesout_timeoutNotCaught', function() {
|
|
1572
|
+
it('waitTimesout_timeoutCaught', function() {
|
|
1589
1573
|
let executor = new FakeExecutor().
|
|
1590
1574
|
expect(CName.FIND_ELEMENTS,
|
|
1591
1575
|
{using: 'css selector', value: '*[id="foo"]'}).
|
|
@@ -1594,14 +1578,94 @@ describe('WebDriver', function() {
|
|
|
1594
1578
|
end();
|
|
1595
1579
|
|
|
1596
1580
|
var driver = executor.createDriver();
|
|
1597
|
-
driver.wait(function() {
|
|
1581
|
+
return driver.wait(function() {
|
|
1598
1582
|
return driver.findElements(By.id('foo')).then(els => els.length > 0);
|
|
1599
|
-
}, 25)
|
|
1600
|
-
return waitForAbort().then(function(e) {
|
|
1583
|
+
}, 25).then(fail, function(e) {
|
|
1601
1584
|
assert.equal('Wait timed out after ',
|
|
1602
1585
|
e.message.substring(0, 'Wait timed out after '.length));
|
|
1603
1586
|
});
|
|
1604
1587
|
});
|
|
1588
|
+
|
|
1589
|
+
enablePromiseManager(() => {
|
|
1590
|
+
it('waitTimesout_timeoutNotCaught', function() {
|
|
1591
|
+
let executor = new FakeExecutor().
|
|
1592
|
+
expect(CName.FIND_ELEMENTS,
|
|
1593
|
+
{using: 'css selector', value: '*[id="foo"]'}).
|
|
1594
|
+
andReturnSuccess([]).
|
|
1595
|
+
anyTimes().
|
|
1596
|
+
end();
|
|
1597
|
+
|
|
1598
|
+
var driver = executor.createDriver();
|
|
1599
|
+
driver.wait(function() {
|
|
1600
|
+
return driver.findElements(By.id('foo')).then(els => els.length > 0);
|
|
1601
|
+
}, 25);
|
|
1602
|
+
return waitForAbort().then(function(e) {
|
|
1603
|
+
assert.equal('Wait timed out after ',
|
|
1604
|
+
e.message.substring(0, 'Wait timed out after '.length));
|
|
1605
|
+
});
|
|
1606
|
+
});
|
|
1607
|
+
});
|
|
1608
|
+
});
|
|
1609
|
+
|
|
1610
|
+
describe('supports condition objects', function() {
|
|
1611
|
+
it('wait succeeds', function() {
|
|
1612
|
+
let executor = new FakeExecutor()
|
|
1613
|
+
.expect(CName.FIND_ELEMENTS,
|
|
1614
|
+
{using: 'css selector', value: '*[id="foo"]'})
|
|
1615
|
+
.andReturnSuccess([])
|
|
1616
|
+
.times(2)
|
|
1617
|
+
.expect(CName.FIND_ELEMENTS,
|
|
1618
|
+
{using: 'css selector', value: '*[id="foo"]'})
|
|
1619
|
+
.andReturnSuccess([WebElement.buildId('bar')])
|
|
1620
|
+
.end();
|
|
1621
|
+
|
|
1622
|
+
let driver = executor.createDriver();
|
|
1623
|
+
return driver.wait(until.elementLocated(By.id('foo')), 200);
|
|
1624
|
+
});
|
|
1625
|
+
|
|
1626
|
+
it('wait times out', function() {
|
|
1627
|
+
let executor = new FakeExecutor()
|
|
1628
|
+
.expect(CName.FIND_ELEMENTS,
|
|
1629
|
+
{using: 'css selector', value: '*[id="foo"]'})
|
|
1630
|
+
.andReturnSuccess([])
|
|
1631
|
+
.anyTimes()
|
|
1632
|
+
.end();
|
|
1633
|
+
|
|
1634
|
+
let driver = executor.createDriver();
|
|
1635
|
+
return driver.wait(until.elementLocated(By.id('foo')), 5)
|
|
1636
|
+
.then(fail, err => assert.ok(err instanceof error.TimeoutError));
|
|
1637
|
+
});
|
|
1638
|
+
});
|
|
1639
|
+
|
|
1640
|
+
describe('supports promise objects', function() {
|
|
1641
|
+
it('wait succeeds', function() {
|
|
1642
|
+
let promise = new Promise(resolve => {
|
|
1643
|
+
setTimeout(() => resolve(1), 10);
|
|
1644
|
+
});
|
|
1645
|
+
|
|
1646
|
+
let driver = new FakeExecutor().createDriver();
|
|
1647
|
+
return driver.wait(promise, 200).then(v => assert.equal(v, 1));
|
|
1648
|
+
});
|
|
1649
|
+
|
|
1650
|
+
it('wait times out', function() {
|
|
1651
|
+
let promise = new Promise(resolve => {/* never resolves */});
|
|
1652
|
+
|
|
1653
|
+
let driver = new FakeExecutor().createDriver();
|
|
1654
|
+
return driver.wait(promise, 5)
|
|
1655
|
+
.then(fail, err => assert.ok(err instanceof error.TimeoutError));
|
|
1656
|
+
});
|
|
1657
|
+
|
|
1658
|
+
it('wait fails if promise is rejected', function() {
|
|
1659
|
+
let err = Error('boom');
|
|
1660
|
+
let driver = new FakeExecutor().createDriver();
|
|
1661
|
+
return driver.wait(Promise.reject(err), 5)
|
|
1662
|
+
.then(fail, e => assert.strictEqual(e, err));
|
|
1663
|
+
});
|
|
1664
|
+
});
|
|
1665
|
+
|
|
1666
|
+
it('fails if not supported condition type provided', function() {
|
|
1667
|
+
let driver = new FakeExecutor().createDriver();
|
|
1668
|
+
assert.throws(() => driver.wait({}, 5), TypeError);
|
|
1605
1669
|
});
|
|
1606
1670
|
});
|
|
1607
1671
|
|
|
@@ -1773,7 +1837,7 @@ describe('WebDriver', function() {
|
|
|
1773
1837
|
|
|
1774
1838
|
describe('actions()', function() {
|
|
1775
1839
|
it('failsIfInitialDriverCreationFailed', function() {
|
|
1776
|
-
let session = Promise.reject(new StubError);
|
|
1840
|
+
let session = Promise.reject(new StubError('no session for you'));
|
|
1777
1841
|
let driver = new FakeExecutor().createDriver(session);
|
|
1778
1842
|
driver.getSession().catch(function() {});
|
|
1779
1843
|
return driver.
|