105 lines
4.8 KiB
TypeScript
105 lines
4.8 KiB
TypeScript
import { DeviceClock, GameEngine, MemoryBackend, SaveStorage } from "@deadhead/engine";
|
|
import { screen, waitFor, within } from "@testing-library/react";
|
|
import userEvent from "@testing-library/user-event";
|
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
import { createEngineClient } from "../src/api/engine-client";
|
|
import { saveFile } from "../src/screens/SaveSettings";
|
|
import { renderApp } from "./render";
|
|
|
|
/** A device with its own storage, running the real engine. */
|
|
function device() {
|
|
const engine = new GameEngine({
|
|
storage: new SaveStorage(new MemoryBackend()),
|
|
clock: new DeviceClock(Date.now),
|
|
});
|
|
return { engine, api: createEngineClient(engine) };
|
|
}
|
|
|
|
async function exportFrom(path = "/settings") {
|
|
const home = device();
|
|
await home.engine.call("createGuest", {});
|
|
const writeText = vi.fn((_: string) => Promise.resolve());
|
|
Object.defineProperty(navigator, "clipboard", { value: { writeText }, configurable: true });
|
|
const view = renderApp(home.api, path);
|
|
await userEvent.click(await screen.findByRole("button", { name: "Export save" }));
|
|
const sheet = screen.getByRole("dialog", { name: "Export save" });
|
|
const box = await within(sheet).findByLabelText("Save code");
|
|
await userEvent.click(within(sheet).getByRole("button", { name: "Copy" }));
|
|
expect(await within(sheet).findByRole("button", { name: "Copied" })).toBeInTheDocument();
|
|
const code = (box as HTMLTextAreaElement).value;
|
|
expect(writeText).toHaveBeenCalledWith(code);
|
|
view.unmount();
|
|
return { home, code };
|
|
}
|
|
|
|
describe("export and import save (Step 14.10)", () => {
|
|
afterEach(() => vi.unstubAllGlobals());
|
|
|
|
it("exports a code that opens the same company on a fresh device", async () => {
|
|
const { home, code } = await exportFrom();
|
|
const before = await home.engine.call("getPlayerState", {});
|
|
|
|
const fresh = device();
|
|
renderApp(fresh.api, "/");
|
|
await userEvent.click(await screen.findByRole("button", { name: "Import save" }));
|
|
const sheet = screen.getByRole("dialog", { name: "Import save" });
|
|
// Nothing to replace on a fresh device, so no warning.
|
|
expect(within(sheet).queryByRole("note")).toBeNull();
|
|
await userEvent.click(within(sheet).getByRole("textbox"));
|
|
await userEvent.paste(code);
|
|
await userEvent.click(within(sheet).getByRole("button", { name: "Import" }));
|
|
await userEvent.click(within(sheet).getByRole("button", { name: "Open company" }));
|
|
|
|
await waitFor(() => expect(screen.queryByRole("dialog", { name: "Import save" })).toBeNull());
|
|
expect(await fresh.engine.call("getPlayerState", {})).toEqual(before);
|
|
});
|
|
|
|
it("opens a save file, warns before replacing, then replaces the company", async () => {
|
|
const { home, code } = await exportFrom();
|
|
const other = device();
|
|
await other.engine.call("createGuest", {});
|
|
renderApp(other.api, "/settings");
|
|
|
|
await userEvent.click(await screen.findByRole("button", { name: "Import save" }));
|
|
const sheet = screen.getByRole("dialog", { name: "Import save" });
|
|
expect(within(sheet).getByRole("note")).toHaveTextContent(
|
|
"This replaces the company on this device.",
|
|
);
|
|
const input = sheet.querySelector('input[type="file"]') as HTMLInputElement;
|
|
await userEvent.upload(input, saveFile(code));
|
|
await waitFor(() => expect(within(sheet).getByRole("textbox")).toHaveValue(code));
|
|
await userEvent.click(within(sheet).getByRole("button", { name: "Import" }));
|
|
expect(sheet).toHaveTextContent("Replace your company with this save?");
|
|
await userEvent.click(within(sheet).getByRole("button", { name: "Replace company" }));
|
|
|
|
await waitFor(() => expect(screen.queryByRole("dialog", { name: "Import save" })).toBeNull());
|
|
expect((await other.engine.call("getPlayerState", {})).company).toEqual(
|
|
(await home.engine.call("getPlayerState", {})).company,
|
|
);
|
|
});
|
|
|
|
it("shows an error for garbage and changes nothing", async () => {
|
|
const own = device();
|
|
await own.engine.call("createGuest", {});
|
|
const before = await own.engine.call("getPlayerState", {});
|
|
renderApp(own.api, "/settings");
|
|
|
|
await userEvent.click(await screen.findByRole("button", { name: "Import save" }));
|
|
const sheet = screen.getByRole("dialog", { name: "Import save" });
|
|
await userEvent.type(within(sheet).getByRole("textbox"), "not a save at all");
|
|
await userEvent.click(within(sheet).getByRole("button", { name: "Import" }));
|
|
await userEvent.click(within(sheet).getByRole("button", { name: "Replace company" }));
|
|
|
|
expect(await within(sheet).findByRole("alert")).toHaveTextContent(
|
|
"That isn't a Deadhead save code.",
|
|
);
|
|
expect(await own.engine.call("getPlayerState", {})).toEqual(before);
|
|
});
|
|
|
|
it("names the save file by date", () => {
|
|
expect(saveFile("x", new Date("2101-02-03T04:05:06Z")).name).toBe(
|
|
"deadhead-save-2101-02-03.txt",
|
|
);
|
|
});
|
|
});
|