From 00be3d12300d8cedf130da0fecb868832dba4ff9 Mon Sep 17 00:00:00 2001 From: Rupesh Tiwari Date: Sun, 12 Nov 2017 22:27:50 -0500 Subject: [PATCH] feat(StoreDevtools): Add support for custom instance name (#517) Closes #463 --- modules/router-store/spec/integration.spec.ts | 3 - .../router-store/src/router_store_module.ts | 1 - modules/store-devtools/spec/config.spec.ts | 10 +++ modules/store-devtools/spec/extension.spec.ts | 79 +++++++++++++++++++ modules/store-devtools/src/config.ts | 8 +- modules/store-devtools/src/devtools.ts | 10 ++- modules/store-devtools/src/extension.ts | 18 ++--- modules/store-devtools/src/instrument.ts | 4 + modules/store-devtools/src/reducer.ts | 3 +- 9 files changed, 115 insertions(+), 21 deletions(-) create mode 100644 modules/store-devtools/spec/config.spec.ts create mode 100644 modules/store-devtools/spec/extension.spec.ts diff --git a/modules/router-store/spec/integration.spec.ts b/modules/router-store/spec/integration.spec.ts index 9c753ad729..f577e9365c 100644 --- a/modules/router-store/spec/integration.spec.ts +++ b/modules/router-store/spec/integration.spec.ts @@ -44,7 +44,6 @@ describe('integration spec', () => { { type: 'router', event: 'NavigationStart', url: '/' }, { type: 'router', event: 'RoutesRecognized', url: '/' }, { type: 'store', state: '/' }, // ROUTER_NAVIGATION event in the store - /* new Router Lifecycle in Angular 4.3 */ { type: 'router', event: 'GuardsCheckStart', url: '/' }, { type: 'router', event: 'GuardsCheckEnd', url: '/' }, @@ -162,7 +161,6 @@ describe('integration spec', () => { { type: 'router', event: 'GuardsCheckEnd', url: '/next' }, // { type: 'router', event: 'ResolveStart', url: '/next' }, // { type: 'router', event: 'ResolveEnd', url: '/next' }, - { type: 'store', state: { @@ -451,7 +449,6 @@ describe('integration spec', () => { { type: 'router', event: 'NavigationStart', url: '/next' }, { type: 'router', event: 'RoutesRecognized', url: '/next' }, { type: 'store', state: undefined }, // after ROUTER_NAVIGATION - /* new Router Lifecycle in Angular 4.3 */ { type: 'router', event: 'GuardsCheckStart', url: '/next' }, { type: 'store', state: undefined }, // after USER_EVENT diff --git a/modules/router-store/src/router_store_module.ts b/modules/router-store/src/router_store_module.ts index 1b805f88b6..991e7db8bb 100644 --- a/modules/router-store/src/router_store_module.ts +++ b/modules/router-store/src/router_store_module.ts @@ -221,7 +221,6 @@ export class StoreRouterConnectingModule { private dispatchTriggeredByRouter: boolean = false; // used only in dev mode in combination with routerReducer private navigationTriggeredByDispatch: boolean = false; // used only in dev mode in combination with routerReducer - private stateKey: string; constructor( diff --git a/modules/store-devtools/spec/config.spec.ts b/modules/store-devtools/spec/config.spec.ts new file mode 100644 index 0000000000..80645df646 --- /dev/null +++ b/modules/store-devtools/spec/config.spec.ts @@ -0,0 +1,10 @@ +import { ActionReducer, Action } from '@ngrx/store'; +import { StoreDevtoolsConfig } from '../'; + +describe('StoreDevtoolsOptions', () => { + it('can be initialized with name', () => { + const options = new StoreDevtoolsConfig(); + options.name = 'my instance'; + expect(options.name).toBe('my instance'); + }); +}); diff --git a/modules/store-devtools/spec/extension.spec.ts b/modules/store-devtools/spec/extension.spec.ts new file mode 100644 index 0000000000..2e0556da8a --- /dev/null +++ b/modules/store-devtools/spec/extension.spec.ts @@ -0,0 +1,79 @@ +import { + StoreDevtools, + StoreDevtoolsModule, + LiftedState, + StoreDevtoolsConfig, + StoreDevtoolsOptions, +} from '../'; +import { + StoreModule, + Store, + StateObservable, + ActionReducer, + Action, + ReducerManager, +} from '@ngrx/store'; +import { of } from 'rxjs/observable/of'; +import { createConfig, noMonitor } from '../src/instrument'; +import { DevtoolsExtension, ReduxDevtoolsExtension } from '../src/extension'; + +describe('DevtoolsExtension', () => { + let reduxDevtoolsExtension: ReduxDevtoolsExtension; + let devtoolsExtension: DevtoolsExtension; + + beforeEach(() => { + reduxDevtoolsExtension = jasmine.createSpyObj('reduxDevtoolsExtension', [ + 'send', + 'connect', + ]); + (reduxDevtoolsExtension.connect as jasmine.Spy).and.returnValue(of({})); + spyOn(Date, 'now').and.returnValue('1509655064369'); + }); + + describe('notify', () => { + it('should send notification with default options', () => { + devtoolsExtension = new DevtoolsExtension( + reduxDevtoolsExtension, + createConfig({}) + ); + const defaultOptions = { + maxAge: false, + monitor: noMonitor, + name: 'NgRx Store DevTools', + serialize: false, + }; + const action = {} as Action; + const state = {} as LiftedState; + devtoolsExtension.notify(action, state); + expect(reduxDevtoolsExtension.send).toHaveBeenCalledWith( + null, + {}, + defaultOptions, + 'ngrx-store-1509655064369' + ); + }); + it('should send notification with given options', () => { + devtoolsExtension = new DevtoolsExtension( + reduxDevtoolsExtension, + createConfig({ + name: 'ngrx-store-devtool-todolist', + }) + ); + const defaultOptions = { + maxAge: false, + monitor: noMonitor, + name: 'ngrx-store-devtool-todolist', + serialize: false, + }; + const action = {} as Action; + const state = {} as LiftedState; + devtoolsExtension.notify(action, state); + expect(reduxDevtoolsExtension.send).toHaveBeenCalledWith( + null, + {}, + defaultOptions, + 'ngrx-store-1509655064369' + ); + }); + }); +}); diff --git a/modules/store-devtools/src/config.ts b/modules/store-devtools/src/config.ts index 7d2a8f7558..c527c0f7be 100644 --- a/modules/store-devtools/src/config.ts +++ b/modules/store-devtools/src/config.ts @@ -1,9 +1,11 @@ -import { ActionReducer } from '@ngrx/store'; +import { ActionReducer, Action } from '@ngrx/store'; import { InjectionToken, Type } from '@angular/core'; export class StoreDevtoolsConfig { - maxAge: number | false; - monitor: ActionReducer; + maxAge?: number | false; + monitor?: ActionReducer; + name?: string; + serialize?: boolean; } export const STORE_DEVTOOLS_CONFIG = new InjectionToken( diff --git a/modules/store-devtools/src/devtools.ts b/modules/store-devtools/src/devtools.ts index e5bf6f7b8d..fa7525bc8b 100644 --- a/modules/store-devtools/src/devtools.ts +++ b/modules/store-devtools/src/devtools.ts @@ -49,7 +49,11 @@ export class StoreDevtools implements Observer { initialState, liftedInitialState, config.monitor, - config.maxAge ? { maxAge: config.maxAge } : {} + { + maxAge: config.maxAge as number, + name: config.name, + serialize: config.serialize, + } ); const liftedAction$ = applyOperators(actions$.asObservable(), [ @@ -80,9 +84,9 @@ export class StoreDevtools implements Observer { liftedStateSubject.next(state); if (action.type === Actions.PERFORM_ACTION) { - const unlifedAction = (action as Actions.PerformAction).action; + const unliftedAction = (action as Actions.PerformAction).action; - scannedActions.next(unlifedAction); + scannedActions.next(unliftedAction); } }); diff --git a/modules/store-devtools/src/extension.ts b/modules/store-devtools/src/extension.ts index b98651b328..a3217797ba 100644 --- a/modules/store-devtools/src/extension.ts +++ b/modules/store-devtools/src/extension.ts @@ -1,4 +1,5 @@ -import { InjectionToken, Inject, Injectable } from '@angular/core'; +import { Inject, Injectable, InjectionToken } from '@angular/core'; +import { Action } from '@ngrx/store'; import { Observable } from 'rxjs/Observable'; import { empty } from 'rxjs/observable/empty'; import { filter } from 'rxjs/operator/filter'; @@ -6,7 +7,8 @@ import { map } from 'rxjs/operator/map'; import { share } from 'rxjs/operator/share'; import { switchMap } from 'rxjs/operator/switchMap'; import { takeUntil } from 'rxjs/operator/takeUntil'; -import { Action } from '@ngrx/store'; + +import { STORE_DEVTOOLS_CONFIG, StoreDevtoolsConfig } from './config'; import { LiftedState } from './reducer'; import { applyOperators } from './utils'; @@ -35,7 +37,7 @@ export interface ReduxDevtoolsExtension { send( action: any, state: any, - options?: boolean | { serialize: boolean | object }, + options: StoreDevtoolsConfig, instanceId?: string ): void; } @@ -49,7 +51,8 @@ export class DevtoolsExtension { actions$: Observable; constructor( - @Inject(REDUX_DEVTOOLS_EXTENSION) devtoolsExtension: ReduxDevtoolsExtension + @Inject(REDUX_DEVTOOLS_EXTENSION) devtoolsExtension: ReduxDevtoolsExtension, + @Inject(STORE_DEVTOOLS_CONFIG) private config: StoreDevtoolsConfig ) { this.devtoolsExtension = devtoolsExtension; this.createActionStreams(); @@ -60,12 +63,7 @@ export class DevtoolsExtension { return; } - this.devtoolsExtension.send( - null, - state, - { serialize: false }, - this.instanceId - ); + this.devtoolsExtension.send(null, state, this.config, this.instanceId); } private createChangesObservable(): Observable { diff --git a/modules/store-devtools/src/instrument.ts b/modules/store-devtools/src/instrument.ts index 101e77479c..6f26e28978 100644 --- a/modules/store-devtools/src/instrument.ts +++ b/modules/store-devtools/src/instrument.ts @@ -64,12 +64,16 @@ export function noMonitor(): null { return null; } +export const DEFAULT_NAME = 'NgRx Store DevTools'; + export function createConfig( _options: StoreDevtoolsOptions ): StoreDevtoolsConfig { const DEFAULT_OPTIONS: StoreDevtoolsConfig = { maxAge: false, monitor: noMonitor, + name: DEFAULT_NAME, + serialize: false, }; let options = typeof _options === 'function' ? _options() : _options; diff --git a/modules/store-devtools/src/reducer.ts b/modules/store-devtools/src/reducer.ts index 253ee149d8..54cc77b75e 100644 --- a/modules/store-devtools/src/reducer.ts +++ b/modules/store-devtools/src/reducer.ts @@ -9,6 +9,7 @@ import { import { difference, liftAction } from './utils'; import * as Actions from './actions'; +import { StoreDevtoolsConfig } from './config'; export type InitAction = { readonly type: typeof INIT; @@ -129,7 +130,7 @@ export function liftReducerWith( initialCommittedState: any, initialLiftedState: LiftedState, monitorReducer?: any, - options: { maxAge?: number } = {} + options: Partial = {} ) { /** * Manages how the history actions modify the history state.