From 0ad87f6f72361270d4a4f150dfc7c3a458807100 Mon Sep 17 00:00:00 2001 From: wawyed Date: Thu, 26 Sep 2019 08:59:07 +0100 Subject: [PATCH] feat(resolve): Remove RXWAIT async policy in favour of allowing user defined async policy function (#366) BREAKING CHANGE: RXWAIT async policy has been removed, but it never worked in the first place --- src/resolve/interface.ts | 3 +- src/resolve/resolvable.ts | 26 ++++----------- test/resolveSpec.ts | 66 ++++++++++++++++++++++++++++++--------- 3 files changed, 60 insertions(+), 35 deletions(-) diff --git a/src/resolve/interface.ts b/src/resolve/interface.ts index 7cb2823b..f7c0c781 100644 --- a/src/resolve/interface.ts +++ b/src/resolve/interface.ts @@ -200,7 +200,8 @@ export interface ResolvePolicy { } export type PolicyWhen = 'LAZY' | 'EAGER'; -export type PolicyAsync = 'WAIT' | 'NOWAIT' | 'RXWAIT'; +export type PolicyAsync = 'WAIT' | 'NOWAIT' | CustomAsyncPolicy; +export type CustomAsyncPolicy = (data: TResolveFnResult) => Promise; /** @internalapi */ export let resolvePolicies = { diff --git a/src/resolve/resolvable.ts b/src/resolve/resolvable.ts index 98d36e3d..af02c5af 100644 --- a/src/resolve/resolvable.ts +++ b/src/resolve/resolvable.ts @@ -1,8 +1,8 @@ /** @publicapi @module resolve */ /** */ -import { extend, equals, inArray, identity } from '../common/common'; +import { extend, identity } from '../common/common'; import { services } from '../common/coreservices'; import { trace } from '../common/trace'; -import { ResolvePolicy, ResolvableLiteral, resolvePolicies } from './interface'; +import { ResolvePolicy, ResolvableLiteral, PolicyAsync } from './interface'; import { ResolveContext } from './resolveContext'; import { stringify } from '../common/strings'; @@ -117,26 +117,12 @@ export class Resolvable implements ResolvableLiteral { // Invokes the resolve function passing the resolved dependencies as arguments const invokeResolveFn = (resolvedDeps: any[]) => this.resolveFn.apply(null, resolvedDeps); - /** - * For RXWAIT policy: - * - * Given an observable returned from a resolve function: - * - enables .cache() mode (this allows multicast subscribers) - * - then calls toPromise() (this triggers subscribe() and thus fetches) - * - Waits for the promise, then return the cached observable (not the first emitted value). - */ - const waitForRx = (observable$: any) => { - const cached = observable$.cache(1); - return cached - .take(1) - .toPromise() - .then(() => cached); - }; - // If the resolve policy is RXWAIT, wait for the observable to emit something. otherwise pass through. const node: PathNode = resolveContext.findNode(this); const state: StateObject = node && node.state; - const maybeWaitForRx = this.getPolicy(state).async === 'RXWAIT' ? waitForRx : identity; + + const asyncPolicy: PolicyAsync = this.getPolicy(state).async; + const customAsyncPolicy = isFunction(asyncPolicy) ? asyncPolicy : identity; // After the final value has been resolved, update the state of the Resolvable const applyResolvedValue = (resolvedValue: any) => { @@ -152,7 +138,7 @@ export class Resolvable implements ResolvableLiteral { .when() .then(getResolvableDependencies) .then(invokeResolveFn) - .then(maybeWaitForRx) + .then(customAsyncPolicy) .then(applyResolvedValue)); } diff --git a/test/resolveSpec.ts b/test/resolveSpec.ts index 9a35d0f1..4507cace 100644 --- a/test/resolveSpec.ts +++ b/test/resolveSpec.ts @@ -1,13 +1,13 @@ -import { ResolveContext, StateObject, PathNode, Resolvable, copy } from '../src/index'; +import { tail } from '../src/common/common'; import { services } from '../src/common/coreservices'; -import { tree2Array } from './_testUtils'; +import { copy, CustomAsyncPolicy, PathNode, Resolvable, ResolveContext, StateObject } from '../src/index'; import { UIRouter } from '../src/router'; - -import { TestingPlugin } from './_testingPlugin'; +import { StateRegistry } from '../src/state/stateRegistry'; import { StateService } from '../src/state/stateService'; import { TransitionService } from '../src/transition/transitionService'; -import { StateRegistry } from '../src/state/stateRegistry'; -import { tail } from '../src/common/common'; + +import { TestingPlugin } from './_testingPlugin'; +import { tree2Array } from './_testUtils'; /////////////////////////////////////////////// @@ -426,6 +426,7 @@ describe('Resolvables system:', function() { const ctx = new ResolveContext(path); let result; + function checkCounts() { expect(result).toBe('JJ2K'); expect(counts['_J']).toBe(1); @@ -561,10 +562,11 @@ describe('Resolvables system:', function() { describe('NOWAIT Resolve Policy', () => { it('should allow a transition to complete before the resolve is settled', async done => { - let resolve, - resolvePromise = new Promise(_resolve => { - resolve = _resolve; - }); + let resolve; + + const resolvePromise = new Promise(_resolve => { + resolve = _resolve; + }); $registry.register({ name: 'nowait', @@ -599,10 +601,11 @@ describe('Resolvables system:', function() { }); it('should wait for WAIT resolves and not wait for NOWAIT resolves', async done => { - let promiseResolveFn, - resolvePromise = new Promise(resolve => { - promiseResolveFn = resolve; - }); + let promiseResolveFn; + + const resolvePromise = new Promise(resolve => { + promiseResolveFn = resolve; + }); $registry.register({ name: 'nowait', @@ -635,4 +638,39 @@ describe('Resolvables system:', function() { $state.go('nowait'); }); }); + + describe('custom Resolve Policy', () => { + let customResolvePolicy: CustomAsyncPolicy; + + it('should wait for the promise to resolve before finishing the transition', async done => { + let resolve: (value: string) => void; + + const resolvePromise: Promise = new Promise(_resolve => { + resolve = _resolve; + }); + + customResolvePolicy = jasmine.createSpy('customResolvePolicy'); + + (customResolvePolicy as jasmine.Spy).and.callFake((data: { useMe: Promise }) => { + return data.useMe; + }); + + resolve('myAwaitedValue'); + + $registry.register({ + name: 'customPolicy', + resolve: { + customWait: () => ({ useMe: resolvePromise }), + }, + resolvePolicy: { async: customResolvePolicy }, + }); + + $transitions.onSuccess({}, trans => { + expect(customResolvePolicy).toHaveBeenCalledWith({ useMe: resolvePromise }); + expect(trans.injector().get('customWait')).toBe('myAwaitedValue'); + }); + + $state.go('customPolicy').then(done); + }); + }); });