125 lines
5.1 KiB
TypeScript
125 lines
5.1 KiB
TypeScript
import { screen, waitFor, within } from "@testing-library/react";
|
|
import userEvent from "@testing-library/user-event";
|
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
import { ApiError } from "../src/api/client";
|
|
import { fakeApi, renderApp, samplePlayerState, unauthorized } from "./render";
|
|
|
|
const CODE = "ABCDE-FGHJK-MNPQR-STUVW";
|
|
const owner = () => samplePlayerState({ tutorial: null });
|
|
const restored = () =>
|
|
samplePlayerState({
|
|
tutorial: null,
|
|
company: { id: "c2", name: "Restored Freight", credits: 77_000, loanBalance: 0 },
|
|
});
|
|
|
|
async function openRestore() {
|
|
await userEvent.click(await screen.findByRole("button", { name: "Restore a company" }));
|
|
return screen.getByRole("dialog", { name: "Restore a company" });
|
|
}
|
|
|
|
describe("backup and restore (Step 10K.9)", () => {
|
|
afterEach(() => vi.unstubAllGlobals());
|
|
|
|
it("shows a new code grouped, and Copy puts it on the clipboard", async () => {
|
|
const writeText = vi.fn(() => Promise.resolve());
|
|
Object.defineProperty(navigator, "clipboard", { value: { writeText }, configurable: true });
|
|
renderApp(
|
|
fakeApi({
|
|
getPlayerState: owner,
|
|
getBackupCode: () => ({ hasCode: false, createdAt: null }),
|
|
createBackupCode: () => ({ code: CODE, createdAt: 1 }),
|
|
}),
|
|
"/settings",
|
|
);
|
|
await userEvent.click(await screen.findByRole("button", { name: "Back up company" }));
|
|
const sheet = screen.getByRole("dialog", { name: "Back up your company" });
|
|
await userEvent.click(
|
|
await within(sheet).findByRole("button", { name: "Create a backup code" }),
|
|
);
|
|
expect(await within(sheet).findByLabelText("Backup code")).toHaveTextContent(CODE);
|
|
expect(sheet).toHaveTextContent("Anyone with this code can open your company.");
|
|
await userEvent.click(within(sheet).getByRole("button", { name: "Copy" }));
|
|
expect(writeText).toHaveBeenCalledWith(CODE);
|
|
expect(await within(sheet).findByRole("button", { name: "Copied" })).toBeInTheDocument();
|
|
});
|
|
|
|
it("warns about an existing code before replacing it", async () => {
|
|
renderApp(
|
|
fakeApi({
|
|
getPlayerState: owner,
|
|
getBackupCode: () => ({ hasCode: true, createdAt: 1 }),
|
|
}),
|
|
"/settings",
|
|
);
|
|
await userEvent.click(await screen.findByRole("button", { name: "Back up company" }));
|
|
const sheet = screen.getByRole("dialog", { name: "Back up your company" });
|
|
expect(
|
|
await within(sheet).findByRole("button", { name: "Generate a new code" }),
|
|
).toBeInTheDocument();
|
|
expect(sheet).toHaveTextContent("Your old code will stop working");
|
|
});
|
|
|
|
it("warns that a company past the tutorial is left behind, and not during the tutorial", async () => {
|
|
const { unmount } = renderApp(fakeApi({ getPlayerState: owner }), "/settings");
|
|
expect(await openRestore()).toHaveTextContent("current company will be left behind");
|
|
unmount();
|
|
renderApp(fakeApi({ getPlayerState: () => samplePlayerState() }), "/settings");
|
|
expect(await openRestore()).not.toHaveTextContent("left behind");
|
|
});
|
|
|
|
it("restores and goes to HQ", async () => {
|
|
let done = false;
|
|
const api = fakeApi({
|
|
getPlayerState: () => (done ? restored() : owner()),
|
|
restoreCompany: () => {
|
|
done = true;
|
|
return { playerId: "p2", state: restored() };
|
|
},
|
|
});
|
|
renderApp(api, "/settings");
|
|
const sheet = await openRestore();
|
|
await userEvent.type(within(sheet).getByRole("textbox"), CODE.toLowerCase());
|
|
await userEvent.click(within(sheet).getByRole("button", { name: "Restore" }));
|
|
await waitFor(() =>
|
|
expect(api.call).toHaveBeenCalledWith("restoreCompany", { code: CODE.toLowerCase() }),
|
|
);
|
|
expect(await screen.findByRole("heading", { level: 1, name: "HQ" })).toBeInTheDocument();
|
|
expect(screen.getByLabelText("Credits")).toHaveTextContent("77,000 cr");
|
|
});
|
|
|
|
it("shows a failed restore's error and stays put", async () => {
|
|
renderApp(
|
|
fakeApi({
|
|
getPlayerState: owner,
|
|
restoreCompany: () => {
|
|
throw new ApiError(409, "restore-failed", "That code didn't open a company.");
|
|
},
|
|
}),
|
|
"/settings",
|
|
);
|
|
const sheet = await openRestore();
|
|
await userEvent.type(within(sheet).getByRole("textbox"), "WRONG");
|
|
await userEvent.click(within(sheet).getByRole("button", { name: "Restore" }));
|
|
expect(await within(sheet).findByRole("alert")).toHaveTextContent("didn't open a company");
|
|
expect(screen.getByRole("dialog", { name: "Restore a company" })).toBeInTheDocument();
|
|
});
|
|
|
|
it("offers restore on the start screen too", async () => {
|
|
let done = false;
|
|
renderApp(
|
|
fakeApi({
|
|
getPlayerState: () => (done ? restored() : unauthorized()),
|
|
restoreCompany: () => {
|
|
done = true;
|
|
return { playerId: "p2", state: restored() };
|
|
},
|
|
}),
|
|
);
|
|
const sheet = await openRestore();
|
|
expect(sheet).not.toHaveTextContent("left behind");
|
|
await userEvent.type(within(sheet).getByRole("textbox"), CODE);
|
|
await userEvent.click(within(sheet).getByRole("button", { name: "Restore" }));
|
|
expect(await screen.findByRole("heading", { level: 1, name: "HQ" })).toBeInTheDocument();
|
|
});
|
|
});
|