Selenium timeouts implementation through the Page Factory model - c#

Please share your experience on the timeouts implementation in case of Page Factory model.
In case of simple variable usage (var loginButton = driver.FindElement(By.Id("login")), there are possibility to make extension like:
public static class WebDriverExtensions
{
public static IWebElement FindElement(this IWebDriver driver, By by, int timeoutInSeconds)
{
if (timeoutInSeconds > 0)
{
var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(timeoutInSeconds));
return wait.Until(drv => drv.FindElement(by));
}
return driver.FindElement(by);
}
}
Code belongs to user Loudenvier
Reference to original discussion
But I can not add attributes to FindsByAttribute sealed class. At this moment my WebElements look like:
[FindsBy(How = How.Id, Using = "tbUsername")]
private IWebElement Username;

Related

Unable to locate element -- OpenQA.Selenium.NoSuchElementException

I am trying to automate a test case through Specflow, by using the Gherkin format, but I keep getting the error:
OpenQA.Selenium.NoSuchElementException : no such element: Unable to locate element: {"method":"xpath","selector":"./ancestor-or-self::form"}
The button that cannot be located is commented in the AddCart Page below.
The website that I am testing is: http://automationpractice.com/index.php
What I am trying to achieve is as seen below:
Feature: Adding products in the cart
Scenario Outline: Adding a product of the HomePage in the cart and continue shopping
Given That the user is on the HomePage
When User clicks the Add to cart button
And User clicks the Continue shopping button
Then The user will stay on the HomePage
The code of the Step Definition is:
namespace WebsiteTestingSpecflow.Steps
{
[Binding]
public sealed class AddingCartContinueStep
{
AddCartPage addcart = null;
[Given(#"That the user is on the HomePage")]
public void GivenThatTheUserIsOnTheHomePage()
{
IWebDriver webDriver = new ChromeDriver();
webDriver.Navigate().GoToUrl("http://automationpractice.com/index.php");
addcart = new AddCartPage(webDriver);
}
[When(#"User clicks the Add to cart button")]
public void WhenUserClicksTheAddToCartButton()
{
addcart.AddCart();
}
[When(#"User clicks the Continue shopping button")]
public void WhenUserClicksTheContinueShoppingButton()
{
addcart.ContinueShopping();
}
[Then(#"The user will stay on the HomePage")]
public void ThenTheUserWillStayOnTheHomePage()
{
addcart.Verifyelement();
}
}
}
The code of the AddCart Page is:
namespace WebsiteTestingSpecflow.Pages
{
public class AddCartPage
{
private readonly WebDriverWait wait;
public IWebDriver Webdriver { get; }
public AddCartPage(IWebDriver webDriver)
{
Webdriver = webDriver;
wait = new WebDriverWait(webDriver, TimeSpan.FromSeconds(30));
}
public IWebElement BtnAddCart => Webdriver.FindElement(By.CssSelector("#homefeatured > .ajax_block_product:nth-child(1) .button:nth-child(1) > span"));
// This is the button that I keep getting error.
public IWebElement btnContinueCart => Webdriver.FindElement(By.CssSelector(".continue > span"));
public void AddCart() {
BtnAddCart.Submit();
}
public void ContinueShopping() {
wait.Until(ExpectedConditions.ElementToBeClickable(By.CssSelector(".continue > span")));
btnContinueCart.Submit();
}
public void Verifyelement() => Webdriver.FindElement(By.CssSelector(".sfHover > .sf-with-ul"));
}
}
The CSS Selector of that button is as mentioned in the AddCart Page, but it still unable to locate the element.
May anyone know how can I fix this problem?
Thank you in advance.
Looks like you are trying to find element at the moment when it's not visible yet, because it will be visible only when you click to Add to cart and only then that element can be reached.
Try code below and also consider using Page Object model
public void ContinueShopping() {
IWebElement btnContinueCart => Webdriver.FindElement(By.CssSelector(".continue > span"));
wait.Until(ExpectedConditions.ElementToBeClickable(By.CssSelector(".continue > span")));
btnContinueCart.Submit();
}
You need to incorporate Actions into your code since this button is not visible unless you hover over it. Also add a wait condition around it
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
Actions action = new Actions(driver);
var btn = wait.Until(ExpectedConditions.ElementIsVisible(By.CssSelector("#homefeatured > .ajax_block_product:nth-child(1) .button:nth-child(1) > span")));
action.MoveToElement(driver.FindElement(By.CssSelector("#homefeatured > .ajax_block_product:nth-child(1) .button:nth-child(1) > span"))).Build().Perform();
btn.Click();
Add the following to the top:
using OpenQA.Selenium.Interactions;
using OpenQA.Selenium.Support.UI;

How can I use the same ChromeDriver from another method

So I have create two methods which I want to use the same ChromDriver. I want the first method to do something with a ChromeDriver and the second method to "continue" the process with the same ChromeDriver.
static void FirstDriver()
{
IWebDriver Driver1 = new ChromeDriver();
Driver1.Navigate().GoToUrl("login page");
//login to the page
}
static void SecondDriver()
{
IWebDriver Driver2 = new ChromeDriver();
Driver2.Navigate().GoToUrl("login page");
//continue while still loged-in
}
I know that an alternative solution would be to get the cookies from Driver1 and use them for Driver2, but I don't know how to do that in selenium...
You need to create a Chrome Driver object out of the method so it can be used in multiple places.
Something like this should help get you started.
public static IWebDriver Driver { get; set; }
static void Main(string[] args)
{
Driver = new ChromeDriver();
// Both should use the same driver object.
FirstDriver();
SecondDriver();
}
static void FirstDriver()
{
Driver.Navigate().GoToUrl("login page");
//login to the page
}
static void SecondDriver()
{
Driver.Navigate().GoToUrl("login page");
//continue while still loged-in
}
Under the class define driver as global variable and use it
**static WebDriver driver;**
driver = new ChromeDriver();

WebDriver: explicitly wait doesn`t work by click to element

My code with implicitly wait works well. But I read information about waits and understood, that I need using explicitly wait in my projects. That`s why I am trying implement my test project with it.
When step of my alhorithm equel click to button, I have error : http://joxi.ru/BA0GMyDhnY0n2y
Please, help me with it.
Base class:
using NUnit.Framework;
using System;
using LinkedinAddContacts.Pages;
using LinkedinAddContacts.TestData;
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Support.UI;
namespace LinkedinAddContacts
{
[TestFixture]
public class TestClass
{
private IWebDriver webDriver;
private WebDriverWait waitDriver;
[SetUp]
public void InitializeBrowser()
{
webDriver = new ChromeDriver();
waitDriver = new WebDriverWait(webDriver, TimeSpan.FromSeconds(10));
webDriver.Manage().Window.Maximize();
webDriver.Manage().Timeouts().PageLoad = TimeSpan.FromSeconds(30);
webDriver.Navigate().GoToUrl("https://www.linkedin.com/");
}
[Test]
public void TestMethod()
{
Authorization authorizationData = new Authorization();
StartPage objStartPage = new StartPage(waitDriver);
NetworkPage objNetworkPage = new NetworkPage(waitDriver);
objStartPage.EntrySystem(authorizationData);
objNetworkPage.ConnectPeople();
}
[TearDown]
public void CloseBrowser()
{
webDriver.Quit();
}
}
}
Secondary class:
using NUnit.Framework;
using LinkedinAddContacts.TestData;
using OpenQA.Selenium;
using OpenQA.Selenium.Support.UI;
namespace LinkedinAddContacts.Pages
{
public class StartPage
{
// private IWebDriver webDriver;
private WebDriverWait waitDriver;
#region Objects
public StartPage(WebDriverWait waitDriver)
{
this.waitDriver = waitDriver;
}
private IWebElement EmailInput
{
get
{
return waitDriver.Until(ExpectedConditions.ElementToBeClickable(By.Name("session_key")));
//return webDriver.FindElement(By.Name("session_key"));
}
}
private IWebElement PasswordInput
{
get
{
return waitDriver.Until(ExpectedConditions.ElementToBeClickable(By.Name("session_password")));
// return webDriver.FindElement(By.Name("session_password"));
}
}
private IWebElement LoginButton
{
get
{
return waitDriver.Until(ExpectedConditions.ElementToBeClickable(By.Name("login-submit")));
//return webDriver.FindElement(By.Id("login-submit"));
}
}
private IWebElement RegistrationForm
{
get
{
return waitDriver.Until(ExpectedConditions.ElementToBeClickable(By.Id("regForm")));
// return webDriver.FindElement(By.Id("regForm"));
}
}
#endregion
#region Methods
public void CloseRegistrationForm()
{
IJavaScriptExecutor js = waitDriver as IJavaScriptExecutor;
js.ExecuteScript("document.getElementById('regForm').style.display = 'none';");
// ((IJavascriptExecutor)driver).executeScript("scroll(0,400)");
}
public void EntrySystem(Authorization authorizationData)
{
// CloseRegistrationForm();
EmailInput.SendKeys(authorizationData.Email);
PasswordInput.SendKeys(authorizationData.Password);
LoginButton.Click();
}
#endregion
}
}
Error there:
public void EntrySystem(Authorization authorizationData)
{
// CloseRegistrationForm();
EmailInput.SendKeys(authorizationData.Email);
PasswordInput.SendKeys(authorizationData.Password);
LoginButton.Click();
}
When I understand it properly your code crashes at this line:
return waitDriver.Until(ExpectedConditions.ElementToBeClickable(By.Name("login-submit")));
Now, taking a look at the startpage of linkedIn reveals that the login-submit button doesn't have a name attribute defined, but you can use it's id instead.
<input tabindex="1" id="login-submit" class="login submit-button" type="submit" value="Einloggen">
So you should be using By.id() instead of By.name().
It is important to notice which web driver you use.
First of all, As #Robert says, its better to find by Id whenever it is available to you.
Second, I think LoginButton.Click() does not work. I had such a problem with chrome driver. When page scale(zooming) is changed, the Click method does not work properly, or clicks elsewhere on the page.
I recommend you to use SendKeys for any click action.
Just like this:
LoginButton.SendKeys(Keys.Enter);// or Keys.Return
Never ever use Click method

Selenium Waits Logic

I am building a testing framework for my website
I want to fully separate the framework away from test
the issue is when I write a test sometimes the Assert needs time until it can be true, for example if I am on Upload file page and when the file is uploaded the website should display File uploaded successfully page but it will need to much time until the browser reaches this page
How should I force the Assert to wait sometime before it returns result?
some code that might explain my current way of work:
Upload Page Class
Public class UploadPage
{
[FindsBy(How = How.Name, Using = "upload-button")]
public IWebElement BtnUpload { get; set; }
public UploadPage()
{
PageFactory.InitElements(Driver, this);
}
public void UploadFile(string path)
{
//select file
BtnUpload.Click();
}
}
Successful Upload Page:
Public class UploadSuccessfulPage
{
[FindsBy(How = How.Name, Using = "success-message")]
public IWebElement LblSuccessMessage{ get; set; }
public UploadSuccessfulPage()
{
PageFactory.InitElements(Driver, this);
}
public bool IsAt()
{
Driver.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(60))
return Driver.url==configurations.UploadSuccessfulPageURL;
}
}
Test Method:
public void TestUpload()
{
UploadPage uploadPage= new UploadPage ();
uploadPage.UploadFile(path);
UploadSuccessfulPage successPage= new UploadSuccessfulPage();
Assert.IsTrue(successPage.IsAt());
}
when I write my tests this way the assert do not wait despite that IsAt() contains implicit wait
P.S: I am not intending to use Thread.Sleep();
The method bool IsAt() should be implemented like:
public bool IsAt()
{
Driver.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(60));
try {
return driver.FindElement(By.Name("success-message").Displayed;
} catch (WebDriverException e) {
return false;
}
}
Or using explicit wait:
public bool IsAt()
{
try {
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(60));
wait.Until(ExpectedConditions.ElementIsVisible(By.Name("success-message")));
return true;
} catch (TimeoutException e){
return false;
}
}
Update:
If you want to verify by url, the bool IsAt() should look like:
public bool IsAt()
{
try {
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(60));
wait.Until(ExpectedConditions.UrlToBe(configurations.UploadSuccessfulPageURL));
return true;
} catch (TimeoutException e){
return false;
}
}
Look at the class ExpectedConditions to find a condition suitable to your require

Get IWebDriver from setupfixture

First sorry about my English.
Here is my problem:
I make a test for mantisbt with many test cases(report issue), so i put the login in [SetUpFixture] and in [TestFixture] [Test, TestCaseSource("function")] I don't know how to get driver which i use for creating chrome browser to get elements.
Here is my code:
namespace testcailz
{
[SetUpFixture]
public class TestsSetupClass
{
public void login(IWebDriver driver)
{
IWebElement username = driver.FindElement(By.Name("username"));
username.SendKeys("1353049");
IWebElement password = driver.FindElement(By.Name("password"));
password.SendKeys("123456");
IWebElement login = driver.FindElement(By.XPath("//input[#value='Login'][#class='button']"));
login.Click();
}
[SetUp]
public void GlobalSetup()
{
IWebDriver driver = new ChromeDriver();
driver.Navigate().GoToUrl("http://www.cs.hcmus.edu.vn/mantisbt/login_page.php");
login(driver);
}
[TearDown]
public void GlobalTeardown()
{
// Do logout here
}
}
[TestFixture]
public class Class1
{
private static int[] data()
{
return new int[3] { 1, 2, 3 };
}
[Test, TestCaseSource("data")]
public void TestCaiLz(int i)
{
//wanna click to report new issue but how to get driver for Findelement
Assert.AreEqual(i, i);
}
}
}
As per java prospective, create driver object globally in class may be TestsSetupClass
public static WebDriver driver;
#BeforeSuite
public void startUp(){
driver=new FirefoxDriver();
driver.manage().window().maximize();
login(driver);
}
If you what to use this driver in another classes then extends this class. like below in java
public class Home extends Setup{ //...
}
Thank You,
Murali

Categories