mgcrea/angular-strap

View on GitHub
src/datepicker/test/datepicker.spec.js

Summary

Maintainability
F
2 mos
Test Coverage
'use strict';

describe('datepicker', function() {

  var bodyEl = $('body'), sandboxEl;
  var $compile, $templateCache, $animate, dateFilter, $datepicker, scope, today, $timeout;

  beforeEach(module('ngAnimate'));
  beforeEach(module('ngAnimateMock'));
  beforeEach(module('ngSanitize'));
  beforeEach(module('mgcrea.ngStrap.datepicker'));

  beforeEach(inject(function ($injector, _$rootScope_, _$compile_, _$templateCache_, _$animate_, _dateFilter_, _$datepicker_, _$timeout_) {
    scope = _$rootScope_.$new();
    $compile = _$compile_;
    $templateCache = _$templateCache_;
    $animate = $injector.get('$animate');
    $timeout = $injector.get('$timeout');
    var flush = $animate.flush || $animate.triggerCallbacks;
    $animate.flush = function() {
      flush.call($animate); if(!$animate.triggerCallbacks) $timeout.flush();
    };
    dateFilter = _dateFilter_;
    today = new Date();
    bodyEl.html('');
    sandboxEl = $('<div>').attr('id', 'sandbox').appendTo(bodyEl);
    $datepicker = _$datepicker_;
  }));

  afterEach(function() {
    scope.$destroy();
    sandboxEl.remove();
  });

  // Templates

  var templates = {
    'default': {
      scope: {selectedDate: new Date()},
      element: '<input type="text" ng-model="selectedDate" bs-datepicker>'
    },
    'default-with-namespace': {
      scope: {selectedDate: new Date()},
      element: '<input type="text" ng-model="selectedDate" bs-datepicker data-prefix-event="modal">'
    },
    'default-with-id': {
      scope: {selectedDate: new Date()},
      element: '<input id="datepicker1" type="text" ng-model="selectedDate" bs-datepicker>'
    },
    'value-past': {
      scope: {selectedDate: new Date(1986, 1, 22)},
      element: '<input type="text" ng-model="selectedDate" bs-datepicker>'
    },
    'markup-ngRepeat': {
      element: '<ul><li ng-repeat="i in [1, 2, 3]"><input type="text" ng-model="selectedDate" bs-datepicker></li></ul>'
    },
    'markup-ngChange': {
      scope: {selectedDate: new Date(1986, 1, 22), onChange: function() {}},
      element: '<input type="text" ng-model="selectedDate" ng-change="onChange()" bs-datepicker>'
    },
    'markup-ngRequired': {
      scope: {selectedDate: new Date(2010, 1, 22)},
      element: '<input type="text" ng-model="selectedDate" ng-required="true" bs-datepicker>'
    },
    'options-animation': {
      element: '<div class="btn" data-animation="am-flip-x" ng-model="datepickeredIcon" bs-datepicker></div>'
    },
    'options-placement': {
      element: '<div class="btn" data-placement="bottom" ng-model="datepickeredIcon" bs-datepicker></div>'
    },
    'options-placement-exotic': {
      element: '<div class="btn" data-placement="bottom-right" ng-model="datepickeredIcon" bs-datepicker></div>'
    },
    'options-trigger': {
      element: '<div class="btn" data-trigger="hover" ng-model="datepickeredIcon" bs-datepicker></div>'
    },
    'options-template': {
      element: '<input type="text" data-template-url="custom" ng-model="selectedDate" bs-datepicker>'
    },
    'options-typeStringDateFormat': {
      scope: {selectedDate: '22/02/1986'},
      element: '<input type="text" ng-model="selectedDate" data-date-type="string" data-date-format="dd/MM/yyyy" bs-datepicker>'
    },
    'options-typeStringDateFormatAlternative': {
      scope: {selectedDate: '2014-04-11'},
      element: '<input type="text" ng-model="selectedDate" data-date-type="string" data-date-format="yyyy-MM-dd" bs-datepicker>'
    },
    'options-typeNumberDateFormat': {
      scope: {selectedDate: +new Date(1986, 2, 22)},
      element: '<input type="text" ng-model="selectedDate" data-date-type="number" bs-datepicker>'
    },
    'options-typeUnixDateFormat': {
      scope: {selectedDate: new Date(1986, 2, 22) / 1000},
      element: '<input type="text" ng-model="selectedDate" data-date-type="unix" bs-datepicker>'
    },
    'options-typeIsoDateFormat': {
      scope: {selectedDate: "2014-12-26T13:03:08.631Z"},
      element: '<input type="text" ng-model="selectedDate" data-date-type="iso" bs-datepicker>'
    },
    'options-dateFormat': {
      scope: {selectedDate: new Date(1986, 1, 22)},
      element: '<input type="text" ng-model="selectedDate" data-date-format="yyyy-MM-dd" bs-datepicker>'
    },
    'options-dateFormat-alt': {
      scope: {selectedDate: new Date(1986, 1, 22)},
      element: '<input type="text" ng-model="selectedDate" data-date-format="EEEE MMMM d, yyyy" bs-datepicker>'
    },
    'options-timezone-utc': {
      element: '<input type="text" ng-model="selectedDate" data-date-format="yyyy-MM-dd" data-timezone="UTC" bs-datepicker>'
    },
    'options-strictFormat': {
      scope: {selectedDate: new Date(1986, 1, 4)},
      element: '<input type="text" ng-model="selectedDate" data-date-format="yyyy-M-d" data-strict-format="1" bs-datepicker>'
    },
    'options-named-dateFormat': {
      scope: {selectedDate: new Date(1986, 1, 22)},
      element: '<input type="text" ng-model="selectedDate" data-date-format="mediumDate" bs-datepicker>'
    },
    'options-minDate': {
      scope: {selectedDate: new Date(1986, 1, 22), minDate: '02/20/86'},
      element: '<input type="text" ng-model="selectedDate" data-min-date="{{minDate}}" bs-datepicker>'
    },
    'options-minDate-today': {
      scope: {selectedDate: new Date()},
      element: '<input type="text" ng-model="selectedDate" data-min-date="today" bs-datepicker>'
    },
    'options-minDate-date': {
      scope: {selectedDate: new Date(1986, 1, 22), minDate: new Date(1986, 1, 20)},
      element: '<input type="text" ng-model="selectedDate" data-min-date="{{minDate}}" bs-datepicker>'
    },
    'options-minDate-number': {
      scope: {selectedDate: new Date(1986, 1, 22), minDate: +new Date(1986, 1, 20)},
      element: '<input type="text" ng-model="selectedDate" data-min-date="{{minDate}}" bs-datepicker>'
    },
    'options-maxDate': {
      scope: {selectedDate: new Date(1986, 1, 22), maxDate: '02/24/86'},
      element: '<input type="text" ng-model="selectedDate" data-max-date="{{maxDate}}" bs-datepicker>'
    },
    'options-maxDate-today': {
      scope: {selectedDate: new Date()},
      element: '<input type="text" ng-model="selectedDate" data-max-date="today" bs-datepicker>'
    },
    'options-maxDate-date': {
      scope: {selectedDate: new Date(1986, 1, 22), maxDate: new Date(1986, 1, 24)},
      element: '<input type="text" ng-model="selectedDate" data-max-date="{{maxDate}}" bs-datepicker>'
    },
    'options-maxDate-number': {
      scope: {selectedDate: new Date(1986, 1, 22), maxDate: +new Date(1986, 1, 24)},
      element: '<input type="text" ng-model="selectedDate" data-max-date="{{maxDate}}" bs-datepicker>'
    },
    'options-startWeek': {
      scope: {selectedDate: new Date(2014, 1, 22), startWeek: 1},
      element: '<input type="text" ng-model="selectedDate" data-start-week="{{startWeek}}" bs-datepicker>'
    },
    'options-startWeek-bis': {
      scope: {selectedDate: new Date(2014, 6, 15), startWeek: 6},
      element: '<input type="text" ng-model="selectedDate" data-start-week="{{startWeek}}" bs-datepicker>'
    },
    'options-startDate': {
      scope: {startDate: '02/03/04'},
      element: '<input type="text" ng-model="selectedDate" data-start-date="{{startDate}}" bs-datepicker>'
    },
    'options-autoclose': {
      element: '<input type="text" ng-model="selectedDate" data-autoclose="{{autoclose}}" bs-datepicker>'
    },
    'options-autoclose-hasToday': {
      element: '<input type="text" ng-model="selectedDate" data-autoclose="{{autoclose}}" data-has-today="true" bs-datepicker>'
    },
    'options-autoclose-hasClear': {
      element: '<input type="text" ng-model="selectedDate" data-autoclose="{{autoclose}}" data-has-clear="true" bs-datepicker>'
    },
    'options-useNative': {
      element: '<input type="text" ng-model="selectedDate" data-use-native="1" bs-datepicker>'
    },
    'options-modelDateFormat': {
      scope: {selectedDate: '2014-12-01' },
      element: '<input type="text" ng-model="selectedDate" data-date-format="dd/MM/yyyy" data-model-date-format="yyyy-MM-dd" data-date-type="string" bs-datepicker>'
    },
    'options-modelDateFormat-longDate': {
      scope: {selectedDate: 'December 1, 2014' },
      element: '<input type="text" ng-model="selectedDate" data-date-format="shortDate" data-model-date-format="longDate" data-date-type="string" bs-datepicker>'
    },
    'options-daysOfWeekDisabled': {
      scope: {selectedDate: new Date(2014, 6, 27)},
      element: '<input type="text" ng-model="selectedDate" data-days-of-week-disabled="{{daysOfWeekDisabled}}" bs-datepicker>'
    },
    'options-daysOfWeekDisabled-bis': {
      scope: {selectedDate: new Date(2014, 6, 27), daysOfWeekDisabled: '0246'},
      element: '<input type="text" ng-model="selectedDate" data-days-of-week-disabled="{{daysOfWeekDisabled}}" bs-datepicker>'
    },
    'options-disabledDates': {
      scope: {selectedDate: new Date(2014, 6, 27)},
      element: '<input type="text" ng-model="selectedDate" data-disabled-dates="disabledDates" bs-datepicker>'
    },
    'options-disabledDates-minmax': {
      scope: {selectedDate: new Date(2014, 6, 27), minDate: new Date(2014, 6, 21), maxDate: new Date(2014, 6, 25)},
      element: '<input type="text" ng-model="selectedDate" data-disabled-dates="disabledDates" data-min-date="{{minDate}}" data-max-date="{{maxDate}}" bs-datepicker>'
    },
    'options-disabledDates-daysOfWeek': {
      scope: {selectedDate: new Date(2014, 6, 27), daysOfWeekDisabled: '0'},
      element: '<input type="text" ng-model="selectedDate" data-disabled-dates="disabledDates" data-days-of-week-disabled="{{daysOfWeekDisabled}}" bs-datepicker>'
    },
    'bsShow-attr': {
      scope: {selectedDate: new Date()},
      element: '<input type="text" ng-model="selectedDate" bs-datepicker bs-show="true">'
    },
    'bsShow-binding': {
      scope: {isVisible: false, selectedDate: new Date()},
      element: '<input type="text" ng-model="selectedDate" bs-datepicker bs-show="isVisible">'
    },
    'options-container': {
      scope: {selectedDate: new Date()},
      element: '<input type="text" data-container="{{container}}" ng-model="selectedDate" bs-datepicker>'
    },
    'options-events': {
      scope: {selectedDate: new Date()},
      element: '<a bs-on-before-hide="onBeforeHide" bs-on-hide="onHide" bs-on-before-show="onBeforeShow" bs-on-show="onShow" ng-model="selectedDate" bs-datepicker>click me</a>'
    },
    'options-hasToday': {
      scope: {selectedDate: new Date()},
      element: '<input type="text" ng-model="selectedDate" data-has-today="true" data-min-date="{{minDate}}" data-date-format="yyyy-MM-dd" bs-datepicker>'
    },
    'options-hasClear': {
      scope: {selectedDate: new Date()},
      element: '<input type="text" ng-model="selectedDate" data-has-clear="true" data-min-date="{{minDate}}" data-date-format="yyyy-MM-dd" bs-datepicker>'
    }
  };

  function compileDirective(template, locals) {
    template = templates[template];
    angular.extend(scope, angular.copy(template.scope || templates['default'].scope), locals);
    var element = $(template.element).appendTo(sandboxEl);
    element = $compile(element)(scope);
    scope.$digest();
    return jQuery(element[0]);
  }

  function triggerKeyDown(elm, keyCode) {
    var evt = $.Event('keydown');
    evt.which = evt.keyCode = keyCode;
    angular.element(elm[0]).triggerHandler(evt);
  }

  // Tests

  describe('with default template', function() {

    it('should open on focus', function() {
      var elm = compileDirective('default');
      expect(sandboxEl.children('.dropdown-menu.datepicker').length).toBe(0);
      angular.element(elm[0]).triggerHandler('focus');
      expect(sandboxEl.children('.dropdown-menu.datepicker').length).toBe(1);
    });

    it('should close on blur', function() {
      var elm = compileDirective('default');
      expect(sandboxEl.children('.dropdown-menu.datepicker').length).toBe(0);
      angular.element(elm[0]).triggerHandler('focus');
      angular.element(elm[0]).triggerHandler('blur');
      expect(sandboxEl.children('.dropdown-menu.datepicker').length).toBe(0);
    });

    it('should close on ENTER keypress', function() {
      var elm = compileDirective('default');
      expect(sandboxEl.children('.dropdown-menu.datepicker').length).toBe(0);
      angular.element(elm[0]).triggerHandler('focus');
      // flush timeout to allow for keyboard events to hookup
      $timeout.flush();
      triggerKeyDown(elm, 13);
      expect(sandboxEl.children('.dropdown-menu.datepicker').length).toBe(0);
    });

    it('should correctly compile inner content', function() {
      var elm = compileDirective('default');
      angular.element(elm[0]).triggerHandler('focus');
      expect(sandboxEl.find('.dropdown-menu tbody td').length).toBe(7 * 6);
      expect(sandboxEl.find('.dropdown-menu tbody td .btn').length).toBe(7 * 6);
      expect(sandboxEl.find('.dropdown-menu thead button:eq(1)').text()).toBe(dateFilter(today, 'MMMM yyyy'));
      var todayDate = today.getDate();
      var firstDate = sandboxEl.find('.dropdown-menu tbody .btn:eq(0)').text() * 1;
      expect(new Date(today.getFullYear(), today.getMonth() - (firstDate !== 1 ? 1 : 0), firstDate).getDay()).toBe($datepicker.defaults.startWeek);
    });

    it('should correctly display active date', function() {
      var elm = compileDirective('default');
      angular.element(elm[0]).triggerHandler('focus');
      expect(sandboxEl.find('.dropdown-menu tbody td .btn-primary').text().trim() * 1).toBe(today.getDate());
      expect(elm.val()).toBe((today.getMonth() + 1) + '/' + today.getDate() + '/' + (today.getFullYear() + '').substr(2));
    });

    it('should correctly select a new date', function() {
      var elm = compileDirective('default');
      angular.element(elm[0]).triggerHandler('focus');
      angular.element(sandboxEl.find('.dropdown-menu tbody .btn:contains(15)')[0]).triggerHandler('click');
      expect(elm.val()).toBe((today.getMonth() + 1) + '/15/' + (today.getFullYear() + '').substr(2));
    });

    it('should correctly select the first day of the month', function() {
      var elm = compileDirective('default');
      angular.element(elm[0]).triggerHandler('focus');

      // change to next month
      angular.element(sandboxEl.find('.dropdown-menu thead button:eq(2)')[0]).triggerHandler('click');

      // select the first day of the month
      angular.element(sandboxEl.find('.dropdown-menu tbody .btn:contains(01)')[0]).triggerHandler('click');
      expect(sandboxEl.find('.dropdown-menu tbody td .btn-primary').text().trim() * 1).toBe(1);

      var nextMonthAndYear = new Date(today.getFullYear(), today.getMonth() +1 , 1);
      expect(elm.val()).toBe(nextMonthAndYear.getMonth() + 1 + '/1/' + (nextMonthAndYear.getFullYear() + '').substr(2));
    });

    it('should correctly set the model with manually typed value', function() {
      var elm = compileDirective('default', { selectedDate: new Date(2014, 1, 10)});
      angular.element(elm[0]).triggerHandler('focus');
      elm.val('11/30/14');
      angular.element(elm[0]).triggerHandler('change');
      scope.$digest();
      expect(scope.selectedDate).toEqual(new Date(2014, 10, 30));
      expect(angular.element(elm[0]).hasClass('ng-valid')).toBeTruthy();
    });

    it('should invalidate input with non-existing manually typed value', function() {
      var elm = compileDirective('default', { selectedDate: new Date(2014, 1, 10)});
      angular.element(elm[0]).triggerHandler('focus');
      elm.val('02/31/14');
      angular.element(elm[0]).triggerHandler('change');
      scope.$digest();
      expect(scope.selectedDate).toBeUndefined();
      expect(angular.element(elm[0]).hasClass('ng-invalid')).toBeTruthy();
    });

    it('should correctly be cleared when model is cleared', function() {
      var elm = compileDirective('default');
      scope.selectedDate = null;
      scope.$digest();
      expect(elm.val()).toBe('');
      scope.selectedDate = new Date(1986, 1, 22);
      scope.$digest();
      expect(elm.val()).toBe('2/22/86');
    });

    it('should correctly change view month when selecting next month button', function() {
      var elm = compileDirective('default');
      // set date to last day of January
      scope.selectedDate = new Date(2014, 0, 31);
      scope.$digest();
      angular.element(elm[0]).triggerHandler('focus');

      for (var nextMonth = 1; nextMonth < 12; nextMonth++) {
        // should show next month view when selecting next month button
        angular.element(sandboxEl.find('.dropdown-menu thead button:eq(2)')[0]).triggerHandler('click');
        expect(sandboxEl.find('.dropdown-menu thead button:eq(1)').text()).toBe(dateFilter(new Date(2014, nextMonth, 1), 'MMMM yyyy'));
      }
    });

    it('should correctly change view month when selecting previous month button', function() {
      var elm = compileDirective('default');
      // set date to last day of December
      scope.selectedDate = new Date(2014, 11, 31);
      scope.$digest();
      angular.element(elm[0]).triggerHandler('focus');

      for (var previousMonth = 10; previousMonth > -1; previousMonth--) {
        // should show previous month view when selecting previous month button
        angular.element(sandboxEl.find('.dropdown-menu thead button:eq(0)')[0]).triggerHandler('click');
        expect(sandboxEl.find('.dropdown-menu thead button:eq(1)').text()).toBe(dateFilter(new Date(2014, previousMonth, 1), 'MMMM yyyy'));
      }
    });

    it('should correctly navigate to upper month view', function() {
      var elm = compileDirective('default');
      var date = today.getDate(), month = today.getMonth();
      angular.element(elm[0]).triggerHandler('focus');
      expect(scope.$$childHead.$mode).toBe(0);
      angular.element(sandboxEl.find('.dropdown-menu thead button:eq(1)')[0]).triggerHandler('click');
      expect(scope.$$childHead.$mode).toBe(1);
    });

    describe('once in month view', function() {

      it('should correctly compile inner content', function() {
        var elm = compileDirective('default');
        angular.element(elm[0]).triggerHandler('focus');
        angular.element(sandboxEl.find('.dropdown-menu thead button:eq(1)')[0]).triggerHandler('click');
        expect(sandboxEl.find('.dropdown-menu tbody td').length).toBe(4 * 3);
        expect(sandboxEl.find('.dropdown-menu tbody .btn').length).toBe(4 * 3);
        expect(sandboxEl.find('.dropdown-menu thead button:eq(1)').text()).toBe(dateFilter(today, 'yyyy'));
      });

      it('should correctly display active date', function() {
        var elm = compileDirective('default');
        angular.element(elm[0]).triggerHandler('focus');
        angular.element(sandboxEl.find('.dropdown-menu thead button:eq(1)')[0]).triggerHandler('click');
        expect(sandboxEl.find('.dropdown-menu tbody td .btn-primary').text()).toBe(dateFilter(today, 'MMM'));
      });

    });

    it('should correctly navigate to upper year view', function() {
      var elm = compileDirective('default');
      var date = today.getDate(), month = today.getMonth();
      angular.element(elm[0]).triggerHandler('focus');
      expect(scope.$$childHead.$mode).toBe(0);
      angular.element(sandboxEl.find('.dropdown-menu thead button:eq(1)')[0]).triggerHandler('click');
      angular.element(sandboxEl.find('.dropdown-menu thead button:eq(1)')[0]).triggerHandler('click');
      expect(scope.$$childHead.$mode).toBe(2);
    });

    describe('once in year view', function() {

      it('should correctly compile inner content', function() {
        var elm = compileDirective('default');
        angular.element(elm[0]).triggerHandler('focus');
        angular.element(sandboxEl.find('.dropdown-menu thead button:eq(1)')[0]).triggerHandler('click');
        angular.element(sandboxEl.find('.dropdown-menu thead button:eq(1)')[0]).triggerHandler('click');
        expect(sandboxEl.find('.dropdown-menu tbody td').length).toBe(4 * 3);
        expect(sandboxEl.find('.dropdown-menu tbody .btn').length).toBe(4 * 3);
        // expect(sandboxEl.find('.dropdown-menu thead button:eq(1)').text()).toBe(dateFilter(today, 'yyyy'));
      });

      it('should correctly display active date', function() {
        var elm = compileDirective('default');
        angular.element(elm[0]).triggerHandler('focus');
        angular.element(sandboxEl.find('.dropdown-menu thead button:eq(1)')[0]).triggerHandler('click');
        angular.element(sandboxEl.find('.dropdown-menu thead button:eq(1)')[0]).triggerHandler('click');
        expect(sandboxEl.find('.dropdown-menu tbody td .btn-primary').text()).toBe(dateFilter(today, 'yyyy'));
      });

    });

    it('should correctly support null values', function() {
      var elm = compileDirective('default', {selectedDate: null});
      angular.element(elm[0]).triggerHandler('focus');
      expect(elm.val()).toBe('');
      expect(sandboxEl.find('.dropdown-menu tbody td').length).toBe(7 * 6);
      expect(sandboxEl.find('.dropdown-menu tbody .btn').length).toBe(7 * 6);
      elm.val('0');
      angular.element(elm[0]).triggerHandler('change');
    });

    it('should correctly support undefined values', function() {
      var elm = compileDirective('default', {selectedDate: undefined});
      angular.element(elm[0]).triggerHandler('focus');
      expect(elm.val()).toBe('');
      expect(sandboxEl.find('.dropdown-menu tbody td').length).toBe(7 * 6);
      expect(sandboxEl.find('.dropdown-menu tbody .btn').length).toBe(7 * 6);
      elm.val('0');
      angular.element(elm[0]).triggerHandler('change');
    });

    it('should correctly support invalid values', function() {
      var elm = compileDirective('default');
      elm.val('invalid');
      angular.element(elm[0]).triggerHandler('change');
      angular.element(elm[0]).triggerHandler('focus');
      expect(sandboxEl.find('.dropdown-menu tbody td .btn-primary').text().trim() * 1).toBe(today.getDate());
      angular.element(sandboxEl.find('.dropdown-menu tbody td .btn-primary')[0]).triggerHandler('click');
    });

    it('should handle empty values', function() {
      var elm = compileDirective('default');
      elm.val('');
      angular.element(elm[0]).triggerHandler('change');
      expect(scope.selectedDate).toBeNull();
    });

    it('should support ngRepeat markup', function() {
      var elm = compileDirective('markup-ngRepeat');
      angular.element(elm.find('[bs-datepicker]:eq(0)')).triggerHandler('focus');
      expect(sandboxEl.find('.dropdown-menu tbody td').length).toBe(7 * 6);
      expect(sandboxEl.find('.dropdown-menu tbody .btn').length).toBe(7 * 6);
    });

    it('should support ngChange markup', function() {
      var elm = compileDirective('markup-ngChange');
      angular.element(elm[0]).triggerHandler('focus');
      var spy = spyOn(scope, 'onChange').and.callThrough();
      angular.element(sandboxEl.find('.dropdown-menu tbody .btn:eq(1)')[0]).triggerHandler('click');
      expect(spy).toHaveBeenCalled();
    });

    it('should support ngRequired markup', function() {
      var elm = compileDirective('markup-ngRequired');
      var testDate = new Date(2010, 1, 22);

      expect(elm.val()).not.toBe('');
      expect(scope.selectedDate).toBeDefined();
      expect(scope.selectedDate).toEqual(testDate);

      angular.element(elm[0]).triggerHandler('focus');
      expect(sandboxEl.find('.dropdown-menu tbody td .btn-primary').text().trim() * 1).toBe(testDate.getDate());
      expect(elm.val()).toBe((testDate.getMonth() + 1) + '/' + testDate.getDate() + '/' + (testDate.getFullYear() + '').substr(2));
    });

    it('should consider empty value valid-date with ngRequired markup', function() {
      var elm = compileDirective('markup-ngRequired');

      // we don't check ng-valid-parse because AngularJs 1.2
      // doesn't use that class

      expect(elm.hasClass('ng-valid')).toBe(true);
      expect(elm.hasClass('ng-valid-required')).toBe(true);

      angular.element(elm[0]).triggerHandler('focus');
      elm.val('');
      angular.element(elm[0]).triggerHandler('change');

      // if input is empty, consider valid-date and let
      // ngRequired invalidate the value
      expect(elm.hasClass('ng-valid-date')).toBe(true);
      expect(elm.hasClass('ng-invalid')).toBe(true);
      expect(elm.hasClass('ng-invalid-required')).toBe(true);
    });

    it('should consider empty value valid-parse without ngRequired markup', function() {
      var elm = compileDirective('default');

      // we don't check ng-valid-parse because AngularJs 1.2
      // doesn't use that class

      expect(elm.hasClass('ng-valid')).toBe(true);

      angular.element(elm[0]).triggerHandler('focus');
      elm.val('');
      angular.element(elm[0]).triggerHandler('change');

      // if input is empty, consider valid-date and let
      // other validators run
      expect(elm.hasClass('ng-valid-date')).toBe(true);
      expect(elm.hasClass('ng-valid')).toBe(true);
    });

    // iit('should only build the datepicker once', function() {
    //   var elm = compileDirective('value-past');
    //   angular.element(elm[0]).triggerHandler('focus');
    // });

    describe('for each month of the year', function() {
      var elm;
      var firstDay, previousDay;
      var monthToCheck = 0;

      beforeEach(function() {
        jasmine.addMatchers({
          toBeNextDayOrFirstDay: function(util, customEqualityTesters) {
            return {
              compare: function(actual, expected) {
                var result = {};
                var previousDay = expected;
                result.pass = actual === (previousDay + 1) || actual === 1;
                result.message = "Expected " + actual + " to be either " + (previousDay + 1) + " or 1";
                return result;
              }
            };
          }
        });

        elm = compileDirective('default', { selectedDate: new Date(2012, monthToCheck, 1) });
        angular.element(elm[0]).triggerHandler('focus');
        firstDay = sandboxEl.find('.dropdown-menu tbody .btn:eq(0)').text() * 1;
        previousDay = firstDay - 1;
      });

      afterEach(function() {
        monthToCheck++;
      });

      for (var month = 0; month < 12; month++) {
        it('should correctly order month days in inner content', function() {
          // 6 rows (weeks) * 7 columns (days)
          for(var index = 0; index < 7 * 6; index++) {
            var indexDay = sandboxEl.find('.dropdown-menu tbody td .btn:eq(' + index + ')').text() * 1;
            expect(indexDay).toBeNextDayOrFirstDay(previousDay);
            previousDay = indexDay;
          }
        });
      }

    });

  });

  describe('resource allocation', function() {
    it('should not create additional scopes after first show', function() {
      var elm = compileDirective('default');
      angular.element(elm[0]).triggerHandler('focus');
      $animate.flush();
      expect(sandboxEl.children('.dropdown-menu.datepicker').length).toBe(1);
      angular.element(elm[0]).triggerHandler('blur');
      $animate.flush();
      expect(sandboxEl.children('.dropdown-menu.datepicker').length).toBe(0);

      var scopeCount = countScopes(scope, 0);

      for (var i = 0; i < 10; i++) {
        angular.element(elm[0]).triggerHandler('focus');
        $animate.flush();
        angular.element(elm[0]).triggerHandler('blur');
        $animate.flush();
      }

      expect(countScopes(scope, 0)).toBe(scopeCount);
    });

    it('should not create additional scopes when changing months', function() {
      var elm = compileDirective('default');
      angular.element(elm[0]).triggerHandler('focus');
      $animate.flush();
      expect(sandboxEl.children('.dropdown-menu.datepicker').length).toBe(1);

      var scopeCount = countScopes(scope, 0);

      for (var i = 0; i < 10; i++) {
        angular.element(sandboxEl.find('.dropdown-menu thead button:eq(2)')[0]).triggerHandler('click');
        scope.$digest();
      }

      expect(countScopes(scope, 0)).toBe(scopeCount);
    });

    it('should destroy scopes when destroying directive scope', function() {
      var scopeCount = countScopes(scope, 0);
      var originalScope = scope;
      scope = scope.$new();
      var elm = compileDirective('default');

      for (var i = 0; i < 10; i++) {
        angular.element(elm[0]).triggerHandler('focus');
        $animate.flush();
        angular.element(elm[0]).triggerHandler('blur');
        $animate.flush();
      }

      scope.$destroy();
      scope = originalScope;
      expect(countScopes(scope, 0)).toBe(scopeCount);
    });
  });

  describe('bsShow attribute', function() {
    it('should support setting to a boolean value', function() {
      var elm = compileDirective('bsShow-attr');
      expect(sandboxEl.children('.dropdown-menu.datepicker').length).toBe(1);
    });

    it('should support binding', function() {
      var elm = compileDirective('bsShow-binding');
      expect(scope.isVisible).toBeFalsy();
      expect(sandboxEl.children('.dropdown-menu.datepicker').length).toBe(0);
      scope.isVisible = true;
      scope.$digest();
      expect(sandboxEl.children('.dropdown-menu.datepicker').length).toBe(1);
      scope.isVisible = false;
      scope.$digest();
      expect(sandboxEl.children('.dropdown-menu.datepicker').length).toBe(0);
    });

    it('should support initial value false', function() {
      var elm = compileDirective('bsShow-binding');
      expect(scope.isVisible).toBeFalsy();
      expect(sandboxEl.children('.dropdown-menu.datepicker').length).toBe(0);
    });

    it('should support initial value true', function() {
      var elm = compileDirective('bsShow-binding', {isVisible: true});
      expect(scope.isVisible).toBeTruthy();
      expect(sandboxEl.children('.dropdown-menu.datepicker').length).toBe(1);
    });

    it('should support undefined value', function() {
      var elm = compileDirective('bsShow-binding', {isVisible: undefined});
      expect(sandboxEl.children('.dropdown-menu.datepicker').length).toBe(0);
    });

    it('should support string value', function() {
      var elm = compileDirective('bsShow-binding', {isVisible: 'a string value'});
      expect(sandboxEl.children('.dropdown-menu.datepicker').length).toBe(0);
      scope.isVisible = 'TRUE';
      scope.$digest();
      expect(sandboxEl.children('.dropdown-menu.datepicker').length).toBe(1);
      scope.isVisible = 'dropdown';
      scope.$digest();
      expect(sandboxEl.children('.dropdown-menu.datepicker').length).toBe(0);
      scope.isVisible = 'datepicker,tooltip';
      scope.$digest();
      expect(sandboxEl.children('.dropdown-menu.datepicker').length).toBe(1);
    });
  });

  // describe('using service', function() {

  //   it('should correctly open on next digest', function() {
  //     var myModal = $modal(templates['default'].scope.modal);
  //     scope.$digest();
  //     expect(bodyEl.children('.modal').length).toBe(1);
  //     myModal.hide();
  //     expect(bodyEl.children('.modal').length).toBe(0);
  //   });

  //   it('should correctly be destroyed', function() {
  //     var myModal = $modal(angular.extend(templates['default'].scope.modal));
  //     scope.$digest();
  //     expect(bodyEl.children('.modal').length).toBe(1);
  //     myModal.destroy();
  //     expect(bodyEl.children('.modal').length).toBe(0);
  //     expect(bodyEl.children().length).toBe(1);
  //   });

  //   it('should correctly work with ngClick', function() {
  //     var elm = compileDirective('markup-ngClick-service');
  //     var myModal = $modal(angular.extend({show: false}, templates['default'].scope.modal));
  //     scope.showModal = function() {
  //       myModal.$promise.then(myModal.show);
  //     };
  //     expect(bodyEl.children('.modal').length).toBe(0);
  //     angular.element(elm[0]).triggerHandler('click');
  //     expect(bodyEl.children('.modal').length).toBe(1);
  //   });

  // });

  describe('show / hide events', function() {

    it('should dispatch show and show.before events', function() {
      var myDatepicker = $datepicker(sandboxEl, null, { scope: scope, options: templates['default'].scope });
      var emit = spyOn(myDatepicker.$scope, '$emit');
      scope.$digest();
      myDatepicker.show();

      expect(emit).toHaveBeenCalledWith('tooltip.show.before', myDatepicker);
      // show only fires AFTER the animation is complete
      expect(emit).not.toHaveBeenCalledWith('tooltip.show', myDatepicker);
      $animate.flush();
      expect(emit).toHaveBeenCalledWith('tooltip.show', myDatepicker);
    });

    it('should dispatch hide and hide.before events', function() {
      var myDatepicker = $datepicker(sandboxEl, null, { scope: scope, options: templates['default'].scope });
      scope.$digest();
      myDatepicker.show();

      var emit = spyOn(myDatepicker.$scope, '$emit');
      myDatepicker.hide();

      expect(emit).toHaveBeenCalledWith('tooltip.hide.before', myDatepicker);
      // hide only fires AFTER the animation is complete
      expect(emit).not.toHaveBeenCalledWith('tooltip.hide', myDatepicker);
      $animate.flush();
      expect(emit).toHaveBeenCalledWith('tooltip.hide', myDatepicker);
    });

    it('should call show.before event with popover element instance id', function() {
      var elm = compileDirective('default-with-id');
      var id = "";
      scope.$on('tooltip.show.before', function(evt, datepicker) {
        id = datepicker.$id;
      });

      angular.element(elm[0]).triggerHandler('focus');
      scope.$digest();
      expect(id).toBe('datepicker1');
    });

  });

  describe('options', function() {

    describe('animation', function() {

      it('should default to `am-fade` animation', function() {
        var elm = compileDirective('default');
        angular.element(elm[0]).triggerHandler('focus');
        expect(sandboxEl.children('.dropdown-menu').hasClass('am-fade')).toBeTruthy();
      });

      it('should support custom animation', function() {
        var elm = compileDirective('options-animation');
        angular.element(elm[0]).triggerHandler('focus');
        expect(sandboxEl.children('.dropdown-menu').hasClass('am-flip-x')).toBeTruthy();
      });

    });

    describe('autoclose', function() {

      it('should close on select if truthy', function() {
        var elm = compileDirective('options-autoclose', {autoclose: "true"});
        expect(sandboxEl.children('.dropdown-menu.datepicker').length).toBe(0);
        angular.element(elm[0]).triggerHandler('focus');
        angular.element(sandboxEl.find('.dropdown-menu tbody .btn:first')).triggerHandler('click');
        $timeout.flush();
        expect(sandboxEl.children('.dropdown-menu.datepicker').length).toBe(0);
      });

      it('should NOT close on select if falsy', function() {
        var elm = compileDirective('options-autoclose', {autoclose: "false"});
        expect(sandboxEl.children('.dropdown-menu.datepicker').length).toBe(0);
        angular.element(elm[0]).triggerHandler('focus');
        angular.element(sandboxEl.find('.dropdown-menu tbody .btn:first')).triggerHandler('click');
        $timeout.flush();
        expect(sandboxEl.children('.dropdown-menu.datepicker').length).not.toBe(0);
      });

      it('should close on click today if truthy', function() {
        var elm = compileDirective('options-autoclose-hasToday', {autoclose: "true"});
        expect(sandboxEl.children('.dropdown-menu.datepicker').length).toBe(0);
        angular.element(elm[0]).triggerHandler('focus');
        angular.element(sandboxEl.find('.dropdown-menu tfoot .btn.today')).triggerHandler('click');
        $timeout.flush();
        expect(sandboxEl.children('.dropdown-menu.datepicker').length).toBe(0);
      });

      it('should NOT close on click today if falsy', function() {
        var elm = compileDirective('options-autoclose-hasToday', {autoclose: "false"});
        expect(sandboxEl.children('.dropdown-menu.datepicker').length).toBe(0);
        angular.element(elm[0]).triggerHandler('focus');
        angular.element(sandboxEl.find('.dropdown-menu tfoot .btn.today')).triggerHandler('click');
        $timeout.flush();
        expect(sandboxEl.children('.dropdown-menu.datepicker').length).not.toBe(0);
      });

      it('should close on click clear if truthy', function() {
        var elm = compileDirective('options-autoclose-hasClear', {autoclose: "true"});
        expect(sandboxEl.children('.dropdown-menu.datepicker').length).toBe(0);
        angular.element(elm[0]).triggerHandler('focus');
        angular.element(sandboxEl.find('.dropdown-menu tfoot .btn.clear')).triggerHandler('click');
        $timeout.flush();
        expect(sandboxEl.children('.dropdown-menu.datepicker').length).toBe(0);
      });

      it('should NOT close on click clear if falsy', function() {
        var elm = compileDirective('options-autoclose-hasClear', {autoclose: "false"});
        expect(sandboxEl.children('.dropdown-menu.datepicker').length).toBe(0);
        angular.element(elm[0]).triggerHandler('focus');
        angular.element(sandboxEl.find('.dropdown-menu tfoot .btn.clear')).triggerHandler('click');
        $timeout.flush();
        expect(sandboxEl.children('.dropdown-menu.datepicker').length).not.toBe(0);
      });

    });

    describe('placement', function() {
      var $$rAF;
      beforeEach(inject(function (_$$rAF_) {
        $$rAF = _$$rAF_
      }));

      it('should default to `bottom-left` placement', function() {
        var elm = compileDirective('default');
        angular.element(elm[0]).triggerHandler('focus');
        $$rAF.flush();
        expect(sandboxEl.children('.dropdown-menu').hasClass('bottom-left')).toBeTruthy();
      });

      it('should support placement', function() {
        var elm = compileDirective('options-placement');
        angular.element(elm[0]).triggerHandler('focus');
        $$rAF.flush();
        expect(sandboxEl.children('.dropdown-menu').hasClass('bottom')).toBeTruthy();
      });

      it('should support exotic-placement', function() {
        var elm = compileDirective('options-placement-exotic');
        angular.element(elm[0]).triggerHandler('focus');
        $$rAF.flush();
        expect(sandboxEl.children('.dropdown-menu').hasClass('bottom-right')).toBeTruthy();
      });

    });

    describe('trigger', function() {

      it('should support an alternative trigger', function() {
        var elm = compileDirective('options-trigger');
        expect(sandboxEl.children('.dropdown-menu').length).toBe(0);
        angular.element(elm[0]).triggerHandler('mouseenter');
        expect(sandboxEl.children('.dropdown-menu').length).toBe(1);
        angular.element(elm[0]).triggerHandler('mouseleave');
        expect(sandboxEl.children('.dropdown-menu').length).toBe(0);
      });

    });

    describe('template', function() {

      it('should support custom template', function() {
        $templateCache.put('custom', '<div class="dropdown-menu"><div class="dropdown-inner">foo: {{selectedDate}}</div></div>');
        var elm = compileDirective('options-template');
        angular.element(elm[0]).triggerHandler('focus');
        expect(sandboxEl.find('.dropdown-inner').text()).toBe('foo: "' + scope.selectedDate.toISOString() + '"');
      });

      it('should support template with ngRepeat', function() {
        $templateCache.put('custom', '<div class="dropdown-menu"><div class="dropdown-inner"><ul><li ng-repeat="i in [1, 2, 3]">{{i}}</li></ul></div></div>');
        var elm = compileDirective('options-template');
        angular.element(elm[0]).triggerHandler('focus');
        expect(sandboxEl.find('.dropdown-inner').text()).toBe('123');
        // Consecutive toggles
        angular.element(elm[0]).triggerHandler('blur');
        angular.element(elm[0]).triggerHandler('focus');
        expect(sandboxEl.find('.dropdown-inner').text()).toBe('123');
      });

      it('should support template with ngClick', function() {
        $templateCache.put('custom', '<div class="dropdown-menu"><div class="dropdown-inner"><a class="btn" ng-click="dropdown.counter=dropdown.counter+1">click me</a></div></div>');
        var elm = compileDirective('options-template');
        scope.dropdown = {counter: 0};
        angular.element(elm[0]).triggerHandler('focus');
        expect(angular.element(sandboxEl.find('.dropdown-inner > .btn')[0]).triggerHandler('click'));
        expect(scope.dropdown.counter).toBe(1);
        // Consecutive toggles
        angular.element(elm[0]).triggerHandler('blur');
        angular.element(elm[0]).triggerHandler('focus');
        expect(angular.element(sandboxEl.find('.dropdown-inner > .btn')[0]).triggerHandler('click'));
        expect(scope.dropdown.counter).toBe(2);
      });

    });

    describe('type', function() {

      it('should support string type with a dateFormat', function() {
        var elm = compileDirective('options-typeStringDateFormat');
        expect(elm.val()).toBe('22/02/1986');
        angular.element(elm[0]).triggerHandler('focus');
        angular.element(sandboxEl.find('.dropdown-menu tbody .btn:contains(16)')).triggerHandler('click');
        expect(elm.val()).toBe('16/02/1986');
      });

      it('should support string type with an alternative dateFormat', function() {
        var elm = compileDirective('options-typeStringDateFormatAlternative');
        expect(elm.val()).toBe('2014-04-11');
        angular.element(elm[0]).triggerHandler('focus');
        angular.element(sandboxEl.find('.dropdown-menu tbody .btn:contains(16)')).triggerHandler('click');
        expect(elm.val()).toBe('2014-04-16');
      });

      it('should support number type with a dateFormat', function() {
        var elm = compileDirective('options-typeNumberDateFormat');
        expect(elm.val()).toBe('3/22/86');
        angular.element(elm[0]).triggerHandler('focus');
        angular.element(sandboxEl.find('.dropdown-menu tbody .btn:contains(16)')).triggerHandler('click');
        expect(elm.val()).toBe('3/16/86');
      });

      it('should support unix type with a dateFormat', function() {
        var elm = compileDirective('options-typeUnixDateFormat');
        expect(elm.val()).toBe('3/22/86');
        angular.element(elm[0]).triggerHandler('focus');
        angular.element(sandboxEl.find('.dropdown-menu tbody .btn:contains(16)')).triggerHandler('click');
        expect(elm.val()).toBe('3/16/86');
      });

      it('should support iso type with a dateFormat', function() {
        var elm = compileDirective('options-typeIsoDateFormat');
        expect(elm.val()).toBe('12/26/14');
        angular.element(elm[0]).triggerHandler('focus');
        angular.element(sandboxEl.find('.dropdown-menu tbody .btn:contains(16)')).triggerHandler('click');
        expect(elm.val()).toBe('12/16/14');
      });

    });

    describe('dateFormat', function() {

      it('should support a custom dateFormat', function() {
        var elm = compileDirective('options-dateFormat');
        expect(elm.val()).toBe('1986-02-22');
        angular.element(elm[0]).triggerHandler('focus');
        angular.element(sandboxEl.find('.dropdown-menu tbody .btn:contains(24)')).triggerHandler('click');
        expect(elm.val()).toBe('1986-02-24');
      });

      it('should support an alternative custom dateFormat', function() {
        var elm = compileDirective('options-dateFormat-alt');
        expect(elm.val()).toBe('Saturday February 22, 1986');
        angular.element(elm[0]).triggerHandler('focus');
        angular.element(sandboxEl.find('.dropdown-menu tbody .btn:contains(24)')).triggerHandler('click');
        expect(elm.val()).toBe('Monday February 24, 1986');
      });

      it('should support a custom named dateFormat', function() {
        var elm = compileDirective('options-named-dateFormat');
        expect(elm.val()).toBe('Feb 22, 1986');
        angular.element(elm[0]).triggerHandler('focus');
        angular.element(sandboxEl.find('.dropdown-menu tbody .btn:contains(24)')).triggerHandler('click');
        expect(elm.val()).toBe('Feb 24, 1986');
      });

    });

    describe('timezone', function () {
      var elm, i = 0;
      var dates = [
        new Date(2014, 0, 1),
        new Date(2015, 0, 1),
        new Date(2014, 11, 31),
        new Date(2015, 11, 31),
        new Date(2014, 7, 1),
        new Date(2015, 7, 1),
        new Date(1985, 0, 11)
      ];

      beforeEach(function() {
        elm = compileDirective('options-timezone-utc', {selectedDate: dates[i]});
      });

      afterEach(function() { i++ });

      for (var t = 0; t < dates.length; t++) {
        it('should select date in utc timezone', function () {
          expect(elm.val()).toBe(dateFilter(dates[i], 'yyyy-MM-dd', 'UTC'));
          expect(scope.selectedDate.toDateString()).toBe(dates[i].toDateString());
          angular.element(elm[0]).triggerHandler('focus');
          angular.element(sandboxEl.find('.dropdown-menu tbody .btn:contains(15)')).triggerHandler('click');
          expect(elm.val()).toBe(dateFilter(dates[i], 'yyyy-MM-\'15\'', 'UTC'));
        });
      }

    });

    describe('strictFormat', function () {

      it('should support strict format date parse', function () {
        var elm = compileDirective('options-strictFormat');
        expect(elm.val()).toBe('1986-2-4');
        elm.val('1996-4-7');
        angular.element(elm[0]).triggerHandler('change');
        expect(elm.val()).toBe('1996-4-7');
        expect(elm.hasClass('ng-valid')).toBe(true);
      });

    });

    describe('minDate', function() {

      it('should support a dynamic minDate', function() {
        var elm = compileDirective('options-minDate');
        angular.element(elm[0]).triggerHandler('focus');
        expect(sandboxEl.find('.dropdown-menu tbody button:contains(19)').is(':disabled')).toBeTruthy();
        expect(sandboxEl.find('.dropdown-menu tbody button:contains(20)').is(':disabled')).toBeFalsy();
        scope.minDate = '02/12/86';
        scope.$digest();
        expect(sandboxEl.find('.dropdown-menu tbody button:contains(11)').is(':disabled')).toBeTruthy();
        expect(sandboxEl.find('.dropdown-menu tbody button:contains(12)').is(':disabled')).toBeFalsy();
      });

      it('should support today as minDate', function() {
        var elm = compileDirective('options-minDate-today');
        angular.element(elm[0]).triggerHandler('focus');
        var todayDate = today.getDate();
        expect(sandboxEl.find('.dropdown-menu tbody button[disabled]').length > 0).toBeTruthy();
        expect(sandboxEl.find('.dropdown-menu tbody button:contains(' + todayDate + ').btn-primary').length).toBe(1);
        expect(sandboxEl.find('.dropdown-menu tbody button:contains(' + todayDate + ').btn-primary').is(':disabled')).toBeFalsy();
      });

      it('should support number as minDate', function() {
        var elm = compileDirective('options-minDate-number');
        angular.element(elm[0]).triggerHandler('focus');
        expect(sandboxEl.find('.dropdown-menu tbody button:contains(19)').is(':disabled')).toBeTruthy();
        expect(sandboxEl.find('.dropdown-menu tbody button:contains(20)').is(':disabled')).toBeFalsy();
      });

      it('should support Date object as minDate', function() {
        var elm = compileDirective('options-minDate-date');
        angular.element(elm[0]).triggerHandler('focus');
        expect(sandboxEl.find('.dropdown-menu tbody button:contains(19)').is(':disabled')).toBeTruthy();
        expect(sandboxEl.find('.dropdown-menu tbody button:contains(20)').is(':disabled')).toBeFalsy();
      });

      it('should trigger validation when minDate changes', function() {
        var elm = compileDirective('options-minDate-date');
        expect(elm.hasClass('ng-valid')).toBe(true);
        scope.minDate = new Date(1986, 1, 25);
        scope.$digest();
        expect(elm.hasClass('ng-valid')).toBe(false);
        scope.minDate = new Date(1986, 1, 18);
        scope.$digest();
        expect(elm.hasClass('ng-valid')).toBe(true);
      });

      it('should reset minDate to -Infinity when set empty string', function() {
        var elm = compileDirective('options-minDate');
        angular.element(elm[0]).triggerHandler('focus');
        expect(sandboxEl.find('.dropdown-menu tbody button:contains(19)').is(':disabled')).toBeTruthy();
        expect(sandboxEl.find('.dropdown-menu tbody button:contains(20)').is(':disabled')).toBeFalsy();
        scope.minDate = '';
        scope.$digest();
        expect(sandboxEl.find('.dropdown-menu tbody button:contains(19)').is(':disabled')).toBeFalsy();
        expect(sandboxEl.find('.dropdown-menu tbody button:contains(20)').is(':disabled')).toBeFalsy();
      });

    });

    describe('maxDate', function() {

      it('should support a dynamic maxDate', function() {
        var elm = compileDirective('options-maxDate');
        angular.element(elm[0]).triggerHandler('focus');
        expect(sandboxEl.find('.dropdown-menu tbody button:contains(24)').is(':disabled')).toBeFalsy();
        expect(sandboxEl.find('.dropdown-menu tbody button:contains(25)').is(':disabled')).toBeTruthy();
        scope.maxDate = '02/12/86';
        scope.$digest();
        // @TODO fixme
        // expect(sandboxEl.find('.dropdown-menu tbody button:contains(12)').is(':disabled')).toBeFalsy();
        expect(sandboxEl.find('.dropdown-menu tbody button:contains(13)').is(':disabled')).toBeTruthy();
      });

      it('should support today as maxDate', function() {
        var elm = compileDirective('options-maxDate-today');
        angular.element(elm[0]).triggerHandler('focus');
        var todayDate = today.getDate();
        expect(sandboxEl.find('.dropdown-menu tbody button[disabled]').length > 0).toBeTruthy();
        expect(sandboxEl.find('.dropdown-menu tbody button:contains(' + todayDate + ').btn-primary').length).toBe(1);
        expect(sandboxEl.find('.dropdown-menu tbody button:contains(' + todayDate + ').btn-primary').is(':disabled')).toBeFalsy();
      });

      it('should support number as maxDate', function() {
        var elm = compileDirective('options-maxDate-number');
        angular.element(elm[0]).triggerHandler('focus');
        expect(sandboxEl.find('.dropdown-menu tbody button:contains(24)').is(':disabled')).toBeFalsy();
        expect(sandboxEl.find('.dropdown-menu tbody button:contains(25)').is(':disabled')).toBeTruthy();
      });

      it('should support Date object as maxDate', function() {
        var elm = compileDirective('options-maxDate-date');
        angular.element(elm[0]).triggerHandler('focus');
        expect(sandboxEl.find('.dropdown-menu tbody button:contains(24)').is(':disabled')).toBeFalsy();
        expect(sandboxEl.find('.dropdown-menu tbody button:contains(25)').is(':disabled')).toBeTruthy();
      });

      it('should trigger validation when maxDate changes', function() {
        var elm = compileDirective('options-maxDate-date');
        expect(elm.hasClass('ng-valid')).toBe(true);
        scope.maxDate = new Date(1986, 1, 20);
        scope.$digest();
        expect(elm.hasClass('ng-valid')).toBe(false);
        scope.maxDate = new Date(1986, 1, 26);
        scope.$digest();
        expect(elm.hasClass('ng-valid')).toBe(true);
      });

      it('should reset maxDate to +Infinity when set empty string', function() {
        var elm = compileDirective('options-maxDate');
        angular.element(elm[0]).triggerHandler('focus');
        expect(sandboxEl.find('.dropdown-menu tbody button:contains(24)').is(':disabled')).toBeFalsy();
        expect(sandboxEl.find('.dropdown-menu tbody button:contains(25)').is(':disabled')).toBeTruthy();
        scope.maxDate = '';
        scope.$digest();
        expect(sandboxEl.find('.dropdown-menu tbody button:contains(24)').is(':disabled')).toBeFalsy();
        expect(sandboxEl.find('.dropdown-menu tbody button:contains(25)').is(':disabled')).toBeFalsy();
      });

    });

    describe('startWeek', function() {

      it('should support a dynamic startWeek', function() {
        var elm = compileDirective('options-startWeek');
        angular.element(elm[0]).triggerHandler('focus');
        expect(sandboxEl.find('.dropdown-menu thead tr:eq(1) th:eq(0)').text()).toBe('Mon');
        expect(sandboxEl.find('.dropdown-menu tbody button:eq(0)').text()).toBe('27');
      });

      it('should support a negative gap induced by startWeek', function() {
        var elm = compileDirective('options-startWeek-bis');
        angular.element(elm[0]).triggerHandler('focus');
        expect(sandboxEl.find('.dropdown-menu thead tr:eq(1) th:eq(0)').text()).toBe('Sat');
        expect(sandboxEl.find('.dropdown-menu tbody button:eq(0)').text()).toBe('28');
      });

    });

    describe('startDate', function() {

      it('should support a dynamic startDate', function() {
        var elm = compileDirective('options-startDate');
        angular.element(elm[0]).triggerHandler('focus');
        expect(sandboxEl.find('.dropdown-menu thead button:eq(1)').text()).toBe(dateFilter(new Date(scope.startDate), 'MMMM yyyy'));
      });

      it('should support a dynamic startDate from date object', function() {
        var elm = compileDirective('options-startDate', {startDate: new Date(2014, 2, 2)});
        angular.element(elm[0]).triggerHandler('focus');
        expect(sandboxEl.find('.dropdown-menu thead button:eq(1)').text()).toBe(dateFilter(scope.startDate, 'MMMM yyyy'));
      });

    });

    describe('useNative', function() {

      it('should correctly compile template according to useNative', function() {
        var elm = compileDirective('options-useNative');
        angular.element(elm[0]).triggerHandler('focus');
        // @TODO ?
      });

    });

    describe('container', function() {

      it('should append to container if defined', function() {
        var testElm = $('<div id="testElm"></div>');
        sandboxEl.append(testElm);
        var elm = compileDirective('options-container', {container: '#testElm'});
        angular.element(elm[0]).triggerHandler('focus');
        expect(testElm.find('.datepicker').length).toBe(1);
      })

      it('should put datepicker in sandbox when container is falsy', function() {
        var elm = compileDirective('options-container', {container: 'false'});
        angular.element(elm[0]).triggerHandler('focus');
        expect(sandboxEl.find('.datepicker').length).toBe(1);
      })

    })

    describe('hasToday', function() {

      it('should correctly support today feature inner content', function() {
        var elm = compileDirective('options-hasToday', {selectedDate: new Date(2015, 3, 1)});
        angular.element(elm[0]).triggerHandler('focus');
        expect(sandboxEl.find('.dropdown-menu tfoot>tr button.today').length).toBe(1);
        expect(sandboxEl.find('.dropdown-menu tfoot>tr>td').length).toBe(1);
        expect(sandboxEl.find('.dropdown-menu tfoot>tr>td').attr('colspan')).toBe('7');
      });

      it('should not be enable if today is out of valid period', function() {
        var elm = compileDirective('options-hasToday', {selectedDate: new Date(2015, 3, 1)});
        angular.element(elm[0]).triggerHandler('focus');

        // Set min-date to tomorrow
        scope.minDate = new Date(today.getTime() + 86400000);
        scope.$digest();
        expect(sandboxEl.find('.dropdown-menu tfoot button.today').is(':disabled')).toBeTruthy();

        // Set min-date to yesterday
        scope.minDate = new Date(today.getTime() - 86400000);
        scope.$digest();
        expect(sandboxEl.find('.dropdown-menu tfoot button.today').is(':disabled')).toBeFalsy();
      });

      it('should set correct value', function() {
        var elm = compileDirective('options-hasToday', {selectedDate: new Date(2015, 3, 1)});
        angular.element(elm[0]).triggerHandler('focus');

        expect(elm.val()).toBe('2015-04-01');
        angular.element(sandboxEl.find('.dropdown-menu tfoot>tr button.today')).triggerHandler('click');
        expect(elm.val()).toBe(dateFilter(today, 'yyyy-MM-dd'));
      });

    })

    describe('hasClear', function() {

      it('should correctly support clear feature inner content', function() {
        var elm = compileDirective('options-hasClear', {selectedDate: new Date(2015, 3, 1)});
        angular.element(elm[0]).triggerHandler('focus');
        expect(sandboxEl.find('.dropdown-menu tfoot>tr button.clear').length).toBe(1);
        expect(sandboxEl.find('.dropdown-menu tfoot>tr>td').length).toBe(1);
        expect(sandboxEl.find('.dropdown-menu tfoot>tr>td').attr('colspan')).toBe('7');
      });

      it('should correctly cleared', function() {
        var elm = compileDirective('options-hasClear', {selectedDate: new Date(2015, 3, 1)});
        angular.element(elm[0]).triggerHandler('focus');
        expect(elm.val()).toBe('2015-04-01');
        angular.element(sandboxEl.find('.dropdown-menu tfoot>tr button.clear')).triggerHandler('click');
        expect(elm.val()).toBe('');
      });

    })

  });

  describe('dateModelFormat', function() {

    it('should support a custom modelDateFormat', function() {
      var elm = compileDirective('options-modelDateFormat');

      // Should have the predefined value
      expect(elm.val()).toBe('01/12/2014');

      // Should correctly set the model value if set via the datepicker
      angular.element(elm[0]).triggerHandler('focus');
      angular.element(sandboxEl.find('.dropdown-menu tbody .btn:contains(24)')).triggerHandler('click');
      expect(elm.val()).toBe('24/12/2014');
      expect(scope.selectedDate).toBe('2014-12-24');

      // Should correctly set the model if the date is manually typed into the input
      elm.val('20/11/2014');
      angular.element(elm[0]).triggerHandler('change');
      scope.$digest();
      expect(scope.selectedDate).toBe('2014-11-20');
    });


    it('should support longDate modelDateFormat', function() {
      var elm = compileDirective('options-modelDateFormat-longDate');

      // Should have the predefined value
      expect(elm.val()).toBe('12/1/14');

      // Should correctly set the model value if set via the datepicker
      angular.element(elm[0]).triggerHandler('focus');
      angular.element(sandboxEl.find('.dropdown-menu tbody .btn:contains(24)')).triggerHandler('click');
      expect(elm.val()).toBe('12/24/14');
      expect(scope.selectedDate).toBe('December 24, 2014');

      // Should correctly set the model if the date is manually typed into the input
      elm.val('11/20/14');
      angular.element(elm[0]).triggerHandler('change');
      scope.$digest();
      expect(scope.selectedDate).toBe('November 20, 2014');
    });

  });

  describe('daysOfWeekDisabled', function() {

      it('should enable all days of the week by default', function() {
        var elm = compileDirective('options-daysOfWeekDisabled');
        angular.element(elm[0]).triggerHandler('focus');
        expect(sandboxEl.find('.dropdown-menu tbody button:contains(20)').is(':disabled')).toBeFalsy();
        expect(sandboxEl.find('.dropdown-menu tbody button:contains(21)').is(':disabled')).toBeFalsy();
        expect(sandboxEl.find('.dropdown-menu tbody button:contains(22)').is(':disabled')).toBeFalsy();
        expect(sandboxEl.find('.dropdown-menu tbody button:contains(23)').is(':disabled')).toBeFalsy();
        expect(sandboxEl.find('.dropdown-menu tbody button:contains(24)').is(':disabled')).toBeFalsy();
        expect(sandboxEl.find('.dropdown-menu tbody button:contains(25)').is(':disabled')).toBeFalsy();
        expect(sandboxEl.find('.dropdown-menu tbody button:contains(26)').is(':disabled')).toBeFalsy();
      });

      it('should allow disabling some days of the week', function() {
        var elm = compileDirective('options-daysOfWeekDisabled-bis');
        angular.element(elm[0]).triggerHandler('focus');
        expect(sandboxEl.find('.dropdown-menu tbody button:contains(20)').is(':disabled')).toBeTruthy();
        expect(sandboxEl.find('.dropdown-menu tbody button:contains(21)').is(':disabled')).toBeFalsy();
        expect(sandboxEl.find('.dropdown-menu tbody button:contains(22)').is(':disabled')).toBeTruthy();
        expect(sandboxEl.find('.dropdown-menu tbody button:contains(23)').is(':disabled')).toBeFalsy();
        expect(sandboxEl.find('.dropdown-menu tbody button:contains(24)').is(':disabled')).toBeTruthy();
        expect(sandboxEl.find('.dropdown-menu tbody button:contains(25)').is(':disabled')).toBeFalsy();
        expect(sandboxEl.find('.dropdown-menu tbody button:contains(26)').is(':disabled')).toBeTruthy();
      });

    });

  describe('disabledDates', function() {

    it('should not disable any dates by default', function() {
      var disabledScopeProperty = {
        disabledDates: []
      };
      var elem = compileDirective('options-disabledDates', disabledScopeProperty);
      angular.element(elem[0]).triggerHandler('focus');
      expect(sandboxEl.find('.dropdown-menu tbody button:contains(20)').is(':disabled')).toBeFalsy();
      expect(sandboxEl.find('.dropdown-menu tbody button:contains(21)').is(':disabled')).toBeFalsy();
      expect(sandboxEl.find('.dropdown-menu tbody button:contains(22)').is(':disabled')).toBeFalsy();
      expect(sandboxEl.find('.dropdown-menu tbody button:contains(23)').is(':disabled')).toBeFalsy();
      expect(sandboxEl.find('.dropdown-menu tbody button:contains(24)').is(':disabled')).toBeFalsy();
      expect(sandboxEl.find('.dropdown-menu tbody button:contains(25)').is(':disabled')).toBeFalsy();
      expect(sandboxEl.find('.dropdown-menu tbody button:contains(26)').is(':disabled')).toBeFalsy();
    });

    it('should disable a single date range', function() {
      var disabledScopeProperty = {
        disabledDates: [
          { start: new Date(2014, 6, 22), end: new Date(2014, 6, 25)}
        ]
      };
      var elem = compileDirective('options-disabledDates', disabledScopeProperty);
      angular.element(elem[0]).triggerHandler('focus');
      expect(sandboxEl.find('.dropdown-menu tbody button:contains(20)').is(':disabled')).toBeFalsy();
      expect(sandboxEl.find('.dropdown-menu tbody button:contains(21)').is(':disabled')).toBeFalsy();
      expect(sandboxEl.find('.dropdown-menu tbody button:contains(22)').is(':disabled')).toBeTruthy();
      expect(sandboxEl.find('.dropdown-menu tbody button:contains(23)').is(':disabled')).toBeTruthy();
      expect(sandboxEl.find('.dropdown-menu tbody button:contains(24)').is(':disabled')).toBeTruthy();
      expect(sandboxEl.find('.dropdown-menu tbody button:contains(25)').is(':disabled')).toBeTruthy();
      expect(sandboxEl.find('.dropdown-menu tbody button:contains(26)').is(':disabled')).toBeFalsy();
    });

    it('should disable multiple sorted date ranges', function() {
      var disabledScopeProperty = {
        disabledDates: [
          { start: new Date(2014, 6, 21), end: new Date(2014, 6, 22) },
          { start: new Date(2014, 6, 24), end: new Date(2014, 6, 24) }
        ]
      };
      var elem = compileDirective('options-disabledDates', disabledScopeProperty);
      angular.element(elem[0]).triggerHandler('focus');
      expect(sandboxEl.find('.dropdown-menu tbody button:contains(20)').is(':disabled')).toBeFalsy();
      expect(sandboxEl.find('.dropdown-menu tbody button:contains(21)').is(':disabled')).toBeTruthy();
      expect(sandboxEl.find('.dropdown-menu tbody button:contains(22)').is(':disabled')).toBeTruthy();
      expect(sandboxEl.find('.dropdown-menu tbody button:contains(23)').is(':disabled')).toBeFalsy();
      expect(sandboxEl.find('.dropdown-menu tbody button:contains(24)').is(':disabled')).toBeTruthy();
      expect(sandboxEl.find('.dropdown-menu tbody button:contains(25)').is(':disabled')).toBeFalsy();
      expect(sandboxEl.find('.dropdown-menu tbody button:contains(26)').is(':disabled')).toBeFalsy();
    });

    it('should disable multiple sorted date ranges out of order', function() {
      var disabledScopeProperty = {
        disabledDates: [
          { start: new Date(2014, 6, 24), end: new Date(2014, 6, 24) },
          { start: new Date(2014, 6, 21), end: new Date(2014, 6, 22) }
        ]
      };
      var elem = compileDirective('options-disabledDates', disabledScopeProperty);
      angular.element(elem[0]).triggerHandler('focus');
      expect(sandboxEl.find('.dropdown-menu tbody button:contains(20)').is(':disabled')).toBeFalsy();
      expect(sandboxEl.find('.dropdown-menu tbody button:contains(21)').is(':disabled')).toBeTruthy();
      expect(sandboxEl.find('.dropdown-menu tbody button:contains(22)').is(':disabled')).toBeTruthy();
      expect(sandboxEl.find('.dropdown-menu tbody button:contains(23)').is(':disabled')).toBeFalsy();
      expect(sandboxEl.find('.dropdown-menu tbody button:contains(24)').is(':disabled')).toBeTruthy();
      expect(sandboxEl.find('.dropdown-menu tbody button:contains(25)').is(':disabled')).toBeFalsy();
      expect(sandboxEl.find('.dropdown-menu tbody button:contains(26)').is(':disabled')).toBeFalsy();
    });

    it('should work combined with minDate/maxDate', function() {
      var disabledScopeProperty = {
        disabledDates: [
          { start: new Date(2014, 6, 23), end: new Date(2014, 6, 24) }
        ]
      };
      var elem = compileDirective('options-disabledDates-minmax', disabledScopeProperty);
      angular.element(elem[0]).triggerHandler('focus');
      expect(sandboxEl.find('.dropdown-menu tbody button:contains(20)').is(':disabled')).toBeTruthy();
      expect(sandboxEl.find('.dropdown-menu tbody button:contains(21)').is(':disabled')).toBeFalsy();
      expect(sandboxEl.find('.dropdown-menu tbody button:contains(22)').is(':disabled')).toBeFalsy();
      expect(sandboxEl.find('.dropdown-menu tbody button:contains(23)').is(':disabled')).toBeTruthy();
      expect(sandboxEl.find('.dropdown-menu tbody button:contains(24)').is(':disabled')).toBeTruthy();
      expect(sandboxEl.find('.dropdown-menu tbody button:contains(25)').is(':disabled')).toBeFalsy();
      expect(sandboxEl.find('.dropdown-menu tbody button:contains(26)').is(':disabled')).toBeTruthy();
    });

    it('should work combined with daysOfWeekDisabled', function() {
      var disabledScopeProperty = {
        disabledDates: [
          { start: new Date(2014, 6, 22), end: new Date(2014, 6, 22) }
        ]
      };
      var elem = compileDirective('options-disabledDates-daysOfWeek', disabledScopeProperty);
      angular.element(elem[0]).triggerHandler('focus');
      expect(sandboxEl.find('.dropdown-menu tbody button:contains(20)').is(':disabled')).toBeTruthy(); // July 25, 2014 is a Sunday, disabled
      expect(sandboxEl.find('.dropdown-menu tbody button:contains(21)').is(':disabled')).toBeFalsy();
      expect(sandboxEl.find('.dropdown-menu tbody button:contains(22)').is(':disabled')).toBeTruthy();
      expect(sandboxEl.find('.dropdown-menu tbody button:contains(23)').is(':disabled')).toBeFalsy();
      expect(sandboxEl.find('.dropdown-menu tbody button:contains(24)').is(':disabled')).toBeFalsy();
      expect(sandboxEl.find('.dropdown-menu tbody button:contains(25)').is(':disabled')).toBeFalsy();
      expect(sandboxEl.find('.dropdown-menu tbody button:contains(26)').is(':disabled')).toBeFalsy();
    });

  });

  describe('datepickerViews', function () {
    var picker;

    beforeEach(inject(function () {
      picker = {
        select: function (date, keep) {},
        $options: {
          startWeek: 0,
          daysOfWeekDisabled: '',
          dateFormat: 'shortDate'
        },
        $date: null
      };

      spyOn(picker, 'select');
    }));

    it('should change the day when navigating with the keyboard in the day view', inject(function (_datepickerViews_) {
      var datepickerViews = _datepickerViews_(picker);

      picker.$date = new Date(2014, 0, 6);
      datepickerViews.views[0].onKeyDown({ keyCode: 37 });
      expect(picker.select).toHaveBeenCalledWith(new Date(2014, 0, 5), true);

      picker.select.calls.reset();
      picker.$date = new Date(2014, 0, 6);
      datepickerViews.views[0].onKeyDown({ keyCode: 38 });
      expect(picker.select).toHaveBeenCalledWith(new Date(2013, 11, 30), true);

      picker.select.calls.reset();
      picker.$date = new Date(2014, 0, 6);
      datepickerViews.views[0].onKeyDown({ keyCode: 39 });
      expect(picker.select).toHaveBeenCalledWith(new Date(2014, 0, 7), true);

      picker.select.calls.reset();
      picker.$date = new Date(2014, 0, 6);
      datepickerViews.views[0].onKeyDown({ keyCode: 40 });
      expect(picker.select).toHaveBeenCalledWith(new Date(2014, 0, 13), true);

      picker.select.calls.reset();
      picker.$date = null;
      datepickerViews.views[0].onKeyDown({ keyCode: 40 });
      expect(picker.select).not.toHaveBeenCalled();
    }));

    it('should change the month when navigating with the keyboard in the month view', inject(function (_datepickerViews_) {
      var datepickerViews = _datepickerViews_(picker);

      picker.$date = new Date(2014, 0, 6);
      datepickerViews.views[1].onKeyDown({ keyCode: 37 });
      expect(picker.select).toHaveBeenCalledWith(new Date(2013, 11, 6), true);

      picker.select.calls.reset();
      picker.$date = new Date(2014, 0, 6);
      datepickerViews.views[1].onKeyDown({ keyCode: 38 });
      expect(picker.select).toHaveBeenCalledWith(new Date(2013, 8, 6), true);

      picker.select.calls.reset();
      picker.$date = new Date(2014, 0, 6);
      datepickerViews.views[1].onKeyDown({ keyCode: 39 });
      expect(picker.select).toHaveBeenCalledWith(new Date(2014, 1, 6), true);

      picker.select.calls.reset();
      picker.$date = new Date(2014, 0, 6);
      datepickerViews.views[1].onKeyDown({ keyCode: 40 });
      expect(picker.select).toHaveBeenCalledWith(new Date(2014, 4, 6), true);

      picker.select.calls.reset();
      picker.$date = null;
      datepickerViews.views[1].onKeyDown({ keyCode: 40 });
      expect(picker.select).not.toHaveBeenCalled();
    }));

    it('should change the year when navigating with the keyboard in the year view', inject(function (_datepickerViews_) {
      var datepickerViews = _datepickerViews_(picker);

      picker.$date = new Date(2014, 0, 6);
      datepickerViews.views[2].onKeyDown({ keyCode: 37 });
      expect(picker.select).toHaveBeenCalledWith(new Date(2013, 0, 6), true);

      picker.select.calls.reset();
      picker.$date = new Date(2014, 0, 6);
      datepickerViews.views[2].onKeyDown({ keyCode: 38 });
      expect(picker.select).toHaveBeenCalledWith(new Date(2010, 0, 6), true);

      picker.select.calls.reset();
      picker.$date = new Date(2014, 0, 6);
      datepickerViews.views[2].onKeyDown({ keyCode: 39 });
      expect(picker.select).toHaveBeenCalledWith(new Date(2015, 0, 6), true);

      picker.select.calls.reset();
      picker.$date = new Date(2014, 0, 6);
      datepickerViews.views[2].onKeyDown({ keyCode: 40 });
      expect(picker.select).toHaveBeenCalledWith(new Date(2018, 0, 6), true);

      picker.select.calls.reset();
      picker.$date = null;
      datepickerViews.views[2].onKeyDown({ keyCode: 40 });
      expect(picker.select).not.toHaveBeenCalled();
    }));
  });

  describe('prefix', function () {
    it('should call namespaced events from provider', function() {
      var myDatepicker = $datepicker(sandboxEl, null, {prefixEvent: 'dp', scope : scope});
      var emit = spyOn(myDatepicker.$scope, '$emit');
      scope.$digest();
      myDatepicker.show();
      myDatepicker.hide();
      $animate.flush();

      expect(emit).toHaveBeenCalledWith('dp.show.before', myDatepicker);
      expect(emit).toHaveBeenCalledWith('dp.show', myDatepicker);
      expect(emit).toHaveBeenCalledWith('dp.hide.before', myDatepicker);
      expect(emit).toHaveBeenCalledWith('dp.hide', myDatepicker);
    });

    it('should call namespaced events from directive', function() {
      var elm = compileDirective('default-with-namespace');
      var showBefore, show, hideBefore, hide;
      scope.$on('modal.show.before', function() {
        showBefore = true;
      });
      scope.$on('modal.show', function() {
        show = true;
      });
      scope.$on('modal.hide.before', function() {
        hideBefore = true;
      });
      scope.$on('modal.hide', function() {
        hide = true;
      });

      angular.element(elm[0]).triggerHandler('focus');
      $animate.flush();

      expect(showBefore).toBe(true);
      expect(show).toBe(true);

      angular.element(elm[0]).triggerHandler('blur');
      $animate.flush();

      expect(hideBefore).toBe(true);
      expect(hide).toBe(true);
    });

  });

  describe('onBeforeShow', function() {

    it('should invoke beforeShow event callback', function() {
      var beforeShow = false;

      function onBeforeShow(select) {
        beforeShow = true;
      }

      var elm = compileDirective('options-events', {onBeforeShow: onBeforeShow});

      angular.element(elm[0]).triggerHandler('focus');

      expect(beforeShow).toBe(true);
    });
  });

  describe('onShow', function() {

    it('should invoke show event callback', function() {
      var show = false;

      function onShow(select) {
        show = true;
      }

      var elm = compileDirective('options-events', {onShow: onShow});

      angular.element(elm[0]).triggerHandler('focus');
      $animate.flush();

      expect(show).toBe(true);
    });
  });

  describe('onBeforeHide', function() {

    it('should invoke beforeHide event callback', function() {
      var beforeHide = false;

      function onBeforeHide(select) {
        beforeHide = true;
      }

      var elm = compileDirective('options-events', {onBeforeHide: onBeforeHide});

      angular.element(elm[0]).triggerHandler('focus');
      angular.element(elm[0]).triggerHandler('blur');

      expect(beforeHide).toBe(true);
    });
  });

  describe('onHide', function() {

    it('should invoke show event callback', function() {
      var hide = false;

      function onHide(select) {
        hide = true;
      }

      var elm = compileDirective('options-events', {onHide: onHide});

      angular.element(elm[0]).triggerHandler('focus');
      angular.element(elm[0]).triggerHandler('blur');
      $animate.flush();

      expect(hide).toBe(true);
    });
  });
});