Skip to content

Commit 30e876f

Browse files
brandonrobertstimdeschryver
authored andcommitted
feat(store): add refreshState method to mock store (#2148)
Closes #2121
1 parent 1728121 commit 30e876f

File tree

4 files changed

+101
-6
lines changed

4 files changed

+101
-6
lines changed

modules/store/testing/spec/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ ts_test_library(
99
"//modules/store",
1010
"//modules/store/spec/fixtures",
1111
"//modules/store/testing",
12+
"@npm//@angular/platform-browser",
1213
"@npm//rxjs",
1314
],
1415
)

modules/store/testing/spec/mock_store.spec.ts

Lines changed: 88 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,18 @@
1-
import { TestBed } from '@angular/core/testing';
2-
import { skip, take } from 'rxjs/operators';
1+
import { TestBed, ComponentFixture } from '@angular/core/testing';
2+
import { skip, take, tap } from 'rxjs/operators';
33
import { MockStore, provideMockStore } from '@ngrx/store/testing';
4-
import { Store, createSelector, select, StoreModule } from '@ngrx/store';
4+
import {
5+
Store,
6+
createSelector,
7+
select,
8+
StoreModule,
9+
MemoizedSelector,
10+
createFeatureSelector,
11+
} from '@ngrx/store';
512
import { INCREMENT } from '../../spec/fixtures/counter';
13+
import { Component, OnInit } from '@angular/core';
14+
import { Observable } from 'rxjs';
15+
import { By } from '@angular/platform-browser';
616

717
interface TestAppSchema {
818
counter1: number;
@@ -260,6 +270,81 @@ describe('Mock Store', () => {
260270
});
261271
});
262272

273+
describe('Refreshing state', () => {
274+
type TodoState = {
275+
items: { name: string; done: boolean }[];
276+
};
277+
const selectTodosState = createFeatureSelector<TodoState>('todos');
278+
const todos = createSelector(selectTodosState, todos => todos.items);
279+
const getTodoItems = (elSelector: string) =>
280+
fixture.debugElement.queryAll(By.css(elSelector));
281+
let mockStore: MockStore<TodoState>;
282+
let mockSelector: MemoizedSelector<TodoState, any[]>;
283+
const initialTodos = [{ name: 'aaa', done: false }];
284+
let fixture: ComponentFixture<TodosComponent>;
285+
286+
@Component({
287+
selector: 'app-todos',
288+
template: `
289+
<ul>
290+
<li *ngFor="let todo of todos | async">
291+
{{ todo.name }} <input type="checkbox" [checked]="todo.done" />
292+
</li>
293+
294+
<p *ngFor="let todo of todosSelect | async">
295+
{{ todo.name }} <input type="checkbox" [checked]="todo.done" />
296+
</p>
297+
</ul>
298+
`,
299+
})
300+
class TodosComponent implements OnInit {
301+
todos: Observable<any[]>;
302+
todosSelect: Observable<any[]>;
303+
304+
constructor(private store: Store<{}>) {}
305+
306+
ngOnInit() {
307+
this.todos = this.store.pipe(select(todos));
308+
this.todosSelect = this.store.select(todos);
309+
}
310+
}
311+
312+
beforeEach(() => {
313+
TestBed.configureTestingModule({
314+
declarations: [TodosComponent],
315+
providers: [provideMockStore()],
316+
}).compileComponents();
317+
318+
mockStore = TestBed.get(Store);
319+
mockSelector = mockStore.overrideSelector(todos, initialTodos);
320+
321+
fixture = TestBed.createComponent(TodosComponent);
322+
fixture.detectChanges();
323+
});
324+
325+
it('should work with store and select operator', () => {
326+
const newTodos = [{ name: 'bbb', done: true }];
327+
mockSelector.setResult(newTodos);
328+
mockStore.refreshState();
329+
330+
fixture.detectChanges();
331+
332+
expect(getTodoItems('li').length).toBe(1);
333+
expect(getTodoItems('li')[0].nativeElement.textContent.trim()).toBe('bbb');
334+
});
335+
336+
it('should work with store.select method', () => {
337+
const newTodos = [{ name: 'bbb', done: true }];
338+
mockSelector.setResult(newTodos);
339+
mockStore.refreshState();
340+
341+
fixture.detectChanges();
342+
343+
expect(getTodoItems('p').length).toBe(1);
344+
expect(getTodoItems('p')[0].nativeElement.textContent.trim()).toBe('bbb');
345+
});
346+
});
347+
263348
describe('Cleans up after each test', () => {
264349
const selectData = createSelector(
265350
(state: any) => state,

modules/store/testing/src/mock_store.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ export class MockStore<T> extends Store<T> {
3636
>();
3737

3838
public scannedActions$: Observable<Action>;
39+
private lastState: T;
3940

4041
constructor(
4142
private state$: MockState<T>,
@@ -46,7 +47,7 @@ export class MockStore<T> extends Store<T> {
4647
) {
4748
super(state$, actionsObserver, reducerManager);
4849
this.resetSelectors();
49-
this.state$.next(this.initialState);
50+
this.setState(this.initialState);
5051
this.scannedActions$ = actionsObserver.asObservable();
5152
if (mockSelectors) {
5253
mockSelectors.forEach(mockSelector => {
@@ -62,6 +63,7 @@ export class MockStore<T> extends Store<T> {
6263

6364
setState(nextState: T): void {
6465
this.state$.next(nextState);
66+
this.lastState = nextState;
6567
}
6668

6769
overrideSelector<T, Result>(
@@ -108,7 +110,7 @@ export class MockStore<T> extends Store<T> {
108110
}
109111

110112
select(selector: any, prop?: any) {
111-
if (MockStore.selectors.has(selector)) {
113+
if (typeof selector === 'string' && MockStore.selectors.has(selector)) {
112114
return new BehaviorSubject<any>(
113115
MockStore.selectors.get(selector)
114116
).asObservable();
@@ -124,4 +126,11 @@ export class MockStore<T> extends Store<T> {
124126
removeReducer() {
125127
/* noop */
126128
}
129+
130+
/**
131+
* Refreshes the existing state.
132+
*/
133+
refreshState() {
134+
this.setState({ ...(this.lastState as T) });
135+
}
127136
}

modules/store/testing/src/testing.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export function provideMockStore<T = any>(
2323
return [
2424
ActionsSubject,
2525
MockState,
26-
{ provide: INITIAL_STATE, useValue: config.initialState },
26+
{ provide: INITIAL_STATE, useValue: config.initialState || {} },
2727
{ provide: MOCK_SELECTORS, useValue: config.selectors },
2828
{ provide: StateObservable, useClass: MockState },
2929
{ provide: ReducerManager, useClass: MockReducerManager },

0 commit comments

Comments
 (0)