David West
David West

Reputation: 2328

Page Object Gem. Making accessors for elements without ids

Lets say I have a simple page that has less IDs than I'd like for testing

<div class="__panel_body">
  <div class="__panel_header">Real Estate Rating</div>

  <div class="__panel_body">
    <div class="__panel_header">Property Rating Info</div>
    <a class="icon.edit"></a>
    <a class="icon.edit"></a>
  </div>

  <div class="__panel_body">
    <div class="__panel_header">General Risks</div>
    <a class="icon.edit"></a>
    <a class="icon.edit"></a>
  </div>

  <div class="__panel_body">
    <div class="__panel_header">Amenities</div>
    <a class="icon.edit"></a>
    <a class="icon.edit"></a>
  </div>

</div> 

I'm using Jeff Morgan's Page Object gem and I want to make accessors for the edit links in any given section.

The challenge is that the panel headers differentiate what body I want to choose. Then I need to access the parent and get all links with class "icon.edit". Assume I can't change the HTML to solve this.

Here's a start

module RealEstateRatingPageFields

  div(:general_risks_section, ....)

  def general_risks_edit_links
    general_risks_section_element.links(class: "icon.edit")
  end

end

How do I get the general_risks_section accessor to work, though?

I want that to represent the parent div to the panel header with text 'General Risks'...

Upvotes: 1

Views: 164

Answers (1)

Justin Ko
Justin Ko

Reputation: 46836

There are a number of ways to get the general risk section.

Using a Block

The accessors can take a block where you can more programatically describe how to locate the element. This allows you to locate a distinguishing element and then traverse the DOM to the element you actually want. In this case, you can locate the header with the matching text and navigate to its parent.

div(:general_risks_section) { div_element(class: '__panel_header', text: 'General Risks').parent }

Using XPath

While harder to read and write, you could also use an XPath locator. The concept and thought process is the same as using the block. The only benefit is that it reduces the number of element calls, which slightly improves performance.

div(:general_risks_section, xpath: './/div[@class="__panel_body"][./div[@class="__panel_header" and text() = "General Risks"]]')

The XPath is saying:

.//div                            # Find a div element that
  [@class="__panel_body"]         # Has the class "__panel_body" and
  [./div[                         # Contains a div element that
    @class="__panel_header" and   # Has the class "__panel_header" and
    text() = "General Risks"      # Has the text "General Risks"
  ]]

Using the Body Text

Given the HTML, you could also just locate the section directly based on its text.

div(:general_risks_section, class: '__panel_body', text: 'General Risks')

Note that this assumes that the HTML given was not simplified. If there are actually other text nodes, this probably would not be the best option.

Upvotes: 3

Related Questions