/// /// Copyright 2015-2026 Micro Focus or one of its affiliates. /// /// Licensed under the Apache License, Version 2.0 (the "License"); /// you may not use this file except in compliance with the License. /// You may obtain a copy of the License at /// /// http://www.apache.org/licenses/LICENSE-2.0 /// /// Unless required by applicable law or agreed to in writing, software /// distributed under the License is distributed on an "AS IS" BASIS, /// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. /// See the License for the specific language governing permissions and /// limitations under the License. /// import { Component } from '@angular/core'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { FormsModule } from '@angular/forms'; import { PopoverModule } from '../../components/popover'; import { IconModule } from '../icon'; import { DateTimePickerModule } from './date-time-picker.module'; import { DateTimePickerTimezone } from './date-time-picker.utils'; @Component({ selector: 'ux-components-date-time-picker', template: ` `, imports: [DateTimePickerModule, IconModule, PopoverModule, FormsModule], }) export class DateTimePickerTestComponent { // eslint-disable-next-line @typescript-eslint/no-empty-function onDateChange(): void {} // eslint-disable-next-line @typescript-eslint/no-empty-function onTimezoneChange(value: DateTimePickerTimezone): void {} date: Date; showTime: boolean = false; showDate: boolean = true; showTimezone: boolean; showMeridian: boolean = true; showSpinners: boolean = true; timezone: DateTimePickerTimezone; timezones: DateTimePickerTimezone[]; } describe('Date Time Picker', () => { let component: DateTimePickerTestComponent; let fixture: ComponentFixture; let nativeElement: HTMLElement; let onTimezoneChangeSpy: jasmine.Spy; let getTimezoneOffset: () => number; beforeAll(() => { getTimezoneOffset = Date.prototype.getTimezoneOffset; Date.prototype.getTimezoneOffset = () => { return 0; }; }); afterAll(() => { Date.prototype.getTimezoneOffset = getTimezoneOffset; }); beforeEach(() => { TestBed.configureTestingModule({ imports: [ DateTimePickerModule, IconModule, PopoverModule, FormsModule, DateTimePickerTestComponent, ], }).compileComponents(); }); beforeEach(() => { fixture = TestBed.createComponent(DateTimePickerTestComponent); component = fixture.componentInstance; nativeElement = fixture.nativeElement; onTimezoneChangeSpy = spyOn(component, 'onTimezoneChange'); fixture.detectChanges(); }); it('should initialise correctly', () => { expect(component).toBeTruthy(); }); it('should update date and call onDateChange when start date is changed ', async () => { spyOn(component, 'onDateChange'); component.date = new Date(2020, 0, 7); fixture.detectChanges(); await fixture.whenStable(); expect(getHeader().innerHTML).toBe(' January 2020 '); expect(component.onDateChange).toHaveBeenCalled(); }); it('should not cause an error when dates set to undefined', async () => { const now = new Date(); component.date = undefined; fixture.detectChanges(); await fixture.whenStable(); expect(getHeader().innerHTML.trim()).toBe( now.toLocaleDateString('en-US', { month: 'long', year: 'numeric' }) ); expect(getSelected()).toBeFalsy(); }); it('should display the correct date when the last day in the month is selected', async () => { component.date = new Date(2020, 1, 18); fixture.detectChanges(); await fixture.whenStable(); expect(component.date.getDate()).toBe(18); expect(getSelected().innerHTML).toBe(' 18 '); component.date = new Date(2020, 2, 31); fixture.detectChanges(); await fixture.whenStable(); expect(component.date.getDate()).toBe(31); expect(getSelected().innerHTML).toBe(' 31 '); }); it('should allow a half time zone to be set if it is present in the timezone array and not call timezoneChange', async () => { // Ignore the initial call from timezone being set to default onTimezoneChangeSpy.calls.reset(); component.showTime = true; component.showTimezone = true; component.timezones = [ { name: 'GMT-3.5', offset: 210 }, { name: 'GMT-3', offset: 180 }, { name: 'GMT-2', offset: 120 }, { name: 'GMT-1', offset: 60 }, { name: 'GMT', offset: 0 }, ]; component.timezone = { name: 'GMT-3.5', offset: 210 }; fixture.detectChanges(); await fixture.whenStable(); expect(getTimezone().value).toBe('GMT-3.5'); expect(component.onTimezoneChange).not.toHaveBeenCalled(); }); it('should not allow a timezone to be set that is not in the provided timezone list and default to GMT', async () => { // Verify the initial `timezone` change from undefined to GMT and reset the spy expect(component.onTimezoneChange).toHaveBeenCalledWith({ name: 'GMT', offset: 0 }); expect(component.onTimezoneChange).toHaveBeenCalledTimes(1); onTimezoneChangeSpy.calls.reset(); component.showTime = true; component.showTimezone = true; component.timezones = [ { name: 'GMT-3.5', offset: 210 }, { name: 'GMT-3', offset: 180 }, { name: 'GMT-2', offset: 120 }, { name: 'GMT-1', offset: 60 }, { name: 'GMT', offset: 0 }, ]; component.timezone = { name: 'GMT-3.5x', offset: 3000 }; fixture.detectChanges(); await fixture.whenStable(); expect(getTimezone().value).toBe('GMT'); // Timezone was changed to GMT on initialization, so we do not expect another call with the same value expect(component.onTimezoneChange).not.toHaveBeenCalled(); }); it('should not allow a timezone to be set that is not in the default timezone list and default to GMT', async () => { // Verify the initial `timezone` change from undefined to GMT and reset the spy expect(component.onTimezoneChange).toHaveBeenCalledWith({ name: 'GMT', offset: 0 }); expect(component.onTimezoneChange).toHaveBeenCalledTimes(1); onTimezoneChangeSpy.calls.reset(); component.showTime = true; component.showTimezone = true; component.timezone = { name: 'GMT-3.5x', offset: 3000 }; fixture.detectChanges(); await fixture.whenStable(); expect(getTimezone().value).toBe('GMT'); // Timezone was changed to GMT on initialization, so we do not expect another call with the same value expect(component.onTimezoneChange).not.toHaveBeenCalled(); }); it('should update only the time is changed', async () => { component.date = new Date(2020, 5, 2, 11); component.showTime = true; fixture.detectChanges(); await fixture.whenStable(); component.date = new Date(2020, 5, 2, 13, 45); fixture.detectChanges(); await fixture.whenStable(); expect(getHours().value).toBe('1'); expect(getMinutes().value).toBe('45'); expect(getMeridianPM().classList).toContain('active'); }); function getHeader(): HTMLElement | null { return nativeElement.querySelector('ux-date-time-picker .header-title'); } function getSelected(): HTMLElement | null { return nativeElement.querySelector('ux-date-time-picker-day-view .active'); } function getTimezone(): HTMLInputElement { return nativeElement.querySelector( 'ux-date-time-picker-time-view .time-zone-picker ux-spin-button input' ); } function getHours(): HTMLInputElement { return nativeElement.querySelector( 'ux-date-time-picker-time-view .time-hours-picker ux-spin-button input' ); } function getMinutes(): HTMLInputElement { return nativeElement.querySelector( 'ux-date-time-picker-time-view .time-minutes-picker ux-spin-button input' ); } function getMeridianPM(): HTMLInputElement { return nativeElement.querySelector('.time-picker-meridian .btn-group button:last-child'); } });