Skip to content

Commit

Permalink
Finished tests
Browse files Browse the repository at this point in the history
  • Loading branch information
hudson-newey committed Dec 13, 2023
1 parent 4317a53 commit 42601c0
Show file tree
Hide file tree
Showing 8 changed files with 149 additions and 164 deletions.
15 changes: 12 additions & 3 deletions src/app/components/shared/datetime/abstract-datetime.component.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,25 @@
export abstract class AbstractDatetimeComponent {
import { OnChanges, Component } from "@angular/core";

@Component({
selector: "baw-abstract-datetime",
templateUrl: "./abstract-datetime.component.html",
})
export abstract class AbstractDatetimeComponent implements OnChanges {
public constructor() {}

public formattedValue: string;
public tooltipValue: string;
public rawDateTime: string;
// I have implemented this method so that each datetime component will default to having no suffix
public suffix = "";

/**
* A method that should update formattedValue, tooltipValue, and rawDateTime
* after during each change detection cycle the component updated as part of
*/
public abstract update(): void;

// I have implemented this method so that each datetime component will default to having no suffix
public suffix = "";
public ngOnChanges(): void {
this.update();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,26 +17,43 @@ interface TestCase {
timeOnly?: boolean;
}

// by setting the defaultZone to utc when we create DateTime objects from ISO strings
// it doesn't implicitly localize the DateTime object to another timezone
// this makes the object produced by the iso string predictable
// eg. an iso string "2020-10-10T12:12:12Z" will always produce the object
// { year: 2020, month: 10, day: 10, hour: 12, minute: 12, second: 12 }
// we re-mock the test runners timezone to a more complex one when the tests are run
// however, during test setup it is easier to read the DateTime objects if we create
// them as if the test runner is in utc+00:00
Settings.defaultZone = "utc";

// in the following test cases, all the dateTime objects are created in UTC+00:00
// therefore, because we are mocking the test runners timezone to be Australia/Perth (UTC+08:00)
// we should see that the date time text, tooltips and machine readable content is localized
// to UTC+08:00. eg. We should see the hours increase by 8
const testCases: TestCase[] = [
{
name: "should display the full date and time in the users local timezone by default",
// create a Luxon DateTime object in UTC+00:00
// we should see this date be localized to UTC+08:00 (the test runners timezone)
value: DateTime.fromISO("2020-01-01T12:10:11.000Z"),
expectedText: "2020-01-01 20:10:11",
expectedTooltip: "2020-01-01 20:10:11 Australia/Perth +08:00",
expectedDateTimeAttribute: "2020-01-01T20:10:11.000+08:00",
},
{
name: "should display the correct dateTime if the Luxon dateTime object is the same timezone as the users local timezone",
// by setting the timezone to Australia/Perth, we should see that the time value is not changed
// because the Luxon DateTime object is already in the same timezone as the test runner
// by using { keepLocalTime: true }, we ensure that the time is not changed when we change the timezone
// without using { keepLocalTime: true }, the DateTime object would be implicitly created as 20:10:11 +08:00
value: DateTime.fromISO("2020-01-01T12:10:11.000Z").setZone(
"Australia/Perth",
{ keepLocalTime: true }
),
expectedText: "2020-01-01 20:10:11",
expectedTooltip: "2020-01-01 20:10:11 Australia/Perth +08:00",
expectedDateTimeAttribute: "2020-01-01T20:10:11.000+08:00",
expectedText: "2020-01-01 12:10:11",
expectedTooltip: "2020-01-01 12:10:11 Australia/Perth +08:00",
expectedDateTimeAttribute: "2020-01-01T12:10:11.000+08:00",
},
{
// most of the tests create the DateTime objects in UTC+00:00
Expand All @@ -45,6 +62,8 @@ const testCases: TestCase[] = [
name: "should display the correct dateTime if the Luxon dateTime object is a different timezone to the users local timezone",
value: DateTime.fromISO("2020-01-01T12:10:11.000+02:00"),
expectedText: "2020-01-01 18:10:11",
// notice that the tooltips are still in the test runners mock timezone
// this is because the emitted date/time has been localized to the test runners timezone
expectedTooltip: "2020-01-01 18:10:11 Australia/Perth +08:00",
expectedDateTimeAttribute: "2020-01-01T18:10:11.000+08:00",
},
Expand Down Expand Up @@ -80,13 +99,6 @@ const testCases: TestCase[] = [
timeOnly: true,
expectedDateTimeAttribute: "2020-01-01T20:10:11.000+08:00",
},
{
name: "should have a tooltip that displays the full un-localized date, time and utc offset for a JavaScript date object",
value: new Date("2020-01-01T12:10:11.000Z"),
expectedText: "2020-01-01 20:10:11",
expectedTooltip: "2020-01-01 20:10:11 Australia/Perth +08:00",
expectedDateTimeAttribute: "2020-01-01T20:10:11.000+08:00",
},
{
name: "should have a tooltip that displays the full un-localized utc date, utc time, and utc offset for a Luxon DateTime object",
value: DateTime.fromISO("2020-01-01T12:10:11.000Z"),
Expand All @@ -95,15 +107,22 @@ const testCases: TestCase[] = [
expectedDateTimeAttribute: "2020-01-01T20:10:11.000+08:00",
},
{
name: "should localize an iso8601 date/time with an offset",
value: new Date("2020-01-01T12:10:11.000+01:00"),
name: "should localize a Luxon DateTime object with a timezone offset",
value: DateTime.fromISO("2020-01-01T12:10:11.000+01:00"),
expectedText: "2020-01-01 19:10:11",
expectedTooltip: "2020-01-01 19:10:11 Australia/Perth +08:00",
expectedDateTimeAttribute: "2020-01-01T19:10:11.000+08:00",
},
{
name: "should localize a Luxon DateTime object with a timezone offset",
value: DateTime.fromISO("2020-01-01T12:10:11.000+01:00"),
name: "should have a tooltip that displays the full date, time and utc offset for a JavaScript date object",
value: new Date("2020-01-01T12:10:11.000Z"),
expectedText: "2020-01-01 20:10:11",
expectedTooltip: "2020-01-01 20:10:11 Australia/Perth +08:00",
expectedDateTimeAttribute: "2020-01-01T20:10:11.000+08:00",
},
{
name: "should localize a JavaScript date/time with an offset",
value: new Date("2020-01-01T12:10:11.000+01:00"),
expectedText: "2020-01-01 19:10:11",
expectedTooltip: "2020-01-01 19:10:11 Australia/Perth +08:00",
expectedDateTimeAttribute: "2020-01-01T19:10:11.000+08:00",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Component, Input, OnChanges } from "@angular/core";
import { Component, Input } from "@angular/core";
import { isInstantiated } from "@helpers/isInstantiated/isInstantiated";
import { TimezoneInformation } from "@interfaces/apiInterfaces";
import { DateTime, Zone } from "luxon";
Expand All @@ -16,10 +16,7 @@ type BawTimezoneUnion = Zone | TimezoneInformation | string;
selector: "baw-datetime",
templateUrl: "../abstract-datetime.component.html",
})
export class DatetimeComponent
extends AbstractDatetimeComponent
implements OnChanges
{
export class DatetimeComponent extends AbstractDatetimeComponent {
public constructor() {
super();
}
Expand All @@ -41,10 +38,6 @@ export class DatetimeComponent
return luxonDate.toLocal();
}

public ngOnChanges(): void {
this.update();
}

public update(): void {
this.formattedValue = this.luxonDateTime.toFormat(this.dateTimeFormat());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ describe("DurationComponent", () => {
spectator = createComponent();
}

function update(): void {
spectator.detectChanges();
spectator.component.ngOnChanges();
spectator.detectChanges();
}

const componentElement = (): HTMLTimeElement =>
spectator.element.querySelector<HTMLTimeElement>("time");

Expand All @@ -76,21 +82,15 @@ describe("DurationComponent", () => {
spectator.component.value = modelData.time();
spectator.component.iso8601 = true;
spectator.component.humanized = true;

spectator.detectChanges();
spectator.component.ngOnChanges();
spectator.detectChanges();
update();

expect(console.error).toHaveBeenCalledOnceWith(expectedErrorMessage);
});

it("should have machine readable dateTime property set to iso8601 format", () => {
const expectedDateTime = "PT2H10M";
spectator.component.value = Duration.fromObject({ hours: 2, minutes: 10 });

spectator.detectChanges();
spectator.component.ngOnChanges();
spectator.detectChanges();
update();

expect(componentElement().dateTime).toBe(expectedDateTime);
});
Expand All @@ -100,10 +100,7 @@ describe("DurationComponent", () => {
spectator.component.value = testCase.value;
spectator.component.iso8601 = testCase.iso8601;
spectator.component.humanized = testCase.humanized;

spectator.detectChanges();
spectator.component.ngOnChanges();
spectator.detectChanges();
update();

expect(componentElement().dateTime).toBe(testCase.expectedDateTimeAttribute);
expect(componentElement()).toHaveExactTrimmedText(testCase.expectedText);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Component, Input, OnChanges } from "@angular/core";
import { Component, Input } from "@angular/core";
import { isInstantiated } from "@helpers/isInstantiated/isInstantiated";
import { HumanizeDurationOptions, toRelative } from "@interfaces/apiInterfaces";
import { Duration } from "luxon";
Expand All @@ -10,7 +10,6 @@ import { AbstractDatetimeComponent } from "../abstract-datetime.component";
})
export class DurationComponent
extends AbstractDatetimeComponent
implements OnChanges
{
public constructor() {
super();
Expand All @@ -20,10 +19,6 @@ export class DurationComponent
@Input() public humanized: string | boolean;
@Input() public iso8601: string | boolean;

public ngOnChanges(): void {
this.update();
}

public update(): void {
this.formattedValue = this.formattedText();
this.tooltipValue = this.tooltipText();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ import { DateTime, Duration, Settings } from "luxon";
import { assertTooltip } from "@test/helpers/html";
import { RelativeTimeComponent } from "./time-since.component";

// I have created this interface for TypeScript LSP typing and auto completion
// it should not be used outside of this file
interface TestCase {
name: string;
value: Duration | Date | DateTime;
expectedText: string;
expectedTooltip: string;
// the dateTimeAttribute assertion asserts that the time element
// has the correct "dateTime" attribute value
expectedDateTimeAttribute: string;
}

Expand Down Expand Up @@ -56,21 +56,20 @@ const testCases: TestCase[] = [
expectedTooltip: "2019-11-13 14:17:58",
expectedDateTimeAttribute: "P1M2W6DT9H42M2S",
},
// {
// name: "should localize a Luxon dateTime object to the users timezone",
// // since the test runners mock timezone is in utc time, by setting the timezone to utc
// // we should see that the date/time has an additional 10 hours added to it
// value: DateTime.fromISO("2019-12-31T04:17:58.000Z"),
// expectedText: "9 hours 42 minutes ago",
// expectedTooltip: "2019-12-31 14:17:58",
// },
// {
// name: "should localize a JavaScript Date object to the users timezone",
// value: new Date("2019-12-31T04:17:58.000-10:00"),
// expectedText: "9 hours 42 minutes ago",
// expectedTooltip: "2019-12-31 14:17:58",
// expectedDateTimeAttribute: "",
// }
{
name: "should handle negative durations correctly",
value: Duration.fromObject({ hours: -2, minutes: -10 }),
expectedText: "2 hours 10 minutes from now",
expectedTooltip: "2020-01-01 02:10:00",
expectedDateTimeAttribute: "PT-2H-10M",
},
{
name: "should localize a Luxon dateTime object to the users timezone",
value: DateTime.fromISO("2019-12-31T04:17:58.000Z", { zone: "Australia/Darwin" }),
expectedText: "19 hours 42 minutes ago",
expectedTooltip: "2019-12-31 13:47:58",
expectedDateTimeAttribute: "PT19H42M2S",
},
];

describe("RelativeTimeComponent", () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Component, Input, OnChanges } from "@angular/core";
import { HumanizeDurationOptions, toRelative } from "@interfaces/apiInterfaces";
import { Component, Input } from "@angular/core";
import { toRelative } from "@interfaces/apiInterfaces";
import { DateTime, Duration } from "luxon";
import { AbstractDatetimeComponent } from "../abstract-datetime.component";

Expand All @@ -8,10 +8,7 @@ import { AbstractDatetimeComponent } from "../abstract-datetime.component";
templateUrl: "../abstract-datetime.component.html",
styleUrls: ["time-since.component.scss"],
})
export class RelativeTimeComponent
extends AbstractDatetimeComponent
implements OnChanges
{
export class RelativeTimeComponent extends AbstractDatetimeComponent {
public constructor() {
super();
}
Expand Down Expand Up @@ -41,24 +38,7 @@ export class RelativeTimeComponent
return durationDifference.rescale();
}

public ngOnChanges(): void {
this.update();
}

public update(): void {
this.formattedValue = this.formattedValueValue();
this.tooltipValue = this.tooltipValueValue();
this.rawDateTime = this.rawDateTimeValue();
}

public tooltipValueValue(): string {
return this.luxonDateTime.toFormat("yyyy-MM-dd HH:mm:ss");
}

// if the value @Input is a js Date object, we convert it to a luxon DateTime object
// otherwise, if it is a Duration, subtract the duration from the current date/time
// we do this so that we can see the exact date/time that this time-since component is referencing
private get luxonDateTime(): DateTime {
private get luxonStartDate(): DateTime {
if (this.value instanceof DateTime) {
return this.value;
}
Expand All @@ -68,16 +48,21 @@ export class RelativeTimeComponent
: DateTime.now().minus(this.durationValue);
}

public formattedValueValue(): string {
const relativeOptions: HumanizeDurationOptions = {
public update(): void {
this.tooltipValue = this.luxonStartDate.toFormat("yyyy-MM-dd HH:mm:ss");
this.rawDateTime = this.durationValue.toISO();

this.formattedValue = toRelative(this.durationValue, {
largest: 2,
round: true,
};
});

return toRelative(this.durationValue, relativeOptions);
}

public rawDateTimeValue(): string {
return this.durationValue.toISO();
// if the duration is negative, it implies that the date/time is in the future
// therefore, we want to change the suffix to "from now"
if (this.durationValue.valueOf() < 0) {
this.suffix = "from now";
} else {
this.suffix = "ago";
}
}
}
Loading

0 comments on commit 42601c0

Please sign in to comment.