46

With protractor whats the best way to select child elements? Say we have the layout below...

<div id='parent_1'>
    <div class='red'>Red</div>
    <div class='blue'>Blue</div>
</div>
<div id='parent_2'>
    <div class='red'>Red</div>
    <div class='blue'>Blue</div>
</div>

With jQuery we'd do something like this.

var p1 = $('#parent_1');
var p1_red = $('.red', p1);  //or p1.find('.red');
var p1_blue = $('.blue', p1); //or p1.find('.blue');

But with Protractor does it make sense to first get the parent element? Since doing this var p1 = element('#parent_1'); doesn't actually retrieve/search for the object until getText() or something is called.

so doing this..

Scenario 1

expect(p1.element('.red')).toBe('red');
expect(p1.element('.blue')).toBe('blue');

OR

Scenario 2

expect(element('#parent_1').element('.red')).toBe('red');
expect(element('#parent_1').element('.blue')).toBe('blue');

OR

Scenario 3

expect(element('#parent_1 > .red')).toBe('red');
expect(element('#parent_1 > .blue')).toBe('blue');

Are there any benefits in one approach over the other?

This is what I'm doing but I don't know if there's any advantage of separating the parent from the cssSelector:

function getChild(cssSelector, parentElement){
    return parentElement.$(cssSelector);
}

var parent = $('#parent_1');
var child_red = getChild('.red', parent);
var child_blue = getChild('.blue', parent);

Looking at Protractor's elementFinder I could be doing this:

function getChild(cssSelector, parentCss){
    return $(parentCss).$(cssSelector);
}

var child_red = getChild('.red', '#parent_1');
var child_blue = getChild('.blue', '#parent_1');
LuckyStarr
  • 1,468
  • 2
  • 26
  • 39
Brad8118
  • 4,672
  • 11
  • 36
  • 48

1 Answers1

58

The advantage of separating the child from the child css selector would only be if you'd like to use the parent for something else. Otherwise, it's slightly faster to do it in one call, like expect(element('#parent_1 > .red')).toBe('red'); since Protractor doesn't need to make two calls to the browser in this case.

Another reason to use the first approach would be if you were using a Locator strategy that cannot be expressed in CSS. For example:

var parent = element(by.css('.foo'));
var child = parent.element(by.binding('childBinding'));
expect(child.getText()).toEqual('whatever');
Liam
  • 27,717
  • 28
  • 128
  • 190
Jmr
  • 12,078
  • 4
  • 39
  • 33
  • So you're saying, Scenario 1 is better to do than Scenario 2. Protractor doesn't fetch the element until its needed. So does that mean for the second call its being cached? I though it would resolve the promise 2x for p1. – Brad8118 Aug 06 '14 at 21:13
  • 1
    I'm saying that scenario 3 is slightly faster than scenario 1 or scenario 2. Scenario 1 and Scenario 2 are equivalent in terms of what commands get sent to the browser. For scenario 3, when getting the element Protractor needs to send 1 command to the browser instead of 2. – Jmr Aug 06 '14 at 22:52
  • All things performance-related aside, this answer contains a better example of how this should be done than any other that I have seen. – MBielski Dec 12 '14 at 17:49
  • 1
    This answer does not work for angular2. `by.binding` is not supported, see https://stackoverflow.com/a/36267362/786389 – Josh Hibschman Aug 11 '17 at 16:30