1
0
Fork 0
mirror of https://github.com/teamcapybara/capybara.git synced 2022-11-09 12:08:07 -05:00

#first now raises when no matching elements

This commit is contained in:
Thomas Walpole 2016-11-14 09:31:03 -08:00
parent 60a75e6cc9
commit b6be01daf3
7 changed files with 64 additions and 50 deletions

View file

@ -1,6 +1,14 @@
# Version 3.0.0 # Version 3.0.0
Release date: unreleased Release date: unreleased
### Added
* Ruby 2.2+ required
* RSpec 2.x no longer supported
* Deprecated methods removed
* `first` now raises ElementNotFound by default instead of returning nil, when no matches are found - Issue #1507
# Version 2.17.0 # Version 2.17.0
Release date: 2018-01-02 Release date: 2018-01-02
@ -244,6 +252,7 @@ Release date: 2016-10-05
Release date: 2016-09-29 Release date: 2016-09-29
### Fixed ### Fixed
* :label built-in selector finds nested label/control by control id if the label has no 'for' attribute [Thomas Walpole] * :label built-in selector finds nested label/control by control id if the label has no 'for' attribute [Thomas Walpole]
* Warning issued if an unknown selector type is specified [Thomas Walpole] * Warning issued if an unknown selector type is specified [Thomas Walpole]

View file

@ -51,8 +51,6 @@ module Capybara
# See {Capybara.configure} # See {Capybara.configure}
# @!method always_include_port # @!method always_include_port
# See {Capybara.configure} # See {Capybara.configure}
# @!method wait_on_first_by_default
# See {Capybara.configure}
SessionConfig::OPTIONS.each do |method| SessionConfig::OPTIONS.each do |method|
def_delegators :config, method, "#{method}=" def_delegators :config, method, "#{method}="
end end
@ -79,7 +77,6 @@ module Capybara
# [ignore_hidden_elements = Boolean] Whether to ignore hidden elements on the page (Default: true) # [ignore_hidden_elements = Boolean] Whether to ignore hidden elements on the page (Default: true)
# [automatic_reload = Boolean] Whether to automatically reload elements as Capybara is waiting (Default: true) # [automatic_reload = Boolean] Whether to automatically reload elements as Capybara is waiting (Default: true)
# [save_path = String] Where to put pages saved through save_(page|screenshot), save_and_open_(page|screenshot) (Default: Dir.pwd) # [save_path = String] Where to put pages saved through save_(page|screenshot), save_and_open_(page|screenshot) (Default: Dir.pwd)
# [wait_on_first_by_default = Boolean] Whether Node#first defaults to Capybara waiting behavior for at least 1 element to match (Default: false)
# [automatic_label_click = Boolean] Whether Node#choose, Node#check, Node#uncheck will attempt to click the associated label element if the checkbox/radio button are non-visible (Default: false) # [automatic_label_click = Boolean] Whether Node#choose, Node#check, Node#uncheck will attempt to click the associated label element if the checkbox/radio button are non-visible (Default: false)
# [enable_aria_label = Boolean] Whether fields, links, and buttons will match against aria-label attribute (Default: false) # [enable_aria_label = Boolean] Whether fields, links, and buttons will match against aria-label attribute (Default: false)
# [reuse_server = Boolean] Reuse the server thread between multiple sessions using the same app object (Default: true) # [reuse_server = Boolean] Reuse the server thread between multiple sessions using the same app object (Default: true)
@ -464,7 +461,6 @@ Capybara.configure do |config|
config.raise_server_errors = true config.raise_server_errors = true
config.server_errors = [StandardError] config.server_errors = [StandardError]
config.visible_text_only = false config.visible_text_only = false
config.wait_on_first_by_default = false
config.automatic_label_click = false config.automatic_label_click = false
config.enable_aria_label = false config.enable_aria_label = false
config.reuse_server = true config.reuse_server = true

View file

@ -271,11 +271,7 @@ module Capybara
## ##
# #
# Find the first element on the page matching the given selector # Find the first element on the page matching the given selector
# and options, or nil if no element matches. By default no waiting # and options. Will raise an error if no matching element is found
# behavior occurs, however if {Capybara.wait_on_first_by_default} is set to true
# it will trigger Capybara's waiting behavior for a minimum of 1 matching element to be found and
# return the first. Waiting behavior can also be triggered by passing in any of the count
# expectation options.
# #
# @overload first([kind], locator, options) # @overload first([kind], locator, options)
# @param [:css, :xpath] kind The type of selector # @param [:css, :xpath] kind The type of selector
@ -283,12 +279,11 @@ module Capybara
# @param [Hash] options Additional options; see {#all} # @param [Hash] options Additional options; see {#all}
# @return [Capybara::Node::Element] The found element or nil # @return [Capybara::Node::Element] The found element or nil
# #
def first(*args, **options, &optional_filter_block) def first(*args, allow_nil: false, **options, &optional_filter_block)
if session_options.wait_on_first_by_default
options = {minimum: 1}.merge(options) options = {minimum: 1}.merge(options)
end
all(*args, **options, &optional_filter_block).first all(*args, **options, &optional_filter_block).first
rescue Capybara::ExpectationNotMet rescue Capybara::ElementNotFound
raise unless allow_nil
nil nil
end end

