Skip to content

Instantly share code, notes, and snippets.

@pmcelhaney
Last active December 22, 2021 17:10
Show Gist options
  • Select an option

  • Save pmcelhaney/accf7727294a8315c504e1985d44c0ce to your computer and use it in GitHub Desktop.

Select an option

Save pmcelhaney/accf7727294a8315c504e1985d44c0ce to your computer and use it in GitHub Desktop.
Reactive controller with unit test
import type { ReactiveControllerHost } from "lit";
import { ClockController } from "../../src/controllers/clock-controller";
class Host implements ReactiveControllerHost {
updateComplete: Promise<boolean>;
clock: ClockController;
emittedTimes: string[] = [];
addController(controller: ClockController): void {
this.clock = controller;
this.requestUpdate();
}
removeController(): void {
return undefined;
}
requestUpdate(): void {
this.emittedTimes.push(
this.clock.time.toString({ smallestUnit: "minute" })
);
}
}
function emittedTimesAfterTicks(hours: number, minutes: number, ticks: number) {
jest.useFakeTimers();
const spy = jest
.spyOn(Date, "now")
.mockReturnValue(new Date(2021, 1, 1, hours, minutes).valueOf());
try {
const host = new Host();
const clock = new ClockController(host);
clock.hostConnected();
for (let secondsPassed = 1; secondsPassed <= ticks; secondsPassed += 1) {
spy.mockReturnValue(
new Date(2021, 1, 1, hours, minutes, secondsPassed).valueOf()
);
jest.advanceTimersByTime(1000);
}
return host.emittedTimes;
} finally {
jest.useRealTimers();
spy.mockRestore();
}
}
describe("clock Controller", () => {
it("starts with the current time", () => {
expect(emittedTimesAfterTicks(12, 15, 0)).toStrictEqual(["12:15"]);
});
it("does not update the time if the minute has not changed", () => {
expect(emittedTimesAfterTicks(12, 15, 59)).toStrictEqual(["12:15"]);
});
it("updates the time if the minute has changed", () => {
expect(emittedTimesAfterTicks(12, 15, 60)).toStrictEqual([
"12:15",
"12:16",
]);
});
it("updates more than once", () => {
expect(emittedTimesAfterTicks(12, 15, 180)).toStrictEqual([
"12:15",
"12:16",
"12:17",
"12:18",
]);
});
it("stops updating the host when disconnected", () => {
jest.useFakeTimers();
const spy = jest
.spyOn(Date, "now")
.mockReturnValue(new Date(2021, 1, 1, 12, 15).valueOf());
try {
const host = new Host();
const clock = new ClockController(host);
clock.hostConnected();
spy.mockReturnValue(new Date(2021, 1, 1, 12, 15).valueOf());
clock.hostDisconnected();
spy.mockReturnValue(new Date(2021, 1, 1, 12, 16).valueOf());
jest.advanceTimersByTime(60 * 1000);
expect(clock.time.toString({ smallestUnit: "minute" })).toBe("12:15");
} finally {
jest.useRealTimers();
spy.mockRestore();
}
});
});
import type { ReactiveController, ReactiveControllerHost } from "lit";
import { Temporal } from "@js-temporal/polyfill";
const CLOCK_TICK_INTERVAL_IN_MS = 1000;
export interface Clock {
time: Temporal.PlainTime;
}
export class ClockController implements Clock, ReactiveController {
time: Temporal.PlainTime = Temporal.Now.plainTimeISO();
host: ReactiveControllerHost;
#interval: ReturnType<typeof setInterval>;
constructor(host: ReactiveControllerHost) {
host.addController(this);
this.host = host;
}
hostConnected() {
this.#interval = setInterval(() => {
const oldTime = this.time;
this.time = Temporal.Now.plainTimeISO();
if (
this.time.toString({ smallestUnit: "minute" }) !==
oldTime.toString({ smallestUnit: "minute" })
) {
this.host.requestUpdate();
}
}, CLOCK_TICK_INTERVAL_IN_MS);
}
hostDisconnected() {
clearInterval(this.#interval);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment