Screen Repository

Our hand written screen class:

    public class CreateCustomerStep1Screen
    {
        private readonly Window window;
        private readonly Application application;

        public CreateCustomerStep1Screen(Window window, Application application)
        {
            this.window = window;
            this.application = application;
        }

        public CreateCustomerStep2Screen FillAndNext(Customer customer)
        {
            window.Get<TextBox>("nameTextBox").BulkText = customer.Name;
            window.Get<TextBox>("ageTextBox").BulkText = customer.Age;
            window.Get<Button>("nextButton").Click();
            Window step2 = application.GetWindow("Create Customer Step2", InitializeOption.NoCache);
            return new CreateCustomerStep2Screen(step2, application);
        }
    }

and the test:

    [Test]
    public void Create()
    {
        DashboardScreen dashboardScreen = new DashboardScreen(window, application);
        Customer customer = new Customer("Rakesh Kumar", "26", "34343545");
        CreateCustomerStep1Screen step1Screen = dashboardScreen.LaunchCreateCustomer();
        step1Screen.FillAndNext(customer);
        CreateCustomerStep2Screen step2Screen = new CreateCustomerStep2Screen(window, application);
        step2Screen.Fill(customer);
    }

In the screen class, lets look closely at how we are interacting with UIItems. That is populating the Textbox and clicking on button in this case. We are: asking the window for a UIItem specifying the type of the UIItem specifying the identification of UIItem calling the UIItem method, optional passing the value

White helps in doing step 1,2 and 3 easily. Screen Repository and Screen Recorder frameworks which works on top of White Core does this. These are available as Repository.dll and Recorder.exe respectively in white distribution. Instead of evolving from what we have in above example lets see the end result first.

Till now we have been writing the screen class by hand. We can change out CreateCustomerStep1Screen to look like this by introducing a few fields and inheriting from AppScreen:

    public partial class CreateCustomerStep1Screen : AppScreen
    {
        protected TextBox NameTextBox;
        protected TextBox AgeTextBox;
        protected Button NextButton;
        protected Button CancelButton;

        protected CreateCustomerStep1Screen() {}

        public CreateCustomerStep1Screen(Window window, ScreenRepository screenRepository) : base(window, screenRepository) {}

        public virtual void FillAndNext(Customer customer)
        {
            NameTextBox.BulkText = customer.Name;
            AgeTextBox.BulkText = customer.Age;
            NextButton.Click();
         }
    }

And this is how the test uses the screen

    [Test]
    public void Create()
    {
        ScreenRepository screenRepository = new ScreenRepository(application.ApplicationSession);
        Dashboard dashboard = screenRepository.Get<Dashboard>("Dashboard", InitializeOption.NoCache);
        CreateCustomerStep1Screen customerStep1Screen = dashboard.LaunchCreateCustomer();

        Customer customer = new Customer("Rahul", "20", "4366654");
        CreateCustomerStep2Screen customerStep2Screen = customerStep1Screen.FillAndNext(customer);
        customerStep2Screen.Finish(customer);
    }

ScreenRepository as the name suggests is the repository of the screens. All the screens should be retrieved from it. ScreenRepository is initialized for a particular application. When asked for a window it internally calls the application to find the window and creates a screen object. But before returning it to the caller it initialized all the fields in the screen class. (The screen class, as shown above, consists of all the UIItems present in the window).

The initialization of field in screen class works on the name of the field and the type of the UIItem defined. e.g. for a field defined like this: protected TextBox NameTextBox;

ScreenRepository would find it by, calling window.Get<TextBox>("NameTextBox");

Or in other words, white supports convention (over configuration) based approach. You can Change ScreenRepository field load strategy (see below). ScreenRepository puts a proxy object for UIItem to avoid initialization until needed. This is done keeping the performance of screens which has a more than handful of UIItems.

Not having to initialize UIItems in screen class, reduces the clutter in the screen class code and makes the work of test developer little easier. Since the screen can change during the course of development of application, it is a good idea to keep the generated and hand code class in separate files.

Change ScreenRepository field load strategy

ScreenRepository (factory for screen classes) populates the fields in the screen class generated by the Recorder. The strategy for doing this is based on the type of the UIItem and the name of the field. The name of field is translated into AutomationId for managed application (under test) and UIAutomation Name property in unmanaged (aut). This behavior can be changed by putting attributes on these fields. Following are the attributes which you can use: AutomationId, Index, FrameworkId and Text. These attributes tell the ScreenRepository about the criteria based on which this particular UIItem is would be searched in a window. Multiple attributes can be used on a single field, in which the search criteria is "AND"ed.

comments powered by Disqus