From 370a5d75c14d1f7d564f12d8edb785e734cce80c Mon Sep 17 00:00:00 2001 From: kwintenp Date: Mon, 12 Sep 2016 21:45:20 +0200 Subject: [PATCH] Added support for nesting in the combineReducers utility method. Added testing for changes made. --- spec/edge_spec.ts | 2 +- spec/integration_spec.ts | 2 +- spec/state_spec.ts | 2 +- spec/store_spec.ts | 107 ++++++++++++++++++++++++++++++++++++--- src/utils.ts | 2 + 5 files changed, 104 insertions(+), 11 deletions(-) diff --git a/spec/edge_spec.ts b/spec/edge_spec.ts index 4623f83..a7fe863 100644 --- a/spec/edge_spec.ts +++ b/spec/edge_spec.ts @@ -4,7 +4,7 @@ require('reflect-metadata'); import {Store, Dispatcher, State, Action, combineReducers} from '../src/index'; import {StoreModule} from '../src/ng2'; import {Observable} from 'rxjs/Observable'; -import {ReflectiveInjector, provide} from '@angular/core'; +import {ReflectiveInjector} from '@angular/core'; import {todos, todoCount} from './fixtures/edge_todos'; diff --git a/spec/integration_spec.ts b/spec/integration_spec.ts index 9aa3d43..f87d908 100644 --- a/spec/integration_spec.ts +++ b/spec/integration_spec.ts @@ -3,7 +3,7 @@ require('es6-shim'); require('reflect-metadata'); import {Store, StoreModule, Action, combineReducers, INITIAL_REDUCER, INITIAL_STATE} from '../src/index'; import {Observable} from 'rxjs/Observable'; -import {ReflectiveInjector, provide} from '@angular/core'; +import {ReflectiveInjector} from '@angular/core'; import 'rxjs/add/observable/combineLatest'; import {counterReducer, INCREMENT, DECREMENT, RESET} from './fixtures/counter'; diff --git a/spec/state_spec.ts b/spec/state_spec.ts index 124d437..1190b19 100644 --- a/spec/state_spec.ts +++ b/spec/state_spec.ts @@ -3,7 +3,7 @@ require('es6-shim'); require('reflect-metadata'); import {Observable} from 'rxjs/Observable'; import {Subject} from 'rxjs/Subject'; -import {ReflectiveInjector, provide} from '@angular/core'; +import {ReflectiveInjector} from '@angular/core'; import {Dispatcher, State, Reducer, Action, provideStore, StoreModule} from '../src/index'; diff --git a/spec/store_spec.ts b/spec/store_spec.ts index 0fe3f38..771072b 100644 --- a/spec/store_spec.ts +++ b/spec/store_spec.ts @@ -1,12 +1,10 @@ declare var describe, it, expect, hot, cold, expectObservable, expectSubscriptions, console, beforeEach; require('es6-shim'); require('reflect-metadata'); -import 'rxjs/add/operator/take'; -import {Store, Dispatcher, State, Action, combineReducers, StoreModule} from '../src/index'; -import {Observable} from 'rxjs/Observable'; -import {ReflectiveInjector, provide} from '@angular/core'; - -import {counterReducer, INCREMENT, DECREMENT, RESET} from './fixtures/counter'; +import "rxjs/add/operator/take"; +import {Store, Dispatcher, combineReducers, StoreModule} from "../src/index"; +import {ReflectiveInjector} from "@angular/core"; +import {counterReducer, INCREMENT, DECREMENT, RESET} from "./fixtures/counter"; interface TestAppSchema { counter1: number; @@ -14,7 +12,17 @@ interface TestAppSchema { counter3: number; } -interface Todo { } +interface TestAppSchema2 { + counter1: number; + counter2: TestAppSchemaCounter2; +} + +interface TestAppSchemaCounter2 { + counter2a: number; +} + +interface Todo { +} interface TodoAppSchema { visibilityFilter: string; @@ -22,7 +30,6 @@ interface TodoAppSchema { } - describe('ngRx Store', () => { describe('basic store actions', function() { @@ -194,4 +201,88 @@ describe('ngRx Store', () => { expect(dispatcherSubscription.isUnsubscribed).toBe(false); }); }); + + describe('store with nested reducer tree', () => { + let injector: ReflectiveInjector; + let store: Store; + let dispatcher: Dispatcher; + + beforeEach(() => { + const rootReducer = combineReducers({ + counter1: counterReducer, + counter2: { + counter2a: counterReducer + } + }); + + const initialValue = + { + counter1: 0, + counter2: { + counter2a: 1 + } + }; + + injector = ReflectiveInjector.resolveAndCreate([ + StoreModule.provideStore(rootReducer, initialValue).providers + ]); + + store = injector.get(Store); + dispatcher = injector.get(Dispatcher); + }); + + const actionSequence = '--a--b--c--d--e'; + const actionValues = { + a: {type: INCREMENT}, + b: {type: INCREMENT}, + c: {type: DECREMENT}, + d: {type: RESET}, + e: {type: INCREMENT} + }; + + it('should create a nested reducer structure', () => { + const storeStructure = store.select(s => s); + + store.take(1).subscribe((state) => { + expect(state).toEqual({counter1: 0, counter2: {counter2a: 1}}); + }); + }); + + it('should let you select the nested state via string or function', function () { + + const counterSteps = hot(actionSequence, actionValues); + + counterSteps.subscribe((action) => store.dispatch(action)); + + const counterStateWithString = store.select('counter2','counter2a'); + const counterStateWithFunc = store.select(s => s.counter2.counter2a); + + const stateSequence = 'i-v--w--x--y--z'; + const counter2aValues = {i: 1, v: 2, w: 3, x: 2, y: 0, z: 1}; + + expectObservable(counterStateWithString).toBe(stateSequence, counter2aValues); + expectObservable(counterStateWithFunc).toBe(stateSequence, counter2aValues); + }); + + it('should increment and decrement counter2', function () { + + const counterSteps = hot(actionSequence, actionValues); + + counterSteps.subscribe((action) => store.dispatch(action)); + + const counterState = store.select('counter2'); + + const stateSequence = 'i-v--w--x--y--z'; + const counter1Values = { + i: {counter2a: 1}, + v: {counter2a: 2}, + w: {counter2a: 3}, + x: {counter2a: 2}, + y: {counter2a: 0}, + z: {counter2a: 1} + }; + + expectObservable(counterState).toBe(stateSequence, counter1Values); + }); + }); }); diff --git a/src/utils.ts b/src/utils.ts index 1710f55..eae3789 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -8,6 +8,8 @@ export function combineReducers(reducers: any): ActionReducer { const key = reducerKeys[i]; if (typeof reducers[key] === 'function') { finalReducers[key] = reducers[key]; + } else { + finalReducers[key] = combineReducers(reducers[key]); } }