CSharp selenium Creating generic Interface that will reference each implementations readonly fields using intellisense when referencing

Viewed 38

I need to build a class that will allow each implementation to pull up fields in intellisense. It has to be implemented in a repeatable Interface where each class has different field names. I can't change the format of the strings each field returns and the field name needs to be very close to the string value it returns.

page.Fields.EACH NAME HERE FROM INTELLISENSE..(.BIR_DAT_TAG_NAME, NAME_ID_FOR_PAGE, CO_NAME_ID, etc....

I would really prefer not to cast using each implementation if at all possible for several reasons I wont try to explain here as they are not relevant to the problem I am trying to solve. ((SetAllValues.WebFields)page.Fields).CO_NAME_ID

//implementation in test
    [Fact()]
    public void SetAllValues()
    {
        _driver.Navigate().GoToUrl( @"\..\WebPages\TestScreen.htm");
        IWebBasePage webBasePage = new BasePage(_driver);
         IWebDataInteraction page = new SomePage(webBasePage);

        vars.Add(page.Fields.**BIR_DAT_TAG_NAME**, "03/01/2001");
        vars.Add(page.Fields.**EML_OPT_IN**, ComboBoxState.Checked);
     
        page.setData(vars);

        Assert.True(true);
    }


public interface IWebDataInteraction
{
    IData getData();
    void setData(IDictionary<string, object> data);
    IFields Fields { get; }
}


public class SomePage : IWebDataInteraction
{
   protected IWebBasePage webBasePage;

   // Properties that return SomePage Form Inputs by name
   public string Name {get "NAME-ID-FOR-PAGE"} //Label
   public string BirthDate {get "BIR-DAT-TAG-NAME"} //TextBox
   public string CompanyLocation {get "CO-NAME-ID"} //Combobox
   public string GetEmailOptIn {get "EML-OPT-IN"} //Checkbox
    
   public SomePage(IWebBasePage BasePage)
   {
        webBasePage = BasePage;
        _fields = new WebFields();
   }


    ***//Reference fields for reference***
    public class WebFields : IFields
    {
        public static readonly string NAME_ID_FOR_PAGE = "NAME-ID-FOR-PAGE"; 
        public static readonly string BIR_DAT_TAG_NAME = "BIR-DAT-TAG-NAME";
        public static readonly string CO_NAME_ID = "CO-NAME-ID";
        public static readonly string EML_OPT_IN = "EML-OPT-IN";
    }

    private WebFields _fields;

    public IFields Fields {get => _fields;}

    public void setData(IDictionary<string, object> data)
    {
     //if field is found on page and has valid value setvalue, 
     click checkbox or radio, etc
    }
///rest of the class 
}

Adding Back more context from original post I changed.

I am building an API to allow other members of QA to automate processes quicker with less programing experience. It will also help in several other ways such as reporting, troubleshooting, etc... Each class will be a representation of a specific page, in each of my companies different websites, that will be automatically scripted as a class from a parser I built to converts the html page to a class. I want to be able to reference each pages form input elements by the html tag reference. The tags name or id is structured something like "XXX-XXX-XXX-XX". Since this is not very readable the property names that reference this element tag will be more readable like.

public string Name {get "NAME-ID-FOR-PAGE"}

The class will have several methods that will use these properties to interact with page. Things like clicking checkbox, entering text in textbox, reading value of label, etc. Because the page is scripted as a class then other members of QA team can use the page class as part of an action for a process and they don't have to figure out what each page contains and how to build references to each page when they need to quickly build a specific testing process or automate a specific process. I also want to be able to send in Data to a page and based on what data is sent in the correct html form inputs will be automatically be set. To be able to build the data for each webpage I want a reference to each webpages elements that can be pulled up using intellisense. I started with enum Fields in my interface but found that wouldn't work for several reasons. What I want is the field string to be referenced from an implementation of the Interface. I cant use the property name for a few reasons that basically boil down to the scripting from html to cs class.

So when I reference the instance of a Webpage I want to see fields in intellisense //...... page.Fields.EACH NAME HERE FROM INTELLISENSE..(.BIR_DAT_TAG_NAME, NAME_ID_FOR_PAGE, CO_NAME_ID, etc....

1 Answers

What you want to do is not possible in a strongly-typed language like C#. You cannot have something "generic" and "specific" at the same time. Looking at the code, it appears the page object model design pattern is not being implemented properly. If you need to interact with a certain page, you need to create a page model class that specializes in interacting with that particular page.

Judging by the code in your question, you appear to be automating some sort of "sign up" page. As an example, this is what your page model should look like:

public class SignUpPage
{
    private readonly IWebDriver driver;

    private IWebElement NameField => driver.FindElement(...);
    private IWebElement BirthDateField => driver.FindElement(...);
    private IWebElement CompanyLocationField => driver.FindElement(...);
    private IWebElement EmailOptInCheckbox => driver.FindElement(...);

    public SignUpPage(IWebDriver driver)
    {
        this.driver = driver;
    }

    public void SetData(string name = null, string birthDate = null, string companyLocation = null, bool? emailOptIn = null)
    {
        if (name == null)
            NameField.Clear();
        else
            NameField.SendKeys(name);
    
        if (birthDate == null)
            BirthDateField.Clear();
        else
            BirthDateField.SendKeys(birthDate);

        if (companyLocation == null)
            CompanyLocationField.Clear();
        else
            CompanyLocationField.SendKeys(companyLocation);

        if (emailOptIn == null || emailOptIn)
        {
            // Do nothing. E-mail opt-in checkbox is checked by default
        }
        else
        {
            EmailOptInCheckbox.Click();
        }
    }
}

Your test would be simplified:

[Fact()]
public void SetAllValues()
{
    _driver.Navigate().GoToUrl( @"\..\WebPages\TestScreen.htm");

    var signUpPage = new SignUpPage(_driver);

    signUpPage.SetData(birthDate: "03/01/2001", emailOptIn: true);

    Assert.True(...);
}

This allows you to design proper abstractions for interacting with web pages. This becomes especially important once you need to introduce explicit waits in your automation code, because some animation or transition effect is running, which can cause ElementNotInteractableException's, among other problems. A proper page model allows you to guard against race conditions between your automation code and what happens in the browser when you interact with it.

Related