Comparing version
@@ -10,3 +10,4 @@ { | ||
"parserOptions": { | ||
"sourceType": "module" | ||
"sourceType": "module", | ||
"ecmaVersion": "2017" | ||
}, | ||
@@ -13,0 +14,0 @@ "rules": { |
@@ -1,2 +0,2 @@ | ||
/*! autodux v3.0.0 by Eric Elliott */ | ||
/*! autodux v3.0.1 by Eric Elliott */ | ||
var get = require('lodash/fp/get'); | ||
@@ -3,0 +3,0 @@ var capitalize = require('lodash/upperFirst'); |
@@ -1,2 +0,2 @@ | ||
/*! autodux v3.0.0 by Eric Elliott */ | ||
/*! autodux v3.0.1 by Eric Elliott */ | ||
'use strict'; | ||
@@ -3,0 +3,0 @@ |
{ | ||
"name": "autodux", | ||
"version": "3.0.0", | ||
"version": "3.0.1", | ||
"description": "Automate the Redux boilerplate.", | ||
@@ -33,2 +33,3 @@ "browser": "dist/index.js", | ||
"prepublish": "^1.1.3", | ||
"riteway": "^2.0.3", | ||
"tap-colorize": "1.2.0", | ||
@@ -35,0 +36,0 @@ "tape": "4.7.0", |
665
src/test.js
@@ -1,2 +0,3 @@ | ||
const test = require('tape'); | ||
const { describe } = require('riteway'); | ||
const autodux = require('./'); | ||
@@ -27,320 +28,337 @@ const id = autodux.id; | ||
test('autodux().slice', assert => { | ||
const msg = 'should have the correct string value'; | ||
describe('autodux().slice', async should => { | ||
const { assert } = should(); | ||
const actual = createDux().slice; | ||
const expected = 'counter'; | ||
assert.same(actual, expected, msg); | ||
assert.end(); | ||
assert({ | ||
given: 'autodux is called with args', | ||
should: 'have the correct string value', | ||
actual: createDux().slice, | ||
expected: 'counter', | ||
}); | ||
}); | ||
test('autodux().actions', assert => { | ||
const msg = 'should contain action creators'; | ||
describe('autodux().actions', async should => { | ||
const { assert } = should(); | ||
const actual = Object.keys(createDux().actions); | ||
const expected = ['setCounter', 'increment', 'decrement', 'multiply']; | ||
assert({ | ||
given: 'autodux is called with args', | ||
should: 'contain action creators', | ||
actual: Object.keys(createDux().actions), | ||
expected: ['setCounter', 'increment', 'decrement', 'multiply'] | ||
}); | ||
assert.same(actual, expected, msg); | ||
assert.end(); | ||
}); | ||
{ | ||
const { actions } = createDux(); | ||
const actual = [ | ||
actions.increment(), | ||
actions.decrement(), | ||
actions.multiply(2) | ||
]; | ||
const expected = [ | ||
{ type: 'counter/increment', payload: undefined }, | ||
{ type: 'counter/decrement', payload: undefined }, | ||
{ type: 'counter/multiply', payload: 2 }, | ||
]; | ||
test('autodux().actions', assert => { | ||
const msg = 'should produce correct action objects'; | ||
assert({ | ||
given: 'autodux is called with args', | ||
should: 'produce correct action objects', | ||
actual, | ||
expected | ||
}); | ||
} | ||
const { actions } = createDux(); | ||
{ | ||
const { | ||
actions: { | ||
setCounter, | ||
increment, | ||
decrement, | ||
multiply | ||
} | ||
} = createDux(); | ||
const actual = [ | ||
actions.increment(), | ||
actions.decrement(), | ||
actions.multiply(2) | ||
]; | ||
const actual = [ | ||
setCounter.type, | ||
increment.type, | ||
decrement.type, | ||
multiply.type | ||
]; | ||
const expected = [ | ||
{ type: 'counter/increment', payload: undefined }, | ||
{ type: 'counter/decrement', payload: undefined }, | ||
{ type: 'counter/multiply', payload: 2 }, | ||
]; | ||
const expected = [ | ||
'counter/setCounter', | ||
'counter/increment', | ||
'counter/decrement', | ||
'counter/multiply' | ||
]; | ||
assert.same(actual, expected, msg); | ||
assert.end(); | ||
assert({ | ||
given: 'autodux is called with args', | ||
should: 'produce namespaced action type constants', | ||
actual, | ||
expected | ||
}); | ||
} | ||
}); | ||
test('autodux().actions', assert => { | ||
const msg = 'should produce namespaced action type constants'; | ||
describe('autodux().reducer', async should => { | ||
const { assert } = should(); | ||
const { | ||
actions: { | ||
setCounter, | ||
increment, | ||
decrement, | ||
multiply | ||
} | ||
} = createDux(); | ||
{ | ||
const { | ||
actions: { | ||
increment, | ||
decrement | ||
}, | ||
reducer, | ||
initial | ||
} = createDux(); | ||
const actual = [ | ||
setCounter.type, | ||
increment.type, | ||
decrement.type, | ||
multiply.type | ||
]; | ||
const actions = [ | ||
increment(), | ||
increment(), | ||
increment(), | ||
decrement() | ||
]; | ||
const expected = [ | ||
'counter/setCounter', | ||
'counter/increment', | ||
'counter/decrement', | ||
'counter/multiply' | ||
]; | ||
assert({ | ||
given: 'a reducer', | ||
should: 'switch correctly', | ||
actual: actions.reduce(reducer, initial), | ||
expected: 2 | ||
}); | ||
} | ||
assert.same(actual, expected, msg); | ||
assert.end(); | ||
}); | ||
{ | ||
const { | ||
actions: { | ||
increment, | ||
multiply | ||
}, | ||
reducer, | ||
initial | ||
} = createDux(); | ||
test('autodux().reducer', assert => { | ||
const msg = 'reducer should switch correctly'; | ||
const actions = [ | ||
increment(), | ||
increment(), | ||
multiply(2) | ||
]; | ||
const { | ||
actions: { | ||
increment, | ||
decrement | ||
}, | ||
reducer, | ||
initial | ||
} = createDux(); | ||
const actions = [ | ||
increment(), | ||
increment(), | ||
increment(), | ||
decrement() | ||
]; | ||
const actual = actions.reduce(reducer, initial); | ||
const expected = 2; | ||
assert.same(actual, expected, msg); | ||
assert.end(); | ||
assert({ | ||
given: 'a reducer', | ||
should: 'deliver action payloads', | ||
actual: actions.reduce(reducer, initial), | ||
expected: 4 | ||
}); | ||
} | ||
}); | ||
test('autodux().reducer', assert => { | ||
const msg = 'reducer should deliver action payloads'; | ||
describe('autodux().selectors', async should => { | ||
const { assert } = should(); | ||
const { getValue } = createDux().selectors; | ||
const { | ||
actions: { | ||
increment, | ||
multiply | ||
}, | ||
reducer, | ||
initial | ||
} = createDux(); | ||
{ | ||
assert({ | ||
given: 'a property and value', | ||
should: 'return a selector that knows its state slice', | ||
actual: getValue({ counter: 3 }), | ||
expected: 3 | ||
}); | ||
} | ||
const actions = [ | ||
increment(), | ||
increment(), | ||
multiply(2) | ||
]; | ||
{ | ||
const initial = { | ||
key1: 'value 1', | ||
key2: 'value 2' | ||
}; | ||
const store = { | ||
slice: initial | ||
}; | ||
const actual = actions.reduce(reducer, initial); | ||
const expected = 4; | ||
const { selectors: { getKey1, getKey2 } } = autodux({ | ||
slice: 'slice', | ||
initial | ||
}); | ||
assert.same(actual, expected, msg); | ||
assert.end(); | ||
}); | ||
const actual = { | ||
key1: getKey1(store), | ||
key2: getKey2(store) | ||
}; | ||
test('autodux().selectors', assert => { | ||
const msg = 'should return selector that knows its state slice'; | ||
const { getValue } = createDux().selectors; | ||
assert({ | ||
given: 'a property and value', | ||
should: 'expose a selector for each key in the initial state', | ||
actual, | ||
expected: initial | ||
}); | ||
} | ||
const actual = getValue({ counter: 3 }); | ||
const expected = 3; | ||
{ | ||
const initial = { | ||
userName: 'Anonymous', | ||
avatar: 'anonymous.png' | ||
}; | ||
const store = { | ||
user: initial | ||
}; | ||
assert.same(actual, expected, msg); | ||
assert.end(); | ||
}); | ||
const { | ||
selectors: { | ||
getUser | ||
} | ||
} = autodux({ | ||
slice: 'user', | ||
initial: { | ||
userName: 'Anonymous', | ||
avatar: 'anon.png' | ||
} | ||
}); | ||
test('autodux().selectors', assert => { | ||
const msg = 'should expose a selector for each key in initial state'; | ||
const initial = { | ||
key1: 'value 1', | ||
key2: 'value 2' | ||
}; | ||
const store = { | ||
slice: initial | ||
}; | ||
const { selectors: { getKey1, getKey2 } } = autodux({ | ||
slice: 'slice', | ||
initial | ||
}); | ||
assert({ | ||
given: 'selectors', | ||
should: 'expose a selector for the entire reducer state', | ||
actual: getUser(store), | ||
expected: initial | ||
}); | ||
} | ||
const actual = { | ||
key1: getKey1(store), | ||
key2: getKey2(store) | ||
}; | ||
const expected = initial; | ||
{ | ||
const { getStore } = createDux().selectors; | ||
assert.same(actual, expected, msg); | ||
assert.end(); | ||
assert({ | ||
given: 'a store', | ||
should: 'pass entire store as a second parameter to selectors', | ||
actual: getStore({ counter: 3, foo: 'bar' }), | ||
expected: { counter: 3, foo: 'bar' } | ||
}); | ||
} | ||
}); | ||
test('autodux().selectors slice selector', assert => { | ||
const msg = 'should expose a selector for the entire reducer state'; | ||
describe('autodux() action creators', async should => { | ||
const { assert } = should(); | ||
const initial = { | ||
userName: 'Anonymous', | ||
avatar: 'anonymous.png' | ||
}; | ||
const store = { | ||
user: initial | ||
}; | ||
{ | ||
const { | ||
selectors: { | ||
getUser | ||
} | ||
} = autodux({ | ||
slice: 'user', | ||
initial: { | ||
userName: 'Anonymous', | ||
avatar: 'anon.png' | ||
} | ||
}); | ||
const value = 'UserName'; | ||
const { actions } = autodux({ | ||
slice: 'emptyCreator', | ||
actions: { | ||
nothing: { | ||
reducer: x => x | ||
} | ||
} | ||
}); | ||
const actual = getUser(store); | ||
const expected = initial; | ||
const expected = { | ||
type: 'emptyCreator/nothing', | ||
payload: value | ||
}; | ||
assert.same(actual, expected, msg); | ||
assert.end(); | ||
}); | ||
assert({ | ||
given: 'no action creators', | ||
should: 'should default missing action creators to identity', | ||
actual: actions.nothing(value), | ||
expected | ||
}); | ||
} | ||
test('autodux().selectors', assert => { | ||
const msg = 'should pass entire store as a second parameter to selectors'; | ||
const { getStore } = createDux().selectors; | ||
{ | ||
const value = 'UserName'; | ||
const { actions } = autodux({ | ||
slice: 'emptyCreator', | ||
actions: { | ||
nothing: { | ||
reducer: x => x | ||
} | ||
} | ||
}); | ||
const actual = getStore({ counter: 3, foo: 'bar' }); | ||
const expected = { counter: 3, foo: 'bar' }; | ||
const expected = { | ||
type: 'emptyCreator/nothing', | ||
payload: value | ||
}; | ||
assert.same(actual, expected, msg); | ||
assert.end(); | ||
}); | ||
assert({ | ||
given: 'no reducer', | ||
should: 'default missing reducer to spread payload into state', | ||
actual: actions.nothing(value), | ||
expected | ||
}); | ||
} | ||
test('autodux() action creators', assert => { | ||
const msg = 'should default missing action creators to identity'; | ||
{ | ||
const initial = { a: 'a' }; | ||
const value = 'UserName'; | ||
const { actions } = autodux({ | ||
slice: 'emptyCreator', | ||
actions: { | ||
nothing: { | ||
const { reducer } = autodux({ | ||
initial, | ||
actions: { | ||
reducer: x => x | ||
} | ||
} | ||
}); | ||
}); | ||
const actual = actions.nothing(value); | ||
const expected = { | ||
type: 'emptyCreator/nothing', | ||
payload: value | ||
}; | ||
assert({ | ||
given: 'no args', | ||
should: 'return valid default state', | ||
actual: reducer(), | ||
expected: initial | ||
}); | ||
} | ||
assert.same(actual, expected, msg); | ||
assert.end(); | ||
}); | ||
{ | ||
test('autodux() action creators', assert => { | ||
const msg = | ||
'should default missing reducer to spread payload into state'; | ||
const { actions, reducer } = autodux({ | ||
slice: 'emptyCreator', | ||
initial: {c: 'c'}, | ||
actions: { | ||
nothing: { | ||
create: () => ({a: 'a', b: 'b'}) | ||
const { | ||
reducer, | ||
actions: { | ||
increment, | ||
decrement, | ||
multiply | ||
}, | ||
selectors: { | ||
getValue | ||
} | ||
} | ||
}); | ||
} = autodux({ | ||
// the slice of state your reducer controls | ||
slice: 'counter', | ||
const actual = reducer(undefined, actions.nothing()); | ||
const expected = { | ||
a: 'a', | ||
b: 'b', | ||
c: 'c' | ||
}; | ||
// The initial value of your reducer state | ||
initial: 0, | ||
assert.same(actual, expected, msg); | ||
assert.end(); | ||
}); | ||
// No need to implement switching logic -- it's | ||
// done for you. | ||
actions: { | ||
increment: state => state + 1, | ||
decrement: state => state - 1, | ||
multiply: { | ||
create: ({ by }) => by, | ||
reducer: (state, payload) => state * payload | ||
} | ||
}, | ||
test('Calling the reducer with no arguments', assert => { | ||
const msg = 'Should return valid default state'; | ||
const initial = { a: 'a' }; | ||
const { reducer } = autodux({ | ||
initial, | ||
actions: { | ||
reducer: x => x | ||
} | ||
}); | ||
const actual = reducer(); | ||
const expected = initial; | ||
assert.same(actual, expected, msg); | ||
assert.end(); | ||
}); | ||
test('Functions as action values', assert => { | ||
const msg = 'should use function as a reducer'; | ||
const { | ||
reducer, | ||
actions: { | ||
increment, | ||
decrement, | ||
multiply | ||
}, | ||
selectors: { | ||
getValue | ||
} | ||
} = autodux({ | ||
// the slice of state your reducer controls | ||
slice: 'counter', | ||
// The initial value of your reducer state | ||
initial: 0, | ||
// No need to implement switching logic -- it's | ||
// done for you. | ||
actions: { | ||
increment: state => state + 1, | ||
decrement: state => state - 1, | ||
multiply: { | ||
create: ({ by }) => by, | ||
reducer: (state, payload) => state * payload | ||
// No need to select the state slice -- it's done for you. | ||
selectors: { | ||
getValue: id | ||
} | ||
}, | ||
}); | ||
// No need to select the state slice -- it's done for you. | ||
selectors: { | ||
getValue: id | ||
} | ||
}); | ||
const state = [ | ||
increment(), | ||
increment(), | ||
increment(), | ||
decrement(), | ||
multiply({ by: 2 }) | ||
].reduce(reducer, undefined); | ||
const state = [ | ||
increment(), | ||
increment(), | ||
increment(), | ||
decrement(), | ||
multiply({ by: 2 }) | ||
].reduce(reducer, undefined); | ||
const actual = getValue({ counter: state }); | ||
const expected = 4; | ||
assert.same(actual, expected, msg); | ||
assert.end(); | ||
assert({ | ||
given: 'functions as action values', | ||
should: 'use function as a reducer', | ||
actual: getValue({ counter: state }), | ||
expected: 4 | ||
}); | ||
} | ||
}); | ||
test('autodux/assign(key)', assert => { | ||
const msg = | ||
'should set the key in the state to the payload value'; | ||
describe('autodux/assign(key)', async should => { | ||
const { assert } = should(); | ||
@@ -377,66 +395,77 @@ const { | ||
assert.same(actual, expected, msg); | ||
assert.end(); | ||
assert({ | ||
given: 'default actions (without action keys)', | ||
should: 'set the key in the state to the payload value', | ||
actual, | ||
expected, | ||
}); | ||
}); | ||
test('autodux/default actions (without actions key)', assert => { | ||
const msg = 'should create `set${slice}` to spread payload into state'; | ||
describe('autodux/default', async should => { | ||
const { assert } = should(); | ||
{ | ||
const { | ||
actions: { | ||
setUser | ||
}, | ||
reducer | ||
} = autodux({ | ||
slice: 'user', | ||
initial: { | ||
userName: 'Anonymous', | ||
avatar: 'anonymous.png' | ||
} | ||
}); | ||
const userName = 'Foo'; | ||
const avatar = 'foo.png'; | ||
const { | ||
actions: { | ||
setUser | ||
}, | ||
reducer | ||
} = autodux({ | ||
slice: 'user', | ||
initial: { | ||
userName: 'Anonymous', | ||
avatar: 'anonymous.png' | ||
} | ||
}); | ||
const userName = 'Foo'; | ||
const avatar = 'foo.png'; | ||
const actual = reducer(undefined, setUser({ userName, avatar })); | ||
const actual = reducer(undefined, setUser({ userName, avatar })); | ||
const expected = { | ||
userName, | ||
avatar | ||
}; | ||
const expected = { | ||
userName, | ||
avatar | ||
}; | ||
assert({ | ||
given: 'actions (without action keys)', | ||
should: 'create `set${slice}` to spread payload into state', | ||
actual, | ||
expected, | ||
}); | ||
} | ||
assert.same(actual, expected, msg); | ||
assert.end(); | ||
}); | ||
{ | ||
const { | ||
actions: { | ||
setUserName, | ||
setAvatar | ||
}, | ||
reducer | ||
} = autodux({ | ||
slice: 'user', | ||
initial: { | ||
userName: 'Anonymous', | ||
avatar: 'anonymous.png' | ||
} | ||
}); | ||
const userName = 'Foo'; | ||
const avatar = 'foo.png'; | ||
test('autodux/default actions (without actions key)', assert => { | ||
const msg = 'should create assign actions for each key in initial'; | ||
const actual = [ | ||
setUserName(userName), | ||
setAvatar(avatar) | ||
].reduce(reducer, undefined); | ||
const { | ||
actions: { | ||
setUserName, | ||
setAvatar | ||
}, | ||
reducer | ||
} = autodux({ | ||
slice: 'user', | ||
initial: { | ||
userName: 'Anonymous', | ||
avatar: 'anonymous.png' | ||
} | ||
}); | ||
const userName = 'Foo'; | ||
const avatar = 'foo.png'; | ||
const expected = { | ||
userName, | ||
avatar | ||
}; | ||
const actual = [ | ||
setUserName(userName), | ||
setAvatar(avatar) | ||
].reduce(reducer, undefined); | ||
const expected = { | ||
userName, | ||
avatar | ||
}; | ||
assert.same(actual, expected, msg); | ||
assert.end(); | ||
assert({ | ||
given: 'actions (without action keys)', | ||
should: 'create assignment actions for each key in initial', | ||
actual, | ||
expected, | ||
}); | ||
} | ||
}); |
55711
0.6%829
5.47%6
20%