summaryrefslogtreecommitdiffstats
path: root/dom/animation/test/css-transitions/file_animation-currenttime.html
blob: 2a0f105d20a60667731b22ead7c888b104eeb152 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
<!doctype html>
<html>
  <head>
    <meta charset=utf-8>
    <title>Tests for the effect of setting a CSS transition's
           Animation.currentTime</title>
    <style>

.animated-div {
  margin-left: 100px;
  transition: margin-left 1000s linear 1000s;
}

    </style>
    <script src="../testcommon.js"></script>
  </head>
  <body>
    <script type="text/javascript">

'use strict';

// TODO: Once the computedTiming property is implemented, add checks to the
// checker helpers to ensure that computedTiming's properties are updated as
// expected.
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1108055


const ANIM_DELAY_MS = 1000000; // 1000s
const ANIM_DUR_MS = 1000000; // 1000s

/**
 * These helpers get the value that the currentTime needs to be set to, to put
 * an animation that uses the above ANIM_DELAY_MS and ANIM_DUR_MS values into
 * the middle of various phases or points through the active duration.
 */
function currentTimeForBeforePhase() {
  return ANIM_DELAY_MS / 2;
}
function currentTimeForActivePhase() {
  return ANIM_DELAY_MS + ANIM_DUR_MS / 2;
}
function currentTimeForAfterPhase() {
  return ANIM_DELAY_MS + ANIM_DUR_MS + ANIM_DELAY_MS / 2;
}
function currentTimeForStartOfActiveInterval() {
  return ANIM_DELAY_MS;
}
function currentTimeForFiftyPercentThroughActiveInterval() {
  return ANIM_DELAY_MS + ANIM_DUR_MS * 0.5;
}
function currentTimeForEndOfActiveInterval() {
  return ANIM_DELAY_MS + ANIM_DUR_MS;
}


// Expected computed 'margin-left' values at points during the active interval:
// When we assert_between_inclusive using these values we could in theory cause
// intermittent failure due to very long delays between paints, but since the
// active duration is 1000s long, a delay would need to be around 100s to cause
// that. If that's happening then there are likely other issues that should be
// fixed, so a failure to make us look into that seems like a good thing.
const INITIAL_POSITION = 100;
const TEN_PCT_POSITION = 110;
const FIFTY_PCT_POSITION = 150;
const END_POSITION = 200;


// The terms used for the naming of the following helper functions refer to
// terms used in the Web Animations specification for specific phases of an
// animation. The terms can be found here:
//
//   http://w3c.github.io/web-animations/#animation-effect-phases-and-states

// Called when currentTime is set to zero (the beginning of the start delay).
function checkStateOnSettingCurrentTimeToZero(animation)
{
  // We don't test animation.currentTime since our caller just set it.

  assert_equals(animation.playState, 'running',
    'Animation.playState should be "running" at the start of ' +
    'the start delay');

  assert_equals(animation.effect.target.style.animationPlayState, 'running',
    'Animation.effect.target.style.animationPlayState should be ' +
    '"running" at the start of the start delay');

  var div = animation.effect.target;
  var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
  assert_equals(marginLeft, UNANIMATED_POSITION,
                'the computed value of margin-left should be unaffected ' +
                'at the beginning of the start delay');
}

// Called when the ready Promise's callbacks should happen
function checkStateOnReadyPromiseResolved(animation)
{
  // the 0.0001 here is for rounding error
  assert_less_than_equal(animation.currentTime,
    animation.timeline.currentTime - animation.startTime + 0.0001,
    'Animation.currentTime should be less than the local time ' +
    'equivalent of the timeline\'s currentTime on the first paint tick ' +
    'after animation creation');

  assert_equals(animation.playState, 'running',
    'Animation.playState should be "running" on the first paint ' +
    'tick after animation creation');

  var div = animation.effect.target;
  var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
  assert_equals(marginLeft, INITIAL_POSITION,
                'the computed value of margin-left should be unaffected ' +
                'by an animation with a delay on ready Promise resolve');
}

// Called when currentTime is set to the time the active interval starts.
function checkStateAtActiveIntervalStartTime(animation)
{
  // We don't test animation.currentTime since our caller just set it.

  assert_equals(animation.playState, 'running',
    'Animation.playState should be "running" at the start of ' +
    'the active interval');

  var div = animation.effect.target;
  var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
  assert_between_inclusive(marginLeft, INITIAL_POSITION, TEN_PCT_POSITION,
    'the computed value of margin-left should be close to the value at the ' +
    'beginning of the animation');
}

function checkStateAtFiftyPctOfActiveInterval(animation)
{
  // We don't test animation.currentTime since our caller just set it.

  var div = animation.effect.target;
  var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
  assert_equals(marginLeft, FIFTY_PCT_POSITION,
    'the computed value of margin-left should be half way through the ' +
    'animation at the midpoint of the active interval');
}

