liquid 3.0.0.rc1 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +4 -0
  3. data/README.md +2 -2
  4. data/lib/liquid.rb +8 -0
  5. data/lib/liquid/block.rb +50 -46
  6. data/lib/liquid/block_body.rb +123 -0
  7. data/lib/liquid/condition.rb +12 -5
  8. data/lib/liquid/context.rb +75 -148
  9. data/lib/liquid/errors.rb +50 -2
  10. data/lib/liquid/expression.rb +33 -0
  11. data/lib/liquid/parser_switching.rb +31 -0
  12. data/lib/liquid/profiler.rb +159 -0
  13. data/lib/liquid/profiler/hooks.rb +23 -0
  14. data/lib/liquid/range_lookup.rb +22 -0
  15. data/lib/liquid/standardfilters.rb +29 -4
  16. data/lib/liquid/tag.rb +6 -25
  17. data/lib/liquid/tags/assign.rb +2 -1
  18. data/lib/liquid/tags/case.rb +1 -1
  19. data/lib/liquid/tags/if.rb +5 -5
  20. data/lib/liquid/tags/ifchanged.rb +1 -1
  21. data/lib/liquid/tags/include.rb +11 -1
  22. data/lib/liquid/tags/raw.rb +1 -4
  23. data/lib/liquid/tags/table_row.rb +1 -1
  24. data/lib/liquid/template.rb +55 -4
  25. data/lib/liquid/token.rb +18 -0
  26. data/lib/liquid/variable.rb +68 -41
  27. data/lib/liquid/variable_lookup.rb +78 -0
  28. data/lib/liquid/version.rb +1 -1
  29. data/test/integration/assign_test.rb +12 -1
  30. data/test/integration/blank_test.rb +1 -1
  31. data/test/integration/capture_test.rb +1 -1
  32. data/test/integration/context_test.rb +10 -11
  33. data/test/integration/drop_test.rb +29 -3
  34. data/test/integration/error_handling_test.rb +138 -41
  35. data/test/integration/filter_test.rb +7 -7
  36. data/test/integration/hash_ordering_test.rb +6 -8
  37. data/test/integration/output_test.rb +1 -1
  38. data/test/integration/parsing_quirks_test.rb +40 -18
  39. data/test/integration/render_profiling_test.rb +154 -0
  40. data/test/integration/security_test.rb +1 -1
  41. data/test/integration/standard_filter_test.rb +47 -1
  42. data/test/integration/tags/break_tag_test.rb +1 -1
  43. data/test/integration/tags/continue_tag_test.rb +1 -1
  44. data/test/integration/tags/for_tag_test.rb +2 -2
  45. data/test/integration/tags/if_else_tag_test.rb +23 -20
  46. data/test/integration/tags/include_tag_test.rb +24 -2
  47. data/test/integration/tags/increment_tag_test.rb +1 -1
  48. data/test/integration/tags/raw_tag_test.rb +1 -1
  49. data/test/integration/tags/standard_tag_test.rb +4 -4
  50. data/test/integration/tags/statements_test.rb +1 -1
  51. data/test/integration/tags/table_row_test.rb +1 -1
  52. data/test/integration/tags/unless_else_tag_test.rb +1 -1
  53. data/test/integration/template_test.rb +16 -4
  54. data/test/integration/variable_test.rb +11 -1
  55. data/test/test_helper.rb +59 -31
  56. data/test/unit/block_unit_test.rb +2 -5
  57. data/test/unit/condition_unit_test.rb +5 -1
  58. data/test/unit/context_unit_test.rb +13 -7
  59. data/test/unit/file_system_unit_test.rb +5 -5
  60. data/test/unit/i18n_unit_test.rb +3 -3
  61. data/test/unit/lexer_unit_test.rb +1 -1
  62. data/test/unit/module_ex_unit_test.rb +1 -1
  63. data/test/unit/parser_unit_test.rb +1 -1
  64. data/test/unit/regexp_unit_test.rb +1 -1
  65. data/test/unit/strainer_unit_test.rb +3 -2
  66. data/test/unit/tag_unit_test.rb +6 -1
  67. data/test/unit/tags/case_tag_unit_test.rb +1 -1
  68. data/test/unit/tags/for_tag_unit_test.rb +1 -1
  69. data/test/unit/tags/if_tag_unit_test.rb +1 -1
  70. data/test/unit/template_unit_test.rb +1 -1
  71. data/test/unit/tokenizer_unit_test.rb +10 -1
  72. data/test/unit/variable_unit_test.rb +49 -46
  73. metadata +71 -47
