Using selenium to access attribute text - html

I'm really new to selenium and this is probably really simple, but what i'm trying to do is store the '2017 League Table Ranking: 25th" text inside this attribute to a string in java:
<a href="/league-tables/rankings">
2017 League Table Ranking: 25th
</a>
public void findRanking() throws Exception {
String ranking = driver.findElement(By.xpath("(//a[contains(#href, '/league-tables/rankings')])")).getAttribute(href) ;
}
This gives me the link to the href that the attribute is using, and is the closest i've got to getting an output kind of right. I've tried simply getting the text of the element above, using the .getText() method but that returns nothing, where am i going wrong?

Since we now have a link to the page, I was able to create a unique locator. The problem was that there was more than one element that matched the locator and the first match wasn't the one you wanted. Since we don't need XPath here, I switched to a CSS selector. CSS selectors have better browser support, are faster, and I think are easier to create. This should work now.
public String findRanking() {
return driver.findElement(By.cssSelector("p.league-table-latest-rank > a[href='/league-tables/rankings']")).getText();
}
Here are some references for CSS selectors. I would suggest that you spend some time learning them. They are extremely powerful and should be your goto locator after By.id().
W3C CSS Selector Reference
Sauce Labs CSS Selector Tips
Original answer with XPath
.getText() should work on that element. It looks like you have an extra set of () in your XPath. Also, you should be able to use equals instead of contains(). There may be other links that contain that partial href that may be causing issues. I would try the below. I added a return and changed the return type to String.
public String findRanking() {
return driver.findElement(By.xpath("//a[#href='/league-tables/rankings']")).getText();
}
You can test your XPath in Chrome. Open the page and try $x("//a[#href='/league-tables/rankings']").length in the dev console and make sure it returns 1. If it's 0 then something is wrong with the locator. If it's > 1 then you'll have to further narrow the focus of your locator. Find a unique parent that has an ID or something unique.

