liquid 4.0.3 → 5.1.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 +54 -0
- data/README.md +6 -0
- data/lib/liquid/block.rb +31 -14
- data/lib/liquid/block_body.rb +166 -54
- data/lib/liquid/condition.rb +41 -20
- data/lib/liquid/context.rb +107 -52
- 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 +20 -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 +13 -3
- data/lib/liquid/register.rb +6 -0
- data/lib/liquid/resource_limits.rb +47 -8
- data/lib/liquid/standardfilters.rb +95 -46
- 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 +40 -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 +34 -0
- data/lib/liquid/tags/for.rb +68 -44
- data/lib/liquid/tags/if.rb +39 -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 +23 -15
- data/lib/liquid/template.rb +53 -72
- data/lib/liquid/template_factory.rb +9 -0
- data/lib/liquid/tokenizer.rb +18 -10
- data/lib/liquid/usage.rb +8 -0
- data/lib/liquid/utils.rb +13 -3
- data/lib/liquid/variable.rb +46 -41
- data/lib/liquid/variable_lookup.rb +11 -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 +609 -5
- data/test/integration/document_test.rb +4 -2
- data/test/integration/drop_test.rb +67 -83
- 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 +385 -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 +132 -124
- data/test/integration/trim_mode_test.rb +78 -44
- data/test/integration/variable_test.rb +74 -32
- data/test/test_helper.rb +113 -22
- 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 +16 -2
- 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 +26 -19
- data/test/unit/variable_unit_test.rb +51 -49
- metadata +76 -50
- data/lib/liquid/strainer.rb +0 -66
- data/lib/liquid/truffle.rb +0 -5
- data/test/truffle/truffle_test.rb +0 -9
- data/test/unit/context_unit_test.rb +0 -489
- 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
|
require 'timeout'
|
3
5
|
|
@@ -38,8 +40,8 @@ class TemplateTest < Minitest::Test
|
|
38
40
|
|
39
41
|
def test_instance_assigns_persist_on_same_template_object_between_parses
|
40
42
|
t = Template.new
|
41
|
-
assert_equal
|
42
|
-
assert_equal
|
43
|
+
assert_equal('from instance assigns', t.parse("{% assign foo = 'from instance assigns' %}{{ foo }}").render!)
|
44
|
+
assert_equal('from instance assigns', t.parse("{{ foo }}").render!)
|
43
45
|
end
|
44
46
|
|
45
47
|
def test_warnings_is_not_exponential_time
|
@@ -49,251 +51,243 @@ class TemplateTest < Minitest::Test
|
|
49
51
|
end
|
50
52
|
|
51
53
|
t = Template.parse(str)
|
52
|
-
assert_equal
|
54
|
+
assert_equal([], Timeout.timeout(1) { t.warnings })
|
53
55
|
end
|
54
56
|
|
55
57
|
def test_instance_assigns_persist_on_same_template_parsing_between_renders
|
56
58
|
t = Template.new.parse("{{ foo }}{% assign foo = 'foo' %}{{ foo }}")
|
57
|
-
assert_equal
|
58
|
-
assert_equal
|
59
|
+
assert_equal('foo', t.render!)
|
60
|
+
assert_equal('foofoo', t.render!)
|
59
61
|
end
|
60
62
|
|
61
63
|
def test_custom_assigns_do_not_persist_on_same_template
|
62
64
|
t = Template.new
|
63
|
-
assert_equal
|
64
|
-
assert_equal
|
65
|
+
assert_equal('from custom assigns', t.parse("{{ foo }}").render!('foo' => 'from custom assigns'))
|
66
|
+
assert_equal('', t.parse("{{ foo }}").render!)
|
65
67
|
end
|
66
68
|
|
67
69
|
def test_custom_assigns_squash_instance_assigns
|
68
70
|
t = Template.new
|
69
|
-
assert_equal
|
70
|
-
assert_equal
|
71
|
+
assert_equal('from instance assigns', t.parse("{% assign foo = 'from instance assigns' %}{{ foo }}").render!)
|
72
|
+
assert_equal('from custom assigns', t.parse("{{ foo }}").render!('foo' => 'from custom assigns'))
|
71
73
|
end
|
72
74
|
|
73
75
|
def test_persistent_assigns_squash_instance_assigns
|
74
76
|
t = Template.new
|
75
|
-
assert_equal
|
77
|
+
assert_equal('from instance assigns', t.parse("{% assign foo = 'from instance assigns' %}{{ foo }}").render!)
|
76
78
|
t.assigns['foo'] = 'from persistent assigns'
|
77
|
-
assert_equal
|
79
|
+
assert_equal('from persistent assigns', t.parse("{{ foo }}").render!)
|
78
80
|
end
|
79
81
|
|
80
82
|
def test_lambda_is_called_once_from_persistent_assigns_over_multiple_parses_and_renders
|
81
83
|
t = Template.new
|
82
|
-
t.assigns['number'] = -> {
|
83
|
-
|
84
|
-
|
85
|
-
|
84
|
+
t.assigns['number'] = -> {
|
85
|
+
@global ||= 0
|
86
|
+
@global += 1
|
87
|
+
}
|
88
|
+
assert_equal('1', t.parse("{{number}}").render!)
|
89
|
+
assert_equal('1', t.parse("{{number}}").render!)
|
90
|
+
assert_equal('1', t.render!)
|
86
91
|
@global = nil
|
87
92
|
end
|
88
93
|
|
89
94
|
def test_lambda_is_called_once_from_custom_assigns_over_multiple_parses_and_renders
|
90
95
|
t = Template.new
|
91
|
-
assigns = { 'number' => -> {
|
92
|
-
|
93
|
-
|
94
|
-
|
96
|
+
assigns = { 'number' => -> {
|
97
|
+
@global ||= 0
|
98
|
+
@global += 1
|
99
|
+
} }
|
100
|
+
assert_equal('1', t.parse("{{number}}").render!(assigns))
|
101
|
+
assert_equal('1', t.parse("{{number}}").render!(assigns))
|
102
|
+
assert_equal('1', t.render!(assigns))
|
95
103
|
@global = nil
|
96
104
|
end
|
97
105
|
|
98
106
|
def test_resource_limits_works_with_custom_length_method
|
99
107
|
t = Template.parse("{% assign foo = bar %}")
|
100
108
|
t.resource_limits.render_length_limit = 42
|
101
|
-
assert_equal
|
109
|
+
assert_equal("", t.render!("bar" => SomethingWithLength.new))
|
102
110
|
end
|
103
111
|
|
104
112
|
def test_resource_limits_render_length
|
105
113
|
t = Template.parse("0123456789")
|
106
|
-
t.resource_limits.render_length_limit =
|
107
|
-
assert_equal
|
108
|
-
assert
|
114
|
+
t.resource_limits.render_length_limit = 9
|
115
|
+
assert_equal("Liquid error: Memory limits exceeded", t.render)
|
116
|
+
assert(t.resource_limits.reached?)
|
109
117
|
|
110
118
|
t.resource_limits.render_length_limit = 10
|
111
|
-
assert_equal
|
112
|
-
refute_nil t.resource_limits.render_length
|
119
|
+
assert_equal("0123456789", t.render!)
|
113
120
|
end
|
114
121
|
|
115
122
|
def test_resource_limits_render_score
|
116
123
|
t = Template.parse("{% for a in (1..10) %} {% for a in (1..10) %} foo {% endfor %} {% endfor %}")
|
117
124
|
t.resource_limits.render_score_limit = 50
|
118
|
-
assert_equal
|
119
|
-
assert
|
125
|
+
assert_equal("Liquid error: Memory limits exceeded", t.render)
|
126
|
+
assert(t.resource_limits.reached?)
|
120
127
|
|
121
128
|
t = Template.parse("{% for a in (1..100) %} foo {% endfor %}")
|
122
129
|
t.resource_limits.render_score_limit = 50
|
123
|
-
assert_equal
|
124
|
-
assert
|
130
|
+
assert_equal("Liquid error: Memory limits exceeded", t.render)
|
131
|
+
assert(t.resource_limits.reached?)
|
125
132
|
|
126
133
|
t.resource_limits.render_score_limit = 200
|
127
|
-
assert_equal
|
128
|
-
refute_nil
|
129
|
-
end
|
130
|
-
|
131
|
-
def test_resource_limits_assign_score
|
132
|
-
t = Template.parse("{% assign foo = 42 %}{% assign bar = 23 %}")
|
133
|
-
t.resource_limits.assign_score_limit = 1
|
134
|
-
assert_equal "Liquid error: Memory limits exceeded", t.render
|
135
|
-
assert t.resource_limits.reached?
|
136
|
-
|
137
|
-
t.resource_limits.assign_score_limit = 2
|
138
|
-
assert_equal "", t.render!
|
139
|
-
refute_nil t.resource_limits.assign_score
|
140
|
-
end
|
141
|
-
|
142
|
-
def test_resource_limits_assign_score_nested
|
143
|
-
t = Template.parse("{% assign foo = 'aaaa' | reverse %}")
|
144
|
-
|
145
|
-
t.resource_limits.assign_score_limit = 3
|
146
|
-
assert_equal "Liquid error: Memory limits exceeded", t.render
|
147
|
-
assert t.resource_limits.reached?
|
148
|
-
|
149
|
-
t.resource_limits.assign_score_limit = 5
|
150
|
-
assert_equal "", t.render!
|
134
|
+
assert_equal((" foo " * 100), t.render!)
|
135
|
+
refute_nil(t.resource_limits.render_score)
|
151
136
|
end
|
152
137
|
|
153
138
|
def test_resource_limits_aborts_rendering_after_first_error
|
154
139
|
t = Template.parse("{% for a in (1..100) %} foo1 {% endfor %} bar {% for a in (1..100) %} foo2 {% endfor %}")
|
155
140
|
t.resource_limits.render_score_limit = 50
|
156
|
-
assert_equal
|
157
|
-
assert
|
141
|
+
assert_equal("Liquid error: Memory limits exceeded", t.render)
|
142
|
+
assert(t.resource_limits.reached?)
|
158
143
|
end
|
159
144
|
|
160
145
|
def test_resource_limits_hash_in_template_gets_updated_even_if_no_limits_are_set
|
161
|
-
t = Template.parse("{% for a in (1..100) %}
|
146
|
+
t = Template.parse("{% for a in (1..100) %}x{% assign foo = 1 %} {% endfor %}")
|
162
147
|
t.render!
|
163
|
-
assert
|
164
|
-
assert
|
165
|
-
assert t.resource_limits.render_length > 0
|
148
|
+
assert(t.resource_limits.assign_score > 0)
|
149
|
+
assert(t.resource_limits.render_score > 0)
|
166
150
|
end
|
167
151
|
|
168
152
|
def test_render_length_persists_between_blocks
|
169
153
|
t = Template.parse("{% if true %}aaaa{% endif %}")
|
170
|
-
t.resource_limits.render_length_limit =
|
171
|
-
assert_equal
|
172
|
-
t.resource_limits.render_length_limit =
|
173
|
-
assert_equal
|
154
|
+
t.resource_limits.render_length_limit = 3
|
155
|
+
assert_equal("Liquid error: Memory limits exceeded", t.render)
|
156
|
+
t.resource_limits.render_length_limit = 4
|
157
|
+
assert_equal("aaaa", t.render)
|
174
158
|
|
175
159
|
t = Template.parse("{% if true %}aaaa{% endif %}{% if true %}bbb{% endif %}")
|
176
|
-
t.resource_limits.render_length_limit =
|
177
|
-
assert_equal
|
178
|
-
t.resource_limits.render_length_limit =
|
179
|
-
assert_equal
|
160
|
+
t.resource_limits.render_length_limit = 6
|
161
|
+
assert_equal("Liquid error: Memory limits exceeded", t.render)
|
162
|
+
t.resource_limits.render_length_limit = 7
|
163
|
+
assert_equal("aaaabbb", t.render)
|
180
164
|
|
181
165
|
t = Template.parse("{% if true %}a{% endif %}{% if true %}b{% endif %}{% if true %}a{% endif %}{% if true %}b{% endif %}{% if true %}a{% endif %}{% if true %}b{% endif %}")
|
182
166
|
t.resource_limits.render_length_limit = 5
|
183
|
-
assert_equal
|
184
|
-
t.resource_limits.render_length_limit =
|
185
|
-
assert_equal
|
186
|
-
|
187
|
-
|
167
|
+
assert_equal("Liquid error: Memory limits exceeded", t.render)
|
168
|
+
t.resource_limits.render_length_limit = 6
|
169
|
+
assert_equal("ababab", t.render)
|
170
|
+
end
|
171
|
+
|
172
|
+
def test_render_length_uses_number_of_bytes_not_characters
|
173
|
+
t = Template.parse("{% if true %}すごい{% endif %}")
|
174
|
+
t.resource_limits.render_length_limit = 8
|
175
|
+
assert_equal("Liquid error: Memory limits exceeded", t.render)
|
176
|
+
t.resource_limits.render_length_limit = 9
|
177
|
+
assert_equal("すごい", t.render)
|
188
178
|
end
|
189
179
|
|
190
180
|
def test_default_resource_limits_unaffected_by_render_with_context
|
191
181
|
context = Context.new
|
192
|
-
t = Template.parse("{% for a in (1..100) %}
|
182
|
+
t = Template.parse("{% for a in (1..100) %}x{% assign foo = 1 %} {% endfor %}")
|
193
183
|
t.render!(context)
|
194
|
-
assert
|
195
|
-
assert
|
196
|
-
assert context.resource_limits.render_length > 0
|
184
|
+
assert(context.resource_limits.assign_score > 0)
|
185
|
+
assert(context.resource_limits.render_score > 0)
|
197
186
|
end
|
198
187
|
|
199
188
|
def test_can_use_drop_as_context
|
200
189
|
t = Template.new
|
201
190
|
t.registers['lulz'] = 'haha'
|
202
191
|
drop = TemplateContextDrop.new
|
203
|
-
assert_equal
|
204
|
-
assert_equal
|
205
|
-
assert_equal
|
192
|
+
assert_equal('fizzbuzz', t.parse('{{foo}}').render!(drop))
|
193
|
+
assert_equal('bar', t.parse('{{bar}}').render!(drop))
|
194
|
+
assert_equal('haha', t.parse("{{baz}}").render!(drop))
|
206
195
|
end
|
207
196
|
|
208
197
|
def test_render_bang_force_rethrow_errors_on_passed_context
|
209
|
-
context = Context.new(
|
198
|
+
context = Context.new('drop' => ErroneousDrop.new)
|
210
199
|
t = Template.new.parse('{{ drop.bad_method }}')
|
211
200
|
|
212
|
-
e = assert_raises
|
201
|
+
e = assert_raises(RuntimeError) do
|
213
202
|
t.render!(context)
|
214
203
|
end
|
215
|
-
assert_equal
|
204
|
+
assert_equal('ruby error in drop', e.message)
|
216
205
|
end
|
217
206
|
|
218
207
|
def test_exception_renderer_that_returns_string
|
219
208
|
exception = nil
|
220
|
-
handler
|
209
|
+
handler = ->(e) {
|
210
|
+
exception = e
|
211
|
+
'<!-- error -->'
|
212
|
+
}
|
221
213
|
|
222
214
|
output = Template.parse("{{ 1 | divided_by: 0 }}").render({}, exception_renderer: handler)
|
223
215
|
|
224
|
-
assert
|
225
|
-
assert_equal
|
216
|
+
assert(exception.is_a?(Liquid::ZeroDivisionError))
|
217
|
+
assert_equal('<!-- error -->', output)
|
226
218
|
end
|
227
219
|
|
228
220
|
def test_exception_renderer_that_raises
|
229
221
|
exception = nil
|
230
222
|
assert_raises(Liquid::ZeroDivisionError) do
|
231
|
-
Template.parse("{{ 1 | divided_by: 0 }}").render({}, exception_renderer: ->(e) {
|
223
|
+
Template.parse("{{ 1 | divided_by: 0 }}").render({}, exception_renderer: ->(e) {
|
224
|
+
exception = e
|
225
|
+
raise
|
226
|
+
})
|
232
227
|
end
|
233
|
-
assert
|
228
|
+
assert(exception.is_a?(Liquid::ZeroDivisionError))
|
234
229
|
end
|
235
230
|
|
236
231
|
def test_global_filter_option_on_render
|
237
232
|
global_filter_proc = ->(output) { "#{output} filtered" }
|
238
|
-
rendered_template
|
233
|
+
rendered_template = Template.parse("{{name}}").render({ "name" => "bob" }, global_filter: global_filter_proc)
|
239
234
|
|
240
|
-
assert_equal
|
235
|
+
assert_equal('bob filtered', rendered_template)
|
241
236
|
end
|
242
237
|
|
243
238
|
def test_global_filter_option_when_native_filters_exist
|
244
239
|
global_filter_proc = ->(output) { "#{output} filtered" }
|
245
|
-
rendered_template
|
240
|
+
rendered_template = Template.parse("{{name | upcase}}").render({ "name" => "bob" }, global_filter: global_filter_proc)
|
246
241
|
|
247
|
-
assert_equal
|
242
|
+
assert_equal('BOB filtered', rendered_template)
|
248
243
|
end
|
249
244
|
|
250
245
|
def test_undefined_variables
|
251
|
-
t
|
252
|
-
result = t.render({ 'x' => 33, 'z' => { 'a' => 32, 'c' => { 'e' => 31 } } },
|
246
|
+
t = Template.parse("{{x}} {{y}} {{z.a}} {{z.b}} {{z.c.d}}")
|
247
|
+
result = t.render({ 'x' => 33, 'z' => { 'a' => 32, 'c' => { 'e' => 31 } } }, strict_variables: true)
|
253
248
|
|
254
|
-
assert_equal
|
255
|
-
assert_equal
|
256
|
-
assert_instance_of
|
257
|
-
assert_equal
|
258
|
-
assert_instance_of
|
259
|
-
assert_equal
|
260
|
-
assert_instance_of
|
261
|
-
assert_equal
|
249
|
+
assert_equal('33 32 ', result)
|
250
|
+
assert_equal(3, t.errors.count)
|
251
|
+
assert_instance_of(Liquid::UndefinedVariable, t.errors[0])
|
252
|
+
assert_equal('Liquid error: undefined variable y', t.errors[0].message)
|
253
|
+
assert_instance_of(Liquid::UndefinedVariable, t.errors[1])
|
254
|
+
assert_equal('Liquid error: undefined variable b', t.errors[1].message)
|
255
|
+
assert_instance_of(Liquid::UndefinedVariable, t.errors[2])
|
256
|
+
assert_equal('Liquid error: undefined variable d', t.errors[2].message)
|
262
257
|
end
|
263
258
|
|
264
259
|
def test_nil_value_does_not_raise
|
265
|
-
|
266
|
-
t = Template.parse("some{{x}}thing")
|
260
|
+
t = Template.parse("some{{x}}thing", error_mode: :strict)
|
267
261
|
result = t.render!({ 'x' => nil }, strict_variables: true)
|
268
262
|
|
269
|
-
assert_equal
|
270
|
-
assert_equal
|
263
|
+
assert_equal(0, t.errors.count)
|
264
|
+
assert_equal('something', result)
|
271
265
|
end
|
272
266
|
|
273
267
|
def test_undefined_variables_raise
|
274
268
|
t = Template.parse("{{x}} {{y}} {{z.a}} {{z.b}} {{z.c.d}}")
|
275
269
|
|
276
|
-
assert_raises
|
277
|
-
t.render!({ 'x' => 33, 'z' => { 'a' => 32, 'c' => { 'e' => 31 } } },
|
270
|
+
assert_raises(UndefinedVariable) do
|
271
|
+
t.render!({ 'x' => 33, 'z' => { 'a' => 32, 'c' => { 'e' => 31 } } }, strict_variables: true)
|
278
272
|
end
|
279
273
|
end
|
280
274
|
|
281
275
|
def test_undefined_drop_methods
|
282
276
|
d = DropWithUndefinedMethod.new
|
283
277
|
t = Template.new.parse('{{ foo }} {{ woot }}')
|
284
|
-
result = t.render(d,
|
278
|
+
result = t.render(d, strict_variables: true)
|
285
279
|
|
286
|
-
assert_equal
|
287
|
-
assert_equal
|
288
|
-
assert_instance_of
|
280
|
+
assert_equal('foo ', result)
|
281
|
+
assert_equal(1, t.errors.count)
|
282
|
+
assert_instance_of(Liquid::UndefinedDropMethod, t.errors[0])
|
289
283
|
end
|
290
284
|
|
291
285
|
def test_undefined_drop_methods_raise
|
292
286
|
d = DropWithUndefinedMethod.new
|
293
287
|
t = Template.new.parse('{{ foo }} {{ woot }}')
|
294
288
|
|
295
|
-
assert_raises
|
296
|
-
t.render!(d,
|
289
|
+
assert_raises(UndefinedDropMethod) do
|
290
|
+
t.render!(d, strict_variables: true)
|
297
291
|
end
|
298
292
|
end
|
299
293
|
|
@@ -304,29 +298,43 @@ class TemplateTest < Minitest::Test
|
|
304
298
|
"-#{v}-"
|
305
299
|
end
|
306
300
|
end
|
307
|
-
result = t.render({ 'a' => 123, 'x' => 'foo' },
|
301
|
+
result = t.render({ 'a' => 123, 'x' => 'foo' }, filters: [filters], strict_filters: true)
|
308
302
|
|
309
|
-
assert_equal
|
310
|
-
assert_equal
|
311
|
-
assert_instance_of
|
312
|
-
assert_equal
|
303
|
+
assert_equal('123 ', result)
|
304
|
+
assert_equal(1, t.errors.count)
|
305
|
+
assert_instance_of(Liquid::UndefinedFilter, t.errors[0])
|
306
|
+
assert_equal('Liquid error: undefined filter somefilter1', t.errors[0].message)
|
313
307
|
end
|
314
308
|
|
315
309
|
def test_undefined_filters_raise
|
316
310
|
t = Template.parse("{{x | somefilter1 | upcase | somefilter2}}")
|
317
311
|
|
318
|
-
assert_raises
|
319
|
-
t.render!({ 'x' => 'foo' },
|
312
|
+
assert_raises(UndefinedFilter) do
|
313
|
+
t.render!({ 'x' => 'foo' }, strict_filters: true)
|
320
314
|
end
|
321
315
|
end
|
322
316
|
|
323
317
|
def test_using_range_literal_works_as_expected
|
324
318
|
t = Template.parse("{% assign foo = (x..y) %}{{ foo }}")
|
325
|
-
result = t.render(
|
326
|
-
assert_equal
|
319
|
+
result = t.render('x' => 1, 'y' => 5)
|
320
|
+
assert_equal('1..5', result)
|
327
321
|
|
328
322
|
t = Template.parse("{% assign nums = (x..y) %}{% for num in nums %}{{ num }}{% endfor %}")
|
329
|
-
result = t.render(
|
330
|
-
assert_equal
|
323
|
+
result = t.render('x' => 1, 'y' => 5)
|
324
|
+
assert_equal('12345', result)
|
325
|
+
end
|
326
|
+
|
327
|
+
def test_source_string_subclass
|
328
|
+
string_subclass = Class.new(String) do
|
329
|
+
# E.g. ActiveSupport::SafeBuffer does this, so don't just rely on to_s to return a String
|
330
|
+
def to_s
|
331
|
+
self
|
332
|
+
end
|
333
|
+
end
|
334
|
+
source = string_subclass.new("{% assign x = 2 -%} x= {{- x }}")
|
335
|
+
assert_instance_of(string_subclass, source)
|
336
|
+
output = Template.parse(source).render!
|
337
|
+
assert_equal("x=2", output)
|
338
|
+
assert_instance_of(String, output)
|
331
339
|
end
|
332
340
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'test_helper'
|
2
4
|
|
3
5
|
class TrimModeTest < Minitest::Test
|
@@ -67,7 +69,7 @@ class TrimModeTest < Minitest::Test
|
|
67
69
|
# Make sure the trim isn't applied to standard tags
|
68
70
|
def test_standard_tags
|
69
71
|
whitespace = ' '
|
70
|
-
text
|
72
|
+
text = <<-END_TEMPLATE
|
71
73
|
<div>
|
72
74
|
<p>
|
73
75
|
{% if true %}
|
@@ -76,14 +78,14 @@ class TrimModeTest < Minitest::Test
|
|
76
78
|
</p>
|
77
79
|
</div>
|
78
80
|
END_TEMPLATE
|
79
|
-
expected =
|
80
|
-
|
81
|
-
|
82
|
-
#{whitespace}
|
83
|
-
|
84
|
-
#{whitespace}
|
85
|
-
|
86
|
-
|
81
|
+
expected = <<~END_EXPECTED
|
82
|
+
<div>
|
83
|
+
<p>
|
84
|
+
#{whitespace}
|
85
|
+
yes
|
86
|
+
#{whitespace}
|
87
|
+
</p>
|
88
|
+
</div>
|
87
89
|
END_EXPECTED
|
88
90
|
assert_template_result(expected, text)
|
89
91
|
|
@@ -96,70 +98,70 @@ class TrimModeTest < Minitest::Test
|
|
96
98
|
</p>
|
97
99
|
</div>
|
98
100
|
END_TEMPLATE
|
99
|
-
expected =
|
100
|
-
|
101
|
-
|
102
|
-
#{whitespace}
|
103
|
-
|
104
|
-
|
101
|
+
expected = <<~END_EXPECTED
|
102
|
+
<div>
|
103
|
+
<p>
|
104
|
+
#{whitespace}
|
105
|
+
</p>
|
106
|
+
</div>
|
105
107
|
END_EXPECTED
|
106
108
|
assert_template_result(expected, text)
|
107
109
|
end
|
108
110
|
|
109
111
|
# Make sure the trim isn't too agressive
|
110
112
|
def test_no_trim_output
|
111
|
-
text
|
113
|
+
text = '<p>{{- \'John\' -}}</p>'
|
112
114
|
expected = '<p>John</p>'
|
113
115
|
assert_template_result(expected, text)
|
114
116
|
end
|
115
117
|
|
116
118
|
# Make sure the trim isn't too agressive
|
117
119
|
def test_no_trim_tags
|
118
|
-
text
|
120
|
+
text = '<p>{%- if true -%}yes{%- endif -%}</p>'
|
119
121
|
expected = '<p>yes</p>'
|
120
122
|
assert_template_result(expected, text)
|
121
123
|
|
122
|
-
text
|
124
|
+
text = '<p>{%- if false -%}no{%- endif -%}</p>'
|
123
125
|
expected = '<p></p>'
|
124
126
|
assert_template_result(expected, text)
|
125
127
|
end
|
126
128
|
|
127
129
|
def test_single_line_outer_tag
|
128
|
-
text
|
130
|
+
text = '<p> {%- if true %} yes {% endif -%} </p>'
|
129
131
|
expected = '<p> yes </p>'
|
130
132
|
assert_template_result(expected, text)
|
131
133
|
|
132
|
-
text
|
134
|
+
text = '<p> {%- if false %} no {% endif -%} </p>'
|
133
135
|
expected = '<p></p>'
|
134
136
|
assert_template_result(expected, text)
|
135
137
|
end
|
136
138
|
|
137
139
|
def test_single_line_inner_tag
|
138
|
-
text
|
140
|
+
text = '<p> {% if true -%} yes {%- endif %} </p>'
|
139
141
|
expected = '<p> yes </p>'
|
140
142
|
assert_template_result(expected, text)
|
141
143
|
|
142
|
-
text
|
144
|
+
text = '<p> {% if false -%} no {%- endif %} </p>'
|
143
145
|
expected = '<p> </p>'
|
144
146
|
assert_template_result(expected, text)
|
145
147
|
end
|
146
148
|
|
147
149
|
def test_single_line_post_tag
|
148
|
-
text
|
150
|
+
text = '<p> {% if true -%} yes {% endif -%} </p>'
|
149
151
|
expected = '<p> yes </p>'
|
150
152
|
assert_template_result(expected, text)
|
151
153
|
|
152
|
-
text
|
154
|
+
text = '<p> {% if false -%} no {% endif -%} </p>'
|
153
155
|
expected = '<p> </p>'
|
154
156
|
assert_template_result(expected, text)
|
155
157
|
end
|
156
158
|
|
157
159
|
def test_single_line_pre_tag
|
158
|
-
text
|
160
|
+
text = '<p> {%- if true %} yes {%- endif %} </p>'
|
159
161
|
expected = '<p> yes </p>'
|
160
162
|
assert_template_result(expected, text)
|
161
163
|
|
162
|
-
text
|
164
|
+
text = '<p> {%- if false %} no {%- endif %} </p>'
|
163
165
|
expected = '<p> </p>'
|
164
166
|
assert_template_result(expected, text)
|
165
167
|
end
|
@@ -328,7 +330,7 @@ class TrimModeTest < Minitest::Test
|
|
328
330
|
assert_template_result(expected, text)
|
329
331
|
|
330
332
|
whitespace = ' '
|
331
|
-
text
|
333
|
+
text = <<-END_TEMPLATE
|
332
334
|
<div>
|
333
335
|
<p>
|
334
336
|
{% if false -%}
|
@@ -337,12 +339,12 @@ class TrimModeTest < Minitest::Test
|
|
337
339
|
</p>
|
338
340
|
</div>
|
339
341
|
END_TEMPLATE
|
340
|
-
expected =
|
341
|
-
|
342
|
-
|
343
|
-
#{whitespace}
|
344
|
-
|
345
|
-
|
342
|
+
expected = <<~END_EXPECTED
|
343
|
+
<div>
|
344
|
+
<p>
|
345
|
+
#{whitespace}
|
346
|
+
</p>
|
347
|
+
</div>
|
346
348
|
END_EXPECTED
|
347
349
|
assert_template_result(expected, text)
|
348
350
|
end
|
@@ -502,7 +504,7 @@ class TrimModeTest < Minitest::Test
|
|
502
504
|
|
503
505
|
def test_raw_output
|
504
506
|
whitespace = ' '
|
505
|
-
text
|
507
|
+
text = <<-END_TEMPLATE
|
506
508
|
<div>
|
507
509
|
{% raw %}
|
508
510
|
{%- if true -%}
|
@@ -513,17 +515,49 @@ class TrimModeTest < Minitest::Test
|
|
513
515
|
{% endraw %}
|
514
516
|
</div>
|
515
517
|
END_TEMPLATE
|
516
|
-
expected =
|
517
|
-
|
518
|
-
#{whitespace}
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
#{whitespace}
|
525
|
-
|
518
|
+
expected = <<~END_EXPECTED
|
519
|
+
<div>
|
520
|
+
#{whitespace}
|
521
|
+
{%- if true -%}
|
522
|
+
<p>
|
523
|
+
{{- 'John' -}}
|
524
|
+
</p>
|
525
|
+
{%- endif -%}
|
526
|
+
#{whitespace}
|
527
|
+
</div>
|
526
528
|
END_EXPECTED
|
527
529
|
assert_template_result(expected, text)
|
528
530
|
end
|
531
|
+
|
532
|
+
def test_pre_trim_blank_preceding_text
|
533
|
+
template = Liquid::Template.parse("\n{%- raw %}{% endraw %}")
|
534
|
+
assert_equal("", template.render)
|
535
|
+
|
536
|
+
template = Liquid::Template.parse("\n{%- if true %}{% endif %}")
|
537
|
+
assert_equal("", template.render)
|
538
|
+
|
539
|
+
template = Liquid::Template.parse("{{ 'B' }} \n{%- if true %}C{% endif %}")
|
540
|
+
assert_equal("BC", template.render)
|
541
|
+
end
|
542
|
+
|
543
|
+
def test_bug_compatible_pre_trim
|
544
|
+
template = Liquid::Template.parse("\n {%- raw %}{% endraw %}", bug_compatible_whitespace_trimming: true)
|
545
|
+
assert_equal("\n", template.render)
|
546
|
+
|
547
|
+
template = Liquid::Template.parse("\n {%- if true %}{% endif %}", bug_compatible_whitespace_trimming: true)
|
548
|
+
assert_equal("\n", template.render)
|
549
|
+
|
550
|
+
template = Liquid::Template.parse("{{ 'B' }} \n{%- if true %}C{% endif %}", bug_compatible_whitespace_trimming: true)
|
551
|
+
assert_equal("B C", template.render)
|
552
|
+
|
553
|
+
template = Liquid::Template.parse("B\n {%- raw %}{% endraw %}", bug_compatible_whitespace_trimming: true)
|
554
|
+
assert_equal("B", template.render)
|
555
|
+
|
556
|
+
template = Liquid::Template.parse("B\n {%- if true %}{% endif %}", bug_compatible_whitespace_trimming: true)
|
557
|
+
assert_equal("B", template.render)
|
558
|
+
end
|
559
|
+
|
560
|
+
def test_trim_blank
|
561
|
+
assert_template_result('foobar', 'foo {{-}} bar')
|
562
|
+
end
|
529
563
|
end # TrimModeTest
|