Showing Posts From

Playwright

Streamlining Browser Automation Harnessing Playwright with Docker for Seamless Testing

Streamlining Browser Automation Harnessing Playwright with Docker for Seamless Testing

Post:In the ever-evolving landscape of web development, automated testing has become a crucial component for ensuring the quality and reliability of applications. Among the various tools available, Playwright stands out as a powerful framework for browser automation, allowing developers to test web applications across multiple browsers with ease. When combined with Docker, Playwright’s capabilities can be further enhanced, providing a robust, consistent, and efficient testing environment. In this blog post, we will explore how to set up and use Playwright with Docker to streamline your browser automation processes. Why Use Playwright?Playwright is an open-source automation library developed by Microsoft that enables you to control headless or full browser instances. Here are a few reasons why developers choose Playwright:Cross-Browser Support: Playwright supports multiple browsers, including Chromium, Firefox, and WebKit, ensuring that your web application functions correctly across different platforms.Rich API: With a rich set of APIs, Playwright provides various features such as auto-waiting, capturing screenshots, and intercepting network requests.Headless Mode: Playwright can run tests in headless mode, allowing for faster execution and the ability to run tests in continuous integration (CI) environments.The Benefits of DockerDocker is a platform that enables developers to automate the deployment of applications inside lightweight containers. Using Docker for your Playwright tests brings several advantages:Environment Consistency: Docker ensures that your testing environment is consistent across different machines and stages of development, minimizing the "it works on my machine" problem. Isolation: Each test can run in isolation, eliminating the risk of tests interfering with each other. Scalability: With Docker, you can easily scale your testing infrastructure by running multiple containers in parallel.Setting Up Playwright with DockerLet’s walk through the steps to set up Playwright with Docker. Step 1: Create a DockerfileStart by creating a Dockerfile in your project directory. This file will define the environment for running your Playwright tests. Here’s a simple example of a Dockerfile: # Use the official Node.js image FROM mcr.microsoft.com/playwright:v1.21.0# Set the working directory WORKDIR /app# Copy package.json and install dependencies COPY package.json ./ RUN npm install# Copy the rest of your application code COPY . .# Run your tests (modify as needed) CMD ["npx", "playwright", "test"]This Dockerfile uses the official Playwright image, which comes with all necessary browsers pre-installed. It sets up the working directory, installs dependencies, and specifies the command to run your tests. Step 2: Build the Docker ImageOnce the Dockerfile is ready, you can build the Docker image by running the following command in your terminal: docker build -t playwright-docker .This command will create a new Docker image named playwright-docker. Step 3: Run the TestsAfter building the image, you can run your tests in a container using the following command: docker run --rm playwright-dockerThe --rm flag ensures that the container is removed after the tests are finished, keeping your environment clean. Tips for Using Playwright with DockerUse Docker Compose: For more complex setups, consider using Docker Compose to manage multiple services and dependencies (e.g., a web server alongside your tests). Parallel Testing: Leverage Docker’s ability to run multiple containers to execute tests in parallel, significantly reducing the overall test execution time. Headless vs. Headful: Depending on your needs, you can run tests in headless mode for speed or in headful mode for debugging purposes.ConclusionIntegrating Playwright with Docker not only simplifies the setup of your testing environment but also enhances the reliability and scalability of your automated testing processes. By following the steps outlined in this blog post, you can create a robust system for testing your web applications efficiently. Explore the rich features of Playwright, and let Docker take care of the environment—resulting in a seamless and powerful browser automation experience. Happy testing!

Unlocking the Power of Advanced Playwright Features Your Guide to Efficient Browser Automation

Unlocking the Power of Advanced Playwright Features Your Guide to Efficient Browser Automation

