Skip to content

Commit

Permalink
Chaining emit matchers (#692)
Browse files Browse the repository at this point in the history
* Collect matcher promises

* Add tests for wrong args

* Fix negating behaviour

* Comment

* Lint

* Add revert messages
  • Loading branch information
yivlad committed Apr 12, 2022
1 parent ffb59e3 commit 501351f
Show file tree
Hide file tree
Showing 2 changed files with 145 additions and 15 deletions.
27 changes: 19 additions & 8 deletions waffle-chai/src/matchers/emit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export function supportEmit(Assertion: Chai.AssertionStatic) {

Assertion.addMethod('emit', function (this: any, contract: Contract, eventName: string) {
const tx = this._obj;
const isNegated = this.__flags.negate === true;
const derivedPromise = waitForPendingTransaction(tx, contract.provider)
.then((receipt: providers.TransactionReceipt) => {
let eventFragment: utils.EventFragment | undefined;
Expand All @@ -16,10 +17,7 @@ export function supportEmit(Assertion: Chai.AssertionStatic) {
} catch (e) {
// ignore error
}

if (eventFragment === undefined) {
const isNegated = this.__flags.negate === true;

this.assert(
isNegated,
`Expected event "${eventName}" to be emitted, but it doesn't` +
Expand All @@ -37,14 +35,22 @@ export function supportEmit(Assertion: Chai.AssertionStatic) {

const topic = contract.interface.getEventTopic(eventFragment);
this.logs = filterLogsWithTopics(receipt.logs, topic, contract.address);
// As this callback will be resolved after the chain of matchers is finished, we need to
// know if the matcher has been negated or not. To simulate chai behaviour, we keep track of whether
// the matcher has been negated or not and set the internal chai flag __flags.negate to the same value.
// After the assertion is finished, we set the flag back to original value to not affect other assertions.
const isCurrentlyNegated = this.__flags.negate === true;
this.__flags.negate = isNegated;
this.assert(this.logs.length > 0,
`Expected event "${eventName}" to be emitted, but it wasn't`,
`Expected event "${eventName}" NOT to be emitted, but it was`
);
this.__flags.negate = isCurrentlyNegated;
});
this.then = derivedPromise.then.bind(derivedPromise);
this.catch = derivedPromise.catch.bind(derivedPromise);
this.promise = derivedPromise;
this.promises = ('promises' in this) ? [derivedPromise, ...this.promises] : [derivedPromise];
this.promise = Promise.all(this.promises);
this.then = this.promise.then.bind(this.promise);
this.catch = this.promise.catch.bind(this.promise);
this.contract = contract;
this.eventName = eventName;
return this;
Expand Down Expand Up @@ -97,8 +103,13 @@ export function supportEmit(Assertion: Chai.AssertionStatic) {
const derivedPromise = this.promise.then(() => {
tryAssertArgsArraysEqual(this, expectedArgs, this.logs);
});
this.then = derivedPromise.then.bind(derivedPromise);
this.catch = derivedPromise.catch.bind(derivedPromise);
if (!('promises' in this)) {
throw new Error('withArgs() must be used after emit()');
}
this.promises = [derivedPromise, ...this.promises];
this.promise = Promise.all(this.promises);
this.then = this.promise.then.bind(this.promise);
this.catch = this.promise.catch.bind(this.promise);
return this;
});
}
133 changes: 126 additions & 7 deletions waffle-chai/test/matchers/eventsTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -270,13 +270,6 @@ export const eventsTest = (provider: MockProvider) => {
);
});

it('Event emitted in one contract but not in the other', async () => {
const differentEvents = await factory.deploy();
await expect(events.emitOne())
.to.emit(events, 'One')
.and.not.to.emit(differentEvents, 'One');
});

it('Emit event multiple times with different args', async () => {
await expect(events.emitOneMultipleTimes())
.to.emit(events, 'One')
Expand Down Expand Up @@ -313,4 +306,130 @@ export const eventsTest = (provider: MockProvider) => {
const tx = await events.emitOne();
await expect(tx.hash).to.emit(events, 'One');
});

describe('Chaining matchers', () => {
it('Both emitted and caught', async () => {
const tx = await events.emitBoth();
await expect(tx)
.to.emit(events, 'One')
.to.emit(events, 'Two');
});

it('One emitted, expecting one then two - fail', async () => {
const tx = await events.emitOne();
await expect(
expect(tx)
.to.emit(events, 'One')
.to.emit(events, 'Two')
).to.be.eventually.rejectedWith(
'Expected event "Two" to be emitted, but it wasn\'t'
);
});

it('One emitted, expecting two then one - fail', async () => {
const tx = await events.emitOne();
await expect(
expect(tx)
.to.emit(events, 'Two')
.to.emit(events, 'One')
).to.be.eventually.rejectedWith(
'Expected event "Two" to be emitted, but it wasn\'t'
);
});

it('Both emitted and caught with args', async () => {
const tx = await events.emitBoth();
await expect(tx)
.to.emit(events, 'One').withArgs(
1,
'One',
'0x0000000000000000000000000000000000000000000000000000000000000001'
)
.to.emit(events, 'Two').withArgs(
2,
'Two'
);
});

it('One emitted, expecting one then two with args - fail', async () => {
const tx = await events.emitOne();
await expect(
expect(tx)
.to.emit(events, 'One').withArgs(
1,
'One',
'0x00cfbbaf7ddb3a1476767101c12a0162e241fbad2a0162e2410cfbbaf7162123'
)
.to.emit(events, 'Two').withArgs(
2,
'Two'
)
).to.be.eventually.rejectedWith(
'Expected event "Two" to be emitted, but it wasn\'t'
);
});

it('One emitted, expecting two then one with args - fail', async () => {
const tx = await events.emitOne();
await expect(
expect(tx)
.to.emit(events, 'Two').withArgs(
2,
'Two'
)
.to.emit(events, 'One').withArgs(
1,
'One',
'0x00cfbbaf7ddb3a1476767101c12a0162e241fbad2a0162e2410cfbbaf7162123'
)
).to.be.eventually.rejectedWith(
'Expected event "Two" to be emitted, but it wasn\'t'
);
});

it('Wrong args, expecting one then two - fail', async () => {
const tx = await events.emitBoth();
await expect(
expect(tx)
.to.emit(events, 'One').withArgs(
1,
'One',
'0x00cfbbaf7ddb3a1476767101c12a0162e241fbad2a0162e2410cfbbaf7162123'
)
.to.emit(events, 'Two').withArgs(
2,
'Two'
)
).to.be.eventually.rejectedWith(
'expected \'0x0000000000000000000000000000000000000000000000000000000000000001\'' +
' to equal \'0x00cfbbaf7ddb3a1476767101c12a0162e241fbad2a0162e2410cfbbaf7162123\''
);
});

it('Wrong args, expecting two then one - fail', async () => {
const tx = await events.emitBoth();
await expect(
expect(tx)
.to.emit(events, 'Two').withArgs(
2,
'Two'
)
.to.emit(events, 'One').withArgs(
1,
'One',
'0x00cfbbaf7ddb3a1476767101c12a0162e241fbad2a0162e2410cfbbaf7162123'
)
).to.be.eventually.rejectedWith(
'expected \'0x0000000000000000000000000000000000000000000000000000000000000001\'' +
' to equal \'0x00cfbbaf7ddb3a1476767101c12a0162e241fbad2a0162e2410cfbbaf7162123\''
);
});

it('Event emitted in one contract but not in the other', async () => {
const differentEvents = await factory.deploy();
await expect(events.emitOne())
.to.emit(events, 'One')
.and.not.to.emit(differentEvents, 'One');
});
});
};

0 comments on commit 501351f

Please sign in to comment.