/** * calendar.component.spec */ import { async, ComponentFixture, inject, TestBed } from '@angular/core/testing'; import { Component, NgZone } from '@angular/core'; import { MockNgZone, dispatchFakeEvent, dispatchMouseEvent, dispatchKeyboardEvent } from './helper/test-helpers'; import { pNativeDateTimeModule } from './adapter/native-date-time.module'; import { pDateTimeModule } from './date-time.module'; import { pCalendarComponent } from './calendar.component'; import { pDateTimeIntl } from './date-time-picker-intl.service'; import { By } from '@angular/platform-browser'; import { ENTER, RIGHT_ARROW } from '@angular/cdk/keycodes'; import { pMonthViewComponent } from './calendar-month-view.component'; import { pYearViewComponent } from './calendar-year-view.component'; import { pMultiYearViewComponent } from './calendar-multi-year-view.component'; export const JAN = 0, FEB = 1, MAR = 2, APR = 3, MAY = 4, JUN = 5, JUL = 6, AUG = 7, SEP = 8, OCT = 9, NOV = 10, DEC = 11; describe('pCalendarComponent', () => { let zone: MockNgZone; beforeEach(async(() => { TestBed.configureTestingModule({ imports: [pNativeDateTimeModule, pDateTimeModule], declarations: [ StandardCalendar, CalendarWithMinMax, CalendarWithDateFilter ], providers: [ pDateTimeIntl, { provide: NgZone, useFactory: () => (zone = new MockNgZone()) } ] }).compileComponents(); })); describe('standard calendar', () => { let fixture: ComponentFixture; let testComponent: StandardCalendar; let calendarElement: HTMLElement; let periodButton: HTMLElement; let calendarInstance: pCalendarComponent; beforeEach(() => { fixture = TestBed.createComponent(StandardCalendar); fixture.detectChanges(); let calendarDebugElement = fixture.debugElement.query( By.directive(pCalendarComponent) ); calendarElement = calendarDebugElement.nativeElement; periodButton = calendarElement.querySelector( '.p-dt-control-period-button' ) as HTMLElement; calendarInstance = calendarDebugElement.componentInstance; testComponent = fixture.componentInstance; }); it('should be in month view with specified month active', async(() => { expect(calendarInstance.currentView).toBe('month'); expect(calendarInstance.pickerMoment).toEqual( new Date(2018, JAN, 31) ); })); it('should select date in month view', () => { let monthCell = calendarElement.querySelector( '[aria-label="January 31, 2018"]' ); (monthCell as HTMLElement).click(); fixture.detectChanges(); expect(calendarInstance.currentView).toBe('month'); expect(testComponent.selected).toEqual(new Date(2018, JAN, 31)); }); it('should emit the selected month on cell clicked in year view', () => { periodButton.click(); fixture.detectChanges(); expect(calendarInstance.currentView).toBe('multi-years'); expect(calendarInstance.pickerMoment).toEqual( new Date(2018, JAN, 31) ); (calendarElement.querySelector( '.p-dt-calendar-cell-active' ) as HTMLElement).click(); fixture.detectChanges(); expect(calendarInstance.currentView).toBe('year'); (calendarElement.querySelector( '.p-dt-calendar-cell-active' ) as HTMLElement).click(); const normalizedMonth: Date = fixture.componentInstance.selectedMonth; expect(normalizedMonth.getMonth()).toEqual(0); }); it('should emit the selected year on cell clicked in multi-years view', () => { periodButton.click(); fixture.detectChanges(); expect(calendarInstance.currentView).toBe('multi-years'); expect(calendarInstance.pickerMoment).toEqual( new Date(2018, JAN, 31) ); (calendarElement.querySelector( '.p-dt-calendar-cell-active' ) as HTMLElement).click(); fixture.detectChanges(); const normalizedYear: Date = fixture.componentInstance.selectedYear; expect(normalizedYear.getFullYear()).toEqual(2018); }); it('should re-render when the i18n labels have changed', () => { inject([pDateTimeIntl], (intl: pDateTimeIntl) => { const button = fixture.debugElement.nativeElement.querySelector( '.p-dt-control-period-button' ); intl.switchToMultiYearViewLabel = 'Go to multi-year view?'; intl.changes.next(); fixture.detectChanges(); expect(button.getAttribute('aria-label')).toBe( 'Go to multi-year view?' ); }); }); it('should set all buttons to be `type="button"`', () => { const invalidButtons = calendarElement.querySelectorAll( 'button:not([type="button"])' ); expect(invalidButtons.length).toBe(0); }); describe('a11y', () => { describe('calendar body', () => { let calendarMainEl: HTMLElement; beforeEach(() => { calendarMainEl = calendarElement.querySelector( '.p-dt-calendar-main' ) as HTMLElement; expect(calendarMainEl).not.toBeNull(); dispatchFakeEvent(calendarMainEl, 'focus'); fixture.detectChanges(); }); it('should initially set pickerMoment', () => { expect(calendarInstance.pickerMoment).toEqual( new Date(2018, JAN, 31) ); }); it('should make the calendar main focusable', () => { expect(calendarMainEl.getAttribute('tabindex')).toBe('-1'); }); it('should not move focus to the active cell on init', () => { const activeCell = calendarMainEl.querySelector( '.p-dt-calendar-cell-active' )! as HTMLElement; spyOn(activeCell, 'focus').and.callThrough(); fixture.detectChanges(); zone.simulateZoneExit(); expect(activeCell.focus).not.toHaveBeenCalled(); }); it('should move focus to the active cell when the view changes', () => { const activeCell = calendarMainEl.querySelector( '.p-dt-calendar-cell-active' )! as HTMLElement; spyOn(activeCell, 'focus').and.callThrough(); fixture.detectChanges(); zone.simulateZoneExit(); expect(activeCell.focus).not.toHaveBeenCalled(); calendarInstance.currentView = 'multi-years'; fixture.detectChanges(); zone.simulateZoneExit(); expect(activeCell.focus).toHaveBeenCalled(); }); describe('year view', () => { beforeEach(() => { dispatchMouseEvent(periodButton, 'click'); fixture.detectChanges(); expect(calendarInstance.currentView).toBe( 'multi-years' ); (calendarMainEl.querySelector( '.p-dt-calendar-cell-active' ) as HTMLElement).click(); fixture.detectChanges(); expect(calendarInstance.currentView).toBe('year'); }); it('should return to month view on enter', () => { const tableBodyEl = calendarMainEl.querySelector( '.p-dt-calendar-body' ) as HTMLElement; dispatchKeyboardEvent( tableBodyEl, 'keydown', RIGHT_ARROW ); fixture.detectChanges(); dispatchKeyboardEvent(tableBodyEl, 'keydown', ENTER); fixture.detectChanges(); expect(calendarInstance.currentView).toBe('month'); expect(calendarInstance.pickerMoment).toEqual( new Date(2018, FEB, 28) ); expect(testComponent.selected).toBeUndefined(); }); }); describe('multi-years view', () => { beforeEach(() => { dispatchMouseEvent(periodButton, 'click'); fixture.detectChanges(); expect(calendarInstance.currentView).toBe( 'multi-years' ); }); it('should return to year view on enter', () => { const tableBodyEl = calendarMainEl.querySelector( '.p-dt-calendar-body' ) as HTMLElement; dispatchKeyboardEvent( tableBodyEl, 'keydown', RIGHT_ARROW ); fixture.detectChanges(); dispatchKeyboardEvent(tableBodyEl, 'keydown', ENTER); fixture.detectChanges(); expect(calendarInstance.currentView).toBe('year'); expect(calendarInstance.pickerMoment).toEqual( new Date(2019, JAN, 31) ); expect(testComponent.selected).toBeUndefined(); }); }); }); }); }); describe('calendar with min and max', () => { let fixture: ComponentFixture; let testComponent: CalendarWithMinMax; let calendarElement: HTMLElement; let calendarInstance: pCalendarComponent; beforeEach(() => { fixture = TestBed.createComponent(CalendarWithMinMax); fixture.detectChanges(); let calendarDebugElement = fixture.debugElement.query( By.directive(pCalendarComponent) ); calendarElement = calendarDebugElement.nativeElement; calendarInstance = calendarDebugElement.componentInstance; testComponent = fixture.componentInstance; }); it('should re-render the month view when the minDate changes', () => { let monthViewDebugElm = fixture.debugElement.query( By.directive(pMonthViewComponent) ); let monthViewComp = monthViewDebugElm.componentInstance; expect(monthViewComp).toBeTruthy(); spyOn(monthViewComp, 'generateCalendar').and.callThrough(); testComponent.minDate = new Date(2017, NOV, 1); fixture.detectChanges(); expect(monthViewComp.generateCalendar).toHaveBeenCalled(); }); it('should re-render the month view when the maxDate changes', () => { let monthViewDebugElm = fixture.debugElement.query( By.directive(pMonthViewComponent) ); let monthViewComp = monthViewDebugElm.componentInstance; expect(monthViewComp).toBeTruthy(); spyOn(monthViewComp, 'generateCalendar').and.callThrough(); testComponent.maxDate = new Date(2017, NOV, 1); fixture.detectChanges(); expect(monthViewComp.generateCalendar).toHaveBeenCalled(); }); it('should re-render the year view when the minDate changes', () => { fixture.detectChanges(); const periodButton = calendarElement.querySelector( '.p-dt-control-period-button' ) as HTMLElement; periodButton.click(); fixture.detectChanges(); (calendarElement.querySelector( '.p-dt-calendar-cell-active' ) as HTMLElement).click(); fixture.detectChanges(); let yearViewDebugElm = fixture.debugElement.query( By.directive(pYearViewComponent) ); let yearViewComp = yearViewDebugElm.componentInstance; expect(yearViewComp).toBeTruthy(); spyOn(yearViewComp, 'generateMonthList').and.callThrough(); testComponent.minDate = new Date(2017, NOV, 1); fixture.detectChanges(); expect(yearViewComp.generateMonthList).toHaveBeenCalled(); }); it('should re-render the year view when the maxDate changes', () => { fixture.detectChanges(); const periodButton = calendarElement.querySelector( '.p-dt-control-period-button' ) as HTMLElement; periodButton.click(); fixture.detectChanges(); (calendarElement.querySelector( '.p-dt-calendar-cell-active' ) as HTMLElement).click(); fixture.detectChanges(); let yearViewDebugElm = fixture.debugElement.query( By.directive(pYearViewComponent) ); let yearViewComp = yearViewDebugElm.componentInstance; expect(yearViewComp).toBeTruthy(); spyOn(yearViewComp, 'generateMonthList').and.callThrough(); testComponent.maxDate = new Date(2017, NOV, 1); fixture.detectChanges(); expect(yearViewComp.generateMonthList).toHaveBeenCalled(); }); it('should re-render the multi-years view when the minDate changes', () => { fixture.detectChanges(); const periodButton = calendarElement.querySelector( '.p-dt-control-period-button' ) as HTMLElement; periodButton.click(); fixture.detectChanges(); let multiYearsViewDebugElm = fixture.debugElement.query( By.directive(pMultiYearViewComponent) ); let multiYearsViewComp = multiYearsViewDebugElm.componentInstance; expect(multiYearsViewComp).toBeTruthy(); spyOn(multiYearsViewComp, 'generateYearList').and.callThrough(); testComponent.minDate = new Date(2017, NOV, 1); fixture.detectChanges(); expect(multiYearsViewComp.generateYearList).toHaveBeenCalled(); }); it('should re-render the multi-years view when the maxDate changes', () => { fixture.detectChanges(); const periodButton = calendarElement.querySelector( '.p-dt-control-period-button' ) as HTMLElement; periodButton.click(); fixture.detectChanges(); let multiYearsViewDebugElm = fixture.debugElement.query( By.directive(pMultiYearViewComponent) ); let multiYearsViewComp = multiYearsViewDebugElm.componentInstance; expect(multiYearsViewComp).toBeTruthy(); spyOn(multiYearsViewComp, 'generateYearList').and.callThrough(); testComponent.maxDate = new Date(2017, NOV, 1); fixture.detectChanges(); expect(multiYearsViewComp.generateYearList).toHaveBeenCalled(); }); }); describe('calendar with date filter', () => { let fixture: ComponentFixture; let testComponent: CalendarWithDateFilter; let calendarElement: HTMLElement; let calendarInstance: pCalendarComponent; beforeEach(() => { fixture = TestBed.createComponent(CalendarWithDateFilter); fixture.detectChanges(); let calendarDebugElement = fixture.debugElement.query( By.directive(pCalendarComponent) ); calendarElement = calendarDebugElement.nativeElement; calendarInstance = calendarDebugElement.componentInstance; testComponent = fixture.componentInstance; }); it('should disable and prevent selection of filtered dates', () => { let monthCell = calendarElement.querySelector( '[aria-label="January 2, 2018"]' ); expect(testComponent.selected).toBeFalsy(); (monthCell as HTMLElement).click(); fixture.detectChanges(); expect(testComponent.selected).toEqual(new Date(2018, JAN, 2)); }); }); }); @Component({ template: ` ` }) class StandardCalendar { selectMode = 'single'; selected: Date; selectedYear: Date; selectedMonth: Date; pickerMoment = new Date(2018, JAN, 31); } @Component({ template: ` ` }) class CalendarWithMinMax { selectMode = 'single'; startAt: Date; minDate = new Date(2016, JAN, 1); maxDate = new Date(2019, JAN, 1); pickerMoment = new Date(2018, JAN, 31); } @Component({ template: ` ` }) class CalendarWithDateFilter { selectMode = 'single'; selected: Date; pickerMoment = new Date(2018, JAN, 31); dateFilter(date: Date) { return !(date.getDate() % 2) && date.getMonth() !== NOV; } }