From f6e46b7c07e9642d5e963327e100749494703cb8 Mon Sep 17 00:00:00 2001 From: Martin Flamm Date: Tue, 21 May 2024 17:31:57 +0200 Subject: [PATCH 1/3] Improve datepipe and fix unit tests --- src/app/shared/relative-date.pipe.spec.ts | 73 +++++++++++++++++++--- src/app/shared/relative-date.pipe.ts | 74 ++++++++++------------- 2 files changed, 94 insertions(+), 53 deletions(-) diff --git a/src/app/shared/relative-date.pipe.spec.ts b/src/app/shared/relative-date.pipe.spec.ts index c57f3e0..5cd4725 100644 --- a/src/app/shared/relative-date.pipe.spec.ts +++ b/src/app/shared/relative-date.pipe.spec.ts @@ -1,19 +1,72 @@ +// Core imports +import { TestBed } from '@angular/core/testing'; import { RelativeDatePipe } from './relative-date.pipe'; +import { TranslateService } from '@ngx-translate/core'; +import { TranslateServiceMock } from 'src/app/test/TranslateServiceMock'; describe('RelativeDatePipe', () => { - const value = []; - const pipe = new RelativeDatePipe(value as any); + let pipe: RelativeDatePipe; + let translateService: TranslateService; + + beforeEach(async () => { + TestBed.configureTestingModule({ + providers: [ + RelativeDatePipe, + { provide: TranslateService, useClass: TranslateServiceMock } + ] + }); + + pipe = TestBed.inject(RelativeDatePipe); + translateService = TestBed.inject(TranslateService); + }); it('create an instance', () => { expect(pipe).toBeTruthy(); }); - it('should transform a string date to relative date', () => { - const result = pipe.transform('2022-07-01'); - expect(result).toBe('1 year ago'); + + it('should transform date strings into relative time format', () => { + const now = new Date(); + const fiveMinutesAgo = new Date(now.getTime() - 5 * 60 * 1000); + const result = pipe.transform(fiveMinutesAgo.toISOString()); + expect(result).toBe('5 minutes ago'); + }); + + it('should transform date objects into relative time format', () => { + const now = new Date(); + const fiveMinutesAgo = new Date(now.getTime() - 5 * 60 * 1000); + const result = pipe.transform(fiveMinutesAgo); + expect(result).toBe('5 minutes ago'); + }); + + it('should return "just now" for very recent dates', () => { + const now = new Date(); + const justNow = new Date(now.getTime() - 1000); // 1 second ago + const result = pipe.transform(justNow); + expect(result).toBe('just now'); + }); + it('should return "just now" for durations under 60 seconds', () => { + const now = new Date(); + const justNow = new Date(now.getTime() - 1000 * 59); // 59 second ago + const result = pipe.transform(justNow); + expect(result).toBe('just now'); + }); + + it('should handle dates in the past year correctly', () => { + const now = new Date(); + const sixMonthsAgo = new Date(now.getTime() - 6 * 30 * 24 * 60 * 60 * 1000); // Approx 6 months + const result = pipe.transform(sixMonthsAgo); + expect(result).toBe('6 months ago'); }); - it('should transform an object date to relative date', () => { - const date = new Date('2022-07-01'); - const result = pipe.transform(date); - expect(result).toBe('1 year ago'); + + it('should handle dates in the past years correctly', () => { + const now = new Date(); + const twoYearsAgo = new Date(now.getTime() - 2 * 365 * 24 * 60 * 60 * 1000); // Approx 2 years + const result = pipe.transform(twoYearsAgo); + expect(result).toBe('2 years ago'); + }); + + it('should handle invalid input gracefully', () => { + const result = pipe.transform('invalid-date'); + expect(result).toBeUndefined(); }); -}); +}); \ No newline at end of file diff --git a/src/app/shared/relative-date.pipe.ts b/src/app/shared/relative-date.pipe.ts index e5e01e5..7fe632e 100644 --- a/src/app/shared/relative-date.pipe.ts +++ b/src/app/shared/relative-date.pipe.ts @@ -1,51 +1,39 @@ -// Core imports import { Pipe, PipeTransform } from '@angular/core'; -// Third party imports -import { TranslateService } from '@ngx-translate/core'; +@Pipe({name: 'relativeDate'}) +export class RelativeDatePipe implements PipeTransform { + transform(value: Date | string | number| undefined): string | undefined { + if (value === undefined) { + return undefined; + } -const DIVISIONS: { amount: number; name: Intl.RelativeTimeFormatUnit }[] = [ - { amount: 60, name: 'seconds' }, - { amount: 60, name: 'minutes' }, - { amount: 24, name: 'hours' }, - { amount: 7, name: 'days' }, - { amount: 4.34524, name: 'weeks' }, - { amount: 12, name: 'months' }, - { amount: Number.POSITIVE_INFINITY, name: 'years' }, -]; + if (!(value instanceof Date)) { + value = new Date(value); + if (isNaN(value.getTime())) { + return undefined; + } + } -@Pipe({ - name: 'relativeDate', -}) -export class RelativeDatePipe implements PipeTransform { - rtf: Intl.RelativeTimeFormat; + const seconds = Math.floor(((new Date()).getTime() - value.getTime()) / 1000); - constructor(private readonly translateService: TranslateService) { - this.rtf = new Intl.RelativeTimeFormat(this.translateService.currentLang, { - style: 'long', - }); - } - /** convert string format into relative date format */ - transform(value: any, args?: any): any { - let date: Date; - switch (typeof value) { - case 'string': - date = new Date(value); - break; - case 'object': - date = value; - break; - default: - break; - } + if (seconds < 60) { + return 'just now'; + } + + const intervals = [ + { label: 'year', seconds: 31536000 }, + { label: 'month', seconds: 2592000 }, + { label: 'day', seconds: 86400 }, + { label: 'hour', seconds: 3600 }, + { label: 'minute', seconds: 60 }, + { label: 'second', seconds: 1 } + ]; - let duration = (date.getTime() - new Date().getTime()) / 1000; - for (let i = 0; i <= DIVISIONS.length; i++) { - const division = DIVISIONS[i]; - if (Math.abs(duration) < division.amount) { - return this.rtf.format(Math.round(duration), division.name); - } - duration /= division.amount; + for (const interval of intervals) { + const count = Math.floor(seconds / interval.seconds); + if (count >= 1) { + return count + ` ${interval.label}${count !== 1 ? 's' : ''} ago`; + } + } } - } } From c59228acbaa569ebff9739437c276a9ecc206d9e Mon Sep 17 00:00:00 2001 From: Martin Flamm Date: Tue, 21 May 2024 17:39:34 +0200 Subject: [PATCH 2/3] Fix formating --- src/app/shared/relative-date.pipe.spec.ts | 6 +-- src/app/shared/relative-date.pipe.ts | 58 +++++++++++------------ 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/src/app/shared/relative-date.pipe.spec.ts b/src/app/shared/relative-date.pipe.spec.ts index 5cd4725..a123197 100644 --- a/src/app/shared/relative-date.pipe.spec.ts +++ b/src/app/shared/relative-date.pipe.spec.ts @@ -12,8 +12,8 @@ describe('RelativeDatePipe', () => { TestBed.configureTestingModule({ providers: [ RelativeDatePipe, - { provide: TranslateService, useClass: TranslateServiceMock } - ] + { provide: TranslateService, useClass: TranslateServiceMock }, + ], }); pipe = TestBed.inject(RelativeDatePipe); @@ -69,4 +69,4 @@ describe('RelativeDatePipe', () => { const result = pipe.transform('invalid-date'); expect(result).toBeUndefined(); }); -}); \ No newline at end of file +}); diff --git a/src/app/shared/relative-date.pipe.ts b/src/app/shared/relative-date.pipe.ts index 7fe632e..72e5b43 100644 --- a/src/app/shared/relative-date.pipe.ts +++ b/src/app/shared/relative-date.pipe.ts @@ -1,39 +1,39 @@ import { Pipe, PipeTransform } from '@angular/core'; -@Pipe({name: 'relativeDate'}) +@Pipe({ name: 'relativeDate' }) export class RelativeDatePipe implements PipeTransform { - transform(value: Date | string | number| undefined): string | undefined { - if (value === undefined) { - return undefined; - } + transform(value: Date | string | number | undefined): string | undefined { + if (value === undefined) { + return undefined; + } - if (!(value instanceof Date)) { - value = new Date(value); - if (isNaN(value.getTime())) { - return undefined; - } - } + if (!(value instanceof Date)) { + value = new Date(value); + if (isNaN(value.getTime())) { + return undefined; + } + } - const seconds = Math.floor(((new Date()).getTime() - value.getTime()) / 1000); + const seconds = Math.floor((new Date().getTime() - value.getTime()) / 1000); - if (seconds < 60) { - return 'just now'; - } + if (seconds < 60) { + return 'just now'; + } - const intervals = [ - { label: 'year', seconds: 31536000 }, - { label: 'month', seconds: 2592000 }, - { label: 'day', seconds: 86400 }, - { label: 'hour', seconds: 3600 }, - { label: 'minute', seconds: 60 }, - { label: 'second', seconds: 1 } - ]; + const intervals = [ + { label: 'year', seconds: 31536000 }, + { label: 'month', seconds: 2592000 }, + { label: 'day', seconds: 86400 }, + { label: 'hour', seconds: 3600 }, + { label: 'minute', seconds: 60 }, + { label: 'second', seconds: 1 }, + ]; - for (const interval of intervals) { - const count = Math.floor(seconds / interval.seconds); - if (count >= 1) { - return count + ` ${interval.label}${count !== 1 ? 's' : ''} ago`; - } - } + for (const interval of intervals) { + const count = Math.floor(seconds / interval.seconds); + if (count >= 1) { + return count + ` ${interval.label}${count !== 1 ? 's' : ''} ago`; + } } + } } From aa123479599be8d17f36e44d1c67dc83eefdabab Mon Sep 17 00:00:00 2001 From: Martin Flamm Date: Tue, 21 May 2024 18:18:01 +0200 Subject: [PATCH 3/3] Increase test coverage for new code --- src/app/shared/relative-date.pipe.spec.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/app/shared/relative-date.pipe.spec.ts b/src/app/shared/relative-date.pipe.spec.ts index a123197..50efbb5 100644 --- a/src/app/shared/relative-date.pipe.spec.ts +++ b/src/app/shared/relative-date.pipe.spec.ts @@ -65,8 +65,20 @@ describe('RelativeDatePipe', () => { expect(result).toBe('2 years ago'); }); + it('should return "1 hour ago" for exactly one hour ago', () => { + const now = new Date(); + const oneHourAgo = new Date(now.getTime() - 60 * 60 * 1000); // 1 hour ago + const result = pipe.transform(oneHourAgo); + expect(result).toBe('1 hour ago'); + }); + it('should handle invalid input gracefully', () => { const result = pipe.transform('invalid-date'); expect(result).toBeUndefined(); }); + + it('should handle invalid input gracefully', () => { + const result = pipe.transform(undefined); + expect(result).toBeUndefined(); + }); });