Skip to content

Commit

Permalink
feat(store): add refreshState method to mock store (#2148)
Browse files Browse the repository at this point in the history
Closes #2121
  • Loading branch information
brandonroberts authored and timdeschryver committed Oct 6, 2019
1 parent 1728121 commit 30e876f
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 6 deletions.
1 change: 1 addition & 0 deletions modules/store/testing/spec/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ ts_test_library(
"//modules/store",
"//modules/store/spec/fixtures",
"//modules/store/testing",
"@npm//@angular/platform-browser",
"@npm//rxjs",
],
)
Expand Down
91 changes: 88 additions & 3 deletions modules/store/testing/spec/mock_store.spec.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
import { TestBed } from '@angular/core/testing';
import { skip, take } from 'rxjs/operators';
import { TestBed, ComponentFixture } from '@angular/core/testing';
import { skip, take, tap } from 'rxjs/operators';
import { MockStore, provideMockStore } from '@ngrx/store/testing';
import { Store, createSelector, select, StoreModule } from '@ngrx/store';
import {
Store,
createSelector,
select,
StoreModule,
MemoizedSelector,
createFeatureSelector,
} from '@ngrx/store';
import { INCREMENT } from '../../spec/fixtures/counter';
import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs';
import { By } from '@angular/platform-browser';

interface TestAppSchema {
counter1: number;
Expand Down Expand Up @@ -260,6 +270,81 @@ describe('Mock Store', () => {
});
});

describe('Refreshing state', () => {
type TodoState = {
items: { name: string; done: boolean }[];
};
const selectTodosState = createFeatureSelector<TodoState>('todos');
const todos = createSelector(selectTodosState, todos => todos.items);
const getTodoItems = (elSelector: string) =>
fixture.debugElement.queryAll(By.css(elSelector));
let mockStore: MockStore<TodoState>;
let mockSelector: MemoizedSelector<TodoState, any[]>;
const initialTodos = [{ name: 'aaa', done: false }];
let fixture: ComponentFixture<TodosComponent>;

@Component({
selector: 'app-todos',
template: `
<ul>
<li *ngFor="let todo of todos | async">
{{ todo.name }} <input type="checkbox" [checked]="todo.done" />
</li>
<p *ngFor="let todo of todosSelect | async">
{{ todo.name }} <input type="checkbox" [checked]="todo.done" />
</p>
</ul>
`,
})
class TodosComponent implements OnInit {
todos: Observable<any[]>;
todosSelect: Observable<any[]>;

constructor(private store: Store<{}>) {}

ngOnInit() {
this.todos = this.store.pipe(select(todos));
this.todosSelect = this.store.select(todos);
}
}

beforeEach(() => {
TestBed.configureTestingModule({
declarations: [TodosComponent],
providers: [provideMockStore()],
}).compileComponents();

mockStore = TestBed.get(Store);
mockSelector = mockStore.overrideSelector(todos, initialTodos);

fixture = TestBed.createComponent(TodosComponent);
fixture.detectChanges();
});

it('should work with store and select operator', () => {
const newTodos = [{ name: 'bbb', done: true }];
mockSelector.setResult(newTodos);
mockStore.refreshState();

fixture.detectChanges();

expect(getTodoItems('li').length).toBe(1);
expect(getTodoItems('li')[0].nativeElement.textContent.trim()).toBe('bbb');
});

it('should work with store.select method', () => {
const newTodos = [{ name: 'bbb', done: true }];
mockSelector.setResult(newTodos);
mockStore.refreshState();

fixture.detectChanges();

expect(getTodoItems('p').length).toBe(1);
expect(getTodoItems('p')[0].nativeElement.textContent.trim()).toBe('bbb');
});
});

describe('Cleans up after each test', () => {
const selectData = createSelector(
(state: any) => state,
Expand Down
13 changes: 11 additions & 2 deletions modules/store/testing/src/mock_store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export class MockStore<T> extends Store<T> {
>();

public scannedActions$: Observable<Action>;
private lastState: T;

constructor(
private state$: MockState<T>,
Expand All @@ -46,7 +47,7 @@ export class MockStore<T> extends Store<T> {
) {
super(state$, actionsObserver, reducerManager);
this.resetSelectors();
this.state$.next(this.initialState);
this.setState(this.initialState);
this.scannedActions$ = actionsObserver.asObservable();
if (mockSelectors) {
mockSelectors.forEach(mockSelector => {
Expand All @@ -62,6 +63,7 @@ export class MockStore<T> extends Store<T> {

setState(nextState: T): void {
this.state$.next(nextState);
this.lastState = nextState;
}

overrideSelector<T, Result>(
Expand Down Expand Up @@ -108,7 +110,7 @@ export class MockStore<T> extends Store<T> {
}

select(selector: any, prop?: any) {
if (MockStore.selectors.has(selector)) {
if (typeof selector === 'string' && MockStore.selectors.has(selector)) {
return new BehaviorSubject<any>(
MockStore.selectors.get(selector)
).asObservable();
Expand All @@ -124,4 +126,11 @@ export class MockStore<T> extends Store<T> {
removeReducer() {
/* noop */
}

/**
* Refreshes the existing state.
*/
refreshState() {
this.setState({ ...(this.lastState as T) });
}
}
2 changes: 1 addition & 1 deletion modules/store/testing/src/testing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export function provideMockStore<T = any>(
return [
ActionsSubject,
MockState,
{ provide: INITIAL_STATE, useValue: config.initialState },
{ provide: INITIAL_STATE, useValue: config.initialState || {} },
{ provide: MOCK_SELECTORS, useValue: config.selectors },
{ provide: StateObservable, useClass: MockState },
{ provide: ReducerManager, useClass: MockReducerManager },
Expand Down

0 comments on commit 30e876f

Please sign in to comment.