REGRESSION: (r255821) [ iOS Mac wk1 ] imported/w3c/web-platform-tests/web-animations...
[WebKit-https.git] / LayoutTests / imported / w3c / web-platform-tests / web-animations / timing-model / animations / finishing-an-animation.html
1 <!DOCTYPE html>
2 <meta charset=utf-8>
3 <title>Finishing an animation</title>
4 <link rel="help"
5   href="https://drafts.csswg.org/web-animations/#finishing-an-animation-section">
6 <script src="/resources/testharness.js"></script>
7 <script src="/resources/testharnessreport.js"></script>
8 <script src="../../testcommon.js"></script>
9 <script src="../../resources/timing-override.js"></script>
10 <body>
11 <div id="log"></div>
12 <script>
13 'use strict';
14
15 test(t => {
16   const div = createDiv(t);
17   const animation = div.animate(null, 100 * MS_PER_SEC);
18   animation.playbackRate = 0;
19
20   assert_throws_dom('InvalidStateError', () => {
21     animation.finish();
22   });
23 }, 'Finishing an animation with a zero playback rate throws');
24
25 test(t => {
26   const div = createDiv(t);
27   const animation = div.animate(null,
28                                 { duration : 100 * MS_PER_SEC,
29                                   iterations : Infinity });
30
31   assert_throws_dom('InvalidStateError', () => {
32     animation.finish();
33   });
34 }, 'Finishing an infinite animation throws');
35
36 test(t => {
37   const div = createDiv(t);
38   const animation = div.animate(null, 100 * MS_PER_SEC);
39   animation.finish();
40
41   assert_time_equals_literal(animation.currentTime, 100 * MS_PER_SEC,
42     'After finishing, the currentTime should be set to the end of the'
43     + ' active duration');
44 }, 'Finishing an animation seeks to the end time');
45
46 test(t => {
47   const div = createDiv(t);
48   const animation = div.animate(null, 100 * MS_PER_SEC);
49    // 1s past effect end
50   animation.currentTime =
51     animation.effect.getComputedTiming().endTime + 1 * MS_PER_SEC;
52   animation.finish();
53
54   assert_time_equals_literal(animation.currentTime, 100 * MS_PER_SEC,
55     'After finishing, the currentTime should be set back to the end of the'
56     + ' active duration');
57 }, 'Finishing an animation with a current time past the effect end jumps'
58    + ' back to the end');
59
60 promise_test(async t => {
61   const div = createDiv(t);
62   const animation = div.animate(null, 100 * MS_PER_SEC);
63   animation.currentTime = 100 * MS_PER_SEC;
64   await animation.finished;
65
66   animation.playbackRate = -1;
67   animation.finish();
68
69   assert_equals(animation.currentTime, 0,
70                 'After finishing a reversed animation the currentTime ' +
71                 'should be set to zero');
72 }, 'Finishing a reversed animation jumps to zero time');
73
74 promise_test(async t => {
75   const div = createDiv(t);
76   const animation = div.animate(null, 100 * MS_PER_SEC);
77   animation.currentTime = 100 * MS_PER_SEC;
78   await animation.finished;
79
80   animation.playbackRate = -1;
81   animation.currentTime = -1000;
82   animation.finish();
83
84   assert_equals(animation.currentTime, 0,
85                 'After finishing a reversed animation the currentTime ' +
86                 'should be set back to zero');
87 }, 'Finishing a reversed animation with a current time less than zero'
88    + ' makes it jump back to zero');
89
90 promise_test(async t => {
91   const div = createDiv(t);
92   const animation = div.animate(null, 100 * MS_PER_SEC);
93   animation.pause();
94   await animation.ready;
95
96   animation.finish();
97
98   assert_equals(animation.playState, 'finished',
99                 'The play state of a paused animation should become ' +
100                 '"finished"');
101   assert_times_equal(animation.startTime,
102                      animation.timeline.currentTime - 100 * MS_PER_SEC,
103                      'The start time of a paused animation should be set');
104 }, 'Finishing a paused animation resolves the start time');
105
106 test(t => {
107   const div = createDiv(t);
108   const animation = div.animate(null, 100 * MS_PER_SEC);
109   // Update playbackRate so we can test that the calculated startTime
110   // respects it
111   animation.playbackRate = 2;
112   animation.pause();
113   // While animation is still pause-pending call finish()
114   animation.finish();
115
116   assert_false(animation.pending);
117   assert_equals(animation.playState, 'finished',
118                 'The play state of a pause-pending animation should become ' +
119                 '"finished"');
120   assert_times_equal(animation.startTime,
121                      animation.timeline.currentTime - 100 * MS_PER_SEC / 2,
122                      'The start time of a pause-pending animation should ' +
123                      'be set');
124 }, 'Finishing a pause-pending animation resolves the pending task'
125    + ' immediately and update the start time');
126
127 test(t => {
128   const div = createDiv(t);
129   const animation = div.animate(null, 100 * MS_PER_SEC);
130   animation.playbackRate = -2;
131   animation.pause();
132   animation.finish();
133
134   assert_false(animation.pending);
135   assert_equals(animation.playState, 'finished',
136                 'The play state of a pause-pending animation should become ' +
137                 '"finished"');
138   assert_times_equal(animation.startTime, animation.timeline.currentTime,
139                      'The start time of a pause-pending animation should be ' +
140                      'set');
141 }, 'Finishing a pause-pending animation with negative playback rate'
142    + ' resolves the pending task immediately');
143
144 test(t => {
145   const div = createDiv(t);
146   const animation = div.animate(null, 100 * MS_PER_SEC);
147   animation.playbackRate = 0.5;
148   animation.finish();
149
150   assert_false(animation.pending);
151   assert_equals(animation.playState, 'finished',
152                 'The play state of a play-pending animation should become ' +
153                 '"finished"');
154   assert_times_equal(animation.startTime,
155                      animation.timeline.currentTime - 100 * MS_PER_SEC / 0.5,
156                      'The start time of a play-pending animation should ' +
157                      'be set');
158 }, 'Finishing an animation while play-pending resolves the pending'
159    + ' task immediately');
160
161 // FIXME: Add a test for when we are play-pending without an active timeline.
162 // - In that case even after calling finish() we should still be pending but
163 //   the current time should be updated
164
165 promise_test(async t => {
166   const div = createDiv(t);
167   const animation = div.animate(null, 100 * MS_PER_SEC);
168   await animation.ready;
169
170   animation.pause();
171   animation.play();
172   // We are now in the unusual situation of being play-pending whilst having
173   // a resolved start time. Check that finish() still triggers a transition
174   // to the finished state immediately.
175   animation.finish();
176
177   assert_equals(animation.playState, 'finished',
178                 'After aborting a pause then finishing an animation its play ' +
179                 'state should become "finished" immediately');
180 }, 'Finishing an animation during an aborted pause makes it finished'
181    + ' immediately');
182
183 promise_test(async t => {
184   const div = createDiv(t);
185   const animation = div.animate(null, 100 * MS_PER_SEC);
186   let resolvedFinished = false;
187   animation.finished.then(() => {
188     resolvedFinished = true;
189   });
190
191   await animation.ready;
192
193   animation.finish();
194   await Promise.resolve();
195
196   assert_true(resolvedFinished, 'finished promise should be resolved');
197 }, 'Finishing an animation resolves the finished promise synchronously');
198
199 promise_test(async t => {
200   const effect = new KeyframeEffect(null, null, 100 * MS_PER_SEC);
201   const animation = new Animation(effect, document.timeline);
202   let resolvedFinished = false;
203   animation.finished.then(() => {
204     resolvedFinished = true;
205   });
206
207   await animation.ready;
208
209   animation.finish();
210   await Promise.resolve();
211
212   assert_true(resolvedFinished, 'finished promise should be resolved');
213 }, 'Finishing an animation without a target resolves the finished promise'
214    + ' synchronously');
215
216 promise_test(async t => {
217   const animation = createDiv(t).animate(null, 100 * MS_PER_SEC);
218   const promise = animation.ready;
219   let readyResolved = false;
220
221   animation.finish();
222   animation.ready.then(() => { readyResolved = true; });
223
224   const promiseResult = await animation.finished;
225
226   assert_equals(promiseResult, animation);
227   assert_equals(animation.ready, promise);
228   assert_true(readyResolved);
229 }, 'A pending ready promise is resolved and not replaced when the animation'
230    + ' is finished');
231
232 promise_test(async t => {
233   const animation = createDiv(t).animate(null, 100 * MS_PER_SEC);
234   await animation.ready;
235
236   animation.updatePlaybackRate(2);
237   assert_true(animation.pending);
238
239   animation.finish();
240   assert_false(animation.pending);
241   assert_equals(animation.playbackRate, 2);
242   assert_time_equals_literal(animation.currentTime, 100 * MS_PER_SEC);
243 }, 'A pending playback rate should be applied immediately when an animation'
244    + ' is finished');
245
246 promise_test(async t => {
247   const animation = createDiv(t).animate(null, 100 * MS_PER_SEC);
248   await animation.ready;
249
250   animation.updatePlaybackRate(0);
251
252   assert_throws_dom('InvalidStateError', () => {
253     animation.finish();
254   });
255 }, 'An exception should be thrown if the effective playback rate is zero');
256
257 promise_test(async t => {
258   const animation = createDiv(t).animate(null, {
259     duration: 100 * MS_PER_SEC,
260     iterations: Infinity
261   });
262   animation.currentTime = 50 * MS_PER_SEC;
263   animation.playbackRate = -1;
264   await animation.ready;
265
266   animation.updatePlaybackRate(1);
267
268   assert_throws_dom('InvalidStateError', () => {
269     animation.finish();
270   });
271 }, 'An exception should be thrown when finishing if the effective playback rate'
272    + ' is positive and the target effect end is infinity');
273
274 promise_test(async t => {
275   const animation = createDiv(t).animate(null, {
276     duration: 100 * MS_PER_SEC,
277     iterations: Infinity
278   });
279   await animation.ready;
280
281   animation.updatePlaybackRate(-1);
282
283   animation.finish();
284   // Should not have thrown
285 }, 'An exception is NOT thrown when finishing if the effective playback rate'
286    + ' is negative and the target effect end is infinity');
287
288 promise_test(async t => {
289   const div = createDiv(t);
290   const animation = div.animate({}, 100 * MS_PER_SEC);
291   div.remove();
292
293   const eventWatcher = new EventWatcher(t, animation, 'finish');
294
295   await animation.ready;
296   animation.finish();
297
298   await eventWatcher.wait_for('finish');
299   assert_equals(animation.effect.target.parentNode, null,
300     'finish event should be fired for the animation on an orphaned element');
301 }, 'Finishing an animation fires finish event on orphaned element');
302
303 promise_test(async t => {
304   const animation = createDiv(t).animate(null, 100 * MS_PER_SEC);
305   await animation.ready;
306
307   const originalFinishPromise = animation.finished.catch(() => {});
308
309   animation.cancel();
310   assert_equals(animation.startTime, null);
311   assert_equals(animation.currentTime, null);
312
313   const resolvedFinishPromise = animation.finished;
314   assert_true(originalFinishPromise != resolvedFinishPromise,
315                'Canceling an animation should create a new finished promise');
316
317   animation.finish();
318   assert_equals(animation.playState, 'finished',
319                 'The play state of a canceled animation should become ' +
320                 '"finished"');
321   assert_times_equal(animation.startTime,
322                      animation.timeline.currentTime - 100 * MS_PER_SEC,
323                      'The start time of a finished animation should be set');
324   assert_times_equal(animation.currentTime, 100000,
325                      'Hold time should be set to end boundary of the animation');
326
327 }, 'Finishing a canceled animation sets the current and start times');
328
329 </script>
330 </body>