File Coverage

blib/lib/Test/Mojo.pm
Criterion Covered Total %
statement 280 280 100.0
branch 36 44 81.8
condition 19 31 61.2
subroutine 97 97 100.0
pod 72 72 100.0
total 504 524 96.1


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