Skip to content

Commit

Permalink
WIP check the type of the state
Browse files Browse the repository at this point in the history
  • Loading branch information
borkxs committed Jun 15, 2016
1 parent 7b3315e commit f082616
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 22 deletions.
34 changes: 25 additions & 9 deletions src/combineReducers.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,16 @@ function getUndefinedStateErrorMessage(key, action) {
)
}

function getInputStateTypeString(inputState) {
var inputMatch = inputState.toString().match(/(.*?)\s/)
var typeName = typeof inputState
return typeName === 'object'
? isPlainObject(inputState)
? ({}).toString.call(inputState).match(/\s([a-z|A-Z]+)/)[1]
: inputMatch && inputMatch[0]
: typeName
}

function getUnexpectedStateShapeWarningMessage(inputState, reducers, action) {
var reducerKeys = Object.keys(reducers)
var argumentName = action && action.type === ActionTypes.INIT ?
Expand All @@ -25,14 +35,14 @@ function getUnexpectedStateShapeWarningMessage(inputState, reducers, action) {
)
}

if (!isPlainObject(inputState)) {
return (
`The ${argumentName} has unexpected type of "` +
({}).toString.call(inputState).match(/\s([a-z|A-Z]+)/)[1] +
`". Expected argument to be an object with the following ` +
`keys: "${reducerKeys.join('", "')}"`
)
}
// if (!isPlainObject(inputState)) {
// return (
// `The ${argumentName} has unexpected type of "` +
// ({}).toString.call(inputState).match(/\s([a-z|A-Z]+)/)[1] +
// `". Expected argument to be an object with the following ` +
// `keys: "${reducerKeys.join('", "')}"`
// )
// }

var unexpectedKeys = Object.keys(inputState).filter(key => !reducers.hasOwnProperty(key))

Expand Down Expand Up @@ -105,7 +115,7 @@ export default function combineReducers(reducers, options = {
set: (state, key, stateForKey) => {
state[key] = stateForKey
return state
},
}
}) {
var reducerKeys = Object.keys(reducers)
var finalReducers = {}
Expand All @@ -125,12 +135,18 @@ export default function combineReducers(reducers, options = {
sanityError = e
}

var initialTypeString = getInputStateTypeString(options.create({}))

return function combination(state = options.create({}), action) {
if (sanityError) {
throw sanityError
}

if (process.env.NODE_ENV !== 'production') {
var currentStateTypeString = getInputStateTypeString(state)
if (initialTypeString !== currentStateTypeString) {
throw new Error(`The state has type ${currentStateTypeString} but was expecting ${initialTypeString}`)
}
var warningMessage = getUnexpectedStateShapeWarningMessage(state, finalReducers, action)
if (warningMessage) {
warning(warningMessage)
Expand Down
31 changes: 18 additions & 13 deletions test/combineReducers.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,15 @@ describe('Utils', () => {
const s1 = reducer(undefined, { type: 'increment' })
expect(s1.get('counter')).toEqual(1)
expect(s1.get('stack')).toEqual([])

const s2 = reducer(s1, { type: 'push', value: 'a' })
expect(s2.get("counter")).toEqual(1)
expect(s2.get("stack")).toEqual([ 'a' ])
expect(s2.get('counter')).toEqual(1)
expect(s2.get('stack')).toEqual([ 'a' ])

// when we supply a previousState with a different type we get a warning
// expect(
// () => reducer({ stack: [], counter: 0 }, { type: 'push', value: 'b' })
// ).toThrow(/The state has type Object but was expecting Map/)
})

it('ignores all props which are not a function', () => {
Expand Down Expand Up @@ -215,9 +221,6 @@ describe('Utils', () => {
}
})

reducer()
expect(spy.calls.length).toBe(0)

reducer({ foo: { bar: 2 } })
expect(spy.calls.length).toBe(0)

Expand All @@ -237,24 +240,26 @@ describe('Utils', () => {
/Unexpected keys "bar", "qux".*createStore.*instead: "foo", "baz"/
)

createStore(reducer, 1)
expect(spy.calls[2].arguments[0]).toMatch(
/createStore has unexpected type of "Number".*keys: "foo", "baz"/
expect(
() => createStore(reducer, 1)
).toThrow(
/The state has type number but was expecting Object/
)

reducer({ bar: 2 })
expect(spy.calls[3].arguments[0]).toMatch(
expect(spy.calls[2].arguments[0]).toMatch(
/Unexpected key "bar".*reducer.*instead: "foo", "baz"/
)

reducer({ bar: 2, qux: 4 })
expect(spy.calls[4].arguments[0]).toMatch(
expect(spy.calls[3].arguments[0]).toMatch(
/Unexpected keys "bar", "qux".*reducer.*instead: "foo", "baz"/
)

reducer(1)
expect(spy.calls[5].arguments[0]).toMatch(
/reducer has unexpected type of "Number".*keys: "foo", "baz"/
expect(
() => reducer(1)
).toThrow(
/The state has type number but was expecting Object/
)

spy.restore()
Expand Down

0 comments on commit f082616

Please sign in to comment.