AnyEvent-WebDriver
view release on metacpan or search on metacpan
WebDriver.pm view on Meta::CPAN
$wd->element_send_keys ($searchbox => "free software");
$wd->element_click ($wd->find_element (css => 'input[type="submit"]'));
# session gets autodeleted by default, so wait a bit
sleep 10;
# this is an example of an action sequence
$wd->actions
->move ($wd->find_element (...), 40, 5)
->click
->type ("some text")
->key ("{Enter}")
->perform;
=head1 DESCRIPTION
This module aims to implement the L<W3C
WebDriver|https://www.w3.org/TR/webdriver1/> specification which is the
standardised equivalent to the Selenium WebDriver API, which in turn aims
at remotely controlling web browsers such as Firefox or Chromium.
One of the design goals of this module was to stay very close to the
language and words used in the WebDriver specification itself, so to make
most of this module, or, in fact, to make any reasonable use of this
module, you would need to refer to the W3C WebDriver recommendation, which
can be found L<here|https://www.w3.org/TR/webdriver1/>:
https://www.w3.org/TR/webdriver1/
Mozilla's C<geckodriver> has had webdriver support for a long time, while
C<chromedriver> only has basic and mostly undocumented webdriver support
as of release 77.
In Debian GNU/Linux, you can install the chromedriver for chromium
via the C<chromium-driver> package. Unfortunately, there is no
(working) package for geckodriver, but you can download it from
L<github|https://github.com/mozilla/geckodriver/releases>.
=head2 CONVENTIONS
Unless otherwise stated, all delays and time differences in this module
are represented as an integer number of milliseconds, which is perhaps
surprising to users of my other modules but is what the WebDriver spec
uses.
=cut
package AnyEvent::WebDriver;
use common::sense;
use Carp ();
use AnyEvent ();
use AnyEvent::HTTP ();
our $VERSION = '1.2';
our $WEB_ELEMENT_IDENTIFIER = "element-6066-11e4-a52e-4f735466cecf";
our $WEB_WINDOW_IDENTIFIER = "window-fcc6-11e5-b4f8-330a88ab9d7f";
our $WEB_FRAME_IDENTIFIER = "frame-075b-4da1-b6ba-e579c2d3230a";
my $json = eval { require JSON::XS; JSON::XS:: } || do { require JSON::PP; JSON::PP:: };
$json = $json->new->utf8;
$json->boolean_values (0, 1)
if $json->can ("boolean_values");
sub _decode_base64 {
require MIME::Base64;
MIME::Base64::decode_base64 (shift)
}
sub req_ {
my ($self, $method, $ep, $body, $cb) = @_;
AnyEvent::HTTP::http_request $method => "$self->{_ep}$ep",
body => $body,
$self->{persistent} ? (persistent => 1) : (),
$self->{proxy} eq "default" ? () : (proxy => $self->{proxy}),
timeout => $self->{timeout},
headers => { "content-type" => "application/json; charset=utf-8", "cache-control" => "no-cache" },
sub {
my ($res, $hdr) = @_;
$res = eval { $json->decode ($res) };
$hdr->{Status} = 500 unless exists $res->{value};
$cb->($hdr->{Status}, $res->{value});
}
;
}
sub get_ {
my ($self, $ep, $cb) = @_;
$self->req_ (GET => $ep, undef, $cb)
}
sub post_ {
my ($self, $ep, $data, $cb) = @_;
$self->req_ (POST => $ep, $json->encode ($data || {}), $cb)
}
sub delete_ {
my ($self, $ep, $cb) = @_;
$self->req_ (DELETE => $ep, "", $cb)
}
sub AUTOLOAD {
our $AUTOLOAD;
$_[0]->isa (__PACKAGE__)
or Carp::croak "$AUTOLOAD: no such function";
(my $name = $AUTOLOAD) =~ s/^.*://;
my $name_ = "$name\_";
WebDriver.pm view on Meta::CPAN
}
sub refresh_ {
$_[0]->post_ (refresh => undef, $_[1]);
}
=item $title = $wd->get_title
Returns the current document title.
=cut
sub get_title_ {
$_[0]->get_ (title => $_[1]);
}
=back
=head3 COMMAND CONTEXTS
=over
=cut
=item $handle = $wd->get_window_handle
Returns the current window handle.
=item $wd->close_window
Closes the current browsing context.
=item $wd->switch_to_window ($handle)
Changes the current browsing context to the given window.
=cut
sub get_window_handle_ {
$_[0]->get_ (window => $_[1]);
}
sub close_window_ {
$_[0]->delete_ (window => $_[1]);
}
sub switch_to_window_ {
$_[0]->post_ (window => { handle => "$_[1]" }, $_[2]);
}
=item $handles = $wd->get_window_handles
Return the current window handles as an array-ref of handle IDs.
=cut
sub get_window_handles_ {
$_[0]->get_ ("window/handles" => $_[1]);
}
=item $handles = $wd->switch_to_frame ($frame)
Switch to the given frame identified by C<$frame>, which must be either
C<undef> to go back to the top-level browsing context, an integer to
select the nth subframe, or an element object.
=cut
sub switch_to_frame_ {
$_[0]->post_ (frame => { id => "$_[1]" }, $_[2]);
}
=item $handles = $wd->switch_to_parent_frame
Switch to the parent frame.
=cut
sub switch_to_parent_frame_ {
$_[0]->post_ ("frame/parent" => undef, $_[1]);
}
=item $rect = $wd->get_window_rect
Return the current window rect(angle), e.g.:
$rect = $wd->get_window_rect
=> { height => 1040, width => 540, x => 0, y => 0 }
=item $wd->set_window_rect ($rect)
Sets the window rect(angle), e.g.:
$wd->set_window_rect ({ width => 780, height => 560 });
$wd->set_window_rect ({ x => 0, y => 0, width => 780, height => 560 });
=cut
sub get_window_rect_ {
$_[0]->get_ ("window/rect" => $_[1]);
}
sub set_window_rect_ {
$_[0]->post_ ("window/rect" => $_[1], $_[2]);
}
=item $wd->maximize_window
=item $wd->minimize_window
=item $wd->fullscreen_window
Changes the window size by either maximising, minimising or making it
fullscreen. In my experience, this will timeout if no window manager is
running.
=cut
sub maximize_window_ {
$_[0]->post_ ("window/maximize" => undef, $_[1]);
}
sub minimize_window_ {
$_[0]->post_ ("window/minimize" => undef, $_[1]);
}
sub fullscreen_window_ {
$_[0]->post_ ("window/fullscreen" => undef, $_[1]);
}
=back
=head3 ELEMENT RETRIEVAL
To reduce typing and memory strain, the element finding functions accept
some shorter and hopefully easier to remember aliases for the standard
locator strategy values, as follows:
Alias Locator Strategy
css css selector
( run in 0.434 second using v1.01-cache-2.11-cpan-df04353d9ac )