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.
- 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/
|