80 lines
2.6 KiB
TypeScript
80 lines
2.6 KiB
TypeScript
import type { Dispatch } from "@deadhead/schema";
|
|
import { focusManager } from "@tanstack/react-query";
|
|
import { act, screen, waitFor } from "@testing-library/react";
|
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
import { useClock } from "../src/clock/clock";
|
|
import { fakeApi, renderApp, samplePlayerState } from "./render";
|
|
|
|
const NOW = Date.parse("2100-01-01T00:00:00Z");
|
|
|
|
function inFlight(arrivalAt: number): Dispatch {
|
|
return {
|
|
id: "d1",
|
|
shipId: "loaner",
|
|
pilotId: "avatar",
|
|
contract: null,
|
|
originId: "earth",
|
|
destinationId: "moon",
|
|
tutorial: true,
|
|
departureAt: NOW - 60_000,
|
|
arrivalAt,
|
|
distanceMillionKm: 0.39,
|
|
fuelCost: 0,
|
|
expectedPayout: 240,
|
|
seed: 1,
|
|
status: "in-flight",
|
|
resolvedAt: null,
|
|
};
|
|
}
|
|
|
|
const stateCalls = (api: ReturnType<typeof fakeApi>) =>
|
|
api.call.mock.calls.filter(([name]) => name === "getPlayerState").length;
|
|
|
|
describe("player state store", () => {
|
|
beforeEach(() => {
|
|
// Fake only the clock and the 1 s ticker; leave setTimeout real for React Query.
|
|
vi.useFakeTimers({ toFake: ["Date", "setInterval", "clearInterval"] });
|
|
vi.setSystemTime(NOW);
|
|
useClock.setState({ offsetMs: 0, synced: false, now: NOW });
|
|
});
|
|
afterEach(() => vi.useRealTimers());
|
|
|
|
it("refetches automatically when the clock passes an in-flight arrival", async () => {
|
|
const api = fakeApi({
|
|
getServerTime: () => ({ realTime: Date.now(), gameTime: 0 }),
|
|
getPlayerState: () => samplePlayerState({ activeDispatches: [inFlight(NOW + 30_000)] }),
|
|
});
|
|
renderApp(api);
|
|
await screen.findByRole("heading", { level: 1, name: "HQ" });
|
|
expect(stateCalls(api)).toBe(1);
|
|
|
|
act(() => vi.advanceTimersByTime(29_000));
|
|
expect(stateCalls(api)).toBe(1);
|
|
|
|
act(() => vi.advanceTimersByTime(2_000));
|
|
await waitFor(() => expect(stateCalls(api)).toBe(2));
|
|
|
|
// The same arrival does not trigger again on later ticks.
|
|
act(() => vi.advanceTimersByTime(5_000));
|
|
expect(stateCalls(api)).toBe(2);
|
|
});
|
|
});
|
|
|
|
describe("player state store on focus", () => {
|
|
afterEach(() => focusManager.setFocused(undefined));
|
|
|
|
it("refetches when the app regains focus", async () => {
|
|
const api = fakeApi({ getPlayerState: () => samplePlayerState() });
|
|
renderApp(api);
|
|
await screen.findByRole("heading", { level: 1, name: "HQ" });
|
|
const before = stateCalls(api);
|
|
|
|
// Leaving and returning to the app, as the browser reports it to TanStack Query.
|
|
act(() => {
|
|
focusManager.setFocused(false);
|
|
focusManager.setFocused(true);
|
|
});
|
|
await waitFor(() => expect(stateCalls(api)).toBeGreaterThan(before));
|
|
});
|
|
});
|