In the ever-evolving landscape of web development and testing, browser automation has become a critical component for ensuring quality and performance. Among the myriad of tools available, Playwright has emerged as a powerful choice for developers seeking robust and flexible browser automation capabilities. This post will delve into some of the advanced features of Playwright that can supercharge your automation efforts, streamline workflows, and enhance testing efficiency. 1. Multi-Browser SupportOne of the standout features of Playwright is its ability to automate multiple browsers with a single API. Whether you're testing on Chromium, Firefox, or WebKit, Playwright allows you to write your tests once and run them across different browsers seamlessly. This cross-browser capability ensures that your web application behaves consistently in various environments, making it easier to catch browser-specific issues early in the development process. 2. Auto-Waiting MechanismGone are the days of manually inserting sleep or wait statements in your test scripts. Playwright’s auto-waiting mechanism intelligently waits for elements to be actionable before executing actions. This means that your tests become more robust and less flaky, as they can handle varying load times and dynamic content without additional coding overhead. 3. Network Interception and MockingWith Playwright, you can intercept and modify network requests on the fly. This feature is particularly useful for testing edge cases or simulating various server responses without altering the backend. You can mock API calls, manipulate request headers, or simulate slow network conditions, allowing you to test your application under various scenarios without relying on an external server. 4. Screenshot and Video RecordingVisual regression testing has never been easier with Playwright's built-in screenshot and video recording capabilities. You can capture full-page screenshots or specific elements at any point during your test execution. Additionally, recording videos of your tests can provide valuable insights when debugging failures, making it easy to identify the root cause of issues quickly. 5. Parallel TestingSpeed up your testing process by leveraging Playwright’s ability to run tests in parallel. By splitting tests across multiple browser instances or devices, you can significantly reduce the time it takes to complete your test suite. This feature is particularly beneficial for large projects where test execution time can become a bottleneck. 6. Custom Test HooksPlaywright supports the implementation of custom test hooks, allowing you to define behavior that should happen before or after tests run. This feature is invaluable for setting up test environments, cleaning up resources, or logging results. By organizing your test lifecycle more effectively, you can create a more maintainable and scalable test suite. 7. Integration with CI/CD PipelinesIntegrating Playwright into your Continuous Integration/Continuous Deployment (CI/CD) pipeline can enhance your development workflow. Playwright is designed to work seamlessly with popular CI tools like GitHub Actions, Jenkins, and CircleCI. This integration ensures that your tests run automatically with each commit, allowing for rapid feedback and facilitating a more agile development process. ConclusionThe advanced features of Playwright provide developers and testers with a powerful toolkit for browser automation that goes beyond basic functionality. By harnessing these capabilities, you can create efficient, reliable, and maintainable test suites that enhance your overall development workflow. Whether you’re a seasoned automation engineer or just starting your journey, exploring these advanced features will undoubtedly help you unlock the full potential of Playwright and achieve higher-quality web applications. So why wait? Dive into Playwright today and transform your browser automation experience!

Exploring Advanced Features of Playwright Enhancing Your Automation Skills

Exploring Advanced Features of Playwright Enhancing Your Automation Skills

In the ever-evolving landscape of web automation, Playwright has emerged as a powerful tool for developers seeking to streamline their testing and automation processes. With its robust features and capabilities, Playwright not only simplifies the automation of web applications but also empowers users to create more sophisticated and efficient test scripts. In this blog post, we’ll delve into some advanced features of Playwright that can significantly enhance your automation skills. 1. Multi-Browser SupportOne of Playwright's standout features is its ability to automate testing across multiple browsers. Unlike some other frameworks, which may support only a limited number of browsers, Playwright allows you to run your tests on Chromium, Firefox, and WebKit, all from a single API. This cross-browser compatibility ensures that you can maintain consistency in your testing practices while also catching browser-specific issues early in the development cycle. 2. Auto-Waiting MechanismPlaywright’s intelligent auto-waiting capabilities reduce the need for manual sleep commands in your tests. The framework automatically waits for elements to be visible, stable, and enabled before proceeding with actions. This not only makes your tests more resilient but also helps in reducing flakiness, which is often a challenge in UI testing. 3. Network InterceptionWith Playwright, you can intercept and manipulate network requests, which is invaluable for testing scenarios that require precise control over server responses. This feature allows you to mock API responses, test error handling, and simulate various network conditions without the need for backend modifications. By leveraging network interception, you can create more comprehensive test cases that cover a wider array of user interactions. 4. Built-in Screenshot and Video RecordingTesting isn’t just about passing or failing; visual feedback can be critical for diagnosing issues. Playwright provides built-in capabilities for taking screenshots and recording videos of your tests. This is especially useful when debugging failed tests, as you can visually inspect what went wrong. The flexibility to capture these media files at any point during the test execution adds an extra layer of insight into your automated workflows. 5. Parallel Test ExecutionSpeed is often a concern in automated testing, particularly for large applications. Playwright supports parallel test execution, allowing you to run tests concurrently across multiple browser instances. By splitting your test suite into smaller, manageable chunks that can be executed simultaneously, you can significantly reduce the time it takes to run your tests, leading to quicker feedback loops and faster release cycles. 6. Customizable Test HooksPlaywright allows you to use hooks to customize the lifecycle of your tests. With hooks like beforeAll, afterAll, beforeEach, and afterEach, you can set up and tear down your test environment seamlessly. This feature is particularly useful for tasks that require repetitive setup or cleanup, such as database resets or user authentication. 7. Integration with CI/CD PipelinesIntegrating Playwright into your Continuous Integration/Continuous Deployment (CI/CD) pipelines can drastically improve your development workflow. Playwright’s ability to run headless tests means you can easily incorporate it into your CI/CD tools, ensuring that your tests are executed automatically with every code change. This integration promotes a culture of quality assurance and helps catch issues early in the development process. ConclusionPlaywright is more than just a tool for writing tests; it’s a comprehensive solution that empowers developers to create robust, scalable, and efficient automation scripts. By exploring and utilizing these advanced features, you can take your automation skills to the next level, ensuring that your applications not only work as intended but also offer an exceptional user experience across various platforms. Whether you’re a seasoned automation engineer or just getting started with Playwright, embracing these advanced features will undoubtedly enhance your testing strategy and help you deliver high-quality software with confidence. Happy testing!

