diff --git a/lib/core/utils/merge-results.js b/lib/core/utils/merge-results.js index b72ecba818..8f34c3dbf2 100644 --- a/lib/core/utils/merge-results.js +++ b/lib/core/utils/merge-results.js @@ -104,6 +104,28 @@ function mergeResults(frameResults, options) { } }); }); + + // Only sort results if we have the ability to run + // document position (such as serial node context) and if + // we have more than 1 result + if (frameResults.length > 1 && window && window.Node) { + mergedResult.forEach(result => { + if (result.nodes) { + result.nodes.sort((a, b) => { + const aNode = a.node.element; + const bNode = b.node.element; + + // only sort if the nodes are from different frames + if (aNode !== bNode && (a.node._fromFrame || b.node._fromFrame)) { + return nodeSorter(aNode, bNode); + } + + return 0; + }); + } + }); + } + return mergedResult; } diff --git a/test/core/utils/merge-results.js b/test/core/utils/merge-results.js index c216845658..0583a6bdaa 100644 --- a/test/core/utils/merge-results.js +++ b/test/core/utils/merge-results.js @@ -43,4 +43,82 @@ describe('axe.utils.mergeResults', function() { assert.deepEqual(node.xpath, ['/iframe', 'html/#foo']); assert.deepEqual(node.ancestry, ['iframe', 'html > div']); }); + + it('sorts results from iframes into their correct DOM position', function() { + var iframe1 = document.createElement('iframe'); + iframe1.id = 'iframe1'; + var iframe2 = document.createElement('iframe'); + iframe2.id = 'iframe2'; + var h1 = document.createElement('h1'); + var h4 = document.createElement('h4'); + var fixture = document.querySelector('#fixture'); + + fixture.appendChild(h1); + fixture.appendChild(iframe1); + fixture.appendChild(iframe2); + fixture.appendChild(h4); + + var result = axe.utils.mergeResults([ + { + results: [ + { + id: 'a', + result: 'a', + nodes: [ + { + node: { + selector: ['h1'], + element: h1 + } + } + ] + }, + { + id: 'a', + result: 'd', + nodes: [ + { + node: { + selector: ['h4'], + element: h4 + } + } + ] + }, + { + id: 'a', + result: 'b', + nodes: [ + { + node: { + selector: ['iframe1'], + element: iframe1, + _fromFrame: true + } + } + ] + }, + { + id: 'a', + result: 'c', + nodes: [ + { + node: { + selector: ['iframe2'], + element: iframe2, + _fromFrame: true + } + } + ] + } + ] + } + ]); + + var ids = result[0].nodes.map(function(el) { + return el.node.selector; + }); + + assert.deepEqual(ids, [['h1'], ['iframe1'], ['iframe2'], ['h4']]); + }); }); diff --git a/test/integration/full/identical-links-same-purpose/page.js b/test/integration/full/identical-links-same-purpose/page.js index bb6b66c7a3..37dc8802df 100644 --- a/test/integration/full/identical-links-same-purpose/page.js +++ b/test/integration/full/identical-links-same-purpose/page.js @@ -36,11 +36,12 @@ describe('identical-links-same-purpose test', function() { assert.lengthOf(results.incomplete, 1, 'incomplete'); assert.lengthOf(results.incomplete[0].nodes, 1); assert.deepEqual(results.incomplete[0].nodes[0].target, [ - '#incomplete-outside-frame' + '#myframe', + '#incomplete-inside-frame' ]); assert.deepEqual( results.incomplete[0].nodes[0].all[0].relatedNodes[0].target, - ['#myframe', '#incomplete-inside-frame'] + ['#incomplete-outside-frame'] ); done(); diff --git a/test/integration/rules/heading-order/frame.html b/test/integration/rules/heading-order/frame.html new file mode 100644 index 0000000000..a28abfdeb7 --- /dev/null +++ b/test/integration/rules/heading-order/frame.html @@ -0,0 +1,12 @@ + + +
+ +