├── .gitignore ├── README.md ├── index.js ├── package.json └── test ├── index.spec.js └── jsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | node_modules -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # redux-chaos 2 | Dead Simple Chaos/Property Testing with Redux 3 | 4 | This randomly chooses an action from an array 100,000 times and dispatches it to a store with a hook on each repetition. 5 | 6 | ##Setup 7 | 8 | `npm i --save-dev redux-chaos` 9 | ```javascript 10 | import { chaos } from 'redux-chaos' 11 | ``` 12 | 13 | ###chaos `(Store: ReduxStore, Actions: Array | Object, Each: ?Function)` 14 | 15 | Store: any redux store 16 | 17 | Actions: your object of actions or an array of your actions 18 | 19 | If you have an actions file, you can simply use `import * as actions from './actions'` for the actions. 20 | 21 | Each: `(prevState: any, nextState: any, action: Object | Function): void` 22 | 23 | Each is executed every iteration and given the parameters above, self explanatory. 24 | 25 | ##Example 26 | 27 | ```javascript 28 | import { chaos } from 'redux-chaos' 29 | 30 | function counter(state = {count: 0, other: ''}, action) { 31 | switch (action.type) { 32 | case 'INCREMENT_COUNTER': 33 | return { 34 | count: state.count + 1, 35 | other: state.other 36 | } 37 | case 'DECREMENT_COUNTER': 38 | return { 39 | count: state.count - 1, 40 | other: state.other 41 | } 42 | case 'AYYY': 43 | return { 44 | count: state.count, 45 | other: state.other.length >= 50 ? state.other : state.other + 'lo' 46 | } 47 | default: 48 | return state 49 | } 50 | } 51 | 52 | const mockStore = createStore(counter) 53 | 54 | function eachTime (prevState, nextState, action) { 55 | if(action.type === 'INCREMENT_COUNTER') 56 | expect(nextState.count).to.be.above(prevState.count) 57 | if(action.type === 'DECREMENT_COUNTER') 58 | expect(nextState.count).to.be.below(prevState.count) 59 | 60 | expect(nextState.other).to.have.length.below(51) 61 | } 62 | 63 | describe('Redux', () => { 64 | it('things', () => { 65 | chaos(mockStore, [ 66 | {type: 'INCREMENT_COUNTER'}, 67 | {type: 'DECREMENT_COUNTER'}, 68 | {type: 'AYYY'}], 69 | eachTime) 70 | }) 71 | }) 72 | ``` 73 | 74 | Very simple. 75 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | function objToArray (obj) { 2 | var keys = Object.keys(obj) 3 | var finalArray = keys.map(function(v) { 4 | if(obj[v].type != undefined || typeof obj[v] === 'function') { 5 | return obj[v] 6 | } else { 7 | return objToArray(obj[v]) 8 | } 9 | }) 10 | return [].concat.apply([], finalArray) 11 | } 12 | 13 | exports.chaos = function chaos (store, actions, each) { 14 | actions = Array.isArray(actions) ? 15 | actions : 16 | objToArray(actions) 17 | for(var i = 0; i < 10000; i++) { 18 | var prevState = store.getState() 19 | var action = actions[Math.floor(Math.random() * actions.length)] 20 | store.dispatch(action) 21 | if(each != undefined) { 22 | each(prevState, store.getState(), action) 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "redux-chaos", 3 | "version": "1.1.0", 4 | "description": "Dead simple chaos/property testing", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "mocha --recursive", 8 | "test:watch": "mocha --recursive -w" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/thomas-jeepe/redux-chaos.git" 13 | }, 14 | "keywords": [ 15 | "chaos", 16 | "redux", 17 | "property", 18 | "test", 19 | "chaos", 20 | "chaos testing", 21 | "property testing" 22 | ], 23 | "author": "thomas-jeepe", 24 | "license": "ISC", 25 | "bugs": { 26 | "url": "https://github.com/thomas-jeepe/redux-chaos/issues" 27 | }, 28 | "homepage": "https://github.com/thomas-jeepe/redux-chaos#readme", 29 | "devDependencies": { 30 | "chai": "^3.4.1", 31 | "mocha": "^2.3.4" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /test/index.spec.js: -------------------------------------------------------------------------------- 1 | // ew es5 2 | var expect = require('chai').expect 3 | var chaos = require('../index').chaos 4 | var createStore = require('redux').createStore 5 | 6 | function counter(state, action) { 7 | switch (action.type) { 8 | case 'INCREMENT_COUNTER': 9 | return { 10 | count: state.count + 1, 11 | other: state.other 12 | } 13 | case 'DECREMENT_COUNTER': 14 | return { 15 | count: state.count - 1, 16 | other: state.other 17 | } 18 | case 'AYYY': 19 | return { 20 | count: state.count, 21 | other: state.other.length >= 50 ? state.other : state.other + 'lo' 22 | } 23 | default: 24 | return state 25 | } 26 | } 27 | 28 | var mockStore = createStore(counter, {count: 0, other: ''}) 29 | 30 | describe('chaos', () => { 31 | it('should handle array and not throw', () => { 32 | expect(() => chaos(mockStore, [{type: 'INCREMENT_COUNTER'}])).to.not.throw() 33 | }) 34 | it('should handle action object and not throw', () => { 35 | expect(() => chaos(mockStore, { 36 | increment: 37 | { 38 | type: 'INCREMENT_COUNTER' 39 | }, 40 | doot: {nested: {type: 'AYYY'}} 41 | })).to.not.throw() 42 | }) 43 | }) -------------------------------------------------------------------------------- /test/jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES6" 4 | } 5 | } --------------------------------------------------------------------------------