File Coverage

blib/lib/Test/Mojo.pm
Criterion Covered Total %
statement 281 281 100.0
branch 36 44 81.8
condition 19 31 61.2
subroutine 98 98 100.0
pod 73 73 100.0
total 507 527 96.2


line stmt bran cond sub pod time code
1             package Test::Mojo;
2 33     33   256512 use Mojo::Base -base;
  33         56  
  33         167  
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 33     33   11460 use Mojo::IOLoop;
  33         79  
  33         125  
12 33     33   173 use Mojo::JSON qw(j);
  33         42  
  33         1726  
13 33     33   11455 use Mojo::JSON::Pointer;
  33         74  
  33         179  
14 33     33   10187 use Mojo::Server;
  33         109  
  33         189  
15 33     33   12914 use Mojo::UserAgent;
  33         116  
  33         218  
16 33     33   179 use Mojo::Util qw(decode encode);
  33         47  
  33         1381  
17 33     33   13744 use Test::More ();
  33         1791605  
  33         185327  
18              
19             has handler => sub { \&_handler };
20             has [qw(message sse 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 129     129 1 134164 my ($self, $app) = @_;
28 129 100       653 return $self->ua->server->app unless $app;
29 4         20 $self->ua->server->app($app);
30 4         21 return $self;
31             }
32              
33             sub attr_is {
34 2     2 1 7 my ($self, $selector, $attr, $value, $desc) = @_;
35 2         7 $desc = _desc($desc, qq{exact match for attribute "$attr" at selector "$selector"});
36 2         7 return $self->test('is', $self->_attr($selector, $attr), $value, $desc);
37             }
38              
39             sub attr_isnt {
40 2     2 1 5 my ($self, $selector, $attr, $value, $desc) = @_;
41 2         7 $desc = _desc($desc, qq{no match for attribute "$attr" at selector "$selector"});
42 2         7 return $self->test('isnt', $self->_attr($selector, $attr), $value, $desc);
43             }
44              
45             sub attr_like {
46 2     2 1 5 my ($self, $selector, $attr, $regex, $desc) = @_;
47 2         7 $desc = _desc($desc, qq{similar match for attribute "$attr" at selector "$selector"});
48 2         5 return $self->test('like', $self->_attr($selector, $attr), $regex, $desc);
49             }
50              
51             sub attr_unlike {
52 2     2 1 6 my ($self, $selector, $attr, $regex, $desc) = @_;
53 2         7 $desc = _desc($desc, qq{no similar match for attribute "$attr" at selector "$selector"});
54 2         6 return $self->test('unlike', $self->_attr($selector, $attr), $regex, $desc);
55             }
56              
57             sub content_is {
58 491     491 1 4618 my ($self, $value, $desc) = @_;
59 491         1400 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 3172 my ($self, $value, $desc) = @_;
64 2         8 return $self->test('isnt', $self->tx->res->text, $value, _desc($desc, 'no match for content'));
65             }
66              
67             sub content_like {
68 112     112 1 3616 my ($self, $regex, $desc) = @_;
69 112         352 return $self->test('like', $self->tx->res->text, $regex, _desc($desc, 'content is similar'));
70             }
71              
72             sub content_type_is {
73 161     161 1 3801 my ($self, $type, $desc) = @_;
74 161         565 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 3788 my ($self, $type, $desc) = @_;
79 2         8 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 3443 my ($self, $regex, $desc) = @_;
84 7         22 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 3363 my ($self, $regex, $desc) = @_;
89 3         8 $desc = _desc($desc, 'Content-Type is not similar');
90 3         13 return $self->test('unlike', $self->tx->res->headers->content_type, $regex, $desc);
91             }
92              
93             sub content_unlike {
94 12     12 1 3461 my ($self, $regex, $desc) = @_;
95 12         53 return $self->test('unlike', $self->tx->res->text, $regex, _desc($desc, 'content is not similar'));
96             }
97              
98 5     5 1 2880 sub delete_ok { shift->_build_ok(DELETE => @_) }
99              
100             sub element_count_is {
101 4     4 1 11 my ($self, $selector, $count, $desc) = @_;
102 4         14 my $size = $self->tx->res->dom->find($selector)->size;
103 4         21 return $self->test('is', $size, $count, _desc($desc, qq{element count for selector "$selector"}));
104             }
105              
106             sub element_exists {
107 25     25 1 72 my ($self, $selector, $desc) = @_;
108 25         88 $desc = _desc($desc, qq{element for selector "$selector" exists});
109 25         97 return $self->test('ok', $self->tx->res->dom->at($selector), $desc);
110             }
111              
112             sub element_exists_not {
113 14     14 1 36 my ($self, $selector, $desc) = @_;
114 14         47 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 86 my $self = shift;
119 34 50       139 $self->tx->finish(@_) if $self->tx->is_websocket;
120 34         240 Mojo::IOLoop->one_tick while !$self->{finished};
121 34         138 return $self->test('ok', 1, 'closed WebSocket');
122             }
123              
124             sub finished_ok {
125 11     11 1 46 my ($self, $code) = @_;
126 11         68 Mojo::IOLoop->one_tick while !$self->{finished};
127 11 50       55 Test::More::diag "WebSocket closed with status $self->{finished}[0]" unless my $ok = $self->{finished}[0] == $code;
128 11         60 return $self->test('ok', $ok, "WebSocket closed with status $code");
129             }
130              
131 618     618 1 680631 sub get_ok { shift->_build_ok(GET => @_) }
132              
133 14     14 1 29375 sub get_sse_ok { shift->_build_sse_ok(GET => @_) }
134              
135 3     3 1 5274 sub head_ok { shift->_build_ok(HEAD => @_) }
136              
137             sub header_exists {
138 4     4 1 3726 my ($self, $name, $desc) = @_;
139 4         8 return $self->test('ok', !!@{$self->tx->res->headers->every_header($name)}, _desc($desc, qq{header "$name" exists}));
  4         16  
140             }
141              
142             sub header_exists_not {
143 11     11 1 3404 my ($self, $name, $desc) = @_;
144 11         22 return $self->test('ok', !@{$self->tx->res->headers->every_header($name)}, _desc($desc, qq{no "$name" header}));
  11         35  
145             }
146              
147             sub header_is {
148 377     377 1 4200 my ($self, $name, $value, $desc) = @_;
149 377   100     1050 return $self->test('is', $self->tx->res->headers->header($name), $value, _desc($desc, "$name: " . ($value // '')));
150             }
151              
152             sub header_isnt {
153 6     6 1 3314 my ($self, $name, $value, $desc) = @_;
154 6   50     29 $desc = _desc($desc, "not $name: " . ($value // ''));
155 6         19 return $self->test('isnt', $self->tx->res->headers->header($name), $value, $desc);
156             }
157              
158             sub header_like {
159 11     11 1 3333 my ($self, $name, $regex, $desc) = @_;
160 11         44 $desc = _desc($desc, "$name is similar");
161 11         44 return $self->test('like', $self->tx->res->headers->header($name), $regex, $desc);
162             }
163              
164             sub header_unlike {
165 8     8 1 3397 my ($self, $name, $regex, $desc) = @_;
166 8         24 return $self->test('unlike', $self->tx->res->headers->header($name), $regex, _desc($desc, "$name is not similar"));
167             }
168              
169             sub json_has {
170 2     2 1 5 my ($self, $p, $desc) = @_;
171 2         5 $desc = _desc($desc, qq{has value for JSON Pointer "$p"});
172 2         6 return $self->test('ok', !!Mojo::JSON::Pointer->new($self->tx->res->json)->contains($p), $desc);
173             }
174              
175             sub json_hasnt {
176 2     2 1 4 my ($self, $p, $desc) = @_;
177 2         5 $desc = _desc($desc, qq{has no value for JSON Pointer "$p"});
178 2         6 return $self->test('ok', !Mojo::JSON::Pointer->new($self->tx->res->json)->contains($p), $desc);
179             }
180              
181             sub json_is {
182 47     47 1 108 my $self = shift;
183 47 100       199 my ($p, $data) = @_ > 1 ? (shift, shift) : ('', shift);
184 47         158 my $desc = _desc(shift, qq{exact match for JSON Pointer "$p"});
185 47         161 return $self->test('is_deeply', $self->tx->res->json($p), $data, $desc);
186             }
187              
188             sub json_like {
189 3     3 1 12 my ($self, $p, $regex, $desc) = @_;
190 3         12 return $self->test('like', $self->tx->res->json($p), $regex, _desc($desc, qq{similar match for JSON Pointer "$p"}));
191             }
192              
193             sub json_message_has {
194 3     3 1 11 my ($self, $p, $desc) = @_;
195 3         14 return $self->test('ok', $self->_json(contains => $p), _desc($desc, qq{has value for JSON Pointer "$p"}));
196             }
197              
198             sub json_message_hasnt {
199 3     3 1 10 my ($self, $p, $desc) = @_;
200 3         12 return $self->test('ok', !$self->_json(contains => $p), _desc($desc, qq{has no value for JSON Pointer "$p"}));
201             }
202              
203             sub json_message_is {
204 10     10 1 27 my $self = shift;
205 10 100       55 my ($p, $data) = @_ > 1 ? (shift, shift) : ('', shift);
206 10         61 return $self->test('is_deeply', $self->_json(get => $p), $data, _desc(shift, qq{exact match for JSON Pointer "$p"}));
207             }
208              
209             sub json_message_like {
210 2     2 1 8 my ($self, $p, $regex, $desc) = @_;
211 2         11 return $self->test('like', $self->_json(get => $p), $regex, _desc($desc, qq{similar match for JSON Pointer "$p"}));
212             }
213              
214             sub json_message_unlike {
215 2     2 1 8 my ($self, $p, $regex, $desc) = @_;
216 2         9 $desc = _desc($desc, qq{no similar match for JSON Pointer "$p"});
217 2         7 return $self->test('unlike', $self->_json(get => $p), $regex, $desc);
218             }
219              
220             sub json_unlike {
221 2     2 1 5 my ($self, $p, $regex, $desc) = @_;
222 2         7 $desc = _desc($desc, qq{no similar match for JSON Pointer "$p"});
223 2         6 return $self->test('unlike', $self->tx->res->json($p), $regex, $desc);
224             }
225              
226             sub message_is {
227 58     58 1 181 my ($self, $value, $desc) = @_;
228 58         180 return $self->_message('is', $value, _desc($desc, 'exact match for message'));
229             }
230              
231             sub message_isnt {
232 7     7 1 40 my ($self, $value, $desc) = @_;
233 7         25 return $self->_message('isnt', $value, _desc($desc, 'no match for message'));
234             }
235              
236             sub message_like {
237 10     10 1 30 my ($self, $regex, $desc) = @_;
238 10         55 return $self->_message('like', $regex, _desc($desc, 'message is similar'));
239             }
240              
241             sub message_ok {
242 80     80 1 245 my ($self, $desc) = @_;
243 80         359 return $self->test('ok', !!$self->_wait, _desc($desc, 'message received'));
244             }
245              
246             sub message_unlike {
247 6     6 1 20 my ($self, $regex, $desc) = @_;
248 6         20 return $self->_message('unlike', $regex, _desc($desc, 'message is not similar'));
249             }
250              
251             sub new {
252 40     40 1 441255 my $self = shift->SUPER::new;
253              
254 40 100       170 return $self unless my $app = shift;
255              
256 4 100       18 my @cfg = @_ ? {config => {config_override => 1, %{shift()}}} : ();
  1         7  
257 4 100       220 return $self->app(Mojo::Server->new->build_app($app, @cfg)) unless ref $app;
258             return $self->app(
259 2 0       33 $app->isa('Mojolicious') ? @cfg ? $app->config($cfg[0]{config}) : $app : Mojo::Server->new->load_app($app, @cfg));
    50          
260             }
261              
262 3     3 1 5089 sub options_ok { shift->_build_ok(OPTIONS => @_) }
263              
264             sub or {
265 5     5 1 26 my ($self, $cb) = @_;
266 5 100       9 $self->$cb unless $self->success;
267 5         15 return $self;
268             }
269              
270 6     6 1 10335 sub patch_ok { shift->_build_ok(PATCH => @_) }
271              
272 96     96 1 150442 sub post_ok { shift->_build_ok(POST => @_) }
273              
274 2     2 1 3976 sub post_sse_ok { shift->_build_sse_ok(POST => @_) }
275              
276 6     6 1 5197 sub put_ok { shift->_build_ok(PUT => @_) }
277              
278 2     2 1 3498 sub query_ok { shift->_build_ok(QUERY => @_) }
279              
280 6     6 1 36 sub request_ok { shift->_request_ok($_[0], $_[0]->req->url->to_string) }
281              
282             sub reset_session {
283 5     5 1 6649 my $self = shift;
284 5         21 $self->ua->cookie_jar->empty;
285 5         16 return $self->tx(undef);
286             }
287              
288             sub send_ok {
289 53     53 1 149 my ($self, $msg, $desc) = @_;
290              
291 53         150 $desc = _desc($desc, 'send message');
292 53 50       220 return $self->test('ok', 0, $desc) unless $self->tx->is_websocket;
293              
294 53     53   134 $self->tx->send($msg => sub { Mojo::IOLoop->stop });
  53         245  
295 53         234 Mojo::IOLoop->start;
296 53         223 return $self->test('ok', 1, $desc);
297             }
298              
299             sub sse_finish_ok {
300 12     12 1 703 my $self = shift;
301 12         48 $self->tx->res->error({message => 'Interrupted by Test::Mojo'});
302 12         58 Mojo::IOLoop->one_tick while !$self->{sse_finished};
303 12         46 return $self->test('ok', 1, 'closed SSE connection');
304             }
305              
306             sub sse_finished_ok {
307 4     4 1 7 my $self = shift;
308 4         22 Mojo::IOLoop->one_tick while !$self->{sse_finished};
309 4         15 return $self->test('ok', 1, 'SSE connection has been closed');
310             }
311              
312             sub sse_ok {
313 24     24 1 655 my ($self, $desc) = @_;
314 24         85 return $self->test('ok', !!$self->_sse_wait, _desc($desc, 'event received'));
315             }
316              
317             sub sse_id_is {
318 3     3 1 8 my ($self, $type, $desc) = @_;
319 3         10 return $self->test('is', $self->sse->{id}, $type, _desc($desc, 'exact match for SSE event id'));
320             }
321              
322             sub sse_id_isnt {
323 3     3 1 7 my ($self, $type, $desc) = @_;
324 3         10 return $self->test('isnt', $self->sse->{id}, $type, _desc($desc, 'no match for SSE event id'));
325             }
326              
327             sub sse_text_is {
328 9     9 1 43 my ($self, $text, $desc) = @_;
329 9         29 return $self->test('is', $self->sse->{text}, $text, _desc($desc, 'exact match for SSE event text'));
330             }
331              
332             sub sse_text_isnt {
333 3     3 1 9 my ($self, $text, $desc) = @_;
334 3         12 return $self->test('isnt', $self->sse->{text}, $text, _desc($desc, 'no match for SSE event text'));
335             }
336              
337             sub sse_text_like {
338 7     7 1 23 my ($self, $regex, $desc) = @_;
339 7         25 return $self->test('like', $self->sse->{text}, $regex, _desc($desc, 'similar match for SSE event text'));
340             }
341              
342             sub sse_text_unlike {
343 3     3 1 8 my ($self, $regex, $desc) = @_;
344 3         12 return $self->test('unlike', $self->sse->{text}, $regex, _desc($desc, 'no similar match for SSE event text'));
345             }
346              
347             sub sse_type_is {
348 14     14 1 32 my ($self, $type, $desc) = @_;
349 14         44 return $self->test('is', $self->sse->{type}, $type, _desc($desc, 'exact match for SSE event type'));
350             }
351              
352             sub sse_type_isnt {
353 3     3 1 7 my ($self, $type, $desc) = @_;
354 3         11 return $self->test('isnt', $self->sse->{type}, $type, _desc($desc, 'no match for SSE event type'));
355             }
356              
357             sub status_is {
358 737     737 1 5333 my ($self, $status, $desc) = @_;
359 737         2603 $desc = _desc($desc, "$status " . $self->tx->res->default_message($status));
360 737         2098 return $self->test('is', $self->tx->res->code, $status, $desc);
361             }
362              
363             sub status_isnt {
364 2     2 1 5 my ($self, $status, $desc) = @_;
365 2         8 $desc = _desc($desc, "not $status " . $self->tx->res->default_message($status));
366 2         5 return $self->test('isnt', $self->tx->res->code, $status, $desc);
367             }
368              
369             sub test {
370 3296     3296 1 8389 my ($self, $name, @args) = @_;
371 3296         5470 local $Test::Builder::Level = $Test::Builder::Level + 3;
372 3296         7972 return $self->success(!!$self->handler->($name, @args));
373             }
374              
375             sub text_is {
376 56     56 1 170 my ($self, $selector, $value, $desc) = @_;
377 56         895 return $self->test('is', $self->_text($selector), $value, _desc($desc, qq{exact match for selector "$selector"}));
378             }
379              
380             sub text_isnt {
381 2     2 1 4 my ($self, $selector, $value, $desc) = @_;
382 2         7 return $self->test('isnt', $self->_text($selector), $value, _desc($desc, qq{no match for selector "$selector"}));
383             }
384              
385             sub text_like {
386 6     6 1 19 my ($self, $selector, $regex, $desc) = @_;
387 6         20 return $self->test('like', $self->_text($selector), $regex, _desc($desc, qq{similar match for selector "$selector"}));
388             }
389              
390             sub text_unlike {
391 1     1 1 3 my ($self, $selector, $regex, $desc) = @_;
392 1         3 $desc = _desc($desc, qq{no similar match for selector "$selector"});
393 1         5 return $self->test('unlike', $self->_text($selector), $regex, $desc);
394             }
395              
396             sub websocket_ok {
397 53     53 1 177287 my $self = shift;
398 53         252 return $self->_request_ok($self->ua->build_websocket_tx(@_), $_[0]);
399             }
400              
401             sub _attr {
402 8     8   14 my ($self, $selector, $attr) = @_;
403 8 50       23 return undef unless my $e = $self->tx->res->dom->at($selector);
404 8   50     21 return $e->attr($attr) // '';
405             }
406              
407             sub _build_ok {
408 739     739   2064 my ($self, $method, $url) = (shift, shift, shift);
409 739         1344 local $Test::Builder::Level = $Test::Builder::Level + 1;
410 739         2575 return $self->_request_ok($self->ua->build_tx($method, $url, @_), $url);
411             }
412              
413             sub _build_sse_ok {
414 16     16   44 my ($self, $method, $url) = (shift, shift, shift);
415 16         24 local $Test::Builder::Level = $Test::Builder::Level + 1;
416 16         59 return $self->_sse_ok($self->ua->build_tx($method, $url, @_), $url);
417             }
418              
419 3235   66 3235   14433 sub _desc { encode 'UTF-8', shift || shift }
420              
421             sub _handler {
422 3164     3164   6446 my ($name, @args) = @_;
423 3164         19545 return Test::More->can($name)->(@args);
424             }
425              
426             sub _json {
427 20     20   55 my ($self, $method, $p) = @_;
428 20   50     67 return Mojo::JSON::Pointer->new(j(@{$self->message // []}[1]))->$method($p);
  20         72  
429             }
430              
431             sub _message {
432 81     81   272 my ($self, $name, $value, $desc) = @_;
433 81         179 local $Test::Builder::Level = $Test::Builder::Level + 1;
434 81   50     144 my ($type, $msg) = @{$self->message // []};
  81         255  
435              
436             # Type check
437 81 100       319 if (ref $value eq 'HASH') {
438 10 100       45 my $expect = exists $value->{text} ? 'text' : 'binary';
439 10         27 $value = $value->{$expect};
440 10 100 50     78 $msg = '' unless ($type // '') eq $expect;
441             }
442              
443             # Decode text frame if there is no type check
444 71 100 50     440 else { $msg = decode 'UTF-8', $msg if ($type // '') eq 'text' }
445              
446 81   50     392 return $self->test($name, $msg // '', $value, $desc);
447             }
448              
449             sub _request_ok {
450 798     798   1905 my ($self, $tx, $url) = @_;
451              
452 798         1540 local $Test::Builder::Level = $Test::Builder::Level + 1;
453              
454             # Establish WebSocket connection
455 798 100       1924 if ($tx->req->is_handshake) {
456 55         214 @$self{qw(finished messages)} = (undef, []);
457             $self->ua->start(
458             $tx => sub {
459 55     55   132 my ($ua, $tx) = @_;
460 55 100       236 $self->{finished} = [] unless $self->tx($tx)->tx->is_websocket;
461 55         252 $tx->on(finish => sub { shift; $self->{finished} = [@_] });
  42         72  
  42         158  
462 55         222 $tx->on(binary => sub { push @{$self->{messages}}, [binary => pop] });
  10         19  
  10         48  
463 55         203 $tx->on(text => sub { push @{$self->{messages}}, [text => pop] });
  71         106  
  71         305  
464 55         371 Mojo::IOLoop->stop;
465             }
466 55         252 );
467 55         235 Mojo::IOLoop->start;
468              
469 55         214 return $self->test('ok', $self->tx->is_websocket, _desc("WebSocket handshake with $url"));
470             }
471              
472             # Perform request
473 743         1911 $self->tx($self->ua->start($tx));
474 743         1663 my $err = $self->tx->error;
475 743 50 66     3719 Test::More::diag $err->{message} if !(my $ok = !$err->{message} || $err->{code}) && $err;
      33        
476 743         1356 return $self->test('ok', $ok, _desc("@{[uc $tx->req->method]} $url"));
  743         1533  
477             }
478              
479             sub _sse_ok {
480 16     16   34 my ($self, $tx, $url) = @_;
481              
482 16         20 my $ok = undef;
483 16         60 @$self{qw(sse_finished sse_events)} = (undef, []);
484             $tx->res->content->on(
485             sse => sub {
486 69     69   126 my ($content, $event) = @_;
487 69 100       103 if ($event) { push @{$self->{sse_events}}, $event }
  53         57  
  53         112  
488             else {
489 16         21 $ok = 1;
490 16         87 Mojo::IOLoop->stop;
491             }
492             }
493 16         61 );
494 16     17   32 my $cb = $self->ua->on(start => sub { $self->tx(pop) });
  17         58  
495             $self->ua->start(
496             $tx => sub {
497 16     16   36 $self->{sse_finished} = 1;
498 16         48 $self->ua->unsubscribe(start => $cb);
499 16         49 Mojo::IOLoop->stop;
500             }
501 16         33 );
502 16         52 Mojo::IOLoop->start;
503              
504 16         32 return $self->test('ok', $ok, _desc("SSE connection established: @{[uc $tx->req->method]} $url"));
  16         46  
505             }
506              
507             sub _sse_wait {
508 24     24   33 my $self = shift;
509 24   66     58 Mojo::IOLoop->one_tick while !$self->{sse_finished} && !@{$self->{sse_events}};
  42         176  
510 24         30 return $self->sse(shift @{$self->{sse_events}})->sse;
  24         101  
511             }
512              
513             sub _text {
514 65 100   65   190 return undef unless my $e = shift->tx->res->dom->at(shift);
515 64         213 return $e->text;
516             }
517              
518             sub _wait {
519 80     80   163 my $self = shift;
520 80   100     323 Mojo::IOLoop->one_tick while !$self->{finished} && !@{$self->{messages}};
  242         1243  
521 80         175 return $self->message(shift @{$self->{messages}})->message;
  80         327  
522             }
523              
524             1;
525              
526             =encoding utf8
527              
528             =head1 NAME
529              
530             Test::Mojo - Testing Mojo
531              
532             =head1 SYNOPSIS
533              
534             use Test::More;
535             use Test::Mojo;
536              
537             my $t = Test::Mojo->new('MyApp');
538              
539             # HTML/XML
540             $t->get_ok('/welcome')->status_is(200)->text_is('div#message' => 'Hello!');
541              
542             # JSON
543             $t->post_ok('/search.json' => form => {q => 'Perl'})
544             ->status_is(200)
545             ->header_is('Server' => 'Mojolicious (Perl)')
546             ->header_isnt('X-Bender' => 'Bite my shiny metal ass!')
547             ->json_is('/results/4/title' => 'Perl rocks!')
548             ->json_like('/results/7/title' => qr/Perl/);
549              
550             # WebSocket
551             $t->websocket_ok('/echo')
552             ->send_ok('hello')
553             ->message_ok
554             ->message_is('echo: hello')
555             ->finish_ok;
556              
557             # Server-Sent Events (SSE)
558             $t->get_sse_ok('/events')
559             ->status_is(200)
560             ->sse_ok
561             ->sse_type_is('message')
562             ->sse_text_is('hello mojo')
563             ->sse_finish_ok;
564              
565             done_testing();
566              
567             =head1 DESCRIPTION
568              
569             L is a test user agent based on L, it is usually used together with L to test
570             L applications. Just run your tests with L.
571              
572             $ prove -l -v
573             $ prove -l -v t/foo.t
574              
575             If it is not already defined, the C environment variable will be set to C or C, depending
576             on the value of the C environment variable. And to make it esier to test HTTPS/WSS web services
577             L will be activated by default for L.
578              
579             See L for more.
580              
581             =head1 ATTRIBUTES
582              
583             L implements the following attributes.
584              
585             =head2 handler
586              
587             my $cb = $t->handler;
588             $t = $t->handler(sub {...});
589              
590             A callback to connect L with L.
591              
592             $t->handler(sub ($name, @args) {
593             return Test::More->can($name)->(@args);
594             });
595              
596             =head2 message
597              
598             my $msg = $t->message;
599             $t = $t->message([text => $bytes]);
600              
601             Current WebSocket message represented as an array reference containing the frame type and payload.
602              
603             # More specific tests
604             use Mojo::JSON qw(decode_json);
605             my $hash = decode_json $t->message->[1];
606             is ref $hash, 'HASH', 'right reference';
607             is $hash->{foo}, 'bar', 'right value';
608              
609             # Test custom message
610             $t->message([binary => $bytes])
611             ->json_message_has('/foo/bar')
612             ->json_message_hasnt('/bar')
613             ->json_message_is('/foo/baz' => {yada => [1, 2, 3]});
614              
615             =head2 sse
616              
617             my $event = $t->sse;
618             $t = $t->sse({type => 'message', text => 'working!'});
619              
620             Current Server-Sent Event (SSE) represented as a hash reference. Note that this attribute is B and may
621             change without warning!
622              
623             =head2 success
624              
625             my $bool = $t->success;
626             $t = $t->success($bool);
627              
628             True if the last test was successful.
629              
630             # Build custom tests
631             my $location_is = sub ($t, $value, $desc = '') {
632             $desc ||= "Location: $value";
633             local $Test::Builder::Level = $Test::Builder::Level + 1;
634             return $t->success(is($t->tx->res->headers->location, $value, $desc));
635             };
636             $t->get_ok('/')
637             ->status_is(302)
638             ->$location_is('https://mojolicious.org')
639             ->or(sub { diag 'Must have been Joel!' });
640              
641             =head2 tx
642              
643             my $tx = $t->tx;
644             $t = $t->tx(Mojo::Transaction::HTTP->new);
645              
646             Current transaction, usually a L or L object.
647              
648             # More specific tests
649             is $t->tx->res->json->{foo}, 'bar', 'right value';
650             ok $t->tx->res->content->is_multipart, 'multipart content';
651             is $t->tx->previous->res->code, 302, 'right status';
652              
653             =head2 ua
654              
655             my $ua = $t->ua;
656             $t = $t->ua(Mojo::UserAgent->new);
657              
658             User agent used for testing, defaults to a L object.
659              
660             # Allow redirects
661             $t->ua->max_redirects(10);
662             $t->get_ok('/redirect')->status_is(200)->content_like(qr/redirected/);
663              
664             # Switch protocol from HTTP to HTTPS
665             $t->ua->server->url('https');
666             $t->get_ok('/secure')->status_is(200)->content_like(qr/secure/);
667              
668             # Use absolute URL for request with Basic authentication
669             my $url = $t->ua->server->url->userinfo('sri:secr3t')->path('/secrets.json');
670             $t->post_ok($url => json => {limit => 10})
671             ->status_is(200)
672             ->json_is('/1/content', 'Mojo rocks!');
673              
674             # Customize all transactions (including followed redirects)
675             $t->ua->on(start => sub ($ua, $tx) { $tx->req->headers->accept_language('en-US') });
676             $t->get_ok('/hello')->status_is(200)->content_like(qr/Howdy/);
677              
678             =head1 METHODS
679              
680             L inherits all methods from L and implements the following new ones.
681              
682             =head2 app
683              
684             my $app = $t->app;
685             $t = $t->app(Mojolicious->new);
686              
687             Access application with L.
688              
689             # Change log level
690             $t->app->log->level('fatal');
691              
692             # Test application directly
693             is $t->app->defaults->{foo}, 'bar', 'right value';
694             ok $t->app->routes->find('echo')->is_websocket, 'WebSocket route';
695             my $c = $t->app->build_controller;
696             ok $c->render(template => 'foo'), 'rendering was successful';
697             is $c->res->status, 200, 'right status';
698             is $c->res->body, 'Foo!', 'right content';
699              
700             # Change application behavior
701             $t->app->hook(before_dispatch => sub ($c) {
702             $c->render(text => 'This request did not reach the router.') if $c->req->url->path->contains('/user');
703             });
704             $t->get_ok('/user')->status_is(200)->content_like(qr/not reach the router/);
705              
706             # Extract additional information
707             my $stash;
708             $t->app->hook(after_dispatch => sub ($c) { $stash = $c->stash });
709             $t->get_ok('/hello')->status_is(200);
710             is $stash->{foo}, 'bar', 'right value';
711              
712             =head2 attr_is
713              
714             $t = $t->attr_is('img.cat', 'alt', 'Grumpy cat');
715             $t = $t->attr_is('img.cat', 'alt', 'Grumpy cat', 'right alt text');
716              
717             Checks text content of attribute with L at the CSS selectors first matching HTML/XML element for
718             exact match with L.
719              
720             =head2 attr_isnt
721              
722             $t = $t->attr_isnt('img.cat', 'alt', 'Calm cat');
723             $t = $t->attr_isnt('img.cat', 'alt', 'Calm cat', 'different alt text');
724              
725             Opposite of L.
726              
727             =head2 attr_like
728              
729             $t = $t->attr_like('img.cat', 'alt', qr/Grumpy/);
730             $t = $t->attr_like('img.cat', 'alt', qr/Grumpy/, 'right alt text');
731              
732             Checks text content of attribute with L at the CSS selectors first matching HTML/XML element for
733             similar match with L.
734              
735             =head2 attr_unlike
736              
737             $t = $t->attr_unlike('img.cat', 'alt', qr/Calm/);
738             $t = $t->attr_unlike('img.cat', 'alt', qr/Calm/, 'different alt text');
739              
740             Opposite of L.
741              
742             =head2 content_is
743              
744             $t = $t->content_is('working!');
745             $t = $t->content_is('working!', 'right content');
746              
747             Check response content for exact match after retrieving it from L.
748              
749             =head2 content_isnt
750              
751             $t = $t->content_isnt('working!');
752             $t = $t->content_isnt('working!', 'different content');
753              
754             Opposite of L.
755              
756             =head2 content_like
757              
758             $t = $t->content_like(qr/working!/);
759             $t = $t->content_like(qr/working!/, 'right content');
760              
761             Check response content for similar match after retrieving it from L.
762              
763             =head2 content_type_is
764              
765             $t = $t->content_type_is('text/html');
766             $t = $t->content_type_is('text/html', 'right content type');
767              
768             Check response C header for exact match.
769              
770             =head2 content_type_isnt
771              
772             $t = $t->content_type_isnt('text/html');
773             $t = $t->content_type_isnt('text/html', 'different content type');
774              
775             Opposite of L.
776              
777             =head2 content_type_like
778              
779             $t = $t->content_type_like(qr/text/);
780             $t = $t->content_type_like(qr/text/, 'right content type');
781              
782             Check response C header for similar match.
783              
784             =head2 content_type_unlike
785              
786             $t = $t->content_type_unlike(qr/text/);
787             $t = $t->content_type_unlike(qr/text/, 'different content type');
788              
789             Opposite of L.
790              
791             =head2 content_unlike
792              
793             $t = $t->content_unlike(qr/working!/);
794             $t = $t->content_unlike(qr/working!/, 'different content');
795              
796             Opposite of L.
797              
798             =head2 delete_ok
799              
800             $t = $t->delete_ok('http://example.com/foo');
801             $t = $t->delete_ok('/foo');
802             $t = $t->delete_ok('/foo' => {Accept => '*/*'} => 'Content!');
803             $t = $t->delete_ok('/foo' => {Accept => '*/*'} => form => {a => 'b'});
804             $t = $t->delete_ok('/foo' => {Accept => '*/*'} => json => {a => 'b'});
805              
806             Perform a C request and check for transport errors, takes the same arguments as L,
807             except for the callback.
808              
809             =head2 element_count_is
810              
811             $t = $t->element_count_is('div.foo[x=y]', 5);
812             $t = $t->element_count_is('html body div', 30, 'thirty elements');
813              
814             Checks the number of HTML/XML elements matched by the CSS selector with L.
815              
816             =head2 element_exists
817              
818             $t = $t->element_exists('div.foo[x=y]');
819             $t = $t->element_exists('html head title', 'has a title');
820              
821             Checks for existence of the CSS selectors first matching HTML/XML element with L.
822              
823             # Check attribute values
824             $t->get_ok('/login')
825             ->element_exists('label[for=email]')
826             ->element_exists('input[name=email][type=text][value*="example.com"]')
827             ->element_exists('label[for=pass]')
828             ->element_exists('input[name=pass][type=password]')
829             ->element_exists('input[type=submit][value]');
830              
831             =head2 element_exists_not
832              
833             $t = $t->element_exists_not('div.foo[x=y]');
834             $t = $t->element_exists_not('html head title', 'has no title');
835              
836             Opposite of L.
837              
838             =head2 finish_ok
839              
840             $t = $t->finish_ok;
841             $t = $t->finish_ok(1000);
842             $t = $t->finish_ok(1003 => 'Cannot accept data!');
843              
844             Close WebSocket connection gracefully.
845              
846             =head2 finished_ok
847              
848             $t = $t->finished_ok(1000);
849              
850             Wait for WebSocket connection to be closed gracefully and check status.
851              
852             =head2 get_ok
853              
854             $t = $t->get_ok('http://example.com/foo');
855             $t = $t->get_ok('/foo');
856             $t = $t->get_ok('/foo' => {Accept => '*/*'} => 'Content!');
857             $t = $t->get_ok('/foo' => {Accept => '*/*'} => form => {a => 'b'});
858             $t = $t->get_ok('/foo' => {Accept => '*/*'} => json => {a => 'b'});
859              
860             Perform a C request and check for transport errors, takes the same arguments as L, except
861             for the callback.
862              
863             # Run tests against remote host
864             $t->get_ok('https://docs.mojolicious.org')->status_is(200);
865              
866             # Use relative URL for request with Basic authentication
867             $t->get_ok('//sri:secr3t@/secrets.json')
868             ->status_is(200)
869             ->json_is('/1/content', 'Mojo rocks!');
870              
871             # Run additional tests on the transaction
872             $t->get_ok('/foo')->status_is(200);
873             is $t->tx->res->dom->at('input')->val, 'whatever', 'right value';
874              
875             =head2 get_sse_ok
876              
877             $t = $t->get_sse_ok('http://example.com/events');
878             $t = $t->get_sse_ok('/events');
879             $t = $t->get_sse_ok('/events' => {Accept => 'text/event-stream'} => 'Content!');
880             $t = $t->get_sse_ok('/events' => {Accept => 'text/event-stream'} => form => {a => 'b'});
881             $t = $t->get_sse_ok('/events' => {Accept => 'text/event-stream'} => json => {a => 'b'});
882              
883             Perform a C reequest to establish a Server-Sent Events (SSE) connection, takes the same arguments as
884             L, except for the callback. Note that this method is B and may change without
885             warning!
886              
887             =head2 head_ok
888              
889             $t = $t->head_ok('http://example.com/foo');
890             $t = $t->head_ok('/foo');
891             $t = $t->head_ok('/foo' => {Accept => '*/*'} => 'Content!');
892             $t = $t->head_ok('/foo' => {Accept => '*/*'} => form => {a => 'b'});
893             $t = $t->head_ok('/foo' => {Accept => '*/*'} => json => {a => 'b'});
894              
895             Perform a C request and check for transport errors, takes the same arguments as L, except
896             for the callback.
897              
898             =head2 header_exists
899              
900             $t = $t->header_exists('ETag');
901             $t = $t->header_exists('ETag', 'header exists');
902              
903             Check if response header exists.
904              
905             =head2 header_exists_not
906              
907             $t = $t->header_exists_not('ETag');
908             $t = $t->header_exists_not('ETag', 'header is missing');
909              
910             Opposite of L.
911              
912             =head2 header_is
913              
914             $t = $t->header_is(ETag => '"abc321"');
915             $t = $t->header_is(ETag => '"abc321"', 'right header');
916              
917             Check response header for exact match.
918              
919             =head2 header_isnt
920              
921             $t = $t->header_isnt(Etag => '"abc321"');
922             $t = $t->header_isnt(ETag => '"abc321"', 'different header');
923              
924             Opposite of L.
925              
926             =head2 header_like
927              
928             $t = $t->header_like(ETag => qr/abc/);
929             $t = $t->header_like(ETag => qr/abc/, 'right header');
930              
931             Check response header for similar match.
932              
933             =head2 header_unlike
934              
935             $t = $t->header_unlike(ETag => qr/abc/);
936             $t = $t->header_unlike(ETag => qr/abc/, 'different header');
937              
938             Opposite of L.
939              
940             =head2 json_has
941              
942             $t = $t->json_has('/foo');
943             $t = $t->json_has('/minibar', 'has a minibar');
944              
945             Check if JSON response contains a value that can be identified using the given JSON Pointer with
946             L.
947              
948             =head2 json_hasnt
949              
950             $t = $t->json_hasnt('/foo');
951             $t = $t->json_hasnt('/minibar', 'no minibar');
952              
953             Opposite of L.
954              
955             =head2 json_is
956              
957             $t = $t->json_is({foo => [1, 2, 3]});
958             $t = $t->json_is('/foo' => [1, 2, 3]);
959             $t = $t->json_is('/foo/1' => 2, 'right value');
960              
961             Check the value extracted from JSON response using the given JSON Pointer with L, which defaults
962             to the root value if it is omitted.
963              
964             # Use an empty JSON Pointer to test the whole JSON response with a test description
965             $t->json_is('' => {foo => [1, 2, 3]}, 'right object');
966              
967             =head2 json_like
968              
969             $t = $t->json_like('/foo/1' => qr/^\d+$/);
970             $t = $t->json_like('/foo/1' => qr/^\d+$/, 'right value');
971              
972             Check the value extracted from JSON response using the given JSON Pointer with L for similar
973             match.
974              
975             =head2 json_message_has
976              
977             $t = $t->json_message_has('/foo');
978             $t = $t->json_message_has('/minibar', 'has a minibar');
979              
980             Check if JSON WebSocket message contains a value that can be identified using the given JSON Pointer with
981             L.
982              
983             =head2 json_message_hasnt
984              
985             $t = $t->json_message_hasnt('/foo');
986             $t = $t->json_message_hasnt('/minibar', 'no minibar');
987              
988             Opposite of L.
989              
990             =head2 json_message_is
991              
992             $t = $t->json_message_is({foo => [1, 2, 3]});
993             $t = $t->json_message_is('/foo' => [1, 2, 3]);
994             $t = $t->json_message_is('/foo/1' => 2, 'right value');
995              
996             Check the value extracted from JSON WebSocket message using the given JSON Pointer with L, which
997             defaults to the root value if it is omitted.
998              
999             =head2 json_message_like
1000              
1001             $t = $t->json_message_like('/foo/1' => qr/^\d+$/);
1002             $t = $t->json_message_like('/foo/1' => qr/^\d+$/, 'right value');
1003              
1004             Check the value extracted from JSON WebSocket message using the given JSON Pointer with L for
1005             similar match.
1006              
1007             =head2 json_message_unlike
1008              
1009             $t = $t->json_message_unlike('/foo/1' => qr/^\d+$/);
1010             $t = $t->json_message_unlike('/foo/1' => qr/^\d+$/, 'different value');
1011              
1012             Opposite of L.
1013              
1014             =head2 json_unlike
1015              
1016             $t = $t->json_unlike('/foo/1' => qr/^\d+$/);
1017             $t = $t->json_unlike('/foo/1' => qr/^\d+$/, 'different value');
1018              
1019             Opposite of L.
1020              
1021             =head2 message_is
1022              
1023             $t = $t->message_is({binary => $bytes});
1024             $t = $t->message_is({text => $bytes});
1025             $t = $t->message_is('working!');
1026             $t = $t->message_is('working!', 'right message');
1027              
1028             Check WebSocket message for exact match.
1029              
1030             =head2 message_isnt
1031              
1032             $t = $t->message_isnt({binary => $bytes});
1033             $t = $t->message_isnt({text => $bytes});
1034             $t = $t->message_isnt('working!');
1035             $t = $t->message_isnt('working!', 'different message');
1036              
1037             Opposite of L.
1038              
1039             =head2 message_like
1040              
1041             $t = $t->message_like({binary => qr/$bytes/});
1042             $t = $t->message_like({text => qr/$bytes/});
1043             $t = $t->message_like(qr/working!/);
1044             $t = $t->message_like(qr/working!/, 'right message');
1045              
1046             Check WebSocket message for similar match.
1047              
1048             =head2 message_ok
1049              
1050             $t = $t->message_ok;
1051             $t = $t->message_ok('got a message');
1052              
1053             Wait for next WebSocket message to arrive.
1054              
1055             # Wait for message and perform multiple tests on it
1056             $t->websocket_ok('/time')
1057             ->message_ok
1058             ->message_like(qr/\d+/)
1059             ->message_unlike(qr/\w+/)
1060             ->finish_ok;
1061              
1062             =head2 message_unlike
1063              
1064             $t = $t->message_unlike({binary => qr/$bytes/});
1065             $t = $t->message_unlike({text => qr/$bytes/});
1066             $t = $t->message_unlike(qr/working!/);
1067             $t = $t->message_unlike(qr/working!/, 'different message');
1068              
1069             Opposite of L.
1070              
1071             =head2 new
1072              
1073             my $t = Test::Mojo->new;
1074             my $t = Test::Mojo->new('MyApp');
1075             my $t = Test::Mojo->new('MyApp', {foo => 'bar'});
1076             my $t = Test::Mojo->new(Mojo::File->new('/path/to/myapp.pl'));
1077             my $t = Test::Mojo->new(Mojo::File->new('/path/to/myapp.pl'), {foo => 'bar'});
1078             my $t = Test::Mojo->new(MyApp->new);
1079             my $t = Test::Mojo->new(MyApp->new, {foo => 'bar'});
1080              
1081             Construct a new L object. In addition to a class name or L object pointing to the application
1082             script, you can pass along a hash reference with configuration values that will be used to override the application
1083             configuration. The special configuration value C will be set in L as well, which
1084             is used to disable configuration plugins like L, L and
1085             L for tests.
1086              
1087             # Load application script relative to the "t" directory
1088             use Mojo::File qw(curfile);
1089             my $t = Test::Mojo->new(curfile->dirname->sibling('myapp.pl'));
1090              
1091             =head2 options_ok
1092              
1093             $t = $t->options_ok('http://example.com/foo');
1094             $t = $t->options_ok('/foo');
1095             $t = $t->options_ok('/foo' => {Accept => '*/*'} => 'Content!');
1096             $t = $t->options_ok('/foo' => {Accept => '*/*'} => form => {a => 'b'});
1097             $t = $t->options_ok('/foo' => {Accept => '*/*'} => json => {a => 'b'});
1098              
1099             Perform a C request and check for transport errors, takes the same arguments as L,
1100             except for the callback.
1101              
1102             =head2 or
1103              
1104             $t = $t->or(sub {...});
1105              
1106             Execute callback if the value of L is false.
1107              
1108             # Diagnostics
1109             $t->get_ok('/bad')->or(sub { diag 'Must have been Glen!' })
1110             ->status_is(200)->or(sub { diag $t->tx->res->dom->at('title')->text });
1111              
1112             =head2 patch_ok
1113              
1114             $t = $t->patch_ok('http://example.com/foo');
1115             $t = $t->patch_ok('/foo');
1116             $t = $t->patch_ok('/foo' => {Accept => '*/*'} => 'Content!');
1117             $t = $t->patch_ok('/foo' => {Accept => '*/*'} => form => {a => 'b'});
1118             $t = $t->patch_ok('/foo' => {Accept => '*/*'} => json => {a => 'b'});
1119              
1120             Perform a C request and check for transport errors, takes the same arguments as L,
1121             except for the callback.
1122              
1123             =head2 post_ok
1124              
1125             $t = $t->post_ok('http://example.com/foo');
1126             $t = $t->post_ok('/foo');
1127             $t = $t->post_ok('/foo' => {Accept => '*/*'} => 'Content!');
1128             $t = $t->post_ok('/foo' => {Accept => '*/*'} => form => {a => 'b'});
1129             $t = $t->post_ok('/foo' => {Accept => '*/*'} => json => {a => 'b'});
1130              
1131             Perform a C request and check for transport errors, takes the same arguments as L, except
1132             for the callback.
1133              
1134             # Test file upload
1135             my $upload = {foo => {content => 'bar', filename => 'baz.txt'}};
1136             $t->post_ok('/upload' => form => $upload)->status_is(200);
1137              
1138             # Test JSON API
1139             $t->post_ok('/hello.json' => json => {hello => 'world'})
1140             ->status_is(200)
1141             ->json_is({bye => 'world'});
1142              
1143             =head2 post_sse_ok
1144              
1145             $t = $t->post_sse_ok('http://example.com/events');
1146             $t = $t->post_sse_ok('/events');
1147             $t = $t->post_sse_ok('/events' => {Accept => 'text/event-stream'} => 'Content!');
1148             $t = $t->post_sse_ok('/events' => {Accept => 'text/event-stream'} => form => {a => 'b'});
1149             $t = $t->post_sse_ok('/events' => {Accept => 'text/event-stream'} => json => {a => 'b'});
1150              
1151             Perform a C reequest to establish a Server-Sent Events (SSE) connection, takes the same arguments as
1152             L, except for the callback. Note that this method is B and may change without
1153             warning!
1154              
1155             =head2 put_ok
1156              
1157             $t = $t->put_ok('http://example.com/foo');
1158             $t = $t->put_ok('/foo');
1159             $t = $t->put_ok('/foo' => {Accept => '*/*'} => 'Content!');
1160             $t = $t->put_ok('/foo' => {Accept => '*/*'} => form => {a => 'b'});
1161             $t = $t->put_ok('/foo' => {Accept => '*/*'} => json => {a => 'b'});
1162              
1163             Perform a C request and check for transport errors, takes the same arguments as L, except
1164             for the callback.
1165              
1166             =head2 query_ok
1167              
1168             $t = $t->query_ok('http://example.com/foo');
1169             $t = $t->query_ok('/foo');
1170             $t = $t->query_ok('/foo' => {Accept => '*/*'} => 'Content!');
1171             $t = $t->query_ok('/foo' => {Accept => '*/*'} => form => {a => 'b'});
1172             $t = $t->query_ok('/foo' => {Accept => '*/*'} => json => {a => 'b'});
1173              
1174             Perform a C request and check for transport errors, takes the same arguments as L,
1175             except for the callback.
1176              
1177             =head2 request_ok
1178              
1179             $t = $t->request_ok(Mojo::Transaction::HTTP->new);
1180              
1181             Perform request and check for transport errors.
1182              
1183             # Request with custom method
1184             my $tx = $t->ua->build_tx(FOO => '/test.json' => json => {foo => 1});
1185             $t->request_ok($tx)->status_is(200)->json_is({success => 1});
1186              
1187             # Request with custom cookie
1188             my $tx = $t->ua->build_tx(GET => '/account');
1189             $tx->req->cookies({name => 'user', value => 'sri'});
1190             $t->request_ok($tx)->status_is(200)->text_is('head > title' => 'Hello sri');
1191              
1192             # Custom WebSocket handshake
1193             my $tx = $t->ua->build_websocket_tx('/foo');
1194             $tx->req->headers->remove('User-Agent');
1195             $t->request_ok($tx)->message_ok->message_is('bar')->finish_ok;
1196              
1197             =head2 reset_session
1198              
1199             $t = $t->reset_session;
1200              
1201             Reset user agent session.
1202              
1203             =head2 send_ok
1204              
1205             $t = $t->send_ok({binary => $bytes});
1206             $t = $t->send_ok({text => $bytes});
1207             $t = $t->send_ok({json => {test => [1, 2, 3]}});
1208             $t = $t->send_ok([$fin, $rsv1, $rsv2, $rsv3, $op, $payload]);
1209             $t = $t->send_ok($chars);
1210             $t = $t->send_ok($chars, 'sent successfully');
1211              
1212             Send message or frame via WebSocket.
1213              
1214             # Send JSON object as "Text" message
1215             $t->websocket_ok('/echo.json')
1216             ->send_ok({json => {test => 'I ♥ Mojolicious!'}})
1217             ->message_ok
1218             ->json_message_is('/test' => 'I ♥ Mojolicious!')
1219             ->finish_ok;
1220              
1221             =head2 sse_finish_ok
1222              
1223             $t = $t->sse_finish_ok;
1224              
1225             Close Server-Sent Events connection. Note that this method is B and may change without warning!
1226              
1227             =head2 sse_finished_ok
1228              
1229             $t = $t->sse_finished_ok;
1230              
1231             Wait for Server-Sent Events connection to be closed. Note that this method is B and may change without
1232             warning!
1233              
1234             =head2 sse_ok
1235              
1236             $t = $t->sse_ok;
1237             $t = $t->sse_ok('got an event');
1238              
1239             Wait for next Server-Sent Event to arrive. Note that this method is B and may change without warning!
1240              
1241             =head2 sse_id_is
1242              
1243             $t = $t->sse_id_is(123);
1244             $t = $t->sse_id_is(123, 'right id');
1245              
1246             Check Server-Sent Event id for exact match. Note that this method is B and may change without warning!
1247              
1248             =head2 sse_id_isnt
1249              
1250             $t = $t->sse_id_isnt(123);
1251             $t = $t->sse_id_isnt(123, 'different id');
1252              
1253             Opposite of L. Note that this method is B and may change without warning!
1254              
1255             =head2 sse_text_is
1256              
1257             $t = $t->sse_text_is('working!');
1258             $t = $t->sse_text_is('working!', 'right text');
1259              
1260             Check Server-Sent Event text for exact match. Note that this method is B and may change without warning!
1261              
1262             =head2 sse_text_isnt
1263              
1264             $t = $t->sse_text_isnt('working!');
1265             $t = $t->sse_text_isnt('working!', 'different text');
1266              
1267             Opposite of L. Note that this method is B and may change without warning!
1268              
1269             =head2 sse_text_like
1270              
1271             $t = $t->sse_text_like(qr/working/);
1272             $t = $t->sse_text_like(qr/working/, 'right text');
1273              
1274             Check Server-Sent Event text for exact match. Note that this method is B and may change without warning!
1275              
1276             =head2 sse_text_unlike
1277              
1278             $t = $t->sse_text_unlike(qr/working/);
1279             $t = $t->sse_text_unlike(qr/working/, 'different text');
1280              
1281             Opposite of L. Note that this method is B and may change without warning!
1282              
1283             =head2 sse_type_is
1284              
1285             $t = $t->sse_type_is('message');
1286             $t = $t->sse_type_is('message', 'right type');
1287              
1288             Check Server-Sent Event type for exact match. Note that this method is B and may change without warning!
1289              
1290             =head2 sse_type_isnt
1291              
1292             $t = $t->sse_type_isnt('message');
1293             $t = $t->sse_type_isnt('message', 'different type');
1294              
1295             Opposite of L. Note that this method is B and may change without warning!
1296              
1297             =head2 status_is
1298              
1299             $t = $t->status_is(200);
1300             $t = $t->status_is(200, 'right status');
1301              
1302             Check response status for exact match.
1303              
1304             =head2 status_isnt
1305              
1306             $t = $t->status_isnt(200);
1307             $t = $t->status_isnt(200, 'different status');
1308              
1309             Opposite of L.
1310              
1311             =head2 test
1312              
1313             $t = $t->test('is', 'first value', 'second value', 'right value');
1314              
1315             Call L functions through L, used to implement L roles. The result will be stored in
1316             L.
1317              
1318             =head2 text_is
1319              
1320             $t = $t->text_is('div.foo[x=y]' => 'Hello!');
1321             $t = $t->text_is('html head title' => 'Hello!', 'right title');
1322              
1323             Checks text content of the CSS selectors first matching HTML/XML element for exact match with L.
1324              
1325             =head2 text_isnt
1326              
1327             $t = $t->text_isnt('div.foo[x=y]' => 'Hello!');
1328             $t = $t->text_isnt('html head title' => 'Hello!', 'different title');
1329              
1330             Opposite of L.
1331              
1332             =head2 text_like
1333              
1334             $t = $t->text_like('div.foo[x=y]' => qr/Hello/);
1335             $t = $t->text_like('html head title' => qr/Hello/, 'right title');
1336              
1337             Checks text content of the CSS selectors first matching HTML/XML element for similar match with L.
1338              
1339             =head2 text_unlike
1340              
1341             $t = $t->text_unlike('div.foo[x=y]' => qr/Hello/);
1342             $t = $t->text_unlike('html head title' => qr/Hello/, 'different title');
1343              
1344             Opposite of L.
1345              
1346             =head2 websocket_ok
1347              
1348             $t = $t->websocket_ok('http://example.com/echo');
1349             $t = $t->websocket_ok('/echo');
1350             $t = $t->websocket_ok('/echo' => {DNT => 1} => ['v1.proto']);
1351              
1352             Open a WebSocket connection with transparent handshake, takes the same arguments as L,
1353             except for the callback.
1354              
1355             # WebSocket with permessage-deflate compression
1356             $t->websocket_ok('/' => {'Sec-WebSocket-Extensions' => 'permessage-deflate'})
1357             ->send_ok('y' x 50000)
1358             ->message_ok
1359             ->message_is('z' x 50000)
1360             ->finish_ok;
1361              
1362             =head1 SEE ALSO
1363              
1364             L, L, L.
1365              
1366             =cut