Patrick Magee
Patrick Magee

Reputation: 2989

Selenium WebDriver PageFactory FindsBy Using jQuery Selector?

To explain my question, I have given a small scenario:

Say I have a login page.

public class LoginPage
{
    [FindsBy(How = How.Id, Using = "SomeReallyLongIdBecauseOfAspNetControlsAndPanels_username"]
    public IWebElement UsernameField { get; set; }

    [FindsBy(How = How.Id, Using = "SomeReallyLongIdBecauseOfAspNetControlsAndPanels_password"]
    public IWebElement PasswordField { get; set; }

    [FindsBy(How = How.Id, Using = "submitButtonId")]
    public IWebElement SubmitButton { get; set; }

    private readonly IWebDriver driver;

    public LoginPage(IWebDriver driver)
    {
        this.driver = driver;

        if(!driver.Url.Contains("Login.aspx"))
        {
            throw new NotFoundException("This is not the login page.");
        }
        PageFactory.InitElements(driver, this);
    }

    public HomePage Login(Credentials cred)
    {

       UsernameField.sendKeys(cred.Username);
       PasswordField.SendKeys(cred.Password);
       SubmitButton.Click();

       return new HomePage(driver);
    }

}

[TestFixture]
public class Test : TestBase
{
    private IWebDriver driver;

    [SetUp]
    public void SetUp()
    {

       driver = StartDriver(); // some function which returns my driver in a wrapped event or something so I can log everything it does.
    }

    [Test]
    public void Test()
    {
        new LoginPage(driver)
                .Login(new Credentials 
                           { Username = "username", 
                             Password = "password" })
                .SomeHomePageFunction()

    }

Eventually, I know the page configuration will change, the id's will mostly stay the same, but things are changing rapidly on my projects. I know xPath is another alternative, but due to how the pages are generated based on certain critera, this will still become painful as the path will not always be the same.

With the current code above, the page is loaded and the PageFactory init's the elements through the Page Constructor. All great. This is what I use at the moment.

Currently, if some things are not always generated on the page until a certain step. I usually do the following:

private const string ThisIsTheUserNameFieldId = "usernamefield";

Then hit up the webdriver using the following:

// Navigate to login page

// code here

// Enter in credentials

driver.FindElement(By.Id(ThisIsTheUserNameFieldId)).SendKeys(cred.Username);

Not as well structured as the PageFactory, but it's certainly a requirement which I am not able to get around.

I have recently come across some jQuery Selector code to use with C#.Net which extends the functionality of the RemoteWebDriver where I can use jQuery selectors to find my Elements on the page.

Selenium jQuery for C#.Net (Including Source)

// So I can do things like this:
driver.FindElement(By.jQuery("a").Find(":contains('Home')").Next())

Does anyone know how I can extend the [FindsBy] Attribute in Selenium WebDriver so that it's possible to use something like the following (pseudo code)?

[FindsBy(How = How.jQuery, Using = "div[id$='txtUserName']")]
public IWebElement UsernameField { get; set; }

Upvotes: 4

Views: 9433

Answers (1)

Anders
Anders

Reputation: 15397

This doesn't extend [FindsBy], but did you know you can use elements returned by javascript?:

var driver = new FirefoxDriver { Url = "http://www.google.com" };
var element = (IWebElement)((IJavaScriptExecutor)driver).ExecuteScript("return document.getElementsByName('q')[0];");
element.SendKeys("hello world");

You could easily extend this to allow for jquery selectors by first injecting jquery (taken from JQuerify and modified):

const string js =
     @"{var b=document.getElementsByTagName('body')[0]; if(typeof jQuery=='undefined'){var script=document" +
     @".createElement('script'); script.src='http://code.jquery.com/jquery-latest.min.js';var head=document" +
     @".getElementsByTagName('head')[0],done=false;script.onload=script.onreadystatechange=function(){if(!" +
     @"done&&(!this.readyState||this.readyState=='loaded'||this.readyState=='complete')){done=true;script." +
     @"onload=script.onreadystatechange=null;head.removeChild(script);}};head.appendChild(script);}}";
((IJavaScriptExecutor)driver).ExecuteScript(js);

And then running javascript to select the element you want:

var driver = new FirefoxDriver { Url = "http://www.google.com" };
var element = (IWebElement)((IJavaScriptExecutor)driver).ExecuteScript(@"return $('input[name*=""q""]')[0];");
element.SendKeys("hello world");

Upvotes: 4

Related Questions