All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.scalatest.selenium.WebBrowser.scala Maven / Gradle / Ivy

Go to download

ScalaTest is a free, open-source testing toolkit for Scala and Java programmers.

There is a newer version: 2.0.M6-SNAP4
Show newest version
/*
 * Copyright 2001-2012 Artima, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.scalatest.selenium

import org.openqa.selenium.WebDriver
import org.openqa.selenium.firefox.FirefoxDriver
import org.openqa.selenium.firefox.FirefoxProfile
import org.openqa.selenium.safari.SafariDriver
import org.openqa.selenium.chrome.ChromeDriver
import org.openqa.selenium.ie.InternetExplorerDriver
import org.openqa.selenium.htmlunit.HtmlUnitDriver
import org.openqa.selenium.By
import org.openqa.selenium.WebElement
import java.util.concurrent.TimeUnit
import org.openqa.selenium.support.ui.WebDriverWait
import org.openqa.selenium.support.ui.Clock
import org.openqa.selenium.support.ui.Sleeper
import org.openqa.selenium.support.ui.ExpectedCondition
import scala.collection.mutable.Buffer
import scala.collection.JavaConverters._
import org.openqa.selenium.Cookie
import java.util.Date
import org.scalatest.time.Span
import org.scalatest.time.Milliseconds
import org.openqa.selenium.TakesScreenshot
import org.openqa.selenium.OutputType
import java.io.File
import java.io.FileOutputStream
import java.io.FileInputStream
import org.openqa.selenium.Alert
import org.openqa.selenium.support.ui.Select
import org.scalatest.exceptions.TestFailedException
import org.scalatest.exceptions.StackDepthException
import org.openqa.selenium.JavascriptExecutor
import org.scalatest.ScreenshotCapturer
import org.scalatest.time.Nanosecond
import org.scalatest.Resources

/**
 * Trait that provides a domain specific language (DSL) for writing browser-based tests using Selenium.  
 *
 * To use ScalaTest's Selenium DSL, mix trait WebBrowser into your test class. This trait provides the DSL in its
 * entirety except for one missing piece: an implicit org.openqa.selenium.WebDriver. One way to provide the missing
 * implicit driver is to declare one as a member of your test class, like this:
 * 
 * 
 * import org.scalatest._
 * import selenium._
 *
 * class BlogSpec extends FlatSpec with ShouldMatchers with WebBrowser {
 *
 *   implicit val webDriver: WebDriver = new HtmlUnitDriver
 *
 *   "The blog app home page" should "have the correct title" in {
 *     go to (host + "index.html")
 *     pageTitle should be ("Awesome Blog")
 *   }
 * }
 * 
* *

* For convenience, however, ScalaTest provides a WebBrowser subtrait containing an implicit WebDriver for each * driver provided by Selenium. * Thus a simpler way to use the HtmlUnit driver, for example, is to extend * ScalaTest's HtmlUnit trait, like this: *

* *
 * import org.scalatest._
 * import selenium._
 *
 * class BlogSpec extends FlatSpec with ShouldMatchers with HtmlUnit {
 *
 *   "The blog app home page" should "have the correct title" in {
 *     go to (host + "index.html")
 *     pageTitle should be ("Awesome Blog")
 *   }
 * }
 * 
* *

* The web driver traits provided by ScalaTest are: *

* * * * * * * * * * * * * * * * * * * * * * * *
DriverWebBrowser subtrait
* Google Chrome * * Chrome *
* Mozilla Firefox * * Firefox *
* HtmlUnit * * HtmlUnit *
* Microsoft Internet Explorer * * InternetExplorer *
* Apple Safari * * Safari *
* *

Navigation

* *

* You can ask the browser to retrieve a page (go to a URL) like this: *

* *
 * go to "http://www.artima.com"
 * 
* *

* Note: If you are using the page object pattern, you can also go to a page using the Page instance, as * illustrated in the section on page objects below. *

* *

* Once you have retrieved a page, you can fill in and submit forms, query for the values of page elements, and make assertions. * In the following example, selenium will go to http://www.google.com, fill in the text box with * Cheese!, press the submit button, and wait for result returned from an AJAX call: *

* *
 * go to "http://www.google.com"
 * click on "q"
 * enter("Cheese!")
 * submit()
 * // Google's search is rendered dynamically with JavaScript.
 * eventually { pageTitle should be ("Cheese! - Google Search") }
 * 
* *

* In the above example, the "q" used in “click on "q"” * can be either the id or name of an element. ScalaTest's Selenium DSL will try to lookup by id first. If it cannot find * any element with an id equal to "q", it will then try lookup by name "q". *

* *

* Alternatively, you can be more specific: *

* *
 * click on id("q")   // to lookup by id "q" 
 * click on name("q") // to lookup by name "q" 
 * 
* *

* In addition to id and name, you can use the following approaches to lookup elements, just as you can do with * Selenium's org.openqa.selenium.By class: *

* *
    *
  • xpath
  • *
  • className
  • *
  • cssSelector
  • *
  • linkText
  • *
  • partialLinkText
  • *
  • tagName
  • *
* *

* For example, you can select by link text with: *

* *
 * click on linkText("click here!")
 * 
* *

* If an element is not found via any form of lookup, evaluation will complete abruptly with a TestFailedException. *

* *

Getting and setting input element values

* *

* ScalaTest's Selenium DSL provides a clear, simple syntax for accessing and updating the values of input elements such as * text fields, radio buttons, checkboxes, and selection lists. If a requested element is not found, or if it is found but is * not of the requested type, an exception will immediately result causing the test to fail. *

* *

Text fields, text areas, and password fields

* *

* You can change a text field's value by assigning it via the = operator, like this: *

* *
 * textField("q").value = "Cheese!"
 * 
* *

* And you can access a text field's value by simply invoking value on it: *

* *
 * textField("q").value should be ("Cheese!")
 * 
* *

* If the text field is empty, value will return an empty string (""). *

* *

* You can use the same syntax with text areas and password fields by replacing textField with textArea or pwdField, as in: *

* *
 * textArea("body").value = "I saw something cool today!"
 * textArea("body").value should be ("I saw something cool today!")
 * 
* *

* or with a password field: *

* *
 * pwdField("secret").value = "Don't tell anybody!"
 * pwdField("secret").value should be ("Don't tell anybody!")
 * 
* *

* An alternate way to enter data into a text field, text area, or password field is to use enter or pressKeys. * Although both of these send characters to the active element, pressKeys can be used on any kind of * element, whereas enter can only be used on text fields, text areas, and password fields. Another difference is that enter * will clear the text field or area before sending the characters, effectively replacing any currently existing text with the * new text passed to enter. By contrast, pressKeys does not do any clearing—it just appends * more characters to any existing text. You can backup with pressKeys, however, by sending explicit backspace * characters, "\u0008". *

* *

* To use these commands, you must first click on the text field or area you are interested in * to give it the focus. Here's an example: *

* *
 * click on "q"
 * enter("Cheese!")
 * 
* *

* Here's a (contrived) example of using pressKeys with backspace to fix a typo: *

* *
 * click on "q"              // q is the name or id of a text field or text area
 * enter("Cheesey!")         // Oops, meant to say Cheese!
 * pressKeys("\u0008\u0008") // Send two backspaces; now the value is Cheese
 * pressKeys("!")            // Send the missing exclamation point; now the value is Cheese!
 * 
* *

Radio buttons

* *

* Radio buttons work together in groups. For example, you could have a group of radio buttons, like this: *

* *
 * <input type="radio" id="opt1" name="group1" value="Option 1"> Option 1</input>
 * <input type="radio" id="opt2" name="group1" value="Option 2"> Option 2</input>
 * <input type="radio" id="opt3" name="group1" value="Option 3"> Option 3</input>
 * 
* *

* You can select an option in either of two ways: *

* *
 * radioButtonGroup("group1").value = "Option 2"
 * radioButtonGroup("group1").selection = Some("Option 2")
 * 
* *

* Likewise, you can read the currently selected value of a group of radio buttons in two ways: *

* *
 * radioButtonGroup("group1").value should be ("Option 2")
 * radioButtonGroup("group1").selection should be (Some("Option 2"))
 * 
* *

* If the radio button has no selection at all, selection will return None whereas value * will throw a TestFailedException. By using value, you are indicating you expect a selection, and if there * isn't a selection that should result in a failed test. *

* *

* If you would like to work with RadioButton element directly, you can select it by calling radioButton: *

* *
 * click on radioButton("opt1")
 * 
* *

* you can check if an option is selected by calling isSelected: *

* *
 * radioButton("opt1").isSelected should be (true)
 * 
* *

* to get the value of radio button, you can call value: *

* *
 * radioButton("opt1").value should be ("Option 1")
 * 
* *

Checkboxes

* *

* A checkbox in one of two states: selected or cleared. Here's how you select a checkbox: *

* *
 * checkbox("cbx1").select()
 * 
* *

* And here's how you'd clear one: *

* *
 * checkbox("cbx1").clear()
 * 
* *

* You can access the current state of a checkbox with isSelected: *

* *
 * checkbox("cbx1").isSelected should be (true)
 * 
* *

Single-selection dropdown lists

* *

* Given the following single-selection dropdown list: *

* *
 * <select id="select1">
 *  <option value="option1">Option 1</option>
 *  <option value="option2">Option 2</option>
 *  <option value="option3">Option 3</option>
 * </select>
 * 
* *

* You could select Option 2 in either of two ways: *

* *
 * singleSel("select1").value = "option2"
 * singleSel("select1").selection = Some("option2")
 * 
* *

* To clear the selection, either invoke clear or set selection to None: *

* *
 * singleSel.clear()
 * singleSel("select1").selection = None
 * 
* *

* You can read the currently selected value of a single-selection list in the same manner as radio buttons: *

* *
 * singleSel("select1").value should be ("option2")
 * singleSel("select1").selection should be (Some("option2"))
 * 
* *

* If the single-selection list has no selection at all, selection will return None whereas value * will throw a TestFailedException. By using value, you are indicating you expect a selection, and if there * isn't a selection that should result in a failed test. *

* *

Multiple-selection lists

* *

* Given the following multiple-selection list: *

* *
 * <select name="select2" multiple="multiple">
 *  <option value="option4">Option 4</option>
 *  <option value="option5">Option 5</option>
 *  <option value="option6">Option 6</option>
 * </select>
 * 
* *

* You could select Option 5 and Option 6 like this: *

* *
 * multiSel("select2").values = Seq("option5", "option6")
 * 
* *

* The previous command would essentially clear all selections first, then select Option 5 and Option 6. * If instead you want to not clear any existing selection, just additionally select Option 5 and Option 6, * you can use the += operator, like this. *

* *
 * multiSel("select2").values += "option5"
 * multiSel("select2").values += "option6"
 * 
* *

* To clear a specific option, pass its name to clear: *

* *
 * multiSel("select2").clear("option5")
 * 
* *

* To clear all selections, call clearAll: *

* *
 * multiSel("select2").clearAll()
 * 
* *

* You can access the current selections with values, which returns an immutable IndexedSeq[String]: *

* *
 * multiSel("select2").values should have size 2
 * multiSel("select2").values(0) should be ("option5")
 * multiSel("select2").values(1) should be ("option6")
 * 
* *

Clicking and submitting

* *

* You can click on any element with “click on” as shown previously: *

* *
 * click on "aButton"
 * click on name("aTextField")
 * 
* *

* If the requested element is not found, click on will throw an exception, failing the test. *

* *

* Clicking on a input element will give it the focus. If current focus is in on an input element within a form, you can submit the form by * calling submit: *

* *
 * submit()
 * 
* *

Switching

* *

* You can switch to a popup alert using the following code: *

* *
 * switch to alert
 * 
* *

* to switch to a frame, you could: *

* *
 * switch to frame(0) // switch by index
 * switch to frame("name") // switch by name
 * 
* *

* If you have reference to a window handle (can be obtained from calling windowHandle/windowHandles), you can switch to a particular * window by: *

* *
 * switch to window(windowHandle)
 * 
* *

* You can also switch to active element and default content: *

* *
 * switch to activeElement
 * switch to defaultContent
 * 
* *

Navigation history

* *

* In real web browser, you can press the 'Back' button to go back to previous page. To emulate that action in your test, you can call goBack: *

* *
 * goBack()
 * 
* *

* To emulate the 'Forward' button, you can call: *

* *
 * goForward()
 * 
* * And to refresh or reload the current page, you can call: * *
 * reloadPage()
 * 
* *

Cookies!

* *

To create a new cookie, you'll say:

* *
 * add cookie ("cookie_name", "cookie_value")
 * 
* *

* to read a cookie value, you do: *

* *
 * cookie("cookie_name").value should be ("cookie_value") // If value is undefined, throws TFE right then and there. Never returns null.
 * 
* *

* In addition to the common use of name-value cookie, you can pass these extra fields when creating the cookie, available ways are: *

* *
 * cookie(name: String, value: String)
 * cookie(name: String, value: String, path: String)
 * cookie(name: String, value: String, path: String, expiry: Date)
 * cookie(name: String, value: String, path: String, expiry: Date, domain: String)
 * cookie(name: String, value: String, path: String, expiry: Date, domain: String, secure: Boolean)
 * 
* * and to read those extra fields: * *
 * cookie("cookie_name").value   // Read cookie's value
 * cookie("cookie_name").path    // Read cookie's path
 * cookie("cookie_name").expiry  // Read cookie's expiry
 * cookie("cookie_name").domain  // Read cookie's domain
 * cookie("cookie_name").isSecure  // Read cookie's isSecure flag
 * 
* *

* In order to delete a cookie, you could use the following code: *

* *
 * delete cookie "cookie_name"
 * 
* *

* or to delete all cookies in the same domain:- *

* *
 * delete all cookies
 * 
* * To get the underlying Selenium cookie, you can use underlying: * *
 * cookie("cookie_name").underlying.validate()  // call the validate() method on underlying Selenium cookie
 * 
* *

Other useful element properties

* *

* All element types (textField, textArea, radioButton, checkbox, singleSel, multiSel) * support the following useful properties: *

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
MethodDescription
* location * * The XY location of the top-left corner of this Element. *
* size * * The width/height size of this Element. *
* isDisplayed * * Indicates whether this Element is displayed. *
* isEnabled * * Indicates whether this Element is enabled. *
* isSelected * * Indicates whether this Element is selected. *
* tagName * * The tag name of this element. *
* underlying * * The underlying WebElement wrapped by this Element. *
* attribute(name: String) * * The attribute value of the given attribute name of this element, wrapped in a Some, or None if no * such attribute exists on this Element. *
* text * * Returns the visible (i.e., not hidden by CSS) text of this element, including sub-elements, without any leading or trailing whitespace. *
* *

Implicit wait

* *

* To set Selenium's implicit wait timeout, you can call the implicitlyWait method: *

* *
 * implicitlyWait(Span(10, Seconds))
 * 
* *

* Invoking this method sets the amount of time the driver will wait when searching for an element that is not immediately present. For * more information, see the documentation for method implicitlyWait. *

* *

Page source and current URL

* *

* It is possible to get the html source of currently loaded page, using: *

* *
 * pageSource
 * 
* *

* and if needed, get the current URL of currently loaded page: *

* *
 * currentUrl
 * 
* *

Screen capture

* *

* You can capture screen using the following code: *

* *
 * val file = capture
 * 
* *

* By default, the captured image file will be saved in temporary folder (returned by java.io.tmpdir property), with random file name * ends with .png extension. You can specify a fixed file name: *

* *
 * capture to "MyScreenShot.png"
 * 
* *

* or *

* *
 * capture to "MyScreenShot"
 * 
* *

* Both will result in a same file name MyScreenShot.png. *

* *

* You can also change the target folder screenshot file is written to, by saying: *

* *
 * setCaptureDir("/home/your_name/screenshots")
 * 
* *

* If you want to capture a screenshot when something goes wrong (e.g. test failed), you can use withScreenshot: *

* *
 * withScreenshot {
 *   assert("Gold" == "Silver", "Expected gold, but got silver")
 * }
 * 
* *

* In case the test code fails, you'll see the screenshot location appended to the error message, for example: *

* *
 * Expected gold but got silver; screenshot capture in /tmp/AbCdEfGhIj.png
 * 
* * *

Using the page object pattern

* *

* If you use the page object pattern, mixing trait Page into your page classes will allow you to use the go to * syntax with your page objects. Here's an example: *

* *
 * class HomePage extends Page {
 *   val url = "http://localhost:9000/index.html"
 * }
 *
 * val homePage = new HomePage
 * go to homePage
 * 
* *

Executing JavaScript

* *

* To execute arbitrary JavaScript, for example, to test some JavaScript functions on your page, pass it to executeScript: *

* *
 * go to (host + "index.html")
 * val result1 = executeScript("return document.title;")
 * result1 should be ("Test Title")
 * val result2 = executeScript("return 'Hello ' + arguments[0]", "ScalaTest")
 * result2 should be ("Hello ScalaTest")
 * 
* *

* To execute an asynchronous bit of JavaScript, pass it to executeAsyncScript. You can set the script timeout with setScriptTimeout: *

* *
 * val script = """
 *   var callback = arguments[arguments.length - 1];
 *   window.setTimeout(function() {callback('Hello ScalaTest')}, 500);
 * """
 * setScriptTimeout(1 second)
 * val result = executeAsyncScript(script)
 * result should be ("Hello ScalaTest")
 * 
* *

Querying for elements

* *

* You can query for arbitrary elements via find and findAll. The find method returns the first matching element, wrapped in a Some, * or None if no element is found. The findAll method returns an immutable IndexedSeq of all matching elements. If no elements match the query, findAll * returns an empty IndexedSeq. These methods allow you to perform rich queries using for expressions. Here are some examples: *

* *
 * val ele: Option[Element] = find("q")
 *
 * val eles: colection.immutable.IndexedSeq[Element] = findAll(className("small"))
 * for (e <- eles; if e.tagName != "input")
 *   e should be ('displayed)
 * val textFields = eles filter { tf.isInstanceOf[TextField] }
 * 
* *

Cleaning up

* *

* To close the current browser window, and exit the driver if the current window was the only one remaining, use close: *

* *
 * close()
 * 
* *

* To close all windows, and exit the driver, use quit: *

* *
 * quit()
 * 
* * *

Alternate forms

* *

* Although statements like “delete all cookies” fit well with matcher statements * like “title should be ("Cheese!")”, they do not fit as well * with the simple method call form of assertions. If you prefer, you can avoid operator notation * and instead use alternatives that take the form of plain-old method calls. Here's an example: *

* *
 * goTo("http://www.google.com")
 * clickOn("q")
 * textField("q").value = "Cheese!"
 * submit()
 * // Google's search is rendered dynamically with JavaScript.
 * eventually(assert(pageTitle === "Cheese! - Google Search"))
 * 
* *

* Here's a table showing the complete list of alternatives: *

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
operator notationmethod call
* go to (host + "index.html") * * goTo(host + "index.html") *
* click on "aButton" * * clickOn("aButton") *
* switch to activeElement * * switchTo(activeElement) *
* add cookie ("cookie_name", "cookie_value") * * addCookie("cookie_name", "cookie_value") *
* delete cookie "cookie_name" * * deleteCookie("cookie_name") *
* delete all cookies * * deleteAllCookies() *
* capture to "MyScreenShot" * * captureTo("MyScreenShot") *
* * @author Chua Chee Seng * @author Bill Venners */ trait WebBrowser { /** * A point containing an XY screen location. */ case class Point(x: Int, y: Int) /** * A dimension containing the width and height of a screen element. */ case class Dimension(width: Int, height: Int) /** * Wrapper class for a Selenium WebElement. * *

* This class provides idiomatic Scala access to the services of an underlying WebElement. * You can access the wrapped WebElement via the underlying method. *

*/ sealed trait Element { /** * The XY location of the top-left corner of this Element. * *

* This invokes getLocation on the underlying WebElement. *

* * @return the location of the top-left corner of this element on the page */ def location: Point = Point(underlying.getLocation.getX, underlying.getLocation.getY) /** * The width/height size of this Element. * *

* This invokes getSize on the underlying WebElement. *

* * @return the size of the element on the page */ def size: Dimension = Dimension(underlying.getSize.getWidth, underlying.getSize.getHeight) /** * Indicates whether this Element is displayed. * *

* This invokes isDisplayed on the underlying WebElement. *

* * @return true if the element is currently displayed */ def isDisplayed: Boolean = underlying.isDisplayed /** * Indicates whether this Element is enabled. * *

* This invokes isEnabled on the underlying WebElement, which * will generally return true for everything but disabled input elements. *

* * @return true if the element is currently enabled */ def isEnabled: Boolean = underlying.isEnabled /** * Indicates whether this Element is selected. * *

* This method, which invokes isSelected on the underlying WebElement, * is relevant only for input elements such as checkboxes, options in a single- or multiple-selection * list box, and radio buttons. For any other element it will simply return false. *

* * @return true if the element is currently selected or checked */ def isSelected: Boolean = underlying.isSelected /** * The tag name of this element. * *

* This method invokes getTagName on the underlying WebElement. * Note it returns the name of the tag, not the value of the of the name attribute. * For example, it will return will return "input" for the element * <input name="city" />, not "city". *

* * @return the tag name of this element */ def tagName: String = underlying.getTagName /** * The underlying WebElement wrapped by this Element */ val underlying: WebElement /** * The attribute value of the given attribute name of this element, wrapped in a Some, or None if no * such attribute exists on this Element. * *

* This method invokes getAttribute on the underlying WebElement, passing in the * specified name. *

* * @return the attribute with the given name, wrapped in a Some, else None */ def attribute(name: String): Option[String] = Option(underlying.getAttribute(name)) /** * Returns the visible (i.e., not hidden by CSS) text of this element, including sub-elements, without any leading or trailing whitespace. * * @return the visible text enclosed by this element, or an empty string, if the element encloses no visible text */ def text: String = { val txt = underlying.getText if (txt != null) txt else "" // Just in case, I'm not sure if Selenium would ever return null here } /** * Returns the result of invoking equals on the underlying Element, passing * in the specified other object. * * @param other the object with which to compare for equality * * @return true if the passed object is equal to this one */ override def equals(other: Any): Boolean = underlying.equals(other) /** * Returns the result of invoking hashCode on the underlying Element. * * @return a hash code for this object */ override def hashCode: Int = underlying.hashCode /** * Returns the result of invoking toString on the underlying Element. * * @return a string representation of this object */ override def toString: String = underlying.toString } /** * Trait that facilitates using the page object pattern with the ScalaTest Selenium DSL. * *

* If you use the page object pattern, mixing trait Page into your page classes will allow you to use the go to * syntax with your page objects. Here's an example: *

* *
   * class HomePage extends Page {
   *   val url = "localhost:9000/index.html"
   * }
   *
   * val homePage = new HomePage
   * go to homePage
   * 
*/ trait Page { /** * The URL of the page represented by this page object. */ val url: String } // fluentLinium has a doubleClick. Wonder how they are doing that? /** * Wrapper class for a Selenium Cookie. * *

* This class provides idiomatic Scala access to the services of an underlying Cookie. * You can access the wrapped Cookie via the underlying method. *

*/ final class WrappedCookie(val underlying: Cookie) { /** * The domain to which this cookie is visible. * *

* This invokes getDomain on the underlying Cookie. *

* * @return the domain of this cookie */ def domain: String = underlying.getDomain /** * The expire date of this cookie. * *

* This invokes getExpiry on the underlying Cookie. *

* * @return the expire date of this cookie */ def expiry: Option[Date] = Option(underlying.getExpiry) /** * The name of this cookie. * *

* This invokes getName on the underlying Cookie. *

* * @return the name of this cookie */ def name: String = underlying.getName /** * The path of this cookie. * *

* This invokes getPath on the underlying Cookie. *

* * @return the path of this cookie */ def path: String = underlying.getPath /** * The value of this cookie. * *

* This invokes getValue on the underlying Cookie. *

* * @return the value of this cookie */ def value: String = underlying.getValue /** * Indicates whether the cookie requires a secure connection. * *

* This invokes isSecure on the underlying Cookie. *

* * @return true if this cookie requires a secure connection. */ def secure: Boolean = underlying.isSecure /** * Returns the result of invoking equals on the underlying Cookie, passing * in the specified other object. * *

* Two Selenium Cookies are considered equal if their name and values are equal. *

* * @param other the object with which to compare for equality * * @return true if the passed object is equal to this one */ override def equals(other: Any): Boolean = underlying.equals(other) /** * Returns the result of invoking hashCode on the underlying Cookie. * * @return a hash code for this object */ override def hashCode: Int = underlying.hashCode /** * Returns the result of invoking toString on the underlying Cookie. * * @return a string representation of this object */ override def toString: String = underlying.toString } /** * This class is part of the ScalaTest's Selenium DSL. Please see the documentation for
WebBrowser * for an overview of the Selenium DSL. */ class CookiesNoun /** * This field supports cookie deletion in ScalaTest's Selenium DSL. Please see the documentation for * WebBrowser for an overview of the Selenium DSL. * *

* This field enables the following syntax: *

* *
   * delete all cookies
   *            ^
   * 
*/ val cookies = new CookiesNoun /** * This sealed abstract class supports switching in ScalaTest's Selenium DSL. * Please see the documentation for WebBrowser for an overview of the Selenium DSL. * *

* One subclass of SwitchTarget exists for each kind of target that * can be switched to: active element, alert box, default content, frame (indentified by index, * name or id, or enclosed element), and window. *

*/ sealed abstract class SwitchTarget[T] { /** * Abstract method implemented by subclasses that represent "targets" to which the user can switch. * * @param driver the WebDriver with which to perform the switch */ def switch(driver: WebDriver): T } /** * This class supports switching to the currently active element in ScalaTest's Selenium DSL. * Please see the documentation for WebBrowser for an overview of the Selenium DSL. * *

* This class is enables the following syntax: *

* *
   * switch to activeElement
   *           ^
   * 
*/ final class ActiveElementTarget extends SwitchTarget[Element] { /** * Switches the driver to the currently active element. * * @param driver the WebDriver with which to perform the switch */ def switch(driver: WebDriver): Element = { createTypedElement(driver.switchTo.activeElement) } } /** * This class supports switching to the alert box in ScalaTest's Selenium DSL. * Please see the documentation for WebBrowser for an overview of the Selenium DSL. * *

* This class is enables the following syntax: *

* *
   * switch to alert
   *           ^
   * 
*/ final class AlertTarget extends SwitchTarget[Alert] { /** * Switches the driver to the currently active alert box. * * @param driver the WebDriver with which to perform the switch */ def switch(driver: WebDriver): Alert = { driver.switchTo.alert } } /** * This class supports switching to the default content in ScalaTest's Selenium DSL. * Please see the documentation for WebBrowser for an overview of the Selenium DSL. * *

* This class is enables the following syntax: *

* *
   * switch to defaultContent
   *           ^
   * 
*/ final class DefaultContentTarget extends SwitchTarget[WebDriver] { /** * Switches the driver to the default content * * @param driver the WebDriver with which to perform the switch */ def switch(driver: WebDriver): WebDriver = { driver.switchTo.defaultContent } } /** * This class supports switching to a frame by index in ScalaTest's Selenium DSL. * Please see the documentation for WebBrowser for an overview of the Selenium DSL. * *

* This class is enables the following syntax: *

* *
   * switch to frame(0)
   *           ^
   * 
*/ final class FrameIndexTarget(index: Int) extends SwitchTarget[WebDriver] { /** * Switches the driver to the frame at the index that was passed to the constructor. * * @param driver the WebDriver with which to perform the switch */ def switch(driver: WebDriver): WebDriver = try { driver.switchTo.frame(index) } catch { case e: org.openqa.selenium.NoSuchFrameException => throw new TestFailedException( sde => Some("Frame at index '" + index + "' not found."), None, getStackDepthFun("WebBrowser.scala", "switch", 1) ) } } /** * This class supports switching to a frame by name or ID in ScalaTest's Selenium DSL. * Please see the documentation for WebBrowser for an overview of the Selenium DSL. * *

* This class is enables the following syntax: *

* *
   * switch to frame("name")
   *           ^
   * 
*/ final class FrameNameOrIdTarget(nameOrId: String) extends SwitchTarget[WebDriver] { /** * Switches the driver to the frame with the name or ID that was passed to the constructor. * * @param driver the WebDriver with which to perform the switch */ def switch(driver: WebDriver): WebDriver = try { driver.switchTo.frame(nameOrId) } catch { case e: org.openqa.selenium.NoSuchFrameException => throw new TestFailedException( sde => Some("Frame with name or ID '" + nameOrId + "' not found."), None, getStackDepthFun("WebBrowser.scala", "switch", 1) ) } } /** * This class supports switching to a frame by web element in ScalaTest's Selenium DSL. * Please see the documentation for WebBrowser for an overview of the Selenium DSL. */ final class FrameWebElementTarget(webElement: WebElement) extends SwitchTarget[WebDriver] { /** * Switches the driver to the frame containing the WebElement that was passed to the constructor. * * @param driver the WebDriver with which to perform the switch */ def switch(driver: WebDriver): WebDriver = try { driver.switchTo.frame(webElement) } catch { case e: org.openqa.selenium.NoSuchFrameException => throw new TestFailedException( sde => Some("Frame element '" + webElement + "' not found."), None, getStackDepthFun("WebBrowser.scala", "switch", 1) ) } } /** * This class supports switching to a frame by element in ScalaTest's Selenium DSL. * Please see the documentation for WebBrowser for an overview of the Selenium DSL. */ final class FrameElementTarget(element: Element) extends SwitchTarget[WebDriver] { /** * Switches the driver to the frame containing the Element that was passed to the constructor. * * @param driver the WebDriver with which to perform the switch */ def switch(driver: WebDriver): WebDriver = try { driver.switchTo.frame(element.underlying) } catch { case e: org.openqa.selenium.NoSuchFrameException => throw new TestFailedException( sde => Some("Frame element '" + element + "' not found."), None, getStackDepthFun("WebBrowser.scala", "switch", 1) ) } } /** * This class supports switching to a window by name or handle in ScalaTest's Selenium DSL. * Please see the documentation for WebBrowser for an overview of the Selenium DSL. * *

* This class is enables the following syntax: *

* *
   * switch to window(windowHandle)
   *           ^
   * 
*/ final class WindowTarget(nameOrHandle: String) extends SwitchTarget[WebDriver] { /** * Switches the driver to the window with the name or ID that was passed to the constructor. * * @param driver the WebDriver with which to perform the switch */ def switch(driver: WebDriver): WebDriver = try { driver.switchTo.window(nameOrHandle) } catch { case e: org.openqa.selenium.NoSuchWindowException => throw new TestFailedException( sde => Some("Window with nameOrHandle '" + nameOrHandle + "' not found."), None, getStackDepthFun("WebBrowser.scala", "switch", 1) ) } } private def isTextField(webElement: WebElement): Boolean = webElement.getTagName.toLowerCase == "input" && webElement.getAttribute("type").toLowerCase == "text" private def isPasswordField(webElement: WebElement): Boolean = webElement.getTagName.toLowerCase == "input" && webElement.getAttribute("type").toLowerCase == "password" private def isTextArea(webElement: WebElement): Boolean = webElement.getTagName.toLowerCase == "textarea" private def isCheckBox(webElement: WebElement): Boolean = webElement.getTagName.toLowerCase == "input" && webElement.getAttribute("type").toLowerCase == "checkbox" private def isRadioButton(webElement: WebElement): Boolean = webElement.getTagName == "input" && webElement.getAttribute("type") == "radio" /** * This class is part of ScalaTest's Selenium DSL. Please see the documentation for * WebBrowser for an overview of the Selenium DSL. * *

* This class enables syntax such as the following: *

* *
   * textField("q").value should be ("Cheese!")
   * 
* * @param underlying the WebElement representing a text field * @throws TestFailedExeption if the passed WebElement does not represent a text field */ final class TextField(val underlying: WebElement) extends Element { if(!isTextField(underlying)) throw new TestFailedException( sde => Some("Element " + underlying + " is not text field."), None, getStackDepthFun("WebBrowser.scala", "this", 1) ) /** * Gets this text field's value. * *

* This method invokes getAttribute("value") on the underlying WebElement. *

* * @return the text field's value */ def value: String = underlying.getAttribute("value") /** * Sets this text field's value. * * @param value the new value */ def value_=(value: String) { underlying.clear() underlying.sendKeys(value) } /** * Clears this text field. */ def clear() { underlying.clear() } } /** * This class is part of ScalaTest's Selenium DSL. Please see the documentation for * WebBrowser for an overview of the Selenium DSL. * *

* This class enables syntax such as the following: *

* *
   * textArea("q").value should be ("Cheese!")
   * 
* * @param underlying the WebElement representing a text area * @throws TestFailedExeption if the passed WebElement does not represent a text area */ final class TextArea(val underlying: WebElement) extends Element { if(!isTextArea(underlying)) throw new TestFailedException( sde => Some("Element " + underlying + " is not text area."), None, getStackDepthFun("WebBrowser.scala", "this", 1) ) /** * Gets this text area's value. * *

* This method invokes getAttribute("value") on the underlying WebElement. *

* * @return the text area's value */ def value: String = underlying.getAttribute("value") /** * Sets this text area's value. * * @param value the new value */ def value_=(value: String) { underlying.clear() underlying.sendKeys(value) } /** * Clears this text area. */ def clear() { underlying.clear() } } /** * This class is part of ScalaTest's Selenium DSL. Please see the documentation for * WebBrowser for an overview of the Selenium DSL. * *

* This class enables syntax such as the following: *

* *
   * pwdField("q").value should be ("Cheese!")
   * 
* * @param underlying the WebElement representing a password field * @throws TestFailedExeption if the passed WebElement does not represent a password field */ final class PasswordField(val underlying: WebElement) extends Element { if(!isPasswordField(underlying)) throw new TestFailedException( sde => Some("Element " + underlying + " is not password field."), None, getStackDepthFun("WebBrowser.scala", "this", 1) ) /** * Gets this password field's value. * *

* This method invokes getAttribute("value") on the underlying WebElement. *

* * @return the password field's value */ def value: String = underlying.getAttribute("value") /** * Sets this password field's value. * * @param value the new value */ def value_=(value: String) { underlying.clear() underlying.sendKeys(value) } /** * Clears this text field. */ def clear() { underlying.clear() } } /** * This class is part of ScalaTest's Selenium DSL. Please see the documentation for * WebBrowser for an overview of the Selenium DSL. * *

* This class enables syntax such as the following: *

* *
   * radioButton(id("opt1")).value should be ("Option 1!")
   * 
* * @param underlying the WebElement representing a text area * @throws TestFailedExeption if the passed WebElement does not represent a text area */ final class RadioButton(val underlying: WebElement) extends Element { if(!isRadioButton(underlying)) throw new TestFailedException( sde => Some("Element " + underlying + " is not radio button."), None, getStackDepthFun("WebBrowser.scala", "this", 1) ) /** * Gets this radio button's value. * *

* Invokes getAttribute("value") on the underlying WebElement. *

* * @return the radio button's value */ def value: String = underlying.getAttribute("value") } /** * This class is part of ScalaTest's Selenium DSL. Please see the documentation for * WebBrowser for an overview of the Selenium DSL. * *

* This class enables syntax such as the following: *

* *
   * radioButtonGroup("group1").value should be ("Option 2")
   * 
* * @throws TestFailedExeption if no radio button with the passed groupName are found */ final class RadioButtonGroup(groupName: String, driver: WebDriver) { private def groupElements = driver.findElements(By.name(groupName)).asScala.toList.filter(isRadioButton(_)) if (groupElements.length == 0) throw new TestFailedException( sde => Some("No radio buttons with group name '" + groupName + "' was found."), None, getStackDepthFun("WebBrowser.scala", "this", 1) ) /** * Returns the value of this group's selected radio button, or throws TestFailedException if no * radio button in this group is selected. * * @return the value of this group's selected radio button * @throws TestFailedExeption if no radio button in this group is selected */ def value: String = selection match { case Some(v) => v case None => throw new TestFailedException( sde => Some("The radio button group on which value was invoked contained no selected radio button."), None, getStackDepthFun("WebBrowser.scala", "value", 1) ) } /** * Returns the value of this group's selected radio button, wrapped in a Some, or None, if no * radio button in this group is selected. * * @return the value of this group's selected radio button, wrapped in a Some, else None */ def selection: Option[String] = { groupElements.find(_.isSelected) match { case Some(radio) => Some(radio.getAttribute("value")) case None => None } } /** * Selects the radio button with the passed value. * * @param the value of the radio button to select * @throws TestFailedExeption if the passed string is not the value of any radio button in this group */ def value_=(value: String) { groupElements.find(_.getAttribute("value") == value) match { case Some(radio) => radio.click() case None => throw new TestFailedException( sde => Some("Radio button value '" + value + "' not found for group '" + groupName + "'."), None, getStackDepthFun("WebBrowser.scala", "value_=", 1) ) } } } /** * This class is part of ScalaTest's Selenium DSL. Please see the documentation for * WebBrowser for an overview of the Selenium DSL. * *

* This class enables syntax such as the following: *

* *
   * checkbox("cbx1").select()
   * 
* * @param underlying the WebElement representing a checkbox * @throws TestFailedExeption if the passed WebElement does not represent a checkbox */ final class Checkbox(val underlying: WebElement) extends Element { if(!isCheckBox(underlying)) throw new TestFailedException( sde => Some("Element " + underlying + " is not check box."), None, getStackDepthFun("WebBrowser.scala", "this", 1) ) /** * Selects this checkbox. */ def select() { if (!underlying.isSelected) underlying.click() } /** * Clears this checkbox */ def clear() { if (underlying.isSelected()) underlying.click() } /** * Gets this checkbox's value. * *

* This method invokes getAttribute("value") on the underlying WebElement. *

* * @return the checkbox's value */ def value: String = underlying.getAttribute("value") } /** * This class is part of ScalaTest's Selenium DSL. Please see the documentation for * WebBrowser for an overview of the Selenium DSL. * *

* This class enables syntax such as the following: *

* *
   * multiSel("select2").values += "option5"
   *                            ^
   * 
* *

* Instances of this class are returned from the values method of MultiSel. * MultiSelOptionSeq is an immutable IndexedSeq[String] that wraps an underlying immutable IndexedSeq[String] and adds two * methods, + and -, to facilitate the += syntax for setting additional options * of the MultiSel. The Scala compiler will rewrite: *

* *
   * multiSel("select2").values += "option5"
   * 
* *

* To: *

* *
   * multiSel("select2").values = multiSel("select2").values + "option5"
   * 
* *

* Thus, first a new MultiSelOptionSeq is created by invoking the + method on the MultiSelOptionSeq * returned by values, and that result is passed to the values_= method. *

* *

* For symmetry, this class also offers a - method, which can be used to deselect an option, like this: *

* *
   * multiSel("select2").values -= "option5"
   *                            ^
   * 
* */ class MultiSelOptionSeq(underlying: collection.immutable.IndexedSeq[String]) extends collection.immutable.IndexedSeq[String] { /** * Selects an element by its index in the sequence. * *

* This method invokes apply on the underlying immutable IndexedSeq[String], passing in idx, and returns the result. *

* * @param idx the index to select * @return the element of this sequence at index idx, where 0 indicates the first element */ def apply(idx: Int): String = underlying.apply(idx) /** * The length of this sequence. * *

* This method invokes length on the underlying immutable IndexedSeq[String] and returns the result. *

* * @return the number of elements in this sequence */ def length: Int = underlying.length /** * Appends a string element to this sequence, if it doesn't already exist in the sequence. * *

* If the string element already exists in this sequence, this method returns itself. If not, * this method returns a new MultiSelOptionSeq with the passed value appended to the * end of the original MultiSelOptionSeq. *

* * @param the string element to append to this sequence * @return a MultiSelOptionSeq that contains the passed string value */ def +(value: String): MultiSelOptionSeq = { if (!underlying.contains(value)) new MultiSelOptionSeq(underlying :+ value) else this } /** * Removes a string element to this sequence, if it already exists in the sequence. * *

* If the string element does not already exist in this sequence, this method returns itself. If the element * is contained in this sequence, this method returns a new MultiSelOptionSeq with the passed value * removed from the the original MultiSelOptionSeq, leaving any other elements in the same order. *

* * @param the string element to append to this sequence * @return a MultiSelOptionSeq that contains the passed string value */ def -(value: String): MultiSelOptionSeq = { if (underlying.contains(value)) new MultiSelOptionSeq(underlying.filter(_ != value)) else this } } /** * This class is part of ScalaTest's Selenium DSL. Please see the documentation for * WebBrowser for an overview of the Selenium DSL. * *

* This class enables syntax such as the following: *

* *
   * singleSel.clear()
   * 
* * @param underlying a WebElement representing a single selection list * @throws TestFailedExeption if the passed WebElement does not represent a single selection list */ class SingleSel(val underlying: WebElement) extends Element { if(underlying.getTagName.toLowerCase != "select") throw new TestFailedException( sde => Some("Element " + underlying + " is not select."), None, getStackDepthFun("WebBrowser.scala", "this", 1) ) private val select = new Select(underlying) if (select.isMultiple) throw new TestFailedException( sde => Some("Element " + underlying + " is not a single-selection list."), None, getStackDepthFun("WebBrowser.scala", "this", 1) ) /** * Returns the value of this single selection list, wrapped in a Some, or None, if this single * selection list has no currently selected value. * * @return the value of this single selection list, wrapped in a Some, else None */ def selection = { val first = select.getFirstSelectedOption if (first == null) None else Some(first.getAttribute("value")) } /** * Gets this single selection list's selected value, or throws TestFailedException if no value is currently selected. * * @return the single selection list's value * @throws TestFailedException if the single selection list has no selected value */ def value: String = selection match { case Some(v) => v case None => throw new TestFailedException( sde => Some("The single selection list on which value was invoked had no selection."), None, getStackDepthFun("WebBrowser.scala", "value", 1) ) } /** * Sets this single selection list's value to the passed value. * * @param value the new value * @throws TestFailedException if the passed value does not match not one of the single selection list's values */ def value_=(value : String) { try { select.selectByValue(value) } catch { case e: org.openqa.selenium.NoSuchElementException => throw new TestFailedException( sde => Some(e.getMessage), Some(e), getStackDepthFun("WebBrowser.scala", "value_=", 1) ) } } } /** * This class is part of ScalaTest's Selenium DSL. Please see the documentation for * WebBrowser for an overview of the Selenium DSL. * *

* This class enables syntax such as the following: *

* *
   * multiSel("select2").clear("option5")
   * 
* * @param underlying a WebElement representing a multiple selection list * @throws TestFailedExeption if the passed WebElement does not represent a multiple selection list */ class MultiSel(val underlying: WebElement) extends Element { if(underlying.getTagName.toLowerCase != "select") throw new TestFailedException( sde => Some("Element " + underlying + " is not select."), None, getStackDepthFun("WebBrowser.scala", "this", 1) ) private val select = new Select(underlying) if (!select.isMultiple) throw new TestFailedException( sde => Some("Element " + underlying + " is not a multi-selection list."), None, getStackDepthFun("WebBrowser.scala", "this", 1) ) /** * Clears the passed value in this multiple selection list. * * @param value the value to clear */ def clear(value: String) { select.deselectByValue(value) } /** * Gets all selected values of this multiple selection list. * *

* If the multiple selection list has no selections, ths method will * return an empty IndexedSeq. *

* * @return An IndexedSeq containing the currently selected values */ def values: MultiSelOptionSeq = { val elementSeq = Vector.empty ++ select.getAllSelectedOptions.asScala new MultiSelOptionSeq(elementSeq.map(_.getAttribute("value"))) } /** * Clears any existing selections then sets all values contained in the passed collection.Seq[String]. * *

* In other words, the values_= method replaces the current selections, if any, with * new selections defined by the passed Seq[String]. *

* * @param values a Seq of string values to select * @throws TestFailedException if a value contained in the passed Seq[String] is not * among this multiple selection list's values. */ def values_=(values: collection.Seq[String]) { try { clearAll() values.foreach(select.selectByValue(_)) } catch { case e: org.openqa.selenium.NoSuchElementException => throw new TestFailedException( sde => Some(e.getMessage), Some(e), getStackDepthFun("WebBrowser.scala", "value_=", 1) ) } } /** * Clears all selected values in this multiple selection list. * * @param value the value to clear */ def clearAll() { select.deselectAll() } } /** * This object is part of ScalaTest's Selenium DSL. Please see the documentation for * WebBrowser for an overview of the Selenium DSL. * *

* This object enables syntax such as the following: *

* *
   * go to "http://www.artima.com"
   * ^
   * 
*/ object go { /** * Sends the browser to the passed URL. * *

* This method enables syntax such as the following: *

* *
     * go to "http://www.artima.com"
     *    ^
     * 
* * @param url the URL to which to send the browser * @param driver the WebDriver with which to drive the browser */ def to(url: String)(implicit driver: WebDriver) { driver.get(url) } /** * Sends the browser to the URL contained in the passed Page object. * *

* This method enables syntax such as the following: *

* *
     * go to homePage
     *    ^
     * 
* * @param page the Page object containing the URL to which to send the browser * @param driver the WebDriver with which to drive the browser */ def to(page: Page)(implicit driver: WebDriver) { driver.get(page.url) } } /** * Sends the browser to the passed URL. * *

* Here's an example: *

* *
   * goTo("http://www.artima.com")
   * 
* * @param url the URL to which to send the browser * @param driver the WebDriver with which to drive the browser */ def goTo(url: String)(implicit driver: WebDriver) { go to url } /** * Sends the browser to the URL contained in the passed Page object. * *

* Here's an example: *

* *
   * goTo(homePage)
   * 
* * @param page the Page object containing the URL to which to send the browser * @param driver the WebDriver with which to drive the browser */ def goTo(page: Page)(implicit driver: WebDriver) { go to page } /** * Closes the current browser window, and exits the driver if the current window was the only one remaining. * * @param driver the WebDriver with which to drive the browser */ def close()(implicit driver: WebDriver) { driver.close() } /** * Returns the title of the current page, or the empty string if the current page has no title. * * @param driver the WebDriver with which to drive the browser * @return the current page's title, or the empty string if the current page has no title */ def pageTitle(implicit driver: WebDriver): String = { val t = driver.getTitle if (t != null) t else "" } /** * Returns the source of the current page. * *

* This method invokes getPageSource on the passed WebDriver and returns the result. *

* * @param driver the WebDriver with which to drive the browser * @return the source of the current page */ def pageSource(implicit driver: WebDriver): String = driver.getPageSource /** * Returns the URL of the current page. * *

* This method invokes getCurrentUrl on the passed WebDriver and returns the result. *

* * @param driver the WebDriver with which to drive the browser * @return the URL of the current page */ def currentUrl(implicit driver: WebDriver): String = driver.getCurrentUrl /** * This trait is part of ScalaTest's Selenium DSL. Please see the documentation for * WebBrowser for an overview of the Selenium DSL. * *

* Subclasses of this trait define different ways of querying for elements, enabling * syntax such as the following: *

* *
   * click on id("q")
   *          ^
   * 
*/ sealed trait Query { /** * The Selenium By for this query. */ val by: By /** * The query string for this query. * *

* For example, the query string for id("q") is "q". *

*/ val queryString: String /** * Returns the first Element selected by this query, or throws TestFailedException * if no Element is selected. * *

* The class of the Element returned will be a subtype of Element if appropriate. * For example, if this query selects a text field, the class of the returned Element will * be TextField. *

* * @param driver the WebDriver with which to drive the browser * @return the Element selected by this query * @throws TestFailedException if nothing is selected by this query */ def element(implicit driver: WebDriver): Element = { try { createTypedElement(driver.findElement(by)) } catch { case e: org.openqa.selenium.NoSuchElementException => throw new TestFailedException( sde => Some("Element '" + queryString + "' not found."), Some(e), getStackDepthFun("WebBrowser.scala", "name", 1) ) } } /** * Returns the first Element selected by this query, wrapped in a Some, or None * if no Element is selected. * *

* The class of the Element returned will be a subtype of Element if appropriate. * For example, if this query selects a text field, the class of the returned Element will * be TextField. *

* * @param driver the WebDriver with which to drive the browser * @return the Element selected by this query, wrapped in a Some, or None if * no Element is selected */ def findElement(implicit driver: WebDriver): Option[Element] = try { Some(createTypedElement(driver.findElement(by))) } catch { case e: org.openqa.selenium.NoSuchElementException => None } /** * Returns an Iterator over all Elements selected by this query. * *

* The class of the Elements produced by the returned Iterator will be a * subtypes of Element if appropriate. For example, if an Elementrepresenting * a text field is returned by the Iterator, the class of the returned Element will * be TextField. *

* *

* If no Elements are selected by this query, this method will return an empty Iterator will be returned. *

* * @param driver the WebDriver with which to drive the browser * @return the Iterator over all Elements selected by this query */ def findAllElements(implicit driver: WebDriver): Iterator[Element] = driver.findElements(by).asScala.toIterator.map { e => createTypedElement(e) } /** * Returns the first WebElement selected by this query, or throws TestFailedException * if no WebElement is selected. * * @param driver the WebDriver with which to drive the browser * @return the WebElement selected by this query * @throws TestFailedException if nothing is selected by this query */ def webElement(implicit driver: WebDriver): WebElement = { try { driver.findElement(by) } catch { case e: org.openqa.selenium.NoSuchElementException => throw new TestFailedException( sde => Some("WebElement '" + queryString + "' not found."), Some(e), getStackDepthFun("WebBrowser.scala", "name", 1) ) } } } /** * An ID query. * *

* This class enables syntax such as the following: *

* *
   * click on id("q")
   *          ^
   * 
* * @param queryString the query string for this query. */ case class IdQuery(queryString: String) extends Query { val by = By.id(queryString)} /** * A name query. * *

* This class enables syntax such as the following: *

* *
   * click on name("q")
   *          ^
   * 
* * @param queryString the query string for this query. */ case class NameQuery(queryString: String) extends Query { val by = By.name(queryString) } /** * An XPath query. * *

* This class enables syntax such as the following: *

* *
   * click on xpath("???")
   *          ^
   * 
* * @param queryString the query string for this query. */ case class XPathQuery(queryString: String) extends Query { val by = By.xpath(queryString) } // TODO: Are these case classes just to get at the val? /** * A class name query. * *

* This class enables syntax such as the following: *

* *
   * click on className("???")
   *          ^
   * 
* * @param queryString the query string for this query. */ case class ClassNameQuery(queryString: String) extends Query { val by = By.className(queryString) } /** * A CSS selector query. * *

* This class enables syntax such as the following: *

* *
   * click on cssSelector("???")
   *          ^
   * 
* * @param queryString the query string for this query. */ case class CssSelectorQuery(queryString: String) extends Query { val by = By.cssSelector(queryString) } /** * A link text query. * *

* This class enables syntax such as the following: *

* *
   * click on linkText("???")
   *          ^
   * 
* * @param queryString the query string for this query. */ case class LinkTextQuery(queryString: String) extends Query { val by = By.linkText(queryString) } /** * A partial link text query. * *

* This class enables syntax such as the following: *

* *
   * click on partialLinkText("???")
   *          ^
   * 
* * @param queryString the query string for this query. */ case class PartialLinkTextQuery(queryString: String) extends Query { val by = By.partialLinkText(queryString) } /** * A tag name query. * *

* This class enables syntax such as the following: *

* *
   * click on tagName("???")
   *          ^
   * 
* * @param queryString the query string for this query. */ case class TagNameQuery(queryString: String) extends Query { val by = By.tagName(queryString) } /** * Returns an ID query. * *

* This method enables syntax such as the following: *

* *
   * click on id("q")
   *          ^
   * 
* * @param queryString the query string for this query. */ def id(elementId: String): IdQuery = new IdQuery(elementId) /** * Returns a name query. * *

* This method enables syntax such as the following: *

* *
   * click on name("q")
   *          ^
   * 
* * @param queryString the query string for this query. */ def name(elementName: String): NameQuery = new NameQuery(elementName) /** * Returns an XPath query. * *

* This method enables syntax such as the following: *

* *
   * click on xpath("???")
   *          ^
   * 
* * @param queryString the query string for this query. */ def xpath(xpath: String): XPathQuery = new XPathQuery(xpath) /** * Returns a class name query. * *

* This method enables syntax such as the following: *

* *
   * click on className("???")
   *          ^
   * 
* * @param queryString the query string for this query. */ def className(className: String): ClassNameQuery = new ClassNameQuery(className) /** * Returns a CSS selector query. * *

* This method enables syntax such as the following: *

* *
   * click on cssSelector("???")
   *          ^
   * 
* * @param queryString the query string for this query. */ def cssSelector(cssSelector: String): CssSelectorQuery = new CssSelectorQuery(cssSelector) /** * Returns a link text query. * *

* This method enables syntax such as the following: *

* *
   * click on linkText("???")
   *          ^
   * 
* * @param queryString the query string for this query. */ def linkText(linkText: String): LinkTextQuery = new LinkTextQuery(linkText) /** * Returns a partial link text query. * *

* This method enables syntax such as the following: *

* *
   * click on partialLinkText("???")
   *          ^
   * 
* * @param queryString the query string for this query. */ def partialLinkText(partialLinkText: String): PartialLinkTextQuery = new PartialLinkTextQuery(partialLinkText) /** * Returns a tag name query. * *

* This method enables syntax such as the following: *

* *
   * click on tagName("???")
   *          ^
   * 
* * @param queryString the query string for this query. */ def tagName(tagName: String): TagNameQuery = new TagNameQuery(tagName) private def createTypedElement(element: WebElement): Element = { if (isTextField(element)) new TextField(element) else if (isTextArea(element)) new TextArea(element) else if (isPasswordField(element)) new PasswordField(element) else if (isCheckBox(element)) new Checkbox(element) else if (isRadioButton(element)) new RadioButton(element) else if (element.getTagName.toLowerCase == "select") { val select = new Select(element) if (select.isMultiple) new MultiSel(element) else new SingleSel(element) } else new Element() { val underlying = element } } // XXX /** * Finds and returns the first element selected by the specified Query, wrapped * in a Some, or None if no element is selected. * *

* The class of the Element returned will be a subtype of Element if appropriate. * For example, if the query selects a text field, the class of the returned Element will * be TextField. *

* * @param query the Query with which to search * @param driver the WebDriver with which to drive the browser * @return the Element selected by this query, wrapped in a Some, or None if * no Element is selected */ def find(query: Query)(implicit driver: WebDriver): Option[Element] = query.findElement /** * Finds and returns the first element selected by the specified string ID or name, wrapped * in a Some, or None if no element is selected. YYY * *

* This method will try to lookup by id first. If it cannot find * any element with an id equal to the specified queryString, it will then try lookup by name. *

* *

* The class of the Element returned will be a subtype of Element if appropriate. * For example, if the query selects a text field, the class of the returned Element will * be TextField. *

* * @param queryString the string with which to search, first by ID then by name * @param driver the WebDriver with which to drive the browser * @return the Element selected by this query, wrapped in a Some, or None if * no Element is selected */ def find(queryString: String)(implicit driver: WebDriver): Option[Element] = new IdQuery(queryString).findElement match { case Some(element) => Some(element) case None => new NameQuery(queryString).findElement match { case Some(element) => Some(element) case None => None } } /** * Returns an Iterator over all Elements selected by this query. * *

* The class of the Elements produced by the returned Iterator will be a * subtypes of Element if appropriate. For example, if an Elementrepresenting * a text field is returned by the Iterator, the class of the returned Element will * be TextField. *

* *

* If no Elements are selected by this query, this method will return an empty Iterator will be returned. *

* * @param query the Query with which to search * @param driver the WebDriver with which to drive the browser * @return the Iterator over all Elements selected by this query */ def findAll(query: Query)(implicit driver: WebDriver): Iterator[Element] = query.findAllElements /** * Returns an Iterator over all Elements selected by the specified string ID or name * *

* This method will try to lookup by id first. If it cannot find * any element with an id equal to the specified queryString, it will then try lookup by name. *

* *

* The class of the Element returned will be a subtype of Element if appropriate. * For example, if the query selects a text field, the class of the returned Element will * be TextField. *

* * @param queryString the string with which to search, first by ID then by name * @param driver the WebDriver with which to drive the browser * @return the Iterator over all Elements selected by this query */ def findAll(queryString: String)(implicit driver: WebDriver): Iterator[Element] = { val byIdItr = new IdQuery(queryString).findAllElements if (byIdItr.hasNext) byIdItr else new NameQuery(queryString).findAllElements } private def tryQueries[T](queryString: String)(f: Query => T)(implicit driver: WebDriver): T = { try { f(IdQuery(queryString)) } catch { case _: Throwable => f(NameQuery(queryString)) } } /** * Finds and returns the first TextField selected by the specified Query, throws TestFailedException * if element not found or the found element is not a TextField. * * @param query the Query with which to search * @param driver the WebDriver with which to drive the browser * @throws TestFailedException if element not found or found element is not a TextField * @return the TextField selected by this query */ def textField(query: Query)(implicit driver: WebDriver): TextField = new TextField(query.webElement) /** * Finds and returns the first TextField selected by the specified string ID or name, throws TestFailedException * if element not found or the found element is not a TextField. * * @param queryString the string with which to search, first by ID then by name * @param driver the WebDriver with which to drive the browser * @throws TestFailedException if element not found or found element is not a TextField * @return the TextField selected by this query */ def textField(queryString: String)(implicit driver: WebDriver): TextField = tryQueries(queryString)(q => new TextField(q.webElement)) /** * Finds and returns the first TextArea selected by the specified Query, throws TestFailedException * if element not found or the found element is not a TextArea. * * @param query the Query with which to search * @param driver the WebDriver with which to drive the browser * @throws TestFailedException if element not found or found element is not a TextArea * @return the TextArea selected by this query */ def textArea(query: Query)(implicit driver: WebDriver) = new TextArea(query.webElement) /** * Finds and returns the first TextArea selected by the specified string ID or name, throws TestFailedException * if element not found or the found element is not a TextArea. * * @param queryString the string with which to search, first by ID then by name * @param driver the WebDriver with which to drive the browser * @throws TestFailedException if element not found or found element is not a TextArea * @return the TextArea selected by this query */ def textArea(queryString: String)(implicit driver: WebDriver): TextArea = tryQueries(queryString)(q => new TextArea(q.webElement)) /** * Finds and returns the first PasswordField selected by the specified Query, throws TestFailedException * if element not found or the found element is not a PasswordField. * * @param query the Query with which to search * @param driver the WebDriver with which to drive the browser * @throws TestFailedException if element not found or found element is not a PasswordField * @return the PasswordField selected by this query */ def pwdField(query: Query)(implicit driver: WebDriver): PasswordField = new PasswordField(query.webElement) /** * Finds and returns the first PasswordField selected by the specified string ID or name, throws TestFailedException * if element not found or the found element is not a PasswordField. * * @param queryString the string with which to search, first by ID then by name * @param driver the WebDriver with which to drive the browser * @throws TestFailedException if element not found or found element is not a PasswordField * @return the PasswordField selected by this query */ def pwdField(queryString: String)(implicit driver: WebDriver): PasswordField = tryQueries(queryString)(q => new PasswordField(q.webElement)) /** * Finds and returns RadioButtonGroup selected by the specified group name, throws TestFailedException if * no element with the specified group name is found, or found any element with the specified group name but not a RadioButton * * @param groupName the group name with which to search * @param driver the WebDriver with which to drive the browser * @throws TestFailedException if no element with the specified group name is found, or found any element with the specified group name but not a RadioButton * @return the RadioButtonGroup selected by this query */ def radioButtonGroup(groupName: String)(implicit driver: WebDriver) = new RadioButtonGroup(groupName, driver) /** * Finds and returns the first RadioButton selected by the specified Query, throws TestFailedException * if element not found or the found element is not a RadioButton. * * @param query the Query with which to search * @param driver the WebDriver with which to drive the browser * @throws TestFailedException if element not found or found element is not a RadioButton * @return the RadioButton selected by this query */ def radioButton(query: Query)(implicit driver: WebDriver) = new RadioButton(query.webElement) /** * Finds and returns the first RadioButton selected by the specified string ID or name, throws TestFailedException * if element not found or the found element is not a RadioButton. * * @param queryString the string with which to search, first by ID then by name * @param driver the WebDriver with which to drive the browser * @throws TestFailedException if element not found or found element is not a RadioButton * @return the RadioButton selected by this query */ def radioButton(queryString: String)(implicit driver: WebDriver): RadioButton = tryQueries(queryString)(q => new RadioButton(q.webElement)) /** * Finds and returns the first Checkbox selected by the specified Query, throws TestFailedException * if element not found or the found element is not a Checkbox. * * @param query the Query with which to search * @param driver the WebDriver with which to drive the browser * @throws TestFailedException if element not found or found element is not a Checkbox * @return the Checkbox selected by this query */ def checkbox(query: Query)(implicit driver: WebDriver) = new Checkbox(query.webElement) /** * Finds and returns the first Checkbox selected by the specified string ID or name, throws TestFailedException * if element not found or the found element is not a Checkbox. * * @param queryString the string with which to search, first by ID then by name * @param driver the WebDriver with which to drive the browser * @throws TestFailedException if element not found or found element is not a Checkbox * @return the Checkbox selected by this query */ def checkbox(queryString: String)(implicit driver: WebDriver): Checkbox = tryQueries(queryString)(q => new Checkbox(q.webElement)) /** * Finds and returns the first SingleSel selected by the specified Query, throws TestFailedException * if element not found or the found element is not a SingleSel. * * @param query the Query with which to search * @param driver the WebDriver with which to drive the browser * @throws TestFailedException if element not found or found element is not a SingleSel * @return the SingleSel selected by this query */ def singleSel(query: Query)(implicit driver: WebDriver) = new SingleSel(query.webElement) /** * Finds and returns the first SingleSel selected by the specified string ID or name, throws TestFailedException * if element not found or the found element is not a SingleSel. * * @param queryString the string with which to search, first by ID then by name * @param driver the WebDriver with which to drive the browser * @throws TestFailedException if element not found or found element is not a SingleSel * @return the SingleSel selected by this query */ def singleSel(queryString: String)(implicit driver: WebDriver): SingleSel = tryQueries(queryString)(q => new SingleSel(q.webElement)) /** * Finds and returns the first MultiSel selected by the specified Query, throws TestFailedException * if element not found or the found element is not a MultiSel. * * @param query the Query with which to search * @param driver the WebDriver with which to drive the browser * @throws TestFailedException if element not found or found element is not a MultiSel * @return the MultiSel selected by this query */ def multiSel(query: Query)(implicit driver: WebDriver) = new MultiSel(query.webElement) /** * Finds and returns the first MultiSel selected by the specified string ID or name, throws TestFailedException * if element not found or the found element is not a MultiSel. * * @param queryString the string with which to search, first by ID then by name * @param driver the WebDriver with which to drive the browser * @throws TestFailedException if element not found or found element is not a MultiSel * @return the MultiSel selected by this query */ def multiSel(queryString: String)(implicit driver: WebDriver): MultiSel = tryQueries(queryString)(q => new MultiSel(q.webElement)) /** * This object is part of ScalaTest's Selenium DSL. Please see the documentation for * WebBrowser for an overview of the Selenium DSL. * *

* This object enables syntax such as the following: *

* *
   * click on "aButton"
   * ^
   * 
*/ object click { /** * Click on the specified WebElement * * @param element the WebElement to click on */ def on(element: WebElement) { element.click() } /** * Click on the first Element selected by the specified Query * * @param query the Query with which to search * @param driver the WebDriver with which to drive the browser */ def on(query: Query)(implicit driver: WebDriver) { query.webElement.click() } /** * Click on the first Element selected by the specified string ID or name * * @param queryString the string with which to search, first by ID then by name * @param driver the WebDriver with which to drive the browser */ def on(queryString: String)(implicit driver: WebDriver) { // stack depth is not correct if just call the button("...") directly. val target = tryQueries(queryString)(q => q.webElement) on(target) } /** * Click on the specified Element * * @param element the Element to click on */ def on(element: Element) { element.underlying.click() } } /** * Click on the specified WebElement * * @param element the WebElement to click on */ def clickOn(element: WebElement) { click on element } /** * Click on the first Element selected by the specified Query * * @param query the Query with which to search * @param driver the WebDriver with which to drive the browser */ def clickOn(query: Query)(implicit driver: WebDriver) { click on query } /** * Click on the first Element selected by the specified string ID or name * * @param queryString the string with which to search, first by ID then by name * @param driver the WebDriver with which to drive the browser */ def clickOn(queryString: String)(implicit driver: WebDriver) { click on queryString } /** * Click on the specified Element * * @param element the Element to click on */ def clickOn(element: Element) { click on element } /** * Submit the form where current active element belongs to, and throws TestFailedException if current active element is not * in a form or underlying WebDriver encounters problem when submitting the form. If this causes the current page to change, * this call will block until the new page is loaded. * * @param driver the WebDriver with which to drive the browser * @throws TestFailedException if current active element is not in a form or underlying WebDriver encounters problem when submitting the form. */ def submit()(implicit driver: WebDriver) { try { (switch to activeElement).underlying.submit() } catch { case e: org.openqa.selenium.NoSuchElementException => throw new TestFailedException( sde => Some("Current element is not a form element."), Some(e), getStackDepthFun("WebBrowser.scala", "name", 1) ) case e: Throwable => // Could happens as bug in different WebDriver, like NullPointerException in HtmlUnitDriver when element is not a form element. // Anyway, we'll just wrap them as TestFailedException throw new TestFailedException( sde => Some("WebDriver encountered problem to submit(): " + e.getMessage), Some(e), getStackDepthFun("WebBrowser.scala", "submit", 0) ) } } /** * Sets the amount of time the driver should wait when searching for an element that is not immediately present. * *

* When searching for requested elements, Selenium will poll the page until the requested element (or at least one of multiple requested * elements) is found or this "implicit wait" timeout has expired. * If the timeout expires, Selenium will throw NoSuchElementException, which ScalaTest's Selenium DSL will wrap in a TestFailedException. *

* *

* You can alternatively set this timeout to zero and use ScalaTest's eventually construct. *

* *

* This method invokes manage.timeouts.implicitlyWait on the passed WebDriver. See the documentation of Selenium's * WebDriver#Timeouts interface for more information. *

* * @param timeout the time span to implicitly wait * @param driver the WebDriver on which to set the implicit wait */ def implicitlyWait(timeout: Span)(implicit driver: WebDriver) { driver.manage.timeouts.implicitlyWait(timeout.totalNanos, TimeUnit.NANOSECONDS) } /** * Close all windows, and exit the driver. * * @param driver the WebDriver on which to quit. */ def quit()(implicit driver: WebDriver) { driver.quit() } /** * Get an opaque handle to current active window that uniquely identifies it within the implicit driver instance. * * @param driver the WebDriver with which to drive the browser */ def windowHandle(implicit driver: WebDriver): String = driver.getWindowHandle /** * Get a set of window handles which can be used to iterate over all open windows * * @param driver the WebDriver with which to drive the browser */ def windowHandles(implicit driver: WebDriver): Set[String] = driver.getWindowHandles.asScala.toSet /** * This object is part of ScalaTest's Selenium DSL. Please see the documentation for * WebBrowser for an overview of the Selenium DSL. * *

* This object enables syntax such as the following: *

* *
   * switch to alert
   * ^
   * 
*/ object switch { /** * Switch to the specified SwitchTarget * * @param target the SwitchTarget to switch to * @param driver the WebDriver with which to drive the browser * @return instance of specified SwitchTarget's type parameter */ def to[T](target: SwitchTarget[T])(implicit driver: WebDriver): T = { target.switch(driver) } } /** * This value supports switching to the currently active element in ScalaTest's Selenium DSL. * Please see the documentation for WebBrowser for an overview of the Selenium DSL. * *

* This class is enables the following syntax: *

* *
   * switch to activeElement
   *           ^
   * 
*/ val activeElement = new ActiveElementTarget() /** * This value supports switching to the alert box in ScalaTest's Selenium DSL. * Please see the documentation for WebBrowser for an overview of the Selenium DSL. * *

* This class is enables the following syntax: *

* *
   * switch to alert
   *           ^
   * 
*/ val alert = new AlertTarget() /** * This value supports switching to the default content in ScalaTest's Selenium DSL. * Please see the documentation for WebBrowser for an overview of the Selenium DSL. * *

* This class is enables the following syntax: *

* *
   * switch to defaultContent
   *           ^
   * 
*/ val defaultContent = new DefaultContentTarget() /** * This method supports switching to a frame by index in ScalaTest's Selenium DSL. * Please see the documentation for WebBrowser for an overview of the Selenium DSL. * *

* This class is enables the following syntax: *

* *
   * switch to frame(0)
   *           ^
   * 
* * @param index the index of frame to switch to * @return a FrameIndexTarget instance */ def frame(index: Int) = new FrameIndexTarget(index) /** * This method supports switching to a frame by name or ID in ScalaTest's Selenium DSL. * Please see the documentation for WebBrowser for an overview of the Selenium DSL. * *

* This class is enables the following syntax: *

* *
   * switch to frame("name")
   *           ^
   * 
* * @param nameOrId name or ID of the frame to switch to * @return a FrameNameOrIdTarget instance */ def frame(nameOrId: String) = new FrameNameOrIdTarget(nameOrId) /** * This method supports switching to a frame by web element in ScalaTest's Selenium DSL. * Please see the documentation for WebBrowser for an overview of the Selenium DSL. * * @param element WebElement which is contained in the frame to switch to * @return a FrameWebElementTarget instance */ def frame(element: WebElement) = new FrameWebElementTarget(element) /** * This method supports switching to a frame by element in ScalaTest's Selenium DSL. * Please see the documentation for WebBrowser for an overview of the Selenium DSL. * * @param element Element which is contained in the frame to switch to * @return a FrameElementTarget instance */ def frame(element: Element) = new FrameElementTarget(element) /** * This method supports switching to a frame by Query in ScalaTest's Selenium DSL. * Please see the documentation for WebBrowser for an overview of the Selenium DSL. * * @param query Query used to select WebElement which is contained in the frame to switch to * @return a FrameWebElementTarget instance */ def frame(query: Query)(implicit driver: WebDriver) = new FrameWebElementTarget(query.webElement) /** * This class supports switching to a window by name or handle in ScalaTest's Selenium DSL. * Please see the documentation for WebBrowser for an overview of the Selenium DSL. * *

* This class is enables the following syntax: *

* *
   * switch to window(windowHandle)
   *           ^
   * 
* * @param nameOrHandle name or window handle of the window to switch to * @return a WindowTarget instance */ def window(nameOrHandle: String) = new WindowTarget(nameOrHandle) /** * Switch to the specified SwitchTarget * * @param target the SwitchTarget to switch to * @param driver the WebDriver with which to drive the browser * @return instance of specified SwitchTarget's type parameter */ def switchTo[T](target: SwitchTarget[T])(implicit driver: WebDriver): T = switch to target /** * Go back to previous page. * * @param driver the WebDriver with which to drive the browser */ def goBack()(implicit driver: WebDriver) { driver.navigate.back() } /** * Go forward to next page. * * @param driver the WebDriver with which to drive the browser */ def goForward()(implicit driver: WebDriver) { driver.navigate.forward() } /** * Reload the current page. * * @param driver the WebDriver with which to drive the browser */ def reloadPage()(implicit driver: WebDriver) { driver.navigate.refresh() } /** * This object is part of ScalaTest's Selenium DSL. Please see the documentation for * WebBrowser for an overview of the Selenium DSL. * *

* This object enables syntax such as the following: *

* *
   * add cookie("aName", "aValue") 
   * ^
   * 
*/ object add { private def addCookie(cookie: Cookie)(implicit driver: WebDriver) { driver.manage.addCookie(cookie) } // Default values determined from http://code.google.com/p/selenium/source/browse/trunk/java/client/src/org/openqa/selenium/Cookie.java /** * Add cookie in the web browser. If the cookie's domain name is left blank (default), it is assumed that the cookie is meant for the domain of the current document. * * @param name cookie's name * @param value cookie's value * @param path cookie's path * @param expiry cookie's expiry data * @param domain cookie's domain name * @param secure whether this cookie is secured. * @param driver the WebDriver with which to drive the browser */ def cookie(name: String, value: String, path: String = "/", expiry: Date = null, domain: String = null, secure: Boolean = false)(implicit driver: WebDriver) { addCookie(new Cookie(name, value, domain, path, expiry, secure)) } } /** * Get a saved cookie from web browser, throws TestFailedException if the cookie does not exist. * * @param name cookie's name * @return a WrappedCookie instance */ def cookie(name: String)(implicit driver: WebDriver): WrappedCookie = { getCookie(name) } private def getCookie(name: String)(implicit driver: WebDriver): WrappedCookie = { driver.manage.getCookies.asScala.toList.find(_.getName == name) match { case Some(cookie) => new WrappedCookie(cookie) case None => throw new TestFailedException( sde => Some("Cookie '" + name + "' not found."), None, getStackDepthFun("WebBrowser.scala", "getCookie", 1) ) } } /** * This object is part of ScalaTest's Selenium DSL. Please see the documentation for * WebBrowser for an overview of the Selenium DSL. * *

* This object enables syntax such as the following: *

* *
   * delete cookie "aName" 
   * ^
   * 
   * delete all cookies
   * ^
   * 
*/ object delete { private def deleteCookie(name: String)(implicit driver: WebDriver) { val cookie = getCookie(name) if (cookie == null) throw new TestFailedException( sde => Some("Cookie '" + name + "' not found."), None, getStackDepthFun("WebBrowser.scala", "deleteCookie", 1) ) driver.manage.deleteCookie(cookie.underlying) } /** * Delete cookie with the specified name from web browser, throws TestFailedException if the specified cookie does not exists. * * @param name cookie's name * @param driver the WebDriver with which to drive the browser */ def cookie(name: String)(implicit driver: WebDriver) { deleteCookie(name) } /** * Delete all cookies in the current domain from web browser. * * @param driver the WebDriver with which to drive the browser */ def all(cookies: CookiesNoun)(implicit driver: WebDriver) { driver.manage.deleteAllCookies() } } /** * Add cookie in the web browser. If the cookie's domain name is left blank (default), it is assumed that the cookie is meant for the domain of the current document. * * @param name cookie's name * @param value cookie's value * @param path cookie's path * @param expiry cookie's expiry data * @param domain cookie's domain name * @param secure whether this cookie is secured. * @param driver the WebDriver with which to drive the browser */ def addCookie(name: String, value: String, path: String = "/", expiry: Date = null, domain: String = null, secure: Boolean = false)(implicit driver: WebDriver) { add cookie (name, value, path, expiry, domain, secure) } /** * Delete cookie with the specified name from web browser, throws TestFailedException if the specified cookie does not exists. * * @param name cookie's name * @param driver the WebDriver with which to drive the browser */ def deleteCookie(name: String)(implicit driver: WebDriver) { delete cookie name } /** * Delete all cookies in the current domain from web browser. * * @param driver the WebDriver with which to drive the browser */ def deleteAllCookies()(implicit driver: WebDriver) { delete all cookies } /** * Check if screenshot is supported * * @param driver the WebDriver with which to drive the browser * @return true if screenshot is supported, false otherwise */ def isScreenshotSupported(implicit driver: WebDriver): Boolean = driver.isInstanceOf[TakesScreenshot] /** * This object is part of ScalaTest's Selenium DSL. Please see the documentation for * WebBrowser for an overview of the Selenium DSL. * *

* This object enables syntax such as the following: *

* *
   * capture
   * ^
   * 
   * capture to "MyScreenshot.png" 
   * ^
   * 
*/ object capture { /** * Capture screenshot and save it as the specified name (if file name does not end with .png, it will be extended automatically) in capture directory, * which by default is system property's java.io.tmpdir. You can change capture directory by calling setCaptureDir * * @param fileName screenshot file name, if does not end with .png, it will be extended automatically */ def to(fileName: String)(implicit driver: WebDriver) { driver match { case takesScreenshot: TakesScreenshot => val tmpFile = takesScreenshot.getScreenshotAs(OutputType.FILE) val outFile = new File(targetDir, if (fileName.toLowerCase.endsWith(".png")) fileName else fileName + ".png") new FileOutputStream(outFile) getChannel() transferFrom( new FileInputStream(tmpFile) getChannel, 0, Long.MaxValue ) case _ => throw new UnsupportedOperationException("Screen capture is not support by " + driver.getClass.getName) } } /** * Capture screenshot and save it in capture directory, which by default is system property's java.io.tmpdir. * You can change capture directory by calling setCaptureDir */ def apply()(implicit driver: WebDriver): File = { driver match { case takesScreenshot: TakesScreenshot => val tmpFile = takesScreenshot.getScreenshotAs(OutputType.FILE) val fileName = tmpFile.getName val outFile = new File(targetDir, if (fileName.toLowerCase.endsWith(".png")) fileName else fileName + ".png") new FileOutputStream(outFile) getChannel() transferFrom( new FileInputStream(tmpFile) getChannel, 0, Long.MaxValue ) outFile case _ => throw new UnsupportedOperationException("Screen capture is not support by " + driver.getClass.getName) } } } /** * Capture screenshot and save it as the specified name (if file name does not end with .png, it will be extended automatically) in capture directory, * which by default is system property's java.io.tmpdir. You can change capture directory by calling setCaptureDir * * @param fileName screenshot file name, if does not end with .png, it will be extended automatically */ def captureTo(fileName: String)(implicit driver: WebDriver) { capture to fileName } // Can get by with volatile, because the setting doesn't depend on the getting @volatile private var targetDir = new File(System.getProperty("java.io.tmpdir")) /** * Set capture directory. * * @param targetDirPath the path of capture directory */ def setCaptureDir(targetDirPath: String) { targetDir = if (targetDirPath.endsWith(File.separator)) new File(targetDirPath) else new File(targetDirPath + File.separator) if (!targetDir.exists) targetDir.mkdirs() } /** * Execute the given function, if ModifiableMessage exception is thrown from the given function, * a screenshot will be captured automatically into capture directory, which by default is system property's java.io.tmpdir. * You can change capture directory by calling setCaptureDir * * @param fun function to execute */ def withScreenshot(fun: => Unit)(implicit driver: WebDriver) { try { fun } catch { case e: org.scalatest.exceptions.ModifiableMessage[_] => throw e.modifyMessage{ (currentMessage: Option[String]) => val captureFile: File = capture.apply() currentMessage match { case Some(currentMsg) => Some(currentMsg + "; screenshot captured in " + captureFile.getAbsolutePath) case None => Some("screenshot captured in " + captureFile.getAbsolutePath) } } } } /** * Executes JavaScript in the context of the currently selected frame or window. The script fragment provided will be executed as the body of an anonymous function. * *

* Within the script, you can use document to refer to the current document. Local variables will not be available once the script has finished executing, but global variables will. *

* *

* To return a value (e.g. if the script contains a return statement), then the following steps will be taken: *

* *
    *
  1. For an HTML element, this method returns a WebElement
  2. *
  3. For a decimal, a Double is returned
  4. *
  5. For a non-decimal number, a Long is returned
  6. *
  7. For a boolean, a Boolean is returned
  8. *
  9. For all other cases, a String is returned
  10. *
  11. For an array, return a List with each object following the rules above. We support nested lists *
  12. Unless the value is null or there is no return value, in which null is returned
  13. * * * @param script the JavaScript to execute * @param args the arguments to the script, may be empty * @return One of Boolean, Long, String, List or WebElement. Or null */ def executeScript[T](script: String, args: AnyRef*)(implicit driver: WebDriver): AnyRef = driver match { case executor: JavascriptExecutor => executor.executeScript(script, args.toArray : _*) case _ => throw new UnsupportedOperationException("Web driver " + driver.getClass.getName + " does not support javascript execution.") } /** * Executes an asynchronous piece of JavaScript in the context of the currently selected frame or window. Unlike executing synchronous JavaScript, * scripts executed with this method must explicitly signal they are finished by invoking the provided callback. This callback is always injected into * the executed function as the last argument. * *

    * The first argument passed to the callback function will be used as the script's result. This value will be handled as follows: *

    * *
      *
    1. For an HTML element, this method returns a WebElement
    2. *
    3. For a number, a Long is returned
    4. *
    5. For a boolean, a Boolean is returned
    6. *
    7. For all other cases, a String is returned
    8. *
    9. For an array, return a List with each object following the rules above. We support nested lists *
    10. Unless the value is null or there is no return value, in which null is returned
    11. * * *

      * Script arguments must be a number, a boolean, a String, WebElement, or a List of any combination of the above. An exception will * be thrown if the arguments do not meet these criteria. The arguments will be made available to the JavaScript via the "arguments" variable. *

      * * @param script the JavaScript to execute * @param args the arguments to the script, may be empty * @return One of Boolean, Long, String, List, WebElement, or null */ def executeAsyncScript(script: String, args: AnyRef*)(implicit driver: WebDriver): AnyRef = driver match { case executor: JavascriptExecutor => executor.executeAsyncScript(script, args.toArray : _*) case _ => throw new UnsupportedOperationException("Web driver " + driver.getClass.getName + " does not support javascript execution.") } /** * Sets the amount of time to wait for an asynchronous script to finish execution before throwing an exception. * * @param timeout the amount of time to wait for an asynchronous script to finish execution before throwing exception */ def setScriptTimeout(timeout: Span)(implicit driver: WebDriver) { driver.manage().timeouts().setScriptTimeout(timeout.totalNanos, TimeUnit.NANOSECONDS); } private def getStackDepthFun(fileName: String, methodName: String, adjustment: Int = 0): (StackDepthException => Int) = { sde => getStackDepth(sde.getStackTrace, fileName, methodName, adjustment) } private def getStackDepth(stackTrace: Array[StackTraceElement], fileName: String, methodName: String, adjustment: Int = 0) = { val stackTraceList = stackTrace.toList val fileNameIsDesiredList: List[Boolean] = for (element <- stackTraceList) yield element.getFileName == fileName // such as "Checkers.scala" val methodNameIsDesiredList: List[Boolean] = for (element <- stackTraceList) yield element.getMethodName == methodName // such as "check" // For element 0, the previous file name was not desired, because there is no previous // one, so you start with false. For element 1, it depends on whether element 0 of the stack trace // had the desired file name, and so forth. val previousFileNameIsDesiredList: List[Boolean] = false :: (fileNameIsDesiredList.dropRight(1)) // Zip these two related lists together. They now have two boolean values together, when both // are true, that's a stack trace element that should be included in the stack depth. val zipped1 = methodNameIsDesiredList zip previousFileNameIsDesiredList val methodNameAndPreviousFileNameAreDesiredList: List[Boolean] = for ((methodNameIsDesired, previousFileNameIsDesired) <- zipped1) yield methodNameIsDesired && previousFileNameIsDesired // Zip the two lists together, that when one or the other is true is an include. val zipped2 = fileNameIsDesiredList zip methodNameAndPreviousFileNameAreDesiredList val includeInStackDepthList: List[Boolean] = for ((fileNameIsDesired, methodNameAndPreviousFileNameAreDesired) <- zipped2) yield fileNameIsDesired || methodNameAndPreviousFileNameAreDesired val includeDepth = includeInStackDepthList.takeWhile(include => include).length val depth = if (includeDepth == 0 && stackTrace(0).getFileName != fileName && stackTrace(0).getMethodName != methodName) stackTraceList.takeWhile(st => st.getFileName != fileName || st.getMethodName != methodName).length else includeDepth depth + adjustment } // Clears the text field or area, then presses the passed keys /** * Clears the current active TextField or TextArea, and presses the passed keys. * Throws TestFailedException if current active is not TextField or TextArea. * * @param value keys to press in current active TextField or TextArea */ def enter(value: String)(implicit driver: WebDriver) { val ae = switch to activeElement ae match { case tf: TextField => tf.value = value case ta: TextArea => ta.value = value case pf: PasswordField => pf.value = value case _ => throw new TestFailedException( sde => Some("Currently selected element is neither a text field, text area nor password field"), None, getStackDepthFun("WebBrowser.scala", "switch", 1) ) } } /** * Press the passed keys to current active element. * * @param value keys to press in current active element */ def pressKeys(value: String)(implicit driver: WebDriver) { val ae: WebElement = driver.switchTo.activeElement ae.sendKeys(value) } } /** * Companion object that facilitates the importing of WebBrowser members as * an alternative to mixing it in. One use case is to import WebBrowser members so you can use * them in the Scala interpreter. */ object WebBrowser extends WebBrowser /** * WebBrowser subtrait that defines an implicit WebDriver for HTMLUnit (an org.openqa.selenium.htmlunit.HtmlUnitDriver), with JavaScript * enabled by default. * *

      * Note: You can disable JavaScript with: *

      * *
       * webDriver.setJavascriptEnabled(false)
       * 
      */ trait HtmlUnit extends WebBrowser with ScreenshotCapturer { /** * WebBrowser subtrait that defines an implicit WebDriver for HTMLUnit (an org.openqa.selenium.htmlunit.HtmlUnitDriver), with JavaScript * enabled by default. * *

      * Note: You can disable JavaScript with: *

      * *
         * webDriver.setJavascriptEnabled(false)
         * 
      */ implicit val webDriver = new HtmlUnitDriver() webDriver.setJavascriptEnabled(true) /** * Captures a screenshot and saves it as a file in the specified directory. */ def captureScreenshot(directory: String) { capture to directory } } /** * Companion object that facilitates the importing of HtmlUnit members as * an alternative to mixing it in. One use case is to import HtmlUnit members so you can use * them in the Scala interpreter. */ object HtmlUnit extends HtmlUnit /** * WebBrowser subtrait that defines an implicit WebDriver for Firefox (an org.openqa.selenium.firefox.FirefoxDriver). * *

      * The FirefoxDriver uses the FirefoxProfile defined as firefoxProfile. By default this is just a new FirefoxProfile. * You can mutate this object to modify the profile, or override firefoxProfile. *

      */ trait Firefox extends WebBrowser with ScreenshotCapturer { /** * The FirefoxProfile passed to the constructor of the FirefoxDriver returned by webDriver. * *

      * The FirefoxDriver uses the FirefoxProfile defined as firefoxProfile. By default this is just a new FirefoxProfile. * You can mutate this object to modify the profile, or override firefoxProfile. *

      */ val firefoxProfile = new FirefoxProfile() /** * WebBrowser subtrait that defines an implicit WebDriver for Firefox (an org.openqa.selenium.firefox.FirefoxDriver), with a default * Firefox profile. * *

      * The FirefoxDriver uses the FirefoxProfile defined as firefoxProfile. By default this is just a new FirefoxProfile. * You can mutate this object to modify the profile, or override firefoxProfile. *

      */ implicit val webDriver = new FirefoxDriver(firefoxProfile) /** * Captures a screenshot and saves it as a file in the specified directory. */ def captureScreenshot(directory: String) { capture to directory } } /** * Companion object that facilitates the importing of Firefox members as * an alternative to mixing it in. One use case is to import Firefox members so you can use * them in the Scala interpreter. */ object Firefox extends Firefox /** * WebBrowser subtrait that defines an implicit WebDriver for Safari (an org.openqa.selenium.safari.SafariDriver). */ trait Safari extends WebBrowser with ScreenshotCapturer { /** * WebBrowser subtrait that defines an implicit WebDriver for Safari (an org.openqa.selenium.safari.SafariDriver). */ implicit val webDriver = new SafariDriver() /** * Captures a screenshot and saves it as a file in the specified directory. */ def captureScreenshot(directory: String) { capture to directory } } /** * Companion object that facilitates the importing of Safari members as * an alternative to mixing it in. One use case is to import Safari members so you can use * them in the Scala interpreter. */ object Safari extends Safari /** * WebBrowser subtrait that defines an implicit WebDriver for Chrome (an org.openqa.selenium.chrome.ChromeDriver). */ trait Chrome extends WebBrowser with ScreenshotCapturer { /** * WebBrowser subtrait that defines an implicit WebDriver for Chrome (an org.openqa.selenium.chrome.ChromeDriver). */ implicit val webDriver = new ChromeDriver() /** * Captures a screenshot and saves it as a file in the specified directory. */ def captureScreenshot(directory: String) { capture to directory } } /** * Companion object that facilitates the importing of Chrome members as * an alternative to mixing it in. One use case is to import Chrome members so you can use * them in the Scala interpreter. */ object Chrome extends Chrome /** * WebBrowser subtrait that defines an implicit WebDriver for Internet Explorer (an org.openqa.selenium.ie.InternetExplorerDriver). */ trait InternetExplorer extends WebBrowser with ScreenshotCapturer { /** * WebBrowser subtrait that defines an implicit WebDriver for Internet Explorer (an org.openqa.selenium.ie.InternetExplorerDriver). */ implicit val webDriver = new InternetExplorerDriver() /** * Captures a screenshot and saves it as a file in the specified directory. */ def captureScreenshot(directory: String) { capture to directory } } /** * Companion object that facilitates the importing of InternetExplorer members as * an alternative to mixing it in. One use case is to import InternetExplorer members so you can use * them in the Scala interpreter. */ object InternetExplorer extends InternetExplorer /* *

      * If you mix in ScreenshotOnFailure, ScalaTest will capture a screenshot and store it to either the system temp directory * or a directory you choose, and send the filename to the report, associated with the failed test. The ScreenshotOnFailure trait requires that it be * mixed into a ScreenshotCapturer, which trait WebBrowser does not extend. To satisfy this * requirement, you can extend one of WebBrowser's subtraits, such as: *

      * *
       * class WebAppSpec extends Firefox with ScreenshotOnFailure {
       *   // ...
       * }
       * 
      * */