113 lines
4.3 KiB
TypeScript
113 lines
4.3 KiB
TypeScript
import { content } from "@deadhead/content";
|
||
import type { Contract } from "@deadhead/schema";
|
||
import { MS_PER_HOUR, realToGame } from "@deadhead/sim";
|
||
import { act, screen } from "@testing-library/react";
|
||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||
import { useClock } from "../src/clock/clock";
|
||
import {
|
||
formatGameHoursSpan,
|
||
formatGameSpan,
|
||
formatGameTime,
|
||
formatSpan,
|
||
formatTimeOfDay,
|
||
} from "../src/clock/timeFormat";
|
||
import { formatClock } from "../src/format";
|
||
import { mapMotionStyle } from "../src/map/layout";
|
||
import { mapStyle } from "../src/map/style";
|
||
import { DEFAULT_PREFERENCES, usePreferences } from "../src/state/preferences";
|
||
import { fakeApi, renderApp, samplePlayerState } from "./render";
|
||
|
||
const NOW = Date.parse("2100-01-01T00:00:00Z");
|
||
/** 4m 16s of real time is 4.27 game hours at 60× compression. */
|
||
const SPAN = 4 * 60_000 + 16_000;
|
||
|
||
describe("time and span helpers (Step 10K.3)", () => {
|
||
it("formats game time in 24-hour and 12-hour clocks", () => {
|
||
const t = Date.parse("2100-03-14T13:05:00Z");
|
||
expect(formatGameTime(t, "24h")).toBe("2100-03-14 13:05");
|
||
expect(formatGameTime(t, "12h")).toMatch(/^2100-03-14 1:05\sPM$/);
|
||
});
|
||
|
||
it("formats a real time of day in each clock mode", () => {
|
||
const t = new Date(2100, 0, 1, 15, 7).getTime();
|
||
expect(formatTimeOfDay(t, "24h")).toBe("15:07");
|
||
expect(formatTimeOfDay(t, "12h")).toMatch(/^0?3:07\sPM$/);
|
||
expect(formatTimeOfDay(t, "locale")).toBe(
|
||
new Date(t).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }),
|
||
);
|
||
});
|
||
|
||
it("formats spans as real time, game time or both", () => {
|
||
expect(formatSpan(SPAN, "real")).toBe("4m 16s");
|
||
expect(formatSpan(SPAN, "game")).toBe("4.3 game h");
|
||
expect(formatSpan(SPAN, "both")).toBe("4m 16s · 4.3 game h");
|
||
expect(formatSpan(SPAN, "both", formatClock)).toBe("04:16 · 4.3 game h");
|
||
expect(formatGameSpan(62.4)).toBe("62 game h");
|
||
expect(formatGameHoursSpan(5, "game")).toBe("5.0 game h");
|
||
expect(formatGameHoursSpan(5, "real")).toBe(
|
||
formatSpan((5 * MS_PER_HOUR) / content.config.timeCompression, "real"),
|
||
);
|
||
});
|
||
|
||
it("stills the map background with reduced motion", () => {
|
||
expect(mapMotionStyle(true).parallax).toBe(0);
|
||
expect(mapMotionStyle(true).backgroundZoomFollow).toBe(0);
|
||
expect(mapMotionStyle(false).parallax).toBe(mapStyle.parallax);
|
||
});
|
||
});
|
||
|
||
describe("display options in the app (Step 10K.3)", () => {
|
||
beforeEach(() => {
|
||
vi.useFakeTimers({ toFake: ["Date", "setInterval", "clearInterval"] });
|
||
vi.setSystemTime(NOW);
|
||
useClock.setState({ offsetMs: 0, synced: true, now: NOW });
|
||
});
|
||
afterEach(() => {
|
||
vi.useRealTimers();
|
||
act(() => usePreferences.getState().set(DEFAULT_PREFERENCES));
|
||
});
|
||
|
||
it("switches the HQ clock to 24-hour", async () => {
|
||
act(() => usePreferences.getState().set({ clock: "12h" }));
|
||
renderApp(fakeApi({ getPlayerState: () => samplePlayerState({ tutorial: null }) }), "/");
|
||
const game = realToGame(NOW, content.config);
|
||
expect(await screen.findByLabelText("Game clock")).toHaveTextContent(
|
||
formatGameTime(game, "12h"),
|
||
);
|
||
act(() => usePreferences.getState().set({ clock: "24h" }));
|
||
expect(screen.getByLabelText("Game clock")).toHaveTextContent(formatGameTime(game, "24h"));
|
||
});
|
||
|
||
it("shows contract expiry in both real and game time when asked", async () => {
|
||
const job: Contract = {
|
||
id: "c1",
|
||
originId: "earth",
|
||
destinationId: "moon",
|
||
cargoTypeId: "standard",
|
||
goodId: "consumer-goods",
|
||
shipper: "Pacific Orbital Logistics",
|
||
priority: false,
|
||
deliveryWindowGameHours: null,
|
||
tier: 0,
|
||
massTonnes: 10,
|
||
payout: 1234,
|
||
requiredSkill: null,
|
||
requiredLongHaulLevel: 0,
|
||
offeredAt: NOW,
|
||
expiresAt: NOW + SPAN,
|
||
firstContact: false,
|
||
};
|
||
renderApp(
|
||
fakeApi({
|
||
getPlayerState: () => samplePlayerState({ tutorial: null }),
|
||
getContractBoard: () => ({ contracts: [job], periodEndsAt: NOW + 600_000 }),
|
||
}),
|
||
"/contracts?location=earth",
|
||
);
|
||
const card = (await screen.findByText("1,234 cr")).closest("li")!;
|
||
expect(card).toHaveTextContent("expires in 4m 16s");
|
||
act(() => usePreferences.getState().set({ countdown: "both" }));
|
||
expect(card).toHaveTextContent("expires in 4m 16s · 4.3 game h");
|
||
});
|
||
});
|