Rand Stats

WebDriver2

zef:zjmarlow

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 address

If 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.xpath

See 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 &wait-present = present $session, $locator, duration => 5, interval => 1/10, :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
	&wait-present();
	
	my WD2::Component::Element $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;
	
	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;
		}
	}

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 (no additional 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:

optionnotes
Str $browser?if none is provided and there is a browser file in the CWD, its value will be read and used. otherwise the test will fail
Str:D :$host = '127.0.0.1'
Int:D :$port = 95159515 was the default for chromedriver and edgedriver. it will likely need to be supplied when using firefox or safari, or if chromedriver or edgedriver is using a random port. the port can also be set when starting the driver (as opposed to or in addition to the script)
IO::Path(Str:D) :$test-root = 'xt'.IOcurrently unused
Int:D :$close-delay = 3if set negative, the session will be left open when the script completes or if there is a fatal exception (except failed Session creation - in which case there will be no session to leave open). If the session is left open, the session-id will be given on STDOUT so that it can be used to close the session gracefully later. E.g., by using the provided bin/close-session.raku script: close-session --host=127.0.0.1 --port=9515 <browser>(required) <session-id>(required)
Bool:D :$no-auto-ss = FalseBy default, in addition to screenshots Tests explicitly request, screenshots are taken anytime there is a failure (if using the provided WD2::Test::Adapter methods) or exception. set to suppress this behavior
Str:D :$debug(:$debug-level) = 'WARN'valid values: OFF, ERR, WARN, Info, trace, extra. debugging output has not been incorporated, yet

In addition, any extra named arguments will be passed to the test class, so that instance variables can be built from them.

TODO

Feedback

Suggestions, design recommendations, and feature requests welcome.

Implementation Status

 WindowsWindows / LinuxMacOS 
endpointedgechromefirefoxsafarimethod
new sessionI✓ (W), I (L)  $driver.new-session: { capabilities => { ... } }
delete sessionI✓ (W), I (L)  $session.delete
statusII  $driver.status
get timeoutsII  $session.get-timeouts
set timeoutsII  $session.set-timeouts: Int $script, Int $page-load, Int $implicit
navigate toI✓ (W), I (L)  $session.navigate-to: Str $url
get current urlII  $session.current-url
backII  $session.back
forwardII  $session.forward
refreshII  $session.refresh
get titleI✓ (W), I (L)  $session.title
get window handleII  $session.get-window-handle
close windowII  $session.close-window
switch to windowII  $session.switch-to-window: $handle
get window handlesII  $session.window-handles
new windowII  $session.new-window: Str $type? where <tab window>.any
switch to frameI✓ (W), I (L)  $session.switch-to: Int $frame-id $frame-element.switch-to
switch to parent frameII  $session.switch-to-parent-frame
get window rect    $session.get-window-rect
set window rectII  $session.set-window-rect: Int $width, Int $height, Int $x, Int $y
maximize windowII  $session.maximize-window
minimize windowII  $session.minimize-window
fullscreen windowII  $session.fullscreen-window
get active elementII  $session.active-element
get element shadow rootII  $element.shadow-root
find elementI✓ (W), I (L)  $session.find-element: By $locator
find elementsII  $session.find-elements: By $locator
find element from elementII  $element.find-element: By $locator
find elements from elementII  $element.find-elements: By $locator
find element from shadow rootII  $shadow-root.find-element: By $locator
find elements from shadow rootII  $shadow-root.find-elements: By $locator
is element selectedII  $element.is-element-selected
get element attributeI✓ (W), I (L)  $element.attribute: Str $name
get element propertyII  $element.property: Str $name
get element css valueII  $element.css-value: Str $css-prop
get element textII  $element.text
get element tag nameII  $element.tag-name
get element rectII  $element.rect
is element enabledII  $element.is-enabled
get computed roleII  $element.computed-role
get computed labelII  $element.computed-label
element clickII  $element.click
element clearII  $element.clear
element send keysII  $element.send-keys: Str $text
get page sourceII  $session.page-source
execute scriptII  $session.execute-script: Str $scr, @args
execute async scriptII  $session.execute-async-script: Str $scr, @args
get all cookiesII  $session.get-all-cookies
get named cookieII  $session.get-named-cookie: Str $name
add cookieII  $session.add-cookie: %cookie-spec

keys:

name* value* path domain secure httpOnly expiry sameSite

* required

delete cookieII  $session.delete-cookie: Str $name
delete all cookiesII  $session.delete-all-cookies
perform actionsNYINYINYINYI$session.perform-actions
release actionsNYINYINYINYI$session.release-actions
dismiss alertII  $session.dismiss-alert
accept alertI✓ (W), I (L)  $session.accept-alert
get alert textI✓ (W), I (L)  $session.alert-text
send alert textII  $session.send-alert-text: Str $text
take screenshotII  $session.take-screenshot
take element screenshotII  $element.take-element-screenshot
print pageII  $session.print-page
is-displayed ( optional endpoint )II  $element.is-displayed
present ( convenience method - not spec'd )II  $session.present: By $locator; $element.present: By $locator
id ( convenience method - not spec'd )II  $element.id
top ( convenience method - not spec'd )II  $session.top
switch-to ( convenience method - not spec'd )II  $frame-element.switch-to
select ( convenience method - not spec'd )II  $select-element.select: Str $option-text
selected-option ( convenience method - not spec'd )II  $select-element.selected-option
selected-value ( convenience method - not spec'd )II  $select-element.selected-value

Locator Status

locatorstatus
By.id
By.tag
By.css
By.xpath
By.link-text
By.partial-link-text

Wait Status

routinestatusimport withnotes
base-waituse WD2::Wait :basebase wait routine. can be used to wait for arbitrary conditions
basic-opIuse WD2::Wait :basebasic wait - use operation's return value directly for truthiness
basic-trueIuse WD2::Wait :basicwait for identically True value
basic-so-trueIuse WD2::Wait :basicwait for truthiness
basic-to-trueIuse WD2::Wait :basicwait for falsiness and alter return value to be the opposite Bool value
basic-eqIuse WD2::Wait :basicwait for a specific value using eq
basic-equalsIuse WD2::Wait :basicwait for a specific value using ==
basic-acceptsIuse WD2::Wait :basicwait for a specific value using ~~
throwableIuse WD2::Wait :throwreturn (non-Falure) Exception, otherwise, the return value. used to build waits but is not one itself
expect-throwIuse WD2::Wait :throw$throwable-return.isa: Exception ?? False !! $result but True. used to build waits but is not one itself
expect-throw-typeIuse WD2::Wait :throwrethrow wrong type; return the type if it was expected; otherwise, return Error-Code (falsy). used to build waits but is not one itself
no-throwIuse WD2::Wait :throw$throwable-return.isa: Exception ?? $throwable-return but False !! $throwable-return. used to build waits but is not one itself
no-throw-typeIuse WD2::Wait :throwrethrow anything unexpected; return the expected type but False. used to build waits but is not one itself
presentuse WD2::Wait::Common :presence
absentuse WD2::Wait::Common :presence
staleuse WD2::Wait::Common :presence
displayeduse WD2::Wait::Common :presence
hiddenuse WD2::Wait::Common :presence
value-not-emptyIuse WD2::Wait::Common :value
value-to-eqIuse WD2::Wait::Common :value
value-to-beIuse WD2::Wait::Common :value
text-to-beIuse WD2::Wait::Common :value
title-to-beIuse WD2::Wait::Common :value