From 790b42190ad98107b3708a59ef8e748728f01e78 Mon Sep 17 00:00:00 2001 From: Wilco Fiers Date: Wed, 21 Feb 2018 10:01:02 +0100 Subject: [PATCH] feat: Support aXe-core 3.0 Shadow DOM selectors (#49) --- index.js | 2 +- lib/utils.js | 11 +++++-- package-lock.json | 27 +++++++++++++++++ package.json | 1 + test/integrations.js | 70 ++++++++++++++++++++++++++++++++++++++++++++ test/testpage.html | 27 +++++++++++++++++ 6 files changed, 134 insertions(+), 4 deletions(-) create mode 100644 test/integrations.js create mode 100644 test/testpage.html diff --git a/index.js b/index.js index 768f17b..9c7ccc2 100755 --- a/index.js +++ b/index.js @@ -98,7 +98,7 @@ axeTestUrls(urls, program, { ' Violation of %j with %d occurrences!\n') + ' %s. Correct invalid elements at:\n' + (violation.nodes.map( node => - ' - ' + node.target + '\n' + ' - ' + utils.selectorToString(node.target) + '\n' ).join('')) + ' For details, see: %s', violation.id, diff --git a/lib/utils.js b/lib/utils.js index 39f9f98..ea2dc38 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -62,13 +62,18 @@ module.exports.getAxeSource = function getAxeSource(axePath) { return fs.readFileSync(axePath, 'utf8') } - module.exports.getAxeVersion = function getAxeVersion(source) { const match = source.match(/\.version\s*=\s'([^']+)'/) return (match ? match[1] : 'unknown version') } - module.exports.splitList = function (val) { return (val.split(/[,;]/)).map(str => str.trim()); -} \ No newline at end of file +} + +module.exports.selectorToString = function (selectors, separator) { + separator = separator || ' '; + return selectors + .reduce((prev, curr) => (prev.concat(curr)), []) + .join(separator); +} diff --git a/package-lock.json b/package-lock.json index ab21e44..1fa396e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1396,6 +1396,12 @@ "yallist": "2.1.2" } }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true + }, "mime-db": { "version": "1.33.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", @@ -1629,6 +1635,17 @@ "inherits": "2.0.3" } }, + "node-static": { + "version": "0.7.10", + "resolved": "https://registry.npmjs.org/node-static/-/node-static-0.7.10.tgz", + "integrity": "sha512-bd7zO5hvCWzdglgwz9t82T4mYTEUzEG5pXnSqEzitvmEacusbhl8/VwuCbMaYR9g2PNK5191yBtAEQLJEmQh1A==", + "dev": true, + "requires": { + "colors": "1.1.2", + "mime": "1.6.0", + "optimist": "0.6.1" + } + }, "node-status-codes": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/node-status-codes/-/node-status-codes-1.0.0.tgz", @@ -1684,6 +1701,16 @@ "integrity": "sha1-QsPhjslUZra/DcQvOilFw/DK2Pw=", "dev": true }, + "optimist": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", + "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", + "dev": true, + "requires": { + "minimist": "0.0.8", + "wordwrap": "0.0.2" + } + }, "os-homedir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", diff --git a/package.json b/package.json index e2c971e..2ce841f 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "devDependencies": { "chai": "^4.0.0", "mocha": "^3.4.2", + "node-static": "^0.7.10", "snyk": "^1.41.1" }, "snyk": true diff --git a/test/integrations.js b/test/integrations.js new file mode 100644 index 0000000..36dd646 --- /dev/null +++ b/test/integrations.js @@ -0,0 +1,70 @@ +/* global mocha */ +'use strict'; + +var assert = require('chai').assert + +var chromedriver = require('chromedriver') +var chrome = require('selenium-webdriver/chrome') +var http = require('http') +var nodeStatic = require('node-static') +var axeTestUrls = require('../lib/axe-test-urls') +var {startDriver, stopDriver} = require('../lib/webdriver') + +describe('integrations', function () { + var program, urls, server; + + before(function () { + // Start a server + var file = new nodeStatic.Server('.'); + server = http.createServer(function (request, response) { + request.addListener('end', function () { + file.serve(request, response); + }).resume(); + }) + server.listen(8182); + }) + + after(function () { + server.close(); + }) + + beforeEach(function () { + program = { + browser: 'chrome-headless' + } + startDriver(program) + urls = ['http://localhost:8182/test/testpage.html'] + }) + + afterEach(function (done) { + stopDriver(program) + + var service = chrome.getDefaultService() + if (service.isRunning()) { + service.stop().then(() => { + // An unfortunately hacky way to clean up + // the service. Stop will shut it down, + // but it doesn't reset the local state + service.address_ = null; + chrome.setDefaultService(null); + done() + }) + } else { + done() + } + }) + + it('finds results in light and shadow DOM', function () { + var listResult + return axeTestUrls(urls, program, { + onTestComplete: function (results) { + listResult = results.violations.find(result => result.id === 'list') + assert.lengthOf(listResult.nodes, 2); + assert.deepEqual(listResult.nodes[0].target, ['#list']) + assert.deepEqual(listResult.nodes[1].target, [['#shadow-root', '#shadow-list']]) + } + }).then(function () { + assert.isDefined(listResult) + }) + }) +}) diff --git a/test/testpage.html b/test/testpage.html new file mode 100644 index 0000000..dbaecfd --- /dev/null +++ b/test/testpage.html @@ -0,0 +1,27 @@ + + + + + List Item Test + + +
+

Page heading

+ + +
+
+ + +