-
Notifications
You must be signed in to change notification settings - Fork 339
/
tests-jsrender-no-jquery.js
2538 lines (2103 loc) · 125 KB
/
tests-jsrender-no-jquery.js
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
/*global QUnit*/
(function(undefined) {
"use strict";
var global = (0, eval)('this'), // jshint ignore:line
isBrowser = !!global.document,
$ = global.jsrender || global.jQuery; // On Node.js with QUnit, jsrender is added as namespace, to global
if (!isBrowser) {
global.document = {};
}
function compileTmpl(template) {
try {
return typeof $.templates(template).fn === "function" ? "compiled" : "failed compile";
} catch(e) {
return e.message;
}
}
function sort(array) {
var ret = "";
if (this.tagCtx.props.reverse) {
// Render in reverse order
if (arguments.length > 1) {
for (i = arguments.length; i; i--) {
ret += sort.call(this, arguments[ i - 1 ]);
}
} else {
for (var i = array.length; i; i--) {
ret += this.tagCtx.render(array[ i - 1 ]);
}
}
} else {
// Render in original order
ret += this.tmpl.render(array);
}
return ret;
}
var person = {name: "Jo"},
people = [{name: "Jo"}, {name: "Bill"}],
towns = [{name: "Seattle"}, {name: "Paris"}, {name: "Delhi"}];
var tmplString = "A_{{:name}}_B";
$.views.tags({sort: sort});
QUnit.module("tagParser");
QUnit.test("{{if}} {{else}}", function(assert) {
assert.equal(compileTmpl("A_{{if true}}{{/if}}_B"), "compiled", "Empty if block: {{if}}{{/if}}");
assert.equal(compileTmpl("A_{{if true}}yes{{/if}}_B"), "compiled", "{{if}}...{{/if}}");
assert.equal(compileTmpl("A_{{if true/}}yes{{/if}}_B"), "Syntax error\nUnmatched or missing {{/if}}, in template:\nA_{{if true/}}yes{{/if}}_B", "unmatched or missing tag error");
assert.equal($.templates("<span id='x'></span> a'b\"c\\").render(), "<span id=\'x\'></span> a\'b\"c\\", "Correct escaping of quotes and backslash");
});
QUnit.test("syntax errors", function(assert) {
assert.equal(compileTmpl("{^{*:foo}}"), "Syntax error\n{^{*:foo}}", "Syntax error for {^{* ...}}");
assert.equal(compileTmpl("{{:foo/}}"), "Syntax error\n{{:foo/}}", "Syntax error for {{: ... /}}");
assert.equal(compileTmpl("{{:foo:}}"), "Syntax error\n{{:foo:}}", "Syntax error for {{: ... :}}");
assert.equal(compileTmpl("{^{:foo:}}"), "Syntax error\n{^{:foo:}}", "Syntax error for {^{: ... :}}");
assert.equal(compileTmpl("{{mytag foo :}}"), "Syntax error\n{{mytag foo :}}", "Syntax error for {{mytag ... :}}");
assert.equal(compileTmpl("{^{mytag foo :}}"), "Syntax error\n{^{mytag foo :}}", "Syntax error for {^{mytag ... :}}");
assert.equal(compileTmpl("{{if foo?bar:baz}}{{/if}}"), "compiled", "No syntax error for {{tag foo?bar:baz}}");
assert.equal(compileTmpl("{{for [1,2]/}}"), "Syntax error\n[1,2]", "Syntax error for {{for [1,2]}} - top-level array");
assert.equal(compileTmpl("{{:constructor()}}"), "Syntax error\nconstructor", "Syntax error for {{: ...constructor ...}}");
assert.equal(compileTmpl("{{for #tmpl.constructor()}}"), "Syntax error\n#tmpl.constructor", "Syntax error for {{for ...constructor ...}}");
$.views.settings.debugMode(true);
assert.equal($.templates('{{:#data["constructor"]["constructor"]("alert(0);")()}}').render(), "{Error: Syntax error\n}", 'Syntax error 1 for ["constructor]"');
assert.equal($.templates('{{:valueOf["constructor"]("alert(1);")()}}').render(1), "{Error: Syntax error\n}", 'Syntax error 2 for ["constructor]"');
assert.equal($.templates('{{:valueOf["const"+"ructor"]("alert(2);")()}}').render(1), "{Error: Syntax error\n}", 'Syntax error 3 for ["constructor]"');
assert.equal($.templates('{{if true ~c=toString["con" + foo + "or"]}}{{:convert=~c("alert(3);")}}{{/if}}').render({foo: "struct"}), "{Error: Syntax error\n}", 'Syntax error 1 for indirect ["constructor]"');
assert.equal($.templates('{{if true ~tmp="constructo"}}{{if true ~tmp2="r"}}{{:toString[~tmp + ~tmp2]}}{{/if}}{{/if}}').render(1), "{Error: Syntax error\n}", 'Syntax error 2 for indirect ["constructor]"');
$.views.settings.debugMode(false);
});
QUnit.module("{{if}}");
QUnit.test("{{if}}", function(assert) {
assert.equal($.templates("A_{{if true}}yes{{/if}}_B").render(), "A_yes_B", "{{if a}}: a");
assert.equal($.templates("A_{{if false}}yes{{/if}}_B").render(), "A__B", "{{if a}}: !a");
assert.equal($.templates("A_{{if true}}{{/if}}_B").render(), "A__B", "{{if a}}: empty: a");
assert.equal($.templates("A_{{if false}}{{/if}}_B").render(), "A__B", "{{if a}}: empty: !a");
});
QUnit.test("{{if}} {{else}}", function(assert) {
assert.equal($.templates("A_{{if true}}yes{{else}}no{{/if}}_B").render(), "A_yes_B", "{{if a}} {{else}}: a");
assert.equal($.templates("A_{{if false}}yes{{else}}no{{/if}}_B").render(), "A_no_B", "{{if a}} {{else}}: !a");
assert.equal($.templates("A_{{if true}}yes{{else true}}or{{else}}no{{/if}}_B").render(), "A_yes_B", "{{if a}} {{else b}} {{else}}: a");
assert.equal($.templates("A_{{if false}}yes{{else true}}or{{else}}no{{/if}}_B").render(), "A_or_B", "{{if a}} {{else b}} {{else}}: b");
assert.equal($.templates("A_{{if false}}yes{{else false}}or{{else}}no{{/if}}_B").render(), "A_no_B", "{{if a}} {{else b}} {{else}}: !a!b");
assert.equal($.templates("A_{{if undefined}}yes{{else true}}or{{else}}no{{/if}}_B").render({}), "A_or_B", "{{if undefined}} {{else b}} {{else}}: !a!b");
assert.equal($.templates("A_{{if false}}yes{{else undefined}}or{{else}}no{{/if}}_B").render({}), "A_no_B", "{{if a}} {{else undefined}} {{else}}: !a!b");
assert.equal($.templates("A_{{if false}}<div title='yes'{{else}}<div title='no'{{/if}}>x</div>_B").render(), "A_<div title='no'>x</div>_B", "{{if}} and {{else}} work across HTML tags");
assert.equal($.templates("A_<div title='{{if true}}yes'{{else}}no'{{/if}}>x</div>_B").render(), "A_<div title='yes'>x</div>_B", "{{if}} and {{else}} work across quoted strings");
});
QUnit.test("{{if}} {{else}} external templates", function(assert) {
assert.equal($.templates("A_{{if true tmpl='yes<br/>'/}}_B").render(), "A_yes<br/>_B", "{{if a tmpl=foo/}}: a");
assert.equal($.templates("A_{{if false tmpl='yes<br/>'}}{{else false tmpl='or<br/>'}}{{else tmpl='no<br/>'}}{{/if}}_B").render(), "A_no<br/>_B", "{{if a tmpl=foo}}{{else b tmpl=bar}}{{else tmpl=baz}}: !a!b");
});
QUnit.module("{{:}}");
QUnit.test("convert", function(assert) {
assert.equal($.templates("{{>#data}}").render("<br/>'\"&"), "<br/>'"&", "default html converter");
assert.equal($.templates("{{html:#data}}").render("<br/>'\"&"), "<br/>'"&", "html converter");
assert.equal($.templates("{{:#data}}").render("<br/>'\"&"), "<br/>'\"&", "no convert");
function loc(data) {
switch (data) {case "desktop": return "bureau";}
}
$.views.converters("loc", loc);
assert.equal($.templates("{{loc:#data}}:{{loc:'desktop'}}").render("desktop"), "bureau:bureau", '$.views.converters("loc", locFunction);... {{loc:#data}}');
});
QUnit.test("paths", function(assert) {
assert.equal($.templates("{{:a}}").render({a: "aVal"}), "aVal", "a");
assert.equal($.templates("{{:a.b}}").render({a: {b: "bVal"}}), "bVal", "a.b");
assert.equal($.templates("{{:a.b.c}}").render({a: {b: {c: "cVal"}}}), "cVal", "a.b.c");
assert.equal($.templates("{{:a.name}}").render({a: {name: "aName"}}), "aName", "a.name");
assert.equal($.templates("{{:a['name']}}").render({a: {name: "aName"}}), "aName", "a['name']");
assert.equal($.templates("{{:a['x - _*!']}}").render({a: {"x - _*!": "aName"}}), "aName", "a['x - _*!']");
assert.equal($.templates("{{:#data['x - _*!']}}").render({"x - _*!": "aName"}), "aName", "#data['x - _*!']");
assert.equal($.templates('{{:a["x - _*!"]}}').render({a: {"x - _*!": "aName"}}), "aName", 'a["x - _*!"]');
assert.equal($.templates("{{:a.b[1].d}}").render({a: {b: [0, {d: "dVal"}]}}), "dVal", "a.b[1].d");
assert.equal($.templates("{{:a.b[1].d}}").render({a: {b: {1:{d: "dVal"}}}}), "dVal", "a.b[1].d");
assert.equal($.templates("{{:a.b[~incr(1-1)].d}}").render({a: {b: {1:{d: "dVal"}}}}, {incr:function(val) {return val + 1;}}), "dVal", "a.b[~incr(1-1)].d");
assert.equal($.templates("{{:a.b.c.d}}").render({a: {b: {c:{d: "dVal"}}}}), "dVal", "a.b.c.d");
assert.equal($.templates("{{:a[0]}}").render({a: [ "bVal" ]}), "bVal", "a[0]");
assert.equal($.templates("{{:a.b[1][0].msg}}").render({a: {b: [22,[{msg: " yes - that's right. "}]]}}), " yes - that's right. ", "a.b[1][0].msg");
assert.equal($.templates("{{:#data.a}}").render({a: "aVal"}), "aVal", "#data.a");
assert.equal($.templates("{{:#view.data.a}}").render({a: "aVal"}), "aVal", "#view.data.a");
assert.equal($.templates("{{:#index === 0}}").render([{a: "aVal"}]), "true", "#index");
});
QUnit.test("types", function(assert) {
assert.equal($.templates("{{:'abc'}}").render(), "abc", "'abc'");
assert.equal($.templates('{{:"abc"}}').render(), "abc", '"abc"');
assert.equal($.templates("{{:true}}").render(), "true", "true");
assert.equal($.templates("{{:false}}").render(), "false", "false");
assert.equal($.templates("{{:null}}").render(), "", 'null -> ""');
assert.equal($.templates("{{:199}}").render(), "199", "199");
assert.equal($.templates("{{: 199.9}}").render(), "199.9", "| 199.9 |");
assert.equal($.templates("{{:-33.33}}").render(), "-33.33", "-33.33");
assert.equal($.templates("{{: -33.33}}").render(), "-33.33", "| -33.33 |");
assert.equal($.templates("{{:-33.33 - 2.2}}").render(), "-35.53", "-33.33 - 2.2");
assert.equal($.templates("{{:notdefined}}").render({}), "", "notdefined");
assert.equal($.templates("{{:}}").render("aString"), "aString", "{{:}} returns current data item");
assert.equal($.templates("{{:x=22}}").render("aString"), "aString", "{{:x=...}} returns current data item");
assert.equal($.templates("{{html:x=22}}").render("aString"), "aString", "{{html:x=...}} returns current data item");
assert.equal($.templates("{{>x=22}}").render("aString"), "aString", "{{>x=...}} returns current data item");
assert.equal($.templates("{{:'abc('}}").render(), "abc(", "'abc(': final paren in string is rendered correctly"); // https://github.com/BorisMoore/jsviews/issues/300
assert.equal($.templates('{{:"abc("}}').render(), "abc(", '"abc(": final paren in string is rendered correctly');
assert.equal($.templates("{{:(('(abc('))}}").render(), "(abc(", "(('(abc('))");
assert.equal($.templates('{{:((")abc)"))}}').render(), ")abc)", '((")abc)"))');
});
QUnit.test("Fallbacks for missing or undefined paths:\nusing {{:some.path onError = 'fallback'}}, etc.", function(assert) {
var message;
try {
$.templates("{{:a.missing.willThrow.path}}").render({a:1});
} catch(e) {
message = e.message;
}
assert.ok(!!message,
"{{:a.missing.willThrow.path}} throws: " + message);
assert.equal($.templates("{{:a.missing.willThrow.path onError='Missing Object'}}").render({a:1}), "Missing Object",
'{{:a.missing.willThrow.path onError="Missing Object"}} renders "Missing Object"');
assert.equal($.templates('{{:a.missing.willThrow.path onError=""}}').render({a:1}), "",
'{{:a.missing.willThrow.path onError=""}} renders ""');
assert.equal($.templates("{{:a.missing.willThrow.path onError=null}}").render({a:1}), "",
'{{:a.missing.willThrow.path onError=null}} renders ""');
assert.equal($.templates("{{>a.missing.willThrow.path onError='Missing Object'}}").render({a:1}), "Missing Object",
'{{>a.missing.willThrow.path onError="Missing Object"}} renders "Missing Object"');
assert.equal($.templates('{{>a.missing.willThrow.path onError=""}}').render({a:1}), "",
'{{>a.missing.willThrow.path onError=""}} renders ""');
assert.equal($.templates("{{>a.missing.willThrow.path onError=defaultVal}}").render(
{
a:1,
defaultVal: "defaultFromData"
}), "defaultFromData",
'{{>a.missing.willThrow.path onError=defaultVal}} renders "defaultFromData"');
assert.equal($.templates("{{>a.missing.willThrow.path onError=~myOnErrorFunction}}").render({a:1}, {
myOnErrorFunction: function(e, view) {
return "Override onError using a callback: " + view.ctx.helperValue + e.message;
},
helperValue: "hlp"
}).slice(0, 38), "Override onError using a callback: hlp",
'{{>a.missing.willThrow.path onError=~myOnErrorFunction}}" >' +
'\nProviding a function "onError=~myOnErrorFunction" calls the function as onError callback');
assert.equal($.templates("{{>a.missing.willThrow.path onError=myOnErrorDataMethod}}").render(
{
a: "dataValue",
myOnErrorDataMethod: function(e) {
var data = this;
return "Override onError using a callback data method: " + data.a;
}
}), "Override onError using a callback data method: dataValue",
'{{>a.missing.willThrow.path onError=myOnErrorDataMethod}}" >' +
'\nProviding a function "onError=myOnErrorDataMethod" calls the function as onError callback');
assert.equal($.templates("1: {{>a.missing.willThrow.path onError=defaultVal}}" +
" 2: {{:a.missing.willThrow.path onError='Missing Object'}}" +
" 3: {{:a.missing.willThrow.path onError=''}}" +
" 4: {{:a.missing.willThrow.path onError=null}}" +
" 5: {{:a onError='missing'}}" +
" 6: {{:a.undefined onError='missing'}}" +
" 7: {{:a.missing.willThrow onError=myCb}} end").render(
{
a:"aVal",
defaultVal: "defaultFromData",
myCb: function(e, view) {
return "myCallback: " + this.a;
}
}), "1: defaultFromData 2: Missing Object 3: 4: 5: aVal 6: 7: myCallback: aVal end",
'multiple onError fallbacks in same template - correctly concatenated into output');
assert.equal($.templates({
markup: "{{withfallback:a.notdefined fallback='fallback for undefined'}}",
converters: {
withfallback: function(val) {
return val || this.tagCtx.props.fallback;
}
}
}).render({a:"yes"}), "fallback for undefined",
'{{withfallback:a.notdefined fallback="fallback for undefined"}}' +
'\nusing converter to get fallback value for undefined properties');
assert.equal($.templates({
markup: "1: {{withfallback:a.missing.y onError='Missing object' fallback='undefined prop'}}" +
" 2: {{withfallback:a.undefined onError='Missing object' fallback='undefined prop'}}",
converters: {
withfallback: function(val) {
return val || this.tagCtx.props.fallback;
}
}
}).render({a:"yes"}), "1: Missing object 2: undefined prop",
'both fallback for undefined and onError for missing on same tags');
assert.equal($.templates({
markup: "1: {{>a.missing.willThrow.path onError=defaultVal}}" +
" 2: {{:a.missing.willThrow.path onError='Missing Object'}}" +
" 3: {{:a.missing.willThrow.path onError=''}}" +
" 4: {{:a onError='missing'}}" +
" 5: {{:a.undefined onError='missing'}}" +
" 6: {{:a.missing.willThrow onError=myCb}}" +
" 7: {{withfallback:a.undefined fallback='undefined prop'}} end",
converters: {
withfallback: function(val) {
return val || this.tagCtx.props.fallback;
}
}
}).render(
{
a:"aVal",
defaultVal: "defaultFromData",
myCb: function(e, view) {
return "myCallback: " + this.a;
}
}), "1: defaultFromData 2: Missing Object 3: 4: aVal 5: 6: myCallback: aVal 7: undefined prop end",
'multiple onError fallbacks or undefined property fallbacks in same template - correctly concatenated into output');
try {
message = "";
$.templates({
markup: "1: {{>a.missing.willThrow.path onError=defaultVal}}" +
" 2: {{:a.missing.willThrow.path onError='Missing Object'}}" +
" 3: {{:a.missing.willThrow.path onError=''}}" +
" 4: {{:a onError='missing'}}" +
" 5: {{:a.missing.willThrow.foo}}" +
" 6: {{:a.undefined onError='missing'}}" +
" 7: {{:a.missing.willThrow onError=myCb}}" +
" 8: {{withfallback:a.undefined fallback='undefined prop'}} end",
converters: {
withfallback: function(val) {
return val || this.tagCtx.props.fallback;
}
}
}).render({
a:"aVal",
defaultVal: "defaultFromData",
myCb: function(e, view) {
return "myCallback: " + this.a;
}
});
} catch(e) {
message = e.message;
}
assert.ok(!!message,
'onError/fallback converter and regular thrown error message in same template: throws:\n"' + message + '"');
assert.equal($.templates("{{for missing.willThrow.path onError='Missing Object'}}yes{{/for}}").render({a:1}), "Missing Object",
'{{for missing.willThrow.path onError="Missing Object"}} -> "Missing Object"');
assert.equal($.templates("{{for true missing.willThrow.path onError='Missing Object'}}yes{{/for}}").render({a:1}), "Missing Object",
'{{for true missing.willThrow.path onError="Missing Object"}} -> "Missing Object"');
assert.equal($.templates("{{for true foo=missing.willThrow.path onError='Missing Object'}}yes{{/for}}").render({a:1}), "Missing Object",
'{{for ... foo=missing.willThrow.path onError="Missing Object"}} -> "Missing Object"');
assert.equal($.templates("{{for true ~foo=missing.willThrow.path onError='Missing Object'}}yes{{/for}}").render({a:1}), "Missing Object",
'{{for ... ~foo=missing.willThrow.path onError="Missing Object"}} -> "Missing Object"');
assert.equal($.templates({
markup: "{{mytag foo='a'/}} {{mytag foo=missing.willThrow.path onError='Missing Object'/}} {{mytag foo='c' bar=missing.willThrow.path onError='Missing Object'/}} {{mytag foo='c' missing.willThrow.path onError='Missing Object'/}} {{mytag foo='b'/}}",
tags: {
mytag: {template: "MyTag: {{:~tagCtx.props.foo}} end"}
}
}).render({a:1}), "MyTag: a end Missing Object Missing Object Missing Object MyTag: b end",
'onError=... for custom tags: e.g. {{mytag foo=missing.willThrow.path onError="Missing Object"/}}');
assert.equal($.templates({
markup: "1: {{for a.missing.willThrow.path onError=defaultVal}}yes{{/for}}" +
" 2: {{if a.missing.willThrow.path onError='Missing Object'}}yes{{/if}}" +
" 3: {{include a.missing.willThrow.path onError=''/}}" +
" 4: {{if a onError='missing'}}yes{{/if}}" +
" 5: {{for a.undefined onError='missing'}}yes{{/for}}" +
" 6: {{if a.missing.willThrow onError=myCb}}yes{{/if}}" +
" 7: {{withfallback:a.undefined fallback='undefined prop'}} end" +
" 8: {{mytag foo=missing.willThrow.path onError='Missing Object'/}}",
converters: {
withfallback: function(val) {
return val || this.tagCtx.props.fallback;
}
},
tags: {
mytag: {template: "MyTag: {{:~tagCtx.props.foo}} end"}
}
}).render(
{
a:"aVal",
defaultVal: "defaultFromData",
myCb: function(e, view) {
return "myCallback: " + this.a;
}
}), "1: defaultFromData 2: Missing Object 3: 4: yes 5: 6: myCallback: aVal 7: undefined prop end 8: Missing Object",
'multiple onError fallbacks or undefined property fallbacks in same template - correctly concatenated into output');
try {
message = "";
$.templates({
markup: "1: {{for a.missing.willThrow.path onError=defaultVal}}yes{{/for}}" +
" 2: {{if a.missing.willThrow.path onError='Missing Object'}}yes{{/if}}" +
" 3: {{include a.missing.willThrow.path onError=''/}}" +
" 4: {{if a onError='missing'}}yes{{/if}}" +
" 5: {{for missing.willThrow.foo}}yes{{/for}}" +
" 6: {{for a.undefined onError='missing'}}yes{{/for}}" +
" 7: {{if a.missing.willThrow onError=myCb}}yes{{/if}}" +
" 8: {{withfallback:a.undefined fallback='undefined prop'}} end",
converters: {
withfallback: function(val) {
return val || this.tagCtx.props.fallback;
}
}
}).render({
a:"aVal",
defaultVal: "defaultFromData",
myCb: function(e, view) {
return "myCallback: " + this.a;
}
});
} catch(e) {
message = e.message;
}
assert.ok(!!message,
'onError/fallback converter and regular thrown error message in same template: throws: \n"' + message + '"');
$.views.settings.debugMode(true);
assert.equal($.templates({
markup: "1: {{for a.missing.willThrow.path onError=defaultVal}}yes{{/for}}" +
" 2: {{if a.missing.willThrow.path onError='Missing Object'}}yes{{/if}}" +
" 3: {{include a.missing.willThrow.path onError=''/}}" +
" 4: {{if a onError='missing'}}yes{{/if}}" +
" 5: {{for missing.willThrow.foo}}yes{{/for}}" +
" 6: {{for a.undefined onError='missing'}}yes{{/for}}" +
" 7: {{if a.missing.willThrow onError=myCb}}yes{{/if}}" +
" 8: {{withfallback:a.undefined fallback='undefined prop'}} end",
converters: {
withfallback: function(val) {
return val || this.tagCtx.props.fallback;
}
}
}).render(
{
a:"aVal",
defaultVal: "defaultFromData",
myCb: function(e, view) {
return "myCallback: " + this.a;
}
}).slice(0, 21), "1: defaultFromData 2:",
'In debug mode, onError/fallback converter and regular thrown error message in same template:' +
'\override errors and regular thrown error each render for the corrresponding tag');
$.views.settings.debugMode(false);
});
QUnit.test("comparisons", function(assert) {
assert.equal($.templates("{{:1<2}}").render(), "true", "1<2");
assert.equal($.templates("{{:2<1}}").render(), "false", "2<1");
assert.equal($.templates("{{:5===5}}").render(), "true", "5===5");
assert.equal($.templates("{{:0==''}}").render(), "true", "0==''");
assert.equal($.templates("{{:'ab'=='ab'}}").render(), "true", "'ab'=='ab'");
assert.equal($.templates("{{:2>1}}").render(), "true", "2>1");
assert.equal($.templates("{{:2 == 2}}").render(), "true", "2 == 2");
assert.equal($.templates("{{:2<=2}}").render(), "true", "2<=2");
assert.equal($.templates("{{:'ab'<'ac'}}").render(), "true", "'ab'<'ac'");
assert.equal($.templates("{{:3>=3}}").render(), "true", "3 =3");
assert.equal($.templates("{{:3>=2}}").render(), "true", "3>=2");
assert.equal($.templates("{{:3>=4}}").render(), "false", "3>=4");
assert.equal($.templates("{{:3 !== 2}}").render(), "true", "3 !== 2");
assert.equal($.templates("{{:3 != 2}}").render(), "true", "3 != 2");
assert.equal($.templates("{{:0 !== null}}").render(), "true", "0 !== null");
assert.equal($.templates("{{:(3 >= 4)}}").render(), "false", "3>=4");
assert.equal($.templates("{{:3 >= 4}}").render(), "false", "3>=4");
assert.equal($.templates("{{:(3>=4)}}").render(), "false", "3>=4");
assert.equal($.templates("{{:(3 < 4)}}").render(), "true", "3>=4");
assert.equal($.templates("{{:3 < 4}}").render(), "true", "3>=4");
assert.equal($.templates("{{:(3<4)}}").render(), "true", "3>=4");
assert.equal($.templates("{{:0 != null}}").render(), "true", "0 != null");
});
QUnit.test("array access", function(assert) {
assert.equal($.templates("{{:a[1]}}").render({a: ["a0","a1"]}), "a1", "a[1]");
assert.equal($.templates("{{:a[1+1]+5}}").render({a: [11,22,33]}), "38", "a[1+1]+5)");
assert.equal($.templates("{{:a[~incr(1)]+5}}").render({a: [11,22,33]}, {incr:function(val) {return val + 1;}}), "38", "a[~incr(1)]+5");
assert.equal($.templates("{{:true && (a[0] || 'default')}}").render({a: [0,22,33]}, {incr:function(val) {return val + 1;}}), "default", "true && (a[0] || 'default')");
});
QUnit.test("context", function(assert) {
assert.equal($.templates("{{:~val}}").render(1, {val: "myvalue"}), "myvalue", "~val");
function format(value, upper) {
return value[upper ? "toUpperCase" : "toLowerCase"]();
}
assert.equal($.templates("{{:~format(name) + ~format(name, true)}}").render(person, {format: format}), "joJO",
"render(data, {format: formatFn}); ... {{:~format(name, true)}}");
assert.equal($.templates("{{for people[0]}}{{:~format(~type) + ~format(name, true)}}{{/for}}").render({people: people}, {format: format, type: "PascalCase"}), "pascalcaseJO",
"render(data, {format: formatFn}); ... {{:~format(name, true)}}");
assert.equal($.templates("{{for people ~twn=town}}{{:name}} lives in {{:~format(~twn, true)}}. {{/for}}").render({people: people, town:"Redmond"}, {format: format}),
"Jo lives in REDMOND. Bill lives in REDMOND. ",
"Passing in context to nested templates: {{for people ~twn=town}}");
assert.equal($.templates("{{if true}}{{for people}}{{:~root.people[0].name}}{{/for}}{{/if}}").render({people: people}), "JoJo",
"{{:~root}} returns the top-level data");
});
QUnit.test("values", function(assert) {
assert.equal($.templates("{{:a}}").render({a: 0}), "0", '{{:0}} returns "0"');
assert.equal($.templates("{{:a}}").render({}), "", "{{:undefined}} returns empty string");
assert.equal($.templates("{{:a}}").render({a: ""}), "", "{{:''}} returns empty string");
assert.equal($.templates("{{:a}}").render({a: null}), "", "{{:null}} returns empty string");
});
QUnit.test("expressions", function(assert) {
assert.equal(compileTmpl("{{:a++}}"), "Syntax error\na++", "a++");
assert.equal(compileTmpl("{{:(a,b)}}"), "Syntax error\n(a,b)", "(a,b)");
assert.equal($.templates("{{: a+2}}").render({a: 2, b: false}), "4", "a+2");
assert.equal($.templates("{{: b?'yes':'no'}}").render({a: 2, b: false}), "no", "b?'yes':'no'");
assert.equal($.templates("{{:(a||-1) + (b||-1)}}").render({a: 2, b: 0}), "1", "a||-1");
assert.equal($.templates("{{:3*b()*!a*4/3}}").render({a: false, b: function() {return 3;}}), "12", "3*b()*!a*4/3");
assert.equal($.templates("{{:a%b}}").render({a: 30, b: 16}), "14", "a%b");
assert.equal($.templates("A_{{if v1 && v2 && v3 && v4}}no{{else !v1 && v2 || v3 && v4}}yes{{/if}}_B").render({v1:true,v2:false,v3:2,v4:"foo"}), "A_yes_B", "x && y || z");
assert.equal($.templates("{{:!true}}").render({}), "false", "!true");
assert.equal($.templates("{{if !true}}yes{{else}}no{{/if}}").render({}), "no", "{{if !true}}...");
assert.equal($.templates("{{:!false}}").render({}), "true", "!false");
assert.equal($.templates("{{if !false}}yes{{else}}no{{/if}}").render({}), "yes", "{{if !false}}...");
assert.equal($.templates("{{:!!true}}").render({}), "true", "!!true");
assert.equal($.templates("{{if !!true}}yes{{else}}no{{/if}}").render({}), "yes", "{{if !!true}}...");
assert.equal($.templates("{{:!(true)}}").render({}), "false", "!(true)");
assert.equal($.templates("{{:!true === false}}").render({}), "true", "!true === false");
assert.equal($.templates("{{:false === !true}}").render({}), "true", "false === !true");
assert.equal($.templates("{{:false === !null}}").render({}), "false", "false === !null");
assert.equal($.templates("{{:\"'\" + 1 + '\"' + 2 + '\\' + 3}}").render({}), "'1\"2\\3", "'1\"2\\3");
});
QUnit.module("{{for}}");
QUnit.test("{{for}}", function(assert) {
$.templates({
forTmpl: "header_{{for people}}{{:name}}{{/for}}_footer",
templateForArray: "header_{{for #data}}{{:name}}{{/for}}_footer",
pageTmpl: '{{for [people] tmpl="templateForArray"/}}',
simpleFor: "a{{for people}}Content{{:#data}}|{{/for}}b",
forPrimitiveDataTypes: "a{{for people}}|{{:#data}}{{/for}}b",
testTmpl: "xxx{{:name}} {{:~foo}}"
});
assert.equal($.render.forTmpl({people: people}), "header_JoBill_footer", '{{for people}}...{{/for}}');
assert.equal($.render.templateForArray([people]), "header_JoBill_footer", 'Can render a template against an array, as a "layout template", by wrapping array in an array');
assert.equal($.render.pageTmpl({people: people}), "header_JoBill_footer", '{{for [people] tmpl="templateForArray"/}}');
assert.equal($.templates("{{for}}xxx{{:name}} {{:~foo}}{{/for}}").render({name: "Jeff"}, {foo:"fooVal"}), "xxxJeff fooVal", "no parameter - renders once with parent #data context: {{for}}");
assert.equal($.templates("{{for tmpl='testTmpl'/}}").render({name: "Jeff"}, {foo:"fooVal"}), "xxxJeff fooVal", ": {{for tmpl=.../}} no parameter - equivalent to {{include tmpl=.../}} - renders once with parent #data context");
assert.equal($.templates("{{include tmpl='testTmpl'/}}").render({name: "Jeff"}, {foo:"fooVal"}), "xxxJeff fooVal", "{{include tmpl=.../}} with tmpl parameter - renders once with parent #data context. Equivalent to {{for tmpl=.../}}");
assert.equal($.templates("{{for missingProperty}}xxx{{:#data===~undefined}}{{/for}}").render({}), "", "missingProperty - renders empty string");
assert.equal($.templates("{{for null}}xxx{{:#data===null}}{{/for}}").render(), "xxxtrue", "null - renders once with #data null: {{for null}}");
assert.equal($.templates("{{for false}}xxx{{:#data}}{{/for}}").render(), "xxxfalse", "false - renders once with #data false: {{for false}}");
assert.equal($.templates("{{for 0}}xxx{{:#data}}{{/for}}").render(), "xxx0", "0 - renders once with #data false: {{for 0}}");
assert.equal($.templates("{{for ''}}xxx{{:#data===''}}{{/for}}").render(), "xxxtrue", "'' - renders once with #data false: {{for ''}}");
assert.equal($.templates("{{for #data}}{{:name}}{{/for}}").render(people), "JoBill", "If #data is an array, {{for #data}} iterates");
assert.equal($.render.simpleFor({people:[]}), "ab", 'Empty array renders empty string');
assert.equal($.render.simpleFor({people:["", false, null, undefined, 1]}), "aContent|Contentfalse|Content|Content|Content1|b", 'Empty string, false, null or undefined members of array are also rendered');
assert.equal($.render.simpleFor({people:null}), "aContent|b", 'null is rendered once with #data null');
assert.equal($.render.simpleFor({}), "ab", 'if #data is undefined, renders empty string');
assert.equal($.render.forPrimitiveDataTypes({people:[0, 1, "abc", "", ,null ,true ,false]}), "a|0|1|abc||||true|falseb", 'Primitive types render correctly, even if falsey');
});
QUnit.test("{{for start end sort filter reverse}}", function(assert) {
// =============================== Arrange ===============================
function level(aField, bField) {
return aField > bField ? 1 : aField < bField ? -1 : 0;
}
var oddValue = function(item, index, items) { return item%2; };
var oddIndex = function(item, index, items) { return index%2; };
var sortAgeName = function(a, b) {
return level(a.details.role.toLowerCase(), b.details.role.toLowerCase()) // First level sort: by role
|| (this.props.reverseAge ? level(b.details.age, a.details.age) : level(a.details.age, b.details.age)) // 2nd level sort: sort by age, or reverse sort by age
|| level(a.name.toLowerCase(), b.name.toLowerCase()); // 3rd level sort: sort by name
};
var underLimit = function(item, index, items) {
return item.details.age < this.props.limit;
};
// ................................ Assert ..................................
assert.equal($.templates("{{for start=0 end=10}}{{:}} {{/for}}").render(), "0 1 2 3 4 5 6 7 8 9 ", "{{for start=0 end=10}}: Auto-create array");
assert.equal($.templates("{{for start=5 end=9 reverse=1}}{{:}} {{/for}}").render(), "8 7 6 5 ", "{{for start=5 end=9 reverse=1}}: Auto-create array");
assert.equal($.templates("{{for start=8 end=4 step=-1}}{{:}} {{/for}}").render(), "8 7 6 5 ", "{{for start=8 end=4 step=-1}}: Auto-create array");
assert.equal($.templates("{{for start=8 end=4 step=-1 reverse=true}}{{:}} {{/for}}").render(), "5 6 7 8 ", "{{for start=8 end=4 step=-1 reverse=true}}: Auto-create array, with reverse");
assert.equal($.templates("{{for start=20 end='10' step=-2}}{{:}} {{/for}}").render(), "20 18 16 14 12 ", "{{for start=20 end='10' step=-2}}: Auto-create array");
assert.equal($.templates("{{for start=20 end='10' step=2}}{{:}} {{/for}}").render(), "", "{{for start=20 end='10' step=2}}: Auto-create array (outputs nothing)");
assert.equal($.templates("{{for start=2 end=-1.5 step=-.5}}{{:}} {{/for}}").render(), "2 1.5 1 0.5 0 -0.5 -1 ", "{{for start=0 end='10' step=-1}}: Auto-create array");
assert.equal($.templates("{{for start=2}}{{:}} {{/for}}").render(), "", "{{for start=2}}: (outputs nothing)");
assert.equal($.templates("{{for end=4}}{{:}} {{/for}}").render(), "0 1 2 3 ", "{{for end=4}}: (start defaults to 0)");
assert.equal($.templates("{{for start=8 end=4 step=-1 reverse=true sort=true filter=~oddIndex}}{{:}} {{/for}}").render({}, {oddIndex: oddIndex}), "5 6 7 8 ", "{{for start=8 end=4 step=-1 reverse=true sort=true}}: Auto-create array, sort and filter not supported with auto-create arrays - do nothing");
// =============================== Arrange ===============================
var myarray = [1, 9, 2, 8, 3, 7, 4, 6, 5];
assert.equal($.templates("{{for #data }}{{:}} {{/for}}").render(myarray, true), "1 9 2 8 3 7 4 6 5 ", "{{for #data}}");
assert.equal($.templates("{{for #data sort=true}}{{:}} {{/for}}").render(myarray, true), "1 2 3 4 5 6 7 8 9 ", "{{for #data sort=true}}");
assert.equal($.templates("{{for myarray reverse=true}}{{:}} {{/for}}").render({myarray: myarray}), "5 6 4 7 3 8 2 9 1 ", "{{for myarray reverse=true}}");
assert.equal($.templates("{{for myarray start=1 end=-1}}{{:}} {{/for}}").render({myarray: myarray}), "9 2 8 3 7 4 6 ", "{{for myarray start=1 end=-1}}");
assert.equal($.templates("{{for myarray start=1}}{{:}} {{/for}}").render({myarray: myarray}), "9 2 8 3 7 4 6 5 ", "{{for myarray start=1}}");
assert.equal($.templates("{{for myarray end=-1}}{{:}} {{/for}}").render({myarray: myarray}), "1 9 2 8 3 7 4 6 ", "{{for myarray end=-1}}");
assert.equal($.templates("{{for myarray sort=true}}{{:}} {{/for}}").render({myarray: myarray}), "1 2 3 4 5 6 7 8 9 ", "{{for myarray sort=true}}");
assert.equal($.templates("{{for myarray sort=true reverse=true}}{{:}} {{/for}}").render({myarray: myarray}), "9 8 7 6 5 4 3 2 1 ", "{{for myarray sort=true reverse=true}}");
assert.equal($.templates("{{for myarray filter=~oddValue}}{{:}} {{/for}}").render({myarray: myarray}, {oddValue: oddValue}), "1 9 3 7 5 ", "{{for myarray filter=~oddValue}}");
assert.equal($.templates("{{for myarray filter=~oddIndex}}{{:}} {{/for}}").render({myarray: myarray}, {oddIndex: oddIndex}), "9 8 7 6 ", "{{for myarray filter=~oddIndex}}");
assert.equal($.templates("{{for myarray sort=true filter=~oddValue}}{{:}} {{/for}}").render({myarray: myarray}, {oddValue: oddValue}), "1 3 5 7 9 ", "{{for myarray sort=true filter=~oddValue}}");
assert.equal($.templates("{{for myarray sort=true filter=~oddIndex}}{{:}} {{/for}}").render({myarray: myarray}, {oddIndex: oddIndex}), "2 4 6 8 ", "{{for myarray sort=true filter=~oddIndex}}");
assert.equal($.templates("{{for myarray sort=true filter=~oddIndex start=1 end=3}}{{:}} {{/for}}").render({myarray: myarray}, {oddIndex: oddIndex}), "4 6 ", "{{for myarray sort=true filter=~oddIndex start=1 end=3}}");
assert.equal($.templates("{{for myarray sort=true filter=~oddIndex start=-3 end=-1}}{{:}} {{/for}}").render({myarray: myarray}, {oddIndex: oddIndex}), "4 6 ", "{{for myarray sort=true filter=~oddIndex start=-3 end=-1}} Negative start or end count from the end");
assert.equal($.templates("{{for myarray sort=true filter=~oddIndex start=3 end=3}}{{:}} {{/for}}").render({myarray: myarray}, {oddIndex: oddIndex}), "", "{{for myarray sort=true filter=~oddIndex start=3 end=3}} (outputs nothing)");
assert.equal($.templates("{{for myarray step=2 start=1}}{{:}} {{/for}}").render({myarray: myarray}, {oddIndex: oddIndex}), "9 8 7 6 ", "{{for myarray step=2 start=1}}");
assert.equal($.templates("{{for myarray sort=true step=2 start=1}}{{:}} {{/for}}").render({myarray: myarray}, {oddIndex: oddIndex}), "2 4 6 8 ", "{{for myarray sort=true step=2 start=1}}");
assert.equal($.templates("{{for myarray sort=true step=2 start=3 end=6}}{{:}} {{/for}}").render({myarray: myarray}, {oddIndex: oddIndex}), "4 6 ", "{{for myarray sort=true step=2 start=3 end=6}}");
assert.equal($.templates("{{for myarray sort=true step=2 start=-6 end=-3}}{{:}} {{/for}}").render({myarray: myarray}, {oddIndex: oddIndex}), "4 6 ", "{{for myarray sort=true step=2 start=-6 end=-3}} Negative start or end count from the end");
assert.equal($.templates("{{for myarray sort=true step=2 start=3 end=3}}{{:}} {{/for}}").render({myarray: myarray}, {oddIndex: oddIndex}), "", "{{for myarray sort=true step=2 start=3 end=3}} (outputs nothing)");
assert.equal($.templates("{{for myarray step=3.5}}{{:}} {{/for}}").render({myarray: myarray}, {oddIndex: oddIndex}), "1 8 4 ", "{{for myarray step=3.5}} - equivalent to step=3");
assert.equal($.templates("{{for myarray step=-2}}{{:}} {{/for}}").render({myarray: myarray}, {oddIndex: oddIndex}), "1 9 2 8 3 7 4 6 5 ", "{{for myarray step=-2}} equivalent to no step");
assert.equal($.templates("{{for myarray step=1}}{{:}} {{/for}}").render({myarray: myarray}, {oddIndex: oddIndex}), "1 9 2 8 3 7 4 6 5 ", "{{for myarray step=1}} equivalent to no step");
// =============================== Arrange ===============================
var mypeople = [
{name: "Jo", details: {age: 22}},
{name: "Bob", details: {age: 2}},
{name: "Emma", details: {age: 12}},
{name: "Jeff", details: {age: 13.5}},
{name: "Julia", details: {age: 0.6}},
{name: "Xavier", details: {age: 0}}
];
// ................................ Assert ..................................
assert.equal($.templates("{{for mypeople sort='name'}}{{:name}}: age {{:details.age}} - {{/for}}").render({mypeople: mypeople}), "Bob: age 2 - Emma: age 12 - Jeff: age 13.5 - Jo: age 22 - Julia: age 0.6 - Xavier: age 0 - ",
"{{for mypeople sort='name'}}");
assert.equal($.templates("{{for mypeople sort='details.age'}}{{:name}}: age {{:details.age}} - {{/for}}").render({mypeople: mypeople}), "Xavier: age 0 - Julia: age 0.6 - Bob: age 2 - Emma: age 12 - Jeff: age 13.5 - Jo: age 22 - ",
"{{for mypeople sort='details.age'}}");
assert.equal($.templates("{{for mypeople sort='details.age' reverse=true filter=~underLimit limit=20}}{{:name}}: age {{:details.age}} - {{/for}}").render({mypeople: mypeople}, {underLimit: underLimit}), "Jeff: age 13.5 - Emma: age 12 - Bob: age 2 - Julia: age 0.6 - Xavier: age 0 - ",
"{{for mypeople sort='details.age' reverse=true filter=~underLimit...}}");
assert.equal($.templates("{{for mypeople sort='details.age' reverse=true filter=~underLimit limit=20 start=1 end=-1}}{{:name}}: age {{:details.age}} - {{/for}}").render({mypeople: mypeople}, {underLimit: underLimit}), "Emma: age 12 - Bob: age 2 - Julia: age 0.6 - ",
"{{for mypeople sort='details.age' reverse=true filter=~underLimit... start=1 end=-1}}");
assert.equal($.templates("{{for mypeople sort='details.age' reverse=true filter=~underLimit limit=20 start=1 end=-1}}{{:name}}: age {{:details.age}} - {{/for}}").render({mypeople: mypeople}, {underLimit: underLimit}), "Emma: age 12 - Bob: age 2 - Julia: age 0.6 - ",
"{{for mypeople sort='details.age' reverse=true filter=~underLimit... start=1 end=-1}}");
// =============================== Arrange ===============================
var mypeople2 = [
{name: "Bill", details: {age: 22, role: "Lead"}},
{name: "Anne", details: {age: 32, role: "Assistant"}},
{name: "Emma", details: {age: 19.1, role: "Team member"}},
{name: "Jeff", details: {age: 33.5, role: "Lead"}},
{name: "Xavier", details: {age: 32, role: "Team member"}},
{name: "Julia", details: {age: 18, role: "Assistant"}},
{name: "Bill", details: {age: 32, role: "Team member"}}
];
// ................................ Assert ..................................
assert.equal($.templates("{{for mypeople sort=~sortAgeName}}{{:name}}: ({{:details.role}}) age {{:details.age}} -{{/for}}").render({mypeople: mypeople2}, {sortAgeName: sortAgeName}),
"Julia: (Assistant) age 18 -Anne: (Assistant) age 32 -Bill: (Lead) age 22 -Jeff: (Lead) age 33.5 -Emma: (Team member) age 19.1 -Bill: (Team member) age 32 -Xavier: (Team member) age 32 -",
"{{for mypeople sort=~sortAgeName}}: custom sort function");
// ................................ Assert ..................................
assert.equal($.templates("{{for mypeople sort=~sortAgeName reverseAge=true}}{{:name}}: ({{:details.role}}) age {{:details.age}} -{{/for}}").render({mypeople: mypeople2}, {sortAgeName: sortAgeName}),
"Anne: (Assistant) age 32 -Julia: (Assistant) age 18 -Jeff: (Lead) age 33.5 -Bill: (Lead) age 22 -Bill: (Team member) age 32 -Xavier: (Team member) age 32 -Emma: (Team member) age 19.1 -",
"{{for mypeople sort=~sortAgeName}}: custom sort function - this pointer is tagCtx");
// ................................ Assert ..................................
assert.equal($.templates("{{for start=0 end=0}}{{else mypeople sort=~sortAgeName reverseAge=true}}{{:name}}: ({{:details.role}}) age {{:details.age}} -{{/for}}").render({mypeople: mypeople2}, {sortAgeName: sortAgeName}),
"Anne: (Assistant) age 32 -Julia: (Assistant) age 18 -Jeff: (Lead) age 33.5 -Bill: (Lead) age 22 -Bill: (Team member) age 32 -Xavier: (Team member) age 32 -Emma: (Team member) age 19.1 -",
"{{for start=0 end=0}}{{else mypeople sort=~sortAgeName}}: custom sort function - this pointer is tagCtx (else block)");
// =============================== Arrange ===============================
$.views.tags("for2", {
baseTag: "for"
});
// ................................ Assert ..................................
assert.equal($.templates("{{for2 mypeople sort='details.age' reverse=true filter=~underLimit limit=20 start=1 end=-1}}{{:name}}: age {{:details.age}} - {{/for2}}").render({mypeople: mypeople}, {underLimit: underLimit}), "Emma: age 12 - Bob: age 2 - Julia: age 0.6 - ",
"{{for2 mypeople sort='details.age' reverse=true filter=~underLimit... start=1 end=-1}} Derived tag");
});
QUnit.module("{{props}}");
QUnit.test("{{props}}", function(assert) {
$.templates({
propsTmpl: "header_{{props person}}Key: {{:key}} - Prop: {{:prop}}| {{/props}}_footer",
propsTmplObjectArray: "header_{{props people}}Key: {{:key}} - Prop: {{for prop}}{{:name}} {{/for}}{{/props}}_footer",
propsTmplPrimitivesArray: "header_{{props people}}Key: {{:key}} - Prop: {{for prop}}{{:name}} {{/for}}{{/props}}_footer",
templatePropsArray: "header_{{props #data}}Key: {{:key}} - Prop: {{for prop}}{{:name}} {{/for}}{{/props}}_footer",
propTmpl: "Key: {{:key}} - Prop: {{:prop}}",
pageTmpl: '{{props person tmpl="propTmpl"/}}',
simpleProps: "a{{props people}}Content{{:#data}}|{{/props}}b",
propsPrimitiveDataTypes: "a{{props people}}|{{:#data}}{{/props}}b",
testTmpl: "xxx{{:name}} {{:~foo}}"
});
assert.equal($.render.propsTmpl({person: people[0]}), "header_Key: name - Prop: Jo| _footer", '{{props person}}...{{/props}} for an object iterates over properties');
assert.equal($.render.propsTmplObjectArray({people: people}), "header_Key: 0 - Prop: Jo Key: 1 - Prop: Bill _footer", '{{props people}}...{{/props}} for an array iterates over the array - with index as key and object a prop');
assert.equal($.render.templatePropsArray([people]), "header_Key: 0 - Prop: Jo Key: 1 - Prop: Bill _footer", 'Can render a template against an array, as a "layout template", by wrapping array in an array');
assert.equal($.render.pageTmpl({person: people[0]}), "Key: name - Prop: Jo", '{{props person tmpl="propTmpl"/}}');
assert.equal($.templates("{{props}}{{:key}} {{:prop}}{{/props}}").render({name: "Jeff"}), "name Jeff", "no parameter - defaults to current data item");
assert.equal($.templates("{{props foo}}xxx{{:key}} {{:prop}} {{:~foo}}{{/props}}").render({name: "Jeff"}), "", "undefined arg - renders nothing");
assert.equal($.templates("{{props tmpl='propTmpl'/}}").render({name: "Jeff"}), "Key: name - Prop: Jeff", ": {{props tmpl=.../}} no parameter - defaults to current data item");
assert.equal($.templates("{{props null}}Key: {{:key}} - Prop: {{:prop}}| {{/props}}").render(), "", "null - renders nothing");
assert.equal($.templates("{{props false}}Key: {{:key}} - Prop: {{:prop}}| {{/props}}").render(), "", "false - renders nothing");
assert.equal($.templates("{{props 0}}Key: {{:key}} - Prop: {{:prop}}| {{/props}}").render(), "", "0 - renders nothing");
assert.equal($.templates("{{props 'abc'}}Key: {{:key}} - Prop: {{:prop}}| {{/props}}").render(), "", "'abc' - renders nothing");
assert.equal($.templates("{{props ''}}Key: {{:key}} - Prop: {{:prop}}| {{/props}}").render(), "", "'' - renders nothing");
assert.equal($.templates("{{props #data}}Key: {{:key}} - Prop: {{:prop}}| {{/props}}").render(people),
"Key: name - Prop: Jo| Key: name - Prop: Bill| ",
"If #data is an array, {{props #data}} iterates");
assert.equal($.render.propsTmpl({person:{}}), "header__footer", 'Empty object renders empty string');
assert.equal($.render.propsTmpl({person:{zero: 0, one: 1, str: "abc", emptyStr: "", nullVal: null , trueVal: true , falseVal: false}}),
"header_Key: zero - Prop: 0| Key: one - Prop: 1| Key: str - Prop: abc| Key: emptyStr - Prop: | Key: nullVal - Prop: | Key: trueVal - Prop: true| Key: falseVal - Prop: false| _footer",
'Primitive types render correctly, even if falsey');
});
QUnit.test("{{props start end sort filter reverse}}", function(assert) {
// =============================== Arrange ===============================
function level(aField, bField) {
return aField > bField ? 1 : aField < bField ? -1 : 0;
}
var oddValue = function(item, index, items) { return item.prop%2; };
var oddIndex = function(item, index, items) { return index%2; };
var sortAgeName = function(a, b) {
return level(a.prop.details.role.toLowerCase(), b.prop.details.role.toLowerCase()) // First level sort: by role
|| (this.props.reverseAge ? level(b.prop.details.age, a.prop.details.age) : level(a.prop.details.age, b.prop.details.age)) // 2nd level sort: sort by age, or reverse sort by age
|| level(a.prop.name.toLowerCase(), b.prop.name.toLowerCase()); // 3rd level sort: sort by name
};
var underLimit = function(item, index, items) {
return item.prop.details.age < this.props.limit;
};
var myobject = {a: 1, b: 9, c: 2, d:8, A:3, B:7, C:4, D:6, e:5};
assert.equal($.templates("{{props myobject}}{{:key}} {{:prop}} - {{/props}}").render({myobject: myobject}), "a 1 - b 9 - c 2 - d 8 - A 3 - B 7 - C 4 - D 6 - e 5 - ", "{{props myobject}} (original order)");
assert.equal($.templates("{{props #data sort='prop'}}{{:key}} {{:prop}} - {{/props}}").render(myobject, true), "a 1 - c 2 - A 3 - C 4 - e 5 - D 6 - B 7 - d 8 - b 9 - ", "{{props #data sort='prop'}}");
assert.equal($.templates("{{props #data sort='key'}}{{:key}} {{:prop}} - {{/props}}").render(myobject, true), "a 1 - A 3 - b 9 - B 7 - c 2 - C 4 - d 8 - D 6 - e 5 - ", "{{props #data sort='key'}}");
assert.equal($.templates("{{props #data sort='prop' reverse=true}}{{:key}} {{:prop}} - {{/props}}").render(myobject, true), "b 9 - d 8 - B 7 - D 6 - e 5 - C 4 - A 3 - c 2 - a 1 - ", "{{props #data sort='prop' reverse=true}}");
assert.equal($.templates("{{props #data sort='key' reverse=true}}{{:key}} {{:prop}} - {{/props}}").render(myobject, true), "e 5 - d 8 - D 6 - c 2 - C 4 - b 9 - B 7 - a 1 - A 3 - ", "{{props #data sort='key' reverse=true}}");
assert.equal($.templates("{{props myobject reverse=true}}{{:key}} {{:prop}} - {{/props}}").render({myobject: myobject}), "e 5 - D 6 - C 4 - B 7 - A 3 - d 8 - c 2 - b 9 - a 1 - ", "{{props myobject reverse=true}}");
assert.equal($.templates("{{props myobject sort='key' reverse=true}}{{:key}} {{:prop}} - {{/props}}").render({myobject: myobject}), "e 5 - d 8 - D 6 - c 2 - C 4 - b 9 - B 7 - a 1 - A 3 - ", "{{props myobject sort='key' reverse=true}}");
assert.equal($.templates("{{props myobject start=1 end=-1}}{{:key}} {{:prop}} - {{/props}}").render({myobject: myobject}, {oddIndex: oddIndex}), "b 9 - c 2 - d 8 - A 3 - B 7 - C 4 - D 6 - ", "{{props myobject start=1 end=-1}}");
assert.equal($.templates("{{props myobject start=1}}{{:key}} {{:prop}} - {{/props}}").render({myobject: myobject}, {oddIndex: oddIndex}), "b 9 - c 2 - d 8 - A 3 - B 7 - C 4 - D 6 - e 5 - ", "{{props myobject start=1}}");
assert.equal($.templates("{{props myobject end=-1}}{{:key}} {{:prop}} - {{/props}}").render({myobject: myobject}, {oddIndex: oddIndex}), "a 1 - b 9 - c 2 - d 8 - A 3 - B 7 - C 4 - D 6 - ", "{{props myobject end=-1}}");
assert.equal($.templates("{{props myobject filter=~oddValue}}{{:key}} {{:prop}} - {{/props}}").render({myobject: myobject}, {oddValue: oddValue}), "a 1 - b 9 - A 3 - B 7 - e 5 - ", "{{props myobject filter=~oddValue}}");
assert.equal($.templates("{{props myobject filter=~oddIndex}}{{:key}} {{:prop}} - {{/props}}").render({myobject: myobject}, {oddIndex: oddIndex}), "b 9 - d 8 - B 7 - D 6 - ", "{{props myobject filter=~oddIndex}}");
assert.equal($.templates("{{props myobject sort='prop' filter=~oddValue}}{{:key}} {{:prop}} - {{/props}}").render({myobject: myobject}, {oddValue: oddValue}), "a 1 - A 3 - e 5 - B 7 - b 9 - ", "{{props myobject sort='prop' filter=~oddValue}}");
assert.equal($.templates("{{props myobject sort='prop' filter=~oddIndex}}{{:key}} {{:prop}} - {{/props}}").render({myobject: myobject}, {oddIndex: oddIndex}), "c 2 - C 4 - D 6 - d 8 - ", "{{props myobject sort='prop' filter=~oddIndex}}");
assert.equal($.templates("{{props myobject sort='prop' filter=~oddIndex start=1 end=3}}{{:key}} {{:prop}} - {{/props}}").render({myobject: myobject}, {oddIndex: oddIndex}), "C 4 - D 6 - ", "{{props myobject sort='prop' filter=~oddIndex start=1 end=3}}");
assert.equal($.templates("{{props myobject sort='prop' filter=~oddIndex start=-3 end=-1}}{{:key}} {{:prop}} - {{/props}}").render({myobject: myobject}, {oddIndex: oddIndex}), "C 4 - D 6 - ", "{{props myobject sort='prop' filter=~oddIndex start=-3 end=-1}} Negative start or end count from the end");
assert.equal($.templates("{{props myobject sort='prop' filter=~oddIndex start=3 end=3}}{{:key}} {{:prop}} - {{/props}}").render({myobject: myobject}, {oddIndex: oddIndex}), "", "{{props myobject sort='key' filter=~oddIndex start=3 end=3}} (outputs nothing)");
assert.equal($.templates("{{props myobject step=2 start=1}}{{:key}} {{:prop}} - {{/props}}").render({myobject: myobject}, {oddIndex: oddIndex}), "b 9 - d 8 - B 7 - D 6 - ", "{{props myobject step=2 start=1}}");
assert.equal($.templates("{{props myobject sort='prop' step=2 start=1}}{{:key}} {{:prop}} - {{/props}}").render({myobject: myobject}, {oddIndex: oddIndex}), "c 2 - C 4 - D 6 - d 8 - ", "{{props myobject sort='prop' step=2 start=1}}");
assert.equal($.templates("{{props myobject sort='prop' step=2 start=3 end=6}}{{:key}} {{:prop}} - {{/props}}").render({myobject: myobject}, {oddIndex: oddIndex}), "C 4 - D 6 - ", "{{props myobject sort='prop' step=2 start=3 end=6}}");
assert.equal($.templates("{{props myobject sort='prop' step=2 start=-6 end=-3}}{{:key}} {{:prop}} - {{/props}}").render({myobject: myobject}, {oddIndex: oddIndex}), "C 4 - D 6 - ", "{{props myobject sort='prop' step=2 start=-6 end=-3}} Negative start or end count from the end");
assert.equal($.templates("{{props myobject sort='prop' step=2 start=3 end=3}}{{:key}} {{:prop}} - {{/props}}").render({myobject: myobject}, {oddIndex: oddIndex}), "", "{{props myobject sort='key' step=2 start=3 end=3}} (outputs nothing)");
// =============================== Arrange ===============================
var mypeople = {
p1: {name: "Jo", details: {age: 22}},
p2: {name: "Bob", details: {age: 2}},
p3: {name: "Emma", details: {age: 12}},
p7: {name: "Jeff", details: {age: 13.5}},
p6: {name: "Julia", details: {age: 0.6}},
p5: {name: "Xavier", details: {age: 0}}
};
// ................................ Assert ..................................
assert.equal($.templates("{{props mypeople sort='prop.name'}}{{:prop.name}}: age {{:prop.details.age}} - {{/props}}").render({mypeople: mypeople}), "Bob: age 2 - Emma: age 12 - Jeff: age 13.5 - Jo: age 22 - Julia: age 0.6 - Xavier: age 0 - ", "{{props mypeople sort='name'}}");
assert.equal($.templates("{{props mypeople sort='prop.details.age'}}{{:prop.name}}: age {{:prop.details.age}} - {{/props}}").render({mypeople: mypeople}), "Xavier: age 0 - Julia: age 0.6 - Bob: age 2 - Emma: age 12 - Jeff: age 13.5 - Jo: age 22 - ", "{{props mypeople sort='details.age'}}");
assert.equal($.templates("{{props mypeople sort='prop.details.age' reverse=true filter=~underLimit limit=20}}{{:prop.name}}: age {{:prop.details.age}} - {{/props}}").render({mypeople: mypeople}, {underLimit: underLimit}), "Jeff: age 13.5 - Emma: age 12 - Bob: age 2 - Julia: age 0.6 - Xavier: age 0 - ", "{{props mypeople sort='details.age' reverse=true filter=~underLimit...}}");
assert.equal($.templates("{{props mypeople sort='prop.details.age' reverse=true filter=~underLimit limit=20 start=1 end=-1}}{{:prop.name}}: age {{:prop.details.age}} - {{/props}}").render({mypeople: mypeople}, {underLimit: underLimit}), "Emma: age 12 - Bob: age 2 - Julia: age 0.6 - ", "{{props mypeople sort='details.age' reverse=true filter=~underLimit... start=1 end=-1}}");
assert.equal($.templates("{{props mypeople sort='prop.details.age' reverse=true filter=~underLimit limit=20 start=1 end=-1}}{{:prop.name}}: age {{:prop.details.age}} - {{/props}}").render({mypeople: mypeople}, {underLimit: underLimit}), "Emma: age 12 - Bob: age 2 - Julia: age 0.6 - ", "{{props mypeople sort='details.age' reverse=true filter=~underLimit... start=1 end=-1}}");
// =============================== Arrange ===============================
var mypeople2 = {
p1: {name: "Bill", details: {age: 22, role: "Lead"}},
p2: {name: "Anne", details: {age: 32, role: "Assistant"}},
p3: {name: "Emma", details: {age: 19.1, role: "Team member"}},
p7: {name: "Jeff", details: {age: 33.5, role: "Lead"}},
p6: {name: "Xavier", details: {age: 32, role: "Team member"}},
p5: {name: "Julia", details: {age: 18, role: "Assistant"}},
p4: {name: "Bill", details: {age: 32, role: "Team member"}}
};
// ................................ Assert ..................................
assert.equal($.templates("{{props mypeople sort=~sortAgeName}}{{:prop.name}}: ({{:prop.details.role}}) age {{:prop.details.age}} - {{/props}}").render({mypeople: mypeople2}, {sortAgeName: sortAgeName}),
"Julia: (Assistant) age 18 - Anne: (Assistant) age 32 - Bill: (Lead) age 22 - Jeff: (Lead) age 33.5 - Emma: (Team member) age 19.1 - Bill: (Team member) age 32 - Xavier: (Team member) age 32 - ",
"{{props mypeople sort=~sortAgeName}}: custom sort function");
// ................................ Assert ..................................
assert.equal($.templates("{{props mypeople sort=~sortAgeName reverseAge=true}}{{:prop.name}}: ({{:prop.details.role}}) age {{:prop.details.age}} - {{/props}}").render({mypeople: mypeople2}, {sortAgeName: sortAgeName}),
"Anne: (Assistant) age 32 - Julia: (Assistant) age 18 - Jeff: (Lead) age 33.5 - Bill: (Lead) age 22 - Bill: (Team member) age 32 - Xavier: (Team member) age 32 - Emma: (Team member) age 19.1 - ",
"{{props mypeople sort=~sortAgeName}}: custom sort function - this pointer is tagCtx");
// ................................ Assert ..................................
assert.equal($.templates("{{props ''}}{{else mypeople sort=~sortAgeName reverseAge=true}}{{:prop.name}}: ({{:prop.details.role}}) age {{:prop.details.age}} - {{/props}}").render({mypeople: mypeople2}, {sortAgeName: sortAgeName}),
"Anne: (Assistant) age 32 - Julia: (Assistant) age 18 - Jeff: (Lead) age 33.5 - Bill: (Lead) age 22 - Bill: (Team member) age 32 - Xavier: (Team member) age 32 - Emma: (Team member) age 19.1 - ",
"{{props ''}}{{else mypeople sort=~sortAgeName}}: custom sort function - this pointer is tagCtx (else block)");
// =============================== Arrange ===============================
$.views.tags("props2", {
baseTag: "props"
});
// ................................ Assert ..................................
assert.equal($.templates("{{props2 mypeople sort='prop.details.age' reverse=true filter=~underLimit limit=20 start=1 end=-1}}{{:prop.name}}: age {{:prop.details.age}} - {{/props2}}").render({mypeople: mypeople}, {underLimit: underLimit}), "Emma: age 12 - Bob: age 2 - Julia: age 0.6 - ", "{{for2 mypeople sort='details.age' reverse=true filter=~underLimit... start=1 end=-1}} Derived tag");
});
QUnit.module("{{!-- --}}");
QUnit.test("{{!-- --}}", function(assert) {
// =============================== Arrange ===============================
var result,
tmpl = $.templates("a {{:'--1'}}\n {{for '--2} }'}} {{:}} {{/for}} \n b"),
tmplWrappedInComment = $.templates("a {{!-- {{:'--1'}}\n {{for '--2} }'}} {{:}} {{/for}} \n--}} b");
// ................................ Assert ..................................
result = tmpl.render() + "|" + tmplWrappedInComment.render();
assert.equal(result, "a --1\n --2} } \n b|a b",
"{{!-- --}} comments out blocks including newlines and --");
});
QUnit.module("allowCode");
QUnit.test("{{*}}", function(assert) {
// =============================== Arrange ===============================
$.views.settings.allowCode(false);
global.glob = {a: "AA"};
var tmpl = $.templates("_{{*:glob.a}}_");
// ................................ Assert ..................................
assert.equal(tmpl.render(), "__",
"{{*:expression}} returns nothing if allowCode not set to true");
// =============================== Arrange ===============================
$.views.settings.allowCode(true);
var result = "" + !!tmpl.allowCode + " " + tmpl.render(); // Still returns "__" until we recompile
tmpl.allowCode = true;
result += "|" + !!tmpl.allowCode + " " + tmpl.render(); // Still returns "__" until we recompile
// ................................ Assert ..................................
assert.equal(result, "false __|true __",
"If $.settings.allowCode() or tmpl.allowCode are set to true, previously compiled template is unchanged, so {{*}} still inactive");
// ................................ Act ..................................
tmpl = $.templates("_{{*:glob.a}}_");
result = "" + !!tmpl.allowCode + " " + tmpl.render(); // Now {{*}} is active
// ................................ Assert ..................................
assert.equal(result, "true _AA_",
"If $.settings.allowCode() set to true, {{*: expression}} returns evaluated expression, with access to globals");
// =============================== Arrange ===============================
$.views.settings.allowCode(false);
tmpl = $.templates({
markup: "_{{*:glob.a}}_",
allowCode: true
});
// ................................ Assert ..................................
assert.equal(tmpl.render(), "_AA_",
"If template allowCode property set to true, {{*: expression}} returns evaluated expression, with access to globals");
// ................................ Act ..................................
tmpl = $.templates({
markup: "_{{*:glob.a}}_"
});
result = "" + !!tmpl.allowCode + ":" + tmpl();
tmpl = $.templates({markup: tmpl, allowCode: true});
result += "|" + tmpl.allowCode + ":" + tmpl();
// ................................ Assert ..................................
assert.equal(result, "false:__|true:_AA_",
"Can recompile tmpl to allow code, using tmpl = $.templates({markup: tmpl, allowCode: true})");
// ................................ Act ..................................
$.templates("myTmpl", {
markup: "_{{*:glob.a}}_"
});
tmpl = $.templates.myTmpl;
result = "" + !!tmpl.allowCode + ":" + tmpl();
$.templates("myTmpl", {markup: $.templates.myTmpl, allowCode: true});
tmpl = $.templates.myTmpl;
result += "|" + tmpl.allowCode + ":" + tmpl();
// ................................ Assert ..................................
assert.equal(result, "false:__|true:_AA_",
'Can recompile named tmpl to allow code, using $.templates("myTemplateName", {markup: $.templates.myTmpl, allowCode:true})"');
// =============================== Arrange ===============================
$.views.settings.allowCode(true);
// ................................ Act ..................................
global.myVar = 0;
tmpl = $.templates(
"{{* myvar=2; myvar+=4; }}"
+ "Initial value: {{*:myvar}} "
+ "{{* myvar+=11; }}"
+ "New value: {{*:myvar}}");
// ................................ Assert ..................................
assert.equal(tmpl.render(), "Initial value: 6 New value: 17",
"{{* expression}} or {{*: expression}} can access globals as window.myVar or myVar");
// ................................ Act ..................................
global.people = people;
tmpl = $.templates("{{:start}}"
+ "{{* for (var i=0, l=people.length; i<l; i++) { }}"
+ " {{:title}} = {{*: people[i].name + ' ' + data.sep + ' '}}!"
+ "{{* } }}"
+ "{{:end}}");
// ................................ Assert ..................................
assert.equal(tmpl.render({title: "name", start: "Start", end: "End", sep: "..."}), "Start name = Jo ... ! name = Bill ... !End",
"If allowCode set to true, on recompiling the template, {{*:expression}} returns evaluated expression, with access to globals");
// ................................ Act ..................................
global.myFunction = function() {
return "myGlobalfunction ";
};
document.title = "myTitle";
tmpl = $.templates("{{for people}}"
+ "{{*: ' ' + glob.a}} {{*: data.name}} {{*: view.index}} {{*: view.ctx.myHelper}} {{*: myFunction() + document.title}}"
+ "{{/for}}");
// ................................ Assert ..................................
assert.equal(tmpl.render({people: people}, {myHelper: "hi"}), " AA Jo 0 hi myGlobalfunction myTitle AA Bill 1 hi myGlobalfunction myTitle",
"{{* expression}} or {{*: expression}} can access globals, the data, the view, the view context, global functions etc.");
document.title = "";
$.views.settings.allowCode(false);
});
QUnit.module("useViews");
QUnit.test("", function(assert) {
// =============================== Arrange ===============================
$.views.settings.allowCode(true);
$.views.tags("exclaim", "!!! ");
var message = "",
tmpl = $.templates(
"{{for towns}}"
+ "{{>name}}"
+ "{{*:view.index===view.parent.data.length-2 ? ' and ' : view.index<view.parent.data.length-2 ? ', ': ''}}"
+ "{{/for}}");
// ................................ Act ..................................
try {
tmpl.render({towns: towns});
} catch(e) {
message = e.message;
}
// ................................ Assert ..................................
assert.ok(!tmpl.useViews && message.indexOf("undefined") > 0,
"A simple template with useViews=false will not provide access to the views through allowCode");
// ................................ Act ..................................
message = "";
tmpl.useViews = true;
// ................................ Assert ..................................
assert.equal(tmpl.render({towns: towns}), "Seattle, Paris and Delhi",
"If tmpl.useViews set to true (for an existing template - without recompiling), the template renders with view hierarchy");
// ................................ Act ..................................
tmpl.useViews = false;
$.views.settings.advanced({useViews: true});
// ................................ Assert ..................................
assert.equal(tmpl.render({towns: towns}), "Seattle, Paris and Delhi",
"If tmpl.useViews is set to false, but $.views.settings.advanced({useViews: ...}) is set to true, the template renders with view hierarchy, (without recompiling).");
// ................................ Act ..................................
$.views.settings.advanced({useViews: false});
tmpl = $.templates({markup: tmpl,
useViews: true
});
// ................................ Assert ..................................
tmpl = $.templates(
"{{:#type}} "
+ "{{for towns}}"
+ "{{>name}}"
+ "{{*:view.index===view.parent.data.length-2 ? ' and ' : view.index<view.parent.data.length-2 ? ', ': ''}}"
+ "{{/for}}");
var html = tmpl.render({towns: towns});
assert.equal(tmpl.useViews && html, "data Seattle, Paris and Delhi",
"Recompiling the template with useViews: true will create a template that has tmpl.useViews = true, which renders with a 'data' view");
// ................................ Act ..................................
tmpl.useViews = false;
html = tmpl.render({towns: towns});
// ................................ Assert ..................................
assert.equal(!tmpl.useViews && html, "top Seattle, Paris and Delhi",
"If tmpl.useViews set to false (for an existing template - without recompiling), the template renders without a 'data' view");
// ................................ Act ..................................
$.views.settings.advanced({useViews: true});
tmpl = $.templates({markup: tmpl});
$.views.settings.advanced({useViews: false});
// ................................ Assert ..................................
assert.equal(tmpl.useViews && tmpl.render({towns: towns}), "data Seattle, Paris and Delhi",
"If $.views.settings.advanced({useViews: ...}) was true when the template was compiled, then the template renders with views, even if $.views.settings.advanced({useViews: ...}) is no longer set to true");