assert_xpath 0.4.2 → 0.4.6

Sign up to get free protection for your applications and to get access to all the features.
File without changes
@@ -0,0 +1,254 @@
1
+ # CONSIDER split the patch in two for separate submissions
2
+
3
+ ##################################################################
4
+ ## eek eek ook ook patch to add directives to RDocage
5
+
6
+ require 'rdoc/rdoc'
7
+ require 'rdoc/markup/simple_markup'
8
+ require 'rdoc/markup/simple_markup/lines'
9
+ require 'rdoc/markup/simple_markup/fragments'
10
+ require 'rdoc/markup/simple_markup/to_html'
11
+
12
+ # note: RDoc task fails if transcluder not found
13
+
14
+ def find_method_contents(file_info, modool, method)
15
+ file_info.each do |top|
16
+ if mod = top.find_module_named(modool) and
17
+ symbol = mod.find_local_symbol(method)
18
+ return symbol.token_stream
19
+ end
20
+ end
21
+ return nil
22
+ end
23
+
24
+ module RubyToken
25
+ class Token
26
+ def to_html
27
+ html = CGI.escapeHTML(text)
28
+
29
+ if text.size == 1 and '!@#$%^&*(){}?+/=[]\\|.,"\'<>-_;:'.include?(text)
30
+ return "<strong>#{ html }</strong>"
31
+ else
32
+ return html
33
+ end
34
+ end
35
+ end
36
+
37
+ class TkCOMMENT; def to_html; '<em style="color: #666; font-size: 110%">' + super + '</em>' ; end; end
38
+ class TkKW; def to_html; '<span style="color: #990099">' + super + '</span>' ; end; end
39
+ class TkIDENTIFIER; def to_html; '<span style="color: #003399">' + super + '</span>' ; end; end
40
+ class TkSYMBOL ; def to_html; '<span style="color: #006600">' + super + '</span>' ; end; end
41
+ class TkOp; def to_html; '<strong>' + super + '</strong>' ; end; end
42
+ class TkSTRING; def to_html; '<span style="background-color: #ddffdd">' + super + '</span>' ; end; end
43
+
44
+ #<RubyToken::TkASSIGN:0xb7870368 @text="=", @char_no=11, @line_no=346>
45
+ #<RubyToken::TkTRUE:0xb786ddac @text="true", @char_no=19, @line_no=347, @name="true">
46
+ #<RubyToken::TkCOMMA:0xb786db7c @text=",", @char_no=23, @line_no=347>
47
+ #<RubyToken::TkfLBRACK:0xb786d2d0 @text="[", @char_no=30, @line_no=347>
48
+ #<RubyToken::TkRBRACK:0xb786c8d0 @text="]", @char_no=39, @line_no=347>
49
+ #<RubyToken::TkFALSE:0xb7868488 @text="false", @char_no=19, @line_no=349, @name="false">
50
+
51
+ end
52
+
53
+ module SM
54
+
55
+ class Line
56
+ PURE_HTML = :PURE_HTML
57
+ PURE_RUBY = :PURE_RUBY
58
+ TRANSCLUDE = :TRANSCLUDE
59
+ end
60
+
61
+ class PureHTML < Fragment
62
+ type_name Line::PURE_HTML
63
+ end
64
+
65
+ class PureRUBY < Fragment
66
+ type_name Line::PURE_RUBY
67
+ end
68
+
69
+ class Transclude < Fragment
70
+ type_name Line::TRANSCLUDE
71
+ end
72
+
73
+ class ToHtml
74
+ def accept_transclude(am, fragment)
75
+ if @context.context.parent and
76
+ (found = find_method_contents(@context.context.parent.in_files, *fragment.txt.split('#')))
77
+ @res << '<pre style="background-color: white">'
78
+ # to do: hilite syntax; make method name clickable
79
+ found.each_with_index do |tok, index|
80
+ next if 0 == index and tok.text =~ /^\#/
81
+ next if 1 == index and tok.text == "\n"
82
+ @res << tok.to_html
83
+ end
84
+ @res << '</pre>'
85
+ else
86
+ raise "missing transclusion: #{ fragment.inspect }"
87
+ @res << fragment.txt
88
+ end
89
+ end
90
+
91
+ def accept_pure_html(am, fragment)
92
+ @res << fragment.txt
93
+ end
94
+
95
+ def accept_pure_ruby(am, fragment)
96
+ @res << eval(fragment.txt)
97
+ end
98
+ end
99
+
100
+ class LineCollection
101
+ def accept(am, visitor)
102
+ visitor.start_accepting
103
+
104
+ @fragments.each do |fragment|
105
+ case fragment
106
+ when Verbatim
107
+ visitor.accept_verbatim(am, fragment)
108
+ when PureHTML
109
+ visitor.accept_pure_html(am, fragment)
110
+ when PureRUBY
111
+ visitor.accept_pure_ruby(am, fragment)
112
+ when Transclude
113
+ visitor.accept_transclude(am, fragment)
114
+ when Rule
115
+ visitor.accept_rule(am, fragment)
116
+ when ListStart
117
+ visitor.accept_list_start(am, fragment)
118
+ when ListEnd
119
+ visitor.accept_list_end(am, fragment)
120
+ when ListItem
121
+ visitor.accept_list_item(am, fragment)
122
+ when BlankLine
123
+ visitor.accept_blank_line(am, fragment)
124
+ when Heading
125
+ visitor.accept_heading(am, fragment)
126
+ when Paragraph
127
+ visitor.accept_paragraph(am, fragment)
128
+ end
129
+ end
130
+
131
+ visitor.end_accepting
132
+ end
133
+
134
+ end
135
+
136
+ class SimpleMarkup
137
+ private
138
+
139
+ def assign_types_to_lines(margin = 0, level = 0)
140
+
141
+ while line = @lines.next
142
+
143
+ if /^\s*%html/ === line.text then
144
+ line.text.sub!("%html","")
145
+ line.stamp( Line::PURE_HTML, level )
146
+ next
147
+ end
148
+
149
+ if /^\s*%transclude/ === line.text then
150
+ line.text.sub!("%transclude","")
151
+ line.stamp( Line::TRANSCLUDE, level )
152
+ next
153
+ end
154
+
155
+ if /^\s*%ruby/ === line.text then
156
+ line.text.sub!("%ruby","")
157
+ line.stamp( Line::PURE_RUBY, level )
158
+ next
159
+ end
160
+
161
+ if line.isBlank?
162
+ line.stamp(Line::BLANK, level)
163
+ next
164
+ end
165
+
166
+ # if a line contains non-blanks before the margin, then it must belong
167
+ # to an outer level
168
+
169
+ text = line.text
170
+
171
+ for i in 0...margin
172
+ if text[i] != SPACE
173
+ @lines.unget
174
+ return
175
+ end
176
+ end
177
+
178
+ active_line = text[margin..-1]
179
+
180
+ # Rules (horizontal lines) look like
181
+ #
182
+ # --- (three or more hyphens)
183
+ #
184
+ # The more hyphens, the thicker the rule
185
+ #
186
+
187
+ if /^(---+)\s*$/ =~ active_line
188
+ line.stamp(Line::RULE, level, $1.length-2)
189
+ next
190
+ end
191
+
192
+ # Then look for list entries. First the ones that have to have
193
+ # text following them (* xxx, - xxx, and dd. xxx)
194
+
195
+ if SIMPLE_LIST_RE =~ active_line
196
+
197
+ offset = margin + $1.length
198
+ prefix = $2
199
+ prefix_length = prefix.length
200
+
201
+ flag = case prefix
202
+ when "*","-" then ListBase::BULLET
203
+ when /^\d/ then ListBase::NUMBER
204
+ when /^[A-Z]/ then ListBase::UPPERALPHA
205
+ when /^[a-z]/ then ListBase::LOWERALPHA
206
+ else raise "Invalid List Type: #{self.inspect}"
207
+ end
208
+
209
+ line.stamp(Line::LIST, level+1, prefix, flag)
210
+ text[margin, prefix_length] = " " * prefix_length
211
+ assign_types_to_lines(offset, level + 1)
212
+ next
213
+ end
214
+
215
+
216
+ if LABEL_LIST_RE =~ active_line
217
+ offset = margin + $1.length
218
+ prefix = $2
219
+ prefix_length = prefix.length
220
+
221
+ next if handled_labeled_list(line, level, margin, offset, prefix)
222
+ end
223
+
224
+ # Headings look like
225
+ # = Main heading
226
+ # == Second level
227
+ # === Third
228
+ #
229
+ # Headings reset the level to 0
230
+
231
+ if active_line[0] == ?= and active_line =~ /^(=+)\s*(.*)/
232
+ prefix_length = $1.length
233
+ prefix_length = 6 if prefix_length > 6
234
+ line.stamp(Line::HEADING, 0, prefix_length)
235
+ line.strip_leading(margin + prefix_length)
236
+ next
237
+ end
238
+
239
+ # If the character's a space, then we have verbatim text,
240
+ # otherwise
241
+
242
+ if active_line[0] == SPACE
243
+ line.strip_leading(margin) if margin > 0
244
+ line.stamp(Line::VERBATIM, level)
245
+ else
246
+ line.stamp(Line::PARAGRAPH, level)
247
+ end
248
+ end
249
+ end
250
+ end
251
+ end
252
+
253
+ ## eek eek ook ook patch to add directives to RDocage
254
+ ##################################################################
metadata CHANGED
@@ -1,66 +1,96 @@
1
1
  --- !ruby/object:Gem::Specification
2
- rubygems_version: 0.9.4
3
- specification_version: 1
4
2
  name: assert_xpath
5
3
  version: !ruby/object:Gem::Version
6
- version: 0.4.2
7
- date: 2008-04-13 00:00:00 -07:00
8
- summary: unit-test HTML at very high resolution
9
- require_paths:
10
- - lib
11
- email: phlip2005@gmail.com
12
- homepage: http://assertxpath.rubyforge.org/
13
- rubyforge_project: assertxpath
14
- description:
15
- autorequire:
16
- default_executable:
17
- bindir: bin
18
- has_rdoc: false
19
- required_ruby_version: !ruby/object:Gem::Version::Requirement
20
- requirements:
21
- - - ">"
22
- - !ruby/object:Gem::Version
23
- version: 0.0.0
24
- version:
4
+ version: 0.4.6
25
5
  platform: ruby
26
- signing_key:
27
- cert_chain:
28
- post_install_message:
29
6
  authors:
30
7
  - Phlip
31
- files:
32
- - lib/assert_javascript.rb
33
- - lib/assert_xpath.rb~
34
- - lib/jsToXml.pl
35
- - lib/assert_xpath.rb
36
- test_files: []
37
-
38
- rdoc_options: []
39
-
40
- extra_rdoc_files: []
41
-
42
- executables: []
43
-
44
- extensions: []
45
-
46
- requirements: []
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
47
11
 
12
+ date: 2008-09-28 00:00:00 -07:00
13
+ default_executable:
48
14
  dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: assert2
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
49
25
  - !ruby/object:Gem::Dependency
50
26
  name: rubynode
27
+ type: :runtime
51
28
  version_requirement:
52
- version_requirements: !ruby/object:Gem::Version::Requirement
29
+ version_requirements: !ruby/object:Gem::Requirement
53
30
  requirements:
54
- - - ">"
31
+ - - ">="
55
32
  - !ruby/object:Gem::Version
56
- version: 0.0.0
33
+ version: "0"
57
34
  version:
58
35
  - !ruby/object:Gem::Dependency
59
- name: assert2
36
+ name: css_parser
37
+ type: :runtime
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: "0"
44
+ version:
45
+ - !ruby/object:Gem::Dependency
46
+ name: libxml-ruby
47
+ type: :runtime
60
48
  version_requirement:
61
- version_requirements: !ruby/object:Gem::Version::Requirement
49
+ version_requirements: !ruby/object:Gem::Requirement
62
50
  requirements:
63
- - - ">"
51
+ - - ">="
64
52
  - !ruby/object:Gem::Version
65
- version: 0.0.0
53
+ version: "0"
66
54
  version:
55
+ description:
56
+ email: phlip2005@gmail.com
57
+ executables: []
58
+
59
+ extensions: []
60
+
61
+ extra_rdoc_files: []
62
+
63
+ files:
64
+ - lib/assert_css.rb
65
+ - lib/jsToXml.pl
66
+ - lib/rdoc_patch.rb
67
+ - lib/assert_xpath.rb
68
+ - lib/assert_javascript.rb
69
+ has_rdoc: false
70
+ homepage: http://assertxpath.rubyforge.org/
71
+ post_install_message:
72
+ rdoc_options: []
73
+
74
+ require_paths:
75
+ - lib
76
+ required_ruby_version: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: "0"
81
+ version:
82
+ required_rubygems_version: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ version: "0"
87
+ version:
88
+ requirements: []
89
+
90
+ rubyforge_project: assertxpath
91
+ rubygems_version: 1.2.0
92
+ signing_key:
93
+ specification_version: 2
94
+ summary: assert_xpath and deny_xpath call assert_ and deny_tag_id
95
+ test_files: []
96
+
@@ -1,997 +0,0 @@
1
- require 'rexml/document'
2
- require 'stringio'
3
- require 'libxml' # FIXME soften that requirement!
4
-
5
- RAILS_ENV = ENV.fetch('RAILS_ENV', 'test') unless defined?(RAILS_ENV)
6
- AFE = Test::Unit::AssertionFailedError unless defined?(AFE)
7
-
8
- =begin
9
- #:stopdoc:
10
- # "We had so much fun robbing the bank we forgot to take the money"
11
- #
12
- # ERGO comic book: programmers hunting bugs portrayed as big game hunters in jungle
13
-
14
- # ERGO use length not size
15
-
16
- # ERGO complain to ZenTest forum that
17
- # assert_in_epsilon should not blot out
18
- # its internal message if you add an external one
19
- - test_zentest_assertions should use assert_raise_message
20
- - we should use assert_include more
21
- - use util_capture more!
22
- - use Test::Rails::ControllerTestCase & HelperTestCase
23
- - use all the assertions in ViewTest (and compete!)
24
- - tell ZT their view assertions should nest
25
- - take target name out of render calls if the test case name is correct!
26
- - use path_parameters to override
27
- - use the LayoutsController dummy object trick!
28
- - use named routes more!
29
-
30
- # ERGO learn FormEncodedPairParser
31
- # ERGO RDoc a blog entry
32
- # ERGO write deny_match, and make it work correctly!!
33
- # ERGO link from Hpricot references to real Hpricot verbiage
34
- # ERGO <tt> -> <code> in RDoc!!
35
- # ERGO assert_xpath is nothing but a call to assert_any_xpath
36
- # ERGO two %transclude directives in a row should work
37
- # ERGO document, redundantly, that @xdoc always has bequeathed_attributes
38
- # ERGO complain to RDoc maintainers that {}[] targets the current frame!
39
- # monkey-patch thereof
40
- # ERGO link out to REXML::Element
41
- # ERGO how to RDoc format the sample for indent_xml etc?
42
- # ERGO how to syntax hilite the sample for indent_xml etc?
43
- # ERGO cross links in sample code
44
- # ERGO back RDoc with complete source
45
- # ERGO links from RDoc file reports in their contents
46
- # ERGO is search{} defined in terms of assert_any_xpath??
47
- # ERGO get Hpricot::search going for self-or-descendent, and take out all Hpricot::Doc cruft!
48
- # ERGO does Hpricot#Doc[] or #Elem[] conflict with anything?
49
- # ERGO merge the common bequeathed attributes into one module
50
- # indent_xml should find a system built-into Hpricot?
51
- #:startdoc:
52
- =end
53
-
54
-
55
- =begin rdoc
56
- See: http://assertxpath.rubyforge.org/
57
-
58
- %html <pre>
59
- ___________________________
60
- # __/ >tok tok tok< \
61
- #< <%< <__ assert_xpath reads XHTML |
62
- # (\\= | and queries its details! |
63
- # | ---------------------------
64
- #===={===
65
- #
66
- %html </pre>
67
-
68
- =end
69
-
70
-
71
- module AssertXPath
72
-
73
- module CommonXPathExtensions #:nodoc:
74
-
75
- def symbolic?(index)
76
- return index.to_s if (index.kind_of? String or index.kind_of? Symbol)
77
- end
78
-
79
- def[](index)
80
- if symbol = symbolic?(index)
81
- return attributes[symbol] if attributes.has_key? symbol
82
- raise_magic_member_not_found(symbol, caller)
83
- end
84
-
85
- return super # ERGO test this works?
86
- end
87
-
88
- def raise_magic_member_not_found(symbol, whats_caller_ERGO)
89
- raise AFE, # ERGO merge with other raiser(s)
90
- "missing attribute: `#{symbol}` in " +
91
- "<#{ name } #{ attributes.keys.join(' ') }>",
92
- whats_caller_ERGO
93
- end
94
-
95
- def identifiable?(str) # ERGO self. ?
96
- return str =~ /^ [[:alpha:]] [[:alnum:]_]* $/ix
97
- end # ERGO simplify??
98
-
99
- # ERGO mock the YarWiki and run its tests locally!!!
100
-
101
- def tribute(block)
102
- stash = {} # put back the ones we changed!
103
-
104
- if block
105
- varz = instance_variables
106
-
107
- attributes.each do |key, value|
108
- if identifiable?(key) # deal if the key ain't a valid variable
109
- key = "@#{ key }"
110
- stash[key] = instance_variable_get(key) if varz.include?(key)
111
- #p stash[key]
112
- instance_variable_set key, value
113
- end
114
- end
115
-
116
- return instance_eval(&block)
117
- end
118
- ensure # put them back!
119
- stash.each{|key, value| instance_variable_set(key, value) }
120
- end # this utterly sick convenience helps Ruby {@id} look like XPathic [@id]
121
-
122
- end
123
-
124
- # ERGO document me
125
- def drill(&block)
126
- if block
127
- # ERGO harmonize with bang! version
128
- # ERGO deal if the key ain't a valid variable
129
-
130
- unless tribute(block) # ERGO pass in self (node)?
131
- sib = self
132
- nil while (sib = sib.next_sibling) and sib.node_type != :element
133
- p sib # ERGO do tests ever get here?
134
- q = sib and _bequeath_attributes(sib).drill(&block)
135
- return sib if q
136
- raise Test::Unit::AssertionFailedError.new("can't find beyond <#{xpath}>")
137
- end
138
- end
139
-
140
- return self
141
- # ERGO if block returns false/nil, find siblings until it passes.
142
- # throw a test failure if it don't.
143
- # ERGO axis concept
144
- end
145
-
146
- end
147
-
148
- # ERGO node.descendant{ @type == 'text' and @id == 'foo' }.value
149
- # ERGO node..a_descendant - overload ..
150
-
151
- def _bequeath_attributes(node) #:nodoc:
152
- return node if node.kind_of?(::Hpricot::Elem) or node.kind_of?(::Hpricot::Doc)
153
- # ERGO shouldn't this be in a stinkin' module??
154
- # ERGO SIMPLER!!
155
- # ERGO document me
156
- def node.drill(&block)
157
- if block
158
- # ERGO harmonize with bang! version
159
- # ERGO deal if the key ain't a valid variable
160
-
161
- unless tribute(block) # ERGO pass in self (node)?
162
- sib = self
163
- nil while (sib = sib.next_sibling) and sib.node_type != :element
164
- q = sib and _bequeath_attributes(sib).drill(&block)
165
- return sib if q
166
- raise Test::Unit::AssertionFailedError.new("can't find beyond <#{ xpath }>")
167
- end
168
- end
169
-
170
- return self
171
- # ERGO if block returns false/nil, find siblings until it passes.
172
- # throw a test failure if it don't.
173
- # ERGO axis concept
174
- end
175
-
176
- return node # ERGO use this return value
177
- end # ERGO is _ a good RPP for a "pretend private"?
178
-
179
-
180
- module AssertXPath
181
-
182
- Element = ::REXML::Element unless defined?(Element) #:nodoc:
183
-
184
- class XmlHelper #:nodoc:
185
- def libxml? ; false end # this is not a 'downcast' (bad in OO)
186
- def rexml? ; false end # becase diverse libraries are a "boundary"
187
- def hpricot? ; false end # situation. We can't control their contents!
188
- end
189
-
190
- class HpricotHelper < XmlHelper #:nodoc:
191
- def hpricot? ; true end
192
- def symbol_to_xpath(tag) tag.to_s end
193
- def assert_xml(suite, *args, &block)
194
- return suite.assert_hpricot(*args, &block)
195
- end
196
- end
197
-
198
- class LibxmlHelper < XmlHelper #:nodoc:
199
- def libxml? ; true end
200
- def symbol_to_xpath(tag) "descendant-or-self::#{tag}" end
201
- def assert_xml(suite, *args, &block)
202
- return suite.assert_libxml(*args, &block)
203
- end
204
- end
205
-
206
- class RexmlHelper < XmlHelper #:nodoc:
207
- def rexml? ; true end
208
- def symbol_to_xpath(tag) ".//#{tag}" end
209
- def assert_xml(suite, *args, &block)
210
- return suite.assert_rexml(*args, &block)
211
- end
212
- end
213
-
214
- # Subsequent +assert_xml+ calls will use
215
- # Hpricot[http://code.whytheluckystiff.net/hpricot/].
216
- # (Alternately,
217
- # +assert_hpricot+ will run one assertion in Hpricot mode.)
218
- # Put +invoke_hpricot+ into +setup+() method, to
219
- # run entire suites in this mode. These test cases
220
- # explore some differences between the two assertion systems:
221
- # %transclude AssertXPathSuite#test_assert_long_xpath
222
- #
223
- def invoke_hpricot
224
- @xdoc = nil
225
- @helper = HpricotHelper.new
226
- end
227
-
228
- # Subsequent +assert_xml+ calls will use
229
- # LibXML[http://libxml.rubyforge.org/].
230
- # (Alternately,
231
- # +assert_libxml+ will run one assertion in Hpricot mode.)
232
- # Put +invoke_libxml+ into +setup+() method, to
233
- # run entire suites in this mode.
234
- #
235
- def invoke_libxml(favorite_flavor = :html)
236
- @_favorite_flavor = favorite_flavor
237
- @xdoc = nil
238
- @helper = LibxmlHelper.new
239
- end
240
-
241
- def _doc_type # ERGO document all these!
242
- { :html => '<!DOCTYPE HTML PUBLIC ' +
243
- '"-//W3C//DTD HTML 4.01 Transitional//EN" '+
244
- '"http://www.w3.org/TR/html4/loose.dtd">',
245
- :xhtml => '<!DOCTYPE html PUBLIC ' +
246
- '"-//W3C//DTD XHTML 1.0 Transitional//EN" ' +
247
- '"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" >',
248
- :xml => nil
249
- }.freeze
250
- end
251
- private :_doc_type
252
-
253
- # ERGO what happens to assert_js_replace_html bearing entities??
254
-
255
- # Subsequent +assert_xml+ calls will use REXML. See
256
- # +invoke_hpricot+ to learn the various differences between the
257
- # two systems
258
- def invoke_rexml
259
- @xdoc = nil
260
- @helper = RexmlHelper.new
261
- end
262
-
263
- # %html <a name='assert_xml'></a>
264
- #
265
- # Prepare XML for assert_xpath <em>et al</em>
266
- # * +xml+ - optional string containing XML. Without it, we read <code>@response.body</code>
267
- # * <code>xpath, diagnostic, block</code> - optional arguments passed to +assert_xpath+
268
- # Sets and returns the new secret <code>@xdoc</code> REXML::Element root
269
- # call-seq:
270
- # assert_xml(xml = @response.body <em>[, assert_xpath arguments]</em>) -> @xdoc, or assert_xpath's return value
271
- #
272
- # Assertions based on +assert_xpath+ will call this automatically if
273
- # the secret <code>@xdoc</code> is +nil+. This implies we may freely call
274
- # +assert_xpath+ after any method that populates <code>@response.body</code>
275
- # -- if <code>@xdoc</code> is +nil+. When in doubt, call +assert_xml+ explicitly
276
- #
277
- # +assert_xml+ also translates the contents of +assert_select+ nodes. Use this to
278
- # bridge assertions from one system to another. For example:
279
- #
280
- # Returns the first node in the XML
281
- #
282
- # Examples:
283
- # assert_select 'div#home_page' do |home_page|
284
- # assert_xml home_page # <-- calls home_page.to_s
285
- # assert_xpath ".//img[ @src = '#{newb.image_uri(self)}' ]"
286
- # deny_tag_id :form, :edit_user
287
- # end
288
- #
289
- # %transclude AssertXPathSuite#test_assert_long_sick_expression
290
- # See: AssertXPathSuite#test_assert_xml_drill
291
- #
292
- def assert_xml(*args, &block)
293
- using :libxml? # prop-ulates @helper
294
- return @helper.assert_xml(self, *args, &block)
295
- end # ERGO take out the rescue nil!, and pass the diagnostic thru
296
-
297
- # Processes a string of text, or the hidden <code>@response.body</code>,
298
- # using REXML, and sets the hidden <code>@xdoc</code> node. Does
299
- # not depend on, or change, the values of +invoke_hpricot+, +invoke_libxml+,
300
- # or +invoke_rexml+
301
- #
302
- # Example:
303
- # %transclude AssertXPathSuite#test_assert_rexml
304
- #
305
- def assert_rexml(*args, &block)
306
- contents = (args.shift || @response.body).to_s
307
- # ERGO benchmark these things
308
-
309
- contents.gsub!('\\\'', '&apos;')
310
- contents.gsub!('//<![CDATA[<![CDATA[', '')
311
- contents.gsub!('//<![CDATA[', '')
312
- contents.gsub!('//]]>', '')
313
- contents.gsub!('//]>', '')
314
- contents.gsub!('//]]', '')
315
- contents.gsub!('//]', '')
316
-
317
- begin
318
- @xdoc = REXML::Document.new(contents)
319
- rescue REXML::ParseException => e
320
- raise e unless e.message =~ /attempted adding second root element to document/
321
- @xdoc = REXML::Document.new("<xhtml>#{ contents }</xhtml>")
322
- end
323
-
324
- _bequeath_attributes(@xdoc)
325
- assert_xpath(*args, &block) if args != []
326
- return (assert_xpath('/*') rescue nil) if @xdoc
327
- end
328
-
329
- # Temporarily sets the validation type to :xml, :html, or :xhtml
330
- #
331
- def validate_as(type) # FIXME use or lose this
332
- @_favorite_flavor, formerly = type, @_favorite_flavor
333
- yield
334
- ensure
335
- @_favorite_flavor = type
336
- end # ERGO more documentation!
337
-
338
- def assert_libxml(*args, &block)
339
- xml = args.shift || @xdoc || @response.body
340
- xhtml = xml.to_s
341
-
342
- # CONSIDER fix this like at the source??
343
- xhtml.gsub!('<![CDATA[>', '')
344
- xhtml.gsub!('<![CDATA[', '')
345
- xhtml.gsub!('//]]]]>', '')
346
- xhtml.gsub!(']]>', '')
347
-
348
- if xhtml !~ /^\<\!DOCTYPE\b/ and xhtml !~ /\<\?xml\b/
349
- xhtml = _doc_type[@_favorite_flavor || :html] + "\n" + xhtml if _doc_type[@_favorite_flavor]
350
- end # ERGO document we pass HTML level into invoker
351
-
352
- if xhtml.index('<?xml version="1" standalone="yes"?>') == 0
353
- xhtml.gsub!('<?xml version="1" standalone="yes"?>', '')
354
- xhtml.strip! # ERGO what is libxml's problem with that line???
355
- end
356
-
357
- # # FIXME blog that libxml will fully validate your ass...
358
-
359
- xp = xhtml =~ /\<\!DOCTYPE/ ? XML::HTMLParser.new() : XML::Parser.new()
360
- xhtml = '<xml/>' unless xhtml.any?
361
- xp.string = xhtml
362
- # FIXME blog we don't work with libxml-ruby 3.8.4
363
- # XML::Parser.default_load_external_dtd = false
364
- XML::Parser.default_pedantic_parser = false # FIXME optionalize that
365
-
366
- #what? xp
367
- doc = xp.parse
368
- #what? doc
369
- #puts doc.debug_dump
370
- @xdoc = doc.root
371
- # @xdoc.namespace ||= XML::NS.new('')
372
-
373
- #pp (@xdoc.root.public_methods - public_methods).sort
374
- return assert_xpath(*args, &block) if args.length > 0
375
- return @xdoc
376
- end
377
-
378
- def using(kode)
379
- @helper ||= RexmlHelper.new # ERGO escallate this!
380
- return @helper.send(kode)
381
- end
382
-
383
- # FIXME test that the helper system withstands this effect:
384
- # ""
385
-
386
- # %html <a name='assert_hpricot'></a>
387
- #
388
- # This parses one XML string using Hpricot, so subsequent
389
- # calls to +assert_xpath+ will use Hpricot expressions.
390
- # This method does not depend on +invoke_hpricot+, and
391
- # subsequent test cases will run in their suite's mode.
392
- #
393
- # Example:
394
- # %transclude AssertXPathSuite#test_assert_hpricot
395
- #
396
- # See also: assert_hpricot[http://www.oreillynet.com/onlamp/blog/2007/08/assert_hpricot_1.html]
397
- #
398
- def assert_hpricot(*args, &block)
399
- xml = args.shift || @xdoc || @response.body ## ERGO why @xdoc??
400
- # ERGO document that callseq!
401
- require 'hpricot'
402
- @xdoc = Hpricot(xml.to_s) # ERGO take that to_s out of all callers
403
- return assert_xpath(*args, &block) if args.length > 0
404
- return @xdoc
405
- end # ERGO reasonable error message if ill-formed
406
-
407
- # ENCORE Bus to Julian! (-;
408
- # ERGO why %html <a name='assert_xpath' /> screws up?
409
-
410
- # %html <a name='assert_xpath'></a>
411
- #
412
- # Return the first XML node matching a query string. Depends on +assert_xml+
413
- # to populate our secret internal REXML::Element, <code>@xdoc</code>
414
- # * +xpath+ - a query string describing a path among XML nodes.
415
- # See: {XPath Tutorial Roundup}[http://krow.livejournal.com/523993.html]
416
- # * +diagnostic+ - optional string to add to failure message
417
- # * <code>block|node|</code> - optional block containing assertions, based on +assert_xpath+,
418
- # which operate on this node as the XPath '.' current +node+
419
- # Returns the obtained REXML::Element +node+
420
- #
421
- # Examples:
422
- #
423
- # render :partial => 'my_partial'
424
- #
425
- # assert_xpath '/table' do |table|
426
- # assert_xpath './/p[ @class = "brown_text" ]/a' do |a|
427
- # assert_equal user.login, a.text # <-- native <code>REXML::Element#text</code> method
428
- # assert_match /\/my_name$/, a[:href] # <-- attribute generated by +assert_xpath+
429
- # end
430
- # assert_equal "ring_#{ring.id}", table.id! # <-- attribute generated by +assert_xpath+, escaped with !
431
- # end
432
- #
433
- # %transclude AssertXPathSuite#test_assert_xpath
434
- #
435
- # See: AssertXPathSuite#test_indent_xml,
436
- # {XPath Checker}[https://addons.mozilla.org/en-US/firefox/addon/1095]
437
- #
438
- def assert_xpath(xpath, diagnostic = nil, &block)
439
- # return assert_any_xpath(xpath, diagnostic) {
440
- # block.call(@xdoc) if block
441
- # true
442
- # }
443
- stash_xdoc do
444
- xpath = symbol_to_xpath(xpath)
445
- node = @xdoc.search(xpath).first
446
- @xdoc = node || flunk_xpath(diagnostic, "should find xpath <#{_esc xpath}>")
447
- @xdoc = _bequeath_attributes(@xdoc)
448
- block.call(@xdoc) if block # ERGO tribute here?
449
- return @xdoc
450
- end
451
- end
452
-
453
- # FIXME assert_js_argument(2) gotta return nill for nada
454
-
455
- # Negates +assert_xpath+. Depends on +assert_xml+
456
- #
457
- # Examples:
458
- # assert_tag_id :td, :object_list do
459
- # assert_xpath "table[ position() = 1 and @id = 'object_#{object1.id}' ]"
460
- # deny_xpath "table[ position() = 2 and @id = 'object_#{object2.id}' ]"
461
- # end # find object1 is still displayed, but object2 is not in position 2
462
- #
463
- # %transclude AssertXPathSuite#test_deny_xpath
464
- #
465
- def deny_xpath(xpath, diagnostic = nil)
466
- @xdoc or assert_xml
467
- xpath = symbol_to_xpath(xpath)
468
-
469
- @xdoc.search(xpath).first and
470
- flunk_xpath(diagnostic, "should not find: <#{_esc xpath}>")
471
- end
472
-
473
- # Search nodes for a matching XPath whose <code>AssertXPath::Element#inner_text</code> matches a Regular Expression. Depends on +assert_xml+
474
- # * +xpath+ - a query string describing a path among XML nodes.
475
- # See: {XPath Tutorial Roundup}[http://krow.livejournal.com/523993.html]
476
- # * +matcher+ - optional Regular Expression to test node contents
477
- # * +diagnostic+ - optional string to add to failure message
478
- # * <code>block|node|</code> - optional block called once per match.
479
- # If this block returns a value other than +false+ or +nil+,
480
- # assert_any_xpath stops looping and returns the current +node+
481
- #
482
- # Example:
483
- # %transclude AssertXPathSuite#test_assert_any_xpath
484
- #
485
- def assert_any_xpath(xpath, matcher = nil, diagnostic = nil, &block)
486
- matcher ||= //
487
- block ||= lambda{ true }
488
- found_any = false
489
- found_match = false
490
- xpath = symbol_to_xpath(xpath)
491
-
492
- stash_xdoc do
493
- #assert_xpath xpath, diagnostic
494
-
495
- if !using(:rexml?)
496
- @xdoc.search(xpath) do |@xdoc|
497
- found_any = true
498
-
499
- if @xdoc.inner_text =~ matcher
500
- found_match = true
501
- _bequeath_attributes(@xdoc)
502
- return @xdoc if block.call(@xdoc)
503
- # note we only exit block if block.nil? or call returns false
504
- end
505
- end
506
- else # ERGO merge!
507
- @xdoc.each_element(xpath) do |@xdoc|
508
- found_any = true
509
-
510
- if @xdoc.inner_text =~ matcher
511
- found_match = true
512
- _bequeath_attributes(@xdoc)
513
- return @xdoc if block.call(@xdoc)
514
- # note we only exit block if block.nil? or call returns false
515
- end
516
- end
517
- end
518
- end
519
-
520
- found_any or
521
- flunk_xpath(diagnostic, "should find xpath <#{_esc xpath}>")
522
-
523
- found_match or
524
- flunk_xpath(
525
- diagnostic,
526
- "can find xpath <#{_esc xpath}> but can't find pattern <?>",
527
- matcher
528
- )
529
- end
530
-
531
- # Negates +assert_any_xpath+. Depends on +assert_xml+
532
- #
533
- # * +xpath+ - a query string describing a path among XML nodes. This
534
- # must succeed - use +deny_xpath+ for simple queries that must fail
535
- # * +matcher+ - optional Regular Expression to test node contents. If +xpath+ locates multiple nodes,
536
- # this pattern must fail to match each node to pass the assertion.
537
- # * +diagnostic+ - optional string to add to failure message
538
- #
539
- # Contrived example:
540
- # assert_xml '<heathrow><terminal>5</terminal><lean>methods</lean></heathrow>'
541
- #
542
- # assert_raise_message Test::Unit::AssertionFailedError,
543
- # /all xpath.*\.\/\/lean.*not have.*methods/ do
544
- # deny_any_xpath :lean, /methods/
545
- # end
546
- #
547
- # deny_any_xpath :lean, /denver/
548
- #
549
- # See: AssertXPathSuite#test_deny_any_xpath,
550
- # {assert_raise (on Ruby) - Don't Just Say "No"}[http://www.oreillynet.com/onlamp/blog/2007/07/assert_raise_on_ruby_dont_just.html]
551
- #
552
- def deny_any_xpath(xpath, matcher, diagnostic = nil)
553
- @xdoc or assert_xml
554
- xpath = symbol_to_xpath(xpath)
555
-
556
- assert_any_xpath xpath, nil, diagnostic do |node|
557
- if node.inner_text =~ matcher
558
- flunk_xpath(
559
- diagnostic,
560
- "all xpath <#{_esc xpath}> nodes should not have pattern <?>",
561
- matcher
562
- )
563
- end
564
- end
565
- end
566
-
567
- # FIXME @helper -> @_helper
568
-
569
- # Wraps the common idiom <code>assert_xpath('descendant-or-self::./<em>my_tag</em>[ @id = "<em>my_id</em>" ]')</code>. Depends on +assert_xml+
570
- # * +tag+ - an XML node name, such as +div+ or +input+.
571
- # If this is a <code>:symbol</code>, we prefix "<code>.//</code>"
572
- # * +id+ - string, symbol, or hash identifying the node. ids must not contain punctuation
573
- # * +diagnostic+ - optional string to add to failure message
574
- # * <code>block|node|</code> - optional block containing assertions, based on
575
- # +assert_xpath+, which operate on this node as the XPath '.' current node.
576
- # Returns the obtained REXML::Element +node+
577
- #
578
- # Examples:
579
- #
580
- # assert_tag_id '/span/div', "audience_#{ring.id}" do
581
- # assert_xpath 'table/tr/td[1]' do |td|
582
- # #...
583
- # assert_tag_id :form, :for_sale
584
- # end
585
- # end
586
- #
587
- # %transclude AssertXPathSuite#test_assert_tag_id_and_tidy
588
- #
589
- # %transclude AssertXPathSuite#test_assert_tag_id
590
- #
591
- def assert_tag_id(tag, id = {}, diagnostic = nil, diagnostic2 = nil, &block)
592
- # if id is not a hash, diagnostic might be a hash too!
593
- # CONSIDER upgrade assert_tag_id to use each_element_with_attribute
594
- assert_xpath build_xpath(tag, id, diagnostic), diagnostic2 || diagnostic, &block
595
- end # NOTE: ids may not contain ', so we are delimiter-safe
596
-
597
- # Negates +assert_tag_id+. Depends on +assert_xml+
598
- #
599
- # Example - see: +assert_xml+
600
- #
601
- # See: +assert_tag_id+
602
- #
603
- def deny_tag_id(tag, id, diagnostic = nil, diagnostic2 = nil)
604
- deny_xpath build_xpath(tag, id, diagnostic), diagnostic2 || diagnostic
605
- end
606
-
607
- # Pretty-print a REXML::Element or Hpricot::Elem
608
- # * +doc+ - optional element. Defaults to the current +assert_xml+ document
609
- # returns: string with indented XML
610
- #
611
- # Use this while developing a test case, to see what
612
- # the current <code>@xdoc</code> node contains (as populated by +assert_xml+ and
613
- # manipulated by +assert_xpath+ <em>et al</em>)
614
- #
615
- # For example:
616
- # assert_javascript 'if(x == 42) answer_great_question();'
617
- #
618
- # assert_js_if /x.*42/ do
619
- # puts indent_xml # <-- temporary statement to see what to assert next!
620
- # end
621
- #
622
- # See: AssertXPathSuite#test_indent_xml
623
- #
624
- def indent_xml(doc = @xdoc || assert_xml)
625
- if doc.kind_of?(Hpricot::Elem) or doc.kind_of?(Hpricot::Doc)
626
- zdoc = doc
627
- doc = REXML::Document.new(doc.to_s.strip) rescue nil
628
- unless doc # Hpricot didn't well-formify the HTML!
629
- return zdoc.to_s # note: not indented, but good enough for error messages
630
- end
631
- end
632
-
633
- # require 'rexml/formatters/default'
634
- # bar = REXML::Formatters::Pretty.new
635
- # out = String.new
636
- # bar.write(doc, out)
637
- # return out
638
-
639
- return doc.to_s # ERGO reconcile with 1.8.6.111!
640
-
641
- x = StringIO.new
642
- doc.write(x, 2)
643
- return x.string # CONSIDER does REXML have a simpler way?
644
- end
645
-
646
- # %html <a name='assert_tidy'></a>
647
- # Thin wrapper on the Tidy command line program (the one released 2005 September)
648
- # * +messy+ - optional string containing messy HTML. Defaults to <code>@response.body</code>.
649
- # * +verbosity+ - optional noise level. Defaults to <code>:noisy</code>, which
650
- # reports most errors. :verbose reports all information, and other value
651
- # will repress all of Tidy's screams of horror regarding the quality of your HTML.
652
- # The resulting XHTML loads into +assert_xml+. Use this to retrofit +assert_xpath+ tests
653
- # to less-than-pristine HTML.
654
- #
655
- # +assert_tidy+ obeys +invoke_rexml+ and +invoke_hpricot+, to
656
- # select its HTML parser
657
- #
658
- # Examples:
659
- #
660
- # get :adjust, :id => transaction.id # <-- fetches ill-formed HTML
661
- # assert_tidy @response.body, :quiet # <-- upgrades it to well-formed
662
- # assert_tag_id '//table', :payment_history do # <-- sees good XML
663
- # #...
664
- # end
665
- #
666
- # %transclude AssertXPathSuite#test_assert_tag_id_and_tidy
667
- #
668
- def assert_tidy(messy = @response.body, verbosity = :noisy)
669
- scratch_html = RAILS_ROOT + '/tmp/scratch.html'
670
- # CONSIDER a railsoid tmp file system?
671
- # CONSIDER yield to something to respond to errors?
672
- File.open(scratch_html, 'w'){|f| f.write(messy) }
673
- gripes = `tidy -eq #{scratch_html} 2>&1`
674
- gripes.split("\n")
675
-
676
- # TODO kvetch about client_input_channel_req: channel 0 rtype keepalive@openssh.com reply 1
677
-
678
- puts gripes if verbosity == :verbose
679
-
680
- puts gripes.reject{|g|
681
- g =~ / - Info\: / or
682
- g =~ /Warning\: missing \<\!DOCTYPE\> declaration/ or
683
- g =~ /proprietary attribute/ or
684
- g =~ /lacks "(summary|alt)" attribute/
685
- } if verbosity == :noisy
686
-
687
- assert_xml `tidy -wrap 1001 -asxhtml #{ scratch_html } 2>/dev/null`
688
- # CONSIDER that should report serious HTML deformities
689
- end # CONSIDER how to tidy <% escaped %> eRB code??
690
-
691
- # FIXME write a plugin for cruisecontrol.rb
692
- # that links metrics to Gruff per project
693
- # (and link from assert2.rf.org to rf.org/projects/assert2
694
-
695
- private
696
-
697
- # ERGO switch to descendant-or-self
698
- # ERGO then update documentation of those who use this
699
- def symbol_to_xpath(tag)
700
- return tag unless tag.class == Symbol
701
- @helper or using :libxml? # prop-ulates @helper
702
- return @helper.symbol_to_xpath(tag)
703
- end
704
-
705
- def build_xpath(tag, id, diagnostic)
706
- options = ( case id
707
- when Symbol, String ; { :id => id }
708
- when Hash ; id
709
- end )
710
-
711
- options.merge!(diagnostic) if diagnostic.kind_of? Hash
712
- predicate = options.map{|k,v| "@#{k} = '#{v}'" }.join('')
713
- return symbol_to_xpath(tag) + "[ #{predicate} ]"
714
- end
715
-
716
- def stash_xdoc
717
- former_xdoc = @xdoc || assert_xml
718
- yield ensure @xdoc = former_xdoc
719
- end
720
-
721
- def flunk_xpath(diagnostic, template, *args) #:nodoc:
722
- xml = _esc(indent_xml).relevance || '(@xdoc is blank!)'
723
- flunk build_message(diagnostic, "#{template} in\n#{xml}", *args)
724
- end
725
-
726
- end
727
-
728
-
729
- def _esc(x) #:nodoc:
730
- return x.gsub('?', '\?')
731
- end
732
-
733
-
734
- #####################################################
735
-
736
- # FIXME got_libxml?
737
-
738
- # ERGO hpricot gets its own module (REXML-free!)
739
-
740
- def got_hpricot? # ERGO help tests pass without it
741
- require 'hpricot'
742
- return true
743
- rescue MissingSourceFile
744
- return false
745
- end
746
-
747
- #####################################################
748
-
749
- # parking some tiny conveniences here,
750
- # where even production code can get to them...
751
- module Relevate
752
- def relevant?
753
- return ! blank?
754
- end
755
-
756
- def relevance
757
- return to_s if relevant?
758
- end
759
- end
760
-
761
- # ERGO dry these up
762
- class String
763
- def blank?
764
- return strip.size == 0
765
- end
766
- end
767
-
768
- class NilClass
769
- def blank?; true; end
770
- end
771
-
772
- # ERGO include our test modules like this too
773
- # ERGO seek relevant? calls that could use relevance
774
-
775
- NilClass.send :include, Relevate
776
- String .send :include, Relevate
777
-
778
- #:enddoc:
779
-
780
- # props: http://www.intertwingly.net/blog/2007/11/02/MonkeyPatch-for-Ruby-1-8-6
781
- doc = REXML::Document.new '<doc xmlns="ns"><item name="foo"/></doc>'
782
- if not doc.root.elements["item[@name='foo']"]
783
- class REXML::Element
784
- def attribute( name, namespace=nil )
785
- prefix = nil
786
- prefix = namespaces.index(namespace) if namespace
787
- prefix = nil if prefix == 'xmlns'
788
- attributes.get_attribute( "#{prefix ? prefix + ':' : ''}#{name}" )
789
- end
790
- end
791
- end
792
-
793
-
794
- # These monkey patches push Hpricot behavior closer to our customized REXML behavior
795
- module Hpricot #:nodoc:
796
- class Doc #:nodoc:
797
- include AssertXPath::CommonXPathExtensions
798
-
799
- def [](index) #:nodoc:
800
- return root[index] if symbolic? index
801
- super
802
- end
803
-
804
- def text
805
- return root.text
806
- end
807
-
808
- def method_missing(*args, &block) #:nodoc:
809
- # if got = search(symbol).first get first descendant working here!
810
- # ERGO call root here
811
- symbol = args.first.to_s.sub(/\!$/, '')
812
-
813
- root.children.grep(Hpricot::Elem).each do |kid|
814
- if kid.name == symbol
815
- return kid.drill(&block)
816
- # ERGO assert on return value
817
- # ERGO pass kid in for if you want it
818
- end
819
- end
820
- # ERGO raise here?
821
- end
822
- end
823
-
824
- class Elem #:nodoc:
825
- include AssertXPath::CommonXPathExtensions
826
-
827
- def [](index) #:nodoc:
828
- # ERGO do this conflict with anything?
829
- if symbol = symbolic?(index)
830
- return attributes[symbol] if attributes.has_key? symbol
831
- raise_magic_member_not_found(symbol, caller)
832
- end
833
-
834
- super
835
- end
836
-
837
- def text # simulate REXML style - fetch child with text
838
- return (text? ? to_s : '') + children.select(&:text?).map(&:to_s).compact.join
839
- end
840
-
841
- def node_type
842
- return :element # ERGO make me less useless
843
- end
844
-
845
- def drill(&block)
846
- if block
847
- # ERGO harmonize with bang! version
848
- # ERGO deal if the key ain't a valid variable
849
- # ERGO get method_missing to stop returning []
850
- unless tribute(block) # ERGO pass in self (node)?
851
- sib = self
852
- nil while (sib = sib.next_sibling) and sib.node_type != :element
853
- q = sib and _bequeath_attributes(sib).drill(&block)
854
- return sib if q
855
- raise Test::Unit::AssertionFailedError.new("can't find beyond <#{_esc xpath}>")
856
- end
857
- end
858
-
859
- return self
860
- # ERGO if block returns false/nil, find siblings until it passes.
861
- # throw a test failure if it don't.
862
- # ERGO axis concept
863
- end
864
-
865
- def method_missing(*args, &block) #:nodoc:
866
- symbol = args.shift.to_s.sub(/\!$/, '')
867
-
868
- children.grep(Hpricot::Elem).each do |kid|
869
- if kid.name == symbol
870
- kid.tribute(block)
871
- # ERGO assert on return value
872
- # ERGO pass kid in for if you want it
873
- return kid
874
- end
875
- end
876
-
877
- raise_magic_member_not_found(symbol, caller) # ERGO repurpose!
878
- end
879
- end
880
-
881
- end
882
-
883
-
884
- module XML
885
- class Node #:nodoc:
886
- include AssertXPath::CommonXPathExtensions
887
-
888
- def search(xpath, &block)
889
- if block
890
- find(xpath, "x:http://www.w3.org/1999/xhtml").each(&block)
891
- #find(xpath, &block)
892
- end
893
- return [find_first(xpath, "x:http://www.w3.org/1999/xhtml")]
894
- end
895
- alias each_element search
896
-
897
- def text
898
- #p text?
899
- find_first('text()').to_s
900
- #text? ? content : ''
901
- end
902
-
903
- def inner_text(interstitial = '')
904
- array = []
905
- self.find( 'descendant-or-self::text()' ).each{|x| array << x }
906
- return array.join(interstitial)
907
- end # ERGO match??
908
-
909
- def attributes
910
- hash = {}
911
- each_attr{|attr| hash[attr.name] = attr.value }
912
- return hash # ERGO uh, was there a leaner way??
913
- end
914
-
915
- def [](index) #:nodoc:
916
- return attributes[index.to_s] || super
917
- end
918
-
919
- def get_path(xpath)
920
- node = find_first(xpath.to_s)
921
- return _bequeath_attributes(node) if node
922
- end # ERGO test that attributes are bequeathed!
923
-
924
- alias :/ get_path
925
-
926
- def method_missing(*args, &block) #:nodoc:
927
- # ERGO use the define_method trick here
928
- symbol = args.shift.to_s
929
- symbol.sub!(/\!$/, '')
930
-
931
- kid = if symbol == '/'
932
- find_first('/')
933
- else
934
- find_first("./#{symbol}")
935
- end
936
- return _bequeath_attributes(kid).drill(&block) if kid
937
- raise_magic_member_not_found(symbol, caller)
938
- end
939
- end
940
-
941
- end
942
-
943
-
944
- class REXML::Element
945
- include AssertXPath::CommonXPathExtensions
946
-
947
- # Semi-private method to match Hpricotic abilities
948
- def search(xpath)
949
- return self.each_element( xpath ){}
950
- end
951
-
952
- def method_missing(*args, &block) #:nodoc:
953
- symbol = args.shift
954
-
955
- each_element("./#{symbol}") do |kid|
956
- return _bequeath_attributes(kid).drill(&block)
957
- end # ERGO element/:child - def/
958
-
959
- raise_magic_member_not_found(symbol, caller) # ERGO repurpose!
960
- end # ERGO convert attribute chain to xpath
961
-
962
- # Returns all text contents from a node and its descendants
963
- #
964
- # Example:
965
- #
966
- # assert_match 'can\'t be blank', assert_tag_id(:div, :errorExplanation).inner_text.strip
967
- #
968
- # %transclude AssertXPathSuite#test_inner_text
969
- #
970
- def inner_text(interstitial = '')
971
- return self.each_element( './/text()' ){}.join(interstitial)
972
- end # ERGO match??
973
-
974
- def get_path(xpath)
975
- node = each_element(xpath.to_s){}.first
976
- return _bequeath_attributes(node) if node
977
- end # ERGO test that attributes are bequeathed!
978
-
979
- alias :/ get_path
980
-
981
- # ERGO use set_backtrace to seat the backtracer to your code
982
- # ERGO move _bequeath stuff in here!
983
- # ERGO phase out the missing_method stuff that adds props
984
-
985
- end
986
-
987
-
988
- # FIXME hpricot, libxml, rexml always in alpha order
989
-
990
- # http://www.oreillynet.com/ruby/blog/2008/01/assert_efficient_sql.html
991
- # http://www.oreillynet.com/onlamp/blog/2007/09/big_requirements_up_front.html
992
- # http://www.oreillynet.com/onlamp/blog/2007/08/assert_hpricot_1.html
993
- # http://www.oreillynet.com/onlamp/blog/2007/08/xpath_checker_and_assert_xpath.html
994
- # http://phlip.eblogs.com/2007/07/28/javascriptpureperl-for-ruby-enthusiasts/
995
- # http://www.oreillynet.com/onlamp/blog/2007/07/assert_latest_and_greatest.html
996
- # http://www.oreillynet.com/onlamp/blog/2007/07/assert_raise_on_ruby_dont_just.html
997
- # http://phlip.eblogs.com/2007/01/02/growl-driven-development/