diff --git a/lib/core/base/context.js b/lib/core/base/context.js index f6ce1c429e..19d9ba7790 100644 --- a/lib/core/base/context.js +++ b/lib/core/base/context.js @@ -129,7 +129,7 @@ function parseSelectorArray(context, type) { nodeList = Array.from(document.querySelectorAll(item)); //eslint no-loop-func:0 result = result.concat(nodeList.map((node) => { - return axe.utils.getNodeFromTree(axe._tree[0], node); + return axe.utils.getNodeFromTree(context.flatTree[0], node); })); break; } else if (item && item.length && !(item instanceof Node)) { @@ -140,14 +140,14 @@ function parseSelectorArray(context, type) { nodeList = Array.from(document.querySelectorAll(item[0])); //eslint no-loop-func:0 result = result.concat(nodeList.map((node) => { - return axe.utils.getNodeFromTree(axe._tree[0], node); + return axe.utils.getNodeFromTree(context.flatTree[0], node); })); } } else if (item instanceof Node) { if (item.documentElement instanceof Node) { - result.push(axe._tree[0]); + result.push(context.flatTree[0]); } else { - result.push(axe.utils.getNodeFromTree(axe._tree[0], item)); + result.push(axe.utils.getNodeFromTree(context.flatTree[0], item)); } } } @@ -232,7 +232,7 @@ function Context(spec) { spec = normalizeContext(spec); //cache the flattened tree - axe._tree = axe.utils.getFlattenedTree(getRootNode(spec)); + this.flatTree = axe.utils.getFlattenedTree(getRootNode(spec)); this.exclude = spec.exclude; this.include = spec.include; diff --git a/lib/core/public/run-rules.js b/lib/core/public/run-rules.js index 1d7ad954df..46209263c2 100644 --- a/lib/core/public/run-rules.js +++ b/lib/core/public/run-rules.js @@ -1,6 +1,13 @@ /*global Context */ /*exported runRules */ +function complete (cb, ...args) { + cb(...args); + // Clean up after resolve / reject + axe._tree = undefined; + axe._selectorData = undefined; +} + /** * Starts analysis on the current document and its subframes * @private @@ -12,8 +19,10 @@ function runRules(context, options, resolve, reject) { 'use strict'; try { context = new Context(context); + axe._tree = context.flatTree; + axe._selectorData = axe.utils.getSelectorData(context.flatTree); } catch (e) { - return reject(e); + return complete(reject, e); } var q = axe.utils.queue(); @@ -45,10 +54,8 @@ function runRules(context, options, resolve, reject) { } // Add wrapper object so that we may use the same "merge" function for results from inside and outside frames - var results = axe.utils.mergeResults(data.map(function (d) { - return { - results: d - }; + var results = axe.utils.mergeResults(data.map(function (results) { + return { results }; })); // after should only run once, so ensure we are in the top level window @@ -59,14 +66,16 @@ function runRules(context, options, resolve, reject) { results = results.map(axe.utils.finalizeRuleResult); } try { - resolve(results); + complete(resolve, results); } catch(e) { - axe.log(e); + complete(axe.log, e); } } catch (e) { - reject(e); + complete(reject, e); } - }).catch(reject); + }).catch((e) => { + complete(reject, e); + }); } axe._runRules = runRules; diff --git a/lib/core/public/run.js b/lib/core/public/run.js index 8fb69c0b95..d3d48dbd19 100644 --- a/lib/core/public/run.js +++ b/lib/core/public/run.js @@ -128,8 +128,6 @@ axe.run = function (context, options, callback) { try { let reporter = axe.getReporter(options.reporter); let results = reporter(rawResults, options, respond); - axe._selectorData = undefined; - axe._tree = undefined; if (results !== undefined) { respond(results); } diff --git a/lib/core/utils/get-selector.js b/lib/core/utils/get-selector.js index cf7f709aa3..a01731d6a9 100644 --- a/lib/core/utils/get-selector.js +++ b/lib/core/utils/get-selector.js @@ -307,11 +307,9 @@ function getThreeLeastCommonFeatures(elm, selectorData) { function generateSelector (elm, options, doc) { /*eslint max-statements:["error", 22], no-loop-func:0*/ if (!axe._selectorData) { - axe._selectorData = axe.utils.getSelectorData(axe._tree); + throw new Error('Expect axe._selectorData to be set up'); } - const { - toRoot = false - } = options; + const { toRoot = false } = options; let selector; let similar; diff --git a/test/core/base/context.js b/test/core/base/context.js index 96f66f629e..988b76aea8 100644 --- a/test/core/base/context.js +++ b/test/core/base/context.js @@ -327,11 +327,10 @@ describe('Context', function() { }); }); - it('should assign the result of getFlattenedTree to axe._tree', function () { - /* eslint no-new:0 */ - new Context({ include: [document] }); + it('should create a flatTree property', function () { + var context = new Context({ include: [document] }); // WARNING: This only works because there is now Shadow DOM on this page - assert.deepEqual(axe._tree, axe.utils.getFlattenedTree(document)); + assert.deepEqual(context.flatTree, axe.utils.getFlattenedTree(document)); }); it('should throw when frame could not be found', function (done) { @@ -350,27 +349,29 @@ describe('Context', function() { describe('object definition', function() { it('should assign include/exclude', function() { - + var flatTree = axe.utils.getFlattenedTree(document); assert.deepEqual(new Context({ include: ['#fixture'], exclude: ['#mocha'] }), { - include: [axe.utils.getFlattenedTree(document.getElementById('fixture'))[0]], - exclude: [axe.utils.getFlattenedTree(document.getElementById('mocha'))[0]], + include: axe.utils.querySelectorAll(flatTree, '#fixture'), + exclude: axe.utils.querySelectorAll(flatTree, '#mocha'), + flatTree: flatTree, initiator: true, page: false, frames: [] }); - }); - it('should disregard bad input, non-matching selectors', function() { + it('should disregard bad input, non-matching selectors', function() { + var flatTree = axe.utils.getFlattenedTree(document); assert.deepEqual(new Context({ include: ['#fixture', '#monkeys'], exclude: ['#bananas'] }), { - include: [axe.utils.getFlattenedTree(document.getElementById('fixture'))[0]], + include: axe.utils.querySelectorAll(flatTree, '#fixture'), exclude: [], + flatTree: flatTree, initiator: true, page: false, frames: [] diff --git a/test/core/public/run-rules.js b/test/core/public/run-rules.js index 822caf6989..38e3943a7a 100644 --- a/test/core/public/run-rules.js +++ b/test/core/public/run-rules.js @@ -650,4 +650,51 @@ describe('runRules', function () { done(); }, isNotCalled); }); + + it('should clear up axe._tree / axe._selectorData after resolving', function (done) { + axe._load({ rules: [{ + id: 'html', + selector: 'html', + any: ['html'] + }], checks: [{ + id: 'html', + evaluate: function () { + return true; + } + }], messages: {}}); + + runRules(document, {}, function resolve() { + assert.isDefined(axe._tree); + assert.isDefined(axe._selectorData); + setTimeout(function () { + assert.isUndefined(axe._tree); + assert.isUndefined(axe._selectorData); + done(); + }, 10); + }, isNotCalled); + }); + + it('should clear up axe._tree / axe._selectorData after an error', function (done) { + axe._load({ rules: [{ + id: 'invalidRule' + }], checks: [], messages: {}}); + + createFrames(function () { + setTimeout(function () { + runRules(document, {}, function () { + assert.ok(false, 'You shall not pass!'); + done(); + }, + function () { + assert.isDefined(axe._tree); + assert.isDefined(axe._selectorData); + setTimeout(function () { + assert.isUndefined(axe._tree); + assert.isUndefined(axe._selectorData); + done(); + }, 10); + }); + }, 100); + }); + }); }); diff --git a/test/core/utils/collect-results-from-frames.js b/test/core/utils/collect-results-from-frames.js index 66564a1575..d1ebc13cc9 100644 --- a/test/core/utils/collect-results-from-frames.js +++ b/test/core/utils/collect-results-from-frames.js @@ -5,6 +5,14 @@ describe('axe.utils.collectResultsFromFrames', function () { var fixture = document.getElementById('fixture'); var noop = function () {}; + function contextSetup (scope) { + var context = new Context(scope); + axe._tree = context.flatTree; + axe._selectorData = axe.utils.getSelectorData(context.flatTree); + + return context + } + afterEach(function () { fixture.innerHTML = ''; axe._tree = undefined; @@ -25,8 +33,7 @@ describe('axe.utils.collectResultsFromFrames', function () { var frame = document.createElement('iframe'); frame.addEventListener('load', function () { - var context = new Context(document); - axe._tree = axe.utils.getFlattenedTree(document.documentElement); + var context = contextSetup(document); axe.utils.collectResultsFromFrames(context, {}, 'stuff', 'morestuff', noop, function (err) { assert.instanceOf(err, Error); @@ -40,7 +47,6 @@ describe('axe.utils.collectResultsFromFrames', function () { frame.id = 'level0'; frame.src = '../mock/frames/results-timeout.html'; fixture.appendChild(frame); - }); it('should override the timeout with `options.frameWaitTime`, if provided', function (done) { @@ -57,9 +63,9 @@ describe('axe.utils.collectResultsFromFrames', function () { var frame = document.createElement('iframe'); frame.addEventListener('load', function () { - var context = new Context(document); + var context = contextSetup(document); var params = { frameWaitTime: 90000 }; - axe._tree = axe.utils.getFlattenedTree(document.documentElement); + axe.utils.collectResultsFromFrames(context, params, 'stuff', 'morestuff', noop, function (err) { assert.instanceOf(err, Error); @@ -73,7 +79,6 @@ describe('axe.utils.collectResultsFromFrames', function () { frame.id = 'level0'; frame.src = '../mock/frames/results-timeout.html'; fixture.appendChild(frame); - }); it('should not throw given a recursive iframe', function (done) { @@ -93,8 +98,8 @@ describe('axe.utils.collectResultsFromFrames', function () { var frame = document.createElement('iframe'); frame.addEventListener('load', function () { - var context = new Context(document); - axe._tree = axe.utils.getFlattenedTree(document.documentElement); + var context = contextSetup(document); + axe.utils.collectResultsFromFrames(context, {}, 'rules', 'morestuff', function () { done(); }, function (e) { @@ -106,14 +111,12 @@ describe('axe.utils.collectResultsFromFrames', function () { frame.id = 'level0'; frame.src = '../mock/frames/nested0.html'; fixture.appendChild(frame); - }); it('returns errors send from the frame', function (done) { var frame = document.createElement('iframe'); frame.addEventListener('load', function () { - var context = new Context(document); - axe._tree = axe.utils.getFlattenedTree(document.documentElement); + var context = contextSetup(document); axe.utils.collectResultsFromFrames(context, {}, 'command', 'params', noop, function (err) { assert.instanceOf(err, Error); diff --git a/test/core/utils/dq-element.js b/test/core/utils/dq-element.js index b36323c136..48ccb8caeb 100644 --- a/test/core/utils/dq-element.js +++ b/test/core/utils/dq-element.js @@ -3,6 +3,7 @@ describe('DqElement', function () { 'use strict'; var fixture = document.getElementById('fixture'); + var fixtureSetup = axe.testUtils.fixtureSetup; afterEach(function () { fixture.innerHTML = ''; @@ -33,7 +34,8 @@ describe('DqElement', function () { it('should not be present in stringified version', function () { var div = document.createElement('div'); - axe._tree = axe.utils.getFlattenedTree(document.documentElement); + fixtureSetup(); + var dqEl = new DqElement(div); assert.isUndefined(JSON.parse(JSON.stringify(dqEl)).element); @@ -141,8 +143,8 @@ describe('DqElement', function () { describe('absolutePaths', function () { it('creates a path all the way to root', function () { - fixture.innerHTML = '
Hello!
'; - axe._tree = axe.utils.getFlattenedTree(document.documentElement); + fixtureSetup('
Hello!
'); + var result = new DqElement(fixture.firstChild, { absolutePaths: true }); diff --git a/test/core/utils/get-selector.js b/test/core/utils/get-selector.js index 1a74bb4249..a94a001139 100644 --- a/test/core/utils/get-selector.js +++ b/test/core/utils/get-selector.js @@ -45,6 +45,7 @@ describe('axe.utils.getSelector', function () { var fixture = document.getElementById('fixture'); var shadowSupported = axe.testUtils.shadowSupport.v1; + var fixtureSetup = axe.testUtils.fixtureSetup; afterEach(function () { fixture.innerHTML = ''; @@ -56,11 +57,17 @@ describe('axe.utils.getSelector', function () { assert.isFunction(axe.utils.getSelector); }); + it('throws if axe._selectorData is undefined', function () { + assert.throws(function () { + var node = document.createElement('div'); + fixture.appendChild(node); + axe.utils.getSelector(node); + }); + }); + it('should generate a unique CSS selector', function () { var node = document.createElement('div'); - fixture.appendChild(node); - axe._tree = axe.utils.getFlattenedTree(document.documentElement); - + fixtureSetup(node); var sel = axe.utils.getSelector(node); var result = document.querySelectorAll(sel); @@ -71,9 +78,7 @@ describe('axe.utils.getSelector', function () { it('should still work if an element has nothing but whitespace as a className', function () { var node = document.createElement('div'); node.className = ' '; - fixture.appendChild(node); - axe._tree = axe.utils.getFlattenedTree(document.documentElement); - + fixtureSetup(node); var sel = axe.utils.getSelector(node); var result = document.querySelectorAll(sel); @@ -84,8 +89,7 @@ describe('axe.utils.getSelector', function () { it('should handle special characters in IDs', function () { var node = document.createElement('div'); node.id = 'monkeys#are.animals\\ok'; - fixture.appendChild(node); - axe._tree = axe.utils.getFlattenedTree(document.documentElement); + fixtureSetup(node); var result = document.querySelectorAll(axe.utils.getSelector(node)); assert.lengthOf(result, 1); @@ -95,8 +99,7 @@ describe('axe.utils.getSelector', function () { it('should handle special characters in classNames', function () { var node = document.createElement('div'); node.className = '. bb-required'; - fixture.appendChild(node); - axe._tree = axe.utils.getFlattenedTree(document.documentElement); + fixtureSetup(node); var result = document.querySelectorAll(axe.utils.getSelector(node)); assert.lengthOf(result, 1); @@ -105,15 +108,15 @@ describe('axe.utils.getSelector', function () { it('should be able to fall back to positional selectors', function () { var node, expected; + var nodes = [] for (var i = 0; i < 10; i++) { node = document.createElement('div'); - fixture.appendChild(node); + nodes.push(node); if (i === 5) { expected = node; } } - axe._tree = axe.utils.getFlattenedTree(document.documentElement); - + fixtureSetup(nodes); var result = document.querySelectorAll(axe.utils.getSelector(expected)); assert.lengthOf(result, 1); assert.equal(result[0], expected); @@ -122,8 +125,7 @@ describe('axe.utils.getSelector', function () { it('should use a unique ID', function () { var node = document.createElement('div'); node.id = 'monkeys'; - fixture.appendChild(node); - axe._tree = axe.utils.getFlattenedTree(document.documentElement); + fixtureSetup(node); var sel = axe.utils.getSelector(node); @@ -136,161 +138,133 @@ describe('axe.utils.getSelector', function () { }); it('should not use ids if they are not unique', function () { - var node = document.createElement('div'); - node.id = 'monkeys'; - fixture.appendChild(node); + var node1 = document.createElement('div'); + var node2 = document.createElement('div'); + node1.id = 'monkeys'; + node2.id = 'monkeys'; - node = document.createElement('div'); - node.id = 'monkeys'; - fixture.appendChild(node); - axe._tree = axe.utils.getFlattenedTree(document.documentElement); - - var sel = axe.utils.getSelector(node); + fixtureSetup([node1, node2]); + var sel = axe.utils.getSelector(node2); assert.notInclude(sel, '#monkeys'); var result = document.querySelectorAll(sel); assert.lengthOf(result, 1); - assert.equal(result[0], node); + assert.equal(result[0], node2); }); it('should use classes if available and unique', function () { - var node = document.createElement('div'); - node.className = 'monkeys simian'; - fixture.appendChild(node); - - node = document.createElement('div'); - node.className = 'dogs cats'; - fixture.appendChild(node); - axe._tree = axe.utils.getFlattenedTree(document.documentElement); + var node1 = document.createElement('div'); + var node2 = document.createElement('div'); + node1.className = 'monkeys simian'; + node2.className = 'dogs cats'; - var sel = axe.utils.getSelector(node); + fixtureSetup([node1, node2]); + var sel = axe.utils.getSelector(node2); assert.equal(sel, '.dogs'); var result = document.querySelectorAll(sel); assert.lengthOf(result, 1); - assert.equal(result[0], node); + assert.equal(result[0], node2); }); it('should use classes if more unique than the tag', function () { - var node = document.createElement('p'); - node.className = 'monkeys simian cats'; - fixture.appendChild(node); - - node = document.createElement('p'); - node.className = 'dogs cats'; - fixture.appendChild(node); - axe._tree = axe.utils.getFlattenedTree(document.documentElement); - - var sel = axe.utils.getSelector(node); + var node1 = document.createElement('p'); + var node2 = document.createElement('p'); + node1.className = 'monkeys simian cats'; + node2.className = 'dogs cats'; + fixtureSetup([node1, node2]); + var sel = axe.utils.getSelector(node2); assert.equal(sel, '.dogs'); var result = document.querySelectorAll(sel); assert.lengthOf(result, 1); - assert.equal(result[0], node); - + assert.equal(result[0], node2); }); it('should NOT use classes if they are more common than the tag', function () { - var node = document.createElement('p'); - node.className = 'dogs cats'; - fixture.appendChild(node); + var node1 = document.createElement('p'); + var node2 = document.createElement('p'); + node1.className = 'dogs cats'; + node2.className = 'dogs cats'; - node = document.createElement('p'); - node.className = 'dogs cats'; - fixture.appendChild(node); - axe._tree = axe.utils.getFlattenedTree(document.documentElement); - - var sel = axe.utils.getSelector(node); + fixtureSetup([node1, node2]); + var sel = axe.utils.getSelector(node2); assert.isTrue(sel.indexOf('.dogs') === -1); assert.isTrue(sel.indexOf('p') === 0); var result = document.querySelectorAll(sel); assert.lengthOf(result, 1); - assert.equal(result[0], node); - + assert.equal(result[0], node2); }); it('should use the most unique class', function () { - var node = document.createElement('div'); - node.className = 'dogs'; - fixture.appendChild(node); - - node = document.createElement('div'); - node.className = 'dogs cats'; - fixture.appendChild(node); - axe._tree = axe.utils.getFlattenedTree(document.documentElement); + var node1 = document.createElement('div'); + var node2 = document.createElement('div'); + node1.className = 'dogs'; + node2.className = 'dogs cats'; - var sel = axe.utils.getSelector(node); - - assert.equal(sel, '.cats'); + fixtureSetup([node1, node2]); + var sel = axe.utils.getSelector(node2); + assert.equal(sel, '.cats') var result = document.querySelectorAll(sel); assert.lengthOf(result, 1); - assert.equal(result[0], node); - + assert.equal(result[0], node2); }); it('should use the most unique class and not the unique attribute', function () { - var node = document.createElement('div'); - node.className = 'dogs'; - fixture.appendChild(node); + var node1 = document.createElement('div'); + var node2 = document.createElement('div'); - node = document.createElement('div'); - node.className = 'dogs cats'; - node.setAttribute('data-axe', 'hello'); - fixture.appendChild(node); - axe._tree = axe.utils.getFlattenedTree(document.documentElement); + node1.className = 'dogs'; + node2.className = 'dogs cats'; + node2.setAttribute('data-axe', 'hello'); - var sel = axe.utils.getSelector(node); + fixtureSetup([node1, node2]); + var sel = axe.utils.getSelector(node2); assert.equal(sel, '.cats'); var result = document.querySelectorAll(sel); assert.lengthOf(result, 1); - assert.equal(result[0], node); - + assert.equal(result[0], node2); }); it('should use only a single unique attribute', function () { - var node = document.createElement('div'); - node.setAttribute('data-thing', 'hello'); - fixture.appendChild(node); + var node1 = document.createElement('div'); + var node2 = document.createElement('div'); - node = document.createElement('div'); - node.setAttribute('data-axe', 'hello'); - node.setAttribute('data-thing', 'hello'); - fixture.appendChild(node); - axe._tree = axe.utils.getFlattenedTree(document.documentElement); + node1.setAttribute('data-thing', 'hello'); + node2.setAttribute('data-thing', 'hello'); + node2.setAttribute('data-axe', 'hello'); - var sel = axe.utils.getSelector(node); + fixtureSetup([node1, node2]); + var sel = axe.utils.getSelector(node2); assert.equal(sel, 'div[data-axe="hello"]'); var result = document.querySelectorAll(sel); assert.lengthOf(result, 1); - assert.equal(result[0], node); - + assert.equal(result[0], node2); }); it('should use three uncommon but not unique features', function () { - var node = document.createElement('div'); - node.setAttribute('data-axe', 'hello'); - node.setAttribute('data-thing', 'hello'); - node.className = 'thing'; - fixture.appendChild(node); - - node = document.createElement('div'); - node.setAttribute('data-axe', 'hello'); - node.setAttribute('data-thing', 'hello'); - node.className = 'thing'; - fixture.appendChild(node); - axe._tree = axe.utils.getFlattenedTree(document.documentElement); - - var sel = axe.utils.getSelector(node); + var node1 = document.createElement('div'); + node1.setAttribute('data-axe', 'hello'); + node1.setAttribute('data-thing', 'hello'); + node1.className = 'thing'; + + var node2 = document.createElement('div'); + node2.setAttribute('data-axe', 'hello'); + node2.setAttribute('data-thing', 'hello'); + node2.className = 'thing'; + + fixtureSetup([node1, node2]); + var sel = axe.utils.getSelector(node2); var clsIndex = sel.indexOf('.thing'); var attIndex = Math.min(sel.indexOf('[data-axe="hello"]'), sel.indexOf('[data-thing="hello"]')); @@ -303,27 +277,24 @@ describe('axe.utils.getSelector', function () { var result = document.querySelectorAll(sel); assert.lengthOf(result, 1); - assert.equal(result[0], node); - + assert.equal(result[0], node2); }); it('should use only three uncommon but not unique features', function () { - var node = document.createElement('div'); - node.setAttribute('data-axe', 'hello'); - node.setAttribute('data-thing', 'hello'); - node.setAttribute('data-thang', 'hello'); - node.className = 'thing thang'; - fixture.appendChild(node); - - node = document.createElement('div'); - node.setAttribute('data-axe', 'hello'); - node.setAttribute('data-thing', 'hello'); - node.setAttribute('data-thang', 'hello'); - node.className = 'thing thang'; - fixture.appendChild(node); - axe._tree = axe.utils.getFlattenedTree(document.documentElement); - - var sel = axe.utils.getSelector(node); + var node1 = document.createElement('div'); + node1.setAttribute('data-axe', 'hello'); + node1.setAttribute('data-thing', 'hello'); + node1.setAttribute('data-thang', 'hello'); + node1.className = 'thing thang'; + + var node2 = document.createElement('div'); + node2.setAttribute('data-axe', 'hello'); + node2.setAttribute('data-thing', 'hello'); + node2.setAttribute('data-thang', 'hello'); + node2.className = 'thing thang'; + + fixtureSetup([node1, node2]); + var sel = axe.utils.getSelector(node2); var parts = sel.split('.'); parts = parts.reduce(function (val, item) { var its = item.split('['); @@ -335,20 +306,17 @@ describe('axe.utils.getSelector', function () { var result = document.querySelectorAll(sel); assert.lengthOf(result, 1); - assert.equal(result[0], node); + assert.equal(result[0], node2); }); it('should use only three uncommon but not unique classes', function () { - var node = document.createElement('div'); - node.className = 'thing thang thug thick'; - fixture.appendChild(node); + var node1 = document.createElement('div'); + var node2 = document.createElement('div'); + node1.className = 'thing thang thug thick'; + node2.className = 'thing thang thug thick'; - node = document.createElement('div'); - node.className = 'thing thang thug thick'; - fixture.appendChild(node); - axe._tree = axe.utils.getFlattenedTree(document.documentElement); - - var sel = axe.utils.getSelector(node); + fixtureSetup([node1, node2]); + var sel = axe.utils.getSelector(node2); var parts = sel.split('.'); parts = parts.reduce(function (val, item) { var its = item.split('['); @@ -360,26 +328,24 @@ describe('axe.utils.getSelector', function () { var result = document.querySelectorAll(sel); assert.lengthOf(result, 1); - assert.equal(result[0], node); + assert.equal(result[0], node2); }); it('should use only three uncommon but not unique attributes', function () { - var node = document.createElement('div'); - node.setAttribute('data-axe', 'hello'); - node.setAttribute('data-thug', 'hello'); - node.setAttribute('data-thing', 'hello'); - node.setAttribute('data-thang', 'hello'); - fixture.appendChild(node); - - node = document.createElement('div'); - node.setAttribute('data-axe', 'hello'); - node.setAttribute('data-thing', 'hello'); - node.setAttribute('data-thang', 'hello'); - node.setAttribute('data-thug', 'hello'); - fixture.appendChild(node); - axe._tree = axe.utils.getFlattenedTree(document.documentElement); - - var sel = axe.utils.getSelector(node); + var node1 = document.createElement('div'); + node1.setAttribute('data-axe', 'hello'); + node1.setAttribute('data-thug', 'hello'); + node1.setAttribute('data-thing', 'hello'); + node1.setAttribute('data-thang', 'hello'); + + var node2 = document.createElement('div'); + node2.setAttribute('data-axe', 'hello'); + node2.setAttribute('data-thing', 'hello'); + node2.setAttribute('data-thang', 'hello'); + node2.setAttribute('data-thug', 'hello'); + + fixtureSetup([node1, node2]); + var sel = axe.utils.getSelector(node2); var parts = sel.split('.'); parts = parts.reduce(function (val, item) { var its = item.split('['); @@ -391,50 +357,45 @@ describe('axe.utils.getSelector', function () { var result = document.querySelectorAll(sel); assert.lengthOf(result, 1); - assert.equal(result[0], node); + assert.equal(result[0], node2); }); it('should not use long attributes', function () { var node = makeNonuniqueLongAttributes(fixture); - axe._tree = axe.utils.getFlattenedTree(document.documentElement); - + fixtureSetup(); var sel = axe.utils.getSelector(node, {}); assert.isTrue(sel.indexOf('data-att') === -1); }); it('should use :root when not unique html element', function () { - // todo var node = document.createElement('html'); node.setAttribute('lang', 'en'); - fixture.appendChild(node); - axe._tree = axe.utils.getFlattenedTree(document.documentElement); + fixtureSetup(node); var sel = axe.utils.getSelector(document.documentElement, {}); assert.equal(sel, ':root'); }); it('should use position if classes are not unique', function () { - var node = document.createElement('div'); - node.className = 'monkeys simian'; - fixture.appendChild(node); + var node1 = document.createElement('div'); + node1.className = 'monkeys simian'; - node = document.createElement('div'); - node.className = 'monkeys simian'; - fixture.appendChild(node); - axe._tree = axe.utils.getFlattenedTree(document.documentElement); + var node2 = document.createElement('div'); + node2.className = 'monkeys simian'; - var sel = axe.utils.getSelector(node); + fixtureSetup([node1, node2]); + var sel = axe.utils.getSelector(node2); assert.equal(sel, '.monkeys.simian:nth-child(2)'); var result = document.querySelectorAll(sel); assert.lengthOf(result, 1); - assert.equal(result[0], node); + assert.equal(result[0], node2); }); it('should work on the documentElement', function () { - axe._tree = axe.utils.getFlattenedTree(document.documentElement); + fixtureSetup(); var sel = axe.utils.getSelector(document.documentElement); var result = document.querySelectorAll(sel); @@ -445,7 +406,7 @@ describe('axe.utils.getSelector', function () { it('should work on the documentElement with classes', function () { var orig = document.documentElement.className; document.documentElement.className = 'stuff and other things'; - axe._tree = axe.utils.getFlattenedTree(document.documentElement); + fixtureSetup(); var sel = axe.utils.getSelector(document.documentElement); var result = document.querySelectorAll(sel); @@ -455,18 +416,17 @@ describe('axe.utils.getSelector', function () { }); it('should work on the body', function () { - axe._tree = axe.utils.getFlattenedTree(document.documentElement); - + fixtureSetup(); var sel = axe.utils.getSelector(document.body); var result = document.querySelectorAll(sel); + assert.lengthOf(result, 1); assert.equal(result[0], document.body); }); it('should work on namespaced elements', function () { - fixture.innerHTML = 'Hello'; + fixtureSetup('Hello'); var node = fixture.firstChild; - axe._tree = axe.utils.getFlattenedTree(document.documentElement); var sel = axe.utils.getSelector(node); var result = document.querySelectorAll(sel); @@ -475,13 +435,12 @@ describe('axe.utils.getSelector', function () { }); it('should work on complex namespaced elements', function () { - fixture.innerHTML = '' + + fixtureSetup('' + 'x' + '' + 'x' + '' + - ''; - axe._tree = axe.utils.getFlattenedTree(document.documentElement); + ''); var node = fixture.querySelector('m\\:ci'); var sel = axe.utils.getSelector(node); @@ -504,8 +463,7 @@ describe('axe.utils.getSelector', function () { ignoredAttributes.forEach(function (att) { node.setAttribute(att, 'true'); }); - fixture.appendChild(node); - axe._tree = axe.utils.getFlattenedTree(document.documentElement); + fixtureSetup(node); assert.isTrue( axe.utils.getSelector(node).indexOf('[') === -1 @@ -513,28 +471,24 @@ describe('axe.utils.getSelector', function () { }); it('should use href and src attributes, shortened', function () { - var link = document.createElement('a'); - link.setAttribute('href', '//deque.com/thang/'); - fixture.appendChild(link); - link = document.createElement('a'); - link.setAttribute('href', '//deque.com/about/'); - fixture.appendChild(link); - - var img = document.createElement('img'); - img.setAttribute('src', '//deque.com/thang.png'); - fixture.appendChild(img); - img = document.createElement('img'); - img.setAttribute('src', '//deque.com/logo.png'); - fixture.appendChild(img); - - axe._tree = axe.utils.getFlattenedTree(document.documentElement); + var link1 = document.createElement('a'); + link1.setAttribute('href', '//deque.com/thang/'); + + var link2 = document.createElement('a'); + link2.setAttribute('href', '//deque.com/about/'); + + var img1 = document.createElement('img'); + img1.setAttribute('src', '//deque.com/thang.png'); + var img2 = document.createElement('img'); + img2.setAttribute('src', '//deque.com/logo.png'); + fixtureSetup([ link1, link2, img1, img2 ]); assert.equal( - axe.utils.getSelector(link), + axe.utils.getSelector(link2), 'a[href$="about/"]' ); assert.equal( - axe.utils.getSelector(img), + axe.utils.getSelector(img2), 'img[src$="logo.png"]' ); }); @@ -542,8 +496,7 @@ describe('axe.utils.getSelector', function () { it('should not generate universal selectors', function () { var node = document.createElement('div'); node.setAttribute('role', 'menuitem'); - fixture.appendChild(node); - axe._tree = axe.utils.getFlattenedTree(document.documentElement); + fixtureSetup(node); assert.equal( axe.utils.getSelector(node), @@ -552,53 +505,55 @@ describe('axe.utils.getSelector', function () { }); it('should work correctly when a URL attribute cannot be shortened', function () { - var href = 'mars2.html?a=be_bold'; - var node = document.createElement('a'); - node.setAttribute('href', href); - fixture.appendChild(node); - axe._tree = axe.utils.getFlattenedTree(document.documentElement); + var href1 = 'mars2.html?a=be_bold'; + var node1 = document.createElement('a'); + node1.setAttribute('href', href1); - assert.include(axe.utils.getSelector(node), href); + var href2 = 'mars2.html?a=be_italic'; + var node2 = document.createElement('a'); + node2.setAttribute('href', href2); + fixtureSetup([node1, node2]); + + assert.include(axe.utils.getSelector(node1), href1); + assert.include(axe.utils.getSelector(node2), href2); }); - it('no options: should work with shadow DOM', function () { + // shadow DOM v1 - note: v0 is compatible with this code, so no need + // to specifically test this + (shadowSupported ? it : xit) + ('no options: should work with shadow DOM', function () { var shadEl; + fixture.innerHTML = '
'; + makeShadowTreeGetSelector(fixture.firstChild); + fixtureSetup(); - if (shadowSupported) { - // shadow DOM v1 - note: v0 is compatible with this code, so no need - // to specifically test this - fixture.innerHTML = '
'; - makeShadowTreeGetSelector(fixture.firstChild); - axe._tree = axe.utils.getFlattenedTree(document.documentElement); - - shadEl = fixture.firstChild.shadowRoot.querySelector('input#myinput'); - assert.deepEqual(axe.utils.getSelector(shadEl), [ - '#fixture > div', - '#myinput' - ]); - } + shadEl = fixture.firstChild.shadowRoot.querySelector('input#myinput'); + assert.deepEqual(axe.utils.getSelector(shadEl), [ + '#fixture > div', + '#myinput' + ]); }); - it('toRoot: should work with shadow DOM', function () { + + // shadow DOM v1 - note: v0 is compatible with this code, so no need + // to specifically test this + (shadowSupported ? it : xit) + ('toRoot: should work with shadow DOM', function () { var shadEl; + fixture.innerHTML = '
'; + makeShadowTreeGetSelector(fixture.firstChild); + axe._tree = axe.utils.getFlattenedTree(document); + axe._selectorData = axe.utils.getSelectorData(axe._tree); - if (shadowSupported) { - // shadow DOM v1 - note: v0 is compatible with this code, so no need - // to specifically test this - fixture.innerHTML = '
'; - makeShadowTreeGetSelector(fixture.firstChild); - axe._tree = axe.utils.getFlattenedTree(document.documentElement); - - shadEl = fixture.firstChild.shadowRoot.querySelector('input#myinput'); - assert.deepEqual(axe.utils.getSelector(shadEl, { toRoot: true }), [ - 'html > body > #fixture > div', - '.parent > div > #myinput' - ]); - } + shadEl = fixture.firstChild.shadowRoot.querySelector('input#myinput'); + assert.deepEqual(axe.utils.getSelector(shadEl, { toRoot: true }), [ + 'html > body > #fixture > div', + '.parent > div > #myinput' + ]); }); it('should correctly calculate unique selector when no discernable features', function () { var node = makeNonunique(fixture); - axe._tree = axe.utils.getFlattenedTree(document.documentElement); + fixtureSetup(); var sel = axe.utils.getSelector(node, {}); var mine = document.querySelector(sel); @@ -607,7 +562,7 @@ describe('axe.utils.getSelector', function () { it('should not traverse further up than required when no discernable features', function () { var node = makeNonunique(fixture); - axe._tree = axe.utils.getFlattenedTree(document.documentElement); + fixtureSetup(); var top = fixture.querySelector('div:nth-child(4)'); var sel = axe.utils.getSelector(node, {}); diff --git a/test/integration/full/context/context.js b/test/integration/full/context/context.js index 09464702d8..23f02231ee 100644 --- a/test/integration/full/context/context.js +++ b/test/integration/full/context/context.js @@ -109,12 +109,14 @@ describe('context test', function () { (shadowSupported ? it : xit) ('should find no nodes in Shadow DOM', function (done) { - var sConfig = { runOnly: { type: 'rule', values: ['list', 'color-contrast'] } }; + var sConfig = { runOnly: { type: 'rule', values: ['color-contrast'] } }; axe.run({ include: [['#shadow-container']], exclude: [['#shadow-host']] }, sConfig, function (err, results) { - assert.isNull(err); - assert.lengthOf(results.violations, 0, 'violations'); - assert.lengthOf(results.passes, 2, 'passes'); - done(); + try { + assert.isNull(err); + assert.lengthOf(results.violations, 0, 'violations'); + assert.lengthOf(results.passes, 1, 'passes'); + done(); + } catch (e) { done(e); } }); }); diff --git a/test/testutils.js b/test/testutils.js index 91feaad87d..462e1d2f1a 100644 --- a/test/testutils.js +++ b/test/testutils.js @@ -68,15 +68,25 @@ testUtils.shadowSupport = (function(document) { testUtils.fixtureSetup = function (content) { 'use strict'; var fixture = document.querySelector('#fixture'); - fixture.innerHTML = ''; + if (typeof content !== 'undefined') { + fixture.innerHTML = ''; + } + if (typeof content === 'string') { fixture.innerHTML = content; } else if (content instanceof Node) { fixture.appendChild(content); + } else if (Array.isArray(content)) { + content.forEach(function (node) { + fixture.appendChild(node); + }); } axe._tree = axe.utils.getFlattenedTree(fixture); + axe._selectorData = axe.utils.getSelectorData(axe._tree); + return fixture; }; + /** * Create check arguments *