File Coverage

blib/lib/Test/XHTML.pm
Criterion Covered Total %
statement 19 21 90.4
branch n/a
condition n/a
subroutine 7 7 100.0
pod n/a
total 26 28 92.8


line stmt bran cond sub pod time code
1             package Test::XHTML;
2              
3 7     7   126439 use strict;
  7         16  
  7         312  
4 7     7   49 use warnings;
  7         12  
  7         264  
5              
6 7     7   27 use vars qw($VERSION);
  7         13  
  7         479  
7             $VERSION = '0.13';
8              
9             #----------------------------------------------------------------------------
10              
11             =head1 NAME
12              
13             Test::XHTML - Test web page code validation.
14              
15             =head1 SYNOPSIS
16              
17             use Test::XHTML;
18              
19             my $tests = "t/102-internal-level7.csv";
20             Test::XHTML::runtests($tests);
21              
22             =head1 DESCRIPTION
23              
24             Test the validation of a list of URLs. This includes DTD Validation, WAI WCAG
25             v2.0 compliance and basic Best Practices.
26              
27             =cut
28              
29             # -------------------------------------
30             # Library Modules
31              
32 7     7   3458 use IO::File;
  7         49615  
  7         863  
33 7     7   5721 use Data::Dumper;
  7         55081  
  7         621  
34 7     7   56 use Test::Builder;
  7         9  
  7         164  
35 7     7   3050 use Test::XHTML::Valid;
  0            
  0            
