mirror of
https://github.com/Shopify/liquid.git
synced 2026-09-15 08:50:45 -07:00
Merge branch 'master' into sort-numeric
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class AssignTest < Minitest::Test
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class FoobarTag < Liquid::Tag
|
||||
def render(*args)
|
||||
" "
|
||||
def render_to_output_buffer(_context, output)
|
||||
output << ' '
|
||||
output
|
||||
end
|
||||
|
||||
Liquid::Template.register_tag('foobar', FoobarTag)
|
||||
end
|
||||
|
||||
class BlankTestFileSystem
|
||||
@@ -31,7 +32,9 @@ class BlankTest < Minitest::Test
|
||||
end
|
||||
|
||||
def test_new_tags_are_not_blank_by_default
|
||||
assert_template_result(" " * N, wrap_in_for("{% foobar %}"))
|
||||
with_custom_tag('foobar', FoobarTag) do
|
||||
assert_template_result(" " * N, wrap_in_for("{% foobar %}"))
|
||||
end
|
||||
end
|
||||
|
||||
def test_loops_are_blank
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class BlockTest < Minitest::Test
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class CaptureTest < Minitest::Test
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class ContextTest < Minitest::Test
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class DocumentTest < Minitest::Test
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class ContextDrop < Liquid::Drop
|
||||
@@ -31,7 +33,7 @@ class ProductDrop < Liquid::Drop
|
||||
|
||||
class CatchallDrop < Liquid::Drop
|
||||
def liquid_method_missing(method)
|
||||
'catchall_method: ' << method.to_s
|
||||
"catchall_method: #{method}"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -48,7 +50,7 @@ class ProductDrop < Liquid::Drop
|
||||
end
|
||||
|
||||
def user_input
|
||||
"foo".taint
|
||||
(+"foo").taint
|
||||
end
|
||||
|
||||
protected
|
||||
@@ -201,9 +203,9 @@ class DropsTest < Minitest::Test
|
||||
end
|
||||
|
||||
def test_scope_though_proc
|
||||
assert_equal '1', Liquid::Template.parse('{{ s }}').render!('context' => ContextDrop.new, 's' => proc{ |c| c['context.scopes'] })
|
||||
assert_equal '2', Liquid::Template.parse('{%for i in dummy%}{{ s }}{%endfor%}').render!('context' => ContextDrop.new, 's' => proc{ |c| c['context.scopes'] }, 'dummy' => [1])
|
||||
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])
|
||||
assert_equal '1', Liquid::Template.parse('{{ s }}').render!('context' => ContextDrop.new, 's' => proc { |c| c['context.scopes'] })
|
||||
assert_equal '2', Liquid::Template.parse('{%for i in dummy%}{{ s }}{%endfor%}').render!('context' => ContextDrop.new, 's' => proc { |c| c['context.scopes'] }, 'dummy' => [1])
|
||||
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])
|
||||
end
|
||||
|
||||
def test_scope_with_assigns
|
||||
@@ -241,7 +243,7 @@ class DropsTest < Minitest::Test
|
||||
end
|
||||
|
||||
def test_some_enumerable_methods_still_get_invoked
|
||||
[ :count, :max ].each do |method|
|
||||
[:count, :max].each do |method|
|
||||
assert_equal "3", Liquid::Template.parse("{{collection.#{method}}}").render!('collection' => RealEnumerableDrop.new)
|
||||
assert_equal "3", Liquid::Template.parse("{{collection[\"#{method}\"]}}").render!('collection' => RealEnumerableDrop.new)
|
||||
assert_equal "3", Liquid::Template.parse("{{collection.#{method}}}").render!('collection' => EnumerableDrop.new)
|
||||
@@ -250,7 +252,7 @@ class DropsTest < Minitest::Test
|
||||
|
||||
assert_equal "yes", Liquid::Template.parse("{% if collection contains 3 %}yes{% endif %}").render!('collection' => RealEnumerableDrop.new)
|
||||
|
||||
[ :min, :first ].each do |method|
|
||||
[:min, :first].each do |method|
|
||||
assert_equal "1", Liquid::Template.parse("{{collection.#{method}}}").render!('collection' => RealEnumerableDrop.new)
|
||||
assert_equal "1", Liquid::Template.parse("{{collection[\"#{method}\"]}}").render!('collection' => RealEnumerableDrop.new)
|
||||
assert_equal "1", Liquid::Template.parse("{{collection.#{method}}}").render!('collection' => EnumerableDrop.new)
|
||||
@@ -270,4 +272,11 @@ class DropsTest < Minitest::Test
|
||||
assert_equal 'ProductDrop', Liquid::Template.parse("{{ product }}").render!('product' => ProductDrop.new)
|
||||
assert_equal 'EnumerableDrop', Liquid::Template.parse('{{ collection }}').render!('collection' => EnumerableDrop.new)
|
||||
end
|
||||
|
||||
def test_invokable_methods
|
||||
assert_equal %w(to_liquid catchall user_input context texts).to_set, ProductDrop.invokable_methods
|
||||
assert_equal %w(to_liquid scopes_as_array loop_pos scopes).to_set, ContextDrop.invokable_methods
|
||||
assert_equal %w(to_liquid size max min first count).to_set, EnumerableDrop.invokable_methods
|
||||
assert_equal %w(to_liquid max min sort count first).to_set, RealEnumerableDrop.invokable_methods
|
||||
end
|
||||
end # DropsTest
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class ErrorHandlingTest < Minitest::Test
|
||||
@@ -83,15 +85,14 @@ class ErrorHandlingTest < Minitest::Test
|
||||
|
||||
def test_with_line_numbers_adds_numbers_to_parser_errors
|
||||
err = assert_raises(SyntaxError) do
|
||||
Liquid::Template.parse(%q(
|
||||
Liquid::Template.parse('
|
||||
foobar
|
||||
|
||||
{% "cat" | foobar %}
|
||||
|
||||
bla
|
||||
),
|
||||
line_numbers: true
|
||||
)
|
||||
',
|
||||
line_numbers: true)
|
||||
end
|
||||
|
||||
assert_match(/Liquid syntax error \(line 4\)/, err.message)
|
||||
@@ -99,15 +100,14 @@ class ErrorHandlingTest < Minitest::Test
|
||||
|
||||
def test_with_line_numbers_adds_numbers_to_parser_errors_with_whitespace_trim
|
||||
err = assert_raises(SyntaxError) do
|
||||
Liquid::Template.parse(%q(
|
||||
Liquid::Template.parse('
|
||||
foobar
|
||||
|
||||
{%- "cat" | foobar -%}
|
||||
|
||||
bla
|
||||
),
|
||||
line_numbers: true
|
||||
)
|
||||
',
|
||||
line_numbers: true)
|
||||
end
|
||||
|
||||
assert_match(/Liquid syntax error \(line 4\)/, err.message)
|
||||
@@ -122,8 +122,7 @@ class ErrorHandlingTest < Minitest::Test
|
||||
bla
|
||||
',
|
||||
error_mode: :warn,
|
||||
line_numbers: true
|
||||
)
|
||||
line_numbers: true)
|
||||
|
||||
assert_equal ['Liquid syntax error (line 4): Unexpected character = in "1 =! 2"'],
|
||||
template.warnings.map(&:message)
|
||||
@@ -139,8 +138,7 @@ class ErrorHandlingTest < Minitest::Test
|
||||
bla
|
||||
',
|
||||
error_mode: :strict,
|
||||
line_numbers: true
|
||||
)
|
||||
line_numbers: true)
|
||||
end
|
||||
|
||||
assert_equal 'Liquid syntax error (line 4): Unexpected character = in "1 =! 2"', err.message
|
||||
@@ -157,8 +155,7 @@ class ErrorHandlingTest < Minitest::Test
|
||||
|
||||
bla
|
||||
',
|
||||
line_numbers: true
|
||||
)
|
||||
line_numbers: true)
|
||||
end
|
||||
|
||||
assert_equal "Liquid syntax error (line 5): Unknown tag 'foo'", err.message
|
||||
@@ -205,7 +202,7 @@ class ErrorHandlingTest < Minitest::Test
|
||||
def test_default_exception_renderer_with_internal_error
|
||||
template = Liquid::Template.parse('This is a runtime error: {{ errors.runtime_error }}', line_numbers: true)
|
||||
|
||||
output = template.render({ 'errors' => ErrorDrop.new })
|
||||
output = template.render('errors' => ErrorDrop.new)
|
||||
|
||||
assert_equal 'This is a runtime error: Liquid error (line 1): internal', output
|
||||
assert_equal [Liquid::InternalError], template.errors.map(&:class)
|
||||
@@ -214,10 +211,13 @@ class ErrorHandlingTest < Minitest::Test
|
||||
def test_setting_default_exception_renderer
|
||||
old_exception_renderer = Liquid::Template.default_exception_renderer
|
||||
exceptions = []
|
||||
Liquid::Template.default_exception_renderer = ->(e) { exceptions << e; '' }
|
||||
Liquid::Template.default_exception_renderer = ->(e) {
|
||||
exceptions << e
|
||||
''
|
||||
}
|
||||
template = Liquid::Template.parse('This is a runtime error: {{ errors.argument_error }}')
|
||||
|
||||
output = template.render({ 'errors' => ErrorDrop.new })
|
||||
output = template.render('errors' => ErrorDrop.new)
|
||||
|
||||
assert_equal 'This is a runtime error: ', output
|
||||
assert_equal [Liquid::ArgumentError], template.errors.map(&:class)
|
||||
@@ -228,7 +228,10 @@ class ErrorHandlingTest < Minitest::Test
|
||||
def test_exception_renderer_exposing_non_liquid_error
|
||||
template = Liquid::Template.parse('This is a runtime error: {{ errors.runtime_error }}', line_numbers: true)
|
||||
exceptions = []
|
||||
handler = ->(e) { exceptions << e; e.cause }
|
||||
handler = ->(e) {
|
||||
exceptions << e
|
||||
e.cause
|
||||
}
|
||||
|
||||
output = template.render({ 'errors' => ErrorDrop.new }, exception_renderer: handler)
|
||||
|
||||
@@ -239,7 +242,7 @@ class ErrorHandlingTest < Minitest::Test
|
||||
end
|
||||
|
||||
class TestFileSystem
|
||||
def read_template_file(template_path)
|
||||
def read_template_file(_template_path)
|
||||
"{{ errors.argument_error }}"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,24 +1,26 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
module MoneyFilter
|
||||
def money(input)
|
||||
sprintf(' %d$ ', input)
|
||||
format(' %d$ ', input)
|
||||
end
|
||||
|
||||
def money_with_underscore(input)
|
||||
sprintf(' %d$ ', input)
|
||||
format(' %d$ ', input)
|
||||
end
|
||||
end
|
||||
|
||||
module CanadianMoneyFilter
|
||||
def money(input)
|
||||
sprintf(' %d$ CAD ', input)
|
||||
format(' %d$ CAD ', input)
|
||||
end
|
||||
end
|
||||
|
||||
module SubstituteFilter
|
||||
def substitute(input, params = {})
|
||||
input.gsub(/%\{(\w+)\}/) { |match| params[$1] }
|
||||
input.gsub(/%\{(\w+)\}/) { |_match| params[Regexp.last_match(1)] }
|
||||
end
|
||||
end
|
||||
|
||||
@@ -26,7 +28,7 @@ class FiltersTest < Minitest::Test
|
||||
include Liquid
|
||||
|
||||
module OverrideObjectMethodFilter
|
||||
def tap(input)
|
||||
def tap(_input)
|
||||
"tap overridden"
|
||||
end
|
||||
end
|
||||
@@ -149,7 +151,7 @@ class FiltersTest < Minitest::Test
|
||||
assert_equal "tap overridden", Template.parse("{{var | tap}}").render!({ 'var' => 1000 }, filters: [OverrideObjectMethodFilter])
|
||||
|
||||
# tap still treated as a non-existent filter
|
||||
assert_equal "1000", Template.parse("{{var | tap}}").render!({ 'var' => 1000 })
|
||||
assert_equal "1000", Template.parse("{{var | tap}}").render!('var' => 1000)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class HashOrderingTest < Minitest::Test
|
||||
module MoneyFilter
|
||||
def money(input)
|
||||
sprintf(' %d$ ', input)
|
||||
format(' %d$ ', input)
|
||||
end
|
||||
end
|
||||
|
||||
module CanadianMoneyFilter
|
||||
def money(input)
|
||||
sprintf(' %d$ CAD ', input)
|
||||
format(' %d$ CAD ', input)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
module FunnyFilter
|
||||
def make_funny(input)
|
||||
def make_funny(_input)
|
||||
'LOL'
|
||||
end
|
||||
|
||||
@@ -32,7 +34,7 @@ class OutputTest < Minitest::Test
|
||||
def setup
|
||||
@assigns = {
|
||||
'best_cars' => 'bmw',
|
||||
'car' => { 'bmw' => 'good', 'gm' => 'bad' }
|
||||
'car' => { 'bmw' => 'good', 'gm' => 'bad' },
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
@@ -0,0 +1,247 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class ParseTreeVisitorTest < Minitest::Test
|
||||
include Liquid
|
||||
|
||||
def test_variable
|
||||
assert_equal(
|
||||
["test"],
|
||||
visit(%({{ test }}))
|
||||
)
|
||||
end
|
||||
|
||||
def test_varible_with_filter
|
||||
assert_equal(
|
||||
["test", "infilter"],
|
||||
visit(%({{ test | split: infilter }}))
|
||||
)
|
||||
end
|
||||
|
||||
def test_dynamic_variable
|
||||
assert_equal(
|
||||
["test", "inlookup"],
|
||||
visit(%({{ test[inlookup] }}))
|
||||
)
|
||||
end
|
||||
|
||||
def test_if_condition
|
||||
assert_equal(
|
||||
["test"],
|
||||
visit(%({% if test %}{% endif %}))
|
||||
)
|
||||
end
|
||||
|
||||
def test_complex_if_condition
|
||||
assert_equal(
|
||||
["test"],
|
||||
visit(%({% if 1 == 1 and 2 == test %}{% endif %}))
|
||||
)
|
||||
end
|
||||
|
||||
def test_if_body
|
||||
assert_equal(
|
||||
["test"],
|
||||
visit(%({% if 1 == 1 %}{{ test }}{% endif %}))
|
||||
)
|
||||
end
|
||||
|
||||
def test_unless_condition
|
||||
assert_equal(
|
||||
["test"],
|
||||
visit(%({% unless test %}{% endunless %}))
|
||||
)
|
||||
end
|
||||
|
||||
def test_complex_unless_condition
|
||||
assert_equal(
|
||||
["test"],
|
||||
visit(%({% unless 1 == 1 and 2 == test %}{% endunless %}))
|
||||
)
|
||||
end
|
||||
|
||||
def test_unless_body
|
||||
assert_equal(
|
||||
["test"],
|
||||
visit(%({% unless 1 == 1 %}{{ test }}{% endunless %}))
|
||||
)
|
||||
end
|
||||
|
||||
def test_elsif_condition
|
||||
assert_equal(
|
||||
["test"],
|
||||
visit(%({% if 1 == 1 %}{% elsif test %}{% endif %}))
|
||||
)
|
||||
end
|
||||
|
||||
def test_complex_elsif_condition
|
||||
assert_equal(
|
||||
["test"],
|
||||
visit(%({% if 1 == 1 %}{% elsif 1 == 1 and 2 == test %}{% endif %}))
|
||||
)
|
||||
end
|
||||
|
||||
def test_elsif_body
|
||||
assert_equal(
|
||||
["test"],
|
||||
visit(%({% if 1 == 1 %}{% elsif 2 == 2 %}{{ test }}{% endif %}))
|
||||
)
|
||||
end
|
||||
|
||||
def test_else_body
|
||||
assert_equal(
|
||||
["test"],
|
||||
visit(%({% if 1 == 1 %}{% else %}{{ test }}{% endif %}))
|
||||
)
|
||||
end
|
||||
|
||||
def test_case_left
|
||||
assert_equal(
|
||||
["test"],
|
||||
visit(%({% case test %}{% endcase %}))
|
||||
)
|
||||
end
|
||||
|
||||
def test_case_condition
|
||||
assert_equal(
|
||||
["test"],
|
||||
visit(%({% case 1 %}{% when test %}{% endcase %}))
|
||||
)
|
||||
end
|
||||
|
||||
def test_case_when_body
|
||||
assert_equal(
|
||||
["test"],
|
||||
visit(%({% case 1 %}{% when 2 %}{{ test }}{% endcase %}))
|
||||
)
|
||||
end
|
||||
|
||||
def test_case_else_body
|
||||
assert_equal(
|
||||
["test"],
|
||||
visit(%({% case 1 %}{% else %}{{ test }}{% endcase %}))
|
||||
)
|
||||
end
|
||||
|
||||
def test_for_in
|
||||
assert_equal(
|
||||
["test"],
|
||||
visit(%({% for x in test %}{% endfor %}))
|
||||
)
|
||||
end
|
||||
|
||||
def test_for_limit
|
||||
assert_equal(
|
||||
["test"],
|
||||
visit(%({% for x in (1..5) limit: test %}{% endfor %}))
|
||||
)
|
||||
end
|
||||
|
||||
def test_for_offset
|
||||
assert_equal(
|
||||
["test"],
|
||||
visit(%({% for x in (1..5) offset: test %}{% endfor %}))
|
||||
)
|
||||
end
|
||||
|
||||
def test_for_body
|
||||
assert_equal(
|
||||
["test"],
|
||||
visit(%({% for x in (1..5) %}{{ test }}{% endfor %}))
|
||||
)
|
||||
end
|
||||
|
||||
def test_tablerow_in
|
||||
assert_equal(
|
||||
["test"],
|
||||
visit(%({% tablerow x in test %}{% endtablerow %}))
|
||||
)
|
||||
end
|
||||
|
||||
def test_tablerow_limit
|
||||
assert_equal(
|
||||
["test"],
|
||||
visit(%({% tablerow x in (1..5) limit: test %}{% endtablerow %}))
|
||||
)
|
||||
end
|
||||
|
||||
def test_tablerow_offset
|
||||
assert_equal(
|
||||
["test"],
|
||||
visit(%({% tablerow x in (1..5) offset: test %}{% endtablerow %}))
|
||||
)
|
||||
end
|
||||
|
||||
def test_tablerow_body
|
||||
assert_equal(
|
||||
["test"],
|
||||
visit(%({% tablerow x in (1..5) %}{{ test }}{% endtablerow %}))
|
||||
)
|
||||
end
|
||||
|
||||
def test_cycle
|
||||
assert_equal(
|
||||
["test"],
|
||||
visit(%({% cycle test %}))
|
||||
)
|
||||
end
|
||||
|
||||
def test_assign
|
||||
assert_equal(
|
||||
["test"],
|
||||
visit(%({% assign x = test %}))
|
||||
)
|
||||
end
|
||||
|
||||
def test_capture
|
||||
assert_equal(
|
||||
["test"],
|
||||
visit(%({% capture x %}{{ test }}{% endcapture %}))
|
||||
)
|
||||
end
|
||||
|
||||
def test_include
|
||||
assert_equal(
|
||||
["test"],
|
||||
visit(%({% include test %}))
|
||||
)
|
||||
end
|
||||
|
||||
def test_include_with
|
||||
assert_equal(
|
||||
["test"],
|
||||
visit(%({% include "hai" with test %}))
|
||||
)
|
||||
end
|
||||
|
||||
def test_include_for
|
||||
assert_equal(
|
||||
["test"],
|
||||
visit(%({% include "hai" for test %}))
|
||||
)
|
||||
end
|
||||
|
||||
def test_preserve_tree_structure
|
||||
assert_equal(
|
||||
[[nil, [
|
||||
[nil, [[nil, [["other", []]]]]],
|
||||
["test", []],
|
||||
["xs", []],
|
||||
]]],
|
||||
traversal(%({% for x in xs offset: test %}{{ other }}{% endfor %})).visit
|
||||
)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def traversal(template)
|
||||
ParseTreeVisitor
|
||||
.for(Template.parse(template).root)
|
||||
.add_callback_for(VariableLookup, &:name)
|
||||
end
|
||||
|
||||
def visit(template)
|
||||
traversal(template).visit.flatten.compact
|
||||
end
|
||||
end
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class ParsingQuirksTest < Minitest::Test
|
||||
@@ -99,7 +101,7 @@ class ParsingQuirksTest < Minitest::Test
|
||||
# After the messed up quotes a filter without parameters (reverse) should work
|
||||
# but one with parameters (remove) shouldn't be detected.
|
||||
assert_template_result('here', "{{ 'hi there' | split:\"t\"\" | reverse | first}}")
|
||||
assert_template_result('hi ', "{{ 'hi there' | split:\"t\"\" | remove:\"i\" | first}}")
|
||||
assert_template_result('hi ', "{{ 'hi there' | split:\"t\"\" | remove:\"i\" | first}}")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class DisabledTagsTest < Minitest::Test
|
||||
include Liquid
|
||||
|
||||
class DisableRaw < Block
|
||||
disable_tags "raw"
|
||||
end
|
||||
|
||||
class DisableRawEcho < Block
|
||||
disable_tags "raw", "echo"
|
||||
end
|
||||
|
||||
def test_disables_raw
|
||||
with_custom_tag('disable', DisableRaw) do
|
||||
assert_template_result 'raw usage is not allowed in this contextfoo', '{% disable %}{% raw %}Foobar{% endraw %}{% echo "foo" %}{% enddisable %}'
|
||||
end
|
||||
end
|
||||
|
||||
def test_disables_echo_and_raw
|
||||
with_custom_tag('disable', DisableRawEcho) do
|
||||
assert_template_result 'raw usage is not allowed in this contextecho usage is not allowed in this context', '{% disable %}{% raw %}Foobar{% endraw %}{% echo "foo" %}{% enddisable %}'
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class RenderProfilingTest < Minitest::Test
|
||||
@@ -128,7 +130,7 @@ class RenderProfilingTest < Minitest::Test
|
||||
t.render!
|
||||
|
||||
timing_count = 0
|
||||
t.profiler.each do |timing|
|
||||
t.profiler.each do |_timing|
|
||||
timing_count += 1
|
||||
end
|
||||
|
||||
@@ -145,7 +147,7 @@ class RenderProfilingTest < Minitest::Test
|
||||
|
||||
def test_profiling_marks_children_of_for_blocks
|
||||
t = Template.parse("{% for item in collection %} {{ item }} {% endfor %}", profile: true)
|
||||
t.render!({ "collection" => ["one", "two"] })
|
||||
t.render!("collection" => ["one", "two"])
|
||||
|
||||
assert_equal 1, t.profiler.length
|
||||
# Will profile each invocation of the for block
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
module SecurityFilter
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
# encoding: utf-8
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
@@ -17,7 +18,7 @@ class TestThing
|
||||
"woot: #{@foo}"
|
||||
end
|
||||
|
||||
def [](whatever)
|
||||
def [](_whatever)
|
||||
to_s
|
||||
end
|
||||
|
||||
@@ -37,7 +38,7 @@ class TestEnumerable < Liquid::Drop
|
||||
include Enumerable
|
||||
|
||||
def each(&block)
|
||||
[ { "foo" => 1, "bar" => 2 }, { "foo" => 2, "bar" => 1 }, { "foo" => 3, "bar" => 3 } ].each(&block)
|
||||
[{ "foo" => 1, "bar" => 2 }, { "foo" => 2, "bar" => 1 }, { "foo" => 3, "bar" => 3 }].each(&block)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -158,6 +159,10 @@ class StandardFiltersTest < Minitest::Test
|
||||
assert_equal '1', @filters.url_decode(1)
|
||||
assert_equal '2001-02-03', @filters.url_decode(Date.new(2001, 2, 3))
|
||||
assert_nil @filters.url_decode(nil)
|
||||
exception = assert_raises Liquid::ArgumentError do
|
||||
@filters.url_decode('%ff')
|
||||
end
|
||||
assert_equal 'Liquid error: invalid byte sequence in UTF-8', exception.message
|
||||
end
|
||||
|
||||
def test_truncatewords
|
||||
@@ -177,6 +182,9 @@ class StandardFiltersTest < Minitest::Test
|
||||
assert_equal 'test', @filters.strip_html("<div\nclass='multiline'>test</div>")
|
||||
assert_equal 'test', @filters.strip_html("<!-- foo bar \n test -->test")
|
||||
assert_equal '', @filters.strip_html(nil)
|
||||
|
||||
# Quirk of the existing implementation
|
||||
assert_equal 'foo;', @filters.strip_html("<<<script </script>script>foo;</script>")
|
||||
end
|
||||
|
||||
def test_join
|
||||
@@ -192,9 +200,13 @@ class StandardFiltersTest < Minitest::Test
|
||||
|
||||
def test_sort_numeric
|
||||
assert_equal ['1', '2', '3', '10'], @filters.sort_numeric(['10', '3', '2', '1'])
|
||||
|
||||
assert_equal [{ "a" => '1' }, { "a" => '2' }, { "a" => '3' }, { "a" => '10' }],
|
||||
@filters.sort_numeric([{ "a" => '10' }, { "a" => '3' }, { "a" => '1' }, { "a" => '2' }], "a")
|
||||
end
|
||||
|
||||
def test_sort_with_nils
|
||||
assert_equal [1, 2, 3, 4, nil], @filters.sort([nil, 4, 3, 2, 1])
|
||||
assert_equal [{ "a" => 1 }, { "a" => 2 }, { "a" => 3 }, { "a" => 4 }, {}], @filters.sort([{ "a" => 4 }, { "a" => 3 }, {}, { "a" => 1 }, { "a" => 2 }], "a")
|
||||
end
|
||||
|
||||
def test_sort_when_property_is_sometimes_missing_puts_nils_last
|
||||
@@ -203,22 +215,85 @@ class StandardFiltersTest < Minitest::Test
|
||||
{ "handle" => "beta" },
|
||||
{ "price" => 1, "handle" => "gamma" },
|
||||
{ "handle" => "delta" },
|
||||
{ "price" => 2, "handle" => "epsilon" }
|
||||
{ "price" => 2, "handle" => "epsilon" },
|
||||
]
|
||||
expectation = [
|
||||
{ "price" => 1, "handle" => "gamma" },
|
||||
{ "price" => 2, "handle" => "epsilon" },
|
||||
{ "price" => 4, "handle" => "alpha" },
|
||||
{ "handle" => "delta" },
|
||||
{ "handle" => "beta" }
|
||||
{ "handle" => "beta" },
|
||||
]
|
||||
assert_equal expectation, @filters.sort(input, "price")
|
||||
end
|
||||
|
||||
def test_sort_natural
|
||||
assert_equal ["a", "B", "c", "D"], @filters.sort_natural(["c", "D", "a", "B"])
|
||||
assert_equal [{ "a" => "a" }, { "a" => "B" }, { "a" => "c" }, { "a" => "D" }], @filters.sort_natural([{ "a" => "D" }, { "a" => "c" }, { "a" => "a" }, { "a" => "B" }], "a")
|
||||
end
|
||||
|
||||
def test_sort_natural_with_nils
|
||||
assert_equal ["a", "B", "c", "D", nil], @filters.sort_natural([nil, "c", "D", "a", "B"])
|
||||
assert_equal [{ "a" => "a" }, { "a" => "B" }, { "a" => "c" }, { "a" => "D" }, {}], @filters.sort_natural([{ "a" => "D" }, { "a" => "c" }, {}, { "a" => "a" }, { "a" => "B" }], "a")
|
||||
end
|
||||
|
||||
def test_sort_natural_when_property_is_sometimes_missing_puts_nils_last
|
||||
input = [
|
||||
{ "price" => "4", "handle" => "alpha" },
|
||||
{ "handle" => "beta" },
|
||||
{ "price" => "1", "handle" => "gamma" },
|
||||
{ "handle" => "delta" },
|
||||
{ "price" => 2, "handle" => "epsilon" },
|
||||
]
|
||||
expectation = [
|
||||
{ "price" => "1", "handle" => "gamma" },
|
||||
{ "price" => 2, "handle" => "epsilon" },
|
||||
{ "price" => "4", "handle" => "alpha" },
|
||||
{ "handle" => "delta" },
|
||||
{ "handle" => "beta" },
|
||||
]
|
||||
assert_equal expectation, @filters.sort_natural(input, "price")
|
||||
end
|
||||
|
||||
def test_sort_natural_case_check
|
||||
input = [
|
||||
{ "key" => "X" },
|
||||
{ "key" => "Y" },
|
||||
{ "key" => "Z" },
|
||||
{ "fake" => "t" },
|
||||
{ "key" => "a" },
|
||||
{ "key" => "b" },
|
||||
{ "key" => "c" },
|
||||
]
|
||||
expectation = [
|
||||
{ "key" => "a" },
|
||||
{ "key" => "b" },
|
||||
{ "key" => "c" },
|
||||
{ "key" => "X" },
|
||||
{ "key" => "Y" },
|
||||
{ "key" => "Z" },
|
||||
{ "fake" => "t" },
|
||||
]
|
||||
assert_equal expectation, @filters.sort_natural(input, "key")
|
||||
assert_equal ["a", "b", "c", "X", "Y", "Z"], @filters.sort_natural(["X", "Y", "Z", "a", "b", "c"])
|
||||
end
|
||||
|
||||
def test_sort_empty_array
|
||||
assert_equal [], @filters.sort([], "a")
|
||||
end
|
||||
|
||||
def test_sort_invalid_property
|
||||
foo = [
|
||||
[1],
|
||||
[2],
|
||||
[3],
|
||||
]
|
||||
|
||||
assert_raises Liquid::ArgumentError do
|
||||
@filters.sort(foo, "bar")
|
||||
end
|
||||
end
|
||||
|
||||
def test_sort_natural_empty_array
|
||||
assert_equal [], @filters.sort_natural([], "a")
|
||||
end
|
||||
@@ -226,9 +301,21 @@ class StandardFiltersTest < Minitest::Test
|
||||
def test_sort_numeric_empty_array
|
||||
assert_equal [], @filters.sort_numeric([], "a")
|
||||
end
|
||||
|
||||
def test_sort_natural_invalid_property
|
||||
foo = [
|
||||
[1],
|
||||
[2],
|
||||
[3],
|
||||
]
|
||||
|
||||
assert_raises Liquid::ArgumentError do
|
||||
@filters.sort_natural(foo, "bar")
|
||||
end
|
||||
end
|
||||
|
||||
def test_legacy_sort_hash
|
||||
assert_equal [{ a: 1, b: 2 }], @filters.sort({ a: 1, b: 2 })
|
||||
assert_equal [{ a: 1, b: 2 }], @filters.sort(a: 1, b: 2)
|
||||
end
|
||||
|
||||
def test_numerical_vs_lexicographical_sort
|
||||
@@ -250,10 +337,34 @@ class StandardFiltersTest < Minitest::Test
|
||||
assert_equal [], @filters.uniq([], "a")
|
||||
end
|
||||
|
||||
def test_uniq_invalid_property
|
||||
foo = [
|
||||
[1],
|
||||
[2],
|
||||
[3],
|
||||
]
|
||||
|
||||
assert_raises Liquid::ArgumentError do
|
||||
@filters.uniq(foo, "bar")
|
||||
end
|
||||
end
|
||||
|
||||
def test_compact_empty_array
|
||||
assert_equal [], @filters.compact([], "a")
|
||||
end
|
||||
|
||||
def test_compact_invalid_property
|
||||
foo = [
|
||||
[1],
|
||||
[2],
|
||||
[3],
|
||||
]
|
||||
|
||||
assert_raises Liquid::ArgumentError do
|
||||
@filters.compact(foo, "bar")
|
||||
end
|
||||
end
|
||||
|
||||
def test_reverse
|
||||
assert_equal [4, 3, 2, 1], @filters.reverse([1, 2, 3, 4])
|
||||
end
|
||||
@@ -280,7 +391,7 @@ class StandardFiltersTest < Minitest::Test
|
||||
|
||||
def test_map_on_hashes
|
||||
assert_template_result "4217", '{{ thing | map: "foo" | map: "bar" }}',
|
||||
"thing" => { "foo" => [ { "bar" => 42 }, { "bar" => 17 } ] }
|
||||
"thing" => { "foo" => [{ "bar" => 42 }, { "bar" => 17 }] }
|
||||
end
|
||||
|
||||
def test_legacy_map_on_hashes_with_dynamic_key
|
||||
@@ -297,7 +408,7 @@ class StandardFiltersTest < Minitest::Test
|
||||
|
||||
def test_map_over_proc
|
||||
drop = TestDrop.new
|
||||
p = proc{ drop }
|
||||
p = proc { drop }
|
||||
templ = '{{ procs | map: "test" }}'
|
||||
assert_template_result "testfoo", templ, "procs" => [p]
|
||||
end
|
||||
@@ -305,10 +416,10 @@ class StandardFiltersTest < Minitest::Test
|
||||
def test_map_over_drops_returning_procs
|
||||
drops = [
|
||||
{
|
||||
"proc" => ->{ "foo" },
|
||||
"proc" => -> { "foo" },
|
||||
},
|
||||
{
|
||||
"proc" => ->{ "bar" },
|
||||
"proc" => -> { "bar" },
|
||||
},
|
||||
]
|
||||
templ = '{{ drops | map: "proc" }}'
|
||||
@@ -319,6 +430,29 @@ class StandardFiltersTest < Minitest::Test
|
||||
assert_template_result "123", '{{ foo | map: "foo" }}', "foo" => TestEnumerable.new
|
||||
end
|
||||
|
||||
def test_map_returns_empty_on_2d_input_array
|
||||
foo = [
|
||||
[1],
|
||||
[2],
|
||||
[3],
|
||||
]
|
||||
|
||||
assert_raises Liquid::ArgumentError do
|
||||
@filters.map(foo, "bar")
|
||||
end
|
||||
end
|
||||
|
||||
def test_map_returns_empty_with_no_property
|
||||
foo = [
|
||||
[1],
|
||||
[2],
|
||||
[3],
|
||||
]
|
||||
assert_raises Liquid::ArgumentError do
|
||||
@filters.map(foo, nil)
|
||||
end
|
||||
end
|
||||
|
||||
def test_sort_works_on_enumerables
|
||||
assert_template_result "213", '{{ foo | sort: "bar" | map: "foo" }}', "foo" => TestEnumerable.new
|
||||
end
|
||||
@@ -349,9 +483,9 @@ class StandardFiltersTest < Minitest::Test
|
||||
assert_equal '07/05/2006', @filters.date("2006-07-05 10:00:00", "%m/%d/%Y")
|
||||
|
||||
assert_equal "07/16/2004", @filters.date("Fri Jul 16 01:00:00 2004", "%m/%d/%Y")
|
||||
assert_equal "#{Date.today.year}", @filters.date('now', '%Y')
|
||||
assert_equal "#{Date.today.year}", @filters.date('today', '%Y')
|
||||
assert_equal "#{Date.today.year}", @filters.date('Today', '%Y')
|
||||
assert_equal Date.today.year.to_s, @filters.date('now', '%Y')
|
||||
assert_equal Date.today.year.to_s, @filters.date('today', '%Y')
|
||||
assert_equal Date.today.year.to_s, @filters.date('Today', '%Y')
|
||||
|
||||
assert_nil @filters.date(nil, "%B")
|
||||
|
||||
@@ -569,6 +703,78 @@ class StandardFiltersTest < Minitest::Test
|
||||
assert_template_result('abc', "{{ 'abc' | date: '%D' }}")
|
||||
end
|
||||
|
||||
def test_where
|
||||
input = [
|
||||
{ "handle" => "alpha", "ok" => true },
|
||||
{ "handle" => "beta", "ok" => false },
|
||||
{ "handle" => "gamma", "ok" => false },
|
||||
{ "handle" => "delta", "ok" => true },
|
||||
]
|
||||
|
||||
expectation = [
|
||||
{ "handle" => "alpha", "ok" => true },
|
||||
{ "handle" => "delta", "ok" => true },
|
||||
]
|
||||
|
||||
assert_equal expectation, @filters.where(input, "ok", true)
|
||||
assert_equal expectation, @filters.where(input, "ok")
|
||||
end
|
||||
|
||||
def test_where_no_key_set
|
||||
input = [
|
||||
{ "handle" => "alpha", "ok" => true },
|
||||
{ "handle" => "beta" },
|
||||
{ "handle" => "gamma" },
|
||||
{ "handle" => "delta", "ok" => true },
|
||||
]
|
||||
|
||||
expectation = [
|
||||
{ "handle" => "alpha", "ok" => true },
|
||||
{ "handle" => "delta", "ok" => true },
|
||||
]
|
||||
|
||||
assert_equal expectation, @filters.where(input, "ok", true)
|
||||
assert_equal expectation, @filters.where(input, "ok")
|
||||
end
|
||||
|
||||
def test_where_non_array_map_input
|
||||
assert_equal [{ "a" => "ok" }], @filters.where({ "a" => "ok" }, "a", "ok")
|
||||
assert_equal [], @filters.where({ "a" => "not ok" }, "a", "ok")
|
||||
end
|
||||
|
||||
def test_where_indexable_but_non_map_value
|
||||
assert_raises(Liquid::ArgumentError) { @filters.where(1, "ok", true) }
|
||||
assert_raises(Liquid::ArgumentError) { @filters.where(1, "ok") }
|
||||
end
|
||||
|
||||
def test_where_non_boolean_value
|
||||
input = [
|
||||
{ "message" => "Bonjour!", "language" => "French" },
|
||||
{ "message" => "Hello!", "language" => "English" },
|
||||
{ "message" => "Hallo!", "language" => "German" },
|
||||
]
|
||||
|
||||
assert_equal [{ "message" => "Bonjour!", "language" => "French" }], @filters.where(input, "language", "French")
|
||||
assert_equal [{ "message" => "Hallo!", "language" => "German" }], @filters.where(input, "language", "German")
|
||||
assert_equal [{ "message" => "Hello!", "language" => "English" }], @filters.where(input, "language", "English")
|
||||
end
|
||||
|
||||
def test_where_array_of_only_unindexable_values
|
||||
assert_nil @filters.where([nil], "ok", true)
|
||||
assert_nil @filters.where([nil], "ok")
|
||||
end
|
||||
|
||||
def test_where_no_target_value
|
||||
input = [
|
||||
{ "foo" => false },
|
||||
{ "foo" => true },
|
||||
{ "foo" => "for sure" },
|
||||
{ "bar" => true },
|
||||
]
|
||||
|
||||
assert_equal [{ "foo" => true }, { "foo" => "for sure" }], @filters.where(input, "foo")
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def with_timezone(tz)
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class BreakTagTest < Minitest::Test
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class ContinueTagTest < Minitest::Test
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class EchoTest < Minitest::Test
|
||||
include Liquid
|
||||
|
||||
def test_echo_outputs_its_input
|
||||
assert_template_result('BAR', <<~LIQUID, 'variable-name' => 'bar')
|
||||
{%- echo variable-name | upcase -%}
|
||||
LIQUID
|
||||
end
|
||||
end
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class ThingWithValue < Liquid::Drop
|
||||
@@ -23,16 +25,16 @@ class ForTagTest < Minitest::Test
|
||||
yo
|
||||
|
||||
HERE
|
||||
template = <<HERE
|
||||
{%for item in array%}
|
||||
yo
|
||||
{%endfor%}
|
||||
HERE
|
||||
template = <<~HERE
|
||||
{%for item in array%}
|
||||
yo
|
||||
{%endfor%}
|
||||
HERE
|
||||
assert_template_result(expected, template, 'array' => [1, 2, 3])
|
||||
end
|
||||
|
||||
def test_for_reversed
|
||||
assigns = { 'array' => [ 1, 2, 3] }
|
||||
assigns = { 'array' => [1, 2, 3] }
|
||||
assert_template_result('321', '{%for item in array reversed %}{{item}}{%endfor%}', assigns)
|
||||
end
|
||||
|
||||
@@ -103,6 +105,34 @@ HERE
|
||||
assert_template_result('3456', '{%for i in array limit: 4 offset: 2 %}{{ i }}{%endfor%}', assigns)
|
||||
end
|
||||
|
||||
def test_limiting_with_invalid_limit
|
||||
assigns = { 'array' => [1, 2, 3, 4, 5, 6, 7, 8, 9, 0] }
|
||||
template = <<-MKUP
|
||||
{% for i in array limit: true offset: 1 %}
|
||||
{{ i }}
|
||||
{% endfor %}
|
||||
MKUP
|
||||
|
||||
exception = assert_raises(Liquid::ArgumentError) do
|
||||
Template.parse(template).render!(assigns)
|
||||
end
|
||||
assert_equal("Liquid error: invalid integer", exception.message)
|
||||
end
|
||||
|
||||
def test_limiting_with_invalid_offset
|
||||
assigns = { 'array' => [1, 2, 3, 4, 5, 6, 7, 8, 9, 0] }
|
||||
template = <<-MKUP
|
||||
{% for i in array limit: 1 offset: true %}
|
||||
{{ i }}
|
||||
{% endfor %}
|
||||
MKUP
|
||||
|
||||
exception = assert_raises(Liquid::ArgumentError) do
|
||||
Template.parse(template).render!(assigns)
|
||||
end
|
||||
assert_equal("Liquid error: invalid integer", exception.message)
|
||||
end
|
||||
|
||||
def test_dynamic_variable_limiting
|
||||
assigns = { 'array' => [1, 2, 3, 4, 5, 6, 7, 8, 9, 0] }
|
||||
assigns['limit'] = 2
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class IfElseTagTest < Minitest::Test
|
||||
@@ -132,7 +134,7 @@ class IfElseTagTest < Minitest::Test
|
||||
end
|
||||
|
||||
def test_syntax_error_no_variable
|
||||
assert_raises(SyntaxError){ assert_template_result('', '{% if jerry == 1 %}') }
|
||||
assert_raises(SyntaxError) { assert_template_result('', '{% if jerry == 1 %}') }
|
||||
end
|
||||
|
||||
def test_syntax_error_no_expression
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class TestFileSystem
|
||||
@@ -30,6 +32,9 @@ class TestFileSystem
|
||||
when 'assignments'
|
||||
"{% assign foo = 'bar' %}"
|
||||
|
||||
when 'break'
|
||||
"{% break %}"
|
||||
|
||||
else
|
||||
template_path
|
||||
end
|
||||
@@ -37,14 +42,14 @@ class TestFileSystem
|
||||
end
|
||||
|
||||
class OtherFileSystem
|
||||
def read_template_file(template_path)
|
||||
def read_template_file(_template_path)
|
||||
'from OtherFileSystem'
|
||||
end
|
||||
end
|
||||
|
||||
class CountingFileSystem
|
||||
attr_reader :count
|
||||
def read_template_file(template_path)
|
||||
def read_template_file(_template_path)
|
||||
@count ||= 0
|
||||
@count += 1
|
||||
'from CountingFileSystem'
|
||||
@@ -56,15 +61,16 @@ class CustomInclude < Liquid::Tag
|
||||
|
||||
def initialize(tag_name, markup, tokens)
|
||||
markup =~ Syntax
|
||||
@template_name = $1
|
||||
@template_name = Regexp.last_match(1)
|
||||
super
|
||||
end
|
||||
|
||||
def parse(tokens)
|
||||
end
|
||||
|
||||
def render(context)
|
||||
@template_name[1..-2]
|
||||
def render_to_output_buffer(_context, output)
|
||||
output << @template_name[1..-2]
|
||||
output
|
||||
end
|
||||
end
|
||||
|
||||
@@ -82,7 +88,7 @@ class IncludeTagTest < Minitest::Test
|
||||
|
||||
def test_include_tag_with
|
||||
assert_template_result "Product: Draft 151cm ",
|
||||
"{% include 'product' with products[0] %}", "products" => [ { 'title' => 'Draft 151cm' }, { 'title' => 'Element 155cm' } ]
|
||||
"{% include 'product' with products[0] %}", "products" => [{ 'title' => 'Draft 151cm' }, { 'title' => 'Element 155cm' }]
|
||||
end
|
||||
|
||||
def test_include_tag_with_default_name
|
||||
@@ -92,7 +98,7 @@ class IncludeTagTest < Minitest::Test
|
||||
|
||||
def test_include_tag_for
|
||||
assert_template_result "Product: Draft 151cm Product: Element 155cm ",
|
||||
"{% include 'product' for products %}", "products" => [ { 'title' => 'Draft 151cm' }, { 'title' => 'Element 155cm' } ]
|
||||
"{% include 'product' for products %}", "products" => [{ 'title' => 'Draft 151cm' }, { 'title' => 'Element 155cm' }]
|
||||
end
|
||||
|
||||
def test_include_tag_with_local_variables
|
||||
@@ -130,7 +136,7 @@ class IncludeTagTest < Minitest::Test
|
||||
|
||||
def test_recursively_included_template_does_not_produce_endless_loop
|
||||
infinite_file_system = Class.new do
|
||||
def read_template_file(template_path)
|
||||
def read_template_file(_template_path)
|
||||
"-{% include 'loop' %}"
|
||||
end
|
||||
end
|
||||
@@ -242,4 +248,9 @@ class IncludeTagTest < Minitest::Test
|
||||
|
||||
assert_equal [], template.errors
|
||||
end
|
||||
|
||||
def test_break_through_include
|
||||
assert_template_result "1", "{% for i in (1..3) %}{{ i }}{% break %}{{ i }}{% endfor %}"
|
||||
assert_template_result "1", "{% for i in (1..3) %}{{ i }}{% include 'break' %}{{ i }}{% endfor %}"
|
||||
end
|
||||
end # IncludeTagTest
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class IncrementTagTest < Minitest::Test
|
||||
@@ -13,11 +15,11 @@ class IncrementTagTest < Minitest::Test
|
||||
end
|
||||
|
||||
def test_dec
|
||||
assert_template_result('9', '{%decrement port %}', { 'port' => 10 })
|
||||
assert_template_result('9', '{%decrement port %}', 'port' => 10)
|
||||
assert_template_result('-1 -2', '{%decrement port %} {%decrement port%}', {})
|
||||
assert_template_result('1 5 2 2 5',
|
||||
'{%increment port %} {%increment starboard%} ' \
|
||||
'{%increment port %} {%decrement port%} ' \
|
||||
'{%decrement starboard %}', { 'port' => 1, 'starboard' => 5 })
|
||||
'{%decrement starboard %}', 'port' => 1, 'starboard' => 5)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class LiquidTagTest < Minitest::Test
|
||||
include Liquid
|
||||
|
||||
def test_liquid_tag
|
||||
assert_template_result('1 2 3', <<~LIQUID, 'array' => [1, 2, 3])
|
||||
{%- liquid
|
||||
echo array | join: " "
|
||||
-%}
|
||||
LIQUID
|
||||
|
||||
assert_template_result('1 2 3', <<~LIQUID, 'array' => [1, 2, 3])
|
||||
{%- liquid
|
||||
for value in array
|
||||
echo value
|
||||
unless forloop.last
|
||||
echo " "
|
||||
endunless
|
||||
endfor
|
||||
-%}
|
||||
LIQUID
|
||||
|
||||
assert_template_result('4 8 12 6', <<~LIQUID, 'array' => [1, 2, 3])
|
||||
{%- liquid
|
||||
for value in array
|
||||
assign double_value = value | times: 2
|
||||
echo double_value | times: 2
|
||||
unless forloop.last
|
||||
echo " "
|
||||
endunless
|
||||
endfor
|
||||
|
||||
echo " "
|
||||
echo double_value
|
||||
-%}
|
||||
LIQUID
|
||||
|
||||
assert_template_result('abc', <<~LIQUID)
|
||||
{%- liquid echo "a" -%}
|
||||
b
|
||||
{%- liquid echo "c" -%}
|
||||
LIQUID
|
||||
end
|
||||
|
||||
def test_liquid_tag_errors
|
||||
assert_match_syntax_error("syntax error (line 1): Unknown tag 'error'", <<~LIQUID)
|
||||
{%- liquid error no such tag -%}
|
||||
LIQUID
|
||||
|
||||
assert_match_syntax_error("syntax error (line 7): Unknown tag 'error'", <<~LIQUID)
|
||||
{{ test }}
|
||||
|
||||
{%-
|
||||
liquid
|
||||
for value in array
|
||||
|
||||
error no such tag
|
||||
endfor
|
||||
-%}
|
||||
LIQUID
|
||||
|
||||
assert_match_syntax_error("syntax error (line 2): Unknown tag '!!! the guards are vigilant'", <<~LIQUID)
|
||||
{%- liquid
|
||||
!!! the guards are vigilant
|
||||
-%}
|
||||
LIQUID
|
||||
|
||||
assert_match_syntax_error("syntax error (line 4): 'for' tag was never closed", <<~LIQUID)
|
||||
{%- liquid
|
||||
for value in array
|
||||
echo 'forgot to close the for tag'
|
||||
-%}
|
||||
LIQUID
|
||||
end
|
||||
|
||||
def test_line_number_is_correct_after_a_blank_token
|
||||
assert_match_syntax_error("syntax error (line 3): Unknown tag 'error'", "{% liquid echo ''\n\n error %}")
|
||||
assert_match_syntax_error("syntax error (line 3): Unknown tag 'error'", "{% liquid echo ''\n \n error %}")
|
||||
end
|
||||
|
||||
def test_cannot_open_blocks_living_past_a_liquid_tag
|
||||
assert_match_syntax_error("syntax error (line 3): 'if' tag was never closed", <<~LIQUID)
|
||||
{%- liquid
|
||||
if true
|
||||
-%}
|
||||
{%- endif -%}
|
||||
LIQUID
|
||||
end
|
||||
|
||||
def test_quirk_can_close_blocks_created_before_a_liquid_tag
|
||||
assert_template_result("42", <<~LIQUID)
|
||||
{%- if true -%}
|
||||
42
|
||||
{%- liquid endif -%}
|
||||
LIQUID
|
||||
end
|
||||
|
||||
def test_liquid_tag_in_raw
|
||||
assert_template_result("{% liquid echo 'test' %}\n", <<~LIQUID)
|
||||
{% raw %}{% liquid echo 'test' %}{% endraw %}
|
||||
LIQUID
|
||||
end
|
||||
end
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class RawTagTest < Minitest::Test
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class RenderTagTest < Minitest::Test
|
||||
include Liquid
|
||||
|
||||
def test_render_with_no_arguments
|
||||
Liquid::Template.file_system = StubFileSystem.new('source' => 'rendered content')
|
||||
assert_template_result 'rendered content', '{% render "source" %}'
|
||||
end
|
||||
|
||||
def test_render_tag_looks_for_file_system_in_registers_first
|
||||
file_system = StubFileSystem.new('pick_a_source' => 'from register file system')
|
||||
assert_equal 'from register file system',
|
||||
Template.parse('{% render "pick_a_source" %}').render!({}, registers: { file_system: file_system })
|
||||
end
|
||||
|
||||
def test_render_passes_named_arguments_into_inner_scope
|
||||
Liquid::Template.file_system = StubFileSystem.new('product' => '{{ inner_product.title }}')
|
||||
assert_template_result 'My Product', '{% render "product", inner_product: outer_product %}',
|
||||
'outer_product' => { 'title' => 'My Product' }
|
||||
end
|
||||
|
||||
def test_render_accepts_literals_as_arguments
|
||||
Liquid::Template.file_system = StubFileSystem.new('snippet' => '{{ price }}')
|
||||
assert_template_result '123', '{% render "snippet", price: 123 %}'
|
||||
end
|
||||
|
||||
def test_render_accepts_multiple_named_arguments
|
||||
Liquid::Template.file_system = StubFileSystem.new('snippet' => '{{ one }} {{ two }}')
|
||||
assert_template_result '1 2', '{% render "snippet", one: 1, two: 2 %}'
|
||||
end
|
||||
|
||||
def test_render_does_not_inherit_parent_scope_variables
|
||||
Liquid::Template.file_system = StubFileSystem.new('snippet' => '{{ outer_variable }}')
|
||||
assert_template_result '', '{% assign outer_variable = "should not be visible" %}{% render "snippet" %}'
|
||||
end
|
||||
|
||||
def test_render_does_not_inherit_variable_with_same_name_as_snippet
|
||||
Liquid::Template.file_system = StubFileSystem.new('snippet' => '{{ snippet }}')
|
||||
assert_template_result '', "{% assign snippet = 'should not be visible' %}{% render 'snippet' %}"
|
||||
end
|
||||
|
||||
def test_render_sets_the_correct_template_name_for_errors
|
||||
Liquid::Template.file_system = StubFileSystem.new('snippet' => '{{ unsafe }}')
|
||||
|
||||
with_taint_mode :error do
|
||||
template = Liquid::Template.parse('{% render "snippet", unsafe: unsafe %}')
|
||||
context = Context.new('unsafe' => (+'unsafe').tap(&:taint))
|
||||
template.render(context)
|
||||
|
||||
assert_equal [Liquid::TaintedError], template.errors.map(&:class)
|
||||
assert_equal 'snippet', template.errors.first.template_name
|
||||
end
|
||||
end
|
||||
|
||||
def test_render_sets_the_correct_template_name_for_warnings
|
||||
Liquid::Template.file_system = StubFileSystem.new('snippet' => '{{ unsafe }}')
|
||||
|
||||
with_taint_mode :warn do
|
||||
template = Liquid::Template.parse('{% render "snippet", unsafe: unsafe %}')
|
||||
context = Context.new('unsafe' => (+'unsafe').tap(&:taint))
|
||||
template.render(context)
|
||||
|
||||
assert_equal [Liquid::TaintedError], context.warnings.map(&:class)
|
||||
assert_equal 'snippet', context.warnings.first.template_name
|
||||
end
|
||||
end
|
||||
|
||||
def test_render_does_not_mutate_parent_scope
|
||||
Liquid::Template.file_system = StubFileSystem.new('snippet' => '{% assign inner = 1 %}')
|
||||
assert_template_result '', "{% render 'snippet' %}{{ inner }}"
|
||||
end
|
||||
|
||||
def test_nested_render_tag
|
||||
Liquid::Template.file_system = StubFileSystem.new(
|
||||
'one' => "one {% render 'two' %}",
|
||||
'two' => 'two'
|
||||
)
|
||||
assert_template_result 'one two', "{% render 'one' %}"
|
||||
end
|
||||
|
||||
def test_recursively_rendered_template_does_not_produce_endless_loop
|
||||
Liquid::Template.file_system = StubFileSystem.new('loop' => '{% render "loop" %}')
|
||||
|
||||
assert_raises Liquid::StackLevelError do
|
||||
Template.parse('{% render "loop" %}').render!
|
||||
end
|
||||
end
|
||||
|
||||
def test_sub_contexts_count_towards_the_same_recursion_limit
|
||||
Liquid::Template.file_system = StubFileSystem.new(
|
||||
'loop_render' => '{% render "loop_render" %}',
|
||||
)
|
||||
assert_raises Liquid::StackLevelError do
|
||||
Template.parse('{% render "loop_render" %}').render!
|
||||
end
|
||||
end
|
||||
|
||||
def test_dynamically_choosen_templates_are_not_allowed
|
||||
Liquid::Template.file_system = StubFileSystem.new('snippet' => 'should not be rendered')
|
||||
|
||||
assert_raises Liquid::SyntaxError do
|
||||
Liquid::Template.parse("{% assign name = 'snippet' %}{% render name %}")
|
||||
end
|
||||
end
|
||||
|
||||
def test_include_tag_caches_second_read_of_same_partial
|
||||
file_system = StubFileSystem.new('snippet' => 'echo')
|
||||
assert_equal 'echoecho',
|
||||
Template.parse('{% render "snippet" %}{% render "snippet" %}')
|
||||
.render!({}, registers: { file_system: file_system })
|
||||
assert_equal 1, file_system.file_read_count
|
||||
end
|
||||
|
||||
def test_render_tag_doesnt_cache_partials_across_renders
|
||||
file_system = StubFileSystem.new('snippet' => 'my message')
|
||||
|
||||
assert_equal 'my message',
|
||||
Template.parse('{% include "snippet" %}').render!({}, registers: { file_system: file_system })
|
||||
assert_equal 1, file_system.file_read_count
|
||||
|
||||
assert_equal 'my message',
|
||||
Template.parse('{% include "snippet" %}').render!({}, registers: { file_system: file_system })
|
||||
assert_equal 2, file_system.file_read_count
|
||||
end
|
||||
|
||||
def test_render_tag_within_if_statement
|
||||
Liquid::Template.file_system = StubFileSystem.new('snippet' => 'my message')
|
||||
assert_template_result 'my message', '{% if true %}{% render "snippet" %}{% endif %}'
|
||||
end
|
||||
|
||||
def test_break_through_render
|
||||
Liquid::Template.file_system = StubFileSystem.new('break' => '{% break %}')
|
||||
assert_template_result '1', '{% for i in (1..3) %}{{ i }}{% break %}{{ i }}{% endfor %}'
|
||||
assert_template_result '112233', '{% for i in (1..3) %}{{ i }}{% render "break" %}{{ i }}{% endfor %}'
|
||||
end
|
||||
|
||||
def test_increment_is_isolated_between_renders
|
||||
Liquid::Template.file_system = StubFileSystem.new('incr' => '{% increment %}')
|
||||
assert_template_result '010', '{% increment %}{% increment %}{% render "incr" %}'
|
||||
end
|
||||
|
||||
def test_decrement_is_isolated_between_renders
|
||||
Liquid::Template.file_system = StubFileSystem.new('decr' => '{% decrement %}')
|
||||
assert_template_result '-1-2-1', '{% decrement %}{% decrement %}{% render "decr" %}'
|
||||
end
|
||||
|
||||
def test_includes_will_not_render_inside_render_tag
|
||||
Liquid::Template.file_system = StubFileSystem.new(
|
||||
'foo' => 'bar',
|
||||
'test_include' => '{% include "foo" %}'
|
||||
)
|
||||
|
||||
assert_template_result 'include usage is not allowed in this context', '{% render "test_include" %}'
|
||||
end
|
||||
|
||||
def test_includes_will_not_render_inside_nested_sibling_tags
|
||||
Liquid::Template.file_system = StubFileSystem.new(
|
||||
'foo' => 'bar',
|
||||
'nested_render_with_sibling_include' => '{% render "test_include" %}{% include "foo" %}',
|
||||
'test_include' => '{% include "foo" %}'
|
||||
)
|
||||
|
||||
assert_template_result 'include usage is not allowed in this contextinclude usage is not allowed in this context', '{% render "nested_render_with_sibling_include" %}'
|
||||
end
|
||||
end
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class StandardTagTest < Minitest::Test
|
||||
@@ -69,7 +71,7 @@ class StandardTagTest < Minitest::Test
|
||||
assert_raises(SyntaxError) do
|
||||
assert_template_result('content foo content foo ',
|
||||
'{{ var2 }}{% capture %}{{ var }} foo {% endcapture %}{{ var2 }}{{ var2 }}',
|
||||
{ 'var' => 'content' })
|
||||
'var' => 'content')
|
||||
end
|
||||
end
|
||||
|
||||
@@ -183,32 +185,32 @@ class StandardTagTest < Minitest::Test
|
||||
|
||||
def test_case_when_or
|
||||
code = '{% case condition %}{% when 1 or 2 or 3 %} its 1 or 2 or 3 {% when 4 %} its 4 {% endcase %}'
|
||||
assert_template_result(' its 1 or 2 or 3 ', code, { 'condition' => 1 })
|
||||
assert_template_result(' its 1 or 2 or 3 ', code, { 'condition' => 2 })
|
||||
assert_template_result(' its 1 or 2 or 3 ', code, { 'condition' => 3 })
|
||||
assert_template_result(' its 4 ', code, { 'condition' => 4 })
|
||||
assert_template_result('', code, { 'condition' => 5 })
|
||||
assert_template_result(' its 1 or 2 or 3 ', code, 'condition' => 1)
|
||||
assert_template_result(' its 1 or 2 or 3 ', code, 'condition' => 2)
|
||||
assert_template_result(' its 1 or 2 or 3 ', code, 'condition' => 3)
|
||||
assert_template_result(' its 4 ', code, 'condition' => 4)
|
||||
assert_template_result('', code, 'condition' => 5)
|
||||
|
||||
code = '{% case condition %}{% when 1 or "string" or null %} its 1 or 2 or 3 {% when 4 %} its 4 {% endcase %}'
|
||||
assert_template_result(' its 1 or 2 or 3 ', code, { 'condition' => 1 })
|
||||
assert_template_result(' its 1 or 2 or 3 ', code, { 'condition' => 'string' })
|
||||
assert_template_result(' its 1 or 2 or 3 ', code, { 'condition' => nil })
|
||||
assert_template_result('', code, { 'condition' => 'something else' })
|
||||
assert_template_result(' its 1 or 2 or 3 ', code, 'condition' => 1)
|
||||
assert_template_result(' its 1 or 2 or 3 ', code, 'condition' => 'string')
|
||||
assert_template_result(' its 1 or 2 or 3 ', code, 'condition' => nil)
|
||||
assert_template_result('', code, 'condition' => 'something else')
|
||||
end
|
||||
|
||||
def test_case_when_comma
|
||||
code = '{% case condition %}{% when 1, 2, 3 %} its 1 or 2 or 3 {% when 4 %} its 4 {% endcase %}'
|
||||
assert_template_result(' its 1 or 2 or 3 ', code, { 'condition' => 1 })
|
||||
assert_template_result(' its 1 or 2 or 3 ', code, { 'condition' => 2 })
|
||||
assert_template_result(' its 1 or 2 or 3 ', code, { 'condition' => 3 })
|
||||
assert_template_result(' its 4 ', code, { 'condition' => 4 })
|
||||
assert_template_result('', code, { 'condition' => 5 })
|
||||
assert_template_result(' its 1 or 2 or 3 ', code, 'condition' => 1)
|
||||
assert_template_result(' its 1 or 2 or 3 ', code, 'condition' => 2)
|
||||
assert_template_result(' its 1 or 2 or 3 ', code, 'condition' => 3)
|
||||
assert_template_result(' its 4 ', code, 'condition' => 4)
|
||||
assert_template_result('', code, 'condition' => 5)
|
||||
|
||||
code = '{% case condition %}{% when 1, "string", null %} its 1 or 2 or 3 {% when 4 %} its 4 {% endcase %}'
|
||||
assert_template_result(' its 1 or 2 or 3 ', code, { 'condition' => 1 })
|
||||
assert_template_result(' its 1 or 2 or 3 ', code, { 'condition' => 'string' })
|
||||
assert_template_result(' its 1 or 2 or 3 ', code, { 'condition' => nil })
|
||||
assert_template_result('', code, { 'condition' => 'something else' })
|
||||
assert_template_result(' its 1 or 2 or 3 ', code, 'condition' => 1)
|
||||
assert_template_result(' its 1 or 2 or 3 ', code, 'condition' => 'string')
|
||||
assert_template_result(' its 1 or 2 or 3 ', code, 'condition' => nil)
|
||||
assert_template_result('', code, 'condition' => 'something else')
|
||||
end
|
||||
|
||||
def test_assign
|
||||
@@ -283,10 +285,10 @@ class StandardTagTest < Minitest::Test
|
||||
end
|
||||
|
||||
def test_ifchanged
|
||||
assigns = { 'array' => [ 1, 1, 2, 2, 3, 3] }
|
||||
assigns = { 'array' => [1, 1, 2, 2, 3, 3] }
|
||||
assert_template_result('123', '{%for item in array%}{%ifchanged%}{{item}}{% endifchanged %}{%endfor%}', assigns)
|
||||
|
||||
assigns = { 'array' => [ 1, 1, 1, 1] }
|
||||
assigns = { 'array' => [1, 1, 1, 1] }
|
||||
assert_template_result('1', '{%for item in array%}{%ifchanged%}{{item}}{% endifchanged %}{%endfor%}', assigns)
|
||||
end
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class StatementsTest < Minitest::Test
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class TableRowTest < Minitest::Test
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class UnlessElseTagTest < Minitest::Test
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
require 'timeout'
|
||||
|
||||
@@ -79,7 +81,10 @@ class TemplateTest < Minitest::Test
|
||||
|
||||
def test_lambda_is_called_once_from_persistent_assigns_over_multiple_parses_and_renders
|
||||
t = Template.new
|
||||
t.assigns['number'] = -> { @global ||= 0; @global += 1 }
|
||||
t.assigns['number'] = -> {
|
||||
@global ||= 0
|
||||
@global += 1
|
||||
}
|
||||
assert_equal '1', t.parse("{{number}}").render!
|
||||
assert_equal '1', t.parse("{{number}}").render!
|
||||
assert_equal '1', t.render!
|
||||
@@ -88,7 +93,10 @@ class TemplateTest < Minitest::Test
|
||||
|
||||
def test_lambda_is_called_once_from_custom_assigns_over_multiple_parses_and_renders
|
||||
t = Template.new
|
||||
assigns = { 'number' => -> { @global ||= 0; @global += 1 } }
|
||||
assigns = { 'number' => -> {
|
||||
@global ||= 0
|
||||
@global += 1
|
||||
} }
|
||||
assert_equal '1', t.parse("{{number}}").render!(assigns)
|
||||
assert_equal '1', t.parse("{{number}}").render!(assigns)
|
||||
assert_equal '1', t.render!(assigns)
|
||||
@@ -139,6 +147,16 @@ class TemplateTest < Minitest::Test
|
||||
refute_nil t.resource_limits.assign_score
|
||||
end
|
||||
|
||||
def test_resource_limits_assign_score_counts_bytes_not_characters
|
||||
t = Template.parse("{% assign foo = 'すごい' %}")
|
||||
t.render
|
||||
assert_equal 9, t.resource_limits.assign_score
|
||||
|
||||
t = Template.parse("{% capture foo %}すごい{% endcapture %}")
|
||||
t.render
|
||||
assert_equal 9, t.resource_limits.assign_score
|
||||
end
|
||||
|
||||
def test_resource_limits_assign_score_nested
|
||||
t = Template.parse("{% assign foo = 'aaaa' | reverse %}")
|
||||
|
||||
@@ -187,6 +205,14 @@ class TemplateTest < Minitest::Test
|
||||
assert_equal "ababab", t.render
|
||||
end
|
||||
|
||||
def test_render_length_uses_number_of_bytes_not_characters
|
||||
t = Template.parse("{% if true %}すごい{% endif %}")
|
||||
t.resource_limits.render_length_limit = 10
|
||||
assert_equal "Liquid error: Memory limits exceeded", t.render
|
||||
t.resource_limits.render_length_limit = 18
|
||||
assert_equal "すごい", t.render
|
||||
end
|
||||
|
||||
def test_default_resource_limits_unaffected_by_render_with_context
|
||||
context = Context.new
|
||||
t = Template.parse("{% for a in (1..100) %} {% assign foo = 1 %} {% endfor %}")
|
||||
@@ -206,7 +232,7 @@ class TemplateTest < Minitest::Test
|
||||
end
|
||||
|
||||
def test_render_bang_force_rethrow_errors_on_passed_context
|
||||
context = Context.new({ 'drop' => ErroneousDrop.new })
|
||||
context = Context.new('drop' => ErroneousDrop.new)
|
||||
t = Template.new.parse('{{ drop.bad_method }}')
|
||||
|
||||
e = assert_raises RuntimeError do
|
||||
@@ -217,7 +243,10 @@ class TemplateTest < Minitest::Test
|
||||
|
||||
def test_exception_renderer_that_returns_string
|
||||
exception = nil
|
||||
handler = ->(e) { exception = e; '<!-- error -->' }
|
||||
handler = ->(e) {
|
||||
exception = e
|
||||
'<!-- error -->'
|
||||
}
|
||||
|
||||
output = Template.parse("{{ 1 | divided_by: 0 }}").render({}, exception_renderer: handler)
|
||||
|
||||
@@ -228,7 +257,10 @@ class TemplateTest < Minitest::Test
|
||||
def test_exception_renderer_that_raises
|
||||
exception = nil
|
||||
assert_raises(Liquid::ZeroDivisionError) do
|
||||
Template.parse("{{ 1 | divided_by: 0 }}").render({}, exception_renderer: ->(e) { exception = e; raise })
|
||||
Template.parse("{{ 1 | divided_by: 0 }}").render({}, exception_renderer: ->(e) {
|
||||
exception = e
|
||||
raise
|
||||
})
|
||||
end
|
||||
assert exception.is_a?(Liquid::ZeroDivisionError)
|
||||
end
|
||||
@@ -249,7 +281,7 @@ class TemplateTest < Minitest::Test
|
||||
|
||||
def test_undefined_variables
|
||||
t = Template.parse("{{x}} {{y}} {{z.a}} {{z.b}} {{z.c.d}}")
|
||||
result = t.render({ 'x' => 33, 'z' => { 'a' => 32, 'c' => { 'e' => 31 } } }, { strict_variables: true })
|
||||
result = t.render({ 'x' => 33, 'z' => { 'a' => 32, 'c' => { 'e' => 31 } } }, strict_variables: true)
|
||||
|
||||
assert_equal '33 32 ', result
|
||||
assert_equal 3, t.errors.count
|
||||
@@ -274,14 +306,14 @@ class TemplateTest < Minitest::Test
|
||||
t = Template.parse("{{x}} {{y}} {{z.a}} {{z.b}} {{z.c.d}}")
|
||||
|
||||
assert_raises UndefinedVariable do
|
||||
t.render!({ 'x' => 33, 'z' => { 'a' => 32, 'c' => { 'e' => 31 } } }, { strict_variables: true })
|
||||
t.render!({ 'x' => 33, 'z' => { 'a' => 32, 'c' => { 'e' => 31 } } }, strict_variables: true)
|
||||
end
|
||||
end
|
||||
|
||||
def test_undefined_drop_methods
|
||||
d = DropWithUndefinedMethod.new
|
||||
t = Template.new.parse('{{ foo }} {{ woot }}')
|
||||
result = t.render(d, { strict_variables: true })
|
||||
result = t.render(d, strict_variables: true)
|
||||
|
||||
assert_equal 'foo ', result
|
||||
assert_equal 1, t.errors.count
|
||||
@@ -293,7 +325,7 @@ class TemplateTest < Minitest::Test
|
||||
t = Template.new.parse('{{ foo }} {{ woot }}')
|
||||
|
||||
assert_raises UndefinedDropMethod do
|
||||
t.render!(d, { strict_variables: true })
|
||||
t.render!(d, strict_variables: true)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -304,7 +336,7 @@ class TemplateTest < Minitest::Test
|
||||
"-#{v}-"
|
||||
end
|
||||
end
|
||||
result = t.render({ 'a' => 123, 'x' => 'foo' }, { filters: [filters], strict_filters: true })
|
||||
result = t.render({ 'a' => 123, 'x' => 'foo' }, filters: [filters], strict_filters: true)
|
||||
|
||||
assert_equal '123 ', result
|
||||
assert_equal 1, t.errors.count
|
||||
@@ -316,17 +348,17 @@ class TemplateTest < Minitest::Test
|
||||
t = Template.parse("{{x | somefilter1 | upcase | somefilter2}}")
|
||||
|
||||
assert_raises UndefinedFilter do
|
||||
t.render!({ 'x' => 'foo' }, { strict_filters: true })
|
||||
t.render!({ 'x' => 'foo' }, strict_filters: true)
|
||||
end
|
||||
end
|
||||
|
||||
def test_using_range_literal_works_as_expected
|
||||
t = Template.parse("{% assign foo = (x..y) %}{{ foo }}")
|
||||
result = t.render({ 'x' => 1, 'y' => 5 })
|
||||
result = t.render('x' => 1, 'y' => 5)
|
||||
assert_equal '1..5', result
|
||||
|
||||
t = Template.parse("{% assign nums = (x..y) %}{% for num in nums %}{{ num }}{% endfor %}")
|
||||
result = t.render({ 'x' => 1, 'y' => 5 })
|
||||
result = t.render('x' => 1, 'y' => 5)
|
||||
assert_equal '12345', result
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class TrimModeTest < Minitest::Test
|
||||
@@ -76,14 +78,14 @@ class TrimModeTest < Minitest::Test
|
||||
</p>
|
||||
</div>
|
||||
END_TEMPLATE
|
||||
expected = <<-END_EXPECTED
|
||||
<div>
|
||||
<p>
|
||||
#{whitespace}
|
||||
yes
|
||||
#{whitespace}
|
||||
</p>
|
||||
</div>
|
||||
expected = <<~END_EXPECTED
|
||||
<div>
|
||||
<p>
|
||||
#{whitespace}
|
||||
yes
|
||||
#{whitespace}
|
||||
</p>
|
||||
</div>
|
||||
END_EXPECTED
|
||||
assert_template_result(expected, text)
|
||||
|
||||
@@ -96,12 +98,12 @@ class TrimModeTest < Minitest::Test
|
||||
</p>
|
||||
</div>
|
||||
END_TEMPLATE
|
||||
expected = <<-END_EXPECTED
|
||||
<div>
|
||||
<p>
|
||||
#{whitespace}
|
||||
</p>
|
||||
</div>
|
||||
expected = <<~END_EXPECTED
|
||||
<div>
|
||||
<p>
|
||||
#{whitespace}
|
||||
</p>
|
||||
</div>
|
||||
END_EXPECTED
|
||||
assert_template_result(expected, text)
|
||||
end
|
||||
@@ -337,12 +339,12 @@ class TrimModeTest < Minitest::Test
|
||||
</p>
|
||||
</div>
|
||||
END_TEMPLATE
|
||||
expected = <<-END_EXPECTED
|
||||
<div>
|
||||
<p>
|
||||
#{whitespace}
|
||||
</p>
|
||||
</div>
|
||||
expected = <<~END_EXPECTED
|
||||
<div>
|
||||
<p>
|
||||
#{whitespace}
|
||||
</p>
|
||||
</div>
|
||||
END_EXPECTED
|
||||
assert_template_result(expected, text)
|
||||
end
|
||||
@@ -513,16 +515,16 @@ class TrimModeTest < Minitest::Test
|
||||
{% endraw %}
|
||||
</div>
|
||||
END_TEMPLATE
|
||||
expected = <<-END_EXPECTED
|
||||
<div>
|
||||
#{whitespace}
|
||||
{%- if true -%}
|
||||
<p>
|
||||
{{- 'John' -}}
|
||||
</p>
|
||||
{%- endif -%}
|
||||
#{whitespace}
|
||||
</div>
|
||||
expected = <<~END_EXPECTED
|
||||
<div>
|
||||
#{whitespace}
|
||||
{%- if true -%}
|
||||
<p>
|
||||
{{- 'John' -}}
|
||||
</p>
|
||||
{%- endif -%}
|
||||
#{whitespace}
|
||||
</div>
|
||||
END_EXPECTED
|
||||
assert_template_result(expected, text)
|
||||
end
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class VariableTest < Minitest::Test
|
||||
@@ -76,7 +78,7 @@ class VariableTest < Minitest::Test
|
||||
|
||||
def test_hash_with_default_proc
|
||||
template = Template.parse(%(Hello {{ test }}))
|
||||
assigns = Hash.new { |h, k| raise "Unknown variable '#{k}'" }
|
||||
assigns = Hash.new { |_h, k| raise "Unknown variable '#{k}'" }
|
||||
assigns['test'] = 'Tobi'
|
||||
assert_equal 'Hello Tobi', template.render!(assigns)
|
||||
assigns.delete('test')
|
||||
|
||||
Regular → Executable
+28
-6
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
ENV["MT_NO_EXPECTATIONS"] = "1"
|
||||
require 'minitest/autorun'
|
||||
@@ -8,13 +9,13 @@ require 'liquid.rb'
|
||||
require 'liquid/profiler'
|
||||
|
||||
mode = :strict
|
||||
if env_mode = ENV['LIQUID_PARSER_MODE']
|
||||
if (env_mode = ENV['LIQUID_PARSER_MODE'])
|
||||
puts "-- #{env_mode.upcase} ERROR MODE"
|
||||
mode = env_mode.to_sym
|
||||
end
|
||||
Liquid::Template.error_mode = mode
|
||||
|
||||
if ENV['LIQUID-C'] == '1'
|
||||
if ENV['LIQUID_C'] == '1'
|
||||
puts "-- LIQUID C"
|
||||
require 'liquid/c'
|
||||
end
|
||||
@@ -37,18 +38,18 @@ module Minitest
|
||||
include Liquid
|
||||
|
||||
def assert_template_result(expected, template, assigns = {}, message = nil)
|
||||
assert_equal expected, Template.parse(template).render!(assigns), message
|
||||
assert_equal expected, Template.parse(template, line_numbers: true).render!(assigns), message
|
||||
end
|
||||
|
||||
def assert_template_result_matches(expected, template, assigns = {}, message = nil)
|
||||
return assert_template_result(expected, template, assigns, message) unless expected.is_a? Regexp
|
||||
return assert_template_result(expected, template, assigns, message) unless expected.is_a?(Regexp)
|
||||
|
||||
assert_match expected, Template.parse(template).render!(assigns), message
|
||||
assert_match expected, Template.parse(template, line_numbers: true).render!(assigns), message
|
||||
end
|
||||
|
||||
def assert_match_syntax_error(match, template, assigns = {})
|
||||
exception = assert_raises(Liquid::SyntaxError) do
|
||||
Template.parse(template).render(assigns)
|
||||
Template.parse(template, line_numbers: true).render(assigns)
|
||||
end
|
||||
assert_match match, exception.message
|
||||
end
|
||||
@@ -84,6 +85,13 @@ module Minitest
|
||||
ensure
|
||||
Liquid::Template.error_mode = old_mode
|
||||
end
|
||||
|
||||
def with_custom_tag(tag_name, tag_class)
|
||||
Liquid::Template.register_tag(tag_name, tag_class)
|
||||
yield
|
||||
ensure
|
||||
Liquid::Template.tags.delete(tag_name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -114,3 +122,17 @@ class ErrorDrop < Liquid::Drop
|
||||
raise Exception, 'exception'
|
||||
end
|
||||
end
|
||||
|
||||
class StubFileSystem
|
||||
attr_reader :file_read_count
|
||||
|
||||
def initialize(values)
|
||||
@file_read_count = 0
|
||||
@values = values
|
||||
end
|
||||
|
||||
def read_template_file(template_path)
|
||||
@file_read_count += 1
|
||||
@values.fetch(template_path)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class BlockUnitTest < Minitest::Test
|
||||
@@ -44,10 +46,47 @@ class BlockUnitTest < Minitest::Test
|
||||
end
|
||||
|
||||
def test_with_custom_tag
|
||||
Liquid::Template.register_tag("testtag", Block)
|
||||
assert Liquid::Template.parse("{% testtag %} {% endtesttag %}")
|
||||
ensure
|
||||
Liquid::Template.tags.delete('testtag')
|
||||
with_custom_tag('testtag', Block) do
|
||||
assert Liquid::Template.parse("{% testtag %} {% endtesttag %}")
|
||||
end
|
||||
end
|
||||
|
||||
def test_custom_block_tags_have_a_default_render_to_output_buffer_method_for_backwards_compatibility
|
||||
klass1 = Class.new(Block) do
|
||||
def render(*)
|
||||
'hello'
|
||||
end
|
||||
end
|
||||
|
||||
with_custom_tag('blabla', klass1) do
|
||||
template = Liquid::Template.parse("{% blabla %} bla {% endblabla %}")
|
||||
|
||||
assert_equal 'hello', template.render
|
||||
|
||||
buf = +''
|
||||
output = template.render({}, output: buf)
|
||||
assert_equal 'hello', output
|
||||
assert_equal 'hello', buf
|
||||
assert_equal buf.object_id, output.object_id
|
||||
end
|
||||
|
||||
klass2 = Class.new(klass1) do
|
||||
def render(*)
|
||||
'foo' + super + 'bar'
|
||||
end
|
||||
end
|
||||
|
||||
with_custom_tag('blabla', klass2) do
|
||||
template = Liquid::Template.parse("{% blabla %} foo {% endblabla %}")
|
||||
|
||||
assert_equal 'foohellobar', template.render
|
||||
|
||||
buf = +''
|
||||
output = template.render({}, output: buf)
|
||||
assert_equal 'foohellobar', output
|
||||
assert_equal 'foohellobar', buf
|
||||
assert_equal buf.object_id, output.object_id
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class ConditionUnitTest < Minitest::Test
|
||||
@@ -24,9 +26,9 @@ class ConditionUnitTest < Minitest::Test
|
||||
assert_evaluates_true 1, '<=', 1
|
||||
# negative numbers
|
||||
assert_evaluates_true 1, '>', -1
|
||||
assert_evaluates_true (-1), '<', 1
|
||||
assert_evaluates_true(-1, '<', 1)
|
||||
assert_evaluates_true 1.0, '>', -1.0
|
||||
assert_evaluates_true (-1.0), '<', 1.0
|
||||
assert_evaluates_true(-1.0, '<', 1.0)
|
||||
end
|
||||
|
||||
def test_default_operators_evalute_false
|
||||
@@ -68,7 +70,7 @@ class ConditionUnitTest < Minitest::Test
|
||||
assert_nil Condition.new({}, '>', 2).evaluate
|
||||
assert_nil Condition.new(2, '>', {}).evaluate
|
||||
assert_equal false, Condition.new({}, '==', 2).evaluate
|
||||
assert_equal true, Condition.new({ 'a' => 1 }, '==', { 'a' => 1 }).evaluate
|
||||
assert_equal true, Condition.new({ 'a' => 1 }, '==', 'a' => 1).evaluate
|
||||
assert_equal true, Condition.new({ 'a' => 2 }, 'contains', 'a').evaluate
|
||||
end
|
||||
|
||||
@@ -107,11 +109,11 @@ class ConditionUnitTest < Minitest::Test
|
||||
|
||||
assert_equal false, condition.evaluate
|
||||
|
||||
condition.or Condition.new(2, '==', 1)
|
||||
condition.or(Condition.new(2, '==', 1))
|
||||
|
||||
assert_equal false, condition.evaluate
|
||||
|
||||
condition.or Condition.new(1, '==', 1)
|
||||
condition.or(Condition.new(1, '==', 1))
|
||||
|
||||
assert_equal true, condition.evaluate
|
||||
end
|
||||
@@ -121,22 +123,22 @@ class ConditionUnitTest < Minitest::Test
|
||||
|
||||
assert_equal true, condition.evaluate
|
||||
|
||||
condition.and Condition.new(2, '==', 2)
|
||||
condition.and(Condition.new(2, '==', 2))
|
||||
|
||||
assert_equal true, condition.evaluate
|
||||
|
||||
condition.and Condition.new(2, '==', 1)
|
||||
condition.and(Condition.new(2, '==', 1))
|
||||
|
||||
assert_equal false, condition.evaluate
|
||||
end
|
||||
|
||||
def test_should_allow_custom_proc_operator
|
||||
Condition.operators['starts_with'] = proc { |cond, left, right| left =~ %r{^#{right}} }
|
||||
Condition.operators['starts_with'] = proc { |_cond, left, right| left =~ /^#{right}/ }
|
||||
|
||||
assert_evaluates_true 'bob', 'starts_with', 'b'
|
||||
assert_evaluates_false 'bob', 'starts_with', 'o'
|
||||
ensure
|
||||
Condition.operators.delete 'starts_with'
|
||||
Condition.operators.delete('starts_with')
|
||||
end
|
||||
|
||||
def test_left_or_right_may_contain_operators
|
||||
|
||||
+103
-13
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class HundredCentes
|
||||
@@ -83,7 +85,7 @@ class ContextUnitTest < Minitest::Test
|
||||
@context['date'] = Date.today
|
||||
assert_equal Date.today, @context['date']
|
||||
|
||||
now = DateTime.now
|
||||
now = Time.now
|
||||
@context['datetime'] = now
|
||||
assert_equal now, @context['datetime']
|
||||
|
||||
@@ -206,9 +208,9 @@ class ContextUnitTest < Minitest::Test
|
||||
end
|
||||
|
||||
def test_merge
|
||||
@context.merge({ "test" => "test" })
|
||||
@context.merge("test" => "test")
|
||||
assert_equal 'test', @context['test']
|
||||
@context.merge({ "test" => "newvalue", "foo" => "bar" })
|
||||
@context.merge("test" => "newvalue", "foo" => "bar")
|
||||
assert_equal 'newvalue', @context['test']
|
||||
assert_equal 'bar', @context['foo']
|
||||
end
|
||||
@@ -235,10 +237,10 @@ class ContextUnitTest < Minitest::Test
|
||||
|
||||
def test_hash_to_array_transition
|
||||
@context['colors'] = {
|
||||
'Blue' => ['003366', '336699', '6699CC', '99CCFF'],
|
||||
'Green' => ['003300', '336633', '669966', '99CC99'],
|
||||
'Yellow' => ['CC9900', 'FFCC00', 'FFFF99', 'FFFFCC'],
|
||||
'Red' => ['660000', '993333', 'CC6666', 'FF9999']
|
||||
'Blue' => ['003366', '336699', '6699CC', '99CCFF'],
|
||||
'Green' => ['003300', '336633', '669966', '99CC99'],
|
||||
'Yellow' => ['CC9900', 'FFCC00', 'FFFF99', 'FFFFCC'],
|
||||
'Red' => ['660000', '993333', 'CC6666', 'FF9999'],
|
||||
}
|
||||
|
||||
assert_equal '003366', @context['colors.Blue[0]']
|
||||
@@ -263,7 +265,7 @@ class ContextUnitTest < Minitest::Test
|
||||
|
||||
def test_access_hashes_with_hash_notation
|
||||
@context['products'] = { 'count' => 5, 'tags' => ['deepsnow', 'freestyle'] }
|
||||
@context['product'] = { 'variants' => [ { 'title' => 'draft151cm' }, { 'title' => 'element151cm' } ] }
|
||||
@context['product'] = { 'variants' => [{ 'title' => 'draft151cm' }, { 'title' => 'element151cm' }] }
|
||||
|
||||
assert_equal 5, @context['products["count"]']
|
||||
assert_equal 'deepsnow', @context['products["tags"][0]']
|
||||
@@ -301,7 +303,7 @@ class ContextUnitTest < Minitest::Test
|
||||
end
|
||||
|
||||
def test_first_can_appear_in_middle_of_callchain
|
||||
@context['product'] = { 'variants' => [ { 'title' => 'draft151cm' }, { 'title' => 'element151cm' } ] }
|
||||
@context['product'] = { 'variants' => [{ 'title' => 'draft151cm' }, { 'title' => 'element151cm' }] }
|
||||
|
||||
assert_equal 'draft151cm', @context['product.variants[0].title']
|
||||
assert_equal 'element151cm', @context['product.variants[1].title']
|
||||
@@ -403,7 +405,11 @@ class ContextUnitTest < Minitest::Test
|
||||
end
|
||||
|
||||
def test_lambda_is_called_once
|
||||
@context['callcount'] = proc { @global ||= 0; @global += 1; @global.to_s }
|
||||
@context['callcount'] = proc {
|
||||
@global ||= 0
|
||||
@global += 1
|
||||
@global.to_s
|
||||
}
|
||||
|
||||
assert_equal '1', @context['callcount']
|
||||
assert_equal '1', @context['callcount']
|
||||
@@ -413,7 +419,11 @@ class ContextUnitTest < Minitest::Test
|
||||
end
|
||||
|
||||
def test_nested_lambda_is_called_once
|
||||
@context['callcount'] = { "lambda" => proc { @global ||= 0; @global += 1; @global.to_s } }
|
||||
@context['callcount'] = { "lambda" => proc {
|
||||
@global ||= 0
|
||||
@global += 1
|
||||
@global.to_s
|
||||
} }
|
||||
|
||||
assert_equal '1', @context['callcount.lambda']
|
||||
assert_equal '1', @context['callcount.lambda']
|
||||
@@ -423,7 +433,11 @@ class ContextUnitTest < Minitest::Test
|
||||
end
|
||||
|
||||
def test_lambda_in_array_is_called_once
|
||||
@context['callcount'] = [1, 2, proc { @global ||= 0; @global += 1; @global.to_s }, 4, 5]
|
||||
@context['callcount'] = [1, 2, proc {
|
||||
@global ||= 0
|
||||
@global += 1
|
||||
@global.to_s
|
||||
}, 4, 5]
|
||||
|
||||
assert_equal '1', @context['callcount[2]']
|
||||
assert_equal '1', @context['callcount[2]']
|
||||
@@ -453,7 +467,7 @@ class ContextUnitTest < Minitest::Test
|
||||
end
|
||||
|
||||
def test_context_initialization_with_a_proc_in_environment
|
||||
contx = Context.new([test: ->(c) { c['poutine'] }], { test: :foo })
|
||||
contx = Context.new([test: ->(c) { c['poutine'] }], test: :foo)
|
||||
|
||||
assert contx
|
||||
assert_nil contx['poutine']
|
||||
@@ -468,11 +482,87 @@ class ContextUnitTest < Minitest::Test
|
||||
assert_equal 'hi filtered', context.apply_global_filter('hi')
|
||||
end
|
||||
|
||||
def test_static_environments_are_read_with_lower_priority_than_environments
|
||||
context = Context.build(
|
||||
static_environments: { 'shadowed' => 'static', 'unshadowed' => 'static' },
|
||||
environments: { 'shadowed' => 'dynamic' }
|
||||
)
|
||||
|
||||
assert_equal 'dynamic', context['shadowed']
|
||||
assert_equal 'static', context['unshadowed']
|
||||
end
|
||||
|
||||
def test_apply_global_filter_when_no_global_filter_exist
|
||||
context = Context.new
|
||||
assert_equal 'hi', context.apply_global_filter('hi')
|
||||
end
|
||||
|
||||
def test_new_isolated_subcontext_does_not_inherit_variables
|
||||
super_context = Context.new
|
||||
super_context['my_variable'] = 'some value'
|
||||
subcontext = super_context.new_isolated_subcontext
|
||||
|
||||
assert_nil subcontext['my_variable']
|
||||
end
|
||||
|
||||
def test_new_isolated_subcontext_inherits_static_environment
|
||||
super_context = Context.build(static_environments: { 'my_environment_value' => 'my value' })
|
||||
subcontext = super_context.new_isolated_subcontext
|
||||
|
||||
assert_equal 'my value', subcontext['my_environment_value']
|
||||
end
|
||||
|
||||
def test_new_isolated_subcontext_inherits_resource_limits
|
||||
resource_limits = ResourceLimits.new({})
|
||||
super_context = Context.new({}, {}, {}, false, resource_limits)
|
||||
subcontext = super_context.new_isolated_subcontext
|
||||
assert_equal resource_limits, subcontext.resource_limits
|
||||
end
|
||||
|
||||
def test_new_isolated_subcontext_inherits_exception_renderer
|
||||
super_context = Context.new
|
||||
super_context.exception_renderer = ->(_e) { 'my exception message' }
|
||||
subcontext = super_context.new_isolated_subcontext
|
||||
assert_equal 'my exception message', subcontext.handle_error(Liquid::Error.new)
|
||||
end
|
||||
|
||||
def test_new_isolated_subcontext_does_not_inherit_non_static_registers
|
||||
registers = {
|
||||
my_register: :my_value,
|
||||
}
|
||||
super_context = Context.new({}, {}, StaticRegisters.new(registers))
|
||||
super_context.registers[:my_register] = :my_alt_value
|
||||
subcontext = super_context.new_isolated_subcontext
|
||||
assert_equal :my_value, subcontext.registers[:my_register]
|
||||
end
|
||||
|
||||
def test_new_isolated_subcontext_inherits_static_registers
|
||||
super_context = Context.build(registers: { my_register: :my_value })
|
||||
subcontext = super_context.new_isolated_subcontext
|
||||
assert_equal :my_value, subcontext.registers[:my_register]
|
||||
end
|
||||
|
||||
def test_new_isolated_subcontext_registers_do_not_pollute_context
|
||||
super_context = Context.build(registers: { my_register: :my_value })
|
||||
subcontext = super_context.new_isolated_subcontext
|
||||
subcontext.registers[:my_register] = :my_alt_value
|
||||
assert_equal :my_value, super_context.registers[:my_register]
|
||||
end
|
||||
|
||||
def test_new_isolated_subcontext_inherits_filters
|
||||
my_filter = Module.new do
|
||||
def my_filter(*)
|
||||
'my filter result'
|
||||
end
|
||||
end
|
||||
|
||||
super_context = Context.new
|
||||
super_context.add_filters([my_filter])
|
||||
subcontext = super_context.new_isolated_subcontext
|
||||
template = Template.parse('{{ 123 | my_filter }}')
|
||||
assert_equal 'my filter result', template.render(subcontext)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def assert_no_object_allocations
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class FileSystemUnitTest < Minitest::Test
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class I18nUnitTest < Minitest::Test
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class LexerUnitTest < Minitest::Test
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class ParserUnitTest < Minitest::Test
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class PartialCacheUnitTest < Minitest::Test
|
||||
def test_uses_the_file_system_register_if_present
|
||||
context = Liquid::Context.build(
|
||||
registers: {
|
||||
file_system: StubFileSystem.new('my_partial' => 'my partial body'),
|
||||
}
|
||||
)
|
||||
|
||||
partial = Liquid::PartialCache.load(
|
||||
'my_partial',
|
||||
context: context,
|
||||
parse_context: Liquid::ParseContext.new
|
||||
)
|
||||
|
||||
assert_equal 'my partial body', partial.render
|
||||
end
|
||||
|
||||
def test_reads_from_the_file_system_only_once_per_file
|
||||
file_system = StubFileSystem.new('my_partial' => 'some partial body')
|
||||
context = Liquid::Context.build(
|
||||
registers: { file_system: file_system }
|
||||
)
|
||||
|
||||
2.times do
|
||||
Liquid::PartialCache.load(
|
||||
'my_partial',
|
||||
context: context,
|
||||
parse_context: Liquid::ParseContext.new
|
||||
)
|
||||
end
|
||||
|
||||
assert_equal 1, file_system.file_read_count
|
||||
end
|
||||
|
||||
def test_cache_state_is_stored_per_context
|
||||
parse_context = Liquid::ParseContext.new
|
||||
shared_file_system = StubFileSystem.new(
|
||||
'my_partial' => 'my shared value'
|
||||
)
|
||||
context_one = Liquid::Context.build(
|
||||
registers: {
|
||||
file_system: shared_file_system,
|
||||
}
|
||||
)
|
||||
context_two = Liquid::Context.build(
|
||||
registers: {
|
||||
file_system: shared_file_system,
|
||||
}
|
||||
)
|
||||
|
||||
2.times do
|
||||
Liquid::PartialCache.load(
|
||||
'my_partial',
|
||||
context: context_one,
|
||||
parse_context: parse_context
|
||||
)
|
||||
end
|
||||
|
||||
Liquid::PartialCache.load(
|
||||
'my_partial',
|
||||
context: context_two,
|
||||
parse_context: parse_context
|
||||
)
|
||||
|
||||
assert_equal 2, shared_file_system.file_read_count
|
||||
end
|
||||
|
||||
def test_cache_is_not_broken_when_a_different_parse_context_is_used
|
||||
file_system = StubFileSystem.new('my_partial' => 'some partial body')
|
||||
context = Liquid::Context.build(
|
||||
registers: { file_system: file_system }
|
||||
)
|
||||
|
||||
Liquid::PartialCache.load(
|
||||
'my_partial',
|
||||
context: context,
|
||||
parse_context: Liquid::ParseContext.new(my_key: 'value one')
|
||||
)
|
||||
Liquid::PartialCache.load(
|
||||
'my_partial',
|
||||
context: context,
|
||||
parse_context: Liquid::ParseContext.new(my_key: 'value two')
|
||||
)
|
||||
|
||||
# Technically what we care about is that the file was parsed twice,
|
||||
# but measuring file reads is an OK proxy for this.
|
||||
assert_equal 1, file_system.file_read_count
|
||||
end
|
||||
end
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class RegexpUnitTest < Minitest::Test
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class DisabledTagsUnitTest < Minitest::Test
|
||||
include Liquid
|
||||
|
||||
def test_disables_tag_specified
|
||||
register = DisabledTags.new
|
||||
register.disable(%w(foo bar)) do
|
||||
assert_equal true, register.disabled?("foo")
|
||||
assert_equal true, register.disabled?("bar")
|
||||
assert_equal false, register.disabled?("unknown")
|
||||
end
|
||||
end
|
||||
|
||||
def test_disables_nested_tags
|
||||
register = DisabledTags.new
|
||||
register.disable(["foo"]) do
|
||||
register.disable(["foo"]) do
|
||||
assert_equal true, register.disabled?("foo")
|
||||
assert_equal false, register.disabled?("bar")
|
||||
end
|
||||
register.disable(["bar"]) do
|
||||
assert_equal true, register.disabled?("foo")
|
||||
assert_equal true, register.disabled?("bar")
|
||||
register.disable(["foo"]) do
|
||||
assert_equal true, register.disabled?("foo")
|
||||
assert_equal true, register.disabled?("bar")
|
||||
end
|
||||
end
|
||||
assert_equal true, register.disabled?("foo")
|
||||
assert_equal false, register.disabled?("bar")
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,248 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class StaticRegistersUnitTest < Minitest::Test
|
||||
include Liquid
|
||||
|
||||
def set
|
||||
static_register = StaticRegisters.new
|
||||
static_register[nil] = true
|
||||
static_register[1] = :one
|
||||
static_register[:one] = "one"
|
||||
static_register["two"] = "three"
|
||||
static_register["two"] = 3
|
||||
static_register[false] = nil
|
||||
|
||||
assert_equal({ nil => true, 1 => :one, :one => "one", "two" => 3, false => nil }, static_register.registers)
|
||||
|
||||
static_register
|
||||
end
|
||||
|
||||
def test_get
|
||||
static_register = set
|
||||
|
||||
assert_equal true, static_register[nil]
|
||||
assert_equal :one, static_register[1]
|
||||
assert_equal "one", static_register[:one]
|
||||
assert_equal 3, static_register["two"]
|
||||
assert_nil static_register[false]
|
||||
assert_nil static_register["unknown"]
|
||||
end
|
||||
|
||||
def test_delete
|
||||
static_register = set
|
||||
|
||||
assert_equal true, static_register.delete(nil)
|
||||
assert_equal :one, static_register.delete(1)
|
||||
assert_equal "one", static_register.delete(:one)
|
||||
assert_equal 3, static_register.delete("two")
|
||||
assert_nil static_register.delete(false)
|
||||
assert_nil static_register.delete("unknown")
|
||||
|
||||
assert_equal({}, static_register.registers)
|
||||
end
|
||||
|
||||
def test_fetch
|
||||
static_register = set
|
||||
|
||||
assert_equal true, static_register.fetch(nil)
|
||||
assert_equal :one, static_register.fetch(1)
|
||||
assert_equal "one", static_register.fetch(:one)
|
||||
assert_equal 3, static_register.fetch("two")
|
||||
assert_nil static_register.fetch(false)
|
||||
assert_nil static_register.fetch("unknown")
|
||||
end
|
||||
|
||||
def test_fetch_default
|
||||
static_register = StaticRegisters.new
|
||||
|
||||
assert_equal true, static_register.fetch(nil, true)
|
||||
assert_equal :one, static_register.fetch(1, :one)
|
||||
assert_equal "one", static_register.fetch(:one, "one")
|
||||
assert_equal 3, static_register.fetch("two", 3)
|
||||
assert_nil static_register.fetch(false, nil)
|
||||
end
|
||||
|
||||
def test_key
|
||||
static_register = set
|
||||
|
||||
assert_equal true, static_register.key?(nil)
|
||||
assert_equal true, static_register.key?(1)
|
||||
assert_equal true, static_register.key?(:one)
|
||||
assert_equal true, static_register.key?("two")
|
||||
assert_equal true, static_register.key?(false)
|
||||
assert_equal false, static_register.key?("unknown")
|
||||
assert_equal false, static_register.key?(true)
|
||||
end
|
||||
|
||||
def set_with_static
|
||||
static_register = StaticRegisters.new(nil => true, 1 => :one, :one => "one", "two" => 3, false => nil)
|
||||
static_register[nil] = false
|
||||
static_register["two"] = 4
|
||||
static_register[true] = "foo"
|
||||
|
||||
assert_equal({ nil => true, 1 => :one, :one => "one", "two" => 3, false => nil }, static_register.static)
|
||||
assert_equal({ nil => false, "two" => 4, true => "foo" }, static_register.registers)
|
||||
|
||||
static_register
|
||||
end
|
||||
|
||||
def test_get_with_static
|
||||
static_register = set_with_static
|
||||
|
||||
assert_equal false, static_register[nil]
|
||||
assert_equal :one, static_register[1]
|
||||
assert_equal "one", static_register[:one]
|
||||
assert_equal 4, static_register["two"]
|
||||
assert_equal "foo", static_register[true]
|
||||
assert_nil static_register[false]
|
||||
end
|
||||
|
||||
def test_delete_with_static
|
||||
static_register = set_with_static
|
||||
|
||||
assert_equal false, static_register.delete(nil)
|
||||
assert_equal 4, static_register.delete("two")
|
||||
assert_equal "foo", static_register.delete(true)
|
||||
assert_nil static_register.delete("unknown")
|
||||
assert_nil static_register.delete(:one)
|
||||
|
||||
assert_equal({}, static_register.registers)
|
||||
assert_equal({ nil => true, 1 => :one, :one => "one", "two" => 3, false => nil }, static_register.static)
|
||||
end
|
||||
|
||||
def test_fetch_with_static
|
||||
static_register = set_with_static
|
||||
|
||||
assert_equal false, static_register.fetch(nil)
|
||||
assert_equal :one, static_register.fetch(1)
|
||||
assert_equal "one", static_register.fetch(:one)
|
||||
assert_equal 4, static_register.fetch("two")
|
||||
assert_equal "foo", static_register.fetch(true)
|
||||
assert_nil static_register.fetch(false)
|
||||
end
|
||||
|
||||
def test_key_with_static
|
||||
static_register = set_with_static
|
||||
|
||||
assert_equal true, static_register.key?(nil)
|
||||
assert_equal true, static_register.key?(1)
|
||||
assert_equal true, static_register.key?(:one)
|
||||
assert_equal true, static_register.key?("two")
|
||||
assert_equal true, static_register.key?(false)
|
||||
assert_equal false, static_register.key?("unknown")
|
||||
assert_equal true, static_register.key?(true)
|
||||
end
|
||||
|
||||
def test_static_register_can_be_frozen
|
||||
static_register = set_with_static
|
||||
|
||||
static = static_register.static.freeze
|
||||
|
||||
assert_raises(RuntimeError) do
|
||||
static["two"] = "foo"
|
||||
end
|
||||
|
||||
assert_raises(RuntimeError) do
|
||||
static["unknown"] = "foo"
|
||||
end
|
||||
|
||||
assert_raises(RuntimeError) do
|
||||
static.delete("two")
|
||||
end
|
||||
end
|
||||
|
||||
def test_new_static_retains_static
|
||||
static_register = StaticRegisters.new(nil => true, 1 => :one, :one => "one", "two" => 3, false => nil)
|
||||
static_register["one"] = 1
|
||||
static_register["two"] = 2
|
||||
static_register["three"] = 3
|
||||
|
||||
new_register = StaticRegisters.new(static_register)
|
||||
assert_equal({}, new_register.registers)
|
||||
|
||||
new_register["one"] = 4
|
||||
new_register["two"] = 5
|
||||
new_register["three"] = 6
|
||||
|
||||
newest_register = StaticRegisters.new(new_register)
|
||||
assert_equal({}, newest_register.registers)
|
||||
|
||||
newest_register["one"] = 7
|
||||
newest_register["two"] = 8
|
||||
newest_register["three"] = 9
|
||||
|
||||
assert_equal({ "one" => 1, "two" => 2, "three" => 3 }, static_register.registers)
|
||||
assert_equal({ "one" => 4, "two" => 5, "three" => 6 }, new_register.registers)
|
||||
assert_equal({ "one" => 7, "two" => 8, "three" => 9 }, newest_register.registers)
|
||||
assert_equal({ nil => true, 1 => :one, :one => "one", "two" => 3, false => nil }, static_register.static)
|
||||
assert_equal({ nil => true, 1 => :one, :one => "one", "two" => 3, false => nil }, new_register.static)
|
||||
assert_equal({ nil => true, 1 => :one, :one => "one", "two" => 3, false => nil }, newest_register.static)
|
||||
end
|
||||
|
||||
def test_multiple_instances_are_unique
|
||||
static_register = StaticRegisters.new(nil => true, 1 => :one, :one => "one", "two" => 3, false => nil)
|
||||
static_register["one"] = 1
|
||||
static_register["two"] = 2
|
||||
static_register["three"] = 3
|
||||
|
||||
new_register = StaticRegisters.new(foo: :bar)
|
||||
assert_equal({}, new_register.registers)
|
||||
|
||||
new_register["one"] = 4
|
||||
new_register["two"] = 5
|
||||
new_register["three"] = 6
|
||||
|
||||
newest_register = StaticRegisters.new(bar: :foo)
|
||||
assert_equal({}, newest_register.registers)
|
||||
|
||||
newest_register["one"] = 7
|
||||
newest_register["two"] = 8
|
||||
newest_register["three"] = 9
|
||||
|
||||
assert_equal({ "one" => 1, "two" => 2, "three" => 3 }, static_register.registers)
|
||||
assert_equal({ "one" => 4, "two" => 5, "three" => 6 }, new_register.registers)
|
||||
assert_equal({ "one" => 7, "two" => 8, "three" => 9 }, newest_register.registers)
|
||||
assert_equal({ nil => true, 1 => :one, :one => "one", "two" => 3, false => nil }, static_register.static)
|
||||
assert_equal({ foo: :bar }, new_register.static)
|
||||
assert_equal({ bar: :foo }, newest_register.static)
|
||||
end
|
||||
|
||||
def test_can_update_static_directly_and_updates_all_instances
|
||||
static_register = StaticRegisters.new(nil => true, 1 => :one, :one => "one", "two" => 3, false => nil)
|
||||
static_register["one"] = 1
|
||||
static_register["two"] = 2
|
||||
static_register["three"] = 3
|
||||
|
||||
new_register = StaticRegisters.new(static_register)
|
||||
assert_equal({}, new_register.registers)
|
||||
|
||||
assert_equal({ nil => true, 1 => :one, :one => "one", "two" => 3, false => nil }, static_register.static)
|
||||
|
||||
new_register["one"] = 4
|
||||
new_register["two"] = 5
|
||||
new_register["three"] = 6
|
||||
new_register.static["four"] = 10
|
||||
|
||||
newest_register = StaticRegisters.new(new_register)
|
||||
assert_equal({}, newest_register.registers)
|
||||
|
||||
assert_equal({ nil => true, 1 => :one, :one => "one", "two" => 3, false => nil, "four" => 10 }, new_register.static)
|
||||
|
||||
newest_register["one"] = 7
|
||||
newest_register["two"] = 8
|
||||
newest_register["three"] = 9
|
||||
new_register.static["four"] = 5
|
||||
new_register.static["five"] = 15
|
||||
|
||||
assert_equal({ "one" => 1, "two" => 2, "three" => 3 }, static_register.registers)
|
||||
assert_equal({ "one" => 4, "two" => 5, "three" => 6 }, new_register.registers)
|
||||
assert_equal({ "one" => 7, "two" => 8, "three" => 9 }, newest_register.registers)
|
||||
|
||||
assert_equal({ nil => true, 1 => :one, :one => "one", "two" => 3, false => nil, "four" => 5, "five" => 15 }, newest_register.static)
|
||||
|
||||
assert_equal({ nil => true, 1 => :one, :one => "one", "two" => 3, false => nil, "four" => 5, "five" => 15 }, static_register.static)
|
||||
assert_equal({ nil => true, 1 => :one, :one => "one", "two" => 3, false => nil, "four" => 5, "five" => 15 }, new_register.static)
|
||||
end
|
||||
end
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class StrainerUnitTest < Minitest::Test
|
||||
@@ -36,7 +38,8 @@ class StrainerUnitTest < Minitest::Test
|
||||
rescue Liquid::ArgumentError => e
|
||||
assert_match(
|
||||
/\ALiquid error: wrong number of arguments \((1 for 0|given 1, expected 0)\)\z/,
|
||||
e.message)
|
||||
e.message
|
||||
)
|
||||
assert_equal e.backtrace[0].split(':')[0], __FILE__
|
||||
end
|
||||
end
|
||||
@@ -135,7 +138,7 @@ class StrainerUnitTest < Minitest::Test
|
||||
end
|
||||
|
||||
module LateAddedFilter
|
||||
def late_added_filter(input)
|
||||
def late_added_filter(_input)
|
||||
"filtered"
|
||||
end
|
||||
end
|
||||
@@ -150,7 +153,7 @@ class StrainerUnitTest < Minitest::Test
|
||||
mod = Module.new do
|
||||
class << self
|
||||
attr_accessor :include_count
|
||||
def included(mod)
|
||||
def included(_mod)
|
||||
self.include_count += 1
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class TagUnitTest < Minitest::Test
|
||||
@@ -18,4 +20,42 @@ class TagUnitTest < Minitest::Test
|
||||
tag = Tag.parse("some_tag", "", Tokenizer.new(""), ParseContext.new)
|
||||
assert_equal 'some_tag', tag.tag_name
|
||||
end
|
||||
|
||||
def test_custom_tags_have_a_default_render_to_output_buffer_method_for_backwards_compatibility
|
||||
klass1 = Class.new(Tag) do
|
||||
def render(*)
|
||||
'hello'
|
||||
end
|
||||
end
|
||||
|
||||
with_custom_tag('blabla', klass1) do
|
||||
template = Liquid::Template.parse("{% blabla %}")
|
||||
|
||||
assert_equal 'hello', template.render
|
||||
|
||||
buf = +''
|
||||
output = template.render({}, output: buf)
|
||||
assert_equal 'hello', output
|
||||
assert_equal 'hello', buf
|
||||
assert_equal buf.object_id, output.object_id
|
||||
end
|
||||
|
||||
klass2 = Class.new(klass1) do
|
||||
def render(*)
|
||||
'foo' + super + 'bar'
|
||||
end
|
||||
end
|
||||
|
||||
with_custom_tag('blabla', klass2) do
|
||||
template = Liquid::Template.parse("{% blabla %}")
|
||||
|
||||
assert_equal 'foohellobar', template.render
|
||||
|
||||
buf = +''
|
||||
output = template.render({}, output: buf)
|
||||
assert_equal 'foohellobar', output
|
||||
assert_equal 'foohellobar', buf
|
||||
assert_equal buf.object_id, output.object_id
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class CaseTagUnitTest < Minitest::Test
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class ForTagUnitTest < Minitest::Test
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class IfTagUnitTest < Minitest::Test
|
||||
def test_if_nodelist
|
||||
template = Liquid::Template.parse('{% if true %}IF{% else %}ELSE{% endif %}')
|
||||
assert_equal ['IF', 'ELSE'], template.root.nodelist[0].nodelist.map(&:nodelist).flatten
|
||||
assert_equal(['IF', 'ELSE'], template.root.nodelist[0].nodelist.map(&:nodelist).flatten)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class TemplateUnitTest < Minitest::Test
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class TokenizerTest < Minitest::Test
|
||||
@@ -33,7 +35,7 @@ class TokenizerTest < Minitest::Test
|
||||
def tokenize(source)
|
||||
tokenizer = Liquid::Tokenizer.new(source)
|
||||
tokens = []
|
||||
while t = tokenizer.shift
|
||||
while (t = tokenizer.shift)
|
||||
tokens << t
|
||||
end
|
||||
tokens
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class VariableUnitTest < Minitest::Test
|
||||
|
||||
Reference in New Issue
Block a user