igniteui-angular-sovn
Version:
Ignite UI for Angular is a dependency-free Angular toolkit for building modern web apps
991 lines (895 loc) • 72.2 kB
text/typescript
import { Component, ViewChild, OnInit, ElementRef, ViewChildren, QueryList } from '@angular/core';
import { fakeAsync, TestBed, tick, waitForAsync } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { IgxToggleActionDirective, IgxToggleDirective } from '../directives/toggle/toggle.directive';
import { IgxDropDownItemComponent } from './drop-down-item.component';
import { IgxDropDownComponent, IgxDropDownItemNavigationDirective } from './public_api';
import { ISelectionEventArgs } from './drop-down.common';
import { IgxTabContentComponent, IgxTabHeaderComponent, IgxTabItemComponent, IgxTabsComponent } from '../tabs/tabs/public_api';
import { UIInteractions, wait } from '../test-utils/ui-interactions.spec';
import { CancelableEventArgs, IBaseCancelableBrowserEventArgs } from '../core/utils';
import { configureTestSuite } from '../test-utils/configure-suite';
import { take } from 'rxjs/operators';
import { IgxDropDownGroupComponent } from './drop-down-group.component';
import { IgxForOfDirective } from '../directives/for-of/for_of.directive';
import { IgxDropDownItemBaseDirective } from './drop-down-item.base';
import { DisplayDensity } from '../core/density';
import { IgxSelectionAPIService } from '../core/selection';
import { IgxButtonDirective } from '../directives/button/button.directive';
import { NgFor } from '@angular/common';
const CSS_CLASS_DROP_DOWN_BASE = 'igx-drop-down';
const CSS_CLASS_LIST = 'igx-drop-down__list';
const CSS_CLASS_SCROLL = 'igx-drop-down__list-scroll';
const CSS_CLASS_ITEM = 'igx-drop-down__item';
const CSS_CLASS_INNER_SPAN = 'igx-drop-down__inner';
const CSS_CLASS_GROUP_ITEM = 'igx-drop-down__group';
const CSS_CLASS_ITEM_COSY = 'igx-drop-down__item--cosy';
const CSS_CLASS_ITEM_COMPACT = 'igx-drop-down__item--compact';
const CSS_CLASS_FOCUSED = 'igx-drop-down__item--focused';
const CSS_CLASS_SELECTED = 'igx-drop-down__item--selected';
const CSS_CLASS_DISABLED = 'igx-drop-down__item--disabled';
const CSS_CLASS_HEADER = 'igx-drop-down__header';
const CSS_CLASS_HEADER_COSY = 'igx-drop-down__header--cosy';
const CSS_CLASS_HEADER_COMPACT = 'igx-drop-down__header--compact';
const CSS_CLASS_TABS = '.igx-tabs__header-item';
describe('IgxDropDown ', () => {
let fixture;
let dropdown: IgxDropDownComponent;
describe('Unit tests', () => {
const data = [
{ value: 'Item0', index: 0 } as IgxDropDownItemComponent,
{ value: 'Item1', index: 1 } as IgxDropDownItemComponent,
{ value: 'Item2', index: 2 } as IgxDropDownItemComponent,
{ value: 'Item3', index: 3 } as IgxDropDownItemComponent,
{ value: 'Item4', index: 4 } as IgxDropDownItemComponent,
{ value: 'Item5', index: 5 } as IgxDropDownItemComponent];
const mockSelection: {
[key: string]: jasmine.Spy;
} = jasmine.createSpyObj('IgxSelectionAPIService', ['get', 'set', 'add_items', 'select_items']);
const mockCdr = jasmine.createSpyObj('ChangeDetectorRef', ['markForCheck', 'detectChanges']);
mockSelection.get.and.returnValue(new Set([]));
const mockForOf = jasmine.createSpyObj('IgxForOfDirective', ['totalItemCount']);
it('should notify when selection has changed', () => {
const selectionService = new IgxSelectionAPIService();
dropdown = new IgxDropDownComponent({ nativeElement: null }, mockCdr, selectionService, null);
dropdown.ngOnInit();
(dropdown as any).virtDir = mockForOf;
spyOnProperty(dropdown, 'items', 'get').and.returnValue(data);
spyOn(dropdown.selectionChanging, 'emit').and.callThrough();
dropdown.selectItem(data[0]);
expect(dropdown.selectedItem).toEqual(data[0]);
expect(dropdown.selectionChanging.emit).toHaveBeenCalledTimes(1);
dropdown.selectItem(data[4]);
expect(dropdown.selectedItem).toEqual(data[4]);
expect(dropdown.selectionChanging.emit).toHaveBeenCalledTimes(2);
});
it('should fire selectionChanging with correct args', () => {
const selectionService = new IgxSelectionAPIService();
dropdown = new IgxDropDownComponent({ nativeElement: null }, mockCdr, selectionService, null);
dropdown.ngOnInit();
(dropdown as any).virtDir = mockForOf;
spyOnProperty(dropdown, 'items', 'get').and.returnValue(data);
spyOn(dropdown.selectionChanging, 'emit').and.callThrough();
const selectionArgs: ISelectionEventArgs = {
newSelection: dropdown.items[1],
oldSelection: null,
cancel: false,
owner: dropdown
};
dropdown.selectItem(data[1]);
expect(dropdown.selectionChanging.emit).toHaveBeenCalledWith(selectionArgs);
const newSelectionArgs: ISelectionEventArgs = {
newSelection: dropdown.items[4],
oldSelection: dropdown.items[1],
cancel: false,
owner: dropdown
};
dropdown.selectItem(data[4]);
expect(dropdown.selectionChanging.emit).toHaveBeenCalledWith(newSelectionArgs);
});
it('should notify when selection is cleared', () => {
const selectionService = new IgxSelectionAPIService();
dropdown = new IgxDropDownComponent({ nativeElement: null }, mockCdr, selectionService, null);
dropdown.ngOnInit();
(dropdown as any).virtDir = mockForOf;
spyOnProperty(dropdown, 'items', 'get').and.returnValue(data);
spyOn(dropdown.selectionChanging, 'emit').and.callThrough();
spyOn(dropdown.closed, 'emit').and.callThrough();
dropdown.selectItem(data[1]);
const selected = dropdown.selectedItem;
expect(dropdown.selectedItem).toEqual(data[1]);
expect(dropdown.selectionChanging.emit).toHaveBeenCalledTimes(1);
let args: ISelectionEventArgs = {
oldSelection: null,
newSelection: data[1],
cancel: false,
owner: dropdown
};
expect(dropdown.selectionChanging.emit).toHaveBeenCalledWith(args);
dropdown.clearSelection();
expect(dropdown.selectedItem).toBeNull();
expect(dropdown.selectionChanging.emit).toHaveBeenCalledTimes(2);
args = {
oldSelection: selected,
newSelection: null,
cancel: false,
owner: dropdown
};
expect(dropdown.selectionChanging.emit).toHaveBeenCalledWith(args);
});
it('setSelectedItem should return selected item', () => {
const selectionService = new IgxSelectionAPIService();
dropdown = new IgxDropDownComponent({ nativeElement: null }, mockCdr, selectionService, null);
dropdown.ngOnInit();
(dropdown as any).virtDir = mockForOf;
(dropdown as any).virtDir.igxForOf = data;
spyOnProperty(dropdown, 'items', 'get').and.returnValue(data);
expect(dropdown.selectedItem).toBeNull();
dropdown.setSelectedItem(3);
const selectedItem = dropdown.selectedItem;
expect(selectedItem).toBeTruthy();
expect(selectedItem.index).toEqual(3);
});
it('setSelectedItem should return null when selection is cleared', () => {
const selectionService = new IgxSelectionAPIService();
dropdown = new IgxDropDownComponent({ nativeElement: null }, mockCdr, selectionService, null);
dropdown.ngOnInit();
(dropdown as any).virtDir = mockForOf;
(dropdown as any).virtDir.igxForOf = data;
spyOnProperty(dropdown, 'items', 'get').and.returnValue(data);
dropdown.setSelectedItem(3);
expect(dropdown.selectedItem).toBeTruthy();
expect(dropdown.selectedItem.index).toEqual(3);
dropdown.clearSelection();
expect(dropdown.selectedItem).toBeNull();
});
it('toggle should call open method when dropdown is collapsed', () => {
const selectionService = new IgxSelectionAPIService();
dropdown = new IgxDropDownComponent({ nativeElement: null }, mockCdr, selectionService, null);
dropdown.ngOnInit();
(dropdown as any).virtDir = mockForOf;
spyOnProperty(dropdown, 'items', 'get').and.returnValue(data);
spyOnProperty(dropdown, 'collapsed', 'get').and.returnValue(true);
spyOn(dropdown, 'open');
dropdown.toggle();
expect(dropdown.open).toHaveBeenCalledTimes(1);
});
it('toggle should call close method when dropdown is opened', () => {
const selectionService = new IgxSelectionAPIService();
dropdown = new IgxDropDownComponent({ nativeElement: null }, mockCdr, selectionService, null);
dropdown.ngOnInit();
(dropdown as any).virtDir = mockForOf;
const mockToggle = jasmine.createSpyObj('IgxToggleDirective', ['open']);
mockToggle.isClosing = false;
(dropdown as any).toggleDirective = mockToggle;
spyOnProperty(dropdown, 'items', 'get').and.returnValue(data);
spyOnProperty(dropdown, 'collapsed', 'get').and.returnValue(false);
spyOn(dropdown, 'close');
dropdown.toggle();
expect(dropdown.close).toHaveBeenCalledTimes(1);
});
});
describe('User interaction tests', () => {
describe('Selection & key navigation', () => {
configureTestSuite();
beforeAll(waitForAsync(() => {
TestBed.configureTestingModule({
imports: [
NoopAnimationsModule,
IgxDropDownTestComponent
]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(IgxDropDownTestComponent);
fixture.detectChanges();
dropdown = fixture.componentInstance.dropdown;
});
it('should toggle drop down on open/close methods call', fakeAsync(() => {
spyOn(dropdown, 'onToggleOpening');
spyOn(dropdown, 'onToggleOpened');
spyOn(dropdown, 'onToggleClosing');
spyOn(dropdown, 'onToggleClosed');
expect(dropdown.collapsed).toBeTruthy();
dropdown.open();
tick();
fixture.detectChanges();
expect(dropdown.collapsed).toBeFalsy();
expect(dropdown.onToggleOpening).toHaveBeenCalledTimes(1);
expect(dropdown.onToggleOpened).toHaveBeenCalledTimes(1);
dropdown.close();
tick();
fixture.detectChanges();
expect(dropdown.collapsed).toBeTruthy();
expect(dropdown.onToggleClosing).toHaveBeenCalledTimes(1);
expect(dropdown.onToggleClosed).toHaveBeenCalledTimes(1);
}));
it('#3810 - should not emit events when calling open on opened dropdown', fakeAsync(() => {
spyOn(dropdown.opening, 'emit').and.callThrough();
spyOn(dropdown.opened, 'emit').and.callThrough();
dropdown.open();
tick();
fixture.detectChanges();
expect(dropdown.opening.emit).toHaveBeenCalledTimes(1);
expect(dropdown.opened.emit).toHaveBeenCalledTimes(1);
dropdown.open();
tick();
fixture.detectChanges();
expect(dropdown.opening.emit).toHaveBeenCalledTimes(1);
expect(dropdown.opened.emit).toHaveBeenCalledTimes(1);
}));
it('#2798 - should allow canceling of open/close through opening/closing events', fakeAsync(() => {
const toggle: IgxToggleDirective = (dropdown as any).toggleDirective;
const onOpeningSpy = spyOn(dropdown.opening, 'emit').and.callThrough();
const onOpenedSpy = spyOn(dropdown.opened, 'emit').and.callThrough();
spyOn(dropdown.closing, 'emit').and.callThrough();
spyOn(dropdown.closed, 'emit').and.callThrough();
dropdown.closing.pipe(take(1)).subscribe((e: CancelableEventArgs) => e.cancel = true);
dropdown.toggle();
tick();
fixture.detectChanges();
expect(dropdown.opening.emit).toHaveBeenCalledTimes(1);
expect(dropdown.opened.emit).toHaveBeenCalledTimes(1);
dropdown.toggle();
tick();
fixture.detectChanges();
expect(dropdown.closing.emit).toHaveBeenCalledTimes(1);
expect(dropdown.closed.emit).toHaveBeenCalledTimes(0);
toggle.close();
fixture.detectChanges();
onOpeningSpy.calls.reset();
onOpenedSpy.calls.reset();
dropdown.opening.pipe(take(1)).subscribe((e: CancelableEventArgs) => e.cancel = true);
dropdown.toggle();
tick();
fixture.detectChanges();
expect(dropdown.opening.emit).toHaveBeenCalledTimes(1);
expect(dropdown.opened.emit).toHaveBeenCalledTimes(0);
}));
it('should select item by SPACE/ENTER keys', fakeAsync(() => {
dropdown.toggle();
tick();
fixture.detectChanges();
let focusedItem = fixture.debugElement.query(By.css(`.${CSS_CLASS_FOCUSED}`));
expect(focusedItem.componentInstance.itemIndex).toEqual(0);
expect(dropdown.collapsed).toEqual(false);
let dropdownElement = fixture.debugElement.query(By.css(`.${CSS_CLASS_DROP_DOWN_BASE}`));
UIInteractions.triggerEventHandlerKeyDown('ArrowDown', dropdownElement);
tick();
fixture.detectChanges();
focusedItem = fixture.debugElement.query(By.css(`.${CSS_CLASS_FOCUSED}`));
expect(focusedItem).toBeDefined();
expect(focusedItem.componentInstance.itemIndex).toEqual(1);
expect(dropdown.selectedItem).toBeFalsy();
UIInteractions.triggerEventHandlerKeyDown('Space', dropdownElement);
tick();
fixture.detectChanges();
expect(dropdown.selectedItem).toEqual(dropdown.items[1]);
expect(dropdown.collapsed).toEqual(true);
dropdown.toggle();
tick();
fixture.detectChanges();
dropdownElement = fixture.debugElement.query(By.css(`.${CSS_CLASS_DROP_DOWN_BASE}`));
focusedItem = fixture.debugElement.query(By.css(`.${CSS_CLASS_FOCUSED}`));
UIInteractions.triggerEventHandlerKeyDown('ArrowDown', dropdownElement);
tick();
fixture.detectChanges();
UIInteractions.triggerEventHandlerKeyDown('Enter', dropdownElement);
tick();
fixture.detectChanges();
expect(dropdown.collapsed).toEqual(true);
expect(dropdown.selectedItem).toEqual(dropdown.items[2]);
dropdown.toggle();
tick();
fixture.detectChanges();
expect(dropdown.collapsed).toEqual(false);
}));
it('should close the dropdown and not change selection by pressing ESC key', fakeAsync(() => {
spyOn(dropdown.selectionChanging, 'emit').and.callThrough();
spyOn(dropdown.opening, 'emit').and.callThrough();
spyOn(dropdown.opened, 'emit').and.callThrough();
spyOn(dropdown.closing, 'emit').and.callThrough();
spyOn(dropdown.closed, 'emit').and.callThrough();
dropdown.toggle();
tick();
fixture.detectChanges();
let focusedItem = fixture.debugElement.query(By.css(`.${CSS_CLASS_FOCUSED}`));
expect(focusedItem).toBeDefined();
const dropdownElement = fixture.debugElement.query(By.css(`.${CSS_CLASS_DROP_DOWN_BASE}`));
UIInteractions.triggerEventHandlerKeyDown('ArrowDown', dropdownElement);
fixture.detectChanges();
focusedItem = fixture.debugElement.query(By.css(`.${CSS_CLASS_FOCUSED}`));
expect(focusedItem).toBeDefined();
expect(focusedItem.componentInstance.itemIndex).toEqual(1);
UIInteractions.triggerEventHandlerKeyDown('Escape', dropdownElement);
tick();
fixture.detectChanges();
expect(dropdown.collapsed).toEqual(true);
expect(dropdown.opening.emit).toHaveBeenCalledTimes(1);
expect(dropdown.opened.emit).toHaveBeenCalledTimes(1);
expect(dropdown.selectionChanging.emit).toHaveBeenCalledTimes(0);
expect(dropdown.closing.emit).toHaveBeenCalledTimes(1);
expect(dropdown.closed.emit).toHaveBeenCalledTimes(1);
}));
it('should navigate through items using Up/Down/Home/End keys', fakeAsync(() => {
dropdown.toggle();
tick();
fixture.detectChanges();
const dropdownElement = fixture.debugElement.query(By.css(`.${CSS_CLASS_DROP_DOWN_BASE}`));
dropdownElement.triggerEventHandler('keydown', UIInteractions.getKeyboardEvent('keydown', 'ArrowDown'));
tick();
fixture.detectChanges();
let focusedItem = fixture.debugElement.query(By.css(`.${CSS_CLASS_FOCUSED}`));
expect(focusedItem.componentInstance.itemIndex).toEqual(1);
UIInteractions.triggerEventHandlerKeyDown('End', dropdownElement);
tick();
fixture.detectChanges();
focusedItem = fixture.debugElement.query(By.css(`.${CSS_CLASS_FOCUSED}`));
expect(focusedItem.componentInstance.itemIndex).toEqual(14);
UIInteractions.triggerEventHandlerKeyDown('ArrowUp', dropdownElement);
tick();
fixture.detectChanges();
focusedItem = fixture.debugElement.query(By.css(`.${CSS_CLASS_FOCUSED}`));
expect(focusedItem.componentInstance.itemIndex).toEqual(13);
UIInteractions.triggerEventHandlerKeyDown('Home', dropdownElement);
tick();
fixture.detectChanges();
focusedItem = fixture.debugElement.query(By.css(`.${CSS_CLASS_FOCUSED}`));
expect(focusedItem.componentInstance.itemIndex).toEqual(0);
}));
it('should not change selection when setting it to non-existing item', fakeAsync(() => {
dropdown.toggle();
tick();
fixture.detectChanges();
dropdown.setSelectedItem(0);
fixture.detectChanges();
let selectedItem = fixture.debugElement.query(By.css(`.${CSS_CLASS_SELECTED}`));
expect(selectedItem.componentInstance.itemIndex).toEqual(0);
dropdown.setSelectedItem(-4);
fixture.detectChanges();
expect(dropdown.items[0].selected).toBeTruthy();
selectedItem = fixture.debugElement.query(By.css(`.${CSS_CLASS_SELECTED}`));
expect(selectedItem.componentInstance.itemIndex).toEqual(0);
dropdown.setSelectedItem(24);
fixture.detectChanges();
expect(dropdown.items[0].selected).toBeTruthy();
selectedItem = fixture.debugElement.query(By.css(`.${CSS_CLASS_SELECTED}`));
expect(selectedItem.componentInstance.itemIndex).toEqual(0);
dropdown.setSelectedItem(5);
fixture.detectChanges();
expect(dropdown.items[5].selected).toBeTruthy();
selectedItem = fixture.debugElement.query(By.css(`.${CSS_CLASS_SELECTED}`));
expect(selectedItem.componentInstance.itemIndex).toEqual(5);
// Verify selecting the already selected element is not affecting selection
dropdown.setSelectedItem(5);
fixture.detectChanges();
expect(dropdown.items[5].selected).toBeTruthy();
selectedItem = fixture.debugElement.query(By.css(`.${CSS_CLASS_SELECTED}`));
expect(selectedItem.componentInstance.itemIndex).toEqual(5);
}));
it('should focus the first enabled item by pressing HOME key', fakeAsync(() => {
dropdown.items[0].disabled = true;
dropdown.items[1].isHeader = true;
dropdown.items[3].disabled = true;
dropdown.items[4].isHeader = true;
dropdown.items[7].disabled = true;
dropdown.items[10].selected = true;
fixture.detectChanges();
dropdown.toggle();
tick();
fixture.detectChanges();
const selectedItem = fixture.debugElement.query(By.css(`.${CSS_CLASS_SELECTED}`));
expect(selectedItem.componentInstance.itemIndex).toEqual(10);
const dropdownElement = fixture.debugElement.query(By.css(`.${CSS_CLASS_DROP_DOWN_BASE}`));
UIInteractions.triggerEventHandlerKeyDown('Home', dropdownElement);
tick();
fixture.detectChanges();
expect(dropdown.items[1].focused).toBeTruthy();
const focusedItem = fixture.debugElement.query(By.css(`.${CSS_CLASS_FOCUSED}`));
expect(focusedItem.componentInstance.itemIndex).toEqual(1);
}));
it('should set isSelected via igxDropDownIteComponent', fakeAsync(() => {
dropdown.toggle();
tick();
fixture.detectChanges();
expect(dropdown.selectedItem).toBeNull();
const items = dropdown.items as IgxDropDownItemComponent[];
items[2].selected = true;
tick();
fixture.detectChanges();
expect(items[2].selected).toBeTruthy();
expect(dropdown.selectedItem.itemIndex).toEqual(2);
items[1].selected = true;
tick();
fixture.detectChanges();
expect(items[2].selected).toBeFalsy();
expect(items[1].selected).toBeTruthy();
expect(dropdown.selectedItem.itemIndex).toEqual(1);
dropdown.toggle();
tick();
fixture.detectChanges();
expect(dropdown.selectedItem.itemIndex).toEqual(1);
}));
it('should not set isSelected via igxDropDownItemBase on header items', fakeAsync(() => {
dropdown.items[0].disabled = true;
dropdown.items[1].isHeader = true;
dropdown.items[3].disabled = true;
dropdown.items[4].isHeader = true;
dropdown.items[7].disabled = true;
dropdown.items[10].isHeader = true;
fixture.detectChanges();
dropdown.toggle();
tick();
fixture.detectChanges();
expect(dropdown.selectedItem).toBeNull();
const items = dropdown.items as IgxDropDownItemComponent[];
const headerItems = dropdown.headers as IgxDropDownItemComponent[];
// Try to select header item
headerItems[0].selected = true;
tick();
fixture.detectChanges();
expect(headerItems[0].selected).toBeFalsy();
expect(dropdown.selectedItem).toBeNull();
// Try to select disabled item
items[2].selected = true;
tick();
fixture.detectChanges();
expect(items[2].selected).toBeTruthy();
expect(dropdown.selectedItem.itemIndex).toEqual(2);
// Try to select header item
headerItems[1].selected = true;
expect(headerItems[1].selected).toBeFalsy();
expect(dropdown.selectedItem.itemIndex).toEqual(2);
dropdown.toggle();
tick();
fixture.detectChanges();
expect(dropdown.selectedItem.itemIndex).toEqual(2);
}));
it('should return the proper eventArgs if selection has been cancelled', fakeAsync(() => {
spyOn(dropdown.selectionChanging, 'emit').and.callThrough();
dropdown.toggle();
tick();
fixture.detectChanges();
let selectedItem = fixture.debugElement.queryAll(By.css(`.${CSS_CLASS_ITEM}`))[3];
selectedItem.triggerEventHandler('click', UIInteractions.getMouseEvent('click'));
fixture.detectChanges();
const selectionArgs: ISelectionEventArgs = {
oldSelection: null,
newSelection: dropdown.items[3],
cancel: false,
owner: dropdown
};
expect(dropdown.selectionChanging.emit).toHaveBeenCalledWith(selectionArgs);
dropdown.selectionChanging.pipe(take(1)).subscribe((e: CancelableEventArgs) => e.cancel = true);
selectedItem = fixture.debugElement.queryAll(By.css(`.${CSS_CLASS_ITEM}`))[1];
selectedItem.triggerEventHandler('click', UIInteractions.getMouseEvent('click'));
fixture.detectChanges();
const canceledSelectionArgs: ISelectionEventArgs = {
oldSelection: dropdown.items[3],
newSelection: dropdown.items[1],
cancel: true,
owner: dropdown
};
expect(dropdown.selectionChanging.emit).toHaveBeenCalledWith(canceledSelectionArgs);
}));
it('should provide correct event argument when closing through keyboard', fakeAsync(() => {
spyOn(dropdown.closing, 'emit').and.callThrough();
const dropdownElement = fixture.debugElement.query(By.css(`.${CSS_CLASS_DROP_DOWN_BASE}`));
dropdown.toggle();
tick();
fixture.detectChanges();
let focusedItem = fixture.debugElement.query(By.css(`.${CSS_CLASS_FOCUSED}`));
expect(focusedItem).toBeDefined();
let eventArgs: IBaseCancelableBrowserEventArgs;
dropdown.closing.pipe(take(1)).subscribe((args: IBaseCancelableBrowserEventArgs) => {
eventArgs = args;
});
UIInteractions.triggerEventHandlerKeyDown('escape', dropdownElement);
tick();
fixture.detectChanges();
expect(dropdown.closing.emit).toHaveBeenCalledTimes(1);
expect(eventArgs.event).toBeDefined();
expect((eventArgs.event as KeyboardEvent).type).toEqual('keydown');
expect((eventArgs.event as KeyboardEvent).key).toEqual('escape');
dropdown.toggle();
tick();
fixture.detectChanges();
focusedItem = fixture.debugElement.query(By.css(`.${CSS_CLASS_FOCUSED}`));
expect(focusedItem).toBeDefined();
dropdown.closing.pipe(take(1)).subscribe((args: IBaseCancelableBrowserEventArgs) => {
eventArgs = args;
});
UIInteractions.triggerEventHandlerKeyDown('enter', dropdownElement);
tick();
fixture.detectChanges();
expect(dropdown.closing.emit).toHaveBeenCalledTimes(2);
expect(eventArgs.event).toBeDefined();
expect((eventArgs.event as KeyboardEvent).type).toEqual('keydown');
expect((eventArgs.event as KeyboardEvent).key).toEqual('enter');
}));
it('should be able to change selection when manipulating ISelectionEventArgs', fakeAsync(() => {
expect(dropdown.selectedItem).toEqual(null);
dropdown.toggle();
tick();
fixture.detectChanges();
// Overwrite selection args
let expectedSelected = dropdown.items[4];
const calledSelected = dropdown.items[1];
const subscription = dropdown.selectionChanging.subscribe((e: ISelectionEventArgs) => {
expect(e.newSelection).toEqual(calledSelected);
e.newSelection = expectedSelected;
});
dropdown.selectItem(calledSelected);
tick();
expect(dropdown.selectedItem).toEqual(expectedSelected);
// Clear selection
expectedSelected = null;
dropdown.selectItem(calledSelected);
tick();
expect(dropdown.selectedItem).toEqual(expectedSelected);
// Set header - error
dropdown.toggle();
tick();
fixture.detectChanges();
expectedSelected = dropdown.items[4];
dropdown.items[4].isHeader = true;
spyOn(dropdown, 'selectItem').and.callThrough();
expect(() => {
dropdown.selectItem(calledSelected);
}).toThrow();
// Set non-IgxDropDownItemBaseDirective
expectedSelected = 7 as any;
expect(() => {
dropdown.selectItem(calledSelected);
}).toThrow();
subscription.unsubscribe();
}));
it('should not take focus when allowItemsFocus is set to false', fakeAsync(() => {
dropdown.toggle();
tick();
fixture.detectChanges();
const focusedItem = fixture.debugElement.queryAll(By.css(`.${CSS_CLASS_ITEM}`))[0].nativeElement;
expect(document.activeElement).toEqual(focusedItem);
dropdown.toggle();
tick();
fixture.detectChanges();
dropdown.allowItemsFocus = false;
tick();
fixture.detectChanges();
const button = fixture.debugElement.query(By.css('button')).nativeElement;
button.focus();
button.click();
tick();
fixture.detectChanges();
expect(document.activeElement).toEqual(button);
}));
it('should not be able to select disabled and header items', fakeAsync(() => {
dropdown.items[2].isHeader = true;
dropdown.items[4].disabled = true;
fixture.detectChanges();
dropdown.toggle();
tick();
fixture.detectChanges();
const currentItem = fixture.debugElement.queryAll(By.css(`.${CSS_CLASS_DISABLED}`))[0];
const headerItem = fixture.debugElement.queryAll(By.css(`.${CSS_CLASS_HEADER}`))[0];
expect(currentItem.componentInstance.itemIndex).toEqual(4);
expect(headerItem.componentInstance).toEqual(dropdown.headers[0]);
currentItem.triggerEventHandler('click', UIInteractions.getMouseEvent('click'));
fixture.detectChanges();
expect(dropdown.selectedItem).toBeNull();
headerItem.triggerEventHandler('click', UIInteractions.getMouseEvent('click'));
fixture.detectChanges();
expect(dropdown.selectedItem).toBeNull();
// clicking on header item should not close the drop down
expect(dropdown.collapsed).toEqual(false);
}));
it('should be possible to enable/disable items at runtime', fakeAsync(() => {
dropdown.items[3].disabled = true;
dropdown.items[7].disabled = true;
dropdown.items[11].disabled = true;
fixture.detectChanges();
dropdown.toggle();
tick();
fixture.detectChanges();
let disabledItems = fixture.debugElement.queryAll(By.css(`.${CSS_CLASS_DISABLED}`));
expect(disabledItems.length).toEqual(3);
expect(dropdown.items[4].disabled).toBeFalsy();
dropdown.items[4].disabled = true;
fixture.detectChanges();
disabledItems = fixture.debugElement.queryAll(By.css(`.${CSS_CLASS_DISABLED}`));
expect(disabledItems.length).toEqual(4);
expect(dropdown.items[4].disabled).toBeTruthy();
}));
it('should focus the last enabled item by pressing END key', fakeAsync(() => {
dropdown.items[0].disabled = true;
dropdown.items[1].isHeader = true;
dropdown.items[3].disabled = true;
dropdown.items[4].isHeader = true;
dropdown.items[7].disabled = true;
dropdown.items[10].selected = true;
dropdown.items[12].disabled = true;
fixture.detectChanges();
dropdown.toggle();
tick();
fixture.detectChanges();
const selectedItem = fixture.debugElement.query(By.css(`.${CSS_CLASS_SELECTED}`));
expect(selectedItem.componentInstance.itemIndex).toEqual(10);
const dropdownElement = fixture.debugElement.query(By.css(`.${CSS_CLASS_DROP_DOWN_BASE}`));
UIInteractions.triggerEventHandlerKeyDown('End', dropdownElement);
tick();
fixture.detectChanges();
expect(dropdown.items[11].focused).toBeTruthy();
let focusedItem = fixture.debugElement.query(By.css(`.${CSS_CLASS_FOCUSED}`));
expect(focusedItem.componentInstance.itemIndex).toEqual(11);
UIInteractions.triggerEventHandlerKeyDown('ArrowDown', dropdownElement);
tick();
fixture.detectChanges();
expect(dropdown.items[11].focused).toBeTruthy();
focusedItem = fixture.debugElement.query(By.css(`.${CSS_CLASS_FOCUSED}`));
expect(focusedItem.componentInstance.itemIndex).toEqual(11);
}));
it('should skip disabled/header items on key navigation', fakeAsync(() => {
dropdown.items[0].disabled = true;
dropdown.items[1].isHeader = true;
dropdown.items[3].disabled = true;
dropdown.items[8].isHeader = true;
dropdown.items[9].disabled = true;
dropdown.items[10].selected = true;
dropdown.items[12].disabled = true;
fixture.detectChanges();
dropdown.toggle();
tick();
fixture.detectChanges();
const dropdownElement = fixture.debugElement.query(By.css(`.${CSS_CLASS_DROP_DOWN_BASE}`));
UIInteractions.triggerEventHandlerKeyDown('ArrowDown', dropdownElement);
tick();
fixture.detectChanges();
expect(dropdown.items[11].focused).toBeTruthy();
let focusedItem = fixture.debugElement.query(By.css(`.${CSS_CLASS_FOCUSED}`));
expect(focusedItem.componentInstance.itemIndex).toEqual(11);
UIInteractions.triggerEventHandlerKeyDown('ArrowUp', dropdownElement);
tick();
fixture.detectChanges();
expect(dropdown.items[10].focused).toBeTruthy();
focusedItem = fixture.debugElement.query(By.css(`.${CSS_CLASS_FOCUSED}`));
expect(focusedItem.componentInstance.itemIndex).toEqual(10);
UIInteractions.triggerEventHandlerKeyDown('ArrowUp', dropdownElement);
tick();
fixture.detectChanges();
expect(dropdown.items[8].focused).toBeTruthy();
focusedItem = fixture.debugElement.query(By.css(`.${CSS_CLASS_FOCUSED}`));
expect(focusedItem.componentInstance.itemIndex).toEqual(8);
UIInteractions.triggerEventHandlerKeyDown('ArrowUp', dropdownElement);
tick();
fixture.detectChanges();
expect(dropdown.items[7].focused).toBeTruthy();
focusedItem = fixture.debugElement.query(By.css(`.${CSS_CLASS_FOCUSED}`));
expect(focusedItem.componentInstance.itemIndex).toEqual(7);
}));
it('should select disabled items via code behind', fakeAsync(() => {
dropdown.items[0].disabled = true;
dropdown.items[1].isHeader = true;
fixture.detectChanges();
dropdown.setSelectedItem(0);
fixture.detectChanges();
dropdown.toggle();
tick();
fixture.detectChanges();
expect(dropdown.items[0].selected).toBeTruthy();
}));
it('should not move the focus when clicking a disabled item', fakeAsync(() => {
dropdown.items[0].disabled = true;
dropdown.items[1].isHeader = true;
dropdown.items[3].disabled = true;
dropdown.items[4].isHeader = true;
dropdown.items[7].disabled = true;
dropdown.items[10].selected = true;
fixture.detectChanges();
dropdown.toggle();
tick();
fixture.detectChanges();
expect(dropdown.items[10].focused).toEqual(true);
const dropdownElement = fixture.debugElement.query(By.css(`.${CSS_CLASS_DROP_DOWN_BASE}`));
UIInteractions.triggerEventHandlerKeyDown('ArrowDown', dropdownElement);
fixture.detectChanges();
expect(dropdown.items[11].focused).toEqual(true);
const firstItem = fixture.debugElement.query(By.css(`.${CSS_CLASS_ITEM}`));
firstItem.triggerEventHandler('click', UIInteractions.getMouseEvent('click'));
fixture.detectChanges();
expect(dropdown.items[11].focused).toEqual(true);
UIInteractions.triggerEventHandlerKeyDown('ArrowDown', dropdownElement);
fixture.detectChanges();
expect(dropdown.items[12].focused).toEqual(true);
}));
});
describe('Other', () => {
configureTestSuite();
beforeAll(waitForAsync(() => {
TestBed.configureTestingModule({
imports: [
NoopAnimationsModule,
DoubleIgxDropDownComponent,
InputWithDropDownDirectiveComponent
]
}).compileComponents();
}));
it('should call preventDefault on a mousedown event when allowItemsFocus is disabled', () => {
fixture = TestBed.createComponent(InputWithDropDownDirectiveComponent);
fixture.detectChanges();
dropdown = fixture.componentInstance.dropdown;
dropdown.allowItemsFocus = false;
fixture.detectChanges();
dropdown.open();
fixture.detectChanges();
const itemToClick = fixture.debugElement.queryAll(By.css(`.${CSS_CLASS_ITEM}`))[0];
const event = new Event('mousedown', { });
spyOn(event, 'preventDefault');
itemToClick.triggerEventHandler('mousedown', event);
fixture.detectChanges();
expect(event.preventDefault).toHaveBeenCalled();
});
it('should properly handle OnEnterKeyDown when the dropdown is not visible', fakeAsync(() => {
fixture = TestBed.createComponent(InputWithDropDownDirectiveComponent);
fixture.detectChanges();
dropdown = fixture.componentInstance.dropdown;
const input = fixture.debugElement.query(By.css('input'));
spyOn(dropdown, 'selectItem').and.callThrough();
expect(dropdown).toBeDefined();
expect(dropdown.focusedItem).toEqual(null);
expect(dropdown.selectedItem).toEqual(null);
expect(dropdown.selectItem).toHaveBeenCalledTimes(0);
expect(dropdown.collapsed).toEqual(true);
input.triggerEventHandler('click', UIInteractions.getMouseEvent('click'));
tick();
fixture.detectChanges();
expect(dropdown.collapsed).toEqual(true);
expect(dropdown.selectItem).toHaveBeenCalledTimes(0);
expect(dropdown.focusedItem).toEqual(null);
UIInteractions.triggerEventHandlerKeyDown('Enter', input);
tick();
fixture.detectChanges();
// does not attempt to select item on keydown if DD is closed;
expect(dropdown.selectItem).toHaveBeenCalledTimes(0);
expect(dropdown.selectedItem).toEqual(null);
expect(dropdown.collapsed).toEqual(true);
dropdown.toggle();
tick();
fixture.detectChanges();
expect(dropdown.collapsed).toEqual(false);
expect(dropdown.focusedItem).toEqual(dropdown.items[0]);
const dropdownItem = dropdown.items[0];
input.triggerEventHandler('keydown', UIInteractions.getKeyboardEvent('keydown', 'Enter'));
tick();
fixture.detectChanges();
expect(dropdown.selectItem).toHaveBeenCalledTimes(1);
expect(dropdown.selectItem).toHaveBeenCalledWith(dropdownItem, UIInteractions.getKeyboardEvent('keydown', 'Enter'));
expect(dropdown.selectedItem).toEqual(dropdownItem);
expect(dropdown.collapsed).toEqual(true);
}));
it('should keep selection per instance', () => {
fixture = TestBed.createComponent(DoubleIgxDropDownComponent);
fixture.detectChanges();
const dropdown1 = fixture.componentInstance.dropdown1;
const dropdown2 = fixture.componentInstance.dropdown2;
dropdown1.setSelectedItem(1);
expect(dropdown1.selectedItem).toEqual(dropdown1.items[1]);
expect(dropdown2.selectedItem).toEqual(null);
dropdown2.setSelectedItem(3);
expect(dropdown1.selectedItem).toEqual(dropdown1.items[1]);
expect(dropdown2.selectedItem).toEqual(dropdown2.items[3]);
dropdown1.setSelectedItem(5);
expect(dropdown1.selectedItem).toEqual(dropdown1.items[5]);
expect(dropdown2.selectedItem).toEqual(dropdown2.items[3]);
});
});
});
describe('Virtualisation tests', () => {
let scroll: IgxForOfDirective<any>;
let items;
configureTestSuite();
beforeAll(waitForAsync(() => {
TestBed.configureTestingModule({
imports: [
NoopAnimationsModule,
VirtualizedDropDownComponent
]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(VirtualizedDropDownComponent);
fixture.detectChanges();
dropdown = fixture.componentInstance.dropdown;
scroll = fixture.componentInstance.virtualScroll;
items = fixture.componentInstance.dropdownItems;
});
it('should properly scroll when virtualized', async () => {
dropdown.toggle();
fixture.detectChanges();
await wait(50);
let firstItemElement = fixture.componentInstance.dropdownItems.first.element.nativeElement;
let lastItemElement = fixture.componentInstance.dropdownItems.last.element.nativeElement;
expect(lastItemElement.textContent.trim()).toEqual('Item 11');
expect(firstItemElement.textContent.trim()).toEqual('Item 1');
scroll.getScroll().scrollTop = scroll.getScroll().scrollHeight;
fixture.detectChanges();
await wait(50);
firstItemElement = fixture.componentInstance.dropdownItems.first.element.nativeElement;
lastItemElement = fixture.componentInstance.dropdownItems.last.element.nativeElement;
expect(firstItemElement.textContent.trim()).toEqual('Item 1990');
expect(lastItemElement.textContent.trim()).toEqual('Item 2000');
});
xit('Should properly handle keyboard navigation when virtualized', async () => {
pending('does not have time to focus last item on navigateLast()');
// dropdown.toggle();
// fixture.detectChanges();
// dropdown.navigateFirst();
// expect(scroll.state.startIndex).toEqual(0);
// expect(items.first.focused).toEqual(true);
// dropdown.navigateLast();
// await wait(200);
// fixture.detectChanges();
// expect(scroll.state.startIndex).toEqual(2000 - scroll.state.chunkSize);
// expect(items.last.focused).toEqual(true);
// const toggleBtn = fixture.debugElement.query(By.css('button'));
// UIInteractions.triggerEventHandlerKeyDown('ArrowUp', toggleBtn);
// await wait(30);
// fixture.detectChanges();
// expect(scroll.state.startIndex).toEqual(2000 - scroll.state.chunkSize);
// expect(items.toArray()[items.toArray().length - 2].focused).toEqual(true);
});
it('should persist selection on scrolling', async () => {
dropdown.toggle();
expect(dropdown.selectedItem).toBe(null);
dropdown.selectItem(dropdown.items[5]);
fixture.detectChanges();
expect(dropdown.selectedItem.value).toEqual({ name: fixture.componentInstance.items[5].name, id: 5 });
expect(dropdown.items[5].selected).toBeTruthy();
scroll.scrollTo(412);
await wait(50);
fixture.detectChanges();
expect(items.toArray()[5].selected).toBeFalsy();
expect(document.getElementsByClassName(CSS_CLASS_SELECTED).length).toEqual(0);
scroll.scrollTo(0);
await wait(50);
fixture.detectChanges();
expect(items.toArray()[5].selected).toBeTruthy();
expect(document.getElementsByClassName(CSS_CLASS_SELECTED).length).toEqual(1);
});
it('should properly select items both inside and outside of the virtual view', async () => {
dropdown.toggle();
expect(dropdown.selectedItem).toBe(null);
let selectedItem = { value: fixture.componentInstance.items[5], index: 5 } as IgxDropDownItemBaseDirective;
dropdown.selectItem(selectedItem);
fixture.detectChanges();
expect(dropdown.selectedItem as any).toEqual(selectedItem);
expect(items.toArray()[5].selected).toEqual(true);
selectedItem = { value: fixture.componentInstance.items[412], index: 412 } as IgxDropDownItemBaseDirective;
dropdown.selectItem(selectedItem);
fixture.detectChanges();
expect(dropdown.selectedItem as any).toEqual(selectedItem);
expect(items.toArray()[5].selected).toEqual(false);
scroll.scrollTo(412);
await wait(50);
fixture.detectChanges();
const selectedEntry = items.find(e => e.value === selectedItem.value && e.index === selectedItem.index);
expect(selectedEntry).toBeTruthy();
expect(selectedEntry.selected).toBeTruthy();
});
it('should scroll selected item into view when virtualized', async () => {
dropdown.toggle();
expect(dropdown.selectedItem).toBe(null);
const virtualScroll = fixture.componentInstance.virtualScroll;
const selectedItem = { value: fixture.componentInstance.items[1000], index: 1000 } as IgxDropDownItemBaseDirective;
dropdown.selectItem(selectedItem);
fixture.detectChanges();
dropdown.toggle();
await wait(50);
dropdown.toggle();
await wait(50);
const itemsInView = virtualScroll.igxForContainerSize / virtualScroll.igxForItemSize;
const expectedScroll = virtualScroll.getScrollForIndex(selectedItem.index)
- (itemsInView / 2 - 1) * virtualScroll.igxForItemSize;
const acceptableDelta = virtualScroll.igxForItemSize;
const scrollTop = virtualScroll.getScroll().scrollTop;
expect(expectedScroll - acceptableDelta < scrollTop && expectedScroll + accep