Module: AssertJavaScript

Includes:
AssertXPath
Defined in:
lib/assert_javascript.rb

Overview

See: assertxpath.rubyforge.org/ %html <pre>

#\    #.:#                   +------------------------------------------+
 #\  #.:#      ^^^^^     ___/  these assertions use Javascript::PurePerl  \
  #\ #.:#      }OvO{    <___   to convert JS into XML. assert_xpath can   |
   ##.:#      {|   |}       |  query this to find important details,      |
    #..#      \|   |/       \  and safely skip over unimportant details!  /
    #..#       \ _ /         +------------------------------------------+
    #..#        | |
    #..#>=======d=b=======
    #..#

%html </pre>

Constant Summary

Constants included from AssertXPath

AssertXPath::Element

Instance Method Summary collapse

Methods included from AssertXPath

#assert_any_xpath, #assert_hpricot, #assert_libxml, #assert_rexml, #assert_tag_id, #assert_tidy, #assert_xml, #assert_xpath, #deny_any_xpath, #deny_tag_id, #deny_xpath, #drill, #indent_xml, #invoke_hpricot, #invoke_libxml, #invoke_rexml, #using, #validate_as

Instance Method Details

#assert_javascript(source = nil, diagnostic = nil) ⇒ Object Also known as: assert_js

%html <a name=‘assert_javascript’></a> Wraps Javascript::PurePerl to convert JavaScript into XML describing each lexeme. This allows subsequent assert_xpath calls to read the JavaScript.

See Javascript::PurePerl for Ruby Enthusiasts to learn to install Javascript::PurePerl

  • source - optional string containing JavaScript. The default is @response.body

  • diagnostic - optional string to add to failure message

Other assert_js_* methods call assert_javascript if the secret @xdoc instance variable is nil

To extract JS from an XHTML page, place the assert_js inside the assert_xpath which located your JavaScript. For example:

assert_xpath './/input[ @type = "image" ]' do |input|
  assert_javascript input.onclick
  assert_xpath './/Statement[2]' do
    assert_js_remote_function '/user/inventory/' do 
      json = assert_js_argument(2)
      path, query = assert_uri('path?' + json[:parameters])
      assert_equal @user.id.to_s, query[:user_id]
      assert_equal @user.inventory.first.id.to_s, query[:inventory_id]
    end
  end
end
assert_tag_id :div, :inventory_bar  #  <-- applies to original XML

See: AssertJavaScriptTest#test_assert_javascript



76
77
78
79
80
81
82
83
84
85
86
# File 'lib/assert_javascript.rb', line 76

def assert_javascript(source = nil, diagnostic = nil)
  javascript_to_xml(source, diagnostic) do |tmp_error|
    @xdoc = assert_xpath(
              '/AST/Program/SourceElements',
              build_message(
                diagnostic,
                "JavaScript <#{source}> contained errors:\n#{File.read(tmp_error) rescue nil}"
              )
            )
  end  #  ERGO  if any diagnostic is a lambda, lazily evaluate it
end

#assert_javascript_too(&block) ⇒ Object

:nodoc: # ERGO merge with assert_javascript!!!



89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/assert_javascript.rb', line 89

def assert_javascript_too(&block) #:nodoc:  #  ERGO  merge with assert_javascript!!!
  @xdoc or assert_javascript
  
  sit_and_spin = './/VariableDeclaration/' +
                 'Identifier[ @name = "Identifier" ]/../' +
                 'Initializer[ @name = "Initializer" ]/' +
                 'Number[ @name = "AssignmentExpression" ]/../..'
  stuff = {}

  @xdoc.each_element(sit_and_spin) do |node|
    name = node.get_path('.//Identifier').text
    number = node.get_path('.//Number').text
    stuff[name.to_sym] = number.to_f
    #  ERGO  is to_f best?
    #  ERGO  simpler way to add an ostruct member?
    #  ERGO  cleaner XPath...
  end

  js = OpenStruct.new(stuff)
  block.call(js)
end

#assert_js_argument(index = 1, diagnostic = nil) ⇒ Object

Explores a function call’s argument list

  • index - 1-based index into the arguments

  • diagnostic - optional string to add to failure message

Example: AssertJavaScriptTest#test_assert_js_argument



