Skip to content
This repository has been archived by the owner on May 27, 2020. It is now read-only.

Commit

Permalink
feat: add selector
Browse files Browse the repository at this point in the history
  • Loading branch information
aymeric-duchein committed Dec 28, 2019
1 parent 39a93f0 commit 58421b6
Show file tree
Hide file tree
Showing 5 changed files with 119 additions and 2 deletions.
2 changes: 1 addition & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ module.exports = {
setupFilesAfterEnv: ['<rootDir>/setupJest.ts'],
rootDir: path.resolve('.'),
testMatch: ['<rootDir>/src/**/*.spec.ts'],
collectCoverageFrom: ['<rootDir>/src/lib/**/*.ts'],
collectCoverageFrom: ['<rootDir>/src/lib/**/*.ts', '<rootDir>/src/jest/**/*.ts' ],
coverageReporters: ['json', 'lcovonly', 'lcov', 'text', 'html'],
coveragePathIgnorePatterns: ['/node_modules/'],
globals: {
Expand Down
3 changes: 3 additions & 0 deletions src/jest/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"ngPackage": {}
}
29 changes: 29 additions & 0 deletions src/jest/src/jest-helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { of, Subject } from 'rxjs';
import { Store } from '@ngxs/store';
import { TestBed } from '@angular/core/testing';

class NgxsJestHelper {
private static mockedSelector: { key: any; value: Subject<any> }[] = [];

static mockSelect<T>(selectorFn: (state: any) => T): Subject<T> {
const store: Store = TestBed.get(Store);
if (!jest.isMockFunction(store.select)) {
jest.spyOn(store, 'select').mockImplementation((selector) => {
const match = NgxsJestHelper.mockedSelector.find((s) => s.key === selector);
if (match) {
return match.value;
}
return of();
});
}

const subject = new Subject<T>();
NgxsJestHelper.mockedSelector = [
...NgxsJestHelper.mockedSelector.filter((s) => s.key !== selectorFn),
{ key: selectorFn, value: subject }
];
return subject;
}
}

export const mockSelect = NgxsJestHelper.mockSelect;
1 change: 1 addition & 0 deletions src/jest/src/public_api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { mockSelect } from './jest-helpers';
86 changes: 85 additions & 1 deletion src/tests/ngxs.setup.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import { Action, NgxsAfterBootstrap, NgxsOnInit, State, StateContext } from '@ngxs/store';
import { Action, NgxsAfterBootstrap, NgxsModule, NgxsOnInit, Select, Selector, State, StateContext } from '@ngxs/store';
import { NgxsTestBed } from '../lib/ngxs.setup';
import { Component } from '@angular/core';
import { Observable, Subject } from 'rxjs';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { CommonModule } from '@angular/common';
import { mockSelect } from '../jest/src/public_api';

describe('Full testing NGXS States with NgxsTestBed', () => {
it('should be correct testing lifecycle with NgxsTestBed', () => {
Expand Down Expand Up @@ -136,3 +142,81 @@ describe('Full testing NGXS States with NgxsTestBed', () => {
expect(getStateContextMocks[FOOD_STATE_NAME].dispatch).toHaveBeenCalledTimes(1);
});
});

describe('Select tests', () => {
class FeedAction {
public static type = 'zoo';
constructor(public payload: number) {}
}

class AddAnimalsAction {
public static type = 'add animals';
constructor(public payload: number) {}
}

@State({ name: 'zoo', defaults: { feed: 0, animals: 0 } })
class ZooState {
@Selector()
static feed(state: { feed: number; animals: number }) {
return state.feed;
}

@Selector()
static animals(state: { feed: number; animals: number }) {
return state.animals;
}

@Action(FeedAction) public feed(ctx: StateContext<any>, { payload }: FeedAction) {
ctx.patchState({ feed: payload });
}

@Action(AddAnimalsAction) public animals(ctx: StateContext<any>, { payload }: AddAnimalsAction) {
const state = ctx.getState();
ctx.patchState({ animals: state.animals + payload });
}
}

@Component({
template: `
<span>{{ foodSelector$ | async }}</span>
<p>{{ animalsSelector$ | async }}</p>
`
})
class HostComponent {
@Select(ZooState.feed) foodSelector$!: Observable<number>;
@Select(ZooState.animals) animalsSelector$!: Observable<number>;
}

let foodSelectorSubject: Subject<number>;
let animalsSelectorSubject: Subject<number>;
let fixture: ComponentFixture<HostComponent>;
let component: HostComponent;

beforeEach(() => {
TestBed.configureTestingModule({
declarations: [HostComponent],
imports: [CommonModule, NgxsModule.forRoot([ZooState])]
}).compileComponents();

foodSelectorSubject = mockSelect(ZooState.feed);
animalsSelectorSubject = mockSelect(ZooState.animals);

fixture = TestBed.createComponent(HostComponent);
component = fixture.componentInstance;

fixture.detectChanges();
});

it('should display mocked value', () => {
foodSelectorSubject.next(1);
animalsSelectorSubject.next(2);

fixture.detectChanges();
expect(component).toBeTruthy();

const span = fixture.debugElement.query(By.css('span'));
expect(span.nativeElement.innerHTML).toMatch('1');
const p = fixture.debugElement.query(By.css('p'));
expect(p.nativeElement.innerHTML).toMatch('2');
});
});

0 comments on commit 58421b6

Please sign in to comment.