liquid 2.6.3 → 3.0.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 +4 -4
- data/History.md +46 -13
- data/README.md +27 -2
- data/lib/liquid/block.rb +85 -51
- data/lib/liquid/block_body.rb +123 -0
- data/lib/liquid/condition.rb +26 -15
- data/lib/liquid/context.rb +106 -140
- data/lib/liquid/document.rb +3 -3
- data/lib/liquid/drop.rb +17 -1
- data/lib/liquid/errors.rb +50 -2
- data/lib/liquid/expression.rb +33 -0
- data/lib/liquid/file_system.rb +17 -6
- data/lib/liquid/i18n.rb +39 -0
- data/lib/liquid/interrupts.rb +1 -1
- data/lib/liquid/lexer.rb +51 -0
- data/lib/liquid/locales/en.yml +22 -0
- data/lib/liquid/parser.rb +90 -0
- data/lib/liquid/parser_switching.rb +31 -0
- data/lib/liquid/profiler/hooks.rb +23 -0
- data/lib/liquid/profiler.rb +159 -0
- data/lib/liquid/range_lookup.rb +22 -0
- data/lib/liquid/standardfilters.rb +143 -55
- data/lib/liquid/strainer.rb +14 -4
- data/lib/liquid/tag.rb +25 -9
- data/lib/liquid/tags/assign.rb +12 -9
- data/lib/liquid/tags/break.rb +1 -1
- data/lib/liquid/tags/capture.rb +10 -8
- data/lib/liquid/tags/case.rb +13 -13
- data/lib/liquid/tags/comment.rb +9 -2
- data/lib/liquid/tags/continue.rb +1 -4
- data/lib/liquid/tags/cycle.rb +5 -7
- data/lib/liquid/tags/decrement.rb +3 -4
- data/lib/liquid/tags/for.rb +69 -36
- data/lib/liquid/tags/if.rb +52 -25
- data/lib/liquid/tags/ifchanged.rb +3 -3
- data/lib/liquid/tags/include.rb +19 -8
- data/lib/liquid/tags/increment.rb +4 -8
- data/lib/liquid/tags/raw.rb +4 -7
- data/lib/liquid/tags/table_row.rb +73 -0
- data/lib/liquid/tags/unless.rb +2 -4
- data/lib/liquid/template.rb +124 -14
- data/lib/liquid/token.rb +18 -0
- data/lib/liquid/utils.rb +13 -4
- data/lib/liquid/variable.rb +103 -25
- data/lib/liquid/variable_lookup.rb +78 -0
- data/lib/liquid/version.rb +1 -1
- data/lib/liquid.rb +19 -11
- data/test/fixtures/en_locale.yml +9 -0
- data/test/{liquid → integration}/assign_test.rb +18 -1
- data/test/integration/blank_test.rb +106 -0
- data/test/{liquid → integration}/capture_test.rb +3 -3
- data/test/integration/context_test.rb +32 -0
- data/test/integration/drop_test.rb +271 -0
- data/test/integration/error_handling_test.rb +207 -0
- data/test/{liquid → integration}/filter_test.rb +11 -11
- data/test/integration/hash_ordering_test.rb +23 -0
- data/test/{liquid → integration}/output_test.rb +13 -13
- data/test/integration/parsing_quirks_test.rb +116 -0
- data/test/integration/render_profiling_test.rb +154 -0
- data/test/{liquid → integration}/security_test.rb +10 -10
- data/test/{liquid → integration}/standard_filter_test.rb +148 -32
- data/test/{liquid → integration}/tags/break_tag_test.rb +1 -1
- data/test/{liquid → integration}/tags/continue_tag_test.rb +1 -1
- data/test/{liquid → integration}/tags/for_tag_test.rb +80 -2
- data/test/{liquid → integration}/tags/if_else_tag_test.rb +24 -21
- data/test/integration/tags/include_tag_test.rb +234 -0
- data/test/{liquid → integration}/tags/increment_tag_test.rb +1 -1
- data/test/{liquid → integration}/tags/raw_tag_test.rb +2 -1
- data/test/{liquid → integration}/tags/standard_tag_test.rb +28 -26
- data/test/integration/tags/statements_test.rb +113 -0
- data/test/{liquid/tags/html_tag_test.rb → integration/tags/table_row_test.rb} +5 -5
- data/test/{liquid → integration}/tags/unless_else_tag_test.rb +1 -1
- data/test/{liquid → integration}/template_test.rb +81 -45
- data/test/integration/variable_test.rb +82 -0
- data/test/test_helper.rb +73 -20
- data/test/{liquid/block_test.rb → unit/block_unit_test.rb} +2 -5
- data/test/{liquid/condition_test.rb → unit/condition_unit_test.rb} +23 -1
- data/test/{liquid/context_test.rb → unit/context_unit_test.rb} +39 -25
- data/test/{liquid/file_system_test.rb → unit/file_system_unit_test.rb} +11 -5
- data/test/unit/i18n_unit_test.rb +37 -0
- data/test/unit/lexer_unit_test.rb +48 -0
- data/test/{liquid/module_ex_test.rb → unit/module_ex_unit_test.rb} +7 -7
- data/test/unit/parser_unit_test.rb +82 -0
- data/test/{liquid/regexp_test.rb → unit/regexp_unit_test.rb} +3 -3
- data/test/{liquid/strainer_test.rb → unit/strainer_unit_test.rb} +20 -1
- data/test/unit/tag_unit_test.rb +16 -0
- data/test/unit/tags/case_tag_unit_test.rb +10 -0
- data/test/unit/tags/for_tag_unit_test.rb +13 -0
- data/test/unit/tags/if_tag_unit_test.rb +8 -0
- data/test/unit/template_unit_test.rb +69 -0
- data/test/unit/tokenizer_unit_test.rb +38 -0
- data/test/unit/variable_unit_test.rb +139 -0
- metadata +135 -67
- data/lib/extras/liquid_view.rb +0 -51
- data/lib/liquid/htmltags.rb +0 -73
- data/test/liquid/drop_test.rb +0 -180
- data/test/liquid/error_handling_test.rb +0 -81
- data/test/liquid/hash_ordering_test.rb +0 -25
- data/test/liquid/parsing_quirks_test.rb +0 -52
- data/test/liquid/tags/include_tag_test.rb +0 -166
- data/test/liquid/tags/statements_test.rb +0 -134
- data/test/liquid/variable_test.rb +0 -186
@@ -14,86 +14,86 @@ class TemplateContextDrop < Liquid::Drop
|
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
17
|
-
class
|
18
|
-
|
19
|
-
|
20
|
-
def test_tokenize_strings
|
21
|
-
assert_equal [' '], Template.new.send(:tokenize, ' ')
|
22
|
-
assert_equal ['hello world'], Template.new.send(:tokenize, 'hello world')
|
23
|
-
end
|
24
|
-
|
25
|
-
def test_tokenize_variables
|
26
|
-
assert_equal ['{{funk}}'], Template.new.send(:tokenize, '{{funk}}')
|
27
|
-
assert_equal [' ', '{{funk}}', ' '], Template.new.send(:tokenize, ' {{funk}} ')
|
28
|
-
assert_equal [' ', '{{funk}}', ' ', '{{so}}', ' ', '{{brother}}', ' '], Template.new.send(:tokenize, ' {{funk}} {{so}} {{brother}} ')
|
29
|
-
assert_equal [' ', '{{ funk }}', ' '], Template.new.send(:tokenize, ' {{ funk }} ')
|
17
|
+
class SomethingWithLength
|
18
|
+
def length
|
19
|
+
nil
|
30
20
|
end
|
31
21
|
|
32
|
-
|
33
|
-
|
34
|
-
assert_equal [' ', '{%comment%}', ' '], Template.new.send(:tokenize, ' {%comment%} ')
|
22
|
+
liquid_methods :length
|
23
|
+
end
|
35
24
|
|
36
|
-
|
37
|
-
|
25
|
+
class ErroneousDrop < Liquid::Drop
|
26
|
+
def bad_method
|
27
|
+
raise 'ruby error in drop'
|
38
28
|
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class TemplateTest < Minitest::Test
|
32
|
+
include Liquid
|
39
33
|
|
40
34
|
def test_instance_assigns_persist_on_same_template_object_between_parses
|
41
35
|
t = Template.new
|
42
|
-
assert_equal 'from instance assigns', t.parse("{% assign foo = 'from instance assigns' %}{{ foo }}").render
|
43
|
-
assert_equal 'from instance assigns', t.parse("{{ foo }}").render
|
36
|
+
assert_equal 'from instance assigns', t.parse("{% assign foo = 'from instance assigns' %}{{ foo }}").render!
|
37
|
+
assert_equal 'from instance assigns', t.parse("{{ foo }}").render!
|
44
38
|
end
|
45
39
|
|
46
40
|
def test_instance_assigns_persist_on_same_template_parsing_between_renders
|
47
41
|
t = Template.new.parse("{{ foo }}{% assign foo = 'foo' %}{{ foo }}")
|
48
|
-
assert_equal 'foo', t.render
|
49
|
-
assert_equal 'foofoo', t.render
|
42
|
+
assert_equal 'foo', t.render!
|
43
|
+
assert_equal 'foofoo', t.render!
|
50
44
|
end
|
51
45
|
|
52
46
|
def test_custom_assigns_do_not_persist_on_same_template
|
53
47
|
t = Template.new
|
54
|
-
assert_equal 'from custom assigns', t.parse("{{ foo }}").render('foo' => 'from custom assigns')
|
55
|
-
assert_equal '', t.parse("{{ foo }}").render
|
48
|
+
assert_equal 'from custom assigns', t.parse("{{ foo }}").render!('foo' => 'from custom assigns')
|
49
|
+
assert_equal '', t.parse("{{ foo }}").render!
|
56
50
|
end
|
57
51
|
|
58
52
|
def test_custom_assigns_squash_instance_assigns
|
59
53
|
t = Template.new
|
60
|
-
assert_equal 'from instance assigns', t.parse("{% assign foo = 'from instance assigns' %}{{ foo }}").render
|
61
|
-
assert_equal 'from custom assigns', t.parse("{{ foo }}").render('foo' => 'from custom assigns')
|
54
|
+
assert_equal 'from instance assigns', t.parse("{% assign foo = 'from instance assigns' %}{{ foo }}").render!
|
55
|
+
assert_equal 'from custom assigns', t.parse("{{ foo }}").render!('foo' => 'from custom assigns')
|
62
56
|
end
|
63
57
|
|
64
58
|
def test_persistent_assigns_squash_instance_assigns
|
65
59
|
t = Template.new
|
66
|
-
assert_equal 'from instance assigns', t.parse("{% assign foo = 'from instance assigns' %}{{ foo }}").render
|
60
|
+
assert_equal 'from instance assigns', t.parse("{% assign foo = 'from instance assigns' %}{{ foo }}").render!
|
67
61
|
t.assigns['foo'] = 'from persistent assigns'
|
68
|
-
assert_equal 'from persistent assigns', t.parse("{{ foo }}").render
|
62
|
+
assert_equal 'from persistent assigns', t.parse("{{ foo }}").render!
|
69
63
|
end
|
70
64
|
|
71
65
|
def test_lambda_is_called_once_from_persistent_assigns_over_multiple_parses_and_renders
|
72
66
|
t = Template.new
|
73
67
|
t.assigns['number'] = lambda { @global ||= 0; @global += 1 }
|
74
|
-
assert_equal '1', t.parse("{{number}}").render
|
75
|
-
assert_equal '1', t.parse("{{number}}").render
|
76
|
-
assert_equal '1', t.render
|
68
|
+
assert_equal '1', t.parse("{{number}}").render!
|
69
|
+
assert_equal '1', t.parse("{{number}}").render!
|
70
|
+
assert_equal '1', t.render!
|
77
71
|
@global = nil
|
78
72
|
end
|
79
73
|
|
80
74
|
def test_lambda_is_called_once_from_custom_assigns_over_multiple_parses_and_renders
|
81
75
|
t = Template.new
|
82
76
|
assigns = {'number' => lambda { @global ||= 0; @global += 1 }}
|
83
|
-
assert_equal '1', t.parse("{{number}}").render(assigns)
|
84
|
-
assert_equal '1', t.parse("{{number}}").render(assigns)
|
85
|
-
assert_equal '1', t.render(assigns)
|
77
|
+
assert_equal '1', t.parse("{{number}}").render!(assigns)
|
78
|
+
assert_equal '1', t.parse("{{number}}").render!(assigns)
|
79
|
+
assert_equal '1', t.render!(assigns)
|
86
80
|
@global = nil
|
87
81
|
end
|
88
82
|
|
83
|
+
def test_resource_limits_works_with_custom_length_method
|
84
|
+
t = Template.parse("{% assign foo = bar %}")
|
85
|
+
t.resource_limits = { :render_length_limit => 42 }
|
86
|
+
assert_equal "", t.render!("bar" => SomethingWithLength.new)
|
87
|
+
end
|
88
|
+
|
89
89
|
def test_resource_limits_render_length
|
90
90
|
t = Template.parse("0123456789")
|
91
91
|
t.resource_limits = { :render_length_limit => 5 }
|
92
92
|
assert_equal "Liquid error: Memory limits exceeded", t.render()
|
93
93
|
assert t.resource_limits[:reached]
|
94
94
|
t.resource_limits = { :render_length_limit => 10 }
|
95
|
-
assert_equal "0123456789", t.render()
|
96
|
-
|
95
|
+
assert_equal "0123456789", t.render!()
|
96
|
+
refute_nil t.resource_limits[:render_length_current]
|
97
97
|
end
|
98
98
|
|
99
99
|
def test_resource_limits_render_score
|
@@ -106,8 +106,8 @@ class TemplateTest < Test::Unit::TestCase
|
|
106
106
|
assert_equal "Liquid error: Memory limits exceeded", t.render()
|
107
107
|
assert t.resource_limits[:reached]
|
108
108
|
t.resource_limits = { :render_score_limit => 200 }
|
109
|
-
assert_equal (" foo " * 100), t.render()
|
110
|
-
|
109
|
+
assert_equal (" foo " * 100), t.render!()
|
110
|
+
refute_nil t.resource_limits[:render_score_current]
|
111
111
|
end
|
112
112
|
|
113
113
|
def test_resource_limits_assign_score
|
@@ -116,8 +116,8 @@ class TemplateTest < Test::Unit::TestCase
|
|
116
116
|
assert_equal "Liquid error: Memory limits exceeded", t.render()
|
117
117
|
assert t.resource_limits[:reached]
|
118
118
|
t.resource_limits = { :assign_score_limit => 2 }
|
119
|
-
assert_equal "", t.render()
|
120
|
-
|
119
|
+
assert_equal "", t.render!()
|
120
|
+
refute_nil t.resource_limits[:assign_score_current]
|
121
121
|
end
|
122
122
|
|
123
123
|
def test_resource_limits_aborts_rendering_after_first_error
|
@@ -129,18 +129,54 @@ class TemplateTest < Test::Unit::TestCase
|
|
129
129
|
|
130
130
|
def test_resource_limits_hash_in_template_gets_updated_even_if_no_limits_are_set
|
131
131
|
t = Template.parse("{% for a in (1..100) %} {% assign foo = 1 %} {% endfor %}")
|
132
|
-
t.render()
|
132
|
+
t.render!()
|
133
133
|
assert t.resource_limits[:assign_score_current] > 0
|
134
134
|
assert t.resource_limits[:render_score_current] > 0
|
135
135
|
assert t.resource_limits[:render_length_current] > 0
|
136
136
|
end
|
137
137
|
|
138
|
+
def test_default_resource_limits_unaffected_by_render_with_context
|
139
|
+
context = Context.new
|
140
|
+
t = Template.parse("{% for a in (1..100) %} {% assign foo = 1 %} {% endfor %}")
|
141
|
+
t.render!(context)
|
142
|
+
assert context.resource_limits[:assign_score_current] > 0
|
143
|
+
assert context.resource_limits[:render_score_current] > 0
|
144
|
+
assert context.resource_limits[:render_length_current] > 0
|
145
|
+
refute Template.default_resource_limits.key?(:assign_score_current)
|
146
|
+
refute Template.default_resource_limits.key?(:render_score_current)
|
147
|
+
refute Template.default_resource_limits.key?(:render_length_current)
|
148
|
+
end
|
149
|
+
|
138
150
|
def test_can_use_drop_as_context
|
139
151
|
t = Template.new
|
140
152
|
t.registers['lulz'] = 'haha'
|
141
153
|
drop = TemplateContextDrop.new
|
142
|
-
assert_equal 'fizzbuzz', t.parse('{{foo}}').render(drop)
|
143
|
-
assert_equal 'bar', t.parse('{{bar}}').render(drop)
|
144
|
-
assert_equal 'haha', t.parse("{{baz}}").render(drop)
|
154
|
+
assert_equal 'fizzbuzz', t.parse('{{foo}}').render!(drop)
|
155
|
+
assert_equal 'bar', t.parse('{{bar}}').render!(drop)
|
156
|
+
assert_equal 'haha', t.parse("{{baz}}").render!(drop)
|
157
|
+
end
|
158
|
+
|
159
|
+
def test_render_bang_force_rethrow_errors_on_passed_context
|
160
|
+
context = Context.new({'drop' => ErroneousDrop.new})
|
161
|
+
t = Template.new.parse('{{ drop.bad_method }}')
|
162
|
+
|
163
|
+
e = assert_raises RuntimeError do
|
164
|
+
t.render!(context)
|
165
|
+
end
|
166
|
+
assert_equal 'ruby error in drop', e.message
|
145
167
|
end
|
146
|
-
|
168
|
+
|
169
|
+
def test_exception_handler_doesnt_reraise_if_it_returns_false
|
170
|
+
exception = nil
|
171
|
+
Template.parse("{{ 1 | divided_by: 0 }}").render({}, exception_handler: ->(e) { exception = e; false })
|
172
|
+
assert exception.is_a?(ZeroDivisionError)
|
173
|
+
end
|
174
|
+
|
175
|
+
def test_exception_handler_does_reraise_if_it_returns_true
|
176
|
+
exception = nil
|
177
|
+
assert_raises(ZeroDivisionError) do
|
178
|
+
Template.parse("{{ 1 | divided_by: 0 }}").render({}, exception_handler: ->(e) { exception = e; true })
|
179
|
+
end
|
180
|
+
assert exception.is_a?(ZeroDivisionError)
|
181
|
+
end
|
182
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class VariableTest < Minitest::Test
|
4
|
+
include Liquid
|
5
|
+
|
6
|
+
def test_simple_variable
|
7
|
+
template = Template.parse(%|{{test}}|)
|
8
|
+
assert_equal 'worked', template.render!('test' => 'worked')
|
9
|
+
assert_equal 'worked wonderfully', template.render!('test' => 'worked wonderfully')
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_variable_render_calls_to_liquid
|
13
|
+
assert_template_result 'foobar', '{{ foo }}', 'foo' => ThingWithToLiquid.new
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_simple_with_whitespaces
|
17
|
+
template = Template.parse(%| {{ test }} |)
|
18
|
+
assert_equal ' worked ', template.render!('test' => 'worked')
|
19
|
+
assert_equal ' worked wonderfully ', template.render!('test' => 'worked wonderfully')
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_ignore_unknown
|
23
|
+
template = Template.parse(%|{{ test }}|)
|
24
|
+
assert_equal '', template.render!
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_hash_scoping
|
28
|
+
template = Template.parse(%|{{ test.test }}|)
|
29
|
+
assert_equal 'worked', template.render!('test' => {'test' => 'worked'})
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_false_renders_as_false
|
33
|
+
assert_equal 'false', Template.parse("{{ foo }}").render!('foo' => false)
|
34
|
+
assert_equal 'false', Template.parse("{{ false }}").render!
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_nil_renders_as_empty_string
|
38
|
+
assert_equal '', Template.parse("{{ nil }}").render!
|
39
|
+
assert_equal 'cat', Template.parse("{{ nil | append: 'cat' }}").render!
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_preset_assigns
|
43
|
+
template = Template.parse(%|{{ test }}|)
|
44
|
+
template.assigns['test'] = 'worked'
|
45
|
+
assert_equal 'worked', template.render!
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_reuse_parsed_template
|
49
|
+
template = Template.parse(%|{{ greeting }} {{ name }}|)
|
50
|
+
template.assigns['greeting'] = 'Goodbye'
|
51
|
+
assert_equal 'Hello Tobi', template.render!('greeting' => 'Hello', 'name' => 'Tobi')
|
52
|
+
assert_equal 'Hello ', template.render!('greeting' => 'Hello', 'unknown' => 'Tobi')
|
53
|
+
assert_equal 'Hello Brian', template.render!('greeting' => 'Hello', 'name' => 'Brian')
|
54
|
+
assert_equal 'Goodbye Brian', template.render!('name' => 'Brian')
|
55
|
+
assert_equal({'greeting'=>'Goodbye'}, template.assigns)
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_assigns_not_polluted_from_template
|
59
|
+
template = Template.parse(%|{{ test }}{% assign test = 'bar' %}{{ test }}|)
|
60
|
+
template.assigns['test'] = 'baz'
|
61
|
+
assert_equal 'bazbar', template.render!
|
62
|
+
assert_equal 'bazbar', template.render!
|
63
|
+
assert_equal 'foobar', template.render!('test' => 'foo')
|
64
|
+
assert_equal 'bazbar', template.render!
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_hash_with_default_proc
|
68
|
+
template = Template.parse(%|Hello {{ test }}|)
|
69
|
+
assigns = Hash.new { |h,k| raise "Unknown variable '#{k}'" }
|
70
|
+
assigns['test'] = 'Tobi'
|
71
|
+
assert_equal 'Hello Tobi', template.render!(assigns)
|
72
|
+
assigns.delete('test')
|
73
|
+
e = assert_raises(RuntimeError) {
|
74
|
+
template.render!(assigns)
|
75
|
+
}
|
76
|
+
assert_equal "Unknown variable 'test'", e.message
|
77
|
+
end
|
78
|
+
|
79
|
+
def test_multiline_variable
|
80
|
+
assert_equal 'worked', Template.parse("{{\ntest\n}}").render!('test' => 'worked')
|
81
|
+
end
|
82
|
+
end
|
data/test/test_helper.rb
CHANGED
@@ -1,29 +1,82 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
require '
|
4
|
-
require '
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
3
|
+
require 'minitest/autorun'
|
4
|
+
require 'spy/integration'
|
5
|
+
|
6
|
+
$:.unshift(File.join(File.expand_path(File.dirname(__FILE__)), '..', 'lib'))
|
7
|
+
require 'liquid.rb'
|
8
|
+
|
9
|
+
mode = :strict
|
10
|
+
if env_mode = ENV['LIQUID_PARSER_MODE']
|
11
|
+
puts "-- #{env_mode.upcase} ERROR MODE"
|
12
|
+
mode = env_mode.to_sym
|
13
|
+
end
|
14
|
+
Liquid::Template.error_mode = mode
|
15
|
+
|
16
|
+
if Minitest.const_defined?('Test')
|
17
|
+
# We're on Minitest 5+. Nothing to do here.
|
18
|
+
else
|
19
|
+
# Minitest 4 doesn't have Minitest::Test yet.
|
20
|
+
Minitest::Test = MiniTest::Unit::TestCase
|
9
21
|
end
|
10
|
-
require File.join(File.dirname(__FILE__), '..', 'lib', 'liquid')
|
11
22
|
|
23
|
+
module Minitest
|
24
|
+
class Test
|
25
|
+
def fixture(name)
|
26
|
+
File.join(File.expand_path(File.dirname(__FILE__)), "fixtures", name)
|
27
|
+
end
|
28
|
+
end
|
12
29
|
|
13
|
-
module
|
14
|
-
|
15
|
-
module Assertions
|
16
|
-
include Liquid
|
30
|
+
module Assertions
|
31
|
+
include Liquid
|
17
32
|
|
18
|
-
|
19
|
-
|
20
|
-
|
33
|
+
def assert_template_result(expected, template, assigns = {}, message = nil)
|
34
|
+
assert_equal expected, Template.parse(template).render!(assigns)
|
35
|
+
end
|
36
|
+
|
37
|
+
def assert_template_result_matches(expected, template, assigns = {}, message = nil)
|
38
|
+
return assert_template_result(expected, template, assigns, message) unless expected.is_a? Regexp
|
21
39
|
|
22
|
-
|
23
|
-
|
40
|
+
assert_match expected, Template.parse(template).render!(assigns)
|
41
|
+
end
|
24
42
|
|
25
|
-
|
43
|
+
def assert_match_syntax_error(match, template, registers = {})
|
44
|
+
exception = assert_raises(Liquid::SyntaxError) {
|
45
|
+
Template.parse(template).render(assigns)
|
46
|
+
}
|
47
|
+
assert_match match, exception.message
|
48
|
+
end
|
49
|
+
|
50
|
+
def with_global_filter(*globals)
|
51
|
+
original_filters = Array.new(Liquid::Strainer.class_variable_get(:@@filters))
|
52
|
+
globals.each do |global|
|
53
|
+
Liquid::Template.register_filter(global)
|
26
54
|
end
|
27
|
-
|
28
|
-
|
29
|
-
|
55
|
+
yield
|
56
|
+
ensure
|
57
|
+
Liquid::Strainer.class_variable_set(:@@filters, original_filters)
|
58
|
+
end
|
59
|
+
|
60
|
+
def with_taint_mode(mode)
|
61
|
+
old_mode = Liquid::Template.taint_mode
|
62
|
+
Liquid::Template.taint_mode = mode
|
63
|
+
yield
|
64
|
+
ensure
|
65
|
+
Liquid::Template.taint_mode = old_mode
|
66
|
+
end
|
67
|
+
|
68
|
+
def with_error_mode(mode)
|
69
|
+
old_mode = Liquid::Template.error_mode
|
70
|
+
Liquid::Template.error_mode = mode
|
71
|
+
yield
|
72
|
+
ensure
|
73
|
+
Liquid::Template.error_mode = old_mode
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
class ThingWithToLiquid
|
79
|
+
def to_liquid
|
80
|
+
'foobar'
|
81
|
+
end
|
82
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
|
3
|
-
class
|
3
|
+
class BlockUnitTest < Minitest::Test
|
4
4
|
include Liquid
|
5
5
|
|
6
6
|
def test_blankspace
|
@@ -45,10 +45,7 @@ class BlockTest < Test::Unit::TestCase
|
|
45
45
|
|
46
46
|
def test_with_custom_tag
|
47
47
|
Liquid::Template.register_tag("testtag", Block)
|
48
|
-
|
49
|
-
assert_nothing_thrown do
|
50
|
-
template = Liquid::Template.parse( "{% testtag %} {% endtesttag %}")
|
51
|
-
end
|
48
|
+
assert Liquid::Template.parse( "{% testtag %} {% endtesttag %}")
|
52
49
|
end
|
53
50
|
|
54
51
|
private
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
|
3
|
-
class
|
3
|
+
class ConditionUnitTest < Minitest::Test
|
4
4
|
include Liquid
|
5
5
|
|
6
6
|
def test_basic_condition
|
@@ -49,6 +49,17 @@ class ConditionTest < Test::Unit::TestCase
|
|
49
49
|
assert_evalutes_false "'bob'", 'contains', "'---'"
|
50
50
|
end
|
51
51
|
|
52
|
+
def test_invalid_comparation_operator
|
53
|
+
assert_evaluates_argument_error "1", '~~', '0'
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_comparation_of_int_and_str
|
57
|
+
assert_evaluates_argument_error "'1'", '>', '0'
|
58
|
+
assert_evaluates_argument_error "'1'", '<', '0'
|
59
|
+
assert_evaluates_argument_error "'1'", '>=', '0'
|
60
|
+
assert_evaluates_argument_error "'1'", '<=', '0'
|
61
|
+
end
|
62
|
+
|
52
63
|
def test_contains_works_on_arrays
|
53
64
|
@context = Liquid::Context.new
|
54
65
|
@context['array'] = [1,2,3,4,5]
|
@@ -69,6 +80,10 @@ class ConditionTest < Test::Unit::TestCase
|
|
69
80
|
assert_evalutes_false "0", 'contains', 'not_assigned'
|
70
81
|
end
|
71
82
|
|
83
|
+
def test_contains_return_false_on_wrong_data_type
|
84
|
+
assert_evalutes_false "1", 'contains', '0'
|
85
|
+
end
|
86
|
+
|
72
87
|
def test_or_condition
|
73
88
|
condition = Condition.new('1', '==', '2')
|
74
89
|
|
@@ -124,4 +139,11 @@ class ConditionTest < Test::Unit::TestCase
|
|
124
139
|
assert !Condition.new(left, op, right).evaluate(@context || Liquid::Context.new),
|
125
140
|
"Evaluated true: #{left} #{op} #{right}"
|
126
141
|
end
|
142
|
+
|
143
|
+
def assert_evaluates_argument_error(left, op, right)
|
144
|
+
assert_raises(Liquid::ArgumentError) do
|
145
|
+
Condition.new(left, op, right).evaluate(@context || Liquid::Context.new)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
127
149
|
end # ConditionTest
|
@@ -63,13 +63,17 @@ class ArrayLike
|
|
63
63
|
end
|
64
64
|
end
|
65
65
|
|
66
|
-
class
|
66
|
+
class ContextUnitTest < Minitest::Test
|
67
67
|
include Liquid
|
68
68
|
|
69
69
|
def setup
|
70
70
|
@context = Liquid::Context.new
|
71
71
|
end
|
72
72
|
|
73
|
+
def teardown
|
74
|
+
Spy.teardown
|
75
|
+
end
|
76
|
+
|
73
77
|
def test_variables
|
74
78
|
@context['string'] = 'string'
|
75
79
|
assert_equal 'string', @context['string']
|
@@ -103,16 +107,14 @@ class ContextTest < Test::Unit::TestCase
|
|
103
107
|
end
|
104
108
|
|
105
109
|
def test_scoping
|
106
|
-
|
107
|
-
|
108
|
-
@context.pop
|
109
|
-
end
|
110
|
+
@context.push
|
111
|
+
@context.pop
|
110
112
|
|
111
|
-
|
113
|
+
assert_raises(Liquid::ContextError) do
|
112
114
|
@context.pop
|
113
115
|
end
|
114
116
|
|
115
|
-
|
117
|
+
assert_raises(Liquid::ContextError) do
|
116
118
|
@context.push
|
117
119
|
@context.pop
|
118
120
|
@context.pop
|
@@ -162,24 +164,6 @@ class ContextTest < Test::Unit::TestCase
|
|
162
164
|
|
163
165
|
end
|
164
166
|
|
165
|
-
def test_override_global_filter
|
166
|
-
global = Module.new do
|
167
|
-
def notice(output)
|
168
|
-
"Global #{output}"
|
169
|
-
end
|
170
|
-
end
|
171
|
-
|
172
|
-
local = Module.new do
|
173
|
-
def notice(output)
|
174
|
-
"Local #{output}"
|
175
|
-
end
|
176
|
-
end
|
177
|
-
|
178
|
-
Template.register_filter(global)
|
179
|
-
assert_equal 'Global test', Template.parse("{{'test' | notice }}").render
|
180
|
-
assert_equal 'Local test', Template.parse("{{'test' | notice }}").render({}, :filters => [local])
|
181
|
-
end
|
182
|
-
|
183
167
|
def test_only_intended_filters_make_it_there
|
184
168
|
|
185
169
|
filter = Module.new do
|
@@ -475,4 +459,34 @@ class ContextTest < Test::Unit::TestCase
|
|
475
459
|
assert_kind_of CategoryDrop, @context['category']
|
476
460
|
assert_equal @context, @context['category'].context
|
477
461
|
end
|
462
|
+
|
463
|
+
def test_use_empty_instead_of_any_in_interrupt_handling_to_avoid_lots_of_unnecessary_object_allocations
|
464
|
+
mock_any = Spy.on_instance_method(Array, :any?)
|
465
|
+
mock_empty = Spy.on_instance_method(Array, :empty?)
|
466
|
+
mock_has_interrupt = Spy.on(@context, :has_interrupt?).and_call_through
|
467
|
+
|
468
|
+
@context.has_interrupt?
|
469
|
+
|
470
|
+
refute mock_any.has_been_called?
|
471
|
+
assert mock_empty.has_been_called?
|
472
|
+
end
|
473
|
+
|
474
|
+
def test_variable_lookup_caches_markup
|
475
|
+
mock_scan = Spy.on_instance_method(String, :scan).and_return(["string"])
|
476
|
+
|
477
|
+
@context['string'] = 'string'
|
478
|
+
@context['string']
|
479
|
+
@context['string']
|
480
|
+
|
481
|
+
assert_equal 1, mock_scan.calls.size
|
482
|
+
end
|
483
|
+
|
484
|
+
def test_context_initialization_with_a_proc_in_environment
|
485
|
+
contx = Context.new([:test => lambda { |c| c['poutine']}], {:test => :foo})
|
486
|
+
|
487
|
+
assert contx
|
488
|
+
assert_nil contx['poutine']
|
489
|
+
end
|
490
|
+
|
491
|
+
|
478
492
|
end # ContextTest
|
@@ -1,10 +1,10 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
|
3
|
-
class
|
3
|
+
class FileSystemUnitTest < Minitest::Test
|
4
4
|
include Liquid
|
5
5
|
|
6
6
|
def test_default
|
7
|
-
|
7
|
+
assert_raises(FileSystemError) do
|
8
8
|
BlankFileSystem.new.read_template_file("dummy", {'dummy'=>'smarty'})
|
9
9
|
end
|
10
10
|
end
|
@@ -14,16 +14,22 @@ class FileSystemTest < Test::Unit::TestCase
|
|
14
14
|
assert_equal "/some/path/_mypartial.liquid" , file_system.full_path("mypartial")
|
15
15
|
assert_equal "/some/path/dir/_mypartial.liquid", file_system.full_path("dir/mypartial")
|
16
16
|
|
17
|
-
|
17
|
+
assert_raises(FileSystemError) do
|
18
18
|
file_system.full_path("../dir/mypartial")
|
19
19
|
end
|
20
20
|
|
21
|
-
|
21
|
+
assert_raises(FileSystemError) do
|
22
22
|
file_system.full_path("/dir/../../dir/mypartial")
|
23
23
|
end
|
24
24
|
|
25
|
-
|
25
|
+
assert_raises(FileSystemError) do
|
26
26
|
file_system.full_path("/etc/passwd")
|
27
27
|
end
|
28
28
|
end
|
29
|
+
|
30
|
+
def test_custom_template_filename_patterns
|
31
|
+
file_system = Liquid::LocalFileSystem.new("/some/path", "%s.html")
|
32
|
+
assert_equal "/some/path/mypartial.html" , file_system.full_path("mypartial")
|
33
|
+
assert_equal "/some/path/dir/mypartial.html", file_system.full_path("dir/mypartial")
|
34
|
+
end
|
29
35
|
end # FileSystemTest
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class I18nUnitTest < Minitest::Test
|
4
|
+
include Liquid
|
5
|
+
|
6
|
+
def setup
|
7
|
+
@i18n = I18n.new(fixture("en_locale.yml"))
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_simple_translate_string
|
11
|
+
assert_equal "less is more", @i18n.translate("simple")
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_nested_translate_string
|
15
|
+
assert_equal "something wasn't right", @i18n.translate("errors.syntax.oops")
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_single_string_interpolation
|
19
|
+
assert_equal "something different", @i18n.translate("whatever", :something => "different")
|
20
|
+
end
|
21
|
+
|
22
|
+
# def test_raises_translation_error_on_undefined_interpolation_key
|
23
|
+
# assert_raises I18n::TranslationError do
|
24
|
+
# @i18n.translate("whatever", :oopstypos => "yes")
|
25
|
+
# end
|
26
|
+
# end
|
27
|
+
|
28
|
+
def test_raises_unknown_translation
|
29
|
+
assert_raises I18n::TranslationError do
|
30
|
+
@i18n.translate("doesnt_exist")
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_sets_default_path_to_en
|
35
|
+
assert_equal I18n::DEFAULT_LOCALE, I18n.new.path
|
36
|
+
end
|
37
|
+
end
|