Elements are automatically reloaded
This commit is contained in:
parent
91de98bb03
commit
941e50132a
|
@ -16,7 +16,7 @@ module Capybara
|
||||||
attr_accessor :asset_root, :app_host, :run_server, :default_host
|
attr_accessor :asset_root, :app_host, :run_server, :default_host
|
||||||
attr_accessor :server_port, :server_boot_timeout
|
attr_accessor :server_port, :server_boot_timeout
|
||||||
attr_accessor :default_selector, :default_wait_time, :ignore_hidden_elements, :prefer_visible_elements
|
attr_accessor :default_selector, :default_wait_time, :ignore_hidden_elements, :prefer_visible_elements
|
||||||
attr_accessor :save_and_open_page_path
|
attr_accessor :save_and_open_page_path, :automatic_reload
|
||||||
|
|
||||||
##
|
##
|
||||||
#
|
#
|
||||||
|
@ -236,6 +236,7 @@ Capybara.configure do |config|
|
||||||
config.ignore_hidden_elements = false
|
config.ignore_hidden_elements = false
|
||||||
config.prefer_visible_elements = true
|
config.prefer_visible_elements = true
|
||||||
config.default_host = "http://www.example.com"
|
config.default_host = "http://www.example.com"
|
||||||
|
config.automatic_reload = true
|
||||||
end
|
end
|
||||||
|
|
||||||
Capybara.register_driver :rack_test do |app|
|
Capybara.register_driver :rack_test do |app|
|
||||||
|
|
|
@ -43,6 +43,10 @@ class Capybara::Driver::Base
|
||||||
raise Capybara::NotSupportedByDriverError
|
raise Capybara::NotSupportedByDriverError
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def invalid_element_errors
|
||||||
|
[]
|
||||||
|
end
|
||||||
|
|
||||||
def wait?
|
def wait?
|
||||||
false
|
false
|
||||||
end
|
end
|
||||||
|
|
|
@ -22,7 +22,7 @@ module Capybara
|
||||||
# session.has_css?('#foobar') # from Capybara::Node::Matchers
|
# session.has_css?('#foobar') # from Capybara::Node::Matchers
|
||||||
#
|
#
|
||||||
class Base
|
class Base
|
||||||
attr_reader :session, :base
|
attr_reader :session, :base, :parent
|
||||||
|
|
||||||
include Capybara::Node::Finders
|
include Capybara::Node::Finders
|
||||||
include Capybara::Node::Actions
|
include Capybara::Node::Actions
|
||||||
|
@ -33,6 +33,10 @@ module Capybara
|
||||||
@base = base
|
@base = base
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def reload
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
def wait?
|
def wait?
|
||||||
|
|
|
@ -22,12 +22,18 @@ module Capybara
|
||||||
#
|
#
|
||||||
class Element < Base
|
class Element < Base
|
||||||
|
|
||||||
|
def initialize(session, base, parent=nil, selector)
|
||||||
|
super(session, base)
|
||||||
|
@parent = parent
|
||||||
|
@selector = selector
|
||||||
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
#
|
#
|
||||||
# @return [Object] The native element from the driver, this allows access to driver specific methods
|
# @return [Object] The native element from the driver, this allows access to driver specific methods
|
||||||
#
|
#
|
||||||
def native
|
def native
|
||||||
base.native
|
carefully { base.native }
|
||||||
end
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
|
@ -35,7 +41,7 @@ module Capybara
|
||||||
# @return [String] The text of the element
|
# @return [String] The text of the element
|
||||||
#
|
#
|
||||||
def text
|
def text
|
||||||
base.text
|
carefully { base.text }
|
||||||
end
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
|
@ -48,7 +54,7 @@ module Capybara
|
||||||
# @return [String] The value of the attribute
|
# @return [String] The value of the attribute
|
||||||
#
|
#
|
||||||
def [](attribute)
|
def [](attribute)
|
||||||
base[attribute]
|
carefully { base[attribute] }
|
||||||
end
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
|
@ -56,7 +62,7 @@ module Capybara
|
||||||
# @return [String] The value of the form element
|
# @return [String] The value of the form element
|
||||||
#
|
#
|
||||||
def value
|
def value
|
||||||
base.value
|
carefully { base.value }
|
||||||
end
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
|
@ -66,7 +72,7 @@ module Capybara
|
||||||
# @param [String] value The new value
|
# @param [String] value The new value
|
||||||
#
|
#
|
||||||
def set(value)
|
def set(value)
|
||||||
base.set(value)
|
carefully { base.set(value) }
|
||||||
end
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
|
@ -74,7 +80,7 @@ module Capybara
|
||||||
# Select this node if is an option element inside a select tag
|
# Select this node if is an option element inside a select tag
|
||||||
#
|
#
|
||||||
def select_option
|
def select_option
|
||||||
base.select_option
|
carefully { base.select_option }
|
||||||
end
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
|
@ -82,7 +88,7 @@ module Capybara
|
||||||
# Unselect this node if is an option element inside a multiple select tag
|
# Unselect this node if is an option element inside a multiple select tag
|
||||||
#
|
#
|
||||||
def unselect_option
|
def unselect_option
|
||||||
base.unselect_option
|
carefully { base.unselect_option }
|
||||||
end
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
|
@ -90,7 +96,7 @@ module Capybara
|
||||||
# Click the Element
|
# Click the Element
|
||||||
#
|
#
|
||||||
def click
|
def click
|
||||||
base.click
|
carefully { base.click }
|
||||||
end
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
|
@ -98,7 +104,7 @@ module Capybara
|
||||||
# @return [String] The tag name of the element
|
# @return [String] The tag name of the element
|
||||||
#
|
#
|
||||||
def tag_name
|
def tag_name
|
||||||
base.tag_name
|
carefully { base.tag_name }
|
||||||
end
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
|
@ -109,7 +115,7 @@ module Capybara
|
||||||
# @return [Boolean] Whether the element is visible
|
# @return [Boolean] Whether the element is visible
|
||||||
#
|
#
|
||||||
def visible?
|
def visible?
|
||||||
base.visible?
|
carefully { base.visible? }
|
||||||
end
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
|
@ -119,7 +125,7 @@ module Capybara
|
||||||
# @return [Boolean] Whether the element is checked
|
# @return [Boolean] Whether the element is checked
|
||||||
#
|
#
|
||||||
def checked?
|
def checked?
|
||||||
base.checked?
|
carefully { base.checked? }
|
||||||
end
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
|
@ -129,7 +135,7 @@ module Capybara
|
||||||
# @return [Boolean] Whether the element is selected
|
# @return [Boolean] Whether the element is selected
|
||||||
#
|
#
|
||||||
def selected?
|
def selected?
|
||||||
base.selected?
|
carefully { base.selected? }
|
||||||
end
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
|
@ -139,7 +145,7 @@ module Capybara
|
||||||
# @return [String] An XPath expression
|
# @return [String] An XPath expression
|
||||||
#
|
#
|
||||||
def path
|
def path
|
||||||
base.path
|
carefully { base.path }
|
||||||
end
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
|
@ -150,7 +156,7 @@ module Capybara
|
||||||
# @param [String] event The name of the event to trigger
|
# @param [String] event The name of the event to trigger
|
||||||
#
|
#
|
||||||
def trigger(event)
|
def trigger(event)
|
||||||
base.trigger(event)
|
carefully { base.trigger(event) }
|
||||||
end
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
|
@ -164,7 +170,13 @@ module Capybara
|
||||||
# @param [Capybara::Element] node The element to drag to
|
# @param [Capybara::Element] node The element to drag to
|
||||||
#
|
#
|
||||||
def drag_to(node)
|
def drag_to(node)
|
||||||
base.drag_to(node.base)
|
carefully { base.drag_to(node.base) }
|
||||||
|
end
|
||||||
|
|
||||||
|
def reload
|
||||||
|
reloaded = parent.reload.first(@selector.name, @selector.locator, @selector.options)
|
||||||
|
@base = reloaded.base if reloaded
|
||||||
|
self
|
||||||
end
|
end
|
||||||
|
|
||||||
def inspect
|
def inspect
|
||||||
|
@ -173,6 +185,19 @@ module Capybara
|
||||||
%(#<Capybara::Element tag="#{tag_name}">)
|
%(#<Capybara::Element tag="#{tag_name}">)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def carefully(seconds=Capybara.default_wait_time)
|
||||||
|
start_time = Time.now
|
||||||
|
|
||||||
|
begin
|
||||||
|
yield
|
||||||
|
rescue => e
|
||||||
|
raise e unless driver.respond_to?(:invalid_element_errors) and driver.invalid_element_errors.include?(e.class)
|
||||||
|
raise e if (Time.now - start_time) >= seconds
|
||||||
|
sleep(0.05)
|
||||||
|
reload
|
||||||
|
retry
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -120,10 +120,10 @@ module Capybara
|
||||||
def all(*args)
|
def all(*args)
|
||||||
options = extract_normalized_options(args)
|
options = extract_normalized_options(args)
|
||||||
|
|
||||||
Capybara::Selector.normalize(*args).xpaths.
|
selector = Capybara::Selector.normalize(*args)
|
||||||
map { |path| find_in_base(path) }.flatten.
|
selector.xpaths.
|
||||||
select { |node| matches_options(node, options) }.
|
map { |path| find_in_base(selector, path) }.flatten.
|
||||||
map { |node| convert_element(node) }
|
select { |node| matches_options(node, options) }
|
||||||
end
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
|
@ -143,10 +143,11 @@ module Capybara
|
||||||
options = extract_normalized_options(args)
|
options = extract_normalized_options(args)
|
||||||
found_elements = []
|
found_elements = []
|
||||||
|
|
||||||
Capybara::Selector.normalize(*args).xpaths.each do |path|
|
selector = Capybara::Selector.normalize(*args)
|
||||||
find_in_base(path).each do |node|
|
selector.xpaths.each do |path|
|
||||||
|
find_in_base(selector, path).each do |node|
|
||||||
if matches_options(node, options)
|
if matches_options(node, options)
|
||||||
found_elements << convert_element(node)
|
found_elements << node
|
||||||
return found_elements.last if not Capybara.prefer_visible_elements or node.visible?
|
return found_elements.last if not Capybara.prefer_visible_elements or node.visible?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -156,12 +157,10 @@ module Capybara
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
def find_in_base(xpath)
|
def find_in_base(selector, xpath)
|
||||||
base.find(xpath)
|
base.find(xpath).map do |node|
|
||||||
end
|
Capybara::Node::Element.new(session, node, self, selector)
|
||||||
|
end
|
||||||
def convert_element(element)
|
|
||||||
Capybara::Node::Element.new(session, element)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def wait_conditionally_until
|
def wait_conditionally_until
|
||||||
|
@ -197,7 +196,7 @@ module Capybara
|
||||||
end
|
end
|
||||||
|
|
||||||
def has_selected_options?(node, expected)
|
def has_selected_options?(node, expected)
|
||||||
actual = node.find('.//option').select { |option| option.selected? }.map { |option| option.text }
|
actual = node.all(:xpath, './/option').select { |option| option.selected? }.map { |option| option.text }
|
||||||
(expected - actual).empty?
|
(expected - actual).empty?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -120,14 +120,10 @@ module Capybara
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
def find_in_base(xpath)
|
def find_in_base(selector, xpath)
|
||||||
native.xpath(xpath).map { |node| self.class.new(node) }
|
native.xpath(xpath).map { |node| self.class.new(node) }
|
||||||
end
|
end
|
||||||
|
|
||||||
def convert_element(element)
|
|
||||||
element
|
|
||||||
end
|
|
||||||
|
|
||||||
def wait?
|
def wait?
|
||||||
false
|
false
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,7 +2,7 @@ require 'selenium-webdriver'
|
||||||
|
|
||||||
class Capybara::Selenium::Driver < Capybara::Driver::Base
|
class Capybara::Selenium::Driver < Capybara::Driver::Base
|
||||||
DEFAULT_OPTIONS = {
|
DEFAULT_OPTIONS = {
|
||||||
:resynchronize => false,
|
:resynchronize => true,
|
||||||
:resynchronization_timeout => 10,
|
:resynchronization_timeout => 10,
|
||||||
:browser => :firefox
|
:browser => :firefox
|
||||||
}
|
}
|
||||||
|
@ -118,6 +118,10 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
||||||
# Browser must have already gone
|
# Browser must have already gone
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def invalid_element_errors
|
||||||
|
[Selenium::WebDriver::Error::ObsoleteElementError]
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def load_wait_for_ajax_support
|
def load_wait_for_ajax_support
|
||||||
|
|
|
@ -284,7 +284,7 @@ module Capybara
|
||||||
end
|
end
|
||||||
|
|
||||||
def document
|
def document
|
||||||
Capybara::Node::Document.new(self, driver)
|
@document ||= Capybara::Node::Document.new(self, driver)
|
||||||
end
|
end
|
||||||
|
|
||||||
NODE_METHODS.each do |method|
|
NODE_METHODS.each do |method|
|
||||||
|
|
|
@ -35,4 +35,9 @@ $(function() {
|
||||||
$('body').append('<p id="ajax_request_done">Ajax request done</p>');
|
$('body').append('<p id="ajax_request_done">Ajax request done</p>');
|
||||||
}});
|
}});
|
||||||
});
|
});
|
||||||
|
$('#reload-link').click(function() {
|
||||||
|
setTimeout(function() {
|
||||||
|
$('#reload-me').replaceWith('<div id="reload-me"><em>RELOADED</em></div>');
|
||||||
|
}, 250)
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -32,6 +32,12 @@ shared_examples_for "find" do
|
||||||
it "should scope CSS selectors" do
|
it "should scope CSS selectors" do
|
||||||
@session.find(:css, '#second').should have_no_css('h1')
|
@session.find(:css, '#second').should have_no_css('h1')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "should have a reference to its parent if there is one" do
|
||||||
|
@node = @session.find(:css, '#first')
|
||||||
|
@node.parent.should == @node.session.document
|
||||||
|
@node.find('a').parent.should == @node
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context "with css selectors" do
|
context "with css selectors" do
|
||||||
|
|
|
@ -18,6 +18,48 @@ shared_examples_for "session with javascript support" do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'Node#reload', :focus => true do
|
||||||
|
context "without automatic reload" do
|
||||||
|
before { Capybara.automatic_reload = false }
|
||||||
|
it "should reload the current context of the node" do
|
||||||
|
@session.visit('/with_js')
|
||||||
|
node = @session.find(:css, '#reload-me')
|
||||||
|
@session.click_link('Reload!')
|
||||||
|
sleep(0.3)
|
||||||
|
node.reload.text.should == 'RELOADED'
|
||||||
|
node.text.should == 'RELOADED'
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should reload a parent node" do
|
||||||
|
@session.visit('/with_js')
|
||||||
|
node = @session.find(:css, '#reload-me').find(:css, 'em')
|
||||||
|
@session.click_link('Reload!')
|
||||||
|
sleep(0.3)
|
||||||
|
node.reload.text.should == 'RELOADED'
|
||||||
|
node.text.should == 'RELOADED'
|
||||||
|
end
|
||||||
|
after { Capybara.automatic_reload = true }
|
||||||
|
end
|
||||||
|
|
||||||
|
context "with automatic reload" do
|
||||||
|
it "should reload the current context of the node automatically" do
|
||||||
|
@session.visit('/with_js')
|
||||||
|
node = @session.find(:css, '#reload-me')
|
||||||
|
@session.click_link('Reload!')
|
||||||
|
sleep(0.3)
|
||||||
|
node.text.should == 'RELOADED'
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should reload a parent node automatically" do
|
||||||
|
@session.visit('/with_js')
|
||||||
|
node = @session.find(:css, '#reload-me').find(:css, 'em')
|
||||||
|
@session.click_link('Reload!')
|
||||||
|
sleep(0.3)
|
||||||
|
node.text.should == 'RELOADED'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe '#find' do
|
describe '#find' do
|
||||||
it "should allow triggering of custom JS events" do
|
it "should allow triggering of custom JS events" do
|
||||||
pending "cannot figure out how to do this with selenium" if @session.mode == :selenium
|
pending "cannot figure out how to do this with selenium" if @session.mode == :selenium
|
||||||
|
|
|
@ -38,6 +38,11 @@
|
||||||
<p>
|
<p>
|
||||||
<input type="submit" id="fire_ajax_request" value="Fire Ajax Request"/>
|
<input type="submit" id="fire_ajax_request" value="Fire Ajax Request"/>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<a id="reload-link" href="#">Reload!</a>
|
||||||
|
<div id="reload-me"><em>waiting to be reloaded</em></div>
|
||||||
|
</p>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue