UNPKG

11.2 kBPlain TextView Raw
1import { expect } from 'chai';
2import { component, ReduxApp, withId } from 'src';
3import { Component } from 'src/components';
4
5// tslint:disable:no-unused-expression
6
7describe(nameof(ReduxApp), () => {
8
9 describe('constructor', () => {
10
11 it("does not throw on null values", () => {
12
13 @component
14 class Root {
15 public value: string = null;
16 }
17
18 // create component tree
19 const app = new ReduxApp(new Root());
20 app.dispose();
21 });
22
23 it("components nested inside standard objects are constructed", () => {
24
25 @component
26 class Root {
27 public first = {
28 second: new Level2()
29 };
30 }
31
32 class Level2 {
33 public third = new Level3();
34 }
35
36 class Level3 {
37 public some = new ThisIsAComponent();
38 }
39
40 @component
41 class ThisIsAComponent {
42 public dispatchMe() {
43 /* noop */
44 }
45 }
46
47 // create component tree
48 const app = new ReduxApp(new Root());
49
50 expect(app.root.first.second.third.some).to.be.an.instanceOf(Component);
51
52 app.dispose();
53 });
54
55 it("handles pre-loaded state", () => {
56
57 @component
58 class Root {
59 public first = {
60 second: new Level2()
61 };
62 }
63
64 class Level2 {
65 public theComponent = new ThisIsAComponent();
66 }
67
68 @component
69 class ThisIsAComponent {
70
71 public value: string = 'before';
72
73 public changeValue() {
74 this.value = 'after';
75 }
76 }
77
78 const preLoadedState = {
79 first: {
80 second: {
81 theComponent: {
82 value: 'I am here!'
83 }
84 }
85 }
86 };
87
88 // create component tree
89 const root = new Root();
90 expect(root.first.second.theComponent).to.be.an.instanceOf(ThisIsAComponent);
91 expect(root.first.second.theComponent.value).to.eql('before');
92
93 // create the app
94 const app = new ReduxApp(root, undefined, preLoadedState);
95 expect(app.root.first.second.theComponent).to.be.an.instanceOf(Component);
96 expect(app.root.first.second.theComponent.value).to.eql('I am here!');
97
98 // verify state is updating
99 app.root.first.second.theComponent.changeValue();
100 expect(app.root.first.second.theComponent).to.be.an.instanceOf(Component);
101 expect(app.root.first.second.theComponent.value).to.eql('after');
102
103 app.dispose();
104 });
105 });
106
107 describe('updateState', () => {
108
109 it("component tree is not updated when 'updateState' options is turned off", () => {
110 @component
111 class App {
112 public num = 0;
113 public increment() {
114 this.num = this.num + 1;
115 }
116 }
117
118 const app = new ReduxApp(new App(), { updateState: false });
119
120 expect(app.root.num).to.eq(0);
121
122 app.root.increment();
123
124 expect(app.root.num).to.eq(0);
125
126 app.dispose();
127 });
128
129 it("store still updates when 'updateState' options is turned off", () => {
130
131 @component
132 class App {
133 public num = 0;
134 public increment() {
135 this.num = this.num + 1;
136 }
137 }
138
139 const app = new ReduxApp(new App(), { updateState: false });
140
141 expect(app.store.getState().num).to.eq(0);
142
143 app.root.increment();
144
145 expect(app.store.getState().num).to.eq(1);
146
147 app.dispose();
148 });
149
150 it('removes component properties that do not exists on the new state', () => {
151
152 // create the component
153 @component
154 class MyComponent {
155 public prop1: string = undefined;
156 public prop2: string = undefined;
157
158 public setAndRemove() {
159 delete this.prop1;
160 this.prop2 = 'hello';
161 }
162 }
163 const app = new ReduxApp(new MyComponent());
164
165 // test before
166 expect(app.root).to.haveOwnProperty('prop1');
167 expect(app.root).to.haveOwnProperty('prop2');
168
169 // test after
170 app.root.setAndRemove();
171 expect(app.root).to.not.haveOwnProperty('prop1');
172 expect(app.root).to.haveOwnProperty('prop2');
173
174 app.dispose();
175 });
176
177 it('does not remove component properties that exists on the new state but are undefined', () => {
178
179 // create the component
180 @component
181 class MyComponent {
182 public prop1: string = undefined;
183 public prop2: string = undefined;
184
185 public updateProp2Only() {
186 this.prop2 = 'hello';
187 }
188 }
189 const app = new ReduxApp(new MyComponent());
190
191 // test before
192 expect(app.root).to.haveOwnProperty('prop1');
193 expect(app.root).to.haveOwnProperty('prop2');
194
195 // test after
196 app.root.updateProp2Only();
197 expect(app.root).to.haveOwnProperty('prop1');
198 expect(app.root).to.haveOwnProperty('prop2');
199
200 app.dispose();
201 });
202
203 it("components nested inside standard objects are synced with the store's state", () => {
204
205 @component
206 class Root {
207 public first = {
208 second: new Level2()
209 };
210 }
211
212 class Level2 {
213 public third = new Level3();
214 }
215
216 class Level3 {
217 public some = new ThisIsAComponent();
218 }
219
220 @component
221 class ThisIsAComponent {
222
223 public value = 0;
224
225 public dispatchMe() {
226 this.value = 1;
227 }
228 }
229
230 // create component tree
231 const app = new ReduxApp(new Root());
232
233 // before dispatching
234 expect(app.root.first.second.third.some.value).to.eql(0);
235
236 // after dispatching
237 app.root.first.second.third.some.dispatchMe();
238 expect(app.root.first.second.third.some.value).to.eql(1);
239
240 app.dispose();
241 });
242
243 it("methods of components nested inside standard objects can be invoked multiple times", () => {
244
245 @component
246 class Root {
247 public first = {
248 second: new Level2()
249 };
250 }
251
252 class Level2 {
253 public third = new Level3();
254 }
255
256 class Level3 {
257 public counter = new Counter();
258 }
259
260 @component
261 class Counter {
262 public value = 0;
263 public increment() {
264 this.value = this.value + 1;
265 }
266 }
267
268 // create component tree
269 const app = new ReduxApp(new Root());
270
271 expect(app.root.first.second.third.counter.value).to.eql(0);
272 app.root.first.second.third.counter.increment();
273 expect(app.root.first.second.third.counter.value).to.eql(1);
274 app.root.first.second.third.counter.increment();
275 expect(app.root.first.second.third.counter.value).to.eql(2);
276 app.root.first.second.third.counter.increment();
277 expect(app.root.first.second.third.counter.value).to.eql(3);
278
279 app.dispose();
280 });
281
282 it("updates arrays and contained components correctly", () => {
283
284 @component
285 class App {
286 public parents = [new ParentComponent()];
287 }
288
289 @component
290 class ParentComponent {
291 public arr: Component1[] = [];
292
293 public push() {
294 this.arr = this.arr.concat(new Component1());
295 }
296
297 public pop() {
298 this.arr = this.arr.slice(0, this.arr.length - 1);
299 }
300
301 public assign() {
302 const newComp = new Component1();
303 newComp.value = 5;
304 this.arr = this.arr.map((val, index) => index === 0 ? newComp : val);
305 }
306
307 public updateOdds() {
308 this.arr.forEach((item, index) => {
309 if (index % 2 === 1) {
310 item.increment();
311 item.child.setMessage('hello_' + item.value);
312 }
313 });
314 }
315 }
316
317 @component
318 class Component1 {
319 public value = 0;
320
321 @withId
322 public child = new Component2();
323
324 public increment() {
325 this.value = this.value + 1;
326 }
327 }
328
329 @component
330 class Component2 {
331 public message = 'hello';
332 public setMessage(newMessage: string): void {
333 this.message = newMessage;
334 }
335 }
336
337 const app = new ReduxApp(new App());
338
339 // push
340
341 expect(app.root.parents[0].arr.length).to.eql(0);
342
343 app.root.parents[0].push();
344 app.root.parents[0].push();
345
346 expect(app.root.parents[0].arr.length).to.eql(2);
347
348 // dispatch
349
350 expect(app.root.parents[0].arr[0].child.message).to.eql('hello');
351 expect(app.root.parents[0].arr[1].child.message).to.eql('hello');
352
353 app.root.parents[0].updateOdds();
354
355 expect(app.root.parents[0].arr[0].child.message).to.eql('hello');
356 expect(app.root.parents[0].arr[1].child.message).to.eql('hello_1');
357
358 // pop
359
360 app.root.parents[0].pop();
361
362 expect(app.root.parents[0].arr.length).to.eql(1);
363 expect(app.root.parents[0].arr[0].child.message).to.eql('hello');
364
365 // assign
366
367 app.root.parents[0].push();
368 app.root.parents[0].assign();
369
370 expect(app.root.parents[0].arr.length).to.eql(2);
371 expect(app.root.parents[0].arr[0].value).to.eql(5);
372 expect(app.root.parents[0].arr[1].value).to.eql(0);
373 });
374 });
375});
\No newline at end of file