Skip to content

Commit ed09c2b

Browse files
committed
Adds tests for signAsync, and README
1 parent dce8af0 commit ed09c2b

File tree

2 files changed

+78
-19
lines changed

2 files changed

+78
-19
lines changed

Diff for: README.md

+37-19
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,10 @@ export default (initialState) => {
6161
It wraps your store's root reducer (to store history), `getState` (to return the
6262
current state in history) and `dispatch` (to connect to peers).
6363

64+
If you're using the redux devtools enhancer, it must come *after* the redux-
65+
scuttlebutt enhancer (or scuttlebutt will try to emit `PERFORM_ACTION` across
66+
the network)
67+
6468
## options
6569

6670
The store enhancer takes an options object, including the key
@@ -79,29 +83,43 @@ scuttlebutt({
7983

8084
// options passed through to the dispatcher (and their defaults)
8185
dispatcherOptions: {
82-
// the default will batch-reduce actions by the hundred, firing redux's
83-
// subscribe method on the last one, triggering the actual rendering on the
84-
// next animationFrame.
85-
// see: https://github.com/grrowl/redux-scuttlebutt/blob/master/src/dispatcher.js#L22
86-
customDispatch: getDelayedDispatch, // (dispatcher) => (action) => {}
87-
88-
// returns whether an action's type should be broadcast to the network.
89-
// (returns false for @@INIT and internal @@scuttlebutt-prefixed action
90-
// types)
91-
isGossipType: isGossipType, // (actionType) => bool
92-
93-
// if specified, the specified function must call the callback with false or
94-
// true, depending on whether the action is valid.
95-
verifyAsync, // (callback, action, getStateHistory) => {}
96-
},
86+
customDispatch: function getDelayedDispatch(dispatcher) {
87+
return function (action) {
88+
// the default will batch-reduce actions by the hundred, firing redux's
89+
// subscribe method on the last one, triggering the actual rendering on
90+
// the next animationFrame.
91+
// see: https://github.com/grrowl/redux-scuttlebutt/blob/master/src/dispatcher.js#L22
92+
}
93+
},
94+
95+
isGossipType: function(actionType) {
96+
// returns a boolean representing whether an action's type should be
97+
// broadcast to the network.
98+
// (by default, returns false for actions prefixed with @@, such as @@INIT
99+
// and internal @@scuttlebutt-prefixed action types)
100+
},
101+
102+
verifyAsync: function(callback, action, getStateHistory) {
103+
// if specified, the verifyAsync function must call callback(false) if the
104+
// action is invalid, or callback(true) if the action is valid.
105+
// getStateHistory() will return an array of ordered updates
106+
},
107+
108+
signAsync: function(callback, action, getStateHistory) {
109+
// if specified, the signAsync will be called for every locally dispatched
110+
// action. must call callback(action) and can mutate the action if
111+
// desired.
112+
// getStateHistory() will return an array of ordered updates
113+
},
114+
}
97115
})
98116
```
99117

100-
### verifyAsync
118+
### verifyAsync & signAsync
101119

102-
The dispatcher option `verifyAsync` allows you to filter actions dispatched or
103-
gossiped about through scuttlebutt. You could validate an action's contents
104-
against a cryptographic signature, or rate limit, or an arbitrary rule:
120+
The dispatcher option `verifyAsync` allows you to filter remote actions
121+
dispatched or gossiped about through scuttlebutt. You could validate an action's
122+
contents against a cryptographic signature, or rate limit, or an arbitrary rule:
105123

106124
```js
107125
import { UPDATE_ACTION } from 'redux-scuttlebutt'

Diff for: test/dispatcher.spec.js

+41
Original file line numberDiff line numberDiff line change
@@ -111,11 +111,52 @@ tape('dispatcher({ verifyAsync })', function (t) {
111111

112112
setTimeout(() => {
113113
t.ok(dispatch.calledTwice, 'called dispatch twice')
114+
114115
// first call
115116
t.equal(dispatch.getCall(0).args[0].payload, valid[0], 'called dispatch with valid action 1')
117+
116118
// second call
117119
t.equal(dispatch.getCall(1).args[0].payload, valid[1], 'called dispatch with valid action 2')
120+
118121
t.equal(getState.callCount, 4, 'getState was called for each getHistory')
119122
t.end()
120123
}, 20)
121124
})
125+
126+
tape('dispatcher({ signAsync })', function (t) {
127+
const
128+
payloads = ['what', 'up'],
129+
signAsync = (callback, action, getHistory) => {
130+
t.ok(Array.isArray(getHistory()), 'getHistory() returns an array')
131+
132+
setTimeout(() => {
133+
// payloads containing 'e' are invalid
134+
t.ok(payloads.includes(action.payload), 'payload is in payloads')
135+
callback({ ...action, signed: true })
136+
}, 5)
137+
},
138+
dispatcher = new Dispatcher({ signAsync }),
139+
dispatch = spy(),
140+
wrappedDispatch = dispatcher.wrapDispatch(dispatch),
141+
getState = spy(() => []) // becomes getHistory, returns array
142+
143+
dispatcher.wrapGetState(getState)
144+
145+
wrappedDispatch(createAction(payloads[0]))
146+
wrappedDispatch(createAction(payloads[1]))
147+
148+
setTimeout(() => {
149+
t.ok(dispatch.calledTwice, 'called dispatch twice')
150+
151+
// first call
152+
t.equal(dispatch.getCall(0).args[0].payload, payloads[0], 'called dispatch with valid action 1')
153+
t.equal(dispatch.getCall(0).args[0].signed, payloads[0], 'called dispatch with signed action 1')
154+
155+
// second call
156+
t.equal(dispatch.getCall(1).args[0].payload, payloads[1], 'called dispatch with valid action 2')
157+
t.equal(dispatch.getCall(1).args[0].signed, payloads[0], 'called dispatch with signed action 2')
158+
159+
t.equal(getState.callCount, 2, 'getState was called for each getHistory')
160+
t.end()
161+
}, 20)
162+
})

0 commit comments

Comments
 (0)