|
1 | 1 | import {Contract, providers, utils} from 'ethers';
|
| 2 | +import {keccak256, toUtf8Bytes} from 'ethers/lib/utils'; |
2 | 3 | import {callPromise} from '../call-promise';
|
3 | 4 | import {waitForPendingTransaction} from './misc/transaction';
|
4 | 5 | import {supportWithArgs} from './withArgs';
|
5 | 6 | import {supportWithNamedArgs} from './withNamedArgs';
|
6 | 7 |
|
7 | 8 | export function supportEmit(Assertion: Chai.AssertionStatic) {
|
8 |
| - const filterLogsWithTopics = (logs: providers.Log[], topic: any, contractAddress: string) => |
| 9 | + const filterLogsWithTopics = (logs: providers.Log[], topic: any, contractAddress?: string) => |
9 | 10 | logs.filter((log) => log.topics.includes(topic))
|
10 |
| - .filter((log) => log.address && log.address.toLowerCase() === contractAddress.toLowerCase()); |
| 11 | + .filter((log) => |
| 12 | + log.address && |
| 13 | + (contractAddress === undefined || log.address.toLowerCase() === contractAddress.toLowerCase() |
| 14 | + )); |
11 | 15 |
|
12 |
| - Assertion.addMethod('emit', function (this: any, contract: Contract, eventName: string) { |
| 16 | + const assertEmit = (assertion: any, frag: utils.EventFragment, isNegated: boolean, from?: string) => { |
| 17 | + const topic = keccak256(toUtf8Bytes(frag.format())); |
| 18 | + const receipt: providers.TransactionReceipt = assertion.txReceipt; |
| 19 | + assertion.args = filterLogsWithTopics(receipt.logs, topic, from); |
| 20 | + const isCurrentlyNegated = assertion.__flags.negate === true; |
| 21 | + assertion.__flags.negate = isNegated; |
| 22 | + assertion.assert(assertion.args.length > 0, |
| 23 | + `Expected event "${frag.name}" to be emitted, but it wasn't`, |
| 24 | + `Expected event "${frag.name}" NOT to be emitted, but it was` |
| 25 | + ); |
| 26 | + assertion.__flags.negate = isCurrentlyNegated; |
| 27 | + }; |
| 28 | + |
| 29 | + Assertion.addMethod('emit', function (this: any, contractOrEventSig: Contract|string, eventName?: string) { |
13 | 30 | if (typeof this._obj === 'string') {
|
| 31 | + if (typeof contractOrEventSig === 'string') { |
| 32 | + throw new Error('The emit by event signature matcher must be called on a transaction'); |
| 33 | + } |
14 | 34 | // Handle specific case of using transaction hash to specify transaction. Done for backwards compatibility.
|
15 |
| - this.callPromise = waitForPendingTransaction(this._obj, contract.provider) |
| 35 | + this.callPromise = waitForPendingTransaction(this._obj, contractOrEventSig.provider) |
16 | 36 | .then(txReceipt => {
|
17 | 37 | this.txReceipt = txReceipt;
|
18 | 38 | });
|
19 | 39 | } else {
|
20 | 40 | callPromise(this);
|
21 | 41 | }
|
22 | 42 | const isNegated = this.__flags.negate === true;
|
23 |
| - this.callPromise = this.callPromise |
24 |
| - .then(() => { |
25 |
| - if (!('txReceipt' in this)) { |
26 |
| - throw new Error('The emit matcher must be called on a transaction'); |
| 43 | + this.callPromise = this.callPromise.then(() => { |
| 44 | + if (!('txReceipt' in this)) { |
| 45 | + throw new Error('The emit matcher must be called on a transaction'); |
| 46 | + } |
| 47 | + let eventFragment: utils.EventFragment | undefined; |
| 48 | + if (typeof contractOrEventSig === 'string') { |
| 49 | + try { |
| 50 | + eventFragment = utils.EventFragment.from(contractOrEventSig); |
| 51 | + } catch (e) { |
| 52 | + throw new Error(`Invalid event signature: "${contractOrEventSig}"`); |
27 | 53 | }
|
28 |
| - const receipt: providers.TransactionReceipt = this.txReceipt; |
29 |
| - let eventFragment: utils.EventFragment | undefined; |
| 54 | + assertEmit(this, eventFragment, isNegated); |
| 55 | + } else if (eventName) { |
30 | 56 | try {
|
31 |
| - eventFragment = contract.interface.getEvent(eventName); |
| 57 | + eventFragment = contractOrEventSig.interface.getEvent(eventName); |
32 | 58 | } catch (e) {
|
33 |
| - // ignore error |
| 59 | + // ignore error |
34 | 60 | }
|
35 | 61 | if (eventFragment === undefined) {
|
36 | 62 | this.assert(
|
37 |
| - isNegated, |
| 63 | + this.__flags.negate, |
38 | 64 | `Expected event "${eventName}" to be emitted, but it doesn't` +
|
39 |
| - ' exist in the contract. Please make sure you\'ve compiled' + |
40 |
| - ' its latest version before running the test.', |
| 65 | + ' exist in the contract. Please make sure you\'ve compiled' + |
| 66 | + ' its latest version before running the test.', |
41 | 67 | `WARNING: Expected event "${eventName}" NOT to be emitted.` +
|
42 |
| - ' The event wasn\'t emitted because it doesn\'t' + |
43 |
| - ' exist in the contract. Please make sure you\'ve compiled' + |
44 |
| - ' its latest version before running the test.', |
| 68 | + ' The event wasn\'t emitted because it doesn\'t' + |
| 69 | + ' exist in the contract. Please make sure you\'ve compiled' + |
| 70 | + ' its latest version before running the test.', |
45 | 71 | eventName,
|
46 | 72 | ''
|
47 | 73 | );
|
48 | 74 | return;
|
49 | 75 | }
|
| 76 | + assertEmit(this, eventFragment, isNegated, contractOrEventSig.address); |
| 77 | + |
| 78 | + this.contract = contractOrEventSig; |
| 79 | + } else { |
| 80 | + throw new Error('The emit matcher must be called with a contract and an event name or an event signature'); |
| 81 | + } |
| 82 | + }); |
50 | 83 |
|
51 |
| - const topic = contract.interface.getEventTopic(eventFragment); |
52 |
| - this.args = filterLogsWithTopics(receipt.logs, topic, contract.address); |
53 |
| - // As this callback will be resolved after the chain of matchers is finished, we need to |
54 |
| - // know if the matcher has been negated or not. To simulate chai behaviour, we keep track of whether |
55 |
| - // the matcher has been negated or not and set the internal chai flag __flags.negate to the same value. |
56 |
| - // After the assertion is finished, we set the flag back to original value to not affect other assertions. |
57 |
| - const isCurrentlyNegated = this.__flags.negate === true; |
58 |
| - this.__flags.negate = isNegated; |
59 |
| - this.assert(this.args.length > 0, |
60 |
| - `Expected event "${eventName}" to be emitted, but it wasn't`, |
61 |
| - `Expected event "${eventName}" NOT to be emitted, but it was` |
62 |
| - ); |
63 |
| - this.__flags.negate = isCurrentlyNegated; |
64 |
| - }); |
65 | 84 | this.then = this.callPromise.then.bind(this.callPromise);
|
66 | 85 | this.catch = this.callPromise.catch.bind(this.callPromise);
|
67 |
| - this.contract = contract; |
68 | 86 | this.eventName = eventName;
|
69 | 87 | this.txMatcher = 'emit';
|
70 | 88 | return this;
|
|
0 commit comments