Enable rack_test driver to reload nodes if stale
This commit is contained in:
parent
8930306e2f
commit
3d9c6997b0
|
@ -83,11 +83,18 @@ module Capybara
|
||||||
yield
|
yield
|
||||||
rescue StandardError => err
|
rescue StandardError => err
|
||||||
session.raise_server_error!
|
session.raise_server_error!
|
||||||
raise err unless driver.wait? && catch_error?(err, errors)
|
raise err unless catch_error?(err, errors)
|
||||||
raise err if timer.expired?
|
|
||||||
|
|
||||||
sleep(0.01)
|
if driver.wait?
|
||||||
reload if session_options.automatic_reload
|
raise err if timer.expired?
|
||||||
|
|
||||||
|
sleep(0.01)
|
||||||
|
reload if session_options.automatic_reload
|
||||||
|
else
|
||||||
|
old_base = @base
|
||||||
|
reload if session_options.automatic_reload
|
||||||
|
raise err if old_base == @base
|
||||||
|
end
|
||||||
retry
|
retry
|
||||||
ensure
|
ensure
|
||||||
session.synchronized = false
|
session.synchronized = false
|
||||||
|
|
|
@ -102,4 +102,8 @@ class Capybara::RackTest::Driver < Capybara::Driver::Base
|
||||||
def put(*args, &block); browser.put(*args, &block); end
|
def put(*args, &block); browser.put(*args, &block); end
|
||||||
def delete(*args, &block); browser.delete(*args, &block); end
|
def delete(*args, &block); browser.delete(*args, &block); end
|
||||||
def header(key, value); browser.header(key, value); end
|
def header(key, value); browser.header(key, value); end
|
||||||
|
|
||||||
|
def invalid_element_errors
|
||||||
|
[ Capybara::RackTest::Errors::StaleElementReferenceError ]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Capybara::RackTest::Errors
|
||||||
|
class StaleElementReferenceError < StandardError
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,5 +1,7 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'capybara/rack_test/errors'
|
||||||
|
|
||||||
class Capybara::RackTest::Node < Capybara::Driver::Node
|
class Capybara::RackTest::Node < Capybara::Driver::Node
|
||||||
BLOCK_ELEMENTS = %w[p h1 h2 h3 h4 h5 h6 ol ul pre address blockquote dl div fieldset form hr noscript table].freeze
|
BLOCK_ELEMENTS = %w[p h1 h2 h3 h4 h5 h6 ol ul pre address blockquote dl div fieldset form hr noscript table].freeze
|
||||||
|
|
||||||
|
@ -106,18 +108,27 @@ class Capybara::RackTest::Node < Capybara::Driver::Node
|
||||||
native.path
|
native.path
|
||||||
end
|
end
|
||||||
|
|
||||||
def find_xpath(locator)
|
def find_xpath(locator, **_hints)
|
||||||
native.xpath(locator).map { |el| self.class.new(driver, el) }
|
native.xpath(locator).map { |el| self.class.new(driver, el) }
|
||||||
end
|
end
|
||||||
|
|
||||||
def find_css(locator)
|
def find_css(locator, **_hints)
|
||||||
native.css(locator, Capybara::RackTest::CSSHandlers.new).map { |el| self.class.new(driver, el) }
|
native.css(locator, Capybara::RackTest::CSSHandlers.new).map { |el| self.class.new(driver, el) }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
self.public_instance_methods(false).each do |meth_name|
|
||||||
|
alias_method "unchecked_#{meth_name}", meth_name
|
||||||
|
private "unchecked_#{meth_name}"
|
||||||
|
define_method meth_name do |*args|
|
||||||
|
stale_check
|
||||||
|
send("unchecked_#{meth_name}", *args)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def ==(other)
|
def ==(other)
|
||||||
native == other.native
|
native == other.native
|
||||||
end
|
end
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
# @api private
|
# @api private
|
||||||
|
@ -141,6 +152,10 @@ protected
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def stale_check
|
||||||
|
raise Capybara::RackTest::Errors::StaleElementReferenceError unless native.document == driver.dom
|
||||||
|
end
|
||||||
|
|
||||||
def deselect_options
|
def deselect_options
|
||||||
select_node.find_xpath('.//option[@selected]').each { |node| node.native.remove_attribute('selected') }
|
select_node.find_xpath('.//option[@selected]').each { |node| node.native.remove_attribute('selected') }
|
||||||
end
|
end
|
||||||
|
|
|
@ -77,9 +77,9 @@ module Capybara
|
||||||
def is_displayed_atom # rubocop:disable Naming/PredicateName
|
def is_displayed_atom # rubocop:disable Naming/PredicateName
|
||||||
@@is_displayed_atom ||= begin
|
@@is_displayed_atom ||= begin
|
||||||
browser.send(:bridge).send(:read_atom, 'isDisplayed')
|
browser.send(:bridge).send(:read_atom, 'isDisplayed')
|
||||||
rescue StandardError
|
rescue StandardError
|
||||||
# If the atom doesn't exist or other error
|
# If the atom doesn't exist or other error
|
||||||
""
|
''
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -26,6 +26,29 @@ Capybara::SpecHelper.spec '#within' do
|
||||||
end
|
end
|
||||||
expect(@session).to have_content('Bar')
|
expect(@session).to have_content('Bar')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'should reload the node if the page is changed' do
|
||||||
|
@session.within(:css, '#for_foo') do
|
||||||
|
@session.visit('/with_scope_other')
|
||||||
|
expect(@session).to have_content('Different text')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should reload multiple nodes if the page is changed' do
|
||||||
|
@session.within(:css, '#for_bar') do
|
||||||
|
@session.within(:css, 'form[action="/redirect"]') do
|
||||||
|
@session.refresh
|
||||||
|
expect(@session).to have_content('First Name')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should error if the page is changed and a matching node no longer exists' do
|
||||||
|
@session.within(:css, '#for_foo') do
|
||||||
|
@session.visit('/')
|
||||||
|
expect { @session.text }.to raise_error(StandardError)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with XPath selector' do
|
context 'with XPath selector' do
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
|
||||||
|
<h1>This page is used for testing various scopes</h1>
|
||||||
|
|
||||||
|
<p id="for_foo">
|
||||||
|
Different text same wrapper id
|
||||||
|
</p>
|
Loading…
Reference in New Issue