diff --git a/.gitignore b/.gitignore
index 651780f..2fbc200 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,3 +10,4 @@ external/
icons/svg-min/
.sass-cache/
dist/
+tmp/
diff --git a/demos/typography.html b/demos/typography.html
index 8552857..3b05b4d 100644
--- a/demos/typography.html
+++ b/demos/typography.html
@@ -1,5 +1,5 @@
-
+
CSS Chassis - Typography
diff --git a/lib/reporter.js b/lib/reporter.js
new file mode 100644
index 0000000..87b479d
--- /dev/null
+++ b/lib/reporter.js
@@ -0,0 +1,48 @@
+
+
+function color(code, str) {
+ return "\u001b[" + code + "m" + str + "\u001b[0m";
+}
+
+exports = module.exports = function( grunt, results, threshold ) {
+ var pass = true;
+ results.forEach( function ( result ) {
+ grunt.log.subhead( result.url );
+ var violations = result.violations;
+ if ( violations.length ) {
+ if ( violations.length > threshold ) {
+ pass = false;
+ grunt.log.error( "Found " + result.violations.length + " accessibility violations:" );
+ } else {
+ grunt.log.ok( "Found " + result.violations.length + " accessibility violations: (under threshold of " + threshold + ")" );
+ }
+ result.violations.forEach( function( ruleResult ) {
+ grunt.log.subhead( " " + color(31, "\u00D7") + " " + ruleResult.help );
+
+ ruleResult.nodes.forEach( function( violation, index ) {
+ grunt.log.writeln( " " + ( index + 1 ) + ". " + JSON.stringify( violation.target ) );
+
+ if ( violation.any.length ) {
+ grunt.log.writeln( " Fix any of the following:" );
+ violation.any.forEach( function( check ) {
+ grunt.log.writeln( " \u2022 " + check.message );
+ } );
+ }
+
+ var alls = violation.all.concat( violation.none );
+ if ( alls.length ) {
+ grunt.log.writeln( " Fix all of the following:" );
+ alls.forEach( function( check ) {
+ grunt.log.writeln( " \u2022 " + check.message );
+ } );
+ }
+ grunt.log.writeln();
+ });
+ });
+ return;
+ } else {
+ grunt.log.ok( "Found no accessibility violations." );
+ }
+ } );
+ return pass;
+};
diff --git a/package.json b/package.json
index 1a089eb..40f9408 100644
--- a/package.json
+++ b/package.json
@@ -37,16 +37,17 @@
},
"dependencies": {},
"devDependencies": {
+ "axe-webdriverjs": "^0.1.0",
"browser-perf": "1.2.3",
"chromedriver": "2.13.0",
"commitplease": "2.0.0",
"ejs-template": "0.1.0",
"grunt": "0.4.5",
"grunt-autoprefixer": "2.1.0",
- "grunt-contrib-cssmin": "0.10.0",
+ "grunt-contrib-connect": "0.9.0",
"grunt-contrib-csslint": "0.4.0",
+ "grunt-contrib-cssmin": "0.10.0",
"grunt-contrib-jshint": "0.10.0",
- "grunt-contrib-connect": "0.9.0",
"grunt-contrib-watch": "0.6.1",
"grunt-csscomb": "3.0.0",
"grunt-git-authors": "2.0.0",
@@ -58,7 +59,9 @@
"grunt-svgstore": "0.5.0",
"jsass-vars": "0.0.3",
"load-grunt-config": "0.16.0",
- "perfjankie": "1.2.2"
+ "perfjankie": "1.2.2",
+ "promise": "^7.0.3",
+ "selenium-webdriver": "^2.46.1"
},
"keywords": []
}
diff --git a/tasks/alias.js b/tasks/alias.js
index 0b1fe20..77e8234 100644
--- a/tasks/alias.js
+++ b/tasks/alias.js
@@ -1,7 +1,8 @@
module.exports = function( grunt ) {
grunt.registerTask( "default", [ "test" ] );
-grunt.registerTask( "test", [ "build", "jshint", "jscs", "csslint" ] );
-grunt.registerTask( "build", [ "svg", "sass", "csscomb", "cssmin" ] );
+grunt.registerTask( "accessibility", [ "connect:accessibility", "axe-webdriver"])
+grunt.registerTask( "test", [ "build", "jshint", "jscs", "csslint", "accessibility" ] );
+grunt.registerTask( "build", [ "buildVariables", "svg", "sass", "csscomb", "cssmin" ] );
grunt.registerTask( "perf", [
"start-selenium-server",
"connect:perf",
diff --git a/tasks/axe-webdriver.js b/tasks/axe-webdriver.js
new file mode 100644
index 0000000..f1d3357
--- /dev/null
+++ b/tasks/axe-webdriver.js
@@ -0,0 +1,58 @@
+/*! aXe-grunt-webdriver
+ * Copyright (c) 2015 Deque Systems, Inc.
+ *
+ * Your use of this Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This entire copyright notice must appear in every copy of this file you
+ * distribute or in any file that contains substantial portions of this source
+ * code.
+ */
+
+'use strict';
+
+module.exports = function( grunt ) {
+ var WebDriver = require( "selenium-webdriver" ),
+ AxeBuilder = require( "axe-webdriverjs" ),
+ Promise = require( "promise" ),
+ path = require( "path" ),
+ reporter = require( "../lib/reporter" );
+
+ grunt.registerMultiTask( "axe-webdriver", "Grunt plugin for aXe utilizing WebDriverJS", function () {
+ var options = this.options( {
+ browser: "firefox",
+ server: null,
+ threshold: 0
+ } );
+
+ var done = this.async ();
+ var driver = new WebDriver.Builder ()
+ .forBrowser( options.browser )
+ .build ();
+
+ var dest = this.data.dest;
+ Promise.all( this.data.urls.map( function( url ) {
+ return new Promise( function( resolve, reject ) {
+
+ driver
+ .get( url )
+ .then( function() {
+ new AxeBuilder( driver )
+ .analyze( function( results ) {
+ results.url = url;
+ resolve( results );
+ } );
+ } );
+ } );
+ })).then( function( results ) {
+ if ( dest ) {
+ grunt.file.write( dest, JSON.stringify( results, null, " " ) );
+ }
+ var result = reporter( grunt, results, options.threshold );
+ driver.quit().then( function () {
+ done( result );
+ } );
+ } );
+ } );
+};
diff --git a/tasks/options/axe-webdriver.js b/tasks/options/axe-webdriver.js
new file mode 100644
index 0000000..b7a485f
--- /dev/null
+++ b/tasks/options/axe-webdriver.js
@@ -0,0 +1,16 @@
+module.exports = {
+ firefox: {
+ options: {
+ threshold: 0
+ },
+ urls: ['http://localhost:4200/demos/typography.html'],
+ dest: 'tmp/gu.json'
+ },
+ chrome: {
+ options: {
+ browser: 'chrome',
+ threshold: 0
+ },
+ urls: ['http://localhost:4200/demos/typography.html']
+ }
+};
diff --git a/tasks/options/connect.js b/tasks/options/connect.js
index 10731f9..abd1ab5 100644
--- a/tasks/options/connect.js
+++ b/tasks/options/connect.js
@@ -5,32 +5,36 @@ var template = require( "ejs-template" ),
module.exports = {
options: {
port: 4200,
- base: ".",
- middleware: [
- template.middleware({ basedir: __dirname }),
- function( req, res ) {
- var data, i,
- url = urlParser.parse( req.url, true ),
- query = {},
- parts = url.pathname.split( "/" ),
- file = req.url.replace( /^\//, "" ).split( "?" )[ 0 ];
+ base: "."
+ },
+ perf: {
+ options: {
+ middleware: [
+ template.middleware({ basedir: __dirname }),
+ function( req, res ) {
+ var data, i,
+ url = urlParser.parse( req.url, true ),
+ query = {},
+ parts = url.pathname.split( "/" ),
+ file = req.url.replace( /^\//, "" ).split( "?" )[ 0 ];
- for ( i = 1; i < parts.length; i += 2 ) {
- query[ parts[ i ] ] = parts[ i + 1 ];
+ for ( i = 1; i < parts.length; i += 2 ) {
+ query[ parts[ i ] ] = parts[ i + 1 ];
+ }
+ if ( file.split( "." ).length <= 1 ) {
+ data = componentGenerator.generate(
+ query.framework,
+ query.component,
+ query.count
+ );
+ file = "../../performance/component.html";
+ }
+ res.endTemplate( file, data );
}
- if ( file.split( "." ).length <= 1 ) {
- data = componentGenerator.generate(
- query.framework,
- query.component,
- query.count
- );
- file = "../../performance/component.html";
- }
- res.endTemplate( file, data );
- }
- ]
+ ]
+ }
},
- perf: {},
+ accessibility: {},
dev: {
options: {
keepalive: true