Developer coding guide - alandiaz08/e2e-web-framework GitHub Wiki

Introduction

This document covers the guidelines and conventions of the framework. It serves as a complete guide on how This framework can be standardized with some basic hard and fast rules.

  1. Naming Conventions
  2. Utilization of Test report and Log
  3. Multi-Language support and limitations on assertions
  4. Usage of enums
  5. Explicit definition of on and off functions
  6. isLoaded vs Explicit Waits
  7. FluentInterface
  8. Exceptions

Naming conventions

Getters

Getters should be used when there is a value to be obtained from the application, for example when the restaurant name is to be extracted from the restaurant page use,

get +(Name Of the value)
getRestaurantName()

Methods that perform a specific action

When a method performs a specific task , make sure to place the verb as a prefix. For example when there is an action to enter a value into the text box use,

action verb +(subject on which the action is performed)
enterUserName(userName)

Some of the commonly performed actions and their associated verbs in our framework are ,

Action to be performed Verbs
Checkbox check/uncheck
Radio select
DropDown select
TextBox enter
Open a link/menu/page/section open
Continue the action to the next page continue

boolean Methods

Use is/has when you have to return a boolean value.

is/has+(Name of the value)
hasCuisine(), hasOffer(), isSnackBarClosed() , isStatusUpdated()

Test cases

  • Construct a test case starting with the name of the group it belongs to. Example : Make sure all the booking tests start with "booking" , customer tests with "customer".

  • After the group name, use a verb (verify, modify, cancel ,login etc.) and continue the remaining with some special details (while already logged in , using facebook account etc)

Exceptions: If a test scenario's primary purpose is not related to the group it belongs to ,
For example in booking group, optinVerificationBookingModalNewUser does not test a booking but verifies the optin/optout feature in the bookingModal , feel free to change the rule and highlight the primary purpose of the test as a prefix.

Points to Note:

  • Always ensure to name a method based on what action it performs for a better understanding.
  • When you are re-using a method in a component in a page, it is mandatory to have similar names to ensure uniformity.
  • Make sure to construct a test case name in such a way that it explains what the test does.

Utilization of Test report and Log

To understand the flow of the code and or debug an issue, we make use of Test reports and logs to record each step as it executes. There are some specific cases where a test report comes into use and some cases where a log comes into use.

LogVsTestReport

Where and when to use Test reports and Logs?

  • The start of every action must be described on a high level and should be done using TestReport.addInfoToReport(msg, parameter). In our framework most of the methods start executing in the Page class. So every page class method should be reported to extent. Each assertion in a test case should be recorded on a TestReporter.

  • Logger comes in handy to describe each single step of a method. Most components in our framework have a complete method implementation which should be recorded in a log. Use logger for a detailed step recording and debugging.

Example: Consider the action to enter the restaurant name on the "what" field on the homePage.

The test reporter should have the high-level information that a value is being entered into the field.

  public HomePage enterWhat(String what) {
    TestReporter.addInfoToReport("Enter what: " + what);
    searchComponent.enterWhat(what);
    return this;
  }

The logger should have all the low-level details such as before action, current action and after action.

 public void enterWhat(String what) {
    logger.debug("Enter what: {}", what);
    WebElement whatInput = container.findElement(whatInputBy);

    String whatInputContent = whatInput.getAttribute(VALUE_ATTRIBUTE);
    if (!Strings.isNullOrEmpty(whatInputContent)) {
      logger.debug("The what field contains the text: {}", whatInputContent);
      clearWhat();
    }

    whatInput.sendKeys(what);
    logger.debug("Entered '{}' in what field", what);
    container.findElement(labelWhatBy).click();
  }
  public SideMenu openSideMenu() {
    TestReporter.addInfoToReport("Open Side Menu");
    logger.debug("Click side menu button");
    container.findElement(sideMenuButtonBy).click();
    logger.debug("Wait until side menu is visible");
    WebDriverWait wait = new WebDriverWait(DriverBase.getDriver(), PANEL_MENU_TIMEOUT);
    WebElement sideMenuContainer =
        wait.until(ExpectedConditions.visibilityOfElementLocated(sideMenuContainerBy));
    logger.debug("Side menu is visible");
    SideMenu sideMenu = new SideMenu(sideMenuContainer);
    sideMenu.get();
    return sideMenu;
  }

Exceptions: Get methods should not be reported using test report because they are reported on the assertion section of the test class.

Points to Note:

  • When creating a new test, please read the test report and it must be well-written in English, concise with no ambiguities.

Multi-Language support and limitations on assertions

This Framework supports multi-lingual.

  • Sometimes, there are scenarios where we might want to assert the value which we get from the application to the expected value. Perform an assertion if and only the value is the same across all the languages.

  • Choose selectors and provide input values appropriately to ensure that it works across all languages or at least English,French and Spanish.

Usage of enums

An enum is a special class which holds a group of constants.An enum can be used when there is a need to constraint an input value to a specific set of values.

Example - UserRole can be only one of the three values (Admin, waiter and manager). So instead of providing a hard-coded input for the action or having separate functions for each value , we can define an enum and a single function.

Points to Note:

  • Enums names must always be in English.
  • In our framework , mostly enums are utilized to provide input for a dropDown and for elements with different states(ex: bookingStatus).

Explicit definition of on and off functions

Some scenarios involve enabling/disabling an item which operates on checkbox, radio button and toggle elements. Instead of defining a single function, it is necessary to explicitly define 2 functions for both(on/off) operations separately to improve the readability of the code.

Example -

  • enableOnlineReservations(),disableOnlineReservations()
  • checkReconfirmByEmail() , uncheckReconfirmByEmail()

isLoaded vs Explicit Waits

isLoaded() - The main purpose of isLoaded() is to make sure that a page or a component is completely loaded with all the elements before any action is performed on them. It ensures that the page is readily available and throws ElementNotFound exception if any element is missing.

ExplicitWaits - Explicit wait is a group of special waits defined in selenium to make sure that a page load is synchronized with an element load.Using element load, one can define specific expected conditions like visibilityofElementLocated(),elementToBeClickable() and much more.The driver waits for a predefined duration before it throws an exception.

When to use isLoaded() and ExplicitWaits?

isLoadedVsExplicitWaits

FluentInterface

In e2e-web-framework, the test cases are constructed using an implementation methodology called FluentInterface where each method is chained to the previous one to create a flow. The aim of this is to improve the readability of the code.

Example -

    BookingCustomerInfoModal bookingCustomerInfoModal = restaurantPage.book()
        .selectPax(pax)
        .selectFirstAvailableDate()
        .selectFirstAvailableService()
        .selectFirstAvailableHour()
        .selectNoSpecialOffer()
        .continueBookingNotLoggedIn()
        .enterEmail(account.email())
        .continueToBookingCustomerInfo();

To know more, please visit the following links,

Points to note

  • Always make sure that the return value of the previous method is referential to the current method and the methods are chained as per the flow of the scenario.

Exceptions

Always make sure to throw the same exceptions for similar types of cases.

Example

  • Use IllegalArgumentException if there is a possibility of an incorrect input argument or the input argument could not return a value.

  • Use throwNotLoadedException if there is a possibility of Page objects not being loaded.

Points to note

  • If the pre-defined exceptions are not related to the issues that may occur, please create custom exceptions and document them.