Skip to content

Commit

Permalink
Allow to override the retry time for clicks
Browse files Browse the repository at this point in the history
  • Loading branch information
twalpole committed Nov 13, 2018
1 parent dc6dd8e commit fb2f6e3
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 15 deletions.
3 changes: 2 additions & 1 deletion lib/capybara/node/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,10 @@ def reload
# @return [Object] The result of the given block
# @raise [Capybara::FrozenInTime] If the return value of `Time.now` appears stuck
#
def synchronize(seconds = session_options.default_max_wait_time, errors: nil)
def synchronize(seconds = nil, errors: nil)
return yield if session.synchronized

seconds = session_options.default_max_wait_time if seconds.nil?
session.synchronized = true
timer = Capybara::Helpers.timer(expire_in: seconds)
begin
Expand Down
20 changes: 10 additions & 10 deletions lib/capybara/node/element.rb
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,9 @@ def set(value, **options)
# Select this node if is an option element inside a select tag
#
# @return [Capybara::Node::Element] The element
def select_option
def select_option(wait: nil)
warn "Attempt to select disabled option: #{value || text}" if disabled?
synchronize { base.select_option }
synchronize(wait) { base.select_option }
self
end

Expand All @@ -136,8 +136,8 @@ def select_option
# Unselect this node if is an option element inside a multiple select tag
#
# @return [Capybara::Node::Element] The element
def unselect_option
synchronize { base.unselect_option }
def unselect_option(wait: nil)
synchronize(wait) { base.unselect_option }
self
end

Expand All @@ -152,8 +152,8 @@ def unselect_option
# @option offset [Integer] x X coordinate to offset the click location from the top left corner of the element
# @option offset [Integer] y Y coordinate to offset the click location from the top left corner of the element
# @return [Capybara::Node::Element] The element
def click(*keys, **offset)
synchronize { base.click(keys, offset) }
def click(*keys, wait: nil, **offset)
synchronize(wait) { base.click(keys, offset) }
self
end

Expand All @@ -163,8 +163,8 @@ def click(*keys, **offset)
#
# @macro click_modifiers
# @return [Capybara::Node::Element] The element
def right_click(*keys, **offset)
synchronize { base.right_click(keys, offset) }
def right_click(*keys, wait: nil, **offset)
synchronize(wait) { base.right_click(keys, offset) }
self
end

Expand All @@ -174,8 +174,8 @@ def right_click(*keys, **offset)
#
# @macro click_modifiers
# @return [Capybara::Node::Element] The element
def double_click(*keys, **offset)
synchronize { base.double_click(keys, offset) }
def double_click(*keys, wait: nil, **offset)
synchronize(wait) { base.double_click(keys, offset) }
self
end

Expand Down
49 changes: 45 additions & 4 deletions lib/capybara/spec/session/node_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,33 @@
tr = @session.find(:css, '#agent_table tr:first-child').click
expect(tr).to have_css('label', text: 'Clicked')
end

it 'should retry clicking', requires: [:js] do
@session.visit('/obscured')
obscured = @session.find(:css, '#obscured')
@session.execute_script <<~JS
setTimeout(function(){ $('#cover').hide(); }, 1000)
JS
expect { obscured.click }.not_to raise_error
end

it 'should allow to retry longer', requires: [:js] do
@session.visit('/obscured')
obscured = @session.find(:css, '#obscured')
@session.execute_script <<~JS
setTimeout(function(){ $('#cover').hide(); }, 3000)
JS
expect { obscured.click(wait: 4) }.not_to raise_error
end

it 'should not retry clicking when wait is disabled', requires: [:js] do
@session.visit('/obscured')
obscured = @session.find(:css, '#obscured')
@session.execute_script <<~JS
setTimeout(function(){ $('#cover').hide(); }, 2000)
JS
expect { obscured.click(wait: 0) }.to(raise_error { |e| expect(e).to be_an_invalid_element_error(@session) })
end
end

describe '#double_click', requires: [:js] do
Expand All @@ -436,6 +463,15 @@
expect(locations[:x].to_f).to be_within(1).of(10)
expect(locations[:y].to_f).to be_within(1).of(5)
end

it 'should retry clicking', requires: [:js] do
@session.visit('/obscured')
obscured = @session.find(:css, '#obscured')
@session.execute_script <<~JS
setTimeout(function(){ $('#cover').hide(); }, 1000)
JS
expect { obscured.double_click }.not_to raise_error
end
end

describe '#right_click', requires: [:js] do
Expand All @@ -461,6 +497,15 @@
expect(locations[:x].to_f).to be_within(1).of(10)
expect(locations[:y].to_f).to be_within(1).of(10)
end

it 'should retry clicking', requires: [:js] do
@session.visit('/obscured')
obscured = @session.find(:css, '#obscured')
@session.execute_script <<~JS
setTimeout(function(){ $('#cover').hide(); }, 1000)
JS
expect { obscured.right_click }.not_to raise_error
end
end

describe '#send_keys', requires: [:send_keys] do
Expand Down Expand Up @@ -670,8 +715,4 @@
end)
end
end

def be_an_invalid_element_error(session)
satisfy { |error| session.driver.invalid_element_errors.any? { |e| error.is_a? e } }
end
end
4 changes: 4 additions & 0 deletions lib/capybara/spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,10 @@ def extract_results(session)
# YAML.load Nokogiri::HTML(session.body).xpath("//pre[@id='results']").first.inner_html.lstrip
YAML.load Capybara::HTML(session.body).xpath("//pre[@id='results']").first.inner_html.lstrip
end

def be_an_invalid_element_error(session)
satisfy { |error| session.driver.invalid_element_errors.any? { |e| error.is_a? e } }
end
end
end

Expand Down
44 changes: 44 additions & 0 deletions lib/capybara/spec/views/obscured.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8"/>
<title>with_animation</title>
<script src="/jquery.js" type="text/javascript" charset="utf-8"></script>
<style>
div {
width: 400px;
height: 400px;
position: absolute;
}
#obscured {
z-index: 1;
background-color: red;
}
#cover {
z-index: 2;
background-color: blue;
}
#offscreen {
top: 2000px;
left: 2000px;
background-color: green;
}
#offscreen_wrapper {
top: 2000px;
left: 2000px;
overflow-x: scroll;
background-color: yellow;
}
</style>
</head>

<body id="with_animation">
<div id="obscured">
<input id="obscured_input"/>
</div>
<div id="cover"></div>
<div id="offscreen_wrapper">
<div id="offscreen"></div>
</div>
</body>
</html>

0 comments on commit fb2f6e3

Please sign in to comment.