Skip to content

Migration v1 → v2

v2 removes deprecated APIs and tightens contracts. There is no compatibility shim — update call sites.

v1v2
ListStrictSelector(id)Selector(id) — identical locator
PageObject.waitProp(name, value)control.expect().toHaveAttribute("data-prop-{name}", value)
PageObject.waitPropAbsence(name, value)control.expect().not.toHaveAttribute("data-prop-{name}", value)
PageObject.waitNoValue()control.expect().not.toHaveAttribute("data-prop-$value")
list.filterByTestId(id)list.filterByItemTestId(id)
list.getItemByIdMask(mask)list.getItemByTestId(new RegExp(mask))
createFixtures({ x: MyClass })unchanged — factory functions now also accepted

Selector(id) already produces the same locator chain. Replace mechanically:

@ListStrictSelector("CartItem")
@Selector("CartItem")
accessor item!: Locator;

PageObject.waitProp / waitPropAbsence / waitNoValue

Section titled “PageObject.waitProp / waitPropAbsence / waitNoValue”

All three assumed an internal data-prop-* attribute convention — your framework was expected to mirror component props to data-prop-{name} attributes for testing. Both waitProp and waitNoValue used that same convention internally; waitNoValue specifically checked for the absence of data-prop-$value.

Express the same intent directly with Playwright’s .expect():

await checkout.PromoCode.waitProp("disabled", "true");
await checkout.PromoCode.expect().toHaveAttribute("data-prop-disabled", "true");
await checkout.PromoCode.waitPropAbsence("disabled", "true");
await checkout.PromoCode.expect().not.toHaveAttribute("data-prop-disabled", "true");
await checkout.PromoCode.waitNoValue();
await checkout.PromoCode.expect().not.toHaveAttribute("data-prop-$value");

If your project does not use the data-prop-* convention and you originally wanted to assert that a form control’s value is empty, use Playwright’s toHaveValue instead — but note this is a different assertion (DOM .value vs. an attribute):

await checkout.PromoCode.expect().toHaveValue("");

PageObject.expect() returns Playwright’s assertion API bound to the underlying locator. If you prefer working with the raw locator directly, import Playwright’s expect and pass control.$:

import { expect } from "@playwright/test";
await expect(checkout.PromoCode.$).toHaveAttribute("data-prop-disabled", "true");

The rename clarifies the self-vs-descendant distinction. The behavior is unchanged.

const item = list.filterByTestId("CartItem_2");
const item = list.filterByItemTestId("CartItem_2");

The companion filterByHasTestId(id) matches rows that contain a descendant with data-testid={id}. See Lists for the full distinction.

getItemByIdMaskgetItemByTestId(new RegExp(...))

Section titled “getItemByIdMask → getItemByTestId(new RegExp(...))”

The mask was always a regex pattern. The new form is explicit:

const item = list.getItemByIdMask("CartItem_\\d+");
const item = list.getItemByTestId(/CartItem_\d+/);
await checkout.PromoCode.waitText(/applied/i);
await checkout.PromoCode.waitValue(/^\d{6}$/);
await checkout.PromoCode.waitValue(42); // number → coerced to "42"

@ListSelector and @ListRootSelector accept string | RegExp

Section titled “@ListSelector and @ListRootSelector accept string | RegExp”

Strings are still used as regex patterns (escape metacharacters if needed). Pass a RegExp for full control:

@ListSelector("CartItem_") // matches /CartItem_/
@ListSelector(/^CartItem_\d+$/) // matches CartItem_1, CartItem_2, ...

PageObject subclasses cannot be passed as factory arguments

Section titled “PageObject subclasses cannot be passed as factory arguments”

In v1, @Selector("X", MyPageObjectSubclass) silently produced broken instances. In v2, it throws at decoration time:

@Selector("PromoCode", PromoPageObject)
accessor PromoCode!: PromoPageObject;
@Selector("PromoCode")
accessor PromoCode = new PromoPageObject();

Use the initializer form for PageObject subclasses. The factory-argument form is reserved for plain classes whose constructor accepts a Locator (see Custom Controls).

ListPageObject constructor validates itemType

Section titled “ListPageObject constructor validates itemType”

Invalid item types throw at construction time instead of failing silently on access:

new ListPageObject("not a class"); // throws
new ListPageObject(42); // throws
new ListPageObject(CartItemControl); // ok
new ListPageObject(new CartItemControl()); // ok
  1. Run a grep pass for the removed and renamed APIs:
    ListStrictSelector | waitNoValue | waitProp | waitPropAbsence
    filterByTestId | getItemByIdMask
  2. Apply the table above — each row is a one-line change.
  3. Run TypeScript. The compiler catches most of the rest.
  4. Run your test suite. Behavior of equivalent calls is unchanged.