1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158 | 1x
1x
1x
1x
1x
1x
1x
1x
1x
1x
1x
1x
10x
1x
1x
1x
1x
1x
1x
1x
1x
1x
1x
1x
1x
1x
1x
1x
1x
1x
1x
1x
1x
1x
1x
1x
1x
1x
1x
1x
1x
4x
1x
1x
1x
1x
1x
1x
1x
2x
1x
4x
1x
1x
1x
1x
1x
1x
1x
1x
1x
1x
1x
1x
2x
1x
1x
1x
1x
1x
1x
1x
1x
1x
1x
1x
1x
1x
1x
1x
2x
1x
2x
2x
2x
2x
2x
1x
1x
2x
1x
2x
1x
1x
1x
1x
1x
1x
1x
2x
1x
| import Mocha from 'mocha'
import * as chai from 'chai'
import { Action } from './action'
import { Observer } from 'rxjs/Observer'
import { initialize } from './init'
import { Observable } from 'rxjs/Observable'
import { ReplaceableState } from './replaceable-state'
// use spies
chai.use(require('chai-spies-next'))
const { expect } = chai
describe('Action', () => {
class TestAction extends Action { }
class UnrelatedAction extends Action { }
beforeEach(() => {
initialize({})
})
it('should call a reducer function when an action is dispatched', () => {
const reducer = chai.spy()
new TestAction().subscribe(reducer, this)
new TestAction().dispatch()
expect(reducer).to.have.been.called.once
})
it('should call all reducer functions when an action is dispatched', () => {
const reducer1 = chai.spy()
const reducer2 = chai.spy()
new TestAction().subscribe(reducer1, this)
new TestAction().subscribe(reducer2, this)
new TestAction().dispatch()
expect(reducer1).to.have.been.called.once
expect(reducer2).to.have.been.called.once
})
it('should call reducer functions subscribed to an action when an action is dispatched', () => {
const reducer1 = chai.spy()
const reducer2 = chai.spy()
new TestAction().subscribe(reducer1, this)
new UnrelatedAction().subscribe(reducer2, this)
new TestAction().dispatch()
expect(reducer1).to.have.been.called.once
expect(reducer2).not.to.have.been.called.once
})
it('should call reducer function with state and payload', () => {
const reducer1 = chai.spy()
new TestAction().subscribe(reducer1, this)
const testAction = new TestAction()
testAction.dispatch()
expect(reducer1).to.have.been.called.with({}, testAction)
})
it('should merge the state returned by the reducer function into global state', () => {
// create a reducer function and subscribe to TestAction
const reducer1 = chai.spy(() => ({ testState: true }))
new TestAction().subscribe(reducer1, this)
// create an action
const testAction = new TestAction()
// update the state to { testState: true }
testAction.dispatch()
expect(reducer1).to.have.been.called.with({}, testAction)
// checking if updated is made available to second call
testAction.dispatch()
expect(reducer1).to.have.been.called.with({ testState: true }, testAction)
})
it('should partially merge the state returned by the reducer function into global state', () => {
// create a reducer function and subscribe to TestAction
const reducer1 = chai.spy(() => ({ testState: true }))
new TestAction().subscribe(reducer1, this)
// create a reducer function and subscribe to TestAction
const reducer2 = chai.spy(() => ({ anotherState: true }))
new UnrelatedAction().subscribe(reducer2, this)
// update the state to { testState: true }
const testAction = new TestAction()
testAction.dispatch()
expect(reducer1).to.have.been.called.with({}, testAction)
const unrelatedAction = new UnrelatedAction()
unrelatedAction.dispatch()
expect(reducer2).to.have.been.called.with({ testState: true }, unrelatedAction)
unrelatedAction.dispatch()
expect(reducer2).to.have.been.called.with({ testState: true, anotherState: true }, unrelatedAction)
})
it('should replace the entire state if reducer function returns replaceable state', () => {
// create a reducer function and subscribe to TestAction
const reducer1 = chai.spy(() => ({ testState: true }))
new TestAction().subscribe(reducer1, this)
// create a reducer function and subscribe to TestAction
const reducer2 = chai.spy(() => new ReplaceableState({ anotherState: true }))
new UnrelatedAction().subscribe(reducer2, this)
// update the state to { testState: true }
const testAction = new TestAction()
testAction.dispatch()
expect(reducer1).to.have.been.called.with({}, testAction)
const unrelatedAction = new UnrelatedAction()
unrelatedAction.dispatch()
expect(reducer2).to.have.been.called.with({ testState: true }, unrelatedAction)
unrelatedAction.dispatch()
expect(reducer2).to.have.been.called.with({ anotherState: true }, unrelatedAction)
})
it('should not throw an error if an action is called without any subscription', () => {
class SampleAction extends Action { }
expect(() => new SampleAction().dispatch()).not.to.throw()
})
it('should call the returned callback function with current state, if reducer returns a function', async () => {
initialize({ initialState: true })
class SampleAction extends Action { }
const callback = chai.spy(() => ({ testState: true }))
const reducer1 = chai.spy(() => {
return Observable.create((observer: Observer<any>) => {
setTimeout(() => {
observer.next(callback)
observer.complete()
}, 100)
observer.next({ reducerCalled: true })
})
})
new SampleAction().subscribe(reducer1, this)
const action1 = new SampleAction()
await action1.dispatch()
expect(reducer1).to.have.been.called.with({ initialState: true }, action1)
await action1.dispatch()
expect(callback).to.have.been.called.with({ initialState: true, reducerCalled: true })
expect(reducer1).to.have.been.called.with({ initialState: true, reducerCalled: true, testState: true }, action1)
})
it('should not fail even if the reducer function reject with an error', async () => {
class SampleAction extends Action { }
const reducer = chai.spy(() => Promise.reject('Simulated Error'))
new SampleAction().subscribe(reducer, this)
const errorHandler = chai.spy()
await new SampleAction().dispatch().then(() => undefined, errorHandler)
expect(errorHandler).to.have.been.called()
})
}) |