@@ -1,4 +1,4 @@
1
1
  # encoding: utf-8
2
2
  module Liquid
3
- VERSION = "3.0.0.rc1"
3
+ VERSION = "3.0.0"
4
4
  end
@@ -1,6 +1,6 @@
1
1
  require 'test_helper'
2
2
 
3
- class AssignTest < Test::Unit::TestCase
3
+ class AssignTest < Minitest::Test
4
4
  include Liquid
5
5
 
6
6
  def test_assigned_variable
@@ -24,4 +24,15 @@ class AssignTest < Test::Unit::TestCase
24
24
  '{% assign foo not values %}.',
25
25
  'values' => "foo,bar,baz")
26
26
  end
27
+
28
+ def test_assign_uses_error_mode
29
+ with_error_mode(:strict) do
30
+ assert_raises(SyntaxError) do
31
+ Template.parse("{% assign foo = ('X' | downcase) %}")
32
+ end
33
+ end
34
+ with_error_mode(:lax) do
35
+ assert Template.parse("{% assign foo = ('X' | downcase) %}")
36
+ end
37
+ end
27
38
  end # AssignTest
@@ -14,7 +14,7 @@ class BlankTestFileSystem
14
14
  end
15
15
  end
16
16
 
17
- class BlankTest < Test::Unit::TestCase
17
+ class BlankTest < Minitest::Test
18
18
  include Liquid
19
19
  N = 10
20
20
 
@@ -1,6 +1,6 @@
1
1
  require 'test_helper'
2
2
 
3
- class CaptureTest < Test::Unit::TestCase
3
+ class CaptureTest < Minitest::Test
4
4
  include Liquid
5
5
 
6
6
  def test_captures_block_content_in_variable
@@ -1,6 +1,6 @@
1
1
  require 'test_helper'
2
2
 
3
- class ContextTest < Test::Unit::TestCase
3
+ class ContextTest < Minitest::Test
4
4
  include Liquid
5
5
 
6
6
  def test_override_global_filter
@@ -16,18 +16,17 @@ class ContextTest < Test::Unit::TestCase
16
16
  end
17
17
  end
18
18
 
19
- Template.register_filter(global)
20
- assert_equal 'Global test', Template.parse("{{'test' | notice }}").render!
21
- assert_equal 'Local test', Template.parse("{{'test' | notice }}").render!({}, :filters => [local])
19
+ with_global_filter(global) do
20
+ assert_equal 'Global test', Template.parse("{{'test' | notice }}").render!
21
+ assert_equal 'Local test', Template.parse("{{'test' | notice }}").render!({}, :filters => [local])
22
+ end
22
23
  end
23
24
 
24
25
  def test_has_key_will_not_add_an_error_for_missing_keys
25
- Template.error_mode = :strict
26
-
27
- context = Context.new
28
-
29
- context.has_key?('unknown')
30
-
31
- assert_empty context.errors
26
+ with_error_mode :strict do
27
+ context = Context.new
28
+ context.has_key?('unknown')
29
+ assert_empty context.errors
30
+ end
32
31
  end
33
32
  end
@@ -48,6 +48,10 @@ class ProductDrop < Liquid::Drop
48
48
  ContextDrop.new
49
49
  end
50
50
 
51
+ def user_input
52
+ "foo".taint
53
+ end
54
+
51
55
  protected
52
56
  def callmenot
53
57
  "protected"
@@ -100,12 +104,34 @@ class RealEnumerableDrop < Liquid::Drop
100
104
  end
