diff options
Diffstat (limited to 'testing/web-platform/tests/dom/events/Event-dispatch-on-disabled-elements.html')
-rw-r--r-- | testing/web-platform/tests/dom/events/Event-dispatch-on-disabled-elements.html | 251 |
1 files changed, 251 insertions, 0 deletions
diff --git a/testing/web-platform/tests/dom/events/Event-dispatch-on-disabled-elements.html b/testing/web-platform/tests/dom/events/Event-dispatch-on-disabled-elements.html new file mode 100644 index 0000000000..361006a724 --- /dev/null +++ b/testing/web-platform/tests/dom/events/Event-dispatch-on-disabled-elements.html @@ -0,0 +1,251 @@ +<!doctype html> +<meta charset="utf8"> +<meta name="timeout" content="long"> +<title>Events must dispatch on disabled elements</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<style> + @keyframes fade { + 0% { + opacity: 1; + } + 100% { + opacity: 0; + } + } +</style> +<body> +<script> +// HTML elements that can be disabled +const formElements = ["button", "fieldset", "input", "select", "textarea"]; + +test(() => { + for (const localName of formElements) { + const elem = document.createElement(localName); + elem.disabled = true; + // pass becomes true if the event is called and it's the right type. + let pass = false; + const listener = ({ type }) => { + pass = type === "click"; + }; + elem.addEventListener("click", listener, { once: true }); + elem.dispatchEvent(new Event("click")); + assert_true( + pass, + `Untrusted "click" Event didn't dispatch on ${elem.constructor.name}.` + ); + } +}, "Can dispatch untrusted 'click' Events at disabled HTML elements."); + +test(() => { + for (const localName of formElements) { + const elem = document.createElement(localName); + elem.disabled = true; + // pass becomes true if the event is called and it's the right type. + let pass = false; + const listener = ({ type }) => { + pass = type === "pass"; + }; + elem.addEventListener("pass", listener, { once: true }); + elem.dispatchEvent(new Event("pass")); + assert_true( + pass, + `Untrusted "pass" Event didn't dispatch on ${elem.constructor.name}` + ); + } +}, "Can dispatch untrusted Events at disabled HTML elements."); + +test(() => { + for (const localName of formElements) { + const elem = document.createElement(localName); + elem.disabled = true; + // pass becomes true if the event is called and it's the right type. + let pass = false; + const listener = ({ type }) => { + pass = type === "custom-pass"; + }; + elem.addEventListener("custom-pass", listener, { once: true }); + elem.dispatchEvent(new CustomEvent("custom-pass")); + assert_true( + pass, + `CustomEvent "custom-pass" didn't dispatch on ${elem.constructor.name}` + ); + } +}, "Can dispatch CustomEvents at disabled HTML elements."); + +test(() => { + for (const localName of formElements) { + const elem = document.createElement(localName); + + // Element is disabled... so this click() MUST NOT fire an event. + elem.disabled = true; + let pass = true; + elem.onclick = e => { + pass = false; + }; + elem.click(); + assert_true( + pass, + `.click() must not dispatch "click" event on disabled ${ + elem.constructor.name + }.` + ); + + // Element is (re)enabled... so this click() fires an event. + elem.disabled = false; + pass = false; + elem.onclick = e => { + pass = true; + }; + elem.click(); + assert_true( + pass, + `.click() must dispatch "click" event on enabled ${ + elem.constructor.name + }.` + ); + } +}, "Calling click() on disabled elements must not dispatch events."); + +promise_test(async () => { + // For each form element type, set up transition event handlers. + for (const localName of formElements) { + const elem = document.createElement(localName); + elem.disabled = true; + document.body.appendChild(elem); + const eventPromises = [ + "transitionrun", + "transitionstart", + "transitionend", + ].map(eventType => { + return new Promise(r => { + elem.addEventListener(eventType, r); + }); + }); + // Flushing style triggers transition. + getComputedStyle(elem).opacity; + elem.style.transition = "opacity .1s"; + elem.style.opacity = 0; + getComputedStyle(elem).opacity; + // All the events fire... + await Promise.all(eventPromises); + elem.remove(); + } +}, "CSS Transitions transitionrun, transitionstart, transitionend events fire on disabled form elements"); + +promise_test(async () => { + // For each form element type, set up transition event handlers. + for (const localName of formElements) { + const elem = document.createElement(localName); + elem.disabled = true; + document.body.appendChild(elem); + getComputedStyle(elem).opacity; + elem.style.transition = "opacity 100s"; + // We use ontransitionstart to cancel the event. + elem.ontransitionstart = () => { + elem.style.display = "none"; + }; + const promiseToCancel = new Promise(r => { + elem.ontransitioncancel = r; + }); + // Flushing style triggers the transition. + elem.style.opacity = 0; + getComputedStyle(elem).opacity; + await promiseToCancel; + // And we are done with this element. + elem.remove(); + } +}, "CSS Transitions transitioncancel event fires on disabled form elements"); + +promise_test(async () => { + // For each form element type, set up transition event handlers. + for (const localName of formElements) { + const elem = document.createElement(localName); + document.body.appendChild(elem); + elem.disabled = true; + const animationStartPromise = new Promise(r => { + elem.addEventListener("animationstart", () => { + // Seek to the second iteration to trigger the animationiteration event + elem.style.animationDelay = "-100s" + r(); + }); + }); + const animationIterationPromise = new Promise(r => { + elem.addEventListener("animationiteration", ()=>{ + elem.style.animationDelay = "-200s" + r(); + }); + }); + const animationEndPromise = new Promise(r => { + elem.addEventListener("animationend", r); + }); + elem.style.animation = "fade 100s 2"; + elem.classList.add("animate"); + // All the events fire... + await Promise.all([ + animationStartPromise, + animationIterationPromise, + animationEndPromise, + ]); + elem.remove(); + } +}, "CSS Animation animationstart, animationiteration, animationend fire on disabled form elements"); + +promise_test(async () => { + // For each form element type, set up transition event handlers. + for (const localName of formElements) { + const elem = document.createElement(localName); + document.body.appendChild(elem); + elem.disabled = true; + + const promiseToCancel = new Promise(r => { + elem.addEventListener("animationcancel", r); + }); + + elem.addEventListener("animationstart", () => { + // Cancel the animation by hiding it. + elem.style.display = "none"; + }); + + // Trigger the animation + elem.style.animation = "fade 100s"; + elem.classList.add("animate"); + await promiseToCancel; + // And we are done with this element. + elem.remove(); + } +}, "CSS Animation's animationcancel event fires on disabled form elements"); + +promise_test(async () => { + for (const localName of formElements) { + const elem = document.createElement(localName); + elem.disabled = true; + document.body.appendChild(elem); + // Element is disabled, so clicking must not fire events + let pass = true; + elem.onclick = e => { + pass = false; + }; + // Disabled elements are not clickable. + await test_driver.click(elem); + assert_true( + pass, + `${elem.constructor.name} is disabled, so onclick must not fire.` + ); + // Element is (re)enabled... so this click() will fire an event. + pass = false; + elem.disabled = false; + elem.onclick = () => { + pass = true; + }; + await test_driver.click(elem); + assert_true( + pass, + `${elem.constructor.name} is enabled, so onclick must fire.` + ); + elem.remove(); + } +}, "Real clicks on disabled elements must not dispatch events."); +</script> |