chromium/third_party/blink/web_tests/accessibility/aria-activedescendant-events.html

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

<div id="contenteditable-textbox" role="textbox" contenteditable="true"
    aria-expanded="true" aria-haspopup="true" aria-autocomplete="list"
    aria-activedescendant="list1-option1">
    <ul id="list1" role="listbox">
        <li id="list1-option1" role="option">Option 1</li>
        <li id="list1-option2" role="option">Option 2</li>
        <li id="list1-option3" role="option">Option 3</li>
    </ul>
</div>

<input id="input-textbox" role="textbox" type="text" aria-expanded="true"
    aria-haspopup="true" aria-autocomplete="list"
    aria-activedescendant="list2-option1" aria-owns="list2">

<ul id="list2" role="listbox">
    <li id="list2-option1" role="option">Option 1</li>
    <li id="list2-option2" role="option">Option 2</li>
    <li id="list2-option3" role="option">Option 3</li>
</ul>

<input id="input-combobox" role="combobox" type="search" aria-expanded="true"
    aria-haspopup="true" aria-autocomplete="list"
    aria-activedescendant="list3-option1" aria-owns="list3">

<ul id="list3" role="listbox">
    <li id="list3-option1" role="option">Option 1</li>
    <li id="list3-option2" role="option">Option 2</li>
    <li id="list3-option3" role="option">Option 3</li>
</ul>

<textarea id="textarea-searchbox" role="searchbox" aria-expanded="true"
    aria-haspopup="true" aria-autocomplete="list"
    aria-activedescendant="list4-option1" aria-owns="list4"></textarea>

<ul id="list4" role="listbox">
    <li id="list4-option1" role="option">Option 1</li>
    <li id="list4-option2" role="option">Option 2</li>
    <li id="list4-option3" role="option">Option 3</li>
</ul>

<input id="pure-native-input-textbox" type="text" aria-expanded="true"
    aria-haspopup="true" aria-autocomplete="list"
    aria-activedescendant="list5-option1" aria-owns="list5">

<ul id="list5" role="listbox">
    <li id="list5-option1" role="option">Option 1</li>
    <li id="list5-option2" role="option">Option 2</li>
    <li id="list5-option3" role="option">Option 3</li>
</ul>

<script>
var idsToTest = [ [ "contenteditable-textbox", "list1" ],
                  [ "input-textbox", "list2" ],
                  [ "input-combobox", "list3" ],
                  [ "textarea-searchbox", "list4" ],
                  [ "pure-native-input-textbox", "list5" ] ];

function testEventExpectations(t, widgetId, listboxId) {
    var focusChangedEventCount = 0;
    var markDirtyEventCount = 0;
    window.setTimeout(t.step_func(() => {
        if (focusChangedEventCount < 1 || markDirtyEventCount < 3)
            assert_unreached("Did not receive all expected notifications on " + widgetId);
    }), 500);
    var listbox = document.getElementById(listboxId);
    var widget = document.getElementById(widgetId);
    var axWidget = accessibilityController.accessibleElementById(widgetId);
    var focusChangedEventCount = 0;
    var markDirtyEventCount = 0;
    var eventsComplete = false;
    axWidget.addNotificationListener(t.step_func(function(notification) {
        if (notification == "Focus") {
            ++focusChangedEventCount;
            console.log("Got focus notification #" + focusChangedEventCount + " on " + widgetId);
        }
        if (notification == "MarkDirty") {
            ++markDirtyEventCount;
            console.log("Got MarkDirty notification #" + markDirtyEventCount + " on " + widgetId);
        }
        if (focusChangedEventCount >= 1 && markDirtyEventCount >= 3 && !eventsComplete) {
            eventsComplete = true;
            window.setTimeout(t.step_func(() => {
                console.log('** Events complete for ' + widgetId);
                testNext(t);
            }, 0));
        }
    }));

    widget.focus();
    widget.setAttribute("aria-activedescendant", listboxId + "-option2");
    widget.setAttribute("aria-activedescendant", listboxId + "-option3");
    widget.removeAttribute("aria-activedescendant");
}

function testNext(t) {
    if (idsToTest.length == 0)
        t.done();
    let nextIds = idsToTest.shift();
    t.step(() => { testEventExpectations(t, nextIds[0], nextIds[1]) });
}

async_test(function(t) {
    testNext(t);
}, "Changing active descendant triggers MarkDirty");
</script>