| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
|
|
2
|
|
|
|
|
|
|
use strict; |
|
3
|
127
|
|
|
127
|
|
6195511
|
use warnings; |
|
|
127
|
|
|
|
|
1201
|
|
|
|
127
|
|
|
|
|
3814
|
|
|
4
|
127
|
|
|
127
|
|
687
|
use Test::More (); |
|
|
127
|
|
|
|
|
249
|
|
|
|
127
|
|
|
|
|
3222
|
|
|
5
|
127
|
|
|
127
|
|
3593
|
|
|
|
127
|
|
|
|
|
310947
|
|
|
|
127
|
|
|
|
|
2501
|
|
|
6
|
|
|
|
|
|
|
use Plack::Test; |
|
7
|
127
|
|
|
127
|
|
53138
|
use Catalyst::Exception; |
|
|
127
|
|
|
|
|
126356
|
|
|
|
127
|
|
|
|
|
6500
|
|
|
8
|
127
|
|
|
127
|
|
37268
|
use Catalyst::Utils; |
|
|
127
|
|
|
|
|
471
|
|
|
|
127
|
|
|
|
|
4515
|
|
|
9
|
127
|
|
|
127
|
|
55110
|
use Class::Load qw(load_class is_class_loaded); |
|
|
127
|
|
|
|
|
449
|
|
|
|
127
|
|
|
|
|
4419
|
|
|
10
|
127
|
|
|
127
|
|
835
|
use Sub::Exporter; |
|
|
127
|
|
|
|
|
288
|
|
|
|
127
|
|
|
|
|
6371
|
|
|
11
|
127
|
|
|
127
|
|
764
|
use Moose::Util 'find_meta'; |
|
|
127
|
|
|
|
|
278
|
|
|
|
127
|
|
|
|
|
995
|
|
|
12
|
127
|
|
|
127
|
|
27290
|
use Carp 'croak', 'carp'; |
|
|
127
|
|
|
|
|
295
|
|
|
|
127
|
|
|
|
|
716
|
|
|
13
|
127
|
|
|
127
|
|
30405
|
|
|
|
127
|
|
|
|
|
281
|
|
|
|
127
|
|
|
|
|
240244
|
|
|
14
|
|
|
|
|
|
|
my ($self, $args) = @_; |
|
15
|
|
|
|
|
|
|
|
|
16
|
128
|
|
|
128
|
|
337
|
return sub { _remote_request(@_) } |
|
17
|
|
|
|
|
|
|
if $args->{remote}; |
|
18
|
0
|
|
|
0
|
|
0
|
|
|
19
|
128
|
50
|
|
|
|
473
|
my $class = $args->{class}; |
|
20
|
|
|
|
|
|
|
|
|
21
|
128
|
|
|
|
|
322
|
# Here we should be failing right away, but for some stupid backcompat thing |
|
22
|
|
|
|
|
|
|
# I don't quite remember we fail lazily here. Needs a proper deprecation and |
|
23
|
|
|
|
|
|
|
# then removal. |
|
24
|
|
|
|
|
|
|
return sub { croak "Must specify a test app: use Catalyst::Test 'TestApp'" } |
|
25
|
|
|
|
|
|
|
unless $class; |
|
26
|
2
|
|
|
2
|
|
1832
|
|
|
27
|
128
|
100
|
|
|
|
527
|
load_class($class) unless is_class_loaded($class); |
|
28
|
|
|
|
|
|
|
$class->import; |
|
29
|
126
|
100
|
|
|
|
1257
|
|
|
30
|
126
|
|
|
|
|
11502
|
return sub { _local_request( $class, @_ ) }; |
|
31
|
|
|
|
|
|
|
} |
|
32
|
126
|
|
|
907
|
|
1113
|
|
|
|
907
|
|
|
|
|
927492
|
|
|
33
|
|
|
|
|
|
|
my ($self, $args) = @_; |
|
34
|
|
|
|
|
|
|
my $request = $args->{request}; |
|
35
|
|
|
|
|
|
|
|
|
36
|
128
|
|
|
128
|
|
468
|
return sub { $request->(@_)->content }; |
|
37
|
128
|
|
|
|
|
380
|
} |
|
38
|
|
|
|
|
|
|
my ($self, $args) = @_; |
|
39
|
128
|
|
|
43
|
|
576
|
my ($class, $request) = @{ $args }{qw(class request)}; |
|
|
43
|
|
|
|
|
50369
|
|
|
40
|
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
return sub { |
|
42
|
128
|
|
|
128
|
|
460
|
my $me = ref $self || $self; |
|
43
|
128
|
|
|
|
|
350
|
|
|
|
128
|
|
|
|
|
480
|
|
|
44
|
|
|
|
|
|
|
# fail if ctx_request is being used against a remote server |
|
45
|
|
|
|
|
|
|
Catalyst::Exception->throw("$me only works with local requests, not remote") |
|
46
|
79
|
|
33
|
79
|
|
95797
|
if $ENV{CATALYST_SERVER}; |
|
|
|
|
|
17
|
|
|
|
|
47
|
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
# check explicitly for the class here, or the Cat->meta call will blow |
|
49
|
|
|
|
|
|
|
# up in our face |
|
50
|
79
|
50
|
|
|
|
368
|
Catalyst::Exception->throw("Must specify a test app: use Catalyst::Test 'TestApp'") unless $class; |
|
51
|
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
# place holder for $c after the request finishes; reset every time |
|
53
|
|
|
|
|
|
|
# requests are done. |
|
54
|
79
|
100
|
|
|
|
286
|
my $ctx_closed_over; |
|
55
|
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
# hook into 'dispatch' -- the function gets called after all plugins |
|
57
|
|
|
|
|
|
|
# have done their work, and it's an easy place to capture $c. |
|
58
|
78
|
|
|
|
|
152
|
my $meta = find_meta($class); |
|
59
|
|
|
|
|
|
|
$meta->make_mutable; |
|
60
|
|
|
|
|
|
|
$meta->add_after_method_modifier( "dispatch", sub { |
|
61
|
|
|
|
|
|
|
$ctx_closed_over = shift; |
|
62
|
78
|
|
|
|
|
293
|
}); |
|
63
|
78
|
|
|
|
|
1405
|
$meta->make_immutable( replace_constructor => 1 ); |
|
64
|
|
|
|
|
|
|
Class::C3::reinitialize(); # Fixes RT#46459, I've failed to write a test for how/why, but it does. |
|
65
|
954
|
|
|
954
|
|
5890
|
|
|
66
|
78
|
|
|
|
|
18116
|
# do the request; C::T::request will know about the class name, and |
|
67
|
78
|
|
|
|
|
43921
|
# we've already stopped it from doing remote requests above. |
|
68
|
78
|
|
|
|
|
29484
|
my $res = $args->{request}->( @_ ); |
|
69
|
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
# Make sure not to leave a reference $ctx hanging around. |
|
71
|
|
|
|
|
|
|
# This means that the context will go out of scope as soon as the |
|
72
|
78
|
|
|
|
|
494
|
# caller disposes of it, rather than waiting till the next time |
|
73
|
|
|
|
|
|
|
# that ctx_request is called. This can be important if your $ctx |
|
74
|
|
|
|
|
|
|
# ends up with a reference to a shared resource or lock (for example) |
|
75
|
|
|
|
|
|
|
# which you want to clean up in test teardown - if the $ctx is still |
|
76
|
|
|
|
|
|
|
# closed over then you're stuffed... |
|
77
|
|
|
|
|
|
|
my $ctx = $ctx_closed_over; |
|
78
|
|
|
|
|
|
|
undef $ctx_closed_over; |
|
79
|
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
return ( $res, $ctx ); |
|
81
|
78
|
|
|
|
|
173
|
}; |
|
82
|
78
|
|
|
|
|
161
|
} |
|
83
|
|
|
|
|
|
|
|
|
84
|
78
|
|
|
|
|
411
|
my $build_exports = sub { |
|
85
|
128
|
|
|
|
|
834
|
my ($self, $meth, $args, $defaults) = @_; |
|
86
|
|
|
|
|
|
|
my $class = $args->{class}; |
|
87
|
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
my $request = $self->_build_request_export({ |
|
89
|
|
|
|
|
|
|
class => $class, |
|
90
|
|
|
|
|
|
|
remote => $ENV{CATALYST_SERVER}, |
|
91
|
|
|
|
|
|
|
}); |
|
92
|
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
my $get = $self->_build_get_export({ request => $request }); |
|
94
|
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
my $ctx_request = $self->_build_ctx_request_export({ |
|
96
|
|
|
|
|
|
|
class => $class, |
|
97
|
|
|
|
|
|
|
request => $request, |
|
98
|
|
|
|
|
|
|
}); |
|
99
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
return { |
|
101
|
|
|
|
|
|
|
request => $request, |
|
102
|
|
|
|
|
|
|
get => $get, |
|
103
|
|
|
|
|
|
|
ctx_request => $ctx_request, |
|
104
|
|
|
|
|
|
|
content_like => sub { |
|
105
|
|
|
|
|
|
|
my $action = shift; |
|
106
|
|
|
|
|
|
|
return Test::More->builder->like($get->($action),@_); |
|
107
|
|
|
|
|
|
|
}, |
|
108
|
|
|
|
|
|
|
action_ok => sub { |
|
109
|
9
|
|
|
9
|
|
6100
|
my $action = shift; |
|
110
|
9
|
|
|
|
|
86
|
my $meth = $request->($action)->request->method; |
|
111
|
|
|
|
|
|
|
my @args = @_ ? @_ : ("$meth $action returns successfully"); |
|
112
|
|
|
|
|
|
|
return Test::More->builder->ok($request->($action)->is_success,@args); |
|
113
|
3
|
|
|
3
|
|
1677
|
}, |
|
114
|
3
|
|
|
|
|
11
|
action_redirect => sub { |
|
115
|
3
|
100
|
|
|
|
119
|
my $action = shift; |
|
116
|
3
|
|
|
|
|
38
|
my $meth = $request->($action)->request->method; |
|
117
|
|
|
|
|
|
|
my @args = @_ ? @_ : ("$meth $action returns a redirect"); |
|
118
|
|
|
|
|
|
|
return Test::More->builder->ok($request->($action)->is_redirect,@args); |
|
119
|
1
|
|
|
1
|
|
461
|
}, |
|
120
|
1
|
|
|
|
|
4
|
action_notfound => sub { |
|
121
|
1
|
50
|
|
|
|
43
|
my $action = shift; |
|
122
|
1
|
|
|
|
|
10
|
my $meth = $request->($action)->request->method; |
|
123
|
|
|
|
|
|
|
my @args = @_ ? @_ : ("$meth $action returns a 404"); |
|
124
|
|
|
|
|
|
|
return Test::More->builder->is_eq($request->($action)->code,404,@args); |
|
125
|
2
|
|
|
2
|
|
1140
|
}, |
|
126
|
2
|
|
|
|
|
10
|
contenttype_is => sub { |
|
127
|
2
|
100
|
|
|
|
65
|
my $action = shift; |
|
128
|
2
|
|
|
|
|
19
|
my $res = $request->($action); |
|
129
|
|
|
|
|
|
|
return Test::More->builder->is_eq(scalar($res->content_type),@_); |
|
130
|
|
|
|
|
|
|
}, |
|
131
|
1
|
|
|
1
|
|
455
|
}; |
|
132
|
1
|
|
|
|
|
5
|
}; |
|
133
|
1
|
|
|
|
|
14
|
|
|
134
|
|
|
|
|
|
|
our $default_host; |
|
135
|
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
{ |
|
137
|
|
|
|
|
|
|
my $import = Sub::Exporter::build_exporter({ |
|
138
|
|
|
|
|
|
|
groups => [ all => $build_exports ], |
|
139
|
|
|
|
|
|
|
into_level => 1, |
|
140
|
|
|
|
|
|
|
}); |
|
141
|
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
my ($self, $class, $opts) = @_; |
|
144
|
|
|
|
|
|
|
Carp::carp( |
|
145
|
|
|
|
|
|
|
qq{Importing Catalyst::Test without an application name is deprecated:\n |
|
146
|
|
|
|
|
|
|
Instead of saying: use Catalyst::Test; |
|
147
|
|
|
|
|
|
|
say: use Catalyst::Test (); # If you don't want to import a test app right now. |
|
148
|
128
|
|
|
128
|
|
5809
|
or say: use Catalyst::Test 'MyApp'; # If you do want to import a test app.\n\n}) |
|
149
|
128
|
100
|
|
|
|
1160
|
unless $class; |
|
150
|
|
|
|
|
|
|
$import->($self, '-all' => { class => $class }); |
|
151
|
|
|
|
|
|
|
$opts = {} unless ref $opts eq 'HASH'; |
|
152
|
|
|
|
|
|
|
$default_host = $opts->{default_host} if exists $opts->{default_host}; |
|
153
|
|
|
|
|
|
|
return 1; |
|
154
|
|
|
|
|
|
|
} |
|
155
|
128
|
|
|
|
|
635
|
} |
|
156
|
128
|
100
|
|
|
|
99468
|
|
|
157
|
128
|
100
|
|
|
|
650
|
=head1 NAME |
|
158
|
128
|
|
|
|
|
430719
|
|
|
159
|
|
|
|
|
|
|
Catalyst::Test - Test Catalyst Applications |
|
160
|
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
162
|
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
# Helper |
|
164
|
|
|
|
|
|
|
script/test.pl |
|
165
|
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
# Tests |
|
167
|
|
|
|
|
|
|
use Catalyst::Test 'TestApp'; |
|
168
|
|
|
|
|
|
|
my $content = get('index.html'); # Content as string |
|
169
|
|
|
|
|
|
|
my $response = request('index.html'); # HTTP::Response object |
|
170
|
|
|
|
|
|
|
my($res, $c) = ctx_request('index.html'); # HTTP::Response & context object |
|
171
|
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
use HTTP::Request::Common; |
|
173
|
|
|
|
|
|
|
my $response = request POST '/foo', [ |
|
174
|
|
|
|
|
|
|
bar => 'baz', |
|
175
|
|
|
|
|
|
|
something => 'else' |
|
176
|
|
|
|
|
|
|
]; |
|
177
|
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
# Run tests against a remote server |
|
179
|
|
|
|
|
|
|
CATALYST_SERVER='http://localhost:3000/' prove -r -l lib/ t/ |
|
180
|
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
use Catalyst::Test 'TestApp'; |
|
182
|
|
|
|
|
|
|
use Test::More tests => 1; |
|
183
|
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
ok( get('/foo') =~ /bar/ ); |
|
185
|
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
# mock virtual hosts |
|
187
|
|
|
|
|
|
|
use Catalyst::Test 'MyApp', { default_host => 'myapp.com' }; |
|
188
|
|
|
|
|
|
|
like( get('/whichhost'), qr/served by myapp.com/ ); |
|
189
|
|
|
|
|
|
|
like( get( '/whichhost', { host => 'yourapp.com' } ), qr/served by yourapp.com/ ); |
|
190
|
|
|
|
|
|
|
{ |
|
191
|
|
|
|
|
|
|
local $Catalyst::Test::default_host = 'otherapp.com'; |
|
192
|
|
|
|
|
|
|
like( get('/whichhost'), qr/served by otherapp.com/ ); |
|
193
|
|
|
|
|
|
|
} |
|
194
|
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
196
|
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
This module allows you to make requests to a Catalyst application either without |
|
198
|
|
|
|
|
|
|
a server, by simulating the environment of an HTTP request using |
|
199
|
|
|
|
|
|
|
L<HTTP::Request::AsCGI> or remotely if you define the CATALYST_SERVER |
|
200
|
|
|
|
|
|
|
environment variable. This module also adds a few Catalyst-specific |
|
201
|
|
|
|
|
|
|
testing methods as displayed in the method section. |
|
202
|
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
The L<get|/"$content = get( ... )"> and L<request|/"$res = request( ... );"> |
|
204
|
|
|
|
|
|
|
functions take either a URI or an L<HTTP::Request> object. |
|
205
|
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
=head1 INLINE TESTS WILL NO LONGER WORK |
|
207
|
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
While it used to be possible to inline a whole test app into a C<.t> file for |
|
209
|
|
|
|
|
|
|
a distribution, this will no longer work. |
|
210
|
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
The convention is to place your L<Catalyst> test apps into C<t/lib> in your |
|
212
|
|
|
|
|
|
|
distribution. E.g.: C<t/lib/TestApp.pm>, C<t/lib/TestApp/Controller/Root.pm>, |
|
213
|
|
|
|
|
|
|
etc.. Multiple test apps can be used in this way. |
|
214
|
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
Then write your C<.t> files like so: |
|
216
|
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
use strict; |
|
218
|
|
|
|
|
|
|
use warnings; |
|
219
|
|
|
|
|
|
|
use FindBin '$Bin'; |
|
220
|
|
|
|
|
|
|
use lib "$Bin/lib"; |
|
221
|
|
|
|
|
|
|
use Test::More tests => 6; |
|
222
|
|
|
|
|
|
|
use Catalyst::Test 'TestApp'; |
|
223
|
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
=head1 METHODS |
|
225
|
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
=head2 $content = get( ... ) |
|
227
|
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
Returns the content. |
|
229
|
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
my $content = get('foo/bar?test=1'); |
|
231
|
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
Note that this method doesn't follow redirects, so to test for a |
|
233
|
|
|
|
|
|
|
correctly redirecting page you'll need to use a combination of this |
|
234
|
|
|
|
|
|
|
method and the L<request|/"$res = request( ... );"> method below: |
|
235
|
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
my $res = request('/'); # redirects to /y |
|
237
|
|
|
|
|
|
|
warn $res->header('location'); |
|
238
|
|
|
|
|
|
|
use URI; |
|
239
|
|
|
|
|
|
|
my $uri = URI->new($res->header('location')); |
|
240
|
|
|
|
|
|
|
is ( $uri->path , '/y'); |
|
241
|
|
|
|
|
|
|
my $content = get($uri->path); |
|
242
|
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
Note also that the content is returned as raw bytes, without any attempt |
|
244
|
|
|
|
|
|
|
to decode it into characters. |
|
245
|
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
=head2 $res = request( ... ); |
|
247
|
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
Returns an L<HTTP::Response> object. Accepts an optional hashref for request |
|
249
|
|
|
|
|
|
|
header configuration; currently only supports setting 'host' value. |
|
250
|
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
my $res = request('foo/bar?test=1'); |
|
252
|
|
|
|
|
|
|
my $virtual_res = request('foo/bar?test=1', {host => 'virtualhost.com'}); |
|
253
|
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
Alternately, you can pass in an L<HTTP::Request::Common> object to set arbitrary |
|
255
|
|
|
|
|
|
|
request headers. |
|
256
|
|
|
|
|
|
|
|
|
257
|
|
|
|
|
|
|
my $res = request(GET '/foo/bar', |
|
258
|
|
|
|
|
|
|
X-Foo => 'Bar', |
|
259
|
|
|
|
|
|
|
Authorization => 'Bearer JWT_HERE', |
|
260
|
|
|
|
|
|
|
... |
|
261
|
|
|
|
|
|
|
); |
|
262
|
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
=head2 ($res, $c) = ctx_request( ... ); |
|
264
|
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
Works exactly like L<request|/"$res = request( ... );">, except it also returns the Catalyst context object, |
|
266
|
|
|
|
|
|
|
C<$c>. Note that this only works for local requests. |
|
267
|
|
|
|
|
|
|
|
|
268
|
|
|
|
|
|
|
=cut |
|
269
|
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
my $args = shift; |
|
271
|
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
my $request = Catalyst::Utils::request(shift); |
|
273
|
|
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
my %extra_env; |
|
275
|
|
|
|
|
|
|
_customize_request($request, \%extra_env, @_); |
|
276
|
908
|
|
|
908
|
|
2358
|
$args->{mangle_request}->($request) if $args->{mangle_request}; |
|
277
|
|
|
|
|
|
|
|
|
278
|
908
|
|
|
|
|
4581
|
my $ret; |
|
279
|
|
|
|
|
|
|
test_psgi |
|
280
|
908
|
|
|
|
|
2129
|
%{ $args }, |
|
281
|
908
|
|
|
|
|
4461
|
app => sub { $args->{app}->({ %{ $_[0] }, %extra_env }) }, |
|
282
|
908
|
50
|
|
|
|
3287
|
client => sub { |
|
283
|
|
|
|
|
|
|
my ($psgi_app) = @_; |
|
284
|
908
|
|
|
|
|
1757
|
my $resp = $psgi_app->($request); |
|
285
|
|
|
|
|
|
|
$args->{mangle_response}->($resp) if $args->{mangle_response}; |
|
286
|
908
|
|
|
|
|
10227
|
$ret = $resp; |
|
287
|
908
|
|
|
908
|
|
847156
|
}; |
|
|
908
|
|
|
|
|
15687
|
|
|
288
|
|
|
|
|
|
|
|
|
289
|
908
|
|
|
908
|
|
812177
|
return $ret; |
|
290
|
908
|
|
|
|
|
2842
|
} |
|
291
|
907
|
50
|
|
|
|
131126
|
|
|
292
|
907
|
|
|
|
|
68978
|
my $class = shift; |
|
293
|
908
|
|
|
|
|
2036
|
|
|
294
|
|
|
|
|
|
|
return _request({ |
|
295
|
907
|
|
|
|
|
22979
|
app => ref($class) eq "CODE" ? $class : $class->_finalized_psgi_app, |
|
296
|
|
|
|
|
|
|
mangle_response => sub { |
|
297
|
|
|
|
|
|
|
my ($resp) = @_; |
|
298
|
|
|
|
|
|
|
|
|
299
|
908
|
|
|
908
|
|
2780
|
# HTML head parsing based on LWP::UserAgent |
|
300
|
|
|
|
|
|
|
# |
|
301
|
|
|
|
|
|
|
# This is because if you make a remote request with LWP, then the |
|
302
|
|
|
|
|
|
|
# <BASE HREF="..."> from the returned HTML document will be used |
|
303
|
|
|
|
|
|
|
# to fill in $res->base, as documented in HTTP::Response. We need |
|
304
|
907
|
|
|
907
|
|
2886
|
# to support this in local test requests so that they work 'the same'. |
|
305
|
|
|
|
|
|
|
# |
|
306
|
|
|
|
|
|
|
# This is not just horrible and possibly broken, but also really |
|
307
|
|
|
|
|
|
|
# doesn't belong here. Whoever wants this should be working on |
|
308
|
|
|
|
|
|
|
# getting it into Plack::Test, or make a middleware out of it, or |
|
309
|
|
|
|
|
|
|
# whatever. Seriously - horrible. |
|
310
|
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
if (!$resp->content_type || $resp->content_is_html) { |
|
312
|
|
|
|
|
|
|
require HTML::HeadParser; |
|
313
|
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
my $parser = HTML::HeadParser->new(); |
|
315
|
|
|
|
|
|
|
$parser->xml_mode(1) if $resp->content_is_xhtml; |
|
316
|
|
|
|
|
|
|
$parser->utf8_mode(1) if $] >= 5.008 && $HTML::Parser::VERSION >= 3.40; |
|
317
|
|
|
|
|
|
|
|
|
318
|
907
|
100
|
100
|
|
|
4741
|
$parser->parse( $resp->content ); |
|
319
|
704
|
|
|
|
|
74119
|
my $h = $parser->header; |
|
320
|
|
|
|
|
|
|
for my $f ( $h->header_field_names ) { |
|
321
|
704
|
|
|
|
|
130140
|
$resp->init_header( $f, [ $h->header($f) ] ); |
|
322
|
704
|
50
|
|
|
|
85564
|
} |
|
323
|
704
|
50
|
33
|
|
|
28464
|
} |
|
324
|
|
|
|
|
|
|
# Another horrible hack to make the response headers have a |
|
325
|
704
|
|
|
|
|
2491
|
# 'status' field. This is for back-compat, but you should |
|
326
|
704
|
|
|
|
|
28765
|
# call $resp->code instead! |
|
327
|
704
|
|
|
|
|
6018
|
$resp->init_header('status', [ $resp->code ]); |
|
328
|
26
|
|
|
|
|
1212
|
}, |
|
329
|
|
|
|
|
|
|
}, @_); |
|
330
|
|
|
|
|
|
|
} |
|
331
|
|
|
|
|
|
|
|
|
332
|
|
|
|
|
|
|
my $agent; |
|
333
|
|
|
|
|
|
|
|
|
334
|
907
|
|
|
|
|
38009
|
require LWP::UserAgent; |
|
335
|
|
|
|
|
|
|
local $Plack::Test::Impl = 'ExternalServer'; |
|
336
|
908
|
50
|
|
|
|
9622
|
|
|
337
|
|
|
|
|
|
|
unless ($agent) { |
|
338
|
|
|
|
|
|
|
$agent = LWP::UserAgent->new( |
|
339
|
|
|
|
|
|
|
keep_alive => 1, |
|
340
|
|
|
|
|
|
|
max_redirect => 0, |
|
341
|
|
|
|
|
|
|
timeout => 60, |
|
342
|
0
|
|
|
0
|
|
0
|
|
|
343
|
0
|
|
|
|
|
0
|
# work around newer LWP max_redirect 0 bug |
|
344
|
|
|
|
|
|
|
# http://rt.cpan.org/Ticket/Display.html?id=40260 |
|
345
|
0
|
0
|
|
|
|
0
|
requests_redirectable => [], |
|
346
|
0
|
|
|
|
|
0
|
); |
|
347
|
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
$agent->env_proxy; |
|
349
|
|
|
|
|
|
|
} |
|
350
|
|
|
|
|
|
|
|
|
351
|
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
my $server = URI->new($ENV{CATALYST_SERVER}); |
|
353
|
|
|
|
|
|
|
if ( $server->path =~ m|^(.+)?/$| ) { |
|
354
|
|
|
|
|
|
|
my $path = $1; |
|
355
|
|
|
|
|
|
|
$server->path("$path") if $path; # need to be quoted |
|
356
|
0
|
|
|
|
|
0
|
} |
|
357
|
|
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
return _request({ |
|
359
|
|
|
|
|
|
|
ua => $agent, |
|
360
|
0
|
|
|
|
|
0
|
uri => $server, |
|
361
|
0
|
0
|
|
|
|
0
|
mangle_request => sub { |
|
362
|
0
|
|
|
|
|
0
|
my ($request) = @_; |
|
363
|
0
|
0
|
|
|
|
0
|
|
|
364
|
|
|
|
|
|
|
# the request path needs to be sanitised if $server is using a |
|
365
|
|
|
|
|
|
|
# non-root path due to potential overlap between request path and |
|
366
|
|
|
|
|
|
|
# response path. |
|
367
|
|
|
|
|
|
|
if ($server->path) { |
|
368
|
|
|
|
|
|
|
# If request path is '/', we have to add a trailing slash to the |
|
369
|
|
|
|
|
|
|
# final request URI |
|
370
|
0
|
|
|
0
|
|
0
|
my $add_trailing = ($request->uri->path eq '/' || $request->uri->path eq '') ? 1 : 0; |
|
371
|
|
|
|
|
|
|
|
|
372
|
|
|
|
|
|
|
my @sp = split '/', $server->path; |
|
373
|
|
|
|
|
|
|
my @rp = split '/', $request->uri->path; |
|
374
|
|
|
|
|
|
|
shift @sp; shift @rp; # leading / |
|
375
|
0
|
0
|
|
|
|
0
|
if (@rp) { |
|
376
|
|
|
|
|
|
|
foreach my $sp (@sp) { |
|
377
|
|
|
|
|
|
|
$sp eq $rp[0] ? shift @rp : last |
|
378
|
0
|
0
|
0
|
|
|
0
|
} |
|
379
|
|
|
|
|
|
|
} |
|
380
|
0
|
|
|
|
|
0
|
$request->uri->path(join '/', @rp); |
|
381
|
0
|
|
|
|
|
0
|
|
|
382
|
0
|
|
|
|
|
0
|
if ( $add_trailing ) { |
|
|
0
|
|
|
|
|
0
|
|
|
383
|
0
|
0
|
|
|
|
0
|
$request->uri->path( $request->uri->path . '/' ); |
|
384
|
0
|
|
|
|
|
0
|
} |
|
385
|
0
|
0
|
|
|
|
0
|
} |
|
386
|
|
|
|
|
|
|
}, |
|
387
|
|
|
|
|
|
|
}, @_); |
|
388
|
0
|
|
|
|
|
0
|
} |
|
389
|
|
|
|
|
|
|
|
|
390
|
0
|
0
|
|
|
|
0
|
for my $name (qw(local_request remote_request)) { |
|
391
|
0
|
|
|
|
|
0
|
my $fun = sub { |
|
392
|
|
|
|
|
|
|
carp <<"EOW"; |
|
393
|
|
|
|
|
|
|
Calling Catalyst::Test::${name}() directly is deprecated. |
|
394
|
|
|
|
|
|
|
|
|
395
|
0
|
|
|
|
|
0
|
Please import Catalyst::Test into your namespace and use the provided request() |
|
396
|
|
|
|
|
|
|
function instead. |
|
397
|
|
|
|
|
|
|
EOW |
|
398
|
|
|
|
|
|
|
return __PACKAGE__->can("_${name}")->(@_); |
|
399
|
|
|
|
|
|
|
}; |
|
400
|
1
|
|
|
1
|
|
242
|
|
|
401
|
|
|
|
|
|
|
no strict 'refs'; |
|
402
|
|
|
|
|
|
|
*$name = $fun; |
|
403
|
|
|
|
|
|
|
} |
|
404
|
|
|
|
|
|
|
|
|
405
|
|
|
|
|
|
|
my $request = shift; |
|
406
|
1
|
|
|
|
|
18
|
my $extra_env = shift; |
|
407
|
|
|
|
|
|
|
my $opts = pop(@_) || {}; |
|
408
|
|
|
|
|
|
|
$opts = {} unless ref($opts) eq 'HASH'; |
|
409
|
127
|
|
|
127
|
|
1171
|
if ( my $host = exists $opts->{host} ? $opts->{host} : $default_host ) { |
|
|
127
|
|
|
|
|
327
|
|
|
|
127
|
|
|
|
|
23995
|
|
|
410
|
|
|
|
|
|
|
$request->header( 'Host' => $host ); |
|
411
|
|
|
|
|
|
|
} |
|
412
|
|
|
|
|
|
|
|
|
413
|
|
|
|
|
|
|
if (my $extra = $opts->{extra_env}) { |
|
414
|
913
|
|
|
913
|
|
2099
|
@{ $extra_env }{keys %{ $extra }} = values %{ $extra }; |
|
415
|
913
|
|
|
|
|
2104
|
} |
|
416
|
913
|
|
100
|
|
|
5185
|
} |
|
417
|
913
|
100
|
|
|
|
4094
|
|
|
418
|
913
|
100
|
|
|
|
4610
|
=head2 action_ok($url [, $test_name ]) |
|
|
|
100
|
|
|
|
|
|
|
419
|
27
|
|
|
|
|
135
|
|
|
420
|
|
|
|
|
|
|
Fetches the given URL and checks that the request was successful. An optional |
|
421
|
|
|
|
|
|
|
second argument can be given to specify the name of the test. |
|
422
|
913
|
100
|
|
|
|
5881
|
|
|
423
|
2
|
|
|
|
|
4
|
=head2 action_redirect($url [, $test_name ]) |
|
|
2
|
|
|
|
|
6
|
|
|
|
2
|
|
|
|
|
6
|
|
|
|
2
|
|
|
|
|
8
|
|
|
424
|
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
Fetches the given URL and checks that the request was a redirect. An optional |
|
426
|
|
|
|
|
|
|
second argument can be given to specify the name of the test. |
|
427
|
|
|
|
|
|
|
|
|
428
|
|
|
|
|
|
|
=head2 action_notfound($url [, $test_name ]) |
|
429
|
|
|
|
|
|
|
|
|
430
|
|
|
|
|
|
|
Fetches the given URL and checks that the request was not found. An optional |
|
431
|
|
|
|
|
|
|
second argument can be given to specify the name of the test. |
|
432
|
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
=head2 content_like( $url, $regexp [, $test_name ] ) |
|
434
|
|
|
|
|
|
|
|
|
435
|
|
|
|
|
|
|
Fetches the given URL and returns whether the content matches the regexp. An |
|
436
|
|
|
|
|
|
|
optional third argument can be given to specify the name of the test. |
|
437
|
|
|
|
|
|
|
|
|
438
|
|
|
|
|
|
|
=head2 contenttype_is($url, $type [, $test_name ]) |
|
439
|
|
|
|
|
|
|
|
|
440
|
|
|
|
|
|
|
Verify the given URL has a content type of $type and optionally specify a test name. |
|
441
|
|
|
|
|
|
|
|
|
442
|
|
|
|
|
|
|
=head1 SEE ALSO |
|
443
|
|
|
|
|
|
|
|
|
444
|
|
|
|
|
|
|
L<Catalyst>, L<Test::WWW::Mechanize::Catalyst>, |
|
445
|
|
|
|
|
|
|
L<Test::WWW::Selenium::Catalyst>, L<Test::More>, L<HTTP::Request::Common> |
|
446
|
|
|
|
|
|
|
|
|
447
|
|
|
|
|
|
|
=head1 AUTHORS |
|
448
|
|
|
|
|
|
|
|
|
449
|
|
|
|
|
|
|
Catalyst Contributors, see Catalyst.pm |
|
450
|
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
=head1 COPYRIGHT |
|
452
|
|
|
|
|
|
|
|
|
453
|
|
|
|
|
|
|
This library is free software. You can redistribute it and/or modify it under |
|
454
|
|
|
|
|
|
|
the same terms as Perl itself. |
|
455
|
|
|
|
|
|
|
|
|
456
|
|
|
|
|
|
|
=begin Pod::Coverage |
|
457
|
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
local_request |
|
459
|
|
|
|
|
|
|
|
|
460
|
|
|
|
|
|
|
remote_request |
|
461
|
|
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
=end Pod::Coverage |
|
463
|
|
|
|
|
|
|
|
|
464
|
|
|
|
|
|
|
=cut |
|
465
|
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
1; |