liquid 2.6.1 → 4.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (130) hide show
  1. checksums.yaml +5 -5
  2. data/History.md +194 -29
  3. data/{MIT-LICENSE → LICENSE} +0 -0
  4. data/README.md +60 -2
  5. data/lib/liquid.rb +25 -14
  6. data/lib/liquid/block.rb +47 -96
  7. data/lib/liquid/block_body.rb +143 -0
  8. data/lib/liquid/condition.rb +70 -39
  9. data/lib/liquid/context.rb +116 -157
  10. data/lib/liquid/document.rb +19 -9
  11. data/lib/liquid/drop.rb +31 -14
  12. data/lib/liquid/errors.rb +54 -10
  13. data/lib/liquid/expression.rb +49 -0
  14. data/lib/liquid/extensions.rb +19 -7
  15. data/lib/liquid/file_system.rb +25 -14
  16. data/lib/liquid/forloop_drop.rb +42 -0
  17. data/lib/liquid/i18n.rb +39 -0
  18. data/lib/liquid/interrupts.rb +2 -3
  19. data/lib/liquid/lexer.rb +55 -0
  20. data/lib/liquid/locales/en.yml +26 -0
  21. data/lib/liquid/parse_context.rb +38 -0
  22. data/lib/liquid/parse_tree_visitor.rb +42 -0
  23. data/lib/liquid/parser.rb +90 -0
  24. data/lib/liquid/parser_switching.rb +31 -0
  25. data/lib/liquid/profiler.rb +158 -0
  26. data/lib/liquid/profiler/hooks.rb +23 -0
  27. data/lib/liquid/range_lookup.rb +37 -0
  28. data/lib/liquid/resource_limits.rb +23 -0
  29. data/lib/liquid/standardfilters.rb +311 -77
  30. data/lib/liquid/strainer.rb +39 -26
  31. data/lib/liquid/tablerowloop_drop.rb +62 -0
  32. data/lib/liquid/tag.rb +28 -11
  33. data/lib/liquid/tags/assign.rb +34 -10
  34. data/lib/liquid/tags/break.rb +1 -4
  35. data/lib/liquid/tags/capture.rb +11 -9
  36. data/lib/liquid/tags/case.rb +37 -22
  37. data/lib/liquid/tags/comment.rb +10 -3
  38. data/lib/liquid/tags/continue.rb +1 -4
  39. data/lib/liquid/tags/cycle.rb +20 -14
  40. data/lib/liquid/tags/decrement.rb +4 -8
  41. data/lib/liquid/tags/for.rb +121 -60
  42. data/lib/liquid/tags/if.rb +73 -30
  43. data/lib/liquid/tags/ifchanged.rb +3 -5
  44. data/lib/liquid/tags/include.rb +77 -46
  45. data/lib/liquid/tags/increment.rb +4 -8
  46. data/lib/liquid/tags/raw.rb +35 -10
  47. data/lib/liquid/tags/table_row.rb +62 -0
  48. data/lib/liquid/tags/unless.rb +6 -9
  49. data/lib/liquid/template.rb +130 -32
  50. data/lib/liquid/tokenizer.rb +31 -0
  51. data/lib/liquid/truffle.rb +5 -0
  52. data/lib/liquid/utils.rb +57 -4
  53. data/lib/liquid/variable.rb +121 -30
  54. data/lib/liquid/variable_lookup.rb +88 -0
  55. data/lib/liquid/version.rb +2 -1
  56. data/test/fixtures/en_locale.yml +9 -0
  57. data/test/integration/assign_test.rb +48 -0
  58. data/test/integration/blank_test.rb +106 -0
  59. data/test/integration/block_test.rb +12 -0
  60. data/test/{liquid → integration}/capture_test.rb +13 -3
  61. data/test/integration/context_test.rb +32 -0
  62. data/test/integration/document_test.rb +19 -0
  63. data/test/integration/drop_test.rb +273 -0
  64. data/test/integration/error_handling_test.rb +260 -0
  65. data/test/integration/filter_test.rb +178 -0
  66. data/test/integration/hash_ordering_test.rb +23 -0
  67. data/test/integration/output_test.rb +123 -0
  68. data/test/integration/parse_tree_visitor_test.rb +247 -0
  69. data/test/integration/parsing_quirks_test.rb +122 -0
  70. data/test/integration/render_profiling_test.rb +154 -0
  71. data/test/integration/security_test.rb +80 -0
  72. data/test/integration/standard_filter_test.rb +776 -0
  73. data/test/{liquid → integration}/tags/break_tag_test.rb +2 -3
  74. data/test/{liquid → integration}/tags/continue_tag_test.rb +1 -2
  75. data/test/integration/tags/for_tag_test.rb +410 -0
  76. data/test/integration/tags/if_else_tag_test.rb +188 -0
  77. data/test/integration/tags/include_tag_test.rb +253 -0
  78. data/test/integration/tags/increment_tag_test.rb +23 -0
  79. data/test/{liquid → integration}/tags/raw_tag_test.rb +9 -2
  80. data/test/integration/tags/standard_tag_test.rb +296 -0
  81. data/test/integration/tags/statements_test.rb +111 -0
  82. data/test/{liquid/tags/html_tag_test.rb → integration/tags/table_row_test.rb} +25 -24
  83. data/test/integration/tags/unless_else_tag_test.rb +26 -0
  84. data/test/integration/template_test.rb +332 -0
  85. data/test/integration/trim_mode_test.rb +529 -0
  86. data/test/integration/variable_test.rb +96 -0
  87. data/test/test_helper.rb +106 -19
  88. data/test/truffle/truffle_test.rb +9 -0
  89. data/test/{liquid/block_test.rb → unit/block_unit_test.rb} +9 -9
  90. data/test/unit/condition_unit_test.rb +166 -0
  91. data/test/{liquid/context_test.rb → unit/context_unit_test.rb} +85 -74
  92. data/test/unit/file_system_unit_test.rb +35 -0
  93. data/test/unit/i18n_unit_test.rb +37 -0
  94. data/test/unit/lexer_unit_test.rb +51 -0
  95. data/test/unit/parser_unit_test.rb +82 -0
  96. data/test/{liquid/regexp_test.rb → unit/regexp_unit_test.rb} +4 -4
  97. data/test/unit/strainer_unit_test.rb +164 -0
  98. data/test/unit/tag_unit_test.rb +21 -0
  99. data/test/unit/tags/case_tag_unit_test.rb +10 -0
  100. data/test/unit/tags/for_tag_unit_test.rb +13 -0
  101. data/test/unit/tags/if_tag_unit_test.rb +8 -0
  102. data/test/unit/template_unit_test.rb +78 -0
  103. data/test/unit/tokenizer_unit_test.rb +55 -0
  104. data/test/unit/variable_unit_test.rb +162 -0
  105. metadata +157 -77
  106. data/lib/extras/liquid_view.rb +0 -51
  107. data/lib/liquid/htmltags.rb +0 -74
  108. data/lib/liquid/module_ex.rb +0 -62
  109. data/test/liquid/assign_test.rb +0 -21
  110. data/test/liquid/condition_test.rb +0 -127
  111. data/test/liquid/drop_test.rb +0 -180
  112. data/test/liquid/error_handling_test.rb +0 -81
  113. data/test/liquid/file_system_test.rb +0 -29
  114. data/test/liquid/filter_test.rb +0 -125
  115. data/test/liquid/hash_ordering_test.rb +0 -25
  116. data/test/liquid/module_ex_test.rb +0 -87
  117. data/test/liquid/output_test.rb +0 -116
  118. data/test/liquid/parsing_quirks_test.rb +0 -52
  119. data/test/liquid/security_test.rb +0 -64
  120. data/test/liquid/standard_filter_test.rb +0 -251
  121. data/test/liquid/strainer_test.rb +0 -52
  122. data/test/liquid/tags/for_tag_test.rb +0 -297
  123. data/test/liquid/tags/if_else_tag_test.rb +0 -166
  124. data/test/liquid/tags/include_tag_test.rb +0 -166
  125. data/test/liquid/tags/increment_tag_test.rb +0 -24
  126. data/test/liquid/tags/standard_tag_test.rb +0 -295
  127. data/test/liquid/tags/statements_test.rb +0 -134
  128. data/test/liquid/tags/unless_else_tag_test.rb +0 -26
  129. data/test/liquid/template_test.rb +0 -146
  130. data/test/liquid/variable_test.rb +0 -186
