Skip to content

Commit

Permalink
[js] Use the W3C set timeouts format. If this fails, fallback to the …
Browse files Browse the repository at this point in the history
…legacy

format.

This commit also updates the API to expose getTimeouts() and setTimeouts()
methods off of the Options class. The current Timeouts class is deprecated
in favor of setTimeouts(), which allows setting multiple timeouts at once.

For #3607
  • Loading branch information
jleyba committed Mar 10, 2017
1 parent 4e4d599 commit 0ca584d
Show file tree
Hide file tree
Showing 5 changed files with 228 additions and 11 deletions.
9 changes: 9 additions & 0 deletions javascript/node/selenium-webdriver/CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
## v.next

* Added `Options#getTimeouts()` for retrieving the currently configured session
timeouts (i.e. implicit wait). This method will only work with W3C compatible
WebDriver implementations.
* Deprecated the `Timeouts` class in favor of `Options#setTimeouts()`, which
supports setting multiple timeouts at once.


## v3.3.0

* Added warning log messages when the user creates new managed promises, or
Expand Down
2 changes: 2 additions & 0 deletions javascript/node/selenium-webdriver/lib/command.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,8 @@ const Name = {
TAKE_ELEMENT_SCREENSHOT: 'takeElementScreenshot',
IMPLICITLY_WAIT: 'implicitlyWait',
SET_SCRIPT_TIMEOUT: 'setScriptTimeout',

GET_TIMEOUT: 'getTimeout',
SET_TIMEOUT: 'setTimeout',

ACCEPT_ALERT: 'acceptAlert',
Expand Down
1 change: 1 addition & 0 deletions javascript/node/selenium-webdriver/lib/http.js
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@ const COMMAND_MAP = new Map([
[cmd.Name.EXECUTE_SCRIPT, post('/session/:sessionId/execute')],
[cmd.Name.EXECUTE_ASYNC_SCRIPT, post('/session/:sessionId/execute_async')],
[cmd.Name.SCREENSHOT, get('/session/:sessionId/screenshot')],
[cmd.Name.GET_TIMEOUT, get('/session/:sessionId/timeouts')],
[cmd.Name.SET_TIMEOUT, post('/session/:sessionId/timeouts')],
[cmd.Name.MOVE_TO, post('/session/:sessionId/moveto')],
[cmd.Name.CLICK, post('/session/:sessionId/click')],
Expand Down
125 changes: 114 additions & 11 deletions javascript/node/selenium-webdriver/lib/webdriver.js
Original file line number Diff line number Diff line change
Expand Up @@ -1326,6 +1326,91 @@ class Options {
});
}

/**
* Schedules a command to fetch the timeouts currently configured for the
* current session.
*
* @return {!promise.Thenable<{script: number,
* pageLoad: number,
* implicit: number}>} A promise that will be
* resolved with the timeouts currently configured for the current
* session.
* @see #setTimeouts()
*/
getTimeouts() {
return this.driver_.schedule(
new command.Command(command.Name.GET_TIMEOUT),
`WebDriver.manage().getTimeouts()`)
}

/**
* Schedules a command to set timeout durations associated with the current
* session.
*
* The following timeouts are supported (all timeouts are specified in
* milliseconds):
*
* - `implicit` specifies the maximum amount of time to wait for an element
* locator to succeed when {@linkplain WebDriver#findElement locating}
* {@linkplain WebDriver#findElements elements} on the page.
* Defaults to 0 milliseconds.
*
* - `pageLoad` specifies the maximum amount of time to wait for a page to
* finishing loading. Defaults to 300000 milliseconds.
*
* - `script` specifies the maximum amount of time to wait for an
* {@linkplain WebDriver#executeScript evaluated script} to run. If set to
* `null`, the script timeout will be indefinite.
* Defaults to 30000 milliseconds.
*
* @param {{script: (number|null|undefined),
* pageLoad: (number|null|undefined),
* implicit: (number|null|undefined)}} conf
* The desired timeout configuration.
* @return {!promise.Thenable<void>} A promise that will be resolved when the
* timeouts have been set.
* @throws {!TypeError} if an invalid options object is provided.
* @see #getTimeouts()
* @see <https://w3c.github.io/webdriver/webdriver-spec.html#dfn-set-timeouts>
*/
setTimeouts({script, pageLoad, implicit} = {}) {
let cmd = new command.Command(command.Name.SET_TIMEOUT);

let valid = false;
function setParam(key, value) {
if (value === null || typeof value === 'number') {
valid = true;
cmd.setParameter(key, value);
} else if (typeof value !== 'undefined') {
throw TypeError(
'invalid timeouts configuration:'
+ ` expected "${key}" to be a number, got ${typeof value}`);
}
}
setParam('implicit', implicit);
setParam('pageLoad', pageLoad);
setParam('script', script);

if (valid) {
return this.driver_.schedule(cmd, `WebDriver.manage().setTimeouts()`)
.catch(() => {
// Fallback to the legacy method.
let cmds = [];
if (typeof script === 'number') {
cmds.push(legacyTimeout(this.driver_, 'script', script));
}
if (typeof implicit === 'number') {
cmds.push(legacyTimeout(this.driver_, 'implicit', implicit));
}
if (typeof pageLoad === 'number') {
cmds.push(legacyTimeout(this.driver_, 'page load', pageLoad));
}
return Promise.all(cmds);
});
}
throw TypeError('no timeouts specified');
}

/**
* @return {!Logs} The interface for managing driver
* logs.
Expand All @@ -1336,6 +1421,7 @@ class Options {

/**
* @return {!Timeouts} The interface for managing driver timeouts.
* @deprecated Use {@link #setTimeouts()} instead.
*/
timeouts() {
return new Timeouts(this.driver_);
Expand All @@ -1350,6 +1436,22 @@ class Options {
}


/**
* @param {!WebDriver} driver
* @param {string} type
* @param {number} ms
* @return {!promise.Thenable<void>}
*/
function legacyTimeout(driver, type, ms) {
return driver.schedule(
new command.Command(command.Name.SET_TIMEOUT)
.setParameter('type', type)
.setParameter('ms', ms),
`WebDriver.manage().setTimeouts({${type}: ${ms}})`);
}



/**
* A record object describing a browser cookie.
*
Expand Down Expand Up @@ -1432,6 +1534,9 @@ Options.Cookie.prototype.expiry;
*
* webdriver.manage().timeouts()
*
* @deprecated This has been deprecated in favor of
* {@link Options#setTimeouts()}, which supports setting multiple timeouts
* at once.
* @see WebDriver#manage()
* @see Options#timeouts()
*/
Expand Down Expand Up @@ -1465,9 +1570,11 @@ class Timeouts {
* @param {number} ms The amount of time to wait, in milliseconds.
* @return {!promise.Thenable<void>} A promise that will be resolved
* when the implicit wait timeout has been set.
* @deprecated Use {@link Options#setTimeouts()
* driver.manage().setTimeouts({implicit: ms})}.
*/
implicitlyWait(ms) {
return this._scheduleCommand(ms, 'implicit', 'implicitlyWait');
return this.driver_.manage().setTimeouts({implicit: ms});
}

/**
Expand All @@ -1478,9 +1585,11 @@ class Timeouts {
* @param {number} ms The amount of time to wait, in milliseconds.
* @return {!promise.Thenable<void>} A promise that will be resolved
* when the script timeout has been set.
* @deprecated Use {@link Options#setTimeouts()
* driver.manage().setTimeouts({script: ms})}.
*/
setScriptTimeout(ms) {
return this._scheduleCommand(ms, 'script', 'setScriptTimeout');
return this.driver_.manage().setTimeouts({script: ms});
}

/**
Expand All @@ -1491,17 +1600,11 @@ class Timeouts {
* @param {number} ms The amount of time to wait, in milliseconds.
* @return {!promise.Thenable<void>} A promise that will be resolved
* when the timeout has been set.
* @deprecated Use {@link Options#setTimeouts()
* driver.manage().setTimeouts({pageLoad: ms})}.
*/
pageLoadTimeout(ms) {
return this._scheduleCommand(ms, 'page load', 'pageLoadTimeout');
}

_scheduleCommand(ms, timeoutIdentifier, timeoutName) {
return this.driver_.schedule(
new command.Command(command.Name.SET_TIMEOUT).
setParameter('type', timeoutIdentifier).
setParameter('ms', ms),
`WebDriver.manage().timeouts().${timeoutName}(${ms})`);
return this.driver_.manage().setTimeouts({pageLoad: ms});
}
}

Expand Down
102 changes: 102 additions & 0 deletions javascript/node/selenium-webdriver/test/lib/webdriver_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1952,6 +1952,108 @@ describe('WebDriver', function() {
});
});

