dom/animation/test/css-transitions/test_event-dispatch.html
author Brian Birtles <birtles@gmail.com>
Thu, 16 Aug 2018 14:41:02 +0900
changeset 487352 9427f6cff88ff691b812846f819c6e93e1ef1aea
parent 487350 8d053037352a55cb0ff3f6900e43679901c23d6d
child 487353 fbdd599db4490ea7e651ef7f2a05c5f6584f6c0d
permissions -rw-r--r--
Bug 1467344 - Fix the spelling of canceled/canceling in dom/animation/test/css-transitions/; r=hiro

<!doctype html>
<meta charset=utf-8>
<title>Tests for CSS-Transition events</title>
<link rel="help" href="https://drafts.csswg.org/css-transitions-2/#transition-events">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../testcommon.js"></script>
<body>
<div id="log"></div>
<script>
'use strict';

/**
 * Helper class to record the elapsedTime member of each event.
 * The EventWatcher class in testharness.js allows us to wait on
 * multiple events in a certain order but only records the event
 * parameters of the most recent event.
 */
function TransitionEventHandler(target) {
  this.target = target;
  this.target.ontransitionrun = evt => {
    this.transitionrun = evt.elapsedTime;
  };
  this.target.ontransitionstart = evt => {
    this.transitionstart = evt.elapsedTime;
  };
  this.target.ontransitionend = evt => {
    this.transitionend = evt.elapsedTime;
  };
  this.target.ontransitioncancel = evt => {
    this.transitioncancel = evt.elapsedTime;
  };
}

TransitionEventHandler.prototype.clear = () => {
  this.transitionrun    = undefined;
  this.transitionstart  = undefined;
  this.transitionend    = undefined;
  this.transitioncancel = undefined;
};

const setupTransition = (t, transitionStyle) => {
  const div = addDiv(t, { style: 'transition: ' + transitionStyle });
  // Note that this TransitionEventHandler should be created before EventWatcher
  // to capture all events in the handler prior to the EventWatcher since
  // testharness.js proceeds when the EventWatcher received watching events.
  const handler = new TransitionEventHandler(div);
  const watcher = new EventWatcher(t, div, [ 'transitionrun',
                                             'transitionstart',
                                             'transitionend',
                                             'transitioncancel' ]);
  flushComputedStyle(div);

  div.style.marginLeft = '100px';
  const transition = div.getAnimations()[0];

  return { transition, watcher, div, handler };
};

// On the next frame (i.e. when events are queued), whether or not the
// transition is still pending depends on the implementation.
promise_test(async t => {
  const { transition, watcher } =
    setupTransition(t, 'margin-left 100s 100s');
  const evt = await watcher.wait_for('transitionrun');
  assert_equals(evt.elapsedTime, 0.0);
}, 'Idle -> Pending or Before');

promise_test(async t => {
  const { transition, watcher } =
    setupTransition(t, 'margin-left 100s 100s');
  // Force the transition to leave the idle phase
  transition.startTime = document.timeline.currentTime;
  const evt = await watcher.wait_for('transitionrun');
  assert_equals(evt.elapsedTime, 0.0);
}, 'Idle -> Before');

promise_test(async t => {
  const { transition, watcher, div, handler } =
    setupTransition(t, 'margin-left 100s 100s');

  // Seek to Active phase.
  transition.currentTime = 100 * MS_PER_SEC;
  transition.pause();
  await watcher.wait_for([ 'transitionrun', 'transitionstart' ]);
  assert_equals(handler.transitionrun, 0.0);
  assert_equals(handler.transitionstart, 0.0);
}, 'Idle or Pending -> Active');

promise_test(async t => {
  const { transition, watcher, div, handler } =
    setupTransition(t, 'margin-left 100s 100s');

  // Seek to After phase.
  transition.finish();
  await watcher.wait_for([ 'transitionrun',
                           'transitionstart',
                           'transitionend' ]);
  assert_equals(handler.transitionrun, 0.0);
  assert_equals(handler.transitionstart, 0.0);
  assert_equals(handler.transitionend, 100.0);
}, 'Idle or Pending -> After');

promise_test(async t => {
  const { transition, watcher, div } =
    setupTransition(t, 'margin-left 100s 100s');

  await Promise.all([ watcher.wait_for('transitionrun'), transition.ready ]);

  // Make idle
  div.style.display = 'none';
  flushComputedStyle(div);
  const evt = await watcher.wait_for('transitioncancel');
  assert_equals(evt.elapsedTime, 0.0);
}, 'Before -> Idle (display: none)');

promise_test(async t => {
  const { transition, watcher } =
    setupTransition(t, 'margin-left 100s 100s');

  await Promise.all([ watcher.wait_for('transitionrun'), transition.ready ]);

  // Make idle
  transition.timeline = null;
  const evt = await watcher.wait_for('transitioncancel');
  assert_equals(evt.elapsedTime, 0.0);
}, 'Before -> Idle (Animation.timeline = null)');

