New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reduce `enabled` Wire Calls #719

Merged
merged 1 commit into from May 4, 2018
Jump to file or symbol
Failed to load files and symbols.
+55 −50
Diff settings

Always

Just for now

@@ -47,6 +47,7 @@ def initialize(query_scope, selector)
#
def exists?
return false if @element && stale?
assert_exists
true
rescue UnknownObjectException, UnknownFrameException
@@ -401,7 +402,10 @@ def wd
#
def visible?
element_call(:assert_exists) { @element.displayed? }
assert_exists
@element.displayed?
rescue Selenium::WebDriver::Error::StaleElementReferenceError
raise unknown_exception
end
#
@@ -497,6 +501,12 @@ def browser
def stale?
raise Watir::Exception::Error, "Can not check staleness of unused element" unless @element
return false unless stale_in_context?
@query_scope.ensure_context
stale_in_context?
end
def stale_in_context?
@element.enabled? # any wire call will check for staleness
false
rescue Selenium::WebDriver::Error::ObsoleteElementError
@@ -566,15 +576,7 @@ def wait_for_writable
# Ensure that the element exists, making sure that it is not stale and located if necessary
def assert_exists
if @element && @selector.empty?
@query_scope.ensure_context
reset! if stale?
elsif @element && !stale?
return
else
@element = locate
end
locate unless @element
assert_element_found
end
@@ -600,7 +602,7 @@ def selector_string
# Ensure the driver is in the desired browser context
def ensure_context
assert_exists
locate unless exists?
end
private
@@ -643,43 +645,58 @@ def assert_is_element(obj)
end
end
def element_call(exist_check = :wait_for_exists)
def element_call(precondition = nil, &block)
caller = caller_locations(1, 1)[0].label
already_locked = Wait.timer.locked?
caller = caller_locations(1,1)[0].label
if already_locked
Watir.logger.info "-> `#{inspect}##{caller}` after `##{exist_check}` (as a prerequisite for a previously specified execution)"
else
Watir.logger.info "-> `#{inspect}##{caller}` after `##{exist_check}`"
unless already_locked
Wait.timer = Wait::Timer.new(timeout: Watir.default_timeout)
end
begin
send exist_check
check_condition(precondition)
Watir.logger.info "-> `Executing #{inspect}##{caller}`"
yield
rescue unknown_exception => ex
if precondition.nil?
element_call(:wait_for_exists, &block)
end
msg = ex.message
msg += "; Maybe look in an iframe?" if @query_scope.iframes.count > 0
msg += "; Maybe look in an iframe?" if @query_scope.ensure_context && @query_scope.iframes.count > 0
custom_attributes = @locator.selector_builder.custom_attributes
msg += "; Watir treated #{custom_attributes} as a non-HTML compliant attribute, ensure that was intended" unless custom_attributes.empty?
raise unknown_exception, msg
rescue Selenium::WebDriver::Error::StaleElementReferenceError
@query_scope.ensure_context
reset!
retry
rescue Selenium::WebDriver::Error::ElementNotVisibleError, Selenium::WebDriver::Error::ElementNotInteractableError
raise_present unless Wait.timer.remaining_time > 0
raise_present unless exist_check == :wait_for_present || exist_check == :wait_for_enabled
raise_present unless precondition == :wait_for_present || precondition == :wait_for_enabled
retry
rescue Selenium::WebDriver::Error::InvalidElementStateError
raise_disabled unless Wait.timer.remaining_time > 0
raise_disabled unless exist_check == :wait_for_writable || exist_check == :wait_for_enabled
raise_disabled unless precondition == :wait_for_writable || precondition == :wait_for_enabled
retry
rescue Selenium::WebDriver::Error::NoSuchWindowError
raise Exception::NoMatchingWindowFoundException, "browser window was closed"
ensure
Watir.logger.info "<- `#{inspect}##{caller}` has been completed"
Watir.logger.info "<- `Completed #{inspect}##{caller}`"
Wait.timer.reset! unless already_locked
end
end
def check_condition(condition)
Watir.logger.info "<- `Verifying precondition #{inspect}##{condition}`"
begin
condition.nil? ? assert_exists : send(condition)
Watir.logger.info "<- `Verified precondition #{inspect}##{condition || 'assert_exists'}`"
rescue unknown_exception
raise unless condition.nil?
Watir.logger.info "<- `Unable to satisfy precondition #{inspect}##{condition}`"
check_condition(:wait_for_exists)
end
end
def method_missing(meth, *args, &blk)
method = meth.to_s
if method =~ Locators::Element::SelectorBuilder::WILDCARD_ATTRIBUTE
View
@@ -20,13 +20,12 @@
end
it "returns false if the element is stale" do
wd_element = browser.div(id: "foo").wd
element = browser.div(id: "foo").tap(&:exists?)
# simulate element going stale during lookup
allow(browser.driver).to receive(:find_element).with(:id, 'foo') { wd_element }
browser.refresh
expect(browser.div(:id, 'foo')).to_not be_present
expect(element).to be_stale
expect(element).to_not be_present
end
end
@@ -54,13 +53,13 @@
browser.goto WatirSpec.url_for('removed_element.html')
end
it "relocates element from a collection when it becomes stale" do
watir_element = browser.divs(id: "text").first
expect(watir_element).to exist
it "element from a collection returns false when it becomes stale" do
element = browser.divs(id: "text").first.tap(&:exists?)
browser.refresh
expect(watir_element).to exist
expect(element).to be_stale
expect(element).to_not exist
end
it "returns false when tag name does not match id" do
@@ -71,22 +70,15 @@
describe "#element_call" do
it 'handles exceptions when taking an action on an element that goes stale during execution' do
it 'handles exceptions when taking an action on a stale element' do
browser.goto WatirSpec.url_for('removed_element.html')
watir_element = browser.div(id: "text")
element = browser.div(id: "text").tap(&:exists?)
# simulate element going stale after assert_exists and before action taken, but not when block retried
allow(watir_element).to receive(:text) do
watir_element.send(:element_call) do
@already_stale ||= false
browser.refresh unless @already_stale
@already_stale = true
watir_element.instance_variable_get('@element').text
end
end
browser.refresh
expect { watir_element.text }.to_not raise_error
expect(element).to be_stale
expect { element.text }.to_not raise_error
end
end
@@ -199,15 +199,12 @@
end
it "raises UnknownObjectException exception if the element is stale" do
wd_element = browser.text_field(id: "new_user_email").wd
element = browser.text_field(id: "new_user_email").tap(&:exists?)
# simulate element going stale during lookup
allow(browser.driver).to receive(:find_element).with(:css, '#new_user_email') { wd_element }
allow(browser.driver).to receive(:find_elements).with(:css, '#new_user_email') { [wd_element] }
allow(browser.driver).to receive(:find_elements).with(:tag_name, 'iframe') { [] }
browser.refresh
expect { browser.text_field(css: '#new_user_email').visible? }.to raise_unknown_object_exception
expect(element).to be_stale
expect { element.visible? }.to raise_unknown_object_exception
end
it "returns true if the element has style='visibility: visible' even if parent has style='visibility: hidden'" do
@@ -247,10 +247,9 @@ def present?
end
it "does not error when element goes stale" do
element = browser.div(id: 'foo')
element = browser.div(id: 'foo').tap(&:exists?)
allow(element).to receive(:stale?).and_return(false, true)
allow(element.wd).to receive(:displayed?).and_raise(Selenium::WebDriver::Error::StaleElementReferenceError)
browser.a(id: 'hide_foo').click
expect { element.wait_while_present(timeout: 1) }.to_not raise_exception
ProTip! Use n and p to navigate between commits in a pull request.