chromium/third_party/blink/web_tests/accessibility/notification-listeners.html

<script src="../resources/testharness.js"></script>
<script src="../resources/testharnessreport.js"></script>

<select id="select" value="Select"></select>

<div id="slider" tabindex="0" role="slider" aria-valuenow="5">Slider</div>

<script>
async_test((t) => {
    // Will track all notifications except for 'MarkDirty' -- MarkDirty is a
    // catch-all notification that tells the serializer to re-serialize
    // a particular node. The number of MarkDirty notifications can
    // change as we optimize or fix issues with stale nodes. It doesn't
    // make sense to assert test failures based on specific numbers of
    // MarkDirty notifications.
    window.selectNotificationCount = 0;
    window.sliderNotificationCount = 0;
    window.globalNotificationCount = 0;

    window.select = accessibilityController.accessibleElementById("select");
    window.slider = accessibilityController.accessibleElementById("slider");

    let expected_element_notifications = new Map([
      ["Focus", new Map([
        ["select", 1],
        ["slider", 1],
      ])],
      ["Blur", new Map([
        ["select", 1],
      ])],
      ["ValueChanged", new Map([
        ["slider", 1],
      ])],
    ]);

    let expected_global_notifications = new Map([
      ["Focus", new Map([
        ["AXRole: AXComboBoxSelect", 1],
        ["AXRole: AXSlider", 1],
      ])],
      ["Blur", new Map([
        ["AXRole: AXComboBoxSelect", 1],
      ])],
      ["ValueChanged", new Map([
        ["AXRole: AXSlider", 1],
      ])],
    ]);

    let HandleElementNotification = function(notification, element_key) {
      if (notification == 'MarkDirty')
        return;
      assert_true(expected_element_notifications.has(notification));
      let element_map = expected_element_notifications.get(notification);
      assert_true(element_map.has(element_key));
      let expected_notification_count = element_map.get(element_key);
      assert_greater_than(expected_notification_count, 0);
      if (expected_notification_count > 1) {
        element_map.set(element_key, expected_notification_count - 1);
      } else {
        element_map.delete(element_key);
        if (element_map.size == 0) {
          expected_element_notifications.delete(notification);
        }
      }
    };

    let HandleGlobalNotification = function(notification, role_key) {
      if (notification == 'MarkDirty')
        return;
      assert_true(expected_global_notifications.has(notification));
      let role_map = expected_global_notifications.get(notification);
      assert_true(role_map.has(role_key));
      let expected_notification_count = role_map.get(role_key);
      assert_greater_than(expected_notification_count, 0);
      if (expected_notification_count > 1) {
        role_map.set(role_key, expected_notification_count - 1);
      } else {
        role_map.delete(role_key);
        if (role_map.size == 0) {
          expected_global_notifications.delete(notification);
        }
      }
    };

    select.addNotificationListener(t.step_func(function(notification) {
      if (notification == 'MarkDirty')
        return;
      selectNotificationCount++;
      console.log("got " + notification + " on select");
      HandleElementNotification(notification, "select");
    }));

    slider.addNotificationListener(t.step_func(function(notification) {
      if (notification == 'MarkDirty')
        return;
      sliderNotificationCount++;
      console.log("got " + notification + " on slider");
      HandleElementNotification(notification, "slider");
    }));

    accessibilityController.addNotificationListener(t.step_func(function(element, notification) {
      if (notification == 'MarkDirty')
        return;
      if (element.isEqual(slider) || element.isEqual(select)) {
        globalNotificationCount++;
        console.log("got " + notification + " on global");
        HandleGlobalNotification(notification, element.role);
      } else {
        console.log('Got unexpected ' + notification + ' notification on ' + element.role);
      }
      if (expected_element_notifications.size == 0 &&
        expected_global_notifications.size === 0) {
        assert_equals(selectNotificationCount, 2);
        assert_equals(sliderNotificationCount, 2);
        assert_equals(globalNotificationCount, 4);
        accessibilityController.removeNotificationListener();
        select.removeNotificationListener();
        slider.removeNotificationListener();
        t.done();
      }
    }));

    // This should trigger a "Focus" notification on the select.
    document.getElementById("select").focus();

    // Wait before focusing the slider, otherwise the select's focus is ingored.
    setTimeout(() => {
      // This should trigger a "Blur" notification on the select,
      // followed by a "Focus" notification on the slider.
      document.getElementById("slider").focus();

      // This should trigger a "MarkDirty" notification on the select.
      document.getElementById("select").setAttribute("aria-invalid", "true");

      // This should trigger a "value changed" notification on the slider.
      document.getElementById("slider").setAttribute("aria-valuenow", "6");
    }, 1);

    window.setTimeout(t.step_func_done(() => {
      assert_unreached();
    }), 100);
}, "This tests that a notification listener on an element only listens to that one element, and that a global notification listener listens to all notifications.");

</script>