I'm trying to get a list of IwebElements that contain the attribute aria-required. Reason why I'm trying to get this list, is so that i can check if all required fields are necessary for the user to fill out, before he can continue to the next page.
So far after 2 days of searching I'm still convinced that it shouldn't be that hard. I'm using the expression:
".//*[#aria-required='true']"
From my research that would mean that it will search for ALL the elements starting from the root of my webdriver.
[TestMethod]
public void CreateProjectWithoutRequiredFields()
{
GoToProjectPage();
tracking = CM.GoToNewlyCreatedFrameAfterClickButton("ftbNew", tracking, theDriver);
CM.Wait(2000);
bool succesSave = false;
CM.LogToFile("Create project whitout required fields", tracking);
foreach (IWebElement e in theDriver.FindElements(By.XPath(".//*[#aria-required='true']")))
{
FillDataInNewProjectPage();
e.Clear();
CM.GetDynamicElementById("btnSave", theDriver).Click();
try
{
CM.GetDynamicElementById("titel", theDriver).Click();
}
catch (Exception)
{
succesSave = true;
NUnit.Framework.Assert.IsFalse(succesSave, "The page is saved with succes, without entering text in the following required fields : " + e.GetAttribute("id").ToString());
}
CM.Wait(1000);
}
}
I will try to explain what i did here:
First i went to a overview page with all my existing projects. On this page i clicked the ftbNew button to create a new project. The driver is automatically switch to the correct frame (i now the right frame is selected because i used this line on other page's.)
then the line
foreach (IWebElement e in theDriver.FindElements(By.XPath(".//*[#aria-required='true']")))
{
should normaly find a the elements in my driver with an attribute "aria-required='true'"
Then it would fill in the page with data, clear the first element that is found from its data en try to save it.
if the element titel is found on the page, than we are still on the same page en the save action wasn't successful ( <- so this is good)
so next we again overwrite every field on the page and clear this time the second element that is found.
en so on...
What I'm guessing, that xpath has difficulty finding the 'old' aria-required attribute... When i try to validate my expression using firebug and other xpath checkers, the aria-required attribute isn't always present. Sometimes it finds the input field sometimes it doesn't.
source code page
As you can see in the firebug console not all attributes are loaded, account-manager has a aria-required attribute, but project leader doesn't. If i inspect the element again, but this time click project leader. The attribute will be loaded. Very strange....
Extra info: I am using frame's and i know a lot can go wrong if you are situated in the wrong frame, but i am sure that he is looking in the correct frame. Especially because i can't find the elements using firebug with the above expression. If i change my expression to .//input, it will find the elements but also selects input fields that aren't required.
In advance i want to thank everybody that want to look into my problem :)
Adriaan
Based on your description of the behavior on the page, you cannot rely on the aria-required attribute to indicate a required field. I think that you should go back to the developers of the site to ask them to give you a reliable handle. It may be as simple as looking for the "*" as the last character of the label associated with the input field, but that's kind of annoying to have to deal with.
As an aside, you should consider catching a more specific exception for your success case. Right now, if ANY exception happens, you'll declare success, but that may not be what you really want to do.
Related
Im trying to find a element in a page that posses a considerable number of the same element, the difference between the elements is one ID with numbers in the final of the name. for example:
The only one visible when the page is loaded: limit_arrown_btn_3201
But this number in the end of the ID changes when some action are aplicated in the system.Because of that I dont have a way to maping the elements to send the click event, because whent the element is changed the last numbers of ID is alterated, crashing my scripts...
I have tried to use CssSelector, but all another caracteristics like Names and Classes is exactly the same.
Did some one haved experienced the same situation and could help me?
Thanks a lot.
You can use partial id, contains *= or start with ^=
By.CssSelector([id*='limit_arrown_btn'])
At work, we run into this situation all the time. For us, this usually happens as a result of extJS dynamically renaming elements on us. To get around it, I use xpath and usually move up the DOM a few levels to find a unique element. Then, with the console open (Chrome is my browser of choice here), I click on and off the element that is dynamically changing. Chrome will highlight what attributes are changing when this happens. This way, you can see what does and does not change and tailor your xpath to match. For instance, we have an application that generates tables using randomly generated IDs. I built a utility class in which you pass in a WebElement that represents the whole table wrapper (header, body, footer, etc.) and a String value of the header column that you want to look at. From that, I get the dynamic ID for the column of the table: (Forgive the examples, as they are in Java and not C#)
public String getGridColumnFromTable(WebElement tableDivElement, String headerColumn) {
WebElement newElement = waitUntilElementIsClickable(tableDivElement);
WebElement contextContainer = null;
String headerXPath = ".//child::div[contains(#id,'headercontainer')]/child::div/child::div/child::div/child::div/span[.='" + headerColumn + "']";
WebElement headerGridColumn = newElement.findElement(By.xpath(headerXPath));
waitUntilElementIsClickable(headerGridColumn);
String contextContainerXpath = ".//parent::div/parent::div";
contextContainer = headerGridColumn.findElement(By.xpath(contextContainerXpath));
String gridColumnID = contextContainer.getAttribute("id");
waitForPostBack();
return gridColumnID;
}
Once I have that dynamic gridColumnID, I use that to tie all the other functions to it, like this:
public String getCellTextInTableByRowAndColumnName(WebElement tableDivElement, int rowNumber, String headerColumn) {
WebElement newElement = waitUntilElementIsClickable(tableDivElement);
String tableGridColumnId = getGridColumnFromTable(newElement, headerColumn);
return newElement.findElement(By.xpath(".//tbody/tr[contains(#data-recordindex, '" + rowNumber + "')]/td[contains(#class, '" + tableGridColumnId + "')]//div")).getText();
}
While this example may be a bit foreign to you, since your project is most likely very different, it does show how to work around the problem by traversing the DOM to find the element you're after.
If you have a few html snippets you could post, it would be most helpful in tailoring an answer.
I'm trying to create this global method that click on this link called "Categories". The ID (t_166) is dynamic, Xpath (//*[#id="t_166"]) and "copy selector" (#t_166) use the id number also so they won't work.
So, I'm left with the html:
I figured class would be a good candidate. So... to start:
var categorymenu = driver.instance.findelement(by.classname("fontMediumBigColorGrey navigatorLinkClicked z-label")
And then I want it to find the category one and click on it, something like:
categorymenu.getattribute(category).click();
Two problems.
Problem 1: The link's class changes depending if you've visited it previously, or the "linkclicked" part in it. It becomes "fontMediumBigColorGrey z-label" if you haven't been on it. Question: it won't be able to find categories if the class is different. How would I handle this?
Problem 2: There are many other links (like users) that use the same classes, so shouldn't I be using findelements and then isolate it by an attribute (category is this case) But findelements doesn't seem to be able to use getattribute (because there are many of them) so how do I cover that part?
Thanks!
You can use search by XPath to find your element:
var categorymenu = driver.instance.findelement(by.xpath("//span[text()='Categories']")
In code above you search for span element with "Categories" as its text value
Also you can try to ignore dynamically changing part of id attribute in following way:
var categorymenu = driver.instance.findelement(by.xpath("//div[starts-with(#id, 't_')][substring-after(#id, '-')='cave']/span")
Above code should search for span that is child of div with id="t_XXXX-cave" where XXXX is ignored part
Note You should also be aware that you will not be able to complete categorymenu.getattribute(category).click(); as categorymenu.getattribute(category) (actually categorymenu.GetAttribute(category)) returns just a string value
In case you want to see if the span has the fontMediumBigColorGrey class:
var categorymenu = driver.instance.findelement(by.xpath("//span[contains(#class, 'fontMediumBigColorGrey')]")
In case you want to see if the text is equal to "Categories":
var categorymenu = driver.instance.findelement(by.xpath("//span[text()='Categories']")
A trick that I sometimes use, and could be useful for you too - if you're using Chrome, open the console and edit the HTML in such a way that you delete the "id" tag. Then, right click and choose 'Copy > Copy XPath'. This will copy the XPath but neglect the ID (because you can't use it since it's dynamic).
I am using C# and Watin.
Imagine the following scenario:
I search for an element using it's innerHTML/Find.ByText.
return Document.Element(Find.ByText(Localisation.CommandHeader));
CommandHeader.SetAttributeValue("Innerhtml", "Save Template");
CommandHeader.SetAttributeValue("onclick", "..");
I then change the elements text, and want to add a onclick event on it using Element.setAttributeValue.
Now Watin throws an Exception, telling me it can't find the element with criteria Innerhtml(..).
Saving the Element to a local variable did not help.
What I also tried, is adding CommandHeader.Refresh() between the two commands.
By the time of writing I came up with the idea of first assigning an id to the element, and then access it by only using that as selector afterwards. However, I didn't test it yet.
Is there a better way? Any help appreciated, thank you.
So I have a very simple Coded UI Test in Visual Studio 13' that simply visits my web page, logs in, and then clicks on a menu item that directs it to a table. During different times, that table may be empty or may be full of data. All I want to do is simply grab the 'id' attribute of the table and make assertions about its row count. Using C#, what is the easiest way to do this? I've researched and it looks like HtmlControl(s) are a possible solution (like the example here) but I can't seem to get the result I want. Thanks!
You can use this:
public HtmlEdit TbxUserName
{
get
{
if ((tbxUserName == null))
{
tbxUserName = new HtmlEdit(browser);
tbxUserName.SearchProperties[HtmlControl.PropertyNames.Id] = "UserName";
}
return tbxUserName;
}
}
There can by problem in webforms if you have generated ids, but it can be done with 'contains':
divMarketMap.SearchProperties.Add(new PropertyExpression(HtmlControl.PropertyNames.Id, "someId", PropertyExpressionOperator.Contains));
Ok I need to change the value of a hidden field in a gridview and here is what I have so far:
for(var i = 0; i < gv_Proofs.rows.length; i++)
{
var tbl_Cell = gv_Proofs.rows[i].cells[0];
var sdiFound = false;
for(var x = 0; x < tbl_Cell.childNodes.length; x++)
{
if(tbl_Cell.childNodes[x].id == "_ctl0_MasterContentPlaceHolder_gv_Proofs__ctl2_lbl_SDI")
{
if(tbl_Cell.childNodes[x].innerHTML == sdi)
sdiFound = true;
}
if(tbl_Cell.childNodes[x].id == "_ctl0_MasterContentPlaceHolder_gv_Proofs__ctl2_lbl_Updated" && sdiFound)
tbl_Cell.childNodes[x].value = "true";
}
}
can anyone tell me what I am doing wrong? Thank You!
I got it working. The above loop was working just right but apparently my value of sdi was not always getting set right, and therefore the value I was checking was always set to false. So the above worked perfectly in my case if anyone ever has this issue again.
Your first statement is only true if the childNode id is (truncated) lbl_SDI.
Your second statement is only true if the first statement is true (through sdiFound) AND the ID is (truncated) lbl_Updated.
So there's no way for the second statement to ever be true. The variable sdiFound will only be true when the node id isn't lbl_Updated.
I think a lot of people probably disagree with you referencing the .NET ID specifically as well. Have you considered using:
<%= lbl_SDI.ClientID %>
in your javascript?
well, can you tell what is happening? it seems like you are forgetting to reset the sdiFound variable to false, or breaking off the loop when you do find it.
as far as your question:
if you run the hidden html input as a server control, then it will be displayed by the GridView and it will have a unique id. you could then find it by using getElementById. You can put this inside a loop that builds the right id for you and you should be able to find your control.
Edit: classic case of check before you post. Apparently setting hidden=true actually prevents databinding which will be why the JS is having issues. I would say this is one of those typical problems with ASP.NET which make me curse it's very name, but you might just choose to adapt the control with one of the many googleable solutions.
Comments about using element ID in ASP.NET left intact because it's evil.
Hard to know without more code (when is this executing exactly? is something else overwriting it? what does the markup look like?) but I can say right now that referencing an element by it's ASP.NET deformed ID is a Bad Idea(TM).
If you can I suggest you try and modify this in the codebehind where the reference is handled for you, or at least write out the JS there where you can use clientID. Failing that I would try and find the element through an expanded element hunter - your own or a library util like jquery et al's "$" function - which will allow you to use a robust CSS or Xpath argument instead.