UNPKG

@rishavtandon93/ng-select-aria

Version:

Angular ng-select - All in One UI Select, Multiselect and Autocomplete

1,134 lines (952 loc) 189 kB
import {ComponentFixture, discardPeriodicTasks, fakeAsync, TestBed, tick, waitForAsync} from '@angular/core/testing'; import {By} from '@angular/platform-browser'; import {Component, DebugElement, ErrorHandler, NgZone, Type, ViewChild, ViewEncapsulation} from '@angular/core'; import {ConsoleService} from './console.service'; import {FormsModule} from '@angular/forms'; import {getNgSelectElement, selectOption, TestsErrorHandler, tickAndDetectChanges, triggerKeyDownEvent} from '../testing/helpers'; import {KeyCode, NgOption} from './ng-select.types'; import {MockConsole, MockNgZone} from '../testing/mocks'; import {NgSelectComponent} from '@ng-select/ng-select'; import {NgSelectModule} from './ng-select.module'; import {Subject} from 'rxjs'; import {NgSelectConfig} from './config.service'; describe('NgSelectComponent', () => { describe('Data source', () => { it('should set items from primitive numbers array', fakeAsync(() => { const fixture = createTestingModule( NgSelectTestComponent, `<ng-select [items]="[0, 30, 60, 90, 120, 180, 240]"> </ng-select>`); tickAndDetectChanges(fixture); const itemsList = fixture.componentInstance.select.itemsList; expect(itemsList.items.length).toBe(7); expect(itemsList.items[0]).toEqual(jasmine.objectContaining({ label: '0', value: 0 })); })); it('should create items from ng-option', fakeAsync(() => { const fixture = createTestingModule( NgSelectTestComponent, `<ng-select [(ngModel)]="selectedCity"> <ng-option [value]="true">Yes</ng-option> <ng-option [value]="false">No</ng-option> </ng-select>`); tickAndDetectChanges(fixture); const items = fixture.componentInstance.select.itemsList.items; expect(items.length).toBe(2); expect(items[0]).toEqual(jasmine.objectContaining({ label: 'Yes', value: true, disabled: false })); expect(items[1]).toEqual(jasmine.objectContaining({ label: 'No', value: false, disabled: false })); })); it('should create empty items list when initialzied with null', fakeAsync(() => { const fixture = createTestingModule( NgSelectTestComponent, `<ng-select [items]="null"> </ng-select>`); tickAndDetectChanges(fixture); const itemsList = fixture.componentInstance.select.itemsList; expect(itemsList.items.length).toBe(0); })); }); describe('Model bindings and data changes', () => { let select: NgSelectComponent; it('should update ngModel on value change', fakeAsync(() => { const fixture = createTestingModule( NgSelectTestComponent, `<ng-select [items]="cities" bindLabel="name" [clearable]="true" [(ngModel)]="selectedCity"> </ng-select>`); selectOption(fixture, KeyCode.ArrowDown, 1); tickAndDetectChanges(fixture); expect(fixture.componentInstance.selectedCity).toEqual(jasmine.objectContaining(fixture.componentInstance.cities[1])); fixture.componentInstance.select.clearModel(); tickAndDetectChanges(fixture); expect(fixture.componentInstance.selectedCity).toEqual(null); discardPeriodicTasks(); })); it('should update internal model on ngModel change', fakeAsync(() => { const fixture = createTestingModule( NgSelectTestComponent, `<ng-select [items]="cities" bindLabel="name" [clearable]="true" [(ngModel)]="selectedCity"> </ng-select>`); fixture.componentInstance.selectedCity = fixture.componentInstance.cities[0]; tickAndDetectChanges(fixture); expect(fixture.componentInstance.select.selectedItems).toEqual([ jasmine.objectContaining({ value: fixture.componentInstance.cities[0] }) ]); fixture.componentInstance.selectedCity = null; tickAndDetectChanges(fixture); expect(fixture.componentInstance.select.selectedItems).toEqual([]); discardPeriodicTasks(); })); it('should update internal model after it was toggled with *ngIf', fakeAsync(() => { const fixture = createTestingModule( NgSelectTestComponent, `<ng-select *ngIf="visible" [items]="cities" bindLabel="name" [clearable]="true" [(ngModel)]="selectedCity"> </ng-select>`); // select first city fixture.componentInstance.selectedCity = fixture.componentInstance.cities[0]; tickAndDetectChanges(fixture); // toggle to hide/show fixture.componentInstance.toggleVisible(); tickAndDetectChanges(fixture); fixture.componentInstance.toggleVisible(); tickAndDetectChanges(fixture); fixture.componentInstance.selectedCity = null; tickAndDetectChanges(fixture); expect(fixture.componentInstance.select.selectedItems).toEqual([]); })); it('should set items correctly after ngModel set first when bindValue is used', fakeAsync(() => { const fixture = createTestingModule( NgSelectTestComponent, `<ng-select [items]="cities" bindLabel="name" bindValue="id" [clearable]="true" [(ngModel)]="selectedCityId"> </ng-select>`); fixture.componentInstance.cities = []; fixture.componentInstance.selectedCityId = 7; tickAndDetectChanges(fixture); fixture.componentInstance.cities = [{ id: 7, name: 'Pailgis' }]; tickAndDetectChanges(fixture); select = fixture.componentInstance.select; expect(select.selectedItems[0]).toBe(select.itemsList.items[0]); expect(select.selectedItems).toEqual([jasmine.objectContaining({ value: { id: 7, name: 'Pailgis' } })]); })); it('should set items correctly after ngModel set first when bindValue is not used', fakeAsync(() => { const fixture = createTestingModule( NgSelectTestComponent, `<ng-select [items]="cities" bindLabel="name" [clearable]="true" [(ngModel)]="selectedCity"> </ng-select>`); fixture.componentInstance.cities = []; fixture.componentInstance.selectedCity = { id: 7, name: 'Pailgis' }; tickAndDetectChanges(fixture); fixture.componentInstance.cities = [{ id: 7, name: 'Pailgis' }]; tickAndDetectChanges(fixture); select = fixture.componentInstance.select; expect(select.selectedItems[0]).toBe(select.itemsList.items[0]); expect(select.selectedItems).toEqual([jasmine.objectContaining({ value: { id: 7, name: 'Pailgis' } })]); })); it('should set items correctly after ngModel set first when bindValue is used from NgSelectConfig', fakeAsync(() => { const config = new NgSelectConfig(); config.bindValue = 'id'; const fixture = createTestingModule( NgSelectTestComponent, `<ng-select [items]="cities" bindLabel="name" [clearable]="true" [(ngModel)]="selectedCityId"> </ng-select>`, config ); fixture.componentInstance.cities = []; fixture.componentInstance.selectedCityId = 7; tickAndDetectChanges(fixture); fixture.componentInstance.cities = [{ id: 7, name: 'Pailgis' }]; tickAndDetectChanges(fixture); select = fixture.componentInstance.select; expect(select.selectedItems[0]).toBe(select.itemsList.items[0]); expect(select.selectedItems).toEqual([jasmine.objectContaining({ value: { id: 7, name: 'Pailgis' } })]); })); it('should not apply global bindValue from NgSelectConfig if bindValue prop explicitly provided in template', fakeAsync(() => { const config = new NgSelectConfig(); config.bindValue = 'globalbindvalue'; const fixture = createTestingModule( NgSelectTestComponent, `<ng-select [items]="cities" bindLabel="name" bindValue="id" [clearable]="true" [(ngModel)]="selectedCityId"> </ng-select>`, config ); fixture.componentInstance.cities = []; fixture.componentInstance.selectedCityId = 7; tickAndDetectChanges(fixture); fixture.componentInstance.cities = [{ id: 7, name: 'Pailgis' }]; tickAndDetectChanges(fixture); select = fixture.componentInstance.select; expect(select.selectedItems[0]).toBe(select.itemsList.items[0]); expect(select.selectedItems).toEqual([jasmine.objectContaining({ value: { id: 7, name: 'Pailgis' } })]); })); it('should bind whole object as value when bindValue prop is specified with empty string in template', fakeAsync(() => { const fixture = createTestingModule( NgSelectTestComponent, `<ng-select [items]="cities" bindLabel="name" bindValue="" [clearable]="true" [(ngModel)]="selectedCity"> </ng-select>` ); fixture.componentInstance.cities = []; fixture.componentInstance.selectedCity = { id: 7, name: 'Pailgis' }; tickAndDetectChanges(fixture); fixture.componentInstance.cities = [{ id: 7, name: 'Pailgis' }]; tickAndDetectChanges(fixture); select = fixture.componentInstance.select; expect(select.selectedItems[0]).toBe(select.itemsList.items[0]); expect(select.selectedItems).toEqual([jasmine.objectContaining({ value: { id: 7, name: 'Pailgis' } })]); })); it('should map label correctly', fakeAsync(() => { const fixture = createTestingModule( NgSelectTestComponent, `<ng-select [items]="cities" bindLabel="name" [clearable]="true" [(ngModel)]="selectedCity"> </ng-select>`); fixture.componentInstance.cities = [{ label: 'Vilnius city', name: 'Vilnius' }]; tickAndDetectChanges(fixture); select = fixture.componentInstance.select; expect(select.itemsList.items[0].label).toBe('Vilnius'); })); it('should escape label', fakeAsync(() => { const fixture = createTestingModule( NgSelectTestComponent, `<ng-select [items]="cities" [clearable]="true" [(ngModel)]="selectedCity"> </ng-select>`); fixture.componentInstance.cities = [{ label: '<img src="azd" (error)="alert(1)" />', name: 'Vilnius' }]; tickAndDetectChanges(fixture); select = fixture.componentInstance.select; select.open(); const options = fixture.debugElement.nativeElement.querySelectorAll('.ng-option'); expect(options[0].innerText).toBe('<img src="azd" (error)="alert(1)" />'); })); it('should set items correctly after ngModel set first when typeahead and single select is used', fakeAsync(() => { const fixture = createTestingModule( NgSelectTestComponent, `<ng-select [items]="cities" bindLabel="name" [typeahead]="filter" placeholder="select value" [(ngModel)]="selectedCity"> </ng-select>`); select = fixture.componentInstance.select; fixture.componentInstance.selectedCity = { id: 1, name: 'Vilnius' }; tickAndDetectChanges(fixture); expect(select.selectedItems).toEqual([ jasmine.objectContaining({ label: 'Vilnius', value: { id: 1, name: 'Vilnius' } }) ]); fixture.componentInstance.cities = [ { id: 1, name: 'Vilnius' }, { id: 2, name: 'Kaunas' }, { id: 3, name: 'Pabrade' }, ]; tickAndDetectChanges(fixture); const vilnius = select.itemsList.items[0]; expect(select.selectedItems[0]).toBe(select.itemsList.items[0]); expect(vilnius.selected).toBeTruthy(); })); it('should set items correctly after ngModel set first when typeahead and multi-select is used', fakeAsync(() => { const fixture = createTestingModule( NgSelectTestComponent, `<ng-select [items]="cities" bindLabel="name" [multiple]="true" [typeahead]="filter" placeholder="select value" [(ngModel)]="selectedCities"> </ng-select>`); select = fixture.componentInstance.select; fixture.componentInstance.selectedCities = [{ id: 1, name: 'Vilnius' }, { id: 2, name: 'Kaunas' }]; tickAndDetectChanges(fixture); expect(select.selectedItems).toEqual([ jasmine.objectContaining({ label: 'Vilnius', value: { id: 1, name: 'Vilnius' } }), jasmine.objectContaining({ label: 'Kaunas', value: { id: 2, name: 'Kaunas' } }) ]); fixture.componentInstance.cities = [ { id: 1, name: 'Vilnius' }, { id: 2, name: 'Kaunas' }, { id: 3, name: 'Pabrade' }, ]; tickAndDetectChanges(fixture); const vilnius = select.itemsList.items[0]; const kaunas = select.itemsList.items[1]; expect(select.selectedItems[0]).toBe(vilnius); expect(vilnius.selected).toBeTruthy(); expect(select.selectedItems[1]).toBe(kaunas); expect(kaunas.selected).toBeTruthy(); })); it('should set items correctly if there is no bindLabel', fakeAsync(() => { const fixture = createTestingModule( NgSelectTestComponent, `<ng-select [items]="cities" [clearable]="true" [(ngModel)]="selectedCity"> </ng-select>`); const cities = [{ id: 7, name: 'Pailgis' }]; fixture.componentInstance.selectedCity = { id: 7, name: 'Pailgis' }; tickAndDetectChanges(fixture); fixture.componentInstance.cities = [{ id: 1, name: 'Vilnius' }, { id: 2, name: 'Kaunas' }]; tickAndDetectChanges(fixture); expect(fixture.componentInstance.select.selectedItems[0]).toEqual(jasmine.objectContaining({ value: cities[0] })); })); it('should bind ngModel object even if items are empty', fakeAsync(() => { const fixture = createTestingModule( NgSelectTestComponent, `<ng-select [items]="cities" bindLabel="name" [clearable]="true" [(ngModel)]="selectedCity"> </ng-select>`); fixture.componentInstance.cities = []; tickAndDetectChanges(fixture); fixture.componentInstance.selectedCity = { id: 7, name: 'Pailgis' }; tickAndDetectChanges(fixture); expect(fixture.componentInstance.select.selectedItems).toEqual([jasmine.objectContaining({ value: { id: 7, name: 'Pailgis' }, selected: true })]); })); it('should bind ngModel simple value even if items are empty', fakeAsync(() => { const fixture = createTestingModule( NgSelectTestComponent, `<ng-select [items]="citiesNames" [clearable]="true" [(ngModel)]="selectedCity"> </ng-select>`); fixture.componentInstance.cities = []; tickAndDetectChanges(fixture); fixture.componentInstance.selectedCity = <any>'Kaunas'; tickAndDetectChanges(fixture); expect(fixture.componentInstance.select.selectedItems).toEqual([jasmine.objectContaining({ value: 'Kaunas', label: 'Kaunas', selected: true })]); })); it('should preserve latest selected value when items are changing', fakeAsync(() => { const fixture = createTestingModule( NgSelectTestComponent, `<ng-select [items]="cities" bindLabel="name" [clearable]="true" [(ngModel)]="selectedCity"> </ng-select>`); fixture.componentInstance.selectedCity = fixture.componentInstance.cities[0]; tickAndDetectChanges(fixture); fixture.componentInstance.select.select(fixture.componentInstance.select.itemsList.items[1]); tickAndDetectChanges(fixture); fixture.componentInstance.cities = [...fixture.componentInstance.cities]; tickAndDetectChanges(fixture); expect(fixture.componentInstance.selectedCity).toEqual(fixture.componentInstance.cities[1]); fixture.componentInstance.select.clearModel(); fixture.componentInstance.cities = [...fixture.componentInstance.cities]; tickAndDetectChanges(fixture); expect(fixture.componentInstance.selectedCity).toBeNull(); })); it('should map selected items with items in dropdown', fakeAsync(() => { const fixture = createTestingModule( NgSelectTestComponent, `<ng-select [items]="cities" bindLabel="name" [clearable]="true" [(ngModel)]="selectedCity"> </ng-select>`); select = fixture.componentInstance.select; fixture.componentInstance.selectedCity = fixture.componentInstance.cities[0]; tickAndDetectChanges(fixture); fixture.componentInstance.cities = [...fixture.componentInstance.cities]; tickAndDetectChanges(fixture); fixture.componentInstance.cities = [...fixture.componentInstance.cities]; tickAndDetectChanges(fixture); expect(fixture.componentInstance.selectedCity).toEqual(fixture.componentInstance.cities[0]); expect(select.itemsList.filteredItems[0].selected).toBeTruthy(); })); it('should keep selected item while setting new items and bindValue is incorrect', fakeAsync(() => { const fixture = createTestingModule( NgSelectTestComponent, `<ng-select [items]="cities" bindLabel="name" bindValue="value" [clearable]="true" [(ngModel)]="selectedCityId"> </ng-select>`); tickAndDetectChanges(fixture); // triggers write value select = fixture.componentInstance.select; select.select(select.itemsList.items[1]); tickAndDetectChanges(fixture); fixture.componentInstance.cities = [...fixture.componentInstance.cities]; tickAndDetectChanges(fixture); expect(select.selectedItems[0]).toEqual(jasmine.objectContaining({ value: { id: 2, name: 'Kaunas' } })); })); it('should clear previous single select value when setting new model', fakeAsync(() => { const fixture = createTestingModule( NgSelectTestComponent, `<ng-select [items]="cities" bindLabel="name" [clearable]="true" [(ngModel)]="selectedCity"> </ng-select>`); fixture.componentInstance.selectedCity = fixture.componentInstance.cities[0]; tickAndDetectChanges(fixture); const lastSelection: any = fixture.componentInstance.select.selectedItems[0]; expect(lastSelection.selected).toBeTruthy(); fixture.componentInstance.selectedCity = null; tickAndDetectChanges(fixture); expect(lastSelection.selected).toBeFalsy(); })); it('should clear disabled selected values when setting new model', fakeAsync(() => { const fixture = createTestingModule( NgSelectTestComponent, `<ng-select [items]="cities" bindLabel="name" [multiple]="true" [clearable]="true" [(ngModel)]="selectedCities"> </ng-select>`); const disabled = { ...fixture.componentInstance.cities[1], disabled: true }; fixture.componentInstance.selectedCities = <any>[fixture.componentInstance.cities[0], disabled]; tickAndDetectChanges(fixture); fixture.componentInstance.cities[1].disabled = true; fixture.componentInstance.cities = [...fixture.componentInstance.cities]; tickAndDetectChanges(fixture); fixture.componentInstance.selectedCities = []; tickAndDetectChanges(fixture); expect(fixture.componentInstance.select.selectedItems).toEqual([]); })); it('should clear previous selected value even if it is disabled', fakeAsync(() => { const fixture = createTestingModule( NgSelectTestComponent, `<ng-select [items]="cities" bindLabel="name" [clearable]="true" [(ngModel)]="selectedCity"> </ng-select>`); fixture.componentInstance.cities[0].disabled = true; fixture.componentInstance.cities = [...fixture.componentInstance.cities]; fixture.componentInstance.selectedCity = fixture.componentInstance.cities[0]; tickAndDetectChanges(fixture); fixture.componentInstance.selectedCity = fixture.componentInstance.cities[1]; tickAndDetectChanges(fixture); expect(fixture.componentInstance.select.selectedItems.length).toBe(1); })); it('should clear previous multiple select value when setting new model', fakeAsync(() => { const fixture = createTestingModule( NgSelectTestComponent, `<ng-select [items]="cities" bindLabel="name" [multiple]="true" [clearable]="true" [(ngModel)]="selectedCities"> </ng-select>`); fixture.componentInstance.selectedCities = [fixture.componentInstance.cities[0]]; tickAndDetectChanges(fixture); select = fixture.componentInstance.select; expect(select.selectedItems.length).toBe(1); fixture.componentInstance.selectedCities = [fixture.componentInstance.cities[1]]; tickAndDetectChanges(fixture); expect(select.selectedItems.length).toBe(1); fixture.componentInstance.selectedCities = []; tickAndDetectChanges(fixture); expect(select.selectedItems.length).toBe(0); })); it('should not add selected items to new items list when [items] are changed', fakeAsync(() => { const fixture = createTestingModule( NgSelectTestComponent, `<ng-select [items]="cities" bindLabel="name" [multiple]="true" [clearable]="true" [(ngModel)]="selectedCities"> </ng-select>`); fixture.componentInstance.selectedCities = [...fixture.componentInstance.cities.slice(0, 2)]; tickAndDetectChanges(fixture); fixture.componentInstance.cities = [{ id: 1, name: 'New city' }]; tickAndDetectChanges(fixture); const internalItems = fixture.componentInstance.select.itemsList.items; expect(internalItems.length).toBe(1); expect(internalItems[0].value).toEqual(jasmine.objectContaining({ id: 1, name: 'New city' })); })); it('should reset marked item when [items] are changed and dropdown is opened', fakeAsync(() => { const fixture = createTestingModule( NgSelectTestComponent, `<ng-select [items]="cities" bindLabel="name" [clearable]="true" [(ngModel)]="selectedCity"> </ng-select>`); select = fixture.componentInstance.select; fixture.componentInstance.selectedCity = fixture.componentInstance.cities[2]; tickAndDetectChanges(fixture); triggerKeyDownEvent(getNgSelectElement(fixture), KeyCode.Space); expect(fixture.componentInstance.select.itemsList.markedItem.value).toEqual({ name: 'Pabrade', id: 3 }); fixture.componentInstance.selectedCity = { name: 'New city', id: 5 }; tickAndDetectChanges(fixture); fixture.componentInstance.cities = [...fixture.componentInstance.cities]; tickAndDetectChanges(fixture); expect(fixture.componentInstance.select.itemsList.markedItem.value).toEqual({ name: 'Vilnius', id: 1 }); })); it('should bind to custom object properties', fakeAsync(() => { const fixture = createTestingModule( NgSelectTestComponent, `<ng-select [items]="cities" bindLabel="name" bindValue="id" [(ngModel)]="selectedCityId"> </ng-select>`); selectOption(fixture, KeyCode.ArrowDown, 0); tickAndDetectChanges(fixture); expect(fixture.componentInstance.selectedCityId).toEqual(1); fixture.componentInstance.selectedCityId = 2; tickAndDetectChanges(fixture); expect(fixture.componentInstance.select.selectedItems).toEqual([jasmine.objectContaining({ value: fixture.componentInstance.cities[1] })]); })); it('should bind to nested label property', fakeAsync(() => { const fixture = createTestingModule( NgSelectTestComponent, `<ng-select [items]="countries" bindLabel="description.name" [(ngModel)]="selectedCountry"> </ng-select>`); selectOption(fixture, KeyCode.ArrowDown, 1); fixture.detectChanges(); expect(fixture.componentInstance.select.selectedItems).toEqual([jasmine.objectContaining({ label: 'USA', value: fixture.componentInstance.countries[1] })]); fixture.componentInstance.selectedCountry = fixture.componentInstance.countries[0]; tickAndDetectChanges(fixture); expect(fixture.componentInstance.select.selectedItems).toEqual([jasmine.objectContaining({ label: 'Lithuania', value: fixture.componentInstance.countries[0] })]); })); it('should bind to nested value property', fakeAsync(() => { const fixture = createTestingModule( NgSelectTestComponent, `<ng-select [items]="countries" bindLabel="description.name" bindValue="description.id" [(ngModel)]="selectedCountry"> </ng-select>`); selectOption(fixture, KeyCode.ArrowDown, 1); tickAndDetectChanges(fixture); expect(fixture.componentInstance.selectedCountry).toEqual('b'); fixture.componentInstance.selectedCountry = fixture.componentInstance.countries[2].description.id; tickAndDetectChanges(fixture); expect(fixture.componentInstance.select.selectedItems).toEqual([jasmine.objectContaining({ label: 'Australia', value: fixture.componentInstance.countries[2] })]); selectOption(fixture, KeyCode.ArrowUp, 1); tickAndDetectChanges(fixture); expect(fixture.componentInstance.selectedCountry).toEqual('b'); })); it('should bind to simple array', fakeAsync(() => { const fixture = createTestingModule( NgSelectTestComponent, `<ng-select [items]="citiesNames" [(ngModel)]="selectedCity"> </ng-select>`); selectOption(fixture, KeyCode.ArrowDown, 0); tickAndDetectChanges(fixture); expect(fixture.componentInstance.selectedCity).toBe(<any>'Vilnius'); fixture.componentInstance.selectedCity = <any>'Kaunas'; tickAndDetectChanges(fixture); expect(fixture.componentInstance.select.selectedItems) .toEqual([jasmine.objectContaining({ label: 'Kaunas', value: 'Kaunas' })]); })); it('should bind to object', fakeAsync(() => { const fixture = createTestingModule( NgSelectTestComponent, `<ng-select [items]="cities" bindLabel="name" [(ngModel)]="selectedCity"> </ng-select>`); // from component to model selectOption(fixture, KeyCode.ArrowDown, 0); tickAndDetectChanges(fixture); expect(fixture.componentInstance.selectedCity).toEqual(fixture.componentInstance.cities[0]); // from model to component fixture.componentInstance.selectedCity = fixture.componentInstance.cities[1]; tickAndDetectChanges(fixture); expect(fixture.componentInstance.select.selectedItems).toEqual([jasmine.objectContaining({ value: fixture.componentInstance.cities[1] })]); discardPeriodicTasks(); })); it('should use bindLabel from NgSelectConfig when bindLabel is not provided in template', fakeAsync(() => { const config = new NgSelectConfig(); config.bindLabel = 'name'; const fixture = createTestingModule( NgSelectTestComponent, `<ng-select [items]="cities" [clearable]="true" [(ngModel)]="selectedCity"> </ng-select>`, config ); fixture.componentInstance.cities = [{ id: 1, name: 'Vilnius', label: '' }]; fixture.componentInstance.selectedCity = fixture.componentInstance.cities[0]; tickAndDetectChanges(fixture); select = fixture.componentInstance.select; expect(select.selectedItems).toEqual([jasmine.objectContaining({ label: 'Vilnius' })]); })); it('should override bindLabel from NgSelectConfig by template-provided bindLabel property', fakeAsync(() => { const config = new NgSelectConfig(); config.bindLabel = 'label'; const fixture = createTestingModule( NgSelectTestComponent, `<ng-select [items]="cities" bindLabel="name" [clearable]="true" [(ngModel)]="selectedCity"> </ng-select>`, config ); fixture.componentInstance.cities = [{ id: 1, name: 'Vilnius', label: 'the capital of Lithuania' }]; fixture.componentInstance.selectedCity = fixture.componentInstance.cities[0]; tickAndDetectChanges(fixture); select = fixture.componentInstance.select; expect(select.selectedItems).toEqual([jasmine.objectContaining({ label: 'Vilnius' })]); })); it('should bind option label to "label" property when bindLabel is not provided', fakeAsync(() => { const fixture = createTestingModule( NgSelectTestComponent, `<ng-select [items]="cities" [clearable]="true" [(ngModel)]="selectedCity"> </ng-select>`, ); fixture.componentInstance.cities = [{ id: 1, name: 'Vilnius', label: 'the capital of Lithuania' }]; fixture.componentInstance.selectedCity = fixture.componentInstance.cities[0]; tickAndDetectChanges(fixture); select = fixture.componentInstance.select; expect(select.selectedItems).toEqual([jasmine.objectContaining({ label: 'the capital of Lithuania' })]); })); describe('ng-option', () => { it('should reset to empty array', fakeAsync(() => { const fixture = createTestingModule( NgSelectTestComponent, `<ng-select [(ngModel)]="selectedCityId"> <ng-option *ngFor="let city of cities" [value]="city.id">{{city.name}}</ng-option> </ng-select>`); select = fixture.componentInstance.select; tickAndDetectChanges(fixture); expect(select.items.length).toEqual(3); fixture.componentInstance.cities = []; tickAndDetectChanges(fixture); expect(select.items.length).toEqual(0); })); it('should bind value', fakeAsync(() => { const fixture = createTestingModule( NgSelectTestComponent, `<ng-select [(ngModel)]="selectedCityId"> <ng-option [value]="1">A</ng-option> <ng-option [value]="2">B</ng-option> </ng-select>`); // from component to model selectOption(fixture, KeyCode.ArrowDown, 0); tickAndDetectChanges(fixture); expect(fixture.componentInstance.selectedCityId).toEqual(1); // from model to component fixture.componentInstance.selectedCityId = 2; tickAndDetectChanges(fixture); expect(fixture.componentInstance.select.selectedItems).toEqual([jasmine.objectContaining({ value: 2, label: 'B' })]); discardPeriodicTasks(); })); it('should not fail while resolving selected item from object', fakeAsync(() => { const fixture = createTestingModule( NgSelectTestComponent, `<ng-select [(ngModel)]="selectedCity"> <ng-option [value]="cities[0]">Vilnius</ng-option> <ng-option [value]="cities[1]">Kaunas</ng-option> </ng-select>`); const selected = { name: 'Vilnius', id: 1 }; fixture.componentInstance.selectedCity = selected; tickAndDetectChanges(fixture); expect(fixture.componentInstance.select.selectedItems).toEqual([jasmine.objectContaining({ value: selected, label: '' })]); })); }); it('should not set internal model when single select ngModel is not valid', fakeAsync(() => { const fixture = createTestingModule( NgSelectTestComponent, `<ng-select [items]="cities" bindLabel="name" [clearable]="true" [multiple]="false" [(ngModel)]="selectedCity"> </ng-select>`); const invalidValues = [undefined, null]; for (const v of invalidValues) { fixture.componentInstance.selectedCity = <any>v; tickAndDetectChanges(fixture); expect(fixture.componentInstance.select.selectedItems.length).toBe(0); } })); it('should not set internal model when multiselect ngModel is not valid', fakeAsync(() => { const fixture = createTestingModule( NgSelectTestComponent, `<ng-select [items]="cities" bindLabel="name" [clearable]="true" [multiple]="true" [(ngModel)]="selectedCity"> </ng-select>`); const invalidValues = [{}, '', undefined, 0, 1, 'false', 'true', false]; for (const v of invalidValues) { fixture.componentInstance.selectedCity = <any>v; tickAndDetectChanges(fixture); expect(fixture.componentInstance.select.selectedItems.length).toBe(0); } })); describe('Pre-selected model', () => { describe('single', () => { it('should select by bindValue when primitive type', fakeAsync(() => { const fixture = createTestingModule( NgSelectTestComponent, `<ng-select [items]="cities" bindLabel="name" bindValue="id" placeholder="select value" [(ngModel)]="selectedCityId"> </ng-select>`); fixture.componentInstance.selectedCityId = 2; tickAndDetectChanges(fixture); const result = [jasmine.objectContaining({ value: { id: 2, name: 'Kaunas' }, selected: true })]; select = fixture.componentInstance.select; expect(select.selectedItems).toEqual(result); })); it('should apply host css classes', fakeAsync(() => { const fixture = createTestingModule( NgSelectTestComponent, `<ng-select [items]="cities" bindLabel="name" bindValue="id" placeholder="select value" [(ngModel)]="selectedCityId"> </ng-select>`); fixture.componentInstance.selectedCityId = 2; tickAndDetectChanges(fixture); tickAndDetectChanges(fixture); const classes = ['ng-select', 'ng-select-single', 'ng-select-searchable']; const selectEl = fixture.nativeElement.querySelector('ng-select'); for (const c of classes) { expect(selectEl.classList.contains(c)).toBeTruthy(`expected to contain "${c}" class`); } let hasValueEl = fixture.nativeElement.querySelector('.ng-has-value'); expect(hasValueEl).not.toBeNull(); fixture.componentInstance.selectedCityId = null; tickAndDetectChanges(fixture); tickAndDetectChanges(fixture); hasValueEl = fixture.nativeElement.querySelector('.ng-has-value'); expect(hasValueEl).toBeNull(); })); it('should select by bindValue ', fakeAsync(() => { const fixture = createTestingModule( NgSelectTestComponent, `<ng-select [items]="cities" bindLabel="name" bindValue="id" placeholder="select value" [(ngModel)]="selectedCityId"> </ng-select>`); fixture.componentInstance.cities = [{ id: 0, name: 'Vilnius' }]; fixture.componentInstance.selectedCityId = 0; tickAndDetectChanges(fixture); const result = [jasmine.objectContaining({ value: { id: 0, name: 'Vilnius' }, selected: true })]; expect(fixture.componentInstance.select.selectedItems).toEqual(result); })); it('should select by bindLabel when binding to object', fakeAsync(() => { const fixture = createTestingModule( NgSelectTestComponent, `<ng-select [items]="cities" bindLabel="name" placeholder="select value" [(ngModel)]="selectedCity"> </ng-select>`); fixture.componentInstance.selectedCity = { id: 2, name: 'Kaunas' }; tickAndDetectChanges(fixture); const result = [jasmine.objectContaining({ value: { id: 2, name: 'Kaunas' }, selected: true })]; expect(fixture.componentInstance.select.selectedItems).toEqual(result); })); it('should select by object reference', fakeAsync(() => { const fixture = createTestingModule( NgSelectTestComponent, `<ng-select [items]="cities" bindLabel="name" placeholder="select value" [(ngModel)]="selectedCity"> </ng-select>`); fixture.componentInstance.selectedCity = fixture.componentInstance.cities[1]; tickAndDetectChanges(fixture); const result = [jasmine.objectContaining({ value: { id: 2, name: 'Kaunas' }, selected: true })]; expect(fixture.componentInstance.select.selectedItems).toEqual(result); })); it('should select by compareWith function when bindValue is not used', fakeAsync(() => { const fixture = createTestingModule( NgSelectTestComponent, `<ng-select [items]="cities" bindLabel="name" placeholder="select value" [compareWith]="compareWith" [(ngModel)]="selectedCity"> </ng-select>`); const city = { name: 'Vilnius', id: 7, district: 'Ozo parkas' }; fixture.componentInstance.cities.push(city); fixture.componentInstance.cities = [...fixture.componentInstance.cities]; fixture.componentInstance.selectedCity = { name: 'Vilnius', district: 'Ozo parkas' } as any; tickAndDetectChanges(fixture); expect(fixture.componentInstance.select.selectedItems[0].value).toEqual(city); })); it('should select by compareWith function when bindValue is used', fakeAsync(() => { const fixture = createTestingModule( NgSelectTestComponent, `<ng-select [items]="cities" bindLabel="name" bindValue="id" placeholder="select value" [compareWith]="compareWith" [(ngModel)]="selectedCityId"> </ng-select>`); const cmp = fixture.componentInstance; cmp.selectedCityId = cmp.cities[1].id.toString(); cmp.compareWith = (city, model: string) => city.id === +model; tickAndDetectChanges(fixture); expect(cmp.select.selectedItems[0].value).toEqual(cmp.cities[1]); })); it('should select selected when there is no items', fakeAsync(() => { const fixture = createTestingModule( NgSelectTestComponent, `<ng-select [items]="cities" bindLabel="name" bindValue="id" placeholder="select value" [(ngModel)]="selectedCityId"> </ng-select>`); fixture.componentInstance.cities = []; fixture.componentInstance.selectedCityId = 2; tickAndDetectChanges(fixture); const selected = fixture.componentInstance.select.selectedItems[0]; expect(selected.label).toEqual(''); expect(selected.value).toEqual({ name: null, id: 2 }); })); }); describe('multiple', () => { const result = [ jasmine.objectContaining({ value: { id: 2, name: 'Kaunas' }, selected: true }), jasmine.objectContaining({ value: { id: 3, name: 'Pabrade' }, selected: true })]; it('should select by bindValue when primitive type', fakeAsync(() => { const fixture = createTestingModule( NgSelectTestComponent, `<ng-select [items]="cities" bindLabel="name" bindValue="id" multiple="true" placeholder="select value" [(ngModel)]="selectedCityIds"> </ng-select>`); fixture.componentInstance.selectedCityIds = [2, 3]; tickAndDetectChanges(fixture); expect(fixture.componentInstance.select.selectedItems).toEqual(result) })); it('should select by bindLabel when binding to object', fakeAsync(() => { const fixture = createTestingModule( NgSelectTestComponent, `<ng-select [items]="cities" bindLabel="name" multiple="true" placeholder="select value" [(ngModel)]="selectedCities"> </ng-select>`); fixture.componentInstance.selectedCities = [{ id: 2, name: 'Kaunas' }, { id: 3, name: 'Pabrade' }]; tickAndDetectChanges(fixture); expect(fixture.componentInstance.select.selectedItems).toEqual(result); })); }); }); }); describe('Dropdown panel', () => { it('should set and render items in dropdown panel', fakeAsync(() => { const fixture = createTestingModule( NgSelectTestComponent, `<ng-select [items]="cities" bindLabel="name" [(ngModel)]="city"> </ng-select>`); const select = fixture.componentInstance.select; select.open(); expect(select.dropdownPanel.items.length).toBe(3); let options = fixture.debugElement.nativeElement.querySelectorAll('.ng-option'); expect(options.length).toBe(3); expect(options[0].innerText).toBe('Vilnius'); expect(options[1].innerText).toBe('Kaunas'); expect(options[2].innerText).toBe('Pabrade'); fixture.componentInstance.cities = Array.from(Array(30).keys()).map((_, i) => ({ id: i, name: String.fromCharCode(97 + i) })); tickAndDetectChanges(fixture); options = fixture.debugElement.nativeElement.querySelectorAll('.ng-option'); expect(options.length).toBe(30); expect(options[0].innerText).toBe('a'); })); it('should always have div #padding with height 0 in dropdown panel when virtua