'use strict'; describe('select', function() { var compile = null, element = null, scope = null, $formFactory = null; beforeEach(function() { scope = null; element = null; compile = function(html, parent) { if (parent) { parent.html(html); element = parent.children(); } else { element = jqLite(html); } scope = angular.compile(element)(); scope.$apply(); $formFactory = scope.$service('$formFactory'); return scope; }; }); afterEach(function() { dealoc(element); }); describe('select-one', function() { it('should compile children of a select without a name, but not create a model for it', function() { compile(''); scope.a = 'foo'; scope.b = 'bar'; scope.$digest(); expect(scope.$element.text()).toBe('foobarC'); }); it('should require', function() { compile(''); scope.log = ''; scope.selection = 'c'; scope.$digest(); expect($formFactory.forElement(element).select.$error.REQUIRED).toEqual(undefined); expect(element).toBeValid(); expect(element).toBePristine(); scope.selection = ''; scope.$digest(); expect($formFactory.forElement(element).select.$error.REQUIRED).toEqual(true); expect(element).toBeInvalid(); expect(element).toBePristine(); expect(scope.log).toEqual(''); element[0].value = 'c'; browserTrigger(element, 'change'); expect(element).toBeValid(); expect(element).toBeDirty(); expect(scope.log).toEqual('change;'); }); it('should not be invalid if no require', function() { compile(''); expect(element).toBeValid(); expect(element).toBePristine(); }); }); describe('select-multiple', function() { it('should support type="select-multiple"', function() { compile(''); scope.selection = ['A']; scope.$digest(); expect(element[0].childNodes[0].selected).toEqual(true); }); it('should require', function() { compile(''); scope.selection = []; scope.$digest(); expect($formFactory.forElement(element).select.$error.REQUIRED).toEqual(true); expect(element).toBeInvalid(); expect(element).toBePristine(); scope.selection = ['A']; scope.$digest(); expect(element).toBeValid(); expect(element).toBePristine(); element[0].value = 'B'; browserTrigger(element, 'change'); expect(element).toBeValid(); expect(element).toBeDirty(); }); }); describe('ng:options', function() { var select, scope; function createSelect(attrs, blank, unknown){ var html = 'blank' : '') + (unknown ? '' : '') + ''; select = jqLite(html); scope = compile(select); } function createSingleSelect(blank, unknown){ createSelect({ 'ng:model':'selected', 'ng:options':'value.name for value in values' }, blank, unknown); } function createMultiSelect(blank, unknown){ createSelect({ 'ng:model':'selected', 'multiple':true, 'ng:options':'value.name for value in values' }, blank, unknown); } afterEach(function() { dealoc(select); dealoc(scope); }); it('should throw when not formated "? for ? in ?"', function() { expect(function() { compile(''); }).toThrow("Expected ng:options in form of '_select_ (as _label_)? for (_key_,)?_value_ in" + " _collection_' but got 'i dont parse'."); }); it('should render a list', function() { createSingleSelect(); scope.values = [{name:'A'}, {name:'B'}, {name:'C'}]; scope.selected = scope.values[0]; scope.$digest(); var options = select.find('option'); expect(options.length).toEqual(3); expect(sortedHtml(options[0])).toEqual(''); expect(sortedHtml(options[1])).toEqual(''); expect(sortedHtml(options[2])).toEqual(''); }); it('should render an object', function() { createSelect({ 'ng:model':'selected', 'ng:options': 'value as key for (key, value) in object' }); scope.object = {'red':'FF0000', 'green':'00FF00', 'blue':'0000FF'}; scope.selected = scope.object.red; scope.$digest(); var options = select.find('option'); expect(options.length).toEqual(3); expect(sortedHtml(options[0])).toEqual(''); expect(sortedHtml(options[1])).toEqual(''); expect(sortedHtml(options[2])).toEqual(''); expect(options[2].selected).toEqual(true); scope.object.azur = '8888FF'; scope.$digest(); options = select.find('option'); expect(options[3].selected).toEqual(true); }); it('should grow list', function() { createSingleSelect(); scope.values = []; scope.$digest(); expect(select.find('option').length).toEqual(1); // because we add special empty option expect(sortedHtml(select.find('option')[0])).toEqual(''); scope.values.push({name:'A'}); scope.selected = scope.values[0]; scope.$digest(); expect(select.find('option').length).toEqual(1); expect(sortedHtml(select.find('option')[0])).toEqual(''); scope.values.push({name:'B'}); scope.$digest(); expect(select.find('option').length).toEqual(2); expect(sortedHtml(select.find('option')[0])).toEqual(''); expect(sortedHtml(select.find('option')[1])).toEqual(''); }); it('should shrink list', function() { createSingleSelect(); scope.values = [{name:'A'}, {name:'B'}, {name:'C'}]; scope.selected = scope.values[0]; scope.$digest(); expect(select.find('option').length).toEqual(3); scope.values.pop(); scope.$digest(); expect(select.find('option').length).toEqual(2); expect(sortedHtml(select.find('option')[0])).toEqual(''); expect(sortedHtml(select.find('option')[1])).toEqual(''); scope.values.pop(); scope.$digest(); expect(select.find('option').length).toEqual(1); expect(sortedHtml(select.find('option')[0])).toEqual(''); scope.values.pop(); scope.selected = null; scope.$digest(); expect(select.find('option').length).toEqual(1); // we add back the special empty option }); it('should shrink and then grow list', function() { createSingleSelect(); scope.values = [{name:'A'}, {name:'B'}, {name:'C'}]; scope.selected = scope.values[0]; scope.$digest(); expect(select.find('option').length).toEqual(3); scope.values = [{name:'1'}, {name:'2'}]; scope.selected = scope.values[0]; scope.$digest(); expect(select.find('option').length).toEqual(2); scope.values = [{name:'A'}, {name:'B'}, {name:'C'}]; scope.selected = scope.values[0]; scope.$digest(); expect(select.find('option').length).toEqual(3); }); it('should update list', function() { createSingleSelect(); scope.values = [{name:'A'}, {name:'B'}, {name:'C'}]; scope.selected = scope.values[0]; scope.$digest(); scope.values = [{name:'B'}, {name:'C'}, {name:'D'}]; scope.selected = scope.values[0]; scope.$digest(); var options = select.find('option'); expect(options.length).toEqual(3); expect(sortedHtml(options[0])).toEqual(''); expect(sortedHtml(options[1])).toEqual(''); expect(sortedHtml(options[2])).toEqual(''); }); it('should preserve existing options', function() { createSingleSelect(true); scope.values = []; scope.$digest(); expect(select.find('option').length).toEqual(1); scope.values = [{name:'A'}]; scope.selected = scope.values[0]; scope.$digest(); expect(select.find('option').length).toEqual(2); expect(jqLite(select.find('option')[0]).text()).toEqual('blank'); expect(jqLite(select.find('option')[1]).text()).toEqual('A'); scope.values = []; scope.selected = null; scope.$digest(); expect(select.find('option').length).toEqual(1); expect(jqLite(select.find('option')[0]).text()).toEqual('blank'); }); describe('binding', function() { it('should bind to scope value', function() { createSingleSelect(); scope.values = [{name:'A'}, {name:'B'}]; scope.selected = scope.values[0]; scope.$digest(); expect(select.val()).toEqual('0'); scope.selected = scope.values[1]; scope.$digest(); expect(select.val()).toEqual('1'); }); it('should bind to scope value and group', function() { createSelect({ 'ng:model':'selected', 'ng:options':'item.name group by item.group for item in values' }); scope.values = [{name:'A'}, {name:'B', group:'first'}, {name:'C', group:'second'}, {name:'D', group:'first'}, {name:'E', group:'second'}]; scope.selected = scope.values[3]; scope.$digest(); expect(select.val()).toEqual('3'); var first = jqLite(select.find('optgroup')[0]); var b = jqLite(first.find('option')[0]); var d = jqLite(first.find('option')[1]); expect(first.attr('label')).toEqual('first'); expect(b.text()).toEqual('B'); expect(d.text()).toEqual('D'); var second = jqLite(select.find('optgroup')[1]); var c = jqLite(second.find('option')[0]); var e = jqLite(second.find('option')[1]); expect(second.attr('label')).toEqual('second'); expect(c.text()).toEqual('C'); expect(e.text()).toEqual('E'); scope.selected = scope.values[0]; scope.$digest(); expect(select.val()).toEqual('0'); }); it('should bind to scope value through experession', function() { createSelect({'ng:model':'selected', 'ng:options':'item.id as item.name for item in values'}); scope.values = [{id:10, name:'A'}, {id:20, name:'B'}]; scope.selected = scope.values[0].id; scope.$digest(); expect(select.val()).toEqual('0'); scope.selected = scope.values[1].id; scope.$digest(); expect(select.val()).toEqual('1'); }); it('should bind to object key', function() { createSelect({ 'ng:model':'selected', 'ng:options':'key as value for (key, value) in object' }); scope.object = {'red':'FF0000', 'green':'00FF00', 'blue':'0000FF'}; scope.selected = 'green'; scope.$digest(); expect(select.val()).toEqual('green'); scope.selected = 'blue'; scope.$digest(); expect(select.val()).toEqual('blue'); }); it('should bind to object value', function() { createSelect({ 'ng:model':'selected', 'ng:options':'value as key for (key, value) in object' }); scope.object = {'red':'FF0000', 'green':'00FF00', 'blue':'0000FF'}; scope.selected = '00FF00'; scope.$digest(); expect(select.val()).toEqual('green'); scope.selected = '0000FF'; scope.$digest(); expect(select.val()).toEqual('blue'); }); it('should insert a blank option if bound to null', function() { createSingleSelect(); scope.values = [{name:'A'}]; scope.selected = null; scope.$digest(); expect(select.find('option').length).toEqual(2); expect(select.val()).toEqual(''); expect(jqLite(select.find('option')[0]).val()).toEqual(''); scope.selected = scope.values[0]; scope.$digest(); expect(select.val()).toEqual('0'); expect(select.find('option').length).toEqual(1); }); it('should reuse blank option if bound to null', function() { createSingleSelect(true); scope.values = [{name:'A'}]; scope.selected = null; scope.$digest(); expect(select.find('option').length).toEqual(2); expect(select.val()).toEqual(''); expect(jqLite(select.find('option')[0]).val()).toEqual(''); scope.selected = scope.values[0]; scope.$digest(); expect(select.val()).toEqual('0'); expect(select.find('option').length).toEqual(2); }); it('should insert a unknown option if bound to something not in the list', function() { createSingleSelect(); scope.values = [{name:'A'}]; scope.selected = {}; scope.$digest(); expect(select.find('option').length).toEqual(2); expect(select.val()).toEqual('?'); expect(jqLite(select.find('option')[0]).val()).toEqual('?'); scope.selected = scope.values[0]; scope.$digest(); expect(select.val()).toEqual('0'); expect(select.find('option').length).toEqual(1); }); }); describe('on change', function() { it('should update model on change', function() { createSingleSelect(); scope.values = [{name:'A'}, {name:'B'}]; scope.selected = scope.values[0]; scope.$digest(); expect(select.val()).toEqual('0'); select.val('1'); browserTrigger(select, 'change'); expect(scope.selected).toEqual(scope.values[1]); }); it('should update model on change through expression', function() { createSelect({'ng:model':'selected', 'ng:options':'item.id as item.name for item in values'}); scope.values = [{id:10, name:'A'}, {id:20, name:'B'}]; scope.selected = scope.values[0].id; scope.$digest(); expect(select.val()).toEqual('0'); select.val('1'); browserTrigger(select, 'change'); expect(scope.selected).toEqual(scope.values[1].id); }); it('should update model to null on change', function() { createSingleSelect(true); scope.values = [{name:'A'}, {name:'B'}]; scope.selected = scope.values[0]; select.val('0'); scope.$digest(); select.val(''); browserTrigger(select, 'change'); expect(scope.selected).toEqual(null); }); }); describe('select-many', function() { it('should read multiple selection', function() { createMultiSelect(); scope.values = [{name:'A'}, {name:'B'}]; scope.selected = []; scope.$digest(); expect(select.find('option').length).toEqual(2); expect(jqLite(select.find('option')[0]).attr('selected')).toBeFalsy(); expect(jqLite(select.find('option')[1]).attr('selected')).toBeFalsy(); scope.selected.push(scope.values[1]); scope.$digest(); expect(select.find('option').length).toEqual(2); expect(select.find('option')[0].selected).toEqual(false); expect(select.find('option')[1].selected).toEqual(true); scope.selected.push(scope.values[0]); scope.$digest(); expect(select.find('option').length).toEqual(2); expect(select.find('option')[0].selected).toEqual(true); expect(select.find('option')[1].selected).toEqual(true); }); it('should update model on change', function() { createMultiSelect(); scope.values = [{name:'A'}, {name:'B'}]; scope.selected = []; scope.$digest(); select.find('option')[0].selected = true; browserTrigger(select, 'change'); expect(scope.selected).toEqual([scope.values[0]]); }); }); }); });