File Coverage

blib/lib/Test/Mojo.pm
Criterion Covered Total %
statement 226 226 100.0
branch 33 42 78.5
condition 17 28 60.7
subroutine 78 78 100.0
pod 59 59 100.0
total 413 433 95.3


line stmt bran cond sub pod time code
1             package Test::Mojo;
2 31     31   221690 use Mojo::Base -base;
  31         85  
  31         209  
3              
4             # "Amy: He knows when you are sleeping.
5             # Professor: He knows when you're on the can.
6             # Leela: He'll hunt you down and blast your ass from here to Pakistan.
7             # Zoidberg: Oh.
8             # Hermes: You'd better not breathe, you'd better not move.
9             # Bender: You're better off dead, I'm telling you, dude.
10             # Fry: Santa Claus is gunning you down!"
11 31     31   11486 use Mojo::IOLoop;
  31         106  
  31         166  
12 31     31   214 use Mojo::JSON qw(j);
  31         67  
  31         1750  
13 31     31   11913 use Mojo::JSON::Pointer;
  31         121  
  31         298  
14 31     31   10791 use Mojo::Server;
  31         78  
  31         252  
15 31     31   14328 use Mojo::UserAgent;
  31         133  
  31         287  
16 31     31   248 use Mojo::Util qw(decode encode);
  31         89  
  31         1688  
17 31     31   17214 use Test::More ();
  31         1489704  
  31         186379  
