mirror of
https://github.com/shishantbiswas/bknd.git
synced 2026-03-16 04:27:21 +00:00
Added support for validating and managing return values in events. Implemented `validate` and `clone` methods in the event base class for event mutation and return handling. Additionally, enhanced error handling, introduced "once" listeners, and improved async execution management in the `EventManager`.
156 lines
4.4 KiB
TypeScript
156 lines
4.4 KiB
TypeScript
import { afterAll, beforeAll, describe, expect, mock, test } from "bun:test";
|
|
import {
|
|
Event,
|
|
EventManager,
|
|
InvalidEventReturn,
|
|
type ListenerHandler,
|
|
NoParamEvent
|
|
} from "../../src/core/events";
|
|
import { disableConsoleLog, enableConsoleLog } from "../helper";
|
|
|
|
beforeAll(disableConsoleLog);
|
|
afterAll(enableConsoleLog);
|
|
|
|
class SpecialEvent extends Event<{ foo: string }> {
|
|
static override slug = "special-event";
|
|
|
|
isBar() {
|
|
return this.params.foo === "bar";
|
|
}
|
|
}
|
|
|
|
class InformationalEvent extends NoParamEvent {
|
|
static override slug = "informational-event";
|
|
}
|
|
|
|
class ReturnEvent extends Event<{ foo: string }, string> {
|
|
static override slug = "return-event";
|
|
|
|
override validate(value: string) {
|
|
if (typeof value !== "string") {
|
|
throw new InvalidEventReturn("string", typeof value);
|
|
}
|
|
|
|
return this.clone({
|
|
foo: [this.params.foo, value].join("-")
|
|
});
|
|
}
|
|
}
|
|
|
|
describe("EventManager", async () => {
|
|
test("executes", async () => {
|
|
const call = mock(() => null);
|
|
const delayed = mock(() => null);
|
|
|
|
const emgr = new EventManager();
|
|
emgr.registerEvents([SpecialEvent, InformationalEvent]);
|
|
|
|
expect(emgr.eventExists("special-event")).toBe(true);
|
|
expect(emgr.eventExists("informational-event")).toBe(true);
|
|
expect(emgr.eventExists("unknown-event")).toBe(false);
|
|
|
|
emgr.onEvent(
|
|
SpecialEvent,
|
|
async (event, name) => {
|
|
expect(name).toBe("special-event");
|
|
expect(event.isBar()).toBe(true);
|
|
call();
|
|
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
delayed();
|
|
},
|
|
"sync"
|
|
);
|
|
|
|
emgr.onEvent(InformationalEvent, async (event, name) => {
|
|
call();
|
|
expect(name).toBe("informational-event");
|
|
});
|
|
|
|
await emgr.emit(new SpecialEvent({ foo: "bar" }));
|
|
await emgr.emit(new InformationalEvent());
|
|
|
|
// expect construct signatures to not cause ts errors
|
|
new SpecialEvent({ foo: "bar" });
|
|
new InformationalEvent();
|
|
|
|
expect(call).toHaveBeenCalledTimes(2);
|
|
expect(delayed).toHaveBeenCalled();
|
|
});
|
|
|
|
test("custom async executor", async () => {
|
|
const call = mock(() => null);
|
|
const asyncExecutor = (p: Promise<any>[]) => {
|
|
call();
|
|
return Promise.all(p);
|
|
};
|
|
const emgr = new EventManager(
|
|
{ InformationalEvent },
|
|
{
|
|
asyncExecutor
|
|
}
|
|
);
|
|
|
|
emgr.onEvent(InformationalEvent, async () => {});
|
|
await emgr.emit(new InformationalEvent());
|
|
expect(call).toHaveBeenCalled();
|
|
});
|
|
|
|
test("piping", async () => {
|
|
const onInvalidReturn = mock(() => null);
|
|
const asyncEventCallback = mock(() => null);
|
|
const emgr = new EventManager(
|
|
{ ReturnEvent, InformationalEvent },
|
|
{
|
|
onInvalidReturn
|
|
}
|
|
);
|
|
|
|
// @ts-expect-error InformationalEvent has no return value
|
|
emgr.onEvent(InformationalEvent, async () => {
|
|
asyncEventCallback();
|
|
return 1;
|
|
});
|
|
|
|
emgr.onEvent(ReturnEvent, async () => "1", "sync");
|
|
emgr.onEvent(ReturnEvent, async () => "0", "sync");
|
|
|
|
// @ts-expect-error must be string
|
|
emgr.onEvent(ReturnEvent, async () => 0, "sync");
|
|
|
|
// return is not required
|
|
emgr.onEvent(ReturnEvent, async () => {}, "sync");
|
|
|
|
// was "async", will not return
|
|
const e1 = await emgr.emit(new InformationalEvent());
|
|
expect(e1.returned).toBe(false);
|
|
|
|
const e2 = await emgr.emit(new ReturnEvent({ foo: "bar" }));
|
|
expect(e2.returned).toBe(true);
|
|
expect(e2.params.foo).toBe("bar-1-0");
|
|
expect(onInvalidReturn).toHaveBeenCalled();
|
|
expect(asyncEventCallback).toHaveBeenCalled();
|
|
});
|
|
|
|
test("once", async () => {
|
|
const call = mock(() => null);
|
|
const emgr = new EventManager({ InformationalEvent });
|
|
|
|
emgr.onEventOnce(
|
|
InformationalEvent,
|
|
async (event, slug) => {
|
|
expect(event).toBeInstanceOf(InformationalEvent);
|
|
expect(slug).toBe("informational-event");
|
|
call();
|
|
},
|
|
"sync"
|
|
);
|
|
|
|
expect(emgr.getListeners().length).toBe(1);
|
|
await emgr.emit(new InformationalEvent());
|
|
expect(emgr.getListeners().length).toBe(0);
|
|
await emgr.emit(new InformationalEvent());
|
|
expect(emgr.getListeners().length).toBe(0);
|
|
expect(call).toHaveBeenCalledTimes(1);
|
|
});
|
|
});
|