2

I try to use IndexOf method in protractor. And I can't return number value instead Promise.

  PsGrid.prototype.getQuickFilterByName = function(name) {
       var num;
       num = this.all(by.xpath(".//tr[contains(@class,'n-grid__head-row')]/th[contains(@class,'n-grid__title')]//div[contains(@class,'n-grid__text')]")).getText().then(function(textArray){
       return textArray.indexOf(name) + 2;
       });

    expect(num).toEqual(8);  // num == 2
    console.log("sdf" + num); // num == Promise::3138 {[[PromiseStatus]]: "pending"} 
    return this.element(by.xpath('.//tr[2]/td[' + num + ']//input')); //protractor can't find element
};

Complete HTML code of table:

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.1/angular.min.js"></script>
<table class="n-grid">
      <colgroup>
        <col ng-repeat="column in $view.columns track by column.$id" ps-link-element="column.elements.col" ng-attr-width="{{column.settings.width}}" class="ng-scope" width=""><col ng-repeat="column in $view.columns track by column.$id" ps-link-element="column.elements.col" ng-attr-width="{{column.settings.width}}" class="ng-scope" width="185">
        <col ng-if="$view.fillerColumn.needVisible()" ng-attr-width="{{$view.fillerColumn.width}}" class="ng-scope" width="*">
      </colgroup>
      <thead class="n-grid__head" ps-event="{ mousedown: [
                         {exp: 'onMouseDown($event,column,internalOptions.states.preMoveColumn)', selector: 'div.n-grid__event'},
                         {exp: 'onMouseDown($event,column,internalOptions.states.resizeColumn)',  selector: 'div.n-grid-col-resize'}]
                       }">
        <tr class="n-grid__head-row" ng-show="$view.hasHeader">
          <th class="n-grid__title ng-scope ng-isolate-scope n-grid__status" ng-class="column.design.classes.title" ng-repeat="column in $view.columns track by column.$id" ps-link-element="column.elements.head" on-link-element="onLinkHead(column, $last)" ps-context-menu="menuOptions.show(column, $event)" ps-grid-bind-element="column.elements.title"><span ng-if="psGrid.isMultiSelect()" class="n-grid__select-all ng-scope"></span></th><th class="n-grid__title ng-scope ng-isolate-scope" ng-class="column.design.classes.title" ng-repeat="column in $view.columns track by column.$id" ps-link-element="column.elements.head" on-link-element="onLinkHead(column, $last)" ps-context-menu="menuOptions.show(column, $event)" ps-grid-bind-element="column.elements.title">
            <div class="n-grid-title-in" ng-style="{'z-index': $view.columns.length - $index}" style="z-index: 2;">
              <div class="n-grid__event n-grid__event_sort" ng-class="{'n-grid__event_sort': column.sortable}">
                <div class="n-grid__text ng-binding" ng-attr-title="{{column.title()}}" ng-bind="column.caption()" title="Имя клиента">Имя клиента</div>
                <span class="n-grid__sort-icon"></span>
              </div>
              <div ng-if="column.resizable" class="n-grid-col-resize ng-scope" ng-dblclick="menuOptions.onColumnAutoSize(column)"></div>
            </div>
          </th><th class="n-grid__title ng-scope ng-isolate-scope n-grid__title_sortable" ng-class="column.design.classes.title" ng-repeat="column in $view.columns track by column.$id" ps-link-element="column.elements.head" on-link-element="onLinkHead(column, $last)" ps-context-menu="menuOptions.show(column, $event)" ps-grid-bind-element="column.elements.title">
            <div class="n-grid-title-in" ng-style="{'z-index': $view.columns.length - $index}" style="z-index: 1;">
              <div class="n-grid__event n-grid__event_sort" ng-class="{'n-grid__event_sort': column.sortable}">
                <div class="n-grid__text ng-binding" ng-attr-title="{{column.title()}}" ng-bind="column.caption()" title="Номер">Номер</div>
                <span class="n-grid__sort-icon"></span>
              </div>
              <div ng-if="column.resizable" class="n-grid-col-resize ng-scope" ng-dblclick="menuOptions.onColumnAutoSize(column)"></div>
            </div>
          </th>
          <th ng-if="$view.fillerColumn.needVisible()" class="n-grid__title ng-scope ng-isolate-scope" ps-context-menu="">
            <div class="n-grid-title-in">&nbsp;</div>
          </th>
        </tr>

        <tr ng-if="$view.hasVisibleFilters" class="n-grid__head-row ng-scope" ps-event="{ 'keydown': {exp: '$event.keyCode === 13 &amp;&amp; $currentScope.onFilterApply()', selector: '.inp-text', apply: true},
                        'apply.value.component': {exp: '$currentScope.onFilterApply()', apply: true}}">
          <td ng-class="column.design.classes.filter" ng-repeat="column in $view.columns track by column.$id" ps-grid-bind-element="column.elements.filter || column.elements.filter_empty" class="ng-scope n-grid__title n-grid__status"><a ng-if="$view.hasVisibleFilters" ng-click="onFilterClear()" class="n-grid__cancel-filter ng-scope" ng-attr-title="{{locale.psGrid.clearFilter}}" title="Очистить фильтр"><span class="n-grid__filter-icon"></span></a></td><td ng-class="column.design.classes.filter" ng-repeat="column in $view.columns track by column.$id" ps-grid-bind-element="column.elements.filter || column.elements.filter_empty" class="ng-scope n-grid__filter">
          <input class="inp-text ng-scope ng-pristine ng-valid" ng-mousedown="in.filter.dirty.customer.filterMethod=4" ng-model="in.filter.dirty.customer.name" maxlength="64">
        </td><td ng-class="column.design.classes.filter" ng-repeat="column in $view.columns track by column.$id" ps-grid-bind-element="column.elements.filter || column.elements.filter_empty" class="ng-scope n-grid__filter">
          <input class="inp-text ng-scope ng-pristine ng-valid" ng-model="in.filter.dirty.inquiry.ids" ps-input-allow="/^[\s,;0-9]*$/">
        </td>
          <td ng-if="$view.fillerColumn.needVisible()" class="n-grid__filter ng-scope">
            <span class="n-grid__empty-placeholder">&nbsp;</span>
          </td>
        </tr>
      </thead>
    </table>

Column name - "Номер".

I tryed to write addLocator method, but I think that I have some misunderstandings there.

  • 1
    Have an alternative solution. Could you post the complete HTML of the table (including headers) and an example "name" value? Thanks. – alecxe Nov 20 '15 at 15:59

2 Answers2

2

Yes, num is a promise. So if you want to do anything with it, it needs to go in a then callback; and your function can only return another promise.

PsGrid.prototype.getQuickFilterByName = function(name) {
    return this.all(by.xpath(".//tr[contains(@class,'n-grid__head-row')]/th[contains(@class,'n-grid__title')]//div[contains(@class,'n-grid__text')]")).getText().then(function(textArray){
        return textArray.indexOf(name) + 2;
    }).then(function(index) {
        expect(index).toEqual(8);
        console.log("sdf" + index);
        return this.element(by.xpath('.//tr[2]/td[' + index + ']//input'));
    });
};
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Thanks! But then I return this element I cant use methods like click(). I always have an error " Failed: undefined is not a function". Also because it is Promise I suppose. – Лилия Сапурина Nov 20 '15 at 10:00
  • Where do you want to call `click` upon? Try `.then(function(element) { element.click(…); })` – Bergi Nov 20 '15 at 10:14
  • Yes, but in general I want to do a lot of actions with my element inside this method. I made a research and understood that with Promises I can't do this. I should to find another alternatives. – Лилия Сапурина Nov 20 '15 at 11:37
  • 1
    If you have lots of promises (and from what I understand, with Protractor you always have), you will want to have a look at [ES6 generators or ES7 async/await](http://stackoverflow.com/a/28250697/1048572) which simplify the syntax a great lot. – Bergi Nov 20 '15 at 11:58
1

You can locate the element in a desired column in one go with a rather complicated XPath expression. The idea is to get the position of a desired header cell and use that position to locate the desired "data" cell. Splitted into multiple lines for readability:

.//tr[2]/td[
    count(//preceding::tr[contains(@class,'n-grid__head-row')]/th[
        contains(@class,'n-grid__title') and .//div/@title = 'Номер'
    ]/preceding-sibling::th) + 3
]//input

where Номер is an example of a header name. Why + 3? - 1 of it is needed to define the position of the header (we are counting preceding th elements to calculate the header position), another 2 is coming from the code you've provided: return textArray.indexOf(name) + 2;.

Hope it's clear what I'm trying to do here.

alecxe
  • 462,703
  • 120
  • 1,088
  • 1,195
  • @alacxe Thank you! This solution looks so good. But I always have the error: "Failed: The xpath expression. For documentation on this error, please visit: http://seleniumhq.org/exceptions/invalid_selector_exception.html" – Лилия Сапурина Nov 25 '15 at 09:07
  • @ЛилияСапурина sorry for that, fixed that, please try it out. – alecxe Nov 25 '15 at 14:14