101
105
  end
102
106
 
103
- class DropsTest < Test::Unit::TestCase
107
+ class DropsTest < Minitest::Test
104
108
  include Liquid
105
109
 
106
110
  def test_product_drop
107
- assert_nothing_raised do
108
- tpl = Liquid::Template.parse( ' ' )
111
+ tpl = Liquid::Template.parse(' ')
112
+ assert_equal ' ', tpl.render!('product' => ProductDrop.new)
113
+ end
114
+
115
+ def test_rendering_raises_on_tainted_attr
116
+ with_taint_mode(:error) do
117
+ tpl = Liquid::Template.parse('{{ product.user_input }}')
118
+ assert_raises TaintedError do
119
+ tpl.render!('product' => ProductDrop.new)
120
+ end
121
+ end
122
+ end
123
+
124
+ def test_rendering_warns_on_tainted_attr
125
+ with_taint_mode(:warn) do
126
+ tpl = Liquid::Template.parse('{{ product.user_input }}')
127
+ tpl.render!('product' => ProductDrop.new)
128
+ assert_match /tainted/, tpl.warnings.first
129
+ end
130
+ end
131
+
132
+ def test_rendering_doesnt_raise_on_escaped_tainted_attr
133
+ with_taint_mode(:error) do
134
+ tpl = Liquid::Template.parse('{{ product.user_input | escape }}')
109
135
  tpl.render!('product' => ProductDrop.new)
110
136
  end
111
137
  end
@@ -19,92 +19,189 @@ class ErrorDrop < Liquid::Drop
19
19
 
20
20
  end
21
21
 
22
- class ErrorHandlingTest < Test::Unit::TestCase
22
+ class ErrorHandlingTest < Minitest::Test
23
23
  include Liquid
24
24
 
25
- def test_standard_error
26
- assert_nothing_raised do
27
- template = Liquid::Template.parse( ' {{ errors.standard_error }} ' )
28
- assert_equal ' Liquid error: standard error ', template.render('errors' => ErrorDrop.new)
25
+ def test_templates_parsed_with_line_numbers_renders_them_in_errors
26
+ template = <<-LIQUID
27
+ Hello,
29
28
 
30
- assert_equal 1, template.errors.size
31
- assert_equal StandardError, template.errors.first.class
32
- end
33
- end
29
+ {{ errors.standard_error }} will raise a standard error.
34
30
 
35
- def test_syntax
31
+ Bla bla test.
36
32
 
37
- assert_nothing_raised do
33
+ {{ errors.syntax_error }} will raise a syntax error.
38
34
 
39
- template = Liquid::Template.parse( ' {{ errors.syntax_error }} ' )
40
- assert_equal ' Liquid syntax error: syntax error ', template.render('errors' => ErrorDrop.new)
35
+ This is an argument error: {{ errors.argument_error }}
41
36
 
42
- assert_equal 1, template.errors.size
43
- assert_equal SyntaxError, template.errors.first.class
37
+ Bla.
38
+ LIQUID
44
39
 
45
- end
40
+ expected = <<-TEXT
41
+ Hello,
42
+
43
+ Liquid error (line 3): standard error will raise a standard error.
44
+
45
+ Bla bla test.
46
+
47
+ Liquid syntax error (line 7): syntax error will raise a syntax error.
48
+
49
+ This is an argument error: Liquid error (line 9): argument error
50
+
51
+ Bla.
52
+ TEXT
53
+
54
+ output = Liquid::Template.parse(template, line_numbers: true).render('errors' => ErrorDrop.new)
55
+ assert_equal expected, output
46
56
  end
47
57
 
48
- def test_argument
49
- assert_nothing_raised do
58
+ def test_standard_error
59
+ template = Liquid::Template.parse( ' {{ errors.standard_error }} ' )
60
+ assert_equal ' Liquid error: standard error ', template.render('errors' => ErrorDrop.new)
50
61
 
