Skip to content

Page-Only Hosts

A page-only host is a class without @RootSelector(...) that still has a page property. Child decorators chain from page.locator("body") — the same default scope as @RootSelector() with no argument.

import type { Locator, Page } from "@playwright/test";
import { Selector } from "playwright-page-object";
class CheckoutPage {
constructor(readonly page: Page) {}
@Selector("PromoCodeInput")
accessor PromoCodeInput!: Locator;
}

Resolves to:

page.locator("body").getByTestId("PromoCodeInput")
  • Test ids in your application are globally unique.
  • Wrapping every page in a container data-testid is more noise than signal.
  • The page logically is the root.

Without a container scope, selectors are not protected against collisions elsewhere on the page. If two pages reuse the same data-testid, switch to @RootSelector(...).

These three are functionally equivalent in scope:

// 1. Page-only host (no decorator)
class A {
constructor(readonly page: Page) {}
}
// 2. @RootSelector() with no argument
@RootSelector()
class B {
constructor(readonly page: Page) {}
}
// 3. @RootSelector() with body
@RootSelector()
class C {
constructor(readonly page: Page) {}
}

All three scope children to page.locator("body"). Pick the page-only form when you don’t need the decorator for any other purpose; pick @RootSelector() if the class extends RootPageObject (the decorator is what wires the root locator into the base class).