fast_haml 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +16 -0
- data/.gitmodules +3 -0
- data/.rspec +2 -0
- data/.travis.yml +25 -0
- data/Appraisals +26 -0
- data/CHANGELOG.md +2 -0
- data/Gemfile +8 -0
- data/LICENSE.txt +22 -0
- data/README.md +96 -0
- data/Rakefile +11 -0
- data/benchmark/rendering.rb +29 -0
- data/bin/fast_haml +4 -0
- data/ext/attribute_builder/attribute_builder.c +259 -0
- data/ext/attribute_builder/extconf.rb +3 -0
- data/fast_haml.gemspec +35 -0
- data/gemfiles/rails_4.0.gemfile +9 -0
- data/gemfiles/rails_4.1.gemfile +9 -0
- data/gemfiles/rails_4.2.gemfile +9 -0
- data/gemfiles/rails_edge.gemfile +10 -0
- data/haml_spec_test.rb +22 -0
- data/lib/fast_haml/ast.rb +112 -0
- data/lib/fast_haml/cli.rb +38 -0
- data/lib/fast_haml/compiler.rb +325 -0
- data/lib/fast_haml/element_parser.rb +288 -0
- data/lib/fast_haml/engine.rb +32 -0
- data/lib/fast_haml/filter_compilers/base.rb +29 -0
- data/lib/fast_haml/filter_compilers/cdata.rb +15 -0
- data/lib/fast_haml/filter_compilers/css.rb +15 -0
- data/lib/fast_haml/filter_compilers/escaped.rb +22 -0
- data/lib/fast_haml/filter_compilers/javascript.rb +15 -0
- data/lib/fast_haml/filter_compilers/plain.rb +17 -0
- data/lib/fast_haml/filter_compilers/preserve.rb +30 -0
- data/lib/fast_haml/filter_compilers/ruby.rb +13 -0
- data/lib/fast_haml/filter_compilers.rb +37 -0
- data/lib/fast_haml/filter_parser.rb +54 -0
- data/lib/fast_haml/html.rb +44 -0
- data/lib/fast_haml/indent_tracker.rb +84 -0
- data/lib/fast_haml/line_parser.rb +66 -0
- data/lib/fast_haml/parser.rb +251 -0
- data/lib/fast_haml/parser_utils.rb +17 -0
- data/lib/fast_haml/rails_handler.rb +10 -0
- data/lib/fast_haml/railtie.rb +8 -0
- data/lib/fast_haml/ruby_multiline.rb +23 -0
- data/lib/fast_haml/static_hash_parser.rb +113 -0
- data/lib/fast_haml/syntax_error.rb +10 -0
- data/lib/fast_haml/text_compiler.rb +69 -0
- data/lib/fast_haml/tilt.rb +16 -0
- data/lib/fast_haml/version.rb +3 -0
- data/lib/fast_haml.rb +10 -0
- data/spec/rails/Rakefile +6 -0
- data/spec/rails/app/assets/images/.keep +0 -0
- data/spec/rails/app/assets/javascripts/application.js +13 -0
- data/spec/rails/app/assets/stylesheets/application.css +15 -0
- data/spec/rails/app/controllers/application_controller.rb +5 -0
- data/spec/rails/app/controllers/books_controller.rb +8 -0
- data/spec/rails/app/controllers/concerns/.keep +0 -0
- data/spec/rails/app/helpers/application_helper.rb +2 -0
- data/spec/rails/app/mailers/.keep +0 -0
- data/spec/rails/app/models/.keep +0 -0
- data/spec/rails/app/models/book.rb +9 -0
- data/spec/rails/app/models/concerns/.keep +0 -0
- data/spec/rails/app/views/books/hello.html.haml +2 -0
- data/spec/rails/app/views/books/with_capture.html.haml +4 -0
- data/spec/rails/app/views/books/with_variables.html.haml +4 -0
- data/spec/rails/app/views/layouts/application.html.haml +9 -0
- data/spec/rails/bin/bundle +3 -0
- data/spec/rails/bin/rails +4 -0
- data/spec/rails/bin/rake +4 -0
- data/spec/rails/bin/setup +29 -0
- data/spec/rails/config/application.rb +12 -0
- data/spec/rails/config/boot.rb +3 -0
- data/spec/rails/config/database.yml +25 -0
- data/spec/rails/config/environment.rb +5 -0
- data/spec/rails/config/environments/development.rb +41 -0
- data/spec/rails/config/environments/production.rb +79 -0
- data/spec/rails/config/environments/test.rb +42 -0
- data/spec/rails/config/initializers/assets.rb +11 -0
- data/spec/rails/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/rails/config/initializers/cookies_serializer.rb +3 -0
- data/spec/rails/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/rails/config/initializers/inflections.rb +16 -0
- data/spec/rails/config/initializers/mime_types.rb +4 -0
- data/spec/rails/config/initializers/secret_key_base.rb +6 -0
- data/spec/rails/config/initializers/session_store.rb +3 -0
- data/spec/rails/config/initializers/wrap_parameters.rb +14 -0
- data/spec/rails/config/locales/en.yml +23 -0
- data/spec/rails/config/routes.rb +7 -0
- data/spec/rails/config/secrets.yml +22 -0
- data/spec/rails/config.ru +4 -0
- data/spec/rails/db/seeds.rb +7 -0
- data/spec/rails/lib/assets/.keep +0 -0
- data/spec/rails/lib/tasks/.keep +0 -0
- data/spec/rails/log/.keep +0 -0
- data/spec/rails/public/404.html +67 -0
- data/spec/rails/public/422.html +67 -0
- data/spec/rails/public/500.html +66 -0
- data/spec/rails/public/favicon.ico +0 -0
- data/spec/rails/public/robots.txt +5 -0
- data/spec/rails/spec/requests/fast_haml_spec.rb +41 -0
- data/spec/rails/vendor/assets/javascripts/.keep +0 -0
- data/spec/rails/vendor/assets/stylesheets/.keep +0 -0
- data/spec/rails_helper.rb +4 -0
- data/spec/render/attribute_spec.rb +209 -0
- data/spec/render/comment_spec.rb +61 -0
- data/spec/render/doctype_spec.rb +62 -0
- data/spec/render/element_spec.rb +165 -0
- data/spec/render/filters/cdata_spec.rb +12 -0
- data/spec/render/filters/css_spec.rb +45 -0
- data/spec/render/filters/escaped_spec.rb +14 -0
- data/spec/render/filters/javascript_spec.rb +44 -0
- data/spec/render/filters/plain_spec.rb +24 -0
- data/spec/render/filters/preserve_spec.rb +25 -0
- data/spec/render/filters/ruby_spec.rb +13 -0
- data/spec/render/filters_spec.rb +11 -0
- data/spec/render/haml_comment_spec.rb +24 -0
- data/spec/render/multiline_spec.rb +39 -0
- data/spec/render/plain_spec.rb +20 -0
- data/spec/render/preserve_spec.rb +8 -0
- data/spec/render/sanitize_spec.rb +36 -0
- data/spec/render/script_spec.rb +74 -0
- data/spec/render/silent_script_spec.rb +97 -0
- data/spec/render/unescape_spec.rb +40 -0
- data/spec/spec_helper.rb +49 -0
- data/spec/tilt_spec.rb +33 -0
- metadata +427 -0
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'rails_helper'
|
2
|
+
|
3
|
+
RSpec.describe 'FastHaml with Rails', type: :request do
|
4
|
+
it 'renders views' do
|
5
|
+
get '/books/hello'
|
6
|
+
expect(response).to be_ok
|
7
|
+
expect(response).to render_template('books/hello')
|
8
|
+
expect(response).to render_template('layouts/application')
|
9
|
+
expect(response.body).to include('<span>Hello, World</span>')
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'renders views with variables' do
|
13
|
+
get '/books/with_variables?title=nanika'
|
14
|
+
expect(response).to be_ok
|
15
|
+
expect(response).to render_template('books/with_variables')
|
16
|
+
expect(response).to render_template('layouts/application')
|
17
|
+
expect(response.body).to include('<p>nanika</p>')
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'escapes non-html_safe string' do
|
21
|
+
uri = URI.parse('/books/with_variables')
|
22
|
+
uri.query = URI.encode_www_form(title: '<script>alert(1)</script>')
|
23
|
+
get uri.to_s
|
24
|
+
expect(response).to be_ok
|
25
|
+
expect(response).to render_template('books/with_variables')
|
26
|
+
expect(response).to render_template('layouts/application')
|
27
|
+
expect(response.body).to include('<a href="/books/hello">hello</a>')
|
28
|
+
expect(response.body).to include('<p><script>alert(1)</script></p>')
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'does not escape object which returns html_safe string by to_s' do
|
32
|
+
get '/books/with_variables?title=nanika'
|
33
|
+
expect(response.body).to include('<span>nanika</span>')
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'works with capture method' do
|
37
|
+
get '/books/with_capture'
|
38
|
+
expect(response).to be_ok
|
39
|
+
expect(response.body).to include("<div><div>\n<p>Hello</p>\n</div>\n</div>\n")
|
40
|
+
end
|
41
|
+
end
|
File without changes
|
File without changes
|
@@ -0,0 +1,209 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe 'Attributes rendering', type: :render do
|
4
|
+
it 'parses attributes' do
|
5
|
+
expect(render_string('%span{class: "x"} hello')).to eq(%Q{<span class='x'>hello</span>\n})
|
6
|
+
end
|
7
|
+
|
8
|
+
it 'parses attributes' do
|
9
|
+
expect(render_string('%span{class: "x", "old" => 2} hello')).to eq(%Q{<span class='x' old='2'>hello</span>\n})
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'is not element with id attribute' do
|
13
|
+
expect(render_string('#{1 + 2}')).to eq("3\n")
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'renders dynamic attributes' do
|
17
|
+
expect(render_string(%q|%span#main{class: "na#{'ni'}ka"} hello|)).to eq(%Q{<span class='nanika' id='main'>hello</span>\n})
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'renders array class' do
|
21
|
+
expect(render_string('%span.c2{class: "c1"}')).to eq("<span class='c1 c2'></span>\n")
|
22
|
+
expect(render_string('%span.c2{class: ["c1", "c3"]}')).to eq("<span class='c1 c2 c3'></span>\n")
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'merges classes' do
|
26
|
+
expect(render_string(<<HAML)).to eq("<span class='c1 c2 content {}' id='main_id1_id3_id2'>hello</span>\n")
|
27
|
+
- h1 = {class: 'c1', id: ['id1', 'id3']}
|
28
|
+
- h2 = {class: [{}, 'c2'], id: 'id2'}
|
29
|
+
%span#main.content{h1, h2} hello
|
30
|
+
HAML
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'escapes' do
|
34
|
+
expect(render_string(%q|%span{class: "x\"y'z"} hello|)).to eq(%Q{<span class='x"y'z'>hello</span>\n})
|
35
|
+
end
|
36
|
+
|
37
|
+
it "doesn't parse extra brace" do
|
38
|
+
expect(render_string('%span{foo: 1}{bar: 2}')).to eq("<span foo='1'>{bar: 2}</span>\n")
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'renders only name if value is true' do
|
42
|
+
expect(render_string(%q|%span{foo: true, bar: 1} hello|)).to eq(%Q{<span bar='1' foo>hello</span>\n})
|
43
|
+
end
|
44
|
+
|
45
|
+
context 'with xhtml format' do
|
46
|
+
it 'renders name="name" if value is true' do
|
47
|
+
expect(render_string(%q|%span{foo: true, bar: 1} hello|, format: :xhtml)).to eq(%Q{<span bar='1' foo='foo'>hello</span>\n})
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'renders nested attributes' do
|
52
|
+
expect(render_string(%q|%span{foo: {bar: 1+2}} hello|)).to eq(%Q|<span foo='{:bar=>3}'>hello</span>\n|)
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'renders code attributes' do
|
56
|
+
expect(render_string(<<HAML)).to eq(%Q|<span bar='{:hoge=>:fuga}' baz foo='1'>hello</span>\n|)
|
57
|
+
- attrs = { foo: 1, bar: { hoge: :fuga }, baz: true }
|
58
|
+
%span{attrs} hello
|
59
|
+
HAML
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'renders dstr attributes' do
|
63
|
+
expect(render_string(<<HAML)).to eq(%Q|<span data='x{:foo=>1}y'>hello</span>\n|)
|
64
|
+
- data = { foo: 1 }
|
65
|
+
%span{data: "x\#{data}y"} hello
|
66
|
+
HAML
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'renders nested dstr attributes' do
|
70
|
+
expect(render_string(<<'HAML')).to eq(%Q|<span foo='{:bar=>"x1y"}'>hello</span>\n|)
|
71
|
+
- data = { foo: 1 }
|
72
|
+
%span{foo: {bar: "x#{1}y"}} hello
|
73
|
+
HAML
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'optimize send case' do
|
77
|
+
expect(render_string('%span{foo: {bar: 1+2}} hello')).to eq("<span foo='{:bar=>3}'>hello</span>\n")
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'merges static id' do
|
81
|
+
expect(render_string('#foo{id: "bar"} baz')).to eq("<div id='foo_bar'>baz</div>\n")
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'merges static class' do
|
85
|
+
expect(render_string('.foo{class: "bar"} baz')).to eq("<div class='bar foo'>baz</div>\n")
|
86
|
+
expect(render_string(<<'HAML')).to eq("<div class='bar foo'>baz</div>\n")
|
87
|
+
- bar = 'bar'
|
88
|
+
.foo{class: "#{bar}"} baz
|
89
|
+
HAML
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'converts underscore to hyphen in data attributes' do
|
93
|
+
expect(render_string("%span{data: {foo_bar: 'baz'}}")).to eq("<span data-foo-bar='baz'></span>\n")
|
94
|
+
expect(render_string("- h = {foo_bar: 'baz'}\n%span{data: h}")).to eq("<span data-foo-bar='baz'></span>\n")
|
95
|
+
end
|
96
|
+
|
97
|
+
context 'with unmatched brace' do
|
98
|
+
it 'raises error' do
|
99
|
+
expect { render_string('%span{foo hello') }.to raise_error(FastHaml::SyntaxError)
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'tries to parse next lines' do
|
103
|
+
expect(render_string(<<HAML)).to eq("<span bar='2' foo='1'>hello</span>\n")
|
104
|
+
%span{foo: 1,
|
105
|
+
bar: 2} hello
|
106
|
+
HAML
|
107
|
+
end
|
108
|
+
|
109
|
+
it "doesn't try to parse next lines without trailing comma" do
|
110
|
+
expect { render_string(<<HAML) }.to raise_error(FastHaml::SyntaxError)
|
111
|
+
%span{foo: 1
|
112
|
+
, bar: 2} hello
|
113
|
+
HAML
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
context 'with data attributes' do
|
118
|
+
it 'renders nested attributes' do
|
119
|
+
expect(render_string(%q|%span{data: {foo: 1, bar: 'baz', :hoge => :fuga, k1: { k2: 'v3' }}} hello|)).to eq(%Q{<span data-bar='baz' data-foo='1' data-hoge='fuga' data-k1-k2='v3'>hello</span>\n})
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'renders nested dynamic attributes' do
|
123
|
+
expect(render_string(%q|%span{data: {foo: "b#{'a'}r"}} hello|)).to eq(%Q{<span data-foo='bar'>hello</span>\n})
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'renders nested attributes' do
|
127
|
+
expect(render_string(%q|%span{data: {foo: 1, bar: 2+3}} hello|)).to eq(%Q{<span data-bar='5' data-foo='1'>hello</span>\n})
|
128
|
+
end
|
129
|
+
|
130
|
+
it 'renders nested code attributes' do
|
131
|
+
expect(render_string(<<HAML)).to eq(%Q{<span data-bar='2' data-foo='1'>hello</span>\n})
|
132
|
+
- data = { foo: 1, bar: 2 }
|
133
|
+
%span{data: data} hello
|
134
|
+
HAML
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
describe 'with HTML-style attributes' do
|
139
|
+
it 'parses simple values' do
|
140
|
+
expect(render_string('%span(foo=1 bar=3) hello')).to eq("<span bar='3' foo='1'>hello</span>\n")
|
141
|
+
end
|
142
|
+
|
143
|
+
it 'parses variables' do
|
144
|
+
expect(render_string(<<HAML)).to eq("<span bar='3' foo='xxx'>hello</span>\n")
|
145
|
+
- foo = 'xxx'
|
146
|
+
%span(foo=foo bar=3) hello
|
147
|
+
HAML
|
148
|
+
end
|
149
|
+
|
150
|
+
it 'parses attributes with old syntax' do
|
151
|
+
expect(render_string(<<HAML)).to eq("<span bar='3' foo='foo'>hello</span>\n")
|
152
|
+
- foo = 'foo'
|
153
|
+
%span(foo=foo){bar: 3} hello
|
154
|
+
HAML
|
155
|
+
end
|
156
|
+
|
157
|
+
it 'parses multiline attribute list' do
|
158
|
+
expect(render_string(<<HAML)).to eq("<span bar='3' foo='1'>hello</span>\n")
|
159
|
+
%span(foo=1
|
160
|
+
|
161
|
+
bar=3) hello
|
162
|
+
HAML
|
163
|
+
end
|
164
|
+
|
165
|
+
it "doesn't parse extra parens" do
|
166
|
+
expect(render_string('%span(foo=1)(bar=3) hello')).to eq("<span foo='1'>(bar=3) hello</span>\n")
|
167
|
+
end
|
168
|
+
|
169
|
+
it 'parses quoted value' do
|
170
|
+
expect(render_string('%span(foo=1 bar="baz") hello')).to eq("<span bar='baz' foo='1'>hello</span>\n")
|
171
|
+
expect(render_string("%span(foo=1 bar='baz') hello")).to eq("<span bar='baz' foo='1'>hello</span>\n")
|
172
|
+
end
|
173
|
+
|
174
|
+
it 'parses key-only attribute' do
|
175
|
+
expect(render_string('%span(foo bar=1) hello')).to eq("<span bar='1' foo>hello</span>\n")
|
176
|
+
end
|
177
|
+
|
178
|
+
it 'renders string interpolation' do
|
179
|
+
expect(render_string(%q|%span(foo=1 bar="baz#{1 + 2}") hello|)).to eq("<span bar='baz3' foo='1'>hello</span>\n")
|
180
|
+
expect(render_string(%q|%span(foo=1 bar='baz#{1 + 2}') hello|)).to eq("<span bar='baz3' foo='1'>hello</span>\n")
|
181
|
+
end
|
182
|
+
|
183
|
+
|
184
|
+
it 'parses escapes' do
|
185
|
+
expect(render_string(%q|%span(foo=1 bar="ba\"z") hello|)).to eq("<span bar='ba"z' foo='1'>hello</span>\n")
|
186
|
+
expect(render_string(%q|%span(foo=1 bar='ba\'z') hello|)).to eq("<span bar='ba'z' foo='1'>hello</span>\n")
|
187
|
+
end
|
188
|
+
|
189
|
+
it 'raises error when attributes list is unterminated' do
|
190
|
+
expect { render_string('%span(foo=1 bar=2') }.to raise_error(FastHaml::SyntaxError)
|
191
|
+
end
|
192
|
+
|
193
|
+
it 'raises error when key is not alnum' do
|
194
|
+
expect { render_string('%span(foo=1 3.14=3) hello') }.to raise_error(FastHaml::SyntaxError)
|
195
|
+
end
|
196
|
+
|
197
|
+
it 'raises error when value is missing' do
|
198
|
+
expect { render_string('%span(foo=1 bar=) hello') }.to raise_error(FastHaml::SyntaxError)
|
199
|
+
end
|
200
|
+
|
201
|
+
it 'raises error when quote is unterminated' do
|
202
|
+
expect { render_string('%span(foo=1 bar="baz) hello') }.to raise_error(FastHaml::SyntaxError)
|
203
|
+
end
|
204
|
+
|
205
|
+
it 'raises error when string interpolation is unterminated' do
|
206
|
+
expect { render_string('%span(foo=1 bar="ba#{1") hello') }.to raise_error(FastHaml::SyntaxError)
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe 'Comment rendering', type: :render do
|
4
|
+
it 'renders html comment' do
|
5
|
+
expect(render_string('/ comments')).to eq("<!-- comments -->\n")
|
6
|
+
end
|
7
|
+
|
8
|
+
it 'strips spaces' do
|
9
|
+
expect(render_string('/ comments ')).to eq("<!-- comments -->\n")
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'renders structured comment' do
|
13
|
+
expect(render_string(<<HAML)).to eq("<span>hello</span>\n<!--\ngreat\n-->\n<span>world</span>\n")
|
14
|
+
%span hello
|
15
|
+
/
|
16
|
+
great
|
17
|
+
%span world
|
18
|
+
HAML
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'renders comment with interpolation' do
|
22
|
+
expect(render_string(<<'HAML')).to eq("<span>hello</span>\n<!--\ngreat\n-->\n<span>world</span>\n")
|
23
|
+
- comment = 'great'
|
24
|
+
%span hello
|
25
|
+
/
|
26
|
+
#{comment}
|
27
|
+
%span world
|
28
|
+
HAML
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'renders conditional comment' do
|
32
|
+
expect(render_string('/ [if IE] hello')).to eq("<!--[if IE]> hello <![endif]-->\n")
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'renders conditional comment with children' do
|
36
|
+
expect(render_string(<<HAML)).to eq("<!--[if IE]>\n<span>hello</span>\nworld\n<![endif]-->\n")
|
37
|
+
/[if IE]
|
38
|
+
%span hello
|
39
|
+
world
|
40
|
+
HAML
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'parses nested conditional comment' do
|
44
|
+
expect(render_string(<<HAML)).to eq("<!--[[if IE]]>\n<span>hello</span>\nworld\n<![endif]-->\n")
|
45
|
+
/[[if IE]]
|
46
|
+
%span hello
|
47
|
+
world
|
48
|
+
HAML
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'raises error if conditional comment bracket is unbalanced' do
|
52
|
+
expect { render_string('/[[if IE]') }.to raise_error(FastHaml::SyntaxError)
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'raises error if both comment text and children are given' do
|
56
|
+
expect { render_string(<<HAML) }.to raise_error(FastHaml::SyntaxError)
|
57
|
+
/ hehehe
|
58
|
+
%span hello
|
59
|
+
HAML
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe 'Doctype rendering', type: :render do
|
4
|
+
it 'ignores 1 or 2 exclamation marks' do
|
5
|
+
expect(render_string('!')).to eq("!\n")
|
6
|
+
expect(render_string('!!')).to eq("!!\n")
|
7
|
+
end
|
8
|
+
|
9
|
+
context 'with html format' do
|
10
|
+
it 'renders html5 doctype by default' do
|
11
|
+
expect(render_string('!!!')).to eq("<!DOCTYPE html>\n")
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'renders xml doctype (silent)' do
|
15
|
+
expect(render_string('!!! xml')).to eq("\n")
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context 'with html4 format' do
|
20
|
+
it 'renders transitional doctype by default' do
|
21
|
+
expect(render_string('!!!', format: :html4)).to eq(%Q|<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">\n|)
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'renders frameset doctype' do
|
25
|
+
expect(render_string('!!! frameset', format: :html4)).to eq(%Q|<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">\n|)
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'renders strict doctype' do
|
29
|
+
expect(render_string('!!! strict', format: :html4)).to eq(%Q|<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">\n|)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'with xhtml format' do
|
34
|
+
it 'renders transitional doctype by default' do
|
35
|
+
expect(render_string('!!!', format: :xhtml)).to eq(%Q|<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\n|)
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'renders xml doctype' do
|
39
|
+
expect(render_string('!!! xml', format: :xhtml)).to eq("<?xml version='1.0' encoding='utf-8' ?>\n")
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'renders xhtml 1.1 doctype' do
|
43
|
+
expect(render_string('!!! 1.1', format: :xhtml)).to eq(%Q|<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">\n|)
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'renders xhtml mobile doctype' do
|
47
|
+
expect(render_string('!!! mobile', format: :xhtml)).to eq(%Q|<!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.2//EN" "http://www.openmobilealliance.org/tech/DTD/xhtml-mobile12.dtd">\n|)
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'renders xhtml basic doctype' do
|
51
|
+
expect(render_string('!!! basic', format: :xhtml)).to eq(%Q|<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML Basic 1.1//EN" "http://www.w3.org/TR/xhtml-basic/xhtml-basic11.dtd">\n|)
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'renders xhtml frameset doctype' do
|
55
|
+
expect(render_string('!!! frameset', format: :xhtml)).to eq(%Q|<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">\n|)
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'renders html 5 doctype with xhtml syntax' do
|
59
|
+
expect(render_string('!!! 5', format: :xhtml)).to eq(%Q|<!DOCTYPE html>\n|)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,165 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe 'Element rendering', type: :render do
|
4
|
+
it 'parses one-line element' do
|
5
|
+
expect(render_string('%span hello')).to eq("<span>hello</span>\n")
|
6
|
+
end
|
7
|
+
|
8
|
+
it 'parses multi-line element' do
|
9
|
+
expect(render_string(<<HAML)).to eq("<span>\nhello\n</span>\n")
|
10
|
+
%span
|
11
|
+
hello
|
12
|
+
HAML
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'parses nested elements' do
|
16
|
+
expect(render_string(<<HAML)).to eq("<span>\n<b>\nhello\n</b>\n<i>\n<small>world</small>\n</i>\n</span>\n")
|
17
|
+
%span
|
18
|
+
%b
|
19
|
+
hello
|
20
|
+
%i
|
21
|
+
%small world
|
22
|
+
HAML
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'parses multi-line texts' do
|
26
|
+
expect(render_string(<<HAML)).to eq("<span>\n<b>\nhello\nworld\n</b>\n</span>\n")
|
27
|
+
%span
|
28
|
+
%b
|
29
|
+
hello
|
30
|
+
world
|
31
|
+
HAML
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'skips empty lines' do
|
35
|
+
expect(render_string(<<HAML)).to eq("<span>\n<b>\nhello\n</b>\n</span>\n")
|
36
|
+
%span
|
37
|
+
|
38
|
+
%b
|
39
|
+
|
40
|
+
hello
|
41
|
+
|
42
|
+
HAML
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'raises error if indent is wrong' do
|
46
|
+
expect { render_string(<<HAML) }.to raise_error(FastHaml::IndentTracker::IndentMismatch)
|
47
|
+
%div
|
48
|
+
%div
|
49
|
+
%div
|
50
|
+
%div
|
51
|
+
HAML
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'parses classes' do
|
55
|
+
expect(render_string('%span.foo.bar hello')).to eq(%Q{<span class='foo bar'>hello</span>\n})
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'parses id' do
|
59
|
+
expect(render_string('%span#foo-bar hello')).to eq(%Q{<span id='foo-bar'>hello</span>\n})
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'parses classes and id' do
|
63
|
+
expect(render_string('%span.foo#foo-bar.bar hello')).to eq(%Q{<span class='foo bar' id='foo-bar'>hello</span>\n})
|
64
|
+
end
|
65
|
+
|
66
|
+
context 'with invalid tag name' do
|
67
|
+
it 'raises error' do
|
68
|
+
expect { render_string('%.foo') }.to raise_error(FastHaml::SyntaxError)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'parses #' do
|
73
|
+
expect(render_string('#main')).to eq(%Q{<div id='main'></div>\n})
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'parses .' do
|
77
|
+
expect(render_string('.wrapper.main')).to eq(%Q{<div class='wrapper main'></div>\n})
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'parses Ruby multiline' do
|
81
|
+
expect(render_string(<<HAML)).to eq("<div>\n<span>2+3i</span>\n</div>\n")
|
82
|
+
%div
|
83
|
+
%span= Complex(2,
|
84
|
+
3)
|
85
|
+
HAML
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'parses string interpolation' do
|
89
|
+
expect(render_string(%q|%span hello <span> #{'</span>'} </span>|)).to eq("<span>hello <span> </span> </span></span>\n")
|
90
|
+
expect(render_string(<<'HAML')).to eq("<span>\nhello <span> </span> </span>\n</span>\n")
|
91
|
+
%span
|
92
|
+
hello <span> #{{text: '</span>'}[:text]} </span>
|
93
|
+
HAML
|
94
|
+
expect(render_string(<<'HAML')).to eq("<span>\nhello <span> </span> </span>\n</span>\n")
|
95
|
+
- @var = '</span>'
|
96
|
+
%span
|
97
|
+
hello <span> #@var </span>
|
98
|
+
HAML
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'parses string interpolation with multibyte characters' do
|
102
|
+
expect(render_string(%q|#{'日本語'} にほんご|)).to eq("日本語 にほんご\n")
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'recognized escaped string interpolation' do
|
106
|
+
expect(render_string(%q|%p hello \#{1 + 2}|)).to eq("<p>hello \#{1 + 2}</p>\n")
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'parses self-closing tag' do
|
110
|
+
expect(render_string('%p/')).to eq("<p>\n")
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'renders some attributes as self-closing by default' do
|
114
|
+
expect(render_string('%meta{"http-equiv" => "Content-Type", :content => "text/html"}')).to eq("<meta content='text/html' http-equiv='Content-Type'>\n")
|
115
|
+
end
|
116
|
+
|
117
|
+
it 'parses nuke-inner-whitespace (<)' do
|
118
|
+
expect(render_string(<<HAML)).to eq("<blockquote><div>\nFoo!\n</div></blockquote>\n")
|
119
|
+
%blockquote<
|
120
|
+
%div
|
121
|
+
Foo!
|
122
|
+
HAML
|
123
|
+
end
|
124
|
+
|
125
|
+
it 'renders pre tag as nuke-inner-whitespace by default' do
|
126
|
+
expect(render_string(<<HAML)).to eq("<pre>hello\nworld</pre>\n")
|
127
|
+
%pre
|
128
|
+
hello
|
129
|
+
world
|
130
|
+
HAML
|
131
|
+
end
|
132
|
+
|
133
|
+
it 'parses nuke-outer-whitespace (>)' do
|
134
|
+
expect(render_string(<<HAML)).to eq("<img><span>hello</span><img>\n")
|
135
|
+
%img
|
136
|
+
%span> hello
|
137
|
+
%img
|
138
|
+
HAML
|
139
|
+
end
|
140
|
+
|
141
|
+
it 'parses nuke-whitespaces' do
|
142
|
+
expect(render_string(<<HAML)).to eq("<img><pre>foo\nbar</pre><img>\n")
|
143
|
+
%img
|
144
|
+
%pre><
|
145
|
+
foo
|
146
|
+
bar
|
147
|
+
%img
|
148
|
+
HAML
|
149
|
+
end
|
150
|
+
|
151
|
+
it 'parses == syntax' do
|
152
|
+
expect(render_string('%p== =#{1+2}hello')).to eq("<p>=3hello</p>\n")
|
153
|
+
end
|
154
|
+
|
155
|
+
it 'raises error if self-closing tag have text' do
|
156
|
+
expect { render_string('%p/ hello') }.to raise_error(FastHaml::SyntaxError)
|
157
|
+
end
|
158
|
+
|
159
|
+
it 'raises error if self-closing tag have children' do
|
160
|
+
expect { render_string(<<HAML) }.to raise_error(FastHaml::SyntaxError)
|
161
|
+
%p/
|
162
|
+
hello
|
163
|
+
HAML
|
164
|
+
end
|
165
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe 'CDATA filter rendering', type: :render do
|
4
|
+
it 'renders CDATA filter' do
|
5
|
+
expect(render_string(<<'HAML')).to eq("<![CDATA[\n hello\n world\n <span>hello</span>\n]]>\n")
|
6
|
+
:cdata
|
7
|
+
hello
|
8
|
+
#{'world'}
|
9
|
+
<span>hello</span>
|
10
|
+
HAML
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe 'CSS filter rendering', type: :render do
|
4
|
+
it 'renders css filter' do
|
5
|
+
expect(render_string(<<HAML)).to eq("<div>\n<style>\n html { font-size: 12px; }\n</style>\n<span>hello</span>\n</div>\n")
|
6
|
+
%div
|
7
|
+
:css
|
8
|
+
html { font-size: 12px; }
|
9
|
+
%span hello
|
10
|
+
HAML
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'keeps indent' do
|
14
|
+
expect(render_string(<<HAML)).to eq("<div>\n<style>\n html {\n font-size: 12px;\n }\n</style>\n<span>hello</span>\n</div>\n")
|
15
|
+
%div
|
16
|
+
:css
|
17
|
+
html {
|
18
|
+
font-size: 12px;
|
19
|
+
}
|
20
|
+
%span hello
|
21
|
+
HAML
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'ignores empty filter' do
|
25
|
+
expect(render_string(<<HAML)).to eq("<div>\n<span>hello</span>\n</div>\n")
|
26
|
+
%div
|
27
|
+
:css
|
28
|
+
%span hello
|
29
|
+
HAML
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'parses string interpolation' do
|
33
|
+
expect(render_string(<<'HAML')).to eq("<style>\n html { font-size: 12px; }\n</style>\n")
|
34
|
+
:css
|
35
|
+
html { font-size: #{10 + 2}px; }
|
36
|
+
HAML
|
37
|
+
end
|
38
|
+
|
39
|
+
it "doesn't escape in string interpolation" do
|
40
|
+
expect(render_string(<<'HAML')).to eq("<style>\n <span/>\n</style>\n")
|
41
|
+
:css
|
42
|
+
#{'<span/>'}
|
43
|
+
HAML
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe 'Escaped filter rendering', type: :render do
|
4
|
+
it' renders escaped filter' do
|
5
|
+
expect(render_string(<<'HAML')).to eq("<span>start</span>\nhello\n <p>world</p>\n<span>hello</span>\n\n<span>end</span>\n")
|
6
|
+
%span start
|
7
|
+
:escaped
|
8
|
+
hello
|
9
|
+
#{'<p>world</p>'}
|
10
|
+
<span>hello</span>
|
11
|
+
%span end
|
12
|
+
HAML
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe 'JavaScript filter renderiong', type: :render do
|
4
|
+
it 'renders javascript filter' do
|
5
|
+
expect(render_string(<<HAML)).to eq("<div>\n<script>\n alert('hello');\n</script>\n<span>world</span>\n</div>\n")
|
6
|
+
%div
|
7
|
+
:javascript
|
8
|
+
alert('hello');
|
9
|
+
%span world
|
10
|
+
HAML
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'keeps indent' do
|
14
|
+
expect(render_string(<<HAML)).to eq("<div>\n<script>\n alert('hello');\n \n alert('world');\n</script>\n</div>\n")
|
15
|
+
%div
|
16
|
+
:javascript
|
17
|
+
alert('hello');
|
18
|
+
|
19
|
+
alert('world');
|
20
|
+
HAML
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'ignores empty filter' do
|
24
|
+
expect(render_string(<<HAML)).to eq("<div>\n<span>world</span>\n</div>\n")
|
25
|
+
%div
|
26
|
+
:javascript
|
27
|
+
%span world
|
28
|
+
HAML
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'parses string interpolation' do
|
32
|
+
expect(render_string(<<'HAML')).to eq("<script>\n var x = 3;\n</script>\n")
|
33
|
+
:javascript
|
34
|
+
var x = #{1 + 2};
|
35
|
+
HAML
|
36
|
+
end
|
37
|
+
|
38
|
+
it "doesn't escape in string interpolation" do
|
39
|
+
expect(render_string(<<'HAML')).to eq("<script>\n <span/>\n</script>\n")
|
40
|
+
:javascript
|
41
|
+
#{'<span/>'}
|
42
|
+
HAML
|
43
|
+
end
|
44
|
+
end
|