… | |
… | |
2 | |
2 | |
3 | AnyEvent::WebDriver - control browsers using the W3C WebDriver protocol |
3 | AnyEvent::WebDriver - control browsers using the W3C WebDriver protocol |
4 | |
4 | |
5 | =head1 SYNOPSIS |
5 | =head1 SYNOPSIS |
6 | |
6 | |
7 | # start geckodriver or any other w3c-compatible webdriver via the shell |
7 | # start geckodriver(chromedriver or any other webdriver via the shell |
8 | $ geckdriver -b myfirefox/firefox --log trace --port 4444 |
8 | $ geckodriver -b myfirefox/firefox --log trace --port 4444 |
|
|
9 | # chromedriver --port=4444 |
9 | |
10 | |
10 | # then use it |
11 | # then use it |
11 | use AnyEvent::WebDriver; |
12 | use AnyEvent::WebDriver; |
12 | |
13 | |
13 | # create a new webdriver object |
14 | # create a new webdriver object |
… | |
… | |
33 | ->key ("{Enter}") |
34 | ->key ("{Enter}") |
34 | ->perform; |
35 | ->perform; |
35 | |
36 | |
36 | =head1 DESCRIPTION |
37 | =head1 DESCRIPTION |
37 | |
38 | |
38 | WARNING: BEFORE VERSION 1.0, API CHANGES ARE LIKELY. |
|
|
39 | |
|
|
40 | This module aims to implement the L<W3C |
39 | This module aims to implement the L<W3C |
41 | WebDriver|https://www.w3.org/TR/webdriver1/> specification which is the |
40 | WebDriver|https://www.w3.org/TR/webdriver1/> specification which is the |
42 | standardised equivalent to the Selenium WebDriver API, which in turn aims |
41 | standardised equivalent to the Selenium WebDriver API, which in turn aims |
43 | at remotely controlling web browsers such as Firefox or Chromium. |
42 | at remotely controlling web browsers such as Firefox or Chromium. |
44 | |
|
|
45 | At the time of this writing, it was so brand new that I could only get |
|
|
46 | C<geckodriver> (for Firefox) to work, but that is expected to be fixed |
|
|
47 | very soon indeed. |
|
|
48 | |
43 | |
49 | One of the design goals of this module was to stay very close to the |
44 | One of the design goals of this module was to stay very close to the |
50 | language and words used in the WebDriver specification itself, so to make |
45 | language and words used in the WebDriver specification itself, so to make |
51 | most of this module, or, in fact, to make any reasonable use of this |
46 | most of this module, or, in fact, to make any reasonable use of this |
52 | module, you would need to refer to the W3C WebDriver recommendation, which |
47 | module, you would need to refer to the W3C WebDriver recommendation, which |
53 | can be found L<here|https://www.w3.org/TR/webdriver1/>: |
48 | can be found L<here|https://www.w3.org/TR/webdriver1/>: |
54 | |
49 | |
55 | https://www.w3.org/TR/webdriver1/ |
50 | https://www.w3.org/TR/webdriver1/ |
|
|
51 | |
|
|
52 | Mozilla's C<geckodriver> has had webdriver for a long time, while |
|
|
53 | C<chromedriver> only has basic and mostly undocumented webdriver support |
|
|
54 | since release 77. |
|
|
55 | |
|
|
56 | In debian GNU/Linux, you can install the C<firefoxdriver> or |
|
|
57 | C<chromium-driver> packages to get the firefox/chromium webdrivers, |
|
|
58 | respectively. |
56 | |
59 | |
57 | =head2 CONVENTIONS |
60 | =head2 CONVENTIONS |
58 | |
61 | |
59 | Unless otherwise stated, all delays and time differences in this module |
62 | Unless otherwise stated, all delays and time differences in this module |
60 | are represented as an integer number of milliseconds. |
63 | are represented as an integer number of milliseconds. |
… | |
… | |
164 | =item new AnyEvent::WebDriver key => value... |
167 | =item new AnyEvent::WebDriver key => value... |
165 | |
168 | |
166 | Create a new WebDriver object. Example for a remote WebDriver connection |
169 | Create a new WebDriver object. Example for a remote WebDriver connection |
167 | (the only type supported at the moment): |
170 | (the only type supported at the moment): |
168 | |
171 | |
169 | my $wd = new AnyEvent::WebDriver host => "localhost", port => 4444; |
172 | my $wd = new AnyEvent::WebDriver endpoint => "http://localhost:4444"; |
170 | |
173 | |
171 | Supported keys are: |
174 | Supported keys are: |
172 | |
175 | |
173 | =over |
176 | =over |
174 | |
177 | |
… | |
… | |
297 | =back |
300 | =back |
298 | |
301 | |
299 | =head2 SIMPLIFIED API |
302 | =head2 SIMPLIFIED API |
300 | |
303 | |
301 | This section documents the simplified API, which is really just a very |
304 | This section documents the simplified API, which is really just a very |
302 | thin wrapper around the WebDriver protocol commands. They all block (using |
305 | thin wrapper around the WebDriver protocol commands. They all block the |
303 | L<AnyEvent> condvars) the caller until the result is available, so must |
306 | caller until the result is available (using L<AnyEvent> condvars), so must |
304 | not be called from an event loop callback - see L<EVENT BASED API> for an |
307 | not be called from an event loop callback - see L<EVENT BASED API> for an |
305 | alternative. |
308 | alternative. |
306 | |
309 | |
307 | The method names are pretty much taken directly from the W3C WebDriver |
310 | The method names are pretty much taken directly from the W3C WebDriver |
308 | specification, e.g. the request documented in the "Get All Cookies" |
311 | specification, e.g. the request documented in the "Get All Cookies" |
… | |
… | |
330 | On success, C<< $wd->{sid} >> is set to the session ID, and C<< |
333 | On success, C<< $wd->{sid} >> is set to the session ID, and C<< |
331 | $wd->{capabilities} >> is set to the returned capabilities. |
334 | $wd->{capabilities} >> is set to the returned capabilities. |
332 | |
335 | |
333 | Simple example of creating a WebDriver object and a new session: |
336 | Simple example of creating a WebDriver object and a new session: |
334 | |
337 | |
335 | my $wd = new AnyEvent::Selenium endpoint => "http://localhost:4545"; |
338 | my $wd = new AnyEvent::WebDriver endpoint => "http://localhost:4444"; |
336 | $wd->new_session ({}); |
339 | $wd->new_session ({}); |
337 | |
340 | |
338 | Real-world example with capability negotiation: |
341 | Real-world example with capability negotiation: |
339 | |
342 | |
340 | $wd->new_session ({ |
343 | $wd->new_session ({ |
… | |
… | |
347 | firstMatch => [ |
350 | firstMatch => [ |
348 | { |
351 | { |
349 | browserName => "firefox", |
352 | browserName => "firefox", |
350 | "moz:firefoxOptions" => { |
353 | "moz:firefoxOptions" => { |
351 | binary => "firefox/firefox", |
354 | binary => "firefox/firefox", |
352 | args => ["-devtools"], |
355 | args => ["-devtools", "-headless"], |
353 | prefs => { |
356 | prefs => { |
354 | "dom.webnotifications.enabled" => \0, |
357 | "dom.webnotifications.enabled" => \0, |
355 | "dom.push.enabled" => \0, |
358 | "dom.push.enabled" => \0, |
356 | "dom.disable_beforeunload" => \1, |
359 | "dom.disable_beforeunload" => \1, |
357 | "browser.link.open_newwindow" => 3, |
360 | "browser.link.open_newwindow" => 3, |
… | |
… | |
360 | "dom.disable_open_during_load" => \1, |
363 | "dom.disable_open_during_load" => \1, |
361 | }, |
364 | }, |
362 | }, |
365 | }, |
363 | }, |
366 | }, |
364 | { |
367 | { |
|
|
368 | browserName => "chrome", |
|
|
369 | "goog:chromeOptions" => { |
|
|
370 | binary => "/bin/chromium", |
|
|
371 | args => ["--no-sandbox", "--headless"], |
|
|
372 | prefs => { |
|
|
373 | # ... |
|
|
374 | }, |
|
|
375 | }, |
|
|
376 | }, |
|
|
377 | { |
365 | # generic fallback |
378 | # generic fallback |
366 | }, |
379 | }, |
367 | ], |
380 | ], |
368 | |
381 | |
369 | }, |
382 | }, |
… | |
… | |
371 | |
384 | |
372 | Firefox-specific capability documentation can be found L<on |
385 | Firefox-specific capability documentation can be found L<on |
373 | MDN|https://developer.mozilla.org/en-US/docs/Web/WebDriver/Capabilities>, |
386 | MDN|https://developer.mozilla.org/en-US/docs/Web/WebDriver/Capabilities>, |
374 | Chrome-specific capability documentation might be found |
387 | Chrome-specific capability documentation might be found |
375 | L<here|http://chromedriver.chromium.org/capabilities>, but the latest |
388 | L<here|http://chromedriver.chromium.org/capabilities>, but the latest |
376 | release at the time of this writing has effectively no WebDriver support |
389 | release at the time of this writing (chromedriver 77) has essentially no |
377 | at all, and canary releases are not freely downloadable. |
390 | webdriver documentation about chrome capabilities. |
378 | |
391 | |
379 | If you have URLs for Safari/IE/Edge etc. capabilities, feel free to tell |
392 | If you have URLs for Safari/IE/Edge etc. capabilities, feel free to tell |
380 | me about them. |
393 | me about them. |
381 | |
394 | |
382 | =cut |
395 | =cut |
383 | |
396 | |
384 | sub new_session_ { |
397 | sub new_session_ { |
385 | my ($self, $kv, $cb) = @_; |
398 | my ($self, $kv, $cb) = @_; |
|
|
399 | |
|
|
400 | $kv->{capabilities} ||= {}; # required by protocol |
386 | |
401 | |
387 | local $self->{_ep} = "$self->{endpoint}/"; |
402 | local $self->{_ep} = "$self->{endpoint}/"; |
388 | $self->post_ (session => $kv, sub { |
403 | $self->post_ (session => $kv, sub { |
389 | my ($status, $res) = @_; |
404 | my ($status, $res) = @_; |
390 | |
405 | |
… | |
… | |
1054 | |
1069 | |
1055 | Create a screenshot, returning it as a PNG image in a C<data:> URL. |
1070 | Create a screenshot, returning it as a PNG image in a C<data:> URL. |
1056 | |
1071 | |
1057 | =item $wd->take_element_screenshot ($element) |
1072 | =item $wd->take_element_screenshot ($element) |
1058 | |
1073 | |
1059 | Accept a simple dialog, if present. |
1074 | Similar to C<take_screenshot>, but only takes a screenshot of the bounding |
|
|
1075 | box of a single element. |
1060 | |
1076 | |
1061 | =cut |
1077 | =cut |
1062 | |
1078 | |
1063 | sub take_screenshot_ { |
1079 | sub take_screenshot_ { |
1064 | $_[0]->get_ (screenshot => $_[1]); |
1080 | $_[0]->get_ (screenshot => $_[1]); |
… | |
… | |
1673 | |
1689 | |
1674 | =head1 HISTORY |
1690 | =head1 HISTORY |
1675 | |
1691 | |
1676 | This module was unintentionally created (it started inside some quickly |
1692 | This module was unintentionally created (it started inside some quickly |
1677 | hacked-together script) simply because I couldn't get the existing |
1693 | hacked-together script) simply because I couldn't get the existing |
1678 | C<Selenium::Remote::Driver> module to work, ever, despite multiple |
1694 | C<Selenium::Remote::Driver> module to work reliably, ever, despite |
1679 | attempts over the years and trying to report multiple bugs, which have |
1695 | multiple attempts over the years and trying to report multiple bugs, which |
1680 | been completely ignored. It's also not event-based, so, yeah... |
1696 | have been completely ignored. It's also not event-based, so, yeah... |
1681 | |
1697 | |
1682 | =head1 AUTHOR |
1698 | =head1 AUTHOR |
1683 | |
1699 | |
1684 | Marc Lehmann <schmorp@schmorp.de> |
1700 | Marc Lehmann <schmorp@schmorp.de> |
1685 | http://anyevent.schmorp.de |
1701 | http://anyevent.schmorp.de |