I am trying to automate Salesforce lightning using Selenium, but getting issues with identifying elements. Reason, its having dynamic IDs , and other attributes are either very long , or they are not unique.
For eg ,
<a id="170:1968;a" class="textUnderline outputLookupLink slds-truncate forceOutputLookup"
data-refid="recordId"
data-recordid="0059E000001aOCSQA2"
data-special-link="true"
href="#/sObject/0059E000001aOCSQA2/view"
target="_blank" rel="noreferrer"
title="" data-aura-rendered-by="170:1968;a" data-aura-class="forceOutputLookup"/>
In above code , ID is dynamic , Class is not unique, and all the Lookup elements are associated with it. Also the absolute path is not much trusted , and hence I am trying to find any concrete option to handle these elements. Any help will be highly appreciated.
Here, you could try using the contains method if at least a part of the id attribute value is static.
From your code, you could try
//a[contains(#id,"a")]/ //--extended xpath--
From the given html code, the 'a' in the id attribute of the a tag looks static, while the rest changes.
You can ask the developers to provide an id to the lightning component using aura:id
Then the dynamic id won't be generated.
You can try with field labels and fetch its parent node(s), and then fetching childs or brother nodes to locate related texts/text boxes etc.
Eg. You are in Account Edit/New page, and you want to fill in a value to the text box for Account Name field. So you can firstly try with //*[text()='Account Name']/parent::* to find an element that covers BOTH the field label and the text box.
And then you can check if the text box is a 'brother' or a 'child'. If it's a 'child' then try with //*[text()='Account Name']/parent::*(/parent::*)//*[attributes for the text box];
If it's 'brothers' then try with //*[text()='Account Name']/parent::*(/parent::*)/following-sibling::*[attributes for the text box]
You can use this logic to locate all type of fields in all standard lightning pages.
Related
I was testing my website using RF. The problem is, every time the modal is opened, a different id(locator) will be set on the textbox that I want to input my text. How do you get value of this locator?
I was supposed to try Get Element Attribute but then it cannot support my problem since it still requires a specific locator.
In ROBOT Framework (RF), the locator can be accessed by several ways. Please refer and read this link: http://robotframework.org/Selenium2Library/Selenium2Library.html
The most common way to access the locator is by id such as :
Input Text id:username # Element with id 'username'.
Input Text id:password # Element with id 'password'. you can also use 'Input Password' keyword.
However, if the 'id' element is so dynamic which it keep changing, then the best alternative is to use either ABSOLUTE XPATH expression or CSS selectors. Install the XPATH add-on in your web browser. For firefox, just install ChroPath.
Then, get the ABSOLUTE Xpath element of that username & password text box. Let's assume we know the absolute xpath expression already, so in ROBOT, you can write like below.
${login_absolute_xpath}= Set Variable xpath=/html[1]//div[7]/form[1]/div[1]/input[1]
${password_absolute_xpath}= Set Variable xpath=/html[1]//div[7]/form[1]/div[2]/input[1]
Wait Until Page Contains Element xpath=${login_absolute_xpath}
Input Text xpath=${login_absolute_xpath}
Input Text xpath=${password_absolute_xpath}
...
This should works. Please let me know if this helps.
I am using Selenium WebDriver and I have number of items on a page and each item on page is a separate form type.
I have saved all of these form elements in a list and I am iterating over every item in an attempt to get the name of the element by using the "alt" attribute.
However when I try to get the "name" attribute from the input element it is always returning the first input tag found on that page, not the name attribute of the element I have currently selected.
The syntax I am using is:
((Webdriver imgtags.get(i)).findelement(By.xpath("//input[#name='qty']")).sendKeys ("100");
I have also tried to get the id from the tag by using:
((Webdriver imgtags.get(i)).getAttribute("id");
It's returning a blank value, but it should return the value of the id attribute in that input tag.
I also tried to get the id by using .bytagname but as id is an attribute it is not accessible
Try:
(driver) findElement(By.xpath("//*[contains(local-name(), 'input') and contains(#name, 'qty')]")).sendKeys("100");
To answer the comment by #rrd: to be honest, I have no idea why OP uses ((Webdriver imgtags.get(i)). I don't know what that is. Normally, I just use driver.findElement[...]
Hoping that he knows what works in his framework :D
Selenium Xpath handling is not fully compliant and it does not always treat // as a synonym of descendant-or-self.
Instead try tweaking your code to use the following Xpath:
((Webdriver imgtags.get(i)).findElement(By.xpath("./descendant-or-self::input[#name='qty']")).sendKeys("100");
This will base your search off the currently selected WebElement and then look for any descendants that have a name attribute with a value of "qty".
I would also suggest storing your imgtags array as an array of WebElement e.g.
List<WebElement> imgtags = new ArrayList<>();
This is a much better idea than casting to WebDriver to be able to use .findElement(). This will cause you problems at some point in the future.
I have a page say profile.htm with dojo declarative form with id="myForm". I have another page say dashboard.htm having border layout with 3 content pane. I want to show the profile.htm in all these three contentPanes. But when I try this then it gives registration error because there will be three forms with same Id.
Is there any solution for this so that I can have same page (with same id) in many contentPanes?
You simply can't, it's because of the HTML spec:
The id global attribute defines a unique identifier (ID) which must be unique in the whole document.
See: https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/id
The same goes for dijit's, if there are many dijit's with the ID myDijit what would registry.byId('myDijit') return? Dijit has it's own method of assigning unique ID's just as long as you don't assign one.
I'm attempting to learn VBA by reading through someone's code and understanding what happens every step of the way. However, I'm confused at to what these two elements are:
What is a HTMLSelectElement?
What is a HTMLInputElement?
See the W3C HTML Specs:
HTMLSelectElement
HTMLInputElement
I assume they correspond to select and input HTML tags. A select tag is also called a drop-down list, and input tags can be used for multiple things (checkbox, radio button, text, password).
I'm trying to select an element given by:
/html/body[#id='someid']/form[#id='formid']/div[#id='someid2']/div[#id='']/div[#id='']/div[#id='']/table/tbody[#id='tableid']/tr[7]/td[2]
Now the html of that row I'm trying to select looks like this:
<tr>
<td class="someClass">some text</td>
<td class="someClass2">my required text for verifying</td>
</tr>
I need to check whether my required text for verifying exists in the page.
I used selenium.isTextPresent("my required text for verifying"); and it doesnt work
So now I tried with selenium.isElementPresent("//td[contains(text(),'my required text for verifying')]")
This works sometimes but occassionally gives random failures.
Tried with selenium.isElementPresent(//*[contains(text(),'my required text for verifying')]) too..
How do I verify this text on the page using selenium?
The problem is not with the page taking time to load. I took screenshots before the failure occurs and found that the page was fully loaded so that shouldnt be the problem.
Could someone please suggest any way to select this element or any way to validate this text on the screen?
Try locating it by CSS:
assertText(selenium.getText("css=.someClass2"), "my required text for verifying");
The above should give a better failure message than isElementPresent, but you can still use that with CSS locators:
assertTrue(selenium.isElementPresent("css=.someClass2"));
If there is an issue with the load times you could try waiting for the element to be present:
selenium.waitForCondition("var value = selenium.isElementPresent('css=.someClass2'); value == true", "60000");
Some other XPath locators that might work for you, if you prefer not to use CSS locators:
//td[contains(#class, 'someClass2')
xpath=id('tableid')/tr[7]/td[2]
xpath=id('tableid')/descendant::td[contains(#class, 'someClass2')][7]
I've never heard of selenium; but your initial XPath is unnecessarily fragile and verbose.
If an element has an id, it's unique; using such a long XPath just to select a particular element is unnecessary; just select the last element with the id. Further, I see that you're occasionally selecting xyz[#id=''] - if you're trying to select elements without id attributes, you can do `xyz[not(#id)] instead.
Assuming your initial XPath is basically correct, it would suffice to do something like this:
//tbody[#id='tableid']/tr[7]/td[2]
However, using a specific row and column number like that is asking for trouble if ever anyhow changes details of the html. Also, it's atypical to have id's on tbody elements, perhaps the table element has the id?
Finally, you may be running into space-normalization issues. In xml, multiple consecutive spaces are often considered equivalent to a single space, and you're not accounting for that. In particular, if the xhtml is pretty-printed and contains a line-break in the middle of your sought-after text, it won't work.
//td[contains(normalize-space(text()),'my required text for verifying')]
Finally, text() explicitly selects
child text nodes - so the above xpath won't select elements where the text isn't the immediate child of td (e.g. <td><b>my required text for verifying</b></td>) won't match. Perhaps you mean to look up the concatenated text vale of all descendents:
//td[contains(normalize-space(string(.)),'my required text for verifying')]
Finally, type conversion can be implicit in XPath, so string(.) can be replaced by . in the above, leading to the version:
//td[contains(normalize-space(.),'my required text for verifying')]
This may be slow on large documents since it needs to normalize the spaces and perform a string search for each td element. If you run into perf problems, try to be more specific about which td elements need to be inspected, or, if you don't care where the text occurs, try to reduce the number of "calls" to normalize-space by normalizing the entire doc in one go (e.g. via /*[contains(normalize-space(.),'my required text for verifying')]).