deadhead-space-freight-tycoon/apps/web/test/clock.test.tsx

65 lines
2.4 KiB
TypeScript

import { content } from "@deadhead/content";
import { realToGame } from "@deadhead/sim";
import { act, render, screen } 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();
render(<GameClock />);
// The default clock follows the device locale (CR-011).
const at = (real: number) => formatGameTime(realToGame(real, content.config), "locale");
expect(screen.getByText(at(DEVICE_NOW))).toBeInTheDocument();
// One real minute is one game hour.
act(() => vi.advanceTimersByTime(60_000));
expect(screen.getByText(at(DEVICE_NOW + 60_000))).toBeInTheDocument();
stop();
});
});