WebDriver2 — README
WebDriver2
WebDriver level 2 bindings implementing W3C's specification. Current implementation status is documented below.
Usage
Using a driver directly
To use a driver directly for driver-level endpoint commands, request a WD2::Component::Driver
with Provider.get-driver: $browser, :$port. The test class
will need to specify the browser and port upon instantiation:
use WD2;
use WD2::Component::Driver;
my WD2::Component::Driver:D $driver =
Provider.get-driver: 'chrome', port => 9515;Most commands are Session or Element endpoints, though:
use WD2::Component::Session;
my WD2::Component::Session:D $session =
$driver.new-session: %optional-capabilities-options;
$session.navigate-to: $url-as-Str; # can be file path or web addressIf no capabilities are given, the minimum, empty default will be supplied: { capabilities => { } }. Please see specification(s) for capability availability and format.
Some Element endpoints:
use WD2::Component::Element;
use WD2::Locators;
my WD2::Component::Element:D $element =
$session.find-element: By.id: 'identifier';
$element.click;In addition to locating Elements by ID, the standard locators are available:
$element = $session.find-element: By.tag: 'input';
$element = $session.find-element: By.css: 'body > div.head';
# also By.link-text, By.partial-link-text, By.xpathSee below for status.
When finished:
$session.delete;Convenience Methods and Routines, Test Template
Methods
Some Session and Element convenience methods have been provided that are not part of the WebDriver2 specification. They are listed below at the end of the implementation status section.
Wait Routines
Since waiting for a condition to be true before moving to the next step is useful, several routines that poll state have also been provided:
# relevant imports and declarations completed above
use WD2::Wait::Common :ALL;
my Duration:D $duration = Duration.new: 5;
my Duration:D $interval = Duration.new: 1/10;
my &wait-present = present $session, $locator, :$duration, :$interval, :soft;
# do something...
# then wait for 5 seconds, polling every .1 second,
# for an element to appear; don't throw an exception if it doesn't
my WD2::Component::Element $element = wait-present;
$element = $session.find-element: By.id: 'gets-removed';
my &wait-stale = stale $element;
# do something...
# then wait for the element to be removed using default values;
# throw Timeout exception if it isn't
wait-stale;
my WD2::Component::Element $updatable =
$session.find-element: By.id: 'updatable';
my WD2::Component::Element $input =
$session.find-element: By.id: 'text-input';
my WD2::Component::Element $updater =
$session.find-element: By.tag: 'button';
my &wait-updated = text-to-be $updatable, 'new text';
$input.send-keys: 'new text';
$updater.click;
wait-updated;List with implementation status given below the endpoints table.
WD2::Test::Template
Test classes can implement the WD2::Test::Template role to avoid some boilerplate.
The example test included with the distribution is explained here.
From xt/lib/Example.rakumod:
use WD2::Test::Template;
use WD2::Locators;
use WD2::Wait::Common :presence;
class Example does WD2::Test::Template {
my IO::Path:D $html-file =
$*PROGRAM.parent.sibling( 'content' ).add: 'test.html';
has Str:D $.name = 'example';
has Str:D $.description = 'example test description';
has Int:D $.plan = 1;
# included so that when run as part of the test suite,
# it will skip testing if the user has not requested
# driver testing. This Should Be Omitted for normal
# user tests.
method init {
if %*ENV<DRIVER_TESTING> {
self.WD2::Test::Template::init;
} else {
self.diag: 'DRIVER_TESTING was not set';
self.pass: 'DRIVER_TESTING was not set';
self.done-testing;
exit;
}
}
method test {
$!session.navigate-to: 'file://' ~ $html-file.absolute;
self.is: 'title', 'test', $!session.title;
my Duration:D $duration = Duration.new: 5;
my Duration:D $interval = Duration.new: 1/10;
my By:D $locator = By.id: 'DNE';
my &wait-present = present $!session, $locator, :$duration, :$interval, :soft;
self.lives-ok: 'lives but False for element that DNE', {
my WD2::Component::Element $element = wait-present;
self.nok: 'no element found', $element;
}
}
}Implementing classes need to provide a test name and description and override the test method. The
self.is: ... is provided by the WD2::Test::Adapter role via WD2::Test::Template (template role is the only import necessary). That and the related methods delegate to the Test routines but will run a handler if a test fails. The default handler takes a screenshot when called (see below for details and how to suppress this behavior). Note that the order of arguments are the reverse of the Test routines. This leaves the final position available for calculating the actual value.
Once such a class is written, it can be used in a test script.
From xt/05-test-template/example.rakutest:
use lib <lib xt/lib>;
use WD2::Test::Template;
use Example;
constant &MAIN = driver-test Example;The script is just a wrapper that runs the test class. The driver-test sub is from the
WD2::Test::Template compunit (hence the first import). It takes the test class and
returns a sub suitable for use as a MAIN. The options provided are:
In addition, any extra named arguments will be passed to the test class, so that instance variables can be built from them.
TODO
cover all implemented endpoints with unit tests
add Rakudoc
browser support
implement the rest of the endpoints
Feedback
Suggestions, design recommendations, and feature requests welcome.
Safari Quirks
is displayed ( optional endpoint ) is not supported ( by Apple's design )
safaridriver ignores interactions with disabled elements instead of throwing the spec'd Element Not Interactable exception ( by Apple's design )
switch to parent frame switches to default content ( bug - needs investigation )
Implementation Status
X - Unsupported
"" - Planned
I - Implemented
✓ - Implemented and tested
Locator Status
"" - Planned
I - Implemented
✓ - Passing
Wait Status
"" - Planned
I - Implemented
✓ - Passing