libtap-formatter-html-perl-0.11+dfsg.orig/ 0000755 0001750 0001750 00000000000 12076716223 017513 5 ustar salvi salvi libtap-formatter-html-perl-0.11+dfsg.orig/Changes 0000444 0001750 0001750 00000010660 12076610727 021011 0 ustar salvi salvi Revision history for 'TAP-Formatter-HTML' Perl module ----------------------------------------------------- All changes by Steve Purkis, unless otherwise noted. 0.11 + fixed RT #81922: tests no longer hang on Win32. Didn't get to the bottom of the issue (likely IPC::Open3 & redirecting STDOUT not playing nicely), but worked around it. + fixed a bug on Win32 where default js_uris & css_uris were being mangled: "C:%5C..." vs "C:\". May have affected other Win32 URIs too. 0.10 + fixed RT #82738: color method was not implemented. + fixed RT #74364: TT2 INCLUDE_PATH no longer set to all dirs in @INC, now it is set to parent directory TAP::Formatter::HTML was loaded from. 0.09 + upgraded to jQuery 1.4.2 + updated App::Prove::Plugin::HTML to simplify cmdline usage to the extent possible. + fixed RT #41457 & RT #49621: applied Neil Hemingway, Tim Esselens, and Panu Ervamaa's patches, and got everything working. This addresses: + small changes to the top-left corner menu (default_report.css) to render it corretly with Firefox 3 (Linux) + inline_js fix + added support for force_inline_js (not on by default, including jQuery inline causes errors) + fixed bug in handling of parse_errors in default_report.tt + fixed bug: nothing happens when I click on the test output. + XHTML support + fixed bug: click on summary changes location + fixed 2 minor javascript bugs where '#' was being used as a jQuery id ref + added new feature: column sorting via Christian Bach's jquery.tablesorter plugin (bundled from http://tablesorter.com). optional. + added new feature: up to top of test when the test filename is no longer visible. 0.08 + applied Steffen Schwigon's patch to display YAML TAP as pre-formatted text. + fixed RT #43871: missing dep on File::Temp 0.17 [reported by Paddy Newman] + fixed RT #37983: CSS and JS Paths on Win32 get mangled and documented solution. [reported by Chris Davaz] + wrote some selenium tests, finally. found & fixed small bug where a test's html_id wasn't being set. + fixed RT #48296: pod for js_uris mentions styles, not scripts [reported by Slaven Rezić] + started on RT #41457: only got "incline_js" fixed [reported by Neil Hemingway] 0.07 + you can now specify an output file for the HTML to be written to! + you can now set some config params using environment vars. this should make it easier to configure without having to write custom code. + started writing an App::Prove plugin + fixed RT #40306: Typo in TAP::Formatter::HTML (incline_js) (also reported by Nathan S. Watson-Haigh) + applied patch from Michael Hendricks (who's work on this was sponsored by Grant Street Group) for a bug he & others identified: If tests are aggregated in multiple phases (per http://xrl.us/bedfih), ... the HTML output shows only the results from the final phase. + finally wrote some more tests, though selenium tests still not done + fixed RT #41411: fails on empty directory * started using IO::* modules for stdout & output_fh 0.06 + fixed RT #37019: tempdir without cleanup [reported by Steffen Schwigon] 0.05 + fixed width issue: test files with hundreds of tests were causing uncontrollable width in summary, making the report pretty annoying to read. + added toggle to display all / failed tests [requested by Alex Monney] + highlight failed test file names + reduced the size of generated HTML by a further ~10-35% (depending on the number of tests you run): * set test title on mouse-over so it's included only once * moved inline JS out of default_report.tt2 to its own file that is included by default. * split css into 'page' & 'report' for ease of user maintainability. [requested by Steffen Schwigon] + fixed bug: % passed on test file != % ok, was really confusing people. [requested by Wolfram Pfeiffer] * upgraded to jquery-1.2.6 0.04 + reduced the size of the generated HTML (by 25% on avg for my tests): * added 'minify' option to strip leading tabs * used short css class names * used numeric html id's * got rid of some other unnecessary whitespace 0.03 + updated deps in Build.PL (again, *sigh*) 0.02 + updated deps in Build.PL + wrote more docs, fixed a few errors & omissions - removed bin/runtests_html (it was added before I found out about 'prove') 0.01 + created Thurs May 14 2008 libtap-formatter-html-perl-0.11+dfsg.orig/META.yml 0000444 0001750 0001750 00000001612 12076610727 020764 0 ustar salvi salvi --- abstract: 'TAP Test Harness output delegate for html output' author: - 'Steve Purkis ' build_requires: Module::Build: 0.20 Test::More: 0.01 configure_requires: Module::Build: 0.38 dynamic_config: 1 generated_by: 'Module::Build version 0.38, CPAN::Meta::Converter version 2.113640' license: perl meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: 1.4 name: TAP-Formatter-HTML provides: App::Prove::Plugin::HTML: file: lib/App/Prove/Plugin/HTML.pm version: 0.11 TAP::Formatter::HTML: file: lib/TAP/Formatter/HTML.pm version: 0.11 TAP::Formatter::HTML::Session: file: lib/TAP/Formatter/HTML/Session.pm version: 0.11 requires: File::Temp: 0.17 TAP::Parser: 3.10 Template: 2.14 Test::Harness: 3.17 URI: 1.35 accessors: 0.02 perl: v5.6.0 resources: license: http://dev.perl.org/licenses/ version: 0.11 libtap-formatter-html-perl-0.11+dfsg.orig/Build.PL 0000444 0001750 0001750 00000002001 12076610727 021000 0 ustar salvi salvi #!/usr/bin/perl =head1 NAME Build.PL - Build script generator for C module =head1 SYNOPSIS perl Build.PL ./Build ./Build test ./Build install =cut use strict; use warnings; use Module::Build; my $build = Module::Build->new ( module_name => 'TAP::Formatter::HTML', dist_version_from => 'lib/TAP/Formatter/HTML.pm', create_makefile_pl => 'passthrough', create_readme => 1, license => 'perl', requires => { 'perl' => '5.6.0', 'accessors' => '0.02', 'URI' => '1.35', 'Template' => '2.14', 'TAP::Parser' => '3.10', 'File::Temp' => '0.17', 'Test::Harness' => '3.17', }, build_requires => { 'Test::More' => '0.01', 'Module::Build' => '0.20', }, ); $build->add_build_element("tt2"); $build->add_build_element("css"); $build->add_build_element("js"); $build->create_build_script; __END__ =head1 AUTHOR Steve Purkis =cut libtap-formatter-html-perl-0.11+dfsg.orig/t/ 0000755 0001750 0001750 00000000000 12076610727 017760 5 ustar salvi salvi libtap-formatter-html-perl-0.11+dfsg.orig/t/30_tap_harness_compat.t 0000444 0001750 0001750 00000000462 12076610727 024321 0 ustar salvi salvi use strict; use warnings; use lib 'lib'; use lib 't/lib'; use Test::More 'no_plan'; use TAP::Harness; use TAP::Formatter::HTML; # RT #82738: required TAP::Harness formatter method not available... eval { TAP::Formatter::HTML->new->color(1); }; my $e = $@; ok(!$e, 'no error on color') || diag($e); libtap-formatter-html-perl-0.11+dfsg.orig/t/99_test_pod.t 0000444 0001750 0001750 00000000201 12076610727 022276 0 ustar salvi salvi use Test::More; eval "use Test::Pod 1.00"; plan skip_all => "Test::Pod 1.00 required for testing POD" if $@; all_pod_files_ok(); libtap-formatter-html-perl-0.11+dfsg.orig/t/selenium/ 0000755 0001750 0001750 00000000000 12076610727 021601 5 ustar salvi salvi libtap-formatter-html-perl-0.11+dfsg.orig/t/selenium/server.pl 0000444 0001750 0001750 00000002676 12076610727 023455 0 ustar salvi salvi use strict; use warnings; use Alien::SeleniumRC; my $sel_rc_port = $ENV{SELENIUM_RC_PORT} || 4446; my $sel_rc_args = "-singleWindow -port $sel_rc_port"; my $sel_rc = Alien::SeleniumRC::Server->new( $sel_rc_args ); $sel_rc->start; while (1) { print "type 'q' to quit\n"; my $i = <>; last if ($i =~ /q/); } $sel_rc->stop; warn "hack alert: you'll likely need to kill the java process running selenium\n"; BEGIN{ package Alien::SeleniumRC::Server; use Carp qw( croak ); use Test::More; sub new { my ($class, $sel_arg) = @_; my $self = { sel_arg => $sel_arg }; return bless $self, $class; } sub start { my ($self) = @_; # fork off a SeleniumRC server if (0 == ($self->{pid} = fork())){ local $SIG{TERM} = sub { diag("SeleniumRC server $$ going down (TERM)"); exit 0; }; diag("Starting SeleniumRC in $$"); Alien::SeleniumRC::start($self->{sel_arg}) or croak "Can't start SeleniumRC server: $!"; diag("SeleniumRC server $$ going down"); exit 1; } return $self->{pid}; } sub stop { my ($self) = @_; if ($self->{pid} and kill(0, $self->{pid})) { diag("Stopping SeleniumRC " . $self->{pid}); kill( 'TERM', $self->{pid} ); sleep 1; if (kill(0, $self->{pid})) { diag("Killing SeleniumRC " . $self->{pid}); kill( 'KILL', $self->{pid} ); } } } sub DESTROY { my ($self) = @_; $self->stop if ($self->{pid}); } 1; } libtap-formatter-html-perl-0.11+dfsg.orig/t/data/ 0000755 0001750 0001750 00000000000 12076610727 020671 5 ustar salvi salvi libtap-formatter-html-perl-0.11+dfsg.orig/t/data/01_pass.pl 0000444 0001750 0001750 00000000674 12076610727 022501 0 ustar salvi salvi use strict; use warnings; use Test::More 'no_plan'; ok(1, 'im ok'); is(1, 1, 'one is one'); like('abc', qr/b/, 'contains b'); TODO: { local $TODO = 'just cant get these working?'; is(1, 2, 'one is two?'); like('abc', qr/d/, 'contains d?'); } SKIP: { skip 'to the loo', 2; ok(2, 'youre ok'); fail('dont run me'); } TODO: { todo_skip 'to the loo again', 2; ok(2, 'were ok'); fail('really dont run me'); } libtap-formatter-html-perl-0.11+dfsg.orig/t/data/11_lots_of_tests.pl 0000444 0001750 0001750 00000000220 12076610727 024406 0 ustar salvi salvi use strict; use warnings; use Test::More 'no_plan'; for my $i (1..500) { # chuck in some failures ok( $i % 11, "$i % 11 is true" ); } libtap-formatter-html-perl-0.11+dfsg.orig/t/data/03_plan_fail.pl 0000444 0001750 0001750 00000000202 12076610727 023445 0 ustar salvi salvi use strict; use warnings; use Test::More tests => 2; ok(1, 'im ok'); is(1, 1, 'one is one'); like('abc', qr/b/, 'contains b'); libtap-formatter-html-perl-0.11+dfsg.orig/t/data/07_todo_pass.pl 0000444 0001750 0001750 00000000534 12076610727 023527 0 ustar salvi salvi use strict; use warnings; use Test::More tests => 7; ok(1, 'im ok'); is(1, 1, 'one is one'); like('abc', qr/b/, 'contains b'); TODO: { local $TODO = 'just cant get these working?'; is(1, 1, 'one is one!'); like('abc', qr/c/, 'contains c?'); } TODO: { todo_skip 'to the loo', 2; ok(2, 'youre ok'); fail('dont run me'); } libtap-formatter-html-perl-0.11+dfsg.orig/t/data/06_skip_all.pl 0000444 0001750 0001750 00000000136 12076610727 023327 0 ustar salvi salvi use strict; use warnings; use Test::More skip_all => 'likity skipity'; fail('dont run me'); libtap-formatter-html-perl-0.11+dfsg.orig/t/data/05_compile_fail.pl 0000444 0001750 0001750 00000000366 12076610727 024160 0 ustar salvi salvi use strict; use warnings; use Test::More 'no_plan'; it'd all be ok, except I'm writting some silly comment here without actually remembering to put the # comment in... ok(1, 'im ok'); is(1, 1, 'one is one'); like('abc', qr/b/, 'contains b'); libtap-formatter-html-perl-0.11+dfsg.orig/t/data/08_html_in_output.pl 0000444 0001750 0001750 00000000404 12076610727 024603 0 ustar salvi salvi use strict; use warnings; use Test::More 'no_plan'; ok(1, 'escape some of these chars: !@#$%^++_)(*&^%$#@!><'); like('ac', qr//, 'contains in the '); is('ac', 'eeek', ' in the diag messages..'); libtap-formatter-html-perl-0.11+dfsg.orig/t/data/02_fail.pl 0000444 0001750 0001750 00000000376 12076610727 022446 0 ustar salvi salvi use strict; use warnings; use Test::More 'no_plan'; ok(1, 'im ok'); is(1, 1, 'one is one'); like('abc', qr/b/, 'contains b'); { is(1, 2, 'one is two?'); like('abc', qr/d/, 'contains d?'); } { ok(2, 'youre ok'); fail('dont run me'); } libtap-formatter-html-perl-0.11+dfsg.orig/t/data/09_skip_error.pl 0000444 0001750 0001750 00000000240 12076610727 023707 0 ustar salvi salvi use strict; use warnings; use Test::More 'no_plan'; # don't define a SKIP label { skip 'to the loo', 2; ok(2, 'youre ok'); fail('dont run me'); } libtap-formatter-html-perl-0.11+dfsg.orig/t/data/10_todo_skip.pl 0000444 0001750 0001750 00000000220 12076610727 023511 0 ustar salvi salvi use strict; use warnings; use Test::More tests => 2; TODO: { todo_skip 'to the loo', 2; ok(2, 'youre ok'); fail('dont run me'); } libtap-formatter-html-perl-0.11+dfsg.orig/t/data/04_die_fail.pl 0000444 0001750 0001750 00000000270 12076610727 023262 0 ustar salvi salvi use strict; use warnings; use Test::More tests => 3; ok(1, 'im ok'); is(1, 1, 'one is one'); like('abc', qr/b/, 'contains b'); die "this is an error test, not some horrible error"; libtap-formatter-html-perl-0.11+dfsg.orig/t/02_escape_output.t 0000444 0001750 0001750 00000001637 12076610727 023333 0 ustar salvi salvi use strict; use warnings; use lib 'lib'; use lib 't/lib'; use Test::More 'no_plan'; use FileTempTFH; use TAP::Harness; use_ok( 'TAP::Formatter::HTML' ); my $stdout_fh = FileTempTFH->new; my $stdout_orig_fh = IO::File->new_from_fd( fileno(STDOUT), 'w' ) or die "Error opening STDOUT for writing: $!"; STDOUT->fdopen( fileno($stdout_fh), 'w' ) or die "Error re-directing STDOUT: $!"; # Only run 1 test or tests will hang on Windows (RT #81922) my @tests = ( 't/data/01_pass.pl' ); my $f = TAP::Formatter::HTML->new({ escape_output => 1, force_inline_css => 0 }); my $h = TAP::Harness->new({ merge => 1, formatter => $f }); $h->runtests(@tests); STDOUT->fdopen( fileno($stdout_orig_fh), 'w' ) or die "Error resetting STDOUT: $!"; my $stdout = $stdout_fh->get_all_output || ''; isnt( $stdout, '', 'should be lots of output to stdout' ); ok( $stdout =~ /^# ok 1 - im ok/ms, 'found an escaped line of output' ); libtap-formatter-html-perl-0.11+dfsg.orig/t/07_multiple_aggregate.t 0000444 0001750 0001750 00000001357 12076610727 024320 0 ustar salvi salvi use strict; use warnings; use lib 'lib'; use lib 't/lib'; use Test::More 'no_plan'; use FileTempTFH; use TAP::Harness; use TAP::Parser::Aggregator; use_ok( 'TAP::Formatter::HTML' ); my $tmp = FileTempTFH->new; my $f = TAP::Formatter::HTML->new({ silent => 1 })->output_fh( $tmp )->force_inline_css(0); my $h = TAP::Harness->new({ merge => 1, formatter => $f }); my $a = TAP::Parser::Aggregator->new; $a->start; $h->aggregate_tests( $a, 't/data/01_pass.pl' ); $h->aggregate_tests( $a, 't/data/02_fail.pl' ); $a->stop; $f->summary( $a ); my $html = $tmp->get_all_output; ok( $html =~ qr|01_pass|, 'html contains file 1' ); ok( $html =~ qr|02_fail|, 'html contains file 2' ); like( $html, qr|2 files|, '2 files processed' ); libtap-formatter-html-perl-0.11+dfsg.orig/t/04_really_quiet.t 0000444 0001750 0001750 00000001553 12076610727 023151 0 ustar salvi salvi use strict; use warnings; use lib 'lib'; use lib 't/lib'; use Test::More 'no_plan'; use FileTempTFH; use TAP::Harness; use_ok( 'TAP::Formatter::HTML' ); my $stdout_fh = FileTempTFH->new; my $stdout_orig_fh = IO::File->new_from_fd( fileno(STDOUT), 'w' ) or die "Error opening STDOUT for writing: $!"; STDOUT->fdopen( fileno($stdout_fh), 'w' ) or die "Error re-directing STDOUT: $!"; my @tests = ( 't/data/01_pass.pl', 't/data/02_fail.pl' ); my $f = TAP::Formatter::HTML->new({ escape_output => 1, really_quiet => 1, force_inline_css => 0 }); my $h = TAP::Harness->new({ merge => 1, formatter => $f }); $h->runtests(@tests); STDOUT->fdopen( fileno($stdout_orig_fh), 'w' ) or die "Error resetting STDOUT: $!"; my $stdout = $stdout_fh->get_all_output || ''; like( $stdout, qr|\A\s*<.+/html>\s*\Z|ms, 'should be no output to stdout other than HTML report' ); libtap-formatter-html-perl-0.11+dfsg.orig/t/selenium_basic.t 0000444 0001750 0001750 00000020745 12076610727 023135 0 ustar salvi salvi use lib 't/lib'; use strict; use warnings; use Test::More; BEGIN { eval "use Test::WWW::Selenium;"; if (my $e = $@) { plan skip_all => "Test::WWW::Selenium is required"; } } use URI::file; use Time::HiRes qw( sleep ); use FileTempTFH; use TAP::Harness; use TAP::Formatter::HTML; my $sel_rc_port = $ENV{SELENIUM_RC_PORT} || 4446; my $sel_speed = $ENV{SELENIUM_SPEED}; #my $sel_rc_args = "-singleWindow -port $sel_rc_port"; #my $sel_rc = Alien::SeleniumRC::Server->new( $sel_rc_args ); #$sel_rc->start; my $tmp = FileTempTFH->new; my $output_uri = URI::file->new( $tmp->filename ); my $sel; eval { $sel = Test::WWW::Selenium ->new( host => "localhost", port => $sel_rc_port, browser => "*firefox", browser_url => $output_uri, default_names => 1 ); }; if (my $e = $@) { plan skip_all => "Couldn't connect to SeleniumRC on $sel_rc_port: " . "try 'sudo perl t/selenium/server.pl'"; } plan 'no_plan'; # generate some test output: my $f = TAP::Formatter::HTML->new({ silent => 1, output_fh => $tmp, force_inline_css => 0 }); my $h = TAP::Harness->new({ merge => 1, formatter => $f }); my @tests = glob( 't/data/*.pl' ); $h->runtests(@tests); my $crappy_wait_time = 0.450; $sel->open_ok($output_uri); $sel->wait_for_page_to_load_ok(5000); my $loc = $sel->get_location; $sel->set_speed( $sel_speed ) if $sel_speed; # test summary { $sel->text_is("//div[\@id='summary']/a", 'FAILED', 'summary text' ); $sel->hi_click_ok("//div[\@id='summary']/a", 'click summary link' ); $sel->location_is($loc, "click summary link shouldn't change location"); } # test show/hide failed: { # TODO: check for visible sections: $sel->is_visible_ok("//tr[\@id='t-data-01-pass-pl']", '01_pass visible'); $sel->is_visible_ok("//tr[\@id='t-data-02-fail-pl']", '02_fail visible'); $sel->hi_click_ok("link=show failed"); sleep( $crappy_wait_time ); ok( !$sel->is_visible("//tr[\@id='t-data-01-pass-pl']"), '01_pass now hidden' ); $sel->is_visible_ok("//tr[\@id='t-data-02-fail-pl']", '02_fail still visible'); $sel->hi_click_ok("link=show all"); sleep( $crappy_wait_time ); $sel->is_visible_ok("//tr[\@id='t-data-01-pass-pl']", '01_pass visible again'); $sel->is_visible_ok("//tr[\@id='t-data-02-fail-pl']", '02_fail still visible'); } $sel->open_ok($output_uri); $sel->wait_for_page_to_load_ok(5000); # test for 01_pass { $sel->attribute_is("//li[\@id='t0']\@class", "tst k"); $sel->attribute_is("//a[contains(\@href, '#t0')]/..\@class", "k p"); $sel->attribute_is("//li[\@id='t3']\@class", "tst t"); $sel->attribute_is("//a[contains(\@href, '#t3')]/..\@class", "t p"); $sel->attribute_is("//li[\@id='t5']\@class", "tst s"); $sel->attribute_is("//a[contains(\@href, '#t5')]/..\@class", "s p"); $sel->is_element_present_ok("//tr[\@id='t-data-01-pass-pl']/td/div/ul/li[\@class='cmt']"); $sel->is_element_present_ok("//tr[\@id='t-data-01-pass-pl']/td/div/ul/li[\@class='pln']"); # show test detail ok( !$sel->is_visible("//tr[\@id='t-data-01-pass-pl']/td/div[\@class='test-detail']"), '01_pass detail hidden' ); $sel->hi_click_ok("link=t/data/01_pass.pl"); sleep( $crappy_wait_time ); $sel->is_visible_ok ("//tr[\@id='t-data-01-pass-pl']/td/div[\@class='test-detail']", '01_pass detail now visible' ); # hide test detail $sel->hi_click_ok("link=t/data/01_pass.pl"); sleep( $crappy_wait_time ); ok( !$sel->is_visible("//tr[\@id='t-data-01-pass-pl']/td/div[\@class='test-detail']"), '01_pass detail hidden again' ); } # test 02_fail { $sel->attribute_is("//li[\@id='t12']\@class", "tst n"); $sel->attribute_is("//a[contains(\@href, '#t12')]/..\@class", "n f"); $sel->is_element_present_ok("//tr[\@id='t-data-02-fail-pl']/td/div/ul/li[\@class='cmt']"); $sel->is_element_present_ok("//tr[\@id='t-data-02-fail-pl']/td/div/ul/li[\@class='pln']"); ok(!$sel->is_element_present("//tr[\@id='t-data-02-fail-pl']/td/div/ul[\@class='parse-errors']/li"), 'no parse errors'); ok(!$sel->is_visible("//tr[\@id='t-data-02-fail-pl']/td/div[\@class='test-detail']"), '02_fail detail hidden'); # show & highlight a particular test $sel->hi_click_ok("//a[contains(\@href, '#t12')]"); sleep( $crappy_wait_time ); # $sel->attribute_is("//li[\@id='t12']\@style", "background-color: yellow;"); $sel->is_visible_ok("//tr[\@id='t-data-02-fail-pl']/td[2]/div[\@class='test-detail']"); # hide again $sel->hi_click_ok("link=t/data/02_fail.pl"); sleep( $crappy_wait_time ); ok(! $sel->is_visible("//tr[\@id='t-data-02-fail-pl']/td/div[\@class='test-detail']"), '02_fail detail hidden again'); } # test 03_plan_fail { $sel->attribute_is("//li[\@id='t18']\@class", "tst k unp"); $sel->attribute_is("//a[contains(\@href, '#t18')]/..\@class", "k f"); $sel->is_element_present_ok("//tr[\@id='t-data-03-plan-fail-pl']/td/div/ul/li[\@class='cmt']"); $sel->is_element_present_ok("//tr[\@id='t-data-03-plan-fail-pl']/td/div/ul/li[\@class='pln']"); $sel->is_element_present_ok("//tr[\@id='t-data-03-plan-fail-pl']/td/div/ul[\@class='parse-errors']/li"); $sel->hi_click_ok("link=t/data/03_plan_fail.pl"); sleep( $crappy_wait_time ); $sel->is_visible_ok("//tr[\@id='t-data-03-plan-fail-pl']/td[2]/div[\@class='test-detail']"); } # test 04_die_fail { $sel->is_element_present_ok("//tr[\@id='t-data-04-die-fail-pl']/td/div/ul/li[\@class='cmt']"); $sel->is_element_present_ok("//tr[\@id='t-data-04-die-fail-pl']/td/div/ul/li[\@class='pln']"); $sel->is_element_present_ok("//tr[\@id='t-data-04-die-fail-pl']/td/div/ul/li[\@class='unk']"); $sel->hi_click_ok("link=t/data/04_die_fail.pl"); sleep( $crappy_wait_time ); } # test 05_compile_fail { $sel->is_element_present_ok("//tr[\@id='t-data-05-compile-fail-pl']/td/div/ul/li[\@class='cmt']"); ok(! $sel->is_element_present("//tr[\@id='t-data-05-compile-fail-pl']/td/div/ul/li[\@class='pln']"), 'no test plan' ); $sel->is_element_present_ok("//tr[\@id='t-data-05-compile-fail-pl']/td/div/ul[\@class='parse-errors']/li"); # show & highlight a particular test ok(! $sel->is_visible("//tr[\@id='t-data-05-compile-fail-pl']/td[2]/div[\@class='test-detail']"), '05_compile_fail detail hidden' ); $sel->highlight_ok("//a[contains(\@title, 'No tests run!')]"); $sel->hi_click_ok("//a[contains(\@title, 'No tests run!')]"); sleep( $crappy_wait_time ); $sel->location_is($loc); $sel->is_visible_ok("//tr[\@id='t-data-05-compile-fail-pl']/td[2]/div[\@class='test-detail']"); # hide again $sel->hi_click_ok("link=t/data/05_compile_fail.pl"); sleep( $crappy_wait_time ); ok(! $sel->is_visible("//tr[\@id='t-data-05-compile-fail-pl']/td[2]/div[\@class='test-detail']"), '05_compile_fail detail hidden again' ); } # test 06_skip_all { $sel->is_element_present_ok("//tr[\@id='t-data-06-skip-all-pl']/td/div/ul/li[\@class='pln']"); # show & highlight a particular test ok(! $sel->is_visible("//tr[\@id='t-data-06-skip-all-pl']/td[2]/div[\@class='test-detail']"), '06_skip_all detail hidden' ); $sel->hi_click_ok("//tr[\@id='t-data-06-skip-all-pl']/td[\@class='results']//a"); sleep( $crappy_wait_time ); $sel->location_is($loc); $sel->is_visible_ok("//tr[\@id='t-data-06-skip-all-pl']/td[2]/div[\@class='test-detail']"); # hide again $sel->hi_click_ok("link=t/data/06_skip_all.pl"); sleep( $crappy_wait_time ); ok(! $sel->is_visible("//tr[\@id='t-data-06-skip-all-pl']/td[2]/div[\@class='test-detail']"), '06_skip_all detail hidden again' ); } # test 07_todo_pass { $sel->attribute_is("//li[\@id='t25']\@class", "tst u"); ok(! $sel->is_visible("//tr[\@id='t-data-07-todo-pass-pl']/td[2]/div[\@class='test-detail']"), '07_todo_pass detail hidden' ); $sel->hi_click_ok("//a[contains(\@href, '#t25')]"); sleep( $crappy_wait_time ); $sel->is_visible_ok("//tr[\@id='t-data-07-todo-pass-pl']/td[2]/div[\@class='test-detail']"); } # test 08_html_in_output { $sel->hi_click_ok("//a[contains(\@href, '#t31')]"); local $TODO = 'selenium returns this back to us w/HTML entities decoded'; $sel->text_like("//li[\@id='t31']", qr/<html>/, 'embedded html' ); } # test 09_skip_error { $sel->hi_click_ok("link=t/data/09_skip_error.pl"); } # test 11_lots_of_tests { $sel->hi_click_ok("//a[contains(\@href, '#t519')]"); } # Laziness sub Test::WWW::Selenium::hi_click_ok { my ($self, $id, $msg) = @_; $self->highlight_ok($id, $msg); $self->click_ok($id, $msg); } libtap-formatter-html-perl-0.11+dfsg.orig/t/03_silent.t 0000444 0001750 0001750 00000003000 12076610727 021734 0 ustar salvi salvi use strict; use warnings; use lib 'lib'; use lib 't/lib'; use Test::More 'no_plan'; use FileTempTFH; use TAP::Harness; use_ok( 'TAP::Formatter::HTML' ); my $output_fh = FileTempTFH->new; my $stdout_fh = FileTempTFH->new; my $stdout_orig_fh = IO::File->new_from_fd( fileno(STDOUT), 'w' ) or die "Error opening STDOUT for writing: $!"; STDOUT->fdopen( fileno($stdout_fh), 'w' ) or die "Error re-directing STDOUT: $!"; # Note: strangely this doesn't fail on Windows, even though we're # running 2 tests... worth pointing out it may be due to lack of # output... my @tests = ( 't/data/01_pass.pl', 't/data/02_fail.pl' ); { my $f = TAP::Formatter::HTML->new({ escape_output => 1, silent => 1 }); my $h = TAP::Harness->new({ merge => 1, formatter => $f }); $h->runtests(@tests); my $stdout = $stdout_fh->get_all_output || ''; is( $stdout, '', 'should be no output to stdout' ); my $html_ref = $f->html; ok( $$html_ref, 'formatter->html exists' ); } { my $f = TAP::Formatter::HTML->new({ silent => 1, output_fh => $output_fh }); my $h = TAP::Harness->new({ merge => 1, formatter => $f }); $h->runtests(@tests); my $stdout = $stdout_fh->get_all_output || ''; is( $stdout, '', 'should be no output to stdout' ); my $html = $output_fh->get_all_output || ''; ok( $html, 'html still output to non-stdout fh when silent is set' ); like( $html, qr|01_pass|, 'html contains file 1' ); my $html_ref = $f->html; ok( $$html_ref, 'formatter->html exists' ); } libtap-formatter-html-perl-0.11+dfsg.orig/t/06_output.t 0000444 0001750 0001750 00000000553 12076610727 022013 0 ustar salvi salvi use strict; use warnings; use lib 'lib'; use lib 't/lib'; use Test::More 'no_plan'; use FileTempTFH; use TAP::Harness; use_ok( 'TAP::Formatter::HTML' ); my @tests = glob( 't/data/*.pl' ); my $f = TAP::Formatter::HTML->new({ silent => 1 }); my $h = TAP::Harness->new({ merge => 1, formatter => $f }); $h->runtests(@tests); # TODO: test output to a custom FH libtap-formatter-html-perl-0.11+dfsg.orig/t/20_prove_html.t 0000444 0001750 0001750 00000003275 12076610727 022632 0 ustar salvi salvi use strict; use warnings; use lib 'lib'; use lib 't/lib'; use Test::More 'no_plan'; use FileTempTFH; use App::Prove; my $tmp = FileTempTFH->new; # prove -P HTML=output:output.html,css_uri:http://www.spurkis.org/style.css,js_uri:jquery.js,js_uri:custom.js,force_inline_css:0,force_inline_js:0 my @args = ( '--norc', '-Q', '-P', join( ',', 'HTML=outfile:' . $tmp->filename, qw( css_uri:http://www.spurkis.org/style.css css_uri:custom.css js_uri:http://www.spurkis.org/jquery.js js_uri:custom.js force_inline_css:0 force_inline_js:0 ), ), 't/data/01_pass.pl', ); my $run; my $app = App::Prove->new; eval { $app->process_args( @args ); $run = $app->run; }; my $e = $@ || ''; ok( $run, 'app->run' ); is( $e, '', '... and no error' ); can_ok( 'App::Prove::Plugin::HTML', 'load' ); can_ok( 'TAP::Formatter::HTML', 'new' ); is( $app->formatter, 'TAP::Formatter::HTML', 'app->formatter' ); is( $ENV{TAP_FORMATTER_HTML_OUTFILE}, $tmp->filename, 'ENV: outfile' ); is( $ENV{TAP_FORMATTER_HTML_CSS_URIS}, 'http://www.spurkis.org/style.css:custom.css', 'ENV: css_uris' ); is( $ENV{TAP_FORMATTER_HTML_JS_URIS}, 'http://www.spurkis.org/jquery.js:custom.js', 'ENV: js_uris' ); is( $ENV{TAP_FORMATTER_HTML_FORCE_INLINE_CSS}, 0, 'ENV: force_inline_css' ); is( $ENV{TAP_FORMATTER_HTML_FORCE_INLINE_JS}, 0, 'ENV: force_inline_js' ); my $out = $tmp->get_all_output || ''; like( $out, qr|\A\s*<.+/html>\s*\Z|ms, 'HTML report output to file' ); like( $out, qr|style.css|, 'css_uri: style.css' ); like( $out, qr|custom.css|, 'css_uri: custom.css' ); like( $out, qr|jquery.js|, 'js_uri: jquery.css' ); like( $out, qr|custom.js|, 'js_uri: custom.css' ); libtap-formatter-html-perl-0.11+dfsg.orig/t/01_basic.t 0000444 0001750 0001750 00000001775 12076610727 021536 0 ustar salvi salvi use strict; use warnings; use lib 'lib'; use lib 't/lib'; use Test::More 'no_plan'; use FileTempTFH; use File::Basename qw( basename ); use TAP::Harness; use_ok( 'TAP::Formatter::HTML' ); my $stdout_fh = FileTempTFH->new; my $stdout_orig_fh = IO::File->new_from_fd( fileno(STDOUT), 'w' ) or die "Error opening STDOUT for writing: $!"; STDOUT->fdopen( fileno($stdout_fh), 'w' ) or die "Error redirecting STDOUT: $!"; # Only run 1 test on Windows or tests will hang (RT #81922) my @tests = ($^O =~ /win/i ? 't/data/01_pass.pl' : glob( 't/data/*.pl' )); my $h = TAP::Harness->new({ merge => 1, formatter_class => 'TAP::Formatter::HTML' }); $h->runtests(@tests); STDOUT->fdopen( fileno($stdout_orig_fh), 'w' ) or die "Error resetting STDOUT: $!"; my $stdout = $stdout_fh->get_all_output || ''; isnt( $stdout, '', 'captured test output to stdout' ); foreach my $file (@tests) { my $test = basename( $file ); $test =~ s/\.pl$//; ok( $stdout =~ qr|$test|, "output contains test '$test'" ); } libtap-formatter-html-perl-0.11+dfsg.orig/t/10_win32.t 0000444 0001750 0001750 00000002116 12076610727 021405 0 ustar salvi salvi use strict; use warnings; use lib 'lib'; use lib 't/lib'; use Test::More 'no_plan'; use FileTempTFH; use URI::file; use TAP::Harness; use_ok( 'TAP::Formatter::HTML' ); if ($^O !~ /win32/i) { no warnings; $TAP::Formatter::HTML::FAKE_WIN32_URIS = 1; } my $tmp = FileTempTFH->new; my $f = TAP::Formatter::HTML ->new({ silent => 1, css_uris => ['C:\\some\\path', 'file:///C:\\another\\path'], js_uris => ['\\yet\\another\\path', 'file://and/another'], force_inline_css => 0 }) ->output_fh( $tmp ); my $h = TAP::Harness->new({ merge => 1, formatter => $f }); my @tests = ( 't/data/01_pass.pl' ); eval { $h->runtests( @tests ) }; my $e = $@ || ''; is( $e, '', 'no error on generate report with win32 CSS URI' ); is_deeply( $f->css_uris, [ URI::file->new( 'C:\\some\\path', 'win32' ), URI::file->new( 'C:\\another\\path', 'win32' ) ], 'win32 css_uris as expected (RT 37983)' ); is_deeply( $f->js_uris, [ URI::file->new( '\\yet\\another\\path', 'win32' ), URI::file->new( 'file://and/another', 'win32' ) ], 'win32 js_uris as expected (RT 37983)' ); libtap-formatter-html-perl-0.11+dfsg.orig/t/08_env_vars.t 0000444 0001750 0001750 00000002620 12076610727 022275 0 ustar salvi salvi use strict; use warnings; use lib 'lib'; use lib 't/lib'; use Test::More 'no_plan'; use FileTempTFH; use TAP::Harness; use_ok( 'TAP::Formatter::HTML' ); my $tmp = FileTempTFH->new; $ENV{TAP_FORMATTER_HTML_OUTFILE} = "$tmp"; $ENV{TAP_FORMATTER_HTML_FORCE_INLINE_CSS} = "0"; $ENV{TAP_FORMATTER_HTML_CSS_URIS} = "/foo/bar.css:/bar/baz.css"; $ENV{TAP_FORMATTER_HTML_JS_URIS} = "/foo/bar.js:/bar/baz.js"; $ENV{TAP_FORMATTER_HTML_TEMPLATE} = "/foo/bar/baz.tt"; my $f = TAP::Formatter::HTML->new({ really_quiet => 1 }); isnt( $f->output_fh, $f->stdout, 'TAP_FORMATTER_HTML_OUTFILE sets output_fh' ); is( $f->force_inline_css, 0, 'TAP_FORMATTER_HTML_FORCE_INLINE_CSS' ); is( $f->template, '/foo/bar/baz.tt', 'TAP_FORMATTER_HTML_TEMPLATE' ); is_deeply( $f->css_uris, [qw( /foo/bar.css /bar/baz.css )], 'TAP_FORMATTER_HTML_CSS_URIS' ); is_deeply( $f->js_uris, [qw( /foo/bar.js /bar/baz.js )], 'TAP_FORMATTER_HTML_JS_URIS' ); # Test #2 - make sure OUTFILE works... delete @ENV{qw(TAP_FORMATTER_HTML_CSS_URIS TAP_FORMATTER_HTML_JS_URIS TAP_FORMATTER_HTML_TEMPLATE)}; my $h = TAP::Harness->new({ merge => 1, verbosity => -3, formatter_class => 'TAP::Formatter::HTML' }); $h->runtests( 't/data/01_pass.pl' ); my $html = $tmp->get_all_output; ok( $html, 'TAP_FORMATTER_HTML_OUTFILE generates file' ); like( $html, qr|01_pass|, 'file contains expected output' ); libtap-formatter-html-perl-0.11+dfsg.orig/t/lib/ 0000755 0001750 0001750 00000000000 12076610727 020526 5 ustar salvi salvi libtap-formatter-html-perl-0.11+dfsg.orig/t/lib/FileTempTFH.pm 0000444 0001750 0001750 00000000412 12076610727 023126 0 ustar salvi salvi package FileTempTFH; use strict; use warnings; use Fcntl qw( SEEK_SET ); use base qw( File::Temp ); sub get_all_output { my $self = shift; $self->seek( 0, SEEK_SET ); my $html; { local $/ = undef; $html = <$self>; } return $html; } 1; libtap-formatter-html-perl-0.11+dfsg.orig/t/31_inc_path.t 0000444 0001750 0001750 00000001177 12076610727 022241 0 ustar salvi salvi use strict; use warnings; use lib 'lib'; use lib 't/lib'; use Test::More 'no_plan'; use TAP::Harness; use TAP::Formatter::HTML; eval { for (1..64) { push @INC, "lib" } #my $t = TAP::Formatter::HTML->default_template_processor; my @tests = 't/data/01_pass.pl'; my $f = TAP::Formatter::HTML->new({ silent => 1 }); my $h = TAP::Harness->new({ merge => 1, formatter => $f }); $h->runtests(@tests); }; my $e = $@; unlike($e, qr/INCLUDE_PATH exceeds .+ directories/i, 'RT 74364 - INCLUDE_PATH exceeds num dirs'); ok(!$e, 'no error set when calling default_template_processor with lots of dirs in @INC') || diag($e); libtap-formatter-html-perl-0.11+dfsg.orig/t/11_force_inline.t 0000444 0001750 0001750 00000001564 12076610727 023106 0 ustar salvi salvi use strict; use warnings; use lib 'lib'; use lib 't/lib'; use Test::More 'no_plan'; use FileTempTFH; use TAP::Harness; use TAP::Parser::Aggregator; use_ok( 'TAP::Formatter::HTML' ); my @tests = ( 't/data/01_pass.pl' ); my $tmp = FileTempTFH->new; my $f = TAP::Formatter::HTML->new({ silent => 1 })->output_fh( $tmp )->force_inline_css(1)->force_inline_js(1); my $h = TAP::Harness->new({ merge => 1, formatter => $f }); $h->runtests(@tests); my $html = $tmp->get_all_output; ok( $html =~ qr|01_pass|, 'html contains file 1' ); ok( $html =~ qr|jQuery JavaScript Library v\d+.\d+.\d+|, 'html contains jQuery src' ); ok( $html =~ qr|default javascript for report|, 'html contains default js' ); ok( $html =~ qr|default stylesheet for report body|, 'html contains default css body' ); ok( $html =~ qr|default stylesheet for report page layout|, 'html contains default css page' ); libtap-formatter-html-perl-0.11+dfsg.orig/t/09_empty_testdir.t 0000444 0001750 0001750 00000000665 12076610727 023356 0 ustar salvi salvi use strict; use warnings; use lib 'lib'; use lib 't/lib'; use Test::More 'no_plan'; use FileTempTFH; use TAP::Harness; use_ok( 'TAP::Formatter::HTML' ); my $tmp = FileTempTFH->new; my $f = TAP::Formatter::HTML->new({ silent => 1 })->output_fh( $tmp )->force_inline_css(0); my $h = TAP::Harness->new({ merge => 1, formatter => $f }); eval { $h->runtests( ) }; my $e = $@ || ''; is( $e, '', 'no error on empty test dir (RT 41411)' ); libtap-formatter-html-perl-0.11+dfsg.orig/t/05_quiet.t 0000444 0001750 0001750 00000001640 12076610727 021577 0 ustar salvi salvi use strict; use warnings; use lib 'lib'; use lib 't/lib'; use Test::More 'no_plan'; use FileTempTFH; use TAP::Harness; use_ok( 'TAP::Formatter::HTML' ); my $stdout_fh = FileTempTFH->new; my $stdout_orig_fh = IO::File->new_from_fd( fileno(STDOUT), 'w' ) or die "Error opening STDOUT for writing: $!"; STDOUT->fdopen( fileno($stdout_fh), 'w' ) or die "Error re-directing STDOUT: $!"; my @tests = ( 't/data/01_pass.pl', 't/data/02_fail.pl' ); my $f = TAP::Formatter::HTML->new({ escape_output => 1, quiet => 1, force_inline_css => 0 }); my $h = TAP::Harness->new({ merge => 1, formatter => $f }); $h->runtests(@tests); STDOUT->fdopen( fileno($stdout_orig_fh), 'w' ) or die "Error resetting STDOUT: $!"; my $stdout = $stdout_fh->get_all_output || ''; like( $stdout, qr|\A(?!\s*\Z|ms, 'HTML report should be output last' ); libtap-formatter-html-perl-0.11+dfsg.orig/MANIFEST 0000444 0001750 0001750 00000001731 12076716223 020644 0 ustar salvi salvi Build.PL Changes examples/DBD-SQLite-example.html examples/test-output.html lib/App/Prove/Plugin/HTML.pm lib/TAP/Formatter/HTML.pm lib/TAP/Formatter/HTML/default_page.css lib/TAP/Formatter/HTML/default_report.css lib/TAP/Formatter/HTML/default_report.js lib/TAP/Formatter/HTML/default_report.tt2 lib/TAP/Formatter/HTML/Session.pm Makefile.PL MANIFEST This list of files META.json META.yml README t/01_basic.t t/02_escape_output.t t/03_silent.t t/04_really_quiet.t t/05_quiet.t t/06_output.t t/07_multiple_aggregate.t t/08_env_vars.t t/09_empty_testdir.t t/10_win32.t t/11_force_inline.t t/20_prove_html.t t/30_tap_harness_compat.t t/31_inc_path.t t/99_test_pod.t t/data/01_pass.pl t/data/02_fail.pl t/data/03_plan_fail.pl t/data/04_die_fail.pl t/data/05_compile_fail.pl t/data/06_skip_all.pl t/data/07_todo_pass.pl t/data/08_html_in_output.pl t/data/09_skip_error.pl t/data/10_todo_skip.pl t/data/11_lots_of_tests.pl t/lib/FileTempTFH.pm t/selenium/server.pl t/selenium_basic.t Todo libtap-formatter-html-perl-0.11+dfsg.orig/Makefile.PL 0000444 0001750 0001750 00000002305 12076610727 021465 0 ustar salvi salvi # Note: this file was auto-generated by Module::Build::Compat version 0.3800 require 5.006000; unless (eval "use Module::Build::Compat 0.02; 1" ) { print "This module requires Module::Build to install itself.\n"; require ExtUtils::MakeMaker; my $yn = ExtUtils::MakeMaker::prompt (' Install Module::Build now from CPAN?', 'y'); unless ($yn =~ /^y/i) { die " *** Cannot install without Module::Build. Exiting ...\n"; } require Cwd; require File::Spec; require CPAN; # Save this 'cause CPAN will chdir all over the place. my $cwd = Cwd::cwd(); CPAN::Shell->install('Module::Build::Compat'); CPAN::Shell->expand("Module", "Module::Build::Compat")->uptodate or die "Couldn't install Module::Build, giving up.\n"; chdir $cwd or die "Cannot chdir() back to $cwd: $!"; } eval "use Module::Build::Compat 0.02; 1" or die $@; Module::Build::Compat->run_build_pl(args => \@ARGV); my $build_script = 'Build'; $build_script .= '.com' if $^O eq 'VMS'; exit(0) unless(-e $build_script); # cpantesters convention require Module::Build; Module::Build::Compat->write_makefile(build_class => 'Module::Build'); libtap-formatter-html-perl-0.11+dfsg.orig/Todo 0000444 0001750 0001750 00000002104 12076610727 020340 0 ustar salvi salvi More tests! * parse the generated output * check the view given to the TT view Reports from Nadim Khemir: * integrate into build scripts (eg: M::B) * possible to open the test file and show the test itself? * why the need to stop using Test::Harness? Various bug reports (+ Patch!) by Hemingway: * https://rt.cpan.org/Ticket/Display.html?id=41457 Progress console: * https://rt.cpan.org/Ticket/Display.html?id=40627 Script still running bug * when you navigate away from page * only on big test outputs * jquery cleanup? Sometimes test details don't collapse properly! * doesn't happen in FireFox 3? * related to the scrollTo? * could hide & show the tr after the slideToggle? sort by cols * time! * test file * percent support file output - in progress * prove --html=output.html * allow output to STDOUT * write an App::Prove plugin wrap unknown test output's in expand all tests expand failed tests collapse current test (from halway down big tests, maybe a mouseover file or something) Give the test suite a title... libtap-formatter-html-perl-0.11+dfsg.orig/lib/ 0000755 0001750 0001750 00000000000 12076610727 020263 5 ustar salvi salvi libtap-formatter-html-perl-0.11+dfsg.orig/lib/App/ 0000755 0001750 0001750 00000000000 12076610727 021003 5 ustar salvi salvi libtap-formatter-html-perl-0.11+dfsg.orig/lib/App/Prove/ 0000755 0001750 0001750 00000000000 12076610727 022076 5 ustar salvi salvi libtap-formatter-html-perl-0.11+dfsg.orig/lib/App/Prove/Plugin/ 0000755 0001750 0001750 00000000000 12076610727 023334 5 ustar salvi salvi libtap-formatter-html-perl-0.11+dfsg.orig/lib/App/Prove/Plugin/HTML.pm 0000444 0001750 0001750 00000006124 12076610727 024437 0 ustar salvi salvi package App::Prove::Plugin::HTML; =head1 NAME App::Prove::Plugin::HTML - a prove plugin for HTML output =head1 SYNOPSIS # command-line usage: prove -P HTML=outfile:out.html,css_uri:style.css,js_uri:foo.js,force_inline_css:0 # NOTE: this is currently in alpha, this usage will likely change! =cut use strict; use warnings; use TAP::Formatter::HTML; our $VERSION = '0.11'; sub import { my ($class, @args) = @_; # deprecated, do nothing return $class; } sub load { my ($class, $p) = @_; my @args = @{ $p->{args} }; my $app = $p->{app_prove}; # parse the args my %TFH_args; foreach my $arg (@args) { my ($key, $val) = split(/:/, $arg, 2); if (grep {$key eq $_} qw(css_uri js_uri)) { push @{ $TFH_args{$key . 's'}}, $val; } else { $TFH_args{$key} = $val; } } # set the formatter to use $app->formatter( 'TAP::Formatter::HTML' ); # set ENV vars in order to pass args to TAP::Formatter::HTML # horrible, but it's currently the only way :-/ while (my ($key, $val) = each %TFH_args) { $val = join( ':', @$val ) if (ref($val) eq 'ARRAY'); $ENV{"TAP_FORMATTER_HTML_".uc($key)} = $val; } # we're done return $class; } 1; __END__ =head1 DESCRIPTION This is a quick & dirty second attempt at making L easier to use from the command line. It will change once L has better support for plugins than need to take cmdline data. The original goal was to be able to specify all the args on the cmdline, ala: prove --html=output.html --css-uri foo.css --css-uri bar.css --force-inline-css 0 But this is currently not possible with the way the L plugin system works. As a compromise, you must use the following syntax: prove -P HTML=arg1:val1,arg2:val2,... Where I is any L parameter that is configurable via C<%ENV>. =head2 Example prove -P HTML=outfile:out.html,css_uri:style.css,js_uri:foo.js,force_inline_css:0 This will cause L to load this plugin, which loads L for you, and sets I to C to save you some typing. To configure L, the following C<%ENV> vars are set: TAP_FORMATTER_HTML_OUTFILE=out.html TAP_FORMATTER_HTML_FORCE_INLINE_CSS=0 TAP_FORMATTER_HTML_CSS_URIS=style.css TAP_FORMATTER_HTML_JS_URIS=func.js Yes, you can pass 2 or more I or I args. =head2 %ENV vars?! Briefly, L currently only lets you specify the C for L, it doesn't let you instantiate a formatter, or pass config to the formatter. I If it bugs you too, then join the L devs and help us fix it ;-). =head1 BUGS Please use http://rt.cpan.org to report any issues. =head1 AUTHOR Steve Purkis =head1 COPYRIGHT Copyright (c) 2008-2010 Steve Purkis , S Purkis Consulting Ltd. All rights reserved. This module is released under the same terms as Perl itself. =head1 SEE ALSO L, L, L =cut libtap-formatter-html-perl-0.11+dfsg.orig/lib/TAP/ 0000755 0001750 0001750 00000000000 12076610727 020707 5 ustar salvi salvi libtap-formatter-html-perl-0.11+dfsg.orig/lib/TAP/Formatter/ 0000755 0001750 0001750 00000000000 12076610727 022652 5 ustar salvi salvi libtap-formatter-html-perl-0.11+dfsg.orig/lib/TAP/Formatter/HTML.pm 0000444 0001750 0001750 00000060041 12076610727 023753 0 ustar salvi salvi =head1 NAME TAP::Formatter::HTML - TAP Test Harness output delegate for html output =head1 SYNOPSIS ## ## command-line usage (alpha): ## prove -m -Q -P HTML=outfile:out.html,css_uri:style.css,js_uri:foo.js,force_inline_css:0 # backwards compat usage: prove -m -Q --formatter=TAP::Formatter::HTML >output.html # for more detail: perldoc App::Prove::Plugin::HTML ## ## perl usage: ## use TAP::Harness; my @tests = glob( 't/*.t' ); my $harness = TAP::Harness->new({ formatter_class => 'TAP::Formatter::HTML', merge => 1 }); $harness->runtests( @tests ); # prints HTML to STDOUT by default # or if you really don't want STDERR merged in: my $harness = TAP::Harness->new({ formatter_class => 'TAP::Formatter::HTML' }); # to use a custom formatter: my $fmt = TAP::Formatter::HTML->new; $fmt->css_uris([])->inline_css( $my_css ) ->js_uris(['http://mysite.com/jquery.js', 'http://mysite.com/custom.js']) ->inline_js( '$(div.summary).hide()' ); my $harness = TAP::Harness->new({ formatter => $fmt, merge => 1 }); # to output HTML to a file[handle]: $fmt->output_fh( $fh ); $fmt->output_file( '/tmp/foo.html' ); # you can use your own customized templates too: $fmt->template('custom.tt2') ->template_processor( Template->new ) ->force_inline_css(0) ->force_inline_js(0); =cut package TAP::Formatter::HTML; use strict; use warnings; use URI; use URI::file; use Template; use POSIX qw( ceil ); use IO::File; use File::Temp qw( tempfile tempdir ); use File::Spec::Functions qw( catdir catfile file_name_is_absolute rel2abs ); use TAP::Formatter::HTML::Session; # DEBUG: #use Data::Dumper 'Dumper'; use base qw( TAP::Base ); use accessors qw( verbosity stdout output_fh escape_output tests session_class sessions template_processor template html html_id_iterator minify color css_uris js_uris inline_css inline_js abs_file_paths force_inline_css force_inline_js ); use constant default_session_class => 'TAP::Formatter::HTML::Session'; use constant default_template => 'TAP/Formatter/HTML/default_report.tt2'; use constant default_js_uris => ['file:TAP/Formatter/HTML/jquery-1.4.2.min.js', 'file:TAP/Formatter/HTML/jquery.tablesorter-2.0.3.min.js', 'file:TAP/Formatter/HTML/default_report.js']; use constant default_css_uris => ['file:TAP/Formatter/HTML/default_page.css', 'file:TAP/Formatter/HTML/default_report.css']; use constant severity_map => { '' => 0, 'very-low' => 1, 'low' => 2, 'med' => 3, 'high' => 4, 'very-high' => 5, 0 => '', 1 => 'very-low', 2 => 'low', 3 => 'med', 4 => 'high', 5 => 'very-high', }; our $VERSION = '0.11'; our $FAKE_WIN32_URIS = 0; # for testing only sub _initialize { my ($self, $args) = @_; $args ||= {}; $self->SUPER::_initialize($args); my $stdout_fh = IO::File->new_from_fd( fileno(STDOUT), 'w' ) or die "Error opening STDOUT for writing: $!"; $self->verbosity( 0 ) ->stdout( $stdout_fh ) ->output_fh( $stdout_fh ) ->minify( 1 ) ->escape_output( 0 ) ->abs_file_paths( 1 ) ->abs_file_paths( 1 ) ->force_inline_css( 1 ) ->force_inline_js( 0 ) ->session_class( $self->default_session_class ) ->template_processor( $self->default_template_processor ) ->template( $self->default_template ) ->js_uris( $self->default_js_uris ) ->css_uris( $self->default_css_uris ) ->inline_js( '' ) ->inline_css( '' ) ->sessions( [] ); $self->check_for_overrides_in_env; # Laziness... # trust the user knows what they're doing with the args: foreach my $key (keys %$args) { $self->$key( $args->{$key} ) if ($self->can( $key )); } $self->html_id_iterator( $self->create_iterator( $args ) ); return $self; } sub check_for_overrides_in_env { my $self = shift; if (my $file = $ENV{TAP_FORMATTER_HTML_OUTFILE}) { $self->output_file( $file ); } my $force_css = $ENV{TAP_FORMATTER_HTML_FORCE_INLINE_CSS}; if (defined( $force_css )) { $self->force_inline_css( $force_css ); } my $force_js = $ENV{TAP_FORMATTER_HTML_FORCE_INLINE_JS}; if (defined( $force_js )) { $self->force_inline_js( $force_js ); } if (my $uris = $ENV{TAP_FORMATTER_HTML_CSS_URIS}) { my $list = [ split( ':', $uris ) ]; $self->css_uris( $list ); } if (my $uris = $ENV{TAP_FORMATTER_HTML_JS_URIS}) { my $list = [ split( ':', $uris ) ]; $self->js_uris( $list ); } if (my $file = $ENV{TAP_FORMATTER_HTML_TEMPLATE}) { $self->template( $file ); } return $self; } sub default_template_processor { my $path = __FILE__; $path =~ s/.TAP.Formatter.HTML.pm$//; return Template->new( # arguably shouldn't compile as this is only used once COMPILE_DIR => catdir( tempdir( CLEANUP => 1 ), 'TAP-Formatter-HTML' ), COMPILE_EXT => '.ttc', INCLUDE_PATH => $path, ); } sub output_file { my ($self, $file) = @_; my $fh = IO::File->new( $file, 'w' ) or die "Error opening '$file' for writing: $!"; $self->output_fh( $fh ); } sub create_iterator { my $self = shift; my $args = shift || {}; my $prefix = $args->{html_id_prefix} || 't'; my $i = 0; my $iter = sub { return $prefix . $i++ }; } sub verbose { my $self = shift; # emulate a classic accessor for compat w/TAP::Formatter::Console: if (@_) { $self->verbosity(1) } return $self->verbosity >= 1; } sub quiet { my $self = shift; # emulate a classic accessor for compat w/TAP::Formatter::Console: if (@_) { $self->verbosity(-1) } return $self->verbosity <= -1; } sub really_quiet { my $self = shift; # emulate a classic accessor for compat w/TAP::Formatter::Console: if (@_) { $self->verbosity(-2) } return $self->verbosity <= -2; } sub silent { my $self = shift; # emulate a classic accessor for compat w/TAP::Formatter::Console: if (@_) { $self->verbosity(-3) } return $self->verbosity <= -3; } # Called by Test::Harness before any test output is generated. sub prepare { my ($self, @tests) = @_; # warn ref($self) . "->prepare called with args:\n" . Dumper( \@tests ); $self->info( 'running ', scalar @tests, ' tests' ); $self->tests( [@tests] ); } # Called to create a new test session. A test session looks like this: # # my $session = $formatter->open_test( $test, $parser ); # while ( defined( my $result = $parser->next ) ) { # $session->result($result); # exit 1 if $result->is_bailout; # } # $session->close_test; sub open_test { my ($self, $test, $parser) = @_; #warn ref($self) . "->open_test called with args: " . Dumper( [$test, $parser] ); my $session = $self->session_class->new({ test => $test, parser => $parser, formatter => $self }); push @{ $self->sessions }, $session; return $session; } # $str = $harness->summary( $aggregate ); # # C produces the summary report after all tests are run. The argument is # an aggregate. sub summary { my ($self, $aggregate) = @_; #warn ref($self) . "->summary called with args: " . Dumper( [$aggregate] ); # farmed out to make sub-classing easy: my $report = $self->prepare_report( $aggregate ); $self->generate_report( $report ); # if silent is set, only print HTML if we're not printing to stdout if (! $self->silent or $self->output_fh->fileno != fileno(STDOUT)) { print { $self->output_fh } ${ $self->html }; $self->output_fh->flush; } return $self; } sub generate_report { my ($self, $r) = @_; $self->check_uris; $self->slurp_css if $self->force_inline_css; $self->slurp_js if $self->force_inline_js; my $params = { report => $r, js_uris => $self->js_uris, css_uris => $self->css_uris, inline_js => $self->inline_js, inline_css => $self->inline_css, formatter => { class => ref( $self ), version => $self->VERSION }, }; my $html = ''; $self->template_processor->process( $self->template, $params, \$html ) || die $self->template_processor->error; $self->html( \$html ); $self->minify_report if $self->minify; return $self; } # try and reduce the size of the report sub minify_report { my $self = shift; my $html_ref = $self->html; $$html_ref =~ s/^\t+//mg; return $self; } # convert all uris to URI objs # check file uris (if relative & not found, try & find them in @INC) sub check_uris { my ($self) = @_; foreach my $uri_list ($self->js_uris, $self->css_uris) { # take them out of the list to verify, push them back on later my @uris = splice( @$uri_list, 0, scalar @$uri_list ); foreach my $uri (@uris) { if (($^O =~ /win32/i or $FAKE_WIN32_URIS) and $uri =~ /^(?:(?:file)|(?:\w:)?\\)/) { $uri = URI::file->new($uri, 'win32'); } else { $uri = URI->new( $uri ); } if ($uri->scheme && $uri->scheme eq 'file') { my $path = $uri->path; unless (file_name_is_absolute($path)) { my $new_path; if (-e $path) { $new_path = rel2abs( $path ) if ($self->abs_file_paths); } else { $new_path = $self->find_in_INC( $path ); } if ($new_path) { if (($^O =~ /win32/i or $FAKE_WIN32_URIS)) { $uri = URI::file->new("file://$new_path", 'win32'); } else { $uri->path( $new_path ); } } } } push @$uri_list, $uri; } } return $self; } sub prepare_report { my ($self, $a) = @_; my $r = { tests => [], start_time => '?', end_time => '?', elapsed_time => $a->elapsed_timestr, }; # add aggregate test info: for my $key (qw( total has_errors has_problems failed parse_errors passed skipped todo todo_passed wait exit )) { $r->{$key} = $a->$key; } # do some other handy calcs: $r->{actual_passed} = $r->{passed} + $r->{todo_passed}; if ($r->{total}) { $r->{percent_passed} = sprintf('%.1f', $r->{actual_passed} / $r->{total} * 100); } else { $r->{percent_passed} = 0; } # estimate # files (# sessions could be different?): $r->{num_files} = scalar @{ $self->sessions }; # add test results: my $total_time = 0; foreach my $s (@{ $self->sessions }) { my $sr = $s->as_report; push @{$r->{tests}}, $sr; $total_time += $sr->{elapsed_time} || 0; } $r->{total_time} = $total_time; # estimate total severity: my $smap = $self->severity_map; my $severity = 0; $severity += $smap->{$_->{severity} || ''} for @{$r->{tests}}; my $avg_severity = 0; if (scalar @{$r->{tests}}) { $avg_severity = ceil($severity / scalar( @{$r->{tests}} )); } $r->{severity} = $smap->{$avg_severity}; # TODO: coverage? return $r; } # adapted from Test::TAP::HTMLMatrix # always return abs file paths if $self->abs_file_paths is on sub find_in_INC { my ($self, $file) = @_; foreach my $path (grep { not ref } @INC) { my $target = catfile($path, $file); if (-e $target) { $target = rel2abs($target) if $self->abs_file_paths; return $target; } } # non-fatal $self->log("Warning: couldn't find $file in \@INC"); return; } # adapted from Test::TAP::HTMLMatrix # slurp all 'file' uris, if possible # note: doesn't remove them from the css_uris list, just in case... sub slurp_css { my ($self) = shift; $self->info("slurping css files inline"); my $inline_css = ''; $self->_slurp_uris( $self->css_uris, \$inline_css ); # append any inline css so it gets interpreted last: $inline_css .= "\n" . $self->inline_css if $self->inline_css; $self->inline_css( $inline_css ); } sub slurp_js { my ($self) = shift; $self->info("slurping js files inline"); my $inline_js = ''; $self->_slurp_uris( $self->js_uris, \$inline_js ); # append any inline js so it gets interpreted last: $inline_js .= "\n" . $self->inline_js if $self->inline_js; $self->inline_js( $inline_js ); } sub _slurp_uris { my ($self, $uris, $slurp_to_ref) = @_; foreach my $uri (@$uris) { my $scheme = $uri->scheme; if ($scheme && $scheme eq 'file') { my $path = $uri->path; if (-e $path) { if (open my $fh, $path) { local $/ = undef; $$slurp_to_ref .= <$fh>; $$slurp_to_ref .= "\n"; } else { $self->log("Warning: couldn't open $path: $!"); } } else { $self->log("Warning: couldn't read $path: file does not exist!"); } } else { $self->log("Warning: can't include $uri inline: not a file uri"); } } return $slurp_to_ref; } sub log { my $self = shift; push @_, "\n" unless grep {/\n/} @_; $self->_output( @_ ); return $self; } sub info { my $self = shift; return unless $self->verbose; return $self->log( @_ ); } sub log_test { my $self = shift; return if $self->really_quiet; return $self->log( @_ ); } sub log_test_info { my $self = shift; return if $self->quiet; return $self->log( @_ ); } sub _output { my $self = shift; return if $self->silent; if (ref($_[0]) && ref( $_[0]) eq 'SCALAR') { # DEPRECATED: printing HTML: print { $self->stdout } ${ $_[0] }; } else { unshift @_, '# ' if $self->escape_output; print { $self->stdout } @_; } } 1; __END__ =head1 DESCRIPTION This module provides HTML output formatting for L (a replacement for L. It is largely based on ideas from L (which was built on L and thus had a few limitations - hence this module). For sample output, see: L This module is targeted at all users of automated test suites. It's meant to make reading test results easier, giving you a visual summary of your test suite and letting you drill down into individual failures (which will hopefully make testing more likely to happen at your organization ;-). The design goals are: =over 4 =item * I Once you've got your test report, it should be obvious how to use it. =item * I It should be helpful by pointing out I & I your test suite is breaking. If you've written your tests well, it should give you enough info to start tracking down the issue. =item * I Eg: should be a clean install from CPAN, and you shouldn't need to modify your existing test suite to get up & running, though I unfortunately>. =item * I You shouldn't need to do any custom-coding to get it working - the default configuration & templates should be enough to get started with. Once installed it should be a matter of running: % prove -m -Q --formatter=TAP::Formatter::HTML >output.html From your project's home dir, and opening the resulting file. =item * I You should be able to configure & customize it to suit your needs. As such, css, javascript and templates are all configurable. =back =head1 METHODS =head2 CONSTRUCTOR =head3 new my $fmt = $class->new({ %args }); =head2 ACCESSORS All chaining L: =head3 verbosity $fmt->verbosity( [ $v ] ) Verbosity level, as defined in L: 1 verbose Print individual test results (and more) to STDOUT. 0 normal -1 quiet Suppress some test output (eg: test failures). -2 really quiet Suppress everything to STDOUT but the HTML report. -3 silent Suppress all output to STDOUT, including the HTML report. Note that the report is also available via L. You can also provide a custom L (aka L) that will be used instead of L, even if I is on. =head3 stdout $fmt->stdout( [ \*FH ] ); An L filehandle for catching standard output. Defaults to C. =head3 output_fh $fmt->output_fh( [ \*FH ] ); An L filehandle for printing the HTML report to. Defaults to the same object as L. B If L is set to C, printing to C will still occur. (that is, assuming you've opened a different file, B C). =head3 output_file $fmt->output_file( $file_name ) Not strictly an accessor - this is a shortcut for setting L, equivalent to: $fmt->output_fh( IO::File->new( $file_name, 'w' ) ); You can set this with the C environment variable =head3 escape_output $fmt->escape_output( [ $boolean ] ); If set, all output to L is escaped. This is probably only useful if you're testing the formatter. Defaults to C<0>. =head3 html $fmt->html( [ \$html ] ); This is a reference to the scalar containing the html generated on the last test run. Useful if you have L set to C, and have not provided a custom L to write the report to. =head3 tests $fmt->tests( [ \@test_files ] ) A list of test files we're running, set by L. =head3 session_class $fmt->session_class( [ $class ] ) Class to use for L test sessions. You probably won't need to use this unless you're hacking or sub-classing the formatter. Defaults to L. =head3 sessions $fmt->sessions( [ \@sessions ] ) Test sessions added by L. You probably won't need to use this unless you're hacking or sub-classing the formatter. =head3 template_processor $fmt->template_processor( [ $processor ] ) The template processor to use. Defaults to a TT2 L processor with the following config: COMPILE_DIR => catdir( tempdir(), 'TAP-Formatter-HTML' ), COMPILE_EXT => '.ttc', INCLUDE_PATH => parent directory TAP::Formatter::HTML was loaded from Note: INCLUDE_PATH used to be set to: C but this was causing issues on systems with > 64 dirs in C<@INC>. See RT #74364 for details. =head3 template $fmt->template( [ $file_name ] ) The template file to load. Defaults to C. You can set this with the C environment variable. =head3 css_uris $fmt->css_uris( [ \@uris ] ) A list of Ls (or strings) to include as external stylesheets in [%- END -%] [%- IF inline_js %] [%- END %] [% IF report.has_errors %][% SET status = 'failed' %][% ELSE %][% SET status = 'passed' %][% END -%] [% status FILTER upper %] show all show failed Test file Test results Time % [%# tfoot must come before tbody %] [% report.num_files %] files [% report.total %] tests, [% report.passed %] ok, [% report.failed %] failed, [% report.todo %] todo, [% report.skipped %] skipped, [% report.parse_errors %] parse errors exit status: [% report.exit %], wait status: [% report.wait %] elapsed time: [% report.elapsed_time | trim %] [% report.total_time | format('%.2fs') %] [% report.percent_passed %]% [%- FOREACH test IN report.tests %] [% test.test %] [%- SET max_tests = 0 -%] [%- SET max_tests = test.tests_planned IF test.tests_planned AND test.tests_planned > max_tests -%] [%- SET max_tests = test.tests_run IF test.tests_run AND test.tests_run > max_tests %] [%# TS=Test Summary. Note: use a table to caulculate widths automatically %] [% IF test.tests_run AND test.tests_run > 0 -%] [%- SET current_test = 0 -%] [%- FOREACH result IN test.results -%] [%- IF result.is_test %] [%#- Note: break up the tests at 100 so as to preserve sanity. This may leave some tests looking silly (if there's only 101) but in the case of 1000s of tests, it makes this more readable. We do this before incrementing the counter to avoid the first row being off by 1. -%] [%- IF current_test > 0 && (current_test % 100) == 0 %] [%- END %] [%- current_test = current_test + 1 -%] [%- END -%] [%- END -%] [%- ELSIF test.skip_all -%][%# below: class 's' = skip-ok -%] [%- ELSE -%][%# below: class 'n' = not-ok -%] [%- END %] [%- IF test.parse_errors.size > 0 %] [%- FOREACH e IN test.parse_errors %] Parse error: [% e %] [%- END %] [%- END %] [%- FOREACH result IN test.results %] [% result.raw | html -%] [%- IF result.is_unplanned %] (unplanned!)[% END -%] [%- IF result.todo_passed %] (unexpectedly succeeded!)[% END %] [%- END -%] [%- IF test.exit OR test.wait -%] exit status: [% test.exit %], wait status: [% test.wait %] [%- END %] [% test.elapsed_time | format('%.2fs') %] [% test.percent_passed ? test.percent_passed _ '%' : 'n/a' %] [%- END %]
expand all tests expand failed tests collapse current test (from halway down big tests, maybe a mouseover file or something) Give the test suite a title... libtap-formatter-html-perl-0.11+dfsg.orig/lib/ 0000755 0001750 0001750 00000000000 12076610727 020263 5 ustar salvi salvi libtap-formatter-html-perl-0.11+dfsg.orig/lib/App/ 0000755 0001750 0001750 00000000000 12076610727 021003 5 ustar salvi salvi libtap-formatter-html-perl-0.11+dfsg.orig/lib/App/Prove/ 0000755 0001750 0001750 00000000000 12076610727 022076 5 ustar salvi salvi libtap-formatter-html-perl-0.11+dfsg.orig/lib/App/Prove/Plugin/ 0000755 0001750 0001750 00000000000 12076610727 023334 5 ustar salvi salvi libtap-formatter-html-perl-0.11+dfsg.orig/lib/App/Prove/Plugin/HTML.pm 0000444 0001750 0001750 00000006124 12076610727 024437 0 ustar salvi salvi package App::Prove::Plugin::HTML; =head1 NAME App::Prove::Plugin::HTML - a prove plugin for HTML output =head1 SYNOPSIS # command-line usage: prove -P HTML=outfile:out.html,css_uri:style.css,js_uri:foo.js,force_inline_css:0 # NOTE: this is currently in alpha, this usage will likely change! =cut use strict; use warnings; use TAP::Formatter::HTML; our $VERSION = '0.11'; sub import { my ($class, @args) = @_; # deprecated, do nothing return $class; } sub load { my ($class, $p) = @_; my @args = @{ $p->{args} }; my $app = $p->{app_prove}; # parse the args my %TFH_args; foreach my $arg (@args) { my ($key, $val) = split(/:/, $arg, 2); if (grep {$key eq $_} qw(css_uri js_uri)) { push @{ $TFH_args{$key . 's'}}, $val; } else { $TFH_args{$key} = $val; } } # set the formatter to use $app->formatter( 'TAP::Formatter::HTML' ); # set ENV vars in order to pass args to TAP::Formatter::HTML # horrible, but it's currently the only way :-/ while (my ($key, $val) = each %TFH_args) { $val = join( ':', @$val ) if (ref($val) eq 'ARRAY'); $ENV{"TAP_FORMATTER_HTML_".uc($key)} = $val; } # we're done return $class; } 1; __END__ =head1 DESCRIPTION This is a quick & dirty second attempt at making L easier to use from the command line. It will change once L has better support for plugins than need to take cmdline data. The original goal was to be able to specify all the args on the cmdline, ala: prove --html=output.html --css-uri foo.css --css-uri bar.css --force-inline-css 0 But this is currently not possible with the way the L plugin system works. As a compromise, you must use the following syntax: prove -P HTML=arg1:val1,arg2:val2,... Where I is any L parameter that is configurable via C<%ENV>. =head2 Example prove -P HTML=outfile:out.html,css_uri:style.css,js_uri:foo.js,force_inline_css:0 This will cause L to load this plugin, which loads L for you, and sets I to C to save you some typing. To configure L, the following C<%ENV> vars are set: TAP_FORMATTER_HTML_OUTFILE=out.html TAP_FORMATTER_HTML_FORCE_INLINE_CSS=0 TAP_FORMATTER_HTML_CSS_URIS=style.css TAP_FORMATTER_HTML_JS_URIS=func.js Yes, you can pass 2 or more I or I args. =head2 %ENV vars?! Briefly, L currently only lets you specify the C for L, it doesn't let you instantiate a formatter, or pass config to the formatter. I If it bugs you too, then join the L devs and help us fix it ;-). =head1 BUGS Please use http://rt.cpan.org to report any issues. =head1 AUTHOR Steve Purkis =head1 COPYRIGHT Copyright (c) 2008-2010 Steve Purkis , S Purkis Consulting Ltd. All rights reserved. This module is released under the same terms as Perl itself. =head1 SEE ALSO L, L, L =cut libtap-formatter-html-perl-0.11+dfsg.orig/lib/TAP/ 0000755 0001750 0001750 00000000000 12076610727 020707 5 ustar salvi salvi libtap-formatter-html-perl-0.11+dfsg.orig/lib/TAP/Formatter/ 0000755 0001750 0001750 00000000000 12076610727 022652 5 ustar salvi salvi libtap-formatter-html-perl-0.11+dfsg.orig/lib/TAP/Formatter/HTML.pm 0000444 0001750 0001750 00000060041 12076610727 023753 0 ustar salvi salvi =head1 NAME TAP::Formatter::HTML - TAP Test Harness output delegate for html output =head1 SYNOPSIS ## ## command-line usage (alpha): ## prove -m -Q -P HTML=outfile:out.html,css_uri:style.css,js_uri:foo.js,force_inline_css:0 # backwards compat usage: prove -m -Q --formatter=TAP::Formatter::HTML >output.html # for more detail: perldoc App::Prove::Plugin::HTML ## ## perl usage: ## use TAP::Harness; my @tests = glob( 't/*.t' ); my $harness = TAP::Harness->new({ formatter_class => 'TAP::Formatter::HTML', merge => 1 }); $harness->runtests( @tests ); # prints HTML to STDOUT by default # or if you really don't want STDERR merged in: my $harness = TAP::Harness->new({ formatter_class => 'TAP::Formatter::HTML' }); # to use a custom formatter: my $fmt = TAP::Formatter::HTML->new; $fmt->css_uris([])->inline_css( $my_css ) ->js_uris(['http://mysite.com/jquery.js', 'http://mysite.com/custom.js']) ->inline_js( '$(div.summary).hide()' ); my $harness = TAP::Harness->new({ formatter => $fmt, merge => 1 }); # to output HTML to a file[handle]: $fmt->output_fh( $fh ); $fmt->output_file( '/tmp/foo.html' ); # you can use your own customized templates too: $fmt->template('custom.tt2') ->template_processor( Template->new ) ->force_inline_css(0) ->force_inline_js(0); =cut package TAP::Formatter::HTML; use strict; use warnings; use URI; use URI::file; use Template; use POSIX qw( ceil ); use IO::File; use File::Temp qw( tempfile tempdir ); use File::Spec::Functions qw( catdir catfile file_name_is_absolute rel2abs ); use TAP::Formatter::HTML::Session; # DEBUG: #use Data::Dumper 'Dumper'; use base qw( TAP::Base ); use accessors qw( verbosity stdout output_fh escape_output tests session_class sessions template_processor template html html_id_iterator minify color css_uris js_uris inline_css inline_js abs_file_paths force_inline_css force_inline_js ); use constant default_session_class => 'TAP::Formatter::HTML::Session'; use constant default_template => 'TAP/Formatter/HTML/default_report.tt2'; use constant default_js_uris => ['file:TAP/Formatter/HTML/jquery-1.4.2.min.js', 'file:TAP/Formatter/HTML/jquery.tablesorter-2.0.3.min.js', 'file:TAP/Formatter/HTML/default_report.js']; use constant default_css_uris => ['file:TAP/Formatter/HTML/default_page.css', 'file:TAP/Formatter/HTML/default_report.css']; use constant severity_map => { '' => 0, 'very-low' => 1, 'low' => 2, 'med' => 3, 'high' => 4, 'very-high' => 5, 0 => '', 1 => 'very-low', 2 => 'low', 3 => 'med', 4 => 'high', 5 => 'very-high', }; our $VERSION = '0.11'; our $FAKE_WIN32_URIS = 0; # for testing only sub _initialize { my ($self, $args) = @_; $args ||= {}; $self->SUPER::_initialize($args); my $stdout_fh = IO::File->new_from_fd( fileno(STDOUT), 'w' ) or die "Error opening STDOUT for writing: $!"; $self->verbosity( 0 ) ->stdout( $stdout_fh ) ->output_fh( $stdout_fh ) ->minify( 1 ) ->escape_output( 0 ) ->abs_file_paths( 1 ) ->abs_file_paths( 1 ) ->force_inline_css( 1 ) ->force_inline_js( 0 ) ->session_class( $self->default_session_class ) ->template_processor( $self->default_template_processor ) ->template( $self->default_template ) ->js_uris( $self->default_js_uris ) ->css_uris( $self->default_css_uris ) ->inline_js( '' ) ->inline_css( '' ) ->sessions( [] ); $self->check_for_overrides_in_env; # Laziness... # trust the user knows what they're doing with the args: foreach my $key (keys %$args) { $self->$key( $args->{$key} ) if ($self->can( $key )); } $self->html_id_iterator( $self->create_iterator( $args ) ); return $self; } sub check_for_overrides_in_env { my $self = shift; if (my $file = $ENV{TAP_FORMATTER_HTML_OUTFILE}) { $self->output_file( $file ); } my $force_css = $ENV{TAP_FORMATTER_HTML_FORCE_INLINE_CSS}; if (defined( $force_css )) { $self->force_inline_css( $force_css ); } my $force_js = $ENV{TAP_FORMATTER_HTML_FORCE_INLINE_JS}; if (defined( $force_js )) { $self->force_inline_js( $force_js ); } if (my $uris = $ENV{TAP_FORMATTER_HTML_CSS_URIS}) { my $list = [ split( ':', $uris ) ]; $self->css_uris( $list ); } if (my $uris = $ENV{TAP_FORMATTER_HTML_JS_URIS}) { my $list = [ split( ':', $uris ) ]; $self->js_uris( $list ); } if (my $file = $ENV{TAP_FORMATTER_HTML_TEMPLATE}) { $self->template( $file ); } return $self; } sub default_template_processor { my $path = __FILE__; $path =~ s/.TAP.Formatter.HTML.pm$//; return Template->new( # arguably shouldn't compile as this is only used once COMPILE_DIR => catdir( tempdir( CLEANUP => 1 ), 'TAP-Formatter-HTML' ), COMPILE_EXT => '.ttc', INCLUDE_PATH => $path, ); } sub output_file { my ($self, $file) = @_; my $fh = IO::File->new( $file, 'w' ) or die "Error opening '$file' for writing: $!"; $self->output_fh( $fh ); } sub create_iterator { my $self = shift; my $args = shift || {}; my $prefix = $args->{html_id_prefix} || 't'; my $i = 0; my $iter = sub { return $prefix . $i++ }; } sub verbose { my $self = shift; # emulate a classic accessor for compat w/TAP::Formatter::Console: if (@_) { $self->verbosity(1) } return $self->verbosity >= 1; } sub quiet { my $self = shift; # emulate a classic accessor for compat w/TAP::Formatter::Console: if (@_) { $self->verbosity(-1) } return $self->verbosity <= -1; } sub really_quiet { my $self = shift; # emulate a classic accessor for compat w/TAP::Formatter::Console: if (@_) { $self->verbosity(-2) } return $self->verbosity <= -2; } sub silent { my $self = shift; # emulate a classic accessor for compat w/TAP::Formatter::Console: if (@_) { $self->verbosity(-3) } return $self->verbosity <= -3; } # Called by Test::Harness before any test output is generated. sub prepare { my ($self, @tests) = @_; # warn ref($self) . "->prepare called with args:\n" . Dumper( \@tests ); $self->info( 'running ', scalar @tests, ' tests' ); $self->tests( [@tests] ); } # Called to create a new test session. A test session looks like this: # # my $session = $formatter->open_test( $test, $parser ); # while ( defined( my $result = $parser->next ) ) { # $session->result($result); # exit 1 if $result->is_bailout; # } # $session->close_test; sub open_test { my ($self, $test, $parser) = @_; #warn ref($self) . "->open_test called with args: " . Dumper( [$test, $parser] ); my $session = $self->session_class->new({ test => $test, parser => $parser, formatter => $self }); push @{ $self->sessions }, $session; return $session; } # $str = $harness->summary( $aggregate ); # # C produces the summary report after all tests are run. The argument is # an aggregate. sub summary { my ($self, $aggregate) = @_; #warn ref($self) . "->summary called with args: " . Dumper( [$aggregate] ); # farmed out to make sub-classing easy: my $report = $self->prepare_report( $aggregate ); $self->generate_report( $report ); # if silent is set, only print HTML if we're not printing to stdout if (! $self->silent or $self->output_fh->fileno != fileno(STDOUT)) { print { $self->output_fh } ${ $self->html }; $self->output_fh->flush; } return $self; } sub generate_report { my ($self, $r) = @_; $self->check_uris; $self->slurp_css if $self->force_inline_css; $self->slurp_js if $self->force_inline_js; my $params = { report => $r, js_uris => $self->js_uris, css_uris => $self->css_uris, inline_js => $self->inline_js, inline_css => $self->inline_css, formatter => { class => ref( $self ), version => $self->VERSION }, }; my $html = ''; $self->template_processor->process( $self->template, $params, \$html ) || die $self->template_processor->error; $self->html( \$html ); $self->minify_report if $self->minify; return $self; } # try and reduce the size of the report sub minify_report { my $self = shift; my $html_ref = $self->html; $$html_ref =~ s/^\t+//mg; return $self; } # convert all uris to URI objs # check file uris (if relative & not found, try & find them in @INC) sub check_uris { my ($self) = @_; foreach my $uri_list ($self->js_uris, $self->css_uris) { # take them out of the list to verify, push them back on later my @uris = splice( @$uri_list, 0, scalar @$uri_list ); foreach my $uri (@uris) { if (($^O =~ /win32/i or $FAKE_WIN32_URIS) and $uri =~ /^(?:(?:file)|(?:\w:)?\\)/) { $uri = URI::file->new($uri, 'win32'); } else { $uri = URI->new( $uri ); } if ($uri->scheme && $uri->scheme eq 'file') { my $path = $uri->path; unless (file_name_is_absolute($path)) { my $new_path; if (-e $path) { $new_path = rel2abs( $path ) if ($self->abs_file_paths); } else { $new_path = $self->find_in_INC( $path ); } if ($new_path) { if (($^O =~ /win32/i or $FAKE_WIN32_URIS)) { $uri = URI::file->new("file://$new_path", 'win32'); } else { $uri->path( $new_path ); } } } } push @$uri_list, $uri; } } return $self; } sub prepare_report { my ($self, $a) = @_; my $r = { tests => [], start_time => '?', end_time => '?', elapsed_time => $a->elapsed_timestr, }; # add aggregate test info: for my $key (qw( total has_errors has_problems failed parse_errors passed skipped todo todo_passed wait exit )) { $r->{$key} = $a->$key; } # do some other handy calcs: $r->{actual_passed} = $r->{passed} + $r->{todo_passed}; if ($r->{total}) { $r->{percent_passed} = sprintf('%.1f', $r->{actual_passed} / $r->{total} * 100); } else { $r->{percent_passed} = 0; } # estimate # files (# sessions could be different?): $r->{num_files} = scalar @{ $self->sessions }; # add test results: my $total_time = 0; foreach my $s (@{ $self->sessions }) { my $sr = $s->as_report; push @{$r->{tests}}, $sr; $total_time += $sr->{elapsed_time} || 0; } $r->{total_time} = $total_time; # estimate total severity: my $smap = $self->severity_map; my $severity = 0; $severity += $smap->{$_->{severity} || ''} for @{$r->{tests}}; my $avg_severity = 0; if (scalar @{$r->{tests}}) { $avg_severity = ceil($severity / scalar( @{$r->{tests}} )); } $r->{severity} = $smap->{$avg_severity}; # TODO: coverage? return $r; } # adapted from Test::TAP::HTMLMatrix # always return abs file paths if $self->abs_file_paths is on sub find_in_INC { my ($self, $file) = @_; foreach my $path (grep { not ref } @INC) { my $target = catfile($path, $file); if (-e $target) { $target = rel2abs($target) if $self->abs_file_paths; return $target; } } # non-fatal $self->log("Warning: couldn't find $file in \@INC"); return; } # adapted from Test::TAP::HTMLMatrix # slurp all 'file' uris, if possible # note: doesn't remove them from the css_uris list, just in case... sub slurp_css { my ($self) = shift; $self->info("slurping css files inline"); my $inline_css = ''; $self->_slurp_uris( $self->css_uris, \$inline_css ); # append any inline css so it gets interpreted last: $inline_css .= "\n" . $self->inline_css if $self->inline_css; $self->inline_css( $inline_css ); } sub slurp_js { my ($self) = shift; $self->info("slurping js files inline"); my $inline_js = ''; $self->_slurp_uris( $self->js_uris, \$inline_js ); # append any inline js so it gets interpreted last: $inline_js .= "\n" . $self->inline_js if $self->inline_js; $self->inline_js( $inline_js ); } sub _slurp_uris { my ($self, $uris, $slurp_to_ref) = @_; foreach my $uri (@$uris) { my $scheme = $uri->scheme; if ($scheme && $scheme eq 'file') { my $path = $uri->path; if (-e $path) { if (open my $fh, $path) { local $/ = undef; $$slurp_to_ref .= <$fh>; $$slurp_to_ref .= "\n"; } else { $self->log("Warning: couldn't open $path: $!"); } } else { $self->log("Warning: couldn't read $path: file does not exist!"); } } else { $self->log("Warning: can't include $uri inline: not a file uri"); } } return $slurp_to_ref; } sub log { my $self = shift; push @_, "\n" unless grep {/\n/} @_; $self->_output( @_ ); return $self; } sub info { my $self = shift; return unless $self->verbose; return $self->log( @_ ); } sub log_test { my $self = shift; return if $self->really_quiet; return $self->log( @_ ); } sub log_test_info { my $self = shift; return if $self->quiet; return $self->log( @_ ); } sub _output { my $self = shift; return if $self->silent; if (ref($_[0]) && ref( $_[0]) eq 'SCALAR') { # DEPRECATED: printing HTML: print { $self->stdout } ${ $_[0] }; } else { unshift @_, '# ' if $self->escape_output; print { $self->stdout } @_; } } 1; __END__ =head1 DESCRIPTION This module provides HTML output formatting for L (a replacement for L. It is largely based on ideas from L (which was built on L and thus had a few limitations - hence this module). For sample output, see: L This module is targeted at all users of automated test suites. It's meant to make reading test results easier, giving you a visual summary of your test suite and letting you drill down into individual failures (which will hopefully make testing more likely to happen at your organization ;-). The design goals are: =over 4 =item * I Once you've got your test report, it should be obvious how to use it. =item * I It should be helpful by pointing out I & I your test suite is breaking. If you've written your tests well, it should give you enough info to start tracking down the issue. =item * I Eg: should be a clean install from CPAN, and you shouldn't need to modify your existing test suite to get up & running, though I unfortunately>. =item * I You shouldn't need to do any custom-coding to get it working - the default configuration & templates should be enough to get started with. Once installed it should be a matter of running: % prove -m -Q --formatter=TAP::Formatter::HTML >output.html From your project's home dir, and opening the resulting file. =item * I You should be able to configure & customize it to suit your needs. As such, css, javascript and templates are all configurable. =back =head1 METHODS =head2 CONSTRUCTOR =head3 new my $fmt = $class->new({ %args }); =head2 ACCESSORS All chaining L: =head3 verbosity $fmt->verbosity( [ $v ] ) Verbosity level, as defined in L: 1 verbose Print individual test results (and more) to STDOUT. 0 normal -1 quiet Suppress some test output (eg: test failures). -2 really quiet Suppress everything to STDOUT but the HTML report. -3 silent Suppress all output to STDOUT, including the HTML report. Note that the report is also available via L. You can also provide a custom L (aka L) that will be used instead of L, even if I is on. =head3 stdout $fmt->stdout( [ \*FH ] ); An L filehandle for catching standard output. Defaults to C. =head3 output_fh $fmt->output_fh( [ \*FH ] ); An L filehandle for printing the HTML report to. Defaults to the same object as L. B If L is set to C, printing to C will still occur. (that is, assuming you've opened a different file, B C). =head3 output_file $fmt->output_file( $file_name ) Not strictly an accessor - this is a shortcut for setting L, equivalent to: $fmt->output_fh( IO::File->new( $file_name, 'w' ) ); You can set this with the C environment variable =head3 escape_output $fmt->escape_output( [ $boolean ] ); If set, all output to L is escaped. This is probably only useful if you're testing the formatter. Defaults to C<0>. =head3 html $fmt->html( [ \$html ] ); This is a reference to the scalar containing the html generated on the last test run. Useful if you have L set to C, and have not provided a custom L to write the report to. =head3 tests $fmt->tests( [ \@test_files ] ) A list of test files we're running, set by L. =head3 session_class $fmt->session_class( [ $class ] ) Class to use for L test sessions. You probably won't need to use this unless you're hacking or sub-classing the formatter. Defaults to L. =head3 sessions $fmt->sessions( [ \@sessions ] ) Test sessions added by L. You probably won't need to use this unless you're hacking or sub-classing the formatter. =head3 template_processor $fmt->template_processor( [ $processor ] ) The template processor to use. Defaults to a TT2 L processor with the following config: COMPILE_DIR => catdir( tempdir(), 'TAP-Formatter-HTML' ), COMPILE_EXT => '.ttc', INCLUDE_PATH => parent directory TAP::Formatter::HTML was loaded from Note: INCLUDE_PATH used to be set to: C but this was causing issues on systems with > 64 dirs in C<@INC>. See RT #74364 for details. =head3 template $fmt->template( [ $file_name ] ) The template file to load. Defaults to C. You can set this with the C environment variable. =head3 css_uris $fmt->css_uris( [ \@uris ] ) A list of Ls (or strings) to include as external stylesheets in [%- END -%] [%- IF inline_js %] [%- END %] [% IF report.has_errors %][% SET status = 'failed' %][% ELSE %][% SET status = 'passed' %][% END -%] [% status FILTER upper %] show all show failed Test file Test results Time % [%# tfoot must come before tbody %] [% report.num_files %] files [% report.total %] tests, [% report.passed %] ok, [% report.failed %] failed, [% report.todo %] todo, [% report.skipped %] skipped, [% report.parse_errors %] parse errors exit status: [% report.exit %], wait status: [% report.wait %] elapsed time: [% report.elapsed_time | trim %] [% report.total_time | format('%.2fs') %] [% report.percent_passed %]% [%- FOREACH test IN report.tests %] [% test.test %] [%- SET max_tests = 0 -%] [%- SET max_tests = test.tests_planned IF test.tests_planned AND test.tests_planned > max_tests -%] [%- SET max_tests = test.tests_run IF test.tests_run AND test.tests_run > max_tests %] [%# TS=Test Summary. Note: use a table to caulculate widths automatically %] [% IF test.tests_run AND test.tests_run > 0 -%] [%- SET current_test = 0 -%] [%- FOREACH result IN test.results -%] [%- IF result.is_test %] [%#- Note: break up the tests at 100 so as to preserve sanity. This may leave some tests looking silly (if there's only 101) but in the case of 1000s of tests, it makes this more readable. We do this before incrementing the counter to avoid the first row being off by 1. -%] [%- IF current_test > 0 && (current_test % 100) == 0 %] [%- END %] [%- current_test = current_test + 1 -%] [%- END -%] [%- END -%] [%- ELSIF test.skip_all -%][%# below: class 's' = skip-ok -%] [%- ELSE -%][%# below: class 'n' = not-ok -%] [%- END %] [%- IF test.parse_errors.size > 0 %] [%- FOREACH e IN test.parse_errors %] Parse error: [% e %] [%- END %] [%- END %] [%- FOREACH result IN test.results %] [% result.raw | html -%] [%- IF result.is_unplanned %] (unplanned!)[% END -%] [%- IF result.todo_passed %] (unexpectedly succeeded!)[% END %] [%- END -%] [%- IF test.exit OR test.wait -%] exit status: [% test.exit %], wait status: [% test.wait %] [%- END %] [% test.elapsed_time | format('%.2fs') %] [% test.percent_passed ? test.percent_passed _ '%' : 'n/a' %] [%- END %]