npm install wd
- Adam Christian (admc)
- Ruben Daniels (javruben)
- Peter Braden (peterbraden)
- Seb Vincent (sebv)
- Peter 'Pita' Martischka (pita)
- Jonathan Lipps (jlipps)
- Phil Sarin (pdsarin)
- Mathieu Sabourin (OniOni)
- Bjorn Tipling (btipling)
- Santiago Suarez Ordonez (santiycr)
- Bernard Kobos (bernii)
- Jason Carr (maudineormsby)
- Matti Schneider (MattiSG)
- License - Apache 2: http://www.apache.org/licenses/LICENSE-2.0
): wd shell > x = wd.remote() or wd.remote("ondemand.saucelabs.com", 80, "username", "apikey") > x.init() or x.init({desired capabilities override}) > x.get("http://www.url.com") > x.eval("window.location.href", function(e, o) { console.log(o) }) > x.quit()
var wd = require('wd') , assert = require('assert') , colors = require('colors') , browser = wd.remote(); browser.on('status', function(info) { console.log(info.cyan); }); browser.on('command', function(meth, path, data) { console.log(' > ' + meth.yellow, path.grey, data || ''); }); browser.init({ browserName:'chrome' , tags : ["examples"] , name: "This is an example test" }, function() { browser.get("http://admc.io/wd/test-pages/guinea-pig.html", function() { browser.title(function(err, title) { assert.ok(~title.indexOf('I am a page title - Sauce Labs'), 'Wrong title!'); browser.elementById('i am a link', function(err, el) { browser.clickElement(el, function() { browser.eval("window.location.href", function(err, href) { assert.ok(~href.indexOf('guinea-pig2')); browser.quit(); }); }); }); }); }); });
var browser = wd.remote(); // or var browser = wd.remote('localhost'); // or var browser = wd.remote('localhost', 8888); // or var browser = wd.remote("ondemand.saucelabs.com", 80, "username", "apikey");
The parameters used are similar to those in the url module.
var browser = wd.remote() // or var browser = wd.remote({ hostname: '127.0.0.1', port: 4444, user: 'username', pwd: 'password', }); // or var browser = wd.remote({ hostname: '127.0.0.1', port: 4444, auth: 'username:password', });
The following parameters may also be used (as in earlier versions):
var browser = wd.remote({ host: '127.0.0.1', port: 4444, username: 'username', accessKey: 'password', });
var browser = wd.remote('http://localhost:4444/wd/hub'); // or var browser = wd.remote('http://user:[email protected]/wd/hub');
var url = require('url'); var browser = wd.remote(url.parse('http://localhost:4444/wd/hub')); // or var browser = wd.remote(url.parse('http://user:[email protected]:80/wd/hub'));
{ protocol: 'http:' hostname: '127.0.0.1', port: '4444' path: '/wd/hub' }
When connecting to Saucelabs, the user
and pwd
fields can also be set through the SAUCE_USERNAME
and SAUCE_ACCESS_KEY
environment variables.
A promise api using q is available. Code sample is here.
Yiewd is a wrapper around Wd.js that uses generators in order to avoid nested callbacks, like so:
wd.remote(function*() {
yield this.init(desiredCaps);
yield this.get("http://mysite.com");
el = yield this.elementById("someId");
yield el.click();
el2 = yield this.elementById("anotherThing")
text = yield el2.text();
text.should.equal("What the text should be");
yield this.quit();
});
A chain api is also available. Code sample is here.
As queue implementation that we're using has some limitations, a special helper method next was added. It allows you to inject new calls to the execution chain inside callbacks.
browser
.chain()
// ...
.elementById('i am a link', function(err, el) {
// following call will be executed apart from the current execution chain
// you won't be able to pass results further in chain
// and it may cause racing conditions in your script
browser.clickElement(el, function() {
console.log("did the click!");
});
})
// ...
browser
.chain()
// ...
.elementById('i am a link', function(err, el) {
// call to clickElement will be injected to the queue
// and will be executed sequentially after current function finishes
browser.next('clickElement', el, function() {
console.log("did the click!");
});
})
// ...
browser
.chain()
// ...
.elementById('i am a link', function(err, el) {
// following call will be executed apart from the current execution chain
// you won't be able to pass results further in chain
// and it may cause racing conditions in your script
})
.queueAddAsync( function(cb) {
// your code here
cb(null);
})
.clickElement(el, function() {
console.log("did the click!");
})
// ...
JsonWireProtocol | wd |
GET /status Query the server's current status. |
status(cb) -> cb(err, status) |
POST /session Create a new session. |
init(desired, cb) -> cb(err, sessionID) Initialize the browser. |
GET /sessions Returns a list of the currently active sessions. |
sessions(cb) -> cb(err, sessions)
Alternate strategy to get session capabilities from server session list: |
GET /session/:sessionId Retrieve the capabilities of the specified session. |
sessionCapabilities(cb) -> cb(err, capabilities) |
DELETE /session/:sessionId Delete the session. |
quit(cb) -> cb(err) Destroy the browser. |
POST /session/:sessionId/timeouts Configure the amount of time that a particular type of operation can execute for before they are aborted and a |Timeout| error is returned to the client. |
setPageLoadTimeout(ms, cb) -> cb(err) (use setImplicitWaitTimeout and setAsyncScriptTimeout to set the other timeouts) |
POST /session/:sessionId/timeouts/async_script Set the amount of time, in milliseconds, that asynchronous scripts executed by /session/:sessionId/execute_async are permitted to run before they are aborted and a |Timeout| error is returned to the client. |
setAsyncScriptTimeout(ms, cb) -> cb(err) |
POST /session/:sessionId/timeouts/implicit_wait Set the amount of time the driver should wait when searching for elements. |
setImplicitWaitTimeout(ms, cb) -> cb(err) |
GET /session/:sessionId/window_handle Retrieve the current window handle. |
windowHandle(cb) -> cb(err, handle) |
GET /session/:sessionId/window_handles Retrieve the list of all window handles available to the session. |
windowHandles(cb) -> cb(err, arrayOfHandles) |
GET /session/:sessionId/url Retrieve the URL of the current page. |
url(cb) -> cb(err, url) |
POST /session/:sessionId/url Navigate to a new URL. |
get(url,cb) -> cb(err) Get a new url. |
POST /session/:sessionId/forward Navigate forwards in the browser history, if possible. |
forward(cb) -> cb(err) |
POST /session/:sessionId/back Navigate backwards in the browser history, if possible. |
back(cb) -> cb(err) |
POST /session/:sessionId/refresh Refresh the current page. |
refresh(cb) -> cb(err) |
POST /session/:sessionId/execute Inject a snippet of JavaScript into the page for execution in the context of the currently selected frame. |
execute(code, args, cb) -> cb(err, result)
Safely execute script within an eval block, always returning:
Evaluate expression (using execute):
Safely evaluate expression, always returning (using safeExecute): |
POST /session/:sessionId/execute_async Inject a snippet of JavaScript into the page for execution in the context of the currently selected frame. |
executeAsync(code, args, cb) -> cb(err, result)
Safely execute async script within an eval block, always returning: |
GET /session/:sessionId/screenshot Take a screenshot of the current page. |
takeScreenshot(cb) -> cb(err, screenshot) |
POST /session/:sessionId/frame Change focus to another frame on the page. |
frame(frameRef, cb) -> cb(err) |
POST /session/:sessionId/window Change focus to another window. |
window(name, cb) -> cb(err) |
DELETE /session/:sessionId/window Close the current window. |
close(cb) -> cb(err) |
POST /session/:sessionId/window/:windowHandle/size Change the size of the specified window. |
windowSize(handle, width, height, cb) -> cb(err)
setWindowSize(width, height, handle, cb) -> cb(err) |
GET /session/:sessionId/window/:windowHandle/size Get the size of the specified window. |
getWindowSize(handle, cb) -> cb(err, size) getWindowSize(cb) -> cb(err, size) handle: window handle to get size (optional, default: 'current') |
POST /session/:sessionId/window/:windowHandle/position Change the position of the specified window. |
setWindowPosition(x, y, handle, cb) -> cb(err) setWindowPosition(x, y, cb) -> cb(err) x: the x-coordinate in pixels to set the window position y: the y-coordinate in pixels to set the window position handle: window handle to set position for (optional, default: 'current') |
GET /session/:sessionId/window/:windowHandle/position Get the position of the specified window. |
getWindowPosition(handle, cb) -> cb(err, position) getWindowPosition(cb) -> cb(err, position) handle: window handle to get position (optional, default: 'current') |
POST /session/:sessionId/window/:windowHandle/maximize Maximize the specified window if not already maximized. |
maximize(handle, cb) -> cb(err) |
GET /session/:sessionId/cookie Retrieve all cookies visible to the current page. |
allCookies() -> cb(err, cookies) |
POST /session/:sessionId/cookie Set a cookie. |
setCookie(cookie, cb) -> cb(err) cookie example: {name:'fruit', value:'apple'} Optional cookie fields: path, domain, secure, expiry |
DELETE /session/:sessionId/cookie Delete all cookies visible to the current page. |
deleteAllCookies(cb) -> cb(err) |
DELETE /session/:sessionId/cookie/:name Delete the cookie with the given name. |
deleteCookie(name, cb) -> cb(err) |
GET /session/:sessionId/source Get the current page source. |
source(cb) -> cb(err, source) |
GET /session/:sessionId/title Get the current page title. |
title(cb) -> cb(err, title) |
POST /session/:sessionId/element Search for an element on the page, starting from the document root. |
element(using, value, cb) -> cb(err, element)
elementByClassName(value, cb) -> cb(err, element) |
POST /session/:sessionId/elements Search for multiple elements on the page, starting from the document root. |
elements(using, value, cb) -> cb(err, elements)
elementsByClassName(value, cb) -> cb(err, elements)
Retrieve an element avoiding not found exception and returning null instead:
elementByClassNameOrNull(value, cb) -> cb(err, element)
Retrieve an element avoiding not found exception and returning undefined instead:
elementByClassNameIfExists(value, cb) -> cb(err, element)
Check if element exists:
hasElementByClassName(value, cb) -> cb(err, boolean) |
POST /session/:sessionId/element/active Get the element on the page that currently has focus. |
active(cb) -> cb(err, element) |
POST /session/:sessionId/element/:id/element Search for an element on the page, starting from the identified element. |
element.element(using, value, cb) -> cb(err, element)
element.elementByClassName(value, cb) -> cb(err, element) |
POST /session/:sessionId/element/:id/elements Search for multiple elements on the page, starting from the identified element. |
element.elements(using, value, cb) -> cb(err, elements)
element.elementsByClassName(value, cb) -> cb(err, elements) |
POST /session/:sessionId/element/:id/click Click on an element. |
clickElement(element, cb) -> cb(err)
element.click(cb) -> cb(err) |
POST /session/:sessionId/element/:id/submit Submit a FORM element. |
submit(element, cb) -> cb(err)
element.submit(cb) -> cb(err) |
GET /session/:sessionId/element/:id/text Returns the visible text for the element. |
text(element, cb) -> cb(err, text)
element.text(cb) -> cb(err, text)
Check if text is present:
element.textPresent(searchText, cb) -> cb(err, boolean) |
POST /session/:sessionId/element/:id/value Send a sequence of key strokes to an element. |
type(element, keys, cb) -> cb(err)
element.type(keys, cb) -> cb(err) |
POST /session/:sessionId/keys Send a sequence of key strokes to the active element. |
keys(keys, cb) -> cb(err) Press keys (keys may still be down at the end of command). special key map: wd.SPECIAL_KEYS (see lib/special-keys.js) |
GET /session/:sessionId/element/:id/name Query for an element's tag name. |
getTagName(element, cb) -> cb(err, name)
element.getTagName(cb) -> cb(err, name) |
POST /session/:sessionId/element/:id/clear Clear a TEXTAREA or text INPUT element's value. |
clear(element, cb) -> cb(err)
element.clear(cb) -> cb(err) |
GET /session/:sessionId/element/:id/selected Determine if an OPTION element, or an INPUT element of type checkbox or radiobutton is currently selected. |
isSelected(element, cb) -> cb(err, selected)
element.isSelected(cb) -> cb(err, selected) |
GET /session/:sessionId/element/:id/enabled Determine if an element is currently enabled. |
isEnabled(element, cb) -> cb(err, enabled)
element.isEnabled(cb) -> cb(err, enabled) |
GET /session/:sessionId/element/:id/attribute/:name Get the value of an element's attribute. |
getAttribute(element, attrName, cb) -> cb(err, value)
element.getAttribute(attrName, cb) -> cb(err, value)
Get element value (in value attribute):
element.getValue(cb) -> cb(err, value) |
GET /session/:sessionId/element/:id/equals/:other Test if two element IDs refer to the same DOM element. |
element.equals(other, cb) -> cb(err, value)
equalsElement(element, other , cb) -> cb(err, value) |
GET /session/:sessionId/element/:id/displayed Determine if an element is currently displayed. |
isDisplayed(element, cb) -> cb(err, displayed)
element.isDisplayed(cb) -> cb(err, displayed) |
GET /session/:sessionId/element/:id/location Determine an element's location on the page. |
getLocation(element, cb) -> cb(err, location)
element.getLocation(cb) -> cb(err, location)
element.getLocationInView(cb) -> cb(err, location) |
GET /session/:sessionId/element/:id/location_in_view Determine an element's location on the screen once it has been scrolled into view. |
getLocationInView(element, cb) -> cb(err, location) |
GET /session/:sessionId/element/:id/size Determine an element's size in pixels. |
getSize(element, cb) -> cb(err, size)
element.getSize(cb) -> cb(err, size) |
GET /session/:sessionId/element/:id/css/:propertyName Query the value of an element's computed CSS property. |
getComputedCss(element, cssProperty , cb) -> cb(err, value)
element.getComputedCss(cssProperty , cb) -> cb(err, value)
element.getComputedCss(cssProperty , cb) -> cb(err, value) |
GET /session/:sessionId/orientation Get the current browser orientation. |
getOrientation(cb) -> cb(err, orientation) |
POST /session/:sessionId/orientation Set the browser orientation. |
setOrientation(cb) -> cb(err, orientation) |
GET /session/:sessionId/alert_text Gets the text of the currently displayed JavaScript alert(), confirm(), or prompt() dialog. |
alertText(cb) -> cb(err, text) |
POST /session/:sessionId/alert_text Sends keystrokes to a JavaScript prompt() dialog. |
alertKeys(keys, cb) -> cb(err) |
POST /session/:sessionId/accept_alert Accepts the currently displayed alert dialog. |
acceptAlert(cb) -> cb(err) |
POST /session/:sessionId/dismiss_alert Dismisses the currently displayed alert dialog. |
dismissAlert(cb) -> cb(err) |
POST /session/:sessionId/moveto Move the mouse by an offset of the specificed element. |
moveTo(element, xoffset, yoffset, cb) -> cb(err) Move to element, xoffset and y offset are optional. |
POST /session/:sessionId/click Click any mouse button (at the coordinates set by the last moveto command). |
click(button, cb) -> cb(err) Click on current element. Buttons: {left: 0, middle: 1 , right: 2} |
POST /session/:sessionId/buttondown Click and hold the left mouse button (at the coordinates set by the last moveto command). |
buttonDown(cb) -> cb(err) |
POST /session/:sessionId/buttonup Releases the mouse button previously held (where the mouse is currently at). |
buttonUp(cb) -> cb(err) |
POST /session/:sessionId/doubleclick Double-clicks at the current mouse coordinates (set by moveto). |
doubleclick(cb) -> cb(err)
element.doubleClick(cb) -> cb(err) |
POST /session/:sessionId/touch/flick Flick on the touch screen using finger motion events. |
flick(xSpeed, ySpeed, swipe, cb) -> cb(err)
element.flick(xoffset, yoffset, speed, cb) -> cb(err) |
POST /session/:sessionId/local_storage Set the storage item for the given key. |
setLocalStorageKey(key, value, cb) -> cb(err) # uses safeExecute() due to localStorage bug in Selenium |
DELETE /session/:sessionId/local_storage Clear the storage. |
clearLocalStorage(cb) -> cb(err) # uses safeExecute() due to localStorage bug in Selenium |
GET /session/:sessionId/local_storage/key/:key Get the storage item for the given key. |
getLocalStorageKey(key, cb) -> cb(err) # uses safeEval() due to localStorage bug in Selenium |
DELETE /session/:sessionId/local_storage/key/:key Remove the storage item for the given key. |
removeLocalStorageKey(key, cb) -> cb(err) # uses safeExecute() due to localStorage bug in Selenium |
EXTRA |
Opens a new window (using Javascript window.open): newWindow(url, name, cb) -> cb(err) newWindow(url, cb) -> cb(err) name: optional window name Window can later be accessed by name with the window method, or by getting the last handle returned by the windowHandles method. |
EXTRA |
windowName(cb) -> cb(err, name) |
EXTRA |
setHTTPInactivityTimeout(ms) ms: how many milliseconds to wait for any communication with the WebDriver server (i.e. any command to complete) before the connection is considered lost |
EXTRA |
waitForElement(using, value, timeout, cb) -> cb(err) |
EXTRA |
waitForVisible(using, value, timeout, cb) -> cb(err) |
EXTRA |
waitForElementByClassName(value, timeout, cb) -> cb(err) waitForElementByCssSelector(value, timeout, cb) -> cb(err) waitForElementById(value, timeout, cb) -> cb(err) waitForElementByName(value, timeout, cb) -> cb(err) waitForElementByLinkText(value, timeout, cb) -> cb(err) waitForElementByPartialLinkText(value, timeout, cb) -> cb(err) waitForElementByTagName(value, timeout, cb) -> cb(err) waitForElementByXPath(value, timeout, cb) -> cb(err) waitForElementByCss(value, timeout, cb) -> cb(err) |
EXTRA |
waitForVisibleByClassName(value, timeout, cb) -> cb(err) waitForVisibleByCssSelector(value, timeout, cb) -> cb(err) waitForVisibleById(value, timeout, cb) -> cb(err) waitForVisibleByName(value, timeout, cb) -> cb(err) waitForVisibleByLinkText(value, timeout, cb) -> cb(err) waitForVisibleByPartialLinkText(value, timeout, cb) -> cb(err) waitForVisibleByTagName(value, timeout, cb) -> cb(err) waitForVisibleByXPath(value, timeout, cb) -> cb(err) waitForVisibleByCss(value, timeout, cb) -> cb(err) |
EXTRA |
isVisible(element , cb) -> cb(err, boolean) isVisible(queryType, querySelector, cb) -> cb(err, boolean) |
EXTRA |
Retrieves the pageIndex element (added for Appium): getPageIndex(element, cb) -> cb(err, pageIndex) |
EXTRA |
Uploads a local file using undocumented POST /session/:sessionId/file uploadFile(filepath, cb) -> cb(err, filepath) |
EXTRA |
Waits for JavaScript condition to be true (polling within wd client): waitForCondition(conditionExpr, timeout, pollFreq, cb) -> cb(err, boolean) waitForCondition(conditionExpr, timeout, cb) -> cb(err, boolean) waitForCondition(conditionExpr, cb) -> cb(err, boolean) conditionExpr: condition expression, should return a boolean timeout: timeout (optional, default: 1000) pollFreq: pooling frequency (optional, default: 100) return true if condition satisfied, error otherwise. |
EXTRA |
Waits for JavaScript condition to be true (async script polling within browser): waitForConditionInBrowser(conditionExpr, timeout, pollFreq, cb) -> cb(err, boolean) waitForConditionInBrowser(conditionExpr, timeout, cb) -> cb(err, boolean) waitForConditionInBrowser(conditionExpr, cb) -> cb(err, boolean) conditionExpr: condition expression, should return a boolean timeout: timeout (optional, default: 1000) pollFreq: pooling frequency (optional, default: 100) return true if condition satisfied, error otherwise. |
EXTRA |
Equivalent to the python sendKeys binding. Upload file if a local file is detected, otherwise behaves like type. element.sendKeys(keys, cb) -> cb(err) |
EXTRA |
isVisible(cb) -> cb(err, boolean) |
WD is simply implementing the Selenium JsonWireProtocol, for more details see the official docs: - http://code.google.com/p/selenium/wiki/JsonWireProtocol
- Install the Selenium server and Chromedriver node_modules/.bin/install_selenium node_modules/.bin/install_chromedriver - Run the selenium server with chromedriver: node_modules/.bin/start_selenium_with_chromedriver - cd wd - npm install . - make test - look at the results!
- cd wd - npm install . - make test_saucelabs
You may want to monkey patch the webdriver class in order to add custom functionalities. There is an example here.
If the method you want to use is not yet implemented, that should be
easy to add it to lib/webdriver.js
. You can use the doubleclick
method as a template for methods not returning data, and getOrientation
for methods which returns data. No need to modify README as the doc
generation is automated. Other contributions are welcomed.
The JsonWire mappings in the README and mapping files are generated from code comments using dox.
To update the mappings run the following commands:
- make mapping > doc/jsonwire-mapping.md - make full_mapping > doc/jsonwire-full-mapping.md - make unsupported_mapping > doc/jsonwire-unsupported-mapping.md
The content of doc/jsonwire-mapping.md should then be manually integrated into README.md.
The safeExecute
and safeEval
methods are equivalent to execute
and eval
but the code is
executed within a eval
block. They are safe in the sense that eventual
code syntax issues are tackled earlier returning as syntax error and
avoiding browser hanging in some cases.
An example below of expression hanging Chrome:
browser.eval("wrong!!!", function(err, res) { // hangs
browser.safeEval("wrong!!!", function(err, res) { // returns
browser.execute("wrong!!!", function(err, res) { //hangs
browser.safeExecute("wrong!!!", function(err, res) { //returns