Why is capybara saying node is obsolete and how to solve?

Viewed 2654

I have an spec/capybara test which searches for an element and then attempts to run a JS script to scroll the element into view. However, Capybara claims the node is obsolete by the time it attempts to run the JS. The lines at issue are consecutive. Here they are:

element = page.find(selector, visible: false)
Capybara.current_session.driver.browser.execute_script(script, element.native)

I have done a fair bit of debugging already. When placing a debugger between the find and execute_script lines, calling element indeed returns an obsolete node Obsolete #<Capybara::Node::Element>.

Running page.find(selector, visible: false) within the debugger does not return an obsolete node but rather the normal active node you would expect #<Capybara::Node::Element tag="div" path="/HTML/BODY[1]/DIV[6]/DIV[2]/DIV[1]/DIV[1]/DIV[54]">

Furthermore, removing the two lines and running them manually in the debugger sees capybara correctly find the DOM element, run the JS correctly, and the spec passes

The relevant code:

  def scroll_to(selector, align = true)
    if align
      script = <<-JS
        arguments[0].scrollIntoView(true);
      JS
    else
      script = <<-JS
        arguments[0].scrollIntoView(false);
      JS
    end

    element = page.find(selector, visible: false)

    Capybara.current_session.driver.browser.execute_script(script, element.native)
  end

  scroll_to(".xdsoft_time[data-hour='13'][data-minute='15']")

1 Answers

Without knowing what's happening on your page it's impossible to say exactly why you're getting the 'obsolete node' error, but that error indicates the node that was originally found is no longer in the page. This can happen if you visit a new page, if the part of the page containing that node is replaced by JS, etc.

Passing visible: false and then trying to scroll that element into the page doesn't make sense though since if the element isn't visible then you'll never be able to scroll it into view (visible means drawn on the page, it does not mean 'in the viewport').

Other issues with your code are

  1. you should not be calling the driver specific execute_script, but rather just use the Capybara session execute_script (generally if you're using Capybara.current_session.driver.browser you're doing something wrong).

    page.execute_script(script, element)
    
  2. Capybara already provides a scroll_to so use it instead of writing your own

      page.scroll_to(page.find(selector)) # Defaults to scrolling to the top
    

    If you need control over the alignment of the element just pass the :align option

      page.scroll_to(page.find(selector), align: :center) # :top, :bottom, :center
    
Related