Skip to content

Commit b6be01d

Browse files
committed
#first now raises when no matching elements
1 parent 60a75e6 commit b6be01d

File tree

7 files changed

+64
-50
lines changed

7 files changed

+64
-50
lines changed

History.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
# Version 3.0.0
2+
23
Release date: unreleased
34

5+
### Added
6+
7+
* Ruby 2.2+ required
8+
* RSpec 2.x no longer supported
9+
* Deprecated methods removed
10+
* `first` now raises ElementNotFound by default instead of returning nil, when no matches are found - Issue #1507
11+
412
# Version 2.17.0
513
Release date: 2018-01-02
614

@@ -244,6 +252,7 @@ Release date: 2016-10-05
244252
Release date: 2016-09-29
245253

246254
### Fixed
255+
247256
* :label built-in selector finds nested label/control by control id if the label has no 'for' attribute [Thomas Walpole]
248257
* Warning issued if an unknown selector type is specified [Thomas Walpole]
249258

lib/capybara.rb

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,6 @@ class << self
5151
# See {Capybara.configure}
5252
# @!method always_include_port
5353
# See {Capybara.configure}
54-
# @!method wait_on_first_by_default
55-
# See {Capybara.configure}
5654
SessionConfig::OPTIONS.each do |method|
5755
def_delegators :config, method, "#{method}="
5856
end
@@ -79,7 +77,6 @@ class << self
7977
# [ignore_hidden_elements = Boolean] Whether to ignore hidden elements on the page (Default: true)
8078
# [automatic_reload = Boolean] Whether to automatically reload elements as Capybara is waiting (Default: true)
8179
# [save_path = String] Where to put pages saved through save_(page|screenshot), save_and_open_(page|screenshot) (Default: Dir.pwd)
82-
# [wait_on_first_by_default = Boolean] Whether Node#first defaults to Capybara waiting behavior for at least 1 element to match (Default: false)
8380
# [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)
8481
# [enable_aria_label = Boolean] Whether fields, links, and buttons will match against aria-label attribute (Default: false)
8582
# [reuse_server = Boolean] Reuse the server thread between multiple sessions using the same app object (Default: true)
@@ -464,7 +461,6 @@ module Selenium; end
464461
config.raise_server_errors = true
465462
config.server_errors = [StandardError]
466463
config.visible_text_only = false
467-
config.wait_on_first_by_default = false
468464
config.automatic_label_click = false
469465
config.enable_aria_label = false
470466
config.reuse_server = true

lib/capybara/node/finders.rb

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -271,24 +271,19 @@ def all(*args, &optional_filter_block)
271271
##
272272
#
273273
# Find the first element on the page matching the given selector
274-
# and options, or nil if no element matches. By default no waiting
275-
# behavior occurs, however if {Capybara.wait_on_first_by_default} is set to true
276-
# it will trigger Capybara's waiting behavior for a minimum of 1 matching element to be found and
277-
# return the first. Waiting behavior can also be triggered by passing in any of the count
278-
# expectation options.
274+
# and options. Will raise an error if no matching element is found
279275
#
280276
# @overload first([kind], locator, options)
281277
# @param [:css, :xpath] kind The type of selector
282278
# @param [String] locator The selector
283279
# @param [Hash] options Additional options; see {#all}
284280
# @return [Capybara::Node::Element] The found element or nil
285281
#
286-
def first(*args, **options, &optional_filter_block)
287-
if session_options.wait_on_first_by_default
288-
options = {minimum: 1}.merge(options)
289-
end
282+
def first(*args, allow_nil: false, **options, &optional_filter_block)
283+
options = {minimum: 1}.merge(options)
290284
all(*args, **options, &optional_filter_block).first
291-
rescue Capybara::ExpectationNotMet
285+
rescue Capybara::ElementNotFound
286+
raise unless allow_nil
292287
nil
293288
end
294289

lib/capybara/session/config.rb

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
module Capybara
55
class SessionConfig
66
OPTIONS = [:always_include_port, :run_server, :default_selector, :default_max_wait_time, :ignore_hidden_elements,
7-
:automatic_reload, :match, :exact, :exact_text, :raise_server_errors, :visible_text_only, :wait_on_first_by_default,
7+
:automatic_reload, :match, :exact, :exact_text, :raise_server_errors, :visible_text_only,
88
:automatic_label_click, :enable_aria_label, :save_path, :exact_options, :asset_host, :default_host, :app_host,
99
:save_and_open_page_path, :server_host, :server_port, :server_errors]
1010

