Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[ScrollTimeline] Upstream based currentTime tests to WPT
This CL starts to upstream the currentTime tests to WPT. To split this up, we start with just the 'basic' tests - basic functionality, adjusting for time range, and start/end scroll offsets. The writing mode and NaN tests will follow in later patches. There are a few mostly non-behavioral changes to the tests in this CL: * Some comments were edited for clarity. * For tests with more than one assert_equals(), descriptions were added to the asserts. This is to help locate failing assert_equal lines, as jsharness does not print out the line number of a failure. * In a few places we had more than one assert_equals for the same scenario (e.g. multiple cases where scroll was after the startScrollOffset point). These have been deduplicated. Bug: 911254 Change-Id: I783fcf7f43cda876defa9c36db9a0e7dff4e82c7
- Loading branch information
1 parent
fc87d9b
commit 9bb14b1
Showing
2 changed files
with
350 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,306 @@ | ||
<!DOCTYPE html> | ||
<meta charset="utf-8"> | ||
<title>ScrollTimeline current time algorithm</title> | ||
<link rel="help" href="https://wicg.github.io/scroll-animations/#current-time-algorithm"> | ||
<script src="/resources/testharness.js"></script> | ||
<script src="/resources/testharnessreport.js"></script> | ||
|
||
<script src="./resources/scrolltimeline-utils.js"></script> | ||
|
||
<body></body> | ||
|
||
<script> | ||
test(function() { | ||
const scroller = setupScrollTimelineTest(); | ||
// Set the timeRange such that currentTime maps directly to the value | ||
// scrolled. The contents and scroller are square, so it suffices to compute | ||
// one edge and use it for all the timelines; | ||
const scrollerSize = scroller.scrollHeight - scroller.clientHeight; | ||
|
||
const blockScrollTimeline = new ScrollTimeline( | ||
{scrollSource: scroller, timeRange: scrollerSize, orientation: 'block'}); | ||
const inlineScrollTimeline = new ScrollTimeline( | ||
{scrollSource: scroller, timeRange: scrollerSize, orientation: 'inline'}); | ||
const horizontalScrollTimeline = new ScrollTimeline({ | ||
scrollSource: scroller, | ||
timeRange: scrollerSize, | ||
orientation: 'horizontal' | ||
}); | ||
const verticalScrollTimeline = new ScrollTimeline({ | ||
scrollSource: scroller, | ||
timeRange: scrollerSize, | ||
orientation: 'vertical' | ||
}); | ||
|
||
// Unscrolled, all timelines should read a currentTime of 0. | ||
assert_equals( | ||
blockScrollTimeline.currentTime, 0, 'Unscrolled block timeline'); | ||
assert_equals( | ||
inlineScrollTimeline.currentTime, 0, 'Unscrolled inline timeline'); | ||
assert_equals( | ||
horizontalScrollTimeline.currentTime, 0, | ||
'Unscrolled horizontal timeline'); | ||
assert_equals( | ||
verticalScrollTimeline.currentTime, 0, 'Unscrolled vertical timeline'); | ||
|
||
// Do some scrolling and make sure that the ScrollTimelines update. | ||
scroller.scrollTop = 50; | ||
scroller.scrollLeft = 75; | ||
|
||
// As noted above timeRange is mapped such that currentTime should be the | ||
// scroll offset. | ||
assert_equals(blockScrollTimeline.currentTime, 50, 'Scrolled block timeline'); | ||
assert_equals( | ||
inlineScrollTimeline.currentTime, 75, 'Scrolled inline timeline'); | ||
assert_equals( | ||
horizontalScrollTimeline.currentTime, 75, 'Scrolled horizontal timeline'); | ||
assert_equals( | ||
verticalScrollTimeline.currentTime, 50, 'Scrolled vertical timeline'); | ||
}, 'currentTime calculates the correct time based on scrolled amount'); | ||
|
||
test(function() { | ||
// It is difficult to calculate the scroll offset which results in an exact | ||
// currentTime. Scrolling is calculated in integers which allows for the | ||
// possibility of rounding, and scrollbar widths differ between platforms | ||
// which means it is not possible to ensure a divisible scroller size. Instead | ||
// the scroller content is made large enough that rounding differences result | ||
// in negligible deltas in the output value. | ||
const contentOverrides = new Map([['width', '1000px'], ['height', '1000px']]); | ||
const scroller = setupScrollTimelineTest(new Map(), contentOverrides); | ||
const scrollTimeline = new ScrollTimeline( | ||
{scrollSource: scroller, timeRange: 100, orientation: 'block'}); | ||
|
||
// Mapping timeRange to 100 means the output is 'percentage scrolled', so | ||
// calculate where the 50% scroll mark would be. | ||
const halfwayY = (scroller.scrollHeight - scroller.clientHeight) / 2; | ||
scroller.scrollTop = halfwayY; | ||
|
||
assert_approx_equals(scrollTimeline.currentTime, 50, 0.5); | ||
}, 'currentTime adjusts correctly for the timeRange'); | ||
|
||
test(function() { | ||
const scroller = setupScrollTimelineTest(); | ||
// Set the timeRange such that currentTime maps directly to the value | ||
// scrolled. The contents and scroller are square, so it suffices to compute | ||
// one edge and use it for all the timelines; | ||
const scrollerSize = scroller.scrollHeight - scroller.clientHeight; | ||
|
||
const lengthScrollTimeline = new ScrollTimeline({ | ||
scrollSource: scroller, | ||
timeRange: scrollerSize, | ||
orientation: 'block', | ||
startScrollOffset: '20px' | ||
}); | ||
const percentageScrollTimeline = new ScrollTimeline({ | ||
scrollSource: scroller, | ||
timeRange: scrollerSize, | ||
orientation: 'block', | ||
startScrollOffset: '20%' | ||
}); | ||
const calcScrollTimeline = new ScrollTimeline({ | ||
scrollSource: scroller, | ||
timeRange: scrollerSize, | ||
orientation: 'block', | ||
startScrollOffset: 'calc(20% - 5px)' | ||
}); | ||
|
||
// Unscrolled all timelines should read a current time of unresolved, as the | ||
// current offset (0) will be less than the startScrollOffset. | ||
assert_equals( | ||
lengthScrollTimeline.currentTime, null, | ||
'Unscrolled length-based timeline'); | ||
assert_equals( | ||
percentageScrollTimeline.currentTime, null, | ||
'Unscrolled percentage-based timeline'); | ||
assert_equals( | ||
calcScrollTimeline.currentTime, null, 'Unscrolled calc-based timeline'); | ||
|
||
// Check the length-based ScrollTimeline. | ||
scroller.scrollTop = 19; | ||
assert_equals( | ||
lengthScrollTimeline.currentTime, null, | ||
'Length-based timeline before the startScrollOffset point'); | ||
scroller.scrollTop = 20; | ||
assert_equals( | ||
lengthScrollTimeline.currentTime, 0, | ||
'Length-based timeline at the startScrollOffset point'); | ||
scroller.scrollTop = 50; | ||
assert_equals( | ||
lengthScrollTimeline.currentTime, | ||
calculateCurrentTime(50, 20, scrollerSize, scrollerSize), | ||
'Length-based timeline after the startScrollOffsetPoint'); | ||
|
||
// Check the percentage-based ScrollTimeline. | ||
scroller.scrollTop = 0.19 * scrollerSize; | ||
assert_equals( | ||
percentageScrollTimeline.currentTime, null, | ||
'Percentage-based scroller before the startScrollOffset point'); | ||
scroller.scrollTop = 0.20 * scrollerSize; | ||
assert_equals( | ||
percentageScrollTimeline.currentTime, 0, | ||
'Percentage-based scroller at the startScrollOffset point'); | ||
scroller.scrollTop = 0.50 * scrollerSize; | ||
assert_equals( | ||
percentageScrollTimeline.currentTime, | ||
calculateCurrentTime( | ||
scroller.scrollTop, 0.2 * scrollerSize, scrollerSize, scrollerSize), | ||
'Percentage-based scroller after the startScrollOffset point'); | ||
|
||
// Check the calc-based ScrollTimeline. | ||
scroller.scrollTop = 0.2 * scrollerSize - 10; | ||
assert_equals( | ||
calcScrollTimeline.currentTime, null, | ||
'Calc-based scroller before the startScrollOffset point'); | ||
scroller.scrollTop = 0.2 * scrollerSize - 5; | ||
assert_equals( | ||
calcScrollTimeline.currentTime, 0, | ||
'Calc-based scroller at the startScrollOffset point'); | ||
scroller.scrollTop = 0.2 * scrollerSize; | ||
assert_equals( | ||
calcScrollTimeline.currentTime, | ||
calculateCurrentTime( | ||
scroller.scrollTop, 0.2 * scrollerSize - 5, scrollerSize, | ||
scrollerSize), | ||
'Calc-based scroller after the startScrollOffset point'); | ||
}, 'currentTime handles startScrollOffset correctly'); | ||
|
||
test(function() { | ||
const scroller = setupScrollTimelineTest(); | ||
// Set the timeRange such that currentTime maps directly to the value | ||
// scrolled. The contents and scroller are square, so it suffices to compute | ||
// one edge and use it for all the timelines; | ||
const scrollerSize = scroller.scrollHeight - scroller.clientHeight; | ||
|
||
const lengthScrollTimeline = new ScrollTimeline({ | ||
scrollSource: scroller, | ||
timeRange: scrollerSize, | ||
orientation: 'block', | ||
endScrollOffset: (scrollerSize - 20) + 'px' | ||
}); | ||
const percentageScrollTimeline = new ScrollTimeline({ | ||
scrollSource: scroller, | ||
timeRange: scrollerSize, | ||
orientation: 'block', | ||
endScrollOffset: '80%' | ||
}); | ||
const calcScrollTimeline = new ScrollTimeline({ | ||
scrollSource: scroller, | ||
timeRange: scrollerSize, | ||
orientation: 'block', | ||
endScrollOffset: 'calc(80% + 5px)' | ||
}); | ||
|
||
// Check the length-based ScrollTimeline. | ||
scroller.scrollTop = scrollerSize; | ||
assert_equals( | ||
lengthScrollTimeline.currentTime, null, | ||
'Length-based timeline after the endScrollOffset point'); | ||
scroller.scrollTop = scrollerSize - 20; | ||
assert_equals( | ||
lengthScrollTimeline.currentTime, | ||
calculateCurrentTime( | ||
scrollerSize - 20, 0, scrollerSize - 20, scrollerSize), | ||
'Length-based timeline at the endScrollOffset point'); | ||
scroller.scrollTop = scrollerSize - 50; | ||
assert_equals( | ||
lengthScrollTimeline.currentTime, | ||
calculateCurrentTime( | ||
scrollerSize - 50, 0, scrollerSize - 20, scrollerSize), | ||
'Length-based timeline before the endScrollOffset point'); | ||
|
||
// Check the percentage-based ScrollTimeline. | ||
scroller.scrollTop = 0.81 * scrollerSize; | ||
assert_equals( | ||
percentageScrollTimeline.currentTime, null, | ||
'Percentage-based timeline after the endScrollOffset point'); | ||
scroller.scrollTop = 0.80 * scrollerSize; | ||
assert_equals( | ||
percentageScrollTimeline.currentTime, | ||
calculateCurrentTime( | ||
scroller.scrollTop, 0, 0.8 * scrollerSize, scrollerSize), | ||
'Percentage-based timeline at the endScrollOffset point'); | ||
scroller.scrollTop = 0.50 * scrollerSize; | ||
assert_equals( | ||
percentageScrollTimeline.currentTime, | ||
calculateCurrentTime( | ||
scroller.scrollTop, 0, 0.8 * scrollerSize, scrollerSize), | ||
'Percentage-based timeline before the endScrollOffset point'); | ||
|
||
// Check the calc-based ScrollTimeline. | ||
scroller.scrollTop = 0.8 * scrollerSize + 6; | ||
assert_equals( | ||
calcScrollTimeline.currentTime, null, | ||
'Calc-based timeline after the endScrollOffset point'); | ||
scroller.scrollTop = 0.8 * scrollerSize + 5; | ||
assert_equals( | ||
calcScrollTimeline.currentTime, | ||
calculateCurrentTime( | ||
scroller.scrollTop, 0, 0.8 * scrollerSize + 5, scrollerSize), | ||
'Calc-based timeline at the endScrollOffset point'); | ||
scroller.scrollTop = 0.5 * scrollerSize; | ||
assert_equals( | ||
calcScrollTimeline.currentTime, | ||
calculateCurrentTime( | ||
scroller.scrollTop, 0, 0.8 * scrollerSize + 5, scrollerSize), | ||
'Calc-based timeline before the endScrollOffset point'); | ||
}, 'currentTime handles endScrollOffset correctly'); | ||
|
||
test(function() { | ||
const scroller = setupScrollTimelineTest(); | ||
// Set the timeRange such that currentTime maps directly to the value | ||
// scrolled. The contents and scroller are square, so it suffices to compute | ||
// one edge and use it for all the timelines; | ||
const scrollerSize = scroller.scrollHeight - scroller.clientHeight; | ||
|
||
const scrollTimeline = new ScrollTimeline({ | ||
scrollSource: scroller, | ||
timeRange: scrollerSize, | ||
orientation: 'block', | ||
startScrollOffset: '20px', | ||
endScrollOffset: (scrollerSize - 50) + 'px' | ||
}); | ||
|
||
scroller.scrollTop = 150; | ||
assert_equals( | ||
scrollTimeline.currentTime, | ||
calculateCurrentTime(150, 20, scrollerSize - 50, scrollerSize)); | ||
}, 'currentTime handles startScrollOffset and endScrollOffset together correctly'); | ||
|
||
test(function() { | ||
const scroller = setupScrollTimelineTest(); | ||
// Set the timeRange such that currentTime maps directly to the value | ||
// scrolled. The contents and scroller are square, so it suffices to compute | ||
// one edge and use it for all the timelines; | ||
const scrollerSize = scroller.scrollHeight - scroller.clientHeight; | ||
|
||
const scrollTimeline = new ScrollTimeline({ | ||
scrollSource: scroller, | ||
timeRange: scrollerSize, | ||
orientation: 'block', | ||
startScrollOffset: '20px', | ||
endScrollOffset: '20px', | ||
}); | ||
|
||
scroller.scrollTop = 150; | ||
assert_equals(scrollTimeline.currentTime, null); | ||
}, 'currentTime handles startScrollOffset == endScrollOffset correctly'); | ||
|
||
test(function() { | ||
const scroller = setupScrollTimelineTest(); | ||
// Set the timeRange such that currentTime maps directly to the value | ||
// scrolled. The contents and scroller are square, so it suffices to compute | ||
// one edge and use it for all the timelines; | ||
const scrollerSize = scroller.scrollHeight - scroller.clientHeight; | ||
|
||
const scrollTimeline = new ScrollTimeline({ | ||
scrollSource: scroller, | ||
timeRange: scrollerSize, | ||
orientation: 'block', | ||
startScrollOffset: '50px', | ||
endScrollOffset: '10px', | ||
}); | ||
|
||
scroller.scrollTop = 150; | ||
assert_equals(scrollTimeline.currentTime, null); | ||
}, 'currentTime handles startScrollOffset > endScrollOffset correctly'); | ||
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
'use strict'; | ||
|
||
// Builds a generic structure that looks like: | ||
// | ||
// <div class="scroller"> // 100x100 viewport | ||
// <div class="contents"></div> // 500x500 | ||
// </div> | ||
// | ||
// The |scrollerOverrides| and |contentOverrides| parameters are maps which | ||
// are applied to the scroller and contents style after basic setup. | ||
// | ||
// Appends the outer 'scroller' element to the document body, and returns it. | ||
function setupScrollTimelineTest( | ||
scrollerOverrides = new Map(), contentOverrides = new Map()) { | ||
let scroller = document.createElement('div'); | ||
scroller.style.width = '100px'; | ||
scroller.style.height = '100px'; | ||
scroller.style.overflow = 'scroll'; | ||
for (const [key, value] of scrollerOverrides) { | ||
scroller.style[key] = value; | ||
} | ||
|
||
let contents = document.createElement('div'); | ||
contents.style.width = '500px'; | ||
contents.style.height = '500px'; | ||
for (const [key, value] of contentOverrides) { | ||
contents.style[key] = value; | ||
} | ||
|
||
scroller.appendChild(contents); | ||
document.body.appendChild(scroller); | ||
return scroller; | ||
} | ||
|
||
// Helper method to calculate the current time, implementing only step 5 of | ||
// https://wicg.github.io/scroll-animations/#current-time-algorithm | ||
function calculateCurrentTime( | ||
currentScrollOffset, startScrollOffset, endScrollOffset, | ||
effectiveTimeRange) { | ||
return ((currentScrollOffset - startScrollOffset) / | ||
(endScrollOffset - startScrollOffset)) * | ||
effectiveTimeRange; | ||
} | ||
|