51
- template = Liquid::Template.parse( ' {{ errors.argument_error }} ' )
52
- assert_equal ' Liquid error: argument error ', template.render('errors' => ErrorDrop.new)
62
+ assert_equal 1, template.errors.size
63
+ assert_equal StandardError, template.errors.first.class
64
+ end
53
65
 
54
- assert_equal 1, template.errors.size
55
- assert_equal ArgumentError, template.errors.first.class
56
- end
66
+ def test_syntax
67
+ template = Liquid::Template.parse( ' {{ errors.syntax_error }} ' )
68
+ assert_equal ' Liquid syntax error: syntax error ', template.render('errors' => ErrorDrop.new)
69
+
70
+ assert_equal 1, template.errors.size
71
+ assert_equal SyntaxError, template.errors.first.class
72
+ end
73
+
74
+ def test_argument
75
+ template = Liquid::Template.parse( ' {{ errors.argument_error }} ' )
76
+ assert_equal ' Liquid error: argument error ', template.render('errors' => ErrorDrop.new)
77
+
78
+ assert_equal 1, template.errors.size
79
+ assert_equal ArgumentError, template.errors.first.class
57
80
  end
58
81
 
59
82
  def test_missing_endtag_parse_time_error
60
- assert_raise(Liquid::SyntaxError) do
83
+ assert_raises(Liquid::SyntaxError) do
61
84
  Liquid::Template.parse(' {% for a in b %} ... ')
62
85
  end
63
86
  end
64
87
 
65
88
  def test_unrecognized_operator
66
89
  with_error_mode(:strict) do
67
- assert_raise(SyntaxError) do
90
+ assert_raises(SyntaxError) do
68
91
  Liquid::Template.parse(' {% if 1 =! 2 %}ok{% endif %} ')
69
92
  end
70
93
  end
71
94
  end
72
-
95
+
73
96
  def test_lax_unrecognized_operator
74
- assert_nothing_raised do
75
- template = Liquid::Template.parse(' {% if 1 =! 2 %}ok{% endif %} ', :error_mode => :lax)
76
- assert_equal ' Liquid error: Unknown operator =! ', template.render
77
- assert_equal 1, template.errors.size
78
- assert_equal Liquid::ArgumentError, template.errors.first.class
97
+ template = Liquid::Template.parse(' {% if 1 =! 2 %}ok{% endif %} ', :error_mode => :lax)
98
+ assert_equal ' Liquid error: Unknown operator =! ', template.render
99
+ assert_equal 1, template.errors.size
100
+ assert_equal Liquid::ArgumentError, template.errors.first.class
101
+ end
102
+
103
+ def test_with_line_numbers_adds_numbers_to_parser_errors
104
+ err = assert_raises(SyntaxError) do
105
+ template = Liquid::Template.parse(%q{
106
+ foobar
107
+
108
+ {% "cat" | foobar %}
109
+
110
+ bla
111
+ },
112
+ :line_numbers => true
113
+ )
114
+ end
115
+
116
+ assert_match /Liquid syntax error \(line 4\)/, err.message
117
+ end
118
+
119
+ def test_parsing_warn_with_line_numbers_adds_numbers_to_lexer_errors
120
+ template = Liquid::Template.parse(%q{
121
+ foobar
122
+
123
+ {% if 1 =! 2 %}ok{% endif %}
124
+
125
+ bla
126
+ },
127
+ :error_mode => :warn,
128
+ :line_numbers => true
129
+ )
130
+
131
+ assert_equal ['Liquid syntax error (line 4): Unexpected character = in "1 =! 2"'],
132
+ template.warnings.map(&:message)
133
+ end
134
+
135
+ def test_parsing_strict_with_line_numbers_adds_numbers_to_lexer_errors
136
+ err = assert_raises(SyntaxError) do
137
+ Liquid::Template.parse(%q{
138
+ foobar
139
+
140
+ {% if 1 =! 2 %}ok{% endif %}
141
+
142
+ bla
143
+ },
144
+ :error_mode => :strict,
145
+ :line_numbers => true
146
+ )
147
+ end
148
+
149
+ assert_equal 'Liquid syntax error (line 4): Unexpected character = in "1 =! 2"', err.message
150
+ end
151
+
152
+ def test_syntax_errors_in_nested_blocks_have_correct_line_number
153
+ err = assert_raises(SyntaxError) do
154
+ Liquid::Template.parse(%q{
155
+ foobar
156
+
157
+ {% if 1 != 2 %}
158
+ {% foo %}
159
+ {% endif %}
160
+
161
+ bla
162
+ },
163
+ :line_numbers => true
164
+ )
79
165
  end
