diff options
Diffstat (limited to 'testing/web-platform/tests/html/semantics/invokers')
7 files changed, 685 insertions, 36 deletions
diff --git a/testing/web-platform/tests/html/semantics/invokers/interestelement-interface.tentative.html b/testing/web-platform/tests/html/semantics/invokers/interestelement-interface.tentative.html new file mode 100644 index 0000000000..dc119de833 --- /dev/null +++ b/testing/web-platform/tests/html/semantics/invokers/interestelement-interface.tentative.html @@ -0,0 +1,164 @@ +<!doctype html> +<meta charset="utf-8" /> +<meta name="author" title="Luke Warlow" href="mailto:lwarlow@igalia.com" /> +<link rel="help" href="https://open-ui.org/components/interest-invokers.explainer/" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<button id="buttonInvoker" interesttarget="interestee"></button> +<a id="aInvoker" interesttarget="interestee"></a> +<input type="button" id="inputInvoker" interesttarget="interestee" /> +<div id="interestee"></div> + +<script> + test(function () { + assert_equals(buttonInvoker.interestTargetElement, interestee); + assert_equals(aInvoker.interestTargetElement, interestee); + assert_equals(inputInvoker.interestTargetElement, interestee); + }, "interestTargetElement reflects interestee HTML element"); + + test(function () { + const div = document.body.appendChild(document.createElement("div")); + buttonInvoker.interestTargetElement = div; + aInvoker.interestTargetElement = div; + inputInvoker.interestTargetElement = div; + assert_equals(buttonInvoker.interestTargetElement, div); + assert_equals(buttonInvoker.getAttribute("interesttarget"), ""); + assert_equals(aInvoker.interestTargetElement, div); + assert_equals(aInvoker.getAttribute("interesttarget"), ""); + assert_equals(inputInvoker.interestTargetElement, div); + assert_equals(inputInvoker.getAttribute("interesttarget"), ""); + }, "interestTargetElement 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.interestTargetElement = interestee; + assert_equals(button.interestTargetElement, interestee); + assert_equals(buttonInvoker.getAttribute("interesttarget"), ""); + }, "interestTargetElement 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")); + buttonInvoker.interestTargetElement = div; + assert_equals(buttonInvoker.interestTargetElement, null); + assert_equals(buttonInvoker.getAttribute("interesttarget"), ""); + }, "interestTargetElement does not reflect set value inside shadowroot"); + + test(function () { + buttonInvoker.setAttribute('interesttarget', 'invalid'); + assert_equals(buttonInvoker.interestTargetElement, null); + assert_equals(buttonInvoker.getAttribute("interesttarget"), "invalid"); + aInvoker.setAttribute('interesttarget', 'invalid'); + assert_equals(aInvoker.interestTargetElement, null); + assert_equals(aInvoker.getAttribute("interesttarget"), "invalid"); + inputInvoker.setAttribute('interesttarget', 'invalid'); + assert_equals(inputInvoker.interestTargetElement, null); + assert_equals(inputInvoker.getAttribute("interesttarget"), "invalid"); + }, "interestTargetElement does not reflect invalid value"); + + test(function () { + assert_throws_js( + TypeError, + function () { + buttonInvoker.interestTargetElement = {}; + }, + "interestTargetElement attribute value must be an instance of Element", + ); + assert_throws_js( + TypeError, + function () { + aInvoker.interestTargetElement = {}; + }, + "interestTargetElement attribute value must be an instance of Element", + ); + assert_throws_js( + TypeError, + function () { + inputInvoker.interestTargetElement = {}; + }, + "interestTargetElement attribute value must be an instance of Element", + ); + }, "interestTargetElement throws error on assignment of non Element"); + + test(function () { + assert_false(buttonInvoker.hasAttribute("interestaction")); + assert_equals(buttonInvoker.interestAction, ""); + assert_false(aInvoker.hasAttribute("interestaction")); + assert_equals(aInvoker.interestAction, ""); + assert_false(inputInvoker.hasAttribute("interestaction")); + assert_equals(inputInvoker.interestAction, ""); + }, "interestAction reflects '' when attribute not present"); + + test(function () { + buttonInvoker.setAttribute("interestaction", ""); + assert_equals(buttonInvoker.getAttribute("interestaction"), ""); + assert_equals(buttonInvoker.interestAction, ""); + aInvoker.setAttribute("interestaction", ""); + assert_equals(aInvoker.getAttribute("interestaction"), ""); + assert_equals(aInvoker.interestAction, ""); + inputInvoker.setAttribute("interestaction", ""); + assert_equals(inputInvoker.getAttribute("interestaction"), ""); + assert_equals(inputInvoker.interestAction, ""); + }, "interestAction reflects '' when attribute empty, setAttribute version"); + + test(function () { + buttonInvoker.interestAction = ""; + assert_equals(buttonInvoker.getAttribute("interestaction"), ""); + assert_equals(buttonInvoker.interestAction, ""); + aInvoker.interestAction = ""; + assert_equals(aInvoker.getAttribute("interestaction"), ""); + assert_equals(aInvoker.interestAction, ""); + inputInvoker.interestAction = ""; + assert_equals(inputInvoker.getAttribute("interestaction"), ""); + assert_equals(inputInvoker.interestAction, ""); + }, "interestAction reflects '' when attribute empty, IDL setter version"); + + test(function () { + buttonInvoker.interestAction = "fooBarBaz"; + assert_equals(buttonInvoker.getAttribute("interestaction"), "fooBarBaz"); + assert_equals(buttonInvoker.interestAction, "fooBarBaz"); + aInvoker.interestAction = "fooBarBaz"; + assert_equals(aInvoker.getAttribute("interestaction"), "fooBarBaz"); + assert_equals(aInvoker.interestAction, "fooBarBaz"); + inputInvoker.interestAction = "fooBarBaz"; + assert_equals(inputInvoker.getAttribute("interestaction"), "fooBarBaz"); + assert_equals(inputInvoker.interestAction, "fooBarBaz"); + }, "interestAction reflects same casing"); + + test(function () { + buttonInvoker.interestAction = []; + assert_equals(buttonInvoker.getAttribute("interestaction"), ""); + assert_equals(buttonInvoker.interestAction, ""); + aInvoker.interestAction = []; + assert_equals(aInvoker.getAttribute("interestaction"), ""); + assert_equals(aInvoker.interestAction, ""); + inputInvoker.interestAction = []; + assert_equals(inputInvoker.getAttribute("interestaction"), ""); + assert_equals(inputInvoker.interestAction, ""); + }, "interestAction reflects '' when attribute set to []"); + + test(function () { + buttonInvoker.interestAction = [1, 2, 3]; + assert_equals(buttonInvoker.getAttribute("interestaction"), "1,2,3"); + assert_equals(buttonInvoker.interestAction, "1,2,3"); + aInvoker.interestAction = [1, 2, 3]; + assert_equals(aInvoker.getAttribute("interestaction"), "1,2,3"); + assert_equals(aInvoker.interestAction, "1,2,3"); + inputInvoker.interestAction = [1, 2, 3]; + assert_equals(inputInvoker.getAttribute("interestaction"), "1,2,3"); + assert_equals(inputInvoker.interestAction, "1,2,3"); + }, "interestAction reflects tostring value"); + + test(function () { + buttonInvoker.interestAction = {}; + assert_equals(buttonInvoker.interestAction, "[object Object]"); + aInvoker.interestAction = {}; + assert_equals(aInvoker.interestAction, "[object Object]"); + inputInvoker.interestAction = {}; + assert_equals(inputInvoker.interestAction, "[object Object]"); + }, "interestAction reflects tostring value 2"); +</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 index b003daf20d..5a2854fe31 100644 --- a/testing/web-platform/tests/html/semantics/invokers/invokeelement-interface.tentative.html +++ b/testing/web-platform/tests/html/semantics/invokers/invokeelement-interface.tentative.html @@ -53,14 +53,14 @@ test(function () { assert_false(invoker.hasAttribute("invokeaction")); - assert_equals(invoker.invokeAction, "auto"); - }, "invokeAction reflects 'auto' when attribute not present"); + assert_equals(invoker.invokeAction, ""); + }, "invokeAction reflects '' 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"); + assert_equals(invoker.invokeAction, ""); + }, "invokeAction reflects '' when attribute empty, setAttribute version"); test(function () { invoker.invokeAction = "fooBarBaz"; @@ -71,8 +71,8 @@ test(function () { invoker.invokeAction = ""; assert_equals(invoker.getAttribute("invokeaction"), ""); - assert_equals(invoker.invokeAction, "auto"); - }, "invokeAction reflects 'auto' when attribute empty 2"); + assert_equals(invoker.invokeAction, ""); + }, "invokeAction reflects '' when attribute empty, IDL version"); test(function () { invoker.invokeAction = [1, 2, 3]; @@ -83,8 +83,8 @@ test(function () { invoker.invokeAction = []; assert_equals(invoker.getAttribute("invokeaction"), ""); - assert_equals(invoker.invokeAction, "auto"); - }, "invokeAction reflects 'auto' when attribute set to []"); + assert_equals(invoker.invokeAction, ""); + }, "invokeAction reflects '' when attribute set to []"); test(function () { invoker.invokeAction = {}; 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 index 82910b3d44..382f808071 100644 --- a/testing/web-platform/tests/html/semantics/invokers/invokeevent-interface.tentative.html +++ b/testing/web-platform/tests/html/semantics/invokers/invokeevent-interface.tentative.html @@ -15,9 +15,9 @@ <script> test(function () { const event = new InvokeEvent("test"); - assert_equals(event.action, "auto"); + assert_equals(event.action, ""); assert_readonly(event, "action", "readonly attribute value"); - }, "action is a readonly defaulting to 'auto'"); + }, "action is a readonly defaulting to ''"); test(function () { const event = new InvokeEvent("test"); @@ -32,7 +32,7 @@ test(function () { const event = new InvokeEvent("test", { action: undefined }); - assert_equals(event.action, "auto"); + assert_equals(event.action, ""); }, "action set to undefined"); test(function () { 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 index b19c1d3adc..d8d9c04022 100644 --- 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 @@ -22,7 +22,7 @@ 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.action, "", "action"); assert_equals(event.target, invokee, "target"); assert_equals(event.invoker, invokerbutton, "invoker"); }, "event dispatches on click"); @@ -107,6 +107,7 @@ "invoke", (event) => { eventInvoker = event.invoker; + eventTarget = event.target; called = true; }, { once: true }, @@ -114,6 +115,7 @@ 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"); + assert_equals(eventInvoker, invokerbutton, "event.invoker is set to right element"); + assert_equals(eventTarget, svgInvokee, "event.target is set to right element"); + }, "event dispatches if invokee is non-HTML Element"); </script> diff --git a/testing/web-platform/tests/html/semantics/invokers/invoketarget-on-dialog-behavior.tentative.html b/testing/web-platform/tests/html/semantics/invokers/invoketarget-on-dialog-behavior.tentative.html new file mode 100644 index 0000000000..774d308703 --- /dev/null +++ b/testing/web-platform/tests/html/semantics/invokers/invoketarget-on-dialog-behavior.tentative.html @@ -0,0 +1,455 @@ +<!doctype html> +<meta charset="utf-8" /> +<meta name="author" title="Keith Cirkel" href="mailto:wpt@keithcirkel.co.uk" /> +<meta name="timeout" content="long"> +<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> + +<dialog id="invokee"> + <button id="containedinvoker" invoketarget="invokee"></button> +</dialog> +<button id="invokerbutton" invoketarget="invokee"></button> + +<script> + function resetState() { + invokee.close(); + try { invokee.hidePopover(); } catch {} + invokee.removeAttribute("popover"); + invokerbutton.removeAttribute("invokeaction"); + containedinvoker.removeAttribute("invokeaction"); + } + + // opening a dialog + + [null, "", "showmodal", /* test case sensitivity */ "sHoWmOdAl"].forEach( + (action) => { + ["property", "attribute"].forEach((setType) => { + promise_test( + async function (t) { + t.add_cleanup(resetState); + assert_false(invokee.open, "invokee.open"); + assert_false(invokee.matches(":modal"), "invokee :modal"); + if (typeof action === "string") { + if (setType === "property") { + invokerbutton.invokeaction = action; + } else { + invokerbutton.setAttribute("invokeaction", action); + } + } + await clickOn(invokerbutton); + assert_true(invokee.open, "invokee.open"); + assert_true(invokee.matches(":modal"), "invokee :modal"); + }, + `invoking (with invokeaction ${setType} as ${ + action == null ? "auto" : action || "explicit empty" + }) closed dialog opens as modal`, + ); + + promise_test( + async function (t) { + t.add_cleanup(resetState); + assert_false(invokee.open, "invokee.open"); + assert_false(invokee.matches(":modal"), "invokee :modal"); + invokee.addEventListener("invoke", (e) => e.preventDefault(), { + once: true, + }); + if (typeof action === "string") { + if (setType === "property") { + invokerbutton.invokeaction = action; + } else { + invokerbutton.setAttribute("invokeaction", action); + } + } + await clickOn(invokerbutton); + assert_false(invokee.open, "invokee.open"); + assert_false(invokee.matches(":modal"), "invokee :modal"); + }, + `invoking (with invokeaction ${setType} as ${ + action == null ? "auto" : action || "explicit empty" + }) closed dialog with preventDefault is noop`, + ); + + promise_test( + async function (t) { + t.add_cleanup(resetState); + assert_false(invokee.open, "invokee.open"); + assert_false(invokee.matches(":modal"), "invokee :modal"); + invokee.addEventListener( + "invoke", + (e) => { + invokerbutton.setAttribute("invokeaction", "close"); + }, + { once: true }, + ); + if (typeof action === "string") { + if (setType === "property") { + invokerbutton.invokeaction = action; + } else { + invokerbutton.setAttribute("invokeaction", action); + } + } + await clickOn(invokerbutton); + assert_true(invokee.open, "invokee.open"); + assert_true(invokee.matches(":modal"), "invokee :modal"); + }, + `invoking (with invokeaction ${setType} as ${ + action == null ? "auto" : action || "explicit empty" + }) while changing action still opens as modal`, + ); + }); + }, + ); + + // closing an already open dialog + + [null, "", "close", /* test case sensitivity */ "cLoSe"].forEach((action) => { + ["property", "attribute"].forEach((setType) => { + promise_test( + async function (t) { + t.add_cleanup(resetState); + invokee.show(); + assert_true(invokee.open, "invokee.open"); + assert_false(invokee.matches(":modal"), "invokee :modal"); + if (typeof action === "string") { + if (setType === "property") { + containedinvoker.invokeaction = action; + } else { + containedinvoker.setAttribute("invokeaction", action); + } + } + await clickOn(containedinvoker); + assert_false(invokee.open, "invokee.open"); + assert_false(invokee.matches(":modal"), "invokee :modal"); + }, + `invoking to close (with invokeaction ${setType} as ${ + action == null ? "auto" : action || "explicit empty" + }) open dialog closes`, + ); + + promise_test( + async function (t) { + t.add_cleanup(resetState); + invokee.show(); + assert_true(invokee.open, "invokee.open"); + assert_false(invokee.matches(":modal"), "invokee :modal"); + if (typeof action === "string") { + if (setType === "property") { + containedinvoker.invokeaction = action; + } else { + containedinvoker.setAttribute("invokeaction", action); + } + } + invokee.addEventListener("invoke", (e) => e.preventDefault(), { + once: true, + }); + await clickOn(containedinvoker); + assert_true(invokee.open, "invokee.open"); + assert_false(invokee.matches(":modal"), "invokee :modal"); + }, + `invoking to close (with invokeaction ${setType} as ${ + action == null ? "auto" : action || "explicit empty" + }) open dialog with preventDefault is no-op`, + ); + + promise_test( + async function (t) { + t.add_cleanup(resetState); + invokee.showModal(); + assert_true(invokee.open, "invokee.open"); + assert_true(invokee.matches(":modal"), "invokee :modal"); + if (typeof action === "string") { + if (setType === "property") { + containedinvoker.invokeaction = action; + } else { + containedinvoker.setAttribute("invokeaction", action); + } + } + invokee.addEventListener("invoke", (e) => e.preventDefault(), { + once: true, + }); + await clickOn(containedinvoker); + assert_true(invokee.open, "invokee.open"); + assert_true(invokee.matches(":modal"), "invokee :modal"); + }, + `invoking to close (with invokeaction ${setType} as ${ + action == null ? "auto" : action || "explicit empty" + }) open modal dialog with preventDefault is no-op`, + ); + + promise_test( + async function (t) { + t.add_cleanup(resetState); + invokee.show(); + assert_true(invokee.open, "invokee.open"); + assert_false(invokee.matches(":modal"), "invokee :modal"); + if (typeof action === "string") { + if (setType === "property") { + containedinvoker.invokeaction = action; + } else { + containedinvoker.setAttribute("invokeaction", action); + } + } + invokee.addEventListener( + "invoke", + (e) => { + containedinvoker.setAttribute("invokeaction", "show"); + }, + { once: true }, + ); + await clickOn(containedinvoker); + assert_false(invokee.open, "invokee.open"); + assert_false(invokee.matches(":modal"), "invokee :modal"); + }, + `invoking to close (with invokeaction ${setType} as ${ + action == null ? "auto" : action || "explicit empty" + }) open dialog while changing action still closes`, + ); + + promise_test( + async function (t) { + t.add_cleanup(resetState); + invokee.showModal(); + assert_true(invokee.open, "invokee.open"); + assert_true(invokee.matches(":modal"), "invokee :modal"); + if (typeof action === "string") { + if (setType === "property") { + containedinvoker.invokeaction = action; + } else { + containedinvoker.setAttribute("invokeaction", action); + } + } + invokee.addEventListener( + "invoke", + (e) => { + containedinvoker.setAttribute("invokeaction", "show"); + }, + { once: true }, + ); + await clickOn(containedinvoker); + assert_false(invokee.open, "invokee.open"); + assert_false(invokee.matches(":modal"), "invokee :modal"); + }, + `invoking to close (with invokeaction ${setType} as ${ + action == null ? "auto" : action || "explicit empty" + }) open modal dialog while changing action still closes`, + ); + }); + }); + + // showmodal explicit behaviours + + promise_test(async function (t) { + t.add_cleanup(resetState); + containedinvoker.setAttribute("invokeaction", "showModal"); + invokee.show(); + assert_true(invokee.open, "invokee.open"); + assert_false(invokee.matches(":modal"), "invokee :modal"); + await clickOn(containedinvoker); + assert_true(invokee.open, "invokee.open"); + assert_false(invokee.matches(":modal"), "invokee :modal"); + }, "invoking (as showmodal) open dialog is noop"); + + promise_test(async function (t) { + t.add_cleanup(resetState); + containedinvoker.setAttribute("invokeaction", "showmodal"); + invokee.showModal(); + assert_true(invokee.open, "invokee.open"); + assert_true(invokee.matches(":modal"), "invokee :modal"); + invokee.addEventListener( + "invoke", + (e) => { + containedinvoker.setAttribute("invokeaction", "close"); + }, + { once: true }, + ); + await clickOn(invokerbutton); + assert_true(invokee.open, "invokee.open"); + assert_true(invokee.matches(":modal"), "invokee :modal"); + }, "invoking (as showmodal) open modal, while changing action still a no-op"); + + promise_test(async function (t) { + t.add_cleanup(resetState); + invokerbutton.setAttribute("invokeaction", "showmodal"); + assert_false(invokee.open, "invokee.open"); + assert_false(invokee.matches(":modal"), "invokee :modal"); + invokee.setAttribute("popover", "auto"); + await clickOn(invokerbutton); + assert_true(invokee.open, "invokee.open"); + assert_true(invokee.matches(":modal"), "invokee :modal"); + }, "invoking (as showmodal) closed popover dialog opens as modal"); + + // close explicit behaviours + + promise_test(async function (t) { + t.add_cleanup(resetState); + invokerbutton.setAttribute("invokeaction", "close"); + assert_false(invokee.open, "invokee.open"); + assert_false(invokee.matches(":modal"), "invokee :modal"); + await clickOn(containedinvoker); + assert_false(invokee.open, "invokee.open"); + assert_false(invokee.matches(":modal"), "invokee :modal"); + }, "invoking (as close) already closed dialog is noop"); + + // invalid + [ + "foo", + "foo-bar", + "auto", + "showpopover", + "hidepopover", + "togglepopover", + "showpicker", + ].forEach((action) => { + promise_test(async function (t) { + t.add_cleanup(resetState); + invokerbutton.setAttribute("invokeaction", action); + assert_false(invokee.open, "invokee.open"); + assert_false(invokee.matches(":modal"), "invokee :modal"); + await clickOn(invokerbutton); + assert_false(invokee.open, "invokee.open"); + assert_false(invokee.matches(":modal"), "invokee :modal"); + }, `invoking (as ${action}) on dialog does nothing`); + + promise_test(async function (t) { + t.add_cleanup(resetState); + containedinvoker.setAttribute("invokeaction", action); + invokee.show(); + assert_true(invokee.open, "invokee.open"); + assert_false(invokee.matches(":modal"), "invokee :modal"); + await clickOn(containedinvoker); + assert_true(invokee.open, "invokee.open"); + assert_false(invokee.matches(":modal"), "invokee :modal"); + }, `invoking (as ${action}) on open dialog does nothing`); + + promise_test(async function (t) { + t.add_cleanup(resetState); + containedinvoker.setAttribute("invokeaction", action); + invokee.showModal(); + assert_true(invokee.open, "invokee.open"); + assert_true(invokee.matches(":modal"), "invokee :modal"); + await clickOn(containedinvoker); + assert_true(invokee.open, "invokee.open"); + assert_true(invokee.matches(":modal"), "invokee :modal"); + }, `invoking (as ${action}) on open modal dialog does nothing`); + + promise_test(async function (t) { + t.add_cleanup(resetState); + containedinvoker.setAttribute("invokeaction", action); + invokee.showModal(); + assert_true(invokee.open, "invokee.open"); + assert_true(invokee.matches(":modal"), "invokee :modal"); + invokee.addEventListener( + "invoke", + (e) => { + containedinvoker.setAttribute("invokeaction", ""); + }, + { once: true }, + ); + await clickOn(containedinvoker); + assert_true(invokee.open, "invokee.open"); + assert_true(invokee.matches(":modal"), "invokee :modal"); + }, `invoking (as ${action}) on open modal while changing the attributer does nothing`); + }); + + // Open Popovers using Dialog actions + ["showmodal", "close", ""].forEach((action) => { + ["manual", "auto"].forEach((popoverState) => { + promise_test( + async function (t) { + t.add_cleanup(resetState); + invokee.setAttribute("popover", popoverState); + invokee.showPopover(); + containedinvoker.setAttribute("invokeaction", action); + assert_true( + invokee.matches(":popover-open"), + "invokee :popover-open", + ); + assert_false(invokee.open, "invokee.open"); + assert_false(invokee.matches(":modal"), "invokee :modal"); + invokee.addEventListener("invoke", (e) => e.preventDefault(), { + once: true, + }); + await clickOn(containedinvoker); + assert_true( + invokee.matches(":popover-open"), + "invokee :popover-open", + ); + assert_false(invokee.open, "invokee.open"); + assert_false(invokee.matches(":modal"), "invokee :modal"); + }, + `invoking (as ${ + action || "explicit empty" + }) dialog as open popover=${popoverState} is noop`, + ); + }); + }); + + // Elements being disconnected during invoke steps + ["showmodal", "close", ""].forEach((action) => { + promise_test( + async function (t) { + t.add_cleanup(() => { + document.body.prepend(invokee); + resetState(); + }); + const invokee = document.querySelector("#invokee"); + invokerbutton.setAttribute("invokeaction", action); + assert_false(invokee.open, "invokee.open"); + assert_false(invokee.matches(":modal"), "invokee :modal"); + invokee.addEventListener( + "invoke", + (e) => { + invokee.remove(); + }, + { + once: true, + }, + ); + await clickOn(invokerbutton); + assert_false(invokee.open, "invokee.open"); + assert_false(invokee.matches(":modal"), "invokee :modal"); + }, + `invoking (as ${ + action || "explicit empty" + }) dialog that is removed is noop`, + ); + + promise_test( + async function (t) { + const invokerbutton = document.createElement("button"); + invokerbutton.invokeTargetElement = invokee; + invokerbutton.setAttribute("invokeaction", action); + assert_false(invokee.open, "invokee.open"); + assert_false(invokee.matches(":modal"), "invokee :modal"); + await clickOn(invokerbutton); + assert_false(invokee.open, "invokee.open"); + assert_false(invokee.matches(":modal"), "invokee :modal"); + }, + `invoking (as ${ + action || "explicit empty" + }) dialog from a detached invoker`, + ); + + promise_test( + async function (t) { + const invokerbutton = document.createElement("button"); + const invokee = document.createElement("dialog"); + invokerbutton.invokeTargetElement = invokee; + invokerbutton.setAttribute("invokeaction", action); + assert_false(invokee.open, "invokee.open"); + assert_false(invokee.matches(":modal"), "invokee :modal"); + await clickOn(invokerbutton); + assert_false(invokee.open, "invokee.open"); + assert_false(invokee.matches(":modal"), "invokee :modal"); + }, + `invoking (as ${ + action || "explicit empty" + }) detached dialog from a detached invoker`, + ); + }); +</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 index 03eba22285..2bddfa7621 100644 --- 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 @@ -10,7 +10,7 @@ <script src="resources/invoker-utils.js"></script> <div id="invokee" popover> - <button id="invokerbutton2" invoketarget="invokee"></button> + <button id="containedinvoker" invoketarget="invokee"></button> </div> <button id="invokerbutton" invoketarget="invokee"></button> @@ -44,7 +44,7 @@ promise_test(async function (t) { invokee.showPopover(); assert_true(invokee.matches(":popover-open")); - await clickOn(invokerbutton2); + await clickOn(containedinvoker); assert_false(invokee.matches(":popover-open")); }, "invoking (as auto) from within open popover closes"); @@ -55,7 +55,7 @@ once: true, }); assert_true(invokee.matches(":popover-open")); - await clickOn(invokerbutton2); + await clickOn(containedinvoker); assert_true(invokee.matches(":popover-open")); }, "invoking (as auto) open popover with preventDefault does not close"); @@ -93,8 +93,8 @@ promise_test(async function (t) { invokee.showPopover(); - invokerbutton2.setAttribute("invokeaction", "togglepopover"); - t.add_cleanup(() => invokerbutton2.removeAttribute("invokeaction")); + containedinvoker.setAttribute("invokeaction", "togglepopover"); + t.add_cleanup(() => containedinvoker.removeAttribute("invokeaction")); assert_true(invokee.matches(":popover-open")); await clickOn(invokerbutton); assert_false(invokee.matches(":popover-open")); @@ -102,23 +102,23 @@ promise_test(async function (t) { invokee.showPopover(); - invokerbutton2.setAttribute("invokeaction", "togglepopover"); - t.add_cleanup(() => invokerbutton2.removeAttribute("invokeaction")); + containedinvoker.setAttribute("invokeaction", "togglepopover"); + t.add_cleanup(() => containedinvoker.removeAttribute("invokeaction")); assert_true(invokee.matches(":popover-open")); - await clickOn(invokerbutton2); + await clickOn(containedinvoker); 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")); + containedinvoker.setAttribute("invokeaction", "togglepopover"); + t.add_cleanup(() => containedinvoker.removeAttribute("invokeaction")); invokee.addEventListener("invoke", (e) => e.preventDefault(), { once: true, }); assert_true(invokee.matches(":popover-open")); - await clickOn(invokerbutton2); + await clickOn(containedinvoker); assert_true(invokee.matches(":popover-open")); }, "invoking (as togglepopover) open popover with preventDefault does not close"); @@ -175,35 +175,59 @@ }, "invoking (as hidepopover) closed popover is noop"); promise_test(async function (t) { - invokerbutton2.setAttribute("invokeaction", "hidepopover"); - t.add_cleanup(() => invokerbutton2.removeAttribute("invokeaction")); + containedinvoker.setAttribute("invokeaction", "hidepopover"); + t.add_cleanup(() => containedinvoker.removeAttribute("invokeaction")); invokee.showPopover(); assert_true(invokee.matches(":popover-open")); - await clickOn(invokerbutton2); + await clickOn(containedinvoker); 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")); + containedinvoker.setAttribute("invokeaction", "hIdEpOpOvEr"); + t.add_cleanup(() => containedinvoker.removeAttribute("invokeaction")); invokee.showPopover(); assert_true(invokee.matches(":popover-open")); - await clickOn(invokerbutton2); + await clickOn(containedinvoker); 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")); + containedinvoker.setAttribute("invokeaction", "hidepopover"); + t.add_cleanup(() => containedinvoker.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); + await clickOn(containedinvoker); assert_true(invokee.matches(":popover-open")); }, "invoking (as hidepopover) open popover with preventDefault does not close"); + + // invalid + + ["foo", "togglemodal", "showpicker", "toggle", "open", "close"].forEach(action => { + promise_test(async function (t) { + invokerbutton.setAttribute("invokeaction", action); + t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction")); + assert_false(invokee.matches(":popover-open")); + await clickOn(invokerbutton); + assert_false(invokee.matches(":popover-open")); + }, `invoking (as ${action}) on popover does nothing`); + + promise_test(async function (t) { + invokerbutton.setAttribute("invokeaction", action); + t.add_cleanup(() => { + invokerbutton.removeAttribute("invokeaction") + invokee.hidePopover(); + }); + invokee.showPopover() + assert_true(invokee.matches(":popover-open")); + await clickOn(invokerbutton); + assert_true(invokee.matches(":popover-open")); + }, `invoking (as ${action}) on open popover does nothing`); + }) </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 index 317945502d..8420f24b6f 100644 --- a/testing/web-platform/tests/html/semantics/invokers/resources/invoker-utils.js +++ b/testing/web-platform/tests/html/semantics/invokers/resources/invoker-utils.js @@ -2,9 +2,13 @@ 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}) + let rect = element.getBoundingClientRect(); + let actions = new test_driver.Actions(); + // FIXME: Switch to pointerMove(0, 0, {origin: element}) once + // https://github.com/web-platform-tests/wpt/issues/41257 is fixed. + await actions + .pointerMove(Math.round(rect.x + rect.width / 2), Math.round(rect.y + rect.height / 2), {}) .pointerDown({button: actions.ButtonType.LEFT}) .pointerUp({button: actions.ButtonType.LEFT}) .send(); |