Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Merge branch 'patch-2' of git://github.com/abotalov/capybara into abo…
…talov-patch-2

Conflicts:
	lib/capybara.rb
  • Loading branch information
jnicklas committed Mar 17, 2013
2 parents 03761db + 7c00c53 commit e580913
Show file tree
Hide file tree
Showing 10 changed files with 250 additions and 102 deletions.
2 changes: 1 addition & 1 deletion lib/capybara.rb
Expand Up @@ -291,13 +291,13 @@ module Driver; end
module RackTest; end module RackTest; end
module Selenium; end module Selenium; end


require 'capybara/helpers'
require 'capybara/session' require 'capybara/session'
require 'capybara/dsl' require 'capybara/dsl'
require 'capybara/server' require 'capybara/server'
require 'capybara/selector' require 'capybara/selector'
require 'capybara/query' require 'capybara/query'
require 'capybara/result' require 'capybara/result'
require 'capybara/helpers'
require 'capybara/version' require 'capybara/version'


require 'capybara/node/finders' require 'capybara/node/finders'
Expand Down
46 changes: 45 additions & 1 deletion lib/capybara/helpers.rb
@@ -1,6 +1,8 @@
# encoding: UTF-8 # encoding: UTF-8


module Capybara module Capybara

# @api private
module Helpers module Helpers
class << self class << self
## ##
Expand All @@ -25,7 +27,7 @@ def normalize_whitespace(text)
# @return [String] Escaped text # @return [String] Escaped text
# #
def to_regexp(text) def to_regexp(text)
text.is_a?(Regexp) ? text : Regexp.escape(normalize_whitespace(text)) text.is_a?(Regexp) ? text : Regexp.new(Regexp.escape(normalize_whitespace(text)))
end end


## ##
Expand All @@ -47,4 +49,46 @@ def inject_asset_host(html)
end end
end end
end end

# @api private
module CountHelpers
class << self
def matches_count?(count, options={})
case
when options[:between]
options[:between] === count
when options[:count]
Integer(options[:count]) == count
when options[:maximum]
Integer(options[:maximum]) >= count
when options[:minimum]
Integer(options[:minimum]) <= count
else
count > 0
end
end

def failure_message(description, options={})
message = "expected to find #{description}"
if options[:count]
message << " #{options[:count]} #{declension('time', 'times', options[:count])}"
elsif options[:between]
message << " between #{options[:between].first} and #{options[:between].last} times"
elsif options[:maximum]
message << " at most #{options[:maximum]} #{declension('time', 'times', options[:maximum])}"
elsif options[:minimum]
message << " at least #{options[:minimum]} #{declension('time', 'times', options[:minimum])}"
end
message
end

