summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/html/semantics/popovers/popover-attribute-basic.html
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /testing/web-platform/tests/html/semantics/popovers/popover-attribute-basic.html
parentInitial commit. (diff)
downloadfirefox-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/popovers/popover-attribute-basic.html')
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-attribute-basic.html359
1 files changed, 359 insertions, 0 deletions
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-attribute-basic.html b/testing/web-platform/tests/html/semantics/popovers/popover-attribute-basic.html
new file mode 100644
index 0000000000..2af3bbc137
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-attribute-basic.html
@@ -0,0 +1,359 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/popover.html">
+<link rel=help href="https://open-ui.org/components/popover.research.explainer">
+<meta name="timeout" content="long">
+<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/popover-utils.js"></script>
+
+<div id=popovers>
+ <div popover id=boolean>Popover</div>
+ <div popover="">Popover</div>
+ <div popover=auto>Popover</div>
+ <div popover=hint>Popover</div>
+ <div popover=manual>Popover</div>
+ <article popover>Different element type</article>
+ <header popover>Different element type</header>
+ <nav popover>Different element type</nav>
+ <input type=text popover value="Different element type">
+ <dialog popover>Dialog with popover attribute</dialog>
+ <dialog popover="manual">Dialog with popover=manual</dialog>
+ <div popover=true>Invalid popover value - defaults to popover=manual</div>
+ <div popover=popover>Invalid popover value - defaults to popover=manual</div>
+ <div popover=invalid>Invalid popover value - defaults to popover=manual</div>
+</div>
+
+<div id=nonpopovers>
+ <div>Not a popover</div>
+ <dialog open>Dialog without popover attribute</dialog>
+</div>
+
+<div id=outside></div>
+<style>
+[popover] {
+ inset:auto;
+ top:0;
+ left:0;
+}
+#outside {
+ position:fixed;
+ top:200px;
+ left:200px;
+ height:10px;
+ width:10px;
+}
+</style>
+
+<script>
+setup({ explicit_done: true });
+window.onload = () => {
+ const outsideElement = document.getElementById('outside');
+
+ // Start with the provided examples:
+ Array.from(document.getElementById('popovers').children).forEach(popover => {
+ test((t) => {
+ assertIsFunctionalPopover(popover, true);
+ }, `The element ${popover.outerHTML} should behave as a popover.`);
+ });
+ Array.from(document.getElementById('nonpopovers').children).forEach(nonPopover => {
+ test((t) => {
+ assertNotAPopover(nonPopover);
+ }, `The element ${nonPopover.outerHTML} should *not* behave as a popover.`);
+ });
+
+ function createPopover(t) {
+ const popover = document.createElement('div');
+ document.body.appendChild(popover);
+ t.add_cleanup(() => popover.remove());
+ popover.setAttribute('popover','auto');
+ return popover;
+ }
+
+ test((t) => {
+ // You can set the `popover` attribute to anything.
+ // Setting the `popover` IDL to a string sets the content attribute to exactly that, always.
+ // Getting the `popover` IDL value only retrieves valid values.
+ const popover = createPopover(t);
+ assert_equals(popover.popover,'auto');
+ popover.setAttribute('popover','auto');
+ assert_equals(popover.popover,'auto');
+ popover.setAttribute('popover','AuTo');
+ assert_equals(popover.popover,'auto','Case is normalized in IDL');
+ assert_equals(popover.getAttribute('popover'),'AuTo','Case is *not* normalized/changed in the content attribute');
+ popover.popover='aUtO';
+ assert_equals(popover.popover,'auto','Case is normalized in IDL');
+ assert_equals(popover.getAttribute('popover'),'aUtO','Value set from IDL is propagated exactly to the content attribute');
+ popover.setAttribute('popover','invalid');
+ assert_equals(popover.popover,'manual','Invalid values should reflect as "manual"');
+ popover.removeAttribute('popover');
+ assert_equals(popover.popover,null,'No value should reflect as null');
+ if (popoverHintSupported()) {
+ popover.popover='hint';
+ assert_equals(popover.getAttribute('popover'),'hint');
+ }
+ popover.popover='auto';
+ assert_equals(popover.getAttribute('popover'),'auto');
+ popover.popover='';
+ assert_equals(popover.getAttribute('popover'),'');
+ assert_equals(popover.popover,'auto');
+ popover.popover='AuTo';
+ assert_equals(popover.getAttribute('popover'),'AuTo');
+ assert_equals(popover.popover,'auto');
+ popover.popover='invalid';
+ assert_equals(popover.getAttribute('popover'),'invalid','IDL setter allows any value');
+ assert_equals(popover.popover,'manual','but IDL getter reflects "manual"');
+ popover.popover='';
+ assert_equals(popover.getAttribute('popover'),'','IDL setter propagates exactly');
+ assert_equals(popover.popover,'auto','Empty should map to auto in IDL');
+ popover.popover='auto';
+ popover.popover=null;
+ assert_equals(popover.getAttribute('popover'),null,'Setting null for the IDL property should remove the content attribute');
+ assert_equals(popover.popover,null,'Null returns null');
+ popover.popover='auto';
+ popover.popover=undefined;
+ assert_equals(popover.getAttribute('popover'),null,'Setting undefined for the IDL property should remove the content attribute');
+ assert_equals(popover.popover,null,'undefined returns null');
+ },'IDL attribute reflection');
+
+ test((t) => {
+ const popover = createPopover(t);
+ assertIsFunctionalPopover(popover, true);
+ popover.removeAttribute('popover');
+ assertNotAPopover(popover);
+ popover.setAttribute('popover','AuTo');
+ assertIsFunctionalPopover(popover, true);
+ popover.removeAttribute('popover');
+ popover.setAttribute('PoPoVeR','AuTo');
+ assertIsFunctionalPopover(popover, true);
+ // Via IDL also
+ popover.popover = 'auto';
+ assertIsFunctionalPopover(popover, true);
+ popover.popover = 'aUtO';
+ assertIsFunctionalPopover(popover, true);
+ popover.popover = 'invalid'; // treated as "manual"
+ assertIsFunctionalPopover(popover, true);
+ },'Popover attribute value should be case insensitive');
+
+ test((t) => {
+ const popover = createPopover(t);
+ assertIsFunctionalPopover(popover, true);
+ popover.setAttribute('popover','manual'); // Change popover type
+ assertIsFunctionalPopover(popover, true);
+ popover.setAttribute('popover','invalid'); // Change popover type to something invalid
+ assertIsFunctionalPopover(popover, true);
+ popover.popover = 'manual'; // Change popover type via IDL
+ assertIsFunctionalPopover(popover, true);
+ popover.popover = 'invalid'; // Make invalid via IDL (treated as "manual")
+ assertIsFunctionalPopover(popover, true);
+ },'Changing attribute values for popover should work');
+
+ test((t) => {
+ const popover = createPopover(t);
+ popover.showPopover();
+ assert_true(popover.matches(':popover-open'));
+ if (popoverHintSupported()) {
+ popover.setAttribute('popover','hint'); // Change popover type
+ assert_false(popover.matches(':popover-open'));
+ popover.showPopover();
+ assert_true(popover.matches(':popover-open'));
+ }
+ popover.setAttribute('popover','manual');
+ assert_false(popover.matches(':popover-open'));
+ popover.showPopover();
+ assert_true(popover.matches(':popover-open'));
+ popover.setAttribute('popover','invalid');
+ assert_true(popover.matches(':popover-open'),'From "manual" to "invalid" (which is interpreted as "manual") should not close the popover');
+ popover.setAttribute('popover','auto');
+ assert_false(popover.matches(':popover-open'),'From "invalid" ("manual") to "auto" should hide the popover');
+ popover.showPopover();
+ assert_true(popover.matches(':popover-open'));
+ popover.setAttribute('popover','invalid');
+ assert_false(popover.matches(':popover-open'),'From "auto" to "invalid" (which is interpreted as "manual") should close the popover');
+ },'Changing attribute values should close open popovers');
+
+ const validTypes = popoverHintSupported() ? ["auto","hint","manual"] : ["auto","manual"];
+ validTypes.forEach(type => {
+ test((t) => {
+ const popover = createPopover(t);
+ popover.setAttribute('popover',type);
+ popover.showPopover();
+ assert_true(popover.matches(':popover-open'));
+ popover.remove();
+ assert_false(popover.matches(':popover-open'));
+ document.body.appendChild(popover);
+ assert_false(popover.matches(':popover-open'));
+ },`Removing a visible popover=${type} element from the document should close the popover`);
+
+ test((t) => {
+ const popover = createPopover(t);
+ popover.setAttribute('popover',type);
+ popover.showPopover();
+ assert_true(popover.matches(':popover-open'));
+ assert_false(popover.matches(':modal'));
+ popover.hidePopover();
+ },`A showing popover=${type} does not match :modal`);
+
+ test((t) => {
+ const popover = createPopover(t);
+ popover.setAttribute('popover',type);
+ assert_false(popover.matches(':popover-open'));
+ // FIXME: Once :open/:closed are defined in HTML we should remove these two constants.
+ const openPseudoClassIsSupported = CSS.supports('selector(:open))');
+ const closePseudoClassIsSupported = CSS.supports('selector(:closed))');
+ assert_false(openPseudoClassIsSupported && popover.matches(':open'),'popovers never match :open');
+ assert_false(closePseudoClassIsSupported && popover.matches(':closed'),'popovers never match :closed');
+ popover.showPopover();
+ assert_true(popover.matches(':popover-open'));
+ assert_false(openPseudoClassIsSupported && popover.matches(':open'),'popovers never match :open');
+ assert_false(closePseudoClassIsSupported && popover.matches(':closed'),'popovers never match :closed');
+ popover.hidePopover();
+ },`A popover=${type} never matches :open or :closed`);
+ });
+
+ test((t) => {
+ const other_popover = createPopover(t);
+ other_popover.setAttribute('popover','auto');
+ other_popover.showPopover();
+ const popover = createPopover(t);
+ popover.setAttribute('popover','auto');
+ other_popover.addEventListener('beforetoggle', (e) => {
+ if (e.newState !== "closed")
+ return;
+ popover.setAttribute('popover','manual');
+ },{once: true});
+ assert_true(other_popover.matches(':popover-open'));
+ assert_false(popover.matches(':popover-open'));
+ assert_throws_dom('InvalidStateError', () => popover.showPopover());
+ assert_false(other_popover.matches(':popover-open'),'unrelated popover is hidden');
+ assert_false(popover.matches(':popover-open'),'popover is not shown if its type changed during show');
+ },`Changing the popover type in a "beforetoggle" event handler should throw an exception (during showPopover())`);
+
+ test((t) => {
+ const popover = createPopover(t);
+ popover.setAttribute('popover','auto');
+ const other_popover = createPopover(t);
+ other_popover.setAttribute('popover','auto');
+ popover.appendChild(other_popover);
+ popover.showPopover();
+ other_popover.showPopover();
+ let nested_popover_hidden=false;
+ other_popover.addEventListener('beforetoggle', (e) => {
+ if (e.newState !== "closed")
+ return;
+ nested_popover_hidden = true;
+ popover.setAttribute('popover','manual');
+ },{once: true});
+ popover.addEventListener('beforetoggle', (e) => {
+ if (e.newState !== "closed")
+ return;
+ assert_true(nested_popover_hidden,'The nested popover should be hidden first');
+ },{once: true});
+ assert_true(popover.matches(':popover-open'));
+ assert_true(other_popover.matches(':popover-open'));
+ popover.hidePopover(); // Calling hidePopover on a hidden popover should not throw.
+ assert_false(other_popover.matches(':popover-open'),'unrelated popover is hidden');
+ assert_false(popover.matches(':popover-open'),'popover is still hidden if its type changed during hide event');
+ other_popover.hidePopover(); // Calling hidePopover on a hidden popover should not throw.
+ },`Changing the popover type in a "beforetoggle" event handler during hidePopover() should not throw an exception`);
+
+ test(t => {
+ const popover = document.createElement('div');
+ assert_throws_dom('NotSupportedError', () => popover.hidePopover(),
+ 'Calling hidePopover on an element without a popover attribute should throw.');
+ popover.setAttribute('popover', 'auto');
+ popover.hidePopover(); // Calling hidePopover on a disconnected popover should not throw.
+ assert_throws_dom('InvalidStateError', () => popover.showPopover(),
+ 'Calling showPopover on a disconnected popover should throw.');
+ },'Calling hidePopover on a disconnected popover should not throw.');
+
+ function interpretedType(typeString,method) {
+ if (validTypes.includes(typeString))
+ return typeString;
+ if (typeString === undefined)
+ return "invalid-value-undefined";
+ if (method === "idl" && typeString === null)
+ return "invalid-value-idl-null";
+ return "manual"; // Invalid types default to "manual"
+ }
+ function setPopoverValue(popover,type,method) {
+ switch (method) {
+ case "attr":
+ if (type === undefined) {
+ popover.removeAttribute('popover');
+ } else {
+ popover.setAttribute('popover',type);
+ }
+ break;
+ case "idl":
+ popover.popover = type;
+ break;
+ default:
+ assert_notreached();
+ }
+ }
+ ["attr","idl"].forEach(method => {
+ validTypes.forEach(type => {
+ [...validTypes,"invalid",null,undefined].forEach(newType => {
+ [...validTypes,"invalid",null,undefined].forEach(inEventType => {
+ promise_test(async (t) => {
+ const popover = createPopover(t);
+ setPopoverValue(popover,type,method);
+ popover.showPopover();
+ assert_true(popover.matches(':popover-open'));
+ let gotEvent = false;
+ popover.addEventListener('beforetoggle', (e) => {
+ if (e.newState !== "closed")
+ return;
+ gotEvent = true;
+ setPopoverValue(popover,inEventType,method);
+ },{once:true});
+ setPopoverValue(popover,newType,method);
+ if (type===interpretedType(newType,method)) {
+ // Keeping the type the same should not hide it or fire events.
+ assert_true(popover.matches(':popover-open'),'popover should remain open when not changing the type');
+ assert_false(gotEvent);
+ try {
+ popover.hidePopover(); // Cleanup
+ } catch (e) {}
+ } else {
+ // Changing the type at all should hide the popover. The hide event
+ // handler should run, set a new type, and that type should end up
+ // as the final result.
+ assert_false(popover.matches(':popover-open'));
+ assert_true(gotEvent);
+ if (inEventType === undefined || (method ==="idl" && inEventType === null)) {
+ assert_throws_dom("NotSupportedError",() => popover.showPopover(),'We should have removed the popover attribute, so showPopover should throw');
+ } else {
+ // Make sure the attribute is correct.
+ assert_equals(popover.getAttribute('popover'),String(inEventType),'Content attribute');
+ assert_equals(popover.popover, interpretedType(inEventType,method),'IDL attribute');
+ // Make sure the type is really correct, via behavior.
+ popover.showPopover(); // Show it
+ assert_true(popover.matches(':popover-open'),'Popover should function');
+ await clickOn(outsideElement); // Try to light dismiss
+ switch (interpretedType(inEventType,method)) {
+ case 'manual':
+ assert_true(popover.matches(':popover-open'),'A popover=manual should not light-dismiss');
+ popover.hidePopover();
+ break;
+ case 'auto':
+ case 'hint':
+ assert_false(popover.matches(':popover-open'),'A popover=auto should light-dismiss');
+ break;
+ }
+ }
+ }
+ },`Changing a popover from ${type} to ${newType} (via ${method}), and then ${inEventType} during 'beforetoggle' works`);
+ });
+ });
+ });
+ });
+
+ done();
+};
+</script>