Skip to content

CeriosTesting/playwright-expectly

Repository files navigation

🎭 Playwright Expectly | By Cerios

npm version License: MIT

Comprehensive Playwright test matchers for strings, numbers, dates, arrays, objects, and locators. Simplify your E2E tests with intuitive assertions.

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 with configurable similarity threshold (separate package)
  • πŸ’ͺ Type-Safe - Full TypeScript support
  • ⚑ Lightweight - Only Playwright required. @cerios/playwright-expectly-fuzzy uses fuzzball as an optional dependency

Installation

npm install @cerios/playwright-expectly --save-dev

For fuzzy matching support (AI-generated text) β€” @cerios/playwright-expectly-fuzzy:

npm install @cerios/playwright-expectly-fuzzy --save-dev

Quick Start

Option 1: Use expectly directly

import { expectly } from "@cerios/playwright-expectly";

// String validation
expectly("user@example.com").toBeValidEmail();

// Number array validation
expectly([1, 2, 3, 4, 5]).toHaveAscendingOrder();

// Date validation
expectly(new Date()).toBeInTheFuture(new Date("2020-01-01"));

// Locator validation
await expectly(page.locator(".username")).toBeAlphanumeric();

Option 2: Extend Playwright expect with setupExpectly

The simplest way to add all matchers to Playwright's native expect. Call setupExpectly() once in your Playwright config and every test file gets full IntelliSense and type support automatically β€” no per-file imports needed.

// playwright.config.ts
import { setupExpectly } from "@cerios/playwright-expectly";

setupExpectly();

Then use expect as usual in your tests β€” no extra imports needed:

import { expect, test } from "@playwright/test";

test("extended expect example", async ({ page }) => {
	expect("john@example.com").toBeValidEmail();
	expect([1, 2, 3, 4]).toHaveAscendingOrder();
	await expect(page.locator(".username")).toBeAlphanumeric();
});

If your playwright.config is JavaScript or is not included in your tsconfig.json, add one ambient import in a .d.ts or shared test file so IntelliSense picks up the matcher types:

import "@cerios/playwright-expectly";

With fuzzy matching

To also add toMatchFuzzy() to expect, call setupExpectlyFuzzy() alongside setupExpectly():

// playwright.config.ts
import { setupExpectly } from "@cerios/playwright-expectly";
import { setupExpectlyFuzzy } from "@cerios/playwright-expectly-fuzzy";

setupExpectly();
setupExpectlyFuzzy();

Both calls register their matchers globally β€” setupExpectlyFuzzy() also augments Playwright's Matchers interface automatically when the package is imported.

Option 3: Extend Playwright expect manually

If you prefer explicit control, extend expect in a shared fixtures file and re-export it:

// tests/fixtures.ts
import { expect, test as base } from "@playwright/test";
import { expectlyMatchers } from "@cerios/playwright-expectly";
import { expectlyFuzzyMatchers } from "@cerios/playwright-expectly-fuzzy"; // optional

expect.extend(expectlyMatchers);
expect.extend(expectlyFuzzyMatchers); // optional β€” adds toMatchFuzzy

export { expect };
export const test = base;

Then import expect from your fixtures file in every test:

import { expect, test } from "./fixtures";

test("extended expect example", async ({ page }) => {
	expect("john@example.com").toBeValidEmail();
	expect([1, 2, 3, 4]).toHaveAscendingOrder();
	expect("Hello Wrold").toMatchFuzzy("Hello World");
	await expect(page.locator(".username")).toBeAlphanumeric();
});

Available Matchers

String Matchers

  • toBeValidEmail() - Validate email format
  • toBeValidUrl() - Validate URL format
  • toBeUUID(version?) - Validate UUID (optionally specific version)
  • toBeAlphanumeric() - Only letters and numbers
  • toBeNumericString() - Only digits
  • toStartWith(prefix) / toEndWith(suffix) - String boundaries
  • toMatchPattern(regex) - Regular expression matching

πŸ“– View all string matchers β†’

Number Array Matchers

  • toHaveAscendingOrder() / toHaveDescendingOrder() - Sort validation
  • toHaveAverage(value) / toHaveMedian(value) - Statistical validation
  • toHaveMin(value) / toHaveMax(value) - Boundary validation
  • toBeAllPositive() / toBeAllNegative() - Sign validation
  • toBeMonotonic() - Consistent direction
  • toHaveConsecutiveIntegers() - Sequential validation

πŸ“– View all number array matchers β†’

Date Matchers

  • toBeCloseTo(date, deviation) - Within time deviation
  • toBeInTheFuture(refDate?) / toBeInThePast(refDate?) - Temporal validation
  • toBeSameDay(date) / toBeSameMonth(date) / toBeSameYear(date) - Date comparison
  • toBeInQuarter(quarter) - Quarter validation
  • toBeLeapYear() - Leap year check
  • toHaveConsecutiveDates(unit) - Sequential dates

πŸ“– View all date matchers β†’

