Migration v1 → v2
Breaking changes from v1 to v2 and how to update existing code.
v2 removes deprecated APIs and tightens contracts. There is no compatibility shim — update call sites.
Breaking changes
| v1 | v2 |
|---|---|
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 |
Removed APIs
ListStrictSelector
Selector(id) already produces the same locator chain. Replace mechanically:
- @ListStrictSelector("CartItem")
+ @Selector("CartItem")
accessor item!: Locator;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");Renamed APIs
filterByTestId → filterByItemTestId
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.
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+/);Widened signatures
waitText and waitValue accept RegExp
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
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, ...New strictness
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
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()); // okUpdate strategy
- Run a grep pass for the removed and renamed APIs:
ListStrictSelector | waitNoValue | waitProp | waitPropAbsence filterByTestId | getItemByIdMask - Apply the table above — each row is a one-line change.
- Run TypeScript. The compiler catches most of the rest.
- Run your test suite. Behavior of equivalent calls is unchanged.
See also
- Lists —
filterByItemTestIdvsfilterByHasTestId. - Custom Controls — factory-argument form.
- Built-In POM — initializer form for
PageObject.
Last updated on