YouTip LogoYouTip

Playwright Pom

\\\\n\\\\n\\\\n\\\\nPlaywright Page Object Model (POM) | Online Tutorial\\\\n\\\\n\\\\n\\\\n

Playwright Page Object Model (POM) | Online Tutorial

\\\\n\\\\n

This chapter introduces the design concept and implementation of the Page Object Model (POM), helping you organize large test suites.

\\\\n\\\\n
\\\\n\\\\n

What is POM

\\\\n\\\\n

The Page Object Model is a test design pattern that encapsulates page elements and operations into independent classes.

\\\\n\\\\n

Each page object represents a page or component in a web application, containing locators and operation methods for that page.

\\\\n\\\\n
\\\\n\\\\n

Advantages of POM

\\\\n\\\\n\\\\n\\\\n\\\\n\\\\n\\\\n\\\\n\\\\n\\\\n\\\\n\\\\n\\\\n\\\\n\\\\n\\\\n\\\\n\\\\n\\\\n\\\\n\\\\n\\\\n\\\\n\\\\n\\\\n\\\\n\\\\n
AdvantageDescription
Simplify test writingTest code uses high-level APIs (like loginPage.login('user', 'pass')) instead of raw Locator operations
Reduce maintenance costsChanges to page structure only need to modify the Page Object class, not every test
Improve readabilityTest code reads more like business language, understandable even by non-technical staff
Avoid duplicationSame locators and operations only need to be defined once
\\\\n\\\\n
\\\\n\\\\n

Creating a Page Object Class

\\\\n\\\\n

Below is an example using the TUTORIAL website to create a Login Page Object.

\\\\n\\\\n

Example

\\\\n\\\\n
// File path: pages/LoginPage.ts\\\\n\\\\nimport{ type Page, type Locator, expect } from '@playwright/test';\\\\n\\\\nexport class LoginPage {\\\\n\\\\n readonly page: Page;\\\\n\\\\n// Declare all locators as read-only properties\\\\n\\\\n readonly usernameInput: Locator;\\\\n\\\\n readonly passwordInput: Locator;\\\\n\\\\n readonly loginButton: Locator;\\\\n\\\\n readonly errorMessage: Locator;\\\\n\\\\nconstructor(page: Page){\\\\n\\\\nthis.page= page;\\\\n\\\\n// Initialize all locators in the constructor\\\\n\\\\nthis.usernameInput= page.getByLabel('Username');\\\\n\\\\nthis.passwordInput= page.getByLabel('Password');\\\\n\\\\nthis.loginButton= page.getByRole('button',{ name:'Login'});\\\\n\\\\nthis.errorMessage= page.getByTestId('login-error');\\\\n\\\\n}\\\\n\\\\n// Navigate to LoginPage\\\\n\\\\n async goto(){\\\\n\\\\n await this.page.goto('/login');\\\\n\\\\n}\\\\n\\\\n// Perform Login operation\\\\n\\\\n async login(username: string, password: string){\\\\n\\\\n await this.usernameInput.fill(username);\\\\n\\\\n await this.passwordInput.fill(password);\\\\n\\\\n await this.loginButton.click();\\\\n\\\\n}\\\\n\\\\n// Verify the error message for Login failure\\\\n\\\\n async expectLoginError(message: string){\\\\n\\\\n await expect(this.errorMessage).toBeVisible();\\\\n\\\\n await expect(this.errorMessage).toContainText(message);\\\\n\\\\n}\\\\n\\\\n}\\\\n
\\\\n\\\\n
\\\\n\\\\n

Using Page Object in Tests

\\\\n\\\\n

Example

