import { LOCALE_ID } from '@angular/core'; import { inject, TestBed, waitForAsync } from '@angular/core/testing'; import { DateAdapter, MAT_DATE_LOCALE } from '@angular/material/core'; import { parseJSON } from 'date-fns'; import { da, ja } from 'date-fns/esm/locale'; import { NgxDateFnsDateAdapter, NGX_MAT_DATEFNS_DATE_ADAPTER_OPTIONS, } from './ngx-mat-datefns-date-adapter'; import { NgxMatDateFnsDateModule } from './ngx-mat-datefns-date-adapter.module'; import { NGX_MAT_DATEFNS_LOCALES } from './ngx-mat-datefns-locales'; const [JAN, FEB, MAR, DEC] = [0, 1, 2, 11]; describe('NgxDateFnsDateAdapter', () => { let adapter: NgxDateFnsDateAdapter; let assertValidDate: (d: Date | null, valid: boolean) => void; beforeEach( waitForAsync(() => { TestBed.configureTestingModule({ imports: [NgxMatDateFnsDateModule], }).compileComponents(); }) ); beforeEach(inject([DateAdapter], (dateAdapter: NgxDateFnsDateAdapter) => { adapter = dateAdapter; assertValidDate = (d: Date | null, valid: boolean) => { expect(adapter.isDateInstance(d)).not.toBeNull( `Expected ${d} to be a date instance` ); expect(adapter.isValid(d as Date)).toBe( valid, `Expected ${d} to be ${valid ? 'valid' : 'invalid'},` + ` but was ${valid ? 'invalid' : 'valid'}` ); }; })); it('should get year', () => { expect(adapter.getYear(new Date(2017, JAN, 1))).toBe(2017); }); it('should get month', () => { expect(adapter.getMonth(new Date(2017, JAN, 1))).toBe(0); }); it('should get number of days in a month', () => { expect(adapter.getNumDaysInMonth(new Date(2017, JAN, 1))).toBe(31); expect(adapter.getNumDaysInMonth(new Date(2016, FEB, 1))).toBe(29); }); it('should get date', () => { expect(adapter.getDate(new Date(2017, JAN, 1))).toBe(1); }); it('should get day of week', () => { expect(adapter.getDayOfWeek(new Date(2017, JAN, 1))).toBe(0); }); it('should get long month names', () => { expect(adapter.getMonthNames('long')).toEqual([ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December', ]); }); it('should get short month names', () => { expect(adapter.getMonthNames('short')).toEqual([ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec', ]); }); it('should get narrow month names', () => { expect(adapter.getMonthNames('narrow')).toEqual([ 'J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D', ]); }); it('should get month names in a different locale', () => { adapter.setLocale(ja); expect(adapter.getMonthNames('long')).toEqual([ '1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月', ]); }); it('should get date names', () => { expect(adapter.getDateNames()).toEqual([ '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '30', '31', ]); }); it('should get long day of week names', () => { expect(adapter.getDayOfWeekNames('long')).toEqual([ 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', ]); }); it('should get short day of week names', () => { expect(adapter.getDayOfWeekNames('short')).toEqual([ 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', ]); }); it('should get narrow day of week names', () => { expect(adapter.getDayOfWeekNames('narrow')).toEqual([ 'S', 'M', 'T', 'W', 'T', 'F', 'S', ]); }); it('should get day of week names in a different locale', () => { adapter.setLocale(ja); expect(adapter.getDayOfWeekNames('long')).toEqual([ '日曜日', '月曜日', '火曜日', '水曜日', '木曜日', '金曜日', '土曜日', ]); }); it('should get year name', () => { expect(adapter.getYearName(new Date(2017, JAN, 1))).toBe('2017'); }); it('should get first day of week', () => { expect(adapter.getFirstDayOfWeek()).toBe(0); }); it('should create Date', () => { expect(adapter.createDate(2017, JAN, 1)).toEqual(new Date(2017, JAN, 1)); }); it('should not create Date with month over/under-flow', () => { expect(() => adapter.createDate(2017, DEC + 1, 1)).toThrow(); expect(() => adapter.createDate(2017, JAN - 1, 1)).toThrow(); }); it('should not create Date with date over/under-flow', () => { expect(() => adapter.createDate(2017, JAN, 32)).toThrow(); expect(() => adapter.createDate(2017, JAN, 0)).toThrow(); }); it('should create Date with low year number', () => { expect(adapter.createDate(-1, JAN, 1).getFullYear()).toBe(-1); expect(adapter.createDate(0, JAN, 1).getFullYear()).toBe(0); expect(adapter.createDate(50, JAN, 1).getFullYear()).toBe(50); expect(adapter.createDate(99, JAN, 1).getFullYear()).toBe(99); expect(adapter.createDate(100, JAN, 1).getFullYear()).toBe(100); }); it("should get today's date", () => { expect(adapter.sameDate(adapter.today(), new Date())).toBe( true, "should be equal to today's date" ); }); it('should parse string', () => { expect(adapter.parse('1/1/2017', 'dd/MM/yyyy')).toEqual( new Date(2017, JAN, 1) ); }); it('should parse number', () => { const timestamp = new Date().getTime(); expect(adapter.parse(timestamp, 'dd/MM/yyyy')).toEqual(new Date(timestamp)); }); it('should parse Date', () => { const date = new Date(2017, JAN, 1); expect(adapter.parse(date, 'dd/MM/yyyy')).toEqual(date); expect(adapter.parse(date, 'dd/MM/yyyy')).not.toBe(date); }); it('should parse undefined as null', () => { expect(adapter.parse(undefined, 'dd/MM/yyyy')).toBeNull(); }); it('should parse [] as null', () => { expect(adapter.parse([], 'dd/MM/yyyy')).toBeNull(); }); it('should parse invalid value as invalid', () => { const d = adapter.parse('hello', 'dd/MM/yyyy'); expect(d).not.toBeNull(); expect(adapter.isDateInstance(d)).toBe( true, 'Expected string to have been fed through Date.parse' ); expect(adapter.isValid(d as Date)).toBe( false, 'Expected to parse as "invalid date" object' ); }); it('should format', () => { expect(adapter.format(new Date(2017, JAN, 1), 'd/d/yyyy')).toEqual( '1/1/2017' ); }); it('should format with custom format', () => { expect(adapter.format(new Date(2017, JAN, 1), 'MMMM d, yyyy')).toEqual( 'January 1, 2017' ); }); it('should format with a different locale', () => { adapter.setLocale(ja); expect(adapter.format(new Date(2017, JAN, 1), 'yyyy/d/d')).toEqual( '2017/1/1' ); }); it('should throw when attempting to format invalid date', () => { expect(() => adapter.format(new Date(NaN), 'd/d/yyyy')).toThrowError( /Invalid time value/ ); }); it('should throw when attempting to set locale via string without providing NGX_MAT_DATEFNS_LOCALES token', () => { expect(() => adapter.setLocale('invalid')).toThrowError( /locale 'invalid' does not exist in locales array. Add it to the NGX_MAT_DATEFNS_LOCALES token./ ); }); it('should throw when attempting to load null locale', () => { // @ts-expect-error - Argument of type 'Date | null' is not assignable to parameter of type 'Date'. expect(() => adapter.setLocale(null)).toThrowError( /setLocale should be called with the string locale code or date-fns Locale object/ ); }); it('should add years', () => { expect(adapter.addCalendarYears(new Date(2017, JAN, 1), 1)).toEqual( new Date(2018, JAN, 1) ); expect(adapter.addCalendarYears(new Date(2017, JAN, 1), -1)).toEqual( new Date(2016, JAN, 1) ); }); it('should respect leap years when adding years', () => { expect(adapter.addCalendarYears(new Date(2016, FEB, 29), 1)).toEqual( new Date(2017, FEB, 28) ); expect(adapter.addCalendarYears(new Date(2016, FEB, 29), -1)).toEqual( new Date(2015, FEB, 28) ); }); it('should add months', () => { expect(adapter.addCalendarMonths(new Date(2017, JAN, 1), 1)).toEqual( new Date(2017, FEB, 1) ); expect(adapter.addCalendarMonths(new Date(2017, JAN, 1), -1)).toEqual( new Date(2016, DEC, 1) ); }); it('should respect month length differences when adding months', () => { expect(adapter.addCalendarMonths(new Date(2017, JAN, 31), 1)).toEqual( new Date(2017, FEB, 28) ); expect(adapter.addCalendarMonths(new Date(2017, MAR, 31), -1)).toEqual( new Date(2017, FEB, 28) ); }); it('should add days', () => { expect(adapter.addCalendarDays(new Date(2017, JAN, 1), 1)).toEqual( new Date(2017, JAN, 2) ); expect(adapter.addCalendarDays(new Date(2017, JAN, 1), -1)).toEqual( new Date(2016, DEC, 31) ); }); it('should clone', () => { const date = new Date(2017, JAN, 1); expect(adapter.clone(date)).toEqual(date); expect(adapter.clone(date)).not.toBe(date); }); it('should preserve time when cloning', () => { const date = new Date(2017, JAN, 1, 4, 5, 6); expect(adapter.clone(date)).toEqual(date); expect(adapter.clone(date)).not.toBe(date); }); it('should compare dates', () => { expect( adapter.compareDate(new Date(2017, JAN, 1), new Date(2017, JAN, 2)) ).toBeLessThan(0); expect( adapter.compareDate(new Date(2017, JAN, 1), new Date(2017, FEB, 1)) ).toBeLessThan(0); expect( adapter.compareDate(new Date(2017, JAN, 1), new Date(2018, JAN, 1)) ).toBeLessThan(0); expect( adapter.compareDate(new Date(2017, JAN, 1), new Date(2017, JAN, 1)) ).toBe(0); expect( adapter.compareDate(new Date(2018, JAN, 1), new Date(2017, JAN, 1)) ).toBeGreaterThan(0); expect( adapter.compareDate(new Date(2017, FEB, 1), new Date(2017, JAN, 1)) ).toBeGreaterThan(0); expect( adapter.compareDate(new Date(2017, JAN, 2), new Date(2017, JAN, 1)) ).toBeGreaterThan(0); }); it('should clamp date at lower bound', () => { expect( adapter.clampDate( new Date(2017, JAN, 1), new Date(2018, JAN, 1), new Date(2019, JAN, 1) ) ).toEqual(new Date(2018, JAN, 1)); }); it('should clamp date at upper bound', () => { expect( adapter.clampDate( new Date(2020, JAN, 1), new Date(2018, JAN, 1), new Date(2019, JAN, 1) ) ).toEqual(new Date(2019, JAN, 1)); }); it('should clamp date already within bounds', () => { expect( adapter.clampDate( new Date(2018, FEB, 1), new Date(2018, JAN, 1), new Date(2019, JAN, 1) ) ).toEqual(new Date(2018, FEB, 1)); }); it('should use UTC for formatting by default', () => { expect(adapter.format(new Date(1800, 7, 14), 'E MMM dd yyyy')).toBe( 'Thu Aug 14 1800' ); }); it('should count today as a valid date instance', () => { const d = new Date(); expect(adapter.isValid(d)).toBe(true); expect(adapter.isDateInstance(d)).toBe(true); }); it('should count an invalid date as an invalid date instance', () => { const d = new Date(NaN); expect(adapter.isValid(d)).toBe(false); expect(adapter.isDateInstance(d)).toBe(true); }); it('should count a string as not a date instance', () => { const d = '1/1/2017'; expect(adapter.isDateInstance(d)).toBe(false); }); it('should create dates from valid ISO strings', () => { assertValidDate(adapter.deserialize('1985-04-12T23:20:50.52Z'), true); assertValidDate(adapter.deserialize('1996-12-19T16:39:57-08:00'), true); assertValidDate(adapter.deserialize('1937-01-01T12:00:27.87+00:20'), true); assertValidDate(adapter.deserialize('2017-01-01'), true); assertValidDate(adapter.deserialize('2017-01-01T00:00:00'), true); assertValidDate(adapter.deserialize('1990-13-31T23:59:00Z'), false); assertValidDate(adapter.deserialize('1/1/2017'), false); assertValidDate(adapter.deserialize('2017-01-01T'), true); assertValidDate(adapter.deserialize(1483228800), true); expect(adapter.deserialize('')).toBeNull(); expect(adapter.deserialize(null)).toBeNull(); expect(adapter.deserialize([])).toBeNull(); assertValidDate(adapter.deserialize(new Date()), true); assertValidDate(adapter.deserialize(new Date(NaN)), false); assertValidDate(adapter.deserialize(1483228800), true); }); it('should format Date to ISO8601 string', () => { expect(adapter.toIso8601(new Date(2017, JAN, 1))).toEqual( new Date(2017, JAN, 1).toISOString() ); }); it('should create an invalid date', () => { assertValidDate(adapter.invalid(), false); }); it('should not throw when attempting to format a date with a year less than 1', () => { expect(() => adapter.format(new Date(-1, 1, 1), 'd/d/yyyy')).not.toThrow(); }); it('should not throw when attempting to format a date with a year greater than 9999', () => { expect(() => adapter.format(new Date(10000, 1, 1), 'd/d/yyyy') ).not.toThrow(); }); }); describe('NgxDateFnsDateAdapter with MAT_DATE_LOCALE override', () => { let adapter: NgxDateFnsDateAdapter; beforeEach( waitForAsync(() => { TestBed.configureTestingModule({ imports: [NgxMatDateFnsDateModule], providers: [ { provide: MAT_DATE_LOCALE, useValue: 'da' }, { provide: NGX_MAT_DATEFNS_LOCALES, useValue: [da] }, ], }).compileComponents(); }) ); beforeEach(inject([DateAdapter], (d: NgxDateFnsDateAdapter) => { adapter = d; })); it('should take the default locale id from the MAT_DATE_LOCALE injection token', () => { const expectedValue = [ 'søndag', 'mandag', 'tirsdag', 'onsdag', 'torsdag', 'fredag', 'lørdag', ]; expect(adapter.getDayOfWeekNames('long')).toEqual(expectedValue); }); }); describe('NgxDateFnsDateAdapter with MAT_DATE_LOCALE override', () => { let adapter: NgxDateFnsDateAdapter; beforeEach( waitForAsync(() => { TestBed.configureTestingModule({ imports: [NgxMatDateFnsDateModule], providers: [{ provide: MAT_DATE_LOCALE, useValue: 'invalid' }], }).compileComponents(); }) ); beforeEach(inject([DateAdapter], (d: NgxDateFnsDateAdapter) => { adapter = d; })); it('should set en-US locale when overriding the MAT_DATE_LOCALE injection token with invalid locale value', () => { expect(adapter.format(new Date(2017, JAN, 1), 'MMMM d, yyyy')).toEqual( 'January 1, 2017' ); }); }); describe('NgxDateFnsDateAdapter with LOCALE_ID override', () => { let adapter: NgxDateFnsDateAdapter; beforeEach( waitForAsync(() => { TestBed.configureTestingModule({ imports: [NgxMatDateFnsDateModule], providers: [ { provide: LOCALE_ID, useValue: 'da' }, { provide: NGX_MAT_DATEFNS_LOCALES, useValue: [da] }, ], }).compileComponents(); }) ); beforeEach(inject([DateAdapter], (d: NgxDateFnsDateAdapter) => { adapter = d; })); it('should cascade locale id from the LOCALE_ID injection token to MAT_DATE_LOCALE', () => { const expectedValue = [ 'søndag', 'mandag', 'tirsdag', 'onsdag', 'torsdag', 'fredag', 'lørdag', ]; expect(adapter.getDayOfWeekNames('long')).toEqual(expectedValue); }); }); describe('NgxDateFnsDateAdapter with MAT_DATE_LOCALE override', () => { let adapter: NgxDateFnsDateAdapter; beforeEach( waitForAsync(() => { TestBed.configureTestingModule({ imports: [NgxMatDateFnsDateModule], providers: [{ provide: MAT_DATE_LOCALE, useValue: '' }], }).compileComponents(); }) ); beforeEach(inject([DateAdapter], (d: NgxDateFnsDateAdapter) => { adapter = d; })); it('should load en-US locale when MAT_DATE_LOCALE is null|empty string|undefined etc ', () => { expect(adapter.getMonthNames('long')).toEqual([ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December', ]); }); }); describe('NgxDateFnsDateAdapter with NGX_MAT_DATEFNS_LOCALES set', () => { let adapter: NgxDateFnsDateAdapter; beforeEach( waitForAsync(() => { TestBed.configureTestingModule({ imports: [NgxMatDateFnsDateModule], providers: [{ provide: NGX_MAT_DATEFNS_LOCALES, useValue: [da] }], }).compileComponents(); }) ); beforeEach(inject([DateAdapter], (d: NgxDateFnsDateAdapter) => { adapter = d; })); it('should throw when attempting to set locale without providing it in the NGX_MAT_DATEFNS_LOCALES token', () => { expect(() => adapter.setLocale('ru')).toThrowError( /locale \'ru\' does not exist in locales array. Add it to the NGX_MAT_DATEFNS_LOCALES token./ ); }); }); describe('NgxDateFnsDateAdapter with NGX_MAT_DATEFNS_DATE_ADAPTER_OPTIONS override', () => { describe('use UTC', () => { let adapter: NgxDateFnsDateAdapter; beforeEach( waitForAsync(() => { TestBed.configureTestingModule({ imports: [NgxMatDateFnsDateModule], providers: [ { provide: NGX_MAT_DATEFNS_DATE_ADAPTER_OPTIONS, useValue: { useUtc: true }, }, ], }).compileComponents(); }) ); beforeEach(inject([DateAdapter], (d: NgxDateFnsDateAdapter) => { adapter = d; })); it('should create date in UTC', () => { const expectedDate = parseJSON('2017-01-02T00:00:00Z'); expect(adapter.createDate(2017, JAN, 2)).toEqual(expectedDate); }); it('should create today in UTC', () => { const today = new Date(); const todayUTCString = `${today.getFullYear()}-${(today.getMonth() + 1) .toString() .padStart(2, '0')}-${today .getDate() .toString() .padStart(2, '0')}T00:00:00Z`; const expectedDate = parseJSON(todayUTCString); expect(adapter.today()).toEqual(expectedDate); }); it('should parse dates to UTC', () => { const expectedDate = parseJSON('2017-01-02T00:00:00Z'); expect(adapter.parse('1/2/2017', 'MM/dd/yyyy')).toEqual(expectedDate); }); it('should return UTC date when deserializing', () => { const expectedDate = parseJSON('2020-04-12T23:20:50.52Z'); expect(adapter.deserialize('2020-04-12T23:20:50.52Z')).toEqual( expectedDate ); }); }); });