Socket
Socket
Sign inDemoInstall

redux-mock-store-await-actions

Package Overview
Dependencies
Maintainers
16
Versions
4
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

redux-mock-store-await-actions - npm Package Compare versions

Comparing version 1.0.0 to 2.0.0

.editorconfig

19

CHANGELOG.md

@@ -5,2 +5,21 @@ # Change Log

<a name="2.0.0"></a>
# [2.0.0](https://github.com/moxystudio/redux-mock-store-await-actions/compare/v1.0.0...v2.0.0) (2018-02-01)
### Features
* add option to supply custom matchers ([#6](https://github.com/moxystudio/redux-mock-store-await-actions/issues/6)) ([907dbc0](https://github.com/moxystudio/redux-mock-store-await-actions/commit/907dbc0))
### BREAKING CHANGES
* convert timeout argument to an options object. This
commit introduces the matcher concept, which allows to supply a function
via options implementing custom comparison of actions. The predicate
function, which could be passed until now in the actions argument to
perform comparisons, is now no longer supported.
<a name="1.0.0"></a>

@@ -7,0 +26,0 @@ # 1.0.0 (2017-10-20)

86

index.js

@@ -6,25 +6,49 @@ 'use strict';

const isPlainObject = require('lodash/isPlainObject');
const isEqualWith = require('lodash/isEqualWith');
function actionsMatch(actions, storeActions) {
return differenceWith(actions, storeActions, (action, storeAction) => isMatch(storeAction, action)).length === 0;
function actionsContaining(expectedActions, storeActions) {
return differenceWith(expectedActions, storeActions, (expectedAction, storeAction) => isMatch(storeAction, expectedAction)).length === 0;
}
function waitForActions(store, actions, timeout) {
timeout = timeout || 50;
function actionsMatchOrder(expectedActions, storeActions) {
const isEqual = isEqualWith(expectedActions, storeActions, (expectedAction, storeAction) => isMatch(storeAction, expectedAction));
if (typeof actions === 'string' || isPlainObject(actions)) {
actions = [actions];
if (!isEqual && storeActions.length >= expectedActions.length) {
throw new MismatchError();
}
if (Array.isArray(actions)) {
actions = actions.map((value) => typeof value === 'string' ? { type: value } : value);
return isEqual;
}
function settledPromise(matcher, expectedActions, storeActions) {
try {
if (matcher(expectedActions, storeActions)) {
return Promise.resolve();
}
} catch (err) {
if (err instanceof MismatchError) {
return Promise.reject(err);
}
}
}
const shouldPromiseResolve = typeof actions === 'function' ?
actions :
(storeActions) => actionsMatch(actions, storeActions);
module.exports = (store, expectedActions, options) => {
const { timeout, matcher } = {
timeout: 50,
matcher: actionsMatchOrder,
...options,
};
// If the store already contains the expected actions, resolve the Promise immediately
if (shouldPromiseResolve(store.getActions())) {
const promise = Promise.resolve();
if (typeof expectedActions === 'string' || isPlainObject(expectedActions)) {
expectedActions = [expectedActions];
}
if (Array.isArray(expectedActions)) {
expectedActions = expectedActions.map((value) => typeof value === 'string' ? { type: value } : value);
}
const matchPromise = settledPromise.bind(null, matcher, expectedActions);
let promise = matchPromise(store.getActions());
if (promise) {
promise.cancel = () => {};

@@ -37,3 +61,3 @@

const promise = new Promise((resolve, reject) => {
promise = new Promise((resolve, reject) => {
const teardown = () => {

@@ -45,9 +69,8 @@ clearTimeout(timeoutId);

teardown();
reject(new waitForActions.TimeoutError());
reject(new TimeoutError());
}, timeout);
const unsubscribe = store.subscribe(() => {
if (shouldPromiseResolve(store.getActions())) {
teardown();
resolve();
}
const promise = matchPromise(store.getActions());
promise && resolve(promise);
});

@@ -57,3 +80,3 @@

teardown();
reject(new waitForActions.CancelledError());
reject(new CancelledError());
};

@@ -65,5 +88,5 @@ });

return promise;
}
};
waitForActions.TimeoutError = class extends Error {
class TimeoutError extends Error {
constructor() {

@@ -74,5 +97,5 @@ super('Timeout reached while waiting for actions');

}
};
}
waitForActions.CancelledError = class extends Error {
class CancelledError extends Error {
constructor() {

@@ -83,4 +106,13 @@ super('Cancel was called by user');

}
};
}
module.exports = waitForActions;
class MismatchError extends Error {
constructor() {
super('Found mismatch between the order of the array of expected and dispatched actions');
this.code = 'EMISMATCH';
this.name = 'MismatchError';
}
}
module.exports.MismatchError = MismatchError;
module.exports.matchers = { containing: actionsContaining, order: actionsMatchOrder };
{
"name": "redux-mock-store-await-actions",
"description": "Waits for specific actions to be dispatched or a timeout expires.",
"version": "1.0.0",
"version": "2.0.0",
"keywords": [

@@ -20,18 +20,27 @@ "redux",

"license": "MIT",
"main": "index.js",
"scripts": {
"lint": "eslint .",
"test": "jest --env node --coverage",
"posttest": "npm run lint",
"prerelease": "npm t && npm run lint",
"release": "standard-version",
"precommit": "lint-staged",
"prerelease": "npm t",
"release": "standard-version"
"commitmsg": "commitlint -e $GIT_PARAMS"
},
"standard-version": {
"scripts": {
"posttag": "git push --follow-tags origin master"
"posttag": "git push --follow-tags origin master && npm publish"
}
},
"lint-staged": {
"*.js": "eslint"
"*.js": [
"eslint --fix",
"git add"
]
},
"commitlint": {
"extends": [
"@commitlint/config-conventional"
]
},
"dependencies": {

@@ -41,10 +50,9 @@ "lodash": "^4.17.4"

"devDependencies": {
"babel-plugin-transform-async-to-generator": "^6.24.1",
"babel-plugin-transform-runtime": "^6.23.0",
"babel-preset-es2015": "^6.22.0",
"@commitlint/cli": "^6.0.1",
"@commitlint/config-conventional": "^6.0.2",
"eslint": "^4.3.0",
"eslint-config-moxy": "^3.0.0",
"eslint-config-moxy": "^4.1.0",
"husky": "^0.14.3",
"jest": "^21.2.1",
"lint-staged": "^4.0.2",
"jest": "^22.0.0",
"lint-staged": "^6.0.0",
"redux": "^3.7.2",

@@ -51,0 +59,0 @@ "redux-mock-store": "^1.2.3",

@@ -71,3 +71,3 @@ # redux-mock-store-await-actions

store.dispatch(login('my-username', '12345'));
store.dispatch(login('my-username', 'my-password'));

@@ -84,3 +84,3 @@ expect(store.getActions()).toContain([

```js
store.dispatch(login('my-username', '12345'));
store.dispatch(login('my-username', 'my-password'));

@@ -109,6 +109,8 @@ setTimeout(() => expect(store.getActions()).toContain([

store.dispatch(login('my-username', '12345'));
store.dispatch(login('my-username', 'my-password'));
await waitForActions(store, ['LOGIN_START', 'FETCH_ORDERS_SUCCESS']);
```
### Example #2: action objects

@@ -125,6 +127,6 @@

store.dispatch(login('my-username', '123'));
store.dispatch(login('my-username', 'my-password'));
// { type: 'LOGIN_START', payload: { username: 'my-username' } }
// matches
// { type: 'LOGIN_START', payload: { username, password } }
// { type: 'LOGIN_START', payload: { username: 'my-username', password } }
//

@@ -134,2 +136,3 @@ // { type: 'FETCH_ORDERS_SUCCESS', }

// { type: 'FETCH_ORDERS_SUCCESS', payload: orders }
await waitForActions(store, [

@@ -147,35 +150,17 @@ {

### Example #3: function (advanced use cases)
Supply a predicate which will be called for every action dispatched.
```js
import waitForActions from 'redux-mock-store-await-actions';
import configureStore from 'redux-mock-store';
import thunkMiddleware from 'redux-thunk';
const store = configureStore([thunkMiddleware])();
store.dispatch(login('my-username', '123'));
await waitForActions(store, (storeActions) => {
const hasLoginStart = storeActions.some((item) => item.type === 'LOGIN_START' && item.payload.username === 'my-username');
const hasFetchOrdersSuccess = storeActions.some((item) => item.type === 'FETCH_ORDERS_SUCCESS');
return hasLoginStart && hasFetchOrdersSuccess;
});
```
**NOTE:** Subsequent calls to `waitForActions` should be preceded by a call to `store.clearActions()`, otherwise the returned `Promise` will resolve immediately.
## API
### waitForActions(store, actions, [timeout])
### waitForActions(store, actions, [options])
Returns a `Promise` which fulfills if all `actions` are dispatched before the timeout expires, otherwise it is rejected. The `Promise` has a `.cancel()` function which, if called, will reject the `Promise`.
Returns a `Promise` which fulfills if all `actions` are dispatched before the timeout expires. The `Promise` has a `.cancel()` function which, if called, will reject the `Promise`.
The `Promise` might be rejected:
* as a result of timeout expiration throwing `TimeoutError`
* as a result of `.cancel()` invocation throwing `CancelledError`
* as a result of timeout expiration, throwing `TimeoutError`
* as a result of `.cancel()` invocation, throwing `CancelledError`
* when the action's matcher throws `MismatchError`
**NOTE:** Subsequent calls to `waitForActions` with the same actions should be preceded by a call to `store.clearActions()`, otherwise the returned `Promise` will resolve immediately.
#### store

@@ -199,6 +184,7 @@

* action objects mixed with action type strings.
* `Function`: a predicate which receives the array returned by `store.getActions()` of `redux-mock-store`.
#### timeout
#### options
##### timeout
Type: `Number`

@@ -209,3 +195,58 @@ Default: 50

##### matcher
Type: `Function`
Default: `.matchers.order`
Supplies custom behavior to specify how expected and dispatched actions should be compared. The function accepts two arguments: the array of expected actions and dispatched actions.
The matcher must either:
* return `true` to indicate a match has occurred and fulfill the `Promise`
* return `false` to indicate a match is yet to occur and the `Promise` remains in pending state
* throw `MismatchError` to indicate a match will not occur anymore and reject the `Promise`
Two built-in matchers are already shipped and available under `.matchers` property:
* `order` matcher performs a comparison between the specified order of expected actions against the order of arrival of dispatched actions. On the first mismatch detected, `MismatchError` is thrown for early rejection
* `containing` matcher is a less strict matcher which checks whether expected actions are contained within dispatched actions
Both matchers perform a *partial deep comparison* between dispatched and expected actions, as per [Lodash's isMatch()](https://lodash.com/docs/4.17.4#isMatch).
Example of a custom matcher implementation:
```js
import waitForActions from 'redux-mock-store-await-actions';
import configureStore from 'redux-mock-store';
import thunkMiddleware from 'redux-thunk';
const store = configureStore([thunkMiddleware])();
const expectedActions = [
{ type: 'LOGIN_START', payload: { username: 'my-username' } },
{ type: 'FETCH_ORDERS_SUCCESS' }
];
store.dispatch(login('my-username', 'my-password'));
// Throws if LOGIN_FAIL is dispatched or
// Matches when LOGIN_START and FETCH_ORDERS_SUCCESS are dispatched
waitForActions(store, expectedActions, { matcher: (expectedActions, storeActions) => {
const hasLoginFail = storeActions.some((action) => action.type === 'LOGIN_FAIL');
if (hasLoginFail) {
throw new waitForActions.MismatchError();
}
const hasLoginStart = storeActions.some((action) => action.type === 'LOGIN_START' && action.payload.username === 'my-username');
const hasFetchOrdersSuccess = storeActions.some((action) => action.type === 'FETCH_ORDERS_SUCCESS');
return hasLoginStart && hasFetchOrdersSuccess;
}})
.then(() => {
// Expected actions dispatched
})
.catch((err) => {
// MismatchError
});
```
## Tests

@@ -212,0 +253,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc