5

I have an ng-repeat which has a new element added to it as soon as the existing "last" element is modified in any way. My protractor test looks something like this:

var emptyPerson = this.people.last() // this gets me the last row of the ng-repeat
emptyPerson.element(by.css('.firstName')).sendKeys('first'); // this sets the firstname correctly but then the app adds a new row to the ng-repeat because of the model change here.
emptyPerson.element(by.css('.lastName')).sendKeys('last');  // this sets the lastname of the new row instead of the row that emptyPerson previously referenced

Is there any way to essentially tell emptyPerson to stick to the same element until we're done with it?

Example HTML before firstname is edited:

<div ng-repeat="person in object.People" class="student ng-scope">
  <div type="text" ng-model="person.FirstName" skip-label="true" placeholder="First" validator="svalidators[$index]" class="layout-default field field-FirstName type-text">
    <input type="text" ng-focus="myFocus()" ng-blur="myBlur()" class="form-control" placeholder="First" ng-model="lModel.val" name="person-FirstName">
  </div>
  <div type="text" ng-model="person.LastName" skip-label="true" placeholder="Last" validator="svalidators[$index]" class="layout-default field field-LastName type-text">
    <input type="text" ng-focus="myFocus()" ng-blur="myBlur()" class="form-control" placeholder="Last" ng-model="lModel.val" name="person-LastName">
</div>

example after firstname is edited:

<div ng-repeat="person in object.People" class="student ng-scope">
  <div type="text" ng-model="person.FirstName" skip-label="true" placeholder="First" validator="svalidators[$index]" class="layout-default field field-FirstName type-text">
    <input type="text" ng-focus="myFocus()" ng-blur="myBlur()" class="form-control" placeholder="First" ng-model="lModel.val" name="person-FirstName">
  </div>
  <div type="text" ng-model="person.LastName" skip-label="true" placeholder="Last" validator="svalidators[$index]" class="layout-default field field-LastName type-text">
    <input type="text" ng-focus="myFocus()" ng-blur="myBlur()" class="form-control" placeholder="Last" ng-model="lModel.val" name="person-LastName">
</div>
<div ng-repeat="person in object.People" class="student ng-scope">
  <div type="text" ng-model="person.FirstName" skip-label="true" placeholder="First" validator="svalidators[$index]" class="layout-default field field-FirstName type-text">
    <input type="text" ng-focus="myFocus()" ng-blur="myBlur()" class="form-control" placeholder="First" ng-model="lModel.val" name="person-FirstName">
  </div>
  <div type="text" ng-model="person.LastName" skip-label="true" placeholder="Last" validator="svalidators[$index]" class="layout-default field field-LastName type-text">
    <input type="text" ng-focus="myFocus()" ng-blur="myBlur()" class="form-control" placeholder="Last" ng-model="lModel.val" name="person-LastName">
</div>
RPichioli
  • 3,245
  • 2
  • 25
  • 29
Robert
  • 1,024
  • 2
  • 9
  • 21
  • Should not the "first name" input of the previous to last element have the `value` attribute set to `first`? – alecxe Sep 05 '16 at 20:10
  • no. the associated model is set to first and the input element's property value is set to first but the value attribute itself does not exist and is not set. – Robert Sep 05 '16 at 21:13
  • Okay, I'm afraid you would have to build sort of a "manual" dirty checking once you start filling out inputs. One option would be to check the value of the corresponding model on the first name input to be non-empty. Or, simply switch to the element before last after filling out first name. Not pretty, I agree. – alecxe Sep 05 '16 at 21:16
  • Or, can you somehow disable this behavior of adding a new repeater element on changing the last element in the repeater? Is this something controllable/configurable/easily changeable? – alecxe Sep 05 '16 at 21:39
  • That behavior is exactly what we want to happen. It's an ever growing list. As soon as you dirty the last item of the list, it creates a new empty item for you to use. So the app behavior is desireable, it's just a pain to test. – Robert Sep 05 '16 at 22:14

2 Answers2

1

This is how Protractor works internally. It searches for the element at the time an action is applied on the element. I am afraid you have to handle it "manually" and re-reference the desired repeater item.

To make things a little bit better in terms of coding, I would hide the first interaction with the repeater item part in a function - basically, touching the input for repeater to have one more item, then returning the item before last. Something along the lines:

var MyPageObject = function () {
    this.people = element(by.repeater("person in object.People"));

    this.touch = function () {
        var emptyPerson = this.people.last();
        emptyPerson.element(by.css('.firstName')).sendKeys('smth');

        var newEmptyPerson = this.people.get(-2);  // I think -1 would be the last
        emptyPerson.element(by.css('.firstName')).clear();

        return newEmptyPerson;
    }

    this.fillForm = function () {
        var emptyPerson = this.touch();

        emptyPerson.element(by.css('.firstName')).sendKeys('first');
        emptyPerson.element(by.css('.lastName')).sendKeys('last');    
    }
}
alecxe
  • 462,703
  • 120
  • 1,088
  • 1,195
0

Try saving to a variable before it changes:

var oldLastName = emptyPerson.element(by.css('.lastName'));
emptyPerson.element(by.css('.firstName')).sendKeys('first'); 
oldLastName.sendKeys('last');

Hope it helps

Jahongir Rahmonov
  • 13,083
  • 10
  • 47
  • 91
  • Nope, that should not work - protractor would "activate" search for an element only if an action is taken place on it. – alecxe Sep 05 '16 at 19:35
  • A) It may not have been clear in the question but there are actually a ton of variables that would need to be accessed in this way so saving it at the field level would be impractical. B) This doesn't work anyway. The thing you're saving in that case is still an element finder that traces all the way up and only resolves when used. Thanks for giving it a shot though. – Robert Sep 05 '16 at 19:36