diff --git a/circle.yml b/circle.yml new file mode 100644 index 0000000..652a8b6 --- /dev/null +++ b/circle.yml @@ -0,0 +1,6 @@ +machine: + node: + version: v6.1.0 +dependencies: + pre: + - npm i selenium-webdriver@2.53.1 diff --git a/lib/index.js b/lib/index.js index 783d068..fdb550c 100644 --- a/lib/index.js +++ b/lib/index.js @@ -103,14 +103,14 @@ AxeBuilder.prototype.analyze = function(callback) { config = this._config, source = this._source; - inject(driver, source, function() { + inject(driver, source, config, function() { driver .executeAsyncScript(function(context, options, config) { /*global document, axe */ if (config !== null) { - axe.configure(config); + window.axe.configure(config); } - axe.a11yCheck(context || document, options, arguments[arguments.length - 1]); + window.axe.a11yCheck(context || document, options, arguments[arguments.length - 1]); }, context, options, config) .then(callback); }); diff --git a/lib/inject.js b/lib/inject.js index 767ff76..51bd165 100644 --- a/lib/inject.js +++ b/lib/inject.js @@ -37,18 +37,14 @@ function findFramesAndInject(parent, script, driver) { * @private * @param {WebDriver} driver Instance of WebDriver to inject into * @param {String} axeSource Optional Attest source and configure string + * @param {Object} config Optional configure object to pass to iframes * @param {Function} callback Callback to execute when aXe has been injected */ -module.exports = function(driver, axeSource, callback) { +module.exports = function(driver, axeSource, config, callback) { axeSource = axeSource || require('axe-core').source; - var script = '(function () {' + - 'if (typeof axe === "object" && axe.version) { return; }' + - 'var s = document.createElement("script");' + - // stringify so that quotes are properly escaped - 's.innerHTML = ' + JSON.stringify(axeSource + ';' + 'axe.configure({branding:{application:"webdriverjs"}});') + ';' + - 'document.body.appendChild(s);' + - '}());'; + var configSrc = config !== null ? 'axe.configure(' + JSON.stringify(config) + ');' : ''; + var script = axeSource + configSrc + 'axe.configure({branding:{application:"webdriverjs"}});'; driver .switchTo().defaultContent(); diff --git a/package.json b/package.json index fe560d8..04abb6f 100644 --- a/package.json +++ b/package.json @@ -42,8 +42,8 @@ "test": "grunt test" }, "peerDependencies": { - "selenium-webdriver": "^3.0.0", - "axe-core": "^2.0.7" + "selenium-webdriver": ">= 2.53.1", + "axe-core": "^2.2.0" }, "devDependencies": { "chai": "^3.0.0", @@ -58,7 +58,8 @@ "proxyquire": "^1.5.0", "serve-static": "^1.9.3", "sinon": "^1.17.3", - "axe-core": "^2.0.7" + "selenium-webdriver": ">= 2.53.1", + "axe-core": "^2.2.0" }, "dependencies": { } diff --git a/test/fixtures/attest-config.json b/test/fixtures/attest-config.json index e3b3e97..9ff3073 100644 --- a/test/fixtures/attest-config.json +++ b/test/fixtures/attest-config.json @@ -5,7 +5,11 @@ "options": [ "dylan" ], - "evaluate": "function (node, options) {\n var lang = (node.getAttribute(\"lang\") || \"\").trim().toLowerCase();\n var xmlLang = (node.getAttribute(\"xml:lang\") || \"\").trim().toLowerCase();\n var invalid = [];\n (options || []).forEach(function(cc) {\n cc = cc.toLowerCase();\n if (lang && (lang === cc || lang.indexOf(cc.toLowerCase() + \"-\") === 0)) {\n lang = null;\n }\n if (xmlLang && (xmlLang === cc || xmlLang.indexOf(cc.toLowerCase() + \"-\") === 0)) {\n xmlLang = null;\n }\n });\n if (xmlLang) {\n invalid.push('xml:lang=\"' + xmlLang + '\"');\n }\n if (lang) {\n invalid.push('lang=\"' + lang + '\"');\n }\n if (invalid.length) {\n this.data(invalid);\n return true;\n }\n return false;\n }" + "evaluate": "function (node, options) {\n var lang = (node.getAttribute(\"lang\") || \"\").trim().toLowerCase();\n var xmlLang = (node.getAttribute(\"xml:lang\") || \"\").trim().toLowerCase();\n var invalid = [];\n (options || []).forEach(function(cc) {\n cc = cc.toLowerCase();\n if (lang && (lang === cc || lang.indexOf(cc.toLowerCase() + \"-\") === 0)) {\n lang = null;\n }\n if (xmlLang && (xmlLang === cc || xmlLang.indexOf(cc.toLowerCase() + \"-\") === 0)) {\n xmlLang = null;\n }\n });\n if (xmlLang) {\n invalid.push('xml:lang=\"' + xmlLang + '\"');\n }\n if (lang) {\n invalid.push('lang=\"' + lang + '\"');\n }\n if (invalid.length) {\n this.data(invalid);\n return true;\n }\n return false;\n }", + "messages": { + "pass": "Good language", + "fail": "You mst use the DYLAN language" + } } ], "rules": [ @@ -13,8 +17,7 @@ "id": "dylang", "metadata": { "description": "Ensures lang attributes have the value of 'dylan'", - "help": "lang attribute must have the value of 'dylan'", - "helpUrl": "https://example.com/dylang" + "help": "lang attribute must have the value of 'dylan'" }, "selector": "html", "any": [], @@ -26,5 +29,13 @@ "wcag2aa" ] } - ] + ], + "data": { + "rules": { + "dylang": { + "description": "Ensures lang attributes have the value of 'dylan'", + "help": "lang attribute must have the value of 'dylan'" + } + } + } } \ No newline at end of file diff --git a/test/fixtures/inner-configure-frame.html b/test/fixtures/inner-configure-frame.html new file mode 100644 index 0000000..661138d --- /dev/null +++ b/test/fixtures/inner-configure-frame.html @@ -0,0 +1,6 @@ + + + + Hello + + diff --git a/test/fixtures/inner-frame.html b/test/fixtures/inner-frame.html index 661138d..eee9cda 100644 --- a/test/fixtures/inner-frame.html +++ b/test/fixtures/inner-frame.html @@ -1,5 +1,5 @@ - + Hello diff --git a/test/fixtures/outer-configure-frame.html b/test/fixtures/outer-configure-frame.html new file mode 100644 index 0000000..ed75cea --- /dev/null +++ b/test/fixtures/outer-configure-frame.html @@ -0,0 +1,9 @@ + + + + Configure Frame Test + + + + + diff --git a/test/integration/configure-frames.js b/test/integration/configure-frames.js new file mode 100644 index 0000000..4ec8775 --- /dev/null +++ b/test/integration/configure-frames.js @@ -0,0 +1,43 @@ +var runWebdriver = require('../run-webdriver'), + assert = require('chai').assert, + host = 'localhost', + json = require('../fixtures/attest-config.json'), + AxeBuilder = require('../../lib'); + +if (process.env.REMOTE_TESTSERVER_HOST) { + host = process.env.REMOTE_TESTSERVER_HOST; +} + +describe('outer-configure-frame.html', function () { + this.timeout(10000); + + var driver; + before(function (done) { + driver = runWebdriver(); + driver.manage().timeouts().setScriptTimeout(500); + + driver + .get('http://' + host + ':9876/test/fixtures/outer-configure-frame.html') + .then(function () { + done(); + }); + }); + + after(function () { + driver.quit(); + }); + + it('should find configured violations in all frames', function (done) { + AxeBuilder(driver) + .options({rules: {'html-lang-valid': {'enabled': false}}}) + .configure(json) + .analyze(function (results) { + assert.equal(results.violations[0].id, 'dylang'); + // the second violation is in a frame + assert.equal(results.violations[0].nodes.length, 2); + + done(); + }); + }); + +}); diff --git a/test/integration/frames.js b/test/integration/frames.js index ecf73e1..e1fe62c 100644 --- a/test/integration/frames.js +++ b/test/integration/frames.js @@ -45,7 +45,7 @@ describe('outer-frame.html', function () { it('should accept options', function (done) { AxeBuilder(driver) .include('body') - .options({ checks: { "valid-lang": { options: ['bob'] }}}) + .options({ checks: { "valid-lang": { options: ['bobbert'] }}}) .withRules('html-lang-valid') .analyze(function (results) { assert.lengthOf(results.violations, 0); diff --git a/test/run-webdriver.js b/test/run-webdriver.js index 490cc35..03e06c3 100644 --- a/test/run-webdriver.js +++ b/test/run-webdriver.js @@ -4,12 +4,12 @@ function runWebdriver() { var webdriver; if (process.env.REMOTE_SELENIUM_URL) { webdriver = new WebDriver.Builder() - .forBrowser('firefox') + .forBrowser('chrome') .usingServer(process.env.REMOTE_SELENIUM_URL) .build(); } else { webdriver = new WebDriver.Builder() - .forBrowser('firefox') + .forBrowser('chrome') .build(); } diff --git a/test/unit/index.js b/test/unit/index.js index 67f869a..1cacfe7 100644 --- a/test/unit/index.js +++ b/test/unit/index.js @@ -105,8 +105,9 @@ describe('Builder', function () { } } }; + var config = {}; var Builder = proxyquire('../../lib/index', { - './inject': function (driver, source, cb) { + './inject': function (driver, source, config, cb) { cb(null, 'source-code'); } }); @@ -146,8 +147,9 @@ describe('Builder', function () { describe('analyze', function () { it('should normalize context', function (done) { var normalized = false; + var config = {}; var Builder = proxyquire('../../lib/index', { - './inject': function (driver, source, cb) { + './inject': function (driver, source, config, cb) { cb(null, 'source-code'); }, './normalize-context': function (include, exclude) { @@ -178,8 +180,9 @@ describe('Builder', function () { it('should inject into the page under test', function () { var called = false; + var config = {}; var Builder = proxyquire('../../lib/index', { - './inject': function (driver, source, cb) { + './inject': function (driver, source, config, cb) { assert.equal(driver, 'driver'); assert.isFunction(cb); called = true; @@ -191,8 +194,9 @@ describe('Builder', function () { }); it('should call axe.a11yCheck with given parameters', function (done) { + var config = {}; var Builder = proxyquire('../../lib/index', { - './inject': function (driver, source, cb) { + './inject': function (driver, source, config, cb) { cb(null, 'source-code'); }, './normalize-context': function (include, exclude) {