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 (6 years, 10 months ago) by root
Branch: MAIN
CVS Tags: rel-0_2
Changes since 1.3: +190 -47 lines
Log Message:
0.2

File Contents

# User Rev Content
1 root 1.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 root 1.4 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 root 1.1
35     To make most of this module, or, in fact, to make any reasonable use of
36 root 1.4 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 root 1.1
40 root 1.4 https://www.w3.org/TR/webdriver1/
41 root 1.1
42 root 1.4 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 root 1.1 new AnyEvent::WebDriver key => value...
48 root 1.3 Create a new WebDriver object. Example for a remote WebDriver
49 root 1.1 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 root 1.3 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 root 1.4 $al = $wd->actions
79     Creates an action list associated with this WebDriver. See ACTION
80     LISTS, below, for full details.
81    
82 root 1.1 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 root 1.3 The method names are pretty much taken directly from the W3C WebDriver
90 root 1.1 specification, e.g. the request documented in the "Get All Cookies"
91     section is implemented via the "get_all_cookies" method.
92    
93 root 1.3 The order is the same as in the WebDriver draft at the time of this
94 root 1.1 writing, and only minimal massaging is done to request parameters and
95     results.
96    
97     SESSIONS
98     $wd->new_session ({ key => value... })
99 root 1.3 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 root 1.1
103     No session-dependent methods must be called before this function
104 root 1.3 returns successfully, and only one session can be created per
105     WebDriver object.
106 root 1.1
107 root 1.3 On success, "$wd->{sid}" is set to the session ID, and
108 root 1.1 "$wd->{capabilities}" is set to the returned capabilities.
109    
110 root 1.3 my $wd = new AnyEvent::Selenium endpoint => "http://localhost:4545";
111 root 1.1
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 root 1.3 => { implicit => 0, pageLoad => 300000, script => 30000 }
127 root 1.1
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 root 1.3 Queries the current page URL as set by "navigate_to".
139 root 1.1
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 root 1.3 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 root 1.4 select the nth subframe, or an element object.
169 root 1.1
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 root 1.3 => { height => 1040, width => 540, x => 0, y => 0 }
178 root 1.1
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 root 1.3 Changes the window size by either maximising, minimising or making
186     it fullscreen. In my experience, this will timeout if no window
187 root 1.1 manager is running.
188    
189     ELEMENT RETRIEVAL
190 root 1.4 $element = $wd->find_element ($location_strategy, $selector)
191 root 1.1 Finds the first element specified by the given selector and returns
192 root 1.4 its element object. Raises an error when no element was found.
193 root 1.1
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 root 1.4 => e.g. { "element-6066-11e4-a52e-4f735466cecf" => "decddca8-5986-4e1d-8c93-efe952505a5f" }
200 root 1.1
201 root 1.4 $elements = $wd->find_elements ($location_strategy, $selector)
202     As above, but returns an arrayref of all found element objects.
203 root 1.1
204 root 1.4 $element = $wd->find_element_from_element ($element, $location_strategy,
205     $selector)
206 root 1.1 Like "find_element", but looks only inside the specified $element.
207    
208 root 1.4 $elements = $wd->find_elements_from_element ($element,
209 root 1.1 $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 root 1.4 $element = $wd->get_active_element
216 root 1.1 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 root 1.4 $string = $wd->get_element_attribute ($element, $name)
224 root 1.1 Returns the value of the given attribute.
225    
226 root 1.4 $string = $wd->get_element_property ($element, $name)
227 root 1.1 Returns the value of the given property.
228    
229 root 1.4 $string = $wd->get_element_css_value ($element, $name)
230 root 1.3 Returns the value of the given CSS value.
231 root 1.1
232 root 1.4 $string = $wd->get_element_text ($element)
233 root 1.1 Returns the (rendered) text content of the given element.
234    
235 root 1.4 $string = $wd->get_element_tag_name ($element)
236 root 1.1 Returns the tag of the given element.
237    
238 root 1.4 $rect = $wd->get_element_rect ($element)
239 root 1.3 Returns the element rect(angle) of the given element.
240 root 1.1
241     $bool = $wd->is_element_enabled
242     Returns whether the element is enabled or not.
243    
244     ELEMENT INTERACTION
245 root 1.4 $wd->element_click ($element)
246 root 1.1 Clicks the given element.
247    
248 root 1.4 $wd->element_clear ($element)
249 root 1.1 Clear the contents of the given element.
250    
251 root 1.4 $wd->element_send_keys ($element, $text)
252 root 1.1 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 root 1.4 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 root 1.1
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 root 1.4 { type => "pointerMove", duration => 100, origin => $input, x => 40, y => 5 },
313 root 1.1 { 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 root 1.4 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 root 1.1 $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 root 1.3 Create a screenshot, returning it as a PNG image in a "data:" URL.
378 root 1.1
379 root 1.4 $wd->take_element_screenshot ($element)
380 root 1.1 Accept a simple dialog, if present.
381    
382 root 1.4 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 root 1.1
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 root 1.3 the HTTP status code (200 for successful requests, typically 4xx or 5xx
549 root 1.1 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 root 1.3 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 root 1.1 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 root 1.4 Example: call "find_elements" to find all "IMG" elements:
600 root 1.1
601 root 1.4 $elems = $wd->post (elements => { using => "css selector", value => "img" });
602 root 1.1
603     HISTORY
604 root 1.2 This module was unintentionally created (it started inside some quickly
605     hacked-together script) simply because I couldn't get the existing
606 root 1.1 "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