Files
2026-07-13 13:39:12 +08:00

172 lines
7.0 KiB
TypeScript

/**
* NotificationStore Tests
*
* Tests for Zustand-based global notification system.
* Covers onClick callback, duration defaults, and state management.
*
* Note: Zustand state updates require re-calling getState() to get fresh state.
*/
import { describe, it, beforeEach, afterEach, mock } from "node:test";
import assert from "node:assert/strict";
import { useNotificationStore } from "../../src/store/notificationStore.ts";
describe("NotificationStore", () => {
beforeEach(() => {
// Clear all notifications before each test
useNotificationStore.getState().clearAll();
});
afterEach(() => {
useNotificationStore.getState().clearAll();
});
describe("addNotification", () => {
it("should add notification with default duration 5000ms", () => {
const id = useNotificationStore.getState().addNotification({ type: "info", message: "test" });
const notification = useNotificationStore.getState().notifications.find((n) => n.id === id);
assert.ok(notification, "notification should exist");
assert.equal(notification?.duration, 5000, "default duration should be 5000ms");
});
it("should add notification with onClick callback", () => {
const onClick = mock.fn();
const id = useNotificationStore.getState().addNotification({
type: "warning",
message: "clickable notification",
onClick,
});
const notification = useNotificationStore.getState().notifications.find((n) => n.id === id);
assert.ok(notification, "notification should exist");
assert.equal(notification?.onClick, onClick, "onClick should be stored");
});
it("should include onClick in notification object", () => {
const onClick = () => {};
const id = useNotificationStore.getState().addNotification({
type: "success",
message: "test",
onClick,
});
const notification = useNotificationStore.getState().notifications.find((n) => n.id === id);
assert.ok(notification?.onClick, "onClick should be present on notification");
});
it("should allow custom duration override", () => {
const id = useNotificationStore.getState().addNotification({
type: "error",
message: "test",
duration: 10000,
});
const notification = useNotificationStore.getState().notifications.find((n) => n.id === id);
assert.equal(notification?.duration, 10000, "custom duration should be applied");
});
});
describe("convenience methods", () => {
it("warning should use 10000ms duration", () => {
const id = useNotificationStore.getState().warning("warning message", "Warning Title");
const notification = useNotificationStore.getState().notifications.find((n) => n.id === id);
assert.ok(notification, "notification should exist");
assert.equal(notification?.type, "warning", "type should be warning");
assert.equal(notification?.duration, 10000, "warning duration should be 10000ms");
assert.equal(notification?.title, "Warning Title", "title should be set");
});
it("error should use 8000ms duration", () => {
const id = useNotificationStore.getState().error("error message");
const notification = useNotificationStore.getState().notifications.find((n) => n.id === id);
assert.equal(notification?.duration, 8000, "error duration should be 8000ms");
});
it("success should use default 5000ms duration", () => {
const id = useNotificationStore.getState().success("success message");
const notification = useNotificationStore.getState().notifications.find((n) => n.id === id);
assert.equal(notification?.duration, 5000, "success duration should be 5000ms");
});
it("info should use default 5000ms duration", () => {
const id = useNotificationStore.getState().info("info message");
const notification = useNotificationStore.getState().notifications.find((n) => n.id === id);
assert.equal(notification?.duration, 5000, "info duration should be 5000ms");
});
});
describe("removeNotification", () => {
it("should remove notification by id", () => {
const id = useNotificationStore
.getState()
.addNotification({ type: "info", message: "to remove" });
assert.equal(
useNotificationStore.getState().notifications.length,
1,
"should have 1 notification"
);
useNotificationStore.getState().removeNotification(id);
assert.equal(
useNotificationStore.getState().notifications.length,
0,
"should have 0 notifications after removal"
);
});
it("should not error when removing non-existent id", () => {
useNotificationStore.getState().removeNotification(999);
assert.equal(
useNotificationStore.getState().notifications.length,
0,
"should still have 0 notifications"
);
});
});
describe("clearAll", () => {
it("should clear all notifications", () => {
useNotificationStore.getState().addNotification({ type: "info", message: "one" });
useNotificationStore.getState().addNotification({ type: "info", message: "two" });
useNotificationStore.getState().addNotification({ type: "info", message: "three" });
assert.equal(
useNotificationStore.getState().notifications.length,
3,
"should have 3 notifications"
);
useNotificationStore.getState().clearAll();
assert.equal(
useNotificationStore.getState().notifications.length,
0,
"should have 0 after clearAll"
);
});
});
describe("notification structure", () => {
it("should include all required fields", () => {
const onClick = () => {};
const id = useNotificationStore.getState().addNotification({
type: "warning",
message: "structured test",
title: "Title",
duration: 15000,
dismissible: false,
onClick,
});
const notification = useNotificationStore.getState().notifications.find((n) => n.id === id);
assert.ok(notification, "notification should exist");
assert.equal(typeof notification?.id, "number", "id should be number");
assert.equal(notification?.type, "warning", "type should match");
assert.equal(notification?.message, "structured test", "message should match");
assert.equal(notification?.title, "Title", "title should match");
assert.equal(notification?.duration, 15000, "duration should match");
assert.equal(notification?.dismissible, false, "dismissible should match");
assert.equal(typeof notification?.createdAt, "number", "createdAt should be number");
assert.equal(notification?.onClick, onClick, "onClick should match");
});
it("should have dismissible true by default", () => {
const id = useNotificationStore.getState().addNotification({ type: "info", message: "test" });
const notification = useNotificationStore.getState().notifications.find((n) => n.id === id);
assert.equal(notification?.dismissible, true, "dismissible should default to true");
});
});
});