Using a selenium driver, I need to know when page is finished loading.
When you navigate to a new page, selenium does that. However, I do a button click, and I need to know when the next page will be loaded.
Is there a way to do something like wait_for_loading_complete?
I don't think selenium has inbuilt solution for checking the page load.
But we can build a method to verify the same. Some examples are below but there may be many more depending on the context.
1st :
public void waitUntilPageLoaded(long timeoutSeconds) {
WebDriverWait wait = new WebDriverWait(driver, timeoutSeconds);
wait.until(ExpectedConditions.presenceOfElementLocated(By.xpath("//body")));
}
2nd :
public void waitUntilPageLoaded(long timeoutSeconds) {
ExpectedCondition<Boolean> pageLoadFinishedCondition = new ExpectedCondition<Boolean>() {
public Boolean apply(WebDriver driver) {
return ((JavascriptExecutor) driver).executeScript(
"return document.readyState").equals("complete");
}
};
WebDriverWait wait = new WebDriverWait(driver, timeoutSeconds);
wait.until(pageLoadFinishedCondition);
}
3rd :
public void waitUntilPageLoaded(int timeoutSeconds, By locator) {
new WebDriverWait(driver, timeoutSeconds).until(ExpectedConditions.presenceOfElementLocated(locator));
}
Related
I am currently using MSTest in visual studio 2019 and am having an issue with selenium finding an element on the dashboard after login. After some debugging I found that the login method that I have set in my OneTimeSetUp method isn't fully finishing, before the other tests in the test class run., isn't this weird behavior? I would expect for my login method with an assert that the page is loaded to finish before moving to the other tests being that it is in the OneTimeSetUp method. Am I doing something wrong? Is there something that anyone would recommend? Again, I am trying to login and validate that the dashboard is loaded before running the test methods in the test class. Thank you!
Here is my login method:
public void Login()
{
var _driver = DriverHelper.Driver;
//Creates a login page object
var loginPage = new HomeBankingLoginPage();
//Enters login credentials
loginPage.UserName.SendKeys(getElementsXML("userName"));
loginPage.Password.SendKeys(getElementsXML("password"));
loginPage.Submit.Click();
WebDriverWait implicitlyWait = new WebDriverWait(_driver, TimeSpan.FromSeconds(20));
IWebElement firstResult = implicitlyWait.Until(e => e.FindElement(By.LinkText("Transfer")));
}
Here is my oneTimeSetUp method:
//Executes before tests start
//Opens Homebanking URL and Login
[OneTimeSetUp]
public void Setup()
{
Login();
}
See this line,
WebDriverWait implicitlyWait = new WebDriverWait(_driver, TimeSpan.FromSeconds(20));
I know that implicitlyWait is just a reference. But new WebDriverWait(_driver, TimeSpan.FromSeconds(20)); this is Explicit waits. See here
as well as I do not see assertion. What I would suggest is :-
Use Explicit wait to login into the application.
Assert the dashboard page (either title, or webpage heading or Profile pic, just make sure whatever you are asserting should be one of the last loaded element in UI after login)
Your next test method after login should be invoked.
I will be using [TestInitialize] -
Marks a method that should be called before each test method. One such
method should be present per test class.
Sample code :-
[TestClass]
public class TestClass
{
public void Login()
{
var _driver = DriverHelper.Driver;
_driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(10); // This is implicit wait
//Creates a login page object
var loginPage = new HomeBankingLoginPage();
//Enters login credentials
loginPage.UserName.SendKeys(getElementsXML("userName"));
loginPage.Password.SendKeys(getElementsXML("password"));
loginPage.Submit.Click();
//If you have NuGet, search for DotNetSeleniumExtras.WaitHelpers, and import that namespace into your class.
var wait = new WebDriverWait(driver, new TimeSpan(0, 0, 30));
var element = wait.Until(SeleniumExtras.WaitHelpers.ExpectedConditions.ElementIsVisible(By.LinkText("Transfer")));
string dashBoardText = element.Text;
string expectedText = "you should write expected text from transfer from Dashboard here"
Assert.IsTrue(expectedText.Contains(dashBoardText));
// if the above does not work, you can still try with lamba expression :-
WebDriverWait explicitWait = new WebDriverWait(_driver, TimeSpan.FromSeconds(20));
IWebElement firstResult = explicitWait.Until(e => e.FindElement(By.LinkText("Transfer")));
string dashBoardText = firstResult.Text;
string expectedText = "you should write expected text from transfer from Dashboard here"
Assert.IsTrue(expectedText.Contains(dashBoardText));
}
[TestInitialize]
public void TestInitialize()
{
Console.WriteLine("Inside TestInitialize");
Login();
}
[TestMethod]
public void Test()
{
Console.WriteLine("Inside TestMethod");
}
[TestCleanup]
public void TestCleanup()
{
Console.WriteLine("Inside TestCleanup");
}
}
You can try setting the implicit wait, then just try and locate an element you know should be visible when the page is ready. The driver will then wait for the implicit wait time before throwing an exception.
public void Login()
{
var _driver = DriverHelper.Driver;
_driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(20);
// Let set the page timeout too, just to be safe.
_driver.Manage().Timeouts().PageLoad = TimeSpan.FromSeconds(20);
//Creates a login page object
var loginPage = new HomeBankingLoginPage();
//Enters login credentials
loginPage.UserName.SendKeys(getElementsXML("userName"));
loginPage.Password.SendKeys(getElementsXML("password"));
loginPage.Submit.Click();
// Locate the element you know should be there, it will time out after the implicitWait.
var transferElement = _driver.FindElement(By.LinkText("Transfer"));
}
I have a POM setup with
SearchPage
LogInPage
and then a Test file.
The SearchPage and LogInPage have all the locators, action methods (find, click, send keys) etc.
Currently in my Test file I have:
[TestInitialize]
public void Setup()
{
driver = new ChromeDriver();
driver.Manage().Window.Maximize();
driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(30);
}
One of the tests looks like this:
[TestMethod]
public void LogIn()
{
var searchPage = new SearchPage(driver);
var url = searchPage.GetUrl();
try
{
url.Should().Be(SearchPage.searchURL);
log.Debug("The LogIn test passed!");
}
catch(AssertFailedException ex)
{
log.Debug("The LogIn test failed", ex);
}
}
My question is, will that ImplicitWait in the [TestInitialize] carry across all Page Objects and methods/actions that are being called through the Tests themselves? Or is it only applicable to whatever actions happen on that Tests page itself (i.e. do I need to put the implicit wait in every Page class)?
BTW I realize explicit waits are probably better to use, but I want to get the hang of this first.
The implicit waits apply to all page models using that specific instance of the web driver.
See Implicit Wait Commands in Selenium WebDriver C# for more information.
When we doing page object model mostly we occour nullpointexception because every page should need create object.
if you create utils class for wait you can use it anywhere in project
**public WebElement waitForElement(By locator, int timeout)
{
WebElement element = new WebDriverWait(driver, timeout).until
(ExpectedConditions.presenceOfElementLocated(locator));
return element;
}**
I am trying to submit a login form with Selenium from C#. But I can't make it wait after submit to wait the new page to load. Only thing that has worked is Thread.Sleep. What should I do to make it wait?
[TestFixture]
public class SeleniumTests
{
private IWebDriver _driver;
[SetUp]
public void SetUpWebDriver()
{
_driver = new FirefoxDriver();
// These doesn't work
//_driver.Manage().Timeouts().SetPageLoadTimeout(TimeSpan.FromSeconds(10));
//_driver.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(10));
}
[Test]
public void SubmitTest()
{
_driver.Url = "http://mypage.com";
_driver.FindElement(By.Name("username")).SendKeys("myname");
_driver.FindElement(By.Name("password")).SendKeys("myeasypassword");
_driver.FindElement(By.TagName("form")).Submit();
// It should wait here until new page is loaded but it doesn't
// So far this is only way it has waited and then test passes
//Thread.Sleep(5000);
var body = _driver.FindElement(By.TagName("body"));
StringAssert.StartsWith("Text in new page", body.Text);
}
}
I've found the best way to do this is to wait for an element on the first page to go stale, then wait for the element on the new page. The problem you are likely having is that you are waiting for the body element... which is going to exist on every page there is. If you want to just wait for an element, you should find an element that is unique to the page you are navigating to. If you still want to use the body tag, you can do this...
public void SubmitTest()
{
_driver.Url = "http://mypage.com";
_driver.FindElement(By.Name("username")).SendKeys("myname");
_driver.FindElement(By.Name("password")).SendKeys("myeasypassword");
IWebElement body = _driver.FindElement(By.TagName("body"));
_driver.FindElement(By.TagName("form")).Submit();
body = new WebDriverWait(_driver, TimeSpan.FromSeconds(10)).Until(ExpectedConditions.ElementIsVisible(By.TagName("body")))
StringAssert.StartsWith("Text in new page", body.Text);
}
Answer was practically in JeffC's answer:
I've found the best way to do this is to wait for an element on the first page to go stale, then wait for the element on the new page.
I solved this with this answer: https://stackoverflow.com/a/15142611/5819671
I put following code before reading body element from new page and now it works:
new WebDriverWait(_driver, TimeSpan.FromSeconds(10)).Until(ExpectedConditions.ElementExists((By.Id("idThatExistsInNewPage"))));
I am new to Selenium and was using Telerik free testing framework before. Problem is I am not able to understand, how to use elements which are already identified with [FindsBy] to wait, check and click on.
ex:
[FindsBySequence]
[FindsBy(How = How.Id, Using = "container-dimpanel")]
[FindsBy(How = How.CssSelector , Using = ".btn.btn-primary.pull-right")]
public IWebElement UpdateButton { get; set; }
internal void ClickUpdateButton(TimeSpan timeout)
{
new WebDriverWait(_driver, timeout).
Until(ExpectedConditions.ElementIsVisible(By.CssSelector(id));
UpdateButton.Click();
}
I want my code to wait for update button to be visible and then click on it. But I want to just pass the UpdateButton element rather than using By selector.
not sure if UpdateButton.Enabled will wait until its visible.
There is an expected condition for visibility that accepts a WebElement:
https://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/support/ui/ExpectedConditions.html#visibilityOf-org.openqa.selenium.WebElement-
Until also returns the element being waited for, so you can combine this into one line:
internal void ClickUpdateButton(TimeSpan timeout)
{
WebDriverWait wait = new WebDriverWait(_driver, timeout);
wait.Until(ExpectedConditions.visibilityOf(UpdateButton)).click();
}
However, in my frameworks I usually add a helper function that does this, as it get's used so much. You can also do similar things with wait until clickable, etc. and have methods that accept a WebElement or a By:
public WebElement waitThenClick(WebElement element)
{
WebDriverWait wait = new WebDriverWait(_driver, timeout);
return wait.Until(ExpectedConditions.visibilityOf(UpdateButton)).click();
}
The C# client doesn't have a builtin condition to check the visibility for a proxied WebElement.
Moreover the expected condition ExpectedConditions.ElementIsVisible checks that the element is displayed but doesn't check that the element is visible from a user perspective.
So the quickest and most reliable way is to retry the click in a waiter until success:
Click(UpdateButton, 5);
static void Click(IWebElement element, int timeout = 5) {
var wait = new DefaultWait<IWebElement>(element);
wait.IgnoreExceptionTypes(typeof(WebDriverException));
wait.PollingInterval = TimeSpan.FromMilliseconds(10);
wait.Timeout = TimeSpan.FromSeconds(timeout);
wait.Until<bool>(drv => {
element.Click();
return true;
});
}
Use this function I've written to test for an element, you can just pass in the name. It will return a bool and you could use a loop to wait for the elements to be present.
static public bool verify(string elementName)
{
try
{
bool isElementDisplayed = driver.FindElement(By.XPath(elementName)).Displayed;
return true;
}
catch
{
return false;
}
return false;
}
I have a webapp that will contain a class of "Loading" which, when fully loaded on the page, will contain a width property of 100% else it will contain nothing. I'm trying to perform a check on this style attribute but I keep getting a timeout. here is what I'm doing:
I'm calling code from within a helper / utility class as follows as this is something I will be using frequently in multiple classes:
Utility.WaitForStyle("Loading", Utility.driver);
In my helper / utility class I have the following code:
public static void WaitForStyle(string Class, IWebDriver driver)
{
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(20));
wait.Until<bool>((d) =>
{
try
{
IWebElement element = d.FindElement(By.ClassName(Class));
String elementresults = element.GetAttribute("style");
System.Diagnostics.Debug.WriteLine(elementresults);
return false;
}
catch (NoSuchElementException)
{
return true;
}
});
}
Note, The code above is currently just looking to check that it can get a handle on the class's style attribute but it's not getting to that point. I know the problem lies within the utility method as I can use the following code in individual classes:
IWebElement element = Utility.driver.FindElement(By.ClassName("Loading"));
String elementresults = element.GetAttribute("style");
System.Diagnostics.Debug.WriteLine(elementresults);
This will printout "width: 100%" as expected so I know that this block of code is actually working ok.
Does anyone know if I'm doing something silly in my utility method?
Here's my code to wait for an element attribute to have a specific value. It assumes the element passed to it has been verified to exist:
public bool WaitForAttribute(IWebDriver driver, IWebElement element, string attributeName, string attributeValue, int timeOut = 5)
{
// Build a function with a signature compatible with the WebDriverWait.Until method
Func<IWebDriver, bool> testCondition = (x) => element.GetAttribute(attributeName).Equals(attributeValue);
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(timeOut));
// Wait until either the test condition is true or timeout
try { wait.Until(testCondition); }
catch (WebDriverTimeoutException e) { }
// Return a value to indicate if our wait was successful
return testCondition.Invoke(null);
}
This is working for me, since more than 4 months.
public static WebDriverWait wait = new WebDriverWait(SeleniumInfo.Driver, TimeSpan.FromSeconds(20));
public static void WaitUntilAttributeValueEquals(this IWebElement webElement, String attributeName, String attributeValue)
{
wait.Until<IWebElement>((d) =>
{
if (webElement.GetAttribute(attributeName) == attributeValue)
{
return webElement;
}
return null;
});
}