Selenium Wait Until using Element rather the By - c#

Afternoon,
I could use a little advise. I have page object setup for example
IWebElement SiteInUse => DriverContext.Driver.FindElement(By.ClassName("site-txt"));
I have a method set up that will
1. Wait till the element is visible.
2. Check the Elements text is correct.
I am trying to above doing something like
WaitHelpers.WaitTillVisiible(By.ClassName("site-txt"));
as if the id changes I will need to edit it in two places. I am trying to create a extension method for IWebElement.
I have tried
ublic static bool WaitUntilElementIsVisible(this IWebElement element)
{
WebDriverWait wait = new WebDriverWait(DriverContext.Driver, TimeSpan.FromSeconds(30));
return wait.Until(ElementIsVisible(element));
}
public static Func<IWebDriver, bool> ElementIsVisible(IWebElement element)
{
return (driver) =>
{
try
{
return element.Displayed;
}
catch (Exception)
{
// If element is null, stale or if it cannot be located
return false;
}
This works but only if the element is visible but will not continue to look for the 30 seconds.
What am I doing wrong?

I will attempt to answer given the original problem, which was needing to test for visibility, but this required you to update a locator in two places.
The answer is insanely simple: define a instance field for the locator:
public class SomePageModel
{
By siteInUseLocator = By.ClassName("site-txt");
IWebElement SiteInUse => DriverContext.Driver.FindElement(siteInUseLocator);
...
}
Then later on you can reuse this field to test for visibility:
WaitHelpers.WaitTillVisiible(siteInUseLocator);

Related

Check if element is interactable in C# Selenium/ChromeDriver app?

I have a C# Selenium app that uses the ChromeDriver/WebDriver NuGet package that supports Chrome 88. My Chrome version is 88.0.4324.104.
Some of the web pages I am working with have more than one BUTTON element with the same class and and tagName so when I execute my FindDomElement() call using my desired xpath, I get back multiple matching elements. Only one of the returned elements is interactable and the other ones are not. Right now I try executing a "Click()" call on each element until I find the right one, catching the ElementNotInteractableException exception for the ones that fail.
Is there a way to ask Selenium if an element is interactable?
Please note, all the elements are visible and enabled and I already know how to check the status of those properties via C#. They simply are not helpful in this case.
An extension method might be a better solution here. It would return true if the element was clicked, and return false if the element was not interactable.
public static class WebElementExtensions
{
public static void TryClick(this IWebElement element)
{
try
{
element.Click();
return true;
}
catch (ElementNotInteractableException)
{
return false;
}
}
}
Then it is just a simple test:
if (button.TryClick())
{
// Button was clicked successfully
}
else
{
// Button is not interactable
}

Selenium test can't click on mat-select element

Angular Material Select (mat-select) inside Form Field (mat-form-field) ignores Selenium click
When test runs, dropdown doesn't appear after click is made by Selenium. If it is made manually by me, test continues and passes.
I also tried to use SelectElement class but it's not applicable since mat-select doesn't use any select elements
public class Page definition:
[FindsBy(How = How.XPath, Using = "//*[#id='form']//mat-form-field//*[#class='mat-form-field-flex' and .//*[#formcontrolname='network']]")] // also tried all surrounding elements from DOM including mat-form-field and mat-select (look at attached screenshot)
public IWebElement _formNetworkFormControl;
public bool IsFormNetworkFormControlComponentPresent()
{
return ExtendedWebElementOperations.IsElementDisplayed(_formNetworkFormControl);
}
// Click dropdown
public void ClickFormNetworkFormControl(IWebDriver webdriver)
{
// Wait is needed until data is loaded and select is enabled
WebDriverWait wait = new WebDriverWait(webdriver, timeout: TimeSpan.FromSeconds(15));
wait.Until(ExpectedConditions.ElementToBeClickable(_formNetworkFormControl));
_formNetworkFormControl.Click();
}
// To select option after dropdown click
public void SelectByOptionName(IWebDriver webdriver, string optionName)
{
string xpath = $"//mat-option[span[text()[contains(.,'{optionName}')]]]";
WebDriverWait wait = new WebDriverWait(webdriver, timeout: TimeSpan.FromSeconds(5));
wait.Until(ExpectedConditions.ElementIsVisible(By.XPath(xpath)));
IWebElement optionNameSelect = webdriver.FindElement(By.XPath(xpath));
optionNameSelect.Click();
}
Test:
var testedPage = new Page(Driver);
testedPage.ClickAddRingTestFormNetworkFormControl(Driver); // click is made but dropdown doesn't appear
testedPage.SelectByOptionName(Driver, networkName); // if select is clicked manually, this works great
The problem is that mat-select has aria-disabled='true' until required data is loaded, so
wait.Until(ExpectedConditions.ElementToBeClickable(_formNetworkFormControl));
doesn't cover this case.
I fixed it with updating Page class following way:
public IWebElement IsElementHasTrueAriaDisabledAttribute(IWebDriver webdriver, IWebElement element)
{
if (element.GetAttribute("aria-disabled").Equals("false"))
{
return element;
}
return null;
}
public void ClickFormNetworkFormControl(IWebDriver webdriver)
{
WebDriverWait wait = new WebDriverWait(webdriver, timeout: TimeSpan.FromSeconds(15));
wait.Until<IWebElement>((d) =>
{
return IsElementHasTrueAriaDisabledAttribute(d, _formNetworkFormControl);
});
_formNetworkFormControl.Click();
}

Selenium ChromeDriver - element never got visible

I try to send keys to input field but can't do it...
I have tried different ways to wait till element is visible but got timeout exceptions...
IWebElement userName = driver.FindElement(By.Id("UserName"));
IWebElement userPassword = driver.FindElement(By.Id("Password"));
IWebElement subButton = driver.FindElement(By.XPath(("//button[contains(.,'Вхід')]")));
while (true)
{
userName = driver.FindElement(By.Id("UserName"));
if (userName.Displayed)
{
userName.SendKeys("test");
break;
}
}
subButton.Click();
Using this method gives me always timeout:
public static void WaitForElementLoad(By by, int timeoutInSeconds)
{
if (timeoutInSeconds > 0)
{
WebDriverWait wait = new WebDriverWait(webDriver, TimeSpan.FromSeconds(timeoutInSeconds));
wait.Until(ExpectedConditions.ElementIsVisible(by));
}
}
If its hidden just send/execute a simple js by selenium that will show the element. But it cant be a little bit more tricki. Set the window size to a bigger one eg 2000x2000. If something is not placed in the viewport selenium will not see it.
please try to use JavaScript to scroll to the element and then perform other operations on the element
((JavascriptExecutor) driver).executeScript("arguments[0].scrollIntoView(true);", element);

Selenium Webdriver C# wait for existence when using PageFactory

I need to click an okay button which might appear after completing a field - it might take 5 seconds to appear. So i need (if) Wait for existence 5 seconds. I'm using PageFactory in a pages framework, I've seen some solutions but cant figure out how to implement them in this context.
[FindsBy(How = How.Name, Using = "OK")]
private IWebElement alertOKBtn;
public void PopulateFields //method to populate the form
{
// Populate fields
dateFromField.SendKeys(DateTime.Now.ToString("dd/MM/yyyy HH:mm:ss"));
// Click on this field
descriptionField.Click();
//OK button might appear, might take 5secs - pseudcode
if ( ***alertOKBtn exists, wait for it for 5 secs..*** )
{
alertOkBtn.Click();
}
//continue populating form
}
The PopulateFields method is called from the [Test] as:-
Pages.PTW.PopulateFields();
where Pages.PTW is a get method to PageFactory.InitElements(browser.Driver, page); return page;
Managed to resolve it - in PopulateFields i now do this:-
//wait to see if alert popup appears - give it 8 secs
string waitToSee = browser.wait(alertOKBtn, 8);
if ( waitToSee == "true" )
{
alertOKBtn.Click(); //alert popup did appear
}
Then I've added a method to my browser.class :-
public static string wait(IWebElement elem, int timeout ) //waits for existence of element up to timeout amount
{
try
{
var wait = new WebDriverWait(webDriver, TimeSpan.FromSeconds(timeout));
wait.Until(ExpectedConditions.ElementToBeClickable(elem));
return "true";
}
catch (Exception e ) //didnt appear so exception thrown return false
{
return "false";
}
So it now waits up to 8 seconds and if it doesnt appear it ignores and moves on. Thanks Bendram for the pointers.
Need to add conditional wait. That means, your code should wait till the control appears and then perform the action.
WebDriverWait class which inherits DefaultWait class serves the purpose. The below is the code snippet.
var wait = new WebDriverWait(this.driver, waitTime);
wait.Until(ExpectedConditions.ElementToBeClickable(alertOkBtn));
alertOkBtn.Click();

WebDriver in C# (work with windows and synchronization)

I work with webDriver in #IE9 and I find one problem. If I started tests in Run mode, then all test fail because webDriver not exists (two window ie), but if I put breakpoint in tests and start tests Debug mode I have passed all tests. Please tell me, what do, because I don't know.
This my code:
private void MyMethods(IWebdriver driver)
{
foreach (var item in driver.WindowHandles) // if I put breakpoint, I see 2 count Window Handles else this methods don't work.
{
if (driver.SwitchTo().Window(item).Title == "PortalSubMenuPopupForm")
{
driver.SwitchTo().Window(item);
break;
}
}
}
Selenium has an "issue" with IE where new windows might not appear on the WindowHandles list right away.
The solution is either
wait a fixed amount of time before calling driver.WindowHandles
or
use the WebDriverWait class to wait for the number of elements under WindowHandles to change
I think the second one is more robust. Here is a quick implementation:
public void LaunchNewWindow(IWebElement element)
{
int windowsBefore = driver.WindowHandles.Count;
element.Click();
TimeSpan timeout = new TimeSpan(0, 0, 10);
WebDriverWait wait = new WebDriverWait(driver, timeout);
wait.Until((_driver) =>
{
return _driver.WindowHandles.Count != windowsBefore;
//optionally use _driver.WindowHandles.Count > windowsBefore
});
}
Now you can use the function like so:
IWebElement clickMe = //some element that launches a new window
LaunchNewWindow(clickMe);
foreach (var item in driver.WindowHandles)
{
//etc.
}

Categories

Resources