Skip to content
This repository has been archived by the owner on Nov 9, 2019. It is now read-only.

Commit

Permalink
Improve error messages.
Browse files Browse the repository at this point in the history
Update tests.
  • Loading branch information
MrEfrem committed Nov 27, 2016
1 parent 2aa66bb commit c7fee84
Show file tree
Hide file tree
Showing 10 changed files with 59 additions and 60 deletions.
16 changes: 11 additions & 5 deletions __tests__/createReducer.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ test('Test invalid signature', () => {
expect(createReducer).toThrowError('InitialState must be plain object')
expect(createReducer.bind(this, { mountPath: 10, initialState: {} })).toThrowError('Mounting path must be string')
expect(createReducer.bind(this, { mountPath: 'ui component' })).toThrowError('InitialState must be plain object')
expect(createReducer.bind(this, { mountPath: 'ui component', initialState: Object.create({ a: 1 }) })).toThrowError('InitialState must be plain object')
expect(createReducer.bind(this, { mountPath: 'ui component', initialState: {}, listenActions: 123 })).toThrowError('ListenActions must be plain object')
expect(createReducer.bind(this, { mountPath: 'ui component', initialState: {}, listenActions: Object.create({ a: 1 }) })).toThrowError('ListenActions must be plain object')
expect(createReducer.bind(this, { mountPath: 'ui component', initialState: {}, connectToStore: null })).toThrowError('ConnectToStore must be boolean')
expect(createReducer.bind(this, { mountPath: 'ui component', initialState: {}, persist: null })).toThrowError('Persist must be boolean')
expect(createReducer.bind(this, { mountPath: 'ui component', initialState: {}, actionPrefix: 123 })).toThrowError('ActionPrefix must be non empty string')
Expand Down Expand Up @@ -629,10 +631,14 @@ test('Test signature reduxSetState', () => {
class Component extends React.Component {
componentDidMount() {
const { reduxSetState } = this.props
expect(reduxSetState).toThrowError('Action type must be non empty string')
expect(reduxSetState.bind(this, 'INCREMENT')).toThrowError('New state must be plain object or function')
expect(reduxSetState.bind(this, 'INCREMENT', 1)).toThrowError('New state must be plain object or function')
expect(reduxSetState.bind(this, 'INCREMENT', () => 123)).toThrowError('New state must be non empty plain object')
expect(reduxSetState).toThrowError('ActionType must be non empty string')
expect(reduxSetState.bind(this, 'INCREMENT')).toThrowError('NewState must be non empty plain object or function')
expect(reduxSetState.bind(this, 'INCREMENT', 1)).toThrowError('NewState must be non empty plain object or function')
expect(reduxSetState.bind(this, 'INCREMENT', {})).toThrowError('NewState must be non empty plain object or function')
expect(reduxSetState.bind(this, 'INCREMENT', Object.create({ a: 1 }))).toThrowError('NewState must be non empty plain object or function')
expect(reduxSetState.bind(this, 'INCREMENT', () => 123)).toThrowError('New state returned from function must be non empty plain object')
expect(reduxSetState.bind(this, 'INCREMENT', () => ({}))).toThrowError('New state returned from function must be non empty plain object')
expect(reduxSetState.bind(this, 'INCREMENT', () => Object.create({ a: 1 }))).toThrowError('New state returned from function must be non empty plain object')
}

render() { return null }
Expand Down Expand Up @@ -831,7 +837,7 @@ test('Test is invalid to create of reducer in partially same mounting path (2)',
<ExtendedComponent1/>
</ExtendedComponent>
</Provider>
)).toThrowError('Mounting path "ui component main" already busy')
)).toThrowError('Mounting path "ui component" already busy')
})

