hamlet 0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. data/.gitignore +1 -0
  2. data/Gemfile +25 -0
  3. data/LICENSE +21 -0
  4. data/README.md +52 -0
  5. data/Rakefile +57 -0
  6. data/bin/hamletrb +7 -0
  7. data/extra/slim-mode.el +409 -0
  8. data/extra/test.hamlet +50 -0
  9. data/hamlet.gemspec +36 -0
  10. data/lib/hamlet.rb +7 -0
  11. data/lib/hamlet/engine.rb +3 -0
  12. data/lib/hamlet/parser.rb +147 -0
  13. data/lib/hamlet/template.rb +19 -0
  14. data/test/rails/Rakefile +7 -0
  15. data/test/rails/app/controllers/application_controller.rb +3 -0
  16. data/test/rails/app/controllers/parents_controller.rb +84 -0
  17. data/test/rails/app/controllers/slim_controller.rb +32 -0
  18. data/test/rails/app/helpers/application_helper.rb +2 -0
  19. data/test/rails/app/models/child.rb +3 -0
  20. data/test/rails/app/models/parent.rb +4 -0
  21. data/test/rails/app/views/layouts/application.html.slim +10 -0
  22. data/test/rails/app/views/parents/_form.html.slim +7 -0
  23. data/test/rails/app/views/parents/edit.html.slim +1 -0
  24. data/test/rails/app/views/parents/new.html.slim +1 -0
  25. data/test/rails/app/views/parents/show.html.slim +5 -0
  26. data/test/rails/app/views/slim/_partial.html.slim +1 -0
  27. data/test/rails/app/views/slim/content_for.html.slim +7 -0
  28. data/test/rails/app/views/slim/erb.html.erb +1 -0
  29. data/test/rails/app/views/slim/integers.html.slim +1 -0
  30. data/test/rails/app/views/slim/nil.html.slim +1 -0
  31. data/test/rails/app/views/slim/no_layout.html.slim +1 -0
  32. data/test/rails/app/views/slim/normal.html.slim +1 -0
  33. data/test/rails/app/views/slim/partial.html.slim +2 -0
  34. data/test/rails/app/views/slim/variables.html.slim +1 -0
  35. data/test/rails/config.ru +4 -0
  36. data/test/rails/config/application.rb +45 -0
  37. data/test/rails/config/boot.rb +10 -0
  38. data/test/rails/config/database.yml +4 -0
  39. data/test/rails/config/environment.rb +5 -0
  40. data/test/rails/config/environments/development.rb +26 -0
  41. data/test/rails/config/environments/production.rb +49 -0
  42. data/test/rails/config/environments/test.rb +35 -0
  43. data/test/rails/config/initializers/backtrace_silencers.rb +7 -0
  44. data/test/rails/config/initializers/inflections.rb +10 -0
  45. data/test/rails/config/initializers/mime_types.rb +5 -0
  46. data/test/rails/config/initializers/secret_token.rb +7 -0
  47. data/test/rails/config/initializers/session_store.rb +8 -0
  48. data/test/rails/config/locales/en.yml +5 -0
  49. data/test/rails/config/routes.rb +60 -0
  50. data/test/rails/db/migrate/20101220223037_parents_and_children.rb +17 -0
  51. data/test/rails/script/rails +6 -0
  52. data/test/rails/test/helper.rb +10 -0
  53. data/test/rails/test/test_slim.rb +77 -0
  54. data/test/slim/helper.rb +200 -0
  55. data/test/slim/test_chain_manipulation.rb +42 -0
  56. data/test/slim/test_code_blocks.rb +68 -0
  57. data/test/slim/test_code_escaping.rb +53 -0
  58. data/test/slim/test_code_evaluation.rb +281 -0
  59. data/test/slim/test_code_output.rb +159 -0
  60. data/test/slim/test_code_structure.rb +95 -0
  61. data/test/slim/test_embedded_engines.rb +141 -0
  62. data/test/slim/test_html_escaping.rb +40 -0
  63. data/test/slim/test_html_structure.rb +438 -0
  64. data/test/slim/test_parser_errors.rb +107 -0
  65. data/test/slim/test_pretty.rb +75 -0
  66. data/test/slim/test_ruby_errors.rb +187 -0
  67. data/test/slim/test_sections.rb +90 -0
  68. data/test/slim/test_slim_template.rb +118 -0
  69. data/test/slim/test_text_interpolation.rb +78 -0
  70. data/test/slim/test_wrapper.rb +39 -0
  71. metadata +230 -0