// Called when currentTime is set to the time the active interval ends.
function checkStateAtActiveIntervalEndTime(animation)
{
  // We don't test animation.currentTime since our caller just set it.

  assert_equals(animation.playState, 'finished',
    'Animation.playState should be "finished" at the end of ' +
    'the active interval');

  var div = animation.effect.target;
  var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
  assert_equals(marginLeft, END_POSITION,
    'the computed value of margin-left should be the final transitioned-to ' +
    'value at the end of the active duration');
}

test(function(t)
{
  var div = addDiv(t, {'class': 'animated-div'});
  flushComputedStyle(div);
  div.style.marginLeft = '200px'; // initiate transition

  var animation = div.getAnimations()[0];
  assert_equals(animation.currentTime, 0, 'currentTime should be zero');
}, 'currentTime of a newly created transition is zero');


test(function(t)
{
  var div = addDiv(t, {'class': 'animated-div'});
  flushComputedStyle(div);
  div.style.marginLeft = '200px'; // initiate transition

  var animation = div.getAnimations()[0];

  // So that animation is running instead of paused when we set currentTime:
  animation.startTime = animation.timeline.currentTime;

  animation.currentTime = 10;
  assert_equals(animation.currentTime, 10,
    'Check setting of currentTime actually works');
}, 'Sanity test to check round-tripping assigning to new animation\'s ' +
   'currentTime');


async_test(function(t) {
  var div = addDiv(t, {'class': 'animated-div'});
  var eventWatcher = new EventWatcher(t, div, 'transitionend');

  flushComputedStyle(div);
  div.style.marginLeft = '200px'; // initiate transition

  var animation = div.getAnimations()[0];

  animation.ready.then(t.step_func(function() {
    checkStateOnReadyPromiseResolved(animation);

    animation.currentTime = currentTimeForStartOfActiveInterval();
    checkStateAtActiveIntervalStartTime(animation);

    animation.currentTime = currentTimeForFiftyPercentThroughActiveInterval();
    checkStateAtFiftyPctOfActiveInterval(animation);

    animation.currentTime = currentTimeForEndOfActiveInterval();
    return eventWatcher.wait_for('transitionend');
  })).then(t.step_func(function() {
    checkStateAtActiveIntervalEndTime(animation);
  })).catch(t.step_func(function(reason) {
    assert_unreached(reason);
  })).then(function() {
    t.done();
  });
}, 'Skipping forward through transition');


test(function(t) {
  var div = addDiv(t, {'class': 'animated-div'});
  var eventWatcher = new EventWatcher(t, div, 'transitionend');

  flushComputedStyle(div);
  div.style.marginLeft = '200px'; // initiate transition

  var animation = div.getAnimations()[0];

  // Unlike in the case of CSS animations, we cannot skip to the end and skip
  // backwards since when we reach the end the transition effect is removed and
  // changes to the Animation object no longer affect the element. For
  // this reason we only skip forwards as far as the 50% through point.

  animation.ready.then(t.step_func(function() {
    animation.currentTime = currentTimeForFiftyPercentThroughActiveInterval();
    checkStateAtFiftyPctOfActiveInterval(animation);

    animation.currentTime = currentTimeForStartOfActiveInterval();

    // Despite going backwards from being in the active interval to being
    // before it, we now expect a 'transitionend' event because the transition
    // should go from being active to inactive.
    //
    // Calling checkStateAtActiveIntervalStartTime will check computed style,
    // causing computed style to be updated and the 'transitionend' event to
    // be dispatched synchronously. We need to call wait_for first
    // otherwise eventWatcher will assert that the event was unexpected.
    eventWatcher.wait_for('transitionend').then(function() {
      t.done();
    });
    checkStateAtActiveIntervalStartTime(animation);
  }));
}, 'Skipping backwards through transition');


async_test(function(t) {
  var div = addDiv(t, {'class': 'animated-div'});
  flushComputedStyle(div);
  div.style.marginLeft = '200px'; // initiate transition

  var animation = div.getAnimations()[0];

  animation.ready.then(t.step_func(function() {
    var exception;
    try {
      animation.currentTime = null;
    } catch (e) {
      exception = e;
    }
    assert_equals(exception.name, 'TypeError',
      'Expect TypeError exception on trying to set ' +
      'Animation.currentTime to null');
  })).catch(t.step_func(function(reason) {
    assert_unreached(reason);
  })).then(function() {
    t.done();
  });
}, 'Setting currentTime to null');


async_test(function(t) {
  var div = addDiv(t, {'class': 'animated-div'});
  flushComputedStyle(div);
  div.style.marginLeft = '200px'; // initiate transition

  var animation = div.getAnimations()[0];
  var pauseTime;

  animation.ready.then(t.step_func(function() {
    assert_not_equals(animation.currentTime, null,
      'Animation.currentTime not null on ready Promise resolve');
    animation.pause();
    return animation.ready;
  })).then(t.step_func(function() {
    pauseTime = animation.currentTime;
    return waitForFrame();
  })).then(t.step_func(function() {
    assert_equals(animation.currentTime, pauseTime,
      'Animation.currentTime is unchanged after pausing');
  })).catch(t.step_func(function(reason) {
    assert_unreached(reason);
  })).then(function() {
    t.done();
  });
}, 'Animation.currentTime after pausing');

done();
    </script>
  </body>
</html>