@tevm/test-matchers
Version:
Vite test matchers for Tevm or EVM-related testing in TypeScript.
248 lines (209 loc) • 9.85 kB
text/typescript
import type { Assertion } from 'vitest'
import { describe, expectTypeOf, it } from 'vitest'
import { toBeAddress, toBeHex } from '../matchers/utils/index.js'
import { createChainableFromVitest } from './chainable.js'
import { type CustomMatchers, testMatchers } from './chainable.spec.js'
import type {
ChainableAssertion,
ChainState,
InferredVitestChainableResult,
MatcherResult,
VitestMatcherConfig,
VitestMatcherFunction,
} from './types.js'
// Import existing chainable matchers from spec
const {
toBeBigIntChainable,
toBeHexChainable,
toBeAddressChainable,
toPassDownStateChainable,
toUsePreviousStateAndBigIntChainable,
toResolveToStringChainable,
} = testMatchers
// Remove the separate asyncMatcher and asyncChainable - use the shared one
describe('chainable type system (exhaustive)', () => {
it('type: basic configuration inference', () => {
// Test real matcher config inference
expectTypeOf<typeof toBeBigIntChainable>().toEqualTypeOf<
InferredVitestChainableResult<VitestMatcherConfig<'toBeBigIntChainable', unknown, false, unknown>>
>()
expectTypeOf<typeof toBeHexChainable>().toEqualTypeOf<
InferredVitestChainableResult<VitestMatcherConfig<'toBeHexChainable', unknown, false, unknown>>
>()
expectTypeOf<typeof toBeAddressChainable>().toEqualTypeOf<
InferredVitestChainableResult<VitestMatcherConfig<'toBeAddressChainable', unknown, false, unknown>>
>()
expectTypeOf<typeof toResolveToStringChainable>().toExtend<
InferredVitestChainableResult<
VitestMatcherConfig<'toResolveToStringChainable', Promise<string>, true, { resolved: boolean }>
>
>()
// Name should be inferred correctly
expectTypeOf<typeof toBeBigIntChainable.name>().toEqualTypeOf<'toBeBigIntChainable'>()
expectTypeOf<typeof toBeHexChainable.name>().toEqualTypeOf<'toBeHexChainable'>()
expectTypeOf<typeof toBeAddressChainable.name>().toEqualTypeOf<'toBeAddressChainable'>()
expectTypeOf<typeof toResolveToStringChainable.name>().toEqualTypeOf<'toResolveToStringChainable'>()
})
it('type: isAsync flag inference', () => {
// Sync matchers should have isAsync: false
expectTypeOf<typeof toBeBigIntChainable.isAsync>().toEqualTypeOf<false>()
expectTypeOf<typeof toBeHexChainable.isAsync>().toEqualTypeOf<false>()
expectTypeOf<typeof toBeAddressChainable.isAsync>().toEqualTypeOf<false>()
// Async matcher should have isAsync: true
expectTypeOf<typeof toResolveToStringChainable.isAsync>().toEqualTypeOf<true>()
})
it('type: method function parameter inference', () => {
// Test that method functions exist and are functions
expectTypeOf<typeof toBeBigIntChainable.methodFunction>().toBeFunction()
expectTypeOf<typeof toBeHexChainable.methodFunction>().toBeFunction()
expectTypeOf<typeof toPassDownStateChainable.methodFunction>().toBeFunction()
expectTypeOf<typeof toResolveToStringChainable.methodFunction>().toBeFunction()
})
it('type: return type inference (sync vs async)', () => {
// Sync matchers return Assertion
expectTypeOf<ReturnType<typeof toBeBigIntChainable.methodFunction>>().toEqualTypeOf<Assertion>()
expectTypeOf<ReturnType<typeof toBeHexChainable.methodFunction>>().toEqualTypeOf<Assertion>()
expectTypeOf<ReturnType<typeof toPassDownStateChainable.methodFunction>>().toEqualTypeOf<Assertion>()
// Async matchers return ChainableAssertion
expectTypeOf<ReturnType<typeof toResolveToStringChainable.methodFunction>>().toExtend<ChainableAssertion>()
})
it('type: chain function consistency', () => {
// All chain functions should have the same signature
expectTypeOf<typeof toBeBigIntChainable.chainFunction>().toBeFunction()
expectTypeOf<typeof toBeHexChainable.chainFunction>().toBeFunction()
expectTypeOf<typeof toPassDownStateChainable.chainFunction>().toBeFunction()
expectTypeOf<typeof toResolveToStringChainable.chainFunction>().toBeFunction()
})
it('type: vitest matcher function types', () => {
// Test that vitest matcher types are properly structured
expectTypeOf<VitestMatcherFunction<unknown, false, unknown>>().toBeFunction()
expectTypeOf<VitestMatcherFunction<Promise<string>, true, { resolved: boolean }>>().toBeFunction()
// Test specific return type structure
expectTypeOf<MatcherResult<{ resolved: boolean }>>().toEqualTypeOf<{
pass: boolean
message: () => string
actual?: unknown
expected?: unknown
state?: { resolved: boolean }
}>()
})
it('type: chain state structure', () => {
// Test ChainState type structure with real state types
type TestState = { a: unknown; b: unknown; originalValue: unknown }
expectTypeOf<ChainState<bigint, TestState>>().toEqualTypeOf<{
chainedFrom: string | undefined
previousPassed: boolean | undefined
previousValue: bigint | undefined
previousState: TestState | undefined
previousArgs: readonly unknown[] | undefined
}>()
// Test that ChainState can be used with different types
expectTypeOf<ChainState<string, { resolved: boolean }>>().toEqualTypeOf<{
chainedFrom: string | undefined
previousPassed: boolean | undefined
previousValue: string | undefined
previousState: { resolved: boolean } | undefined
previousArgs: readonly unknown[] | undefined
}>()
})
it('type: real world matcher inference', () => {
// Test that real matchers are properly typed
expectTypeOf<typeof toBeBigIntChainable>().toEqualTypeOf<
InferredVitestChainableResult<VitestMatcherConfig<'toBeBigIntChainable', unknown, false, unknown>>
>()
expectTypeOf<typeof toBeHexChainable>().toEqualTypeOf<
InferredVitestChainableResult<VitestMatcherConfig<'toBeHexChainable', unknown, false, unknown>>
>()
expectTypeOf<typeof toBeAddressChainable>().toEqualTypeOf<
InferredVitestChainableResult<VitestMatcherConfig<'toBeAddressChainable', unknown, false, unknown>>
>()
expectTypeOf<typeof toResolveToStringChainable>().toExtend<
InferredVitestChainableResult<
VitestMatcherConfig<'toResolveToStringChainable', Promise<string>, true, { resolved: boolean }>
>
>()
})
it('type: state-aware matchers', () => {
// Test state passing matchers from spec
expectTypeOf<typeof toPassDownStateChainable>().toEqualTypeOf<
InferredVitestChainableResult<VitestMatcherConfig<'toPassDownStateChainable', unknown, false, any>>
>()
expectTypeOf<typeof toUsePreviousStateAndBigIntChainable>().toEqualTypeOf<
InferredVitestChainableResult<VitestMatcherConfig<'toUsePreviousStateAndBigIntChainable', unknown, false, any>>
>()
})
it('type: utility types exist and work', () => {
// Test that ChainState is properly generic and has the right properties
expectTypeOf<ChainState<string, { resolved: boolean }>>().toHaveProperty('previousValue')
expectTypeOf<ChainState<string, { resolved: boolean }>>().toHaveProperty('previousState')
expectTypeOf<ChainState<string, { resolved: boolean }>>().toHaveProperty('chainedFrom')
})
it('type: custom matchers interface', () => {
// Test that CustomMatchers interface is properly defined
expectTypeOf<CustomMatchers>().toHaveProperty('toBeBigIntChainable')
expectTypeOf<CustomMatchers>().toHaveProperty('toBeHexChainable')
expectTypeOf<CustomMatchers>().toHaveProperty('toBeAddressChainable')
expectTypeOf<CustomMatchers>().toHaveProperty('toPassDownStateChainable')
expectTypeOf<CustomMatchers>().toHaveProperty('toUsePreviousStateAndBigIntChainable')
expectTypeOf<CustomMatchers>().toHaveProperty('toResolveToStringChainable')
})
it('type: negative test cases (should not compile)', () => {
// These test cases document what should NOT work
// @ts-expect-error - cannot use wrong received type
const wrongReceived: VitestMatcherFunction<number, false> = (_received: string) => ({
pass: true,
message: () => '',
})
// @ts-expect-error - cannot use wrong async flag
const wrongAsync: VitestMatcherFunction<number, false> = async (_received: number) => ({
pass: true,
message: () => '',
})
// @ts-expect-error - async matcher must return Promise
const asyncWithoutPromise: VitestMatcherFunction<number, true> = (_received: number) => ({
pass: true,
message: () => '',
})
// Suppress unused variable warnings
void wrongReceived
void wrongAsync
void asyncWithoutPromise
})
it('type: conditional return types based on TAsync', () => {
// Test that conditional types work correctly
type SyncResult = VitestMatcherFunction<unknown, false, unknown> extends VitestMatcherFunction<any, true, any>
? ChainableAssertion
: Assertion
type AsyncResult = VitestMatcherFunction<
Promise<string>,
true,
{ resolved: boolean }
> extends VitestMatcherFunction<any, true, any>
? ChainableAssertion
: Assertion
expectTypeOf<SyncResult>().toEqualTypeOf<Assertion>()
expectTypeOf<AsyncResult>().toEqualTypeOf<ChainableAssertion>()
})
it('type: createChainableFromVitest function signature', () => {
// Test that the creation function itself is properly typed
expectTypeOf<typeof createChainableFromVitest>().toBeFunction()
// Test that it can accept proper configs with real matchers
expectTypeOf<VitestMatcherConfig<'toBeBigInt', unknown, false, unknown>>().toEqualTypeOf<{
readonly name: 'toBeBigInt'
readonly vitestMatcher: VitestMatcherFunction<unknown, false, unknown>
}>()
})
it('type: real matcher functions from utils', () => {
// Test that the original vitest matchers are properly typed
expectTypeOf<typeof toBeHex>().toBeFunction()
expectTypeOf<typeof toBeAddress>().toBeFunction()
// Test that they can be used to create chainable matchers
const testChainable = createChainableFromVitest({
name: 'testMatcher' as const,
vitestMatcher: toBeHex,
})
expectTypeOf<typeof testChainable>().toEqualTypeOf<
InferredVitestChainableResult<VitestMatcherConfig<'testMatcher', unknown, false, unknown>>
>()
})
})