4
<label class="radio inline check">

            <input class="input ng-new ng-valid" name="BookType" required="" type="radio">

            <!---->

            <!---->
            Fiction
</label>

<label class="radio inline check">   

            <input class="input ng-new ng-valid" name="BookType" required="" type="radio">

            <!---->

            <!---->
            NonFiction
</label>

<label class="radio inline check">

            <input class="input ng-new ng-valid" name="BookTypeReal" required="" type="radio">

            <!---->

            <!---->
            Fiction
</label>

<label class="radio inline check">   

            <input class="input ng-new ng-valid" name="BookTypeReal" required="" type="radio">

            <!---->

            <!---->
            Fantasy
</label>

http://www.protractortest.org/#/api?view=ElementArrayFinder.prototype.filter

If I use

element.all(locator).filter(filterFn) 

the text returned is empty.

How can I go to parent element <label> to get the text?

label[class="radio inline check"] 

returns 60 elements where more than one getText will return same text so it won't be unique.

input[name="BookType"] 

returns two elements where each element is unique.

Andersson
  • 51,635
  • 17
  • 77
  • 129
SUM
  • 1,651
  • 4
  • 33
  • 57
  • What is the scenario you are trying to test? In my opinion, selenium tests should always know what they are looking for and not try to figure it out with some logic. So in this case, you would be looking for the element with text `Fiction` or `NonFiction` instead of figuring out what the text is if the value is static. It's hard to say for sure though since I'm not sure what your case is. – mrfreester Mar 06 '18 at 15:40
  • @mrfreester I am trying to click on radio button/label that has label Fiction – SUM Mar 06 '18 at 15:42
  • So you need the `input`? Or the `label`? You can try `//label[text()='Fiction']` or `//label[contains(text(),'Fiction')]` if there are weird spaces for the `label`. If you need the `input` you can put a `/input` at the end of that. If you can confirm that's what you're looking for and that works I can post as an answer. – mrfreester Mar 06 '18 at 15:53
  • @mrfreester //label[contains(text(),'Fiction')] or //label[text()='Fiction'] does not work. I have updated the code above to show there are many labels with same text 'Fiction', what makes each one unique is the input[name] value. I want to click on label which has text as Fiction and – SUM Mar 06 '18 at 16:02
  • I think I understand now. So you're looking for this? `//label[contains(text(),'Fiction') and ./input[@name='BookType']]`. That's an xpath... unfortunately css selectors don't have a select parent :( which is too bad because css selectors are usually cleaner to read. – mrfreester Mar 06 '18 at 16:11
  • There is [this for css with children...](https://developer.mozilla.org/en-US/docs/Web/CSS/:has), but it's experimental so I probably would be careful to adopt it. – mrfreester Mar 06 '18 at 16:13
  • Could you provide the code what you have tried instead of just the html. I have an idea how to solve it but it would be better if there was more context. If you want to give it a try, you should use http://www.protractortest.org/#/api?view=webdriver.WebElement.prototype.getDriver – cnishina Mar 06 '18 at 20:07
  • @SUM, the easy way is directly with an XPath: `by.xpath("input[@name='BookTypeReal']/..")`. The other way is to use a JavaScript injection: `browser.executeScript("return arguments[0].parentElement", $("[name='BookTypeReal'"))`. – Florent B. Mar 06 '18 at 21:09
  • @cnishina Code I have tried (does not work for me): var inputRadio = element.all(by.css('input[name=\'BookType\']')).get(0).getWebElement(); var labelText = inputRadio.getDriver().findElement(by.css('label[class*='radio inline']')); labelText.click(); – SUM Mar 20 '18 at 18:49
  • Currently not at a computer to test this out but have you seen this? https://stackoverflow.com/questions/8577636/select-parent-element-of-known-element-in-selenium the xpath with '..' looks interesting – cnishina Mar 24 '18 at 09:47
  • Could you share your `filter` function? – Oleksii Mar 25 '18 at 19:06

5 Answers5

1

I used the following approach using TypeScript:

import {by, ElementFinder} from 'protractor';

export interface ElementDescriptor {
  tag?: string;
  cssClass?: string;
}

async function matches(element: ElementFinder, parentDescription: ElementDescriptor): Promise<boolean> {
  const promises = [];
  if (parentDescription.tag) {
    promises.push(element.getTagName());
  }
  if (parentDescription.cssClass) {
    promises.push(element.getAttribute('class'));
  }

  let i = 0;
  const results: string[] = await Promise.all(promises);

  return (!parentDescription.tag || results[i++] === parentDescription.tag)
    && (!parentDescription.cssClass || results[i++].split(' ').includes(parentDescription.cssClass));
}

export async function findParent(element: ElementFinder, parentDescription: ElementDescriptor): Promise<ElementFinder> {
  let parent: ElementFinder;
  do {
    parent = element.element(by.xpath('..'));
  } while ((await parent.getTagName()) !== 'html' && !(await matches(parent, parentDescription)));
  return parent;
}

Kamil Janowski
  • 1,872
  • 2
  • 21
  • 43
0

To click on the <input> tag whose <label> has text as Fiction and <input> tag has name as BookType you can use the following xpath :

"//label[@class='radio inline check' and contains(normalize-space(), 'Fiction')]/input[@class='input ng-new ng-valid' and @name='BookType']"
undetected Selenium
  • 183,867
  • 41
  • 278
  • 352
0

You have two options to do it:

  1. To get the parent element, as you mentioned. For this you can use xpath's ancestor for it like this:

    .//*[@name='BookType']/ancestor::label

  2. To get the text as the sibling of the element with @name='BookType':

    .//*[@name='BookType']/following-sibling::text()[normalize-space()]

Cosmin
  • 2,354
  • 2
  • 22
  • 39
0

In Xpath if you want parent of input you can use //input[name="BookType"]/..

mosaad
  • 2,276
  • 5
  • 27
  • 49
0

i have framed this based on available details over the net. Please give a try.

element.all(by.css('.radio inline check')).filter(function(elem){
        return elem.element(by.xpath("//*[text()[contains(.,'Fiction')]]")).getText() 
        }).then(function(filteredElements){
        filteredElements.first().element(by.css('input[name=BookType]')).click();
        });

Note: You may have some format issue in this.

Guna Yuvan
  • 101
  • 11