OLD | NEW |
(Empty) | |
| 1 <!DOCTYPE html> |
| 2 <meta charset=utf-8> |
| 3 <title>Tests for updating the finished state of an animation</title> |
| 4 <link rel="help" href="https://w3c.github.io/web-animations/#updating-the-finish
ed-state"> |
| 5 <script src="/resources/testharness.js"></script> |
| 6 <script src="/resources/testharnessreport.js"></script> |
| 7 <script src="../../testcommon.js"></script> |
| 8 <body> |
| 9 <div id="log"></div> |
| 10 <script> |
| 11 'use strict'; |
| 12 |
| 13 // |
| 14 // NOTE TO THE POOR PERSON WHO HAS TO MERGE THIS WITH THE TEST OF THE SAME |
| 15 // NAME FROM BLINK |
| 16 // |
| 17 // There is a pull request from Blink at: |
| 18 // |
| 19 // https://github.com/w3c/web-platform-tests/pull/3328 |
| 20 // |
| 21 // which this file will surely conflict with. |
| 22 // |
| 23 // However, those tests cover a different part of the same algorithm. They |
| 24 // are mostly concerned with testing events and promises rather than the |
| 25 // timing part of the algorithm. |
| 26 // |
| 27 // The tests below cover the first part of the algorithm. So, please keep both |
| 28 // sets of tests and delete this comment. Preferably put the tests in this |
| 29 // file first. |
| 30 // |
| 31 // Thank you! |
| 32 // |
| 33 |
| 34 |
| 35 // CASE 1: playback rate > 0 and current time >= target effect end |
| 36 // (Also the start time is resolved and there is pending task) |
| 37 |
| 38 // Did seek = false |
| 39 promise_test(function(t) { |
| 40 var anim = createDiv(t).animate(null, 100 * MS_PER_SEC); |
| 41 |
| 42 // Here and in the following tests we wait until ready resolves as |
| 43 // otherwise we don't have a resolved start time. We test the case |
| 44 // where the start time is unresolved in a subsequent test. |
| 45 return anim.ready.then(function() { |
| 46 // Seek to 1ms before the target end and wait a frame (> 16ms) |
| 47 anim.currentTime = 100 * MS_PER_SEC - 1; |
| 48 return waitForAnimationFrames(1); |
| 49 }).then(function() { |
| 50 assert_equals(anim.currentTime, 100 * MS_PER_SEC, |
| 51 'Hold time is set to target end clamping current time'); |
| 52 }); |
| 53 }, 'Updating the finished state when playing past end'); |
| 54 |
| 55 // Did seek = true |
| 56 promise_test(function(t) { |
| 57 var anim = createDiv(t).animate(null, 100 * MS_PER_SEC); |
| 58 return anim.ready.then(function() { |
| 59 anim.currentTime = 200 * MS_PER_SEC; |
| 60 return waitForAnimationFrames(1); |
| 61 }).then(function() { |
| 62 assert_equals(anim.currentTime, 200 * MS_PER_SEC, |
| 63 'Hold time is set so current time should NOT change'); |
| 64 }); |
| 65 }, 'Updating the finished state when seeking past end'); |
| 66 |
| 67 // Test current time == target end |
| 68 // |
| 69 // We can't really write a test for current time == target end with |
| 70 // did seek = false since that would imply setting up an animation where |
| 71 // the next animation frame time happens to exactly align with the target end. |
| 72 // |
| 73 // Fortunately, we don't need to test that case since even if the implementation |
| 74 // fails to set the hold time on such a tick, it should be mostly unobservable |
| 75 // (on the subsequent tick the hold time will be set to the same value anyway). |
| 76 |
| 77 // Did seek = true |
| 78 promise_test(function(t) { |
| 79 var anim = createDiv(t).animate(null, 100 * MS_PER_SEC); |
| 80 return anim.ready.then(function() { |
| 81 anim.currentTime = 100 * MS_PER_SEC; |
| 82 return waitForAnimationFrames(1); |
| 83 }).then(function() { |
| 84 assert_equals(anim.currentTime, 100 * MS_PER_SEC, |
| 85 'Hold time is set so current time should NOT change'); |
| 86 }); |
| 87 }, 'Updating the finished state when seeking exactly to end'); |
| 88 |
| 89 |
| 90 // CASE 2: playback rate < 0 and current time <= 0 |
| 91 // (Also the start time is resolved and there is pending task) |
| 92 |
| 93 // Did seek = false |
| 94 promise_test(function(t) { |
| 95 var anim = createDiv(t).animate(null, 100 * MS_PER_SEC); |
| 96 anim.playbackRate = -1; |
| 97 anim.play(); // Make sure animation is not initially finished |
| 98 return anim.ready.then(function() { |
| 99 // Seek to 1ms before 0 end and wait a frame (> 16ms) |
| 100 anim.currentTime = 1; |
| 101 return waitForAnimationFrames(1); |
| 102 }).then(function() { |
| 103 assert_equals(anim.currentTime, 0 * MS_PER_SEC, |
| 104 'Hold time is set to zero clamping current time'); |
| 105 }); |
| 106 }, 'Updating the finished state when playing in reverse past zero'); |
| 107 |
| 108 // Did seek = true |
| 109 promise_test(function(t) { |
| 110 var anim = createDiv(t).animate(null, 100 * MS_PER_SEC); |
| 111 anim.playbackRate = -1; |
| 112 anim.play(); |
| 113 return anim.ready.then(function() { |
| 114 anim.currentTime = -100 * MS_PER_SEC; |
| 115 return waitForAnimationFrames(1); |
| 116 }).then(function() { |
| 117 assert_equals(anim.currentTime, -100 * MS_PER_SEC, |
| 118 'Hold time is set so current time should NOT change'); |
| 119 }); |
| 120 }, 'Updating the finished state when seeking a reversed animation past zero'); |
| 121 |
| 122 // As before, it's difficult to test current time == 0 for did seek = false but |
| 123 // it doesn't really matter. |
| 124 |
| 125 // Did seek = true |
| 126 promise_test(function(t) { |
| 127 var anim = createDiv(t).animate(null, 100 * MS_PER_SEC); |
| 128 anim.playbackRate = -1; |
| 129 anim.play(); |
| 130 return anim.ready.then(function() { |
| 131 anim.currentTime = 0; |
| 132 return waitForAnimationFrames(1); |
| 133 }).then(function() { |
| 134 assert_equals(anim.currentTime, 0 * MS_PER_SEC, |
| 135 'Hold time is set so current time should NOT change'); |
| 136 }); |
| 137 }, 'Updating the finished state when seeking a reversed animation exactly' |
| 138 + ' to zero'); |
| 139 |
| 140 // CASE 3: playback rate > 0 and current time < target end OR |
| 141 // playback rate < 0 and current time > 0 |
| 142 // (Also the start time is resolved and there is pending task) |
| 143 |
| 144 // Did seek = false; playback rate > 0 |
| 145 promise_test(function(t) { |
| 146 var anim = createDiv(t).animate(null, 100 * MS_PER_SEC); |
| 147 |
| 148 // We want to test that the hold time is cleared so first we need to |
| 149 // put the animation in a state where the hold time is set. |
| 150 anim.finish(); |
| 151 return anim.ready.then(function() { |
| 152 assert_equals(anim.currentTime, 100 * MS_PER_SEC, |
| 153 'Hold time is initially set'); |
| 154 // Then extend the duration so that the hold time is cleared and on |
| 155 // the next tick the current time will increase. |
| 156 anim.effect.timing.duration *= 2; |
| 157 return waitForAnimationFrames(1); |
| 158 }).then(function() { |
| 159 assert_greater_than(anim.currentTime, 100 * MS_PER_SEC, |
| 160 'Hold time is not set so current time should increase'); |
| 161 }); |
| 162 }, 'Updating the finished state when playing before end'); |
| 163 |
| 164 // Did seek = true; playback rate > 0 |
| 165 promise_test(function(t) { |
| 166 var anim = createDiv(t).animate(null, 100 * MS_PER_SEC); |
| 167 anim.finish(); |
| 168 return anim.ready.then(function() { |
| 169 anim.currentTime = 50 * MS_PER_SEC; |
| 170 // When did seek = true, updating the finished state: (i) updates |
| 171 // the animation's start time and (ii) clears the hold time. |
| 172 // We can test both by checking that the currentTime is initially |
| 173 // updated and then increases. |
| 174 assert_equals(anim.currentTime, 50 * MS_PER_SEC, 'Start time is updated'); |
| 175 return waitForAnimationFrames(1); |
| 176 }).then(function() { |
| 177 assert_greater_than(anim.currentTime, 50 * MS_PER_SEC, |
| 178 'Hold time is not set so current time should increase'); |
| 179 }); |
| 180 }, 'Updating the finished state when seeking before end'); |
| 181 |
| 182 // Did seek = false; playback rate < 0 |
| 183 // |
| 184 // Unfortunately it is not possible to test this case. We need to have |
| 185 // a hold time set, a resolved start time, and then perform some |
| 186 // operation that updates the finished state with did seek set to true. |
| 187 // |
| 188 // However, the only situation where this could arrive is when we |
| 189 // replace the timeline and that procedure is likely to change. For all |
| 190 // other cases we either have an unresolved start time (e.g. when |
| 191 // paused), we don't have a set hold time (e.g. regular playback), or |
| 192 // the current time is zero (and anything that gets us out of that state |
| 193 // will set did seek = true). |
| 194 |
| 195 // Did seek = true; playback rate < 0 |
| 196 promise_test(function(t) { |
| 197 var anim = createDiv(t).animate(null, 100 * MS_PER_SEC); |
| 198 anim.playbackRate = -1; |
| 199 return anim.ready.then(function() { |
| 200 anim.currentTime = 50 * MS_PER_SEC; |
| 201 assert_equals(anim.currentTime, 50 * MS_PER_SEC, 'Start time is updated'); |
| 202 return waitForAnimationFrames(1); |
| 203 }).then(function() { |
| 204 assert_less_than(anim.currentTime, 50 * MS_PER_SEC, |
| 205 'Hold time is not set so current time should decrease'); |
| 206 }); |
| 207 }, 'Updating the finished state when seeking a reversed animation before end'); |
| 208 |
| 209 // CASE 4: playback rate == 0 |
| 210 |
| 211 // current time < 0 |
| 212 promise_test(function(t) { |
| 213 var anim = createDiv(t).animate(null, 100 * MS_PER_SEC); |
| 214 anim.playbackRate = 0; |
| 215 return anim.ready.then(function() { |
| 216 anim.currentTime = -100 * MS_PER_SEC; |
| 217 return waitForAnimationFrames(1); |
| 218 }).then(function() { |
| 219 assert_equals(anim.currentTime, -100 * MS_PER_SEC, |
| 220 'Hold time should not be cleared so current time should' |
| 221 + ' NOT change'); |
| 222 }); |
| 223 }, 'Updating the finished state when playback rate is zero and the' |
| 224 + ' current time is less than zero'); |
| 225 |
| 226 // current time < target end |
| 227 promise_test(function(t) { |
| 228 var anim = createDiv(t).animate(null, 100 * MS_PER_SEC); |
| 229 anim.playbackRate = 0; |
| 230 return anim.ready.then(function() { |
| 231 anim.currentTime = 50 * MS_PER_SEC; |
| 232 return waitForAnimationFrames(1); |
| 233 }).then(function() { |
| 234 assert_equals(anim.currentTime, 50 * MS_PER_SEC, |
| 235 'Hold time should not be cleared so current time should' |
| 236 + ' NOT change'); |
| 237 }); |
| 238 }, 'Updating the finished state when playback rate is zero and the' |
| 239 + ' current time is less than end'); |
| 240 |
| 241 // current time > target end |
| 242 promise_test(function(t) { |
| 243 var anim = createDiv(t).animate(null, 100 * MS_PER_SEC); |
| 244 anim.playbackRate = 0; |
| 245 return anim.ready.then(function() { |
| 246 anim.currentTime = 200 * MS_PER_SEC; |
| 247 return waitForAnimationFrames(1); |
| 248 }).then(function() { |
| 249 assert_equals(anim.currentTime, 200 * MS_PER_SEC, |
| 250 'Hold time should not be cleared so current time should' |
| 251 + ' NOT change'); |
| 252 }); |
| 253 }, 'Updating the finished state when playback rate is zero and the' |
| 254 + ' current time is greater than end'); |
| 255 |
| 256 // CASE 5: current time unresolved |
| 257 |
| 258 promise_test(function(t) { |
| 259 var anim = createDiv(t).animate(null, 100 * MS_PER_SEC); |
| 260 anim.cancel(); |
| 261 // Trigger a change that will cause the "update the finished state" |
| 262 // procedure to run. |
| 263 anim.effect.timing.duration = 200 * MS_PER_SEC; |
| 264 assert_equals(anim.currentTime, null, |
| 265 'The animation hold time / start time should not be updated'); |
| 266 // The "update the finished state" procedure is supposed to run after any |
| 267 // change to timing, but just in case an implementation defers that, let's |
| 268 // wait a frame and check that the hold time / start time has still not been |
| 269 // updated. |
| 270 return waitForAnimationFrames(1).then(function() { |
| 271 assert_equals(anim.currentTime, null, |
| 272 'The animation hold time / start time should not be updated'); |
| 273 }); |
| 274 }, 'Updating the finished state when current time is unresolved'); |
| 275 |
| 276 // CASE 6: has a pending task |
| 277 |
| 278 test(function(t) { |
| 279 var anim = createDiv(t).animate(null, 100 * MS_PER_SEC); |
| 280 anim.cancel(); |
| 281 anim.currentTime = 75 * MS_PER_SEC; |
| 282 anim.play(); |
| 283 // We now have a pending task and a resolved current time. |
| 284 // |
| 285 // In the next step we will adjust the timing so that the current time |
| 286 // is greater than the target end. At this point the "update the finished |
| 287 // state" procedure should run and if we fail to check for a pending task |
| 288 // we will set the hold time to the target end, i.e. 50ms. |
| 289 anim.effect.timing.duration = 50 * MS_PER_SEC; |
| 290 assert_equals(anim.currentTime, 75 * MS_PER_SEC, |
| 291 'Hold time should not be updated'); |
| 292 }, 'Updating the finished state when there is a pending task'); |
| 293 |
| 294 // CASE 7: start time unresolved |
| 295 |
| 296 // Did seek = false |
| 297 promise_test(function(t) { |
| 298 var anim = createDiv(t).animate(null, 100 * MS_PER_SEC); |
| 299 anim.cancel(); |
| 300 // Make it so that only the start time is unresolved (to avoid overlapping |
| 301 // with the test case where current time is unresolved) |
| 302 anim.currentTime = 150 * MS_PER_SEC; |
| 303 // Trigger a change that will cause the "update the finished state" |
| 304 // procedure to run (did seek = false). |
| 305 anim.effect.timing.duration = 200 * MS_PER_SEC; |
| 306 return waitForAnimationFrames(1).then(function() { |
| 307 assert_equals(anim.currentTime, 150 * MS_PER_SEC, |
| 308 'The animation hold time should not be updated'); |
| 309 assert_equals(anim.startTime, null, |
| 310 'The animation start time should not be updated'); |
| 311 }); |
| 312 }, 'Updating the finished state when start time is unresolved and' |
| 313 + ' did seek = false'); |
| 314 |
| 315 // Did seek = true |
| 316 test(function(t) { |
| 317 var anim = createDiv(t).animate(null, 100 * MS_PER_SEC); |
| 318 anim.cancel(); |
| 319 anim.currentTime = 150 * MS_PER_SEC; |
| 320 // Trigger a change that will cause the "update the finished state" |
| 321 // procedure to run. |
| 322 anim.currentTime = 50 * MS_PER_SEC; |
| 323 assert_equals(anim.currentTime, 50 * MS_PER_SEC, |
| 324 'The animation hold time should not be updated'); |
| 325 assert_equals(anim.startTime, null, |
| 326 'The animation start time should not be updated'); |
| 327 }, 'Updating the finished state when start time is unresolved and' |
| 328 + ' did seek = true'); |
| 329 |
| 330 </script> |
| 331 </body> |
OLD | NEW |