File Coverage

blib/lib/WWW/HtmlUnit/Sweet.pm
Criterion Covered Total %
statement 22 81 27.1
branch 5 36 13.8
condition 1 36 2.7
subroutine 5 10 50.0
pod 2 2 100.0
total 35 165 21.2


line stmt bran cond sub pod time code
1             package WWW::HtmlUnit::Sweet;
2              
3             =head1 NAME
4              
5             WWW::HtmlUnit::Sweet - Wrapper around WWW::HtmlUnit to add some sweetness
6              
7             =head1 SYNOPSIS
8              
9             use WWW::HtmlUnit::Sweet;
10             my $agent = WWW::HtmlUnit::Sweet->new;
11              
12             $agent->getPage('http://google.com/');
13              
14             # Type into the currently focused element
15             $agent->type("Hello\n");
16              
17             # Print out the XML of the page
18             print $agent->asXml;
19              
20             =head1 DESCRIPTION
21              
22             Using L as a foundation, this adds some convenience things. The main addition is that the $agent you get from ->new does some AUTOLOAD things to allow you to treat the $agent as either a browser, a window, or a page. That way you can treat it a lot more like a L object.
23              
24             This module might change drastically, buyer beware!
25              
26             =head1 IMPORT PARAMETERS
27              
28             When you 'use' this module, you can pass some parameters. Any parameter that L doesn't use will be passed on to L, or ultimately L.
29              
30             =over 4
31              
32             =item * show_errors - Flag to stop the supression of stderr
33              
34             =item * error_filename - Filename to append stderr to
35              
36             =item * error_fh - Filehandle to append stderr to
37              
38             =item * errors_to_tmpfile - Send stderr to a temporary file (L)
39              
40             =back 4
41              
42             Useful examples:
43              
44             # Show errors on STDERR
45             use WWW::HtmlUnit::Sweet show_errors => 1;
46              
47             # Append errors to /tmp/errors.txt
48             use WWW::HtmlUnit::Sweet error_filename => '/tmp/errors.txt';
49              
50             Note that if you don't pass anything, errors will be sent to /dev/null (or a temporary file if you don't have /dev/null).
51              
52             =cut
53              
54 1     1   658 use strict;
  1         5  
  1         33  
55 1     1   6 use warnings;
  1         2  
  1         284  
