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   205403 use Mojo::Base -base;
  30         75  
  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   10817 use Mojo::IOLoop;
  30         112  
  30         140  
12 30     30   193 use Mojo::JSON qw(j);
  30         77  
  30         1678  
13 30     30   10795 use Mojo::JSON::Pointer;
  30         95  
  30         271  
14 30     30   10206 use Mojo::Server;
  30         74  
  30         235  
15 30     30   13024 use Mojo::UserAgent;
  30         138  
  30         272  
16 30     30   219 use Mojo::Util qw(decode encode);
  30         78  
  30         1496  
17 30     30   15216 use Test::More ();
  30         1330771  
  30         168577  
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 99053 my ($self, $app) = @_;
28 124 100       620 return $self->ua->server->app unless $app;
29 4         22 $self->ua->server->app($app);
30 4         30 return $self;
31             }
32              
33             sub attr_is {
34 2     2 1 8 my ($self, $selector, $attr, $value, $desc) = @_;
35 2         10 $desc = _desc($desc, qq{exact match for attribute "$attr" at selector "$selector"});
36 2         15 return $self->test('is', $self->_attr($selector, $attr), $value, $desc);
37             }
38              
39             sub attr_isnt {
40 2     2 1 8 my ($self, $selector, $attr, $value, $desc) = @_;
41 2         9 $desc = _desc($desc, qq{no match for attribute "$attr" at selector "$selector"});
42 2         21 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         19 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         11 $desc = _desc($desc, qq{no similar match for attribute "$attr" at selector "$selector"});
54 2         242 return $self->test('unlike', $self->_attr($selector, $attr), $regex, $desc);
55             }
56              
57             sub content_is {
58 468     468 1 5041 my ($self, $value, $desc) = @_;
59 468         1371 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 3729 my ($self, $value, $desc) = @_;
64 2         13 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 4224 my ($self, $regex, $desc) = @_;
69 111         362 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 4301 my ($self, $type, $desc) = @_;
74 160         554 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 3787 my ($self, $type, $desc) = @_;
79 2         10 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 3830 my ($self, $regex, $desc) = @_;
84 7         23 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 4002 my ($self, $regex, $desc) = @_;
89 3         11 $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 3999 my ($self, $regex, $desc) = @_;
95 11         45 return $self->test('unlike', $self->tx->res->text, $regex, _desc($desc, 'content is not similar'));
96             }
97              
98 5     5 1 3024 sub delete_ok { shift->_build_ok(DELETE => @_) }
99              
100             sub element_count_is {
101 4     4 1 15 my ($self, $selector, $count, $desc) = @_;
102 4         12 my $size = $self->tx->res->dom->find($selector)->size;
103 4         28 return $self->test('is', $size, $count, _desc($desc, qq{element count for selector "$selector"}));
104             }
105              
106             sub element_exists {
107 23     23 1 65 my ($self, $selector, $desc) = @_;
108 23         98 $desc = _desc($desc, qq{element for selector "$selector" exists});
109 23         82 return $self->test('ok', $self->tx->res->dom->at($selector), $desc);
110             }
111              
112             sub element_exists_not {
113 14     14 1 49 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 80 my $self = shift;
119 34 50       105 $self->tx->finish(@_) if $self->tx->is_websocket;
120 34         187 Mojo::IOLoop->one_tick while !$self->{finished};
121 34         156 return $self->test('ok', 1, 'closed WebSocket');
122             }
123              
124             sub finished_ok {
125 11     11 1 32 my ($self, $code) = @_;
126 11         58 Mojo::IOLoop->one_tick while !$self->{finished};
127 11 50       52 Test::More::diag "WebSocket closed with status $self->{finished}[0]" unless my $ok = $self->{finished}[0] == $code;
128 11         52 return $self->test('ok', $ok, "WebSocket closed with status $code");
129             }
130              
131 593     593 1 602052 sub get_ok { shift->_build_ok(GET => @_) }
132 3     3 1 5587 sub head_ok { shift->_build_ok(HEAD => @_) }
133              
134             sub header_exists {
135 4     4 1 3782 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         14  
137             }
138              
139             sub header_exists_not {
140 10     10 1 3871 my ($self, $name, $desc) = @_;
141 10         24 return $self->test('ok', !@{$self->tx->res->headers->every_header($name)}, _desc($desc, qq{no "$name" header}));
  10         42  
142             }
143              
144             sub header_is {
145 367     367 1 4828 my ($self, $name, $value, $desc) = @_;
146 367   100     1065 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 3636 my ($self, $name, $value, $desc) = @_;
151 6   50     41 $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 3946 my ($self, $name, $regex, $desc) = @_;
157 11         49 $desc = _desc($desc, "$name is similar");
158 11         3604 return $self->test('like', $self->tx->res->headers->header($name), $regex, $desc);
159             }
160              
161             sub header_unlike {
162 8     8 1 4314 my ($self, $name, $regex, $desc) = @_;
163 8         31 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 10 my ($self, $p, $desc) = @_;
168 2         10 $desc = _desc($desc, qq{has value for JSON Pointer "$p"});
169 2         11 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         8 $desc = _desc($desc, qq{has no value for JSON Pointer "$p"});
175 2         17 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 124 my $self = shift;
180 47 100       235 my ($p, $data) = @_ > 1 ? (shift, shift) : ('', shift);
181 47         207 my $desc = _desc(shift, qq{exact match for JSON Pointer "$p"});
182 47         182 return $self->test('is_deeply', $self->tx->res->json($p), $data, $desc);
183             }
184              
185             sub json_like {
186 3     3 1 12 my ($self, $p, $regex, $desc) = @_;
187 3         13 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         11 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 7 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 24 my $self = shift;
202 10 100       37 my ($p, $data) = @_ > 1 ? (shift, shift) : ('', shift);
203 10         29 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 7 my ($self, $p, $regex, $desc) = @_;
208 2         8 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 8 my ($self, $p, $regex, $desc) = @_;
213 2         9 $desc = _desc($desc, qq{no similar match for JSON Pointer "$p"});
214 2         11 return $self->test('unlike', $self->_json(get => $p), $regex, $desc);
215             }
216              
217             sub json_unlike {
218 2     2 1 7 my ($self, $p, $regex, $desc) = @_;
219 2         8 $desc = _desc($desc, qq{no similar match for JSON Pointer "$p"});
220 2         11 return $self->test('unlike', $self->tx->res->json($p), $regex, $desc);
221             }
222              
223             sub message_is {
224 55     55 1 169 my ($self, $value, $desc) = @_;
225 55         143 return $self->_message('is', $value, _desc($desc, 'exact match for message'));
226             }
227              
228             sub message_isnt {
229 4     4 1 12 my ($self, $value, $desc) = @_;
230 4         16 return $self->_message('isnt', $value, _desc($desc, 'no match for message'));
231             }
232              
233             sub message_like {
234 8     8 1 30 my ($self, $regex, $desc) = @_;
235 8         32 return $self->_message('like', $regex, _desc($desc, 'message is similar'));
236             }
237              
238             sub message_ok {
239 67     67 1 168 my ($self, $desc) = @_;
240 67         186 return $self->test('ok', !!$self->_wait, _desc($desc, 'message received'));
241             }
242              
243             sub message_unlike {
244 4     4 1 15 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 7595 my $self = shift->SUPER::new;
250              
251 37 100       257 return $self unless my $app = shift;
252              
253 4 100       22 my @cfg = @_ ? {config => {config_override => 1, %{shift()}}} : ();
  1         10  
254 4 100       37 return $self->app(Mojo::Server->new->build_app($app, @cfg)) unless ref $app;
255             return $self->app(
256 2 0       35 $app->isa('Mojolicious') ? @cfg ? $app->config($cfg[0]{config}) : $app : Mojo::Server->new->load_app($app, @cfg));
    50          
257             }
258              
259 3     3 1 5294 sub options_ok { shift->_build_ok(OPTIONS => @_) }
260              
261             sub or {
262 5     5 1 47 my ($self, $cb) = @_;
263 5 100       15 $self->$cb unless $self->success;
264 5         31 return $self;
265             }
266              
267 6     6 1 7446 sub patch_ok { shift->_build_ok(PATCH => @_) }
268 96     96 1 153477 sub post_ok { shift->_build_ok(POST => @_) }
269 6     6 1 5453 sub put_ok { shift->_build_ok(PUT => @_) }
270              
271 6     6 1 57 sub request_ok { shift->_request_ok($_[0], $_[0]->req->url->to_string) }
272              
273             sub reset_session {
274 6     6 1 5864 my $self = shift;
275 6         24 $self->ua->cookie_jar->empty;
276 6         26 return $self->tx(undef);
277             }
278              
279             sub send_ok {
280 53     53 1 138 my ($self, $msg, $desc) = @_;
281              
282 53         135 $desc = _desc($desc, 'send message');
283 53 50       160 return $self->test('ok', 0, $desc) unless $self->tx->is_websocket;
284              
285 53     53   134 $self->tx->send($msg => sub { Mojo::IOLoop->stop });
  53         227  
286 53         234 Mojo::IOLoop->start;
287 53         192 return $self->test('ok', 1, $desc);
288             }
289              
290             sub status_is {
291 705     705 1 5685 my ($self, $status, $desc) = @_;
292 705         2636 $desc = _desc($desc, "$status " . $self->tx->res->default_message($status));
293 705         2621 return $self->test('is', $self->tx->res->code, $status, $desc);
294             }
295              
296             sub status_isnt {
297 2     2 1 8 my ($self, $status, $desc) = @_;
298 2         9 $desc = _desc($desc, "not $status " . $self->tx->res->default_message($status));
299 2         10 return $self->test('isnt', $self->tx->res->code, $status, $desc);
300             }
301              
302             sub test {
303 3061     3061 1 9147 my ($self, $name, @args) = @_;
304 3061         5841 local $Test::Builder::Level = $Test::Builder::Level + 3;
305 3061         8368 return $self->success(!!$self->handler->($name, @args));
306             }
307              
308             sub text_is {
309 56     56 1 198 my ($self, $selector, $value, $desc) = @_;
310 56         239 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 9 my ($self, $selector, $value, $desc) = @_;
315 2         10 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 19 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 6 my ($self, $selector, $regex, $desc) = @_;
325 1         5 $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 140476 my $self = shift;
331 40         153 return $self->_request_ok($self->ua->build_websocket_tx(@_), $_[0]);
332             }
333              
334             sub _attr {
335 8     8   22 my ($self, $selector, $attr) = @_;
336 8 50       27 return undef unless my $e = $self->tx->res->dom->at($selector);
337 8   50     34 return $e->attr($attr) // '';
338             }
339              
340             sub _build_ok {
341 712     712   1933 my ($self, $method, $url) = (shift, shift, shift);
342 712         1490 local $Test::Builder::Level = $Test::Builder::Level + 1;
343 712         2417 return $self->_request_ok($self->ua->build_tx($method, $url, @_), $url);
344             }
345              
346 3016   66 3016   13984 sub _desc { encode 'UTF-8', shift || shift }
347              
348             sub _handler {
349 3014     3014   7339 my ($name, @args) = @_;
350 3014         18236 return Test::More->can($name)->(@args);
351             }
352              
353             sub _json {
354 20     20   42 my ($self, $method, $p) = @_;
355 20   50     33 return Mojo::JSON::Pointer->new(j(@{$self->message // []}[1]))->$method($p);
  20         55  
356             }
357              
358             sub _message {
359 71     71   233 my ($self, $name, $value, $desc) = @_;
360 71         146 local $Test::Builder::Level = $Test::Builder::Level + 1;
361 71   50     129 my ($type, $msg) = @{$self->message // []};
  71         191  
362              
363             # Type check
364 71 100       238 if (ref $value eq 'HASH') {
365 10 100       41 my $expect = exists $value->{text} ? 'text' : 'binary';
366 10         24 $value = $value->{$expect};
367 10 100 50     46 $msg = '' unless ($type // '') eq $expect;
368             }
369              
370             # Decode text frame if there is no type check
371 61 100 50     347 else { $msg = decode 'UTF-8', $msg if ($type // '') eq 'text' }
372              
373 71   50     266 return $self->test($name, $msg // '', $value, $desc);
374             }
375              
376             sub _request_ok {
377 758     758   2024 my ($self, $tx, $url) = @_;
378              
379 758         1803 local $Test::Builder::Level = $Test::Builder::Level + 1;
380              
381             # Establish WebSocket connection
382 758 100       2243 if ($tx->req->is_handshake) {
383 42         190 @$self{qw(finished messages)} = (undef, []);
384             $self->ua->start(
385             $tx => sub {
386 42     42   99 my ($ua, $tx) = @_;
387 42 50       160 $self->{finished} = [] unless $self->tx($tx)->tx->is_websocket;
388 42         215 $tx->on(finish => sub { shift; $self->{finished} = [@_] });
  42         71  
  42         150  
389 42         181 $tx->on(binary => sub { push @{$self->{messages}}, [binary => pop] });
  10         19  
  10         57  
390 42         187 $tx->on(text => sub { push @{$self->{messages}}, [text => pop] });
  59         104  
  59         296  
391 42         240 Mojo::IOLoop->stop;
392             }
393 42         142 );
394 42         176 Mojo::IOLoop->start;
395              
396 42         207 return $self->test('ok', $self->tx->is_websocket, _desc("WebSocket handshake with $url"));
397             }
398              
399             # Perform request
400 716         2607 $self->tx($self->ua->start($tx));
401 716         2059 my $err = $self->tx->error;
402 716 50 66     4462 Test::More::diag $err->{message} if !(my $ok = !$err->{message} || $err->{code}) && $err;
      33        
403 716         1547 return $self->test('ok', $ok, _desc("@{[uc $tx->req->method]} $url"));
  716         2299  
404             }
405              
406             sub _text {
407 65 100   65   221 return undef unless my $e = shift->tx->res->dom->at(shift);
408 64         235 return $e->text;
409             }
410              
411             sub _wait {
412 67     67   126 my $self = shift;
413 67   100     229 Mojo::IOLoop->one_tick while !$self->{finished} && !@{$self->{messages}};
  223         1136  
414 67         163 return $self->message(shift @{$self->{messages}})->message;
  67         261  
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