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 30     30   210520 use Mojo::Base -base;
  30         77  
  30         194  
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 30     30   11019 use Mojo::IOLoop;
  30         172  
  30         173  
12 30     30   202 use Mojo::JSON qw(j);
  30         89  
  30         1767  
13 30     30   11494 use Mojo::JSON::Pointer;
  30         79  
  30         290  
14 30     30   10282 use Mojo::Server;
  30         93  
  30         250  
15 30     30   14049 use Mojo::UserAgent;
  30         139  
  30         272  
16 30     30   205 use Mojo::Util qw(decode encode);
  30         74  
  30         1592  
17 30     30   16230 use Test::More ();
  30         1404198  
  30         175701  
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 124     124 1 103176 my ($self, $app) = @_;
28 124 100       617 return $self->ua->server->app unless $app;
29 4         22 $self->ua->server->app($app);
30 4         31 return $self;
31             }
32              
33             sub attr_is {
34 2     2 1 8 my ($self, $selector, $attr, $value, $desc) = @_;
35 2         11 $desc = _desc($desc, qq{exact match for attribute "$attr" at selector "$selector"});
36 2         22 return $self->test('is', $self->_attr($selector, $attr), $value, $desc);
37             }
38              
39             sub attr_isnt {
40 2     2 1 15 my ($self, $selector, $attr, $value, $desc) = @_;
41 2         9 $desc = _desc($desc, qq{no match for attribute "$attr" at selector "$selector"});
42 2         11 return $self->test('isnt', $self->_attr($selector, $attr), $value, $desc);
43             }
44              
45             sub attr_like {
46 2     2 1 8 my ($self, $selector, $attr, $regex, $desc) = @_;
47 2         10 $desc = _desc($desc, qq{similar match for attribute "$attr" at selector "$selector"});
48 2         18 return $self->test('like', $self->_attr($selector, $attr), $regex, $desc);
49             }
50              
51             sub attr_unlike {
52 2     2 1 7 my ($self, $selector, $attr, $regex, $desc) = @_;
53 2         11 $desc = _desc($desc, qq{no similar match for attribute "$attr" at selector "$selector"});
54 2         381 return $self->test('unlike', $self->_attr($selector, $attr), $regex, $desc);
55             }
56              
57             sub content_is {
58 468     468 1 5135 my ($self, $value, $desc) = @_;
59 468         1575 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 4121 my ($self, $value, $desc) = @_;
64 2         9 return $self->test('isnt', $self->tx->res->text, $value, _desc($desc, 'no match for content'));
65             }
66              
67             sub content_like {
68 111     111 1 4304 my ($self, $regex, $desc) = @_;
69 111         389 return $self->test('like', $self->tx->res->text, $regex, _desc($desc, 'content is similar'));
70             }
71              
72             sub content_type_is {
73 160     160 1 4413 my ($self, $type, $desc) = @_;
74 160         477 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 3810 my ($self, $type, $desc) = @_;
79 2         17 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 4017 my ($self, $regex, $desc) = @_;
84 7         31 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 4057 my ($self, $regex, $desc) = @_;
89 3         13 $desc = _desc($desc, 'Content-Type is not similar');
90 3         17 return $self->test('unlike', $self->tx->res->headers->content_type, $regex, $desc);
91             }
92              
93             sub content_unlike {
94 11     11 1 4086 my ($self, $regex, $desc) = @_;
95 11         48 return $self->test('unlike', $self->tx->res->text, $regex, _desc($desc, 'content is not similar'));
96             }
97              
98 5     5 1 3148 sub delete_ok { shift->_build_ok(DELETE => @_) }
99              
100             sub element_count_is {
101 4     4 1 20 my ($self, $selector, $count, $desc) = @_;
102 4         17 my $size = $self->tx->res->dom->find($selector)->size;
103 4         31 return $self->test('is', $size, $count, _desc($desc, qq{element count for selector "$selector"}));
104             }
105              
106             sub element_exists {
107 23     23 1 71 my ($self, $selector, $desc) = @_;
108 23         105 $desc = _desc($desc, qq{element for selector "$selector" exists});
109 23         125 return $self->test('ok', $self->tx->res->dom->at($selector), $desc);
110             }
111              
112             sub element_exists_not {
113 14     14 1 59 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 72 my $self = shift;
119 34 50       106 $self->tx->finish(@_) if $self->tx->is_websocket;
120 34         250 Mojo::IOLoop->one_tick while !$self->{finished};
121 34         291 return $self->test('ok', 1, 'closed WebSocket');
122             }
123              
124             sub finished_ok {
125 11     11 1 43 my ($self, $code) = @_;
126 11         82 Mojo::IOLoop->one_tick while !$self->{finished};
127 11 50       68 Test::More::diag "WebSocket closed with status $self->{finished}[0]" unless my $ok = $self->{finished}[0] == $code;
128 11         55 return $self->test('ok', $ok, "WebSocket closed with status $code");
129             }
130              
131 593     593 1 579688 sub get_ok { shift->_build_ok(GET => @_) }
132 3     3 1 5381 sub head_ok { shift->_build_ok(HEAD => @_) }
133              
134             sub header_exists {
135 4     4 1 3887 my ($self, $name, $desc) = @_;
136 4         11 return $self->test('ok', !!@{$self->tx->res->headers->every_header($name)}, _desc($desc, qq{header "$name" exists}));
  4         15  
137             }
138              
139             sub header_exists_not {
140 10     10 1 5677 my ($self, $name, $desc) = @_;
141 10         27 return $self->test('ok', !@{$self->tx->res->headers->every_header($name)}, _desc($desc, qq{no "$name" header}));
  10         35  
142             }
143              
144             sub header_is {
145 367     367 1 5888 my ($self, $name, $value, $desc) = @_;
146 367   100     1141 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 3748 my ($self, $name, $value, $desc) = @_;
151 6   50     38 $desc = _desc($desc, "not $name: " . ($value // ''));
152 6         30 return $self->test('isnt', $self->tx->res->headers->header($name), $value, $desc);
153             }
154              
155             sub header_like {
156 11     11 1 3911 my ($self, $name, $regex, $desc) = @_;
157 11         53 $desc = _desc($desc, "$name is similar");
158 11         4504 return $self->test('like', $self->tx->res->headers->header($name), $regex, $desc);
159             }
160              
161             sub header_unlike {
162 8     8 1 4013 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 7 my ($self, $p, $desc) = @_;
168 2         9 $desc = _desc($desc, qq{has value for JSON Pointer "$p"});
169 2         10 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 6 my ($self, $p, $desc) = @_;
174 2         7 $desc = _desc($desc, qq{has no value for JSON Pointer "$p"});
175 2         7 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 110 my $self = shift;
180 47 100       164 my ($p, $data) = @_ > 1 ? (shift, shift) : ('', shift);
181 47         167 my $desc = _desc(shift, qq{exact match for JSON Pointer "$p"});
182 47         173 return $self->test('is_deeply', $self->tx->res->json($p), $data, $desc);
183             }
184              
185             sub json_like {
186 3     3 1 13 my ($self, $p, $regex, $desc) = @_;
187 3         12 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 10 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 10 my ($self, $p, $desc) = @_;
197 3         9 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       36 my ($p, $data) = @_ > 1 ? (shift, shift) : ('', shift);
203 10         34 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 9 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 9 my ($self, $p, $regex, $desc) = @_;
213 2         11 $desc = _desc($desc, qq{no similar match for JSON Pointer "$p"});
214 2         16 return $self->test('unlike', $self->_json(get => $p), $regex, $desc);
215             }
216              
217             sub json_unlike {
218 2     2 1 8 my ($self, $p, $regex, $desc) = @_;
219 2         10 $desc = _desc($desc, qq{no similar match for JSON Pointer "$p"});
220 2         7 return $self->test('unlike', $self->tx->res->json($p), $regex, $desc);
221             }
222              
223             sub message_is {
224 55     55 1 182 my ($self, $value, $desc) = @_;
225 55         146 return $self->_message('is', $value, _desc($desc, 'exact match for message'));
226             }
227              
228             sub message_isnt {
229 4     4 1 16 my ($self, $value, $desc) = @_;
230 4         17 return $self->_message('isnt', $value, _desc($desc, 'no match for message'));
231             }
232              
233             sub message_like {
234 8     8 1 34 my ($self, $regex, $desc) = @_;
235 8         28 return $self->_message('like', $regex, _desc($desc, 'message is similar'));
236             }
237              
238             sub message_ok {
239 67     67 1 205 my ($self, $desc) = @_;
240 67         244 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         16 return $self->_message('unlike', $regex, _desc($desc, 'message is not similar'));
246             }
247              
248             sub new {
249 37     37 1 7661 my $self = shift->SUPER::new;
250              
251 37 100       313 return $self unless my $app = shift;
252              
253 4 100       25 my @cfg = @_ ? {config => {config_override => 1, %{shift()}}} : ();
  1         9  
254 4 100       38 return $self->app(Mojo::Server->new->build_app($app, @cfg)) unless ref $app;
255             return $self->app(
256 2 0       44 $app->isa('Mojolicious') ? @cfg ? $app->config($cfg[0]{config}) : $app : Mojo::Server->new->load_app($app, @cfg));
    50          
257             }
258              
259 3     3 1 5368 sub options_ok { shift->_build_ok(OPTIONS => @_) }
260              
261             sub or {
262 5     5 1 33 my ($self, $cb) = @_;
263 5 100       15 $self->$cb unless $self->success;
264 5         24 return $self;
265             }
266              
267 6     6 1 6882 sub patch_ok { shift->_build_ok(PATCH => @_) }
268 96     96 1 134880 sub post_ok { shift->_build_ok(POST => @_) }
269 6     6 1 5225 sub put_ok { shift->_build_ok(PUT => @_) }
270              
271 6     6 1 48 sub request_ok { shift->_request_ok($_[0], $_[0]->req->url->to_string) }
272              
273             sub reset_session {
274 6     6 1 6191 my $self = shift;
275 6         22 $self->ua->cookie_jar->empty;
276 6         24 return $self->tx(undef);
277             }
278              
279             sub send_ok {
280 53     53 1 174 my ($self, $msg, $desc) = @_;
281              
282 53         134 $desc = _desc($desc, 'send message');
283 53 50       175 return $self->test('ok', 0, $desc) unless $self->tx->is_websocket;
284              
285 53     53   179 $self->tx->send($msg => sub { Mojo::IOLoop->stop });
  53         225  
286 53         307 Mojo::IOLoop->start;
287 53         312 return $self->test('ok', 1, $desc);
288             }
289              
290             sub status_is {
291 705     705 1 5790 my ($self, $status, $desc) = @_;
292 705         2562 $desc = _desc($desc, "$status " . $self->tx->res->default_message($status));
293 705         2483 return $self->test('is', $self->tx->res->code, $status, $desc);
294             }
295              
296             sub status_isnt {
297 2     2 1 6 my ($self, $status, $desc) = @_;
298 2         9 $desc = _desc($desc, "not $status " . $self->tx->res->default_message($status));
299 2         9 return $self->test('isnt', $self->tx->res->code, $status, $desc);
300             }
301              
302             sub test {
303 3061     3061 1 9308 my ($self, $name, @args) = @_;
304 3061         6001 local $Test::Builder::Level = $Test::Builder::Level + 3;
305 3061         8912 return $self->success(!!$self->handler->($name, @args));
306             }
307              
308             sub text_is {
309 56     56 1 152 my ($self, $selector, $value, $desc) = @_;
310 56         182 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         12 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 27 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 4 my ($self, $selector, $regex, $desc) = @_;
325 1         6 $desc = _desc($desc, qq{no similar match for selector "$selector"});
326 1         5 return $self->test('unlike', $self->_text($selector), $regex, $desc);
327             }
328              
329             sub websocket_ok {
330 40     40 1 146586 my $self = shift;
331 40         193 return $self->_request_ok($self->ua->build_websocket_tx(@_), $_[0]);
332             }
333              
334             sub _attr {
335 8     8   25 my ($self, $selector, $attr) = @_;
336 8 50       31 return undef unless my $e = $self->tx->res->dom->at($selector);
337 8   50     26 return $e->attr($attr) // '';
338             }
339              
340             sub _build_ok {
341 712     712   1991 my ($self, $method, $url) = (shift, shift, shift);
342 712         1439 local $Test::Builder::Level = $Test::Builder::Level + 1;
343 712         2621 return $self->_request_ok($self->ua->build_tx($method, $url, @_), $url);
344             }
345              
346 3016   66 3016   15027 sub _desc { encode 'UTF-8', shift || shift }
347              
348             sub _handler {
349 3014     3014   7479 my ($name, @args) = @_;
350 3014         19297 return Test::More->can($name)->(@args);
351             }
352              
353             sub _json {
354 20     20   46 my ($self, $method, $p) = @_;
355 20   50     31 return Mojo::JSON::Pointer->new(j(@{$self->message // []}[1]))->$method($p);
  20         54  
356             }
357              
358             sub _message {
359 71     71   234 my ($self, $name, $value, $desc) = @_;
360 71         161 local $Test::Builder::Level = $Test::Builder::Level + 1;
361 71   50     96 my ($type, $msg) = @{$self->message // []};
  71         195  
362              
363             # Type check
364 71 100       271 if (ref $value eq 'HASH') {
365 10 100       42 my $expect = exists $value->{text} ? 'text' : 'binary';
366 10         29 $value = $value->{$expect};
367 10 100 50     58 $msg = '' unless ($type // '') eq $expect;
368             }
369              
370             # Decode text frame if there is no type check
371 61 100 50     313 else { $msg = decode 'UTF-8', $msg if ($type // '') eq 'text' }
372              
373 71   50     337 return $self->test($name, $msg // '', $value, $desc);
374             }
375              
376             sub _request_ok {
377 758     758   2068 my ($self, $tx, $url) = @_;
378              
379 758         1663 local $Test::Builder::Level = $Test::Builder::Level + 1;
380              
381             # Establish WebSocket connection
382 758 100       2265 if ($tx->req->is_handshake) {
383 42         232 @$self{qw(finished messages)} = (undef, []);
384             $self->ua->start(
385             $tx => sub {
386 42     42   138 my ($ua, $tx) = @_;
387 42 50       182 $self->{finished} = [] unless $self->tx($tx)->tx->is_websocket;
388 42         261 $tx->on(finish => sub { shift; $self->{finished} = [@_] });
  42         596  
  42         157  
389 42         241 $tx->on(binary => sub { push @{$self->{messages}}, [binary => pop] });
  10         46  
  10         46  
390 42         212 $tx->on(text => sub { push @{$self->{messages}}, [text => pop] });
  59         132  
  59         292  
391 42         331 Mojo::IOLoop->stop;
392             }
393 42         153 );
394 42         211 Mojo::IOLoop->start;
395              
396 42         281 return $self->test('ok', $self->tx->is_websocket, _desc("WebSocket handshake with $url"));
397             }
398              
399             # Perform request
400 716         2980 $self->tx($self->ua->start($tx));
401 716         2103 my $err = $self->tx->error;
402 716 50 66     4707 Test::More::diag $err->{message} if !(my $ok = !$err->{message} || $err->{code}) && $err;
      33        
403 716         1642 return $self->test('ok', $ok, _desc("@{[uc $tx->req->method]} $url"));
  716         1945  
404             }
405              
406             sub _text {
407 65 100   65   173 return undef unless my $e = shift->tx->res->dom->at(shift);
408 64         227 return $e->text;
409             }
410              
411             sub _wait {
412 67     67   147 my $self = shift;
413 67   100     265 Mojo::IOLoop->one_tick while !$self->{finished} && !@{$self->{messages}};
  222         1148  
414 67         255 return $self->message(shift @{$self->{messages}})->message;
  67         329  
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