File Coverage

blib/lib/RapidApp/Test/Client.pm
Criterion Covered Total %
statement 90 98 91.8
branch 20 28 71.4
condition 5 12 41.6
subroutine 25 29 86.2
pod 0 16 0.0
total 140 183 76.5


line stmt bran cond sub pod time code
1             package RapidApp::Test::Client;
2              
3 4     4   22 use strict;
  4         10  
  4         126  
4 4     4   23 use warnings;
  4         7  
  4         95  
5              
6             # Object class for simulating RapidApp HTTP client sessions
7              
8 4     4   1696 use Moo;
  4         30892  
  4         16  
9 4     4   17364 use Types::Standard qw(:all);
  4         243675  
  4         38  
10              
11 4     4   160386 use RapidApp;
  4         8  
  4         126  
12 4     4   23 use Scalar::Util qw(blessed);
  4         10  
  4         236  
13 4     4   23 use Time::HiRes qw(gettimeofday tv_interval);
  4         8  
  4         28  
14 4     4   3487 use LWP::UserAgent;
  4         67501  
  4         147  
15 4     4   37 use HTTP::Request::Common;
  4         7  
  4         250  
16 4     4   25 use JSON qw(decode_json);
  4         7  
  4         37  
17 4     4   420 use Try::Tiny;
  4         7  
  4         4480  
