2

I am using Wait.Until method to check if my page is already loaded or if it still loading . This is how it looks like :

protected IWebElement FindElement(By by, int timeoutInSeconds)
    {
        StackTrace stackTrace = new StackTrace();
        string callingMethod = stackTrace.GetFrame(1).GetMethod().Name;

        string message = "Error finding element in method: " + callingMethod;

        if (timeoutInSeconds > 0)
        {
            try
            {
                WebDriverWait wait = new WebDriverWait(chromeDriver, TimeSpan.FromSeconds(timeoutInSeconds));
                wait.Until(ExpectedConditions.ElementIsVisible(by));
                Thread.Sleep(800);
            }
            catch (Exception)
            {
                Assert(false, message);
                throw new Exception(message);
            }
        }

        return chromeDriver.FindElement(by);
    }

But now we want to change our automation pages and start using FindBy foe every element , like this :

  [FindsBy(How = How.Id, Using = "username")]
    public IWebElement _logInUserName;

but wait.until needs "by" element .

I saw the abstract solution for this problem , but it is no good for my case . can anyone know another solution that i can use ?

Daria Shalimov
  • 131
  • 1
  • 1
  • 8

3 Answers3

2

There is a ByFactory class in Selenium .NET solution. I took this implementation to achieve what you want:

using OpenQA.Selenium;
using OpenQA.Selenium.Support.PageObjects;
using System;
using System.Globalization;
using System.Linq;
using System.Reflection;

namespace SeleniumPlayground
{
    public static class SeleniumHelper
    {
        public static FindsByAttribute GetFindsByAttributeFromField(Type pageObject, string iwebElementFieldName)
        {

            FieldInfo fi = pageObject.GetField(iwebElementFieldName);

            FindsByAttribute attr = (FindsByAttribute)fi.GetCustomAttributes(typeof(FindsByAttribute), false).FirstOrDefault();

            return attr;
        }

        public static By GeyByFromFindsBy(FindsByAttribute attribute)
        {
            var how = attribute.How;
            var usingValue = attribute.Using;
            switch (how)
            {
                case How.Id:
                    return By.Id(usingValue);
                case How.Name:
                    return By.Name(usingValue);
                case How.TagName:
                    return By.TagName(usingValue);
                case How.ClassName:
                    return By.ClassName(usingValue);
                case How.CssSelector:
                    return By.CssSelector(usingValue);
                case How.LinkText:
                    return By.LinkText(usingValue);
                case How.PartialLinkText:
                    return By.PartialLinkText(usingValue);
                case How.XPath:
                    return By.XPath(usingValue);
                case How.Custom:
                    if (attribute.CustomFinderType == null)
                    {
                        throw new ArgumentException("Cannot use How.Custom without supplying a custom finder type");
                    }

                    if (!attribute.CustomFinderType.IsSubclassOf(typeof(By)))
                    {
                        throw new ArgumentException("Custom finder type must be a descendent of the By class");
                    }

                    ConstructorInfo ctor = attribute.CustomFinderType.GetConstructor(new Type[] { typeof(string) });
                    if (ctor == null)
                    {
                        throw new ArgumentException("Custom finder type must expose a public constructor with a string argument");
                    }

                    By finder = ctor.Invoke(new object[] { usingValue }) as By;
                    return finder;
            }

            throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "Did not know how to construct How from how {0}, using {1}", how, usingValue));
      }
}

And here's an example usage:

public class Page
{
    private IWebDriver driver;

    [FindsBy(How = How.Id, Using = "content")]
    public IWebElement ele;

    public Page(IWebDriver _driver)
    {
        this.driver = _driver;
    }
}

Use as follows:

Page page = PageFactory.InitElements<Page>(driver);

FindsByAttribute findsBy = SeleniumHelper.GetFindsByAttributeFromField(typeof(Page), "ele");

By by = SeleniumHelper.GeyByFromFindsBy(findsBy);
Moshisho
  • 2,781
  • 1
  • 23
  • 39
1

I found a way to o this :)

        public static IWebElement FindElement( IWebElement element, int timeoutInSeconds)
    {
        if (timeoutInSeconds > 0)
        {
            var wait = new WebDriverWait(chromeDriver, TimeSpan.FromSeconds(timeoutInSeconds));
            return wait.Until(drv => element);
        }
        return element;
    }
Daria Shalimov
  • 131
  • 1
  • 1
  • 8
1

We faced the same issue when using selenium for testing. So we created a mini framework on top of selenium which keeps trying to do (whatever you are trying to do with selenium). Or otherwise you can provide a custom pre or post condition.

https://github.com/LiquidThinking/Xenon

It is very simple to setup and all information is available on github, plus it comes with Screen objects which can help to reuse your code.

For example

new XenonTest(new SeleniumXenonBrowser())
        .GoToUrl("http://www.google.co.uk", a => a.PageContains("google") );

So in this example, we added a pre wait condition which says "before going to google.co.uk make sure that the current page contains "google" in page source. Which is obviously incorrect way to do it but it explains how to use pre or post wait conditions.

If you do not specify any wait condition then for some actions, there is a default wait action. for e.g. https://github.com/LiquidThinking/Xenon/blob/master/Xenon/BaseXenonTest.cs#L72 See how we check if a customPreWait is available for "Click" and if not then we added a custom pre-wait to check if that css selectors exists on the page before performing the "actual click action".

Hope it will help you, it is on nuget or otherwise just use the code which you want. it is under MIT license.

adeel41
  • 3,123
  • 1
  • 29
  • 25