🎭 Playwright Expectly | By Cerios

Comprehensive Playwright test matchers for strings, numbers, dates, arrays, objects, and locators. Simplify your E2E tests with intuitive assertions like toBeAlphanumeric, toHaveAscendingOrder, toBeInTheFuture, and 50+ more matchers.
Features
- 🎯 50+ Custom Matchers — extensive validation for all data types
- 🔤 String Validation — email, URL, UUID, alphanumeric, and more
- 🔢 Number Arrays — sorting, statistics, ranges, and patterns
- 📅 Date Operations — comparisons, ranges, quarters, and business logic
- 🎨 Locator Assertions — DOM element validation and text formatting
- 📦 Object Arrays — sorting, uniqueness, and property validation
- 🤖 Fuzzy Matching — AI-generated text validation (separate
@cerios/playwright-expectly-fuzzy package)
- 💪 Type-Safe — full TypeScript support
- ⚡ Lightweight — only Playwright required
Installation
npm install @cerios/playwright-expectly --save-dev
Peer dependency: requires @playwright/test to be installed.
Quick Start
Recommended: a single tests/support module for native expect
Create ONE shared module in your own project that extends Playwright's expect and captures the return value, then re-exports it. Every fixture file and spec file imports test/expect from this module — never straight from @playwright/test.
import "@cerios/playwright-expectly";
import { expect as baseExpect, test as base } from "@playwright/test";
import { expectlyMatchers } from "@cerios/playwright-expectly";
export const expect = baseExpect.extend(expectlyMatchers);
export const test = base;
import { expect, test } from "./support/expect";
test("extended expect example", async ({ page }) => {
expect("john@example.com").toBeValidEmail();
expect([1, 2, 3, 4]).toHaveAscendingOrder();
expect(new Date("2025-01-02")).toBeCloseTo(new Date("2025-01-01"), { days: 1 });
await expect(page.locator(".username")).toBeAlphanumeric();
});
Capturing the return value is the key detail. Playwright only mutates the original expect in place for matcher names that do not collide with built-ins. The Date matcher toBeCloseTo collides with Playwright's native numeric toBeCloseTo, so the Date version only exists on the value returned by .extend(...).
Standalone expectly
import { expectly } from "@cerios/playwright-expectly";
expectly("user@example.com").toBeValidEmail();
expectly([1, 2, 3, 4, 5]).toHaveAscendingOrder();
expectly(new Date()).toBeInTheFuture(new Date("2020-01-01"));
await expectly(page.locator(".username")).toBeAlphanumeric();
Combining with @cerios/playwright-expectly-fuzzy, or your own matchers, via mergeExpects():
import { expect as baseExpect, mergeExpects, test as base } from "@playwright/test";
import { expectlyMatchers } from "@cerios/playwright-expectly";
import { expectlyFuzzyMatchers } from "@cerios/playwright-expectly-fuzzy";
const expectlyExpect = baseExpect.extend(expectlyMatchers);
const fuzzyExpect = baseExpect.extend(expectlyFuzzyMatchers);
export const expect = mergeExpects(baseExpect, expectlyExpect, fuzzyExpect);
export const test = base;
If you only need the standalone package exports, the shorter form is also valid:
import { expectly } from "@cerios/playwright-expectly";
import { expectlyFuzzy } from "@cerios/playwright-expectly-fuzzy";
import { expect as baseExpect, mergeExpects, test as base } from "@playwright/test";
export const expect = mergeExpects(baseExpect, expectly, expectlyFuzzy);
export const test = base;
Note: you may see a setupExpectly() function in older docs or code — it is deprecated. Playwright's expect.extend() only mutates the original expect object in place for matcher names that don't collide with a Playwright built-in. toBeCloseTo collides with Playwright's own built-in numeric matcher, so setupExpectly() can never make the Date toBeCloseTo matcher work, regardless of where it's called. The tests/support pattern above always works correctly.
Available Matchers
- String —
toBeValidEmail(), toBeValidUrl(), toBeUUID(), toBeAlphanumeric(), toStartWith(), toEndWith(), toMatchPattern(), and more — 📖 docs
- Number Array —
toHaveAscendingOrder(), toHaveAverage(), toBeAllPositive(), toBeMonotonic(), and more — 📖 docs
- Date —
toBeCloseTo(), toBeInTheFuture(), toBeSameDay(), toBeInQuarter(), and more — 📖 docs
- Locator —
toBeAlphanumeric(), toBeUpperCase(), toHaveSrc(), toHaveHref(), and more — 📖 docs
- Object Array —
toHaveObjectsInAscendingOrderBy(), toHaveOnlyUniqueObjects(), and more — 📖 docs
- String Array —
toHaveAscendingOrder(), toHaveStrictlyAscendingOrder(), toHaveUniqueValues(), and more — 📖 docs
- Generic —
toBeInteger(), toBeAnyOf(), toEqualPartially(), toBeNullish(), and more — 📖 docs
Links
License
MIT © Cerios