File Coverage

blib/lib/Test/FormValidator.pm
Criterion Covered Total %
statement 213 213 100.0
branch 85 86 98.8
condition 35 40 87.5
subroutine 24 24 100.0
pod 12 12 100.0
total 369 375 98.4


line stmt bran cond sub pod time code
1             package Test::FormValidator;
2              
3 7     7   221890 use warnings;
  7         19  
  7         233  
4 7     7   37 use strict;
  7         18  
  7         205  
5 7     7   33 use Carp;
  7         16  
  7         577  
6 7     7   7373 use Data::FormValidator;
  7         243194  
  7         370  
7 7     7   86 use Test::Builder ();
  7         18  
  7         104  
8 7     7   39 use Test::More ();
  7         11  
  7         99  
9 7     7   7744 use Data::Dumper ();
  7         51204  
  7         203  
10 7     7   7455 use HTML::TokeParser::Simple ();
  7         215019  
  7         18473  
11              
12             my $Test = Test::Builder->new;
13              
14             sub import {
15 7     7   147 my($self) = shift;
16 7         22 my $pack = caller;
17              
18 7         153 $Test->exported_to($pack);
19 7         96 $Test->plan(@_);
20             }
21              
22             =head1 NAME
23              
24             Test::FormValidator - Test framework for Data::FormValidator profiles
25              
26             =head1 VERSION
27              
28             Version 0.07
29              
30             =cut
31              
32             our $VERSION = '0.07';
33              
34             =head1 SYNOPSIS
35              
36             use Test::FormValidator 'no_plan';
37              
38             my $tfv = Test::FormValidator->new;
39              
40             $tfv->profile(WebApp->_change_password_profile);
41              
42             # check that the profile detects missing retyped password
43             $tfv->check(
44             'email' => 'someone-at-example.com',
45             'old_pass' => 'seekrit',
46             'new_pass1' => 'foo',
47             );
48             $tfv->missing_ok(['new_pass2'], "caught missing retyped password");
49              
50             # and that it detects missing fields
51             $tfv->check(
52             'email' => 'someone-at-example.com',
53             'old_pass' => 'seekrit',
54             'new_pass1' => 'foo',
55             'new_pass2' => 'bar',
56             );
57             $tfv->invalid_ok([qw(email new_pass1 new_pass2)], "caught bad email & passwd");
58              
59              
60             =head1 DESCRIPTION
61              
62             This is a module for testing your C profiles. It uses
63             the standard Perl test protocol (TAP) and prints out the familiar
64             C<'ok/not ok'> stuff you expect.
65              
66             Basically it lets you use a test script to quickly throw a lot of
67             different input scenarios at your profiles and make sure they work
68             properly.
69              
70             You can test for missing fields:
71              
72             # Test a profile that requires an email address and a password - if we
73             # provide only a name, then the password should be flagged as missing
74             $tfv->check(
75             'email' => 'test@example.com',
76             );
77             $tfv->missing_ok(['password'], "caught missing passwd");
78              
79             You can also test for invalid fields:
80              
81             # Test a profile that should catch a bad email address
82             $tfv->check(
83             'email' => 'test-at-example.com',
84             );
85             $tfv->invalid_ok(['email'], "caught bad email address");
86              
87             And if you have custom constraint methods, you can confirm that they
88             each work properly:
89              
90             # Test a profile that requires passwords longer than 5 characters and
91             # they have to contain both letters and numbers
92             $tfv->check(
93             'new_pass1' => 'foo',
94             'new_pass2' => 'foo',
95             );
96             $tfv->invalid_ok(
97             {
98             'new_pass1' => [qw(too_short need_alpha_num)],
99             },
100             "caught short, non-alpha-numeric password");
101              
102             And you can also test that the form fields in your HTML form match the
103             list of fields in your profile:
104              
105             $tfv->html_ok('/path/to/template.html', 'Template matches profile');
106              
107              
108             =head1 EXAMPLE
109              
110             Here's a more complete example. Assume you have a signup form with
111             these fields:
112              
113             name
114             email
115             pass1
116             pass2
117             newsletter
118              
119             The form (C) might look vaguely like this:
120              
121            
122             Name:
123             Email:
124             Password:
125             Retype Password:
126             Yummy SPAM?
127            
128              
129             In your web application, you test the input generated by this form using
130             a C profile like this:
131              
132             package WebApp;
133             use Data::FormValidator::Constraints qw(:closures);
134              
135             sub _signup_profile {
136             return {
137             required => [ qw(
138             name
139             email
140             pass1
141             pass2
142             ) ],
143             optional => [ qw(
144             newsletter
145             ) ],
146             dependencies => {
147             pass1 => 'pass2',
148             },
149             constraint_methods => {
150             # passwords must be longer than 5 characters
151             pass1 => [
152             sub {
153             my ($dfv, $val) = @_;
154             $dfv->name_this('too_short');
155             return $val if (length $val) > 5;
156             return;
157             },
158             # passwords must contain both letters and numbers
159             sub {
160             my ($dfv, $val) = @_;
161             $dfv->name_this('need_alpha_num');
162             return $val if $val =~ /\d/ and $val =~ /[[:alpha:]]/;
163             return;
164             },
165             ],
166             # passwords must match
167             pass2 => sub {
168             my ($dfv, $val) = @_;
169             $dfv->name_this('mismatch');
170             my $data = $dfv->get_input_data('as_hashref' => 1);
171             return $data->{'pass1'} if ($data->{'pass1'} || '') eq ($data->{'pass2'} || '');
172             return;
173             },
174             # email must be valid
175             email => valid_email(),
176             },
177             };
178             }
179              
180              
181             In your test script, you test the profile against various input
182             scenarios. First test that the fields listed in the profile match the
183             fields that are actually present in the HTML form:
184              
185             use Test::FormValidator 'no_plan';
186              
187             my $tfv = Test::FormValidator->new;
188             $tfv->profile(WebApp->_signup_profile);
189              
190             $tfv->html_ok('signup.html', 'Template matches profile');
191              
192             # Check for missing fields
193             $tfv->check(); # empty form
194             $tfv->missing_ok([qw(name email pass1 pass2)], 'caught missing fields');
195              
196             # check for invalid email, password
197             $tfv->check(
198             name => 'Foo',
199             email => 'foo-at-example.com',
200             pass1 => 'foo',
201             pass2 => 'bar',
202             );
203             $tfv->invalid_ok(
204             {
205             email => 'invalid'
206             pass1 => [qw(too_short need_alpha_num)],
207             pass2 => 'mismatch',
208             },
209             'caught invalid fields');
210              
211              
212             =head1 METHODS
213              
214             =head2 Seting up the Validator
215              
216             =over 4
217              
218             =item new
219              
220             You set up C by calling C. You can pass any
221             arguments to new that you can pass to C.
222              
223             For instance, to use default profile settings, you would use:
224              
225             my $tfv = Test::FormValidator->new({}, \%defaults);
226              
227             =cut
228              
229             sub new {
230 8     8 1 19464 my $class = shift;
231              
232 8         90 my $dfv = Data::FormValidator->new(@_);
233              
234 8         146 my $self = {
235             '__DFV' => $dfv,
236             '__CURRENT_PROFILE' => undef,
237             '__DFV_RESULTS' => undef,
238             };
239              
240 8         36 bless $self, $class;
241              
242              
243             }
244              
245             =item profile(\%profile)
246              
247             This sets up the current profile for all subsequent tests
248              
249             $tfv->profile(\%profile);
250              
251             Typically, you will fetch the profile from your web application:
252              
253             $tfv->profile(Webapp->_some_profile);
254              
255             You can switch profiles in the same script:
256              
257             $tfv->profile(Webapp->_first_profile);
258              
259             # ... run some tests ...
260              
261             $tfv->profile(Webapp->_second_profile);
262              
263             # ... run some other tests ...
264              
265             You can also explicitly pass a profile to L.
266              
267             =cut
268              
269             sub profile {
270 55     55 1 3579 my $self = shift;
271 55 100       151 $self->{'__CURRENT_PROFILE'} = shift if @_;
272 55         449 return $self->{'__CURRENT_PROFILE'};
273             }
274              
275             =item prefix('some text')
276              
277             This is a convenience function to set some text to be printed at the
278             start of every test description.
279              
280             It's useful to save typing:
281              
282             $tfv->profile(WebApp->_login_profile);
283             $tfv->prefix('[login form] ');
284              
285             $tfv->check({
286             'email' => 'test-at-example.com',
287             });
288              
289             $tfv->missing_ok(['password'], 'password missing');
290             $tfv->invalid_ok(['email'], 'email invalid');
291              
292             This prints out:
293              
294             ok 1 - [login form] password missing
295             ok 2 - [login form] email invalid
296              
297             You can switch prefixes in the same script; just call C with a
298             new value:
299              
300             $tfv->profile(Webapp->_first_profile);
301             $tfv->prefix('FIRST: ');
302              
303             # ... run some tests ...
304              
305             $tfv->profile(Webapp->_second_profile);
306             $tfv->prefix('SECOND: ');
307              
308             # ... run some other tests ...
309              
310             To remove the prefix either pass a value of C:
311              
312             $tfv->prefix(undef);
313              
314             or the empty string (C<''>):
315              
316             $tfv->prefix('');
317              
318              
319             =back
320              
321             =cut
322              
323             sub prefix {
324 65     65 1 6635 my $self = shift;
325 65 100       156 $self->{'__CURRENT_PREFIX'} = shift if @_;
326 65         144 return $self->{'__CURRENT_PREFIX'};
327             }
328              
329             sub _format_description {
330 53     53   126 my $self = shift;
331 53         78 my $description = shift;
332              
333 53         143 my $prefix = $self->prefix;
334 53 100 100     270 if (defined $prefix and $prefix ne '') {
335 14         29 $description = $prefix . $description;
336             }
337 53         222 return $description;
338             }
339              
340              
341             =head2 Checking the input
342              
343             =over 4
344              
345             =item check(%input)
346              
347             This runs %input through the current profile, and returns a
348             C results object.
349              
350             If you want to use a new profile for this check only, you can do so:
351              
352             $tfv->check(\%input, WebApp->_some_profile);
353              
354             =cut
355              
356             sub check {
357 34     34 1 9354 my $self = shift;
358              
359 34         50 my $input;
360             my $profile;
361 34 100       100 if (ref $_[0]) {
362 15         22 $input = shift;
363 15 100       42 if (@_) {
364 5         8 $profile = shift;
365             }
366             }
367             else {
368 19         83 $input = { @_ };
369             }
370              
371 34         65 my $dfv = $self->{'__DFV'};
372 34 100 100     159 $profile ||= $self->profile or croak "Test::FormValidator you need to set a profile before calling check";
373              
374 32         136 my $results = $dfv->check($input, $profile);
375              
376 32         321174 $self->{'__DFV_RESULTS'} = $results;
377 32         254 return $results;
378             }
379              
380             =item results
381              
382             Returns the C results object corresponding to the
383             most recent C, C, or C. Throws an error
384             if there has not yet been a check.
385              
386             $tfv->check_not_ok(\%input, 'some comment');
387             my $results = $tfv->results;
388              
389             =back
390              
391             =cut
392              
393             sub results {
394 3     3 1 531 my $self = shift;
395 3 100 33     23 if (not exists $self->{'__DFV_RESULTS'} or not defined $self->{'__DFV_RESULTS'}) {
396 1         186 croak "Test::FormValidator you need to call check, check_ok or check_not_ok before calling results";
397             }
398 2         7 return $self->{'__DFV_RESULTS'};
399             }
400              
401              
402             =head2 Test Methods
403              
404             Methods ending in C<_ok> do the standard C thing: on success,
405             they print out C<'ok'> and return a true value. On failure, they print
406             out C<'not ok'> and return false.
407              
408             =over 4
409              
410             =item check_ok(%input, 'description')
411              
412             Checks that the given input is valid:
413              
414             $tfv->check_ok(\%input, 'some comment');
415              
416             This is the equivalent of:
417              
418             ok($tfv->check(\%input), 'some comment') or $tfv->diag;
419              
420             It returns a C results object which is overloaded
421             to be true or false depending on the check of the test.
422              
423             =cut
424              
425             sub check_ok {
426 5     5 1 9767 my $self = shift;
427 5         8 my $input = shift;
428 5         10 my $description = shift;
429              
430 5 100 66     31 if (!$input or !(ref $input)) {
431 2         197 croak "Test::FV: usage \$tfv->check_ok(\\%input, 'description')";
432             }
433              
434 3         10 my $results = $self->check($input);
435              
436 3 100       30 if (!$results) {
437 1         24 $self->diag;
438             }
439              
440 3         185 $Test->ok($results, $self->_format_description($description));
441              
442 3         1144 return $results;
443             }
444              
445             =item check_not_ok(%input)
446              
447             Checks that the given input is not valid:
448              
449             $tfv->check_not_ok(\%input, 'some comment');
450              
451             This is the equivalent of:
452              
453             ok(!$tfv->check(\%input), 'some comment') or $tfv->diag;
454              
455             =cut
456              
457             sub check_not_ok {
458 5     5 1 6024 my $self = shift;
459 5         8 my $input = shift;
460 5         10 my $description = shift;
461              
462 5 100 66     29 if (!$input or !(ref $input)) {
463 2         169 croak "Test::FV: usage \$tfv->check_not_ok(\\%input, 'description')";
464             }
465              
466 3         9 my $results = $self->check($input);
467              
468 3 100       21 if ($results) {
469 1         26 $self->diag;
470             }
471              
472 3         131 return $Test->ok(!$results, $self->_format_description($description));
473             }
474              
475             =item missing_ok(\@fields, 'description')
476              
477             Checks C<\%input> against the current profile, and verifies that
478             C<@fields> are all flagged as missing, and that no other fields are
479             flagged as mising.
480              
481             For example:
482              
483             $tfv->check(
484             email => 'foo@example.com',
485             );
486             $tfv->missing_ok(['password'], "caught missing password");
487              
488             =cut
489              
490             sub missing_ok {
491 12     12 1 4640 my $self = shift;
492 12         18 my $fields = shift;
493 12         20 my $description = shift;
494              
495 12 100 100     89 if (!$fields or ref $fields ne 'ARRAY') {
496 2         180 croak "Test::FV: usage \$tfv->missing_ok(\\\@fields, 'description')";
497             }
498              
499 10         28 my $results = $self->{'__DFV_RESULTS'};
500 10 100       30 if (!defined $results) {
501 1         183 croak "Test::FV: call check before missing_ok";
502             }
503              
504 9         51 my @missing = sort $results->missing;
505 9         104 my @expected = sort @$fields;
506              
507 9         37 my $success = Test::More::eq_array(\@missing, \@expected);
508 9 100       3387 if (!$success) {
509 2         7 $self->diag;
510             }
511              
512 9         240 return $Test->ok($success, $self->_format_description($description));
513             }
514              
515             =item invalid_ok(\@fields, 'description');
516              
517             Checks C<\%input> against the current profile, and verifies that
518             C<@fields> are all flagged as invalid, and that no other fields are
519             flagged as invalid.
520              
521             $tfv->check(
522             email => 'foo-at-example.com',
523             );
524             $tfv->invalid_ok(['email'], "caught invalid email address");
525              
526              
527             =item invalid_ok(\%fields_and_constraints, 'description');
528              
529             Runs the current profile against C<\%input>, and verifies that specific
530             fields were invalid. It also verifies that specific constraints failed:
531              
532             $tfv->check(
533             email => 'foo-at-example.com',
534             pass1 => 'foo',
535             pass2 => 'bar',
536             )
537             $tfv->invalid_ok(
538             {
539             email => 'invalid',
540             pass1 => [qw(too_short )],
541             pass2 => 'mismatch',
542             }
543             "caught invalid email address, mismatched password and bad password");
544              
545             C<@fields> are all flagged as invalid, and that no other fields are
546             flagged as invalid.
547              
548             =cut
549              
550             sub invalid_ok {
551 18     18 1 2636 my $self = shift;
552 18         25 my $expected = shift;
553 18         32 my $description = shift;
554              
555 18 100 100     139 if (!$expected or (ref $expected ne 'ARRAY' and ref $expected ne 'HASH')) {
      66        
556 2         168 croak "Test::FV: usage \$tfv->missing_ok(\\\@expected, 'description') or \$tfv->missing_ok(\\%constraints, 'description') or ";
557             }
558              
559 16         39 my $results = $self->{'__DFV_RESULTS'};
560 16 100       40 if (!defined $results) {
561 1         113 croak "Test::FV: call check before invalid_ok";
562             }
563              
564 15         20 my $success;
565              
566             # Testing array of expected
567 15 100       45 if (ref $expected eq 'ARRAY') {
568              
569 7         39 my @invalid = sort $results->invalid;
570 7         75 my @expected = sort @$expected;
571              
572 7         25 $success = Test::More::eq_array(\@invalid, \@expected);
573             }
574 15 100       1180 if (ref $expected eq 'HASH') {
575 8         28 my $invalid = $results->invalid;
576              
577 8 100       70 if (scalar (keys %$expected) == scalar (keys %$invalid)) {
578              
579 6         9 $success = 1;
580              
581 6         16 foreach my $field (keys %$invalid) {
582              
583 10         900 my $constraints = $invalid->{$field};
584 10         15 my $expected_constraints = $expected->{$field};
585              
586 10 50       29 $constraints = [$constraints] unless ref $constraints eq 'ARRAY';
587 10 100       29 $expected_constraints = [$expected_constraints] unless ref $expected_constraints eq 'ARRAY';
588              
589             # order of constraints doesn't matter
590 10         33 $constraints = [sort @$constraints];
591 10         23 $expected_constraints = [sort @$expected_constraints];
592              
593 10 100       31 unless (Test::More::eq_array($constraints, $expected_constraints)) {
594 2         583 undef $success;
595 2         8 last;
596             }
597             }
598             }
599              
600             }
601 15 100       559 if (!$success) {
602 4         12 $self->diag;
603             }
604              
605 15         473 return $Test->ok($success, $self->_format_description($description));
606              
607             }
608              
609             =item valid_ok(\@fields, 'description');
610              
611             Checks C<\%input> against the current profile, and verifies that
612             C<@fields> are all flagged as valid, and that no other fields are
613             flagged as valid.
614              
615             $tfv->check(
616             email => 'foo@example.com',
617             );
618             $tfv->valid_ok(['email'], "only email is valid");
619              
620             =cut
621              
622             sub valid_ok {
623 12     12 1 2786 my $self = shift;
624 12         23 my $fields = shift;
625 12         20 my $description = shift;
626              
627 12 100 100     83 if (!$fields or ref $fields ne 'ARRAY') {
628 2         205 croak "Test::FV: usage \$tfv->missing_ok(\\\@fields, 'description')";
629             }
630              
631 10         23 my $results = $self->{'__DFV_RESULTS'};
632 10 100       29 if (!defined $results) {
633 1         106 croak "Test::FV: call check before invalid_ok";
634             }
635              
636 9         41 my @valid = sort $results->valid;
637 9         142 my @expected = sort @$fields;
638              
639 9         34 my $success = Test::More::eq_array(\@valid, \@expected);
640 9 100       4438 if (!$success) {
641 6         26 $self->diag;
642             }
643              
644 9         620 return $Test->ok($success, $self->_format_description($description));
645              
646             }
647              
648             =item html_ok($file, 'description');
649              
650             =item html_ok($file, { ignore => [qw(foo bar)] }, 'description');
651              
652             =item html_ok($file, { ignore => /^foo/ }, 'description');
653              
654             This checks that the form fields in the given file match the fields
655             listed in the current profile (including both C and
656             C fields).
657              
658             $tfv->html_ok('/path/to/template.html');
659              
660             If there are any extra fields in the HTML that aren't in the profile,
661             the test fails. Similarly, if there are any extra fields in the profile
662             that aren't in the HTML, the test fails.
663              
664             It's designed to catch typos and inconsistencies between the form and
665             the profile.
666              
667             For example, given a form like this (login.html):
668              
669            
670             Email:
671             Password:
672            
673              
674             and a profile like this:
675              
676             package WebApp;
677              
678             sub _login_profile {
679             return {
680             required => [ qw(
681             email
682             passwd
683             ) ],
684             };
685             }
686              
687             and the following test script (login_profile.t):
688              
689             use Test::FormValidator 'no_plan';
690              
691             use WebApp;
692             my $tfv = Test::FormValidator->new;
693             $tfv->profile(Webapp->_login_profile);
694              
695             $tfv->html_ok('template.html');
696              
697             in this scenario, the form contains the fields 'email' and 'passwd', and
698             the profile contains the fields 'email' and 'passwd'. So running the
699             test would fail:
700              
701             $ prove login_profile.t
702             t/login_profile....NOK 1
703             # Failed test (t/login_profile.t at line 7)
704             # HTML Form does not match profile:
705             # field 'password' is in the HTML but not in the profile
706             # field 'passwd' is in the profile but not in the HTML
707             #
708             # Looks like you failed 1 test of 1.
709             t/01-tfv....dubious
710             Test returned status 1 (wstat 256, 0x100)
711             DIED. FAILED test 1
712             Failed 1/1 tests, 0.00% okay
713             Failed Test Stat Wstat Total Fail Failed List of Failed
714             -------------------------------------------------------------------------------
715             t/login_profile.t 1 256 1 1 100.00% 1
716             Failed 1/1 test scripts, 0.00% okay. 1/1 subtests failed, 0.00% okay.
717              
718             If you want to ignore the presense or absense of certain fields, you can
719             do so by passing an C<'ignore'> option. Its value is either a list of
720             fields to ignore or a regex to match all fields against.
721              
722             # ignore the fields 'foo' and 'bar'
723             $tfv->html_ok($file, { ignore => [qw(foo bar)] }, 'form good!');
724              
725             # ignore the fields beginning with 'foo_'
726             $tfv->html_ok($file, { ignore => /^foo_/ }, 'form good!');
727              
728             =back
729              
730             =cut
731              
732             sub html_ok {
733 16     16 1 16863 my $self = shift;
734 16         29 my $filename = shift;
735 16         50 my $description = pop;
736              
737 16         24 my %ignore_list;
738             my $ignore_match;
739              
740 16         23 my $options = shift;
741 16 100 100     95 if ($options and ref $options eq 'HASH') {
742 9         19 my $ignore = $options->{'ignore'};
743 9 100       29 if ($ignore) {
744 8 100       47 if (ref $ignore eq 'ARRAY') {
    100          
745 1         2 my $list = $ignore;
746 1         3 %ignore_list = map { $_ => 1 } @$list;
  5         11  
747             }
748             elsif (ref $ignore eq 'Regexp') {
749 6         13 $ignore_match = $ignore;
750             }
751             }
752             }
753              
754 16 100       40 if (!$filename) {
755 1         85 croak "Test::FV: usage \$tfv->html_ok('/path/to/template.html', 'description')";
756             }
757              
758 15 100       44 my $profile = $self->profile or croak "Test::FV: must set profile before calling html_ok";
759              
760 14   100     54 my $required = $profile->{'required'} || [];
761 14   100     42 my $optional = $profile->{'optional'} || [];
762              
763 14 100       45 $required = [$required] unless ref $required eq 'ARRAY';
764 14 100       38 $optional = [$optional] unless ref $optional eq 'ARRAY';
765              
766 14         65 my @profile_fields = sort (@$required, @$optional);
767 14         50 my @html_fields = $self->_extract_form_fields_from_html($filename);
768              
769 10 100       39 if (%ignore_list) {
770 1         3 @profile_fields = grep { ! $ignore_list{$_} } @profile_fields;
  4         8  
771 1         3 @html_fields = grep { ! $ignore_list{$_} } @html_fields;
  7         11  
772             }
773 10 100       59 if ($ignore_match) {
774 6         16 @profile_fields = grep { ! /$ignore_match/ } @profile_fields;
  30         147  
775 6         14 @html_fields = grep { ! /$ignore_match/ } @html_fields;
  42         134  
776             }
777              
778 10         47 my $success = Test::More::eq_array(\@profile_fields, \@html_fields);
779 10 100       2038 if (!$success) {
780 2         14 $Test->diag("Profile fields: ". (join ", ", @profile_fields));
781 2         140 $Test->diag("HTML fields: ". (join ", ", @html_fields));
782             }
783              
784 10         139 return $Test->ok($success, $self->_format_description($description));
785             }
786              
787             sub _extract_form_fields_from_html {
788 15     15   31 my ($self, $file) = @_;
789              
790 15 100       121 my $p = HTML::TokeParser::Simple->new($file) or die "Test::FV: Can't parse HTML file $file: $!\n";
791              
792 11         25367 my %fields;
793 11         52 while (my $token = $p->get_token) {
794 550 100 100     36569 if ($token->is_start_tag('input')
      100        
795             or $token->is_start_tag('textarea')
796             or $token->is_start_tag('select') ) {
797 99         1507 my $name = $token->return_attr->{'name'};
798 99         1413 $fields{$name}++;
799             }
800             }
801 11         474 return sort keys %fields;
802             }
803              
804             =head2 Utility Methods
805              
806             These functions do not print out C<'ok'> or C<'not ok'>.
807              
808             =over 4
809              
810             =item diag()
811              
812             All of the test methods (the methods ending in C<'_ok'>) print out
813             diagnostic information on failure. However, if you are using other test
814             functions (such as C C<'ok'>), calling C<< $dfv->diag >>
815             will display the same diagnostics.
816              
817             For instance:
818              
819             use Test::More 'no_plan';
820             use Test::FormValidator;
821              
822             my $results = $tfv->check(
823             email => 'foo-at-example.com',
824             pass1 => 'foo',
825             pass2 => 'bar',
826             );
827              
828             ok($results, 'form was perfect!') or $tfv->diag;
829              
830             Running this test would produce the following output:
831              
832             $ prove profile.t
833             t/profile....NOK 1
834             # Failed test (t/profile.t at line 10)
835             # Validation results:
836             # missing: name, phone
837             # invalid:
838             # email => invalid
839             # pass1 => too_short, need_alpha_num
840             # pass2 => password_mismatch
841             # msgs:
842             # {
843             # 'email' => '* Invalid',
844             # 'pass1' => '* Invalid',
845             # 'pass2' => '* Invalid'
846             # }
847             # Looks like you failed 1 test of 1.
848             t/profile....dubious
849             Test returned status 1 (wstat 256, 0x100)
850             DIED. FAILED test 1
851             Failed 1/1 tests, 0.00% okay
852             Failed Test Stat Wstat Total Fail Failed List of Failed
853             -------------------------------------------------------------------------------
854             t/profile.t 1 256 1 1 100.00% 1
855             Failed 1/1 test scripts, 0.00% okay. 1/1 subtests failed, 0.00% okay.
856              
857             =back
858              
859             =cut
860              
861             sub diag {
862 14     14 1 27 my $self = shift;
863 14         42 $Test->diag($self->_results_diagnostics);
864             }
865              
866             sub _results_diagnostics {
867 18     18   2484 my $self = shift;
868              
869 18         36 my $results = $self->{'__DFV_RESULTS'};
870 18 100       52 if (!defined $results) {
871 1         185 croak "Test::FV: no results to diagnose!";
872             }
873              
874 17         25 my $indent = " ";
875 17         39 my @output = "Validation Results:";
876              
877 17 100       67 if ($results) {
878 7         175 push @output, $indent . "input is valid!";
879 7         45 return join "\n", @output;
880             }
881              
882 10         270 my $missing = join ", ", sort $results->missing;
883              
884 10         93 my @invalid_lines;
885 10         38 foreach my $field (sort $results->invalid) {
886 16         98 my $pad = abs(length($indent) * 2 - length $field);
887              
888 16         68 my $line = $indent . "$field" . (' ' x $pad) . '=> ';
889              
890 16         19 $line .= join ", ", sort @{ $results->invalid($field) };
  16         49  
891              
892 16         134 push @invalid_lines, $line;
893             }
894              
895 10         63 my $dumper = Data::Dumper->new([$results->msgs]);
896 10         1561 $dumper->Terse(1);
897 10         91 $dumper->Sortkeys(1);
898 10         76 my $messages_dump = $dumper->Dump;
899              
900 10 100       470 if ($missing) {
901 4         12 push @output, $indent . "missing: $missing";
902             }
903 10 100       28 if (@invalid_lines) {
904 7         17 push @output, $indent . "invalid:";
905 7         47 push @output, (($indent x 2) . $_) for @invalid_lines;
906             }
907 10         29 push @output, $indent . "msgs:";
908 10         16 push @output, $messages_dump;
909              
910 10         158 return join "\n", @output;
911             }
912              
913             =head1 AUTHOR
914              
915             Michael Graham, C<< >>
916              
917             =head1 BUGS
918              
919             Please report any bugs or feature requests to
920             C, or through the web interface at
921             L. I will be notified, and then you'll automatically
922             be notified of progress on your bug as I make changes.
923              
924             =head1 SOURCE
925              
926             The source code repository for this module can be found at http://github.com/mgraham/Test-FormValidator
927              
928             =head1 ACKNOWLEDGEMENTS
929              
930             Thanks to Mark Stosberg for input, including the crucially sensible
931             suggestion to go with an object oriented approach. He also provided the
932             code that extracts form fields from an HTML file.
933              
934              
935             =head1 COPYRIGHT & LICENSE
936              
937             Copyright 2005 Michael Graham, All Rights Reserved.
938              
939             This program is free software; you can redistribute it and/or modify it
940             under the same terms as Perl itself.
941              
942             =cut
943              
944             1; # End of Test::FormValidator