On Github marcysutton / a11y-testing-with-angular
By Marcy Sutton / @MarcySutton Senior Front-End Engineer, Deque Systems
…by making the Internet more accessible.
<label for="roundTrip"> <input type="radio" id="roundTrip"> Round Trip </label>HTML Radio Input
<button>Button</button>HTML Button
CSS changes button text to uppercase following the Material Design guidelines, which shows in the accessibility tree
aria-checked code example and link to documentation https://docs.angularjs.org/api/ngAria
Testing for accessibility in:
Good for:
it('should apply aria-hidden="true" when parent has valid label', inject(function() { var el = make('<md-radio-button aria-label="Squishy Avatar" '+ 'role="radio" tabindex="0"> '+ '<div class="md-container"></div> '+ '<div class="md-label"> '+ '<md-icon md-svg-icon="android"></md-icon> '+ '</div></md-radio-button>'); expect(el.find('md-icon').attr('aria-hidden')).toEqual('true'); }));mdIcon Spec
it('closes on escape', inject(function($document, $mdConstant) { var menu = setup(); openMenu(menu); expect(getOpenMenuContainer(menu).length).toBe(1); var openMenuEl = $document[0].querySelector('md-menu-content'); pressKey(openMenuEl, $mdConstant.KEY_CODE.ESCAPE); waitForMenuClose(); expect(getOpenMenuContainer(menu).length).toBe(0); }));mdMenu Spec
Good for:
Save yourself from:
https://github.com/dequelabs/axe-core
†There’s also a supported/enterprise option, Worldspace Attest
<md-toolbar layout="row"> <h1 layout-align-gt-sm="center center" class="md-toolbar-tools">My Favorite Muppets</h1> </md-toolbar> <div layout="row" flex class="content-wrapper"> <md-sidenav layout="column" class="md-sidenav-left md-whiteframe-z2" md-component-id="left"> <md-list class="muppet-list"> <md-item ng-repeat="it in muppets"> <md-item-content> <md-button ng-click="selectMuppet(it)" ng-class="{'selected' : it === selected }"> <img ng-src="{{it.iconurl}}" class="face" alt=""> {{it.name}} </md-button> </md-item-content> </md-item> </md-list> </md-sidenav> <div layout="column" flex class="content-wrapper" id="primary-col"> <md-content layout="column" flex class="md-padding"> <h2>{{selected.name}}</h2> <p>{{selected.content}}</p> <div class="cell"> <img ng-src="{{selected.imgurl}}" alt="{{selected.imgalt}}"> </div> </md-content> </div> </div>http://bit.ly/ngmaterial-muppets
var AxeBuilder = require('axe-webdriverjs'); describe('view1', function() { beforeEach(function() { browser.get('index.html'); }); it('should change Muppets', function() { element.all(by.css('[ng-click="selectMuppet(it)"]')).first(). sendKeys(protractor.Key.ENTER); expect(element.all(by.css('#primary-col h2')).first().getText()). toMatch('Animal'); }); it('should have no accessibility violations', function(done) { AxeBuilder(browser) .analyze(function(results) { if (results.violations.length > 0) { console.log(results.violations); } expect(results.violations.length).toBe(0); done(); }); });https://github.com/dequelabs/axe-webdriverjs
Testing for accessibility in:
describe('mdCheckbox with provided aria-labelledby ', () => { let checkboxDebugElement: DebugElement; let checkboxNativeElement: HTMLElement; let inputElement: HTMLInputElement; it('should use the provided aria-labelledby', () => { fixture = TestBed.createComponent(CheckboxWithAriaLabelledby); checkboxDebugElement = fixture.debugElement.query(By.directive(MdCheckbox)); checkboxNativeElement = checkboxDebugElement.nativeElement; inputElement = <HTMLInputElement>checkboxNativeElement.querySelector('input'); fixture.detectChanges(); expect(inputElement.getAttribute('aria-labelledby')).toBe('some-id'); }); /** Simple test component with aria-labelledby set. */ @Component({ template: `` }) class CheckboxWithAriaLabelledby {}Material 2 Checkbox Spec
it('toggles "aria-checked" on the host element', function() { builder.createAsync(CheckboxController).then(function(fixture) { let el = fixture.debugElement.query(By.css('.md-checkbox')); expect(el.nativeElement.getAttribute('aria-checked')).toEqual('false'); controller = fixture.componentInstance; changePromise = waitForChange(); controller.isChecked = true; fixture.detectChanges(); expect(el.nativeElement.getAttribute('aria-checked')).toEqual('true'); }); });Older Material 2 Checkbox Spec
it('should respond to keyboard events', () => { let fixture = TestBed.createComponent(Board); fixture.detectChanges(); var event1 = new KeyboardEvent('keydown', { key: '1' }); var event3 = new KeyboardEvent('keydown', { key: '3' }); window.dispatchEvent(event1); window.dispatchEvent(event3); fixture.detectChanges(); let board = fixture.nativeElement; let box = board.querySelectorAll('.ttt-box')[2]; expect(box.textContent).toContain('x'); });Tic-tac-toe Game Spec by Julie Ralph
~ with ~
/*----- menu.e2e.ts -------*/ import { MenuPage } from './menu-page'; describe('menu', () => { let page: MenuPage; beforeEach(function() { page = new MenuPage(); }); describe('keyboard events', () => { beforeEach(() => { // click start button to avoid tabbing past navigation page.start().click(); page.pressKey(protractor.Key.TAB); }); it('should auto-focus the first item when opened with keyboard', () => { page.pressKey(protractor.Key.ENTER); page.expectFocusOn(page.items(0)); }); it('should focus subsequent items when down arrow is pressed', () => { page.pressKey(protractor.Key.ENTER); page.pressKey(protractor.Key.DOWN); page.expectFocusOn(page.items(1)); }); }); }); /*----- menu-page.ts -------*/ pressKey(key: any): void { browser.actions().sendKeys(key).perform(); } expectFocusOn(el: ElementFinder): void { expect(browser.driver.switchTo().activeElement().getInnerHtml()) .toBe(el.getInnerHtml()); }Material 2 Menu
DOM helper getKeyEvent does not use keyCodehttps://github.com/angular/angular/issues/9419
import * as axe from 'axe-core'; ... it('should have no accessibility violations', function(done) { browser.executeScript(axe.source); // Run A11Y tests in the browsers event loop. browser.executeAsyncScript((resolve: any) => { return new Promise<axe.AxeResults>(res => { axe.a11yCheck(document, {}, resolve) }); }).then((results: any) => { if (results.violations.length > 0) { console.log(results.violations); } expect(results.violations.length).toBe(0); done(); }); });Protractor test