summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/html/semantics/popovers/popover-target-action-hover.tentative.html
diff options
context:
space:
mode:
Diffstat (limited to 'testing/web-platform/tests/html/semantics/popovers/popover-target-action-hover.tentative.html')
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-target-action-hover.tentative.html180
1 files changed, 180 insertions, 0 deletions
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-target-action-hover.tentative.html b/testing/web-platform/tests/html/semantics/popovers/popover-target-action-hover.tentative.html
new file mode 100644
index 0000000000..b03ec78ebf
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-target-action-hover.tentative.html
@@ -0,0 +1,180 @@
+<!DOCTYPE html>
+<meta charset="utf-8" />
+<title>The popovertargetaction=hover behavior</title>
+<link rel="author" href="mailto:masonf@chromium.org">
+<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>
+
+<body>
+<style>
+.unrelated {top:0;}
+.invoker {top:100px; width:fit-content; height:fit-content;}
+[popover] {top: 200px;}
+.offset-child {top:300px; left:300px;}
+</style>
+
+<script>
+const popoverShowDelay = 100; // The CSS delay setting.
+const hoverWaitTime = 200; // How long to wait to cover the delay for sure.
+async function makePopoverAndInvoker(test, popoverType, invokerType, delayMs) {
+ delayMs = delayMs || popoverShowDelay;
+ const popover = Object.assign(document.createElement('div'),{popover: popoverType});
+ document.body.appendChild(popover);
+ popover.textContent = 'Popover';
+ // Set popover-show-delay on the popover to 0 - it should be ignored.
+ popover.setAttribute('style',`popover-show-delay: 0; popover-hide-delay: 1000s;`);
+ let invoker = document.createElement('button');
+ invoker.setAttribute('class','invoker');
+ invoker.popoverTargetElement = popover;
+ invoker.popoverTargetAction = "hover";
+ // Set popover-hide-delay on the invoker to 0 - it should be ignored.
+ invoker.setAttribute('style',`popover-show-delay: ${delayMs}ms; popover-hide-delay: 0;`);
+ document.body.appendChild(invoker);
+ const actualHoverDelay = Number(getComputedStyle(invoker)['popoverShowDelay'].slice(0,-1))*1000;
+ assert_equals(actualHoverDelay,delayMs,'popover-show-delay is incorrect');
+ const originalInvoker = invoker;
+ const reassignPopoverFn = (p) => {originalInvoker.popoverTargetElement = p};
+ switch (invokerType) {
+ case 'plain':
+ // Invoker is just a button.
+ invoker.textContent = 'Invoker';
+ break;
+ case 'nested':
+ // Invoker is just a button containing a div.
+ const child1 = invoker.appendChild(document.createElement('div'));
+ child1.textContent = 'Invoker';
+ break;
+ case 'nested-offset':
+ // Invoker is a child of the invoking button, and is not contained within
+ // the bounds of the popovertarget element.
+ invoker.textContent = 'Invoker';
+ // Reassign invoker to the child:
+ invoker = invoker.appendChild(document.createElement('div'));
+ invoker.textContent = 'Invoker child';
+ invoker.setAttribute('class','offset-child');
+ break;
+ case 'none':
+ // No invoker.
+ invoker.remove();
+ break;
+ default:
+ assert_unreached(`Invalid invokerType ${invokerType}`);
+ }
+ const unrelated = document.createElement('div');
+ document.body.appendChild(unrelated);
+ unrelated.textContent = 'Unrelated';
+ unrelated.setAttribute('class','unrelated');
+ test.add_cleanup(async () => {
+ popover.remove();
+ invoker.remove();
+ originalInvoker.remove();
+ unrelated.remove();
+ await waitForRender();
+ });
+ await mouseOver(unrelated); // Start by mousing over the unrelated element
+ await waitForRender();
+ return {popover,invoker,reassignPopoverFn};
+}
+
+// NOTE about testing methodology:
+// This test checks whether popovers are triggered *after* the appropriate hover
+// delay. The delay used for testing is kept low, to avoid this test taking too
+// long, but that means that sometimes on a slow bot/client, the hover delay can
+// elapse before we are able to check the popover status. And that can make this
+// test flaky. To avoid that, the msSinceMouseOver() function is used to check
+// that not-too-much time has passed, and if it has, the test is simply skipped.
+
+["auto","hint","manual"].forEach(type => {
+ ["plain","nested","nested-offset"].forEach(invokerType => {
+ promise_test(async (t) => {
+ const {popover,invoker} = await makePopoverAndInvoker(t,type,invokerType);
+ assert_false(popover.matches(':popover-open'));
+ await mouseOver(invoker);
+ let showing = popover.matches(':popover-open');
+ // See NOTE above.
+ if (msSinceMouseOver() < popoverShowDelay)
+ assert_false(showing,'popover should not show immediately');
+ await waitForHoverTime(hoverWaitTime);
+ assert_true(msSinceMouseOver() >= hoverWaitTime,'waitForHoverTime should wait the specified time');
+ assert_true(popover.matches(':popover-open'),'popover should show after delay');
+ assert_true(hoverWaitTime > popoverShowDelay,'popoverShowDelay is the CSS setting, hoverWaitTime should be longer than that');
+ popover.hidePopover(); // Cleanup
+ },`popovertargetaction=hover shows a popover with popover=${type}, invokerType=${invokerType}`);
+
+ promise_test(async (t) => {
+ const {popover,invoker} = await makePopoverAndInvoker(t,type,invokerType);
+ assert_false(popover.matches(':popover-open'));
+ invoker.click(); // Click the invoker
+ assert_true(popover.matches(':popover-open'),'Clicking the invoker should show the popover, even when popovertargetaction=hover');
+ popover.hidePopover(); // Cleanup
+ },`popovertargetaction=hover should also allow click activation, for popover=${type}, invokerType=${invokerType}`);
+
+ promise_test(async (t) => {
+ const longerHoverDelay = hoverWaitTime*2;
+ const {popover,invoker} = await makePopoverAndInvoker(t,type,invokerType,longerHoverDelay);
+ await mouseOver(invoker);
+ let showing = popover.matches(':popover-open');
+ // See NOTE above.
+ if (msSinceMouseOver() >= longerHoverDelay)
+ return; // The WPT runner was too slow.
+ assert_false(showing,'popover should not show immediately');
+ await waitForHoverTime(hoverWaitTime);
+ showing = popover.matches(':popover-open');
+ if (msSinceMouseOver() >= longerHoverDelay)
+ return; // The WPT runner was too slow.
+ assert_false(showing,'popover should not show after not long enough of a delay');
+ },`popovertargetaction=hover popover-show-delay is respected (popover=${type}, invokerType=${invokerType})`);
+
+ promise_test(async (t) => {
+ const {popover,invoker} = await makePopoverAndInvoker(t,type,invokerType);
+ popover.showPopover();
+ assert_true(popover.matches(':popover-open'));
+ await mouseOver(invoker);
+ assert_true(popover.matches(':popover-open'),'popover should stay showing on mouseover');
+ await waitForHoverTime(hoverWaitTime);
+ assert_true(popover.matches(':popover-open'),'popover should stay showing after delay');
+ popover.hidePopover(); // Cleanup
+ },`popovertargetaction=hover does nothing when popover is already showing (popover=${type}, invokerType=${invokerType})`);
+
+ promise_test(async (t) => {
+ const {popover,invoker} = await makePopoverAndInvoker(t,type,invokerType);
+ await mouseOver(invoker);
+ let showing = popover.matches(':popover-open');
+ popover.remove();
+ // See NOTE above.
+ if (msSinceMouseOver() >= popoverShowDelay)
+ return; // The WPT runner was too slow.
+ assert_false(showing,'popover should not show immediately');
+ await waitForHoverTime(hoverWaitTime);
+ assert_false(popover.matches(':popover-open'),'popover should not show even after a delay');
+ // Now put it back in the document and make sure it doesn't trigger.
+ document.body.appendChild(popover);
+ await waitForHoverTime(hoverWaitTime);
+ assert_false(popover.matches(':popover-open'),'popover should not show even when returned to the document');
+ },`popovertargetaction=hover does nothing when popover is moved out of the document (popover=${type}, invokerType=${invokerType})`);
+
+ promise_test(async (t) => {
+ const {popover,invoker,reassignPopoverFn} = await makePopoverAndInvoker(t,type,invokerType);
+ const popover2 = Object.assign(document.createElement('div'),{popover: type});
+ document.body.appendChild(popover2);
+ t.add_cleanup(() => popover2.remove());
+ await mouseOver(invoker);
+ let eitherShowing = popover.matches(':popover-open') || popover2.matches(':popover-open');
+ reassignPopoverFn(popover2);
+ // See NOTE above.
+ if (msSinceMouseOver() >= popoverShowDelay)
+ return; // The WPT runner was too slow.
+ assert_false(eitherShowing,'popover should not show immediately');
+ await waitForHoverTime(hoverWaitTime);
+ assert_false(popover.matches(':popover-open'),'popover #1 should not show since popovertarget was reassigned');
+ assert_false(popover2.matches(':popover-open'),'popover #2 should not show since popovertarget was reassigned');
+ },`popovertargetaction=hover does nothing when target changes (popover=${type}, invokerType=${invokerType})`);
+ });
+});
+</script>