File Coverage

lib/Test/BDD/Cucumber/Harness/TermColor.pm
Criterion Covered Total %
statement 105 112 93.7
branch 26 36 72.2
condition 13 22 59.0
subroutine 18 18 100.0
pod 6 6 100.0
total 168 194 86.6


line stmt bran cond sub pod time code
1 11     11   7477 use v5.14;
  11         46  
2 11     11   64 use warnings;
  11         22  
  11         933  
3              
4             package Test::BDD::Cucumber::Harness::TermColor 0.87;
5              
6             =head1 NAME
7              
8             Test::BDD::Cucumber::Harness::TermColor - Prints colorized text to the screen
9              
10             =head1 VERSION
11              
12             version 0.87
13              
14             =head1 DESCRIPTION
15              
16             A L subclass that prints test output, colorized,
17             to the terminal.
18              
19             =head1 CONFIGURABLE ENV
20              
21             =head2 ANSI_COLORS_DISABLED
22              
23             You can use L's C to turn off colors
24             in the output.
25              
26             =cut
27              
28 11     11   73 use Moo;
  11         23  
  11         91  
29 11     11   6568 use Types::Standard qw( Str HashRef FileHandle );
  11         28  
  11         234  
30              
31 11     11   24479 use Getopt::Long;
  11         9206  
  11         107  
32              
33             # Try and make the colors just work on Windows...
34             BEGIN {
35 11 0 33 11   3677 if (
      33        
36             # We're apparently on Windows
37             $^O =~ /MSWin32/i &&
38              
39             # We haven't disabled coloured output for Term::ANSIColor
40             ( !$ENV{'ANSI_COLORS_DISABLED'} ) &&
41              
42             # Here's a flag you can use if you really really need to turn this fall-
43             # back behaviour off
44             ( !$ENV{'DISABLE_WIN32_FALLBACK'} )
45             )
46             {
47             # Try and load
48 0         0 eval { require Win32::Console::ANSI };
  0         0  
49 0 0       0 if ($@) {
50 0         0 print "# Install Win32::Console::ANSI to display colors properly\n";
51             }
52             }
53             }
54              
55 11     11   7924 use Term::ANSIColor;
  11         116790  
  11         1072  
56 11     11   103 use Test::BDD::Cucumber::Model::Result;
  11         23  
  11         20438  
