Foobar tittle
9 |Not a label
10 | 11 |Not an alt
12 |
├── .editorconfig ├── .eslintrc.json ├── .gitattributes ├── .gitignore ├── .npmignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── gulpfile.js ├── index.js ├── lib └── reporter.js ├── package-lock.json ├── package.json ├── tasks ├── bump.js ├── changelog.js ├── git.js ├── lint.js ├── mocha.js └── release.js └── test ├── fixtures ├── broken.html └── working.html └── test.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = tab 5 | end_of_line = lf 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [{package.json,*.yml}] 11 | indent_style = space 12 | indent_size = 2 13 | 14 | [*.md] 15 | trim_trailing_whitespace = false 16 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "eslint:recommended", 3 | "rules": { 4 | "no-console": "off" 5 | }, 6 | "env": { 7 | "browser": true 8 | }, 9 | "plugins": [ 10 | "html" 11 | ], 12 | "globals": { 13 | "require" : false, 14 | "__dirname" : false, 15 | "describe" : false, 16 | "process" : false, 17 | "beforeEach" : false, 18 | "afterEach" : false, 19 | "module" : false, 20 | "it" : false, 21 | "xit": false, 22 | "xdescribe": false 23 | } 24 | } -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .travis.yml 2 | .gitignore 3 | .eslintrc.json 4 | tasks 5 | test 6 | gulpfile.js -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | ## [3.1.3](https://github.com/felixzapata/gulp-axe-webdriver/compare/3.1.2...3.1.3) (2019-04-24) 3 | 4 | 5 | ### Features 6 | 7 | * **index.js:** remove import fs-path ([3ca751c](https://github.com/felixzapata/gulp-axe-webdriver/commit/3ca751c)) 8 | * **package.json:** remove fs-path package ([d2b24a5](https://github.com/felixzapata/gulp-axe-webdriver/commit/d2b24a5)) 9 | 10 | 11 | 12 | 13 | ## [3.1.2](https://github.com/felixzapata/gulp-axe-webdriver/compare/3.1.1...3.1.2) (2019-04-20) 14 | 15 | 16 | ### Bug Fixes 17 | 18 | * **packages:** update dependencies ([f701836](https://github.com/felixzapata/gulp-axe-webdriver/commit/f701836)) 19 | 20 | 21 | 22 | 23 | ## [3.1.1](https://github.com/felixzapata/gulp-axe-webdriver/compare/3.1.0...3.1.1) (2019-03-15) 24 | 25 | 26 | ### Bug Fixes 27 | 28 | * **index.js:** remove deprecated warning ([#22](https://github.com/felixzapata/gulp-axe-webdriver/issues/22)) ([23a4bcc](https://github.com/felixzapata/gulp-axe-webdriver/commit/23a4bcc)) 29 | 30 | 31 | 32 | 33 | # [3.1.0](https://github.com/felixzapata/gulp-axe-webdriver/compare/3.0.1...3.1.0) (2018-09-19) 34 | 35 | 36 | ### Features 37 | 38 | * **scriptTimeout:** add scriptTimeout option ([5cc450c](https://github.com/felixzapata/gulp-axe-webdriver/commit/5cc450c)), closes [#18](https://github.com/felixzapata/gulp-axe-webdriver/issues/18) 39 | 40 | 41 | 42 | 43 | ## [3.0.1](https://github.com/felixzapata/gulp-axe-webdriver/compare/3.0.0...3.0.1) (2018-08-09) 44 | 45 | 46 | ### Bug Fixes 47 | 48 | * **index.js:** return promise with axeBuilder ([20c18ed](https://github.com/felixzapata/gulp-axe-webdriver/commit/20c18ed)) 49 | 50 | 51 | 52 | 53 | # [3.0.0](https://github.com/felixzapata/gulp-axe-webdriver/compare/2.1.2...3.0.0) (2018-07-18) 54 | 55 | 56 | ### Features 57 | 58 | * **index.js:** return a promise ([cb877d6](https://github.com/felixzapata/gulp-axe-webdriver/commit/cb877d6)) 59 | 60 | 61 | 62 | 63 | ## [2.1.2](https://github.com/felixzapata/gulp-axe-webdriver/compare/2.1.1...2.1.2) (2018-06-19) 64 | 65 | ### Bug Fixes 66 | 67 | * **index.js:** driver instance due to invalid session ID ([bbf4dd5](https://github.com/felixzapata/gulp-axe-webdriver/commit/bbf4dd5)) 68 | 69 | 70 | ## [2.1.1](https://github.com/felixzapata/gulp-axe-webdriver/compare/2.1.0...2.1.1) (2018-05-31) 71 | 72 | 73 | ### Bug Fixes 74 | 75 | * **index.js:** increase timeout to 60000 ms ([68d43f1](https://github.com/felixzapata/gulp-axe-webdriver/commit/68d43f1)) 76 | 77 | 78 | 79 | # [2.1.0](https://github.com/felixzapata/gulp-axe-webdriver/compare/2.0.0...2.1.0) (2018-04-24) 80 | 81 | 82 | ### Features 83 | 84 | * **headless:** add support for headless mode ([7b1345b](https://github.com/felixzapata/gulp-axe-webdriver/commit/7b1345b)) 85 | 86 | 87 | 88 | 89 | # [2.0.0](https://github.com/felixzapata/gulp-axe-webdriver/compare/1.4.1...2.0.0) (2018-04-23) 90 | 91 | 92 | ### Features 93 | 94 | * **package.json:** update axe-core to ^3.0.1 ([bec83e6](https://github.com/felixzapata/gulp-axe-webdriver/commit/bec83e6)) 95 | * **package.json:** update axe-webdriverjs to ^2.0.0 ([cf8e76e](https://github.com/felixzapata/gulp-axe-webdriver/commit/cf8e76e)) 96 | * **phantomjs:** remove phantomjs ([94d61e1](https://github.com/felixzapata/gulp-axe-webdriver/commit/94d61e1)) 97 | 98 | 99 | 100 | 101 | ## [1.4.1](https://github.com/felixzapata/gulp-axe-webdriver/compare/1.4.0...1.4.1) (2018-04-23) 102 | 103 | 104 | ### Features 105 | 106 | * **package.json:** update chromedriver to 2.38.0 ([958233b](https://github.com/felixzapata/gulp-axe-webdriver/commit/958233b)) 107 | 108 | 109 | 110 | 111 | # [1.4.0](https://github.com/felixzapata/gulp-axe-webdriver/compare/1.3.2...v1.4.0) (2016-12-27) 112 | 113 | 114 | ### Features 115 | 116 | * **package.json:** update axe-webdriverjs and selenium-webdriver ([dd7aee2](https://github.com/felixzapata/gulp-axe-webdriver/commit/dd7aee2)) 117 | 118 | 119 | 120 | 121 | ## [1.3.2](https://github.com/felixzapata/gulp-axe-webdriver/compare/1.3.1...v1.3.2) (2016-12-16) 122 | 123 | 124 | ### Bug Fixes 125 | 126 | * **package.json:** set chromedriver to version 2.25.1 to avoid problems behind a proxy ([e99840e](https://github.com/felixzapata/gulp-axe-webdriver/commit/e99840e)) 127 | 128 | 129 | 130 | 131 | ## [1.3.1](https://github.com/felixzapata/gulp-axe-webdriver/compare/1.3.0...v1.3.1) (2016-12-09) 132 | 133 | 134 | ### Bug Fixes 135 | 136 | * **index.js:** modify drivers and capabilities to check complex urls in Chrome and PhantomJS ([71df07f](https://github.com/felixzapata/gulp-axe-webdriver/commit/71df07f)) 137 | 138 | 139 | 140 | 141 | # [1.3.0](https://github.com/felixzapata/gulp-axe-webdriver/compare/1.2.0...v1.3.0) (2016-12-09) 142 | 143 | 144 | ### Features 145 | 146 | * **404:** create first approach to check only valid urls ([3035949](https://github.com/felixzapata/gulp-axe-webdriver/commit/3035949)) 147 | * **index.js:** filter local and non local urls to review the status ([dd2e25d](https://github.com/felixzapata/gulp-axe-webdriver/commit/dd2e25d)) 148 | * **package.json:** update axe-core to 2.0.7 ([29a3bcc](https://github.com/felixzapata/gulp-axe-webdriver/commit/29a3bcc)) 149 | * **package.json:** update axe-webdriverjs to 0.4.0 ([8eb21a2](https://github.com/felixzapata/gulp-axe-webdriver/commit/8eb21a2)) 150 | * **reporter.js:** show message when the URL is not valid ([d0be5f6](https://github.com/felixzapata/gulp-axe-webdriver/commit/d0be5f6)) 151 | 152 | 153 | 154 | 155 | # [1.2.0](https://github.com/felixzapata/gulp-axe-webdriver/compare/1.1.0...v1.2.0) (2016-10-10) 156 | 157 | 158 | ### Features 159 | 160 | * **a11yCheckOptions:** add attribute a11yCheckOptions to override options ([5244f05](https://github.com/felixzapata/gulp-axe-webdriver/commit/5244f05)) 161 | * **onlyViolations:** add option to return only the violations ([14b7cc5](https://github.com/felixzapata/gulp-axe-webdriver/commit/14b7cc5)) 162 | * **verbose:** add verbose option to show information messages about the analysis ([861f076](https://github.com/felixzapata/gulp-axe-webdriver/commit/861f076)) 163 | 164 | 165 | 166 | 167 | # [1.1.0](https://github.com/felixzapata/gulp-axe-webdriver/compare/1.0.0...v1.1.0) (2016-09-25) 168 | 169 | 170 | ### Features 171 | 172 | * **exclude:** add exclude option to add a CSS selector to the list of elements to exclude in analysis ([c93dd88](https://github.com/felixzapata/gulp-axe-webdriver/commit/c93dd88)) 173 | * **include:** add include option to add a CSS selector to the list of elements to include in analysis ([204748a](https://github.com/felixzapata/gulp-axe-webdriver/commit/204748a)) 174 | 175 | 176 | 177 | 178 | # [1.0.0](https://github.com/felixzapata/gulp-axe-webdriver/compare/0.1.0...v1.0.0) (2016-09-07) 179 | 180 | 181 | ### Bug Fixes 182 | 183 | * **index.js:** change how to show the url to test ([f3f7cc3](https://github.com/felixzapata/gulp-axe-webdriver/commit/f3f7cc3)) 184 | 185 | 186 | ### Features 187 | 188 | * **plugin:** modify the plugin to admin local and remote urls also glob patterns ([ca9abc0](https://github.com/felixzapata/gulp-axe-webdriver/commit/ca9abc0)) 189 | 190 | 191 | 192 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016, Félix Zapata 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gulp-axe-webdriver 2 | 3 | [](http://npm.packagequality.com/badge/gulp-axe-webdriver.png) 4 | 5 | > Gulp plugin for [aXe](https://github.com/dequelabs/axe-core) utilizing WebDriverJS. 6 | 7 | Inspired by [grunt-axe-webdriver](https://github.com/dequelabs/grunt-axe-webdriver) and [gulp-axe-core](https://github.com/felixzapata/gulp-axe-core). 8 | 9 | This plugin checks local and remote urls, using the Chrome browser via `chromedriver`. 10 | 11 | ## Install 12 | 13 | ``` 14 | $ npm install --save-dev gulp-axe-webdriver 15 | ``` 16 | 17 | ## The task 18 | 19 | ### Usage 20 | 21 | ```js 22 | var gulp = require('gulp'); 23 | var axe = require('gulp-axe-webdriver'); 24 | 25 | gulp.task('axe', function() { 26 | var options = { 27 | saveOutputIn: 'allHtml.json', 28 | urls: ['http://www.foobar-url-1/', 'http://www.foobar-url-2/'] 29 | }; 30 | return axe(options); 31 | }); 32 | ``` 33 | 34 | ```js 35 | var gulp = require('gulp'); 36 | var axe = require('gulp-axe-webdriver'); 37 | 38 | gulp.task('axe', function() { 39 | var options = { 40 | saveOutputIn: 'allHtml.json', 41 | urls: ['src/file2.html'] 42 | }; 43 | return axe(options); 44 | }); 45 | ``` 46 | 47 | #### With Headless Chrome 48 | 49 | ```js 50 | var gulp = require('gulp'); 51 | var axe = require('gulp-axe-webdriver'); 52 | 53 | gulp.task('axe', function() { 54 | var options = { 55 | saveOutputIn: 'allHtml.json', 56 | headless: true, 57 | urls: ['src/file2.html'] 58 | }; 59 | return axe(options); 60 | }); 61 | ``` 62 | 63 | #### With Glob patterns 64 | 65 | ```js 66 | var gulp = require('gulp'); 67 | var axe = require('gulp-axe-webdriver'); 68 | 69 | gulp.task('axe', function() { 70 | var options = { 71 | saveOutputIn: 'allHtml.json', 72 | urls: ['src/*.html', 'http://www.foobar-url-2/'] 73 | }; 74 | return axe(options); 75 | }); 76 | ``` 77 | 78 | ### Options 79 | Type: `Object` 80 | 81 | Default value: 82 | ```js 83 | { 84 | errorOnViolation: false, 85 | folderOutputReport: 'aXeReports', 86 | headless: false, 87 | saveOutputIn: '', 88 | scriptTimeout: 60000, 89 | showOnlyViolations: false, 90 | tags: null, 91 | threshold: 0, 92 | urls: [], 93 | verbose: false 94 | } 95 | ``` 96 | 97 | #### a11yCheckOptions 98 | Type: `Object` 99 | 100 | Specifies options to be used by axe.a11yCheck. Will override any other configured options. See [axe-core API documentation](https://github.com/dequelabs/axe-core/blob/master/doc/API.md) for information on its structure. 101 | 102 | #### errorOnViolation 103 | Type: `Boolean` 104 | 105 | Default value: `false` 106 | 107 | It throws an error on violation from aXe, useful for CI environments if you want to break the build if any new violations are in your results. 108 | 109 | #### exclude 110 | Type: `String` 111 | 112 | Default value: `null` 113 | 114 | Add a CSS selector to the list of elements to exclude from analysis. 115 | 116 | #### folderOutputReport 117 | Type: `String` 118 | 119 | Default value: `aXeReports` 120 | 121 | An optional folder to indicate where the output will be saved. 122 | 123 | #### headless 124 | Type: `Boolean` 125 | 126 | Default value: `false` 127 | 128 | To run the Chrome browser in Headless mode. 129 | 130 | #### include 131 | Type: `String` 132 | 133 | Default value: `null` 134 | 135 | Adds a CSS selector to the list of elements to include in analysis. 136 | 137 | #### saveOutputIn 138 | Type: `String` 139 | 140 | Default value: '' 141 | 142 | An optional file to which the results of the accessibility scans will be written as a JSON Array of results objects. 143 | 144 | #### scriptTimeout 145 | Type: `Number` 146 | 147 | Default value: `60000` 148 | 149 | Number of milliseconds for WebDriver to wait before timing out an injected script. 150 | 151 | #### showOnlyViolations 152 | Type: `Boolean` 153 | 154 | Default value: `false` 155 | 156 | Returns only the results with the accessibility issues. 157 | 158 | #### tags 159 | Type: `String` or `Array[String]` 160 | 161 | Default value: `null` 162 | 163 | Which tags to filter violations on. 164 | 165 | #### threshold 166 | Type: `Number` 167 | 168 | Default value: `0` 169 | 170 | A number that represents the maximum number of allowable violations. Each violation represents a rule that fails, it may fail for an number of nodes. It is recommended that this value not be changed. 171 | A negative value will prevent failure whatever the number of violations. 172 | 173 | #### urls 174 | Type: `Array[String]` 175 | 176 | Default value: `[]` 177 | 178 | An Array of URLs that will be tested. The default value is an empty array, you must supply at least one URL in order to successfully complete this task. 179 | 180 | Can also be a glob pattern; 181 | 182 | #### verbose 183 | Type: `Boolean` 184 | 185 | Default value: `false` 186 | 187 | Show status of the analysis. 188 | 189 | ## Release History 190 | 191 | Read the [full changelog](CHANGELOG.md). 192 | 193 | ## License 194 | 195 | MIT © [Felix Zapata](http://github.com/felixzapata) 196 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | /* 2 | * gulp-axe-webdriver 3 | * https://github.com/felixzapata/gulp-axe-webdriver 4 | * 5 | * Copyright (c) 2016 Felix Zapata 6 | * Licensed under the MIT license. 7 | */ 8 | 9 | 'use strict'; 10 | 11 | require('require-dir')('./tasks', { 12 | recurse: true 13 | }); 14 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var path = require('path'); 3 | var fs = require('fs'); 4 | var glob = require('glob'); 5 | var AxeBuilder = require('axe-webdriverjs'); 6 | var WebDriver = require('selenium-webdriver'); 7 | var Promise = require('promise'); 8 | var fileUrl = require('file-url'); 9 | var reporter = require('./lib/reporter'); 10 | var chalk = require('chalk'); 11 | var request = require('then-request'); 12 | var PluginError = require('plugin-error'); 13 | require('chromedriver'); 14 | 15 | module.exports = function (customOptions) { 16 | 17 | var defaultOptions = { 18 | errorOnViolation: false, 19 | folderOutputReport: 'aXeReports', 20 | headless: false, 21 | saveOutputIn: '', 22 | scriptTimeout: 60000, 23 | showOnlyViolations: false, 24 | tags: null, 25 | threshold: 0, 26 | urls: [], 27 | verbose: false 28 | }; 29 | 30 | var violationsCount = 0; 31 | var options = customOptions ? Object.assign(defaultOptions, customOptions) : defaultOptions; 32 | var chromeCapabilities = WebDriver.Capabilities.chrome(); 33 | var chromeOptions = options.headless ? { 'args': ['--headless'] } : {}; 34 | chromeCapabilities.set('chromeOptions', chromeOptions); 35 | var driver = new WebDriver.Builder().withCapabilities(chromeCapabilities).build(); 36 | if (typeof options.scriptTimeout === 'number') { 37 | driver.manage().timeouts().setScriptTimeout(options.scriptTimeout); 38 | } 39 | var tagsAreDefined = (!Array.isArray(options.tags) && options.tags !== null && options.tags !== '') || 40 | (Array.isArray(options.tags) && options.tags.length > 0); 41 | 42 | var isRemoteUrl = function (url) { 43 | return url.indexOf('http://') >= 0 || url.indexOf('https://') >= 0; 44 | }; 45 | 46 | var flatten = function (arr) { 47 | return [].concat.apply([], arr); 48 | }; 49 | 50 | var getUrl = function (url) { 51 | return isRemoteUrl(url) ? url : fileUrl(url); 52 | } 53 | 54 | var checkNotValidUrls = function (result) { 55 | return new Promise(function (resolve) { 56 | request('GET', result.url, function (error) { 57 | result.status = (error) ? 404 : 200; 58 | resolve(result); 59 | }); 60 | }); 61 | } 62 | 63 | var getRemoteUrls = function (result) { 64 | if (isRemoteUrl(result.url)) { 65 | return result; 66 | } 67 | }; 68 | 69 | var getLocalUrls = function (result) { 70 | if (!isRemoteUrl(result.url)) { 71 | return result; 72 | } 73 | }; 74 | 75 | var onlyViolations = function (item) { 76 | return item.violations.length > 0; 77 | }; 78 | 79 | var removePassesValues = function (item) { 80 | delete item.passes; 81 | return item; 82 | }; 83 | 84 | var mergeArray = function (coll, item) { 85 | coll.push(item); 86 | return coll; 87 | }; 88 | 89 | var createResults = function (results) { 90 | var dest = ''; 91 | var localUrls = results.filter(getLocalUrls); 92 | var remoteUrls = results.filter(getRemoteUrls); 93 | var promises = remoteUrls.map(checkNotValidUrls); 94 | var resultsForReporter; 95 | return Promise.all(promises).then(function (results) { 96 | resultsForReporter = localUrls.reduce(mergeArray, results); 97 | if (options.showOnlyViolations) { 98 | resultsForReporter = resultsForReporter.map(removePassesValues).filter(onlyViolations); 99 | } 100 | if (options.saveOutputIn !== '') { 101 | dest = path.join(options.folderOutputReport, options.saveOutputIn); 102 | if(!fs.existsSync(options.folderOutputReport)) { 103 | fs.mkdirSync(options.folderOutputReport); 104 | } 105 | fs.writeFileSync(dest, JSON.stringify(resultsForReporter, null, ' ')); 106 | } 107 | if (options.verbose) { 108 | console.log(chalk.yellow('Preparing results')); 109 | console.log(chalk.yellow('=================')); 110 | } 111 | reporter(resultsForReporter, options.threshold); 112 | if (options.errorOnViolation && violationsCount > 0) { 113 | throw new PluginError( 114 | 'gulp-axe-webdriver', 115 | 'Encountered ' + violationsCount + ' axe violation errors' 116 | ); 117 | } 118 | return driver.quit(); 119 | }); 120 | }; 121 | 122 | var findGlobPatterns = function (urls) { 123 | return urls.map(function (url) { 124 | return isRemoteUrl(url) ? url : glob.sync(url); 125 | }); 126 | }; 127 | 128 | var urls = flatten(findGlobPatterns(options.urls)); 129 | 130 | if (options.verbose) { 131 | console.log(chalk.yellow('Start reading the urls')); 132 | console.log(chalk.yellow('======================')); 133 | } 134 | return Promise.all(urls.map(function (url) { 135 | return new Promise(function (resolve) { 136 | driver 137 | .get(getUrl(url)) 138 | .then(function () { 139 | if (options.verbose) { 140 | console.log(chalk.cyan('Analysis start for: ') + url); 141 | } 142 | var startTimestamp = new Date().getTime(); 143 | var axeBuilder = new AxeBuilder(driver); 144 | 145 | if (options.include) { 146 | axeBuilder.include(options.include); 147 | } 148 | 149 | if (options.exclude) { 150 | axeBuilder.exclude(options.exclude); 151 | } 152 | 153 | if (tagsAreDefined) { 154 | axeBuilder.withTags(options.tags); 155 | } 156 | 157 | if (options.a11yCheckOptions) { 158 | axeBuilder.options(options.a11yCheckOptions); 159 | } 160 | 161 | return axeBuilder.analyze(function (err, results) { 162 | if (err) { 163 | console.log(err); 164 | } 165 | results.url = url; 166 | results.timestamp = new Date().getTime(); 167 | results.time = results.timestamp - startTimestamp; 168 | if (results.violations.length > 0) { 169 | ++violationsCount; 170 | } 171 | if (options.verbose) { 172 | console.log(chalk.cyan('Analysis finished for: ') + url); 173 | } 174 | resolve(results); 175 | }); 176 | }); 177 | }); 178 | 179 | })).then(createResults); 180 | 181 | }; 182 | -------------------------------------------------------------------------------- /lib/reporter.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var chalk = require('chalk'); 4 | 5 | function color(code, str) { 6 | return '\u001b[' + code + 'm' + str + '\u001b[0m'; 7 | } 8 | 9 | function isValidURL(status) { 10 | return status !== 404; 11 | } 12 | 13 | module.exports = function (results, threshold) { 14 | results.forEach(function (result) { 15 | var violations = result.violations; 16 | console.log(chalk.cyan('File to test: ' + result.url)); 17 | if (isValidURL(result.status)) { 18 | if (violations.length) { 19 | if (threshold < 0) { 20 | console.log(chalk.green('Found ' + violations.length + ' accessibility violations: (no threshold)')); 21 | } else if (violations.length > threshold) { 22 | console.log(chalk.red('Found ' + violations.length + ' accessibility violations:')); 23 | } else { 24 | console.log(chalk.green('Found ' + violations.length + ' accessibility violations: (under threshold of ' + threshold + ')')); 25 | } 26 | violations.forEach(function (ruleResult) { 27 | console.log(' ' + color(31, '\u00D7') + ' ' + ruleResult.help); 28 | 29 | ruleResult.nodes.forEach(function (violation, index) { 30 | console.log(' ' + (index + 1) + '. ' + JSON.stringify(violation.target)); 31 | 32 | if (violation.any.length) { 33 | console.log(' Fix any of the following:'); 34 | violation.any.forEach(function (check) { 35 | console.log(' \u2022 ' + check.message); 36 | }); 37 | } 38 | 39 | var alls = violation.all.concat(violation.none); 40 | if (alls.length) { 41 | console.log(' Fix all of the following:'); 42 | alls.forEach(function (check) { 43 | console.log(' \u2022 ' + check.message); 44 | }); 45 | } 46 | console.log('\n'); 47 | }); 48 | }); 49 | } else { 50 | console.log(chalk.green('Found no accessibility violations.')); 51 | } 52 | } else { 53 | console.log(chalk.red('URL not valid for analysis')); 54 | } 55 | }); 56 | }; 57 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gulp-axe-webdriver", 3 | "version": "3.1.3", 4 | "description": "Gulp plugin for aXe utilizing WebDriverJS", 5 | "license": "MIT", 6 | "repository": "felixzapata/gulp-axe-webdriver", 7 | "author": { 8 | "name": "Felix Zapata", 9 | "email": "fzberlinches@gmail.com", 10 | "url": "https://github.com/felixzapata/" 11 | }, 12 | "engines": { 13 | "node": ">=0.10.0" 14 | }, 15 | "scripts": { 16 | "test": "gulp test" 17 | }, 18 | "files": [ 19 | "lib/reporter.js", 20 | "index.js" 21 | ], 22 | "keywords": [ 23 | "gulp", 24 | "gulpplugin", 25 | "a11y", 26 | "unit", 27 | "testing", 28 | "aXe", 29 | "accessibility" 30 | ], 31 | "dependencies": { 32 | "axe-core": "^3.0.1", 33 | "axe-webdriverjs": "^2.2.0", 34 | "chalk": "^1.1.3", 35 | "chromedriver": "2.41.0", 36 | "file-url": "^1.1.0", 37 | "fs-extra": "^0.30.0", 38 | "glob": "^7.0.6", 39 | "natives": "^1.1.6", 40 | "plugin-error": "^1.0.1", 41 | "promise": "^7.1.1", 42 | "request": "^2.76.0", 43 | "selenium-webdriver": "^3.6.0", 44 | "then-request": "^2.2.0" 45 | }, 46 | "devDependencies": { 47 | "conventional-changelog": "^1.1.0", 48 | "eslint-plugin-html": "^1.5.2", 49 | "gulp": "^3.9.1", 50 | "gulp-bump": "^2.1.0", 51 | "gulp-eslint": "^3.0.1", 52 | "gulp-git": "^2.7.0", 53 | "gulp-load-plugins": "^1.2.0", 54 | "gulp-mocha": "^3.0.1", 55 | "gulp-util": "^3.0.7", 56 | "mocha": "^2.5.3", 57 | "require-dir": "^0.3.0", 58 | "run-sequence": "^1.1.5" 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /tasks/bump.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var gulp = require('gulp'); 3 | var $ = require('gulp-load-plugins')(); 4 | var path = require('path'); 5 | 6 | 7 | function doBump(type) { 8 | return function () { 9 | return gulp.src('./package.json') 10 | .pipe($.bump(type).on('error', $.util.log)) 11 | .pipe(gulp.dest('./')); 12 | }; 13 | } 14 | 15 | gulp.task('bump:major', doBump({ 16 | type: 'major' 17 | })); 18 | 19 | gulp.task('bump:minor', doBump({ 20 | type: 'minor' 21 | })); 22 | 23 | gulp.task('bump:patch', doBump({ 24 | type: 'patch' 25 | })); 26 | -------------------------------------------------------------------------------- /tasks/changelog.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var gulp = require('gulp'); 3 | var $ = require('gulp-load-plugins')(); 4 | var conventionalChangelog = require('conventional-changelog'); 5 | var fs = require('fs'); 6 | var file = 'CHANGELOG.md'; 7 | 8 | gulp.task('changelog', function (done) { 9 | 10 | fs.readFile('./package.json', 'utf8', function (err, rawData) { 11 | var buffer = []; 12 | var stream = conventionalChangelog({ 13 | preset: 'angular', 14 | host: 'github', 15 | }, { linkReferences: true }); 16 | 17 | stream.on('end', function () { 18 | var contentToWrite = Buffer.concat(buffer); 19 | 20 | function create() { 21 | var oldChangelog = fs.readFileSync(file); 22 | var fd = fs.openSync(file, 'w+'); 23 | fs.writeSync(fd, contentToWrite, 0, contentToWrite.length); 24 | fs.writeSync(fd, oldChangelog, 0, oldChangelog.length); 25 | fs.close(fd); 26 | } 27 | 28 | function update() { 29 | fs.writeFile(file, contentToWrite); 30 | } 31 | 32 | function existFile() { 33 | return fs.existsSync(file); 34 | } 35 | 36 | if (existFile()) { 37 | create(); 38 | } else { 39 | update(); 40 | } 41 | done(); 42 | }); 43 | 44 | stream.on('data', function (data) { 45 | buffer.push(data); 46 | }); 47 | 48 | }); 49 | }); -------------------------------------------------------------------------------- /tasks/git.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var gulp = require('gulp'); 3 | var $ = require('gulp-load-plugins')(); 4 | var path = require('path'); 5 | var runSequence = require('run-sequence'); 6 | var fs = require('fs'); 7 | 8 | function getPackageJSONVersion() { 9 | return JSON.parse(fs.readFileSync('./package.json', 'utf8')).version; 10 | } 11 | 12 | gulp.task('commit-changes', function () { 13 | var version = getPackageJSONVersion(); 14 | return gulp.src('./package.json') 15 | .pipe($.git.add()) 16 | .pipe($.git.commit('chore(package.json): bump version ' + version)); 17 | }); 18 | 19 | 20 | gulp.task('commit-changelog', function () { 21 | var version = getPackageJSONVersion(); 22 | return gulp.src('CHANGELOG.md') 23 | .pipe($.git.add()) 24 | .pipe($.git.commit('docs(CHANGELOG.md): update CHANGELOG.md with version ' + version)); 25 | }); 26 | 27 | 28 | gulp.task('create-new-tag', function () { 29 | var version = getPackageJSONVersion(); 30 | return $.git.tag(version, 'New version ' + version, function (error) { 31 | if (error) { 32 | $.util.log($.util.colors.red(error)); 33 | } 34 | }); 35 | 36 | 37 | }); 38 | 39 | 40 | gulp.task('git', function (callback) { 41 | runSequence('commit-changelog', 'commit-changes', 'create-new-tag', function (error) { 42 | if (error) { 43 | $.util.log($.util.colors.red(error.message)); 44 | } else { 45 | callback(); 46 | } 47 | }); 48 | }); -------------------------------------------------------------------------------- /tasks/lint.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var gulp = require('gulp'); 3 | var $ = require('gulp-load-plugins')(); 4 | 5 | // Lint JavaScript 6 | gulp.task('lint', function() { 7 | return gulp.src([ 8 | 'index.js', 9 | 'test/test.js' 10 | ]) 11 | .pipe($.eslint()) 12 | .pipe($.eslint.format()) 13 | .pipe($.eslint.failAfterError()); 14 | }); -------------------------------------------------------------------------------- /tasks/mocha.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var gulp = require('gulp'); 3 | var $ = require('gulp-load-plugins')(); 4 | 5 | gulp.task('test', ['lint'], function () { 6 | return gulp.src('test.js', {cwd: './test', read: false}) 7 | .pipe($.mocha()); 8 | }); 9 | -------------------------------------------------------------------------------- /tasks/release.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var gulp = require('gulp'); 3 | var $ = require('gulp-load-plugins')(); 4 | var runSequence = require('run-sequence'); 5 | 6 | function done(error) { 7 | if (error) { 8 | $.util.log($.util.colors.red(error.message)); 9 | } else { 10 | $.util.log($.util.colors.green('Release finished successfully')); 11 | } 12 | 13 | } 14 | 15 | gulp.task('release:major', function (cb) { 16 | runSequence('test', 'bump:major', 'changelog', 'git', done); 17 | }); 18 | 19 | gulp.task('release:minor', function (cb) { 20 | runSequence('test', 'bump:minor', 'changelog', 'git', done); 21 | }); 22 | 23 | gulp.task('release:patch', function () { 24 | runSequence('test', 'bump:patch', 'changelog', 'git', done); 25 | }); -------------------------------------------------------------------------------- /test/fixtures/broken.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |Not a label
10 | 11 |Not an alt
12 |