吸收数字员工通知权限引导
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const notificationMock = vi.hoisted(() => ({
|
||||
instances: [] as Array<{ options: Record<string, unknown> }>,
|
||||
show: vi.fn(),
|
||||
isSupported: vi.fn(() => true),
|
||||
openExternal: vi.fn(async () => undefined),
|
||||
}));
|
||||
|
||||
vi.mock("electron", () => {
|
||||
@@ -24,16 +25,29 @@ vi.mock("electron", () => {
|
||||
}
|
||||
}
|
||||
|
||||
return { Notification: MockNotification };
|
||||
return { Notification: MockNotification, shell: { openExternal: notificationMock.openExternal } };
|
||||
});
|
||||
|
||||
const originalPlatform = process.platform;
|
||||
|
||||
function setPlatform(platform: NodeJS.Platform): void {
|
||||
Object.defineProperty(process, "platform", { value: platform, configurable: true });
|
||||
}
|
||||
|
||||
describe("digital employee desktop notifications", () => {
|
||||
beforeEach(() => {
|
||||
vi.resetModules();
|
||||
setPlatform(originalPlatform);
|
||||
notificationMock.instances = [];
|
||||
notificationMock.show.mockReset();
|
||||
notificationMock.isSupported.mockReset();
|
||||
notificationMock.isSupported.mockReturnValue(true);
|
||||
notificationMock.openExternal.mockReset();
|
||||
notificationMock.openExternal.mockResolvedValue(undefined);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
setPlatform(originalPlatform);
|
||||
});
|
||||
|
||||
it("shows unread action notifications through Electron Notification", async () => {
|
||||
@@ -120,4 +134,52 @@ describe("digital employee desktop notifications", () => {
|
||||
error: "system denied",
|
||||
});
|
||||
});
|
||||
|
||||
it("reports desktop notification status from Electron support", async () => {
|
||||
const { getDigitalEmployeeDesktopNotificationStatus } = await import("./desktopNotificationService");
|
||||
|
||||
expect(getDigitalEmployeeDesktopNotificationStatus()).toMatchObject({
|
||||
supported: true,
|
||||
platform: originalPlatform,
|
||||
can_open_settings: ["darwin", "win32"].includes(originalPlatform),
|
||||
reason: null,
|
||||
});
|
||||
|
||||
notificationMock.isSupported.mockReturnValue(false);
|
||||
expect(getDigitalEmployeeDesktopNotificationStatus()).toMatchObject({
|
||||
supported: false,
|
||||
reason: "notification_unavailable",
|
||||
});
|
||||
});
|
||||
|
||||
it("opens macOS and Windows notification settings", async () => {
|
||||
const { openDigitalEmployeeDesktopNotificationSettings } = await import("./desktopNotificationService");
|
||||
|
||||
setPlatform("darwin");
|
||||
await expect(openDigitalEmployeeDesktopNotificationSettings()).resolves.toMatchObject({ success: true, platform: "darwin" });
|
||||
expect(notificationMock.openExternal).toHaveBeenLastCalledWith(expect.stringContaining("x-apple.systempreferences"));
|
||||
|
||||
setPlatform("win32");
|
||||
await expect(openDigitalEmployeeDesktopNotificationSettings()).resolves.toMatchObject({ success: true, platform: "win32" });
|
||||
expect(notificationMock.openExternal).toHaveBeenLastCalledWith("ms-settings:notifications");
|
||||
});
|
||||
|
||||
it("returns a safe failure when notification settings cannot be opened", async () => {
|
||||
const { openDigitalEmployeeDesktopNotificationSettings } = await import("./desktopNotificationService");
|
||||
|
||||
setPlatform("linux");
|
||||
await expect(openDigitalEmployeeDesktopNotificationSettings()).resolves.toMatchObject({
|
||||
success: false,
|
||||
platform: "linux",
|
||||
reason: "notification_settings_unavailable",
|
||||
});
|
||||
expect(notificationMock.openExternal).not.toHaveBeenCalled();
|
||||
|
||||
setPlatform("darwin");
|
||||
notificationMock.openExternal.mockRejectedValueOnce(new Error("settings denied"));
|
||||
await expect(openDigitalEmployeeDesktopNotificationSettings()).resolves.toMatchObject({
|
||||
success: false,
|
||||
error: "settings denied",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Notification } from "electron";
|
||||
import { Notification, shell } from "electron";
|
||||
|
||||
const IMPORTANT_LEVELS = new Set(["action", "warn", "error"]);
|
||||
const TITLE_MAX_LENGTH = 80;
|
||||
@@ -22,6 +22,25 @@ export interface DigitalEmployeeDesktopNotificationResult {
|
||||
error?: string | null;
|
||||
}
|
||||
|
||||
export interface DigitalEmployeeDesktopNotificationStatus {
|
||||
supported: boolean;
|
||||
platform: NodeJS.Platform;
|
||||
can_open_settings: boolean;
|
||||
reason: string | null;
|
||||
}
|
||||
|
||||
export interface DigitalEmployeeDesktopNotificationSettingsResult {
|
||||
success: boolean;
|
||||
platform: NodeJS.Platform;
|
||||
reason?: string | null;
|
||||
error?: string | null;
|
||||
}
|
||||
|
||||
const NOTIFICATION_SETTINGS_URLS: Partial<Record<NodeJS.Platform, string>> = {
|
||||
darwin: "x-apple.systempreferences:com.apple.Notifications-Settings.extension",
|
||||
win32: "ms-settings:notifications",
|
||||
};
|
||||
|
||||
export function showDigitalEmployeeDesktopNotification(
|
||||
input: DigitalEmployeeDesktopNotificationInput,
|
||||
): DigitalEmployeeDesktopNotificationResult {
|
||||
@@ -45,6 +64,29 @@ export function showDigitalEmployeeDesktopNotification(
|
||||
}
|
||||
}
|
||||
|
||||
export function getDigitalEmployeeDesktopNotificationStatus(): DigitalEmployeeDesktopNotificationStatus {
|
||||
const supported = Boolean(Notification && typeof Notification.isSupported === "function" && Notification.isSupported());
|
||||
return {
|
||||
supported,
|
||||
platform: process.platform,
|
||||
can_open_settings: Boolean(NOTIFICATION_SETTINGS_URLS[process.platform]),
|
||||
reason: supported ? null : "notification_unavailable",
|
||||
};
|
||||
}
|
||||
|
||||
export async function openDigitalEmployeeDesktopNotificationSettings(): Promise<DigitalEmployeeDesktopNotificationSettingsResult> {
|
||||
const url = NOTIFICATION_SETTINGS_URLS[process.platform];
|
||||
if (!url) {
|
||||
return { success: false, platform: process.platform, reason: "notification_settings_unavailable" };
|
||||
}
|
||||
try {
|
||||
await shell.openExternal(url);
|
||||
return { success: true, platform: process.platform };
|
||||
} catch (error) {
|
||||
return { success: false, platform: process.platform, error: normalizeError(error) };
|
||||
}
|
||||
}
|
||||
|
||||
function stringField(value: unknown): string {
|
||||
return typeof value === "string" ? value.trim() : "";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user