Playwright-Java (Part-2): Component driven pattern

Sudarshan
6 min readMay 16, 2022

--

The Legend Page Object Pattern:

Page Object Model is a popular design pattern used widely in the industry in test automation. In this pattern, we consider each web page of the Application under test (AUT) as a class file that contains web page elements and the methods to interact with these web page elements. We create objects of these classes and perform required operations on the web page by calling methods of these classes.

There are a lot of good reads on the internet emphasizing Page Objects Pattern and their advantages in reducing code duplication and improving test maintenance. Here’s a great read by Martin Fowler on the topic.

Here’s an example from Playwright’s Official documentation on how to implement Page objects.

What is Component driven pattern?

Component-driven development has become one of the go-to strategies worldwide to scale and streamline front-end development.

Here are some good reads that emphasize the component driven approach:

Web Elements/Components have their logic and UI encapsulated in an HTML element tag which is reused across different web pages.

Why not leverage and apply this pattern in the Test Automation?

Locators: Playwright Locators represent a way to find element(s) on a web page and can be created by calling the locator method with the required selector on the Playwright’s page instance.
Example: page.locator("#user-name")

Pre-Read:
* Kindly read about the Playwright setup from my previous blog here.
*
Read about the different Playwright selector techniques here.

Most of the automation libraries expose a class to interact with the web page components which does not differentiate between a button, textbox, hyperlink, table, etc.

Let’s go ahead and solve this by creating components as an abstraction over the Playwright methods.

Let’s create a class Element that abstracts the common actions that can be performed on the web page elements as shown in the snippet below.

getLocator() method is called prior to any action so that up-to-date DOM element is located on the page. This means that if the DOM changes in between the calls due to re-render, the new element corresponding to the locator will be used.

Let’s add some wait methods to the Element class so that rather than implementing a utility to wait, we can call the relevant wait method directly on the Element.

The wait method accepts Duration as a parameter, which is the max timeout for the wait condition to be satisfied.

Awaitility helps in polling the DOM for the specified condition to be satisfied and will throw an exception if not met within the specified timeout.

Let’s implement the TextBox component by extending the Element class and implementing methods that are relevant to TextBox.

Similarly, Let’s implement the Button class extending the Element as shown in the snippet below. Notice that we can abstract most of the selector logic in the component.

Let’s try to build a component that is specific to the AUT. Let’s try to automate the SauceLabs demo application’s cart functionality.

The Inventory Items page and its DOM are as shown in the image below.

Let’s try creating InventoryList and InventoryItem components and implement Add to Cart and getting inventory list methods.

The InventoryItem class accepts the item name and based on the DOM in the screenshot above, our selector logic can be abstracted as shown in the snippet below.

page.locator(“”).locator(“”) builds the element location hierarchy.
Example: In the snippet above, we first locate the InventoryItem based on the Item name and then locate the Add to Cart button within the inventory item scope.

Let’s create InventoryList class and use the InventoryItem methods to fetch the item's prices or to add items to the cart.

Similarly, let’s implement CartItem and CartItemsList components based on the DOM in the image below.

Let’s modify the WebPage created in the previous blog by adding some sugar-coated methods to initialize the components. This eliminates the need to instantiate the components in the page objects using the new keyword.

Let’s create Page objects extending the WebPage as shown in the snippet below.

Let’s create ProductsPage and use the InventoryList component methods to add items to the cart and get their prices as shown in the snippet below.

Let’s create CartPage and use the CartItemsList component to get the items in the cart and their prices as shown in the snippet below.

Let’s write the test using the page objects created to add items to the cart and validate the same.

The Assertion used in the above method is provided by assertj and can be used by adding the dependency to the project’s build.gradle

webPageInstance is a sugar coated method created in the WebTest to instantiate a Web Page. Refer this blog for the same.

Okay, I could have done the same with Page Objects and without components, so what is the real deal?

  • Components have made our Page Objects more readable and lighter by abstracting the element interaction logic.
  • If the Web Component is enhanced or changed altogether, the effort required to tackle this change is very minimal. We’ll have to just re-implement the component with the updated interaction logic or location logic. We can get the tests back to a working state with minimal effort.
  • The component-driven approach is used in front-end development to increase code reusability. This pretty much applies to test automation as well.

Please find the source code of this blog here.

Hurray! We just built a test implementing Page objects and Component driven patterns. I’ll be happy to hear feedback/suggestions on the approach.

Happy automating!

--

--

Sudarshan

SDET by Profession, Passionate Coder and Technology Enthusiast