Handle elements that have changing ids - java

I am running the script to automate test cases and found that id's keep on changing. Below is my HTML code
Firebug for test drive:
<button class="G0036HC-b-a G0036HC-b-o G0036HC-b-g" id="gwt-uid-470" tabindex="0" aria-labelledby="gwt-uid-470" role="button" type="button"><div class="G0036HC-b-j">Click to continue</div></button>
Inspector:
<button class="G0036HC-b-a G0036HC-b-o G0036HC-b-g" id="gwt-uid-320" tabindex="0" aria-labelledby="gwt-uid-320" role="button" type="button"><div class="G0036HC-b-j">Click to continue</div></button>
Only id's changes.
Any Help would be appreciated.

You can get the element by xpath and check that id attribute starts with gwt-uid-:
//button[starts-with(#id, "gwt-uid-")]

Besides, the answer #alecxe suggested I would also suggest you to try with text based xpath search. I often faced issue with wait so also suggest to use explicit wait this.
// //div[.='Click to continue']/..
By byXpath = By.xpath("//div[.='Click to continue']");
WebElement myDynamicElement = (new WebDriverWait(driver, 10)).until(ExpectedConditions.presenceOfElementLocated(byXpath));
// myDynamicElement.click();

Considering the following observations, the below code should work fine.
All the buttons on the page are prefixed with gwt-uid- with changing number suffix
If the buttons on the page have a unique text then use
By.xpath("//button/div[text()='Click to continue']")
If there are multiple buttons with same text then use indexes. eg. two close buttons with same text then use,
By.xpath("(//button/div[text()='Close'])[2]") # for the 2nd occurence, mind that selenium is not zero indexed

use following...
//input[#id=’’]/following::input[1]
You can use preceding in the same way to identify the child node

Related

Selenium | Unable to locate input element

