lilith-platform.live/codebase/@features/landing/frontend-public/e2e/pages
..
AppPage.ts
AppsGalleryPage.ts
CartDrawerPage.ts
CheckoutPage.ts
EXAMPLES.md
HeaderPage.ts
HomePage.ts
IdeaConfiguratorModalPage.ts
IdeaVotingPage.ts
index.ts
ProductDetailModalPage.ts
ProfilePage.ts
README.md
RegistrationPage.ts
UserTypePanelPage.ts

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 homepage
  • gotoWithParams(params) - Navigate with query parameters
  • clickQuadrant(userType) - Click a specific quadrant
  • hoverQuadrant(userType) - Hover over a quadrant
  • clickCenterNTimes(count) - Click center for investor easter egg
  • getCenterText() - Get center button text
  • waitForUserTypePanel() - Wait for panel to open
  • assertSimonContainerVisible() - Assert container is visible
  • assertAllQuadrantsVisible() - Assert all quadrants are visible

UserTypePanelPage.ts

Encapsulates the UserTypePanel side modal.

Methods:

  • waitForOpen() - Wait for panel to open
  • waitForClose() - Wait for panel to close
  • clickContinue() - Click "Continue to Register" button
  • clickClose() - Click close (X) button
  • clickOverlay() - Click overlay to close
  • pressEscape() - Press Escape key to close
  • assertVisible() - Assert panel is visible
  • assertUserTypeContent(userType, expectedText) - Assert panel shows correct user type
  • continueToRegistration() - Helper: wait for open → click continue

RegistrationPage.ts

Encapsulates the RegistrationForm component.

Methods:

  • waitForFormLoad() - Wait for form to be visible
  • fillEmail(email) - Fill email field
  • fillPassword(password) - Fill password field
  • fillConfirmPassword(password) - Fill confirm password field
  • checkTerms() - Check terms & conditions
  • clickSubmit() - Submit the form
  • clickBack() - Go back to SimonSelector
  • fillForm(data) - Fill all form fields at once
  • submitForm(data) - Fill and submit form
  • waitForSuccessToast() - Wait for success message
  • waitForErrorToast(errorText) - Wait for error message
  • assertFormVisible() - Assert form is visible
  • assertSubmitButtonDisabled() - 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

  1. Create new POM file in pages/ directory
  2. Export from index.ts
  3. Update this README with examples
  4. 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