From 4b299fb2652efdd8735ec6d20f517e0192ef1446 Mon Sep 17 00:00:00 2001 From: Steven Lambert Date: Thu, 7 Jan 2021 14:01:27 -0700 Subject: [PATCH 1/2] feat(setup/teardown): add functions to setup and teardown axe-core internal data --- lib/core/core.js | 4 ++++ lib/core/public/run-rules.js | 29 +++++++-------------------- lib/core/public/setup.js | 12 +++++++++++ lib/core/public/teardown.js | 20 ++++++++++++++++++ test/core/public/setup.js | 29 +++++++++++++++++++++++++++ test/core/public/teardown.js | 39 ++++++++++++++++++++++++++++++++++++ 6 files changed, 111 insertions(+), 22 deletions(-) create mode 100644 lib/core/public/setup.js create mode 100644 lib/core/public/teardown.js create mode 100644 test/core/public/setup.js create mode 100644 test/core/public/teardown.js diff --git a/lib/core/core.js b/lib/core/core.js index 740e79ea0a..789ec1c3f3 100644 --- a/lib/core/core.js +++ b/lib/core/core.js @@ -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'; @@ -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; diff --git a/lib/core/public/run-rules.js b/lib/core/public/run-rules.js index d4f7697c3e..79bd5405cf 100644 --- a/lib/core/public/run-rules.js +++ b/lib/core/public/run-rules.js @@ -1,5 +1,5 @@ import Context from '../base/context'; -import cache from '../base/cache'; +import teardown from './teardown'; import { getSelectorData, queue, @@ -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) { @@ -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); } @@ -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); }); } diff --git a/lib/core/public/setup.js b/lib/core/public/setup.js new file mode 100644 index 0000000000..96b520c310 --- /dev/null +++ b/lib/core/public/setup.js @@ -0,0 +1,12 @@ +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) { + axe._tree = getFlattenedTree(node); + axe._selectorData = getSelectorData(axe._tree); +} + +export default setup; diff --git a/lib/core/public/teardown.js b/lib/core/public/teardown.js new file mode 100644 index 0000000000..e94f012ebe --- /dev/null +++ b/lib/core/public/teardown.js @@ -0,0 +1,20 @@ +import cache from '../base/cache'; + +/** + * Clean up axe-core tree and caches + */ +function teardown() { + 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; diff --git a/test/core/public/setup.js b/test/core/public/setup.js new file mode 100644 index 0000000000..5b8c567a7f --- /dev/null +++ b/test/core/public/setup.js @@ -0,0 +1,29 @@ +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 setup selector data', function() { + axe._selectorData = undefined; + axe.setup(); + assert.exists(axe._selectorData); + }); +}); diff --git a/test/core/public/teardown.js b/test/core/public/teardown.js new file mode 100644 index 0000000000..ba2214035a --- /dev/null +++ b/test/core/public/teardown.js @@ -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; + }); +}); From 029f5f52b4d0e64411cf26b332e9019e6b422840 Mon Sep 17 00:00:00 2001 From: Steven Lambert Date: Fri, 8 Jan 2021 11:48:49 -0700 Subject: [PATCH 2/2] return tree --- lib/core/public/setup.js | 8 ++++++++ lib/core/public/teardown.js | 2 +- test/core/public/setup.js | 14 ++++++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/lib/core/public/setup.js b/lib/core/public/setup.js index 96b520c310..3584a205e5 100644 --- a/lib/core/public/setup.js +++ b/lib/core/public/setup.js @@ -5,8 +5,16 @@ import { getFlattenedTree, getSelectorData } from '../utils'; * @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) { + 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); + + return axe._tree[0]; } export default setup; diff --git a/lib/core/public/teardown.js b/lib/core/public/teardown.js index e94f012ebe..b4b2ddc07b 100644 --- a/lib/core/public/teardown.js +++ b/lib/core/public/teardown.js @@ -1,7 +1,7 @@ import cache from '../base/cache'; /** - * Clean up axe-core tree and caches + * 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() { if (cache.get('globalDocumentSet')) { diff --git a/test/core/public/setup.js b/test/core/public/setup.js index 5b8c567a7f..01144a8097 100644 --- a/test/core/public/setup.js +++ b/test/core/public/setup.js @@ -21,9 +21,23 @@ describe('axe.setup', function() { 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); + }); });