describe('manage()', function() {
describe('setTimeouts()', function() {
describe('throws if no timeouts are specified', function() {
let driver;
before(() => driver = new FakeExecutor().createDriver());

it('; no arguments', function() {
assert.throws(() => driver.manage().setTimeouts(), TypeError);
});

it('; ignores unrecognized timeout keys', function() {
assert.throws(
() => driver.manage().setTimeouts({foo: 123}), TypeError);
});

it('; ignores positional arguments', function() {
assert.throws(
() => driver.manage().setTimeouts(1234, 56), TypeError);
});
});

describe('throws timeout is not a number, null, or undefined', () => {
let driver;
before(() => driver = new FakeExecutor().createDriver());

function checkError(e) {
return e instanceof TypeError
&& /expected "(script|pageLoad|implicit)" to be a number/.test(
e.message);
}

it('script', function() {
assert.throws(
() => driver.manage().setTimeouts({script: 'abc'}),
checkError);
});

it('pageLoad', function() {
assert.throws(
() => driver.manage().setTimeouts({pageLoad: 'abc'}),
checkError);
});

it('implicit', function() {
assert.throws(
() => driver.manage().setTimeouts({implicit: 'abc'}),
checkError);
});
});

it('can set multiple timeouts', function() {
let executor = new FakeExecutor()
.expect(CName.SET_TIMEOUT, {script:1, pageLoad: 2, implicit: 3})
.andReturnSuccess()
.end();
let driver = executor.createDriver();
return driver.manage()
.setTimeouts({script: 1, pageLoad: 2, implicit: 3});
});

it('falls back to legacy wire format if W3C version fails', () => {
let executor = new FakeExecutor()
.expect(CName.SET_TIMEOUT, {implicit: 3})
.andReturnError(Error('oops'))
.expect(CName.SET_TIMEOUT, {type: 'implicit', ms: 3})
.andReturnSuccess()
.end();
let driver = executor.createDriver();
return driver.manage().setTimeouts({implicit: 3});
});

describe('deprecated API calls setTimeouts()', function() {
it('implicitlyWait()', function() {
let executor = new FakeExecutor()
.expect(CName.SET_TIMEOUT, {implicit: 3})
.andReturnSuccess()
.end();
let driver = executor.createDriver();
return driver.manage().timeouts().implicitlyWait(3);
});

it('setScriptTimeout()', function() {
let executor = new FakeExecutor()
.expect(CName.SET_TIMEOUT, {script: 3})
.andReturnSuccess()
.end();
let driver = executor.createDriver();
return driver.manage().timeouts().setScriptTimeout(3);
});

it('pageLoadTimeout()', function() {
let executor = new FakeExecutor()
.expect(CName.SET_TIMEOUT, {pageLoad: 3})
.andReturnSuccess()
.end();
let driver = executor.createDriver();
return driver.manage().timeouts().pageLoadTimeout(3);
});
});
});
});

describe('generator support', function() {
var driver;

Expand Down

0 comments on commit 0ca584d

Please sign in to comment.