redux-mock-store-await-actions
Advanced tools
Comparing version 2.0.1 to 2.1.0
@@ -5,2 +5,12 @@ # Change Log | ||
<a name="2.1.0"></a> | ||
# [2.1.0](https://github.com/moxystudio/redux-mock-store-await-actions/compare/v2.0.1...v2.1.0) (2018-02-16) | ||
### Features | ||
* add throttle option ([#5](https://github.com/moxystudio/redux-mock-store-await-actions/issues/5)) ([#23](https://github.com/moxystudio/redux-mock-store-await-actions/issues/23)) ([6092f1f](https://github.com/moxystudio/redux-mock-store-await-actions/commit/6092f1f)) | ||
<a name="2.0.1"></a> | ||
@@ -7,0 +17,0 @@ ## [2.0.1](https://github.com/moxystudio/redux-mock-store-await-actions/compare/v2.0.0...v2.0.1) (2018-02-07) |
36
index.js
'use strict'; | ||
const differenceWith = require('lodash/differenceWith'); | ||
const isEqualWith = require('lodash/isEqualWith'); | ||
const isMatch = require('lodash/isMatch'); | ||
const isPlainObject = require('lodash/isPlainObject'); | ||
const isEqualWith = require('lodash/isEqualWith'); | ||
const throttle = require('lodash/throttle'); | ||
@@ -35,5 +36,6 @@ function actionsContaining(expectedActions, storeActions) { | ||
module.exports = (store, expectedActions, options) => { | ||
const { timeout, matcher } = { | ||
timeout: 50, | ||
const { timeout, matcher, throttleWait } = { | ||
timeout: 2000, | ||
matcher: actionsMatchOrder, | ||
throttleWait: 0, | ||
...options, | ||
@@ -62,3 +64,22 @@ }; | ||
promise = new Promise((resolve, reject) => { | ||
const maybeThrottled = (() => { | ||
const runMatcher = (storeActions) => { | ||
const promise = matchPromise(storeActions); | ||
if (promise) { | ||
teardown(); | ||
resolve(promise); | ||
} | ||
}; | ||
if (throttleWait > 0) { | ||
return throttle(runMatcher, throttleWait, { leading: false, trailing: true }); | ||
} | ||
runMatcher.cancel = () => {}; | ||
return runMatcher; | ||
})(); | ||
const teardown = () => { | ||
maybeThrottled.cancel(); | ||
clearTimeout(timeoutId); | ||
@@ -71,11 +92,4 @@ unsubscribe(); | ||
}, timeout); | ||
const unsubscribe = store.subscribe(() => { | ||
const promise = matchPromise(store.getActions()); | ||
const unsubscribe = store.subscribe(() => maybeThrottled(store.getActions())); | ||
if (promise) { | ||
teardown(); | ||
resolve(promise); | ||
} | ||
}); | ||
cancel = () => { | ||
@@ -82,0 +96,0 @@ teardown(); |
{ | ||
"name": "redux-mock-store-await-actions", | ||
"description": "Waits for specific actions to be dispatched or a timeout expires.", | ||
"version": "2.0.1", | ||
"version": "2.1.0", | ||
"keywords": [ | ||
@@ -6,0 +6,0 @@ "redux", |
@@ -184,9 +184,19 @@ # redux-mock-store-await-actions | ||
Type: `Number` | ||
Default: 50 | ||
Default: 2000 | ||
The timeout given in milliseconds. | ||
##### throttleWait | ||
Type: `Number` | ||
Default: 0 | ||
Specifies the time in milliseconds that every invocation to the action's matcher take place at since the last invocation. When set to zero, throttling is disabled. | ||
When throttling is enabled, the `matcher` will be called at most once per `throttleWait` milliseconds receiving the array of actions dispatched until that time. If the `matcher` does not resolve the `Promise` until `timeout` milliseconds have elapsed, the `Promise` is rejected throwing `TimeoutError`. | ||
This feature is useful when one needs to wait for several actions or a burst of actions to be dispatched, effectively skip invocations to the action's matcher until the Redux store "settles" to avoid running complex action comparison logic in the meantime and improve performance. | ||
##### matcher | ||
Type: `Function` | ||
Type: `Function` | ||
Default: `.matchers.order` | ||
@@ -193,0 +203,0 @@ |
'use strict'; | ||
const waitForActions = require('../'); | ||
const { assertError, spyOnUnsubscribe, spyOnThrottleCancel } = require('./util'); | ||
const configureStore = require('redux-mock-store').default; | ||
const thunkMiddleware = require('redux-thunk').default; | ||
const { assertError, spyOnUnsubscribe } = require('./util'); | ||
const { containing } = waitForActions.matchers; | ||
const action1 = { type: 'ACTION-1', payload: { id: 1, name: 'ACTION ONE' } }; | ||
@@ -27,5 +25,7 @@ const action2 = { type: 'ACTION-2', payload: { id: 2, name: 'ACTION TWO' } }; | ||
jest.restoreAllMocks(); | ||
jest.resetModules(); | ||
}); | ||
it('should resolve the promise when a single action is dispatched', async () => { | ||
const waitForActions = require('../'); // eslint-disable-line global-require | ||
const mockStore = createMockStore(); | ||
@@ -40,2 +40,3 @@ | ||
it('should fulfill the promise when action creator is dispatched', async () => { | ||
const waitForActions = require('../'); // eslint-disable-line global-require | ||
const mockStore = createMockStore([thunkMiddleware]); | ||
@@ -52,2 +53,3 @@ const actions = [action1, action2, action3]; | ||
it('should fulfill the promise when async action creator is dispatched', async () => { | ||
const waitForActions = require('../'); // eslint-disable-line global-require | ||
const mockStore = createMockStore([thunkMiddleware]); | ||
@@ -64,2 +66,4 @@ const actions = [action1, action2, action3]; | ||
it('should fulfill the promise when no actions are expected', async () => { | ||
const waitForActions = require('../'); // eslint-disable-line global-require | ||
const { containing } = waitForActions.matchers; | ||
const mockStore = createMockStore([thunkMiddleware]); | ||
@@ -79,2 +83,3 @@ const actions = [action1, action2, action3]; | ||
it('should reject the promise when an action creator is dispatched and the order of expected and dispatched actions mismatch', () => { | ||
const waitForActions = require('../'); // eslint-disable-line global-require | ||
const mockStore = createMockStore([thunkMiddleware]); | ||
@@ -91,2 +96,3 @@ const actions = [action1, action2, action3]; | ||
it('should reject the promise when an async action creator is dispatched and the order of expected and dispatched actions mismatch', () => { | ||
const waitForActions = require('../'); // eslint-disable-line global-require | ||
const mockStore = createMockStore([thunkMiddleware]); | ||
@@ -103,2 +109,3 @@ const actions = [action1, action2, action3]; | ||
it('should fulfill the promise when expected actions match a subset of property values of dispatched actions', async () => { | ||
const waitForActions = require('../'); // eslint-disable-line global-require | ||
const mockStore = createMockStore([thunkMiddleware]); | ||
@@ -119,2 +126,6 @@ const actions = [action1, action2, action3, action4]; | ||
() => { | ||
jest.useFakeTimers(); | ||
const waitForActions = require('../'); // eslint-disable-line global-require | ||
const { containing } = waitForActions.matchers; | ||
const mockStore = createMockStore([thunkMiddleware]); | ||
@@ -130,2 +141,4 @@ const actions = [action1, action2]; | ||
jest.runAllTimers(); | ||
return assertError(promise, timeoutError); | ||
@@ -136,2 +149,5 @@ } | ||
it('should reject the promise when expected actions are not received and timeout expires', () => { | ||
jest.useFakeTimers(); | ||
const waitForActions = require('../'); // eslint-disable-line global-require | ||
const mockStore = createMockStore(); | ||
@@ -143,6 +159,11 @@ const actions = [action1, action2, action3]; | ||
return assertError(waitForActions(mockStore, actions), timeoutError); | ||
const promise = waitForActions(mockStore, actions); | ||
jest.runAllTimers(); | ||
return assertError(promise, timeoutError); | ||
}); | ||
it('should fulfill the promise when a single action type is expected', () => { | ||
const waitForActions = require('../'); // eslint-disable-line global-require | ||
const mockStore = createMockStore([thunkMiddleware]); | ||
@@ -156,2 +177,3 @@ | ||
it('should fulfill the promise when action types are expected', () => { | ||
const waitForActions = require('../'); // eslint-disable-line global-require | ||
const mockStore = createMockStore([thunkMiddleware]); | ||
@@ -165,2 +187,3 @@ | ||
it('should fulfill the promise when custom matcher is passed and evaluates to true', async () => { | ||
const waitForActions = require('../'); // eslint-disable-line global-require | ||
const mockStore = createMockStore([thunkMiddleware]); | ||
@@ -178,2 +201,5 @@ const matcher = jest.fn(() => true); | ||
it('should reject the promise via timeout when custom matcher is passed and evaluates to false', async () => { | ||
jest.useFakeTimers(); | ||
const waitForActions = require('../'); // eslint-disable-line global-require | ||
const mockStore = createMockStore([thunkMiddleware]); | ||
@@ -184,4 +210,8 @@ const matcher = jest.fn(() => false); | ||
await assertError(waitForActions(mockStore, [], { matcher }), timeoutError); | ||
const promise = waitForActions(mockStore, [], { matcher }); | ||
jest.runAllTimers(); | ||
await assertError(promise, timeoutError); | ||
expect(matcher).toHaveBeenCalledWith([], [action1]); | ||
@@ -191,2 +221,3 @@ }); | ||
it('should reject the promise when custom matcher throws mismatch error', async () => { | ||
const waitForActions = require('../'); // eslint-disable-line global-require | ||
const mockStore = createMockStore([thunkMiddleware]); | ||
@@ -205,2 +236,4 @@ const matcher = jest.fn(() => { throw new waitForActions.MismatchError(); }); | ||
const waitForActions = require('../'); // eslint-disable-line global-require | ||
const mockStore = createMockStore(); | ||
@@ -216,2 +249,3 @@ | ||
it('should return a promise with a cancel function', () => { | ||
const waitForActions = require('../'); // eslint-disable-line global-require | ||
const mockStore = createMockStore(); | ||
@@ -230,2 +264,3 @@ | ||
it('should reject the promise when cancel() is called before dispatch of remaining actions', () => { | ||
const waitForActions = require('../'); // eslint-disable-line global-require | ||
const mockStore = createMockStore(); | ||
@@ -241,2 +276,4 @@ | ||
it('should reject the promise when the specified timeout expires', () => { | ||
const waitForActions = require('../'); // eslint-disable-line global-require | ||
jest.useFakeTimers(); | ||
@@ -248,3 +285,3 @@ | ||
jest.runTimersToTime(1000); | ||
jest.advanceTimersByTime(1000); | ||
@@ -255,2 +292,4 @@ return assertError(promise, timeoutError); | ||
it('should fulfill the promise the array of expected actions is contained in the array of dispatched actions', () => { | ||
const waitForActions = require('../'); // eslint-disable-line global-require | ||
const { containing } = waitForActions.matchers; | ||
const mockStore = createMockStore([thunkMiddleware]); | ||
@@ -267,2 +306,5 @@ const actions = [action1, action2, action3]; | ||
it('should teardown correctly when promise fulfills', async () => { | ||
expect.assertions(2); | ||
const waitForActions = require('../'); // eslint-disable-line global-require | ||
const mockStore = createMockStore([thunkMiddleware]); | ||
@@ -283,2 +325,3 @@ const clearTimeoutSpy = jest.spyOn(global, 'clearTimeout'); | ||
const waitForActions = require('../'); // eslint-disable-line global-require | ||
const mockStore = createMockStore([thunkMiddleware]); | ||
@@ -297,4 +340,6 @@ const getUnsubcribeSpy = spyOnUnsubscribe(mockStore); | ||
it('should teardown correctly when promise rejects via cancelation', async () => { | ||
expect.assertions(2); | ||
expect.assertions(3); | ||
const throttleCancelSpy = spyOnThrottleCancel(); | ||
const waitForActions = require('../'); // eslint-disable-line global-require | ||
const mockStore = createMockStore([thunkMiddleware]); | ||
@@ -306,3 +351,3 @@ const clearTimeoutSpy = jest.spyOn(global, 'clearTimeout'); | ||
const promise = waitForActions(mockStore, [action1, action2]); | ||
const promise = waitForActions(mockStore, [action1, action2], { throttleWait: 100 }); | ||
@@ -315,3 +360,5 @@ setTimeout(() => promise.cancel(), 10); | ||
expect(getUnsubcribeSpy()).toHaveBeenCalledTimes(1); | ||
expect(clearTimeoutSpy).toHaveBeenCalledTimes(1); | ||
expect(throttleCancelSpy()).toHaveBeenCalledTimes(1); | ||
// When .cancel() is called on a throttled function, clearTimeout() is also called | ||
expect(clearTimeoutSpy).toHaveBeenCalledTimes(2); | ||
} | ||
@@ -323,2 +370,3 @@ }); | ||
const waitForActions = require('../'); // eslint-disable-line global-require | ||
const mockStore = createMockStore([thunkMiddleware]); | ||
@@ -337,1 +385,31 @@ const clearTimeoutSpy = jest.spyOn(global, 'clearTimeout'); | ||
}); | ||
it('should fulfill the promise when throttling is enabled', () => { | ||
jest.useFakeTimers(); | ||
const waitForActions = require('../'); // eslint-disable-line global-require | ||
const mockStore = createMockStore([thunkMiddleware]); | ||
mockStore.dispatch(asyncActionsCreator([action1, action2, action3])); | ||
const promise = waitForActions(mockStore, [action1, action2, action3], { throttleWait: 100 }); | ||
jest.runAllTimers(); | ||
return promise; | ||
}); | ||
it('should reject the promise via timeout when throttling is enabled', () => { | ||
jest.useFakeTimers(); | ||
const waitForActions = require('../'); // eslint-disable-line global-require | ||
const mockStore = createMockStore([thunkMiddleware]); | ||
mockStore.dispatch(asyncActionsCreator([action1])); | ||
const promise = waitForActions(mockStore, [action1, action2, action3], { throttleWait: 100 }); | ||
jest.runAllTimers(); | ||
return assertError(promise, timeoutError); | ||
}); |
@@ -5,1 +5,2 @@ 'use strict'; | ||
exports.spyOnUnsubscribe = require('./spyOnUnsubscribe'); | ||
exports.spyOnThrottleCancel = require('./spyOnThrottleCancel'); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
33055
14
413
264