interestfor-basic-behavior.tentative.html (5636B)
1 <!DOCTYPE html> 2 <meta charset="utf-8" /> 3 <meta name="timeout" content="long"> 4 <link rel="author" href="mailto:masonf@chromium.org"> 5 <link rel="help" href="https://open-ui.org/components/interest-invokers.explainer/" /> 6 <link rel="help" href="https://github.com/whatwg/html/pull/11006" /> 7 <script src="/resources/testharness.js"></script> 8 <script src="/resources/testharnessreport.js"></script> 9 <script src="/resources/testdriver.js"></script> 10 <script src="/resources/testdriver-actions.js"></script> 11 <script src="/resources/testdriver-vendor.js"></script> 12 <script src="resources/invoker-utils.js"></script> 13 14 <meta name=variant content=?method=hover> 15 <meta name=variant content=?method=focus> 16 17 <button data-testcase="<button>" interestfor=target>Button</button> 18 19 <a data-testcase="<a>" href="#" interestfor=target>Link</a> 20 21 <img src="/images/blue.png" usemap="#map" id=areatarget> 22 <map id=map> 23 <area data-testcase="<area>" data-hover="areatarget" interestfor=target href="/" shape=default> 24 </map> 25 26 <svg viewBox="0 0 100 100" style="width: 100px" xmlns="http://www.w3.org/2000/svg"> 27 <a data-testcase="SVG <a>" href="#" interestfor=target> 28 <text x=50 y=90>SVG A</text> 29 </a> 30 </svg> 31 32 <a data-testcase="Broken img" href="#" interestfor=target> 33 <img src="broken" width="50" height="50"> 34 </a> 35 36 <a data-testcase="SVG <use>" href="#" interestfor=target> 37 <svg width="50" height="50" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> 38 <use xlink:href="#thick-star"></use> 39 </svg> 40 </a> 41 <svg style="display: none;"> 42 <symbol id="thick-star" viewBox="0 0 24 24"> 43 <path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z" 44 fill="yellow" stroke="black" stroke-width="2" stroke-linejoin="round"/> 45 </symbol> 46 </svg> 47 48 <div id=target>Target</div> 49 <button id=otherbutton>Other button</button> 50 51 <style> 52 [interestfor] { 53 interest-delay: 0s; 54 } 55 </style> 56 57 <script> 58 const allInterestForElements = document.querySelectorAll('[data-testcase]'); 59 assert_true(allInterestForElements.length > 0); 60 function verifyInterest(onlyElements,description) { 61 if (!(onlyElements instanceof Array)) { 62 onlyElements = [onlyElements]; 63 } 64 [...allInterestForElements, another].forEach(el => { 65 const expectInterest = onlyElements.includes(el); 66 assert_equals(el.matches(':interest-source'),expectInterest,`${description}, element ${el.dataset.testcase} should ${expectInterest ? "" : "NOT "}have interest`); 67 }) 68 } 69 function reinsert(element) { 70 const parent = element.parentElement; 71 const nextSibling = element.nextSibling; 72 element.remove(); 73 parent.insertBefore(element,nextSibling); 74 } 75 function preventEvent(shouldCancel,el,type) { 76 if (shouldCancel) { 77 assert_not_equals(type,'focusin','focusin can\'t be cancelled'); 78 assert_not_equals(type,'focusout','focusout can\'t be cancelled'); 79 el.addEventListener(type, (e) => e.preventDefault(), {once:true}); 80 } 81 } 82 83 const urlParams = new URLSearchParams(window.location.search); 84 method = urlParams.get('method'); 85 ['none','cancel-trigger','cancel-lose'].forEach(cancelEvent => { 86 allInterestForElements.forEach(el => { 87 const description = `${el.dataset.testcase}, ${cancelEvent}, ${method}`; 88 promise_test(async function (t) { 89 t.add_cleanup(() => { 90 reinsert(el); 91 reinsert(target); 92 }); 93 assert_false(el.matches(':interest-source'),'setup'); 94 assert_false(target.matches(':interest-target'),'setup'); 95 const signal = t.get_signal(); 96 let interestCount = 0; 97 let loseInterestCount = 0; 98 target.addEventListener('interest', (e) => (++interestCount), {signal}); 99 target.addEventListener('loseinterest', () => (++loseInterestCount), {signal}); 100 const cancelTrigger = cancelEvent === 'cancel-trigger'; 101 const cancelLose = cancelEvent === 'cancel-lose'; 102 assert_true(cancelTrigger || cancelLose || cancelEvent === 'none'); 103 104 switch (method) { 105 case 'hover': 106 preventEvent(cancelTrigger,el,'mouseover'); 107 hovertarget = el; 108 if (el.dataset.hover) { 109 hovertarget = document.getElementById(el.dataset.hover); 110 } 111 await hoverOver(hovertarget) 112 break; 113 case 'focus': 114 if (cancelTrigger) { 115 return; // focusin cannot be cancelled, nothing to test 116 } 117 await focusOn(el); 118 break; 119 default: 120 assert_unreached(); 121 } 122 assert_equals(loseInterestCount, 0, 'Lose interest should not be fired yet'); 123 assert_equals(interestCount, 1, 'Interest should be fired (cancelling the trigger event shouldn\'t cancel interest)'); 124 assert_true(el.matches(':interest-source'),':interest-source should match'); 125 interestCount = 0; 126 127 switch (method) { 128 case 'hover': 129 preventEvent(cancelLose,el,'mouseout'); 130 await hoverOver(otherbutton); 131 break; 132 case 'focus': 133 if (cancelLose) { 134 return; // focusout cannot be cancelled, nothing to test 135 } 136 await focusOn(otherbutton); 137 break; 138 default: 139 assert_unreached(); 140 } 141 assert_equals(interestCount, 0, 'No new interest event should be fired'); 142 assert_equals(loseInterestCount, 1, 'Lose interest event should be fired (cancelling the trigger event shouldn\'t cancel loseinterest)' ); 143 assert_false(el.matches(':interest-source'),':interest-source should not match'); 144 },`Basic behavior, ${description}`); 145 }); 146 }); 147 </script>