From bb38d9c6971bbe0d3f6dede55533fc61f440a594 Mon Sep 17 00:00:00 2001 From: George Cht <2hardforutoo@hotmail.gr> Date: Wed, 23 Oct 2024 10:46:35 +0300 Subject: [PATCH 1/3] test(dispatching): added dispatching test --- tests/dispatching.test.ts | 43 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 tests/dispatching.test.ts diff --git a/tests/dispatching.test.ts b/tests/dispatching.test.ts new file mode 100644 index 0000000..5bbcfce --- /dev/null +++ b/tests/dispatching.test.ts @@ -0,0 +1,43 @@ +import { z } from 'zod' +import { EventController } from '../src' + +describe('EventController Event Dispatching', () => { + const schema = z.object({ name: z.string() }) + let controller: EventController + + beforeEach(() => { + controller = new EventController(schema, 'testEvent') + }) + + test('should dispatch an event with valid payload', async () => { + const listener = jest.fn() + controller.subscribe(listener) + await controller.dispatch({ name: 'Test' }) + expect(listener).toHaveBeenCalledWith(expect.any(CustomEvent)) + }) + + test('should not dispatch an event with invalid payload', async () => { + const listener = jest.fn() + controller.subscribe(listener) + await expect(controller.dispatch({ name: 123 } as any)).rejects.toThrow() + expect(listener).not.toHaveBeenCalled() + }) + + test('should dispatch event with bubbling', async () => { + const listener = jest.fn() + controller.subscribe(listener) + await controller.dispatch({ name: 'Test' }, { bubbles: true }) + expect(listener).toHaveBeenCalledWith( + expect.objectContaining({ bubbles: true }), + ) + }) + + test('should dispatch event with cancellation', async () => { + const listener = jest.fn() + controller.subscribe(listener) + await controller.dispatch({ name: 'Test' }, { cancelable: true }) + expect(listener).toHaveBeenCalledWith( + expect.objectContaining({ cancelable: true }), + ) + }) +}) From 3724e2c9f9567c4630829ab9ccec4ac5f64f9a81 Mon Sep 17 00:00:00 2001 From: George Cht <2hardforutoo@hotmail.gr> Date: Wed, 23 Oct 2024 10:48:59 +0300 Subject: [PATCH 2/3] test(lifecycle): added lifecycle tests --- tests/lifecycle.test.ts | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 tests/lifecycle.test.ts diff --git a/tests/lifecycle.test.ts b/tests/lifecycle.test.ts new file mode 100644 index 0000000..565eda1 --- /dev/null +++ b/tests/lifecycle.test.ts @@ -0,0 +1,41 @@ +import { z } from 'zod' +import { EventController } from '../src' + +describe('EventController Lifecycle Callbacks', () => { + const schema = z.object({ name: z.string() }) + + test('should execute onError callback when dispatching invalid payload', async () => { + const onError = jest.fn() + const controller = new EventController(schema, 'testEvent', { onError }) + await controller.dispatch({ name: 123 } as any) + expect(onError).toHaveBeenCalled() + }) + + test('should execute onDispatch callback before dispatching event', async () => { + const onDispatch = jest.fn() + const controller = new EventController(schema, 'testEvent', { onDispatch }) + await controller.dispatch({ name: 'Test' }) + expect(onDispatch).toHaveBeenCalledWith( + expect.objectContaining({ + payload: { name: 'Test' }, + }), + ) + }) + + test('should execute onSubscribe callback when subscribing', () => { + const onSubscribe = jest.fn() + const controller = new EventController(schema, 'testEvent', { onSubscribe }) + controller.subscribe(() => {}) + expect(onSubscribe).toHaveBeenCalled() + }) + + test('should execute onUnsubscribe callback when unsubscribing', () => { + const onUnsubscribe = jest.fn() + const controller = new EventController(schema, 'testEvent', { + onUnsubscribe, + }) + controller.subscribe(() => {}) + controller.unsubscribe() + expect(onUnsubscribe).toHaveBeenCalled() + }) +}) From a58d9de1955ae1fca51be2fc988c4a5be562b5b2 Mon Sep 17 00:00:00 2001 From: George Cht <2hardforutoo@hotmail.gr> Date: Wed, 23 Oct 2024 10:59:52 +0300 Subject: [PATCH 3/3] test(tests): added even more tests; reached 100% coverage --- tests/edge.test.ts | 88 ++++++++++++++++++++++++++++++++++++++++ tests/middleware.test.ts | 2 +- tests/refine.test.ts | 32 +++++++++++++++ 3 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 tests/edge.test.ts create mode 100644 tests/refine.test.ts diff --git a/tests/edge.test.ts b/tests/edge.test.ts new file mode 100644 index 0000000..c07906c --- /dev/null +++ b/tests/edge.test.ts @@ -0,0 +1,88 @@ +import { z } from 'zod' +import { EventController } from '../src' + +describe('EventController Advanced Scenarios and Edge Cases', () => { + test('should handle high-frequency event dispatches', async () => { + const schema = z.object({ value: z.number() }) + const controller = new EventController(schema, 'testEvent') + const listener = jest.fn() + controller.subscribe(listener) + + const dispatches = Array.from({ length: 1000 }, (_, i) => ({ value: i })) + await Promise.all(dispatches.map((payload) => controller.dispatch(payload))) + + expect(listener).toHaveBeenCalledTimes(1000) + }) + + test('should handle concurrent dispatches from multiple controllers', async () => { + const schema = z.object({ value: z.number() }) + const controller1 = new EventController(schema, 'testEvent1') + const controller2 = new EventController(schema, 'testEvent2') + + const listener1 = jest.fn() + const listener2 = jest.fn() + + controller1.subscribe(listener1) + controller2.subscribe(listener2) + + await Promise.all([ + controller1.dispatch({ value: 1 }), + controller2.dispatch({ value: 2 }), + controller1.dispatch({ value: 3 }), + controller2.dispatch({ value: 4 }), + ]) + + expect(listener1).toHaveBeenCalledTimes(2) + expect(listener2).toHaveBeenCalledTimes(2) + }) + + test('should handle recursive schema', async () => { + type TreeNode = { value: number; children?: TreeNode[] } + const treeSchema: z.ZodType = z.lazy(() => + z.object({ + value: z.number(), + children: z.array(treeSchema).optional(), + }), + ) + + const controller = new EventController(treeSchema, 'treeEvent') + const listener = jest.fn() + controller.subscribe(listener) + + await controller.dispatch({ + value: 1, + children: [{ value: 2 }, { value: 3, children: [{ value: 4 }] }], + }) + + expect(listener).toHaveBeenCalled() + }) + + test('should handle error in middleware without breaking the system', async () => { + const schema = z.object({ value: z.number() }) + const controller = new EventController(schema, 'testEvent') + + controller.use(() => { + throw new Error('Middleware error') + }) + + const listener = jest.fn() + controller.subscribe(listener) + + await expect(controller.dispatch({ value: 1 })).rejects.toThrow( + 'Middleware error', + ) + expect(listener).not.toHaveBeenCalled() + }) + + test('should prevent multiple calls to next() in middleware', async () => { + const schema = z.object({ value: z.number() }) + const controller = new EventController(schema, 'testEvent') + + controller.use(async (_, next) => { + await next() + await expect(next()).rejects.toThrow('next() called multiple times') + }) + + await controller.dispatch({ value: 1 }) + }) +}) diff --git a/tests/middleware.test.ts b/tests/middleware.test.ts index e3a753b..50690a2 100644 --- a/tests/middleware.test.ts +++ b/tests/middleware.test.ts @@ -3,7 +3,7 @@ import { createPipeline } from '../src/middleware' import type { Middleware, Pipeline } from '../src/middleware' -describe('createPipeline', () => { +describe('Middleware Pipeline', () => { const TestSchema = z.object({ id: z.number(), name: z.string(), diff --git a/tests/refine.test.ts b/tests/refine.test.ts new file mode 100644 index 0000000..fad28ab --- /dev/null +++ b/tests/refine.test.ts @@ -0,0 +1,32 @@ +import { z } from 'zod' +import { EventController } from '../src' + +describe('EventController Refinement', () => { + const schema = z.object({ value: z.number() }) + let controller: EventController + + beforeEach(() => { + controller = new EventController(schema, 'testEvent') + }) + + afterEach(() => { + controller.unsubscribe() + controller.update({}) + }) + + test('should apply refinement condition', async () => { + controller.refine((payload) => payload.value > 0) + const listener = jest.fn() + controller.subscribe(listener) + await controller.dispatch({ value: 10 }) + await controller.dispatch({ value: -5 }) + expect(listener).toHaveBeenCalledTimes(1) + }) + + test('should execute refinement callback when condition is not met', async () => { + const callback = jest.fn() + controller.refine((payload) => payload.value > 0, callback) + await controller.dispatch({ value: -5 }) + expect(callback).toHaveBeenCalled() + }) +})