Amritanshu Joshi
Amritanshu Joshi

Reputation: 33

How do I get rows from a list(table) in a protractor e2e test?

The list in question is a table generated by a reactive angular form which does not have a specific ID. Following code is used to generate the list in angular part:

<p-table id='paragraphList' *ngIf="paragraphsObs | async; else loading"
         [value]="paragraphsObs | async"
         selectionMode="single" (onRowSelect)="select($event)"
         scrollable="true">
  <ng-template pTemplate="header">
    <tr> ...header... </tr>
  </ng-template>
  <ng-template pTemplate="body" let-paragraph let-rowData>
    <tr [pSelectableRow]="rowData">
      <td width="15%">{{paragraph.cell1}}</td>
      <td width="10%">{{paragraph.cell2}}</td>
      <td width="31%">{{paragraph.cell3}}</td>
      <td width="11%">{{paragraph.cell4 | dateTransform: helperService.MM_DD_YYYY_HH_MM_A_Z_DATE_PATTERN}}
      </td>
      <td width="11%">{{paragraph.cell5}}</td>
      <td width="11%">{{paragraph.cell6 | dateTransform: helperService.MM_DD_YYYY_HH_MM_A_Z_DATE_PATTERN}}
      </td>
      <td width="11%">{{paragraph.cell7}}</td>
    </tr>
  </ng-template>
</p-table>

The corresponding table generated at the front-end has the following html source:

<p-table _ngcontent-c6="" id="paragraphList" scrollable="true" selectionmode="single" ng-reflect-selection-mode="single" ng-reflect-scrollable="true" class="ng-star-inserted" ng-reflect-value="[object Object],[object Object">
  <div class="ui-table ui-widget ui-table-hoverable-rows" ng-reflect-ng-class="[object Object]">
    <div class="ui-table-scrollable-wrapper ng-star-inserted">  
      <div class="ui-table-scrollable-view" ng-reflect-frozen="false">
        <div class="ui-table-scrollable-header ui-widget-header">...header...</div>
        <div class="ui-table-scrollable-body">
          <table class="ui-table-scrollable-body-table" ng-reflect-klass="ui-table-scrollable-body-table" ng-reflect-ng-class="[object Object]">               
            <tbody class="ui-table-tbody" ng-reflect-template="[object Object]">
              <tr _ngcontent-c6="" ng-reflect-data="[object Object]" class="ng-star-inserted">...</tr>
              <tr _ngcontent-c6="" ng-reflect-data="[object Object]" class="ng-star-inserted">...</tr>
               ...
            </tbody>
          </table>
          <div class="ui-table-virtual-scroller"></div>
        </div>
      </div>
    </div>
  </div>
</p-table>

I want to reach to those inner elements and get them as a list. I have tried using class names with element and all locators, to get the elements but to no avail. Then I tried using tag names to reach to those elements but that too doesn't seem to work.

This following small snippet returns 0 for the count of elements that I try to obtain from the list.

element(by.id('paragraphList')).element(by.css('.ui-table-scrollable-body-table'))
  .all(by.tagName('tr')).count().then(function (result) {
  console.log(result);
});

Any help would be appreciated. Thanks

Upvotes: 1

Views: 2150

Answers (1)

Ashish Ranjan
Ashish Ranjan

Reputation: 12960

Considering above is your full rendered HTML.. The code below will give an Array of arrays, where each array would be containing texts from all the cells of a row.

Explanation: The code has three functions, populateData() - is the driving function where we pass the resolved list of rows.

Then _populateRows() and _populateCells() run recursively to gather the text from the cells. This is also possible to do with a loop (as protractor queues the promises by itself) but I like keeping things clear on my end. _populateRows() recur on rows and _populateCells() recur on cells of each row. (more in comments)

Note This first thing which you should do before implementing this is: check the count() (or .length of resolvedRows) of element.all(by.css('#paragraphList table tbody tr')). As basically this was your original question I believe. Now If you have a count, then you can go with this solution or whatever suites your need.

let allRows = element.all(by.css(`#paragraphList table tbody tr`)); //will have all the rows.
allRows.then((rowsResolved) => {
    // now have all the rows
    PO.populateData(rowsResolved).then((allData) => {console.log(allData)})  // should be an Array od arrays, each array would be containing texts from all the cells. 
    // Considering you have a Page Object and added the functions below in the Page Object.
    // Page Object is nothing but another class where we keep our utility methods
})


//    driving function
populateData(rowsResolved) {
    let data = [];
    return this._populateRows(0, rowsResolved, data);
}

// calls itself recursively to loop over the rows
private _populateRows(index, rowsResolved, data) {
    if (index >= rowsResolved.length) {
        let defer = protractor.promise.defer();
        defer.fulfill(data);
        return defer.promise;   // so that it is chainable even if I don't have any rows
    }

    let cells = element.all(by.css(`#paragraphList table tbody tr:nth-child(${index + 1}) td`));
    cells.then((cellsResolved) => {
        let cellData = [];
        if (cellsResolved.length) {
            data.push(cellData);
        }
        this._populateCells(0, cellsResolved, cellData);
        return this._populateRows(index + 1, rowsResolved, data);
    })
}

// calls itself recursively to loop over all the cells ofeach row.
private _populateCells(index, cellsResolved, cellData) {
    if (index >= cellsResolved.length) {
        let defer = protractor.promise.defer();
        defer.fulfill(cellData);
        return defer.promise;  // so that it is chainable even if I don't have any cells(that would be an incorrect structure though, if a row exists then cells have to exist )
    }

    cellsResolved[index].getText().then((cellValue) => {
        cellData.push(cellValue)
    });
    return this._populateCells(index + 1, cellsResolved, cellData);
}

Upvotes: 2

Related Questions