erector 0.7.2 → 0.8.0

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 (67) hide show
  1. data/README.txt +17 -3
  2. data/VERSION.yml +2 -2
  3. data/bin/erector +1 -1
  4. data/lib/erector.rb +22 -2
  5. data/lib/erector/after_initialize.rb +34 -0
  6. data/lib/erector/caching.rb +93 -0
  7. data/lib/erector/convenience.rb +58 -0
  8. data/lib/erector/dependencies.rb +24 -0
  9. data/lib/erector/dependency.rb +21 -0
  10. data/lib/erector/{erect.rb → erect/erect.rb} +14 -4
  11. data/lib/erector/{erected.rb → erect/erected.rb} +6 -4
  12. data/lib/erector/{indenting.rb → erect/indenting.rb} +0 -0
  13. data/lib/erector/{rhtml.treetop → erect/rhtml.treetop} +51 -11
  14. data/lib/erector/errors.rb +12 -0
  15. data/lib/erector/extensions/hash.rb +21 -0
  16. data/lib/erector/externals.rb +88 -24
  17. data/lib/erector/html.rb +352 -0
  18. data/lib/erector/inline.rb +5 -5
  19. data/lib/erector/jquery.rb +36 -0
  20. data/lib/erector/mixin.rb +3 -5
  21. data/lib/erector/needs.rb +94 -0
  22. data/lib/erector/output.rb +117 -0
  23. data/lib/erector/rails.rb +2 -2
  24. data/lib/erector/rails/extensions/action_controller.rb +5 -3
  25. data/lib/erector/rails/extensions/rails_helpers.rb +159 -0
  26. data/lib/erector/rails/extensions/rails_widget.rb +98 -56
  27. data/lib/erector/rails/rails_form_builder.rb +8 -4
  28. data/lib/erector/rails/rails_version.rb +2 -2
  29. data/lib/erector/rails/template_handlers/ert_handler.rb +1 -1
  30. data/lib/erector/rails/template_handlers/rb_handler.rb +42 -1
  31. data/lib/erector/raw_string.rb +2 -2
  32. data/lib/erector/sass.rb +22 -0
  33. data/lib/erector/widget.rb +100 -653
  34. data/lib/erector/widgets.rb +1 -0
  35. data/lib/erector/widgets/external_renderer.rb +51 -0
  36. data/lib/erector/widgets/page.rb +45 -63
  37. data/lib/erector/widgets/table.rb +9 -1
  38. data/spec/erect/erect_rails_spec.rb +19 -17
  39. data/spec/erect/erect_spec.rb +11 -1
  40. data/spec/erect/erected_spec.rb +76 -5
  41. data/spec/erect/rhtml_parser_spec.rb +11 -1
  42. data/spec/erector/caching_spec.rb +267 -0
  43. data/spec/erector/convenience_spec.rb +258 -0
  44. data/spec/erector/dependency_spec.rb +46 -0
  45. data/spec/erector/externals_spec.rb +233 -0
  46. data/spec/erector/html_spec.rb +508 -0
  47. data/spec/erector/indentation_spec.rb +84 -24
  48. data/spec/erector/inline_spec.rb +19 -8
  49. data/spec/erector/jquery_spec.rb +35 -0
  50. data/spec/erector/mixin_spec.rb +1 -1
  51. data/spec/erector/needs_spec.rb +120 -0
  52. data/spec/erector/output_spec.rb +199 -0
  53. data/spec/erector/sample-file.txt +1 -0
  54. data/spec/erector/sass_spec.rb +33 -0
  55. data/spec/erector/widget_spec.rb +113 -932
  56. data/spec/erector/widgets/field_table_spec.rb +6 -6
  57. data/spec/erector/widgets/form_spec.rb +3 -3
  58. data/spec/erector/widgets/page_spec.rb +52 -6
  59. data/spec/erector/widgets/table_spec.rb +4 -4
  60. data/spec/spec_helper.rb +70 -29
  61. metadata +56 -19
  62. data/lib/erector/rails/extensions/rails_widget/rails_helpers.rb +0 -137
  63. data/spec/core_spec_suite.rb +0 -3
  64. data/spec/erector/external_spec.rb +0 -110
  65. data/spec/rails_spec_suite.rb +0 -3
  66. data/spec/spec.opts +0 -1
  67. data/spec/spec_suite.rb +0 -40