18              
19             has handler => sub { \&_handler };
20             has [qw(message success tx)];
21             has ua => sub { Mojo::UserAgent->new(insecure => 1)->ioloop(Mojo::IOLoop->singleton) };
22              
23             # Silent or loud tests
24             $ENV{MOJO_LOG_LEVEL} ||= $ENV{HARNESS_IS_VERBOSE} ? 'trace' : 'fatal';
25              
26             sub app {
27 126     126 1 100774 my ($self, $app) = @_;
28 126 100       605 return $self->ua->server->app unless $app;
29 4         25 $self->ua->server->app($app);
30 4         25 return $self;
31             }
32              
33             sub attr_is {
34 2     2 1 9 my ($self, $selector, $attr, $value, $desc) = @_;
35 2         11 $desc = _desc($desc, qq{exact match for attribute "$attr" at selector "$selector"});
36 2         13 return $self->test('is', $self->_attr($selector, $attr), $value, $desc);
37             }
38              
39             sub attr_isnt {
40 2     2 1 9 my ($self, $selector, $attr, $value, $desc) = @_;
41 2         10 $desc = _desc($desc, qq{no match for attribute "$attr" at selector "$selector"});
42 2         8 return $self->test('isnt', $self->_attr($selector, $attr), $value, $desc);
43             }
44              
45             sub attr_like {
46 2     2 1 10 my ($self, $selector, $attr, $regex, $desc) = @_;
47 2         9 $desc = _desc($desc, qq{similar match for attribute "$attr" at selector "$selector"});
48 2         23 return $self->test('like', $self->_attr($selector, $attr), $regex, $desc);
49             }
50              
51             sub attr_unlike {
52 2     2 1 8 my ($self, $selector, $attr, $regex, $desc) = @_;
53 2         10 $desc = _desc($desc, qq{no similar match for attribute "$attr" at selector "$selector"});
54 2         12 return $self->test('unlike', $self->_attr($selector, $attr), $regex, $desc);
55             }
56              
57             sub content_is {
58 474     474 1 5210 my ($self, $value, $desc) = @_;
59 474         1459 return $self->test('is', $self->tx->res->text, $value, _desc($desc, 'exact match for content'));
60             }
61              
62             sub content_isnt {
63 2     2 1 3774 my ($self, $value, $desc) = @_;
64 2         10 return $self->test('isnt', $self->tx->res->text, $value, _desc($desc, 'no match for content'));
65             }
66              
67             sub content_like {
68 112     112 1 4863 my ($self, $regex, $desc) = @_;
69 112         382 return $self->test('like', $self->tx->res->text, $regex, _desc($desc, 'content is similar'));
70             }
71              
72             sub content_type_is {
73 161     161 1 4405 my ($self, $type, $desc) = @_;
74 161         544 return $self->test('is', $self->tx->res->headers->content_type, $type, _desc($desc, "Content-Type: $type"));
75             }
76              
77             sub content_type_isnt {
78 2     2 1 3774 my ($self, $type, $desc) = @_;
79 2         13 return $self->test('isnt', $self->tx->res->headers->content_type, $type, _desc($desc, "not Content-Type: $type"));
80             }
81              
82             sub content_type_like {
83 7     7 1 3945 my ($self, $regex, $desc) = @_;
84 7         26 return $self->test('like', $self->tx->res->headers->content_type, $regex, _desc($desc, 'Content-Type is similar'));
85             }
86              
87             sub content_type_unlike {
88 3     3 1 4104 my ($self, $regex, $desc) = @_;
89 3         16 $desc = _desc($desc, 'Content-Type is not similar');
90 3         40 return $self->test('unlike', $self->tx->res->headers->content_type, $regex, $desc);
91             }
92              
93             sub content_unlike {
94 12     12 1 4119 my ($self, $regex, $desc) = @_;
95 12         57 return $self->test('unlike', $self->tx->res->text, $regex, _desc($desc, 'content is not similar'));
96             }
97              
98 5     5 1 3282 sub delete_ok { shift->_build_ok(DELETE => @_) }
99              
100             sub element_count_is {
101 4     4 1 18 my ($self, $selector, $count, $desc) = @_;
102 4         21 my $size = $self->tx->res->dom->find($selector)->size;
103 4         35 return $self->test('is', $size, $count, _desc($desc, qq{element count for selector "$selector"}));
104             }
105              
106             sub element_exists {
107 25     25 1 85 my ($self, $selector, $desc) = @_;
108 25         113 $desc = _desc($desc, qq{element for selector "$selector" exists});
109 25         105 return $self->test('ok', $self->tx->res->dom->at($selector), $desc);
110             }
111              
112             sub element_exists_not {
113 14     14 1 46 my ($self, $selector, $desc) = @_;
114 14         53 return $self->test('ok', !$self->tx->res->dom->at($selector), _desc($desc, qq{no element for selector "$selector"}));
115             }
116              
117             sub finish_ok {
118 34     34 1 81 my $self = shift;
119 34 50       119 $self->tx->finish(@_) if $self->tx->is_websocket;
120 34         216 Mojo::IOLoop->one_tick while !$self->{finished};
121 34         128 return $self->test('ok', 1, 'closed WebSocket');
122             }
123              
124             sub finished_ok {
125 11     11 1 42 my ($self, $code) = @_;
126 11         58 Mojo::IOLoop->one_tick while !$self->{finished};
127 11 50       59 Test::More::diag "WebSocket closed with status $self->{finished}[0]" unless my $ok = $self->{finished}[0] == $code;
128 11         56 return $self->test('ok', $ok, "WebSocket closed with status $code");
129             }
130              
131 605     605 1 614856 sub get_ok { shift->_build_ok(GET => @_) }
132 3     3 1 4987 sub head_ok { shift->_build_ok(HEAD => @_) }
133              
134             sub header_exists {
135 4     4 1 3896 my ($self, $name, $desc) = @_;
136 4         12 return $self->test('ok', !!@{$self->tx->res->headers->every_header($name)}, _desc($desc, qq{header "$name" exists}));
  4         17  
137             }
138              
139             sub header_exists_not {
140 11     11 1 3825 my ($self, $name, $desc) = @_;
141 11         37 return $self->test('ok', !@{$self->tx->res->headers->every_header($name)}, _desc($desc, qq{no "$name" header}));
  11         48  
142             }
143              
144             sub header_is {
145 376     376 1 4894 my ($self, $name, $value, $desc) = @_;
146 376   100     1123 return $self->test('is', $self->tx->res->headers->header($name), $value, _desc($desc, "$name: " . ($value // '')));
147             }
148              
149             sub header_isnt {
150 6     6 1 3757 my ($self, $name, $value, $desc) = @_;
151 6   50     41 $desc = _desc($desc, "not $name: " . ($value // ''));
152 6         50 return $self->test('isnt', $self->tx->res->headers->header($name), $value, $desc);
153             }
154              
155             sub header_like {
156 11     11 1 3879 my ($self, $name, $regex, $desc) = @_;
157 11         51 $desc = _desc($desc, "$name is similar");
158 11         97 return $self->test('like', $self->tx->res->headers->header($name), $regex, $desc);
159             }
160              
161             sub header_unlike {
162 8     8 1 3987 my ($self, $name, $regex, $desc) = @_;
163 8         32 return $self->test('unlike', $self->tx->res->headers->header($name), $regex, _desc($desc, "$name is not similar"));
164             }
165              
166             sub json_has {
167 2     2 1 12 my ($self, $p, $desc) = @_;
168 2         12 $desc = _desc($desc, qq{has value for JSON Pointer "$p"});
169 2         20 return $self->test('ok', !!Mojo::JSON::Pointer->new($self->tx->res->json)->contains($p), $desc);
170             }
171              
172             sub json_hasnt {
173 2     2 1 7 my ($self, $p, $desc) = @_;
174 2         9 $desc = _desc($desc, qq{has no value for JSON Pointer "$p"});
175 2         15 return $self->test('ok', !Mojo::JSON::Pointer->new($self->tx->res->json)->contains($p), $desc);
176             }
177              
178             sub json_is {
179 47     47 1 112 my $self = shift;
180 47 100       191 my ($p, $data) = @_ > 1 ? (shift, shift) : ('', shift);
181 47         174 my $desc = _desc(shift, qq{exact match for JSON Pointer "$p"});
182 47         175 return $self->test('is_deeply', $self->tx->res->json($p), $data, $desc);
183             }
184              
185             sub json_like {
186 3     3 1 16 my ($self, $p, $regex, $desc) = @_;
187 3         15 return $self->test('like', $self->tx->res->json($p), $regex, _desc($desc, qq{similar match for JSON Pointer "$p"}));
188             }
189              
190             sub json_message_has {
191 3     3 1 9 my ($self, $p, $desc) = @_;
192 3         10 return $self->test('ok', $self->_json(contains => $p), _desc($desc, qq{has value for JSON Pointer "$p"}));
193             }
194              
195             sub json_message_hasnt {
196 3     3 1 8 my ($self, $p, $desc) = @_;
197 3         8 return $self->test('ok', !$self->_json(contains => $p), _desc($desc, qq{has no value for JSON Pointer "$p"}));
198             }
199              
200             sub json_message_is {
201 10     10 1 22 my $self = shift;
202 10 100       52 my ($p, $data) = @_ > 1 ? (shift, shift) : ('', shift);
203 10         31 return $self->test('is_deeply', $self->_json(get => $p), $data, _desc(shift, qq{exact match for JSON Pointer "$p"}));
204             }
205              
206             sub json_message_like {
207 2     2 1 11 my ($self, $p, $regex, $desc) = @_;
208 2         7 return $self->test('like', $self->_json(get => $p), $regex, _desc($desc, qq{similar match for JSON Pointer "$p"}));
209             }
210              
211             sub json_message_unlike {
212 2     2 1 11 my ($self, $p, $regex, $desc) = @_;
213 2         11 $desc = _desc($desc, qq{no similar match for JSON Pointer "$p"});
214 2         12 return $self->test('unlike', $self->_json(get => $p), $regex, $desc);
215             }
216              
217             sub json_unlike {
218 2     2 1 11 my ($self, $p, $regex, $desc) = @_;
219 2         38 $desc = _desc($desc, qq{no similar match for JSON Pointer "$p"});
220 2         14 return $self->test('unlike', $self->tx->res->json($p), $regex, $desc);
221             }
222              
223             sub message_is {
224 55     55 1 151 my ($self, $value, $desc) = @_;
225 55         165 return $self->_message('is', $value, _desc($desc, 'exact match for message'));
226             }
227              
228             sub message_isnt {
229 4     4 1 21 my ($self, $value, $desc) = @_;
230 4         18 return $self->_message('isnt', $value, _desc($desc, 'no match for message'));
231             }
232              
233             sub message_like {
234 8     8 1 50 my ($self, $regex, $desc) = @_;
235 8         34 return $self->_message('like', $regex, _desc($desc, 'message is similar'));
236             }
237              
238             sub message_ok {
239 67     67 1 222 my ($self, $desc) = @_;
240 67         211 return $self->test('ok', !!$self->_wait, _desc($desc, 'message received'));
241             }
242              
243             sub message_unlike {
244 4     4 1 18 my ($self, $regex, $desc) = @_;
245 4         18 return $self->_message('unlike', $regex, _desc($desc, 'message is not similar'));
246             }
247              
248             sub new {
249 38     38 1 7938 my $self = shift->SUPER::new;
250              
251 38 100       281 return $self unless my $app = shift;
252              
253 4 100       26 my @cfg = @_ ? {config => {config_override => 1, %{shift()}}} : ();
  1         12  
254 4 100       32 return $self->app(Mojo::Server->new->build_app($app, @cfg)) unless ref $app;
255             return $self->app(
256 2 0       34 $app->isa('Mojolicious') ? @cfg ? $app->config($cfg[0]{config}) : $app : Mojo::Server->new->load_app($app, @cfg));
    50          
257             }
258              
259 3     3 1 5833 sub options_ok { shift->_build_ok(OPTIONS => @_) }
260              
261             sub or {
262 5     5 1 31 my ($self, $cb) = @_;
263 5 100       14 $self->$cb unless $self->success;
264 5         18 return $self;
265             }
266              
267 6     6 1 8129 sub patch_ok { shift->_build_ok(PATCH => @_) }
268 96     96 1 132184 sub post_ok { shift->_build_ok(POST => @_) }
269 6     6 1 5319 sub put_ok { shift->_build_ok(PUT => @_) }
270              
271 6     6 1 79 sub request_ok { shift->_request_ok($_[0], $_[0]->req->url->to_string) }
272              
273             sub reset_session {
274 6     6 1 6560 my $self = shift;
275 6         25 $self->ua->cookie_jar->empty;
276 6         25 return $self->tx(undef);
277             }
278              
279             sub send_ok {
280 53     53 1 148 my ($self, $msg, $desc) = @_;
281              
282 53         140 $desc = _desc($desc, 'send message');
283 53 50       169 return $self->test('ok', 0, $desc) unless $self->tx->is_websocket;
284              
285 53     53   138 $self->tx->send($msg => sub { Mojo::IOLoop->stop });
  53         228  
286 53         255 Mojo::IOLoop->start;
287 53         234 return $self->test('ok', 1, $desc);
288             }
289              
290             sub status_is {
291 717     717 1 5731 my ($self, $status, $desc) = @_;
292 717         2532 $desc = _desc($desc, "$status " . $self->tx->res->default_message($status));
293 717         2503 return $self->test('is', $self->tx->res->code, $status, $desc);
294             }
295              
296             sub status_isnt {
297 2     2 1 7 my ($self, $status, $desc) = @_;
298 2         9 $desc = _desc($desc, "not $status " . $self->tx->res->default_message($status));
299 2         8 return $self->test('isnt', $self->tx->res->code, $status, $desc);
300             }
301              
302             sub test {
303 3106     3106 1 9223 my ($self, $name, @args) = @_;
304 3106         5790 local $Test::Builder::Level = $Test::Builder::Level + 3;
305 3106         9062 return $self->success(!!$self->handler->($name, @args));
306             }
307              
308             sub text_is {
309 56     56 1 163 my ($self, $selector, $value, $desc) = @_;
310 56         163 return $self->test('is', $self->_text($selector), $value, _desc($desc, qq{exact match for selector "$selector"}));
311             }
312              
313             sub text_isnt {
314 2     2 1 7 my ($self, $selector, $value, $desc) = @_;
315 2         8 return $self->test('isnt', $self->_text($selector), $value, _desc($desc, qq{no match for selector "$selector"}));
316             }
317              
318             sub text_like {
319 6     6 1 26 my ($self, $selector, $regex, $desc) = @_;
320 6         22 return $self->test('like', $self->_text($selector), $regex, _desc($desc, qq{similar match for selector "$selector"}));
321             }
322              
323             sub text_unlike {
324 1     1 1 14 my ($self, $selector, $regex, $desc) = @_;
325 1         9 $desc = _desc($desc, qq{no similar match for selector "$selector"});
326 1         6 return $self->test('unlike', $self->_text($selector), $regex, $desc);
327             }
328              
329             sub websocket_ok {
330 40     40 1 148658 my $self = shift;
331 40         187 return $self->_request_ok($self->ua->build_websocket_tx(@_), $_[0]);
332             }
333              
334             sub _attr {
335 8     8   18 my ($self, $selector, $attr) = @_;
336 8 50       25 return undef unless my $e = $self->tx->res->dom->at($selector);
337 8   50     33 return $e->attr($attr) // '';
338             }
339              
340             sub _build_ok {
341 724     724   2010 my ($self, $method, $url) = (shift, shift, shift);
342 724         1498 local $Test::Builder::Level = $Test::Builder::Level + 1;
343 724         2753 return $self->_request_ok($self->ua->build_tx($method, $url, @_), $url);
344             }
345              
346 3061   66 3061   14463 sub _desc { encode 'UTF-8', shift || shift }
347              
348             sub _handler {
349 3059     3059   7617 my ($name, @args) = @_;
350 3059         19467 return Test::More->can($name)->(@args);
351             }
352              
353             sub _json {
354 20     20   41 my ($self, $method, $p) = @_;
355 20   50     35 return Mojo::JSON::Pointer->new(j(@{$self->message // []}[1]))->$method($p);
  20         51  
356             }
357              
358             sub _message {
359 71     71   241 my ($self, $name, $value, $desc) = @_;
360 71         187 local $Test::Builder::Level = $Test::Builder::Level + 1;
361 71   50     110 my ($type, $msg) = @{$self->message // []};
  71         198  
362              
363             # Type check
364 71 100       237 if (ref $value eq 'HASH') {
365 10 100       49 my $expect = exists $value->{text} ? 'text' : 'binary';
366 10         29 $value = $value->{$expect};
367 10 100 50     49 $msg = '' unless ($type // '') eq $expect;
368             }
369              
370             # Decode text frame if there is no type check
371 61 100 50     304 else { $msg = decode 'UTF-8', $msg if ($type // '') eq 'text' }
372              
373 71   50     279 return $self->test($name, $msg // '', $value, $desc);
374             }
375              
376             sub _request_ok {
377 770     770   1935 my ($self, $tx, $url) = @_;
378              
379 770         1639 local $Test::Builder::Level = $Test::Builder::Level + 1;
380              
381             # Establish WebSocket connection
382 770 100       2431 if ($tx->req->is_handshake) {
383 42         209 @$self{qw(finished messages)} = (undef, []);
384             $self->ua->start(
385             $tx => sub {
386 42     42   102 my ($ua, $tx) = @_;
387 42 50       155 $self->{finished} = [] unless $self->tx($tx)->tx->is_websocket;
388 42         244 $tx->on(finish => sub { shift; $self->{finished} = [@_] });
  42         78  
  42         198  
389 42         186 $tx->on(binary => sub { push @{$self->{messages}}, [binary => pop] });
  10         33  
  10         48  
390 42         169 $tx->on(text => sub { push @{$self->{messages}}, [text => pop] });
  59         124  
  59         335  
391 42         263 Mojo::IOLoop->stop;
392             }
393 42         125 );
394 42         189 Mojo::IOLoop->start;
395              
396 42         217 return $self->test('ok', $self->tx->is_websocket, _desc("WebSocket handshake with $url"));
397             }
398              
399             # Perform request
400 728         2881 $self->tx($self->ua->start($tx));
401 728         2050 my $err = $self->tx->error;
402 728 50 66     4583 Test::More::diag $err->{message} if !(my $ok = !$err->{message} || $err->{code}) && $err;
      33        
403 728         1580 return $self->test('ok', $ok, _desc("@{[uc $tx->req->method]} $url"));
  728         1712  
404             }
405              
406             sub _text {
407 65 100   65   210 return undef unless my $e = shift->tx->res->dom->at(shift);
408 64         244 return $e->text;
409             }
410              
411             sub _wait {
412 67     67   114 my $self = shift;
413 67   100     229 Mojo::IOLoop->one_tick while !$self->{finished} && !@{$self->{messages}};
  223         1168  
414 67         185 return $self->message(shift @{$self->{messages}})->message;
  67         291  
415             }
416              
417             1;
418              
419             =encoding utf8
420              
421             =head1 NAME
422              
423             Test::Mojo - Testing Mojo
424              
425             =head1 SYNOPSIS
426              
427             use Test::More;
428             use Test::Mojo;
429              
430             my $t = Test::Mojo->new('MyApp');
431              
432             # HTML/XML
433             $t->get_ok('/welcome')->status_is(200)->text_is('div#message' => 'Hello!');
434              
435             # JSON
436             $t->post_ok('/search.json' => form => {q => 'Perl'})
437             ->status_is(200)
438             ->header_is('Server' => 'Mojolicious (Perl)')
439             ->header_isnt('X-Bender' => 'Bite my shiny metal ass!')
440             ->json_is('/results/4/title' => 'Perl rocks!')
441             ->json_like('/results/7/title' => qr/Perl/);
442              
443             # WebSocket
444             $t->websocket_ok('/echo')
445             ->send_ok('hello')
446             ->message_ok
447             ->message_is('echo: hello')
448             ->finish_ok;
449              
450             done_testing();
451              
452             =head1 DESCRIPTION
453              
454             L is a test user agent based on L, it is usually used together with L to test
455             L applications. Just run your tests with L.
456              
457             $ prove -l -v
458             $ prove -l -v t/foo.t
459              
460             If it is not already defined, the C environment variable will be set to C or C, depending
461             on the value of the C environment variable. And to make it esier to test HTTPS/WSS web services
462             L will be activated by default for L.
463              
464             See L for more.
465              
466             =head1 ATTRIBUTES
467              
468             L implements the following attributes.
469              
470             =head2 handler
471              
472             my $cb = $t->handler;
473             $t = $t->handler(sub {...});
474              
475             A callback to connect L with L.
476              
477             $t->handler(sub ($name, @args) {
478             return Test::More->can($name)->(@args);
479             });
480              
481             =head2 message
482              
483             my $msg = $t->message;
484             $t = $t->message([text => $bytes]);
485              
486             Current WebSocket message represented as an array reference containing the frame type and payload.
487              
488             # More specific tests
489             use Mojo::JSON qw(decode_json);
490             my $hash = decode_json $t->message->[1];
491             is ref $hash, 'HASH', 'right reference';
492             is $hash->{foo}, 'bar', 'right value';
493              
494             # Test custom message
495             $t->message([binary => $bytes])
496             ->json_message_has('/foo/bar')
497             ->json_message_hasnt('/bar')
498             ->json_message_is('/foo/baz' => {yada => [1, 2, 3]});
499              
500             =head2 success
501              
502             my $bool = $t->success;
503             $t = $t->success($bool);
504              
505             True if the last test was successful.
506              
507             # Build custom tests
508             my $location_is = sub ($t, $value, $desc = '') {
509             $desc ||= "Location: $value";
510             local $Test::Builder::Level = $Test::Builder::Level + 1;
511             return $t->success(is($t->tx->res->headers->location, $value, $desc));
512             };
513             $t->get_ok('/')
514             ->status_is(302)
515             ->$location_is('https://mojolicious.org')
516             ->or(sub { diag 'Must have been Joel!' });
517              
518             =head2 tx
519              
520             my $tx = $t->tx;
521             $t = $t->tx(Mojo::Transaction::HTTP->new);
522              
523             Current transaction, usually a L or L object.
524              
525             # More specific tests
526             is $t->tx->res->json->{foo}, 'bar', 'right value';
527             ok $t->tx->res->content->is_multipart, 'multipart content';
528             is $t->tx->previous->res->code, 302, 'right status';
529              
530             =head2 ua
531              
532             my $ua = $t->ua;
533             $t = $t->ua(Mojo::UserAgent->new);
534              
535             User agent used for testing, defaults to a L object.
536              
537             # Allow redirects
538             $t->ua->max_redirects(10);
539             $t->get_ok('/redirect')->status_is(200)->content_like(qr/redirected/);
540              
541             # Switch protocol from HTTP to HTTPS
542             $t->ua->server->url('https');
543             $t->get_ok('/secure')->status_is(200)->content_like(qr/secure/);
544              
545             # Use absolute URL for request with Basic authentication
546             my $url = $t->ua->server->url->userinfo('sri:secr3t')->path('/secrets.json');
547             $t->post_ok($url => json => {limit => 10})
548             ->status_is(200)
549             ->json_is('/1/content', 'Mojo rocks!');
550              
551             # Customize all transactions (including followed redirects)
552             $t->ua->on(start => sub ($ua, $tx) { $tx->req->headers->accept_language('en-US') });
553             $t->get_ok('/hello')->status_is(200)->content_like(qr/Howdy/);
554              
555             =head1 METHODS
556              
557             L inherits all methods from L and implements the following new ones.
558              
559             =head2 app
560              
561             my $app = $t->app;
562             $t = $t->app(Mojolicious->new);
563              
564             Access application with L.
565              
566             # Change log level
567             $t->app->log->level('fatal');
568              
569             # Test application directly
570             is $t->app->defaults->{foo}, 'bar', 'right value';
571             ok $t->app->routes->find('echo')->is_websocket, 'WebSocket route';
572             my $c = $t->app->build_controller;
573             ok $c->render(template => 'foo'), 'rendering was successful';
574             is $c->res->status, 200, 'right status';
575             is $c->res->body, 'Foo!', 'right content';
576              
577             # Change application behavior
578             $t->app->hook(before_dispatch => sub ($c) {
579             $c->render(text => 'This request did not reach the router.') if $c->req->url->path->contains('/user');
580             });
581             $t->get_ok('/user')->status_is(200)->content_like(qr/not reach the router/);
582              
583             # Extract additional information
584             my $stash;
585             $t->app->hook(after_dispatch => sub ($c) { $stash = $c->stash });
586             $t->get_ok('/hello')->status_is(200);
587             is $stash->{foo}, 'bar', 'right value';
588              
589             =head2 attr_is
590              
591             $t = $t->attr_is('img.cat', 'alt', 'Grumpy cat');
592             $t = $t->attr_is('img.cat', 'alt', 'Grumpy cat', 'right alt text');
593              
594             Checks text content of attribute with L at the CSS selectors first matching HTML/XML element for
595             exact match with L.
596              
597             =head2 attr_isnt
598              
599             $t = $t->attr_isnt('img.cat', 'alt', 'Calm cat');
600             $t = $t->attr_isnt('img.cat', 'alt', 'Calm cat', 'different alt text');
601              
602             Opposite of L.
603              
604             =head2 attr_like
605              
606             $t = $t->attr_like('img.cat', 'alt', qr/Grumpy/);
607             $t = $t->attr_like('img.cat', 'alt', qr/Grumpy/, 'right alt text');
608              
609             Checks text content of attribute with L at the CSS selectors first matching HTML/XML element for
610             similar match with L.
611              
612             =head2 attr_unlike
613              
614             $t = $t->attr_unlike('img.cat', 'alt', qr/Calm/);
615             $t = $t->attr_unlike('img.cat', 'alt', qr/Calm/, 'different alt text');
616              
617             Opposite of L.
618              
619             =head2 content_is
620              
621             $t = $t->content_is('working!');
622             $t = $t->content_is('working!', 'right content');
623              
624             Check response content for exact match after retrieving it from L.
625              
626             =head2 content_isnt
627              
628             $t = $t->content_isnt('working!');
629             $t = $t->content_isnt('working!', 'different content');
630              
631             Opposite of L.
632              
633             =head2 content_like
634              
635             $t = $t->content_like(qr/working!/);
636             $t = $t->content_like(qr/working!/, 'right content');
637              
638             Check response content for similar match after retrieving it from L.
639              
640             =head2 content_type_is
641              
642             $t = $t->content_type_is('text/html');
643             $t = $t->content_type_is('text/html', 'right content type');
644              
645             Check response C header for exact match.
646              
647             =head2 content_type_isnt
648              
649             $t = $t->content_type_isnt('text/html');
650             $t = $t->content_type_isnt('text/html', 'different content type');
651              
652             Opposite of L.
653              
654             =head2 content_type_like
655              
656             $t = $t->content_type_like(qr/text/);
657             $t = $t->content_type_like(qr/text/, 'right content type');
658              
659             Check response C header for similar match.
660              
661             =head2 content_type_unlike
662              
663             $t = $t->content_type_unlike(qr/text/);
664             $t = $t->content_type_unlike(qr/text/, 'different content type');
665              
666             Opposite of L.
667              
668             =head2 content_unlike
669              
670             $t = $t->content_unlike(qr/working!/);
671             $t = $t->content_unlike(qr/working!/, 'different content');
672              
673             Opposite of L.
674              
675             =head2 delete_ok
676              
677             $t = $t->delete_ok('http://example.com/foo');
678             $t = $t->delete_ok('/foo');
679             $t = $t->delete_ok('/foo' => {Accept => '*/*'} => 'Content!');
680             $t = $t->delete_ok('/foo' => {Accept => '*/*'} => form => {a => 'b'});
681             $t = $t->delete_ok('/foo' => {Accept => '*/*'} => json => {a => 'b'});
682              
683             Perform a C request and check for transport errors, takes the same arguments as L,
684             except for the callback.
685              
686             =head2 element_count_is
687              
688             $t = $t->element_count_is('div.foo[x=y]', 5);
689             $t = $t->element_count_is('html body div', 30, 'thirty elements');
690              
691             Checks the number of HTML/XML elements matched by the CSS selector with L.
692              
693             =head2 element_exists
694              
695             $t = $t->element_exists('div.foo[x=y]');
696             $t = $t->element_exists('html head title', 'has a title');
697              
698             Checks for existence of the CSS selectors first matching HTML/XML element with L.
699              
700             # Check attribute values
701             $t->get_ok('/login')
702             ->element_exists('label[for=email]')
703             ->element_exists('input[name=email][type=text][value*="example.com"]')
704             ->element_exists('label[for=pass]')
705             ->element_exists('input[name=pass][type=password]')
706             ->element_exists('input[type=submit][value]');
707              
708             =head2 element_exists_not
709              
710             $t = $t->element_exists_not('div.foo[x=y]');
711             $t = $t->element_exists_not('html head title', 'has no title');
712              
713             Opposite of L.
714              
715             =head2 finish_ok
716              
717             $t = $t->finish_ok;
718             $t = $t->finish_ok(1000);
719             $t = $t->finish_ok(1003 => 'Cannot accept data!');
720              
721             Close WebSocket connection gracefully.
722              
723             =head2 finished_ok
724              
725             $t = $t->finished_ok(1000);
726              
727             Wait for WebSocket connection to be closed gracefully and check status.
728              
729             =head2 get_ok
730              
731             $t = $t->get_ok('http://example.com/foo');
732             $t = $t->get_ok('/foo');
733             $t = $t->get_ok('/foo' => {Accept => '*/*'} => 'Content!');
734             $t = $t->get_ok('/foo' => {Accept => '*/*'} => form => {a => 'b'});
735             $t = $t->get_ok('/foo' => {Accept => '*/*'} => json => {a => 'b'});
736              
737             Perform a C request and check for transport errors, takes the same arguments as L, except
738             for the callback.
739              
740             # Run tests against remote host
741             $t->get_ok('https://docs.mojolicious.org')->status_is(200);
742              
743             # Use relative URL for request with Basic authentication
744             $t->get_ok('//sri:secr3t@/secrets.json')
745             ->status_is(200)
746             ->json_is('/1/content', 'Mojo rocks!');
747              
748             # Run additional tests on the transaction
749             $t->get_ok('/foo')->status_is(200);
750             is $t->tx->res->dom->at('input')->val, 'whatever', 'right value';
751              
752             =head2 head_ok
753              
754             $t = $t->head_ok('http://example.com/foo');
755             $t = $t->head_ok('/foo');
756             $t = $t->head_ok('/foo' => {Accept => '*/*'} => 'Content!');
757             $t = $t->head_ok('/foo' => {Accept => '*/*'} => form => {a => 'b'});
758             $t = $t->head_ok('/foo' => {Accept => '*/*'} => json => {a => 'b'});
759              
760             Perform a C request and check for transport errors, takes the same arguments as L, except
761             for the callback.
762              
763             =head2 header_exists
764              
765             $t = $t->header_exists('ETag');
766             $t = $t->header_exists('ETag', 'header exists');
767              
768             Check if response header exists.
769              
770             =head2 header_exists_not
771              
772             $t = $t->header_exists_not('ETag');
773             $t = $t->header_exists_not('ETag', 'header is missing');
774              
775             Opposite of L.
776              
777             =head2 header_is
778              
779             $t = $t->header_is(ETag => '"abc321"');
780             $t = $t->header_is(ETag => '"abc321"', 'right header');
781              
782             Check response header for exact match.
783              
784             =head2 header_isnt
785              
786             $t = $t->header_isnt(Etag => '"abc321"');
787             $t = $t->header_isnt(ETag => '"abc321"', 'different header');
788              
789             Opposite of L.
790              
791             =head2 header_like
792              
793             $t = $t->header_like(ETag => qr/abc/);
794             $t = $t->header_like(ETag => qr/abc/, 'right header');
795              
796             Check response header for similar match.
797              
798             =head2 header_unlike
799              
800             $t = $t->header_unlike(ETag => qr/abc/);
801             $t = $t->header_unlike(ETag => qr/abc/, 'different header');
802              
803             Opposite of L.
804              
805             =head2 json_has
806              
807             $t = $t->json_has('/foo');
808             $t = $t->json_has('/minibar', 'has a minibar');
809              
810             Check if JSON response contains a value that can be identified using the given JSON Pointer with
811             L.
812              
813             =head2 json_hasnt
814              
815             $t = $t->json_hasnt('/foo');
816             $t = $t->json_hasnt('/minibar', 'no minibar');
817              
818             Opposite of L.
819              
820             =head2 json_is
821              
822             $t = $t->json_is({foo => [1, 2, 3]});
823             $t = $t->json_is('/foo' => [1, 2, 3]);
824             $t = $t->json_is('/foo/1' => 2, 'right value');
825              
826             Check the value extracted from JSON response using the given JSON Pointer with L, which defaults
827             to the root value if it is omitted.
828              
829             # Use an empty JSON Pointer to test the whole JSON response with a test description
830             $t->json_is('' => {foo => [1, 2, 3]}, 'right object');
831              
832             =head2 json_like
833              
834             $t = $t->json_like('/foo/1' => qr/^\d+$/);
835             $t = $t->json_like('/foo/1' => qr/^\d+$/, 'right value');
836              
837             Check the value extracted from JSON response using the given JSON Pointer with L for similar
838             match.
839              
840             =head2 json_message_has
841              
842             $t = $t->json_message_has('/foo');
843             $t = $t->json_message_has('/minibar', 'has a minibar');
844              
845             Check if JSON WebSocket message contains a value that can be identified using the given JSON Pointer with
846             L.
847              
848             =head2 json_message_hasnt
849              
850             $t = $t->json_message_hasnt('/foo');
851             $t = $t->json_message_hasnt('/minibar', 'no minibar');
852              
853             Opposite of L.
854              
855             =head2 json_message_is
856              
857             $t = $t->json_message_is({foo => [1, 2, 3]});
858             $t = $t->json_message_is('/foo' => [1, 2, 3]);
859             $t = $t->json_message_is('/foo/1' => 2, 'right value');
860              
861             Check the value extracted from JSON WebSocket message using the given JSON Pointer with L, which
862             defaults to the root value if it is omitted.
863              
864             =head2 json_message_like
865              
866             $t = $t->json_message_like('/foo/1' => qr/^\d+$/);
867             $t = $t->json_message_like('/foo/1' => qr/^\d+$/, 'right value');
868              
869             Check the value extracted from JSON WebSocket message using the given JSON Pointer with L for
870             similar match.
871              
872             =head2 json_message_unlike
873              
874             $t = $t->json_message_unlike('/foo/1' => qr/^\d+$/);
875             $t = $t->json_message_unlike('/foo/1' => qr/^\d+$/, 'different value');
876              
877             Opposite of L.
878              
879             =head2 json_unlike
880              
881             $t = $t->json_unlike('/foo/1' => qr/^\d+$/);
882             $t = $t->json_unlike('/foo/1' => qr/^\d+$/, 'different value');
883              
884             Opposite of L.
885              
886             =head2 message_is
887              
888             $t = $t->message_is({binary => $bytes});
889             $t = $t->message_is({text => $bytes});
890             $t = $t->message_is('working!');
891             $t = $t->message_is('working!', 'right message');
892              
893             Check WebSocket message for exact match.
894              
895             =head2 message_isnt
896              
897             $t = $t->message_isnt({binary => $bytes});
898             $t = $t->message_isnt({text => $bytes});
899             $t = $t->message_isnt('working!');
900             $t = $t->message_isnt('working!', 'different message');
901              
902             Opposite of L.
903              
904             =head2 message_like
905              
906             $t = $t->message_like({binary => qr/$bytes/});
907             $t = $t->message_like({text => qr/$bytes/});
908             $t = $t->message_like(qr/working!/);
909             $t = $t->message_like(qr/working!/, 'right message');
910              
911             Check WebSocket message for similar match.
912              
913             =head2 message_ok
914              
915             $t = $t->message_ok;
916             $t = $t->message_ok('got a message');
917              
918             Wait for next WebSocket message to arrive.
919              
920             # Wait for message and perform multiple tests on it
921             $t->websocket_ok('/time')
922             ->message_ok
923             ->message_like(qr/\d+/)
924             ->message_unlike(qr/\w+/)
925             ->finish_ok;
926              
927             =head2 message_unlike
928              
929             $t = $t->message_unlike({binary => qr/$bytes/});
930             $t = $t->message_unlike({text => qr/$bytes/});
931             $t = $t->message_unlike(qr/working!/);
932             $t = $t->message_unlike(qr/working!/, 'different message');
933              
934             Opposite of L.
935              
936             =head2 new
937              
938             my $t = Test::Mojo->new;
939             my $t = Test::Mojo->new('MyApp');
940             my $t = Test::Mojo->new('MyApp', {foo => 'bar'});
941             my $t = Test::Mojo->new(Mojo::File->new('/path/to/myapp.pl'));
942             my $t = Test::Mojo->new(Mojo::File->new('/path/to/myapp.pl'), {foo => 'bar'});
943             my $t = Test::Mojo->new(MyApp->new);
944             my $t = Test::Mojo->new(MyApp->new, {foo => 'bar'});
945              
946             Construct a new L object. In addition to a class name or L object pointing to the application
947             script, you can pass along a hash reference with configuration values that will be used to override the application
948             configuration. The special configuration value C will be set in L as well, which
949             is used to disable configuration plugins like L, L and
950             L for tests.
951              
952             # Load application script relative to the "t" directory
953             use Mojo::File qw(curfile);
954             my $t = Test::Mojo->new(curfile->dirname->sibling('myapp.pl'));
955              
956             =head2 options_ok
957              
958             $t = $t->options_ok('http://example.com/foo');
959             $t = $t->options_ok('/foo');
960             $t = $t->options_ok('/foo' => {Accept => '*/*'} => 'Content!');
961             $t = $t->options_ok('/foo' => {Accept => '*/*'} => form => {a => 'b'});
962             $t = $t->options_ok('/foo' => {Accept => '*/*'} => json => {a => 'b'});
963              
964             Perform a C request and check for transport errors, takes the same arguments as L,
965             except for the callback.
966              
967             =head2 or
968              
969             $t = $t->or(sub {...});
970              
971             Execute callback if the value of L is false.
972              
973             # Diagnostics
974             $t->get_ok('/bad')->or(sub { diag 'Must have been Glen!' })
975             ->status_is(200)->or(sub { diag $t->tx->res->dom->at('title')->text });
976              
977             =head2 patch_ok
978              
979             $t = $t->patch_ok('http://example.com/foo');
980             $t = $t->patch_ok('/foo');
981             $t = $t->patch_ok('/foo' => {Accept => '*/*'} => 'Content!');
982             $t = $t->patch_ok('/foo' => {Accept => '*/*'} => form => {a => 'b'});
983             $t = $t->patch_ok('/foo' => {Accept => '*/*'} => json => {a => 'b'});
984              
985             Perform a C request and check for transport errors, takes the same arguments as L,
986             except for the callback.
987              
988             =head2 post_ok
989              
990             $t = $t->post_ok('http://example.com/foo');
991             $t = $t->post_ok('/foo');
992             $t = $t->post_ok('/foo' => {Accept => '*/*'} => 'Content!');
993             $t = $t->post_ok('/foo' => {Accept => '*/*'} => form => {a => 'b'});
994             $t = $t->post_ok('/foo' => {Accept => '*/*'} => json => {a => 'b'});
995              
996             Perform a C request and check for transport errors, takes the same arguments as L, except
997             for the callback.
998              
999             # Test file upload
1000             my $upload = {foo => {content => 'bar', filename => 'baz.txt'}};
1001             $t->post_ok('/upload' => form => $upload)->status_is(200);
1002              
1003             # Test JSON API
1004             $t->post_ok('/hello.json' => json => {hello => 'world'})
1005             ->status_is(200)
1006             ->json_is({bye => 'world'});
1007              
1008             =head2 put_ok
1009              
1010             $t = $t->put_ok('http://example.com/foo');
1011             $t = $t->put_ok('/foo');
1012             $t = $t->put_ok('/foo' => {Accept => '*/*'} => 'Content!');
1013             $t = $t->put_ok('/foo' => {Accept => '*/*'} => form => {a => 'b'});
1014             $t = $t->put_ok('/foo' => {Accept => '*/*'} => json => {a => 'b'});
1015              
1016             Perform a C request and check for transport errors, takes the same arguments as L, except
1017             for the callback.
1018              
1019             =head2 request_ok
1020              
1021             $t = $t->request_ok(Mojo::Transaction::HTTP->new);
1022              
1023             Perform request and check for transport errors.
1024              
1025             # Request with custom method
1026             my $tx = $t->ua->build_tx(FOO => '/test.json' => json => {foo => 1});
1027             $t->request_ok($tx)->status_is(200)->json_is({success => 1});
1028              
1029             # Request with custom cookie
1030             my $tx = $t->ua->build_tx(GET => '/account');
1031             $tx->req->cookies({name => 'user', value => 'sri'});
1032             $t->request_ok($tx)->status_is(200)->text_is('head > title' => 'Hello sri');
1033              
1034             # Custom WebSocket handshake
1035             my $tx = $t->ua->build_websocket_tx('/foo');
1036             $tx->req->headers->remove('User-Agent');
1037             $t->request_ok($tx)->message_ok->message_is('bar')->finish_ok;
1038              
1039             =head2 reset_session
1040              
1041             $t = $t->reset_session;
1042              
1043             Reset user agent session.
1044              
1045             =head2 send_ok
1046              
1047             $t = $t->send_ok({binary => $bytes});
1048             $t = $t->send_ok({text => $bytes});
1049             $t = $t->send_ok({json => {test => [1, 2, 3]}});
1050             $t = $t->send_ok([$fin, $rsv1, $rsv2, $rsv3, $op, $payload]);
1051             $t = $t->send_ok($chars);
1052             $t = $t->send_ok($chars, 'sent successfully');
1053              
1054             Send message or frame via WebSocket.
1055              
1056             # Send JSON object as "Text" message
1057             $t->websocket_ok('/echo.json')
1058             ->send_ok({json => {test => 'I ♥ Mojolicious!'}})
1059             ->message_ok
1060             ->json_message_is('/test' => 'I ♥ Mojolicious!')
1061             ->finish_ok;
1062              
1063             =head2 status_is
1064              
1065             $t = $t->status_is(200);
1066             $t = $t->status_is(200, 'right status');
1067              
1068             Check response status for exact match.
1069              
1070             =head2 status_isnt
1071              
1072             $t = $t->status_isnt(200);
1073             $t = $t->status_isnt(200, 'different status');
1074              
1075             Opposite of L.
1076              
1077             =head2 test
1078              
1079             $t = $t->test('is', 'first value', 'second value', 'right value');
1080              
1081             Call L functions through L, used to implement L roles. The result will be stored in
1082             L.
1083              
1084             =head2 text_is
1085              
1086             $t = $t->text_is('div.foo[x=y]' => 'Hello!');
1087             $t = $t->text_is('html head title' => 'Hello!', 'right title');
1088              
1089             Checks text content of the CSS selectors first matching HTML/XML element for exact match with L.
1090              
1091             =head2 text_isnt
1092              
1093             $t = $t->text_isnt('div.foo[x=y]' => 'Hello!');
1094             $t = $t->text_isnt('html head title' => 'Hello!', 'different title');
1095              
1096             Opposite of L.
1097              
1098             =head2 text_like
1099              
1100             $t = $t->text_like('div.foo[x=y]' => qr/Hello/);
1101             $t = $t->text_like('html head title' => qr/Hello/, 'right title');
1102              
1103             Checks text content of the CSS selectors first matching HTML/XML element for similar match with L.
1104              
1105             =head2 text_unlike
1106              
1107             $t = $t->text_unlike('div.foo[x=y]' => qr/Hello/);
1108             $t = $t->text_unlike('html head title' => qr/Hello/, 'different title');
1109              
1110             Opposite of L.
1111              
1112             =head2 websocket_ok
1113              
1114             $t = $t->websocket_ok('http://example.com/echo');
1115             $t = $t->websocket_ok('/echo');
1116             $t = $t->websocket_ok('/echo' => {DNT => 1} => ['v1.proto']);
1117              
1118             Open a WebSocket connection with transparent handshake, takes the same arguments as L,
1119             except for the callback.
1120              
1121             # WebSocket with permessage-deflate compression
1122             $t->websocket_ok('/' => {'Sec-WebSocket-Extensions' => 'permessage-deflate'})
1123             ->send_ok('y' x 50000)
1124             ->message_ok
1125             ->message_is('z' x 50000)
1126             ->finish_ok;
1127              
1128             =head1 SEE ALSO
1129              
1130             L, L, L.
1131              
1132             =cut