better_html 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/Rakefile +30 -0
  4. data/lib/better_html.rb +53 -0
  5. data/lib/better_html/better_erb.rb +68 -0
  6. data/lib/better_html/better_erb/erubi_implementation.rb +50 -0
  7. data/lib/better_html/better_erb/erubis_implementation.rb +44 -0
  8. data/lib/better_html/better_erb/runtime_checks.rb +161 -0
  9. data/lib/better_html/better_erb/validated_output_buffer.rb +166 -0
  10. data/lib/better_html/errors.rb +22 -0
  11. data/lib/better_html/helpers.rb +5 -0
  12. data/lib/better_html/html_attributes.rb +26 -0
  13. data/lib/better_html/node_iterator.rb +144 -0
  14. data/lib/better_html/node_iterator/attribute.rb +34 -0
  15. data/lib/better_html/node_iterator/base.rb +27 -0
  16. data/lib/better_html/node_iterator/cdata.rb +8 -0
  17. data/lib/better_html/node_iterator/comment.rb +8 -0
  18. data/lib/better_html/node_iterator/content_node.rb +13 -0
  19. data/lib/better_html/node_iterator/element.rb +26 -0
  20. data/lib/better_html/node_iterator/html_erb.rb +78 -0
  21. data/lib/better_html/node_iterator/html_lodash.rb +101 -0
  22. data/lib/better_html/node_iterator/javascript_erb.rb +60 -0
  23. data/lib/better_html/node_iterator/location.rb +14 -0
  24. data/lib/better_html/node_iterator/text.rb +8 -0
  25. data/lib/better_html/node_iterator/token.rb +8 -0
  26. data/lib/better_html/railtie.rb +7 -0
  27. data/lib/better_html/test_helper/ruby_expr.rb +89 -0
  28. data/lib/better_html/test_helper/safe_erb_tester.rb +202 -0
  29. data/lib/better_html/test_helper/safe_lodash_tester.rb +121 -0
  30. data/lib/better_html/test_helper/safety_tester_base.rb +34 -0
  31. data/lib/better_html/tree.rb +113 -0
  32. data/lib/better_html/version.rb +3 -0
  33. data/lib/tasks/better_html_tasks.rake +4 -0
  34. data/test/better_html/better_erb/implementation_test.rb +402 -0
  35. data/test/better_html/helpers_test.rb +49 -0
  36. data/test/better_html/node_iterator/html_lodash_test.rb +132 -0
  37. data/test/better_html/node_iterator_test.rb +221 -0
  38. data/test/better_html/test_helper/ruby_expr_test.rb +206 -0
  39. data/test/better_html/test_helper/safe_erb_tester_test.rb +358 -0
  40. data/test/better_html/test_helper/safe_lodash_tester_test.rb +80 -0
  41. data/test/better_html/tree_test.rb +110 -0
  42. data/test/dummy/README.rdoc +28 -0
  43. data/test/dummy/Rakefile +6 -0
  44. data/test/dummy/app/assets/javascripts/application.js +13 -0
  45. data/test/dummy/app/assets/stylesheets/application.css +15 -0
  46. data/test/dummy/app/controllers/application_controller.rb +5 -0
  47. data/test/dummy/app/helpers/application_helper.rb +2 -0
  48. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  49. data/test/dummy/bin/bundle +3 -0
  50. data/test/dummy/bin/rails +4 -0
  51. data/test/dummy/bin/rake +4 -0
  52. data/test/dummy/bin/setup +29 -0
  53. data/test/dummy/config.ru +4 -0
  54. data/test/dummy/config/application.rb +26 -0
  55. data/test/dummy/config/boot.rb +5 -0
  56. data/test/dummy/config/database.yml +25 -0
  57. data/test/dummy/config/environment.rb +5 -0
  58. data/test/dummy/config/environments/development.rb +41 -0
  59. data/test/dummy/config/environments/production.rb +79 -0
  60. data/test/dummy/config/environments/test.rb +42 -0
  61. data/test/dummy/config/initializers/assets.rb +11 -0
  62. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  63. data/test/dummy/config/initializers/cookies_serializer.rb +3 -0
  64. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  65. data/test/dummy/config/initializers/inflections.rb +16 -0
  66. data/test/dummy/config/initializers/mime_types.rb +4 -0
  67. data/test/dummy/config/initializers/session_store.rb +3 -0
  68. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  69. data/test/dummy/config/locales/en.yml +23 -0
  70. data/test/dummy/config/routes.rb +56 -0
  71. data/test/dummy/config/secrets.yml +22 -0
  72. data/test/dummy/public/404.html +67 -0
  73. data/test/dummy/public/422.html +67 -0
  74. data/test/dummy/public/500.html +66 -0
  75. data/test/dummy/public/favicon.ico +0 -0
  76. data/test/test_helper.rb +19 -0
  77. metadata +205 -0
