1

I am using Selenium and I have the following extensions methods for executing javascript.

    private const int s_PageWaitSeconds = 30;

    public static IWebElement FindElementByJs(this IWebDriver driver, string jsCommand)
    {
        return (IWebElement)((IJavaScriptExecutor)driver).ExecuteScript(jsCommand);
    }

    public static IWebElement FindElementByJsWithWait(this IWebDriver driver, string jsCommand, int timeoutInSeconds)
    {
        if (timeoutInSeconds > 0)
        {
            var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(timeoutInSeconds));
            wait.Until(d => d.FindElementByJs(jsCommand));
        }
        return driver.FindElementByJs(jsCommand);
    }

    public static IWebElement FindElementByJsWithWait(this IWebDriver driver, string jsCommand)
    {
        return FindElementByJsWithWait(driver, jsCommand, s_PageWaitSeconds);
    } 

In my HomePage class I have the following attribute.

public class HomePage : SurveyPage
{
    [FindsBy(How = How.Id, Using = "radio2")]
    private IWebElement employerSelect; 

    public HomePage(IWebDriver driver) : base(driver)
    {
    }

    public override SurveyPage FillOutThisPage()
    {
        employerSelect.Click();
        employerSelect.Submit();
        m_driver.FindElement(By.)
        return new Level10Page(m_driver); 
    }
}

However, employerSelect is generated by Javascript, so is there a way to do something like this:

public class HomePage : SurveyPage
{
    const string getEmployerJsCommand= "return $(\"li:contains('Employer')\")[0];";

    [FindsBy(How = How.FindElementByJsWithWait, Using = "getEmployerJsCommand")]
    private IWebElement employerSelect; 

    public HomePage(IWebDriver driver) : base(driver)
    {
    }

    public override SurveyPage FillOutThisPage()
    {
        employerSelect.Click();
        employerSelect.Submit();
        m_driver.FindElement(By.)
        return new Level10Page(m_driver); 
    }
}

In essence, I want to replace the raw ExecuteJs call as part of the FindsBy attribute such as:

    const string getEmployerJsCommand = "return $(\"li:contains('Employer')\")[0];";
    IWebElement employerSelect = driver.FindElementByJsWithWait(getEmployerJsCommand);

into part of the FindsBy attribute like this:

    const string getEmployerJsCommand= "return $(\"li:contains('Employer')\")[0];";

    [FindsBy(How = How.FindElementByJsWithWait, Using = "getEmployerJsCommand")]
    private IWebElement employerSelect; 

What could I extend to do something like that?

perryzheng
  • 199
  • 1
  • 8

1 Answers1

0

Unfortunately, FindsByAttribute is sealed in Selenium. As a result you cannot override it to add new behaviuor. And How is an Enum so there is no way to to add new values without overriding the entire attribute class.

So there is no way to do what you would like currently.

However, I don't think that having FindsBy be sealed was a deliberate design decision, but a result of the fact that .NET Selenium is a direct port of Java Selenium. In Java all classes are "sealed" by default and have to be explicitly marked virtual to allow for overriding. C# is the opposite, and I think most classes were marked sealed during the Java-C# port as a result without consideration as to whether it should be allowed or not.

Up until the latest version of Selenium, the By class was also sealed, and this was just fixed, and I'm taking advantage of it to create my own By lookups. (using jQuery selectors). I'm sure one could submit a pull request to do the same for FindsBy

Nik Pinski
  • 317
  • 3
  • 9
  • you are wrong, see http://stackoverflow.com/questions/14263483/how-to-write-my-own-customize-locator-for-selenium-webdriver-in-java – Liraz Shay Nov 15 '16 at 09:22