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

feat(setup/teardown): add functions to setup and teardown axe-core internal data, deprecate axe._tree #2738

Merged
merged 2 commits into from
Jan 12, 2021
Merged
Show file tree
Hide file tree
Changes from all 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
4 changes: 4 additions & 0 deletions lib/core/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import reset from './public/reset';
import runRules from './public/run-rules';
import runVirtualRule from './public/run-virtual-rule';
import run from './public/run';
import setup from './public/setup';
import teardown from './public/teardown';

import naReporter from './reporters/na';
import noPassesReporter from './reporters/no-passes';
Expand Down Expand Up @@ -73,6 +75,8 @@ axe.reset = reset;
axe._runRules = runRules;
axe.runVirtualRule = runVirtualRule;
axe.run = run;
axe.setup = setup;
axe.teardown = teardown;

axe.commons = commons;
axe.utils = utils;
Expand Down
29 changes: 7 additions & 22 deletions lib/core/public/run-rules.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Context from '../base/context';
import cache from '../base/cache';
import teardown from './teardown';
import {
getSelectorData,
queue,
Expand All @@ -11,27 +11,12 @@ import {
} from '../utils';
import log from '../log';

// Clean up after resolve / reject
function cleanup() {
if (cache.get('globalDocumentSet')) {
document = null;
}
if (cache.get('globalWindowSet')) {
window = null;
}

axe._memoizedFns.forEach(fn => fn.clear());
cache.clear();
axe._tree = undefined;
axe._selectorData = undefined;
}

/**
* Starts analysis on the current document and its subframes
* @private
* @param {Object} context The `Context` specification object @see Context
* @param {Array} options Optional RuleOptions
* @param {Function} resolve Called when done running rules, receives ([results : Object], cleanup : Function)
* @param {Function} resolve Called when done running rules, receives ([results : Object], teardown : Function)
* @param {Function} reject Called when execution failed, receives (err : Error)
*/
function runRules(context, options, resolve, reject) {
Expand All @@ -40,7 +25,7 @@ function runRules(context, options, resolve, reject) {
axe._tree = context.flatTree;
axe._selectorData = getSelectorData(context.flatTree);
} catch (e) {
cleanup();
teardown();
return reject(e);
}

Expand Down Expand Up @@ -80,17 +65,17 @@ function runRules(context, options, resolve, reject) {
results = results.map(finalizeRuleResult);
}
try {
resolve(results, cleanup);
resolve(results, teardown);
} catch (e) {
cleanup();
teardown();
log(e);
}
} catch (e) {
cleanup();
teardown();
reject(e);
}
}).catch(e => {
cleanup();
teardown();
reject(e);
});
}
Expand Down
20 changes: 20 additions & 0 deletions lib/core/public/setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { getFlattenedTree, getSelectorData } from '../utils';

/**
* Setup axe-core so axe.common functions can work properly.
* @param {Node} [node=document.documentElement] optional node. NOTE: passing in anything other than body or the documentElement may result in incomplete results.
*/
function setup(node) {
straker marked this conversation as resolved.
Show resolved Hide resolved
if (axe._tree) {
throw new Error(
'Axe is already setup. Call `axe.teardown()` before calling `axe.setup` again.'
);
}

axe._tree = getFlattenedTree(node);
axe._selectorData = getSelectorData(axe._tree);
straker marked this conversation as resolved.
Show resolved Hide resolved

return axe._tree[0];
}

export default setup;
20 changes: 20 additions & 0 deletions lib/core/public/teardown.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import cache from '../base/cache';

/**
* Clean up axe-core tree and caches. `axe.run` will call this function at the end of the run so there's no need to call it yourself afterwards.
*/
function teardown() {
straker marked this conversation as resolved.
Show resolved Hide resolved
if (cache.get('globalDocumentSet')) {
document = null;
}
if (cache.get('globalWindowSet')) {
window = null;
}

axe._memoizedFns.forEach(fn => fn.clear());
cache.clear();
axe._tree = undefined;
axe._selectorData = undefined;
}

export default teardown;
43 changes: 43 additions & 0 deletions test/core/public/setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
describe('axe.setup', function() {
'use strict';

afterEach(function() {
axe.teardown();
});

it('should setup the tree', function() {
axe._tree = undefined;
axe.setup();
assert.exists(axe._tree);
});

it('should default the tree to use html element', function() {
axe.setup();
assert.equal(axe._tree[0].actualNode, document.documentElement);
});

it('should use the passed in node as the root of the tree', function() {
axe.setup(document.body);
assert.equal(axe._tree[0].actualNode, document.body);
});

it('should return the root node', function() {
var vNode = axe.setup(document.body);
assert.equal(vNode.actualNode, document.body);
});

it('should setup selector data', function() {
axe._selectorData = undefined;
axe.setup();
assert.exists(axe._selectorData);
});

it('should throw if called twice in a row', function() {
function fn() {
axe.setup();
axe.setup();
}

assert.throws(fn);
});
});
39 changes: 39 additions & 0 deletions test/core/public/teardown.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
describe('axe.teardown', function() {
'use strict';

it('should reset the tree', function() {
axe._tree = 'foo';
axe.teardown();
assert.isUndefined(axe._tree);
});

it('should reset selector data', function() {
axe._selectorData = 'foo';
axe.teardown();
assert.isUndefined(axe._selectorData);
});

it('should reset memozied functions', function() {
var orgFn = axe._memoizedFns[0];
var called = false;
axe._memoizedFns[0] = {
clear: function() {
called = true;
}
};
axe.teardown();
assert.isTrue(called);
axe._memoizedFns[0] = orgFn;
});

it('should reset the cache', function() {
var orgFn = axe._cache.clear;
var called = false;
axe._cache.clear = function() {
called = true;
};
axe.teardown();
assert.isTrue(called);
axe._cache.clear = orgFn;
});
});