Source code

Revision control

Copy as Markdown

Other Tools

Test Info:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Tests that the sequential focus navigation starting point is tracked correctly</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/resources/testdriver-actions.js"></script>
</head>
<body>
<button id="before-container">(before container)</button>
<div id="container"></div>
<button id="after-container">(after container)</button>
<template data-description="Focus navigation starting point should be maintained when focused element is removed.">
<button id="button1" data-prev>button1</button>
<button data-focus data-remove>focus</button>
<button id="button2" data-next>button2</button>
</template>
<template data-description="Focus navigation starting point should be maintained when parent element is removed.">
<button id="button1" data-prev>button1</button>
<div data-remove>
<button data-focus>focus</button>
</div>
<button id="button2" data-next>button2</button>
</template>
<template data-description="Focus navigation starting point should be maintained when shadow host is removed.">
<button id="button1" data-prev>button1</button>
<div data-remove data-shadow>
<template>
<button data-focus>focus</button>
</template>
</div>
<button id="button2" data-next>button2</button>
</template>
<template data-description="Focus navigation starting point should be maintained when focused button is disabled.">
<button id="button1" data-prev>button1</button>
<button data-focus data-disable>focus</button>
<button id="button2" data-next>button2</button>
</template>
<template data-description="Focus navigation starting point should not change when previous element is removed.">
<button id="button1" data-prev>button1</button><button data-focus data-remove-2>
will be removed
</button><button data-focus data-remove>
focus
</button><button id="button2" data-next>button2</button>
</template>
<template data-description="Focus navigation starting point should not change when next element is removed.">
<button id="button1" data-prev>button1</button><button data-focus data-remove>
focus
</button><button data-focus data-remove-2>
will be removed
</button><button id="button2" data-next>button2</button>
</template>
<template data-description="Focus navigation starting point should not change when next element is moved with moveBefore.">
<button id="button1" data-prev>button1</button><button data-focus data-remove>
focus
</button><button data-move id="moved">
will be moved
</button><button id="button2" data-next>button2</button>
<button id="button3">button3</button>
<div data-move-target></div>
<button id="button4">button4</button>
</template>
<template data-description="Focus navigation starting point should not change when previous element is moved with moveBefore.">
<button id="button1" data-prev>button1</button><button data-move id="moved">
will be moved
</button><button data-focus data-remove>
focus
</button><button id="button2" data-next>button2</button>
<button id="button3">button3</button>
<div data-move-target></div>
<button id="button4">button4</button>
</template>
<template data-description="Focus navigation starting point should not change when previous element gets new children.">
<button id="button1">button1</button><div data-add-child-with-data-prev>
parent
</div><button data-focus data-remove>
focus
</button><button id="button2" data-next>button2</button>
</template>
<template data-description="Focus navigation starting point should not change when focused element at end of shadow tree is removed.">
<button id="button1" data-prev>button1</button>
<div data-remove data-shadow>
<template><button data-focus data-remove>focus</button></template>
</div>
<button id="button2" data-next>button2</button>
</template>
<template data-description="Focus navigation starting point should stay on element when it is disabled and re-enabled.">
<button data-prev>button1</button
><button data-focus data-disable data-reenable>
button2
</button><button data-next>button3</button>
</template>
<template data-description="Focus navigation starting point should stay on element when it is blurred.">
<button data-prev>button1</button
><button data-focus data-blur>
button2
</button><button data-next>button3</button>
</template>
<template data-description="Focus navigation starting point should be maintained when slotted focused element is removed.">
<div data-shadow>
<template><slot name="a"
></slot><slot name="b"
></slot><slot name="c"
></slot><slot name="d"
></slot><slot name="e"
></slot></template>
<button slot="a">button 1</button>
<button data-focus data-remove slot="c">focus</button>
<button slot="e">button 5</button>
<button data-prev slot="b">button 2</button>
<button data-next slot="d">button 4</button>
</div>
</template>
<script>
"use strict";
const kShift = "\uE008";
const kTab = "\uE004";
// Runs callback on each element matching selector, descending into open shadow roots.
function forEachMatching(selector, callback, root) {
if (!root) { root = container; }
for (let element of root.querySelectorAll(selector)) {
callback(element);
}
for (let element of root.querySelectorAll("[data-shadow]")) {
forEachMatching(selector, callback, element.shadowRoot);
}
}
for (let template of document.querySelectorAll("template")) {
promise_test(async t => {
for (let backwards of [false, true]) {
container.replaceChildren();
let test = document.importNode(template.content, true);
container.append(test);
forEachMatching("[data-shadow]", element => {
let shadow = element.attachShadow({mode: "open"});
shadow.append(element.querySelector("template").content);
});
forEachMatching("[data-focus]", element => {
element.focus();
});
forEachMatching("[data-blur]", element => {
element.blur();
});
forEachMatching("[data-disable]", element => {
element.disabled = true;
});
forEachMatching("[data-remove]", element => {
element.remove();
});
forEachMatching("[data-remove-2]", element => {
element.remove();
});
forEachMatching("[data-move]", element => {
forEachMatching("[data-move-target]", target => {
target.moveBefore(element, null);
});
});
forEachMatching("[data-add-child-with-data-prev]", element => {
let button = document.createElement("button");
button.id = "child";
button.append("child");
button.setAttribute("data-prev", "");
element.append(button);
});
forEachMatching("[data-reenable]", element => {
element.disabled = false;
});
let actions = new test_driver.Actions();
if (backwards) {
actions.keyDown(kShift);
}
actions.keyDown(kTab)
.keyUp(kTab);
if (backwards) {
actions.keyUp(kShift);
}
await actions.send();
let assertions = 0;
forEachMatching(backwards ? "[data-prev]" : "[data-next]", expected => {
let direction = backwards ? "Backwards" : "Forwards";
assertions++;
assert_equals(document.activeElement, expected,
`${direction} sequential focus navigation should go to ${expected.nodeName}#${expected.id}`);
});
assert_equals(assertions, 1, "Each test should have exactly one [data-prev] and one [data-next]");
}
}, template.dataset.description);
}
</script>
</body>
</html>