liquid-4-0-2 4.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/History.md +235 -0
- data/LICENSE +20 -0
- data/README.md +108 -0
- data/lib/liquid.rb +80 -0
- data/lib/liquid/block.rb +77 -0
- data/lib/liquid/block_body.rb +142 -0
- data/lib/liquid/condition.rb +151 -0
- data/lib/liquid/context.rb +226 -0
- data/lib/liquid/document.rb +27 -0
- data/lib/liquid/drop.rb +78 -0
- data/lib/liquid/errors.rb +56 -0
- data/lib/liquid/expression.rb +49 -0
- data/lib/liquid/extensions.rb +74 -0
- data/lib/liquid/file_system.rb +73 -0
- data/lib/liquid/forloop_drop.rb +42 -0
- data/lib/liquid/i18n.rb +39 -0
- data/lib/liquid/interrupts.rb +16 -0
- data/lib/liquid/lexer.rb +55 -0
- data/lib/liquid/locales/en.yml +26 -0
- data/lib/liquid/parse_context.rb +38 -0
- data/lib/liquid/parse_tree_visitor.rb +42 -0
- data/lib/liquid/parser.rb +90 -0
- data/lib/liquid/parser_switching.rb +31 -0
- data/lib/liquid/profiler.rb +158 -0
- data/lib/liquid/profiler/hooks.rb +23 -0
- data/lib/liquid/range_lookup.rb +37 -0
- data/lib/liquid/resource_limits.rb +23 -0
- data/lib/liquid/standardfilters.rb +485 -0
- data/lib/liquid/strainer.rb +66 -0
- data/lib/liquid/tablerowloop_drop.rb +62 -0
- data/lib/liquid/tag.rb +43 -0
- data/lib/liquid/tags/assign.rb +59 -0
- data/lib/liquid/tags/break.rb +18 -0
- data/lib/liquid/tags/capture.rb +38 -0
- data/lib/liquid/tags/case.rb +94 -0
- data/lib/liquid/tags/comment.rb +16 -0
- data/lib/liquid/tags/continue.rb +18 -0
- data/lib/liquid/tags/cycle.rb +65 -0
- data/lib/liquid/tags/decrement.rb +35 -0
- data/lib/liquid/tags/for.rb +203 -0
- data/lib/liquid/tags/if.rb +122 -0
- data/lib/liquid/tags/ifchanged.rb +18 -0
- data/lib/liquid/tags/include.rb +124 -0
- data/lib/liquid/tags/increment.rb +31 -0
- data/lib/liquid/tags/raw.rb +47 -0
- data/lib/liquid/tags/table_row.rb +62 -0
- data/lib/liquid/tags/unless.rb +30 -0
- data/lib/liquid/template.rb +254 -0
- data/lib/liquid/tokenizer.rb +31 -0
- data/lib/liquid/utils.rb +83 -0
- data/lib/liquid/variable.rb +148 -0
- data/lib/liquid/variable_lookup.rb +88 -0
- data/lib/liquid/version.rb +4 -0
- data/test/fixtures/en_locale.yml +9 -0
- data/test/integration/assign_test.rb +48 -0
- data/test/integration/blank_test.rb +106 -0
- data/test/integration/block_test.rb +12 -0
- data/test/integration/capture_test.rb +50 -0
- data/test/integration/context_test.rb +32 -0
- data/test/integration/document_test.rb +19 -0
- data/test/integration/drop_test.rb +273 -0
- data/test/integration/error_handling_test.rb +260 -0
- data/test/integration/filter_test.rb +178 -0
- data/test/integration/hash_ordering_test.rb +23 -0
- data/test/integration/output_test.rb +123 -0
- data/test/integration/parse_tree_visitor_test.rb +247 -0
- data/test/integration/parsing_quirks_test.rb +122 -0
- data/test/integration/render_profiling_test.rb +154 -0
- data/test/integration/security_test.rb +80 -0
- data/test/integration/standard_filter_test.rb +698 -0
- data/test/integration/tags/break_tag_test.rb +15 -0
- data/test/integration/tags/continue_tag_test.rb +15 -0
- data/test/integration/tags/for_tag_test.rb +410 -0
- data/test/integration/tags/if_else_tag_test.rb +188 -0
- data/test/integration/tags/include_tag_test.rb +245 -0
- data/test/integration/tags/increment_tag_test.rb +23 -0
- data/test/integration/tags/raw_tag_test.rb +31 -0
- data/test/integration/tags/standard_tag_test.rb +296 -0
- data/test/integration/tags/statements_test.rb +111 -0
- data/test/integration/tags/table_row_test.rb +64 -0
- data/test/integration/tags/unless_else_tag_test.rb +26 -0
- data/test/integration/template_test.rb +332 -0
- data/test/integration/trim_mode_test.rb +529 -0
- data/test/integration/variable_test.rb +96 -0
- data/test/test_helper.rb +116 -0
- data/test/unit/block_unit_test.rb +58 -0
- data/test/unit/condition_unit_test.rb +166 -0
- data/test/unit/context_unit_test.rb +489 -0
- data/test/unit/file_system_unit_test.rb +35 -0
- data/test/unit/i18n_unit_test.rb +37 -0
- data/test/unit/lexer_unit_test.rb +51 -0
- data/test/unit/parser_unit_test.rb +82 -0
- data/test/unit/regexp_unit_test.rb +44 -0
- data/test/unit/strainer_unit_test.rb +164 -0
- data/test/unit/tag_unit_test.rb +21 -0
- data/test/unit/tags/case_tag_unit_test.rb +10 -0
- data/test/unit/tags/for_tag_unit_test.rb +13 -0
- data/test/unit/tags/if_tag_unit_test.rb +8 -0
- data/test/unit/template_unit_test.rb +78 -0
- data/test/unit/tokenizer_unit_test.rb +55 -0
- data/test/unit/variable_unit_test.rb +162 -0
- metadata +224 -0
@@ -0,0 +1,122 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class ParsingQuirksTest < Minitest::Test
|
4
|
+
include Liquid
|
5
|
+
|
6
|
+
def test_parsing_css
|
7
|
+
text = " div { font-weight: bold; } "
|
8
|
+
assert_equal text, Template.parse(text).render!
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_raise_on_single_close_bracet
|
12
|
+
assert_raises(SyntaxError) do
|
13
|
+
Template.parse("text {{method} oh nos!")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_raise_on_label_and_no_close_bracets
|
18
|
+
assert_raises(SyntaxError) do
|
19
|
+
Template.parse("TEST {{ ")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_raise_on_label_and_no_close_bracets_percent
|
24
|
+
assert_raises(SyntaxError) do
|
25
|
+
Template.parse("TEST {% ")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_error_on_empty_filter
|
30
|
+
assert Template.parse("{{test}}")
|
31
|
+
|
32
|
+
with_error_mode(:lax) do
|
33
|
+
assert Template.parse("{{|test}}")
|
34
|
+
end
|
35
|
+
|
36
|
+
with_error_mode(:strict) do
|
37
|
+
assert_raises(SyntaxError) { Template.parse("{{|test}}") }
|
38
|
+
assert_raises(SyntaxError) { Template.parse("{{test |a|b|}}") }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_meaningless_parens_error
|
43
|
+
with_error_mode(:strict) do
|
44
|
+
assert_raises(SyntaxError) do
|
45
|
+
markup = "a == 'foo' or (b == 'bar' and c == 'baz') or false"
|
46
|
+
Template.parse("{% if #{markup} %} YES {% endif %}")
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_unexpected_characters_syntax_error
|
52
|
+
with_error_mode(:strict) do
|
53
|
+
assert_raises(SyntaxError) do
|
54
|
+
markup = "true && false"
|
55
|
+
Template.parse("{% if #{markup} %} YES {% endif %}")
|
56
|
+
end
|
57
|
+
assert_raises(SyntaxError) do
|
58
|
+
markup = "false || true"
|
59
|
+
Template.parse("{% if #{markup} %} YES {% endif %}")
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_no_error_on_lax_empty_filter
|
65
|
+
assert Template.parse("{{test |a|b|}}", error_mode: :lax)
|
66
|
+
assert Template.parse("{{test}}", error_mode: :lax)
|
67
|
+
assert Template.parse("{{|test|}}", error_mode: :lax)
|
68
|
+
end
|
69
|
+
|
70
|
+
def test_meaningless_parens_lax
|
71
|
+
with_error_mode(:lax) do
|
72
|
+
assigns = { 'b' => 'bar', 'c' => 'baz' }
|
73
|
+
markup = "a == 'foo' or (b == 'bar' and c == 'baz') or false"
|
74
|
+
assert_template_result(' YES ', "{% if #{markup} %} YES {% endif %}", assigns)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def test_unexpected_characters_silently_eat_logic_lax
|
79
|
+
with_error_mode(:lax) do
|
80
|
+
markup = "true && false"
|
81
|
+
assert_template_result(' YES ', "{% if #{markup} %} YES {% endif %}")
|
82
|
+
markup = "false || true"
|
83
|
+
assert_template_result('', "{% if #{markup} %} YES {% endif %}")
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def test_raise_on_invalid_tag_delimiter
|
88
|
+
assert_raises(Liquid::SyntaxError) do
|
89
|
+
Template.new.parse('{% end %}')
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def test_unanchored_filter_arguments
|
94
|
+
with_error_mode(:lax) do
|
95
|
+
assert_template_result('hi', "{{ 'hi there' | split$$$:' ' | first }}")
|
96
|
+
|
97
|
+
assert_template_result('x', "{{ 'X' | downcase) }}")
|
98
|
+
|
99
|
+
# After the messed up quotes a filter without parameters (reverse) should work
|
100
|
+
# but one with parameters (remove) shouldn't be detected.
|
101
|
+
assert_template_result('here', "{{ 'hi there' | split:\"t\"\" | reverse | first}}")
|
102
|
+
assert_template_result('hi ', "{{ 'hi there' | split:\"t\"\" | remove:\"i\" | first}}")
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def test_invalid_variables_work
|
107
|
+
with_error_mode(:lax) do
|
108
|
+
assert_template_result('bar', "{% assign 123foo = 'bar' %}{{ 123foo }}")
|
109
|
+
assert_template_result('123', "{% assign 123 = 'bar' %}{{ 123 }}")
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def test_extra_dots_in_ranges
|
114
|
+
with_error_mode(:lax) do
|
115
|
+
assert_template_result('12345', "{% for i in (1...5) %}{{ i }}{% endfor %}")
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def test_contains_in_id
|
120
|
+
assert_template_result(' YES ', '{% if containsallshipments == true %} YES {% endif %}', 'containsallshipments' => true)
|
121
|
+
end
|
122
|
+
end # ParsingQuirksTest
|
@@ -0,0 +1,154 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class RenderProfilingTest < Minitest::Test
|
4
|
+
include Liquid
|
5
|
+
|
6
|
+
class ProfilingFileSystem
|
7
|
+
def read_template_file(template_path)
|
8
|
+
"Rendering template {% assign template_name = '#{template_path}'%}\n{{ template_name }}"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def setup
|
13
|
+
Liquid::Template.file_system = ProfilingFileSystem.new
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_template_allows_flagging_profiling
|
17
|
+
t = Template.parse("{{ 'a string' | upcase }}")
|
18
|
+
t.render!
|
19
|
+
|
20
|
+
assert_nil t.profiler
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_parse_makes_available_simple_profiling
|
24
|
+
t = Template.parse("{{ 'a string' | upcase }}", profile: true)
|
25
|
+
t.render!
|
26
|
+
|
27
|
+
assert_equal 1, t.profiler.length
|
28
|
+
|
29
|
+
node = t.profiler[0]
|
30
|
+
assert_equal " 'a string' | upcase ", node.code
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_render_ignores_raw_strings_when_profiling
|
34
|
+
t = Template.parse("This is raw string\nstuff\nNewline", profile: true)
|
35
|
+
t.render!
|
36
|
+
|
37
|
+
assert_equal 0, t.profiler.length
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_profiling_includes_line_numbers_of_liquid_nodes
|
41
|
+
t = Template.parse("{{ 'a string' | upcase }}\n{% increment test %}", profile: true)
|
42
|
+
t.render!
|
43
|
+
assert_equal 2, t.profiler.length
|
44
|
+
|
45
|
+
# {{ 'a string' | upcase }}
|
46
|
+
assert_equal 1, t.profiler[0].line_number
|
47
|
+
# {{ increment test }}
|
48
|
+
assert_equal 2, t.profiler[1].line_number
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_profiling_includes_line_numbers_of_included_partials
|
52
|
+
t = Template.parse("{% include 'a_template' %}", profile: true)
|
53
|
+
t.render!
|
54
|
+
|
55
|
+
included_children = t.profiler[0].children
|
56
|
+
|
57
|
+
# {% assign template_name = 'a_template' %}
|
58
|
+
assert_equal 1, included_children[0].line_number
|
59
|
+
# {{ template_name }}
|
60
|
+
assert_equal 2, included_children[1].line_number
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_profiling_times_the_rendering_of_tokens
|
64
|
+
t = Template.parse("{% include 'a_template' %}", profile: true)
|
65
|
+
t.render!
|
66
|
+
|
67
|
+
node = t.profiler[0]
|
68
|
+
refute_nil node.render_time
|
69
|
+
end
|
70
|
+
|
71
|
+
def test_profiling_times_the_entire_render
|
72
|
+
t = Template.parse("{% include 'a_template' %}", profile: true)
|
73
|
+
t.render!
|
74
|
+
|
75
|
+
assert t.profiler.total_render_time >= 0, "Total render time was not calculated"
|
76
|
+
end
|
77
|
+
|
78
|
+
def test_profiling_uses_include_to_mark_children
|
79
|
+
t = Template.parse("{{ 'a string' | upcase }}\n{% include 'a_template' %}", profile: true)
|
80
|
+
t.render!
|
81
|
+
|
82
|
+
include_node = t.profiler[1]
|
83
|
+
assert_equal 2, include_node.children.length
|
84
|
+
end
|
85
|
+
|
86
|
+
def test_profiling_marks_children_with_the_name_of_included_partial
|
87
|
+
t = Template.parse("{{ 'a string' | upcase }}\n{% include 'a_template' %}", profile: true)
|
88
|
+
t.render!
|
89
|
+
|
90
|
+
include_node = t.profiler[1]
|
91
|
+
include_node.children.each do |child|
|
92
|
+
assert_equal "a_template", child.partial
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def test_profiling_supports_multiple_templates
|
97
|
+
t = Template.parse("{{ 'a string' | upcase }}\n{% include 'a_template' %}\n{% include 'b_template' %}", profile: true)
|
98
|
+
t.render!
|
99
|
+
|
100
|
+
a_template = t.profiler[1]
|
101
|
+
a_template.children.each do |child|
|
102
|
+
assert_equal "a_template", child.partial
|
103
|
+
end
|
104
|
+
|
105
|
+
b_template = t.profiler[2]
|
106
|
+
b_template.children.each do |child|
|
107
|
+
assert_equal "b_template", child.partial
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def test_profiling_supports_rendering_the_same_partial_multiple_times
|
112
|
+
t = Template.parse("{{ 'a string' | upcase }}\n{% include 'a_template' %}\n{% include 'a_template' %}", profile: true)
|
113
|
+
t.render!
|
114
|
+
|
115
|
+
a_template1 = t.profiler[1]
|
116
|
+
a_template1.children.each do |child|
|
117
|
+
assert_equal "a_template", child.partial
|
118
|
+
end
|
119
|
+
|
120
|
+
a_template2 = t.profiler[2]
|
121
|
+
a_template2.children.each do |child|
|
122
|
+
assert_equal "a_template", child.partial
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def test_can_iterate_over_each_profiling_entry
|
127
|
+
t = Template.parse("{{ 'a string' | upcase }}\n{% increment test %}", profile: true)
|
128
|
+
t.render!
|
129
|
+
|
130
|
+
timing_count = 0
|
131
|
+
t.profiler.each do |timing|
|
132
|
+
timing_count += 1
|
133
|
+
end
|
134
|
+
|
135
|
+
assert_equal 2, timing_count
|
136
|
+
end
|
137
|
+
|
138
|
+
def test_profiling_marks_children_of_if_blocks
|
139
|
+
t = Template.parse("{% if true %} {% increment test %} {{ test }} {% endif %}", profile: true)
|
140
|
+
t.render!
|
141
|
+
|
142
|
+
assert_equal 1, t.profiler.length
|
143
|
+
assert_equal 2, t.profiler[0].children.length
|
144
|
+
end
|
145
|
+
|
146
|
+
def test_profiling_marks_children_of_for_blocks
|
147
|
+
t = Template.parse("{% for item in collection %} {{ item }} {% endfor %}", profile: true)
|
148
|
+
t.render!({ "collection" => ["one", "two"] })
|
149
|
+
|
150
|
+
assert_equal 1, t.profiler.length
|
151
|
+
# Will profile each invocation of the for block
|
152
|
+
assert_equal 2, t.profiler[0].children.length
|
153
|
+
end
|
154
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module SecurityFilter
|
4
|
+
def add_one(input)
|
5
|
+
"#{input} + 1"
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
class SecurityTest < Minitest::Test
|
10
|
+
include Liquid
|
11
|
+
|
12
|
+
def setup
|
13
|
+
@assigns = {}
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_no_instance_eval
|
17
|
+
text = %( {{ '1+1' | instance_eval }} )
|
18
|
+
expected = %( 1+1 )
|
19
|
+
|
20
|
+
assert_equal expected, Template.parse(text).render!(@assigns)
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_no_existing_instance_eval
|
24
|
+
text = %( {{ '1+1' | __instance_eval__ }} )
|
25
|
+
expected = %( 1+1 )
|
26
|
+
|
27
|
+
assert_equal expected, Template.parse(text).render!(@assigns)
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_no_instance_eval_after_mixing_in_new_filter
|
31
|
+
text = %( {{ '1+1' | instance_eval }} )
|
32
|
+
expected = %( 1+1 )
|
33
|
+
|
34
|
+
assert_equal expected, Template.parse(text).render!(@assigns)
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_no_instance_eval_later_in_chain
|
38
|
+
text = %( {{ '1+1' | add_one | instance_eval }} )
|
39
|
+
expected = %( 1+1 + 1 )
|
40
|
+
|
41
|
+
assert_equal expected, Template.parse(text).render!(@assigns, filters: SecurityFilter)
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_does_not_add_filters_to_symbol_table
|
45
|
+
current_symbols = Symbol.all_symbols
|
46
|
+
|
47
|
+
test = %( {{ "some_string" | a_bad_filter }} )
|
48
|
+
|
49
|
+
template = Template.parse(test)
|
50
|
+
assert_equal [], (Symbol.all_symbols - current_symbols)
|
51
|
+
|
52
|
+
template.render!
|
53
|
+
assert_equal [], (Symbol.all_symbols - current_symbols)
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_does_not_add_drop_methods_to_symbol_table
|
57
|
+
current_symbols = Symbol.all_symbols
|
58
|
+
|
59
|
+
assigns = { 'drop' => Drop.new }
|
60
|
+
assert_equal "", Template.parse("{{ drop.custom_method_1 }}", assigns).render!
|
61
|
+
assert_equal "", Template.parse("{{ drop.custom_method_2 }}", assigns).render!
|
62
|
+
assert_equal "", Template.parse("{{ drop.custom_method_3 }}", assigns).render!
|
63
|
+
|
64
|
+
assert_equal [], (Symbol.all_symbols - current_symbols)
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_max_depth_nested_blocks_does_not_raise_exception
|
68
|
+
depth = Liquid::Block::MAX_DEPTH
|
69
|
+
code = "{% if true %}" * depth + "rendered" + "{% endif %}" * depth
|
70
|
+
assert_equal "rendered", Template.parse(code).render!
|
71
|
+
end
|
72
|
+
|
73
|
+
def test_more_than_max_depth_nested_blocks_raises_exception
|
74
|
+
depth = Liquid::Block::MAX_DEPTH + 1
|
75
|
+
code = "{% if true %}" * depth + "rendered" + "{% endif %}" * depth
|
76
|
+
assert_raises(Liquid::StackLevelError) do
|
77
|
+
Template.parse(code).render!
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end # SecurityTest
|
@@ -0,0 +1,698 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'test_helper'
|
4
|
+
|
5
|
+
class Filters
|
6
|
+
include Liquid::StandardFilters
|
7
|
+
end
|
8
|
+
|
9
|
+
class TestThing
|
10
|
+
attr_reader :foo
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
@foo = 0
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_s
|
17
|
+
"woot: #{@foo}"
|
18
|
+
end
|
19
|
+
|
20
|
+
def [](whatever)
|
21
|
+
to_s
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_liquid
|
25
|
+
@foo += 1
|
26
|
+
self
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class TestDrop < Liquid::Drop
|
31
|
+
def test
|
32
|
+
"testfoo"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class TestEnumerable < Liquid::Drop
|
37
|
+
include Enumerable
|
38
|
+
|
39
|
+
def each(&block)
|
40
|
+
[ { "foo" => 1, "bar" => 2 }, { "foo" => 2, "bar" => 1 }, { "foo" => 3, "bar" => 3 } ].each(&block)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
class NumberLikeThing < Liquid::Drop
|
45
|
+
def initialize(amount)
|
46
|
+
@amount = amount
|
47
|
+
end
|
48
|
+
|
49
|
+
def to_number
|
50
|
+
@amount
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
class StandardFiltersTest < Minitest::Test
|
55
|
+
include Liquid
|
56
|
+
|
57
|
+
def setup
|
58
|
+
@filters = Filters.new
|
59
|
+
end
|
60
|
+
|
61
|
+
def test_size
|
62
|
+
assert_equal 3, @filters.size([1, 2, 3])
|
63
|
+
assert_equal 0, @filters.size([])
|
64
|
+
assert_equal 0, @filters.size(nil)
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_downcase
|
68
|
+
assert_equal 'testing', @filters.downcase("Testing")
|
69
|
+
assert_equal '', @filters.downcase(nil)
|
70
|
+
end
|
71
|
+
|
72
|
+
def test_upcase
|
73
|
+
assert_equal 'TESTING', @filters.upcase("Testing")
|
74
|
+
assert_equal '', @filters.upcase(nil)
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_slice
|
78
|
+
assert_equal 'oob', @filters.slice('foobar', 1, 3)
|
79
|
+
assert_equal 'oobar', @filters.slice('foobar', 1, 1000)
|
80
|
+
assert_equal '', @filters.slice('foobar', 1, 0)
|
81
|
+
assert_equal 'o', @filters.slice('foobar', 1, 1)
|
82
|
+
assert_equal 'bar', @filters.slice('foobar', 3, 3)
|
83
|
+
assert_equal 'ar', @filters.slice('foobar', -2, 2)
|
84
|
+
assert_equal 'ar', @filters.slice('foobar', -2, 1000)
|
85
|
+
assert_equal 'r', @filters.slice('foobar', -1)
|
86
|
+
assert_equal '', @filters.slice(nil, 0)
|
87
|
+
assert_equal '', @filters.slice('foobar', 100, 10)
|
88
|
+
assert_equal '', @filters.slice('foobar', -100, 10)
|
89
|
+
assert_equal 'oob', @filters.slice('foobar', '1', '3')
|
90
|
+
assert_raises(Liquid::ArgumentError) do
|
91
|
+
@filters.slice('foobar', nil)
|
92
|
+
end
|
93
|
+
assert_raises(Liquid::ArgumentError) do
|
94
|
+
@filters.slice('foobar', 0, "")
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def test_slice_on_arrays
|
99
|
+
input = 'foobar'.split(//)
|
100
|
+
assert_equal %w(o o b), @filters.slice(input, 1, 3)
|
101
|
+
assert_equal %w(o o b a r), @filters.slice(input, 1, 1000)
|
102
|
+
assert_equal %w(), @filters.slice(input, 1, 0)
|
103
|
+
assert_equal %w(o), @filters.slice(input, 1, 1)
|
104
|
+
assert_equal %w(b a r), @filters.slice(input, 3, 3)
|
105
|
+
assert_equal %w(a r), @filters.slice(input, -2, 2)
|
106
|
+
assert_equal %w(a r), @filters.slice(input, -2, 1000)
|
107
|
+
assert_equal %w(r), @filters.slice(input, -1)
|
108
|
+
assert_equal %w(), @filters.slice(input, 100, 10)
|
109
|
+
assert_equal %w(), @filters.slice(input, -100, 10)
|
110
|
+
end
|
111
|
+
|
112
|
+
def test_truncate
|
113
|
+
assert_equal '1234...', @filters.truncate('1234567890', 7)
|
114
|
+
assert_equal '1234567890', @filters.truncate('1234567890', 20)
|
115
|
+
assert_equal '...', @filters.truncate('1234567890', 0)
|
116
|
+
assert_equal '1234567890', @filters.truncate('1234567890')
|
117
|
+
assert_equal "测试...", @filters.truncate("测试测试测试测试", 5)
|
118
|
+
assert_equal '12341', @filters.truncate("1234567890", 5, 1)
|
119
|
+
end
|
120
|
+
|
121
|
+
def test_split
|
122
|
+
assert_equal ['12', '34'], @filters.split('12~34', '~')
|
123
|
+
assert_equal ['A? ', ' ,Z'], @filters.split('A? ~ ~ ~ ,Z', '~ ~ ~')
|
124
|
+
assert_equal ['A?Z'], @filters.split('A?Z', '~')
|
125
|
+
assert_equal [], @filters.split(nil, ' ')
|
126
|
+
assert_equal ['A', 'Z'], @filters.split('A1Z', 1)
|
127
|
+
end
|
128
|
+
|
129
|
+
def test_escape
|
130
|
+
assert_equal '<strong>', @filters.escape('<strong>')
|
131
|
+
assert_equal '1', @filters.escape(1)
|
132
|
+
assert_equal '2001-02-03', @filters.escape(Date.new(2001, 2, 3))
|
133
|
+
assert_nil @filters.escape(nil)
|
134
|
+
end
|
135
|
+
|
136
|
+
def test_h
|
137
|
+
assert_equal '<strong>', @filters.h('<strong>')
|
138
|
+
assert_equal '1', @filters.h(1)
|
139
|
+
assert_equal '2001-02-03', @filters.h(Date.new(2001, 2, 3))
|
140
|
+
assert_nil @filters.h(nil)
|
141
|
+
end
|
142
|
+
|
143
|
+
def test_escape_once
|
144
|
+
assert_equal '<strong>Hulk</strong>', @filters.escape_once('<strong>Hulk</strong>')
|
145
|
+
end
|
146
|
+
|
147
|
+
def test_url_encode
|
148
|
+
assert_equal 'foo%2B1%40example.com', @filters.url_encode('foo+1@example.com')
|
149
|
+
assert_equal '1', @filters.url_encode(1)
|
150
|
+
assert_equal '2001-02-03', @filters.url_encode(Date.new(2001, 2, 3))
|
151
|
+
assert_nil @filters.url_encode(nil)
|
152
|
+
end
|
153
|
+
|
154
|
+
def test_url_decode
|
155
|
+
assert_equal 'foo bar', @filters.url_decode('foo+bar')
|
156
|
+
assert_equal 'foo bar', @filters.url_decode('foo%20bar')
|
157
|
+
assert_equal 'foo+1@example.com', @filters.url_decode('foo%2B1%40example.com')
|
158
|
+
assert_equal '1', @filters.url_decode(1)
|
159
|
+
assert_equal '2001-02-03', @filters.url_decode(Date.new(2001, 2, 3))
|
160
|
+
assert_nil @filters.url_decode(nil)
|
161
|
+
end
|
162
|
+
|
163
|
+
def test_truncatewords
|
164
|
+
assert_equal 'one two three', @filters.truncatewords('one two three', 4)
|
165
|
+
assert_equal 'one two...', @filters.truncatewords('one two three', 2)
|
166
|
+
assert_equal 'one two three', @filters.truncatewords('one two three')
|
167
|
+
assert_equal 'Two small (13” x 5.5” x 10” high) baskets fit inside one large basket (13”...', @filters.truncatewords('Two small (13” x 5.5” x 10” high) baskets fit inside one large basket (13” x 16” x 10.5” high) with cover.', 15)
|
168
|
+
assert_equal "测试测试测试测试", @filters.truncatewords('测试测试测试测试', 5)
|
169
|
+
assert_equal 'one two1', @filters.truncatewords("one two three", 2, 1)
|
170
|
+
end
|
171
|
+
|
172
|
+
def test_strip_html
|
173
|
+
assert_equal 'test', @filters.strip_html("<div>test</div>")
|
174
|
+
assert_equal 'test', @filters.strip_html("<div id='test'>test</div>")
|
175
|
+
assert_equal '', @filters.strip_html("<script type='text/javascript'>document.write('some stuff');</script>")
|
176
|
+
assert_equal '', @filters.strip_html("<style type='text/css'>foo bar</style>")
|
177
|
+
assert_equal 'test', @filters.strip_html("<div\nclass='multiline'>test</div>")
|
178
|
+
assert_equal 'test', @filters.strip_html("<!-- foo bar \n test -->test")
|
179
|
+
assert_equal '', @filters.strip_html(nil)
|
180
|
+
end
|
181
|
+
|
182
|
+
def test_join
|
183
|
+
assert_equal '1 2 3 4', @filters.join([1, 2, 3, 4])
|
184
|
+
assert_equal '1 - 2 - 3 - 4', @filters.join([1, 2, 3, 4], ' - ')
|
185
|
+
assert_equal '1121314', @filters.join([1, 2, 3, 4], 1)
|
186
|
+
end
|
187
|
+
|
188
|
+
def test_sort
|
189
|
+
assert_equal [1, 2, 3, 4], @filters.sort([4, 3, 2, 1])
|
190
|
+
assert_equal [{ "a" => 1 }, { "a" => 2 }, { "a" => 3 }, { "a" => 4 }], @filters.sort([{ "a" => 4 }, { "a" => 3 }, { "a" => 1 }, { "a" => 2 }], "a")
|
191
|
+
end
|
192
|
+
|
193
|
+
def test_sort_with_nils
|
194
|
+
assert_equal [1, 2, 3, 4, nil], @filters.sort([nil, 4, 3, 2, 1])
|
195
|
+
assert_equal [{ "a" => 1 }, { "a" => 2 }, { "a" => 3 }, { "a" => 4 }, {}], @filters.sort([{ "a" => 4 }, { "a" => 3 }, {}, { "a" => 1 }, { "a" => 2 }], "a")
|
196
|
+
end
|
197
|
+
|
198
|
+
def test_sort_when_property_is_sometimes_missing_puts_nils_last
|
199
|
+
input = [
|
200
|
+
{ "price" => 4, "handle" => "alpha" },
|
201
|
+
{ "handle" => "beta" },
|
202
|
+
{ "price" => 1, "handle" => "gamma" },
|
203
|
+
{ "handle" => "delta" },
|
204
|
+
{ "price" => 2, "handle" => "epsilon" }
|
205
|
+
]
|
206
|
+
expectation = [
|
207
|
+
{ "price" => 1, "handle" => "gamma" },
|
208
|
+
{ "price" => 2, "handle" => "epsilon" },
|
209
|
+
{ "price" => 4, "handle" => "alpha" },
|
210
|
+
{ "handle" => "delta" },
|
211
|
+
{ "handle" => "beta" }
|
212
|
+
]
|
213
|
+
assert_equal expectation, @filters.sort(input, "price")
|
214
|
+
end
|
215
|
+
|
216
|
+
def test_sort_natural
|
217
|
+
assert_equal ["a", "B", "c", "D"], @filters.sort_natural(["c", "D", "a", "B"])
|
218
|
+
assert_equal [{ "a" => "a" }, { "a" => "B" }, { "a" => "c" }, { "a" => "D" }], @filters.sort_natural([{ "a" => "D" }, { "a" => "c" }, { "a" => "a" }, { "a" => "B" }], "a")
|
219
|
+
end
|
220
|
+
|
221
|
+
def test_sort_natural_with_nils
|
222
|
+
assert_equal ["a", "B", "c", "D", nil], @filters.sort_natural([nil, "c", "D", "a", "B"])
|
223
|
+
assert_equal [{ "a" => "a" }, { "a" => "B" }, { "a" => "c" }, { "a" => "D" }, {}], @filters.sort_natural([{ "a" => "D" }, { "a" => "c" }, {}, { "a" => "a" }, { "a" => "B" }], "a")
|
224
|
+
end
|
225
|
+
|
226
|
+
def test_sort_natural_when_property_is_sometimes_missing_puts_nils_last
|
227
|
+
input = [
|
228
|
+
{ "price" => "4", "handle" => "alpha" },
|
229
|
+
{ "handle" => "beta" },
|
230
|
+
{ "price" => "1", "handle" => "gamma" },
|
231
|
+
{ "handle" => "delta" },
|
232
|
+
{ "price" => 2, "handle" => "epsilon" }
|
233
|
+
]
|
234
|
+
expectation = [
|
235
|
+
{ "price" => "1", "handle" => "gamma" },
|
236
|
+
{ "price" => 2, "handle" => "epsilon" },
|
237
|
+
{ "price" => "4", "handle" => "alpha" },
|
238
|
+
{ "handle" => "delta" },
|
239
|
+
{ "handle" => "beta" }
|
240
|
+
]
|
241
|
+
assert_equal expectation, @filters.sort_natural(input, "price")
|
242
|
+
end
|
243
|
+
|
244
|
+
def test_sort_natural_case_check
|
245
|
+
input = [
|
246
|
+
{ "key" => "X" },
|
247
|
+
{ "key" => "Y" },
|
248
|
+
{ "key" => "Z" },
|
249
|
+
{ "fake" => "t" },
|
250
|
+
{ "key" => "a" },
|
251
|
+
{ "key" => "b" },
|
252
|
+
{ "key" => "c" }
|
253
|
+
]
|
254
|
+
expectation = [
|
255
|
+
{ "key" => "a" },
|
256
|
+
{ "key" => "b" },
|
257
|
+
{ "key" => "c" },
|
258
|
+
{ "key" => "X" },
|
259
|
+
{ "key" => "Y" },
|
260
|
+
{ "key" => "Z" },
|
261
|
+
{ "fake" => "t" }
|
262
|
+
]
|
263
|
+
assert_equal expectation, @filters.sort_natural(input, "key")
|
264
|
+
assert_equal ["a", "b", "c", "X", "Y", "Z"], @filters.sort_natural(["X", "Y", "Z", "a", "b", "c"])
|
265
|
+
end
|
266
|
+
|
267
|
+
def test_sort_empty_array
|
268
|
+
assert_equal [], @filters.sort([], "a")
|
269
|
+
end
|
270
|
+
|
271
|
+
def test_sort_natural_empty_array
|
272
|
+
assert_equal [], @filters.sort_natural([], "a")
|
273
|
+
end
|
274
|
+
|
275
|
+
def test_legacy_sort_hash
|
276
|
+
assert_equal [{ a: 1, b: 2 }], @filters.sort({ a: 1, b: 2 })
|
277
|
+
end
|
278
|
+
|
279
|
+
def test_numerical_vs_lexicographical_sort
|
280
|
+
assert_equal [2, 10], @filters.sort([10, 2])
|
281
|
+
assert_equal [{ "a" => 2 }, { "a" => 10 }], @filters.sort([{ "a" => 10 }, { "a" => 2 }], "a")
|
282
|
+
assert_equal ["10", "2"], @filters.sort(["10", "2"])
|
283
|
+
assert_equal [{ "a" => "10" }, { "a" => "2" }], @filters.sort([{ "a" => "10" }, { "a" => "2" }], "a")
|
284
|
+
end
|
285
|
+
|
286
|
+
def test_uniq
|
287
|
+
assert_equal ["foo"], @filters.uniq("foo")
|
288
|
+
assert_equal [1, 3, 2, 4], @filters.uniq([1, 1, 3, 2, 3, 1, 4, 3, 2, 1])
|
289
|
+
assert_equal [{ "a" => 1 }, { "a" => 3 }, { "a" => 2 }], @filters.uniq([{ "a" => 1 }, { "a" => 3 }, { "a" => 1 }, { "a" => 2 }], "a")
|
290
|
+
testdrop = TestDrop.new
|
291
|
+
assert_equal [testdrop], @filters.uniq([testdrop, TestDrop.new], 'test')
|
292
|
+
end
|
293
|
+
|
294
|
+
def test_uniq_empty_array
|
295
|
+
assert_equal [], @filters.uniq([], "a")
|
296
|
+
end
|
297
|
+
|
298
|
+
def test_compact_empty_array
|
299
|
+
assert_equal [], @filters.compact([], "a")
|
300
|
+
end
|
301
|
+
|
302
|
+
def test_reverse
|
303
|
+
assert_equal [4, 3, 2, 1], @filters.reverse([1, 2, 3, 4])
|
304
|
+
end
|
305
|
+
|
306
|
+
def test_legacy_reverse_hash
|
307
|
+
assert_equal [{ a: 1, b: 2 }], @filters.reverse(a: 1, b: 2)
|
308
|
+
end
|
309
|
+
|
310
|
+
def test_map
|
311
|
+
assert_equal [1, 2, 3, 4], @filters.map([{ "a" => 1 }, { "a" => 2 }, { "a" => 3 }, { "a" => 4 }], 'a')
|
312
|
+
assert_template_result 'abc', "{{ ary | map:'foo' | map:'bar' }}",
|
313
|
+
'ary' => [{ 'foo' => { 'bar' => 'a' } }, { 'foo' => { 'bar' => 'b' } }, { 'foo' => { 'bar' => 'c' } }]
|
314
|
+
end
|
315
|
+
|
316
|
+
def test_map_doesnt_call_arbitrary_stuff
|
317
|
+
assert_template_result "", '{{ "foo" | map: "__id__" }}'
|
318
|
+
assert_template_result "", '{{ "foo" | map: "inspect" }}'
|
319
|
+
end
|
320
|
+
|
321
|
+
def test_map_calls_to_liquid
|
322
|
+
t = TestThing.new
|
323
|
+
assert_template_result "woot: 1", '{{ foo | map: "whatever" }}', "foo" => [t]
|
324
|
+
end
|
325
|
+
|
326
|
+
def test_map_on_hashes
|
327
|
+
assert_template_result "4217", '{{ thing | map: "foo" | map: "bar" }}',
|
328
|
+
"thing" => { "foo" => [ { "bar" => 42 }, { "bar" => 17 } ] }
|
329
|
+
end
|
330
|
+
|
331
|
+
def test_legacy_map_on_hashes_with_dynamic_key
|
332
|
+
template = "{% assign key = 'foo' %}{{ thing | map: key | map: 'bar' }}"
|
333
|
+
hash = { "foo" => { "bar" => 42 } }
|
334
|
+
assert_template_result "42", template, "thing" => hash
|
335
|
+
end
|
336
|
+
|
337
|
+
def test_sort_calls_to_liquid
|
338
|
+
t = TestThing.new
|
339
|
+
Liquid::Template.parse('{{ foo | sort: "whatever" }}').render("foo" => [t])
|
340
|
+
assert t.foo > 0
|
341
|
+
end
|
342
|
+
|
343
|
+
def test_map_over_proc
|
344
|
+
drop = TestDrop.new
|
345
|
+
p = proc{ drop }
|
346
|
+
templ = '{{ procs | map: "test" }}'
|
347
|
+
assert_template_result "testfoo", templ, "procs" => [p]
|
348
|
+
end
|
349
|
+
|
350
|
+
def test_map_over_drops_returning_procs
|
351
|
+
drops = [
|
352
|
+
{
|
353
|
+
"proc" => ->{ "foo" },
|
354
|
+
},
|
355
|
+
{
|
356
|
+
"proc" => ->{ "bar" },
|
357
|
+
},
|
358
|
+
]
|
359
|
+
templ = '{{ drops | map: "proc" }}'
|
360
|
+
assert_template_result "foobar", templ, "drops" => drops
|
361
|
+
end
|
362
|
+
|
363
|
+
def test_map_works_on_enumerables
|
364
|
+
assert_template_result "123", '{{ foo | map: "foo" }}', "foo" => TestEnumerable.new
|
365
|
+
end
|
366
|
+
|
367
|
+
def test_sort_works_on_enumerables
|
368
|
+
assert_template_result "213", '{{ foo | sort: "bar" | map: "foo" }}', "foo" => TestEnumerable.new
|
369
|
+
end
|
370
|
+
|
371
|
+
def test_first_and_last_call_to_liquid
|
372
|
+
assert_template_result 'foobar', '{{ foo | first }}', 'foo' => [ThingWithToLiquid.new]
|
373
|
+
assert_template_result 'foobar', '{{ foo | last }}', 'foo' => [ThingWithToLiquid.new]
|
374
|
+
end
|
375
|
+
|
376
|
+
def test_truncate_calls_to_liquid
|
377
|
+
assert_template_result "wo...", '{{ foo | truncate: 5 }}', "foo" => TestThing.new
|
378
|
+
end
|
379
|
+
|
380
|
+
def test_date
|
381
|
+
assert_equal 'May', @filters.date(Time.parse("2006-05-05 10:00:00"), "%B")
|
382
|
+
assert_equal 'June', @filters.date(Time.parse("2006-06-05 10:00:00"), "%B")
|
383
|
+
assert_equal 'July', @filters.date(Time.parse("2006-07-05 10:00:00"), "%B")
|
384
|
+
|
385
|
+
assert_equal 'May', @filters.date("2006-05-05 10:00:00", "%B")
|
386
|
+
assert_equal 'June', @filters.date("2006-06-05 10:00:00", "%B")
|
387
|
+
assert_equal 'July', @filters.date("2006-07-05 10:00:00", "%B")
|
388
|
+
|
389
|
+
assert_equal '2006-07-05 10:00:00', @filters.date("2006-07-05 10:00:00", "")
|
390
|
+
assert_equal '2006-07-05 10:00:00', @filters.date("2006-07-05 10:00:00", "")
|
391
|
+
assert_equal '2006-07-05 10:00:00', @filters.date("2006-07-05 10:00:00", "")
|
392
|
+
assert_equal '2006-07-05 10:00:00', @filters.date("2006-07-05 10:00:00", nil)
|
393
|
+
|
394
|
+
assert_equal '07/05/2006', @filters.date("2006-07-05 10:00:00", "%m/%d/%Y")
|
395
|
+
|
396
|
+
assert_equal "07/16/2004", @filters.date("Fri Jul 16 01:00:00 2004", "%m/%d/%Y")
|
397
|
+
assert_equal "#{Date.today.year}", @filters.date('now', '%Y')
|
398
|
+
assert_equal "#{Date.today.year}", @filters.date('today', '%Y')
|
399
|
+
assert_equal "#{Date.today.year}", @filters.date('Today', '%Y')
|
400
|
+
|
401
|
+
assert_nil @filters.date(nil, "%B")
|
402
|
+
|
403
|
+
assert_equal '', @filters.date('', "%B")
|
404
|
+
|
405
|
+
with_timezone("UTC") do
|
406
|
+
assert_equal "07/05/2006", @filters.date(1152098955, "%m/%d/%Y")
|
407
|
+
assert_equal "07/05/2006", @filters.date("1152098955", "%m/%d/%Y")
|
408
|
+
end
|
409
|
+
end
|
410
|
+
|
411
|
+
def test_first_last
|
412
|
+
assert_equal 1, @filters.first([1, 2, 3])
|
413
|
+
assert_equal 3, @filters.last([1, 2, 3])
|
414
|
+
assert_nil @filters.first([])
|
415
|
+
assert_nil @filters.last([])
|
416
|
+
end
|
417
|
+
|
418
|
+
def test_replace
|
419
|
+
assert_equal '2 2 2 2', @filters.replace('1 1 1 1', '1', 2)
|
420
|
+
assert_equal '2 2 2 2', @filters.replace('1 1 1 1', 1, 2)
|
421
|
+
assert_equal '2 1 1 1', @filters.replace_first('1 1 1 1', '1', 2)
|
422
|
+
assert_equal '2 1 1 1', @filters.replace_first('1 1 1 1', 1, 2)
|
423
|
+
assert_template_result '2 1 1 1', "{{ '1 1 1 1' | replace_first: '1', 2 }}"
|
424
|
+
end
|
425
|
+
|
426
|
+
def test_remove
|
427
|
+
assert_equal ' ', @filters.remove("a a a a", 'a')
|
428
|
+
assert_equal ' ', @filters.remove("1 1 1 1", 1)
|
429
|
+
assert_equal 'a a a', @filters.remove_first("a a a a", 'a ')
|
430
|
+
assert_equal ' 1 1 1', @filters.remove_first("1 1 1 1", 1)
|
431
|
+
assert_template_result 'a a a', "{{ 'a a a a' | remove_first: 'a ' }}"
|
432
|
+
end
|
433
|
+
|
434
|
+
def test_pipes_in_string_arguments
|
435
|
+
assert_template_result 'foobar', "{{ 'foo|bar' | remove: '|' }}"
|
436
|
+
end
|
437
|
+
|
438
|
+
def test_strip
|
439
|
+
assert_template_result 'ab c', "{{ source | strip }}", 'source' => " ab c "
|
440
|
+
assert_template_result 'ab c', "{{ source | strip }}", 'source' => " \tab c \n \t"
|
441
|
+
end
|
442
|
+
|
443
|
+
def test_lstrip
|
444
|
+
assert_template_result 'ab c ', "{{ source | lstrip }}", 'source' => " ab c "
|
445
|
+
assert_template_result "ab c \n \t", "{{ source | lstrip }}", 'source' => " \tab c \n \t"
|
446
|
+
end
|
447
|
+
|
448
|
+
def test_rstrip
|
449
|
+
assert_template_result " ab c", "{{ source | rstrip }}", 'source' => " ab c "
|
450
|
+
assert_template_result " \tab c", "{{ source | rstrip }}", 'source' => " \tab c \n \t"
|
451
|
+
end
|
452
|
+
|
453
|
+
def test_strip_newlines
|
454
|
+
assert_template_result 'abc', "{{ source | strip_newlines }}", 'source' => "a\nb\nc"
|
455
|
+
assert_template_result 'abc', "{{ source | strip_newlines }}", 'source' => "a\r\nb\nc"
|
456
|
+
end
|
457
|
+
|
458
|
+
def test_newlines_to_br
|
459
|
+
assert_template_result "a<br />\nb<br />\nc", "{{ source | newline_to_br }}", 'source' => "a\nb\nc"
|
460
|
+
end
|
461
|
+
|
462
|
+
def test_plus
|
463
|
+
assert_template_result "2", "{{ 1 | plus:1 }}"
|
464
|
+
assert_template_result "2.0", "{{ '1' | plus:'1.0' }}"
|
465
|
+
|
466
|
+
assert_template_result "5", "{{ price | plus:'2' }}", 'price' => NumberLikeThing.new(3)
|
467
|
+
end
|
468
|
+
|
469
|
+
def test_minus
|
470
|
+
assert_template_result "4", "{{ input | minus:operand }}", 'input' => 5, 'operand' => 1
|
471
|
+
assert_template_result "2.3", "{{ '4.3' | minus:'2' }}"
|
472
|
+
|
473
|
+
assert_template_result "5", "{{ price | minus:'2' }}", 'price' => NumberLikeThing.new(7)
|
474
|
+
end
|
475
|
+
|
476
|
+
def test_abs
|
477
|
+
assert_template_result "17", "{{ 17 | abs }}"
|
478
|
+
assert_template_result "17", "{{ -17 | abs }}"
|
479
|
+
assert_template_result "17", "{{ '17' | abs }}"
|
480
|
+
assert_template_result "17", "{{ '-17' | abs }}"
|
481
|
+
assert_template_result "0", "{{ 0 | abs }}"
|
482
|
+
assert_template_result "0", "{{ '0' | abs }}"
|
483
|
+
assert_template_result "17.42", "{{ 17.42 | abs }}"
|
484
|
+
assert_template_result "17.42", "{{ -17.42 | abs }}"
|
485
|
+
assert_template_result "17.42", "{{ '17.42' | abs }}"
|
486
|
+
assert_template_result "17.42", "{{ '-17.42' | abs }}"
|
487
|
+
end
|
488
|
+
|
489
|
+
def test_times
|
490
|
+
assert_template_result "12", "{{ 3 | times:4 }}"
|
491
|
+
assert_template_result "0", "{{ 'foo' | times:4 }}"
|
492
|
+
assert_template_result "6", "{{ '2.1' | times:3 | replace: '.','-' | plus:0}}"
|
493
|
+
assert_template_result "7.25", "{{ 0.0725 | times:100 }}"
|
494
|
+
assert_template_result "-7.25", '{{ "-0.0725" | times:100 }}'
|
495
|
+
assert_template_result "7.25", '{{ "-0.0725" | times: -100 }}'
|
496
|
+
assert_template_result "4", "{{ price | times:2 }}", 'price' => NumberLikeThing.new(2)
|
497
|
+
end
|
498
|
+
|
499
|
+
def test_divided_by
|
500
|
+
assert_template_result "4", "{{ 12 | divided_by:3 }}"
|
501
|
+
assert_template_result "4", "{{ 14 | divided_by:3 }}"
|
502
|
+
|
503
|
+
assert_template_result "5", "{{ 15 | divided_by:3 }}"
|
504
|
+
assert_equal "Liquid error: divided by 0", Template.parse("{{ 5 | divided_by:0 }}").render
|
505
|
+
|
506
|
+
assert_template_result "0.5", "{{ 2.0 | divided_by:4 }}"
|
507
|
+
assert_raises(Liquid::ZeroDivisionError) do
|
508
|
+
assert_template_result "4", "{{ 1 | modulo: 0 }}"
|
509
|
+
end
|
510
|
+
|
511
|
+
assert_template_result "5", "{{ price | divided_by:2 }}", 'price' => NumberLikeThing.new(10)
|
512
|
+
end
|
513
|
+
|
514
|
+
def test_modulo
|
515
|
+
assert_template_result "1", "{{ 3 | modulo:2 }}"
|
516
|
+
assert_raises(Liquid::ZeroDivisionError) do
|
517
|
+
assert_template_result "4", "{{ 1 | modulo: 0 }}"
|
518
|
+
end
|
519
|
+
|
520
|
+
assert_template_result "1", "{{ price | modulo:2 }}", 'price' => NumberLikeThing.new(3)
|
521
|
+
end
|
522
|
+
|
523
|
+
def test_round
|
524
|
+
assert_template_result "5", "{{ input | round }}", 'input' => 4.6
|
525
|
+
assert_template_result "4", "{{ '4.3' | round }}"
|
526
|
+
assert_template_result "4.56", "{{ input | round: 2 }}", 'input' => 4.5612
|
527
|
+
assert_raises(Liquid::FloatDomainError) do
|
528
|
+
assert_template_result "4", "{{ 1.0 | divided_by: 0.0 | round }}"
|
529
|
+
end
|
530
|
+
|
531
|
+
assert_template_result "5", "{{ price | round }}", 'price' => NumberLikeThing.new(4.6)
|
532
|
+
assert_template_result "4", "{{ price | round }}", 'price' => NumberLikeThing.new(4.3)
|
533
|
+
end
|
534
|
+
|
535
|
+
def test_ceil
|
536
|
+
assert_template_result "5", "{{ input | ceil }}", 'input' => 4.6
|
537
|
+
assert_template_result "5", "{{ '4.3' | ceil }}"
|
538
|
+
assert_raises(Liquid::FloatDomainError) do
|
539
|
+
assert_template_result "4", "{{ 1.0 | divided_by: 0.0 | ceil }}"
|
540
|
+
end
|
541
|
+
|
542
|
+
assert_template_result "5", "{{ price | ceil }}", 'price' => NumberLikeThing.new(4.6)
|
543
|
+
end
|
544
|
+
|
545
|
+
def test_floor
|
546
|
+
assert_template_result "4", "{{ input | floor }}", 'input' => 4.6
|
547
|
+
assert_template_result "4", "{{ '4.3' | floor }}"
|
548
|
+
assert_raises(Liquid::FloatDomainError) do
|
549
|
+
assert_template_result "4", "{{ 1.0 | divided_by: 0.0 | floor }}"
|
550
|
+
end
|
551
|
+
|
552
|
+
assert_template_result "5", "{{ price | floor }}", 'price' => NumberLikeThing.new(5.4)
|
553
|
+
end
|
554
|
+
|
555
|
+
def test_at_most
|
556
|
+
assert_template_result "4", "{{ 5 | at_most:4 }}"
|
557
|
+
assert_template_result "5", "{{ 5 | at_most:5 }}"
|
558
|
+
assert_template_result "5", "{{ 5 | at_most:6 }}"
|
559
|
+
|
560
|
+
assert_template_result "4.5", "{{ 4.5 | at_most:5 }}"
|
561
|
+
assert_template_result "5", "{{ width | at_most:5 }}", 'width' => NumberLikeThing.new(6)
|
562
|
+
assert_template_result "4", "{{ width | at_most:5 }}", 'width' => NumberLikeThing.new(4)
|
563
|
+
assert_template_result "4", "{{ 5 | at_most: width }}", 'width' => NumberLikeThing.new(4)
|
564
|
+
end
|
565
|
+
|
566
|
+
def test_at_least
|
567
|
+
assert_template_result "5", "{{ 5 | at_least:4 }}"
|
568
|
+
assert_template_result "5", "{{ 5 | at_least:5 }}"
|
569
|
+
assert_template_result "6", "{{ 5 | at_least:6 }}"
|
570
|
+
|
571
|
+
assert_template_result "5", "{{ 4.5 | at_least:5 }}"
|
572
|
+
assert_template_result "6", "{{ width | at_least:5 }}", 'width' => NumberLikeThing.new(6)
|
573
|
+
assert_template_result "5", "{{ width | at_least:5 }}", 'width' => NumberLikeThing.new(4)
|
574
|
+
assert_template_result "6", "{{ 5 | at_least: width }}", 'width' => NumberLikeThing.new(6)
|
575
|
+
end
|
576
|
+
|
577
|
+
def test_append
|
578
|
+
assigns = { 'a' => 'bc', 'b' => 'd' }
|
579
|
+
assert_template_result('bcd', "{{ a | append: 'd'}}", assigns)
|
580
|
+
assert_template_result('bcd', "{{ a | append: b}}", assigns)
|
581
|
+
end
|
582
|
+
|
583
|
+
def test_concat
|
584
|
+
assert_equal [1, 2, 3, 4], @filters.concat([1, 2], [3, 4])
|
585
|
+
assert_equal [1, 2, 'a'], @filters.concat([1, 2], ['a'])
|
586
|
+
assert_equal [1, 2, 10], @filters.concat([1, 2], [10])
|
587
|
+
|
588
|
+
assert_raises(Liquid::ArgumentError, "concat filter requires an array argument") do
|
589
|
+
@filters.concat([1, 2], 10)
|
590
|
+
end
|
591
|
+
end
|
592
|
+
|
593
|
+
def test_prepend
|
594
|
+
assigns = { 'a' => 'bc', 'b' => 'a' }
|
595
|
+
assert_template_result('abc', "{{ a | prepend: 'a'}}", assigns)
|
596
|
+
assert_template_result('abc', "{{ a | prepend: b}}", assigns)
|
597
|
+
end
|
598
|
+
|
599
|
+
def test_default
|
600
|
+
assert_equal "foo", @filters.default("foo", "bar")
|
601
|
+
assert_equal "bar", @filters.default(nil, "bar")
|
602
|
+
assert_equal "bar", @filters.default("", "bar")
|
603
|
+
assert_equal "bar", @filters.default(false, "bar")
|
604
|
+
assert_equal "bar", @filters.default([], "bar")
|
605
|
+
assert_equal "bar", @filters.default({}, "bar")
|
606
|
+
end
|
607
|
+
|
608
|
+
def test_cannot_access_private_methods
|
609
|
+
assert_template_result('a', "{{ 'a' | to_number }}")
|
610
|
+
end
|
611
|
+
|
612
|
+
def test_date_raises_nothing
|
613
|
+
assert_template_result('', "{{ '' | date: '%D' }}")
|
614
|
+
assert_template_result('abc', "{{ 'abc' | date: '%D' }}")
|
615
|
+
end
|
616
|
+
|
617
|
+
def test_where
|
618
|
+
input = [
|
619
|
+
{ "handle" => "alpha", "ok" => true },
|
620
|
+
{ "handle" => "beta", "ok" => false },
|
621
|
+
{ "handle" => "gamma", "ok" => false },
|
622
|
+
{ "handle" => "delta", "ok" => true }
|
623
|
+
]
|
624
|
+
|
625
|
+
expectation = [
|
626
|
+
{ "handle" => "alpha", "ok" => true },
|
627
|
+
{ "handle" => "delta", "ok" => true }
|
628
|
+
]
|
629
|
+
|
630
|
+
assert_equal expectation, @filters.where(input, "ok", true)
|
631
|
+
assert_equal expectation, @filters.where(input, "ok")
|
632
|
+
end
|
633
|
+
|
634
|
+
def test_where_no_key_set
|
635
|
+
input = [
|
636
|
+
{ "handle" => "alpha", "ok" => true },
|
637
|
+
{ "handle" => "beta" },
|
638
|
+
{ "handle" => "gamma" },
|
639
|
+
{ "handle" => "delta", "ok" => true }
|
640
|
+
]
|
641
|
+
|
642
|
+
expectation = [
|
643
|
+
{ "handle" => "alpha", "ok" => true },
|
644
|
+
{ "handle" => "delta", "ok" => true }
|
645
|
+
]
|
646
|
+
|
647
|
+
assert_equal expectation, @filters.where(input, "ok", true)
|
648
|
+
assert_equal expectation, @filters.where(input, "ok")
|
649
|
+
end
|
650
|
+
|
651
|
+
def test_where_non_array_map_input
|
652
|
+
assert_equal [{ "a" => "ok" }], @filters.where({ "a" => "ok" }, "a", "ok")
|
653
|
+
assert_equal [], @filters.where({ "a" => "not ok" }, "a", "ok")
|
654
|
+
end
|
655
|
+
|
656
|
+
def test_where_indexable_but_non_map_value
|
657
|
+
assert_raises(Liquid::ArgumentError) { @filters.where(1, "ok", true) }
|
658
|
+
assert_raises(Liquid::ArgumentError) { @filters.where(1, "ok") }
|
659
|
+
end
|
660
|
+
|
661
|
+
def test_where_non_boolean_value
|
662
|
+
input = [
|
663
|
+
{ "message" => "Bonjour!", "language" => "French" },
|
664
|
+
{ "message" => "Hello!", "language" => "English" },
|
665
|
+
{ "message" => "Hallo!", "language" => "German" }
|
666
|
+
]
|
667
|
+
|
668
|
+
assert_equal [{ "message" => "Bonjour!", "language" => "French" }], @filters.where(input, "language", "French")
|
669
|
+
assert_equal [{ "message" => "Hallo!", "language" => "German" }], @filters.where(input, "language", "German")
|
670
|
+
assert_equal [{ "message" => "Hello!", "language" => "English" }], @filters.where(input, "language", "English")
|
671
|
+
end
|
672
|
+
|
673
|
+
def test_where_array_of_only_unindexable_values
|
674
|
+
assert_nil @filters.where([nil], "ok", true)
|
675
|
+
assert_nil @filters.where([nil], "ok")
|
676
|
+
end
|
677
|
+
|
678
|
+
def test_where_no_target_value
|
679
|
+
input = [
|
680
|
+
{ "foo" => false },
|
681
|
+
{ "foo" => true },
|
682
|
+
{ "foo" => "for sure" },
|
683
|
+
{ "bar" => true }
|
684
|
+
]
|
685
|
+
|
686
|
+
assert_equal [{ "foo" => true }, { "foo" => "for sure" }], @filters.where(input, "foo")
|
687
|
+
end
|
688
|
+
|
689
|
+
private
|
690
|
+
|
691
|
+
def with_timezone(tz)
|
692
|
+
old_tz = ENV['TZ']
|
693
|
+
ENV['TZ'] = tz
|
694
|
+
yield
|
695
|
+
ensure
|
696
|
+
ENV['TZ'] = old_tz
|
697
|
+
end
|
698
|
+
end # StandardFiltersTest
|