86 lines
3.4 KiB
TypeScript
86 lines
3.4 KiB
TypeScript
import {
|
|
noNotifications,
|
|
type NotificationScheduler,
|
|
type ScheduledNotification,
|
|
} from "@deadhead/engine";
|
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
import { createEngineClient } from "../src/api/engine-client";
|
|
import { createGame } from "../src/platform/game";
|
|
import { platformNotifications } from "../src/platform/notifications";
|
|
import { DEFAULT_PREFERENCES, usePreferences } from "../src/state/preferences";
|
|
|
|
class FakeScheduler implements NotificationScheduler {
|
|
scheduled: ScheduledNotification[] | null = null;
|
|
permissionAsked = 0;
|
|
async replaceAll(list: ScheduledNotification[]) {
|
|
this.scheduled = list;
|
|
}
|
|
async requestPermission() {
|
|
this.permissionAsked++;
|
|
}
|
|
}
|
|
|
|
afterEach(() => usePreferences.setState(DEFAULT_PREFERENCES));
|
|
|
|
describe("local notifications on the device (Step 14.9)", () => {
|
|
it("uses the no-op scheduler on the web, and playing still works", async () => {
|
|
expect(platformNotifications()).toBe(noNotifications);
|
|
const game = createGame();
|
|
await game.lock.acquire();
|
|
const { state } = await game.engine.call("createGuest", {});
|
|
expect(state.tutorial).not.toBeNull();
|
|
await expect(game.engine.requestNotificationPermission()).resolves.toBeUndefined();
|
|
});
|
|
|
|
it("reschedules when the Settings toggle changes", async () => {
|
|
const scheduler = new FakeScheduler();
|
|
const game = createGame(scheduler);
|
|
await game.lock.acquire();
|
|
await game.engine.call("createGuest", {});
|
|
const [contract] = (await game.engine.call("getTutorialBoard", {})).contracts;
|
|
const { dispatch } = await game.engine.call("createDispatch", {
|
|
kind: "tutorial",
|
|
contractId: contract!.id,
|
|
});
|
|
expect(scheduler.scheduled).toMatchObject([{ at: dispatch.arrivalAt }]);
|
|
|
|
usePreferences.getState().set({ notifications: false });
|
|
await vi.waitFor(() => expect(scheduler.scheduled).toEqual([]));
|
|
usePreferences.getState().set({ notifications: true });
|
|
await vi.waitFor(() => expect(scheduler.scheduled).toMatchObject([{ at: dispatch.arrivalAt }]));
|
|
});
|
|
|
|
it("asks for permission after the deal, not before", async () => {
|
|
const scheduler = new FakeScheduler();
|
|
const game = createGame(scheduler);
|
|
await game.lock.acquire();
|
|
const api = createEngineClient(game.engine);
|
|
await api.call("createGuest", {});
|
|
expect(scheduler.permissionAsked).toBe(0);
|
|
// The deal itself is covered by the engine's tests; here it just succeeds.
|
|
vi.spyOn(game.engine, "call").mockResolvedValueOnce({} as never);
|
|
await api.call("acceptDeal", {});
|
|
await vi.waitFor(() => expect(scheduler.permissionAsked).toBe(1));
|
|
});
|
|
|
|
it("tells the skill point pop-up when the permission prompt is answered (Step 17.6)", async () => {
|
|
let answer!: () => void;
|
|
const scheduler = new FakeScheduler();
|
|
scheduler.requestPermission = () => new Promise<void>((resolve) => (answer = resolve));
|
|
const game = createGame(scheduler);
|
|
await game.lock.acquire();
|
|
const api = createEngineClient(game.engine);
|
|
// Before the deal there is nothing to wait for.
|
|
await expect(api.notificationPrompt!()).resolves.toBeUndefined();
|
|
|
|
vi.spyOn(game.engine, "call").mockResolvedValueOnce({} as never);
|
|
await api.call("acceptDeal", {});
|
|
let answered = false;
|
|
void api.notificationPrompt!().then(() => (answered = true));
|
|
await Promise.resolve();
|
|
expect(answered).toBe(false);
|
|
answer();
|
|
await vi.waitFor(() => expect(answered).toBe(true));
|
|
});
|
|
});
|