1

I have the following HTML on a page that I am trying to test with cucumber.

<div class='try_options'>
  <ul>
    <li>
      <div>
        <img alt="" src="/images/main_page1.jpg" />
        <div class='right_section'>

          <p>
            Feature 1
          </p>
          <a class='suggest_btn' href='url1'>Try Now</a>
        </div>
      </div>
    </li>
    <li>
      <div>
        <img alt="" src="/images/main_page2.jpg" />
        <div class='right_section'>

          <p>
            Feature 2
          </p>
          <a class='suggest_btn' href='url2'>Try Now</a>
        </div>
      </div>
    </li>
    <li>
      <div>
        <img alt="" src="/images/main_page3.jpg" />
        <div class='right_section'>

          <p>
            Feature 3
          </p>
          <a class='suggest_btn' href='url3'>Try Now</a>
        </div>
      </div>
    </li>
    <li>
      <div>
        <img alt="" src="/images/main_page4.jpg" />
        <div class='right_section'>

          <p>
            Feature 4
          </p>
          <a class='suggest_btn' href='url4'>Try Now</a>
        </div>
      </div>
    </li>
  </ul>
</div>

Basically, there are features, each with a "Try Now" link. I want to test if clicking on the link takes me to the page for the respective feature.

I found a simalar step implementation here, but there is something wrong with the XPATH. Any help would be much appreciated.

Community
  • 1
  • 1
zsquare
  • 9,916
  • 6
  • 53
  • 87
  • Is it possible to modify your HTML to make it a little more semantic? e.g. add a class or ID to each `li` containing the feature name? It's certainly possible to do this with XPath - I've done it recently and will dig it out for you - but it's not pretty! – Jon M Nov 29 '11 at 08:27

2 Answers2

5

When I've done something similar before, I've used XPath to locate the 'identifier' element - in your case the p tag containing e.g. 'Feature 1', then walked up the DOM to a common parent (li here), and down again to find whatever sibling I was after (e.g. the link associated with the p). This may work for you:

def find_link_for_feature(feature_name)
    find(:xpath, "//p[contains(.,'#{feature_name}')]/ancestor::li/descendant::a")
end

find_link_for_feature('Feature 1').click

You may want to add extra filtering so that it only finds the link with the suggest_btn class, depending on your situation.

As I mentioned in the comment on the question, however, I much prefer to modify the markup so that it's actually testable without having to jump through hoops such as these! I consider this kind of XPath to be something of a last resort for when I'm not able to modify the markup.

EDIT:

Thinking about your comment asking how to do other things in the scope of the parent element, I'd be more tempted to do something like the following, which gives you more flexibility, and trims down the ugly XPath slightly:

def find_section_for_feature(feature_name)
    find(:xpath, "//p[contains(.,'#{feature_name}')]/ancestor::li")
end

within find_section_for_feature('Feature 1') do
    click_link 'Try Now'
    # Anything else you want to do
and
Jon M
  • 11,669
  • 3
  • 41
  • 47
  • Thanks! That worked. Unfortunately, i count touch the html, all i can do is write the steps :) – zsquare Nov 29 '11 at 09:10
  • Another question, say I wanted to do some other things within the scope of the parent of the text and link, any idea how i'd select the relevant parent? – zsquare Nov 29 '11 at 09:12
  • I guess you could just grab the whole parent (by stripping off the `descendant` part of the XPath) and pass it to a `within` block, something like `within(:xpath, "//p[contains(.,'#{feature_name}')]/ancestor::li") { # Do things in here }` – Jon M Nov 29 '11 at 09:15
  • Sorry to drag this on, but in the interest of making this more "generic", any way i can loose the binding to the "li"? – zsquare Nov 29 '11 at 09:58
  • You could locate the 'right_section' div by changing the end of the XPath to `ancestor::div[contains(@class,'right_section')]`, but that doesn't contain the image so you wouldn't be able to interact with that. You could also locate the `div` immediately inside the `li` by using `ancestor::div[not(@class)]` (which, I think, will find the nearest parent `div` without a `class` attribute). I haven't tested either of these so YMMV... – Jon M Nov 29 '11 at 10:15
  • Thanks! you have been very helpful! – zsquare Nov 29 '11 at 10:58
0

If your xpath is based on the link you provided it's probably getting a collection of elements. So rather than just one link it's getting a group of links. Try grabbing just one of the links (like the first one in the collection) and use that.

Dty
  • 12,253
  • 6
  • 43
  • 61