From 3ebd505d6d637a2e8e919d4b7187c1428b1486e2 Mon Sep 17 00:00:00 2001 From: jkodu Date: Wed, 20 Mar 2019 15:54:08 +0000 Subject: [PATCH 01/12] refactor: get backgground color function --- .../color/element-has-background-image.js | 39 ++ lib/commons/color/get-background-color.js | 548 ++++++++---------- .../get-non-alpha-blended-background-color.js | 27 + lib/core/utils/get-coordinates-from-rect.js | 28 + 4 files changed, 341 insertions(+), 301 deletions(-) create mode 100644 lib/commons/color/element-has-background-image.js create mode 100644 lib/commons/color/get-non-alpha-blended-background-color.js create mode 100644 lib/core/utils/get-coordinates-from-rect.js diff --git a/lib/commons/color/element-has-background-image.js b/lib/commons/color/element-has-background-image.js new file mode 100644 index 0000000000..03ef715941 --- /dev/null +++ b/lib/commons/color/element-has-background-image.js @@ -0,0 +1,39 @@ +/* global color */ + +/** + * Reports if an element has a background image or gradient + * + * @method elementHasBackgroundImage + * @memberof axe.commons.color + * @private + * @param {Element} elm + * @param {Object|null} style + * @return {Boolean} + */ +color.elementHasBackgroundImage = function elementHasBackgroundImage( + elm, + style +) { + const graphicNodes = ['IMG', 'CANVAS', 'OBJECT', 'IFRAME', 'VIDEO', 'SVG']; + const nodeName = elm.nodeName.toUpperCase(); + + if (graphicNodes.includes(nodeName)) { + axe.commons.color.incompleteData.set('bgColor', 'imgNode'); + return true; + } + + style = style || window.getComputedStyle(elm); + + const bgImageStyle = style.getPropertyValue('background-image'); + const hasBgImage = bgImageStyle !== 'none'; + + if (hasBgImage) { + var hasGradient = /gradient/.test(bgImageStyle); + axe.commons.color.incompleteData.set( + 'bgColor', + hasGradient ? 'bgGradient' : 'bgImage' + ); + } + + return hasBgImage; +}; diff --git a/lib/commons/color/get-background-color.js b/lib/commons/color/get-background-color.js index 8cffcf4be6..6b50f5fcbe 100644 --- a/lib/commons/color/get-background-color.js +++ b/lib/commons/color/get-background-color.js @@ -1,121 +1,229 @@ /* global axe, color, dom */ -const graphicNodes = ['IMG', 'CANVAS', 'OBJECT', 'IFRAME', 'VIDEO', 'SVG']; /** - * Reports if an element has a background image or gradient - * @private - * @param {Element} elm - * @param {Object|null} style - * @return {Boolean} + * Returns background color for element + * Uses getBackgroundStack() to get all elements rendered underneath the current element, + * to help determine the composite background color. + * + * @method getBackgroundColor + * @memberof axe.commons.color + * @param {Element} elm Element to determine background color + * @param {Array} [bgElms=[]] elements to inspect + * @param {Boolean} [noScroll=false] should scroll + * @returns {Color} */ -function elmHasImage(elm, style) { - var nodeName = elm.nodeName.toUpperCase(); - if (graphicNodes.includes(nodeName)) { - axe.commons.color.incompleteData.set('bgColor', 'imgNode'); - return true; +color.getBackgroundColor = function getBackgroundColor( + elm, + bgElms = [], + noScroll = false +) { + if (noScroll !== true) { + /** + * Avoid scrolling overflow:hidden containers, by only aligning to top, + * when not doing so would move the center point above the viewport top. + */ + const clientHeight = elm.getBoundingClientRect().height; + const alignToTop = clientHeight - 2 >= window.innerHeight * 2; + elm.scrollIntoView(alignToTop); } - style = style || window.getComputedStyle(elm); - var bgImageStyle = style.getPropertyValue('background-image'); - var hasBgImage = bgImageStyle !== 'none'; - if (hasBgImage) { - var hasGradient = /gradient/.test(bgImageStyle); - axe.commons.color.incompleteData.set( - 'bgColor', - hasGradient ? 'bgGradient' : 'bgImage' - ); + let bgColors = []; + let elmStack = getBackgroundStack(elm); + + // Search the stack until we have an alpha === 1 background + (elmStack || []).some(bgElm => { + let bgElmStyle = window.getComputedStyle(bgElm); + + // Get the background color + let bgColor = color.getNonAlphaBlendedBackgroundColor(bgElm, bgElmStyle); + + if ( + // abort if a node is partially obscured and obscuring element has a background + elmPartiallyObscured(elm, bgElm, bgColor) || + // OR if the background elm is a graphic + color.elementHasBackgroundImage(bgElm, bgElmStyle) + ) { + bgColors = null; + bgElms.push(bgElm); + + return true; + } + + if (bgColor.alpha !== 0) { + // store elements contributing to the br color. + bgElms.push(bgElm); + bgColors.push(bgColor); + + // Exit if the background is opaque + return bgColor.alpha === 1; + } else { + return false; + } + }); + + if (bgColors !== null && elmStack !== null) { + // Mix the colors together, on top of a default white + bgColors.push(new color.Color(255, 255, 255, 1)); + var colors = bgColors.reduce(color.flattenColors); + return colors; } - return hasBgImage; -} + + return null; +}; /** - * Returns the non-alpha-blended background color of an element + * Get all elements rendered underneath the current element, + * In the order they are displayed (front to back) + * + * @method getBackgroundStack * @private * @param {Element} elm - * @return {Color} + * @return {Array} */ -function getBgColor(elm, elmStyle) { - elmStyle = elmStyle || window.getComputedStyle(elm); +function getBackgroundStack(elm) { + let elmStack = filteredRectStack(elm); - let bgColor = new color.Color(); - bgColor.parseRgbString(elmStyle.getPropertyValue('background-color')); + if (elmStack === null) { + return null; + } + elmStack = includeMissingElements(elmStack, elm); + elmStack = dom.reduceToElementsBelowFloating(elmStack, elm); + elmStack = sortPageBackground(elmStack); - if (bgColor.alpha !== 0) { - let opacity = elmStyle.getPropertyValue('opacity'); - bgColor.alpha = bgColor.alpha * opacity; + // Return all elements BELOW the current element, null if the element is undefined + let elmIndex = elmStack.indexOf(elm); + if (calculateObscuringAlpha(elmIndex, elmStack, elm) >= 0.99) { + // if the total of the elements above our element results in total obscuring, return null + axe.commons.color.incompleteData.set('bgColor', 'bgOverlap'); + return null; } - return bgColor; + return elmIndex !== -1 ? elmStack : null; } /** - * Determines overlap of node's content with a bgNode. Used for inline elements + * Get filtered stack of block and inline elements, excluding line breaks + * @method filteredRectStack * @private - * @param {Element} targetElement - * @param {Element} bgNode - * @return {Boolean} + * @param {Element} elm + * @return {Array} */ -function contentOverlapping(targetElement, bgNode) { - // get content box of target element - // check to see if the current bgNode is overlapping - var targetRect = targetElement.getClientRects()[0]; - var obscuringElements = dom.shadowElementsFromPoint( - targetRect.left, - targetRect.top - ); - if (obscuringElements) { - for (var i = 0; i < obscuringElements.length; i++) { - if ( - obscuringElements[i] !== targetElement && - obscuringElements[i] === bgNode - ) { - return true; +function filteredRectStack(elm) { + const rectStack = getRectStack(elm); + + if (rectStack && rectStack.length === 1) { + return rectStack[0]; + } + + if (rectStack && rectStack.length > 1) { + const boundingStack = rectStack.shift(); + let isSame; + + // iterating over arrays of DOMRects + rectStack.forEach((rectList, index) => { + if (index === 0) { + return; } + // if the stacks are the same, use the first one. otherwise, return null. + let rectA = rectStack[index - 1], + rectB = rectStack[index]; + + // if elements in clientRects are the same + // or the boundingClientRect contains the differing element, pass it + isSame = + rectA.every( + (element, elementIndex) => element === rectB[elementIndex] + ) || boundingStack.includes(elm); + }); + if (!isSame) { + axe.commons.color.incompleteData.set('bgColor', 'elmPartiallyObscuring'); + return null; } + // pass the first stack if it wasn't partially covered + return rectStack[0]; } - return false; + + // rect outside of viewport + axe.commons.color.incompleteData.set('bgColor', 'outsideViewport'); + return null; } + /** - * Calculate alpha transparency of a background element obscuring the current node + * Get relevant stacks of block and inline elements, excluding line breaks + * @method getRectStack * @private - * @param {Number} elmIndex - * @param {Array} elmStack - * @param {Element} originalElm - * @return {Number|undefined} + * @param {Element} elm + * @return {Array} */ -function calculateObscuringAlpha(elmIndex, elmStack, originalElm) { - var totalAlpha = 0; +function getRectStack(elm) { + const boundingCoords = axe.utils.getCoordinatesFromRect( + elm.getBoundingClientRect() + ); - if (elmIndex > 0) { - // there are elements above our element, check if they contribute to the background - for (var i = elmIndex - 1; i >= 0; i--) { - let bgElm = elmStack[i]; - let bgElmStyle = window.getComputedStyle(bgElm); - let bgColor = getBgColor(bgElm, bgElmStyle); - if (bgColor.alpha && contentOverlapping(originalElm, bgElm)) { - totalAlpha += bgColor.alpha; - } else { - // remove elements not contributing to the background - elmStack.splice(i, 1); + if (!boundingCoords) { + return null; + } + + let boundingStack = dom.shadowElementsFromPoint( + boundingCoords.x, + boundingCoords.y + ); + + let rects = Array.from(elm.getClientRects()); + // If the element does not have multiple rects, like for display:block, return a single stack + if (!rects || rects.length <= 1) { + return [boundingStack]; + } + + // Handle inline elements spanning multiple lines to be evaluated + let filteredArr = rects + .filter(rect => { + // exclude manual line breaks in Chrome/Safari + return rect.width && rect.width > 0; + }) + .map(rect => { + const coords = axe.utils.getCoordinatesFromRect(rect); + if (coords) { + return dom.shadowElementsFromPoint(coords.x, coords.y); } - } + }); + + if (filteredArr.some(stack => stack === undefined)) { + // Can be happen when one or more of the rects sits outside the viewport + return null; } - return totalAlpha; + + // add bounding client rect stack for comparison later + filteredArr.splice(0, 0, boundingStack); + return filteredArr; } + /** - * Determine if element is partially overlapped, triggering a Can't Tell result + * Look at document and body elements for relevant background information + * @method sortPageBackground * @private - * @param {Element} elm - * @param {Element} bgElm - * @param {Object} bgColor - * @return {Boolean} + * @param {Array} elmStack + * @returns {Array} */ -function elmPartiallyObscured(elm, bgElm, bgColor) { - var obscured = - elm !== bgElm && !dom.visuallyContains(elm, bgElm) && bgColor.alpha !== 0; - if (obscured) { - axe.commons.color.incompleteData.set('bgColor', 'elmPartiallyObscured'); +function sortPageBackground(elmStack) { + let bodyIndex = elmStack.indexOf(document.body); + + let bgNodes = elmStack; + + if ( + // Check that the body background is the page's background + bodyIndex > 1 && // only if there are negative z-index elements + !color.elementHasBackgroundImage(document.documentElement) && + color.getNonAlphaBlendedBackgroundColor(document.documentElement).alpha === + 0 + ) { + // Remove body and html from it's current place + bgNodes.splice(bodyIndex, 1); + bgNodes.splice(elmStack.indexOf(document.documentElement), 1); + + // Put the body background as the lowest element + bgNodes.push(document.body); } - return obscured; + return bgNodes; } /** @@ -123,10 +231,10 @@ function elmPartiallyObscured(elm, bgElm, bgColor) { * document.elementsFromPoint misses some elements we need * i.e. TR is missing from table elementStack and leaves out bgColor * https://github.com/dequelabs/axe-core/issues/273 - * * @private * @param {Array} elmStack * @param {Element} elm + * @returns {Array} */ function includeMissingElements(elmStack, elm) { /*eslint max-depth:["error",7]*/ @@ -176,236 +284,74 @@ function includeMissingElements(elmStack, elm) { } /** - * Look at document and body elements for relevant background information + * Determine if element is partially overlapped, triggering a Can't Tell result * @private - * @param {Array} elmStack + * @param {Element} elm + * @param {Element} bgElm + * @param {Object} bgColor + * @return {Boolean} */ -function sortPageBackground(elmStack) { - let bodyIndex = elmStack.indexOf(document.body); - - let bgNodes = elmStack; - - if ( - // Check that the body background is the page's background - bodyIndex > 1 && // only if there are negative z-index elements - !elmHasImage(document.documentElement) && - getBgColor(document.documentElement).alpha === 0 - ) { - // Remove body and html from it's current place - bgNodes.splice(bodyIndex, 1); - bgNodes.splice(elmStack.indexOf(document.documentElement), 1); - - // Put the body background as the lowest element - bgNodes.push(document.body); +function elmPartiallyObscured(elm, bgElm, bgColor) { + var obscured = + elm !== bgElm && !dom.visuallyContains(elm, bgElm) && bgColor.alpha !== 0; + if (obscured) { + axe.commons.color.incompleteData.set('bgColor', 'elmPartiallyObscured'); } - return bgNodes; + return obscured; } -/** - * Get coordinates for an element's client rects or bounding client rect - * @method getCoords - * @memberof axe.commons.color - * @instance - * @param {DOMRect} rect - * @return {Object} - */ -color.getCoords = function(rect) { - let x, y; - if (rect.left > window.innerWidth) { - return; - } - if (rect.top > window.innerHeight) { - return; - } - x = Math.min(Math.ceil(rect.left + rect.width / 2), window.innerWidth - 1); - y = Math.min(Math.ceil(rect.top + rect.height / 2), window.innerHeight - 1); - return { x, y }; -}; /** - * Get relevant stacks of block and inline elements, excluding line breaks - * @method getRectStack - * @memberof axe.commons.color - * @instance - * @param {Element} elm - * @return {Array} + * Calculate alpha transparency of a background element obscuring the current node + * @private + * @param {Number} elmIndex + * @param {Array} elmStack + * @param {Element} originalElm + * @return {Number|undefined} */ -color.getRectStack = function(elm) { - let boundingCoords = color.getCoords(elm.getBoundingClientRect()); - if (!boundingCoords) { - return null; - } - - let boundingStack = dom.shadowElementsFromPoint( - boundingCoords.x, - boundingCoords.y - ); - - let rects = Array.from(elm.getClientRects()); - // If the element does not have multiple rects, like for display:block, return a single stack - if (!rects || rects.length <= 1) { - return [boundingStack]; - } - - // Handle inline elements spanning multiple lines to be evaluated - let filteredArr = rects - .filter(rect => { - // exclude manual line breaks in Chrome/Safari - return rect.width && rect.width > 0; - }) - .map(rect => { - let coords = color.getCoords(rect); - if (coords) { - return dom.shadowElementsFromPoint(coords.x, coords.y); - } - }); - - if (filteredArr.some(stack => stack === undefined)) { - // Can be happen when one or more of the rects sits outside the viewport - return null; - } +function calculateObscuringAlpha(elmIndex, elmStack, originalElm) { + var totalAlpha = 0; - // add bounding client rect stack for comparison later - filteredArr.splice(0, 0, boundingStack); - return filteredArr; -}; -/** - * Get filtered stack of block and inline elements, excluding line breaks - * @method filteredRectStack - * @memberof axe.commons.color - * @instance - * @param {Element} elm - * @return {Array} - */ -color.filteredRectStack = function(elm) { - let rectStack = color.getRectStack(elm); - if (rectStack && rectStack.length === 1) { - // default case, elm.getBoundingClientRect() - return rectStack[0]; - } else if (rectStack && rectStack.length > 1) { - let boundingStack = rectStack.shift(); - let isSame; - // iterating over arrays of DOMRects - rectStack.forEach((rectList, index) => { - if (index === 0) { - return; + if (elmIndex > 0) { + // there are elements above our element, check if they contribute to the background + for (var i = elmIndex - 1; i >= 0; i--) { + let bgElm = elmStack[i]; + let bgElmStyle = window.getComputedStyle(bgElm); + let bgColor = color.getNonAlphaBlendedBackgroundColor(bgElm, bgElmStyle); + if (bgColor.alpha && contentOverlapping(originalElm, bgElm)) { + totalAlpha += bgColor.alpha; + } else { + // remove elements not contributing to the background + elmStack.splice(i, 1); } - // if the stacks are the same, use the first one. otherwise, return null. - let rectA = rectStack[index - 1], - rectB = rectStack[index]; - - // if elements in clientRects are the same - // or the boundingClientRect contains the differing element, pass it - isSame = - rectA.every(function(element, elementIndex) { - return element === rectB[elementIndex]; - }) || boundingStack.includes(elm); - }); - if (!isSame) { - axe.commons.color.incompleteData.set('bgColor', 'elmPartiallyObscuring'); - return null; } - // pass the first stack if it wasn't partially covered - return rectStack[0]; - } else { - // rect outside of viewport - axe.commons.color.incompleteData.set('bgColor', 'outsideViewport'); - return null; } -}; -/** - * Get all elements rendered underneath the current element, In the order they are displayed (front to back) - * @method getBackgroundStack - * @memberof axe.commons.color - * @instance - * @param {Element} elm - * @return {Array} - */ -color.getBackgroundStack = function(elm) { - let elmStack = color.filteredRectStack(elm); - if (elmStack === null) { - return null; - } - elmStack = includeMissingElements(elmStack, elm); - elmStack = dom.reduceToElementsBelowFloating(elmStack, elm); - elmStack = sortPageBackground(elmStack); - - // Return all elements BELOW the current element, null if the element is undefined - let elmIndex = elmStack.indexOf(elm); - if (calculateObscuringAlpha(elmIndex, elmStack, elm) >= 0.99) { - // if the total of the elements above our element results in total obscuring, return null - axe.commons.color.incompleteData.set('bgColor', 'bgOverlap'); - return null; - } - return elmIndex !== -1 ? elmStack : null; -}; + return totalAlpha; +} /** - * Returns background color for element - * Uses color.getBackgroundStack() to get all elements rendered underneath the current element to - * help determine the background color. - * @param {Element} elm Element to determine background color - * @param {Array} [bgElms=[]] [description] - * @param {Boolean} [noScroll=false] [description] - * @return {Color} [description] + * Determines overlap of node's content with a bgNode. Used for inline elements + * @private + * @param {Element} targetElement + * @param {Element} bgNode + * @return {Boolean} */ -color.getBackgroundColor = function(elm, bgElms = [], noScroll = false) { - if (noScroll !== true) { - // Avoid scrolling overflow:hidden containers, by only aligning to top - // when not doing so would move the center point above the viewport top. - const clientHeight = elm.getBoundingClientRect().height; - const alignToTop = clientHeight - 2 >= window.innerHeight * 2; - elm.scrollIntoView(alignToTop); - } - let bgColors = []; - let elmStack = color.getBackgroundStack(elm); - - // Search the stack until we have an alpha === 1 background - (elmStack || []).some(bgElm => { - let bgElmStyle = window.getComputedStyle(bgElm); - - // Get the background color - let bgColor = getBgColor(bgElm, bgElmStyle); - - if ( - // abort if a node is partially obscured and obscuring element has a background - elmPartiallyObscured(elm, bgElm, bgColor) || - // OR if the background elm is a graphic - elmHasImage(bgElm, bgElmStyle) - ) { - bgColors = null; - bgElms.push(bgElm); - - return true; - } - - if (bgColor.alpha !== 0) { - // store elements contributing to the br color. - bgElms.push(bgElm); - bgColors.push(bgColor); - - // Exit if the background is opaque - return bgColor.alpha === 1; - } else { - return false; +function contentOverlapping(targetElement, bgNode) { + // get content box of target element + // check to see if the current bgNode is overlapping + var targetRect = targetElement.getClientRects()[0]; + var obscuringElements = dom.shadowElementsFromPoint( + targetRect.left, + targetRect.top + ); + if (obscuringElements) { + for (var i = 0; i < obscuringElements.length; i++) { + if ( + obscuringElements[i] !== targetElement && + obscuringElements[i] === bgNode + ) { + return true; + } } - }); - - if (bgColors !== null && elmStack !== null) { - // Mix the colors together, on top of a default white - bgColors.push(new color.Color(255, 255, 255, 1)); - var colors = bgColors.reduce(color.flattenColors); - return colors; } - - return null; -}; - -/** - * Determines whether an element has a fully opaque background, whether solid color or an image - * @param {Element} node - * @return {Boolean} false if the background is transparent, true otherwise - */ -dom.isOpaque = function(node) { - let style = window.getComputedStyle(node); - return elmHasImage(node, style) || getBgColor(node, style).alpha === 1; -}; + return false; +} diff --git a/lib/commons/color/get-non-alpha-blended-background-color.js b/lib/commons/color/get-non-alpha-blended-background-color.js new file mode 100644 index 0000000000..43b06ebe0d --- /dev/null +++ b/lib/commons/color/get-non-alpha-blended-background-color.js @@ -0,0 +1,27 @@ +/* global color */ + +/** + * Returns the non-alpha-blended background color of an element + * + * @method getNonAlphaBlendedBackgroundColor + * @memberof axe.commons.color + * + * @param {Element} elm + * @return {Color} + */ +color.getNonAlphaBlendedBackgroundColor = function getNonAlphaBlendedBackgroundColor( + elm, + elmStyle +) { + elmStyle = elmStyle || window.getComputedStyle(elm); + + let bgColor = new color.Color(); + bgColor.parseRgbString(elmStyle.getPropertyValue('background-color')); + + if (bgColor.alpha !== 0) { + let opacity = elmStyle.getPropertyValue('opacity'); + bgColor.alpha = bgColor.alpha * opacity; + } + + return bgColor; +}; diff --git a/lib/core/utils/get-coordinates-from-rect.js b/lib/core/utils/get-coordinates-from-rect.js new file mode 100644 index 0000000000..614bd6b25f --- /dev/null +++ b/lib/core/utils/get-coordinates-from-rect.js @@ -0,0 +1,28 @@ +/** + * Get coordinates for an element's client rects or bounding client rect + * + * @method getCoordinatesFromRect + * @memberof axe.utils + * + * @param {DOMRect} rect + * @return {Object | undefined} + */ +axe.utils.getCoordinatesFromRect = function getCoordinatesFromRect(rect) { + if (rect.left > window.innerWidth) { + return; + } + if (rect.top > window.innerHeight) { + return; + } + + const x = Math.min( + Math.ceil(rect.left + rect.width / 2), + window.innerWidth - 1 + ); + const y = Math.min( + Math.ceil(rect.top + rect.height / 2), + window.innerHeight - 1 + ); + + return { x, y }; +}; From a9ae29cb9eb18085100085de6317a948d28c38f3 Mon Sep 17 00:00:00 2001 From: jkodu Date: Thu, 4 Apr 2019 16:15:58 +0100 Subject: [PATCH 02/12] test: getCoordinatesFromRect --- lib/core/utils/get-coordinates-from-rect.js | 9 +-- test/core/utils/get-coordinates-from-rect.js | 66 ++++++++++++++++++++ 2 files changed, 71 insertions(+), 4 deletions(-) create mode 100644 test/core/utils/get-coordinates-from-rect.js diff --git a/lib/core/utils/get-coordinates-from-rect.js b/lib/core/utils/get-coordinates-from-rect.js index 614bd6b25f..85f855fa7f 100644 --- a/lib/core/utils/get-coordinates-from-rect.js +++ b/lib/core/utils/get-coordinates-from-rect.js @@ -8,11 +8,12 @@ * @return {Object | undefined} */ axe.utils.getCoordinatesFromRect = function getCoordinatesFromRect(rect) { - if (rect.left > window.innerWidth) { - return; + if (rect.left < 0 || rect.left > window.innerWidth) { + return undefined; } - if (rect.top > window.innerHeight) { - return; + + if (rect.top < 0 || rect.top > window.innerHeight) { + return undefined; } const x = Math.min( diff --git a/test/core/utils/get-coordinates-from-rect.js b/test/core/utils/get-coordinates-from-rect.js new file mode 100644 index 0000000000..5fbf0deceb --- /dev/null +++ b/test/core/utils/get-coordinates-from-rect.js @@ -0,0 +1,66 @@ +describe('axe.utils.getCoordinatesFromRect', function() { + 'use strict'; + + var isPhantom = window.PHANTOMJS ? true : false; + var fixture = document.getElementById('fixture'); + var queryFixture = axe.testUtils.queryFixture; + + afterEach(function() { + fixture.innerHTML = ''; + }); + + it('returns `undefined` when element is placed outside of viewport (left position > window dimension)', function() { + var vNode = queryFixture( + '' + ); + var node = vNode.actualNode; + var rect = node.getBoundingClientRect(); + var actual = axe.utils.getCoordinatesFromRect(rect); + + assert.isUndefined(actual); + }); + + it('returns `undefined` when element is placed outside of viewport (top position is negative)', function() { + var vNode = queryFixture( + '' + ); + var node = vNode.actualNode; + var rect = node.getBoundingClientRect(); + var actual = axe.utils.getCoordinatesFromRect(rect); + + assert.isUndefined(actual); + }); + + /** + * Note: + * Believe in PhantomJs run, the button which is relatively positioned, + * ends up out of bounds of viewport, and thus returns `undefined`. + */ + isPhantom + ? it.skip + : it('returns `{x,y}` when element is with in viewport', function() { + var vNode = queryFixture( + '' + ); + var node = vNode.actualNode; + var rect = node.getBoundingClientRect(); + var actual = axe.utils.getCoordinatesFromRect(rect); + + assert.isDefined(actual); + assert.hasAllKeys(actual, ['x', 'y']); + }); + + it('returns `{x,y}` when element is with in viewport (check returned coordinate values)', function() { + var vNode = queryFixture( + '' + ); + var node = vNode.actualNode; + var rect = node.getBoundingClientRect(); + var actual = axe.utils.getCoordinatesFromRect(rect); + + assert.isDefined(actual); + assert.hasAllKeys(actual, ['x', 'y']); + assert.equal(actual.x, 225); + assert.equal(actual.y, 225); + }); +}); From 1fe5a34157fc082915f738fc27e892e700515561 Mon Sep 17 00:00:00 2001 From: jkodu Date: Thu, 4 Apr 2019 20:06:38 +0100 Subject: [PATCH 03/12] test: add tests for common color utility fns --- .../color/element-has-background-image.js | 2 +- .../get-non-alpha-blended-background-color.js | 2 +- .../color/element-has-background-image.js | 62 +++++++++++++++++ .../get-non-alpha-blended-background-color.js | 69 +++++++++++++++++++ 4 files changed, 133 insertions(+), 2 deletions(-) create mode 100644 test/commons/color/element-has-background-image.js create mode 100644 test/commons/color/get-non-alpha-blended-background-color.js diff --git a/lib/commons/color/element-has-background-image.js b/lib/commons/color/element-has-background-image.js index 03ef715941..9e48467b9e 100644 --- a/lib/commons/color/element-has-background-image.js +++ b/lib/commons/color/element-has-background-image.js @@ -28,7 +28,7 @@ color.elementHasBackgroundImage = function elementHasBackgroundImage( const hasBgImage = bgImageStyle !== 'none'; if (hasBgImage) { - var hasGradient = /gradient/.test(bgImageStyle); + const hasGradient = /gradient/.test(bgImageStyle); axe.commons.color.incompleteData.set( 'bgColor', hasGradient ? 'bgGradient' : 'bgImage' diff --git a/lib/commons/color/get-non-alpha-blended-background-color.js b/lib/commons/color/get-non-alpha-blended-background-color.js index 43b06ebe0d..d09fd61aa9 100644 --- a/lib/commons/color/get-non-alpha-blended-background-color.js +++ b/lib/commons/color/get-non-alpha-blended-background-color.js @@ -19,7 +19,7 @@ color.getNonAlphaBlendedBackgroundColor = function getNonAlphaBlendedBackgroundC bgColor.parseRgbString(elmStyle.getPropertyValue('background-color')); if (bgColor.alpha !== 0) { - let opacity = elmStyle.getPropertyValue('opacity'); + const opacity = elmStyle.getPropertyValue('opacity'); bgColor.alpha = bgColor.alpha * opacity; } diff --git a/test/commons/color/element-has-background-image.js b/test/commons/color/element-has-background-image.js new file mode 100644 index 0000000000..518e3dcbce --- /dev/null +++ b/test/commons/color/element-has-background-image.js @@ -0,0 +1,62 @@ +describe('color.elementHasBackgroundImage', function() { + 'use strict'; + + var fixture = document.getElementById('fixture'); + var queryFixture = axe.testUtils.queryFixture; + var elementHasBackgroundImage = axe.commons.color.elementHasBackgroundImage; + + afterEach(function() { + fixture.innerHTML = ''; + axe._tree = undefined; + }); + + it('returns true when `HTMLElement` is of graphical type', function() { + ['img', 'canvas', 'object', 'iframe', 'video', 'svg'].forEach(function( + nodeName + ) { + var vNode = queryFixture( + '<' + nodeName + ' id="target">' + ); + var actual = elementHasBackgroundImage(vNode.actualNode); + assert.isTrue(actual); + }); + }); + + it('returns false when `HTMLElement` has no background-image style set', function() { + var vNode = queryFixture( + '
No background style
' + ); + var actual = elementHasBackgroundImage(vNode.actualNode); + assert.isFalse(actual); + }); + + it('returns false when `HTMLElement` has background-image style set to none', function() { + var vNode = queryFixture( + '
Some text...
' + ); + var actual = elementHasBackgroundImage(vNode.actualNode); + assert.isFalse(actual); + }); + + it('returns true when `HTMLElement` has background-image (url)', function() { + var vNode = queryFixture( + '
Some text...
' + ); + var actual = elementHasBackgroundImage(vNode.actualNode); + assert.isTrue(actual); + + var bgColorIncompleteData = axe.commons.color.incompleteData.get('bgColor'); + assert.equal(bgColorIncompleteData, 'bgImage'); + }); + + it('returns true when `HTMLElement` has background-image (gradient)', function() { + var vNode = queryFixture( + '
Some text...
' + ); + var actual = elementHasBackgroundImage(vNode.actualNode); + assert.isTrue(actual); + + var bgColorIncompleteData = axe.commons.color.incompleteData.get('bgColor'); + assert.equal(bgColorIncompleteData, 'bgGradient'); + }); +}); diff --git a/test/commons/color/get-non-alpha-blended-background-color.js b/test/commons/color/get-non-alpha-blended-background-color.js new file mode 100644 index 0000000000..15dace4dfc --- /dev/null +++ b/test/commons/color/get-non-alpha-blended-background-color.js @@ -0,0 +1,69 @@ +describe('color.getNonAlphaBlendedBackgroundColor', function() { + 'use strict'; + + var fixture = document.getElementById('fixture'); + var queryFixture = axe.testUtils.queryFixture; + var getNonAlphaBlendedBackgroundColor = + axe.commons.color.getNonAlphaBlendedBackgroundColor; + var isPhantom = window.PHANTOMJS ? true : false; + + afterEach(function() { + fixture.innerHTML = ''; + axe._tree = undefined; + }); + + it('returns `new axe.commons.color.Color` instance when no background is set', function() { + var vNode = queryFixture( + '
' + '
' + ); + var actual = getNonAlphaBlendedBackgroundColor(vNode.actualNode); + assert.equal(actual.red, 0); + assert.equal(actual.green, 0); + assert.equal(actual.blue, 0); + if (!isPhantom) { + assert.equal(actual.alpha, 0); + } + }); + + it('returns the non-blended color with rgba values of specified background-color value', function() { + var vNode = queryFixture( + '
' + + '
' + ); + var actual = getNonAlphaBlendedBackgroundColor(vNode.actualNode); + assert.equal(actual.red, 255); + assert.equal(actual.green, 192); + assert.equal(actual.blue, 203); + if (!isPhantom) { + assert.equal(actual.alpha, 1); + } + }); + + it('returns the non-blended color with rgba values excluding alpha (for blending)', function() { + var vNode = queryFixture( + '
' + + '
' + ); + var actual = getNonAlphaBlendedBackgroundColor(vNode.actualNode); + assert.equal(actual.red, 0); + assert.equal(actual.green, 128); + assert.equal(actual.blue, 0); + if (!isPhantom) { + assert.equal(actual.alpha, 0.5); + } + }); + + it('returns the non-blended color with rgba values excluding opacity (for blending)', function() { + var vNode = queryFixture( + '
' + + '
' + ); + var actual = getNonAlphaBlendedBackgroundColor(vNode.actualNode); + assert.equal(actual.red, 0); + assert.equal(actual.green, 128); + assert.equal(actual.blue, 0); + if (!isPhantom) { + assert.equal(actual.alpha, 1); + } + }); +}); From 07cf21a345a32017d915da40d33f0ef303a1ec55 Mon Sep 17 00:00:00 2001 From: jkodu Date: Thu, 4 Apr 2019 20:13:23 +0100 Subject: [PATCH 04/12] test: fix test --- test/commons/color/get-non-alpha-blended-background-color.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/commons/color/get-non-alpha-blended-background-color.js b/test/commons/color/get-non-alpha-blended-background-color.js index 15dace4dfc..655bb6ea62 100644 --- a/test/commons/color/get-non-alpha-blended-background-color.js +++ b/test/commons/color/get-non-alpha-blended-background-color.js @@ -63,7 +63,7 @@ describe('color.getNonAlphaBlendedBackgroundColor', function() { assert.equal(actual.green, 128); assert.equal(actual.blue, 0); if (!isPhantom) { - assert.equal(actual.alpha, 1); + assert.equal(actual.alpha, 0.5); } }); }); From c133b915aa0d2dd1811411f223e204f7d9fbbade Mon Sep 17 00:00:00 2001 From: jkodu Date: Thu, 4 Apr 2019 20:56:25 +0100 Subject: [PATCH 05/12] test: update test --- test/core/utils/get-coordinates-from-rect.js | 28 +++++++------------- 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/test/core/utils/get-coordinates-from-rect.js b/test/core/utils/get-coordinates-from-rect.js index 5fbf0deceb..3a2551ff4d 100644 --- a/test/core/utils/get-coordinates-from-rect.js +++ b/test/core/utils/get-coordinates-from-rect.js @@ -1,7 +1,6 @@ describe('axe.utils.getCoordinatesFromRect', function() { 'use strict'; - var isPhantom = window.PHANTOMJS ? true : false; var fixture = document.getElementById('fixture'); var queryFixture = axe.testUtils.queryFixture; @@ -31,24 +30,17 @@ describe('axe.utils.getCoordinatesFromRect', function() { assert.isUndefined(actual); }); - /** - * Note: - * Believe in PhantomJs run, the button which is relatively positioned, - * ends up out of bounds of viewport, and thus returns `undefined`. - */ - isPhantom - ? it.skip - : it('returns `{x,y}` when element is with in viewport', function() { - var vNode = queryFixture( - '' - ); - var node = vNode.actualNode; - var rect = node.getBoundingClientRect(); - var actual = axe.utils.getCoordinatesFromRect(rect); + it('returns `{x,y}` when element is with in viewport', function() { + var vNode = queryFixture( + '' + ); + var node = vNode.actualNode; + var rect = node.getBoundingClientRect(); + var actual = axe.utils.getCoordinatesFromRect(rect); - assert.isDefined(actual); - assert.hasAllKeys(actual, ['x', 'y']); - }); + assert.isDefined(actual); + assert.hasAllKeys(actual, ['x', 'y']); + }); it('returns `{x,y}` when element is with in viewport (check returned coordinate values)', function() { var vNode = queryFixture( From b6afc5e240f0906fe26d36702776ab91349148c6 Mon Sep 17 00:00:00 2001 From: jkodu Date: Mon, 15 Apr 2019 13:58:36 +0100 Subject: [PATCH 06/12] refactor: update color fns and tests --- .../color/center-point-of-rect.js} | 15 ++-- ...ement-has-background-image-or-gradient.js} | 4 +- lib/commons/color/get-background-color.js | 49 +++++++---- ...d-color.js => get-own-background-color.js} | 13 +-- test/commons/color/center-point-of-rect.js | 38 ++++++++ ...lement-has-background-image-or-gradient.js | 86 +++++++++++++++++++ .../color/element-has-background-image.js | 62 ------------- ...d-color.js => get-own-background-color.js} | 43 +++++++--- test/core/utils/get-coordinates-from-rect.js | 58 ------------- 9 files changed, 202 insertions(+), 166 deletions(-) rename lib/{core/utils/get-coordinates-from-rect.js => commons/color/center-point-of-rect.js} (56%) rename lib/commons/color/{element-has-background-image.js => element-has-background-image-or-gradient.js} (85%) rename lib/commons/color/{get-non-alpha-blended-background-color.js => get-own-background-color.js} (58%) create mode 100644 test/commons/color/center-point-of-rect.js create mode 100644 test/commons/color/element-has-background-image-or-gradient.js delete mode 100644 test/commons/color/element-has-background-image.js rename test/commons/color/{get-non-alpha-blended-background-color.js => get-own-background-color.js} (56%) delete mode 100644 test/core/utils/get-coordinates-from-rect.js diff --git a/lib/core/utils/get-coordinates-from-rect.js b/lib/commons/color/center-point-of-rect.js similarity index 56% rename from lib/core/utils/get-coordinates-from-rect.js rename to lib/commons/color/center-point-of-rect.js index 85f855fa7f..e5200767b5 100644 --- a/lib/core/utils/get-coordinates-from-rect.js +++ b/lib/commons/color/center-point-of-rect.js @@ -1,18 +1,19 @@ +/* global color */ + /** * Get coordinates for an element's client rects or bounding client rect * - * @method getCoordinatesFromRect - * @memberof axe.utils - * + * @method centerPointOfRect + * @memberof axe.commons.color * @param {DOMRect} rect - * @return {Object | undefined} + * @returns {Object | undefined} */ -axe.utils.getCoordinatesFromRect = function getCoordinatesFromRect(rect) { - if (rect.left < 0 || rect.left > window.innerWidth) { +color.centerPointOfRect = function centerPointOfRect(rect) { + if (rect.left > window.innerWidth) { return undefined; } - if (rect.top < 0 || rect.top > window.innerHeight) { + if (rect.top > window.innerHeight) { return undefined; } diff --git a/lib/commons/color/element-has-background-image.js b/lib/commons/color/element-has-background-image-or-gradient.js similarity index 85% rename from lib/commons/color/element-has-background-image.js rename to lib/commons/color/element-has-background-image-or-gradient.js index 9e48467b9e..996cf983e9 100644 --- a/lib/commons/color/element-has-background-image.js +++ b/lib/commons/color/element-has-background-image-or-gradient.js @@ -3,14 +3,14 @@ /** * Reports if an element has a background image or gradient * - * @method elementHasBackgroundImage + * @method elementHasBackgroundImageOrGradient * @memberof axe.commons.color * @private * @param {Element} elm * @param {Object|null} style * @return {Boolean} */ -color.elementHasBackgroundImage = function elementHasBackgroundImage( +color.elementHasBackgroundImageOrGradient = function elementHasBackgroundImageOrGradient( elm, style ) { diff --git a/lib/commons/color/get-background-color.js b/lib/commons/color/get-background-color.js index 6b50f5fcbe..2bb0e054fb 100644 --- a/lib/commons/color/get-background-color.js +++ b/lib/commons/color/get-background-color.js @@ -28,20 +28,20 @@ color.getBackgroundColor = function getBackgroundColor( } let bgColors = []; - let elmStack = getBackgroundStack(elm); + let elmStack = color.getBackgroundStack(elm); // Search the stack until we have an alpha === 1 background (elmStack || []).some(bgElm => { - let bgElmStyle = window.getComputedStyle(bgElm); + const bgElmStyle = window.getComputedStyle(bgElm); // Get the background color - let bgColor = color.getNonAlphaBlendedBackgroundColor(bgElm, bgElmStyle); + let bgColor = color.getOwnBackgroundColor(bgElmStyle); if ( // abort if a node is partially obscured and obscuring element has a background elmPartiallyObscured(elm, bgElm, bgColor) || // OR if the background elm is a graphic - color.elementHasBackgroundImage(bgElm, bgElmStyle) + color.elementHasBackgroundImageOrGradient(bgElm, bgElmStyle) ) { bgColors = null; bgElms.push(bgElm); @@ -76,11 +76,11 @@ color.getBackgroundColor = function getBackgroundColor( * In the order they are displayed (front to back) * * @method getBackgroundStack - * @private + * @memberof axe.commons.color * @param {Element} elm * @return {Array} */ -function getBackgroundStack(elm) { +color.getBackgroundStack = function getBackgroundStack(elm) { let elmStack = filteredRectStack(elm); if (elmStack === null) { @@ -98,7 +98,7 @@ function getBackgroundStack(elm) { return null; } return elmIndex !== -1 ? elmStack : null; -} +}; /** * Get filtered stack of block and inline elements, excluding line breaks @@ -108,7 +108,7 @@ function getBackgroundStack(elm) { * @return {Array} */ function filteredRectStack(elm) { - const rectStack = getRectStack(elm); + const rectStack = color.getRectStack(elm); if (rectStack && rectStack.length === 1) { return rectStack[0]; @@ -150,12 +150,12 @@ function filteredRectStack(elm) { /** * Get relevant stacks of block and inline elements, excluding line breaks * @method getRectStack - * @private + * @memberof axe.commons.color * @param {Element} elm * @return {Array} */ -function getRectStack(elm) { - const boundingCoords = axe.utils.getCoordinatesFromRect( +color.getRectStack = function(elm) { + const boundingCoords = axe.commons.color.centerPointOfRect( elm.getBoundingClientRect() ); @@ -181,7 +181,7 @@ function getRectStack(elm) { return rect.width && rect.width > 0; }) .map(rect => { - const coords = axe.utils.getCoordinatesFromRect(rect); + const coords = axe.commons.color.centerPointOfRect(rect); if (coords) { return dom.shadowElementsFromPoint(coords.x, coords.y); } @@ -195,7 +195,7 @@ function getRectStack(elm) { // add bounding client rect stack for comparison later filteredArr.splice(0, 0, boundingStack); return filteredArr; -} +}; /** * Look at document and body elements for relevant background information @@ -206,15 +206,15 @@ function getRectStack(elm) { */ function sortPageBackground(elmStack) { let bodyIndex = elmStack.indexOf(document.body); - let bgNodes = elmStack; if ( // Check that the body background is the page's background bodyIndex > 1 && // only if there are negative z-index elements - !color.elementHasBackgroundImage(document.documentElement) && - color.getNonAlphaBlendedBackgroundColor(document.documentElement).alpha === - 0 + !color.elementHasBackgroundImageOrGradient(document.documentElement) && + color.getOwnBackgroundColor( + window.getComputedStyle(document.documentElement) + ).alpha === 0 ) { // Remove body and html from it's current place bgNodes.splice(bodyIndex, 1); @@ -316,7 +316,7 @@ function calculateObscuringAlpha(elmIndex, elmStack, originalElm) { for (var i = elmIndex - 1; i >= 0; i--) { let bgElm = elmStack[i]; let bgElmStyle = window.getComputedStyle(bgElm); - let bgColor = color.getNonAlphaBlendedBackgroundColor(bgElm, bgElmStyle); + let bgColor = color.getOwnBackgroundColor(bgElmStyle); if (bgColor.alpha && contentOverlapping(originalElm, bgElm)) { totalAlpha += bgColor.alpha; } else { @@ -355,3 +355,16 @@ function contentOverlapping(targetElement, bgNode) { } return false; } + +/** + * Determines whether an element has a fully opaque background, whether solid color or an image + * @param {Element} node + * @return {Boolean} false if the background is transparent, true otherwise + */ +dom.isOpaque = function(node) { + const style = window.getComputedStyle(node); + return ( + color.elementHasBackgroundImageOrGradient(node, style) || + color.getOwnBackgroundColor(style).alpha === 1 + ); +}; diff --git a/lib/commons/color/get-non-alpha-blended-background-color.js b/lib/commons/color/get-own-background-color.js similarity index 58% rename from lib/commons/color/get-non-alpha-blended-background-color.js rename to lib/commons/color/get-own-background-color.js index d09fd61aa9..ad63d25def 100644 --- a/lib/commons/color/get-non-alpha-blended-background-color.js +++ b/lib/commons/color/get-own-background-color.js @@ -3,19 +3,14 @@ /** * Returns the non-alpha-blended background color of an element * - * @method getNonAlphaBlendedBackgroundColor + * @method getOwnBackgroundColor * @memberof axe.commons.color * - * @param {Element} elm + * @param {Object} elmStyle style of the element * @return {Color} */ -color.getNonAlphaBlendedBackgroundColor = function getNonAlphaBlendedBackgroundColor( - elm, - elmStyle -) { - elmStyle = elmStyle || window.getComputedStyle(elm); - - let bgColor = new color.Color(); +color.getOwnBackgroundColor = function getOwnBackgroundColor(elmStyle) { + const bgColor = new color.Color(); bgColor.parseRgbString(elmStyle.getPropertyValue('background-color')); if (bgColor.alpha !== 0) { diff --git a/test/commons/color/center-point-of-rect.js b/test/commons/color/center-point-of-rect.js new file mode 100644 index 0000000000..289b85b877 --- /dev/null +++ b/test/commons/color/center-point-of-rect.js @@ -0,0 +1,38 @@ +describe('color.centerPointOfRect', function() { + 'use strict'; + + it('returns `undefined` when element is placed outside of viewport (left position > window dimension)', function() { + var actual = axe.commons.color.centerPointOfRect({ + left: 9999, + top: 0, + width: 200, + height: 100 + }); + assert.isUndefined(actual); + }); + + it('returns `{x,y}` when element is with in viewport', function() { + var actual = axe.commons.color.centerPointOfRect({ + left: 0, + top: 0, + width: 200, + height: 100 + }); + assert.isDefined(actual); + assert.hasAllKeys(actual, ['x', 'y']); + }); + + it('returns `{x,y}` when element is with in viewport (check returned coordinate values)', function() { + var actual = axe.commons.color.centerPointOfRect({ + left: 100, + top: 100, + width: 250, + height: 250 + }); + + assert.isDefined(actual); + assert.hasAllKeys(actual, ['x', 'y']); + assert.equal(actual.x, 225); + assert.equal(actual.y, 225); + }); +}); diff --git a/test/commons/color/element-has-background-image-or-gradient.js b/test/commons/color/element-has-background-image-or-gradient.js new file mode 100644 index 0000000000..507494b75f --- /dev/null +++ b/test/commons/color/element-has-background-image-or-gradient.js @@ -0,0 +1,86 @@ +describe('color.elementHasBackgroundImageOrGradient', function() { + 'use strict'; + + var fixture = document.getElementById('fixture'); + var queryFixture = axe.testUtils.queryFixture; + var elementHasBackgroundImageOrGradient = + axe.commons.color.elementHasBackgroundImageOrGradient; + var origColorIncompleteData = axe.commons.color.incompleteData; + + afterEach(function() { + fixture.innerHTML = ''; + axe._tree = undefined; + axe.commons.color.incompleteData = origColorIncompleteData; + }); + + it('returns true when `HTMLElement` is of graphical type', function() { + ['img', 'canvas', 'object', 'iframe', 'video', 'svg'].forEach(function( + nodeName + ) { + var vNode = queryFixture( + '<' + nodeName + ' id="target">' + ); + var actual = elementHasBackgroundImageOrGradient(vNode.actualNode); + assert.isTrue(actual); + }); + }); + + it('returns false when `HTMLElement` has no background-image style set', function() { + var vNode = queryFixture( + '
No background style
' + ); + var actual = elementHasBackgroundImageOrGradient(vNode.actualNode); + assert.isFalse(actual); + }); + + it('returns false when `HTMLElement` has background-image style set to none', function() { + var vNode = queryFixture( + '
Some text...
' + ); + var actual = elementHasBackgroundImageOrGradient(vNode.actualNode); + assert.isFalse(actual); + }); + + it('returns true when `HTMLElement` has background-image (url)', function() { + var vNode = queryFixture( + '
Some text...
' + ); + var actual = elementHasBackgroundImageOrGradient(vNode.actualNode); + assert.isTrue(actual); + assert.equal(axe.commons.color.incompleteData.get('bgColor'), 'bgImage'); + }); + + it('returns true when `HTMLElement` has background-image (gradient)', function() { + var vNode = queryFixture( + '
Some text...
' + ); + var actual = elementHasBackgroundImageOrGradient(vNode.actualNode); + assert.isTrue(actual); + assert.equal(axe.commons.color.incompleteData.get('bgColor'), 'bgGradient'); + }); + + it('returns true when `HTMLElement` has background-image (gradient) and ensure incompleteData setter is invoked', function() { + var incompleteDataCalled = false; + var vNode = queryFixture( + '
Some text...
' + ); + // override `incompleteData` setter + axe.commons.color.incompleteData = (function() { + var data = {}; + return { + set: function(key, value) { + incompleteDataCalled = true; + data[key] = value; + return data[key]; + }, + get: function(key) { + return data[key]; + } + }; + })(); + var actual = elementHasBackgroundImageOrGradient(vNode.actualNode); + assert.isTrue(actual); + assert.isTrue(incompleteDataCalled); + assert.equal(axe.commons.color.incompleteData.get('bgColor'), 'bgGradient'); + }); +}); diff --git a/test/commons/color/element-has-background-image.js b/test/commons/color/element-has-background-image.js deleted file mode 100644 index 518e3dcbce..0000000000 --- a/test/commons/color/element-has-background-image.js +++ /dev/null @@ -1,62 +0,0 @@ -describe('color.elementHasBackgroundImage', function() { - 'use strict'; - - var fixture = document.getElementById('fixture'); - var queryFixture = axe.testUtils.queryFixture; - var elementHasBackgroundImage = axe.commons.color.elementHasBackgroundImage; - - afterEach(function() { - fixture.innerHTML = ''; - axe._tree = undefined; - }); - - it('returns true when `HTMLElement` is of graphical type', function() { - ['img', 'canvas', 'object', 'iframe', 'video', 'svg'].forEach(function( - nodeName - ) { - var vNode = queryFixture( - '<' + nodeName + ' id="target">' - ); - var actual = elementHasBackgroundImage(vNode.actualNode); - assert.isTrue(actual); - }); - }); - - it('returns false when `HTMLElement` has no background-image style set', function() { - var vNode = queryFixture( - '
No background style
' - ); - var actual = elementHasBackgroundImage(vNode.actualNode); - assert.isFalse(actual); - }); - - it('returns false when `HTMLElement` has background-image style set to none', function() { - var vNode = queryFixture( - '
Some text...
' - ); - var actual = elementHasBackgroundImage(vNode.actualNode); - assert.isFalse(actual); - }); - - it('returns true when `HTMLElement` has background-image (url)', function() { - var vNode = queryFixture( - '
Some text...
' - ); - var actual = elementHasBackgroundImage(vNode.actualNode); - assert.isTrue(actual); - - var bgColorIncompleteData = axe.commons.color.incompleteData.get('bgColor'); - assert.equal(bgColorIncompleteData, 'bgImage'); - }); - - it('returns true when `HTMLElement` has background-image (gradient)', function() { - var vNode = queryFixture( - '
Some text...
' - ); - var actual = elementHasBackgroundImage(vNode.actualNode); - assert.isTrue(actual); - - var bgColorIncompleteData = axe.commons.color.incompleteData.get('bgColor'); - assert.equal(bgColorIncompleteData, 'bgGradient'); - }); -}); diff --git a/test/commons/color/get-non-alpha-blended-background-color.js b/test/commons/color/get-own-background-color.js similarity index 56% rename from test/commons/color/get-non-alpha-blended-background-color.js rename to test/commons/color/get-own-background-color.js index 655bb6ea62..097c3edf20 100644 --- a/test/commons/color/get-non-alpha-blended-background-color.js +++ b/test/commons/color/get-own-background-color.js @@ -1,10 +1,9 @@ -describe('color.getNonAlphaBlendedBackgroundColor', function() { +describe('color.getOwnBackgroundColor', function() { 'use strict'; var fixture = document.getElementById('fixture'); var queryFixture = axe.testUtils.queryFixture; - var getNonAlphaBlendedBackgroundColor = - axe.commons.color.getNonAlphaBlendedBackgroundColor; + var getOwnBackgroundColor = axe.commons.color.getOwnBackgroundColor; var isPhantom = window.PHANTOMJS ? true : false; afterEach(function() { @@ -16,7 +15,9 @@ describe('color.getNonAlphaBlendedBackgroundColor', function() { var vNode = queryFixture( '
' + '
' ); - var actual = getNonAlphaBlendedBackgroundColor(vNode.actualNode); + var actual = getOwnBackgroundColor( + window.getComputedStyle(vNode.actualNode) + ); assert.equal(actual.red, 0); assert.equal(actual.green, 0); assert.equal(actual.blue, 0); @@ -25,12 +26,14 @@ describe('color.getNonAlphaBlendedBackgroundColor', function() { } }); - it('returns the non-blended color with rgba values of specified background-color value', function() { + it('returns color with rgba values of specified background-color value', function() { var vNode = queryFixture( '
' + '
' ); - var actual = getNonAlphaBlendedBackgroundColor(vNode.actualNode); + var actual = getOwnBackgroundColor( + window.getComputedStyle(vNode.actualNode) + ); assert.equal(actual.red, 255); assert.equal(actual.green, 192); assert.equal(actual.blue, 203); @@ -39,12 +42,14 @@ describe('color.getNonAlphaBlendedBackgroundColor', function() { } }); - it('returns the non-blended color with rgba values excluding alpha (for blending)', function() { + it('returns color with rgba values and alpha', function() { var vNode = queryFixture( '
' + '
' ); - var actual = getNonAlphaBlendedBackgroundColor(vNode.actualNode); + var actual = getOwnBackgroundColor( + window.getComputedStyle(vNode.actualNode) + ); assert.equal(actual.red, 0); assert.equal(actual.green, 128); assert.equal(actual.blue, 0); @@ -53,12 +58,14 @@ describe('color.getNonAlphaBlendedBackgroundColor', function() { } }); - it('returns the non-blended color with rgba values excluding opacity (for blending)', function() { + it('returns color with rgba values and opacity (for blending)', function() { var vNode = queryFixture( '
' + '
' ); - var actual = getNonAlphaBlendedBackgroundColor(vNode.actualNode); + var actual = getOwnBackgroundColor( + window.getComputedStyle(vNode.actualNode) + ); assert.equal(actual.red, 0); assert.equal(actual.green, 128); assert.equal(actual.blue, 0); @@ -66,4 +73,20 @@ describe('color.getNonAlphaBlendedBackgroundColor', function() { assert.equal(actual.alpha, 0.5); } }); + + it('returns color with rgba values, alpha and opacity', function() { + var vNode = queryFixture( + '
' + + '
' + ); + var actual = getOwnBackgroundColor( + window.getComputedStyle(vNode.actualNode) + ); + assert.equal(actual.red, 0); + assert.equal(actual.green, 128); + assert.equal(actual.blue, 0); + if (!isPhantom) { + assert.equal(actual.alpha, 0.25); + } + }); }); diff --git a/test/core/utils/get-coordinates-from-rect.js b/test/core/utils/get-coordinates-from-rect.js deleted file mode 100644 index 3a2551ff4d..0000000000 --- a/test/core/utils/get-coordinates-from-rect.js +++ /dev/null @@ -1,58 +0,0 @@ -describe('axe.utils.getCoordinatesFromRect', function() { - 'use strict'; - - var fixture = document.getElementById('fixture'); - var queryFixture = axe.testUtils.queryFixture; - - afterEach(function() { - fixture.innerHTML = ''; - }); - - it('returns `undefined` when element is placed outside of viewport (left position > window dimension)', function() { - var vNode = queryFixture( - '' - ); - var node = vNode.actualNode; - var rect = node.getBoundingClientRect(); - var actual = axe.utils.getCoordinatesFromRect(rect); - - assert.isUndefined(actual); - }); - - it('returns `undefined` when element is placed outside of viewport (top position is negative)', function() { - var vNode = queryFixture( - '' - ); - var node = vNode.actualNode; - var rect = node.getBoundingClientRect(); - var actual = axe.utils.getCoordinatesFromRect(rect); - - assert.isUndefined(actual); - }); - - it('returns `{x,y}` when element is with in viewport', function() { - var vNode = queryFixture( - '' - ); - var node = vNode.actualNode; - var rect = node.getBoundingClientRect(); - var actual = axe.utils.getCoordinatesFromRect(rect); - - assert.isDefined(actual); - assert.hasAllKeys(actual, ['x', 'y']); - }); - - it('returns `{x,y}` when element is with in viewport (check returned coordinate values)', function() { - var vNode = queryFixture( - '' - ); - var node = vNode.actualNode; - var rect = node.getBoundingClientRect(); - var actual = axe.utils.getCoordinatesFromRect(rect); - - assert.isDefined(actual); - assert.hasAllKeys(actual, ['x', 'y']); - assert.equal(actual.x, 225); - assert.equal(actual.y, 225); - }); -}); From 03088ae9492b19572793a7bee041477127b10d7f Mon Sep 17 00:00:00 2001 From: jkodu Date: Tue, 16 Apr 2019 15:40:22 +0100 Subject: [PATCH 07/12] test: debugging --- lib/commons/dom/is-offscreen.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/commons/dom/is-offscreen.js b/lib/commons/dom/is-offscreen.js index 5e649bf991..8461bf9ea3 100644 --- a/lib/commons/dom/is-offscreen.js +++ b/lib/commons/dom/is-offscreen.js @@ -29,6 +29,8 @@ dom.isOffscreen = function(element) { const dir = window .getComputedStyle(document.body || docElement) .getPropertyValue('direction'); + + console.log(dir); const coords = dom.getElementCoordinates(element); // bottom edge beyond @@ -41,6 +43,8 @@ dom.isOffscreen = function(element) { if (coords.left === 0 && coords.right === 0) { //This is an edge case, an empty (zero-width) element that isn't positioned 'off screen'. + console.log('GOT HERE 1'); + console.log(JSON.stringify(coords)); return false; } @@ -58,5 +62,6 @@ dom.isOffscreen = function(element) { } } + console.log('GOT HERE 2'); return false; }; From ca74b835eb4fd9a7c2cf5eac8859c4d8fb8398c0 Mon Sep 17 00:00:00 2001 From: jkodu Date: Tue, 16 Apr 2019 15:46:31 +0100 Subject: [PATCH 08/12] test: add more logging --- lib/commons/dom/is-offscreen.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/commons/dom/is-offscreen.js b/lib/commons/dom/is-offscreen.js index 8461bf9ea3..7d2c070842 100644 --- a/lib/commons/dom/is-offscreen.js +++ b/lib/commons/dom/is-offscreen.js @@ -57,6 +57,9 @@ dom.isOffscreen = function(element) { docElement.scrollWidth, dom.getViewportSize(window).width ); + console.log('docElement.scrollWidth', docElement.scrollWidth); + console.log('viewport width', dom.getViewportSize(window).width); + console.log('left boundary', leftBoundary); if (coords.left >= leftBoundary) { return true; } From 6916858ab7ca7a8315fa9b0d1fb8afff28817994 Mon Sep 17 00:00:00 2001 From: jkodu Date: Wed, 17 Apr 2019 14:35:22 +0100 Subject: [PATCH 09/12] fix: wrap mocha anchors --- test/runner.tmpl | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/runner.tmpl b/test/runner.tmpl index d53be6d1b6..b82e7c4a40 100644 --- a/test/runner.tmpl +++ b/test/runner.tmpl @@ -7,6 +7,13 @@ +