@@ -31,8 +31,6 @@ class SessionConfig
3131
# See {Capybara.configure}
3232
#@!method visible_text_only
3333
# See {Capybara.configure}
34-
#@!method wait_on_first_by_default
35-
# See {Capybara.configure}
3634
#@!method automatic_label_click
3735
# See {Capybara.configure}
3836
#@!method enable_aria_label

lib/capybara/spec/session/first_spec.rb

Lines changed: 48 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,14 @@
99
expect(@session.first("//input[@id='test_field']").value).to eq('monkey')
1010
end
1111

12-
it "should return nil when nothing was found" do
13-
expect(@session.first('//div[@id="nosuchthing"]')).to be_nil
12+
it "should raise ElementNotFound when nothing was found" do
13+
expect do
14+
@session.first('//div[@id="nosuchthing"]')
15+
end.to raise_error Capybara::ElementNotFound
16+
end
17+
18+
it "should return nil when nothing was found if count options allow no results" do
19+
expect(@session.first('//div[@id="nosuchthing"]', minimum: 0)).to be_nil
1420
end
1521

1622
it "should accept an XPath instance" do
@@ -22,7 +28,7 @@
2228

2329
it "should warn when unused parameters are passed" do
2430
expect_any_instance_of(Kernel).to receive(:warn).with(/Unused parameters passed.*unused text/)
25-
@session.first(:css, '.header h2', 'unused text')
31+
@session.first(:css, 'h1', 'unused text')
2632
end
2733

2834
context "with css selectors" do
@@ -49,40 +55,52 @@
4955

5056
context "with visible filter" do
5157
it "should only find visible nodes when true" do
52-
expect(@session.first(:css, "a#invisible", visible: true)).to be_nil
58+
expect do
59+
@session.first(:css, "a#invisible", visible: true)
60+
end.to raise_error Capybara::ElementNotFound
5361
end
5462

5563
it "should find nodes regardless of whether they are invisible when false" do
56-
expect(@session.first(:css, "a#invisible", visible: false)).not_to be_nil
57-
expect(@session.first(:css, "a#invisible", visible: false, text: 'hidden link')).not_to be_nil
58-
expect(@session.first(:css, "a#visible", visible: false)).not_to be_nil
64+
expect(@session.first(:css, "a#invisible", visible: false)).to be
65+
expect(@session.first(:css, "a#invisible", visible: false, text: 'hidden link')).to be
66+
expect(@session.first(:css, "a#visible", visible: false)).to be
5967
end
6068

6169
it "should find nodes regardless of whether they are invisible when :all" do
62-
expect(@session.first(:css, "a#invisible", visible: :all)).not_to be_nil
63-
expect(@session.first(:css, "a#invisible", visible: :all, text: 'hidden link')).not_to be_nil
64-
expect(@session.first(:css, "a#visible", visible: :all)).not_to be_nil
70+
expect(@session.first(:css, "a#invisible", visible: :all)).to be
71+
expect(@session.first(:css, "a#invisible", visible: :all, text: 'hidden link')).to be
72+
expect(@session.first(:css, "a#visible", visible: :all)).to be
6573
end
6674

6775
it "should find only hidden nodes when :hidden" do
68-
expect(@session.first(:css, "a#invisible", visible: :hidden)).not_to be_nil
69-
expect(@session.first(:css, "a#invisible", visible: :hidden, text: 'hidden link')).not_to be_nil
70-
expect(@session.first(:css, "a#invisible", visible: :hidden, text: 'not hidden link')).to be_nil
71-
expect(@session.first(:css, "a#visible", visible: :hidden)).to be_nil
76+
expect(@session.first(:css, "a#invisible", visible: :hidden)).to be
77+
expect(@session.first(:css, "a#invisible", visible: :hidden, text: 'hidden link')).to be
78+
expect do
79+
@session.first(:css, "a#invisible", visible: :hidden, text: 'not hidden link')
80+
end.to raise_error Capybara::ElementNotFound
81+
expect do
82+
@session.first(:css, "a#visible", visible: :hidden)
83+
end.to raise_error Capybara::ElementNotFound
7284
end
7385

