ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/AnyEvent-WebDriver/README
Revision: 1.4
Committed: Wed Aug 29 02:17:51 2018 UTC (5 years, 9 months ago) by root
Branch: MAIN
CVS Tags: rel-0_2
Changes since 1.3: +190 -47 lines
Log Message:
0.2

File Contents

# Content
1 NAME
2 AnyEvent::WebDriver - control browsers using the W3C WebDriver protocol
3
4 SYNOPSIS
5 # start geckodriver or any other w3c-compatible webdriver via the shell
6 $ geckdriver -b myfirefox/firefox --log trace --port 4444
7
8 # then use it
9 use AnyEvent::WebDriver;
10
11 # create a new webdriver object
12 my $wd = new AnyEvent::WebDriver;
13
14 # create a new session with default capabilities.
15 $wd->new_session ({});
16
17 $wd->navigate_to ("https://duckduckgo.com/html");
18 my $searchbox = $wd->find_element ("css selector" => 'input[type="text"]');
19
20 $wd->element_send_keys ($searchbox => "free software");
21 $wd->element_click ($wd->find_element ("css selector" => 'input[type="submit"]'));
22
23 sleep 10;
24
25 DESCRIPTION
26 This module aims to implement the W3C WebDriver specification which is
27 the standardised equivalent to the Selenium WebDriver API., which in
28 turn aims at remotely controlling web browsers such as Firefox or
29 Chromium.
30
31 At the time of this writing, it was so brand new that I ciould only get
32 "geckodriver" (For Firefox) to work, but that is expected to be fioxed
33 very soon indeed.
34
35 To make most of this module, or, in fact, to make any reasonable use of
36 this module, you would need to refer to the W3C WebDriver
37 recommendation, which can be found here
38 <https://www.w3.org/TR/webdriver1/>:
39
40 https://www.w3.org/TR/webdriver1/
41
42 CONVENTIONS
43 Unless otherwise stated, all delays and time differences in this module
44 are represented as an integer number of milliseconds.
45
46 WEBDRIVER OBJECTS
47 new AnyEvent::WebDriver key => value...
48 Create a new WebDriver object. Example for a remote WebDriver
49 connection (the only type supported at the moment):
50
51 my $wd = new AnyEvent::WebDriver host => "localhost", port => 4444;
52
53 Supported keys are:
54
55 endpoint => $string
56 For remote connections, the endpoint to connect to (defaults to
57 "http://localhost:4444").
58
59 proxy => $proxyspec
60 The proxy to use (same as the "proxy" argument used by
61 AnyEvent::HTTP). The default is "undef", which disables proxies.
62 To use the system-provided proxy (e.g. "http_proxy" environment
63 variable), specify a value of "default".
64
65 autodelete => $boolean
66 If true (the default), then automatically execute
67 "delete_session" when the WebDriver object is destroyed with an
68 active session. IF set to a false value, then the session will
69 continue to exist.
70
71 timeout => $seconds
72 The HTTP timeout, in (fractional) seconds (default: 300, but
73 this will likely drastically reduce). This timeout is reset on
74 any activity, so it is not an overall request timeout. Also,
75 individual requests might extend this timeout if they are known
76 to take longer.
77
78 $al = $wd->actions
79 Creates an action list associated with this WebDriver. See ACTION
80 LISTS, below, for full details.
81
82 SIMPLIFIED API
83 This section documents the simplified API, which is really just a very
84 thin wrapper around the WebDriver protocol commands. They all block
85 (using AnyEvent condvars) the caller until the result is available, so
86 must not be called from an event loop callback - see "EVENT BASED API"
87 for an alternative.
88
89 The method names are pretty much taken directly from the W3C WebDriver
90 specification, e.g. the request documented in the "Get All Cookies"
91 section is implemented via the "get_all_cookies" method.
92
93 The order is the same as in the WebDriver draft at the time of this
94 writing, and only minimal massaging is done to request parameters and
95 results.
96
97 SESSIONS
98 $wd->new_session ({ key => value... })
99 Try to connect to the WebDriver and initialize a new session with a
100 "new session" command, passing the given key-value pairs as value
101 (e.g. "capabilities").
102
103 No session-dependent methods must be called before this function
104 returns successfully, and only one session can be created per
105 WebDriver object.
106
107 On success, "$wd->{sid}" is set to the session ID, and
108 "$wd->{capabilities}" is set to the returned capabilities.
109
110 my $wd = new AnyEvent::Selenium endpoint => "http://localhost:4545";
111
112 $wd->new_session ({
113 capabilities => {
114 pageLoadStrategy => "normal",
115 }.
116 });
117
118 $wd->delete_session
119 Deletes the session - the WebDriver object must not be used after
120 this call.
121
122 $timeouts = $wd->get_timeouts
123 Get the current timeouts, e.g.:
124
125 my $timeouts = $wd->get_timeouts;
126 => { implicit => 0, pageLoad => 300000, script => 30000 }
127
128 $wd->set_timeouts ($timeouts)
129 Sets one or more timeouts, e.g.:
130
131 $wd->set_timeouts ({ script => 60000 });
132
133 NAVIGATION
134 $wd->navigate_to ($url)
135 Navigates to the specified URL.
136
137 $url = $wd->get_current_url
138 Queries the current page URL as set by "navigate_to".
139
140 $wd->back
141 The equivalent of pressing "back" in the browser.
142
143 $wd->forward
144 The equivalent of pressing "forward" in the browser.
145
146 $wd->refresh
147 The equivalent of pressing "refresh" in the browser.
148
149 $title = $wd->get_title
150 Returns the current document title.
151
152 COMMAND CONTEXTS
153 $handle = $wd->get_window_handle
154 Returns the current window handle.
155
156 $wd->close_window
157 Closes the current browsing context.
158
159 $wd->switch_to_window ($handle)
160 Changes the current browsing context to the given window.
161
162 $handles = $wd->get_window_handles
163 Return the current window handles as an array-ref of handle IDs.
164
165 $handles = $wd->switch_to_frame ($frame)
166 Switch to the given frame identified by $frame, which must be either
167 "undef" to go back to the top-level browsing context, an integer to
168 select the nth subframe, or an element object.
169
170 $handles = $wd->switch_to_parent_frame
171 Switch to the parent frame.
172
173 $rect = $wd->get_window_rect
174 Return the current window rect, e.g.:
175
176 $rect = $wd->get_window_rect
177 => { height => 1040, width => 540, x => 0, y => 0 }
178
179 $wd->set_window_rect ($rect)
180 Sets the window rect.
181
182 $wd->maximize_window
183 $wd->minimize_window
184 $wd->fullscreen_window
185 Changes the window size by either maximising, minimising or making
186 it fullscreen. In my experience, this will timeout if no window
187 manager is running.
188
189 ELEMENT RETRIEVAL
190 $element = $wd->find_element ($location_strategy, $selector)
191 Finds the first element specified by the given selector and returns
192 its element object. Raises an error when no element was found.
193
194 $element = $wd->find_element ("css selector" => "body a");
195 $element = $wd->find_element ("link text" => "Click Here For Porn");
196 $element = $wd->find_element ("partial link text" => "orn");
197 $element = $wd->find_element ("tag name" => "input");
198 $element = $wd->find_element ("xpath" => '//input[@type="text"]');
199 => e.g. { "element-6066-11e4-a52e-4f735466cecf" => "decddca8-5986-4e1d-8c93-efe952505a5f" }
200
201 $elements = $wd->find_elements ($location_strategy, $selector)
202 As above, but returns an arrayref of all found element objects.
203
204 $element = $wd->find_element_from_element ($element, $location_strategy,
205 $selector)
206 Like "find_element", but looks only inside the specified $element.
207
208 $elements = $wd->find_elements_from_element ($element,
209 $location_strategy, $selector)
210 Like "find_elements", but looks only inside the specified $element.
211
212 my $head = $wd->find_element ("tag name" => "head");
213 my $links = $wd->find_elements_from_element ($head, "tag name", "link");
214
215 $element = $wd->get_active_element
216 Returns the active element.
217
218 ELEMENT STATE
219 $bool = $wd->is_element_selected
220 Returns whether the given input or option element is selected or
221 not.
222
223 $string = $wd->get_element_attribute ($element, $name)
224 Returns the value of the given attribute.
225
226 $string = $wd->get_element_property ($element, $name)
227 Returns the value of the given property.
228
229 $string = $wd->get_element_css_value ($element, $name)
230 Returns the value of the given CSS value.
231
232 $string = $wd->get_element_text ($element)
233 Returns the (rendered) text content of the given element.
234
235 $string = $wd->get_element_tag_name ($element)
236 Returns the tag of the given element.
237
238 $rect = $wd->get_element_rect ($element)
239 Returns the element rect(angle) of the given element.
240
241 $bool = $wd->is_element_enabled
242 Returns whether the element is enabled or not.
243
244 ELEMENT INTERACTION
245 $wd->element_click ($element)
246 Clicks the given element.
247
248 $wd->element_clear ($element)
249 Clear the contents of the given element.
250
251 $wd->element_send_keys ($element, $text)
252 Sends the given text as key events to the given element.
253
254 DOCUMENT HANDLING
255 $source = $wd->get_page_source
256 Returns the (HTML/XML) page source of the current document.
257
258 $results = $wd->execute_script ($javascript, $args)
259 Synchronously execute the given script with given arguments and
260 return its results ($args can be "undef" if no arguments are
261 wanted/needed).
262
263 $ten = $wd->execute_script ("return arguments[0]+arguments[1]", [3, 7]);
264
265 $results = $wd->execute_async_script ($javascript, $args)
266 Similar to "execute_script", but doesn't wait for script to return,
267 but instead waits for the script to call its last argument, which is
268 added to $args automatically.
269
270 $twenty = $wd->execute_async_script ("arguments[0](20)", undef);
271
272 COOKIES
273 $cookies = $wd->get_all_cookies
274 Returns all cookies, as an arrayref of hashrefs.
275
276 # google surely sets a lot of cookies without my consent
277 $wd->navigate_to ("http://google.com");
278 use Data::Dump;
279 ddx $wd->get_all_cookies;
280
281 $cookie = $wd->get_named_cookie ($name)
282 Returns a single cookie as a hashref.
283
284 $wd->add_cookie ($cookie)
285 Adds the given cookie hashref.
286
287 $wd->delete_cookie ($name)
288 Delete the named cookie.
289
290 $wd->delete_all_cookies
291 Delete all cookies.
292
293 ACTIONS
294 $wd->perform_actions ($actions)
295 Perform the given actions (an arrayref of action specifications
296 simulating user activity, or an "AnyEvent::WebDriver::Actions"
297 object). For further details, read the spec or the section "ACTION
298 LISTS", below.
299
300 An example to get you started (see the next example for a mostly
301 equivalent example using the "AnyEvent::WebDriver::Actions" helper
302 API):
303
304 $wd->navigate_to ("https://duckduckgo.com/html");
305 my $input = $wd->find_element ("css selector", 'input[type="text"]');
306 $wd->perform_actions ([
307 {
308 id => "myfatfinger",
309 type => "pointer",
310 pointerType => "touch",
311 actions => [
312 { type => "pointerMove", duration => 100, origin => $input, x => 40, y => 5 },
313 { type => "pointerDown", button => 1 },
314 { type => "pause", duration => 40 },
315 { type => "pointerUp", button => 1 },
316 ],
317 },
318 {
319 id => "mykeyboard",
320 type => "key",
321 actions => [
322 { type => "pause" },
323 { type => "pause" },
324 { type => "pause" },
325 { type => "pause" },
326 { type => "keyDown", value => "a" },
327 { type => "pause", duration => 100 },
328 { type => "keyUp", value => "a" },
329 { type => "pause", duration => 100 },
330 { type => "keyDown", value => "b" },
331 { type => "pause", duration => 100 },
332 { type => "keyUp", value => "b" },
333 { type => "pause", duration => 2000 },
334 { type => "keyDown", value => "\x{E007}" }, # enter
335 { type => "pause", duration => 100 },
336 { type => "keyUp", value => "\x{E007}" }, # enter
337 { type => "pause", duration => 5000 },
338 ],
339 },
340 ]);
341
342 And here is essentially the same (except for fewer pauses) example
343 as above, using the much simpler "AnyEvent::WebDriver::Actions" API.
344 Note that the pointer up and key down event happen concurrently in
345 this example:
346
347 $wd->navigate_to ("https://duckduckgo.com/html");
348 my $input = $wd->find_element ("css selector", 'input[type="text"]');
349 $wd->actions
350 ->move ($input, 40, 5, "touch1")
351 ->click;
352 ->key ("a");
353 ->key ("b");
354 ->pause (2000);
355 ->key ("\x{E007}")
356 ->pause (5000);
357 ->perform;
358
359 $wd->release_actions
360 Release all keys and pointer buttons currently depressed.
361
362 USER PROMPTS
363 $wd->dismiss_alert
364 Dismiss a simple dialog, if present.
365
366 $wd->accept_alert
367 Accept a simple dialog, if present.
368
369 $text = $wd->get_alert_text
370 Returns the text of any simple dialog.
371
372 $text = $wd->send_alert_text
373 Fills in the user prompt with the given text.
374
375 SCREEN CAPTURE
376 $wd->take_screenshot
377 Create a screenshot, returning it as a PNG image in a "data:" URL.
378
379 $wd->take_element_screenshot ($element)
380 Accept a simple dialog, if present.
381
382 ACTION LISTS
383 Action lists can be quite complicated. Or at least it took a while for
384 me to twist my head around them. Basically, an action list consists of a
385 number of sources representing devices (such as a finger, a mouse, a pen
386 or a keyboard) and a list of actions for each source.
387
388 An action can be a key press, a pointer move or a pause (time delay).
389 Actions from different sources can happen "at the same time", while
390 actions from a single source are executed in order.
391
392 While you can provide an action list manually, it is (hopefully) less
393 cumbersome to use the API described in this section to create them.
394
395 The basic process of creating and performing actions is to create a new
396 action list, adding action sources, followed by adding actions. Finally
397 you would "perform" those actions on the WebDriver.
398
399 Virtual time progresses as long as you add actions to the same event
400 source. Adding events to different sources are considered to happen
401 concurrently. If you want to force time to progress, you can do this
402 using a call to "->pause (0)".
403
404 Most methods here are designed to chain, i.e. they return the web
405 actions object, to simplify multiple calls.
406
407 For example, to simulate a mouse click to an input element, followed by
408 entering some text and pressing enter, you can use this:
409
410 $wd->actions
411 ->click (1, 100)
412 ->type ("some text")
413 ->key ("Enter")
414 ->perform;
415
416 By default, keyboard and mouse input sources are provided. You can
417 create your own sources and use them when adding events. The above
418 example could be more verbosely written like this:
419
420 $wd->actions
421 ->click (1, 100, "mouse")
422 ->type ("some text")
423 ->key ("Enter")
424 ->perform;
425
426 #TODO verboser example
427
428 When you specify the event source expliticly it will switch the current
429 "focus" for this class of device (all keyboards are in one class, all
430 pointer-like devices such as mice/fingers/pens are in one class), so you
431 don't have to specify the source for subsequent actions.
432
433 When you use the sources "keyboard", "mouse", "touch1".."touch3", "pen"
434 without defining them, then a suitable default source will be created
435 for them.
436
437 $al = new AnyEvent::WebDriver::Actions
438 Create a new empty action list object. More often you would use the
439 "$sel->action_list" method to create one that is already associated
440 with a given web driver.
441
442 $al = $al->source ($id, $type, key => value...)
443 The first time you call this with a givne ID, this defines the event
444 source using the extra parameters. Subsequent calls merely switch
445 the current source for its event class.
446
447 It's not an error to define built-in sources (such as "keyboard" or
448 "touch1") differently then the defaults.
449
450 Example: define a new touch device called "fatfinger".
451
452 $al->source (fatfinger => "pointer", pointerType => "touch");
453
454 Example: switchdefine a new touch device called "fatfinger".
455
456 $al->source (fatfinger => "pointer", pointerType => "touch");
457
458 $al = $al->pause ($duration)
459 Creates a pause with the given duration. Makes sure that time
460 progresses in any case, even when $duration is 0.
461
462 $al = $al->pointer_down ($button, $source)
463 $al = $al->pointer_up ($button, $source)
464 Press or release the given button. $button defaults to 1.
465
466 $al = $al->click ($button, $source)
467 Convenience function that creates a button press and release action
468 without any delay between them. $button defaults to 1.
469
470 $al = $al->doubleclick ($button, $source)
471 Convenience function that creates two button press and release
472 action pairs in a row, with no unnecessary delay between them.
473 $button defaults to 1.
474
475 $al = $al->move ($button, $origin, $x, $y, $duration, $source)
476 Moves a pointer to the given position, relative to origin (either
477 "viewport", "pointer" or an element object.
478
479 $al = $al->keyDown ($key, $source)
480 $al = $al->keyUp ($key, $source)
481 Press or release the given key.
482
483 $al->perform ($wd)
484 Finaluses and compiles the list, if not done yet, and calls
485 "$wd->perform" with it.
486
487 If $wd is undef, and the action list was created using the
488 "$wd->actions" method, then perform it against that WebDriver
489 object.
490
491 There is no underscore variant - call the "perform_actions_" method
492 with the action object instead.
493
494 $al->perform_release ($wd)
495 Exactly like "perform", but additionally call "release_actions"
496 afterwards.
497
498 ($actions, $duration) = $al->compile
499 Finalises and compiles the list, if not done yet, and returns an
500 actions object suitable for calls to "$wd->perform_actions". When
501 called in list context, additionally returns the total duration of
502 the action list.
503
504 Since building large action lists can take nontrivial amounts of
505 time, it can make sense to build an action list only once and then
506 perform it multiple times.
507
508 Actions must not be added after compiling a list.
509
510 EVENT BASED API
511 This module wouldn't be a good AnyEvent citizen if it didn't have a true
512 event-based API.
513
514 In fact, the simplified API, as documented above, is emulated via the
515 event-based API and an "AUTOLOAD" function that automatically provides
516 blocking wrappers around the callback-based API.
517
518 Every method documented in the "SIMPLIFIED API" section has an
519 equivalent event-based method that is formed by appending a underscore
520 ("_") to the method name, and appending a callback to the argument list
521 (mnemonic: the underscore indicates the "the action is not yet finished"
522 after the call returns).
523
524 For example, instead of a blocking calls to "new_session", "navigate_to"
525 and "back", you can make a callback-based ones:
526
527 my $cv = AE::cv;
528
529 $wd->new_session ({}, sub {
530 my ($status, $value) = @_,
531
532 die "error $value->{error}" if $status ne "200";
533
534 $wd->navigate_to_ ("http://www.nethype.de", sub {
535
536 $wd->back_ (sub {
537 print "all done\n";
538 $cv->send;
539 });
540
541 });
542 });
543
544 $cv->recv;
545
546 While the blocking methods "croak" on errors, the callback-based ones
547 all pass two values to the callback, $status and $res, where $status is
548 the HTTP status code (200 for successful requests, typically 4xx or 5xx
549 for errors), and $res is the value of the "value" key in the JSON
550 response object.
551
552 Other than that, the underscore variants and the blocking variants are
553 identical.
554
555 LOW LEVEL API
556 All the simplified API methods are very thin wrappers around WebDriver
557 commands of the same name. They are all implemented in terms of the
558 low-level methods ("req", "get", "post" and "delete"), which exists in
559 blocking and callback-based variants ("req_", "get_", "post_" and
560 "delete_").
561
562 Examples are after the function descriptions.
563
564 $wd->req_ ($method, $uri, $body, $cb->($status, $value))
565 $value = $wd->req ($method, $uri, $body)
566 Appends the $uri to the "endpoint/session/{sessionid}/" URL and
567 makes a HTTP $method request ("GET", "POST" etc.). "POST" requests
568 can provide a UTF-8-encoded JSON text as HTTP request body, or the
569 empty string to indicate no body is used.
570
571 For the callback version, the callback gets passed the HTTP status
572 code (200 for every successful request), and the value of the
573 "value" key in the JSON response object as second argument.
574
575 $wd->get_ ($uri, $cb->($status, $value))
576 $value = $wd->get ($uri)
577 Simply a call to "req_" with $method set to "GET" and an empty body.
578
579 $wd->post_ ($uri, $data, $cb->($status, $value))
580 $value = $wd->post ($uri, $data)
581 Simply a call to "req_" with $method set to "POST" - if $body is
582 "undef", then an empty object is send, otherwise, $data must be a
583 valid request object, which gets encoded into JSON for you.
584
585 $wd->delete_ ($uri, $cb->($status, $value))
586 $value = $wd->delete ($uri)
587 Simply a call to "req_" with $method set to "DELETE" and an empty
588 body.
589
590 Example: implement "get_all_cookies", which is a simple "GET" request
591 without any parameters:
592
593 $cookies = $wd->get ("cookie");
594
595 Example: implement "execute_script", which needs some parameters:
596
597 $results = $wd->post ("execute/sync" => { script => "$javascript", args => [] });
598
599 Example: call "find_elements" to find all "IMG" elements:
600
601 $elems = $wd->post (elements => { using => "css selector", value => "img" });
602
603 HISTORY
604 This module was unintentionally created (it started inside some quickly
605 hacked-together script) simply because I couldn't get the existing
606 "Selenium::Remote::Driver" module to work, ever, despite multiple
607 attempts over the years and trying to report multiple bugs, which have
608 been completely ignored. It's also not event-based, so, yeah...
609
610 AUTHOR
611 Marc Lehmann <schmorp@schmorp.de>
612 http://anyevent.schmorp.de
613