liquid 5.1.0 → 5.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (98) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +35 -0
  3. data/README.md +4 -4
  4. data/lib/liquid/block_body.rb +6 -6
  5. data/lib/liquid/condition.rb +7 -1
  6. data/lib/liquid/context.rb +6 -2
  7. data/lib/liquid/expression.rb +11 -10
  8. data/lib/liquid/forloop_drop.rb +44 -1
  9. data/lib/liquid/locales/en.yml +6 -5
  10. data/lib/liquid/partial_cache.rb +3 -3
  11. data/lib/liquid/registers.rb +51 -0
  12. data/lib/liquid/standardfilters.rb +463 -75
  13. data/lib/liquid/strainer_factory.rb +15 -10
  14. data/lib/liquid/strainer_template.rb +9 -0
  15. data/lib/liquid/tablerowloop_drop.rb +58 -1
  16. data/lib/liquid/tags/assign.rb +12 -8
  17. data/lib/liquid/tags/break.rb +8 -0
  18. data/lib/liquid/tags/capture.rb +13 -10
  19. data/lib/liquid/tags/case.rb +21 -0
  20. data/lib/liquid/tags/comment.rb +13 -0
  21. data/lib/liquid/tags/continue.rb +8 -9
  22. data/lib/liquid/tags/cycle.rb +12 -11
  23. data/lib/liquid/tags/decrement.rb +16 -17
  24. data/lib/liquid/tags/echo.rb +16 -9
  25. data/lib/liquid/tags/for.rb +22 -43
  26. data/lib/liquid/tags/if.rb +11 -9
  27. data/lib/liquid/tags/include.rb +15 -13
  28. data/lib/liquid/tags/increment.rb +16 -14
  29. data/lib/liquid/tags/inline_comment.rb +43 -0
  30. data/lib/liquid/tags/raw.rb +11 -0
  31. data/lib/liquid/tags/render.rb +29 -4
  32. data/lib/liquid/tags/table_row.rb +22 -0
  33. data/lib/liquid/tags/unless.rb +15 -4
  34. data/lib/liquid/template.rb +2 -3
  35. data/lib/liquid/variable.rb +4 -4
  36. data/lib/liquid/variable_lookup.rb +10 -7
  37. data/lib/liquid/version.rb +1 -1
  38. data/lib/liquid.rb +4 -4
  39. metadata +7 -121
  40. data/lib/liquid/register.rb +0 -6
  41. data/lib/liquid/static_registers.rb +0 -44
  42. data/test/fixtures/en_locale.yml +0 -9
  43. data/test/integration/assign_test.rb +0 -117
  44. data/test/integration/blank_test.rb +0 -109
  45. data/test/integration/block_test.rb +0 -58
  46. data/test/integration/capture_test.rb +0 -58
  47. data/test/integration/context_test.rb +0 -636
  48. data/test/integration/document_test.rb +0 -21
  49. data/test/integration/drop_test.rb +0 -257
  50. data/test/integration/error_handling_test.rb +0 -272
  51. data/test/integration/expression_test.rb +0 -46
  52. data/test/integration/filter_test.rb +0 -189
  53. data/test/integration/hash_ordering_test.rb +0 -25
  54. data/test/integration/output_test.rb +0 -125
  55. data/test/integration/parsing_quirks_test.rb +0 -134
  56. data/test/integration/profiler_test.rb +0 -213
  57. data/test/integration/security_test.rb +0 -89
  58. data/test/integration/standard_filter_test.rb +0 -880
  59. data/test/integration/tag/disableable_test.rb +0 -59
  60. data/test/integration/tag_test.rb +0 -45
  61. data/test/integration/tags/break_tag_test.rb +0 -17
  62. data/test/integration/tags/continue_tag_test.rb +0 -17
  63. data/test/integration/tags/echo_test.rb +0 -13
  64. data/test/integration/tags/for_tag_test.rb +0 -466
  65. data/test/integration/tags/if_else_tag_test.rb +0 -190
  66. data/test/integration/tags/include_tag_test.rb +0 -269
  67. data/test/integration/tags/increment_tag_test.rb +0 -25
  68. data/test/integration/tags/liquid_tag_test.rb +0 -116
  69. data/test/integration/tags/raw_tag_test.rb +0 -34
  70. data/test/integration/tags/render_tag_test.rb +0 -213
  71. data/test/integration/tags/standard_tag_test.rb +0 -303
  72. data/test/integration/tags/statements_test.rb +0 -113
  73. data/test/integration/tags/table_row_test.rb +0 -66
  74. data/test/integration/tags/unless_else_tag_test.rb +0 -28
  75. data/test/integration/template_test.rb +0 -340
  76. data/test/integration/trim_mode_test.rb +0 -563
  77. data/test/integration/variable_test.rb +0 -138
  78. data/test/test_helper.rb +0 -207
  79. data/test/unit/block_unit_test.rb +0 -53
  80. data/test/unit/condition_unit_test.rb +0 -168
  81. data/test/unit/file_system_unit_test.rb +0 -37
  82. data/test/unit/i18n_unit_test.rb +0 -39
  83. data/test/unit/lexer_unit_test.rb +0 -53
  84. data/test/unit/parse_tree_visitor_test.rb +0 -261
  85. data/test/unit/parser_unit_test.rb +0 -84
  86. data/test/unit/partial_cache_unit_test.rb +0 -128
  87. data/test/unit/regexp_unit_test.rb +0 -46
  88. data/test/unit/static_registers_unit_test.rb +0 -156
  89. data/test/unit/strainer_factory_unit_test.rb +0 -100
  90. data/test/unit/strainer_template_unit_test.rb +0 -82
  91. data/test/unit/tag_unit_test.rb +0 -23
  92. data/test/unit/tags/case_tag_unit_test.rb +0 -12
  93. data/test/unit/tags/for_tag_unit_test.rb +0 -15
  94. data/test/unit/tags/if_tag_unit_test.rb +0 -10
  95. data/test/unit/template_factory_unit_test.rb +0 -12
  96. data/test/unit/template_unit_test.rb +0 -87
  97. data/test/unit/tokenizer_unit_test.rb +0 -62
  98. data/test/unit/variable_unit_test.rb +0 -164
@@ -1,189 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'test_helper'
4
-
5
- module MoneyFilter
6
- def money(input)
7
- format(' %d$ ', input)
8
- end
9
-
10
- def money_with_underscore(input)
11
- format(' %d$ ', input)
12
- end
13
- end
14
-
15
- module CanadianMoneyFilter
16
- def money(input)
17
- format(' %d$ CAD ', input)
18
- end
19
- end
20
-
21
- module SubstituteFilter
22
- def substitute(input, params = {})
23
- input.gsub(/%\{(\w+)\}/) { |_match| params[Regexp.last_match(1)] }
24
- end
25
- end
26
-
27
- class FiltersTest < Minitest::Test
28
- include Liquid
29
-
30
- module OverrideObjectMethodFilter
31
- def tap(_input)
32
- "tap overridden"
33
- end
34
- end
35
-
36
- def setup
37
- @context = Context.new
38
- end
39
-
40
- def test_local_filter
41
- @context['var'] = 1000
42
- @context.add_filters(MoneyFilter)
43
-
44
- assert_equal(' 1000$ ', Template.parse("{{var | money}}").render(@context))
45
- end
46
-
47
- def test_underscore_in_filter_name
48
- @context['var'] = 1000
49
- @context.add_filters(MoneyFilter)
50
- assert_equal(' 1000$ ', Template.parse("{{var | money_with_underscore}}").render(@context))
51
- end
52
-
53
- def test_second_filter_overwrites_first
54
- @context['var'] = 1000
55
- @context.add_filters(MoneyFilter)
56
- @context.add_filters(CanadianMoneyFilter)
57
-
58
- assert_equal(' 1000$ CAD ', Template.parse("{{var | money}}").render(@context))
59
- end
60
-
61
- def test_size
62
- @context['var'] = 'abcd'
63
- @context.add_filters(MoneyFilter)
64
-
65
- assert_equal('4', Template.parse("{{var | size}}").render(@context))
66
- end
67
-
68
- def test_join
69
- @context['var'] = [1, 2, 3, 4]
70
-
71
- assert_equal("1 2 3 4", Template.parse("{{var | join}}").render(@context))
72
- end
73
-
74
- def test_sort
75
- @context['value'] = 3
76
- @context['numbers'] = [2, 1, 4, 3]
77
- @context['words'] = ['expected', 'as', 'alphabetic']
78
- @context['arrays'] = ['flower', 'are']
79
- @context['case_sensitive'] = ['sensitive', 'Expected', 'case']
80
-
81
- assert_equal('1 2 3 4', Template.parse("{{numbers | sort | join}}").render(@context))
82
- assert_equal('alphabetic as expected', Template.parse("{{words | sort | join}}").render(@context))
83
- assert_equal('3', Template.parse("{{value | sort}}").render(@context))
84
- assert_equal('are flower', Template.parse("{{arrays | sort | join}}").render(@context))
85
- assert_equal('Expected case sensitive', Template.parse("{{case_sensitive | sort | join}}").render(@context))
86
- end
87
-
88
- def test_sort_natural
89
- @context['words'] = ['case', 'Assert', 'Insensitive']
90
- @context['hashes'] = [{ 'a' => 'A' }, { 'a' => 'b' }, { 'a' => 'C' }]
91
- @context['objects'] = [TestObject.new('A'), TestObject.new('b'), TestObject.new('C')]
92
-
93
- # Test strings
94
- assert_equal('Assert case Insensitive', Template.parse("{{words | sort_natural | join}}").render(@context))
95
-
96
- # Test hashes
97
- assert_equal('A b C', Template.parse("{{hashes | sort_natural: 'a' | map: 'a' | join}}").render(@context))
98
-
99
- # Test objects
100
- assert_equal('A b C', Template.parse("{{objects | sort_natural: 'a' | map: 'a' | join}}").render(@context))
101
- end
102
-
103
- def test_compact
104
- @context['words'] = ['a', nil, 'b', nil, 'c']
105
- @context['hashes'] = [{ 'a' => 'A' }, { 'a' => nil }, { 'a' => 'C' }]
106
- @context['objects'] = [TestObject.new('A'), TestObject.new(nil), TestObject.new('C')]
107
-
108
- # Test strings
109
- assert_equal('a b c', Template.parse("{{words | compact | join}}").render(@context))
110
-
111
- # Test hashes
112
- assert_equal('A C', Template.parse("{{hashes | compact: 'a' | map: 'a' | join}}").render(@context))
113
-
114
- # Test objects
115
- assert_equal('A C', Template.parse("{{objects | compact: 'a' | map: 'a' | join}}").render(@context))
116
- end
117
-
118
- def test_strip_html
119
- @context['var'] = "<b>bla blub</a>"
120
-
121
- assert_equal("bla blub", Template.parse("{{ var | strip_html }}").render(@context))
122
- end
123
-
124
- def test_strip_html_ignore_comments_with_html
125
- @context['var'] = "<!-- split and some <ul> tag --><b>bla blub</a>"
126
-
127
- assert_equal("bla blub", Template.parse("{{ var | strip_html }}").render(@context))
128
- end
129
-
130
- def test_capitalize
131
- @context['var'] = "blub"
132
-
133
- assert_equal("Blub", Template.parse("{{ var | capitalize }}").render(@context))
134
- end
135
-
136
- def test_nonexistent_filter_is_ignored
137
- @context['var'] = 1000
138
-
139
- assert_equal('1000', Template.parse("{{ var | xyzzy }}").render(@context))
140
- end
141
-
142
- def test_filter_with_keyword_arguments
143
- @context['surname'] = 'john'
144
- @context['input'] = 'hello %{first_name}, %{last_name}'
145
- @context.add_filters(SubstituteFilter)
146
- output = Template.parse(%({{ input | substitute: first_name: surname, last_name: 'doe' }})).render(@context)
147
- assert_equal('hello john, doe', output)
148
- end
149
-
150
- def test_override_object_method_in_filter
151
- assert_equal("tap overridden", Template.parse("{{var | tap}}").render!({ 'var' => 1000 }, filters: [OverrideObjectMethodFilter]))
152
-
153
- # tap still treated as a non-existent filter
154
- assert_equal("1000", Template.parse("{{var | tap}}").render!('var' => 1000))
155
- end
156
-
157
- def test_liquid_argument_error
158
- source = "{{ '' | size: 'too many args' }}"
159
- exc = assert_raises(Liquid::ArgumentError) do
160
- Template.parse(source).render!
161
- end
162
- assert_match(/\ALiquid error: wrong number of arguments /, exc.message)
163
- assert_equal(exc.message, Template.parse(source).render)
164
- end
165
- end
166
-
167
- class FiltersInTemplate < Minitest::Test
168
- include Liquid
169
-
170
- def test_local_global
171
- with_global_filter(MoneyFilter) do
172
- assert_equal(" 1000$ ", Template.parse("{{1000 | money}}").render!(nil, nil))
173
- assert_equal(" 1000$ CAD ", Template.parse("{{1000 | money}}").render!(nil, filters: CanadianMoneyFilter))
174
- assert_equal(" 1000$ CAD ", Template.parse("{{1000 | money}}").render!(nil, filters: [CanadianMoneyFilter]))
175
- end
176
- end
177
-
178
- def test_local_filter_with_deprecated_syntax
179
- assert_equal(" 1000$ CAD ", Template.parse("{{1000 | money}}").render!(nil, CanadianMoneyFilter))
180
- assert_equal(" 1000$ CAD ", Template.parse("{{1000 | money}}").render!(nil, [CanadianMoneyFilter]))
181
- end
182
- end # FiltersTest
183
-
184
- class TestObject < Liquid::Drop
185
- attr_accessor :a
186
- def initialize(a)
187
- @a = a
188
- end
189
- end
@@ -1,25 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'test_helper'
4
-
5
- class HashOrderingTest < Minitest::Test
6
- module MoneyFilter
7
- def money(input)
8
- format(' %d$ ', input)
9
- end
10
- end
11
-
12
- module CanadianMoneyFilter
13
- def money(input)
14
- format(' %d$ CAD ', input)
15
- end
16
- end
17
-
18
- include Liquid
19
-
20
- def test_global_register_order
21
- with_global_filter(MoneyFilter, CanadianMoneyFilter) do
22
- assert_equal(" 1000$ CAD ", Template.parse("{{1000 | money}}").render(nil, nil))
23
- end
24
- end
25
- end
@@ -1,125 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'test_helper'
4
-
5
- module FunnyFilter
6
- def make_funny(_input)
7
- 'LOL'
8
- end
9
-
10
- def cite_funny(input)
11
- "LOL: #{input}"
12
- end
13
-
14
- def add_smiley(input, smiley = ":-)")
15
- "#{input} #{smiley}"
16
- end
17
-
18
- def add_tag(input, tag = "p", id = "foo")
19
- %(<#{tag} id="#{id}">#{input}</#{tag}>)
20
- end
21
-
22
- def paragraph(input)
23
- "<p>#{input}</p>"
24
- end
25
-
26
- def link_to(name, url)
27
- %(<a href="#{url}">#{name}</a>)
28
- end
29
- end
30
-
31
- class OutputTest < Minitest::Test
32
- include Liquid
33
-
34
- def setup
35
- @assigns = {
36
- 'best_cars' => 'bmw',
37
- 'car' => { 'bmw' => 'good', 'gm' => 'bad' },
38
- }
39
- end
40
-
41
- def test_variable
42
- text = %( {{best_cars}} )
43
-
44
- expected = %( bmw )
45
- assert_equal(expected, Template.parse(text).render!(@assigns))
46
- end
47
-
48
- def test_variable_traversing_with_two_brackets
49
- text = %({{ site.data.menu[include.menu][include.locale] }})
50
- assert_equal("it works!", Template.parse(text).render!(
51
- "site" => { "data" => { "menu" => { "foo" => { "bar" => "it works!" } } } },
52
- "include" => { "menu" => "foo", "locale" => "bar" }
53
- ))
54
- end
55
-
56
- def test_variable_traversing
57
- text = %( {{car.bmw}} {{car.gm}} {{car.bmw}} )
58
-
59
- expected = %( good bad good )
60
- assert_equal(expected, Template.parse(text).render!(@assigns))
61
- end
62
-
63
- def test_variable_piping
64
- text = %( {{ car.gm | make_funny }} )
65
- expected = %( LOL )
66
-
67
- assert_equal(expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter]))
68
- end
69
-
70
- def test_variable_piping_with_input
71
- text = %( {{ car.gm | cite_funny }} )
72
- expected = %( LOL: bad )
73
-
74
- assert_equal(expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter]))
75
- end
76
-
77
- def test_variable_piping_with_args
78
- text = %! {{ car.gm | add_smiley : ':-(' }} !
79
- expected = %| bad :-( |
80
-
81
- assert_equal(expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter]))
82
- end
83
-
84
- def test_variable_piping_with_no_args
85
- text = %( {{ car.gm | add_smiley }} )
86
- expected = %| bad :-) |
87
-
88
- assert_equal(expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter]))
89
- end
90
-
91
- def test_multiple_variable_piping_with_args
92
- text = %! {{ car.gm | add_smiley : ':-(' | add_smiley : ':-('}} !
93
- expected = %| bad :-( :-( |
94
-
95
- assert_equal(expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter]))
96
- end
97
-
98
- def test_variable_piping_with_multiple_args
99
- text = %( {{ car.gm | add_tag : 'span', 'bar'}} )
100
- expected = %( <span id="bar">bad</span> )
101
-
102
- assert_equal(expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter]))
103
- end
104
-
105
- def test_variable_piping_with_variable_args
106
- text = %( {{ car.gm | add_tag : 'span', car.bmw}} )
107
- expected = %( <span id="good">bad</span> )
108
-
109
- assert_equal(expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter]))
110
- end
111
-
112
- def test_multiple_pipings
113
- text = %( {{ best_cars | cite_funny | paragraph }} )
114
- expected = %( <p>LOL: bmw</p> )
115
-
116
- assert_equal(expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter]))
117
- end
118
-
119
- def test_link_to
120
- text = %( {{ 'Typo' | link_to: 'http://typo.leetsoft.com' }} )
121
- expected = %( <a href="http://typo.leetsoft.com">Typo</a> )
122
-
123
- assert_equal(expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter]))
124
- end
125
- end # OutputTest
@@ -1,134 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'test_helper'
4
-
5
- class ParsingQuirksTest < Minitest::Test
6
- include Liquid
7
-
8
- def test_parsing_css
9
- text = " div { font-weight: bold; } "
10
- assert_equal(text, Template.parse(text).render!)
11
- end
12
-
13
- def test_raise_on_single_close_bracet
14
- assert_raises(SyntaxError) do
15
- Template.parse("text {{method} oh nos!")
16
- end
17
- end
18
-
19
- def test_raise_on_label_and_no_close_bracets
20
- assert_raises(SyntaxError) do
21
- Template.parse("TEST {{ ")
22
- end
23
- end
24
-
25
- def test_raise_on_label_and_no_close_bracets_percent
26
- assert_raises(SyntaxError) do
27
- Template.parse("TEST {% ")
28
- end
29
- end
30
-
31
- def test_error_on_empty_filter
32
- assert(Template.parse("{{test}}"))
33
-
34
- with_error_mode(:lax) do
35
- assert(Template.parse("{{|test}}"))
36
- end
37
-
38
- with_error_mode(:strict) do
39
- assert_raises(SyntaxError) { Template.parse("{{|test}}") }
40
- assert_raises(SyntaxError) { Template.parse("{{test |a|b|}}") }
41
- end
42
- end
43
-
44
- def test_meaningless_parens_error
45
- with_error_mode(:strict) do
46
- assert_raises(SyntaxError) do
47
- markup = "a == 'foo' or (b == 'bar' and c == 'baz') or false"
48
- Template.parse("{% if #{markup} %} YES {% endif %}")
49
- end
50
- end
51
- end
52
-
53
- def test_unexpected_characters_syntax_error
54
- with_error_mode(:strict) do
55
- assert_raises(SyntaxError) do
56
- markup = "true && false"
57
- Template.parse("{% if #{markup} %} YES {% endif %}")
58
- end
59
- assert_raises(SyntaxError) do
60
- markup = "false || true"
61
- Template.parse("{% if #{markup} %} YES {% endif %}")
62
- end
63
- end
64
- end
65
-
66
- def test_no_error_on_lax_empty_filter
67
- assert(Template.parse("{{test |a|b|}}", error_mode: :lax))
68
- assert(Template.parse("{{test}}", error_mode: :lax))
69
- assert(Template.parse("{{|test|}}", error_mode: :lax))
70
- end
71
-
72
- def test_meaningless_parens_lax
73
- with_error_mode(:lax) do
74
- assigns = { 'b' => 'bar', 'c' => 'baz' }
75
- markup = "a == 'foo' or (b == 'bar' and c == 'baz') or false"
76
- assert_template_result(' YES ', "{% if #{markup} %} YES {% endif %}", assigns)
77
- end
78
- end
79
-
80
- def test_unexpected_characters_silently_eat_logic_lax
81
- with_error_mode(:lax) do
82
- markup = "true && false"
83
- assert_template_result(' YES ', "{% if #{markup} %} YES {% endif %}")
84
- markup = "false || true"
85
- assert_template_result('', "{% if #{markup} %} YES {% endif %}")
86
- end
87
- end
88
-
89
- def test_raise_on_invalid_tag_delimiter
90
- assert_raises(Liquid::SyntaxError) do
91
- Template.new.parse('{% end %}')
92
- end
93
- end
94
-
95
- def test_unanchored_filter_arguments
96
- with_error_mode(:lax) do
97
- assert_template_result('hi', "{{ 'hi there' | split$$$:' ' | first }}")
98
-
99
- assert_template_result('x', "{{ 'X' | downcase) }}")
100
-
101
- # After the messed up quotes a filter without parameters (reverse) should work
102
- # but one with parameters (remove) shouldn't be detected.
103
- assert_template_result('here', "{{ 'hi there' | split:\"t\"\" | reverse | first}}")
104
- assert_template_result('hi ', "{{ 'hi there' | split:\"t\"\" | remove:\"i\" | first}}")
105
- end
106
- end
107
-
108
- def test_invalid_variables_work
109
- with_error_mode(:lax) do
110
- assert_template_result('bar', "{% assign 123foo = 'bar' %}{{ 123foo }}")
111
- assert_template_result('123', "{% assign 123 = 'bar' %}{{ 123 }}")
112
- end
113
- end
114
-
115
- def test_extra_dots_in_ranges
116
- with_error_mode(:lax) do
117
- assert_template_result('12345', "{% for i in (1...5) %}{{ i }}{% endfor %}")
118
- end
119
- end
120
-
121
- def test_blank_variable_markup
122
- assert_template_result('', "{{}}")
123
- end
124
-
125
- def test_lookup_on_var_with_literal_name
126
- assigns = { "blank" => { "x" => "result" } }
127
- assert_template_result('result', "{{ blank.x }}", assigns)
128
- assert_template_result('result', "{{ blank['x'] }}", assigns)
129
- end
130
-
131
- def test_contains_in_id
132
- assert_template_result(' YES ', '{% if containsallshipments == true %} YES {% endif %}', 'containsallshipments' => true)
133
- end
134
- end # ParsingQuirksTest
@@ -1,213 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'test_helper'
4
-
5
- class ProfilerTest < Minitest::Test
6
- include Liquid
7
-
8
- class ProfilingFileSystem
9
- def read_template_file(template_path)
10
- "Rendering template {% assign template_name = '#{template_path}'%}\n{{ template_name }}"
11
- end
12
- end
13
-
14
- def setup
15
- Liquid::Template.file_system = ProfilingFileSystem.new
16
- end
17
-
18
- def test_template_allows_flagging_profiling
19
- t = Template.parse("{{ 'a string' | upcase }}")
20
- t.render!
21
-
22
- assert_nil(t.profiler)
23
- end
24
-
25
- def test_parse_makes_available_simple_profiling
26
- t = Template.parse("{{ 'a string' | upcase }}", profile: true)
27
- t.render!
28
-
29
- assert_equal(1, t.profiler.length)
30
-
31
- node = t.profiler[0]
32
- assert_equal(" 'a string' | upcase ", node.code)
33
- end
34
-
35
- def test_render_ignores_raw_strings_when_profiling
36
- t = Template.parse("This is raw string\nstuff\nNewline", profile: true)
37
- t.render!
38
-
39
- assert_equal(0, t.profiler.length)
40
- end
41
-
42
- def test_profiling_includes_line_numbers_of_liquid_nodes
43
- t = Template.parse("{{ 'a string' | upcase }}\n{% increment test %}", profile: true)
44
- t.render!
45
- assert_equal(2, t.profiler.length)
46
-
47
- # {{ 'a string' | upcase }}
48
- assert_equal(1, t.profiler[0].line_number)
49
- # {{ increment test }}
50
- assert_equal(2, t.profiler[1].line_number)
51
- end
52
-
53
- def test_profiling_includes_line_numbers_of_included_partials
54
- t = Template.parse("{% include 'a_template' %}", profile: true)
55
- t.render!
56
-
57
- included_children = t.profiler[0].children
58
-
59
- # {% assign template_name = 'a_template' %}
60
- assert_equal(1, included_children[0].line_number)
61
- # {{ template_name }}
62
- assert_equal(2, included_children[1].line_number)
63
- end
64
-
65
- def test_profiling_render_tag
66
- t = Template.parse("{% render 'a_template' %}", profile: true)
67
- t.render!
68
-
69
- render_children = t.profiler[0].children
70
- render_children.each do |timing|
71
- assert_equal('a_template', timing.partial)
72
- end
73
- assert_equal([1, 2], render_children.map(&:line_number))
74
- end
75
-
76
- def test_profiling_times_the_rendering_of_tokens
77
- t = Template.parse("{% include 'a_template' %}", profile: true)
78
- t.render!
79
-
80
- node = t.profiler[0]
81
- refute_nil(node.render_time)
82
- end
83
-
84
- def test_profiling_times_the_entire_render
85
- t = Template.parse("{% include 'a_template' %}", profile: true)
86
- t.render!
87
-
88
- assert(t.profiler.total_render_time >= 0, "Total render time was not calculated")
89
- end
90
-
91
- class SleepTag < Liquid::Tag
92
- def initialize(tag_name, markup, parse_context)
93
- super
94
- @duration = Float(markup)
95
- end
96
-
97
- def render_to_output_buffer(_context, _output)
98
- sleep(@duration)
99
- end
100
- end
101
-
102
- def test_profiling_multiple_renders
103
- with_custom_tag('sleep', SleepTag) do
104
- context = Liquid::Context.new
105
- t = Liquid::Template.parse("{% sleep 0.001 %}", profile: true)
106
- context.template_name = 'index'
107
- t.render!(context)
108
- context.template_name = 'layout'
109
- first_render_time = context.profiler.total_time
110
- t.render!(context)
111
-
112
- profiler = context.profiler
113
- children = profiler.children
114
- assert_operator(first_render_time, :>=, 0.001)
115
- assert_operator(profiler.total_time, :>=, 0.001 + first_render_time)
116
- assert_equal(["index", "layout"], children.map(&:template_name))
117
- assert_equal([nil, nil], children.map(&:code))
118
- assert_equal(profiler.total_time, children.map(&:total_time).reduce(&:+))
119
- end
120
- end
121
-
122
- def test_profiling_uses_include_to_mark_children
123
- t = Template.parse("{{ 'a string' | upcase }}\n{% include 'a_template' %}", profile: true)
124
- t.render!
125
-
126
- include_node = t.profiler[1]
127
- assert_equal(2, include_node.children.length)
128
- end
129
-
130
- def test_profiling_marks_children_with_the_name_of_included_partial
131
- t = Template.parse("{{ 'a string' | upcase }}\n{% include 'a_template' %}", profile: true)
132
- t.render!
133
-
134
- include_node = t.profiler[1]
135
- include_node.children.each do |child|
136
- assert_equal("a_template", child.partial)
137
- end
138
- end
139
-
140
- def test_profiling_supports_multiple_templates
141
- t = Template.parse("{{ 'a string' | upcase }}\n{% include 'a_template' %}\n{% include 'b_template' %}", profile: true)
142
- t.render!
143
-
144
- a_template = t.profiler[1]
145
- a_template.children.each do |child|
146
- assert_equal("a_template", child.partial)
147
- end
148
-
149
- b_template = t.profiler[2]
150
- b_template.children.each do |child|
151
- assert_equal("b_template", child.partial)
152
- end
153
- end
154
-
155
- def test_profiling_supports_rendering_the_same_partial_multiple_times
156
- t = Template.parse("{{ 'a string' | upcase }}\n{% include 'a_template' %}\n{% include 'a_template' %}", profile: true)
157
- t.render!
158
-
159
- a_template1 = t.profiler[1]
160
- a_template1.children.each do |child|
161
- assert_equal("a_template", child.partial)
162
- end
163
-
164
- a_template2 = t.profiler[2]
165
- a_template2.children.each do |child|
166
- assert_equal("a_template", child.partial)
167
- end
168
- end
169
-
170
- def test_can_iterate_over_each_profiling_entry
171
- t = Template.parse("{{ 'a string' | upcase }}\n{% increment test %}", profile: true)
172
- t.render!
173
-
174
- timing_count = 0
175
- t.profiler.each do |_timing|
176
- timing_count += 1
177
- end
178
-
179
- assert_equal(2, timing_count)
180
- end
181
-
182
- def test_profiling_marks_children_of_if_blocks
183
- t = Template.parse("{% if true %} {% increment test %} {{ test }} {% endif %}", profile: true)
184
- t.render!
185
-
186
- assert_equal(1, t.profiler.length)
187
- assert_equal(2, t.profiler[0].children.length)
188
- end
189
-
190
- def test_profiling_marks_children_of_for_blocks
191
- t = Template.parse("{% for item in collection %} {{ item }} {% endfor %}", profile: true)
192
- t.render!("collection" => ["one", "two"])
193
-
194
- assert_equal(1, t.profiler.length)
195
- # Will profile each invocation of the for block
196
- assert_equal(2, t.profiler[0].children.length)
197
- end
198
-
199
- def test_profiling_supports_self_time
200
- t = Template.parse("{% for item in collection %} {{ item }} {% endfor %}", profile: true)
201
- t.render!("collection" => ["one", "two"])
202
- leaf = t.profiler[0].children[0]
203
-
204
- assert_operator(leaf.self_time, :>, 0)
205
- end
206
-
207
- def test_profiling_supports_total_time
208
- t = Template.parse("{% if true %} {% increment test %} {{ test }} {% endif %}", profile: true)
209
- t.render!
210
-
211
- assert_operator(t.profiler[0].total_time, :>, 0)
212
- end
213
- end