56              
57             # Hold our error filehandle
58             our $error_fh;
59              
60             sub import {
61 1     1   9 my $class = shift;
62 1         2 my %parameters = @_;
63              
64 1 50       5 if($parameters{show_errors}) {
65 0         0 delete $parameters{show_errors};
66 0         0 require WWW::HtmlUnit;
67 0         0 WWW::HtmlUnit->import( %parameters );
68             } else {
69 1 50 33     34 if($parameters{error_filename}) {
    50          
    50          
70 0 0       0 open $error_fh, '>>', $parameters{error_filename}
71             or die "Error opening $parameters{error_filename}, $!\n";
72 0         0 delete $parameters{error_filename};
73             } elsif($parameters{error_fh}) {
74 0         0 $error_fh = $parameters{error_fh};
75 0         0 delete $parameters{error_fh};
76             } elsif($parameters{errors_to_tmpfile} || ! -c '/dev/null') {
77 0         0 require IO::File;
78 0         0 $error_fh = IO::File->new_tmpfile;
79 0         0 delete $parameters{errors_to_tmpfile};
80             } else {
81 1 50       43 open $error_fh, '>', '/dev/null'
82             or die "Error opening $parameters{error_filename}, $!\n";
83             }
84              
85             # So we save STDERR, then redirect it
86              
87 1     1   6 no warnings; # stop complaint about SAVEERR never being used again
  1         2  
  1         60  
88 1         21 open SAVEERR, '>&', STDERR;
89 1     1   6 use warnings;
  1         1  
  1         841  
90 1         5 close STDERR;
91 1         14 open STDERR, '>&', $error_fh;
92            
93             # Now Inline::Java will use our special filehandle instead of STDERR
94 1         653 require WWW::HtmlUnit;
95 1         6 WWW::HtmlUnit->import( %parameters );
96              
97             # Now put STDERR back!
98 0           close STDERR;
99 0           open STDERR, '>&', SAVEERR;
100             }
101              
102             }
103              
104             =head1 METHODS
105              
106             =head2 $agent = WWW::HtmlUnit::Sweet->new
107              
108             Create a new sweet agent. Use this kinda like looking at a browser on the screen. The methods you call will be invoked (if possible) on the current browser, window, page, or focused element.
109              
110             The 'new' method can also take a browser version and a starting url, like this:
111              
112             my $agent = WWW::HtmlUnit::Sweet->new(
113             version => 'FIREFOX_3',
114             url => 'http://google.com/'
115             );
116              
117             =cut
118              
119             sub new {
120 0     0 1   my $class = shift;
121 0           my $self = { @_ };
122 0           bless $self, $class;
123 0           $self->{browser} = WWW::HtmlUnit->new( $self->{version} );
124 0 0         $self->getPage( $self->{url} ) if $self->{url};
125 0           return $self;
126             }
127              
128             =head2 $agent->wait_for(sub { ... }, $timeout)
129              
130             Execute the provided sub once a second until it returns true, or until the the timeout has been reached. If a timeout isn't passed, it will default to 10 seconds (which you can change by setting C<< $WWW::HtmlUnit::Sweet::default_timeout >>). This is handy for waiting for the page to finish executing some javascript, or loading.
131              
132             Example:
133              
134             # Wait for an element with id 'foo' to exist
135             $agent->wait_for(sub {
136             $agent->getElementById('foo')
137             });
138              
139             =cut
140              
141             our $default_timeout = 10;
142              
143             sub wait_for {
144 0     0 1   my ($agent, $subref, $timeout) = @_;
145 0   0       $timeout ||= $default_timeout;
146 0           while($timeout) {
147 0 0 0       return if eval { $subref->() } && ! $@;
  0            
148 0           sleep 1;
149 0           $timeout--;
150             }
151 0           die "Timeout!\n";
152             }
153              
154             =head2 AUTOLOAD, aka $agent->whatever(..)
155              
156             This is where the sweetness starts kicking in. First it will try to call ->whatever on the browser, and if there is no method named 'whatever' there it will be called on the current window, and if there is no method named 'whatever' there it will be called on the current page in that window, and if there is no method 'whatever' there it will be called on the currently focused element.
157              
158             Examples:
159              
160             # This works at the browser level
161             $agent->getPage('http://google.com/');
162              
163             # Get the 'name' for the current window
164             my $window_name = $agent->getName;
165              
166             # Working from the current page, get an element by ID
167             my $sidebar_element = $agent->getElementById('sidebar');
168              
169             # Click on the currently focused element
170             $agent->click;
171              
172             This scheme works quite well because HtmlUnit itself just so happens to not overlap their method names between different classes. Lucky us!
173              
174             Note: We also call ->toArray on results if needed. Probably at some point we'll get ALL array-like results from HtmlUnit to auto-execute ->toArray.
175              
176             =cut
177              
178             # This will make us act a bit more like Mechanize
179             sub AUTOLOAD {
180 0     0     my $self = shift;
181 0           our $AUTOLOAD;
182 0           my $method = $AUTOLOAD; $method =~ s/.*:://;
  0            
183 0 0         return if $method eq 'DESTROY';
184 0           my $retval = eval {
185            
186 0           my $browser = $self->{browser};
187 0   0       my $window = $browser && $browser->getCurrentWindow;
188 0   0       my $page = $window && $window->getEnclosedPage;
189 0   0       my $element = $page && $page->getFocusedElement;
190              
191 0           my $result;
192 0 0 0       if($browser && $browser->can($method)) {
    0 0        
    0 0        
    0 0        
193 0           $result = $browser->$method(@_);
194             } elsif($window && $window->can($method)) {
195 0           $result = $window->$method(@_);
196             } elsif($page && $page->can($method)) {
197 0           $result = $page->$method(@_);
198             } elsif($element && $element->can($method)) {
199 0           $result = $element->$method(@_);
200             } else {
201 0           die "Method $method not found!";
202             }
203 0 0 0       if(ref $result && $result->can('toArray')) {
204 0           return $result->toArray;
205             } else {
206 0           return $result;
207             }
208             };
209 0 0 0       if($@ && ref($@) =~ /Exception/) {
    0          
210 0           print STDERR "HtmlUnit ERROR: " . $@->getMessage . "\n";
211 0           die $@; # Pass it up the chain
212             } elsif($@) {
213 0           warn $@;
214             }
215 0           return $retval;
216             }
217              
218              
219             package WWW::HtmlUnit::com::gargoylesoftware::htmlunit::html::HtmlSelect;
220              
221             # Fix the get_option to take nicer params
222             # TODO: document this!
223              
224             sub get_option {
225 0     0     my ($self, %params) = @_;
226 0 0         if($params{text}) {
    0          
227 0           return eval {$self->getOptionByText($params{text})};
  0            
228             } elsif($params{value}) {
229 0           return eval {$self->getOptionByValue($params{value})};
  0            
230             }
231 0           die "Must pass either text or value";
232             }
233              
234             package WWW::HtmlUnit::java::lang::Object;
235              
236             sub sweeten {
237 0     0     return WWW::HtmlUnit::Sweet->new();
238             }
239              
240             =head1 TODO
241              
242             Add more documentation and examples and sweetness :)
243              
244             =head1 SEE ALSO
245              
246             L
247              
248             =head1 AUTHOR
249              
250             Brock Wilcox - http://thelackthereof.org/
251              
252             =head1 COPYRIGHT
253              
254             Copyright (c) 2009-2011 Brock Wilcox . All rights
255             reserved. This program is free software; you can redistribute it and/or
256             modify it under the same terms as Perl itself.
257              
258             =cut
259              
260             1;
261