chromium/third_party/blink/web_tests/external/wpt/uievents/order-of-events/mouse-events/mouseover-out.html

<!doctype html>
<meta charset="utf-8">
<title>Mouseover/mouseout handling</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-actions.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<style>
#outer,
#inner,
#released {
  padding: 10px;
  margin: 10px;
  height: 15px;
}

#outer:hover,
#inner:hover,
#released:hover {
  background: red;
}

#outer {
  background: blue;
}

#inner {
  background: green;
}

#released {
  background: yellow;
}
</style>
<p>
  Steps:

  <ol>
    <li>Move your mouse over the blue <code>&lt;div&gt;</code> element, later
      over the green one, later over the yellow one.
    <li>Move the mouse from the yellow element to the green one, later to the
      blue one, and later over this paragraph.
  </ol>
</p>


<div id="outer">
  <div id="inner"></div>
</div>
<div id="released"></div>

<div id="log"></div>

<script>
var t = async_test("Mouseover/out events");
var outer = document.getElementById("outer");
var inner = document.getElementById("inner");
var actions_promise;

var inner_over = 0;
var inner_out = 0;

var outer_own_over = 0;
var outer_own_out = 0;
var outer_over = 0;
var outer_out = 0;

inner.addEventListener("mouseover", t.step_func(function(e) {
  assert_equals(inner_over, inner_out, "mouseover is received before mouseout");

  switch (inner_over) {
    case 0:
      assert_equals(outer_own_over, 1, "should have triggered a mouseover in the outer before");
      break;
    case 1:
      assert_equals(outer_own_over, 1, "should have not triggered a mouseover in the outer before");
      break;
    default:
      assert_true(false, "should not get more than two mouseovers");
  }

  inner_over++;
}), false);

inner.addEventListener("mouseout", t.step_func(function(e) {
  assert_equals(inner_over, inner_out + 1, "mouseout is received after mouseover");

  switch (inner_out) {
    case 0:
      assert_equals(outer_own_out, 1, "mouseout should have been received in the parent when hovering over this element");
      break;
    case 1:
      break;
    default:
      assert_true(false, "should not get more than two mouseouts");
  }

  inner_out++;
}), false);

outer.addEventListener("mouseover", t.step_func(function(e) {
  if (e.target == outer) {
    assert_equals(outer_own_over, outer_own_out, "outer: mouseover is received before mouseout");
    outer_own_over++;
  } else {
    assert_equals(outer_over - outer_own_over, inner_over - 1, "mouseover: should only receive this via bubbling");
  }

  outer_over++;
}), false);

outer.addEventListener('mouseout', t.step_func(function(e) {
  if (e.target == outer) {
    assert_equals(outer_own_over, outer_own_out + 1, "outer: mouseout is received after mouseover");
    if (outer_own_out == 1) {
      assert_equals(inner_out, 2, "inner should be done now");
      // Make sure the test finishes after all the input actions are completed.
      actions_promise.then( () => {
        t.done();
      });
    }

    outer_own_out++;
  } else {
    assert_equals(outer_out - outer_own_out, inner_out - 1, "mouseout: should only receive this via bubbling");
  }

  outer_out++;
}), false);

// Inject mouse inputs.
actions_promise = new test_driver.Actions()
    .pointerMove(0, 0, {origin: outer})
    .pointerMove(0, 0, {origin: inner})
    .pointerMove(0, 0, {origin: released})
    .pointerMove(0, 0, {origin: inner})
    .pointerMove(0, 0, {origin: outer})
    .pointerMove(0, 0)
    .send();
</script>