Skip to content

Instantly share code, notes, and snippets.

@tlaitinen
Last active May 25, 2018 12:36
Show Gist options
  • Select an option

  • Save tlaitinen/a84bb04f1b165657b12db734d6dd75ac to your computer and use it in GitHub Desktop.

Select an option

Save tlaitinen/a84bb04f1b165657b12db734d6dd75ac to your computer and use it in GitHub Desktop.
import * as crud from './crud';
import configureMockStore from 'redux-mock-store';
import createSagaMiddleware from 'redux-saga';
import {select, put, call} from 'redux-saga/effects';
interface Dummy {
id: string;
name: string;
}
interface Query {
offset: number;
}
type State = crud.State<Dummy, Query>;
type Action = crud.Action<'dummy', Dummy, Query>;
interface RootState {
dummy: State;
}
async function fetchFunc1(_query:Query):Promise<Dummy[]> {
return [{id:'1',name:'dummy1'},{id:'2',name:'dummy2'}];
}
async function fetchFunc2(_query:Query):Promise<Dummy[]> {
throw new Error('fetch failed');
}
async function putFunc(dummyId:string, dummy:Dummy):Promise<Dummy> {
return {
id: dummyId,
...dummy,
name: 'modified1'
};
}
async function putFuncFail():Promise<null> {
throw new Error('put failed');
}
async function postFunc1(dummy:Dummy):Promise<Dummy> {
return {
id: '1',
...dummy
};
}
async function postFunc2(_dummy:Dummy):Promise<Dummy> {
return {
id: '1',
name: 'modified2'
};
}
async function postFunc3(_dummy:Dummy):Promise<Dummy> {
throw new Error('post failed');
}
const getId = (d:Dummy) => d.id;
const getState = (state:RootState) => state.dummy;
// const selectors = crud.mkSelectors<Dummy, Query, RootState>(getState);
const actions1 = crud.mkActions<'dummy', Dummy, Query, RootState>({
crud:'dummy',
getId,
getState,
fetchFunc: fetchFunc1,
putFunc: putFunc,
postFunc: postFunc1
});
const actions2 = crud.mkActions<'dummy', Dummy, Query, RootState>({
crud:'dummy',
getId,
getState,
fetchFunc: fetchFunc2,
putFunc: putFunc,
postFunc: postFunc2
});
const actions3 = crud.mkActions<'dummy', Dummy, Query, RootState>({
crud:'dummy',
getId,
getState,
fetchFunc: fetchFunc1,
putFunc: putFuncFail,
postFunc: postFunc3
});
const defRootState:RootState = {
dummy: crud.mkDefState<Dummy, Query>()
};
const sagaMiddleware = createSagaMiddleware();
const middlewares = [sagaMiddleware];
const mockStore = configureMockStore(middlewares);
describe('frontend:crud:actions', () => {
it('should create correct actions when fetchFunc succeeds', async () => {
const gen = actions1.fetchResults('query1', {offset:3});
expect(gen.next().value).toEqual(put(actions1.setResults('query1', {
results: [],
loading: true
})));
expect(gen.next().value).toEqual(put(actions1.setQuery('query1', {
offset: 3
})));
expect(gen.next().value).toEqual(call(fetchFunc1, {offset:3}));
const r = await fetchFunc1({offset:3});
expect(gen.next(r).value).toEqual(put(actions1.setEntities({
'1': {
id: '1',
name: 'dummy1'
},
'2': {
id: '2',
name: 'dummy2'
}
})));
expect(gen.next().value).toEqual(put(actions1.setResults('query1', {
results: ['1','2'],
loading: false
})));
});
it('it should create correct actions and an exception when fetchFunc fails', async () => {
const store = mockStore(defRootState);
await sagaMiddleware.run(() => actions2.fetchResults('query1', {offset:3}));
expect(store.getActions()).toEqual([
actions1.setResults('query1', {
results: [],
loading: true
}),
actions1.setQuery('query1', {
offset: 3
}),
actions1.setResults('query1', {
results: [],
loading: false,
error: 'fetch failed'
})
]);
});
it('should create setStatus and setEntities actions when put succeeds and returns entity', async () => {
const d:Dummy = {
id: '1',
name: 'to overwrite'
};
const gen = actions1.put('1', d);
expect(gen.next().value).toEqual(put(actions1.setStatus('1', {busy:true})));
expect(gen.next().value).toEqual(call(putFunc, '1', d));
const r = await putFunc('1', d);
expect(gen.next(r).value).toEqual(put(actions1.setEntities({
'1': {
id: '1',
name: 'modified1'
}
})));
expect(gen.next().value).toEqual(put(actions1.setStatus('1', {busy:false})));
});
it('should create setStatus actions and throw exception when put fails', async () => {
const store = mockStore(defRootState);
await sagaMiddleware.run(() => actions3.put('1', {
id: '1',
name: 'dummy1'
}));
expect(store.getActions()).toEqual([
actions1.setStatus('1', {busy:true}),
actions1.setStatus('1', {busy:false, error: 'put failed'})
]);
});
it('should create setEntities action when post succeeds and returns an entity', async () => {
const d:Dummy = {
id: '',
name: 'dummy'
};
const gen = actions2.post(d);
expect(gen.next().value).toEqual(select(getState));
expect(gen.next(getState(defRootState)).value).toEqual(call(postFunc2, d));
const r = await postFunc2(d);
expect(gen.next(r).value).toEqual(put(actions2.setEntities({[getId(r)]:r})));
});
it('should throw an exception when post fails', async () => {
const store = mockStore(defRootState);
await sagaMiddleware.run(() => actions3.post({
id: '',
name: 'dummy'
}));
expect(store.getActions()).toEqual([actions3.setPostError('post failed')]);
});
});
describe('frontend:crud:reducer', () => {
const reducer = crud.mkReducer<'dummy', Dummy, Query>('dummy');
const defState = defRootState.dummy;
it('should return the initial state', () => {
expect(reducer(undefined, <Action> {})).toEqual({
queries: {},
results: {},
entities: {},
entityStatus: {}
});
});
it('should handle CRUD_SET_QUERY', () => {
expect(reducer(defState, actions1.setQuery('query1', {offset: 3}))).toEqual({
...defState,
queries: {
query1: {
offset: 3
}
}
});
});
it('should handle CRUD_SET_RESULTS', () => {
expect(reducer(defState, actions1.setResults('query1', {
results: ['1', '2'],
loading: false
}))).toEqual({
...defState,
results: {
query1: {
results: ['1', '2'],
loading: false
}
}
});
});
it('should handle CRUD_SET_ENTITIES', () => {
expect(reducer(
{
...defState,
entities: {
'1': {
id: '1',
name: 'old dummy1'
},
'3': {
id: '3',
name: 'dummy3'
}
}
}, actions1.setEntities({
'1': {
id: '1',
name: 'dummy1'
},
'2': {
id: '2',
name: 'dummy2'
}
})
)).toEqual({
...defState,
entities: {
'1': {
id: '1',
name: 'dummy1'
},
'2': {
id: '2',
name: 'dummy2'
},
'3': {
id: '3',
name: 'dummy3'
}
}
});
});
it('should handle CRUD_SET_STATUS', () => {
expect(reducer(
{
...defState,
entityStatus: {
'1': {busy:true},
'3': {busy:false}
}
}, actions1.setStatus('3', {busy:true})
)).toEqual({
...defState,
entityStatus: {
'1': {busy:true},
'3': {busy:true}
}
});
});
});
describe('frontend:crud:selectors', () => {
const selectors = crud.mkSelectors<Dummy, Query, RootState>(getState);
const testState:RootState = {
dummy: {
queries: {
query1: {
offset: 3
}
},
results: {
query1: {
results: ['1', '2'],
loading: false
}
},
entities: {
'1': {
id: '1',
name: 'dummy1'
},
'2': {
id: '2',
name: 'dummy2'
}
},
entityStatus: {
'1': {busy:true},
'2': {busy:false}
}
}
};
it('should select query', () => {
expect(selectors.querySelector(testState, 'query1')).toEqual({
offset: 3
});
});
it('should select results', () => {
expect(selectors.resultsSelector(testState, 'query1')).toEqual({
results: ['1', '2'],
loading: false
});
});
it('should select entity status', () => {
expect(selectors.statusSelector(testState)).toEqual({
'1': {busy:true},
'2': {busy:false}
});
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment