eimxml 0.0.2 → 0.0.3.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.
@@ -3,6 +3,7 @@
3
3
  # Copyright (C) 2006, KURODA Hiraku <hiraku@hinet.mydns.jp>
4
4
  # You can redistribute it and/or modify it under GPL2.
5
5
 
6
+ $:.unshift "#{File.dirname(File.dirname(File.expand_path(__FILE__)))}/lib"
6
7
  require "test/unit"
7
8
  require "eim_xml/assertions"
8
9
 
@@ -0,0 +1,217 @@
1
+ require "eim_xml/dsl"
2
+
3
+ module Module.new::M
4
+ include EimXML
5
+ EDSL = EimXML::DSL
6
+
7
+ describe EimXML::DSL do
8
+ it "scope is in instance of DSL" do
9
+ outer = inner = nil
10
+ e3 = e2 = nil
11
+ block_executed = false
12
+ e = EDSL.element(:out, :k1=>"v1") do
13
+ outer = self
14
+ e2 = element(:in, :k2=>"v2") do
15
+ block_executed = true
16
+ inner = self
17
+ e3 = element(:deep)
18
+ end
19
+ end
20
+
21
+ block_executed.should == true
22
+ outer.should be_kind_of(EDSL)
23
+ inner.should be_kind_of(EDSL)
24
+ outer.should be_equal(inner)
25
+
26
+ e.name.should == :out
27
+ e[:k1].should == "v1"
28
+ e[0].name.should == :in
29
+ e[0][:k2].should == "v2"
30
+ e[0][0].name.should == :deep
31
+ e2.should be_equal(e[0])
32
+ e3.should be_equal(e[0][0])
33
+ end
34
+
35
+ it "#comment" do
36
+ Comment.should_receive(:new).with("comment").and_return(:success)
37
+ EDSL.comment("comment").should == :success
38
+ end
39
+
40
+ it "#import_variables" do
41
+ d = EDSL.new
42
+ o = Object.new
43
+ o.instance_variable_set("@v1", 1)
44
+ o.instance_variable_set("@v2", "2")
45
+ o.instance_variable_set("@_v3", :t)
46
+ o.instance_variable_set("@__v4", 4)
47
+ o.instance_variable_set("@_container", :t)
48
+ orig_c = d.instance_variable_get("@_container")
49
+
50
+ d.import_variables(o).should be_equal(d)
51
+
52
+ d.instance_variable_get("@_container").should == orig_c
53
+ d.instance_variables.map(&:to_s).sort.should == ["@v1", "@v2", "@__v4"].sort
54
+ d.instance_variable_get("@v1").should == 1
55
+ d.instance_variable_get("@v2").should == "2"
56
+ d.instance_variable_get("@__v4").should == 4
57
+ end
58
+
59
+ describe "#_push" do
60
+ before do
61
+ m = Module.new
62
+ class m::D < EimXML::DSL
63
+ def call_push(c)
64
+ _push(c) do
65
+ element(:e)
66
+ end
67
+ end
68
+
69
+ def exec
70
+ element(:e) do
71
+ element(:f)
72
+ end
73
+ end
74
+ end
75
+ @D = m::D
76
+ end
77
+
78
+ it "should return given container" do
79
+ a = []
80
+ @D.new.call_push(a).should be_equal(a)
81
+ a.should == [EimXML::Element.new(:e)]
82
+
83
+ @D.new.exec.should == EimXML::Element.new(:e).add(EimXML::Element.new(:f))
84
+ end
85
+ end
86
+ end
87
+
88
+ describe "Subclass of BaseDSL" do
89
+ class DSL1 < EimXML::BaseDSL
90
+ register([EimXML::Element, "call"])
91
+ register(Hash)
92
+ register(String, Array, Object)
93
+ end
94
+
95
+ it "register" do
96
+ lambda{EDSL.call(:dummy)}.should raise_error(NoMethodError)
97
+ lambda{BaseDSL.call(:dummy)}.should raise_error(NoMethodError)
98
+ lambda{DSL1.element(:dummy)}.should raise_error(NoMethodError)
99
+ DSL1.call(:dummy).should be_kind_of(Element)
100
+ DSL1.hash.should be_kind_of(Hash)
101
+ DSL1.string.should be_kind_of(String)
102
+ DSL1.array.should be_kind_of(Array)
103
+ DSL1.object.should be_kind_of(Object)
104
+ end
105
+ end
106
+
107
+ describe EimXML::OpenDSL do
108
+ it "scope of block is one of outside" do
109
+ @scope_checker_variable = 1
110
+ block_executed = false
111
+ d = OpenDSL.new do |d|
112
+ block_executed = true
113
+ d.should be_kind_of(OpenDSL)
114
+ d.container.should be_nil
115
+ d.element(:base, :key1=>"v1") do
116
+ @scope_checker_variable.should == 1
117
+ self.should_not be_kind_of(Element)
118
+ d.container.should be_kind_of(Element)
119
+ d.container.should == Element.new(:base, :key1=>"v1")
120
+ d.element(:sub, :key2=>"v2") do
121
+ d.container.should be_kind_of(Element)
122
+ d.container.should == Element.new(:sub, :key2=>"v2")
123
+ end
124
+ d.element(:sub2).should == Element.new(:sub2)
125
+ end
126
+ end
127
+ block_executed.should be_true
128
+ end
129
+
130
+ it "DSL methods return element" do
131
+ d = OpenDSL.new
132
+ d.container.should be_nil
133
+ r = d.element(:base, :key1=>"v1") do
134
+ d.element(:sub, :key2=>"v2")
135
+ end
136
+ r.should == EDSL.element(:base, :key1=>"v1") do
137
+ element(:sub, :key2=>"v2")
138
+ end
139
+ end
140
+
141
+ it "DSL method's block given instance of OpenDSL" do
142
+ e = OpenDSL.new.element(:base) do |d|
143
+ d.should be_kind_of(OpenDSL)
144
+ d.container.name.should == :base
145
+ d.element(:sub) do |d2|
146
+ d2.should be_equal(d)
147
+ end
148
+ end
149
+
150
+ e.should == EDSL.element(:base) do
151
+ element(:sub)
152
+ end
153
+ end
154
+
155
+ it "ensure reset container when error raised" do
156
+ OpenDSL.new do |d|
157
+ begin
158
+ d.element(:base) do
159
+ begin
160
+ d.element(:sub) do
161
+ raise "OK"
162
+ end
163
+ rescue RuntimeError => e
164
+ raise unless e.message=="OK"
165
+ d.container.name.should == :base
166
+ raise
167
+ end
168
+ end
169
+ rescue RuntimeError => e
170
+ raise unless e.message=="OK"
171
+ d.container.should == nil
172
+ end
173
+ end
174
+ end
175
+
176
+ it "respond to add" do
177
+ r = OpenDSL.new.element(:base) do |d|
178
+ d.add "text"
179
+ d.element(:sub) do
180
+ s = Element.new(:sub)
181
+ s.add("sub text")
182
+ d.add("sub text").should == s
183
+ end
184
+ end
185
+
186
+ r.should == EDSL.element(:base) do
187
+ add "text"
188
+ element(:sub) do
189
+ add "sub text"
190
+ end
191
+ end
192
+ end
193
+
194
+ it "respond to <<" do
195
+ r = OpenDSL.new.element(:base) do |d|
196
+ b = Element.new(:base)
197
+ b << "text" << "next"
198
+ (d << "text" << "next").should == b
199
+ end
200
+ r.should == EDSL.element(:base) do
201
+ add "text"
202
+ add "next"
203
+ end
204
+ end
205
+
206
+ it "can call directly element method" do
207
+ r = OpenDSL.element(:base) do |d|
208
+ d.element(:sub)
209
+ d.element(:sub2)
210
+ end
211
+ r.should == EDSL.element(:base) do
212
+ element(:sub)
213
+ element(:sub2)
214
+ end
215
+ end
216
+ end
217
+ end
@@ -0,0 +1,441 @@
1
+ require "eim_xml/dsl"
2
+
3
+ module Module.new::M
4
+ include EimXML
5
+ EDSL = EimXML::DSL
6
+
7
+ describe PCString do
8
+ it ".encode" do
9
+ PCString.encode("<>\"'&").should == "&lt;&gt;&quot;&apos;&amp;"
10
+ PCString.encode("&test;").should == "&amp;test;"
11
+ PCString.encode("&amp;").should == "&amp;amp;"
12
+ PCString.encode(:'sym&').should == "sym&amp;"
13
+ end
14
+
15
+ it ".new" do
16
+ PCString.new("&").encoded_string.should == "&amp;"
17
+ PCString.new("&", true).encoded_string.should == "&"
18
+ pcs = PCString.new(:'sym&')
19
+ pcs.encoded_string.should == "sym&amp;"
20
+ pcs.src.should == :'sym&'
21
+ end
22
+
23
+ describe ".[]" do
24
+ it "should return itself when given object is a PCString" do
25
+ pcs = PCString.new("s")
26
+ PCString[pcs].should equal(pcs)
27
+ end
28
+
29
+ it "should return PCString.new(obj) if given obj is not a PCString" do
30
+ o = "str"
31
+ r = PCString[o]
32
+ r.is_a?(EimXML::PCString)
33
+ r.src.should == o
34
+ end
35
+ end
36
+
37
+ it "#==" do
38
+ PCString.new("str").should == PCString.new("str")
39
+ PCString.new("&").should == "&"
40
+ PCString.new("&", true).should_not == "&"
41
+ PCString.new("&", true).should == PCString.new("&", true)
42
+ PCString.new("&").should == PCString.new("&amp;", true)
43
+ end
44
+
45
+ describe "#write_to" do
46
+ before do
47
+ @pc = PCString.new("&amp;")
48
+ end
49
+
50
+ it "should return encoded string" do
51
+ @pc.write_to.should == "&amp;amp;"
52
+ end
53
+
54
+ it "should return given destination" do
55
+ s = ""
56
+ @pc.write_to(s).should be_equal(s)
57
+ end
58
+ end
59
+ end
60
+
61
+ describe Comment do
62
+ it ".new should raise error if given string include '--'" do
63
+ lambda{Comment.new("--")}.should raise_error(ArgumentError)
64
+ end
65
+
66
+ describe "#write_to" do
67
+ it "should return comment with markup" do
68
+ Comment.new("flat comment").write_to.should == "<!-- flat comment -->"
69
+ Comment.new("multi-line\ncomment").write_to.should == "<!-- multi-line\ncomment -->"
70
+ Comment.new("&").write_to.should == "<!-- & -->"
71
+ end
72
+
73
+ it "should return given destination" do
74
+ s = ""
75
+ Comment.new("dummy").write_to(s).should be_equal(s)
76
+ end
77
+ end
78
+ end
79
+
80
+ describe Element do
81
+ class Dummy < Element
82
+ def chgname(name)
83
+ self.name = name
84
+ end
85
+ end
86
+
87
+ it "#name" do
88
+ e = Element.new("el")
89
+ e.name.should == :el
90
+ lambda{e.name="changed"}.should raise_error(NoMethodError)
91
+
92
+ d = Dummy.new("el1")
93
+ d.name.should == :el1
94
+ d.chgname(:el2)
95
+ d.name.should == :el2
96
+ d.chgname("el3")
97
+ d.name.should == :el3
98
+ end
99
+
100
+ it "#attributes should return hash whose keys are Symbol" do
101
+ e = Element.new("el", "a1"=>"v1", :a2=>"v2", "a3"=>nil)
102
+ e.name.should == :el
103
+ e.attributes.should == {:a1=>"v1", :a2=>"v2", :a3=>nil}
104
+ end
105
+
106
+ it "#[]" do
107
+ e = Element.new(:el, :attr=>"value")
108
+ e << "test"
109
+ e[:attr].should == "value"
110
+ e[0].should == "test"
111
+ end
112
+
113
+ it "#add_attribute" do
114
+ e = Element.new("el")
115
+ e.add_attribute("key_str", "value1")
116
+ e.add_attribute(:key_sym, "value2")
117
+ e.attributes.should == {:key_str=>"value1", :key_sym=>"value2"}
118
+ e.add_attribute(:nil, nil)
119
+ e.attributes.should == {:key_str=>"value1", :key_sym=>"value2", :nil=>nil}
120
+ end
121
+
122
+ it "#del_attribute" do
123
+ e = Element.new("el", {:a1=>"v1", :a2=>"v2"})
124
+ e.del_attribute("a1")
125
+ e.attributes.should == {:a2=>"v2"}
126
+ e.del_attribute(:a2)
127
+ e.attributes.should == {}
128
+ end
129
+
130
+ it "#contents" do
131
+ sub = Element.new("sub")
132
+ e = Element.new("el") << "String1" << "String2" << sub
133
+ e.contents.should == ["String1", "String2", sub]
134
+ end
135
+
136
+ it "#add" do
137
+ e = Element.new("el").add(Element.new("sub"))
138
+ e.should be_kind_of(Element)
139
+ e.name.should == :el
140
+
141
+ e = Element.new("el")
142
+ e.add(Element.new("sub1"))
143
+ e.add([Element.new("sub2").add("text"), "string"])
144
+ e.contents.should == [Element.new("sub1"), Element.new("sub2").add("text"), "string"]
145
+
146
+ e = Element.new("el")
147
+ e.add(nil)
148
+ e.contents.size.should == 0
149
+
150
+ e = Element.new("el").add(:symbol)
151
+ e.contents.should == [:symbol]
152
+ e.to_s.should == "<el>symbol</el>"
153
+
154
+ e = Element.new("super") << Element.new("sub")
155
+ e.name.should == :super
156
+ e.contents.should == [Element.new("sub")]
157
+ end
158
+
159
+ describe "#write_to" do
160
+ it "should return flatten string" do
161
+ Element.new("e").write_to.should == "<e />"
162
+
163
+ e = Element.new("super")
164
+ e << Element.new("sub")
165
+ e.write_to.should == "<super><sub /></super>"
166
+ e << Element.new("sub2")
167
+ e.write_to.should == "<super><sub /><sub2 /></super>"
168
+
169
+ e = Element.new("super") << "content1"
170
+ s = Element.new("sub")
171
+ s << "content2"
172
+ e << s
173
+ e.write_to.should == "<super>content1<sub>content2</sub></super>"
174
+
175
+ e = Element.new("el")
176
+ e.attributes["a1"] = "v1"
177
+ e.attributes["a2"] = "'\"<>&"
178
+ s = e.write_to
179
+ s.should =~ /\A<el ([^>]*) \/>\z/
180
+ s.should =~ /a1='v1'/
181
+ s.should =~ /a2='&apos;&quot;&lt;&gt;&amp;'/
182
+ end
183
+
184
+ it "should return string without attribute whose value is nil or false" do
185
+ s = EimXML::Element.new("e", :attr1=>"1", :attr2=>true, :attr3=>nil, :attr4=>false).write_to
186
+ re = /\A<e attr(.*?)='(.*?)' attr(.*?)='(.*?)' \/>\z/
187
+ s.should match(re)
188
+ s =~ /\A<e attr(.*?)='(.*?)' attr(.*?)='(.*?)' \/>\z/
189
+ [[$1, $2], [$3, $4]].sort.should == [["1", "1"], ["2", "true"]]
190
+ end
191
+
192
+ it "should return same string whenever name of element given with string or symbol" do
193
+ sym = Element.new(:tag, :attr=>"value")
194
+ str_name = Element.new("tag", :attr=>"value")
195
+ str_attr = Element.new(:tag, "attr"=>"value")
196
+
197
+ str_name.write_to.should == sym.write_to
198
+ str_attr.write_to.should == sym.write_to
199
+ end
200
+ end
201
+
202
+ it "encode special characters" do
203
+ e = Element.new("el") << "&\"'<>"
204
+ e << PCString.new("&\"'<>", true)
205
+ e.attributes["key"] = PCString.new("&\"'<>", true)
206
+ e.to_s.should == %[<el key='&\"'<>'>&amp;&quot;&apos;&lt;&gt;&\"'<></el>]
207
+ end
208
+
209
+ it "#dup" do
210
+ e = Element.new("el")
211
+ e.attributes["key"] = "value"
212
+ e << "String"
213
+ e << "Freeze".freeze
214
+ s = Element.new("sub")
215
+ s.attributes["subkey"] = "subvalue"
216
+ e << s
217
+ f = e.dup
218
+
219
+ f.attributes.object_id.should == e.attributes.object_id
220
+ f.contents.object_id.should == e.contents.object_id
221
+
222
+ f.to_s.should == e.to_s
223
+ end
224
+
225
+ it "#clone" do
226
+ e = Element.new("el")
227
+ e.attributes["key"] = "value"
228
+ e << "String"
229
+ e << "Freeze".freeze
230
+ s = Element.new("sub")
231
+ s.attributes["subkey"] = "subvalue"
232
+ e << s
233
+ f = e.clone
234
+
235
+ f.attributes.object_id.should == e.attributes.object_id
236
+ f.contents.object_id.should == e.contents.object_id
237
+
238
+ f.to_s.should == e.to_s
239
+ end
240
+
241
+ it "#==" do
242
+ e1 = Element.new("el")
243
+ e1.attributes["key"] = "value"
244
+ s = Element.new("sub")
245
+ s << "String"
246
+ e1 << s
247
+ e2 = e1.dup
248
+ e2.should == e1
249
+
250
+ e3 = Element.new("e")
251
+ e3.attributes["key"] = "value"
252
+ s = Element.new("sub")
253
+ s << "String"
254
+ e3 << s
255
+ e3.should_not == e1
256
+
257
+ e3 = Element.new("e")
258
+ e3.attributes["k"] = "value"
259
+ s = Element.new("sub")
260
+ s << "String"
261
+ e3 << s
262
+ e3.should_not == e1
263
+
264
+ e3 = Element.new("e")
265
+ e3.attributes["key"] = "v"
266
+ s = Element.new("sub")
267
+ s << "String"
268
+ e3 << s
269
+ e3.should_not == e1
270
+
271
+ e3 = Element.new("e")
272
+ e3.attributes["key"] = "value"
273
+ s = Element.new("sub")
274
+ s << "S"
275
+ e3 << s
276
+ e3.should_not == e1
277
+
278
+ e3 = Element.new("e")
279
+ e3.attributes["key"] = "value"
280
+ s = Element.new("s")
281
+ s << "String"
282
+ e3 << s
283
+ e3.should_not == e1
284
+
285
+ "string".should_not == e1
286
+ end
287
+
288
+ describe ".new" do
289
+ it "should convert name of attributes to Symbol" do
290
+ e = Element.new(:e, "a"=>"v")
291
+ e.attributes.keys.should == [:a]
292
+ e[:a].should == "v"
293
+ end
294
+
295
+ it "with block" do
296
+ base = nil
297
+ e = Element.new("base") do |b|
298
+ b["attr"]="value"
299
+ b << Element.new("sub")
300
+ base = b
301
+ end
302
+ base.object_id.should == e.object_id
303
+
304
+ e2 = Element.new("base", :attr=>"value")
305
+ e2 << Element.new("sub")
306
+ e2.should == e
307
+
308
+ e = Element.new("base") do |e|
309
+ e <<= Element.new("sub1") do |e|
310
+ e <<= Element.new("sub12")
311
+ end
312
+ e <<= Element.new("sub2")
313
+ end
314
+ base = Element.new("base")
315
+ sub1 = Element.new("sub1")
316
+ sub1 << Element.new("sub12")
317
+ sub2 = Element.new("sub2")
318
+ base << sub1 << sub2
319
+ e.should == base
320
+ end
321
+ end
322
+
323
+ it "#match" do
324
+ e = Element.new(:tag, :attr=>"value")
325
+ e.match(:tag).should be_true
326
+ e.match(:tag, :attr=>"value").should be_true
327
+ e.match(:t).should be_false
328
+ e.match(:tag, :attr2=>"value").should be_false
329
+ e.match(:tag, :attr=>"value2").should be_false
330
+ e.match(:tag, :attr=>/val/).should be_true
331
+
332
+ e.match(Element.new(:tag)).should be_true
333
+ e.match(Element.new(:tag, :attr=>"value")).should be_true
334
+ e.match(Element.new(:tag, :attr=>/alu/)).should be_true
335
+ e.match(Element.new(:t)).should be_false
336
+ e.match(Element.new(:tag, :attr2=>"value")).should be_false
337
+ e.match(Element.new(:tag, :attr=>"value2")).should be_false
338
+ e.match(Element.new(:tag, :attr=>/aul/)).should be_false
339
+ e.match(Element.new(:tag, :attr=>PCString.new("value"))).should be_true
340
+ Element.new(:tag, :attr=>PCString.new("value")).should match(e)
341
+
342
+ e.match(Element.new(:tag, :attr=>nil)).should be_false
343
+ e.match(Element.new(:tag, :nonattr=>nil)).should be_true
344
+
345
+ (!!e.match(/ag/)).should be_true
346
+ (!!e.match(/elem/)).should be_false
347
+
348
+ e.match(Element).should be_true
349
+ e.match(Dummy).should be_false
350
+ e.match(String).should be_false
351
+
352
+ e = Element.new(:element)
353
+ e << Element.new(:sub)
354
+ e << "text"
355
+ e.match(EDSL.element(:element){element(:sub)}).should be_true
356
+ e.match(EDSL.element(:element){element(:other)}).should be_false
357
+ e.match(EDSL.element(:element){add("text")}).should be_true
358
+ e.match(EDSL.element(:element){add("other")}).should be_false
359
+ e.match(EDSL.element(:element){add(/ex/)}).should be_true
360
+ e.match(EDSL.element(:element){add(/th/)}).should be_false
361
+ e.match(EDSL.element(:element){add(/sub/)}).should be_false
362
+
363
+ e = Element.new(:t, :a=>"&")
364
+ e.should match(Element.new(:t, :a=>"&"))
365
+ e.should match(Element.new(:t, :a=>PCString.new("&amp;", true)))
366
+ e.should match(Element.new(:t, :a=>PCString.new("&")))
367
+
368
+ Element.new(:t, "a"=>"v").should match(Element.new(:t, :a=>"v"))
369
+ end
370
+
371
+ it "#=~" do
372
+ e = Element.new(:tag, :attr=>"value", :a2=>"v2")
373
+ e.should =~ :tag
374
+ e.should =~ Element.new(:tag)
375
+ e.should =~ Element.new(:tag, :a2=>"v2")
376
+ e.should =~ Element.new(:tag, :attr=>/alu/)
377
+ e.should =~ Element.new(:tag, :attr=>PCString.new("value"))
378
+ e.should_not =~ :t
379
+ e.should_not =~ Element.new(:t)
380
+ e.should_not =~ Element.new(:tag, :attr=>/aul/)
381
+
382
+ e = Element.new(:t, :a=>"&")
383
+ e.should =~ Element.new(:t, :a=>"&")
384
+ e.should =~ Element.new(:t, :a=>PCString.new("&amp;", true))
385
+ e.should =~ Element.new(:t, :a=>PCString.new("&"))
386
+ end
387
+
388
+ %w[has? has_element? include?].each do |method|
389
+ it "##{method}" do
390
+ e = Element.new(:base) do |b|
391
+ b <<= Element.new(:sub) do |s|
392
+ s <<= Element.new(:deep) do |d|
393
+ d << "text"
394
+ d << PCString.new("&amp;", true)
395
+ d << "<"
396
+ end
397
+ end
398
+ b <<= Element.new(:sub, :attr=>"value")
399
+ end
400
+
401
+ e.send(method, :sub).should be_true
402
+ e.send(method, :sub, :attr=>"value").should be_true
403
+ e.send(method, :sub, :attr=>"value", :attr2=>"").should be_false
404
+ e.send(method, :deep).should be_true
405
+
406
+ e.send(method, String).should be_true
407
+ e.send(method, PCString).should be_true
408
+
409
+ d = Element.new(:deep)
410
+ d << "text"
411
+ d << PCString.new("&amp;", true)
412
+ d << "<"
413
+ e.send(method, d).should be_true
414
+
415
+ d = Element.new(:deep)
416
+ d << PCString.new("text", true)
417
+ d << "&"
418
+ d << PCString.new("&lt;", true)
419
+ e.send(method, d).should be_true
420
+ end
421
+ end
422
+
423
+ it "#find" do
424
+ s1 = Element.new(:sub)
425
+ d = Element.new(:deep)
426
+ d << "3rd"
427
+ s1 << "2nd" << d
428
+ s2 = Element.new(:sub, :attr=>"value")
429
+ e = Element.new(:base)
430
+ e << "1st" << s1 << s2
431
+
432
+ e.find(:deep).should be_kind_of(Element)
433
+ e.find(:deep).name.should == :found
434
+ e.find(:deep).contents.should == [d]
435
+ e.find(:sub).contents.should == [s1, s2]
436
+ e.find(//).contents.should == [e, s1, d, s2]
437
+ e.find(:sub, :attr=>"value").contents.should == [s2]
438
+ e.find(String).contents.should == ["1st", "2nd", "3rd"]
439
+ end
440
+ end
441
+ end