layout | title |
---|---|
doc |
WebDriver - Codeception - Documentation |
{% highlight yaml %} composer require --dev codeception/module-webdriver
{% endhighlight %}
Run tests in real browsers using the W3C WebDriver protocol. There are multiple ways of running browser tests using WebDriver:
- Java is required
- NodeJS is required
The fastest way to get started is to Install and launch Selenium using selenium-standalone NodeJS package.
Launch selenium standalone in separate console window:
{% highlight yaml %} selenium-standalone start
{% endhighlight %}
Update configuration in acceptance.suite.yml
:
{% highlight yaml %}
modules: enabled: - WebDriver: url: 'http://localhost/' browser: chrome # 'chrome' or 'firefox'
{% endhighlight %}
To enable headless mode (launch tests without showing a window) for Chrome browser using Selenium use this config in acceptance.suite.yml
:
{% highlight yaml %}
modules: enabled: - WebDriver: url: 'http://localhost/' browser: chrome capabilities: goog:chromeOptions: args: ["--headless"]
{% endhighlight %}
Docker can ship Selenium Server with all its dependencies and browsers inside a single container. Running tests inside Docker is as easy as pulling official selenium image and starting a container with Chrome:
{% highlight yaml %} docker run --net=host --shm-size 2g selenium/standalone-chrome
{% endhighlight %}
By using --net=host
allow Selenium to access local websites.
Tests can be executed directly through ChromeDriver or GeckoDriver (for Firefox). Consider using this option if you don't plan to use Selenium.
- Download and install ChromeDriver
- Launch ChromeDriver in a separate console window:
chromedriver --url-base=/wd/hub
.
Configuration in acceptance.suite.yml
:
{% highlight yaml %}
modules: enabled: - WebDriver: browser: chrome url: 'http://localhost/' window_size: 2000x1000 port: 9515 capabilities: goog:chromeOptions: args: ["--headless"] # Run Chrome in headless mode prefs: download.default_directory: "..."
{% endhighlight %} See here for additional Chrome options
- GeckoDriver must be installed
- Start GeckoDriver in a separate console window:
geckodriver
.
Configuration in acceptance.suite.yml
:
{% highlight yaml %}
modules:
enabled:
- WebDriver:
browser: firefox
url: 'http://localhost/'
window_size: 2000x1000
path: ''
capabilities:
acceptInsecureCerts: true # allow self-signed certificates
moz:firefoxOptions:
args: ["-headless"] # Run Firefox in headless mode
prefs:
intl.accept_languages: "de-AT" # Set HTTP-Header Accept-Language: de-AT
for requests
{% endhighlight %} See here for Firefox capabilities
Cloud Testing services can run your WebDriver tests in the cloud. In case you want to test a local site or site behind a firewall you should use a tunnel application provided by a service.
- Create an account at SauceLabs.com to get your username and access key
- In the module configuration use the format
username
:access_key
@ondemand.saucelabs.com' forhost
- Configure
platformName
undercapabilities
to define the Operating System - run a tunnel app if your site can't be accessed from Internet
{% highlight yaml %}
modules:
enabled:
- WebDriver:
url: http://mysite.com
host: '<username>:<access key>@ondemand.saucelabs.com'
port: 80
browser: chrome
capabilities:
platformName: 'Windows 10'
{% endhighlight %}
- Create an account at BrowserStack to get your username and access key
- In the module configuration use the format
username
:access_key
@hub.browserstack.com' forhost
- Configure
os
andos_version
undercapabilities
to define the operating System - If your site is available only locally or via VPN you should use a tunnel app. In this case add
browserstack.local
capability and set it to true.
{% highlight yaml %}
modules:
enabled:
- WebDriver:
url: http://mysite.com
host: '<username>:<access key>@hub.browserstack.com'
port: 80
browser: chrome
capabilities:
bstack:options:
os: Windows
osVersion: 10
local: true # for local testing
{% endhighlight %}
- Create an account at LambdaTest to get your username and access key
- In the module configuration use the format
username
:access key
@hub.lambdatest.com' forhost
- Configure
platformName
, 'browserVersion', and 'browserName' underLT:Options
to define test environments. - If your website is available only locally or via VPN you should use LambdaTest tunnel. In this case, you can add capability "tunnel":true;.
{% highlight yaml %}
modules: enabled:
- WebDriver: url: "https://openclassrooms.com" host: 'hub.lambdatest.com' port: 80 browser: 'Chrome' capabilities: LT:Options: platformName: 'Windows 10' browserVersion: 'latest-5' browserName: 'Chrome' tunnel: true #for Local testing
{% endhighlight %}
- Create an account at TestingBot to get your key and secret
- In the module configuration use the format
key
:secret
@hub.testingbot.com' forhost
- Configure
platformName
undercapabilities
to define the Operating System - Run TestingBot Tunnel if your site can't be accessed from Internet
{% highlight yaml %}
modules:
enabled:
- WebDriver:
url: http://mysite.com
host: '<key>:<secret>@hub.testingbot.com'
port: 80
browser: chrome
capabilities:
platformName: Windows 10
{% endhighlight %}
url
required - Base URL for your app (amOnPage opens URLs relative to this setting).browser
required - Browser to launch.host
- Selenium server host (127.0.0.1 by default).port
- Selenium server port (4444 by default).restart
- Set tofalse
(default) to use the same browser window for all tests, or set totrue
to create a new window for each test. In any case, when all tests are finished the browser window is closed.start
- Autostart a browser for tests. Can be disabled if browser session is started with_initializeSession
inside a Helper.window_size
- Initial window size. Set tomaximize
or a dimension in the format640x480
.clear_cookies
- Set to false to keep cookies, or set to true (default) to delete all cookies between tests.wait
(default: 0 seconds) - Whenever element is required and is not on page, wait for n seconds to find it before fail.capabilities
- Sets Selenium desired capabilities. Should be a key-value array.connection_timeout
- timeout for opening a connection to remote selenium server (30 seconds by default).request_timeout
- timeout for a request to return something from remote selenium server (30 seconds by default).pageload_timeout
- amount of time to wait for a page load to complete before throwing an error (default 0 seconds).http_proxy
- sets http proxy server url for testing a remote server.http_proxy_port
- sets http proxy server portssl_proxy
- sets ssl(https) proxy server url for testing a remote server.ssl_proxy_port
- sets ssl(https) proxy server portdebug_log_entries
- how many selenium entries to print withdebugWebDriverLogs
or on fail (0 by default).log_js_errors
- Set to true to include possible JavaScript to HTML report, or set to false (default) to deactivate.webdriver_proxy
- sets http proxy to tunnel requests to the remote Selenium WebDriver throughwebdriver_proxy_port
- sets http proxy server port to tunnel requests to the remote Selenium WebDriver through
Example (acceptance.suite.yml
)
{% highlight yaml %}
modules:
enabled:
- WebDriver:
url: 'http://localhost/'
browser: firefox
window_size: 1024x768
capabilities:
unhandledPromptBehaviour: 'accept'
moz:firefoxOptions:
profile: '~/firefox-profiles/codeception-profile.zip.b64'
{% endhighlight %}
While all Codeception modules are designed to work stand-alone, it's still possible to load several modules at once. To use e.g. the Asserts module in your acceptance tests, just load it like this in your acceptance.suite.yml
:
{% highlight yaml %}
modules: enabled: - WebDriver - Asserts
{% endhighlight %}
However, when loading a framework module (e.g. Symfony) like this, it would lead to a conflict: When you call $I->amOnPage()
, Codeception wouldn't know if you want to access the page using WebDriver's amOnPage()
, or Symfony's amOnPage()
. That's why possibly conflicting modules are separated into "parts". Here's how to load just the "services" part from e.g. Symfony:
{% highlight yaml %}
modules: enabled: - WebDriver - Symfony: part: services
{% endhighlight %} To find out which parts each module has, look at the "Parts" header on the module's page.
Most methods in this module that operate on a DOM element (e.g. click
) accept a locator as the first argument,
which can be either a string or an array.
If the locator is an array, it should have a single element,
with the key signifying the locator type (id
, name
, css
, xpath
, link
, or class
)
and the value being the locator itself.
This is called a "strict" locator.
Examples:
['id' => 'foo']
matches<div id="foo">
['name' => 'foo']
matches<div name="foo">
['css' => 'input[type=input][value=foo]']
matches<input type="input" value="foo">
['xpath' => "//input[@type='submit'][contains(@value, 'foo')]"]
matches<input type="submit" value="foobar">
['link' => 'Click here']
matches<a href="google.com">Click here</a>
['class' => 'foo']
matches<div class="foo">
Writing good locators can be tricky. The Mozilla team has written an excellent guide titled Writing reliable locators for Selenium and WebDriver tests.
If you prefer, you may also pass a string for the locator. This is called a "fuzzy" locator.
In this case, Codeception uses a a variety of heuristics (depending on the exact method called) to determine what element you're referring to.
For example, here's the heuristic used for the submitForm
method:
- Does the locator look like an ID selector (e.g. "#foo")? If so, try to find a form matching that ID.
- If nothing found, check if locator looks like a CSS selector. If so, run it.
- If nothing found, check if locator looks like an XPath expression. If so, run it.
- Throw an
ElementNotFound
exception.
Be warned that fuzzy locators can be significantly slower than strict locators.
Especially if you use Selenium WebDriver with wait
(aka implicit wait) option.
In the example above if you set wait
to 5 seconds and use XPath string as fuzzy locator,
submitForm
method will wait for 5 seconds at each step.
That means 5 seconds finding the form by ID, another 5 seconds finding by CSS
until it finally tries to find the form by XPath).
If speed is a concern, it's recommended you stick with explicitly specifying the locator type via the array syntax.
You can inject \Codeception\Scenario
into your test to get information about the current configuration:
{% highlight php %}
use Codeception\Scenario public function myTest(AcceptanceTester $I, Scenario $scenario) { if ('firefox' === $scenario->current('browser')) { // ... } }
{% endhighlight %}
See Get Scenario Metadata for more information on $scenario
.
webDriver
- instance of\Facebook\WebDriver\Remote\RemoteWebDriver
. Can be accessed from Helper classes for complex WebDriver interactions.
{% highlight php %}
// inside Helper class $this->getModule('WebDriver')->webDriver->getKeyboard()->sendKeys('hello, webdriver');
{% endhighlight %}
hidden API method, expected to be used from Helper classes
api
return \Facebook\WebDriver\WebDriver
Returns current WebDriver session for saving
hidden API method, expected to be used from Helper classes
api
param \Closure
$capabilityFunctionreturn void
Change capabilities of WebDriver. Should be executed before starting a new browser session.
This method expects a function to be passed which returns array or WebDriver Desired Capabilities object. Additional Chrome options (like adding extensions) can be passed as well.
{% highlight php %}
getModule('WebDriver')->_capabilities(function($currentCapabilities) { // or new \Facebook\WebDriver\Remote\DesiredCapabilities(); return \Facebook\WebDriver\Remote\DesiredCapabilities::firefox(); }); } {% endhighlight %} to make this work load `\Helper\Acceptance` before `WebDriver` in `acceptance.suite.yml`: {% highlight yaml %} modules: enabled: - \Helper\Acceptance - WebDriver {% endhighlight %} For instance, [**BrowserStack** cloud service](https://www.browserstack.com/automate/capabilities) may require a test name to be set in capabilities. This is how it can be done via `_capabilities` method from `Helper\Acceptance`: {% highlight php %} getMetadata()->getName(); $this->getModule('WebDriver')->_capabilities(function($currentCapabilities) use ($name) { $currentCapabilities['name'] = $name; return $currentCapabilities; }); } {% endhighlight %} In this case, please ensure that `\Helper\Acceptance` is loaded before WebDriver so new capabilities could be applied. #### _closeSession *hidden API method, expected to be used from Helper classes* * `api` * `param RemoteWebDriver|null` $webDriver a specific webdriver session instance * `return void` Manually closes current WebDriver session. {% highlight php %} getModule('WebDriver')->_closeSession(); // close a specific session $webDriver = $this->getModule('WebDriver')->webDriver; $this->getModule('WebDriver')->_closeSession($webDriver); {% endhighlight %} #### _findClickable *hidden API method, expected to be used from Helper classes* * `api` * `param WebDriverSearchContext` $page WebDriver instance or an element to search within * `param string|array|WebDriverBy` $link A link text or locator to click * `return ?\Facebook\WebDriver\WebDriverElement` Locates a clickable element. Use it in Helpers or GroupObject or Extension classes: {% highlight php %} getModule('WebDriver'); $page = $module->webDriver; // search a link or button on a page $el = $module->_findClickable($page, 'Click Me'); // search a link or button within an element $topBar = $module->_findElements('.top-bar')[0]; $el = $module->_findClickable($topBar, 'Click Me'); {% endhighlight %} #### _findElements *hidden API method, expected to be used from Helper classes* * `api` * `param ` $locator * `return array` Locates element using available Codeception locator types: * XPath * CSS * Strict Locator Use it in Helpers or GroupObject or Extension classes: {% highlight php %} getModule('WebDriver')->_findElements('.items'); $els = $this->getModule('WebDriver')->_findElements(['name' => 'username']); $editLinks = $this->getModule('WebDriver')->_findElements(['link' => 'Edit']); // now you can iterate over $editLinks and check that all them have valid hrefs {% endhighlight %} WebDriver module returns `Facebook\WebDriver\Remote\RemoteWebElement` instances PhpBrowser and Framework modules return `Symfony\Component\DomCrawler\Crawler` instances #### _getCurrentUri *hidden API method, expected to be used from Helper classes* * `api` * `throws ModuleException` * `return string` Uri of currently opened page. #### _getUrl *hidden API method, expected to be used from Helper classes* * `api` * `throws ModuleConfigException` * `return mixed` Returns URL of a host. #### _initializeSession *hidden API method, expected to be used from Helper classes* * `api` * `return void` Manually starts a new browser session. {% highlight php %} getModule('WebDriver')->_initializeSession(); {% endhighlight %} #### _loadSession *hidden API method, expected to be used from Helper classes* * `api` * `param RemoteWebDriver` $session * `return void` Loads current RemoteWebDriver instance as a session #### _restart *hidden API method, expected to be used from Helper classes* * `api` * `param array` $config * `return void` Restarts a web browser. Can be used with `_reconfigure` to open browser with different configuration {% highlight php %} getModule('WebDriver')->_restart(); // just restart $this->getModule('WebDriver')->_restart(['browser' => $browser]); // reconfigure + restart {% endhighlight %} #### _savePageSource *hidden API method, expected to be used from Helper classes* * `param string` $filename * `return void` Saves HTML source of a page to a file #### _saveScreenshot *hidden API method, expected to be used from Helper classes* * `api` * `param string` $filename Saves screenshot of current page to a file {% highlight php %} $this->getModule('WebDriver')->_saveScreenshot(codecept_output_dir().'screenshot_1.png'); {% endhighlight %} #### acceptPopup * `return void` Accepts the active JavaScript native popup window, as created by `window.alert`|`window.confirm`|`window.prompt`. Don't confuse popups with modal windows, as created by [various libraries](https://jster.net/category/windows-modals-popups). #### amOnPage * `param ` $page * `return void` Opens the page for the given relative URI. {% highlight php %} amOnPage('/'); // opens /register page $I->amOnPage('/register'); {% endhighlight %} #### amOnSubdomain * `param string` $subdomain * `return void` Changes the subdomain for the 'url' configuration parameter. Does not open a page; use `amOnPage` for that. {% highlight php %} amOnSubdomain('user'); $I->amOnPage('/'); // moves to https://user.mysite.com/ {% endhighlight %} #### amOnUrl * `param ` $url * `return void` Open web page at the given absolute URL and sets its hostname as the base host. {% highlight php %} amOnUrl('https://codeception.com'); $I->amOnPage('/quickstart'); // moves to https://codeception.com/quickstart {% endhighlight %} #### appendField * `param string|array|WebDriverBy` $field * `param string` $value * `throws ElementNotFound` * `return void` Append the given text to the given element. Can also add a selection to a select box. {% highlight php %} appendField('#mySelectbox', 'SelectValue'); $I->appendField('#myTextField', 'appended'); {% endhighlight %} #### attachFile * `param ` $field * `param string` $filename * `return void` Attaches a file relative to the Codeception `_data` directory to the given file upload field. {% highlight php %} attachFile('input[@type="file"]', 'prices.xls'); {% endhighlight %} #### cancelPopup * `return void` Dismisses the active JavaScript popup, as created by `window.alert`, `window.confirm`, or `window.prompt`. #### checkOption * `param ` $option * `return void` Ticks a checkbox. For radio buttons, use the `selectOption` method instead. {% highlight php %} checkOption('#agree'); {% endhighlight %} #### clearField * `param string|array|WebDriverBy` $field * `return void` Clears given field which isn't empty. {% highlight php %} clearField('#username'); {% endhighlight %} #### click * `param string|array` $link * `param ` $context * `return void` Perform a click on a link or a button, given by a locator. If a fuzzy locator is given, the page will be searched for a button, link, or image matching the locator string. For buttons, the "value" attribute, "name" attribute, and inner text are searched. For links, the link text is searched. For images, the "alt" attribute and inner text of any parent links are searched. The second parameter is a context (CSS or XPath locator) to narrow the search. Note that if the locator matches a button of type `submit`, the form will be submitted. {% highlight php %} click('Logout'); // button of form $I->click('Submit'); // CSS button $I->click('#form input[type=submit]'); // XPath $I->click('//form/*[@type="submit"]'); // link in context $I->click('Logout', '#nav'); // using strict locator $I->click(['link' => 'Login']); {% endhighlight %} #### clickWithLeftButton * `param null|string|array|WebDriverBy` $cssOrXPath css or xpath of the web element (body by default). * `param ?int` $offsetX * `param ?int` $offsetY * `throws ElementNotFound` * `return void` Performs click with the left mouse button on an element. If the first parameter `null` then the offset is relative to the actual mouse position. If the second and third parameters are given, then the mouse is moved to an offset of the element's top-left corner. Otherwise, the mouse is moved to the center of the element. {% highlight php %} clickWithLeftButton(['css' => '.checkout']); $I->clickWithLeftButton(null, 20, 50); $I->clickWithLeftButton(['css' => '.checkout'], 20, 50); {% endhighlight %} #### clickWithRightButton * `param null|string|array|WebDriverBy` $cssOrXPath css or xpath of the web element (body by default). * `param ?int` $offsetX * `param ?int` $offsetY * `throws ElementNotFound` * `return void` Performs contextual click with the right mouse button on an element. If the first parameter `null` then the offset is relative to the actual mouse position. If the second and third parameters are given, then the mouse is moved to an offset of the element's top-left corner. Otherwise, the mouse is moved to the center of the element. {% highlight php %} clickWithRightButton(['css' => '.checkout']); $I->clickWithRightButton(null, 20, 50); $I->clickWithRightButton(['css' => '.checkout'], 20, 50); {% endhighlight %} #### closeTab * `return void` Closes current browser tab and switches to previous active tab. {% highlight php %} closeTab(); {% endhighlight %} #### debugWebDriverLogs * `param ?\Codeception\TestInterface` $test * `return void` Print out latest Selenium Logs in debug mode #### deleteSessionSnapshot * `param ` $name * `return mixed` Deletes session snapshot. See [saveSessionSnapshot](#saveSessionSnapshot) #### dontSee * `param ` $text * `param array|string` $selector optional * `return void` Checks that the current page doesn't contain the text specified (case insensitive). Give a locator as the second parameter to match a specific region. {% highlight php %} dontSee('Login'); // I can suppose user is already logged in $I->dontSee('Sign Up','h1'); // I can suppose it's not a signup page $I->dontSee('Sign Up','//body/h1'); // with XPath $I->dontSee('Sign Up', ['css' => 'body h1']); // with strict CSS locator {% endhighlight %} Note that the search is done after stripping all HTML tags from the body, so `$I->dontSee('strong')` will fail on strings like: - `I am Stronger than thou
` - `<script>document.createElement('strong');</script>` But will ignore strings like: - `Home` - `I am Stronger than thou
` - `<script>document.createElement('strong');</script>` But will *not* be true for strings like: - `Home` - `Password:
Do you agree to our terms?
Select pricing plan: Free Paid {% endhighlight %} You could write the following to submit it: {% highlight php %} submitForm( '#userForm', [ 'user[login]' => 'Davert', 'user[password]' => '123456', 'user[agree]' => true ], 'submitButton' ); {% endhighlight %} Note that "2" will be the submitted value for the "plan" field, as it is the selected option. Also note that this differs from PhpBrowser, in that {% highlight yaml %} 'user' => [ 'login' => 'Davert' ] {% endhighlight %} is not supported at the moment. Named array keys *must* be included in the name as above. Pair this with seeInFormFields for quick testing magic. {% highlight php %} 'value', 'field2' => 'another value', 'checkbox1' => true, // ... ]; $I->submitForm('//form[@id=my-form]', $form, 'submitButton'); // $I->amOnPage('/path/to/form-page') may be needed $I->seeInFormFields('//form[@id=my-form]', $form); {% endhighlight %} Parameter values must be set to arrays for multiple input fields of the same name, or multi-select combo boxes. For checkboxes, either the string value can be used, or boolean values which will be replaced by the checkbox's value in the DOM. {% highlight php %} submitForm('#my-form', [ 'field1' => 'value', 'checkbox' => [ 'value of first checkbox', 'value of second checkbox', ], 'otherCheckboxes' => [ true, false, false, ], 'multiselect' => [ 'first option value', 'second option value', ] ]); {% endhighlight %} Mixing string and boolean values for a checkbox's value is not supported and may produce unexpected results. Field names ending in "[]" must be passed without the trailing square bracket characters, and must contain an array for its value. This allows submitting multiple values with the same name, consider: {% highlight php %} $I->submitForm('#my-form', [ 'field[]' => 'value', 'field[]' => 'another value', // 'field[]' is already a defined key ]); {% endhighlight %} The solution is to pass an array value: {% highlight php %} // this way both values are submitted $I->submitForm('#my-form', [ 'field' => [ 'value', 'another value', ] ]); {% endhighlight %} The `$button` parameter can be either a string, an array or an instance of Facebook\WebDriver\WebDriverBy. When it is a string, the button will be found by its "name" attribute. If $button is an array then it will be treated as a strict selector and a WebDriverBy will be used verbatim. For example, given the following HTML: {% highlight html %} {% endhighlight %} `$button` could be any one of the following: - 'submitButton' - ['name' => 'submitButton'] - WebDriverBy::name('submitButton') #### switchToFrame * `param string|null` $locator (name, CSS or XPath) * `return void` Switch to another frame on the page. Example: {% highlight html %} {% endhighlight %} {% highlight php %} switchToFrame("another_frame"); # switch to frame by CSS or XPath $I->switchToFrame("#fr1"); # switch to parent page $I->switchToFrame(); {% endhighlight %} #### switchToIFrame * `param string|null` $locator (name, CSS or XPath) * `return void` Switch to another iframe on the page. Example: {% highlight html %} <iframe name="another_frame" id="fr1" src="https://example.com"> {% endhighlight %} {% highlight php %} switchToIFrame("another_frame"); # switch to iframe by CSS or XPath $I->switchToIFrame("#fr1"); # switch to parent page $I->switchToIFrame(); {% endhighlight %} #### switchToNextTab * `param int` $offset * `return void` Switches to next browser tab. An offset can be specified. {% highlight php %} switchToNextTab(); // switch to 2nd next tab $I->switchToNextTab(2); {% endhighlight %} #### switchToPreviousTab * `param int` $offset * `return void` Switches to previous browser tab. An offset can be specified. {% highlight php %} switchToPreviousTab(); // switch to 2nd previous tab $I->switchToPreviousTab(2); {% endhighlight %} #### switchToWindow * `param ?string` $name * `return void` Switch to another window identified by name. The window can only be identified by name. If the $name parameter is blank, the parent window will be used. Example: {% highlight html %} {% endhighlight %} {% highlight php %} click("Open window"); # switch to another window $I->switchToWindow("another_window"); # switch to parent window $I->switchToWindow(); {% endhighlight %} If the window has no name, match it by switching to next active tab using `switchToNextTab` method. Or use native Selenium functions to get access to all opened windows: {% highlight php %} executeInSelenium(function (\Facebook\WebDriver\Remote\RemoteWebDriver $webdriver) { $handles=$webdriver->getWindowHandles(); $last_window = end($handles); $webdriver->switchTo()->window($last_window); }); {% endhighlight %} #### type * `param string` $text * `param int` $delay [sec] * `return void` Type in characters on active element. With a second parameter you can specify delay between key presses. {% highlight php %} click('#input'); // type text in active element $I->type('Hello world'); // type text with a 1sec delay between chars $I->type('Hello World', 1); {% endhighlight %} This might be useful when you an input reacts to typing and you need to slow it down to emulate human behavior. For instance, this is how Credit Card fields can be filled in. #### typeInPopup * `param string` $keys * `throws ModuleException` * `return void` Enters text into a native JavaScript prompt popup, as created by `window.prompt`. #### uncheckOption * `param ` $option * `return void` Unticks a checkbox. {% highlight php %} uncheckOption('#notify'); {% endhighlight %} #### unselectOption * `param string|array|WebDriverBy` $select * `param string|array|WebDriverBy` $option * `return void` Unselect an option in the given select box. #### wait * `param int|float` $timeout secs * `throws TestRuntimeException` * `return void` Wait for $timeout seconds. #### waitForElement * `param string|array|WebDriverBy` $element * `param int` $timeout seconds * `throws Exception` * `return void` Waits up to $timeout seconds for an element to appear on the page. If the element doesn't appear, a timeout exception is thrown. {% highlight php %} waitForElement('#agree_button', 30); // secs $I->click('#agree_button'); {% endhighlight %} #### waitForElementChange * `param string|array|WebDriverBy` $element * `param \Closure` $callback * `param int` $timeout * `throws ElementNotFound` * `return void` Waits up to $timeout seconds for the given element to change. Element "change" is determined by a callback function which is called repeatedly until the return value evaluates to true. {% highlight php %} waitForElementChange('#menu', function(WebDriverElement $el) { return $el->isDisplayed(); }, 100); {% endhighlight %} #### waitForElementClickable * `param string|array|WebDriverBy` $element * `param int` $timeout seconds * `throws Exception` * `return void` Waits up to $timeout seconds for the given element to be clickable. If element doesn't become clickable, a timeout exception is thrown. {% highlight php %} waitForElementClickable('#agree_button', 30); // secs $I->click('#agree_button'); {% endhighlight %} #### waitForElementNotVisible * `param string|array|WebDriverBy` $element * `param int` $timeout seconds * `throws Exception` * `return void` Waits up to $timeout seconds for the given element to become invisible. If element stays visible, a timeout exception is thrown. {% highlight php %} waitForElementNotVisible('#agree_button', 30); // secs {% endhighlight %} #### waitForElementVisible * `param string|array|WebDriverBy` $element * `param int` $timeout seconds * `throws Exception` * `return void` Waits up to $timeout seconds for the given element to be visible on the page. If element doesn't appear, a timeout exception is thrown. {% highlight php %} waitForElementVisible('#agree_button', 30); // secs $I->click('#agree_button'); {% endhighlight %} #### waitForJS * `param string` $script * `param int` $timeout seconds * `return void` Executes JavaScript and waits up to $timeout seconds for it to return true. In this example we will wait up to 60 seconds for all jQuery AJAX requests to finish. {% highlight php %} waitForJS("return $.active == 0;", 60); {% endhighlight %} #### waitForText * `param string` $text * `param int` $timeout seconds * `param null|string|array|WebDriverBy` $selector * `throws Exception` * `return void` Waits up to $timeout seconds for the given string to appear on the page. Can also be passed a selector to search in, be as specific as possible when using selectors. waitForText() will only watch the first instance of the matching selector / text provided. If the given text doesn't appear, a timeout exception is thrown. {% highlight php %} waitForText('foo', 30); // secs $I->waitForText('foo', 30, '.title'); // secs {% endhighlight %}