'use strict';

describe('ui-select tests', function() {
  var scope, $rootScope, $compile, $timeout, $injector;

  var Key = {
    Enter: 13,
    Tab: 9,
    Up: 38,
    Down: 40,
    Left: 37,
    Right: 39,
    Backspace: 8,
    Delete: 46,
    Escape: 27
  };

  beforeEach(module('ngSanitize', 'ui.select'));
  beforeEach(inject(function(_$rootScope_, _$compile_, _$timeout_, _$injector_) {
    $rootScope = _$rootScope_;
    scope = $rootScope.$new();
    $compile = _$compile_;
    $timeout = _$timeout_;
    $injector = _$injector_;
    scope.selection = {};

    scope.getGroupLabel = function(person) {
      return person.age % 2 ? 'even' : 'odd';
    };

    scope.people = [
      { name: 'Adam',      email: 'adam@email.com',      group: 'Foo', age: 12 },
      { name: 'Amalie',    email: 'amalie@email.com',    group: 'Foo', age: 12 },
      { name: 'Estefanía', email: 'estefanía@email.com', group: 'Foo', age: 21 },
      { name: 'Adrian',    email: 'adrian@email.com',    group: 'Foo', age: 21 },
      { name: 'Wladimir',  email: 'wladimir@email.com',  group: 'Foo', age: 30 },
      { name: 'Samantha',  email: 'samantha@email.com',  group: 'bar', age: 30 },
      { name: 'Nicole',    email: 'nicole@email.com',    group: 'bar', age: 43 },
      { name: 'Natasha',   email: 'natasha@email.com',   group: 'Baz', age: 54 }
    ];

    scope.someObject = {};
    scope.someObject.people = [
      { name: 'Adam',      email: 'adam@email.com',      group: 'Foo', age: 12 },
      { name: 'Amalie',    email: 'amalie@email.com',    group: 'Foo', age: 12 },
      { name: 'Estefanía', email: 'estefanía@email.com', group: 'Foo', age: 21 },
      { name: 'Adrian',    email: 'adrian@email.com',    group: 'Foo', age: 21 },
      { name: 'Wladimir',  email: 'wladimir@email.com',  group: 'Foo', age: 30 },
      { name: 'Samantha',  email: 'samantha@email.com',  group: 'bar', age: 30 },
      { name: 'Nicole',    email: 'nicole@email.com',    group: 'bar', age: 43 },
      { name: 'Natasha',   email: 'natasha@email.com',   group: 'Baz', age: 54 }
    ];
  }));


  // DSL (domain-specific language)

  function compileTemplate(template) {
    var el = $compile(angular.element(template))(scope);
    scope.$digest();
    return el;
  }

  function createUiSelect(attrs) {
    var attrsHtml = '';
    if (attrs !== undefined) {
      if (attrs.disabled !== undefined) { attrsHtml += ' ng-disabled="' + attrs.disabled + '"'; }
      if (attrs.required !== undefined) { attrsHtml += ' ng-required="' + attrs.required + '"'; }
      if (attrs.theme !== undefined) { attrsHtml += ' theme="' + attrs.theme + '"'; }
      if (attrs.tabindex !== undefined) { attrsHtml += ' tabindex="' + attrs.tabindex + '"'; }
      if (attrs.tagging !== undefined) { attrsHtml += ' tagging="' + attrs.tagging + '"'; }
      if (attrs.title !== undefined) { attrsHtml += ' title="' + attrs.title + '"'; }
    }

    return compileTemplate(
      '<ui-select ng-model="selection.selected"' + attrsHtml + '> \
        <ui-select-match placeholder="Pick one...">{{$select.selected.name}}</ui-select-match> \
        <ui-select-choices repeat="person in people | filter: $select.search"> \
          <div ng-bind-html="person.name | highlight: $select.search"></div> \
          <div ng-bind-html="person.email | highlight: $select.search"></div> \
        </ui-select-choices> \
      </ui-select>'
    );
  }

  function getMatchLabel(el) {
    return $(el).find('.ui-select-match > button:first > span[ng-transclude]:not(.ng-hide)').text();
  }

  function clickItem(el, text) {

    if (!isDropdownOpened(el)){
      openDropdown(el);
    }

    $(el).find('.ui-select-choices-row div:contains("' + text + '")').click();
    scope.$digest();
  }

  function clickMatch(el) {
    $(el).find('.ui-select-match > button:first').click();
    scope.$digest();
  }

  function isDropdownOpened(el) {
    // Does not work with jQuery 2.*, have to use jQuery 1.11.*
    // This will be fixed in AngularJS 1.3
    // See issue with unit-testing directive using karma https://github.com/angular/angular.js/issues/4640#issuecomment-35002427
    return el.scope().$select.open && el.hasClass('open');
  }

  function triggerKeydown(element, keyCode) {
    var e = jQuery.Event("keydown");
    e.which = keyCode;
    e.keyCode = keyCode;
    element.trigger(e);
  }

  function setSearchText(el, text) {
    el.scope().$select.search = text;
    scope.$digest();
    $timeout.flush();
  }

  function openDropdown(el) {
    var $select = el.scope().$select;
    $select.open = true;
    scope.$digest();
  };


  // Tests

  it('should compile child directives', function() {
    var el = createUiSelect();

    var searchEl = $(el).find('.ui-select-search');
    expect(searchEl.length).toEqual(1);

    var matchEl = $(el).find('.ui-select-match');
    expect(matchEl.length).toEqual(1);

    var choicesContentEl = $(el).find('.ui-select-choices-content');
    expect(choicesContentEl.length).toEqual(1);

    var choicesContainerEl = $(el).find('.ui-select-choices');
    expect(choicesContainerEl.length).toEqual(1);

    openDropdown(el);
    var choicesEls = $(el).find('.ui-select-choices-row');
    expect(choicesEls.length).toEqual(8);
  });

  it('should correctly render initial state', function() {
    scope.selection.selected = scope.people[0];

    var el = createUiSelect();

    expect(getMatchLabel(el)).toEqual('Adam');
  });
  
  it('should correctly render initial state with track by feature', function() {
    var el = compileTemplate(
      '<ui-select ng-model="selection.selected"> \
        <ui-select-match placeholder="Pick one...">{{$select.selected.name}}</ui-select-match> \
        <ui-select-choices repeat="person in people | filter: $select.search track by person.name"> \
          <div ng-bind-html="person.name | highlight: $select.search"></div> \
          <div ng-bind-html="person.email | highlight: $select.search"></div> \
        </ui-select-choices> \
      </ui-select>'
    );
    scope.selection.selected =  { name: 'Samantha',  email: 'something different than array source',  group: 'bar', age: 30 };
    scope.$digest();
    expect(getMatchLabel(el)).toEqual('Samantha');
  });

  it('should display the choices when activated', function() {
    var el = createUiSelect();

    expect(isDropdownOpened(el)).toEqual(false);

    clickMatch(el);

    expect(isDropdownOpened(el)).toEqual(true);
  });

  it('should select an item', function() {
    var el = createUiSelect();

    clickItem(el, 'Samantha');

    expect(getMatchLabel(el)).toEqual('Samantha');
  });

  it('should select an item (controller)', function() {
    var el = createUiSelect();

    el.scope().$select.select(scope.people[1]);
    scope.$digest();

    expect(getMatchLabel(el)).toEqual('Amalie');
  });

  it('should not select a non existing item', function() {
    var el = createUiSelect();

    clickItem(el, "I don't exist");

    expect(getMatchLabel(el)).toEqual('');
  });

  it('should close the choices when an item is selected', function() {
    var el = createUiSelect();

    clickMatch(el);

    expect(isDropdownOpened(el)).toEqual(true);

    clickItem(el, 'Samantha');

    expect(isDropdownOpened(el)).toEqual(false);
  });


  it('should open/close dropdown when clicking caret icon', function() {

    var el = createUiSelect({theme : 'select2'});
    var searchInput = el.find('.ui-select-search');
    var $select = el.scope().$select;

    expect($select.open).toEqual(false);

    el.find(".ui-select-toggle").click();
    expect($select.open).toEqual(true);


    el.find(".ui-select-toggle").click();
    expect($select.open).toEqual(false);
  });

  it('should pass tabindex to focusser', function() {
    var el = createUiSelect({tabindex: 5});

    expect($(el).find('.ui-select-focusser').attr('tabindex')).toEqual('5');
    expect($(el).attr('tabindex')).toEqual(undefined);
  });

  it('should pass tabindex to focusser when tabindex is an expression', function() {
    scope.tabValue = 22;
    var el = createUiSelect({tabindex: '{{tabValue + 10}}'});

    expect($(el).find('.ui-select-focusser').attr('tabindex')).toEqual('32');
    expect($(el).attr('tabindex')).toEqual(undefined);
  });

  it('should not give focusser a tabindex when ui-select does not have one', function() {
    var el = createUiSelect();

    expect($(el).find('.ui-select-focusser').attr('tabindex')).toEqual(undefined);
    expect($(el).attr('tabindex')).toEqual(undefined);
  });

  it('should be disabled if the attribute says so', function() {
    var el1 = createUiSelect({disabled: true});
    expect(el1.scope().$select.disabled).toEqual(true);
    clickMatch(el1);
    expect(isDropdownOpened(el1)).toEqual(false);

    var el2 = createUiSelect({disabled: false});
    expect(el2.scope().$select.disabled).toEqual(false);
    clickMatch(el2);
    expect(isDropdownOpened(el2)).toEqual(true);

    var el3 = createUiSelect();
    expect(el3.scope().$select.disabled).toBeFalsy();
    clickMatch(el3);
    expect(isDropdownOpened(el3)).toEqual(true);
  });

  it('should allow tagging if the attribute says so', function() {
    var el = createUiSelect({tagging: true});
    clickMatch(el);

    $(el).scope().$select.select("I don't exist");

    expect($(el).scope().$select.selected).toEqual("I don't exist");
  });

  it('should format new items using the tagging function when the attribute is a function', function() {
    scope.taggingFunc = function (name) {
      return {
        name: name,
        email: name + '@email.com',
        group: 'Foo',
        age: 12
      };
    };

    var el = createUiSelect({tagging: 'taggingFunc'});
    clickMatch(el);

    $(el).scope().$select.search = 'idontexist';
    $(el).scope().$select.activeIndex = 0;
    $(el).scope().$select.select('idontexist');

    expect($(el).scope().$select.selected).toEqual({
      name: 'idontexist',
      email: 'idontexist@email.com',
      group: 'Foo',
      age: 12
    });
  });

  // See when an item that evaluates to false (such as "false" or "no") is selected, the placeholder is shown https://github.com/angular-ui/ui-select/pull/32
  it('should not display the placeholder when item evaluates to false', function() {
    scope.items = ['false'];

    var el = compileTemplate(
      '<ui-select ng-model="selection.selected"> \
        <ui-select-match>{{$select.selected}}</ui-select-match> \
        <ui-select-choices repeat="item in items | filter: $select.search"> \
          <div ng-bind-html="item | highlight: $select.search"></div> \
        </ui-select-choices> \
      </ui-select>'
    );
    expect(el.scope().$select.selected).toEqual(undefined);

    clickItem(el, 'false');

    expect(el.scope().$select.selected).toEqual('false');
    expect(getMatchLabel(el)).toEqual('false');
  });

  it('should close an opened select when another one is opened', function() {
    var el1 = createUiSelect();
    var el2 = createUiSelect();
    el1.appendTo(document.body);
    el2.appendTo(document.body);

    expect(isDropdownOpened(el1)).toEqual(false);
    expect(isDropdownOpened(el2)).toEqual(false);
    clickMatch(el1);
    expect(isDropdownOpened(el1)).toEqual(true);
    expect(isDropdownOpened(el2)).toEqual(false);
    clickMatch(el2);
    expect(isDropdownOpened(el1)).toEqual(false);
    expect(isDropdownOpened(el2)).toEqual(true);

    el1.remove();
    el2.remove();
  });

  describe('disabled options', function() {
    function createUiSelect(attrs) {
      var attrsDisabled = '';
      if (attrs !== undefined) {
        if (attrs.disabled !== undefined) {
          attrsDisabled = ' ui-disable-choice="' + attrs.disabled + '"';
        } else {
          attrsDisabled = '';
        }
      }

      return compileTemplate(
        '<ui-select ng-model="selection.selected"> \
          <ui-select-match placeholder="Pick one...">{{$select.selected.name}}</ui-select-match> \
          <ui-select-choices repeat="person in people | filter: $select.search"' + attrsDisabled + '> \
            <div ng-bind-html="person.name | highlight: $select.search"></div> \
            <div ng-bind-html="person.email | highlight: $select.search"></div> \
          </ui-select-choices> \
        </ui-select>'
      );
    }

    function disablePerson(opts) {
      opts = opts || {};

      var key = opts.key || 'people',
          disableAttr = opts.disableAttr || 'disabled',
          disableBool = opts.disableBool === undefined ? true : opts.disableBool,
          matchAttr = opts.match || 'name',
          matchVal = opts.matchVal || 'Wladimir';

      scope['_' + key] = angular.copy(scope[key]);
      scope[key].map(function (model) {
        if (model[matchAttr] == matchVal) {
          model[disableAttr] = disableBool;
        }
        return model;
      });
    }

    function resetScope(opts) {
      opts = opts || {};
      var key = opts.key || 'people';
      scope[key] = angular.copy(scope['_' + key]);
    }

    describe('without disabling expression', function () {
      beforeEach(function() {
        disablePerson();
        this.el = createUiSelect();
      });

      it('should not allow disabled options to be selected', function() {
        clickItem(this.el, 'Wladimir');

        expect(getMatchLabel(this.el)).toEqual('Wladimir');
      });

      it('should set a disabled class on the option', function() {
        var option = $(this.el).find('.ui-select-choices-row div:contains("Wladimir")');
        var container = option.closest('.ui-select-choices-row');

        expect(container.hasClass('disabled')).toBeFalsy();
      });
    });

    describe('disable on truthy property', function () {
      beforeEach(function() {
        disablePerson({
          disableAttr : 'inactive',
          disableBool : true
        });
        this.el = createUiSelect({
          disabled: 'person.inactive'
        });
      });

      it('should allow the user to define the selected option', function () {
        expect($(this.el).find('.ui-select-choices').attr('ui-disable-choice')).toBe('person.inactive');
      });

      it('should not allow disabled options to be selected', function() {
        clickItem(this.el, 'Wladimir');

        expect(getMatchLabel(this.el)).not.toEqual('Wladimir');
      });

      it('should set a disabled class on the option', function() {

        openDropdown(this.el);

        var option = $(this.el).find('.ui-select-choices-row div:contains("Wladimir")');
        var container = option.closest('.ui-select-choices-row');

        expect(container.hasClass('disabled')).toBeTruthy();

      });
    });

    describe('disable on inverse property check', function () {
      beforeEach(function() {
        disablePerson({
          disableAttr : 'active',
          disableBool : false
        });
        this.el = createUiSelect({
          disabled: '!person.active'
        });
      });

      it('should allow the user to define the selected option', function () {
        expect($(this.el).find('.ui-select-choices').attr('ui-disable-choice')).toBe('!person.active');
      });

      it('should not allow disabled options to be selected', function() {
        clickItem(this.el, 'Wladimir');

        expect(getMatchLabel(this.el)).not.toEqual('Wladimir');
      });

      it('should set a disabled class on the option', function() {
        openDropdown(this.el);

        var option = $(this.el).find('.ui-select-choices-row div:contains("Wladimir")');
        var container = option.closest('.ui-select-choices-row');

        expect(container.hasClass('disabled')).toBeTruthy();
      });
    });

    describe('disable on expression', function () {
      beforeEach(function() {
        disablePerson({
          disableAttr : 'status',
          disableBool : 'inactive'
        });
        this.el = createUiSelect({
          disabled: "person.status == 'inactive'"
        });
      });

      it('should allow the user to define the selected option', function () {
        expect($(this.el).find('.ui-select-choices').attr('ui-disable-choice')).toBe("person.status == 'inactive'");
      });

      it('should not allow disabled options to be selected', function() {
        clickItem(this.el, 'Wladimir');

        expect(getMatchLabel(this.el)).not.toEqual('Wladimir');
      });

      it('should set a disabled class on the option', function() {
        openDropdown(this.el);

        var option = $(this.el).find('.ui-select-choices-row div:contains("Wladimir")');
        var container = option.closest('.ui-select-choices-row');

        expect(container.hasClass('disabled')).toBeTruthy();
      });
    });

    afterEach(function() {
      resetScope();
    });
  });

  describe('choices group', function() {
    function getGroupLabel(item) {
      return item.parent('.ui-select-choices-group').find('.ui-select-choices-group-label');
    }
    function createUiSelect() {
      return compileTemplate(
          '<ui-select ng-model="selection.selected"> \
        <ui-select-match placeholder="Pick one...">{{$select.selected.name}}</ui-select-match> \
        <ui-select-choices group-by="\'group\'" repeat="person in people | filter: $select.search"> \
          <div ng-bind-html="person.name | highlight: $select.search"></div> \
          <div ng-bind-html="person.email | highlight: $select.search"></div> \
        </ui-select-choices> \
      </ui-select>'
      );
    }

    it('should create items group', function() {
      var el = createUiSelect();
      expect(el.find('.ui-select-choices-group').length).toBe(3);
    });

    it('should show label before each group', function() {
      var el = createUiSelect();
      expect(el.find('.ui-select-choices-group .ui-select-choices-group-label').map(function() {
        return this.textContent;
      }).toArray()).toEqual(['Foo', 'bar', 'Baz']);
    });

    it('should hide empty groups', function() {
      var el = createUiSelect();
      el.scope().$select.search = 'd';
      scope.$digest();

      expect(el.find('.ui-select-choices-group .ui-select-choices-group-label').map(function() {
        return this.textContent;
      }).toArray()).toEqual(['Foo']);
    });

    it('should change activeItem through groups', function() {
      var el = createUiSelect();
      el.scope().$select.search = 't';
      scope.$digest();
      openDropdown(el);
      var choices = el.find('.ui-select-choices-row');

      expect(choices.eq(0)).toHaveClass('active');
      expect(getGroupLabel(choices.eq(0)).text()).toBe('Foo');

      triggerKeydown(el.find('input'), 40 /*Down*/);
      scope.$digest();
      expect(choices.eq(1)).toHaveClass('active');
      expect(getGroupLabel(choices.eq(1)).text()).toBe('bar');
    });
  });

  describe('choices group by function', function() {
    function createUiSelect() {
      return compileTemplate(
        '<ui-select ng-model="selection.selected"> \
      <ui-select-match placeholder="Pick one...">{{$select.selected.name}}</ui-select-match> \
      <ui-select-choices group-by="getGroupLabel" repeat="person in people | filter: $select.search"> \
        <div ng-bind-html="person.name | highlight: $select.search"></div> \
      </ui-select-choices> \
    </ui-select>'
      );
    }
    it("should extract group value through function", function () {
      var el = createUiSelect();
      expect(el.find('.ui-select-choices-group .ui-select-choices-group-label').map(function() {
        return this.textContent;
      }).toArray()).toEqual(['odd', 'even']);
    });
  });

  it('should throw when no ui-select-choices found', function() {
    expect(function() {
      compileTemplate(
        '<ui-select ng-model="selection.selected"> \
          <ui-select-match></ui-select-match> \
        </ui-select>'
      );
    }).toThrow(new Error('[ui.select:transcluded] Expected 1 .ui-select-choices but got \'0\'.'));
  });

  it('should throw when no repeat attribute is provided to ui-select-choices', function() {
    expect(function() {
      compileTemplate(
        '<ui-select ng-model="selection.selected"> \
          <ui-select-choices></ui-select-choices> \
        </ui-select>'
      );
    }).toThrow(new Error('[ui.select:repeat] Expected \'repeat\' expression.'));
  });

  it('should throw when repeat attribute has incorrect format ', function() {
    expect(function() {
      compileTemplate(
        '<ui-select ng-model="selection.selected"> \
          <ui-select-match></ui-select-match> \
          <ui-select-choices repeat="incorrect format people"></ui-select-choices> \
      </ui-select>'
      );
    }).toThrow(new Error('[ui.select:iexp] Expected expression in form of \'_item_ in _collection_[ track by _id_]\' but got \'incorrect format people\'.'));
  });

  it('should throw when no ui-select-match found', function() {
    expect(function() {
      compileTemplate(
        '<ui-select ng-model="selection.selected"> \
          <ui-select-choices repeat="item in items"></ui-select-choices> \
        </ui-select>'
      );
    }).toThrow(new Error('[ui.select:transcluded] Expected 1 .ui-select-match but got \'0\'.'));
  });

  it('should format the model correctly using alias', function() {
    var el = compileTemplate(
      '<ui-select ng-model="selection.selected"> \
        <ui-select-match placeholder="Pick one...">{{$select.selected.name}}</ui-select-match> \
        <ui-select-choices repeat="person as person in people | filter: $select.search"> \
          <div ng-bind-html="person.name | highlight: $select.search"></div> \
          <div ng-bind-html="person.email | highlight: $select.search"></div> \
        </ui-select-choices> \
      </ui-select>'
    );
    clickItem(el, 'Samantha');
	  expect(scope.selection.selected).toBe(scope.people[5]);
  });

  it('should parse the model correctly using alias', function() {
    var el = compileTemplate(
      '<ui-select ng-model="selection.selected"> \
        <ui-select-match placeholder="Pick one...">{{$select.selected.name}}</ui-select-match> \
        <ui-select-choices repeat="person as person in people | filter: $select.search"> \
          <div ng-bind-html="person.name | highlight: $select.search"></div> \
          <div ng-bind-html="person.email | highlight: $select.search"></div> \
        </ui-select-choices> \
      </ui-select>'
    );
    scope.selection.selected = scope.people[5];
    scope.$digest();
    expect(getMatchLabel(el)).toEqual('Samantha');
  });

  it('should format the model correctly using property of alias', function() {
    var el = compileTemplate(
      '<ui-select ng-model="selection.selected"> \
        <ui-select-match placeholder="Pick one...">{{$select.selected.name}}</ui-select-match> \
        <ui-select-choices repeat="person.name as person in people | filter: $select.search"> \
          <div ng-bind-html="person.name | highlight: $select.search"></div> \
          <div ng-bind-html="person.email | highlight: $select.search"></div> \
        </ui-select-choices> \
      </ui-select>'
    );
    clickItem(el, 'Samantha');
	  expect(scope.selection.selected).toBe('Samantha');
  });

  it('should parse the model correctly using property of alias', function() {
    var el = compileTemplate(
      '<ui-select ng-model="selection.selected"> \
        <ui-select-match placeholder="Pick one...">{{$select.selected.name}}</ui-select-match> \
        <ui-select-choices repeat="person.name as person in people | filter: $select.search"> \
          <div ng-bind-html="person.name | highlight: $select.search"></div> \
          <div ng-bind-html="person.email | highlight: $select.search"></div> \
        </ui-select-choices> \
      </ui-select>'
    );
    scope.selection.selected = 'Samantha';
    scope.$digest();
    expect(getMatchLabel(el)).toEqual('Samantha');
  });

  it('should parse the model correctly using property of alias with async choices data', function() {
    var el = compileTemplate(
      '<ui-select ng-model="selection.selected"> \
        <ui-select-match placeholder="Pick one...">{{$select.selected.name}}</ui-select-match> \
        <ui-select-choices repeat="person.name as person in peopleAsync | filter: $select.search"> \
          <div ng-bind-html="person.name | highlight: $select.search"></div> \
          <div ng-bind-html="person.email | highlight: $select.search"></div> \
        </ui-select-choices> \
      </ui-select>'
    );
    $timeout(function() {
      scope.peopleAsync = scope.people;
    });

    scope.selection.selected = 'Samantha';
    scope.$digest();
    expect(getMatchLabel(el)).toEqual('');

    $timeout.flush(); //After choices populated (async), it should show match correctly
    expect(getMatchLabel(el)).toEqual('Samantha');

  });

  //TODO Is this really something we should expect?
  it('should parse the model correctly using property of alias but passed whole object', function() {
    var el = compileTemplate(
      '<ui-select ng-model="selection.selected"> \
        <ui-select-match placeholder="Pick one...">{{$select.selected.name}}</ui-select-match> \
        <ui-select-choices repeat="person.name as person in people | filter: $select.search"> \
          <div ng-bind-html="person.name | highlight: $select.search"></div> \
          <div ng-bind-html="person.email | highlight: $select.search"></div> \
        </ui-select-choices> \
      </ui-select>'
    );
    scope.selection.selected = scope.people[5];
    scope.$digest();
    expect(getMatchLabel(el)).toEqual('Samantha');
  });

  it('should format the model correctly without alias', function() {
    var el = createUiSelect();
    clickItem(el, 'Samantha');
	  expect(scope.selection.selected).toBe(scope.people[5]);
  });

  it('should parse the model correctly without alias', function() {
    var el = createUiSelect();
    scope.selection.selected = scope.people[5];
    scope.$digest();
    expect(getMatchLabel(el)).toEqual('Samantha');
  });

  it('should display choices correctly with child array', function() {
    var el = compileTemplate(
      '<ui-select ng-model="selection.selected"> \
        <ui-select-match placeholder="Pick one...">{{$select.selected.name}}</ui-select-match> \
        <ui-select-choices repeat="person in someObject.people | filter: $select.search"> \
          <div ng-bind-html="person.name | highlight: $select.search"></div> \
          <div ng-bind-html="person.email | highlight: $select.search"></div> \
        </ui-select-choices> \
      </ui-select>'
    );
    scope.selection.selected = scope.people[5];
    scope.$digest();
    expect(getMatchLabel(el)).toEqual('Samantha');
  });

  it('should format the model correctly using property of alias and when using child array for choices', function() {
    var el = compileTemplate(
      '<ui-select ng-model="selection.selected"> \
        <ui-select-match placeholder="Pick one...">{{$select.selected.name}}</ui-select-match> \
        <ui-select-choices repeat="person.name as person in someObject.people | filter: $select.search"> \
          <div ng-bind-html="person.name | highlight: $select.search"></div> \
          <div ng-bind-html="person.email | highlight: $select.search"></div> \
        </ui-select-choices> \
      </ui-select>'
    );
    clickItem(el, 'Samantha');
    expect(scope.selection.selected).toBe('Samantha');
  });

  it('should invoke select callback on select', function () {

    scope.onSelectFn = function ($item, $model, $label) {
      scope.$item = $item;
      scope.$model = $model;
    };
    var el = compileTemplate(
      '<ui-select on-select="onSelectFn($item, $model)" ng-model="selection.selected"> \
        <ui-select-match placeholder="Pick one...">{{$select.selected.name}}</ui-select-match> \
        <ui-select-choices repeat="person.name as person in people | filter: $select.search"> \
          <div ng-bind-html="person.name | highlight: $select.search"></div> \
          <div ng-bind-html="person.email | highlight: $select.search"></div> \
        </ui-select-choices> \
      </ui-select>'
    );

    expect(scope.$item).toBeFalsy();
    expect(scope.$model).toBeFalsy();

    clickItem(el, 'Samantha');
    $timeout.flush();


    expect(scope.selection.selected).toBe('Samantha');

    expect(scope.$item).toEqual(scope.people[5]);
    expect(scope.$model).toEqual('Samantha');

  });

  it('should invoke hover callback', function(){

    var highlighted;
    scope.onHighlightFn = function ($item) {
      highlighted = $item;
    };

    var el = compileTemplate(
      '<ui-select on-select="onSelectFn($item, $model)" ng-model="selection.selected"> \
        <ui-select-match placeholder="Pick one...">{{$select.selected.name}}</ui-select-match> \
        <ui-select-choices on-highlight="onHighlightFn(person)" repeat="person.name as person in people | filter: $select.search"> \
          <div ng-bind-html="person.name | highlight: $select.search"></div> \
          <div ng-bind-html="person.email | highlight: $select.search"></div> \
        </ui-select-choices> \
      </ui-select>'
    );

    expect(highlighted).toBeFalsy();

    if (!isDropdownOpened(el)){
      openDropdown(el);
    }

    $(el).find('.ui-select-choices-row div:contains("Samantha")').trigger('mouseover');
    scope.$digest();

    expect(highlighted).toBe(scope.people[5]);
  })

  it('should set $item & $model correctly when invoking callback on select and no single prop. binding', function () {

    scope.onSelectFn = function ($item, $model, $label) {
      scope.$item = $item;
      scope.$model = $model;
    };

    var el = compileTemplate(
      '<ui-select on-select="onSelectFn($item, $model)" ng-model="selection.selected"> \
        <ui-select-match placeholder="Pick one...">{{$select.selected.name}}</ui-select-match> \
        <ui-select-choices repeat="person in people | filter: $select.search"> \
          <div ng-bind-html="person.name | highlight: $select.search"></div> \
          <div ng-bind-html="person.email | highlight: $select.search"></div> \
        </ui-select-choices> \
      </ui-select>'
    );

    expect(scope.$item).toBeFalsy();
    expect(scope.$model).toBeFalsy();

    clickItem(el, 'Samantha');
    expect(scope.$item).toEqual(scope.$model);

  });

  it('should invoke remove callback on remove', function () {

    scope.onRemoveFn = function ($item, $model, $label) {
      scope.$item = $item;
      scope.$model = $model;
    };

    var el = compileTemplate(
      '<ui-select multiple on-remove="onRemoveFn($item, $model)" ng-model="selection.selected"> \
        <ui-select-match placeholder="Pick one...">{{$select.selected.name}}</ui-select-match> \
        <ui-select-choices repeat="person.name as person in people | filter: $select.search"> \
          <div ng-bind-html="person.name" | highlight: $select.search"></div> \
          <div ng-bind-html="person.email | highlight: $select.search"></div> \
        </ui-select-choices> \
      </ui-select>'
    );

    expect(scope.$item).toBeFalsy();
    expect(scope.$model).toBeFalsy();

    clickItem(el, 'Samantha');
    clickItem(el, 'Adrian');
    el.find('.ui-select-match-item').first().find('.ui-select-match-close').click();
    $timeout.flush();

    expect(scope.$item).toBe(scope.people[5]);
    expect(scope.$model).toBe('Samantha');

  });

  it('should set $item & $model correctly when invoking callback on remove and no single prop. binding', function () {

    scope.onRemoveFn = function ($item, $model, $label) {
      scope.$item = $item;
      scope.$model = $model;
    };

    var el = compileTemplate(
      '<ui-select multiple on-remove="onRemoveFn($item, $model)" ng-model="selection.selected"> \
        <ui-select-match placeholder="Pick one...">{{$select.selected.name}}</ui-select-match> \
        <ui-select-choices repeat="person in people | filter: $select.search"> \
          <div ng-bind-html="person.name" | highlight: $select.search"></div> \
          <div ng-bind-html="person.email | highlight: $select.search"></div> \
        </ui-select-choices> \
      </ui-select>'
    );

    expect(scope.$item).toBeFalsy();
    expect(scope.$model).toBeFalsy();

    clickItem(el, 'Samantha');
    clickItem(el, 'Adrian');
    el.find('.ui-select-match-item').first().find('.ui-select-match-close').click();
    $timeout.flush();

    expect(scope.$item).toBe(scope.people[5]);
    expect(scope.$model).toBe(scope.$item);
  });

  it('should append/transclude content (with correct scope) that users add at <match> tag', function () {

    var el = compileTemplate(
      '<ui-select ng-model="selection.selected"> \
        <ui-select-match> \
          <span ng-if="$select.selected.name!==\'Wladimir\'">{{$select.selected.name}}</span>\
          <span ng-if="$select.selected.name===\'Wladimir\'">{{$select.selected.name | uppercase}}</span>\
        </ui-select-match> \
        <ui-select-choices repeat="person in people | filter: $select.search"> \
          <div ng-bind-html="person.name | highlight: $select.search"></div> \
        </ui-select-choices> \
      </ui-select>'
    );

    clickItem(el, 'Samantha');
    expect(getMatchLabel(el).trim()).toEqual('Samantha');

    clickItem(el, 'Wladimir');
    expect(getMatchLabel(el).trim()).not.toEqual('Wladimir');
    expect(getMatchLabel(el).trim()).toEqual('WLADIMIR');

  });
  it('should append/transclude content (with correct scope) that users add at <choices> tag', function () {

    var el = compileTemplate(
      '<ui-select ng-model="selection.selected"> \
        <ui-select-match> \
        </ui-select-match> \
        <ui-select-choices repeat="person in people | filter: $select.search"> \
          <div ng-bind-html="person.name | highlight: $select.search"></div> \
          <div ng-if="person.name==\'Wladimir\'"> \
            <span class="only-once">I should appear only once</span>\
          </div> \
        </ui-select-choices> \
      </ui-select>'
    );

    openDropdown(el);
    expect($(el).find('.only-once').length).toEqual(1);


  });

  it('should call refresh function when search text changes', function () {

    var el = compileTemplate(
      '<ui-select ng-model="selection.selected"> \
        <ui-select-match> \
        </ui-select-match> \
        <ui-select-choices repeat="person in people | filter: $select.search" \
          refresh="fetchFromServer($select.search)" refresh-delay="0"> \
          <div ng-bind-html="person.name | highlight: $select.search"></div> \
          <div ng-if="person.name==\'Wladimir\'"> \
            <span class="only-once">I should appear only once</span>\
          </div> \
        </ui-select-choices> \
      </ui-select>'
    );

    scope.fetchFromServer = function(){};

    spyOn(scope, 'fetchFromServer');

    el.scope().$select.search = 'r';
    scope.$digest();
    $timeout.flush();

    expect(scope.fetchFromServer).toHaveBeenCalledWith('r');

  });

  it('should call refresh function when search text changes', function () {

    var el = compileTemplate(
      '<ui-select ng-model="selection.selected"> \
        <ui-select-match> \
        </ui-select-match> \
        <ui-select-choices repeat="person in people | filter: $select.search" \
          refresh="fetchFromServer($select.search)" refresh-delay="0"> \
          <div ng-bind-html="person.name | highlight: $select.search"></div> \
          <div ng-if="person.name==\'Wladimir\'"> \
            <span class="only-once">I should appear only once</span>\
          </div> \
        </ui-select-choices> \
      </ui-select>'
    );

    scope.fetchFromServer = function(){};

    spyOn(scope, 'fetchFromServer');

    el.scope().$select.search = 'r';
    scope.$digest();
    $timeout.flush();

    expect(scope.fetchFromServer).toHaveBeenCalledWith('r');

  });

  it('should format view value correctly when using single property binding and refresh funcion', function () {

    var el = compileTemplate(
      '<ui-select ng-model="selection.selected"> \
        <ui-select-match>{{$select.selected.name}}</ui-select-match> \
        <ui-select-choices repeat="person.name as person in people | filter: $select.search" \
          refresh="fetchFromServer($select.search)" refresh-delay="0"> \
          <div ng-bind-html="person.name | highlight: $select.search"></div> \
          <div ng-if="person.name==\'Wladimir\'"> \
            <span class="only-once">I should appear only once</span>\
          </div> \
        </ui-select-choices> \
      </ui-select>'
    );

    scope.fetchFromServer = function(searching){

      if (searching == 's')
        return scope.people

      if (searching == 'o'){
        scope.people = []; //To simulate cases were previously selected item isnt in the list anymore
      }

    };

    setSearchText(el, 'r')
    clickItem(el, 'Samantha');
    expect(getMatchLabel(el)).toBe('Samantha');

    setSearchText(el, 'o')
    expect(getMatchLabel(el)).toBe('Samantha');

  });

  describe('search-enabled option', function() {

    var el;

    function setupSelectComponent(searchEnabled, theme) {
      el = compileTemplate(
        '<ui-select ng-model="selection.selected" theme="' + theme + '" search-enabled="' + searchEnabled + '"> \
          <ui-select-match placeholder="Pick one...">{{$select.selected.name}}</ui-select-match> \
          <ui-select-choices repeat="person in people | filter: $select.search"> \
            <div ng-bind-html="person.name | highlight: $select.search"></div> \
            <div ng-bind-html="person.email | highlight: $select.search"></div> \
          </ui-select-choices> \
        </ui-select>'
      );
    }

    describe('selectize theme', function() {

      it('should show search input when true', function() {
        setupSelectComponent(true, 'selectize');
        expect($(el).find('.ui-select-search')).not.toHaveClass('ng-hide');
      });

      it('should hide search input when false', function() {
        setupSelectComponent(false, 'selectize');
        expect($(el).find('.ui-select-search')).toHaveClass('ng-hide');
      });

    });

    describe('select2 theme', function() {

      it('should show search input when true', function() {
        setupSelectComponent('true', 'select2');
        expect($(el).find('.select2-search')).not.toHaveClass('ng-hide');
      });

      it('should hide search input when false', function() {
        setupSelectComponent('false', 'select2');
        expect($(el).find('.select2-search')).toHaveClass('ng-hide');
      });

    });

    describe('bootstrap theme', function() {

      it('should show search input when true', function() {
        setupSelectComponent('true', 'bootstrap');
        clickMatch(el);
        expect($(el).find('.ui-select-search')).not.toHaveClass('ng-hide');
      });

      it('should hide search input when false', function() {
        setupSelectComponent('false', 'bootstrap');
        clickMatch(el);
        expect($(el).find('.ui-select-search')).toHaveClass('ng-hide');
      });

    });

  });


  describe('multi selection', function() {

    function createUiSelectMultiple(attrs) {
        var attrsHtml = '';
        if (attrs !== undefined) {
            if (attrs.disabled !== undefined) { attrsHtml += ' ng-disabled="' + attrs.disabled + '"'; }
            if (attrs.required !== undefined) { attrsHtml += ' ng-required="' + attrs.required + '"'; }
            if (attrs.tabindex !== undefined) { attrsHtml += ' tabindex="' + attrs.tabindex + '"'; }
            if (attrs.closeOnSelect !== undefined) { attrsHtml += ' close-on-select="' + attrs.closeOnSelect + '"'; }
        }

        return compileTemplate(
            '<ui-select multiple ng-model="selection.selectedMultiple"' + attrsHtml + ' theme="bootstrap" style="width: 800px;"> \
                <ui-select-match placeholder="Pick one...">{{$item.name}} &lt;{{$item.email}}&gt;</ui-select-match> \
                <ui-select-choices repeat="person in people | filter: $select.search"> \
                  <div ng-bind-html="person.name | highlight: $select.search"></div> \
                  <div ng-bind-html="person.email | highlight: $select.search"></div> \
                </ui-select-choices> \
            </ui-select>'
        );
    }

    it('should render initial state', function() {
        var el = createUiSelectMultiple();
        expect(el).toHaveClass('ui-select-multiple');
        expect(el.scope().$select.selected.length).toBe(0);
        expect(el.find('.ui-select-match-item').length).toBe(0);
    });

    it('should set model as an empty array if ngModel isnt defined after an item is selected', function () {

      // scope.selection.selectedMultiple = [];
      var el = createUiSelectMultiple();
      expect(scope.selection.selectedMultiple instanceof Array).toBe(false);
      clickItem(el, 'Samantha');
      expect(scope.selection.selectedMultiple instanceof Array).toBe(true);
    });

    it('should render initial selected items', function() {
        scope.selection.selectedMultiple = [scope.people[4], scope.people[5]]; //Wladimir & Samantha
        var el = createUiSelectMultiple();
        expect(el.scope().$select.selected.length).toBe(2);
        expect(el.find('.ui-select-match-item').length).toBe(2);
    });

    it('should remove item by pressing X icon', function() {
        scope.selection.selectedMultiple = [scope.people[4], scope.people[5]]; //Wladimir & Samantha
        var el = createUiSelectMultiple();
        expect(el.scope().$select.selected.length).toBe(2);
        el.find('.ui-select-match-item').first().find('.ui-select-match-close').click();
        expect(el.scope().$select.selected.length).toBe(1);
        // $timeout.flush();
    });

    it('should pass tabindex to searchInput', function() {
      var el = createUiSelectMultiple({tabindex: 5});
      var searchInput = el.find('.ui-select-search');

      expect(searchInput.attr('tabindex')).toEqual('5');
      expect($(el).attr('tabindex')).toEqual(undefined);
    });

    it('should pass tabindex to searchInput when tabindex is an expression', function() {
      scope.tabValue = 22;
      var el = createUiSelectMultiple({tabindex: '{{tabValue + 10}}'});
      var searchInput = el.find('.ui-select-search');

      expect(searchInput.attr('tabindex')).toEqual('32');
      expect($(el).attr('tabindex')).toEqual(undefined);
    });

    it('should not give searchInput a tabindex when ui-select does not have one', function() {
      var el = createUiSelectMultiple();
      var searchInput = el.find('.ui-select-search');

      expect(searchInput.attr('tabindex')).toEqual(undefined);
      expect($(el).attr('tabindex')).toEqual(undefined);
    });

    it('should update size of search input after removing an item', function() {
        scope.selection.selectedMultiple = [scope.people[4], scope.people[5]]; //Wladimir & Samantha
        var el = createUiSelectMultiple();

        spyOn(el.scope().$select, 'sizeSearchInput');

        var searchInput = el.find('.ui-select-search');
        var oldWidth = searchInput.css('width');

        el.find('.ui-select-match-item').first().find('.ui-select-match-close').click();
        expect(el.scope().$select.sizeSearchInput).toHaveBeenCalled();

    });

    it('should move to last match when pressing BACKSPACE key from search', function() {

        var el = createUiSelectMultiple();
        var searchInput = el.find('.ui-select-search');

        expect(isDropdownOpened(el)).toEqual(false);
        triggerKeydown(searchInput, Key.Backspace);
        expect(isDropdownOpened(el)).toEqual(false);
        expect(el.scope().$select.activeMatchIndex).toBe(el.scope().$select.selected.length - 1);

    });

    it('should remove hightlighted match when pressing BACKSPACE key from search and decrease activeMatchIndex', function() {

        scope.selection.selectedMultiple = [scope.people[4], scope.people[5], scope.people[6]]; //Wladimir, Samantha & Nicole
        var el = createUiSelectMultiple();
        var searchInput = el.find('.ui-select-search');

        expect(isDropdownOpened(el)).toEqual(false);
        triggerKeydown(searchInput, Key.Left);
        triggerKeydown(searchInput, Key.Left);
        triggerKeydown(searchInput, Key.Backspace);
        expect(el.scope().$select.selected).toEqual([scope.people[4], scope.people[6]]); //Wladimir & Nicole

        expect(el.scope().$select.activeMatchIndex).toBe(0);

    });

    it('should remove hightlighted match when pressing DELETE key from search and keep same activeMatchIndex', function() {

        scope.selection.selectedMultiple = [scope.people[4], scope.people[5], scope.people[6]]; //Wladimir, Samantha & Nicole
        var el = createUiSelectMultiple();
        var searchInput = el.find('.ui-select-search');

        expect(isDropdownOpened(el)).toEqual(false);
        triggerKeydown(searchInput, Key.Left);
        triggerKeydown(searchInput, Key.Left);
        triggerKeydown(searchInput, Key.Delete);
        expect(el.scope().$select.selected).toEqual([scope.people[4], scope.people[6]]); //Wladimir & Nicole

        expect(el.scope().$select.activeMatchIndex).toBe(1);

    });

    it('should move to last match when pressing LEFT key from search', function() {

        var el = createUiSelectMultiple();
        var searchInput = el.find('.ui-select-search');

        expect(isDropdownOpened(el)).toEqual(false);
        triggerKeydown(searchInput, Key.Left);
        expect(isDropdownOpened(el)).toEqual(false);
        expect(el.scope().$select.activeMatchIndex).toBe(el.scope().$select.selected.length - 1);

    });

    it('should move between matches when pressing LEFT key from search', function() {

        scope.selection.selectedMultiple = [scope.people[4], scope.people[5], scope.people[6]]; //Wladimir, Samantha & Nicole
        var el = createUiSelectMultiple();
        var searchInput = el.find('.ui-select-search');

        expect(isDropdownOpened(el)).toEqual(false);
        triggerKeydown(searchInput, Key.Left)
        triggerKeydown(searchInput, Key.Left)
        expect(isDropdownOpened(el)).toEqual(false);
        expect(el.scope().$select.activeMatchIndex).toBe(el.scope().$select.selected.length - 2);
        triggerKeydown(searchInput, Key.Left)
        triggerKeydown(searchInput, Key.Left)
        triggerKeydown(searchInput, Key.Left)
        expect(el.scope().$select.activeMatchIndex).toBe(0);

    });

    it('should decrease $select.activeMatchIndex when pressing LEFT key', function() {

        scope.selection.selectedMultiple = [scope.people[4], scope.people[5], scope.people[6]]; //Wladimir, Samantha & Nicole
        var el = createUiSelectMultiple();
        var searchInput = el.find('.ui-select-search');

        el.scope().$select.activeMatchIndex = 3
        triggerKeydown(searchInput, Key.Left)
        triggerKeydown(searchInput, Key.Left)
        expect(el.scope().$select.activeMatchIndex).toBe(1);

    });

    it('should increase $select.activeMatchIndex when pressing RIGHT key', function() {

        scope.selection.selectedMultiple = [scope.people[4], scope.people[5], scope.people[6]]; //Wladimir, Samantha & Nicole
        var el = createUiSelectMultiple();
        var searchInput = el.find('.ui-select-search');

        el.scope().$select.activeMatchIndex = 0
        triggerKeydown(searchInput, Key.Right)
        triggerKeydown(searchInput, Key.Right)
        expect(el.scope().$select.activeMatchIndex).toBe(2);

    });

    it('should open dropdown when pressing DOWN key', function() {

        scope.selection.selectedMultiple = [scope.people[4], scope.people[5]]; //Wladimir & Samantha
        var el = createUiSelectMultiple();
        var searchInput = el.find('.ui-select-search');

        expect(isDropdownOpened(el)).toEqual(false);
        triggerKeydown(searchInput, Key.Down)
        expect(isDropdownOpened(el)).toEqual(true);

    });

    it('should search/open dropdown when writing to search input', function() {

        scope.selection.selectedMultiple = [scope.people[5]]; //Wladimir & Samantha
        var el = createUiSelectMultiple();
        var searchInput = el.find('.ui-select-search');

        el.scope().$select.search = 'r';
        scope.$digest();
        expect(isDropdownOpened(el)).toEqual(true);

    });

    it('should add selected match to selection array', function() {

        scope.selection.selectedMultiple = [scope.people[5]]; //Samantha
        var el = createUiSelectMultiple();
        var searchInput = el.find('.ui-select-search');

        clickItem(el, 'Wladimir');
        expect(scope.selection.selectedMultiple).toEqual([scope.people[5], scope.people[4]]); //Samantha & Wladimir

    });

    it('should close dropdown after selecting', function() {

        scope.selection.selectedMultiple = [scope.people[5]]; //Samantha
        var el = createUiSelectMultiple();
        var searchInput = el.find('.ui-select-search');

        expect(isDropdownOpened(el)).toEqual(false);
        triggerKeydown(searchInput, Key.Down)
        expect(isDropdownOpened(el)).toEqual(true);

        clickItem(el, 'Wladimir');

        expect(isDropdownOpened(el)).toEqual(false);

    });

    it('should not close dropdown after selecting if closeOnSelect=false', function() {

        scope.selection.selectedMultiple = [scope.people[5]]; //Samantha
        var el = createUiSelectMultiple({closeOnSelect: false});
        var searchInput = el.find('.ui-select-search');

        expect(isDropdownOpened(el)).toEqual(false);
        triggerKeydown(searchInput, Key.Down)
        expect(isDropdownOpened(el)).toEqual(true);

        clickItem(el, 'Wladimir');

        expect(isDropdownOpened(el)).toEqual(true);

    });

    it('should closes dropdown when pressing ESC key from search input', function() {

        scope.selection.selectedMultiple = [scope.people[4], scope.people[5], scope.people[6]]; //Wladimir, Samantha & Nicole
        var el = createUiSelectMultiple();
        var searchInput = el.find('.ui-select-search');

        expect(isDropdownOpened(el)).toEqual(false);
        triggerKeydown(searchInput, Key.Down)
        expect(isDropdownOpened(el)).toEqual(true);
        triggerKeydown(searchInput, Key.Escape)
        expect(isDropdownOpened(el)).toEqual(false);

    });

    it('should select highlighted match when pressing ENTER key from dropdown', function() {

        scope.selection.selectedMultiple = [scope.people[5]]; //Samantha
        var el = createUiSelectMultiple();
        var searchInput = el.find('.ui-select-search');

        triggerKeydown(searchInput, Key.Down)
        triggerKeydown(searchInput, Key.Enter)
        expect(scope.selection.selectedMultiple.length).toEqual(2);

    });

    it('should increase $select.activeIndex when pressing DOWN key from dropdown', function() {

        var el = createUiSelectMultiple();
        var searchInput = el.find('.ui-select-search');

        triggerKeydown(searchInput, Key.Down); //Open dropdown

        el.scope().$select.activeIndex = 0
        triggerKeydown(searchInput, Key.Down)
        triggerKeydown(searchInput, Key.Down)
        expect(el.scope().$select.activeIndex).toBe(2);

    });

    it('should decrease $select.activeIndex when pressing UP key from dropdown', function() {

        var el = createUiSelectMultiple();
        var searchInput = el.find('.ui-select-search');

        triggerKeydown(searchInput, Key.Down); //Open dropdown

        el.scope().$select.activeIndex = 5
        triggerKeydown(searchInput, Key.Up)
        triggerKeydown(searchInput, Key.Up)
        expect(el.scope().$select.activeIndex).toBe(3);

    });

    it('should render initial selected items', function() {
        scope.selection.selectedMultiple = [scope.people[4], scope.people[5]]; //Wladimir & Samantha
        var el = createUiSelectMultiple();
        expect(el.scope().$select.selected.length).toBe(2);
        expect(el.find('.ui-select-match-item').length).toBe(2);
    });

    it('should parse the items correctly using single property binding', function() {

      scope.selection.selectedMultiple = ['wladimir@email.com', 'samantha@email.com'];

      var el = compileTemplate(
          '<ui-select multiple ng-model="selection.selectedMultiple" theme="bootstrap" style="width: 800px;"> \
              <ui-select-match placeholder="Pick one...">{{$item.name}} &lt;{{$item.email}}&gt;</ui-select-match> \
              <ui-select-choices repeat="person.email as person in people | filter: $select.search"> \
                <div ng-bind-html="person.name | highlight: $select.search"></div> \
                <div ng-bind-html="person.email | highlight: $select.search"></div> \
              </ui-select-choices> \
          </ui-select>'
      );

      expect(el.scope().$select.selected).toEqual([scope.people[4], scope.people[5]]);

    });

    it('should add selected match to selection array using single property binding', function() {

      scope.selection.selectedMultiple = ['wladimir@email.com', 'samantha@email.com'];

      var el = compileTemplate(
          '<ui-select multiple ng-model="selection.selectedMultiple" theme="bootstrap" style="width: 800px;"> \
              <ui-select-match placeholder="Pick one...">{{$item.name}} &lt;{{$item.email}}&gt;</ui-select-match> \
              <ui-select-choices repeat="person.email as person in people | filter: $select.search"> \
                <div ng-bind-html="person.name | highlight: $select.search"></div> \
                <div ng-bind-html="person.email | highlight: $select.search"></div> \
              </ui-select-choices> \
          </ui-select>'
      );

      var searchInput = el.find('.ui-select-search');

      clickItem(el, 'Natasha');

      expect(el.scope().$select.selected).toEqual([scope.people[4], scope.people[5], scope.people[7]]);
      scope.selection.selectedMultiple = ['wladimir@email.com', 'samantha@email.com', 'natasha@email.com'];

    });

    it('should format view value correctly when using single property binding and refresh funcion', function () {

      scope.selection.selectedMultiple = ['wladimir@email.com', 'samantha@email.com'];

      var el = compileTemplate(
          '<ui-select multiple ng-model="selection.selectedMultiple" theme="bootstrap" style="width: 800px;"> \
              <ui-select-match placeholder="Pick one...">{{$item.name}} &lt;{{$item.email}}&gt;</ui-select-match> \
              <ui-select-choices repeat="person.email as person in people | filter: $select.search" \
                refresh="fetchFromServer($select.search)" refresh-delay="0"> \
                <div ng-bind-html="person.name | highlight: $select.search"></div> \
                <div ng-bind-html="person.email | highlight: $select.search"></div> \
              </ui-select-choices> \
          </ui-select>'
      );

      var searchInput = el.find('.ui-select-search');

      scope.fetchFromServer = function(searching){

        if (searching == 'n')
          return scope.people

        if (searching == 'o'){
          scope.people = []; //To simulate cases were previously selected item isnt in the list anymore
        }

      };

      setSearchText(el, 'n')
      clickItem(el, 'Nicole');

      expect(el.find('.ui-select-match-item [uis-transclude-append]:not(.ng-hide)').text())
        .toBe("Wladimir <wladimir@email.com>Samantha <samantha@email.com>Nicole <nicole@email.com>");

      setSearchText(el, 'o')

      expect(el.find('.ui-select-match-item [uis-transclude-append]:not(.ng-hide)').text())
        .toBe("Wladimir <wladimir@email.com>Samantha <samantha@email.com>Nicole <nicole@email.com>");

    });

    it('should watch changes for $select.selected and update formatted value correctly', function () {

      scope.selection.selectedMultiple = ['wladimir@email.com', 'samantha@email.com'];

      var el = compileTemplate(
          '<ui-select multiple ng-model="selection.selectedMultiple" theme="bootstrap" style="width: 800px;"> \
              <ui-select-match placeholder="Pick one...">{{$item.name}} &lt;{{$item.email}}&gt;</ui-select-match> \
              <ui-select-choices repeat="person.email as person in people | filter: $select.search"> \
                <div ng-bind-html="person.name | highlight: $select.search"></div> \
                <div ng-bind-html="person.email | highlight: $select.search"></div> \
              </ui-select-choices> \
          </ui-select> \
          '
      );

      var el2 = compileTemplate('<span class="resultDiv" ng-bind="selection.selectedMultiple"></span>');

      expect(el.find('.ui-select-match-item [uis-transclude-append]:not(.ng-hide)').text())
        .toBe("Wladimir <wladimir@email.com>Samantha <samantha@email.com>");

      clickItem(el, 'Nicole');

      expect(el.find('.ui-select-match-item [uis-transclude-append]:not(.ng-hide)').text())
        .toBe("Wladimir <wladimir@email.com>Samantha <samantha@email.com>Nicole <nicole@email.com>");

      expect(scope.selection.selectedMultiple.length).toBe(3);

    });

    it('should change viewvalue only once when updating modelvalue', function () {

      scope.selection.selectedMultiple = ['wladimir@email.com', 'samantha@email.com'];

      var el = compileTemplate(
          '<ui-select ng-change="onlyOnce()" multiple ng-model="selection.selectedMultiple" theme="bootstrap" style="width: 800px;"> \
              <ui-select-match placeholder="Pick one...">{{$item.name}} &lt;{{$item.email}}&gt;</ui-select-match> \
              <ui-select-choices repeat="person.email as person in people | filter: $select.search"> \
                <div ng-bind-html="person.name | highlight: $select.search"></div> \
                <div ng-bind-html="person.email | highlight: $select.search"></div> \
              </ui-select-choices> \
          </ui-select> \
          '
      );

      scope.counter = 0;
      scope.onlyOnce = function(){
        scope.counter++;
      }

      clickItem(el, 'Nicole');

      expect(scope.counter).toBe(1);

    });


    it('should run $formatters when changing model directly', function () {

      scope.selection.selectedMultiple = ['wladimir@email.com', 'samantha@email.com'];

      var el = compileTemplate(
          '<ui-select multiple ng-model="selection.selectedMultiple" theme="bootstrap" style="width: 800px;"> \
              <ui-select-match placeholder="Pick one...">{{$item.name}} &lt;{{$item.email}}&gt;</ui-select-match> \
              <ui-select-choices repeat="person.email as person in people | filter: $select.search"> \
                <div ng-bind-html="person.name | highlight: $select.search"></div> \
                <div ng-bind-html="person.email | highlight: $select.search"></div> \
              </ui-select-choices> \
          </ui-select> \
          '
      );

      // var el2 = compileTemplate('<span class="resultDiv" ng-bind="selection.selectedMultiple"></span>');

      scope.selection.selectedMultiple.push("nicole@email.com");

      scope.$digest();
      scope.$digest(); //2nd $digest needed when using angular 1.3.0-rc.1+, might be related with the fact that the value is an array

      expect(el.find('.ui-select-match-item [uis-transclude-append]:not(.ng-hide)').text())
         .toBe("Wladimir <wladimir@email.com>Samantha <samantha@email.com>Nicole <nicole@email.com>");

    });

    it('should support multiple="multiple" attribute', function() {

      var el = compileTemplate(
          '<ui-select multiple="multiple" ng-model="selection.selectedMultiple" theme="bootstrap" style="width: 800px;"> \
              <ui-select-match placeholder="Pick one...">{{$item.name}} &lt;{{$item.email}}&gt;</ui-select-match> \
              <ui-select-choices repeat="person.email as person in people | filter: $select.search"> \
                <div ng-bind-html="person.name | highlight: $select.search"></div> \
                <div ng-bind-html="person.email | highlight: $select.search"></div> \
              </ui-select-choices> \
          </ui-select> \
          '
      );

      expect(el.scope().$select.multiple).toBe(true);
    });
  });

  describe('default configuration via uiSelectConfig', function() {

    describe('searchEnabled option', function() {

      function setupWithoutAttr(){
        return compileTemplate(
          '<ui-select ng-model="selection.selected"> \
            <ui-select-match placeholder="Pick one...">{{$select.selected.name}}</ui-select-match> \
            <ui-select-choices repeat="person in people | filter: $select.search"> \
              <div ng-bind-html="person.name | highlight: $select.search"></div> \
              <div ng-bind-html="person.email | highlight: $select.search"></div> \
            </ui-select-choices> \
          </ui-select>'
        );
      }

      function setupWithAttr(searchEnabled){
        return compileTemplate(
          '<ui-select ng-model="selection.selected" search-enabled="'+searchEnabled+'"> \
            <ui-select-match placeholder="Pick one...">{{$select.selected.name}}</ui-select-match> \
            <ui-select-choices repeat="person in people | filter: $select.search"> \
              <div ng-bind-html="person.name | highlight: $select.search"></div> \
              <div ng-bind-html="person.email | highlight: $select.search"></div> \
            </ui-select-choices> \
          </ui-select>'
        );
      }

      it('should be true by default', function(){
        var el = setupWithoutAttr();
        expect(el.scope().$select.searchEnabled).toBe(true);
      });

      it('should disable search if default set to false', function(){
        var uiSelectConfig = $injector.get('uiSelectConfig');
        uiSelectConfig.searchEnabled = false;

        var el = setupWithoutAttr();
        expect(el.scope().$select.searchEnabled).not.toBe(true);
      });

      it('should be overridden by inline option search-enabled=true', function(){
        var uiSelectConfig = $injector.get('uiSelectConfig');
        uiSelectConfig.searchEnabled = false;

        var el = setupWithAttr(true);
        expect(el.scope().$select.searchEnabled).toBe(true);
      });

      it('should be overridden by inline option search-enabled=false', function(){
        var uiSelectConfig = $injector.get('uiSelectConfig');
        uiSelectConfig.searchEnabled = true;

        var el = setupWithAttr(false);
        expect(el.scope().$select.searchEnabled).not.toBe(true);
      });
    });

  });

  describe('accessibility', function() {
    it('should have baseTitle in scope', function() {
      expect(createUiSelect().scope().$select.baseTitle).toBe('Select box');
      expect(createUiSelect().scope().$select.focusserTitle).toBe('Select box focus');
      expect(createUiSelect({ title: 'Choose a person' }).scope().$select.baseTitle).toBe('Choose a person');
      expect(createUiSelect({ title: 'Choose a person' }).scope().$select.focusserTitle).toBe('Choose a person focus');
    });

    it('should have aria-label on all input and button elements', function() {
      checkTheme();
      checkTheme('select2');
      checkTheme('selectize');
      checkTheme('bootstrap');

      function checkTheme(theme) {
        var el = createUiSelect({ theme: theme});
        checkElements(el.find('input'));
        checkElements(el.find('button'));

        function checkElements(els) {
          for (var i = 0; i < els.length; i++) {
            expect(els[i].attributes['aria-label']).toBeTruthy();
          };
        }
      }
    });
  });
});
