UNPKG

11 kBJavaScriptView Raw
1import { expect, time } from '../test';
2import { Subject } from 'rxjs';
3import { Store } from '.';
4const initial = { count: 0, foo: { list: [] } };
5describe('Store', () => {
6 describe('lifecycle', () => {
7 it('constructs', () => {
8 const store = Store.create({ initial });
9 expect(store.isDisposed).to.eql(false);
10 expect(store.state).to.not.equal(initial);
11 expect(store.state).to.eql(initial);
12 });
13 it('disposes', () => {
14 const store = Store.create({ initial });
15 let count = 0;
16 store.dispose$.subscribe(() => count++);
17 store.dispose();
18 store.dispose();
19 expect(store.isDisposed).to.eql(true);
20 expect(count).to.eql(1);
21 });
22 it('takes event$ at creation', () => {
23 const event$ = new Subject();
24 const store = Store.create({ initial, event$ });
25 expect(store._event$).to.equal(event$);
26 });
27 });
28 describe('state', () => {
29 it('returns new immutable object from [state] property', () => {
30 const store = Store.create({ initial });
31 const state1 = store.state;
32 const state2 = store.state;
33 expect(store.state).to.eql(initial);
34 expect(store.state).to.not.equal(initial);
35 expect(state1).to.eql(store.state);
36 expect(state1).to.eql(initial);
37 expect(state1).to.not.equal(state2);
38 expect(store.state).to.not.equal(initial);
39 });
40 });
41 describe('dispatch', () => {
42 it('returns the state object', () => {
43 const state = Store.create({ initial });
44 const res = state.dispatch({ type: 'TEST/increment', payload: { by: 1 } });
45 expect(res).to.equal(res);
46 });
47 it('fires dispatch event', () => {
48 const store = Store.create({ initial });
49 const events = [];
50 store.event$.subscribe((e) => events.push(e));
51 store.dispatch({ type: 'TEST/increment', payload: { by: 1 } });
52 store.dispatch({ type: 'TEST/decrement', payload: { by: 2 } });
53 expect(events.length).to.eql(2);
54 expect(events[0].type).to.eql('TEST/increment');
55 expect(events[1].type).to.eql('TEST/decrement');
56 });
57 it('fires (via injected event$)', () => {
58 const event$ = new Subject();
59 const store = Store.create({ initial, event$ });
60 const events = [];
61 store.event$.subscribe((e) => events.push(e));
62 event$.next({ type: 'TEST/increment', payload: { by: 1 } });
63 event$.next({ type: 'TEST/decrement', payload: { by: 2 } });
64 expect(events.length).to.eql(2);
65 expect(events[0].type).to.eql('TEST/increment');
66 expect(events[1].type).to.eql('TEST/decrement');
67 });
68 it('returns copy of the current state object on event', () => {
69 const store = Store.create({ initial });
70 const states = [];
71 store.on('TEST/increment').subscribe((e) => {
72 states.push(e.state);
73 states.push(e.state);
74 });
75 store.dispatch({ type: 'TEST/increment', payload: { by: 1 } });
76 expect(states.length).to.eql(2);
77 expect(states[0]).to.eql(store.state);
78 expect(states[1]).to.eql(store.state);
79 expect(states[0]).to.not.equal(store.state);
80 expect(states[1]).to.not.equal(store.state);
81 expect(states[0]).to.not.equal(states[1]);
82 });
83 it('changes the current state (via {...object})', () => {
84 const store = Store.create({ initial });
85 expect(store.state.count).to.eql(0);
86 store.on('TEST/increment').subscribe((e) => {
87 const count = e.state.count + e.payload.by;
88 const next = Object.assign(Object.assign({}, e.state), { count });
89 e.change(next);
90 });
91 store.on('TEST/decrement').subscribe((e) => {
92 const count = e.state.count - e.payload.by;
93 const next = Object.assign(Object.assign({}, e.state), { count });
94 e.change(next);
95 });
96 store.dispatch({ type: 'TEST/increment', payload: { by: 1 } });
97 expect(store.state.count).to.eql(1);
98 store.dispatch({ type: 'TEST/decrement', payload: { by: 2 } });
99 expect(store.state.count).to.eql(-1);
100 });
101 it('changes the current state (via immutable function)', () => {
102 const store = Store.create({
103 initial: Object.assign(Object.assign({}, initial), { bar: { msg: 'hello' } }),
104 });
105 const before = store.state;
106 expect(before.count).to.eql(0);
107 store.on('TEST/increment').subscribe((e) => {
108 e.change((draft) => {
109 draft.count += e.payload.by;
110 });
111 });
112 store.on('TEST/changeFoo').subscribe((e) => {
113 e.change((draft) => {
114 draft.foo.list.push(123);
115 const foo = draft.foo;
116 foo.msg = 'hello';
117 });
118 });
119 store.dispatch({ type: 'TEST/increment', payload: { by: 1 } });
120 const after1 = store.state;
121 expect(before).to.not.equal(after1);
122 expect(after1.count).to.eql(1);
123 expect(after1.foo).to.equal(before.foo);
124 expect(after1.bar).to.equal(before.bar);
125 store.dispatch({ type: 'TEST/changeFoo', payload: {} });
126 const after2 = store.state;
127 expect(after2.foo.list).to.eql([123]);
128 expect(after2.foo.msg).to.eql('hello');
129 expect(after2).to.not.equal(after1);
130 expect(after2.foo).to.not.equal(after1.foo);
131 expect(after2.foo.list).to.not.equal(after1.foo.list);
132 });
133 it('fires [changing] event', () => {
134 const store = Store.create({ initial });
135 const events = [];
136 store.changing$.subscribe((e) => events.push(e));
137 store.on('TEST/increment').subscribe((e) => e.change(e.state));
138 store
139 .on('TEST/changeFoo')
140 .subscribe((e) => e.change((draft) => (draft.foo.list = [1, 2, 3])));
141 store.dispatch({ type: 'TEST/increment', payload: { by: 1 } });
142 expect(events.length).to.eql(1);
143 expect(events[0].isCancelled).to.eql(false);
144 expect(events[0].change.type).to.eql('TEST/increment');
145 store.dispatch({ type: 'TEST/changeFoo', payload: {} });
146 expect(events.length).to.eql(2);
147 expect(events[1].isCancelled).to.eql(false);
148 expect(events[1].change.type).to.eql('TEST/changeFoo');
149 });
150 it('cancels change', () => {
151 const store = Store.create({ initial });
152 let cancel = false;
153 store.changing$.subscribe((e) => {
154 if (cancel) {
155 e.cancel();
156 }
157 });
158 store.on('TEST/increment').subscribe((e) => {
159 if (e.payload.by > 0) {
160 const count = e.state.count + e.payload.by;
161 const next = Object.assign(Object.assign({}, e.state), { count });
162 e.change(next);
163 }
164 });
165 store
166 .on('TEST/changeFoo')
167 .subscribe((e) => e.change((draft) => (draft.foo.list = [1, 2, 3])));
168 expect(store.state.count).to.eql(0);
169 store.dispatch({ type: 'TEST/increment', payload: { by: 1 } });
170 expect(store.state.count).to.eql(1);
171 cancel = true;
172 store.dispatch({ type: 'TEST/increment', payload: { by: 99 } });
173 expect(store.state.count).to.eql(1);
174 store.dispatch({ type: 'TEST/changeFoo', payload: {} });
175 expect(store.state.foo.list).to.eql([]);
176 cancel = false;
177 store.dispatch({ type: 'TEST/changeFoo', payload: {} });
178 expect(store.state.foo.list).to.eql([1, 2, 3]);
179 });
180 it('fires [changed] event', () => {
181 const store = Store.create({ initial });
182 const events = [];
183 store.changed$.subscribe((e) => events.push(e));
184 store.on('TEST/increment').subscribe((e) => {
185 if (e.payload.by > 0) {
186 const count = e.state.count + e.payload.by;
187 const next = Object.assign(Object.assign({}, e.state), { count });
188 e.change(next);
189 }
190 });
191 store.dispatch({ type: 'TEST/increment', payload: { by: 90 } });
192 store.dispatch({ type: 'TEST/increment', payload: { by: 0 } });
193 store.dispatch({ type: 'TEST/increment', payload: { by: 2 } });
194 expect(events.length).to.eql(2);
195 const change1 = events[0];
196 const change2 = events[1];
197 expect(change1.type).to.eql('TEST/increment');
198 expect(change1.event.type).to.eql('TEST/increment');
199 expect(change1.event.payload.by).to.eql(90);
200 expect(change2.type).to.eql('TEST/increment');
201 expect(change2.event.type).to.eql('TEST/increment');
202 expect(change2.event.payload.by).to.eql(2);
203 expect(change1.from.count).to.eql(0);
204 expect(change1.to.count).to.eql(90);
205 expect(change2.from.count).to.eql(90);
206 expect(change2.to.count).to.eql(92);
207 });
208 });
209 describe('epics', () => {
210 it('dispatches a follow-on event (sync)', () => {
211 const store = Store.create({ initial });
212 const events = [];
213 store.event$.subscribe((e) => events.push(e));
214 store.on('TEST/increment').subscribe((e) => {
215 e.dispatch({ type: 'TEST/decrement', payload: { by: 2 } });
216 });
217 store.dispatch({ type: 'TEST/increment', payload: { by: 1 } });
218 expect(events.length).to.eql(2);
219 expect(events[0].type).to.eql('TEST/increment');
220 expect(events[1].type).to.eql('TEST/decrement');
221 });
222 it('dispatches a follow-on event (async)', async () => {
223 const store = Store.create({ initial });
224 const events = [];
225 store.event$.subscribe((e) => events.push(e));
226 store.on('TEST/increment').subscribe(async (e) => {
227 await time.wait(3);
228 e.dispatch({ type: 'TEST/decrement', payload: { by: 2 } });
229 });
230 expect(events.length).to.eql(0);
231 store.dispatch({ type: 'TEST/increment', payload: { by: 1 } });
232 expect(events.length).to.eql(1);
233 await time.wait(10);
234 expect(events.length).to.eql(2);
235 });
236 });
237});