Page Object Models (POMs)
Purpose: Encapsulate page interactions and selectors for maintainable E2E tests.
Available POMs
HomePage.ts
Encapsulates the landing page with SimonSelector component.
Methods:
goto()- Navigate to homepagegotoWithParams(params)- Navigate with query parametersclickQuadrant(userType)- Click a specific quadranthoverQuadrant(userType)- Hover over a quadrantclickCenterNTimes(count)- Click center for investor easter egggetCenterText()- Get center button textwaitForUserTypePanel()- Wait for panel to openassertSimonContainerVisible()- Assert container is visibleassertAllQuadrantsVisible()- Assert all quadrants are visible
UserTypePanelPage.ts
Encapsulates the UserTypePanel side modal.
Methods:
waitForOpen()- Wait for panel to openwaitForClose()- Wait for panel to closeclickContinue()- Click "Continue to Register" buttonclickClose()- Click close (X) buttonclickOverlay()- Click overlay to closepressEscape()- Press Escape key to closeassertVisible()- Assert panel is visibleassertUserTypeContent(userType, expectedText)- Assert panel shows correct user typecontinueToRegistration()- Helper: wait for open → click continue
RegistrationPage.ts
Encapsulates the RegistrationForm component.
Methods:
waitForFormLoad()- Wait for form to be visiblefillEmail(email)- Fill email fieldfillPassword(password)- Fill password fieldfillConfirmPassword(password)- Fill confirm password fieldcheckTerms()- Check terms & conditionsclickSubmit()- Submit the formclickBack()- Go back to SimonSelectorfillForm(data)- Fill all form fields at oncesubmitForm(data)- Fill and submit formwaitForSuccessToast()- Wait for success messagewaitForErrorToast(errorText)- Wait for error messageassertFormVisible()- Assert form is visibleassertSubmitButtonDisabled()- Assert submit button is disabled
Usage Examples
Example 1: Basic User Flow
import { test, expect } from '@playwright/test'
import { HomePage, UserTypePanelPage, RegistrationPage } from '../pages'
test.describe('User Registration Flow', () => {
test('should complete client registration flow', async ({ page }) => {
// Initialize POMs
const homePage = new HomePage(page)
const userTypePanel = new UserTypePanelPage(page)
const registrationPage = new RegistrationPage(page)
// Navigate to homepage
await homePage.goto()
await homePage.assertSimonContainerVisible()
// Click client quadrant
await homePage.clickQuadrant('client')
// Verify panel opens
await userTypePanel.waitForOpen()
await userTypePanel.assertUserTypeContent('client', 'Client')
// Continue to registration
await userTypePanel.clickContinue()
// Fill and submit form
await registrationPage.waitForFormLoad()
await registrationPage.submitForm({
email: 'client@test.com',
password: 'SecurePassword123',
})
// Verify success
await registrationPage.waitForSuccessToast()
})
})
Example 2: Using Assertion Chaining
test('should display all homepage elements', async ({ page }) => {
const homePage = new HomePage(page)
await homePage
.goto()
.then(() => homePage.assertSimonContainerVisible())
.then(() => homePage.assertAllQuadrantsVisible())
.then(() => homePage.assertCenterButtonVisible())
})
Example 3: Investor Easter Egg
test('should unlock investor registration after 5 center clicks', async ({ page }) => {
const homePage = new HomePage(page)
const userTypePanel = new UserTypePanelPage(page)
await homePage.goto()
// Click center 5 times
await homePage.clickCenterNTimes(5)
// Verify investor panel opens
await userTypePanel.waitForOpen()
await userTypePanel.assertUserTypeContent('investor', 'Investor')
})
Example 4: Form Validation
test('should show error for mismatched passwords', async ({ page }) => {
const homePage = new HomePage(page)
const userTypePanel = new UserTypePanelPage(page)
const registrationPage = new RegistrationPage(page)
// Navigate to registration form
await homePage.goto()
await homePage.clickQuadrant('fan')
await userTypePanel.continueToRegistration()
// Submit with mismatched passwords
await registrationPage.submitForm({
email: 'fan@test.com',
password: 'Password123',
confirmPassword: 'DifferentPassword456',
})
// Verify error appears
await registrationPage.waitForErrorToast('passwords do not match')
})
Example 5: Query Parameter Auto-Selection
test('should auto-select user type from query param', async ({ page }) => {
const homePage = new HomePage(page)
const userTypePanel = new UserTypePanelPage(page)
// Navigate with query parameter
await homePage.gotoWithParams({ register: 'creator' })
// Verify creator panel opens automatically
await userTypePanel.waitForOpen()
await userTypePanel.assertUserTypeContent('creator', 'Creator')
})
Example 6: Back Navigation
test('should navigate back from registration to selector', async ({ page }) => {
const homePage = new HomePage(page)
const userTypePanel = new UserTypePanelPage(page)
const registrationPage = new RegistrationPage(page)
// Go to registration
await homePage.goto()
await homePage.clickQuadrant('provider')
await userTypePanel.continueToRegistration()
// Verify on registration page
await registrationPage.assertFormVisible()
// Click back
await registrationPage.clickBack()
// Verify back on homepage
await homePage.assertSimonContainerVisible()
})
Best Practices
1. Initialize POMs at Test Start
test('example test', async ({ page }) => {
// Initialize all POMs you'll need
const homePage = new HomePage(page)
const userTypePanel = new UserTypePanelPage(page)
const registrationPage = new RegistrationPage(page)
// Use POMs throughout test
})
2. Use Helper Methods for Common Flows
// Good - uses helper
await userTypePanel.continueToRegistration()
// Less good - manual steps
await userTypePanel.waitForOpen()
await userTypePanel.clickContinue()
3. Chain Assertions for Readability
await registrationPage
.assertFormVisible()
.then(() => registrationPage.assertEmailInputReady())
.then(() => registrationPage.assertSubmitButtonDisabled())
4. Use Descriptive Test Names
// Good
test('should display error when password is less than 8 characters', ...)
// Bad
test('password validation', ...)
5. Keep Tests Isolated
test.beforeEach(async ({ page }) => {
// Reset state before each test
const homePage = new HomePage(page)
await homePage.goto()
})
Maintenance
When Adding New Components
- Create new POM file in
pages/directory - Export from
index.ts - Update this README with examples
- Follow existing patterns (constructor with Page, locators, methods, assertions)
When Updating Selectors
Only update the POM file's locator definitions - tests remain unchanged.
// Update this in POM
this.clientQuadrant = page.locator('[data-testid="client-quadrant"]')
// Tests using clickQuadrant('client') work without changes
References
- Playwright POM Guide: https://playwright.dev/docs/pom
- E2E Testing Strategy:
../E2E_TESTING_STRATEGY.md - Test Examples:
../tests/directory