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).
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 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.
I'm making an autocad plugin and i want to create a new entity that is a combination of a line and the text.If i select the line the text is selected and backwords when i delete the line delete the text etc etc.How to treat them as one object referencing eachother?Is this possible?
I recommend using groups. Below is a link on how to access groups, I'm sure that site has more information on creating groups.
Users can control whether objects are selected with the group based on the system variable, PICKSTYLE. you can use ctrl+ h to toggle the PICKSTYLE value.
http://adndevblog.typepad.com/autocad/2012/04/how-to-detect-whether-entity-is-belong-to-any-group-or-not.html
Another option - though it doesn't answer your question - and this is something for you to think about: is to create a new block which consists of the line and some text. The line can be an entity in your block, and the text can be a tag string value. the tag can be called "line_information".
I know this might be abit too late but there is a better more flexible way to do this, although it's not actually combining the two entities but rather more of a visual effect.
It's called using Overrules. Basically you change the way the entity is displayed. So instead of displaying a line, you can display a circle, or in your case display a text and a line.
Overruling is a very powerful tool, you can not only change how the entity looks but also add grips, remove grips, change how entity is highlighted or highlight other entities when your entity is highlighted, override some of the entity methods like erase and much more.
Best place to start from is Kean Walmsley's "Through the Interface" blog.
And here is a link to this blog related to what you want to achieve
http://through-the-interface.typepad.com/through_the_interface/2009/08/a-simple-overrule-to-change-the-way-autocad-lines-are-displayed-using-net.html
I'm currently writing a function to save and read data to/from and XML document through
LINQ. Currently I can write the document just fine, but if I go to add data to an existing item, it simply adds a new item. My goal is to create an address book type system (yes I know there's 1000 out there, it's just a learning project for myself) and I've tried ini and basic text but it seems that XML is the best way to go short of using a local DB like sql. Currently I have:
XDocument doc = XDocument.Load(#"C:\TextXML.xml");
var data = new XElement("Entry",
new XElement("Name", textBox1.Text),
new XElement("Address", richTextBox2.Text),
new XElement("Comments", richTextBox1.Text));
doc.Element("Contacts").Add(data);
doc.Save(#"C:\TextXML.xml");
I searched SO and can't seem to find how to append/replace.
Now this saves everything properly, even when I add to the document, but if I want to update an entry I'm not sure how to without creating a new "Entry" nor am have I gotten the knack of removing one. (I'm somewhat new to C# still and self-taught so pardon anything obvious I've overlooked.)
My second issues revolves around loading the information into textboxes.
I'm able to load a list of Entry names into a listbox, but when I go to open the information from that entry I'm not sure how to properly get the nested info.
With the example above I'd need something similar to the following:
XDocument doc = XDocument.Load(#"C:\TextXML.xml");
boxName.Text = The name from the SelectedItem of the list box.
boxAddress.Text = The address child of the element named above etc.
Each method I've tried I wind up with a null reference exception, which tells me I'm not pointing to the right thing, but I'm not sure how to get to those things properly.
I've also tried creating a string and var of the SelectedItem from the list box to help with the naming, and using ToString methods, but still can't figure it out.
For replacing values, there are several functions you can use in XElement:
Value (property with a public setter)
SetValue()
SetElementValue()
SetAttributeValue()
ReplaceWith()
ReplaceNodes()
For example, if you wanted to replace the value in Name:
data.Element("Name").SetValue("NewValue");
or
data.Element("Name").Value = "NewValue";
For loading, once you have the XElement node you desire, then it's as simple as doing
xelement.Value
Or if it's an attribute:
xelement.Attribute("AttributeName").Value
Using your code as an example:
boxName.Text = doc.Element("Entry").Element("Name").Value;
Edit to address comment:
If I'm reading your comment right, you're wanting to extract the Name/Address/etc. data from all the nodes within the Contacts main node?
If so, then you would probably want something like this:
boxName.Text = string.Join(",", doc.Elements("Entry").Select(x => x.Element("Name").Value));
This will give you a single string that has all the names in all Entries separated by a comma. Just change "Name" to "Address" to do the same for addresses.
I'd suggest doing a search for Linq to XML for finding more information about how to use this parsing.
I'm working on a project where the user can insert data into a document using fields, document properties and variables. The user also needs to be able to remove the data from the document. So far, I've managed to remove the document property and variable, but I'm not sure how I would go about removing the field (that's already inserted into the document). Note that I need to compare the field to a string, and if it matches; delete it from the doc.
I'm assuming you're using .NET Interop with Word. In that case, I believe you're looking for Field.Delete.
This is of course also assuming you know how to get the field you're looking for, which would usually be enumerating through _Document.Fields (or a more finite range if you know one) until you get the right one.
The Field has a Delete method. See the documentation for Field.Delete.
So I think something like this would work:
foreach(Field f in ActiveDocument.Fields)
{
f.Select();
if(f.Type == TypeYouWantToDelete)
{
d.Delete();
}
}