No idea how to address this input text field element with selenium / Java (openjdk 11 2018-09-25).
I tried xpath, cssSelector etc. it never works. Its always "Unable to locate element".
<slot name="input">
<input part="value" tabindex="0" aria-labelledby="vaadin-text-field-input-3">
</slot>
This did NOT work:
driver.findElement(By.xpath("//input[#aria-labelledby='vaadin-text-field-input-3']")).sendKeys("test");
Is there a solution for this?
UPDATE:
This thread solved partly my problem. The solution that worked for me can be found here link.
To send a character sequence to the element you need to induce WebDriverWait for the elementToBeClickable() and you can use either of the following locator strategies:
css_selector:
new WebDriverWait(driver, 20)
.until(ExpectedConditions.elementToBeClickable(
By.cssSelector("div.vaadin-text-field-container div[part=input-field][id^='vaadin-text-field-input'] slot[name='input'] > input[part='value'][aria-labelledby^='vaadin-text-field-input']")
)).sendKeys("pixelhead");
xpath:
new WebDriverWait(driver, 20)
.until(ExpectedConditions.elementToBeClickable(
By.xpath("//div[#class='vaadin-text-field-container']//div[#part='input-field' and starts-with(#id, 'vaadin-text-field-input')]//slot[#name='input']/input[#part='value' and starts-with(#aria-labelledby, 'vaadin-text-field-input')]")
)).sendKeys("pixelhead");
Xpath:
//div[#class='vaadin-text-field-container']//descendant::input[#part='value' and starts-with(#aria-labelledby, 'vaadin-text-field-input')]
Please check in the dev tools (Google chrome) if we have unique entry in HTML DOM or not.
Steps to check:
Press F12 in Chrome -> go to element section -> do a CTRL + F -> then paste the xpath and see, if your desired element is getting highlighted with 1/1 matching node.
If this is unique //div[#class='vaadin-text-field-container']//descendant::input[#part='value' and starts-with(#aria-labelledby, 'vaadin-text-field-input')] then you need to check for the below conditions as well.
Check if it's in any iframe/frame/frameset.
Solution: switch to iframe/frame/frameset first and then interact with this web element.
Check if it's in any shadow-root.
Solution: Use driver.execute_script('return document.querySelector to have returned a web element and then operates accordingly.
Make sure that the element is rendered properly before interacting with it. Put some hardcoded delay or Explicit wait and try again.
Solution: time.sleep(5) or
WebDriverWait(driver, 20).until(EC.visibility_of_element_located((By.XPATH, "//div[#class='vaadin-text-field-container']//descendant::input[#part='value' and starts-with(#aria-labelledby, 'vaadin-text-field-input')]"))).send_keys("test")
If you have redirected to a new tab/ or new windows and you have not switched to that particular new tab/new window, otherwise you will likely get NoSuchElement exception.
Solution: switch to the relevant window/tab first.
If you have switched to an iframe and the new desired element is not in the same iframe context then first switch to default content and then interact with it.
Solution: switch to default content and then switch to respective iframe.
You can start debugging from step1.
Update:
Solution specific to the problem:
Thread.sleep(2000);
WebElement inputButton = (WebElement) ((JavascriptExecutor)driver).executeScript("return document.querySelector('#TextFieldTitle').shadowRoot.querySelector('#vaadin-text-field-input-3 > slot:nth-child(2) > input')");
inputButton.sendKeys("test");
in the place of paste query selector here you will have to go to dev tools again in goog chrome by pressing F12, and then
Go to that input box
Do a right click
Select copy
Select copy JS path.
Ctrl + v into notepad to see what you've got from the dev tool.
It'd be something like this:
document.querySelector("#vaadin-text-field-input-3 > slot:nth-child(2) > input")
replace this paste query selector here with the stuff that is wrapped inside ""
Per the html provided, this works:
driver.find_element(By.XPATH, "//input[#part='value' and contains(#aria-labelledby, 'vaadin-text-field-input')]"]
x = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.XPATH, "//input[#part='value' and contains(#aria-labelledby, 'vaadin-text-field-input')]")))
x.send_keys('This is typed here by selenium')
print(f"the typed text in input box is: {x.get_attribute('value')}")
Output:
the typed text in input box is: This is typed here by selenium
Process finished with exit code 0
Looks like part of HTML looks like:
<div class="vaadin-text-field-container">
<label part="label" id="vaadin-text-field-label-3"></label>
<div part="input-field" id="vaadin-text-field-input-3">
<slot name="prefix"></slot>
<slot name="input">
<input part="value" tabindex="0" aria-labelledby="vaadin-text-field-input-3">
</slot>
</div>
</div>
Much better is to build your XPATH locator from some id field:
//div[#id='vaadin-text-field-input-3']//input[contains(#aria-labelledby, 'vaadin-text-field-input')]"]
Also, as mentioned earlier have to check:
if it is unique for this page
if the input element is not dynamic
If you found solved that comment you could move to the code part and use the founded locator with WebDriver:
driver.findElement(
By.xpath("//div[#id='vaadin-text-field-input-3']//input[contains(#aria-labelledby, 'vaadin-text-field-input-3')]"]"
)).sendKeys("test");
Also, keep in mind that you have to know that element is already loaded on a page. As suggested before you could use some explicit wait for your element.

Selenium WebDriver "java", I can not click on button on footer

