Skip to content

Commit 9c5468a

Browse files
committed
chore: run a11y audits on protractor
* Fixes accessibility issue in grid-list with `role="listitem"` * Adds protractor plugin to run aXe-core accessibility audits * Fixes a bunch of a11y issues in the e2e app to make the aXe audits green
1 parent 26eb7ce commit 9c5468a

File tree

7 files changed

+102
-16
lines changed

7 files changed

+102
-16
lines changed

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@
5151
"@types/node": "^6.0.34",
5252
"@types/run-sequence": "0.0.27",
5353
"@types/rx": "^2.5.33",
54+
"axe-core": "^2.0.7",
55+
"axe-protractor": "0.0.7",
56+
"axe-webdriverjs": "^0.4.0",
5457
"browserstacktunnel-wrapper": "^2.0.0",
5558
"conventional-changelog": "^1.1.0",
5659
"express": "^4.14.0",
@@ -80,7 +83,6 @@
8083
"minimist": "^1.2.0",
8184
"node-sass": "^3.4.2",
8285
"protractor": "^4.0.8",
83-
"protractor-accessibility-plugin": "0.1.1",
8486
"resolve-bin": "^0.4.0",
8587
"rollup": "^0.34.13",
8688
"run-sequence": "^1.2.2",

src/e2e-app/e2e-app/e2e-app.html

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
1-
<a md-list-item [routerLink]="['button']">Button</a>
2-
<a md-list-item [routerLink]="['checkbox']">Checkbox</a>
3-
<a md-list-item [routerLink]="['dialog']">Dialog</a>
4-
<a md-list-item [routerLink]="['grid-list']">Grid list</a>
5-
<a md-list-item [routerLink]="['icon']">Icon</a>
6-
<a md-list-item [routerLink]="['list']">List</a>
7-
<a md-list-item [routerLink]="['menu']">Menu</a>
8-
<a md-list-item [routerLink]="['progress-bar']">Progress bar</a>
9-
<a md-list-item [routerLink]="['progress-circle']">Progress circle</a>
10-
<a md-list-item [routerLink]="['radio']">Radios</a>
11-
<a md-list-item [routerLink]="['tabs']">Tabs</a>
1+
<md-nav-list>
2+
<a md-list-item [routerLink]="['button']">Button</a>
3+
<a md-list-item [routerLink]="['checkbox']">Checkbox</a>
4+
<a md-list-item [routerLink]="['dialog']">Dialog</a>
5+
<a md-list-item [routerLink]="['grid-list']">Grid list</a>
6+
<a md-list-item [routerLink]="['icon']">Icon</a>
7+
<a md-list-item [routerLink]="['list']">List</a>
8+
<a md-list-item [routerLink]="['menu']">Menu</a>
9+
<a md-list-item [routerLink]="['progress-bar']">Progress bar</a>
10+
<a md-list-item [routerLink]="['progress-circle']">Progress circle</a>
11+
<a md-list-item [routerLink]="['radio']">Radios</a>
12+
<a md-list-item [routerLink]="['tabs']">Tabs</a>
13+
</md-nav-list>
1214

13-
<router-outlet></router-outlet>
15+
<main>
16+
<router-outlet role="main"></router-outlet>
17+
</main>

src/e2e-app/radio/radio-e2e.html

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
<section>
22
<md-radio-group [disabled]="isGroupDisabled"
33
[(value)]="groupValue"
4-
id="test-group">
4+
id="test-group" aria-label="Select a Pokemon">
5+
56
<md-radio-button value="fire" id="fire">Charmander</md-radio-button>
67
<md-radio-button value="water" id="water">Squirtle</md-radio-button>
78
<md-radio-button value="leaf" id="leaf">Bulbasaur</md-radio-button>
9+
810
</md-radio-group>
911
<button (click)="isGroupDisabled=!isGroupDisabled" id="toggle-disable">Disable/enable group</button>
1012
</section>

src/lib/grid-list/grid-list.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ const MD_FIT_MODE = 'fit';
3535
selector: 'md-grid-list',
3636
templateUrl: 'grid-list.html',
3737
styleUrls: ['grid-list.css'],
38+
host: {
39+
'role': 'list'
40+
},
3841
encapsulation: ViewEncapsulation.None,
3942
})
4043
export class MdGridList implements OnInit, AfterContentChecked {

test/protractor.conf.js

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,27 @@ require('ts-node').register({
99

1010
const E2E_BASE_URL = process.env['E2E_BASE_URL'] || 'http://localhost:4200';
1111
const config = {
12-
// TODO(jelbourn): add back plugin for a11y assersions once it supports specifying AXS options.
1312
useAllAngular2AppRoots: true,
1413
specs: [ path.join(__dirname, '../e2e/**/*.e2e.ts') ],
1514
baseUrl: E2E_BASE_URL,
1615
allScriptsTimeout: 120000,
1716
getPageTimeout: 120000,
1817
jasmineNodeOpts: {
1918
defaultTimeoutInterval: 120000,
20-
}
19+
},
20+
21+
plugins: [
22+
{
23+
// Runs the axe-core accessibility checks each time the e2e page changes and
24+
// Angular is ready.
25+
path: '../tools/axe-protractor/axe-protractor.js',
26+
27+
rules: [
28+
// Exclude md-menu elements because those are empty if not active.
29+
{ id: 'aria-required-children', selector: '*:not(.md-menu)' },
30+
]
31+
}
32+
]
2133
};
2234

2335
if (process.env['TRAVIS']) {
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
'use strict';
2+
3+
/**
4+
* Protractor Plugin to run axe-core accessibility audits after Angular bootstrapped.
5+
*/
6+
7+
const AxeBuilder = require('axe-webdriverjs');
8+
const {buildMessage} = require('./build-message');
9+
10+
/* List of pages which were already checked by axe-core and shouldn't run again */
11+
const checkedPages = [];
12+
13+
/**
14+
* Protractor plugin hook which always runs when Angular successfully bootsrapped.
15+
*/
16+
function onPageStable() {
17+
AxeBuilder(browser.driver)
18+
.configure(this.config || {})
19+
.analyze(results => handleResults(this, results));
20+
}
21+
22+
/**
23+
* Processes the axe-core results by reporting recognized violations
24+
* to Protractor and printing them out.
25+
* @param {!protractor.ProtractorPlugin} context
26+
* @param {axe.AxeResults} results
27+
*/
28+
function handleResults(context, results) {
29+
30+
if (checkedPages.indexOf(results.url) !== -1) {
31+
checkedPages.push(results.url);
32+
33+
results.violations.forEach(violation => {
34+
35+
let specName = `${violation.help} (${results.url})`;
36+
let message = '\n' + buildMessage(violation);
37+
38+
context.addFailure(message, {specName});
39+
40+
});
41+
42+
}
43+
44+
}
45+
46+
exports.name = 'protractor-axe';
47+
exports.onPageStable = onPageStable;

tools/axe-protractor/build-message.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/**
2+
* Builds a simple message of the violation results of axe-core by listing
3+
* each violation and the associated element selector in a new line.
4+
* @param {!axe.Violation} violation
5+
*/
6+
exports.buildMessage = violation => {
7+
8+
let selectors = violation.nodes.map(node => {
9+
return node.target.join(' ');
10+
});
11+
12+
return selectors.reduce((content, selector) => {
13+
return content + '- ' + selector + '\n';
14+
}, '');
15+
16+
};

0 commit comments

Comments
 (0)