Merge branch 'master' into sort-numeric

This commit is contained in:
Mike Angell
2019-10-04 15:20:00 +10:00
committed by GitHub
138 changed files with 4094 additions and 1051 deletions
+2
View File
@@ -1,3 +1,5 @@
# frozen_string_literal: true
require 'test_helper'
class AssignTest < Minitest::Test
+8 -5
View File
@@ -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
+2
View File
@@ -1,3 +1,5 @@
# frozen_string_literal: true
require 'test_helper'
class BlockTest < Minitest::Test
+2
View File
@@ -1,3 +1,5 @@
# frozen_string_literal: true
require 'test_helper'
class CaptureTest < Minitest::Test
+2
View File
@@ -1,3 +1,5 @@
# frozen_string_literal: true
require 'test_helper'
class ContextTest < Minitest::Test
+2
View File
@@ -1,3 +1,5 @@
# frozen_string_literal: true
require 'test_helper'
class DocumentTest < Minitest::Test
+16 -7
View File
@@ -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
+22 -19
View File
@@ -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
+8 -6
View File
@@ -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
+4 -2
View File
@@ -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
+4 -2
View File
@@ -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
+247
View File
@@ -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
+3 -1
View File
@@ -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
+4 -2
View File
@@ -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
+2
View File
@@ -1,3 +1,5 @@
# frozen_string_literal: true
require 'test_helper'
module SecurityFilter
+219 -13
View File
@@ -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)
+2
View File
@@ -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
+13
View File
@@ -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
+36 -6
View File
@@ -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
+3 -1
View File
@@ -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
+19 -8
View File
@@ -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
+4 -2
View File
@@ -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
+106
View File
@@ -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
+2
View File
@@ -1,3 +1,5 @@
# frozen_string_literal: true
require 'test_helper'
class RawTagTest < Minitest::Test
+168
View File
@@ -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
+23 -21
View File
@@ -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
+2
View File
@@ -1,3 +1,5 @@
# frozen_string_literal: true
require 'test_helper'
class StatementsTest < Minitest::Test
+2
View File
@@ -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
+45 -13
View File
@@ -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
+32 -30
View File
@@ -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
+3 -1
View File
@@ -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
View File
@@ -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
+43 -4
View File
@@ -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
+11 -9
View File
@@ -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
View File
@@ -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
+2
View File
@@ -1,3 +1,5 @@
# frozen_string_literal: true
require 'test_helper'
class FileSystemUnitTest < Minitest::Test
+2
View File
@@ -1,3 +1,5 @@
# frozen_string_literal: true
require 'test_helper'
class I18nUnitTest < Minitest::Test
+2
View File
@@ -1,3 +1,5 @@
# frozen_string_literal: true
require 'test_helper'
class LexerUnitTest < Minitest::Test
+2
View File
@@ -1,3 +1,5 @@
# frozen_string_literal: true
require 'test_helper'
class ParserUnitTest < Minitest::Test
+93
View File
@@ -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
+2
View File
@@ -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
+248
View File
@@ -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
+6 -3
View File
@@ -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
+40
View File
@@ -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
+2
View File
@@ -1,3 +1,5 @@
# frozen_string_literal: true
require 'test_helper'
class CaseTagUnitTest < Minitest::Test
+2
View File
@@ -1,3 +1,5 @@
# frozen_string_literal: true
require 'test_helper'
class ForTagUnitTest < Minitest::Test
+3 -1
View File
@@ -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
+2
View File
@@ -1,3 +1,5 @@
# frozen_string_literal: true
require 'test_helper'
class TemplateUnitTest < Minitest::Test
+3 -1
View File
@@ -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
+2
View File
@@ -1,3 +1,5 @@
# frozen_string_literal: true
require 'test_helper'
class VariableUnitTest < Minitest::Test