Source code
Revision control
Copy as Markdown
Other Tools
Test Info:
- This WPT test may be referenced by the following Test IDs:
- /navigation-api/focus-reset/autofocus.html?delayed-checkpoint - WPT Dashboard Interop Dashboard
- /navigation-api/focus-reset/autofocus.html?same-checkpoint - WPT Dashboard Interop Dashboard
<!doctype html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<meta name="variant" content="?delayed-checkpoint">
<meta name="variant" content="?same-checkpoint">
<button autofocus id="initialAutofocusTarget">Initial autofocus target</button>
<script type="module">
const delayed = location.search === "?delayed-checkpoint";
function createExpected(t, decoy, autofocusTarget) {
const handler = delayed ? { handler: () => new Promise(r => t.step_timeout(r, 0)) } : {};
const expectedDecoy = delayed ? decoy : autofocusTarget;
const decoyMessage = delayed ? "Focus moves to the autofocused button after the transition" : "Focus stays on the non-autofocused button after the transition";
const expectedBody = delayed ? document.body : expectedDecoy;
const bodyMessage = delayed ? "Focus gets reset after the transition" : "Focus moves to the autofocused button after the transition";
return { handler, expectedDecoy, decoyMessage, expectedBody, bodyMessage };
}
promise_setup(async () => {
// Get the overall autofocus processed flag to flip to true, so that
// we only test the navigation API-specific stuff.
await new Promise(r => requestAnimationFrame(() => requestAnimationFrame(r)));
assert_equals(document.activeElement, initialAutofocusTarget, "Non-navigation API autofocus was processed");
initialAutofocusTarget.remove();
assert_equals(document.activeElement, document.body);
});
promise_test(async t => {
const decoy = createAndAppend(t);
const autofocusTarget = createAndAppend(t, { autofocus: true });
assert_equals(document.activeElement, document.body, "Start on body");
decoy.focus();
assert_equals(document.activeElement, decoy, "focus() worked");
const { handler, expectedDecoy, decoyMessage } = createExpected(t, decoy, autofocusTarget);
navigation.addEventListener("navigate", e => {
e.intercept(handler);
}, { once: true });
const { committed, finished } = navigation.navigate("#1");
await committed;
assert_equals(document.activeElement, expectedDecoy, decoyMessage);
await finished;
assert_equals(document.activeElement, autofocusTarget, "Focus moves to the autofocused button after the transition");
}, "An element with autofocus, present before navigation, gets focused");
promise_test(async t => {
const autofocusTarget = createAndAppend(t, { autofocus: true });
const decoy = createAndAppend(t, { autofocus: true });
assert_equals(document.activeElement, document.body, "Start on body");
decoy.focus();
assert_equals(document.activeElement, decoy, "focus() worked");
const { handler, expectedDecoy, decoyMessage } = createExpected(t, decoy, autofocusTarget);
navigation.addEventListener("navigate", e => {
e.intercept(handler);
}, { once: true });
const { committed, finished } = navigation.navigate("#1");
await committed;
assert_equals(document.activeElement, expectedDecoy, decoyMessage);
await finished;
assert_equals(document.activeElement, autofocusTarget, "Focus moves to the first autofocused button after the transition");
}, "Two elements with autofocus, present before navigation; the first gets focused");
promise_test(async t => {
const decoy = createAndAppend(t);
const autofocusTarget = createAndAppend(t, { autofocus: true });
assert_equals(document.activeElement, document.body, "Start on body");
decoy.focus();
assert_equals(document.activeElement, decoy, "focus() worked");
const { handler, expectedDecoy, decoyMessage, expectedBody, bodyMessage } = createExpected(t, decoy, autofocusTarget);
navigation.addEventListener("navigate", e => {
e.intercept(handler);
}, { once: true });
const { committed, finished } = navigation.navigate("#1");
await committed;
assert_equals(document.activeElement, expectedDecoy, decoyMessage);
autofocusTarget.disabled = true;
await finished;
assert_equals(document.activeElement, expectedBody, bodyMessage);
}, "An element with autofocus, present before navigation but disabled before finished, does not get focused");
promise_test(async t => {
const decoy = createAndAppend(t);
const autofocusTarget = createAndAppend(t, { autofocus: true });
assert_equals(document.activeElement, document.body, "Start on body");
decoy.focus();
assert_equals(document.activeElement, decoy, "focus() worked");
const { handler, expectedDecoy, decoyMessage, expectedBody, bodyMessage } = createExpected(t, decoy, autofocusTarget);
navigation.addEventListener("navigate", e => {
e.intercept(handler);
}, { once: true });
const { committed, finished } = navigation.navigate("#1");
await committed;
assert_equals(document.activeElement, expectedDecoy, decoyMessage);
autofocusTarget.autofocus = false;
await finished;
assert_equals(document.activeElement, expectedBody, bodyMessage);
}, "An element with autofocus, present before navigation but with its autofocus attribute removed before finished, does not get focused");
promise_test(async t => {
const decoy = createAndAppend(t, { autofocus: true });
const autofocusTarget = createAndAppend(t, { autofocus: true });
assert_equals(document.activeElement, document.body, "Start on body");
decoy.focus();
assert_equals(document.activeElement, decoy, "focus() worked");
const { handler } = createExpected(t, decoy, autofocusTarget);
navigation.addEventListener("navigate", e => {
e.intercept(handler);
}, { once: true });
const { committed, finished } = navigation.navigate("#1");
await committed;
assert_equals(document.activeElement, decoy, "Focus stays on the initially-focused button during the transition");
decoy.disabled = true;
await finished;
if (delayed) {
assert_equals(document.activeElement, autofocusTarget, "Focus moves to the second autofocused button after the transition");
} else {
assert_equals(document.activeElement, decoy, "Focus stays on the initially-focused button during the transition");
}
}, "Two elements with autofocus, present before navigation, but the first gets disabled; the second gets focused");
promise_test(async t => {
if (delayed) {
assert_true(delayed, "This is only relevant for non-finished ")
return;
}
const decoy = createAndAppend(t);
assert_equals(document.activeElement, document.body, "Start on body");
decoy.focus();
assert_equals(document.activeElement, decoy, "focus() worked");
const { handler } = createExpected(t, decoy, document.body);
navigation.addEventListener("navigate", e => {
e.intercept(() => {
t.step_timeout(handler, 0)
});
}, { once: true });
const { committed, finished } = navigation.navigate("#1");
await committed;
assert_equals(document.activeElement, document.body, "Focus gets reset after the transition");
const autofocusTarget = createAndAppend(t, { autofocus: true });
await finished;
assert_equals(document.activeElement, document.body, "Focus gets reset after the transition");
await new Promise(r => requestAnimationFrame(() => requestAnimationFrame(r)));
assert_equals(document.activeElement, document.body, "Focus stays reset two animation frames after the transition");
}, "An element with autofocus, introduced between committed and finished, gets focused");
promise_test(async t => {
const decoy = createAndAppend(t);
assert_equals(document.activeElement, document.body, "Start on body");
decoy.focus();
assert_equals(document.activeElement, decoy, "focus() worked");
const { handler } = createExpected(t, decoy, document.body);
navigation.addEventListener("navigate", e => {
e.intercept(handler);
}, { once: true });
const { committed, finished } = navigation.navigate("#1");
await committed;
assert_equals(document.activeElement, delayed ? decoy : document.body, delayed ? "Focus stays on the non-autofocused button during the transition" : "Focus gets reset after the transition");
await finished;
assert_equals(document.activeElement, document.body, "Focus gets reset after the transition");
const autofocusTarget = createAndAppend(t, { autofocus: true });
await new Promise(r => requestAnimationFrame(() => requestAnimationFrame(r)));
assert_equals(document.activeElement, document.body, "Focus stays reset two animation frames after the transition");
}, "An element with autofocus, introduced after finished, does not get focused");
function createAndAppend(t, props) {
const element = document.createElement("button");
Object.assign(element, props);
document.body.append(element);
t.add_cleanup(() => { element.remove(); });
return element;
}
</script>