diff --git a/lib/core/base/check.js b/lib/core/base/check.js index 6646d9eac2..593c9b521e 100644 --- a/lib/core/base/check.js +++ b/lib/core/base/check.js @@ -108,7 +108,7 @@ Check.prototype.run = function run(node, options, context, resolve, reject) { // possible reference error. if (node && node.actualNode) { // Save a reference to the node we errored on for futher debugging. - e.errorNode = new DqElement(node.actualNode).toJSON(); + e.errorNode = new DqElement(node).toJSON(); } reject(e); return; @@ -162,7 +162,7 @@ Check.prototype.runSync = function runSync(node, options, context) { // possible reference error. if (node && node.actualNode) { // Save a reference to the node we errored on for futher debugging. - e.errorNode = new DqElement(node.actualNode).toJSON(); + e.errorNode = new DqElement(node).toJSON(); } throw e; } diff --git a/lib/core/base/rule.js b/lib/core/base/rule.js index ad6bcdc79b..12788d4c1d 100644 --- a/lib/core/base/rule.js +++ b/lib/core/base/rule.js @@ -252,7 +252,7 @@ Rule.prototype.run = function run(context, options = {}, resolve, reject) { .then(results => { const result = getResult(results); if (result) { - result.node = new DqElement(node.actualNode, options); + result.node = new DqElement(node, options); ruleResult.nodes.push(result); // mark rule as incomplete rather than failure for rules with reviewOnFail @@ -322,7 +322,7 @@ Rule.prototype.runSync = function runSync(context, options = {}) { const result = getResult(results); if (result) { result.node = node.actualNode - ? new DqElement(node.actualNode, options) + ? new DqElement(node, options) : null; ruleResult.nodes.push(result); diff --git a/lib/core/utils/dq-element.js b/lib/core/utils/dq-element.js index 8953798a9f..898df34126 100644 --- a/lib/core/utils/dq-element.js +++ b/lib/core/utils/dq-element.js @@ -1,6 +1,8 @@ import getSelector from './get-selector'; import getAncestry from './get-ancestry'; import getXpath from './get-xpath'; +import getNodeFromTree from './get-node-from-tree'; +import AbstractVirtualNode from '../base/virtual-node/abstract-virtual-node'; function truncate(str, maxLength) { maxLength = maxLength || 300; @@ -27,33 +29,40 @@ function getSource(element) { * @param {HTMLElement} element The element to serialize * @param {Object} spec Properties to use in place of the element when instantiated on Elements from other frames */ -function DqElement(element, options, spec) { - this._fromFrame = !!spec; +function DqElement(elm, options = {}, spec = {}) { + if (elm instanceof AbstractVirtualNode) { + this._virtualNode = elm; + this._element = elm.actualNode; + } else { + this._element = elm; + this._virtualNode = getNodeFromTree(elm); + } - this.spec = spec || {}; - if (options && options.absolutePaths) { + this.spec = spec; + if (options.absolutePaths) { this._options = { toRoot: true }; } /** - * The generated HTML source code of the element - * @type {String} + * Number by which nodes in the flat tree can be sorted + * @type {Number} */ - // TODO: es-modules_audit - if (axe._audit.noHtml) { - this.source = null; - } else if (this.spec.source !== undefined) { - this.source = this.spec.source; - } else { - this.source = getSource(element); + this.nodeIndexes = []; + if (Array.isArray(spec.nodeIndexes)) { + this.nodeIndexes = spec.nodeIndexes + } else if (this._virtualNode?.nodeIndex) { + this.nodeIndexes = [this._virtualNode?.nodeIndex] } /** - * The element which this object is based off or the containing frame, used for sorting. - * Excluded in toJSON method. - * @type {HTMLElement} + * The generated HTML source code of the element + * @type {String|null} */ - this._element = element; + this.source = null; + // TODO: es-modules_audit + if (!axe._audit.noHtml) { + this.source = this.spec.source ?? getSource(this._element); + } } DqElement.prototype = { @@ -97,7 +106,8 @@ DqElement.prototype = { selector: this.selector, source: this.source, xpath: this.xpath, - ancestry: this.ancestry + ancestry: this.ancestry, + nodeIndexes: this.nodeIndexes }; } }; @@ -107,7 +117,8 @@ DqElement.fromFrame = function fromFrame(node, options, frame) { ...node, selector: [...frame.selector, ...node.selector], ancestry: [...frame.ancestry, ...node.ancestry], - xpath: [...frame.xpath, ...node.xpath] + xpath: [...frame.xpath, ...node.xpath], + nodeIndexes: [...frame.nodeIndexes, ...node.nodeIndexes], }; return new DqElement(frame.element, options, spec); }; diff --git a/test/core/public/run-rules.js b/test/core/public/run-rules.js index ebbd3574ff..be7700f769 100644 --- a/test/core/public/run-rules.js +++ b/test/core/public/run-rules.js @@ -229,7 +229,8 @@ describe('runRules', function() { "/iframe[@id='context-test']", "/div[@id='target']" ], - source: '
' + source: '', + nodeIndexes: [12, 14] }, any: [ { @@ -271,7 +272,8 @@ describe('runRules', function() { "/div[@id='foo']" ], source: - '