generated from nhcarrigan/template
7e45c685d3
Adds 55 tests for clipboard store pure functions (detectLanguage for 12 languages + formatTimestamp with fake timers) and 13 tests for the notification rules state machine (reconnect transition logic and no-op handlers). Total test count now 698 across 30 files.
151 lines
5.1 KiB
TypeScript
151 lines
5.1 KiB
TypeScript
/**
|
|
* Notification Rules Tests
|
|
*
|
|
* Tests the connection status change handler, which fires a connection
|
|
* notification sound exactly once per reconnect cycle.
|
|
*
|
|
* What this module does:
|
|
* - Tracks the previous connection status in module-level state
|
|
* - Fires a notification only when transitioning from a non-connected
|
|
* state (disconnected/connecting) to "connected"
|
|
* - Ignores the initial connection (null → connected) to avoid noisy
|
|
* notifications on app start
|
|
* - Provides no-op handlers for tool execution and user messages
|
|
* (reserved for future notification rules)
|
|
* - cleanupNotificationRules() resets tracking state on teardown
|
|
*/
|
|
|
|
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
|
|
const { mockNotifyConnection } = vi.hoisted(() => ({
|
|
mockNotifyConnection: vi.fn(),
|
|
}));
|
|
|
|
vi.mock("./notificationManager", () => ({
|
|
notificationManager: {
|
|
notifyConnection: mockNotifyConnection,
|
|
},
|
|
}));
|
|
|
|
import {
|
|
handleConnectionStatusChange,
|
|
handleToolExecution,
|
|
handleNewUserMessage,
|
|
initializeNotificationRules,
|
|
cleanupNotificationRules,
|
|
} from "./rules";
|
|
|
|
// ---
|
|
|
|
describe("handleConnectionStatusChange", () => {
|
|
beforeEach(() => {
|
|
mockNotifyConnection.mockReset();
|
|
cleanupNotificationRules(); // Reset module-level previousConnectionStatus to null
|
|
});
|
|
|
|
describe("initial connection (null → status)", () => {
|
|
it("does not notify on first connection (null → connected)", () => {
|
|
// previousConnectionStatus is null (falsy), so condition is not met
|
|
handleConnectionStatusChange("connected");
|
|
expect(mockNotifyConnection).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("does not notify when disconnecting from initial state (null → disconnected)", () => {
|
|
handleConnectionStatusChange("disconnected");
|
|
expect(mockNotifyConnection).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("does not notify when entering connecting from initial state (null → connecting)", () => {
|
|
handleConnectionStatusChange("connecting");
|
|
expect(mockNotifyConnection).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe("reconnection (disconnected → connected)", () => {
|
|
it("notifies when reconnecting after a disconnection", () => {
|
|
handleConnectionStatusChange("disconnected");
|
|
handleConnectionStatusChange("connected");
|
|
expect(mockNotifyConnection).toHaveBeenCalledWith();
|
|
});
|
|
|
|
it("notifies exactly once per reconnect", () => {
|
|
handleConnectionStatusChange("disconnected");
|
|
handleConnectionStatusChange("connected");
|
|
expect(mockNotifyConnection).toHaveBeenCalledTimes(1);
|
|
});
|
|
});
|
|
|
|
describe("reconnection (connecting → connected)", () => {
|
|
it("notifies when transitioning from connecting to connected", () => {
|
|
handleConnectionStatusChange("connecting");
|
|
handleConnectionStatusChange("connected");
|
|
expect(mockNotifyConnection).toHaveBeenCalledWith();
|
|
});
|
|
});
|
|
|
|
describe("already connected (connected → connected)", () => {
|
|
it("does not notify when already connected", () => {
|
|
handleConnectionStatusChange("disconnected");
|
|
handleConnectionStatusChange("connected"); // First connection — notifies
|
|
mockNotifyConnection.mockReset();
|
|
|
|
handleConnectionStatusChange("connected"); // Second — same status, no notify
|
|
expect(mockNotifyConnection).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe("disconnecting (connected → disconnected)", () => {
|
|
it("does not notify when disconnecting", () => {
|
|
handleConnectionStatusChange("disconnected");
|
|
handleConnectionStatusChange("connected");
|
|
mockNotifyConnection.mockReset();
|
|
|
|
handleConnectionStatusChange("disconnected");
|
|
expect(mockNotifyConnection).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe("multiple reconnect cycles", () => {
|
|
it("notifies once per reconnect cycle", () => {
|
|
// First cycle
|
|
handleConnectionStatusChange("disconnected");
|
|
handleConnectionStatusChange("connected");
|
|
expect(mockNotifyConnection).toHaveBeenCalledTimes(1);
|
|
|
|
mockNotifyConnection.mockReset();
|
|
|
|
// Second cycle
|
|
handleConnectionStatusChange("disconnected");
|
|
handleConnectionStatusChange("connected");
|
|
expect(mockNotifyConnection).toHaveBeenCalledTimes(1);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("cleanupNotificationRules", () => {
|
|
it("resets state so the next connection is treated as the first", () => {
|
|
// Establish a known previous status
|
|
handleConnectionStatusChange("disconnected");
|
|
// Now cleanup
|
|
cleanupNotificationRules();
|
|
// After cleanup, previousConnectionStatus is null again
|
|
// So the next "connected" should NOT notify (treated as initial connection)
|
|
handleConnectionStatusChange("connected");
|
|
expect(mockNotifyConnection).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe("no-op handlers", () => {
|
|
it("handleToolExecution does not throw", () => {
|
|
expect(() => handleToolExecution("Bash")).not.toThrow();
|
|
});
|
|
|
|
it("handleNewUserMessage does not throw", () => {
|
|
expect(() => handleNewUserMessage()).not.toThrow();
|
|
});
|
|
|
|
it("initializeNotificationRules does not throw", () => {
|
|
expect(() => initializeNotificationRules()).not.toThrow();
|
|
});
|
|
});
|