View file

@ -4,7 +4,7 @@ require 'delegate'
module Capybara module Capybara
class SessionConfig class SessionConfig
OPTIONS = [:always_include_port, :run_server, :default_selector, :default_max_wait_time, :ignore_hidden_elements, OPTIONS = [: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, :wait_on_first_by_default, :automatic_reload, :match, :exact, :exact_text, :raise_server_errors, :visible_text_only,
:automatic_label_click, :enable_aria_label, :save_path, :exact_options, :asset_host, :default_host, :app_host, :automatic_label_click, :enable_aria_label, :save_path, :exact_options, :asset_host, :default_host, :app_host,
:save_and_open_page_path, :server_host, :server_port, :server_errors] :save_and_open_page_path, :server_host, :server_port, :server_errors]
@ -31,8 +31,6 @@ module Capybara
# See {Capybara.configure} # See {Capybara.configure}
#@!method visible_text_only #@!method visible_text_only
# See {Capybara.configure} # See {Capybara.configure}
#@!method wait_on_first_by_default
# See {Capybara.configure}
#@!method automatic_label_click #@!method automatic_label_click
# See {Capybara.configure} # See {Capybara.configure}
#@!method enable_aria_label #@!method enable_aria_label

View file

@ -9,8 +9,14 @@ Capybara::SpecHelper.spec '#first' do
expect(@session.first("//input[@id='test_field']").value).to eq('monkey') expect(@session.first("//input[@id='test_field']").value).to eq('monkey')
end end
it "should return nil when nothing was found" do it "should raise ElementNotFound when nothing was found" do
expect(@session.first('//div[@id="nosuchthing"]')).to be_nil expect do
@session.first('//div[@id="nosuchthing"]')
end.to raise_error Capybara::ElementNotFound
end
it "should return nil when nothing was found if count options allow no results" do
expect(@session.first('//div[@id="nosuchthing"]', minimum: 0)).to be_nil
end end
it "should accept an XPath instance" do it "should accept an XPath instance" do
@ -22,7 +28,7 @@ Capybara::SpecHelper.spec '#first' do
it "should warn when unused parameters are passed" do it "should warn when unused parameters are passed" do
expect_any_instance_of(Kernel).to receive(:warn).with(/Unused parameters passed.*unused text/) expect_any_instance_of(Kernel).to receive(:warn).with(/Unused parameters passed.*unused text/)
@session.first(:css, '.header h2', 'unused text') @session.first(:css, 'h1', 'unused text')
end end
context "with css selectors" do context "with css selectors" do
@ -49,40 +55,52 @@ Capybara::SpecHelper.spec '#first' do
context "with visible filter" do context "with visible filter" do
it "should only find visible nodes when true" do it "should only find visible nodes when true" do
expect(@session.first(:css, "a#invisible", visible: true)).to be_nil expect do
@session.first(:css, "a#invisible", visible: true)
end.to raise_error Capybara::ElementNotFound
end end
it "should find nodes regardless of whether they are invisible when false" do it "should find nodes regardless of whether they are invisible when false" do
expect(@session.first(:css, "a#invisible", visible: false)).not_to be_nil expect(@session.first(:css, "a#invisible", visible: false)).to be
expect(@session.first(:css, "a#invisible", visible: false, text: 'hidden link')).not_to be_nil expect(@session.first(:css, "a#invisible", visible: false, text: 'hidden link')).to be
expect(@session.first(:css, "a#visible", visible: false)).not_to be_nil expect(@session.first(:css, "a#visible", visible: false)).to be
end end
it "should find nodes regardless of whether they are invisible when :all" do it "should find nodes regardless of whether they are invisible when :all" do
expect(@session.first(:css, "a#invisible", visible: :all)).not_to be_nil expect(@session.first(:css, "a#invisible", visible: :all)).to be
expect(@session.first(:css, "a#invisible", visible: :all, text: 'hidden link')).not_to be_nil expect(@session.first(:css, "a#invisible", visible: :all, text: 'hidden link')).to be
expect(@session.first(:css, "a#visible", visible: :all)).not_to be_nil expect(@session.first(:css, "a#visible", visible: :all)).to be
end end
it "should find only hidden nodes when :hidden" do it "should find only hidden nodes when :hidden" do
expect(@session.first(:css, "a#invisible", visible: :hidden)).not_to be_nil expect(@session.first(:css, "a#invisible", visible: :hidden)).to be
expect(@session.first(:css, "a#invisible", visible: :hidden, text: 'hidden link')).not_to be_nil expect(@session.first(:css, "a#invisible", visible: :hidden, text: 'hidden link')).to be
expect(@session.first(:css, "a#invisible", visible: :hidden, text: 'not hidden link')).to be_nil expect do
expect(@session.first(:css, "a#visible", visible: :hidden)).to be_nil @session.first(:css, "a#invisible", visible: :hidden, text: 'not hidden link')
end.to raise_error Capybara::ElementNotFound
expect do
@session.first(:css, "a#visible", visible: :hidden)
end.to raise_error Capybara::ElementNotFound
end end
it "should find only visible nodes when :visible" do it "should find only visible nodes when :visible" do
expect(@session.first(:css, "a#invisible", visible: :visible)).to be_nil expect do
expect(@session.first(:css, "a#invisible", visible: :visible, text: 'hidden link')).to be_nil @session.first(:css, "a#invisible", visible: :visible)
expect(@session.first(:css, "a#visible", visible: :visible)).not_to be_nil end.to raise_error Capybara::ElementNotFound
expect do
@session.first(:css, "a#invisible", visible: :visible, text: 'hidden link')
end.to raise_error Capybara::ElementNotFound
expect(@session.first(:css, "a#visible", visible: :visible)).to be
end end
it "should default to Capybara.ignore_hidden_elements" do it "should default to Capybara.ignore_hidden_elements" do
Capybara.ignore_hidden_elements = true Capybara.ignore_hidden_elements = true
expect(@session.first(:css, "a#invisible")).to be_nil expect do
@session.first(:css, "a#invisible")
end.to raise_error Capybara::ElementNotFound
Capybara.ignore_hidden_elements = false Capybara.ignore_hidden_elements = false
expect(@session.first(:css, "a#invisible")).not_to be_nil expect(@session.first(:css, "a#invisible")).to be
expect(@session.first(:css, "a")).not_to be_nil expect(@session.first(:css, "a")).to be
end end
end end
@ -93,35 +111,34 @@ Capybara::SpecHelper.spec '#first' do
it "should find the first element using the given locator" do it "should find the first element using the given locator" do
@session.within(:xpath, "//div[@id='for_bar']") do @session.within(:xpath, "//div[@id='for_bar']") do
expect(@session.first('.//form')).not_to be_nil expect(@session.first('.//form')).to be
end end
end end
end end
context "with Capybara.wait_on_first_by_default", requires: [:js] do context "waiting behavior", requires: [:js] do
before do before do
@session.visit('/with_js') @session.visit('/with_js')
end end
it "should not wait if false" do it "should not wait if minimum: 0" do
Capybara.wait_on_first_by_default = false
@session.click_link('clickable') @session.click_link('clickable')
expect(@session.first(:css, 'a#has-been-clicked')).to be_nil expect(@session.first(:css, 'a#has-been-clicked', minimum: 0)).to be_nil
end end
it "should wait for at least one match if true" do it "should wait for at least one match by default" do
Capybara.wait_on_first_by_default = true
Capybara.using_wait_time(3) do Capybara.using_wait_time(3) do
@session.click_link('clickable') @session.click_link('clickable')
expect(@session.first(:css, 'a#has-been-clicked')).not_to be_nil expect(@session.first(:css, 'a#has-been-clicked')).not_to be_nil
end end
end end
it "should return nil after waiting if no match" do it "should return nil after waiting if no match and allow_nil is true" do
Capybara.wait_on_first_by_default = true
Capybara.using_wait_time(3) do
@session.click_link('clickable') @session.click_link('clickable')
expect(@session.first(:css, 'a#not-a-real-link')).to be_nil start_time = Time.now
Capybara.using_wait_time(3) do
expect(@session.first(:css, 'a#not-a-real-link', allow_nil: true)).to be_nil
expect(Time.now-start_time).to be > 3
end end
end end
end end