def declension(singular, plural, count)
if count == 1
singular
else
plural
end
end
end
end
end end
6 changes: 3 additions & 3 deletions lib/capybara/node/base.rb
Expand Up @@ -63,12 +63,12 @@ def reload
# until a certain amount of time passes. The amount of time defaults to # until a certain amount of time passes. The amount of time defaults to
# {Capybara.default_wait_time} and can be overriden through the `seconds` # {Capybara.default_wait_time} and can be overriden through the `seconds`
# argument. This time is compared with the system time to see how much # argument. This time is compared with the system time to see how much
# time has passed. If the return value of {Time.now} is stubbed out, # time has passed. If the return value of `Time.now` is stubbed out,
# Capybara will raise `Capybara::FrozenInTime`. # Capybara will raise `Capybara::FrozenInTime`.
# #
# @param [Integer] seconds Number of seconds to retry this block # @param [Integer] seconds Number of seconds to retry this block
# @return [Object] The result of the given block # @return [Object] The result of the given block
# @raise [Capybara::FrozenInTime] If the return value of {Time.now} appears stuck # @raise [Capybara::FrozenInTime] If the return value of `Time.now` appears stuck
# #
def synchronize(seconds=Capybara.default_wait_time) def synchronize(seconds=Capybara.default_wait_time)
start_time = Time.now start_time = Time.now
Expand Down
59 changes: 37 additions & 22 deletions lib/capybara/node/matchers.rb
Expand Up @@ -28,8 +28,12 @@ module Matchers
# page.has_selector?(:xpath, XPath.descendant(:p)) # page.has_selector?(:xpath, XPath.descendant(:p))
# #
# @param (see Capybara::Node::Finders#all) # @param (see Capybara::Node::Finders#all)
# @option options [Integer] :count (nil) Number of times the expression should occur # @param options a customizable set of options
# @return [Boolean] If the expression exists # @option options [Integer] :count (nil) Number of times the text should occur
# @option options [Integer] :minimum (nil) Minimum number of times the text should occur
# @option options [Integer] :maximum (nil) Maximum number of times the text should occur
# @option options [Range] :between (nil) Range of times that should contain number of times text occurs
# @return [Boolean] If the expression exists
# #
def has_selector?(*args) def has_selector?(*args)
assert_selector(*args) assert_selector(*args)
Expand Down Expand Up @@ -71,7 +75,7 @@ def has_no_selector?(*args)
# #
# page.assert_selector('li', :text => 'Horse', :visible => true) # page.assert_selector('li', :text => 'Horse', :visible => true)
# #
# {assert_selector} can also accept XPath expressions generated by the # `assert_selector` can also accept XPath expressions generated by the
# XPath gem: # XPath gem:
# #
# page.assert_selector(:xpath, XPath.descendant(:p)) # page.assert_selector(:xpath, XPath.descendant(:p))
Expand Down Expand Up @@ -193,18 +197,26 @@ def has_no_css?(path, options={})
# Checks if the page or current node has the given text content, # Checks if the page or current node has the given text content,
# ignoring any HTML tags and normalizing whitespace. # ignoring any HTML tags and normalizing whitespace.
# #
# This only matches displayable text and specifically excludes text # By default it will check if the text occurs at least once,
# contained within non-display nodes such as script or head tags. # but a different number can be specified.
#
# page.has_text?('lorem ipsum', between: 2..4)
# #
# @param [:all, :visible] type Whether to only check for visible or all text # This will check if the text occurs from 2 to 4 times.
# @param [String] content The text to check for
# @return [Boolean] Whether it exists
# #
def has_text?(type=nil, content) # @overload has_text?([type], text, [options])
# @param [:all, :visible] type Whether to check for only visible or all text
# @param [String, Regexp] text The text/regexp to check for
# @param [Hash] options additional options
# @option options [Integer] :count (nil) Number of times the text should occur
# @option options [Integer] :minimum (nil) Minimum number of times the text should occur
# @option options [Integer] :maximum (nil) Maximum number of times the text should occur
# @option options [Range] :between (nil) Range of times that should contain number of times text occurs
# @return [Boolean] Whether it exists
#
def has_text?(*args)
synchronize do synchronize do
unless Capybara::Helpers.normalize_whitespace(text(type)).match(Capybara::Helpers.to_regexp(content)) raise ExpectationNotMet unless text_found?(*args)
raise ExpectationNotMet
end
end end
return true return true
rescue Capybara::ExpectationNotMet rescue Capybara::ExpectationNotMet
Expand All @@ -217,17 +229,12 @@ def has_text?(type=nil, content)
# Checks if the page or current node does not have the given text # Checks if the page or current node does not have the given text
# content, ignoring any HTML tags and normalizing whitespace. # content, ignoring any HTML tags and normalizing whitespace.
# #
# This only matches displayable text and specifically excludes text # @param (see #has_text?)
# contained within non-display nodes such as script or head tags. # @return [Boolean] Whether it doesn't exist
#
# @param [String] content The text to check for
# @return [Boolean] Whether it doesn't exist
# #
def has_no_text?(type=nil, content) def has_no_text?(*args)
synchronize do synchronize do
if Capybara::Helpers.normalize_whitespace(text(type)).match(Capybara::Helpers.to_regexp(content)) raise ExpectationNotMet if text_found?(*args)
raise ExpectationNotMet
end
end end
return true return true
rescue Capybara::ExpectationNotMet rescue Capybara::ExpectationNotMet
Expand Down Expand Up @@ -451,8 +458,16 @@ def ==(other)
self.eql?(other) or (other.respond_to?(:base) and base == other.base) self.eql?(other) or (other.respond_to?(:base) and base == other.base)
end end


private private


def text_found?(*args)
type = args.shift if args.first.is_a?(Symbol) or args.first.nil?
content = args.shift
options = (args.first.is_a?(Hash))? args.first : {}
count = Capybara::Helpers.normalize_whitespace(text(type)).scan(Capybara::Helpers.to_regexp(content)).count

Capybara::CountHelpers.matches_count?(count, options)
end
end end
end end
end end
15 changes: 0 additions & 15 deletions lib/capybara/query.rb
Expand Up @@ -53,21 +53,6 @@ def matches_filters?(node)
end end
end end


def matches_count?(count)
case
when options[:between]
options[:between] === count
when options[:count]
options[:count].to_i == count
when options[:maximum]
options[:maximum].to_i >= count
when options[:minimum]
options[:minimum].to_i <= count
else
count > 0
end
end

