diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
commit | 43a97878ce14b72f0981164f87f2e35e14151312 (patch) | |
tree | 620249daf56c0258faa40cbdcf9cfba06de2a846 /testing/web-platform/tests/html/semantics/disabled-elements | |
parent | Initial commit. (diff) | |
download | firefox-43a97878ce14b72f0981164f87f2e35e14151312.tar.xz firefox-43a97878ce14b72f0981164f87f2e35e14151312.zip |
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/html/semantics/disabled-elements')
3 files changed, 350 insertions, 0 deletions
diff --git a/testing/web-platform/tests/html/semantics/disabled-elements/disabled-event-dispatch.tentative.html b/testing/web-platform/tests/html/semantics/disabled-elements/disabled-event-dispatch.tentative.html new file mode 100644 index 0000000000..e2b8846fc3 --- /dev/null +++ b/testing/web-platform/tests/html/semantics/disabled-elements/disabled-event-dispatch.tentative.html @@ -0,0 +1,57 @@ +<!DOCTYPE html> +<meta name="timeout" content="long"> +<link rel=author href="mailto:jarhar@chromium.org"> +<link rel=help href="https://github.com/whatwg/html/issues/2368"> +<link rel=help href="https://github.com/whatwg/html/issues/5886"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="/resources/testdriver-actions.js"></script> + +<div id=targetparent> + <button disabled> + hello world + <span style="border: 1px solid black">child</span> + </button> + <my-control disabled> + hello world + <span style="border: 1px solid black">child</span> + </my-control> +</div> + +<script> +customElements.define('my-control', class extends HTMLElement { + static get formAssociated() { return true; } +}); + +['mousedown', 'mouseup', 'pointerdown', 'pointerup', 'click'].forEach(eventName => { + [true, false].forEach(clickChildElement => { + for (const target of targetparent.children) { + promise_test(async () => { + let parentReceivedEvent = false; + targetparent.addEventListener(eventName, () => parentReceivedEvent = true); + + let targetReceivedEvent = false; + target.addEventListener(eventName, () => targetReceivedEvent = true); + + let childReceivedEvent = false; + let targetchild = target.firstElementChild; + targetchild.addEventListener(eventName, () => childReceivedEvent = true); + + await test_driver.click(clickChildElement ? targetchild : target); + + const parentShouldReceiveEvents = eventName.startsWith('pointer'); + assert_equals(parentReceivedEvent, parentShouldReceiveEvents, + `parent element received ${eventName} events`); + + const targetShouldReceiveEvents = eventName.startsWith('pointer'); + assert_equals(targetReceivedEvent, targetShouldReceiveEvents, + `target element received ${eventName} events`); + assert_equals(childReceivedEvent, clickChildElement, + `child element received ${eventName} events`); + }, `Testing ${eventName} events when clicking ${clickChildElement ? 'child of ' : ''}disabled ${target.localName}.`); + } + }); +}); +</script> diff --git a/testing/web-platform/tests/html/semantics/disabled-elements/disabledElement.html b/testing/web-platform/tests/html/semantics/disabled-elements/disabledElement.html new file mode 100644 index 0000000000..03f57424d2 --- /dev/null +++ b/testing/web-platform/tests/html/semantics/disabled-elements/disabledElement.html @@ -0,0 +1,44 @@ +<!DOCTYPE html> +<meta charset=utf-8> +<title>Disabled elements</title> +<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org"> +<link rel=help href="https://html.spec.whatwg.org/multipage/#disabled-elements"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<div id="log"></div> +<button disabled>button</button> +<input disabled> +<select disabled> + <optgroup label="options" disabled> + <option value="option1" disabled>option1 + <option value="option2">option2 +</select> +<textarea disabled>textarea</textarea> +<fieldset disabled> + <input type=radio name=c value=0 checked> + <input type=radio name=c value=1> +</fieldset> +<a href="http://www.w3.org/" disabled>w3</a> +<span tabindex=0 disabled>foobar</span> + +<script> + test(function(){ + assert_equals(document.activeElement, document.body); + }, "The body element must be the active element if no element is focused"); + + ["button", "input", "select", "optgroup", "option", "textarea", "input[type=radio]"].forEach(function(el) { + test(function() { + var element = document.querySelector(el); + element.focus(); + assert_equals(document.activeElement, document.body, "activeElement after focus on a disabled <" + el + "> remains unchanged"); + }, "A disabled <" + el + "> should not be focusable"); + }); + + ["a", "span"].forEach(function(el) { + test(function() { + var element = document.querySelector(el); + element.focus(); + assert_equals(document.activeElement, element, "focus on a <" + el + "> with a disabled attribute should make it the activeElement"); + }, "A disabled <" + el + "> should be focusable"); + }); +</script> diff --git a/testing/web-platform/tests/html/semantics/disabled-elements/event-propagate-disabled.tentative.html b/testing/web-platform/tests/html/semantics/disabled-elements/event-propagate-disabled.tentative.html new file mode 100644 index 0000000000..9c8642d10f --- /dev/null +++ b/testing/web-platform/tests/html/semantics/disabled-elements/event-propagate-disabled.tentative.html @@ -0,0 +1,249 @@ +<!DOCTYPE html> +<meta charset="utf8"> +<meta name="timeout" content="long"> +<title>Event propagation on disabled form elements</title> +<link rel="author" href="mailto:krosylight@mozilla.com"> +<link rel="help" href="https://github.com/whatwg/html/issues/2368"> +<link rel="help" href="https://github.com/whatwg/html/issues/5886"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="/resources/testdriver-actions.js"></script> + +<div id="cases"> + <input> <!-- Sanity check with non-disabled control --> + <select disabled></select> + <select disabled> + <!-- <option> can't be clicked as it doesn't have its own painting area --> + <option>foo</option> + </select> + <fieldset disabled>Text</fieldset> + <fieldset disabled><span class="target">Span</span></fieldset> + <button disabled>Text</button> + <button disabled><span class="target">Span</span></button> + <textarea disabled></textarea> + <input disabled type="button"> + <input disabled type="checkbox"> + <input disabled type="color" value="#000000"> + <input disabled type="date"> + <input disabled type="datetime-local"> + <input disabled type="email"> + <input disabled type="file"> + <input disabled type="image"> + <input disabled type="month"> + <input disabled type="number"> + <input disabled type="password"> + <input disabled type="radio"> + <!-- Native click will click the bar --> + <input disabled type="range" value="0"> + <!-- Native click will click the slider --> + <input disabled type="range" value="50"> + <input disabled type="reset"> + <input disabled type="search"> + <input disabled type="submit"> + <input disabled type="tel"> + <input disabled type="text"> + <input disabled type="time"> + <input disabled type="url"> + <input disabled type="week"> + <my-control disabled>Text</my-control> +</div> + +<script> + customElements.define('my-control', class extends HTMLElement { + static get formAssociated() { return true; } + get disabled() { return this.hasAttribute("disabled"); } + }); + + /** + * @param {Element} element + */ + function getEventFiringTarget(element) { + return element.querySelector(".target") || element; + } + + const allEvents = ["pointermove", "mousemove", "pointerdown", "mousedown", "pointerup", "mouseup", "click"]; + + /** + * @param {*} t + * @param {Element} element + * @param {Element} observingElement + */ + function setupTest(t, element, observingElement) { + /** @type {{type: string, composedPath: Node[]}[]} */ + const observedEvents = []; + const controller = new AbortController(); + const { signal } = controller; + const listenerFn = t.step_func(event => { + observedEvents.push({ + type: event.type, + target: event.target, + isTrusted: event.isTrusted, + composedPath: event.composedPath().map(n => n.constructor.name), + }); + }); + for (const event of allEvents) { + observingElement.addEventListener(event, listenerFn, { signal }); + } + t.add_cleanup(() => controller.abort()); + + const target = getEventFiringTarget(element); + return { target, observedEvents }; + } + + /** + * @param {Element} target + * @param {*} observedEvent + */ + function shouldNotBubble(target, observedEvent) { + return ( + target.disabled && + observedEvent.isTrusted && + ["mousedown", "mouseup", "click"].includes(observedEvent.type) + ); + } + + /** + * @param {Event} event + */ + function getExpectedComposedPath(event) { + let target = event.target; + const result = []; + while (target) { + if (shouldNotBubble(target, event)) { + return result; + } + result.push(target.constructor.name); + target = target.parentNode; + } + result.push("Window"); + return result; + } + + /** + * @param {object} options + * @param {Element & { disabled: boolean }} options.element + * @param {Element} options.observingElement + * @param {string[]} options.expectedEvents + * @param {(target: Element) => (Promise<void> | void)} options.clickerFn + * @param {string} options.title + */ + function promise_event_test({ element, observingElement, expectedEvents, nonDisabledExpectedEvents, clickerFn, title }) { + promise_test(async t => { + const { target, observedEvents } = setupTest(t, element, observingElement); + + await t.step_func(clickerFn)(target); + await new Promise(resolve => t.step_timeout(resolve, 0)); + + const expected = element.disabled ? expectedEvents : nonDisabledExpectedEvents; + assert_array_equals(observedEvents.map(e => e.type), expected, "Observed events"); + + for (const observed of observedEvents) { + assert_equals(observed.target, target, `${observed.type}.target`) + assert_array_equals( + observed.composedPath, + getExpectedComposedPath(observed), + `${observed.type}.composedPath` + ); + } + + }, `${title} on ${element.outerHTML}, observed from <${observingElement.localName}>`); + } + + /** + * @param {object} options + * @param {Element & { disabled: boolean }} options.element + * @param {string[]} options.expectedEvents + * @param {(target: Element) => (Promise<void> | void)} options.clickerFn + * @param {string} options.title + */ + function promise_event_test_hierarchy({ element, expectedEvents, nonDisabledExpectedEvents, clickerFn, title }) { + const targets = [element, document.body]; + if (element.querySelector(".target")) { + targets.unshift(element.querySelector(".target")); + } + for (const observingElement of targets) { + promise_event_test({ element, observingElement, expectedEvents, nonDisabledExpectedEvents, clickerFn, title }); + } + } + + function trusted_click(target) { + // To workaround type=file clicking issue + // https://github.com/w3c/webdriver/issues/1666 + return new test_driver.Actions() + .pointerMove(0, 0, { origin: target }) + .pointerDown() + .pointerUp() + .send(); + } + + const mouseEvents = ["mousemove", "mousedown", "mouseup", "click"]; + const pointerEvents = ["pointermove", "pointerdown", "pointerup"]; + + // Events except mousedown/up/click + const allowedEvents = ["pointermove", "mousemove", "pointerdown", "pointerup"]; + + const elements = document.getElementById("cases").children; + for (const element of elements) { + // Observe on a child element of the control, if exists + const target = element.querySelector(".target"); + if (target) { + promise_event_test({ + element, + observingElement: target, + expectedEvents: allEvents, + nonDisabledExpectedEvents: allEvents, + clickerFn: trusted_click, + title: "Trusted click" + }); + } + + // Observe on the control itself + promise_event_test({ + element, + observingElement: element, + expectedEvents: allowedEvents, + nonDisabledExpectedEvents: allEvents, + clickerFn: trusted_click, + title: "Trusted click" + }); + + // Observe on document.body + promise_event_test({ + element, + observingElement: document.body, + expectedEvents: allowedEvents, + nonDisabledExpectedEvents: allEvents, + clickerFn: trusted_click, + title: "Trusted click" + }); + + const eventFirePair = [ + [MouseEvent, mouseEvents], + [PointerEvent, pointerEvents] + ]; + + for (const [eventInterface, events] of eventFirePair) { + promise_event_test_hierarchy({ + element, + expectedEvents: events, + nonDisabledExpectedEvents: events, + clickerFn: target => { + for (const event of events) { + target.dispatchEvent(new eventInterface(event, { bubbles: true })) + } + }, + title: `Dispatch new ${eventInterface.name}()` + }) + } + + promise_event_test_hierarchy({ + element, + expectedEvents: getEventFiringTarget(element) === element ? [] : ["click"], + nonDisabledExpectedEvents: ["click"], + clickerFn: target => target.click(), + title: `click()` + }) + } +</script> |