promise_test(async t => {
  const { transition, watcher } =
    setupTransition(t, 'margin-left 100s 100s');

  await Promise.all([ watcher.wait_for('transitionrun'), transition.ready ]);

  transition.currentTime = 100 * MS_PER_SEC;
  const evt = await watcher.wait_for('transitionstart');
  assert_equals(evt.elapsedTime, 0.0);
}, 'Before -> Active');

promise_test(async t => {
  const { transition, watcher, div, handler } =
    setupTransition(t, 'margin-left 100s 100s');

  await Promise.all([ watcher.wait_for('transitionrun'), transition.ready ]);
  // Seek to After phase.
  transition.currentTime = 200 * MS_PER_SEC;
  await watcher.wait_for([ 'transitionstart', 'transitionend' ]);

  assert_equals(handler.transitionstart, 0.0);
  assert_equals(handler.transitionend, 100.0);
}, 'Before -> After');

promise_test(async t => {
  const { transition, watcher, div } = setupTransition(t, 'margin-left 100s');

  // Seek to Active start position.
  transition.pause();
  await watcher.wait_for([ 'transitionrun', 'transitionstart' ]);

  // Make idle
  div.style.display = 'none';
  flushComputedStyle(div);
  const evt = await watcher.wait_for('transitioncancel');
  assert_equals(evt.elapsedTime, 0.0);
}, 'Active -> Idle, no delay (display: none)');

promise_test(async t => {
  const { transition, watcher } = setupTransition(t, 'margin-left 100s');

  await watcher.wait_for([ 'transitionrun', 'transitionstart' ]);

  // Make idle
  transition.currentTime = 0;
  transition.timeline = null;
  const evt = await watcher.wait_for('transitioncancel');
  assert_equals(evt.elapsedTime, 0.0);
}, 'Active -> Idle, no delay (Animation.timeline = null)');

promise_test(async t => {
  const { transition, watcher, div } =
    setupTransition(t, 'margin-left 100s 100s');
  // Pause so the currentTime is fixed and we can accurately compare the event
  // time in transition cancel events.
  transition.pause();

  // Seek to Active phase.
  transition.currentTime = 100 * MS_PER_SEC;
  await watcher.wait_for([ 'transitionrun', 'transitionstart' ]);

  // Make idle
  div.style.display = 'none';
  flushComputedStyle(div);
  const evt = await watcher.wait_for('transitioncancel');
  assert_equals(evt.elapsedTime, 0.0);
}, 'Active -> Idle, with positive delay (display: none)');

promise_test(async t => {
  const { transition, watcher } = setupTransition(t, 'margin-left 100s 100s');

  // Seek to Active phase.
  transition.currentTime = 100 * MS_PER_SEC;
  await watcher.wait_for([ 'transitionrun', 'transitionstart' ]);

  // Make idle
  transition.currentTime = 100 * MS_PER_SEC;
  transition.timeline = null;
  const evt = await watcher.wait_for('transitioncancel');
  assert_equals(evt.elapsedTime, 0.0);
}, 'Active -> Idle, with positive delay (Animation.timeline = null)');

promise_test(async t => {
  const { transition, watcher, div } =
    setupTransition(t, 'margin-left 100s -50s');

  // Pause so the currentTime is fixed and we can accurately compare the event
  // time in transition cancel events.
  transition.pause();

  await watcher.wait_for([ 'transitionrun', 'transitionstart' ]);

  // Make idle
  div.style.display = 'none';
  flushComputedStyle(div);
  const evt = await watcher.wait_for('transitioncancel');
  assert_equals(evt.elapsedTime, 50.0);
}, 'Active -> Idle, with negative delay (display: none)');

promise_test(async t => {
  const { transition, watcher } = setupTransition(t, 'margin-left 100s -50s');

  await watcher.wait_for([ 'transitionrun', 'transitionstart' ]);

  // Make idle
  transition.currentTime = 50 * MS_PER_SEC;
  transition.timeline = null;
  const evt = await watcher.wait_for('transitioncancel');
  assert_equals(evt.elapsedTime, 0.0);
}, 'Active -> Idle, with negative delay (Animation.timeline = null)');

promise_test(async t => {
  const { transition, watcher } = setupTransition(t, 'margin-left 100s 100s');

  // Seek to Active phase.
  transition.currentTime = 100 * MS_PER_SEC;
  await watcher.wait_for([ 'transitionrun', 'transitionstart' ]);

  // Seek to Before phase.
  transition.currentTime = 0;
  const evt = await watcher.wait_for('transitionend');
  assert_equals(evt.elapsedTime, 0.0);
}, 'Active -> Before');

promise_test(async t => {
  const { transition, watcher } = setupTransition(t, 'margin-left 100s 100s');

  // Seek to Active phase.
  transition.currentTime = 100 * MS_PER_SEC;
  await watcher.wait_for([ 'transitionrun', 'transitionstart' ]);

  // Seek to After phase.
  transition.currentTime = 200 * MS_PER_SEC;
  const evt = await watcher.wait_for('transitionend');
  assert_equals(evt.elapsedTime, 100.0);
}, 'Active -> After');

promise_test(async t => {
  const { transition, watcher, div, handler } =
    setupTransition(t, 'margin-left 100s 100s');

  // Seek to After phase.
  transition.finish();
  await watcher.wait_for([ 'transitionrun',
                           'transitionstart',
                           'transitionend' ]);

  // Seek to Before phase.
  transition.currentTime = 0;
  await watcher.wait_for([ 'transitionstart', 'transitionend' ]);

  assert_equals(handler.transitionstart, 100.0);
  assert_equals(handler.transitionend, 0.0);
}, 'After -> Before');

promise_test(async t => {
  const { transition, watcher } = setupTransition(t, 'margin-left 100s 100s');

  // Seek to After phase.
  transition.finish();
  await watcher.wait_for([ 'transitionrun',
                           'transitionstart',
                           'transitionend' ]);

  // Seek to Active phase.
  transition.currentTime = 100 * MS_PER_SEC;
  const evt = await watcher.wait_for('transitionstart');
  assert_equals(evt.elapsedTime, 100.0);
}, 'After -> Active');

promise_test(async t => {
  const { transition, watcher, div, handler } =
    setupTransition(t, 'margin-left 100s -50s');

  await watcher.wait_for([ 'transitionrun',
                           'transitionstart' ]);

  assert_equals(handler.transitionrun, 50.0);
  assert_equals(handler.transitionstart, 50.0);
  transition.finish();

  const evt = await watcher.wait_for('transitionend');
  assert_equals(evt.elapsedTime, 100.0);
}, 'Calculating the interval start and end time with negative start delay.');

promise_test(async t => {
  const { transition, watcher, div, handler } =
    setupTransition(t, 'margin-left 100s 100s');

  await watcher.wait_for('transitionrun');

  // We can't set the end delay via generated effect timing
  // because mutating CSS transitions is not specced yet.
  transition.effect = new KeyframeEffect(div,
                                         { marginleft: [ '0px', '100px' ]},
                                         { duration: 100 * MS_PER_SEC,
                                           endDelay: -50 * MS_PER_SEC });
  // Seek to Before and play.
  transition.cancel();
  transition.play();
  await watcher.wait_for([ 'transitioncancel',
                           'transitionrun',
                           'transitionstart' ]);
  assert_equals(handler.transitionstart, 0.0);

  // Seek to After phase.
  transition.finish();
  const evt = await watcher.wait_for('transitionend');
  assert_equals(evt.elapsedTime, 50.0);
}, 'Calculating the interval start and end time with negative end delay.');

promise_test(async t => {
  const { transition, watcher, div } =
    setupTransition(t, 'margin-left 100s 100s');

  await watcher.wait_for('transitionrun');

  // Make idle
  div.style.display = 'none';
  flushComputedStyle(div);
  await watcher.wait_for('transitioncancel');

  transition.cancel();
  // Then wait a couple of frames and check that no event was dispatched
  await waitForAnimationFrames(2);
}, 'Call Animation.cancel after canceling transition.');

promise_test(async t => {
  const { transition, watcher, div } =
    setupTransition(t, 'margin-left 100s 100s');

  await watcher.wait_for('transitionrun');

  // Make idle
  transition.cancel();
  transition.play();
  await watcher.wait_for([ 'transitioncancel',
                           'transitionrun' ]);
}, 'Restart transition after canceling transition immediately');

promise_test(async t => {
  const { transition, watcher, div } =
    setupTransition(t, 'margin-left 100s 100s');

  await watcher.wait_for('transitionrun');

  // Make idle
  div.style.display = 'none';
  flushComputedStyle(div);
  transition.play();
  transition.cancel();
  await watcher.wait_for('transitioncancel');

  // Then wait a couple of frames and check that no event was dispatched
  await waitForAnimationFrames(2);
}, 'Call Animation.cancel after restarting transition immediately');

promise_test(async t => {
  const { transition, watcher } = setupTransition(t, 'margin-left 100s');

  await watcher.wait_for([ 'transitionrun', 'transitionstart' ]);

  // Make idle
  transition.timeline = null;
  await watcher.wait_for('transitioncancel');

  transition.timeline = document.timeline;
  transition.play();

  await watcher.wait_for(['transitionrun', 'transitionstart']);
}, 'Set timeline and play transition after clear the timeline');

promise_test(async t => {
  const { transition, watcher, div } =
    setupTransition(t, 'margin-left 100s');

  await watcher.wait_for([ 'transitionrun', 'transitionstart' ]);

  transition.cancel();
  await watcher.wait_for('transitioncancel');

  // Make After phase
  transition.effect = null;

  // Then wait a couple of frames and check that no event was dispatched
  await waitForAnimationFrames(2);
}, 'Set null target effect after canceling the transition');

promise_test(async t => {
  const { transition, watcher, div } = setupTransition(t, 'margin-left 100s');

  await watcher.wait_for([ 'transitionrun', 'transitionstart' ]);

  transition.effect = null;
  await watcher.wait_for('transitionend');

  transition.cancel();

  // Then wait a couple of frames and check that no event was dispatched
  await waitForAnimationFrames(2);
}, 'Cancel the transition after clearing the target effect');

</script>
</body>
</html>