166
+
167
+ assert_equal "Liquid syntax error (line 5): Unknown tag 'foo'", err.message
80
168
  end
81
169
 
82
170
  def test_strict_error_messages
83
- err = assert_raise(SyntaxError) do
171
+ err = assert_raises(SyntaxError) do
84
172
  Liquid::Template.parse(' {% if 1 =! 2 %}ok{% endif %} ', :error_mode => :strict)
85
173
  end
86
- assert_equal 'Unexpected character = in "1 =! 2"', err.message
174
+ assert_equal 'Liquid syntax error: Unexpected character = in "1 =! 2"', err.message
87
175
 
88
- err = assert_raise(SyntaxError) do
176
+ err = assert_raises(SyntaxError) do
89
177
  Liquid::Template.parse('{{%%%}}', :error_mode => :strict)
90
178
  end
91
- assert_equal 'Unexpected character % in "{{%%%}}"', err.message
179
+ assert_equal 'Liquid syntax error: Unexpected character % in "{{%%%}}"', err.message
92
180
  end
93
181
 
94
182
  def test_warnings
95
183
  template = Liquid::Template.parse('{% if ~~~ %}{{%%%}}{% else %}{{ hello. }}{% endif %}', :error_mode => :warn)
96
184
  assert_equal 3, template.warnings.size
97
- assert_equal 'Unexpected character ~ in "~~~"', template.warnings[0].message
98
- assert_equal 'Unexpected character % in "{{%%%}}"', template.warnings[1].message
99
- assert_equal 'Expected id but found end_of_string in "{{ hello. }}"', template.warnings[2].message
185
+ assert_equal 'Unexpected character ~ in "~~~"', template.warnings[0].to_s(false)
186
+ assert_equal 'Unexpected character % in "{{%%%}}"', template.warnings[1].to_s(false)
187
+ assert_equal 'Expected id but found end_of_string in "{{ hello. }}"', template.warnings[2].to_s(false)
100
188
  assert_equal '', template.render
101
189
  end
102
190
 
191
+ def test_warning_line_numbers
192
+ template = Liquid::Template.parse("{% if ~~~ %}\n{{%%%}}{% else %}\n{{ hello. }}{% endif %}", :error_mode => :warn, :line_numbers => true)
193
+ assert_equal 'Liquid syntax error (line 1): Unexpected character ~ in "~~~"', template.warnings[0].message
194
+ assert_equal 'Liquid syntax error (line 2): Unexpected character % in "{{%%%}}"', template.warnings[1].message
195
+ assert_equal 'Liquid syntax error (line 3): Expected id but found end_of_string in "{{ hello. }}"', template.warnings[2].message
196
+ assert_equal 3, template.warnings.size
197
+ assert_equal [1,2,3], template.warnings.map(&:line_number)
198
+ end
199
+
103
200
  # Liquid should not catch Exceptions that are not subclasses of StandardError, like Interrupt and NoMemoryError
104
201
  def test_exceptions_propagate
105
- assert_raise Exception do
106
- template = Liquid::Template.parse( ' {{ errors.exception }} ' )
202
+ assert_raises Exception do
203
+ template = Liquid::Template.parse('{{ errors.exception }}')
107
204
  template.render('errors' => ErrorDrop.new)
108
205
  end
109
206
  end
110
- end # ErrorHandlingTest
207
+ end
@@ -22,7 +22,7 @@ module SubstituteFilter
22
22
  end
23
23
  end
24
24
 
