handlebars 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +1 -2
  3. data/README.mdown +15 -7
  4. data/handlebars.gemspec +2 -5
  5. data/lib/handlebars.rb +5 -5
  6. data/lib/handlebars/context.rb +3 -12
  7. data/lib/handlebars/version.rb +1 -1
  8. data/spec/handlebars_spec.rb +1 -1
  9. metadata +47 -58
  10. data/.gitmodules +0 -3
  11. data/vendor/handlebars/.gitignore +0 -8
  12. data/vendor/handlebars/.jshintrc +0 -52
  13. data/vendor/handlebars/.npmignore +0 -10
  14. data/vendor/handlebars/.rspec +0 -1
  15. data/vendor/handlebars/Gemfile +0 -5
  16. data/vendor/handlebars/LICENSE +0 -19
  17. data/vendor/handlebars/README.markdown +0 -317
  18. data/vendor/handlebars/Rakefile +0 -109
  19. data/vendor/handlebars/bench/benchwarmer.js +0 -149
  20. data/vendor/handlebars/bench/handlebars.js +0 -172
  21. data/vendor/handlebars/bin/handlebars +0 -193
  22. data/vendor/handlebars/dist/handlebars.js +0 -2201
  23. data/vendor/handlebars/dist/handlebars.runtime.js +0 -321
  24. data/vendor/handlebars/lib/handlebars.js +0 -14
  25. data/vendor/handlebars/lib/handlebars/base.js +0 -154
  26. data/vendor/handlebars/lib/handlebars/compiler/ast.js +0 -133
  27. data/vendor/handlebars/lib/handlebars/compiler/base.js +0 -21
  28. data/vendor/handlebars/lib/handlebars/compiler/compiler.js +0 -1267
  29. data/vendor/handlebars/lib/handlebars/compiler/index.js +0 -7
  30. data/vendor/handlebars/lib/handlebars/compiler/printer.js +0 -131
  31. data/vendor/handlebars/lib/handlebars/compiler/visitor.js +0 -13
  32. data/vendor/handlebars/lib/handlebars/runtime.js +0 -88
  33. data/vendor/handlebars/lib/handlebars/utils.js +0 -67
  34. data/vendor/handlebars/package.json +0 -35
  35. data/vendor/handlebars/spec/acceptance_spec.rb +0 -101
  36. data/vendor/handlebars/spec/parser_spec.rb +0 -433
  37. data/vendor/handlebars/spec/qunit_spec.js +0 -1370
  38. data/vendor/handlebars/spec/spec_helper.rb +0 -157
  39. data/vendor/handlebars/spec/tokenizer_spec.rb +0 -301
  40. data/vendor/handlebars/src/handlebars.l +0 -53
  41. data/vendor/handlebars/src/handlebars.yy +0 -109
  42. data/vendor/handlebars/src/parser-prefix.js +0 -1
  43. 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: "&\"'`\\<>"}, '&amp;&quot;&#x27;&#x60;\\&lt;&gt;',
143
- "by default expressions should be escaped");
144
-
145
- shouldCompileTo("{{awesome}}", {awesome: "Escaped, <b> looks like: &lt;b&gt;"}, 'Escaped, &lt;b&gt; looks like: &amp;lt;b&amp;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&#x27;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 = "&lt;b&gt;#1&lt;/b&gt;. goodbye! 2. GOODBYE! cruel world!";
761
- var expected2 = "2. GOODBYE! &lt;b&gt;#1&lt;/b&gt;. 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
- });