diff --git a/modules/store/testing/spec/BUILD b/modules/store/testing/spec/BUILD index 49a3118f63..64c2a4a799 100644 --- a/modules/store/testing/spec/BUILD +++ b/modules/store/testing/spec/BUILD @@ -9,6 +9,7 @@ ts_test_library( "//modules/store", "//modules/store/spec/fixtures", "//modules/store/testing", + "@npm//@angular/platform-browser", "@npm//rxjs", ], ) diff --git a/modules/store/testing/spec/mock_store.spec.ts b/modules/store/testing/spec/mock_store.spec.ts index ca0fdd578f..5ecb49ca91 100644 --- a/modules/store/testing/spec/mock_store.spec.ts +++ b/modules/store/testing/spec/mock_store.spec.ts @@ -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; @@ -260,6 +270,81 @@ describe('Mock Store', () => { }); }); +describe('Refreshing state', () => { + type TodoState = { + items: { name: string; done: boolean }[]; + }; + const selectTodosState = createFeatureSelector('todos'); + const todos = createSelector(selectTodosState, todos => todos.items); + const getTodoItems = (elSelector: string) => + fixture.debugElement.queryAll(By.css(elSelector)); + let mockStore: MockStore; + let mockSelector: MemoizedSelector; + const initialTodos = [{ name: 'aaa', done: false }]; + let fixture: ComponentFixture; + + @Component({ + selector: 'app-todos', + template: ` +
    +
  • + {{ todo.name }} +
  • + +

    + {{ todo.name }} +

    +
+ `, + }) + class TodosComponent implements OnInit { + todos: Observable; + todosSelect: Observable; + + 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, diff --git a/modules/store/testing/src/mock_store.ts b/modules/store/testing/src/mock_store.ts index 38e236f24b..7a3eb0a249 100644 --- a/modules/store/testing/src/mock_store.ts +++ b/modules/store/testing/src/mock_store.ts @@ -36,6 +36,7 @@ export class MockStore extends Store { >(); public scannedActions$: Observable; + private lastState: T; constructor( private state$: MockState, @@ -46,7 +47,7 @@ export class MockStore extends Store { ) { super(state$, actionsObserver, reducerManager); this.resetSelectors(); - this.state$.next(this.initialState); + this.setState(this.initialState); this.scannedActions$ = actionsObserver.asObservable(); if (mockSelectors) { mockSelectors.forEach(mockSelector => { @@ -62,6 +63,7 @@ export class MockStore extends Store { setState(nextState: T): void { this.state$.next(nextState); + this.lastState = nextState; } overrideSelector( @@ -108,7 +110,7 @@ export class MockStore extends Store { } select(selector: any, prop?: any) { - if (MockStore.selectors.has(selector)) { + if (typeof selector === 'string' && MockStore.selectors.has(selector)) { return new BehaviorSubject( MockStore.selectors.get(selector) ).asObservable(); @@ -124,4 +126,11 @@ export class MockStore extends Store { removeReducer() { /* noop */ } + + /** + * Refreshes the existing state. + */ + refreshState() { + this.setState({ ...(this.lastState as T) }); + } } diff --git a/modules/store/testing/src/testing.ts b/modules/store/testing/src/testing.ts index a81c6bd30b..70f41c742e 100644 --- a/modules/store/testing/src/testing.ts +++ b/modules/store/testing/src/testing.ts @@ -23,7 +23,7 @@ export function provideMockStore( 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 },