Accessibility Testingwith Angular



Accessibility Testingwith Angular

2 9


a11y-testing-angular2

Slides for my talks on accessibility testing with Angular

On Github marcysutton / a11y-testing-angular2

Accessibility Testingwith Angular 2

By Marcy Sutton / @MarcySutton Senior Front-End Engineer, Deque Systems

Break it down:Accessibility Testing

  • Who is impacted?
  • What can we do?
  • How can we do it?

15% of the population has some kind of disability

Source: World Health Organization

Some people can’t:

  • Use a mouse
  • View a screen
  • See low contrast text
  • Hear dialogue or music
  • Understand complex language

Some people need:

  • Keyboard support
  • Screen reader support
  • High contrast text
  • Captions and transcripts
  • Plain language

Guess what: you can help!

…by making the Internet more accessible.

Build accessibility into your workflow with Automated Testing

DOM vs. accessibility/ ax tree keyboard event stubbing tests as documentation for accessibility features

1 of 2

Component-Specific Unit Tests

Good for:

  • Text alternatives / Labeling
  • Keyboard Operability
  • ARIA attribute watching
Escape key, tabbing, hitting Enter/Space, Arrow keys offscreen/attribute text computation (stuff you don't see) ARIA default values for custom elements

Unit testing for text alternatives

aria-labelledby

 describe('MdCheckbox', function() {
  var builder: TestComponentBuilder;

  beforeEach(inject([TestComponentBuilder], function(tcb: TestComponentBuilder) {
    builder = tcb;
  }));

  it('sets the "aria-labelledby" attribute to the id of the label', function(done: () => void) {
      builder.createAsync(CheckboxController).then(function(fixture) {
        fixture.detectChanges();
        let el = fixture.debugElement.query(By.css('.md-checkbox'));
        let label = el.nativeElement.querySelector('label');

        expect(el.nativeElement.getAttribute('aria-labelledby')).toEqual(label.id);
      }).then(done).catch(done);
    });
 });
					
Keyboard Support

Unit tests: keyboard support

  describe('keyboard support', function() {
      var fixture: ComponentFixture;
      var controller: MenuController;
      var el: DebugElement;

      beforeEach(function(done: () => void) {
        builder.createAsync(MenuController).then(function(f) {
          fixture = f;
          controller = fixture.componentInstance;
          fixture.detectChanges();
          el = fixture.debugElement.query(By.css('.menuitem'));
        }).then(done).catch(done);
      });

      it('should toggle with the spacebar', function(done: () => void) {
        keyEvent('keydown', el, ' ', fixture);
        expect(el.nativeElement.className).toContain('menuitem-active');

        keyEvent('keydown', el, ' ', fixture);
        expect(el.nativeElement.className).not.toContain('menuitem-active');
      });
  });
  function keyEvent(name: string, el: DebugElement, key: string, fixture: ComponentFixture): Event {
    var kbdEvent: Event;
    if (BROWSER_SUPPORTS_EVENT_CONSTRUCTORS) {
      kbdEvent = new KeyboardEvent(name);
    } else {
    kbdEvent = document.createEvent('Event');
    kbdEvent.initEvent(name, true, true);
  
    // Hack DOM Level 3 Events "key" prop into keyboard event.
    Object.defineProperty(kbdEvent, 'key', {
      value: key,
      enumerable: false,
      writable: false,
      configurable: true
    });
    spyOn(kbdEvent, 'preventDefault').and.callThrough();
    spyOn(kbdEvent, 'stopPropagation').and.callThrough();
    el.nativeElement.dispatchEvent(kbdEvent);
    fixture.detectChanges();
    return kbdEvent;
  }
						
Material 2 checkbox

Mocking Keyboard Events

Reducing that wall of text is a todo for the Angular team

a11y inspector in Chrome Canary

Unit tests: ARIA attribute watching

	
    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');
      });
    });
					

2 of 2

Testing with an accessibility API

Save yourself from testing:

  • Label/name computation
  • Incorrect ARIA usage
  • Color contrast
  • Data table markup
  • Viewport/zooming probz
Escape key, tabbing, hitting Enter/Space, Arrow keys offscreen/attribute text computation (stuff you don't see) ARIA default values for custom elements

APIs: Your options(in general)

  • QUAIL
  • WAVE
  • aXe
  • Tenon
  • Chrome AccessibilityDeveloper Tools

aXe-core API

https://github.com/dequelabs/axe-core

  • Runs locally
  • Open source
  • Free †
  • Good for integration tests
  • Also unit tests

†There’s also a supported/enterprise option

Testing a component with aXe-core*

  npm install axe-core
					
  require('axe-core/axe.js');
  declare var axe: any;

  ...

  it('should have a form label', injectAsync([ TestComponentBuilder ], (tcb: TestComponentBuilder) => {
    return tcb.createAsync(Home).then((componentFixture: ComponentFixture) => {
      const element = componentFixture.nativeElement;

      axe.a11yCheck(element, null, (result) => {
        console.log(result);
        expect(result.violations.length).toBe(0);
      });
    });
  }));
					

*CommonJS module format, SystemJS support coming

Recap

  • You need accessibility tests.
  • Unit test for keyboard support, usability basics
  • Use TestComponentBuilder for DOM testing
  • Use an API for extra a11y muscle
  • Prevent broken stuff from going out the door!
Accessibility Testingwith Angular 2 By Marcy Sutton / @MarcySutton Senior Front-End Engineer, Deque Systems