invoker-utils.js (5225B)
1 function waitForRender() { 2 return new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve))); 3 } 4 async function clickOn(element) { 5 await waitForRender(); 6 let rect = element.getBoundingClientRect(); 7 let actions = new test_driver.Actions(); 8 // FIXME: Switch to pointerMove(0, 0, {origin: element}) once 9 // https://github.com/web-platform-tests/wpt/issues/41257 is fixed. 10 await actions 11 .pointerMove(Math.round(rect.x + rect.width / 2), Math.round(rect.y + rect.height / 2), {}) 12 .pointerDown({button: actions.ButtonType.LEFT}) 13 .pointerUp({button: actions.ButtonType.LEFT}) 14 .send(); 15 await waitForRender(); 16 } 17 async function focusOn(element) { 18 element.focus(); 19 await waitForRender(); 20 assert_equals(document.activeElement,element,'focus should be on element'); 21 } 22 async function hoverOver(element) { 23 await waitForRender(); 24 let rect = element.getBoundingClientRect(); 25 let actions = new test_driver.Actions(); 26 // FIXME: Switch to pointerMove(0, 0, {origin: element}) once 27 // https://github.com/web-platform-tests/wpt/issues/41257 is fixed. 28 await actions 29 .pointerMove(Math.round(rect.x + rect.width / 2), Math.round(rect.y + rect.height / 2), {}) 30 .send(); 31 await waitForRender(); 32 } 33 async function longPress(element) { 34 await waitForRender(); 35 let rect = element.getBoundingClientRect(); 36 // FIXME: Switch to pointerMove(0, 0, {origin: element}) once 37 // https://github.com/web-platform-tests/wpt/issues/41257 is fixed. 38 const x = Math.round(rect.x + rect.width / 2); 39 const y = Math.round(rect.y + rect.height / 2); 40 await new test_driver.Actions() 41 .addPointer("touchPointer", "touch") 42 .pointerMove(x, y, {sourceName: "touchPointer"}) 43 .pointerDown({sourceName: "touchPointer"}) 44 // This needs to be long enough to trigger long-press on all platforms: 45 .pause(1000, "pointer", {sourceName: "touchPointer"}) 46 .pointerUp({sourceName: "touchPointer"}) 47 .send(); 48 await waitForRender(); 49 } 50 function mouseOverAndRecord(t,element) { 51 let timingInfo = {element, started: performance.now()}; 52 return (new test_driver.Actions()) 53 .pointerMove(0, 0, {origin: element}) 54 .send() 55 .then(() => timingInfo); 56 } 57 function focusAndRecord(t,element) { 58 let timingInfo = {element, started: performance.now()}; 59 element.focus(); 60 return timingInfo; 61 } 62 async function hoverOrFocus(invokerMethod,element) { 63 if (invokerMethod === 'hover') { 64 await hoverOver(element); 65 } else { 66 assert_equals(invokerMethod,'focus'); 67 element.focus(); 68 await waitForRender(); 69 } 70 } 71 async function mouseOverOrFocusAndRecord(t,invokerMethod,element) { 72 if (invokerMethod === 'hover') { 73 return await mouseOverAndRecord(t,element); 74 } else { 75 assert_equals(invokerMethod,'focus'); 76 return focusAndRecord(t,element); 77 } 78 } 79 // Note that this may err on the side of being too large (reporting a number 80 // that is larger than the actual time since the mouseover happened), due to how 81 // `timingInfo.started` is initialized, on first mouse move. However, this 82 // function is intended to be used as a detector for the test harness taking too 83 // long for some tests, so it's ok to be conservative. 84 function msSinceMouseOver(timingInfo) { 85 return performance.now() - timingInfo.started; 86 } 87 async function waitForHoverTime(hoverWaitTimeMs) { 88 await new Promise(resolve => step_timeout(resolve,hoverWaitTimeMs)); 89 await waitForRender(); 90 }; 91 92 async function createPopoverAndInvokerForHoverTests(test, showdelayMs, hideDelayMs) { 93 const unrelated = document.createElement('div'); 94 unrelated.tabIndex = 0; 95 document.body.appendChild(unrelated); 96 unrelated.textContent = 'Unrelated'; 97 unrelated.setAttribute('style','position:fixed; top:0;'); 98 // Ensure we never hover over or focus on an active interestfor element. 99 unrelated.focus(); 100 await hoverOver(unrelated); 101 const popover = document.createElement('div'); 102 popover.popover = 'auto'; 103 popover.setAttribute('style','inset:auto; top: 100px;'); 104 popover.textContent = 'Popover'; 105 document.body.appendChild(popover); 106 let invoker = document.createElement('button'); 107 invoker.interestForElement = popover; 108 invoker.setAttribute('style',` 109 interest-delay-start: ${showdelayMs}ms; 110 interest-delay-end: ${hideDelayMs}ms; 111 position:fixed; 112 top:200px; 113 width:fit-content; 114 height:fit-content; 115 `); 116 invoker.innerText = 'Invoker'; 117 document.body.appendChild(invoker); 118 const actualShowDelay = Number(getComputedStyle(invoker).interestDelayStart.slice(0,-1))*1000; 119 assert_equals(actualShowDelay,showdelayMs,'interest-delay-start is incorrect'); 120 const actualHideDelay = Number(getComputedStyle(invoker).interestDelayEnd.slice(0,-1))*1000; 121 assert_equals(actualHideDelay,hideDelayMs,'interest-delay-end is incorrect'); 122 test.add_cleanup(() => { 123 popover.remove(); 124 invoker.remove(); 125 unrelated.remove(); 126 }); 127 assert_false(popover.matches(':popover-open'),'The popover should start out closed'); 128 return {popover, invoker, unrelated}; 129 } 130 async function sendLoseInterestHotkey() { 131 const kEscape = '\uE00C'; 132 await new test_driver.Actions() 133 .keyDown(kEscape) 134 .keyUp(kEscape) 135 .send(); 136 await waitForRender(); 137 }