Skip to content

Commit

Permalink
Added a proper substitution context class. Changed ArgumentFilter to …
Browse files Browse the repository at this point in the history
…be a selector. It is now called HTMLSelector.
  • Loading branch information
kaspth committed Jun 16, 2014
1 parent 8d6c92c commit 5f756e3
Showing 1 changed file with 47 additions and 34 deletions.
81 changes: 47 additions & 34 deletions actionpack/lib/action_dispatch/testing/assertions/selector.rb
Expand Up @@ -158,21 +158,20 @@ def css_select(*args)
def assert_select(*args, &block)
@selected ||= nil

filter = ArgumentFilter.new(@selected, response_from_page, args)
selector = filter.css_selector
equals = filter.comparisons
selector = HTMLSelector.new(@selected, response_from_page, args)

matches = filter.root.css(selector, filter.substitution_context)
matches = selector.select
equals = selector.comparisons
content_mismatch = nil
matches = filter_matches(matches, equals) do |mismatch|
content_mismatch ||= mismatch
end

# Expecting foo found bar element only if found zero, not if
# found one but expecting two.
message = filter.message
message = selector.message
message ||= content_mismatch if matches.empty?
assert_size_match!(matches.size, equals, selector, message)
assert_size_match!(matches.size, equals, selector.source, message)

# Set @selected to allow nested assert_select.
# Can be nested several levels deep.
Expand Down Expand Up @@ -339,7 +338,7 @@ def html_document
end
end

class ArgumentFilter #:nodoc:
class HTMLSelector #:nodoc:
attr_accessor :root, :css_selector, :comparisons, :message

def initialize(selected, page, args)
Expand All @@ -363,6 +362,12 @@ def initialize(selected, page, args)
end
end

alias :source :css_selector

def select
root.css(css_selector, context)
end

def determine_root_from(root_or_selector)
@css_selector_is_second_argument = false
if root_or_selector == nil
Expand All @@ -385,33 +390,7 @@ def selector_from(selector, substitution_values)
unless selector.is_a? String
raise ArgumentError, "Expecting a selector as the first argument"
end
while selector.index('?')
break if substitution_values.empty?
value = substitution_values.shift
if value.is_a?(Regexp)
value = substitution_context.add_regex value
end
selector.sub!('?', value)
end
selector
end

# used for regex substitution in selectors
def substitution_context
@substitution_context ||= Class.new do
# +regex+ will be a +String+
def match(matches, attribute, regex)
matches.find_all { |node| node[attribute] =~ @regexes[regex] }
end

def add_regex(regex)
@regexes ||= []
@regexes.push(regex)
id_for(regex)
end

def id_for(regex); @regexes.index(regex).to_s; end
end.new
context.substitute!(selector, substitution_values)
end

def comparisons_from(comparator)
Expand Down Expand Up @@ -441,6 +420,40 @@ def comparisons_from(comparator)
end
comparisons
end

def context
@context ||= SubstitutionContext.new
end

class SubstitutionContext
def initialize(substitute = '?')
@substitute = substitute
@regexes = []
end

def add(regex)
@regexes.push(regex)
id_for(regex)
end

def id_for(regex)
@regexes.index(regex).to_s # to_s to store it in selector string
end

def match(matches, attribute, id)
matches.find_all { |node| node[attribute] =~ @regexes[id] }
end

def substitute!(selector, values)
while selector.index(@substitute)
break if values.empty?
value = values.shift
value = add(value) if value.is_a?(Regexp)
selector.sub!(@substitute, value)
end
selector
end
end
end
end
end
Expand Down

0 comments on commit 5f756e3

Please sign in to comment.