@@ -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,776 @@
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 '&lt;strong&gt;', @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 '&lt;strong&gt;', @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 '&lt;strong&gt;Hulk&lt;/strong&gt;', @filters.escape_once('&lt;strong&gt;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
+ exception = assert_raises Liquid::ArgumentError do
162
+ @filters.url_decode('%ff')
163
+ end
164
+ assert_equal 'Liquid error: invalid byte sequence in UTF-8', exception.message
165
+ end
166
+
167
+ def test_truncatewords
168
+ assert_equal 'one two three', @filters.truncatewords('one two three', 4)
169
+ assert_equal 'one two...', @filters.truncatewords('one two three', 2)
170
+ assert_equal 'one two three', @filters.truncatewords('one two three')
171
+ assert_equal 'Two small (13&#8221; x 5.5&#8221; x 10&#8221; high) baskets fit inside one large basket (13&#8221;...', @filters.truncatewords('Two small (13&#8221; x 5.5&#8221; x 10&#8221; high) baskets fit inside one large basket (13&#8221; x 16&#8221; x 10.5&#8221; high) with cover.', 15)
172
+ assert_equal "测试测试测试测试", @filters.truncatewords('测试测试测试测试', 5)
173
+ assert_equal 'one two1', @filters.truncatewords("one two three", 2, 1)
174
+ end
175
+
176
+ def test_strip_html
177
+ assert_equal 'test', @filters.strip_html("<div>test</div>")
178
+ assert_equal 'test', @filters.strip_html("<div id='test'>test</div>")
179
+ assert_equal '', @filters.strip_html("<script type='text/javascript'>document.write('some stuff');</script>")
180
+ assert_equal '', @filters.strip_html("<style type='text/css'>foo bar</style>")
181
+ assert_equal 'test', @filters.strip_html("<div\nclass='multiline'>test</div>")
182
+ assert_equal 'test', @filters.strip_html("<!-- foo bar \n test -->test")
183
+ assert_equal '', @filters.strip_html(nil)
184
+
185
+ # Quirk of the existing implementation
186
+ assert_equal 'foo;', @filters.strip_html("<<<script </script>script>foo;</script>")
187
+ end
188
+
189
+ def test_join
190
+ assert_equal '1 2 3 4', @filters.join([1, 2, 3, 4])
191
+ assert_equal '1 - 2 - 3 - 4', @filters.join([1, 2, 3, 4], ' - ')
192
+ assert_equal '1121314', @filters.join([1, 2, 3, 4], 1)
193
+ end
194
+
195
+ def test_sort
196
+ assert_equal [1, 2, 3, 4], @filters.sort([4, 3, 2, 1])
197
+ assert_equal [{ "a" => 1 }, { "a" => 2 }, { "a" => 3 }, { "a" => 4 }], @filters.sort([{ "a" => 4 }, { "a" => 3 }, { "a" => 1 }, { "a" => 2 }], "a")
198
+ end
199
+
200
+ def test_sort_with_nils
201
+ assert_equal [1, 2, 3, 4, nil], @filters.sort([nil, 4, 3, 2, 1])
202
+ assert_equal [{ "a" => 1 }, { "a" => 2 }, { "a" => 3 }, { "a" => 4 }, {}], @filters.sort([{ "a" => 4 }, { "a" => 3 }, {}, { "a" => 1 }, { "a" => 2 }], "a")
203
+ end
204
+
205
+ def test_sort_when_property_is_sometimes_missing_puts_nils_last
206
+ input = [
207
+ { "price" => 4, "handle" => "alpha" },
208
+ { "handle" => "beta" },
209
+ { "price" => 1, "handle" => "gamma" },
210
+ { "handle" => "delta" },
211
+ { "price" => 2, "handle" => "epsilon" }
212
+ ]
213
+ expectation = [
214
+ { "price" => 1, "handle" => "gamma" },
215
+ { "price" => 2, "handle" => "epsilon" },
216
+ { "price" => 4, "handle" => "alpha" },
217
+ { "handle" => "delta" },
218
+ { "handle" => "beta" }
219
+ ]
220
+ assert_equal expectation, @filters.sort(input, "price")
221
+ end
222
+
223
+ def test_sort_natural
224
+ assert_equal ["a", "B", "c", "D"], @filters.sort_natural(["c", "D", "a", "B"])
225
+ assert_equal [{ "a" => "a" }, { "a" => "B" }, { "a" => "c" }, { "a" => "D" }], @filters.sort_natural([{ "a" => "D" }, { "a" => "c" }, { "a" => "a" }, { "a" => "B" }], "a")
226
+ end
227
+
228
+ def test_sort_natural_with_nils
229
+ assert_equal ["a", "B", "c", "D", nil], @filters.sort_natural([nil, "c", "D", "a", "B"])
230
+ assert_equal [{ "a" => "a" }, { "a" => "B" }, { "a" => "c" }, { "a" => "D" }, {}], @filters.sort_natural([{ "a" => "D" }, { "a" => "c" }, {}, { "a" => "a" }, { "a" => "B" }], "a")
231
+ end
232
+
233
+ def test_sort_natural_when_property_is_sometimes_missing_puts_nils_last
234
+ input = [
235
+ { "price" => "4", "handle" => "alpha" },
236
+ { "handle" => "beta" },
237
+ { "price" => "1", "handle" => "gamma" },
238
+ { "handle" => "delta" },
239
+ { "price" => 2, "handle" => "epsilon" }
240
+ ]
241
+ expectation = [
242
+ { "price" => "1", "handle" => "gamma" },
243
+ { "price" => 2, "handle" => "epsilon" },
244
+ { "price" => "4", "handle" => "alpha" },
245
+ { "handle" => "delta" },
246
+ { "handle" => "beta" }
247
+ ]
248
+ assert_equal expectation, @filters.sort_natural(input, "price")
249
+ end
250
+
251
+ def test_sort_natural_case_check
252
+ input = [
253
+ { "key" => "X" },
254
+ { "key" => "Y" },
255
+ { "key" => "Z" },
256
+ { "fake" => "t" },
257
+ { "key" => "a" },
258
+ { "key" => "b" },
259
+ { "key" => "c" }
260
+ ]
261
+ expectation = [
262
+ { "key" => "a" },
263
+ { "key" => "b" },
264
+ { "key" => "c" },
265
+ { "key" => "X" },
266
+ { "key" => "Y" },
267
+ { "key" => "Z" },
268
+ { "fake" => "t" }
269
+ ]
270
+ assert_equal expectation, @filters.sort_natural(input, "key")
271
+ assert_equal ["a", "b", "c", "X", "Y", "Z"], @filters.sort_natural(["X", "Y", "Z", "a", "b", "c"])
272
+ end
273
+
274
+ def test_sort_empty_array
275
+ assert_equal [], @filters.sort([], "a")
276
+ end
277
+
278
+ def test_sort_invalid_property
279
+ foo = [
280
+ [1],
281
+ [2],
282
+ [3]
283
+ ]
284
+
285
+ assert_raises Liquid::ArgumentError do
286
+ @filters.sort(foo, "bar")
287
+ end
288
+ end
289
+
290
+ def test_sort_natural_empty_array
291
+ assert_equal [], @filters.sort_natural([], "a")
292
+ end
293
+
294
+ def test_sort_natural_invalid_property
295
+ foo = [
296
+ [1],
297
+ [2],
298
+ [3]
299
+ ]
300
+
301
+ assert_raises Liquid::ArgumentError do
302
+ @filters.sort_natural(foo, "bar")
303
+ end
304
+ end
305
+
306
+ def test_legacy_sort_hash
307
+ assert_equal [{ a: 1, b: 2 }], @filters.sort({ a: 1, b: 2 })
308
+ end
309
+
310
+ def test_numerical_vs_lexicographical_sort
311
+ assert_equal [2, 10], @filters.sort([10, 2])
312
+ assert_equal [{ "a" => 2 }, { "a" => 10 }], @filters.sort([{ "a" => 10 }, { "a" => 2 }], "a")
313
+ assert_equal ["10", "2"], @filters.sort(["10", "2"])
314
+ assert_equal [{ "a" => "10" }, { "a" => "2" }], @filters.sort([{ "a" => "10" }, { "a" => "2" }], "a")
315
+ end
316
+
317
+ def test_uniq
318
+ assert_equal ["foo"], @filters.uniq("foo")
319
+ assert_equal [1, 3, 2, 4], @filters.uniq([1, 1, 3, 2, 3, 1, 4, 3, 2, 1])
320
+ assert_equal [{ "a" => 1 }, { "a" => 3 }, { "a" => 2 }], @filters.uniq([{ "a" => 1 }, { "a" => 3 }, { "a" => 1 }, { "a" => 2 }], "a")
321
+ testdrop = TestDrop.new
322
+ assert_equal [testdrop], @filters.uniq([testdrop, TestDrop.new], 'test')
323
+ end
324
+
325
+ def test_uniq_empty_array
326
+ assert_equal [], @filters.uniq([], "a")
327
+ end
328
+
329
+ def test_uniq_invalid_property
330
+ foo = [
331
+ [1],
332
+ [2],
333
+ [3]
334
+ ]
335
+
336
+ assert_raises Liquid::ArgumentError do
337
+ @filters.uniq(foo, "bar")
338
+ end
339
+ end
340
+
341
+ def test_compact_empty_array
342
+ assert_equal [], @filters.compact([], "a")
343
+ end
344
+
345
+ def test_compact_invalid_property
346
+ foo = [
347
+ [1],
348
+ [2],
349
+ [3]
350
+ ]
351
+
352
+ assert_raises Liquid::ArgumentError do
353
+ @filters.compact(foo, "bar")
354
+ end
355
+ end
356
+
357
+ def test_reverse
358
+ assert_equal [4, 3, 2, 1], @filters.reverse([1, 2, 3, 4])
359
+ end
360
+
361
+ def test_legacy_reverse_hash
362
+ assert_equal [{ a: 1, b: 2 }], @filters.reverse(a: 1, b: 2)
363
+ end
364
+
365
+ def test_map
366
+ assert_equal [1, 2, 3, 4], @filters.map([{ "a" => 1 }, { "a" => 2 }, { "a" => 3 }, { "a" => 4 }], 'a')
367
+ assert_template_result 'abc', "{{ ary | map:'foo' | map:'bar' }}",
368
+ 'ary' => [{ 'foo' => { 'bar' => 'a' } }, { 'foo' => { 'bar' => 'b' } }, { 'foo' => { 'bar' => 'c' } }]
369
+ end
370
+
371
+ def test_map_doesnt_call_arbitrary_stuff
372
+ assert_template_result "", '{{ "foo" | map: "__id__" }}'
373
+ assert_template_result "", '{{ "foo" | map: "inspect" }}'
374
+ end
375
+
376
+ def test_map_calls_to_liquid
377
+ t = TestThing.new
378
+ assert_template_result "woot: 1", '{{ foo | map: "whatever" }}', "foo" => [t]
379
+ end
380
+
381
+ def test_map_on_hashes
382
+ assert_template_result "4217", '{{ thing | map: "foo" | map: "bar" }}',
383
+ "thing" => { "foo" => [ { "bar" => 42 }, { "bar" => 17 } ] }
384
+ end
385
+
386
+ def test_legacy_map_on_hashes_with_dynamic_key
387
+ template = "{% assign key = 'foo' %}{{ thing | map: key | map: 'bar' }}"
388
+ hash = { "foo" => { "bar" => 42 } }
389
+ assert_template_result "42", template, "thing" => hash
390
+ end
391
+
392
+ def test_sort_calls_to_liquid
393
+ t = TestThing.new
394
+ Liquid::Template.parse('{{ foo | sort: "whatever" }}').render("foo" => [t])
395
+ assert t.foo > 0
396
+ end
397
+
398
+ def test_map_over_proc
399
+ drop = TestDrop.new
400
+ p = proc{ drop }
401
+ templ = '{{ procs | map: "test" }}'
402
+ assert_template_result "testfoo", templ, "procs" => [p]
403
+ end
404
+
405
+ def test_map_over_drops_returning_procs
406
+ drops = [
407
+ {
408
+ "proc" => ->{ "foo" },
409
+ },
410
+ {
411
+ "proc" => ->{ "bar" },
412
+ },
413
+ ]
414
+ templ = '{{ drops | map: "proc" }}'
415
+ assert_template_result "foobar", templ, "drops" => drops
416
+ end
417
+
418
+ def test_map_works_on_enumerables
419
+ assert_template_result "123", '{{ foo | map: "foo" }}', "foo" => TestEnumerable.new
420
+ end
421
+
422
+ def test_map_returns_empty_on_2d_input_array
423
+ foo = [
424
+ [1],
425
+ [2],
426
+ [3]
427
+ ]
428
+
429
+ assert_raises Liquid::ArgumentError do
430
+ @filters.map(foo, "bar")
431
+ end
432
+ end
433
+
434
+ def test_map_returns_empty_with_no_property
435
+ foo = [
436
+ [1],
437
+ [2],
438
+ [3]
439
+ ]
440
+ assert_raises Liquid::ArgumentError do
441
+ @filters.map(foo, nil)
442
+ end
443
+ end
444
+
445
+ def test_sort_works_on_enumerables
446
+ assert_template_result "213", '{{ foo | sort: "bar" | map: "foo" }}', "foo" => TestEnumerable.new
447
+ end
448
+
449
+ def test_first_and_last_call_to_liquid
450
+ assert_template_result 'foobar', '{{ foo | first }}', 'foo' => [ThingWithToLiquid.new]
451
+ assert_template_result 'foobar', '{{ foo | last }}', 'foo' => [ThingWithToLiquid.new]
452
+ end
453
+
454
+ def test_truncate_calls_to_liquid
455
+ assert_template_result "wo...", '{{ foo | truncate: 5 }}', "foo" => TestThing.new
456
+ end
457
+
458
+ def test_date
459
+ assert_equal 'May', @filters.date(Time.parse("2006-05-05 10:00:00"), "%B")
460
+ assert_equal 'June', @filters.date(Time.parse("2006-06-05 10:00:00"), "%B")
461
+ assert_equal 'July', @filters.date(Time.parse("2006-07-05 10:00:00"), "%B")
462
+
463
+ assert_equal 'May', @filters.date("2006-05-05 10:00:00", "%B")
464
+ assert_equal 'June', @filters.date("2006-06-05 10:00:00", "%B")
465
+ assert_equal 'July', @filters.date("2006-07-05 10:00:00", "%B")
466
+
467
+ assert_equal '2006-07-05 10:00:00', @filters.date("2006-07-05 10:00:00", "")
468
+ assert_equal '2006-07-05 10:00:00', @filters.date("2006-07-05 10:00:00", "")
469
+ assert_equal '2006-07-05 10:00:00', @filters.date("2006-07-05 10:00:00", "")
470
+ assert_equal '2006-07-05 10:00:00', @filters.date("2006-07-05 10:00:00", nil)
471
+
472
+ assert_equal '07/05/2006', @filters.date("2006-07-05 10:00:00", "%m/%d/%Y")
473
+
474
+ assert_equal "07/16/2004", @filters.date("Fri Jul 16 01:00:00 2004", "%m/%d/%Y")
475
+ assert_equal Date.today.year.to_s, @filters.date('now', '%Y')
476
+ assert_equal Date.today.year.to_s, @filters.date('today', '%Y')
477
+ assert_equal Date.today.year.to_s, @filters.date('Today', '%Y')
478
+
479
+ assert_nil @filters.date(nil, "%B")
480
+
481
+ assert_equal '', @filters.date('', "%B")
482
+
483
+ with_timezone("UTC") do
484
+ assert_equal "07/05/2006", @filters.date(1152098955, "%m/%d/%Y")
485
+ assert_equal "07/05/2006", @filters.date("1152098955", "%m/%d/%Y")
486
+ end
487
+ end
488
+
489
+ def test_first_last
490
+ assert_equal 1, @filters.first([1, 2, 3])
491
+ assert_equal 3, @filters.last([1, 2, 3])
492
+ assert_nil @filters.first([])
493
+ assert_nil @filters.last([])
494
+ end
495
+
496
+ def test_replace
497
+ assert_equal '2 2 2 2', @filters.replace('1 1 1 1', '1', 2)
498
+ assert_equal '2 2 2 2', @filters.replace('1 1 1 1', 1, 2)
499
+ assert_equal '2 1 1 1', @filters.replace_first('1 1 1 1', '1', 2)
500
+ assert_equal '2 1 1 1', @filters.replace_first('1 1 1 1', 1, 2)
501
+ assert_template_result '2 1 1 1', "{{ '1 1 1 1' | replace_first: '1', 2 }}"
502
+ end
503
+
504
+ def test_remove
505
+ assert_equal ' ', @filters.remove("a a a a", 'a')
506
+ assert_equal ' ', @filters.remove("1 1 1 1", 1)
507
+ assert_equal 'a a a', @filters.remove_first("a a a a", 'a ')
508
+ assert_equal ' 1 1 1', @filters.remove_first("1 1 1 1", 1)
509
+ assert_template_result 'a a a', "{{ 'a a a a' | remove_first: 'a ' }}"
510
+ end
511
+
512
+ def test_pipes_in_string_arguments
513
+ assert_template_result 'foobar', "{{ 'foo|bar' | remove: '|' }}"
514
+ end
515
+
516
+ def test_strip
517
+ assert_template_result 'ab c', "{{ source | strip }}", 'source' => " ab c "
518
+ assert_template_result 'ab c', "{{ source | strip }}", 'source' => " \tab c \n \t"
519
+ end
520
+
521
+ def test_lstrip
522
+ assert_template_result 'ab c ', "{{ source | lstrip }}", 'source' => " ab c "
523
+ assert_template_result "ab c \n \t", "{{ source | lstrip }}", 'source' => " \tab c \n \t"
524
+ end
525
+
526
+ def test_rstrip
527
+ assert_template_result " ab c", "{{ source | rstrip }}", 'source' => " ab c "
528
+ assert_template_result " \tab c", "{{ source | rstrip }}", 'source' => " \tab c \n \t"
529
+ end
530
+
531
+ def test_strip_newlines
532
+ assert_template_result 'abc', "{{ source | strip_newlines }}", 'source' => "a\nb\nc"
533
+ assert_template_result 'abc', "{{ source | strip_newlines }}", 'source' => "a\r\nb\nc"
534
+ end
535
+
536
+ def test_newlines_to_br
537
+ assert_template_result "a<br />\nb<br />\nc", "{{ source | newline_to_br }}", 'source' => "a\nb\nc"
538
+ end
539
+
540
+ def test_plus
541
+ assert_template_result "2", "{{ 1 | plus:1 }}"
542
+ assert_template_result "2.0", "{{ '1' | plus:'1.0' }}"
543
+
544
+ assert_template_result "5", "{{ price | plus:'2' }}", 'price' => NumberLikeThing.new(3)
545
+ end
546
+
547
+ def test_minus
548
+ assert_template_result "4", "{{ input | minus:operand }}", 'input' => 5, 'operand' => 1
549
+ assert_template_result "2.3", "{{ '4.3' | minus:'2' }}"
550
+
551
+ assert_template_result "5", "{{ price | minus:'2' }}", 'price' => NumberLikeThing.new(7)
552
+ end
553
+
554
+ def test_abs
555
+ assert_template_result "17", "{{ 17 | abs }}"
556
+ assert_template_result "17", "{{ -17 | abs }}"
557
+ assert_template_result "17", "{{ '17' | abs }}"
558
+ assert_template_result "17", "{{ '-17' | abs }}"
559
+ assert_template_result "0", "{{ 0 | abs }}"
560
+ assert_template_result "0", "{{ '0' | abs }}"
561
+ assert_template_result "17.42", "{{ 17.42 | abs }}"
562
+ assert_template_result "17.42", "{{ -17.42 | abs }}"
563
+ assert_template_result "17.42", "{{ '17.42' | abs }}"
564
+ assert_template_result "17.42", "{{ '-17.42' | abs }}"
565
+ end
566
+
567
+ def test_times
568
+ assert_template_result "12", "{{ 3 | times:4 }}"
569
+ assert_template_result "0", "{{ 'foo' | times:4 }}"
570
+ assert_template_result "6", "{{ '2.1' | times:3 | replace: '.','-' | plus:0}}"
571
+ assert_template_result "7.25", "{{ 0.0725 | times:100 }}"
572
+ assert_template_result "-7.25", '{{ "-0.0725" | times:100 }}'
573
+ assert_template_result "7.25", '{{ "-0.0725" | times: -100 }}'
574
+ assert_template_result "4", "{{ price | times:2 }}", 'price' => NumberLikeThing.new(2)
575
+ end
576
+
577
+ def test_divided_by
578
+ assert_template_result "4", "{{ 12 | divided_by:3 }}"
579
+ assert_template_result "4", "{{ 14 | divided_by:3 }}"
580
+
581
+ assert_template_result "5", "{{ 15 | divided_by:3 }}"
582
+ assert_equal "Liquid error: divided by 0", Template.parse("{{ 5 | divided_by:0 }}").render
583
+
584
+ assert_template_result "0.5", "{{ 2.0 | divided_by:4 }}"
585
+ assert_raises(Liquid::ZeroDivisionError) do
586
+ assert_template_result "4", "{{ 1 | modulo: 0 }}"
587
+ end
588
+
589
+ assert_template_result "5", "{{ price | divided_by:2 }}", 'price' => NumberLikeThing.new(10)
590
+ end
591
+
592
+ def test_modulo
593
+ assert_template_result "1", "{{ 3 | modulo:2 }}"
594
+ assert_raises(Liquid::ZeroDivisionError) do
595
+ assert_template_result "4", "{{ 1 | modulo: 0 }}"
596
+ end
597
+
598
+ assert_template_result "1", "{{ price | modulo:2 }}", 'price' => NumberLikeThing.new(3)
599
+ end
600
+
601
+ def test_round
602
+ assert_template_result "5", "{{ input | round }}", 'input' => 4.6
603
+ assert_template_result "4", "{{ '4.3' | round }}"
604
+ assert_template_result "4.56", "{{ input | round: 2 }}", 'input' => 4.5612
605
+ assert_raises(Liquid::FloatDomainError) do
606
+ assert_template_result "4", "{{ 1.0 | divided_by: 0.0 | round }}"
607
+ end
608
+
609
+ assert_template_result "5", "{{ price | round }}", 'price' => NumberLikeThing.new(4.6)
610
+ assert_template_result "4", "{{ price | round }}", 'price' => NumberLikeThing.new(4.3)
611
+ end
612
+
613
+ def test_ceil
614
+ assert_template_result "5", "{{ input | ceil }}", 'input' => 4.6
615
+ assert_template_result "5", "{{ '4.3' | ceil }}"
616
+ assert_raises(Liquid::FloatDomainError) do
617
+ assert_template_result "4", "{{ 1.0 | divided_by: 0.0 | ceil }}"
618
+ end
619
+
620
+ assert_template_result "5", "{{ price | ceil }}", 'price' => NumberLikeThing.new(4.6)
621
+ end
622
+
623
+ def test_floor
624
+ assert_template_result "4", "{{ input | floor }}", 'input' => 4.6
625
+ assert_template_result "4", "{{ '4.3' | floor }}"
626
+ assert_raises(Liquid::FloatDomainError) do
627
+ assert_template_result "4", "{{ 1.0 | divided_by: 0.0 | floor }}"
628
+ end
629
+
630
+ assert_template_result "5", "{{ price | floor }}", 'price' => NumberLikeThing.new(5.4)
631
+ end
632
+
633
+ def test_at_most
634
+ assert_template_result "4", "{{ 5 | at_most:4 }}"
635
+ assert_template_result "5", "{{ 5 | at_most:5 }}"
636
+ assert_template_result "5", "{{ 5 | at_most:6 }}"
637
+
638
+ assert_template_result "4.5", "{{ 4.5 | at_most:5 }}"
639
+ assert_template_result "5", "{{ width | at_most:5 }}", 'width' => NumberLikeThing.new(6)
640
+ assert_template_result "4", "{{ width | at_most:5 }}", 'width' => NumberLikeThing.new(4)
641
+ assert_template_result "4", "{{ 5 | at_most: width }}", 'width' => NumberLikeThing.new(4)
642
+ end
643
+
644
+ def test_at_least
645
+ assert_template_result "5", "{{ 5 | at_least:4 }}"
646
+ assert_template_result "5", "{{ 5 | at_least:5 }}"
647
+ assert_template_result "6", "{{ 5 | at_least:6 }}"
648
+
649
+ assert_template_result "5", "{{ 4.5 | at_least:5 }}"
650
+ assert_template_result "6", "{{ width | at_least:5 }}", 'width' => NumberLikeThing.new(6)
651
+ assert_template_result "5", "{{ width | at_least:5 }}", 'width' => NumberLikeThing.new(4)
652
+ assert_template_result "6", "{{ 5 | at_least: width }}", 'width' => NumberLikeThing.new(6)
653
+ end
654
+
655
+ def test_append
656
+ assigns = { 'a' => 'bc', 'b' => 'd' }
657
+ assert_template_result('bcd', "{{ a | append: 'd'}}", assigns)
658
+ assert_template_result('bcd', "{{ a | append: b}}", assigns)
659
+ end
660
+
661
+ def test_concat
662
+ assert_equal [1, 2, 3, 4], @filters.concat([1, 2], [3, 4])
663
+ assert_equal [1, 2, 'a'], @filters.concat([1, 2], ['a'])
664
+ assert_equal [1, 2, 10], @filters.concat([1, 2], [10])
665
+
666
+ assert_raises(Liquid::ArgumentError, "concat filter requires an array argument") do
667
+ @filters.concat([1, 2], 10)
668
+ end
669
+ end
670
+
671
+ def test_prepend
672
+ assigns = { 'a' => 'bc', 'b' => 'a' }
673
+ assert_template_result('abc', "{{ a | prepend: 'a'}}", assigns)
674
+ assert_template_result('abc', "{{ a | prepend: b}}", assigns)
675
+ end
676
+
677
+ def test_default
678
+ assert_equal "foo", @filters.default("foo", "bar")
679
+ assert_equal "bar", @filters.default(nil, "bar")
680
+ assert_equal "bar", @filters.default("", "bar")
681
+ assert_equal "bar", @filters.default(false, "bar")
682
+ assert_equal "bar", @filters.default([], "bar")
683
+ assert_equal "bar", @filters.default({}, "bar")
684
+ end
685
+
686
+ def test_cannot_access_private_methods
687
+ assert_template_result('a', "{{ 'a' | to_number }}")
688
+ end
689
+
690
+ def test_date_raises_nothing
691
+ assert_template_result('', "{{ '' | date: '%D' }}")
692
+ assert_template_result('abc', "{{ 'abc' | date: '%D' }}")
693
+ end
694
+
695
+ def test_where
696
+ input = [
697
+ { "handle" => "alpha", "ok" => true },
698
+ { "handle" => "beta", "ok" => false },
699
+ { "handle" => "gamma", "ok" => false },
700
+ { "handle" => "delta", "ok" => true }
701
+ ]
702
+
703
+ expectation = [
704
+ { "handle" => "alpha", "ok" => true },
705
+ { "handle" => "delta", "ok" => true }
706
+ ]
707
+
708
+ assert_equal expectation, @filters.where(input, "ok", true)
709
+ assert_equal expectation, @filters.where(input, "ok")
710
+ end
711
+
712
+ def test_where_no_key_set
713
+ input = [
714
+ { "handle" => "alpha", "ok" => true },
715
+ { "handle" => "beta" },
716
+ { "handle" => "gamma" },
717
+ { "handle" => "delta", "ok" => true }
718
+ ]
719
+
720
+ expectation = [
721
+ { "handle" => "alpha", "ok" => true },
722
+ { "handle" => "delta", "ok" => true }
723
+ ]
724
+
725
+ assert_equal expectation, @filters.where(input, "ok", true)
726
+ assert_equal expectation, @filters.where(input, "ok")
727
+ end
728
+
729
+ def test_where_non_array_map_input
730
+ assert_equal [{ "a" => "ok" }], @filters.where({ "a" => "ok" }, "a", "ok")
731
+ assert_equal [], @filters.where({ "a" => "not ok" }, "a", "ok")
732
+ end
733
+
734
+ def test_where_indexable_but_non_map_value
735
+ assert_raises(Liquid::ArgumentError) { @filters.where(1, "ok", true) }
736
+ assert_raises(Liquid::ArgumentError) { @filters.where(1, "ok") }
737
+ end
738
+
739
+ def test_where_non_boolean_value
740
+ input = [
741
+ { "message" => "Bonjour!", "language" => "French" },
742
+ { "message" => "Hello!", "language" => "English" },
743
+ { "message" => "Hallo!", "language" => "German" }
744
+ ]
745
+
746
+ assert_equal [{ "message" => "Bonjour!", "language" => "French" }], @filters.where(input, "language", "French")
747
+ assert_equal [{ "message" => "Hallo!", "language" => "German" }], @filters.where(input, "language", "German")
748
+ assert_equal [{ "message" => "Hello!", "language" => "English" }], @filters.where(input, "language", "English")
749
+ end
750
+
751
+ def test_where_array_of_only_unindexable_values
752
+ assert_nil @filters.where([nil], "ok", true)
753
+ assert_nil @filters.where([nil], "ok")
754
+ end
755
+
756
+ def test_where_no_target_value
757
+ input = [
758
+ { "foo" => false },
759
+ { "foo" => true },
760
+ { "foo" => "for sure" },
761
+ { "bar" => true }
762
+ ]
763
+
764
+ assert_equal [{ "foo" => true }, { "foo" => "for sure" }], @filters.where(input, "foo")
765
+ end
766
+
767
+ private
768
+
769
+ def with_timezone(tz)
770
+ old_tz = ENV['TZ']
771
+ ENV['TZ'] = tz
772
+ yield
773
+ ensure
774
+ ENV['TZ'] = old_tz
775
+ end
776
+ end # StandardFiltersTest