Package Exports
- expect-asymmetric
- expect-asymmetric/dist/index.js
- expect-asymmetric/dist/index.mjs
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (expect-asymmetric) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
expect-asymmetric
Asymmetric matchers for expect (Jest/Vitest)
These matchers work inside expect.toEqual() / expect.toMatchObject(), letting you ignore noise (IDs, dates,
timestamps, etc.) and assert only what matters.
Asymmetric matchers are the same kind of objects you get from expect.stringContaining() or expect.any(Number) — but
here you can define your own.
Install
$ npm install --save-dev expect-asymmetric
$ yarn add -D expect-asymmetric
$ pnpm add -D expect-asymmetricUsage
import ms from 'ms'
import { expect, test } from 'vitest'
import matchers from 'expect-asymmetric'
test('example with custom matchers', () => {
const result = {
id: 'f93f5f83-64c0-4f57-9f53-542c9055bb3f',
createdAt: new Date(),
email: 'user@example.com',
}
expect(result).toEqual({
id: matchers.stringUUID(),
createdAt: matchers.dateWithin(new Date(), ms('2s')),
email: matchers.stringMatching(/@example\.com$/),
})
})Matchers
You can import matchers from this package either by:
- As a complete import (this is how the documentation is written):
import { expect } from 'vitest'
import matchers from 'expect-asymmetric'
expect("Hello, world!").toEqual(matchers.stringStartsWith("Hello"))- Or as individual functions:
import { expect } from 'vitest'
import { stringStartsWith } from 'expect-asymmetric'
expect("Hello, world!").toEqual(stringStartsWith("Hello"))Strings
stringEquals(expected: string | string[]): AsymmetricMatcher
import { expect } from 'vitest'
import matchers from 'expect-asymmetric'
expect("Hello, world!").toEqual(matchers.stringEquals("Hello, world!"))
// This is identical to:
expect("Hello, world!").toEqual("Hello, world!")
// But provided so you can use matchers.not with thisstringStartsWith(prefix: string): AsymmetricMatcher
import { expect } from 'vitest'
import matchers from 'expect-asymmetric'
expect("Hello, world!").toEqual(matchers.stringStartsWith("Hello"))stringEndsWith(suffix: string): AsymmetricMatcher
import { expect } from 'vitest'
import matchers from 'expect-asymmetric'
expect("Hello, world!").toEqual(matchers.stringEndsWith("world!"))stringIncludes(contains: string): AsymmetricMatcher
import { expect } from 'vitest'
import matchers from 'expect-asymmetric'
expect("Hello, world!").toEqual(matchers.stringIncludes('ello, world'))stringMatches(pattern: RegExp): AsymmetricMatcher
import { expect } from 'vitest'
import matchers from 'expect-asymmetric'
expect("Hello, world!").toEqual(matchers.stringMatches(/hello/i))stringBase64Encoded(): AsymmetricMatcher
import { expect } from 'vitest'
import matchers from 'expect-asymmetric'
expect("WW91IGFyZSB0aGUgT25lLCBOZW8=").toEqual(matchers.stringBase64Encoded())stringDateISO8601(): AsymmetricMatcher
import { expect } from 'vitest'
import matchers from 'expect-asymmetric'
expect("YYYY-MM-DDTHH:mm:ss.sssZ").toEqual(matchers.stringDateISO8601())stringEmail(): AsymmetricMatcher
import { expect } from 'vitest'
import matchers from 'expect-asymmetric'
expect("noreply+jdrydn@github.io").toEqual(matchers.stringEmail())stringJSON(): AsymmetricMatcher
import { expect } from 'vitest'
import matchers from 'expect-asymmetric'
expect('{"hello": "world"}').toEqual(matchers.stringJSON())stringJWT(): AsymmetricMatcher
import { expect } from 'vitest'
import matchers from 'expect-asymmetric'
const jwt = 'eyJhbGciOiJIUzI1NiJ9.RlJFRSBZT1VSIE1JTkQ.edKefjjRqI9yxTTT6vpuWti040WoGzX5a-ofc36-acg'
expect(jwt).toEqual(matchers.stringJWT())stringMD5(): AsymmetricMatcher
import { expect } from 'vitest'
import matchers from 'expect-asymmetric'
const md5 = '064ff83b22543c064ff83b22543ca5e9f7cfa1d702daa9a5e9f7cfa1d702daa9'
expect(md5).toEqual(matchers.stringMD5())stringSHA1(): AsymmetricMatcher
import { expect } from 'vitest'
import matchers from 'expect-asymmetric'
const sha1 = '467d6b9adfe97245b6504467d6b9adfe97245b65049168be0c26da8fe8d749168be0c26da8fe8d74'
expect(sha1).toEqual(matchers.stringSHA1())stringULID(): AsymmetricMatcher
import { expect } from 'vitest'
import matchers from 'expect-asymmetric'
expect('01ARZ3NDEKTSV4RRFFQ69G5FAV').toEqual(matchers.stringULID())stringUUID(): AsymmetricMatcher
import { expect } from 'vitest'
import matchers from 'expect-asymmetric'
expect('fb840792-9595-11f0-8de9-0242ac120002').toEqual(matchers.stringUUID())Numbers
numberEquals(expected: number | number[]): AsymmetricMatcher
import { expect } from 'vitest'
import matchers from 'expect-asymmetric'
expect(42).toEqual(matchers.numberEquals(42))
// This is identical to:
expect(42).toEqual(42)
// But provided so you can use matchers.not with thisnumberGreaterThan(expected: number): AsymmetricMatcher
import { expect } from 'vitest'
import matchers from 'expect-asymmetric'
expect(42).toEqual(matchers.numberGreaterThan(40))numberLessThan(expected: number): AsymmetricMatcher
import { expect } from 'vitest'
import matchers from 'expect-asymmetric'
expect(42).toEqual(matchers.numberLessThan(45))numberBetween(min: number, max: number): AsymmetricMatcher
import { expect } from 'vitest'
import matchers from 'expect-asymmetric'
expect(42).toEqual(matchers.numberLessThan(40, 45))numberFloat(): AsymmetricMatcher
import { expect } from 'vitest'
import matchers from 'expect-asymmetric'
expect(3.14159265).toEqual(matchers.numberFloat())Booleans
booleanEquals(expected: boolean): AsymmetricMatcher
import { expect } from 'vitest'
import matchers from 'expect-asymmetric'
expect(true).toEqual(matchers.booleanEquals(true))
// This is identical to:
expect(true).toEqual(true)
// But provided so you can use matchers.not with thisDates
dateEquals(expected: Date | Date[]): AsymmetricMatcher
import { expect } from 'vitest'
import matchers from 'expect-asymmetric'
const now = new Date()
expect(now).toEqual(matchers.dateEquals(now))
// This is identical to:
expect(now).toEqual(now)
// But provided so you can use matchers.not with thisdateToday(): AsymmetricMatcher
import { expect } from 'vitest'
import matchers from 'expect-asymmetric'
const today = new Date()
expect(today).toEqual(matchers.dateToday())
expect(today.toISOString()).toEqual(matchers.dateToday())dateYesterday(): AsymmetricMatcher
import { expect } from 'vitest'
import matchers from 'expect-asymmetric'
const yesterday = new Date(Date.now() - 86400 * 1000)
expect(yesterday).toEqual(matchers.dateYesterday())
expect(yesterday.toISOString()).toEqual(matchers.dateYesterday())dateGreaterThan(expected: Date): AsymmetricMatcher
import { expect } from 'vitest'
import matchers from 'expect-asymmetric'
const now = new Date('2025-09-25T21:45:00.000Z')
const then = new Date('2025-09-25T20:00:00.000Z')
expect(now).dateGreaterThan(matchers.dateGreaterThan(then))dateLessThan(expected: Date): AsymmetricMatcher
import { expect } from 'vitest'
import matchers from 'expect-asymmetric'
const now = new Date('2025-09-25T21:45:00.000Z')
const then = new Date('2025-09-25T22:00:00.000Z')
expect(now).dateGreaterThan(matchers.dateLessThan(then))dateWithin(target: Date, tolerance: number): AsymmetricMatcher
import { expect } from 'vitest'
import matchers from 'expect-asymmetric'
import ms from 'ms'
const today = new Date()
expect(today).toEqual(matchers.dateWithin(today, ms('20m')))
expect(today.toISOString()).toEqual(matchers.dateWithin(today, ms('20m')))Collections
arrayContains(expected: unknown): AsymmetricMatcher
import { expect } from 'vitest'
import matchers from 'expect-asymmetric'
expect([4, 8, 15, 16, 23, 42]).toEqual(matchers.arrayContains(42))arrayEmpty(): AsymmetricMatcher
import { expect } from 'vitest'
import matchers from 'expect-asymmetric'
expect([]).toEqual(matchers.arrayEmpty())objectEmpty(): AsymmetricMatcher
import { expect } from 'vitest'
import matchers from 'expect-asymmetric'
expect({}).toEqual(matchers.objectEmpty())objectHasProperty(key: string): AsymmetricMatcher
import { expect } from 'vitest'
import matchers from 'expect-asymmetric'
expect({ hello: 'world' }).toEqual(matchers.objectHasProperty('hello'))objectWithProperty(key: string, value: unknown): AsymmetricMatcher
import { expect } from 'vitest'
import matchers from 'expect-asymmetric'
expect({ hello: 'world' }).toEqual(matchers.objectWithProperty('hello', 'world'))Compose
and(matchers: AsymmetricMatcher[]): AsymmetricMatcher
import { expect } from 'vitest'
import matchers from 'expect-asymmetric'
expect("someone@example.com").toEqual(matchers.and([
matchers.stringStartsWith("someone@"),
matchers.stringEndsWith("@example.com"),
]))or(matchers: AsymmetricMatcher[]): AsymmetricMatcher
import { expect } from 'vitest'
import matchers from 'expect-asymmetric'
expect("someone@example.com").toEqual(matchers.or([
matchers.stringEndsWith("@example.com"),
matchers.stringEndsWith("@example.co.uk"),
matchers.stringEndsWith("@example.local"),
]))not(matchers: AsymmetricMatcher[]): AsymmetricMatcher
import { expect } from 'vitest'
import matchers from 'expect-asymmetric'
expect("someone@example.com").toEqual(matchers.not([
matchers.stringEndsWith("@example.co.uk"),
matchers.stringEndsWith("@example.local"),
]))Custom
You can use custom validators to check specific values.
- The
descriptionproperty will be shown to the developer when this matcher fails.
stringValidator(description: string, compare: (value: string) => boolean): AsymmetricMatcher
import { expect } from 'vitest'
import matchers from 'expect-asymmetric'
const isCaseInsensitive = matches.stringValidator('isCaseInsensitive', (value) => value.toLowerCase() === value)
expect("hello world").toEqual(isCaseInsensitive)This works great with validator.js, for example:
import { expect } from 'vitest'
import matchers from 'expect-asymmetric'
import isSemVer from 'validator/es/lib/isSemVer';
expect("1.0.0").toEqual(matches.stringValidator('isSemVer', isSemVer))numberValidator(description: string, compare: (value: number) => boolean): AsymmetricMatcher
import { expect } from 'vitest'
import matchers from 'expect-asymmetric'
expect("hello world").toEqual(matches.numberValidator('isOdd', (value) => value % 2 === 1))booleanValidator(description: string, compare: (value: boolean) => boolean): AsymmetricMatcher
import { expect } from 'vitest'
import matchers from 'expect-asymmetric'
expect("hello world").toEqual(matches.numberValidator('example', (value) => value === true))customMatcher(description: string, compare: (value: unknown) => boolean): AsymmetricMatcher
import { expect } from 'vitest'
import matchers from 'expect-asymmetric'
expect(new CustomClass()).toEqual(matches.customMatcher('example', (value) => value instanceof CustomClass))Notes
Any questions or suggestions please open an issue.