Passing a condition for click in Selenium - c#

I saw this line of code someone wrote once that essentially resolves a condition for a selenium click by passing in the webdriver, some soft of boolean condition, and a Timespan to wait for the condition to be fulfilled. It looks like this:
_driver.WaitForClick(By.XPath("xpath value"), () =>
{
return _driver.IsElementVisible(By.XPath("//div[#class='some value']"));
}, TimeSpan.FromMilliseconds(250));
What i want to know is how to build the same method. How can i build a custom driver click method like this? Please help. New to C# Can someone share some sample code?

In order to implement such a method you need to understand several concepts first. I won't be explaining all of them and would request you to search and read.
Explicit waits in selenium - Explicit Waits . Toggle C# as language of choice.
Extension methods in C#
Func delegate in C# Func delegate
Here is a sample implementation with a slightly modified signature i.e. this implementation doesn't require By.Xpath/By.Id etc as first parameter:
using OpenQA.Selenium;
using OpenQA.Selenium.Support.UI;
using System;
namespace SeleniumWebAutomation
{
public class WebElementWait : DefaultWait<IWebElement>
{
public WebElementWait(IWebElement element, TimeSpan timeout) : base(element, new SystemClock())
{
this.Timeout = timeout;
this.IgnoreExceptionTypes(typeof(NotFoundException));
}
}
public static class WebDriverExtension
{
public static void WaitAndClick(this IWebDriver driver, Func<IWebDriver,IWebElement> condition,TimeSpan timeSpan)
{
IWebElement webElement = new WebDriverWait(driver, timeSpan).Until(condition);
webElement.Click();
}
}
}
You can invoke it as shown below now :
//Wait for element to exist for up to 10 second before performing click operation
driver.WaitAndClick(ExpectedConditions.ElementExists(By.XPath("xpath value")),TimeSpan.FromMilliseconds(10000));
//Wait for element to be visible for up to 5 second before performing click operation
driver.WaitAndClick(ExpectedConditions.ElementIsVisible(By.Id("Id")),TimeSpan.FromMilliseconds(5000));
Please note that i have used the inbuilt helper class ExpectedConditions
which has several methods such as ElementExists(),ElementIsVisible(),etc. which returns a Func given a locator. If element is not found /not visible respectively in provided examples within specified timeout, exception will be thrown.
Regards,
Nish26

Related

Can't verify a modal is showing with Selenium in C#

I'm making some automated tests with Selenium in C#. It's an ASP.NET MVC Core 3 application, using xUnit for the Unit Tests and we are using Bootstrap as the UI framework. I have a problem with verifying that a modal is showing on screen.
When I run this test:
// HomePageAutomatedUITests.cs
[Fact]
public void Try_Open_Arkivskaber_Modal()
{
_page.ClickArkivskaberNyButton();
string modalValue = _page.GetArkivskaberModalAttribute("class");
Assert.Contains("show", modalValue);
}
It always fails, telling me that the Assert.Contains() is never true. The Class string is normally modal fade but when the button is pressed its supposed to add the class "show" so it becomes modal fade show. This works when a user does it on the website, but for some reason I can't make it work in this test. The test tells me that the "modalValue" reads modal fade so it is at least reading the correct place.
The _page variable is a Page Object Model and the following two properties and methods are used in the test:
// HomePage.cs
private IWebElement ArkivskaberNyButton => _driver.FindElement(By.Id("arkivskaber-ny"));
public void ClickArkivskaberNyButton() => ArkivskaberNyButton.Click();
private IWebElement ArkivskaberModalWindow => _driver.FindElement(By.Id("opretArkivskaber"));
public string GetArkivskaberModalAttribute(string attrName) => ArkivskaberModalWindow.GetAttribute(attrName);
I have checked the id arkivskaber-ny I am passing is correct.
I'm running all the tests I do Headless using the Chrome Driver. What might be wrong here?
If anyone else runs into this issue then here is the solution:
Use the WebDriverWait class from the OpenQA.Selenium.Support.UI namespace. You'll also need to get the DotNetSeleniumExtras.WaitHelpers package from NPM or Github.
Then in your code you can do this:
// HomePageAutomatedUITests.cs
public void Arkivskaber_Open_NyArkivskaber_Modal()
{
WebDriverWait waitDriver = new WebDriverWait(_driver, TimeSpan.FromSeconds(2));
_page.ClickArkivskaberNyButton();
waitDriver.Until(_page.GetArkivskaberModalWaitFunc());
string modalValue = _page.GetArkivskaberModalAttribute("class");
Assert.Contains("show", modalValue);
}
In your POM (if you use those) you do this:
// HomePage.cs
using ExpectedConditions = SeleniumExtras.WaitHelpers.ExpectedConditions;
...
public Func<IWebDriver, IWebElement> GetArkivskaberModalWaitFunc() => ExpectedConditions.ElementIsVisible(By.XPath("//div[#id='modal-window-id-here']"));
If you don't use a POM, then you can just make the ExpectedConditions.ElementIsVisible() call straight in your Test function. The key here is how long you wait for the modal to appear after clicking. If you set this too low, it might not have time to appear after you click.
Hope this helps others.

