medialize/ally.js

View on GitHub
test/unit/is.only-tabbable.test.js

Summary

Maintainability
F
1 wk
Test Coverage
define(function(require) {
  'use strict';

  var bdd = require('intern!bdd');
  var expect = require('intern/chai!expect');
  var focusableFixture = require('../helper/fixtures/focusable.fixture');
  var platform = require('ally/util/platform');
  var isOnlyTabbable = require('ally/is/only-tabbable');

  bdd.describe('is/only-tabbable', function() {
    var fixture;

    bdd.before(function() {
      var deferred = this.async(10000);
      fixture = focusableFixture();
      fixture.add([
        /* eslint-disable indent */
        '<label tabindex="0" id="label-tabindex-0">text</label>',
        '<label tabindex="-1" id="label-tabindex--1">text</label>',
        /*eslint-enable indent */
      ], 'svg-container');
      // NOTE: Firefox decodes DataURIs asynchronously
      setTimeout(deferred.resolve, 200);
    });

    bdd.after(function() {
      fixture.remove();
      fixture = null;
    });

    bdd.it('should handle invalid input', function() {
      expect(function() {
        isOnlyTabbable(null);
      }).to.throw(TypeError, 'is/only-tabbable requires valid options.context');

      expect(function() {
        isOnlyTabbable([true]);
      }).to.throw(TypeError, 'is/only-tabbable requires options.context to be an Element');
    });

    bdd.it('should provide .rules() and .except()', function() {
      var element = document.getElementById('inert-div');
      expect(isOnlyTabbable.rules({
        context: element,
      })).to.equal(false, '.rules()');

      expect(isOnlyTabbable.rules.except({})(element)).to.equal(false, '.rules.except()');
    });

    bdd.describe('for document structure', function() {
      bdd.it('should return false for document', function() {
        expect(isOnlyTabbable(document)).to.equal(false);
      });

      bdd.it('should return false for <html>', function() {
        expect(isOnlyTabbable(document.documentElement)).to.equal(false);
      });

      bdd.it('should return false for <body>', function() {
        expect(isOnlyTabbable(document.body)).to.equal(false);
      });

      bdd.it('should return false for <head>', function() {
        expect(isOnlyTabbable(document.head)).to.equal(false);
      });
    });

    bdd.describe('for <div> with tabindex attribute', function() {
      bdd.it('should return false for <div>', function() {
        var element = document.getElementById('inert-div');
        expect(isOnlyTabbable(element)).to.equal(false);
      });

      bdd.it('should return false for <div tabindex="-1">', function() {
        var element = document.getElementById('tabindex--1');
        expect(isOnlyTabbable(element)).to.equal(false);
      });

      bdd.it('should return false for <div tabindex="0">', function() {
        var element = document.getElementById('tabindex-0');
        expect(isOnlyTabbable(element)).to.equal(false);
      });

      bdd.it('should return false for <div tabindex="1">', function() {
        var element = document.getElementById('tabindex-1');
        expect(isOnlyTabbable(element)).to.equal(false);
      });

      bdd.it('should return false for <div tabindex="bad">', function() {
        var element = document.getElementById('tabindex-bad');
        expect(isOnlyTabbable(element)).to.equal(false);
      });
    });

    bdd.describe('for <a>', function() {
      bdd.it('should return false for <a> (without href attribute)', function() {
        var element = document.getElementById('anchor');
        expect(isOnlyTabbable(element)).to.equal(false);
      });

      bdd.it('should return false for <a href="…">', function() {
        var element = document.getElementById('link');
        expect(isOnlyTabbable(element)).to.equal(false);
      });

      bdd.it('should return false for <a tabindex="-1">', function() {
        var element = document.getElementById('link-tabindex--1');
        expect(isOnlyTabbable(element)).to.equal(false);
      });
    });

    bdd.describe('for <input>', function() {
      bdd.it('should return false for <input>', function() {
        var element = document.getElementById('input');
        expect(isOnlyTabbable(element)).to.equal(false);
      });

      bdd.it('should return false for <input tabindex="-1">', function() {
        var element = document.getElementById('input-tabindex--1');
        expect(isOnlyTabbable(element)).to.equal(false);
      });

      bdd.it('should return false for <input disabled>', function() {
        var element = document.getElementById('input-disabled');
        expect(isOnlyTabbable(element)).to.equal(false);
      });

      bdd.it('should return false for <fieldset disabled> <input>', function() {
        var element = document.getElementById('fieldset-disabled-input');
        expect(isOnlyTabbable(element)).to.equal(false);
      });

      bdd.it('should return false for <input type="hidden">', function() {
        var element = document.getElementById('input-hidden');
        expect(isOnlyTabbable(element)).to.equal(false);
      });
    });

    bdd.describe('for editable elements', function() {
      bdd.it('should return false for <span contenteditable>', function() {
        var element = document.getElementById('span-contenteditable');
        expect(isOnlyTabbable(element)).to.equal(false);
      });

      bdd.it('should return false for <span style="user-modify: read-write">', function() {
        var element = document.getElementById('span-user-modify');
        expect(isOnlyTabbable(element)).to.equal(false);
      });
    });

    bdd.describe('for <img>', function() {
      bdd.it('should return false for <img usemap="…">', function() {
        var element = document.getElementById('img-usemap');
        expect(isOnlyTabbable(element)).to.equal(false);
      });

      bdd.it('should return false for <img usemap="…" tabindex="-1">', function() {
        var element = document.getElementById('img-usemap');
        element.setAttribute('tabindex', '-1');
        expect(isOnlyTabbable(element)).to.equal(false);
      });

      bdd.it('should return false for <a> <img ismap>', function() {
        var element = document.getElementById('img-ismap');
        expect(isOnlyTabbable(element)).to.equal(false);
      });
    });

    bdd.describe('for <area>', function() {
      bdd.it('should return false for <area>', function() {
        var element = document.getElementById('image-map-area');
        expect(isOnlyTabbable(element)).to.equal(false);
      });

      bdd.it('should return false for <area tabindex="-1">', function() {
        var element = document.getElementById('image-map-area');
        element.setAttribute('tabindex', '-1');
        expect(isOnlyTabbable(element)).to.equal(false);
      });
    });

    bdd.describe('for <label>', function() {
      bdd.it('should return false for <label>', function() {
        var element = document.getElementById('label');
        expect(isOnlyTabbable(element)).to.equal(false);
      });

      bdd.it('should return false for <label tabindex="-1">', function() {
        var element = document.getElementById('label');
        element.setAttribute('tabindex', '-1');
        expect(isOnlyTabbable(element)).to.equal(false);
      });

      bdd.it('should return {browser-specific} for <label tabindex="0">', function() {
        var element = document.getElementById('label');
        element.setAttribute('tabindex', '0');
        expect(isOnlyTabbable(element)).to.equal(platform.is.GECKO);
      });
    });

    bdd.describe('for <audio>', function() {
      bdd.it('should return false for <audio controls>', function() {
        var element = document.getElementById('audio-controls');
        expect(isOnlyTabbable(element)).to.equal(false);
      });

      bdd.it('should return false for <audio> without controls attribute', function() {
        var element = document.getElementById('audio');
        expect(isOnlyTabbable(element)).to.equal(false);
      });
    });

    bdd.describe('for SVG', function() {
      bdd.it('should return false for <svg>', function() {
        var element = document.getElementById('svg');
        expect(isOnlyTabbable(element)).to.equal(false);
      });

      bdd.it('should return false for <svg tabindex="-1">', function() {
        var element = document.getElementById('svg');
        element.setAttribute('tabindex', '-1');
        expect(isOnlyTabbable(element)).to.equal(false);
      });

      bdd.it('should return {browser-specific} for <a xlink:href="…">', function() {
        var element = document.getElementById('svg-link');
        var expected = Boolean(platform.is.GECKO && !element.focus);
        expect(isOnlyTabbable(element)).to.equal(expected);
      });

      bdd.it('should return false for <text>', function() {
        var element = document.getElementById('svg-link-text');
        expect(isOnlyTabbable(element)).to.equal(false);
      });
    });

    bdd.describe('for <object>', function() {
      bdd.it('should return false for <object> referencing an SVG', function() {
        var element = document.getElementById('object-svg');
        expect(isOnlyTabbable(element)).to.equal(false);
      });

      bdd.it('should return false for <object tabindex="-1"> referencing an SVG', function() {
        var element = document.getElementById('object-tabindex-svg');
        expect(isOnlyTabbable(element)).to.equal(false);
      });
    });

    bdd.describe('for <embed>', function() {
      bdd.before(function() {
        var element = document.getElementById('embed');
        if (!element) {
          this.skip('skipping to avoid test colliding with QuickTime');
        }
      });

      bdd.it('should return false for <embed>', function() {
        var element = document.getElementById('embed');
        expect(isOnlyTabbable(element)).to.equal(false);
      });

      bdd.it('should return false for <embed tabindex="0">', function() {
        var element = document.getElementById('embed-tabindex-0');
        expect(isOnlyTabbable(element)).to.equal(false);
      });
    });

    bdd.describe('for scrollable elements', function() {
      bdd.it('should return false for scrollable <div> without CSS overflow property', function() {
        var element = document.getElementById('scroll-container-without-overflow');
        expect(isOnlyTabbable(element)).to.equal(false);
      });

      bdd.it('should return false for scrollable <div> with CSS overflow property', function() {
        var element = document.getElementById('scroll-container');
        expect(isOnlyTabbable(element)).to.equal(false);
      });

      bdd.it('should return false for child of scrollable <div> without CSS overflow property', function() {
        var element = document.getElementById('scroll-body');
        expect(isOnlyTabbable(element)).to.equal(false);
      });
    });

    bdd.describe('for CSS Flexbox Layout', function() {
      bdd.before(function() {
        fixture.add([
          /* eslint-disable indent */
          '<div id="flexbox-parent" style="display: -webkit-flex; display: -ms-flexbox; display: flex;">',
            '<span id="flexbox-child" style="display: block;">hello</span>',
          '</div>',
          /*eslint-enable indent */
        ]);
      });

      bdd.it('should return false for child of flexbox container', function() {
        var element = document.getElementById('flexbox-child');
        expect(isOnlyTabbable(element)).to.equal(false);
      });

      bdd.it('should return false for flexbox container', function() {
        var element = document.getElementById('flexbox-parent');
        expect(isOnlyTabbable(element)).to.equal(false);
      });
    });

    bdd.describe('for ShadowDOM', function() {
      var host;
      var root;

      bdd.before(function() {
        if (document.body.createShadowRoot === undefined) {
          this.skip('ShadowDOM is not supported');
        }

        host = fixture.add([
          /* eslint-disable indent */
          '<div></div>',
          /*eslint-enable indent */
        ]).firstElementChild;
        root = host.createShadowRoot();
        root.innerHTML = '<input>';
      });

      bdd.it('should return false for ShadowHost', function() {
        expect(isOnlyTabbable(host)).to.equal(false);
      });

      bdd.it('should return false for ShadowHost with tabindex="-1"', function() {
        host.setAttribute('tabindex', '-1');
        expect(isOnlyTabbable(host)).to.equal(false);
      });
    });
  });
});