/// /// 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 { DateTimePickerTimezone } from '../date-time-picker'; import { IconModule } from '../icon'; import { DateRangePickerModule } from './date-range-picker.module'; @Component({ selector: 'ux-components-date-range-picker', template: ` `, imports: [DateRangePickerModule, IconModule, PopoverModule, FormsModule], }) export class DateRangePickerComponent { // eslint-disable-next-line @typescript-eslint/no-empty-function onStartChange(): void {} // eslint-disable-next-line @typescript-eslint/no-empty-function onEndChange(): void {} // eslint-disable-next-line @typescript-eslint/no-empty-function onStartTimezoneChange(value: DateTimePickerTimezone): void {} // eslint-disable-next-line @typescript-eslint/no-empty-function onEndTimezoneChange(value: DateTimePickerTimezone): void {} start: Date; end: Date; date: string; invalid: boolean = false; showTime: boolean = false; showTimezone: boolean = false; showSeconds: boolean = false; showMeridian: boolean = true; showSpinners: boolean = true; showNowBtn: boolean = false; startTimezone: DateTimePickerTimezone; endTimezone: DateTimePickerTimezone; timezones: DateTimePickerTimezone[]; } describe('Date Range Picker', () => { let component: DateRangePickerComponent; let fixture: ComponentFixture; let nativeElement: HTMLElement; let onStartTimezoneChangeSpy: jasmine.Spy; let onEndTimezoneChangeSpy: jasmine.Spy; let getTimezoneOffset: () => number; beforeAll(() => { getTimezoneOffset = Date.prototype.getTimezoneOffset; Date.prototype.getTimezoneOffset = () => { return 0; }; }); afterAll(() => { Date.prototype.getTimezoneOffset = getTimezoneOffset; }); beforeEach(() => { TestBed.configureTestingModule({ imports: [ DateRangePickerModule, IconModule, PopoverModule, FormsModule, DateRangePickerComponent, ], }).compileComponents(); }); beforeEach(() => { fixture = TestBed.createComponent(DateRangePickerComponent); component = fixture.componentInstance; nativeElement = fixture.nativeElement; onStartTimezoneChangeSpy = spyOn(component, 'onStartTimezoneChange'); onEndTimezoneChangeSpy = spyOn(component, 'onEndTimezoneChange'); fixture.detectChanges(); }); it('should initialise correctly', () => { expect(component).toBeTruthy(); }); it('should update start date and call onStartChange when start date is changed and not call onEndChange', async () => { spyOn(component, 'onStartChange'); spyOn(component, 'onEndChange'); component.start = new Date(2020, 0, 7, 0, 0, 0); fixture.detectChanges(); await fixture.whenStable(); expect(getDate().innerHTML).toBe(' 7 January 2020 '); expect(component.onStartChange).toHaveBeenCalled(); expect(component.onEndChange).not.toHaveBeenCalled(); }); it('should update end date and call onEndChange when end date is changed and not call onStartChange', async () => { spyOn(component, 'onEndChange'); spyOn(component, 'onStartChange'); component.end = new Date(2020, 0, 23, 23, 59, 59); fixture.detectChanges(); await fixture.whenStable(); expect(getDate(1).innerHTML).toBe(' 23 January 2020 '); expect(component.onEndChange).toHaveBeenCalled(); expect(component.onStartChange).not.toHaveBeenCalled(); }); it('should not cause an error when dates set to undefined', async () => { component.start = undefined; component.end = undefined; fixture.detectChanges(); await fixture.whenStable(); expect(getDate(0)).toBeNull(); expect(getDate(1)).toBeNull(); }); it('should allow a half time zone to be set if it is present in the timezone array and not call startTimezoneChange or endTimzoneChange', async () => { // Ignore the initial call from timezone being set to default onStartTimezoneChangeSpy.calls.reset(); onEndTimezoneChangeSpy.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.startTimezone = { name: 'GMT-3.5', offset: 210 }; component.endTimezone = { name: 'GMT-3.5', offset: 210 }; fixture.detectChanges(); await fixture.whenStable(); await expect(getTimezone(0).value).toBe('GMT-3.5'); await expect(getTimezone(1).value).toBe('GMT-3.5'); expect(component.onStartTimezoneChange).not.toHaveBeenCalled(); expect(component.onEndTimezoneChange).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 spies expect(component.onStartTimezoneChange).toHaveBeenCalledWith({ name: 'GMT', offset: 0 }); expect(component.onStartTimezoneChange).toHaveBeenCalledTimes(1); onStartTimezoneChangeSpy.calls.reset(); expect(component.onEndTimezoneChange).toHaveBeenCalledWith({ name: 'GMT', offset: 0 }); expect(component.onEndTimezoneChange).toHaveBeenCalledTimes(1); onEndTimezoneChangeSpy.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.startTimezone = { name: 'GMT-3.5x', offset: 3000 }; component.endTimezone = { name: 'GMT-3.5x', offset: 3000 }; fixture.detectChanges(); await fixture.whenStable(); await expect(getTimezone(0).value).toBe('GMT'); await expect(getTimezone(1).value).toBe('GMT'); // Timezone was changed to GMT on initialization, so we do not expect another call with the same value expect(component.onStartTimezoneChange).not.toHaveBeenCalled(); expect(component.onEndTimezoneChange).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 spies expect(component.onStartTimezoneChange).toHaveBeenCalledWith({ name: 'GMT', offset: 0 }); expect(component.onStartTimezoneChange).toHaveBeenCalledTimes(1); onStartTimezoneChangeSpy.calls.reset(); expect(component.onEndTimezoneChange).toHaveBeenCalledWith({ name: 'GMT', offset: 0 }); expect(component.onEndTimezoneChange).toHaveBeenCalledTimes(1); onEndTimezoneChangeSpy.calls.reset(); component.showTime = true; component.showTimezone = true; component.startTimezone = { name: 'GMT-3.5x', offset: 3000 }; component.endTimezone = { name: 'GMT-3.5x', offset: 3000 }; fixture.detectChanges(); await fixture.whenStable(); await expect(getTimezone(0).value).toBe('GMT'); await expect(getTimezone(1).value).toBe('GMT'); // Timezone was changed to GMT on initialization, so we do not expect another call with the same value expect(component.onStartTimezoneChange).not.toHaveBeenCalled(); expect(component.onEndTimezoneChange).not.toHaveBeenCalled(); }); function getDate(index: number = 0): HTMLElement { const headerSections = nativeElement.querySelectorAll('ux-date-range-picker .header-section'); return headerSections[index].querySelector('.date-header'); } function getPicker(index: number = 0): HTMLElement { const pickers = nativeElement.querySelector('ux-date-range-picker .content'); if (index === 0) { return pickers.querySelector('.start-date-picker'); } else { return pickers.querySelector('.end-date-picker'); } } function getTimezone(index: number): HTMLInputElement { const picker = getPicker(index); return picker.querySelector( 'ux-date-time-picker-time-view .time-zone-picker ux-spin-button input' ); } });