The scenario is:
Try to add experience in linkedin.
Then click on save button to save the added experience.
The below is html code for this button:
<button class="pe-form-footer__action--submit form-submit-action Sans-15px-white-100%" type="submit">
Save
</button>
I am trying to find it by xpath using:
#FindBy (xpath = "//*[contains(text(), 'Save')]")
WebElement saveExperienceButton;
Following screenshot may help:
I will appreciate your help.
if you do not mind css/xpath selectors that do not look very elegant, you can always open up Chrome developer tools on the website you wanna test with Selenium, mark the DOM elements you wanna access and in the context menu choose 'Copy xpath' or 'copy selector':
Try this xpath:
(//*[text()='Save'])[2]
On my profile there are 2 Save buttons - the second one is the skill save. Also, you might want to check this question for the contains syntax.
Creating an XPath using text is a less preferable way. instead of that use other attribute value which is unique.
for ex: in your case
//footer//*[contains(#class, 'form-submit')]

How to get value from <h1> tag using class in Selenium WebDriver, Java

I have html code:
<div class="formCaptionContainer fill-width" data-dyn-bind="sizing: { width: $dyn.layout.Size.available }">
<h1 class="formCaption" data-dyn-bind="text: $data.Caption, click: $data.GoGridView">Expense report for Aditi Mehta - GS1-000282, testing2</h1>
<h2 class="formCaption-context" data-dyn-bind="text: $data.ParentTitleFields">Aditi Mehta : GS1-000282</h2>
</div>
I want to get the value Expense report for Aditi Mehta - GS1-000282, testing2 from /h1 tag
Any one know how to do it?
I've tried :
By.xpath(".//div[#class='formCaption']/h1";
Above showing no element found
By.className("formCaption");
Above showing blank data
By.xpath(".//*[#class='formCaption']/h1");
Above showing no element found
This code work for me very well
String textprint=driver.findElement(By.xpath("//h2[#class='formCaption-context']")).getText();
System.out.println(textprint);
Hope It will solve your problem
Try this one
String msg= driver.findElement(By.xpath(//div[contains(#class='formCaptionContainer')]/h1 ).getText();
Can you try the below Xpath.
String msg=driver.findElement(By.xpath("//div[#class='formCaptionContainer fill-width']/h1[#class='formCaption-context']")).getText();
As per the HTML you have shared, the node attributes looks dynamic to me. So we have to induce WebDriverWait as follows :
new WebDriverWait(driver, 10).until(ExpectedConditions.textToBePresentInElementLocated(By.xpath("//h1[#class='formCaption']"), "Aditi Mehta - GS1-"));
System.out.println(driver.findElement(By.xpath("//h1[#class='formCaption']")).getAttribute("innerHTML"));
You need to consider your locator strategy, the following provide good preferences for general case approach.
location by ID globally unique elements
locate by name for page unique elements
locate by css as catch all case
You should also take a look at the PageObject pattern.
It appears the page is using a JS library to display the contents through late binding/resolution. Your test needs allow for this and wait until the page elements you are interested in are fully rendered. Find out from the developers what the element looks like before it is resolved and afterwards. Late resolution is one considerations in determining your locator strategy. The strategy should include fluent waiting and default time-outs for elements to appear.
This is not a problem of fixing a single locator.
Thanks all for your help. It is working fine now. I use the below code
By headingExpenseText = By.xpath("//*[#class='formCaption'][contains(text(),'Expense report for')]");
public String getTitleEx()
{
return driver.findElement(headingExpenseText).getAttribute("innerHTML");
}
This worked for me:
String titleElem = driver.findElement(By.xpath("//*[#class='formCaptionContainer fill-width']/h1")).getAttribute("innerHTML");
assertEquals("Worked", titleElem);
What do you need exactly, Data displayed on web page or textValue used in HTML code?
If your locator is correct , then instead of getText(), you can try getAttibute("innerHTML") like below,
By.xpath("//*[#class='formCaption'][contains(text(),'Aditi Mehta')]").getAttribute("innerHTML");

How to click on a link with a span tag inside using selenium java

I am having a problem in clicking the link text given inside a span tag.
html code :
<div id="menu" style="width: 1752px;">
<div class="dd_menu" dd_event_id="dd_event_2">
<a class="dd_menu_menu_entry dd_menu_entry_clickable" href="javascript:void(0);" style="left: 3px; width: 111px;" dd_menu_id="0">
<a class="dd_menu_entry dd_menu_entry_clickable" href="javascript:void(0);" style="left: 114px; width: 131px;" dd_menu_id="1">
<span class="text" style="background-color: rgba(0, 0, 0, 0);">FirstMenu</span>
I need to click on the text 'FirstMenu' .
I have used the xpath : .//*[#id='menu']/div/a[2]/span
It does not seem to work. How do I fix it?
If your requirement is to "click on the link FirstMenu", then you should use that as the locator. No need to mess around with XPath.
driver.findElement(By.partialLinkText("FirstMenu")).click();
The .partialLinkText() locator strategy should account for any extra whitespace padding due to the extra span element.
Your xPath returns span element so you're clicking that span. To make your xpath return a link ament your query to the following:
//*[#id='menu']/div/a[span]
This query returns a "link" that has span element as a child.
Try to use below xpath :-
//span[contains(.,'FirstMenu')]
If it doesn't work then there may be any frame present. You need to switch it on first.
Please let me know if there is more element with name FirstMenu on DOM
Hope it will help you :)
The problem got solved by using the same xpath i specified above with the usual syntax driver.findElement(By.xpath("//*[#id='menu']/div/a[2]/span")).click(); after i gave the order by which testcases have to be executed and using #Test(priority=something) and giving some implicit waits.
Thank you all for the suggestions.
regards,
roma
It is not good to use xpath. If the html of the page is changed your code would stop working. Try with css selector.
This is is simple code and you can modify it for you case:
var collection = driver.getelementsBy(By.cssSelector('div#menu div'))
It should return you collection with elements
And after that you can iterate through collection and find the element you want to click.
Hope the answer helps you.

How to find element having dynamic ID / Name changing on every page load using Selenium WebDriver

I found a few answers about this but they did not answer my question an so I am writing a new question.
I have HTML code having below kind of checkbox elements (in browser's inspect element)
<input role="checkbox" type="checkbox" id="jqg_TransactionFormModel501EditCollection2_147354_grid_-1274" class="cbox" name="jqg_TransactionFormModel501EditCollection2_147354_grid_-1274" value="true">
In my test case I want to click on checkbox using its ID using Selenium Webdriver.
here Id= "jqg_TransactionFormModel501EditCollection2_147354grid-1274" is dynamic.
in above id, Bold & Italic marked letters (dynamic) will change with different check boxes in same page as well as page refresh.
Bold marked letters (dynamic) will change on page refresh only (remain same through all the check boxes in same page.)
How shoud I format/write XPATH so that I can click on desired check boxes using below statement.
WebElement checkbox = webDriver.findElement(By.id("idOfTheElement"));
if (!checkbox.isSelected()) {
checkbox.click();
}
Thanks for your help in advance.. !
Here are a few examples of xpaths which you can use to find your checkbox
//input[contains(#id,'jqg_TransactionFormModel')]
OR, if you want more checks, try something like
//*[starts-with(#id,'jqg_TransactionFormModel') and contains(#id,'EditCollection2_')]
Additionally, you can try regex as well using matches
//*[matches(#id,'<regex matching your id>')]
You can use partial ID using cssSelector or xpath
webDriver.findElement(By.cssSelector("[id*='TransactionFormModel']"));
webDriver.findElement(By.xpath("//input[contains(#id, 'TransactionFormModel')]"));
You can replace TransactionFormModel with any other fixed part of the ID.
As a side note, no need to locate the element twice. You can do
WebElement checkbox = webDriver.findElement(By.id("idOfTheElement"));
if (!checkbox.isSelected()) {
checkbox.click();
}
You can write xpath like this :
//input[starts-with(#id,'jqg_TransactionFormModel')]
//input[contains(#id,'jqg_TransactionFormModel')]
I recommend not using ID's or xpath and adding a data attribute to your elements. This way, you can avoid the annoyance of xpath and have a strong selector that you feel confident will always be selectable.
For example, you could call the attribute: data-selector. You can assign a value of "transactionFormModelCheckbox". Then, when creating a new element, you create by css selector with the value referenced above.
No matter what type of formatting, you'll always be able to select that checkbox - as long as the element exists in the DOM. If you need to investigate other attributes, you can use the selector to do things like hasClass() or anything else you need.
Hope that helps !

Categories

Resources