UNPKG

igniteui-angular-sovn

Version:

Ignite UI for Angular is a dependency-free Angular toolkit for building modern web apps

1,009 lines (905 loc) 72.7 kB
import { ComponentFixture, fakeAsync, flush, TestBed, tick, waitForAsync } from '@angular/core/testing'; import { UntypedFormControl, UntypedFormGroup, FormsModule, NgForm, ReactiveFormsModule, Validators } from '@angular/forms'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; 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 { IFormattingViews, IgxCalendarComponent, WEEKDAYS } from '../calendar/public_api'; import { IgxCalendarContainerComponent } from '../date-common/calendar-container/calendar-container.component'; import { IgxDatePickerComponent } from './date-picker.component'; import { IgxOverlayService, OverlayCancelableEventArgs, OverlayClosingEventArgs, OverlayEventArgs, OverlaySettings } from '../services/public_api'; import { Component, ElementRef, EventEmitter, QueryList, Renderer2, ViewChild } from '@angular/core'; import { By } from '@angular/platform-browser'; import { PickerHeaderOrientation, PickerInteractionMode } from '../date-common/types'; import { DatePart } from '../directives/date-time-editor/date-time-editor.common'; import { DisplayDensity } from '../core/density'; import { DateRangeDescriptor, DateRangeType } from '../core/dates'; import { IgxOverlayOutletDirective } from '../directives/toggle/toggle.directive'; import { IgxPickerClearComponent, IgxPickerToggleComponent } from '../date-common/public_api'; import { DateTimeUtil } from '../date-common/util/date-time.util'; import { NgIf, registerLocaleData } from "@angular/common"; import localeES from "@angular/common/locales/es"; const CSS_CLASS_CALENDAR = 'igx-calendar'; const CSS_CLASS_DATE_SELECTED = 'igx-calendar__date--selected'; const CSS_CLASS_DATE_PICKER = 'igx-date-picker'; const DATE_PICKER_TOGGLE_ICON = 'today'; const DATE_PICKER_CLEAR_ICON = 'clear'; 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'; describe('IgxDatePicker', () => { describe('Integration tests', () => { configureTestSuite(); beforeAll(waitForAsync(() => { TestBed.configureTestingModule({ imports: [ NoopAnimationsModule, IgxDatePickerTestKbrdComponent, IgxDatePickerTestComponent, IgxDatePickerNgModelComponent, IgxDatePickerWithProjectionsComponent, IgxDatePickerInFormComponent, IgxDatePickerReactiveFormComponent ] }).compileComponents(); })); describe('Rendering', () => { let fixture: ComponentFixture<IgxDatePickerTestComponent>; beforeEach(fakeAsync(() => { fixture = TestBed.createComponent(IgxDatePickerTestComponent); fixture.detectChanges(); })); it('Should render default toggle and clear icons', () => { const inputGroup = fixture.debugElement.query(By.directive(IgxInputGroupComponent)); const prefix = inputGroup.queryAll(By.directive(IgxPrefixDirective)); expect(prefix).toHaveSize(1); expect(prefix[0].nativeElement.innerText).toEqual(DATE_PICKER_TOGGLE_ICON); const suffix = inputGroup.queryAll(By.directive(IgxSuffixDirective)); expect(suffix).toHaveSize(1); expect(suffix[0].nativeElement.innerText).toEqual(DATE_PICKER_CLEAR_ICON); }); }); describe('Events', () => { let fixture: ComponentFixture<any>; let datePicker: IgxDatePickerComponent; beforeEach(fakeAsync(() => { fixture = TestBed.createComponent(IgxDatePickerTestComponent); fixture.detectChanges(); datePicker = fixture.componentInstance.datePicker; })); it('should be able to cancel opening/closing', fakeAsync(() => { spyOn(datePicker.opening, 'emit').and.callThrough(); spyOn(datePicker.opened, 'emit').and.callThrough(); spyOn(datePicker.closing, 'emit').and.callThrough(); spyOn(datePicker.closed, 'emit').and.callThrough(); const openingSub = datePicker.opening.subscribe((event) => event.cancel = true); datePicker.open(); // wait for calendar animation.done timeout tick(350); fixture.detectChanges(); expect(datePicker.collapsed).toBeTruthy(); expect(datePicker.opening.emit).toHaveBeenCalled(); expect(datePicker.opened.emit).not.toHaveBeenCalled(); openingSub.unsubscribe(); const closingSub = datePicker.closing.subscribe((event) => event.cancel = true); datePicker.open(); // wait for calendar animation.done timeout tick(350); fixture.detectChanges(); datePicker.close(); tick(350); fixture.detectChanges(); expect(datePicker.collapsed).toBeFalsy(); expect(datePicker.closing.emit).toHaveBeenCalled(); expect(datePicker.closed.emit).not.toHaveBeenCalled(); closingSub.unsubscribe(); (datePicker as any)._overlayService.detachAll(); })); }); describe('Keyboard navigation', () => { let fixture: ComponentFixture<any>; let datePicker: IgxDatePickerComponent; let calendar; beforeEach(fakeAsync(() => { fixture = TestBed.createComponent(IgxDatePickerTestKbrdComponent); fixture.detectChanges(); datePicker = fixture.componentInstance.datePicker; calendar = fixture.componentInstance.datePicker.calendar; })); it('should toggle the calendar with ALT + DOWN/UP ARROW key', fakeAsync(() => { spyOn(datePicker.opening, 'emit').and.callThrough(); spyOn(datePicker.opened, 'emit').and.callThrough(); spyOn(datePicker.closing, 'emit').and.callThrough(); spyOn(datePicker.closed, 'emit').and.callThrough(); expect(datePicker.collapsed).toBeTruthy(); const picker = fixture.debugElement.query(By.css(CSS_CLASS_DATE_PICKER)); UIInteractions.triggerEventHandlerKeyDown('ArrowDown', picker, true); tick(); fixture.detectChanges(); expect(datePicker.collapsed).toBeFalsy(); expect(datePicker.opening.emit).toHaveBeenCalledTimes(1); expect(datePicker.opened.emit).toHaveBeenCalledTimes(1); calendar = document.getElementsByClassName(CSS_CLASS_CALENDAR)[0]; UIInteractions.triggerKeyDownEvtUponElem('ArrowUp', calendar, true, true); tick(350); fixture.detectChanges(); expect(datePicker.collapsed).toBeTruthy(); expect(datePicker.closing.emit).toHaveBeenCalledTimes(1); expect(datePicker.closed.emit).toHaveBeenCalledTimes(1); })); it('should open the calendar with SPACE key', fakeAsync(() => { spyOn(datePicker.opening, 'emit').and.callThrough(); spyOn(datePicker.opened, 'emit').and.callThrough(); expect(datePicker.collapsed).toBeTruthy(); const picker = fixture.debugElement.query(By.css(CSS_CLASS_DATE_PICKER)); UIInteractions.triggerEventHandlerKeyDown(' ', picker); tick(350); fixture.detectChanges(); expect(datePicker.collapsed).toBeFalsy(); expect(datePicker.opening.emit).toHaveBeenCalledTimes(1); expect(datePicker.opened.emit).toHaveBeenCalledTimes(1); // wait datepicker to get destroyed and test to cleanup tick(350); })); it('should close the calendar with ESC', fakeAsync(() => { spyOn(datePicker.closing, 'emit').and.callThrough(); spyOn(datePicker.closed, 'emit').and.callThrough(); expect(datePicker.collapsed).toBeTruthy(); datePicker.open(); tick(); fixture.detectChanges(); expect(datePicker.collapsed).toBeFalsy(); calendar = document.getElementsByClassName(CSS_CLASS_CALENDAR)[0]; UIInteractions.triggerKeyDownEvtUponElem('Escape', calendar, true); tick(350); fixture.detectChanges(); expect(datePicker.collapsed).toBeTruthy(); expect(datePicker.closing.emit).toHaveBeenCalledTimes(1); expect(datePicker.closed.emit).toHaveBeenCalledTimes(1); })); }); describe('NgControl integration', () => { let fixture: ComponentFixture<IgxDatePickerNgModelComponent | IgxDatePickerInFormComponent | IgxDatePickerReactiveFormComponent>; let datePicker: IgxDatePickerComponent; beforeEach(fakeAsync(() => { fixture = TestBed.createComponent(IgxDatePickerNgModelComponent); fixture.detectChanges(); datePicker = fixture.componentInstance.datePicker; })); it('should initialize date picker with required correctly', () => { const inputGroup = (datePicker as any).inputGroup; expect(datePicker).toBeDefined(); expect(inputGroup.isRequired).toBeTruthy(); }); it('should update inputGroup isRequired correctly', () => { const inputGroup = (datePicker as any).inputGroup; expect(datePicker).toBeDefined(); expect(inputGroup.isRequired).toBeTruthy(); (fixture.componentInstance as IgxDatePickerNgModelComponent).isRequired = false; fixture.detectChanges(); expect(inputGroup.isRequired).toBeFalsy(); }); it('should set validity to initial when the form is reset', fakeAsync(() => { fixture = TestBed.createComponent(IgxDatePickerInFormComponent); fixture.detectChanges(); datePicker = fixture.componentInstance.datePicker; const input = document.getElementsByClassName('igx-input-group__input')[0] as HTMLInputElement; input.focus(); tick(); fixture.detectChanges(); datePicker.clear(); expect((datePicker as any).inputDirective.valid).toEqual(IgxInputState.INVALID); (fixture.componentInstance as IgxDatePickerInFormComponent).form.resetForm(); tick(); expect((datePicker as any).inputDirective.valid).toEqual(IgxInputState.INITIAL); })); it('should apply asterix properly when required validator is set dynamically', () => { fixture = TestBed.createComponent(IgxDatePickerReactiveFormComponent); fixture.detectChanges(); datePicker = fixture.componentInstance.datePicker; let inputGroupRequiredClass = fixture.debugElement.query(By.css('.' + CSS_CLASS_INPUT_GROUP_REQUIRED)); let inputGroupInvalidClass = fixture.debugElement.query(By.css('.' + CSS_CLASS_INPUT_GROUP_INVALID)); let asterisk = window. getComputedStyle(fixture.debugElement.query(By.css('.' + CSS_CLASS_INPUT_GROUP_LABEL)).nativeElement, ':after'). content; expect(asterisk).toBe('"*"'); expect(inputGroupRequiredClass).toBeDefined(); expect(inputGroupRequiredClass).not.toBeNull(); datePicker.clear(); fixture.detectChanges(); inputGroupInvalidClass = fixture.debugElement.query(By.css('.' + CSS_CLASS_INPUT_GROUP_INVALID)); expect(inputGroupInvalidClass).not.toBeNull(); expect(inputGroupInvalidClass).not.toBeUndefined(); inputGroupRequiredClass = fixture.debugElement.query(By.css('.' + CSS_CLASS_INPUT_GROUP_REQUIRED)); expect(inputGroupRequiredClass).not.toBeNull(); expect(inputGroupRequiredClass).not.toBeUndefined(); (fixture.componentInstance as IgxDatePickerReactiveFormComponent).removeValidators(); fixture.detectChanges(); inputGroupRequiredClass = fixture.debugElement.query(By.css('.' + CSS_CLASS_INPUT_GROUP_REQUIRED)); asterisk = window. getComputedStyle(fixture.debugElement.query(By.css('.' + CSS_CLASS_INPUT_GROUP_LABEL)).nativeElement, ':after'). content; expect(inputGroupRequiredClass).toBeNull(); expect(asterisk).toBe('none'); (fixture.componentInstance as IgxDatePickerReactiveFormComponent).addValidators(); fixture.detectChanges(); inputGroupRequiredClass = fixture.debugElement.query(By.css('.' + CSS_CLASS_INPUT_GROUP_REQUIRED)); asterisk = window. getComputedStyle(fixture.debugElement.query(By.css('.' + CSS_CLASS_INPUT_GROUP_LABEL)).nativeElement, ':after'). content; expect(inputGroupRequiredClass).toBeDefined(); expect(inputGroupRequiredClass).not.toBeNull(); expect(asterisk).toBe('"*"'); }); it('Should the weekStart property takes precedence over locale.', fakeAsync(() => { fixture = TestBed.createComponent(IgxDatePickerReactiveFormComponent); fixture.detectChanges(); datePicker = fixture.componentInstance.datePicker; datePicker.locale = 'en'; fixture.detectChanges(); expect(datePicker.weekStart).toEqual(0); datePicker.weekStart = WEEKDAYS.FRIDAY; expect(datePicker.weekStart).toEqual(5); datePicker.locale = 'fr'; fixture.detectChanges(); expect(datePicker.weekStart).toEqual(5); flush(); })); it('Should passing invalid value for locale, then setting weekStart must be respected.', fakeAsync(() => { fixture = TestBed.createComponent(IgxDatePickerReactiveFormComponent); fixture.detectChanges(); datePicker = fixture.componentInstance.datePicker; const locale = 'en-US'; datePicker.locale = locale; fixture.detectChanges(); expect(datePicker.locale).toEqual(locale); expect(datePicker.weekStart).toEqual(WEEKDAYS.SUNDAY) datePicker.locale = 'frrr'; datePicker.weekStart = WEEKDAYS.FRIDAY; fixture.detectChanges(); expect(datePicker.locale).toEqual('en-US'); expect(datePicker.weekStart).toEqual(WEEKDAYS.FRIDAY); })); it('should set initial validity state when the form group is disabled', () => { fixture = TestBed.createComponent(IgxDatePickerReactiveFormComponent); fixture.detectChanges(); datePicker = fixture.componentInstance.datePicker; (fixture.componentInstance as IgxDatePickerReactiveFormComponent).markAsTouched(); fixture.detectChanges(); expect((datePicker as any).inputDirective.valid).toBe(IgxInputState.INVALID); (fixture.componentInstance as IgxDatePickerReactiveFormComponent).disableForm(); fixture.detectChanges(); expect((datePicker as any).inputDirective.valid).toBe(IgxInputState.INITIAL); }); it('should update validity state when programmatically setting errors on reactive form controls', () => { fixture = TestBed.createComponent(IgxDatePickerReactiveFormComponent); fixture.detectChanges(); datePicker = fixture.componentInstance.datePicker; const form = (fixture.componentInstance as IgxDatePickerReactiveFormComponent).form as UntypedFormGroup; // the form control has validators form.markAllAsTouched(); form.get('date').setErrors({ error: true }); fixture.detectChanges(); expect((datePicker as any).inputDirective.valid).toBe(IgxInputState.INVALID); expect((datePicker as any).inputGroup.element.nativeElement.classList.contains(CSS_CLASS_INPUT_GROUP_INVALID)).toBe(true); expect((datePicker as any).inputGroup.element.nativeElement.classList.contains(CSS_CLASS_INPUT_GROUP_REQUIRED)).toBe(true); // remove the validators and set errors (fixture.componentInstance as IgxDatePickerReactiveFormComponent).removeValidators(); form.markAsUntouched(); fixture.detectChanges(); form.markAllAsTouched(); form.get('date').setErrors({ error: true }); fixture.detectChanges(); // no validator, but there is a set error expect((datePicker as any).inputDirective.valid).toBe(IgxInputState.INVALID); expect((datePicker as any).inputGroup.element.nativeElement.classList.contains(CSS_CLASS_INPUT_GROUP_INVALID)).toBe(true); expect((datePicker as any).inputGroup.element.nativeElement.classList.contains(CSS_CLASS_INPUT_GROUP_REQUIRED)).toBe(false); }); }); describe('Projected elements', () => { let fixture: ComponentFixture<IgxDatePickerWithProjectionsComponent>; beforeEach(fakeAsync(() => { fixture = TestBed.createComponent(IgxDatePickerWithProjectionsComponent); fixture.detectChanges(); })); it('Should project label/hint and additional prefix/suffix in the correct location', () => { fixture.componentInstance.datePicker.value = new Date(); fixture.detectChanges(); const inputGroup = fixture.debugElement.query(By.directive(IgxInputGroupComponent)); const label = inputGroup.queryAll(By.directive(IgxLabelDirective)); expect(label).toHaveSize(1); expect(label[0].nativeElement.innerText).toEqual('Label'); const hint = inputGroup.queryAll(By.directive(IgxHintDirective)); expect(hint).toHaveSize(1); expect(hint[0].nativeElement.innerText).toEqual('Hint'); const prefix = inputGroup.queryAll(By.directive(IgxPrefixDirective)); expect(prefix).toHaveSize(2); expect(prefix[0].nativeElement.innerText).toEqual(DATE_PICKER_TOGGLE_ICON); expect(prefix[1].nativeElement.innerText).toEqual('Prefix'); const suffix = inputGroup.queryAll(By.directive(IgxSuffixDirective)); expect(suffix).toHaveSize(2); expect(suffix[0].nativeElement.innerText).toEqual(DATE_PICKER_CLEAR_ICON); expect(suffix[1].nativeElement.innerText).toEqual('Suffix'); }); it('Should project custom toggle/clear and hide defaults', () => { fixture.componentInstance.datePicker.value = new Date(); fixture.componentInstance.showCustomClear = true; fixture.componentInstance.showCustomToggle = true; fixture.detectChanges(); const inputGroup = fixture.debugElement.query(By.directive(IgxInputGroupComponent)); const prefix = inputGroup.queryAll(By.directive(IgxPrefixDirective)); expect(prefix).toHaveSize(2); expect(prefix[0].nativeElement.innerText).toEqual('CustomToggle'); expect(prefix[1].nativeElement.innerText).toEqual('Prefix'); const suffix = inputGroup.queryAll(By.directive(IgxSuffixDirective)); expect(suffix).toHaveSize(2); expect(suffix[0].nativeElement.innerText).toEqual('CustomClear'); expect(suffix[1].nativeElement.innerText).toEqual('Suffix'); }); it('Should correctly sub/unsub to custom toggle and clear', () => { const datePicker = fixture.componentInstance.datePicker; datePicker.value = new Date(); fixture.componentInstance.showCustomClear = true; fixture.componentInstance.showCustomToggle = true; fixture.detectChanges(); spyOn(datePicker, 'open'); spyOn(datePicker, 'clear'); const inputGroup = fixture.debugElement.query(By.directive(IgxInputGroupComponent)); const toggleElem = inputGroup.query(By.directive(IgxPickerToggleComponent)); const clearElem = inputGroup.query(By.directive(IgxPickerClearComponent)); let toggle = fixture.componentInstance.customToggle; let clear = fixture.componentInstance.customClear; expect(toggle.clicked.observers).toHaveSize(1); expect(clear.clicked.observers).toHaveSize(1); const event = jasmine.createSpyObj('event', ['stopPropagation']); toggleElem.triggerEventHandler('click', event); expect(datePicker.open).toHaveBeenCalledTimes(1); clearElem.triggerEventHandler('click', event); expect(datePicker.clear).toHaveBeenCalledTimes(1); // hide fixture.componentInstance.showCustomToggle = false; fixture.detectChanges(); expect(toggle.clicked.observers).toHaveSize(0); expect(clear.clicked.observers).toHaveSize(1); fixture.componentInstance.showCustomClear = false; fixture.detectChanges(); expect(toggle.clicked.observers).toHaveSize(0); expect(clear.clicked.observers).toHaveSize(0); // show again fixture.componentInstance.showCustomClear = true; fixture.componentInstance.showCustomToggle = true; fixture.detectChanges(); toggle = fixture.componentInstance.customToggle; clear = fixture.componentInstance.customClear; expect(toggle.clicked.observers).toHaveSize(1); expect(clear.clicked.observers).toHaveSize(1); datePicker.ngOnDestroy(); expect(toggle.clicked.observers).toHaveSize(0); expect(clear.clicked.observers).toHaveSize(0); }); }); describe('UI Interaction', () => { let fixture: ComponentFixture<any>; let datePicker: IgxDatePickerComponent; beforeEach(fakeAsync(() => { fixture = TestBed.createComponent(IgxDatePickerTestComponent); fixture.detectChanges(); datePicker = fixture.componentInstance.datePicker; })); it('should focus today\'s date when reopening the calendar', fakeAsync(() => { datePicker.clear(); datePicker.open(); expect(datePicker.value).toEqual(null); expect(datePicker.collapsed).toBeFalsy(); datePicker.close(); tick(); fixture.detectChanges(); expect(datePicker.collapsed).toBeTruthy(); datePicker.open(); tick(); fixture.detectChanges(); expect(datePicker.collapsed).toBeFalsy(); const today = new Date().getDate().toString(); expect(document.activeElement.textContent.trim()).toEqual(today); expect(document.activeElement.classList).not.toContain(CSS_CLASS_DATE_SELECTED); })); it('should focus today\'s date when an invalid date is selected', fakeAsync(() => { datePicker.clear(); expect(datePicker.value).toEqual(null); expect(datePicker.collapsed).toBeTruthy(); datePicker.select(new Date('test')); datePicker.open(); tick(); fixture.detectChanges(); expect(datePicker.collapsed).toBeFalsy(); const today = new Date().getDate().toString(); expect(document.activeElement.textContent.trim()).toEqual(today); expect(document.activeElement.classList).not.toContain(CSS_CLASS_DATE_SELECTED); })); }); }); describe('Unit Tests', () => { let overlay: IgxOverlayService; let mockOverlayEventArgs: OverlayEventArgs & OverlayCancelableEventArgs; let mockInjector; let mockCdr; let mockInputGroup: Partial<IgxInputGroupComponent>; let datePicker: IgxDatePickerComponent; let mockDateEditor: any; let mockCalendar: Partial<IgxCalendarComponent>; let mockInputDirective: any; const viewsContainerRef = {} as any; const mockOverlayId = '1'; const today = new Date(); const elementRef = { nativeElement: jasmine.createSpyObj<HTMLElement>('mockElement', ['blur', 'click', 'focus']) }; let mockNgControl: any; let mockControlInstance: any; let renderer2: Renderer2; beforeEach(() => { renderer2 = jasmine.createSpyObj('Renderer2', ['setAttribute'], [{}, 'aria-labelledby', 'test-label-id-1']); 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 }; mockInjector = jasmine.createSpyObj('Injector', { get: mockNgControl }); mockCdr = jasmine.createSpyObj('ChangeDetectorRef', ['detectChanges']); mockCalendar = { selected: new EventEmitter<any>() }; const mockComponentInstance = { calendar: mockCalendar, todaySelection: new EventEmitter<any>(), calendarClose: new EventEmitter<any>() }; const mockComponentRef = { instance: mockComponentInstance } as any; mockOverlayEventArgs = { id: mockOverlayId, componentRef: mockComponentRef, cancel: false }; overlay = { opening: new EventEmitter<OverlayCancelableEventArgs>(), opened: new EventEmitter<OverlayEventArgs>(), closed: new EventEmitter<OverlayEventArgs>(), closing: new EventEmitter<OverlayClosingEventArgs>(), show(..._args) { this.opening.emit(Object.assign({}, mockOverlayEventArgs, { cancel: false })); this.opened.emit(mockOverlayEventArgs); }, hide(..._args) { this.closing.emit(Object.assign({}, mockOverlayEventArgs, { cancel: false })); this.closed.emit(mockOverlayEventArgs); }, detach: (..._args) => { }, attach: (..._args) => mockOverlayId } as any; mockDateEditor = { _value: null, get value() { return this._value; }, clear() { this.valueChange.emit(null); }, set value(val: any) { this._value = val; }, valueChange: new EventEmitter<any>(), validationFailed: new EventEmitter<any>() }; 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; mockInputDirective = { valid: IgxInputState.INITIAL, 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: () => { } }; datePicker = new IgxDatePickerComponent(elementRef, 'en-US', overlay, mockInjector, renderer2, null, mockCdr); (datePicker as any).inputGroup = mockInputGroup; (datePicker as any).inputDirective = mockInputDirective; (datePicker as any).dateTimeEditor = mockDateEditor; (datePicker as any).viewContainerRef = viewsContainerRef; // TODO: TEMP workaround for afterViewInit call in unit tests: datePicker.clearComponents = new QueryList<any>(); datePicker.toggleComponents = new QueryList<any>(); }); afterEach(() => { datePicker?.ngOnDestroy(); UIInteractions.clearOverlay(); }); describe('API tests', () => { registerLocaleData(localeES); it('Should initialize and update all inputs properly', () => { // no ngControl initialized expect(datePicker.required).toEqual(false); datePicker.ngOnInit(); datePicker.ngAfterViewInit(); expect(datePicker.collapsed).toBeTruthy(); expect(datePicker.disabled).toBeFalsy(); expect(datePicker.disabledDates).toEqual(null); expect(datePicker.displayDensity).toEqual(DisplayDensity.comfortable); expect(datePicker.displayFormat).toEqual(undefined); expect(datePicker.calendarFormat).toEqual(undefined); expect(datePicker.displayMonthsCount).toEqual(1); expect(datePicker.formatViews).toEqual(undefined); expect(datePicker.headerOrientation).toEqual(PickerHeaderOrientation.Horizontal); expect(datePicker.hideOutsideDays).toEqual(undefined); expect(datePicker.inputFormat).toEqual(undefined); expect(datePicker.mode).toEqual(PickerInteractionMode.DropDown); expect(datePicker.isDropdown).toEqual(true); expect(datePicker.minValue).toEqual(undefined); expect(datePicker.maxValue).toEqual(undefined); expect(datePicker.outlet).toEqual(undefined); expect(datePicker.specialDates).toEqual(null); expect(datePicker.spinDelta).toEqual(undefined); expect(datePicker.spinLoop).toEqual(true); expect(datePicker.tabIndex).toEqual(undefined); expect(datePicker.overlaySettings).toEqual(undefined); expect(datePicker.locale).toEqual('en-US'); expect(datePicker.placeholder).toEqual(''); expect(datePicker.readOnly).toEqual(false); expect(datePicker.value).toEqual(undefined); expect(datePicker.formatter).toEqual(undefined); expect(() => datePicker.displayValue.transform(today)).toThrow(); // set datePicker.open(); overlay.opened.emit(mockOverlayEventArgs); expect(datePicker.collapsed).toBeFalsy(); datePicker.disabled = true; expect(datePicker.disabled).toBeTruthy(); datePicker.disabled = false; datePicker.setDisabledState(true); expect(datePicker.disabled).toBeTruthy(); datePicker.disabled = false; const mockDisabledDates: DateRangeDescriptor[] = [{ type: DateRangeType.Weekdays }, { type: DateRangeType.Before, dateRange: [today] }]; datePicker.disabledDates = mockDisabledDates; expect(datePicker.disabledDates).toEqual(mockDisabledDates); spyOn(datePicker.densityChanged, 'emit').and.callThrough(); datePicker.displayDensity = DisplayDensity.cosy; expect(datePicker.displayDensity).toEqual(DisplayDensity.cosy); // if no base token is provided, _displayDensity is undefined // first emit of below event will always be w/ oldDensity === undefined expect(datePicker.densityChanged.emit) .toHaveBeenCalledWith({ oldDensity: undefined, newDensity: DisplayDensity.cosy }); datePicker.displayFormat = 'MM/yy/DD'; expect(datePicker.displayFormat).toEqual('MM/yy/DD'); datePicker.displayMonthsCount = Infinity; expect(datePicker.displayMonthsCount).toEqual(Infinity); datePicker.displayMonthsCount = 0; expect(datePicker.displayMonthsCount).toEqual(0); datePicker.displayMonthsCount = 12; expect(datePicker.displayMonthsCount).toEqual(12); let newFormat: any = { day: '2-digit' }; datePicker.calendarFormat = newFormat; // this SHOULD NOT mutate the underlying base settings expect((datePicker as any).pickerCalendarFormat).toEqual({ day: '2-digit', month: 'short', weekday: 'short', year: 'numeric' }); newFormat = { month: 'numeric' }; datePicker.calendarFormat = newFormat; expect((datePicker as any).pickerCalendarFormat).toEqual({ day: 'numeric', month: 'numeric', weekday: 'short', year: 'numeric' }); datePicker.formatViews = null; expect((datePicker as any).pickerFormatViews).toEqual({ day: false, month: true, year: false }); const formatViewVal: IFormattingViews = {}; datePicker.formatViews = formatViewVal; expect((datePicker as any).pickerFormatViews).toEqual({ day: false, month: true, year: false }); formatViewVal.day = true; datePicker.formatViews = formatViewVal; expect((datePicker as any).pickerFormatViews).toEqual({ day: true, month: true, year: false }); formatViewVal.year = true; datePicker.formatViews = formatViewVal; expect((datePicker as any).pickerFormatViews).toEqual({ day: true, month: true, year: true }); formatViewVal.month = false; datePicker.formatViews = formatViewVal; expect((datePicker as any).pickerFormatViews).toEqual({ day: true, month: false, year: true }); datePicker.headerOrientation = PickerHeaderOrientation.Vertical; expect(datePicker.headerOrientation).toEqual(PickerHeaderOrientation.Vertical); datePicker.hideOutsideDays = false; expect(datePicker.hideOutsideDays).toEqual(false); datePicker.hideOutsideDays = true; expect(datePicker.hideOutsideDays).toEqual(true); datePicker.inputFormat = 'dd/MM/YY'; expect(datePicker.inputFormat).toEqual('dd/MM/YY'); datePicker.mode = PickerInteractionMode.Dialog; expect(datePicker.mode).toEqual(PickerInteractionMode.Dialog); expect(datePicker.isDropdown).toEqual(false); datePicker.minValue = 'Test'; expect(datePicker.minValue).toEqual('Test'); datePicker.minValue = today; expect(datePicker.minValue).toEqual(today); datePicker.minValue = '12/12/1998'; expect(datePicker.minValue).toEqual('12/12/1998'); datePicker.maxValue = 'Test'; expect(datePicker.maxValue).toEqual('Test'); datePicker.maxValue = today; expect(datePicker.maxValue).toEqual(today); datePicker.maxValue = '12/12/1998'; expect(datePicker.maxValue).toEqual('12/12/1998'); datePicker.outlet = null; expect(datePicker.outlet).toEqual(null); const mockEl: ElementRef = jasmine.createSpyObj<ElementRef>('mockEl', ['nativeElement']); datePicker.outlet = mockEl; expect(datePicker.outlet).toEqual(mockEl); const mockOverlayDirective: IgxOverlayOutletDirective = jasmine.createSpyObj<IgxOverlayOutletDirective>('mockEl', ['nativeElement']); datePicker.outlet = mockOverlayDirective; expect(datePicker.outlet).toEqual(mockOverlayDirective); const specialDates: DateRangeDescriptor[] = [{ type: DateRangeType.Weekdays }, { type: DateRangeType.Before, dateRange: [today] }]; datePicker.specialDates = specialDates; expect(datePicker.specialDates).toEqual(specialDates); const spinDeltaSettings = { date: Infinity, month: Infinity }; datePicker.spinDelta = spinDeltaSettings; expect(datePicker.spinDelta).toEqual(spinDeltaSettings); datePicker.spinLoop = false; expect(datePicker.spinLoop).toEqual(false); datePicker.tabIndex = 0; expect(datePicker.tabIndex).toEqual(0); datePicker.tabIndex = -1; expect(datePicker.tabIndex).toEqual(-1); const customSettings: OverlaySettings = { modal: true, closeOnEscape: true }; datePicker.overlaySettings = customSettings; expect(datePicker.overlaySettings).toEqual(customSettings); datePicker.locale = 'ES'; expect(datePicker.locale).toEqual('ES'); datePicker.placeholder = 'Buenos dias, muchachos'; expect(datePicker.placeholder).toEqual('Buenos dias, muchachos'); datePicker.readOnly = true; expect(datePicker.readOnly).toEqual(true); spyOn(datePicker.valueChange, 'emit').and.callThrough(); datePicker.value = today; expect(datePicker.value).toEqual(today); expect(mockDateEditor.value).toEqual(today); expect(datePicker.valueChange.emit).toHaveBeenCalledWith(today); const newDate = new Date('02/02/2002'); const boundObject = { date: newDate }; datePicker.value = boundObject.date; expect(datePicker.value).toEqual(newDate); expect(mockDateEditor.value).toEqual(newDate); expect(datePicker.valueChange.emit).toHaveBeenCalledWith(newDate); expect(boundObject.date).toEqual(newDate); datePicker.value = '2003-03-03'; expect(datePicker.value).toEqual('2003-03-03'); // expect(mockDateEditor.value).toEqual('03/03/2003'); expect(datePicker.valueChange.emit).not.toHaveBeenCalledWith('2003-03-03' as any); const customFormatter: (val: Date) => string = (val: Date) => val.getFullYear().toString(); datePicker.formatter = customFormatter; expect(datePicker.formatter).toEqual(customFormatter); expect(datePicker.displayValue.transform(today)).toEqual(today.getFullYear().toString()); }); it('Should properly set date w/ `selectToday` methods', () => { spyOn(datePicker, 'select'); spyOn(datePicker, 'close'); const now = new Date(); now.setHours(0); now.setMinutes(0); now.setSeconds(0); now.setMilliseconds(0); datePicker.selectToday(); expect(datePicker.select).toHaveBeenCalledWith(now); expect(datePicker.close).toHaveBeenCalled(); }); it('Should call underlying dateEditor decrement and increment methods', () => { mockDateEditor.decrement = jasmine.createSpy(); mockDateEditor.increment = jasmine.createSpy(); datePicker.decrement(); expect(mockDateEditor.decrement).toHaveBeenCalledWith(undefined, undefined); const mockDatePart = {} as DatePart; datePicker.decrement(mockDatePart); expect(mockDateEditor.decrement).toHaveBeenCalledWith(mockDatePart, undefined); datePicker.decrement(mockDatePart, 0); expect(mockDateEditor.decrement).toHaveBeenCalledWith(mockDatePart, 0); datePicker.decrement(mockDatePart, Infinity); expect(mockDateEditor.decrement).toHaveBeenCalledWith(mockDatePart, Infinity); datePicker.increment(); expect(mockDateEditor.increment).toHaveBeenCalledWith(undefined, undefined); datePicker.increment(mockDatePart); expect(mockDateEditor.increment).toHaveBeenCalledWith(mockDatePart, undefined); datePicker.increment(mockDatePart, 0); expect(mockDateEditor.increment).toHaveBeenCalledWith(mockDatePart, 0); datePicker.increment(mockDatePart, Infinity); expect(mockDateEditor.increment).toHaveBeenCalledWith(mockDatePart, Infinity); }); it('Should call underlying overlay `open` and `attach` methods with proper settings', () => { spyOn(overlay, 'attach').and.returnValue(mockOverlayId); spyOn(overlay, 'detach'); spyOn(overlay, 'show'); spyOn(overlay, 'hide'); const baseDialogSettings: OverlaySettings = Object.assign( {}, (datePicker as any)._dialogOverlaySettings ); const baseDropdownSettings: OverlaySettings = Object.assign( {}, (datePicker as any)._dropDownOverlaySettings, { target: mockInputGroup.element.nativeElement } ); const collapsedSpy = spyOnProperty(datePicker, 'collapsed', 'get'); collapsedSpy.and.returnValue(false); datePicker.disabled = false; datePicker.open(); expect(overlay.attach).not.toHaveBeenCalled(); collapsedSpy.and.returnValue(true); datePicker.disabled = true; datePicker.open(); expect(overlay.attach).not.toHaveBeenCalled(); collapsedSpy.and.returnValue(false); datePicker.open(); expect(overlay.attach).not.toHaveBeenCalled(); collapsedSpy.and.returnValue(true); datePicker.disabled = false; const isDropdownSpy = spyOnProperty(datePicker, 'isDropdown', 'get'); isDropdownSpy.and.returnValue(false); datePicker.open(); expect(overlay.attach).toHaveBeenCalledWith(IgxCalendarContainerComponent, viewsContainerRef, baseDialogSettings); expect(overlay.show).toHaveBeenCalledWith(mockOverlayId); isDropdownSpy.and.returnValue(true); datePicker.open(); expect(overlay.attach).toHaveBeenCalledWith(IgxCalendarContainerComponent, viewsContainerRef, baseDropdownSettings); expect(overlay.show).toHaveBeenCalledWith(mockOverlayId); const mockOutlet = {} as any; datePicker.outlet = mockOutlet; datePicker.open(); expect(overlay.attach).toHaveBeenCalledWith( IgxCalendarContainerComponent, viewsContainerRef, Object.assign({}, baseDropdownSettings, { outlet: mockOutlet }), ); expect(overlay.show).toHaveBeenCalledWith(mockOverlayId); let mockSettings: OverlaySettings = { closeOnEscape: true, closeOnOutsideClick: true, modal: false }; datePicker.outlet = null; datePicker.open(mockSettings); expect(overlay.attach).toHaveBeenCalledWith( IgxCalendarContainerComponent, viewsContainerRef, Object.assign({}, baseDropdownSettings, mockSettings), ); expect(overlay.show).toHaveBeenCalledWith(mockOverlayId); isDropdownSpy.and.returnValue(false); mockSettings = { closeOnEscape: false, closeOnOutsideClick: false, modal: false }; datePicker.open(mockSettings); expect(overlay.attach).toHaveBeenCalledWith( IgxCalendarContainerComponent, viewsContainerRef, Object.assign({}, baseDialogSettings, mockSettings), ); expect(overlay.show).toHaveBeenCalledWith(mockOverlayId); isDropdownSpy.and.returnValue(true); datePicker.overlaySettings = { modal: false