assert_xpath 0.4.2 → 0.4.6
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/lib/assert_css.rb +52 -0
- data/lib/assert_javascript.rb +1 -1
- data/lib/assert_xpath.rb +94 -83
- data/lib/jsToXml.pl +0 -0
- data/lib/rdoc_patch.rb +254 -0
- metadata +77 -47
- data/lib/assert_xpath.rb~ +0 -997
data/lib/jsToXml.pl
CHANGED
File without changes
|
data/lib/rdoc_patch.rb
ADDED
@@ -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.
|
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
|
-
|
32
|
-
|
33
|
-
|
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::
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
53
30
|
requirements:
|
54
|
-
- - "
|
31
|
+
- - ">="
|
55
32
|
- !ruby/object:Gem::Version
|
56
|
-
version: 0
|
33
|
+
version: "0"
|
57
34
|
version:
|
58
35
|
- !ruby/object:Gem::Dependency
|
59
|
-
name:
|
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::
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
62
50
|
requirements:
|
63
|
-
- - "
|
51
|
+
- - ">="
|
64
52
|
- !ruby/object:Gem::Version
|
65
|
-
version: 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
|
+
|
data/lib/assert_xpath.rb~
DELETED
@@ -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!('\\\'', ''')
|
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/
|