Custom Selector Logic (@SelectorBy)
Relevant source files
The following files were used as context for generating this wiki page:
The @SelectorBy decorator provides a mechanism for implementing custom element resolution logic within the playwright-page-object framework. Unlike standard decorators that map directly to Playwright's built-in getBy* methods, @SelectorBy allows developers to define a custom function to derive a locator or value based on the current root context.
This feature is essential for handling complex UI patterns where standard test IDs or roles are insufficient. It integrates with the framework's lazy resolution model, ensuring that custom selectors are only evaluated when the decorated accessor is accessed during test execution. Sources: src/decorators/selectorBy.ts, README.md:231-235
Architecture and Core Logic
The @SelectorBy decorator is designed to work exclusively with ECMAScript accessor keywords. It wraps the getter of the decorated property, intercepting the access to inject custom logic that utilizes the parent object's locator context. Sources: src/decorators/selectorBy.ts:8-12, src/tests/decorators/selectorBy.spec.ts:63-83
The Selector Function
The core of this feature is the selector function passed to the decorator. This function receives two arguments:
- Root: The resolved
Locatorof the parent component or page. - Value: The initial value assigned to the accessor.
This allows the custom logic to either transform the existing value using the root or return a completely new locator. Sources: src/decorators/selectorBy.ts:16-17, src/tests/decorators/selectorBy.spec.ts:24-42
Data Flow for Custom Resolution
The following diagram illustrates how the framework resolves a property decorated with @SelectorBy when it is accessed in a test.
The diagram shows the sequence from the initial property access to the execution of the custom logic. Sources: src/decorators/selectorBy.ts:15-25, src/tests/decorators/selectorBy.spec.ts:46-61
Implementation Details
Decorator Constraints
The @SelectorBy decorator enforces strict usage rules:
- Target Type: It can only be applied to
accessorfields. Using it on regular fields or methods results in a runtime error. - Context Requirement: The class containing the accessor must have a valid root context, typically established via
@RootSelectoror by extendingPageObject. Sources: src/decorators/selectorBy.ts:8-12, src/tests/decorators/selectorBy.spec.ts:63-83
Integration with PageObject
When used within a class extending PageObject, @SelectorBy leverages the internal locator property of the instance to provide the root argument to the custom function. Sources: src/page-objects/PageObject.ts:61-75, src/tests/decorators/selectorBy.spec.ts:46-61
| Component | Responsibility |
|---|---|
selector function | User-defined logic to find an element or transform a value. |
accessor | Required keyword for the property to enable decorator interception. |
root | The parent's resolved Playwright Locator. |
value | The original value of the accessor, passed to the selector function. |
Sources: src/decorators/selectorBy.ts:1-27, src/page-objects/PageObject.ts:10-14
Usage Examples
Custom Locator Transformation
Developers can use @SelectorBy to implement logic that Playwright's standard selectors do not cover directly, such as complex relative positioning or attribute filtering.
const customSelector = (root: Locator, value: string) => {
return root.locator(`[data-custom="${value}"]`);
};
class MyComponent extends PageObject {
@SelectorBy(customSelector)
accessor myElement = "initial-id";
}
Sources: src/tests/decorators/selectorBy.spec.ts:24-42, README.md:231-235
Interaction with PageObject Instances
If the initial value of the accessor is a PageObject or an external control, @SelectorBy can return a cloned or modified version of that instance. Sources: src/tests/decorators/selectorBy.spec.ts:102-129
Technical Summary
The @SelectorBy feature provides the ultimate escape hatch for selector logic. By providing access to the raw Playwright Locator of the parent, it enables sophisticated element resolution while maintaining the library's benefits of lazy evaluation and typed accessors. Sources: src/index.ts:13, src/decorators/selectorBy.ts:1-27