Finding a parent element in webdriver.io - webdriver-io

I've seen a couple of solutions in the original webdriver that use getAttribute('xpath') and append to that '/..' but webdriver.io doesn't have an xpath attribute so I haven't been able to use that. Any ideas on how to grab the parent element?
The case I am trying to test is inside of a bootstrap layout and the element that is actually getting the class I am trying to check is one above. It looks like this:
<div class="form-group">
<input class="form-control" type="text" name="username">
<other stuff>
</div>
I am selecting by driver.element("input[name='username'"] but the error class actually hits the div
<div class="form-group error">
<input class="form-control" type="text" name="username">
<other stuff>
</div>
So I need to check if the div itself has an error class, not the input I can find (there are no uniques on the div)
Any help would be greatly appreciated.

Just searched the same and found this by inspecting Webdriver.IO source code - you can use el.$('..') to get the parent element, like this:
$('input[name="username"]').$('..') // returns the parent element
Voila! It's a part of their XPath selectors support.

I ended up solving this by using execute to get the xpath from jQuery, here is the code I used (albeit for a different test I was writing - if every label that has a * is a required field). Hopefully this is helpful to somebody in the future:
var requiredXPaths = browser.execute(function () {
var returner = [];
var $elements = $('.modal.fade.in').find("label:contains('*')");
_.each($elements, el => {
var xpath = '';
var element = el.parentElement;
for (; element && element.nodeType == 1; element = element.parentNode) {
var id = $(element.parentNode).children(element.tagName).index(element) + 1;
id > 1 ? (id = '[' + id + ']') : (id = '');
xpath = '/' + element.tagName.toLowerCase() + id + xpath;
}
returner.push(xpath);
});
return returner;
});
I got the xPath function from another stackoverflow page (How to calculate the XPath position of an element using Javascript?)

WebdriverIO actually lets you use XPath in your selectors, so any valid Xpath selector should work to find the element you want.
Alternatively, you could just use isExisting to check if the element exists on the page using the error class parent:
driver.isExisting('.error input[name="username"]');
If the element doesn't exist, then your error class didn't get added.

I've been talking about this in gitter, when the parent can be selected input[name="username"]
The parent of this element can be selected as //div[input[name="username"]]

Related

how to verify the drop down value using asserts in selenium when the HTML element is not Select class

