import type { PlayerState } from "@deadhead/schema"; import { cleanup, screen, waitFor, within } 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, unauthorized } from "./render"; /** A fresh guest: "Start your company", then the new company with its welcome unseen. */ function newCompany() { let started = false; let state: PlayerState = samplePlayerState({ welcomeSeen: false }); const api = fakeApi({ getPlayerState: () => (started ? state : unauthorized()), createGuest: () => { started = true; return { playerId: "p1", state }; }, getTutorialBoard: () => sampleTutorialBoard(), markWelcomeSeen: () => { state = { ...state, welcomeSeen: true }; return state; }, }); return { api, state: () => state }; } async function start(api: ReturnType["api"]) { renderApp(api, "/"); await userEvent.click(await screen.findByRole("button", { name: "Start your company" })); return screen.findByRole("dialog", { name: "Welcome to Deadhead" }); } describe("welcome cards (Step 22.2, decision 0.5.131)", () => { it("walks through three cards and lands on Contracts with the intro card", async () => { const { api, state } = newCompany(); const dialog = await start(api); expect(dialog).toHaveTextContent("1 of 3"); expect(dialog).toHaveTextContent("never fly home empty"); await userEvent.click(within(dialog).getByRole("button", { name: "Next" })); expect(dialog).toHaveTextContent("2 of 3"); expect(within(dialog).getByRole("heading")).toHaveTextContent("Your ships keep flying"); await userEvent.click(within(dialog).getByRole("button", { name: "Next" })); expect(dialog).toHaveTextContent("3 of 3"); expect(dialog).toHaveTextContent( "You're flying for Halvorsen Lunar Freight on the Earth–Moon run.", ); expect(within(dialog).queryByRole("button", { name: "Next" })).not.toBeInTheDocument(); await userEvent.click(within(dialog).getByRole("button", { name: "Let's fly" })); await waitFor(() => expect(screen.queryByRole("dialog")).not.toBeInTheDocument()); expect(api.call).toHaveBeenCalledWith("markWelcomeSeen", {}); expect(state().welcomeSeen).toBe(true); expect(await screen.findByRole("heading", { level: 1, name: "Contracts" })).toBeInTheDocument(); expect(await screen.findByLabelText("Intro")).toHaveTextContent("needs a pilot"); }); it("closes from Skip on any card, marking it seen and landing on Contracts", async () => { for (const nexts of [0, 1, 2]) { const { api, state } = newCompany(); const dialog = await start(api); for (let i = 0; i < nexts; i++) { await userEvent.click(within(dialog).getByRole("button", { name: "Next" })); } await userEvent.click(within(dialog).getByRole("button", { name: "Skip" })); await waitFor(() => expect(screen.queryByRole("dialog")).not.toBeInTheDocument()); expect(state().welcomeSeen).toBe(true); expect(await screen.findByLabelText("Intro")).toBeInTheDocument(); cleanup(); } }); it("doesn't come back after a reload once seen", async () => { const { api } = newCompany(); const dialog = await start(api); await userEvent.click(within(dialog).getByRole("button", { name: "Skip" })); await waitFor(() => expect(screen.queryByRole("dialog")).not.toBeInTheDocument()); cleanup(); renderApp(api, "/contracts"); expect(await screen.findByLabelText("Intro")).toBeInTheDocument(); expect(screen.queryByRole("dialog", { name: "Welcome to Deadhead" })).not.toBeInTheDocument(); }); it("slides cards in, but not with reduced motion on", async () => { const { api } = newCompany(); await start(api); expect(screen.getByTestId("welcome-card")).toHaveClass("animate-slide"); cleanup(); usePreferences.getState().set({ reducedMotion: true }); const again = newCompany(); await start(again.api); expect(screen.getByTestId("welcome-card")).not.toHaveClass("animate-slide"); usePreferences.getState().set({ reducedMotion: null }); }); it("keeps focus inside the pop-up until it closes", async () => { const { api } = newCompany(); const dialog = await start(api); // Focus starts on the main button. expect(within(dialog).getByRole("button", { name: "Next" })).toHaveFocus(); for (let i = 0; i < 5; i++) { await userEvent.tab(); expect(dialog).toContainElement(document.activeElement as HTMLElement); } await userEvent.tab({ shift: true }); expect(dialog).toContainElement(document.activeElement as HTMLElement); }); });