View file

@ -30,7 +30,6 @@ module Capybara
Capybara.raise_server_errors = true Capybara.raise_server_errors = true
Capybara.visible_text_only = false Capybara.visible_text_only = false
Capybara.match = :smart Capybara.match = :smart
Capybara.wait_on_first_by_default = false
Capybara.enable_aria_label = false Capybara.enable_aria_label = false
reset_threadsafe reset_threadsafe
end end

View file

@ -9,7 +9,7 @@ RSpec.describe Capybara::SessionConfig do
session = Capybara::Session.new(:rack_test, TestApp) session = Capybara::Session.new(:rack_test, TestApp)
[:default_host, :app_host, :save_and_open_page_path, [:default_host, :app_host, :save_and_open_page_path,
:always_include_port, :run_server, :default_selector, :default_max_wait_time, :ignore_hidden_elements, :always_include_port, :run_server, :default_selector, :default_max_wait_time, :ignore_hidden_elements,
:automatic_reload, :match, :exact, :raise_server_errors, :visible_text_only, :wait_on_first_by_default, :automatic_reload, :match, :exact, :raise_server_errors, :visible_text_only,
:automatic_label_click, :enable_aria_label, :automatic_label_click, :enable_aria_label,
:save_path, :exact_options, :asset_host].each do |m| :save_path, :exact_options, :asset_host].each do |m|
expect(session.config.public_send(m)).to eq Capybara.public_send(m) expect(session.config.public_send(m)).to eq Capybara.public_send(m)