import { content } from "@deadhead/content"; import { screen, waitFor } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { describe, expect, it } from "vitest"; import { fakeApi, renderApp, samplePlayerState, sampleTutorialBoard, shipExtras } from "./render"; const offered = () => samplePlayerState({ tutorial: { loanerLocationId: "earth", runInFlight: false, runsCompleted: 4, dealAvailable: true, }, }); /** State the server returns after the deal: tutorial over, the Courier and Clipper, a bay, the loan. */ const afterDeal = () => samplePlayerState({ tutorial: null, company: { id: "co1", name: "Deadhead Freight", credits: 800, loanBalance: content.dealLoanAmount, }, ships: [ { id: "s1", modelId: "courier", name: "Dusty Mule", status: "idle", locationId: "earth", dispatchId: null, lastArrivalAt: 0, ...shipExtras("courier"), pilotId: "avatar", }, { id: "s2", modelId: "clipper", name: "Swift Heron", status: "idle", locationId: "earth", dispatchId: null, lastArrivalAt: 0, ...shipExtras("clipper"), pilotId: "veteran", }, ], bays: [{ id: "b1", locationId: "earth" }], }); describe("the deal", () => { it("does not appear when the deal is not available", async () => { renderApp( fakeApi({ getPlayerState: () => samplePlayerState(), getTutorialBoard: () => sampleTutorialBoard(), }), "/contracts", ); await screen.findByRole("heading", { level: 1, name: "Contracts" }); expect( screen.queryByRole("dialog", { name: "An offer from Halvorsen" }), ).not.toBeInTheDocument(); }); it("appears when available, showing the configured loan and repayment share", async () => { renderApp( fakeApi({ getPlayerState: offered, getTutorialBoard: () => sampleTutorialBoard() }), "/contracts", ); const dialog = await screen.findByRole("dialog", { name: "An offer from Halvorsen" }); // Both ships and the veteran (CR-013), names from content. const veteran = content.veteranPilot; expect(screen.getByLabelText("Ships and bay")).toHaveTextContent( `A Courier for you, a Clipper for ${veteran.name}, and a bay at Earth`, ); expect(dialog).toHaveTextContent("his Courier, his Clipper, and his Earth bay"); expect(screen.getByLabelText("Veteran pilot")).toHaveTextContent(veteran.name); expect(screen.getByLabelText("Veteran pilot")).toHaveTextContent(veteran.bio); expect(screen.getByLabelText("Loan amount")).toHaveTextContent( `${content.dealLoanAmount.toLocaleString("en-US")} cr`, ); expect(screen.getByLabelText("Repayment share")).toHaveTextContent( `${Math.round(content.config.loanRepaymentShare * 100)}% of every payout`, ); expect(screen.getAllByRole("button").filter((b) => dialog.contains(b))).toHaveLength(1); }); it("waits until the delivery summary is dismissed", async () => { const withSummary = () => ({ ...offered(), summaries: [ { dispatchId: "d4", shipId: "loaner", pilotId: "avatar", originId: "moon", destinationId: "earth", tutorial: true, credits: 96, deadhead: false, independent: false, fuelCost: 0, topUp: null, goodId: "consumer-goods", massTonnes: 12, shipper: "Pacific Orbital Logistics", loanRepayment: 0, pilotPay: 0, pilotXp: 10, discoveredLocationId: null, scriptedId: null, event: null, restEndsAt: null, resolvedAt: 1, }, ], }); renderApp( fakeApi({ getPlayerState: withSummary, getTutorialBoard: () => sampleTutorialBoard() }), "/contracts", ); await screen.findByRole("dialog", { name: /While you were away/ }); expect( screen.queryByRole("dialog", { name: "An offer from Halvorsen" }), ).not.toBeInTheDocument(); }); it("Shake on it accepts the deal and goes to HQ", async () => { let accepted = false; const api = fakeApi({ getPlayerState: () => (accepted ? afterDeal() : offered()), getTutorialBoard: () => sampleTutorialBoard(), acceptDeal: () => { accepted = true; return afterDeal(); }, }); renderApp(api, "/contracts"); // The loan offered is the loan the server books (CR-013: Courier + Clipper + bay). expect(await screen.findByLabelText("Loan amount")).toHaveTextContent( `${afterDeal().company.loanBalance.toLocaleString("en-US")} cr`, ); await userEvent.click(await screen.findByRole("button", { name: "Shake on it" })); await waitFor(() => expect(api.call).toHaveBeenCalledWith("acceptDeal", {})); expect(await screen.findByRole("heading", { level: 1, name: "Dashboard" })).toBeInTheDocument(); expect( screen.queryByRole("dialog", { name: "An offer from Halvorsen" }), ).not.toBeInTheDocument(); }); });