Locator Matchers

  • toBeAlphanumeric() - Alphanumeric text
  • toBeNumericString() - Numeric text
  • toBeUUID(version?) - UUID format
  • toBeUpperCase() / toBeLowerCase() - Case validation
  • toBeTitleCase() - Title case format
  • toHaveSrc(value) / toHaveHref(value) / toHaveAlt(value) - Attribute validation

πŸ“– View all locator matchers β†’

Object Array Matchers

  • toHaveObjectsInAscendingOrderBy(property) - Sort by property
  • toHaveObjectsInDescendingOrderBy(property) - Reverse sort by property
  • toHaveOnlyUniqueObjects() - Uniqueness validation

πŸ“– View all object array matchers β†’

String Array Matchers

  • toHaveAscendingOrder() / toHaveDescendingOrder() - Alphabetical order
  • toHaveStrictlyAscendingOrder() - No duplicates ascending
  • toBeMonotonic() - Consistent direction
  • toHaveUniqueValues() - No duplicates

πŸ“– View all string array matchers β†’

Generic Matchers

  • toBeInteger() / toBeFloat() - Number type validation
  • toBeAnyOf(...values) - Multiple value matching
  • toEqualPartially(expected) - Partial object matching
  • toBeNullish() - Null or undefined check
  • toBePrimitive() / toBeArray() / toBeObject() - Type checking

πŸ“– View all generic matchers β†’

Fuzzy Matchers (@cerios/playwright-expectly-fuzzy)

  • toMatchFuzzy(expected, threshold?) - Fuzzy string matching using fuzzball's token-sort ratio. Works on strings and locators. Ideal for AI-generated text validation.

πŸ“– View fuzzy matchers β†’

Usage Examples

Basic String Validation

import { expectly } from "@cerios/playwright-expectly";

test("validate user input", async () => {
	expectly("john.doe@example.com").toBeValidEmail();
	expectly("https://example.com").toBeValidUrl();
	expectly("550e8400-e29b-41d4-a716-446655440000").toBeUUID(4);
});

Number Array Assertions

test("validate sorted data", async () => {
	const scores = [85, 90, 92, 95];

	expectly(scores).toHaveAscendingOrder();
	expectly(scores).toHaveAverage(90.5);
	expectly(scores).toBeAllPositive();
});

Date Comparisons

test("validate dates", async () => {
	const now = new Date();
	const tomorrow = new Date(now.getTime() + 86400000);

	expectly(tomorrow).toBeInTheFuture(now);
	expectly(tomorrow).toBeCloseTo(now, { hours: 24 });
});

DOM Element Validation

test("validate form elements", async ({ page }) => {
	await expectly(page.locator(".email")).toBeValidEmail();
	await expectly(page.locator(".username")).toBeAlphanumeric();
	await expectly(page.locator("img")).toHaveAlt("Company Logo");
});

Advanced Usage

Extend in one place (global)

Use this when you want all tests to use the extra matchers without importing expectly everywhere.

// e.g. tests/fixtures.ts or a shared setup module imported by your tests
import { expect } from "@playwright/test";
import { expectlyMatchers } from "@cerios/playwright-expectly";

expect.extend(expectlyMatchers);

Exported matcher objects

All matcher objects are exported, so you can extend expect with everything or only specific families.

import { expectlyDateMatchers, expectlyMatchers, expectlyStringMatchers } from "@cerios/playwright-expectly";

// All matchers
expect.extend(expectlyMatchers);

// Or only selected matcher families
expect.extend({
	...expectlyStringMatchers,
	...expectlyDateMatchers,
});

Use expectly families directly

Use this when you want a smaller, explicit API per matcher family.

import { expectlyDate, expectlyLocator, expectlyNumberArray, expectlyString } from "@cerios/playwright-expectly";

expectlyString("ABC123").toBeAlphanumeric();
expectlyNumberArray([10, 20, 30]).toHaveAscendingOrder();
expectlyDate(new Date()).toBeSameYear(new Date("2026-01-01"));
await expectlyLocator(page.locator(".email")).toBeValidEmail();

Individual Matcher Imports

For tree-shaking optimization, import only what you need:

import { expectlyString } from "@cerios/playwright-expectly";
import { expectlyNumberArray } from "@cerios/playwright-expectly";
import { expectlyDate } from "@cerios/playwright-expectly";

expectlyString("test@example.com").toBeValidEmail();
expectlyNumberArray([1, 2, 3]).toHaveAscendingOrder();
expectlyDate(new Date()).toBeInTheFuture(new Date("2020-01-01"));

Negation

All matchers support .not for inverse assertions:

expectly("not-an-email").not.toBeValidEmail();
expectly([5, 3, 1]).not.toHaveAscendingOrder();
await expectly(page.locator(".text")).not.toBeNumericString();

Documentation

Contributing

Contributions are welcome! Feel free to open an issue or submit a pull request.

License

MIT Β© Cerios

Support


Built with ❀️ by Cerios

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors