diff --git a/libs/effects/src/lib/effects-factory-function.spec.ts b/libs/effects/src/lib/effects-factory-function.spec.ts index 065789f..95b901e 100644 --- a/libs/effects/src/lib/effects-factory-function.spec.ts +++ b/libs/effects/src/lib/effects-factory-function.spec.ts @@ -4,74 +4,72 @@ import { Component, inject, OnDestroy } from '@angular/core'; import { of, Subject } from 'rxjs'; describe(`${rxEffect.name} factory function`, () => { - it('should execute all effects', async () => { - const { service, component } = await setup(); + it('should execute all effects', async () => { + const { service, component } = await setup(); - component.trigger$$.next(10); - component.trigger$$.next(20); + component.trigger$$.next(10); + component.trigger$$.next(20); - expect(service.sourceEffect).toHaveBeenCalledWith(10); - expect(service.triggerEffect).toBeCalledTimes(2); - expect(service.triggerEffect).toHaveBeenNthCalledWith(1, 10); - expect(service.triggerEffect).toHaveBeenNthCalledWith(2, 20); - }); - it('should unsubscribe from sources onDestroy', async () => { - const { service, component, fixture } = await setup(); + expect(service.sourceEffect).toHaveBeenCalledWith(10); + expect(service.triggerEffect).toBeCalledTimes(2); + expect(service.triggerEffect).toHaveBeenNthCalledWith(1, 10); + expect(service.triggerEffect).toHaveBeenNthCalledWith(2, 20); + }); + it('should unsubscribe from sources onDestroy', async () => { + const { service, component, fixture } = await setup(); - fixture.destroy(); - component.trigger$$.next(10); + fixture.destroy(); + component.trigger$$.next(10); - expect(service.triggerEffect).not.toHaveBeenCalled(); - }); - it('should execute teardown function onDestroy', async () => { - const { component, fixture } = await setup(); + expect(service.triggerEffect).not.toHaveBeenCalled(); + }); + it('should execute teardown function onDestroy', async () => { + const { component, fixture } = await setup(); + jest.spyOn(component.clearInterval$$, 'next'); + fixture.destroy(); + + expect(component.clearInterval$$.next).toHaveBeenCalled(); + }); + + describe('cleanUp', () => { + it('should execute runOnCleanUp function when terminate is called', async () => { + const { component } = await setup(); jest.spyOn(component.clearInterval$$, 'next'); - fixture.destroy(); + component.effects.cleanUp(); expect(component.clearInterval$$.next).toHaveBeenCalled(); }); + it('should unsubscribe from all sources when cleanUp is called', async () => { + const { service, component } = await setup(); - describe('cleanUp', () => { - it('should execute runOnCleanUp function when terminate is called', async () => { - const { component } = await setup(); - jest.spyOn(component.clearInterval$$, 'next'); - component.effects.cleanUp(); - - expect(component.clearInterval$$.next).toHaveBeenCalled(); - }); - it('should unsubscribe from all sources when cleanUp is called', async () => { - const { service, component } = await setup(); - - component.effects.cleanUp(); - component.trigger$$.next(10); + component.effects.cleanUp(); + component.trigger$$.next(10); - expect(service.triggerEffect).not.toHaveBeenCalled(); - }); + expect(service.triggerEffect).not.toHaveBeenCalled(); }); + }); - describe('runOnCleanUp', () => { - it('should execute effect onDestroy', async () => { - const { service, fixture } = await setup(); - fixture.destroy(); + describe('runOnCleanUp', () => { + it('should execute effect onDestroy', async () => { + const { service, fixture } = await setup(); + fixture.destroy(); - expect(service.teardownEffect).toHaveBeenCalled(); - }); + expect(service.teardownEffect).toHaveBeenCalled(); }); - describe('cleanUp', () => { - it('should unsubscribe trigger$$ when unsubscribeTrigger$$ emits', async () => { - const { component, service } = await setup(); + }); + describe('cleanUp', () => { + it('should unsubscribe trigger$$ when unsubscribeTrigger$$ emits', async () => { + const { component, service } = await setup(); - component.trigger$$.next(10); - component.unsubscribeTrigger$$.next(void 0); - component.trigger$$.next(20); + component.trigger$$.next(10); + component.unsubscribeTrigger$$.next(void 0); + component.trigger$$.next(20); - expect(service.triggerEffect).toBeCalledTimes(1); - expect(service.triggerEffect).toHaveBeenCalledWith(10); - }); + expect(service.triggerEffect).toBeCalledTimes(1); + expect(service.triggerEffect).toHaveBeenCalledWith(10); }); - -}) - + }); +}); async function setup() { const serviceMock = { @@ -120,15 +118,17 @@ class TestComponent implements OnDestroy { source$ = of(10); clearInterval$$ = new Subject(); - effects = rxEffect(({ run, runOnInstanceCleanUp }) => { + effects = rxEffect(({ run, runOnInstanceDestroy }) => { run(this.source$, (v) => this.service.sourceEffect(v)); - const triggerEffect = run(this.trigger$$, (v) => this.service.triggerEffect(v)); + const triggerEffect = run(this.trigger$$, (v) => + this.service.triggerEffect(v) + ); run(this.unsubscribeTrigger$$, () => { triggerEffect.cleanUp(); }); - runOnInstanceCleanUp(() => { + runOnInstanceDestroy(() => { this.service.teardownEffect(); this.clearInterval$$.next(void 0); }); diff --git a/libs/effects/src/lib/effects-standalone.spec.ts b/libs/effects/src/lib/effects-standalone.spec.ts index 1d07c91..b7f8966 100644 --- a/libs/effects/src/lib/effects-standalone.spec.ts +++ b/libs/effects/src/lib/effects-standalone.spec.ts @@ -47,7 +47,7 @@ describe(`${rxEffect.name} standalone`, () => { it('should execute functions registered in runOnInstanceCleanUp ', async () => { const { effects, fixture } = await setupSingleEffectsInstance(); const spy = jest.fn(); - effects.runOnInstanceCleanUp(() => spy()); + effects.runOnInstanceDestroy(() => spy()); fixture.destroy(); expect(spy).toHaveBeenCalledTimes(1); diff --git a/libs/effects/src/lib/effects.ts b/libs/effects/src/lib/effects.ts index 1307aab..5210510 100644 --- a/libs/effects/src/lib/effects.ts +++ b/libs/effects/src/lib/effects.ts @@ -1,5 +1,21 @@ -import { DestroyRef, ErrorHandler, inject, Injectable, Injector, Optional, runInInjectionContext } from '@angular/core'; -import { catchError, EMPTY, Observable, pipe, ReplaySubject, Subscription, tap } from 'rxjs'; +import { + DestroyRef, + ErrorHandler, + inject, + Injectable, + Injector, + Optional, + runInInjectionContext, +} from '@angular/core'; +import { + catchError, + EMPTY, + Observable, + pipe, + ReplaySubject, + Subscription, + tap, +} from 'rxjs'; import { assertInjector } from './assert-injector'; export type EffectCleanUpRef = { @@ -11,7 +27,11 @@ type RunOptions = { }; export function isRunOptionsGuard(obj: any): obj is RunOptions { - return typeof obj === 'object' && obj?.onCleanUp !== undefined && typeof obj.onCleanUp === 'function'; + return ( + typeof obj === 'object' && + obj?.onCleanUp !== undefined && + typeof obj.onCleanUp === 'function' + ); } /** @@ -82,7 +102,11 @@ export class Effects { * @param sideEffectFn * @param options */ - run(o$: Observable, sideEffectFn: (arg: T) => void, options?: RunOptions): EffectCleanUpRef; + run( + o$: Observable, + sideEffectFn: (arg: T) => void, + options?: RunOptions + ): EffectCleanUpRef; /** * @internal */ @@ -98,7 +122,9 @@ export class Effects { this.idSubMap.set(effectId, obsOrSub$); let runOnInstanceDestroySub: undefined | EffectCleanUpRef = undefined; if (sideEffectFn ?? isRunOptionsGuard(sideEffectFn)) { - runOnInstanceDestroySub = this.runOnInstanceCleanUp(() => (sideEffectFn as RunOptions).onCleanUp?.()); + runOnInstanceDestroySub = this.runOnInstanceDestroy(() => + (sideEffectFn as RunOptions).onCleanUp?.() + ); } return { @@ -115,7 +141,9 @@ export class Effects { const subscription = obsOrSub$ .pipe( // execute operation/ side effect - sideEffectFn && typeof sideEffectFn === 'function' ? tap(sideEffectFn) : pipe(), + sideEffectFn && typeof sideEffectFn === 'function' + ? tap(sideEffectFn) + : pipe(), catchError((err) => { this.errorHandler?.handleError(err); return EMPTY; @@ -128,10 +156,14 @@ export class Effects { // cases sideEffectFn is a CleanUpRef OR options given let runOnInstanceDestroySub: undefined | EffectCleanUpRef = undefined; if (options && options.onCleanUp) { - runOnInstanceDestroySub = this.runOnInstanceCleanUp(() => options.onCleanUp?.()); + runOnInstanceDestroySub = this.runOnInstanceDestroy(() => + options.onCleanUp?.() + ); } if (sideEffectFn && isRunOptionsGuard(sideEffectFn)) { - runOnInstanceDestroySub = this.runOnInstanceCleanUp(() => (sideEffectFn as RunOptions).onCleanUp?.()); + runOnInstanceDestroySub = this.runOnInstanceDestroy(() => + (sideEffectFn as RunOptions).onCleanUp?.() + ); } return { @@ -153,7 +185,7 @@ export class Effects { * Execute a sideEffect when the rxEffect instance OnDestroy hook is executed * @param sideEffectFn */ - runOnInstanceCleanUp(sideEffectFn: () => void) { + runOnInstanceDestroy(sideEffectFn: () => void) { return this.run(this.destroyHook$$.pipe(tap(sideEffectFn)).subscribe()); } @@ -174,9 +206,9 @@ export class Effects { } } - -type EffectsSetupFn = (rxEffect: Pick) => void; - +type EffectsSetupFn = ( + rxEffect: Pick +) => void; /** * @description @@ -216,12 +248,11 @@ export function rxEffect( const teardownFn = setupFn?.({ run: effects.run.bind(effects), - runOnInstanceCleanUp: effects.runOnInstanceCleanUp.bind(effects), + runOnInstanceDestroy: effects.runOnInstanceDestroy.bind(effects), }); - destroyRef.onDestroy(() => { - effects.cleanUp() + effects.cleanUp(); }); return effects;