36             use Test::XHTML::WAI;
37             use Test::XHTML::Critic;
38             use WWW::Mechanize;
39              
40             # -------------------------------------
41             # Singletons
42              
43             my $mech = WWW::Mechanize->new();
44             my $txv = Test::XHTML::Valid->new(mech => $mech);
45             my $txw = Test::XHTML::WAI->new();
46             my $txc = Test::XHTML::Critic->new();
47             my $Test = Test::Builder->new();
48              
49             sub import {
50             my $self = shift;
51             my $caller = caller;
52             no strict 'refs';
53             *{$caller.'::runtests'} = \&runtests;
54             *{$caller.'::setlog'} = \&setlog;
55              
56             my @args = @_;
57              
58             $Test->exported_to($caller);
59             $Test->plan(@args) if(@args);
60             }
61              
62             # -------------------------------------
63             # Public Methods
64              
65             sub runtests {
66             my $tests = shift;
67             my ($link,$type,$content,%config,@all);
68              
69             my $fh = IO::File->new($tests,'r') or die "Cannot open file [$tests]: $!\n";
70             while(<$fh>) {
71             s/\s*$//;
72             s/^[#,].*$//;
73             next if(/^\s*$/);
74              
75             my ($cmd,$text,$label) = split(',',$_,3);
76             #$cmd =~ s/\s*$//;
77             #$Test->diag("cmd=[$cmd], text=[$text], label=[$label]");
78              
79             if($cmd eq 'config') {
80             my ($key,$value) = split('=',$text,2);
81             $config{lc $key} = $value;
82             $txw->level($value) if($key =~ /wai/i);
83             } elsif($cmd eq 'all body') {
84             push @all, {type => 'like', text => $text, label => $label};
85             } elsif($cmd eq 'all body not') {
86             push @all, {type => 'unlike', text => $text, label => $label};
87             } elsif($cmd eq 'except') {
88             push @{ $all[-1]->{except} }, $text;
89              
90             } elsif($cmd eq 'body') {
91             $label ||= ".. embedded text ('$text') found for '$link'";
92             $Test->like($content,qr!$text!s, $label);
93             $Test->diag($content) if($content !~ m!$text!s && $config{'dump'});
94              
95             } elsif($cmd eq 'body not') {
96             $label ||= ".. embedded text ('$text') not found for '$link'";
97             $Test->unlike($content,qr!$text!s, $label);
98             $Test->diag($content) if($content =~ m!$text!s && $config{'dump'});
99              
100             } elsif($cmd eq 'form' && $type eq 'url') {
101             my ($fname,$ftype) = split('=',$text,2);
102             $ftype = undef unless($ftype =~ /^(num|name|id)$/);
103             my $ok = 0;
104             my $rs;
105              
106             if($fname =~ /^\d+$/ && (!$ftype || $ftype eq 'num')) {
107             eval { $rs = $mech->form_number($fname) };
108             #$Test->diag("form_number: rs=$rs, [$@]");
109             if(!$@ && $rs) { $ok = 1; }
110             }
111             if(!$ok && (!$ftype || $ftype eq 'name')) {
112             eval { $rs = $mech->form_name($fname) };
113             #$Test->diag("form_name: rs=$rs, [$@]");
114             if(!$@ && $rs) { $ok = 1; }
115             }
116             if(!$ok && (!$ftype || $ftype eq 'id')) {
117             eval { $rs = $mech->form_id($fname) };
118             #$Test->diag("form_id: rs=$rs, [$@]");
119             if(!$@ && $rs) { $ok = 1; }
120             }
121              
122             $Test->ok($ok,".. form '$fname' found");
123              
124             } elsif($cmd eq 'input' && $type eq 'url') {
125             my ($key,$value) = split('=',$text,2);
126             if($text eq 'submit' || $key eq 'submit') {
127             $mech->submit();
128             if($mech->success()) {
129             $content = $mech->content();
130             $link = $mech->base();
131              
132             if(my $result = _check_xhtml(\%config,'xml',$content)) {
133             $Test->is_num($result->{PASS},1,"XHTML validity check for '$link'");
134             if($result->{PASS} != 1) {
135             $Test->diag($txv->errstr());
136             $Test->diag(Dumper($txv->errors())) if($config{ 'dump'});
137             $Test->diag(Dumper($result)) if($config{ 'dump'});
138             }
139             }
140              
141             if(my $result = _check_wai(\%config,$content)) {
142             $Test->is_num($result->{PASS},1,"Content passes basic WAI compliance checks for '$link'");
143             if($result->{PASS} != 1) {
144             $Test->diag($txw->errstr());
145             $Test->diag(Dumper($txw->errors())) if($config{ 'dump'});
146             $Test->diag(Dumper($result)) if($config{ 'dump'});
147             $Test->diag(Dumper($content)) if($config{ 'dump'} && $config{ 'dump'} == 2);
148             }
149             }
150              
151             if(my $result = _check_critic(\%config,$content)) {
152             $Test->is_num($result->{PASS},1,"Content passes basic page critique checks for '$link'");
153             if($result->{PASS} != 1) {
154             $Test->diag($txc->errstr());
155             $Test->diag(Dumper($txc->errors())) if($config{ 'dump'});
156             $Test->diag(Dumper($result)) if($config{ 'dump'});
157             $Test->diag(Dumper($content)) if($config{ 'dump'} && $config{ 'dump'} == 2);
158             }
159             }
160              
161             } else {
162             $content = '';
163             }
164             } else {
165             $mech->field($key,$value);
166             }
167              
168             } elsif($cmd eq 'file') {
169             $type = $cmd;
170             $link = $text;
171              
172             if(my $result = _check_xhtml(\%config,$type,$link)) {
173             $Test->is_num($result->{PASS},1,"XHTML validity check for '$link'");
174             if($result->{PASS} != 1) {
175             $Test->diag($txv->errstr());
176             $Test->diag(Dumper($txv->errors())) if($config{ 'dump'});
177             $Test->diag(Dumper($result)) if($config{ 'dump'});
178             }
179             }
180              
181              
182             $content = $txv->content();
183             $label ||= "Got FILE '$link'";
184             $Test->ok($content,$label);
185              
186             if(my $result = _check_wai(\%config,$content)) {
187             $Test->is_num($result->{PASS},1,"Content passes basic WAI compliance checks for '$link'");
188             if($result->{PASS} != 1) {
189             $Test->diag($txw->errstr());
190             $Test->diag(Dumper($txw->errors())) if($config{ 'dump'});
191             $Test->diag(Dumper($result)) if($config{ 'dump'});
192             $Test->diag(Dumper($content)) if($config{ 'dump'} && $config{ 'dump'} == 2);
193             }
194             }
195              
196             if(my $result = _check_critic(\%config,$content)) {
197             $Test->is_num($result->{PASS},1,"Content passes basic page critique checks for '$link'");
198             if($result->{PASS} != 1) {
199             $Test->diag($txc->errstr());
200             $Test->diag(Dumper($txc->errors())) if($config{ 'dump'});
201             $Test->diag(Dumper($result)) if($config{ 'dump'});
202             $Test->diag(Dumper($content)) if($config{ 'dump'} && $config{ 'dump'} == 2);
203             }
204             }
205              
206             for my $all (@all) {
207             my $ignore = 0;
208             for my $except (@{ $all->{except} }) {
209             next unless($link =~ /$except/);
210             $ignore = 1;
211             }
212              
213             if($all->{type} eq 'like') {
214             $label = $all->{label} || ".. embedded text ('$all->{text}') found for '$link'";
215             next if($ignore);
216             $Test->like($content,qr!$all->{text}!, $label);
217             $Test->diag($content) if($content !~ m!$all->{text}! && $config{'dump'});
218             } else {
219             $label = $all->{label} || ".. embedded text ('$all->{text}') not found for '$link'";
220             next if($ignore);
221             $Test->unlike($content,qr!$all->{text}!, $label);
222             $Test->diag($content) if($content =~ m!$all->{text}! && $config{'dump'});
223             }
224             }
225              
226             } elsif($cmd eq 'url') {
227             $type = $cmd;
228             $link = $text;
229              
230             if(my $result = _check_xhtml(\%config,$type,$link)) {
231             $Test->is_num($result->{PASS},1,"XHTML validity check for '$link'");
232             if($result->{PASS} != 1) {
233             $Test->diag($txv->errstr());
234             $Test->diag(Dumper($txv->errors())) if($config{ 'dump'});
235             $Test->diag(Dumper($result)) if($config{ 'dump'});
236             }
237             }
238              
239             $content = $txv->content();
240             $label ||= "Got URL '$link'";
241             $Test->ok($content,$label);
242              
243             if(my $result = _check_wai(\%config,$content)) {
244             $Test->is_num($result->{PASS},1,"Content passes basic WAI compliance checks for '$link'");
245             if($result->{PASS} != 1) {
246             $Test->diag($txw->errstr());
247             $Test->diag(Dumper($txw->errors())) if($config{ 'dump'});
248             $Test->diag(Dumper($result)) if($config{ 'dump'});
249             $Test->diag(Dumper($content)) if($config{ 'dump'} && $config{ 'dump'} == 2);
250             }
251             }
252              
253             if(my $result = _check_critic(\%config,$content)) {
254             $Test->is_num($result->{PASS},1,"Content passes basic page critique checks for '$link'");
255             if($result->{PASS} != 1) {
256             $Test->diag($txc->errstr());
257             $Test->diag(Dumper($txc->errors())) if($config{ 'dump'});
258             $Test->diag(Dumper($result)) if($config{ 'dump'});
259             $Test->diag(Dumper($content)) if($config{ 'dump'} && $config{ 'dump'} == 2);
260             }
261             }
262              
263             for my $all (@all) {
264             my $ignore = 0;
265             for my $except (@{ $all->{except} }) {
266             next unless($link =~ /$except/);
267             $ignore = 1;
268             }
269              
270             if($all->{type} eq 'like') {
271             $label = $all->{label} || ".. embedded text ('$all->{text}') found for '$link'";
272             next if($ignore);
273             $Test->like($content,qr!$all->{text}!, $label);
274             $Test->diag($content) if($content !~ m!$all->{text}! && $config{'dump'});
275             } else {
276             $label = $all->{label} || ".. embedded text ('$all->{text}') not found for '$link'";
277             next if($ignore);
278             $Test->unlike($content,qr!$all->{text}!, $label);
279             $Test->diag($content) if($content =~ m!$all->{text}! && $config{'dump'});
280             }
281             }
282              
283             }
284             }
285             $fh->close;
286             }
287              
288             sub _check_xhtml {
289             my ($config,$type,$link) = @_;
290              
291             if($config->{xhtml}) {
292             $txv->clear();
293              
294             if($type eq 'file') { $txv->process_file($link); }
295             elsif($type eq 'url') { $txv->process_link($link); }
296             elsif($type eq 'xml') { $txv->process_xml($link); }
297              
298             return $txv->process_results();
299              
300             } else {
301             if($type eq 'file') { $txv->retrieve_file($link); }
302             elsif($type eq 'url') { $txv->retrieve_url($link); }
303             }
304              
305             return;
306             }
307              
308             sub _check_wai {
309             my ($config,$content) = @_;
310              
311             return unless($config->{wai});
312              
313             $txw->clear();
314             $txw->validate($content);
315             return $txw->results();
316             }
317              
318             sub _check_critic {
319             my ($config,$content) = @_;
320              
321             return unless($config->{critic});
322              
323             $txc->clear();
324             $txc->validate($content);
325             return $txc->results();
326             }
327              
328             sub setlog {
329             my %hash = @_;
330              
331             $txv->logfile($hash{logfile}) if($hash{logfile});
332             $txv->logclean($hash{logclean}) if(defined $hash{logclean});
333              
334             $txw->logfile($hash{logfile}) if($hash{logfile});
335             $txw->logclean($hash{logclean}) if(defined $hash{logclean});
336             }
337              
338             1;
339              
340             __END__