Hello community, here is the log from the commit of package perl-Mojolicious for openSUSE:Factory checked in at 2017-08-19 10:55:44 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/perl-Mojolicious (Old) and /work/SRC/openSUSE:Factory/.perl-Mojolicious.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "perl-Mojolicious" Sat Aug 19 10:55:44 2017 rev:73 rq:517491 version:7.42 Changes: -------- --- /work/SRC/openSUSE:Factory/perl-Mojolicious/perl-Mojolicious.changes 2017-08-06 11:25:37.924915911 +0200 +++ /work/SRC/openSUSE:Factory/.perl-Mojolicious.new/perl-Mojolicious.changes 2017-08-19 10:55:45.534029326 +0200 @@ -1,0 +2,30 @@ +Fri Aug 18 05:48:34 UTC 2017 - [email protected] + +- updated to 7.42 + see /usr/share/doc/packages/perl-Mojolicious/Changes + + 7.42 2017-08-17 + - Improved Mojo::Base role support with shorthands. + MyClass->with_roles('MyClass::Role::Foo') + becomes + MyClass->with_roles('+Foo') + + 7.41 2017-08-15 + - Added with_roles method to Mojo::ByteStream, Mojo::Collection, Mojo::DOM and + Mojo::File. + +------------------------------------------------------------------- +Tue Aug 15 05:49:47 UTC 2017 - [email protected] + +- updated to 7.40 + see /usr/share/doc/packages/perl-Mojolicious/Changes + + 7.40 2017-08-14 + - Added support for Role::Tiny extensions to all classes based on Mojo::Base. + (dotan) + - Added with_roles method to Mojo::Base. (dotan) + - Added the guide Mojolicious::Guides::Testing. (scottw) + - Replaced systemd detection in Mojo::Log with the MOJO_LOG_SHORT environment + variable, since there is no reliable way to detect systemd. + +------------------------------------------------------------------- Old: ---- Mojolicious-7.39.tar.gz New: ---- Mojolicious-7.42.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ perl-Mojolicious.spec ++++++ --- /var/tmp/diff_new_pack.qDeHtf/_old 2017-08-19 10:55:47.433761471 +0200 +++ /var/tmp/diff_new_pack.qDeHtf/_new 2017-08-19 10:55:47.445759779 +0200 @@ -17,7 +17,7 @@ Name: perl-Mojolicious -Version: 7.39 +Version: 7.42 Release: 0 %define cpan_name Mojolicious Summary: Real-time web framework ++++++ Mojolicious-7.39.tar.gz -> Mojolicious-7.42.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Mojolicious-7.39/Changes new/Mojolicious-7.42/Changes --- old/Mojolicious-7.39/Changes 2017-08-03 10:38:56.000000000 +0200 +++ new/Mojolicious-7.42/Changes 2017-08-17 13:14:25.000000000 +0200 @@ -1,4 +1,22 @@ +7.42 2017-08-17 + - Improved Mojo::Base role support with shorthands. + MyClass->with_roles('MyClass::Role::Foo') + becomes + MyClass->with_roles('+Foo') + +7.41 2017-08-15 + - Added with_roles method to Mojo::ByteStream, Mojo::Collection, Mojo::DOM and + Mojo::File. + +7.40 2017-08-14 + - Added support for Role::Tiny extensions to all classes based on Mojo::Base. + (dotan) + - Added with_roles method to Mojo::Base. (dotan) + - Added the guide Mojolicious::Guides::Testing. (scottw) + - Replaced systemd detection in Mojo::Log with the MOJO_LOG_SHORT environment + variable, since there is no reliable way to detect systemd. + 7.39 2017-08-03 - Removed experimental close_idle_connections method from Mojo::Server::Daemon. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Mojolicious-7.39/MANIFEST new/Mojolicious-7.42/MANIFEST --- old/Mojolicious-7.39/MANIFEST 2017-08-03 10:48:59.000000000 +0200 +++ new/Mojolicious-7.42/MANIFEST 2017-08-17 13:15:13.000000000 +0200 @@ -101,6 +101,7 @@ lib/Mojolicious/Guides/Growing.pod lib/Mojolicious/Guides/Rendering.pod lib/Mojolicious/Guides/Routing.pod +lib/Mojolicious/Guides/Testing.pod lib/Mojolicious/Guides/Tutorial.pod lib/Mojolicious/Lite.pm lib/Mojolicious/Plugin.pm @@ -214,6 +215,7 @@ t/mojo/request.t t/mojo/request_cgi.t t/mojo/response.t +t/mojo/roles.t t/mojo/subprocess.t t/mojo/template.t t/mojo/templates/exception.mt diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Mojolicious-7.39/META.json new/Mojolicious-7.42/META.json --- old/Mojolicious-7.39/META.json 2017-08-03 10:48:59.000000000 +0200 +++ new/Mojolicious-7.42/META.json 2017-08-17 13:15:13.000000000 +0200 @@ -58,6 +58,6 @@ }, "x_IRC" : "irc://irc.perl.org/#mojo" }, - "version" : "7.39", + "version" : "7.42", "x_serialization_backend" : "JSON::PP version 2.94" } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Mojolicious-7.39/META.yml new/Mojolicious-7.42/META.yml --- old/Mojolicious-7.39/META.yml 2017-08-03 10:48:59.000000000 +0200 +++ new/Mojolicious-7.42/META.yml 2017-08-17 13:15:13.000000000 +0200 @@ -31,5 +31,5 @@ homepage: http://mojolicious.org license: http://www.opensource.org/licenses/artistic-license-2.0 repository: https://github.com/kraih/mojo.git -version: '7.39' +version: '7.42' x_serialization_backend: 'CPAN::Meta::YAML version 0.018' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Mojolicious-7.39/lib/Mojo/Base.pm new/Mojolicious-7.42/lib/Mojo/Base.pm --- old/Mojolicious-7.39/lib/Mojo/Base.pm 2017-07-17 21:07:54.000000000 +0200 +++ new/Mojolicious-7.42/lib/Mojo/Base.pm 2017-08-16 23:39:42.000000000 +0200 @@ -11,6 +11,10 @@ # Only Perl 5.14+ requires it on demand use IO::Handle (); +# Role support requires Role::Tiny 2.000001+ +use constant ROLES => + !!(eval { require Role::Tiny; Role::Tiny->VERSION('2.000001'); 1 }); + # Supported on Perl 5.22+ my $NAME = eval { require Sub::Util; Sub::Util->can('set_subname') } || sub { $_[1] }; @@ -101,6 +105,13 @@ return $self; } +sub with_roles { + Carp::croak 'Role::Tiny 2.000001+ is required for roles' unless ROLES; + my $class = shift; + return Role::Tiny->create_class_with_roles($class, + map { /^\+(.+)$/ ? "${class}::Role::$1" : $_ } @_); +} + 1; =encoding utf8 @@ -158,7 +169,6 @@ use utf8; use feature ':5.10'; use IO::Handle (); - use Mojo::Base; push @ISA, 'Mojo::Base'; sub has { Mojo::Base::attr(__PACKAGE__, @_) } @@ -170,7 +180,6 @@ use IO::Handle (); require SomeBaseClass; push @ISA, 'SomeBaseClass'; - use Mojo::Base; sub has { Mojo::Base::attr(__PACKAGE__, @_) } =head1 FUNCTIONS @@ -238,6 +247,20 @@ # Inject side effects into a method chain $object->foo('A')->tap(sub { say $_->foo })->foo('B'); +=head2 with_roles + + my $new_class = SubClass->with_roles('SubClass::Role::One'); + my $new_class = SubClass->with_roles('+One', '+Two'); + +Create and return a new class that extends the given class with one or more +L<Role::Tiny> roles. For roles following the naming scheme +C<MyClass::Role::RoleName> you can use the shorthand C<+RoleName>. Note that +role support depends on L<Role::Tiny> (2.000001+). + + # Create a new class with the role "SubClass::Role::Foo" and instantiate it + my $new_class = SubClass->with_roles('+Foo'); + my $object = $new_class->new; + =head1 SEE ALSO L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicious.org>. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Mojolicious-7.39/lib/Mojo/ByteStream.pm new/Mojolicious-7.42/lib/Mojo/ByteStream.pm --- old/Mojolicious-7.39/lib/Mojo/ByteStream.pm 2017-07-21 09:56:39.000000000 +0200 +++ new/Mojolicious-7.42/lib/Mojo/ByteStream.pm 2017-08-16 23:41:01.000000000 +0200 @@ -56,6 +56,8 @@ sub to_string { ${$_[0]} } +sub with_roles { shift->Mojo::Base::with_roles(@_) } + sub _delegate { my ($self, $sub) = (shift, shift); $$self = $sub->(shift || 'UTF-8', $$self); @@ -328,6 +330,13 @@ # "<html>" b('%3Chtml%3E')->url_unescape->xml_escape; +=head2 with_roles + + my $new_class = Mojo::ByteStream->with_roles('Mojo::ByteStream::Role::One'); + my $new_class = Mojo::ByteStream->with_roles('+One', '+Two'); + +Alias for L<Mojo::Base/"with_roles">. + =head2 xml_escape $stream = $stream->xml_escape; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Mojolicious-7.39/lib/Mojo/Collection.pm new/Mojolicious-7.42/lib/Mojo/Collection.pm --- old/Mojolicious-7.39/lib/Mojo/Collection.pm 2017-07-17 21:07:54.000000000 +0200 +++ new/Mojolicious-7.42/lib/Mojo/Collection.pm 2017-08-16 23:40:47.000000000 +0200 @@ -99,6 +99,8 @@ return $self->new(grep { !$seen{$_}++ } @$self); } +sub with_roles { shift->Mojo::Base::with_roles(@_) } + sub _flatten { map { _ref($_) ? _flatten(@$_) : $_ } @_; } @@ -367,6 +369,13 @@ # "[[1, 2], [2, 1]]" c([1, 2], [2, 1], [3, 2])->uniq(sub{ $_->[1] })->to_array; +=head2 with_roles + + my $new_class = Mojo::Collection->with_roles('Mojo::Collection::Role::One'); + my $new_class = Mojo::Collection->with_roles('+One', '+Two'); + +Alias for L<Mojo::Base/"with_roles">. + =head1 SEE ALSO L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicious.org>. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Mojolicious-7.39/lib/Mojo/DOM.pm new/Mojolicious-7.42/lib/Mojo/DOM.pm --- old/Mojolicious-7.39/lib/Mojo/DOM.pm 2017-07-17 21:07:54.000000000 +0200 +++ new/Mojolicious-7.42/lib/Mojo/DOM.pm 2017-08-16 23:41:23.000000000 +0200 @@ -13,7 +13,7 @@ use Mojo::Collection; use Mojo::DOM::CSS; use Mojo::DOM::HTML; -use Scalar::Util qw(blessed weaken); +use Scalar::Util 'weaken'; sub all_text { _text(_nodes(shift->tree), 1) } @@ -178,6 +178,8 @@ return exists $self->{multiple} ? $v->size ? $v->to_array : undef : $v->last; } +sub with_roles { shift->Mojo::Base::with_roles(@_) } + sub wrap { shift->_wrap(0, @_) } sub wrap_content { shift->_wrap(1, @_) } @@ -946,6 +948,13 @@ # "on" $dom->parse('<input name=test type=checkbox>')->at('input')->val; +=head2 with_roles + + my $new_class = Mojo::DOM->with_roles('Mojo::DOM::Role::One'); + my $new_class = Mojo::DOM->with_roles('+One', '+Two'); + +Alias for L<Mojo::Base/"with_roles">. + =head2 wrap $dom = $dom->wrap('<div></div>'); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Mojolicious-7.39/lib/Mojo/File.pm new/Mojolicious-7.42/lib/Mojo/File.pm --- old/Mojolicious-7.39/lib/Mojo/File.pm 2017-07-17 21:07:54.000000000 +0200 +++ new/Mojolicious-7.42/lib/Mojo/File.pm 2017-08-16 23:41:49.000000000 +0200 @@ -137,6 +137,8 @@ sub to_string {"${$_[0]}"} +sub with_roles { shift->Mojo::Base::with_roles(@_) } + 1; =encoding utf8 @@ -446,6 +448,13 @@ Stringify the path. +=head2 with_roles + + my $new_class = Mojo::File->with_roles('Mojo::File::Role::One'); + my $new_class = Mojo::File->with_roles('+One', '+Two'); + +Alias for L<Mojo::Base/"with_roles">. + =head1 OPERATORS L<Mojo::File> overloads the following operators. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Mojolicious-7.39/lib/Mojo/Log.pm new/Mojolicious-7.42/lib/Mojo/Log.pm --- old/Mojolicious-7.39/lib/Mojo/Log.pm 2017-07-17 21:07:54.000000000 +0200 +++ new/Mojolicious-7.42/lib/Mojo/Log.pm 2017-08-14 10:12:20.000000000 +0200 @@ -19,8 +19,7 @@ has level => 'debug'; has max_history_size => 10; has 'path'; -has short => - sub { $ENV{INVOCATION_ID} && $ENV{JOURNAL_STREAM} && !shift->path }; +has short => sub { $ENV{MOJO_LOG_SHORT} }; # Supported log levels my %LEVEL = (debug => 1, info => 2, warn => 3, error => 4, fatal => 5); @@ -178,8 +177,7 @@ $log = $log->short($bool); Generate short log messages without a timestamp, suitable for systemd, defaults -to auto-detection based on the presence of a L</"path"> and the -C<INVOCATION_ID> and C<JOURNAL_STREAM> environment variables. +to the value of the C<MOJO_LOG_SHORT> environment variables. =head1 METHODS diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Mojolicious-7.39/lib/Mojolicious/Command/version.pm new/Mojolicious-7.42/lib/Mojolicious/Command/version.pm --- old/Mojolicious-7.39/lib/Mojolicious/Command/version.pm 2017-07-17 21:07:54.000000000 +0200 +++ new/Mojolicious-7.42/lib/Mojolicious/Command/version.pm 2017-08-11 10:27:26.000000000 +0200 @@ -11,11 +11,12 @@ sub run { my $self = shift; - my $ev = eval 'use Mojo::Reactor::EV; 1' ? $EV::VERSION : 'n/a'; + my $ev = eval { require Mojo::Reactor::EV; 1 } ? $EV::VERSION : 'n/a'; my $socks = Mojo::IOLoop::Client->can_socks ? $IO::Socket::Socks::VERSION : 'n/a'; my $tls = Mojo::IOLoop::TLS->can_tls ? $IO::Socket::SSL::VERSION : 'n/a'; my $nnr = Mojo::IOLoop::Client->can_nnr ? $Net::DNS::Native::VERSION : 'n/a'; + my $roles = Mojo::Base->ROLES ? $Role::Tiny::VERSION : 'n/a'; print <<EOF; CORE @@ -27,6 +28,7 @@ IO::Socket::Socks 0.64+ ($socks) IO::Socket::SSL 1.94+ ($tls) Net::DNS::Native 0.15+ ($nnr) + Role::Tiny 2.000001+ ($roles) EOF diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Mojolicious-7.39/lib/Mojolicious/Guides/Contributing.pod new/Mojolicious-7.42/lib/Mojolicious/Guides/Contributing.pod --- old/Mojolicious-7.39/lib/Mojolicious/Guides/Contributing.pod 2017-07-18 18:47:28.000000000 +0200 +++ new/Mojolicious-7.42/lib/Mojolicious/Guides/Contributing.pod 2017-08-12 00:41:04.000000000 +0200 @@ -303,8 +303,8 @@ =head1 MORE You can continue with L<Mojolicious::Guides> now or take a look at the -L<Mojolicious wiki|http://github.com/kraih/mojo/wiki>, which contains a lot -more documentation and examples by many different authors. +L<Mojolicious wiki|http://github.com/kraih/mojo/wiki>, which contains a lot more +documentation and examples by many different authors. =head1 SUPPORT diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Mojolicious-7.39/lib/Mojolicious/Guides/Cookbook.pod new/Mojolicious-7.42/lib/Mojolicious/Guides/Cookbook.pod --- old/Mojolicious-7.39/lib/Mojolicious/Guides/Cookbook.pod 2017-07-17 21:07:54.000000000 +0200 +++ new/Mojolicious-7.42/lib/Mojolicious/Guides/Cookbook.pod 2017-08-12 00:40:53.000000000 +0200 @@ -795,36 +795,6 @@ You can also use L<Mojo::Transaction::WebSocket/"with_protocols"> to negotiate a subprotocol. -=head2 Testing WebSocket web services - -While the message flow on WebSocket connections can be rather dynamic, it more -often than not is quite predictable, which allows this rather pleasant -L<Test::Mojo> API to be used. - - use Test::More; - use Test::Mojo; - - # Include application - use FindBin; - require "$FindBin::Bin/../echo.pl"; - - # Test echo web service - my $t = Test::Mojo->new; - $t->websocket_ok('/echo') - ->send_ok('Hello Mojo!') - ->message_ok - ->message_is('echo: Hello Mojo!') - ->finish_ok; - - # Test JSON web service - $t->websocket_ok('/echo.json') - ->send_ok({json => {test => [1, 2, 3]}}) - ->message_ok - ->json_message_is('/test', [1, 2, 3]) - ->finish_ok; - - done_testing(); - =head2 EventSource web service EventSource is a special form of long polling where you can use @@ -1779,8 +1749,8 @@ =head1 MORE You can continue with L<Mojolicious::Guides> now or take a look at the -L<Mojolicious wiki|http://github.com/kraih/mojo/wiki>, which contains a lot -more documentation and examples by many different authors. +L<Mojolicious wiki|http://github.com/kraih/mojo/wiki>, which contains a lot more +documentation and examples by many different authors. =head1 SUPPORT diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Mojolicious-7.39/lib/Mojolicious/Guides/FAQ.pod new/Mojolicious-7.42/lib/Mojolicious/Guides/FAQ.pod --- old/Mojolicious-7.39/lib/Mojolicious/Guides/FAQ.pod 2017-07-21 18:10:18.000000000 +0200 +++ new/Mojolicious-7.42/lib/Mojolicious/Guides/FAQ.pod 2017-08-12 00:40:40.000000000 +0200 @@ -35,7 +35,8 @@ discourage adding non-optional ones in favor of a faster and more painless installation process. And we do in fact already use several optional CPAN modules such as L<EV>, L<IO::Socket::Socks>, L<IO::Socket::SSL>, -L<Net::DNS::Native> and L<Plack> to provide advanced functionality if possible. +L<Net::DNS::Native>, L<Plack> and L<Role::Tiny> to provide advanced +functionality if possible. =head2 Why reinvent wheels? @@ -236,8 +237,8 @@ =head1 MORE You can continue with L<Mojolicious::Guides> now or take a look at the -L<Mojolicious wiki|http://github.com/kraih/mojo/wiki>, which contains a lot -more documentation and examples by many different authors. +L<Mojolicious wiki|http://github.com/kraih/mojo/wiki>, which contains a lot more +documentation and examples by many different authors. =head1 SUPPORT diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Mojolicious-7.39/lib/Mojolicious/Guides/Growing.pod new/Mojolicious-7.42/lib/Mojolicious/Guides/Growing.pod --- old/Mojolicious-7.39/lib/Mojolicious/Guides/Growing.pod 2017-07-17 21:07:54.000000000 +0200 +++ new/Mojolicious-7.42/lib/Mojolicious/Guides/Growing.pod 2017-08-12 00:40:32.000000000 +0200 @@ -816,8 +816,8 @@ =head1 MORE You can continue with L<Mojolicious::Guides> now or take a look at the -L<Mojolicious wiki|http://github.com/kraih/mojo/wiki>, which contains a lot -more documentation and examples by many different authors. +L<Mojolicious wiki|http://github.com/kraih/mojo/wiki>, which contains a lot more +documentation and examples by many different authors. =head1 SUPPORT diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Mojolicious-7.39/lib/Mojolicious/Guides/Rendering.pod new/Mojolicious-7.42/lib/Mojolicious/Guides/Rendering.pod --- old/Mojolicious-7.39/lib/Mojolicious/Guides/Rendering.pod 2017-07-17 21:07:54.000000000 +0200 +++ new/Mojolicious-7.42/lib/Mojolicious/Guides/Rendering.pod 2017-08-12 00:40:24.000000000 +0200 @@ -1501,8 +1501,8 @@ =head1 MORE You can continue with L<Mojolicious::Guides> now or take a look at the -L<Mojolicious wiki|http://github.com/kraih/mojo/wiki>, which contains a lot -more documentation and examples by many different authors. +L<Mojolicious wiki|http://github.com/kraih/mojo/wiki>, which contains a lot more +documentation and examples by many different authors. =head1 SUPPORT diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Mojolicious-7.39/lib/Mojolicious/Guides/Routing.pod new/Mojolicious-7.42/lib/Mojolicious/Guides/Routing.pod --- old/Mojolicious-7.39/lib/Mojolicious/Guides/Routing.pod 2017-07-17 21:07:54.000000000 +0200 +++ new/Mojolicious-7.42/lib/Mojolicious/Guides/Routing.pod 2017-08-12 00:40:03.000000000 +0200 @@ -1069,8 +1069,8 @@ =head1 MORE You can continue with L<Mojolicious::Guides> now or take a look at the -L<Mojolicious wiki|http://github.com/kraih/mojo/wiki>, which contains a lot -more documentation and examples by many different authors. +L<Mojolicious wiki|http://github.com/kraih/mojo/wiki>, which contains a lot more +documentation and examples by many different authors. =head1 SUPPORT diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Mojolicious-7.39/lib/Mojolicious/Guides/Testing.pod new/Mojolicious-7.42/lib/Mojolicious/Guides/Testing.pod --- old/Mojolicious-7.39/lib/Mojolicious/Guides/Testing.pod 1970-01-01 01:00:00.000000000 +0100 +++ new/Mojolicious-7.42/lib/Mojolicious/Guides/Testing.pod 2017-08-16 23:53:50.000000000 +0200 @@ -0,0 +1,718 @@ + +=encoding utf8 + +=head1 NAME + +Mojolicious::Guides::Testing - Web Application Testing Made Easy + +=head1 OVERVIEW + +This document is an introduction to testing web applications with L<Test::Mojo>. +L<Test::Mojo> can be thought of as a module that provides all of the tools and +testing assertions needed to test web applications in a Perl-ish way. + +While L<Test::Mojo> can be used to test any web application, it has shortcuts +designed to make testing L<Mojolicious> web applications easy and pain-free. + +Please refer to the L<Test::Mojo> documentation for a complete reference to many +of the ideas and syntax introduced in this document. + +A test file for a simple web application might look like: + + use Mojo::Base -strict; + + use Test::Mojo; + use Test::More; + + # Start a Mojolicious app named "Celestial" + my $t = Test::Mojo->new('Celestial'); + + # Post a JSON document + $t->post_ok('/notifications' => json => {event => 'full moon'}) + ->status_is(201) + ->json_is('/message' => 'notification created'); + + # Perform GET requests and look at the responses + $t->get_ok('/sunrise') + ->status_is(200) + ->content_like(qr/ am$/); + $t->get_ok('/sunset') + ->status_is(200) + ->content_like(qr/ pm$/); + + # Post a URL-encoded form + $t->post_ok('/insurance' => form => {name => 'Jimmy', amount => '€3.000.000'}) + ->status_is(200); + + # Use Test::More's like() to check the response + like $t->tx->res->dom->at('div#thanks')->text, qr/thank you/, 'thanks'; + + done_testing(); + +In the rest of this document we'll explore these concepts and others related to +L<Test::Mojo>. + +=head1 CONCEPTS + +Essentials every L<Mojolicious> developer should know. + +=head2 L<Test::Mojo> at a glance + +The L<Test::More> module bundled with Perl includes several primitive test +assertions, such as C<ok>, C<is>, C<isnt>, C<like>, C<unlike>, C<cmp_ok>, etc. +An assertion "passes" if its expression returns a true value. The assertion +method prints "ok" or "not ok" if an assertion passes or fails (respectively). + +L<Test::Mojo> supplies additional test assertions organized around the web +application request/response transaction (transport, response headers, response +bodies, etc.), and WebSocket communications. + +One interesting thing of note: the return value of L<Test::Mojo> object +assertions is always the test object itself, allowing us to "chain" test +assertion methods. So rather than grouping related test statements like this: + + $t->get_ok('/frogs'); + $t->status_is(200); + $t->content_like(qr/bullfrog/); + $t->content_like(qr/hypnotoad/); + +Method chaining allows us to connect test assertions that belong together: + + $t->get_ok('/frogs') + ->status_is(200) + ->content_like(qr/bullfrog/) + ->content_like(qr/hypnotoad/); + +This makes for a much more I<concise> and I<coherent> testing experience: +concise because we are not repeating the invocant for each test, and coherent +because assertions that belong to the same request are syntactically bound in +the same method chain. + +Occasionally it makes sense to break up a test to perform more complex +assertions on a response. L<Test::Mojo> exposes the entire transaction object so +you can get all the data you need from a response: + + $t->put_ok('/bees' => json => {type => 'worker', name => 'Karl'}) + ->status_is(202) + ->json_has('/id'); + + # Pull out the id from the response + my $newbee = $t->tx->res->json('/id'); + + # Make a new request with data from the previous response + $t->get_ok("/bees/$newbee") + ->status_is(200) + ->json_is('/name' => 'Karl'); + +The L<Test::Mojo> object is I<stateful>. As long as we haven't started a new +transaction by invoking one of the C<*_ok> methods, the request and response +objects from the previous transaction are available in the L<Test::Mojo> +object: + + # First transaction + $t->get_ok('/frogs?q=bullfrog' => {'Content-Type' => 'application/json'}) + ->status_is(200) + ->json_like('/0/species' => qr/catesbeianus/i); + + # Still first transaction + $t->content_type_is('application/json'); + + # Second transaction + $t->get_ok('/frogs?q=banjo' => {'Content-Type' => 'text/html'}) + ->status_is(200) + ->content_like(qr/interioris/i); + + # Still second transaction + $t->content_type_is('text/html'); + +This statefulness also enables L<Test::Mojo> to handle sessions, follow +redirects, and inspect past responses during a redirect. + +=head2 The L<Test::Mojo> object + +The L<Test::Mojo> object manages the Mojolicious application lifecycle (if a +Mojolicious application class is supplied) as well as exposes the built-in +L<Mojo::UserAgent> object. To create a bare L<Test::Mojo> object: + + my $t = Test::Mojo->new; + +This object initializes a L<Mojo::UserAgent> object and provides a variety of +test assertion methods for accessing a web application. For example, with this +object, we could test any running web application: + + $t->get_ok('https://www.google.com/') + ->status_is(200) + ->content_like(qr/search/i); + +You can access the user agent directly if you want to make web requests without +triggering test assertions: + + my $tx = $t->ua->post( + 'https://duckduckgo.com/html' => form => {q => 'hypnotoad'}); + $tx->result->dom->find('a.result__a')->each(sub { say $_->text }); + +See L<Mojo::UserAgent> for the complete API and return values. + +=head2 Testing Mojolicious applications + +If you pass the name of a L<Mojolicious> application class (e.g., 'MyApp') to +the L<Test::Mojo> constructor, L<Test::Mojo> will instantiate the class and +start it, and cause it to listen on a random (unused) port number. Testing a +Mojolicious application using L<Test::Mojo> will never conflict with running +applications, including the application you're testing. + +The L<Mojo::UserAgent> object in L<Test::Mojo> will know where the application +is running and make requests to it. Once the tests have completed, the +L<Mojolicious> application will be torn down. + + # Listens on localhost:32114 (some unused TCP port) + my $t = Test::Mojo->new('Frogs'); + +This object initializes a L<Mojo::UserAgent> object, loads the Mojolicious +application C<Frogs>, binds and listens on a free TCP port (e.g., 32114), and +starts the application event loop. When the L<Test::Mojo> object (C<$t>) goes +out of scope, the application is stopped. + +Relative URLs in the test object method assertions (C<get_ok>, C<post_ok>, etc.) +will be sent to the Mojolicious application started by L<Test::Mojo>: + + # Rewritten to "http://localhost:32114/frogs" + $t->get_ok('/frogs'); + +L<Test::Mojo> has a lot of handy shortcuts built into it to make testing +L<Mojolicious> or L<Mojolicious::Lite> applications enjoyable. + +=head3 An example + +Let's spin up a Mojolicious application using C<mojo generate app MyApp>. The +C<mojo> utility will create a working application and a C<t> directory with a +working test file: + + $ mojo generate app MyApp + [mkdir] /my_app/script + [write] /my_app/script/my_app + [chmod] /my_app/script/my_app 744 + ... + [mkdir] /my_app/t + [write] /my_app/t/basic.t + ... + +Let's run the tests (we'll create the C<log> directory to quiet the application +output): + + $ cd my_app + $ mkdir log + $ prove -lv t + t/basic.t .. + ok 1 - GET / + ok 2 - 200 OK + ok 3 - content is similar + 1..3 + ok + All tests successful. + Files=1, Tests=3, 0 wallclock secs ( 0.03 usr 0.01 sys + 0.33 cusr 0.07 + csys = 0.44 CPU) + Result: PASS + +The boilerplate test file looks like this: + + use Mojo::Base -strict; + + use Test::More; + use Test::Mojo; + + my $t = Test::Mojo->new('MyApp'); + $t->get_ok('/')->status_is(200)->content_like(qr/Mojolicious/i); + + done_testing(); + +Here we can see our application class name C<MyApp> is passed to the +L<Test::Mojo> constructor. Under the hood, L<Test::Mojo> creates a new +L<Mojo::Server> instance, loads C<MyApp> (which we just created), and runs the +application. We write our tests with relative URLs because L<Test::Mojo> takes +care of getting the request to the running test application (since its port may +change between runs). + +=head3 Testing with configuration data + +We can alter the behavior of our application using environment variables (such +as C<MOJO_MODE>) and through configuration values. One nice feature of +L<Test::Mojo> is its ability to pass configuration values directly from its +constructor. + +Let's modify our application and add a "feature flag" to enable a new feature +when the C<enable_weather> configuration value is set: + + # Load configuration from hash returned by "my_app.conf" + my $config = $self->plugin('Config'); + + # Normal route to controller + $r->get('/')->to('example#welcome'); + + # NEW: this route only exists if "enable_weather" is set in the configuration + if ($config->{enable_weather}) { + $r->get('/weather' => sub { shift->render(text => "It's hot! 🔥") } + } + +To test this new feature, we don't even need to create a configuration file—we +can simply pass the configuration data to the application directly via +L<Test::Mojo>'s constructor: + + my $t = Test::Mojo->new(MyApp => {enable_weather => 1}); + $t->get_ok('/')->status_is(200)->content_like(qr/Mojolicious/i); + $t->get_ok('/weather')->status_is(200)->content_like(qr/🔥/); + +When we run these tests, L<Test::Mojo> will pass this configuration data to the +application, which will cause it to create a special C</weather> route that we +can access in our tests. Unless C<enable_weather> is set in a configuration +file, this route will not exist when the application runs. Feature flags like +this allow us to do soft rollouts of features, targeting a small audience for a +period of time. Once the feature has been proven, we can refactor the +conditional and make it a full release. + +This example shows how easy it is to start testing a Mojolicious application and +how to set specific application configuration directives from a test file. + +=head3 Testing application helpers + +Let's say we register a helper in our application to generate an HTTP Basic +Authorization header: + + use Mojo::Util 'b64_encode'; + + app->helper(basic_auth => sub { + my ($c, @values) = @_; + return {Authorization => 'Basic ' . b64_encode join(':' => @values), ''}; + }); + +How do we test application helpers like this? L<Test::Mojo> has access to the +application object, which allows us to invoke helpers from our test file: + + my $t = Test::Mojo->new('MyApp'); + + is_deeply $t->app->basic_auth(bif => "Bif's Passwerdd"), + {Authorization => 'Basic YmlmOkJpZidzIFBhc3N3ZXJkZA=='}, + 'correct header value'; + +Any aspect of the application (helpers, plugins, routes, etc.) can be +introspected from L<Test::Mojo> through the application object. This enables us +to get deep test coverage of L<Mojolicious>-based applications. + +=head1 ASSERTIONS + +This section describes the basic test assertions supplied by L<Test::Mojo>. +There are four broad categories of assertions for HTTP requests: + +=over 2 + +=item * HTTP requests + +=item * HTTP response status + +=item * HTTP response headers + +=item * HTTP response content/body + +=back + +WebSocket test assertions are covered in L</Testing WebSocket web services>. + +=head2 HTTP request assertions + +L<Test::Mojo> has a L<Mojo::UserAgent> object that allows it to make HTTP +requests and check for HTTP transport errors. HTTP request assertions include +C<get_ok>, C<post_ok>, etc. These assertions do not test whether the request +was handled I<successfully>, only that the web application handled the request +in an HTTP compliant way. + +You may also make HTTP requests using custom verbs (beyond C<GET>, C<POST>, +C<PUT>, etc.) by building your own transaction object. See +L</"Custom transactions"> below. + +=head3 Using HTTP request assertions + +To post a URL-encoded form to the C</calls> endpoint of an application, we +simply use the C<form> content type shortcut: + + $t->post_ok('/calls' => form => {to => '+43.55.555.5555'}); + +Which will create the following HTTP request: + + POST /calls HTTP/1.1 + Content-Length: 20 + Content-Type: application/x-www-form-urlencoded + + to=%2B43.55.555.5555 + +The C<*_ok> HTTP request assertion methods accept the same arguments as their +corresponding L<Mojo::UserAgent> methods (except for the callback argument). +This allows us to set headers and build query strings for authentic test +situations: + + $t->get_ok('/internal/personnel' => {Authorization => 'Token secret-password'} + => form => {q => 'Professor Plum'}); + +which generates the following request: + + GET /internal/personnel?q=Professor+Plum HTTP/1.1 + Content-Length: 0 + Authorization: Token secret-password + +The C<form> content generator (see L<Mojo::UserAgent::Transactor>) will generate +a query string for C<GET> requests and C<application/x-www-form-urlencoded> or +C<multipart/form-data> for POST requests. + +While these C<*_ok> assertions make the HTTP I<requests> we expect, they tell us +little about I<how well> the application handled the request. The application +we're testing might have returned any content-type, body, or HTTP status code +(200, 302, 400, 404, 500, etc.) and we wouldn't know it. + +L<Test::Mojo> provides assertions to test almost every aspect of the HTTP +response, including the HTTP response status code, the value of the +C<Content-Type> header, and other arbitrary HTTP header information. + +=head2 HTTP response status code + +While not technically an HTTP header, the status line is the first line in an +HTTP response and is followed by the response headers. Testing the response +status code is common in REST-based and other web applications that use the HTTP +status codes to broadly indicate the type of response the server is returning. + +Testing the status code is as simple as adding the C<status_is> assertion: + + $t->post_ok('/doorbell' => form => {action => 'ring once'}) + ->status_is(200); + +Along with C<status_isnt>, this will cover most needs. For more elaborate status +code testing, you can access the response internals directly: + + $t->post_ok('/doorbell' => form => {action => 'ring once'}); + is $t->tx->res->message, 'Moved Permanently', 'try next door'; + +=head2 HTTP response headers + +L<Test::Mojo> allows us to inspect and make assertions about HTTP response +headers. The C<Content-Type> header is commonly tested and has its own +assertion: + + $t->get_ok('/map-of-the-world.pdf') + ->content_type_is('application/pdf'); + +This is equivalent to the more verbose: + + $t->get_ok('/map-of-the-world.pdf') + ->header_is('Content-Type' => 'application/pdf'); + +We can test for multiple headers in a single response using method chains: + + $t->get_ok('/map-of-the-world.pdf') + ->content_type_is('application/pdf') + ->header_isnt('Compression' => 'gzip') + ->header_unlike('Server' => qr/IIS/i); + +=head2 HTTP response content assertions + +L<Test::Mojo> also exposes a rich set of assertions for testing the body of a +response, whether that body be HTML, plain-text, or JSON. The C<content_*> +methods look at the body of the response as plain text (as defined by the +response's character set): + + $t->get_ok('/scary-things/spiders.json') + ->content_is('{"arachnid":"brown recluse"}'); + +Although this is a JSON document, C<content_is> treats it as if it were a text +document. This may be useful for situations where we're looking for a particular +string and not concerned with the structure of the document. For example, we can +do the same thing with an HTML document: + + $t->get_ok('/scary-things/spiders.html') + ->content_like(qr{<title>All The Spiders</title>}); + +But because L<Test::Mojo> has access to everything that L<Mojo::UserAgent> does, +we can introspect JSON documents as well as DOM-based documents (HTML, XML) with +assertions that allow us to check for the existence of elements as well as +inspect the content of text nodes. + +=head3 JSON response assertions + +L<Test::Mojo>'s L<Mojo::UserAgent> has access to a JSON parser, which allows us +to test to see if a JSON response contains a value at a location in the document +using JSON pointer syntax: + + $t->get_ok('/animals/friendly.json') + ->json_has('/beings/jeremiah/age'); + +This assertion tells us that the C<friendly.json> document contains a value at +the C</beings/jeremiah/age> JSON pointer location. We can also inspect the value +at JSON pointer locations: + + $t->get_ok('/animals/friendly.json') + ->json_has('/beings/jeremiah/age') + ->json_is('/beings/jeremiah/age' => 42) + ->json_like('/beings/jeremiah/species' => qr/bullfrog/i); + +JSON pointer syntax makes testing JSON responses simple and readable. + +=head3 DOM response assertions + +We can also inspect HTML and XML responses using the L<Mojo::DOM> parser in the +user agent. Here are a few examples from the L<Test::Mojo> documentation: + + $t->text_is('div.foo[x=y]' => 'Hello!'); + $t->text_is('html head title' => 'Hello!', 'right title'); + +The L<Mojo::DOM> parser uses the CSS selector syntax described in +L<Mojo::DOM::CSS>, allowing us to test for values in HTML and XML documents +without resorting to typically verbose and inflexible DOM traversal methods. + +=head1 ADVANCED TOPICS + +This section describes some complex (but common) testing situations that +L<Test::Mojo> excels in making simple. + +=head2 Redirects + +The L<Mojo::UserAgent> object in L<Test::Mojo> can handle HTTP redirections +internally to whatever level you need. Let's say we have a web service that +redirects C</1> to C</2>, C</2> redirects to C</3>, C</3> redirects to C</4>, +and C</4> redirects to C</5>: + + GET /1 + +returns: + + 302 Found + Location: /2 + +and: + + GET /2 + +returns: + + 302 Found + Location: /3 + +and so forth, up to C</5>: + + GET /5 + +which returns the data we wanted: + + 200 OK + + {"message":"this is five"} + +We can tell the user agent in L<Test::Mojo> how to deal with redirects. Each +test is making a request to C<GET /1>, but we vary the number of redirects the +user agent should follow with each test: + + my $t = Test::Mojo->new; + + $t->get_ok('/1') + ->header_is(location => '/2'); + + $t->ua->max_redirects(1); + $t->get_ok('/1') + ->header_is(location => '/3'); + + $t->ua->max_redirects(2); + $t->get_ok('/1') + ->header_is(location => '/4'); + + # Look at the previous hop + is $t->tx->previous->res->headers->location, '/3', 'previous redirect'; + + $t->ua->max_redirects(3); + $t->get_ok('/1') + ->header_is(location => '/5'); + + $t->ua->max_redirects(4); + $t->get_ok('/1') + ->json_is('/message' => 'this is five'); + +When we set C<max_redirects>, it stays set for the life of the test object until +we change it. + +L<Test::Mojo>'s handling of HTTP redirects eliminates the need for making many, +sometimes an unknown number, of redirections to keep testing precise and easy to +follow (ahem). + +=head2 Cookies and session management + +We can use L<Test::Mojo> to test applications that keep session state in +cookies. By default, the L<Mojo::UserAgent> object in L<Test::Mojo> will manage +session for us by saving and sending cookies automatically, just like common web +browsers: + + use Mojo::Base -strict; + + use Test::More; + use Test::Mojo; + + my $t = Test::Mojo->new('MyApp'); + + # No authorization cookie + $t->get_ok('/') + ->status_is(401) + ->content_is('Please log in'); + + # Application sets an authorization cookie + $t->post_ok('/login' => form => {password => 'let me in'}) + ->status_is(200) + ->content_is('You are logged in'); + + # Sends the cookie from the previous transaction + $t->get_ok('/') + ->status_is(200) + ->content_like(qr/You logged in at \d+/); + + # Clear the cookies + $t->reset_session; + + # No authorization cookie again + $t->get_ok('/') + ->status_is(401) + ->content_is('Please log in'); + +We can also inspect cookies in responses for special values through the +transaction's response (L<Mojo::Message::Response>) object: + + $t->get_ok('/'); + like $t->tx->res->cookie('smarty'), qr/smarty=pants/, 'cookie found'; + +=head2 Custom transactions + +Let's say we have an application that responds to a new HTTP verb C<RING> and to +use it we must also pass in a secret cookie value. This is not a problem. We can +test the application by creating a L<Mojo::Transaction> object, setting the +cookie (see L<Mojo::Message::Request>), then passing the transaction object to +C<request_ok>: + + # Use custom "RING" verb + my $tx = $t->ua->build_tx(RING => '/doorbell'); + + # Set a special cookie + $tx->req->cookies({name => 'Secret', value => "don't tell anybody"}); + + # Make the request + $t->request_ok($tx) + ->status_is(200) + ->json_is('/status' => 'ding dong'); + +=head2 Testing WebSocket web services + +While the message flow on WebSocket connections can be rather dynamic, it more +often than not is quite predictable, which allows this rather pleasant +L<Test::Mojo> WebSocket API to be used: + + use Mojo::Base -strict; + + use Test::More; + use Test::Mojo; + + # Test echo web service + my $t = Test::Mojo->new('EchoService'); + $t->websocket_ok('/echo') + ->send_ok('Hello Mojo!') + ->message_ok + ->message_is('echo: Hello Mojo!') + ->finish_ok; + + # Test JSON web service + $t->websocket_ok('/echo.json') + ->send_ok({json => {test => [1, 2, 3]}}) + ->message_ok + ->json_message_is('/test' => [1, 2, 3]) + ->finish_ok; + + done_testing(); + +Because of their inherent asynchronous nature, testing WebSocket communications +can be tricky. The L<Test::Mojo> WebSocket assertions serialize messages via +event loop primitives. This enables us to treat WebSocket messages as if they +were using the same request-response communication pattern we're accustomed to +with HTTP. + +To illustrate, let's walk through these tests. In the first test, we use the +C<websocket_ok> assertion to ensure that we can connect to our application's +WebSocket route at C</echo> and that it's "speaking" WebSocket protocol to us. +The next C<send_ok> assertion tests the connection again (in case it closed, for +example) and attempts to send the message C<Hello Mojo!>. The next assertion, +C<message_ok>, blocks (using the L<Mojo::IOLoop> singleton in the application) +and waits for a response from the server. The response is then compared with +C<'echo: Hello Mojo!'> in the C<message_is> assertion, and finally we close and +test our connection status again with C<finish_ok>. + +The second test is like the first, but now we're sending and expecting JSON +documents at C</echo.json>. In the C<send_ok> assertion we take advantage of +L<Mojo::UserAgent>'s JSON content generator (see L<Mojo::UserAgent::Transactor>) +to marshal hash and array references into JSON documents, and then send them as +a WebSocket message. We wait (block) for a response from the server with +C<message_ok>. Then because we're expecting a JSON document back, we can +leverage C<json_message_ok> which parses the WebSocket response body and returns +an object we can access through L<Mojo::JSON::Pointer> syntax. Then we close +(and test) our WebSocket connection. + +Testing WebSocket servers does not get any simpler than with L<Test::Mojo>. + +=head2 Extending L<Test::Mojo> + +If you see that you're writing a lot of test assertions that aren't chainable, +you may benefit from writing your own test assertions. Let's say we want to test +the C<Location> header after a redirect. We'll create a new class with +L<Role::Tiny> that implements a test assertion named C<location_is>: + + package Test::Mojo::Role::Location; + + use Role::Tiny; + use Test::More; + + sub location_is { + my ($t, $value, $desc) = @_; + $desc ||= "Location: $value"; + local $Test::Builder::Level = $Test::Builder::Level + 1; + return $t->success(is($t->tx->res->headers->location, $value, $desc)); + } + + 1; + +When we make new test assertions using roles, we want to use method signatures +that match other C<*_is> methods in L<Test::Mojo>, so here we accept the test +object, the value to compare, and an optional description. + +We assign a default description value (C<$desc>), set the L<Test::Builder> +C<Level> global variable one level higher (which tells L<Test::Builder> how far +up the call stack to look when something fails), then we use L<Test::More>'s +C<is> function to compare the location header with the expected header value. We +wrap that in the C<success> attribute, which records the boolean test result and +propagates the L<Test::Mojo> object for method chaining. + +With this new package, we're ready to compose a new test object that uses the +role: + + my $t = Test::Mojo->with_roles('+Location')->new('MyApp'); + + $t->post_ok('/redirect/mojo' => json => {message => 'Mojo, here I come!'}) + ->status_is(302) + ->location_is('http://mojolicious.org') + ->or(sub { diag 'I miss tempire.' }); + +In this section we've covered how to add custom test assertions to L<Test::Mojo> +with roles and how to use those roles to simplify testing. + +=head1 MORE + +You can continue with L<Mojolicious::Guides> now or take a look at the +L<Mojolicious wiki|http://github.com/kraih/mojo/wiki>, which contains a lot more +documentation and examples by many different authors. + +=head1 SUPPORT + +If you have any questions the documentation might not yet answer, don't +hesitate to ask on the +L<mailing list|http://groups.google.com/group/mojolicious> or the official IRC +channel C<#mojo> on C<irc.perl.org> +(L<chat now!|https://chat.mibbit.com/?channel=%23mojo&server=irc.perl.org>). + +=cut diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Mojolicious-7.39/lib/Mojolicious/Guides/Tutorial.pod new/Mojolicious-7.42/lib/Mojolicious/Guides/Tutorial.pod --- old/Mojolicious-7.39/lib/Mojolicious/Guides/Tutorial.pod 2017-07-17 21:07:54.000000000 +0200 +++ new/Mojolicious-7.42/lib/Mojolicious/Guides/Tutorial.pod 2017-08-12 00:40:16.000000000 +0200 @@ -996,8 +996,8 @@ =head1 MORE You can continue with L<Mojolicious::Guides> now or take a look at the -L<Mojolicious wiki|http://github.com/kraih/mojo/wiki>, which contains a lot -more documentation and examples by many different authors. +L<Mojolicious wiki|http://github.com/kraih/mojo/wiki>, which contains a lot more +documentation and examples by many different authors. =head1 SUPPORT diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Mojolicious-7.39/lib/Mojolicious/Guides.pod new/Mojolicious-7.42/lib/Mojolicious/Guides.pod --- old/Mojolicious-7.39/lib/Mojolicious/Guides.pod 2017-07-17 21:07:54.000000000 +0200 +++ new/Mojolicious-7.42/lib/Mojolicious/Guides.pod 2017-08-12 00:12:40.000000000 +0200 @@ -88,6 +88,11 @@ Generating content with the L<Mojolicious> renderer. +=item L<Mojolicious::Guides::Testing> + +Powerful yet elegant testing techniques and tools for L<Mojolicious> and other +web applications. + =item L<Mojolicious::Guides::Cookbook> Cooking with L<Mojolicious>, recipes for every taste. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Mojolicious-7.39/lib/Mojolicious.pm new/Mojolicious-7.42/lib/Mojolicious.pm --- old/Mojolicious-7.39/lib/Mojolicious.pm 2017-08-02 12:09:15.000000000 +0200 +++ new/Mojolicious-7.42/lib/Mojolicious.pm 2017-08-16 10:19:34.000000000 +0200 @@ -58,7 +58,7 @@ has validator => sub { Mojolicious::Validator->new }; our $CODENAME = 'Doughnut'; -our $VERSION = '7.39'; +our $VERSION = '7.42'; sub AUTOLOAD { my $self = shift; @@ -909,6 +909,8 @@ Dominique Dumont +Dotan Dimet + Douglas Christopher Wilson Eugen Konkov diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Mojolicious-7.39/lib/Test/Mojo.pm new/Mojolicious-7.42/lib/Test/Mojo.pm --- old/Mojolicious-7.39/lib/Test/Mojo.pm 2017-07-17 21:07:54.000000000 +0200 +++ new/Mojolicious-7.42/lib/Test/Mojo.pm 2017-08-12 00:12:28.000000000 +0200 @@ -450,6 +450,8 @@ be set to C<debug> or C<fatal>, depending on the value of the C<HARNESS_IS_VERBOSE> environment variable. +See L<Mojolicious::Guides::Testing> for more. + =head1 ATTRIBUTES L<Test::Mojo> implements the following attributes. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Mojolicious-7.39/t/mojo/log.t new/Mojolicious-7.42/t/mojo/log.t --- old/Mojolicious-7.39/t/mojo/log.t 2017-07-17 21:07:54.000000000 +0200 +++ new/Mojolicious-7.42/t/mojo/log.t 2017-08-14 10:11:31.000000000 +0200 @@ -50,22 +50,19 @@ like $log->format->(time, 'debug', qw(Test 1 2 3)), qr/^debug:\d+:Test:1:2:3$/, 'right format'; -# systemd +# Short log messages { $log = Mojo::Log->new; - ok !$log->short, 'systemd has not been detected'; + ok !$log->short, 'long messages'; like $log->format->(time, 'debug', 'Test 123'), qr/^\[.*\] \[debug\] Test 123\n$/, 'right format'; - local $ENV{JOURNAL_STREAM} = '1:23456'; - local $ENV{INVOCATION_ID} = 1; + local $ENV{MOJO_LOG_SHORT} = 1; $log = Mojo::Log->new; - ok $log->short, 'systemd has been detected'; + ok $log->short, 'short messages'; + $log = Mojo::Log->new(short => 1); + ok $log->short, 'short messages'; like $log->format->(time, 'debug', 'Test 123'), qr/^\[debug\] Test 123\n$/, 'right format'; - $log = Mojo::Log->new(path => $path); - ok !$log->short, 'a path will be used'; - like $log->format->(time, 'debug', 'Test 123'), - qr/^\[.*\] \[debug\] Test 123\n$/, 'right format'; } # Events diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Mojolicious-7.39/t/mojo/roles.t new/Mojolicious-7.42/t/mojo/roles.t --- old/Mojolicious-7.39/t/mojo/roles.t 1970-01-01 01:00:00.000000000 +0100 +++ new/Mojolicious-7.42/t/mojo/roles.t 2017-08-16 23:42:31.000000000 +0200 @@ -0,0 +1,100 @@ +use Mojo::Base -strict; + +use Test::More; + +BEGIN { + plan skip_all => 'Role::Tiny 2.000001+ required for this test!' + unless Mojo::Base->ROLES; +} + +package Mojo::RoleTest::Role::LOUD; +use Role::Tiny; + +sub yell {'HEY!'} + +requires 'name'; + +sub hello { + my $self = shift; + return $self->yell . ' ' . uc($self->name) . '!!!'; +} + +package Mojo::RoleTest::Role::quiet; +use Role::Tiny; + +requires 'name'; + +sub whisper { + my $self = shift; + return 'psst, ' . lc($self->name); +} + +package Mojo::RoleTest; +use Mojo::Base -base; + +has name => 'bob'; + +sub hello { + my ($self) = shift; + return 'hello ' . $self->name; +} + +package Mojo::RoleTest::Hello; +use Role::Tiny; + +sub hello {'hello mojo!'} + +package main; + +use Mojo::ByteStream; +use Mojo::Collection; +use Mojo::DOM; +use Mojo::File; + +# Plain class +my $obj = Mojo::RoleTest->new(name => 'Ted'); +is $obj->name, 'Ted', 'attribute'; +is $obj->hello, 'hello Ted', 'method'; + +# Single role +my $obj2 = Mojo::RoleTest->with_roles('Mojo::RoleTest::Role::LOUD')->new; +is $obj2->hello, 'HEY! BOB!!!', 'role method'; +is $obj2->yell, 'HEY!', 'another role method'; + +# Single role (shorthand) +my $obj4 = Mojo::RoleTest->with_roles('+LOUD')->new; +is $obj4->hello, 'HEY! BOB!!!', 'role method'; +is $obj4->yell, 'HEY!', 'another role method'; + +# Multiple roles +my $obj3 = Mojo::RoleTest->with_roles('Mojo::RoleTest::Role::quiet', + 'Mojo::RoleTest::Role::LOUD')->new(name => 'Joel'); +is $obj3->name, 'Joel', 'base attribute'; +is $obj3->whisper, 'psst, joel', 'method from first role'; +is $obj3->hello, 'HEY! JOEL!!!', 'method from second role'; + +# Multiple roles (shorthand) +my $obj5 = Mojo::RoleTest->with_roles('+quiet', '+LOUD')->new(name => 'Joel'); +is $obj5->name, 'Joel', 'base attribute'; +is $obj5->whisper, 'psst, joel', 'method from first role'; +is $obj5->hello, 'HEY! JOEL!!!', 'method from second role'; + +# Multiple roles (mixed) +my $obj6 = Mojo::RoleTest->with_roles('Mojo::RoleTest::Role::quiet', '+LOUD') + ->new(name => 'Joel'); +is $obj6->name, 'Joel', 'base attribute'; +is $obj6->whisper, 'psst, joel', 'method from first role'; +is $obj6->hello, 'HEY! JOEL!!!', 'method from second role'; + +# Classes that are not subclasses of Mojo::Base +my $stream = Mojo::ByteStream->with_roles('Mojo::RoleTest::Hello')->new; +is $stream->hello, 'hello mojo!', 'right result'; +my $c = Mojo::Collection->with_roles('Mojo::RoleTest::Hello')->new; +is $c->hello, 'hello mojo!', 'right result'; +my $dom = Mojo::DOM->with_roles('Mojo::RoleTest::Hello')->new; +is $dom->hello, 'hello mojo!', 'right result'; +my $file = Mojo::File->with_roles('Mojo::RoleTest::Hello')->new; +is $file->hello, 'hello mojo!', 'right result'; + +done_testing(); +