57              
58             extends 'Test::BDD::Cucumber::Harness';
59              
60             =head1 CONFIGURABLE ATTRIBUTES
61              
62             =head2 fh
63              
64             A filehandle to write output to; defaults to C
65              
66             =cut
67              
68             has 'fh' => ( is => 'rw', isa => FileHandle, default => sub { \*STDOUT } );
69              
70             =head2 theme
71              
72             Name of the theme to use for colours. Defaults to `dark`. Themes are defined
73             in the private attribute C<_themes>, and currently include `light` and `dark`
74              
75             =cut
76              
77             has theme => (
78             'is' => 'ro',
79             isa => Str,
80             lazy => 1,
81             default => sub {
82             my $theme = 'dark';
83             Getopt::Long::Configure('pass_through');
84             GetOptions( "c|theme=s" => \$theme );
85             return ($theme);
86             }
87             );
88              
89             has _themes => (
90             is => 'ro',
91             isa => HashRef[HashRef],
92             lazy => 1,
93             default => sub {
94             {
95             dark => {
96             'feature' => 'bright_white',
97             'scenario' => 'bright_white',
98             'scenario_name' => 'bright_blue',
99             'pending' => 'yellow',
100             'passing' => 'green',
101             'failed' => 'red',
102             'step_data' => 'bright_cyan',
103             },
104             light => {
105             'feature' => 'reset',
106             'scenario' => 'black',
107             'scenario_name' => 'blue',
108             'pending' => 'yellow',
109             'passing' => 'green',
110             'failed' => 'red',
111             'step_data' => 'magenta',
112             },
113             };
114             }
115             );
116              
117             sub _colors {
118 853     853   2419 my $self = shift;
119 853   50     23315 return $self->_themes->{ $self->theme }
120             || die( 'Unknown color theme [' . $self->theme . ']' );
121             }
122              
123             my $margin = 2;
124             my $current_feature;
125              
126             sub feature {
127 10     10 1 30 my ( $self, $feature ) = @_;
128 10         546 my $fh = $self->fh;
129              
130 10         85 $current_feature = $feature;
131             $self->_display(
132             {
133             indent => 0,
134             color => $self->_colors->{'feature'},
135             text => $feature->keyword_original . ': ' . ( $feature->name || '' ),
136             follow_up =>
137 10 50 50     59 [ map { $_->content } @{ $feature->satisfaction || [] } ],
  27         163  
  10         1091  
138             trailing => 1
139             }
140             );
141             }
142              
143             sub feature_done {
144 10     10 1 28 my $self = shift;
145 10         258 my $fh = $self->fh;
146 10         209 print $fh "\n";
147             }
148              
149             sub scenario {
150 69     69 1 324 my ( $self, $scenario, $dataset, $longest ) = @_;
151             my $text =
152             $scenario->keyword_original . ': '
153 69   50     2679 . color( $self->_colors->{'scenario_name'} )
154             . ( $scenario->name || '' );
155              
156             $self->_display(
157             {
158             indent => 2,
159             color => $self->_colors->{'scenario'},
160             text => $text,
161             follow_up =>
162 69 50 50     5398 [ map { $_->content } @{ $scenario->description || [] } ],
  0         0  
  69         3600  
163             trailing => 0,
164             longest_line => ( $longest || 0 )
165             }
166             );
167             }
168              
169             sub scenario_done {
170 69     69 1 172 my $self = shift;
171 69         2085 my $fh = $self->fh;
172 69         2990 print $fh "\n";
173             }
174              
175       356 1   sub step { }
176              
177             sub step_done {
178 356     356 1 1078 my ( $self, $context, $result, $highlights ) = @_;
179              
180 356         650 my $color;
181 356         727 my $follow_up = [];
182 356         1434 my $status = $result->result;
183 356         821 my $failed = 0;
184              
185 356 100 100     2330 if ( $status eq 'undefined' || $status eq 'pending' ) {
    100          
186 9         29 $color = $self->_colors->{'pending'};
187             } elsif ( $status eq 'passing' ) {
188 346         1309 $color = $self->_colors->{'passing'};
189             } else {
190 1         4 $failed = 1;
191 1         6 $color = $self->_colors->{'failed'};
192 1         63 $follow_up = [ split( /\n/, $result->{'output'} ) ];
193              
194 1 50       33 if ( !$context->is_hook ) {
195 1         39 unshift @{$follow_up},
  1         36  
196             'step defined at '
197             . $context->step->line->document->filename
198             . ' line '
199             . $context->step->line->number . '.';
200             }
201             }
202              
203 356         22414 my $text;
204              
205 356 100       13257 if ( $context->is_hook ) {
    100          
206 66 50       2520 $failed or return;
207 0         0 $text = 'In ' . ucfirst( $context->verb ) . ' Hook';
208 0         0 undef $highlights;
209             } elsif ($highlights) {
210 281         15835 $text = $context->step->verb_original . ' ' . $context->text;
211 281         7724 $highlights =
212             [ [ 0, $context->step->verb_original . ' ' ], @$highlights ];
213             } else {
214 9         391 $text = $context->step->verb_original . ' ' . $context->text;
215 9         81 $highlights = [ [ 0, $text ] ];
216             }
217              
218             $self->_display(
219             {
220             indent => 4,
221             color => $color,
222             text => $text,
223             highlights => $highlights,
224             highlight => $self->_colors->{'step_data'},
225             trailing => 0,
226             follow_up => $follow_up,
227 290         3063 longest_line => $context->stash->{'scenario'}->{'longest_step_line'}
228             }
229             );
230              
231 290         2216 $self->_note_step_data( $context->step );
232             }
233              
234             sub _note_step_data {
235 290     290   785 my ( $self, $step ) = @_;
236 290 50       900 return unless $step;
237 290         580 my @step_data = @{ $step->data_as_strings };
  290         8922  
238 290 100       3889 return unless @step_data;
239              
240             my $note = sub {
241 59     59   132 my ( $text, $extra_indent ) = @_;
242 59   100     241 $extra_indent ||= 0;
243              
244             $self->_display(
245             {
246             indent => 6 + $extra_indent,
247 59         174 color => $self->_colors->{'step_data'},
248             text => $text
249             }
250             );
251 13         95 };
252              
253 13 100       104 if ( ref( $step->data ) eq 'ARRAY' ) {
254 4         13 for (@step_data) {
255 16         37 $note->($_);
256             }
257             } else {
258 9         41 $note->('"""');
259 9         39 for (@step_data) {
260 25         69 $note->( $_, 2 );
261             }
262 9         38 $note->('"""');
263             }
264             }
265              
266             sub _display {
267 428     428   16509 my ( $class, $options ) = @_;
268 428 50       9767 my $fh = ref $class ? $class->fh : \*STDOUT;
269 428         3173 $options->{'indent'} += $margin;
270              
271             # Reset it all...
272 428         1887 print $fh color 'reset';
273              
274             # Print the main line
275 428         19550 print $fh ' ' x $options->{'indent'};
276              
277             # Highlight as appropriate
278 428         1760 my $color = color $options->{'color'};
279 428 100 66     5095 if ( $options->{'highlight'} && $options->{'highlights'} ) {
280 290         827 my $reset = color 'reset';
281 290         2267 my $base = color $options->{'color'};
282 290         2108 my $hl = color $options->{'highlight'};
283              
284 290         1839 for ( @{ $options->{'highlights'} } ) {
  290         1074  
285 1066         2726 my ( $flag, $text ) = @$_;
286 1066 100       16101 print $fh $reset . ( $flag ? $hl : $base ) . $text . $reset;
287             }
288              
289             # Normal output
290             } else {
291 138         438 print $fh color $options->{'color'};
292 138         2160 print $fh $options->{'text'};
293             }
294              
295             # Reset and newline
296 428         1436 print $fh color 'reset';
297 428         4913 print $fh "\n";
298              
299             # Print follow-up lines...
300 428 100       797 for my $line ( @{ $options->{'follow_up'} || [] } ) {
  428         1913  
301 35         108 print $fh color 'reset';
302 35         325 print $fh ' ' x ( $options->{'indent'} + 4 );
303 35         99 print $fh color $options->{'color'};
304 35         331 print $fh $line;
305 35         90 print $fh color 'reset';
306 35         324 print $fh "\n";
307             }
308              
309 428 100       1973 print $fh "\n" if $options->{'trailing'};
310             }
311              
312             =head1 AUTHOR
313              
314             Peter Sergeant C
315              
316             =head1 LICENSE
317              
318             Copyright 2019-2023, Erik Huelsmann
319             Copyright 2011-2019, Peter Sergeant; Licensed under the same terms as Perl
320              
321             =cut
322              
323             1;