File Coverage

blib/lib/Test/WWW/Mechanize/Object.pm
Criterion Covered Total %
statement 92 94 97.8
branch 17 26 65.3
condition 11 17 64.7
subroutine 17 17 100.0
pod 2 2 100.0
total 139 156 89.1


line stmt bran cond sub pod time code
1             package Test::WWW::Mechanize::Object;
2              
3 2     2   147895 use v5.6.1;
  2         8  
  2         95  
4 2     2   12 use Carp ();
  2         5  
  2         40  
5 2     2   11 use warnings;
  2         9  
  2         53  
6 2     2   156 use strict;
  2         3  
  2         95  
7 2     2   23 use base qw(Test::WWW::Mechanize);
  2         4  
  2         2679  
8              
9             =head1 NAME
10              
11             Test::WWW::Mechanize::Object - run mech tests by making
12             requests on an object
13              
14             =head1 VERSION
15              
16             Version 0.020
17              
18             =cut
19              
20             our $VERSION = '0.020';
21              
22             =head1 SYNOPSIS
23              
24             use Test::WWW::Mechanize::Object;
25             my $mech = Test::WWW::Mechanize::Object->new(handler => $obj);
26             $mech->get_ok('/foo');
27             # use $mech as usual, omitting scheme/host if you want to
28              
29             =head1 DESCRIPTION
30              
31             Test::WWW::Mechanize::Object exists to make it easier to run
32             tests with unusual request semantics.
33              
34             Instead of having to guess at which parts of the
35             LWP::UserAgent and WWW::Mechanize code needs to be
36             overridden, any object that implements a (relatively) simple
37             API can be passed in.
38              
39             All methods from Test::WWW::Mechanize. The only change is
40             the addition of the 'handler' parameter to the C<< new >>
41             method.
42              
43             =head1 METHODS
44              
45             =head2 request
46              
47             $obj->request($request);
48              
49             This method receives a L as its
50             only argument. It should return a
51             L object. It should not
52             follow redirects; LWP will take care of that.
53              
54             This method B exist.
55              
56             =head2 url_base
57              
58             =head2 default_url_base
59              
60             These method should return the current or default base for
61             request URLs, e.g.
62              
63             http://localhost.localdomain (the default default)
64             http://myserver.com/myurl
65              
66             These methods are optional. They are provided for handler
67             objects that change their behavior based on some contextual
68             information (e.g. %ENV). If this confuses you, you probably
69             don't need them.
70              
71             The results of these methods are cached after being called
72             once, so if your object's return values might change during
73             program execution, that will not be reflected properly in
74             Test::WWW::Mechanize::Object. If this matters to anyone,
75             send me a bug.
76              
77             =head2 prepare_request
78              
79             $obj->prepare_request($request, $mech);
80              
81             Called before LWP and Mech do all their request object
82             preparation.
83              
84             Note: this method will be called once per request in a redirect
85             chain.
86              
87             This method is optional.
88              
89             =head2 before_request
90              
91             $obj->before_request($request, $mech);
92              
93             Called after LWP and Mech do their request object
94             preparation, but before C<< $obj->request >> is called.
95              
96             Note: this method will be called once per request in a redirect
97             chain.
98              
99             =head2 after_request
100              
101             $obj->after_request($request, $response, $mech);
102              
103             Called after the object has returned its response, but before
104             LWP and Mech have done any post-processing.
105              
106             Note: this method will be called once per request in a redirect
107             chain.
108              
109             This method is optional.
110              
111             =head2 on_redirect
112              
113             $obj->on_redirect($request, $response, $mech);
114              
115             Called after C each time the object returns a response that is a
116             redirect (3XX status code).
117              
118             This method is optional.
119              
120             =head1 INTERNALS
121              
122             You don't need to read this section unless you are
123             interested in finding out how this module works, for
124             subclassing or debugging. Most users will only need to read
125             the method documentation above.
126              
127             =head2 new
128              
129             Overridden to note the 'handler' parameter.
130              
131             =cut
132              
133             sub new {
134 1     1 1 33 my ($class, %arg) = @_;
135 1 50       12 my $handler = delete $arg{handler}
136             or Carp::croak("the 'handler' argument is required for $class->new()");
137 1         15 my $self = $class->SUPER::new(%arg);
138 1         20393 $self->{handler} = $handler;
139 1         5 return $self;
140             }
141              
142             sub __hook {
143 32     32   1418 my ($self, $hookname, $args) = @_;
144 32 50       292 return unless my $meth = $self->{handler}->can($hookname);
145 0         0 $self->{handler}->$meth(@$args);
146             }
147              
148             =head2 _make_request
149              
150             Overridden (from WWW::Mechanize) to call the C hook.
151              
152             =cut
153              
154             sub _make_request {
155 10     10   26783 my ($self, $request, @rest) = @_;
156 10         70 $self->__hook(prepare_request => [ $request, $self ]);
157 10         372 $self->SUPER::_make_request($request, @rest);
158             }
159              
160             =head2 get
161              
162             =head2 head
163              
164             =head2 post
165              
166             Overridden (from LWP::UserAgent) to allow path-only URLs to be passed in, e.g.
167              
168             $mech->get('/foo', ...);
169              
170             =cut
171              
172             sub __add_url_base {
173 8     8   16 my $self = shift;
174 8         17 my $url = shift;
175 8 50       229 if ($url =~ m!^/!) {
176             #warn "prepending url_base to $url\n";
177 8         37 $url = $self->__url_base . $url;
178 8         17453 $url =~ s{(?
179             }
180 8         141 return ($url, @_);
181             }
182              
183             # replaces "$old" with "$new" in $uri
184             sub __rebase_uri {
185 21     21   160 my ($uri, $old, $new) = @_;
186 21 100       89 return $uri if $old->eq($new);
187 11         4808 my $clone = $uri->clone;
188 11         320 for my $part (qw(host scheme)) {
189 22 50       662 return $uri unless $clone->$part eq $old->$part;
190             }
191 27         922 my %path = (
192 10         178 clone => [ grep { length } $clone->path_segments ],
193 11         297 old => [ grep { length } $old->path_segments ],
194             );
195 11   100     820 while (@{$path{clone}} and @{$path{old}}
  16   66     72  
  14         94  
196             and $path{clone}->[0] eq $path{old}->[0]
197             ) {
198 5         15 shift @{$path{$_}} for qw(clone old);
  10         28  
199             }
200 11 50       101 if (@{$path{old}}) {
  11         39  
201             # unmatched path parts remaining
202 0         0 return $uri;
203             }
204 11         22 for my $part (qw(host scheme)) {
205 22         2982 $clone->$part($new->$part);
206             }
207 11         2601 my $path = join "/", $new->path_segments, @{$path{clone}};
  11         1479  
208 11         57 $path =~ s{/+}{/}g;
209 11         48 $clone->path($path);
210 11         2501 return $clone->canonical;
211             }
212              
213             sub __rebase_request_uri {
214 20     20   440 my $req = shift;
215 20         63 $req->uri( __rebase_uri( $req->uri, @_ ) );
216             }
217              
218             sub __url_base {
219 43     43   287 my $self = shift;
220 43 50 66     560 return $self->{__url_base} ||= (
221             $self->{handler}->can('url_base') ?
222             URI->new($self->{handler}->url_base)->canonical :
223             $self->__default_url_base
224             );
225             }
226              
227             sub __default_url_base {
228 35     35   363 my $self = shift;
229 35 50 66     150 return $self->{__default_url_base} ||= (
230             URI->new(
231             $self->{handler}->can('default_url_base') ?
232             $self->{handler}->default_url_base :
233             'http://localhost.localdomain'
234             )
235             );
236             }
237              
238             BEGIN {
239 2     2   9 for my $sub (qw(get head post)) {
240 2     2   895721 no strict 'refs';
  2         27  
  2         202  
241             *$sub = sub {
242 8     8   79380 my $self = shift;
243 8         31 my $meth = "SUPER::$sub";
244 8         42 $self->$meth($self->__add_url_base(@_));
245             }
246 6         2453 }
247             }
248              
249             =head2 send_request
250              
251             Overridden (from LWP::UserAgent) to send requests to the
252             handler object and to call the C hook.
253              
254             Note: This ignores the C<$arg> and C<$size> arguments that
255             LWP::UserAgent uses.
256              
257             =cut
258              
259             sub send_request {
260 10     10 1 13786 my ($self, $request, $arg, $size) = @_;
261 10         51 $self->__hook(before_request => [ $request, $self ]);
262             # url_base will have already been added, so we change it to the default here
263 10         38 __rebase_request_uri(
264             $request,
265             $self->__url_base,
266             $self->__default_url_base,
267             );
268 10         5111 my $response = $self->{handler}->request($request);
269 10         18106 $response->request($request);
270              
271             # change the default back to the real current url_base for cookie extraction
272 10         131 __rebase_request_uri(
273             $request,
274             $self->__default_url_base,
275             $self->__url_base,
276             );
277             # change cookie and location headers
278 10 100       3542 unless ($self->__url_base->eq($self->__default_url_base)) {
279 5         2862 for my $header (qw(Set-Cookie Set-Cookie2 Set-Cookie3)) {
280 15         954 my @values = $response->header($header);
281 1         3 $response->header($header => [ map {
282             #warn "$header: was: $_\n";
283 15         973 my $domain = $self->__default_url_base->host;
284 1   50     32 my $path = $self->__default_url_base->path || '/';
285 1 50 33     97 if (m{ \b domain = \Q$domain\E ([;\s]|$) }x and
286             m{\b path = \Q$path\E ([;\s]|$) }x) {
287 1         28 s{ \b domain = \Q$domain\E ([;\s]|$) }
  1         3  
288             {domain=@{[ $self->__url_base->host ]}$1}x;
289 1         57 s{ \b path = \Q$path\E ([;\s]|$)}
  1         4  
290             {path=@{[ $self->__url_base->path ]}$1}x;
291             }
292             #warn "$header: now: $_\n";
293             $_
294 1         26 } @values ]);
295             }
296             }
297              
298 10 50       2030 $self->cookie_jar->extract_cookies($response) if $self->cookie_jar;
299              
300 10         2335 $self->__hook(after_request => [ $request, $response, $self ]);
301              
302 10 100       78 if ($response->is_redirect) {
303 2         31 $self->__hook(on_redirect => [ $request, $response, $self ]);
304 2 100       9 unless ($self->__url_base->eq($self->__default_url_base)) {
305 1         1499 $response->header(
306             Location => __rebase_uri(
307             URI->new($response->header('Location')),
308             $self->__default_url_base,
309             $self->__url_base,
310             ),
311             );
312             }
313             }
314              
315 10         535 return $response;
316             }
317              
318             =head1 TODO
319              
320             Consider using L instead of
321             rebasing URIs internally.
322              
323             =head1 SEE ALSO
324              
325             L
326             L
327             L
328              
329             =head1 AUTHOR
330              
331             Hans Dieter Pearcey, C<< >>
332              
333             =head1 BUGS
334              
335             Please report any bugs or feature requests to
336             C, or through the web interface at
337             L.
338             I will be notified, and then you'll automatically be notified of progress on
339             your bug as I make changes.
340              
341             =head1 SUPPORT
342              
343             You can find documentation for this module with the perldoc command.
344              
345             perldoc Test::WWW::Mechanize::Object
346              
347             You can also look for information at:
348              
349             =over 4
350              
351             =item * AnnoCPAN: Annotated CPAN documentation
352              
353             L
354              
355             =item * CPAN Ratings
356              
357             L
358              
359             =item * RT: CPAN's request tracker
360              
361             L
362              
363             =item * Search CPAN
364              
365             L
366              
367             =back
368              
369             =head1 ACKNOWLEDGEMENTS
370              
371             Thanks to Pobox.com, who sponsored the original development of this module.
372              
373             =head1 COPYRIGHT & LICENSE
374              
375             Copyright 2006 Hans Dieter Pearcey, all rights reserved.
376              
377             This program is free software; you can redistribute it and/or modify it
378             under the same terms as Perl itself.
379              
380             =cut
381              
382             1; # End of Test::WWW::Mechanize::Object