i have an HTML element which is not select element. so i want to verify all the dropdown list using soft assert.
This is the code which i tried but it works only for select HTML element.
String[] exp = {"--None--","Open","Closed","Priority-Reopened","Researching","Updated","Escalated"};
WebElement dropdown = threadWebDriver.get().findElement(By.id("ddlNights"));
Select select = new Select(dropdown);
List<WebElement> options = select.getOptions();
for(WebElement we:options)
{
boolean match = false;
for (int i=0; i<exp.length(); i++){
if (we.getText().equals(exp[i]){
match = true;
}
}
Assert.assertTrue(match);
}
HTML element for this is given below:-
<a aria-required="true" class="select" aria-disabled="false" aria-describedby="2295:0-label" aria-haspopup="true" tabindex="0" role="button" title="" data-aura-rendered-by="2305:0" href="javascript:void(0);" data-interactive-lib-uid="9">Open</a>
How i can verify the list of drop down values?
I would take another approach of checking if the dropdown have any of the values specified using xpath like this.
//*[#id='ddlNights'][contains('--None--,Open,Closed,Priority-Reopened,Researching,Updated,Escalated',a)]
Screenshot:
You can check if the element exist with the above xpath. That validates true if any of the list items present otherwise it will be false. You have to handle that assertion using softAssertion.

click only visible element selenium webdriverjs

I have multiple div like below
<div class="one">send Message</div>
<div class="one">send Message</div>
<div class="one">send Message</div>
I have a web page where there is send Message buttons like above, in which only one button is visible at a time.Other two buttons are hidden via some javascript codes.So for example if 2nd button is visible , I should be able to click only that element.But in my selenium code , its trying to click first hidden div and its failing
driver.findElements(by.className(".one")).then((els) => {
var element = els[index];
element.click();
});
So basically I wanna convert below javascript code to Selenium nodejs code,If some one guide me that will be helpful
var all = document.getElementsByTagName("*");
for (var i = 0, max = all.length; i < max; i++) {
if (isHidden(all[i]))
// hidden
else
// visible
}
function isHidden(el) {
var style = window.getComputedStyle(el);
return ((style.display === 'none') || (style.visibility === 'hidden'))
}
Do you want to click the button ( basically a div as far as code is concerned ) which is visible ?
If that is your main agenda, then the code you've written will fail to find desired element. As you are selecting the element by it's classname not its visibility.
Your code will find all the matched class element. As it's a basic element selector and all your buttons have the same class, so they are basically rendered on the page.
Approach 1
driver.findElements(by.className(".one")).then((els) => {
for(var key in els){
var element = els[key];
if(element.isDisplayed()){ //if visible element
element.click();
}
}
});
The Key here is to check if the element you are trying to click is visible on the page.
Approach 2
By giving a unique class to the target button. Some class for eg 'clickable' or 'active'. So it will be a more optimized solution to select the target element using the Css selector. The Key here is to give uniqueness to your target element to be more identifiable.
Usually many Java Scripts are run in the node Js without the convert.
Have you try it in the node Js without converting ???
** Remember to import selenium

xpath selenium multi/and operators

Could anybody help how to solve this dilemma
I have this code:
<div>
Link
<span class="make">Chevrolet</span><br>
<span class="year">1956</span><br>
<span class="price">$20,000</span><br>
</div>
<div>
Link
<span class="make">Ford</span><br>
<span class="year">1958</span><br>
<span class="price">$21,000</span><br>
</div>
I need get the link for example Fords with the year greater then 1950.
Presently, I am using following xpath:
//*[text()='Ford' and .//text()>'1950']//parent::a
And this doesn't work! Have you any idea ?
This is one possible XPath :
//div[span/text()='Ford' and span/text()>1950]/a
Basically the XPath check if div has child span with text equals 'Ford' and another child span with value greater than 1950. Then from such div that match the two criteria above, return child a element.
demo
Better yet, only check span with class 'make' for manufacturer and span with class 'year' for manufacturing year :
//div[span[#class='make']='Ford' and span[#class='year']>1950]/a
You can write a generic method to get the required links, as shown below:
public static List<WebElement> getLinks(String linkText, int year) {
List<WebElement> links = driver.findElements(By.xpath("//a[text()='" + linkText + "']/following-sibling::span[1]"));
List<WebElement> linksGreaterThanRequiredYear = new ArrayList<WebElement>();
for (WebElement link : links) {
if (Integer.parseInt(link.getText()) > year)
linksGreaterThanRequiredYear.add(link);
}
return linksGreaterThanRequiredYear;
}
Hence, if you want to get the Fords with year greater than 1950, you can call above method in following way:
List<WebElement> fordsWithYearGreaterThan1950 = getLinks("Ford", 1950);
Above method can be further enhanced to include less than criteria as well. Let me know, if you have any further queries.

Locating Element with same class in Selenium using c#

I am trying to access ABC. I know that simple By.ClassName("bb") will not work here. How else can I access this content.
<body>
<div id="Frame">
<div class="bb"></div>
<div class="bb">ABC</div>
</div>
</body>
You can use the below css selector to get the value of "ABC".
.bb:nth-child(2)
You can use "XPath" Expression to find or locating your element.
Example : element = findElement(By.xpath("Your xpath expression");
For your XML use following line.
element = findElement(By.xpath("/body/div/div[#class='bb'][node()]");
There is a way to do this in the search using XPath but I am not an XPath expert. I can give you a solution using CSS Selectors. Basically you grab all the DIVs with class bb and then search their text to find the desired text.
String searchText = "ABC";
IReadOnlyCollection<IWebElement> divs = driver.FindElements(By.CssSelector("div.bb"));
foreach (IWebElement div in divs)
{
if (div.Text == searchText)
{
break; // exit the for and use the variable 'div' which contains the desired DIV
}
}

Testing relative positions of elements

On the page under test I have the following Support link:
Which is represented with the following HTML:
<div class="ap-version-panel ap-version-support">
<i class="fa fa-external-link"></i>Support
</div>
What I'm trying to is to test that the icon is located before the "Support" text. How can I do that?
I've tried the following - locate the icon element with an XPath that additionally checks that there is "Support" text after and check if the element is present:
expect(element(by.xpath("//text()[. = 'Support']/preceding-sibling::i[contains(#class, 'fa-external-link')]")).isPresent().toBe(true);
This works but it is quite ugly and I don't like that the actual position check is hidden inside the XPath expression which is not really readable and reliable.
I recommend changing the product under test by adding a <span> and move the text "Support" into the <span>.
<div class="ap-version-panel ap-version-support">
<i class="fa fa-external-link"></i><span>Support<span>
</div>
But if you cannot change the product, you can use javascriptExecutor to get childNodes then check order of the nodes:
var aFunctionToCheckOrder = function (arguments) {
var nodes = arguments[0].childNodes;
var result;
// Check nodes[0] = <i>
// Check node [1] is textNode and its value = "Support"
return result;
}
var supportLink = element(by.linkText("Support"));
browser.executeScript(aFunctionToCheckOrder, supportLink).then(...)
As you can see, it is more uglier than your solution. You'd better change your product under test.