Invoking an event listener in nunit 3 using C# and Selenium Webdriver

I have read and understood how to set up an event listener (I think) for Nunit3 by reading https://github.com/nunit/docs/wiki/Event-Listeners but what I don't understand is how to invoke it. I understand it needs to be invoked outside the structure I just need a solid example.
EDIT: I cannot find a valid example online after about 3 hours of searching. Maybe I am not searching the right context or syntax. Here is what I have tried so far:
The decleration:
[Extension(EngineVersion="3.4")]
public class MyEventListener : ITestEventListener
{
OnTestResult(string report){
....
}
}
The caller:
public class DriverSetup(){
driver = new EventFiringWebDriver FiringDriver;
public void RegisterHandler(){
MyEventListener listener = new MyEventListener;
}
}
I can't figure how to attach the handler to the driver as in C# there is no 'register' method like in Java.

C# Webdriver - Page Title assert fails before page loads

This issue began when I switched from testing on the www website to my localhost version of it. Working in VS 2012, I will begin debugging so the localhost is active, detach the process so I can test on it, then run any test I like. For a very basic example:
[Test]
public void CanGoToHomePage()
{
Pages.HomePage.Goto();
Assert.IsTrue(Pages.HomePage.IsAt());
}
And the functions it references are here:
public class HomePage
{
const string Url = "http://localhost:3738";
const string HomepageTitle = "FunnelFire - Home Page";
public void Goto()
{
Browser.Goto(Url);
}
public bool IsAt()
{
return Browser.Title == HomepageTitle;
}
}
And the actual selenium code here:
public static class Browser
{
static IWebDriver webDriver = new FirefoxDriver();
public static void Goto(string url)
{
webDriver.Manage().Window.Maximize();
webDriver.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(10));
webDriver.Url = url;
}
}
Now the issue. The 10 second implicit wait that I added in Browser does successfully wait at most 10 seconds after loading a page to see if it can locate whatever information I want it to find, that is not the problem.
As I said earlier, after I switched to testing on localhost, suddenly I ran into a strange issue where a page would begin to load (i.e. screen still totally white, nothing finished) or even sometimes the next page would JUST barely finish loading and suddenly the test would just up and fail, pointing to the Assert of IsAt returning false even though the page it was loading was the correct one. I could run that test immediately once more and it would pass without a problem. Run it a third time and it could randomly fail again. I'm honestly not sure what is causing the issue and any help would be appreciated!
Implicit waits work only for finding elements. For waiting on the title of the page to be a certain value, you'll want to use an explicit wait. You can write your own version of this pattern, but in the .NET bindings, the WebDriver.Support.dll assembly has a WebDriverWait class to help with this. Its use would look something like this:
// WARNING! Untested code written from memory below. It has not
// been tested or even compiled in an IDE, so may be syntactically
// incorrect. The concept, however, should still be valid.
public void WaitForTitle(IWebDriver driver, string title, TimeSpan timeout)
{
WebDriverWait wait = new WebDriverWait(driver, timeout);
wait.Until((d) => { return d.Title == title; });
}
You could even modify your IsAt method to use this pattern, catching the WebDriverTimeoutException and returning false if the wait function times out.

