UNPKG

@tevm/test-matchers

Version:

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

174 lines (163 loc) 6.35 kB
import type { TevmNode } from '@tevm/node' import type { AbiParameter, AbiParametersToPrimitiveTypes, ExtractAbiFunction } from 'abitype' import type { Abi, Client, ContractFunctionName, Hex } from 'viem' import { createChainableFromVitest } from '../../chainable/chainable.js' import type { ChainableAssertion } from '../../chainable/types.js' import type { AbiInputsToNamedArgs, ContainsContractAddressAndOptionalAbi } from '../../common/types.js' import { toCallContractFunction } from './toCallContractFunction.js' import { withFunctionArgs } from './withFunctionArgs.js' import { withFunctionNamedArgs } from './withFunctionNamedArgs.js' // Create chainable matchers export const toCallContractFunctionChainable = createChainableFromVitest({ name: 'toCallContractFunction' as const, vitestMatcher: toCallContractFunction, }) export const withFunctionArgsChainable = createChainableFromVitest({ name: 'withFunctionArgs' as const, vitestMatcher: withFunctionArgs, }) export const withFunctionNamedArgsChainable = createChainableFromVitest({ name: 'withFunctionNamedArgs' as const, vitestMatcher: withFunctionNamedArgs, }) // Register the chainable matchers export const chainableContractMatchers = { toCallContractFunction: toCallContractFunctionChainable, withFunctionArgs: withFunctionArgsChainable, withFunctionNamedArgs: withFunctionNamedArgsChainable, } export { toCallContractFunction, withFunctionArgs, withFunctionNamedArgs } // TypeScript declaration for vitest export interface ContractMatchers { /** * Asserts that a transaction called a specific contract function. * Can be used with contract objects, function signatures, or selectors. * * @param client - Client for transaction execution * @param contract - Contract object with ABI and address * @param functionName - Name of the function in the ABI * * @example * ```typescript * // Using contract object * await expect(txHash) * .toCallContractFunction(client, tokenContract, 'transfer') * * // Chain with argument assertions * await expect(txHash) * .toCallContractFunction(client, tokenContract, 'transfer') * .withFunctionArgs(to, 100n) * * // Chain with argument assertions by name (partial matching) * await expect(txHash) * .toCallContractFunction(client, tokenContract, 'transfer') * .withFunctionNamedArgs({ to, value: 100n }) * ``` * * @see {@link withFunctionArgs} to test function arguments positionally * @see {@link withFunctionNamedArgs} to test function arguments by name */ toCallContractFunction<TAbi extends Abi, TFunctionName extends ContractFunctionName<TAbi>>( client: Client | TevmNode, contract: ContainsContractAddressAndOptionalAbi<TAbi>, functionName: TFunctionName, ): Promise<ContractAssertionWithContract<TAbi, TFunctionName>> & ContractAssertionWithContract<TAbi, TFunctionName> /** * Asserts that a transaction called a function matching the signature. * * @param client - Client for transaction execution * @param functionSignature - Function signature string (e.g., "transfer(address,uint256)") * * @example * ```typescript * await expect(txHash).toCallContractFunction(client, contract, 'transfer(address,uint256)') * await expect(txHash).toCallContractFunction(client, { address: '0x123...' }, 'transfer(address,uint256)') * ``` */ toCallContractFunction( client: Client | TevmNode, contract: ContainsContractAddressAndOptionalAbi, functionSignature: string, ): ChainableAssertion /** * Asserts that a transaction called a function matching the selector. * * @param client - Client for transaction execution * @param functionSelector - Function selector (4-byte hex) * * @example * ```typescript * await expect(txHash).toCallContractFunction(client, contract, '0xa9059cbb') // transfer function selector * await expect(txHash).toCallContractFunction(client, { address: '0x123...' }, '0xa9059cbb') // transfer function selector * ``` */ toCallContractFunction( client: Client | TevmNode, contract: ContainsContractAddressAndOptionalAbi, functionSelector: Hex, ): ChainableAssertion } // Only-after toCallContractFunction assertion type interface ContractAssertionWithContract<TAbi extends Abi, TFunctionName extends ContractFunctionName<TAbi>> { /** * Chains with toCallContractFunction to assert function arguments in positional order. * Arguments must match exactly in the order they appear in the function. * * **Limitation**: Cannot use .not before this method. * * @param expectedArgs - Expected arguments in order * * @example * ```typescript * // transfer function: transfer(address to, uint256 value) * await expect(txHash) * .toCallContractFunction(client, tokenContract, 'transfer') * .withFunctionArgs( * '0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed', // to * 1000n // value * ) * ``` * * @see {@link withFunctionNamedArgs} for partial matching by name */ withFunctionArgs< TInputs extends readonly AbiParameter[] = ExtractAbiFunction<TAbi, TFunctionName> extends { inputs: infer U extends readonly AbiParameter[] } ? U : readonly AbiParameter[], >(...expectedArgs: AbiParametersToPrimitiveTypes<TInputs>): ChainableAssertion /** * Chains with toCallContractFunction to assert function 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) * .toCallContractFunction(client, tokenContract, 'transfer') * .withFunctionNamedArgs({ * to: recipient, * value: 1000n * }) * * // Empty object matches any call to this function * await expect(txHash) * .toCallContractFunction(client, tokenContract, 'transfer') * .withFunctionNamedArgs({}) * ``` * * @see {@link withFunctionArgs} for positional argument matching */ withFunctionNamedArgs< TInputs extends readonly AbiParameter[] = ExtractAbiFunction<TAbi, TFunctionName> extends { inputs: infer U extends readonly AbiParameter[] } ? U : readonly AbiParameter[], >(expectedArgs: Partial<AbiInputsToNamedArgs<TInputs>>): ChainableAssertion }