Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(utils): make cache global instead of only setup in axe.run #1535

Merged
merged 4 commits into from
May 9, 2019
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions lib/commons/aria/is-accessible-ref.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ const idRefsRegex = /^idrefs?$/;
function cacheIdRefs(node, refAttrs) {
if (node.hasAttribute) {
if (node.nodeName.toUpperCase() === 'LABEL' && node.hasAttribute('for')) {
axe._cache.idRefs[node.getAttribute('for')] = true;
axe._cache.get('idRefs')[node.getAttribute('for')] = true;
}

refAttrs
.filter(attr => node.hasAttribute(attr))
.forEach(attr => {
const attrValue = node.getAttribute(attr);
axe.utils.tokenList(attrValue).forEach(id => {
axe._cache.idRefs[id] = true;
axe._cache.get('idRefs')[id] = true;
});
});
}
Expand All @@ -35,8 +35,8 @@ aria.isAccessibleRef = function isAccessibleRef(node) {

// because axe.commons is not available in axe.utils, we can't do
// this caching when we build up the virtual tree
if (!axe._cache.idRefs) {
axe._cache.idRefs = {};
if (!axe._cache.get('idRefs')) {
axe._cache.set('idRefs', {});
// Get all idref(s) attributes on the lookup table
const refAttrs = Object.keys(aria.lookupTable.attributes).filter(attr => {
const { type } = aria.lookupTable.attributes[attr];
Expand All @@ -46,5 +46,5 @@ aria.isAccessibleRef = function isAccessibleRef(node) {
cacheIdRefs(root, refAttrs);
}

return axe._cache.idRefs[id] === true;
return axe._cache.get('idRefs')[id] === true;
};
6 changes: 3 additions & 3 deletions lib/commons/dom/is-skip-link.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ dom.isSkipLink = function(element) {
}

let firstPageLink;
if (typeof axe._cache.firstPageLink !== 'undefined') {
firstPageLink = axe._cache.firstPageLink;
if (typeof axe._cache.get('firstPageLink') !== 'undefined') {
firstPageLink = axe._cache.get('firstPageLink');
} else {
// define a skip link as any anchor element whose href starts with `#...`
// and which precedes the first anchor element whose href doesn't start
Expand All @@ -29,7 +29,7 @@ dom.isSkipLink = function(element) {
)[0];

// null will signify no first page link
axe._cache.firstPageLink = firstPageLink || null;
axe._cache.set('firstPageLink', firstPageLink || null);
}

// if there are no page links then all all links will need to be
Expand Down
33 changes: 33 additions & 0 deletions lib/core/base/cache.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
(function() {
'use strict';
let _cache = {};

const cache = {
/**
* Set an item in the cache.
* @param {String} key - Name of the key.
* @param {*} value - Value to store.
*/
set(key, value) {
_cache[key] = value;
},

/**
* Retrieve an item from the cache.
* @param {String} key - Name of the key the value was stored as.
* @returns {*} The item stored
*/
get(key) {
return _cache[key];
},

/**
* Clear the cache.
*/
clear() {
_cache = {};
}
};

axe._cache = cache;
})();
6 changes: 1 addition & 5 deletions lib/core/public/run-rules.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

// Clean up after resolve / reject
function cleanup() {
axe._cache = undefined;
axe._cache.clear();
axe._tree = undefined;
axe._selectorData = undefined;
}
Expand All @@ -19,10 +19,6 @@ function cleanup() {
function runRules(context, options, resolve, reject) {
'use strict';
try {
axe._cache = {
nodeMap: new WeakMap()
};

context = new Context(context);
axe._tree = context.flatTree;
axe._selectorData = axe.utils.getSelectorData(context.flatTree);
Expand Down
21 changes: 17 additions & 4 deletions lib/core/utils/flattened-tree.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ function virtualDOMfromNode(node, shadowId) {
return vNodeCache._tabbableElements;
}
};
axe._cache.nodeMap.set(node, vNode);
axe._cache.get('nodeMap').set(node, vNode);
return vNode;
}

Expand Down Expand Up @@ -78,11 +78,11 @@ function getSlotChildren(node) {
* @param {String} shadowId, optional ID of the shadow DOM that is the closest shadow
* ancestor of the node
*/
axe.utils.getFlattenedTree = function(node, shadowId) {
function flattenTree(node, shadowId) {
// using a closure here and therefore cannot easily refactor toreduce the statements
var retVal, realArray, nodeName;
function reduceShadowDOM(res, child) {
var replacements = axe.utils.getFlattenedTree(child, shadowId);
var replacements = flattenTree(child, shadowId);
if (replacements) {
res = res.concat(replacements);
}
Expand Down Expand Up @@ -144,6 +144,19 @@ axe.utils.getFlattenedTree = function(node, shadowId) {
return undefined;
}
}
}

/**
* Recursvely returns an array of the virtual DOM nodes at this level
* excluding comment nodes and the shadow DOM nodes <content> and <slot>
*
* @param {Node} node the current node
* @param {String} shadowId, optional ID of the shadow DOM that is the closest shadow
* ancestor of the node
*/
axe.utils.getFlattenedTree = function(node, shadowId) {
axe._cache.set('nodeMap', new WeakMap());
return flattenTree(node, shadowId);
};

/**
Expand All @@ -154,5 +167,5 @@ axe.utils.getFlattenedTree = function(node, shadowId) {
*/
axe.utils.getNodeFromTree = function(vNode, node) {
const el = node || vNode;
return axe._cache.nodeMap.get(el);
return axe._cache.get('nodeMap') ? axe._cache.get('nodeMap').get(el) : null;
};
32 changes: 32 additions & 0 deletions test/core/base/cache.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
describe('axe._cache', function() {
'use strict';

it('should set items from the cache without error', function() {
function fn() {
axe._cache.set('foo', 'bar');
}
assert.doesNotThrow(fn);
});

it('should not throw for non-string keys', function() {
function fn() {
axe._cache.set(1, 'bar');
axe._cache.set({}, 'bar');
axe._cache.set(null, 'bar');
}
assert.doesNotThrow(fn);
});

it('should get an item from the cache', function() {
axe._cache.set('foo', 'bar');
var value = axe._cache.get('foo');
assert.equal(value, 'bar');
});

it('should clear the cache', function() {
axe._cache.set('foo', 'bar');
axe._cache.clear();
var value = axe._cache.get('foo');
assert.isUndefined(value);
});
});
6 changes: 2 additions & 4 deletions test/testutils.js
Original file line number Diff line number Diff line change
Expand Up @@ -308,8 +308,6 @@ testUtils.isIE11 = (function isIE11(navigator) {

axe.testUtils = testUtils;

beforeEach(function() {
axe._cache = {
nodeMap: new WeakMap()
};
afterEach(function() {
axe._cache.clear();
});