Root Decorators Reference
Relevant source files
The following files were used as context for generating this wiki page:
Root decorators serve as the foundation of the playwright-page-object framework by establishing the initial locator context for a class. They bind a class instance to a specific top-level Playwright Locator, allowing all subsequent child decorators within that class to resolve selectors relative to this root. This mechanism replaces deep page.locator() chains with a typed, hierarchical control graph.
These decorators are designed to be flexible, supporting both the built-in RootPageObject base class and plain TypeScript classes. In all cases, the decorated class must accept a Playwright Page object as its first constructor argument to facilitate the resolution of the root element.
Sources: README.md:12-25, src/decorators/rootSelectors.ts:47-75, skills/playwright-page-object/SKILL.md:16-25
Architecture and Core Logic
Root decorators function as class decorators that override the class constructor. When a decorated class is instantiated, the decorator intercepts the first constructor argument (the Page), resolves a locator based on the provided selector criteria, and attaches this context to the instance.
Execution Flow
The following diagram illustrates how a root decorator establishes context during instantiation:
This flowchart describes the logic within the RootSelectorBy factory used by all root decorators.
Sources: src/decorators/rootSelectors.ts:47-75, src/tests/decorators/rootSelectors.spec.ts:57-75
Implementation Requirements
- Constructor Signature: For plain classes, the first argument must be
page: Page. The decorator validates this at runtime and will throw an explicit error if aLocatorornullis passed instead. - Base Class Restrictions: Never use
@RootSelectoron a class that extendsPageObjectdirectly;PageObjectis reserved for nested controls. Root classes should either be plain classes or extendRootPageObject. - Lazy Resolution: While the root context is established at construction, the actual Playwright locators are resolved lazily when an accessor is accessed.
Sources: src/decorators/rootSelectors.ts:28-45, skills/playwright-page-object/SKILL.md:43-52, README.md:89-95
Available Root Decorators
The library provides a suite of decorators mapping to standard Playwright getBy* locator strategies.
| Decorator | Playwright Mapping | Use Case |
|---|---|---|
@RootSelector(id?) | page.getByTestId(id) | Default choice using data-testid. If id is omitted, root is body. |
@ListRootSelector(id) | page.getByTestId(new RegExp(id)) | For list containers with dynamic/pattern-based test IDs. |
@RootSelectorByRole(role, options?) | page.getByRole(role, options) | Selecting by ARIA roles (e.g., 'button', 'heading'). |
@RootSelectorByText(text) | page.getByText(text) | Selecting by visible text content. |
@RootSelectorByLabel(label) | page.getByLabel(label) | Selecting form elements by their associated labels. |
@RootSelectorByPlaceholder(text) | page.getByPlaceholder(text) | Selecting inputs by placeholder text. |
@RootSelectorByAltText(text) | page.getByAltText(text) | Selecting images or area elements by alt text. |
@RootSelectorByTitle(text) | page.getByTitle(text) | Selecting elements by their title attribute. |
Sources: src/decorators/rootSelectors.ts:101-183, README.md:204-216
Integration Patterns
Built-in POM vs. Plain Classes
Developers can choose between extending the framework's base classes or using decorators on existing classes to maintain a "clean" inheritance tree.
The diagram shows how RootSelector can be applied regardless of the inheritance model.
Sources: skills/playwright-page-object/SKILL.md:55-80, example/e2e/page-objects/ExternalCheckoutPage.ts:16-24
Plain Class Usage
Used when avoiding library inheritance is preferred. The decorator handles the internal wiring of the locator context.
@RootSelector("CheckoutPage")
class CheckoutPage {
constructor(readonly page: Page) {}
@Selector("PromoCodeInput")
accessor PromoCodeInput!: Locator;
}
Sources: README.md:113-125, src/decorators/rootSelectors.ts:77-90
RootPageObject Usage
Extending RootPageObject provides access to built-in convenience methods for assertions and waiting.
@RootSelector("CheckoutPage")
class CheckoutPage extends RootPageObject {
@Selector("PromoCodeInput")
accessor PromoCode = new PageObject();
}
Sources: README.md:144-165, src/page-objects/RootPageObject.ts
Error Handling and Validation
The root decorator system includes strict runtime checks to prevent common misconfigurations:
- Non-Class Targets: Decorators will throw an error if applied to methods or fields instead of the class itself.
- Missing Page Argument: If the first argument to the constructor is missing or invalid, an error is thrown:
[RootSelector] class_name must receive Playwright Page as the first constructor argument. - Inheritance Conflict: Attempting to use a root decorator on a
PageObject(nested control) results in a specific error advising the use ofRootPageObject.
Sources: src/decorators/rootSelectors.ts:55-73, src/tests/decorators/rootSelectors.spec.ts:45-55
Summary
Root decorators are the entry point for defining typed page objects. By establishing a root locator context at the class level, they enable a composable architecture where child components can be defined relative to their parents. This approach supports incremental adoption, allowing teams to mix plain Playwright classes, external controls, and the built-in PageObject layer within a single test suite.
Sources: README.md:31-45, skills/playwright-page-object/SKILL.md:120-135