/*
* @license
* Copyright Akveo. All Rights Reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*/
import {
Component,
ElementRef,
EventEmitter,
Input,
Output,
QueryList,
ViewChild,
ViewChildren,
} from '@angular/core';
import { ComponentFixture, fakeAsync, flush, TestBed } from '@angular/core/testing';
import { FormControl, FormsModule, ReactiveFormsModule } from '@angular/forms';
import { By } from '@angular/platform-browser';
import { RouterTestingModule } from '@angular/router/testing';
import { from, zip, Subject } from 'rxjs';
import createSpy = jasmine.createSpy;
import {
NbSelectModule,
NbThemeModule,
NbOverlayContainerAdapter,
NB_DOCUMENT,
NbSelectComponent,
NbLayoutModule,
NbOptionComponent,
NbOptionGroupComponent,
NbTriggerStrategyBuilderService,
} from '@nebular/theme';
import { NbFocusKeyManagerFactoryService } from '@nebular/theme/components/cdk/a11y/focus-key-manager';
const eventMock = { preventDefault() {} } as Event;
const TEST_GROUPS = [
{
title: 'Group 1',
options: [
{ title: 'Option 1', value: 'Option 1' },
{ title: 'Option 2', value: 'Option 2' },
{ title: 'Option 3', value: 'Option 3' },
],
},
{
title: 'Group 2',
options: [
{ title: 'Option 21', value: 'Option 21' },
{ title: 'Option 22', value: 'Option 22' },
{ title: 'Option 23', value: 'Option 23' },
],
},
{
title: 'Group 3',
options: [
{ title: 'Option 31', value: 'Option 31' },
{ title: 'Option 32', value: 'Option 32' },
{ title: 'Option 33', value: 'Option 33' },
],
},
{
title: 'Group 4',
options: [
{ title: 'Option 41', value: '' },
{ title: 'Option 42', value: '0' },
{ title: 'Option 43', value: 0 },
{ title: 'Option 44' },
],
},
];
@Component({
selector: 'nb-select-test',
template: `
{{ selected.reverse() }}
None
{{ option.title }}
`,
})
export class NbSelectTestComponent {
@Input() selected: any = null;
@Input() multiple: boolean;
@Input() customLabel: boolean;
@Output() selectedChange: EventEmitter = new EventEmitter();
@ViewChildren(NbOptionComponent) options: QueryList>;
groups = TEST_GROUPS;
}
@Component({
template: `
a
b
c
`,
})
export class BasicSelectTestComponent {}
@Component({
template: `
{{ option }}
`,
})
export class NbSelectWithOptionsObjectsComponent {
@Input() compareFn = (o1: any, o2: any) => JSON.stringify(o1) === JSON.stringify(o2);
@Input() selected = { id: 2 };
@Input() options = [{ id: 1 }, { id: 2 }, { id: 3 }];
@ViewChildren(NbOptionComponent) optionComponents: QueryList;
}
@Component({
template: `
{{ option }}
`,
})
export class NbSelectWithInitiallySelectedOptionComponent {
@Input() selected = 1;
@Input() options = [ 1, 2, 3 ];
}
@Component({
template: `
{{ option }}
`,
})
export class NbReactiveFormSelectComponent {
options: number[] = [ 1 ];
showSelect: boolean = true;
formControl: FormControl = new FormControl();
@ViewChild(NbSelectComponent) selectComponent: NbSelectComponent;
@ViewChildren(NbOptionComponent) optionComponents: QueryList>;
}
@Component({
template: `
{{ option }}
`,
})
export class NbNgModelSelectComponent {
options: number[] = [ 1 ];
selectedValue: number = null;
@ViewChild(NbOptionComponent) optionComponent: NbOptionComponent;
}
@Component({
template: `
No value option
undefined value
undefined value
false value
0 value
empty string value
NaN value
truthy value
`,
})
export class NbSelectWithFalsyOptionValuesComponent {
nanValue = NaN;
@ViewChildren(NbOptionComponent) options: QueryList>;
@ViewChildren(NbOptionComponent, { read: ElementRef }) optionElements: QueryList>;
get noValueOption(): NbOptionComponent {
return this.options.toArray()[0];
}
get noValueOptionElement(): ElementRef {
return this.optionElements.toArray()[0];
}
get nullOption(): NbOptionComponent {
return this.options.toArray()[1];
}
get nullOptionElement(): ElementRef {
return this.optionElements.toArray()[1];
}
get undefinedOption(): NbOptionComponent {
return this.options.toArray()[2];
}
get undefinedOptionElement(): ElementRef {
return this.optionElements.toArray()[2];
}
get falseOption(): NbOptionComponent {
return this.options.toArray()[3];
}
get falseOptionElement(): ElementRef {
return this.optionElements.toArray()[3];
}
get zeroOption(): NbOptionComponent {
return this.options.toArray()[4];
}
get zeroOptionElement(): ElementRef {
return this.optionElements.toArray()[4];
}
get emptyStringOption(): NbOptionComponent {
return this.options.toArray()[5];
}
get emptyStringOptionElement(): ElementRef {
return this.optionElements.toArray()[5];
}
get nanOption(): NbOptionComponent {
return this.options.toArray()[6];
}
get nanOptionElement(): ElementRef {
return this.optionElements.toArray()[6];
}
get truthyOption(): NbOptionComponent {
return this.options.toArray()[7];
}
get truthyOptionElement(): ElementRef {
return this.optionElements.toArray()[7];
}
}
@Component({
template: `
No value option
undefined value
undefined value
false value
0 value
empty string value
NaN value
truthy value
`,
})
export class NbMultipleSelectWithFalsyOptionValuesComponent extends NbSelectWithFalsyOptionValuesComponent {}
@Component({
template: `
1
`,
})
export class NbOptionDisabledTestComponent {
optionGroupDisabled = false;
optionDisabled = false;
@ViewChild(NbSelectComponent) selectComponent: NbSelectComponent;
@ViewChild(NbOptionGroupComponent) optionGroupComponent: NbOptionGroupComponent;
@ViewChild(NbOptionComponent) optionComponent: NbOptionComponent;
}
describe('Component: NbSelectComponent', () => {
let fixture: ComponentFixture;
let overlayContainerService: NbOverlayContainerAdapter;
let overlayContainer: HTMLElement;
let document: Document;
let select: NbSelectComponent;
const setSelectedAndOpen = selected => {
fixture.componentInstance.selected = selected;
fixture.detectChanges();
select.show();
};
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
RouterTestingModule.withRoutes([]),
FormsModule,
ReactiveFormsModule,
NbThemeModule.forRoot(),
NbLayoutModule,
NbSelectModule,
],
declarations: [
NbSelectTestComponent,
NbSelectWithOptionsObjectsComponent,
NbSelectWithInitiallySelectedOptionComponent,
NbReactiveFormSelectComponent,
NbNgModelSelectComponent,
],
});
fixture = TestBed.createComponent(NbSelectTestComponent);
overlayContainerService = TestBed.inject(NbOverlayContainerAdapter);
document = TestBed.inject(NB_DOCUMENT);
select = fixture.debugElement.query(By.directive(NbSelectComponent)).componentInstance;
overlayContainer = document.createElement('div');
overlayContainerService.setContainer(overlayContainer);
fixture.detectChanges();
});
afterEach(() => {
select.hide();
overlayContainerService.clearContainer();
});
it('should render passed item as selected', () => {
setSelectedAndOpen('Option 23');
const selected = overlayContainer.querySelector('nb-option.selected');
expect(selected).toBeTruthy();
expect(selected.textContent).toContain('Option 23');
});
it('should render passed items as selected', () => {
select.multiple = true;
setSelectedAndOpen(['Option 1', 'Option 21', 'Option 31']);
const selected = overlayContainer.querySelectorAll('nb-option.selected');
expect(selected.length).toBe(3);
expect(selected[0].textContent).toContain('Option 1');
expect(selected[1].textContent).toContain('Option 21');
expect(selected[2].textContent).toContain('Option 31');
});
it('should fire selectedChange item when selection changes', done => {
setSelectedAndOpen('Option 1');
fixture.componentInstance.selectedChange.subscribe(selection => {
expect(selection).toBe('Option 21');
done();
});
const option = overlayContainer.querySelectorAll('nb-option')[4];
option.dispatchEvent(new Event('click'));
});
it('should fire selectedChange items when selecting multiple one by one', done => {
select.multiple = true;
setSelectedAndOpen([]);
zip(
from([['Option 2'], ['Option 2', 'Option 21'], ['Option 2', 'Option 21', 'Option 23']]),
fixture.componentInstance.selectedChange,
).subscribe(([expected, real]) => expect(real).toEqual(expected), null, done);
const option1 = overlayContainer.querySelectorAll('nb-option')[2];
const option2 = overlayContainer.querySelectorAll('nb-option')[4];
const option3 = overlayContainer.querySelectorAll('nb-option')[6];
option1.dispatchEvent(new Event('click'));
option2.dispatchEvent(new Event('click'));
option3.dispatchEvent(new Event('click'));
});
it('should deselect item when clicking on reselect item', () => {
setSelectedAndOpen('Option 1');
const option = overlayContainer.querySelector('nb-option');
option.dispatchEvent(new Event('click'));
expect(overlayContainer.querySelectorAll('nb-option.selected').length).toBe(0);
});
it('should deselect all items when clicking on reset item in multiple select', () => {
select.multiple = true;
setSelectedAndOpen(['Option 1', 'Option 2']);
const option = overlayContainer.querySelector('nb-option');
option.dispatchEvent(new Event('click'));
expect(overlayContainer.querySelectorAll('nb-option.selected').length).toBe(0);
});
it('should emit selectionChange with empty array when reset option selected in multiple select', () => {
select.multiple = true;
setSelectedAndOpen(['Option 1', 'Option 2']);
const selectionChangeSpy = createSpy('selectionChangeSpy');
select.selectedChange.subscribe(selectionChangeSpy);
const option = overlayContainer.querySelector('nb-option');
option.dispatchEvent(new Event('click'));
expect(selectionChangeSpy).toHaveBeenCalledWith([]);
});
it('should emit selectionChange with null when reset option selected in single select', () => {
setSelectedAndOpen('Option 1');
const selectionChangeSpy = createSpy('selectionChangeSpy');
select.selectedChange.subscribe(selectionChangeSpy);
const option = overlayContainer.querySelector('nb-option');
option.dispatchEvent(new Event('click'));
expect(selectionChangeSpy).toHaveBeenCalledWith(null);
});
it('should deselect only clicked item in multiple select', () => {
select.multiple = true;
setSelectedAndOpen(['Option 1', 'Option 2']);
const option = overlayContainer.querySelectorAll('nb-option')[1];
option.dispatchEvent(new Event('click'));
fixture.detectChanges();
const selected = overlayContainer.querySelectorAll('nb-option.selected');
expect(selected.length).toBe(1);
expect(selected[0].textContent).toContain('Option 2');
});
it('should render placeholder when nothing selected', () => {
select.multiple = true;
setSelectedAndOpen([]);
const button = fixture.nativeElement.querySelector('button');
expect(button.textContent).toContain('This is test select component');
});
it('should render default label when something selected', () => {
setSelectedAndOpen('Option 1');
const button = fixture.nativeElement.querySelector('button');
expect(button.textContent).toContain('Option 1');
});
it('should render custom label when something selected and custom label provided', () => {
select.customLabel = true;
setSelectedAndOpen('Option 1');
select.selectedChange.subscribe(selection => {
fixture.componentInstance.selected = selection;
fixture.detectChanges();
const button = fixture.nativeElement.querySelector('button');
expect(button.textContent).toContain('1 noitpO');
});
});
it('should select initially specified value without errors', fakeAsync(() => {
const selectFixture = TestBed.createComponent(NbSelectWithInitiallySelectedOptionComponent);
selectFixture.detectChanges();
flush();
selectFixture.detectChanges();
const selectedOption = selectFixture.debugElement.query(By.directive(NbSelectComponent))
.componentInstance
.options.find(o => o.selected);
expect(selectedOption.value).toEqual(selectFixture.componentInstance.selected);
const selectButton = selectFixture.nativeElement.querySelector('nb-select button') as HTMLElement;
expect(selectButton.textContent).toEqual(selectedOption.value.toString());
}));
it('should use compareWith function to compare values', fakeAsync(() => {
const selectFixture = TestBed.createComponent(NbSelectWithOptionsObjectsComponent);
const testComponent = selectFixture.componentInstance;
selectFixture.detectChanges();
flush();
selectFixture.detectChanges();
const selectedOption = testComponent.optionComponents.find(o => o.selected);
expect(selectedOption.value).toEqual({ id: 2 });
}));
it('should ignore selection change if destroyed', fakeAsync(() => {
const selectFixture = TestBed.createComponent(NbReactiveFormSelectComponent);
const testSelectComponent = selectFixture.componentInstance;
selectFixture.detectChanges();
flush();
const setSelectionSpy = spyOn((testSelectComponent.selectComponent as any), 'setSelection').and.callThrough();
testSelectComponent.showSelect = false;
selectFixture.detectChanges();
expect(() => testSelectComponent.formControl.setValue(1)).not.toThrow();
expect(setSelectionSpy).not.toHaveBeenCalled();
}));
it('should select option set through formControl binding', fakeAsync(() => {
const selectFixture = TestBed.createComponent(NbReactiveFormSelectComponent);
const testComponent = selectFixture.componentInstance;
selectFixture.detectChanges();
flush();
const optionSelectSpy = spyOn(testComponent.optionComponents.first, 'select').and.callThrough();
expect(testComponent.optionComponents.first.selected).toEqual(false);
testComponent.formControl.setValue(1);
selectFixture.detectChanges();
expect(testComponent.optionComponents.first.selected).toEqual(true);
expect(optionSelectSpy).toHaveBeenCalledTimes(1);
}));
it('should select option set through select "selected" binding', fakeAsync(() => {
const selectFixture = TestBed.createComponent(NbSelectTestComponent);
const testComponent = selectFixture.componentInstance;
selectFixture.detectChanges();
flush();
const optionToSelect = testComponent.options.find(o => o.value != null);
const optionSelectSpy = spyOn(optionToSelect, 'select').and.callThrough();
expect(optionToSelect.selected).toEqual(false);
testComponent.selected = optionToSelect.value;
selectFixture.detectChanges();
expect(optionToSelect.selected).toEqual(true);
expect(optionSelectSpy).toHaveBeenCalledTimes(1);
}));
it('should select option set through ngModel binding', fakeAsync(() => {
const selectFixture = TestBed.createComponent(NbNgModelSelectComponent);
const testComponent = selectFixture.componentInstance;
selectFixture.detectChanges();
const optionToSelect = testComponent.optionComponent;
const optionSelectSpy = spyOn(optionToSelect, 'select').and.callThrough();
expect(optionToSelect.selected).toEqual(false);
testComponent.selectedValue = optionToSelect.value;
selectFixture.detectChanges();
// need to call flush because NgModelDirective updates value on
// resolvedPromise.then
flush();
selectFixture.detectChanges();
expect(optionToSelect.selected).toEqual(true);
expect(optionSelectSpy).toHaveBeenCalledTimes(1);
}));
it('should unselect previously selected option', fakeAsync(() => {
const selectFixture = TestBed.createComponent(NbSelectTestComponent);
const testSelectComponent = selectFixture.componentInstance;
testSelectComponent.selected = TEST_GROUPS[0].options[0].value;
selectFixture.detectChanges();
flush();
selectFixture.detectChanges();
const selectedOption: NbOptionComponent = testSelectComponent.options.find(o => o.selected);
const selectionChangeSpy = createSpy('selectionChangeSpy');
selectedOption.selectionChange.subscribe(selectionChangeSpy);
testSelectComponent.selected = TEST_GROUPS[0].options[1].value;
selectFixture.detectChanges();
expect(selectionChangeSpy).toHaveBeenCalledTimes(1);
expect(selectedOption.selected).toEqual(false);
}));
it('should not deselect option if option stays selected', fakeAsync(() => {
const selectFixture = TestBed.createComponent(NbSelectTestComponent);
const testSelectComponent = selectFixture.componentInstance;
testSelectComponent.selected = TEST_GROUPS[0].options[0].value;
selectFixture.detectChanges();
flush();
selectFixture.detectChanges();
const selectedOption: NbOptionComponent = testSelectComponent.options.find(o => o.selected);
const selectionChangeSpy = spyOn(selectedOption, 'deselect');
testSelectComponent.selected = selectedOption.value;
selectFixture.detectChanges();
expect(selectionChangeSpy).not.toHaveBeenCalled();
}));
it(`should not call dispose on uninitialized resources`, () => {
const selectFixture = new NbSelectComponent(null, null, null, null, null, null, null, null, null, null, null);
expect(() => selectFixture.ngOnDestroy()).not.toThrow();
});
it(`should has 'empty' class when has no placeholder and text`, () => {
const selectFixture = TestBed.createComponent(NbSelectComponent);
selectFixture.detectChanges();
const button = selectFixture.debugElement.query(By.css('button'));
expect(button.classes['empty']).toEqual(true);
});
it(`should set overlay width same as button inside select`, () => {
const selectFixture = TestBed.createComponent(NbSelectComponent);
const selectComponent = selectFixture.componentInstance;
selectFixture.detectChanges();
const selectElement: HTMLElement = selectFixture.nativeElement;
const buttonElement: HTMLElement = selectElement.querySelector('button');
selectElement.style.padding = '1px';
expect(selectComponent.hostWidth).not.toEqual(selectElement.offsetWidth);
expect(selectComponent.hostWidth).toEqual(buttonElement.offsetWidth);
});
it('should not open when disabled and button clicked', fakeAsync(() => {
const selectFixture = TestBed.createComponent(NbSelectComponent);
selectFixture.componentInstance.disabled = true;
selectFixture.detectChanges();
const selectButton: HTMLElement = selectFixture.debugElement.query(By.css('button')).nativeElement;
selectButton.click();
flush();
fixture.detectChanges();
expect(selectFixture.componentInstance.isOpen).toBeFalsy();
}));
it('should not open when disabled and toggle icon clicked', fakeAsync(() => {
const selectFixture = TestBed.createComponent(NbSelectComponent);
selectFixture.componentInstance.disabled = true;
selectFixture.detectChanges();
const selectToggleIcon: HTMLElement = selectFixture.debugElement.query(By.css('nb-icon')).nativeElement;
selectToggleIcon.click();
flush();
fixture.detectChanges();
expect(selectFixture.componentInstance.isOpen).toBeFalsy();
}));
it('should mark touched when select button loose focus and select closed', fakeAsync(() => {
const touchedSpy = jasmine.createSpy('touched spy');
const selectFixture = TestBed.createComponent(NbSelectComponent);
const selectComponent: NbSelectComponent = selectFixture.componentInstance;
selectFixture.detectChanges();
flush();
selectComponent.registerOnTouched(touchedSpy);
selectFixture.debugElement.query(By.css('.select-button')).triggerEventHandler('blur', {});
expect(touchedSpy).toHaveBeenCalledTimes(1);
}));
it('should not mark touched when select button loose focus and select open', fakeAsync(() => {
const touchedSpy = jasmine.createSpy('touched spy');
const selectFixture = TestBed.createComponent(NbSelectComponent);
select = selectFixture.componentInstance as NbSelectComponent;
selectFixture.detectChanges();
flush();
select.registerOnTouched(touchedSpy);
select.show();
selectFixture.debugElement.query(By.css('.select-button')).triggerEventHandler('blur', {});
expect(touchedSpy).not.toHaveBeenCalled();
}));
});
describe('NbSelectComponent - falsy values', () => {
let fixture: ComponentFixture;
let testComponent: NbSelectWithFalsyOptionValuesComponent;
let select: NbSelectComponent;
beforeEach(fakeAsync(() => {
TestBed.configureTestingModule({
imports: [
RouterTestingModule.withRoutes([]),
NbThemeModule.forRoot(),
NbLayoutModule,
NbSelectModule,
],
declarations: [
NbSelectWithFalsyOptionValuesComponent,
NbMultipleSelectWithFalsyOptionValuesComponent,
],
});
fixture = TestBed.createComponent(NbSelectWithFalsyOptionValuesComponent);
testComponent = fixture.componentInstance;
select = fixture.debugElement.query(By.directive(NbSelectComponent)).componentInstance;
fixture.detectChanges();
flush();
}));
it('should clean selection when selected option does not have a value', fakeAsync(() => {
select.selected = testComponent.truthyOption.value;
fixture.detectChanges();
testComponent.noValueOption.onClick(eventMock);
fixture.detectChanges();
expect(select.selectionModel.length).toEqual(0);
}));
it('should clean selection when selected option has null value', fakeAsync(() => {
select.selected = testComponent.truthyOption.value;
fixture.detectChanges();
testComponent.nullOption.onClick(eventMock);
fixture.detectChanges();
expect(select.selectionModel.length).toEqual(0);
}));
it('should clean selection when selected option has undefined value', fakeAsync(() => {
select.selected = testComponent.truthyOption.value;
fixture.detectChanges();
testComponent.undefinedOption.onClick(eventMock);
fixture.detectChanges();
expect(select.selectionModel.length).toEqual(0);
}));
it('should not reset selection when selected option has false value', fakeAsync(() => {
select.selected = testComponent.truthyOption.value;
fixture.detectChanges();
testComponent.falseOption.onClick(eventMock);
fixture.detectChanges();
expect(select.selectionModel.length).toEqual(1);
}));
it('should not reset selection when selected option has zero value', fakeAsync(() => {
select.selected = testComponent.truthyOption.value;
fixture.detectChanges();
testComponent.zeroOption.onClick(eventMock);
fixture.detectChanges();
expect(select.selectionModel.length).toEqual(1);
}));
it('should not reset selection when selected option has empty string value', fakeAsync(() => {
select.selected = testComponent.truthyOption.value;
fixture.detectChanges();
testComponent.emptyStringOption.onClick(eventMock);
fixture.detectChanges();
expect(select.selectionModel.length).toEqual(1);
}));
it('should not reset selection when selected option has NaN value', fakeAsync(() => {
select.selected = testComponent.truthyOption.value;
fixture.detectChanges();
testComponent.nanOption.onClick(eventMock);
fixture.detectChanges();
expect(select.selectionModel.length).toEqual(1);
}));
it('should set class if fullWidth input set to true', () => {
select.fullWidth = true;
fixture.detectChanges();
const button = fixture.debugElement.query(By.directive(NbSelectComponent));
expect(button.classes['full-width']).toEqual(true);
});
describe('multiple', () => {
beforeEach(fakeAsync(() => {
fixture = TestBed.createComponent(NbMultipleSelectWithFalsyOptionValuesComponent);
testComponent = fixture.componentInstance;
select = fixture.debugElement.query(By.directive(NbSelectComponent)).componentInstance;
fixture.detectChanges();
flush();
select.show();
fixture.detectChanges();
}));
it('should not render checkbox on options with reset values', () => {
expect(testComponent.noValueOptionElement.nativeElement.querySelector('nb-checkbox')).toEqual(null);
expect(testComponent.nullOptionElement.nativeElement.querySelector('nb-checkbox')).toEqual(null);
expect(testComponent.undefinedOptionElement.nativeElement.querySelector('nb-checkbox')).toEqual(null);
});
it('should render checkbox on options with falsy non-reset values', () => {
expect(testComponent.falseOptionElement.nativeElement.querySelector('nb-checkbox')).not.toEqual(null);
expect(testComponent.zeroOptionElement.nativeElement.querySelector('nb-checkbox')).not.toEqual(null);
expect(testComponent.emptyStringOptionElement.nativeElement.querySelector('nb-checkbox')).not.toEqual(null);
expect(testComponent.nanOptionElement.nativeElement.querySelector('nb-checkbox')).not.toEqual(null);
expect(testComponent.truthyOptionElement.nativeElement.querySelector('nb-checkbox')).not.toEqual(null);
});
});
it('should select initial falsy value', fakeAsync(() => {
fixture = TestBed.createComponent(NbSelectWithFalsyOptionValuesComponent);
testComponent = fixture.componentInstance;
select = fixture.debugElement.query(By.directive(NbSelectComponent)).componentInstance;
select.selected = '';
fixture.detectChanges();
flush();
expect(select.selectionModel[0]).toEqual(testComponent.emptyStringOption);
expect(testComponent.emptyStringOption.selected).toEqual(true);
}));
});
describe('NbSelectComponent - Triggers', () => {
let fixture: ComponentFixture;
let selectComponent: NbSelectComponent;
let triggerBuilderStub;
let showTriggerStub: Subject;
let hideTriggerStub: Subject;
beforeEach(fakeAsync(() => {
showTriggerStub = new Subject();
hideTriggerStub = new Subject();
triggerBuilderStub = {
trigger() { return this },
host() { return this },
container() { return this },
build() {
return { show$: showTriggerStub, hide$: hideTriggerStub, destroy() {} };
},
};
TestBed.configureTestingModule({
imports: [ RouterTestingModule.withRoutes([]), NbThemeModule.forRoot(), NbLayoutModule, NbSelectModule ],
declarations: [ BasicSelectTestComponent ],
});
TestBed.overrideProvider(NbTriggerStrategyBuilderService, { useValue: triggerBuilderStub });
fixture = TestBed.createComponent(BasicSelectTestComponent);
fixture.detectChanges();
flush();
selectComponent = fixture.debugElement.query(By.directive(NbSelectComponent)).componentInstance;
}));
it('should mark touched if clicked outside of overlay and select', fakeAsync(() => {
const touchedSpy = jasmine.createSpy('touched spy');
selectComponent.registerOnTouched(touchedSpy);
const elementOutsideSelect = fixture.debugElement.query(By.css('nb-layout')).nativeElement;
selectComponent.show();
fixture.detectChanges();
hideTriggerStub.next({ target: elementOutsideSelect } as unknown as Event);
expect(touchedSpy).toHaveBeenCalledTimes(1);
}));
it('should not mark touched if clicked on the select button', fakeAsync(() => {
const touchedSpy = jasmine.createSpy('touched spy');
selectComponent.registerOnTouched(touchedSpy);
const selectButton = fixture.debugElement.query(By.css('.select-button')).nativeElement;
selectComponent.show();
fixture.detectChanges();
hideTriggerStub.next({ target: selectButton } as unknown as Event);
expect(touchedSpy).not.toHaveBeenCalled();
}));
});
describe('NbSelectComponent - Key manager', () => {
let fixture: ComponentFixture;
let selectComponent: NbSelectComponent;
let tabOutStub: Subject;
let keyManagerFactoryStub;
let keyManagerStub;
beforeEach(fakeAsync(() => {
tabOutStub = new Subject();
keyManagerStub = {
withTypeAhead() { return this; },
setActiveItem() {},
setFirstItemActive() {},
onKeydown() {},
tabOut: tabOutStub,
};
keyManagerFactoryStub = { create() { return keyManagerStub; } };
TestBed.configureTestingModule({
imports: [ RouterTestingModule.withRoutes([]), NbThemeModule.forRoot(), NbLayoutModule, NbSelectModule ],
declarations: [ BasicSelectTestComponent ],
});
TestBed.overrideProvider(NbFocusKeyManagerFactoryService, { useValue: keyManagerFactoryStub });
fixture = TestBed.createComponent(BasicSelectTestComponent);
fixture.detectChanges();
flush();
selectComponent = fixture.debugElement.query(By.directive(NbSelectComponent)).componentInstance;
}));
it('should mark touched when tabbing out from options list', fakeAsync(() => {
selectComponent.show();
fixture.detectChanges();
const touchedSpy = jasmine.createSpy('touched spy');
selectComponent.registerOnTouched(touchedSpy);
tabOutStub.next();
flush();
expect(touchedSpy).toHaveBeenCalledTimes(1);
}));
});
describe('NbOptionComponent', () => {
let fixture: ComponentFixture;
let testSelectComponent: NbReactiveFormSelectComponent;
let option: NbOptionComponent;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
RouterTestingModule.withRoutes([]),
FormsModule,
ReactiveFormsModule,
NbThemeModule.forRoot(),
NbLayoutModule,
NbSelectModule,
],
declarations: [
NbNgModelSelectComponent,
NbSelectTestComponent,
NbReactiveFormSelectComponent,
],
});
fixture = TestBed.createComponent(NbReactiveFormSelectComponent);
testSelectComponent = fixture.componentInstance;
fixture.detectChanges();
option = testSelectComponent.optionComponents.first;
});
it('should ignore selection change if destroyed', fakeAsync(() => {
const selectionChangeSpy = createSpy('selectionChangeSpy');
option.selectionChange.subscribe(selectionChangeSpy);
expect(option.selected).toEqual(false);
testSelectComponent.showSelect = false;
fixture.detectChanges();
expect((option as any).alive).toEqual(false);
option.select();
expect(option.selected).toEqual(false);
expect(selectionChangeSpy).not.toHaveBeenCalled();
}));
it('should not emit selection change when already selected', fakeAsync(() => {
option.select();
fixture.detectChanges();
expect(option.selected).toEqual(true);
const selectionChangeSpy = createSpy('selectionChangeSpy');
option.selectionChange.subscribe(selectionChangeSpy);
option.select();
expect(option.selected).toEqual(true);
expect(selectionChangeSpy).not.toHaveBeenCalled();
}));
it('should emit selection change when deselected', fakeAsync(() => {
option.select();
fixture.detectChanges();
expect(option.selected).toEqual(true);
const selectionChangeSpy = createSpy('selectionChangeSpy');
option.selectionChange.subscribe(selectionChangeSpy);
option.deselect();
expect(option.selected).toEqual(false);
expect(selectionChangeSpy).toHaveBeenCalledTimes(1);
}));
});
describe('NbOptionComponent disabled', () => {
let fixture: ComponentFixture;
let testComponent: NbOptionDisabledTestComponent;
let selectComponent: NbSelectComponent;
let optionGroupComponent: NbOptionGroupComponent;
let optionComponent: NbOptionComponent;
beforeEach(fakeAsync(() => {
TestBed.configureTestingModule({
imports: [
RouterTestingModule.withRoutes([]),
NbThemeModule.forRoot(),
NbLayoutModule,
NbSelectModule,
],
declarations: [ NbOptionDisabledTestComponent ],
});
fixture = TestBed.createComponent(NbOptionDisabledTestComponent);
testComponent = fixture.componentInstance;
fixture.detectChanges();
flush();
selectComponent = testComponent.selectComponent;
optionGroupComponent = testComponent.optionGroupComponent;
optionComponent = testComponent.optionComponent;
}));
it('should has disabled attribute if disabled set to true', () => {
selectComponent.show();
testComponent.optionDisabled = true;
fixture.detectChanges();
const option = fixture.debugElement.query(By.directive(NbOptionComponent));
expect(option.attributes.disabled).toEqual('');
});
it('should has disabled attribute if group disabled set to true', fakeAsync(() => {
selectComponent.show();
testComponent.optionGroupDisabled = true;
fixture.detectChanges();
flush();
fixture.detectChanges();
const option = fixture.debugElement.query(By.directive(NbOptionComponent));
expect(option.attributes.disabled).toEqual('');
}));
});
describe('NbSelect - dynamic options', () => {
let fixture: ComponentFixture;
let testComponent: NbReactiveFormSelectComponent;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
RouterTestingModule.withRoutes([]),
FormsModule,
ReactiveFormsModule,
NbThemeModule.forRoot(),
NbLayoutModule,
NbSelectModule,
],
declarations: [ NbReactiveFormSelectComponent ],
});
fixture = TestBed.createComponent(NbReactiveFormSelectComponent);
testComponent = fixture.componentInstance;
});
describe('Set value from queue', () => {
let selectComponent: NbSelectComponent;
beforeEach(() => {
// Force select to cache the value as there is no options to select.
testComponent.options = [];
testComponent.formControl = new FormControl(1);
fixture.detectChanges();
selectComponent = fixture.debugElement
.query(By.directive(NbSelectComponent)).componentInstance;
});
it('should set value from queue when options added dynamically (after change detection run)', fakeAsync(() => {
expect(selectComponent.selectionModel.length).toEqual(0);
testComponent.options = [1];
fixture.detectChanges();
flush();
fixture.detectChanges();
expect(selectComponent.selectionModel[0]).toEqual(testComponent.optionComponents.first);
}));
it('should set value from queue when options change', fakeAsync(() => {
testComponent.options = [0];
fixture.detectChanges();
flush();
fixture.detectChanges();
expect(selectComponent.selectionModel.length).toEqual(0);
testComponent.options.push(1);
fixture.detectChanges();
flush();
fixture.detectChanges();
expect(selectComponent.selectionModel[0]).toEqual(testComponent.optionComponents.last);
}));
});
describe('Clear queue after value set', () => {
/*
We can ensure queue is clean by spying on `writeValue` calls on select. It will be called only if options
change and queue has a value.
*/
it('should clear queue after option selected by click', fakeAsync(() => {
testComponent.options = [];
testComponent.formControl = new FormControl(1);
fixture.detectChanges();
const selectComponent: NbSelectComponent = fixture.debugElement
.query(By.directive(NbSelectComponent)).componentInstance;
testComponent.options = [0];
fixture.detectChanges();
flush();
fixture.detectChanges();
testComponent.optionComponents.first.onClick({ preventDefault() {} } as Event);
fixture.detectChanges();
const writeValueSpy = spyOn(selectComponent, 'writeValue').and.callThrough();
testComponent.options.push(1);
fixture.detectChanges();
flush();
expect(writeValueSpy).not.toHaveBeenCalled();
}));
it(`should clear queue after option selected via 'selected' input`, fakeAsync(() => {
testComponent.options = [];
testComponent.formControl = new FormControl(1);
fixture.detectChanges();
const selectComponent: NbSelectComponent = fixture.debugElement
.query(By.directive(NbSelectComponent)).componentInstance;
testComponent.options = [0];
fixture.detectChanges();
flush();
fixture.detectChanges();
selectComponent.selected = 0;
fixture.detectChanges();
const writeValueSpy = spyOn(selectComponent, 'writeValue').and.callThrough();
testComponent.options.push(1);
fixture.detectChanges();
flush();
expect(writeValueSpy).not.toHaveBeenCalled();
}));
it('should clear queue after options change and selection model change', fakeAsync(() => {
testComponent.options = [];
testComponent.formControl = new FormControl(1);
fixture.detectChanges();
const selectComponent: NbSelectComponent = fixture.debugElement
.query(By.directive(NbSelectComponent)).componentInstance;
const writeValueSpy = spyOn(selectComponent, 'writeValue').and.callThrough();
testComponent.options = [1];
fixture.detectChanges();
flush();
fixture.detectChanges();
expect(writeValueSpy).toHaveBeenCalledTimes(1);
testComponent.options.push(2);
fixture.detectChanges();
flush();
fixture.detectChanges();
expect(writeValueSpy).toHaveBeenCalledTimes(1);
}));
it('should not clear queue after options change and selection model is empty', fakeAsync(() => {
testComponent.options = [];
testComponent.formControl = new FormControl(2);
fixture.detectChanges();
const selectComponent: NbSelectComponent = fixture.debugElement
.query(By.directive(NbSelectComponent)).componentInstance;
const writeValueSpy = spyOn(selectComponent, 'writeValue').and.callThrough();
testComponent.options = [0];
fixture.detectChanges();
flush();
fixture.detectChanges();
expect(writeValueSpy).toHaveBeenCalledTimes(1);
testComponent.options.push(1);
fixture.detectChanges();
flush();
fixture.detectChanges();
expect(writeValueSpy).toHaveBeenCalledTimes(2);
}));
});
});