Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/core/utils/get-xpath.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ function getXPathArray(node, path) {
function xpathToString(xpathArray) {
return xpathArray.reduce((str, elm) => {
if (elm.id) {
return `/${elm.str}[@id='${elm.id}']`;
return `//${elm.str}[@id='${elm.id}']`;
} else {
return str + `/${elm.str}` + (elm.count > 0 ? `[${elm.count}]` : '');
}
Expand Down
18 changes: 9 additions & 9 deletions test/core/public/run-rules.js
Original file line number Diff line number Diff line change
Expand Up @@ -221,8 +221,8 @@ describe('runRules', function () {
'html > body > div:nth-child(2)'
],
xpath: [
"/iframe[@id='context-test']",
"/div[@id='target']"
"//iframe[@id='context-test']",
"//div[@id='target']"
],
source: '<div id="target"></div>',
nodeIndexes: [12, 14],
Expand Down Expand Up @@ -264,8 +264,8 @@ describe('runRules', function () {
'html > body > div:nth-child(1)'
],
xpath: [
"/iframe[@id='context-test']",
"/div[@id='foo']"
"//iframe[@id='context-test']",
"//div[@id='foo']"
],
source:
'<div id="foo">\n <div id="bar"></div>\n </div>',
Expand All @@ -284,8 +284,8 @@ describe('runRules', function () {
'html > body > div:nth-child(1)'
],
xpath: [
"/iframe[@id='context-test']",
"/div[@id='foo']"
"//iframe[@id='context-test']",
"//div[@id='foo']"
],
source:
'<div id="foo">\n <div id="bar"></div>\n </div>',
Expand Down Expand Up @@ -536,7 +536,7 @@ describe('runRules', function () {
ancestry: [
'html > body > div:nth-child(1) > div:nth-child(1)'
],
xpath: ["/div[@id='target']"],
xpath: ["//div[@id='target']"],
source: '<div id="target">Target!</div>',
nodeIndexes: [12],
fromFrame: false
Expand Down Expand Up @@ -578,7 +578,7 @@ describe('runRules', function () {
impact: null,
node: {
selector: ['#target'],
xpath: ["/div[@id='target']"],
xpath: ["//div[@id='target']"],
ancestry: [
'html > body > div:nth-child(1) > div:nth-child(1)'
],
Expand All @@ -599,7 +599,7 @@ describe('runRules', function () {
ancestry: [
'html > body > div:nth-child(1) > div:nth-child(1)'
],
xpath: ["/div[@id='target']"],
xpath: ["//div[@id='target']"],
source: '<div id="target">Target!</div>',
nodeIndexes: [12],
fromFrame: false
Expand Down
31 changes: 16 additions & 15 deletions test/core/public/run.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ describe('axe.run', function () {
var fixture = document.getElementById('fixture');
var noop = function () {};
var origRunRules = axe._runRules;
var captureError = axe.testUtils.captureError;

beforeEach(function () {
axe._load({
Expand Down Expand Up @@ -347,12 +348,12 @@ describe('axe.run', function () {
{
xpath: true
},
function (err, result) {
captureError(function (err, result) {
assert.deepEqual(result.violations[0].nodes[0].xpath, [
"/div[@id='fixture']"
"//div[@id='fixture']"
]);
done();
}
}, done)
);
});

Expand All @@ -362,13 +363,13 @@ describe('axe.run', function () {
{
xpath: true
},
function (err, result) {
captureError(function (err, result) {
assert.deepEqual(
result.violations[0].nodes[0].none[0].relatedNodes[0].xpath,
["/div[@id='fixture']"]
["//div[@id='fixture']"]
);
done();
}
}, done)
);
});

Expand All @@ -379,12 +380,12 @@ describe('axe.run', function () {
xpath: true,
reporter: 'no-passes'
},
function (err, result) {
captureError(function (err, result) {
assert.deepEqual(result.violations[0].nodes[0].xpath, [
"/div[@id='fixture']"
"//div[@id='fixture']"
]);
done();
}
}, done)
);
});
});
Expand All @@ -396,10 +397,10 @@ describe('axe.run', function () {
{
absolutePaths: 0
},
function (err, result) {
captureError(function (err, result) {
assert.deepEqual(result.violations[0].nodes[0].target, ['#fixture']);
done();
}
}, done)
);
});

Expand All @@ -409,12 +410,12 @@ describe('axe.run', function () {
{
absolutePaths: 'yes please'
},
function (err, result) {
captureError(function (err, result) {
assert.deepEqual(result.violations[0].nodes[0].target, [
'html > body > #fixture'
]);
done();
}
}, done)
);
});

Expand All @@ -424,13 +425,13 @@ describe('axe.run', function () {
{
absolutePaths: true
},
function (err, result) {
captureError(function (err, result) {
assert.deepEqual(
result.violations[0].nodes[0].none[0].relatedNodes[0].target,
['html > body > #fixture']
);
done();
}
}, done)
);
});
});
Expand Down
101 changes: 68 additions & 33 deletions test/core/utils/get-xpath.js
Original file line number Diff line number Diff line change
@@ -1,60 +1,91 @@
describe('axe.utils.getXpath', function () {
describe('axe.utils.getXpath', () => {
'use strict';

var fixture = document.getElementById('fixture');
const fixture = document.getElementById('fixture');

afterEach(function () {
fixture.innerHTML = '';
});
// @see https://stackoverflow.com/a/14284815/2124254
function getElementByXPath(path) {
return document.evaluate(
path,
document,
() => 'http://www.w3.org/1998/Math/MathML',
XPathResult.FIRST_ORDERED_NODE_TYPE,
null
).singleNodeValue;
}

it('should be a function', function () {
it('should be a function', () => {
assert.isFunction(axe.utils.getXpath);
});

it('should generate an XPath selector', function () {
var node = document.createElement('div');
it('should generate an XPath selector', () => {
const node = document.createElement('div');
fixture.appendChild(node);

var sel = axe.utils.getXpath(node);
const sel = axe.utils.getXpath(node);

assert.equal(sel, "/div[@id='fixture']/div");
assert.equal(sel, "//div[@id='fixture']/div");
assert.equal(node, getElementByXPath(sel));
});

it('should handle special characters', function () {
var node = document.createElement('div');
it('should handle special characters', () => {
const node = document.createElement('div');
node.id = 'monkeys#are.animals\\ok';
fixture.appendChild(node);
assert.equal(
axe.utils.getXpath(node),
"/div[@id='monkeys#are.animals\\ok']"
);

const sel = axe.utils.getXpath(node);

assert.equal(sel, "//div[@id='monkeys#are.animals\\ok']");

assert.equal(node, getElementByXPath(sel));
});

it('should stop on unique ID', function () {
var node = document.createElement('div');
it('should stop on unique ID', () => {
const node = document.createElement('div');
node.id = 'monkeys';
fixture.appendChild(node);

var sel = axe.utils.getXpath(node);
assert.equal(sel, "/div[@id='monkeys']");
const sel = axe.utils.getXpath(node);
assert.equal(sel, "//div[@id='monkeys']");
assert.equal(node, getElementByXPath(sel));
});

it('should use the nearest unique ID', () => {
fixture.innerHTML = `
<div id="dogs">
<div>
<div>
<div id="monkeys">
<div></div>
</div>
</div>
</div>
</div>
`;
const node = fixture.querySelector('#monkeys > div');

const sel = axe.utils.getXpath(node);
assert.equal(sel, "//div[@id='monkeys']/div");
assert.equal(node, getElementByXPath(sel));
});

it('should not use ids if they are not unique', function () {
var node = document.createElement('div');
it('should not use ids if they are not unique', () => {
let node = document.createElement('div');
node.id = 'monkeys';
fixture.appendChild(node);

node = document.createElement('div');
node.id = 'monkeys';
fixture.appendChild(node);

var sel = axe.utils.getXpath(node);
const sel = axe.utils.getXpath(node);

assert.equal(sel, "/div[@id='fixture']/div[2]");
assert.equal(sel, "//div[@id='fixture']/div[2]");
assert.equal(node, getElementByXPath(sel));
});

it('should properly calculate number when siblings are of different type', function () {
var node, target;
it('should properly calculate number when siblings are of different type', () => {
let node, target;
node = document.createElement('span');
fixture.appendChild(node);

Expand All @@ -74,26 +105,30 @@ describe('axe.utils.getXpath', function () {
node = document.createElement('span');
fixture.appendChild(node);

var sel = axe.utils.getXpath(target);
const sel = axe.utils.getXpath(target);

assert.equal(sel, "/div[@id='fixture']/div[2]");
assert.equal(sel, "//div[@id='fixture']/div[2]");
assert.equal(target, getElementByXPath(sel));
});

it('should work on the documentElement', function () {
var sel = axe.utils.getXpath(document.documentElement);
it('should work on the documentElement', () => {
const sel = axe.utils.getXpath(document.documentElement);
assert.equal(sel, '/html');
assert.equal(document.documentElement, getElementByXPath(sel));
});

it('should work on the body', function () {
var sel = axe.utils.getXpath(document.body);
it('should work on the body', () => {
const sel = axe.utils.getXpath(document.body);
assert.equal(sel, '/html/body');
assert.equal(document.body, getElementByXPath(sel));
});

it('should work on namespaced elements', function () {
fixture.innerHTML = '<hx:include>Hello</hx:include>';
var node = fixture.firstChild;
var sel = axe.utils.getXpath(node);

assert.equal(sel, "/div[@id='fixture']/hx:include");
assert.equal(sel, "//div[@id='fixture']/hx:include");
// couldn't figure out how to use document.evaluate to select an element with namespace
});
});
4 changes: 2 additions & 2 deletions test/core/utils/merge-results.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ describe('axe.utils.mergeResults', function () {

var node = result[0].nodes[0].node;
assert.deepEqual(node.selector, ['#target', '#foo']);
assert.deepEqual(node.xpath, ["/iframe[@id='target']", 'html/#foo']);
assert.deepEqual(node.xpath, ["//iframe[@id='target']", 'html/#foo']);
assert.deepEqual(node.ancestry, [
'html > body > div:nth-child(1) > iframe',
'html > div'
Expand Down Expand Up @@ -76,7 +76,7 @@ describe('axe.utils.mergeResults', function () {

var node = result[0].nodes[0].node;
assert.deepEqual(node.selector, ['#target', '#foo']);
assert.deepEqual(node.xpath, ["/iframe[@id='target']", 'html/#foo']);
assert.deepEqual(node.xpath, ["//iframe[@id='target']", 'html/#foo']);
assert.deepEqual(node.ancestry, [
'html > body > div:nth-child(1) > iframe',
'html > div'
Expand Down