Dave P
Dave P

Reputation: 89

Protractor element chaining is not working for me

I'm building some test operations against an Angular application using Protractor. I'm trying to keep my locators as easy to read and maintain as possible, and was trying to use "element chaining" to do this. Based on everything that I've read here in SO and the Protractor documentation, I think the following locator strategy should work:

The xpPanelPayment variable is defined just for readability.

let xpPanelPayment = "//div [@class='panel-heading' and text()='Payment']/following-sibling::div [@class='panel-body']";   
this.pnlPayment = element(by.xpath(`${xpPanelPayment}`));
this.valTotalPayment = element(by.xpath(`${xpPanelPayment}`))
    .element(by.xpath(`//strong [text()='Total Payment:']/../following-sibling::div/strong`));

What I would prefer is:

this.valTotalPayment = this.pnlPayment
    .element(by.xpath(`//strong [text()='Total Payment:']/../following-sibling::div/strong`));

But when I try that, I get an error that seems to indicate that this.pnlPayment is undefined. Perhaps this is a clue?

Here is the method that makes use of those locators:

const Receipt = require('./Receipt.js').Receipt;

exports.verifyTotalPayment = (payment) => {
    it(`Receipt Validation - Verify total payment $${payment}`, () => {
        console.log(`Receipt.pnlPayment.locator() = '${Receipt.pnlPayment.locator()}'`);
        console.log(`Receipt.valTotalPayment.locator() =         
'${Receipt.valTotalPayment.locator()}'`);
        expect(Receipt.valTotalPayment.getText()).toEqual(`$${payment}`);
    });
}

Here is the contents of the run log:

Receipt.pnlPayment.locator() = 'By(xpath, //div [@class='panel-heading' and text()='Payment']/following-sibling::div [@class='panel-body'])'
Receipt.valTotalPayment.locator() = 'By(xpath, //strong [text()='Total Payment:']/../following-sibling::div/strong)'
[09:31:07] W/element - more than one element found for locator By(xpath, //strong [text()='Total Payment:']/../following-sibling::div/strong) - the first result will be used

It appears that the "parent" portion of valTotalPayment is completely ignored. What did I do wrong with my specification for valTotalPayment? If I use the entire xpath string without referencing the parent object, valTotalPayment finds the correct element, but that defeats what I'm trying to do.

Upvotes: 2

Views: 893

Answers (2)

yong
yong

Reputation: 13712

The issue comes from your xpath for valTotalPayment. You expect to find valTotalPayment from pnlPayment's descendant.

You use // but ./ for valTotalPayment.

// means any element node of the whole page

./ means any descendant element node of previous/parent element

Finally, CSS selector is first option when writing locator, xpath is second.

And you can mix Css selector and xpath in element chain if necessary: element(by.css()).element(by.xpath()).element(by.css())....

Upvotes: 5

Vishal Aggarwal
Vishal Aggarwal

Reputation: 4177

It works for me:

var parent = element(by.css('.parent-class'));
var child = element(by.css('.child-class'));

parent.element(child.locator()).getText();

Upvotes: 0

Related Questions