The Perl and Raku Conference 2025: Greenville, South Carolina - June 27-29 Learn more

#!/usr/bin/perl -T
use strict; use warnings; no warnings qw 'qw regexp once utf8 parenthesis';
our $tests;
BEGIN { ++$INC{'tests.pm'} }
sub tests'VERSION { $tests += pop };
plan tests => $tests;
use tests 1; # use
use_ok 'CSS::DOM::Value', ':all';
use tests 4; # constants
{
my $x;
for (qw/ CSS_INHERIT CSS_PRIMITIVE_VALUE CSS_VALUE_LIST
CSS_CUSTOM /) {
eval "is $_, " . $x++ . ", '$_'";
}
}
use tests 2; # constructor & isa
isa_ok +CSS::DOM::Value->new(type => &CSS_INHERIT), 'CSS::DOM::Value';
isa_ok +CSS::DOM::Value->new(type => &CSS_CUSTOM, value => "top left"),
'CSS::DOM::Value';
# --- cssText and cssValueType --- #
# Each subclass implements them itself, so I have to test each case. And I
# also have to make sure that getPropertyCSSValue produces the right
# thing, too.
my $s = new CSS'DOM'Style
property_parser => my $spec = $CSS::DOM::PropertyParser::Default;
# The default parser has no properties with a simple string, attr or
# counter value. They all take a list. So we add a few just to make test-
# ing easier:
$spec->add_property(s => {
format => '<string>',
});
$spec->add_property(a => {
format => '<attr>',
});
$spec->add_property(c => {
format => '<counter>',
});
# This runs 4 tests if the $property is specified and accepts $valstr.
# It runs 2 otherwise.
sub test_value {
my($s,$property,$class,$args,$valstr,$type,$name) = @_;
my $donefirst;
$s->setProperty($property, $valstr) if $property;
for my $val (
"CSS::DOM::Value$class"->new( @$args ),
$property ? $s->getPropertyCSSValue($property) : ()
) {
$name .= " (from getPCV)" x $donefirst++;
is $val->cssText, $valstr, "$name ->cssText";
is $val->cssValueType, $type,
"$name ->cssValueType";
}
}
use tests 8;
test_value $s,"top","", [type => &CSS_INHERIT], 'inherit', &CSS_INHERIT,
'inherit';
test_value $s,"background-position", "",
[type=>&CSS_CUSTOM,value=>"top left"],
'top left', &CSS_CUSTOM, 'custom value';
use tests 130;
my $css_num = &CSS::DOM::Value::Primitive::CSS_NUMBER;
for( #constant constructor val arg prop css str test name
[ number => '73' , 'z-index', '73' , 'number' ],
[ percentage => '73' , 'top' , '73%' , '%' ],
[ ems => '73' , 'top' , '73em' , 'em' ],
[ exs => '73' , 'top' , '73ex' , 'ex' ],
[ px => '73' , 'top' , '73px' , 'px' ],
[ cm => '73' , 'top' , '73cm' , 'cm' ],
[ mm => '73' , 'top' , '73mm' , 'mm' ],
[ in => '73' , 'top' , '73in' , 'inch' ],
[ pt => '73' , 'top' , '73pt' , 'point' ],
[ pc => '73' , 'top' , '73pc' , 'pica' ],
[ deg => '73' , 'azimuth', '73deg' , 'degree' ],
[ rad => '73' , 'azimuth', '73rad' , 'radian' ],
[ grad => '73' , 'azimuth', '73grad' , 'grad' ],
[ s => '73' , 'pause-after', '73s' , 'second' ],
[ ms => '73' , 'pause-after', '73ms' , 'ms' ],
[ Hz => '73' , 'pitch' , '73Hz' , 'hertz' ],
[ kHz => '73' , 'pitch' , '73kHz' , 'kHertz' ],
[ dimension => ['73', 'wob' ], '' , '73wob' , 'misc dim' ],
[ string => '73' , 's' , "'73'" , 'string' ],
[ uri => '73' , 'cue-after', "url(73)", 'URI' ],
[ ident => 'red' , 'color' , "red" , 'ident' ],
[ attr => 'red' , 'a' , "attr(red)", 'attr' ],
[ counter => ['red' ], 'c' , 'counter(red)', 'counter' ],
[ counter => ['red',undef,'lower-roman'], 'c',
'counter(red, lower-roman)', 'counter with style'],
[ counter => ['red','. '], 'c',
"counters(red, '. ')", 'counters'],
[ counter => ['red','. ','upper-latin'], 'c',
"counters(red, '. ', upper-latin)", 'counters with style'],
[ rect => [
[type=>&CSS::DOM::Value::Primitive::CSS_PX,value=>1],
[type=>&CSS::DOM::Value::Primitive::CSS_EMS,value=>2],
[type=>&CSS::DOM::Value::Primitive::CSS_IDENT,value=>'auto'],
[type=>&CSS::DOM::Value::Primitive::CSS_CM,value=>4],
], 'clip', "rect(1px, 2em, auto, 4cm)", 'rect'],
[ rgbcolor => 'red' , 'color' , 'red' , 'colour (ident)'],
[ rgbcolor => '#fff' , 'color' , '#fff', 'colour (#hhh)' ],
[ rgbcolor => '#abcdef' , 'color', '#abcdef', 'colour (#hhhhhh)'],
[ rgbcolor => [
[type=>$css_num,value=>255],
[type=>$css_num,value=>0],
[type=>$css_num,value=>0]
], 'color', 'rgb(255, 0, 0)', 'colour (rgb)'],
[ rgbcolor => [
[type=>$css_num,value=>255],
[type=>$css_num,value=>0],
[type=>$css_num,value=>0],
[type=>$css_num,value=>.5]
], 'color', 'rgba(255, 0, 0, 0.5)', 'colour (rgba)'],
[ ident => 'activeborder' , 'color', 'activeborder', 'system colour'],
) {
test_value $s, $$_[2], "::Primitive",
[
type =>
&{\&{"CSS::DOM::Value::Primitive::CSS_\U$$_[0]"}},
value => $$_[1],
],
$$_[3], &CSS_PRIMITIVE_VALUE, $$_[4]
}
use tests 20;
test_value $s,"counter-increment","::List", [
separator => ' ', values => [
[type => &CSS::DOM::Value::Primitive::CSS_IDENT, value => 'open-quote'],
[type => &CSS::DOM::Value::Primitive::CSS_NUMBER, value => '8'],
]
], "open-quote 8", &CSS_VALUE_LIST, 'space-separated list';
test_value $s,"cursor","::List", [
separator => ', ', values => [
[type => &CSS::DOM::Value::Primitive::CSS_URI, value => 'frew'],
[type => &CSS::DOM::Value::Primitive::CSS_IDENT, value => 'crosshair'],
]
], "url(frew), crosshair", &CSS_VALUE_LIST, 'comma-separated list';
test_value $s,"content","::List", [
separator => ', ', values => [
[type => &CSS::DOM::Value::Primitive::CSS_URI, value => 'cror'],
]
], "url(cror)", &CSS_VALUE_LIST, 'single-valued list';
test_value $s,"font-family","::List", [
separator => ', ', values => [
[type => &CSS::DOM::Value::Primitive::CSS_STRING, value => 'dat drin',
css => 'dat drin'],
]
], "dat drin", &CSS_VALUE_LIST,
'single-valued nominally comma-separated list';
test_value $s,"counter-reset","::List", [
separator => ' ', values => []
], 'none', &CSS_VALUE_LIST,
'empty list';
use tests 14; # writing cssText on inherit/custom values
{
my $v = new CSS::DOM::Value type => &CSS_INHERIT;
ok !eval{ $v->cssText('aaa'); 1 },
'setting cssText on an unowned css value object dies';
isa_ok $@, 'CSS::DOM::Exception', 'class of error after cssText dies';
cmp_ok $@, '==', &CSS::DOM::Exception::NO_MODIFICATION_ALLOWED_ERR,
'and the right type of error, too (after cssText dies)';
$v = new CSS::DOM::Value type => &CSS_INHERIT, owner => $s;
ok !eval{ $v->cssText('aaa'); 1 },
'setting cssText on a css value object with no property dies';
isa_ok $@, 'CSS::DOM::Exception',
'class of error after cssText dies (val with no prop)';
cmp_ok $@, '==', &CSS::DOM::Exception::NO_MODIFICATION_ALLOWED_ERR,
'and the right type of error, too (after cssText dies [val w/no prop])';
$s->backgroundPosition('inherit');
$v = $s->getPropertyCSSValue('background-position');
$v->cssText('top left'); # We write it twice on purpose, to make sure the
$v->cssText('top left'); # change in type did not discard the
is $s->backgroundPosition, 'top left', # internal owner attribute.
'value->cssText("top left") sets the owner CSS property';
is $v->cssValueType, &CSS_CUSTOM, ' and the value type';
is $v->cssText, 'top left', ' and the value object\'s own cssText';
$s->backgroundColor('inherit');
$v = $s->getPropertyCSSValue('background-color');
$v->cssText('red');
is $s->backgroundColor, 'red',
'setting the cssText of an inherit value to a colour changes the prop';
is $v->cssText, 'red',
'setting the cssText of an inherit value changes the cssText thereof';
is $v->cssValueType, &CSS_PRIMITIVE_VALUE,
'value type after setting an inherit value to a colour';
isa_ok $v, "CSS::DOM::Value::Primitive",
'object class after setting an inherit value to a colour';
$s->backgroundColor('inherit');
my $called;
$s->modification_handler(sub { ++$called });
$s->getPropertyCSSValue('background-color')->cssText('red');
is $called, 1,
'modification_handler is called when a CSS::DOM::Value changes';
}
use tests 30; # writing cssText on ‘primitive’ values
{
my $v = new CSS::DOM::Value::Primitive
type => &CSS::DOM::Value::Primitive::CSS_NUMBER, value => 43;
ok !eval{ $v->cssText('aaa'); 1 },
'setting cssText on an unowned primitive value object dies';
isa_ok $@, 'CSS::DOM::Exception',
'class of error after primitive->cssText dies';
cmp_ok $@, '==', &CSS::DOM::Exception::NO_MODIFICATION_ALLOWED_ERR,
'and the right type of error, too (after primitive->cssText dies)';
$s->backgroundImage('url(dwow)');
$v = $s->getPropertyCSSValue('background-image');
is $v->cssText('none'), 'url(dwow)',
'setting cssText returns the old value';
is $s->backgroundImage, 'none',
'prim_value->cssText("...") sets the owner CSS property';
is $v->primitiveType, &CSS::DOM::Value::Primitive::CSS_IDENT,
' prim->cssText sets the “primitive” type';
is $v->cssText, 'none',
' prim->cssText sets the value object\'s own cssText';
# We re-use the same value on purpose, to make sure the change in type did
# not discard the internal owner attribute.
$v->cssText('inherit');
is $s->backgroundImage, 'inherit',
'setting the cssText of a primitive value to inherit changes the prop';
is $v->cssText, 'inherit',
'setting the cssText of a prim val to inherit changes its cssText';
is $v->cssValueType, &CSS_INHERIT,
'value type after setting a primitive value to inherit';
isa_ok $v, "CSS::DOM::Value",
'object class after setting a primitive value to inherit';
$s->clip('rect(0,0,0,0)');
$v = $s->getPropertyCSSValue('clip')->top;
$v->cssText('red');
is $v->cssText, 0,
'setting cssText on a sub-value of a rect to a colour does nothing';
$v->cssText(50);
is $v->cssText, 0,
'setting cssText on a rect’s sub-value to a non-zero num does nothing';
$v->cssText('5px');
is $v->cssText, '5px',
'setting cssText on a sub-value of a rect to 5px works';
is $v->primitiveType, &CSS::DOM::Value::Primitive::CSS_PX,
'setting cssText on a sub-value of a rect to 5px changes the prim type';
like $s->clip, qr/^rect\(5px,\s*0,\s*0,\s*0\)\z/,
'setting cssText on a sub-value of a rect changes the prop that owns it';
$v->cssText('auto');
is $v->cssText, 'auto', 'rect sub-values can be set to auto';
$v->cssText('bdelp');
is $v->cssText, 'auto', 'but not to any other identifier';
$s->color('#c0ffee');
$v = (my $clr = $s->getPropertyCSSValue('color'))->red;
$v->cssText('red');
is $v->cssText, 192,
'setting cssText on a sub-value of a colour to a colour does nothing';
$v->cssText('255');
is $v->cssText, '255',
'setting cssText on a sub-value of a colour to 255 works';
is $clr->cssText, '#ffffee',
'changing a colour’s sub-value sets the colour’s cssText';
$v->cssText('50%');
is $v->cssText, '50%',
'setting cssText on a sub-value of a colour to 50% works';
is $v->primitiveType, &CSS::DOM::Value::Primitive::CSS_PERCENTAGE,
'changing the cssText of a colour’s sub-value changes the prim type';
like $clr->cssText, qr/^rgb\(127.5,\s*255,\s*238\)\z/,
'the colour’s cssText after making the subvalues mixed numbers & %’s';
$v = $clr->alpha;
$v->cssText('50%');
is $v->cssText, 1,
'alpha values ignore assignments of percentage values to cssText';
$v->cssText(.5);
is $v->cssText, .5,
'but number assignments (to alpha values’ cssText) work';
like $clr->cssText, qr/^rgba\(127.5,\s*255,\s*238,\s*0.5\)\z/,
'the colour’s cssText after making the subvalues mixed numbers & %’s';
$v = $s->getPropertyCSSValue('color');
$v->cssText('activeborder');;
is $v->primitiveType, &CSS::DOM::Value::Primitive::CSS_IDENT,
'setting a colour property’s cssText to a sys. colour makes it an ident';
$s->backgroundColor('red');
my $called;
$s->modification_handler(sub { ++$called });
$s->getPropertyCSSValue('background-color')->cssText('white');
is $called, 1,
"modification_handler is called when a ‘primitive’ value changes";
# Bug in 0.08 and 0.09: non-void context causes cssText not to write
# anything if the existing value is a string and there is no existing
# serialisation recorded.
$v = new CSS::DOM::Value::Primitive::
type => &CSS::DOM::Value::Primitive::CSS_STRING,
value => 'nin',
owner => $s,
property => 's',
;
scalar $v->cssText("'squow'");
is $v->cssText, "'squow'",
'prim->cssText(...) in non-void cx sets the val if existing val is str'
# ~~~ We also need a test for list sub-values retaining their owner attri-
# bute when they change type
}
use tests 10; # writing cssText on list values
{
my $v = new CSS::DOM::Value::List values => [];
ok !eval{ $v->cssText('aaa'); 1 },
'setting cssText on an unowned css list value object dies';
isa_ok $@,'CSS::DOM::Exception', 'class of error when list->cssText dies';
cmp_ok $@, '==', &CSS::DOM::Exception::NO_MODIFICATION_ALLOWED_ERR,
'and the right type of error, too (after list->cssText dies)';
$v = new CSS::DOM::Value::List values => [], owner => $s;
ok !eval{ $v->cssText('aaa'); 1 },
'setting cssText on a css value list object with no property dies';
isa_ok $@, 'CSS::DOM::Exception',
'class of error after cssText dies (val list with no prop)';
cmp_ok $@, '==', &CSS::DOM::Exception::NO_MODIFICATION_ALLOWED_ERR,
'error code when cssText dies (val list w/no prop)';
$s->fontFamily('ching');
$v = $s->getPropertyCSSValue('font-family');
$v->cssText('breck, chon');
is $s->fontFamily, 'breck, chon',
'setting the cssText of a value list changes the prop';
is $v->cssText, 'breck, chon',
'setting the cssText of a value list changes the cssText thereof';
$v->[0]->cssText('phrext');
is $v->cssText, 'phrext, chon',
'setting the cssText of a list’s sub-value sets the cssText of the list';
my $called;
$s->modification_handler(sub { ++$called });
$s->getPropertyCSSValue('font-family')->cssText('red');
is $called, 1,
'modification_handler is called when a CSS::DOM::Value::List changes';
}