@@ -0,0 +1,200 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'minitest/unit'
5
+ require 'hamlet'
6
+ require 'slim/grammar'
7
+
8
+ MiniTest::Unit.autorun
9
+
10
+ Slim::Engine.after Slim::Parser, Temple::Filters::Validator, :grammar => Slim::Grammar
11
+ Slim::Engine.before Slim::Compiler, Temple::Filters::Validator, :grammar => Slim::Grammar
12
+ Slim::Engine.before Temple::HTML::Pretty, Temple::Filters::Validator
13
+
14
+ class TestSlim < MiniTest::Unit::TestCase
15
+ def setup
16
+ @env = Env.new
17
+ end
18
+
19
+ def teardown
20
+ Slim::Sections.set_default_options(:dictionary_access => :wrapped)
21
+ end
22
+
23
+ def render(source, options = {}, &block)
24
+ Hamlet::Template.new(options[:file], options) { source }.render(options[:scope] || @env, &block)
25
+ end
26
+
27
+ def assert_html(expected, source, options = {}, &block)
28
+ assert_equal expected, render(source, options, &block)
29
+ end
30
+
31
+ def assert_syntax_error(message, source, options = {})
32
+ render(source, options)
33
+ raise 'Syntax error expected'
34
+ rescue Slim::Parser::SyntaxError => ex
35
+ assert_equal message, ex.message
36
+ end
37
+
38
+ def assert_ruby_error(error, from, source, options = {})
39
+ render(source, options)
40
+ raise 'Ruby error expected'
41
+ rescue error => ex
42
+ assert_backtrace(ex, from)
43
+ end
44
+
45
+ def assert_backtrace(ex, from)
46
+ if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'rbx'
47
+ # HACK: Rubinius stack trace sometimes has one entry more
48
+ if ex.backtrace[0] !~ /^#{Regexp.escape from}:/
49
+ ex.backtrace[1] =~ /^(.*?:\d+):/
50
+ assert_equal from, $1
51
+ end
52
+ else
53
+ ex.backtrace[0] =~ /^(.*?:\d+):/
54
+ assert_equal from, $1
55
+ end
56
+ end
57
+
58
+ def assert_ruby_syntax_error(from, source, options = {})
59
+ render(source, options)
60
+ raise 'Ruby syntax error expected'
61
+ rescue SyntaxError => ex
62
+ ex.message =~ /^(.*?:\d+):/
63
+ assert_equal from, $1
64
+ end
65
+
66
+ def assert_runtime_error(message, source, options = {})
67
+ render(source, options)
68
+ raise Exception, 'Runtime error expected'
69
+ rescue RuntimeError => ex
70
+ assert_equal message, ex.message
71
+ end
72
+ end
73
+
74
+ class Env
75
+ attr_reader :var
76
+
77
+ class ::HtmlSafeString < String
78
+ def html_safe?
79
+ true
80
+ end
81
+ end
82
+
83
+ class ::HtmlUnsafeString < String
84
+ def html_safe?
85
+ false
86
+ end
87
+ end
88
+
89
+ def initialize
90
+ @var = 'instance'
91
+ end
92
+
93
+ def id_helper
94
+ "notice"
95
+ end
96
+
97
+ def hash
98
+ {:a => 'The letter a', :b => 'The letter b'}
99
+ end
100
+
101
+ def show_first?(show = false)
102
+ show
103
+ end
104
+
105
+ def define_macro(name, &block)
106
+ @macro ||= {}
107
+ @macro[name.to_s] = block
108
+ ''
109
+ end
110
+
111
+ def call_macro(name, *args)
112
+ @macro[name.to_s].call(*args)
113
+ end
114
+
115
+ def hello_world(text = "Hello World from @env", opts = {})
116
+ text << opts.to_a * " " if opts.any?
117
+ if block_given?
118
+ "#{text} #{yield} #{text}"
119
+ else
120
+ text
121
+ end
122
+ end
123
+
124
+ def message(*args)
125
+ args.join(' ')
126
+ end
127
+
128
+ def action_path(*args)
129
+ "/action-#{args.join('-')}"
130
+ end
131
+
132
+ def in_keyword
133
+ "starts with keyword"
134
+ end
135
+
136
+ def evil_method
137
+ "<script>do_something_evil();</script>"
138
+ end
139
+
140
+ def method_which_returns_true
141
+ true
142
+ end
143
+
144
+ def output_number
145
+ 1337
146
+ end
147
+ end
148
+
149
+ class ViewEnv
150
+ def output_number
151
+ 1337
152
+ end
153
+
154
+ def person
155
+ [{:name => 'Joe'}, {:name => 'Jack'}]
156
+ end
157
+
158
+ def people
159
+ %w(Andy Fred Daniel).collect{|n| Person.new(n)}
160
+ end
161
+
162
+ def cities
163
+ %w{Atlanta Melbourne Karlsruhe}
164
+ end
165
+
166
+ def people_with_locations
167
+ array = []
168
+ people.each_with_index do |p,i|
169
+ p.location = Location.new cities[i]
170
+ array << p
171
+ end
172
+ array
173
+ end
174
+ end
175
+
176
+ require 'forwardable'
177
+
178
+ class Person
179
+ extend Forwardable
180
+
181
+ attr_accessor :name
182
+
183
+ def initialize(name)
184
+ @name = name
185
+ end
186
+
187
+ def location=(location)
188
+ @location = location
189
+ end
190
+
191
+ def_delegators :@location, :city
192
+ end
193
+
194
+ class Location
195
+ attr_accessor :city
196
+
197
+ def initialize(city)
198
+ @city = city
199
+ end
200
+ end
@@ -0,0 +1,42 @@
1
+ require 'helper'
2
+
3
+ class TestSlimChainManipulation < TestSlim
4
+ def test_replace
5
+ source = %q{
6
+ p Test
7
+ }
8
+ chain = proc do |engine|
9
+ engine.replace(Temple::HTML::Pretty, :ReplacementFilter) do |exp|
10
+ [:dynamic, '1+1']
11
+ end
12
+ end
13
+
14
+ assert_html '2', source, :chain => chain
15
+ end
16
+
17
+ def test_before
18
+ source = %q{
19
+ p Test
20
+ }
21
+ chain = proc do |engine|
22
+ engine.before(Slim::Parser, :WrapInput) do |input|
23
+ "p Header\n#{input}\np Footer"
24
+ end
25
+ end
26
+
27
+ assert_html '<p>Header</p><p>Test</p><p>Footer</p>', source, :chain => chain
28
+ end
29
+
30
+ def test_after
31
+ source = %q{
32
+ p Test
33
+ }
34
+ chain = proc do |engine|
35
+ engine.after(Slim::Parser, :ReplaceParsedExp) do |exp|
36
+ [:slim, :output, false, '1+1', [:multi]]
37
+ end
38
+ end
39
+
40
+ assert_html '2', source, :chain => chain
41
+ end
42
+ end
@@ -0,0 +1,68 @@
1
+ require 'helper'
2
+
3
+ class TestSlimCodeBlocks < TestSlim
4
+ def test_render_with_output_code_block
5
+ source = %q{
6
+ p
7
+ = hello_world "Hello Ruby!" do
8
+ | Hello from within a block!
9
+ }
10
+
11
+ assert_html '<p>Hello Ruby! Hello from within a block! Hello Ruby!</p>', source
12
+ end
13
+
14
+ def test_render_with_output_code_within_block
15
+ source = %q{
16
+ p
17
+ = hello_world "Hello Ruby!" do
18
+ = hello_world "Hello from within a block!"
19
+ }
20
+
21
+ assert_html '<p>Hello Ruby! Hello from within a block! Hello Ruby!</p>', source
22
+ end
23
+
24
+ def test_render_with_output_code_within_block_2
25
+ source = %q{
26
+ p
27
+ = hello_world "Hello Ruby!" do
28
+ = hello_world "Hello from within a block!" do
29
+ = hello_world "And another one!"
30
+ }
31
+
32
+ assert_html '<p>Hello Ruby! Hello from within a block! And another one! Hello from within a block! Hello Ruby!</p>', source
33
+ end
34
+
35
+ def test_output_block_with_arguments
36
+ source = %q{
37
+ p
38
+ = define_macro :person do |first_name, last_name|
39
+ .first_name = first_name
40
+ .last_name = last_name
41
+ == call_macro :person, 'John', 'Doe'
42
+ == call_macro :person, 'Max', 'Mustermann'
43
+ }
44
+
45
+ assert_html '<p><div class="first_name">John</div><div class="last_name">Doe</div><div class="first_name">Max</div><div class="last_name">Mustermann</div></p>', source
46
+ end
47
+
48
+
49
+ def test_render_with_control_code_loop
50
+ source = %q{
51
+ p
52
+ - 3.times do
53
+ | Hey!
54
+ }
55
+
56
+ assert_html '<p>Hey!Hey!Hey!</p>', source
57
+ end
58
+
59
+ def test_captured_code_block_with_conditional
60
+ source = %q{
61
+ = hello_world "Hello Ruby!" do
62
+ - if true
63
+ | Hello from within a block!
64
+ }
65
+
66
+ assert_html 'Hello Ruby! Hello from within a block! Hello Ruby!', source
67
+ end
68
+ end
@@ -0,0 +1,53 @@
1
+ require 'helper'
2
+
3
+ class TestSlimCodeEscaping < TestSlim
4
+ def test_escaping_evil_method
5
+ source = %q{
6
+ p = evil_method
7
+ }
8
+
9
+ assert_html '<p>&lt;script&gt;do_something_evil();&lt;&#47;script&gt;</p>', source
10
+ end
11
+
12
+ def test_render_without_html_safe
13
+ source = %q{
14
+ p = "<strong>Hello World\\n, meet \\"Slim\\"</strong>."
15
+ }
16
+
17
+ assert_html "<p>&lt;strong&gt;Hello World\n, meet \&quot;Slim\&quot;&lt;&#47;strong&gt;.</p>", source
18
+ end
19
+
20
+ def test_render_with_html_safe_false
21
+ source = %q{
22
+ p = HtmlUnsafeString.new("<strong>Hello World\\n, meet \\"Slim\\"</strong>.")
23
+ }
24
+
25
+ assert_html "<p>&lt;strong&gt;Hello World\n, meet \&quot;Slim\&quot;&lt;&#47;strong&gt;.</p>", source, :use_html_safe => true
26
+ end
27
+
28
+ def test_render_with_html_safe_true
29
+ source = %q{
30
+ p = HtmlSafeString.new("<strong>Hello World\\n, meet \\"Slim\\"</strong>.")
31
+ }
32
+
33
+ assert_html "<p><strong>Hello World\n, meet \"Slim\"</strong>.</p>", source, :use_html_safe => true
34
+ end
35
+
36
+ def test_render_with_disable_escape_false
37
+ source = %q{
38
+ = "<p>Hello</p>"
39
+ == "<p>World</p>"
40
+ }
41
+
42
+ assert_html "&lt;p&gt;Hello&lt;&#47;p&gt;<p>World</p>", source
43
+ end
44
+
45
+ def test_render_with_disable_escape_true
46
+ source = %q{
47
+ = "<p>Hello</p>"
48
+ == "<p>World</p>"
49
+ }
50
+
51
+ assert_html "<p>Hello</p><p>World</p>", source, :disable_escape => true
52
+ end
53
+ end
@@ -0,0 +1,281 @@
1
+ require 'helper'
2
+
3
+ class TestSlimCodeEvaluation < TestSlim
4
+ def test_render_with_call_to_set_attributes
5
+ source = %q{
6
+ p id="#{id_helper}" class="hello world" = hello_world
7
+ }
8
+
9
+ assert_html '<p class="hello world" id="notice">Hello World from @env</p>', source
10
+ end
11
+
12
+ def test_render_with_call_to_set_custom_attributes
13
+ source = %q{
14
+ p data-id="#{id_helper}" data-class="hello world"
15
+ = hello_world
16
+ }
17
+
18
+ assert_html '<p data-class="hello world" data-id="notice">Hello World from @env</p>', source
19
+ end
20
+
21
+ def test_render_with_call_to_set_attributes_and_call_to_set_content
22
+ source = %q{
23
+ p id="#{id_helper}" class="hello world" = hello_world
24
+ }
25
+
26
+ assert_html '<p class="hello world" id="notice">Hello World from @env</p>', source
27
+ end
28
+
29
+ def test_render_with_parameterized_call_to_set_attributes_and_call_to_set_content
30
+ source = %q{
31
+ p id="#{id_helper}" class="hello world" = hello_world("Hello Ruby!")
32
+ }
33
+
34
+ assert_html '<p class="hello world" id="notice">Hello Ruby!</p>', source
35
+ end
36
+
37
+ def test_render_with_spaced_parameterized_call_to_set_attributes_and_call_to_set_content
38
+ source = %q{
39
+ p id="#{id_helper}" class="hello world" = hello_world "Hello Ruby!"
40
+ }
41
+
42
+ assert_html '<p class="hello world" id="notice">Hello Ruby!</p>', source
43
+ end
44
+
45
+ def test_render_with_spaced_parameterized_call_to_set_attributes_and_call_to_set_content_2
46
+ source = %q{
47
+ p id="#{id_helper}" class="hello world" = hello_world "Hello Ruby!", :dummy => "value"
48
+ }
49
+
50
+ assert_html '<p class="hello world" id="notice">Hello Ruby!dummy value</p>', source
51
+ end
52
+
53
+ def test_hash_call_in_attribute
54
+ source = %q{
55
+ p id="#{hash[:a]}" Test it
56
+ }
57
+
58
+ assert_html '<p id="The letter a">Test it</p>', source
59
+ end
60
+
61
+ def test_instance_variable_in_attribute_without_quotes
62
+ source = %q{
63
+ p id=@var
64
+ }
65
+
66
+ assert_html '<p id="instance"></p>', source
67
+ end
68
+
69
+ def test_method_call_in_attribute_without_quotes
70
+ source = %q{
71
+ form action=action_path(:page, :save) method='post'
72
+ }
73
+
74
+ assert_html '<form action="&#47;action-page-save" method="post"></form>', source
75
+ end
76
+
77
+ def test_ruby_attribute_with_unbalanced_delimiters
78
+ source = %q{
79
+ div crazy=action_path('[') id="crazy_delimiters"
80
+ }
81
+
82
+ assert_html '<div crazy="&#47;action-[" id="crazy_delimiters"></div>', source
83
+ end
84
+
85
+ def test_method_call_in_delimited_attribute_without_quotes
86
+ source = %q{
87
+ form(action=action_path(:page, :save) method='post')
88
+ }
89
+
90
+ assert_html '<form action="&#47;action-page-save" method="post"></form>', source
91
+ end
92
+
93
+ def test_method_call_in_delimited_attribute_without_quotes2
94
+ source = %q{
95
+ form(method='post' action=action_path(:page, :save))
96
+ }
97
+
98
+ assert_html '<form action="&#47;action-page-save" method="post"></form>', source
99
+ end
100
+
101
+ def test_bypassing_escape_in_attribute
102
+ source = %q{
103
+ form action==action_path(:page, :save) method='post'
104
+ }
105
+
106
+ assert_html '<form action="/action-page-save" method="post"></form>', source
107
+ end
108
+
109
+ def test_hash_call_in_attribute_without_quotes
110
+ source = %q{
111
+ p id=hash[:a] Test it
112
+ }
113
+
114
+ assert_html '<p id="The letter a">Test it</p>', source
115
+ end
116
+
117
+ def test_hash_call_in_delimited_attribute
118
+ source = %q{
119
+ p(id=hash[:a]) Test it
120
+ }
121
+
122
+ assert_html '<p id="The letter a">Test it</p>', source
123
+ end
124
+
125
+ def test_hash_call_in_attribute_with_ruby_evaluation
126
+ source = %q{
127
+ p id={hash[:a] + hash[:a]} Test it
128
+ }
129
+
130
+ assert_html '<p id="The letter aThe letter a">Test it</p>', source
131
+ end
132
+
133
+ def test_hash_call_in_delimited_attribute_with_ruby_evaluation
134
+ source = %q{
135
+ p(id=(hash[:a] + hash[:a])) Test it
136
+ }
137
+
138
+ assert_html '<p id="The letter aThe letter a">Test it</p>', source
139
+ end
140
+
141
+ def test_hash_call_in_delimited_attribute_with_ruby_evaluation_2
142
+ source = %q{
143
+ p[id=(hash[:a] + hash[:a])] Test it
144
+ }
145
+
146
+ assert_html '<p id="The letter aThe letter a">Test it</p>', source
147
+ end
148
+
149
+ def test_hash_call_in_delimited_attribute_with_ruby_evaluation_3
150
+ source = %q{
151
+ p(id=[hash[:a] + hash[:a]]) Test it
152
+ }
153
+
154
+ assert_html '<p id="The letter aThe letter a">Test it</p>', source
155
+ end
156
+
157
+ def test_hash_call_in_delimited_attribute_with_ruby_evaluation_4
158
+ source = %q{
159
+ p(id=[hash[:a] + hash[:a]] class=[hash[:a]]) Test it
160
+ }
161
+
162
+ assert_html '<p class="The letter a" id="The letter aThe letter a">Test it</p>', source
163
+ end
164
+
165
+ def test_hash_call_in_delimited_attribute_with_ruby_evaluation_5
166
+ source = %q{
167
+ p(id=hash[:a] class=[hash[:a]]) Test it
168
+ }
169
+
170
+ assert_html '<p class="The letter a" id="The letter a">Test it</p>', source
171
+ end
172
+
173
+ def test_computation_in_attribute
174
+ source = %q{
175
+ p id=(1 + 1)*5 Test it
176
+ }
177
+
178
+ assert_html '<p id="10">Test it</p>', source
179
+ end
180
+
181
+ def test_number_type_interpolation
182
+ source = %q{
183
+ p = output_number
184
+ }
185
+
186
+ assert_html '<p>1337</p>', source
187
+ end
188
+
189
+ def test_ternary_operation_in_attribute
190
+ source = %q{
191
+ p id="#{(false ? 'notshown' : 'shown')}" = output_number
192
+ }
193
+
194
+ assert_html '<p id="shown">1337</p>', source
195
+ end
196
+
197
+ def test_class_attribute_merging
198
+ source = %{
199
+ .alpha class="beta" Test it
200
+ }
201
+ assert_html '<div class="alpha beta">Test it</div>', source
202
+ end
203
+
204
+ def test_class_attribute_merging_with_nil
205
+ source = %{
206
+ .alpha class="beta" class=nil class="gamma" Test it
207
+ }
208
+ assert_html '<div class="alpha beta gamma">Test it</div>', source
209
+ end
210
+
211
+ def test_id_attribute_merging
212
+ source = %{
213
+ #alpha id="beta" Test it
214
+ }
215
+ assert_html '<div id="alpha_beta">Test it</div>', source, :attr_delimiter => {'class' => ' ', 'id' => '_' }
216
+ end
217
+
218
+ def test_id_attribute_merging2
219
+ source = %{
220
+ #alpha id="beta" Test it
221
+ }
222
+ assert_html '<div id="alpha-beta">Test it</div>', source, :attr_delimiter => {'class' => ' ', 'id' => '-' }
223
+ end
224
+
225
+ def test_boolean_attribute_false
226
+ source = %{
227
+ option selected=false Text
228
+ }
229
+
230
+ assert_html '<option>Text</option>', source
231
+ end
232
+
233
+ def test_boolean_attribute_true
234
+ source = %{
235
+ option selected=true Text
236
+ }
237
+
238
+ assert_html '<option selected="selected">Text</option>', source
239
+ end
240
+
241
+ def test_boolean_attribute_dynamic
242
+ source = %{
243
+ option selected=method_which_returns_true Text
244
+ }
245
+
246
+ assert_html '<option selected="selected">Text</option>', source
247
+ end
248
+
249
+ def test_boolean_attribute_nil
250
+ source = %{
251
+ option selected=nil Text
252
+ }
253
+
254
+ assert_html '<option>Text</option>', source
255
+ end
256
+
257
+ def test_boolean_attribute_string2
258
+ source = %{
259
+ option selected="selected" Text
260
+ }
261
+
262
+ assert_html '<option selected="selected">Text</option>', source
263
+ end
264
+
265
+ def test_boolean_attribute_shortcut
266
+ source = %{
267
+ option(class="clazz" selected) Text
268
+ option(selected class="clazz") Text
269
+ }
270
+
271
+ assert_html '<option class="clazz" selected="selected">Text</option><option class="clazz" selected="selected">Text</option>', source
272
+ end
273
+
274
+ def test_array_attribute
275
+ source = %{
276
+ .alpha class="beta" class=[:gamma, nil, :delta, [true, false]]
277
+ }
278
+
279
+ assert_html '<div class="alpha beta gamma delta true false"></div>', source
280
+ end
281
+ end