-
Notifications
You must be signed in to change notification settings - Fork 0
/
rss.xml
1549 lines (1481 loc) · 397 KB
/
rss.xml
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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[XKoji.dev]]></title><description><![CDATA[Website for all things XkojiMedia]]></description><link>https://www.xkoji.dev</link><generator>GatsbyJS</generator><lastBuildDate>Sun, 05 Mar 2023 00:25:50 GMT</lastBuildDate><item><title><![CDATA[Introducing Team collaboration - share queries and collections with Altair GraphQL (alpha)]]></title><description><![CDATA[Sometime last year, we announce the remote sync functionality. However this only allowed you to sync your collections across devices. Today…]]></description><link>https://www.xkoji.dev/blog/introducing-team-collaboration--share-queries-and-collections-with-altair-graphql/</link><guid isPermaLink="false">https://www.xkoji.dev/blog/introducing-team-collaboration--share-queries-and-collections-with-altair-graphql/</guid><pubDate>Sun, 05 Mar 2023 00:08:54 GMT</pubDate><content:encoded><p>Sometime last year, we announce the <a href="https://www.xkoji.dev/blog/altair-graphql-introducing-remote-sync-functionality---now-in-beta/" target="_blank" rel="nofollow">remote sync functionality</a>. However this only allowed you to sync your collections across devices.</p>
<p>Today we are thrilled to announce a new feature in Altair GraphQL Client: team collaboration (in alpha)! With this new feature, users can now easily share queries and collections with their team members, enabling more effective collaboration and streamlined workflows.</p>
<p>The new team sharing feature is currently in alpha and can be tested by enabling <a href="https://altairgraphql.dev/docs/features/settings-pane.html#enableexperimental-enable-experimental-features-in-altair-note-the-features-might-be-unstable" target="_blank" rel="nofollow">experimental mode</a> in Altair. Once enabled, users will be able to login and have access to a dashboard where they can create teams with team members to share queries and collections with. This allows for improved and seamless collaboration between team members.</p>
<p>Please note that this feature is still in alpha and is not yet fully tested or stable. We encourage users to use caution when testing this feature and to provide feedback on their experience. Your feedback will be essential in helping us refine and improve this feature as we move towards a full release.</p>
<p>While in alpha/beta phase, you can only create 1 team with 1 other team member, and you are allowed to have a maximum of 20 queries. These limits should be sufficient to get the feature tested without a huge cost incurred if something goes wrong. We will gradually increase these limits as we approach a full release.</p>
<p>NOTE: This feature is available to all platforms, and not just the desktop apps.</p>
<p>Looking forward to hear your thoughts and feedback!</p></content:encoded></item><item><title><![CDATA[Altair GraphQL introducing remote sync functionality - now in beta]]></title><description><![CDATA[We have released Altair GraphQL Client v5.0.0 recently and with it, comes a brand refresh, but also the beta release of the remote sync…]]></description><link>https://www.xkoji.dev/blog/altair-graphql-introducing-remote-sync-functionality---now-in-beta/</link><guid isPermaLink="false">https://www.xkoji.dev/blog/altair-graphql-introducing-remote-sync-functionality---now-in-beta/</guid><pubDate>Wed, 21 Sep 2022 17:31:14 GMT</pubDate><content:encoded><p>We have released Altair GraphQL Client v5.0.0 recently and with it, comes a brand refresh, but also the beta release of the remote sync functionality.</p>
<p>Traditionally, Altair has been delightful for developers working locally on their own, with the ability to store queries and collections to disk. Sharing these documents across devices and teammates by manually copying the files over is not a great experience as it requires a manual process, and the copied files can easily get out of sync.</p>
<p><span
class="gatsby-resp-image-wrapper"
style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1016px;"
>
<span
class="gatsby-resp-image-background-image"
style="padding-bottom: 42.66666666666667%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAABYlAAAWJQFJUiTwAAABX0lEQVQoz21RW07DMBD0YZHgBhwCxAE4ET8VHyDU0EfUSmnSPO0kTey8Bo+rVhC60mrj7Hp2ZixgQ0oJ3/cRBAG85RJRFPE3pmn6k79Da41MJQjlHrEKIZVElmYQp9Pp3ExTxHGMsizdeQ7IMMa4vrF9ow0O2R4f0Rs28RfSPMF2s4UYxxFd14HAHP4dc0AuSpLEzXadcQzrukbfDaiqCk3TQPCDktfrNXa7HTzPw+p79Y8lYxgGtG17BQ+kj7Sy9th2URSIj0cIDhM0DENkWebyEByQ5/kVbJxGV/u+d+x4Jpu20XaBdiy5gH1BdpTNmibWx2PsvLolmRcuthBYlQraaMea6STzgx4qpc5b7ZkXb70wJXPmCmjvjMOEoRtdnQZAsEnA1L4yZd56kAsg56jkAki2ZVtANilUm6HUOQQ9o34CSmvsHGietIdVW+/KWuH18xFPizu8vD/geXGPH89CtiokDfzpAAAAAElFTkSuQmCC'); background-size: cover; display: block;"
></span>
<img
class="gatsby-resp-image-image"
alt="Synced collection on device #1"
title="Synced collection on device #1"
src="/static/b48948318fc911b028bc2d51bb9e7175/f4281/synced-collection-1.png"
srcset="/static/b48948318fc911b028bc2d51bb9e7175/5a46d/synced-collection-1.png 300w,
/static/b48948318fc911b028bc2d51bb9e7175/0a47e/synced-collection-1.png 600w,
/static/b48948318fc911b028bc2d51bb9e7175/f4281/synced-collection-1.png 1016w"
sizes="(max-width: 1016px) 100vw, 1016px"
style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"
loading="lazy"
/>
</span>
<span
class="gatsby-resp-image-wrapper"
style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 942px;"
>
<span
class="gatsby-resp-image-background-image"
style="padding-bottom: 46.333333333333336%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAABYlAAAWJQFJUiTwAAABd0lEQVQoz2VSy3KCQBDkL/JQEAR5LiyIoKB4MElVrEr0kMr/f0pnehXLMocp2GW6p7sHK5j7CPwAVVmhWTVYNy36TY+6qs39wl88VACfmHmANE6htUZZlVLylHcrDC5NKlPYdr2Qddi0ayFcwpnamNnOrXh2nZnpn7seiO2GFQ6/DQ4/ImRoYfneHFTJ6XEYIVc5pq8TvDw9G/B9kZBkdKHzwqjUOkdVF1B5KqIyWNEiRBzFWIrlXb/FfjdgPwwYtjuxlBgVHMS+sbIkgUozuRdcp9AOGt7MRS4uDSGn0u7p6xsfb+84fh5xPp0NKZUQzEj4zLPc3DMWlSqkSSqVIBFRFGaRzKiUaYWAy0KbyqRxzDe4LuESTYBElHMxdMABWZKhkKhWyxoWZTI3NnNLVET7tOlMprIM+99SiOGZQkhI7LgLyxYQm/hRF4Uh4lJG8H2RhFlRPc9miUIYiRNaJoexHF7DHu0+Ej0S0upFBP+KAq7n336xP89KCYXLeeLWAAAAAElFTkSuQmCC'); background-size: cover; display: block;"
></span>
<img
class="gatsby-resp-image-image"
alt="Synced collection on device #2"
title="Synced collection on device #2"
src="/static/98a386136b592ea348461a328709ecff/f1901/synced-collection-2.png"
srcset="/static/98a386136b592ea348461a328709ecff/5a46d/synced-collection-2.png 300w,
/static/98a386136b592ea348461a328709ecff/0a47e/synced-collection-2.png 600w,
/static/98a386136b592ea348461a328709ecff/f1901/synced-collection-2.png 942w"
sizes="(max-width: 942px) 100vw, 942px"
style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"
loading="lazy"
/>
</span></p>
<p>Now with the newly added remote sync functionality, once a collection is synced, it is no longer local to the device but is accessible from any device where the account is logged in, and changes made to the collection becomes immediately available to the other devices as well. One thing that is not yet available though is the ability to share the documents to other people or within a team.</p>
<p>This feature is still in beta though, and so there will still be some kinks that need to be addressed before it becomes stable enough for General Availability. While in beta, the number of queries that can be created are limited to 25 which should be large enough to checkout this feature. As it becomes more stable, the limit would be gradually increased. We can’t wait to fully release this but we would need your help testing it and providing us with lots of feedback to improve it!</p>
<p><span
class="gatsby-resp-image-wrapper"
style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 466px;"
>
<span
class="gatsby-resp-image-background-image"
style="padding-bottom: 69%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAYAAAAvxDzwAAAACXBIWXMAABYlAAAWJQFJUiTwAAAB6klEQVQ4y5WTW2/aQBCF+RcFjD1783XXNrZJaAq5VG2f276XSCVq/v8/OJ1dIxIiQtWH0eDF++nMmeOJSBKIhCDisdvSonENXGWhpQIdzsf3xvJnUhFSy+UIWS1CaaUxeQt0lUNbtwFspD4DJNAigc4I5ZVA9VGgXLOQTxJFnp4HDt2A2tbvK2Sg0h7CsEHAKAOnJYyg88C+61FzPw+kF+ANK7wWKGqDgoEukww8XqBXCvsLCl9GtjcSec+9N3wmMVjlFRKSRYw4WhyBq34I/RLQlKPCcsUKV4TcGeR+KR5WZjkvoQqXnR0Vvr/l06V4cNYIpLlEpnjkaDbHw909fn7/McaGlXVt90+gMhT8y5c81dpAk0JXMdBfyIxBVZRvPLwwMp8pzmHpF9JxBksNm7KfWvyvh3QINnGwecNrNQKLNABTKQ6xif0YFF70Hnrgslmy8jRAJAnIQxdxDJnXkJkFyRLJNAoWJIvxC5ok8wRFL+A2IvzhgcumRZHm4Xn2YYr5dIYFe+39PiqWBsr2J5HzNaGIPXQsu/Xh5m3pNAC/ffmKz/cP2G42uNve4np1hdvNdrSBLRIkIavu5KPw0wYPPdQrVUKGsTsGPu2f8Phrh+c/z/x7j/3vPR53OxQcMe958DKKTmC+/gKqr5DUcrvg+gAAAABJRU5ErkJggg=='); background-size: cover; display: block;"
></span>
<img
class="gatsby-resp-image-image"
alt="Account login"
title="Account login"
src="/static/877599887079813879fde5451eb16ef4/fc1a1/account-login.png"
srcset="/static/877599887079813879fde5451eb16ef4/5a46d/account-login.png 300w,
/static/877599887079813879fde5451eb16ef4/fc1a1/account-login.png 466w"
sizes="(max-width: 466px) 100vw, 466px"
style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"
loading="lazy"
/>
</span></p>
<p>As with all other beta features, to enable this new feature, you should enable experimental features from the settings, and then you will have access to this feature (you can access the login dialog by clicking the icon at the bottom left corner). <strong>NOTE: It does not work in Mozilla or Chrome extensions just yet but support should be coming there soon.</strong> </p>
<p>Looking forward to hear your thoughts and feedback!</p></content:encoded></item><item><title><![CDATA[Altair GraphQL Client v4 now available]]></title><description><![CDATA[Altair v4 has been released! It is a mojor update that comes with several signifant changes as well as other small improvements and fixes in…]]></description><link>https://www.xkoji.dev/blog/altair-graphql-client-v4-now-available/</link><guid isPermaLink="false">https://www.xkoji.dev/blog/altair-graphql-client-v4-now-available/</guid><pubDate>Thu, 25 Mar 2021 22:45:23 GMT</pubDate><content:encoded><p>Altair v4 has been released! It is a mojor update that comes with several signifant changes as well as other small improvements and fixes in the application.</p>
<p>Starting with the JS framework itself, we have migrated from angular v9 to angular v11 (the latest version at the time of this release) <a href="https://github.com/imolorhe/altair/pull/1461" target="_blank" rel="nofollow">#1461</a>. This brings with it all the goodies from both <a href="https://blog.angular.io/version-10-of-angular-now-available-78960babd41" target="_blank" rel="nofollow">version 10</a> and <a href="https://blog.angular.io/version-11-of-angular-now-available-74721b7952f7" target="_blank" rel="nofollow">11</a>, which includes (among other things) a faster, more performant application overall. In the process we also updated <a href="https://ng.ant.design/" target="_blank" rel="nofollow">ant design framework</a> to v11, <a href="https://ngrx.io/" target="_blank" rel="nofollow">ngrx</a> to v11 and several other packages.</p>
<p>As part of the performance improvements, we migrated from using <a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage" target="_blank" rel="nofollow">localStorage</a> to store and manage data, and instead we store data in <a href="https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API" target="_blank" rel="nofollow">indexedDB</a> <a href="https://github.com/imolorhe/altair/pull/1483" target="_blank" rel="nofollow">#1483</a>. Given the synchronous nature of localStorage, it blocks the main thread whenever it stores data. We managed to improve this thread blocking issue by deferring writes using the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback" target="_blank" rel="nofollow"><code class="language-text">requestIdleCallback</code></a> API but that didn’t completely eliminate the blocking issue. It becomes even more obvious when dealing with very large schema setups. IndexedDB has an asynchronous API meaning it doesn’t block the thread when writing or reading from storage. This leads to a more fluid and consistent user experience… and <strong>a much BIGGER storage</strong> (now you can use Altair to load up that 1GB schema 😄)!</p>
<p>We introduced <a href="https://altair.sirmuel.design/docs/features/prerequest-scripts.html" target="_blank" rel="nofollow">Post request scripts</a> <a href="https://github.com/imolorhe/altair/pull/1467" target="_blank" rel="nofollow">#1467</a> which enables you perform logic on the result of your query. Assuming you want to run a mutation query to login your user and retrieve the auth token, using the post request script you can now retrieve the token from the response <code class="language-text">altair.response.body.data.login.token</code> and set it in the currently active environment variable <code class="language-text">altair.helpers.setEnvironment(&#39;auth_token&#39;, token, true)</code> and use the variable in the headers to authenticate subsequent requests! We also introduced response headers that come with the response from the server. This enables easier debugging with regards to the headers. <em>One important note about the response headers: the returned response headers in the browser clients (web app, browser extensions) are <a href="https://developers.google.com/web/updates/2015/03/introduction-to-fetch#response_types" target="_blank" rel="nofollow">limited</a>. However these response headers show up in the desktop apps.</em></p>
<p><span
class="gatsby-resp-image-wrapper"
style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1200px;"
>
<span
class="gatsby-resp-image-background-image"
style="padding-bottom: 57.00000000000001%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAABYlAAAWJQFJUiTwAAAByElEQVQoz22T64rbMBCF/ShtoPFVsiTL91uym9LdhUILhb5FfxT6/Kdn5HXiQn8MCtbomzNnJlGhexS2R90+oxu+oO0/IysG5GqAdhOUmVDoEaVd4PwVqpyQ8U6ZBbpc4OcZP36/4eefr/j+6xWRAA2TffOEohzD47To8Olcob8+YVhvfDhDmzlA5cyKHinzkrzFOfY4fajwkXE61Ygy1cGx0nX8BldfUboFhRlwTjxstWIa39C0LzDuQuDKc1OZ5h2SrCG0oYCWwZO/o5xq5INSK5KUypIKcVqH0GzLVc8BZtz6DrwEoHJUmHnESX3PlwJRRlhlLxjbG6ybQhW5kAR5uCsr7eUOLDR9bVfoZkZuR2R2DErlTRRnNToOovE3GPqiSiqmNwIVvx6tPk4ZVF7R036B6em76wkTIQTKY+slaQ4hbW7mb/E/oAwvZU4q9x0nXnWh9dBykrX3xGPItyNwj61lAnUX2tzsaY4edsGfADr4JLEDj0V2YMwtSNLHQAKQ4EiVXOCKJositlH57UGuh7B//yjkpC2XOwDfFR0jDEUWW9ttSUWt8ytNbpGXDP5jworswWEYv7BYHzyTfBFUsLjMQoB/AfSZXtFofbtpAAAAAElFTkSuQmCC'); background-size: cover; display: block;"
></span>
<img
class="gatsby-resp-image-image"
alt="altair with tabs"
title="altair with tabs"
src="/static/e07a58eb2c24abad469032dadc29bd01/c1b63/altair-v4.png"
srcset="/static/e07a58eb2c24abad469032dadc29bd01/5a46d/altair-v4.png 300w,
/static/e07a58eb2c24abad469032dadc29bd01/0a47e/altair-v4.png 600w,
/static/e07a58eb2c24abad469032dadc29bd01/c1b63/altair-v4.png 1200w,
/static/e07a58eb2c24abad469032dadc29bd01/d61c2/altair-v4.png 1800w,
/static/e07a58eb2c24abad469032dadc29bd01/97a96/altair-v4.png 2400w,
/static/e07a58eb2c24abad469032dadc29bd01/d9ed5/altair-v4.png 2880w"
sizes="(max-width: 1200px) 100vw, 1200px"
style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"
loading="lazy"
/>
</span></p>
<p>With all the new feature additions, we added tabs to the query and result sections to better manage the components. This also enables us have better management of the sections than was possible before. Now you can view the results of a subscription query even after running a subscription.</p>
<p><span
class="gatsby-resp-image-wrapper"
style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1200px;"
>
<span
class="gatsby-resp-image-background-image"
style="padding-bottom: 96%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAATCAYAAACQjC21AAAACXBIWXMAABYlAAAWJQFJUiTwAAACRElEQVQ4y62US28SURTH+VBGoBAbdNEaXdhFa0WrTfwYGm0R2/paGBduXNQuXJhIkS07A1hs60dAoBt5zot5Xmb+3nOH26YGaH2c5J9zucz9zf+cOTOR69cWsHw1icR8AvGZJGKxGa74qTw3N49U6gouRmOjvTGK8/PJFCI3FhZRyH3EzocdbG+/x+5uHvn8Z3zK5VAsFrH/rYrvh4c4ONhHdW8PXytlVMonKpdK4ppCoYB4YhaROyursB0Hju3Asi0wxsCGDB7zQGHbNlRVxWAw4DIxKTqdLi5EE4jcW70PXVdhmnTAgGHoQrquwbIstNtt1Go1fqADTdMwHA7h+77IJDJAUa83ODCJSPr2XQGQQJJcuy53zt3rOv1v8t+ucOp5ofsgCAScotk8Oh+QXCqKIkSld7tdcZOJwMWlW1OBNu8rlUugfr+PVqsl3FK5Y4FLN9MTgbbjQjMs9Hpd0UuCUiYouaU4Bh6dATQHfM/28FOx0WkTQBO9NAxDOJVlk9PQYXM6kPYYb75lGqJ/v4csVwIbjeZZJevhU+X9YkOfzyUfkdGoSBFQjs1UII2GyZ0Zlot3VYbXX1y8rXh4U/LQMoJjh3/8UBzuULEC9AY++qYvMvMlEKcfynmAVPK4CATtLxxKoCxP6gSKfwOOi7Fviqoq4mNAw6ppoWiP3gjGwg/AJMmb/qjXQ+ByegX/I2hWBTB56TIer61jbT0jsliPcibzBNmnG8hmN0Te3HqOLa7NkcL1M7x4+QoPHj5CdGYWvwCgXSlAqJTE2gAAAABJRU5ErkJggg=='); background-size: cover; display: block;"
></span>
<img
class="gatsby-resp-image-image"
alt="proxy settings in Altair"
title="proxy settings in Altair"
src="/static/c7dc793447d51f4e0a49468814a802f5/c1b63/altair-proxy.png"
srcset="/static/c7dc793447d51f4e0a49468814a802f5/5a46d/altair-proxy.png 300w,
/static/c7dc793447d51f4e0a49468814a802f5/0a47e/altair-proxy.png 600w,
/static/c7dc793447d51f4e0a49468814a802f5/c1b63/altair-proxy.png 1200w,
/static/c7dc793447d51f4e0a49468814a802f5/6569d/altair-proxy.png 1328w"
sizes="(max-width: 1200px) 100vw, 1200px"
style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"
loading="lazy"
/>
</span></p>
<p>On the desktop apps, you can now configure the proxy settings of Altair <a href="https://github.com/imolorhe/altair/pull/1475" target="_blank" rel="nofollow">#1475</a>. This includes choosing to use the system proxy settings, using a manual proxy setting or via a PAC script, or not using any proxy at all.</p>
<p>A new setting (<code class="language-text">response.hideExtensions</code>) has been added to allow hiding the extensions data that gets returned from some servers.</p>
<p>Other improvements include: using JetBrains mono as the default editor font, updating the variable highlighting and implementation in text inputs, several other bug fixes and minor improvements.</p>
<p>This was originally planned to be a minor version release (v3.3.0) but given the number of complex changes being introduced, it made sense to bump it up to a major release instead. Hope you guys enjoy this update and find it very useful.</p>
<p>If you experience any issue in this update or just want to holla, you can reach out to us on <a href="https://twitter.com/AltairGraphQL" target="_blank" rel="nofollow">twitter</a> or on <a href="https://github.com/imolorhe/altair" target="_blank" rel="nofollow">github</a>.</p>
<p>Ps: We are always open to contributions in various ways. You can learn more <a href="https://altair.sirmuel.design/docs/contributing.html" target="_blank" rel="nofollow">here</a>🚀 or donate to the project <a href="https://opencollective.com/altair/donate" target="_blank" rel="nofollow">here</a>🥰.</p>
<p>✌🏾</p></content:encoded></item><item><title><![CDATA[Theming in Altair GraphQL]]></title><description><![CDATA[I have been working on introducing more theming options in Altair GraphQL Client, as has been requested several times. Altair already had a…]]></description><link>https://www.xkoji.dev/blog/theming-in-altair-graphql/</link><guid isPermaLink="false">https://www.xkoji.dev/blog/theming-in-altair-graphql/</guid><pubDate>Fri, 18 Sep 2020 11:02:39 GMT</pubDate><content:encoded><p>I have been working on introducing more theming options in <a href="https://altair.sirmuel.design/" target="_blank" rel="nofollow">Altair GraphQL Client</a>, as has been requested several times. Altair already had a few customization options available for a while now (font size and font family), but the implementation wasn’t going to scale well for more customization options. So I decided to look for better solutions.</p>
<h2 id="css-in-js" style="position:relative;">CSS-in-JS</h2>
<p><a href="https://www.wikiwand.com/en/CSS-in-JS" target="_blank" rel="nofollow">CSS-in-JS</a> has been a popular solution for managing component styles in the React community for a while now with several CSS-in-JS solutions existing including <a href="https://styled-components.com/" target="_blank" rel="nofollow">styled-components</a>, <a href="https://emotion.sh/docs/introduction" target="_blank" rel="nofollow">emotion</a>, <a href="https://cssinjs.org/?v=v10.4.0" target="_blank" rel="nofollow">JSS</a>, and <a href="https://github.com/MicheleBertoli/css-in-js" target="_blank" rel="nofollow">many more</a>. This also comes with the advantage of having the styles managed by JavaScript, where we can dynamically modify the styles easily.</p>
<p>These benefits makes this a preferred solution to the theming problem. I decided to go with <strong><a href="https://emotion.sh/docs/introduction" target="_blank" rel="nofollow">emotion</a></strong> since it is one of the popular framework agnostic libraries.</p>
<h2 id="themeconfig-settings" style="position:relative;">themeConfig settings</h2>
<p>Altair can now be customized using <code class="language-text">themeConfig</code> in the settings. The interface can be found <a href="https://github.com/imolorhe/altair/blob/staging/packages/altair-app/src/app/services/theme/theme.ts#L32-L71" target="_blank" rel="nofollow">here</a>.</p>
<div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token punctuation">{</span>
colors<span class="token operator">:</span> <span class="token punctuation">{</span>
primary<span class="token operator">:</span> foundations<span class="token punctuation">.</span>colors<span class="token punctuation">.</span>green<span class="token punctuation">,</span>
secondary<span class="token operator">:</span> foundations<span class="token punctuation">.</span>colors<span class="token punctuation">.</span>blue<span class="token punctuation">,</span>
bg<span class="token operator">:</span> foundations<span class="token punctuation">.</span>colors<span class="token punctuation">.</span>white<span class="token punctuation">,</span>
offBg<span class="token operator">:</span> foundations<span class="token punctuation">.</span>colors<span class="token punctuation">.</span>lightGray<span class="token punctuation">,</span>
font<span class="token operator">:</span> foundations<span class="token punctuation">.</span>colors<span class="token punctuation">.</span>black<span class="token punctuation">,</span>
offFont<span class="token operator">:</span> foundations<span class="token punctuation">.</span>colors<span class="token punctuation">.</span>darkGray<span class="token punctuation">,</span>
border<span class="token operator">:</span> foundations<span class="token punctuation">.</span>colors<span class="token punctuation">.</span>gray<span class="token punctuation">,</span>
offBorder<span class="token operator">:</span> foundations<span class="token punctuation">.</span>colors<span class="token punctuation">.</span>lightGray<span class="token punctuation">,</span>
headerBg<span class="token operator">:</span> foundations<span class="token punctuation">.</span>colors<span class="token punctuation">.</span>white<span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
shadow<span class="token operator">:</span> <span class="token punctuation">{</span>
color<span class="token operator">:</span> foundations<span class="token punctuation">.</span>colors<span class="token punctuation">.</span>black<span class="token punctuation">,</span>
opacity<span class="token operator">:</span> <span class="token number">.1</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
editor<span class="token operator">:</span> <span class="token punctuation">{</span>
fontFamily<span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token keyword">default</span><span class="token operator">:</span> <span class="token string">'inherit'</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
fontSize<span class="token operator">:</span> foundations<span class="token punctuation">.</span>type<span class="token punctuation">.</span>fontSize<span class="token punctuation">.</span>body<span class="token punctuation">,</span>
colors<span class="token operator">:</span> <span class="token punctuation">{</span>
comment<span class="token operator">:</span> foundations<span class="token punctuation">.</span>colors<span class="token punctuation">.</span>darkGray<span class="token punctuation">,</span>
string<span class="token operator">:</span> foundations<span class="token punctuation">.</span>colors<span class="token punctuation">.</span>orange<span class="token punctuation">,</span>
number<span class="token operator">:</span> foundations<span class="token punctuation">.</span>colors<span class="token punctuation">.</span>orange<span class="token punctuation">,</span>
variable<span class="token operator">:</span> foundations<span class="token punctuation">.</span>colors<span class="token punctuation">.</span>black<span class="token punctuation">,</span>
keyword<span class="token operator">:</span> foundations<span class="token punctuation">.</span>colors<span class="token punctuation">.</span>blue<span class="token punctuation">,</span>
atom<span class="token operator">:</span> foundations<span class="token punctuation">.</span>colors<span class="token punctuation">.</span>black<span class="token punctuation">,</span>
attribute<span class="token operator">:</span> foundations<span class="token punctuation">.</span>colors<span class="token punctuation">.</span>green<span class="token punctuation">,</span>
property<span class="token operator">:</span> foundations<span class="token punctuation">.</span>colors<span class="token punctuation">.</span>blue<span class="token punctuation">,</span>
punctuation<span class="token operator">:</span> foundations<span class="token punctuation">.</span>colors<span class="token punctuation">.</span>blue<span class="token punctuation">,</span>
definition<span class="token operator">:</span> foundations<span class="token punctuation">.</span>colors<span class="token punctuation">.</span>orange<span class="token punctuation">,</span>
builtin<span class="token operator">:</span> foundations<span class="token punctuation">.</span>colors<span class="token punctuation">.</span>orange<span class="token punctuation">,</span>
cursor<span class="token operator">:</span> foundations<span class="token punctuation">.</span>colors<span class="token punctuation">.</span>blue<span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre></div>
<p>So to change the primary color of Altair to <span style="color: rebeccapurple;">rebeccapurple</span>, you can use the following in the settings:</p>
<div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token punctuation">{</span>
<span class="token comment">// ...</span>
<span class="token string">"themeConfig"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token string">"colors"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token string">"primary"</span><span class="token operator">:</span> <span class="token string">"rebeccapurple"</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre></div>
<p><img src="https://i.imgur.com/5zrqWb6.png" alt="Altair in rebeccapurple"></p>
<p>The selected base theme is dracula, and the <code class="language-text">themeConfig</code> is merged on top of the selected theme. This makes it easy t just customize parts of the theme, instead of having to customize every token.</p>
<h2 id="conclusion" style="position:relative;">Conclusion</h2>
<p>This config options should be available from v2.5.1. Now you can customize Altair to be consistent with the design style of your services for your users. The next steps after this would be the option of providing the theme customization via the plugin system.</p></content:encoded></item><item><title><![CDATA[Unit testing JavaScript Applications - Part 3 (Vue components)]]></title><description><![CDATA[We have talked about the basic unit testing concepts, why you should be writing unit tests, the approaches to writing unit testing, and we…]]></description><link>https://www.xkoji.dev/blog/unit-testing-javascript-applications---part-3/</link><guid isPermaLink="false">https://www.xkoji.dev/blog/unit-testing-javascript-applications---part-3/</guid><pubDate>Tue, 09 Jun 2020 21:23:47 GMT</pubDate><content:encoded><p>We have talked about the basic unit testing concepts, why you should be writing unit tests, the approaches to writing unit testing, and we have also written some unit tests for Vuex getters, actions and mutations. In this post, we would be looking at writing unit tests for Vue.js components. Previously <a href="https://www.xkoji.dev/blog/unit-testing-javascript-applications---part-2/#defining-test-cases" target="_blank" rel="nofollow">we described the things we need to consider for defining unit tests cases</a>. The same steps apply for writing tests for UI components:</p>
<h3 id="define-the-inputs-and-outputs-of-the-component-based-on-its-public-interface" style="position:relative;">Define the inputs and outputs of the component based on its public interface</h3>
<p>The inputs of Vue.js components include:</p>
<ul>
<li>user interaction (like a click, mouseover, keydown event)</li>
<li>props provided to the component</li>
<li>slots</li>
<li>data from Vuex (via state and getters)</li>
</ul>
<p>The output of Vue.js components include:</p>
<ul>
<li>the rendered HTML</li>
<li>emitted events</li>
<li>called Vuex methods (actions and mutations)</li>
</ul>
<p>You might have noticed that this doesn’t include things like checking that a method on the Vue component was called, since those are only known to the Vue component and aren’t called by external parties. They are not part of the public interface of the component.</p>
<h3 id="consider-the-dependencies-you-need-to-mock" style="position:relative;">Consider the dependencies you need to mock</h3>
<p>As much as possible, the inputs and outputs of the component are where your focus should be when writing unit tests for your component. However, the component could have dependencies like using some Vue <a href="https://vuejs.org/v2/cookbook/adding-instance-properties.html" target="_blank" rel="nofollow">instance prototype methods</a> that were defined elsewhere, importing a module dependency to handle some operation, using globally registered sub components, etc.</p>
<p>Just like in other cases, the dependencies are not part of the functionality of the component you’re testing, so they should be mocked so you can focus on testing just your component. Knowing what to mock unfortunately requires knowledge about the internals of the component, which would mean that the test is more tightly coupled with the internal implementation of the component, which makes the tests less maintainable since any change to the internals of the component would always require an update to the unit tests as well. Luckily we would be using jest, so mocking dependencies would be easier.</p>
<h3 id="consider-all-the-logical-execution-flows-within-the-component" style="position:relative;">Consider all the logical execution flows within the component</h3>
<p>For a very robust solution, you should consider all the logical execution flows within your component, and create test cases to cover any of the missing flows. As <a href="https://www.xkoji.dev/blog/unit-testing-javascript-applications---part-2/#create-extra-test-cases-covering-all-the-logical-execution-flows" target="_blank" rel="nofollow">mentioned before</a>, the logical flows are usually created by <code class="language-text">if</code>, <code class="language-text">switch</code>, and other short circuiting operations within the code. For Vue.js components, these can be created in the JS logic of the component, as well as in the component template using the <a href="https://vuejs.org/v2/guide/conditional.html" target="_blank" rel="nofollow"><code class="language-text">v-if</code></a> directive and other conditional rendering directives.</p>
<h2 id="testing-tools" style="position:relative;">Testing Tools</h2>
<p>As we have already seen, we use <a href="https://jestjs.io/" target="_blank" rel="nofollow">Jest</a> testing framework for writing and running our unit tests. However Jest on its own can only carry us so far. UI components require extra specialized testing tools to be able to easily write tests for them. For Vue.js components, we use the <a href="https://vue-test-utils.vuejs.org/" target="_blank" rel="nofollow">Vue test utils</a> which is maintained by the Vue.js team. It provides us with the ability to mount the Vue.js component, simulate user interactions, easily mock global plugins and mixins as well as mocking sub components, props, data, methods, etc. It also enables us to be able to check the rendered HTML as well as checking that certain events were emitted. We would use it within the Jest test cases.</p>
<p>For a simple example, consider the following Vue component. It has a button that increments the counter, and displays the counter value.</p>
<div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token comment">// counter.js</span>
<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token punctuation">{</span>
template<span class="token operator">:</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">
&lt;div>
&lt;span class="count">{{ count }}&lt;/span>
&lt;button @click="increment">Increment&lt;/button>
&lt;/div>
</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">,</span>
<span class="token function">data</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> <span class="token punctuation">{</span>
count<span class="token operator">:</span> <span class="token number">0</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
methods<span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token function">increment</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">this</span><span class="token punctuation">.</span>count<span class="token operator">++</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre></div>
<p>To test the component, you need to “mount” the component, creating a vue test utils <a href="https://vue-test-utils.vuejs.org/api/wrapper/#properties" target="_blank" rel="nofollow">wrapper</a> which gives you several methods for manipulating, traversing and querying the underlying Vue component instance.</p>
<div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token keyword">import</span> <span class="token punctuation">{</span> mount <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@vue/test-utils'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> Counter <span class="token keyword">from</span> <span class="token string">'./counter'</span><span class="token punctuation">;</span>
<span class="token function">it</span><span class="token punctuation">(</span><span class="token string">'should have a button'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token keyword">const</span> wrapper <span class="token operator">=</span> <span class="token function">mount</span><span class="token punctuation">(</span>Counter<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">expect</span><span class="token punctuation">(</span>wrapper<span class="token punctuation">.</span><span class="token function">exists</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toBe</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">expect</span><span class="token punctuation">(</span>wrapper<span class="token punctuation">.</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token string">'button'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">html</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toBeTruthy</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div>
<p>We mount the <code class="language-text">Counter</code> component, creating an instance of the component wrapped within a wrapper. From the wrapper object, we can use the convenience methods to check if the button element exists and was rendered.</p>
<blockquote>
<p>The wrapper has several methods that could be useful. You should check out all the <a href="https://vue-test-utils.vuejs.org/api/wrapper/#properties" target="_blank" rel="nofollow">available methods</a> to see what is available.</p>
</blockquote>
<h3 id="mount-vs-shallowmount" style="position:relative;">mount vs shallowMount</h3>
<p>Vue test utils comes with two methods for mounting components: <code class="language-text">mount</code> and <code class="language-text">shallowMount</code>. <code class="language-text">mount</code> instantiates the component along with all its sub components initialized as well, building out the whole DOM tree. <code class="language-text">shallowMount</code> on the other hand instantiates the component but stubs out the sub components, replacing them with stubs. This ensures that its just the current component that is executed during the test. This aligns with the idea of mocking dependencies instead of executing the real dependencies. This makes the tests run faster, and also keeps the tests isolated, which is something we have established that you need when running unit tests.</p>
<h2 id="lets-write-some-tests" style="position:relative;">Let’s write some tests!</h2>
<p>Again, we will be using the <a href="https://github.com/imolorhe/rick-morty" target="_blank" rel="nofollow">rick and morty web app</a> as the case study and we would be writing unit tests for the components.</p>
<p>The app is a simple one with a few views and components: CharacterCard, Pagination, and Header. We would be testing the <strong>CharacterCard</strong> component here.</p>
<p>The <strong>CharacterCard</strong> component simply displays the basic details about a character in a card form. So as you would expect, it takes as input a <code class="language-text">character</code> prop. It also emits the <code class="language-text">click</code> event when the card is clicked.</p>
<div class="gatsby-highlight" data-language="tsx"><pre class="language-tsx"><code class="language-tsx"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>template</span><span class="token punctuation">></span></span><span class="token plain-text">
&lt;div
class="max-w-sm w-full lg:max-w-full lg:flex shadow-md hover:shadow-xl transition duration-300 ease-in transform hover:-translate-y-px rounded cursor-pointer"
@click="onClickCard"
>
</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span>
<span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>h-48 lg:h-auto lg:w-48 flex-none bg-cover rounded-t lg:rounded-t-none lg:rounded-l text-center overflow-hidden<span class="token punctuation">"</span></span>
<span class="token attr-name">:</span><span class="token style-attr language-css"><span class="token attr-name"><span class="token attr-name">style</span></span><span class="token punctuation">="</span><span class="token attr-value">`<span class="token property">background-image</span><span class="token punctuation">:</span> <span class="token url"><span class="token function">url</span><span class="token punctuation">(</span>'${character.image}'<span class="token punctuation">)</span></span>`</span><span class="token punctuation">"</span></span>
<span class="token attr-name">:title</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>character.name<span class="token punctuation">"</span></span>
<span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><span class="token plain-text">
</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span>
<span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>bg-white rounded-b lg:rounded-b-none lg:rounded-r p-4 flex flex-col flex-grow justify-between leading-normal text-left<span class="token punctuation">"</span></span>
<span class="token punctuation">></span></span><span class="token plain-text">
</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>mb-8<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token plain-text">
</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>text-gray-900 font-bold text-xl mb-2<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token plain-text">
</span><span class="token punctuation">{</span><span class="token punctuation">{</span> character<span class="token punctuation">.</span>name <span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token plain-text">
</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>text-sm text-gray-600<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token plain-text">
</span><span class="token punctuation">{</span><span class="token punctuation">{</span> character<span class="token punctuation">.</span>gender <span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token plain-text"> &amp;middot;
</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>span</span>
<span class="token attr-name">tid</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>character-card-status<span class="token punctuation">"</span></span>
<span class="token attr-name">:class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>statusClasses<span class="token punctuation">"</span></span>
<span class="token punctuation">></span></span><span class="token punctuation">{</span><span class="token punctuation">{</span> character<span class="token punctuation">.</span>status <span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>span</span>
<span class="token punctuation">></span></span><span class="token plain-text">
&amp;middot; </span><span class="token punctuation">{</span><span class="token punctuation">{</span> character<span class="token punctuation">.</span>origin<span class="token punctuation">.</span>name <span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token plain-text">
</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><span class="token plain-text">
</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><span class="token plain-text">
</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>text-sm text-gray-600 flex items-center<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token plain-text">
</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>img</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>../assets/planet.png<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>w-5 mr-1<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span><span class="token plain-text">
</span><span class="token punctuation">{</span><span class="token punctuation">{</span> character<span class="token punctuation">.</span>location<span class="token punctuation">.</span>name <span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token plain-text">
</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><span class="token plain-text">
</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>mb-3<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><span class="token plain-text">
</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>span</span>
<span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>inline-block bg-gray-200 rounded-full px-3 py-1 text-sm font-semibold text-gray-700 mr-2<span class="token punctuation">"</span></span>
<span class="token punctuation">></span></span><span class="token punctuation">{</span><span class="token punctuation">{</span> character<span class="token punctuation">.</span>species <span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>span</span>
<span class="token punctuation">></span></span><span class="token plain-text">
</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><span class="token plain-text">
</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><span class="token plain-text">
</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><span class="token plain-text">
</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>template</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>script</span> <span class="token attr-name">lang</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>ts<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token plain-text">
import Vue from 'vue';
export default Vue.extend({
name: 'CharacterCard',
props: {
character: {
type: Object,
required: true,
},
},
computed: {
statusClasses(): unknown {
return {
'ml-1': true,
'text-green-600': this.character.status === 'Alive',
'text-red-600': this.character.status !== 'Alive',
};
},
},
methods: {
onClickCard() {
this.$emit('click', this.character);
},
},
});
</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>script</span><span class="token punctuation">></span></span></code></pre></div>
<p>Like we have previously discussed about defining the test cases:</p>
<ul>
<li>We start by identifying the <strong>inputs</strong> and <strong>outputs</strong> of the component. The inputs here include: the <code class="language-text">character</code> props, and the user click action. The outputs would be: the rendered component (with all the expected data), and the emitted <code class="language-text">click</code> event, after the user clicks the card.</li>
<li>Next, we check the dependencies of the component. In this case, there isn’t any dependencies, so we skip this.</li>
<li>Finally, we check all logical execution flows. There’s one conditional in the <code class="language-text">statusClasses()</code> computed properties, although it’s nt very obvious. When the character status is “Alive”, we set the <code class="language-text">green</code> class, else we set the <code class="language-text">red</code> class. This is one of those conditions that isn’t very obvious at first glance. A good way to find these is to check for the conditional expressions (equalities <code class="language-text">===</code> <code class="language-text">!==</code>, greater than <code class="language-text">&gt;=</code>, less than <code class="language-text">&lt;=</code>, negation <code class="language-text">!</code>, etc).</li>
</ul>
<p>From this, we can see that the test cases required are not much.</p>
<div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token keyword">import</span> CharacterCard <span class="token keyword">from</span> <span class="token string">'./CharacterCard.vue'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> shallowMount <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@vue/test-utils'</span><span class="token punctuation">;</span>
<span class="token function">describe</span><span class="token punctuation">(</span><span class="token string">'CharacterCard'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token function">it</span><span class="token punctuation">(</span><span class="token string">'should render the character basic details'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token keyword">const</span> character <span class="token operator">=</span> <span class="token punctuation">{</span>
name<span class="token operator">:</span> <span class="token string">'Character'</span><span class="token punctuation">,</span>
image<span class="token operator">:</span> <span class="token string">'character.jpg'</span><span class="token punctuation">,</span>
gender<span class="token operator">:</span> <span class="token string">'Gender'</span><span class="token punctuation">,</span>
status<span class="token operator">:</span> <span class="token string">'Alive'</span><span class="token punctuation">,</span>
species<span class="token operator">:</span> <span class="token string">'Human'</span><span class="token punctuation">,</span>
origin<span class="token operator">:</span> <span class="token punctuation">{</span>
name<span class="token operator">:</span> <span class="token string">'Origin'</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
location<span class="token operator">:</span> <span class="token punctuation">{</span>
name<span class="token operator">:</span> <span class="token string">'Location'</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> comp <span class="token operator">=</span> <span class="token function">shallowMount</span><span class="token punctuation">(</span>CharacterCard<span class="token punctuation">,</span> <span class="token punctuation">{</span>
propsData<span class="token operator">:</span> <span class="token punctuation">{</span>
character<span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">expect</span><span class="token punctuation">(</span>comp<span class="token punctuation">.</span><span class="token function">html</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toContain</span><span class="token punctuation">(</span>character<span class="token punctuation">.</span>name<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">expect</span><span class="token punctuation">(</span>comp<span class="token punctuation">.</span><span class="token function">html</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toContain</span><span class="token punctuation">(</span>character<span class="token punctuation">.</span>image<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">expect</span><span class="token punctuation">(</span>comp<span class="token punctuation">.</span><span class="token function">html</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toContain</span><span class="token punctuation">(</span>character<span class="token punctuation">.</span>gender<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">expect</span><span class="token punctuation">(</span>comp<span class="token punctuation">.</span><span class="token function">html</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toContain</span><span class="token punctuation">(</span>character<span class="token punctuation">.</span>status<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">expect</span><span class="token punctuation">(</span>comp<span class="token punctuation">.</span><span class="token function">html</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toContain</span><span class="token punctuation">(</span>character<span class="token punctuation">.</span>species<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">expect</span><span class="token punctuation">(</span>comp<span class="token punctuation">.</span><span class="token function">html</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toContain</span><span class="token punctuation">(</span>character<span class="token punctuation">.</span>origin<span class="token punctuation">.</span>name<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">expect</span><span class="token punctuation">(</span>comp<span class="token punctuation">.</span><span class="token function">html</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toContain</span><span class="token punctuation">(</span>character<span class="token punctuation">.</span>location<span class="token punctuation">.</span>name<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">it</span><span class="token punctuation">(</span><span class="token string">'should render status as green if alive'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token keyword">const</span> character <span class="token operator">=</span> <span class="token punctuation">{</span>
<span class="token comment">// ...</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> comp <span class="token operator">=</span> <span class="token function">shallowMount</span><span class="token punctuation">(</span>CharacterCard<span class="token punctuation">,</span> <span class="token punctuation">{</span>
propsData<span class="token operator">:</span> <span class="token punctuation">{</span>
character<span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">expect</span><span class="token punctuation">(</span>
comp
<span class="token punctuation">.</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token string">'[tid="character-card-status"]'</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">classes</span><span class="token punctuation">(</span><span class="token string">'text-green-600'</span><span class="token punctuation">)</span>
<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toBe</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">it</span><span class="token punctuation">(</span><span class="token string">'should render status as red if dead'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token keyword">const</span> character <span class="token operator">=</span> <span class="token punctuation">{</span>
<span class="token comment">// ...</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> comp <span class="token operator">=</span> <span class="token function">shallowMount</span><span class="token punctuation">(</span>CharacterCard<span class="token punctuation">,</span> <span class="token punctuation">{</span>
propsData<span class="token operator">:</span> <span class="token punctuation">{</span>
character<span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">expect</span><span class="token punctuation">(</span>
comp
<span class="token punctuation">.</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token string">'[tid="character-card-status"]'</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">classes</span><span class="token punctuation">(</span><span class="token string">'text-red-600'</span><span class="token punctuation">)</span>
<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toBe</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">it</span><span class="token punctuation">(</span><span class="token string">'should emit click with the character when clicked'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token keyword">const</span> character <span class="token operator">=</span> <span class="token punctuation">{</span>
<span class="token comment">// ...</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> comp <span class="token operator">=</span> <span class="token function">shallowMount</span><span class="token punctuation">(</span>CharacterCard<span class="token punctuation">,</span> <span class="token punctuation">{</span>
propsData<span class="token operator">:</span> <span class="token punctuation">{</span>
character<span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
comp<span class="token punctuation">.</span><span class="token function">trigger</span><span class="token punctuation">(</span><span class="token string">'click'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">expect</span><span class="token punctuation">(</span>comp<span class="token punctuation">.</span><span class="token function">emitted</span><span class="token punctuation">(</span><span class="token string">'click'</span><span class="token punctuation">)</span><span class="token punctuation">.</span>length<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toBe</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">expect</span><span class="token punctuation">(</span>comp<span class="token punctuation">.</span><span class="token function">emitted</span><span class="token punctuation">(</span><span class="token string">'click'</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toEqual</span><span class="token punctuation">(</span><span class="token punctuation">[</span>character<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div>
<p>First we check that the basic character info are rendered in the output. The next test case checks that the <code class="language-text">green</code> class is rendered when <code class="language-text">character.status</code> is “Alive”. After that we check the else clause: that the <code class="language-text">red</code> class is rendered instead. <em>Note the <code class="language-text">tid</code> attribute added to the status element. This makes it easy to select that element in the test.</em> Finally, we check that the click event is emitted when the user clicks the character card.</p>
<h3 id="snapshot-testing" style="position:relative;">Snapshot testing</h3>
<p>As you might have noticed in the first test case, to verify that the character info is rendered as we expect, we needed to add a lot of <code class="language-text">expect</code> assertions for the different data points. This is already a lot of assertions for a simple test case. You can imagine how many more <code class="language-text">expect</code> assertions would be required for more complex components. This makes the tests less maintainable, as changes made could require you to update several of the assertions. One way to solve this kinds of test cases is with <a href="https://jestjs.io/docs/en/snapshot-testing" target="_blank" rel="nofollow">snapshot testing</a>.</p>
<p>Snapshot testing basically allows us to render our component and save the rendered output to disk (as a snapshot of the component), and use this rendered output as a reference when running future tests. If the rendered output during a test is different from the original snapshot, the test would fail. You can then compare the two snapshots to see the difference, and then determine if the new rendered output is valid or not, and update the snapshot.</p>
<p>One of the limitations that appear when using snapshot is how you manage dynamic content. For example, assuming a component renders the current date, each time the snapshot test is run, the snapshots would be different. The recommended way to go around this is by mocking the dependencies (in this case, the <code class="language-text">Date</code> object) as we have previously discussed.</p>
<p>If we re-write the first test case using snapshot testing, we would have this:</p>
<div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token function">it</span><span class="token punctuation">(</span><span class="token string">'should render the character basic details'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token keyword">const</span> character <span class="token operator">=</span> <span class="token punctuation">{</span>
name<span class="token operator">:</span> <span class="token string">'Character'</span><span class="token punctuation">,</span>
image<span class="token operator">:</span> <span class="token string">'character.jpg'</span><span class="token punctuation">,</span>
gender<span class="token operator">:</span> <span class="token string">'Gender'</span><span class="token punctuation">,</span>
status<span class="token operator">:</span> <span class="token string">'Alive'</span><span class="token punctuation">,</span>
species<span class="token operator">:</span> <span class="token string">'Human'</span><span class="token punctuation">,</span>
origin<span class="token operator">:</span> <span class="token punctuation">{</span>
name<span class="token operator">:</span> <span class="token string">'Origin'</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
location<span class="token operator">:</span> <span class="token punctuation">{</span>
name<span class="token operator">:</span> <span class="token string">'Location'</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> comp <span class="token operator">=</span> <span class="token function">shallowMount</span><span class="token punctuation">(</span>CharacterCard<span class="token punctuation">,</span> <span class="token punctuation">{</span>
propsData<span class="token operator">:</span> <span class="token punctuation">{</span>
character<span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">expect</span><span class="token punctuation">(</span>comp<span class="token punctuation">.</span><span class="token function">html</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toMatchSnapshot</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div>
<p>We replaced all the assertions with a single <code class="language-text">expect(comp.html()).toMatchSnapshot()</code>. This is much cleaner, and easier to maintain. Generally when testing the rendered component output, snapshot testing makes for a cleaner testing approach.</p>
<p>The snapshot looks like this:</p>
<div class="gatsby-highlight" data-language="text"><pre class="language-text"><code class="language-text">// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`CharacterCard should render the character basic details 1`] = `
&lt;div class=&quot;max-w-sm w-full lg:max-w-full lg:flex shadow-md hover:shadow-xl transition duration-300 ease-in transform hover:-translate-y-px rounded cursor-pointer&quot;&gt;
&lt;div title=&quot;Character&quot; class=&quot;h-48 lg:h-auto lg:w-48 flex-none bg-cover rounded-t lg:rounded-t-none lg:rounded-l text-center overflow-hidden&quot; style=&quot;background-image: url(character.jpg);&quot;&gt;&lt;/div&gt;
&lt;div class=&quot;bg-white rounded-b lg:rounded-b-none lg:rounded-r p-4 flex flex-col flex-grow justify-between leading-normal text-left&quot;&gt;
&lt;div class=&quot;mb-8&quot;&gt;
&lt;div class=&quot;text-gray-900 font-bold text-xl mb-2&quot;&gt;
Character
&lt;div class=&quot;text-sm text-gray-600&quot;&gt;
Gender ·
&lt;span tid=&quot;character-card-status&quot; class=&quot;ml-1 text-green-600&quot;&gt;Alive&lt;/span&gt;
· Origin
&lt;/div&gt;
&lt;/div&gt;
&lt;p class=&quot;text-sm text-gray-600 flex items-center&quot;&gt;&lt;img src=&quot;../assets/planet.png&quot; class=&quot;w-5 mr-1&quot;&gt;
Location
&lt;/p&gt;
&lt;div class=&quot;mb-3&quot;&gt;&lt;/div&gt; &lt;span class=&quot;inline-block bg-gray-200 rounded-full px-3 py-1 text-sm font-semibold text-gray-700 mr-2&quot;&gt;Human&lt;/span&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
`;</code></pre></div>
<blockquote>
<p>Note: You would need the <a href="https://www.npmjs.com/package/jest-serializer-vue" target="_blank" rel="nofollow">jest-serializer-vue</a> snapshot serializer to generate useful snapshots for your Vue components.</p>
</blockquote>
<p>Let’s consider writing a test for the <strong>Header</strong> component. It just displays the logo and a list of links used for navigating between pages.</p>
<div class="gatsby-highlight" data-language="tsx"><pre class="language-tsx"><code class="language-tsx"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>template</span><span class="token punctuation">></span></span><span class="token plain-text">
</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>nav</span>
<span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>flex items-center justify-between flex-wrap bg-purple-900 p-6<span class="token punctuation">"</span></span>
<span class="token punctuation">></span></span><span class="token plain-text">
&lt;!-- ... -->
</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span>
<span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>w-full block flex-grow lg:flex lg:items-center lg:w-auto<span class="token punctuation">"</span></span>
<span class="token punctuation">></span></span><span class="token plain-text">
</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>text-md lg:flex-grow<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token plain-text">
</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>router-link</span>
<span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>block mt-4 lg:inline-block lg:mt-0 text-purple-200 rounded-full lg:hover:bg-purple-800 lg:px-3 py-1 hover:text-white mr-4 transition duration-300<span class="token punctuation">"</span></span>
<span class="token attr-name">to</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>/<span class="token punctuation">"</span></span>
<span class="token punctuation">></span></span><span class="token plain-text">
Home
</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>router-link</span><span class="token punctuation">></span></span><span class="token plain-text">
</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>router-link</span>
<span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>block mt-4 lg:inline-block lg:mt-0 text-purple-200 rounded-full lg:hover:bg-purple-800 lg:px-3 py-1 hover:text-white mr-4 transition duration-300<span class="token punctuation">"</span></span>
<span class="token attr-name">to</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>/about<span class="token punctuation">"</span></span>
<span class="token punctuation">></span></span><span class="token plain-text">
About
</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>router-link</span><span class="token punctuation">></span></span><span class="token plain-text">
</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>router-link</span>
<span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>block mt-4 lg:inline-block lg:mt-0 text-purple-200 rounded-full lg:hover:bg-purple-800 lg:px-3 py-1 hover:text-white mr-4 transition duration-300<span class="token punctuation">"</span></span>
<span class="token attr-name">to</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>/characters<span class="token punctuation">"</span></span>
<span class="token punctuation">></span></span><span class="token plain-text">
Characters
</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>router-link</span><span class="token punctuation">></span></span><span class="token plain-text">
</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><span class="token plain-text">
</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><span class="token plain-text">
</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>nav</span><span class="token punctuation">></span></span><span class="token plain-text">
</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>template</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>script</span> <span class="token attr-name">lang</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>ts<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token plain-text">
import Vue from 'vue';
export default Vue.extend({
name: 'Header',
});
</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>script</span><span class="token punctuation">></span></span></code></pre></div>
<p>We can define the test cases as follows:</p>
<ul>
<li><strong>inputs and outputs:</strong> it doesn’t really have any inputs. Although it contains <code class="language-text">router-link</code> components that the user’s can click, the click interaction occurs on the <code class="language-text">router-link</code> component, not the <code class="language-text">Header</code> component. For the output, we only have the rendered component output.</li>
<li><strong>mocking dependencies:</strong> it contains <code class="language-text">router-link</code> components for navigating the user between pages. <code class="language-text">router-link</code> component is globally registered, and is not registered locally within the <strong>Header</strong> component, so using <code class="language-text">shallowMount</code> would not stub the component. We would need to explicitly stub it out ourselves.</li>
<li><strong>logical execution flows:</strong> it doesn’t have any conditional statements or expressions in the templates or scripts section, so we can skip this.</li>
</ul>
<div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token keyword">import</span> Header <span class="token keyword">from</span> <span class="token string">'./Header.vue'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> shallowMount <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@vue/test-utils'</span><span class="token punctuation">;</span>
<span class="token function">describe</span><span class="token punctuation">(</span><span class="token string">'Header'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token function">it</span><span class="token punctuation">(</span><span class="token string">'should render'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token keyword">const</span> comp <span class="token operator">=</span> <span class="token function">shallowMount</span><span class="token punctuation">(</span>Header<span class="token punctuation">,</span> <span class="token punctuation">{</span>
stubs<span class="token operator">:</span> <span class="token punctuation">{</span>
RouterLink<span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">expect</span><span class="token punctuation">(</span>comp<span class="token punctuation">.</span><span class="token function">html</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toMatchSnapshot</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div>
<p>We have a single test case that asserts the rendered output. Given what we know about testing rendered output, we just use a <code class="language-text">expect(comp.html()).toMatchSnapshot()</code>.</p>
<p>Like we mentioned, the <code class="language-text">router-link</code> component is not registered locally within the <code class="language-text">Header</code> component. So <code class="language-text">shallowMount</code> wouldn’t know to create a stub for it. Fortunately, we can explicitly indicate to <code class="language-text">shallowMount</code> that it should stub the <code class="language-text">router-link</code> component. <em>Note that we can use either <code class="language-text">RouterLink: true</code> or <code class="language-text">&#39;router-link&#39;: true</code>. Vue uses both formats for defining component names.</em> Setting <code class="language-text">RouterLink</code> to true tells <code class="language-text">shallowMount</code> to create a simple dummy stub. If you check the generated snapshot, you’d notice that the <code class="language-text">router-link</code> component is now <code class="language-text">routerlink-stub</code>.</p>
<p>You can also pass a component to be used as the stub instead, if you’d like.</p>
<div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token keyword">const</span> routerStub <span class="token operator">=</span> <span class="token punctuation">{</span>
template<span class="token operator">:</span> <span class="token string">'&lt;div>&lt;/div>'</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> comp <span class="token operator">=</span> <span class="token function">shallowMount</span><span class="token punctuation">(</span>Header<span class="token punctuation">,</span> <span class="token punctuation">{</span>
stubs<span class="token operator">:</span> <span class="token punctuation">{</span>
RouterLink<span class="token operator">:</span> routerStub<span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div>
<h3 id="testing-with-subcomponents-that-use-slots" style="position:relative;">Testing with subcomponents that use slots</h3>
<p>Sometimes you would have sub components that use <a href="https://vuejs.org/v2/guide/components-slots.html" target="_blank" rel="nofollow">slots</a> to specify where specific contents would be rendered.</p>
<div class="gatsby-highlight" data-language="tsx"><pre class="language-tsx"><code class="language-tsx"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>template</span><span class="token punctuation">></span></span><span class="token plain-text">
</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span><span class="token punctuation">></span></span><span class="token plain-text">
</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>header</span><span class="token punctuation">></span></span><span class="token plain-text">My wonderful component</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>header</span><span class="token punctuation">></span></span><span class="token plain-text">
</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">MySelect</span></span><span class="token punctuation">></span></span><span class="token plain-text">
</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>template</span> <span class="token attr-name">slot</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>label<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token plain-text">My select label</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>template</span><span class="token punctuation">></span></span><span class="token plain-text">
</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>template</span> <span class="token attr-name">slot</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>error<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token plain-text">My select error message</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>template</span><span class="token punctuation">></span></span><span class="token plain-text">
</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token class-name">MySelect</span></span><span class="token punctuation">></span></span><span class="token plain-text">
</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><span class="token plain-text">
</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>template</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>script</span> <span class="token attr-name">lang</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>ts<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token plain-text">
import Vue from 'vue';
import MySelect from './MySelect.vue';
export default Vue.extend({
name: 'MyWonderfulComponent',
components: {
MySelect,
}
});
</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>script</span><span class="token punctuation">></span></span></code></pre></div>
<p>Here the <code class="language-text">MySelect</code> component has a <strong>label</strong> and <strong>error</strong> slots, which we pass values to. If we write a unit test for <code class="language-text">MyWonderfulComponent</code> component using <code class="language-text">shallowMount</code>, you would notice that the rendered output doesn’t contain the text for the label and error slots, because the <code class="language-text">MySelect</code> component would have been stubbed with a dummy stub component which doesn’t use slots. To work around this, we can provide our own stub component to be used to replace <code class="language-text">MySelect</code> component in a deterministic way, and use that during the test rather than the real <code class="language-text">MySelect</code> component.</p>
<div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token keyword">const</span> mySelectStub <span class="token operator">=</span> <span class="token punctuation">{</span>
template<span class="token operator">:</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">&lt;div>
&lt;div class="label-slot">
&lt;slot name="label">&lt;/slot>
&lt;/div>
&lt;div class="error-slot">
&lt;slot name="error">&lt;/slot>
&lt;/div>
&lt;div class="default-slot">
&lt;slot />
&lt;/div>
&lt;/div></span><span class="token template-punctuation string">`</span></span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> comp <span class="token operator">=</span> <span class="token function">shallowMount</span><span class="token punctuation">(</span>Header<span class="token punctuation">,</span> <span class="token punctuation">{</span>
stubs<span class="token operator">:</span> <span class="token punctuation">{</span>
MySelect<span class="token operator">:</span> mySelectStub<span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div>
<p>With this, we would have the slots rendered, and we can check for that easily.</p>
<h2 id="conclusion" style="position:relative;">Conclusion</h2>
<p>One final note about dependencies is that: <em>not all dependencies are equal</em>. In the previous article, we asked the question about whether <code class="language-text">Vue.set()</code> used within mutations should be mocked when writing unit tests. To determine if a dependency should be mocked, these are a couple of questions that can help you decide:</p>
<ul>
<li>is it a <a href="https://www.xkoji.dev/blog/unit-testing-javascript-applications-part-1/#pure-functions" target="_blank" rel="nofollow">pure function</a> dependency? This means it doesn’t have an effect on any thing besides returning an output for the input you give it.</li>
<li>if it has side effects, are they local to the piece of code being tested? For example, <code class="language-text">Vue.set()</code> only mutates the given input, and does nothing else.</li>
<li>is its output dependent on external factors? This affects the deterministic nature of the dependency. For example, <code class="language-text">Date.now()</code> would give a different result based on the current time.</li>
<li>is it difficult to mock the dependency? Does mocking dependency require a whole lot more work (like A LOT MORE) than actually writing the test?</li>
</ul>
<p>Usually this question comes up when dealing with implicit dependencies, because they make mocking dependencies hard.</p>
<p>Over the course of this series, we have looked at unit testing approaches, and how to write unit tests for different forms of code. We looked at how to mock dependencies as well as how to make writing tests easier. We considered the TDD approach and saw how we can write our code following that pattern. We have also looked at various testing tools like Jest and the Vue test utils for a robust unit testing solution.</p>
<p>While it is recommended to follow the TDD approach as much as possible due to the benefits you get, it is not a mandatory practice for writing unit tests. Following the testing approach for defining your test cases, you can have full test coverage of your code, even if you write the tests afterwards.</p>
<p>Writing unit tests doesn’t have to be hard. Following a defined approach makes it easier.</p></content:encoded></item><item><title><![CDATA[Unit testing JavaScript Applications - Part 2]]></title><description><![CDATA[In the previous post, we began talking about what unit testing is about, why we should consider writing unit tests, and how we should…]]></description><link>https://www.xkoji.dev/blog/unit-testing-javascript-applications---part-2/</link><guid isPermaLink="false">https://www.xkoji.dev/blog/unit-testing-javascript-applications---part-2/</guid><pubDate>Fri, 22 May 2020 15:59:40 GMT</pubDate><content:encoded><p>In the <a href="https://www.xkoji.dev/blog/unit-testing-javascript-applications-part-1/" target="_blank" rel="nofollow">previous post</a>, we began talking about what unit testing is about, why we should consider writing unit tests, and how we should approach it. In this post, we will continue with some more practical things to consider and also writing some unit tests for a Vue.js application.</p>
<h2 id="the-application" style="position:relative;">The Application</h2>
<p><span
class="gatsby-resp-image-wrapper"
style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1200px;"
>
<span
class="gatsby-resp-image-background-image"
style="padding-bottom: 57.00000000000001%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAABYlAAAWJQFJUiTwAAACJklEQVQoz22SW2sTQRiG9y95ulAKgpaq4IU3/gl/gbVFMEJFiuhNlWAiWm8SYtog29QkjZtDDyZjE2vWNidpqGiNSUlru+fD68ykWVPoBw+z8L07M8+3K9y67MPt0SncHPFh7PwExi5MUiYweu4erpy563H9og83Rh7h2qWHuHp2nDPcHyAk0wSRyEcsRlNYzlawnJFBVusga3UUV2t91hoI+d8gcH8SoWczyEsyclIFhRXWr59AKO18x0Iui005jyPtL0XFaVUW/egU51GKPMFerwvV1E7NCaS4glxqEU0iQbdsOLYL27Y5lsVWB5quoy1LUCpL+Lkex6GiwXHg9RnsmSHUqwRSNgJCEvzFQ0WFYVjQKYZp8ZBpOdj/kcKf6lv0tkUoqgZF03hfN0wvxzckH2YRC9zBy/A4dtsdtDt7UOgNWJCdzIqt7co0tktB/P76lOZ20e0dQNX0fs5x/isXPhWRef0CYfExNNpk5boux6HQB6iGjZh/CnLwARKhaZh0HMM1yDKE5hZBMTYHeSPJldmpJte0+GrRl9lst9bLqMTj9A8gPHNER8PQdINnPOWCOAN5IYBW+R1+Hasoal/FOVZxHReFL3kk3j9Ho17rj6bbRW//ADq9xImv/I1EsZN+hdqGSNVMZsg38mAqdMNq9TNC6Tk0Wi1vtoNifU95s1bCfDhIT2zy6/eVLa4xUDZMG/FMBrNJCdHkEjSaUanF8Ncd8A+xFgcvsIAwEwAAAABJRU5ErkJggg=='); background-size: cover; display: block;"
></span>
<img
class="gatsby-resp-image-image"
alt="app screenshot"
title="app screenshot"
src="/static/74dd8acefece9fe3b016646427da5df2/c1b63/ricks-place-app.png"
srcset="/static/74dd8acefece9fe3b016646427da5df2/5a46d/ricks-place-app.png 300w,
/static/74dd8acefece9fe3b016646427da5df2/0a47e/ricks-place-app.png 600w,
/static/74dd8acefece9fe3b016646427da5df2/c1b63/ricks-place-app.png 1200w,
/static/74dd8acefece9fe3b016646427da5df2/d61c2/ricks-place-app.png 1800w,
/static/74dd8acefece9fe3b016646427da5df2/97a96/ricks-place-app.png 2400w,
/static/74dd8acefece9fe3b016646427da5df2/93ba6/ricks-place-app.png 2874w"
sizes="(max-width: 1200px) 100vw, 1200px"
style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"
loading="lazy"
/>
</span></p>
<p><span
class="gatsby-resp-image-wrapper"
style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1200px;"
>
<span
class="gatsby-resp-image-background-image"
style="padding-bottom: 57.00000000000001%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAABYlAAAWJQFJUiTwAAACXklEQVQoz22Ty2sTURSH8xe5cuVKaEUsii5EVFQqsVpM0po07xd5vyYzeTZNTGLatFFErFZwoYvShRQtFjeCFQoRV1YpNLSpSTPJz3PvpCOCA4dMyLnfOd85N5oHLh8mDCaYLTMIJ1IUGcTFHNL5EhaePEf96QtU55cgRQIoZyUIfjdCMRERgfKkPJLpAsSMEuxdY7E7MW00w22z8QQhVUCUksvzDXzeWMPm6ks0m03stfYhD4CjnoxipY5oMku5s0hIf4Od17hcDuj0WuhMOqosIRhL8erFcg3N9dfYflPHz50d/Nrdw+Hhb8iyjNnSI/gjIhIEjIt5DlKBRoMOE7cu4Kr2IsEkrsygD6uLaG2v4WDrFdrtNvrUHXt6vR4Ba/BHRcSSOR4MzHRZxxqz3Yfp23eh1d8k1Sz9MMc/K6Tc6bYIRLB+H4PBQAWWqBgDJTNzEIYgRZ86dDptmNFPweW0IhRXOoxQVGgRzS8H+PG9S5gBqTIozfCox5W9wQSCUYnnKjNU1DWm++OwTo7DbZkk1STNL8uVy7UlbH3axbevLcL1eWcyddrpdLlygJTZbRCGuqqy3WWG13gPDqeZNpdTlGmD1YXHvLP/PZVag8NEpjzUVZUdHg+uXLtB7YeULVNlpp0tVPBufQPvP2xSfERjeQUrb1ex+GyZH/aFBV74eLtcmTausdmMODt6Gm6XieaRRoR02VJYcoCuRoi+s1mdPDOGc5cu48TIGDw0vxifXV7VVZW9Tgum7lyH1WSg2aXVK6D8A+YgZouQ6H2EYKdGz/PwhqV/IMdzZGf/AEMsbomoUVKvAAAAAElFTkSuQmCC'); background-size: cover; display: block;"
></span>
<img
class="gatsby-resp-image-image"
alt="character details"
title="character details"
src="/static/987e828c7fa19de1342a7f7a16d7776c/c1b63/ricks-place-character-details.png"
srcset="/static/987e828c7fa19de1342a7f7a16d7776c/5a46d/ricks-place-character-details.png 300w,
/static/987e828c7fa19de1342a7f7a16d7776c/0a47e/ricks-place-character-details.png 600w,
/static/987e828c7fa19de1342a7f7a16d7776c/c1b63/ricks-place-character-details.png 1200w,
/static/987e828c7fa19de1342a7f7a16d7776c/d61c2/ricks-place-character-details.png 1800w,
/static/987e828c7fa19de1342a7f7a16d7776c/97a96/ricks-place-character-details.png 2400w,
/static/987e828c7fa19de1342a7f7a16d7776c/d9ed5/ricks-place-character-details.png 2880w"
sizes="(max-width: 1200px) 100vw, 1200px"
style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"
loading="lazy"
/>
</span></p>
<p>The application we would be testing is a very simple one. It fetches and displays a list of characters from the TV series <em>“Rick and Morty”</em>. It also displays the character details in a modal dialog. You can add a note to any character in the app, and the notes are persisted to a remote storage and available to anyone using the app.</p>
<p><em>You can checkout the full application with the tests in this repo: <a href="https://github.com/imolorhe/rick-morty" target="_blank" rel="nofollow">https://github.com/imolorhe/rick-morty</a></em></p>
<h3 id="what-is-the-application-built-with" style="position:relative;">What is the application built with?</h3>
<p>The application is built with the <a href="https://vuejs.org/" target="_blank" rel="nofollow">Vue.js</a> as the JS framework consisting of a few views and components, bootstrapped using the <a href="https://cli.vuejs.org/" target="_blank" rel="nofollow">Vue CLI</a> tool. The application is written in <a href="https://www.typescriptlang.org/" target="_blank" rel="nofollow">TypeScript</a>, compiled to JavaScript. The client state of the application is managed using <a href="https://vuex.vuejs.org/" target="_blank" rel="nofollow">Vuex</a>, the de-facto state management library for Vue applications, routing managed by <a href="https://router.vuejs.org/" target="_blank" rel="nofollow">Vue router</a>, styling based on <a href="https://tailwindcss.com/" target="_blank" rel="nofollow">Tailwind CSS</a>, and <a href="https://github.com/axios/axios" target="_blank" rel="nofollow">axios</a> for sending API requests. Nothing too fancy.</p>
<p>The application makes use of the <a href="https://rickandmortyapi.com/" target="_blank" rel="nofollow">Rick and Morty API</a> <em>(btw consider <a href="https://rickandmortyapi.com/help-us" target="_blank" rel="nofollow">supporting</a> them)</em>. Although the rickandmortyapi also comes with a GraphQL endpoint, we would be using the REST API, to keep things simple (REST is still the most common API format). If you would like to use the GraphQL API, you can make use of the <a href="https://apollo.vuejs.org/" target="_blank" rel="nofollow">Vue Apollo</a> package to integrate GraphQL within your application.</p>
<p><span
class="gatsby-resp-image-wrapper"
style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1200px;"
>
<span
class="gatsby-resp-image-background-image"
style="padding-bottom: 57.333333333333336%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAABYlAAAWJQFJUiTwAAABuElEQVQoz4WTS3ajMBBFWU34CSEkkIT42IBjx04yyDDD3kbvoFf9usTHON05J4M6AlR1/epVOWi6G+rmAluf0R1e4doXen+BdWdoe4ahaCnHv7uO8toz6vYCVY4o9YTKnND0V4zXN4y3VwRpViPJDGQ5QFVLkih6pMyhqHqI8gCed+BiDbrL6GT+W9EhLw4QusPn73f8+vOBgHGHOPXQlpL6+UxYjadQozlNGC43FHKYC+dieQTjDWKKKLUI4wpRrOczTAyChBkodSSFI2ICxZQU0UWY6FlZ5z6ovedZmYctwJZyzQIhWJRsUa1AUlCII54iRb9WzRc+Wcge1l5nK4TcFWa576ZFyu1sVxhVdyh5aJGLHixrFvnrxQI8QBsahDuRt8MXoHQTSgqhySK+1wQhqeGUkGx+PAAlWWFWoKp2oB+OIpjtnpGrlrxc6jwr2IofYdu3Qi0KTT19AXqFwg6Qhp6l913f64N/Vf3fst/HieDHfcq+ZTPCtCdwSRNn+g79QeHSslfon7nY1ybzO0gdMFEjzQ3ibAUy7iWbbxVuLXsPjRvpX0HbUHqgoz1VNN3yYQ8XD/8Cy6JhQNyYq1QAAAAASUVORK5CYII='); background-size: cover; display: block;"
></span>
<img
class="gatsby-resp-image-image"
alt="rickandmorty GraphQL query in Altair"
title="rickandmorty GraphQL query in Altair"
src="/static/581e5f6799d44c248c1f7e41819b1ba7/c1b63/rickandmortyapi-graphql.png"
srcset="/static/581e5f6799d44c248c1f7e41819b1ba7/5a46d/rickandmortyapi-graphql.png 300w,
/static/581e5f6799d44c248c1f7e41819b1ba7/0a47e/rickandmortyapi-graphql.png 600w,
/static/581e5f6799d44c248c1f7e41819b1ba7/c1b63/rickandmortyapi-graphql.png 1200w,
/static/581e5f6799d44c248c1f7e41819b1ba7/d61c2/rickandmortyapi-graphql.png 1800w,
/static/581e5f6799d44c248c1f7e41819b1ba7/97a96/rickandmortyapi-graphql.png 2400w,
/static/581e5f6799d44c248c1f7e41819b1ba7/60708/rickandmortyapi-graphql.png 2872w"
sizes="(max-width: 1200px) 100vw, 1200px"
style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"
loading="lazy"
/>
</span></p>
<p><span
class="gatsby-resp-image-wrapper"
style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1200px;"
>
<span
class="gatsby-resp-image-background-image"
style="padding-bottom: 75%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAYAAADkmO9VAAAACXBIWXMAABYlAAAWJQFJUiTwAAABdklEQVQ4y6WS2U7DMBBF8/9/xTvQquINUTZRlSykSWzH+2XGVWiWVpVKlCs71p2TGc9kWmv0fQ8pJZRSEELAGIMYI0IMoBeV2GPzdYcyr1BWJfI8h7WWPAEhhKOXVuccMg723qcDXvmQxXs2JrOPsMbCOvbyGcjDMbQH0soPJ5dxwLUnUoC0LeouhzMKznbwTiTxPgS3BA7ZzIWUQUBPoN5KytaQqNzgEyiyRr6LwOEsGX2AcRo/cgetSsyrGr75mhbAsWlstM6iVnv0qqD7bKhcKp3L9vov5ixwDh2M3KhO19B9RVl+01rA0PfxDj27p8ABwCPEIzEHcoat3EN1n6R30kfaW3Ogn0nyevKFKZAvlTs1zOEYyJLtFnXxgEO5Iq3RVE+TihYZjueQ4ePzCbB4TNCm2ix82byjl7p3DshZ8ghdbcq8QeeB6yT/P+DrETYBmltKdqcM8/tTlsXqVqCnzluIdoe2foZoXkhb0lv62VANj9sv7HaXqW7uHaAAAAAASUVORK5CYII='); background-size: cover; display: block;"
></span>
<img
class="gatsby-resp-image-image"
alt="rickandmorty REST API request in insomnia"
title="rickandmorty REST API request in insomnia"
src="/static/8c73a3f57685bca35c5e18ffd689213c/c1b63/rickandmortyapi-rest.png"
srcset="/static/8c73a3f57685bca35c5e18ffd689213c/5a46d/rickandmortyapi-rest.png 300w,
/static/8c73a3f57685bca35c5e18ffd689213c/0a47e/rickandmortyapi-rest.png 600w,
/static/8c73a3f57685bca35c5e18ffd689213c/c1b63/rickandmortyapi-rest.png 1200w,
/static/8c73a3f57685bca35c5e18ffd689213c/d61c2/rickandmortyapi-rest.png 1800w,
/static/8c73a3f57685bca35c5e18ffd689213c/4654d/rickandmortyapi-rest.png 2392w"
sizes="(max-width: 1200px) 100vw, 1200px"
style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"
loading="lazy"
/>
</span></p>
<p>The application uses <a href="https://jsonbin.io/" target="_blank" rel="nofollow">JSONBin.io</a> to create and store the notes as bins for retrieval later on.</p>
<p>As you can already tell, there are only two main views (pages) in the application: the <strong>character list</strong>, and the <strong>character details</strong> views. Other components in the app include: the <strong>Header</strong> component used across the pages, the <strong>Pagination</strong> component for the list view, and the <strong>CharacterCard</strong> component for displaying the basic info about each character in the list.</p>
<p>For the store, there are 3 actions:</p>
<ul>
<li><code class="language-text">getCharacters</code> fetches the characters from the API for the current page, and commits the <code class="language-text">getCharactersSuccess</code> mutation with the result to the state.</li>
<li><code class="language-text">getNotes</code> fetches the list of notes from the collection on JSONBin.io, and commits the <code class="language-text">getNotesSuccess</code> mutation with the retrieved notes to the state.</li>
<li><code class="language-text">addNote</code> sends a request to JSONBin.io to create a bin containing the note in a collection, and dispatches the <code class="language-text">getNotes</code> action to re-fetch the characters. <em>Ideally dispatching the <code class="language-text">getNotes</code> action shouldn’t be necessary at this step. You should be able to transform and commit the returned result from the request to create a bin without requiring to re-fetch all the notes, but for the purpose of this post, I have opted to dispatch the <code class="language-text">getNotes</code> actions.</em></li>
</ul>
<p>There are also a few getters for transforming data from the state: <code class="language-text">characterList</code>, <code class="language-text">getCharacterById</code>, and <code class="language-text">getCharacterNotes</code>.</p>
<h2 id="defining-test-cases" style="position:relative;">Defining Test Cases</h2>
<p>Irrespective of if you decide to follow the <a href="https://www.xkoji.dev/blog/unit-testing-javascript-applications-part-1/#what-should-i-know-about-tdd" target="_blank" rel="nofollow">TDD approach</a> or not, your tests should reflect the functional requirements of your component or piece of code. If you recall how we should think about the test cases from the first post in this series:</p>
<ul>
<li><em>I think from a blackbox perspective when</em> determining the <em>inputs</em> and <em>outputs</em> of the piece of code to test, and I use that to create the test cases.</li>
<li><em>I think from a whitebox perspective when</em> I need to mock dependencies, since we need knowledge about the internals of the piece of code in order to be able to mock the dependencies.</li>
<li><em>I think from a whitebox perspective when</em> I want to cover all the logical execution flows within the piece of code, and I use this to add extra test cases, making the test suite robust.</li>
</ul>
<p>Let’s use another example to illustrate these points. Consider the following class:</p>
<div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token keyword">import</span> pow <span class="token keyword">from</span> <span class="token string">'super-power'</span><span class="token punctuation">;</span>
<span class="token keyword">class</span> <span class="token class-name">Squarer</span> <span class="token punctuation">{</span>
lastComputed <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span>
#previousLastComputed <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span>
<span class="token comment">/**
* Returns the square the specified input.
* If a list is provided, returns the sum of the squared of the items in list
*/</span>
<span class="token function">compute</span><span class="token punctuation">(</span><span class="token parameter">val</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">this</span><span class="token punctuation">.</span>#previousLastComputed <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>lastComputed<span class="token punctuation">;</span>
<span class="token keyword">this</span><span class="token punctuation">.</span>lastComputed <span class="token operator">=</span> val<span class="token punctuation">;</span>
<span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">#getSquared</span><span class="token punctuation">(</span>val<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token function">#getSquared</span><span class="token punctuation">(</span><span class="token parameter">val</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>Array<span class="token punctuation">.</span><span class="token function">isArray</span><span class="token punctuation">(</span>val<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> val<span class="token punctuation">.</span><span class="token function">reduce</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">acc<span class="token punctuation">,</span> cur</span><span class="token punctuation">)</span> <span class="token operator">=></span> acc <span class="token operator">+</span> <span class="token function">pow</span><span class="token punctuation">(</span><span class="token function">Number</span><span class="token punctuation">(</span>cur<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">return</span> <span class="token function">pow</span><span class="token punctuation">(</span><span class="token function">Number</span><span class="token punctuation">(</span>val<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">)</span> <span class="token operator">||</span> <span class="token number">0</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token keyword">const</span> squarer <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Squarer</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
squarer<span class="token punctuation">.</span><span class="token function">compute</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// => 4</span>
squarer<span class="token punctuation">.</span>lastComputed<span class="token punctuation">;</span> <span class="token comment">// => 2</span></code></pre></div>
<p>This <code class="language-text">Squarer</code> class has one <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Class_fields#Private_Methods" target="_blank" rel="nofollow">private</a> method <code class="language-text">#getSquared()</code> and one public method <code class="language-text">compute()</code>. The usage is simple: you call the <code class="language-text">compute(val)</code> method with a <code class="language-text">val</code> parameter, and it returns the squared value. Assuming we want to test this class as a unit of code following the steps above:</p>
<h3 id="create-test-cases-from-the-inputs-and-outputs-of-the-piece-of-code" style="position:relative;">Create test cases from the inputs and outputs of the piece of code</h3>
<p>The class only accepts input via the <code class="language-text">compute</code> method. However, it accepts multiple input types: an array of values, or primitive values.
The <em>output</em> from the class includes the result of the <code class="language-text">compute()</code> method call, as well as the public <code class="language-text">lastComputed</code> property.</p>
<p>Although the class also contains <code class="language-text">#previousLastComputed</code> property and <code class="language-text">#getSquared()</code> method, those are private to the class and are not exposed outside the class definition. Hence, an external library or function can only interact with this class using the <code class="language-text">compute()</code> method and the <code class="language-text">lastComputed</code> properties. This is what we would consider as the public API/interface of the class, and that is what we consider.</p>
<p>Given that, we can define a test case for each of the following:</p>
<ul>
<li>input as number</li>
<li>input as string</li>
<li>input as array of numbers</li>
</ul>
<p>It’s always a good practice to also account for edge cases when supplying inputs. For instance, you could add test cases for input as null, undefined, or an object. This could reveal possible misbehaviors within the code which you can then fix, and would lead to more consistent behavior.</p>
<h3 id="determine-the-dependencies-of-the-code-that-would-need-to-be-mocked" style="position:relative;">Determine the dependencies of the code that would need to be mocked</h3>
<p>In the class we have used a fictional module <code class="language-text">super-power</code> for calculating exponents. For us to test this class in isolation <em>(unit testing)</em>, we need to mock out the <code class="language-text">super-power</code> library. For this step, we just note the things that need to be mocked. Actually mocking these dependencies would require specific implementations based on the testing framework in use. We would see how we mock this in <a href="https://jestjs.io/" target="_blank" rel="nofollow">jest</a> in a coming section.</p>
<h3 id="create-extra-test-cases-covering-all-the-logical-execution-flows" style="position:relative;">Create extra test cases covering all the logical execution flows</h3>
<p>We use <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/if...else" target="_blank" rel="nofollow"><code class="language-text">if</code></a> and <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/switch" target="_blank" rel="nofollow"><code class="language-text">switch</code></a> statements as well as <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_Operators#Short-circuit_evaluation" target="_blank" rel="nofollow">logical (short circuit evaluation)</a> and <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Conditional_Operator" target="_blank" rel="nofollow">ternary (conditional)</a> operators in JavaScript to create branches in the execution flow based on the specified conditions. These change the execution path of the code, hence could yield different results based on the applied conditions. It might not always be obvious (especially with ternary and logical operators) that there are multiple execution paths within the code. This means you could have tests for your code but still have some untested flows that could contain bugs because the tests don’t cover them.</p>
<p>Looking at the <code class="language-text">Squarer</code> class example, you would notice we have one <code class="language-text">if</code> statement in the private <code class="language-text">#getSquared</code> method. You would also notice that the function does something different if the condition is met, and otherwise. There is also a short circuit evaluation used on the line with the <code class="language-text">return</code> statement.</p>
<div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token keyword">import</span> pow <span class="token keyword">from</span> <span class="token string">'super-power'</span><span class="token punctuation">;</span>
<span class="token keyword">class</span> <span class="token class-name">Squarer</span> <span class="token punctuation">{</span>
<span class="token comment">// ...</span>
<span class="token function">#getSquared</span><span class="token punctuation">(</span><span class="token parameter">val</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>Array<span class="token punctuation">.</span><span class="token function">isArray</span><span class="token punctuation">(</span>val<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> val<span class="token punctuation">.</span><span class="token function">reduce</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">acc<span class="token punctuation">,</span> cur</span><span class="token punctuation">)</span> <span class="token operator">=></span> acc <span class="token operator">+</span> <span class="token function">pow</span><span class="token punctuation">(</span><span class="token function">Number</span><span class="token punctuation">(</span>cur<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="gatsby-highlight-code-line"> <span class="token keyword">return</span> <span class="token function">pow</span><span class="token punctuation">(</span><span class="token function">Number</span><span class="token punctuation">(</span>val<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">)</span> <span class="token operator">||</span> <span class="token number">0</span><span class="token punctuation">;</span></span> <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token comment">// ...</span></code></pre></div>
<p>If <code class="language-text">pow(Number(val), 2)</code> returns a falsy value (like <code class="language-text">&#39;&#39;</code>, <code class="language-text">0</code>, <code class="language-text">null</code>, <code class="language-text">undefined</code>), we return 0 instead. As you can see, this is not immediately obvious as it is in the same line as the <code class="language-text">return</code> statement.</p>
<p>Luckily, we already covered the multiple cases in the <code class="language-text">if</code> statement in the first step (input as array, input as number, etc). However we didn’t consider the short circuit with the logical OR operator. For a more robust test suite covering all cases, we should add a test case that ensures that execution path is covered as well. To do this here, we just need to ensure the returned value from the mocked <code class="language-text">pow()</code> returns a falsy value, and the 0 should be returned.</p>
<p>Having followed those 3 steps, we would have all the test cases covered, without unnecessarily testing the internal API of our class.</p>
<h2 id="introducing-jest" style="position:relative;">Introducing Jest</h2>
<p>We would be using the <a href="https://jestjs.io/" target="_blank" rel="nofollow">Jest testing framework</a> for writing the tests for our application. Jest has gained a lot of popularity recently I believe because of its simplicity. Compared to other testing frameworks, it is very easy to get started writing tests in Jest with little or no configuration. Jest also comes with all the related tools regularly used for testing like mock functions, stubs, spies, assertions, snapshots, etc. Jest is also extendable with the ability to write your own custom runners, reporters, watch plugins, custom matchers, etc.</p>
<p>By default, jest runs tests from files ending with <code class="language-text">.spec.js</code>, <code class="language-text">.test.js</code>, <code class="language-text">.spec.jsx</code>, <code class="language-text">.test.jsx</code>, and their typescript equivalents (<code class="language-text">.spec.ts</code>, <code class="language-text">.test.ts</code>, <code class="language-text">.spec.tsx</code>, <code class="language-text">.test.tsx</code>). Jest also runs any <code class="language-text">.js</code>, <code class="language-text">.jsx</code>, <code class="language-text">.ts</code>, <code class="language-text">.tsx</code> files in any <code class="language-text">__tests__</code> directory. There are usually two patterns used to organize your test files: either storing the test files together in a <code class="language-text">__tests__</code> directory next to the source directory, or storing them right next to the source files.</p>
<p>Let’s write the test cases we have defined for the <code class="language-text">Squarer</code> class.</p>
<div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token comment">// squarer.spec.js</span>
<span class="token keyword">import</span> pow <span class="token keyword">from</span> <span class="token string">'super-power'</span><span class="token punctuation">;</span>
jest<span class="token punctuation">.</span><span class="token function">mock</span><span class="token punctuation">(</span><span class="token string">'super-power'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">(</span><span class="token parameter">x<span class="token punctuation">,</span> y</span><span class="token punctuation">)</span> <span class="token operator">=></span> x <span class="token operator">**</span> y<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">describe</span><span class="token punctuation">(</span><span class="token string">'Squarer'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token function">describe</span><span class="token punctuation">(</span><span class="token string">'.compute()'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token function">it</span><span class="token punctuation">(</span><span class="token string">'should square the number input'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token keyword">const</span> squarer <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Squarer</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">expect</span><span class="token punctuation">(</span>squarer<span class="token punctuation">.</span><span class="token function">compute</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toBe</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">expect</span><span class="token punctuation">(</span>squarer<span class="token punctuation">.</span><span class="token function">compute</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toBe</span><span class="token punctuation">(</span><span class="token number">4</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">expect</span><span class="token punctuation">(</span>squarer<span class="token punctuation">.</span><span class="token function">compute</span><span class="token punctuation">(</span><span class="token number">3</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toBe</span><span class="token punctuation">(</span><span class="token number">9</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">expect</span><span class="token punctuation">(</span>squarer<span class="token punctuation">.</span><span class="token function">compute</span><span class="token punctuation">(</span><span class="token number">10</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toBe</span><span class="token punctuation">(</span><span class="token number">100</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">it</span><span class="token punctuation">(</span><span class="token string">'should square the parsed number in the string input'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token keyword">const</span> squarer <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Squarer</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">expect</span><span class="token punctuation">(</span>squarer<span class="token punctuation">.</span><span class="token function">compute</span><span class="token punctuation">(</span><span class="token string">'2'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toBe</span><span class="token punctuation">(</span><span class="token number">4</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">expect</span><span class="token punctuation">(</span>squarer<span class="token punctuation">.</span><span class="token function">compute</span><span class="token punctuation">(</span><span class="token string">'10'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toBe</span><span class="token punctuation">(</span><span class="token number">100</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">it</span><span class="token punctuation">(</span><span class="token string">'should return the sum of the square of the numbers in the array input'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token keyword">const</span> squarer <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Squarer</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">expect</span><span class="token punctuation">(</span>squarer<span class="token punctuation">.</span><span class="token function">compute</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">4</span><span class="token punctuation">,</span> <span class="token number">6</span><span class="token punctuation">,</span> <span class="token number">7</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toBe</span><span class="token punctuation">(</span><span class="token number">105</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">it</span><span class="token punctuation">(</span><span class="token string">'should return zero for invalid inputs'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token keyword">const</span> squarer <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Squarer</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">expect</span><span class="token punctuation">(</span>squarer<span class="token punctuation">.</span><span class="token function">compute</span><span class="token punctuation">(</span><span class="token keyword">null</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toBe</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">expect</span><span class="token punctuation">(</span>squarer<span class="token punctuation">.</span><span class="token function">compute</span><span class="token punctuation">(</span><span class="token keyword">undefined</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toBe</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">expect</span><span class="token punctuation">(</span>squarer<span class="token punctuation">.</span><span class="token function">compute</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toBe</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div>
<p>Within each test file, the test cases are organized into test suites. Test suites are used for grouping test cases related to the same piece of code. In Jest, test suites are defined using the <a href="https://jestjs.io/docs/en/api#describename-fn" target="_blank" rel="nofollow"><code class="language-text">describe()</code></a> method while test cases are defined using the <a href="https://jestjs.io/docs/en/api#testname-fn-timeout" target="_blank" rel="nofollow"><code class="language-text">test()</code> or <code class="language-text">it()</code></a> methods. <em>You can learn more about all the globals available in Jest tests <a href="https://jestjs.io/docs/en/api" target="_blank" rel="nofollow">here</a>.</em></p>
<p>The tests above cover all the test cases we previously defined, including the edge cases like <code class="language-text">null</code> and <code class="language-text">undefined</code>. It also covers the short circuit to return zero, by providing an input that would cause the <code class="language-text">pow</code> mock to return zero (the square of zero is zero). Jest allows us to mock the <code class="language-text">pow</code> module using the <code class="language-text">jest.mock()</code> method at the top of the file. Here, we define the module to return a function that returns the exponentiation result of the base (first parameter) and the exponent (second parameter).</p>
<h2 id="testing-the-store" style="position:relative;">Testing the Store</h2>
<p>Now coming back to our application. Let’s write the unit tests for the store (actions, mutations and getters). We would write tests for just one function in each category, as the learnings can be applied to the others.</p>
<h3 id="action-getcharacters" style="position:relative;">Action: getCharacters</h3>
<p>Let’s look at the <strong>functional requirements</strong> for the <code class="language-text">getCharacters</code> action:</p>
<ul>
<li>it can be called without parameters, or with an object containing the page number to fetch.</li>
<li>it should fetch characters data from the rickandmorty API using the <code class="language-text">rickmorty</code> module for the specified page.</li>
<li>it should commit the fetched data to the <code class="language-text">getCharactersSuccess</code> mutation.</li>
</ul>
<div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token keyword">import</span> <span class="token punctuation">{</span> getCharacter <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@/api/rickmorty'</span><span class="token punctuation">;</span>
<span class="token comment">// ...</span>
<span class="token keyword">const</span> actions <span class="token punctuation">{</span>
<span class="gatsby-highlight-code-line"> <span class="token keyword">async</span> <span class="token function">getCharacters</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> commit <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> page <span class="token operator">=</span> <span class="token number">1</span> <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span><span class="gatsby-highlight-code-line"> <span class="token keyword">const</span> res <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token function">getCharacter</span><span class="token punctuation">(</span><span class="token punctuation">{</span> page <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><span class="gatsby-highlight-code-line"> <span class="token function">commit</span><span class="token punctuation">(</span><span class="token string">'getCharactersSuccess'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> data<span class="token operator">:</span> res<span class="token punctuation">,</span> page<span class="token operator">:</span> <span class="token function">Number</span><span class="token punctuation">(</span>page<span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><span class="gatsby-highlight-code-line"> <span class="token punctuation">}</span><span class="token punctuation">,</span></span> <span class="token comment">// ...</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">export</span> <span class="token keyword">default</span> actions<span class="token punctuation">;</span></code></pre></div>
<p>Now looking at the <strong>inputs and outputs</strong> (public interface) of the action:</p>
<ul>
<li>first parameter is passed by Vuex, so is a constant which we don’t control.</li>
<li>we provide the second parameter to the <code class="language-text">getCharacters</code> action, an object containing <code class="language-text">page</code>.</li>
<li>we can omit the second parameter and the default should be used instead.</li>
<li>actions don’t return values, or their returned values aren’t used. Instead, they are expected to perform tasks like asynchronously fetching data from a server, saving a file to the disk, committing some mutation, etc. In that sense, the expected result (<em>output</em>) of an action is not the value it returns, but the actions it performs. This is usually the case for impure functions. Since they could have intended side effects, you want to make sure that the intended side effect happens as expected, which is the result/output of the function. For the case of the <code class="language-text">getCharacters</code> action, you expect that the <code class="language-text">getCharacters</code> rickandmorty API is called to get the characters data, and you expect that the <code class="language-text">commit()</code> is called with the <code class="language-text">getCharactersSuccess</code> mutation and the characters data and page.</li>
</ul>
<p><em>Note: Ideally the main side effect to be considered should be just the committing of the mutation, but we generally also test that the <code class="language-text">getCharacters</code> method is called from the <code class="language-text">rickmorty</code> module.</em></p>
<div class="gatsby-highlight" data-language="ts"><pre class="language-ts"><code class="language-ts"><span class="token keyword">import</span> actions <span class="token keyword">from</span> <span class="token string">'./actions'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token operator">*</span> <span class="token keyword">as</span> rickmorty <span class="token keyword">from</span> <span class="token string">'@/api/rickmorty'</span><span class="token punctuation">;</span>
jest<span class="token punctuation">.</span><span class="token function">mock</span><span class="token punctuation">(</span><span class="token string">'@/api/rickmorty'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">describe</span><span class="token punctuation">(</span><span class="token string">'getCharacters'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token function">beforeEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
jest<span class="token punctuation">.</span><span class="token function">clearAllMocks</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">it</span><span class="token punctuation">(</span><span class="token string">'should commit the default page and data from the api'</span><span class="token punctuation">,</span> <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token keyword">const</span> commit <span class="token operator">=</span> jest<span class="token punctuation">.</span><span class="token function">fn</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> data <span class="token operator">=</span> <span class="token punctuation">{</span> x<span class="token operator">:</span> <span class="token number">1</span> <span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token punctuation">(</span>rickmorty<span class="token punctuation">.</span>getCharacter <span class="token keyword">as</span> <span class="token builtin">any</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">mockImplementation</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token builtin">Promise</span><span class="token punctuation">.</span><span class="token function">resolve</span><span class="token punctuation">(</span>data<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">await</span> <span class="token punctuation">(</span>actions <span class="token keyword">as</span> <span class="token builtin">any</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getCharacters</span><span class="token punctuation">(</span><span class="token punctuation">{</span> commit <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">expect</span><span class="token punctuation">(</span>rickmorty<span class="token punctuation">.</span>getCharacter<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toHaveBeenCalled</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">expect</span><span class="token punctuation">(</span>commit<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toHaveBeenCalledWith</span><span class="token punctuation">(</span>
<span class="token string">'getCharactersSuccess'</span><span class="token punctuation">,</span>
<span class="token punctuation">{</span> data<span class="token punctuation">,</span> page<span class="token operator">:</span> <span class="token number">1</span> <span class="token punctuation">}</span>
<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">it</span><span class="token punctuation">(</span><span class="token string">'should commit the specified page and data from the API'</span><span class="token punctuation">,</span> <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token keyword">const</span> commit <span class="token operator">=</span> jest<span class="token punctuation">.</span><span class="token function">fn</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> data <span class="token operator">=</span> <span class="token punctuation">{</span> x<span class="token operator">:</span> <span class="token number">1</span> <span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token punctuation">(</span>rickmorty<span class="token punctuation">.</span>getCharacter <span class="token keyword">as</span> <span class="token builtin">any</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">mockImplementation</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token builtin">Promise</span><span class="token punctuation">.</span><span class="token function">resolve</span><span class="token punctuation">(</span>data<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">await</span> <span class="token punctuation">(</span>actions <span class="token keyword">as</span> <span class="token builtin">any</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getCharacters</span><span class="token punctuation">(</span><span class="token punctuation">{</span> commit <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> page<span class="token operator">:</span> <span class="token number">3</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">expect</span><span class="token punctuation">(</span>rickmorty<span class="token punctuation">.</span>getCharacter<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toHaveBeenCalledWith</span><span class="token punctuation">(</span><span class="token punctuation">{</span> page<span class="token operator">:</span> <span class="token number">3</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">expect</span><span class="token punctuation">(</span>commit<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toHaveBeenCalledWith</span><span class="token punctuation">(</span>
<span class="token string">'getCharactersSuccess'</span><span class="token punctuation">,</span>
<span class="token punctuation">{</span> data<span class="token punctuation">,</span> page<span class="token operator">:</span> <span class="token number">3</span> <span class="token punctuation">}</span>
<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div>
<p>We have written two test cases for the <code class="language-text">getCharacters</code> action. In both test cases, we mocked the <code class="language-text">rickmorty.getCharacter()</code> method to return some data, and we check that the method was called using <code class="language-text">expect().toHaveBeenCalled()</code>. We also check that the <code class="language-text">commit()</code> method was called with the <code class="language-text">getCharactersSuccess</code> mutation and the data containing the response from the API and the page number. In the first case we don’t pass in the page number as a parameter to the action, but we expect the default page number to be used and committed.</p>
<p>One thing to note here is that you can only use the <code class="language-text">.toHaveBeenCalled()</code>/<code class="language-text">.toHaveBeenCalledWith()</code> assertions for jest mock functions (<code class="language-text">jest.fn()</code>). In the test cases above, we can see that <code class="language-text">commit</code> is a jest mock function. We also mocked the <code class="language-text">rickmorty</code> module using the <code class="language-text">jest.mock(&#39;@/api/rickmorty&#39;)</code> statement at the top. This is jest’s “automatic mock” ability. With that, every method or property from the <code class="language-text">rickmorty</code> module is now a jest mock, and not the original implementation. So you can implement the mock to return specific outputs within a test case using the <a href="https://jestjs.io/docs/en/mock-function-api#mockfnmockimplementationfn" target="_blank" rel="nofollow"><code class="language-text">.mockImplementation()</code></a> factory method.
We mocked the <code class="language-text">rickmorty.getCharacter()</code> method to resolve with <code class="language-text">data</code> in the test cases above, so anytime that method is executed within thw action, it would return a promise that resolves with <code class="language-text">data</code>.</p>
<p>We also added a <code class="language-text">beforeEach()</code> method within the test suite that calls <code class="language-text">jest.clearAllMocks()</code>. This is already self-explanatory. Basically before each test case is run, the <code class="language-text">beforeEach()</code> method is executed, and it clears all the mocks and their implementations (like those done using <code class="language-text">mockImplementation</code>), ensuring that the mocks don’t have any stale data. This ensures the tests don’t get influenced by mocks from previous test cases.</p>
<blockquote>
<p>You can find out other ways to mock modules <a href="https://jestjs.io/docs/en/es6-class-mocks" target="_blank" rel="nofollow">here</a>.</p>
</blockquote>
<p>The same approach for testing this action can be applied to all the other actions as well. If an action simply dispatches another action, then check that the <code class="language-text">dispatch</code> is called with the right parameters. If it calls some other API to fetch or store data, or perform some other task, then check that the API was called from within the action. Remember the API should be mocked in the test since its implementation is not part of the action being tested.</p>
<p><em>Side Note: You might be wondering why we have things like <code class="language-text">(actions as any)</code> in the tests. This is so that typescript doesn’t throw errors. The type of <code class="language-text">actions</code> should be <code class="language-text">ActionTree</code> since it is the actions object, and you can’t directly call the functions in the actions (you would normally dispatch actions from your Vue components and Vue would handle calling the actions), so typescript doesn’t allow us use the action this way in the test, so we cast the <code class="language-text">actions</code> to <code class="language-text">any</code> to remove all the type checking. Similarly, <code class="language-text">(rickmorty.getCharacter as any)</code> is used since the module is mocked and now has the <code class="language-text">.mockImplementation()</code> method, but typescript doesn’t know that either, so we disable type checking there as well.</em></p>
<h3 id="mutation-getcharacterssuccess" style="position:relative;">Mutation: getCharactersSuccess</h3>
<p>Vuex mutations have only one job - to modify the state. Your mutations should be as simple as possible, and shouldn’t manage a lot of business logic. If you need to perform a lot of business logic before modifying the state, then you should consider performing those operations within an action, and only call the mutation when the state needs to be modified.</p>
<p>Let’s define the requirements for the <code class="language-text">getCharactersSuccess</code> mutation:</p>
<ul>
<li>it should be called with an object containing <code class="language-text">data</code> and <code class="language-text">page</code>.</li>
<li>it should store a list of character IDs.</li>
<li>it should store a map/lookup table of the characters by their IDs for easy retrieval of single characters.</li>
<li>it should store the <code class="language-text">info</code> returned from the API.</li>
<li>it should store the <code class="language-text">page</code> in the <code class="language-text">info</code> object.</li>
</ul>
<div class="gatsby-highlight" data-language="ts"><pre class="language-ts"><code class="language-ts"><span class="token keyword">import</span> Vue <span class="token keyword">from</span> <span class="token string">'vue'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> MutationTree <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'vuex'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> State<span class="token punctuation">,</span> CharacterDataModel <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'./state'</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> mutations<span class="token operator">:</span> MutationTree<span class="token operator">&lt;</span>State<span class="token operator">></span> <span class="token operator">=</span> <span class="token punctuation">{</span>
<span class="token function">getCharactersSuccess</span><span class="token punctuation">(</span><span class="token parameter">state<span class="token punctuation">,</span> <span class="token punctuation">{</span> page<span class="token punctuation">,</span> data <span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">const</span> list<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
data<span class="token punctuation">.</span>results<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">item<span class="token operator">:</span> CharacterDataModel</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
list<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>item<span class="token punctuation">.</span>id<span class="token punctuation">)</span><span class="token punctuation">;</span>
Vue<span class="token punctuation">.</span><span class="token keyword">set</span><span class="token punctuation">(</span>state<span class="token punctuation">.</span>characters<span class="token punctuation">.</span>map<span class="token punctuation">,</span> item<span class="token punctuation">.</span>id<span class="token punctuation">,</span> item<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
state<span class="token punctuation">.</span>characters<span class="token punctuation">.</span>list <span class="token operator">=</span> list<span class="token punctuation">;</span>
state<span class="token punctuation">.</span>characters<span class="token punctuation">.</span>info <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token operator">...</span>data<span class="token punctuation">.</span>info <span class="token punctuation">}</span><span class="token punctuation">;</span>
state<span class="token punctuation">.</span>characters<span class="token punctuation">.</span>info<span class="token punctuation">.</span>page <span class="token operator">=</span> page<span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token comment">// ...</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">export</span> <span class="token keyword">default</span> mutations<span class="token punctuation">;</span></code></pre></div>
<p>Looking at the public interface of this mutation, the <em>input</em> is an object containing <code class="language-text">page</code> and <code class="language-text">data</code>. The <em>output</em> (expected result of executing this mutation) is that the <code class="language-text">state</code> object is modified to contain the data as specified in the requirements. Similarly to the actions, the first input parameter to the mutation (<code class="language-text">state</code>) is provided by Vue, and is not something the user of the mutation can control.</p>
<div class="gatsby-highlight" data-language="ts"><pre class="language-ts"><code class="language-ts"><span class="token function">describe</span><span class="token punctuation">(</span><span class="token string">'getCharactersSuccess'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token function">it</span><span class="token punctuation">(</span><span class="token string">'should set the correct state values'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token keyword">const</span> state <span class="token operator">=</span> <span class="token punctuation">{</span>
characters<span class="token operator">:</span> <span class="token punctuation">{</span>
info<span class="token operator">:</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">,</span>
list<span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
map<span class="token operator">:</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> payload <span class="token operator">=</span> <span class="token punctuation">{</span>
info<span class="token operator">:</span> <span class="token punctuation">{</span> reqDetails<span class="token operator">:</span> <span class="token number">1</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
results<span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token punctuation">{</span>
id<span class="token operator">:</span> <span class="token number">1</span><span class="token punctuation">,</span>
name<span class="token operator">:</span> <span class="token string">'First'</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">{</span>
id<span class="token operator">:</span> <span class="token number">2</span><span class="token punctuation">,</span>
name<span class="token operator">:</span> <span class="token string">'Second'</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token punctuation">(</span>mutations <span class="token keyword">as</span> <span class="token builtin">any</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getCharactersSuccess</span><span class="token punctuation">(</span>state<span class="token punctuation">,</span> <span class="token punctuation">{</span> data<span class="token operator">:</span> payload<span class="token punctuation">,</span> page<span class="token operator">:</span> <span class="token number">1</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">expect</span><span class="token punctuation">(</span>state<span class="token punctuation">.</span>characters<span class="token punctuation">.</span>list<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toEqual</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">expect</span><span class="token punctuation">(</span>state<span class="token punctuation">.</span>characters<span class="token punctuation">.</span>map<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toEqual</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
<span class="token number">1</span><span class="token operator">:</span> <span class="token punctuation">{</span>
id<span class="token operator">:</span> <span class="token number">1</span><span class="token punctuation">,</span>
name<span class="token operator">:</span> <span class="token string">'First'</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token number">2</span><span class="token operator">:</span> <span class="token punctuation">{</span>
id<span class="token operator">:</span> <span class="token number">2</span><span class="token punctuation">,</span>
name<span class="token operator">:</span> <span class="token string">'Second'</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">expect</span><span class="token punctuation">(</span>state<span class="token punctuation">.</span>characters<span class="token punctuation">.</span>info<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toEqual</span><span class="token punctuation">(</span><span class="token punctuation">{</span> reqDetails<span class="token operator">:</span> <span class="token number">1</span><span class="token punctuation">,</span> page<span class="token operator">:</span> <span class="token number">1</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div>
<p>There isn’t much variation in this mutation. It only accepts one type of data, and always modifies the state in the same way. No conditionals, no short circuiting, no default values. Therefore we only need one test case to cover this mutation.</p>
<p>We call the mutation with a <code class="language-text">state</code> object and an object containing <code class="language-text">data</code> and <code class="language-text">page</code>.
Then we check that the state contains a list of character IDs.
We check that the state contains a mapping of character IDs to characters.
We check that the state contains the <code class="language-text">info</code> object as well as the <code class="language-text">page</code> property.</p>
<p>One thing to note is the use of <a href="https://jestjs.io/docs/en/expect#toequalvalue" target="_blank" rel="nofollow"><code class="language-text">.toEqual()</code></a> instead of the more common <a href="https://jestjs.io/docs/en/expect#tobevalue" target="_blank" rel="nofollow"><code class="language-text">.toBe()</code></a> assertion. The reason for this is that we want to compare the values based on their value, and not by reference. The <code class="language-text">.toBe()</code> would use the strict comparison <code class="language-text">===</code> to compare the two values, which means two objects containing the same sets of data <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators#Identity" target="_blank" rel="nofollow">would not be equal</a> since they have different references. <code class="language-text">.toEqual()</code> recursively compares all the properties of the values instead of just checking their referential identity.</p>
<blockquote>
<p>Should we mock <code class="language-text">Vue.set</code>? If we want to be as strict as possible, then I believe you should mock it. However, in this context, <code class="language-text">Vue.set</code> simply assigns a value to an object property, like doing <code class="language-text">object.a = 1;</code>. For me, its presence in the code is as normal as native JavaScript methods and as such, I don’t really consider it as something that needs to be mocked, but that might just be a bias on my part. Either way, mocking it is rather easy:</p>
</blockquote>
<div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js">jest<span class="token punctuation">.</span><span class="token function">mock</span><span class="token punctuation">(</span><span class="token string">'vue'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> <span class="token punctuation">{</span>
<span class="token keyword">set</span><span class="token punctuation">(</span>target<span class="token punctuation">,</span> prop<span class="token punctuation">,</span> value<span class="token punctuation">)</span> <span class="token punctuation">{</span>
target<span class="token punctuation">[</span>prop<span class="token punctuation">]</span> <span class="token operator">=</span> value<span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div>
<h3 id="getter-characterlist" style="position:relative;">Getter: characterList</h3>
<p>Vuex getters return a slice of the state, or data derived from the state. Getters are similar to the computed properties in Vue components in that they are cached and reactive, so they only get computed whenever the input changes, in this case the input is the state. They can either return a value or return a function.</p>
<p>We would be looking at the <code class="language-text">characterList</code> getter which returns a list of character data based on the list of character IDs and the mapping of character ID to character data we already have in the state, from the <code class="language-text">getCharactersSuccess</code> mutation above.</p>
<p>The requirements of the <code class="language-text">characterList</code> getter are very simple:</p>
<ul>
<li>it takes the state as the only argument which is provided by Vue and the user wouldn’t have control over it. So using it in the vue context, the user would provide no parameter.</li>
<li>it returns an array of character data based on the list of character IDs and character data mappings.</li>
</ul>
<div class="gatsby-highlight" data-language="ts"><pre class="language-ts"><code class="language-ts"><span class="token keyword">import</span> <span class="token punctuation">{</span> GetterTree <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'vuex'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> State <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'./state'</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> getters<span class="token operator">:</span> GetterTree<span class="token operator">&lt;</span>State<span class="token punctuation">,</span> State<span class="token operator">></span> <span class="token operator">=</span> <span class="token punctuation">{</span>
<span class="token function">characterList</span><span class="token punctuation">(</span><span class="token parameter">state</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> state<span class="token punctuation">.</span>characters<span class="token punctuation">.</span>list<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token parameter">id</span> <span class="token operator">=></span> state<span class="token punctuation">.</span>characters<span class="token punctuation">.</span>map<span class="token punctuation">[</span>id<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token comment">// ...</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">export</span> <span class="token keyword">default</span> getters<span class="token punctuation">;</span></code></pre></div>
<p>The getters are generally pure functions, so we know that the output is only dependent on the input, and there is no side effect either. Looking at the public interface of the <code class="language-text">characterList</code> getter, it accepts one input (<code class="language-text">state</code>) and always returns an array of zero or more character data.</p>
<div class="gatsby-highlight" data-language="ts"><pre class="language-ts"><code class="language-ts"><span class="token keyword">const</span> state <span class="token operator">=</span> <span class="token punctuation">{</span>
characters<span class="token operator">:</span> <span class="token punctuation">{</span>
list<span class="token operator">:</span> <span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
map<span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token number">1</span><span class="token operator">:</span> <span class="token punctuation">{</span>
x<span class="token operator">:</span> <span class="token number">1</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token number">2</span><span class="token operator">:</span> <span class="token punctuation">{</span>
y<span class="token operator">:</span> <span class="token number">2</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token function">describe</span><span class="token punctuation">(</span><span class="token string">'characterList'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token function">it</span><span class="token punctuation">(</span><span class="token string">'should return list of character data'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token function">expect</span><span class="token punctuation">(</span><span class="token punctuation">(</span>getters <span class="token keyword">as</span> <span class="token builtin">any</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">characterList</span><span class="token punctuation">(</span>state<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toEqual</span><span class="token punctuation">(</span><span class="token punctuation">[</span>
<span class="token punctuation">{</span>
x<span class="token operator">:</span> <span class="token number">1</span><span class="token punctuation">,</span>