def visible def visible
if options.has_key?(:visible) if options.has_key?(:visible)
case @options[:visible] case @options[:visible]
Expand Down
28 changes: 4 additions & 24 deletions lib/capybara/result.rb
Expand Up @@ -32,23 +32,13 @@ def initialize(elements, query)
def_delegators :@result, :each, :[], :at, :size, :count, :length, :first, :last, :empty? def_delegators :@result, :each, :[], :at, :size, :count, :length, :first, :last, :empty?


def matches_count? def matches_count?
@query.matches_count?(@result.size) Capybara::CountHelpers.matches_count?(@result.size, @query.options)
end end


def failure_message def failure_message
message = if @query.options[:count] message = Capybara::CountHelpers.failure_message(@query.description, @query.options)
"expected #{@query.description} to be found #{@query.options[:count]} #{declension("time", "times", @query.options[:count])}"
elsif @query.options[:between]
"expected #{@query.description} to be found between #{@query.options[:between].first} and #{@query.options[:between].last} times"
elsif @query.options[:maximum]
"expected #{@query.description} to be found at most #{@query.options[:maximum]} #{declension("time", "times", @query.options[:maximum])}"
elsif @query.options[:minimum]
"expected #{@query.description} to be found at least #{@query.options[:minimum]} #{declension("time", "times", @query.options[:minimum])}"
else
"expected to find #{@query.description}"
end
if count > 0 if count > 0
message << ", found #{count} #{declension("match", "matches")}: " << @result.map(&:text).map(&:inspect).join(", ") message << ", found #{count} #{Capybara::CountHelpers.declension("match", "matches", count)}: " << @result.map(&:text).map(&:inspect).join(", ")
else else
message << " but there were no matches" message << " but there were no matches"
end end
Expand All @@ -60,17 +50,7 @@ def failure_message
end end


def negative_failure_message def negative_failure_message
"expected not to find #{@query.description}, but there #{declension("was", "were")} #{count} #{declension("match", "matches")}" failure_message.sub(/(to be found|to find)/, 'not \1')
end

private

def declension(singular, plural, count=count)
if count == 1
singular
else
plural
end
end end
end end
end end
38 changes: 19 additions & 19 deletions lib/capybara/rspec/matchers.rb
Expand Up @@ -33,38 +33,41 @@ def query
end end


class HaveText < Matcher class HaveText < Matcher
attr_reader :text, :type attr_reader :type, :content, :options


def initialize(type, text) def initialize(*args)
@type = type @type = args.shift if args.first.is_a?(Symbol)
@text = text @content = args.shift
@options = (args.first.is_a?(Hash))? args.first : {}
end end


def matches?(actual) def matches?(actual)
@actual = wrap(actual) @actual = wrap(actual)
@actual.has_text?(type, text) @actual.has_text?(type, content, options)
end end


def does_not_match?(actual) def does_not_match?(actual)
@actual = wrap(actual) @actual = wrap(actual)
@actual.has_no_text?(type, text) @actual.has_no_text?(type, content, options)
end end


def failure_message_for_should def failure_message_for_should
"expected there to be text #{format(text)} in #{format(@actual.text(type))}" message = Capybara::CountHelpers.failure_message(description, options)
message << " in #{format(@actual.text(type))}"
message
end end


def failure_message_for_should_not def failure_message_for_should_not
"expected there not to be text #{format(text)} in #{format(@actual.text(type))}" failure_message_for_should.sub(/(to find)/, 'not \1')
end end


def description def description
"have text #{format(text)}" "text #{format(content)}"
end end


def format(text) def format(content)
text = Capybara::Helpers.normalize_whitespace(text) unless text.is_a? Regexp content = Capybara::Helpers.normalize_whitespace(content) unless content.is_a? Regexp
text.inspect content.inspect
end end
end end


Expand Down Expand Up @@ -106,17 +109,14 @@ def have_xpath(xpath, options={})
HaveSelector.new(:xpath, xpath, options) HaveSelector.new(:xpath, xpath, options)
end end


def have_css(css, options={}) def have_css(css, options={})
HaveSelector.new(:css, css, options) HaveSelector.new(:css, css, options)
end end


def have_content(type=nil, text) def have_text(*args)
HaveText.new(type, text) HaveText.new(*args)
end

def have_text(type=nil, text)
HaveText.new(type, text)
end end
alias_method :have_content, :have_text


def have_title(title) def have_title(title)
HaveTitle.new(title) HaveTitle.new(title)
Expand Down

0 comments on commit e580913

Please sign in to comment.