Skip to content

Obsolete nodes are not refreshed when using Capybara's #within and/or #synchronize #242

Open
@shepmaster

Description

@shepmaster

Reproduction

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.

Gemfile

# frozen_string_literal: true

source "https://rubygems.org"

gem "cuprite", "~> 0.14.3"
gem "capybara", "~> 3.39"

repro.rb

require 'capybara'
require 'capybara/dsl'
require 'capybara/cuprite'

include Capybara::DSL

Capybara.javascript_driver = :cuprite
Capybara.register_driver(:cuprite) do |app|
  Capybara::Cuprite::Driver.new(app)
end

Capybara.current_driver = :cuprite
Capybara.app_host = 'http://[::1]:8888'
Capybara.run_server = false

class DummyError < StandardError; end

visit('/')

within('#parent') do
  ok_errors = page.driver.invalid_element_errors + [DummyError]
  page.document.synchronize(Capybara.default_max_wait_time, errors: ok_errors) do
    page.all('#child', count: 1)

    # We always fail this test. If everything is working correctly,
    # this program should report this exception.
    raise DummyError
  end
end

index.html

<!doctype html>
<html>
  <head>
    <title>Reproduction</title>
  </head>

  <body>
    <div id="parent">
      <div id="child">Child 1</div>
    </div>
  </body>

  <script type="text/javascript">
    function sleep(ms) {
      return new Promise(resolve => setTimeout(resolve, ms));
    }

    async function main() {
      await sleep(1000);

      const parent = document.getElementById('parent');
      const body = parent.parentElement;
      parent.remove();

      const newParent = document.createElement('div');
      newParent.id = 'parent';

      const newChild =  document.createElement('div');
      newChild.id = 'child';
      newChild.innerText = 'Child 2';

      newParent.appendChild(newChild);
      body.appendChild(newParent);
    }

    main()
  </script>
</html>

Steps

  • 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions