Where to put implicit wait in Page Object Model? - c#

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

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

Selenium. Bring-up window on the front

If you run the following code, then at each iteration of the cycle, the browser will bring up on the front and get focus.
public class Program
{
private static void Main()
{
var driver = new ChromeDriver();
driver.Navigate().GoToUrl("https://i.imgur.com/cdA7SBB.jpg");
for (int i = 0; i < 100; i++)
{
var ss = ((ITakesScreenshot)driver).GetScreenshot();
ss.SaveAsFile("D:/imgs/i.jpg");
}
}
}
The question is: why does this happen and can it be turned off? headless mod does not fit.
It seems that this always happens when Selenium needs to save / read the file or start the process.
To take a screenshot, chromedriver activates the window. It's by design and there's no option to avoid it even though it's technically possible.
For the relevant sources have a look at window_commands.cc.
You could however avoid the effect by moving the window off-screen:
driver.Manage().Window.Position = new Point(-32000, -32000);
or by launching the browser off-screen:
var options = new ChromeOptions();
options.AddArgument("--window-position=-32000,-32000");
UPDATE
You can avoid the activation by taking the screenshot directly via the devtool API. Here's a class to override GetScreenshot:
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Remote;
using JObject = System.Collections.Generic.Dictionary<string, object>;
class ChromeDriverEx : ChromeDriver
{
public ChromeDriverEx(ChromeOptions options = null)
: base(options ?? new ChromeOptions()) {
var repo = base.CommandExecutor.CommandInfoRepository;
repo.TryAddCommand("send", new CommandInfo("POST", "/session/{sessionId}/chromium/send_command_and_get_result"));
}
public new Screenshot GetScreenshot() {
object response = Send("Page.captureScreenshot", new JObject {{"format", "png"}, {"fromSurface", true}});
string base64 = (string)((JObject)response)["data"];
return new Screenshot(base64);
}
protected object Send(string cmd, JObject args) {
return this.Execute("send", new JObject {{"cmd", cmd}, {"params", args}}).Value;
}
}
usage:
var driver = new ChromeDriverEx();
driver.Url = "https://stackoverflow.com";
driver.GetScreenshot().SaveAsFile("/tmp/screenshot.png");
driver.Quit();
When you invoke Navigate().GoToUrl("url") method through your Automation script, it is expected that your script will be interacting with some of the elements on the webpage. So for Selenium to interact with those elements, Selenium needs focus. Hence opening up the browser, bring up on the front and getting the focus is the default phenomenon implemented through Navigate().GoToUrl("url").
Now Default Mode or Headless Mode is controlled by the ChromeOption/FirefoxOptions class which is passed as an argument while initializing the WebDriver instance and will call Navigate().GoToUrl("url"). So, Navigate().GoToUrl("url") would have no impact how the WebDriver instance is controlling the Mode of Operation i.e. Default Mode or Headless Mode.
Now when you try to invoke the method from ITakesScreenshot Interface i.e. ITakesScreenshot.GetScreenshot Method which is defined as :
Gets a Screenshot object representing the image of the page on the screen.
In case of WebDriver instance which extends ITakesScreenshot, makes the best effort depending on the browser to return the following in order of preference:
Entire page
Current window
Visible portion of the current frame
The screenshot of the entire display containing the browser
There may be some instances when the browser looses the focus. In that case you can use IJavascriptExecutor to regain the focus as follows :
((IJavascriptExecutor) driver).executeScript("window.focus();");
I was struggling with an issue when generic GetScreenshot() in parallel testing was causing browser to lose focus. Some elements were being removed from DOM and my tests were failing. I've come up with a working solution for Edge and Chrome 100+ with Selenium 4.1:
public Screenshot GetScreenshot()
{
IHasCommandExecutor executor = webDriverInstance as IHasCommandExecutor;
var sessionId = ((WebDriver)webDriverInstance).SessionId;
var command = new HttpCommandInfo(HttpCommandInfo.PostCommand, $"/session/{sessionId}/chromium/send_command_and_get_result");
executor.CommandExecutor.TryAddCommand("Send", command);
var response = Send(executor, "Page.captureScreenshot", new JObject { { "format", "png" }, { "fromSurface", true } });
var base64 = ((Dictionary<string, object>)response.Value)["data"];
return new Screenshot(base64.ToString());
}
private Response Send(IHasCommandExecutor executor, string cmd, JObject args)
{
var json = new JObject { { "cmd", cmd }, { "params", args } };
var command = new Command("Send", json.ToString());
return executor.CommandExecutor.Execute(command);
}

Selenium doesn't wait after submit until website is loaded

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

How to run one browser per thread in Selenium?

I don't think this is particular to Selenium, but I've included that tag because I think it's a problem that's very relevant to Selenium tests.
I have a Browser class that's working as it stands:
public static class Browser {
private static IWebDriver webDriver;
private static IWebDriver ieDriver;
private static IWebDriver chromeDriver;
private static BrowserType _browserType;
public static BrowserType BrowserType {
set {
_browserType = value;
switch (_browserType) {
case BrowserType.IE:
if (ieDriver == null)
{
var ieOptions = new InternetExplorerOptions();
ieOptions.InitialBrowserUrl = "about:home";
ieDriver = new InternetExplorerDriver(DriverPath, ieOptions);
}
webDriver = ieDriver;
break;
case BrowserType.Chrome:
if (chromeDriver == null)
{
chromeDriver = new ChromeDriver(DriverPath);
}
webDriver = chromeDriver;
break;
default:
if (chromeDriver == null)
{
chromeDriver = new ChromeDriver(DriverPath);
}
webDriver = chromeDriver;
break;
break;
}
} get { return _browserType; }
}
public static void Goto(string url) {
webDriver.Navigate().GoToUrl(url);
}
}
The problem is that each of these browsers should run in their own thread, so that each test can run on each browser simultaneously (cutting cross-browser test times to the time it takes to run a single browser's test). Right now tests are called sequentially with the following method:
public void RunTest(Func<TestSettings, TestRole, bool> testToRun)
{
foreach (var browserType in BrowserTypes)
{
// Assert test passes in given browser
// browser should have its own thread
}
}
How can multithreading be achieved in this scenario?
Multithreadding is usually achieved to run multiple tests with a testunit.
For PHP you have PHPUnit and some other options:
http://net.tutsplus.com/tutorials/php/parallel-testing-for-phpunit-with-paratest/
For Java you could try to dig in maven-surefire-plugin using JUnit.
http://maven.apache.org/surefire/maven-surefire-plugin/examples/junit.html
Don't know if it is achievable through any selenium API.
If you find a way, please make sure to let me know!
Hope this helps.
I see that you have only one driver:
private static IWebDriver webDriver;
When you set BrowserType for the first time (for example as IE) you assign webDriver (as IE).
Then when you set BrowserType for the second time (for example as Chrome) you re-assign webDriver (now it is Chrome, IE is lost). You will never get simultaneously run of both browsers in this way.
BrowserType should be set externally. For example, as a parameter of your test-project or from App.config. If you want to run tests in one machine simultaneously, create an app (console app for example), that launches your test-project with different BrowserType values in two different threads.

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