Filling a Simple Form

Example 1

It is always easier to start with simple examples.

Let's begin by looking at one of the most basic scenarios—filling out a primitive form with various types of input fields. The page code is available in the examples project. The form looks like this:



First, let's look at one way to do this using Selenide

We will use JUnit because it is as simple as possible, and no advanced functionality is required here.

1. Create a Page Object and describe the elements available on the page.

@PageObject
@Getter
@Accessors(fluent = true)
public class FormPage extends Page {

    protected TopBar topBar = new TopBar();
    protected SelenideElement fieldLogin = $("#login").as("Textfield Login");
    protected SelenideElement fieldEmail = $("#email").as("Textfield Email");
    protected SelenideElement fieldPassword = $("#password").as("Textfield Password");
    protected SelenideElement fieldRank = $("#rank").as("Textfield rank");
    protected SelenideElement fieldDate = $x("//input[@id='date']").as("Textfield Date");
    protected SelenideElement fieldTelephone = $("#tel").as("Textfield Telephone");
    protected SelenideElement uploadAvatar = $("#file").as("File input Avatar");
    protected SelenideElement radioBtnMale = $("#male").as("Radiobutton Gender Male");
    protected SelenideElement radioBtnFemale = $("#female").as("Radiobutton Gender Female");
    protected SelenideElement ckbMorning = $("#checkbox1").as("Checkbox Preferable time - Morning");
    protected SelenideElement ckbEvening = $("#checkbox2").as("Checkbox Preferable time - Evening");
    protected SelenideElement ddExperience = $("#experience").as("Dropdown list Experience");
    protected SelenideElement selectWorkingDays = $("#multiple-select").as("Select Working Days");
    protected SelenideElement btnReset = $(".btn-secondary").as("Button Reset");
    protected SelenideElement btnSubmit = $(".btn-primary").as("Button Submit");
}

2. Write the test scenario itself in the corresponding class.

@Epic("Selenide style")
public class ITExampleSelenideTest extends TestBaseSelenide {

    @Test
    @Feature("Form")
    @DisplayName("Filling the example form")
    protected void fillTheForm() {
        URL imageUrl = this.getClass().getClassLoader().getResource("img/testicon.png");
        Selenide.open(formPageUrl);
        formPage.fieldLogin().shouldBe(Condition.visible);
        formPage.fieldLogin().sendKeys("John");
        formPage.fieldLogin().shouldHave(Condition.cssClass("form-control"));
        formPage.fieldLogin().shouldHave(Condition.value("John"));
        formPage.fieldEmail().sendKeys("john.doe@gmail.com");
        formPage.fieldEmail().shouldHave(Condition.value("john.doe@gmail.com"));
        formPage.fieldPassword().sendKeys("Password12345!");
        formPage.fieldRank().sendKeys("10");
        formPage.fieldDate().sendKeys("11.11.2011");
        formPage.fieldTelephone().sendKeys("199887688");
        formPage.uploadAvatar().uploadFile(new File(imageUrl.getFile()));
        formPage.radioBtnMale().click();
        formPage.radioBtnMale().shouldBe(Condition.enabled);
        formPage.radioBtnFemale().shouldBe(Condition.disabled);
        formPage.ckbMorning().click();
        formPage.ckbMorning().shouldBe(Condition.checked);
        formPage.ckbEvening().shouldNotBe(Condition.checked);
        formPage.ddExperience().selectOption("2 years");
        formPage.selectWorkingDays().selectOption("Monday", "Friday");
        formPage.btnSubmit().click();
    }
}

The script is simple and clear. We have designated the names of the fields and filled them out. It might seem there is nothing to improve. Now, let’s generate the Allure report and take a look:

Selenide Report

From this report, one can understand what is happening, but even in such an extremely simple case, for someone who did not write it, much can be off-putting. And for non-coders or those accustomed to BDD, even more so.

Moreover, what if, as in one company, the report needs to be in another language? That presents a problem.

After trying all approaches, I found that I dislike writing tests using BDD, where every simple click requires creating several levels of classes, or, heaven forbid, working with Serenity. Although I generally liked BDD reports, I wondered how to receive convenient and readable reports similar to BDD without using the BDD architecture, or at least find a compromise solution. Additionally, there should be a possibility to automatically generate the report in any language, not only in English.

I wanted to automatically obtain a report like this:

Allurium Report

In this report, all element types are labeled, and the performed action is described in plain language. All applied asserts are visible, yet without unnecessary clutter. I get such a report automatically if the test is composed in a similar manner:

@Epic("Selenide style")
public class ITExampleAlluriumTest extends TestBaseAllurium {
    @Test
    @Feature("Form")
    @DisplayName("Filling the example form")
    public void fillTheForm() {
        URL imageUrl = this.getClass().getClassLoader().getResource("img/testicon.png");
        UiSteps.openBrowser(formPageUrl);
        formPage.fieldLogin().assertVisible();
        formPage.fieldLogin().write("John");
        formPage.fieldLogin().assertHasCssClass("form-control");
        formPage.fieldLogin().assertCurrentValue("John");
        formPage.fieldEmail().write("john.doe@gmail.com");
        formPage.fieldEmail().assertCurrentValue("john.doe@gmail.com");
        formPage.fieldPassword().write("Password12345!");
        formPage.fieldRank().write("10");
        formPage.fieldDate().clearAndWrite("11.11.2011");
        formPage.fieldTelephone().write("199887688");
        formPage.uploadAvatar().uploadFile(new File(imageUrl.getFile()));
        formPage.radioBtnMale().click();
        formPage.radioBtnMale().assertEnabled();
        formPage.radioBtnFemale().assertDisabled();
        formPage.ckbMorning().check();
        formPage.ckbMorning().assertChecked();
        formPage.ckbEvening().assertUnchecked();
        formPage.ddExperience().select("2 years");
        formPage.selectWorkingDays().select("Monday");
        formPage.selectWorkingDays().select("Friday");
        formPage.btnSubmit().click();
    }
}