\\\\n\\\\n
// File path: tests/login.spec.ts\\\\n\\\\nimport{ test, expect } from '@playwright/test';\\\\n\\\\nimport{ LoginPage } from '../pages/LoginPage';\\\\n\\\\ntest.describe('LoginFunction',()=>{\\\\n\\\\n test('Correct Username Password Login successful', async ({ page })=>{\\\\n\\\\nconst loginPage =new LoginPage(page);\\\\n\\\\n// Use the method of the Page Object\\\\n\\\\n await loginPage.goto();\\\\n\\\\n await loginPage.login('tutorial_user','correct_password');\\\\n\\\\n// LoginVerify redirection after success\\\\n\\\\n await expect(page).toHaveURL('/dashboard');\\\\n\\\\n});\\\\n\\\\ntest('Incorrect Password Login failed', async ({ page })=>{\\\\n\\\\nconst loginPage =new LoginPage(page);\\\\n\\\\nawait loginPage.goto();\\\\n\\\\n await loginPage.login('tutorial_user','wrong_password');\\\\n\\\\n// Use the assertion method of the Page Object\\\\n\\\\n await loginPage.expectLoginError('UsernameorPasswordError');\\\\n\\\\n});\\\\n\\\\n});\\\\n
\\\\n\\\\n
\\\\n\\\\n

Multi-layer Page Objects

\\\\n\\\\n

In real projects, you can create multiple Page Objects that can reference each other.

\\\\n\\\\n

Example

\\\\n\\\\n
// File path: pages/DashboardPage.ts\\\\n\\\\nimport{ type Page, type Locator, expect } from '@playwright/test';\\\\n\\\\nexport class DashboardPage {\\\\n\\\\n readonly page: Page;\\\\n\\\\n readonly welcomeText: Locator;\\\\n\\\\n readonly logoutButton: Locator;\\\\n\\\\nconstructor(page: Page){\\\\n\\\\nthis.page= page;\\\\n\\\\nthis.welcomeText= page.getByText('Welcome back');\\\\n\\\\nthis.logoutButton= page.getByRole('button',{ name:'Exit'});\\\\n\\\\n}\\\\n\\\\nasync expectWelcomeMessage(username: string){\\\\n\\\\n await expect(this.welcomeText).toContainText(username);\\\\n\\\\n}\\\\n\\\\nasync logout(){\\\\n\\\\n await this.logoutButton.click();\\\\n\\\\n}\\\\n\\\\n}\\\\n
\\\\n\\\\n

Combining Page Objects:

\\\\n\\\\n

Example

\\\\n\\\\n
test('Complete LoginLogout flow', async ({ page })=>{\\\\n\\\\nconst loginPage =new LoginPage(page);\\\\n\\\\nconst dashboardPage =new DashboardPage(page);\\\\n\\\\n// Login\\\\n\\\\n await loginPage.goto();\\\\n\\\\n await loginPage.login('tutorial_user','password');\\\\n\\\\n// Verify dashboard\\\\n\\\\n await dashboardPage.expectWelcomeMessage('tutorial_user');\\\\n\\\\n// Logout\\\\n\\\\n await dashboardPage.logout();\\\\n\\\\n await expect(page).toHaveURL('/login');\\\\n\\\\n});\\\\n
\\\\n\\\\n
\\\\n\\\\n

POM Best Practices

\\\\n\\\\n\\\\n\\\\n\\\\n\\\\n\\\\n\\\\n\\\\n\\\\n\\\\n\\\\n\\\\n\\\\n\\\\n\\\\n\\\\n\\\\n\\\\n\\\\n\\\\n\\\\n\\\\n\\\\n\\\\n\\\\n\\\\n\\\\n\\\\n\\\\n\\\\n
PracticeDescription
Locators in constructorDefine all locators in the constructor, don't create them dynamically in methods
Methods return Page ObjectOperation methods can return new Page Objects (e.g., return DashboardPage after login)
Encapsulate assertions but don't overuseEncapsulate commonly used assertion combinations as methods, write simple assertions directly in tests
Use composition over inheritanceAvoid deep inheritance, use composition pattern to associate multiple Page Objects
One object per page/componentAppropriate granularity, don't create separate classes for every small element
\\\\n\\\\n
\\\\n

POM is not mandatory.

\\\\n

For small projects with only a few tests, writing Locators directly in tests may be clearer. When you have more than 5 test files, consider introducing POM.

\\\\n
\\\\n\\\\n \\\\n
← Playwright Parallel ShardingPlaywright Javascript β†’