blob: 8724ddd2559911fef2659266a4f1e7163609128c [file] [log] [blame] [edit]
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/css/support/parsing-testcommon.js"></script>
<div id="target"></div>
<script>
// Runs a function while a stylesheet is temporarily inserted into the
// document.
function with_stylesheet(text, func) {
let s = document.createElement('style');
try {
s.textContent = text;
document.documentElement.append(s);
func(s.sheet.rules);
} finally {
s.remove();
}
}
// Runs a test while a stylesheet is temporarily inserted into the
// document.
function test_stylesheet(text, func, description) {
test(() => {
with_stylesheet(text, func);
}, description);
}
function test_valid_rule(text, description) {
test_stylesheet(text, (rules) => {
assert_equals(rules.length, 1);
assert_equals(rules[0].constructor.name, 'CSSScrollTimelineRule');
}, description);
}
function test_invalid_rule(text, description) {
test_stylesheet(text, (rules) => {
assert_equals(rules.length, 0);
}, description);
}
// Verify that for the _specifed_ value for a given _descriptor_, the _expected_
// string can be observed via the equivalent attribute on CSSScrollTimelineRule.
function test_descriptor(descriptor, specified, expected) {
if (typeof(expected) == 'undefined')
expected = specified;
let attribute = descriptor.replaceAll(/\-./g, x => x[1].toUpperCase());
test_stylesheet(`@scroll-timeline test { ${descriptor}:${specified}; }`, (rules) => {
assert_equals(rules.length, 1);
assert_equals(rules[0].constructor.name, 'CSSScrollTimelineRule');
if (Array.isArray(expected)) {
assert_in_array(rules[0][attribute], expected, "serialization should be sound");
} else {
assert_equals(rules[0][attribute], expected, "serialization should be canonical");
}
}, `CSSScrollTimelineRule.${attribute} ${specified}`);
}
test_valid_rule('@scroll-timeline foo {}', 'Empty block');
test_valid_rule('@scroll-timeline foo {', 'EOF ends block');
test_valid_rule('@scroll-timeline "foo" {}', 'Timeline name can be a <string>');
test_invalid_rule('@scroll-timeline', 'Missing prelude');
test_invalid_rule('@scroll-timeline foo', 'Missing block');
test_invalid_rule('@scroll-timeline {}', 'Missing timeline name');
test_invalid_rule('@scroll-timeline 123 {}', 'Timeline name must be an identifier');
test_invalid_rule('@scroll-timeline none {}', 'Timeline name must match <custom-ident>');
test_invalid_rule('@scroll-timeline NONE {}', 'Timeline name must match <custom-ident> (caps)');
test_invalid_rule('@scroll-timeline NoNe {}', 'Timeline name must match <custom-ident> (mixed)');
test_invalid_rule('@scroll-timeline initial {}', 'Timeline name may not be initial');
test_invalid_rule('@scroll-timeline inherit {}', 'Timeline name may not be inherit');
test_invalid_rule('@scroll-timeline unset {}', 'Timeline name may not be unset');
test_invalid_rule('@scroll-timeline revert {}', 'Timeline name may not be revert');
test_invalid_rule('@scroll-timeline default {}', 'Timeline name may not be default');
test_invalid_rule('@scroll-timeline foo bar {}', 'Extra timeline name');
// CSSRule.type
test(() => {
with_stylesheet(`@scroll-timeline valid { }`, (rules) => {
assert_equals(rules.length, 1);
let rule = rules[0];
assert_equals(rule.constructor.name, 'CSSScrollTimelineRule');
assert_equals(rule.type, 0);
});
}, 'CSSRule.type returns 0');
// CSSScrollTimelineRule.name
function test_name(specified, expected) {
if (typeof(expected) == 'undefined')
expected = specified;
test_stylesheet(`@scroll-timeline ${specified} { }`, (rules) => {
assert_equals(rules.length, 1);
assert_equals(rules[0].constructor.name, 'CSSScrollTimelineRule');
assert_equals(rules[0].name, expected);
}, `CSSScrollTimelineRule.name ${specified}`);
}
test_name('foo');
test_name('Foo');
test_name('f___123');
test_name('a\\9 b', 'a\tb'); // U+0009 CHARACTER TABULATION
test_name('"foo"', 'foo');
test_name('"none"', 'none');
// CSSScrollTimelineRule.cssText
function test_csstext(description, specified, expected) {
if (typeof(expected) == 'undefined')
expected = specified;
test_stylesheet(specified, (rules) => {
assert_equals(rules.length, 1);
assert_equals(rules[0].constructor.name, 'CSSScrollTimelineRule');
assert_equals(rules[0].cssText, expected);
}, `CSSScrollTimelineRule.cssText: ${description}`);
}
test_csstext(
'empty rule',
`@scroll-timeline timeline {}`,
`@scroll-timeline timeline { }`);
// U+0009 CHARACTER TABULATION
test_csstext(
'escaped name',
`@scroll-timeline tab\\9 tab {}`,
`@scroll-timeline tab\\9 tab { }`);
test_csstext(
'source descriptor',
`@scroll-timeline timeline { source: selector(#foo); }`);
test_csstext(
'orientation descriptor',
`@scroll-timeline timeline { orientation: inline; }`);
// https://github.com/w3c/csswg-drafts/issues/6617
test_csstext(
'scroll-offsets descriptor (none)',
`@scroll-timeline timeline { scroll-offsets: none; }`,
`@scroll-timeline timeline { scroll-offsets: none; }`,);
test_csstext(
'scroll-offsets descriptor (auto)',
`@scroll-timeline timeline { scroll-offsets: auto; }`);
test_csstext(
'scroll-offsets descriptor (container-based offset, px)',
`@scroll-timeline timeline { scroll-offsets: 100px; }`);
test_csstext(
'scroll-offsets descriptor (container-based offset, percentage)',
`@scroll-timeline timeline { scroll-offsets: 100%; }`);
test_csstext(
'scroll-offsets descriptor (element offset)',
`@scroll-timeline timeline { scroll-offsets: selector(#bar); }`);
test_csstext(
'scroll-offsets descriptor (element offset with edge)',
`@scroll-timeline timeline { scroll-offsets: selector(#bar) start; }`);
test_csstext(
'scroll-offsets descriptor (element offset with threshold)',
`@scroll-timeline timeline { scroll-offsets: selector(#bar) 1; }`);
test_csstext(
'scroll-offsets descriptor (element offset with edge and threshold)',
`@scroll-timeline timeline { scroll-offsets: selector(#bar) start 1; }`);
test_csstext(
'scroll-offsets descriptor (element offset with threshold and edge)',
`@scroll-timeline timeline { scroll-offsets: selector(#bar) 1 start; }`,
`@scroll-timeline timeline { scroll-offsets: selector(#bar) start 1; }`);
test_csstext(
'scroll-offsets descriptor (multiple offsets)',
`@scroll-timeline timeline { scroll-offsets: auto, selector(#bar) start 1; }`);
test_csstext(
'defaults',
`@scroll-timeline timeline { source: auto; orientation: auto; scroll-offsets: none; }`);
test_csstext(
'order',
`@scroll-timeline timeline { orientation: auto; source: auto; scroll-offsets: none; }`,
`@scroll-timeline timeline { source: auto; orientation: auto; scroll-offsets: none; }`);
// CSSScrollTimelineRule.source
function test_source(specified, expected) {
test_descriptor('source', specified, expected);
}
test_source('selector(#foo)');
test_source('selector( #foo )', 'selector(#foo)');
test_source(' selector(#foo) ', 'selector(#foo)');
test_source('none');
test_source(' none ', 'none');
test_source('selector(#a\\9 b)');
test_source('auto');
test_source('#foo', 'auto');
test_source('', 'auto');
test_source('element(#foo)', 'auto');
test_source('selector(#foo more)', 'auto');
test_source('selector(html)', 'auto');
test_source('selector(foo)', 'auto');
test_source('selector(:before)', 'auto');
test_source('selector(*)', 'auto');
test_source('selector(.a)', 'auto');
test_source('selector(.a, .b)', 'auto');
// CSSScrollTimelineRule.orientation
function test_orientation(specified, expected) {
test_descriptor('orientation', specified, expected);
}
test_orientation('auto');
test_orientation('block');
test_orientation('inline');
test_orientation('horizontal');
test_orientation('vertical');
test_orientation(' vertical ', 'vertical');
test_orientation('', 'auto');
test_orientation('foo', 'auto');
test_orientation('10px', 'auto');
test_orientation('red', 'auto');
// CSSScrollTimelineRule.scrollOffsets
function test_offsets(specified, expected) {
test_descriptor('scroll-offsets', specified, expected);
}
test_offsets('none');
test_offsets(' none ', 'none');
test_offsets('', 'none');
test_offsets('red', 'none');
test_offsets('#fff', 'none');
test_offsets('unset', 'none');
test_offsets('unknown(#foo)', 'none');
test_offsets('start', 'none');
test_offsets('end', 'none');
test_offsets('3', 'none');
test_offsets('selector(#foo) 3 start 10', 'none');
test_offsets('0%, 100%, selector(.a)', 'none');
test_offsets('selector(#foo) 3 end, start', 'none');
test_offsets('auto');
test_offsets('10em');
test_offsets('10%');
test_offsets('calc(1px + 1%)', ['calc(1px + 1%)', 'calc(1% + 1px)']);
test_offsets('selector(#foo)');
test_offsets(' selector(#foo)', 'selector(#foo)');
test_offsets('selector(#foo) start');
test_offsets('selector(#foo) start 3');
test_offsets('selector(#foo) 3');
test_offsets('selector(#foo) 3.5');
test_offsets('selector(#foo) end');
test_offsets('selector(#foo) end 3');
test_offsets('selector(#foo) 3 end', 'selector(#foo) end 3');
test_offsets(' auto , auto ', 'auto, auto');
test_offsets('10%, 100px');
test_offsets('auto, 100%');
test_offsets('0%, selector( #foo) 3 end ', "0%, selector(#foo) end 3");
test_offsets('selector(#foo) end 3, selector(#bar)');
test_offsets('0%, auto, selector(#foo) start, auto, selector(#bar) 12.3');
</script>