<commit_before>'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: '<EMAIL>',      group: 'Foo', age: 12 },
      { name: 'Amalie',    email: '<EMAIL>',    group: 'Foo', age: 12 },
      { name: 'Estefanía', email: '<EMAIL>', group: 'Foo', age: 21 },
      { name: 'Adrian',    email: '<EMAIL>',    group: 'Foo', age: 21 },
      { name: 'Wladimir',  email: '<EMAIL>',  group: 'Foo', age: 30 },
      { name: 'Samantha',  email: '<EMAIL>',  group: 'bar', age: 30 },
      { name: 'Nicole',    email: '<EMAIL>',    group: 'bar', age: 43 },
      { name: 'Natasha',   email: '<EMAIL>',   group: 'Baz', age: 54 }
    ];

    scope.someObject = {};
    scope.someObject.people = [
      { name: 'Adam',      email: '<EMAIL>',      group: 'Foo', age: 12 },
      { name: 'Amalie',    email: '<EMAIL>',    group: 'Foo', age: 12 },
      { name: 'Estefanía', email: '<EMAIL>', group: 'Foo', age: 21 },
      { name: 'Adrian',    email: '<EMAIL>',    group: 'Foo', age: 21 },
      { name: 'Wladimir',  email: '<EMAIL>',  group: 'Foo', age: 30 },
      { name: 'Samantha',  email: '<EMAIL>',  group: 'bar', age: 30 },
      { name: 'Nicole',    email: '<EMAIL>',    group: 'bar', age: 43 },
      { name: 'Natasha',   email: '<EMAIL>',   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 + '"'; }
    }

    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 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.<EMAIL>',
        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: '<EMAIL>',
      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');
    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();

    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();

    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');<commit_msg>Fixed tests of select/remove bug fix.<commit_after>'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: '<EMAIL>',      group: 'Foo', age: 12 },
      { name: 'Amalie',    email: '<EMAIL>',    group: 'Foo', age: 12 },
      { name: 'Estefanía', email: '<EMAIL>', group: 'Foo', age: 21 },
      { name: 'Adrian',    email: '<EMAIL>',    group: 'Foo', age: 21 },
      { name: 'Wladimir',  email: '<EMAIL>',  group: 'Foo', age: 30 },
      { name: 'Samantha',  email: '<EMAIL>',  group: 'bar', age: 30 },
      { name: 'Nicole',    email: '<EMAIL>',    group: 'bar', age: 43 },
      { name: 'Natasha',   email: '<EMAIL>',   group: 'Baz', age: 54 }
    ];

    scope.someObject = {};
    scope.someObject.people = [
      { name: 'Adam',      email: '<EMAIL>',      group: 'Foo', age: 12 },
      { name: 'Amalie',    email: '<EMAIL>',    group: 'Foo', age: 12 },
      { name: 'Estefanía', email: '<EMAIL>', group: 'Foo', age: 21 },
      { name: 'Adrian',    email: '<EMAIL>',    group: 'Foo', age: 21 },
      { name: 'Wladimir',  email: '<EMAIL>',  group: 'Foo', age: 30 },
      { name: 'Samantha',  email: '<EMAIL>',  group: 'bar', age: 30 },
      { name: 'Nicole',    email: '<EMAIL>',    group: 'bar', age: 43 },
      { name: 'Natasha',   email: '<EMAIL>',   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 + '"'; }
    }

    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 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.<EMAIL>',
        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: '<EMAIL>',
      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');
{'fraction_non_alphanumeric': 0.18213798661046368, 'fraction_numerical': 0.00424621869575998, 'mean_word_length': 2.9549521941652364, 'pattern_counts': {'":': 0, '<': 453, '<?xml version=': 0, '>': 459, 'https://': 4, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7419441', 'n_tokens_mistral': 20141, 'n_tokens_neox': 19592, 'n_words': 3875}