WebSpec API
WebSpec is Watij’s new cross browser and cross platform api for testing web applications. It works with IE, Mozilla, and Safari on their respective platforms for Windows, Linux, and Mac. You can write your scripts using Java or Ruby (via JRuby). The following documentation includes examples of using both.
Installation
WebSpec can be used with your favorite Java or JRuby IDE like IntelliJ IDEA, Eclipse, Netbeans, Redcar, etc…all you need to do is add the jars located in the lib folder to your classpath. Next you can either copy over the WebSpec src and compile it with your project, or you can just add webspec.jar located in the java/src/dist folder to your classpath. If you are using JRuby you also have to require ‘web_spec’ in your ruby file.
Note: If you are running on a Mac you must add -d32 to your Java VM parameters.
Opening a URL
Method chaining is a big part of WebSpec’s api and almost every method call will return a WebSpec framework class. For example here is how you would open up a url in Mozilla:
Java
WebSpec spec = new WebSpec().mozilla(); spec.open("http://www.google.com"); //opens google.com in Mozilla
Ruby
spec = WebSpec.new.mozilla spec.open "http://www.google.com" #opens google.com in Mozilla
Using Safari or IE are just as easy:
Java
spec.safari().open("http://www.google.com"); //opens google.com in Safari spec.ie().open("http://www.google.com"); //opens google.com in Internet Explorer
Ruby
spec.safari.open "http://www.google.com" #opens google.com in Safari spec.ie.open "http://www.google.com" #opens google.com in Internet Explorer
Here are some additional methods on WebSpec for interacting with the browser.
WebSpec class:
public String url() //returns the browser's current url public String source() //returns the brower's source as a html string public void source(String html) //dynamically sets the browser's source to the html string public boolean isVisible() //returns true if the browser is displayed public WebSpec show() //displays the browser public WebSpec hide() //hides the browser public boolean busy() //returns true if the browser is busy public boolean ready() //returns true if the browser is not busy public boolean done() //returns false when pauseUntilDone is called and then returns true after someone manually clicks "Done" in the navigation bar public String execute(String script) //executes the provided Javascript in the browser and returns the result as a String public WebSpec eval(String script) //executes the provided Javascript in the browser public boolean isSafari() //returns true if the browser is a Safari browser public boolean isMozilla() //returns true if the browser is a Mozilla browser public boolean isIE() //returns true if the browser is an IE browser
HTML Tags and Javascript
WebSpec uses a simple dsl for finding the html tags you want to interact with. It’s based on the makeup of an HTML tag. Take for example the following textarea html tag:
<textarea id="mytext" onclick="window.alert('hello')" name="myname">some text</textarea>A Tag has a name, properties (or attributes), methods, and content. The above tag has the name “textarea”, the property “id” and “name”, the method onclick(), and the inner text “some text”.
Javascript plays a big role in WebSpec and it’s very important to understand its use. After finding an html tag, WebSpec allows you to call any Javascript method or property on that tag. When interacting with an html tag, WebSpec becomes a sophisticated Javascript proxy. Let’s take a look at what some WebSpec code is doing under the hood.
Below each line of the WebSpec code is a representation of the Javascript that is run, and the result of the Javascript code is handled by the WebSpec method being called.
Tag textarea = spec.findWithId("mytext"); //var temp1 = document.getElementById("mytext"); /*textarea now has a handle to the Javascript object temp1*/ textarea.set("value='somevalue'"); //temp1.value='somevalue'; /*textarea's value is now set to 'somevalue'*/ textarea.shouldHave("value=='somevalue'"); //temp1.value=='somevalue'; /*shouldHave expects the result of the Javascript call to be true*/ textarea.call("onclick()"); //temp1.onclick(); /*this calls the onclick() method on the Javascript object temp1*/ String name = textarea.get("name"); //temp1.name; /*get takes the result of the Javascript call and returns it as a String*/
A good understanding of the above will allow you to do almost anything you want with an html tag using WebSpec as a proxy. Here is a reference you can use to see most of the methods and properties available on an html tag http://www.w3schools.com/jsref/default.asp.
Java Convenience Methods
We now know that we can interact with an html tag by passing the appropriate Javascript as a String to WebSpec. Most Java IDE’s these days provide really nice auto-completion, but unfortunately they don’t know anything about a String’s content. So in order utilize the power of these IDE’s, WebSpec is now including the most frequently used html tags and properties as convenience methods. Let’s look at how we can rewrite the previous example above using the new convenience methods:
Tag textarea = spec.find.textArea().with.name("myname"); textarea.set.value("somevalue"); textarea.shouldHave.value("somevalue"); textarea.call.onClick(); String name = textarea.get.name();
These convenience methods are exposed through a public member variables on WebSpec and Tag:
WebSpec class:
public Find find //returns the Find class for finding tags through named methods public Record record //returns the Record class for recording javascript dialog actions
Tag class:
public Find find //returns the Find class for finding tags through named methods public Parent parent //returns the Parent class for finding parent tags through named methods public Child child //returns the Child class for finding child tags through named methods public With with //returns the With class for filtering tags by properties through named methods public Get get //returns the Get class for getting property values on a tag through named methods public Set set //returns the Set class for setting properties through named methods public ShouldHave shouldHave //returns the ShouldHave class for expecting property values through named methods public Call call //returns the Call class for calling javascript on a tag through named methods public All all //returns the All class for performing actions on all tags found
Ruby Dynamic API
When writing your scripts in Ruby you also have access to the dynamic api which let’s you code using the actual the tag name, properties, and methods. Let try the same example from before using the Ruby Dynamic API:
textarea = spec.text_area.name("myname") textarea.value = "somevalue" textarea.should_have.value == "somevalue" textarea.onClick() name = textarea.name
Finding Tags
The first step in interacting with a tag is finding it. WebSpec offers two ways to do this 1) using it’s own searching mechanism and 2) using JQuery selectors. Complete documentation on JQuery selectors can be found here http://api.jquery.com/category/selectors/. Here are the main finders provided by WebSpec:
WebSpec class:
public Tag find(String tag) //Finds all tags by tag name public Tag findWithId(String id) //Finds a single tag by id
Tag class:
public Tag find(String tag) //Finds all tags by tag name under this tag public Tag findWithId(String id) //Finds a single tag by id under this tag public Tag child(String tag) //Finds all the direct child tags by tag name under this tag public Tag parent(String tag) //Finds all the parent tags above this tag /*Filters*/ public Tag with(String with) //Filters tags by property, or tag's content (if no comparison operator is present) public Tag with(String property, String value) //Filters tags by property, if the first and last characters are "/" it does a match otherwise uses an equal comparison public Tag with(String property, Number value) //Filters tags by property using an equals comparison public Tag with(String property, boolean value) //Filters tags by property using an equals comparison public Tag at(int index) //Filters to single Tag at the given index
The following are examples of finding tags by various ways.
Find an input tag by the name property:
Java
Tag input = spec.find("input").with("name=='myinput'"); //Using the standard finders Tag input = spec.find("input").with("name", "myinput"); //Using the standard finders Tag input = spec.find.input().with.name("myinput"); //Using the Java convenience methods Tag input = spec.jquery("input[name=myinput]"); //Using JQuery selectors //Find the first span with innerText "Silver", then the 3rd parent div, then the first child ul, then flash all it's lis spec.find.span().with("Silver").parent.div().at(2).child.ul().find.li().all.flash();
Ruby
input = spec.find("input").with("name=='myinput'") #Using the standard finders input = spec.find("input").with("name", "myinput") #Using the standard finders input = spec.input.name("myinput") #Using the Ruby Dynamic API input = spec.jquery("input[name=myinput]") #Using JQuery selectors #Find the first span with innerText "Silver", then the 3rd parent div, then the first child ul, then flash all it's lis spec.span("Silver").parent.div[2].child.ul.li.all.flash
So when using WebSpec finders you first call the method WebSpec.find(String tag) and then filter to the one you want using Tag.with(Sting with). You can chain as many “with” calls as you like to further refine the results. You also can get a handle to a Tag by index using Tag.at(int index).
Interacting with Tags
WebSpec currently supports interacting with a Tag through the following methods
Tag class:
public Tag set(String set) //sets a property's value, or the tag's content (if no assignment operator is present) public Tag set(String property, String value) //sets a property's value to a String public Tag set(String property, Number value) //sets a property's value to a Number public Tag set(String property, boolean value) //sets a property's value to a boolean public String get(String property) //returns the property's value public Tag click() //simulates a mouse click public String call(String call) //calls the passed method and returns the result as a String public Tag flash() //flashes this tag on the browser so you can see where it is
By default you interact with the tag found at index zero. If you want to interact with all the found tags, first call the method “all”. Here are some examples of interacting with tags:
Java
spec.find("input").with("name", "email").set("value", "b@b.com"); //sets the value of the input field spec.find.input().with.type("checkbox").all.set.checked(true); //checks all the checkboxes on the page using only the Java convenience methods spec.jquery("input[type=button]").at(2).click(); //clicks the third button spec.find.a().all.flash(); //flashes all links in the browser using the Java convenience methods
The following examples show how you can use a combination of standard and Java convenience methods
spec.find.iframe().find.option().with("United States").set("selected=true"); //selects the option with content "United States" under the first iframe spec.find("input").with.type("radio").with("checked==true").get.value(); //gets the value of the checked radio button spec.findWithId("link0").get.innerText(); //returns the content of the link spec.jquery("select[name=myselect]").call.onChange(); //calls the onchange() method on the select tag
Ruby
spec.find("input").with("name=='email'").set("value='b@b.com'") #sets the value of the input field spec.input.type('checkbox').all.checked = true #checks all the checkboxes on the page using the Ruby Dynamic API spec.jquery("input[type=button]").at(2).click #clicks the third button spec.a.all.flash #flashes all links in the browser using the Ruby Dynamic API
The following examples show how you can use a combination of the standard and Ruby Dynamic API
spec.iframe.option.with("United States").set("selected=true") #selects the option with content "United States" under the first iframe spec.find("input").type('radio').with("checked==true").value #gets the value of the checked radio button spec.findWithId("link0").innerText #returns the content of the link spec.jquery("select[name=myselect]").onchange() #calls the onchange() method on the select tag
Dialogs
WebSpec has a special Dialog handler for the javascript dialogs: alert, confirm, and prompt. Due to their modal nature you have to record your actions before the dialog actually appears. Once it appears the recorded actions will be played. For example:
Java
//first record how you want to interact with the dialog spec.record.alert().ok(); //the following will bring up the alert and then the recorded interactions will be played spec.find("a").with("click for alert").click(); //confirm dialog spec.record.confirm().cancel(); spec.find("a").with("click for confirm").click(); //prompt dialog spec.record.prompt().set("A Value").ok(); spec.find("a").with("click for prompt").click();
Ruby
#first record how you want to interact with the dialog spec.record.alert.ok #the following will bring up the alert and then the recorded interactions will be played spec.a("click for alert").click #confirm dialog spec.record.confirm.cancel spec.a("click for confirm").click #prompt dialog spec.record.prompt.set("some value").ok spec.a("click for prompt").click
IE Prompt Dialog
Due to limitations that Microsoft put on Javascript prompt interactions, WebSpec must know the location of it’s root directory so it can run a special script to handle Javascript prompt dialogs in Internet Explorer. So you must set the following static method on WebSpec either directly or through a vm arg.
Directly on WebSpec
WebSpec.webspec_home = "C:\\Testing\\WebSpec";
Through Java vm args
-Dwebspec_home="C:\\Testing\\WebSpec"
Windows File Upload Dialog
Currently WebSpec only supports the File Upload Dialog on Windows only for IE an Mozilla browsers. You interact with a file dialog in exactly the same way as other dialogs by first recording your actions and then causing the dialog to appear. Here is a simple example:
Note: For this to work you must set the webspec_home property as described in the previous section.
Java
spec.record.file().set("C:\\something.txt").ok(); //record the file to upload and click ok spec.find.input().with.type("file").click(); //click the upload field to bring up the file dialog
Ruby
spec.record.file.set("C:\\something.txt").ok #record the file to upload and click ok spec.input.type("file").click #click the upload field to bring up the file dialog
Popup Browsers
With the release of the latest WebSpec you can now work with child popup browsers. WebSpec keeps track of all browser windows that are popped up with the javascript window.open(). Here are the main methods on WebSpec for handling multiple browsers:
WebSpec class:
public WebSpec browser(int index) //switches WebSpec's context to the browser at the provided index (zero-based) public int browserCount() //returns the number of open browsers public WebSpec close() //closes the current browser public WebSpec closeAll() //closes all the open browsers
By default WebSpec’s browser context is on the first browser (index 0) – the one brought up with WebSpec.open(url). To change the context, just tell it which browser to switch to using browser(index) and after doing so all method calls will be applied to that browser. Here is an example:
Java
spec.find("a").with("Open Popup Window").click(); //this opens a new browser window spec.browser(1); //switch context to the new browser spec.find("input").with("value", "Close Popup Window").click(); //this will close the popup spec.browser(0); //switch context back to the original browser
Ruby
spec.a("Open Popup Window").click #this opens a new browser window spec.browser 1 #switch context to the new browser spec.input.value('Close Popup Window').click #this will close the popup spec.browser 0 #switch context back to the original browser
Timing Considerations
WebSpec will continue looking for a tag up to a default timeout or to one you set explicitly. You can also ask WebSpec to pause until the browser has gone through an entire cycle of navigation. Here are some examples in Java:
//Overrides the default timeout for finding this tag to be 1000 milliseconds or 1 second. spec.timeout(1000).find("a").with("click me"); //Sit at this line of code until the browser has gone into a busy state (started navigating) and then finally into a ready state (navigation complete) spec.pauseUntilReady();
The following are some additional methods you can use around timing:
WebSpec class:
public WebSpec pause(long milliseconds) //halts the current Thread for the given milliseconds public WebSpec pauseUntilDone() //halts the current Thread until someone manually clicks the Done button on the navigation bar
Image Capture
Java
spec.snap("my_picture.png"); //snaps picture of visible screen spec.snapAll("my_picture.png"); //snaps picture of entire screen including scrollable content
Ruby
spec.snap "my_picture.png" #snaps picture of visible screen spec.snap_all "my_picture.png" #snaps picture of entire screen including scrollable content
Settings
WebSpec provides several settings that you can set globally on its WebSpec class. They are as follows:
public static boolean click_then_pause_until_ready = true; //set this to false if most of your clicks don't result in a browser navigation public static long default_timeout = 10000; //10 seconds public static int window_width = 1200; public static int window_height = 800; public static boolean debug = false; //set to true to see all the debug information in the System output stream public static boolean silent_mode = false; //set to true to hide the browser from displaying when using WebSpec public static boolean extra_careful_pause_until_ready = true; //set this to false to speed up navigation and lessen stability public static boolean show_navigation_bar = false; //set to true to display a navigation bar at the top of the screen, also contains the Done button for interaction with pauseUntilDone public static String file_window_title_for_ie = "Choose File to Upload"; //set this to the title of the IE Window that displays when uploading a file public static String file_window_title_for_mozilla = "File Upload"; //set this to the title of the Mozilla Window that displays when uploading a file public static String prompt_window_title_for_ie = "Explorer User Prompt"; //set this to the title of the IE Window that displays when a javascript prompt is invoked public static String webspec_home = System.getProperty("webspec_home"); //this must be set to the WebSpec root directory in order for file upload and javascript prompts to work in IE //Proxy configuration public static String http_proxy = null; public static int http_proxy_port = 80; public static String http_proxy_username = null; public static String http_proxy_password = null;
Testing and Expectations
WebSpec fits nicely into your favorite testing framework. Just include the WebSpec jars and you are ready to go. As a convenience WebSpec comes with a helper class for JUnit. It is called WebSpecTestCase…just extend this class and all the methods on WebSpec are locally available to your JUnit test.
WebSpec also provides ability to set expectations for a Tag and if the expectation is not met an Exception is thrown. The following methods are available on the Tag class:
public Tag shouldHave(String expectation) //Expects property values, or tag content (if no comparison operator is present) public Tag shouldHave(String property, String value) //Expects property value, if the first and last characters are "/" it does a match otherwise uses an equal comparison public Tag shouldHave(String property, Number value) //Expects property value using an equals comparison public Tag shouldHave(String property, boolean value) //Expects property value using an equals comparison public Tag shouldExist() //Expects tag to exist public Tag shouldNotExist() //Expects tag to not exist
Here are some examples of using Expectations taken from WebSpecTest.
Java
Tag checkbox0 = spec.find("input").with("name=='checkbox0'"); checkbox0.shouldHave("checked==false"); checkbox0.set("checked=true"); checkbox0.shouldHave("checked==true"); //using the matches comparison "/some subtext/" spec.find("input").with("name", "description").shouldHave("value", "/some subtext/");
Same as above but using the Java convenience methods
Tag checkbox0 = find.input().with.name("checkbox0"); checkbox0.shouldHave.checked(false); checkbox0.set.checked(true); checkbox0.shouldHave.checked(true); //using the matches comparison "/some subtext/" spec.find.input().with.name("description").shouldHave.value("/some subtext/")
Ruby
checkbox0 = spec.input.name('checkbox0') checkbox0.should_have.checked==false checkbox0.checked=true checkbox0.should_have.checked==true #using the matches comparison "/some subtext/" spec.input.name("description").should_have.value("/some subtext/")
Cookies
WebSpec provides the ability to find, delete, and create Javascript cookies. For example here is how you would delete all cookies in the browser:
Java
spec.cookie().all.delete();
Ruby
spec.cookie.all.delete
The Cookie class delegates filtering to the With class, and delegates retrieving values to the Get class. Here is an outline of these classes:
Cookie class:
public Cookie at(int index) //returns the cookie at the provided index (zero-based) public Cookie delete() //deletes the first cookie public void create(String url, String name, String value, String domain, String path, long expires, boolean httpOnly, boolean secure, boolean sessionOnly) //sets a new cookie in the browser public With with //returns filtering handler public Get get //returns value handler public All all //returns all handler
Cookie’s With class
public Cookie domain(String domain) //filters the cookies by domain public Cookie name(final String name) //filters the cookies by name public Cookie value(final String value) //filters the cookies by value
Cookie’s Get class
public String name() public String value() public String domain() public String path() public long expires() public boolean httpOnly() public boolean secure() public boolean sessionOnly()
Cookie’s All class:
public Cookie delete() //deletes all the cookies in the browser public int length() //returns the number of cookies in the browser
Interactive Ruby Console
WebSpec makes it easy to use JRuby’s IRB shell for interacting with the browser in real-time.
On Windows
From the windows command prompt go to the root directory of WebSpec and run:
console
On Mac
From the Terminal prompt go to the root directory of WebSpec and run:
./console.sh
Once you run the above command you will get the irb prompt. A var
called @spec will already be available to use. So now you can drive
WebSpec from the irb prompt. Try something like the following,
hitting enter after each line:
@spec.open "http://www.google.com" @spec.input.name("q").value = "Watij" @spec.input.value("Google Search").click @spec.a("Watij").all.flash