@@ -0,0 +1,46 @@
1
+ require File.expand_path("#{File.dirname(__FILE__)}/../spec_helper")
2
+ require 'benchmark'
3
+ require 'active_support' # for Symbol#to_proc
4
+
5
+ module DependencySpec
6
+ describe Erector::Dependency do
7
+ it "can be constructed with type and text" do
8
+ x = Erector::Dependency.new(:foo, "abc")
9
+ x.type.should == :foo
10
+ x.text.should == "abc"
11
+ x.options.should == {}
12
+ end
13
+
14
+ it "can be constructed with type, text, and options" do
15
+ x = Erector::Dependency.new(:foo, "abc", {:bar => 7})
16
+ x.options.should == {:bar => 7}
17
+ end
18
+
19
+ it "can be constructed with a file" do
20
+ file = File.new("#{File.dirname(__FILE__)}/sample-file.txt")
21
+ x = Erector::Dependency.new(:foo, file)
22
+ x.text.should == "sample file contents, 2 + 2 = \#{2 + 2}\n"
23
+ end
24
+
25
+ it "can be constructed with a file and interpolate the text" do
26
+ file = File.new("#{File.dirname(__FILE__)}/sample-file.txt")
27
+ x = Erector::Dependency.new(:foo, file, :interpolate => true)
28
+ x.text.should == "sample file contents, 2 + 2 = 4\n"
29
+ end
30
+
31
+ it "is equal to an identical external" do
32
+ x = Erector::Dependency.new(:foo, "abc", {:bar => 7})
33
+ y = Erector::Dependency.new(:foo, "abc", {:bar => 7})
34
+ x.should == y
35
+ [x].should include(y)
36
+ end
37
+
38
+ it "is not equal to an otherwise identical external with different options" do
39
+ x = Erector::Dependency.new(:foo, "abc")
40
+ y = Erector::Dependency.new(:foo, "abc", {:bar => 7})
41
+ x.should_not == y
42
+ [x].should_not include(y)
43
+ end
44
+
45
+ end
46
+ end
@@ -0,0 +1,233 @@
1
+ require File.expand_path("#{File.dirname(__FILE__)}/../spec_helper")
2
+ require 'benchmark'
3
+ require 'active_support' # for Symbol#to_proc
4
+
5
+ module ExternalsSpec
6
+
7
+ describe "adding dependencies" do
8
+ before do
9
+ @args = [:what, :ever, :is, :passed]
10
+ @result = Erector::Dependency.new :js, '/foo.js'
11
+ @result2 = Erector::Dependency.new :css, '/foo.css'
12
+ end
13
+
14
+ after do
15
+ Erector::Widget.my_dependencies.clear
16
+ end
17
+
18
+ it "calls #interpret_args with given arguments and passes result to #push_dependency" do
19
+ mock(Erector::Widget).interpret_args(*@args).returns(@result)
20
+ Erector::Widget.depends_on *@args
21
+ end
22
+
23
+ it "starts out with no items in @_dependencies" do
24
+ class Quesadilla < Erector::Widget
25
+ end
26
+ Quesadilla.my_dependencies.should == []
27
+ end
28
+
29
+
30
+ describe '#interpret_args' do
31
+
32
+ class Test
33
+ include Erector::Externals
34
+ end
35
+
36
+ it "will infer that a .js extension is javascript" do
37
+ x = Test.send :interpret_args, ('/path/to/a.js')
38
+ x.text.should == '/path/to/a.js'
39
+ x.type.should == :js
40
+ end
41
+
42
+ it "will infer that a .css extension is a stylesheet" do
43
+ x = Test.send :interpret_args, ('/path/to/a.css')
44
+ x.text.should == '/path/to/a.css'
45
+ x.type.should == :css
46
+ end
47
+
48
+ it "will capture render options when just a file is mentioned" do
49
+ x = Test.send(:interpret_args, '/path/to/a.css', :render=>:link)
50
+ x.text.should == '/path/to/a.css'
51
+ x.type.should == :css
52
+ x.options.should == {:render=>:link} # could also be "embed"
53
+ end
54
+
55
+ it "embeds javascript" do
56
+ x = Test.send :interpret_args, :js, "alert('foo')"
57
+ x.text.should == "alert('foo')"
58
+ x.type.should == :js
59
+ end
60
+
61
+ it "guesses Javascript type from .js" do
62
+ x = Test.send :interpret_args, "/script/foo.js"
63
+ x.text.should == "/script/foo.js"
64
+ x.type.should == :js
65
+ end
66
+
67
+ it "guesses CSS type from .css" do
68
+ x = Test.send :interpret_args, "/script/foo.css"
69
+ x.text.should == "/script/foo.css"
70
+ x.type.should == :css
71
+ end
72
+
73
+ it "add multiple files without an options hash" do
74
+ x = Test.send :interpret_args, :js, "/script/foo.js", "/script/bar.js"
75
+ x.size.should == 2
76
+ x[0].text.should == "/script/foo.js"
77
+ x[0].type.should == :js
78
+ x[1].text.should == "/script/bar.js"
79
+ x[1].type.should == :js
80
+ end
81
+
82
+ it "add multiple files with an options hash" do
83
+ x = Test.send :interpret_args, :js, "/script/foo.js", "/script/bar.js", :embed=>true
84
+ x.size.should == 2
85
+ x[0].text.should == "/script/foo.js"
86
+ x[0].type.should == :js
87
+ x[0].options[:embed].should == true
88
+ x[1].text.should == "/script/bar.js"
89
+ x[1].type.should == :js
90
+ x[1].options[:embed].should == true
91
+ end
92
+
93
+ it "adds multiple files from hash" do
94
+ x = Test.send :interpret_args, :js => ["foo.js", "bar.js"]
95
+ x.size.should == 2
96
+ x[0].text.should == "foo.js"
97
+ x[0].type.should == :js
98
+ x[1].text.should == "bar.js"
99
+ x[1].type.should == :js
100
+ end
101
+ it "adds multiple files from hash of different types" do
102
+ x = Test.send :interpret_args, :js => ["foo.js", "bar.js"], :css=>'file.css'
103
+ x.size.should == 3
104
+ x.map(&:text).include?('foo.js')
105
+ x.map(&:text).include?('bar.js')
106
+ x.map(&:text).include?('file.css')
107
+ end
108
+ it "adds multiple files from hash and preserves the options" do
109
+ x = Test.send :interpret_args, :js => ["foo.js", "bar.js"], :foo=>false
110
+ x.size.should == 2
111
+ x[0].text.should == "foo.js"
112
+ x[0].type.should == :js
113
+ x[0].options.should == {:foo=>false}
114
+ x[1].text.should == "bar.js"
115
+ x[1].type.should == :js
116
+ x[1].options.should == {:foo=>false}
117
+ end
118
+ end
119
+
120
+ end
121
+
122
+ describe 'extracting the dependencies (integration tests)' do
123
+ attr_reader :HotSauce, :SourCream, :Tabasco
124
+
125
+ before do
126
+ @HotSauce = Class.new(Erector::Widget) do
127
+ depends_on :css, "/css/tapatio.css", :media => "print"
128
+ depends_on :css, "/css/salsa_picante.css"
129
+ depends_on :js, "/lib/jquery.js"
130
+ depends_on :js, "/lib/picante.js"
131
+ end
132
+ @SourCream = Class.new(Erector::Widget) do
133
+ depends_on :css, "/css/sourcream.css"
134
+ depends_on :js, "/lib/jquery.js"
135
+ depends_on :js, "/lib/dairy.js"
136
+ end
137
+ @Tabasco = Class.new(self.HotSauce) do
138
+ depends_on :js, "tabasco.js"
139
+ depends_on :css, "/css/salsa_picante.css"
140
+ end
141
+ end
142
+
143
+ it "can be fetched via the type" do
144
+ self.HotSauce.dependencies(:css).map(&:text).should == [
145
+ "/css/tapatio.css",
146
+ "/css/salsa_picante.css",
147
+ ]
148
+ end
149
+
150
+ it "can be filtered via the class" do
151
+ self.SourCream.dependencies(:css).map(&:text).should == [
152
+ "/css/sourcream.css",
153
+ ]
154
+ end
155
+
156
+ it "grabs dependencies from superclasses too" do
157
+ self.Tabasco.dependencies(:js).map(&:text).should == ["/lib/jquery.js", "/lib/picante.js", "tabasco.js"]
158
+ end
159
+
160
+ it "retains the options" do
161
+ self.HotSauce.dependencies(:css).map(&:options).should == [
162
+ {:media => "print"},
163
+ {}
164
+ ]
165
+ end
166
+
167
+ it "removes duplicates" do
168
+ self.Tabasco.dependencies(:css).map(&:text).should == [
169
+ "/css/tapatio.css",
170
+ "/css/salsa_picante.css",
171
+ ]
172
+ end
173
+
174
+ it "works with strings or symbols" do
175
+ self.HotSauce.dependencies("css").map(&:text).should == [
176
+ "/css/tapatio.css",
177
+ "/css/salsa_picante.css",
178
+ ]
179
+ end
180
+
181
+ class Taco < Erector::Widget
182
+ depends_on :filling, "beef"
183
+ depends_on :filling, "beef", :media => "print"
184
+ end
185
+
186
+ it "considers options when removing duplicates" do
187
+ Taco.dependencies(:filling).map(&:text).should == ["beef", "beef"]
188
+ end
189
+
190
+
191
+ end
192
+
193
+ describe "rendering with externals" do
194
+ class Dinner < Erector::Widget
195
+ external :js, "/dinner.js"
196
+
197
+ def content
198
+ span "dinner"
199
+ widget Dessert
200
+ end
201
+ end
202
+
203
+ class Dessert < Erector::Widget
204
+ external :js, "/dessert.js"
205
+ external :css, "/dessert.css"
206
+
207
+ def content
208
+ span "dessert"
209
+ end
210
+ end
211
+
212
+ it "#render_with_externals sticks the externals for all its rendered sub-widgets at the end of the output buffer" do
213
+ s = Dinner.new.render_with_externals
214
+ s.to_s.should ==
215
+ "<span>dinner</span>" +
216
+ "<span>dessert</span>" +
217
+ "<link href=\"/dessert.css\" media=\"all\" rel=\"stylesheet\" type=\"text/css\" />" +
218
+ "<script src=\"/dinner.js\" type=\"text/javascript\"></script>" +
219
+ "<script src=\"/dessert.js\" type=\"text/javascript\"></script>"
220
+ end
221
+
222
+ it "#render_externals returns externals for all rendered sub-widgets to an output buffer" do
223
+ widget = Dinner.new
224
+ widget.to_html
225
+ widget.render_externals.to_s.should ==
226
+ "<link href=\"/dessert.css\" media=\"all\" rel=\"stylesheet\" type=\"text/css\" />" +
227
+ "<script src=\"/dinner.js\" type=\"text/javascript\"></script>" +
228
+ "<script src=\"/dessert.js\" type=\"text/javascript\"></script>"
229
+ end
230
+ end
231
+
232
+
233
+ end
@@ -0,0 +1,508 @@
1
+ require File.expand_path("#{File.dirname(__FILE__)}/../spec_helper")
2
+
3
+ describe Erector::HTML do
4
+ include Erector::Mixin
5
+
6
+ describe ".all_tags" do
7
+ it "returns set of full and empty tags" do
8
+ Erector::Widget.all_tags.class.should == Array
9
+ Erector::Widget.all_tags.should == Erector::Widget.full_tags + Erector::Widget.empty_tags
10
+ end
11
+ end
12
+
13
+ describe "#instruct" do
14
+ it "when passed no arguments; returns an XML declaration with version 1 and utf-8" do
15
+ # version must precede encoding, per XML 1.0 4th edition (section 2.8)
16
+ erector { instruct }.should == "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
17
+ end
18
+ end
19
+
20
+ describe "#element" do
21
+ context "when receiving one argument" do
22
+ it "returns an empty element" do
23
+ erector { element('div') }.should == "<div></div>"
24
+ end
25
+ end
26
+
27
+ context "with a attribute hash" do
28
+ it "returns an empty element with the attributes" do
29
+ html = erector do
30
+ element(
31
+ 'div',
32
+ :class => "foo bar",
33
+ :style => "display: none; color: white; float: left;",
34
+ :nil_attribute => nil
35
+ )
36
+ end
37
+ doc = Nokogiri::HTML(html)
38
+ div = doc.at('div')
39
+ div[:class].should == "foo bar"
40
+ div[:style].should == "display: none; color: white; float: left;"
41
+ div[:nil_attribute].should be_nil
42
+ end
43
+ end
44
+
45
+ context "with an array of CSS classes" do
46
+ it "returns a tag with the classes separated" do
47
+ erector do
48
+ element('div', :class => [:foo, :bar])
49
+ end.should == "<div class=\"foo bar\"></div>";
50
+ end
51
+ end
52
+
53
+ context "with an array of CSS classes as strings" do
54
+ it "returns a tag with the classes separated" do
55
+ erector do
56
+ element('div', :class => ['foo', 'bar'])
57
+ end.should == "<div class=\"foo bar\"></div>";
58
+ end
59
+ end
60
+
61
+ context "with a CSS class which is a string" do
62
+ it "just use that as the attribute value" do
63
+ erector do
64
+ element('div', :class => "foo bar")
65
+ end.should == "<div class=\"foo bar\"></div>";
66
+ end
67
+ end
68
+
69
+ context "with an empty array of CSS classes" do
70
+ it "does not emit a class attribute" do
71
+ erector do
72
+ element('div', :class => [])
73
+ end.should == "<div></div>"
74
+ end
75
+ end
76
+
77
+ context "with many attributes" do
78
+ it "alphabetize them" do
79
+ erector do
80
+ empty_element('foo', :alpha => "", :betty => "5", :aardvark => "tough",
81
+ :carol => "", :demon => "", :erector => "", :pi => "3.14", :omicron => "", :zebra => "", :brain => "")
82
+ end.should == "<foo aardvark=\"tough\" alpha=\"\" betty=\"5\" brain=\"\" carol=\"\" demon=\"\" " \
83
+ "erector=\"\" omicron=\"\" pi=\"3.14\" zebra=\"\" />";
84
+ end
85
+ end
86
+
87
+ context "with inner tags" do
88
+ it "returns nested tags" do
89
+ erector do
90
+ element 'div' do
91
+ element 'div'
92
+ end
93
+ end.should == '<div><div></div></div>'
94
+ end
95
+ end
96
+
97
+ context "with text" do
98
+ it "returns element with inner text" do
99
+ erector do
100
+ element 'div', 'test text'
101
+ end.should == "<div>test text</div>"
102
+ end
103
+ end
104
+
105
+ context "with a widget" do
106
+ it "renders the widget inside the element" do
107
+ erector do
108
+ element 'div', Erector.inline { p "foo" }
109
+ end.should == '<div><p>foo</p></div>'
110
+ end
111
+ end
112
+
113
+ context "with object other than hash" do
114
+ it "returns element with inner text == object.to_s" do
115
+ object = ['a', 'b']
116
+ erector do
117
+ element 'div', object
118
+ end.should == "<div>#{object.to_s}</div>"
119
+ end
120
+ end
121
+
122
+ context "with parameters and block" do
123
+ it "returns element with inner html and attributes" do
124
+ erector do
125
+ element 'div', 'class' => "foobar" do
126
+ element 'span', 'style' => 'display: none;'
127
+ end
128
+ end.should == '<div class="foobar"><span style="display: none;"></span></div>'
129
+ end
130
+ end
131
+
132
+ context "with content and parameters" do
133
+ it "returns element with content as inner html and attributes" do
134
+ erector do
135
+ element 'div', 'test text', :style => "display: none;"
136
+ end.should == '<div style="display: none;">test text</div>'
137
+ end
138
+ end
139
+
140
+ context "with more than three arguments" do
141
+ it "raises ArgumentError" do
142
+ proc do
143
+ erector do
144
+ element 'div', 'foobar', {}, 'fourth'
145
+ end
146
+ end.should raise_error(ArgumentError)
147
+ end
148
+ end
149
+
150
+ it "renders the proper full tags" do
151
+ Erector::Widget.full_tags.each do |tag_name|
152
+ expected = "<#{tag_name}></#{tag_name}>"
153
+ actual = erector { send(tag_name) }
154
+ begin
155
+ actual.should == expected
156
+ rescue Spec::Expectations::ExpectationNotMetError => e
157
+ puts "Expected #{tag_name} to be a full element. Expected #{expected}, got #{actual}"
158
+ raise e
159
+ end
160
+ end
161
+ end
162
+
163
+ describe "quoting" do
164
+ context "when outputting text" do
165
+ it "quotes it" do
166
+ erector do
167
+ element 'div', 'test &<>text'
168
+ end.should == "<div>test &amp;&lt;&gt;text</div>"
169
+ end
170
+ end
171
+
172
+ context "when outputting text via text" do
173
+ it "quotes it" do
174
+ erector do
175
+ element 'div' do
176
+ text "test &<>text"
177
+ end
178
+ end.should == "<div>test &amp;&lt;&gt;text</div>"
179
+ end
180
+ end
181
+
182
+ context "when outputting attribute value" do
183
+ it "quotes it" do
184
+ erector do
185
+ element 'a', :href => "foo.cgi?a&b"
186
+ end.should == "<a href=\"foo.cgi?a&amp;b\"></a>"
187
+ end
188
+ end
189
+
190
+ context "with raw text" do
191
+ it "does not quote it" do
192
+ erector do
193
+ element 'div' do
194
+ text raw("<b>bold</b>")
195
+ end
196
+ end.should == "<div><b>bold</b></div>"
197
+ end
198
+ end
199
+
200
+ context "with raw text and no block" do
201
+ it "does not quote it" do
202
+ erector do
203
+ element 'div', raw("<b>bold</b>")
204
+ end.should == "<div><b>bold</b></div>"
205
+ end
206
+ end
207
+
208
+ context "with raw attribute" do
209
+ it "does not quote it" do
210
+ erector do
211
+ element 'a', :href => raw("foo?x=&nbsp;")
212
+ end.should == "<a href=\"foo?x=&nbsp;\"></a>"
213
+ end
214
+ end
215
+
216
+ context "with quote in attribute" do
217
+ it "quotes it" do
218
+ erector do
219
+ element 'a', :onload => "alert(\"foo\")"
220
+ end.should == "<a onload=\"alert(&quot;foo&quot;)\"></a>"
221
+ end
222
+ end
223
+ end
224
+
225
+ context "with a non-string, non-raw" do
226
+ it "calls to_s and quotes" do
227
+ erector do
228
+ element 'a' do
229
+ text [7, "foo&bar"]
230
+ end
231
+ end.should == "<a>7foo&amp;bar</a>"
232
+ end
233
+ end
234
+ end
235
+
236
+ describe "#empty_element" do
237
+ context "when receiving attributes" do
238
+ it "renders an empty element with the attributes" do
239
+ erector do
240
+ empty_element 'input', :name => 'foo[bar]'
241
+ end.should == '<input name="foo[bar]" />'
242
+ end
243
+ end
244
+
245
+ context "when not receiving attributes" do
246
+ it "renders an empty element without attributes" do
247
+ erector do
248
+ empty_element 'br'
249
+ end.should == '<br />'
250
+ end
251
+ end
252
+
253
+ it "renders the proper empty-element tags" do
254
+ Erector::Widget.empty_tags.each do |tag_name|
255
+ expected = "<#{tag_name} />"
256
+ actual = erector { send(tag_name) }
257
+ begin
258
+ actual.should == expected
259
+ rescue Spec::Expectations::ExpectationNotMetError => e
260
+ puts "Expected #{tag_name} to be an empty-element tag. Expected #{expected}, got #{actual}"
261
+ raise e
262
+ end
263
+ end
264
+ end
265
+ end
266
+
267
+ describe "#comment" do
268
+ it "emits a single line comment when receiving a string" do
269
+ erector do
270
+ comment "foo"
271
+ end.should == "<!--foo-->\n"
272
+ end
273
+
274
+ it "emits a multiline comment when receiving a block" do
275
+ erector do
276
+ comment do
277
+ text "Hello"
278
+ text " world!"
279
+ end
280
+ end.should == "<!--\nHello world!\n-->\n"
281
+ end
282
+
283
+ it "emits a multiline comment when receiving a string and a block" do
284
+ erector do
285
+ comment "Hello" do
286
+ text " world!"
287
+ end
288
+ end.should == "<!--Hello\n world!\n-->\n"
289
+ end
290
+
291
+ # see http://www.w3.org/TR/html4/intro/sgmltut.html#h-3.2.4
292
+ it "does not HTML-escape character references" do
293
+ erector do
294
+ comment "&nbsp;"
295
+ end.should == "<!--&nbsp;-->\n"
296
+ end
297
+
298
+ # see http://www.w3.org/TR/html4/intro/sgmltut.html#h-3.2.4
299
+ # "Authors should avoid putting two or more adjacent hyphens inside comments."
300
+ it "warns if there's two hyphens in a row" do
301
+ capturing_output do
302
+ erector do
303
+ comment "he was -- awesome!"
304
+ end.should == "<!--he was -- awesome!-->\n"
305
+ end.should == "Warning: Authors should avoid putting two or more adjacent hyphens inside comments.\n"
306
+ end
307
+
308
+ it "renders an IE conditional comment with endif when receiving an if IE" do
309
+ erector do
310
+ comment "[if IE]" do
311
+ text "Hello IE!"
312
+ end
313
+ end.should == "<!--[if IE]>\nHello IE!\n<![endif]-->\n"
314
+ end
315
+
316
+ it "doesn't render an IE conditional comment if there's just some text in brackets" do
317
+ erector do
318
+ comment "[puppies are cute]"
319
+ end.should == "<!--[puppies are cute]-->\n"
320
+ end
321
+
322
+ end
323
+
324
+ describe "#nbsp" do
325
+ it "turns consecutive spaces into consecutive non-breaking spaces" do
326
+ erector do
327
+ text nbsp("a b")
328
+ end.should == "a&#160;&#160;b"
329
+ end
330
+
331
+ it "works in text context" do
332
+ erector do
333
+ element 'a' do
334
+ text nbsp("&<> foo")
335
+ end
336
+ end.should == "<a>&amp;&lt;&gt;&#160;foo</a>"
337
+ end
338
+
339
+ it "works in attribute value context" do
340
+ erector do
341
+ element 'a', :href => nbsp("&<> foo")
342
+ end.should == "<a href=\"&amp;&lt;&gt;&#160;foo\"></a>"
343
+ end
344
+
345
+ it "defaults to a single non-breaking space if given no argument" do
346
+ erector do
347
+ text nbsp
348
+ end.should == "&#160;"
349
+ end
350
+
351
+ end
352
+
353
+ describe "#character" do
354
+ it "renders a character given the codepoint number" do
355
+ erector do
356
+ text character(160)
357
+ end.should == "&#xa0;"
358
+ end
359
+
360
+ it "renders a character given the unicode name" do
361
+ erector do
362
+ text character(:right_arrow)
363
+ end.should == "&#x2192;"
364
+ end
365
+
366
+ it "renders a character above 0xffff" do
367
+ erector do
368
+ text character(:old_persian_sign_ka)
369
+ end.should == "&#x103a3;"
370
+ end
371
+
372
+ it "throws an exception if a name is not recognized" do
373
+ lambda {
374
+ erector { text character(:no_such_character_name) }
375
+ }.should raise_error("Unrecognized character no_such_character_name")
376
+ end
377
+
378
+ it "throws an exception if passed something besides a symbol or integer" do
379
+ # Perhaps calling to_s would be more ruby-esque, but that seems like it might
380
+ # be pretty confusing when this method can already take either a name or number
381
+ lambda {
382
+ erector { text character([]) }
383
+ }.should raise_error("Unrecognized argument to character: ")
384
+ end
385
+ end
386
+
387
+ describe '#h' do
388
+ before do
389
+ @widget = Erector::Widget.new
390
+ end
391
+
392
+ it "escapes regular strings" do
393
+ @widget.h("&").should == "&amp;"
394
+ end
395
+
396
+ it "does not escape raw strings" do
397
+ @widget.h(@widget.raw("&")).should == "&"
398
+ end
399
+ end
400
+
401
+ describe 'escaping' do
402
+ plain = 'if (x < y && x > z) alert("don\'t stop");'
403
+ escaped = "if (x &lt; y &amp;&amp; x &gt; z) alert(&quot;don't stop&quot;);"
404
+
405
+ describe "#text" do
406
+ it "does HTML escape its param" do
407
+ erector { text plain }.should == escaped
408
+ end
409
+
410
+ it "doesn't escape pre-escaped strings" do
411
+ erector { text h(plain) }.should == escaped
412
+ end
413
+ end
414
+ describe "#rawtext" do
415
+ it "doesn't HTML escape its param" do
416
+ erector { rawtext plain }.should == plain
417
+ end
418
+ end
419
+ describe "#text!" do
420
+ it "doesn't HTML escape its param" do
421
+ erector { text! plain }.should == plain
422
+ end
423
+ end
424
+ describe "#element" do
425
+ it "does HTML escape its param" do
426
+ erector { element "foo", plain }.should == "<foo>#{escaped}</foo>"
427
+ end
428
+ end
429
+ describe "#element!" do
430
+ it "doesn't HTML escape its param" do
431
+ erector { element! "foo", plain }.should == "<foo>#{plain}</foo>"
432
+ end
433
+ end
434
+ end
435
+
436
+ describe "#javascript" do
437
+ context "when receiving a block" do
438
+ it "renders the content inside of script text/javascript tags" do
439
+ expected = <<-EXPECTED
440
+ <script type="text/javascript">
441
+ // <![CDATA[
442
+ if (x < y && x > z) alert("don't stop");
443
+ // ]]>
444
+ </script>
445
+ EXPECTED
446
+ expected.gsub!(/^ /, '')
447
+ erector do
448
+ javascript do
449
+ rawtext 'if (x < y && x > z) alert("don\'t stop");'
450
+ end
451
+ end.should == expected
452
+ end
453
+ end
454
+
455
+ it "renders the raw content inside script tags when given text" do
456
+ expected = <<-EXPECTED
457
+ <script type="text/javascript">
458
+ // <![CDATA[
459
+ alert("&<>'hello");
460
+ // ]]>
461
+ </script>
462
+ EXPECTED
463
+ expected.gsub!(/^ /, '')
464
+ erector do
465
+ javascript('alert("&<>\'hello");')
466
+ end.should == expected
467
+ end
468
+
469
+ context "when receiving a params hash" do
470
+ it "renders a source file" do
471
+ html = erector do
472
+ javascript(:src => "/my/js/file.js")
473
+ end
474
+ doc = Nokogiri::HTML(html)
475
+ doc.at("script")[:src].should == "/my/js/file.js"
476
+ end
477
+ end
478
+
479
+ context "when receiving text and a params hash" do
480
+ it "renders a source file" do
481
+ html = erector do
482
+ javascript('alert("&<>\'hello");', :src => "/my/js/file.js")
483
+ end
484
+ doc = Nokogiri::HTML(html)
485
+ script_tag = doc.at('script')
486
+ script_tag[:src].should == "/my/js/file.js"
487
+ script_tag.inner_html.should include('alert("&<>\'hello");')
488
+ end
489
+ end
490
+
491
+ context "with too many arguments" do
492
+ it "raises ArgumentError" do
493
+ proc do
494
+ erector do
495
+ javascript 'foobar', {}, 'fourth'
496
+ end
497
+ end.should raise_error(ArgumentError)
498
+ end
499
+ end
500
+ end
501
+
502
+ describe "#close_tag" do
503
+ it "works when it's all alone, even though it messes with the indent level" do
504
+ erector { close_tag :foo }.should == "</foo>"
505
+ erector { close_tag :foo; close_tag :bar }.should == "</foo></bar>"
506
+ end
507
+ end
508
+ end