7486
it "should find only visible nodes when :visible" do
75-
expect(@session.first(:css, "a#invisible", visible: :visible)).to be_nil
76-
expect(@session.first(:css, "a#invisible", visible: :visible, text: 'hidden link')).to be_nil
77-
expect(@session.first(:css, "a#visible", visible: :visible)).not_to be_nil
87+
expect do
88+
@session.first(:css, "a#invisible", visible: :visible)
89+
end.to raise_error Capybara::ElementNotFound
90+
expect do
91+
@session.first(:css, "a#invisible", visible: :visible, text: 'hidden link')
92+
end.to raise_error Capybara::ElementNotFound
93+
expect(@session.first(:css, "a#visible", visible: :visible)).to be
7894
end
7995

8096
it "should default to Capybara.ignore_hidden_elements" do
8197
Capybara.ignore_hidden_elements = true
82-
expect(@session.first(:css, "a#invisible")).to be_nil
98+
expect do
99+
@session.first(:css, "a#invisible")
100+
end.to raise_error Capybara::ElementNotFound
83101
Capybara.ignore_hidden_elements = false
84-
expect(@session.first(:css, "a#invisible")).not_to be_nil
85-
expect(@session.first(:css, "a")).not_to be_nil
102+
expect(@session.first(:css, "a#invisible")).to be
103+
expect(@session.first(:css, "a")).to be
86104
end
87105
end
88106

@@ -93,35 +111,34 @@
93111

94112
it "should find the first element using the given locator" do
95113
@session.within(:xpath, "//div[@id='for_bar']") do
96-
expect(@session.first('.//form')).not_to be_nil
114+
expect(@session.first('.//form')).to be
97115
end
98116
end
99117
end
100118

101-
context "with Capybara.wait_on_first_by_default", requires: [:js] do
119+
context "waiting behavior", requires: [:js] do
102120
before do
103121
@session.visit('/with_js')
104122
end
105123

106-
it "should not wait if false" do
107-
Capybara.wait_on_first_by_default = false
124+
it "should not wait if minimum: 0" do
108125
@session.click_link('clickable')
109-
expect(@session.first(:css, 'a#has-been-clicked')).to be_nil
126+
expect(@session.first(:css, 'a#has-been-clicked', minimum: 0)).to be_nil
110127
end
111128

112-
it "should wait for at least one match if true" do
113-
Capybara.wait_on_first_by_default = true
129+
it "should wait for at least one match by default" do
114130
Capybara.using_wait_time(3) do
115131
@session.click_link('clickable')
116132
expect(@session.first(:css, 'a#has-been-clicked')).not_to be_nil
117133
end
118134
end
119135

120-
it "should return nil after waiting if no match" do
121-
Capybara.wait_on_first_by_default = true
136+
it "should return nil after waiting if no match and allow_nil is true" do
137+
@session.click_link('clickable')
138+
start_time = Time.now
122139
Capybara.using_wait_time(3) do
123-
@session.click_link('clickable')
124-
expect(@session.first(:css, 'a#not-a-real-link')).to be_nil
140+
expect(@session.first(:css, 'a#not-a-real-link', allow_nil: true)).to be_nil
141+
expect(Time.now-start_time).to be > 3
125142
end
126143
end
127144
end

lib/capybara/spec/spec_helper.rb

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ def reset!
3030
Capybara.raise_server_errors = true
3131
Capybara.visible_text_only = false
3232
Capybara.match = :smart
33-
Capybara.wait_on_first_by_default = false
3433
Capybara.enable_aria_label = false
3534
reset_threadsafe
3635
end

spec/per_session_config_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
session = Capybara::Session.new(:rack_test, TestApp)
1010
[:default_host, :app_host, :save_and_open_page_path,
1111
:always_include_port, :run_server, :default_selector, :default_max_wait_time, :ignore_hidden_elements,
12-
:automatic_reload, :match, :exact, :raise_server_errors, :visible_text_only, :wait_on_first_by_default,
12+
:automatic_reload, :match, :exact, :raise_server_errors, :visible_text_only,
1313
:automatic_label_click, :enable_aria_label,
1414
:save_path, :exact_options, :asset_host].each do |m|
1515
expect(session.config.public_send(m)).to eq Capybara.public_send(m)

0 commit comments

Comments
 (0)