diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /testing/web-platform/tests/html/semantics/invokers | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/html/semantics/invokers')
12 files changed, 1668 insertions, 0 deletions
diff --git a/testing/web-platform/tests/html/semantics/invokers/idlharness.tentative.html b/testing/web-platform/tests/html/semantics/invokers/idlharness.tentative.html new file mode 100644 index 0000000000..b215f65813 --- /dev/null +++ b/testing/web-platform/tests/html/semantics/invokers/idlharness.tentative.html @@ -0,0 +1,16 @@ +<!doctype html> +<meta charset="utf-8" /> +<meta name="author" title="Keith Cirkel" href="mailto:keithamus@github.com" /> +<link rel="help" href="https://open-ui.org/components/invokers.explainer/" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/WebIDLParser.js"></script> +<script src="/resources/idlharness.js"></script> + +<script> + idl_test(["invokers.tentative"], ["html", "dom"], (idl_array) => { + idl_array.add_objects({ + InvokeEvent: ['new InvokeEvent("invoke")'], + }); + }); +</script> diff --git a/testing/web-platform/tests/html/semantics/invokers/invokeelement-interface.tentative.html b/testing/web-platform/tests/html/semantics/invokers/invokeelement-interface.tentative.html new file mode 100644 index 0000000000..b003daf20d --- /dev/null +++ b/testing/web-platform/tests/html/semantics/invokers/invokeelement-interface.tentative.html @@ -0,0 +1,93 @@ +<!doctype html> +<meta charset="utf-8" /> +<meta name="author" title="Keith Cirkel" href="mailto:keithamus@github.com" /> +<link rel="help" href="https://open-ui.org/components/invokers.explainer/" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<button id="invoker" invoketarget="invokee"></button> +<div id="invokee"></div> + +<script> + test(function () { + assert_equals(invoker.invokeTargetElement, invokee); + }, "invokeTargetElement reflects invokee HTML element"); + + test(function () { + const div = document.body.appendChild(document.createElement("div")); + invoker.invokeTargetElement = div; + assert_equals(invoker.invokeTargetElement, div); + assert_equals(invoker.getAttribute("invoketarget"), ""); + assert_false(invoker.hasAttribute("invokeaction")); + }, "invokeTargetElement reflects set value"); + + test(function () { + const host = document.body.appendChild(document.createElement("div")); + const shadow = host.attachShadow({ mode: "open" }); + const button = shadow.appendChild(document.createElement("button")); + button.invokeTargetElement = invokee; + assert_equals(button.invokeTargetElement, invokee); + assert_equals(invoker.getAttribute("invoketarget"), ""); + assert_false(invoker.hasAttribute("invokeaction")); + }, "invokeTargetElement reflects set value across shadow root into light dom"); + + test(function () { + const host = document.body.appendChild(document.createElement("div")); + const shadow = host.attachShadow({ mode: "open" }); + const div = shadow.appendChild(document.createElement("div")); + invoker.invokeTargetElement = div; + assert_equals(invoker.invokeTargetElement, null); + assert_equals(invoker.getAttribute("invoketarget"), ""); + assert_false(invoker.hasAttribute("invokeaction")); + }, "invokeTargetElement does not reflect set value inside shadowroot"); + + test(function () { + assert_throws_js( + TypeError, + function () { + invoker.invokeTargetElement = {}; + }, + "invokeTargetElement attribute must be an instance of Element", + ); + }, "invokeTargetElement throws error on assignment of non Element"); + + test(function () { + assert_false(invoker.hasAttribute("invokeaction")); + assert_equals(invoker.invokeAction, "auto"); + }, "invokeAction reflects 'auto' when attribute not present"); + + test(function () { + invoker.setAttribute("invokeaction", ""); + assert_equals(invoker.getAttribute("invokeaction"), ""); + assert_equals(invoker.invokeAction, "auto"); + }, "invokeAction reflects 'auto' when attribute empty"); + + test(function () { + invoker.invokeAction = "fooBarBaz"; + assert_equals(invoker.getAttribute("invokeaction"), "fooBarBaz"); + assert_equals(invoker.invokeAction, "fooBarBaz"); + }, "invokeAction reflects same casing"); + + test(function () { + invoker.invokeAction = ""; + assert_equals(invoker.getAttribute("invokeaction"), ""); + assert_equals(invoker.invokeAction, "auto"); + }, "invokeAction reflects 'auto' when attribute empty 2"); + + test(function () { + invoker.invokeAction = [1, 2, 3]; + assert_equals(invoker.getAttribute("invokeaction"), "1,2,3"); + assert_equals(invoker.invokeAction, "1,2,3"); + }, "invokeAction reflects tostring value"); + + test(function () { + invoker.invokeAction = []; + assert_equals(invoker.getAttribute("invokeaction"), ""); + assert_equals(invoker.invokeAction, "auto"); + }, "invokeAction reflects 'auto' when attribute set to []"); + + test(function () { + invoker.invokeAction = {}; + assert_equals(invoker.invokeAction, "[object Object]"); + }, "invokeAction reflects tostring value 2"); +</script> diff --git a/testing/web-platform/tests/html/semantics/invokers/invokeevent-dispatch-shadow.tentative.html b/testing/web-platform/tests/html/semantics/invokers/invokeevent-dispatch-shadow.tentative.html new file mode 100644 index 0000000000..84337d5723 --- /dev/null +++ b/testing/web-platform/tests/html/semantics/invokers/invokeevent-dispatch-shadow.tentative.html @@ -0,0 +1,104 @@ +<!doctype html> +<meta charset="utf-8" /> +<meta name="author" title="Keith Cirkel" href="mailto:keithamus@github.com" /> +<link rel="help" href="https://open-ui.org/components/invokers.explainer/" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-actions.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="resources/invoker-utils.js"></script> + +<div id="div"></div> +<button id="button"></button> + +<script> + test(function () { + const host = document.createElement("div"); + const child = host.appendChild(document.createElement("p")); + const shadow = host.attachShadow({ mode: "closed" }); + const slot = shadow.appendChild(document.createElement("slot")); + let childEvent = null; + let childEventTarget = null; + let childEventInvoker = null; + let hostEvent = null; + let hostEventTarget = null; + let hostEventInvoker = null; + slot.addEventListener( + "invoke", + (e) => { + childEvent = e; + childEventTarget = e.target; + childEventInvoker = e.invoker; + }, + { once: true }, + ); + host.addEventListener( + "invoke", + (e) => { + hostEvent = e; + hostEventTarget = e.target; + hostEventInvoker = e.invoker; + }, + { once: true }, + ); + const event = new InvokeEvent("invoke", { + bubbles: true, + invoker: slot, + composed: true, + }); + slot.dispatchEvent(event); + assert_true(childEvent instanceof InvokeEvent, "slot saw invoke event"); + assert_equals( + childEventTarget, + slot, + "target is child inside shadow boundary", + ); + assert_equals( + childEventInvoker, + slot, + "invoker is child inside shadow boundary", + ); + assert_equals( + hostEvent, + childEvent, + "event dispatch propagates across shadow boundary", + ); + assert_equals( + hostEventTarget, + host, + "target is retargeted to shadowroot host", + ); + assert_equals( + hostEventInvoker, + host, + "invoker is retargeted to shadowroot host", + ); + }, "InvokeEvent propagates across shadow boundaries retargeting invoker"); + + test(function (t) { + const host = document.createElement("div"); + document.body.append(host); + t.add_cleanup(() => host.remove()); + const shadow = host.attachShadow({ mode: "open" }); + const button = shadow.appendChild(document.createElement("button")); + const invokee = host.appendChild(document.createElement("div")); + button.invokeTargetElement = invokee; + let event = null; + let eventTarget = null; + let eventInvoker = null; + invokee.addEventListener( + "invoke", + (e) => { + event = e; + eventTarget = e.target; + eventInvoker = e.invoker; + }, + { once: true }, + ); + button.click(); + assert_true(event instanceof InvokeEvent); + assert_equals(eventTarget, invokee, "target is invokee"); + assert_equals(eventInvoker, host, "invoker is host"); + }, "cross shadow InvokeEvent retargets invoker to host element"); +</script> diff --git a/testing/web-platform/tests/html/semantics/invokers/invokeevent-interface.tentative.html b/testing/web-platform/tests/html/semantics/invokers/invokeevent-interface.tentative.html new file mode 100644 index 0000000000..82910b3d44 --- /dev/null +++ b/testing/web-platform/tests/html/semantics/invokers/invokeevent-interface.tentative.html @@ -0,0 +1,166 @@ +<!doctype html> +<meta charset="utf-8" /> +<meta name="author" title="Keith Cirkel" href="mailto:keithamus@github.com" /> +<link rel="help" href="https://open-ui.org/components/invokers.explainer/" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-actions.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="resources/invoker-utils.js"></script> + +<div id="div"></div> +<button id="button"></button> + +<script> + test(function () { + const event = new InvokeEvent("test"); + assert_equals(event.action, "auto"); + assert_readonly(event, "action", "readonly attribute value"); + }, "action is a readonly defaulting to 'auto'"); + + test(function () { + const event = new InvokeEvent("test"); + assert_equals(event.invoker, null); + assert_readonly(event, "invoker", "readonly attribute value"); + }, "invoker is readonly defaulting to null"); + + test(function () { + const event = new InvokeEvent("test", { action: "sAmPle" }); + assert_equals(event.action, "sAmPle"); + }, "action reflects initialized attribute"); + + test(function () { + const event = new InvokeEvent("test", { action: undefined }); + assert_equals(event.action, "auto"); + }, "action set to undefined"); + + test(function () { + const event = new InvokeEvent("test", { action: null }); + assert_equals(event.action, "null"); + }, "action set to null"); + + test(function () { + const event = new InvokeEvent("test", { action: false }); + assert_equals(event.action, "false"); + }, "action set to false"); + + test(function () { + const event = new InvokeEvent("test", { action: "" }); + assert_equals(event.action, ""); + }, "action explicitly set to empty string"); + + test(function () { + const event = new InvokeEvent("test", { action: true }); + assert_equals(event.action, "true"); + }, "action set to true"); + + test(function () { + const event = new InvokeEvent("test", { action: 0.5 }); + assert_equals(event.action, "0.5"); + }, "action set to a number"); + + test(function () { + const event = new InvokeEvent("test", { action: [] }); + assert_equals(event.action, ""); + }, "action set to []"); + + test(function () { + const event = new InvokeEvent("test", { action: [1, 2, 3] }); + assert_equals(event.action, "1,2,3"); + }, "action set to [1, 2, 3]"); + + test(function () { + const event = new InvokeEvent("test", { action: { sample: 0.5 } }); + assert_equals(event.action, "[object Object]"); + }, "action set to an object"); + + test(function () { + const event = new InvokeEvent("test", { + action: { + toString() { + return "sample"; + }, + }, + }); + assert_equals(event.action, "sample"); + }, "action set to an object with a toString function"); + + test(function () { + const eventInit = { action: "sample", invoker: document.body }; + const event = new InvokeEvent("test", eventInit); + assert_equals(event.action, "sample"); + assert_equals(event.invoker, document.body); + }, "InvokeEventInit properties set value"); + + test(function () { + const eventInit = { + action: "open", + invoker: document.getElementById("div"), + }; + const event = new InvokeEvent("beforetoggle", eventInit); + assert_equals(event.action, "open"); + assert_equals(event.invoker, document.getElementById("div")); + }, "InvokeEventInit properties set value 2"); + + test(function () { + const eventInit = { + action: "closed", + invoker: document.getElementById("button"), + }; + const event = new InvokeEvent("toggle", eventInit); + assert_equals(event.action, "closed"); + assert_equals(event.invoker, document.getElementById("button")); + }, "InvokeEventInit properties set value 3"); + + test(function () { + const event = new InvokeEvent("test", { invoker: undefined }); + assert_equals(event.invoker, null); + }, "invoker set to undefined"); + + test(function () { + const event = new InvokeEvent("test", { invoker: null }); + assert_equals(event.invoker, null); + }, "invoker set to null"); + + test(function () { + assert_throws_js( + TypeError, + function () { + new InvokeEvent("test", { invoker: false }); + }, + "invoker is not an object", + ); + }, "invoker set to false"); + + test(function () { + assert_throws_js( + TypeError, + function () { + const event = new InvokeEvent("test", { invoker: true }); + }, + "invoker is not an object", + ); + }, "invoker set to true"); + + test(function () { + assert_throws_js( + TypeError, + function () { + const event = new InvokeEvent("test", { invoker: {} }); + }, + "invoker is not an object", + ); + }, "invoker set to {}"); + + test(function () { + assert_throws_js( + TypeError, + function () { + const eventInit = { action: "closed", invoker: new XMLHttpRequest() }; + const event = new InvokeEvent("toggle", eventInit); + }, + "invoker is not an Element", + ); + }, "invoker set to non-Element EventTarget"); +</script> diff --git a/testing/web-platform/tests/html/semantics/invokers/invoketarget-button-event-dispatch.tentative.html b/testing/web-platform/tests/html/semantics/invokers/invoketarget-button-event-dispatch.tentative.html new file mode 100644 index 0000000000..b19c1d3adc --- /dev/null +++ b/testing/web-platform/tests/html/semantics/invokers/invoketarget-button-event-dispatch.tentative.html @@ -0,0 +1,119 @@ +<!doctype html> +<meta charset="utf-8" /> +<meta name="author" title="Keith Cirkel" href="mailto:keithamus@github.com" /> +<link rel="help" href="https://open-ui.org/components/invokers.explainer/" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-actions.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="resources/invoker-utils.js"></script> + +<div id="invokee"></div> +<button id="invokerbutton" invoketarget="invokee"></button> + +<script> + promise_test(async function (t) { + let event = null; + invokee.addEventListener("invoke", (e) => (event = e), { once: true }); + await clickOn(invokerbutton); + assert_true(event instanceof InvokeEvent, "event is InvokeEvent"); + assert_equals(event.type, "invoke", "type"); + assert_equals(event.bubbles, false, "bubbles"); + assert_equals(event.composed, true, "composed"); + assert_equals(event.isTrusted, true, "isTrusted"); + assert_equals(event.action, "auto", "action"); + assert_equals(event.target, invokee, "target"); + assert_equals(event.invoker, invokerbutton, "invoker"); + }, "event dispatches on click"); + + promise_test(async function (t) { + let event = null; + invokee.addEventListener("invoke", (e) => (event = e), { once: true }); + invokerbutton.invokeAction = "fooBar"; + await clickOn(invokerbutton); + assert_true(event instanceof InvokeEvent, "event is InvokeEvent"); + assert_equals(event.type, "invoke", "type"); + assert_equals(event.bubbles, false, "bubbles"); + assert_equals(event.composed, true, "composed"); + assert_equals(event.isTrusted, true, "isTrusted"); + assert_equals(event.action, "fooBar", "action"); + assert_equals(event.target, invokee, "target"); + assert_equals(event.invoker, invokerbutton, "invoker"); + }, "event action is set to invokeAction"); + + promise_test(async function (t) { + let event = null; + invokee.addEventListener("invoke", (e) => (event = e), { once: true }); + invokerbutton.setAttribute("invokeaction", "BaRbAz"); + await clickOn(invokerbutton); + assert_true(event instanceof InvokeEvent, "event is InvokeEvent"); + assert_equals(event.type, "invoke", "type"); + assert_equals(event.bubbles, false, "bubbles"); + assert_equals(event.composed, true, "composed"); + assert_equals(event.isTrusted, true, "isTrusted"); + assert_equals(event.action, "BaRbAz", "action"); + assert_equals(event.target, invokee, "target"); + assert_equals(event.invoker, invokerbutton, "invoker"); + }, "event action is set to invokeaction attribute"); + + promise_test(async function (t) { + let called = false; + invokerbutton.addEventListener( + "click", + (event) => { + event.preventDefault(); + }, + { once: true }, + ); + invokee.addEventListener( + "invoke", + (event) => { + called = true; + }, + { once: true }, + ); + await clickOn(invokerbutton); + assert_false(called, "event was not called"); + }, "event does not dispatch if click:preventDefault is called"); + + promise_test(async function (t) { + t.add_cleanup(() => invokerbutton.removeAttribute('disabled')); + let called = false; + invokee.addEventListener( + "invoke", + (event) => { + called = true; + }, + { once: true }, + ); + invokerbutton.setAttribute("disabled", ""); + await clickOn(invokerbutton); + assert_false(called, "event was not called"); + }, "event does not dispatch if invoker is disabled"); + + promise_test(async function (t) { + svgInvokee = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); + t.add_cleanup(() => { + invokerbutton.invokeTargetElement = invokee; + svgInvokee.remove(); + }); + document.body.append(svgInvokee); + let called = false; + assert_false(svgInvokee instanceof HTMLElement); + assert_true(svgInvokee instanceof Element); + let eventInvoker = null; + svgInvokee.addEventListener( + "invoke", + (event) => { + eventInvoker = event.invoker; + called = true; + }, + { once: true }, + ); + invokerbutton.invokeTargetElement = svgInvokee; + await clickOn(invokerbutton); + assert_true(called, "event was called"); + assert_true(eventInvoker == svgInvokee, "event invoker is set to right element"); + }, "event dispatches if invoker is non-HTML Element"); +</script> diff --git a/testing/web-platform/tests/html/semantics/invokers/invoketarget-fullscreen-behavior.tentative.html b/testing/web-platform/tests/html/semantics/invokers/invoketarget-fullscreen-behavior.tentative.html new file mode 100644 index 0000000000..b72020283e --- /dev/null +++ b/testing/web-platform/tests/html/semantics/invokers/invoketarget-fullscreen-behavior.tentative.html @@ -0,0 +1,175 @@ +<!doctype html> +<meta charset="utf-8" /> +<meta name="author" title="Luke Warlow" href="mailto:luke@warlow.dev" /> +<link rel="help" href="https://open-ui.org/components/invokers.explainer/" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-actions.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="resources/invoker-utils.js"></script> + +<div id="invokee"> + Fullscreen content + <button id="invokerbutton" invoketarget="invokee"></button> +</div> + +<script> + // auto + + promise_test(async function (t) { + t.add_cleanup(async () => { + if (document.fullscreenElement) await document.exitFullscreen(); + }); + assert_false(invokee.matches(":fullscreen")); + await clickOn(invokerbutton); + assert_false(invokee.matches(":fullscreen")); + }, "invoking div with auto action is no-op"); + + // toggleFullscreen + + promise_test(async function (t) { + t.add_cleanup(async () => { + invokerbutton.removeAttribute("invokeaction"); + if (document.fullscreenElement) await document.exitFullscreen(); + }); + assert_false(invokee.matches(":fullscreen")); + invokerbutton.setAttribute("invokeaction", "toggleFullscreen"); + await clickOn(invokerbutton); + assert_true(invokee.matches(":fullscreen")); + }, "invoking div with toggleFullscreen action makes div fullscreen"); + + promise_test(async function (t) { + t.add_cleanup(async () => { + invokerbutton.removeAttribute("invokeaction"); + if (document.fullscreenElement) await document.exitFullscreen(); + }); + assert_false(invokee.matches(":fullscreen")); + invokerbutton.setAttribute("invokeaction", "toggleFullscreen"); + invokerbutton.click(); + assert_false(invokee.matches(":fullscreen")); + }, "invoking div with toggleFullscreen action (without user activation) is a no-op"); + + promise_test(async function (t) { + t.add_cleanup(async () => { + invokerbutton.removeAttribute("invokeaction"); + if (document.fullscreenElement) await document.exitFullscreen(); + }); + invokee.addEventListener("invoke", (e) => e.preventDefault(), { + once: true, + }); + assert_false(invokee.matches(":fullscreen")); + invokerbutton.setAttribute("invokeaction", "toggleFullscreen"); + await clickOn(invokerbutton); + assert_false(invokee.matches(":fullscreen")); + }, "invoking div with toggleFullscreen action and preventDefault is a no-op"); + + promise_test(async function (t) { + t.add_cleanup(async () => { + invokerbutton.removeAttribute("invokeaction"); + if (document.fullscreenElement) await document.exitFullscreen(); + }); + invokerbutton.setAttribute("invokeaction", "toggleFullscreen"); + await test_driver.bless('go fullscreen'); + await invokee.requestFullscreen(); + assert_true(invokee.matches(":fullscreen")); + await clickOn(invokerbutton); + assert_false(invokee.matches(":fullscreen")); + }, "invoking fullscreen div with toggleFullscreen action exits fullscreen"); + + promise_test(async function (t) { + t.add_cleanup(async () => { + invokerbutton.removeAttribute("invokeaction"); + if (document.fullscreenElement) await document.exitFullscreen(); + }); + invokerbutton.setAttribute("invokeaction", "tOgGlEFullscreen"); + await test_driver.bless('go fullscreen'); + await invokee.requestFullscreen(); + assert_true(invokee.matches(":fullscreen")); + await clickOn(invokerbutton); + assert_false(invokee.matches(":fullscreen")); + }, "invoking fullscreen div with toggleFullscreen (case-insensitive) action exits fullscreen"); + + // requestFullscreen + + promise_test(async function (t) { + t.add_cleanup(async () => { + invokerbutton.removeAttribute("invokeaction"); + if (document.fullscreenElement) await document.exitFullscreen(); + }); + assert_false(invokee.matches(":fullscreen")); + invokerbutton.setAttribute("invokeaction", "requestFullscreen"); + await clickOn(invokerbutton); + assert_true(invokee.matches(":fullscreen")); + }, "invoking div with requestFullscreen action makes div fullscreen"); + + promise_test(async function (t) { + t.add_cleanup(async () => { + invokerbutton.removeAttribute("invokeaction"); + if (document.fullscreenElement) await document.exitFullscreen(); + }); + invokee.addEventListener("invoke", (e) => e.preventDefault(), { + once: true, + }); + assert_false(invokee.matches(":fullscreen")); + invokerbutton.setAttribute("invokeaction", "requestFullscreen"); + await clickOn(invokerbutton); + assert_false(invokee.matches(":fullscreen")); + }, "invoking div with requestFullscreen action and preventDefault is a no-op"); + + promise_test(async function (t) { + t.add_cleanup(async () => { + invokerbutton.removeAttribute("invokeaction"); + if (document.fullscreenElement) await document.exitFullscreen(); + }); + invokerbutton.setAttribute("invokeaction", "requestFullscreen"); + await test_driver.bless('go fullscreen'); + await invokee.requestFullscreen(); + assert_true(invokee.matches(":fullscreen")); + await clickOn(invokerbutton); + assert_true(invokee.matches(":fullscreen")); + }, "invoking fullscreen div with requestFullscreen action is a no-op"); + + // exitFullscreen + + promise_test(async function (t) { + t.add_cleanup(async () => { + invokerbutton.removeAttribute("invokeaction"); + if (document.fullscreenElement) await document.exitFullscreen(); + }); + assert_false(invokee.matches(":fullscreen")); + invokerbutton.setAttribute("invokeaction", "exitFullscreen"); + await clickOn(invokerbutton); + assert_false(invokee.matches(":fullscreen")); + }, "invoking div with exitFullscreen action is a no-op"); + + promise_test(async function (t) { + t.add_cleanup(async () => { + invokerbutton.removeAttribute("invokeaction"); + if (document.fullscreenElement) await document.exitFullscreen(); + }); + invokerbutton.setAttribute("invokeaction", "exitFullscreen"); + await test_driver.bless('go fullscreen'); + await invokee.requestFullscreen(); + assert_true(invokee.matches(":fullscreen")); + await clickOn(invokerbutton); + assert_false(invokee.matches(":fullscreen")); + }, "invoking fullscreen div with exitFullscreen action exits fullscreen"); + + promise_test(async function (t) { + t.add_cleanup(async () => { + invokerbutton.removeAttribute("invokeaction"); + if (document.fullscreenElement) await document.exitFullscreen(); + }); + invokee.addEventListener("invoke", (e) => e.preventDefault(), { + once: true, + }); + invokerbutton.setAttribute("invokeaction", "exitFullscreen"); + await test_driver.bless('go fullscreen'); + await invokee.requestFullscreen(); + assert_true(invokee.matches(":fullscreen")); + await clickOn(invokerbutton); + assert_true(invokee.matches(":fullscreen")); + }, "invoking fullscreen div with exitFullscreen action and preventDefault is a no-op"); + +</script> diff --git a/testing/web-platform/tests/html/semantics/invokers/invoketarget-generic-eventtarget-crash.tentative.html b/testing/web-platform/tests/html/semantics/invokers/invoketarget-generic-eventtarget-crash.tentative.html new file mode 100644 index 0000000000..b2179640dd --- /dev/null +++ b/testing/web-platform/tests/html/semantics/invokers/invoketarget-generic-eventtarget-crash.tentative.html @@ -0,0 +1,18 @@ +<!doctype html> +<meta charset="utf-8" /> +<meta name="author" title="Keith Cirkel" href="mailto:wpt@keithcirkel.co.uk" /> +<link rel="help" href="https://open-ui.org/components/invokers.explainer/" /> +<div id="invoker"></div> + +<script type="module"> + const invokeEvent = new InvokeEvent('invoke', { bubbles: true }); + document.body.addEventListener('invoke', e => { + e.invoker.toString(); + }); + invoker.addEventListener('invoke', e => { + e.invoker.toString(); + }); + invoker.dispatchEvent(invokeEvent); + await Promise.resolve(); + invokeEvent.invoker.toString(); +</script> diff --git a/testing/web-platform/tests/html/semantics/invokers/invoketarget-on-audio-behavior.tentative.html b/testing/web-platform/tests/html/semantics/invokers/invoketarget-on-audio-behavior.tentative.html new file mode 100644 index 0000000000..f3abeae165 --- /dev/null +++ b/testing/web-platform/tests/html/semantics/invokers/invoketarget-on-audio-behavior.tentative.html @@ -0,0 +1,285 @@ +<!doctype html> +<meta charset="utf-8" /> +<meta name="author" title="Luke Warlow" href="mailto:luke@warlow.dev" /> +<link rel="help" href="https://open-ui.org/components/invokers.explainer/" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-actions.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="resources/invoker-utils.js"></script> + +<audio controls id="invokee" src="/media/sound_5.mp3"></audio> +<button id="invokerbutton" invoketarget="invokee"></button> + +<script> + // auto + + promise_test(async function (t) { + t.add_cleanup(async () => { + invokerbutton.removeAttribute("invokeaction"); + invokee.pause(); + invokee.currentTime = 0; + invokee.muted = false; + }); + assert_true(invokee.paused); + invokerbutton.setAttribute("invokeaction", ""); + await clickOn(invokerbutton); + await new Promise(resolve => { + requestAnimationFrame(resolve); + }); + assert_true(invokee.paused); + }, "invoking audio with auto action is no-op"); + + // playpause + + promise_test(async function (t) { + t.add_cleanup(async () => { + invokerbutton.removeAttribute("invokeaction"); + invokee.pause(); + invokee.currentTime = 0; + invokee.muted = false; + }); + assert_true(invokee.paused); + invokerbutton.setAttribute("invokeaction", "playpause"); + await clickOn(invokerbutton); + await new Promise(resolve => { + requestAnimationFrame(resolve); + }); + assert_false(invokee.paused); + }, "invoking audio with playpause action makes audio play"); + + promise_test(async function (t) { + t.add_cleanup(async () => { + invokerbutton.removeAttribute("invokeaction"); + invokee.pause(); + invokee.currentTime = 0; + invokee.muted = false; + }); + assert_true(invokee.paused); + invokerbutton.setAttribute("invokeaction", "playpause"); + invokerbutton.click(); + await new Promise(resolve => { + requestAnimationFrame(resolve); + }); + assert_false(invokee.paused); + }, "invoking audio with playpause action (without user activation) is a no-op"); + + promise_test(async function (t) { + t.add_cleanup(async () => { + invokerbutton.removeAttribute("invokeaction"); + invokee.pause(); + invokee.currentTime = 0; + invokee.muted = false; + }); + invokee.addEventListener("invoke", (e) => e.preventDefault(), { + once: true, + }); + assert_true(invokee.paused); + invokerbutton.setAttribute("invokeaction", "playpause"); + await clickOn(invokerbutton); + await new Promise(resolve => { + requestAnimationFrame(resolve); + }); + assert_true(invokee.paused); + }, "invoking audio with playpause action and preventDefault is a no-op"); + + promise_test(async function (t) { + t.add_cleanup(async () => { + invokerbutton.removeAttribute("invokeaction"); + invokee.pause(); + invokee.currentTime = 0; + invokee.muted = false; + }); + await test_driver.bless('play audio'); + invokee.play(); + assert_false(invokee.paused); + invokerbutton.setAttribute("invokeaction", "playpause"); + await clickOn(invokerbutton); + await new Promise(resolve => { + requestAnimationFrame(resolve); + }); + assert_true(invokee.paused); + }, "invoking playing audio with playpause action pauses it"); + + // play + + promise_test(async function (t) { + t.add_cleanup(async () => { + invokerbutton.removeAttribute("invokeaction"); + invokee.pause(); + invokee.currentTime = 0; + invokee.muted = false; + }); + assert_true(invokee.paused); + invokerbutton.setAttribute("invokeaction", "play"); + await clickOn(invokerbutton); + await new Promise(resolve => { + requestAnimationFrame(resolve); + }); + assert_false(invokee.paused); + }, "invoking audio with play action makes audio play"); + + promise_test(async function (t) { + t.add_cleanup(async () => { + invokerbutton.removeAttribute("invokeaction"); + invokee.pause(); + invokee.currentTime = 0; + invokee.muted = false; + }); + assert_true(invokee.paused); + invokerbutton.setAttribute("invokeaction", "play"); + invokerbutton.click(); + await new Promise(resolve => { + requestAnimationFrame(resolve); + }); + assert_false(invokee.paused); + }, "invoking audio with play action (without user activation) is a no-op"); + + promise_test(async function (t) { + t.add_cleanup(async () => { + invokerbutton.removeAttribute("invokeaction"); + invokee.pause(); + invokee.currentTime = 0; + invokee.muted = false; + }); + invokee.addEventListener("invoke", (e) => e.preventDefault(), { + once: true, + }); + assert_true(invokee.paused); + invokerbutton.setAttribute("invokeaction", "play"); + await clickOn(invokerbutton); + await new Promise(resolve => { + requestAnimationFrame(resolve); + }); + assert_true(invokee.paused); + }, "invoking audio with play action and preventDefault is a no-op"); + + promise_test(async function (t) { + t.add_cleanup(async () => { + invokerbutton.removeAttribute("invokeaction"); + invokee.pause(); + invokee.currentTime = 0; + invokee.muted = false; + }); + await test_driver.bless('play audio'); + invokee.play(); + assert_false(invokee.paused); + invokerbutton.setAttribute("invokeaction", "play"); + await clickOn(invokerbutton); + await new Promise(resolve => { + requestAnimationFrame(resolve); + }); + assert_false(invokee.paused); + }, "invoking playing audio with play action is a no-op"); + + // pause + + promise_test(async function (t) { + t.add_cleanup(async () => { + invokerbutton.removeAttribute("invokeaction"); + invokee.pause(); + invokee.currentTime = 0; + invokee.muted = false; + }); + assert_true(invokee.paused); + invokerbutton.setAttribute("invokeaction", "pause"); + await clickOn(invokerbutton); + await new Promise(resolve => { + requestAnimationFrame(resolve); + }); + assert_true(invokee.paused); + }, "invoking audio with pause action is a no-op"); + + promise_test(async function (t) { + t.add_cleanup(async () => { + invokerbutton.removeAttribute("invokeaction"); + invokee.pause(); + invokee.currentTime = 0; + invokee.muted = false; + }); + invokee.addEventListener("invoke", (e) => e.preventDefault(), { + once: true, + }); + assert_true(invokee.paused); + invokerbutton.setAttribute("invokeaction", "pause"); + await clickOn(invokerbutton); + await new Promise(resolve => { + requestAnimationFrame(resolve); + }); + assert_true(invokee.paused); + }, "invoking audio with pause action and preventDefault is a no-op"); + + promise_test(async function (t) { + t.add_cleanup(async () => { + invokerbutton.removeAttribute("invokeaction"); + invokee.pause(); + invokee.currentTime = 0; + invokee.muted = false; + }); + await test_driver.bless('play audio'); + invokee.play(); + assert_false(invokee.paused); + invokerbutton.setAttribute("invokeaction", "pause"); + await clickOn(invokerbutton); + await new Promise(resolve => { + requestAnimationFrame(resolve); + }); + assert_true(invokee.paused); + }, "invoking playing audio with pause action makes it pause"); + + // mute + + promise_test(async function (t) { + t.add_cleanup(async () => { + invokerbutton.removeAttribute("invokeaction"); + invokee.pause(); + invokee.currentTime = 0; + invokee.muted = false; + }); + assert_false(invokee.muted); + invokerbutton.setAttribute("invokeaction", "toggleMuted"); + await clickOn(invokerbutton); + await new Promise(resolve => { + requestAnimationFrame(resolve); + }); + assert_true(invokee.muted); + }, "invoking audio with toggleMuted action mutes it"); + + promise_test(async function (t) { + t.add_cleanup(async () => { + invokerbutton.removeAttribute("invokeaction"); + invokee.pause(); + invokee.currentTime = 0; + invokee.muted = false; + }); + invokee.addEventListener("invoke", (e) => e.preventDefault(), { + once: true, + }); + assert_false(invokee.muted); + invokerbutton.setAttribute("invokeaction", "toggleMuted"); + await clickOn(invokerbutton); + await new Promise(resolve => { + requestAnimationFrame(resolve); + }); + assert_false(invokee.muted); + }, "invoking audio with toggleMuted action and preventDefault is a no-op"); + + promise_test(async function (t) { + t.add_cleanup(async () => { + invokerbutton.removeAttribute("invokeaction"); + invokee.pause(); + invokee.currentTime = 0; + invokee.muted = false; + }); + invokee.muted = true; + assert_true(invokee.muted); + invokerbutton.setAttribute("invokeaction", "toggleMuted"); + await clickOn(invokerbutton); + await new Promise(resolve => { + requestAnimationFrame(resolve); + }); + assert_false(invokee.muted); + }, "invoking muted audio with toggleMuted action unmutes it"); + +</script> diff --git a/testing/web-platform/tests/html/semantics/invokers/invoketarget-on-details-behavior.tentative.html b/testing/web-platform/tests/html/semantics/invokers/invoketarget-on-details-behavior.tentative.html new file mode 100644 index 0000000000..c6735e2611 --- /dev/null +++ b/testing/web-platform/tests/html/semantics/invokers/invoketarget-on-details-behavior.tentative.html @@ -0,0 +1,218 @@ +<!doctype html> +<meta charset="utf-8" /> +<meta name="author" title="Luke Warlow" href="mailto:luke@warlow.dev" /> +<link rel="help" href="https://open-ui.org/components/invokers.explainer/" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-actions.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="resources/invoker-utils.js"></script> + +<details id="invokee"> + Details Contents +</details> +<button id="invokerbutton" invoketarget="invokee"></button> + +<script> + // auto + + promise_test(async function (t) { + assert_false(invokee.matches("[open]")); + await clickOn(invokerbutton); + t.add_cleanup(() => invokee.removeAttribute('open')); + assert_true(invokee.matches("[open]")); + }, "invoking closed details with auto action opens"); + + promise_test(async function (t) { + assert_false(invokee.matches("[open]")); + invokee.addEventListener("invoke", (e) => e.preventDefault(), { + once: true, + }); + await clickOn(invokerbutton); + t.add_cleanup(() => invokee.removeAttribute('open')); + assert_false(invokee.matches("[open]")); + }, "invoking closed details with auto action and preventDefault does not open"); + + promise_test(async function (t) { + invokee.setAttribute('open', ''); + assert_true(invokee.matches("[open]")); + await clickOn(invokerbutton); + assert_false(invokee.matches("[open]")); + }, "invoking open details with auto action closes"); + + promise_test(async function (t) { + invokee.setAttribute('open', ''); + t.add_cleanup(() => invokee.removeAttribute('open')); + invokee.addEventListener("invoke", (e) => e.preventDefault(), { + once: true, + }); + assert_true(invokee.matches("[open]")); + await clickOn(invokerbutton); + assert_true(invokee.matches("[open]")); + }, "invoking open details with auto action and preventDefault does not close"); + + promise_test(async function (t) { + t.add_cleanup(() => invokee.removeAttribute('open')); + invokee.addEventListener("invoke", (e) => { + invokee.setAttribute('open', ''); + }, { + once: true, + }); + assert_false(invokee.matches("[open]")); + await clickOn(invokerbutton); + assert_false(invokee.matches("[open]")); + }, "invoking details with auto action where event listener opens leads to a closed details"); + + promise_test(async function (t) { + invokee.setAttribute('open', ''); + t.add_cleanup(() => invokee.removeAttribute('open')); + invokee.addEventListener("invoke", (e) => { + invokee.removeAttribute('open'); + }, { + once: true, + }); + assert_true(invokee.matches("[open]")); + await clickOn(invokerbutton); + assert_true(invokee.matches("[open]")); + }, "invoking open details with auto action where event listener closes leads to an open details"); + + // toggle + + promise_test(async function (t) { + assert_false(invokee.matches("[open]")); + invokerbutton.setAttribute("invokeaction", "toggle"); + t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction")); + await clickOn(invokerbutton); + t.add_cleanup(() => invokee.removeAttribute('open')); + assert_true(invokee.matches("[open]")); + }, "invoking closed details with toggle action opens"); + + promise_test(async function (t) { + assert_false(invokee.matches("[open]")); + invokerbutton.setAttribute("invokeaction", "tOgGlE"); + t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction")); + await clickOn(invokerbutton); + t.add_cleanup(() => invokee.removeAttribute('open')); + assert_true(invokee.matches("[open]")); + }, "invoking closed details with toggle (case-insensitive) action opens"); + + promise_test(async function (t) { + assert_false(invokee.matches("[open]")); + invokerbutton.setAttribute("invokeaction", "toggle"); + t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction")); + invokee.addEventListener("invoke", (e) => e.preventDefault(), { + once: true, + }); + await clickOn(invokerbutton); + t.add_cleanup(() => invokee.removeAttribute('open')); + assert_false(invokee.matches("[open]")); + }, "invoking closed details with toggle action and preventDefault does not open"); + + promise_test(async function (t) { + invokee.setAttribute('open', ''); + invokerbutton.setAttribute("invokeaction", "toggle"); + t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction")); + assert_true(invokee.matches("[open]")); + await clickOn(invokerbutton); + assert_false(invokee.matches("[open]")); + }, "invoking open details with toggle action closes"); + + promise_test(async function (t) { + invokee.setAttribute('open', ''); + t.add_cleanup(() => invokee.removeAttribute('open')); + invokerbutton.setAttribute("invokeaction", "toggle"); + t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction")); + invokee.addEventListener("invoke", (e) => e.preventDefault(), { + once: true, + }); + assert_true(invokee.matches("[open]")); + await clickOn(invokerbutton); + assert_true(invokee.matches("[open]")); + }, "invoking open details with toggle action and preventDefault does not close"); + + // open + + promise_test(async function (t) { + invokerbutton.setAttribute("invokeaction", "open"); + t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction")); + assert_false(invokee.matches("[open]")); + await clickOn(invokerbutton); + t.add_cleanup(() => invokee.removeAttribute('open')); + assert_true(invokee.matches("[open]")); + }, "invoking closed details with open action opens"); + + promise_test(async function (t) { + invokerbutton.setAttribute("invokeaction", "oPeN"); + t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction")); + assert_false(invokee.matches("[open]")); + await clickOn(invokerbutton); + t.add_cleanup(() => invokee.removeAttribute('open')); + assert_true(invokee.matches("[open]")); + }, "invoking closed details with open (case insensitive) action opens"); + + promise_test(async function (t) { + invokerbutton.setAttribute("invokeaction", "open"); + t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction")); + invokee.setAttribute('open', ''); + assert_true(invokee.matches("[open]")); + await clickOn(invokerbutton); + t.add_cleanup(() => invokee.removeAttribute('open')); + assert_true(invokee.matches("[open]")); + }, "invoking open details with open action is noop"); + + promise_test(async function (t) { + invokerbutton.setAttribute("invokeaction", "open"); + t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction")); + assert_false(invokee.matches("[open]")); + invokee.addEventListener("invoke", (e) => e.preventDefault(), { + once: true, + }); + await clickOn(invokerbutton); + t.add_cleanup(() => invokee.removeAttribute('open')); + assert_false(invokee.matches("[open]")); + }, "invoking closed popover with open action and preventDefault does not open"); + + // close + + promise_test(async function (t) { + invokerbutton.setAttribute("invokeaction", "close"); + t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction")); + assert_false(invokee.matches("[open]")); + await clickOn(invokerbutton); + assert_false(invokee.matches("[open]")); + }, "invoking closed details with close action is noop"); + + promise_test(async function (t) { + invokerbutton.setAttribute("invokeaction", "close"); + t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction")); + invokee.setAttribute('open', ''); + assert_true(invokee.matches("[open]")); + await clickOn(invokerbutton); + t.add_cleanup(() => invokee.removeAttribute('open')); + assert_false(invokee.matches("[open]")); + }, "invoking open details with close action closes"); + + promise_test(async function (t) { + invokerbutton.setAttribute("invokeaction", "cLoSe"); + t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction")); + invokee.setAttribute('open', ''); + assert_true(invokee.matches("[open]")); + await clickOn(invokerbutton); + t.add_cleanup(() => invokee.removeAttribute('open')); + assert_false(invokee.matches("[open]")); + }, "invoking open details with close (case insensitive) action closes"); + + promise_test(async function (t) { + invokerbutton.setAttribute("invokeaction", "close"); + t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction")); + invokee.setAttribute('open', ''); + t.add_cleanup(() => invokee.removeAttribute('open')); + assert_true(invokee.matches("[open]")); + invokee.addEventListener("invoke", (e) => e.preventDefault(), { + once: true, + }); + await clickOn(invokerbutton); + assert_true(invokee.matches("[open]")); + }, "invoking open details with close action with preventDefault does not close"); +</script> diff --git a/testing/web-platform/tests/html/semantics/invokers/invoketarget-on-popover-behavior.tentative.html b/testing/web-platform/tests/html/semantics/invokers/invoketarget-on-popover-behavior.tentative.html new file mode 100644 index 0000000000..03eba22285 --- /dev/null +++ b/testing/web-platform/tests/html/semantics/invokers/invoketarget-on-popover-behavior.tentative.html @@ -0,0 +1,209 @@ +<!doctype html> +<meta charset="utf-8" /> +<meta name="author" title="Keith Cirkel" href="mailto:keithamus@github.com" /> +<link rel="help" href="https://open-ui.org/components/invokers.explainer/" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-actions.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="resources/invoker-utils.js"></script> + +<div id="invokee" popover> + <button id="invokerbutton2" invoketarget="invokee"></button> +</div> +<button id="invokerbutton" invoketarget="invokee"></button> + +<script> + // auto + + promise_test(async function (t) { + assert_false(invokee.matches(":popover-open")); + await clickOn(invokerbutton); + t.add_cleanup(() => invokee.hidePopover()); + assert_true(invokee.matches(":popover-open")); + }, "invoking (as auto) closed popover opens"); + + promise_test(async function (t) { + assert_false(invokee.matches(":popover-open")); + invokee.addEventListener("invoke", (e) => e.preventDefault(), { + once: true, + }); + await clickOn(invokerbutton); + t.add_cleanup(() => invokee.hidePopover()); + assert_false(invokee.matches(":popover-open")); + }, "invoking (as auto) closed popover with preventDefault does not open"); + + promise_test(async function (t) { + invokee.showPopover(); + assert_true(invokee.matches(":popover-open")); + await clickOn(invokerbutton); + assert_false(invokee.matches(":popover-open")); + }, "invoking (as auto) open popover closes"); + + promise_test(async function (t) { + invokee.showPopover(); + assert_true(invokee.matches(":popover-open")); + await clickOn(invokerbutton2); + assert_false(invokee.matches(":popover-open")); + }, "invoking (as auto) from within open popover closes"); + + promise_test(async function (t) { + invokee.showPopover(); + t.add_cleanup(() => invokee.hidePopover()); + invokee.addEventListener("invoke", (e) => e.preventDefault(), { + once: true, + }); + assert_true(invokee.matches(":popover-open")); + await clickOn(invokerbutton2); + assert_true(invokee.matches(":popover-open")); + }, "invoking (as auto) open popover with preventDefault does not close"); + + // togglepopover + + promise_test(async function (t) { + assert_false(invokee.matches(":popover-open")); + invokerbutton.setAttribute("invokeaction", "togglepopover"); + t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction")); + await clickOn(invokerbutton); + t.add_cleanup(() => invokee.hidePopover()); + assert_true(invokee.matches(":popover-open")); + }, "invoking (as togglepopover) closed popover opens"); + + promise_test(async function (t) { + assert_false(invokee.matches(":popover-open")); + invokerbutton.setAttribute("invokeaction", "tOgGlEpOpOvEr"); + t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction")); + await clickOn(invokerbutton); + t.add_cleanup(() => invokee.hidePopover()); + assert_true(invokee.matches(":popover-open")); + }, "invoking (as togglepopover - case insensitive) closed popover opens"); + + promise_test(async function (t) { + assert_false(invokee.matches(":popover-open")); + invokerbutton.setAttribute("invokeaction", "togglepopover"); + t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction")); + invokee.addEventListener("invoke", (e) => e.preventDefault(), { + once: true, + }); + await clickOn(invokerbutton); + t.add_cleanup(() => invokee.hidePopover()); + assert_false(invokee.matches(":popover-open")); + }, "invoking (as togglepopover) closed popover with preventDefault does not open"); + + promise_test(async function (t) { + invokee.showPopover(); + invokerbutton2.setAttribute("invokeaction", "togglepopover"); + t.add_cleanup(() => invokerbutton2.removeAttribute("invokeaction")); + assert_true(invokee.matches(":popover-open")); + await clickOn(invokerbutton); + assert_false(invokee.matches(":popover-open")); + }, "invoking (as togglepopover) open popover closes"); + + promise_test(async function (t) { + invokee.showPopover(); + invokerbutton2.setAttribute("invokeaction", "togglepopover"); + t.add_cleanup(() => invokerbutton2.removeAttribute("invokeaction")); + assert_true(invokee.matches(":popover-open")); + await clickOn(invokerbutton2); + assert_false(invokee.matches(":popover-open")); + }, "invoking (as togglepopover) from within open popover closes"); + + promise_test(async function (t) { + invokee.showPopover(); + t.add_cleanup(() => invokee.hidePopover()); + invokerbutton2.setAttribute("invokeaction", "togglepopover"); + t.add_cleanup(() => invokerbutton2.removeAttribute("invokeaction")); + invokee.addEventListener("invoke", (e) => e.preventDefault(), { + once: true, + }); + assert_true(invokee.matches(":popover-open")); + await clickOn(invokerbutton2); + assert_true(invokee.matches(":popover-open")); + }, "invoking (as togglepopover) open popover with preventDefault does not close"); + + // showpopover + + promise_test(async function (t) { + invokerbutton.setAttribute("invokeaction", "showpopover"); + t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction")); + assert_false(invokee.matches(":popover-open")); + await clickOn(invokerbutton); + t.add_cleanup(() => invokee.hidePopover()); + assert_true(invokee.matches(":popover-open")); + }, "invoking (as showpopover) closed popover opens"); + + promise_test(async function (t) { + invokerbutton.setAttribute("invokeaction", "sHoWpOpOvEr"); + t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction")); + assert_false(invokee.matches(":popover-open")); + await clickOn(invokerbutton); + t.add_cleanup(() => invokee.hidePopover()); + assert_true(invokee.matches(":popover-open")); + }, "invoking (as showpopover - case insensitive) closed popover opens"); + + promise_test(async function (t) { + invokerbutton.setAttribute("invokeaction", "showpopover"); + t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction")); + invokee.showPopover(); + assert_true(invokee.matches(":popover-open")); + await clickOn(invokerbutton); + t.add_cleanup(() => invokee.hidePopover()); + assert_true(invokee.matches(":popover-open")); + }, "invoking (as showpopover) open popover is noop"); + + promise_test(async function (t) { + invokerbutton.setAttribute("invokeaction", "showpopover"); + t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction")); + assert_false(invokee.matches(":popover-open")); + invokee.addEventListener("invoke", (e) => e.preventDefault(), { + once: true, + }); + await clickOn(invokerbutton); + t.add_cleanup(() => invokee.hidePopover()); + assert_false(invokee.matches(":popover-open")); + }, "invoking (as showpopover) closed popover with preventDefault does not open"); + + // hidepopover + + promise_test(async function (t) { + invokerbutton.setAttribute("invokeaction", "hidepopover"); + t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction")); + assert_false(invokee.matches(":popover-open")); + await clickOn(invokerbutton); + assert_false(invokee.matches(":popover-open")); + }, "invoking (as hidepopover) closed popover is noop"); + + promise_test(async function (t) { + invokerbutton2.setAttribute("invokeaction", "hidepopover"); + t.add_cleanup(() => invokerbutton2.removeAttribute("invokeaction")); + invokee.showPopover(); + assert_true(invokee.matches(":popover-open")); + await clickOn(invokerbutton2); + t.add_cleanup(() => invokee.hidePopover()); + assert_false(invokee.matches(":popover-open")); + }, "invoking (as hidepopover) open popover closes"); + + promise_test(async function (t) { + invokerbutton2.setAttribute("invokeaction", "hIdEpOpOvEr"); + t.add_cleanup(() => invokerbutton2.removeAttribute("invokeaction")); + invokee.showPopover(); + assert_true(invokee.matches(":popover-open")); + await clickOn(invokerbutton2); + t.add_cleanup(() => invokee.hidePopover()); + assert_false(invokee.matches(":popover-open")); + }, "invoking (as hidepopover - case insensitive) open popover closes"); + + promise_test(async function (t) { + invokerbutton2.setAttribute("invokeaction", "hidepopover"); + t.add_cleanup(() => invokerbutton2.removeAttribute("invokeaction")); + invokee.showPopover(); + t.add_cleanup(() => invokee.hidePopover()); + assert_true(invokee.matches(":popover-open")); + invokee.addEventListener("invoke", (e) => e.preventDefault(), { + once: true, + }); + await clickOn(invokerbutton2); + assert_true(invokee.matches(":popover-open")); + }, "invoking (as hidepopover) open popover with preventDefault does not close"); +</script> diff --git a/testing/web-platform/tests/html/semantics/invokers/invoketarget-on-video-behavior.tentative.html b/testing/web-platform/tests/html/semantics/invokers/invoketarget-on-video-behavior.tentative.html new file mode 100644 index 0000000000..5bbcd83e72 --- /dev/null +++ b/testing/web-platform/tests/html/semantics/invokers/invoketarget-on-video-behavior.tentative.html @@ -0,0 +1,253 @@ +<!doctype html> +<meta charset="utf-8" /> +<meta name="author" title="Luke Warlow" href="mailto:luke@warlow.dev" /> +<link rel="help" href="https://open-ui.org/components/invokers.explainer/" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-actions.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="resources/invoker-utils.js"></script> + +<video controls id="invokee" src="/media/movie_5.mp4"></video> +<button id="invokerbutton" invoketarget="invokee"></button> + +<script> + // auto + + promise_test(async function (t) { + t.add_cleanup(async () => { + invokerbutton.removeAttribute("invokeaction"); + invokee.pause(); + invokee.currentTime = 0; + invokee.muted = false; + }); + assert_true(invokee.paused); + invokerbutton.setAttribute("invokeaction", ""); + await clickOn(invokerbutton); + await new Promise(resolve => { + requestAnimationFrame(resolve); + }); + assert_true(invokee.paused); + }, "invoking video with auto action is no-op"); + + // playpause + + promise_test(async function (t) { + t.add_cleanup(async () => { + invokerbutton.removeAttribute("invokeaction"); + invokee.pause(); + invokee.currentTime = 0; + invokee.muted = false; + }); + assert_true(invokee.paused); + invokerbutton.setAttribute("invokeaction", "playpause"); + await clickOn(invokerbutton); + await new Promise(resolve => { + requestAnimationFrame(resolve); + }); + assert_false(invokee.paused); + }, "invoking video with playpause action makes video play"); + + promise_test(async function (t) { + t.add_cleanup(async () => { + invokerbutton.removeAttribute("invokeaction"); + invokee.pause(); + invokee.currentTime = 0; + invokee.muted = false; + }); + invokee.addEventListener("invoke", (e) => e.preventDefault(), { + once: true, + }); + assert_true(invokee.paused); + invokerbutton.setAttribute("invokeaction", "playpause"); + await clickOn(invokerbutton); + await new Promise(resolve => { + requestAnimationFrame(resolve); + }); + assert_true(invokee.paused); + }, "invoking video with playpause action and preventDefault is a no-op"); + + promise_test(async function (t) { + t.add_cleanup(async () => { + invokerbutton.removeAttribute("invokeaction"); + invokee.pause(); + invokee.currentTime = 0; + invokee.muted = false; + }); + await test_driver.bless('play video'); + invokee.play(); + assert_false(invokee.paused); + invokerbutton.setAttribute("invokeaction", "playpause"); + await clickOn(invokerbutton); + await new Promise(resolve => { + requestAnimationFrame(resolve); + }); + assert_true(invokee.paused); + }, "invoking playing video with playpause action pauses it"); + + // play + + promise_test(async function (t) { + t.add_cleanup(async () => { + invokerbutton.removeAttribute("invokeaction"); + invokee.pause(); + invokee.currentTime = 0; + invokee.muted = false; + }); + assert_true(invokee.paused); + invokerbutton.setAttribute("invokeaction", "play"); + await clickOn(invokerbutton); + await new Promise(resolve => { + requestAnimationFrame(resolve); + }); + assert_false(invokee.paused); + }, "invoking video with play action makes video play"); + + promise_test(async function (t) { + t.add_cleanup(async () => { + invokerbutton.removeAttribute("invokeaction"); + invokee.pause(); + invokee.currentTime = 0; + invokee.muted = false; + }); + invokee.addEventListener("invoke", (e) => e.preventDefault(), { + once: true, + }); + assert_true(invokee.paused); + invokerbutton.setAttribute("invokeaction", "play"); + await clickOn(invokerbutton); + await new Promise(resolve => { + requestAnimationFrame(resolve); + }); + assert_true(invokee.paused); + }, "invoking video with play action and preventDefault is a no-op"); + + promise_test(async function (t) { + t.add_cleanup(async () => { + invokerbutton.removeAttribute("invokeaction"); + invokee.pause(); + invokee.currentTime = 0; + invokee.muted = false; + }); + await test_driver.bless('play video'); + invokee.play(); + assert_false(invokee.paused); + invokerbutton.setAttribute("invokeaction", "play"); + await clickOn(invokerbutton); + await new Promise(resolve => { + requestAnimationFrame(resolve); + }); + assert_false(invokee.paused); + }, "invoking playing video with play action is a no-op"); + + // pause + + promise_test(async function (t) { + t.add_cleanup(async () => { + invokerbutton.removeAttribute("invokeaction"); + invokee.pause(); + invokee.currentTime = 0; + invokee.muted = false; + }); + assert_true(invokee.paused); + invokerbutton.setAttribute("invokeaction", "pause"); + await clickOn(invokerbutton); + await new Promise(resolve => { + requestAnimationFrame(resolve); + }); + assert_true(invokee.paused); + }, "invoking video with pause action is a no-op"); + + promise_test(async function (t) { + t.add_cleanup(async () => { + invokerbutton.removeAttribute("invokeaction"); + invokee.pause(); + invokee.currentTime = 0; + invokee.muted = false; + }); + invokee.addEventListener("invoke", (e) => e.preventDefault(), { + once: true, + }); + assert_true(invokee.paused); + invokerbutton.setAttribute("invokeaction", "pause"); + await clickOn(invokerbutton); + await new Promise(resolve => { + requestAnimationFrame(resolve); + }); + assert_true(invokee.paused); + }, "invoking video with pause action and preventDefault is a no-op"); + + promise_test(async function (t) { + t.add_cleanup(async () => { + invokerbutton.removeAttribute("invokeaction"); + invokee.pause(); + invokee.currentTime = 0; + invokee.muted = false; + }); + await test_driver.bless('play video'); + invokee.play(); + assert_false(invokee.paused); + invokerbutton.setAttribute("invokeaction", "pause"); + await clickOn(invokerbutton); + await new Promise(resolve => { + requestAnimationFrame(resolve); + }); + assert_true(invokee.paused); + }, "invoking playing video with pause action makes it pause"); + + // mute + + promise_test(async function (t) { + t.add_cleanup(async () => { + invokerbutton.removeAttribute("invokeaction"); + invokee.pause(); + invokee.currentTime = 0; + invokee.muted = false; + }); + assert_false(invokee.muted); + invokerbutton.setAttribute("invokeaction", "toggleMuted"); + await clickOn(invokerbutton); + await new Promise(resolve => { + requestAnimationFrame(resolve); + }); + assert_true(invokee.muted); + }, "invoking video with toggleMuted action mutes it"); + + promise_test(async function (t) { + t.add_cleanup(async () => { + invokerbutton.removeAttribute("invokeaction"); + invokee.pause(); + invokee.currentTime = 0; + invokee.muted = false; + }); + invokee.addEventListener("invoke", (e) => e.preventDefault(), { + once: true, + }); + assert_false(invokee.muted); + invokerbutton.setAttribute("invokeaction", "toggleMuted"); + await clickOn(invokerbutton); + await new Promise(resolve => { + requestAnimationFrame(resolve); + }); + assert_false(invokee.muted); + }, "invoking video with toggleMuted action and preventDefault is a no-op"); + + promise_test(async function (t) { + t.add_cleanup(async () => { + invokerbutton.removeAttribute("invokeaction"); + invokee.pause(); + invokee.currentTime = 0; + invokee.muted = false; + }); + invokee.muted = true; + assert_true(invokee.muted); + invokerbutton.setAttribute("invokeaction", "toggleMuted"); + await clickOn(invokerbutton); + await new Promise(resolve => { + requestAnimationFrame(resolve); + }); + assert_false(invokee.muted); + }, "invoking muted video with toggleMuted action unmutes it"); + +</script> diff --git a/testing/web-platform/tests/html/semantics/invokers/resources/invoker-utils.js b/testing/web-platform/tests/html/semantics/invokers/resources/invoker-utils.js new file mode 100644 index 0000000000..317945502d --- /dev/null +++ b/testing/web-platform/tests/html/semantics/invokers/resources/invoker-utils.js @@ -0,0 +1,12 @@ +function waitForRender() { + return new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve))); +} +async function clickOn(element) { + const actions = new test_driver.Actions(); + await waitForRender(); + await actions.pointerMove(0, 0, {origin: element}) + .pointerDown({button: actions.ButtonType.LEFT}) + .pointerUp({button: actions.ButtonType.LEFT}) + .send(); + await waitForRender(); +} |