igniteui-angular-sovn
Version:
Ignite UI for Angular is a dependency-free Angular toolkit for building modern web apps
1,006 lines (856 loc) • 92.1 kB
text/typescript
import { Component, ViewChild, DebugElement, EventEmitter, QueryList } from '@angular/core';
import { TestBed, fakeAsync, tick, ComponentFixture, waitForAsync } from '@angular/core/testing';
import { UntypedFormControl, UntypedFormGroup, FormsModule, NgForm, ReactiveFormsModule, Validators } from '@angular/forms';
import { By } from '@angular/platform-browser';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { IgxTimePickerComponent, IgxTimePickerValidationFailedEventArgs } from './time-picker.component';
import { UIInteractions } from '../test-utils/ui-interactions.spec';
import {
IgxHintDirective, IgxInputGroupComponent, IgxInputState, IgxLabelDirective, IgxPrefixDirective, IgxSuffixDirective
} from '../input-group/public_api';
import { configureTestSuite } from '../test-utils/configure-suite';
import { PickerInteractionMode } from '../date-common/types';
import { PlatformUtil } from '../core/utils';
import { DatePart, IgxDateTimeEditorDirective } from '../directives/date-time-editor/public_api';
import { DateTimeUtil } from '../date-common/util/date-time.util';
import { IgxItemListDirective, IgxTimeItemDirective } from './time-picker.directives';
import { IgxPickerClearComponent, IgxPickerToggleComponent } from '../date-common/public_api';
import { Subscription } from 'rxjs';
import { HammerGesturesManager } from '../core/touch';
import { NgIf } from '@angular/common';
const CSS_CLASS_TIMEPICKER = 'igx-time-picker';
const CSS_CLASS_INPUTGROUP = 'igx-input-group';
const CSS_CLASS_INPUTGROUP_DISABLED = 'igx-input-group--disabled';
const CSS_CLASS_INPUT_GROUP_REQUIRED = 'igx-input-group--required';
const CSS_CLASS_INPUT_GROUP_INVALID = 'igx-input-group--invalid';
const CSS_CLASS_INPUT_GROUP_LABEL = 'igx-input-group__label';
const CSS_CLASS_INPUT = '.igx-input-group__input';
const CSS_CLASS_DROPDOWN = '.igx-time-picker--dropdown';
const CSS_CLASS_HOURLIST = 'igx-time-picker__hourList';
const CSS_CLASS_MINUTELIST = 'igx-time-picker__minuteList';
const CSS_CLASS_SECONDSLIST = '.igx-time-picker__secondsList';
const CSS_CLASS_AMPMLIST = 'igx-time-picker__ampmList';
const CSS_CLASS_SELECTED_ITEM = '.igx-time-picker__item--selected';
const CSS_CLASS_HEADER_HOUR = '.igx-time-picker__header-hour';
const CSS_CLASS_OVERLAY = 'igx-overlay';
const CSS_CLASS_OVERLAY_WRAPPER = 'igx-overlay__wrapper';
const TIME_PICKER_TOGGLE_ICON = 'access_time';
const TIME_PICKER_CLEAR_ICON = 'clear';
describe('IgxTimePicker', () => {
let timePicker: IgxTimePickerComponent;
describe('Unit tests', () => {
let mockControlInstance: any;
let elementRef;
let mockNgControl;
let mockInjector;
let mockCdr;
let mockDateTimeEditorDirective;
let mockInputGroup: Partial<IgxInputGroupComponent>;
let mockInputDirective;
beforeEach(() => {
mockDateTimeEditorDirective = {
_value: null,
get value() {
return this._value;
},
clear() {
this.valueChange.emit(null);
},
increment: () => { },
decrement: () => { },
set value(val: any) {
this._value = val;
},
valueChange: new EventEmitter<any>(),
validationFailed: new EventEmitter<any>()
};
spyOn(mockDateTimeEditorDirective, 'increment');
spyOn(mockDateTimeEditorDirective, 'decrement');
mockInputGroup = {
_isFocused: false,
get isFocused() {
return this._isFocused;
},
set isFocused(val: boolean) {
this._isFocused = val;
},
_isRequired: false,
get isRequired() {
return this._isRequired;
},
set isRequired(val: boolean) {
this._isRequired = val;
},
element: {
nativeElement: jasmine.createSpyObj('mockElement',
['focus', 'blur', 'click', 'addEventListener', 'removeEventListener'])
}
} as any;
elementRef = {
nativeElement: jasmine.createSpyObj<HTMLElement>('mockElement', ['blur', 'click', 'focus'])
};
mockControlInstance = {
_touched: false,
get touched() {
return this._touched;
},
set touched(val: boolean) {
this._touched = val;
},
_dirty: false,
get dirty() {
return this._dirty;
},
set dirty(val: boolean) {
this._dirty = val;
},
_asyncValidator: () => { },
get asyncValidator() {
return this._asyncValidator;
},
set asyncValidator(val: () => boolean) {
this._asyncValidator = val;
},
_validator: () => { },
get validator() {
return this._validator;
},
set validator(val: () => boolean) {
this._validator = val;
}
};
mockNgControl = {
registerOnChangeCb: () => { },
registerOnTouchedCb: () => { },
registerOnValidatorChangeCb: () => { },
statusChanges: new EventEmitter(),
_control: mockControlInstance,
get control() {
return this._control;
},
set control(val: any) {
this._control = val;
},
valid: true
};
mockInputDirective = {
valid: 'mock',
nativeElement: {
_listeners: {
none: []
},
addEventListener(event: string, cb: () => void) {
let target = this._listeners[event];
if (!target) {
this._listeners[event] = [];
target = this._listeners[event];
}
target.push(cb);
},
removeEventListener(event: string, cb: () => void) {
const target = this._listeners[event];
if (!target) {
return;
}
const index = target.indexOf(cb);
if (index !== -1) {
target.splice(index, 1);
}
},
dispatchEvent(event: string) {
const target = this._listeners[event];
if (!target) {
return;
}
target.forEach(e => {
e();
});
},
focus() {
this.dispatchEvent('focus');
},
click() {
this.dispatchEvent('click');
},
blur() {
this.dispatchEvent('blur');
}
},
focus: () => { }
};
mockInjector = jasmine.createSpyObj('Injector', {
get: mockNgControl
});
mockCdr = jasmine.createSpyObj('ChangeDetectorRef', ['detectChanges']);
timePicker = new IgxTimePickerComponent(elementRef, 'en', null, null, mockInjector, null, mockCdr);
(timePicker as any).dateTimeEditor = mockDateTimeEditorDirective;
(timePicker as any)._inputGroup = mockInputGroup;
(timePicker as any).inputDirective = mockInputDirective;
timePicker.toggleComponents = new QueryList<any>();
timePicker.clearComponents = new QueryList<any>();
});
it('should properly initialize w/ ngControl', () => {
const mockSub = jasmine.createSpyObj<Subscription>('mockSub', ['unsubscribe']);
spyOn(mockNgControl.statusChanges, 'subscribe').and.returnValue(mockSub);
timePicker.ngOnInit();
timePicker.ngAfterViewInit();
expect(mockNgControl.statusChanges.subscribe).toHaveBeenCalledTimes(1);
timePicker.ngOnDestroy();
expect(mockSub.unsubscribe).toHaveBeenCalledTimes(1);
});
it('should properly subscribe to ngControl status changes', () => {
timePicker.ngOnInit();
timePicker.ngAfterViewInit();
const touchedSpy = spyOnProperty(mockControlInstance, 'touched', 'get');
const dirtySpy = spyOnProperty(mockControlInstance, 'dirty', 'get');
const validatorSpy = spyOnProperty(mockControlInstance, 'validator');
const asyncValidatorSpy = spyOnProperty(mockControlInstance, 'asyncValidator');
const inputGroupFocusedSpy = spyOnProperty(mockInputGroup, 'isFocused', 'get');
const inputGroupRequiredGet = spyOnProperty(mockInputGroup, 'isRequired', 'get');
const inputGroupRequiredSet = spyOnProperty(mockInputGroup, 'isRequired', 'set');
inputGroupRequiredGet.and.returnValue(false);
inputGroupFocusedSpy.and.returnValue(false);
expect(touchedSpy).not.toHaveBeenCalled();
expect(dirtySpy).not.toHaveBeenCalled();
expect(validatorSpy).not.toHaveBeenCalled();
expect(asyncValidatorSpy).not.toHaveBeenCalled();
touchedSpy.and.returnValue(false);
dirtySpy.and.returnValue(false);
mockNgControl.statusChanges.emit();
expect(touchedSpy).toHaveBeenCalledTimes(1);
expect(dirtySpy).toHaveBeenCalledTimes(1);
// required getter
expect(validatorSpy).toHaveBeenCalledTimes(1);
touchedSpy.and.returnValue(true);
dirtySpy.and.returnValue(true);
validatorSpy.and.returnValue(false);
asyncValidatorSpy.and.returnValue(false);
mockNgControl.statusChanges.emit();
expect(validatorSpy).toHaveBeenCalledTimes(3);
expect(asyncValidatorSpy).toHaveBeenCalledTimes(1);
expect(inputGroupFocusedSpy).not.toHaveBeenCalled();
validatorSpy.and.returnValue(() => { });
asyncValidatorSpy.and.returnValue(() => { });
mockNgControl.statusChanges.emit();
expect(inputGroupFocusedSpy).toHaveBeenCalledTimes(1);
expect(inputGroupRequiredSet).not.toHaveBeenCalled();
inputGroupRequiredGet.and.returnValue(false);
validatorSpy.and.returnValue(() => ({ required: true }));
mockNgControl.statusChanges.emit();
expect(inputGroupFocusedSpy).toHaveBeenCalledTimes(2);
expect(inputGroupRequiredSet).toHaveBeenCalledTimes(1);
expect(inputGroupRequiredSet).toHaveBeenCalledWith(true);
inputGroupRequiredGet.and.returnValue(true);
mockNgControl.statusChanges.emit();
expect(inputGroupFocusedSpy).toHaveBeenCalledTimes(3);
expect(mockInputDirective.valid).toBe(IgxInputState.INITIAL);
mockNgControl.valid = false;
mockNgControl.statusChanges.emit();
expect(mockInputDirective.valid).toBe(IgxInputState.INVALID);
inputGroupFocusedSpy.and.returnValue(true);
mockNgControl.statusChanges.emit();
expect(mockInputDirective.valid).toBe(IgxInputState.INVALID);
mockNgControl.valid = true;
mockNgControl.statusChanges.emit();
expect(mockInputDirective.valid).toBe(IgxInputState.VALID);
timePicker.ngOnDestroy();
});
it('should open/close the dropdown with open()/close() method', () => {
const mockToggleDirective = jasmine.createSpyObj('IgxToggleDirective', ['open', 'close'], { collapsed: true });
(timePicker as any).toggleRef = mockToggleDirective;
timePicker.ngOnInit();
timePicker.open();
expect(mockToggleDirective.open).toHaveBeenCalledTimes(1);
(Object.getOwnPropertyDescriptor(mockToggleDirective, 'collapsed')?.get as jasmine.Spy<() => boolean>).and.returnValue(false);
timePicker.close();
expect(mockToggleDirective.close).toHaveBeenCalledTimes(1);
});
it('should open/close the dropdown with toggle() method', () => {
timePicker = new IgxTimePickerComponent(elementRef, 'en', null, null, mockInjector, null, mockCdr);
(timePicker as any).dateTimeEditor = mockDateTimeEditorDirective;
const mockToggleDirective = jasmine.createSpyObj('IgxToggleDirective', ['open', 'close'], { collapsed: true });
(timePicker as any).toggleRef = mockToggleDirective;
timePicker.ngOnInit();
timePicker.toggle();
expect(mockToggleDirective.open).toHaveBeenCalledTimes(1);
(Object.getOwnPropertyDescriptor(mockToggleDirective, 'collapsed')?.get as jasmine.Spy<() => boolean>).and.returnValue(false);
timePicker.toggle();
expect(mockToggleDirective.close).toHaveBeenCalledTimes(1);
});
it('should reset value and emit valueChange with clear() method', () => {
timePicker = new IgxTimePickerComponent(elementRef, 'en', null, null, mockInjector, null, mockCdr);
(timePicker as any).dateTimeEditor = mockDateTimeEditorDirective;
const mockToggleDirective = jasmine.createSpyObj('IgxToggleDirective', { collapsed: true });
(timePicker as any).toggleRef = mockToggleDirective;
timePicker.minDropdownValue = timePicker.minDateValue;
timePicker.maxDropdownValue = timePicker.maxDateValue;
const date = new Date(2020, 12, 12, 10, 30, 30);
timePicker.value = new Date(date);
date.setHours(0, 0, 0);
spyOn(timePicker.valueChange, 'emit').and.callThrough();
timePicker.clear();
expect(timePicker.value).toEqual(date);
expect(timePicker.valueChange.emit).toHaveBeenCalled();
expect(timePicker.valueChange.emit).toHaveBeenCalledWith(date);
const stringDate = '09:20:00';
timePicker.value = stringDate;
timePicker.clear();
expect(timePicker.value).toBeNull();
expect(timePicker.valueChange.emit).toHaveBeenCalled();
expect(timePicker.valueChange.emit).toHaveBeenCalledWith(null);
});
it('should not emit valueChange when value is \'00:00:00\' and is cleared', () => {
timePicker = new IgxTimePickerComponent(elementRef, 'en', null, null, mockInjector, null, mockCdr);
(timePicker as any).dateTimeEditor = mockDateTimeEditorDirective;
const mockToggleDirective = jasmine.createSpyObj('IgxToggleDirective', { collapsed: true });
(timePicker as any).toggleRef = mockToggleDirective;
const date = new Date(2020, 12, 12, 0, 0, 0);
timePicker.value = date;
spyOn(timePicker.valueChange, 'emit').and.callThrough();
timePicker.ngOnInit();
timePicker.clear();
expect(timePicker.valueChange.emit).not.toHaveBeenCalled();
});
it('should not emit valueChange when value is null and is cleared', () => {
timePicker = new IgxTimePickerComponent(elementRef, 'en', null, null, mockInjector, null, mockCdr);
(timePicker as any).dateTimeEditor = mockDateTimeEditorDirective;
const mockToggleDirective = jasmine.createSpyObj('IgxToggleDirective', { collapsed: true });
(timePicker as any).toggleRef = mockToggleDirective;
timePicker.value = null;
timePicker.ngOnInit();
spyOn(timePicker.valueChange, 'emit').and.callThrough();
timePicker.clear();
expect(timePicker.valueChange.emit).not.toHaveBeenCalled();
});
it('should select time and trigger valueChange event with select() method', () => {
timePicker = new IgxTimePickerComponent(elementRef, 'en', null, null, mockInjector, null, mockCdr);
(timePicker as any).dateTimeEditor = mockDateTimeEditorDirective;
const date = new Date(2020, 12, 12, 10, 30, 30);
timePicker.value = new Date(date);
timePicker.minDropdownValue = timePicker.minDateValue;
timePicker.maxDropdownValue = timePicker.maxDateValue;
const selectedDate = new Date(2020, 12, 12, 6, 45, 0);
spyOn(timePicker.valueChange, 'emit').and.callThrough();
timePicker.select(selectedDate);
expect(timePicker.value).toEqual(selectedDate);
expect(timePicker.valueChange.emit).toHaveBeenCalled();
expect(timePicker.valueChange.emit).toHaveBeenCalledWith(selectedDate);
});
it('should correctly implement ControlValueAccessor methods', () => {
const date = new Date(2020, 12, 12, 10, 30, 30);
const updatedDate = new Date(2020, 12, 12, 11, 30, 30);
timePicker = new IgxTimePickerComponent(elementRef, 'en', null, null, mockInjector, null, mockCdr);
const mockToggleDirective = jasmine.createSpyObj('IgxToggleDirective', ['close'], { collapsed: true });
timePicker['dateTimeEditor'] = mockDateTimeEditorDirective;
timePicker['inputDirective'] = mockInputDirective;
timePicker['toggleRef'] = mockToggleDirective;
timePicker.minDropdownValue = timePicker.minDateValue;
timePicker.maxDropdownValue = timePicker.maxDateValue;
timePicker.ngOnInit();
spyOn(mockNgControl, 'registerOnChangeCb');
spyOn(mockNgControl, 'registerOnTouchedCb');
timePicker.registerOnChange(mockNgControl.registerOnChangeCb);
timePicker.registerOnTouched(mockNgControl.registerOnTouchedCb);
expect(timePicker.value).toBeUndefined();
expect(mockNgControl.registerOnChangeCb).not.toHaveBeenCalled();
timePicker.writeValue(date);
expect(timePicker.value).toBe(date);
timePicker.nextHour(100);
timePicker.okButtonClick();
expect(mockNgControl.registerOnChangeCb).toHaveBeenCalledTimes(1);
expect(mockNgControl.registerOnChangeCb).toHaveBeenCalledWith(updatedDate);
(timePicker as any).updateValidityOnBlur();
expect(mockNgControl.registerOnTouchedCb).toHaveBeenCalledTimes(1);
timePicker.setDisabledState(true);
expect(timePicker.disabled).toBe(true);
timePicker.setDisabledState(false);
expect(timePicker.disabled).toBe(false);
});
it('should validate correctly minValue and maxValue', () => {
timePicker = new IgxTimePickerComponent(elementRef, 'en', null, null, mockInjector, null, mockCdr);
timePicker['dateTimeEditor'] = mockDateTimeEditorDirective;
timePicker['inputDirective'] = mockInputDirective;
timePicker.ngOnInit();
spyOn(mockNgControl, 'registerOnChangeCb');
spyOn(mockNgControl, 'registerOnValidatorChangeCb');
timePicker.registerOnChange(mockNgControl.registerOnChangeCb);
timePicker.registerOnValidatorChange(mockNgControl.registerOnValidatorChangeCb);
timePicker.minValue = new Date(2020, 4, 7, 6, 0, 0);
expect(mockNgControl.registerOnValidatorChangeCb).toHaveBeenCalledTimes(1);
timePicker.maxValue = new Date(2020, 4, 7, 16, 0, 0);
expect(mockNgControl.registerOnValidatorChangeCb).toHaveBeenCalledTimes(2);
const date = new Date(2020, 4, 7, 8, 50, 0);
timePicker.writeValue(date);
const mockFormControl = new UntypedFormControl(timePicker.value);
expect(timePicker.validate(mockFormControl)).toBeNull();
date.setHours(3);
expect(timePicker.validate(mockFormControl)).toEqual({ minValue: true });
date.setHours(20);
expect(timePicker.validate(mockFormControl)).toEqual({ maxValue: true });
});
it('should handle panmove event correctly', () => {
const touchManager = new HammerGesturesManager(null, null, new PlatformUtil(1));
const itemListDirective = new IgxItemListDirective(timePicker, elementRef, touchManager);
spyOn(touchManager, 'addEventListener');
itemListDirective.ngOnInit();
expect(touchManager.addEventListener).toHaveBeenCalledTimes(1);
const hammerOptions: HammerOptions = { recognizers: [[Hammer.Pan, { direction: Hammer.DIRECTION_VERTICAL, threshold: 10 }]] };
expect(touchManager.addEventListener).toHaveBeenCalledWith(
elementRef.nativeElement,
'pan',
(itemListDirective as any).onPanMove,
hammerOptions);
spyOn<any>(itemListDirective, 'onPanMove').and.callThrough();
const event = { type: 'pan' };
(itemListDirective as any).onPanMove(event);
expect(itemListDirective['onPanMove']).toHaveBeenCalled();
});
});
describe('Interaction tests', () => {
let timePickerElement: DebugElement;
let timePickerDebElement: DebugElement;
let inputGroup: DebugElement;
let hourColumn: DebugElement;
let minutesColumn: DebugElement;
let secondsColumn: DebugElement;
let ampmColumn: DebugElement;
describe('Dropdown/dialog mode', () => {
let fixture: ComponentFixture<IgxTimePickerTestComponent>;
configureTestSuite();
beforeAll(waitForAsync(() => {
TestBed.configureTestingModule({
imports: [
FormsModule,
NoopAnimationsModule,
IgxTimePickerTestComponent
],
providers: [PlatformUtil]
}).compileComponents();
}));
beforeEach(fakeAsync(() => {
fixture = TestBed.createComponent(IgxTimePickerTestComponent);
fixture.detectChanges();
timePicker = fixture.componentInstance.timePicker;
timePickerDebElement = fixture.debugElement.query(By.css(CSS_CLASS_TIMEPICKER));
timePickerElement = fixture.debugElement.query(By.css(CSS_CLASS_TIMEPICKER)).nativeElement;
inputGroup = fixture.debugElement.query(By.css(`.${CSS_CLASS_INPUTGROUP}`));
hourColumn = fixture.debugElement.query(By.css(`.${CSS_CLASS_HOURLIST}`));
minutesColumn = fixture.debugElement.query(By.css(`.${CSS_CLASS_MINUTELIST}`));
secondsColumn = fixture.debugElement.query(By.css(CSS_CLASS_SECONDSLIST));
ampmColumn = fixture.debugElement.query(By.css(`.${CSS_CLASS_AMPMLIST}`));
}));
it('should open/close the dropdown and keep the current selection on toggle icon click', fakeAsync(() => {
const toggleIcon = fixture.debugElement.query(By.css('igx-prefix'));
toggleIcon.triggerEventHandler('click', UIInteractions.getMouseEvent('click'));
fixture.detectChanges();
expect(timePicker.collapsed).toBeFalsy();
const event = new WheelEvent('wheel', { deltaX: 0, deltaY: -100 });
hourColumn.triggerEventHandler('wheel', event);
fixture.detectChanges();
toggleIcon.triggerEventHandler('click', UIInteractions.getMouseEvent('click'));
tick();
fixture.detectChanges();
expect(timePicker.collapsed).toBeTruthy();
const pickerValue = new Date(fixture.componentInstance.date);
pickerValue.setHours(pickerValue.getHours() - 1);
expect(timePicker.value).toEqual(pickerValue);
}));
it('should open the dropdown with `ArrowDown` + `Alt` key press and close it on outside click', fakeAsync(() => {
UIInteractions.triggerEventHandlerKeyDown('ArrowDown', timePickerDebElement, true);
tick();
fixture.detectChanges();
expect(timePicker.collapsed).toBeFalsy();
const event = new WheelEvent('wheel', { deltaX: 0, deltaY: -100 });
hourColumn.triggerEventHandler('wheel', event);
fixture.detectChanges();
const overlay = document.getElementsByClassName(CSS_CLASS_OVERLAY_WRAPPER)[0];
UIInteractions.simulateClickEvent(overlay);
tick();
fixture.detectChanges();
expect(timePicker.collapsed).toBeTruthy();
const pickerValue = new Date(fixture.componentInstance.date);
pickerValue.setHours(pickerValue.getHours() - 1);
expect(timePicker.value).toEqual(pickerValue);
}));
it('should close the dropdown and keep the current selection on outside click in dialog mode', fakeAsync(() => {
fixture.componentInstance.mode = PickerInteractionMode.Dialog;
fixture.detectChanges();
const input = fixture.debugElement.query(By.css(CSS_CLASS_INPUT));
input.triggerEventHandler('click', UIInteractions.getMouseEvent('click'));
tick();
fixture.detectChanges();
expect(timePicker.collapsed).toBeFalsy();
const event = new WheelEvent('wheel', { deltaX: 0, deltaY: -100 });
hourColumn.triggerEventHandler('wheel', event);
fixture.detectChanges();
const overlayWrapper = document.getElementsByClassName(CSS_CLASS_TIMEPICKER)[0].parentNode.parentNode;
UIInteractions.simulateClickEvent(overlayWrapper);
tick();
fixture.detectChanges();
expect(timePicker.collapsed).toBeTruthy();
const pickerValue = new Date(fixture.componentInstance.date);
pickerValue.setHours(pickerValue.getHours() - 1);
expect(timePicker.value).toEqual(pickerValue);
}));
it('should not assign value on dropdown open and outside click without interaction', fakeAsync(() => {
timePicker.value = null;
fixture.detectChanges();
timePicker.open();
fixture.detectChanges();
expect(timePicker.collapsed).toBeFalsy();
const overlay = document.getElementsByClassName(CSS_CLASS_OVERLAY_WRAPPER)[0];
UIInteractions.simulateClickEvent(overlay);
tick();
fixture.detectChanges();
expect(timePicker.collapsed).toBeTruthy();
expect(timePicker.value).toEqual(null);
}));
it('should assign Date value after interaction when initial value is null', fakeAsync(() => {
timePicker.value = null;
fixture.detectChanges();
timePicker.open();
fixture.detectChanges();
expect(timePicker.collapsed).toBeFalsy();
const event = new WheelEvent('wheel', { deltaX: 0, deltaY: 100 });
hourColumn.triggerEventHandler('wheel', event);
fixture.detectChanges();
const overlay = document.getElementsByClassName(CSS_CLASS_OVERLAY_WRAPPER)[0];
UIInteractions.simulateClickEvent(overlay);
tick();
fixture.detectChanges();
const expectedDate = new Date();
expectedDate.setHours(1, 0, 0, 0);
expect(timePicker.collapsed).toBeTruthy();
expect((timePicker.value as Date).getTime()).toEqual(expectedDate.getTime());
}));
it('should open/close the dropdown and keep the current selection on Space/Enter key press', fakeAsync(() => {
UIInteractions.triggerEventHandlerKeyDown(' ', timePickerDebElement);
tick();
fixture.detectChanges();
expect(timePicker.collapsed).toBeFalsy();
const event = new WheelEvent('wheel', { deltaX: 0, deltaY: -100 });
hourColumn.triggerEventHandler('wheel', event);
fixture.detectChanges();
UIInteractions.triggerKeyDownEvtUponElem('Enter', hourColumn.nativeElement);
tick();
fixture.detectChanges();
expect(timePicker.collapsed).toBeTruthy();
const pickerValue = new Date(fixture.componentInstance.date);
pickerValue.setHours(pickerValue.getHours() - 1);
expect(timePicker.value).toEqual(pickerValue);
}));
it('should reset selection to the value when dropdown was opened on Escape key press', fakeAsync(() => {
fixture.componentInstance.minValue = new Date(2021, 24, 2, 9, 45, 0);
fixture.componentInstance.maxValue = new Date(2021, 24, 2, 13, 45, 0);
fixture.detectChanges();
timePicker.open();
fixture.detectChanges();
expect(timePicker.collapsed).toBeFalsy();
const event = new WheelEvent('wheel', { deltaX: 0, deltaY: -100 });
hourColumn.triggerEventHandler('wheel', event);
fixture.detectChanges();
hourColumn.triggerEventHandler('wheel', event);
fixture.detectChanges(); hourColumn.triggerEventHandler('wheel', event);
fixture.detectChanges();
let selectedHour = fixture.componentInstance.date.getHours() + 2;
const selectedAmpm = selectedHour < 12 ? 'AM' : 'PM';
selectedHour = selectedHour > 12 ? selectedHour - 12 : selectedHour;
const selectedMinutes = fixture.componentInstance.date.getMinutes();
const dateTimeEditor = fixture.debugElement.query(By.directive(IgxDateTimeEditorDirective)).nativeElement;
expect((dateTimeEditor.value)).toEqual(`0${selectedHour}:${selectedMinutes} ${selectedAmpm}`);
UIInteractions.triggerEventHandlerKeyDown('Escape', timePickerDebElement);
tick();
fixture.detectChanges();
expect(timePicker.collapsed).toBeTruthy();
expect(timePicker.value).toEqual(fixture.componentInstance.date);
}));
it('should not change the current selection and close the dropdown on OK button click', fakeAsync(() => {
timePicker.open();
tick();
fixture.detectChanges();
const event = new WheelEvent('wheel', { deltaX: 0, deltaY: -100 });
hourColumn.triggerEventHandler('wheel', event);
fixture.detectChanges();
const okButton = fixture.debugElement.queryAll(By.css('button'))[1];
okButton.triggerEventHandler('click', UIInteractions.getMouseEvent('click'));
tick();
fixture.detectChanges();
expect(timePicker.collapsed).toBeTruthy();
const pickerValue = new Date(fixture.componentInstance.date);
pickerValue.setHours(pickerValue.getHours() - 1);
expect(timePicker.value).toEqual(pickerValue);
}));
it('should close the dropdown and discard the current selection on Cancel button click', fakeAsync(() => {
timePicker.open();
tick();
fixture.detectChanges();
const event = new WheelEvent('wheel', { deltaX: 0, deltaY: -100 });
hourColumn.triggerEventHandler('wheel', event);
fixture.detectChanges();
const cancelButton = fixture.debugElement.queryAll(By.css('button'))[0];
cancelButton.triggerEventHandler('click', UIInteractions.getMouseEvent('click'));
tick();
fixture.detectChanges();
expect(timePicker.collapsed).toBeTruthy();
expect(timePicker.value).toEqual(fixture.componentInstance.date);
}));
it('should fire opening/closing event on open/close', fakeAsync(() => {
spyOn(timePicker.opening, 'emit').and.callThrough();
spyOn(timePicker.opened, 'emit').and.callThrough();
spyOn(timePicker.closing, 'emit').and.callThrough();
spyOn(timePicker.closed, 'emit').and.callThrough();
timePicker.open();
tick();
fixture.detectChanges();
expect(timePicker.collapsed).toBeFalsy();
expect(timePicker.opening.emit).toHaveBeenCalled();
expect(timePicker.opened.emit).toHaveBeenCalled();
timePicker.close();
tick();
fixture.detectChanges();
expect(timePicker.collapsed).toBeTruthy();
expect(timePicker.closing.emit).toHaveBeenCalled();
expect(timePicker.closed.emit).toHaveBeenCalled();
}));
it('should be able to cancel opening/closing events', fakeAsync(() => {
spyOn(timePicker.opening, 'emit').and.callThrough();
spyOn(timePicker.opened, 'emit').and.callThrough();
spyOn(timePicker.closing, 'emit').and.callThrough();
spyOn(timePicker.closed, 'emit').and.callThrough();
const openingSub = timePicker.opening.subscribe((event) => event.cancel = true);
timePicker.open();
tick();
fixture.detectChanges();
expect(timePicker.collapsed).toBeTruthy();
expect(timePicker.opening.emit).toHaveBeenCalled();
expect(timePicker.opened.emit).not.toHaveBeenCalled();
openingSub.unsubscribe();
const closingSub = timePicker.closing.subscribe((event) => event.cancel = true);
timePicker.open();
tick();
fixture.detectChanges();
timePicker.close();
tick();
fixture.detectChanges();
expect(timePicker.collapsed).toBeFalsy();
expect(timePicker.closing.emit).toHaveBeenCalled();
expect(timePicker.closed.emit).not.toHaveBeenCalled();
closingSub.unsubscribe();
}));
it('should change date parts correctly and emit valueChange with increment() and decrement() methods', () => {
const date = new Date(2020, 12, 12, 10, 30, 30);
timePicker.value = new Date(date);
timePicker.minValue = new Date(2020, 12, 12, 6, 0, 0);
timePicker.maxValue = new Date(2020, 12, 12, 16, 0, 0);
timePicker.itemsDelta = { hours: 2, minutes: 20, seconds: 15 };
fixture.detectChanges();
spyOn(timePicker.valueChange, 'emit').and.callThrough();
timePicker.increment(DatePart.Hours);
date.setHours(date.getHours() + timePicker.itemsDelta.hours);
expect(timePicker.value).toEqual(date);
expect(timePicker.valueChange.emit).toHaveBeenCalledTimes(1);
expect(timePicker.valueChange.emit).toHaveBeenCalledWith(date);
timePicker.increment(DatePart.Minutes);
date.setMinutes(date.getMinutes() + timePicker.itemsDelta.minutes);
expect(timePicker.value).toEqual(date);
expect(timePicker.valueChange.emit).toHaveBeenCalledTimes(2);
expect(timePicker.valueChange.emit).toHaveBeenCalledWith(date);
timePicker.decrement(DatePart.Seconds);
date.setSeconds(date.getSeconds() - timePicker.itemsDelta.seconds);
expect(timePicker.value).toEqual(date);
expect(timePicker.valueChange.emit).toHaveBeenCalledTimes(3);
expect(timePicker.valueChange.emit).toHaveBeenCalledWith(date);
});
it('should fire vallidationFailed on incrementing time outside min/max range', () => {
const date = new Date(2020, 12, 12, 15, 30, 30);
const selectedDate = new Date(date);
selectedDate.setHours(date.getHours() + 2);
timePicker.value = new Date(date);
timePicker.minValue = new Date(2020, 12, 12, 6, 0, 0);
timePicker.maxValue = new Date(2020, 12, 12, 16, 0, 0);
timePicker.itemsDelta = { hours: 2, minutes: 20, seconds: 15 };
fixture.detectChanges();
spyOn(timePicker.validationFailed, 'emit').and.callThrough();
timePicker.increment(DatePart.Hours);
fixture.detectChanges();
const args: IgxTimePickerValidationFailedEventArgs = {
owner: timePicker,
previousValue: date,
currentValue: selectedDate
};
expect(timePicker.value).toEqual(selectedDate);
expect(timePicker.validationFailed.emit).toHaveBeenCalled();
expect(timePicker.validationFailed.emit).toHaveBeenCalledWith(args);
});
it('should scroll trough hours/minutes/seconds/AM PM based on default or set itemsDelta', fakeAsync(() => {
timePicker.inputFormat = 'hh:mm:ss tt';
fixture.detectChanges();
secondsColumn = fixture.debugElement.query(By.css(CSS_CLASS_SECONDSLIST));
timePicker.open();
fixture.detectChanges();
expect(timePicker.collapsed).toBeFalsy();
// spin all columns with default delta
const eventScrollDown = new WheelEvent('wheel', { deltaX: 0, deltaY: 100 });
const eventScrollUp = new WheelEvent('wheel', { deltaX: 0, deltaY: -100 });
hourColumn.triggerEventHandler('wheel', eventScrollDown);
minutesColumn.triggerEventHandler('wheel', eventScrollDown);
secondsColumn.triggerEventHandler('wheel', eventScrollDown);
ampmColumn.triggerEventHandler('wheel', eventScrollUp);
fixture.detectChanges();
const expectedValuedHour = 0;
const expectedDisplayHour = 12;
const expectedMinute = 46;
const expectedSecond = 1;
const expectedAmPm = 'AM';
const expectedPrependZero = '0';
// test rendered display value
const selectedItems = fixture.debugElement.queryAll(By.css(CSS_CLASS_SELECTED_ITEM));
const selectedHour = selectedItems[0].nativeElement.innerText;
const selectedMinute = selectedItems[1].nativeElement.innerText;
const selectedSecond = selectedItems[2].nativeElement.innerText;
const selectedAMPM = selectedItems[3].nativeElement.innerText;
expect(selectedHour).toEqual(expectedDisplayHour.toString());
expect(selectedMinute).toEqual(expectedMinute.toString());
expect(selectedSecond).toEqual(expectedPrependZero + expectedSecond.toString());
expect(selectedAMPM).toEqual(expectedAmPm);
// apply selected value on toggle btn click
const toggleIcon = fixture.debugElement.query(By.css('igx-prefix'));
toggleIcon.triggerEventHandler('click', UIInteractions.getMouseEvent('click'));
tick();
fixture.detectChanges();
expect(timePicker.collapsed).toBeTrue();
expect((timePicker.value as Date).getHours()).toEqual(expectedValuedHour);
expect((timePicker.value as Date).getMinutes()).toEqual(expectedMinute);
expect((timePicker.value as Date).getSeconds()).toEqual(expectedSecond);
}));
it('should scroll trough hours/minutes/seconds/AM PM based on custom itemsDelta', fakeAsync(() => {
const newDate = new Date(2021, 24, 2, 10, 20, 0);
fixture.componentInstance.date = newDate;
timePicker.inputFormat = 'hh:mm:ss tt';
timePicker.itemsDelta = { hours: 2, minutes: 20, seconds: 20 };
fixture.detectChanges();
timePicker.open();
fixture.detectChanges();
secondsColumn = fixture.debugElement.query(By.css(CSS_CLASS_SECONDSLIST));
expect(timePicker.collapsed).toBeFalsy();
// spin all columns with the custom itemsDelta
const eventScrollDown = new WheelEvent('wheel', { deltaX: 0, deltaY: 100 });
const eventScrollUp = new WheelEvent('wheel', { deltaX: 0, deltaY: -100 });
hourColumn.triggerEventHandler('wheel', eventScrollDown);
minutesColumn.triggerEventHandler('wheel', eventScrollDown);
secondsColumn.triggerEventHandler('wheel', eventScrollDown);
ampmColumn.triggerEventHandler('wheel', eventScrollUp);
fixture.detectChanges();
const expectedValuedHour = 0;
const expectedDisplayHour = 12;
const expectedMinute = 40;
const expectedSecond = 20;
const expectedAmPm = 'AM';
// test rendered display value
const selectedItems = fixture.debugElement.queryAll(By.css(CSS_CLASS_SELECTED_ITEM));
const selectedHour = selectedItems[0].nativeElement.innerText;
const selectedMinute = selectedItems[1].nativeElement.innerText;
const selectedSecond = selectedItems[2].nativeElement.innerText;
const selectedAMPM = selectedItems[3].nativeElement.innerText;
expect(selectedHour).toEqual(expectedDisplayHour.toString());
expect(selectedMinute).toEqual(expectedMinute.toString());
expect(selectedSecond).toEqual(expectedSecond.toString());
expect(selectedAMPM).toEqual(expectedAmPm);
// apply selected value on toggle btn click
const toggleIcon = fixture.debugElement.query(By.css('igx-prefix'));
toggleIcon.triggerEventHandler('click', UIInteractions.getMouseEvent('click'));
tick();
fixture.detectChanges();
expect(timePicker.collapsed).toBeTrue();
expect((timePicker.value as Date).getHours()).toEqual(expectedValuedHour);
expect((timePicker.value as Date).getMinutes()).toEqual(expectedMinute);
expect((timePicker.value as Date).getSeconds()).toEqual(expectedSecond);
}));
it('should navigate through columns with arrow keys', () => {
timePicker.open();
fixture.detectChanges();
expect(timePicker.collapsed).toBeFalsy();
hourColumn.nativeElement.focus();
fixture.detectChanges();
expect(document.activeElement.classList).toContain(CSS_CLASS_HOURLIST);
UIInteractions.triggerKeyDownEvtUponElem('ArrowRight', hourColumn.nativeElement, true);
fixture.detectChanges();
expect(document.activeElement.classList).toContain(CSS_CLASS_MINUTELIST);
UIInteractions.triggerKeyDownEvtUponElem('ArrowRight', minutesColumn.nativeElement, true);
fixture.detectChanges();
expect(document.activeElement.classList).toContain(CSS_CLASS_AMPMLIST);
UIInteractions.triggerKeyDownEvtUponElem('ArrowLeft', ampmColumn.nativeElement, true);
fixture.detectChanges();
expect(document.activeElement.classList).toContain(CSS_CLASS_MINUTELIST);
UIInteractions.triggerKeyDownEvtUponElem('ArrowDown', minutesColumn.nativeElement, true);
fixture.detectChanges();
expect(document.activeElement.children[3].innerHTML.trim()).toBe('46');
UIInteractions.triggerKeyDownEvtUponElem('ArrowLeft', minutesColumn.nativeElement, true);
fixture.detectChanges();
expect(document.activeElement.classList).toContain(CSS_CLASS_HOURLIST);
UIInteractions.triggerKeyDownEvtUponElem('ArrowUp', hourColumn.nativeElement, true);
fixture.detectChanges();
expect(document.activeElement.children[3].innerHTML.trim()).toBe('10');
});
it('should navigate through items with arrow keys', () => {
timePicker.itemsDelta = { hours: 4, minutes: 7, seconds: 1 };
fixture.detectChanges();
timePicker.open();
fixture.detectChanges();
let selectedItems = fixture.debugElement.queryAll(By.css(CSS_CLASS_SELECTED_ITEM));
let selectedHour = selectedItems[0].nativeElement.innerText;
let selectedMinutes = selectedItems[1].nativeElement.innerText;
let selectedAMPM = selectedItems[2].nativeElement.innerText;
expect(selectedHour).toEqual('12');
expect(selectedMinutes).toEqual('00');
expect(selectedAMPM).toEqual('PM');
hourColumn.nativeElement.focus();
fixture.detectChanges();
UIInteractions.triggerKeyDownEvtUponElem('ArrowUp', hourColumn.nativeElement, true);
fixture.detectChanges();
selectedItems = fixture.debugElement.queryAll(By.css(CSS_CLASS_SELECTED_ITEM));
selectedHour = selectedItems[0].nativeElement.innerText;
selectedMinutes = selectedItems[1].nativeElement.innerText;
selectedAMPM = selectedItems[2].nativeElement.innerText;
expect(selectedHour).toEqual('08');
expect(selectedMinutes).toEqual('00');
expect(selectedAMPM).toEqual('AM');
UIInteractions.triggerKeyDownEvtUponElem('ArrowDown', hourColumn.nativeElement, true);
fixture.detectChanges();
selectedItems = fixture.debugElement.queryAll(By.css(CSS_CLASS_SELECTED_ITEM));
selectedHour = selectedItems[0].nativeElement.innerText;
selectedMinutes = selectedItems[1].nativeElement.innerText;
selectedAMPM = selectedItems[2].nativeElement.innerText;
expect(selectedHour).toEqual('12');
expect(selectedMinutes).toEqual('00');
expect(selectedAMPM).toEqual('PM');
UIInteractions.triggerKeyDownEvtUponElem('ArrowRight', hourColumn.nativeElement, true);
fixture.detectChanges();
UIInteractions.triggerKeyDownEvtUponElem('ArrowRight', minutesColumn.nativeElement, true);
fixture.detectChanges();
UIInteractions.triggerKeyDownEvtUponElem('ArrowUp', ampmColumn.nativeElement, true);
fixture.detectChanges();
selectedItems = fixture.debugElement.queryAll(By.css(CSS_CLASS_SELECTED_ITEM));
selectedHour = selectedItems[0].nativeElement.innerText;
selectedMinutes = selectedItems[1].nativeElement.innerText;
selectedAMPM = selectedItems[2].nativeElement.innerText;
expect(selectedHour).toEqual('12');
expect(selectedMinutes).toEqual('00');
expect(selectedAMPM).toEqual('AM');
UIInteractions.triggerKeyDownEvtUponElem('ArrowLeft', ampmColumn.nativeElement, true);
fixture.detectChanges();
UIInteractions.triggerKeyDownEvtUponElem('ArrowUp', minutesColumn.nativeElement, true);
fixture.detectChanges();
selectedItems = fixture.debugElement.queryAll(By.css(CSS_CLASS_SELECTED_ITEM));
selectedHour = selectedItems[0].nativeElement.innerText;
selectedMinutes = selectedItems[1].nativeElement.innerText;
selectedAMPM = selectedItems[2].nativeElement.innerText;
expect(selectedHour).toEqual('12');
expect(selectedMinutes).toEqual('56');
expect(selectedAMPM).toEqual('AM');
UIInteractions.triggerKeyDownEvtUponElem('ArrowDown', minutesColumn.nativeElement, true);
fixture.detectChanges();
UIInteractions.triggerKeyDownEvtUponElem('ArrowDown', minutesColumn.nativeElement, true);
fixture.detectChanges();
selectedItems = fixture.debugElement.queryAll(By.css(CSS_CLASS_SELECTED_ITEM));
selectedHour =