Skip to content

Commit cb2e433

Browse files
committed
Add handler tests
1 parent 949093a commit cb2e433

13 files changed

+236
-23
lines changed

docs.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -358,12 +358,12 @@ Returns **[Object][20]** An [LP_API] action that can be handled by the lp-redux-
358358

359359
This library exports the following selectors for determining the status of requests:
360360

361-
- `selectors.status(state, requestKey, [slice])`
362-
- `selectors.hasStatus(state, requestKey, [slice])`
363-
- `selectors.isLoading(state, requestKey, [slice])`
364-
- `selectors.isComplete(state, requestKey, [slice])`
365-
- `selectors.isSuccess(state, requestKey, [slice])`
366-
- `selectors.isFailure(state, requestKey, [slice])`
361+
- `selectors.status(state, requestAction, [slice])`
362+
- `selectors.hasStatus(state, requestAction, [slice])`
363+
- `selectors.isLoading(state, requestAction, [slice])`
364+
- `selectors.isComplete(state, requestAction, [slice])`
365+
- `selectors.isSuccess(state, requestAction, [slice])`
366+
- `selectors.isFailure(state, requestAction, [slice])`
367367

368368
In order to work, the `lp-redux-api` reducer must be included in `combineReducers()`.
369369
Selectors expect the reducer to be keyed under `'api'`- if a different key is used,
@@ -389,12 +389,12 @@ combineReducers({
389389

390390
// Now you can keep track of request status elsewhere in your app
391391

392-
import { requestWithKey, selectors as apiSelectors } from 'lp-redux-api'
392+
import { createRequest, selectors as apiSelectors } from 'lp-redux-api'
393393

394-
const REQ_FETCH_USERS = 'REQ_FETCH_USERS'
395-
dispatch(requestWithKey(REQ_FETCH_USERS, { url: '/users' }))
394+
const fetchUsers = createRequest('FETCH_USERS', { url: '/users' })
395+
dispatch(fetchUsers())
396396

397-
apiSelectors.status(state, REQ_FETCH_USERS) // -> 'loading'
397+
apiSelectors.status(state, fetchUsers) // -> 'loading'
398398

399399
*
400400
```

src/create-request.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import LP_API from './LP_API'
2+
import { isObject, isFunction } from 'lodash'
23

34
/**
45
* A function that creates action creators for making API requests, much like [createAction](https://redux-actions.js.org/api-reference/createaction-s) from `redux-actions`.
@@ -26,13 +27,15 @@ import LP_API from './LP_API'
2627
**/
2728

2829
function createActionOptions (definition, args) {
29-
if (typeof definition === 'object') return definition
30-
if (typeof definition === 'function') return definition(...args) || {}
31-
throw new Error('Request definition must be an object or a function.')
30+
return isFunction(definition)
31+
? definition(...args) || {}
32+
: definition
3233
}
3334

3435
function createRequest (type, definition) {
3536
if (!type) throw new Error('Must include a type for your request.')
37+
if (!definition) throw new Error('Must include a request definition for your request.')
38+
if (!(isObject(definition) || isFunction(definition))) throw new Error('Request definition must be an object or a function.')
3639
function actionCreator (...args) {
3740
return {
3841
[LP_API]: {

src/handlers/handle-response.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import { isSuccessAction, isFailureAction } from './helpers'
2626
**/
2727

2828
function handleResponse (successHandler, failureHandler) {
29+
if (!(successHandler && failureHandler)) throw new Error('handleResponse requires both a success handler and failure handler.')
2930
return (state, action) => {
3031
if (isSuccessAction(action)) return successHandler(state, action)
3132
if (isFailureAction(action)) return failureHandler(state, action)

src/selectors.js

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@ import {
77

88
/**
99
* This library exports the following selectors for determining the status of requests:
10-
* - `selectors.status(state, requestKey, [slice])`
11-
* - `selectors.hasStatus(state, requestKey, [slice])`
12-
* - `selectors.isLoading(state, requestKey, [slice])`
13-
* - `selectors.isComplete(state, requestKey, [slice])`
14-
* - `selectors.isSuccess(state, requestKey, [slice])`
15-
* - `selectors.isFailure(state, requestKey, [slice])`
10+
* - `selectors.status(state, requestAction, [slice])`
11+
* - `selectors.hasStatus(state, requestAction, [slice])`
12+
* - `selectors.isLoading(state, requestAction, [slice])`
13+
* - `selectors.isComplete(state, requestAction, [slice])`
14+
* - `selectors.isSuccess(state, requestAction, [slice])`
15+
* - `selectors.isFailure(state, requestAction, [slice])`
1616
*
1717
* In order to work, the `lp-redux-api` reducer must be included in `combineReducers()`.
1818
* Selectors expect the reducer to be keyed under `'api'`- if a different key is used,
@@ -38,12 +38,12 @@ import {
3838
*
3939
* // Now you can keep track of request status elsewhere in your app
4040
*
41-
* import { requestWithKey, selectors as apiSelectors } from 'lp-redux-api'
41+
* import { createRequest, selectors as apiSelectors } from 'lp-redux-api'
4242
*
43-
* const REQ_FETCH_USERS = 'REQ_FETCH_USERS'
44-
* dispatch(requestWithKey(REQ_FETCH_USERS, { url: '/users' }))
43+
* const fetchUsers = createRequest('FETCH_USERS', { url: '/users' })
44+
* dispatch(fetchUsers())
4545
*
46-
* apiSelectors.status(state, REQ_FETCH_USERS) // -> 'loading'
46+
* apiSelectors.status(state, fetchUsers) // -> 'loading'
4747
*
4848
**/
4949

test/create-request.test.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { createRequest, LP_API } from '../src/'
2+
import { REQUEST_TYPE } from './fixtures'
3+
4+
test('createRequest requires a type argument', () => {
5+
expect(createRequest).toThrow()
6+
})
7+
8+
test('createRequest requires a definition argument', () => {
9+
expect(() => createRequest(REQUEST_TYPE)).toThrow()
10+
})
11+
12+
test('createRequest accepts object request definitions', () => {
13+
const options = { foo: 'bar' }
14+
const actionCreator = createRequest(REQUEST_TYPE, options)
15+
const action = actionCreator()
16+
expect(action[LP_API]).toEqual({ requestKey: REQUEST_TYPE, foo: 'bar' })
17+
})
18+
19+
test('createRequest accepts function request definitions', () => {
20+
const actionCreator = createRequest(REQUEST_TYPE, (arg) => ({
21+
foo: arg
22+
}))
23+
const action = actionCreator('bar')
24+
expect(action[LP_API]).toEqual({ requestKey: REQUEST_TYPE, foo: 'bar' })
25+
})
26+
27+
test('createRequest rejects other types of request definitions', () => {
28+
expect(() => createRequest(REQUEST_TYPE, 'wtf')).toThrow()
29+
})

test/fixtures.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import { LP_API } from '../src'
44

55
export const REQUEST_KEY = 'REQUEST_KEY'
6+
export const REQUEST_TYPE = 'REQUEST_TYPE'
67
export const ACTION_TYPE_REQUEST = 'ACTION_TYPE_REQUEST'
78
export const ACTION_TYPE_SUCCESS = 'ACTION_TYPE_SUCCESS'
89
export const ACTION_TYPE_FAILURE = 'ACTION_TYPE_FAILURE'

test/handlers/handle-failure.test.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { handleFailure, LP_API_STATUS_FAILURE, LP_API_STATUS_SUCCESS } from '../../src/'
2+
3+
const STATE = { foo: 'bar' }
4+
const REQUEST_TYPE = 'FETCH_USERS'
5+
const FAILURE_ACTION = { type: REQUEST_TYPE, payload: { status: LP_API_STATUS_FAILURE }}
6+
const SUCCESS_ACTION = { type: REQUEST_TYPE, payload: { status: LP_API_STATUS_SUCCESS }}
7+
8+
test('handleFailure runs handler with state and action when request fails', () => {
9+
const NEW_STATE = { new: 'state' }
10+
const handler = jest.fn(() => NEW_STATE)
11+
const failureHandler = handleFailure(handler)
12+
const newState = failureHandler(STATE, FAILURE_ACTION)
13+
expect(handler).toHaveBeenCalledWith(STATE, FAILURE_ACTION)
14+
expect(newState).toEqual(NEW_STATE)
15+
})
16+
17+
test('handleFailure returns state when request does not fail', () => {
18+
const handler = jest.fn()
19+
const failureHandler = handleFailure(handler)
20+
const newState = failureHandler(STATE, SUCCESS_ACTION)
21+
expect(newState).toEqual(STATE)
22+
})

test/handlers/handle-response.test.js

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { handleResponse, LP_API_STATUS_FAILURE, LP_API_STATUS_SUCCESS } from '../../src/'
2+
3+
const STATE = { foo: 'bar' }
4+
const REQUEST_TYPE = 'FETCH_USERS'
5+
const FAILURE_ACTION = { type: REQUEST_TYPE, payload: { status: LP_API_STATUS_FAILURE }}
6+
const SUCCESS_ACTION = { type: REQUEST_TYPE, payload: { status: LP_API_STATUS_SUCCESS }}
7+
8+
test('handleResponse requires two handlers', () => {
9+
const successHandler = jest.fn()
10+
const failureHandler = jest.fn()
11+
expect(() => handleResponse(successHandler)).toThrow()
12+
expect(() => handleResponse(null, failureHandler)).toThrow()
13+
})
14+
15+
test('handleResponse runs success handler with state and action when request succeeds', () => {
16+
const SUCCESS_STATE = { success: 'state' }
17+
const FAILURE_STATE = { failure: 'state' }
18+
const successHandler = jest.fn(() => SUCCESS_STATE)
19+
const failureHandler = jest.fn(() => FAILURE_STATE)
20+
const handler = handleResponse(successHandler, failureHandler)
21+
const newState = handler(STATE, SUCCESS_ACTION)
22+
expect(successHandler).toHaveBeenCalledWith(STATE, SUCCESS_ACTION)
23+
expect(newState).toEqual(SUCCESS_STATE)
24+
})
25+
26+
test('handleResponse runs failure handler with state and action when request fails', () => {
27+
const SUCCESS_STATE = { success: 'state' }
28+
const FAILURE_STATE = { failure: 'state' }
29+
const successHandler = jest.fn(() => SUCCESS_STATE)
30+
const failureHandler = jest.fn(() => FAILURE_STATE)
31+
const handler = handleResponse(successHandler, failureHandler)
32+
const newState = handler(STATE, FAILURE_ACTION)
33+
expect(failureHandler).toHaveBeenCalledWith(STATE, FAILURE_ACTION)
34+
expect(newState).toEqual(FAILURE_STATE)
35+
})

test/handlers/handle-success.test.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { handleSuccess, LP_API_STATUS_FAILURE, LP_API_STATUS_SUCCESS } from '../../src/'
2+
3+
const STATE = { foo: 'bar' }
4+
const REQUEST_TYPE = 'FETCH_USERS'
5+
const FAILURE_ACTION = { type: REQUEST_TYPE, payload: { status: LP_API_STATUS_FAILURE }}
6+
const SUCCESS_ACTION = { type: REQUEST_TYPE, payload: { status: LP_API_STATUS_SUCCESS }}
7+
8+
test('handleSuccess runs handler with state and action when request succeeds', () => {
9+
const NEW_STATE = { new: 'state' }
10+
const handler = jest.fn(() => NEW_STATE)
11+
const successHandler = handleSuccess(handler)
12+
const newState = successHandler(STATE, SUCCESS_ACTION)
13+
expect(handler).toHaveBeenCalledWith(STATE, SUCCESS_ACTION)
14+
expect(newState).toEqual(NEW_STATE)
15+
})
16+
17+
test('handleSuccess returns state when request does not succeed', () => {
18+
const handler = jest.fn()
19+
const successHandler = handleSuccess(handler)
20+
const newState = successHandler(STATE, FAILURE_ACTION)
21+
expect(newState).toEqual(STATE)
22+
})

test/handlers/index.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export handleSuccess from './handle-success'
2+
export handleFailure from './handle-failure'
3+
export handleResponse from './handle-response'
4+
export setOnSuccess from './set-on-success'
5+
export setOnFailure from './set-on-failure'
6+
export setOnResponse from './set-on-response'

test/handlers/set-on-failure.test.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { setOnFailure, LP_API_STATUS_FAILURE, LP_API_STATUS_SUCCESS } from '../../src/'
2+
3+
const STATE = { foo: 'bar' }
4+
const REQUEST_TYPE = 'FETCH_USERS'
5+
const ERROR_DATA = 'ERRRRRROR'
6+
const FAILURE_ACTION = { type: REQUEST_TYPE, payload: { status: LP_API_STATUS_FAILURE, data: ERROR_DATA }}
7+
const SUCCESS_ACTION = { type: REQUEST_TYPE, payload: { status: LP_API_STATUS_SUCCESS }}
8+
9+
test('setOnFailure sets state with error info when request fails', () => {
10+
const failureHandler = setOnFailure('errorPath')
11+
const newState = failureHandler(STATE, FAILURE_ACTION)
12+
expect(newState.errorPath).toEqual(ERROR_DATA)
13+
})
14+
15+
test('setOnFailure can receive a transform function', () => {
16+
const CUSTOM_DATA = 'custom stuff'
17+
const transform = jest.fn(() => CUSTOM_DATA)
18+
const failureHandler = setOnFailure('errorPath', transform)
19+
const newState = failureHandler(STATE, FAILURE_ACTION)
20+
expect(transform).toHaveBeenCalledWith(FAILURE_ACTION, STATE)
21+
expect(newState.errorPath).toEqual(CUSTOM_DATA)
22+
})
23+
24+
test('setOnFailure returns state when request does not fail', () => {
25+
const failureHandler = setOnFailure('errorPath')
26+
const newState = failureHandler(STATE, SUCCESS_ACTION)
27+
expect(newState).toEqual(STATE)
28+
})

test/handlers/set-on-response.test.js

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { setOnResponse, LP_API_STATUS_FAILURE, LP_API_STATUS_SUCCESS } from '../../src/'
2+
3+
const STATE = { foo: 'bar' }
4+
const REQUEST_TYPE = 'FETCH_USERS'
5+
const ERROR_DATA = 'BAD!'
6+
const DATA = 'returned data'
7+
const FAILURE_ACTION = { type: REQUEST_TYPE, payload: { status: LP_API_STATUS_FAILURE, data: ERROR_DATA }}
8+
const SUCCESS_ACTION = { type: REQUEST_TYPE, payload: { status: LP_API_STATUS_SUCCESS, data: DATA }}
9+
10+
test('setOnResponse sets state with data when request succeeds', () => {
11+
const handler = setOnResponse('dataPath', 'errorPath')
12+
const newState = handler(STATE, SUCCESS_ACTION)
13+
expect(newState.dataPath).toEqual(DATA)
14+
})
15+
16+
test('setOnResponse sets state with error data when request fails', () => {
17+
const handler = setOnResponse('dataPath', 'errorPath')
18+
const newState = handler(STATE, FAILURE_ACTION)
19+
expect(newState.errorPath).toEqual(ERROR_DATA)
20+
})
21+
22+
test('setOnResponse can receive a success transform function', () => {
23+
const CUSTOM_DATA = 'custom stuff'
24+
const transform = jest.fn(() => CUSTOM_DATA)
25+
const handler = setOnResponse('dataPath', 'errorPath', transform)
26+
const newState = handler(STATE, SUCCESS_ACTION)
27+
expect(transform).toHaveBeenCalledWith(SUCCESS_ACTION, STATE)
28+
expect(newState.dataPath).toEqual(CUSTOM_DATA)
29+
})
30+
31+
test('setOnResponse can receive a failure transform function', () => {
32+
const CUSTOM_DATA = 'custom stuff'
33+
const transform = jest.fn(() => CUSTOM_DATA)
34+
const handler = setOnResponse('dataPath', 'errorPath', null, transform)
35+
const newState = handler(STATE, FAILURE_ACTION)
36+
expect(transform).toHaveBeenCalledWith(FAILURE_ACTION, STATE)
37+
expect(newState.errorPath).toEqual(CUSTOM_DATA)
38+
})

test/handlers/set-on-success.test.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { setOnSuccess, LP_API_STATUS_FAILURE, LP_API_STATUS_SUCCESS } from '../../src/'
2+
3+
const STATE = { foo: 'bar' }
4+
const REQUEST_TYPE = 'FETCH_USERS'
5+
const DATA = 'returned data'
6+
const FAILURE_ACTION = { type: REQUEST_TYPE, payload: { status: LP_API_STATUS_FAILURE }}
7+
const SUCCESS_ACTION = { type: REQUEST_TYPE, payload: { status: LP_API_STATUS_SUCCESS, data: DATA }}
8+
9+
test('setOnSuccess sets state with data when request succeeds', () => {
10+
const successHandler = setOnSuccess('dataPath')
11+
const newState = successHandler(STATE, SUCCESS_ACTION)
12+
expect(newState.dataPath).toEqual(DATA)
13+
})
14+
15+
test('setOnSuccess can receive a transform function', () => {
16+
const CUSTOM_DATA = 'custom stuff'
17+
const transform = jest.fn(() => CUSTOM_DATA)
18+
const successHandler = setOnSuccess('dataPath', transform)
19+
const newState = successHandler(STATE, SUCCESS_ACTION)
20+
expect(transform).toHaveBeenCalledWith(SUCCESS_ACTION, STATE)
21+
expect(newState.dataPath).toEqual(CUSTOM_DATA)
22+
})
23+
24+
test('setOnSuccess returns state when request does not succeed', () => {
25+
const successHandler = setOnSuccess('dataPath')
26+
const newState = successHandler(STATE, FAILURE_ACTION)
27+
expect(newState).toEqual(STATE)
28+
})

0 commit comments

Comments
 (0)