assert_xpath 0.3.0
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_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
|