… | |
… | |
23 | $wd->element_click ($wd->find_element ("css selector" => 'input[type="submit"]')); |
23 | $wd->element_click ($wd->find_element ("css selector" => 'input[type="submit"]')); |
24 | |
24 | |
25 | sleep 10; |
25 | sleep 10; |
26 | |
26 | |
27 | =head1 DESCRIPTION |
27 | =head1 DESCRIPTION |
|
|
28 | |
|
|
29 | WARNING: THE API IS NOT GUARANTEED TO BE STABLE UNTIL VERSION 1.0. |
28 | |
30 | |
29 | This module aims to implement the W3C WebDriver specification which is the |
31 | This module aims to implement the W3C WebDriver specification which is the |
30 | standardised equivalent to the Selenium WebDriver API., which in turn aims |
32 | standardised equivalent to the Selenium WebDriver API., which in turn aims |
31 | at remotely controlling web browsers such as Firefox or Chromium. |
33 | at remotely controlling web browsers such as Firefox or Chromium. |
32 | |
34 | |
… | |
… | |
50 | package AnyEvent::WebDriver; |
52 | package AnyEvent::WebDriver; |
51 | |
53 | |
52 | use common::sense; |
54 | use common::sense; |
53 | |
55 | |
54 | use Carp (); |
56 | use Carp (); |
55 | use JSON::XS (); |
|
|
56 | use AnyEvent (); |
57 | use AnyEvent (); |
57 | use AnyEvent::HTTP (); |
58 | use AnyEvent::HTTP (); |
58 | |
59 | |
59 | our $VERSION = 0.2; |
60 | our $VERSION = 0.2; |
60 | |
61 | |
61 | our $WEB_ELEMENT_IDENTIFIER = "element-6066-11e4-a52e-4f735466cecf"; |
62 | our $WEB_ELEMENT_IDENTIFIER = "element-6066-11e4-a52e-4f735466cecf"; |
62 | |
63 | |
63 | my $json = JSON::XS->new |
64 | my $json = eval { require JSON::XS; JSON::XS:: } || do { require JSON::PP; JSON::PP:: }; |
64 | ->utf8; |
65 | $json = $json->new->utf8; |
65 | |
66 | |
66 | $json->boolean_values (0, 1) |
67 | $json->boolean_values (0, 1) |
67 | if $json->can ("boolean_values"); |
68 | if $json->can ("boolean_values"); |
68 | |
69 | |
69 | sub req_ { |
70 | sub req_ { |
… | |
… | |
112 | (my $name = $AUTOLOAD) =~ s/^.*://; |
113 | (my $name = $AUTOLOAD) =~ s/^.*://; |
113 | |
114 | |
114 | my $name_ = "$name\_"; |
115 | my $name_ = "$name\_"; |
115 | |
116 | |
116 | defined &$name_ |
117 | defined &$name_ |
117 | or Carp::croak "AUTOLOAD: no such method"; |
118 | or Carp::croak "$AUTOLOAD: no such method"; |
118 | |
119 | |
119 | my $func_ = \&$name_; |
120 | my $func_ = \&$name_; |
120 | |
121 | |
121 | *$name = sub { |
122 | *$name = sub { |
122 | $func_->(@_, my $cv = AE::cv); |
123 | $func_->(@_, my $cv = AE::cv); |
… | |
… | |
212 | |
213 | |
213 | sub actions { |
214 | sub actions { |
214 | AnyEvent::WebDriver::Actions->new (wd => $_[0]) |
215 | AnyEvent::WebDriver::Actions->new (wd => $_[0]) |
215 | } |
216 | } |
216 | |
217 | |
|
|
218 | =item $sessionstring = $wd->save_session |
|
|
219 | |
|
|
220 | Save the current session in a string so it can be restored load with |
|
|
221 | C<load_session>. Note that only the session data itself is stored |
|
|
222 | (currently the session id and capabilities), not the endpoint information |
|
|
223 | itself. |
|
|
224 | |
|
|
225 | The main use of this function is in conjunction with disabled |
|
|
226 | C<autodelete>, to save a session to e.g., and restore it later. It could |
|
|
227 | presumably used for other applications, suhc as using the same sssion from |
|
|
228 | multiple processes and so on. |
|
|
229 | |
|
|
230 | =item $wd->load_session ($sessionstring) |
|
|
231 | |
|
|
232 | =item $wd->set_session ($sessionid, $capabilities) |
|
|
233 | |
|
|
234 | Starts using the given session, as identified by |
|
|
235 | C<$sessionid>. C<$capabilities> should be the original session |
|
|
236 | capabilities, although the current version of this module does not make |
|
|
237 | any use of it. |
|
|
238 | |
|
|
239 | The C<$sessionid> is stored in C<< $wd->{sid} >> (and could be fetched |
|
|
240 | form there for later use), while the capabilities are stored in C<< |
|
|
241 | $wd->{capabilities} >>. |
|
|
242 | |
|
|
243 | =cut |
|
|
244 | |
|
|
245 | sub save_session { |
|
|
246 | my ($self) = @_; |
|
|
247 | |
|
|
248 | $json->encode ([1, $self->{sid}, $self->{capabilities}]); |
|
|
249 | } |
|
|
250 | |
|
|
251 | sub load_session { |
|
|
252 | my ($self, $session) = @_; |
|
|
253 | |
|
|
254 | $session = $json->decode ($session); |
|
|
255 | |
|
|
256 | $session->[0] == 1 |
|
|
257 | or Carp::croak "AnyEvent::WebDriver::load_session: session corrupted or from different version"; |
|
|
258 | |
|
|
259 | $self->set_session ($session->[1], $session->[2]); |
|
|
260 | } |
|
|
261 | |
|
|
262 | sub set_session { |
|
|
263 | my ($self, $sid, $caps) = @_; |
|
|
264 | |
|
|
265 | $self->{sid} = $sid; |
|
|
266 | $self->{capabilities} = $caps; |
|
|
267 | |
|
|
268 | $self->{_ep} = "$self->{endpoint}/session/$self->{sid}/"; |
|
|
269 | } |
|
|
270 | |
217 | =back |
271 | =back |
218 | |
272 | |
219 | =head2 SIMPLIFIED API |
273 | =head2 SIMPLIFIED API |
220 | |
274 | |
221 | This section documents the simplified API, which is really just a very |
275 | This section documents the simplified API, which is really just a very |
… | |
… | |
248 | successfully, and only one session can be created per WebDriver object. |
302 | successfully, and only one session can be created per WebDriver object. |
249 | |
303 | |
250 | On success, C<< $wd->{sid} >> is set to the session ID, and C<< |
304 | On success, C<< $wd->{sid} >> is set to the session ID, and C<< |
251 | $wd->{capabilities} >> is set to the returned capabilities. |
305 | $wd->{capabilities} >> is set to the returned capabilities. |
252 | |
306 | |
|
|
307 | Simple example of creatring a WebDriver object and a new session: |
|
|
308 | |
253 | my $wd = new AnyEvent::Selenium endpoint => "http://localhost:4545"; |
309 | my $wd = new AnyEvent::Selenium endpoint => "http://localhost:4545"; |
|
|
310 | $wd->new_session ({}); |
|
|
311 | |
|
|
312 | Real-world example with capability negotiation: |
254 | |
313 | |
255 | $wd->new_session ({ |
314 | $wd->new_session ({ |
256 | capabilities => { |
315 | capabilities => { |
|
|
316 | alwaysMatch => { |
257 | pageLoadStrategy => "normal", |
317 | pageLoadStrategy => "eager", |
|
|
318 | unhandledPromptBehavior => "dismiss", |
|
|
319 | }, |
|
|
320 | firstMatch => [ |
|
|
321 | { |
|
|
322 | browserName => "firefox", |
|
|
323 | "moz:firefoxOptions" => { |
|
|
324 | binary => "firefox/firefox", |
|
|
325 | args => ["-devtools"], |
|
|
326 | prefs => { |
|
|
327 | "dom.webnotifications.enabled" => \0, |
|
|
328 | }, |
|
|
329 | }, |
|
|
330 | }, |
|
|
331 | { |
|
|
332 | # generic fallback |
|
|
333 | }, |
|
|
334 | ], |
|
|
335 | |
258 | }. |
336 | }, |
259 | }); |
337 | }); |
|
|
338 | |
|
|
339 | Firefox-specific capability documentation can be found L<on |
|
|
340 | MDN|https://developer.mozilla.org/en-US/docs/Web/WebDriver/Capabilities>, |
|
|
341 | Chrome-specific capability documentation might be found |
|
|
342 | L<here|http://chromedriver.chromium.org/capabilities>, but the latest |
|
|
343 | release at the time of this writing has effectively no WebDriver support |
|
|
344 | at all, and canary releases are not freely downloadable. |
|
|
345 | |
|
|
346 | If you have URLs for Safari/IE/Edge etc. capabilities, feel free to tell |
|
|
347 | me about them. |
260 | |
348 | |
261 | =cut |
349 | =cut |
262 | |
350 | |
263 | sub new_session_ { |
351 | sub new_session_ { |
264 | my ($self, $kv, $cb) = @_; |
352 | my ($self, $kv, $cb) = @_; |
265 | |
353 | |
266 | local $self->{_ep} = "$self->{endpoint}/"; |
354 | local $self->{_ep} = "$self->{endpoint}/"; |
267 | $self->post_ (session => $kv, sub { |
355 | $self->post_ (session => $kv, sub { |
268 | my ($status, $res) = @_; |
356 | my ($status, $res) = @_; |
269 | |
357 | |
|
|
358 | exists $res->{capabilities} |
|
|
359 | or $status = "500"; # blasted chromedriver |
|
|
360 | |
|
|
361 | $self->set_session ($res->{sessionId}, $res->{capabilities}); |
270 | if ($status eq "200") { |
362 | if $status eq "200"; |
271 | $self->{sid} = $res->{sessionId}; |
|
|
272 | $self->{capabilities} = $res->{capabilities}; |
|
|
273 | |
|
|
274 | $self->{_ep} = "$self->{endpoint}/session/$self->{sid}/"; |
|
|
275 | } |
|
|
276 | |
363 | |
277 | $cb->($status, $res); |
364 | $cb->($status, $res); |
278 | }); |
365 | }); |
279 | } |
366 | } |
280 | |
367 | |