220
221
222
223
224
225
226
227
228
229
# File 'lib/assert_javascript.rb', line 220

def assert_js_argument(index = 1, diagnostic = nil)
  @xdoc or assert_javascript
#  ERGO rdoc: what do we depend on?
  #  ERGO  propagate xpathic 'descendant-or-self'

  assert_xpath xpath_argument(index), diagnostic do |node|
    return node.text if %w(String Identifier).include?(node.name)
    return assert_json if node.name == 'ObjectLiteral'
  end  #  ERGO  test we slip not into a nested argument list
end

#assert_js_call(*args, &block) ⇒ Object

Not ready for public use!

Example:



409
410
411
412
# File 'lib/assert_javascript.rb', line 409

def assert_js_call(*args, &block)
  path, arg1, arg2, diagnostic = *calling_js_path(args)
  assert_xpath path, diagnostic, &block
end

#assert_js_hide(element_id) ⇒ Object

Alias for assert_js_show(element_id, :hide)



440
# File 'lib/assert_javascript.rb', line 440

def assert_js_hide(element_id) assert_js_show(element_id, :hide) end

#assert_js_if(*args, &block) ⇒ Object

Find a JavaScript if statement, or one of its elements

  • *args - ERGO!

  • diagnostic - optional string to add to failure message

Example:



448
449
450
# File 'lib/assert_javascript.rb', line 448

def assert_js_if(*args, &block)
  return assert_any_xpath(*_re_arg_if(*args), &block)
end

#assert_js_insert_html(orientation, element_id, matcher = //, diagnostic = nil) ⇒ Object

Detects an Insertion attack.

  • orientation - :top, :bottom, etc…

Remaining arguments similar to assert_js_replace_html

Example: %transclude AssertJavaScriptTest#test_assert_js_insert_html



321
322
323
324
325
326
327
328
329
330
331
332
# File 'lib/assert_javascript.rb', line 321

