assert_xpath 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/assert_javascript.rb +594 -0
- data/lib/assert_xpath.rb +1004 -0
- data/lib/jsToXml.pl +9 -0
- metadata +72 -0
@@ -0,0 +1,594 @@
|
|
1
|
+
require 'assert_xpath'
|
2
|
+
require 'tempfile'
|
3
|
+
require 'cgi'
|
4
|
+
|
5
|
+
# These assertions soak in Javascript::PurePerl goodness
|
6
|
+
|
7
|
+
#:stopdoc:
|
8
|
+
# ERGO this module should use search
|
9
|
+
# ERGO assert_javascript should only use assert_rexml not assert_xml
|
10
|
+
# ERGO tell crew to set eblogs' background color correctly...
|
11
|
+
# ERGO return value on each method
|
12
|
+
# ERGO assert_js_remote_function should take url_for args
|
13
|
+
# ERGO move got_pure_perl? rdoc to AssertJavaScript page
|
14
|
+
# ERGO clicking in Konsole navigates in Kate
|
15
|
+
# ERGO any matcher in an assert_js should search
|
16
|
+
#:startdoc:
|
17
|
+
|
18
|
+
def temporarily(obj, member, new_value)
|
19
|
+
old_value = obj.send(member)
|
20
|
+
|
21
|
+
begin
|
22
|
+
obj.send(member.to_s+'=', new_value) # CONSIDER look up the assign thinger?
|
23
|
+
yield
|
24
|
+
ensure
|
25
|
+
obj.send(member.to_s+'=', old_value)
|
26
|
+
end
|
27
|
+
end # ERGO document!
|
28
|
+
|
29
|
+
=begin rdoc
|
30
|
+
See: http://assertxpath.rubyforge.org/
|
31
|
+
%html <pre>
|
32
|
+
#\ #.:# +------------------------------------------+
|
33
|
+
#\ #.:# ^^^^^ ___/ these assertions use Javascript::PurePerl \
|
34
|
+
#\ #.:# }OvO{ <___ to convert JS into XML. assert_xpath can |
|
35
|
+
##.:# {| |} | query this to find important details, |
|
36
|
+
#..# \| |/ \ and safely skip over unimportant details! /
|
37
|
+
#..# \ _ / +------------------------------------------+
|
38
|
+
#..# | |
|
39
|
+
#..#>=======d=b=======
|
40
|
+
#..#
|
41
|
+
%html </pre>
|
42
|
+
=end
|
43
|
+
|
44
|
+
module AssertJavaScript
|
45
|
+
include AssertXPath
|
46
|
+
|
47
|
+
# %html <a name='assert_javascript'></a>
|
48
|
+
# Wraps <tt>Javascript::PurePerl</tt> to convert JavaScript into XML describing
|
49
|
+
# each lexeme. This allows subsequent +assert_xpath+ calls to read the JavaScript.
|
50
|
+
#
|
51
|
+
# See {Javascript::PurePerl for Ruby Enthusiasts}[http://phlip.eblogs.com/2007/07/28/javascriptpureperl-for-ruby-enthusiasts/]
|
52
|
+
# to learn to install Javascript::PurePerl
|
53
|
+
# * +source+ - optional string containing JavaScript. The default is
|
54
|
+
# <tt>@response.body</tt>
|
55
|
+
# * +diagnostic+ - optional string to add to failure message
|
56
|
+
# Other +assert_js_+* methods call +assert_javascript+ if the secret
|
57
|
+
# <tt>@xdoc</tt> instance variable is +nil+
|
58
|
+
#
|
59
|
+
# To extract JS from an XHTML page, place the +assert_js+ inside the +assert_xpath+
|
60
|
+
# which located your JavaScript. For example:
|
61
|
+
# assert_xpath './/input[ @type = "image" ]' do |input|
|
62
|
+
# assert_javascript input.onclick
|
63
|
+
# assert_xpath './/Statement[2]' do
|
64
|
+
# assert_js_remote_function '/user/inventory/' do
|
65
|
+
# json = assert_js_argument(2)
|
66
|
+
# params = assert_params('uri?' + json[:parameters]).last
|
67
|
+
# assert_equal @user.id.to_s, params[:user_id]
|
68
|
+
# assert_equal @user.inventory.first.id.to_s, params[:inventory_id]
|
69
|
+
# end
|
70
|
+
# end
|
71
|
+
# end
|
72
|
+
# assert_tag_id :div, :inventory_bar # <-- applies to original XML
|
73
|
+
#
|
74
|
+
# See: AssertJavaScriptTest#test_assert_javascript
|
75
|
+
#
|
76
|
+
def assert_javascript(source = nil, diagnostic = nil)
|
77
|
+
javascript_to_xml(source, diagnostic) do |tmp_error|
|
78
|
+
@xdoc = assert_xpath(
|
79
|
+
'/AST/Program/SourceElements',
|
80
|
+
build_message(
|
81
|
+
diagnostic,
|
82
|
+
"JavaScript <#{source}> contained errors:\n#{File.read(tmp_error) rescue nil}"
|
83
|
+
)
|
84
|
+
)
|
85
|
+
end # ERGO if any diagnostic is a lambda, lazily evaluate it
|
86
|
+
end
|
87
|
+
alias assert_js assert_javascript
|
88
|
+
|
89
|
+
def assert_javascript_too(&block) #:nodoc: # ERGO merge with assert_javascript!!!
|
90
|
+
@xdoc or assert_javascript
|
91
|
+
|
92
|
+
sit_and_spin = './/VariableDeclaration/' +
|
93
|
+
'Identifier[ @name = "Identifier" ]/../' +
|
94
|
+
'Initializer[ @name = "Initializer" ]/' +
|
95
|
+
'Number[ @name = "AssignmentExpression" ]/../..'
|
96
|
+
stuff = {}
|
97
|
+
|
98
|
+
@xdoc.each_element(sit_and_spin) do |node|
|
99
|
+
name = REXML::XPath.first(node, './/Identifier').text
|
100
|
+
number = REXML::XPath.first(node, './/Number').text
|
101
|
+
stuff[name.to_sym] = number.to_f
|
102
|
+
# ERGO is to_f best?
|
103
|
+
# ERGO simpler way to add an ostruct member?
|
104
|
+
# ERGO cleaner XPath...
|
105
|
+
end
|
106
|
+
|
107
|
+
js = OpenStruct.new(stuff)
|
108
|
+
block.call(js)
|
109
|
+
end
|
110
|
+
|
111
|
+
# ERGO what is "id('content')/div[1]/" ? Can we do that?
|
112
|
+
# ERGO "put the subtle to the metal..."
|
113
|
+
|
114
|
+
# Negates +assert_javascript+. Inexplicably passes if a string does not contain
|
115
|
+
# anything which satisfies <tt>Javascript::PurePerl</tt>'s narrow definition of
|
116
|
+
# JavaScript. Depends on +assert_javascript+
|
117
|
+
#
|
118
|
+
# * +source+ - optional string that should not contain JavaScript. The default is @response.body
|
119
|
+
# * +diagnostic+ - optional string to add to failure message
|
120
|
+
#
|
121
|
+
# Note that "var = 2" will pass, (and fail in +assert_javascript+)
|
122
|
+
# because <tt>Javascript::PurePerl</tt> requires a trailing <tt>;</tt>
|
123
|
+
#
|
124
|
+
# Example:
|
125
|
+
# The author would be interested to hear if anyone finds a use for this
|
126
|
+
#
|
127
|
+
def deny_javascript(source = nil, diagnostic = nil)
|
128
|
+
javascript_to_xml(source, diagnostic) do
|
129
|
+
stash_xdoc do
|
130
|
+
deny_xpath( '/AST/Program/SourceElements',
|
131
|
+
build_message(
|
132
|
+
diagnostic,
|
133
|
+
"string <#{_esc source}> should not be well-formed JavaScript"
|
134
|
+
)
|
135
|
+
)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
alias deny_js deny_javascript
|
140
|
+
|
141
|
+
unless respond_to? :returning
|
142
|
+
def returning(value) #:nodoc:
|
143
|
+
yield(value)
|
144
|
+
return value
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
# ERGO "if that Congress still can't budge
|
149
|
+
# daddy's in tight with a Supreme Court judge"
|
150
|
+
|
151
|
+
# %html <a name='assert_json'></a>
|
152
|
+
# Converts a REXML::Element into a Hash containing named elements. Currently
|
153
|
+
# we only support Booleans or Strings
|
154
|
+
#
|
155
|
+
# This depend on methods like assert_js_argument to locate a JSON argument.
|
156
|
+
# Otherwise, the method searches for the first JSON it finds in
|
157
|
+
# the current context
|
158
|
+
# * +jsonic+ - the input REXML::Element node - defaults to the secret <tt>@xdoc</tt>
|
159
|
+
# variable set by +assert_javascript+.
|
160
|
+
#
|
161
|
+
# Example:
|
162
|
+
#
|
163
|
+
# %transclude AssertJavaScriptTest#test_assert_json
|
164
|
+
#
|
165
|
+
def assert_json(jsonic = @xdoc || assert_javascript, diagnostic = nil)
|
166
|
+
@xdoc or assert_javascript
|
167
|
+
|
168
|
+
returning Hash.new do |json|
|
169
|
+
stash_xdoc do
|
170
|
+
@xdoc = jsonic
|
171
|
+
assert_any_xpath 'descendant-or-self::PropertyNameAndValueList/PropertyPair', diagnostic
|
172
|
+
end # ERGO use assert_any_xpath
|
173
|
+
|
174
|
+
jsonic.each_element('descendant-or-self::PropertyNameAndValueList/PropertyPair') do |node|
|
175
|
+
name = REXML::XPath.first(node, 'PropertyName/*')
|
176
|
+
name = name.text.to_sym
|
177
|
+
|
178
|
+
json[name] =
|
179
|
+
case
|
180
|
+
when b = REXML::XPath.first(node, 'ObjectLiteral/PropertyNameAndValueList')
|
181
|
+
assert_json(b)
|
182
|
+
|
183
|
+
when b = REXML::XPath.first(node, 'Boolean')
|
184
|
+
b.text == '1'
|
185
|
+
|
186
|
+
when b = REXML::XPath.first(node, 'Number')
|
187
|
+
b.text
|
188
|
+
|
189
|
+
# ERGO how to do an or in an XPath??
|
190
|
+
|
191
|
+
when b = REXML::XPath.first(node, 'String')
|
192
|
+
b.text
|
193
|
+
|
194
|
+
when b = REXML::XPath.first(node, 'Identifier')
|
195
|
+
b.text # ERGO test me!
|
196
|
+
|
197
|
+
when b = REXML::XPath.first(node, 'ArrayLiteral/ElementList')
|
198
|
+
# ERGO recurse here
|
199
|
+
returning [] do |list|
|
200
|
+
b.each_element('*') do |item|
|
201
|
+
list << item.text # ERGO more accurate
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
else
|
206
|
+
# ERGO handle embedded JS expressons!
|
207
|
+
#puts indent_xml(node)
|
208
|
+
# ERGO flunk "Add type #{node.children.last.name} to assert_json!"
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
# Explores a function call's argument list
|
215
|
+
# * +index+ - 1-based index into the arguments
|
216
|
+
# * +diagnostic+ - optional string to add to failure message
|
217
|
+
#
|
218
|
+
# Example: AssertJavaScriptTest#test_assert_js_argument
|
219
|
+
#
|
220
|
+
def assert_js_argument(index = 1, diagnostic = nil)
|
221
|
+
@xdoc or assert_javascript
|
222
|
+
# ERGO rdoc: what do we depend on?
|
223
|
+
# ERGO propagate xpathic 'descendant-or-self'
|
224
|
+
|
225
|
+
assert_xpath xpath_argument(index), diagnostic do |node|
|
226
|
+
return node.text if %w(String Identifier).include?(node.name)
|
227
|
+
return assert_json if node.name == 'ObjectLiteral'
|
228
|
+
end # ERGO test we slip not into a nested argument list
|
229
|
+
end
|
230
|
+
|
231
|
+
def deny_js_argument(index = 1, diagnostic = nil)
|
232
|
+
@xdoc or assert_javascript
|
233
|
+
# ERGO rdoc: what do we depend on?
|
234
|
+
deny_xpath xpath_argument(index), diagnostic
|
235
|
+
end
|
236
|
+
|
237
|
+
# Not ready for public use yet!
|
238
|
+
#
|
239
|
+
def assert_params(path_query)
|
240
|
+
path, query = path_query.split('?')
|
241
|
+
params = {}
|
242
|
+
|
243
|
+
if query
|
244
|
+
query.split('&').each do |item|
|
245
|
+
key, value = item.split('=')
|
246
|
+
params[key.to_sym] = CGI::unescape(value)
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
return [path, params]
|
251
|
+
end # ERGO get this working on incomplete URLs
|
252
|
+
|
253
|
+
# ERGO sick local tests for all this stuff that the MMORPG tests
|
254
|
+
# ERGO links out to all this stuff
|
255
|
+
# ERGO tag_id -> element_id
|
256
|
+
|
257
|
+
# Detect the JavaScriptGenerator method +replace_html+
|
258
|
+
# * +element_id+ - first argument to +replace_html+ -
|
259
|
+
# the target HTML Element's +id+
|
260
|
+
# * +matcher+ - optional regular expression to match
|
261
|
+
# the raw contents of the second argument to +replace_html+
|
262
|
+
# * +diagnostic+ - optional string to add to failure message
|
263
|
+
# * +block+ - optional; permits +assert_xpath+ calls
|
264
|
+
# into the HTML contents of the replacement
|
265
|
+
#
|
266
|
+
# Example: AssertJavaScriptTest#test_assert_js_replace_html
|
267
|
+
#
|
268
|
+
def assert_js_replace_html(element_id, matcher = nil, diagnostic = nil, &block)
|
269
|
+
assert_xpath object_method_xpath('Element', 'update', element_id), diagnostic do
|
270
|
+
assert_equal element_id.to_s, assert_js_argument(1)
|
271
|
+
assert_match matcher, assert_js_argument(2), diagnostic if matcher
|
272
|
+
# ERGO fetch argument 2 as yielded xml and match its inner text
|
273
|
+
if block
|
274
|
+
assert_js_xml(assert_js_argument(2)) # ERGO do this even without the if
|
275
|
+
block.call
|
276
|
+
end
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
# Negates assert_js_replace_html
|
281
|
+
# * +element_id+ - first argument to +replace_html+ -
|
282
|
+
# the target HTML Element's +id+
|
283
|
+
# * +matcher+ - optional regular expression to not match
|
284
|
+
# the raw contents of the second argument to +replace_html+
|
285
|
+
# * +diagnostic+ - optional string to add to failure message
|
286
|
+
# If the +matcher+ is +nil+ or not provided, the +element_id+
|
287
|
+
# must /not/ match any +Element.update+ call in its context.
|
288
|
+
# If the +matcher+ /is/ provided, the +element_id+ /must/
|
289
|
+
# match, and the +matcher+ must /not/ agree with its contents
|
290
|
+
#
|
291
|
+
# Example: AssertJavaScriptTest#test_deny_js_replace_html
|
292
|
+
#
|
293
|
+
def deny_js_replace_html(element_id, matcher = nil, diagnostic = nil)
|
294
|
+
path = object_method_xpath('Element', 'update', element_id)
|
295
|
+
|
296
|
+
# ERGO don't let subsequent updates with the same element_id confuse the matcher!
|
297
|
+
|
298
|
+
if matcher and node = REXML::XPath.first(@xdoc, path)
|
299
|
+
stash_xdoc do
|
300
|
+
@xdoc = node
|
301
|
+
assert_no_match matcher, assert_js_argument(2), diagnostic
|
302
|
+
return # ERGO pass node for 1st arg to assert_js_argument
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
deny_xpath path, diagnostic
|
307
|
+
end
|
308
|
+
|
309
|
+
# ERGO this line should not crash on bad page:
|
310
|
+
# yar = YarWiki.new(params[:id].to_s)
|
311
|
+
# ERGO provide link for Flash if you have none
|
312
|
+
|
313
|
+
# Detects an Insertion attack.
|
314
|
+
# * +orientation+ - <tt>:top</tt>, <tt>:bottom</tt>, etc...
|
315
|
+
# Remaining arguments similar to assert_js_replace_html
|
316
|
+
#
|
317
|
+
# Example:
|
318
|
+
# %transclude AssertJavaScriptTest#test_assert_js_insert_html
|
319
|
+
#
|
320
|
+
def assert_js_insert_html(orientation, element_id, matcher = //, diagnostic = nil)
|
321
|
+
orientation = orientation.to_s.capitalize
|
322
|
+
|
323
|
+
# ERGO the matcher should work the same as assert_js_replace_html
|
324
|
+
|
325
|
+
assert_any_xpath object_method_xpath('Insertion', orientation, element_id), matcher, diagnostic do
|
326
|
+
assert_equal element_id.to_s, assert_js_argument(1), diagnostic # note: shouldn't happen!
|
327
|
+
assert_js_xml(assert_js_argument(2)) # ERGO good error diagnostic if _this_ fails!
|
328
|
+
yield if block_given? # ERGO yield something?
|
329
|
+
return @xdoc # ERGO everyone should return their guts like this
|
330
|
+
end
|
331
|
+
end # ERGO local test for me
|
332
|
+
|
333
|
+
# Detects an +Ajax.Update+ or +Ajax.Request+ call
|
334
|
+
# * +action+ - optional - the HTTP Post action - the first
|
335
|
+
# argument to +Ajax.Update+ or +Ajax.Request+
|
336
|
+
# * +tag_id+ - any of the following
|
337
|
+
# * - <tt>:request</tt> -
|
338
|
+
# * - <tt>:update</tt> -
|
339
|
+
# * +diagnostic+ - optional string to add to failure message
|
340
|
+
#
|
341
|
+
# Example:
|
342
|
+
#
|
343
|
+
def assert_js_remote_function( action = nil,
|
344
|
+
tag_id = nil,
|
345
|
+
diagnostic = nil )
|
346
|
+
matcher = // # ERGO
|
347
|
+
|
348
|
+
method = if tag_id.class === String or
|
349
|
+
tag_id.class == Regexp # ERGO test regexp
|
350
|
+
'Update'
|
351
|
+
elsif tag_id == :request # ERGO test this branch
|
352
|
+
'Request'
|
353
|
+
elsif tag_id == :update # ERGO test this branch
|
354
|
+
'Update'
|
355
|
+
else
|
356
|
+
"Request' or . = 'Update"
|
357
|
+
end
|
358
|
+
|
359
|
+
xpath = object_method_xpath('Ajax', method, action)
|
360
|
+
|
361
|
+
# ERGO search via tag_id!!!
|
362
|
+
|
363
|
+
assert_any_xpath xpath, //, diagnostic do
|
364
|
+
if (tag_id.class != String and tag_id.class != Regexp) or
|
365
|
+
/#{tag_id}/ =~ assert_js_argument(2)
|
366
|
+
if action
|
367
|
+
assert_equal action.to_s.gsub('&', '&'),
|
368
|
+
assert_js_argument(1),
|
369
|
+
diagnostic
|
370
|
+
end
|
371
|
+
# ERGO a better system to extract GET parameters
|
372
|
+
yield if block_given? # ERGO test this block
|
373
|
+
return @xdoc # ERGO test that return
|
374
|
+
end
|
375
|
+
end # ERGO less confusing error diagnostic if this fails
|
376
|
+
|
377
|
+
flunk build_message(diagnostic, "expected id <#{tag_id}>")
|
378
|
+
end
|
379
|
+
|
380
|
+
# ERGO test via a javascript-like dsl:
|
381
|
+
# assert_javascript do
|
382
|
+
# js_if, js_replace_html, etc...
|
383
|
+
# var(:x) = 42
|
384
|
+
# object(:Render).update :div_name etc.
|
385
|
+
# end
|
386
|
+
|
387
|
+
# ERGO diagnostic
|
388
|
+
|
389
|
+
# Not ready for public use!
|
390
|
+
#
|
391
|
+
def assert_js_xml(q) # ERGO explain this beast; hide inside assert_js_*
|
392
|
+
xml = eval('"' + q + '"')
|
393
|
+
assert_xml xml # ERGO what are the errors if these fail?
|
394
|
+
end # ERGO test the error recoverer in assert_xml
|
395
|
+
|
396
|
+
# Not ready for public use!
|
397
|
+
#
|
398
|
+
# Example:
|
399
|
+
#
|
400
|
+
def assert_js_call(*args, &block)
|
401
|
+
path, arg1, arg2, diagnostic = *calling_js_path(args)
|
402
|
+
assert_xpath path, diagnostic, &block
|
403
|
+
end # ERGO local tests for all these
|
404
|
+
|
405
|
+
#
|
406
|
+
# * ++ -
|
407
|
+
# * +diagnostic+ - optional string to add to failure message
|
408
|
+
#
|
409
|
+
# Example:
|
410
|
+
#
|
411
|
+
def deny_js_call(*args)
|
412
|
+
path, arg1, arg2, diagnostic = calling_js_path(args)
|
413
|
+
# ERGO do something with arg1 and arg2
|
414
|
+
deny_xpath path, diagnostic
|
415
|
+
end
|
416
|
+
|
417
|
+
# %html <a name='assert_js_show'></a>
|
418
|
+
# Detects the Element.show or Element.hide commands
|
419
|
+
# * +element_id+ - HTML Element +id+ to target
|
420
|
+
# * +visibility+ - defaults to :show; optionally :hide
|
421
|
+
#
|
422
|
+
def assert_js_show(element_id, visibility = :show) # ERGO also deny, also diagnostic
|
423
|
+
assert_xpath object_method_xpath(:Element, visibility, element_id) do
|
424
|
+
assert_equal element_id.to_s, assert_js_argument(1)
|
425
|
+
end
|
426
|
+
# ERGO what if the visibility is wrong?
|
427
|
+
end # ERGO deny for every assert
|
428
|
+
|
429
|
+
# Alias for assert_js_show(element_id, :hide)
|
430
|
+
#
|
431
|
+
def assert_js_hide(element_id) assert_js_show(element_id, :hide) end
|
432
|
+
|
433
|
+
# Find a JavaScript if statement, or one of its elements
|
434
|
+
# * +*args+ - ERGO!
|
435
|
+
# * +diagnostic+ - optional string to add to failure message
|
436
|
+
#
|
437
|
+
# Example:
|
438
|
+
#
|
439
|
+
def assert_js_if(*args, &block)
|
440
|
+
return assert_any_xpath(*_re_arg_if(*args), &block)
|
441
|
+
end
|
442
|
+
|
443
|
+
# Fails if a JavaScript +if+ expression or block has the given characteristics
|
444
|
+
# * +*args+ - ERGO!
|
445
|
+
# * +diagnostic+ - optional string to add to failure message
|
446
|
+
#
|
447
|
+
# Example:
|
448
|
+
#
|
449
|
+
def deny_js_if(condition = :all, matcher = nil, diagnostic = nil)
|
450
|
+
xpath, matcher, diagnostic = _re_arg_if(condition, matcher, diagnostic)
|
451
|
+
return deny_xpath(xpath, diagnostic) unless matcher
|
452
|
+
return deny_any_xpath(xpath, matcher, diagnostic)
|
453
|
+
# ERGO flunk here - with param explanation (if humanly possible!)
|
454
|
+
end # ERGO assert_any_xpath should take an explicit block
|
455
|
+
|
456
|
+
# ERGO extract-method the x[@name=y] stuff!
|
457
|
+
|
458
|
+
# ERGO test nesting if statements to require this temporarily
|
459
|
+
# ERGO rdoc should provide external-able links for important methods
|
460
|
+
|
461
|
+
private # ERGO assert_js.js_like_ruby_statements_as_dsl
|
462
|
+
|
463
|
+
def javascript_to_xml(source, diagnostic)
|
464
|
+
source ||= default_js_source(diagnostic)
|
465
|
+
here = File.dirname(__FILE__)
|
466
|
+
jsToXml_pl = File.join(here, 'jsToXml.pl')
|
467
|
+
|
468
|
+
Tempfile.open('assert_javascript_sample') do |sample|
|
469
|
+
Tempfile.open('assert_javascript_error') do |error|
|
470
|
+
sample.write(source)
|
471
|
+
sample.flush
|
472
|
+
got = `perl "#{jsToXml_pl}" "#{sample.path}" 2>#{error.path}`
|
473
|
+
assert_xml got
|
474
|
+
yield(error.path)
|
475
|
+
end
|
476
|
+
end
|
477
|
+
end # ERGO use @xdoc = assert_xpath in certain other places
|
478
|
+
|
479
|
+
def default_js_source(diagnostic = nil)
|
480
|
+
@response.respond_to?(:body) || flunk(build_message(diagnostic, "no @response.body found - use assert_js(my_js)"))
|
481
|
+
return @response.body.relevance || flunk(build_message(diagnostic, "no JavaScript found in @response.body"))
|
482
|
+
end # ERGO publish relevance
|
483
|
+
|
484
|
+
# CONSIDER permit symbols for both element_id and matcher
|
485
|
+
|
486
|
+
def _re_arg_if(condition = :all, matcher = nil, diagnostic = nil)
|
487
|
+
@xdoc or assert_javascript
|
488
|
+
|
489
|
+
matcher = condition.respond_to?(:match) ? condition : matcher
|
490
|
+
aspect = :all
|
491
|
+
aspect = condition if [:condition, :all, :true, :false].include?(condition)
|
492
|
+
|
493
|
+
xpath = './/IfStatement[ @name = "IfStatement" ]' +
|
494
|
+
if aspect == :condition
|
495
|
+
'/Expression[ @name = "Expression" ]'
|
496
|
+
elsif aspect == :all
|
497
|
+
'/..' # CONSIDER why is this here??
|
498
|
+
else
|
499
|
+
"/Statement[ @name='#{aspect}' ]"
|
500
|
+
end
|
501
|
+
|
502
|
+
# ERGO flunk here - with param explanation (if humanly possible!)
|
503
|
+
|
504
|
+
return [xpath, matcher, diagnostic]
|
505
|
+
end
|
506
|
+
|
507
|
+
def yield_xdoc_block(xdoc, aspect, matcher, diagnostic, &block)
|
508
|
+
@if_condition = @xdoc.parent.parent if aspect == :condition
|
509
|
+
yield(@xdoc = xdoc) if block_given?
|
510
|
+
assert_match matcher, @xdoc.inner_text, diagnostic if matcher
|
511
|
+
return @xdoc
|
512
|
+
end # ERGO better name and/or tighter abstraction
|
513
|
+
|
514
|
+
def xpath_argument(index)
|
515
|
+
return "descendant-or-self::ArgumentList/*[position() = #{index}]"
|
516
|
+
end
|
517
|
+
|
518
|
+
# ERGO patch test:recent to run tests with same names as
|
519
|
+
# edited code - hook SVN diff to do it!
|
520
|
+
|
521
|
+
def object_method_xpath(object, method, element_id)
|
522
|
+
@xdoc or assert_javascript
|
523
|
+
# ERGO: element_id may be nil. Propagate this
|
524
|
+
# ERGO: element_id -> first_argument
|
525
|
+
|
526
|
+
return ".//ArgumentList[ @name = 'Arguments' ]/" +
|
527
|
+
(element_id ? "String[ . = '#{element_id}' ]/../" : '') +
|
528
|
+
'../' + member_expression(object, method)
|
529
|
+
end # ERGO quanti-friably
|
530
|
+
|
531
|
+
def member_expression(object, method)
|
532
|
+
return "MemberMemberExpression[ @name = 'Callee' or
|
533
|
+
@name = 'ClassExpression' ]" + # ERGO productively reconcile them two
|
534
|
+
"/Identifier[ position() = 1 and
|
535
|
+
@name = 'lhs' and
|
536
|
+
. = '#{ object }' ]" +
|
537
|
+
"/../Identifier[ position() = 2 and
|
538
|
+
@name = 'member' and
|
539
|
+
. = '#{ method }' ]" +
|
540
|
+
"/../../ArgumentList[ @name = 'Arguments' ]"
|
541
|
+
end
|
542
|
+
|
543
|
+
def calling_js_path(args)
|
544
|
+
@xdoc or assert_javascript
|
545
|
+
object, method = args.first.to_s.split('.')
|
546
|
+
element_id, diagnostic = [args[1], args[2]]
|
547
|
+
|
548
|
+
# ERGO this should also work without element_id
|
549
|
+
# ERGO local test that this calls its blocks correctly
|
550
|
+
|
551
|
+
if object and method and element_id
|
552
|
+
return object_method_xpath(object, method, element_id), *args
|
553
|
+
elsif object and method # ERGO document - pass nil for element_id if you need a diagnostic
|
554
|
+
# ERGO descendent or self?
|
555
|
+
return './/' + member_expression(object, method), *args
|
556
|
+
else
|
557
|
+
return ".//Identifier[ @name = 'Callee' and . = '#{args.shift}' ]" +
|
558
|
+
'/following-sibling::ArgumentList', *args
|
559
|
+
end
|
560
|
+
end
|
561
|
+
|
562
|
+
end
|
563
|
+
|
564
|
+
# ERGO: Don't put all your eggs in one basket before they're hatched.
|
565
|
+
|
566
|
+
# Detect if your kit is complete, and detect if you have <tt>Javascript::PurePerl</tt>.
|
567
|
+
# If you don't, we warn one time. Use this method to defend portable tests that
|
568
|
+
# should not break on computers without Javascript::PurePerl
|
569
|
+
#
|
570
|
+
# Example:
|
571
|
+
# if RAILS_ENV == 'test' and got_pure_perl?
|
572
|
+
# class AssertJavaScriptTest < Test::Unit::TestCase
|
573
|
+
# # ...
|
574
|
+
# end
|
575
|
+
# end
|
576
|
+
#
|
577
|
+
def got_pure_perl?
|
578
|
+
perl_version = `perl -v 2>&1` rescue 'perl, v0'
|
579
|
+
perl_version =~ /perl, v(\d+)/
|
580
|
+
|
581
|
+
if $1.to_i < 5
|
582
|
+
puts "\ninsufficient Perl for assert_js!" unless $already_warned_about_missing_pure_perl
|
583
|
+
$already_warned_about_missing_pure_perl = true
|
584
|
+
return false
|
585
|
+
end
|
586
|
+
|
587
|
+
unless system('perl -e "use Javascript::PurePerl" 2>/dev/null')
|
588
|
+
puts "\ninstall Javascript::PurePerl for best results!" unless $already_warned_about_missing_pure_perl
|
589
|
+
$already_warned_about_missing_pure_perl = true
|
590
|
+
return false
|
591
|
+
end
|
592
|
+
|
593
|
+
return true
|
594
|
+
end
|