handlebars 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/Gemfile +1 -2
- data/README.mdown +15 -7
- data/handlebars.gemspec +2 -5
- data/lib/handlebars.rb +5 -5
- data/lib/handlebars/context.rb +3 -12
- data/lib/handlebars/version.rb +1 -1
- data/spec/handlebars_spec.rb +1 -1
- metadata +47 -58
- data/.gitmodules +0 -3
- data/vendor/handlebars/.gitignore +0 -8
- data/vendor/handlebars/.jshintrc +0 -52
- data/vendor/handlebars/.npmignore +0 -10
- data/vendor/handlebars/.rspec +0 -1
- data/vendor/handlebars/Gemfile +0 -5
- data/vendor/handlebars/LICENSE +0 -19
- data/vendor/handlebars/README.markdown +0 -317
- data/vendor/handlebars/Rakefile +0 -109
- data/vendor/handlebars/bench/benchwarmer.js +0 -149
- data/vendor/handlebars/bench/handlebars.js +0 -172
- data/vendor/handlebars/bin/handlebars +0 -193
- data/vendor/handlebars/dist/handlebars.js +0 -2201
- data/vendor/handlebars/dist/handlebars.runtime.js +0 -321
- data/vendor/handlebars/lib/handlebars.js +0 -14
- data/vendor/handlebars/lib/handlebars/base.js +0 -154
- data/vendor/handlebars/lib/handlebars/compiler/ast.js +0 -133
- data/vendor/handlebars/lib/handlebars/compiler/base.js +0 -21
- data/vendor/handlebars/lib/handlebars/compiler/compiler.js +0 -1267
- data/vendor/handlebars/lib/handlebars/compiler/index.js +0 -7
- data/vendor/handlebars/lib/handlebars/compiler/printer.js +0 -131
- data/vendor/handlebars/lib/handlebars/compiler/visitor.js +0 -13
- data/vendor/handlebars/lib/handlebars/runtime.js +0 -88
- data/vendor/handlebars/lib/handlebars/utils.js +0 -67
- data/vendor/handlebars/package.json +0 -35
- data/vendor/handlebars/spec/acceptance_spec.rb +0 -101
- data/vendor/handlebars/spec/parser_spec.rb +0 -433
- data/vendor/handlebars/spec/qunit_spec.js +0 -1370
- data/vendor/handlebars/spec/spec_helper.rb +0 -157
- data/vendor/handlebars/spec/tokenizer_spec.rb +0 -301
- data/vendor/handlebars/src/handlebars.l +0 -53
- data/vendor/handlebars/src/handlebars.yy +0 -109
- data/vendor/handlebars/src/parser-prefix.js +0 -1
- data/vendor/handlebars/src/parser-suffix.js +0 -4
@@ -1,433 +0,0 @@
|
|
1
|
-
require "spec_helper"
|
2
|
-
|
3
|
-
describe "Parser" do
|
4
|
-
let(:handlebars) { @context["Handlebars"] }
|
5
|
-
|
6
|
-
before(:all) do
|
7
|
-
@compiles = true
|
8
|
-
end
|
9
|
-
|
10
|
-
def root(&block)
|
11
|
-
ASTBuilder.build do
|
12
|
-
instance_eval(&block)
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
def ast_for(string)
|
17
|
-
ast = handlebars.parse(string)
|
18
|
-
handlebars.print(ast)
|
19
|
-
end
|
20
|
-
|
21
|
-
class ASTBuilder
|
22
|
-
def self.build(&block)
|
23
|
-
ret = new
|
24
|
-
ret.evaluate(&block)
|
25
|
-
ret.out
|
26
|
-
end
|
27
|
-
|
28
|
-
attr_reader :out
|
29
|
-
|
30
|
-
def initialize
|
31
|
-
@padding = 0
|
32
|
-
@out = ""
|
33
|
-
end
|
34
|
-
|
35
|
-
def evaluate(&block)
|
36
|
-
instance_eval(&block)
|
37
|
-
end
|
38
|
-
|
39
|
-
def pad(string)
|
40
|
-
@out << (" " * @padding) + string + "\n"
|
41
|
-
end
|
42
|
-
|
43
|
-
def with_padding
|
44
|
-
@padding += 1
|
45
|
-
ret = yield
|
46
|
-
@padding -= 1
|
47
|
-
ret
|
48
|
-
end
|
49
|
-
|
50
|
-
def program
|
51
|
-
pad("PROGRAM:")
|
52
|
-
with_padding { yield }
|
53
|
-
end
|
54
|
-
|
55
|
-
def inverse
|
56
|
-
pad("{{^}}")
|
57
|
-
with_padding { yield }
|
58
|
-
end
|
59
|
-
|
60
|
-
def block
|
61
|
-
pad("BLOCK:")
|
62
|
-
with_padding { yield }
|
63
|
-
end
|
64
|
-
|
65
|
-
def inverted_block
|
66
|
-
pad("INVERSE:")
|
67
|
-
with_padding { yield }
|
68
|
-
end
|
69
|
-
|
70
|
-
def mustache(id, params = [], hash = nil)
|
71
|
-
hash = " #{hash}" if hash
|
72
|
-
pad("{{ #{id} [#{params.join(", ")}]#{hash} }}")
|
73
|
-
end
|
74
|
-
|
75
|
-
def partial(id, context = nil)
|
76
|
-
content = id.dup
|
77
|
-
content << " #{context}" if context
|
78
|
-
pad("{{> #{content} }}")
|
79
|
-
end
|
80
|
-
|
81
|
-
def comment(comment)
|
82
|
-
pad("{{! '#{comment}' }}")
|
83
|
-
end
|
84
|
-
|
85
|
-
def multiline_comment(comment)
|
86
|
-
pad("{{! '\n#{comment}\n' }}")
|
87
|
-
end
|
88
|
-
|
89
|
-
def content(string)
|
90
|
-
pad("CONTENT[ '#{string}' ]")
|
91
|
-
end
|
92
|
-
|
93
|
-
def string(string)
|
94
|
-
string.inspect
|
95
|
-
end
|
96
|
-
|
97
|
-
def integer(string)
|
98
|
-
"INTEGER{#{string}}"
|
99
|
-
end
|
100
|
-
|
101
|
-
def boolean(string)
|
102
|
-
"BOOLEAN{#{string}}"
|
103
|
-
end
|
104
|
-
|
105
|
-
def hash(*pairs)
|
106
|
-
"HASH{" + pairs.map {|k,v| "#{k}=#{v}" }.join(", ") + "}"
|
107
|
-
end
|
108
|
-
|
109
|
-
def id(id)
|
110
|
-
"ID:#{id}"
|
111
|
-
end
|
112
|
-
|
113
|
-
def data(id)
|
114
|
-
"@#{id}"
|
115
|
-
end
|
116
|
-
|
117
|
-
def partial_name(name)
|
118
|
-
"PARTIAL:#{name}"
|
119
|
-
end
|
120
|
-
|
121
|
-
def path(*parts)
|
122
|
-
"PATH:#{parts.join("/")}"
|
123
|
-
end
|
124
|
-
end
|
125
|
-
|
126
|
-
it "parses simple mustaches" do
|
127
|
-
ast_for("{{foo}}").should == root { mustache id("foo") }
|
128
|
-
end
|
129
|
-
|
130
|
-
it "parses simple mustaches with data" do
|
131
|
-
ast_for("{{@foo}}").should == root { mustache data("foo") }
|
132
|
-
end
|
133
|
-
|
134
|
-
it "parses mustaches with paths" do
|
135
|
-
ast_for("{{foo/bar}}").should == root { mustache path("foo", "bar") }
|
136
|
-
end
|
137
|
-
|
138
|
-
it "parses mustaches with this/foo" do
|
139
|
-
ast_for("{{this/foo}}").should == root { mustache id("foo") }
|
140
|
-
end
|
141
|
-
|
142
|
-
it "parses mustaches with - in a path" do
|
143
|
-
ast_for("{{foo-bar}}").should == root { mustache id("foo-bar") }
|
144
|
-
end
|
145
|
-
|
146
|
-
it "parses mustaches with parameters" do
|
147
|
-
ast_for("{{foo bar}}").should == root { mustache id("foo"), [id("bar")] }
|
148
|
-
end
|
149
|
-
|
150
|
-
it "parses mustaches with hash arguments" do
|
151
|
-
ast_for("{{foo bar=baz}}").should == root do
|
152
|
-
mustache id("foo"), [], hash(["bar", id("baz")])
|
153
|
-
end
|
154
|
-
|
155
|
-
ast_for("{{foo bar=1}}").should == root do
|
156
|
-
mustache id("foo"), [], hash(["bar", integer("1")])
|
157
|
-
end
|
158
|
-
|
159
|
-
ast_for("{{foo bar=true}}").should == root do
|
160
|
-
mustache id("foo"), [], hash(["bar", boolean("true")])
|
161
|
-
end
|
162
|
-
|
163
|
-
ast_for("{{foo bar=false}}").should == root do
|
164
|
-
mustache id("foo"), [], hash(["bar", boolean("false")])
|
165
|
-
end
|
166
|
-
|
167
|
-
ast_for("{{foo bar=@baz}}").should == root do
|
168
|
-
mustache id("foo"), [], hash(["bar", data("baz")])
|
169
|
-
end
|
170
|
-
|
171
|
-
ast_for("{{foo bar=baz bat=bam}}").should == root do
|
172
|
-
mustache id("foo"), [], hash(["bar", "ID:baz"], ["bat", "ID:bam"])
|
173
|
-
end
|
174
|
-
|
175
|
-
ast_for("{{foo bar=baz bat=\"bam\"}}").should == root do
|
176
|
-
mustache id("foo"), [], hash(["bar", "ID:baz"], ["bat", "\"bam\""])
|
177
|
-
end
|
178
|
-
|
179
|
-
ast_for("{{foo bat='bam'}}").should == root do
|
180
|
-
mustache id("foo"), [], hash(["bat", "\"bam\""])
|
181
|
-
end
|
182
|
-
|
183
|
-
ast_for("{{foo omg bar=baz bat=\"bam\"}}").should == root do
|
184
|
-
mustache id("foo"), [id("omg")], hash(["bar", id("baz")], ["bat", string("bam")])
|
185
|
-
end
|
186
|
-
|
187
|
-
ast_for("{{foo omg bar=baz bat=\"bam\" baz=1}}").should == root do
|
188
|
-
mustache id("foo"), [id("omg")], hash(["bar", id("baz")], ["bat", string("bam")], ["baz", integer("1")])
|
189
|
-
end
|
190
|
-
|
191
|
-
ast_for("{{foo omg bar=baz bat=\"bam\" baz=true}}").should == root do
|
192
|
-
mustache id("foo"), [id("omg")], hash(["bar", id("baz")], ["bat", string("bam")], ["baz", boolean("true")])
|
193
|
-
end
|
194
|
-
|
195
|
-
ast_for("{{foo omg bar=baz bat=\"bam\" baz=false}}").should == root do
|
196
|
-
mustache id("foo"), [id("omg")], hash(["bar", id("baz")], ["bat", string("bam")], ["baz", boolean("false")])
|
197
|
-
end
|
198
|
-
end
|
199
|
-
|
200
|
-
it "parses mustaches with string parameters" do
|
201
|
-
ast_for("{{foo bar \"baz\" }}").should == root { mustache id("foo"), [id("bar"), string("baz")] }
|
202
|
-
end
|
203
|
-
|
204
|
-
it "parses mustaches with INTEGER parameters" do
|
205
|
-
ast_for("{{foo 1}}").should == root { mustache id("foo"), [integer("1")] }
|
206
|
-
end
|
207
|
-
|
208
|
-
it "parses mustaches with BOOLEAN parameters" do
|
209
|
-
ast_for("{{foo true}}").should == root { mustache id("foo"), [boolean("true")] }
|
210
|
-
ast_for("{{foo false}}").should == root { mustache id("foo"), [boolean("false")] }
|
211
|
-
end
|
212
|
-
|
213
|
-
it "parses mutaches with DATA parameters" do
|
214
|
-
ast_for("{{foo @bar}}").should == root { mustache id("foo"), [data("bar")] }
|
215
|
-
end
|
216
|
-
|
217
|
-
it "parses contents followed by a mustache" do
|
218
|
-
ast_for("foo bar {{baz}}").should == root do
|
219
|
-
content "foo bar "
|
220
|
-
mustache id("baz")
|
221
|
-
end
|
222
|
-
end
|
223
|
-
|
224
|
-
it "parses a partial" do
|
225
|
-
ast_for("{{> foo }}").should == root { partial partial_name("foo") }
|
226
|
-
end
|
227
|
-
|
228
|
-
it "parses a partial with context" do
|
229
|
-
ast_for("{{> foo bar}}").should == root { partial partial_name("foo"), id("bar") }
|
230
|
-
end
|
231
|
-
|
232
|
-
it "parses a partial with a complex name" do
|
233
|
-
ast_for("{{> shared/partial}}").should == root { partial partial_name("shared/partial") }
|
234
|
-
end
|
235
|
-
|
236
|
-
it "parses a comment" do
|
237
|
-
ast_for("{{! this is a comment }}").should == root do
|
238
|
-
comment " this is a comment "
|
239
|
-
end
|
240
|
-
end
|
241
|
-
|
242
|
-
it "parses a multi-line comment" do
|
243
|
-
ast_for("{{!\nthis is a multi-line comment\n}}").should == root do
|
244
|
-
multiline_comment "this is a multi-line comment"
|
245
|
-
end
|
246
|
-
end
|
247
|
-
|
248
|
-
it "parses an inverse section" do
|
249
|
-
ast_for("{{#foo}} bar {{^}} baz {{/foo}}").should == root do
|
250
|
-
block do
|
251
|
-
mustache id("foo")
|
252
|
-
|
253
|
-
program do
|
254
|
-
content " bar "
|
255
|
-
end
|
256
|
-
|
257
|
-
inverse do
|
258
|
-
content " baz "
|
259
|
-
end
|
260
|
-
end
|
261
|
-
end
|
262
|
-
end
|
263
|
-
|
264
|
-
it "parses an inverse ('else'-style) section" do
|
265
|
-
ast_for("{{#foo}} bar {{else}} baz {{/foo}}").should == root do
|
266
|
-
block do
|
267
|
-
mustache id("foo")
|
268
|
-
|
269
|
-
program do
|
270
|
-
content " bar "
|
271
|
-
end
|
272
|
-
|
273
|
-
inverse do
|
274
|
-
content " baz "
|
275
|
-
end
|
276
|
-
end
|
277
|
-
end
|
278
|
-
end
|
279
|
-
|
280
|
-
it "parses empty blocks" do
|
281
|
-
ast_for("{{#foo}}{{/foo}}").should == root do
|
282
|
-
block do
|
283
|
-
mustache id("foo")
|
284
|
-
|
285
|
-
program do
|
286
|
-
# empty program
|
287
|
-
end
|
288
|
-
end
|
289
|
-
end
|
290
|
-
end
|
291
|
-
|
292
|
-
it "parses empty blocks with empty inverse section" do
|
293
|
-
ast_for("{{#foo}}{{^}}{{/foo}}").should == root do
|
294
|
-
block do
|
295
|
-
mustache id("foo")
|
296
|
-
|
297
|
-
program do
|
298
|
-
# empty program
|
299
|
-
end
|
300
|
-
|
301
|
-
inverse do
|
302
|
-
# empty inverse
|
303
|
-
end
|
304
|
-
end
|
305
|
-
end
|
306
|
-
end
|
307
|
-
|
308
|
-
it "parses empty blocks with empty inverse ('else'-style) section" do
|
309
|
-
ast_for("{{#foo}}{{else}}{{/foo}}").should == root do
|
310
|
-
block do
|
311
|
-
mustache id("foo")
|
312
|
-
|
313
|
-
program do
|
314
|
-
# empty program
|
315
|
-
end
|
316
|
-
|
317
|
-
inverse do
|
318
|
-
# empty inverse
|
319
|
-
end
|
320
|
-
end
|
321
|
-
end
|
322
|
-
end
|
323
|
-
|
324
|
-
it "parses non-empty blocks with empty inverse section" do
|
325
|
-
ast_for("{{#foo}} bar {{^}}{{/foo}}").should == root do
|
326
|
-
block do
|
327
|
-
mustache id("foo")
|
328
|
-
|
329
|
-
program do
|
330
|
-
content " bar "
|
331
|
-
end
|
332
|
-
|
333
|
-
inverse do
|
334
|
-
# empty inverse
|
335
|
-
end
|
336
|
-
end
|
337
|
-
end
|
338
|
-
end
|
339
|
-
|
340
|
-
it "parses non-empty blocks with empty inverse ('else'-style) section" do
|
341
|
-
ast_for("{{#foo}} bar {{else}}{{/foo}}").should == root do
|
342
|
-
block do
|
343
|
-
mustache id("foo")
|
344
|
-
|
345
|
-
program do
|
346
|
-
content " bar "
|
347
|
-
end
|
348
|
-
|
349
|
-
inverse do
|
350
|
-
# empty inverse
|
351
|
-
end
|
352
|
-
end
|
353
|
-
end
|
354
|
-
end
|
355
|
-
|
356
|
-
it "parses empty blocks with non-empty inverse section" do
|
357
|
-
ast_for("{{#foo}}{{^}} bar {{/foo}}").should == root do
|
358
|
-
block do
|
359
|
-
mustache id("foo")
|
360
|
-
|
361
|
-
program do
|
362
|
-
# empty program
|
363
|
-
end
|
364
|
-
|
365
|
-
inverse do
|
366
|
-
content " bar "
|
367
|
-
end
|
368
|
-
end
|
369
|
-
end
|
370
|
-
end
|
371
|
-
|
372
|
-
it "parses empty blocks with non-empty inverse ('else'-style) section" do
|
373
|
-
ast_for("{{#foo}}{{else}} bar {{/foo}}").should == root do
|
374
|
-
block do
|
375
|
-
mustache id("foo")
|
376
|
-
|
377
|
-
program do
|
378
|
-
# empty program
|
379
|
-
end
|
380
|
-
|
381
|
-
inverse do
|
382
|
-
content " bar "
|
383
|
-
end
|
384
|
-
end
|
385
|
-
end
|
386
|
-
end
|
387
|
-
|
388
|
-
it "parses a standalone inverse section" do
|
389
|
-
ast_for("{{^foo}}bar{{/foo}}").should == root do
|
390
|
-
block do
|
391
|
-
mustache id("foo")
|
392
|
-
|
393
|
-
inverse do
|
394
|
-
content "bar"
|
395
|
-
end
|
396
|
-
end
|
397
|
-
end
|
398
|
-
end
|
399
|
-
|
400
|
-
it "raises if there's a Parse error" do
|
401
|
-
lambda { ast_for("{{foo}") }.should raise_error(V8::JSError, /Parse error on line 1/)
|
402
|
-
lambda { ast_for("{{foo &}}")}.should raise_error(V8::JSError, /Parse error on line 1/)
|
403
|
-
lambda { ast_for("{{#goodbyes}}{{/hellos}}") }.should raise_error(V8::JSError, /goodbyes doesn't match hellos/)
|
404
|
-
end
|
405
|
-
|
406
|
-
it "knows how to report the correct line number in errors" do
|
407
|
-
lambda { ast_for("hello\nmy\n{{foo}") }.should raise_error(V8::JSError, /Parse error on line 3/m)
|
408
|
-
lambda { ast_for("hello\n\nmy\n\n{{foo}") }.should raise_error(V8::JSError, /Parse error on line 5/m)
|
409
|
-
end
|
410
|
-
|
411
|
-
it "knows how to report the correct line number in errors when the first character is a newline" do
|
412
|
-
lambda { ast_for("\n\nhello\n\nmy\n\n{{foo}") }.should raise_error(V8::JSError, /Parse error on line 7/m)
|
413
|
-
end
|
414
|
-
|
415
|
-
context "externally compiled AST" do
|
416
|
-
|
417
|
-
it "can pass through an already-compiled AST" do
|
418
|
-
ast_for(@context.eval('new Handlebars.AST.ProgramNode([ new Handlebars.AST.ContentNode("Hello")]);')).should == root do
|
419
|
-
content "Hello"
|
420
|
-
end
|
421
|
-
end
|
422
|
-
|
423
|
-
it "can pass through an already-compiled AST via compile/precompile" do
|
424
|
-
@context = Handlebars::Spec::FULL_CONTEXT
|
425
|
-
|
426
|
-
code = 'Handlebars.compile(new Handlebars.AST.ProgramNode([ new Handlebars.AST.ContentNode("Hello")]))();'
|
427
|
-
@context.eval(code).should == "Hello"
|
428
|
-
|
429
|
-
code = @context.eval 'Handlebars.precompile(new Handlebars.AST.ProgramNode([ new Handlebars.AST.ContentNode("Hello")]))'
|
430
|
-
@context.eval("(#{code})(this)").should == "Hello"
|
431
|
-
end
|
432
|
-
end
|
433
|
-
end
|
@@ -1,1370 +0,0 @@
|
|
1
|
-
var Handlebars;
|
2
|
-
if (!Handlebars) {
|
3
|
-
// Setup for Node package testing
|
4
|
-
Handlebars = require('../lib/handlebars');
|
5
|
-
|
6
|
-
var assert = require("assert"),
|
7
|
-
|
8
|
-
equal = assert.equal,
|
9
|
-
equals = assert.equal,
|
10
|
-
ok = assert.ok;
|
11
|
-
|
12
|
-
// Note that this doesn't have the same context separation as the rspec test.
|
13
|
-
// Both should be run for full acceptance of the two libary modes.
|
14
|
-
var CompilerContext = {
|
15
|
-
compile: function(template, options) {
|
16
|
-
var templateSpec = Handlebars.precompile(template, options);
|
17
|
-
return Handlebars.template(eval('(' + templateSpec + ')'));
|
18
|
-
},
|
19
|
-
compileWithPartial: function(template, options) {
|
20
|
-
return Handlebars.compile(template, options);
|
21
|
-
}
|
22
|
-
};
|
23
|
-
} else {
|
24
|
-
var _equal = equal;
|
25
|
-
equals = equal = function(a, b, msg) {
|
26
|
-
// Allow exec with missing message params
|
27
|
-
_equal(a, b, msg || '');
|
28
|
-
};
|
29
|
-
}
|
30
|
-
|
31
|
-
suite("basic context");
|
32
|
-
|
33
|
-
function shouldCompileTo(string, hashOrArray, expected, message) {
|
34
|
-
shouldCompileToWithPartials(string, hashOrArray, false, expected, message);
|
35
|
-
}
|
36
|
-
|
37
|
-
function shouldCompileToWithPartials(string, hashOrArray, partials, expected, message) {
|
38
|
-
var result = compileWithPartials(string, hashOrArray, partials);
|
39
|
-
equal(result, expected, "'" + expected + "' should === '" + result + "': " + message);
|
40
|
-
}
|
41
|
-
|
42
|
-
function compileWithPartials(string, hashOrArray, partials) {
|
43
|
-
var template = CompilerContext[partials ? 'compileWithPartial' : 'compile'](string), ary;
|
44
|
-
if(Object.prototype.toString.call(hashOrArray) === "[object Array]") {
|
45
|
-
var helpers = hashOrArray[1];
|
46
|
-
|
47
|
-
if(helpers) {
|
48
|
-
for(var prop in Handlebars.helpers) {
|
49
|
-
helpers[prop] = helpers[prop] || Handlebars.helpers[prop];
|
50
|
-
}
|
51
|
-
}
|
52
|
-
|
53
|
-
ary = [];
|
54
|
-
ary.push(hashOrArray[0]);
|
55
|
-
ary.push({ helpers: hashOrArray[1], partials: hashOrArray[2] });
|
56
|
-
} else {
|
57
|
-
ary = [hashOrArray];
|
58
|
-
}
|
59
|
-
|
60
|
-
return template.apply(this, ary);
|
61
|
-
}
|
62
|
-
|
63
|
-
function shouldThrow(fn, exception, message) {
|
64
|
-
var caught = false,
|
65
|
-
exType, exMessage;
|
66
|
-
|
67
|
-
if (exception instanceof Array) {
|
68
|
-
exType = exception[0];
|
69
|
-
exMessage = exception[1];
|
70
|
-
} else if (typeof exception === 'string') {
|
71
|
-
exType = Error;
|
72
|
-
exMessage = exception;
|
73
|
-
} else {
|
74
|
-
exType = exception;
|
75
|
-
}
|
76
|
-
|
77
|
-
try {
|
78
|
-
fn();
|
79
|
-
}
|
80
|
-
catch (e) {
|
81
|
-
if (e instanceof exType) {
|
82
|
-
if (!exMessage || e.message === exMessage) {
|
83
|
-
caught = true;
|
84
|
-
}
|
85
|
-
}
|
86
|
-
}
|
87
|
-
|
88
|
-
ok(caught, message || null);
|
89
|
-
}
|
90
|
-
|
91
|
-
test("most basic", function() {
|
92
|
-
shouldCompileTo("{{foo}}", { foo: "foo" }, "foo");
|
93
|
-
});
|
94
|
-
|
95
|
-
test("compiling with a basic context", function() {
|
96
|
-
shouldCompileTo("Goodbye\n{{cruel}}\n{{world}}!", {cruel: "cruel", world: "world"}, "Goodbye\ncruel\nworld!",
|
97
|
-
"It works if all the required keys are provided");
|
98
|
-
});
|
99
|
-
|
100
|
-
test("comments", function() {
|
101
|
-
shouldCompileTo("{{! Goodbye}}Goodbye\n{{cruel}}\n{{world}}!",
|
102
|
-
{cruel: "cruel", world: "world"}, "Goodbye\ncruel\nworld!",
|
103
|
-
"comments are ignored");
|
104
|
-
});
|
105
|
-
|
106
|
-
test("boolean", function() {
|
107
|
-
var string = "{{#goodbye}}GOODBYE {{/goodbye}}cruel {{world}}!";
|
108
|
-
shouldCompileTo(string, {goodbye: true, world: "world"}, "GOODBYE cruel world!",
|
109
|
-
"booleans show the contents when true");
|
110
|
-
|
111
|
-
shouldCompileTo(string, {goodbye: false, world: "world"}, "cruel world!",
|
112
|
-
"booleans do not show the contents when false");
|
113
|
-
});
|
114
|
-
|
115
|
-
test("zeros", function() {
|
116
|
-
shouldCompileTo("num1: {{num1}}, num2: {{num2}}", {num1: 42, num2: 0},
|
117
|
-
"num1: 42, num2: 0");
|
118
|
-
shouldCompileTo("num: {{.}}", 0, "num: 0");
|
119
|
-
shouldCompileTo("num: {{num1/num2}}", {num1: {num2: 0}}, "num: 0");
|
120
|
-
});
|
121
|
-
|
122
|
-
test("newlines", function() {
|
123
|
-
shouldCompileTo("Alan's\nTest", {}, "Alan's\nTest");
|
124
|
-
shouldCompileTo("Alan's\rTest", {}, "Alan's\rTest");
|
125
|
-
});
|
126
|
-
|
127
|
-
test("escaping text", function() {
|
128
|
-
shouldCompileTo("Awesome's", {}, "Awesome's", "text is escaped so that it doesn't get caught on single quotes");
|
129
|
-
shouldCompileTo("Awesome\\", {}, "Awesome\\", "text is escaped so that the closing quote can't be ignored");
|
130
|
-
shouldCompileTo("Awesome\\\\ foo", {}, "Awesome\\\\ foo", "text is escaped so that it doesn't mess up backslashes");
|
131
|
-
shouldCompileTo("Awesome {{foo}}", {foo: '\\'}, "Awesome \\", "text is escaped so that it doesn't mess up backslashes");
|
132
|
-
shouldCompileTo(' " " ', {}, ' " " ', "double quotes never produce invalid javascript");
|
133
|
-
});
|
134
|
-
|
135
|
-
test("escaping expressions", function() {
|
136
|
-
shouldCompileTo("{{{awesome}}}", {awesome: "&\"\\<>"}, '&\"\\<>',
|
137
|
-
"expressions with 3 handlebars aren't escaped");
|
138
|
-
|
139
|
-
shouldCompileTo("{{&awesome}}", {awesome: "&\"\\<>"}, '&\"\\<>',
|
140
|
-
"expressions with {{& handlebars aren't escaped");
|
141
|
-
|
142
|
-
shouldCompileTo("{{awesome}}", {awesome: "&\"'`\\<>"}, '&"'`\\<>',
|
143
|
-
"by default expressions should be escaped");
|
144
|
-
|
145
|
-
shouldCompileTo("{{awesome}}", {awesome: "Escaped, <b> looks like: <b>"}, 'Escaped, <b> looks like: &lt;b&gt;',
|
146
|
-
"escaping should properly handle amperstands");
|
147
|
-
});
|
148
|
-
|
149
|
-
test("functions returning safestrings shouldn't be escaped", function() {
|
150
|
-
var hash = {awesome: function() { return new Handlebars.SafeString("&\"\\<>"); }};
|
151
|
-
shouldCompileTo("{{awesome}}", hash, '&\"\\<>',
|
152
|
-
"functions returning safestrings aren't escaped");
|
153
|
-
});
|
154
|
-
|
155
|
-
test("functions", function() {
|
156
|
-
shouldCompileTo("{{awesome}}", {awesome: function() { return "Awesome"; }}, "Awesome",
|
157
|
-
"functions are called and render their output");
|
158
|
-
shouldCompileTo("{{awesome}}", {awesome: function() { return this.more; }, more: "More awesome"}, "More awesome",
|
159
|
-
"functions are bound to the context");
|
160
|
-
});
|
161
|
-
|
162
|
-
test("paths with hyphens", function() {
|
163
|
-
shouldCompileTo("{{foo-bar}}", {"foo-bar": "baz"}, "baz", "Paths can contain hyphens (-)");
|
164
|
-
shouldCompileTo("{{foo.foo-bar}}", {foo: {"foo-bar": "baz"}}, "baz", "Paths can contain hyphens (-)");
|
165
|
-
shouldCompileTo("{{foo/foo-bar}}", {foo: {"foo-bar": "baz"}}, "baz", "Paths can contain hyphens (-)");
|
166
|
-
});
|
167
|
-
|
168
|
-
test("nested paths", function() {
|
169
|
-
shouldCompileTo("Goodbye {{alan/expression}} world!", {alan: {expression: "beautiful"}},
|
170
|
-
"Goodbye beautiful world!", "Nested paths access nested objects");
|
171
|
-
});
|
172
|
-
|
173
|
-
test("nested paths with empty string value", function() {
|
174
|
-
shouldCompileTo("Goodbye {{alan/expression}} world!", {alan: {expression: ""}},
|
175
|
-
"Goodbye world!", "Nested paths access nested objects with empty string");
|
176
|
-
});
|
177
|
-
|
178
|
-
test("literal paths", function() {
|
179
|
-
shouldCompileTo("Goodbye {{[@alan]/expression}} world!", {"@alan": {expression: "beautiful"}},
|
180
|
-
"Goodbye beautiful world!", "Literal paths can be used");
|
181
|
-
shouldCompileTo("Goodbye {{[foo bar]/expression}} world!", {"foo bar": {expression: "beautiful"}},
|
182
|
-
"Goodbye beautiful world!", "Literal paths can be used");
|
183
|
-
});
|
184
|
-
|
185
|
-
test('literal references', function() {
|
186
|
-
shouldCompileTo("Goodbye {{[foo bar]}} world!", {"foo bar": "beautiful"},
|
187
|
-
"Goodbye beautiful world!", "Literal paths can be used");
|
188
|
-
});
|
189
|
-
|
190
|
-
test("that current context path ({{.}}) doesn't hit helpers", function() {
|
191
|
-
shouldCompileTo("test: {{.}}", [null, {helper: "awesome"}], "test: ");
|
192
|
-
});
|
193
|
-
|
194
|
-
test("complex but empty paths", function() {
|
195
|
-
shouldCompileTo("{{person/name}}", {person: {name: null}}, "");
|
196
|
-
shouldCompileTo("{{person/name}}", {person: {}}, "");
|
197
|
-
});
|
198
|
-
|
199
|
-
test("this keyword in paths", function() {
|
200
|
-
var string = "{{#goodbyes}}{{this}}{{/goodbyes}}";
|
201
|
-
var hash = {goodbyes: ["goodbye", "Goodbye", "GOODBYE"]};
|
202
|
-
shouldCompileTo(string, hash, "goodbyeGoodbyeGOODBYE",
|
203
|
-
"This keyword in paths evaluates to current context");
|
204
|
-
|
205
|
-
string = "{{#hellos}}{{this/text}}{{/hellos}}";
|
206
|
-
hash = {hellos: [{text: "hello"}, {text: "Hello"}, {text: "HELLO"}]};
|
207
|
-
shouldCompileTo(string, hash, "helloHelloHELLO", "This keyword evaluates in more complex paths");
|
208
|
-
});
|
209
|
-
|
210
|
-
test("this keyword nested inside path", function() {
|
211
|
-
var string = "{{#hellos}}{{text/this/foo}}{{/hellos}}";
|
212
|
-
shouldThrow(function() {
|
213
|
-
CompilerContext.compile(string);
|
214
|
-
}, Error, "Should throw exception");
|
215
|
-
});
|
216
|
-
|
217
|
-
test("this keyword in helpers", function() {
|
218
|
-
var helpers = {foo: function(value) {
|
219
|
-
return 'bar ' + value;
|
220
|
-
}};
|
221
|
-
var string = "{{#goodbyes}}{{foo this}}{{/goodbyes}}";
|
222
|
-
var hash = {goodbyes: ["goodbye", "Goodbye", "GOODBYE"]};
|
223
|
-
shouldCompileTo(string, [hash, helpers], "bar goodbyebar Goodbyebar GOODBYE",
|
224
|
-
"This keyword in paths evaluates to current context");
|
225
|
-
|
226
|
-
string = "{{#hellos}}{{foo this/text}}{{/hellos}}";
|
227
|
-
hash = {hellos: [{text: "hello"}, {text: "Hello"}, {text: "HELLO"}]};
|
228
|
-
shouldCompileTo(string, [hash, helpers], "bar hellobar Hellobar HELLO", "This keyword evaluates in more complex paths");
|
229
|
-
});
|
230
|
-
|
231
|
-
test("this keyword nested inside helpers param", function() {
|
232
|
-
var string = "{{#hellos}}{{foo text/this/foo}}{{/hellos}}";
|
233
|
-
shouldThrow(function() {
|
234
|
-
CompilerContext.compile(string);
|
235
|
-
}, Error, "Should throw exception");
|
236
|
-
});
|
237
|
-
|
238
|
-
suite("inverted sections");
|
239
|
-
|
240
|
-
test("inverted sections with unset value", function() {
|
241
|
-
var string = "{{#goodbyes}}{{this}}{{/goodbyes}}{{^goodbyes}}Right On!{{/goodbyes}}";
|
242
|
-
var hash = {};
|
243
|
-
shouldCompileTo(string, hash, "Right On!", "Inverted section rendered when value isn't set.");
|
244
|
-
});
|
245
|
-
|
246
|
-
test("inverted section with false value", function() {
|
247
|
-
var string = "{{#goodbyes}}{{this}}{{/goodbyes}}{{^goodbyes}}Right On!{{/goodbyes}}";
|
248
|
-
var hash = {goodbyes: false};
|
249
|
-
shouldCompileTo(string, hash, "Right On!", "Inverted section rendered when value is false.");
|
250
|
-
});
|
251
|
-
|
252
|
-
test("inverted section with empty set", function() {
|
253
|
-
var string = "{{#goodbyes}}{{this}}{{/goodbyes}}{{^goodbyes}}Right On!{{/goodbyes}}";
|
254
|
-
var hash = {goodbyes: []};
|
255
|
-
shouldCompileTo(string, hash, "Right On!", "Inverted section rendered when value is empty set.");
|
256
|
-
});
|
257
|
-
|
258
|
-
suite("blocks");
|
259
|
-
|
260
|
-
test("array", function() {
|
261
|
-
var string = "{{#goodbyes}}{{text}}! {{/goodbyes}}cruel {{world}}!";
|
262
|
-
var hash = {goodbyes: [{text: "goodbye"}, {text: "Goodbye"}, {text: "GOODBYE"}], world: "world"};
|
263
|
-
shouldCompileTo(string, hash, "goodbye! Goodbye! GOODBYE! cruel world!",
|
264
|
-
"Arrays iterate over the contents when not empty");
|
265
|
-
|
266
|
-
shouldCompileTo(string, {goodbyes: [], world: "world"}, "cruel world!",
|
267
|
-
"Arrays ignore the contents when empty");
|
268
|
-
|
269
|
-
});
|
270
|
-
|
271
|
-
test("array with @index", function() {
|
272
|
-
var string = "{{#goodbyes}}{{@index}}. {{text}}! {{/goodbyes}}cruel {{world}}!";
|
273
|
-
var hash = {goodbyes: [{text: "goodbye"}, {text: "Goodbye"}, {text: "GOODBYE"}], world: "world"};
|
274
|
-
|
275
|
-
var template = CompilerContext.compile(string);
|
276
|
-
var result = template(hash);
|
277
|
-
|
278
|
-
equal(result, "0. goodbye! 1. Goodbye! 2. GOODBYE! cruel world!", "The @index variable is used");
|
279
|
-
});
|
280
|
-
|
281
|
-
test("empty block", function() {
|
282
|
-
var string = "{{#goodbyes}}{{/goodbyes}}cruel {{world}}!";
|
283
|
-
var hash = {goodbyes: [{text: "goodbye"}, {text: "Goodbye"}, {text: "GOODBYE"}], world: "world"};
|
284
|
-
shouldCompileTo(string, hash, "cruel world!",
|
285
|
-
"Arrays iterate over the contents when not empty");
|
286
|
-
|
287
|
-
shouldCompileTo(string, {goodbyes: [], world: "world"}, "cruel world!",
|
288
|
-
"Arrays ignore the contents when empty");
|
289
|
-
});
|
290
|
-
|
291
|
-
test("nested iteration", function() {
|
292
|
-
|
293
|
-
});
|
294
|
-
|
295
|
-
test("block with complex lookup", function() {
|
296
|
-
var string = "{{#goodbyes}}{{text}} cruel {{../name}}! {{/goodbyes}}";
|
297
|
-
var hash = {name: "Alan", goodbyes: [{text: "goodbye"}, {text: "Goodbye"}, {text: "GOODBYE"}]};
|
298
|
-
|
299
|
-
shouldCompileTo(string, hash, "goodbye cruel Alan! Goodbye cruel Alan! GOODBYE cruel Alan! ",
|
300
|
-
"Templates can access variables in contexts up the stack with relative path syntax");
|
301
|
-
});
|
302
|
-
|
303
|
-
test("block with complex lookup using nested context", function() {
|
304
|
-
var string = "{{#goodbyes}}{{text}} cruel {{foo/../name}}! {{/goodbyes}}";
|
305
|
-
|
306
|
-
shouldThrow(function() {
|
307
|
-
CompilerContext.compile(string);
|
308
|
-
}, Error, "Should throw exception");
|
309
|
-
});
|
310
|
-
|
311
|
-
test("helper with complex lookup$", function() {
|
312
|
-
var string = "{{#goodbyes}}{{{link ../prefix}}}{{/goodbyes}}";
|
313
|
-
var hash = {prefix: "/root", goodbyes: [{text: "Goodbye", url: "goodbye"}]};
|
314
|
-
var helpers = {link: function(prefix) {
|
315
|
-
return "<a href='" + prefix + "/" + this.url + "'>" + this.text + "</a>";
|
316
|
-
}};
|
317
|
-
shouldCompileTo(string, [hash, helpers], "<a href='/root/goodbye'>Goodbye</a>");
|
318
|
-
});
|
319
|
-
|
320
|
-
test("helper block with complex lookup expression", function() {
|
321
|
-
var string = "{{#goodbyes}}{{../name}}{{/goodbyes}}";
|
322
|
-
var hash = {name: "Alan"};
|
323
|
-
var helpers = {goodbyes: function(options) {
|
324
|
-
var out = "";
|
325
|
-
var byes = ["Goodbye", "goodbye", "GOODBYE"];
|
326
|
-
for (var i = 0,j = byes.length; i < j; i++) {
|
327
|
-
out += byes[i] + " " + options.fn(this) + "! ";
|
328
|
-
}
|
329
|
-
return out;
|
330
|
-
}};
|
331
|
-
shouldCompileTo(string, [hash, helpers], "Goodbye Alan! goodbye Alan! GOODBYE Alan! ");
|
332
|
-
});
|
333
|
-
|
334
|
-
test("helper with complex lookup and nested template", function() {
|
335
|
-
var string = "{{#goodbyes}}{{#link ../prefix}}{{text}}{{/link}}{{/goodbyes}}";
|
336
|
-
var hash = {prefix: '/root', goodbyes: [{text: "Goodbye", url: "goodbye"}]};
|
337
|
-
var helpers = {link: function (prefix, options) {
|
338
|
-
return "<a href='" + prefix + "/" + this.url + "'>" + options.fn(this) + "</a>";
|
339
|
-
}};
|
340
|
-
shouldCompileToWithPartials(string, [hash, helpers], false, "<a href='/root/goodbye'>Goodbye</a>");
|
341
|
-
});
|
342
|
-
|
343
|
-
test("helper with complex lookup and nested template in VM+Compiler", function() {
|
344
|
-
var string = "{{#goodbyes}}{{#link ../prefix}}{{text}}{{/link}}{{/goodbyes}}";
|
345
|
-
var hash = {prefix: '/root', goodbyes: [{text: "Goodbye", url: "goodbye"}]};
|
346
|
-
var helpers = {link: function (prefix, options) {
|
347
|
-
return "<a href='" + prefix + "/" + this.url + "'>" + options.fn(this) + "</a>";
|
348
|
-
}};
|
349
|
-
shouldCompileToWithPartials(string, [hash, helpers], true, "<a href='/root/goodbye'>Goodbye</a>");
|
350
|
-
});
|
351
|
-
|
352
|
-
test("block with deep nested complex lookup", function() {
|
353
|
-
var string = "{{#outer}}Goodbye {{#inner}}cruel {{../../omg}}{{/inner}}{{/outer}}";
|
354
|
-
var hash = {omg: "OMG!", outer: [{ inner: [{ text: "goodbye" }] }] };
|
355
|
-
|
356
|
-
shouldCompileTo(string, hash, "Goodbye cruel OMG!");
|
357
|
-
});
|
358
|
-
|
359
|
-
test("block helper", function() {
|
360
|
-
var string = "{{#goodbyes}}{{text}}! {{/goodbyes}}cruel {{world}}!";
|
361
|
-
var template = CompilerContext.compile(string);
|
362
|
-
|
363
|
-
var result = template({world: "world"}, { helpers: {goodbyes: function(options) { return options.fn({text: "GOODBYE"}); }}});
|
364
|
-
equal(result, "GOODBYE! cruel world!", "Block helper executed");
|
365
|
-
});
|
366
|
-
|
367
|
-
test("block helper staying in the same context", function() {
|
368
|
-
var string = "{{#form}}<p>{{name}}</p>{{/form}}";
|
369
|
-
var template = CompilerContext.compile(string);
|
370
|
-
|
371
|
-
var result = template({name: "Yehuda"}, {helpers: {form: function(options) { return "<form>" + options.fn(this) + "</form>"; } }});
|
372
|
-
equal(result, "<form><p>Yehuda</p></form>", "Block helper executed with current context");
|
373
|
-
});
|
374
|
-
|
375
|
-
test("block helper should have context in this", function() {
|
376
|
-
var source = "<ul>{{#people}}<li>{{#link}}{{name}}{{/link}}</li>{{/people}}</ul>";
|
377
|
-
var link = function(options) {
|
378
|
-
return '<a href="/people/' + this.id + '">' + options.fn(this) + '</a>';
|
379
|
-
};
|
380
|
-
var data = { "people": [
|
381
|
-
{ "name": "Alan", "id": 1 },
|
382
|
-
{ "name": "Yehuda", "id": 2 }
|
383
|
-
]};
|
384
|
-
|
385
|
-
shouldCompileTo(source, [data, {link: link}], "<ul><li><a href=\"/people/1\">Alan</a></li><li><a href=\"/people/2\">Yehuda</a></li></ul>");
|
386
|
-
});
|
387
|
-
|
388
|
-
test("block helper for undefined value", function() {
|
389
|
-
shouldCompileTo("{{#empty}}shouldn't render{{/empty}}", {}, "");
|
390
|
-
});
|
391
|
-
|
392
|
-
test("block helper passing a new context", function() {
|
393
|
-
var string = "{{#form yehuda}}<p>{{name}}</p>{{/form}}";
|
394
|
-
var template = CompilerContext.compile(string);
|
395
|
-
|
396
|
-
var result = template({yehuda: {name: "Yehuda"}}, { helpers: {form: function(context, options) { return "<form>" + options.fn(context) + "</form>"; }}});
|
397
|
-
equal(result, "<form><p>Yehuda</p></form>", "Context variable resolved");
|
398
|
-
});
|
399
|
-
|
400
|
-
test("block helper passing a complex path context", function() {
|
401
|
-
var string = "{{#form yehuda/cat}}<p>{{name}}</p>{{/form}}";
|
402
|
-
var template = CompilerContext.compile(string);
|
403
|
-
|
404
|
-
var result = template({yehuda: {name: "Yehuda", cat: {name: "Harold"}}}, { helpers: {form: function(context, options) { return "<form>" + options.fn(context) + "</form>"; }}});
|
405
|
-
equal(result, "<form><p>Harold</p></form>", "Complex path variable resolved");
|
406
|
-
});
|
407
|
-
|
408
|
-
test("nested block helpers", function() {
|
409
|
-
var string = "{{#form yehuda}}<p>{{name}}</p>{{#link}}Hello{{/link}}{{/form}}";
|
410
|
-
var template = CompilerContext.compile(string);
|
411
|
-
|
412
|
-
var result = template({
|
413
|
-
yehuda: {name: "Yehuda" }
|
414
|
-
}, {
|
415
|
-
helpers: {
|
416
|
-
link: function(options) { return "<a href='" + this.name + "'>" + options.fn(this) + "</a>"; },
|
417
|
-
form: function(context, options) { return "<form>" + options.fn(context) + "</form>"; }
|
418
|
-
}
|
419
|
-
});
|
420
|
-
equal(result, "<form><p>Yehuda</p><a href='Yehuda'>Hello</a></form>", "Both blocks executed");
|
421
|
-
});
|
422
|
-
|
423
|
-
test("block inverted sections", function() {
|
424
|
-
shouldCompileTo("{{#people}}{{name}}{{^}}{{none}}{{/people}}", {none: "No people"},
|
425
|
-
"No people");
|
426
|
-
});
|
427
|
-
|
428
|
-
test("block inverted sections with empty arrays", function() {
|
429
|
-
shouldCompileTo("{{#people}}{{name}}{{^}}{{none}}{{/people}}", {none: "No people", people: []},
|
430
|
-
"No people");
|
431
|
-
});
|
432
|
-
|
433
|
-
test("block helper inverted sections", function() {
|
434
|
-
var string = "{{#list people}}{{name}}{{^}}<em>Nobody's here</em>{{/list}}";
|
435
|
-
var list = function(context, options) {
|
436
|
-
if (context.length > 0) {
|
437
|
-
var out = "<ul>";
|
438
|
-
for(var i = 0,j=context.length; i < j; i++) {
|
439
|
-
out += "<li>";
|
440
|
-
out += options.fn(context[i]);
|
441
|
-
out += "</li>";
|
442
|
-
}
|
443
|
-
out += "</ul>";
|
444
|
-
return out;
|
445
|
-
} else {
|
446
|
-
return "<p>" + options.inverse(this) + "</p>";
|
447
|
-
}
|
448
|
-
};
|
449
|
-
|
450
|
-
var hash = {people: [{name: "Alan"}, {name: "Yehuda"}]};
|
451
|
-
var empty = {people: []};
|
452
|
-
var rootMessage = {
|
453
|
-
people: [],
|
454
|
-
message: "Nobody's here"
|
455
|
-
};
|
456
|
-
|
457
|
-
var messageString = "{{#list people}}Hello{{^}}{{message}}{{/list}}";
|
458
|
-
|
459
|
-
// the meaning here may be kind of hard to catch, but list.not is always called,
|
460
|
-
// so we should see the output of both
|
461
|
-
shouldCompileTo(string, [hash, { list: list }], "<ul><li>Alan</li><li>Yehuda</li></ul>", "an inverse wrapper is passed in as a new context");
|
462
|
-
shouldCompileTo(string, [empty, { list: list }], "<p><em>Nobody's here</em></p>", "an inverse wrapper can be optionally called");
|
463
|
-
shouldCompileTo(messageString, [rootMessage, { list: list }], "<p>Nobody's here</p>", "the context of an inverse is the parent of the block");
|
464
|
-
});
|
465
|
-
|
466
|
-
suite("helpers hash");
|
467
|
-
|
468
|
-
test("providing a helpers hash", function() {
|
469
|
-
shouldCompileTo("Goodbye {{cruel}} {{world}}!", [{cruel: "cruel"}, {world: function() { return "world"; }}], "Goodbye cruel world!",
|
470
|
-
"helpers hash is available");
|
471
|
-
|
472
|
-
shouldCompileTo("Goodbye {{#iter}}{{cruel}} {{world}}{{/iter}}!", [{iter: [{cruel: "cruel"}]}, {world: function() { return "world"; }}],
|
473
|
-
"Goodbye cruel world!", "helpers hash is available inside other blocks");
|
474
|
-
});
|
475
|
-
|
476
|
-
test("in cases of conflict, helpers win", function() {
|
477
|
-
shouldCompileTo("{{{lookup}}}", [{lookup: 'Explicit'}, {lookup: function() { return 'helpers'; }}], "helpers",
|
478
|
-
"helpers hash has precedence escaped expansion");
|
479
|
-
shouldCompileTo("{{lookup}}", [{lookup: 'Explicit'}, {lookup: function() { return 'helpers'; }}], "helpers",
|
480
|
-
"helpers hash has precedence simple expansion");
|
481
|
-
});
|
482
|
-
|
483
|
-
test("the helpers hash is available is nested contexts", function() {
|
484
|
-
shouldCompileTo("{{#outer}}{{#inner}}{{helper}}{{/inner}}{{/outer}}",
|
485
|
-
[{'outer': {'inner': {'unused':[]}}}, {'helper': function() { return 'helper'; }}], "helper",
|
486
|
-
"helpers hash is available in nested contexts.");
|
487
|
-
});
|
488
|
-
|
489
|
-
suite("partials");
|
490
|
-
|
491
|
-
test("basic partials", function() {
|
492
|
-
var string = "Dudes: {{#dudes}}{{> dude}}{{/dudes}}";
|
493
|
-
var partial = "{{name}} ({{url}}) ";
|
494
|
-
var hash = {dudes: [{name: "Yehuda", url: "http://yehuda"}, {name: "Alan", url: "http://alan"}]};
|
495
|
-
shouldCompileToWithPartials(string, [hash, {}, {dude: partial}], true, "Dudes: Yehuda (http://yehuda) Alan (http://alan) ",
|
496
|
-
"Basic partials output based on current context.");
|
497
|
-
});
|
498
|
-
|
499
|
-
test("partials with context", function() {
|
500
|
-
var string = "Dudes: {{>dude dudes}}";
|
501
|
-
var partial = "{{#this}}{{name}} ({{url}}) {{/this}}";
|
502
|
-
var hash = {dudes: [{name: "Yehuda", url: "http://yehuda"}, {name: "Alan", url: "http://alan"}]};
|
503
|
-
shouldCompileToWithPartials(string, [hash, {}, {dude: partial}], true, "Dudes: Yehuda (http://yehuda) Alan (http://alan) ",
|
504
|
-
"Partials can be passed a context");
|
505
|
-
});
|
506
|
-
|
507
|
-
test("partial in a partial", function() {
|
508
|
-
var string = "Dudes: {{#dudes}}{{>dude}}{{/dudes}}";
|
509
|
-
var dude = "{{name}} {{> url}} ";
|
510
|
-
var url = "<a href='{{url}}'>{{url}}</a>";
|
511
|
-
var hash = {dudes: [{name: "Yehuda", url: "http://yehuda"}, {name: "Alan", url: "http://alan"}]};
|
512
|
-
shouldCompileToWithPartials(string, [hash, {}, {dude: dude, url: url}], true, "Dudes: Yehuda <a href='http://yehuda'>http://yehuda</a> Alan <a href='http://alan'>http://alan</a> ", "Partials are rendered inside of other partials");
|
513
|
-
});
|
514
|
-
|
515
|
-
test("rendering undefined partial throws an exception", function() {
|
516
|
-
shouldThrow(function() {
|
517
|
-
var template = CompilerContext.compile("{{> whatever}}");
|
518
|
-
template();
|
519
|
-
}, [Handlebars.Exception, 'The partial whatever could not be found'], "Should throw exception");
|
520
|
-
});
|
521
|
-
|
522
|
-
test("rendering template partial in vm mode throws an exception", function() {
|
523
|
-
shouldThrow(function() {
|
524
|
-
var template = CompilerContext.compile("{{> whatever}}");
|
525
|
-
template();
|
526
|
-
}, [Handlebars.Exception, 'The partial whatever could not be found'], "Should throw exception");
|
527
|
-
});
|
528
|
-
|
529
|
-
test("rendering function partial in vm mode", function() {
|
530
|
-
var string = "Dudes: {{#dudes}}{{> dude}}{{/dudes}}";
|
531
|
-
var partial = function(context) {
|
532
|
-
return context.name + ' (' + context.url + ') ';
|
533
|
-
};
|
534
|
-
var hash = {dudes: [{name: "Yehuda", url: "http://yehuda"}, {name: "Alan", url: "http://alan"}]};
|
535
|
-
shouldCompileTo(string, [hash, {}, {dude: partial}], "Dudes: Yehuda (http://yehuda) Alan (http://alan) ",
|
536
|
-
"Function partials output based in VM.");
|
537
|
-
});
|
538
|
-
|
539
|
-
test("GH-14: a partial preceding a selector", function() {
|
540
|
-
var string = "Dudes: {{>dude}} {{another_dude}}";
|
541
|
-
var dude = "{{name}}";
|
542
|
-
var hash = {name:"Jeepers", another_dude:"Creepers"};
|
543
|
-
shouldCompileToWithPartials(string, [hash, {}, {dude:dude}], true, "Dudes: Jeepers Creepers", "Regular selectors can follow a partial");
|
544
|
-
});
|
545
|
-
|
546
|
-
test("Partials with slash paths", function() {
|
547
|
-
var string = "Dudes: {{> shared/dude}}";
|
548
|
-
var dude = "{{name}}";
|
549
|
-
var hash = {name:"Jeepers", another_dude:"Creepers"};
|
550
|
-
shouldCompileToWithPartials(string, [hash, {}, {'shared/dude':dude}], true, "Dudes: Jeepers", "Partials can use literal paths");
|
551
|
-
});
|
552
|
-
|
553
|
-
test("Partials with integer path", function() {
|
554
|
-
var string = "Dudes: {{> 404}}";
|
555
|
-
var dude = "{{name}}";
|
556
|
-
var hash = {name:"Jeepers", another_dude:"Creepers"};
|
557
|
-
shouldCompileToWithPartials(string, [hash, {}, {404:dude}], true, "Dudes: Jeepers", "Partials can use literal paths");
|
558
|
-
});
|
559
|
-
|
560
|
-
|
561
|
-
suite("String literal parameters");
|
562
|
-
|
563
|
-
test("simple literals work", function() {
|
564
|
-
var string = 'Message: {{hello "world" 12 true false}}';
|
565
|
-
var hash = {};
|
566
|
-
var helpers = {hello: function(param, times, bool1, bool2) {
|
567
|
-
if(typeof times !== 'number') { times = "NaN"; }
|
568
|
-
if(typeof bool1 !== 'boolean') { bool1 = "NaB"; }
|
569
|
-
if(typeof bool2 !== 'boolean') { bool2 = "NaB"; }
|
570
|
-
return "Hello " + param + " " + times + " times: " + bool1 + " " + bool2;
|
571
|
-
}};
|
572
|
-
shouldCompileTo(string, [hash, helpers], "Message: Hello world 12 times: true false", "template with a simple String literal");
|
573
|
-
});
|
574
|
-
|
575
|
-
test("using a quote in the middle of a parameter raises an error", function() {
|
576
|
-
shouldThrow(function() {
|
577
|
-
var string = 'Message: {{hello wo"rld"}}';
|
578
|
-
CompilerContext.compile(string);
|
579
|
-
}, Error, "should throw exception");
|
580
|
-
});
|
581
|
-
|
582
|
-
test("escaping a String is possible", function(){
|
583
|
-
var string = 'Message: {{{hello "\\"world\\""}}}';
|
584
|
-
var hash = {};
|
585
|
-
var helpers = {hello: function(param) { return "Hello " + param; }};
|
586
|
-
shouldCompileTo(string, [hash, helpers], "Message: Hello \"world\"", "template with an escaped String literal");
|
587
|
-
});
|
588
|
-
|
589
|
-
test("it works with ' marks", function() {
|
590
|
-
var string = 'Message: {{{hello "Alan\'s world"}}}';
|
591
|
-
var hash = {};
|
592
|
-
var helpers = {hello: function(param) { return "Hello " + param; }};
|
593
|
-
shouldCompileTo(string, [hash, helpers], "Message: Hello Alan's world", "template with a ' mark");
|
594
|
-
});
|
595
|
-
|
596
|
-
suite("multiple parameters");
|
597
|
-
|
598
|
-
test("simple multi-params work", function() {
|
599
|
-
var string = 'Message: {{goodbye cruel world}}';
|
600
|
-
var hash = {cruel: "cruel", world: "world"};
|
601
|
-
var helpers = {goodbye: function(cruel, world) { return "Goodbye " + cruel + " " + world; }};
|
602
|
-
shouldCompileTo(string, [hash, helpers], "Message: Goodbye cruel world", "regular helpers with multiple params");
|
603
|
-
});
|
604
|
-
|
605
|
-
test("block multi-params work", function() {
|
606
|
-
var string = 'Message: {{#goodbye cruel world}}{{greeting}} {{adj}} {{noun}}{{/goodbye}}';
|
607
|
-
var hash = {cruel: "cruel", world: "world"};
|
608
|
-
var helpers = {goodbye: function(cruel, world, options) {
|
609
|
-
return options.fn({greeting: "Goodbye", adj: cruel, noun: world});
|
610
|
-
}};
|
611
|
-
shouldCompileTo(string, [hash, helpers], "Message: Goodbye cruel world", "block helpers with multiple params");
|
612
|
-
});
|
613
|
-
|
614
|
-
suite("safestring");
|
615
|
-
|
616
|
-
test("constructing a safestring from a string and checking its type", function() {
|
617
|
-
var safe = new Handlebars.SafeString("testing 1, 2, 3");
|
618
|
-
ok(safe instanceof Handlebars.SafeString, "SafeString is an instance of Handlebars.SafeString");
|
619
|
-
equal(safe, "testing 1, 2, 3", "SafeString is equivalent to its underlying string");
|
620
|
-
});
|
621
|
-
|
622
|
-
suite("helperMissing");
|
623
|
-
|
624
|
-
test("if a context is not found, helperMissing is used", function() {
|
625
|
-
shouldThrow(function() {
|
626
|
-
var template = CompilerContext.compile("{{hello}} {{link_to world}}");
|
627
|
-
template({});
|
628
|
-
}, [Error, "Could not find property 'link_to'"], "Should throw exception");
|
629
|
-
});
|
630
|
-
|
631
|
-
test("if a context is not found, custom helperMissing is used", function() {
|
632
|
-
var string = "{{hello}} {{link_to world}}";
|
633
|
-
var context = { hello: "Hello", world: "world" };
|
634
|
-
|
635
|
-
var helpers = {
|
636
|
-
helperMissing: function(helper, context) {
|
637
|
-
if(helper === "link_to") {
|
638
|
-
return new Handlebars.SafeString("<a>" + context + "</a>");
|
639
|
-
}
|
640
|
-
}
|
641
|
-
};
|
642
|
-
|
643
|
-
shouldCompileTo(string, [context, helpers], "Hello <a>world</a>");
|
644
|
-
});
|
645
|
-
|
646
|
-
suite("knownHelpers");
|
647
|
-
|
648
|
-
test("Known helper should render helper", function() {
|
649
|
-
var template = CompilerContext.compile("{{hello}}", {knownHelpers: {"hello" : true}});
|
650
|
-
|
651
|
-
var result = template({}, {helpers: {hello: function() { return "foo"; }}});
|
652
|
-
equal(result, "foo", "'foo' should === '" + result);
|
653
|
-
});
|
654
|
-
|
655
|
-
test("Unknown helper in knownHelpers only mode should be passed as undefined", function() {
|
656
|
-
var template = CompilerContext.compile("{{typeof hello}}", {knownHelpers: {'typeof': true}, knownHelpersOnly: true});
|
657
|
-
|
658
|
-
var result = template({}, {helpers: {'typeof': function(arg) { return typeof arg; }, hello: function() { return "foo"; }}});
|
659
|
-
equal(result, "undefined", "'undefined' should === '" + result);
|
660
|
-
});
|
661
|
-
test("Builtin helpers available in knownHelpers only mode", function() {
|
662
|
-
var template = CompilerContext.compile("{{#unless foo}}bar{{/unless}}", {knownHelpersOnly: true});
|
663
|
-
|
664
|
-
var result = template({});
|
665
|
-
equal(result, "bar", "'bar' should === '" + result);
|
666
|
-
});
|
667
|
-
test("Field lookup works in knownHelpers only mode", function() {
|
668
|
-
var template = CompilerContext.compile("{{foo}}", {knownHelpersOnly: true});
|
669
|
-
|
670
|
-
var result = template({foo: 'bar'});
|
671
|
-
equal(result, "bar", "'bar' should === '" + result);
|
672
|
-
});
|
673
|
-
test("Conditional blocks work in knownHelpers only mode", function() {
|
674
|
-
var template = CompilerContext.compile("{{#foo}}bar{{/foo}}", {knownHelpersOnly: true});
|
675
|
-
|
676
|
-
var result = template({foo: 'baz'});
|
677
|
-
equal(result, "bar", "'bar' should === '" + result);
|
678
|
-
});
|
679
|
-
test("Invert blocks work in knownHelpers only mode", function() {
|
680
|
-
var template = CompilerContext.compile("{{^foo}}bar{{/foo}}", {knownHelpersOnly: true});
|
681
|
-
|
682
|
-
var result = template({foo: false});
|
683
|
-
equal(result, "bar", "'bar' should === '" + result);
|
684
|
-
});
|
685
|
-
test("Functions are bound to the context in knownHelpers only mode", function() {
|
686
|
-
var template = CompilerContext.compile("{{foo}}", {knownHelpersOnly: true});
|
687
|
-
var result = template({foo: function() { return this.bar; }, bar: 'bar'});
|
688
|
-
equal(result, "bar", "'bar' should === '" + result);
|
689
|
-
});
|
690
|
-
|
691
|
-
suite("blockHelperMissing");
|
692
|
-
|
693
|
-
test("lambdas are resolved by blockHelperMissing, not handlebars proper", function() {
|
694
|
-
var string = "{{#truthy}}yep{{/truthy}}";
|
695
|
-
var data = { truthy: function() { return true; } };
|
696
|
-
shouldCompileTo(string, data, "yep");
|
697
|
-
});
|
698
|
-
test("lambdas resolved by blockHelperMissing are bound to the context", function() {
|
699
|
-
var string = "{{#truthy}}yep{{/truthy}}";
|
700
|
-
var boundData = { truthy: function() { return this.truthiness(); }, truthiness: function() { return false; } };
|
701
|
-
shouldCompileTo(string, boundData, "");
|
702
|
-
});
|
703
|
-
|
704
|
-
var teardown;
|
705
|
-
suite("built-in helpers", {
|
706
|
-
setup: function(){ teardown = null; },
|
707
|
-
teardown: function(){ if (teardown) { teardown(); } }
|
708
|
-
});
|
709
|
-
|
710
|
-
test("with", function() {
|
711
|
-
var string = "{{#with person}}{{first}} {{last}}{{/with}}";
|
712
|
-
shouldCompileTo(string, {person: {first: "Alan", last: "Johnson"}}, "Alan Johnson");
|
713
|
-
});
|
714
|
-
|
715
|
-
test("if", function() {
|
716
|
-
var string = "{{#if goodbye}}GOODBYE {{/if}}cruel {{world}}!";
|
717
|
-
shouldCompileTo(string, {goodbye: true, world: "world"}, "GOODBYE cruel world!",
|
718
|
-
"if with boolean argument shows the contents when true");
|
719
|
-
shouldCompileTo(string, {goodbye: "dummy", world: "world"}, "GOODBYE cruel world!",
|
720
|
-
"if with string argument shows the contents");
|
721
|
-
shouldCompileTo(string, {goodbye: false, world: "world"}, "cruel world!",
|
722
|
-
"if with boolean argument does not show the contents when false");
|
723
|
-
shouldCompileTo(string, {world: "world"}, "cruel world!",
|
724
|
-
"if with undefined does not show the contents");
|
725
|
-
shouldCompileTo(string, {goodbye: ['foo'], world: "world"}, "GOODBYE cruel world!",
|
726
|
-
"if with non-empty array shows the contents");
|
727
|
-
shouldCompileTo(string, {goodbye: [], world: "world"}, "cruel world!",
|
728
|
-
"if with empty array does not show the contents");
|
729
|
-
});
|
730
|
-
|
731
|
-
test("if with function argument", function() {
|
732
|
-
var string = "{{#if goodbye}}GOODBYE {{/if}}cruel {{world}}!";
|
733
|
-
shouldCompileTo(string, {goodbye: function() {return true;}, world: "world"}, "GOODBYE cruel world!",
|
734
|
-
"if with function shows the contents when function returns true");
|
735
|
-
shouldCompileTo(string, {goodbye: function() {return this.world;}, world: "world"}, "GOODBYE cruel world!",
|
736
|
-
"if with function shows the contents when function returns string");
|
737
|
-
shouldCompileTo(string, {goodbye: function() {return false;}, world: "world"}, "cruel world!",
|
738
|
-
"if with function does not show the contents when returns false");
|
739
|
-
shouldCompileTo(string, {goodbye: function() {return this.foo;}, world: "world"}, "cruel world!",
|
740
|
-
"if with function does not show the contents when returns undefined");
|
741
|
-
});
|
742
|
-
|
743
|
-
test("each", function() {
|
744
|
-
var string = "{{#each goodbyes}}{{text}}! {{/each}}cruel {{world}}!";
|
745
|
-
var hash = {goodbyes: [{text: "goodbye"}, {text: "Goodbye"}, {text: "GOODBYE"}], world: "world"};
|
746
|
-
shouldCompileTo(string, hash, "goodbye! Goodbye! GOODBYE! cruel world!",
|
747
|
-
"each with array argument iterates over the contents when not empty");
|
748
|
-
shouldCompileTo(string, {goodbyes: [], world: "world"}, "cruel world!",
|
749
|
-
"each with array argument ignores the contents when empty");
|
750
|
-
});
|
751
|
-
|
752
|
-
test("each with an object and @key", function() {
|
753
|
-
var string = "{{#each goodbyes}}{{@key}}. {{text}}! {{/each}}cruel {{world}}!";
|
754
|
-
var hash = {goodbyes: {"<b>#1</b>": {text: "goodbye"}, 2: {text: "GOODBYE"}}, world: "world"};
|
755
|
-
|
756
|
-
// Object property iteration order is undefined according to ECMA spec,
|
757
|
-
// so we need to check both possible orders
|
758
|
-
// @see http://stackoverflow.com/questions/280713/elements-order-in-a-for-in-loop
|
759
|
-
var actual = compileWithPartials(string, hash);
|
760
|
-
var expected1 = "<b>#1</b>. goodbye! 2. GOODBYE! cruel world!";
|
761
|
-
var expected2 = "2. GOODBYE! <b>#1</b>. goodbye! cruel world!";
|
762
|
-
|
763
|
-
ok(actual === expected1 || actual === expected2, "each with object argument iterates over the contents when not empty");
|
764
|
-
shouldCompileTo(string, {goodbyes: [], world: "world"}, "cruel world!",
|
765
|
-
"each with object argument ignores the contents when empty");
|
766
|
-
});
|
767
|
-
|
768
|
-
test("each with @index", function() {
|
769
|
-
var string = "{{#each goodbyes}}{{@index}}. {{text}}! {{/each}}cruel {{world}}!";
|
770
|
-
var hash = {goodbyes: [{text: "goodbye"}, {text: "Goodbye"}, {text: "GOODBYE"}], world: "world"};
|
771
|
-
|
772
|
-
var template = CompilerContext.compile(string);
|
773
|
-
var result = template(hash);
|
774
|
-
|
775
|
-
equal(result, "0. goodbye! 1. Goodbye! 2. GOODBYE! cruel world!", "The @index variable is used");
|
776
|
-
});
|
777
|
-
|
778
|
-
test("data passed to helpers", function() {
|
779
|
-
var string = "{{#each letters}}{{this}}{{detectDataInsideEach}}{{/each}}";
|
780
|
-
var hash = {letters: ['a', 'b', 'c']};
|
781
|
-
|
782
|
-
var template = CompilerContext.compile(string);
|
783
|
-
var result = template(hash, {
|
784
|
-
data: {
|
785
|
-
exclaim: '!'
|
786
|
-
}
|
787
|
-
});
|
788
|
-
equal(result, 'a!b!c!', 'should output data');
|
789
|
-
});
|
790
|
-
|
791
|
-
Handlebars.registerHelper('detectDataInsideEach', function(options) {
|
792
|
-
return options.data && options.data.exclaim;
|
793
|
-
});
|
794
|
-
|
795
|
-
test("log", function() {
|
796
|
-
var string = "{{log blah}}";
|
797
|
-
var hash = { blah: "whee" };
|
798
|
-
|
799
|
-
var levelArg, logArg;
|
800
|
-
var originalLog = Handlebars.log;
|
801
|
-
Handlebars.log = function(level, arg){ levelArg = level, logArg = arg; };
|
802
|
-
teardown = function(){ Handlebars.log = originalLog; };
|
803
|
-
|
804
|
-
shouldCompileTo(string, hash, "", "log should not display");
|
805
|
-
equals(1, levelArg, "should call log with 1");
|
806
|
-
equals("whee", logArg, "should call log with 'whee'");
|
807
|
-
});
|
808
|
-
|
809
|
-
test("overriding property lookup", function() {
|
810
|
-
|
811
|
-
});
|
812
|
-
|
813
|
-
|
814
|
-
test("passing in data to a compiled function that expects data - works with helpers", function() {
|
815
|
-
var template = CompilerContext.compile("{{hello}}", {data: true});
|
816
|
-
|
817
|
-
var helpers = {
|
818
|
-
hello: function(options) {
|
819
|
-
return options.data.adjective + " " + this.noun;
|
820
|
-
}
|
821
|
-
};
|
822
|
-
|
823
|
-
var result = template({noun: "cat"}, {helpers: helpers, data: {adjective: "happy"}});
|
824
|
-
equals("happy cat", result, "Data output by helper");
|
825
|
-
});
|
826
|
-
|
827
|
-
test("data can be looked up via @foo", function() {
|
828
|
-
var template = CompilerContext.compile("{{@hello}}");
|
829
|
-
var result = template({}, { data: { hello: "hello" } });
|
830
|
-
equals("hello", result, "@foo retrieves template data");
|
831
|
-
});
|
832
|
-
|
833
|
-
var objectCreate = Handlebars.createFrame;
|
834
|
-
|
835
|
-
test("deep @foo triggers automatic top-level data", function() {
|
836
|
-
var template = CompilerContext.compile('{{#let world="world"}}{{#if foo}}{{#if foo}}Hello {{@world}}{{/if}}{{/if}}{{/let}}');
|
837
|
-
|
838
|
-
var helpers = objectCreate(Handlebars.helpers);
|
839
|
-
|
840
|
-
helpers.let = function(options) {
|
841
|
-
var frame = Handlebars.createFrame(options.data);
|
842
|
-
|
843
|
-
for (var prop in options.hash) {
|
844
|
-
frame[prop] = options.hash[prop];
|
845
|
-
}
|
846
|
-
return options.fn(this, { data: frame });
|
847
|
-
};
|
848
|
-
|
849
|
-
var result = template({ foo: true }, { helpers: helpers });
|
850
|
-
equals("Hello world", result, "Automatic data was triggered");
|
851
|
-
});
|
852
|
-
|
853
|
-
test("parameter data can be looked up via @foo", function() {
|
854
|
-
var template = CompilerContext.compile("{{hello @world}}");
|
855
|
-
var helpers = {
|
856
|
-
hello: function(noun) {
|
857
|
-
return "Hello " + noun;
|
858
|
-
}
|
859
|
-
};
|
860
|
-
|
861
|
-
var result = template({}, { helpers: helpers, data: { world: "world" } });
|
862
|
-
equals("Hello world", result, "@foo as a parameter retrieves template data");
|
863
|
-
});
|
864
|
-
|
865
|
-
test("hash values can be looked up via @foo", function() {
|
866
|
-
var template = CompilerContext.compile("{{hello noun=@world}}");
|
867
|
-
var helpers = {
|
868
|
-
hello: function(options) {
|
869
|
-
return "Hello " + options.hash.noun;
|
870
|
-
}
|
871
|
-
};
|
872
|
-
|
873
|
-
var result = template({}, { helpers: helpers, data: { world: "world" } });
|
874
|
-
equals("Hello world", result, "@foo as a parameter retrieves template data");
|
875
|
-
});
|
876
|
-
|
877
|
-
test("data is inherited downstream", function() {
|
878
|
-
var template = CompilerContext.compile("{{#let foo=bar.baz}}{{@foo}}{{/let}}", { data: true });
|
879
|
-
var helpers = {
|
880
|
-
let: function(options) {
|
881
|
-
for (var prop in options.hash) {
|
882
|
-
options.data[prop] = options.hash[prop];
|
883
|
-
}
|
884
|
-
return options.fn(this);
|
885
|
-
}
|
886
|
-
};
|
887
|
-
|
888
|
-
var result = template({ bar: { baz: "hello world" } }, { helpers: helpers, data: {} });
|
889
|
-
equals("hello world", result, "data variables are inherited downstream");
|
890
|
-
});
|
891
|
-
|
892
|
-
test("passing in data to a compiled function that expects data - works with helpers in partials", function() {
|
893
|
-
var template = CompilerContext.compile("{{>my_partial}}", {data: true});
|
894
|
-
|
895
|
-
var partials = {
|
896
|
-
my_partial: CompilerContext.compile("{{hello}}", {data: true})
|
897
|
-
};
|
898
|
-
|
899
|
-
var helpers = {
|
900
|
-
hello: function(options) {
|
901
|
-
return options.data.adjective + " " + this.noun;
|
902
|
-
}
|
903
|
-
};
|
904
|
-
|
905
|
-
var result = template({noun: "cat"}, {helpers: helpers, partials: partials, data: {adjective: "happy"}});
|
906
|
-
equals("happy cat", result, "Data output by helper inside partial");
|
907
|
-
});
|
908
|
-
|
909
|
-
test("passing in data to a compiled function that expects data - works with helpers and parameters", function() {
|
910
|
-
var template = CompilerContext.compile("{{hello world}}", {data: true});
|
911
|
-
|
912
|
-
var helpers = {
|
913
|
-
hello: function(noun, options) {
|
914
|
-
return options.data.adjective + " " + noun + (this.exclaim ? "!" : "");
|
915
|
-
}
|
916
|
-
};
|
917
|
-
|
918
|
-
var result = template({exclaim: true, world: "world"}, {helpers: helpers, data: {adjective: "happy"}});
|
919
|
-
equals("happy world!", result, "Data output by helper");
|
920
|
-
});
|
921
|
-
|
922
|
-
test("passing in data to a compiled function that expects data - works with block helpers", function() {
|
923
|
-
var template = CompilerContext.compile("{{#hello}}{{world}}{{/hello}}", {data: true});
|
924
|
-
|
925
|
-
var helpers = {
|
926
|
-
hello: function(options) {
|
927
|
-
return options.fn(this);
|
928
|
-
},
|
929
|
-
world: function(options) {
|
930
|
-
return options.data.adjective + " world" + (this.exclaim ? "!" : "");
|
931
|
-
}
|
932
|
-
};
|
933
|
-
|
934
|
-
var result = template({exclaim: true}, {helpers: helpers, data: {adjective: "happy"}});
|
935
|
-
equals("happy world!", result, "Data output by helper");
|
936
|
-
});
|
937
|
-
|
938
|
-
test("passing in data to a compiled function that expects data - works with block helpers that use ..", function() {
|
939
|
-
var template = CompilerContext.compile("{{#hello}}{{world ../zomg}}{{/hello}}", {data: true});
|
940
|
-
|
941
|
-
var helpers = {
|
942
|
-
hello: function(options) {
|
943
|
-
return options.fn({exclaim: "?"});
|
944
|
-
},
|
945
|
-
world: function(thing, options) {
|
946
|
-
return options.data.adjective + " " + thing + (this.exclaim || "");
|
947
|
-
}
|
948
|
-
};
|
949
|
-
|
950
|
-
var result = template({exclaim: true, zomg: "world"}, {helpers: helpers, data: {adjective: "happy"}});
|
951
|
-
equals("happy world?", result, "Data output by helper");
|
952
|
-
});
|
953
|
-
|
954
|
-
test("passing in data to a compiled function that expects data - data is passed to with block helpers where children use ..", function() {
|
955
|
-
var template = CompilerContext.compile("{{#hello}}{{world ../zomg}}{{/hello}}", {data: true});
|
956
|
-
|
957
|
-
var helpers = {
|
958
|
-
hello: function(options) {
|
959
|
-
return options.data.accessData + " " + options.fn({exclaim: "?"});
|
960
|
-
},
|
961
|
-
world: function(thing, options) {
|
962
|
-
return options.data.adjective + " " + thing + (this.exclaim || "");
|
963
|
-
}
|
964
|
-
};
|
965
|
-
|
966
|
-
var result = template({exclaim: true, zomg: "world"}, {helpers: helpers, data: {adjective: "happy", accessData: "#win"}});
|
967
|
-
equals("#win happy world?", result, "Data output by helper");
|
968
|
-
});
|
969
|
-
|
970
|
-
test("you can override inherited data when invoking a helper", function() {
|
971
|
-
var template = CompilerContext.compile("{{#hello}}{{world zomg}}{{/hello}}", {data: true});
|
972
|
-
|
973
|
-
var helpers = {
|
974
|
-
hello: function(options) {
|
975
|
-
return options.fn({exclaim: "?", zomg: "world"}, { data: {adjective: "sad"} });
|
976
|
-
},
|
977
|
-
world: function(thing, options) {
|
978
|
-
return options.data.adjective + " " + thing + (this.exclaim || "");
|
979
|
-
}
|
980
|
-
};
|
981
|
-
|
982
|
-
var result = template({exclaim: true, zomg: "planet"}, {helpers: helpers, data: {adjective: "happy"}});
|
983
|
-
equals("sad world?", result, "Overriden data output by helper");
|
984
|
-
});
|
985
|
-
|
986
|
-
|
987
|
-
test("you can override inherited data when invoking a helper with depth", function() {
|
988
|
-
var template = CompilerContext.compile("{{#hello}}{{world ../zomg}}{{/hello}}", {data: true});
|
989
|
-
|
990
|
-
var helpers = {
|
991
|
-
hello: function(options) {
|
992
|
-
return options.fn({exclaim: "?"}, { data: {adjective: "sad"} });
|
993
|
-
},
|
994
|
-
world: function(thing, options) {
|
995
|
-
return options.data.adjective + " " + thing + (this.exclaim || "");
|
996
|
-
}
|
997
|
-
};
|
998
|
-
|
999
|
-
var result = template({exclaim: true, zomg: "world"}, {helpers: helpers, data: {adjective: "happy"}});
|
1000
|
-
equals("sad world?", result, "Overriden data output by helper");
|
1001
|
-
});
|
1002
|
-
|
1003
|
-
test("helpers take precedence over same-named context properties", function() {
|
1004
|
-
var template = CompilerContext.compile("{{goodbye}} {{cruel world}}");
|
1005
|
-
|
1006
|
-
var helpers = {
|
1007
|
-
goodbye: function() {
|
1008
|
-
return this.goodbye.toUpperCase();
|
1009
|
-
},
|
1010
|
-
|
1011
|
-
cruel: function(world) {
|
1012
|
-
return "cruel " + world.toUpperCase();
|
1013
|
-
}
|
1014
|
-
};
|
1015
|
-
|
1016
|
-
var context = {
|
1017
|
-
goodbye: "goodbye",
|
1018
|
-
world: "world"
|
1019
|
-
};
|
1020
|
-
|
1021
|
-
var result = template(context, {helpers: helpers});
|
1022
|
-
equals(result, "GOODBYE cruel WORLD", "Helper executed");
|
1023
|
-
});
|
1024
|
-
|
1025
|
-
test("helpers take precedence over same-named context properties$", function() {
|
1026
|
-
var template = CompilerContext.compile("{{#goodbye}} {{cruel world}}{{/goodbye}}");
|
1027
|
-
|
1028
|
-
var helpers = {
|
1029
|
-
goodbye: function(options) {
|
1030
|
-
return this.goodbye.toUpperCase() + options.fn(this);
|
1031
|
-
},
|
1032
|
-
|
1033
|
-
cruel: function(world) {
|
1034
|
-
return "cruel " + world.toUpperCase();
|
1035
|
-
}
|
1036
|
-
};
|
1037
|
-
|
1038
|
-
var context = {
|
1039
|
-
goodbye: "goodbye",
|
1040
|
-
world: "world"
|
1041
|
-
};
|
1042
|
-
|
1043
|
-
var result = template(context, {helpers: helpers});
|
1044
|
-
equals(result, "GOODBYE cruel WORLD", "Helper executed");
|
1045
|
-
});
|
1046
|
-
|
1047
|
-
test("Scoped names take precedence over helpers", function() {
|
1048
|
-
var template = CompilerContext.compile("{{this.goodbye}} {{cruel world}} {{cruel this.goodbye}}");
|
1049
|
-
|
1050
|
-
var helpers = {
|
1051
|
-
goodbye: function() {
|
1052
|
-
return this.goodbye.toUpperCase();
|
1053
|
-
},
|
1054
|
-
|
1055
|
-
cruel: function(world) {
|
1056
|
-
return "cruel " + world.toUpperCase();
|
1057
|
-
},
|
1058
|
-
};
|
1059
|
-
|
1060
|
-
var context = {
|
1061
|
-
goodbye: "goodbye",
|
1062
|
-
world: "world"
|
1063
|
-
};
|
1064
|
-
|
1065
|
-
var result = template(context, {helpers: helpers});
|
1066
|
-
equals(result, "goodbye cruel WORLD cruel GOODBYE", "Helper not executed");
|
1067
|
-
});
|
1068
|
-
|
1069
|
-
test("Scoped names take precedence over block helpers", function() {
|
1070
|
-
var template = CompilerContext.compile("{{#goodbye}} {{cruel world}}{{/goodbye}} {{this.goodbye}}");
|
1071
|
-
|
1072
|
-
var helpers = {
|
1073
|
-
goodbye: function(options) {
|
1074
|
-
return this.goodbye.toUpperCase() + options.fn(this);
|
1075
|
-
},
|
1076
|
-
|
1077
|
-
cruel: function(world) {
|
1078
|
-
return "cruel " + world.toUpperCase();
|
1079
|
-
},
|
1080
|
-
};
|
1081
|
-
|
1082
|
-
var context = {
|
1083
|
-
goodbye: "goodbye",
|
1084
|
-
world: "world"
|
1085
|
-
};
|
1086
|
-
|
1087
|
-
var result = template(context, {helpers: helpers});
|
1088
|
-
equals(result, "GOODBYE cruel WORLD goodbye", "Helper executed");
|
1089
|
-
});
|
1090
|
-
|
1091
|
-
test("helpers can take an optional hash", function() {
|
1092
|
-
var template = CompilerContext.compile('{{goodbye cruel="CRUEL" world="WORLD" times=12}}');
|
1093
|
-
|
1094
|
-
var helpers = {
|
1095
|
-
goodbye: function(options) {
|
1096
|
-
return "GOODBYE " + options.hash.cruel + " " + options.hash.world + " " + options.hash.times + " TIMES";
|
1097
|
-
}
|
1098
|
-
};
|
1099
|
-
|
1100
|
-
var context = {};
|
1101
|
-
|
1102
|
-
var result = template(context, {helpers: helpers});
|
1103
|
-
equals(result, "GOODBYE CRUEL WORLD 12 TIMES", "Helper output hash");
|
1104
|
-
});
|
1105
|
-
|
1106
|
-
test("helpers can take an optional hash with booleans", function() {
|
1107
|
-
var helpers = {
|
1108
|
-
goodbye: function(options) {
|
1109
|
-
if (options.hash.print === true) {
|
1110
|
-
return "GOODBYE " + options.hash.cruel + " " + options.hash.world;
|
1111
|
-
} else if (options.hash.print === false) {
|
1112
|
-
return "NOT PRINTING";
|
1113
|
-
} else {
|
1114
|
-
return "THIS SHOULD NOT HAPPEN";
|
1115
|
-
}
|
1116
|
-
}
|
1117
|
-
};
|
1118
|
-
|
1119
|
-
var context = {};
|
1120
|
-
|
1121
|
-
var template = CompilerContext.compile('{{goodbye cruel="CRUEL" world="WORLD" print=true}}');
|
1122
|
-
var result = template(context, {helpers: helpers});
|
1123
|
-
equals(result, "GOODBYE CRUEL WORLD", "Helper output hash");
|
1124
|
-
|
1125
|
-
template = CompilerContext.compile('{{goodbye cruel="CRUEL" world="WORLD" print=false}}');
|
1126
|
-
result = template(context, {helpers: helpers});
|
1127
|
-
equals(result, "NOT PRINTING", "Boolean helper parameter honored");
|
1128
|
-
});
|
1129
|
-
|
1130
|
-
test("block helpers can take an optional hash", function() {
|
1131
|
-
var template = CompilerContext.compile('{{#goodbye cruel="CRUEL" times=12}}world{{/goodbye}}');
|
1132
|
-
|
1133
|
-
var helpers = {
|
1134
|
-
goodbye: function(options) {
|
1135
|
-
return "GOODBYE " + options.hash.cruel + " " + options.fn(this) + " " + options.hash.times + " TIMES";
|
1136
|
-
}
|
1137
|
-
};
|
1138
|
-
|
1139
|
-
var result = template({}, {helpers: helpers});
|
1140
|
-
equals(result, "GOODBYE CRUEL world 12 TIMES", "Hash parameters output");
|
1141
|
-
});
|
1142
|
-
|
1143
|
-
test("block helpers can take an optional hash with single quoted stings", function() {
|
1144
|
-
var template = CompilerContext.compile("{{#goodbye cruel='CRUEL' times=12}}world{{/goodbye}}");
|
1145
|
-
|
1146
|
-
var helpers = {
|
1147
|
-
goodbye: function(options) {
|
1148
|
-
return "GOODBYE " + options.hash.cruel + " " + options.fn(this) + " " + options.hash.times + " TIMES";
|
1149
|
-
}
|
1150
|
-
};
|
1151
|
-
|
1152
|
-
var result = template({}, {helpers: helpers});
|
1153
|
-
equals(result, "GOODBYE CRUEL world 12 TIMES", "Hash parameters output");
|
1154
|
-
});
|
1155
|
-
|
1156
|
-
test("block helpers can take an optional hash with booleans", function() {
|
1157
|
-
var helpers = {
|
1158
|
-
goodbye: function(options) {
|
1159
|
-
if (options.hash.print === true) {
|
1160
|
-
return "GOODBYE " + options.hash.cruel + " " + options.fn(this);
|
1161
|
-
} else if (options.hash.print === false) {
|
1162
|
-
return "NOT PRINTING";
|
1163
|
-
} else {
|
1164
|
-
return "THIS SHOULD NOT HAPPEN";
|
1165
|
-
}
|
1166
|
-
}
|
1167
|
-
};
|
1168
|
-
|
1169
|
-
var template = CompilerContext.compile('{{#goodbye cruel="CRUEL" print=true}}world{{/goodbye}}');
|
1170
|
-
var result = template({}, {helpers: helpers});
|
1171
|
-
equals(result, "GOODBYE CRUEL world", "Boolean hash parameter honored");
|
1172
|
-
|
1173
|
-
template = CompilerContext.compile('{{#goodbye cruel="CRUEL" print=false}}world{{/goodbye}}');
|
1174
|
-
result = template({}, {helpers: helpers});
|
1175
|
-
equals(result, "NOT PRINTING", "Boolean hash parameter honored");
|
1176
|
-
});
|
1177
|
-
|
1178
|
-
|
1179
|
-
test("arguments to helpers can be retrieved from options hash in string form", function() {
|
1180
|
-
var template = CompilerContext.compile('{{wycats is.a slave.driver}}', {stringParams: true});
|
1181
|
-
|
1182
|
-
var helpers = {
|
1183
|
-
wycats: function(passiveVoice, noun) {
|
1184
|
-
return "HELP ME MY BOSS " + passiveVoice + ' ' + noun;
|
1185
|
-
}
|
1186
|
-
};
|
1187
|
-
|
1188
|
-
var result = template({}, {helpers: helpers});
|
1189
|
-
|
1190
|
-
equals(result, "HELP ME MY BOSS is.a slave.driver", "String parameters output");
|
1191
|
-
});
|
1192
|
-
|
1193
|
-
test("when using block form, arguments to helpers can be retrieved from options hash in string form", function() {
|
1194
|
-
var template = CompilerContext.compile('{{#wycats is.a slave.driver}}help :({{/wycats}}', {stringParams: true});
|
1195
|
-
|
1196
|
-
var helpers = {
|
1197
|
-
wycats: function(passiveVoice, noun, options) {
|
1198
|
-
return "HELP ME MY BOSS " + passiveVoice + ' ' +
|
1199
|
-
noun + ': ' + options.fn(this);
|
1200
|
-
}
|
1201
|
-
};
|
1202
|
-
|
1203
|
-
var result = template({}, {helpers: helpers});
|
1204
|
-
|
1205
|
-
equals(result, "HELP ME MY BOSS is.a slave.driver: help :(", "String parameters output");
|
1206
|
-
});
|
1207
|
-
|
1208
|
-
test("when inside a block in String mode, .. passes the appropriate context in the options hash", function() {
|
1209
|
-
var template = CompilerContext.compile('{{#with dale}}{{tomdale ../need dad.joke}}{{/with}}', {stringParams: true});
|
1210
|
-
|
1211
|
-
var helpers = {
|
1212
|
-
tomdale: function(desire, noun, options) {
|
1213
|
-
return "STOP ME FROM READING HACKER NEWS I " +
|
1214
|
-
options.contexts[0][desire] + " " + noun;
|
1215
|
-
},
|
1216
|
-
|
1217
|
-
"with": function(context, options) {
|
1218
|
-
return options.fn(options.contexts[0][context]);
|
1219
|
-
}
|
1220
|
-
};
|
1221
|
-
|
1222
|
-
var result = template({
|
1223
|
-
dale: {},
|
1224
|
-
|
1225
|
-
need: 'need-a'
|
1226
|
-
}, {helpers: helpers});
|
1227
|
-
|
1228
|
-
equals(result, "STOP ME FROM READING HACKER NEWS I need-a dad.joke", "Proper context variable output");
|
1229
|
-
});
|
1230
|
-
|
1231
|
-
test("in string mode, information about the types is passed along", function() {
|
1232
|
-
var template = CompilerContext.compile('{{tomdale "need" dad.joke true false}}', { stringParams: true });
|
1233
|
-
|
1234
|
-
var helpers = {
|
1235
|
-
tomdale: function(desire, noun, trueBool, falseBool, options) {
|
1236
|
-
equal(options.types[0], 'STRING', "the string type is passed");
|
1237
|
-
equal(options.types[1], 'ID', "the expression type is passed");
|
1238
|
-
equal(options.types[2], 'BOOLEAN', "the expression type is passed");
|
1239
|
-
equal(desire, "need", "the string form is passed for strings");
|
1240
|
-
equal(noun, "dad.joke", "the string form is passed for expressions");
|
1241
|
-
equal(trueBool, true, "raw booleans are passed through");
|
1242
|
-
equal(falseBool, false, "raw booleans are passed through");
|
1243
|
-
return "Helper called";
|
1244
|
-
}
|
1245
|
-
};
|
1246
|
-
|
1247
|
-
var result = template({}, { helpers: helpers });
|
1248
|
-
equal(result, "Helper called");
|
1249
|
-
});
|
1250
|
-
|
1251
|
-
test("in string mode, hash parameters get type information", function() {
|
1252
|
-
var template = CompilerContext.compile('{{tomdale he.says desire="need" noun=dad.joke bool=true}}', { stringParams: true });
|
1253
|
-
|
1254
|
-
var helpers = {
|
1255
|
-
tomdale: function(exclamation, options) {
|
1256
|
-
equal(exclamation, "he.says");
|
1257
|
-
equal(options.types[0], "ID");
|
1258
|
-
|
1259
|
-
equal(options.hashTypes.desire, "STRING");
|
1260
|
-
equal(options.hashTypes.noun, "ID");
|
1261
|
-
equal(options.hashTypes.bool, "BOOLEAN");
|
1262
|
-
equal(options.hash.desire, "need");
|
1263
|
-
equal(options.hash.noun, "dad.joke");
|
1264
|
-
equal(options.hash.bool, true);
|
1265
|
-
return "Helper called";
|
1266
|
-
}
|
1267
|
-
};
|
1268
|
-
|
1269
|
-
var result = template({}, { helpers: helpers });
|
1270
|
-
equal(result, "Helper called");
|
1271
|
-
});
|
1272
|
-
|
1273
|
-
test("when inside a block in String mode, .. passes the appropriate context in the options hash to a block helper", function() {
|
1274
|
-
var template = CompilerContext.compile('{{#with dale}}{{#tomdale ../need dad.joke}}wot{{/tomdale}}{{/with}}', {stringParams: true});
|
1275
|
-
|
1276
|
-
var helpers = {
|
1277
|
-
tomdale: function(desire, noun, options) {
|
1278
|
-
return "STOP ME FROM READING HACKER NEWS I " +
|
1279
|
-
options.contexts[0][desire] + " " + noun + " " +
|
1280
|
-
options.fn(this);
|
1281
|
-
},
|
1282
|
-
|
1283
|
-
"with": function(context, options) {
|
1284
|
-
return options.fn(options.contexts[0][context]);
|
1285
|
-
}
|
1286
|
-
};
|
1287
|
-
|
1288
|
-
var result = template({
|
1289
|
-
dale: {},
|
1290
|
-
|
1291
|
-
need: 'need-a'
|
1292
|
-
}, {helpers: helpers});
|
1293
|
-
|
1294
|
-
equals(result, "STOP ME FROM READING HACKER NEWS I need-a dad.joke wot", "Proper context variable output");
|
1295
|
-
});
|
1296
|
-
|
1297
|
-
suite("Regressions");
|
1298
|
-
|
1299
|
-
test("GH-94: Cannot read property of undefined", function() {
|
1300
|
-
var data = {"books":[{"title":"The origin of species","author":{"name":"Charles Darwin"}},{"title":"Lazarillo de Tormes"}]};
|
1301
|
-
var string = "{{#books}}{{title}}{{author.name}}{{/books}}";
|
1302
|
-
shouldCompileTo(string, data, "The origin of speciesCharles DarwinLazarillo de Tormes",
|
1303
|
-
"Renders without an undefined property error");
|
1304
|
-
});
|
1305
|
-
|
1306
|
-
test("GH-150: Inverted sections print when they shouldn't", function() {
|
1307
|
-
var string = "{{^set}}not set{{/set}} :: {{#set}}set{{/set}}";
|
1308
|
-
|
1309
|
-
shouldCompileTo(string, {}, "not set :: ", "inverted sections run when property isn't present in context");
|
1310
|
-
shouldCompileTo(string, {set: undefined}, "not set :: ", "inverted sections run when property is undefined");
|
1311
|
-
shouldCompileTo(string, {set: false}, "not set :: ", "inverted sections run when property is false");
|
1312
|
-
shouldCompileTo(string, {set: true}, " :: set", "inverted sections don't run when property is true");
|
1313
|
-
});
|
1314
|
-
|
1315
|
-
test("Mustache man page", function() {
|
1316
|
-
var string = "Hello {{name}}. You have just won ${{value}}!{{#in_ca}} Well, ${{taxed_value}}, after taxes.{{/in_ca}}";
|
1317
|
-
var data = {
|
1318
|
-
"name": "Chris",
|
1319
|
-
"value": 10000,
|
1320
|
-
"taxed_value": 10000 - (10000 * 0.4),
|
1321
|
-
"in_ca": true
|
1322
|
-
};
|
1323
|
-
|
1324
|
-
shouldCompileTo(string, data, "Hello Chris. You have just won $10000! Well, $6000, after taxes.", "the hello world mustache example works");
|
1325
|
-
});
|
1326
|
-
|
1327
|
-
test("GH-158: Using array index twice, breaks the template", function() {
|
1328
|
-
var string = "{{arr.[0]}}, {{arr.[1]}}";
|
1329
|
-
var data = { "arr": [1,2] };
|
1330
|
-
|
1331
|
-
shouldCompileTo(string, data, "1, 2", "it works as expected");
|
1332
|
-
});
|
1333
|
-
|
1334
|
-
test("bug reported by @fat where lambdas weren't being properly resolved", function() {
|
1335
|
-
var string = "<strong>This is a slightly more complicated {{thing}}.</strong>.\n{{! Just ignore this business. }}\nCheck this out:\n{{#hasThings}}\n<ul>\n{{#things}}\n<li class={{className}}>{{word}}</li>\n{{/things}}</ul>.\n{{/hasThings}}\n{{^hasThings}}\n\n<small>Nothing to check out...</small>\n{{/hasThings}}";
|
1336
|
-
var data = {
|
1337
|
-
thing: function() {
|
1338
|
-
return "blah";
|
1339
|
-
},
|
1340
|
-
things: [
|
1341
|
-
{className: "one", word: "@fat"},
|
1342
|
-
{className: "two", word: "@dhg"},
|
1343
|
-
{className: "three", word:"@sayrer"}
|
1344
|
-
],
|
1345
|
-
hasThings: function() {
|
1346
|
-
return true;
|
1347
|
-
}
|
1348
|
-
};
|
1349
|
-
|
1350
|
-
var output = "<strong>This is a slightly more complicated blah.</strong>.\n\nCheck this out:\n\n<ul>\n\n<li class=one>@fat</li>\n\n<li class=two>@dhg</li>\n\n<li class=three>@sayrer</li>\n</ul>.\n\n";
|
1351
|
-
shouldCompileTo(string, data, output);
|
1352
|
-
});
|
1353
|
-
|
1354
|
-
test("Passing falsy values to Handlebars.compile throws an error", function() {
|
1355
|
-
shouldThrow(function() {
|
1356
|
-
CompilerContext.compile(null);
|
1357
|
-
}, "You must pass a string or Handlebars AST to Handlebars.compile. You passed null");
|
1358
|
-
});
|
1359
|
-
|
1360
|
-
test('GH-408: Multiple loops fail', function() {
|
1361
|
-
var context = [
|
1362
|
-
{ name: "John Doe", location: { city: "Chicago" } },
|
1363
|
-
{ name: "Jane Doe", location: { city: "New York"} }
|
1364
|
-
];
|
1365
|
-
|
1366
|
-
var template = CompilerContext.compile('{{#.}}{{name}}{{/.}}{{#.}}{{name}}{{/.}}{{#.}}{{name}}{{/.}}');
|
1367
|
-
|
1368
|
-
var result = template(context);
|
1369
|
-
equals(result, "John DoeJane DoeJohn DoeJane DoeJohn DoeJane Doe", 'It should output multiple times');
|
1370
|
-
});
|