liquid 4.0.4 → 5.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 +32 -4
- data/README.md +6 -0
- data/lib/liquid/block.rb +31 -14
- data/lib/liquid/block_body.rb +164 -54
- data/lib/liquid/condition.rb +39 -18
- data/lib/liquid/context.rb +106 -51
- data/lib/liquid/document.rb +47 -9
- data/lib/liquid/drop.rb +4 -2
- data/lib/liquid/errors.rb +20 -18
- data/lib/liquid/expression.rb +29 -34
- data/lib/liquid/extensions.rb +2 -0
- data/lib/liquid/file_system.rb +6 -4
- data/lib/liquid/forloop_drop.rb +11 -4
- data/lib/liquid/i18n.rb +5 -3
- data/lib/liquid/interrupts.rb +3 -1
- data/lib/liquid/lexer.rb +30 -23
- data/lib/liquid/locales/en.yml +3 -1
- data/lib/liquid/parse_context.rb +16 -4
- data/lib/liquid/parse_tree_visitor.rb +2 -2
- data/lib/liquid/parser.rb +30 -18
- data/lib/liquid/parser_switching.rb +17 -3
- data/lib/liquid/partial_cache.rb +24 -0
- data/lib/liquid/profiler/hooks.rb +26 -14
- data/lib/liquid/profiler.rb +67 -86
- data/lib/liquid/range_lookup.rb +5 -3
- data/lib/liquid/register.rb +6 -0
- data/lib/liquid/resource_limits.rb +47 -8
- data/lib/liquid/standardfilters.rb +62 -43
- data/lib/liquid/static_registers.rb +44 -0
- data/lib/liquid/strainer_factory.rb +36 -0
- data/lib/liquid/strainer_template.rb +53 -0
- data/lib/liquid/tablerowloop_drop.rb +6 -4
- data/lib/liquid/tag/disableable.rb +22 -0
- data/lib/liquid/tag/disabler.rb +21 -0
- data/lib/liquid/tag.rb +28 -6
- data/lib/liquid/tags/assign.rb +24 -10
- data/lib/liquid/tags/break.rb +8 -3
- data/lib/liquid/tags/capture.rb +11 -8
- data/lib/liquid/tags/case.rb +33 -27
- data/lib/liquid/tags/comment.rb +5 -3
- data/lib/liquid/tags/continue.rb +8 -3
- data/lib/liquid/tags/cycle.rb +25 -14
- data/lib/liquid/tags/decrement.rb +6 -3
- data/lib/liquid/tags/echo.rb +26 -0
- data/lib/liquid/tags/for.rb +68 -44
- data/lib/liquid/tags/if.rb +35 -23
- data/lib/liquid/tags/ifchanged.rb +11 -10
- data/lib/liquid/tags/include.rb +34 -47
- data/lib/liquid/tags/increment.rb +7 -3
- data/lib/liquid/tags/raw.rb +14 -11
- data/lib/liquid/tags/render.rb +84 -0
- data/lib/liquid/tags/table_row.rb +23 -19
- data/lib/liquid/tags/unless.rb +15 -15
- data/lib/liquid/template.rb +55 -69
- data/lib/liquid/template_factory.rb +9 -0
- data/lib/liquid/tokenizer.rb +17 -9
- data/lib/liquid/usage.rb +8 -0
- data/lib/liquid/utils.rb +5 -3
- data/lib/liquid/variable.rb +47 -19
- data/lib/liquid/variable_lookup.rb +8 -6
- data/lib/liquid/version.rb +2 -1
- data/lib/liquid.rb +17 -5
- data/test/integration/assign_test.rb +74 -5
- data/test/integration/blank_test.rb +11 -8
- data/test/integration/block_test.rb +47 -1
- data/test/integration/capture_test.rb +18 -10
- data/test/integration/context_test.rb +608 -5
- data/test/integration/document_test.rb +4 -2
- data/test/integration/drop_test.rb +67 -57
- data/test/integration/error_handling_test.rb +73 -61
- data/test/integration/expression_test.rb +46 -0
- data/test/integration/filter_test.rb +53 -42
- data/test/integration/hash_ordering_test.rb +5 -3
- data/test/integration/output_test.rb +26 -24
- data/test/integration/parsing_quirks_test.rb +19 -7
- data/test/integration/{render_profiling_test.rb → profiler_test.rb} +84 -25
- data/test/integration/security_test.rb +30 -21
- data/test/integration/standard_filter_test.rb +339 -281
- data/test/integration/tag/disableable_test.rb +59 -0
- data/test/integration/tag_test.rb +45 -0
- data/test/integration/tags/break_tag_test.rb +4 -2
- data/test/integration/tags/continue_tag_test.rb +4 -2
- data/test/integration/tags/echo_test.rb +13 -0
- data/test/integration/tags/for_tag_test.rb +107 -51
- data/test/integration/tags/if_else_tag_test.rb +5 -3
- data/test/integration/tags/include_tag_test.rb +70 -54
- data/test/integration/tags/increment_tag_test.rb +4 -2
- data/test/integration/tags/liquid_tag_test.rb +116 -0
- data/test/integration/tags/raw_tag_test.rb +14 -11
- data/test/integration/tags/render_tag_test.rb +213 -0
- data/test/integration/tags/standard_tag_test.rb +38 -31
- data/test/integration/tags/statements_test.rb +23 -21
- data/test/integration/tags/table_row_test.rb +2 -0
- data/test/integration/tags/unless_else_tag_test.rb +4 -2
- data/test/integration/template_test.rb +118 -124
- data/test/integration/trim_mode_test.rb +78 -44
- data/test/integration/variable_test.rb +43 -32
- data/test/test_helper.rb +75 -14
- data/test/unit/block_unit_test.rb +19 -24
- data/test/unit/condition_unit_test.rb +79 -77
- data/test/unit/file_system_unit_test.rb +6 -4
- data/test/unit/i18n_unit_test.rb +7 -5
- data/test/unit/lexer_unit_test.rb +11 -9
- data/test/{integration → unit}/parse_tree_visitor_test.rb +1 -1
- data/test/unit/parser_unit_test.rb +37 -35
- data/test/unit/partial_cache_unit_test.rb +128 -0
- data/test/unit/regexp_unit_test.rb +17 -15
- data/test/unit/static_registers_unit_test.rb +156 -0
- data/test/unit/strainer_factory_unit_test.rb +100 -0
- data/test/unit/strainer_template_unit_test.rb +82 -0
- data/test/unit/tag_unit_test.rb +5 -3
- data/test/unit/tags/case_tag_unit_test.rb +3 -1
- data/test/unit/tags/for_tag_unit_test.rb +4 -2
- data/test/unit/tags/if_tag_unit_test.rb +3 -1
- data/test/unit/template_factory_unit_test.rb +12 -0
- data/test/unit/template_unit_test.rb +19 -10
- data/test/unit/tokenizer_unit_test.rb +19 -17
- data/test/unit/variable_unit_test.rb +51 -49
- metadata +75 -47
- data/lib/liquid/strainer.rb +0 -66
- data/test/unit/context_unit_test.rb +0 -490
- data/test/unit/strainer_unit_test.rb +0 -164
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'test_helper'
|
2
4
|
|
3
5
|
class ContextDrop < Liquid::Drop
|
@@ -31,7 +33,7 @@ class ProductDrop < Liquid::Drop
|
|
31
33
|
|
32
34
|
class CatchallDrop < Liquid::Drop
|
33
35
|
def liquid_method_missing(method)
|
34
|
-
|
36
|
+
"catchall_method: #{method}"
|
35
37
|
end
|
36
38
|
end
|
37
39
|
|
@@ -47,10 +49,6 @@ class ProductDrop < Liquid::Drop
|
|
47
49
|
ContextDrop.new
|
48
50
|
end
|
49
51
|
|
50
|
-
def user_input
|
51
|
-
"foo"
|
52
|
-
end
|
53
|
-
|
54
52
|
protected
|
55
53
|
|
56
54
|
def callmenot
|
@@ -109,139 +107,151 @@ class DropsTest < Minitest::Test
|
|
109
107
|
|
110
108
|
def test_product_drop
|
111
109
|
tpl = Liquid::Template.parse(' ')
|
112
|
-
assert_equal
|
110
|
+
assert_equal(' ', tpl.render!('product' => ProductDrop.new))
|
113
111
|
end
|
114
112
|
|
115
113
|
def test_drop_does_only_respond_to_whitelisted_methods
|
116
|
-
assert_equal
|
117
|
-
assert_equal
|
118
|
-
assert_equal
|
119
|
-
assert_equal
|
120
|
-
assert_equal
|
121
|
-
assert_equal
|
114
|
+
assert_equal("", Liquid::Template.parse("{{ product.inspect }}").render!('product' => ProductDrop.new))
|
115
|
+
assert_equal("", Liquid::Template.parse("{{ product.pretty_inspect }}").render!('product' => ProductDrop.new))
|
116
|
+
assert_equal("", Liquid::Template.parse("{{ product.whatever }}").render!('product' => ProductDrop.new))
|
117
|
+
assert_equal("", Liquid::Template.parse('{{ product | map: "inspect" }}').render!('product' => ProductDrop.new))
|
118
|
+
assert_equal("", Liquid::Template.parse('{{ product | map: "pretty_inspect" }}').render!('product' => ProductDrop.new))
|
119
|
+
assert_equal("", Liquid::Template.parse('{{ product | map: "whatever" }}').render!('product' => ProductDrop.new))
|
122
120
|
end
|
123
121
|
|
124
122
|
def test_drops_respond_to_to_liquid
|
125
|
-
assert_equal
|
126
|
-
assert_equal
|
123
|
+
assert_equal("text1", Liquid::Template.parse("{{ product.to_liquid.texts.text }}").render!('product' => ProductDrop.new))
|
124
|
+
assert_equal("text1", Liquid::Template.parse('{{ product | map: "to_liquid" | map: "texts" | map: "text" }}').render!('product' => ProductDrop.new))
|
127
125
|
end
|
128
126
|
|
129
127
|
def test_text_drop
|
130
128
|
output = Liquid::Template.parse(' {{ product.texts.text }} ').render!('product' => ProductDrop.new)
|
131
|
-
assert_equal
|
129
|
+
assert_equal(' text1 ', output)
|
132
130
|
end
|
133
131
|
|
134
132
|
def test_catchall_unknown_method
|
135
133
|
output = Liquid::Template.parse(' {{ product.catchall.unknown }} ').render!('product' => ProductDrop.new)
|
136
|
-
assert_equal
|
134
|
+
assert_equal(' catchall_method: unknown ', output)
|
137
135
|
end
|
138
136
|
|
139
137
|
def test_catchall_integer_argument_drop
|
140
138
|
output = Liquid::Template.parse(' {{ product.catchall[8] }} ').render!('product' => ProductDrop.new)
|
141
|
-
assert_equal
|
139
|
+
assert_equal(' catchall_method: 8 ', output)
|
142
140
|
end
|
143
141
|
|
144
142
|
def test_text_array_drop
|
145
143
|
output = Liquid::Template.parse('{% for text in product.texts.array %} {{text}} {% endfor %}').render!('product' => ProductDrop.new)
|
146
|
-
assert_equal
|
144
|
+
assert_equal(' text1 text2 ', output)
|
147
145
|
end
|
148
146
|
|
149
147
|
def test_context_drop
|
150
148
|
output = Liquid::Template.parse(' {{ context.bar }} ').render!('context' => ContextDrop.new, 'bar' => "carrot")
|
151
|
-
assert_equal
|
149
|
+
assert_equal(' carrot ', output)
|
150
|
+
end
|
151
|
+
|
152
|
+
def test_context_drop_array_with_map
|
153
|
+
output = Liquid::Template.parse(' {{ contexts | map: "bar" }} ').render!('contexts' => [ContextDrop.new, ContextDrop.new], 'bar' => "carrot")
|
154
|
+
assert_equal(' carrotcarrot ', output)
|
152
155
|
end
|
153
156
|
|
154
157
|
def test_nested_context_drop
|
155
158
|
output = Liquid::Template.parse(' {{ product.context.foo }} ').render!('product' => ProductDrop.new, 'foo' => "monkey")
|
156
|
-
assert_equal
|
159
|
+
assert_equal(' monkey ', output)
|
157
160
|
end
|
158
161
|
|
159
162
|
def test_protected
|
160
163
|
output = Liquid::Template.parse(' {{ product.callmenot }} ').render!('product' => ProductDrop.new)
|
161
|
-
assert_equal
|
164
|
+
assert_equal(' ', output)
|
162
165
|
end
|
163
166
|
|
164
167
|
def test_object_methods_not_allowed
|
165
168
|
[:dup, :clone, :singleton_class, :eval, :class_eval, :inspect].each do |method|
|
166
169
|
output = Liquid::Template.parse(" {{ product.#{method} }} ").render!('product' => ProductDrop.new)
|
167
|
-
assert_equal
|
170
|
+
assert_equal(' ', output)
|
168
171
|
end
|
169
172
|
end
|
170
173
|
|
171
174
|
def test_scope
|
172
|
-
assert_equal
|
173
|
-
assert_equal
|
174
|
-
assert_equal
|
175
|
+
assert_equal('1', Liquid::Template.parse('{{ context.scopes }}').render!('context' => ContextDrop.new))
|
176
|
+
assert_equal('2', Liquid::Template.parse('{%for i in dummy%}{{ context.scopes }}{%endfor%}').render!('context' => ContextDrop.new, 'dummy' => [1]))
|
177
|
+
assert_equal('3', Liquid::Template.parse('{%for i in dummy%}{%for i in dummy%}{{ context.scopes }}{%endfor%}{%endfor%}').render!('context' => ContextDrop.new, 'dummy' => [1]))
|
175
178
|
end
|
176
179
|
|
177
180
|
def test_scope_though_proc
|
178
|
-
assert_equal
|
179
|
-
assert_equal
|
180
|
-
assert_equal
|
181
|
+
assert_equal('1', Liquid::Template.parse('{{ s }}').render!('context' => ContextDrop.new, 's' => proc { |c| c['context.scopes'] }))
|
182
|
+
assert_equal('2', Liquid::Template.parse('{%for i in dummy%}{{ s }}{%endfor%}').render!('context' => ContextDrop.new, 's' => proc { |c| c['context.scopes'] }, 'dummy' => [1]))
|
183
|
+
assert_equal('3', Liquid::Template.parse('{%for i in dummy%}{%for i in dummy%}{{ s }}{%endfor%}{%endfor%}').render!('context' => ContextDrop.new, 's' => proc { |c| c['context.scopes'] }, 'dummy' => [1]))
|
181
184
|
end
|
182
185
|
|
183
186
|
def test_scope_with_assigns
|
184
|
-
assert_equal
|
185
|
-
assert_equal
|
186
|
-
assert_equal
|
187
|
-
assert_equal
|
187
|
+
assert_equal('variable', Liquid::Template.parse('{% assign a = "variable"%}{{a}}').render!('context' => ContextDrop.new))
|
188
|
+
assert_equal('variable', Liquid::Template.parse('{% assign a = "variable"%}{%for i in dummy%}{{a}}{%endfor%}').render!('context' => ContextDrop.new, 'dummy' => [1]))
|
189
|
+
assert_equal('test', Liquid::Template.parse('{% assign header_gif = "test"%}{{header_gif}}').render!('context' => ContextDrop.new))
|
190
|
+
assert_equal('test', Liquid::Template.parse("{% assign header_gif = 'test'%}{{header_gif}}").render!('context' => ContextDrop.new))
|
188
191
|
end
|
189
192
|
|
190
193
|
def test_scope_from_tags
|
191
|
-
assert_equal
|
192
|
-
assert_equal
|
193
|
-
assert_equal
|
194
|
+
assert_equal('1', Liquid::Template.parse('{% for i in context.scopes_as_array %}{{i}}{% endfor %}').render!('context' => ContextDrop.new, 'dummy' => [1]))
|
195
|
+
assert_equal('12', Liquid::Template.parse('{%for a in dummy%}{% for i in context.scopes_as_array %}{{i}}{% endfor %}{% endfor %}').render!('context' => ContextDrop.new, 'dummy' => [1]))
|
196
|
+
assert_equal('123', Liquid::Template.parse('{%for a in dummy%}{%for a in dummy%}{% for i in context.scopes_as_array %}{{i}}{% endfor %}{% endfor %}{% endfor %}').render!('context' => ContextDrop.new, 'dummy' => [1]))
|
194
197
|
end
|
195
198
|
|
196
199
|
def test_access_context_from_drop
|
197
|
-
assert_equal
|
200
|
+
assert_equal('123', Liquid::Template.parse('{%for a in dummy%}{{ context.loop_pos }}{% endfor %}').render!('context' => ContextDrop.new, 'dummy' => [1, 2, 3]))
|
198
201
|
end
|
199
202
|
|
200
203
|
def test_enumerable_drop
|
201
|
-
assert_equal
|
204
|
+
assert_equal('123', Liquid::Template.parse('{% for c in collection %}{{c}}{% endfor %}').render!('collection' => EnumerableDrop.new))
|
202
205
|
end
|
203
206
|
|
204
207
|
def test_enumerable_drop_size
|
205
|
-
assert_equal
|
208
|
+
assert_equal('3', Liquid::Template.parse('{{collection.size}}').render!('collection' => EnumerableDrop.new))
|
206
209
|
end
|
207
210
|
|
208
211
|
def test_enumerable_drop_will_invoke_liquid_method_missing_for_clashing_method_names
|
209
212
|
["select", "each", "map", "cycle"].each do |method|
|
210
|
-
assert_equal
|
211
|
-
assert_equal
|
212
|
-
assert_equal
|
213
|
-
assert_equal
|
213
|
+
assert_equal(method.to_s, Liquid::Template.parse("{{collection.#{method}}}").render!('collection' => EnumerableDrop.new))
|
214
|
+
assert_equal(method.to_s, Liquid::Template.parse("{{collection[\"#{method}\"]}}").render!('collection' => EnumerableDrop.new))
|
215
|
+
assert_equal(method.to_s, Liquid::Template.parse("{{collection.#{method}}}").render!('collection' => RealEnumerableDrop.new))
|
216
|
+
assert_equal(method.to_s, Liquid::Template.parse("{{collection[\"#{method}\"]}}").render!('collection' => RealEnumerableDrop.new))
|
214
217
|
end
|
215
218
|
end
|
216
219
|
|
217
220
|
def test_some_enumerable_methods_still_get_invoked
|
218
|
-
[
|
219
|
-
assert_equal
|
220
|
-
assert_equal
|
221
|
-
assert_equal
|
222
|
-
assert_equal
|
221
|
+
[:count, :max].each do |method|
|
222
|
+
assert_equal("3", Liquid::Template.parse("{{collection.#{method}}}").render!('collection' => RealEnumerableDrop.new))
|
223
|
+
assert_equal("3", Liquid::Template.parse("{{collection[\"#{method}\"]}}").render!('collection' => RealEnumerableDrop.new))
|
224
|
+
assert_equal("3", Liquid::Template.parse("{{collection.#{method}}}").render!('collection' => EnumerableDrop.new))
|
225
|
+
assert_equal("3", Liquid::Template.parse("{{collection[\"#{method}\"]}}").render!('collection' => EnumerableDrop.new))
|
223
226
|
end
|
224
227
|
|
225
|
-
assert_equal
|
228
|
+
assert_equal("yes", Liquid::Template.parse("{% if collection contains 3 %}yes{% endif %}").render!('collection' => RealEnumerableDrop.new))
|
226
229
|
|
227
|
-
[
|
228
|
-
assert_equal
|
229
|
-
assert_equal
|
230
|
-
assert_equal
|
231
|
-
assert_equal
|
230
|
+
[:min, :first].each do |method|
|
231
|
+
assert_equal("1", Liquid::Template.parse("{{collection.#{method}}}").render!('collection' => RealEnumerableDrop.new))
|
232
|
+
assert_equal("1", Liquid::Template.parse("{{collection[\"#{method}\"]}}").render!('collection' => RealEnumerableDrop.new))
|
233
|
+
assert_equal("1", Liquid::Template.parse("{{collection.#{method}}}").render!('collection' => EnumerableDrop.new))
|
234
|
+
assert_equal("1", Liquid::Template.parse("{{collection[\"#{method}\"]}}").render!('collection' => EnumerableDrop.new))
|
232
235
|
end
|
233
236
|
end
|
234
237
|
|
235
238
|
def test_empty_string_value_access
|
236
|
-
assert_equal
|
239
|
+
assert_equal('', Liquid::Template.parse('{{ product[value] }}').render!('product' => ProductDrop.new, 'value' => ''))
|
237
240
|
end
|
238
241
|
|
239
242
|
def test_nil_value_access
|
240
|
-
assert_equal
|
243
|
+
assert_equal('', Liquid::Template.parse('{{ product[value] }}').render!('product' => ProductDrop.new, 'value' => nil))
|
241
244
|
end
|
242
245
|
|
243
246
|
def test_default_to_s_on_drops
|
244
|
-
assert_equal
|
245
|
-
assert_equal
|
247
|
+
assert_equal('ProductDrop', Liquid::Template.parse("{{ product }}").render!('product' => ProductDrop.new))
|
248
|
+
assert_equal('EnumerableDrop', Liquid::Template.parse('{{ collection }}').render!('collection' => EnumerableDrop.new))
|
249
|
+
end
|
250
|
+
|
251
|
+
def test_invokable_methods
|
252
|
+
assert_equal(%w(to_liquid catchall context texts).to_set, ProductDrop.invokable_methods)
|
253
|
+
assert_equal(%w(to_liquid scopes_as_array loop_pos scopes).to_set, ContextDrop.invokable_methods)
|
254
|
+
assert_equal(%w(to_liquid size max min first count).to_set, EnumerableDrop.invokable_methods)
|
255
|
+
assert_equal(%w(to_liquid max min sort count first).to_set, RealEnumerableDrop.invokable_methods)
|
246
256
|
end
|
247
257
|
end # DropsTest
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'test_helper'
|
2
4
|
|
3
5
|
class ErrorHandlingTest < Minitest::Test
|
@@ -33,31 +35,31 @@ class ErrorHandlingTest < Minitest::Test
|
|
33
35
|
TEXT
|
34
36
|
|
35
37
|
output = Liquid::Template.parse(template, line_numbers: true).render('errors' => ErrorDrop.new)
|
36
|
-
assert_equal
|
38
|
+
assert_equal(expected, output)
|
37
39
|
end
|
38
40
|
|
39
41
|
def test_standard_error
|
40
42
|
template = Liquid::Template.parse(' {{ errors.standard_error }} ')
|
41
|
-
assert_equal
|
43
|
+
assert_equal(' Liquid error: standard error ', template.render('errors' => ErrorDrop.new))
|
42
44
|
|
43
|
-
assert_equal
|
44
|
-
assert_equal
|
45
|
+
assert_equal(1, template.errors.size)
|
46
|
+
assert_equal(StandardError, template.errors.first.class)
|
45
47
|
end
|
46
48
|
|
47
49
|
def test_syntax
|
48
50
|
template = Liquid::Template.parse(' {{ errors.syntax_error }} ')
|
49
|
-
assert_equal
|
51
|
+
assert_equal(' Liquid syntax error: syntax error ', template.render('errors' => ErrorDrop.new))
|
50
52
|
|
51
|
-
assert_equal
|
52
|
-
assert_equal
|
53
|
+
assert_equal(1, template.errors.size)
|
54
|
+
assert_equal(SyntaxError, template.errors.first.class)
|
53
55
|
end
|
54
56
|
|
55
57
|
def test_argument
|
56
58
|
template = Liquid::Template.parse(' {{ errors.argument_error }} ')
|
57
|
-
assert_equal
|
59
|
+
assert_equal(' Liquid error: argument error ', template.render('errors' => ErrorDrop.new))
|
58
60
|
|
59
|
-
assert_equal
|
60
|
-
assert_equal
|
61
|
+
assert_equal(1, template.errors.size)
|
62
|
+
assert_equal(ArgumentError, template.errors.first.class)
|
61
63
|
end
|
62
64
|
|
63
65
|
def test_missing_endtag_parse_time_error
|
@@ -76,22 +78,21 @@ class ErrorHandlingTest < Minitest::Test
|
|
76
78
|
|
77
79
|
def test_lax_unrecognized_operator
|
78
80
|
template = Liquid::Template.parse(' {% if 1 =! 2 %}ok{% endif %} ', error_mode: :lax)
|
79
|
-
assert_equal
|
80
|
-
assert_equal
|
81
|
-
assert_equal
|
81
|
+
assert_equal(' Liquid error: Unknown operator =! ', template.render)
|
82
|
+
assert_equal(1, template.errors.size)
|
83
|
+
assert_equal(Liquid::ArgumentError, template.errors.first.class)
|
82
84
|
end
|
83
85
|
|
84
86
|
def test_with_line_numbers_adds_numbers_to_parser_errors
|
85
87
|
err = assert_raises(SyntaxError) do
|
86
|
-
Liquid::Template.parse(
|
88
|
+
Liquid::Template.parse('
|
87
89
|
foobar
|
88
90
|
|
89
91
|
{% "cat" | foobar %}
|
90
92
|
|
91
93
|
bla
|
92
|
-
|
93
|
-
line_numbers: true
|
94
|
-
)
|
94
|
+
',
|
95
|
+
line_numbers: true)
|
95
96
|
end
|
96
97
|
|
97
98
|
assert_match(/Liquid syntax error \(line 4\)/, err.message)
|
@@ -99,15 +100,14 @@ class ErrorHandlingTest < Minitest::Test
|
|
99
100
|
|
100
101
|
def test_with_line_numbers_adds_numbers_to_parser_errors_with_whitespace_trim
|
101
102
|
err = assert_raises(SyntaxError) do
|
102
|
-
Liquid::Template.parse(
|
103
|
+
Liquid::Template.parse('
|
103
104
|
foobar
|
104
105
|
|
105
106
|
{%- "cat" | foobar -%}
|
106
107
|
|
107
108
|
bla
|
108
|
-
|
109
|
-
line_numbers: true
|
110
|
-
)
|
109
|
+
',
|
110
|
+
line_numbers: true)
|
111
111
|
end
|
112
112
|
|
113
113
|
assert_match(/Liquid syntax error \(line 4\)/, err.message)
|
@@ -122,11 +122,10 @@ class ErrorHandlingTest < Minitest::Test
|
|
122
122
|
bla
|
123
123
|
',
|
124
124
|
error_mode: :warn,
|
125
|
-
line_numbers: true
|
126
|
-
)
|
125
|
+
line_numbers: true)
|
127
126
|
|
128
|
-
assert_equal
|
129
|
-
template.warnings.map(&:message)
|
127
|
+
assert_equal(['Liquid syntax error (line 4): Unexpected character = in "1 =! 2"'],
|
128
|
+
template.warnings.map(&:message))
|
130
129
|
end
|
131
130
|
|
132
131
|
def test_parsing_strict_with_line_numbers_adds_numbers_to_lexer_errors
|
@@ -139,11 +138,10 @@ class ErrorHandlingTest < Minitest::Test
|
|
139
138
|
bla
|
140
139
|
',
|
141
140
|
error_mode: :strict,
|
142
|
-
line_numbers: true
|
143
|
-
)
|
141
|
+
line_numbers: true)
|
144
142
|
end
|
145
143
|
|
146
|
-
assert_equal
|
144
|
+
assert_equal('Liquid syntax error (line 4): Unexpected character = in "1 =! 2"', err.message)
|
147
145
|
end
|
148
146
|
|
149
147
|
def test_syntax_errors_in_nested_blocks_have_correct_line_number
|
@@ -157,46 +155,45 @@ class ErrorHandlingTest < Minitest::Test
|
|
157
155
|
|
158
156
|
bla
|
159
157
|
',
|
160
|
-
line_numbers: true
|
161
|
-
)
|
158
|
+
line_numbers: true)
|
162
159
|
end
|
163
160
|
|
164
|
-
assert_equal
|
161
|
+
assert_equal("Liquid syntax error (line 5): Unknown tag 'foo'", err.message)
|
165
162
|
end
|
166
163
|
|
167
164
|
def test_strict_error_messages
|
168
165
|
err = assert_raises(SyntaxError) do
|
169
166
|
Liquid::Template.parse(' {% if 1 =! 2 %}ok{% endif %} ', error_mode: :strict)
|
170
167
|
end
|
171
|
-
assert_equal
|
168
|
+
assert_equal('Liquid syntax error: Unexpected character = in "1 =! 2"', err.message)
|
172
169
|
|
173
170
|
err = assert_raises(SyntaxError) do
|
174
171
|
Liquid::Template.parse('{{%%%}}', error_mode: :strict)
|
175
172
|
end
|
176
|
-
assert_equal
|
173
|
+
assert_equal('Liquid syntax error: Unexpected character % in "{{%%%}}"', err.message)
|
177
174
|
end
|
178
175
|
|
179
176
|
def test_warnings
|
180
177
|
template = Liquid::Template.parse('{% if ~~~ %}{{%%%}}{% else %}{{ hello. }}{% endif %}', error_mode: :warn)
|
181
|
-
assert_equal
|
182
|
-
assert_equal
|
183
|
-
assert_equal
|
184
|
-
assert_equal
|
185
|
-
assert_equal
|
178
|
+
assert_equal(3, template.warnings.size)
|
179
|
+
assert_equal('Unexpected character ~ in "~~~"', template.warnings[0].to_s(false))
|
180
|
+
assert_equal('Unexpected character % in "{{%%%}}"', template.warnings[1].to_s(false))
|
181
|
+
assert_equal('Expected id but found end_of_string in "{{ hello. }}"', template.warnings[2].to_s(false))
|
182
|
+
assert_equal('', template.render)
|
186
183
|
end
|
187
184
|
|
188
185
|
def test_warning_line_numbers
|
189
186
|
template = Liquid::Template.parse("{% if ~~~ %}\n{{%%%}}{% else %}\n{{ hello. }}{% endif %}", error_mode: :warn, line_numbers: true)
|
190
|
-
assert_equal
|
191
|
-
assert_equal
|
192
|
-
assert_equal
|
193
|
-
assert_equal
|
194
|
-
assert_equal
|
187
|
+
assert_equal('Liquid syntax error (line 1): Unexpected character ~ in "~~~"', template.warnings[0].message)
|
188
|
+
assert_equal('Liquid syntax error (line 2): Unexpected character % in "{{%%%}}"', template.warnings[1].message)
|
189
|
+
assert_equal('Liquid syntax error (line 3): Expected id but found end_of_string in "{{ hello. }}"', template.warnings[2].message)
|
190
|
+
assert_equal(3, template.warnings.size)
|
191
|
+
assert_equal([1, 2, 3], template.warnings.map(&:line_number))
|
195
192
|
end
|
196
193
|
|
197
194
|
# Liquid should not catch Exceptions that are not subclasses of StandardError, like Interrupt and NoMemoryError
|
198
195
|
def test_exceptions_propagate
|
199
|
-
assert_raises
|
196
|
+
assert_raises(Exception) do
|
200
197
|
template = Liquid::Template.parse('{{ errors.exception }}')
|
201
198
|
template.render('errors' => ErrorDrop.new)
|
202
199
|
end
|
@@ -205,41 +202,47 @@ class ErrorHandlingTest < Minitest::Test
|
|
205
202
|
def test_default_exception_renderer_with_internal_error
|
206
203
|
template = Liquid::Template.parse('This is a runtime error: {{ errors.runtime_error }}', line_numbers: true)
|
207
204
|
|
208
|
-
output = template.render(
|
205
|
+
output = template.render('errors' => ErrorDrop.new)
|
209
206
|
|
210
|
-
assert_equal
|
211
|
-
assert_equal
|
207
|
+
assert_equal('This is a runtime error: Liquid error (line 1): internal', output)
|
208
|
+
assert_equal([Liquid::InternalError], template.errors.map(&:class))
|
212
209
|
end
|
213
210
|
|
214
211
|
def test_setting_default_exception_renderer
|
215
212
|
old_exception_renderer = Liquid::Template.default_exception_renderer
|
216
213
|
exceptions = []
|
217
|
-
Liquid::Template.default_exception_renderer = ->(e) {
|
214
|
+
Liquid::Template.default_exception_renderer = ->(e) {
|
215
|
+
exceptions << e
|
216
|
+
''
|
217
|
+
}
|
218
218
|
template = Liquid::Template.parse('This is a runtime error: {{ errors.argument_error }}')
|
219
219
|
|
220
|
-
output = template.render(
|
220
|
+
output = template.render('errors' => ErrorDrop.new)
|
221
221
|
|
222
|
-
assert_equal
|
223
|
-
assert_equal
|
222
|
+
assert_equal('This is a runtime error: ', output)
|
223
|
+
assert_equal([Liquid::ArgumentError], template.errors.map(&:class))
|
224
224
|
ensure
|
225
225
|
Liquid::Template.default_exception_renderer = old_exception_renderer if old_exception_renderer
|
226
226
|
end
|
227
227
|
|
228
228
|
def test_exception_renderer_exposing_non_liquid_error
|
229
|
-
template
|
229
|
+
template = Liquid::Template.parse('This is a runtime error: {{ errors.runtime_error }}', line_numbers: true)
|
230
230
|
exceptions = []
|
231
|
-
handler
|
231
|
+
handler = ->(e) {
|
232
|
+
exceptions << e
|
233
|
+
e.cause
|
234
|
+
}
|
232
235
|
|
233
236
|
output = template.render({ 'errors' => ErrorDrop.new }, exception_renderer: handler)
|
234
237
|
|
235
|
-
assert_equal
|
236
|
-
assert_equal
|
237
|
-
assert_equal
|
238
|
-
assert_equal
|
238
|
+
assert_equal('This is a runtime error: runtime error', output)
|
239
|
+
assert_equal([Liquid::InternalError], exceptions.map(&:class))
|
240
|
+
assert_equal(exceptions, template.errors)
|
241
|
+
assert_equal('#<RuntimeError: runtime error>', exceptions.first.cause.inspect)
|
239
242
|
end
|
240
243
|
|
241
244
|
class TestFileSystem
|
242
|
-
def read_template_file(
|
245
|
+
def read_template_file(_template_path)
|
243
246
|
"{{ errors.argument_error }}"
|
244
247
|
end
|
245
248
|
end
|
@@ -249,12 +252,21 @@ class ErrorHandlingTest < Minitest::Test
|
|
249
252
|
|
250
253
|
begin
|
251
254
|
Liquid::Template.file_system = TestFileSystem.new
|
255
|
+
|
252
256
|
template = Liquid::Template.parse("Argument error:\n{% include 'product' %}", line_numbers: true)
|
253
|
-
page
|
257
|
+
page = template.render('errors' => ErrorDrop.new)
|
254
258
|
ensure
|
255
259
|
Liquid::Template.file_system = old_file_system
|
256
260
|
end
|
257
|
-
assert_equal
|
258
|
-
assert_equal
|
261
|
+
assert_equal("Argument error:\nLiquid error (product line 1): argument error", page)
|
262
|
+
assert_equal("product", template.errors.first.template_name)
|
263
|
+
end
|
264
|
+
|
265
|
+
def test_bug_compatible_silencing_of_errors_in_blank_nodes
|
266
|
+
output = Liquid::Template.parse("{% assign x = 0 %}{% if 1 < '2' %}not blank{% assign x = 3 %}{% endif %}{{ x }}").render
|
267
|
+
assert_equal("Liquid error: comparison of Integer with String failed0", output)
|
268
|
+
|
269
|
+
output = Liquid::Template.parse("{% assign x = 0 %}{% if 1 < '2' %}{% assign x = 3 %}{% endif %}{{ x }}").render
|
270
|
+
assert_equal("0", output)
|
259
271
|
end
|
260
272
|
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'test_helper'
|
4
|
+
|
5
|
+
class ExpressionTest < Minitest::Test
|
6
|
+
def test_keyword_literals
|
7
|
+
assert_equal(true, parse_and_eval("true"))
|
8
|
+
assert_equal(true, parse_and_eval(" true "))
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_string
|
12
|
+
assert_equal("single quoted", parse_and_eval("'single quoted'"))
|
13
|
+
assert_equal("double quoted", parse_and_eval('"double quoted"'))
|
14
|
+
assert_equal("spaced", parse_and_eval(" 'spaced' "))
|
15
|
+
assert_equal("spaced2", parse_and_eval(' "spaced2" '))
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_int
|
19
|
+
assert_equal(123, parse_and_eval("123"))
|
20
|
+
assert_equal(456, parse_and_eval(" 456 "))
|
21
|
+
assert_equal(12, parse_and_eval("012"))
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_float
|
25
|
+
assert_equal(1.5, parse_and_eval("1.5"))
|
26
|
+
assert_equal(2.5, parse_and_eval(" 2.5 "))
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_range
|
30
|
+
assert_equal(1..2, parse_and_eval("(1..2)"))
|
31
|
+
assert_equal(3..4, parse_and_eval(" ( 3 .. 4 ) "))
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def parse_and_eval(markup, **assigns)
|
37
|
+
if Liquid::Template.error_mode == :strict
|
38
|
+
p = Liquid::Parser.new(markup)
|
39
|
+
markup = p.expression
|
40
|
+
p.consume(:end_of_string)
|
41
|
+
end
|
42
|
+
expression = Liquid::Expression.parse(markup)
|
43
|
+
context = Liquid::Context.new(assigns)
|
44
|
+
context.evaluate(expression)
|
45
|
+
end
|
46
|
+
end
|