25
- class FiltersTest < Test::Unit::TestCase
25
+ class FiltersTest < Minitest::Test
26
26
  include Liquid
27
27
 
28
28
  def setup
@@ -107,15 +107,15 @@ class FiltersTest < Test::Unit::TestCase
107
107
  end
108
108
  end
109
109
 
110
- class FiltersInTemplate < Test::Unit::TestCase
110
+ class FiltersInTemplate < Minitest::Test
111
111
  include Liquid
112
112
 
113
113
  def test_local_global
114
- Template.register_filter(MoneyFilter)
115
-
116
- assert_equal " 1000$ ", Template.parse("{{1000 | money}}").render!(nil, nil)
117
- assert_equal " 1000$ CAD ", Template.parse("{{1000 | money}}").render!(nil, :filters => CanadianMoneyFilter)
118
- assert_equal " 1000$ CAD ", Template.parse("{{1000 | money}}").render!(nil, :filters => [CanadianMoneyFilter])
114
+ with_global_filter(MoneyFilter) do
115
+ assert_equal " 1000$ ", Template.parse("{{1000 | money}}").render!(nil, nil)
116
+ assert_equal " 1000$ CAD ", Template.parse("{{1000 | money}}").render!(nil, :filters => CanadianMoneyFilter)
117
+ assert_equal " 1000$ CAD ", Template.parse("{{1000 | money}}").render!(nil, :filters => [CanadianMoneyFilter])
118
+ end
119
119
  end
120
120
 
121
121
  def test_local_filter_with_deprecated_syntax
@@ -12,14 +12,12 @@ module CanadianMoneyFilter
12
12
  end
13
13
  end
14
14
 
15
- class HashOrderingTest < Test::Unit::TestCase
15
+ class HashOrderingTest < Minitest::Test
16
16
  include Liquid
17
17
 
18
- def test_global_register_order
19
- Template.register_filter(MoneyFilter)
20
- Template.register_filter(CanadianMoneyFilter)
21
-
22
- assert_equal " 1000$ CAD ", Template.parse("{{1000 | money}}").render(nil, nil)
23
- end
24
-
18
+ def test_global_register_order
19
+ with_global_filter(MoneyFilter, CanadianMoneyFilter) do
20
+ assert_equal " 1000$ CAD ", Template.parse("{{1000 | money}}").render(nil, nil)
21
+ end
22
+ end
25
23
  end
@@ -27,7 +27,7 @@ module FunnyFilter
27
27
 
28
28
  end
29
29
 
30
- class OutputTest < Test::Unit::TestCase
30
+ class OutputTest < Minitest::Test
31
31
  include Liquid
32
32
 
33
33
  def setup
@@ -1,6 +1,6 @@
1
1
  require 'test_helper'
2
2
 
3
- class ParsingQuirksTest < Test::Unit::TestCase
3
+ class ParsingQuirksTest < Minitest::Test
4
4
  include Liquid
5
5
 
6
6
  def test_parsing_css
@@ -9,30 +9,28 @@ class ParsingQuirksTest < Test::Unit::TestCase
9
9
  end
10
10
 
11
11
  def test_raise_on_single_close_bracet
12
- assert_raise(SyntaxError) do
12
+ assert_raises(SyntaxError) do
13
13
  Template.parse("text {{method} oh nos!")
14
14
  end
15
15
  end
16
16
 
17
17
  def test_raise_on_label_and_no_close_bracets
18
- assert_raise(SyntaxError) do
18
+ assert_raises(SyntaxError) do
19
19
  Template.parse("TEST {{ ")
20
20
  end
21
21
  end
22
22
 
23
23
  def test_raise_on_label_and_no_close_bracets_percent
24
- assert_raise(SyntaxError) do
24
+ assert_raises(SyntaxError) do
25
25
  Template.parse("TEST {% ")
26
26
  end
27
27
  end
28
28
 
29
29
  def test_error_on_empty_filter
