Last active
May 25, 2018 12:36
-
-
Save tlaitinen/a84bb04f1b165657b12db734d6dd75ac to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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