118 lines
4.7 KiB
TypeScript
118 lines
4.7 KiB
TypeScript
import { content } from "@deadhead/content";
|
|
import type { PlayerState } from "@deadhead/schema";
|
|
import { act, screen, waitFor } from "@testing-library/react";
|
|
import userEvent from "@testing-library/user-event";
|
|
import { describe, expect, it } from "vitest";
|
|
import { usePreferences } from "../src/state/preferences";
|
|
import { fakeApi, renderApp, samplePlayerState, sampleTutorialBoard } from "./render";
|
|
|
|
const POP_UP = "Your pilots are ready to train";
|
|
const veteran = content.veteranPilot;
|
|
const base = samplePlayerState();
|
|
const avatar = base.pilots[0]!;
|
|
|
|
/** The tutorial with the deal on offer; the avatar already has a point from five runs. */
|
|
const offered = (): PlayerState =>
|
|
samplePlayerState({
|
|
tutorial: {
|
|
loanerLocationId: "moon",
|
|
runInFlight: false,
|
|
runsCompleted: 5,
|
|
dealAvailable: true,
|
|
},
|
|
pilots: [{ ...avatar, skillPointsAvailable: 1 }],
|
|
});
|
|
|
|
/** After the deal: You with `you` points, Rosa with `rosa`. */
|
|
const afterDeal = (you = 1, rosa = 2): PlayerState =>
|
|
samplePlayerState({
|
|
tutorial: null,
|
|
bays: [{ id: "b1", locationId: "earth" }],
|
|
pilots: [
|
|
{ ...avatar, skillPointsAvailable: you },
|
|
{
|
|
...avatar,
|
|
id: "veteran",
|
|
name: veteran.name,
|
|
isAvatar: false,
|
|
isVeteran: true,
|
|
skillPointsAvailable: rosa,
|
|
},
|
|
],
|
|
});
|
|
|
|
/** A permission prompt the test answers. */
|
|
function pendingPrompt() {
|
|
let answer!: () => void;
|
|
const answered = new Promise<void>((resolve) => (answer = resolve));
|
|
return { answered, answer };
|
|
}
|
|
|
|
describe("skill point pop-up (Step 17.6, decision 0.5.109)", () => {
|
|
it("appears only after both the deal and the permission prompt, listing each pilot's points", async () => {
|
|
let accepted = false;
|
|
const prompt = pendingPrompt();
|
|
const api = Object.assign(
|
|
fakeApi({
|
|
getPlayerState: () => (accepted ? afterDeal() : offered()),
|
|
getTutorialBoard: () => sampleTutorialBoard(),
|
|
acceptDeal: () => {
|
|
accepted = true;
|
|
return afterDeal();
|
|
},
|
|
}),
|
|
{ notificationPrompt: () => (accepted ? prompt.answered : Promise.resolve()) },
|
|
);
|
|
renderApp(api, "/contracts");
|
|
|
|
// During the tutorial the avatar's point doesn't bring it up.
|
|
await screen.findByRole("dialog", { name: "An offer from Halvorsen" });
|
|
expect(screen.queryByRole("dialog", { name: POP_UP })).not.toBeInTheDocument();
|
|
|
|
await userEvent.click(screen.getByRole("button", { name: "Shake on it" }));
|
|
await screen.findByRole("heading", { level: 1, name: "Bridge" });
|
|
// The deal is done, but the permission prompt is still open.
|
|
expect(screen.queryByRole("dialog", { name: POP_UP })).not.toBeInTheDocument();
|
|
|
|
act(() => prompt.answer());
|
|
const dialog = await screen.findByRole("dialog", { name: POP_UP });
|
|
expect(dialog).toHaveTextContent("You and Rosa both have skill points to spend.");
|
|
expect(screen.getByLabelText("Unspent skill points")).toHaveTextContent(
|
|
"You: 1 point · Rosa: 2 points",
|
|
);
|
|
});
|
|
|
|
it("Go to Pilots opens the Pilots tab and the pop-up doesn't come back", async () => {
|
|
renderApp(fakeApi({ getPlayerState: () => afterDeal() }), "/");
|
|
await userEvent.click(await screen.findByRole("button", { name: "Go to Pilots" }));
|
|
await screen.findByRole("heading", { level: 1, name: "Crew" });
|
|
expect(screen.queryByRole("dialog", { name: POP_UP })).not.toBeInTheDocument();
|
|
});
|
|
|
|
it("Later dismisses it until tips are shown again", async () => {
|
|
renderApp(fakeApi({ getPlayerState: () => afterDeal(0, 2) }), "/");
|
|
const dialog = await screen.findByRole("dialog", { name: POP_UP });
|
|
// Only the pilots with points are listed.
|
|
expect(dialog).toHaveTextContent("Rosa has skill points to spend.");
|
|
expect(screen.getByLabelText("Unspent skill points")).toHaveTextContent(/^Rosa: 2 points$/);
|
|
|
|
await userEvent.click(screen.getByRole("button", { name: "Later" }));
|
|
await waitFor(() =>
|
|
expect(screen.queryByRole("dialog", { name: POP_UP })).not.toBeInTheDocument(),
|
|
);
|
|
await screen.findByRole("heading", { level: 1, name: "Bridge" });
|
|
expect(screen.queryByRole("dialog", { name: POP_UP })).not.toBeInTheDocument();
|
|
|
|
act(() => usePreferences.getState().showTipsAgain());
|
|
expect(await screen.findByRole("dialog", { name: POP_UP })).toBeInTheDocument();
|
|
});
|
|
|
|
it("doesn't appear with no unspent points", async () => {
|
|
renderApp(fakeApi({ getPlayerState: () => afterDeal(0, 0) }), "/");
|
|
await screen.findByRole("heading", { level: 1, name: "Bridge" });
|
|
// Give the prompt check a turn to settle.
|
|
await act(async () => {});
|
|
expect(screen.queryByRole("dialog", { name: POP_UP })).not.toBeInTheDocument();
|
|
});
|
|
});
|