Is there a way in the latest version of Selenium DotNet Webdriver (2.22.0) to check to see if an element is visible before clicking/interacting with it?
The only way I've found is to try to handle the ElementNotVisible exception that occurs when you try to send keys, or click on it. Unfortunately this only occurs after an attempt to interact with the element has been made. I'm using a recursive function to find elements with a certain value, and some of these elements are only visible in certain scenarios (but their html is still there no matter what, so they can be found).
It's my understanding that the RenderedWebElement class is deprecated as well other variants. So no casting to that.
Thanks.
For Java there is isDisplayed() on the RemoteWebElement - as well is isEnabled()
In C#, there is a Displayed & Enabled property.
Both must be true for an element to be on the page and visible to a user.
In the case of "html is still there no matter what, so they can be found", simply check BOTH isDisplayed (Java) / Displayed (C#) AND isEnabled (Java) / Enabled (C#).
Example, in C#:
public void Test()
{
IWebDriver driver = new FirefoxDriver();
IWebElement element = null;
if (TryFindElement(By.CssSelector("div.logintextbox"), out element)
{
bool visible = IsElementVisible(element);
if (visible)
{
// do something
}
}
}
public bool TryFindElement(By by, out IWebElement element)
{
try
{
element = driver.FindElement(by);
}
catch (NoSuchElementException ex)
{
return false;
}
return true;
}
public bool IsElementVisible(IWebElement element)
{
return element.Displayed && element.Enabled;
}
It seems the current answer to this question is outdated: With WebDriver 3.13 both the Displayed and Enabled properties will return true as long as the element exists on the page, even if it is outside of the viewport. The following C# code works for WebDriver 3.13 (from this StackOverflow answer):
{
return (bool)((IJavaScriptExecutor)Driver).ExecuteScript(#"
var element = arguments[0];
var boundingBox = element.getBoundingClientRect();
var cx = boundingBox.left + boundingBox.width/2, cy = boundingBox.top + boundingBox.height/2;
return !!document.elementFromPoint(cx, cy);
", element);
}
There is a simple way to do that, follow below:
public bool ElementDisplayed(By locator)
{
new WebDriverWait(driver, TimeSpan.FromSeconds(timeOut)).Until(condition: ExpectedConditions.PresenceOfAllElementsLocatedBy(locator));
return driver.FindElement(locator).Displayed ;
}
You can use the following:
WebDriver web = new FirefoxDriver(;
String visibility = web.findElement(By.xpath("//your xpath")).getCssValue("display");
Related
I am trying to find the element of the field First Name on the page https://whitelabel.sandbox.array.io/signup?platform=v3. I tried searching by id, classname, name, cssSelector, etc. but none works. I even added waiter to ensure it is loaded well before I try to find the element. Same issue happens for all fields in the page. So, the issue is not unique to this field.
Tried this in Chrome and Firefox on Mac. The same code works well to find the username field in gmail.com page.
driver.get("https://whitelabel.sandbox.array.io/signup?platform=v3");
driver.manage().window().maximize();
WebDriverWait wait = new WebDriverWait(driver, java.time.Duration.ofSeconds(10));
WebElement selectFirstName = driver.findElement(By.name("firstName"));
//assertNotNull(driver.findElement(By.name("firstName")));
//assertNotNull(driver.findElement(By.xpath("//input[#name='firstName']")));
//driver.findElement(By.cssSelector("input[name='firstName']")).sendKeys("Thomas");
//driver.findElement(By.name("firstName")).sendKeys("Thomas");
//driver.findElement(By.xpath("//input[#name='firstName']")).sendKeys("Thomas");
//page.locator("[name='firstName']").type("Thomas");
//driver.findElement(By.cssSelector("input[name='firstName']")).sendKeys("Thomas");
//driver.locator("[name='firstName']").type("Thomas");
Error that I get is:
Exception in thread "main" org.openqa.selenium.NoSuchElementException:
no such element: Unable to locate element: {"method":"css
selector","selector":"*[name='firstName']"} (Session info:
chrome=103.0.5060.134)
Does anyone know what I need to do differently to be able to get the webElement?
The First Name field within the website https://whitelabel.sandbox.array.io/signup?platform=v3 is within a #shadow-root (open)
ShadowRoot in selenium4
As per the test implementation in ShadowRoot.java:
import static org.openqa.selenium.remote.Dialect.W3C;
import static org.openqa.selenium.remote.DriverCommand.FIND_ELEMENTS_FROM_SHADOW_ROOT;
import static org.openqa.selenium.remote.DriverCommand.FIND_ELEMENT_FROM_SHADOW_ROOT;
// Note: we want people to code against the SearchContext API, so we keep this class package private
class ShadowRoot implements SearchContext, WrapsDriver {
private final RemoteWebDriver parent;
private final String id;
ShadowRoot(RemoteWebDriver parent, String id) {
this.parent = Require.nonNull("Owning remote webdriver", parent);
this.id = Require.nonNull("Shadow root ID", id);
}
#Override
public List<WebElement> findElements(By by) {
return parent.findElements(
this,
(using, value) -> FIND_ELEMENTS_FROM_SHADOW_ROOT(id, using, String.valueOf(value)),
by);
}
#Override
public WebElement findElement(By by) {
return parent.findElement(
this,
(using, value) -> FIND_ELEMENT_FROM_SHADOW_ROOT(id, using, String.valueOf(value)),
by);
}
#Override
public WebDriver getWrappedDriver() {
return parent;
}
public String getId() {
return this.id;
}
private Map<String, Object> toJson() {
return singletonMap(W3C.getShadowRootElementKey(), id);
}
}
#titusfortner explains the same in their comment as:
The actual state is that the return value of that JavaScript changed in v96 of ChromeDriver in order to be w3c compliant. Selenium 3.141.59 can not parse this new return value. You can use getShadowRoot() in Selenium 4, or you'll be able to get a ShadowRoot instance returned from the JS in Selenium 4.1.
And I stand corrected, you need to cast to SearchContext interface.
Solution
To send a character sequence within the First Name you can use the following Locator Strategy:
Code Block:
driver.get("https://whitelabel.sandbox.array.io/signup?platform=v3");
WebElement element = new WebDriverWait(driver, Duration.ofSeconds(10), Duration.ofSeconds(10)).until(ExpectedConditions.visibilityOfElementLocated(By.tagName("array-account-enroll")));
SearchContext context = element.getShadowRoot();
WebElement firstName = context.findElement(By.cssSelector("input[name='firstName'][placeholder='Enter first name']"));
firstName.sendKeys("Supramanian");
Browser snapshot:
I'm new to Selenium, TestNG and Stackoverflow.
After sendkeys, I want to do some validation. If the validation is true, then the assert is true. I know this is not the right way to write the Assert method.
WebDriver driver;
#DataProvider(name= "testdata")
public static Object[][] loginData(){
return new Object[][]{{"username1", "123"}, {"username2", "4211"}};
}
#BeforeTest
public void configure(){
....
}
#Test(dataProvider = "testdata")
public void testmethod(String uname, String password){
WebElement usernameTextbox = driver.findElement(By.id("username"));
usernameTextbox.sendKeys(uname);
WebElement passwordTextbox = driver.findElement(By.id("username"));
passwordTextbox.sendKeys(uname);
driver.manage().timeouts().implicitlyWait(2, TimeUnit.MICROSECONDS);
Assert.assertTrue(if(usernameTextbox.contains("[a-zA-Z0-9]+") && passwordTextbox.contains("[0-9]+") == true));
PS: Any inputs will be appreciated.
Try implementing explicit wait in Your code. What that mean, is that You wait for some condition to be set, here is example how to manage this:
But my suggestion is that You assert if there are some error messages (labels, span, or whatever that appears saying something is wrong with email or pass)
So here is how I would do it:
WebDriver driver;
#DataProvider(name= "testdata")
public static Object[][] loginData(){
return new Object[][]{{"username1", "123"}, {"username2", "4211"}};
}
#BeforeTest
public void configure(){
driver = new WebDriver();
driver.manage().timeouts().implicitlyWait(2, TimeUnit.SECONDS); //this is wait which will wait until driver throws exception (that is not found eg."NoSuchElementException")
}
#Test(dataProvider = "testdata")
public void testmethod(String uname, String password){
WebElement usernameTextbox = driver.findElement(By.id("usernameID"));
usernameTextbox.sendKeys(uname);
WebElement passwordTextbox = driver.findElement(By.id("passwordID"));
passwordTextbox.sendKeys(password); //here is where You've sent wrong param
// if You know You will get error label or something use this
WebDriverWait wait = new WebDriverWait(driver, 10); //wait for max 10 sec, and wait for error element defined bellow
WebElement errorElement = wait.until(ExpectedConditions. presenceOfElementLocated(By.id("someErrorElementId"))); //(or ExpectedConditions.textToBePresentInElement(..)), see what better suites You
// If You're expecting error than use this assert
Assert.assertTrue(errorElement.isDisplayed(),"There should be error message!")
// but If You're expecting that there should not be any error than use this assert
Assert.assertFalse(errorElement.isDisplayed(),"There shouldn't be no error messages!")
}
tweak this code, but basicaly this is the logic.
So to try to answer the original question your code could look like below:
1. Using the getAttribute("value")
2. Building the assertion - you don't need to wrap the condition in an if as the contains() function will return true or false for you:
WebDriver driver;
#DataProvider(name= "testdata")
public static Object[][] loginData(){
return new Object[][]{{"username1", "123"}, {"username2", "4211"}};
}
#BeforeTest
public void configure(){
....
}
#Test(dataProvider = "testdata")
public void testmethod(String uname, String password){
WebElement usernameTextbox = driver.findElement(By.id("username"));
usernameTextbox.sendKeys(uname);
WebElement passwordTextbox = driver.findElement(By.id("username"));
passwordTextbox.sendKeys(uname);
driver.manage().timeouts().implicitlyWait(2, TimeUnit.MICROSECONDS);
Assert.assertTrue(usernameTextbox.getAttribute("value").contains("[a-zA-Z0-9]+") && passwordTextbox.getAttribute("value").contains("[0-9]+"));
HTH
As per your question just after invoking sendKeys() you want to do some assertions.
At this point it is worth to mention that when you invoke sendKeys() on a <input> node/tag/field the HTML DOM is not immediately updated with the value / characters which you have just entered in majority of the cases (of-coarse there are exceptional cases). Moving forward when you invoke click() or submit()on a <button> or similar <input> element, the associated onclick event of this <input> element updates the HTML DOM and the value / characters previously sent through sendKeys() are adjusted within the HTML DOM.
Unless the value / characters are not accommodated within the DOM Tree Selenium won't be able to interact with them.
As per your code block, you have populated the passwordTextbox field with the String value of uname as follows :
passwordTextbox.sendKeys(uname);
This value / characterset is still volatile and editable (can be overwritten/cleared/deleted) as follows :
passwordTextbox.clear();
passwordTextbox.sendKeys("Emma E");
Essentially, Assert methods can be invoked on text those are part of the HTML DOM. As an example you can use the following Assert for a Page Heading, Table Heading, etc :
Assert.assertTrue(if(pageHeaderElement.contains("[a-zA-Z0-9]+") && tableHeaderElement.contains("[0-9]+") == true));
how is possible to find the xpath for an angularJs element? for instance i discovered that all links in my page have the same xpath due to the repeated items in angularJs -->
.//*[#id='div_1_1_1_2']/div/div[1]/div[2]/div[2]/div/div[2]/div[1]/div/div[2]/a
but i have 10 of element , they are differente for text, so i tried with `"
so i tried with contains but it never find it
.//[#id='div_1_1_1_2']/div/div[1]/div[2]/div[2]/div/div[2]/div[1]/div/div[2]/a[contains(#aria-label='Creazione Prodotto')]"`
i use selenium, junit4 , firefox webDriver
this is my code
public class PB01_TTT {
private WebDriver driver;
private String baseUrl;
private boolean acceptNextAlert = true;
private StringBuffer verificationErrors = new StringBuffer();
WebElement element;
#Before()
public void setUp() throws Exception {
FirefoxProfile fxProfile = new FirefoxProfile();
fxProfile.setPreference("browser.download.folderList", 2);
fxProfile.setPreference("browser.download.manager.showWhenStarting", false);
fxProfile.setPreference("browser.helperApps.alwaysAsk.force", false);
fxProfile.setPreference("browser.helperApps.neverAsk.saveToDisk",
"application/pdf, application/x-pdf, application/octet-stream");
fxProfile.setPreference("pdfjs.disabled", true);
driver = new FirefoxDriver(fxProfile);
baseUrl = "https://w8aon2bpm.replynet.prv:9443";
driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
}
#Test
public void testPBO1TTT() throws Exception {
driver.get(baseUrl + "/ProcessPortal/login.jsp");
// driver.get(baseUrl + "/ProcessPortal/dashboards/SYSRP/RESPONSIVE_WORK");
driver.findElement(By.id("username")).clear();
driver.findElement(By.id("username")).sendKeys("user");
driver.findElement(By.id("password")).clear();
driver.findElement(By.id("password")).sendKeys("password");
String columnToDisplay=driver.findElements(By.xpath(".//*[#id='div_1_1_1_2']/div/div[1]/div[2]/div[2]/div/div[2]/div[1]/div/div[2]/a[contains(#aria-label='Creazione Prodotto')]"));
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
element = (WebElement) driver.findElements(By.xpath(columnToDisplayXpath));
Assert.assertNotNull(element);
it always return me a notFoundElement, any suggestions ?
Thank you
If you have 10 links, then there is a high chance they are different in a way even if the path is the same, in this case you need to construct an path based on the thing that is different.
For example: use href, text or other any part that differs
//a[contains(#href, 'part_of_href')]
//a[contains(text(), 'part_of_text')]
//a[#title='title']
//a[contains(#aria-label='Creazione Prodotto')]
If you need any help in getting the selector please add the html section of the links, you can change the url if needed.
Tip: avoid using absolute xpaths and attributes that do not suggest anything and they can change like: .//*[#id='div_1_1_1_2']/div/div[1]/div[2]/div[2]/div/div[2]/div[1]/div/div[2]/a[contains(#aria-label='Creazione Prodotto')]"
This will give you a lot of work in the future.
To test some of the legacy pages I need to execute few test cases against IE8. These same testcases run efficiently against IE10/11, FF, Chrome without any issue.
public void TypePassword(string password)
{
var element = new WebDriverWait(Driver, TimeSpan.FromSeconds(10)).Until(
ExpectedConditions.ElementIsVisible(By.XPath("//input[#id='txtPassword']")));
//I also tried with just id and cssselector
element.Clear();
element.SendKeys(password);
}
I also tried
public void TypePassword(string password)
{
Password.Clear();
Password.SendKeys(password);
}
Interestingly,
public void TypeUsername(string username)
{
Username.Clear();
Username.SendKeys(username);
}
always work without any issue.
The IE driver configuration
var options = new InternetExplorerOptions { EnableNativeEvents = false};
options.AddAdditionalCapability("EnsureCleanSession", true);
Driver = new InternetExplorerDriver(options);
Seems like I am missing some configuration which is specific to IE8.
Also, confirmed zoom level and protected mode set up
Have you tried JavascriptExecutor ?
var element = new WebDriverWait(Driver, TimeSpan.FromSeconds(10)).Until(ExpectedConditions.ElementIsVisible(By.XPath("//input[#id='txtPassword']")));
((JavascriptExecutor)Driver).executeScript("arguments[0].value='"+password+"'", element);
It is difficult to find the element exists,
so u can go for the element count >1 using do while loop,
do
{
Thread.Sleep(500);
}while(driver.FindElements(By.Id("IDNAME")).Count>0);
Unless until, the count of the element gets, this do loop will execute and wait for the element to visible also
Is their any way to find the object locator type, by passing the object locator alone.
for e.g. i need to click on a login button, where its id=login, classname=loginbutton or xpath=//input[#name='login']. I need to build method where i will be just passing the objectlocator (either id or name) as the input and its type(either id or name) should be decided in the method like if it contains // then type should be of xpath etc.
I need to pass the objectLocator() which returns type to the findElement()
WebElement element = driver.findElement(objectLocator());
I do not think it is available off the shelf, you would have to implement your own logic.
The only thing is, let's say you want to search by linktext. As per your usecase, you would, in your object repo specify, "this is my linktext".
Now how do you know it is an id or a name or a linktext?
For xpath you can check if it starts with /, then its an xpath. If its only id or name then you can use ByIdorName, but i think it would become tricky with css and linktext.
The one thing I can think is you can establish some sort of conventions like if it is linktext precede your lcoator definition with linktext=blah blah and then you split and consume it.
I find it very useful to store all my locators as By objects and either use the By directly or pass the By into methods as I need them. For example:
By passwordField= By.id("login");
By userNameField = By.name("username");
By submitButton = By.xpath("\\myxpath\div[2]");
public void clickLogin() {
driver.findElement(submitButton).click();
}
I also use static Bys from other classes as well:
public void clickLogin() {
driver.findElement(LoginPage.SUBMIT_BUTTON).click();
}
The modern way to do this is using PageFactory and PageObjects
The following is a quick and dirty which will adapt selenium locators strings to WebDriver locators.
public enum LocatorType {
CLASSNAME, CSS, ID, LINK, NAME, TAGNAME, XPATH ;
}
public WebElement objectLocator(LocatorType type, String ref) {
switch(type) {
case ID:
return this.webDriver.findElement(By.id(ref));
case CLASSNAME:
return this.webDriver.findElement(By.className(ref));
case XPATH:
return this.webDriver.findElement(By.xpath(ref));
case CSS:
return this.webDriver.findElement(By.cssSelector(ref));
case LINK:
return this.webDriver.findElement(By.linkText(ref));
case NAME:
return this.webDriver.findElement(By.name(ref));
case TAGNAME:
return this.webDriver.findElement(By.tagName(ref));
}
return null;
}
public WebElement objectLocator(String identifier) {
String typeString = identifier.substring(0, identifier.indexOf('='));
String ref = identifier.substring(identifier.indexOf('=')+1, identifier.length());
if (typeString.toLowerCase().contains("classname")) {
return objectLocator(LocatorType.CLASSNAME, ref);
} else if (typeString.toLowerCase().contains("css")) {
return objectLocator(LocatorType.CSS, ref);
} else if (typeString.toLowerCase().contains("id")) {
return objectLocator(LocatorType.ID, ref);
} else if (typeString.toLowerCase().contains("link")) {
return objectLocator(LocatorType.LINK, ref);
} else if (typeString.toLowerCase().contains("name")) {
return objectLocator(LocatorType.NAME, ref);
} else if (typeString.toLowerCase().contains("tagname")) {
return objectLocator(LocatorType.TAGNAME, ref);
} else if (typeString.toLowerCase().contains("xpath")) {
return objectLocator(LocatorType.XPATH, ref);
} else {
return null;
}
}
It looks like you are looking for this solution because you have an object repository maintained somewhere outside of your code in some kind of properties file or xml.
Using gui maps has lot of disadvantages like,
- maintain an external file with a list of locators
- parse locator files to read keys (you can abstract this but still an overhead)
- when writing PageObjects you need to switch back and forth from Page to gui map
- possibility of multiple duplicate locators in gui maps
- object repo grows over time and becomes impossible to maintain
- debugging is far more difficult
What you are looking for is adding one more layer of complexity which is not required in my opinion. Automating browsers is a challenge in itself and writing maintainable test automation code is utmost important.
Use PageFactory in your page objects.
- Natural place for your locators are Page Objects themselves.
- Locators easily accessible in page objects for review or correction
- No need for explicit driver.findElement, with #FindBy you get that for free
- modern Java and awesome annotations make page objects look beautiful & readable
I have used gui maps before and struggled a lot. Switching to page factory made me realize that using object repository was such a bad idea!
This should do for locating element. I have given example till 3 level deep.
public WebElement findElement(String locator){
WebElement w = null;
try{
return (driver.findElement(By.id(locator)));
}catch(Exception e1){
try{
return ( driver.findElement(By.name(locator)));
}catch(Exception e2){
try{
return (driver.findElement(By.xpath(locator)));
}catch(Exception e3){
System.out.println("Cound not find a locator");
e3.printStackTrace();
}
}
}
return(w);
}
public void type(String locator, String value){
try{
WebElement w= findElement(locator);
w.sendKeys(""+value);
Thread.sleep(sleepTime);
}catch(Exception e){
e.printStackTrace();
}
}
-Vinay