Sfoglia il codice sorgente

added tests for demo_module

Tomi Cvetic 8 anni fa
parent
commit
f1b0ce3ba9
2 ha cambiato i file con 99 aggiunte e 22 eliminazioni
  1. 27 22
      src/demo_module/state.js
  2. 72 0
      src/demo_module/state.test.js

+ 27 - 22
src/demo_module/state.js

@@ -18,15 +18,15 @@
  * reference), but creates a modified state and returns it. Also, the reducer
  * should return immediately. If an asynchronous operation has to be executed,
  * it is handled through redux-sagas, which is basically a concurrent process
- * triggered by a reducer, which generates a new action once it's completed. 
+ * triggered by a reducer, which generates a new action once it's completed.
  *
- * Action creators (defined as 'actions' in this file) are bound to the 
+ * Action creators (defined as 'actions' in this file) are bound to the
  * Redux dispatcher in 'index.js'. When they are called, the dispatcher sends
  * the greated action to **all** reducers. This module, and other modules which
- * change the state of this module, use these action creators to modifiy the 
+ * change the state of this module, use these action creators to modifiy the
  * state.
  *
- * This file exports everything which is necessary to set up and operate the 
+ * This file exports everything which is necessary to set up and operate the
  * state of this module.
  * - actions
  * - reducers
@@ -34,27 +34,29 @@
  * - sagas
  **/
 
-
 /* Import NAME and DATA to be able to create action creators */
 import { NAME, DATA } from './constants'
 import primary from './initialData'
 import { delay } from 'redux-saga'
 import { call, put, takeEvery } from 'redux-saga/effects'
 
-
 /** actionTypes define what actions are handeled by the reducer. */
 const actionTypes = {}
 export const actionCreators = {}
 
 // Generate default actionsTypes CREATE, UPDATE, REMOVE
