Skip to content

Commit

Permalink
Merge pull request #2075 from teamcapybara/predicates_wait
Browse files Browse the repository at this point in the history
WIP: Add Capybara.predicates_wait setting - defaults to true
  • Loading branch information
twalpole authored Aug 14, 2018
2 parents a7ba3e9 + 97bcbe1 commit 5bf3e4a
Show file tree
Hide file tree
Showing 9 changed files with 62 additions and 40 deletions.
1 change: 1 addition & 0 deletions History.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ Release date: unreleased

* Workaround geckodriver/firefox send_keys issues as much as possible using the Selenium actions API
* Workaround lack of HTML5 native drag and drop events when using Selenium driver with Chrome and FF >= 62
* `Capybara.predicates_wait` option which sets whether or not Capybaras matcher predicate methods (`has_css?`, `has_selector?`, `has_text?`, etc.) default to using waiting/retrying behavior (defaults to true)

# Version 3.5.1
Release date: 2018-08-03
Expand Down
2 changes: 2 additions & 0 deletions lib/capybara.rb
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ class << self
# [server = Symbol] The name of the registered server to use when running the app under test (Default: :webrick)
# [default_set_options = Hash] The default options passed to Node::set (Default: {})
# [test_id = Symbol/String/nil] Optional attribute to match locator aginst with builtin selectors along with id (Default: nil)
# [predicates_wait = Boolean] Whether Capybaras predicate matchers use waiting behavior by default (Default: true)
#
# === DSL Options
#
Expand Down Expand Up @@ -484,6 +485,7 @@ module Selenium; end
config.reuse_server = true
config.default_set_options = {}
config.test_id = nil
config.predicates_wait = true
end

Capybara.register_driver :rack_test do |app|
Expand Down
8 changes: 2 additions & 6 deletions lib/capybara/node/document_matchers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,7 @@ def assert_no_title(title, **options)
# @return [Boolean]
#
def has_title?(title, **options)
assert_title(title, options)
rescue Capybara::ExpectationNotMet
false
make_predicate(options) { assert_title(title, options) }
end

##
Expand All @@ -50,9 +48,7 @@ def has_title?(title, **options)
# @return [Boolean]
#
def has_no_title?(title, **options)
assert_no_title(title, options)
rescue Capybara::ExpectationNotMet
false
make_predicate(options) { assert_no_title(title, options) }
end

private
Expand Down
47 changes: 20 additions & 27 deletions lib/capybara/node/matchers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,8 @@ module Matchers
# @option args [Range] :between (nil) Range of times that should contain number of times text occurs
# @return [Boolean] If the expression exists
#
def has_selector?(*args, &optional_filter_block)
assert_selector(*args, &optional_filter_block)
rescue Capybara::ExpectationNotMet
false
def has_selector?(*args, **options, &optional_filter_block)
make_predicate(options) { assert_selector(*args, options, &optional_filter_block) }
end

##
Expand All @@ -50,10 +48,8 @@ def has_selector?(*args, &optional_filter_block)
# @param (see Capybara::Node::Finders#has_selector?)
# @return [Boolean]
#
def has_no_selector?(*args, &optional_filter_block)
assert_no_selector(*args, &optional_filter_block)
rescue Capybara::ExpectationNotMet
false
def has_no_selector?(*args, **options, &optional_filter_block)
make_predicate(options) { assert_no_selector(*args, options, &optional_filter_block) }
end

##
Expand All @@ -66,9 +62,7 @@ def has_no_selector?(*args, &optional_filter_block)
# @return [Boolean] If the styles match
#
def has_style?(styles, **options)
assert_style(styles, **options)
rescue Capybara::ExpectationNotMet
false
make_predicate(options) { assert_style(styles, options) }
end

##
Expand Down Expand Up @@ -554,10 +548,8 @@ def refute_matches_selector(*args, &optional_filter_block)
# @param (see Capybara::Node::Finders#has_selector?)
# @return [Boolean]
#
def matches_selector?(*args, &optional_filter_block)
assert_matches_selector(*args, &optional_filter_block)
rescue Capybara::ExpectationNotMet
false
def matches_selector?(*args, **options, &optional_filter_block)
make_predicate(options) { assert_matches_selector(*args, options, &optional_filter_block) }
end

##
Expand Down Expand Up @@ -590,10 +582,8 @@ def matches_css?(css, **options, &optional_filter_block)
# @param (see Capybara::Node::Finders#has_selector?)
# @return [Boolean]
#
def not_matches_selector?(*args, &optional_filter_block)
assert_not_matches_selector(*args, &optional_filter_block)
rescue Capybara::ExpectationNotMet
false
def not_matches_selector?(*args, **options, &optional_filter_block)
make_predicate(options) { assert_not_matches_selector(*args, options, &optional_filter_block) }
end

