UNPKG

@tevm/test-matchers

Version:

Vite test matchers for Tevm or EVM-related testing in TypeScript.

163 lines (152 loc) 5.23 kB
import type { AbiEventParameter, AbiParametersToPrimitiveTypes, ExtractAbiEvent } from 'abitype' import type { Abi, ContractEventName, Hex } from 'viem' import { createChainableFromVitest } from '../../chainable/chainable.js' import type { ChainableAssertion } from '../../chainable/types.js' import type { AbiInputsToNamedArgs, ContainsContractAbi } from '../../common/types.js' import { toEmit } from './toEmit.js' import { withEventArgs } from './withEventArgs.js' import { withEventNamedArgs } from './withEventNamedArgs.js' // Create chainable matchers export const toEmitChainable = createChainableFromVitest({ name: 'toEmit' as const, vitestMatcher: toEmit, }) export const withEventArgsChainable = createChainableFromVitest({ name: 'withEventArgs' as const, vitestMatcher: withEventArgs, }) export const withEventNamedArgsChainable = createChainableFromVitest({ name: 'withEventNamedArgs' as const, vitestMatcher: withEventNamedArgs, }) // Register the chainable matchers with the new structure export const chainableEventMatchers = { toEmit: toEmitChainable, withEventArgs: withEventArgsChainable, withEventNamedArgs: withEventNamedArgsChainable, } export { toEmit, withEventArgs, withEventNamedArgs } // TypeScript declaration for vitest export interface EmitMatchers { /** * Asserts that a transaction emitted a specific event. * Can be used with contract objects, event signatures, or selectors. * * @param contract - Contract object with ABI and address * @param eventName - Name of the event in the ABI * * @example * ```typescript * // Using contract object * await expect(txHash) * .toEmit(tokenContract, 'Transfer') * * // Chain with argument assertions * await expect(txHash) * .toEmit(tokenContract, 'Transfer') * .withEventArgs(from, to, 100n) * * // Chain with argument assertions by name (partial matching) * await expect(txHash) * .toEmit(tokenContract, 'Transfer') * .withEventNamedArgs({ to }) * ``` * * @see {@link withEventArgs} to test event arguments positionally * @see {@link withEventNamedArgs} to test event arguments by name */ toEmit<TAbi extends Abi, TEventName extends ContractEventName<TAbi>>( contract: ContainsContractAbi<TAbi>, eventName: TEventName, ): Promise<EmitAssertionWithContract<TAbi, TEventName>> & EmitAssertionWithContract<TAbi, TEventName> /** * Asserts that a transaction emitted an event matching the signature. * * @param eventSignature - Event signature string (e.g., "Transfer(address,address,uint256)") * * @example * ```typescript * await expect(txHash) * .toEmit('Transfer(address,address,uint256)') * .withEventArgs(from, to, amount) * ``` */ toEmit(eventSignature: string): ChainableAssertion /** * Asserts that a transaction emitted an event matching the selector. * * @param eventSelector - Event selector (4-byte hex) * * @example * ```typescript * await expect(txHash) * .toEmit('0xddf252ad...') // Transfer event selector * ``` */ toEmit(eventSelector: Hex): ChainableAssertion } // Only-after toEmit assertion type interface EmitAssertionWithContract<TAbi extends Abi, TEventName extends ContractEventName<TAbi>> { /** * Chains with toEmit to assert event arguments in positional order. * Arguments must match exactly in the order they appear in the event. * * **Limitation**: Cannot use .not before this method. * * @param expectedArgs - Expected arguments in order * * @example * ```typescript * // Transfer event: Transfer(address from, address to, uint256 value) * await expect(txHash) * .toEmit(tokenContract, 'Transfer') * .withEventArgs( * '0x742d35Cc6274c36e1019e41D77d0A4aa7D7dE01e', // from * '0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed', // to * 1000n // value * ) * ``` * * @see {@link withEventNamedArgs} for partial matching by name */ withEventArgs< TInputs extends readonly AbiEventParameter[] = ExtractAbiEvent<TAbi, TEventName> extends { inputs: infer U extends readonly AbiEventParameter[] } ? U : readonly AbiEventParameter[], >(...expectedArgs: AbiParametersToPrimitiveTypes<TInputs>): ChainableAssertion /** * Chains with toEmit to assert event arguments by name. * Supports partial matching - only specified arguments are checked. * * **Limitation**: Cannot use .not before this method. * * @param expectedArgs - Object with expected named arguments (partial) * * @example * ```typescript * // Check only specific arguments * await expect(txHash) * .toEmit(tokenContract, 'Transfer') * .withEventNamedArgs({ * to: recipient, * value: 1000n * }) * * // Empty object matches any event of this type * await expect(txHash) * .toEmit(tokenContract, 'Transfer') * .withEventNamedArgs({}) * ``` * * @see {@link withEventArgs} for positional argument matching */ withEventNamedArgs< TInputs extends readonly AbiEventParameter[] = ExtractAbiEvent<TAbi, TEventName> extends { inputs: infer U extends readonly AbiEventParameter[] } ? U : readonly AbiEventParameter[], >(expectedArgs: Partial<AbiInputsToNamedArgs<TInputs>>): ChainableAssertion }