-DATA.forEach(dataItem => {
+DATA.forEach((dataItem, idx) => {
   ['create', 'update', 'remove'].forEach(action => {
     // The Redux convention is to name action types e.g. demo_module/UPDATE
     // For action creators, we define here the name e.g. removePrimary(id, data)
     // where id is the element id and data is the element itself.
     const actionType = `${action.toUpperCase()}_${dataItem.toUpperCase()}`
-    const actionName = `${action}${dataItem[0].toUpperCase()}${dataItem[0].substring(1)}`
-    actionCreators[actionName] = (id, data) => { return { type: actionType, id, data } }
+    const actionName = `${action}${dataItem[0].toUpperCase()}${dataItem.substring(1)}`
+    if (idx === 0) {
+      actionCreators[actionName] = (id, data) => { return { type: `${NAME}/${actionType}`, id, data } }
+    } else {
+      actionCreators[actionName] = (primaryId, id, data) => { return { type: `${NAME}/${actionType}`, primaryId, id, data } }
+    }
     actionTypes[actionType] = `${NAME}/${actionType}`
   })
 })
@@ -66,7 +68,7 @@ updateSecondary1 (id, data) {
   // id is used to find the secondary item. data is the new value of the item.
 }
 removeSecondary2 (id, data) {
-  // id is used to find the secondary item. 
+  // id is used to find the secondary item.
 }
 */
 
@@ -75,13 +77,11 @@ actionCreators['loadSamples'] = () => { return { type: `${NAME}/LOAD_SAMPLES` }
 actionTypes['LOAD_SAMPLES'] = `${NAME}/LOAD_SAMPLES`
 console.log('State actionTypes, actionCreators', actionTypes, actionCreators)
 
-
 /** state definition */
 /** It is generally easier to not have another object here. */
 export const state = []
 console.log('State state', state)
 
-
 /** reducer is called by the redux dispatcher and handles all component actions */
 /** The module's root reducer tries to split up the state and let specific
   * specialized reducers handle the work. This is not necessary, but more
@@ -89,22 +89,24 @@ console.log('State state', state)
   // In this example it is assumed, that the secondary items have a key 'primaryId',
   // which references the primary data element.
 export function reducer (state = [], action) {
+  // Return immediately, if action type doesn't match
+  if (typeof action.type === 'undefined' || !action.type.match(RegExp(NAME))) {
+    return state
+  }
   // find the primary data element with the matching id.
-  const idx = state.findIndex(elem => {return (action.id === elem.id)})
-  console.log(`Entering demo_module root reducer.`, state, action, idx)
+  let idx
   let nextState
+  console.log(`Entering demo_module root reducer.`, state, action, idx)
   if (action.type.match(/SECONDARY/)) {
+    idx = state.findIndex(elem => { return (action.primaryId === elem.id) })
     // leave immediately, if no idx was found (operations on secondary not valid if there's no primary)
     if (idx < 0) {
       return state
     }
     console.log(`Using secondary reducer.`)
-    const subState = state.secondary
+    const subState = state[idx].secondary
     const reducedState = secondaryReducer(subState, action)
-    nextState = {
-      ...state,
-      secondary: reducedState
-    }
+    nextState = [ ...state.slice(0, idx), {...state[idx], secondary: reducedState}, ...state.slice(idx + 1) ]
     console.log('Leaving demo_module root reducer', subState, reducedState, nextState)
     return nextState
   }
@@ -119,7 +121,7 @@ export function reducer (state = [], action) {
       return nextState
     case actionTypes.REMOVE_PRIMARY:
       console.log('wtf')
-      nextState = [ ...state.slice(0, idx), ...state.slice(idx+1) ]
+      nextState = [ ...state.slice(0, idx), ...state.slice(idx + 1) ]
       console.log('Removing primary', state, nextState)
       return nextState
     case actionTypes.LOAD_SAMPLES:
@@ -133,7 +135,10 @@ export function reducer (state = [], action) {
 
 function secondaryReducer (state = [], action) {
   console.log(`Entering secondary reducer.`, state, action)
-  const idx = state.findIndex(elem => {return (action.id === elem.id)})
+  const idx = state.findIndex(elem => {
+    console.log('searching index', action, elem)
+    return (action.id === elem.id)
+  })
   let nextState
   switch (action.type) {
     case actionTypes.CREATE_SECONDARY:
@@ -141,7 +146,7 @@ function secondaryReducer (state = [], action) {
       console.log(`Creating secondary.`, state, nextState)
       return nextState
     case actionTypes.UPDATE_SECONDARY:
-      nextState = [ ...state.slice(0, idx), action.data, ...state.slice(idx + 1)]
+      nextState = [ ...state.slice(0, idx), action.data, ...state.slice(idx + 1) ]
       console.log(`Updating secondary.`, idx, state, nextState)
       return nextState
     case actionTypes.REMOVE_SECONDARY:

+ 72 - 0
src/demo_module/state.test.js

@@ -0,0 +1,72 @@
+import {actionCreators, state, reducer} from './state.js'
+console.log(actionCreators)
+
+describe('actions', () => {
+  describe('primary actions', () => {
+    it('creates primary actions', () => {
+      const id = 'primary-1'
+      const data = {id: 'primary-1', name: 'some name', secondary: []}
+      expect(actionCreators.createPrimary(id, data)).toEqual({type: 'demo_module/CREATE_PRIMARY', id, data})
+      expect(actionCreators.updatePrimary(id, data)).toEqual({type: 'demo_module/UPDATE_PRIMARY', id, data})
+      expect(actionCreators.removePrimary(id, data)).toEqual({type: 'demo_module/REMOVE_PRIMARY', id, data})
+    })
+  })
+  describe('secondary actions', () => {
+    it('creates secondary actions', () => {
+      const primaryId = 'primary-1'
+      const id = 'secondary-1'
+      const data = {id: 'secondary-1', name: 'some name'}
+      expect(actionCreators.createSecondary(primaryId, id, data)).toEqual({type: 'demo_module/CREATE_SECONDARY', primaryId, id, data})
+      expect(actionCreators.updateSecondary(primaryId, id, data)).toEqual({type: 'demo_module/UPDATE_SECONDARY', primaryId, id, data})
+      expect(actionCreators.removeSecondary(primaryId, id, data)).toEqual({type: 'demo_module/REMOVE_SECONDARY', primaryId, id, data})
+    })
+  })
+  describe('additional actions', () => {
+    it('creates additional actions', () => {
+      const data = {id: 'primary-1', name: 'some name', secondary: []}
+      expect(actionCreators.loadSamples()).toEqual({type: 'demo_module/LOAD_SAMPLES'})
+    })
+  })
+})
+describe('state', () => {
+  it('default state is empty array', () => {
+    const defaultState = []
+    expect(state).toEqual([])
+  })
+})
+
+describe('reducer', () => {
+  const initialSecondary = { id: 'secondary-1', name: 'Secondary 1' }
+  const otherSecondary = { id: 'secondary-2', name: 'Secondary 2' }
+  const initialPrimary = { id: 'primary-1', name: 'Primary 1', active: true, secondary: [initialSecondary]}
+  const initialState = [initialPrimary]
+  const otherPrimary = { id: 'primary-2', name: 'Primary 2', active: false, secondary: [otherSecondary]}
+
+  describe('initialization', () => {
+    it('returns the initial state', () => {
+      expect(reducer(undefined, {})).toEqual([])
+    })
+  })
+  describe('primary reduction', () => {
+    it('handles CREATE_PRIMARY', () => {
+      expect(reducer(initialState, {type: 'demo_module/CREATE_PRIMARY', id: otherPrimary.id, data: otherPrimary})).toEqual([initialPrimary, otherPrimary])
+    })
+    it('handles UPDATE_PRIMARY', () => {
+      expect(reducer(initialState, {type: 'demo_module/UPDATE_PRIMARY', id: initialPrimary.id, data: otherPrimary})).toEqual([otherPrimary])
+    })
+    it('handles REMOVE_PRIMARY', () => {
+      expect(reducer(initialState, {type: 'demo_module/REMOVE_PRIMARY', id: initialPrimary.id, data: initialPrimary})).toEqual([])
+    })
+  })
+  describe('secondary reduction', () => {
+    it('handles CREATE_SECONDARY', () => {
+      expect(reducer(initialState, {type: 'demo_module/CREATE_SECONDARY', primaryid: initialPrimary.id, id: otherSecondary.id, data: otherSecondary})).toEqual([{...initialPrimary, secondary: [initialSecondary, otherSecondary]}])
+    })
+    it('handles UPDATE_SECONDARY', () => {
+      expect(reducer(initialState, {type: 'demo_module/UPDATE_SECONDARY', primaryId: initialPrimary.id, id: initialSecondary.id, data: otherSecondary})).toEqual([{...initialPrimary, secondary: [otherSecondary]}])
+    })
+    it('handles REMOVE_SECONDARY', () => {
+      expect(reducer(initialState, {type: 'demo_module/REMOVE_SECONDARY', primaryId: initialPrimary.id, id: initialSecondary.id, data: initialSecondary})).toEqual([{...initialPrimary, secondary: []}])
+    })
+  })
+})