1(function() { 2 3function insertAfter(nodeToAdd, referenceNode) 4{ 5 if (referenceNode == document.body) { 6 document.body.appendChild(nodeToAdd); 7 return; 8 } 9 10 if (referenceNode.nextSibling) 11 referenceNode.parentNode.insertBefore(nodeToAdd, referenceNode.nextSibling); 12 else 13 referenceNode.parentNode.appendChild(nodeToAdd); 14} 15 16function positionedAncestor(node) 17{ 18 var ancestor = node.parentNode; 19 while (getComputedStyle(ancestor).position == 'static') 20 ancestor = ancestor.parentNode; 21 return ancestor; 22} 23 24function checkSubtreeExpectedValues(parent, failures) 25{ 26 var checkedLayout = checkExpectedValues(parent, failures); 27 Array.prototype.forEach.call(parent.childNodes, function(node) { 28 checkedLayout |= checkSubtreeExpectedValues(node, failures); 29 }); 30 return checkedLayout; 31} 32 33function checkAttribute(output, node, attribute) 34{ 35 var result = node.getAttribute && node.getAttribute(attribute); 36 output.checked |= !!result; 37 return result; 38} 39 40function checkExpectedValues(node, failures) 41{ 42 var output = { checked: false }; 43 var expectedWidth = checkAttribute(output, node, "data-expected-width"); 44 if (expectedWidth) { 45 if (isNaN(expectedWidth) || Math.abs(node.offsetWidth - expectedWidth) >= 1) 46 failures.push("Expected " + expectedWidth + " for width, but got " + node.offsetWidth + ". "); 47 } 48 49 var expectedHeight = checkAttribute(output, node, "data-expected-height"); 50 if (expectedHeight) { 51 if (isNaN(expectedHeight) || Math.abs(node.offsetHeight - expectedHeight) >= 1) 52 failures.push("Expected " + expectedHeight + " for height, but got " + node.offsetHeight + ". "); 53 } 54 55 var expectedOffset = checkAttribute(output, node, "data-offset-x"); 56 if (expectedOffset) { 57 if (isNaN(expectedOffset) || Math.abs(node.offsetLeft - expectedOffset) >= 1) 58 failures.push("Expected " + expectedOffset + " for offsetLeft, but got " + node.offsetLeft + ". "); 59 } 60 61 var expectedOffset = checkAttribute(output, node, "data-offset-y"); 62 if (expectedOffset) { 63 if (isNaN(expectedOffset) || Math.abs(node.offsetTop - expectedOffset) >= 1) 64 failures.push("Expected " + expectedOffset + " for offsetTop, but got " + node.offsetTop + ". "); 65 } 66 67 var expectedOffset = checkAttribute(output, node, "data-positioned-offset-x"); 68 if (expectedOffset) { 69 var actualOffset = node.getBoundingClientRect().left - positionedAncestor(node).getBoundingClientRect().left; 70 if (isNaN(expectedOffset) || Math.abs(actualOffset - expectedOffset) >= 1) 71 failures.push("Expected " + expectedOffset + " for getBoundingClientRect().left offset, but got " + actualOffset + ". "); 72 } 73 74 var expectedOffset = checkAttribute(output, node, "data-positioned-offset-y"); 75 if (expectedOffset) { 76 var actualOffset = node.getBoundingClientRect().top - positionedAncestor(node).getBoundingClientRect().top; 77 if (isNaN(expectedOffset) || Math.abs(actualOffset - expectedOffset) >= 1) 78 failures.push("Expected " + expectedOffset + " for getBoundingClientRect().top offset, but got " + actualOffset + ". "); 79 } 80 81 var expectedWidth = checkAttribute(output, node, "data-expected-client-width"); 82 if (expectedWidth) { 83 if (isNaN(expectedWidth) || Math.abs(node.clientWidth - expectedWidth) >= 1) 84 failures.push("Expected " + expectedWidth + " for clientWidth, but got " + node.clientWidth + ". "); 85 } 86 87 var expectedHeight = checkAttribute(output, node, "data-expected-client-height"); 88 if (expectedHeight) { 89 if (isNaN(expectedHeight) || Math.abs(node.clientHeight - expectedHeight) >= 1) 90 failures.push("Expected " + expectedHeight + " for clientHeight, but got " + node.clientHeight + ". "); 91 } 92 93 var expectedWidth = checkAttribute(output, node, "data-expected-scroll-width"); 94 if (expectedWidth) { 95 if (isNaN(expectedWidth) || Math.abs(node.scrollWidth - expectedWidth) >= 1) 96 failures.push("Expected " + expectedWidth + " for scrollWidth, but got " + node.scrollWidth + ". "); 97 } 98 99 var expectedHeight = checkAttribute(output, node, "data-expected-scroll-height"); 100 if (expectedHeight) { 101 if (isNaN(expectedHeight) || Math.abs(node.scrollHeight - expectedHeight) >= 1) 102 failures.push("Expected " + expectedHeight + " for scrollHeight, but got " + node.scrollHeight + ". "); 103 } 104 105 var expectedOffset = checkAttribute(output, node, "data-total-x"); 106 if (expectedOffset) { 107 var totalLeft = node.clientLeft + node.offsetLeft; 108 if (isNaN(expectedOffset) || Math.abs(totalLeft - expectedOffset) >= 1) 109 failures.push("Expected " + expectedOffset + " for clientLeft+offsetLeft, but got " + totalLeft + ", clientLeft: " + node.clientLeft + ", offsetLeft: " + node.offsetLeft + ". "); 110 } 111 112 var expectedOffset = checkAttribute(output, node, "data-total-y"); 113 if (expectedOffset) { 114 var totalTop = node.clientTop + node.offsetTop; 115 if (isNaN(expectedOffset) || Math.abs(totalTop - expectedOffset) >= 1) 116 failures.push("Expected " + expectedOffset + " for clientTop+offsetTop, but got " + totalTop + ", clientTop: " + node.clientTop + ", + offsetTop: " + node.offsetTop + ". "); 117 } 118 119 var expectedDisplay = checkAttribute(output, node, "data-expected-display"); 120 if (expectedDisplay) { 121 var actualDisplay = getComputedStyle(node).display; 122 if (actualDisplay != expectedDisplay) 123 failures.push("Expected " + expectedDisplay + " for display, but got " + actualDisplay + ". "); 124 } 125 126 var expectedPaddingTop = checkAttribute(output, node, "data-expected-padding-top"); 127 if (expectedPaddingTop) { 128 var actualPaddingTop = getComputedStyle(node).paddingTop; 129 // Trim the unit "px" from the output. 130 actualPaddingTop = actualPaddingTop.substring(0, actualPaddingTop.length - 2); 131 if (actualPaddingTop != expectedPaddingTop) 132 failures.push("Expected " + expectedPaddingTop + " for padding-top, but got " + actualPaddingTop + ". "); 133 } 134 135 var expectedPaddingBottom = checkAttribute(output, node, "data-expected-padding-bottom"); 136 if (expectedPaddingBottom) { 137 var actualPaddingBottom = getComputedStyle(node).paddingBottom; 138 // Trim the unit "px" from the output. 139 actualPaddingBottom = actualPaddingBottom.substring(0, actualPaddingBottom.length - 2); 140 if (actualPaddingBottom != expectedPaddingBottom) 141 failures.push("Expected " + expectedPaddingBottom + " for padding-bottom, but got " + actualPaddingBottom + ". "); 142 } 143 144 var expectedPaddingLeft = checkAttribute(output, node, "data-expected-padding-left"); 145 if (expectedPaddingLeft) { 146 var actualPaddingLeft = getComputedStyle(node).paddingLeft; 147 // Trim the unit "px" from the output. 148 actualPaddingLeft = actualPaddingLeft.substring(0, actualPaddingLeft.length - 2); 149 if (actualPaddingLeft != expectedPaddingLeft) 150 failures.push("Expected " + expectedPaddingLeft + " for padding-left, but got " + actualPaddingLeft + ". "); 151 } 152 153 var expectedPaddingRight = checkAttribute(output, node, "data-expected-padding-right"); 154 if (expectedPaddingRight) { 155 var actualPaddingRight = getComputedStyle(node).paddingRight; 156 // Trim the unit "px" from the output. 157 actualPaddingRight = actualPaddingRight.substring(0, actualPaddingRight.length - 2); 158 if (actualPaddingRight != expectedPaddingRight) 159 failures.push("Expected " + expectedPaddingRight + " for padding-right, but got " + actualPaddingRight + ". "); 160 } 161 162 var expectedMarginTop = checkAttribute(output, node, "data-expected-margin-top"); 163 if (expectedMarginTop) { 164 var actualMarginTop = getComputedStyle(node).marginTop; 165 // Trim the unit "px" from the output. 166 actualMarginTop = actualMarginTop.substring(0, actualMarginTop.length - 2); 167 if (actualMarginTop != expectedMarginTop) 168 failures.push("Expected " + expectedMarginTop + " for margin-top, but got " + actualMarginTop + ". "); 169 } 170 171 var expectedMarginBottom = checkAttribute(output, node, "data-expected-margin-bottom"); 172 if (expectedMarginBottom) { 173 var actualMarginBottom = getComputedStyle(node).marginBottom; 174 // Trim the unit "px" from the output. 175 actualMarginBottom = actualMarginBottom.substring(0, actualMarginBottom.length - 2); 176 if (actualMarginBottom != expectedMarginBottom) 177 failures.push("Expected " + expectedMarginBottom + " for margin-bottom, but got " + actualMarginBottom + ". "); 178 } 179 180 var expectedMarginLeft = checkAttribute(output, node, "data-expected-margin-left"); 181 if (expectedMarginLeft) { 182 var actualMarginLeft = getComputedStyle(node).marginLeft; 183 // Trim the unit "px" from the output. 184 actualMarginLeft = actualMarginLeft.substring(0, actualMarginLeft.length - 2); 185 if (actualMarginLeft != expectedMarginLeft) 186 failures.push("Expected " + expectedMarginLeft + " for margin-left, but got " + actualMarginLeft + ". "); 187 } 188 189 var expectedMarginRight = checkAttribute(output, node, "data-expected-margin-right"); 190 if (expectedMarginRight) { 191 var actualMarginRight = getComputedStyle(node).marginRight; 192 // Trim the unit "px" from the output. 193 actualMarginRight = actualMarginRight.substring(0, actualMarginRight.length - 2); 194 if (actualMarginRight != expectedMarginRight) 195 failures.push("Expected " + expectedMarginRight + " for margin-right, but got " + actualMarginRight + ". "); 196 } 197 198 return output.checked; 199} 200 201window.checkLayout = function(selectorList, outputContainer) 202{ 203 var result = true; 204 if (!selectorList) { 205 document.body.appendChild(document.createTextNode("You must provide a CSS selector of nodes to check.")); 206 return; 207 } 208 var nodes = document.querySelectorAll(selectorList); 209 nodes = Array.prototype.slice.call(nodes); 210 nodes.reverse(); 211 var checkedLayout = false; 212 Array.prototype.forEach.call(nodes, function(node) { 213 var failures = []; 214 checkedLayout |= checkExpectedValues(node.parentNode, failures); 215 checkedLayout |= checkSubtreeExpectedValues(node, failures); 216 217 var container = node.parentNode.className == 'container' ? node.parentNode : node; 218 219 var pre = document.createElement('pre'); 220 if (failures.length) { 221 pre.className = 'FAIL'; 222 result = false; 223 } 224 pre.appendChild(document.createTextNode(failures.length ? "FAIL:\n" + failures.join('\n') + '\n\n' + container.outerHTML : "PASS")); 225 226 var referenceNode = container; 227 if (outputContainer) { 228 if (!outputContainer.lastChild) { 229 // Inserting a text node so we have something to insertAfter. 230 outputContainer.textContent = " "; 231 } 232 referenceNode = outputContainer.lastChild; 233 } 234 insertAfter(pre, referenceNode); 235 }); 236 237 if (!checkedLayout) { 238 document.body.appendChild(document.createTextNode("FAIL: No valid data-* attributes found in selector list : " + selectorList)); 239 return false; 240 } 241 242 return result; 243} 244 245})(); 246