1 // A SmoothScrollInterruptionTest verifies that in-progress smooth scrolls
2 // stop when interrupted by an instant scroll, another smooth scroll, a
3 // touch scroll, or a mouse wheel scroll.
5 // The only SmoothScrollInerruptionTest method that should be called by
6 // outside code is run().
8 // Creates a SmoothScrollInterruptionTest with arguments:
9 // scrollElement - Element being scrolled.
10 // innerPoint - Absolute position (expressed as a dictionary with x and y fields)
11 // of a point inside |scrollElement|, that can be used as the location
12 // of input events that trigger scrolls on |scrollElement|.
13 // targets - A dictionary whose members y_min, y_mid, and y_max should be
14 // y co-ordinates that are far enough apart from each other that a
15 // smooth scroll between any pair of them will be non-trivial (that
16 // is, take multiple frames to finish), and should be such that
17 // y_min < y_mid < y_max.
18 // jsScroll - Callback that takes a y co-ordinate and executes a js-driven
19 // smooth scroll to that y co-ordinate.
20 function SmoothScrollInterruptionTest(scrollElement, innerPoint, targets, jsScroll) {
21 this.scrollElement = scrollElement;
22 this.innerPoint = innerPoint;
23 this.scrollStartPoint = targets.y_mid;
24 this.scrollEndPoint = targets.y_max;
25 this.scrollNewEndpoint = targets.y_min;
26 this.jsScroll = jsScroll;
29 this.testCases.push(new SmoothScrollInterruptionTestCase(interruptWithInstantScroll, verifyScrollInterruptedByInstantScroll, "instant scroll"));
30 this.testCases.push(new SmoothScrollInterruptionTestCase(interruptWithSmoothScroll, verifyScrollInterruptedBySmoothScroll, "smooth scroll"));
31 this.testCases.push(new SmoothScrollInterruptionTestCase(interruptWithTouchScroll, verifyScrollInterruptedByInputDrivenScroll, "touch scroll"));
32 this.testCases.push(new SmoothScrollInterruptionTestCase(interruptWithWheelScroll, verifyScrollInterruptedByInputDrivenScroll, "wheel scroll"));
34 this.currentTestCase = 0;
37 SmoothScrollInterruptionTest.prototype.startNextTestCase = function() {
38 if (this.currentTestCase >= this.testCases.length) {
39 this.allTestCasesComplete();
43 var testCase = this.testCases[this.currentTestCase];
44 this.asyncTest = async_test(testCase.description);
46 var scrollElement = this.scrollElement;
47 var scrollStartPoint = this.scrollStartPoint;
49 scrollElement.scrollTop = scrollStartPoint;
50 window.requestAnimationFrame(this.performSmoothScroll.bind(this));
53 SmoothScrollInterruptionTest.prototype.performSmoothScroll = function() {
54 var testCase = this.testCases[this.currentTestCase];
55 var scrollElement = this.scrollElement;
56 var scrollStartPoint = this.scrollStartPoint;
58 this.jsScroll(this.scrollEndPoint);
59 this.asyncTest.step(function() {
60 assert_equals(scrollElement.scrollTop, scrollStartPoint);
63 if (scrollElement.scrollTop == this.scrollEndPoint) {
64 // We've instant-scrolled, and failed the assert above.
65 this.testCaseComplete();
69 window.requestAnimationFrame(this.waitForSmoothScrollStart.bind(this));
72 SmoothScrollInterruptionTest.prototype.waitForSmoothScrollStart = function() {
73 if (this.scrollElement.scrollTop == this.scrollStartPoint) {
74 window.requestAnimationFrame(this.waitForSmoothScrollStart.bind(this));
78 var testCase = this.testCases[this.currentTestCase];
79 testCase.interruptSmoothScroll(this);
80 window.requestAnimationFrame(testCase.verifyScrollInterrupted.bind(testCase, this, this.testCaseComplete.bind(this)));
83 SmoothScrollInterruptionTest.prototype.testCaseComplete = function() {
84 this.asyncTest.done();
86 this.currentTestCase++;
87 this.startNextTestCase();
90 SmoothScrollInterruptionTest.prototype.run = function() {
91 setup({explicit_done: true, explicit_timeout: true});
92 this.startNextTestCase();
95 SmoothScrollInterruptionTest.prototype.allTestCasesComplete = function() {
99 // A SmoothScrollInterruptionTestCase represents a single way of interrupting
100 // a smooth scroll and verifying that the smooth scroll gets canceled.
102 // Creates a SmoothScrollInterruptionTestCase with arguments:
103 // interruptSmoothScoll - Callback that takes a SmoothScrollInterruptionTest,
104 // and interrupts the on-going smooth scroll.
105 // verifyScrollInterrupted - Callback that takes a SmoothScrollInterruptionTest,
106 // a |verificationComplete| callback, and a timestamp,
107 // verifies (possibly asynchronously) that the smooth
108 // scroll has been superseded by the interruption, and
109 // then calls |verificationComplete|.
110 // description - String describing this test case.
111 function SmoothScrollInterruptionTestCase(interruptSmoothScroll, verifyScrollInterrupted, description) {
112 this.interruptSmoothScroll = interruptSmoothScroll;
113 this.verifyScrollInterrupted = verifyScrollInterrupted;
114 this.description = description;
118 function interruptWithInstantScroll(smoothScrollTest) {
119 smoothScrollTest.scrollElement.scrollTop = smoothScrollTest.scrollNewEndpoint;
120 smoothScrollTest.asyncTest.step(function() {
121 assert_equals(smoothScrollTest.scrollElement.scrollTop, smoothScrollTest.scrollNewEndpoint);
125 function verifyScrollInterruptedByInstantScroll(smoothScrollTest, verificationComplete) {
126 smoothScrollTest.asyncTest.step(function() {
127 assert_equals(smoothScrollTest.scrollElement.scrollTop, smoothScrollTest.scrollNewEndpoint);
129 verificationComplete();
132 function interruptWithSmoothScroll(smoothScrollTest) {
133 smoothScrollTest.jsScroll(smoothScrollTest.scrollNewEndpoint);
134 smoothScrollTest.asyncTest.step(function() {
135 assert_not_equals(smoothScrollTest.scrollElement.scrollTop, smoothScrollTest.scrollNewEndpoint);
138 this.scrollInterruptionPoint = smoothScrollTest.scrollElement.scrollTop;
141 function verifyScrollInterruptedBySmoothScroll(smoothScrollTest, verificationComplete) {
142 var currentPosition = smoothScrollTest.scrollElement.scrollTop;
144 if (currentPosition < this.scrollInterruptionPoint && currentPosition >= smoothScrollTest.scrollNewEndpoint) {
145 verificationComplete();
147 window.requestAnimationFrame(this.verifyScrollInterrupted.bind(this, smoothScrollTest, verificationComplete));
151 function interruptWithTouchScroll(smoothScrollTest) {
152 if (window.eventSender) {
153 window.eventSender.gestureScrollBegin(smoothScrollTest.innerPoint.x, smoothScrollTest.innerPoint.y);
154 window.eventSender.gestureScrollUpdate(0, -10);
155 window.eventSender.gestureScrollEnd(0, 0);
157 document.write("This test does not work in manual mode.");
161 function verifyScrollInterruptedByInputDrivenScroll(smoothScrollTest, verificationComplete, timestamp) {
162 var currentPosition = smoothScrollTest.scrollElement.scrollTop;
164 if (this.previousPosition && this.previousPosition == currentPosition) {
165 // Ensure that the animation has really stopped, not that we just have
166 // two frames that are so close together that the animation only seems to
168 if (timestamp - this.previousTimestamp > 16) {
169 verificationComplete();
171 window.requestAnimationFrame(this.verifyScrollInterrupted.bind(this, smoothScrollTest, verificationComplete));
177 this.previousPosition = currentPosition;
178 this.previousTimestamp = timestamp;
179 smoothScrollTest.asyncTest.step(function() {
180 assert_not_equals(currentPosition, smoothScrollTest.scrollEndPoint);
182 window.requestAnimationFrame(this.verifyScrollInterrupted.bind(this, smoothScrollTest, verificationComplete));
185 function interruptWithWheelScroll(smoothScrollTest) {
186 if (window.eventSender) {
187 window.eventSender.mouseMoveTo(smoothScrollTest.innerPoint.x, smoothScrollTest.innerPoint.y);
188 window.eventSender.mouseScrollBy(0, -10);
190 document.write("This test does not work in manual mode.");