Skip to content
This repository has been archived by the owner on Mar 19, 2021. It is now read-only.

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
David Sturley committed Jun 12, 2015
0 parents commit 6d3ac21
Show file tree
Hide file tree
Showing 13 changed files with 806 additions and 0 deletions.
15 changes: 15 additions & 0 deletions .editorconfig
@@ -0,0 +1,15 @@
# EditorConfig is awesome: http://EditorConfig.org

# top-most EditorConfig file
root = true

[*]
end_of_line = lf
insert_final_newline = true
charset = utf-8
indent_style = tab
trim_trailing_whitespace = true

[*.json]
indent_style = space
indent_size = 2
1 change: 1 addition & 0 deletions .gitignore
@@ -0,0 +1 @@
node_modules
362 changes: 362 additions & 0 deletions LICENSE

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions README.md
@@ -0,0 +1 @@
# axe-webdriverjs
46 changes: 46 additions & 0 deletions lib/index.js
@@ -0,0 +1,46 @@
var inject = require('./inject'),
normalizeContext = require('./normalize-context');

function AxeBuilder(driver) {
if (!(this instanceof AxeBuilder)) {
return new AxeBuilder(driver);
}

this._driver = driver;
this._includes = [];
this._excludes = [];
this._options = null;
}

AxeBuilder.prototype.include = function(selector) {
this._includes.push(Array.isArray(selector) ? selector : [selector]);
return this;
};

AxeBuilder.prototype.exclude = function(selector) {
this._excludes.push(Array.isArray(selector) ? selector : [selector]);
return this;
};

AxeBuilder.prototype.options = function(options) {
this._options = options;
return this;
};

AxeBuilder.prototype.analyze = function(callback) {
var context = normalizeContext(this._includes, this._excludes),
driver = this._driver,
options = this._options;


inject(driver, function() {
driver
.executeAsyncScript(function(context, options) {
/*global document, axe */
axe.a11yCheck(context || document, options, arguments[arguments.length - 1]);
}, context, options)
.then(callback);
});
};

exports = module.exports = AxeBuilder;
85 changes: 85 additions & 0 deletions lib/inject.js
@@ -0,0 +1,85 @@
var path = require('path'),
fs = require('fs');

var AXE_SOURCE;

/**
* Recursively find frames and inject a script into them
* @private
* @param {Array} parent Array of parent frames; or falsey if top level frame
* @param {String} script The script to inject
* @param {WebDriver} driver The driver to inject into
*/
function findFramesAndInject(parent, script, driver) {
driver
.findElements({
tagName: 'iframe'
})
.then(function(results) {
results.forEach(function(frame) {
driver.switchTo().defaultContent();
if (parent) {
parent.forEach(function(p) {
driver.switchTo().frame(p);
});
}
driver.switchTo().frame(frame)
.then(function() {
driver
.executeScript(script)
.then(function() {
findFramesAndInject((parent || []).concat(frame), script, driver);
});
});
});
});
}

/**
* Get the source of the axe-core package
* @param {Function} callback Callback to execute when source is retrieved
*/
function getSource(callback) {
if (AXE_SOURCE) {
return callback(null, AXE_SOURCE);
}
var pathToAxe = path.resolve(__dirname, '../node_modules/axe-core/axe.js');
fs.readFile(pathToAxe, {
encoding: 'utf-8'
}, callback);
}

/**
* Recursively inject aXe into all iframes and top level document, then execute a callback when complete
* @param {WebDriver} driver Instance of WebDriver to inject into
* @param {Function} callback Callback to execute when aXe has been injected
*/
module.exports = function(driver, callback) {
getSource(function(err, axeSource) {
if (err) {
return console.error(err);
}

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) + ';' +
'document.body.appendChild(s);' +
'}());';

driver
.switchTo().defaultContent();

driver
.executeScript(script)
.then(function() {
findFramesAndInject(null, script, driver);
})
.then(function() {
driver.switchTo().defaultContent();
callback();
});

});
};
23 changes: 23 additions & 0 deletions lib/normalize-context.js
@@ -0,0 +1,23 @@

exports = module.exports = function (include, exclude) {
if (!exclude.length) {
if (!include.length) {
return null;
}

return {
include: include
};
}

if (!include.length) {
return {
exclude: exclude
};
}

return {
include: include,
exclude: exclude
};
};
40 changes: 40 additions & 0 deletions package.json
@@ -0,0 +1,40 @@
{
"name": "axe-webdriverjs",
"description": "Provides a method to inject and analyze web pages using aXe",
"version": "0.0.1",
"license": "MPL-2.0",
"author": {
"name": "David Sturley",
"organization": "Deque Systems, Inc.",
"url": "http://deque.com/"
},
"repository": {
"type": "git",
"url": "https://github.com/dequelabs/axe-webdriverjs.git"
},
"keywords": [
"a11y",
"unit",
"testing",
"tdd",
"bdd",
"accessibility",
"aXe",
"selenium",
"webdriver",
"webdriverjs"
],
"scripts": {
"test": "./node_modules/mocha/bin/mocha test/unit/**/* test/integration/**/*"
},
"dependencies": {},
"devDependencies": {
"chai": "^3.0.0",
"mocha": "^2.2.5",
"proxyquire": "^1.5.0",
"axe-core": "~1.0.1"
},
"peerDependencies": {
"axe-core": "~1.0.1"
}
}
6 changes: 6 additions & 0 deletions test/fixtures/doc-lang.html
@@ -0,0 +1,6 @@
<!doctype html>
<html>
<head>
<title>Test</title>
</head>
</html>
57 changes: 57 additions & 0 deletions test/integration/doc-lang.js
@@ -0,0 +1,57 @@
var WebDriver = require('selenium-webdriver'),
assert = require('chai').assert,
AxeBuilder = require('../../lib');

describe('doc-lang.html', function () {
this.timeout(10000);

var driver;
before(function (done) {
driver = new WebDriver.Builder()
.forBrowser('firefox')
.build();

driver
.get('http://localhost:8000/test/fixtures/doc-lang.html')
.then(function () {
done();
});
});

it('should find violations', function (done) {
AxeBuilder(driver)
.options({ runOnly: { type: 'rule', values: ['html-lang'] }})
.analyze(function (results) {
assert.lengthOf(results.violations, 1);
assert.equal(results.violations[0].id, 'html-lang');
assert.lengthOf(results.passes, 0);
done();
});
});

it('should not find violations when given context (document level rule)', function (done) {
AxeBuilder(driver)
.include('body')
.options({ runOnly: { type: 'rule', values: ['html-lang'] }})
.analyze(function (results) {
assert.lengthOf(results.violations, 0);
assert.lengthOf(results.passes, 0);
done();
});
});

it('should not find violations when the rule is disabled', function (done) {
AxeBuilder(driver)
.options({ rules: { 'html-lang': { enabled: false } } })
.analyze(function (results) {
results.violations.forEach(function (violation) {
assert.notEqual(violation.id, 'html-lang');
});
results.passes.forEach(function (violation) {
assert.notEqual(violation.id, 'html-lang');
});
done();
});
});

});

0 comments on commit 6d3ac21

Please sign in to comment.