Home > other >  Locating table cell using the header cell text
Locating table cell using the header cell text

Time:02-15

I have table kind of appearance as shown below but it's not a single table. Header is in one table and rows are in another table.

enter image description here

The header has Primary, Language and the add button which is one table and rest of the two rows are in another table. Now I have to identify the cell using the header text. For an example, If I give 1 Language then it has to locate the first row second cell in which Arabic is chosen. Likewise, If I give 2 Primary it has locate the second row first column.

The HTML code is shown in the pic below. If it's possible to solve this problem, then I will give the actual code.

<div id="d78Pt30" style="width: 35em;" >
    <div id="d78Pt30-head"  style="">
        <table id="d78Pt30-headtbl" style="table-layout: fixed;" width="100%">
            <colgroup id="d78Px30-hdfaker">
                <col id="d78Py30-hdfaker" style="width: 61px;">
                <col id="d78Pz30-hdfaker" style="">
                <col id="d78P_40-hdfaker" style="width: 50px;">
                <col id="d78Px30-hdfaker-bar" style="width: 0px">
            </colgroup>
            <tbody id="d78Pt30-headrows">
            <tr id="d78Px30"  style="text-align: left;">
                <th id="d78Py30" >
                    <div id="d78Py30-cave" >
                        <div ><i id="d78Py30-sort-icon"></i></div>
                        Primary
                    </div>
                </th>
                <th id="d78Pz30" >
                    <div id="d78Pz30-cave" >
                        <div ><i id="d78Pz30-sort-icon"></i></div>
                        Language
                    </div>
                </th>
                <th id="d78P_40" >
                    <div id="d78P_40-cave" >
                        <div ><i id="d78P_40-sort-icon"></i></div>
                        <a id="d78P040"  href="javascript:;"><img src="assets/images/add.png"
                                                                             align="absmiddle"></a></div>
                </th>
                <th id="d78Px30-bar" ></th>
            </tr>
            </tbody>
        </table>
    </div>
    <div ></div>
    <div id="d78Pt30-body"  style="overflow: auto;">
        <table id="d78Pt30-cave" style="table-layout: fixed;" width="100%">
            <colgroup id="d78Px30-bdfaker">
                <col id="d78Py30-bdfaker" style="width: 61px;">
                <col id="d78Pz30-bdfaker" style="">
                <col id="d78P_40-bdfaker" style="width: 50px;">
            </colgroup>
            <tbody id="d78Pi50" >
            <tr id="d78P260" >
                <td id="d78P360-chdextr" >
                    <div id="d78P360-cell" ><span id="d78P360"
                                                                       ><input
                            type="radio" id="d78P360-real" name="d78P360" checked="checked"><label for="d78P360-real"
                                                                                                   id="d78P360-cnt"
                                                                                                   ></label></span>
                    </div>
                </td>
                <td id="d78P460-chdextr" >
                    <div id="d78P460-cell" ><span id="d78P460" 
                                                                       style="width: 225px;"><input id="d78P460-real"
                                                                                                    
                                                                                                    autocomplete="off"
                                                                                                    value="" type="text"
                                                                                                    size="20"
                                                                                                    style="width: 196px;"><a
                            id="d78P460-btn" ><i id="d78P460-icon"
                                                                          ></i></a><div
                            id="d78P460-pp" style="display: none;"></div></span></div>
                </td>
                <td id="d78Py60-chdextr" >
                    <div id="d78Py60-cell" >
                        <div id="d78Py60" >
                            <div id="d78Pz60-chdex"  style=""><a id="d78Pz60" 
                                                                                        href="javascript:;"><img
                                    src="assets/images/delete.png" align="absmiddle"></a></div>
                        </div>
                    </div>
                </td>
            </tr>
            </tbody>
            <tbody >
            <tr>
                <td id="d78Pt30-empty" style="display: none;" colspan="3">
                    <div id="d78Pt30-empty-content" >No data available</div>
                </td>
            </tr>
            </tbody>
        </table>
    </div>
</div>

As you can see in the HTML, there are two tables and first one is having the header and second one is having the rest of the rows.

CodePudding user response:

Since the full markup of the table was not provided, I've used the HTML at the end of the answer to illustrate the possible solutions.

Solution 1 - Hardcode the column index

If the table columns are _static, the easiest solution is to hardcode an index lookup in your code. This solution also makes it easier to deal with header cells that do not have text - eg the add icon.

For example, you know that "Language" header is always column index 1 and "Primary" is always column index 0. Therefore, you know that if you want the "Language" of a data row, it will be the 2nd cell in the row.

def cell_by_header_text(browser, data_row_index, header_text)
  columns = ['Primary', 'Language', 'Add'] # must match the order on the page
  column_index = columns.index(header_text)

  data_table = browser.div(class: 'z-grid-body').table
  data_table[data_row_index - 1][column_index] # returns Watir::Cell
end

p cell_by_header_text(browser, 1, 'Language').html
#=> "<td><select><option selected=\"selected\">Arabic</option><option>Bengali</option></select></td>"
p cell_by_header_text(browser, 2, 'Primary').html
#=> "<td><input type=\"radio\" checked=\"\"></td>"

Solution 2 - Dynamic lookup of column index

If the table columns are dynamic or you want a more general solution, you can lookup the column index from the header table.

def cell_by_header_text(browser, data_row_index, header_text)
  header_table = browser.div(class: 'z-grid-header').table
  column_index = header_table.tds.find_index { |td| td.text == header_text }

  data_table = browser.div(class: 'z-grid-body').table
  data_table[data_row_index - 1][column_index] # returns Watir::Cell
end

Solution 3 - Domain-specific collection

If you want to improve readability and have more flexibility, you could take it a step further and create a domain-specific collection for the grid:

class LanguageRowCollection
  include Enumerable

  def initialize(browser)
    @browser = browser
  end

  def each
    data_rows.map { |data| yield LanguageRow.new(header_row, data) }
  end

  def [](value)
    to_a[value]
  end

  private

  def header_row
    @browser.div(class: 'z-grid-header').table.tr
  end

  def data_rows
    @browser.div(class: 'z-grid-body').table.trs
  end
end

class LanguageRow
  def initialize(header_row, tr)
    @header_row = header_row
    @tr = tr
  end

  def primary_cell
    @tr.tds[@header_row.tds.map(&:text).index('Primary')]
  end

  def primary?
    primary_cell.radio.selected?
  end

  def set_primary(value)
    primary_cell.radio.set(value)
  end

  def language_cell
    @tr.tds[@header_row.tds.map(&:text).index('Language')]
  end

  def language
    language_cell.select.text
  end

  def set_language(value)
    language_cell.select.set(value)
  end

  def remove_cell
    # Locating the 3rd column by it's image since it doesn't have text
    @tr.tds[@header_row.tds.find_index { |td| td.image(class: 'add').exists? }]
  end

  def remove
    remove_cell.link.click
  end
end

def languages(browser)
  grid = browser.div(class: 'z-grid')
  LanguageRowCollection.new(grid)
end

You get a more readable way to get/set values:

# Get/set the language of the first row (note the 0-based index)
languages(browser)[0].language
#=> "Arabic"
languages(browser)[0].set_language('Bengali')

You also get the flexibility of locating rows based on their values:

# Get the primary language
languages(browser).find(&:primary?).language
#=> "Bengali"

# Remove the Arabic row
languages(browser).find { |l| l.language == 'Arabic' }.remove

HTML Example

The following HTML was used for the above examples.

<html>
  <body>
    <div id="d78Pt30" >
      <div >
        <table>
          <tr>
            <td>Primary</td>
            <td>Language</td>
            <td>Add</td>
          </tr>
        </table>
      </div>
      <div ></div>
      <div >
        <table>
          <tr>
            <td><input type="radio"></td>
            <td><select><option selected="selected">Arabic</option><option>Bengali</option></select></td>
            <td><a href="#">Minus</a></td>
          </tr>
          <tr>
            <td><input type="radio" checked></td>
            <td><select><option>Arabic</option><option selected="selected">Bengali</option></select></td>
            <td><a href="#">Minus</a></td>
          </tr>
      </div>
    </div>
  </body>
</html>
  • Related