///
/// 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'
);
}
});