def assert_js_insert_html(orientation, element_id, matcher = //, diagnostic = nil)
  orientation = orientation.to_s.capitalize
  
    #  ERGO  the matcher should work the same as assert_js_replace_html
    
  assert_any_xpath object_method_xpath('Insertion', orientation, element_id), matcher, diagnostic do
    assert_equal element_id.to_s, assert_js_argument(1), diagnostic  #  note: shouldn't happen!
    assert_js_xml(assert_js_argument(2))  #  ERGO  good error diagnostic if _this_ fails!
    yield  if block_given?  #  ERGO  yield something?
    return @xdoc  #   ERGO  everyone should return their guts like this
  end
end

#assert_js_remote_function(action = nil, tag_id = nil, diagnostic = nil) ⇒ Object

Detects an Ajax.Update or Ajax.Request call

  • action - optional - the HTTP Post action - the first argument to Ajax.Update or Ajax.Request

  • tag_id - any of the following

    • :request -

    • :update -

  • diagnostic - optional string to add to failure message

Example:



344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
# File 'lib/assert_javascript.rb', line 344

def assert_js_remote_function( action = nil, 
                               tag_id = nil, 
                               diagnostic = nil )
  matcher = //  #  ERGO
      
  method = if tag_id.class === String or
              tag_id.class == Regexp  #  ERGO  test regexp
             'Update'
           elsif tag_id == :request  #  ERGO  test this branch
             'Request'
           elsif tag_id == :update  #  ERGO  test this branch
             'Update'
           else
             "Request' or . = 'Update"
           end

  xpath = object_method_xpath('Ajax', method, action)

  #  ERGO  search via tag_id!!!

  assert_any_xpath xpath, //, diagnostic do
    if (tag_id.class != String and tag_id.class != Regexp) or
        /#{tag_id}/ =~ assert_js_argument(2)
      if action
      
      #  ERGO  move those GCI::unescapeHTMLs up the food chain?
      
        assert_equal CGI::unescapeHTML(action),
                     CGI::unescapeHTML(assert_js_argument(1)),
                     diagnostic
      end
      #  ERGO  a better system to extract GET parameters
      yield if block_given?  #  ERGO  test this block
      return @xdoc  #  ERGO  test that return
    end
  end  #  ERGO  less confusing error diagnostic if this fails

  flunk build_message(diagnostic, "expected id <#{tag_id}>")
end

#assert_js_replace_html(element_id, matcher = nil, diagnostic = nil, &block) ⇒ Object

Detect the JavaScriptGenerator method replace_html

  • element_id - first argument to replace_html - the target HTML Element’s id

  • matcher - optional regular expression to match the raw contents of the second argument to replace_html

  • diagnostic - optional string to add to failure message

  • block - optional; permits assert_xpath calls

into the HTML contents of the replacement

Example: AssertJavaScriptTest#test_assert_js_replace_html



269
270
271
272
273
274
275
276
277
278
279
# File 'lib/assert_javascript.rb', line 269

def assert_js_replace_html(element_id, matcher = nil, diagnostic = nil, &block)
  assert_xpath object_method_xpath('Element', 'update', element_id), diagnostic do
    assert_equal element_id.to_s, assert_js_argument(1)
    assert_match matcher, assert_js_argument(2), diagnostic  if matcher
#  ERGO  fetch argument 2 as yielded xml and match its inner text
    if block
      assert_js_xml(assert_js_argument(2))  #  ERGO do this even without the if
      block.call
    end
  end
end

#assert_js_show(element_id, visibility = :show) ⇒ Object

%html <a name=‘assert_js_show’></a> Detects the Element.show or Element.hide commands

  • element_id - HTML Element id to target

  • visibility - defaults to :show; optionally :hide



431
432
433
434
435
436
# File 'lib/assert_javascript.rb', line 431

def assert_js_show(element_id, visibility = :show)  #  ERGO  also deny, also diagnostic
  assert_xpath object_method_xpath(:Element, visibility, element_id) do
    assert_equal element_id.to_s, assert_js_argument(1)
  end
  #  ERGO  what if the visibility is wrong?
end

#assert_js_xml(q) ⇒ Object

Not ready for public use!



395
396
397
398
399
400
401
402
403
# File 'lib/assert_javascript.rb', line 395

def assert_js_xml(q)  #  ERGO explain this beast; hide inside assert_js_*

#  FIXME test this with an &entity;!

  xml = eval('"' + q + '"')
  xml = '<html><body>' + CGI::unescapeHTML(xml) + '</body></html>'  if using :libxml?

  assert_xml xml  #  ERGO  what are the errors if these fail?
end

#assert_json(jsonic = @xdoc || assert_javascript, diagnostic = nil) ⇒ Object

%html <a name=‘assert_json’></a> Converts a REXML::Element into a Hash containing named elements. Currently we only support Booleans or Strings

This depend on methods like assert_js_argument to locate a JSON argument. Otherwise, the method searches for the first JSON it finds in the current context

  • jsonic - the input REXML::Element node - defaults to the secret @xdoc variable set by assert_javascript.

Example:

%transclude AssertJavaScriptTest#test_assert_json



165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
# File 'lib/assert_javascript.rb', line 165

def assert_json(jsonic = @xdoc || assert_javascript, diagnostic = nil)
  @xdoc or assert_javascript

  returning Hash.new do |json|
    stash_xdoc do
      @xdoc = jsonic
      assert_any_xpath 'descendant-or-self::PropertyNameAndValueList/PropertyPair', diagnostic
    end  #  ERGO  use assert_any_xpath

    jsonic.each_element('descendant-or-self::PropertyNameAndValueList/PropertyPair') do |node|
      name = node.get_path('PropertyName/*')
      name = name.text.to_sym

      json[name] =
        case
          when b = node.get_path('ObjectLiteral/PropertyNameAndValueList')
            assert_json(b)

          when b = node.get_path('Boolean')
            b.text == '1'

          when b = node.get_path('Number')
            b.text

#  ERGO  how to do an or in an XPath??

          when b = node.get_path('String')
            b.text

          when b = node.get_path('Identifier')
            b.text  #  ERGO  test me!

          when b = node.get_path('ArrayLiteral/ElementList')
            #  ERGO  recurse here
            returning [] do |list|
              b.each_element('*') do |item|
                list << item.text  #  ERGO  more accurate
              end
            end

          else
          #  ERGO  handle embedded JS expressons!
#puts indent_xml(node)
 # ERGO             flunk "Add type #{node.children.last.name} to assert_json!"
        end
    end
  end
end

#assert_uri(uri) ⇒ Object

Not ready for public use yet!



239
240
241
242
243
244
245
246
247
248
249
250
251
252
# File 'lib/assert_javascript.rb', line 239

def assert_uri(uri)
  path, query = uri.split('?')
  params = {}

  if query
    splitter = using(:libxml?) ? '&amp;' : '&'
    query.split(splitter).each do |item|
      key, value = item.split('=')
      params[key.to_sym] = CGI::unescape(value || '')
    end
  end

  return [path, params]
end

#deny_javascript(source = nil, diagnostic = nil) ⇒ Object Also known as: deny_js

Negates assert_javascript. Inexplicably passes if a string does not contain anything which satisfies Javascript::PurePerl‘s narrow definition of JavaScript. Depends on assert_javascript

  • source - optional string that should not contain JavaScript. The default is @response.body

  • diagnostic - optional string to add to failure message

Note that “var = 2” will pass, (and fail in assert_javascript) because Javascript::PurePerl requires a trailing ;

Example: The author would be interested to hear if anyone finds a use for this



127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/assert_javascript.rb', line 127

def deny_javascript(source = nil, diagnostic = nil)
  javascript_to_xml(source, diagnostic) do
    stash_xdoc do 
      deny_xpath( '/AST/Program/SourceElements',
                  build_message(
                    diagnostic,
                    "string <#{_esc source}> should not be well-formed JavaScript"
                 )
              )
    end
  end
end

#deny_js_argument(index = 1, diagnostic = nil) ⇒ Object



231
232
233
234
235
# File 'lib/assert_javascript.rb', line 231

def deny_js_argument(index = 1, diagnostic = nil)
  @xdoc or assert_javascript
#  ERGO rdoc: what do we depend on?
  deny_xpath xpath_argument(index), diagnostic
end

#deny_js_call(*args) ⇒ Object

  • ++ -

  • diagnostic - optional string to add to failure message

Example:



420
421
422
423
424
# File 'lib/assert_javascript.rb', line 420

def deny_js_call(*args)
  path, arg1, arg2, diagnostic = calling_js_path(args)
  #  ERGO  do something with arg1 and arg2
  deny_xpath path, diagnostic
end

#deny_js_if(condition = :all, matcher = nil, diagnostic = nil) ⇒ Object

Fails if a JavaScript if expression or block has the given characteristics

  • *args - ERGO!

  • diagnostic - optional string to add to failure message

Example:



458
459
460
461
462
463
# File 'lib/assert_javascript.rb', line 458

def deny_js_if(condition = :all, matcher = nil, diagnostic = nil)
  xpath, matcher, diagnostic = _re_arg_if(condition, matcher, diagnostic)
  return deny_xpath(xpath, diagnostic) unless matcher
  return deny_any_xpath(xpath, matcher, diagnostic)
  #  ERGO  flunk here - with param explanation (if humanly possible!)
end

#deny_js_replace_html(element_id, matcher = nil, diagnostic = nil) ⇒ Object

Negates assert_js_replace_html

  • element_id - first argument to replace_html - the target HTML Element’s id

  • matcher - optional regular expression to not match the raw contents of the second argument to replace_html

  • diagnostic - optional string to add to failure message

If the matcher is nil or not provided, the element_id must /not/ match any Element.update call in its context. If the matcher /is/ provided, the element_id /must/ match, and the matcher must /not/ agree with its contents

Example: AssertJavaScriptTest#test_deny_js_replace_html



294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
# File 'lib/assert_javascript.rb', line 294

def deny_js_replace_html(element_id, matcher = nil, diagnostic = nil)
  path = object_method_xpath('Element', 'update', element_id)

#  ERGO  don't let subsequent updates with the same element_id confuse the matcher!

  if matcher and node = @xdoc.get_path(path)
    stash_xdoc do
      @xdoc = node
      assert_no_match matcher, assert_js_argument(2), diagnostic
      return  #  ERGO  pass node for 1st arg to assert_js_argument
    end
  end

  deny_xpath path, diagnostic
end

#returning(value) {|value| ... } ⇒ Object

:nodoc:

Yields:

  • (value)


142
143
144
145
# File 'lib/assert_javascript.rb', line 142

def returning(value) #:nodoc:
  yield(value)
  return value
end