HTML-D3
view release on metacpan or search on metacpan
'tooltips: tooltip div present' => 1,
'tooltips: mouseover handler present' => 1,
'tooltips: no raw </b>' => 1,
'tooltips: escaped <\/b> present' => 1,
# render_line_chart_snippet
'snippet: die non-array' => 1,
'snippet: returns hashref' => 1,
'snippet: svg_id is chart' => 1,
'snippet: html is string' => 1,
'snippet: no DOCTYPE' => 1,
'snippet: no html wrapper' => 1,
'snippet: no body wrapper' => 1,
'snippet: no head closing' => 1,
'snippet: SVG element present' => 1,
'snippet: tooltip div present' => 1,
'snippet: mouseover present' => 1,
'snippet: no raw </b>' => 1,
'snippet: escaped <\/b> present' => 1,
'snippet: extra hashref serialised as d.extra' => 1,
'snippet: extra key appears in JSON' => 1,
'snippet: Object.entries(d.extra) in mouseover' => 1,
'snippet: non-hashref third element ignored' => 1,
'snippet: exactly one extra in data' => 1,
# render_zoomable_line_chart_snippet
'zoom: die non-array' => 1,
'zoom: returns hashref' => 1,
'zoom: svg_id is chart' => 1,
'zoom: html is string' => 1,
'zoom: no DOCTYPE' => 1,
'zoom: no html wrapper' => 1,
'zoom: d3.brushX present' => 1,
'zoom: Reset zoom button present' => 1,
'zoom: allData declared' => 1,
'zoom: currentData declared' => 1,
'zoom: zoomed.length < 2 guard' => 1,
'zoom: reset restores allData.slice' => 1,
'zoom: no raw </b>' => 1,
'zoom: escaped <\/b> present' => 1,
'zoom: extra data serialised' => 1,
'zoom: Object.entries(d.extra) present' => 1,
# render_multi_series_line_chart_with_tooltips
'ms-tt: die non-array' => 1,
'ms-tt: returns string' => 1,
'ms-tt: DOCTYPE present' => 1,
'ms-tt: tooltip div present' => 1,
'ms-tt: series name in output' => 1,
'ms-tt: no raw </b>' => 1,
'ms-tt: escaped <\/b> present' => 1,
# render_multi_series_line_chart_with_animated_tooltips
'ms-anim: die non-array' => 1,
'ms-anim: returns string' => 1,
'ms-anim: DOCTYPE present' => 1,
'ms-anim: translateY animation present' => 1,
'ms-anim: no raw </b>' => 1,
'ms-anim: escaped <\/b> present' => 1,
# render_multi_series_line_chart_with_legends
'ms-leg: die non-array' => 1,
'ms-leg: returns string' => 1,
'ms-leg: DOCTYPE present' => 1,
'ms-leg: legend CSS class in stylesheet' => 1,
'ms-leg: series name in output' => 1,
'ms-leg: no raw </b>' => 1,
'ms-leg: escaped <\/b> present' => 1,
# render_multi_series_line_chart_with_interactive_legends
'ms-int: die non-array' => 1,
'ms-int: returns string' => 1,
'ms-int: DOCTYPE present' => 1,
'ms-int: isVisible variable present' => 1,
'ms-int: isVisible ? 0 : 1 toggle present' => 1,
'ms-int: legend CSS class in stylesheet' => 1,
'ms-int: no raw </b>' => 1,
'ms-int: escaped <\/b> present' => 1,
);
# Marks a ledger entry as verified. Calling with an unknown key catches typos.
sub mark {
my ($key) = @_;
exists $LEDGER{$key}
? delete $LEDGER{$key}
: fail("BUG: unknown ledger key '$key'");
}
# âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
# new()
# âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
subtest 'new() -- default values' => sub {
# Mock Object::Configure so no config file on the test runner's machine
# can interfere with the default-value assertions.
mock('Object::Configure::configure' => sub { $_[1] });
my $chart = new_ok('HTML::D3');
isa_ok($chart, 'HTML::D3', 'constructor returns a blessed HTML::D3 object');
mark('new: returns blessed HTML::D3 object');
is($chart->{width}, $DEFAULT_WIDTH, 'default width is 800');
mark('new: default width 800');
is($chart->{height}, $DEFAULT_HEIGHT, 'default height is 600');
mark('new: default height 600');
is($chart->{title}, $DEFAULT_TITLE, 'default title is "Chart"');
mark('new: default title Chart');
restore('Object::Configure::configure');
diag('new() object: ' . join(', ', map { "$_=$chart->{$_}" } sort keys %$chart))
if $ENV{TEST_VERBOSE};
};
subtest 'new() -- custom flat-hash arguments' => sub {
my $chart = HTML::D3->new(width => 1_024, height => 768, title => 'Sales');
is($chart->{width}, 1_024, 'custom width stored');
mark('new: custom width applied');
is($chart->{height}, 768, 'custom height stored');
mark('new: custom height applied');
is($chart->{title}, 'Sales', 'custom title stored');
mark('new: custom title applied');
};
subtest 'new() -- hashref arguments' => sub {
# The POD documents that new() accepts both flat-hash and hashref calling styles.
my $chart = HTML::D3->new({ width => 640, height => 480, title => 'Ref' });
is($chart->{width}, 640, 'hashref width stored');
is($chart->{height}, 480, 'hashref height stored');
};
subtest 'render_multi_series_line_chart_with_tooltips() -- output' => sub {
my $html = HTML::D3->new(title => 'Multi TT')
->render_multi_series_line_chart_with_tooltips(\@MULTI_DATA);
returns_ok($html, { type => 'string' }, 'return value is a plain string');
mark('ms-tt: returns string');
like($html, qr/<!DOCTYPE html>/i, 'HTML5 DOCTYPE present');
mark('ms-tt: DOCTYPE present');
like($html, qr/<div class="tooltip" id="tooltip">/, 'tooltip div present');
mark('ms-tt: tooltip div present');
like($html, qr/Series A/, 'first series name present in output');
mark('ms-tt: series name in output');
unlike($html, qr{</b>}, 'raw </b> absent');
mark('ms-tt: no raw </b>');
like($html, qr{<\\/b>}, 'escaped <\/b> present');
mark('ms-tt: escaped <\/b> present');
};
# âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
# render_multi_series_line_chart_with_animated_tooltips()
# âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
subtest 'render_multi_series_line_chart_with_animated_tooltips() -- validation' => sub {
throws_ok(
sub { HTML::D3->new()->render_multi_series_line_chart_with_animated_tooltips('bad') },
qr/\Q$ERR_ARRAY_OF_HASH\E/,
'non-array data dies',
);
mark('ms-anim: die non-array');
};
subtest 'render_multi_series_line_chart_with_animated_tooltips() -- output' => sub {
my $html = HTML::D3->new(title => 'Animated')
->render_multi_series_line_chart_with_animated_tooltips(\@MULTI_DATA);
returns_ok($html, { type => 'string' }, 'return value is a plain string');
mark('ms-anim: returns string');
like($html, qr/<!DOCTYPE html>/i, 'HTML5 DOCTYPE present');
mark('ms-anim: DOCTYPE present');
# POD specifies a CSS translateY slide-in animation on the tooltip.
like($html, qr/translateY/, 'CSS translateY animation present');
mark('ms-anim: translateY animation present');
unlike($html, qr{</b>}, 'raw </b> absent');
mark('ms-anim: no raw </b>');
like($html, qr{<\\/b>}, 'escaped <\/b> present');
mark('ms-anim: escaped <\/b> present');
};
# âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
# render_multi_series_line_chart_with_legends()
# âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
subtest 'render_multi_series_line_chart_with_legends() -- validation' => sub {
throws_ok(
sub { HTML::D3->new()->render_multi_series_line_chart_with_legends('bad') },
qr/\Q$ERR_ARRAY_OF_HASH\E/,
'non-array data dies',
);
mark('ms-leg: die non-array');
};
subtest 'render_multi_series_line_chart_with_legends() -- output' => sub {
my $html = HTML::D3->new(title => 'Legends')
->render_multi_series_line_chart_with_legends(\@MULTI_DATA);
returns_ok($html, { type => 'string' }, 'return value is a plain string');
mark('ms-leg: returns string');
like($html, qr/<!DOCTYPE html>/i, 'HTML5 DOCTYPE present');
mark('ms-leg: DOCTYPE present');
# POD specifies a .legend CSS class defined in the stylesheet.
like($html, qr/\.legend\s*\{/, '.legend CSS class defined in <style> block');
mark('ms-leg: legend CSS class in stylesheet');
like($html, qr/Series A/, 'series name present in output');
mark('ms-leg: series name in output');
unlike($html, qr{</b>}, 'raw </b> absent');
mark('ms-leg: no raw </b>');
like($html, qr{<\\/b>}, 'escaped <\/b> present');
mark('ms-leg: escaped <\/b> present');
};
# âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
# render_multi_series_line_chart_with_interactive_legends()
# âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
subtest 'render_multi_series_line_chart_with_interactive_legends() -- validation' => sub {
throws_ok(
sub { HTML::D3->new()->render_multi_series_line_chart_with_interactive_legends('bad') },
qr/\Q$ERR_ARRAY_OF_HASH\E/,
'non-array data dies',
);
mark('ms-int: die non-array');
};
subtest 'render_multi_series_line_chart_with_interactive_legends() -- output' => sub {
my $html = HTML::D3->new(title => 'Interactive')
->render_multi_series_line_chart_with_interactive_legends(\@MULTI_DATA);
returns_ok($html, { type => 'string' }, 'return value is a plain string');
mark('ms-int: returns string');
like($html, qr/<!DOCTYPE html>/i, 'HTML5 DOCTYPE present');
mark('ms-int: DOCTYPE present');
# POD: "isVisible variable tracks current visibility state"
like($html, qr/isVisible/, 'isVisible variable declared in legend click handler');
mark('ms-int: isVisible variable present');
# POD: "opacity toggled by isVisible ? 0 : 1"
like($html, qr/isVisible\s*\?\s*0\s*:\s*1/, 'isVisible ? 0 : 1 opacity toggle present');
mark('ms-int: isVisible ? 0 : 1 toggle present');
like($html, qr/\.legend\s*\{/, '.legend CSS class defined in <style> block');
mark('ms-int: legend CSS class in stylesheet');
unlike($html, qr{</b>}, 'raw </b> absent');
mark('ms-int: no raw </b>');
like($html, qr{<\\/b>}, 'escaped <\/b> present');
mark('ms-int: escaped <\/b> present');
};
# âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
# Ledger check -- every documented condition must have been exercised.
# âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
subtest 'API contract ledger -- all documented conditions exercised' => sub {
if(keys %LEDGER) {
for my $untested (sort keys %LEDGER) {
fail("UNTESTED documented condition: $untested");
}
} else {
pass('All documented conditions were covered by the test suite');
}
};
done_testing();
( run in 1.073 second using v1.01-cache-2.11-cpan-364913b4093 )