From 25dacea269c7d6c57e280600a637fbf1cbaf2f1a Mon Sep 17 00:00:00 2001 From: 18alantom <2.alan.tom@gmail.com> Date: Wed, 27 Apr 2022 23:59:57 +0530 Subject: [PATCH] test: observable - add hasListener --- fyo/tests/testObservable.spec.ts | 131 +++++++++++++++++++++++++++++++ fyo/utils/observable.ts | 22 +++++- 2 files changed, 152 insertions(+), 1 deletion(-) create mode 100644 fyo/tests/testObservable.spec.ts diff --git a/fyo/tests/testObservable.spec.ts b/fyo/tests/testObservable.spec.ts new file mode 100644 index 00000000..23cfa386 --- /dev/null +++ b/fyo/tests/testObservable.spec.ts @@ -0,0 +1,131 @@ +import * as assert from 'assert'; +import Observable from 'fyo/utils/observable'; +import 'mocha'; + +enum ObsEvent { + A = 'event-a', + B = 'event-b', +} + +describe('Observable', function () { + const obs = new Observable(); + let counter = 0; + const params = { aOne: 18, aTwo: 21, b: 42 }; + + const listenerAOnce = (value: number) => { + assert.strictEqual(params.aOne, value, 'listenerAOnce'); + }; + + const listenerAEvery = (value: number) => { + if (counter === 0) { + assert.strictEqual(params.aOne, value, 'listenerAEvery 0'); + } else if (counter === 1) { + assert.strictEqual(params.aTwo, value, 'listenerAEvery 1'); + } else { + throw new Error("this shouldn't run"); + } + counter += 1; + }; + + const listenerBOnce = (value: number) => { + assert.strictEqual(params.b, value, 'listenerBOnce'); + }; + + specify('set A One', function () { + assert.strictEqual(obs.hasListener(ObsEvent.A), false, 'pre'); + + obs.once(ObsEvent.A, listenerAOnce); + assert.strictEqual(obs.hasListener(ObsEvent.A), true, 'non specific'); + assert.strictEqual( + obs.hasListener(ObsEvent.A, listenerAOnce), + true, + 'specific once' + ); + assert.strictEqual( + obs.hasListener(ObsEvent.A, listenerAEvery), + false, + 'specific every' + ); + }); + + specify('set A Two', function () { + obs.on(ObsEvent.A, listenerAEvery); + assert.strictEqual(obs.hasListener(ObsEvent.A), true, 'non specific'); + assert.strictEqual( + obs.hasListener(ObsEvent.A, listenerAOnce), + true, + 'specific once' + ); + assert.strictEqual( + obs.hasListener(ObsEvent.A, listenerAEvery), + true, + 'specific every' + ); + }); + + specify('set B', function () { + assert.strictEqual(obs.hasListener(ObsEvent.B), false, 'pre'); + + obs.once(ObsEvent.B, listenerBOnce); + assert.strictEqual( + obs.hasListener(ObsEvent.A, listenerBOnce), + false, + 'specific false' + ); + assert.strictEqual( + obs.hasListener(ObsEvent.B, listenerBOnce), + true, + 'specific true' + ); + }); + + specify('trigger A 0', async function () { + await obs.trigger(ObsEvent.A, params.aOne); + assert.strictEqual(obs.hasListener(ObsEvent.A), true, 'non specific'); + assert.strictEqual( + obs.hasListener(ObsEvent.A, listenerAOnce), + false, + 'specific' + ); + }); + + specify('trigger A 1', async function () { + assert.strictEqual( + obs.hasListener(ObsEvent.A, listenerAEvery), + true, + 'specific pre' + ); + await obs.trigger(ObsEvent.A, params.aTwo); + assert.strictEqual( + obs.hasListener(ObsEvent.A, listenerAEvery), + true, + 'specific post' + ); + }); + + specify('trigger B', async function () { + assert.strictEqual( + obs.hasListener(ObsEvent.B, listenerBOnce), + true, + 'specific pre' + ); + await obs.trigger(ObsEvent.B, params.b); + assert.strictEqual( + obs.hasListener(ObsEvent.B, listenerBOnce), + false, + 'specific post' + ); + }); + + specify('remove A', async function () { + obs.off(ObsEvent.A, listenerAEvery); + assert.strictEqual( + obs.hasListener(ObsEvent.A, listenerAEvery), + false, + 'specific pre' + ); + + assert.strictEqual(counter, 2, 'incorrect counter'); + await obs.trigger(ObsEvent.A, 777); + }); +}); diff --git a/fyo/utils/observable.ts b/fyo/utils/observable.ts index 61c8f998..2dba8abc 100644 --- a/fyo/utils/observable.ts +++ b/fyo/utils/observable.ts @@ -43,6 +43,26 @@ export default class Observable { }); } + /** + * Checks if any `listener` or the given `listener` has been registered + * for the passed `event`. + * + * @param event : name of the event for which the listener is checked + * @param listener : specific listener that is checked for + */ + hasListener(event: string, listener?: Function) { + const listeners = this[EventType.Listeners].get(event) ?? []; + const onceListeners = this[EventType.OnceListeners].get(event) ?? []; + + if (listener === undefined) { + return [...listeners, ...onceListeners].length > 0; + } + + let has = listeners.includes(listener); + has ||= onceListeners.includes(listener); + return has; + } + /** * Sets a `listener` that executes every time `event` is triggered * @@ -91,7 +111,7 @@ export default class Observable { * @param throttle : wait time before triggering the event. */ - async trigger(event: string, params: unknown, throttle: number = 0) { + async trigger(event: string, params?: unknown, throttle: number = 0) { let isHot = false; if (throttle > 0) { isHot = this._throttled(event, params, throttle);