Selenium doesn't wait after submit until website is loaded - c#

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"))));

Related

MSTest Login to web app and assert web app is loaded before beginning tests

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"));
}

Where to put implicit wait in Page Object Model?

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;
}**

How to know when a page is finished loading?

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));
}

Waiting for all actions to be executed after clicking a button in selenium

i'm sorry if it's been answered before but i really couldn't find a good solution to my problem
I have an email input field and i want to check that when you input an invalid email an error message appears.
my code looks somthing like this:
private void LoadElements()
{
Email = driver.FindElement(By.Id("MainContent_Email"));
SaveButton = driver.FindElement(By.Id("MainContent_SaveButton"));
ErrorMessage = driver.FindElement(By.Id("MainContent_PersDetsError"));
}
public void test()
{
//Email, SaveButton & Error are IWebElement
LoadElements();
Email.Clear();
Email.SendKeys(emailString);
SaveButton.Click();
// Avoid stale element exception on refresh after a successful save:
LoadElements();
Assert.IsTrue(ErrorMessage.Displayed);
}
My problem is that when i try to purposly fail the test (input a valid email), some of the time I still get a stale element exception. I presume this happens becuse LoadElements() loads the web elements too quickly.
My question is this: is there a way to wait for the page to fully load before executing any commends?
PS I tried the following code and it didn't work for me
IWait<IWebDriver> wait = new OpenQA.Selenium.Support.UI.WebDriverWait(driver, TimeSpan.FromSeconds(30.00));
wait.Until(driver1 => ((IJavaScriptExecutor)driver).ExecuteScript("return document.readyState").Equals("complete"));

Selenium - Scroll down a growing page

I'm using Selenium with c#.
Selenium usually can automatically scroll down to the bottom of a web page to find elements but I having issues with a certain page which can increase in size.
Can anyone suggest code that will scroll down to the bottom of the page once it grows in size?
Try using javascript as described in this question
IJavaScriptExecutor js = (IJavaScriptExecutor)driver;
js.ExecuteScript("window.scrollTo(0, document.body.scrollHeight);");
I know it's an old one, but it may be of someone's help. I came out with the following C# code:
private void ScrollToBottom(IWebDriver driver)
{
long scrollHeight = 0;
do
{
IJavaScriptExecutor js = (IJavaScriptExecutor) driver;
var newScrollHeight = (long) js.ExecuteScript("window.scrollTo(0, document.body.scrollHeight); return document.body.scrollHeight;");
if(newScrollHeight == scrollHeight)
{
break;
}
else
{
scrollHeight = newScrollHeight;
Thread.Sleep(400);
}
} while (true);
}
An example in C# using .Net 4.5 and Selenium WebDriver 2.45
Just change the _url variable to point to your website and run.
I used the ChromeDriver but it should work with the other drivers as well.
using System;
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
namespace SeleniumScrollTest {
internal static class Program {
// Declare Selenium Web Driver
private static IWebDriver _chromeDriver;
private static String _url;
private static void Main(string[] args) {
// Instantiate URL
_url = #"http://my.website.com/LazyLoadContent";
// Instantiate Web Driver as ChromeDriver and set initial URL
_chromeDriver = new ChromeDriver {Url = _url};
// Instruct the WebDriver to wait X seconds for elements to load
_chromeDriver.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(15));
// Instantiate JavaScript Executor using the web driver
var jse = (IJavaScriptExecutor) _chromeDriver;
// The minified JavaScript to execute
const string script =
"var timeId=setInterval(function(){window.scrollY<document.body.scrollHeight-window.screen.availHeight?window.scrollTo(0,document.body.scrollHeight):(clearInterval(timeId),window.scrollTo(0,0))},500);";
// Start Scrolling
jse.ExecuteScript(script);
// Wait for user input
Console.ReadKey();
// Close the browser instance
_chromeDriver.Close();
// Close the ChromeDriver Server
_chromeDriver.Quit();
}
}
}
If you've already a moderate understanding of Selenium and C#, the important bit is really the JavaScript.
-Sourced from Cybermaxs, here
var timeId = setInterval(function () {
if (window.scrollY !== document.body.scrollHeight)
window.scrollTo(0, document.body.scrollHeight);
else
clearInterval(timeId);
}, 500);
The 500 above is the interval at which it will attempt scroll (in microseconds), adjust this as necessary. [1000 microseconds = 1 second]
Am sorry I don't work with c# but guess the logic would remain the same for any language. If it is a lazy load of the page then you can use Actions class to perform sending pagedown key option. If you get message like more items to load or no more items then you can identify this element. Put the page down option inside a while loop which performs page down until the condition is satisfied. This way you can completely load all the content of the page. Let me know if you need more help.

Categories