Explicit Waits in page object model

I am trying to use the Explicit Waits in page object model to test a single page app , The problem i am facing is that the Explicit wait starts checking for the element as soon the page is called up ,since the page completely dynamic some element only appear after a some action . Since the element is checked even before its created it always troughs a error "No such Element exception"
Sample Code:
public class example
{
private IWebElement ExampleElement = wait.Until<IWebElement>((d) =>
{
return d.FindElement(By.Id("ExampleElement ID"));
});
private IWebElement ExampleElement2 = wait.Until<IWebElement>((d) =>
{
return d.FindElement(By.Id("ExampleElement ID"));
});
public void example1()
{
ExampleElement.click();
ExampleElement2.click();
}
}
public class exampleTest
{
[Test]
public void SomeTest()
{
example.example1();
}
}
The ExampleElement2 only loads after the ExampleElement click but what happens is it keeps checking for the ExampleElement2 even before the click action so it is not able to find it and it gives a exception . The thing is i dont want the element to checked for exsistence even before its called for .
I am completely new , thanks in advance
It seems that you have a pattern issue here. Your WebElements are at class level, so when the instance of the class is initiated webdriver is trying to initialize everything and therefore waiting even for Element2. If Element2 is dynamic then you can't wait for it at class level. Wait for it after the condition you expect it to appear.
I would say just have at class level,
private IWebElement ExampleElement2;
and then, do
public void example1()
{
ExampleElement.click();
ExampleElement2 = wait.Until<IWebElement>((d) =>
{
return d.FindElement(By.Id("ExampleElement ID"));
});
ExampleElement2.click();
}
Also you might want to look at PageFactory. You don't want to repeat driver.findElement all over your code and other obvious advantages which are off this topic :-).
As nilesh mentioned in his answer, it's required to defer the loading of the ExampleElement2. However it should be an explicit wait and you need to wait for relevant expected condition for example you can say wait until ElementIsVisible (in Java API you can put specific conditions like elementToBeClickable based on the element type). Then it makes sure the element satisfies the particular condition you passed.
P.S - I am sorry I couldn't help you through a code snippet. Since I am a Java guy I haven't done any Selenium tests using C#. But the principle is same.

Reactive Extensions Web Server

Just out of personal interest, I decided to try to write a basic web server using Reactive Extensions (aka Rx). Almost immediately, I came up against the following problem:
HttpListener's async pattern doesn't match the normal async pattern signature, the EndGetContext returns a HttpListenerContext instead of an IAsyncResult!
So my question is: is there a way around this?
Here's some code - the compiler (rightly) complains about the second argument to Observable.FromAsyncPattern:
Update - As Jon rightly pointed out, it wasn't the pattern, it's complaining that I haven't provided the return type - this works:
var observable = Observable.FromAsyncPattern<HttpListenerContext>(listener.BeginGetContext, listener.EndGetContext);
The original code:
using System;
using System.Net;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace RxWebTest
{
class Program
{
static void Main(string[] args)
{
HttpListener listener = new HttpListener();
try
{
listener.Start();
var observable = Observable.FromAsyncPattern(listener.BeginGetContext, listener.EndGetContext);
Console.ReadLine();
}
finally
{
listener.Abort();
}
}
}
}
That is the normal pattern. It's BeginGetContext which returns IAsyncResult; EndGetContext takes an IAsyncResult and returns the result of the asynchronous operation - the context. That's exactly the same as (say) Stream.BeginRead/EndRead, where EndRead returns an int, the number of bytes read.
What happens when you try the code you've posted? I can't immediately see why it wouldn't work.
The trick to figuring out the template parameters for FromAsyncPattern, is to imagine you're creating a Func that is matching the synchronous version of the function.
In this case, the prototype is:
public HttpListenerContext GetContext()
Which means, my Func would look like:
Func<HttpListenerContext>
Then, just take your template parameter and copy-paste it to the FromAsyncPattern.

Categories