1(function() { 2// Test is initiated from body.onload, so explicit done() call is required. 3setup({ explicit_done: true }); 4 5function checkSubtreeExpectedValues(t, parent, prefix) 6{ 7 var checkedLayout = checkExpectedValues(t, parent, prefix); 8 Array.prototype.forEach.call(parent.childNodes, function(node) { 9 checkedLayout |= checkSubtreeExpectedValues(t, node, prefix); 10 }); 11 return checkedLayout; 12} 13 14function checkAttribute(output, node, attribute) 15{ 16 var result = node.getAttribute && node.getAttribute(attribute); 17 output.checked |= !!result; 18 return result; 19} 20 21function assert_tolerance(actual, expected, message) 22{ 23 if (isNaN(expected) || isNaN(actual) || Math.abs(actual - expected) >= 1) { 24 assert_equals(actual, Number(expected), message); 25 } 26} 27 28function checkDataKeys(node) { 29 var validData = new Set([ 30 "data-expected-width", 31 "data-expected-height", 32 "data-offset-x", 33 "data-offset-y", 34 "data-expected-client-width", 35 "data-expected-client-height", 36 "data-expected-scroll-width", 37 "data-expected-scroll-height", 38 "data-expected-bounding-client-rect-width", 39 "data-expected-bounding-client-rect-height", 40 "data-total-x", 41 "data-total-y", 42 "data-expected-display", 43 "data-expected-padding-top", 44 "data-expected-padding-bottom", 45 "data-expected-padding-left", 46 "data-expected-padding-right", 47 "data-expected-margin-top", 48 "data-expected-margin-bottom", 49 "data-expected-margin-left", 50 "data-expected-margin-right" 51 ]); 52 if (!node || !node.getAttributeNames) 53 return; 54 // Use "data-test" prefix if you need custom-named data elements. 55 for (let name of node.getAttributeNames()) { 56 if (name.startsWith("data-") && !name.startsWith("data-test")) 57 assert_true(validData.has(name), name + " is a valid data attribute"); 58 } 59} 60 61function checkExpectedValues(t, node, prefix) 62{ 63 checkDataKeys(node); 64 var output = { checked: false }; 65 66 var expectedWidth = checkAttribute(output, node, "data-expected-width"); 67 if (expectedWidth) { 68 assert_tolerance(node.offsetWidth, expectedWidth, prefix + "width"); 69 } 70 71 var expectedHeight = checkAttribute(output, node, "data-expected-height"); 72 if (expectedHeight) { 73 assert_tolerance(node.offsetHeight, expectedHeight, prefix + "height"); 74 } 75 76 var expectedOffset = checkAttribute(output, node, "data-offset-x"); 77 if (expectedOffset) { 78 assert_tolerance(node.offsetLeft, expectedOffset, prefix + "offsetLeft"); 79 } 80 81 var expectedOffset = checkAttribute(output, node, "data-offset-y"); 82 if (expectedOffset) { 83 assert_tolerance(node.offsetTop, expectedOffset, prefix + "offsetTop"); 84 } 85 86 var expectedWidth = checkAttribute(output, node, "data-expected-client-width"); 87 if (expectedWidth) { 88 assert_tolerance(node.clientWidth, expectedWidth, prefix + "clientWidth"); 89 } 90 91 var expectedHeight = checkAttribute(output, node, "data-expected-client-height"); 92 if (expectedHeight) { 93 assert_tolerance(node.clientHeight, expectedHeight, prefix + "clientHeight"); 94 } 95 96 var expectedWidth = checkAttribute(output, node, "data-expected-scroll-width"); 97 if (expectedWidth) { 98 assert_tolerance(node.scrollWidth, expectedWidth, prefix + "scrollWidth"); 99 } 100 101 var expectedHeight = checkAttribute(output, node, "data-expected-scroll-height"); 102 if (expectedHeight) { 103 assert_tolerance(node.scrollHeight, expectedHeight, prefix + "scrollHeight"); 104 } 105 106 var expectedWidth = checkAttribute(output, node, "data-expected-bounding-client-rect-width"); 107 if (expectedWidth) { 108 assert_tolerance(node.getBoundingClientRect().width, expectedWidth, prefix + "getBoundingClientRect().width"); 109 } 110 111 var expectedHeight = checkAttribute(output, node, "data-expected-bounding-client-rect-height"); 112 if (expectedHeight) { 113 assert_tolerance(node.getBoundingClientRect().height, expectedHeight, prefix + "getBoundingClientRect().height"); 114 } 115 116 var expectedOffset = checkAttribute(output, node, "data-total-x"); 117 if (expectedOffset) { 118 var totalLeft = node.clientLeft + node.offsetLeft; 119 assert_tolerance(totalLeft, expectedOffset, prefix + 120 "clientLeft+offsetLeft (" + node.clientLeft + " + " + node.offsetLeft + ")"); 121 } 122 123 var expectedOffset = checkAttribute(output, node, "data-total-y"); 124 if (expectedOffset) { 125 var totalTop = node.clientTop + node.offsetTop; 126 assert_tolerance(totalTop, expectedOffset, prefix + 127 "clientTop+offsetTop (" + node.clientTop + " + " + node.offsetTop + ")"); 128 } 129 130 var expectedDisplay = checkAttribute(output, node, "data-expected-display"); 131 if (expectedDisplay) { 132 var actualDisplay = getComputedStyle(node).display; 133 assert_equals(actualDisplay, expectedDisplay, prefix + "display"); 134 } 135 136 var expectedPaddingTop = checkAttribute(output, node, "data-expected-padding-top"); 137 if (expectedPaddingTop) { 138 var actualPaddingTop = getComputedStyle(node).paddingTop; 139 // Trim the unit "px" from the output. 140 actualPaddingTop = actualPaddingTop.slice(0, -2); 141 assert_equals(actualPaddingTop, expectedPaddingTop, prefix + "padding-top"); 142 } 143 144 var expectedPaddingBottom = checkAttribute(output, node, "data-expected-padding-bottom"); 145 if (expectedPaddingBottom) { 146 var actualPaddingBottom = getComputedStyle(node).paddingBottom; 147 // Trim the unit "px" from the output. 148 actualPaddingBottom = actualPaddingBottom.slice(0, -2); 149 assert_equals(actualPaddingBottom, expectedPaddingBottom, prefix + "padding-bottom"); 150 } 151 152 var expectedPaddingLeft = checkAttribute(output, node, "data-expected-padding-left"); 153 if (expectedPaddingLeft) { 154 var actualPaddingLeft = getComputedStyle(node).paddingLeft; 155 // Trim the unit "px" from the output. 156 actualPaddingLeft = actualPaddingLeft.slice(0, -2); 157 assert_equals(actualPaddingLeft, expectedPaddingLeft, prefix + "padding-left"); 158 } 159 160 var expectedPaddingRight = checkAttribute(output, node, "data-expected-padding-right"); 161 if (expectedPaddingRight) { 162 var actualPaddingRight = getComputedStyle(node).paddingRight; 163 // Trim the unit "px" from the output. 164 actualPaddingRight = actualPaddingRight.slice(0, -2); 165 assert_equals(actualPaddingRight, expectedPaddingRight, prefix + "padding-right"); 166 } 167 168 var expectedMarginTop = checkAttribute(output, node, "data-expected-margin-top"); 169 if (expectedMarginTop) { 170 var actualMarginTop = getComputedStyle(node).marginTop; 171 // Trim the unit "px" from the output. 172 actualMarginTop = actualMarginTop.slice(0, -2); 173 assert_equals(actualMarginTop, expectedMarginTop, prefix + "margin-top"); 174 } 175 176 var expectedMarginBottom = checkAttribute(output, node, "data-expected-margin-bottom"); 177 if (expectedMarginBottom) { 178 var actualMarginBottom = getComputedStyle(node).marginBottom; 179 // Trim the unit "px" from the output. 180 actualMarginBottom = actualMarginBottom.slice(0, -2); 181 assert_equals(actualMarginBottom, expectedMarginBottom, prefix + "margin-bottom"); 182 } 183 184 var expectedMarginLeft = checkAttribute(output, node, "data-expected-margin-left"); 185 if (expectedMarginLeft) { 186 var actualMarginLeft = getComputedStyle(node).marginLeft; 187 // Trim the unit "px" from the output. 188 actualMarginLeft = actualMarginLeft.slice(0, -2); 189 assert_equals(actualMarginLeft, expectedMarginLeft, prefix + "margin-left"); 190 } 191 192 var expectedMarginRight = checkAttribute(output, node, "data-expected-margin-right"); 193 if (expectedMarginRight) { 194 var actualMarginRight = getComputedStyle(node).marginRight; 195 // Trim the unit "px" from the output. 196 actualMarginRight = actualMarginRight.slice(0, -2); 197 assert_equals(actualMarginRight, expectedMarginRight, prefix + "margin-right"); 198 } 199 200 return output.checked; 201} 202 203var testNumber = 0; 204var highlightError = false; // displays outline around failed test element. 205var printDomOnError = true; // prints dom when test fails. 206 207window.checkLayout = function(selectorList, callDone = true) 208{ 209 if (!selectorList) { 210 console.error("You must provide a CSS selector of nodes to check."); 211 return; 212 } 213 var nodes = document.querySelectorAll(selectorList); 214 nodes = Array.prototype.slice.call(nodes); 215 var checkedLayout = false; 216 Array.prototype.forEach.call(nodes, function(node) { 217 test(function(t) { 218 var container = node.parentNode.className == 'container' ? node.parentNode : node; 219 var prefix = 220 printDomOnError ? '\n' + container.outerHTML + '\n' : ''; 221 var passed = false; 222 try { 223 checkedLayout |= checkExpectedValues(t, node.parentNode, prefix); 224 checkedLayout |= checkSubtreeExpectedValues(t, node, prefix); 225 passed = true; 226 } finally { 227 if (!passed && highlightError) { 228 if (!document.getElementById('testharness_error_css')) { 229 var style = document.createElement('style'); 230 style.id = 'testharness_error_css'; 231 style.textContent = '.testharness_error { outline: red dotted 2px !important; }'; 232 document.body.appendChild(style); 233 } 234 if (node) 235 node.classList.add('testharness_error'); 236 } 237 checkedLayout |= !passed; 238 } 239 }, selectorList + ' ' + String(++testNumber)); 240 }); 241 if (!checkedLayout) { 242 console.error("No valid data-* attributes found in selector list : " + selectorList); 243 } 244 if (callDone) 245 done(); 246}; 247 248})(); 249