Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Warnings

<!DOCTYPE html>
<meta charset="utf-8" />
<link rel="author" href="mailto:masonf@chromium.org">
<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/invoker-utils.js"></script>
<body>
<script>
// NOTE about testing methodology:
// This test uses popovers as an invoker target, and checks whether they 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. Because of this
// methodology, many/most of these tests will pass on browsers that do not
// implement `interesttarget`. See the `interesttarget-basic-delays` test.
const invokerShowDelay = 100; // The CSS delay setting.
const hoverWaitTime = 200; // How long to wait to cover the delay for sure.
async function makePopoverAndInvoker(test, invokerLayout, delayMs) {
// This ensures the tests in this file don't succeed sometimes, due to the above NOTE.
assert_true(HTMLAnchorElement.prototype.hasOwnProperty('interestTargetElement'),'interesttarget is not supported');
if (delayMs === undefined) {
delayMs = invokerShowDelay;
}
let {popover, invoker, unrelated} = createPopoverAndInvokerForHoverTests(test, delayMs, 1000000);
invoker.setAttribute('class','invoker');
const originalInvoker = invoker;
const reassignInvokerTargetFn = (p) => {originalInvoker.interestTargetElement = p};
switch (invokerLayout) {
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 interesttarget element.
invoker.textContent = 'Invoker';
// Reassign invoker to the child:
invoker = invoker.appendChild(document.createElement('div'));
invoker.textContent = 'Invoker child';
invoker.setAttribute('style','position:relative; top:300px; left:300px;');
break;
default:
assert_unreached(`Invalid invokerLayout ${invokerLayout}`);
}
test.add_cleanup(async () => {
originalInvoker.remove();
await waitForRender();
});
await mouseOverAndRecord(unrelated); // Start by mousing over the unrelated element
await waitForRender();
assert_false(popover.matches(':popover-open'));
return {popover,invoker,reassignInvokerTargetFn};
}
["plain","nested","nested-offset"].forEach(invokerLayout => {
promise_test(async (t) => {
const {popover,invoker} = await makePopoverAndInvoker(t,invokerLayout);
await mouseOverAndRecord(invoker);
let showing = popover.matches(':popover-open');
// See NOTE above.
if (msSinceMouseOver() < invokerShowDelay)
assert_false(showing,'interest should not be shown immediately');
await waitForHoverTime(hoverWaitTime);
assert_true(msSinceMouseOver() >= hoverWaitTime,'waitForHoverTime should wait the specified time');
assert_true(popover.matches(':popover-open'),'interest should be shown after the delay');
assert_true(hoverWaitTime > invokerShowDelay,'invokerShowDelay is the CSS setting, hoverWaitTime should be longer than that');
},`interesttarget fires after a delay, invokerLayout=${invokerLayout}`);
promise_test(async (t) => {
const {popover,invoker} = await makePopoverAndInvoker(t,invokerLayout);
assert_false(popover.matches(':popover-open'));
invoker.click(); // Click the invoker
assert_false(popover.matches(':popover-open'),'Clicking the invoker should not "show interest"');
},`interesttarget should not trigger via mouse click, invokerLayout=${invokerLayout}`);
promise_test(async (t) => {
const longerHoverDelay = hoverWaitTime*2;
const {popover,invoker} = await makePopoverAndInvoker(t,invokerLayout,longerHoverDelay);
await mouseOverAndRecord(invoker);
await waitForHoverTime(hoverWaitTime);
showing = popover.matches(':popover-open');
if (msSinceMouseOver() >= longerHoverDelay)
return; // The WPT runner was too slow.
assert_false(showing,'interesttarget should respect CSS setting');
},`interesttarget interest-target-show-delay is respected, invokerLayout=${invokerLayout}`);
promise_test(async (t) => {
const {popover,invoker} = await makePopoverAndInvoker(t,invokerLayout);
popover.showPopover();
assert_true(popover.matches(':popover-open'));
let gotInterest = false;
popover.addEventListener('interest',() => (gotInterest=true),{once:true});
await mouseOverAndRecord(invoker);
const stillOpen = popover.matches(':popover-open');
await waitForHoverTime(hoverWaitTime);
assert_true(popover.matches(':popover-open'),'popover should stay showing after delay');
assert_true(stillOpen,'popover should have been open before the delay also');
assert_true(gotInterest,'interest event should still be fired');
},`interesttarget does not close an already-open popover, invokerLayout=${invokerLayout}`);
promise_test(async (t) => {
const {popover,invoker} = await makePopoverAndInvoker(t,invokerLayout);
popover.remove(); // Remove from the document
let gotInterest = false;
popover.addEventListener('interest',() => (gotInterest=true),{once:true});
await mouseOverAndRecord(invoker);
await waitForHoverTime(hoverWaitTime);
assert_false(gotInterest,'interest should not be shown if the target is removed');
// Now put it back in the document and make sure it doesn't trigger.
document.body.appendChild(popover);
await waitForHoverTime(hoverWaitTime);
assert_false(gotInterest,'interest should not be shown even when returned to the document');
},`interesttarget does nothing when the target is moved out of the document, invokerLayout=${invokerLayout}`);
promise_test(async (t) => {
const {popover,invoker,reassignInvokerTargetFn} = await makePopoverAndInvoker(t,invokerLayout);
const popover2 = document.createElement('div');
popover2.popover = 'auto';
document.body.appendChild(popover2);
t.add_cleanup(() => popover2.remove());
await mouseOverAndRecord(invoker);
let eitherShowing = popover.matches(':popover-open') || popover2.matches(':popover-open');
reassignInvokerTargetFn(popover2);
// See NOTE above.
if (msSinceMouseOver() >= invokerShowDelay)
return; // The WPT runner was too slow.
assert_false(eitherShowing,'interest should should not be shown immediately');
await waitForHoverTime(hoverWaitTime);
assert_false(popover.matches(':popover-open'),'old target should not receive interest, since interesttarget was reassigned');
assert_false(popover2.matches(':popover-open'),'new target should not receive interest, since interesttarget was reassigned');
},`interesttarget does nothing when the target changes, invokerLayout=${invokerLayout}`);
});
</script>