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.
- data/README.txt +17 -3
- data/VERSION.yml +2 -2
- data/bin/erector +1 -1
- data/lib/erector.rb +22 -2
- data/lib/erector/after_initialize.rb +34 -0
- data/lib/erector/caching.rb +93 -0
- data/lib/erector/convenience.rb +58 -0
- data/lib/erector/dependencies.rb +24 -0
- data/lib/erector/dependency.rb +21 -0
- data/lib/erector/{erect.rb → erect/erect.rb} +14 -4
- data/lib/erector/{erected.rb → erect/erected.rb} +6 -4
- data/lib/erector/{indenting.rb → erect/indenting.rb} +0 -0
- data/lib/erector/{rhtml.treetop → erect/rhtml.treetop} +51 -11
- data/lib/erector/errors.rb +12 -0
- data/lib/erector/extensions/hash.rb +21 -0
- data/lib/erector/externals.rb +88 -24
- data/lib/erector/html.rb +352 -0
- data/lib/erector/inline.rb +5 -5
- data/lib/erector/jquery.rb +36 -0
- data/lib/erector/mixin.rb +3 -5
- data/lib/erector/needs.rb +94 -0
- data/lib/erector/output.rb +117 -0
- data/lib/erector/rails.rb +2 -2
- data/lib/erector/rails/extensions/action_controller.rb +5 -3
- data/lib/erector/rails/extensions/rails_helpers.rb +159 -0
- data/lib/erector/rails/extensions/rails_widget.rb +98 -56
- data/lib/erector/rails/rails_form_builder.rb +8 -4
- data/lib/erector/rails/rails_version.rb +2 -2
- data/lib/erector/rails/template_handlers/ert_handler.rb +1 -1
- data/lib/erector/rails/template_handlers/rb_handler.rb +42 -1
- data/lib/erector/raw_string.rb +2 -2
- data/lib/erector/sass.rb +22 -0
- data/lib/erector/widget.rb +100 -653
- data/lib/erector/widgets.rb +1 -0
- data/lib/erector/widgets/external_renderer.rb +51 -0
- data/lib/erector/widgets/page.rb +45 -63
- data/lib/erector/widgets/table.rb +9 -1
- data/spec/erect/erect_rails_spec.rb +19 -17
- data/spec/erect/erect_spec.rb +11 -1
- data/spec/erect/erected_spec.rb +76 -5
- data/spec/erect/rhtml_parser_spec.rb +11 -1
- data/spec/erector/caching_spec.rb +267 -0
- data/spec/erector/convenience_spec.rb +258 -0
- data/spec/erector/dependency_spec.rb +46 -0
- data/spec/erector/externals_spec.rb +233 -0
- data/spec/erector/html_spec.rb +508 -0
- data/spec/erector/indentation_spec.rb +84 -24
- data/spec/erector/inline_spec.rb +19 -8
- data/spec/erector/jquery_spec.rb +35 -0
- data/spec/erector/mixin_spec.rb +1 -1
- data/spec/erector/needs_spec.rb +120 -0
- data/spec/erector/output_spec.rb +199 -0
- data/spec/erector/sample-file.txt +1 -0
- data/spec/erector/sass_spec.rb +33 -0
- data/spec/erector/widget_spec.rb +113 -932
- data/spec/erector/widgets/field_table_spec.rb +6 -6
- data/spec/erector/widgets/form_spec.rb +3 -3
- data/spec/erector/widgets/page_spec.rb +52 -6
- data/spec/erector/widgets/table_spec.rb +4 -4
- data/spec/spec_helper.rb +70 -29
- metadata +56 -19
- data/lib/erector/rails/extensions/rails_widget/rails_helpers.rb +0 -137
- data/spec/core_spec_suite.rb +0 -3
- data/spec/erector/external_spec.rb +0 -110
- data/spec/rails_spec_suite.rb +0 -3
- data/spec/spec.opts +0 -1
- 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 &<>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 &<>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&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= ")
|
212
|
+
end.should == "<a href=\"foo?x= \"></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("foo")\"></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&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 " "
|
295
|
+
end.should == "<!-- -->\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  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>&<> 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=\"&<> 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 == " "
|
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 == " "
|
358
|
+
end
|
359
|
+
|
360
|
+
it "renders a character given the unicode name" do
|
361
|
+
erector do
|
362
|
+
text character(:right_arrow)
|
363
|
+
end.should == "→"
|
364
|
+
end
|
365
|
+
|
366
|
+
it "renders a character above 0xffff" do
|
367
|
+
erector do
|
368
|
+
text character(:old_persian_sign_ka)
|
369
|
+
end.should == "𐎣"
|
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 == "&"
|
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 < y && x > z) alert("don't stop");"
|
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
|