The issue is that when there are no intercept handlers, or the handlers provide a result immediately by returning or resolving/rejecting. This will mean that the reactions for committed and finished will run in the same microtask, which is correct according to spec. Differential Revision: https://phabricator.services.mozilla.com/D291776
222 lines
8.5 KiB
HTML
222 lines
8.5 KiB
HTML
<!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>
|