Jump to content

Connect SuperML | Leeroopedia MCP: Equip your AI agents with best practices, code verification, and debugging knowledge. Powered by Leeroo — building Organizational Superintelligence. Contact us at founders@leeroo.com.

Implementation:Webdriverio Webdriverio Page Object Class Pattern

From Leeroopedia

Metadata

Field Value
Page ID Page_Object_Class_Pattern
Wiki Webdriverio_Webdriverio
Type Implementation (Pattern Doc)
Domains Testing, Design_Patterns
Knowledge Sources Repo (https://github.com/webdriverio/webdriverio), Doc (https://webdriver.io/docs/pageobjects)
Related Principles Principle: Page_Object_Model

Overview

Interface specification for implementing the Page Object Model pattern in WebdriverIO test projects. The pattern consists of a base Page class with an open(path) method, and subclasses that define element getters using $('selector') and action methods. Page objects are exported as singletons. Tests import these singletons and call their methods.

Description

The Page Object Class Pattern in WebdriverIO is implemented through a class inheritance hierarchy:

  • A base Page class defines the open(path) method that delegates to browser.url(path).
  • Page-specific subclasses extend the base class and define element getters using $('selector') (without await) and action methods that compose element interactions.
  • Each page module exports a singleton instance (export default new PageClass()).
  • Test specs import these singletons and invoke their methods using await.

The getter pattern leverages WebdriverIO's ChainablePromiseElement -- calling $('#username') returns a thenable wrapper that resolves the element only when an action is performed. This means element lookups are always fresh and never stale.

Source

File Lines Description
examples/pageobject/pageobjects/page.js L1-5 Base Page class with open(path) method
examples/pageobject/pageobjects/form.page.js L1-24 FormPage subclass with element getters and action methods
examples/pageobject/specs/form.spec.js L1-26 Test spec using FormPage singleton

Pattern Interface

Base Page Class

// examples/pageobject/pageobjects/page.js
export default class Page {
    open (path) {
        return browser.url(path)
    }
}

Interface contract:

  • Input: path (string) -- the URL path segment to navigate to.
  • Output: Returns the result of browser.url(path), a Promise that resolves when navigation completes.
  • Behavior: Navigates the browser to the given path. Subclasses call super.open('specific-path') to navigate to their page.

Page Subclass

// examples/pageobject/pageobjects/form.page.js
import Page from './page.js'

class FormPage extends Page {
    /**
     * define elements
     */
    get username () { return $('#username') }
    get password () { return $('#password') }
    get submitButton () { return $('#login button[type=submit]') }
    get flash () { return $('#flash') }

    /**
     * define or overwrite page methods
     */
    open () {
        return super.open('login')
    }

    submit () {
        return this.submitButton.click()
    }
}

export default new FormPage()

Interface contract:

  • Element getters: Each getter returns a ChainablePromiseElement via $('selector'). No await is used in the getter body.
    • Input: None (getter property).
    • Output: ChainablePromiseElement -- a thenable that resolves to a WebdriverIO Element when acted upon.
  • Action methods: Compose element interactions into domain-specific actions (e.g., submit()).
    • Input: Method-specific parameters (none for submit()).
    • Output: Promise from the underlying WebdriverIO command.
  • Singleton export: export default new FormPage() provides a shared instance.

Test Usage

// examples/pageobject/specs/form.spec.js
import FormPage from '../pageobjects/form.page.js'

describe('auth form', () => {
    it('should deny access with wrong creds', async () => {
        await FormPage.open()
        await FormPage.username.addValue('foo')
        await FormPage.password.addValue('bar')
        await FormPage.submit()

        await expect(FormPage.flash).toHaveText(
            expect.stringContaining('Your username is invalid!')
        )
    })

    it('should allow access with correct creds', async () => {
        await FormPage.open()
        await FormPage.username.addValue('tomsmith')
        await FormPage.password.addValue('SuperSecretPassword!')
        await FormPage.submit()

        await FormPage.flash.waitForDisplayed()
        await expect(FormPage.flash).toHaveText(
            expect.stringContaining('You logged into a secure area!')
        )
    })
})

Test contract:

  • Input: Page object method calls with test data.
  • Output: Assertions on element state using expect-webdriverio matchers.
  • Pattern: Open page, interact with elements, assert on outcomes.

Directory Convention

Directory Purpose
pageobjects/ Page object classes (base + page-specific)
specs/ Test specification files
wdio.conf.js WebdriverIO configuration at project root

I/O Contract Summary

Component Input Output
Page.open(path) URL path string Promise (navigation complete)
Element getter (get username()) None ChainablePromiseElement
Action method (submit()) Method-specific params Promise (action complete)
Singleton export None Page object instance

Usage Examples

Creating a new page object

import Page from './page.js'

class DashboardPage extends Page {
    get welcomeMessage () { return $('[data-testid="welcome"]') }
    get logoutButton () { return $('button.logout') }
    get navItems () { return $$('.nav-item') }

    open () {
        return super.open('dashboard')
    }

    logout () {
        return this.logoutButton.click()
    }
}

export default new DashboardPage()

Using multiple page objects in a test

import LoginPage from '../pageobjects/login.page.js'
import DashboardPage from '../pageobjects/dashboard.page.js'

describe('user flow', () => {
    it('should login and see dashboard', async () => {
        await LoginPage.open()
        await LoginPage.username.addValue('admin')
        await LoginPage.password.addValue('secret')
        await LoginPage.submit()

        await expect(DashboardPage.welcomeMessage).toBeDisplayed()
        await expect(DashboardPage.welcomeMessage).toHaveTextContaining('Welcome')
    })
})

Related Pages

Page Connections

Double-click a node to navigate. Hold to expand connections.
Principle
Implementation
Heuristic
Environment