In this example, the test is not shorter, but in my opinion, it is significantly more readable. The asserts are called directly on the element, and the framework automatically verifies them and logs the report in a convenient format.

PageObject for Allurium

When creating a PageObject using Allurium, the approach does not change much. Everything must be clearly structured and typed so that each type of element has its proper type, and the framework understands how to work with it and compose the correct description in the report.

@PageObject
@Getter
@Accessors(fluent = true)
public class FormPage extends Page {

    @Name("Login")
    @Locator(css = "#login")
    protected TextField fieldLogin;

    @Name("Email")
    @Locator(css = "#email")
    protected TextField fieldEmail;

    @Name("Password")
    @Locator(css = "#password")
    protected TextField fieldPassword;

    @Name("Rank")
    protected TextField fieldRank = $textField("#rank");

    @Name("Date")
    protected TextField fieldDate = _$textField("//input[@id='date']");

    @Name("Telephone")
    @Locator(css = "#tel")
    protected TextField fieldTelephone;

    @Name("Avatar")
    @Locator(css = "#file")
    protected UploadField uploadAvatar;

    @Name("Gender Male")
    @Locator(css = "#male")
    protected Button radioBtnMale;

    @Name("Gender Female")
    @Locator(css = "#female")
    protected Button radioBtnFemale;

    @Name("Preferable time - Morning")
    @Locator(css = "#checkbox1")
    protected CheckBox ckbMorning;

    @Name("Preferable time - Evening")
    @Locator(css = "#checkbox2")
    protected CheckBox ckbEvening;

    @Name("Experience")
    @Locator(css = "#experience")
    protected DropdownSelect ddExperience;

    @Name("Working Days")
    @Locator(css = "#multiple-select")
    protected Select selectWorkingDays;

    @Name("Reset")
    @Locator(css = ".btn-secondary")
    protected Button btnReset;

    @Name("Submit")
    @Locator(css = ".btn-primary")
    protected Button btnSubmit;
}

In this case, the PageObject looks more voluminous, but the number of lines can be made the same—the approach is a matter of choice. I prefer to leave spaces between fields with annotations—it improves readability.

In the structure of Allurium, the fundamental classes are those marked with the @PageObject annotation. They serve as containers in which widgets and simple elements are placed. One page = one PageObject. Page objects can be inheritable and extendable without limitations.

Within our PageObject FormPage reside simple elements. Not all elements have the type SelenideElement—each element has its corresponding type:

Element Declaration

When declaring an element, besides its type, you must always assign it a name. I prefer to use the @Name("name") annotation instead of calling .as("name"). This allows creating instances when needed without unnecessary complications and looks more aesthetic. However, you can also assign a name via the object’s constructor.

For example, instead of:

@Name("Submit")
@Locator(css = ".btn-primary")
protected Button btnSubmit;

you can write:

protected Button btnSubmit = $button(".btn-primary", "Submit");

Or, if the instance is already created, via setName("some-name"):

Button btnSubmit = $button(".btn-primary");
btnSubmit.setName("Submit");

Next, you need to specify the locator. This can be done in different ways:

  1. Similar to how it was done with SelenideElement:

    @Name("Submit")
    protected Button btnSubmit = $button(".btn-primary");
    

    Where ".btn-primary" is the CSS selector of the button. Primitive elements such as buttons, text fields, and text blocks are already described in the framework. The instance is created by a static method, similar to the creation of a SelenideElement:

    protected SelenideElement btnSubmit = Selenide.$(".btn-primary").as("Submit");
    

    Here, Selenide.$ is replaced by simply $ (imported as a static method).

    The long version would be:

    @Name("Submit")
    protected Button btnSubmit = Button.$button(".btn-primary");
    

    And the short version via a static method:

    @Name("Submit")
    protected Button btnSubmit = $button(".btn-primary");
    

    If you put an underscore (_) before the $ symbol, as in the example below, the element will be searched by XPath:

    @Name("Date")
    protected TextField fieldDate = _$textField("//input[@id='date']");
    
  2. You can create an instance via a constructor:

    @Name("Submit")
    protected Button btnSubmit = new Button(".btn-primary");
    
  3. Or using the @Locator annotation, similar to @FindBy in Selenium:

    @Name("Submit")
    @Locator(css = ".btn-primary")
    protected Button btnSubmit;
    

    In this case, it is not necessary to declare an instance—it is automatically created by the framework if the element has the @Locator annotation.

    @Locator can accept the following types of locators:

    • css — equivalent to By.cssSelector(".btn-primary")
    • xpath — equivalent to By.xpath("//*[@class='btn-primary']")
    • className — equivalent to By.className("btn-primary")
    • id — equivalent to By.id("#id")

To open the browser and load the page instead of using Selenide.open("url");, you use UiSteps.openBrowser(url);—this step is also recorded in the report. UiSteps is a class with static methods, not tied to a specific page or element, which the framework treats as steps and logs in the report.

Calling and Logging Steps

In this system, each element has a set of basic and specific methods (steps) that are logged by the framework as report steps when called. For example, the methods contextClick() and doubleClick() are available for any element, while the method write("some text") is only available where text input is possible (for example, in TextField or TextArea).

All steps are public, and when they are called, the framework automatically records the information in the report, making it as readable as possible.