67 lines
2.5 KiB
TypeScript
67 lines
2.5 KiB
TypeScript
import { content } from "@deadhead/content";
|
|
import { realToGame } from "@deadhead/sim";
|
|
import { act, render } from "@testing-library/react";
|
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
import type { ApiClient } from "../src/api/client";
|
|
import { serverNow, startClockTicker, syncClock, useClock } from "../src/clock/clock";
|
|
import { formatGameTime, GameClock } from "../src/clock/GameClock";
|
|
|
|
const DEVICE_NOW = Date.parse("2100-01-01T00:00:00Z");
|
|
const TEN_MINUTES = 10 * 60_000;
|
|
|
|
/** An API whose server clock runs `aheadMs` ahead of the device. */
|
|
function serverAhead(aheadMs: number): ApiClient {
|
|
return {
|
|
call: vi.fn(async () => ({ realTime: Date.now() + aheadMs, gameTime: 0 })),
|
|
} as unknown as ApiClient;
|
|
}
|
|
|
|
describe("server-synced clock", () => {
|
|
beforeEach(() => {
|
|
vi.useFakeTimers();
|
|
vi.setSystemTime(DEVICE_NOW);
|
|
useClock.setState({ offsetMs: 0, synced: false, now: DEVICE_NOW });
|
|
});
|
|
afterEach(() => vi.useRealTimers());
|
|
|
|
it("returns a time 10 minutes ahead when the server is 10 minutes ahead", async () => {
|
|
await syncClock(serverAhead(TEN_MINUTES));
|
|
expect(useClock.getState().synced).toBe(true);
|
|
expect(serverNow()).toBe(DEVICE_NOW + TEN_MINUTES);
|
|
|
|
vi.advanceTimersByTime(5000);
|
|
expect(serverNow()).toBe(DEVICE_NOW + TEN_MINUTES + 5000);
|
|
});
|
|
|
|
it("works when the server is behind the device", async () => {
|
|
await syncClock(serverAhead(-30_000));
|
|
expect(serverNow()).toBe(DEVICE_NOW - 30_000);
|
|
});
|
|
|
|
it("ticks the store every second", async () => {
|
|
await syncClock(serverAhead(TEN_MINUTES));
|
|
const stop = startClockTicker();
|
|
vi.advanceTimersByTime(3000);
|
|
expect(useClock.getState().now).toBe(DEVICE_NOW + TEN_MINUTES + 3000);
|
|
stop();
|
|
vi.advanceTimersByTime(3000);
|
|
expect(useClock.getState().now).toBe(DEVICE_NOW + TEN_MINUTES + 3000);
|
|
});
|
|
|
|
it("shows the in-game time in the game clock and keeps it ticking", async () => {
|
|
await syncClock(serverAhead(0));
|
|
const stop = startClockTicker();
|
|
const { container } = render(<GameClock />);
|
|
// Date and time are separate spans (the date hides on phones).
|
|
const clock = () => container.querySelector("time")!;
|
|
// The default clock follows the device locale (CR-011).
|
|
const at = (real: number) => formatGameTime(realToGame(real, content.config), "locale");
|
|
expect(clock()).toHaveTextContent(at(DEVICE_NOW));
|
|
|
|
// One real minute is one game hour.
|
|
act(() => vi.advanceTimersByTime(60_000));
|
|
expect(clock()).toHaveTextContent(at(DEVICE_NOW + 60_000));
|
|
stop();
|
|
});
|
|
});
|