test: add src/native/badges.test.ts
This commit is contained in:
parent
d57aceebff
commit
f9c616ba6e
|
|
@ -0,0 +1,336 @@
|
||||||
|
/// <reference types="jest" />
|
||||||
|
|
||||||
|
const mockSetOverlayIcon = jest.fn();
|
||||||
|
const mockSetBadge = jest.fn();
|
||||||
|
const mockCreateFromDataURL = jest.fn();
|
||||||
|
const mockDbusMessage = jest.fn();
|
||||||
|
const mockDbusConnection = { message: mockDbusMessage };
|
||||||
|
const mockDbusSessionBus = jest.fn().mockReturnValue({
|
||||||
|
connection: mockDbusConnection,
|
||||||
|
});
|
||||||
|
|
||||||
|
jest.mock("@homebridge/dbus-native", () => ({
|
||||||
|
messageType: {
|
||||||
|
signal: "signal",
|
||||||
|
},
|
||||||
|
sessionBus: mockDbusSessionBus,
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock("electron", () => ({
|
||||||
|
app: {
|
||||||
|
dock: {
|
||||||
|
setBadge: mockSetBadge,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
nativeImage: {
|
||||||
|
createFromDataURL: mockCreateFromDataURL,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock("./window", () => ({
|
||||||
|
mainWindow: {
|
||||||
|
setOverlayIcon: mockSetOverlayIcon,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Mock dynamic imports for .ico?asset files — dynamic import returns a module
|
||||||
|
// object with a `default` export, so each mock returns an object with `default`.
|
||||||
|
jest.mock(
|
||||||
|
"../../assets/desktop/badges/1.ico?asset",
|
||||||
|
() => ({ default: "mocked-icon-1" }),
|
||||||
|
{ virtual: true },
|
||||||
|
);
|
||||||
|
jest.mock(
|
||||||
|
"../../assets/desktop/badges/2.ico?asset",
|
||||||
|
() => ({ default: "mocked-icon-2" }),
|
||||||
|
{ virtual: true },
|
||||||
|
);
|
||||||
|
jest.mock(
|
||||||
|
"../../assets/desktop/badges/3.ico?asset",
|
||||||
|
() => ({ default: "mocked-icon-3" }),
|
||||||
|
{ virtual: true },
|
||||||
|
);
|
||||||
|
jest.mock(
|
||||||
|
"../../assets/desktop/badges/4.ico?asset",
|
||||||
|
() => ({ default: "mocked-icon-4" }),
|
||||||
|
{ virtual: true },
|
||||||
|
);
|
||||||
|
jest.mock(
|
||||||
|
"../../assets/desktop/badges/5.ico?asset",
|
||||||
|
() => ({ default: "mocked-icon-5" }),
|
||||||
|
{ virtual: true },
|
||||||
|
);
|
||||||
|
jest.mock(
|
||||||
|
"../../assets/desktop/badges/6.ico?asset",
|
||||||
|
() => ({ default: "mocked-icon-6" }),
|
||||||
|
{ virtual: true },
|
||||||
|
);
|
||||||
|
jest.mock(
|
||||||
|
"../../assets/desktop/badges/7.ico?asset",
|
||||||
|
() => ({ default: "mocked-icon-7" }),
|
||||||
|
{ virtual: true },
|
||||||
|
);
|
||||||
|
jest.mock(
|
||||||
|
"../../assets/desktop/badges/8.ico?asset",
|
||||||
|
() => ({ default: "mocked-icon-8" }),
|
||||||
|
{ virtual: true },
|
||||||
|
);
|
||||||
|
jest.mock(
|
||||||
|
"../../assets/desktop/badges/9.ico?asset",
|
||||||
|
() => ({ default: "mocked-icon-9" }),
|
||||||
|
{ virtual: true },
|
||||||
|
);
|
||||||
|
jest.mock(
|
||||||
|
"../../assets/desktop/badges/10.ico?asset",
|
||||||
|
() => ({ default: "mocked-icon-10" }),
|
||||||
|
{ virtual: true },
|
||||||
|
);
|
||||||
|
jest.mock(
|
||||||
|
"../../assets/desktop/badges/-1.ico?asset",
|
||||||
|
() => ({ default: "mocked-icon--1" }),
|
||||||
|
{ virtual: true },
|
||||||
|
);
|
||||||
|
|
||||||
|
// Import after all mocks
|
||||||
|
import { setBadgeCount } from "./badges";
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
// Restore the default return value that clearAllMocks wipes out
|
||||||
|
mockDbusSessionBus.mockReturnValue({
|
||||||
|
connection: mockDbusConnection,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("setBadgeCount", () => {
|
||||||
|
describe("win32", () => {
|
||||||
|
const originalPlatform = process.platform;
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
Object.defineProperty(process, "platform", {
|
||||||
|
value: "win32",
|
||||||
|
configurable: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
Object.defineProperty(process, "platform", {
|
||||||
|
value: originalPlatform,
|
||||||
|
configurable: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should clear overlay icon when count is 0", async () => {
|
||||||
|
await setBadgeCount(0);
|
||||||
|
|
||||||
|
expect(mockSetOverlayIcon).toHaveBeenCalledWith(
|
||||||
|
null,
|
||||||
|
"No Notifications",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not load icon when count is 0", async () => {
|
||||||
|
await setBadgeCount(0);
|
||||||
|
|
||||||
|
expect(mockCreateFromDataURL).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should load and set overlay icon for count > 0", async () => {
|
||||||
|
const mockImage = {};
|
||||||
|
mockCreateFromDataURL.mockReturnValue(mockImage);
|
||||||
|
|
||||||
|
await setBadgeCount(5);
|
||||||
|
|
||||||
|
expect(mockCreateFromDataURL).toHaveBeenCalled();
|
||||||
|
expect(mockSetOverlayIcon).toHaveBeenCalledWith(
|
||||||
|
mockImage,
|
||||||
|
"5 Notifications",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should cache native icons and reuse them", async () => {
|
||||||
|
const mockImage = {};
|
||||||
|
mockCreateFromDataURL.mockReturnValue(mockImage);
|
||||||
|
|
||||||
|
await setBadgeCount(3);
|
||||||
|
mockCreateFromDataURL.mockClear();
|
||||||
|
|
||||||
|
await setBadgeCount(3);
|
||||||
|
|
||||||
|
expect(mockCreateFromDataURL).not.toHaveBeenCalled();
|
||||||
|
expect(mockSetOverlayIcon).toHaveBeenCalledWith(
|
||||||
|
mockImage,
|
||||||
|
"3 Notifications",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should cap icon filename at 10 for counts greater than 10", async () => {
|
||||||
|
const mockImage = {};
|
||||||
|
mockCreateFromDataURL.mockReturnValue(mockImage);
|
||||||
|
|
||||||
|
await setBadgeCount(99);
|
||||||
|
|
||||||
|
expect(mockCreateFromDataURL).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({ default: expect.objectContaining({ default: "mocked-icon-10" }) }),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should show 'Unread Messages' description when count is -1", async () => {
|
||||||
|
const mockImage = {};
|
||||||
|
mockCreateFromDataURL.mockReturnValue(mockImage);
|
||||||
|
|
||||||
|
await setBadgeCount(-1);
|
||||||
|
|
||||||
|
expect(mockSetOverlayIcon).toHaveBeenCalledWith(
|
||||||
|
mockImage,
|
||||||
|
"Unread Messages",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("darwin", () => {
|
||||||
|
const originalPlatform = process.platform;
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
Object.defineProperty(process, "platform", {
|
||||||
|
value: "darwin",
|
||||||
|
configurable: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
Object.defineProperty(process, "platform", {
|
||||||
|
value: originalPlatform,
|
||||||
|
configurable: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should set badge to '•' when count is -1", async () => {
|
||||||
|
await setBadgeCount(-1);
|
||||||
|
|
||||||
|
expect(mockSetBadge).toHaveBeenCalledWith("•");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should set badge to empty string when count is 0", async () => {
|
||||||
|
await setBadgeCount(0);
|
||||||
|
|
||||||
|
expect(mockSetBadge).toHaveBeenCalledWith("");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should set badge to count string when count > 0", async () => {
|
||||||
|
await setBadgeCount(7);
|
||||||
|
|
||||||
|
expect(mockSetBadge).toHaveBeenCalledWith("7");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not use overlay icon on darwin", async () => {
|
||||||
|
await setBadgeCount(5);
|
||||||
|
|
||||||
|
expect(mockSetOverlayIcon).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("linux D-Bus", () => {
|
||||||
|
const originalPlatform = process.platform;
|
||||||
|
const originalContainer = process.env.container;
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
Object.defineProperty(process, "platform", {
|
||||||
|
value: "_",
|
||||||
|
configurable: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
Object.defineProperty(process, "platform", {
|
||||||
|
value: originalPlatform,
|
||||||
|
configurable: true,
|
||||||
|
});
|
||||||
|
process.env.container = originalContainer;
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should create a session bus connection if not exists", async () => {
|
||||||
|
process.env.container = "0";
|
||||||
|
await setBadgeCount(5);
|
||||||
|
|
||||||
|
expect(mockDbusSessionBus).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should reuse existing session bus on subsequent calls", async () => {
|
||||||
|
process.env.container = "0";
|
||||||
|
mockDbusSessionBus.mockClear();
|
||||||
|
|
||||||
|
await setBadgeCount(10);
|
||||||
|
|
||||||
|
// sessionBus was already created, so it should not be called again
|
||||||
|
expect(mockDbusSessionBus).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should send a D-Bus signal message with correct properties", async () => {
|
||||||
|
process.env.container = "0";
|
||||||
|
mockDbusMessage.mockClear();
|
||||||
|
|
||||||
|
await setBadgeCount(5);
|
||||||
|
|
||||||
|
expect(mockDbusMessage).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
type: "signal",
|
||||||
|
path: "/",
|
||||||
|
interface: "com.canonical.Unity.LauncherEntry",
|
||||||
|
member: "Update",
|
||||||
|
signature: "sa{sv}",
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should use flatpak app ID when container env is '1'", async () => {
|
||||||
|
process.env.container = "1";
|
||||||
|
mockDbusMessage.mockClear();
|
||||||
|
|
||||||
|
await setBadgeCount(5);
|
||||||
|
|
||||||
|
const msg = mockDbusMessage.mock.calls[0][0];
|
||||||
|
expect(msg.body[0]).toBe(
|
||||||
|
"application://chat.stoat.stoat-desktop.desktop",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should use standard app ID when container env is not '1'", async () => {
|
||||||
|
process.env.container = "0";
|
||||||
|
mockDbusMessage.mockClear();
|
||||||
|
|
||||||
|
await setBadgeCount(5);
|
||||||
|
|
||||||
|
const msg = mockDbusMessage.mock.calls[0][0];
|
||||||
|
expect(msg.body[0]).toBe("application://stoat-desktop.desktop");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should set count-visible to false when count is 0", async () => {
|
||||||
|
process.env.container = "0";
|
||||||
|
mockDbusMessage.mockClear();
|
||||||
|
|
||||||
|
await setBadgeCount(0);
|
||||||
|
|
||||||
|
const msg = mockDbusMessage.mock.calls[0][0];
|
||||||
|
const props = msg.body[1];
|
||||||
|
const countVisible = props.find(
|
||||||
|
(p: [string, unknown[]]) => p[0] === "count-visible",
|
||||||
|
);
|
||||||
|
expect(countVisible[1]).toEqual(["b", false]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should set count-visible to true when count is not 0", async () => {
|
||||||
|
process.env.container = "0";
|
||||||
|
mockDbusMessage.mockClear();
|
||||||
|
|
||||||
|
await setBadgeCount(5);
|
||||||
|
|
||||||
|
const msg = mockDbusMessage.mock.calls[0][0];
|
||||||
|
const props = msg.body[1];
|
||||||
|
const countVisible = props.find(
|
||||||
|
(p: [string, unknown[]]) => p[0] === "count-visible",
|
||||||
|
);
|
||||||
|
expect(countVisible[1]).toEqual(["b", true]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
Reference in New Issue