diff --git a/src/combineReducers.js b/src/combineReducers.js index 36250e8d94..8fe9fbb428 100644 --- a/src/combineReducers.js +++ b/src/combineReducers.js @@ -108,6 +108,10 @@ export default function combineReducers(reducers) { if (process.env.NODE_ENV !== 'production') { if (typeof reducers[key] === 'undefined') { warning(`No reducer provided for key "${key}"`) + } else if (key === '__esModule') { + warning(`Passing a whole ES Module to combine reducers is discouraged.`) + } else if (typeof reducers[key] !== 'function') { + warning(`Reducer provided for "${key}" is not a function. Received type: ${typeof key}.`) } } diff --git a/test/combineReducers.spec.js b/test/combineReducers.spec.js index 2fd1ab1eab..c338a1ac95 100644 --- a/test/combineReducers.spec.js +++ b/test/combineReducers.spec.js @@ -1,6 +1,7 @@ /* eslint-disable no-console */ import { combineReducers } from '../src' import createStore, { ActionTypes } from '../src/createStore' +import * as reducers from './helpers/reducers' describe('Utils', () => { describe('combineReducers', () => { @@ -18,7 +19,11 @@ describe('Utils', () => { expect(s2).toEqual({ counter: 1, stack: [ 'a' ] }) }) - it('ignores all props which are not a function', () => { + it('warns on props which are not a function and excludes them', () => { + const preSpy = console.error + const spy = jest.fn() + console.error = spy + const reducer = combineReducers({ fake: true, broken: 'string', @@ -26,9 +31,27 @@ describe('Utils', () => { stack: (state = []) => state }) + expect(spy).toHaveBeenLastCalledWith('Reducer provided for "another" is not a function. Received type: string.') + expect(spy).toHaveBeenCalledTimes(3) + expect( Object.keys(reducer({ }, { type: 'push' })) ).toEqual([ 'stack' ]) + + spy.mockClear() + console.error = preSpy + }) + + it('warns if a module imported with * syntax is passed', () => { + const preSpy = console.error + const spy = jest.fn() + console.error = spy + + combineReducers(reducers) + expect(spy).toHaveBeenCalledWith(`Passing a whole ES Module to combine reducers is discouraged.`) + + spy.mockClear() + console.error = preSpy }) it('warns if a reducer prop is undefined', () => { @@ -38,15 +61,11 @@ describe('Utils', () => { let isNotDefined combineReducers({ isNotDefined }) - expect(spy.mock.calls[0][0]).toMatch( - /No reducer provided for key "isNotDefined"/ - ) + expect(spy).toHaveBeenCalledWith('No reducer provided for key "isNotDefined"') spy.mockClear() combineReducers({ thing: undefined }) - expect(spy.mock.calls[0][0]).toMatch( - /No reducer provided for key "thing"/ - ) + expect(spy).toHaveBeenCalledWith('No reducer provided for key "thing"') spy.mockClear() console.error = preSpy diff --git a/test/createStore.spec.js b/test/createStore.spec.js index 90e60b73b2..297ce10e0e 100644 --- a/test/createStore.spec.js +++ b/test/createStore.spec.js @@ -6,7 +6,7 @@ import $$observable from 'symbol-observable' describe('createStore', () => { it('exposes the public API', () => { - const store = createStore(combineReducers(reducers)) + const store = createStore(reducers.todos) const methods = Object.keys(store) expect(methods.length).toBe(4)