Unleashing the Power of Playwright Advanced Features to Elevate Your Automation Game

Unleashing the Power of Playwright Advanced Features to Elevate Your Automation Game

In the ever-evolving landscape of web automation, Playwright has emerged as a powerful tool, offering developers a robust framework for testing and interaction with web applications. While many users are familiar with the basics of Playwright, the advanced features often remain underutilized. In this post, we will explore some of the advanced capabilities of Playwright that can help you elevate your automation strategy and make your testing processes more efficient and effective. 1. Multiple Browser ContextsOne of the standout features of Playwright is its ability to create multiple browser contexts within a single instance. This allows you to simulate different users or sessions without the overhead of launching multiple browsers. For example, you can log in as different users in separate contexts, enabling comprehensive testing of user-specific scenarios. This feature is particularly useful for applications with complex user roles and permissions. Example: const { chromium } = require('playwright');(async () => { const browser = await chromium.launch(); const context1 = await browser.newContext(); const context2 = await browser.newContext(); const page1 = await context1.newPage(); const page2 = await context2.newPage(); await page1.goto('https://example.com/login'); await page2.goto('https://example.com/login'); await page1.fill('#username', 'user1'); await page1.fill('#password', 'password1'); await page1.click('#submit'); await page2.fill('#username', 'user2'); await page2.fill('#password', 'password2'); await page2.click('#submit'); await browser.close(); })();2. Network Interception and MockingPlaywright offers extensive capabilities for intercepting network requests. This allows you to mock API responses, test edge cases, and simulate various network conditions. By controlling the server responses, you can validate that your application behaves correctly under different scenarios without relying on the actual backend. Example: const { webkit } = require('playwright');(async () => { const browser = await webkit.launch(); const context = await browser.newContext(); const page = await context.newPage(); await page.route('**/api/data', (route) => { route.fulfill({ contentType: 'application/json', body: JSON.stringify({ data: 'mocked data' }), }); }); await page.goto('https://example.com'); // Your test logic here await browser.close(); })();3. Advanced Locator StrategiesPlaywright’s locator strategies go beyond simple selectors, allowing for more complex queries and interactions with elements. You can use text, roles, and even custom predicates to locate elements more reliably. This is especially useful for dynamic web applications where elements may change frequently. Example: const { firefox } = require('playwright');(async () => { const browser = await firefox.launch(); const context = await browser.newContext(); const page = await context.newPage(); await page.goto('https://example.com'); const button = page.locator('button:has-text("Submit")'); await button.click(); await browser.close(); })();4. Tracing and DebuggingPlaywright includes built-in tracing capabilities that allow you to capture a detailed record of your test runs. This feature is invaluable for debugging, as it provides a step-by-step playback of your test, including network activity, screenshots, and console logs. By analyzing traces, you can quickly identify issues and optimize your tests. Example: const { chromium } = require('playwright');(async () => { const browser = await chromium.launch(); const context = await browser.newContext(); const page = await context.newPage(); await context.tracing.start({ screenshots: true, snapshots: true }); await page.goto('https://example.com'); await page.click('text=Get Started'); await context.tracing.stop({ path: 'trace.zip' }); await browser.close(); })();5. Headless and Headful ModesPlaywright supports both headless and headful (with a GUI) modes for your browser sessions. While headless mode is ideal for CI/CD environments where you want to run tests quickly, headful mode is great for local development and debugging when you want to visually inspect your automation. ConclusionPlaywright is a versatile and powerful tool for automating web applications. By leveraging its advanced features—such as multiple browser contexts, network interception, sophisticated locator strategies, tracing, and the flexibility of headless and headful modes—you can significantly enhance your testing framework. As you explore these capabilities, you'll find that Playwright not only simplifies your testing processes but also empowers you to deliver higher quality applications faster. So, dive into the world of Playwright and discover how these advanced features can transform your automation experience today!

Playwright framework implementation - Part 2

Playwright framework implementation - Part 2

Hoping the you have completed part 1 of the post, let’s move on to part 2!! In this post, let's tryin make use of the files that we haven't used in the part 1. We are gonna create a new test for creating a new user and checking whether the user is landed in the My Account page after sign up First things first. Creating the files that we need for the next test. Run the below command from the project directory cd src/apps/automation-practice && touch data/create-account.data.json && touch data/my-account.data.json && touch locators/create-account.locator.ts && touch locators/my-account.locator.ts && touch pages/create-account.page.ts && touch pages/my-account.page.ts && touch tests/create-account.spec.ts && touch tests/my-account.spec.tsLet’s take a moment to plan on how to tackle the sign-up functionality. We know that we need a unique email and password everytime we need to test this. Considering this, let's use an existing package named faker-js. faker-js is a package that is used to provide dummy but valid data for testing. Fortunately, this package is already part of the package.json file and no action needed for this 😇 Now that we know that we need to create unique test data for every run, we also need to persist the existing data, so that we can use it later. Well, good news! We have that as well covered in the frameworkStep # 1Starting with adding test data to the *.data.json files As we discussed, the test data for new account creation is gonna use the faker-js, all we have to do is add an empty array to src/apps/automation-practice/data/create-account.data.json as below []And for the My Account page validation, we are going to validate whether the user is navigated to the My Account page after sign up. All we are doing here is to verify the title of the page matches with the My Account page title. Add the below to src/apps/automation-practice/data/my-account.data.json file { "title": "My Account Magento Commerce - website to practice selenium | demo website for automation testing | selenium practice sites" }Step # 2Now that we have the test data task out of our way, let's close the creation of page objects task by updating the *.locator.json files. Add the below code to src/apps/automation-practice/locators/create-account.locator.ts export const firstNameEdit = 'input[name="firstname"]'; export const lastNameEdit = 'input[name="lastname"]'; export const emailEdit = 'input[name="email"]'; export const passwordEdit = 'input[name="password"]'; export const passwordConfirm = 'input[name="password_confirmation"]'; export const createAccountButton = 'button:has-text("Create an Account")';Since, we are only validating the title in the My Account page and not any elements within the page, we can ignore the src/apps/automation-practice/locators/my-account.locator.ts file for nowStep # 3Start creating the necessary steps for the test. Add the below code to src/apps/automation-practice/pages/create-account.page.ts import path from "path"; import fs from "fs"; import type { Page, TestInfo } from "@playwright/test"; import { test } from "../fixtures"; import * as locators from "../locators/create-account.locator"; import * as actions from "@utils/base/web/actions"; import { appendFile } from "@home/src/utils/functions/file"; import { faker } from "@faker-js/faker";export default class CreateAccountPage { constructor(public page: Page, public workerInfo: TestInfo) {} async createAccount() { const dataFilePath = path.join( __dirname, "..", "data", "create-account.data.json" ); const firstName = faker.name.firstName(); const lastName = faker.name.lastName(); const email = faker.internet.email(); const password = faker.internet.password(); const account = { firstName, lastName, email, password, confirmPassword: password, }; await appendFile(dataFilePath, account); const data = JSON.parse(fs.readFileSync(dataFilePath, "utf-8")); await test.step( this.workerInfo.project.name + ": Enter first name: " + data[data.length - 1].firstName, async () => await actions.fill( this.page, locators.firstNameEdit, data[data.length - 1].firstName, this.workerInfo ) ); await test.step( this.workerInfo.project.name + ": Enter last name: " + data[data.length - 1].lastName, async () => await actions.fill( this.page, locators.lastNameEdit, data[data.length - 1].lastName, this.workerInfo ) ); await test.step( this.workerInfo.project.name + ": Enter email: " + data[data.length - 1].email, async () => await actions.fill( this.page, locators.emailEdit, data[data.length - 1].email, this.workerInfo ) ); await test.step( this.workerInfo.project.name + ": Enter password: " + data[data.length - 1].password, async () => await actions.fill( this.page, locators.passwordEdit, data[data.length - 1].password, this.workerInfo ) ); await test.step( this.workerInfo.project.name + ": Enter confirm password: " + data[data.length - 1].confirmPassword, async () => await actions.fill( this.page, locators.passwordConfirm, data[data.length - 1].confirmPassword, this.workerInfo ) ); await test.step( this.workerInfo.project.name + ": Click create account button ", async () => await actions.clickElement( this.page, locators.createAccountButton, this.workerInfo ) ); } }Add the below code to src/apps/automation-practice/pages/my-account.page.ts import type { Page, TestInfo } from "@playwright/test"; import * as data from "../data/my-account.data.json"; import * as actions from "@utils/base/web/actions";export default class MyAccountPage { constructor(public page: Page, public workerInfo: TestInfo) {} async verifyPageTitle() { await actions.verifyPageTitle(this.page, data.title, this.workerInfo); } }Step # 4Updating fixtures. Once we have added the steps that needs to be performed for the test, it's time for us to update the fixtures. Your src/apps/automation-practice/fixtures/index.ts file should looks like below import { test as baseTest } from "@playwright/test"; import CommonPage from "@common/pages/common.page"; import HomePage from "../pages/home.page"; import CreateAccountPage from "../pages/create-account.page"; import MyAccountPage from "../pages/my-account.page";type pages = { commonPage: CommonPage; homePage: HomePage; createAccountPage: CreateAccountPage; myAccountPage: MyAccountPage; };const testPages = baseTest.extend<pages>({ commonPage: async ({ page }, use, workerInfo) => await use(new CommonPage(page, workerInfo)), homePage: async ({ page }, use, workerInfo) => await use(new HomePage(page, workerInfo)), createAccountPage: async ({ page }, use, workerInfo) => await use(new CreateAccountPage(page, workerInfo)), myAccountPage: async ({ page }, use, workerInfo) => await use(new MyAccountPage(page, workerInfo)), });export const test = testPages; export const expect = testPages.expect; export const describe = testPages.describe;Step # 5Time to create our test. Before we add the test code to the *.spec.ts files, we need to add the locators in src/apps/automation-practice/locators/home.locator.ts to click on the Create Account link in home page Add the below code to src/apps/automation-practice/locators/home.locator.ts export const createAccountLink = "text=Create an Account";Add the below code to src/apps/automation-practice/tests/create-account.spec.ts import { test, describe } from "../fixtures"; import * as homePageLocators from "../locators/home.locator"; import * as createAccountLocators from "../locators/create-account.locator";test.beforeEach(async ({ homePage }) => { await homePage.navigateToAutomationPractice(); });describe("New Account", () => { test("Create new account", async ({ homePage, commonPage, createAccountPage, myAccountPage, }) => { await homePage.clickLink(homePageLocators.createAccountLink); await commonPage.waitForAnimationEnd( createAccountLocators.createAccountButton ); await createAccountPage.createAccount(); await myAccountPage.verifyPageTitle(); }); });Add the below code to src/apps/automation-practice/tests/my-account.spec.ts import { test, describe } from "../fixtures";describe("My Account", () => { test("Verify account page url and title", async ({ myAccountPage }) => { await myAccountPage.verifyPageTitle(); }); });Time to test 🍹Step # 6Change the testMatch option in src/apps/automation-practice/playwright.config.ts as below testMatch: "tests/create-account.spec.ts",and run yarn test:automation-practice Hoping that you have done everything right until now, you would now be able to see something like belowAnd you should be able to see the newly created users test data in src/apps/automation-practice/data/create-account.data.json That's all for today. Happie coding 📣

Playwright framework implementation - Part 1

Playwright framework implementation - Part 1

Let’s see the implementation of the playwright framework from the github repo which I created as part of my hands-on effort. Now before I begin, I just wanted to give a little heads-up. This could be a long post and you might have to spend some quality time in order to make the framework up and running. That being said, I will try to reduce the words as much as possible and explain things clear. However, just keep an open mind for now 😉 Couple of things before we start. Let’s quickly spend some time in understanding how the framework is structured, so that you can have a mental picture of how things work when you start making changes. Framework structure . ├── 📁 src │   ├── 📁 apps │   │   └── 📁 common │   │   │   ├── 📁 pages │   │   │   │   ├── 📃 common.page.ts # common page shared b/w apps │   │   └── 📁 app 01 │   │   │   ├── 📁 data │   │   │   │   ├── 📃 page_01.data.json # data for app 01, page 01 │   │   │   │   ├── 📃 page_02.data.json # data for app 01, page 02 │   │   │   │   ├── 📃 ... │   │   │   │   ├── 📃 page_n.data.json # data for app 01, page n │   │   │   ├── 📁 fixtures │   │   │   │   └── 📃 index.ts # app 01 specific fixtures │   │   │   ├── 📁 locators │   │   │   │   ├── 📃 page_01.locator.ts # locators for app 01, page 01 │   │   │   │   ├── 📃 page_02.locator.ts # locators for app 01, page 02 │   │   │   │   ├── 📃 ... │   │   │   │   ├── 📃 page_n.locator.ts # locators for app 01, page n │   │   │   ├── 📁 pages │   │   │   │   ├── 📃 page_01.page.ts # functions for app 01, page 01 │   │   │   │   ├── 📃 page_02.page.ts # functions for app 01, page 02 │   │   │   │   ├── 📃 ... │   │   │   │   ├── 📃 page_n.page.ts # functions for app 01, page n │   │   │   └── 📁 tests │   │   │   │   ├── 📃 page_01.spec.ts # tests for app 01, page 01 │   │   │   │   ├── 📃 page_02.spec.ts # tests for app 01, page 02 │   │   │   │   ├── 📃 ... │   │   │   │   ├── 📃 page_n.spec.ts # tests for app 01, page n │   │   │   ├── 📃 config.json # config data for app 01 │   │   │   ├── 📃 package.json # scripts for app 01 │   │   │   ├── 📃 playwright.config.ts # config details for app 01 │   │   └── 📁 app 02 │   │   │   ├── 📁 data │   │   │   │   ├── 📃 page_01.data.json # data for app 02, page 01 │   │   │   │   ├── 📃 page_02.data.json # data for app 02, page 02 │   │   │   │   ├── 📃 ... │   │   │   │   ├── 📃 page_n.data.json # data for app 02, page n │   │   │   ├── 📁 fixtures │   │   │   │   └── 📃 index.ts # app 02 specific fixtures │   │   │   ├── 📁 locators │   │   │   │   ├── 📃 page_01.locator.ts # locators for app 02, page 01 │   │   │   │   ├── 📃 page_02.locator.ts # locators for app 02, page 02 │   │   │   │   ├── 📃 ... │   │   │   │   ├── 📃 page_n.locator.ts # locators for app 02, page n │   │   │   ├── 📁 pages │   │   │   │   ├── 📃 page_01.page.ts # functions for app 02, page 01 │   │   │   │   ├── 📃 page_02.page.ts # functions for app 02, page 02 │   │   │   │   ├── 📃 ... │   │   │   │   ├── 📃 page_n.page.ts # functions for app 02, page n │   │   │   └── 📁 tests │   │   │   │   ├── 📃 page_01.spec.ts # tests for app 02, page 01 │   │   │   │   ├── 📃 page_02.spec.ts # tests for app 02, page 02 │   │   │   │   ├── 📃 ... │   │   │   │   ├── 📃 page_n.spec.ts # tests for app 02, page n │   │   │   ├── 📃 config.json # config data for app 02 │   │   │   ├── 📃 package.json # scripts for app 02 │   │   │   ├── 📃 playwright.config.ts # config details for app 02 │   │   └── 📁 ... │   │   └── 📁 app n │   │   │   ├── 📁 data │   │   │   │   ├── 📃 page_01.data.json # data for app n, page 01 │   │   │   │   ├── 📃 page_02.data.json # data for app n, page 02 │   │   │   │   ├── 📃 ... │   │   │   │   ├── 📃 page_n.data.json # data for app n, page n │   │   │   ├── 📁 fixtures │   │   │   │   └── 📃 index.ts # app n specific fixtures │   │   │   ├── 📁 locators │   │   │   │   ├── 📃 page_01.locator.ts # locators for app n, page 01 │   │   │   │   ├── 📃 page_02.locator.ts # locators for app n, page 02 │   │   │   │   ├── 📃 ... │   │   │   │   ├── 📃 page_n.locator.ts # locators for app n, page n │   │   │   ├── 📁 pages │   │   │   │   ├── 📃 page_01.page.ts # functions for app n, page 01 │   │   │   │   ├── 📃 page_02.page.ts # functions for app n, page 02 │   │   │   │   ├── 📃 ... │   │   │   │   ├── 📃 page_n.page.ts # functions for app n, page n │   │   │   └── 📁 tests │   │   │   │   ├── 📃 page_01.spec.ts # tests for app n, page 01 │   │   │   │   ├── 📃 page_02.spec.ts # tests for app n, page 02 │   │   │   │   ├── 📃 ... │   │   │   │   ├── 📃 page_n.spec.ts # tests for app n, page n │   │   │   ├── 📃 config.json # config data for app n │   │   │   ├── 📃 package.json # scripts for app n │   │   │   ├── 📃 playwright.config.ts # config details for app n │   └── 📁 utils │   │   ├── 📁 base │   │   │   └── 📁 web │   │   │   │   ├── 📃 actions.ts # actions in web applications │   │   │   │   └── 📃 screenshots.ts # screenshot functions │   │   ├── 📁 functions (utility functions) │   │   ├── 📁 packages (other reusable packages) │   │   └── 📁 reports │   │   │   └── 📃 custom-reporter.ts # custom reporter to pretty print in console ├── 📃 config.init.ts # configuration initializer ├── 📃 global-setup.ts ├── 📃 global-teardown.ts ├── 📃 package.json ├── 📃 playwright.config.ts # global configSo, why is the structure important 🤔 Coz' it gives you the view of the things that you need to add for using this framework for a brand new application. The only problem is to know how to do it sequentially. For example, as soon as you look at the framework structure, you might already understand that you need app_name folder within the src/apps folder within which you would need to create all the sub-folders as shown.The question is which one should I create first and how are these folders linked?Moving on.. First things first. Listed below are the necessary pre-requisites for the framework implementation.Node JS - v18.1.0 or above (what I used while developing) IDE of your choice (VS Code recommended)and... that's it! 💁‍♂️ - If you want to use different versions of node in different projects, use nvmAlright, now that we have completed the pre-requisites, let’s start with the rest of the implementation 🔰Step # 1Clone the git repo using the below command git clone git@github.com:eric-stanley/playwright-framework.gitand cd into the cloned folderStep # 2Let’s assume that the application under test is named automation-practice Start creating the necessary 📁 and 📃 using the below commands. mkdir src/apps/automation-practice && cd src/apps/automation-practice && mkdir data && mkdir fixtures && mkdir locators && mkdir pages && mkdir tests && touch config.json && touch package.json && touch playwright.config.ts && touch data/home.data.json && touch fixtures/index.ts && touch locators/home.locator.ts && touch pages/home.page.ts && touch tests/home.spec.tsCopy the below code and paste it in src/apps/automation-practice/config.json. This file has the environment specific url's for the AUT (Application Under Test) { "env": { "dev": { "url": "https://magento.softwaretestingboard.com/" }, "test": { "url": "https://magento.softwaretestingboard.com/" }, "uat": { "url": "https://magento.softwaretestingboard.com/" } } }🔔 Assuming that we have the same url for all environments 😏 Copy the below code and paste it in src/apps/automation-practice/package.json. Here we define the commands that we use to trigger the necessary run Note: This is a dependent file and will not run standalone. All dependencies for the framework is part of the main package.json which resides in the parent folder { "name": "automation-practice", "version": "1.0.0", "private": true, "author": "Eric Stanley", "license": "MIT", "scripts": { "test": "APP_NAME=automation-practice NODE_ENV=dev playwright test", "test:debug": "APP_NAME=automation-practice NODE_ENV=dev PWDEBUG=1 playwright test", "report": "npx playwright show-report reports/playwright-report", "allure": "npx allure generate reports/allure/allure-result -o reports/allure/allure-report --clean && npx allure open reports/allure/allure-report" } }🔔 Change the author to your name Copy the below code and paste it in src/apps/automation-practice/playwright.config.ts. This file has the app specific playwright config. import { PlaywrightTestConfig, devices } from "@playwright/test";const config: PlaywrightTestConfig = { testDir: "tests", testMatch: "tests/*.spec.ts", timeout: 30 * 1000, retries: 3, workers: 3, globalSetup: require.resolve("@home/global-setup"), globalTeardown: require.resolve("@home/global-teardown"), expect: { timeout: 20000, }, use: { headless: true, actionTimeout: 0, trace: "retain-on-failure", ignoreHTTPSErrors: true, video: "on-first-retry", screenshot: "only-on-failure", acceptDownloads: true, colorScheme: "dark", launchOptions: { slowMo: 500, }, }, reporter: [ ["list"], [ "json", { outputFile: "reports/json-reports/json-report.json", }, ], [ "html", { outputFolder: "reports/playwright-report/", open: "never", }, ], [ "allure-playwright", { outputFolder: "reports/allure/allure-result/", open: "never", }, ], ], projects: [ { name: "chromium", use: { ...devices["Desktop Chrome"] }, }, { name: "firefox", use: { ...devices["Desktop Firefox"] }, }, { name: "webkit", use: { ...devices["Desktop Safari"] }, }, ], };export default config;Now that we have completed the basic setup, let’s move on to step # 3Step # 3Let’s start with writing a basic test for verifying if the home page url contains a specific text and title equals a specific text Starting with the data first. Add the below data in src/apps/automation-practice/data/home.data.json { "urlContains": "softwaretestingboard", "title": "Home Page - Magento eCommerce - website to practice selenium | demo website for automation testing | selenium practice sites | selenium demo sites | best website to practice selenium automation | automation practice sites Magento Commerce - website to practice selenium | demo website for automation testing | selenium practice sites" }Now that we have the data in place, lets start writing the functions for the test. Add the below code to src/apps/automation-practice/pages/home.page.ts import type { Page, TestInfo } from "@playwright/test"; import { test, expect } from "../fixtures"; import * as data from "../data/home.data.json"; import * as actions from "@utils/base/web/actions";export default class HomePage { constructor(public page: Page, public workerInfo: TestInfo) {} async navigateToAutomationPractice() { await actions.navigateTo(this.page, process.env.URL, this.workerInfo); const url = this.page.url(); await test.step( this.workerInfo.project.name + ": Check if URL contains " + data.urlContains, async () => expect(url).toContain(data.urlContains) ); } async verifyPageTitle() { await actions.verifyPageTitle(this.page, data.title, this.workerInfo); } }At this point, you will get an error with the fixtures import line stating that, fixtures/index.ts is not a module. This is fine. We are going to tackle this next 😉 But before that, let's take a moment to look at the code in home.page.ts. Basically, I have added two functions in this class; one for navigating to the home page navigateToAutomationPractice() and the other one to verify the title of the page verifyPageTitle(). The actions is a consolidated list of all actions (or atleast most of the actions) that you could possibly perform in a web page, since we will be using these actions in almost all of our pages, it's only logical to have this as a separate utility! Let’s tackle the fixtures error now. Add the below code in src/apps/automation-practice/fixtures/index.ts import { test as baseTest } from "@playwright/test"; import CommonPage from "@common/pages/common.page"; import HomePage from "../pages/home.page";type pages = { commonPage: CommonPage; homePage: HomePage; };const testPages = baseTest.extend<pages>({ commonPage: async ({ page }, use, workerInfo) => { await use(new CommonPage(page, workerInfo)); }, homePage: async ({ page }, use, workerInfo) => { await use(new HomePage(page, workerInfo)); }, });export const test = testPages; export const expect = testPages.expect; export const describe = testPages.describe;What we do here is basically extending the test object in @playwright/test to add all pages in our application, so that when we start writing our tests, we will be able to easily pull the page objects and its associated functions without creating a new object everytime when we need to access the home.page.ts which might inturn cause a lot of duplication in code. In other words, every time we access the test object from @playwright/test, it automatically injects all the app specific pages in it. Let’s see how it happens next Add the below code in src/apps/automation-practice/tests/home.spec.ts import { test, describe } from "../fixtures";describe("Home", () => { test("Verify home page url and title", async ({ homePage }) => { await homePage.navigateToAutomationPractice(); await homePage.verifyPageTitle(); }); });As you can see the second argument in the test object takes in a function and since we have already extended the homePage object within the test object, we are able to access it with in the function And..... that's it ⏰ Now, before we run the test, there is one last place where we need to update few things which is basically the script object in package.json which is present in the parent directory.Remember, this is not the package.json that we edited in step # 2. This is the one that is present in your parent (root) directoryAdd the below code in the scripts section in package.json file "test:automation-practice": "yarn workspace automation-practice test", "test:debug:automation-practice": "yarn workspace automation-practice test:debug", "report:automation-practice": "yarn workspace automation-practice report", "allure:automation-practice": "yarn workspace automation-practice allure"Once you are done with editing the package.json file in your root directory, you package.json file would look something like below { "name": "playwright-hands-on", "version": "1.0.0", "private": true, "description": "", "workspaces": ["src/apps/*"], "scripts": { "test": "yarn workspace ui-testing-playground test", "test:debug": "yarn workspace ui-testing-playground test:debug", "report": "yarn workspace ui-testing-playground report", "allure": "yarn workspace ui-testing-playground allure", "test:ui-testing-playground": "yarn workspace ui-testing-playground test", "test:debug:ui-testing-playground": "yarn workspace ui-testing-playground test:debug", "report:ui-testing-playground": "yarn workspace ui-testing-playground report", "allure:ui-testing-playground": "yarn workspace ui-testing-playground allure", "test:automation-practice": "yarn workspace automation-practice test", "test:debug:automation-practice": "yarn workspace automation-practice test:debug", "report:automation-practice": "yarn workspace automation-practice report", "allure:automation-practice": "yarn workspace automation-practice allure" }, "author": "Eric Stanley", "license": "MIT", "devDependencies": { "@playwright/test": "^1.25.2", "@types/adm-zip": "^0.5.0", "adm-zip": "^0.5.9", "allure-commandline": "^2.18.1", "allure-playwright": "^2.0.0-beta.19", "colors": "^1.4.0", "playwright": "^1.25.2", "ts-node": "^10.9.1", "typescript": "^4.8.3" } }Step # 4Time to test If you are running the project for the first time run the yarn command from your parent folder to install all dependencies. Once you are done with installing the dependencies, run the below command yarn test:automation-practiceIf you have done everything right, you might probably see something like belowYou might or might not get the Slow test file warning. You can play around with it by changing the timeout value in playwright.config.ts in src/apps/automation-practice folder 🙋 Now, does that mean that we are done?Yep!I told you that this post would be a long one and we have covered a lotta ground until now, but why do we have the home.locator.ts file in the locators folder. And how do we compare screenshots 🤔 Like I said, this could take some time, but at the same time, a lot has been already done. See you in the next post with implementing more features 🥏 Feel free to add your thoughts and comments in the comments section and I will try to address those in time ⏰