From 46f6628cc710b9aaa8b872790a0b8c42032a8134 Mon Sep 17 00:00:00 2001 From: Steven Lambert <2433219+straker@users.noreply.github.com> Date: Thu, 22 Oct 2020 10:26:12 -0600 Subject: [PATCH] fix(heading-order): evaluate headings from iframes in DOM order (#2572) * fix(heading-order): evaluate headings from iframes in DOM order * fix selector * Update lib/core/utils/merge-results.js Co-authored-by: Wilco Fiers * Update test/integration/rules/heading-order/heading-order.json Co-authored-by: Wilco Fiers * fix comment Co-authored-by: Wilco Fiers --- lib/core/utils/merge-results.js | 22 ++++++ test/core/utils/merge-results.js | 78 +++++++++++++++++++ .../full/identical-links-same-purpose/page.js | 5 +- .../rules/heading-order/frame.html | 12 +++ .../rules/heading-order/heading-order.html | 5 ++ .../rules/heading-order/heading-order.json | 7 +- .../landmark-unique/landmark-unique-fail.json | 4 +- 7 files changed, 128 insertions(+), 5 deletions(-) create mode 100644 test/integration/rules/heading-order/frame.html 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 @@ + + + + + landmark-unique test + + + +

Header

+

header

+ + diff --git a/test/integration/rules/heading-order/heading-order.html b/test/integration/rules/heading-order/heading-order.html index 034f17ef23..876da78f5e 100644 --- a/test/integration/rules/heading-order/heading-order.html +++ b/test/integration/rules/heading-order/heading-order.html @@ -7,3 +7,8 @@

Header

Header

Ignore
+ +

Header

+

Header

+ +

Header

diff --git a/test/integration/rules/heading-order/heading-order.json b/test/integration/rules/heading-order/heading-order.json index befdf53952..7abbd2424c 100644 --- a/test/integration/rules/heading-order/heading-order.json +++ b/test/integration/rules/heading-order/heading-order.json @@ -8,6 +8,11 @@ ["#heading3"], ["#heading4"], ["#heading5"], - ["#heading7"] + ["#heading7"], + ["#heading8"], + ["#heading9"], + ["iframe", "#frame-heading1"], + ["iframe", "#frame-heading2"], + ["#heading10"] ] } diff --git a/test/integration/rules/landmark-unique/landmark-unique-fail.json b/test/integration/rules/landmark-unique/landmark-unique-fail.json index 6039a02ec3..ba21f3e017 100644 --- a/test/integration/rules/landmark-unique/landmark-unique-fail.json +++ b/test/integration/rules/landmark-unique/landmark-unique-fail.json @@ -9,8 +9,8 @@ ["#violation-aside-aria-label-1"], ["#violation-aside-aria-labelledby-1"], ["#violation-footer-1"], - ["#violation-form-through-iframe-1"], - ["#violation-nav-through-iframe-1"], + ["#frame", "div[role=\"form\"]"], + ["#frame", "div[role=\"navigation\"]"], ["#violation-role-banner"], ["#violation-role-complementary"], ["#violation-role-contentinfo"],