#1077 Add Unit Tests For Cancellation

This commit is contained in:
Sebastian
2020-09-24 13:58:37 +02:00
parent 56ddf5b8b9
commit 1260a1e9f8
3 changed files with 108 additions and 26 deletions

View File

@@ -40,6 +40,7 @@ fdescribe('ShelfSearchComponent', () => {
resetSelectedFilters: () => {},
getProcessId: () => Promise.resolve(processId),
getProcess$: of(process),
cancelSearch: () => {},
}),
},
WindowRef,
@@ -69,6 +70,11 @@ fdescribe('ShelfSearchComponent', () => {
expect(component instanceof ShelfSearchComponent).toBeTruthy();
});
it('should cancel outstanding requests on destroy', () => {
component.ngOnDestroy();
expect(searchFacade.cancelSearch).toHaveBeenCalledTimes(1);
});
it('should restore the initial state on page load (onInit)', () => {
expect(component.restoreInitialState).toHaveBeenCalledTimes(1);
});

View File

@@ -2,21 +2,25 @@ import { SearchStateFacade } from './search.facade';
import { TestBed } from '@angular/core/testing';
import { Store, StoreModule } from '@ngrx/store';
import { Store as NgxsStore, NgxsModule } from '@ngxs/store';
import { primaryFiltersMock, mockFilters } from 'apps/sales/src/app/modules/shelf/shared/mockdata';
import {
primaryFiltersMock,
mockFilters,
} from 'apps/sales/src/app/modules/shelf/shared/mockdata';
import { PrimaryFilterOption } from 'apps/sales/src/app/modules/shelf/defs';
import { of, Observable } from 'rxjs';
import { of, Observable, Subject } from 'rxjs';
import * as actions from './search.actions';
import * as selectors from './search.selectors';
import { SelectFilter } from 'apps/sales/src/app/modules/filter';
import { first } from 'rxjs/operators';
import { hot, cold } from 'jasmine-marbles';
import { TypedAction } from '@ngrx/store/src/models';
fdescribe('SearchFacade', () => {
let facade: SearchStateFacade;
let store: Store<any>;
let ngxsStore: NgxsStore;
const id = 123;
beforeEach(async () => {
TestBed.configureTestingModule({
imports: [StoreModule.forRoot({}), NgxsModule.forRoot()],
@@ -28,6 +32,10 @@ fdescribe('SearchFacade', () => {
facade = TestBed.get(SearchStateFacade);
store = TestBed.get(Store);
ngxsStore = TestBed.get(NgxsStore);
spyOn(facade, 'getProcessId').and.returnValue(
new Promise((resolve) => resolve(id))
);
});
it('should be created', () => {
@@ -44,7 +52,9 @@ fdescribe('SearchFacade', () => {
it('should dispatch setPrimary in Store', async () => {
spyOn(store, 'dispatch').and.callThrough();
spyOn(facade, 'setPrimaryFilters').and.callThrough();
spyOn(facade, 'getPrimaryFilters$').and.returnValue(of(primaryFiltersMock));
spyOn(facade, 'getPrimaryFilters$').and.returnValue(
of(primaryFiltersMock)
);
await facade.setPrimaryFilters(primaryFilterOption, id);
@@ -54,13 +64,17 @@ fdescribe('SearchFacade', () => {
it('should keep the original order of filter when dispatching setPrimary in Store', async () => {
spyOn(store, 'dispatch').and.callThrough();
spyOn(facade, 'setPrimaryFilters').and.callThrough();
spyOn(facade, 'getPrimaryFilters$').and.returnValue(of(primaryFiltersMock));
spyOn(facade, 'getPrimaryFilters$').and.returnValue(
of(primaryFiltersMock)
);
await facade.setPrimaryFilters(primaryFilterOption, id);
const updatedFilters = [primaryFilterOption, primaryFiltersMock[1]];
expect(store.dispatch).toHaveBeenCalledWith(actions.setPrimaryFilters({ filters: updatedFilters, id }));
expect(store.dispatch).toHaveBeenCalledWith(
actions.setPrimaryFilters({ filters: updatedFilters, id })
);
});
});
@@ -101,8 +115,12 @@ fdescribe('SearchFacade', () => {
selected: false,
},
]);
spyOnProperty(facade, 'selectFilters$', 'get').and.returnValue(currentFilters);
spyOnProperty(facade, 'primaryFilters$', 'get').and.returnValue(primaryFilters);
spyOnProperty(facade, 'selectFilters$', 'get').and.returnValue(
currentFilters
);
spyOnProperty(facade, 'primaryFilters$', 'get').and.returnValue(
primaryFilters
);
const result = await facade.selectedFilter$.pipe(first()).toPromise();
@@ -123,26 +141,40 @@ fdescribe('SearchFacade', () => {
});
it('should return true if the select filters are changed', async () => {
spyOnProperty(facade, 'primaryFilters$', 'get').and.returnValue(of(primaryFilters));
spyOnProperty(facade, 'primaryFilters$', 'get').and.returnValue(
of(primaryFilters)
);
const updatedSelectFilters = [
...selectFilters.map((filter) =>
filter.options
? {
...filter,
options: filter.options.map((o, i) => (!i ? { ...o, selected: !o.initial_selected_state } : o)),
options: filter.options.map((o, i) =>
!i ? { ...o, selected: !o.initial_selected_state } : o
),
}
: filter
),
];
spyOnProperty(facade, 'selectFilters$', 'get').and.returnValue(of(updatedSelectFilters));
const result = await facade.filtersChangedFromDefault$.pipe(first()).toPromise();
spyOnProperty(facade, 'selectFilters$', 'get').and.returnValue(
of(updatedSelectFilters)
);
const result = await facade.filtersChangedFromDefault$
.pipe(first())
.toPromise();
expect(result).toBe(true);
});
it('should return false if the select filters are unchanged', async () => {
spyOnProperty(facade, 'primaryFilters$', 'get').and.returnValue(of(primaryFilters));
spyOnProperty(facade, 'selectFilters$', 'get').and.returnValue(of(selectFilters));
const result = await facade.filtersChangedFromDefault$.pipe(first()).toPromise();
spyOnProperty(facade, 'primaryFilters$', 'get').and.returnValue(
of(primaryFilters)
);
spyOnProperty(facade, 'selectFilters$', 'get').and.returnValue(
of(selectFilters)
);
const result = await facade.filtersChangedFromDefault$
.pipe(first())
.toPromise();
expect(result).toBe(false);
});
@@ -152,8 +184,16 @@ fdescribe('SearchFacade', () => {
const primaryFilterChangedFromDefault$ = hot('--a', { a: false });
const expected = cold('--b', { b: false });
spyOnProperty(facade, 'selectedFilterChangedFromDefault$', 'get').and.returnValue(selectedFilterChangedFromDefault$);
spyOnProperty(facade, 'primaryFilterChangedFromDefault$', 'get').and.returnValue(primaryFilterChangedFromDefault$);
spyOnProperty(
facade,
'selectedFilterChangedFromDefault$',
'get'
).and.returnValue(selectedFilterChangedFromDefault$);
spyOnProperty(
facade,
'primaryFilterChangedFromDefault$',
'get'
).and.returnValue(primaryFilterChangedFromDefault$);
expect(facade.filtersChangedFromDefault$).toBeObservable(expected);
});
@@ -165,8 +205,12 @@ fdescribe('SearchFacade', () => {
});
const expected = cold('--b', { b: true });
spyOnProperty(facade, 'selectFilters$', 'get').and.returnValue(selectFilters$);
spyOnProperty(facade, 'primaryFilters$', 'get').and.returnValue(primaryFilters$);
spyOnProperty(facade, 'selectFilters$', 'get').and.returnValue(
selectFilters$
);
spyOnProperty(facade, 'primaryFilters$', 'get').and.returnValue(
primaryFilters$
);
expect(facade.filtersChangedFromDefault$).toBeObservable(expected);
});
@@ -184,7 +228,10 @@ fdescribe('SearchFacade', () => {
await facade.pendingInput$.pipe(first()).toPromise();
expect(ngxsStore.select).toHaveBeenCalled();
expect(store.select).toHaveBeenCalledWith(selectors.selectPendingInput, 123);
expect(store.select).toHaveBeenCalledWith(
selectors.selectPendingInput,
123
);
});
it('should return the pending input for the provided id', () => {
@@ -201,10 +248,7 @@ fdescribe('SearchFacade', () => {
});
describe('setPendingInput', () => {
const id = 123;
beforeEach(() => {
spyOn(facade, 'getProcessId').and.returnValue(new Promise((resolve) => resolve(id)));
spyOn(facade, 'setPendingInput').and.callThrough();
spyOn(store, 'dispatch').and.callThrough();
});
@@ -213,14 +257,43 @@ fdescribe('SearchFacade', () => {
await facade.setPendingInput('Input Without Id');
expect(facade.getProcessId).toHaveBeenCalled();
expect(store.dispatch).toHaveBeenCalledWith(actions.setPendingInput({ id, input: pendingInput }));
expect(store.dispatch).toHaveBeenCalledWith(
actions.setPendingInput({ id, input: pendingInput })
);
});
it('should dispach set input to the store', async () => {
const pendingInput = 'Input With Id';
await facade.setPendingInput(pendingInput, id);
expect(facade.getProcessId).not.toHaveBeenCalled();
expect(store.dispatch).toHaveBeenCalledWith(actions.setPendingInput({ id, input: pendingInput }));
expect(store.dispatch).toHaveBeenCalledWith(
actions.setPendingInput({ id, input: pendingInput })
);
});
});
describe('cancelSearch', () => {
let sub: Subject<void>;
beforeEach(() => {
sub = new Subject<void>();
spyOn(sub, 'complete').and.callThrough();
spyOn(facade['shouldCancelSearchRequestMap'], 'get').and.returnValue(sub);
});
it('should get the current process id and get the respective cancellation subject and complete it', async () => {
await facade.cancelSearch();
expect(
facade['shouldCancelSearchRequestMap']['get']
).toHaveBeenCalledWith(id);
expect(sub.complete).toHaveBeenCalled();
});
it('should get the cancellation subject for the provided id and complete it', async () => {
await facade.cancelSearch(id);
expect(
facade['shouldCancelSearchRequestMap']['get']
).toHaveBeenCalledWith(id);
expect(sub.complete).toHaveBeenCalled();
});
});
});

View File

@@ -164,7 +164,10 @@ export class SearchStateFacade {
);
}
shouldCancelSearchRequestMap = new Map<number, Subject<void>>();
protected readonly shouldCancelSearchRequestMap = new Map<
number,
Subject<void>
>();
constructor(private store: Store<any>, private ngxsStore: NgxsStore) {}