summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/navigation-api/focus-reset
diff options
context:
space:
mode:
Diffstat (limited to 'testing/web-platform/tests/navigation-api/focus-reset')
-rw-r--r--testing/web-platform/tests/navigation-api/focus-reset/autofocus.html185
-rw-r--r--testing/web-platform/tests/navigation-api/focus-reset/basic.html63
-rw-r--r--testing/web-platform/tests/navigation-api/focus-reset/change-focus-again-in-blur-during-intercept.html35
-rw-r--r--testing/web-platform/tests/navigation-api/focus-reset/change-focus-back-to-origial-during-intercept.html36
-rw-r--r--testing/web-platform/tests/navigation-api/focus-reset/change-focus-during-intercept.html34
-rw-r--r--testing/web-platform/tests/navigation-api/focus-reset/change-focus-then-remove-during-intercept.html40
-rw-r--r--testing/web-platform/tests/navigation-api/focus-reset/multiple-intercept.html69
-rw-r--r--testing/web-platform/tests/navigation-api/focus-reset/resources/helpers.mjs73
8 files changed, 535 insertions, 0 deletions
diff --git a/testing/web-platform/tests/navigation-api/focus-reset/autofocus.html b/testing/web-platform/tests/navigation-api/focus-reset/autofocus.html
new file mode 100644
index 0000000000..6044447367
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/focus-reset/autofocus.html
@@ -0,0 +1,185 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<button autofocus id="initialAutofocusTarget">Initial autofocus target</button>
+
+<script type="module">
+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");
+
+ navigation.addEventListener("navigate", e => {
+ e.intercept();
+ }, { once: true });
+
+ const { committed, finished } = navigation.navigate("#1");
+
+ await committed;
+ assert_equals(document.activeElement, decoy, "Focus stays on the non-autofocused button during the transition");
+
+ 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");
+
+ navigation.addEventListener("navigate", e => {
+ e.intercept();
+ }, { 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");
+
+ 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");
+
+ navigation.addEventListener("navigate", e => {
+ e.intercept();
+ }, { once: true });
+
+ const { committed, finished } = navigation.navigate("#1");
+
+ await committed;
+ assert_equals(document.activeElement, decoy, "Focus stays on the non-autofocused button during the transition");
+
+ autofocusTarget.disabled = true;
+
+ await finished;
+ assert_equals(document.activeElement, document.body, "Focus gets reset after the transition");
+}, "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");
+
+ navigation.addEventListener("navigate", e => {
+ e.intercept();
+ }, { once: true });
+
+ const { committed, finished } = navigation.navigate("#1");
+
+ await committed;
+ assert_equals(document.activeElement, decoy, "Focus stays on the non-autofocused button during the transition");
+
+ autofocusTarget.autofocus = false;
+
+ await finished;
+ assert_equals(document.activeElement, document.body, "Focus gets reset after the transition");
+}, "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");
+
+ navigation.addEventListener("navigate", e => {
+ e.intercept();
+ }, { 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;
+ assert_equals(document.activeElement, document.body, "Disabling the initially-focused button temporarily resets focus to the body");
+
+ await finished;
+ assert_equals(document.activeElement, autofocusTarget, "Focus moves to the second autofocused button after the transition");
+}, "Two elements with autofocus, present before navigation, but the first gets disabled; the second 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");
+
+ navigation.addEventListener("navigate", e => {
+ e.intercept();
+ }, { once: true });
+
+ const { committed, finished } = navigation.navigate("#1");
+
+ await committed;
+ assert_equals(document.activeElement, decoy, "Focus stays on the non-autofocused button during the transition");
+
+ const autofocusTarget = createAndAppend(t, { autofocus: true });
+
+ await finished;
+ assert_equals(document.activeElement, autofocusTarget, "Focus moves to the autofocused button 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");
+
+ navigation.addEventListener("navigate", e => {
+ e.intercept();
+ }, { once: true });
+
+ const { committed, finished } = navigation.navigate("#1");
+
+ await committed;
+ assert_equals(document.activeElement, decoy, "Focus stays on the non-autofocused button during 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>
diff --git a/testing/web-platform/tests/navigation-api/focus-reset/basic.html b/testing/web-platform/tests/navigation-api/focus-reset/basic.html
new file mode 100644
index 0000000000..f5a30972b0
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/focus-reset/basic.html
@@ -0,0 +1,63 @@
+<!doctype html>
+<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 type="module">
+import { testFocusWasReset, testFocusWasNotReset } from "./resources/helpers.mjs";
+
+test(() => {
+ let throwAssertionHappened = false;
+
+ navigation.addEventListener("navigate", e => {
+ assert_throws_js(TypeError, () => {
+ e.intercept({ focusReset: "invalid" });
+ });
+ throwAssertionHappened = true;
+ }, { once: true });
+
+ navigation.navigate("#1");
+ assert_true(throwAssertionHappened);
+}, "Invalid values for focusReset throw");
+
+testFocusWasNotReset(() => {
+ // Intentionally left blank.
+}, "Does not reset the focus when no navigate handler is present");
+
+testFocusWasReset(t => {
+ navigation.addEventListener("navigate", e => {
+ e.intercept();
+ }, { once: true });
+}, "Resets the focus when no focusReset option is provided");
+
+testFocusWasReset(t => {
+ navigation.addEventListener("navigate", e => {
+ e.intercept();
+ }, { once: true });
+}, "Resets the focus when focusReset is explicitly set to undefined");
+
+testFocusWasReset(t => {
+ navigation.addEventListener("navigate", e => {
+ e.intercept({ handler: () => new Promise(r => t.step_timeout(r, 5)) });
+ }, { once: true });
+}, "Resets the focus when no focusReset option is provided (nontrivial fulfilled promise)");
+
+testFocusWasReset(t => {
+ navigation.addEventListener("navigate", e => {
+ e.intercept({ handler: () => Promise.reject() });
+ }, { once: true });
+}, "Resets the focus when no focusReset option is provided (rejected promise)");
+
+testFocusWasReset(t => {
+ navigation.addEventListener("navigate", e => {
+ e.intercept({ focusReset: "after-transition" });
+ }, { once: true });
+}, "Resets the focus when focusReset is explicitly set to 'after-transition'");
+
+testFocusWasNotReset(t => {
+ navigation.addEventListener("navigate", e => {
+ e.intercept({ focusReset: "manual" });
+ });
+}, "Does not reset the focus when focusReset is set to 'manual'");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/focus-reset/change-focus-again-in-blur-during-intercept.html b/testing/web-platform/tests/navigation-api/focus-reset/change-focus-again-in-blur-during-intercept.html
new file mode 100644
index 0000000000..a7339c9788
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/focus-reset/change-focus-again-in-blur-during-intercept.html
@@ -0,0 +1,35 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script>
+promise_test(async t => {
+ let intercept_resolve;
+ navigation.addEventListener("navigate", e => {
+ e.intercept({ handler: () => new Promise(resolve => intercept_resolve = resolve),
+ focusReset: "after-transition" });
+ }, { once: true });
+
+ const button = document.body.appendChild(document.createElement("button"));
+ const button2 = document.body.appendChild(document.createElement("button"));
+ button2.tabIndex = 0;
+ t.add_cleanup(() => {
+ button.remove();
+ button2.remove();
+ });
+
+ assert_equals(document.activeElement, document.body, "Start on body");
+ button.focus();
+ assert_equals(document.activeElement, button, "focus() worked");
+
+ const finished = navigation.navigate("#1").finished;
+ button.onblur = () => button2.focus();
+ button.blur();
+ assert_equals(document.activeElement, button2, "focus() in blur worked");
+
+ intercept_resolve();
+ await finished;
+ assert_equals(document.activeElement, button2, "Focus was not reset after the transition");
+}, "");
+</script>
+</body>
diff --git a/testing/web-platform/tests/navigation-api/focus-reset/change-focus-back-to-origial-during-intercept.html b/testing/web-platform/tests/navigation-api/focus-reset/change-focus-back-to-origial-during-intercept.html
new file mode 100644
index 0000000000..4e5b9dfb6a
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/focus-reset/change-focus-back-to-origial-during-intercept.html
@@ -0,0 +1,36 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script>
+promise_test(async t => {
+ let intercept_resolve;
+ navigation.addEventListener("navigate", e => {
+ e.intercept({ handler: () => new Promise(resolve => intercept_resolve = resolve),
+ focusReset: "after-transition" });
+ }, { once: true });
+
+ const button = document.body.appendChild(document.createElement("button"));
+ const button2 = document.body.appendChild(document.createElement("button"));
+ button2.tabIndex = 0;
+ t.add_cleanup(() => {
+ button.remove();
+ button2.remove();
+ });
+
+ assert_equals(document.activeElement, document.body, "Start on body");
+ button.focus();
+ assert_equals(document.activeElement, button, "focus() worked");
+
+ const finished = navigation.navigate("#1").finished;
+ button2.focus();
+ assert_equals(document.activeElement, button2, "focus() worked");
+ button.focus();
+ assert_equals(document.activeElement, button, "focus() worked");
+
+ intercept_resolve();
+ await finished;
+ assert_equals(document.activeElement, button, "Focus was not reset after the transition");
+}, "");
+</script>
+</body>
diff --git a/testing/web-platform/tests/navigation-api/focus-reset/change-focus-during-intercept.html b/testing/web-platform/tests/navigation-api/focus-reset/change-focus-during-intercept.html
new file mode 100644
index 0000000000..0593231a39
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/focus-reset/change-focus-during-intercept.html
@@ -0,0 +1,34 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script>
+promise_test(async t => {
+ let intercept_resolve;
+ navigation.addEventListener("navigate", e => {
+ e.intercept({ handler: () => new Promise(resolve => intercept_resolve = resolve),
+ focusReset: "after-transition" });
+ }, { once: true });
+
+ const button = document.body.appendChild(document.createElement("button"));
+ const button2 = document.body.appendChild(document.createElement("button"));
+ button2.tabIndex = 0;
+ t.add_cleanup(() => {
+ button.remove();
+ button2.remove();
+ });
+
+ assert_equals(document.activeElement, document.body, "Start on body");
+ button.focus();
+ assert_equals(document.activeElement, button, "focus() worked");
+
+ const finished = navigation.navigate("#1").finished;
+ button2.focus();
+ assert_equals(document.activeElement, button2, "focus() worked");
+
+ intercept_resolve();
+ await finished;
+ assert_equals(document.activeElement, button2, "Focus was not reset after the transition");
+}, "");
+</script>
+</body>
diff --git a/testing/web-platform/tests/navigation-api/focus-reset/change-focus-then-remove-during-intercept.html b/testing/web-platform/tests/navigation-api/focus-reset/change-focus-then-remove-during-intercept.html
new file mode 100644
index 0000000000..a5d8062ce0
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/focus-reset/change-focus-then-remove-during-intercept.html
@@ -0,0 +1,40 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script>
+promise_test(async t => {
+ let intercept_resolve;
+ navigation.addEventListener("navigate", e => {
+ e.intercept({ handler: () => new Promise(resolve => intercept_resolve = resolve),
+ focusReset: "after-transition" });
+ }, { once: true });
+
+ const button = document.body.appendChild(document.createElement("button"));
+ const button2 = document.body.appendChild(document.createElement("button"));
+ button2.tabIndex = 0;
+ t.add_cleanup(() => {
+ button.remove();
+ button2.remove();
+ });
+
+ assert_equals(document.activeElement, document.body, "Start on body");
+ button.focus();
+ assert_equals(document.activeElement, button, "focus() worked");
+
+ const finished = navigation.navigate("#1").finished;
+
+ let onfocus_called = false;
+ document.body.onfocus = onfocus_called = true;
+ button.remove();
+ assert_equals(document.activeElement, document.body, "Removing the element reset focus");
+ assert_true(onfocus_called);
+
+ document.body.onfocus = t.unreached_func("onfocus shouldn't fire a second time due to focus reset");
+ intercept_resolve();
+ await finished;
+ assert_equals(document.activeElement, document.body, "Focus remains on document.body after promise fulfills");
+ await new Promise(resolve => t.step_timeout(resolve, 10));
+}, "");
+</script>
+</body>
diff --git a/testing/web-platform/tests/navigation-api/focus-reset/multiple-intercept.html b/testing/web-platform/tests/navigation-api/focus-reset/multiple-intercept.html
new file mode 100644
index 0000000000..75e38c98a4
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/focus-reset/multiple-intercept.html
@@ -0,0 +1,69 @@
+<!doctype html>
+<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 type="module">
+import { testFocusWasReset, testFocusWasNotReset } from "./resources/helpers.mjs";
+
+testFocusWasReset(t => {
+ navigation.addEventListener("navigate", e => {
+ e.intercept();
+ }, { once: true });
+
+ navigation.addEventListener("navigate", e => {
+ e.intercept({ focusReset: "after-transition" });
+ }, { once: true });
+}, "(not provided) + after-transition");
+
+testFocusWasNotReset(t => {
+ navigation.addEventListener("navigate", e => {
+ e.intercept();
+ }, { once: true });
+
+ navigation.addEventListener("navigate", e => {
+ e.intercept({ focusReset: "manual" });
+ }, { once: true });
+}, "(not provided) + manual");
+
+testFocusWasNotReset(t => {
+ navigation.addEventListener("navigate", e => {
+ e.intercept({ focusReset: "after-transition" });
+ }, { once: true });
+
+ navigation.addEventListener("navigate", e => {
+ e.intercept({ focusReset: "manual" });
+ }, { once: true });
+}, "after-transition + manual");
+
+testFocusWasReset(t => {
+ navigation.addEventListener("navigate", e => {
+ e.intercept({ focusReset: "after-transition" });
+ }, { once: true });
+
+ navigation.addEventListener("navigate", e => {
+ e.intercept();
+ }, { once: true });
+}, "after-transition + (not provided)");
+
+testFocusWasReset(t => {
+ navigation.addEventListener("navigate", e => {
+ e.intercept({ focusReset: "manual" });
+ }, { once: true });
+
+ navigation.addEventListener("navigate", e => {
+ e.intercept({ focusReset: "after-transition" });
+ }, { once: true });
+}, "manual + after-transition");
+
+testFocusWasNotReset(t => {
+ navigation.addEventListener("navigate", e => {
+ e.intercept({ focusReset: "manual" });
+ }, { once: true });
+
+ navigation.addEventListener("navigate", e => {
+ e.intercept();
+ }, { once: true });
+}, "manual + (not provided)");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/focus-reset/resources/helpers.mjs b/testing/web-platform/tests/navigation-api/focus-reset/resources/helpers.mjs
new file mode 100644
index 0000000000..0a8a0439e1
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/focus-reset/resources/helpers.mjs
@@ -0,0 +1,73 @@
+// Usage note: if you use these more than once in a given file, be sure to
+// clean up any navigate event listeners, e.g. by using { once: true }, between
+// tests.
+
+const TAB_KEY = "\uE004";
+
+export function testFocusWasReset(setupFunc, description) {
+ promise_test(async t => {
+ setupFunc(t);
+
+ const button = document.body.appendChild(document.createElement("button"));
+ const button2 = document.body.appendChild(document.createElement("button"));
+ button2.tabIndex = 0;
+ t.add_cleanup(() => {
+ button.remove();
+ button2.remove();
+ });
+
+ assert_equals(document.activeElement, document.body, "Start on body");
+ button.focus();
+ assert_equals(document.activeElement, button, "focus() worked");
+
+ const { committed, finished } = navigation.navigate("#" + location.hash.substring(1) + "1");
+
+ await committed;
+ assert_equals(document.activeElement, button, "Focus stays on the button during the transition");
+
+ await finished.catch(() => {});
+ assert_equals(document.activeElement, document.body, "Focus reset after the transition");
+
+ button2.onfocus = t.unreached_func("button2 must not be focused after pressing Tab");
+ const focusPromise = waitForFocus(t, button);
+ await test_driver.send_keys(document.body, TAB_KEY);
+ await focusPromise;
+ }, description);
+}
+
+export function testFocusWasNotReset(setupFunc, description) {
+ promise_test(async t => {
+ setupFunc(t);
+
+ const button = document.body.appendChild(document.createElement("button"));
+ const button2 = document.body.appendChild(document.createElement("button"));
+ button2.tabIndex = 0;
+ t.add_cleanup(() => {
+ button.remove();
+ button2.remove();
+ });
+
+ assert_equals(document.activeElement, document.body, "Start on body");
+ button.focus();
+ assert_equals(document.activeElement, button, "focus() worked");
+
+ const { committed, finished } = navigation.navigate("#" + location.hash.substring(1) + "1");
+
+ await committed;
+ assert_equals(document.activeElement, button, "Focus stays on the button during the transition");
+
+ await finished.catch(() => {});
+ assert_equals(document.activeElement, button, "Focus stays on the button after the transition");
+
+ button.onfocus = t.unreached_func("button must not be focused after pressing Tab");
+ const focusPromise = waitForFocus(t, button2);
+ await test_driver.send_keys(document.body, TAB_KEY);
+ await focusPromise;
+ }, description);
+}
+
+function waitForFocus(t, target) {
+ return new Promise(resolve => {
+ target.addEventListener("focus", () => resolve(), { once: true });
+ });
+}