better_html 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/Rakefile +30 -0
- data/lib/better_html.rb +53 -0
- data/lib/better_html/better_erb.rb +68 -0
- data/lib/better_html/better_erb/erubi_implementation.rb +50 -0
- data/lib/better_html/better_erb/erubis_implementation.rb +44 -0
- data/lib/better_html/better_erb/runtime_checks.rb +161 -0
- data/lib/better_html/better_erb/validated_output_buffer.rb +166 -0
- data/lib/better_html/errors.rb +22 -0
- data/lib/better_html/helpers.rb +5 -0
- data/lib/better_html/html_attributes.rb +26 -0
- data/lib/better_html/node_iterator.rb +144 -0
- data/lib/better_html/node_iterator/attribute.rb +34 -0
- data/lib/better_html/node_iterator/base.rb +27 -0
- data/lib/better_html/node_iterator/cdata.rb +8 -0
- data/lib/better_html/node_iterator/comment.rb +8 -0
- data/lib/better_html/node_iterator/content_node.rb +13 -0
- data/lib/better_html/node_iterator/element.rb +26 -0
- data/lib/better_html/node_iterator/html_erb.rb +78 -0
- data/lib/better_html/node_iterator/html_lodash.rb +101 -0
- data/lib/better_html/node_iterator/javascript_erb.rb +60 -0
- data/lib/better_html/node_iterator/location.rb +14 -0
- data/lib/better_html/node_iterator/text.rb +8 -0
- data/lib/better_html/node_iterator/token.rb +8 -0
- data/lib/better_html/railtie.rb +7 -0
- data/lib/better_html/test_helper/ruby_expr.rb +89 -0
- data/lib/better_html/test_helper/safe_erb_tester.rb +202 -0
- data/lib/better_html/test_helper/safe_lodash_tester.rb +121 -0
- data/lib/better_html/test_helper/safety_tester_base.rb +34 -0
- data/lib/better_html/tree.rb +113 -0
- data/lib/better_html/version.rb +3 -0
- data/lib/tasks/better_html_tasks.rake +4 -0
- data/test/better_html/better_erb/implementation_test.rb +402 -0
- data/test/better_html/helpers_test.rb +49 -0
- data/test/better_html/node_iterator/html_lodash_test.rb +132 -0
- data/test/better_html/node_iterator_test.rb +221 -0
- data/test/better_html/test_helper/ruby_expr_test.rb +206 -0
- data/test/better_html/test_helper/safe_erb_tester_test.rb +358 -0
- data/test/better_html/test_helper/safe_lodash_tester_test.rb +80 -0
- data/test/better_html/tree_test.rb +110 -0
- data/test/dummy/README.rdoc +28 -0
- data/test/dummy/Rakefile +6 -0
- data/test/dummy/app/assets/javascripts/application.js +13 -0
- data/test/dummy/app/assets/stylesheets/application.css +15 -0
- data/test/dummy/app/controllers/application_controller.rb +5 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/bin/bundle +3 -0
- data/test/dummy/bin/rails +4 -0
- data/test/dummy/bin/rake +4 -0
- data/test/dummy/bin/setup +29 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/config/application.rb +26 -0
- data/test/dummy/config/boot.rb +5 -0
- data/test/dummy/config/database.yml +25 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +41 -0
- data/test/dummy/config/environments/production.rb +79 -0
- data/test/dummy/config/environments/test.rb +42 -0
- data/test/dummy/config/initializers/assets.rb +11 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/test/dummy/config/initializers/inflections.rb +16 -0
- data/test/dummy/config/initializers/mime_types.rb +4 -0
- data/test/dummy/config/initializers/session_store.rb +3 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +23 -0
- data/test/dummy/config/routes.rb +56 -0
- data/test/dummy/config/secrets.yml +22 -0
- data/test/dummy/public/404.html +67 -0
- data/test/dummy/public/422.html +67 -0
- data/test/dummy/public/500.html +66 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/test_helper.rb +19 -0
- 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 "> 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 "> 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=\"<">\">")
|
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=\"<<%= > %>>\">")
|
175
|
+
|
176
|
+
element = tree.nodes.first
|
177
|
+
assert_equal 1, element.attributes.size
|
178
|
+
attribute = element['foo']
|
179
|
+
assert_equal '<<%= > %>>', 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
|