Skip to content

Commit

Permalink
Add ability to stub getEvents with specific filter args (#32)
Browse files Browse the repository at this point in the history
  • Loading branch information
DannyDelott authored Feb 16, 2024
1 parent 9993008 commit 593a286
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 13 deletions.
5 changes: 5 additions & 0 deletions .changeset/wet-eyes-arrive.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@delvtech/evm-client": patch
---

Add ability to stub events for dynamic filter args
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ describe('createCachedReadContract', () => {
transactionHash: '0x123abc',
},
];
contract.stubEvents('Transfer', stubbedEvents);
contract.stubEvents('Transfer', undefined, stubbedEvents);

const events = await cachedContract.getEvents('Transfer');
expect(events).toBe(stubbedEvents);
Expand Down
53 changes: 49 additions & 4 deletions packages/evm-client/src/contract/stubs/ReadContractStub.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,11 @@ describe('ReadContractStub', () => {
it('stubs the getEvents function', async () => {
const contract = new ReadContractStub(ERC20ABI);

// throws an error if you forget to stub the event your requesting
expect(contract.getEvents('Transfer')).rejects.toThrowError();

const stubbedEvents: Event<typeof ERC20ABI, 'Transfer'>[] = [
// Stub out the events when calling `getEvents` without any filter args
const stubbedAllEvents: Event<typeof ERC20ABI, 'Transfer'>[] = [
{
eventName: 'Transfer',
args: {
Expand All @@ -89,13 +91,56 @@ describe('ReadContractStub', () => {
data: '0x123abc',
transactionHash: '0x123abc',
},
{
eventName: 'Transfer',
args: {
from: ALICE,
to: BOB,
value: 100n,
},
blockNumber: 1n,
data: '0x123abc',
transactionHash: '0x123abc',
},
];
contract.stubEvents('Transfer', stubbedEvents);
contract.stubEvents('Transfer', undefined, stubbedAllEvents);

// Stub out the events when calling `getEvents` *with* filter args
const stubbedFilteredEvents: Event<typeof ERC20ABI, 'Transfer'>[] = [
{
eventName: 'Transfer',
args: {
to: ALICE,
from: BOB,
value: 100n,
},
blockNumber: 1n,
data: '0x123abc',
transactionHash: '0x123abc',
},
];
contract.stubEvents(
'Transfer',
{ filter: { from: BOB } },
stubbedFilteredEvents,
);

// getting events without any filter args should return the stub that was
// specified without any filter args
const events = await contract.getEvents('Transfer');
expect(events).toBe(stubbedEvents);

expect(events).toBe(stubbedAllEvents);
const stub = contract.getEventsStub('Transfer');
expect(stub?.callCount).toBe(1);

// getting events with filter args should return the stub that was specified
// *with* filter args
const filteredEvents = await contract.getEvents('Transfer', {
filter: { from: BOB },
});
expect(filteredEvents).toBe(stubbedFilteredEvents);
const filteredStub = contract.getEventsStub('Transfer', {
filter: { from: BOB },
});
expect(filteredStub?.callCount).toBe(1);
});
});
26 changes: 18 additions & 8 deletions packages/evm-client/src/contract/stubs/ReadContractStub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ export class ReadContractStub<TAbi extends Abi = Abi>
async getEvents<TEventName extends EventName<TAbi>>(
...[eventName, options]: ContractGetEventsArgs<TAbi, TEventName>
): Promise<Event<TAbi, TEventName>[]> {
const stub = this.getEventsStub(eventName);
const stub = this.getEventsStub(eventName, options);
if (!stub) {
throw new Error(
`Called getEvents for ${eventName} on a stubbed contract without a return value. The function must be stubbed first:\n\tcontract.stubEvents("${eventName}", value)`,
Expand Down Expand Up @@ -162,18 +162,17 @@ export class ReadContractStub<TAbi extends Abi = Abi>
* Stubs the return value for a given event name when `getEvents` is called
* with that event name. This method overrides any previously stubbed values
* for the same event.
*
* *Note: The stub doesn't account for dynamic values based on provided
* arguments/options.*
*/
stubEvents<TEventName extends EventName<TAbi>>(
eventName: TEventName,
args: ContractGetEventsOptions<TAbi, TEventName> | undefined,
value: Event<TAbi, TEventName>[],
): void {
if (this.eventsStubMap.has(eventName)) {
this.getEventsStub(eventName)!.resolves(value);
const stubKey = stringify({ eventName, args });
if (this.eventsStubMap.has(stubKey)) {
this.getEventsStub(eventName, args)!.resolves(value as any);
} else {
this.eventsStubMap.set(eventName, stub().resolves(value) as any);
this.eventsStubMap.set(stubKey, stub().resolves(value) as any);
}
}

Expand Down Expand Up @@ -209,8 +208,10 @@ export class ReadContractStub<TAbi extends Abi = Abi>
*/
getEventsStub<TEventName extends EventName<TAbi>>(
eventName: TEventName,
args?: ContractGetEventsOptions<TAbi, TEventName> | undefined,
): EventsStub<TAbi, TEventName> | undefined {
return this.eventsStubMap.get(eventName) as
const stubKey = stringify({ eventName, args });
return this.eventsStubMap.get(stubKey) as
| EventsStub<TAbi, TEventName>
| undefined;
}
Expand Down Expand Up @@ -270,3 +271,12 @@ type SimulateWriteStub<
],
Promise<FunctionReturn<TAbi, TFunctionName>>
>;

function stringify(obj: Record<any, any>) {
// simple non-recursive stringify replacer for bigints
function replacer(_: any, v: any) {
return typeof v === 'bigint' ? v.toString() : v;
}

return JSON.stringify(obj, replacer);
}

0 comments on commit 593a286

Please sign in to comment.