Try this:
public String findRanking(){
WebElement e = driver.findElement(By.xpath("//a[contains(#href, '/league-tables/rankings')]");
return e.getText();
}

Try this way.
If you want to get the text of 2017 League Table Ranking: 25th then use below code.
String test = driver.findElement(By.xpath("//a[contains(text(), '2017 League Table Ranking: 25th')]")).getText();
System.out.println(test);
If you want to get the href attribute of this text 2017 League Table Ranking: 25th then use below code.
String href = driver.findElement(By.xpath("//a[contains(text(), '2017 League Table Ranking: 25th')]")).getAttribute("href");
System.out.println(href);

Related

Select parent of XCUIElement

How can I select the parent element of an XCUIElement in XCUITest? According to the documentation, the class has children() and descendants() but nothing to select parents or siblings. It seems to me I must be missing something - how can Apple have an element tree without navigation in both directions???
I know there is a method containing() on XCUIElementQuery but that is not the same. I also know that an accessibilityIdentifier might help but I am thinking of writing a generic method for testing any view with a given Navbar label. Passing in all the identifiers of all the elements I would like to access does not seem like a good option.
Unfortunately there is no direct named method to access parent elements similar to children() and descendants() provided by Apple but you were actually on the right track with containing(). There are two ways I usually approach it when I need to locate a parent element based on children/descendants:
Using containing(_:identifier:)
let parentElement = app.otherElements.containing(.textField, identifier: "test").firstMatch
or
let parentElement = app.otherElements.containing(.textField, identifier: "test").element(boundBy: 0)
Using containing(_ predicate: NSPredicate)
let parentElement = app.otherElements.containing(NSPredicate(format: "label CONTAINS[c] 'test'").firstMatch
or
let parentElement = app.otherElements.containing(NSPredicate(format: "label CONTAINS[c] 'test'").element(boundBy: 0)
These are just examples with random data/element types because you didn't mention exactly what you want to achieve but you can go from there.
Update:
As usual the Apple documentation doesn't do a good service. They say 'descendants' but what they actually mean is both direct descendants(children) and non-direct descendants(descendants). Unfortunately there is no guarantee and there is no generic solution. It should be based on your current needs and the application implementation. More examples that could be useful:
If you don't want the first element from the query you are better off using element(boundBy: index). So if you know that XCUIElementQuery will give you 5 elements and you know you need the 3rd one:
let parentElement = app.otherElements.containing(.textField, identifier: "test").element(boundBy: 2)
Fine graining of your element locators. Lets say you have 3 views with identifier "SomeView", these 3 views each contain 2 other subviews and the subviews have a button with identifier "SomeButton".
let parentViews = app.otherElements.matching(identifier: "SomeView")
let subView = parentViews.element(boundBy: 2).otherElements.containing(.button, identifier: "SomeButton").element(boundBy: 1)
This will give you the second subview containing a button with identifier "SomeButton" from the third parent view with identifier "SomeView". Using such an approach you can fine tune until you get exactly what you need and not all parents, grandparents, great-grandparents etc.
I wish Apple provided a bit more flexibility for the locators with XCTest like Xpath does for Appium but even these tools can be sufficient most of the time.

Need to get the 2sxc Field Type from an Entity

After getting everything working on this previous question Is there a way to Clone one or many Entities (records) in Code, I wanted to clean it up and make it more useful/reusable. So far, I am deciding how to copy/add the field using the content-type field names, so attribute.Key inside the foreach on Attributes. What I need instead is to know the Entity field's Type; meaning String, Number, Hyperlink, Entity, etc.
So I want something like if(AsEntity(original).FieldType == "HyperLink") { do this stuff }. I have explored the API docs but have not spotted how to get to the info. Is it possible?
I did figure out that the attribute.Value has a Type that I could use to answer most of them, but Hyperlink and String are both showing, System.String.
Here are, in order, String, Hyperlink, Entity, and Number:
atts: ToSic.Eav.Data.Attribute`1[System.String]
atts: ToSic.Eav.Data.Attribute`1[System.String]
atts: ToSic.Eav.Data.Attribute`1[ToSic.Eav.Data.EntityRelationship]
atts: ToSic.Eav.Data.Attribute`1[System.Nullable`1[System.Decimal]]
So is there a way from the Entity or its Attributes or some other pathway of object/methods/properties to just get the answer as the field Type name? Or is there a wrapper of some kind I can get to that will let me handle (convert to/from) Hyperlinks? I am open to other ideas. Since the fields.Add() is different by "FieldType" this would be really helpful.
It's kind of simple, but needs a bit more code because of the dynamic nature of Razor. Here's a sample code that should get you want you need:
#using System.Collections.Generic;
#using System.Linq;
#using ToSic.Eav.Data;
var type = AsEntity(Content).Type;
var attributes = type.Attributes as IEnumerable<IContentTypeAttribute>;
var typeOfAwards attributes.First(t => t.Name == "Awards").Type; // this will return "Entity"
I created a quick sample for you here: https://2sxc.org/dnn-tutorials/en/razor/data910/page

How to Retrieve Values from a Class Within a Class Using JavaScript

I am not experienced with HTML and 'JavaScript', and is having a roadblock when attempting to check the values from a class.
Below is the source as seen from F12
I would like to retrieve all createdby text -muted values (as they may contain more than one row) to check if any of them matches SYSTEM, may I know how can it be done?
I understand that an image is not the best way to portrait my question, I will try to type the source in my question.
My apologies, and thank you.
You can get NodeList which contains createdby text-muted classes using document.querySelectorAll as follows.
const elems = document.querySelectorAll(".createdby .text-muted");
elems.forEach((item) => {
console.log(item.innerHTML); // This will contain the text value of the selected selector
});

Html selector using Regex

So there is a page that I want to perform some action on with puppeteer. The problem is that there is a text area in which I want to type in something however the id of it is :
id="pin-draft-title-13a10e18-5a1e-49b9-893c-c5e028dc63e1"
As you might have guess for some reason only pin-draft-title remains the same but the whole number part changes for every refresh so puppeteer can't find it. I tried deleting the id and copying the selector itself the whoe #_Root div>div etc but that seems to be changing after sometime as well. So the main question is is there any way i can just select it using the pin-draft-title part and no matter what numbers follow it still selects it ?
You can use [id^=pin-draft-title-]
In case of javascript, if you want to select all the elements whos ID starts with a specified string or pattern, in your case "pin-draft-title", then consider using the following syntax.
document.querySelectorAll('[id^="pin-draft-title"]');

How to find the index of HTML child tag in Selenium WebDriver?

I am trying to find a way to return the index of a HTML child tag based on its xpath.
For instance, on the right rail of a page, I have three elements:
//*[#id="ctl00_ctl50_g_3B684B74_3A19_4750_AA2A_FB3D56462880"]/div[1]/h4
//*[#id="ctl00_ctl50_g_3B684B74_3A19_4750_AA2A_FB3D56462880"]/div[2]/h4
//*[#id="ctl00_ctl50_g_3B684B74_3A19_4750_AA2A_FB3D56462880"]/div[3]/h4
Assume that I've found the first element, and I want to return the number inside the tag div, which is 1. How can I do it?
I referred to this previous post (How to count HTML child tag in Selenium WebDriver using Java) but still cannot figure it out.
You can get the number using regex:
var regExp = /div\[([^)]+)\]/;
var matches = regExp.exec("//[#id=\"ctl00_ctl50_g_3B684B74_3A19_4750_AA2A_FB3D56462880\"]/div[2]/h4");
console.log(matches[1]); \\ returns 2
You can select preceeding sibling in xpath to get all the reports before your current one like this:
//h4[contains(text(),'hello1')]/preceding-sibling::h4
Now you only have to count how many you found plus the current and you have your index.
Another option would be to select all the reports at once and loop over them checking for their content. They always come in the same order they are in the dom.
for java it could look like this:
List<WebElement> reports = driver.findElements(By.xpath("//*[#id='ctl00_ctl50_g_3B684B74_3A19_4750_AA2A_FB3D56462880']/div/h4")
for(WebElement element : reports){
if(element.getText().contains("report1"){
return reports.indexOf(element) + 1;
}
}
Otherwise you will have to parse the xpath by yourself to extract the value (see LG3527118's answer for this).