##
Expand Down Expand Up @@ -683,10 +673,8 @@ def assert_no_text(*args)
# @macro text_query_params
# @return [Boolean] Whether it exists
#
def has_text?(*args)
assert_text(*args)
rescue Capybara::ExpectationNotMet
false
def has_text?(*args, **options)
make_predicate(options) { assert_text(*args, options) }
end
alias_method :has_content?, :has_text?

Expand All @@ -697,10 +685,8 @@ def has_text?(*args)
# @macro text_query_params
# @return [Boolean] Whether it doesn't exist
#
def has_no_text?(*args)
assert_no_text(*args)
rescue Capybara::ExpectationNotMet
false
def has_no_text?(*args, **options)
make_predicate(options) { assert_no_text(*args, options) }
end
alias_method :has_no_content?, :has_no_text?

Expand Down Expand Up @@ -745,6 +731,13 @@ def _set_query_session_options(*query_args, **query_options)
query_options[:session_options] = session_options
query_args.push(query_options)
end

def make_predicate(options)
options[:wait] = 0 unless options.key?(:wait) || session_options.predicates_wait
yield
rescue Capybara::ExpectationNotMet
false
end
end
end
end
3 changes: 2 additions & 1 deletion lib/capybara/session/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ class SessionConfig
OPTIONS = %i[always_include_port run_server default_selector default_max_wait_time ignore_hidden_elements
automatic_reload match exact exact_text raise_server_errors visible_text_only
automatic_label_click enable_aria_label save_path asset_host default_host app_host
server_host server_port server_errors default_set_options disable_animation test_id].freeze
server_host server_port server_errors default_set_options disable_animation test_id
predicates_wait].freeze

attr_accessor(*OPTIONS)

Expand Down
15 changes: 9 additions & 6 deletions lib/capybara/session/matchers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,7 @@ def assert_no_current_path(path, **options)
# @return [Boolean]
#
def has_current_path?(path, **options)
assert_current_path(path, options)
rescue Capybara::ExpectationNotMet
false
make_predicate(options) { assert_current_path(path, options) }
end

##
Expand All @@ -62,9 +60,7 @@ def has_current_path?(path, **options)
# @return [Boolean]
#
def has_no_current_path?(path, **options)
assert_no_current_path(path, options)
rescue Capybara::ExpectationNotMet
false
make_predicate(options) { assert_no_current_path(path, options) }
end

private
Expand All @@ -76,5 +72,12 @@ def _verify_current_path(path, options)
end
true
end

def make_predicate(options)
options[:wait] = 0 unless options.key?(:wait) || config.predicates_wait
yield
rescue Capybara::ExpectationNotMet
false
end
end
end
20 changes: 20 additions & 0 deletions lib/capybara/spec/session/has_css_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,26 @@
expect(@session).to have_css("input[type='submit'][value='New Here']")
end

context 'with predicates_wait == true' do
it 'should wait for content to appear', requires: [:js] do
Capybara.predicates_wait = true
Capybara.default_max_wait_time = 2
@session.visit('/with_js')
@session.click_link('Click me')
expect(@session.has_css?("input[type='submit'][value='New Here']")).to be true
end
end

context 'with predicates_wait == false' do
it 'should not wait for content to appear', requires: [:js] do
Capybara.predicates_wait = false
Capybara.default_max_wait_time = 2
@session.visit('/with_js')
@session.click_link('Click me')
expect(@session.has_css?("input[type='submit'][value='New Here']")).to be false
end
end

context 'with between' do
it 'should be true if the content occurs within the range given' do
expect(@session).to have_css('p', between: 1..4)
Expand Down
5 changes: 5 additions & 0 deletions lib/capybara/spec/session/has_title_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

it 'should be true if the page has the given title' do
expect(@session).to have_title('with_js')
expect(@session.has_title?('with_js')).to be true
end

it 'should allow regexp matches' do
Expand All @@ -21,6 +22,7 @@

it 'should be false if the page has not the given title' do
expect(@session).not_to have_title('monkey')
expect(@session.has_title?('monkey')).to be false
end

it 'should default to exact: false matching' do
Expand All @@ -31,6 +33,8 @@
it 'should match exactly if exact: true option passed' do
expect(@session).to have_title('with_js', exact: true)
expect(@session).not_to have_title('with_', exact: true)
expect(@session.has_title?('with_js', exact: true)).to be true
expect(@session.has_title?('with_', exact: true)).to be false
end

it 'should match partial if exact: false option passed' do
Expand Down Expand Up @@ -62,5 +66,6 @@

it 'should be true if the page has not the given title' do
expect(@session).to have_no_title('monkey')
expect(@session.has_no_title?('monkey')).to be true
end
end
1 change: 1 addition & 0 deletions lib/capybara/spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ def reset!
Capybara.default_set_options = {}
Capybara.disable_animation = false
Capybara.test_id = nil
Capybara.predicates_wait = true
reset_threadsafe
end

Expand Down

0 comments on commit 5bf3e4a

Please sign in to comment.