Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test/add specs #85

Merged
merged 2 commits into from
Jul 19, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion modules/store/spec/integration.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ describe('ngRx Integration spec', () => {
return todos.filter(predicate);
};

let currentlyVisibleTodos: any;
let currentlyVisibleTodos: Todo[] = [];

Observable.combineLatest(
store.select('visibilityFilter'),
Expand Down
180 changes: 137 additions & 43 deletions modules/store/spec/modules.spec.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
import 'rxjs/add/operator/take';
import { TestBed } from '@angular/core/testing';
import { NgModule, InjectionToken } from '@angular/core';
import { StoreModule, Store, ActionReducer, ActionReducerMap } from '../';
import {
StoreModule,
Store,
ActionReducer,
ActionReducerMap,
combineReducers,
} from '../';
import createSpy = jasmine.createSpy;

describe('Nested Store Modules', () => {
describe(`Store Modules`, () => {
type RootState = { fruit: string };
type FeatureAState = number;
type FeatureBState = { list: number[]; index: number };
Expand All @@ -14,57 +21,144 @@ describe('Nested Store Modules', () => {
const reducersToken = new InjectionToken<ActionReducerMap<RootState>>(
'Root Reducers'
);
const rootFruitReducer: ActionReducer<string> = () => 'apple';
const featureAReducer: ActionReducer<FeatureAState> = () => 5;
const featureBListReducer: ActionReducer<number[]> = () => [1, 2, 3];
const featureBIndexReducer: ActionReducer<number> = () => 2;

// Trigger here is basically an action type used to trigger state update
const createDummyReducer = <T>(def: T, trigger: string): ActionReducer<T> => (
s = def,
{ type, payload }: any
) => (type === trigger ? payload : s);
const rootFruitReducer = createDummyReducer('apple', 'fruit');
const featureAReducer = createDummyReducer(5, 'a');
const featureBListReducer = createDummyReducer([1, 2, 3], 'bList');
const featureBIndexReducer = createDummyReducer(2, 'bIndex');
const featureBReducerMap: ActionReducerMap<FeatureBState> = {
list: featureBListReducer,
index: featureBIndexReducer,
};

@NgModule({
imports: [StoreModule.forFeature('a', featureAReducer)],
})
class FeatureAModule {}

@NgModule({
imports: [StoreModule.forFeature('b', featureBReducerMap)],
})
class FeatureBModule {}

@NgModule({
imports: [
StoreModule.forRoot<RootState>(reducersToken),
FeatureAModule,
FeatureBModule,
],
providers: [
{
provide: reducersToken,
useValue: { fruit: rootFruitReducer },
},
],
})
class RootModule {}

beforeEach(() => {
TestBed.configureTestingModule({
imports: [RootModule],
describe(`: Config`, () => {
let featureAReducerFactory: any;
let rootReducerFactory: any;

const featureAInitial = () => ({ a: 42 });
const rootInitial = { fruit: 'orange' };

beforeEach(() => {
featureAReducerFactory = createSpy(
'featureAReducerFactory'
).and.callFake((rm: any, initialState?: any) => {
return (state: any, action: any) => 4;
});
rootReducerFactory = createSpy('rootReducerFactory').and.callFake(
combineReducers
);

@NgModule({
imports: [
StoreModule.forFeature(
'a',
{ a: featureAReducer },
{
initialState: featureAInitial,
reducerFactory: featureAReducerFactory,
}
),
],
})
class FeatureAModule {}

@NgModule({
imports: [
StoreModule.forRoot<RootState>(reducersToken, {
initialState: rootInitial,
reducerFactory: rootReducerFactory,
}),
FeatureAModule,
],
providers: [
{
provide: reducersToken,
useValue: { fruit: rootFruitReducer },
},
],
})
class RootModule {}

TestBed.configureTestingModule({
imports: [RootModule],
});

store = TestBed.get(Store);
});

it(`should accept configurations`, () => {
expect(featureAReducerFactory).toHaveBeenCalledWith(
{ a: featureAReducer },
featureAInitial()
);
expect(rootReducerFactory).toHaveBeenCalledWith(
{ fruit: rootFruitReducer },
rootInitial
);
});

store = TestBed.get(Store);
it(`should should use config.reducerFactory`, () => {
store.dispatch({ type: 'fruit', payload: 'banana' });
store.dispatch({ type: 'a', payload: 42 });

store.take(1).subscribe((s: any) => {
expect(s).toEqual({
fruit: 'banana',
a: 4,
});
});
});
});

it('should nest the child module in the root store object', () => {
store.take(1).subscribe((state: State) => {
expect(state).toEqual({
fruit: 'apple',
a: 5,
b: {
list: [1, 2, 3],
index: 2,
describe(`: Nested`, () => {
@NgModule({
imports: [StoreModule.forFeature('a', featureAReducer)],
})
class FeatureAModule {}

@NgModule({
imports: [StoreModule.forFeature('b', featureBReducerMap)],
})
class FeatureBModule {}

@NgModule({
imports: [
StoreModule.forRoot<RootState>(reducersToken),
FeatureAModule,
FeatureBModule,
],
providers: [
{
provide: reducersToken,
useValue: { fruit: rootFruitReducer },
},
],
})
class RootModule {}

beforeEach(() => {
TestBed.configureTestingModule({
imports: [RootModule],
});

store = TestBed.get(Store);
});

it('should nest the child module in the root store object', () => {
store.take(1).subscribe((state: State) => {
expect(state).toEqual({
fruit: 'apple',
a: 5,
b: {
list: [1, 2, 3],
index: 2,
},
});
});
});
});
Expand Down
4 changes: 2 additions & 2 deletions modules/store/spec/state.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Observable } from 'rxjs/Observable';
import { Subject } from 'rxjs/Subject';
import { ReflectiveInjector } from '@angular/core';
import { createInjector } from './helpers/injector';
import { StoreModule, Store } from '../';
import { StoreModule, Store, INIT } from '../';

describe('ngRx State', () => {
const initialState = 123;
Expand All @@ -22,7 +22,7 @@ describe('ngRx State', () => {
injector.get(Store);

expect(reducer).toHaveBeenCalledWith(initialState, {
type: '@ngrx/store/init',
type: INIT,
});
});
});
69 changes: 50 additions & 19 deletions modules/store/spec/store.spec.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,22 @@
import 'rxjs/add/operator/take';
import { Observable } from 'rxjs/Observable';
import { ReflectiveInjector } from '@angular/core';
import { hot } from 'jasmine-marbles';
import { createInjector } from './helpers/injector';
import { Store, Action, combineReducers, StoreModule } from '../';
import { ActionsSubject } from '../src/private_export';
import { ActionsSubject, ReducerManager, Store, StoreModule } from '../';
import {
counterReducer,
INCREMENT,
DECREMENT,
RESET,
} from './fixtures/counter';
import Spy = jasmine.Spy;
import any = jasmine.any;

interface TestAppSchema {
counter1: number;
counter2: number;
counter3: number;
}

interface Todo {}

interface TodoAppSchema {
visibilityFilter: string;
todos: Todo[];
counter4?: number;
}

describe('ngRx Store', () => {
Expand Down Expand Up @@ -68,7 +62,7 @@ describe('ngRx Store', () => {
});
});

describe('basic store actions', function() {
describe('basic store actions', () => {
beforeEach(() => setup());

it('should provide an Observable Store', () => {
Expand All @@ -84,7 +78,7 @@ describe('ngRx Store', () => {
e: { type: INCREMENT },
};

it('should let you select state with a key name', function() {
it('should let you select state with a key name', () => {
const counterSteps = hot(actionSequence, actionValues);

counterSteps.subscribe(action => store.dispatch(action));
Expand All @@ -99,7 +93,7 @@ describe('ngRx Store', () => {
);
});

it('should let you select state with a selector function', function() {
it('should let you select state with a selector function', () => {
const counterSteps = hot(actionSequence, actionValues);

counterSteps.subscribe(action => store.dispatch(action));
Expand All @@ -114,13 +108,13 @@ describe('ngRx Store', () => {
);
});

it('should correctly lift itself', function() {
it('should correctly lift itself', () => {
const result = store.select('counter1');

expect(result instanceof Store).toBe(true);
});

it('should increment and decrement counter1', function() {
it('should increment and decrement counter1', () => {
const counterSteps = hot(actionSequence, actionValues);

counterSteps.subscribe(action => store.dispatch(action));
Expand All @@ -133,7 +127,7 @@ describe('ngRx Store', () => {
expect(counterState).toBeObservable(hot(stateSequence, counter1Values));
});

it('should increment and decrement counter1 using the dispatcher', function() {
it('should increment and decrement counter1 using the dispatcher', () => {
const counterSteps = hot(actionSequence, actionValues);

counterSteps.subscribe(action => dispatcher.next(action));
Expand All @@ -146,7 +140,7 @@ describe('ngRx Store', () => {
expect(counterState).toBeObservable(hot(stateSequence, counter1Values));
});

it('should increment and decrement counter2 separately', function() {
it('should increment and decrement counter2 separately', () => {
const counterSteps = hot(actionSequence, actionValues);

counterSteps.subscribe(action => store.dispatch(action));
Expand All @@ -160,7 +154,7 @@ describe('ngRx Store', () => {
expect(counter2State).toBeObservable(hot(stateSequence, counter2Values));
});

it('should implement the observer interface forwarding actions and errors to the dispatcher', function() {
it('should implement the observer interface forwarding actions and errors to the dispatcher', () => {
spyOn(dispatcher, 'next');
spyOn(dispatcher, 'error');

Expand All @@ -171,7 +165,7 @@ describe('ngRx Store', () => {
expect(dispatcher.error).toHaveBeenCalledWith(2);
});

it('should not be completable', function() {
it('should not be completable', () => {
const storeSubscription = store.subscribe();
const dispatcherSubscription = dispatcher.subscribe();

Expand All @@ -192,4 +186,41 @@ describe('ngRx Store', () => {
expect(dispatcherSubscription.closed).toBe(true);
});
});

describe(`add/remove reducers`, () => {
let addReducerSpy: Spy;
let removeReducerSpy: Spy;
const key = 'counter4';

beforeEach(() => {
setup();
const reducerManager = injector.get(ReducerManager);
addReducerSpy = spyOn(reducerManager, 'addReducer').and.callThrough();
removeReducerSpy = spyOn(
reducerManager,
'removeReducer'
).and.callThrough();
});

it(`should delegate add/remove to ReducerManager`, () => {
store.addReducer(key, counterReducer);
expect(addReducerSpy).toHaveBeenCalledWith(key, counterReducer);

store.removeReducer(key);
expect(removeReducerSpy).toHaveBeenCalledWith(key);
});

it(`should work with added / removed reducers`, () => {
store.addReducer(key, counterReducer);
store.take(1).subscribe(val => {
expect(val.counter4).toBe(0);
});

store.removeReducer(key);
store.dispatch({ type: INCREMENT });
store.take(1).subscribe(val => {
expect(val.counter4).toBeUndefined();
});
});
});
});
Loading