├── .eslintrc.js ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── cypress.json ├── demo-nuxt ├── .gitignore ├── README.md ├── assets │ ├── README.md │ └── logo.png ├── components │ ├── Logo.vue │ └── README.md ├── layouts │ ├── README.md │ └── default.vue ├── middleware │ └── README.md ├── nuxt.config.js ├── package-lock.json ├── package.json ├── pages │ ├── About.vue │ ├── Contact.vue │ ├── README.md │ ├── Test.vue │ └── index.vue ├── plugins │ ├── README.md │ └── vue-axe.js ├── static │ ├── README.md │ └── favicon.ico ├── store │ └── README.md └── vue-axe.js ├── demo ├── .browserslistrc ├── .gitignore ├── README.md ├── babel.config.js ├── package.json ├── public │ ├── favicon.ico │ └── index.html └── src │ ├── App.vue │ ├── assets │ └── logo.png │ ├── components │ ├── HelloWorld.vue │ └── Logo.vue │ ├── main.js │ ├── router │ └── index.js │ └── views │ ├── About.vue │ ├── Contact.vue │ ├── Home.vue │ └── Test.vue ├── docs ├── .vuepress │ ├── config.js │ └── public │ │ ├── favicon-dark.svg │ │ └── favicon.svg ├── README.md └── guide │ ├── README.md │ ├── api.md │ ├── locales.md │ └── options.md ├── index.d.ts ├── package-lock.json ├── package.json ├── rollup.config.dev.js ├── rollup.config.prod.js ├── src ├── audit.js ├── index.js └── utils.js └── tests └── e2e ├── fixtures └── demo.json └── integration └── home-test.js /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | 'cypress/globals': true, 5 | browser: true, 6 | node: true 7 | }, 8 | extends: [ 9 | 'plugin:vue/recommended', 10 | '@vue/standard' 11 | ], 12 | plugins: [ 13 | 'cypress' 14 | ], 15 | parserOptions: { 16 | parser: 'babel-eslint' 17 | }, 18 | rules: { 19 | 'no-console': 'off' 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | node_modules 3 | .editorconfig 4 | .DS_Store 5 | demo/vue-axe.js 6 | demo/node_modules 7 | demo/dist 8 | demo/CNAME 9 | demo/package-lock.json 10 | cypress 11 | dist 12 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 4 | 5 | ### [2.4.4](https://github.com/vue-a11y/vue-axe/compare/v2.4.3...v2.4.4) (2020-12-11) 6 | 7 | 8 | ### Bug Fixes 9 | 10 | * Console error in Nuxt app ([c6f03a7](https://github.com/vue-a11y/vue-axe/commit/c6f03a7c73829164f95c0319bb57380c84c64897)) 11 | 12 | ### [2.4.3](https://github.com/vue-a11y/vue-axe/compare/v2.4.2...v2.4.3) (2020-11-15) 13 | 14 | ### [2.4.2](https://github.com/vue-a11y/vue-axe/compare/v2.4.1...v2.4.2) (2020-10-18) 15 | 16 | ### [2.4.1](https://github.com/vue-a11y/vue-axe/compare/v2.4.0...v2.4.1) (2020-09-08) 17 | 18 | 19 | ### Bug Fixes 20 | 21 | * peerDependencies axe-core ([aaf2b74](https://github.com/vue-a11y/vue-axe/commit/aaf2b7428c9cf177104682a7b229fb007b2e35d8)) 22 | 23 | ## [2.4.0](https://github.com/vue-a11y/vue-axe/compare/v2.3.1...v2.4.0) (2020-07-31) 24 | 25 | 26 | ### Features 27 | 28 | * add option 'allowConsoleClears' ([a71f444](https://github.com/vue-a11y/vue-axe/commit/a71f44444ae87f9c10ec74b5da66b768ea6944ea)) 29 | 30 | 31 | ### Bug Fixes 32 | 33 | * Title log in console inherit to support light and dark mode ([d4807d1](https://github.com/vue-a11y/vue-axe/commit/d4807d1e32c7f5e8a6e3791d1fe08b852ad350b2)) 34 | 35 | ### [2.3.1](https://github.com/vue-a11y/vue-axe/compare/v2.3.0...v2.3.1) (2020-06-15) 36 | 37 | 38 | ### Bug Fixes 39 | 40 | * **types:** Update index.d.ts ([2d916b5](https://github.com/vue-a11y/vue-axe/commit/2d916b5884165343dce0b9ae93c0e77a13b6c5e5)) 41 | 42 | ## [2.3.0](https://github.com/vue-a11y/vue-axe/compare/v2.2.1...v2.3.0) (2020-06-01) 43 | 44 | 45 | ### Features 46 | 47 | * **checkAndReport:** Add the checked component name in log title ([b6317e9](https://github.com/vue-a11y/vue-axe/commit/b6317e99dd111560654dcc11c19ef48beb8f1508)) 48 | * **checkAndReport:** Check only the updated component ([672f261](https://github.com/vue-a11y/vue-axe/commit/672f261902e9190fcfd172f846e6fe5ae7bef0ae)) 49 | 50 | 51 | ### Bug Fixes 52 | 53 | * Add delay 500 to first check ([ba488b0](https://github.com/vue-a11y/vue-axe/commit/ba488b027b398acce021a1d155a444567b555da1)) 54 | 55 | ### [2.2.2](https://github.com/vue-a11y/vue-axe/compare/v2.2.1...v2.2.2) (2020-06-01) 56 | 57 | ### [2.2.1](https://github.com/vue-a11y/vue-axe/compare/v2.2.0...v2.2.1) (2020-06-01) 58 | 59 | 60 | ### Bug Fixes 61 | 62 | * **docs:** Fixed typo ([64f4b3c](https://github.com/vue-a11y/vue-axe/commit/64f4b3c0ca862141795d8f84fdd1ee41d491de1a)) 63 | 64 | ## [2.2.0](https://github.com/vue-a11y/vue-axe/compare/v2.1.1...v2.2.0) (2020-06-01) 65 | 66 | 67 | ### Features 68 | 69 | * Add documentation ([2f32681](https://github.com/vue-a11y/vue-axe/commit/2f32681e7f0c4822dfddde8194936e06b413a853)) 70 | 71 | 72 | ### Bug Fixes 73 | 74 | * **checkAndReport:** Using double requestAnimationFrame to checkAndReport ([0c59ff9](https://github.com/vue-a11y/vue-axe/commit/0c59ff9dab03e8d1d08ac2e8cbe292d996128d20)) 75 | * **types:** More detailed typescript support ([f097f3a](https://github.com/vue-a11y/vue-axe/commit/f097f3adf044107821701a14f82864fee8fd3c6b)) 76 | 77 | ### [2.1.1](https://github.com/vue-a11y/vue-axe/compare/v2.1.0...v2.1.1) (2020-05-30) 78 | 79 | 80 | ### Bug Fixes 81 | 82 | * **$axe.run:** Generate logs only if there are new violations ([539e41b](https://github.com/vue-a11y/vue-axe/commit/539e41b6a2bf889a925145283455d1b660ac3842)) 83 | 84 | ## [2.1.0](https://github.com/vue-a11y/vue-axe/compare/v2.0.1...v2.1.0) (2020-05-15) 85 | 86 | 87 | ### Features 88 | 89 | * Added "auto" option to manage automatic axe verification. ([efc99e1](https://github.com/vue-a11y/vue-axe/commit/efc99e16edaba73f50846cadf59d456dd19483db)) 90 | 91 | ### [2.0.1](https://github.com/vue-a11y/vue-axe/compare/v2.0.0...v2.0.1) (2020-05-15) 92 | 93 | 94 | ### Bug Fixes 95 | 96 | * address typescript compile issues ([fcf69fc](https://github.com/vue-a11y/vue-axe/commit/fcf69fc5d753d1adb46277effc54e93bda84124b)) 97 | 98 | ## [2.0.0](https://github.com/vue-a11y/vue-axe/compare/v1.2.0...v2.0.0) (2020-05-13) 99 | 100 | ### Breaking Changes 101 | - Now axe-core is a peerDependency (external) 102 | - e.g. `npm i -D axe-core vue-axe` 103 | - `clearConsoleOnUpdate` now is **false by default** 104 | 105 | ## [1.2.0](https://github.com/vue-a11y/vue-axe/compare/v1.0.8...v1.2.0) (2020-05-13) 106 | 107 | ### Features 108 | 109 | * add custom result handler ([8c5c81b](https://github.com/vue-a11y/vue-axe/commit/8c5c81b4f0f7227d9acba3c1a7b6c11692a89bc1)) 110 | * Adding option to register and execute plugins ([fb21d52](https://github.com/vue-a11y/vue-axe/commit/fb21d52805bd7ae2ab4a6098e00ccc5e63ab9173)) 111 | * Adding the "axeRun" method to trigger manually ([a1b856b](https://github.com/vue-a11y/vue-axe/commit/a1b856bc27b297c7199a802e37ac7b84046a3a75)) 112 | 113 | 114 | # [1.1.0](https://github.com/vue-a11y/vue-axe/compare/v1.0.8...v1.1.0) (2020-04-22) 115 | 116 | 117 | ### Features 118 | 119 | * add custom result handler ([8c5c81b](https://github.com/vue-a11y/vue-axe/commit/8c5c81b)) 120 | 121 | 122 | 123 | 124 | ## [1.0.8](https://github.com/vue-a11y/vue-axe/compare/v1.0.7...v1.0.8) (2020-01-24) 125 | 126 | 127 | 128 | 129 | ## [1.0.7](https://github.com/vue-a11y/vue-axe/compare/v1.0.4...v1.0.7) (2018-10-09) 130 | 131 | 132 | 133 | 134 | ## [1.0.6](https://github.com/vue-a11y/vue-axe/compare/v1.0.5...v1.0.6) (2018-10-04) 135 | 136 | 137 | 138 | 139 | ## [1.0.5](https://github.com/vue-a11y/vue-axe/compare/v1.0.4...v1.0.5) (2018-10-04) 140 | 141 | 142 | 143 | 144 | ## [1.0.4](https://github.com/vue-a11y/vue-axe/compare/v1.0.3...v1.0.4) (2018-05-23) 145 | 146 | 147 | 148 | 149 | ## [1.0.3](https://github.com/vue-a11y/vue-axe/compare/v1.0.2...v1.0.3) (2018-05-16) 150 | 151 | 152 | 153 | 154 | ## [1.0.2](https://github.com/vue-a11y/vue-axe/compare/v1.0.1...v1.0.2) (2018-05-16) 155 | 156 | 157 | 158 | 159 | ## [1.0.1](https://github.com/vue-a11y/vue-axe/compare/v1.0.0...v1.0.1) (2018-05-16) 160 | 161 | 162 | 163 | 164 | # [1.0.0](https://github.com/vue-a11y/vue-axe/compare/v0.0.1...v1.0.0) (2018-05-16) 165 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 vue-a11y (Alan Ktquez) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | 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, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vue-axe 2 | 3 | Accessibility auditing for Vue.js applications by running [dequelabs/axe-core](https://github.com/dequelabs/axe-core/) validation on the page you're viewing, `axe-core` will run 1 second after the last VueJS update (with a 5 seconds debounce max wait). Package inspired by [dequelabs/react-axe](https://github.com/dequelabs/react-axe). 4 | 5 | **NOTE: You can use [vue-axe-next](https://github.com/vue-a11y/vue-axe-next) for Vue.js 3** 6 | 7 | ## Links 8 | - [Documentation](https://axe.vue-a11y.com) 9 | - [Demo](https://vue-axe.surge.sh/) 10 | 11 | ## Contributing 12 | - From typos in documentation to coding new features; 13 | - Check the open issues or open a new issue to start a discussion around your feature idea or the bug you found; 14 | - Fork repository, make changes and send a pull request; 15 | 16 | Follow us on Twitter [@vue_a11y](https://twitter.com/vue_a11y) 17 | 18 | **Thank you** 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /cypress.json: -------------------------------------------------------------------------------- 1 | { 2 | "baseUrl": "http://localhost:8080", 3 | "fixturesFolder": "tests/e2e/fixtures", 4 | "integrationFolder": "tests/e2e/integration", 5 | "fileServerFolder": "demo", 6 | "pluginsFile": false, 7 | "supportFile": false, 8 | "videoRecording": false 9 | } -------------------------------------------------------------------------------- /demo-nuxt/.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Node template 3 | # Logs 4 | /logs 5 | *.log 6 | npm-debug.log* 7 | yarn-debug.log* 8 | yarn-error.log* 9 | 10 | # Runtime data 11 | pids 12 | *.pid 13 | *.seed 14 | *.pid.lock 15 | 16 | # Directory for instrumented libs generated by jscoverage/JSCover 17 | lib-cov 18 | 19 | # Coverage directory used by tools like istanbul 20 | coverage 21 | 22 | # nyc test coverage 23 | .nyc_output 24 | 25 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 26 | .grunt 27 | 28 | # Bower dependency directory (https://bower.io/) 29 | bower_components 30 | 31 | # node-waf configuration 32 | .lock-wscript 33 | 34 | # Compiled binary addons (https://nodejs.org/api/addons.html) 35 | build/Release 36 | 37 | # Dependency directories 38 | node_modules/ 39 | jspm_packages/ 40 | 41 | # TypeScript v1 declaration files 42 | typings/ 43 | 44 | # Optional npm cache directory 45 | .npm 46 | 47 | # Optional eslint cache 48 | .eslintcache 49 | 50 | # Optional REPL history 51 | .node_repl_history 52 | 53 | # Output of 'npm pack' 54 | *.tgz 55 | 56 | # Yarn Integrity file 57 | .yarn-integrity 58 | 59 | # dotenv environment variables file 60 | .env 61 | 62 | # parcel-bundler cache (https://parceljs.org/) 63 | .cache 64 | 65 | # next.js build output 66 | .next 67 | 68 | # nuxt.js build output 69 | .nuxt 70 | 71 | # Nuxt generate 72 | dist 73 | 74 | # vuepress build output 75 | .vuepress/dist 76 | 77 | # Serverless directories 78 | .serverless 79 | 80 | # IDE / Editor 81 | .idea 82 | 83 | # Service worker 84 | sw.* 85 | 86 | # macOS 87 | .DS_Store 88 | 89 | # Vim swap files 90 | *.swp 91 | -------------------------------------------------------------------------------- /demo-nuxt/README.md: -------------------------------------------------------------------------------- 1 | # demo-nuxt 2 | 3 | ## Build Setup 4 | 5 | ```bash 6 | # install dependencies 7 | $ npm install 8 | 9 | # serve with hot reload at localhost:3000 10 | $ npm run dev 11 | 12 | # build for production and launch server 13 | $ npm run build 14 | $ npm run start 15 | 16 | # generate static project 17 | $ npm run generate 18 | ``` 19 | 20 | For detailed explanation on how things work, check out [Nuxt.js docs](https://nuxtjs.org). 21 | -------------------------------------------------------------------------------- /demo-nuxt/assets/README.md: -------------------------------------------------------------------------------- 1 | # ASSETS 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains your un-compiled assets such as LESS, SASS, or JavaScript. 6 | 7 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/assets#webpacked). 8 | -------------------------------------------------------------------------------- /demo-nuxt/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vue-a11y/vue-axe/0be16ddfe98271f52e8b17f483178624918e8a3d/demo-nuxt/assets/logo.png -------------------------------------------------------------------------------- /demo-nuxt/components/Logo.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 23 | -------------------------------------------------------------------------------- /demo-nuxt/components/README.md: -------------------------------------------------------------------------------- 1 | # COMPONENTS 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | The components directory contains your Vue.js Components. 6 | 7 | _Nuxt.js doesn't supercharge these components._ 8 | -------------------------------------------------------------------------------- /demo-nuxt/layouts/README.md: -------------------------------------------------------------------------------- 1 | # LAYOUTS 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains your Application Layouts. 6 | 7 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/views#layouts). 8 | -------------------------------------------------------------------------------- /demo-nuxt/layouts/default.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 59 | -------------------------------------------------------------------------------- /demo-nuxt/middleware/README.md: -------------------------------------------------------------------------------- 1 | # MIDDLEWARE 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains your application middleware. 6 | Middleware let you define custom functions that can be run before rendering either a page or a group of pages. 7 | 8 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/routing#middleware). 9 | -------------------------------------------------------------------------------- /demo-nuxt/nuxt.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | // Global page headers (https://go.nuxtjs.dev/config-head) 3 | head: { 4 | title: 'demo-nuxt', 5 | meta: [ 6 | { charset: 'utf-8' }, 7 | { name: 'viewport', content: 'width=device-width, initial-scale=1' }, 8 | { hid: 'description', name: 'description', content: '' } 9 | ], 10 | link: [ 11 | { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' } 12 | ] 13 | }, 14 | 15 | // Global CSS (https://go.nuxtjs.dev/config-css) 16 | css: [ 17 | ], 18 | 19 | // Plugins to run before rendering page (https://go.nuxtjs.dev/config-plugins) 20 | plugins: [ 21 | './plugins/vue-axe' 22 | ], 23 | 24 | // Auto import components (https://go.nuxtjs.dev/config-components) 25 | components: true, 26 | 27 | // Modules for dev and build (recommended) (https://go.nuxtjs.dev/config-modules) 28 | buildModules: [ 29 | ], 30 | 31 | // Modules (https://go.nuxtjs.dev/config-modules) 32 | modules: [ 33 | ], 34 | 35 | // Build Configuration (https://go.nuxtjs.dev/config-build) 36 | build: { 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /demo-nuxt/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "demo-nuxt", 3 | "version": "1.0.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "nuxt", 7 | "build": "nuxt build", 8 | "start": "nuxt start", 9 | "generate": "nuxt generate" 10 | }, 11 | "dependencies": { 12 | "core-js": "^3.6.5", 13 | "nuxt": "^2.14.6" 14 | }, 15 | "devDependencies": {} 16 | } 17 | -------------------------------------------------------------------------------- /demo-nuxt/pages/About.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 24 | -------------------------------------------------------------------------------- /demo-nuxt/pages/Contact.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 19 | 20 | 23 | -------------------------------------------------------------------------------- /demo-nuxt/pages/README.md: -------------------------------------------------------------------------------- 1 | # PAGES 2 | 3 | This directory contains your Application Views and Routes. 4 | The framework reads all the `*.vue` files inside this directory and creates the router of your application. 5 | 6 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/routing). 7 | -------------------------------------------------------------------------------- /demo-nuxt/pages/Test.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 43 | 44 | 49 | -------------------------------------------------------------------------------- /demo-nuxt/pages/index.vue: -------------------------------------------------------------------------------- 1 | 109 | 110 | 141 | 142 | 151 | -------------------------------------------------------------------------------- /demo-nuxt/plugins/README.md: -------------------------------------------------------------------------------- 1 | # PLUGINS 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains Javascript plugins that you want to run before mounting the root Vue.js application. 6 | 7 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/plugins). 8 | -------------------------------------------------------------------------------- /demo-nuxt/plugins/vue-axe.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import VueAxe from '../vue-axe' 3 | 4 | Vue.use(VueAxe) 5 | -------------------------------------------------------------------------------- /demo-nuxt/static/README.md: -------------------------------------------------------------------------------- 1 | # STATIC 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains your static files. 6 | Each file inside this directory is mapped to `/`. 7 | Thus you'd want to delete this README.md before deploying to production. 8 | 9 | Example: `/static/robots.txt` is mapped as `/robots.txt`. 10 | 11 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/assets#static). 12 | -------------------------------------------------------------------------------- /demo-nuxt/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vue-a11y/vue-axe/0be16ddfe98271f52e8b17f483178624918e8a3d/demo-nuxt/static/favicon.ico -------------------------------------------------------------------------------- /demo-nuxt/store/README.md: -------------------------------------------------------------------------------- 1 | # STORE 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains your Vuex Store files. 6 | Vuex Store option is implemented in the Nuxt.js framework. 7 | 8 | Creating a file in this directory automatically activates the option in the framework. 9 | 10 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/vuex-store). 11 | -------------------------------------------------------------------------------- /demo/.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | not dead 4 | -------------------------------------------------------------------------------- /demo/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | pnpm-debug.log* 14 | 15 | # Editor directories and files 16 | .idea 17 | .vscode 18 | *.suo 19 | *.ntvs* 20 | *.njsproj 21 | *.sln 22 | *.sw? 23 | -------------------------------------------------------------------------------- /demo/README.md: -------------------------------------------------------------------------------- 1 | # demo 2 | 3 | ## Project setup 4 | ``` 5 | npm install 6 | ``` 7 | 8 | ### Compiles and hot-reloads for development 9 | ``` 10 | npm run serve 11 | ``` 12 | 13 | ### Compiles and minifies for production 14 | ``` 15 | npm run build 16 | ``` 17 | 18 | ### Customize configuration 19 | See [Configuration Reference](https://cli.vuejs.org/config/). 20 | -------------------------------------------------------------------------------- /demo/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /demo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "demo", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build" 8 | }, 9 | "dependencies": { 10 | "core-js": "^3.6.5", 11 | "vue": "^2.6.11", 12 | "vue-router": "^3.2.0" 13 | }, 14 | "devDependencies": { 15 | "@vue/cli-plugin-babel": "~4.4.0", 16 | "@vue/cli-plugin-router": "~4.4.0", 17 | "@vue/cli-service": "~4.4.0", 18 | "vue-template-compiler": "^2.6.11" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /demo/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vue-a11y/vue-axe/0be16ddfe98271f52e8b17f483178624918e8a3d/demo/public/favicon.ico -------------------------------------------------------------------------------- /demo/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /demo/src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 13 | 39 | -------------------------------------------------------------------------------- /demo/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vue-a11y/vue-axe/0be16ddfe98271f52e8b17f483178624918e8a3d/demo/src/assets/logo.png -------------------------------------------------------------------------------- /demo/src/components/HelloWorld.vue: -------------------------------------------------------------------------------- 1 | 32 | 33 | 41 | 42 | 43 | 59 | -------------------------------------------------------------------------------- /demo/src/components/Logo.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 14 | -------------------------------------------------------------------------------- /demo/src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import VueAxe from '../vue-axe' 3 | import App from './App.vue' 4 | import router from './router' 5 | 6 | // Use this plugin only development => if (process.env.NODE_ENV === 'development') 7 | Vue.use(VueAxe) 8 | Vue.config.productionTip = false 9 | 10 | new Vue({ 11 | router, 12 | render: h => h(App) 13 | }).$mount('#app') 14 | -------------------------------------------------------------------------------- /demo/src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import VueRouter from 'vue-router' 3 | 4 | Vue.use(VueRouter) 5 | 6 | const router = new VueRouter({ 7 | mode: 'history', 8 | routes: [ 9 | { 10 | name: 'home', 11 | path: '/', 12 | component: () => import('@/views/Home'), 13 | meta: { 14 | announcer: 'Home page' 15 | } 16 | }, 17 | { 18 | name: 'about', 19 | path: '/about', 20 | component: () => import('@/views/About'), 21 | meta: { 22 | announcer: 'About page' 23 | } 24 | }, 25 | { 26 | name: 'contact', 27 | path: '/contact', 28 | component: () => import('@/views/Contact'), 29 | meta: { 30 | announcer: 'Contact page' 31 | } 32 | }, 33 | { 34 | name: 'test', 35 | path: '/test', 36 | component: () => import('@/views/Test'), 37 | meta: { 38 | announcer: 'Test page' 39 | } 40 | } 41 | ] 42 | }) 43 | 44 | export default router 45 | -------------------------------------------------------------------------------- /demo/src/views/About.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 24 | -------------------------------------------------------------------------------- /demo/src/views/Contact.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 19 | 20 | 23 | -------------------------------------------------------------------------------- /demo/src/views/Home.vue: -------------------------------------------------------------------------------- 1 | 109 | 110 | 141 | 142 | 151 | -------------------------------------------------------------------------------- /demo/src/views/Test.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 43 | 44 | 49 | -------------------------------------------------------------------------------- /docs/.vuepress/config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | theme: 'default-vue-a11y', 3 | title: 'Vue Axe', 4 | description: 'Accessibility auditing for Vue.js applications.', 5 | head: [ 6 | ['meta', { name: 'theme-color', content: '#fff' }], 7 | ['link', { rel: 'icon', type: 'image/svg+xml', href: '/favicon.svg' }] 8 | ], 9 | themeConfig: { 10 | home: false, 11 | repo: 'vue-a11y/vue-axe', 12 | docsDir: 'docs', 13 | docsBranch: 'master', 14 | editLinks: true, 15 | locales: { 16 | '/': { 17 | editLinkText: 'Edit this page on GitHub', 18 | sidebar: [ 19 | '/', 20 | { 21 | title: "Guide", 22 | collapsable: false, 23 | children: [ 24 | '/guide/', 25 | '/guide/options.md', 26 | '/guide/api.md', 27 | '/guide/locales.md', 28 | ] 29 | } 30 | ] 31 | } 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /docs/.vuepress/public/favicon-dark.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/.vuepress/public/favicon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | Accessibility auditing for Vue.js applications by running dequelabs/axe-core validation on the page you're viewing, axe-core will run 1 second after the last VueJS update (with a 5 seconds debounce max wait). 4 | - Package inspired by [dequelabs/react-axe](https://github.com/dequelabs/react-axe). 5 | 6 | ## Links 7 | - [Demo](https://vue-axe.surge.sh/) -------------------------------------------------------------------------------- /docs/guide/README.md: -------------------------------------------------------------------------------- 1 | # Setup 2 | 3 | ## Installation 4 | 5 | ```shell 6 | npm install -D axe-core vue-axe 7 | # or 8 | yarn add -D axe-core vue-axe 9 | ``` 10 | 11 | ## Usage 12 | 13 | Add to your `src/main.js` 14 | 15 | ```javascript 16 | if (process.env.NODE_ENV === 'development') { 17 | const VueAxe = require('vue-axe').default 18 | Vue.use(VueAxe) 19 | } 20 | ``` 21 | 22 | ### Nuxt.js 23 | 24 | Create plugin file `plugins/axe.js` 25 | 26 | ```javascript 27 | import Vue from 'vue' 28 | 29 | if (process.env.NODE_ENV === 'development') { 30 | const VueAxe = require('vue-axe').default 31 | Vue.use(VueAxe) 32 | } 33 | 34 | ``` 35 | 36 | In config file `nuxt.config.js` 37 | 38 | ```javascript 39 | ... 40 | plugins: [ 41 | { src: '~/plugins/axe.js', mode: 'client' } 42 | ] 43 | ``` -------------------------------------------------------------------------------- /docs/guide/api.md: -------------------------------------------------------------------------------- 1 | # API 2 | 3 | The `$axe` is available on the property injected into the Vue instance, so it is available everywhere in your application. 4 | 5 | ## Run 6 | 7 | To execute the `$axe.run` method to check manually your document or any desired HTMLElement. 8 | 9 | ### `$axe.run` 10 | 11 | | Key | Type | Default 12 | | ------------- | ------------------------ | ---------------------------------- 13 | | clearConsole | Boolean | The same as `clearConsoleOnUpdate` 14 | | element | Document or HTMLElement | `document` 15 | | label | Strong | `Run manually` 16 | 17 | ```js 18 | methods: { 19 | axeRun() { 20 | this.$axe.run({ 21 | clearConsole: true, 22 | element: this.$el, // or document, document.querySelector('.selector'), ... 23 | label: 'Logo component' 24 | }) 25 | } 26 | } 27 | ``` 28 | 29 | ## Plugins 30 | 31 | Use the `$axe.plugins` method to have access to registered plugins. 32 | 33 | ::: tip 34 | To see how to register your plugins [click here](/guide/options.html#plugins) 35 | ::: 36 | 37 | ### `$axe.plugins` 38 | 39 |
40 | 41 | ```js 42 | methods: { 43 | handle () { 44 | this.$axe.plugins.myPlugin.run() 45 | } 46 | } 47 | ``` 48 | 49 | ::: tip 50 | Learn more about [Axe docs: Plugins](https://github.com/dequelabs/axe-core/blob/master/doc/plugins.md) 51 | ::: 52 | 53 | -------------------------------------------------------------------------------- /docs/guide/locales.md: -------------------------------------------------------------------------------- 1 | # Locales 2 | 3 | It's possible easily through the settings, to define the language that will be used for the logs. 4 | 5 | ::: tip 6 | axe-core already has several languages ​​available. 7 | [See axe-core locales](https://github.com/dequelabs/axe-core/tree/develop/locales) 8 | ::: 9 | 10 | ```js 11 | import ptBR from 'axe-core/locales/pt_BR.json' 12 | 13 | Vue.use(VueAxe, { 14 | config: { 15 | locale: ptBR 16 | } 17 | }) 18 | ``` 19 | -------------------------------------------------------------------------------- /docs/guide/options.md: -------------------------------------------------------------------------------- 1 | # Options 2 | 3 | ## auto 4 | 5 | | Type | Default | 6 | | -------- | -------- | 7 | | Boolean | `true` | 8 | 9 | If false disables automatic verification. Only checks with `$axe.run` 10 | 11 | 12 | ```js 13 | Vue.use(VueAxe, { 14 | auto: false // Disable auto check. 15 | }) 16 | ``` 17 | 18 | ::: tip 19 | To check manually, use [$axe.run](/guide/api.html#run) 20 | ::: 21 | 22 | ## allowConsoleClears 23 | 24 | | Type | Default | 25 | | -------- | -------- | 26 | | Boolean | `true` | 27 | 28 | If false, disables all console clears (overriding `clearConsoleOnUpdate`). 29 | 30 | ```js 31 | Vue.use(VueAxe, { 32 | allowConsoleClears: false // disable all console clears 33 | }) 34 | ``` 35 | 36 | ## clearConsoleOnUpdate 37 | 38 | | Type | Default | 39 | | -------- | -------- | 40 | | Boolean | `false` | 41 | 42 | If true, clean the console each time the component is updated. No effect if `allowConsoleClears = false`. 43 | 44 | ```js 45 | Vue.use(VueAxe, { 46 | clearConsoleOnUpdate: true 47 | }) 48 | ``` 49 | 50 | ## customResultHandler 51 | 52 | | Type | Default | 53 | | -------- | ------------------------ | 54 | | Function | `standardResultHandler` | 55 | 56 | The `customResultHandler` config property expects a callback like the `axeCore.run()` callback ([see documentation](https://github.com/dequelabs/axe-core/blob/master/doc/API.md#parameters-axerun)). It will be triggered after each call to `axeCore.run()`. 57 | 58 | ```js 59 | Vue.use(VueAxe, { 60 | customResultHandler: (error, results) => { 61 | results.violations.forEach(violation => console.log(violation)) 62 | } 63 | }) 64 | ``` 65 | 66 | ## config 67 | 68 | | Type | Default | 69 | | -------- | ---------------------------------------- | 70 | | Object | `{ branding: { application: 'vue-axe' }` | 71 | 72 | To configure the format of the data used by axe. This can be used to add new rules, which must be registered with the library to execute. 73 | Provide your 74 | 75 | ::: tip 76 | Learn more about [Axe-core configuration](https://github.com/dequelabs/axe-core/blob/master/doc/API.md#api-name-axeconfigure) 77 | ::: 78 | 79 | ## runOptions 80 | 81 | | Type | Default | 82 | | -------- | --------------------------------------------------------------- | 83 | | Object | `{ runOptions: { reporter: 'v2', resultTypes: ['violations'] }` | 84 | 85 | Flexible way to configure how `axeCore.run()` operates. 86 | 87 | ::: tip 88 | Learn more about [Axe-core runtime options](https://github.com/dequelabs/axe-core/blob/master/doc/API.md#options-parameter) 89 | ::: 90 | 91 | ## delay 92 | 93 | | Type | Default | 94 | | -------- | -------- | 95 | | Number | `500` | 96 | 97 | Used only to delay the first check. 98 | 99 | ## style 100 | 101 | | Type | 102 | | -------- | 103 | | Object | 104 | 105 | Customize style for console logs. 106 | 107 | | key | description | 108 | | ----------- | ----------------------------- | 109 | | head | Style title "New axe issues" | 110 | | boldCourier | Font style | 111 | | critical | Critical logs badge | 112 | | serious | Serious logs badge | 113 | | moderate | Moderate logs badge | 114 | | minor | Minor logs badge | 115 | | title | Issue title | 116 | | url | Issue URL | 117 | 118 | ::: tip 119 | To see more about [default style](https://github.com/vue-a11y/vue-axe/blob/master/src/index.js#L22) 120 | ::: 121 | 122 | ## plugins 123 | 124 | | Type | 125 | | -------- | 126 | | Array | 127 | 128 | Register Axe plugins. 129 | 130 | ```js 131 | import { myPlugin, myPlugin2 } from '@/plugins/axe' 132 | 133 | Vue.use(VueAxe, { 134 | plugins: [myPlugin, myPlugin2] 135 | }) 136 | ``` 137 | 138 | ::: tip 139 | Learn more about [Axe docs: Plugins](https://github.com/dequelabs/axe-core/blob/master/doc/plugins.md) 140 | ::: 141 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | import { PluginFunction } from 'vue'; 2 | 3 | export interface RunOptions { 4 | clearConsole: boolean; 5 | element: Document | HTMLElement; 6 | } 7 | 8 | export interface AxeConfig { 9 | /** 10 | * mixed(optional) Used to set the branding of the helpUrls 11 | */ 12 | branding?: { 13 | /** 14 | * string(optional) sets the brand string - default "axe" 15 | */ 16 | brand?: string; 17 | /** 18 | * string(optional) sets the application string - default "axeAPI" 19 | */ 20 | application?: string; 21 | }; 22 | /** 23 | * Used to set the output format that the axe.run function will pass to the callback function 24 | */ 25 | reporter?: string; 26 | /** 27 | * Used to add checks to the list of checks used by rules, or to override the properties of existing checks 28 | */ 29 | checks?: any; 30 | /** 31 | * Used to add rules to the existing set of rules, or to override the properties of existing rules 32 | */ 33 | rules?: any; 34 | /** 35 | * A locale object to apply (at runtime) to all rules and checks, in the same shape as /locales/*.json. 36 | */ 37 | locale?: any; 38 | /** 39 | * Set the compatible version of a custom rule with the current axe version. Compatible versions are all patch and minor updates that are the same as, or newer than those of the `axeVersion` property 40 | */ 41 | axeVersion?: string; 42 | } 43 | 44 | export interface AxeRunOptions { 45 | /** 46 | * Limit which rules are executed, based on names or tags 47 | */ 48 | runOnly?: any; 49 | /** 50 | * Allow customizing a rule's properties (including { enable: false }) 51 | */ 52 | rules?: any; 53 | /** 54 | * Which reporter to use 55 | */ 56 | reporter?: string; 57 | /** 58 | * Limit which result types are processed and aggregated 59 | */ 60 | resultTypes?: any; 61 | /** 62 | * Return xpath selectors for elements 63 | */ 64 | xpath?: boolean; 65 | /** 66 | * Use absolute paths when creating element selectors 67 | */ 68 | absolutePaths?: boolean; 69 | /** 70 | * Tell axe to run inside iframes 71 | */ 72 | iframes?: boolean; 73 | /** 74 | * Return element references in addition to the target 75 | */ 76 | elementRef?: boolean; 77 | /** 78 | * Scrolls elements back to before axe started 79 | */ 80 | restoreScroll?: boolean; 81 | /** 82 | * How long (in milliseconds) axe waits for a response from embedded frames before timing out 83 | */ 84 | frameWaitTime?: number; 85 | /** 86 | * Any additional assets (eg: cssom) to preload before running rules 87 | */ 88 | preload?: boolean; 89 | /** 90 | * Log rule performance metrics to the console 91 | */ 92 | performanceTimer?: boolean; 93 | } 94 | 95 | export interface VueAxeStyle { 96 | head?: string; 97 | boldCourier?: string; 98 | moderate?: string; 99 | critical?: string; 100 | serious?: string; 101 | minor?: string; 102 | title?: string; 103 | url?: string; 104 | } 105 | 106 | export interface VueAxeOptions { 107 | /** 108 | * Disables automatic verification. Only checks with $axe.run 109 | */ 110 | auto?: boolean; 111 | /** 112 | * Clears the console each time vue-axe runs 113 | */ 114 | clearConsoleOnUpdate?: boolean; 115 | /** 116 | * Provide your Axe-core configuration 117 | */ 118 | config?: AxeConfig; 119 | /** 120 | * Provide your Axe-core runtime options 121 | */ 122 | runOptions?: AxeRunOptions; 123 | /** 124 | * Used to delay the first check. - `Millisecond` 125 | */ 126 | delay?: number; 127 | /** 128 | * 129 | */ 130 | style?: VueAxeStyle; 131 | /** 132 | * Register Axe plugins 133 | */ 134 | plugins?: any[]; 135 | /** 136 | * Handle the results. (This may be needed for automated tests) 137 | */ 138 | customResultHandler?: (error: any, results: any) => any; 139 | } 140 | 141 | export interface VueAxe { 142 | run({ clearConsole, element }: RunOptions): void; 143 | plugins: Record; 144 | } 145 | 146 | declare module 'vue/types/vue' { 147 | interface Vue { 148 | $axe: VueAxe; 149 | } 150 | } 151 | 152 | declare class VueAxePlugin { 153 | static install: PluginFunction; 154 | } 155 | 156 | export default VueAxePlugin; 157 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-axe", 3 | "version": "2.4.4", 4 | "description": "Dynamic accessibility analysis for Vue.js using axe-core", 5 | "main": "dist/vue-axe.umd.js", 6 | "module": "dist/vue-axe.esm.js", 7 | "unpkg": "dist/vue-axe.min.js", 8 | "types": "index.d.ts", 9 | "scripts": { 10 | "dev": "rollup --config rollup.config.dev.js --watch", 11 | "build": "npm run build:umd & npm run build:es & npm run build:unpkg", 12 | "build:umd": "rollup --config rollup.config.prod.js --format umd --file dist/vue-axe.umd.js", 13 | "build:es": "rollup --config rollup.config.prod.js --format es --file dist/vue-axe.esm.js", 14 | "build:unpkg": "rollup --config rollup.config.prod.js --format iife --file dist/vue-axe.min.js", 15 | "demo:dev": "cd demo && npm run serve", 16 | "demo:build": "cd demo && npm run build", 17 | "demo:publish": "surge ./demo/dist https://vue-axe.surge.sh/", 18 | "docs:dev": "./node_modules/.bin/vuepress dev docs --no-cache", 19 | "docs:build": "./node_modules/.bin/vuepress build docs --no-cache && echo axe.vue-a11y.com >> docs/.vuepress/dist/CNAME", 20 | "docs:publish": "gh-pages -d docs/.vuepress/dist", 21 | "release": "standard-version", 22 | "test:e2e": "node_modules/.bin/cypress run --headless", 23 | "test:e2e:open": "node_modules/.bin/cypress open", 24 | "project:publish": "git push --tags origin master && npm run build && npm publish" 25 | }, 26 | "repository": { 27 | "type": "git", 28 | "url": "git+https://github.com/vue-a11y/vue-axe.git" 29 | }, 30 | "keywords": [ 31 | "accessibility", 32 | "a11y", 33 | "vue.js", 34 | "test", 35 | "auditing" 36 | ], 37 | "author": "Alan Ktquez (https://github.com/ktquez/)", 38 | "contributors": [ 39 | { 40 | "name": "Josh", 41 | "url": "https://github.com/MechJosh0" 42 | }, 43 | { 44 | "name": "Maarten Van Hoof", 45 | "email": "info@mrtnvh.com", 46 | "url": "https://github.com/vanhoofmaarten" 47 | }, 48 | { 49 | "name": "Takayuki Shimada", 50 | "email": "taka@tsmd.jp", 51 | "url": "https://github.com/tsmd" 52 | }, 53 | { 54 | "name": "Michael Große", 55 | "url": "https://github.com/micgro42" 56 | } 57 | ], 58 | "license": "MIT", 59 | "bugs": { 60 | "url": "https://github.com/vue-a11y/vue-axe/issues" 61 | }, 62 | "homepage": "https://github.com/vue-a11y/vue-axe#readme", 63 | "devDependencies": { 64 | "@rollup/plugin-buble": "^0.21.3", 65 | "@rollup/plugin-commonjs": "^11.1.0", 66 | "@rollup/plugin-node-resolve": "^7.1.3", 67 | "@rollup/plugin-replace": "^2.4.2", 68 | "@vue/eslint-config-standard": "^5.1.2", 69 | "axe-core": "^3.5.6", 70 | "babel-eslint": "^10.1.0", 71 | "chokidar": "^3.6.0", 72 | "cypress": "^2.1.0", 73 | "deepmerge": "^4.3.1", 74 | "eslint": "^7.32.0", 75 | "eslint-plugin-cypress": "^2.15.2", 76 | "eslint-plugin-import": "^2.30.0", 77 | "eslint-plugin-node": "^11.1.0", 78 | "eslint-plugin-promise": "^4.3.1", 79 | "eslint-plugin-standard": "^4.1.0", 80 | "eslint-plugin-vue": "^6.2.2", 81 | "gh-pages": "^3.2.3", 82 | "rollup": "^2.79.2", 83 | "rollup-plugin-eslint": "^7.0.0", 84 | "rollup-plugin-peer-deps-external": "^2.2.4", 85 | "rollup-plugin-terser": "^5.3.1", 86 | "rollup-plugin-vue": "^5.1.9", 87 | "standard-version": "^8.0.2", 88 | "vue": "^2.7.16", 89 | "vue-template-compiler": "^2.7.16", 90 | "vuepress": "^1.9.10", 91 | "vuepress-theme-default-vue-a11y": "^0.1.15", 92 | "watchpack": "^1.7.5" 93 | }, 94 | "peerDependencies": { 95 | "axe-core": "3.x || 4.x" 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /rollup.config.dev.js: -------------------------------------------------------------------------------- 1 | import buble from '@rollup/plugin-buble' 2 | import commonJs from '@rollup/plugin-commonjs' 3 | import resolve from '@rollup/plugin-node-resolve' 4 | import chokidar from 'chokidar' 5 | import { eslint } from 'rollup-plugin-eslint' 6 | import vue from 'rollup-plugin-vue' 7 | 8 | export default { 9 | input: 'src/index.js', 10 | watch: { 11 | chokidar, 12 | include: ['src/**'] 13 | }, 14 | plugins: [ 15 | resolve(), 16 | commonJs(), 17 | eslint({ 18 | include: './src/**' 19 | }), 20 | buble({ 21 | objectAssign: true 22 | }), 23 | vue() 24 | ], 25 | output: [ 26 | { 27 | name: 'VueAxe', 28 | file: 'demo/vue-axe.js', 29 | format: 'umd', 30 | exports: 'named', 31 | globals: { 32 | 'axe-core': 'axeCore' 33 | } 34 | } 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /rollup.config.prod.js: -------------------------------------------------------------------------------- 1 | import buble from '@rollup/plugin-buble' 2 | import commonjs from '@rollup/plugin-commonjs' 3 | import resolve from '@rollup/plugin-node-resolve' 4 | import replace from '@rollup/plugin-replace' 5 | import peerDepsExternal from 'rollup-plugin-peer-deps-external' 6 | import { terser } from 'rollup-plugin-terser' 7 | import vue from 'rollup-plugin-vue' 8 | 9 | export default { 10 | input: 'src/index.js', 11 | plugins: [ 12 | peerDepsExternal(), 13 | resolve(), 14 | commonjs(), 15 | replace({ 16 | 'process.env.NODE_ENV': JSON.stringify('production') 17 | }), 18 | vue(), 19 | buble({ 20 | objectAssign: true 21 | }), 22 | terser() 23 | ], 24 | output: { 25 | name: 'VueAxe', 26 | exports: 'named', 27 | globals: { 28 | 'axe-core': 'axeCore' 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/audit.js: -------------------------------------------------------------------------------- 1 | import axeCore from 'axe-core' 2 | 3 | let cache = {} 4 | let style = {} 5 | let lastNotification = '' 6 | 7 | const deferred = {} 8 | const impacts = [...axeCore.constants.impact].reverse() 9 | 10 | export function checkAndReport (options, node, label) { 11 | const deferred = createDeferred() 12 | style = { ...options.style } 13 | 14 | axeCore.run(node || document, options.runOptions, (error, results) => { 15 | if (error) deferred.reject(error) 16 | if (results && !results.violations.length) return 17 | if (JSON.stringify(results.violations) === lastNotification) return 18 | 19 | if (options.clearConsoleOnUpdate) { 20 | console.clear() 21 | } 22 | 23 | options.customResultHandler ? options.customResultHandler(error, results) : standardResultHandler(error, results, label) 24 | deferred.resolve() 25 | lastNotification = JSON.stringify(results.violations) 26 | }) 27 | return deferred.promise 28 | } 29 | 30 | const standardResultHandler = function (errorInfo, results, label) { 31 | results.violations = results.violations.filter(result => { 32 | result.nodes = result.nodes.filter(node => { 33 | const key = node.target.toString() + result.id 34 | const retVal = (!cache[key]) 35 | cache[key] = key 36 | return retVal 37 | }) 38 | return (!!result.nodes.length) 39 | }) 40 | 41 | if (results.violations.length) { 42 | const violations = sortViolations(results.violations) 43 | console.group(`%cAxe issues ${label ? '- ' + label : ''}`, style.head) 44 | violations.forEach(result => { 45 | console.groupCollapsed('%c%s%c %s %s %c%s', style[result.impact || 'minor'], result.impact, style.title, result.help, '\n', style.url, result.helpUrl) 46 | result.nodes.forEach(node => { 47 | failureSummary(node, 'any') 48 | failureSummary(node, 'none') 49 | }) 50 | console.groupEnd() 51 | }) 52 | console.groupEnd() 53 | } 54 | } 55 | 56 | export function resetCache () { 57 | cache = {} 58 | } 59 | 60 | export function resetLastNotification () { 61 | lastNotification = '' 62 | } 63 | 64 | export const draf = (cb) => requestAnimationFrame(() => requestAnimationFrame(cb)) 65 | 66 | function sortViolations (violations) { 67 | let sorted = [] 68 | impacts.forEach(impact => { 69 | sorted = [...sorted, ...violations.filter(violation => violation.impact === impact)] 70 | }) 71 | return sorted 72 | } 73 | 74 | function createDeferred () { 75 | deferred.promise = new Promise((resolve, reject) => { 76 | deferred.resolve = resolve 77 | deferred.reject = reject 78 | }) 79 | return deferred 80 | } 81 | 82 | function failureSummary (node, key) { 83 | if (node[key].length > 0) { 84 | logElement(node, console.groupCollapsed) 85 | logHtml(node) 86 | logFailureMessage(node, key) 87 | 88 | var relatedNodes = [] 89 | node[key].forEach(check => { 90 | relatedNodes = relatedNodes.concat(check.relatedNodes) 91 | }) 92 | 93 | if (relatedNodes.length > 0) { 94 | console.groupCollapsed('Related nodes') 95 | relatedNodes.forEach(relatedNode => { 96 | logElement(relatedNode, console.log) 97 | logHtml(relatedNode) 98 | }) 99 | console.groupEnd() 100 | } 101 | console.groupEnd() 102 | } 103 | } 104 | 105 | function logElement (node, logFn) { 106 | const el = document.querySelector(node.target.toString()) 107 | if (!el) { 108 | return logFn('Selector: %c%s', style.boldCourier, node.target.toString()) 109 | } 110 | logFn('Element: %o', el) 111 | } 112 | 113 | function logHtml (node) { 114 | console.log('HTML: %c%s', style.boldCourier, node.html) 115 | } 116 | 117 | function logFailureMessage (node, key) { 118 | const message = axeCore._audit.data.failureSummaries[key] 119 | .failureMessage(node[key] 120 | .map(function (check) { 121 | return check.message || '' 122 | })) 123 | console.error(message) 124 | } 125 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import axeCore from 'axe-core' 2 | import merge from 'deepmerge' 3 | import { checkAndReport, resetCache } from './audit' 4 | import { clear, defaultOptions, draf } from './utils' 5 | 6 | export default function install (Vue, options = {}) { 7 | // Browser only 8 | if (typeof window === 'undefined') return 9 | 10 | // merge options 11 | options = merge(defaultOptions, options) 12 | 13 | // set config 14 | axeCore.configure({ ...options.config }) 15 | 16 | // register plugins 17 | options.plugins.forEach(plugin => axeCore.registerPlugin(plugin)) 18 | 19 | // vue-axe methods in Vue Instance 20 | Vue.prototype.$axe = { 21 | run ({ clearConsole = options.clearConsoleOnUpdate, element, label } = {}) { 22 | clear(clearConsole, options) 23 | draf(() => checkAndReport(options, element, (label || 'Run manually'))) 24 | }, 25 | plugins: axeCore.plugins 26 | } 27 | 28 | // if false, disable automatic verification 29 | if (!options.auto) return 30 | 31 | function axeRun () { 32 | const componentsName = this.$options.name || '' 33 | resetCache() 34 | draf(() => checkAndReport(options, this.$el, componentsName)) 35 | } 36 | 37 | // Rechecking when updating specific component 38 | let timeout = null 39 | Vue.mixin({ 40 | updated () { 41 | timeout && clearTimeout(timeout) 42 | timeout = setTimeout(axeRun.bind(this), 2500) 43 | }, 44 | // Used for change of route 45 | beforeDestroy () { 46 | clear(true, options) 47 | } 48 | }) 49 | 50 | // First check 51 | setTimeout(() => draf(() => checkAndReport(options, document, 'App')), options.delay) 52 | } 53 | -------------------------------------------------------------------------------- /src/utils.js: -------------------------------------------------------------------------------- 1 | import { resetCache, resetLastNotification } from './audit' 2 | 3 | export const draf = (cb) => requestAnimationFrame(() => requestAnimationFrame(cb)) 4 | 5 | export const defaultOptions = { 6 | auto: true, 7 | allowConsoleClears: true, 8 | clearConsoleOnUpdate: false, 9 | delay: 500, 10 | config: { 11 | branding: { 12 | application: 'vue-axe' 13 | } 14 | }, 15 | runOptions: { 16 | reporter: 'v2', 17 | resultTypes: ['violations'] 18 | }, 19 | style: { 20 | head: 'padding:6px;font-size:20px;font-weight:bold;', 21 | boldCourier: 'font-weight:bold;font-family:Courier;', 22 | moderate: 'padding:2px 4px;border-radius:5px;background-color:#FFBA52;color:#222;font-weight:normal;', 23 | critical: 'padding:2px 4px;border-radius:5px;background-color:#AD0000;color:#fff;font-weight:normal;', 24 | serious: 'padding:2px 4px;border-radius:5px;background-color:#333;color:#FFCE85;font-weight:normal;', 25 | minor: 'padding:2px 4px;border-radius:5px;background-color:#333;color:#FFCE85;font-weight:normal;', 26 | title: 'font-color:black;font-weight:bold;', 27 | url: 'font-color:#4D4D4D;font-weight:normal;' 28 | }, 29 | plugins: [] 30 | } 31 | 32 | export function clear (forceClear = false, options) { 33 | resetCache() 34 | if (forceClear || options.clearConsoleOnUpdate) { 35 | resetLastNotification() 36 | if (options.allowConsoleClears) { 37 | console.clear() 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /tests/e2e/fixtures/demo.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Using fixtures to represent data", 3 | "email": "hello@cypress.io", 4 | "body": "Fixtures are a great way to mock data for responses to routes" 5 | } -------------------------------------------------------------------------------- /tests/e2e/integration/home-test.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import VueAxe from '../../../dist/vue-axe' 3 | 4 | const config = { 5 | rules: [ 6 | { id: 'heading-order', enabled: true }, 7 | { id: 'label-title-only', enabled: true }, 8 | { id: 'link-in-text-block', enabled: true }, 9 | { id: 'region', enabled: true }, 10 | { id: 'skip-link', enabled: true }, 11 | { id: 'help-same-as-label', enabled: true } 12 | ] 13 | } 14 | 15 | describe('Home-VueAxe', () => { 16 | beforeEach(() => { 17 | cy.visit('/') 18 | }) 19 | 20 | it('Should display the main headline', () => { 21 | cy.get('[data-va="main header"]').should('be.visible') 22 | }) 23 | 24 | it('should assert that page content is correct', () => { 25 | cy.get('[data-va="main header"]').should('contain', 'Welcome') 26 | }) 27 | 28 | it('should run axe in the context of the document', () => { 29 | cy.visit('/') 30 | .then(win => { 31 | console.log(win.console) 32 | cy.spy(win.console, 'group') 33 | cy.spy(win.console, 'groupCollapsed') 34 | cy.spy(win.console, 'groupEnd') 35 | 36 | VueAxe(Vue, config) 37 | .then(() => { 38 | expect(win.console.group).to.be.calledWith('%cNew aXe issues', 'padding:6px;font-size:20px;background-color:#8ba6c5;color:#fff;font-weight:bold;') 39 | expect(win.console.groupCollapsed).to.be.calledWith('%c%s: %c%s %s') 40 | expect(win.console.groupEnd).to.be.called 41 | }) 42 | }) 43 | }) 44 | }) 45 | --------------------------------------------------------------------------------