132 lines
4.1 KiB
TypeScript
132 lines
4.1 KiB
TypeScript
import { content } from "@deadhead/content";
|
|
import type { Contract, Pilot, ResolutionSummary, Ship } from "@deadhead/schema";
|
|
import { pilotShare, realToGame, tripFuelCost } from "@deadhead/sim";
|
|
import { screen, within } from "@testing-library/react";
|
|
import userEvent from "@testing-library/user-event";
|
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
import { useClock } from "../src/clock/clock";
|
|
import { formatCredits } from "../src/format";
|
|
import { fakeApi, renderApp, samplePlayerState, shipExtras } from "./render";
|
|
|
|
const NOW = Date.parse("2100-01-01T00:00:00Z");
|
|
|
|
const ship = (id: string, name: string, pilotId: string): Ship => ({
|
|
id,
|
|
name,
|
|
modelId: "courier",
|
|
status: "idle",
|
|
locationId: "earth",
|
|
dispatchId: null,
|
|
pilotId,
|
|
lastArrivalAt: 0,
|
|
...shipExtras("courier"),
|
|
});
|
|
const pilot = (id: string, name: string, shipId: string, isAvatar: boolean): Pilot => ({
|
|
id,
|
|
name,
|
|
isAvatar,
|
|
isVeteran: !isAvatar,
|
|
xp: 0,
|
|
level: 0,
|
|
skills: {},
|
|
skillPointsAvailable: 0,
|
|
assignedShipId: shipId,
|
|
status: "idle",
|
|
locationId: "earth",
|
|
dutyHours: 0,
|
|
dutyLimit: 12,
|
|
resting: false,
|
|
restEndsAt: null,
|
|
});
|
|
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: 900,
|
|
requiredSkill: null,
|
|
requiredLongHaulLevel: 0,
|
|
offeredAt: NOW,
|
|
expiresAt: NOW + 30 * 60_000,
|
|
firstContact: false,
|
|
};
|
|
const summary: ResolutionSummary = {
|
|
dispatchId: "d1",
|
|
shipId: "s-vet",
|
|
pilotId: "p-vet",
|
|
originId: "earth",
|
|
destinationId: "moon",
|
|
tutorial: false,
|
|
deadhead: false,
|
|
fuelCost: 150,
|
|
goodId: "consumer-goods",
|
|
massTonnes: 10,
|
|
shipper: "Pacific Orbital Logistics",
|
|
credits: 600,
|
|
loanRepayment: 0,
|
|
pilotPay: 63,
|
|
pilotXp: 10,
|
|
discoveredLocationId: null,
|
|
scriptedId: null,
|
|
restEndsAt: null,
|
|
resolvedAt: NOW,
|
|
};
|
|
|
|
const state = (summaries: ResolutionSummary[] = []) =>
|
|
samplePlayerState({
|
|
tutorial: null,
|
|
company: { id: "c", name: "D", credits: 100_000, loanBalance: 0 },
|
|
bays: [{ id: "b", locationId: "earth" }],
|
|
ships: [ship("s-me", "My Lark", "avatar"), ship("s-vet", "Rosa's Lark", "p-vet")],
|
|
pilots: [pilot("avatar", "Captain", "s-me", true), pilot("p-vet", "Rosa Kell", "s-vet", false)],
|
|
summaries,
|
|
});
|
|
|
|
describe("pilot share in the client (Step 10J.6)", () => {
|
|
beforeEach(() => {
|
|
vi.useFakeTimers({ toFake: ["Date", "setInterval", "clearInterval"] });
|
|
vi.setSystemTime(NOW);
|
|
useClock.setState({ offsetMs: 0, synced: true, now: NOW });
|
|
});
|
|
afterEach(() => vi.useRealTimers());
|
|
|
|
it("shows the hired pilot's share and the net profit after it, and no share for the avatar", async () => {
|
|
renderApp(
|
|
fakeApi({
|
|
getPlayerState: () => state(),
|
|
getContractBoard: () => ({ contracts: [job], periodEndsAt: NOW + 600_000 }),
|
|
}),
|
|
"/contracts?location=earth",
|
|
);
|
|
await userEvent.click(await screen.findByRole("button", { name: "Dispatch" }));
|
|
const dialog = screen.getByRole("dialog");
|
|
const fuel = tripFuelCost(
|
|
content.shipModel("courier")!,
|
|
content.location("earth")!,
|
|
content.location("moon")!,
|
|
realToGame(NOW, content.config),
|
|
);
|
|
const share = pilotShare(job.payout, fuel, content.config);
|
|
|
|
const hired = within(dialog).getByText("Rosa's Lark").closest("li")!;
|
|
expect(hired).toHaveTextContent(`Pilot share${formatCredits(share)}`);
|
|
expect(hired).toHaveTextContent(`Net profit${formatCredits(job.payout - fuel - share)}`);
|
|
|
|
const mine = within(dialog).getByText("My Lark").closest("li")!;
|
|
expect(mine).not.toHaveTextContent("Pilot share");
|
|
expect(mine).toHaveTextContent(`Net profit${formatCredits(job.payout - fuel)}`);
|
|
});
|
|
|
|
it("lists the pilot's share in a delivery summary", async () => {
|
|
renderApp(fakeApi({ getPlayerState: () => state([summary]) }), "/");
|
|
const dialog = await screen.findByRole("dialog", { name: "While you were away" });
|
|
expect(dialog).toHaveTextContent(`${formatCredits(63)} pilot share to Rosa Kell`);
|
|
});
|
|
});
|