1import { beforeEach, vi } from "vite-plus/test";
2import { describe, expect, it } from "@effect/vitest";
3import * as Effect from "effect/Effect";
4import * as Notifications from "expo-notifications";
5import { requestAgentNotificationPermission } from "./notificationPermissions";
6
7const platform = vi.hoisted(() => ({ OS: "android" }));
8vi.mock("react-native", () => ({ Platform: platform }));
9vi.mock("expo-notifications", () => ({
10 AndroidImportance: { HIGH: 4 },
11 setNotificationChannelAsync: vi.fn(() => Promise.resolve(null)),
12 getPermissionsAsync: vi.fn(() => Promise.resolve({ granted: false, canAskAgain: true })),
13 requestPermissionsAsync: vi.fn(() => Promise.resolve({ granted: true, canAskAgain: true })),
14}));
15
16describe("agent notification permission", () => {
17 beforeEach(() => {
18 vi.clearAllMocks();
19 platform.OS = "android";
20 });
21
22 it.effect("creates an Android channel before prompting for notification permission", () =>
23 Effect.gen(function* () {
24 expect(yield* requestAgentNotificationPermission).toEqual({ type: "granted" });
25 expect(Notifications.setNotificationChannelAsync).toHaveBeenCalledWith("agent-alerts", {
26 name: "Agent alerts",
27 importance: 4,
28 });
29 expect(
30 vi.mocked(Notifications.setNotificationChannelAsync).mock.invocationCallOrder[0],
31 ).toBeLessThan(vi.mocked(Notifications.requestPermissionsAsync).mock.invocationCallOrder[0]!);
32 }),
33 );
34
35 it.effect("preserves denied permission when Android cannot ask again", () =>
36 Effect.gen(function* () {
37 vi.mocked(Notifications.getPermissionsAsync).mockResolvedValueOnce({
38 granted: false,
39 canAskAgain: false,
40 } as Notifications.NotificationPermissionsStatus);
41 expect(yield* requestAgentNotificationPermission).toEqual({
42 type: "denied",
43 canAskAgain: false,
44 });
45 expect(Notifications.requestPermissionsAsync).not.toHaveBeenCalled();
46 }),
47 );
48
49 it.effect("keeps iOS permission requests free of Android channel setup", () =>
50 Effect.gen(function* () {
51 platform.OS = "ios";
52 expect(yield* requestAgentNotificationPermission).toEqual({ type: "granted" });
53 expect(Notifications.setNotificationChannelAsync).not.toHaveBeenCalled();
54 expect(Notifications.requestPermissionsAsync).toHaveBeenCalledWith({
55 ios: { allowAlert: true, allowBadge: true, allowSound: true },
56 });
57 }),
58 );
59});