30
- assert_nothing_raised do
31
- Template.parse("{{test}}")
32
- Template.parse("{{|test}}")
33
- end
30
+ assert Template.parse("{{test}}")
31
+ assert Template.parse("{{|test}}")
34
32
  with_error_mode(:strict) do
35
- assert_raise(SyntaxError) do
33
+ assert_raises(SyntaxError) do
36
34
  Template.parse("{{test |a|b|}}")
37
35
  end
38
36
  end
@@ -40,7 +38,7 @@ class ParsingQuirksTest < Test::Unit::TestCase
40
38
 
41
39
  def test_meaningless_parens_error
42
40
  with_error_mode(:strict) do
43
- assert_raise(SyntaxError) do
41
+ assert_raises(SyntaxError) do
44
42
  markup = "a == 'foo' or (b == 'bar' and c == 'baz') or false"
45
43
  Template.parse("{% if #{markup} %} YES {% endif %}")
46
44
  end
@@ -49,11 +47,11 @@ class ParsingQuirksTest < Test::Unit::TestCase
49
47
 
50
48
  def test_unexpected_characters_syntax_error
51
49
  with_error_mode(:strict) do
52
- assert_raise(SyntaxError) do
50
+ assert_raises(SyntaxError) do
53
51
  markup = "true && false"
54
52
  Template.parse("{% if #{markup} %} YES {% endif %}")
55
53
  end
56
- assert_raise(SyntaxError) do
54
+ assert_raises(SyntaxError) do
57
55
  markup = "false || true"
58
56
  Template.parse("{% if #{markup} %} YES {% endif %}")
59
57
  end
@@ -61,11 +59,9 @@ class ParsingQuirksTest < Test::Unit::TestCase
61
59
  end
62
60
 
63
61
  def test_no_error_on_lax_empty_filter
64
- assert_nothing_raised do
65
- Template.parse("{{test |a|b|}}", :error_mode => :lax)
66
- Template.parse("{{test}}", :error_mode => :lax)
67
- Template.parse("{{|test|}}", :error_mode => :lax)
68
- end
62
+ assert Template.parse("{{test |a|b|}}", :error_mode => :lax)
63
+ assert Template.parse("{{test}}", :error_mode => :lax)
64
+ assert Template.parse("{{|test|}}", :error_mode => :lax)
69
65
  end
70
66
 
71
67
  def test_meaningless_parens_lax
@@ -86,9 +82,35 @@ class ParsingQuirksTest < Test::Unit::TestCase
86
82
  end
87
83
 
88
84
  def test_raise_on_invalid_tag_delimiter
89
- assert_raise(Liquid::SyntaxError) do
85
+ assert_raises(Liquid::SyntaxError) do
90
86
  Template.new.parse('{% end %}')
91
87
  end
92
88
  end
93
89
 
90
+ def test_unanchored_filter_arguments
91
+ with_error_mode(:lax) do
92
+ assert_template_result('hi',"{{ 'hi there' | split$$$:' ' | first }}")
93
+
94
+ assert_template_result('x', "{{ 'X' | downcase) }}")
95
+
96
+ # After the messed up quotes a filter without parameters (reverse) should work
97
+ # but one with parameters (remove) shouldn't be detected.
98
+ assert_template_result('here', "{{ 'hi there' | split:\"t\"\" | reverse | first}}")
99
+ assert_template_result('hi ', "{{ 'hi there' | split:\"t\"\" | remove:\"i\" | first}}")
100
+ end
101
+ end
102
+
103
+ def test_invalid_variables_work
104
+ with_error_mode(:lax) do
105
+ assert_template_result('bar', "{% assign 123foo = 'bar' %}{{ 123foo }}")
106
+ assert_template_result('123', "{% assign 123 = 'bar' %}{{ 123 }}")
107
+ end
108
+ end
109
+
110
+ def test_extra_dots_in_ranges
111
+ with_error_mode(:lax) do
112
+ assert_template_result('12345', "{% for i in (1...5) %}{{ i }}{% endfor %}")
113
+ end
114
+ end
115
+
94
116
  end # ParsingQuirksTest