List child elements for a Capybara/Poltergeist element
Asked Answered
R

2

7

I've searched around and I can't find a way to do this.

We are running Capybara tests with the Poltergeist driver in cucumber features on an EmberJS/Rails app. I can't use page.driver.debug because I'm running inside a headless vagrant instance, so that troubleshooting is not available to me, but screenshots work, and a Chrome dev tools inspect on the page in question shows the right elements.

I have a cucumber scenario that's failing, and it's because the find isn't working. My test looks like this:

When(/^I delete description "(.*?)"$/) do |description|
  within :css, '#myform' do
    first('label', :text => /#{description}/).find(:xpath, '..').find(:css, 'button.delete')
  end
end

It can't find the element in question. Here is the relevant section of the DOM:

<form id="myform">
  <div class="row-fluid question">
      <div class="span12">
        <div class="padding">
          <div id="ember576" class="ember-view is-viewing-edit-controls">
            <button class="delete" data-ember-action="31"></button>
            <button class="edit" data-ember-action="32"></button>
            <button class="insert_above" data-ember-action="33"></button>
            <button class="insert_below" data-ember-action="34"></button>
            <button class="move_up" data-ember-action="35"></button>
            <button class="move_down" data-ember-action="36"></button>
            <label class="question" data-ember-action="37">Enter your first name</label>
            <input id="ember577" class="ember-view ember-text-field">
            <span class="error"></span>
          </div>
        </div>
    </div>
  </div>
</form>

If I add in a binding.pry, this:

first('label', :text => /#{description}/).find(:xpath, '..')
                                                                                              │

gives me this response:

=> #<Capybara::Element tag="div">

And this:

first('label', :text => /#{description}/).find(:xpath, '..')
                                                                                              │

gives me this response:

=> "Enter your first name"

but the full find:

first('label', :text => /#{description}/).find(:xpath, '..').find(:css, 'button.delete')

gives me this response:

Capybara::ElementNotFound: Unable to find css "button.delete"

I have a feeling that this is a parent/sibling issue, but I can't troubleshoot it. So I guess I have a couple of questions:

  1. For debugging, is there anyway with a poltergeist driver to list all child elements?
  2. Is there something wrong with my xpath selector? The fact that .. is returning Capybara::Element tag="div" makes me think I have the right parent element (and like I said, it works on other pages in other similar tests), but I have no way to verify which element it is. I can't find how to get the xpath or anything else that helps me identify the element.
Romanticist answered 4/9, 2013 at 19:4 Comment(0)
R
2

I figured this out. The test was working fine, but I was missing an action step in my scenario.

However, I thought y'all might find it useful to know how I ended up getting the contents: executing jQuery on the page. I added a binding.pry to my test, and then in the binding.pry I console logged a jQuery result, e.g.:

# this gave me the expected contents and verified that button.delete was a child
page.execute_script("console.log($('#myform label.question').parent().html())");

# and this gave me the classes to demonstrate that it was the correct div
page.execute_script("console.log($('#myform label.question').parent().attr('class'))");

I took a shortcut with the selectors because I knew that the label I was looking for was the first one, but the selector doesn't matter, it's the idea of console.log()-ing jQuery in binding.pry that was useful.

Romanticist answered 5/9, 2013 at 14:43 Comment(0)
S
2

Something to try for others who are still looking: Try using first(:xpath, './/..') instead of find(:xpath, '..').

Also it is a good idea to reduce the scope of the find when you can, assuming the form's id attribute is static, for better performance and preventing ambiguous matching:

within('#myform') do
  find('label', text: "#{description}", match: :first). 
  first(:xpath, './/..').
  find('button.delete').
  click
end
Sungsungari answered 27/3, 2017 at 21:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.