@@ -0,0 +1,49 @@
1
+ require 'test_helper'
2
+
3
+ class BetterHtml::HelpersTest < ActiveSupport::TestCase
4
+ include BetterHtml::Helpers
5
+
6
+ test "html_attributes return a HtmlAttributes object" do
7
+ assert_equal BetterHtml::HtmlAttributes, html_attributes(foo: "bar").class
8
+ end
9
+
10
+ test "html_attributes are formatted as string" do
11
+ assert_equal 'foo="bar" baz="qux"',
12
+ html_attributes(foo: "bar", baz: "qux").to_s
13
+ end
14
+
15
+ test "html_attributes keys cannot contain invalid characters" do
16
+ e = assert_raises(ArgumentError) do
17
+ html_attributes("invalid key": "bar", baz: "qux").to_s
18
+ end
19
+ assert_equal "Attribute names must match the pattern /\\A[a-zA-Z0-9\\-\\:]+\\z/", e.message
20
+ end
21
+
22
+ test "#html_attributes does not accept incorrectly escaped html_safe values" do
23
+ e = assert_raises(ArgumentError) do
24
+ html_attributes('something': 'with "> double quote'.html_safe).to_s
25
+ end
26
+ assert_equal "The value provided for attribute 'something' contains a `\"` character which is not allowed. "\
27
+ "Did you call .html_safe without properly escaping this data?", e.message
28
+ end
29
+
30
+ test "#html_attributes accepts correctly escaped html_safe values" do
31
+ assert_equal 'something="with &quot;&gt; double quote"',
32
+ html_attributes('something': CGI.escapeHTML('with "> double quote').html_safe).to_s
33
+ end
34
+
35
+ test "#html_attributes escapes non-html_safe values" do
36
+ assert_equal 'something="with &quot;&gt; double quote"',
37
+ html_attributes('something': 'with "> double quote').to_s
38
+ end
39
+
40
+ test "#html_attributes accepts nil values as value-less attributes" do
41
+ assert_equal 'data-thing data-other-thing',
42
+ html_attributes('data-thing': nil, 'data-other-thing': nil).to_s
43
+ end
44
+
45
+ test "#html_attributes empty string value is output" do
46
+ assert_equal 'data-thing="" data-other-thing=""',
47
+ html_attributes('data-thing': "", 'data-other-thing': "").to_s
48
+ end
49
+ end
@@ -0,0 +1,132 @@
1
+ require 'test_helper'
2
+
3
+ module BetterHtml
4
+ class NodeIterator
5
+ class HtmlLodashTest < ActiveSupport::TestCase
6
+ test "matches text" do
7
+ scanner = BetterHtml::NodeIterator::HtmlLodash.new("just some text")
8
+ assert_equal 1, scanner.tokens.size
9
+ token = scanner.tokens[0]
10
+ assert_equal :text, token.type
11
+ assert_equal "just some text", token.text
12
+ assert_nil token.code
13
+ assert_equal 0, token.location.start
14
+ assert_equal 14, token.location.stop
15
+ assert_equal 1, token.location.line
16
+ assert_equal 0, token.location.column
17
+ end
18
+
19
+ test "matches strings to be escaped" do
20
+ scanner = BetterHtml::NodeIterator::HtmlLodash.new("[%= foo %]")
21
+ assert_equal 1, scanner.tokens.size
22
+ token = scanner.tokens[0]
23
+ assert_equal :expr_literal, token.type
24
+ assert_equal "[%= foo %]", token.text
25
+ assert_equal " foo ", token.code
26
+ assert_equal 0, token.location.start
27
+ assert_equal 10, token.location.stop
28
+ assert_equal 1, token.location.line
29
+ assert_equal 0, token.location.column
30
+ end
31
+
32
+ test "matches interpolate" do
33
+ scanner = BetterHtml::NodeIterator::HtmlLodash.new("[%! foo %]")
34
+ assert_equal 1, scanner.tokens.size
35
+ token = scanner.tokens[0]
36
+ assert_equal :expr_escaped, token.type
37
+ assert_equal "[%! foo %]", token.text
38
+ assert_equal " foo ", token.code
39
+ assert_equal 0, token.location.start
40
+ assert_equal 10, token.location.stop
41
+ assert_equal 1, token.location.line
42
+ assert_equal 0, token.location.column
43
+ end
44
+
45
+ test "matches statement" do
46
+ scanner = BetterHtml::NodeIterator::HtmlLodash.new("[% foo %]")
47
+ assert_equal 1, scanner.tokens.size
48
+ token = scanner.tokens[0]
49
+ assert_equal :stmt, token.type
50
+ assert_equal "[% foo %]", token.text
51
+ assert_equal " foo ", token.code
52
+ assert_equal 0, token.location.start
53
+ assert_equal 9, token.location.stop
54
+ assert_equal 1, token.location.line
55
+ assert_equal 0, token.location.column
56
+ end
57
+
58
+ test "matches text before and after" do
59
+ scanner = BetterHtml::NodeIterator::HtmlLodash.new("before\n[%= foo %]\nafter")
60
+ assert_equal 3, scanner.tokens.size
61
+
62
+ token = scanner.tokens[0]
63
+ assert_equal :text, token.type
64
+ assert_equal "before\n", token.text
65
+ assert_nil token.code
66
+ assert_equal 0, token.location.start
67
+ assert_equal 7, token.location.stop
68
+ assert_equal 1, token.location.line
69
+ assert_equal 0, token.location.column
70
+
71
+ token = scanner.tokens[1]
72
+ assert_equal :expr_literal, token.type
73
+ assert_equal "[%= foo %]", token.text
74
+ assert_equal " foo ", token.code
75
+ assert_equal 7, token.location.start
76
+ assert_equal 17, token.location.stop
77
+ assert_equal 2, token.location.line
78
+ assert_equal 0, token.location.column
79
+
80
+ token = scanner.tokens[2]
81
+ assert_equal :text, token.type
82
+ assert_equal "\nafter", token.text
83
+ assert_nil token.code
84
+ assert_equal 17, token.location.start
85
+ assert_equal 23, token.location.stop
86
+ assert_equal 2, token.location.line
87
+ assert_equal 10, token.location.column
88
+ end
89
+
90
+ test "matches multiple" do
91
+ scanner = BetterHtml::NodeIterator::HtmlLodash.new("[% if() { %][%= foo %][% } %]")
92
+ assert_equal 3, scanner.tokens.size
93
+
94
+ token = scanner.tokens[0]
95
+ assert_equal :stmt, token.type
96
+ assert_equal "[% if() { %]", token.text
97
+ assert_equal " if() { ", token.code
98
+ assert_equal 0, token.location.start
99
+ assert_equal 12, token.location.stop
100
+ assert_equal 1, token.location.line
101
+ assert_equal 0, token.location.column
102
+
103
+ token = scanner.tokens[1]
104
+ assert_equal :expr_literal, token.type
105
+ assert_equal "[%= foo %]", token.text
106
+ assert_equal " foo ", token.code
107
+ assert_equal 12, token.location.start
108
+ assert_equal 22, token.location.stop
109
+ assert_equal 1, token.location.line
110
+ assert_equal 12, token.location.column
111
+
112
+ token = scanner.tokens[2]
113
+ assert_equal :stmt, token.type
114
+ assert_equal "[% } %]", token.text
115
+ assert_equal " } ", token.code
116
+ assert_equal 22, token.location.start
117
+ assert_equal 29, token.location.stop
118
+ assert_equal 1, token.location.line
119
+ assert_equal 22, token.location.column
120
+ end
121
+
122
+ test "parses out html correctly" do
123
+ scanner = BetterHtml::NodeIterator::HtmlLodash.new('<div class="[%= foo %]">')
124
+ assert_equal 9, scanner.tokens.size
125
+ assert_equal [:tag_start, :tag_name, :whitespace, :attribute_name,
126
+ :equal, :attribute_quoted_value_start, :expr_literal,
127
+ :attribute_quoted_value_end, :tag_end], scanner.tokens.map(&:type)
128
+ assert_equal ["<", "div", " ", "class", "=", "\"", "[%= foo %]", "\"", ">"], scanner.tokens.map(&:text)
129
+ end
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,221 @@
1
+ require 'test_helper'
2
+
3
+ module BetterHtml
4
+ class NodeIteratorTest < ActiveSupport::TestCase
5
+ test "consume cdata nodes" do
6
+ tree = BetterHtml::NodeIterator.new("<![CDATA[ foo ]]>")
7
+
8
+ assert_equal 1, tree.nodes.size
9
+ assert_equal BetterHtml::NodeIterator::CData, tree.nodes.first.class
10
+ assert_equal [" foo "], tree.nodes.first.content_parts.map(&:text)
11
+ end
12
+
13
+ test "unterminated cdata nodes are consumed until end" do
14
+ tree = BetterHtml::NodeIterator.new("<![CDATA[ foo")
15
+
16
+ assert_equal 1, tree.nodes.size
17
+ assert_equal BetterHtml::NodeIterator::CData, tree.nodes.first.class
18
+ assert_equal [" foo"], tree.nodes.first.content_parts.map(&:text)
19
+ end
20
+
21
+ test "consume cdata with interpolation" do
22
+ tree = BetterHtml::NodeIterator.new("<![CDATA[ foo <%= bar %> baz ]]>")
23
+
24
+ assert_equal 1, tree.nodes.size
25
+ assert_equal BetterHtml::NodeIterator::CData, tree.nodes.first.class
26
+ assert_equal [" foo ", "<%= bar %>", " baz "], tree.nodes.first.content_parts.map(&:text)
27
+ end
28
+
29
+ test "consume comment nodes" do
30
+ tree = BetterHtml::NodeIterator.new("<!-- foo -->")
31
+
32
+ assert_equal 1, tree.nodes.size
33
+ assert_equal BetterHtml::NodeIterator::Comment, tree.nodes.first.class
34
+ assert_equal [" foo "], tree.nodes.first.content_parts.map(&:text)
35
+ end
36
+
37
+ test "unterminated comment nodes are consumed until end" do
38
+ tree = BetterHtml::NodeIterator.new("<!-- foo")
39
+
40
+ assert_equal 1, tree.nodes.size
41
+ assert_equal BetterHtml::NodeIterator::Comment, tree.nodes.first.class
42
+ assert_equal [" foo"], tree.nodes.first.content_parts.map(&:text)
43
+ end
44
+
45
+ test "consume comment with interpolation" do
46
+ tree = BetterHtml::NodeIterator.new("<!-- foo <%= bar %> baz -->")
47
+
48
+ assert_equal 1, tree.nodes.size
49
+ assert_equal BetterHtml::NodeIterator::Comment, tree.nodes.first.class
50
+ assert_equal [" foo ", "<%= bar %>", " baz "], tree.nodes.first.content_parts.map(&:text)
51
+ end
52
+
53
+ test "consume tag nodes" do
54
+ tree = BetterHtml::NodeIterator.new("<div>")
55
+
56
+ assert_equal 1, tree.nodes.size
57
+ assert_equal BetterHtml::NodeIterator::Element, tree.nodes.first.class
58
+ assert_equal ["div"], tree.nodes.first.name_parts.map(&:text)
59
+ assert_equal false, tree.nodes.first.self_closing?
60
+ end
61
+
62
+ test "consume tag nodes with solidus" do
63
+ tree = BetterHtml::NodeIterator.new("</div>")
64
+
65
+ assert_equal 1, tree.nodes.size
66
+ assert_equal BetterHtml::NodeIterator::Element, tree.nodes.first.class
67
+ assert_equal ["div"], tree.nodes.first.name_parts.map(&:text)
68
+ assert_equal true, tree.nodes.first.closing?
69
+ end
70
+
71
+ test "sets self_closing when appropriate" do
72
+ tree = BetterHtml::NodeIterator.new("<div/>")
73
+
74
+ assert_equal 1, tree.nodes.size
75
+ assert_equal BetterHtml::NodeIterator::Element, tree.nodes.first.class
76
+ assert_equal ["div"], tree.nodes.first.name_parts.map(&:text)
77
+ assert_equal true, tree.nodes.first.self_closing?
78
+ end
79
+
80
+ test "consume tag nodes until name ends" do
81
+ tree = BetterHtml::NodeIterator.new("<div/>")
82
+ assert_equal 1, tree.nodes.size
83
+ assert_equal BetterHtml::NodeIterator::Element, tree.nodes.first.class
84
+ assert_equal ["div"], tree.nodes.first.name_parts.map(&:text)
85
+
86
+ tree = BetterHtml::NodeIterator.new("<div ")
87
+ assert_equal 1, tree.nodes.size
88
+ assert_equal BetterHtml::NodeIterator::Element, tree.nodes.first.class
89
+ assert_equal ["div"], tree.nodes.first.name_parts.map(&:text)
90
+ end
91
+
92
+ test "consume tag nodes with interpolation" do
93
+ tree = BetterHtml::NodeIterator.new("<ns:<%= name %>-thing>")
94
+
95
+ assert_equal 1, tree.nodes.size
96
+ assert_equal BetterHtml::NodeIterator::Element, tree.nodes.first.class
97
+ assert_equal ["ns:", "<%= name %>", "-thing"], tree.nodes.first.name_parts.map(&:text)
98
+ end
99
+
100
+ test "consume tag attributes nodes unquoted value" do
101
+ tree = BetterHtml::NodeIterator.new("<div foo=bar>")
102
+
103
+ assert_equal 1, tree.nodes.size
104
+ tag = tree.nodes.first
105
+ assert_equal BetterHtml::NodeIterator::Element, tag.class
106
+ assert_equal 1, tag.attributes.size
107
+ attribute = tag.attributes.first
108
+ assert_equal BetterHtml::NodeIterator::Attribute, attribute.class
109
+ assert_equal ["foo"], attribute.name_parts.map(&:text)
110
+ assert_equal ["bar"], attribute.value_parts.map(&:text)
111
+ end
112
+
113
+ test "consume attributes without name" do
114
+ tree = BetterHtml::NodeIterator.new("<div 'thing'>")
115
+
116
+ assert_equal 1, tree.nodes.size
117
+ tag = tree.nodes.first
118
+ assert_equal BetterHtml::NodeIterator::Element, tag.class
119
+ assert_equal 1, tag.attributes.size
120
+ attribute = tag.attributes.first
121
+ assert_equal BetterHtml::NodeIterator::Attribute, attribute.class
122
+ assert_predicate attribute.name, :empty?
123
+ assert_equal ["'", "thing", "'"], attribute.value_parts.map(&:text)
124
+ end
125
+
126
+ test "consume tag attributes nodes quoted value" do
127
+ tree = BetterHtml::NodeIterator.new("<div foo=\"bar\">")
128
+
129
+ assert_equal 1, tree.nodes.size
130
+ tag = tree.nodes.first
131
+ assert_equal BetterHtml::NodeIterator::Element, tag.class
132
+ assert_equal 1, tag.attributes.size
133
+ attribute = tag.attributes.first
134
+ assert_equal BetterHtml::NodeIterator::Attribute, attribute.class
135
+ assert_equal ["foo"], attribute.name_parts.map(&:text)
136
+ assert_equal ['"', "bar", '"'], attribute.value_parts.map(&:text)
137
+ end
138
+
139
+ test "consume tag attributes nodes interpolation in name and value" do
140
+ tree = BetterHtml::NodeIterator.new("<div data-<%= foo %>=\"some <%= value %> foo\">")
141
+
142
+ assert_equal 1, tree.nodes.size
143
+ tag = tree.nodes.first
144
+ assert_equal BetterHtml::NodeIterator::Element, tag.class
145
+ assert_equal 1, tag.attributes.size
146
+ attribute = tag.attributes.first
147
+ assert_equal BetterHtml::NodeIterator::Attribute, attribute.class
148
+ assert_equal ["data-", "<%= foo %>"], attribute.name_parts.map(&:text)
149
+ assert_equal ['"', "some ", "<%= value %>", " foo", '"'], attribute.value_parts.map(&:text)
150
+ end
151
+
152
+ test "attributes can be accessed through [] on Element object" do
153
+ tree = BetterHtml::NodeIterator.new("<div foo=\"bar\">")
154
+
155
+ assert_equal 1, tree.nodes.size
156
+ element = tree.nodes.first
157
+ assert_equal BetterHtml::NodeIterator::Element, element.class
158
+ assert_equal 1, element.attributes.size
159
+ assert_nil element['nonexistent']
160
+ refute_nil attribute = element['foo']
161
+ assert_equal BetterHtml::NodeIterator::Attribute, attribute.class
162
+ end
163
+
164
+ test "attribute values can be read unescaped" do
165
+ tree = BetterHtml::NodeIterator.new("<div foo=\"&lt;&quot;&gt;\">")
166
+
167
+ element = tree.nodes.first
168
+ assert_equal 1, element.attributes.size
169
+ attribute = element['foo']
170
+ assert_equal '<">', attribute.unescaped_value
171
+ end
172
+
173
+ test "attribute values does not unescape stuff inside erb" do
174
+ tree = BetterHtml::NodeIterator.new("<div foo=\"&lt;<%= &gt; %>&gt;\">")
175
+
176
+ element = tree.nodes.first
177
+ assert_equal 1, element.attributes.size
178
+ attribute = element['foo']
179
+ assert_equal '<<%= &gt; %>>', attribute.unescaped_value
180
+ end
181
+
182
+ test "consume text nodes" do
183
+ tree = BetterHtml::NodeIterator.new("here is <%= some %> text")
184
+
185
+ assert_equal 1, tree.nodes.size
186
+ assert_equal BetterHtml::NodeIterator::Text, tree.nodes.first.class
187
+ assert_equal ["here is ", "<%= some %>", " text"], tree.nodes.first.content_parts.map(&:text)
188
+ end
189
+
190
+ test "javascript template parsing works" do
191
+ tree = BetterHtml::NodeIterator.new("here is <%= some %> text", template_language: :javascript)
192
+
193
+ assert_equal 1, tree.nodes.size
194
+ assert_equal BetterHtml::NodeIterator::Text, tree.nodes.first.class
195
+ assert_equal ["here is ", "<%= some %>", " text"], tree.nodes.first.content_parts.map(&:text)
196
+ end
197
+
198
+ test "javascript template does not consume html tags" do
199
+ tree = BetterHtml::NodeIterator.new("<div <%= some %> />", template_language: :javascript)
200
+
201
+ assert_equal 1, tree.nodes.size
202
+ assert_equal BetterHtml::NodeIterator::Text, tree.nodes.first.class
203
+ assert_equal ["<div ", "<%= some %>", " />"], tree.nodes.first.content_parts.map(&:text)
204
+ end
205
+
206
+ test "lodash template parsing works" do
207
+ tree = BetterHtml::NodeIterator.new('<div class="[%= foo %]">', template_language: :lodash)
208
+
209
+ assert_equal 1, tree.nodes.size
210
+ node = tree.nodes.first
211
+ assert_equal BetterHtml::NodeIterator::Element, node.class
212
+ assert_equal "div", node.name
213
+ assert_equal 1, node.attributes.size
214
+ attribute = node.attributes.first
215
+ assert_equal "class", attribute.name
216
+ assert_equal [:attribute_quoted_value_start, :expr_literal,
217
+ :attribute_quoted_value_end], attribute.value_parts.map(&:type)
218
+ assert_equal ["\"", "[%= foo %]", "\""], attribute.value_parts.map(&:text)
219
+ end
220
+ end
221
+ end
@@ -0,0 +1,206 @@
1
+ require 'test_helper'
2
+ require 'better_html/test_helper/ruby_expr'
3
+
4
+ module BetterHtml
5
+ module TestHelper
6
+ class RubyExprTest < ActiveSupport::TestCase
7
+ test "simple call" do
8
+ expr = BetterHtml::TestHelper::RubyExpr.new(code: "foo")
9
+ assert_equal 1, expr.calls.size
10
+ assert_nil expr.calls.first.instance
11
+ assert_equal "foo", expr.calls.first.method
12
+ assert_nil expr.calls.first.arguments
13
+ end
14
+
15
+ test "instance call" do
16
+ expr = BetterHtml::TestHelper::RubyExpr.new(code: "foo.bar")
17
+ assert_equal 1, expr.calls.size
18
+ assert_equal [:vcall, [:@ident, "foo", [1, 0]]], expr.calls.first.instance
19
+ assert_equal "bar", expr.calls.first.method
20
+ assert_nil expr.calls.first.arguments
21
+ end
22
+
23
+ test "instance call with arguments" do
24
+ expr = BetterHtml::TestHelper::RubyExpr.new(code: "foo(x).bar")
25
+ assert_equal 1, expr.calls.size
26
+ assert_equal [:method_add_arg, [:fcall, [:@ident, "foo", [1, 0]]], [:arg_paren, [:args_add_block, [[:vcall, [:@ident, "x", [1, 4]]]], false]]], expr.calls.first.instance
27
+ assert_equal "bar", expr.calls.first.method
28
+ assert_nil expr.calls.first.arguments
29
+ end
30
+
31
+ test "instance call with parenthesis" do
32
+ expr = BetterHtml::TestHelper::RubyExpr.new(code: "(foo).bar")
33
+ assert_equal 1, expr.calls.size
34
+ assert_equal [:paren, [[:vcall, [:@ident, "foo", [1, 1]]]]], expr.calls.first.instance
35
+ assert_equal "bar", expr.calls.first.method
36
+ assert_nil expr.calls.first.arguments
37
+ end
38
+
39
+ test "instance call with parenthesis 2" do
40
+ expr = BetterHtml::TestHelper::RubyExpr.new(code: "(foo)")
41
+ assert_equal 1, expr.calls.size
42
+ assert_nil expr.calls.first.instance
43
+ assert_equal "foo", expr.calls.first.method
44
+ assert_nil expr.calls.first.arguments
45
+ end
46
+
47
+ test "command call" do
48
+ expr = BetterHtml::TestHelper::RubyExpr.new(code: "foo bar")
49
+ assert_equal 1, expr.calls.size
50
+ assert_nil expr.calls.first.instance
51
+ assert_equal "foo", expr.calls.first.method
52
+ assert_equal [[:vcall, [:@ident, "bar", [1, 4]]]], expr.calls.first.arguments
53
+ end
54
+
55
+ test "command call with block" do
56
+ expr = BetterHtml::TestHelper::RubyExpr.new(code: "foo bar do")
57
+ assert_equal 1, expr.calls.size
58
+ assert_nil expr.calls.first.instance
59
+ assert_equal "foo", expr.calls.first.method
60
+ assert_equal [[:vcall, [:@ident, "bar", [1, 4]]]], expr.calls.first.arguments
61
+ end
62
+
63
+ test "call with parameters" do
64
+ expr = BetterHtml::TestHelper::RubyExpr.new(code: "foo(bar)")
65
+ assert_equal 1, expr.calls.size
66
+ assert_nil expr.calls.first.instance
67
+ assert_equal "foo", expr.calls.first.method
68
+ assert_equal [[:vcall, [:@ident, "bar", [1, 4]]]], expr.calls.first.arguments
69
+ end
70
+
71
+ test "instance call with parameters" do
72
+ expr = BetterHtml::TestHelper::RubyExpr.new(code: "foo.bar(baz, x)")
73
+ assert_equal 1, expr.calls.size
74
+ assert_equal [:vcall, [:@ident, "foo", [1, 0]]], expr.calls.first.instance
75
+ assert_equal "bar", expr.calls.first.method
76
+ assert_equal [[:vcall, [:@ident, "baz", [1, 8]]], [:vcall, [:@ident, "x", [1, 13]]]], expr.calls.first.arguments
77
+ end
78
+
79
+ test "call with parameters with if conditional modifier" do
80
+ expr = BetterHtml::TestHelper::RubyExpr.new(code: "foo(bar) if something?")
81
+ assert_equal 1, expr.calls.size
82
+ assert_nil expr.calls.first.instance
83
+ assert_equal "foo", expr.calls.first.method
84
+ assert_equal [[:vcall, [:@ident, "bar", [1, 4]]]], expr.calls.first.arguments
85
+ end
86
+
87
+ test "call with parameters with unless conditional modifier" do
88
+ expr = BetterHtml::TestHelper::RubyExpr.new(code: "foo(bar) unless something?")
89
+ assert_equal 1, expr.calls.size
90
+ assert_nil expr.calls.first.instance
91
+ assert_equal "foo", expr.calls.first.method
92
+ assert_equal [[:vcall, [:@ident, "bar", [1, 4]]]], expr.calls.first.arguments
93
+ end
94
+
95
+ test "expression call in ternary" do
96
+ expr = BetterHtml::TestHelper::RubyExpr.new(code: "something? ? foo : bar")
97
+ assert_equal 2, expr.calls.size
98
+
99
+ assert_nil expr.calls.first.instance
100
+ assert_equal "foo", expr.calls.first.method
101
+ assert_nil expr.calls.first.arguments
102
+
103
+ assert_nil expr.calls.last.instance
104
+ assert_equal "bar", expr.calls.last.method
105
+ assert_nil expr.calls.last.arguments
106
+ end
107
+
108
+ test "expression call with args in ternary" do
109
+ expr = BetterHtml::TestHelper::RubyExpr.new(code: "something? ? foo(x) : bar(x)")
110
+ assert_equal 2, expr.calls.size
111
+
112
+ assert_nil expr.calls.first.instance
113
+ assert_equal "foo", expr.calls.first.method
114
+ assert_equal [[:vcall, [:@ident, "x", [1, 17]]]], expr.calls.first.arguments
115
+
116
+ assert_nil expr.calls.last.instance
117
+ assert_equal "bar", expr.calls.last.method
118
+ assert_equal [[:vcall, [:@ident, "x", [1, 26]]]], expr.calls.last.arguments
119
+ end
120
+
121
+ test "string without interpolation" do
122
+ expr = BetterHtml::TestHelper::RubyExpr.new(code: '"foo"')
123
+ assert_equal 0, expr.calls.size
124
+ end
125
+
126
+ test "string with interpolation" do
127
+ expr = BetterHtml::TestHelper::RubyExpr.new(code: '"foo #{bar}"')
128
+ assert_equal 1, expr.calls.size
129
+ assert_nil expr.calls.first.instance
130
+ assert_equal "bar", expr.calls.first.method
131
+ assert_nil expr.calls.first.arguments
132
+ end
133
+
134
+ test "ternary in string with interpolation" do
135
+ expr = BetterHtml::TestHelper::RubyExpr.new(code: '"foo #{foo? ? bar : baz}"')
136
+ assert_equal 2, expr.calls.size
137
+
138
+ assert_nil expr.calls.first.instance
139
+ assert_equal "bar", expr.calls.first.method
140
+ assert_nil expr.calls.first.arguments
141
+
142
+ assert_nil expr.calls.last.instance
143
+ assert_equal "baz", expr.calls.last.method
144
+ assert_nil expr.calls.last.arguments
145
+ end
146
+
147
+ test "assignment to variable" do
148
+ expr = BetterHtml::TestHelper::RubyExpr.new(code: 'x = foo.bar')
149
+ assert_equal 1, expr.calls.size
150
+ assert_equal [:vcall, [:@ident, "foo", [1, 4]]], expr.calls.first.instance
151
+ assert_equal "bar", expr.calls.first.method
152
+ assert_nil expr.calls.first.arguments
153
+ end
154
+
155
+ test "assignment to variable with command call" do
156
+ expr = BetterHtml::TestHelper::RubyExpr.new(code: 'raw x = foo.bar')
157
+ assert_equal 1, expr.calls.size
158
+ assert_nil expr.calls.first.instance
159
+ assert_equal "raw", expr.calls.first.method
160
+ assert_equal [[:assign, [:var_field, [:@ident, "x", [1, 4]]], [:call, [:vcall, [:@ident, "foo", [1, 8]]], :".", [:@ident, "bar", [1, 12]]]]], expr.calls.first.arguments
161
+ end
162
+
163
+ test "assignment with instance call" do
164
+ expr = BetterHtml::TestHelper::RubyExpr.new(code: '(x = foo).bar')
165
+ assert_equal 1, expr.calls.size
166
+ assert_equal [:paren, [[:assign, [:var_field, [:@ident, "x", [1, 1]]], [:vcall, [:@ident, "foo", [1, 5]]]]]], expr.calls.first.instance
167
+ assert_equal "bar", expr.calls.first.method
168
+ assert_nil expr.calls.first.arguments
169
+ end
170
+
171
+ test "assignment to multiple variables" do
172
+ expr = BetterHtml::TestHelper::RubyExpr.new(code: 'x, y = foo.bar')
173
+ assert_equal 1, expr.calls.size
174
+ assert_equal [:vcall, [:@ident, "foo", [1, 7]]], expr.calls.first.instance
175
+ assert_equal "bar", expr.calls.first.method
176
+ assert_nil expr.calls.first.arguments
177
+ end
178
+
179
+ test "safe navigation operator" do
180
+ expr = BetterHtml::TestHelper::RubyExpr.new(code: 'foo&.bar')
181
+ assert_equal 1, expr.calls.size
182
+ assert_equal [:vcall, [:@ident, "foo", [1, 0]]], expr.calls.first.instance
183
+ assert_equal "bar", expr.calls.first.method
184
+ assert_nil expr.calls.first.arguments
185
+ end
186
+
187
+ test "instance variable" do
188
+ expr = BetterHtml::TestHelper::RubyExpr.new(code: '@foo')
189
+ assert_equal 0, expr.calls.size
190
+ end
191
+
192
+ test "instance method on variable" do
193
+ expr = BetterHtml::TestHelper::RubyExpr.new(code: '@foo.bar')
194
+ assert_equal 1, expr.calls.size
195
+ assert_equal [:var_ref, [:@ivar, "@foo", [1, 0]]], expr.calls.first.instance
196
+ assert_equal "bar", expr.calls.first.method
197
+ assert_nil expr.calls.first.arguments
198
+ end
199
+
200
+ test "index into array" do
201
+ expr = BetterHtml::TestHelper::RubyExpr.new(code: 'local_assigns[:text_class] if local_assigns[:text_class]')
202
+ assert_equal 0, expr.calls.size
203
+ end
204
+ end
205
+ end
206
+ end