test('Test is valid to create of reducer after create of reducer', () => {
Expand Down
24 changes: 14 additions & 10 deletions __tests__/enhanceStore.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,23 @@ import enhanceStore from '../src/enhanceStore'
import { createStore } from 'redux'

test('Test invalid signature', () => {
expect(enhanceStore).toThrowError('Create store must be function')
expect(enhanceStore.bind(this, 123)).toThrowError('Create store must be function')
expect(createStore.bind(this, 123, enhanceStore)).toThrowError('The reducers parameter must be non empty plain object')
expect(createStore.bind(this, null, 123, enhanceStore)).toThrowError('Preloaded state must be plain object')
expect(enhanceStore).toThrowError('CreateStore must be function')
expect(enhanceStore.bind(this, 123)).toThrowError('CreateStore must be function')
expect(createStore.bind(this, 123, enhanceStore)).toThrowError('Reducers must be non empty plain object')
expect(createStore.bind(this, {}, enhanceStore)).toThrowError('Reducers must be non empty plain object')
expect(createStore.bind(this, Object.create({ a: () => ({}) }), enhanceStore)).toThrowError('Reducers must be non empty plain object')
expect(createStore.bind(this, null, 123, enhanceStore)).toThrowError('PreloadedState must be plain object')
expect(createStore.bind(this, null, Object.create({}), enhanceStore)).toThrowError('PreloadedState must be plain object')
expect(createStore.bind(this, null, null, 123)).toThrow()
expect(createStore.bind(this, { ui: 123 }, enhanceStore)).toThrowError('The values of reducers parameter must be functions')
expect(createStore.bind(this, { ui: 123 }, enhanceStore)).toThrowError('Reducers has to contain functions')
})

test('Test invalid signature registerReducers', () => {
const store = createStore(null, enhanceStore)
expect(store.registerReducers).toThrowError('The reducers parameter must be non empty plain object')
expect(store.registerReducers.bind(store, {})).toThrowError('The reducers parameter must be non empty plain object')
expect(store.registerReducers.bind(store, { ui: 123 })).toThrowError('The values of reducers parameter must be functions')
expect(store.registerReducers).toThrowError('Reducers must be non empty plain object')
expect(store.registerReducers.bind(store, {})).toThrowError('Reducers must be non empty plain object')
expect(store.registerReducers.bind(store, Object.create({ ui: () => {} }))).toThrowError('Reducers must be non empty plain object')
expect(store.registerReducers.bind(store, { ui: 123 })).toThrowError('Reducers has to contain functions')
expect(store.registerReducers.bind(store, { ui: () => {} })).toThrow()
})

Expand All @@ -37,13 +41,13 @@ test('Test registerReducers', () => {
store.registerReducers({ ' ui ': reducer1 })
expect(store.getState().ui).toBe(initialState1)
expect(store.registerReducers({ ui: reducer1 })).toBeUndefined()
expect(store.registerReducers.bind(store, { 'ui component ': reducer1 })).toThrowError('Reducer mounting path "ui" already busy')
expect(store.registerReducers.bind(store, { 'ui component ': reducer1 })).toThrowError('Mounting path "ui" already busy')
store.dispatch({ type: 'UPDATE-COMPONENT', text: 'My first updated todo 1' })
expect(JSON.stringify(store.getState())).toBe('{\"ui\":{\"text\":\"My first updated todo 1\"}}')

store.registerReducers({ ' todo list ': reducer2 })
expect(store.registerReducers({ 'todo list': reducer2 })).toBeUndefined()
expect(store.registerReducers.bind(store, { 'todo ': reducer2 })).toThrowError('Reducer mounting path "todo list" already busy')
expect(store.registerReducers.bind(store, { 'todo ': reducer2 })).toThrowError('Mounting path "todo" already busy')
expect(store.registerReducers({ 'todo list1': () => ({}) })).toBeUndefined()

expect(JSON.stringify(store.getState())).toBe('{\"ui\":{\"text\":\"My first updated todo 1\"},\"todo\":{\"list\":{\"text\":\"My second todo\"},\"list1\":{}}}')
Expand Down
1 change: 1 addition & 0 deletions __tests__/getState.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ test('Test invalid signature', () => {
expect(getState.bind(this, '')).toThrowError('Mounting path must be non empty string')
expect(getState('ui component')).toThrowError('State must be plain object')
expect(getState('ui component').bind(this, 123)).toThrowError('State must be plain object')
expect(getState('ui component').bind(this, Object.create({ a: 1 }))).toThrowError('State must be plain object')
})

test('Test valid signature', () => {
Expand Down
2 changes: 1 addition & 1 deletion __tests__/registerReducers.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ test('Test is invalid to register of reducer in partially same mounting path (2)
<ExtendedComponent1/>
</ExtendedComponent>
</Provider>
)).toThrowError('Mounting path "ui component main" already busy')
)).toThrowError('Mounting path "ui component" already busy')
})

test('Test is valid to register of reducer after register of reducer', () => {
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "redux-fly",
"version": "0.0.2",
"description": "Simple redux",
"version": "0.0.3",
"description": "Reduce Redux boilerplate",
"browser": "dist/redux-fly.js",
"main": "lib/index.js",
"module": "es/index.js",
Expand Down
6 changes: 1 addition & 5 deletions src/createReducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -143,11 +143,7 @@ export default ({
}
const _mountPath = normalizeMountPath(`${propMountPath || ''} ${mountPath || ''}`)


if (this.reduxMountPaths.some(path =>
((path.indexOf(_mountPath) === 0 && !((path.substr(_mountPath.length)[0] || '').trim())) ||
(_mountPath.indexOf(path) === 0 && !((_mountPath.substr(path.length)[0] || '').trim()))))
) {
if (this.reduxMountPaths.some(path => path === _mountPath)) {
throw new Error(`Mounting path "${_mountPath}" already busy`)
}
if (this.lastReduxMountPath && _mountPath.indexOf(this.lastReduxMountPath) === -1) {
Expand Down
36 changes: 18 additions & 18 deletions src/enhanceStore.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,29 +22,29 @@ import { normalizeMountPath } from './utils/normalize'
*/
const enhanceStore = (createStore: Function) => {
if (typeof createStore !== 'function') {
throw new Error('Create store must be function')
throw new Error('CreateStore must be function')
}
return (reducer?: Object, preloadedState?: Object, enhancer?: Function) => {
let store
let reducers = {}
let combinedReducers = {}
let rawReducers = {}
let rawReducersMap = []

if (preloadedState && !isPlainObject(preloadedState)) {
throw new Error('Preloaded state must be plain object')
throw new Error('PreloadedState must be plain object')
}

// Create store with middleware for process batch actions
if (reducer) {
registerReducers(reducer)
store = createStore(reducers, preloadedState, enhancer)
store = createStore(combinedReducers, preloadedState, enhancer)
} else {
store = createStore(() => ({}), undefined, enhancer)
}

// Recreate reducers tree and replace them in store
function recreateReducers () {
reducers = {}
combinedReducers = {}
function recreate(node) {
if (isPlainObject(node)) {
const newReducers = {}
Expand All @@ -59,9 +59,9 @@ const enhanceStore = (createStore: Function) => {
return node
}
}
reducers = recreate(rawReducers)
combinedReducers = recreate(rawReducers)
if (store) {
store.replaceReducer(reducers)
store.replaceReducer(combinedReducers)
}
}

Expand All @@ -72,27 +72,27 @@ const enhanceStore = (createStore: Function) => {

/**
* Add reducers in store
* @param {Object} newReducers
* @param {Object} reducers
* @return {void}
*/
function registerReducers(newReducers: Object) {
if (!isPlainObject(newReducers) || Object.keys(newReducers).length === 0) {
throw new Error('The reducers parameter must be non empty plain object')
function registerReducers(reducers: Object) {
if (!isPlainObject(reducers) || Object.keys(reducers).length === 0) {
throw new Error('Reducers must be non empty plain object')
}
Object.keys(newReducers).forEach(key => {
if (typeof newReducers[key] !== 'function') {
throw new Error('The values of reducers parameter must be functions')
Object.keys(reducers).forEach(key => {
if (typeof reducers[key] !== 'function') {
throw new Error('Reducers has to contain functions')
}
})

Object.keys(newReducers).forEach(key => {
Object.keys(reducers).forEach(key => {
const normalizedKey = normalizeMountPath(key)
rawReducersMap.forEach(key1 => {
if (((normalizedKey.indexOf(key1) === 0 && !((normalizedKey.substr(key1.length)[0] || '').trim())) ||
(key1.indexOf(normalizedKey) === 0 && !((key1.substr(normalizedKey.length)[0] || '').trim())))
&& key1 !== normalizedKey
) {
throw new Error(`Reducer mounting path "${key1}" already busy`)
throw new Error(`Mounting path "${key1.length < normalizedKey.length ? key1 : normalizedKey}" already busy`)
}
})
const keys = normalizedKey.split(' ')
Expand All @@ -108,9 +108,9 @@ const enhanceStore = (createStore: Function) => {
}, rawReducers)
const lastKey = keys.slice(-1)
if (preloadedState1 && preloadedState1[lastKey]) {
result[lastKey] = wrapperReducerPreloadedState(newReducers[key], preloadedState1[lastKey])
result[lastKey] = wrapperReducerPreloadedState(reducers[key], preloadedState1[lastKey])
} else {
result[lastKey] = newReducers[key]
result[lastKey] = reducers[key]
}
if (rawReducersMap.indexOf(normalizedKey) === -1) {
rawReducersMap.push(normalizedKey)
Expand Down
20 changes: 8 additions & 12 deletions src/reduxSetState.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,25 +17,21 @@ import getStateByMountPath from './getState'
export default (mountPath: string, dispatch: Function, getState: Function, actionPrefix: string) =>
(actionType: string, newState: Object | Function) => {
if (typeof actionType !== 'string' || !actionType.length) {
throw new Error('Action type must be non empty string')
throw new Error('ActionType must be non empty string')
}

if ((typeof newState !== 'object' && typeof newState !== 'function') &&
(process.env.NODE_ENV === 'production' || process.env.NODE_ENV !== 'production' && !isPlainObject(newState))
if ((typeof newState !== 'object' && typeof newState !== 'function') ||
(typeof newState === 'object' && (!Object.keys(newState).length || (process.env.NODE_ENV !== 'production' && !isPlainObject(newState))))
) {
throw new Error('New state must be plain object or function')
throw new Error('NewState must be non empty plain object or function')
}

let _newState
let _newState = newState
if (typeof newState === 'function') {
// Pass last state as param in newState
_newState = newState(getStateByMountPath(mountPath)(getState()))
} else {
_newState = newState
}

if (typeof _newState !== 'object' || !Object.keys(_newState).length || (process.env.NODE_ENV !== 'production' && !isPlainObject(_newState))) {
throw new Error('New state must be non empty plain object')
if (typeof _newState !== 'object' || !Object.keys(_newState).length || (process.env.NODE_ENV !== 'production' && !isPlainObject(_newState))) {
throw new Error('New state returned from function must be non empty plain object')
}
}

// Else dispatch calculated state
Expand Down
5 changes: 1 addition & 4 deletions src/registerReducers.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,7 @@ export default (
let _normReducers = {}
Object.keys(_reducers).forEach(key => {
const normalizedMountPath = normalizeMountPath(`${propMountPath || ''} ${key || ''}`)
if (this.reduxMountPaths.some(path =>
((path.indexOf(normalizedMountPath) === 0 && !((path.substr(normalizedMountPath.length)[0] || '').trim())) ||
(normalizedMountPath.indexOf(path) === 0 && !((normalizedMountPath.substr(path.length)[0] || '').trim()))))
) {
if (this.reduxMountPaths.some(path => path === normalizedMountPath)) {
throw new Error(`Mounting path "${normalizedMountPath}" already busy`)
}
if (this.lastReduxMountPath && normalizedMountPath.indexOf(this.lastReduxMountPath) === -1) {
Expand Down
5 changes: 2 additions & 3 deletions src/utils/normalize.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
// @flow
import { checkMountPath } from './checks'
/**
* Trimmed and collapsed spaces in mounting path
*/
export const normalizeMountPath = (path: any): string => {
if (typeof path !== 'string') {
throw new Error('Mounting path must be string')
}
checkMountPath(path)
return path.trim().replace(/\s{2,}/g,' ')
}

0 comments on commit c7fee84

Please sign in to comment.