18              
19             # shorthand aliases:
20 0     0 0 0 sub lreq { (shift)->last_request }
21 8     8 0 116 sub lres { (shift)->last_response }
22              
23             has 'ajax_request_headers', is => 'ro', default => sub {{
24             'X-RapidApp-RequestContentType' => 'JSON',
25             'X-RapidApp-VERSION' => $RapidApp::VERSION,
26             'X-Requested-With' => 'XMLHttpRequest',
27             'Content-Type' => 'application/x-www-form-urlencoded; charset=UTF-8',
28             }}, isa => HashRef;
29              
30             has 'request_num', is => 'rw', default => sub{0}, isa => Int;
31             has 'last_request', is => 'rw', default => sub{undef}, isa => Maybe[InstanceOf['HTTP::Request']];
32             has 'last_response', is => 'rw', default => sub{undef}, isa => Maybe[InstanceOf['HTTP::Response']];
33             has 'last_request_started', is => 'rw', default => sub{undef};
34             has 'last_request_elapsed', is => 'rw', default => sub{undef}, isa => Maybe[Str];
35             has 'last_url', is => 'rw', default => sub{undef}, isa => Maybe[Str];
36             has 'cookie', is => 'rw', default => sub{undef}, isa => Maybe[Str];
37              
38             # i.e. http://localhost:3000
39             has 'base_url', is => 'ro', isa => Maybe[Str], default => sub {undef};
40              
41             has 'agent_string', is => 'ro', lazy => 1, default => sub {
42             my $self = shift;
43             return (ref $self);
44             }, isa => Str;
45              
46             has 'request_caller', is => 'ro', lazy => 1, default => sub {
47             my $self = shift;
48            
49             # create in a closure:
50             my $ua = LWP::UserAgent->new;
51             $ua->agent($self->agent_string);
52            
53             return sub {
54             my $request = shift;
55             return $ua->request($request);
56             }
57             }, isa => CodeRef;
58              
59             sub make_request {
60 26     26 0 89 my ($self, $req) = @_;
61            
62 26         546 $self->last_request(undef);
63 26         1129 $self->last_response(undef);
64 26         1138 $self->last_request_elapsed(undef);
65 26         921 $self->request_num( $self->request_num + 1 );
66            
67 26         992 $self->last_request_started([gettimeofday]);
68            
69 26 100       430 $req->header( Cookie => $self->cookie ) if ($self->cookie);
70            
71 26         1909 my $res = $self->request_caller->( $self->last_request($req) );
72            
73             # Record the response unless the request_caller already did:
74 26 50       555 $self->record_response( $res ) unless ($self->last_response);
75            
76 26         275 return $res;
77             }
78              
79             sub record_response {
80 26     26 0 43672 my ($self, $res) = @_;
81 26 50       604 die "last_response already defined" if ($self->last_response);
82 26         784 $self->last_response( $res );
83 26         1050 $self->cookie( $res->header('Set-Cookie') );
84 26         2504 $self->last_request_elapsed(sprintf("%0.5f sec",tv_interval(
85             $self->last_request_started
86             )));
87 26         2254 return $res;
88             }
89              
90             sub normalize_url {
91 26     26 0 80 my ($self, $url) = @_;
92            
93 26 50 33     167 $url = join('',$self->base_url,$url) if (
94             $self->base_url &&
95             $url =~ /^\// #<-- starts with '/'
96             );
97              
98 26         657 return $self->last_url($url);
99             }
100              
101              
102             sub get_request {
103 8     8 0 30 my ($self, $url, $headers) = @_;
104 8         30 $url = $self->normalize_url($url);
105 8         388 my $req = GET($url);
106 8 100       1136 $req->header( %$headers ) if ($headers);
107 8         324 $self->make_request($req);
108             }
109              
110             sub post_request {
111 18     18 0 85 my ($self, $url, $params, $headers) = @_;
112 18         69 $url = $self->normalize_url($url);
113 18 100       740 my $arr_arg = ref($params) eq 'HASH' ? [%$params] : $params;
114 18         114 my $req = POST($url,$params);
115 18 100       12238 $req->header( %$headers ) if ($headers);
116 18         1840 $self->make_request($req);
117             }
118              
119             sub last_request_is_ajax {
120 26     26 0 54 my $self = shift;
121 26 50       375 my $req = $self->last_request or return 0;
122 26         226 my $req_with = $req->header('X-Requested-With');
123 26   66     1459 return $req_with && $req_with eq 'XMLHttpRequest';
124             }
125              
126             sub last_request_type {
127 26     26 0 238 my $self = shift;
128 26 50       346 return '(none)' unless ($self->last_request);
129 26 100       207 return $self->last_request_is_ajax ? 'Ajax' : 'Browser';
130             }
131              
132             sub last_response_set_cookie {
133 26     26 0 62 my $self = shift;
134 26 50       337 my $res = $self->last_response or return 0;
135 26         238 return $res->header('Set-Cookie');
136             }
137              
138             sub describe_request {
139 26     26 0 71 my $self = shift;
140 26 50       443 my $req = $self->last_request or return '(no request)';
141            
142 26         808 my @list = (
143             ' <r', $self->request_num,'> ',
144             $self->last_request_type,
145             '->', $req->method, '(\'',$req->uri->path,'\')',
146             );
147            
148             # If we already have the response, include the elapsed time:
149 26 50       1328 push @list,(' [',$self->last_request_elapsed,']')
150             if ($self->last_response);
151            
152 26 100       670 push @list, ' **set-cookie**' if ($self->last_response_set_cookie);
153            
154 26         1344 return join('',@list);
155             }
156              
157             #####################
158              
159              
160             # Simulate an Ajax POST request as if it was generated by the
161             # RapidApp/ExtJS JavaScript client/browser to a JSON-encoded
162             # resource. Decodes and returns the JSON as perl ref
163             sub ajax_post_decode {
164 13     13 0 46 my ($self, $url, $params) = @_;
165 13         87 my $res = $self->post_request($url, $params, $self->ajax_request_headers);
166 13   66 13   100 return try{decode_json($res->decoded_content)} || $res->decoded_content;
  13         415  
167             }
168              
169              
170             sub ajax_get_raw {
171 2     2 0 8 my ($self, $url) = @_;
172 2         15 my $res = $self->get_request($url, $self->ajax_request_headers);
173 2         11 return $res->decoded_content;
174             }
175              
176              
177             sub ajax_get_decode {
178 0     0 0 0 my ($self, $url) = @_;
179 0         0 my $content = $self->ajax_get_raw($url);
180 0   0 0   0 return try{decode_json($content)} || $content;
  0         0  
181             }
182              
183             sub browser_get_raw {
184 3     3 0 10 my ($self, $url) = @_;
185 3         27 my $res = $self->get_request($url);
186 3         21 return $res->decoded_content;
187             }
188              
189             sub browser_post_raw {
190 0     0 0   my ($self, $url, $params) = @_;
191 0           my $res = $self->post_request($url,$params);
192 0           return $res->decoded_content;
193             }
194              
195              
196             1;