Wednesday, March 5, 2025

Dynamic POM spec using typescript

Dynamic Page Object Model (POM) is a design pattern used in test automation to create reusable and maintainable code for interacting with web pages. Using Playwright with TypeScript, you can create a dynamic POM structure that adapts to different pages or components dynamically.


Here’s how you can create a Dynamic POM using TypeScript in Playwright:


## **1. Project Setup**

1. **Initialize a Playwright project**:

       npm init playwright@latest

   Choose TypeScript as the language during setup.


2. **Install dependencies**:

   Playwright is already installed, but ensure TypeScript and related tools are installed:

       npm install --save-dev typescript @playwright/test


3. **Folder structure**:

   Create a folder structure for your POM:


   my-playwright-project/

   ├── pages/                # Page Object files

   │   ├── BasePage.ts       # Base class for all pages

   │   ├── HomePage.ts       # Example page object

   │   └── LoginPage.ts      # Example page object

   ├── tests/                # Test files

   │   └── example.spec.ts   # Example test

   ├── playwright.config.ts  # Playwright configuration

   └── tsconfig.json         # TypeScript configuration



## **2. Create a Base Page Class**

The `BasePage` class will contain common methods and properties that all pages can inherit.


import { Page } from '@playwright/test';


export class BasePage {

  protected page: Page;


  constructor(page: Page) {

    this.page = page;

  }


  // Navigate to a URL

  async navigateTo(url: string): Promise<void> {

    await this.page.goto(url);

  }


  // Generic method to click an element

  async click(selector: string): Promise<void> {

    await this.page.click(selector);

  }


  // Generic method to type into an input field

  async type(selector: string, text: string): Promise<void> {

    await this.page.fill(selector, text);

  }


  // Generic method to get text from an element

  async getText(selector: string): Promise<string> {

    return await this.page.textContent(selector) || '';

  }


  // Generic method to check if an element is visible

  async isVisible(selector: string): Promise<boolean> {

    return await this.page.isVisible(selector);

  }

}



## **3. Create Specific Page Classes**

Extend the `BasePage` class to create specific page objects for different pages.


### **HomePage.ts**


import { BasePage } from './BasePage';


export class HomePage extends BasePage {

  // Selectors for elements on the home page

  private readonly headerSelector = 'h1';

  private readonly loginButtonSelector = 'text=Login';


  // Get the header text

  async getHeaderText(): Promise<string> {

    return await this.getText(this.headerSelector);

  }


  // Click the login button

  async clickLoginButton(): Promise<void> {

    await this.click(this.loginButtonSelector);

  }

}



### **LoginPage.ts**


import { BasePage } from './BasePage';


export class LoginPage extends BasePage {

  // Selectors for elements on the login page

  private readonly usernameInput = '#username';

  private readonly passwordInput = '#password';

  private readonly submitButton = 'button[type="submit"]';


  // Log in with username and password

  async login(username: string, password: string): Promise<void> {

    await this.type(this.usernameInput, username);

    await this.type(this.passwordInput, password);

    await this.click(this.submitButton);

  }

}


## **4. Write a Test Using Dynamic POM**

Now, you can use the page objects in your test files.


### **example.spec.ts**

```typescript

import { test, expect } from '@playwright/test';

import { HomePage } from '../pages/HomePage';

import { LoginPage } from '../pages/LoginPage';


test('Dynamic POM example', async ({ page }) => {

  // Create instances of page objects

  const homePage = new HomePage(page);

  const loginPage = new LoginPage(page);


  // Navigate to the home page

  await homePage.navigateTo('https://example.com');


  // Verify the header text

  const headerText = await homePage.getHeaderText();

  expect(headerText).toBe('Example Domain');


  // Click the login button

  await homePage.clickLoginButton();


  // Log in with credentials

  await loginPage.login('testuser', 'password123');


  // Verify successful login (example)

  const isLoggedIn = await loginPage.isVisible('text=Welcome, testuser');

  expect(isLoggedIn).toBeTruthy();

});


## **5. Run the Test**

Run the test using the Playwright CLI:

    npx playwright test


## **6. Advantages of Dynamic POM**

- **Reusability**: Common methods are defined in the `BasePage` class, reducing code duplication.

- **Maintainability**: Changes to selectors or methods are isolated to specific page objects.

- **Scalability**: Easily add new page objects for additional pages or components.


## **7. Tips for Dynamic POM**

- Use **TypeScript interfaces** or types to define reusable data structures.

- Use **Playwright locators** for better performance and readability:

  ```typescript

  async click(locator: Locator): Promise<void> {

    await locator.click();

  }


ADDITIONAL NOTE: Use Playwright's **test fixtures** to initialize page objects dynamically.


By following this structure, you can create a robust and scalable test automation framework using Playwright and TypeScript. 🎉

No comments:

Post a Comment