File Coverage

blib/lib/Test/HTML/Differences.pm
Criterion Covered Total %
statement 98 99 98.9
branch 26 28 92.8
condition 5 6 83.3
subroutine 20 20 100.0
pod 0 2 0.0
total 149 155 96.1


line stmt bran cond sub pod time code
1             package Test::HTML::Differences;
2              
3 5     5   34737 use strict;
  5         11  
  5         173  
4 5     5   27 use warnings;
  5         8  
  5         154  
5 5     5   4296 use parent qw(Exporter);
  5         1529  
  5         30  
6 5     5   5014 use HTML::Parser;
  5         33359  
  5         235  
7 5     5   80 use HTML::Entities;
  5         9  
  5         366  
8 5     5   2903 use Text::Diff;
  5         30088  
  5         323  
9 5     5   4892 use Text::Diff::Table;
  5         91415  
  5         329  
10 5     5   2972 use Test::Differences;
  5         7084  
  5         1092  
11              
12             our $VERSION = '0.03';
13              
14             our @EXPORT = qw(
15             eq_or_diff_html
16             );
17              
18             sub import {
19 5     5   39 my $class = shift;
20 5 100 66     38 if ($_[0] && $_[0] eq '-color') {
21 1         2 shift @_;
22 1     1   70 eval "use Test::Differences::Color"; ## no critic
  1         989  
  1         13095  
  1         57  
23 1 50       7 $@ and die $@;
24             }
25 5         5696 __PACKAGE__->export_to_level(1, @_);
26             }
27              
28             sub eq_or_diff_html ($$;$) { ## no critic
29 12     12 0 34 my ($got_raw, $expected_raw, $desc) = @_;
30              
31 12         37 my $got = normalize_html($got_raw);
32 12         31 my $expected = normalize_html($expected_raw);
33              
34 12         30 my $got_pretty = normalize_html($got_raw, 1);
35 12         29 my $expected_pretty = normalize_html($expected_raw, 1);
36              
37 5     5   33 no warnings 'redefine';
  5         11  
  5         10314  
38 12         25 my $orig = \&Text::Diff::Table::file_footer;
39             local *Text::Diff::Table::file_footer = sub {
40 2     2   3612 my ($self, $seqa, $seqb, $options) = @_;
41 2         5 my $elts = $self->{ELTS};
42 2         5 for my $elt (@$elts) {
43 17 100       39 next if $elt->[-1] eq 'bar';
44 15 100       34 $elt->[1] = $got_pretty->[$elt->[0]] unless $elt->[-1] eq 'B';
45 15 100       35 $elt->[3] = $expected_pretty->[$elt->[2]] unless $elt->[-1] eq 'A';
46             }
47 2         8 $orig->(@_);
48 12         63 };
49              
50 12         25 local $Test::Builder::Level = $Test::Builder::Level + 1;
51 12         47 table_diff();
52 12         127 eq_or_diff($got, $expected, $desc);
53             }
54              
55             sub normalize_html {
56 56     56 0 92 my ($s, $pretty) = @_;
57              
58 56         170 my $root = [ root => {} => [] ];
59 56         105 my $stack = [ $root ];
60             my $p = HTML::Parser->new(
61             api_version => 3,
62             handlers => {
63             start => [
64             sub {
65 83     83   420 my ($tagname, $attr) = @_;
66 83         173 my $e = [
67             $tagname => $attr => []
68             ];
69 83         97 push @{ $stack->[-1]->[2] }, $e;
  83         164  
70 83         398 push @$stack, $e;
71             },
72             "tagname, attr"
73             ],
74             end => [
75             sub {
76 83     83   498 pop @$stack;
77             },
78             "tagname",
79             ],
80             comment => [
81             sub {
82 10     10   13 my ($text) = @_;
83 10         13 push @{ $stack->[-1]->[2] }, $text;
  10         41  
84             },
85             "text"
86             ],
87             text => [
88             sub {
89 158     158   210 my ($dtext) = @_;
90 158         482 $dtext =~ s/^\s+|\s+$//g;
91 158 100       571 push @{ $stack->[-1]->[2] }, encode_entities($dtext) if $dtext =~ /\S/;
  51         181  
92             },
93 56         802 "dtext"
94             ]
95             }
96             );
97 56         2594 $p->unbroken_text(1);
98 56         121 $p->empty_element_tags(1);
99 56         422 $p->parse($s);
100 56         237 $p->eof;
101              
102 56 100   82   334 my $indent = $pretty ? sub { " " x shift() . sprintf(shift, @_) } : sub { shift; sprintf(shift, @_) };
  82         392  
  64         72  
  64         281  
103              
104 56         90 my $ret = [];
105 56         61 my $walker; $walker = sub {
106 139     139   180 my ($parent, $level) = @_;
107 139         242 my ($tag, $attr, $children) = @$parent;
108              
109 139         395 my $a = join ' ', map { sprintf('%s="%s"', $_, encode_entities($attr->{$_})) } sort { $a cmp $b } keys %$attr;
  74         432  
  18         46  
110 139   100     1078 my $has_element = @$children > 1 || grep { ref($_) } @$children;
111 139 100       287 if ($has_element) {
112 85 100       277 push @$ret, $indent->($level, '<%s%s>', $tag, $a ? " $a" : "") unless $tag eq 'root';
    100          
113 85         138 for my $node (@$children) {
114 117 100       221 if (ref($node)) {
115 83         588 $walker->($node, $level + 1);
116             } else {
117 34         80 push @$ret, $indent->($level + 1, '%s', $node);
118             }
119             }
120 85 100       285 push @$ret, $indent->($level, '', $tag) unless $tag eq 'root';
121             } else {
122 54 50       105 if ($tag eq 'root') {
123 0         0 push @$ret, join(' ', @$children);
124             } else {
125 54 100       235 push @$ret, $indent->($level, '<%s%s>%s', $tag, $a ? " $a" : "", join(' ', @$children), $tag);
126             }
127             }
128 56         362 };
129 56         126 $walker->($root, -1);
130              
131 56         799 $ret;
132             }
133              
134              
135             1;
136             __END__