medialize/ally.js

View on GitHub
test/unit/style.focus-within.test.js

Summary

Maintainability
C
1 day
Test Coverage
define(function(require) {
  'use strict';

  var bdd = require('intern!bdd');
  var expect = require('intern/chai!expect');
  var shadowInputFixture = require('../helper/fixtures/shadow-input.fixture');
  var delay = require('../helper/delay');
  var supports = require('../helper/supports');
  var styleFocusWithin = require('ally/style/focus-within');

  bdd.describe('style/focus-within', function() {
    var fixture;
    var handle;

    function focusWithinElements(context) {
      return [].slice.call((context || document).querySelectorAll('.ally-focus-within'), 0);
    }

    function _expect(message, expected, context) {
      var result = focusWithinElements(context).map(fixture.nodeToString);
      expect(result).to.deep.equal(expected, message);
    }

    bdd.before(function() {
      if (!document.hasFocus()) {
        this.skip('focus events are not processed properly while document does not have focus');
      }

      fixture = shadowInputFixture();
    });

    bdd.after(function() {
      handle && handle.disengage({ force: true });
      fixture && fixture.remove();
      fixture = null;
    });

    bdd.describe('lifecycle', function() {
      bdd.before(function() {
        if (document.activeElement === document.documentElement) {
          // Internet Explorer 10 initially focuses <html>
          // NOTE: blur() on document does nothing, you actually need to focus() the body
          // document.activeElement.blur();
          document.body.focus();
        }

        // allow IE10 and IE11 to run their async focus event
        return delay(20);
      });

      bdd.it('should engage a service', function() {
        handle = styleFocusWithin();
        expect(handle.disengage).to.be.a('function', 'handle');
      });

      bdd.it('should start in initial state', function() {
        expect(document.activeElement).to.equal(document.body, 'activeElement');
        _expect('ancestry', 'html body'.split(' '));
      });

      bdd.it('should follow focus to #outer-input', function() {
        fixture.input.outer.focus();

        expect(document.activeElement).to.equal(fixture.input.outer, 'activeElement');
        _expect('ancestry', 'html body #intern-dom-fixture #outer-input'.split(' '));
      });

      bdd.it('should follow focus to #after-input', function() {
        fixture.input.after.focus();

        expect(document.activeElement).to.equal(fixture.input.after, 'activeElement');
        _expect('ancestry', 'html body #intern-dom-fixture #after-wrapper #after-input'.split(' '));
      });

      bdd.it('should follow focus to document.body', function() {
        // blur events are handled asynchronously
        var deferred = this.async(10000);

        document.activeElement.blur();

        setTimeout(deferred.callback(function() {
          expect(document.activeElement).to.equal(document.body, 'activeElement');
          _expect('ancestry', 'html body'.split(' '));
        }), 200);
      });

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

          if (!supports.cssShadowPiercingDeepCombinator) {
            this.skip('ShadowDOM "shadow-piercing descendant combinator" not supported');
          }
        });

        bdd.it('should follow focus into ShadowHost', function() {
          fixture.input.first.focus();

          // @browser-issue Gecko https://bugzilla.mozilla.org/show_bug.cgi?id=1117535
          if (document.activeElement === fixture.input.first) {
            expect(document.activeElement).to.equal(fixture.input.first);
            _expect('first sequence', 'html body #intern-dom-fixture #first-shadow-host'.split(' '));
            return;
          }

          expect(document.activeElement).to.equal(fixture.shadow.first, 'activeElement');
          _expect('ancestry', 'html body #intern-dom-fixture #first-shadow-host'.split(' '));
          _expect('ancestry in host', ['#first-input'], fixture.shadow.first.shadowRoot);
        });

        bdd.it('should follow focus into nested ShadowHost', function() {
          // shadow dom updates are performed asynchronous
          // (async introduced by event/shadow-focus)
          var deferred = this.async(10000);

          fixture.input.second.focus();

          setTimeout(deferred.callback(function() {
            _expect('ancestry', 'html body #intern-dom-fixture #first-shadow-host'.split(' '));
            _expect('ancestry in first host', ['#second-shadow-host'], fixture.shadow.first.shadowRoot);
            _expect('ancestry in second host', ['#second-input'], fixture.shadow.second.shadowRoot);
          }), 100);
        });

        bdd.it('should clean up after leaving ShadowHost', function() {
          // make sure classes are removed upon leaving the ShadowRoot
          fixture.input.after.focus();
          _expect('ancestry', 'html body #intern-dom-fixture #after-wrapper #after-input'.split(' '));
          _expect('ancestry in first host', [], fixture.shadow.first.shadowRoot);
          _expect('ancestry in second host', [], fixture.shadow.second.shadowRoot);
        });
      });

      bdd.it('should cleanup after disengage', function() {
        handle.disengage();
        handle = null;
        _expect('after disengage', []);
      });
    });
  });
});