blob: 0ba2f53cf929b74d5435308b459cebf1985e1a45 [file] [log] [blame]
<!DOCTYPE html>
<meta charset="utf-8">
<title>ScrollTimeline constructor</title>
<link rel="help" href="https://wicg.github.io/scroll-animations/#scrolltimeline-interface">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<style>
.scroller {
height: 100px;
width: 100px;
overflow: scroll;
}
.content {
height: 500px;
width: 500px;
}
</style>
<div class="scroller">
<div class="content"></div>
</div>
<script>
'use strict';
function formatOffset(v) {
if (typeof(v) == 'object')
return `${v.constructor.name}(${v.toString()})`;
return `'${v.toString()}'`;
}
function assert_offsets_equal(a, b) {
assert_equals(formatOffset(a), formatOffset(b));
}
// source
test(t => {
const scroller = document.querySelector('.scroller');
assert_equals(
new ScrollTimeline({source: scroller}).source, scroller);
}, 'A ScrollTimeline can be created with a source');
test(t => {
const div = document.createElement('div');
assert_equals(new ScrollTimeline({source: div}).source, div);
}, 'A ScrollTimeline can be created with a non-scrolling source');
test(t => {
assert_equals(new ScrollTimeline({source: null}).source, null);
}, 'A ScrollTimeline created with a null source should have no source');
test(t => {
assert_equals(new ScrollTimeline().source, document.scrollingElement);
}, 'A ScrollTimeline created without a source should use the ' +
'document.scrollingElement');
// orientation
test(t => {
assert_equals(new ScrollTimeline().orientation, 'block');
}, 'A ScrollTimeline created with the default orientation should default to ' +
`'block'`);
const gValidOrientationValues = [
'block',
'inline',
'horizontal',
'vertical',
];
for (let orientation of gValidOrientationValues) {
test(function() {
const scrollTimeline =
new ScrollTimeline({orientation: orientation});
assert_equals(scrollTimeline.orientation, orientation);
}, `'${orientation}' is a valid orientation value`);
}
test(t => {
let constructorFunc = function() {
new ScrollTimeline({orientation: 'nonsense'})
};
assert_throws_js(TypeError, constructorFunc);
// 'auto' for orientation was previously in the spec, but was removed. Make
// sure that implementations do not support it.
constructorFunc = function() {
new ScrollTimeline({orientation: 'auto'})
};
assert_throws_js(TypeError, constructorFunc);
}, 'Creating a ScrollTimeline with an invalid orientation value should throw');
// scrollOffsets
function formatOffset(v) {
if (typeof(v) == 'object')
return `${v.constructor.name}(${v.toString()})`;
return `'${v.toString()}'`;
}
function assert_offsets_equal(a, b) {
assert_equals(formatOffset(a), formatOffset(b));
}
test(t => {
assert_array_equals(new ScrollTimeline().scrollOffsets, []);
}, 'A ScrollTimeline created with the default scrollOffsets should default ' +
'to []');
test(t => {
assert_array_equals(
new ScrollTimeline({scrollOffsets: []}).scrollOffsets, []);
}, 'A ScrollTimeline created with empty scrollOffsets should resolve to []');
test(t => {
let offsets =
new ScrollTimeline({scrollOffsets: [CSS.percent(20), 'auto']})
.scrollOffsets;
assert_offsets_equal(offsets[0], CSS.percent(20));
assert_offsets_equal(offsets[1], new CSSKeywordValue('auto'));
}, 'A ScrollTimeline created with last \'auto\' offset in scrollOffsets ' +
'should be allowed.');
test(t => {
let constructorFunc = function() {
new ScrollTimeline({scrollOffsets: null})
};
assert_throws_js(TypeError, constructorFunc);
}, 'Creating a ScrollTimeline with an invalid scrollOffsets value should ' +
'throw.');
test(t => {
let constructorFunc = function() {
new ScrollTimeline(
{scrollOffsets: [CSS.percent(20), 'auto', CSS.percent(50)]})
};
assert_throws_js(TypeError, constructorFunc);
}, 'Creating a ScrollTimeline with an scrollOffsets value of ' +
'[CSS.percent(20), \'auto\', CSS.percent(50)] should throw');
const gValidScrollOffsetValues = [
CSS.px(0),
CSS.percent(100).sub(CSS.px(80)),
];
const gValidScrollOffsetSuffixes = [
// Relative lengths.
'em',
'ex',
'ch',
'rem',
'vw',
'vh',
'vmin',
'vmax',
// Absolute lengths.
'cm',
'mm',
'q',
'in',
'pc',
'pt',
'px',
// Percentage.
'%',
];
for (let offset of gValidScrollOffsetValues) {
test(function() {
const scrollTimeline =
new ScrollTimeline({scrollOffsets: [offset, offset]});
// Special case for 'auto'. This is valid input because of CSSKeywordish,
// but when read back we expect a real CSSKeywordValue.
if (offset === 'auto')
offset = new CSSKeywordValue('auto');
assert_offsets_equal(scrollTimeline.scrollOffsets[0], offset);
assert_offsets_equal(scrollTimeline.scrollOffsets[1], offset);
}, '\'' + offset + '\' is a valid scroll offset value');
}
for (const suffix of gValidScrollOffsetSuffixes) {
test(function() {
const offset = new CSSUnitValue(75, suffix);
const scrollTimeline =
new ScrollTimeline({scrollOffsets: [offset, offset]});
assert_offsets_equal(scrollTimeline.scrollOffsets[0], offset);
assert_offsets_equal(scrollTimeline.scrollOffsets[1], offset);
}, '\'' + suffix + '\' is a valid scroll offset unit');
}
// These are deliberately incomplete, just a random sampling of invalid
// values/units.
const gInvalidScrollOffsetValues = [
'',
'calc(360deg / 4)',
'left',
'#ff0000',
'rgb(0, 128, 0)',
'url("http://www.example.com/pinkish.gif")',
'this_is_garbage',
CSS.number(0),
// Multiple valid values.
'100px 5%',
// Values that would be valid if represented with CSS Typed OM:
0,
'10px',
'10%',
'calc(100% - 80px)',
];
const gInvalidScrollOffsetSuffixes = [
'deg',
's',
'Hz',
'dpi',
];
for (const offset of gInvalidScrollOffsetValues) {
test(function() {
const constructorFunc = function() {
new ScrollTimeline({scrollOffsets: ['0px', offset]})
};
assert_throws_js(TypeError, constructorFunc);
}, formatOffset(offset) + ' is an invalid scroll offset value in scrollOffsets');
}
for (const suffix of gInvalidScrollOffsetSuffixes) {
test(function() {
const offset = '75' + suffix;
const constructorFunc = function() {
new ScrollTimeline({scrollOffsets: ['0px', offset]});
};
assert_throws_js(TypeError, constructorFunc);
}, '\'' + suffix + '\' is an invalid scroll offset unit in scrollOffsets');
}
const offset_target = document.createElement('div');
const gValidElementBasedScrollOffsetValues = [
{target: offset_target},
{target: offset_target, threshold: 0},
{target: offset_target, threshold: 0.5},
{target: offset_target, threshold: 1},
];
for (let offset of gValidElementBasedScrollOffsetValues) {
test(function() {
const scrollTimeline =
new ScrollTimeline({scrollOffsets: [offset, offset]});
// Special case unspecified threshold since it gets initialized to 0.
if (!offset.hasOwnProperty('threshold'))
offset.threshold = 0;
assert_equals(scrollTimeline.scrollOffsets[0].target, offset.target);
assert_equals(scrollTimeline.scrollOffsets[0].threshold, offset.threshold);
assert_equals(scrollTimeline.scrollOffsets[1].target, offset.target);
assert_equals(scrollTimeline.scrollOffsets[1].threshold, offset.threshold);
}, '\'' + JSON.stringify(offset) + '\' is a valid scroll offset value');
}
const gInvalidElementBasedScrollOffsetValues = [
{}, // empty
{target: offset_target, threshold: "test"},
{target: offset_target, threshold: 2},
{target: offset_target, threshold: -0.2},
];
for (let offset of gInvalidElementBasedScrollOffsetValues) {
test(function() {
const constructorFunc = function() {
new ScrollTimeline({scrollOffsets: [offset]})
};
assert_throws_js(TypeError, constructorFunc);
}, `'${JSON.stringify(offset)}' is an invalid scroll offset value in ` +
`scrollOffsets`);
}
</script>