Playwright Python Tutorial: Getting Started With Python End To End Testing
Tags: webdev, javascript, testing
Published: 2022-12-21 22:33:31



It’s essential to test all components of your website to see if they work as expected. [Playwright’s end to end testing](https://www.lambdatest.com/blog/playwright-end-to-end-testing/?utm_source=devto&utm_medium=organic&utm_campaign=dec22_sd&utm_term=sd&utm_content=blog) capability helps you achieve this easily. However, if you’re comfortable using Python, you can pair it with the [Playwright testing](https://www.lambdatest.com/playwright-testing?utm_source=devto&utm_medium=organic&utm_campaign=dec22_sd&utm_term=sd&utm_content=webpage) framework to run Python end to end testing on your website.

The Playwright testing framework is more straightforward and lightweight than most alternatives, including [Cypress](https://www.lambdatest.com/learning-hub/cypress-tutorial?utm_source=devto&utm_medium=organic&utm_campaign=dec22_sd&utm_term=sd&utm_content=learning_hub) and [TestCafe](https://www.lambdatest.com/testcafe?utm_source=devto&utm_medium=organic&utm_campaign=dec22_sd&utm_term=sd&utm_content=webpage), among many others. To give it a boost, it also offers [API testing](https://www.lambdatest.com/blog/everything-you-need-to-know-about-api-testing/?utm_source=devto&utm_medium=organic&utm_campaign=dec22_sd&utm_term=sd&utm_content=blog). Hence, it’s an excellent choice for end to end testing.

In this Playwright Python tutorial, you’ll learn how to combine Python’s simplicity with Playwright’s utility for end to end testing. But let’s start by understanding end to end testing and why you want to use Playwright to execute your test.

## What is End to End Testing?

[End to end testing](https://www.lambdatest.com/learning-hub/end-to-end-testing?utm_source=devto&utm_medium=organic&utm_campaign=dec22_sd&utm_term=sd&utm_content=learning_hub) is a method that validates the entire usage flow of an application to ensure that every part of it works as expected. It involves paying attention to every detail and extending the [test coverage](https://www.lambdatest.com/blog/code-coverage-vs-test-coverage/?utm_source=devto&utm_medium=organic&utm_campaign=dec22_sd&utm_term=sd&utm_content=blog) to discover and fix bugs. Its ultimate goal is to help you build a bug-free site.

Hence, it’s an adopted methodology for testing complex websites. Generally, it involves picking up your website’s user story and simulating your users’ behavior in different integration environments.

During end to end testing, you want to consider operating system and browser variability, among many other factors determining an app’s viability. Therefore, it involves conducting a critically inclusive test — from the back (API testing) to the front end.

With that in mind, automated end to end testing reduces testing costs, improves application integrity, and helps your product ship faster.

![](https://cdn-images-1.medium.com/max/2000/0*pfbpkw2h60v0dEzb.png)

## What is Playwright?

[Playwright](https://www.lambdatest.com/playwright?utm_source=devto&utm_medium=organic&utm_campaign=dec22_sd&utm_term=sd&utm_content=webpage), released by Microsoft in 2020, is an open-source, cross-platform [automation testing framework](https://www.lambdatest.com/blog/best-test-automation-frameworks-2021/?utm_source=devto&utm_medium=organic&utm_campaign=dec22_sd&utm_term=sd&utm_content=blog). It supports many browsers, including Chromium, Firefox, and Microsoft Edge, as well as WebKit-based applications. So it provides a handy toolkit for testing web and mobile applications.

It also works with most modern programming languages. As of writing, it supports JavaScript, Python, TypeScript, .NET, and Java. This Playwright Python tutorial will look into how to perform end to end tests with Playwright using Python.

{% youtube wawbt1cATsk %}

## Why use Playwright for Python end to end testing?

The [Playwright framework](https://www.lambdatest.com/blog/playwright-framework/?utm_source=devto&utm_medium=organic&utm_campaign=dec22_sd&utm_term=sd&utm_content=blog) has many event igniters that let you interact with different client-side features of your website like a real user. And it plays well with both synchronous and asynchronous requests. Thus, you can choose to run your test sequentially or leverage the power of Python’s **asyncio **package to run test steps concurrently using **async/await**. The latter is handy for interacting with websites that run tasks asynchronously.

Its API testing capability also helps you validate requests and responses on your app’s endpoints.

Depending on the specified test browser, the Playwright also isolates each test per browser. Thus, each test case in Playwright is a browser context, which defines a browser instance generated rapidly to reduce overhead.

Considering Playwright’s versatile utility and Python’s simplicity and acceptance, combining both to write your test script eases automation testing. Besides, the 2021 Stackoverflow Developer Survey puts Python in third place among the most used programming languages.

![](https://cdn-images-1.medium.com/max/2000/0*BJyj1NV7oycuTu5p.png)

The Playwright framework is popular, with over 2,000 forks and over 41,800 stars on GitHub. And what’s more? It also has fair enough mentions on Stack Overflow with a good community. So you can always get help resolving bugs in your tests.

Unlike [Selenium](https://www.lambdatest.com/selenium?utm_source=devto&utm_medium=organic&utm_campaign=dec22_sd&utm_term=sd&utm_content=webpage), where you might have to define custom timeout intervals to prevent false failure if elements fail to load, Playwright has an auto-wait feature that tentatively pauses for DOM elements to load before executing further steps in the test case. This wait time defaults to 30 seconds before it times out, but you can extend this using Playwright’s built-in **wait_for_timeout()** function. Hence, you’ll have a lower false failure probability with Playwright.

Playwright also provides an inspector tool, which you can use to generate selectors easily as you write your test. This helps reduce or even eliminate the need to inspect the browser manually.

Its code generator works in a play-and-record fashion. You can use it to spin up your app’s UI and auto-generate test scripts in any language as you interact with the DOM. Although this might be handy if you’re unfamiliar with coding, it’s less customizable. It doesn’t suit complex websites (e.g., e-commerce, banking, fintech, and more), as the code generated is often lengthy and complex.

These features, coupled with its intuitive selector and locator classes, make Playwright an excellent end to end testing framework.

**Playwright GitHub trends as of writing this Playwright Python tutorial:**

* Stars: 41.8k

* Used by: 14.1k

* Forks: 2k

* Watchers: 360

* Contributors: 274

* Releases: 79

Before we deep dive into this Playwright Python tutorial, let’s see how to install Playwright for Python end to end testing and set up the test project.

Run Automated Playwright Python Tests Online. [Try LambdaTest Now!](https://accounts.lambdatest.com/register?utm_source=devto&utm_medium=organic&utm_campaign=dec22_sd&utm_term=sd&utm_content=register)

***Check this out: [Data Driven Testing](https://www.lambdatest.com/learning-hub/data-driven-testing?utm_source=devto&utm_medium=organic&utm_campaign=dec22_sd&utm_term=sd&utm_content=learning_hub): A Comprehensive Guide With Examples and Best Practices***

## How to setup Playwright for Python end to end testing?

You have to install and set up your computer to start using Playwright Python for [automation testing](https://www.lambdatest.com/automation-testing?utm_source=devto&utm_medium=organic&utm_campaign=dec22_sd&utm_term=sd&utm_content=webpage). However, this Playwright Python tutorial assumes that your machine runs on Python 3.

**Step 1: Install Python’s latest version.**

Go to the official Python website to download and install the latest version of Python on your machine.

The Windows OS doesn’t come with Python by default, so you’ll have to install it explicitly. However, ensure that you allow the installer to add Python to your system variable path during installation. Else you’ll have to do this manually later.

To install Python on Windows OS:

 1. Open the installation file.

 2. Click the **Add Python to Path** checkbox at the installation window’s bottom right. Then click **Install Now**.

 3. The installation initializes and starts in the following menu.

![](https://cdn-images-1.medium.com/max/2284/0*wwoxaOtT2rtfwK7R.jpg)

While most Linux distributions may have Python pre-installed by default, you might still have to upgrade to a more recent version, as the system version might be obsolete to run Playwright. Playwright, however, works with Python version 3.7 or later.

To install Python on Linux from scratch:

  

```
  sudo apt-get install python 3
```

**Step 2. Install Playwright and its WebKit.**

You can install Playwright using **pip**:

 

```
   pip install playwright
```

If you’re on Linux or Mac, you can use **pip3 **instead, which works with Python 3 on these platforms:

    

```
pip3 install playwright
```

Either of the above commands run the Playwright installer:

![](https://cdn-images-1.medium.com/max/2616/0*OHEacD71zyXu5X55.jpg)

And if you’re using **conda **to manage dependencies:

 

```
   conda install playwright
```

Depending on your choice of Python dependency installer, the above commands install the Playwright framework into your machine or Python virtual environment (if you use one).

And to install the Playwright WebKit:

  

```
  playwright install
```

The above command installs Playwright’s built-in WebKit. You’ll use this to bring up test scenarios for different web browsers.

**Step 3. Create Project Folder.**

The next step in this Playwright Python tutorial is to create a project folder for your automated testing. You can create one from the command line or your graphic user interface.

We’ll use Visual Studio Code on the Windows platform in this Playwright Python tutorial. But you can use any IDE that works best for you.

To create a folder on Windows and open it with VS Code:

 1. Open any folder on your PC and right-click a blank space. Go to **New > Folder**.

 2. Name your folder; this can be a descriptive name (for example **playwright_test_folder**).

 3. Now, open VS Code to your project folder. Open VS Code and click **Open Folder**. Go to the folder you created earlier and select it to open VS Code to that directory.

![](https://cdn-images-1.medium.com/max/3832/0*FNCJStPBS5A8XBpl.jpg)

Now that you’ve seen how to set up a development environment for testing your app in Python using Playwright — let’s dive into the coding aspect. But before that, remember we mentioned that Playwright has a code generator in this Playwright Python tutorial.

You’ll see how this code generator works in the following section of this tutorial on Playwright Python since it shows you how to select elements on your website when writing tests.

Run your Playwright test scripts instantly on 50+ browser and OS combinations. [Try LambdaTest Now!](https://accounts.lambdatest.com/register?utm_source=devto&utm_medium=organic&utm_campaign=dec22_sd&utm_term=sd&utm_content=register)

***Check this out: [Jest testing](https://www.lambdatest.com/jest?utm_source=devto&utm_medium=organic&utm_campaign=dec22_sd&utm_term=sd&utm_content=webpage) Tutorial: Complete Guide to Jest Testing***

## How does the Playwright Code Generator work?

As mentioned in the earlier section of this Playwright Python tutorial, the Playwright code generator makes it easy to scan the DOM without going to the inspection tab of your browser. Although it limits browser selection flexibility and is not as clean as writing custom tests, it’s also a nifty way for no-code lovers to auto-generate test scripts while interacting with the UI.

The code generator works in two ways. You can use it to pick out your website’s selectors. Or you can generate an entire test case script with its record and play functionality.

Open your terminal to your project root folder and use the following command to spin up a selector generator for your app. In my case, I’ll use the LambdaTest e-commerce playground, so remember to replace this with your website’s URL:

```
    playWright codegen https://ecommerce-playground.lambdatest.io/
```

The above command generates the appropriate selectors as you hover on the elements on a web page:

![](https://cdn-images-1.medium.com/max/2000/0*p97V9cr_jY70yu2Z.png)

Look at the image above closely. You’ll see that the **My account** dropdown on that page generates a selector. This helps determine how the Playwright locator finds elements or features during testing.

To generate a test script into a Python file from your interaction with the target website, paste the following command in your terminal, replacing [https://ecommerce-playground.lambdatest.io/](https://ecommerce-playground.lambdatest.io/) with your app’s URL:

```
    playwright codegen — target python -o example2.py https://ecommerce-playground.lambdatest.io/
```

The above command brings up a browser like the first one. But this time, it tells Playwright to write test code into the target file **(example2.py)** as you interact with the specified website. You don’t need to create the target file explicitly. The Playwright does this automatically.

Now open your text editor to your project folder. You should see the generated Python script (**example2.py** in my case).

Here’s the website and the code generated from interacting with it below:

![](https://cdn-images-1.medium.com/max/2000/0*OBzgBHk0_to655Et.png)

If you place the generated Python script and the website side-by-side, as shown above, you’ll see the code changing dynamically in the Python script as you interact with your app.

**Pros of the Playwright Code Generator:**

* It auto-generates code, which can be a good starting point if you need to extend the use case further.

* Handy for picking test selectors in custom automated tests.

* You can generate code for any Playwright-supported programming language.

**Cons of the Playwright Code Generator:**

* Generated code is not scalable and maintainable.

* It’s not suitable for testing complex websites.

* There’s no way to execute parallel tests since it only generates one code file.

* The generated code is hard to read and debug.

* It doesn’t provide room for connecting to a cloud grid.

* The codebase becomes messier as you generate more tests.

* The test cases are repetitive.

***Check this out: [Appium](https://www.lambdatest.com/appium-mobile-testing?utm_source=devto&utm_medium=organic&utm_campaign=dec22_sd&utm_term=sd&utm_content=webpage) Cloud For App Automation- Test your Native, Hybrid, and Web Apps on Appium mobile device cloud of 3000+ different real devices.***

## Playwright Locators and Selectors

As with most testing frameworks, locators and selectors are some of the core features of Playwright.

Like [Selenium locators](https://www.lambdatest.com/blog/locators-in-selenium-webdriver-with-examples/?utm_source=devto&utm_medium=organic&utm_campaign=dec22_sd&utm_term=sd&utm_content=blog), you use Playwright selectors to specify the position of an element within the DOM. The locator is a class that points Playwright to the specified DOM elements so it can perform events on them.

The locator holds off an event and triggers auto-wait, which keeps retrying the test but fails after 30 seconds if it can’t find an element. The locator and selector in Playwright work hand-in-hand.

For example, the following locator performs a click event on the selected element:

```
    page.locator(‘input[name=”Search”]’).click()
```

The selector in the above example is **‘input[name=”search”]’**, while the **page.locator()** function points to the selected element to perform JavaScript events on it (a **click** event in this case). However, the **page**, in this case, is a browser context instance.

![](https://cdn-images-1.medium.com/max/3786/0*FhwIXinMW2yfdnNF.jpg)

***Check this out: How To Debug Websites Using [Developer Tools for safari](https://www.lambdatest.com/blog/debug-websites-using-safari-developer-tools/?utm_source=devto&utm_medium=organic&utm_campaign=dec22_sd&utm_term=sd&utm_content=blog)***

## Implementing Python end to end testing with Playwright

Before you start testing, you may want to inspect the website elements to pick the selectors for your test. Place your cursor on a **web element > right-click** it and go to **Inspect **(for Chrome or any other web browser of your choice).

**Tip:** You can use the Playwright code generator to generate the selectors instead. Simply launch it using the playwright codegen Website_URL command. Hover over the elements and reuse the generated selectors in your test code.

As mentioned earlier, Codegen is extremely useful when it comes to generating selectors since selectors are an integral part of any automation test code.

We would run the tests using Playwright Python in series and parallel. And our test scenario will consider a practical user story to demonstrate our end to end test. Read through this Playwright Python tutorial to learn more about [parallel testing](https://www.lambdatest.com/blog/what-is-parallel-testing-and-why-to-adopt-it/?utm_source=devto&utm_medium=organic&utm_campaign=dec22_sd&utm_term=sd&utm_content=blog).

We’ll run our test on the LambdaTest cloud grid. So you’ll need to grab your username and secret key from your [LambdaTest Build Dashboard](https://accounts.lambdatest.com/login?utm_source=devto&utm_medium=organic&utm_campaign=dec22_sd&utm_term=sd&utm_content=login) by clicking the Access Key tab at the top-right:

By using a [cloud testing](https://www.lambdatest.com/blog/benefits-of-cloud-testing/?utm_source=devto&utm_medium=organic&utm_campaign=dec22_sd&utm_term=sd&utm_content=blog) platform like LambdaTest, you can dramatically cut down the time taken to run your Playwright Python tests by using a browser farm of 50+ browsers and browser versions of Chrome, Chromium, Microsoft Edge, Mozilla Firefox, and even Webkit.

{% youtube ioJaNCYX7Qc %}

You can also subscribe to the [LambdaTest YouTube Channel](https://www.youtube.com/channel/UCCymWVaTozpEng_ep0mdUyw?sub_confirmation=1) and stay updated with the latest tutorial around [automated browser testing](https://www.lambdatest.com/automated-browser-testing?utm_source=devto&utm_medium=organic&utm_campaign=dec22_sd&utm_term=sd&utm_content=webpage), [Cypress E2E testing](https://www.lambdatest.com/cypress-e2e-testing?utm_source=devto&utm_medium=organic&utm_campaign=dec22_sd&utm_term=sd&utm_content=webpage), [Mobile App Testing](https://www.lambdatest.com/learning-hub/mobile-app-testing?utm_source=devto&utm_medium=organic&utm_campaign=dec22_sd&utm_term=sd&utm_content=learning_hub), and more.

***Check this out: How To Debug Websites Using [dev tools in  Safari](https://www.lambdatest.com/blog/debug-websites-using-safari-developer-tools/?utm_source=devto&utm_medium=organic&utm_campaign=dec22_sd&utm_term=sd&utm_content=blog)***

![](https://cdn-images-1.medium.com/max/2486/0*tq7CyuhLfn0aIb0c.jpg)

The tests are demonstrated using the [LambdaTest e-commerce playground.](https://www.lambdatest.com/selenium-playground/simple-form-demo?utm_source=devto&utm_medium=organic&utm_campaign=dec22_sd&utm_term=sd&utm_content=webpage) The use cases for this Playwright Python tutorial are below:

**Test Scenario — 1 (New User Registration)**

 1. The user launches the LambdaTest e-commerce playground registration page.

 2. Fills out the registration form.

 3. User submits the registration form.

 4. Go to their dashboard for the first time.

**Test Scenario — 2 (Login and Purchase Item)**

 1. The user logs in another time with the registered account.

 2. Searches for a product.

 3. The user selects the product searched for in step.

 4. Adds the product to the cart.

 5. The user checks out.

 6. Perform logout.

**Playwright Python Test Structure:**

We will adopt the [Page Object Model (POM)](https://www.lambdatest.com/blog/page-object-model-in-selenium-python/?utm_source=devto&utm_medium=organic&utm_campaign=dec22_sd&utm_term=sd&utm_content=blog) design pattern to structure the test project. So our code structure, in this case, separates concerns into different files in separate folders for modularization. This helps us scale our code later on and get rid of complexities.

Maintainability and scalability of the source code are some of the biggest plus points of the Page Object Model design pattern.

For instance, you might want to separate parallel test cases from single ones along the line.

Here’s the Playwright Python test structure:

  

```
  Project Directory
            |-----------elementSelectors
            |                 |----------------loginAndBuySelectors.py
            |                 |----------------registrationPageSelectors.py
            |
            |------------testCapabilities
            |                 |-----------------testCaps.py
            |
            |-------------testScenarios
            |                 |-----------------.env
            |                 |-----------------parallelLoginBuyRun.py
            |                 |-----------------parallelSignupRun.py
            |                 |-----------------singleLoginBuyRun.py
            |                 |-----------------singleSignupRun.py
            |
            |---------------testScripts
                              |-----------------parallelLoginBuyScript
                              |-----------------parallelSignup.py
                              |-----------------singleLoginBuyScript.py
                              |-----------------singleSignupScript.py
```

The **elementSelectors** folder contains the selectors for all the test scenarios. All the capabilities required to run all the test suites are inside the **testCapabilities** directory. This folder has only one file, which defines the capabilities shared by all test cases.

We also place all test actions (event handlers) inside the **testScripts **folder. Each file in this folder holds the class handling event methods. And finally, the **testScenarios **directory contains the test runners for each test case. To run any test, you only need to run each file here.

Therefore, we have to place the **.env** file in this **testScenarios **folder since all imported modules will pick declared environment variables from it while running the test. Remember that the **.env** file holds all secret parameters, including your cloud grid key and username.

Here’s the VS Code screenshot of the project directory used for this Playwright Python tutorial:

![](https://cdn-images-1.medium.com/max/3816/0*4znIUyHMm6brpwgg.jpg)

Now, we start by setting up the test capabilities for the entire test suite.

**Test Capabilities Implementation:**

**FileName **—*testCapabilities / testCaps.py*

 

```
   import json
    import os
    import urllib.parse
    import subprocess
    from dotenv import load_dotenv
    
    load_dotenv('.env')
    username = os.getenv("my_username")
    gridAcessKey = os.getenv("access_key")
    
    playwrightVersion = str(subprocess.getoutput('playwright --version')).strip().split(" ")[1]
    
    
    capabilities =[{
            'browserName': 'Chrome',  # Browsers allowed: `Chrome`, `MicrosoftEdge`, `pw-chromium`, `pw-firefox` and `pw-webkit`
            'browserVersion': 'latest',
            'platform': 'Windows 10',
            'build': 'E2E for Chrome POM',
            'name': 'Playwright Test',
            'user': username,
            'accessKey': gridAcessKey,
            'console': True,
            'playwrightversion': playwrightVersion
        },
        {
            'browserName': 'MicrosoftEdge',  # Browsers allowed: `Chrome`, `MicrosoftEdge`, `pw-chromium`, `pw-firefox` and `pw-webkit`
            'browserVersion': 'latest',
            'platform': 'Windows 10',
            'build': 'E2E for POM Egde',
            'name': 'Playwright Test Case 2',
            'user': username,
            'accessKey': gridAcessKey,
            'console': True,
            'playwrightversion': playwrightVersion
        }
        ]
    
    class testCapabilities:
        def __init__(self) -> None:
            self.lambdatestGridURL = 'wss://cdp.lambdatest.com/playwright?capabilities='
    
        def Chrome(self):
            self.stringifiedCaps = urllib.parse.quote(json.dumps(capabilities[0]))
            return self.lambdatestGridURL+self.stringifiedCaps
        def Edge(self):
            self.stringifiedCaps1 = urllib.parse.quote(json.dumps(capabilities[1]))
            return self.lambdatestGridURL+self.stringifiedCaps1

```
***Check this out: [Emulator vs Simulator](https://www.lambdatest.com/blog/emulator-vs-simulator-vs-real-device/?utm_source=devto&utm_medium=organic&utm_campaign=dec22_sd&utm_term=sd&utm_content=blog) vs Real Device Testing: Key Differences***

**Playwright Python Code Walkthrough:**

The **testCaps.py** file contains the capabilities module required to run all our test cases, including single and parallel tests. So you don’t have to change it for subsequent test cases while running the Playwright Python test — unless you want to add more capabilities to the array, which involves scaling the methods.

You start by importing all the necessary packages to run this Playwright Python module.

```
    import json
        import os
        import urllib.parse
        import subprocess
        from dotenv import load_dotenv
```

Next, load your cloud grid access key and username from the environmental variables using the **dotenv** package, as you’ll provide them in the capabilities:

```
    load_dotenv('.env')
        username = os.getenv("my_username")
        gridAcessKey = os.getenv("access_key")
```

You’ll also provide the Playwright version as a string inside the test capability. In this case, we extract this value using the subprocess module. Then we clean it with Python’s built-in **strip **and **split **functions:

```
    playwrightVersion = str(subprocess.getoutput(‘playwright — version’)).strip().split(“ “)[1]
```

Each capability is a dictionary specifying settings for the test. There are two capabilities in the array — in this case (Chrome and Microsoft Edge). Therefore we can call them individually in separate methods using an array index. And that makes it easy to scale to perform cross browser testing using Playwright Python — even in parallel.

```
    capabilities =[{
                'browserName': 'Chrome',  # Browsers allowed: `Chrome`, `MicrosoftEdge`, `pw-chromium`, `pw-firefox` and `pw-webkit`
                'browserVersion': 'latest',
                'platform': 'Windows 10',
                'build': 'E2E for Chrome POM',
                'name': 'Playwright Test',
                'user': username,
                'accessKey': gridAcessKey,
                'console': True,
                'playwrightversion': playwrightVersion
            },
            {
                'browserName': 'MicrosoftEdge',  # Browsers allowed: `Chrome`, `MicrosoftEdge`, `pw-chromium`, `pw-firefox` and `pw-webkit`
                'browserVersion': 'latest',
                'platform': 'Windows 10',
                'build': 'E2E for POM Egde',
                'name': 'Playwright Test Case 2',
                'user': username,
                'accessKey': gridAcessKey,
                'console': True,
                'playwrightversion': playwrightVersion
            }
            ]
```

The **testCapabilities **class holds the Chrome and Microsoft Edge capability methods. First, you initialize this class with the cloud grid URL:

```
    class testCapabilities:
            def __init__(self) -&gt; None:
                self.lambdatestGridURL = 'wss://cdp.lambdatest.com/playwright?capabilities='

```
The LamdaTest grid URL for Playwright, as indicated above, is **self.lambdatestGridURL**.

You then extract the capability for each browser method (**Chrome** and **Edge**) using its index position. Then concatenate each with the grid URL inherited from the **__init__** function.

The **Chrome **method capability dictionary is at index zero (first item in the array):

```
    self.stringifiedCaps = urllib.parse.quote(json.dumps(capabilities[0]))
                return self.lambdatestGridURL+self.stringifiedCaps
```

The **Edge()** method gets the second dictionary in the array:

```
    self.stringifiedCaps1 = urllib.parse.quote(json.dumps(capabilities[1]))
                return self.lambdatestGridURL+self.stringifiedCaps1
```

Hence, this design helps you run your test singly or in parallel based on the different capabilities.

**Implementation of the Registration Test Scenario:**

**FileName **—*elementSelectors/registrationPageSelectors.py*

```
    webElements = {
        'webpage': "https://ecommerce-playground.lambdatest.io/index.php?route=account/register",
        'First_Name': 'input[placeholder="First Name"]',
        'Last_Name': 'input[placeholder="Last Name"]',
        'E-Mail': 'input[placeholder="E-Mail"]',
        'Telephone': 'input[placeholder="Telephone"]',
        'Password': 'input[placeholder="Password"]',
        'Confirm_Password': 'input[placeholder="Password Confirm"]',
        'Subscribe': 'label:has-text("No")',
        'Privacy_Policy': 'label:has-text("I have read and agree to the Privacy Policy")',
        'Submit': 'input[value="Continue"]',
        'Continue': "text=Continue"
    }
    
    
    class elementSelector:
        def __init__(self) -> None:
            self.webpage = webElements["webpage"]
            self.firstname = webElements["First_Name"]
            self.lastname = webElements["Last_Name"]
            self.email = webElements["E-Mail"]
            self.telephone = webElements["Telephone"]
            self.password = webElements["Password"]
            self.confirmpassword = webElements["Confirm_Password"]
            self.subscribe = webElements["Subscribe"]
            self.privacypolicy = webElements["Privacy_Policy"]
            self.submit = webElements["Submit"]
            self.todashboard = webElements["Continue"]
       
        def webPage(self):
            return self.webpage
        def firstName(self):
            return self.firstname
        def lastName(self):
            return self.lastname
        def eMail(self):
            return self.email
        def Telephone(self):
            return self.telephone
        def Password(self):
            return self.password
        def confirmPassword(self):
            return self.confirmpassword
        def Subscribe(self):
            return self.subscribe  
        def privacyPolicy(self):
            return self.privacypolicy
        def Submit(self):
            return self.submit
        def goToDashboard(self):
            return self.todashboard
```

**FileName **—*testScripts / singleSignupScript.py*

```
    from playwright.sync_api import sync_playwright
    import sys
    sys.path.append(sys.path[0] + "/..")
    
    
    from elementSelectors.registrationPageSelectors import elementSelector
    from testCapabilities.testCaps import testCapabilities
    
    select = elementSelector()
    
    capability = testCapabilities()
    
    def set_test_status(page, status, remark):
            page.evaluate("_ => {}",
            "lambdatest_action: {\"action\": \"setTestStatus\", \"arguments\": {\"status\":\"" + status + "\", \"remark\": \"" + remark + "\"}}")
    
    
    class Register:
        def __init__(self, playwright) -> None:
            self.browser = playwright.chromium.connect(capability.Chrome())
            page = self.browser.new_page()
            self.page = page
        def launchWeb(self):
            self.page.goto(select.webPage())
            title = self.page.title()
            print(title)
    
        def fillFirstName(self, data):
            self.page.locator(select.firstName()).fill(data)
    
        def fillLastName(self, data):
            self.page.locator(select.lastName()).fill(data)
    
        def fillEmail(self, data):
            self.page.locator(select.eMail()).fill(data)
    
        def fillPhone(self, data):
            self.page.locator(select.Telephone()).fill(data)
    
        def fillPassword(self, data):
            self.page.locator(select.Password()).fill(data)
    
        def confirmPassword(self, data):
            self.page.locator(select.confirmPassword()).fill(data)
    
        def subscribe(self):
            self.page.locator(select.Subscribe()).click()
    
        def acceptPolicy(self):
            self.page.locator(select.privacyPolicy()).click()
    
        def submit(self):
            self.page.locator(select.Submit()).click()
    
        def continueToDashboard(self):
            self.page.locator(select.goToDashboard()).click()
    
        def getSuccessStatus(self):
            return set_test_status(self.page, "passed", "Success")
    
        def getFailedStatus(self):
            return set_test_status(self.page, "failed", "Test failed")
    
        def closeBrowser(self):
            self.browser.close()

```
**FileName **—*testScenario / singleSignupRun.py*

```
    import sys
    sys.path.append(sys.path[0] + "/..")
    
    from testScripts.singleSignupScript import Register
    from playwright.sync_api import sync_playwright
    
    
    with sync_playwright() as playwright:
    
        try:
            playwright = Register(playwright)
            playwright.launchWeb()
            playwright.fillFirstName("Idowu")
            playwright.fillLastName("Omisola")
            playwright.fillEmail("anEmailgmai@gmail.com")
            playwright.fillPhone("08122334433")
            playwright.fillPassword("mypassword")
            playwright.confirmPassword("mypassword")
            playwright.subscribe()
            playwright.acceptPolicy()
            playwright.submit()
            playwright.continueToDashboard()
            playwright.getSuccessStatus()
        except:
            playwright.getFailedStatus()
    
        playwright.closeBrowser()
```

**Playwright Python Code Walkthrough:**

**FileName — elementSelectors / registrationPageSelectors.py:**

We insert all the web element selectors, including the test site URL, into a dictionary inside the **registrationPageSelectors.py** file:

For instance, the Email field selector takes this format in the dictionary:

```
    ‘E-Mail’: ‘input[placeholder=”E-Mail Address”]’
```

The entire element selector dictionary looks like this:

```
    webElements = {
            'webpage': "https://ecommerce-playground.lambdatest.io/index.php?route=account/register",
            'First_Name': 'input[placeholder="First Name"]',
            'Last_Name': 'input[placeholder="Last Name"]',
            'E-Mail': 'input[placeholder="E-Mail"]',
            'Telephone': 'input[placeholder="Telephone"]',
            'Password': 'input[placeholder="Password"]',
            'Confirm_Password': 'input[placeholder="Password Confirm"]',
            'Subscribe': 'label:has-text("No")',
            'Privacy_Policy': 'label:has-text("I have read and agree to the Privacy Policy")',
            'Submit': 'input[value="Continue"]',
            'Continue': "text=Continue"
        }

```
Next is to turn these into the attributes that initiate the **elementSelector** class by calling each element by its key

```
    class elementSelector:
            def __init__(self) -&gt; None:
                self.webpage = webElements["webpage"]
                self.firstname = webElements["First_Name"]
                self.lastname = webElements["Last_Name"]
                self.email = webElements["E-Mail"]
                self.telephone = webElements["Telephone"]
                self.password = webElements["Password"]
                self.confirmpassword = webElements["Confirm_Password"]
                self.subscribe = webElements["Subscribe"]
                self.privacypolicy = webElements["Privacy_Policy"]
                self.submit = webElements["Submit"]
                self.todashboard = webElements["Continue"]
```

Then we declare them as methods of the **elementSelector** class like so:

```
    def webPage(self):
                return self.webpage
            def firstName(self):
                return self.firstname
            def lastName(self):
                return self.lastname
            def eMail(self):
                return self.email
            def Telephone(self):
                return self.telephone
            def Password(self):
                return self.password
            def confirmPassword(self):
                return self.confirmpassword
            def Subscribe(self):
                return self.subscribe  
            def privacyPolicy(self):
                return self.privacypolicy
            def Submit(self):
                return self.submit
            def goToDashboard(self):
                return self.todashboard
```

**FileName — testScripts / singleSignupScript.py:**

The **singleSignupScript.py** file contains the test event methods. We start by importing the built-in modules:

 

```
   from playwright.sync_api import sync_playwright
        import sys

```
The **sys **package connects the Python files in different folders and makes imported custom modules visible:

 

```
   sys.path.append(sys.path[0] + “/..”)
```

The directory path (“/..”) is a file-level declaration, showing that the imported module is two steps away from the root folder. So it might change in your case, depending on your project structure.

After this, we import and instantiate the **elementSelector** and **testCapability** classes from their respective modules:

```
    from elementSelectors.registrationPageSelectors import elementSelector
        from testCapabilities.testCaps import testCapabilities
        
        select = elementSelector()
        
        capability = testCapabilities()
```

So, for instance, to call the Chrome method from the **testCapability** class:

```
    capability.Chrome()
```

And to get a method, say the **webPage** from the **elementSelector** class:

```
    select.webPage()
```

To start the **Register **class, it inherits the Playwright package methods:

    

```
def __init__(self, playwright) -&gt; None:
```

However, to initiate the **Register **class, we connected to the Chrome method (which defines the grid URL and the JSONified capabilities). The **self.page** attribute is an instance of the browser context containing the locator actions.

```
    self.browser = playwright.chromium.connect(capability.Chrome())
                page = self.browser.new_page()
                self.page = page
```

The **launchWeb **method opens the website using the page instance **goto **method by calling the website URL with the **select.webPage()** instance of the **elementSelector **class:

```
    def launchWeb(self):
                self.page.goto(select.webPage())
                title = self.page.title()
                print(title)
```

Each of the other attributes in the class gets the appropriate method similarly. For readability, each field element accepts a **data **keyword.

For example, to fill in the first name:

 

```
   def fillFirstName(self, data):
                self.page.locator(select.firstName()).fill(data)
```

The elements that accept a click event doesn’t take any data. To click the Subscribe button, for instance:

 

```
   def subscribe(self):
                self.page.locator(select.Subscribe()).click()
```

The **set_test_status **function at the top of the script returns test status on the LambdaTest Grid UI. So once a test passes, it returns **passed**, but you get a **failed **message for failed tests:

```
    def set_test_status(page, status, remark):
                page.evaluate("_ =&gt; {}",
                "lambdatest_action: {\"action\": \"setTestStatus\", \"arguments\": {\"status\":\"" + status + "\", \"remark\": \"" + remark + "\"}}")

This later becomes a part of the **Register** class methods:

    def getSuccessStatus(self):
                return set_test_status(self.page, "passed", "Success")
        
            def getFailedStatus(self):
                return set_test_status(self.page, "failed", "Test failed")
```

**FileName — testScenario / singleSignupRun.py:**

The **singleSignupRun.py** file also starts with module imports. But notably, we imported the **Register **class from the **singleSignupScript **module. We then created an event loop using the sync class of Playwright and passed this as **playwright**:

```

    with sync_playwright() as playwright:
```

Remember that the **Register **class inherited the Playwright method earlier. To actualize this, we pass the **playwright **variable as an instance of the **Register **class. And it also now bears the inherited Playwright sync class:

```
    playwright = Register(playwright)
```

Therefore, the event loop executes the registration test steps in this order:

**Step 1**: Launch the test website using the launch method:

```
    playwright.launchWeb()
```

**Step 2**: Fill out the registration form. So for form fields, you must call and fill them since they accept a positional **data** argument:

```
    playwright.fillFirstName("Idowu")
                playwright.fillLastName("Omisola")
                playwright.fillEmail("anEmailgmai@gmail.com")
                playwright.fillPhone("08122334433")
                playwright.fillPassword("mypassword")
                playwright.confirmPassword("mypassword")
                playwright.subscribe()
                playwright.acceptPolicy()
```

**Step 3**: Submit the registration form:

```

    playwright.submit()

```
**Step 4**: Visit your dashboard for the first time:

 

```
   playwright.continueToDashboard()
```

The **getSuccessStatus()** method returns a **passed** message on the grid if the code in the try block executes successfully:

   

```
 playwright.getSuccessStatus()
```

Otherwise, it runs the **getfailedStatus()** method and returns a **failed **message on the grid:

```
    except Exception as err:
                playwright.getFailedStatus()
```

Finally, we close the browser outside the **try-except** block to prevent an endless loop, which throws an error during execution:

```
    playwright.closeBrowser()
```

**Playwright Python Execution:**

Run the test runner file (**singleSignupRun.py**):

   

```
 python singleSignupRun.py
```

The test executes successfully on the LambdaTest grid as shown below:

![](https://cdn-images-1.medium.com/max/3808/0*r_RAOl1_hltkOGxF.jpg)

***Check this out: Cross [Browser Test](https://www.lambdatest.com/?utm_source=devto&utm_medium=organic&utm_campaign=dec22_sd&utm_term=sd&utm_content=webpage) Cloud- Browser & app testing cloud to perform both exploratory and automated testing across 3000+ different browsers, real devices and operating systems.***

**Login and Product Purchase Test Case Implementation:**

The element selectors for the login test case are inside the **loginAndBuySelectors.py** file.

**FileName **—*elementSelectors/loginAndBuySelectors.py*

 

```
   webElements = {
        'webpage': "https://ecommerce-playground.lambdatest.io/index.php?route=account/login",
        'E-Mail': 'input[placeholder="E-Mail Address"]',
        'Password': 'input[placeholder="Password"]',
        'Login': 'input:has-text("Login")',
        'Search': 'input[name="search"] >> nth = 0',
        'searchbutton': 'div[class="search-button"]:has-text("Search")',
        'product': 'h4[class="title"]:has-text("Nikon D300") >> nth = 0',
        'addcart': 'div[id="entry_216842"]:has-text("Add to Cart")',
        'checkoutmodal': 'div[role="alert"]:has-text("Checkout")',
        'Hoverable': 'a[role="button"]:has-text("My account")',
        'Logout': 'span:has-text("Logout")'
    }
    
    class elementSelector:
        def __init__(self) -> None:
            self.webpage = webElements['webpage']
            self.email = webElements['E-Mail']
            self.password = webElements['Password']
            self.login = webElements['Login']
            self.hover = webElements['Hoverable']
            self.searchproduct = webElements['Search']
            self.product = webElements['product']
            self.addcart = webElements['addcart']
            self.checkout = webElements['checkoutmodal']
            self.searchbutton = webElements['searchbutton']
            self.logout = webElements['Logout']
        def webPage(self):
            return self.webpage
        def eMail(self):
            return self.email
        def Password(self):
            return self.password
        def loginAccount(self):
            return self.login
        def hoverBox(self):
            return self.hover
        def searchProduct(self):
            return self.searchproduct
        def Product(self):
            return self.product
        def addCart(self):
            return self.addcart
        def checkOut(self):
            return self.checkout
        def searchButton(self):
            return self.searchbutton
        def logoutUser(self):
            return self.logout
```

**FileName **—*testScripts / singleLoginButScript.py*:

 

```
   from playwright.sync_api import sync_playwright
    import sys
    sys.path.append(sys.path[0] + "/..")
    
    
    from elementSelectors.loginAndBuySelectors import elementSelector
    from testCapabilities.testCaps import testCapabilities
    
    select = elementSelector()
    
    capability = testCapabilities()
    
    # Setting grid status to failed or passed
    def set_test_status(page, status, remark):
            page.evaluate("_ => {}",
            "lambdatest_action: {\"action\": \"setTestStatus\", \"arguments\": {\"status\":\"" + status + "\", \"remark\": \"" + remark + "\"}}")
    
    class LoginAndBuy:
       
        def __init__(self, playwright) -> None:
            self.browser = playwright.chromium.connect(capability.Chrome())
            page = self.browser.new_page()
            self.page = page
    
        def launchWeb(self):
            self.page.goto(select.webPage())
            title = self.page.title()
            print(title)
    
        def fillEmail(self, data):
            self.page.locator(select.eMail()).fill(data)
       
        def fillPassword(self, data):
            self.page.locator(select.Password()).fill(data)
    
        def clickLogin(self):
            self.page.locator(select.loginAccount()).click()
       
        def fillSearchBox(self, data):
            self.page.locator(select.searchProduct()).fill(data)
       
        def clickSearchButton(self):
            self.page.locator(select.searchButton()).click()
    
        def clickProduct(self):
            self.page.locator(select.Product()).click()
    
        def clickAddToCart(self):
            self.page.locator(select.addCart()).click()
       
        def clickCheckOutModal(self):
            self.page.locator(select.checkOut()).click()
    
        def hoverMenuBox(self):
            self.page.locator(select.hoverBox()).hover()
       
        def clickLogout(self):
            self.page.locator(select.logoutUser()).click()
    
        def getSuccessStatus(self):
            set_test_status(self.page, "passed", "Success")
    
        def getFailedStatus(self):
            set_test_status(self.page, "failed", "Test failed")
    
        def closeBrowser(self):
            self.browser.close()
    
    
```

**FileName **—*testScenarios / singleLoginBuyRun.py:*

```
    import sys
    sys.path.append(sys.path[0] + "/..")
    
    from testScripts.singleLoginBuyScript import LoginAndBuy
    from playwright.sync_api import sync_playwright
    
    
    with sync_playwright() as playwright:
        try:
            playwright = LoginAndBuy(playwright)
            playwright.launchWeb()
            playwright.fillEmail("anEmailgmai@gmail.com")
            playwright.fillPassword("mypassword")
            playwright.clickLogin()
            playwright.fillSearchBox("Nikon")
            playwright.clickSearchButton()
            playwright.clickProduct()
            playwright.clickAddToCart()
            playwright.clickCheckOutModal()
    
            playwright.hoverMenuBox()
            playwright.clickLogout()
            playwright.getSuccessStatus()
        except:
            playwright.getFailedStatus()
        playwright.closeBrowser()
```

**Playwright Python Code Walkthrough:**

**FileName — elementSelectors / loginAndBuySelectors.py:**

As with the registration test case, we place all the login page element selectors and the website URL in a dictionary (**webElements**):

 

```
   webElements = {
            'webpage': "https://ecommerce-playground.lambdatest.io/index.php?route=account/login",
            'E-Mail': 'input[placeholder="E-Mail Address"]',
            'Password': 'input[placeholder="Password"]',
            'Login': 'input:has-text("Login")',
            'Search': 'input[name="search"] &gt;&gt; nth = 0',
            'searchbutton': 'div[class="search-button"]:has-text("Search")',
            'product': 'h4[class="title"]:has-text("Nikon D300") &gt;&gt; nth = 0',
            'addcart': 'div[id="entry_216842"]:has-text("Add to Cart")',
            'checkoutmodal': 'div[role="alert"]:has-text("Checkout")',
            'Hoverable': 'a[role="button"]:has-text("My account")',
            'Logout': 'span:has-text("Logout")'
        }
```

The **Search **and **product **elements have the **>> nth = index** because each of these DOM elements has an nth node with similar selectors. So the **nth **argument helps specify the index position of the children, starting from zero.

For example, to select the first product from a group of similar ones as we did above:

```
    ‘product’: ‘h4[class=”title”]:has-text(“Nikon D300”) &gt;&gt; nth = 0’
```

We use these elements to initiate the **elementSelector **class by attaching each to an attribute:

 

```
   class elementSelector:
            def __init__(self) -&gt; None:
                self.webpage = webElements['webpage']
                self.email = webElements['E-Mail']
                self.password = webElements['Password']
                self.login = webElements['Login']
                self.hover = webElements['Hoverable']
                self.searchproduct = webElements['Search']
                self.product = webElements['product']
                self.addcart = webElements['addcart']
                self.checkout = webElements['checkoutmodal']
                self.searchbutton = webElements['searchbutton']
                self.logout = webElements['Logout']
```

Next, we create a method that returns each attribute. These methods will handle Playwright locator events in the test script:

 

```
   def webPage(self):
                return self.webpage
            def firstName(self):
                return self.firstname
            def lastName(self):
                return self.lastname
            def eMail(self):
                return self.email
            def Telephone(self):
                return self.telephone
            def Password(self):
                return self.password
            def confirmPassword(self):
                return self.confirmpassword
            def Subscribe(self):
                return self.subscribe  
            def privacyPolicy(self):
                return self.privacypolicy
            def Submit(self):
                return self.submit
            def goToDashboard(self):
                return self.todashboard
```

So, the above block attaches each attribute to the **elementSelector **class method. We can use these easily in another file; you only have to call each from an instance of the **elementSelector **class. That’s one of the strong points of the Page Object Model (POM).

**FileName — testScripts / singleLoginBuyScript.py:**

We start the **singleLoginButScript.py** file by importing the synchronous Playwright and **sys** packages. Then we use the sys append method to point Python to the system path before importing our custom modules (**elementSelector and testCapabilities**):

```
    from playwright.sync_api import sync_playwright
        import sys
        sys.path.append(sys.path[0] + "/..")
        
        from elementSelectors.loginAndBuySelectors import elementSelector
        from testCapabilities.testCaps import testCapabilities

```
We create an instance of the **elementSelector** and **testCapabilities** classes to prevent conflict with the class inheritance:

 

```
   select = elementSelector()
        
        capability = testCapabilities()
```

It’s now easy to call each method from these classes via their object instance. We leverage this to declare our methods in the **LoginAndBuy** class, starting with initialization with the Chrome browser context. However, the initialization inherits the Playwright class:

 

```
   class LoginAndBuy:
           
            def __init__(self, playwright) -&gt; None:
                self.browser = playwright.chromium.connect(capability.Chrome())
                page = self.browser.new_page()
                self.page = page
```

In the above snippet, **capability.Chrome()** picks the **Chrome()** method from the testCapabilities class. You can change this to Microsoft Edge by replacing **Chrome()** with the **Edge()** method:

 

```
   self.browser = playwright.chromium.connect(capability.Edge())
```

The other methods belong to the **elementSelector **class. So the test case starts by launching the browser using the inherited browser context. Note how it selects the **webPage()** method from this class:

```
    def launchWeb(self):
                self.page.goto(select.webPage())
```

The following three methods define the login process. Each form field accepts a **data** keyword (the Email and password registered earlier):

Below are the login steps:

```

    def fillEmail(self, data):
                self.page.locator(select.eMail()).fill(data)
           
            def fillPassword(self, data):
                self.page.locator(select.Password()).fill(data)
        
            def clickLogin(self):
                self.page.locator(select.loginAccount()).click()
```

Next up, we search for a product, select it when found, then add it to the cart and checkout:

  

```
  def fillSearchBox(self, data):
                self.page.locator(select.searchProduct()).fill(data)
           
            def clickSearchButton(self):
                self.page.locator(select.searchButton()).click()
        
            def clickProduct(self):
                self.page.locator(select.Product()).click()
        
            def clickAddToCart(self):
                self.page.locator(select.addCart()).click()
           
            def clickCheckOutModal(self):
                self.page.locator(select.checkOut()).click()
```

The final test step is to log out of the website. The **hover()** event makes the logout button accessible since it’s in a hoverable dropdown:

  

```
  def hoverMenuBox(self):
                self.page.locator(select.hoverBox()).hover()
           
            def clickLogout(self):
                self.page.locator(select.logoutUser()).click()

```
The **set_test_status **function at the top of the script helps communicate the test status to the cloud grid’s UI:

```
    def set_test_status(page, status, remark):
                page.evaluate("_ =&gt; {}",
                "lambdatest_action: {\"action\": \"setTestStatus\", \"arguments\": {\"status\":\"" + status + "\", \"remark\": \"" + remark + "\"}}")

```
This becomes part of the **LoginAndBuy **class methods:

```
    def getSuccessStatus(self):
                set_test_status(self.page, "passed", "Success")
        
            def getFailedStatus(self):
                set_test_status(self.page, "failed", "Test failed")

Finally, we close the browser context:

    def closeBrowser(self):
                self.browser.close()
```

**FileName — testScenarios / singleLoginBuyRun.py:**

The **singleLoginBuyRun.py** file runs the login and product purchase test case. First, we import the Playwright sync module and resolve the system path using the sys package:

```
    import sys
        sys.path.append(sys.path[0] + "/..")
        
        from playwright.sync_api import sync_playwright

```
We also have to import the **LoginAndBuy **class:

```
    from testScripts.singleLoginBuyScript import LoginAndBuy
```

Next is the class instance declaration inside the Playwright event loop, followed by website launch on the selected browser:

  

```
  with sync_playwright() as playwright:
            try:
                playwright = LoginAndBuy(playwright)
                playwright.launchWeb()

```
The following steps occur within the event loop:

**Step 1:** Log in another time with the registered account. Below are the login steps declaration within the event loop:

 

```
       playwright.fillEmail("anEmailgmai@gmail.com")
                playwright.fillPassword("mypassword")
                playwright.clickLogin()
```

**Step 2**: Search for a product and go to the product page:

```
    playwright.fillSearchBox("Nikon")
                playwright.clickSearchButton()

```
**Step 3**: Select the item you searched to go to the product page:

 

```
   playwright.clickProduct()
```

**Step 4:** Add the selected product to the cart:

```
    playwright.clickAddToCart()
```

**Step 5**: Check out with the purchased product:

   

```
 playwright.clickCheckOutModal()
```

**Step 6**: The event loop ends with the logout steps. The success status must be within the **try** clause since it runs only if the series of code in the **try** block work:

```
    playwright.hoverMenuBox()
                playwright.clickLogout()
                playwright.getSuccessStatus()
```

Otherwise, the failed status only runs if there’s an exception:

 

```
   except:
         
       playwright.getFailedStatus()
```

Finally, we close the browser regardless of what happens in the event loop:

 

```
   playwright.closeBrowser()
```

**Execution of the Login and Product Purchase Test on the Cloud Grid:**

To execute this test case:

  

```
  python singleLoginBuyRun.py
```

The test runs successfully on the cloud grid as shown:

![](https://cdn-images-1.medium.com/max/3200/0*BLwmcbF0TQsRLMxK.jpg)

***Check this out: Test on [Safari Browser Online](https://www.lambdatest.com/test-on-safari-browsers?utm_source=devto&utm_medium=organic&utm_campaign=dec22_sd&utm_term=sd&utm_content=webpage) with Real Mac Machines. Test on Latest Safari Desktop and Mobile Browsers for Cross Browser Compatibility.***

## Implementing parallel testing with Playwright Python

Performing cross-browser [regression testing](https://www.lambdatest.com/blog/regression-testing-what-is-and-how-to-do-it/?utm_source=devto&utm_medium=organic&utm_campaign=dec22_sd&utm_term=sd&utm_content=blog) for multiple browsers at once helps find issues faster, allowing you to move on to other work. We will continue to maintain the same browser configurations for the parallel test for testing efficiency purposes.

Running the test cases in parallel on the cloud grid helps you simulate an instance of servers in the cloud where you can run your test cases concurrently on separate cloud host machines. This helps check if your app works as it should in different integration environments.

In this case, the parallel test concept is to iterate the test cases through a capabilities array. So for this section of this Playwright Python tutorial, we’ll run the registration and login tests on two browsing capabilities; Chrome and Microsoft Edge.

**Note**: The entire test suite shares the **testCap.py** file, which we’ve shown earlier. However, the selector modules (**registrationPageSelectors.py** and **loginAndBuySelectors.py**) also remain the same for the signup and login to purchase parallel test cases.

**Registration Parallel Test Implementation:**

***FileName **— testScripts/parallelSignup.py*

 

```
   from playwright.sync_api import sync_playwright
    import sys
    sys.path.append(sys.path[0] + "/..")
    
    
    from elementSelectors.registrationPageSelectors import elementSelector
    from testCapabilities.testCaps import testCapabilities
    
    select = elementSelector()
    
    capability = testCapabilities()
    
    def set_test_status(page, status, remark):
            page.evaluate("_ => {}",
            "lambdatest_action: {\"action\": \"setTestStatus\", \"arguments\": {\"status\":\"" + status + "\", \"remark\": \"" + remark + "\"}}")
    
    class Register:
        def __init__(self, playwright) -> None:
            self.allCaps = [capability.Edge(), capability.Chrome()]
            self.browsers = [playwright.chromium.connect(i) for i in self.allCaps]
            pages = [i.new_page() for i in self.browsers]
            self.pages = pages
        def launchWeb(self):
            for page in self.pages:
                page.goto(select.webPage())
                title = page.title()
                print(title)
        def fillFirstName(self, data):
            for page in self.pages:
                page.locator(select.firstName()).fill(data)
        def fillLastName(self, data):
            for page in self.pages:
                page.locator(select.lastName()).fill(data)
        def fillEmail(self, data):
            for page in self.pages:
                page.locator(select.eMail()).fill(data)
        def fillPhone(self, data):
            for page in self.pages:
                page.locator(select.Telephone()).fill(data)
        def fillPassword(self, data):
            for page in self.pages:
                page.locator(select.Password()).fill(data)
        def confirmPassword(self, data):
            for page in self.pages:
                page.locator(select.confirmPassword()).fill(data)
        def subscribe(self):
            for page in self.pages:
                page.locator(select.Subscribe()).click()
        def acceptPolicy(self):
            for page in self.pages:
                page.locator(select.privacyPolicy()).click()
        def submit(self):
            for page in self.pages:
                page.locator(select.Submit()).click()
        def continueToDashboard(self):
            for page in self.pages:
                page.locator(select.goToDashboard()).click()
        def getSuccessStatus(self):
            for page in self.pages:
                set_test_status(page, "passed", "Success")
    
        def getFailedStatus(self):
            for page in self.pages:
                set_test_status(page, "failed", "Test failed")
    
        def closeBrowser(self):
            for browser in self.browsers:
                browser.close()

```
***FileName **— testScenarios/parallelSignupRun.py*

```
    import sys
    sys.path.append(sys.path[0] + "/..")
    
    from testScripts.parallelSignup import Register
    from playwright.sync_api import sync_playwright
    
    
    with sync_playwright() as playwright:
    
        try:
            playwright = Register(playwright)
            playwright.launchWeb()
            playwright.fillFirstName("Idowu")
            playwright.fillLastName("Omisola")
            playwright.fillEmail("some7gmail@gmail.com")
            playwright.fillPhone("08122334433")
            playwright.fillPassword("mypassword")
            playwright.confirmPassword("mypassword")
            playwright.subscribe()
            playwright.acceptPolicy()
            playwright.submit()
            playwright.continueToDashboard()
            playwright.getSuccessStatus()
        except Exception as err:
            playwright.getFailedStatus()
    
        playwright.closeBrowser() 


```
**Playwright Python Code Walkthrough:**

**FileName — testScripts/parallelSignup.py:**

We import the needed modules for the test at the top of the script:

```
    from playwright.sync_api import sync_playwright
        import sys
```

The **sys** module points Python to the root directory path. Then we import the **elementSelector** and **testCapabilities** classes:

```
    from elementSelectors.registrationPageSelectors import elementSelector
        from testCapabilities.testCaps import testCapabilities
```

Next is the object instance creation. The **select** variable is the elementSelector class instance, while **capability** holds the **testCapabilities** class instance:

```

    select = elementSelector()
        
        capability = testCapabilities()
```

We initiate the **Register** class as we did in the previous single tests. But this time, we insert the **capability** methods (**Chrome()** and **Edge()**) into a list — so we can iterate through each browser instance to spin up a cross-browser parallel test.

So notably within the **__init__** function:

```
    self.allCaps = [capability.Edge(), capability.Chrome()]
```

Next is an iteration of the browser contexts, ending with the self.pages inheriting the pages list:

```
    self.browsers = [playwright.chromium.connect(i) for i in self.allCaps]
                pages = [i.new_page() for i in self.browsers]
                self.pages = pages
```

The **launchWeb()** method also iterates through the browser contexts to launch the test website in each browser. We then print out the title for each page:

```

    def launchWeb(self):
                for page in self.pages:
                    page.goto(select.webPage())
                    title = page.title()
                    print(title)
```

This iteration continues for every test method under the **Register **class:

  

```
  def fillFirstName(self, data):
                for page in self.pages:
                    page.locator(select.firstName()).fill(data)
            def fillLastName(self, data):
                for page in self.pages:
                    page.locator(select.lastName()).fill(data)
            def fillEmail(self, data):
                for page in self.pages:
                    page.locator(select.eMail()).fill(data)
            def fillPhone(self, data):
                for page in self.pages:
                    page.locator(select.Telephone()).fill(data)
            def fillPassword(self, data):
                for page in self.pages:
                    page.locator(select.Password()).fill(data)
            def confirmPassword(self, data):
                for page in self.pages:
                    page.locator(select.confirmPassword()).fill(data)
            def subscribe(self):
                for page in self.pages:
                    page.locator(select.Subscribe()).click()
            def acceptPolicy(self):
                for page in self.pages:
                    page.locator(select.privacyPolicy()).click()
            def submit(self):
                for page in self.pages:
                    page.locator(select.Submit()).click()
            def continueToDashboard(self):
                for page in self.pages:
                    page.locator(select.goToDashboard()).click()
```

We then defined the status methods to get the appropriate messages in the grid UI:

```
    def getSuccessStatus(self):
                for page in self.pages:
                    set_test_status(page, "passed", "Success")
        
            def getFailedStatus(self):
                for page in self.pages:
                    set_test_status(page, "failed", "Test failed")
```

The script ends by closing the browser context.

 

```
   def closeBrowser(self):
                for browser in self.browsers:
                    browser.close()

```
**FileName — testScenarios / parallelSignupRun.py:**

Nothing much has changed about the test runner file here. We import all the modules and point Python to the root directory, as we did earlier. However, this time, we import the **Register** class from the **parallelSignup** module since this is a parallel test:

```
    import sys
        sys.path.append(sys.path[0] + "/..")
        
        from testScripts.parallelSignup import Register
        from playwright.sync_api import sync_playwright

from playwright.sync_api import sync_playwright
```

The methods called under **sync_playwright()** event loop run the parallel signup test using the **Register** class instance (playwright):

```
    with sync_playwright() as playwright:
        
            try:
                playwright = Register(playwright)
```

The test steps haven’t changed. But this time, they run in parallel:

**Step 1:** Go to the registration page on both browser contexts in consideration:

```

    playwright.launchWeb()
```

**Step 2**: Filling of the registration form occurs in parallel on both browsers:

  

```
  playwright.fillFirstName("Idowu")
                playwright.fillLastName("Omisola")
                playwright.fillEmail("some7gmail@gmail.com")
                playwright.fillPhone("08122334433")
                playwright.fillPassword("mypassword")
                playwright.confirmPassword("mypassword")
                playwright.subscribe()
                playwright.acceptPolicy()
```

**Step 3**: Submit the form on both browsers:

```
    playwright.submit()
```

**Step 4**: Go to your dashboard for the first time. Then end the test execution with a status message on the grid UI (passed or failed):

```
    playwright.continueToDashboard()
                playwright.getSuccessStatus()
            except Exception as err:
                playwright.getFailedStatus()
```

Finally, call the **closeBrowser()** method to close all browser instances and end the test:

 

```
   playwright.closeBrowser()
```

**Registration Parallel Test Execution:**

Run the parallelSignupRun.py file:

 

```
   python parallelSignupRun.py
```

Our parallel cross-browser test cases run simultaneously on the LambdaTest cloud grid as shown:

![](https://cdn-images-1.medium.com/max/3812/0*G2mE57Un1tmzqc_e.jpg)

**Implementation (Login and Purchase use case):**

***FileName **— testScripts / parallelLoginBuyScript.py*

```
    from playwright.sync_api import sync_playwright
    import sys
    sys.path.append(sys.path[0] + "/..")
    
    
    from elementSelectors.loginAndBuySelectors import elementSelector
    from testCapabilities.testCaps import testCapabilities
    
    select = elementSelector()
    
    capability = testCapabilities()
    
    # Setting grid status to failed or passed
    def set_test_status(page, status, remark):
            page.evaluate("_ => {}",
            "lambdatest_action: {\"action\": \"setTestStatus\", \"arguments\": {\"status\":\"" + status + "\", \"remark\": \"" + remark + "\"}}")
    
    class LoginAndBuy:
       
       
        def __init__(self, playwright) -> None:
            self.allCaps = [capability.Edge(), capability.Chrome()]
            self.browsers = [playwright.chromium.connect(i) for i in self.allCaps]
            pages = [i.new_page() for i in self.browsers]
            self.pages = pages
    
        def launchWeb(self):
            for page in self.pages:
                page.goto(select.webPage())
                title = page.title()
                print(title)
    
        def fillEmail(self, data):
            for page in self.pages:
                page.locator(select.eMail()).fill(data)
       
        def fillPassword(self, data):
            for page in self.pages:
                page.locator(select.Password()).fill(data)
    
        def clickLogin(self):
            for page in self.pages:
                page.locator(select.loginAccount()).click()
       
        def fillSearchBox(self, data):
            for page in self.pages:
                page.locator(select.searchProduct()).fill(data)
       
        def clickSearchButton(self):
            for page in self.pages:
                page.locator(select.searchButton()).click()
    
        def clickProduct(self):
            for page in self.pages:
                page.locator(select.Product()).click()
    
        def clickAddToCart(self):
            for page in self.pages:
                page.locator(select.addCart()).click()
       
        def clickCheckOutModal(self):
            for page in self.pages:
                page.locator(select.checkOut()).click()
       
        def hoverMenuBox(self):
            for page in self.pages:
                page.locator(select.hoverBox()).hover()
       
        def clickLogout(self):
            for page in self.pages:
                page.locator(select.logoutUser()).click()
    
        def getSuccessStatus(self):
            for page in self.pages:
                set_test_status(page, "passed", "Success")
    
        def getFailed(self):
            for page in self.pages:
                set_test_status(page, "failed", "Test failed")
    
        def closeBrowser(self):
            for browser in self.browsers:
                browser.close()

```
***FileName **— testScenarios / parallelLoginBuyRun.py:*

```
    import sys
    sys.path.append(sys.path[0] + "/..")
    
    from testScripts.parallelLoginBuyScript import LoginAndBuy
    from playwright.sync_api import sync_playwright
    
    
    with sync_playwright() as playwright:
        try:
            playwright = LoginAndBuy(playwright)
            playwright.launchWeb()
            playwright.fillEmail("some3gmail@gmail.com")
            playwright.fillPassword("mypassword")
            playwright.clickLogin()
            playwright.fillSearchBox("Nikon")
            playwright.clickSearchButton()
            playwright.clickProduct()
            playwright.clickAddToCart()
            playwright.clickCheckOutModal()
    
            playwright.hoverMenuBox()
            playwright.clickLogout()
            playwright.getSuccessStatus()
        except:
            playwright.getFailed()
        playwright.closeBrowser()
```

![](https://cdn-images-1.medium.com/max/2000/0*6s0gn4HBN3sWHpA0.png)

**Playwright Python Code Walkthrough:**

**FileName — testScripts / parallelLoginBuyScript.py:**

We start by importing all necessary modules for the test case inside the **parallelLoginBuyScript.py** file:

 

```
   from playwright.sync_api import sync_playwright
        import sys
        sys.path.append(sys.path[0] + "/..")
        
        from elementSelectors.loginAndBuySelectors import elementSelector
        from testCapabilities.testCaps import testCapabilities

```
Next is an instance of the **elementSelector** and **testCapabilities** classes:

  

```
  select = elementSelector()
        
        capability = testCapabilities()
```

We then set up the test status function to trigger failed or success messages on the cloud grid UI. Remember, we’ll implement this as a method of the **loginAndBuy** class:

```
    def set_test_status(page, status, remark):
                page.evaluate("_ =&gt; {}",
                "lambdatest_action: {\"action\": \"setTestStatus\", \"arguments\": {\"status\":\"" + status + "\", \"remark\": \"" + remark + "\"}}")
```

The **loginAndBuy** inherits the Playwright class methods. As you can see, the **__init__ **function accepts an extra **playwright** argument:

```

    class LoginAndBuy:
            def __init__(self, playwright) -&gt; None:
```

We have to iterate through the browser capabilities, then instantiate a browser context for each. Thus, the **self.pages** attribute inherits the **pages** list created from the loop:

```
    def __init__(self, playwright) -&gt; None:
                self.allCaps = [capability.Edge(), capability.Chrome()]
                self.browsers = [playwright.chromium.connect(i) for i in self.allCaps]
                pages = [i.new_page() for i in self.browsers]
                self.pages = pages
```

Next, we launch the test website on both browser contexts inside a loop:

 

```
   def launchWeb(self):
                for page in self.pages:
                    page.goto(select.webPage())
                    title = page.title()
                    print(title)
```

The rest of the methods in the class take an event handler, which we’ll run inside another file in an event loop:

  

```
  def fillEmail(self, data):
                for page in self.pages:
                    page.locator(select.eMail()).fill(data)
           
            def fillPassword(self, data):
                for page in self.pages:
                    page.locator(select.Password()).fill(data)
        
            def clickLogin(self):
                for page in self.pages:
                    page.locator(select.loginAccount()).click()
           
            def fillSearchBox(self, data):
                for page in self.pages:
                    page.locator(select.searchProduct()).fill(data)
           
            def clickSearchButton(self):
                for page in self.pages:
                    page.locator(select.searchButton()).click()
        
            def clickProduct(self):
                for page in self.pages:
                    page.locator(select.Product()).click()
        
            def clickAddToCart(self):
                for page in self.pages:
                    page.locator(select.addCart()).click()
           
            def clickCheckOutModal(self):
                for page in self.pages:
                    page.locator(select.checkOut()).click()
           
            def hoverMenuBox(self):
                for page in self.pages:
                    page.locator(select.hoverBox()).hover()
           
            def clickLogout(self):
                for page in self.pages:
                    page.locator(select.logoutUser()).click()
```

We also declare the test status functions as methods under the **loginAndBuy** class:

 

```
   def getSuccessStatus(self):
                for page in self.pages:
                    set_test_status(page, "passed", "Success")
        
            def getFailed(self):
                for page in self.pages:
                    set_test_status(page, "failed", "Test failed")
```

Finally, we iterate through the browser contexts to close the opened instances:

```
    def closeBrowser(self):
                for browser in self.browsers:
                    browser.close()
```

**FileName — testScenarios / parallelLoginBuyRun.py:**

We import all the essential packages in the parallel test runner file. However, the custom class of interest this time is **loginAndBuy**:

```
    import sys
        sys.path.append(sys.path[0] + "/..")
        
        from testScripts.parallelLoginBuyScript import LoginAndBuy
        from playwright.sync_api import sync_playwright

```
The **playwright_sync** event loop runs the parallel test case, starting with a **playwright** instance of the **loginAndBuy** class, followed by website launch on both browser contexts:

```

    with sync_playwright() as playwright:
            try:
                playwright = LoginAndBuy(playwright)
                playwright.launchWeb()
```

**Step 1:** Fill out the login form and sign in on both browser contexts considered for the parallel test:

```
    playwright.fillEmail("some3gmail@gmail.com")
        playwright.fillPassword("mypassword")
        playwright.clickLogin()
```

**Step 2**: Search for a product and click the search button:

   

```
 playwright.fillSearchBox("Nikon")
        playwright.clickSearchButton()
```

**Step 3:** Go to the product page by clicking it once found:

```
    playwright.clickProduct()
```

**Step 4**: Add the product to the cart by clicking the **ADD TO CART** button:

   

```
 playwright.clickAddToCart()
```

**Step 5:** Check out of the product page:

   

```
 playwright.clickCheckOutModal()
```

**Step 6**: Log out of the website and return a success status on the LambdaTest cloud grid UI:

 

```
   playwright.hoverMenuBox()
        playwright.clickLogout()
        playwright.getSuccessStatus()

```
If the test fails for whatever reason, the **getFailed()** method within the **except** block runs and returns a failed message on the grid UI:

```
    except:
                playwright.getFailed()
```

It’s also important to close the event loop for each browser context to prevent an endless loop. We do this outside the **try-except** block to avoid termination conflict:

  

```
  playwright.closeBrowser()
```

**Execution of the Login and Purchase Parallel Test:**

Run the **parallelLoginBuyRun.py** file.

    

```
python parallelLoginBuyRun.py
```

Below is the parallel test execution for the selected browser on the cloud grid:

![](https://cdn-images-1.medium.com/max/3200/0*2sGoclRtS_0VIdSG.jpg)

***Check this out: Test On [Online Emulator Android](https://www.lambdatest.com/android-emulator-online?utm_source=devto&utm_medium=organic&utm_campaign=dec22_sd&utm_term=sd&utm_content=webpage)- Test your web and mobile apps on Android Emulators online.***

## Conclusion

The Playwright testing framework provides simple methods for end to end testing. In this article, we demonstrated the use of Playwright Python for single and parallel testing. And we wrapped all our test cases inside the POM model for simplicity.

We started by exploring the power of Playwright’s code generator and how it can help us automatically create test case code and selectors. But since that method is complex and unscalable, we further implemented a single custom end to end Playwright test on user registration and product purchase on the LambdaTest e-commerce playground.

This led us to perform a cross-browser parallel test for a similar test scenario. And you’ve also seen how to create class methods that connect your tests to the LambdaTest cloud grid.