WebBrowser
subtrait that defines an implicit WebDriver
for Chrome (an org.openqa.selenium.chrome.ChromeDriver
).
Trait declaring a webDriver
field that enables tests to be abstracted across different kinds of WebDriver
s.
Trait declaring a webDriver
field that enables tests to be abstracted across different kinds of WebDriver
s.
This trait enables you to place tests that you want to run in multiple browsers in a trait with a self type of
WebBrowser with Driver
, like this:
trait MyBrowserTests { this: WebBrowser with Driver => // Your browser tests }
Then you can create concrete subclasses for each actual browser you want to run those tests in:
class MyBrowserTestsWithChrome extends MyBrowserTests with Chrome class MyBrowserTestsWithSafari extends MyBrowserTests with Safari class MyBrowserTestsWithInternetExplorer extends MyBrowserTests with InternetExplorer class MyBrowserTestsWithFirefox extends MyBrowserTests with Firefox
WebBrowser
subtrait that defines an implicit WebDriver
for Firefox (an org.openqa.selenium.firefox.FirefoxDriver
).
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
.
WebBrowser
subtrait that defines an implicit WebDriver
for HTMLUnit (an org.openqa.selenium.htmlunit.HtmlUnitDriver
), with JavaScript
enabled by default.
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)
WebBrowser
subtrait that defines an implicit WebDriver
for Internet Explorer (an org.openqa.selenium.ie.InternetExplorerDriver
).
WebBrowser
subtrait that defines an implicit WebDriver
for Internet Explorer (an org.openqa.selenium.ie.InternetExplorerDriver
).
Trait that facilitates using the page object pattern with the ScalaTest Selenium DSL.
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
WebBrowser
subtrait that defines an implicit WebDriver
for Safari (an org.openqa.selenium.safari.SafariDriver
).
WebBrowser
subtrait that defines an implicit WebDriver
for Safari (an org.openqa.selenium.safari.SafariDriver
).
Trait that provides a domain specific language (DSL) for writing browser-based tests using Selenium.
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._ import org.openqa.selenium._ import htmlunit._ class BlogSpec extends FlatSpec with Matchers with WebBrowser { implicit val webDriver: WebDriver = new HtmlUnitDriver val host = "http://localhost:9000/" "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 Matchers with HtmlUnit { val host = "http://localhost:9000/" "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:
Driver | WebBrowser subtrait |
---|---|
Google Chrome |
Chrome
|
Mozilla Firefox |
Firefox
|
HtmlUnit |
HtmlUnit
|
Microsoft Internet Explorer |
InternetExplorer
|
Apple Safari |
Safari
|
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
.
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, selection lists, and the input types introduced in HTML5. 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.
The most common way to access field value is through the value
property, which is supported by the following
input types:
Tag Name | Input Type | Lookup Method |
---|---|---|
input
|
text
|
textField
|
textarea
|
-
|
textArea
|
input
|
password
|
pwdField
|
input
|
email
|
emailField
|
input
|
color
|
colorField
|
input
|
date
|
dateField
|
input
|
datetime
|
dateTimeField
|
input
|
datetime-local
|
dateTimeLocalField
|
input
|
month
|
monthField
|
input
|
number
|
numberField
|
input
|
range
|
rangeField
|
input
|
search
|
searchField
|
input
|
tel
|
telField
|
input
|
time
|
timeField
|
input
|
url
|
urlField
|
input
|
week
|
weekField
|
You can change a input field's value by assigning it via the =
operator, like this:
textField("q").value = "Cheese!"
And you can access a input 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 other type of input fields by replacing textField
with Lookup Method
listed in table above,
for example to use text area:
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 input fields is to use enter
or pressKeys
.
Although both of enter
and pressKeys
send characters to the active element, pressKeys
can be used on any kind of
element, whereas enter
can only be used on text entry fields, which include:
textField
textArea
pwdField
emailField
searchField
telField
urlField
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 input field 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 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")
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)
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("select1").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.
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")
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()
You can switch to a popup alert bo using the following code:
switch to alertBox
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
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()
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
All element types (textField
, textArea
, radioButton
, checkbox
, singleSel
, multiSel
)
support the following useful properties:
Method | Description |
---|---|
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. |
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
.
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
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
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
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")
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] }
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()
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 notation | method 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")
|
Companion object that facilitates the importing of Chrome
members as
an alternative to mixing it in.
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.
Companion object that facilitates the importing of Firefox
members as
an alternative to mixing it in.
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.
Companion object that facilitates the importing of HtmlUnit
members as
an alternative to mixing it in.
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.
Companion object that facilitates the importing of InternetExplorer
members as
an alternative to mixing it in.
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.
Companion object that facilitates the importing of Safari
members as
an alternative to mixing it in.
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.
Companion object that facilitates the importing of WebBrowser
members as
an alternative to mixing it in.
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.
WebBrowser
subtrait that defines an implicitWebDriver
for Chrome (anorg.openqa.selenium.chrome.ChromeDriver
).