Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/visible/cruel/llms.txt

Use this file to discover all available pages before exploring further.

Cruel provides custom test matchers for Vitest, Jest, and Bun to make testing chaos behaviors easier.

Setup

import { setupMatchers } from 'cruel/matchers'
import { beforeAll } from 'bun:test'

beforeAll(() => {
  setupMatchers()
})

Matchers

toThrowCruelError

Check if function throws any Cruel error:
import { cruel, matchers } from 'cruel/matchers'
import { expect, test } from 'bun:test'

test('throws cruel error', () => {
  const fn = () => {
    throw new CruelError('test')
  }
  
  expect(fn).toThrowCruelError()
})

toThrowTimeout

Check if function throws a timeout error:
test('throws timeout', async () => {
  const fn = cruel.timeout(async () => 'data', 1)
  
  await expect(fn()).toEventuallyThrow(CruelTimeoutError)
})
received
() => unknown
required
Function to test
Returns: MatcherResult

toThrowNetworkError

Check if function throws a network error, optionally with specific type:
test('throws network error', () => {
  const fn = () => {
    throw new CruelNetworkError('disconnect')
  }
  
  expect(fn).toThrowNetworkError()
  expect(fn).toThrowNetworkError('disconnect')
})
received
() => unknown
required
Function to test
type
string
Expected network error type (disconnect, packet_loss, dns_failure, offline)
Returns: MatcherResult

toThrowHttpError

Check if function throws an HTTP error, optionally with specific status:
test('throws http error', () => {
  const fn = () => {
    throw new CruelHttpError(500)
  }
  
  expect(fn).toThrowHttpError()
  expect(fn).toThrowHttpError(500)
})
received
() => unknown
required
Function to test
status
number
Expected HTTP status code
Returns: MatcherResult

toThrowRateLimit

Check if function throws a rate limit error:
test('throws rate limit', () => {
  const fn = () => {
    throw new CruelRateLimitError(60)
  }
  
  expect(fn).toThrowRateLimit()
})
received
() => unknown
required
Function to test
Returns: MatcherResult

toThrowAIError

Check if function throws an AI error, optionally with specific type:
test('throws AI error', () => {
  const fn = () => {
    throw new CruelAIError('rate_limit')
  }
  
  expect(fn).toThrowAIError()
  expect(fn).toThrowAIError('rate_limit')
})
received
() => unknown
required
Function to test
type
string
Expected AI error type (rate_limit, overloaded, context_length, content_filter, model_unavailable, stream_cut)
Returns: MatcherResult

toEventuallyThrow

Check if async function throws an error:
test('eventually throws', async () => {
  const fn = async () => {
    throw new Error('failed')
  }
  
  await expect(fn()).toEventuallyThrow()
  await expect(fn()).toEventuallyThrow(Error)
})
received
() => Promise<unknown>
required
Async function to test
errorType
new () => Error
Expected error class
Returns: Promise<MatcherResult>

toCompleteWithin

Check if async function completes within time limit:
test('completes quickly', async () => {
  const fn = async () => {
    await new Promise(r => setTimeout(r, 100))
    return 'done'
  }
  
  await expect(fn()).toCompleteWithin(200)
})
received
() => Promise<unknown>
required
Async function to test
ms
number
required
Maximum allowed duration in milliseconds
Returns: Promise<MatcherResult>

toTakeLongerThan

Check if async function takes at least specified time:
test('takes time', async () => {
  const fn = cruel.slow(async () => 'data', 500)
  
  await expect(fn()).toTakeLongerThan(400)
})
received
() => Promise<unknown>
required
Async function to test
ms
number
required
Minimum expected duration in milliseconds
Returns: Promise<MatcherResult>

Usage with testing frameworks

Bun

import { setupMatchers } from 'cruel/matchers'
import { beforeAll, test, expect } from 'bun:test'

beforeAll(() => {
  setupMatchers()
})

test('uses matchers', async () => {
  const fn = cruel.fail(async () => 'data', 1)
  await expect(fn()).toEventuallyThrow()
})

Vitest

import { setupMatchers } from 'cruel/matchers'
import { beforeAll, test, expect } from 'vitest'

beforeAll(() => {
  setupMatchers()
})

test('uses matchers', async () => {
  const fn = cruel.fail(async () => 'data', 1)
  await expect(fn()).toEventuallyThrow()
})

Jest

import { setupMatchers } from 'cruel/matchers'
import { beforeAll, test, expect } from '@jest/globals'

beforeAll(() => {
  setupMatchers()
})

test('uses matchers', async () => {
  const fn = cruel.fail(async () => 'data', 1)
  await expect(fn()).resolves.toEventuallyThrow()
})

Type definitions

interface MatcherResult {
  pass: boolean
  message: () => string
}

declare global {
  namespace jest {
    interface Matchers<R> {
      toThrowCruelError(): R
      toThrowTimeout(): R
      toThrowNetworkError(type?: string): R
      toThrowHttpError(status?: number): R
      toThrowRateLimit(): R
      toThrowAIError(type?: string): R
      toEventuallyThrow(errorType?: new () => Error): Promise<R>
      toCompleteWithin(ms: number): Promise<R>
      toTakeLongerThan(ms: number): Promise<R>
    }
  }
}
Matchers are automatically extended to expect when you call setupMatchers().
Call setupMatchers() once in your test setup (beforeAll or setupFiles) to enable all matchers globally.

Build docs developers (and LLMs) love