# WD.js -- WebDriver/Selenium 2 for node.js [![Build Status](https://secure.travis-ci.org/admc/wd.png?branch=master)](http://travis-ci.org/admc/wd) [![Selenium Test Status](https://saucelabs.com/buildstatus/wdjs)](https://saucelabs.com/u/wdjs) ## Update node to latest http://nodejs.org/#download ## Install
npm install wd## Authors - Adam Christian ([admc](http://github.com/admc)) - Ruben Daniels ([javruben](https://github.com/javruben)) - Peter Braden ([peterbraden](https://github.com/peterbraden)) - Seb Vincent ([sebv](https://github.com/sebv)) - Peter 'Pita' Martischka ([pita](https://github.com/Pita)) - Jonathan Lipps ([jlipps](https://github.com/jlipps)) - Phil Sarin ([pdsarin](https://github.com/pdsarin)) - Mathieu Sabourin ([OniOni](https://github.com/OniOni)) - Bjorn Tipling ([btipling](https://github.com/btipling)) - Santiago Suarez Ordonez ([santiycr](https://github.com/santiycr)) - Bernard Kobos ([bernii](https://github.com/bernii)) - Jason Carr ([maudineormsby](https://github.com/maudineormsby)) ## License * License - Apache 2: http://www.apache.org/licenses/LICENSE-2.0 ## Usage
): wd shell > x = wd.remote() or wd.remote("ondemand.saucelabs.com", 80, "username", "apikey") > x.init() or x.init({desired capabilities ovveride}) > x.get("http://www.url.com") > x.eval("window.location.href", function(e, o) { console.log(o) }) > x.quit()## Writing a test!
var webdriver = require('wd') , assert = require('assert'); var browser = webdriver.remote(); browser.on('status', function(info){ console.log('\x1b[36m%s\x1b[0m', info); }); browser.on('command', function(meth, path){ console.log(' > \x1b[33m%s\x1b[0m: %s', meth, path); }); desired = { browserName:'chrome' , tags: ["examples"] , name: "This is an example test" }; browser.init(desired, 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, location) { assert.ok(~location.indexOf('guinea-pig2')); browser.quit(); }); }); }); }); }); });## Promises Api A promise api using [q](https://github.com/kriskowal/q) is available. Code sample is [here](https://github.com/admc/wd/blob/master/examples/example.promise.chrome.js). ## Chain Api A chain api is also available. Code sample is [here](https://github.com/admc/wd/blob/master/examples/example.chain.chrome.js). ## Supported Methods
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)
Execute script using eval(code):
Evaluate expression (using execute):
Evaluate expression (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)
Execute async script using eval(code): |
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/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. |
elementByClassName(value, cb) -> cb(err, element) elementByCssSelector(value, cb) -> cb(err, element) elementById(value, cb) -> cb(err, element) elementByName(value, cb) -> cb(err, element) elementByLinkText(value, cb) -> cb(err, element) elementByPartialLinkText(value, cb) -> cb(err, element) elementByTagName(value, cb) -> cb(err, element) elementByXPath(value, cb) -> cb(err, element) elementByCss(value, cb) -> cb(err, element) |
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) Submit a `FORM` element. |
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) |
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/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) |
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) |
POST /session/:sessionId/touch/flick Flick on the touch screen using finger motion events. |
flick(xSpeed, ySpeed, cb) -> cb(err)
element.flick(xoffset, yoffset, speed, cb) -> cb(err) |
EXTRA |
esired, cb |
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 |
rl, name, cb |
EXTRA |
windowName(cb) -> cb(err, name) |
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 |
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 |
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## Run the tests!
- Run the selenium server with chromedriver: java -jar selenium-server-standalone-2.21.0.jar -Dwebdriver.chrome.driver=<PATH>/chromedriver - cd wd - npm install . - make test - look at the results!## Run the tests on Sauce Labs cloud!
- cd wd - npm install . - make test_saucelabs## Adding new method / Contributing 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. ## Doc The JsonWire mappings in the README and mapping files are generated from code comments using [dox](https://github.com/visionmedia/dox). To update the mappings run the following commands:
- make mapping > doc/jsonwire-mapping.md - make full_mapping > doc/jsonwire-full-mapping.mdThe content of doc/jsonwire-mapping.md should then be manually integrated into README.md. ## Test Coverage [test coverage](http://admc.io/wd/coverage.html)