1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
|
require 'rails/dom/testing/assertions/selector_assertions'
module Rails::Dom::Testing::Assertions::SelectorAssertions
# Selects content from a JQuery response. Patterned loosely on
# assert_select_rjs.
#
# === Narrowing down
#
# With no arguments, asserts that one or more method calls are made.
#
# Use the +method+ argument to narrow down the assertion to only
# statements that call that specific method.
#
# Use the +opt+ argument to narrow down the assertion to only statements
# that pass +opt+ as the first argument.
#
# Use the +id+ argument to narrow down the assertion to only statements
# that invoke methods on the result of using that identifier as a
# selector.
#
# === Using blocks
#
# Without a block, +assert_select_jquery_ merely asserts that the
# response contains one or more statements that match the conditions
# specified above
#
# With a block +assert_select_jquery_ also asserts that the method call
# passes a javascript escaped string containing HTML. All such HTML
# fragments are selected and passed to the block. Nested assertions are
# supported.
#
# === Examples
#
# # asserts that the #notice element is hidden
# assert_select :hide, '#notice'
#
# # asserts that the #cart element is shown with a blind parameter
# assert_select :show, :blind, '#cart'
#
# # asserts that #cart content contains a #current_item
# assert_select :html, '#cart' do
# assert_select '#current_item'
# end
#
# # asserts that #product append to a #product_list
# assert_select_jquery :appendTo, '#product_list' do
# assert_select '.product'
# end
PATTERN_HTML = "['\"]((\\\\\"|\\\\'|[^\"'])*)['\"]"
PATTERN_UNICODE_ESCAPED_CHAR = /\\u([0-9a-zA-Z]{4})/
SKELETAL_PATTERN = "(?:jQuery|\\$)\\(%s\\)\\.%s\\(%s\\)[;]?"
def assert_select_jquery(*args, &block)
jquery_method = args.first.is_a?(Symbol) ? args.shift : nil
jquery_opt = args.first.is_a?(Symbol) ? args.shift : nil
id = args.first.is_a?(String) ? escape_id(args.shift) : nil
target_pattern = "['\"]#{id || '.*'}['\"]"
method_pattern = "#{jquery_method || '\\w+'}"
argument_pattern = jquery_opt ? "['\"]#{jquery_opt}['\"].*" : PATTERN_HTML
# $("#id").show('blind', 1000);
# $("#id").html("<div>something</div>");
# $("#id").replaceWith("<div>something</div>");
target_as_receiver_pattern = SKELETAL_PATTERN % [target_pattern, method_pattern, argument_pattern]
# $("<div>something</div>").appendTo("#id");
# $("<div>something</div>").prependTo("#id");
target_as_argument_pattern = SKELETAL_PATTERN % [argument_pattern, method_pattern, target_pattern]
# $("#id").remove();
# $("#id").hide();
argumentless_pattern = SKELETAL_PATTERN % [target_pattern, method_pattern, '']
patterns = [target_as_receiver_pattern, target_as_argument_pattern]
patterns << argumentless_pattern unless jquery_opt
matched_pattern = nil
patterns.each do |pattern|
if response.body.match(Regexp.new(pattern))
matched_pattern = pattern
break
end
end
unless matched_pattern
opts = [jquery_method, jquery_opt, id].compact
flunk "No JQuery call matches #{opts.inspect}"
end
if block_given?
@selected ||= nil
fragments = Nokogiri::HTML::Document.new.fragment
if matched_pattern
response.body.scan(Regexp.new(matched_pattern)).each do |match|
flunk 'This function can\'t have HTML argument' if match.is_a?(String)
doc = Nokogiri::HTML::DocumentFragment.parse(unescape_js(match.first))
doc.children.each do |child|
fragments << child if child.element?
end
end
end
begin
in_scope, @selected = @selected, fragments
yield
ensure
@selected = in_scope
end
end
end
private
# Unescapes a JS string.
def unescape_js(js_string)
# js encodes double quotes and line breaks.
unescaped= js_string.gsub('\"', '"')
unescaped.gsub!('\\\'', "'")
unescaped.gsub!(/\\\//, '/')
unescaped.gsub!('\n', "\n")
unescaped.gsub!('\076', '>')
unescaped.gsub!('\074', '<')
unescaped.gsub!(/\\\$/, '$')
unescaped.gsub!(/\\`/, '`')
# js encodes non-ascii characters.
unescaped.gsub!(PATTERN_UNICODE_ESCAPED_CHAR) {|u| [$1.hex].pack('U*')}
unescaped
end
def escape_id(selector)
return unless selector
id = selector.gsub('[', '\[')
id.gsub!(']', '\]')
id.gsub!('*', '\*')
id.gsub!('(', '\(')
id.gsub!(')', '\)')
id.gsub!('.', '\.')
id.gsub!('|', '\|')
id.gsub!('^', '\^')
id.gsub!('$', '\$')
id.gsub!('+', "\\\\+")
id.gsub!(',', '\,')
id
end
end
|