You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This code uses Capybara's synchronize inside of within. The code inside synchronize always fails, but that's a simplification from the real application. The page has JavaScript that fully replaces the #parent element that contains the#child element, which is what Capybara is looking for.
require'capybara'require'capybara/dsl'require'capybara/cuprite'includeCapybara::DSLCapybara.javascript_driver=:cupriteCapybara.register_driver(:cuprite)do |app|
Capybara::Cuprite::Driver.new(app)endCapybara.current_driver=:cupriteCapybara.app_host='http://[::1]:8888'Capybara.run_server=falseclassDummyError < StandardError;endvisit('/')within('#parent')dook_errors=page.driver.invalid_element_errors + [DummyError]page.document.synchronize(Capybara.default_max_wait_time,errors: ok_errors)dopage.all('#child',count: 1)# We always fail this test. If everything is working correctly,# this program should report this exception.raiseDummyErrorendend
Serve the page via python3 -m http.server --bind '::1' 8888
Run the test via bundle exec ruby repro.rb
Output
gems/cuprite-0.14.3/lib/capybara/cuprite/node.rb:23:in `rescue in command': The element you are trying to interact with is either not part of the DOM, or is not currently visible on the page (perhaps display: none is set). It is possible the element has been replaced by another element and you meant to interact with the new element. If so you need to do a new find in order to get a reference to the new element. (Capybara::Cuprite::ObsoleteNode)
from gems/cuprite-0.14.3/lib/capybara/cuprite/node.rb:20:in `command'
from gems/cuprite-0.14.3/lib/capybara/cuprite/node.rb:48:in `find'
from gems/cuprite-0.14.3/lib/capybara/cuprite/node.rb:44:in `find_css'
from gems/capybara-3.39.2/lib/capybara/node/base.rb:108:in `find_css'
from gems/capybara-3.39.2/lib/capybara/queries/selector_query.rb:253:in `find_nodes_by_selector_format'
from gems/capybara-3.39.2/lib/capybara/queries/selector_query.rb:166:in `block in resolve_for'
from gems/capybara-3.39.2/lib/capybara/node/base.rb:77:in `synchronize'
from gems/capybara-3.39.2/lib/capybara/queries/selector_query.rb:165:in `resolve_for'
from gems/capybara-3.39.2/lib/capybara/node/finders.rb:265:in `block in all'
from gems/capybara-3.39.2/lib/capybara/node/base.rb:77:in `synchronize'
from gems/capybara-3.39.2/lib/capybara/node/finders.rb:264:in `all'
from gems/capybara-3.39.2/lib/capybara/session.rb:773:in `all'
from repro.rb:25:in `block (2 levels) in <main>'
from gems/capybara-3.39.2/lib/capybara/node/base.rb:84:in `synchronize'
from repro.rb:24:in `block in <main>'
from gems/capybara-3.39.2/lib/capybara/session.rb:365:in `within'
from gems/capybara-3.39.2/lib/capybara/dsl.rb:52:in `call'
from gems/capybara-3.39.2/lib/capybara/dsl.rb:52:in `within'
from repro.rb:20:in `<main>'
gems/ferrum-0.13/lib/ferrum/browser/client.rb:88:in `raise_browser_error': No node with given id found (Ferrum::NodeNotFoundError)
from gems/ferrum-0.13/lib/ferrum/browser/client.rb:49:in `command'
from gems/ferrum-0.13/lib/ferrum/page.rb:276:in `command'
from gems/cuprite-0.14.3/lib/capybara/cuprite/browser.rb:74:in `find_within'
from gems/cuprite-0.14.3/lib/capybara/cuprite/node.rb:21:in `command'
from gems/cuprite-0.14.3/lib/capybara/cuprite/node.rb:48:in `find'
from gems/cuprite-0.14.3/lib/capybara/cuprite/node.rb:44:in `find_css'
from gems/capybara-3.39.2/lib/capybara/node/base.rb:108:in `find_css'
from gems/capybara-3.39.2/lib/capybara/queries/selector_query.rb:253:in `find_nodes_by_selector_format'
from gems/capybara-3.39.2/lib/capybara/queries/selector_query.rb:166:in `block in resolve_for'
from gems/capybara-3.39.2/lib/capybara/node/base.rb:77:in `synchronize'
from gems/capybara-3.39.2/lib/capybara/queries/selector_query.rb:165:in `resolve_for'
from gems/capybara-3.39.2/lib/capybara/node/finders.rb:265:in `block in all'
from gems/capybara-3.39.2/lib/capybara/node/base.rb:77:in `synchronize'
from gems/capybara-3.39.2/lib/capybara/node/finders.rb:264:in `all'
from gems/capybara-3.39.2/lib/capybara/session.rb:773:in `all'
from repro.rb:25:in `block (2 levels) in <main>'
from gems/capybara-3.39.2/lib/capybara/node/base.rb:84:in `synchronize'
from repro.rb:24:in `block in <main>'
from gems/capybara-3.39.2/lib/capybara/session.rb:365:in `within'
from gems/capybara-3.39.2/lib/capybara/dsl.rb:52:in `call'
from gems/capybara-3.39.2/lib/capybara/dsl.rb:52:in `within'
from repro.rb:20:in `<main>'
Observations
If I add puts page.has_selector?('#child', text: 'Child 2') before the synchronize block, it will print out true and the program will (correctly) exit with DummyError.
If I replace cuprite with apparition, the program will (correctly) exit with DummyError.
The text was updated successfully, but these errors were encountered:
moduleNodePatchdefcommand(name, *args)browser.send(name,node, *args)rescueFerrum::NodeNotFoundError=>e# Throw ElementNotFound because it's not inside #invalid_element_errors# so it would not be suppresed inside Capybara::Queries::SelectorQuery#matches_filters?# and then error would force element to reload Capybara::Node::Element#reload# instead of just ignore it## Original code:# raise ObsoleteNode.new(self, e.response)raiseCapybara::ElementNotFound,e.responserescueFerrum::BrowserError=>ecasee.messagewhen'Cuprite.MouseEventFailed'raiseMouseEventFailed.new(self,e.response)elseraiseendendendCapybara::Cuprite::Node.prependNodePatch
I still think that there should be better way to do that
I spent a lot of time debugging Capybara/Cuprite/Ferrum source code trying to get to the root of this issue just to realize it's not actually a bug. There is a good reason outlined by @jnicklas in this comment. Basically, when you use #all and the node on a page becomes obsolete, you can't be sure that if you reload results of #all they would be the same. However, it is possible to make #all results reload, just pass allow_reload option (e.g. page.all('#child', count: 1, allow_reload: true)).
Reproduction
This code uses Capybara's
synchronize
inside ofwithin
. The code insidesynchronize
always fails, but that's a simplification from the real application. The page has JavaScript that fully replaces the#parent
element that contains the#child
element, which is what Capybara is looking for.Gemfile
repro.rb
index.html
Steps
python3 -m http.server --bind '::1' 8888
bundle exec ruby repro.rb
Output
Observations
If I add
puts page.has_selector?('#child', text: 'Child 2')
before thesynchronize
block, it will print outtrue
and the program will (correctly) exit withDummyError
.If I replace
cuprite
withapparition
, the program will (correctly) exit withDummyError
.The text was updated successfully, but these errors were encountered: