Skip to content

Commit bbb8ba5

Browse files
committed
Adjust emit with args
1 parent 8d27358 commit bbb8ba5

File tree

2 files changed

+110
-93
lines changed

2 files changed

+110
-93
lines changed

packages/hardhat-chai-matchers/src/internal/emit.ts

+15-92
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,23 @@ import type { AssertWithSsfi, Ssfi } from "../utils";
44

55
import { AssertionError } from "chai";
66
import util from "util";
7-
import ordinal from "ordinal";
87

98
import { buildAssert } from "../utils";
109
import { ASSERTION_ABORTED, EMIT_MATCHER } from "./constants";
1110
import { HardhatChaiMatchersAssertionError } from "./errors";
1211
import { assertIsNotNull, preventAsyncMatcherChaining } from "./utils";
1312

1413
type EventFragment = EthersT.EventFragment;
15-
type Interface = EthersT.Interface;
1614
type Provider = EthersT.Provider;
15+
type AssertArgsArraysEqual = (
16+
context: any,
17+
Assertion: Chai.AssertionStatic,
18+
chaiUtils: Chai.ChaiUtils,
19+
expectedArgs: any[],
20+
log: any,
21+
assert: AssertWithSsfi,
22+
ssfi: Ssfi
23+
) => void;
1724

1825
export const EMIT_CALLED = "emitAssertionCalled";
1926

@@ -130,7 +137,8 @@ export async function emitWithArgs(
130137
Assertion: Chai.AssertionStatic,
131138
chaiUtils: Chai.ChaiUtils,
132139
expectedArgs: any[],
133-
ssfi: Ssfi
140+
ssfi: Ssfi,
141+
assertArgsArraysEqual: AssertArgsArraysEqual
134142
) {
135143
const negated = false; // .withArgs cannot be negated
136144
const assert = buildAssert(negated, ssfi);
@@ -142,95 +150,9 @@ export async function emitWithArgs(
142150
expectedArgs,
143151
context.logs,
144152
assert,
145-
ssfi
146-
);
147-
}
148-
149-
function assertArgsArraysEqual(
150-
context: any,
151-
Assertion: Chai.AssertionStatic,
152-
chaiUtils: Chai.ChaiUtils,
153-
expectedArgs: any[],
154-
log: any,
155-
assert: AssertWithSsfi,
156-
ssfi: Ssfi
157-
) {
158-
const ethers = require("ethers") as typeof EthersT;
159-
const parsedLog = (
160-
chaiUtils.flag(context, "contract").interface as Interface
161-
).parseLog(log);
162-
assertIsNotNull(parsedLog, "parsedLog");
163-
const actualArgs = parsedLog.args;
164-
const eventName = chaiUtils.flag(context, "eventName");
165-
assert(
166-
actualArgs.length === expectedArgs.length,
167-
`Expected "${eventName}" event to have ${expectedArgs.length} argument(s), but it has ${actualArgs.length}`
153+
ssfi,
154+
assertArgsArraysEqual
168155
);
169-
for (const [index, expectedArg] of expectedArgs.entries()) {
170-
const actualArg = actualArgs[index];
171-
if (typeof expectedArg === "function") {
172-
const errorPrefix = `The predicate for the ${ordinal(
173-
index + 1
174-
)} event argument`;
175-
try {
176-
if (expectedArg(actualArg) === true) continue;
177-
} catch (e: any) {
178-
assert(
179-
false,
180-
`${errorPrefix} threw when called: ${e.message}`
181-
// no need for a negated message, since we disallow mixing .not. with
182-
// .withArgs
183-
);
184-
}
185-
assert(
186-
false,
187-
`${errorPrefix} did not return true `
188-
// no need for a negated message, since we disallow mixing .not. with
189-
// .withArgs
190-
);
191-
} else if (expectedArg instanceof Uint8Array) {
192-
new Assertion(actualArg, undefined, ssfi, true).equal(
193-
ethers.hexlify(expectedArg)
194-
);
195-
} else if (
196-
expectedArg?.length !== undefined &&
197-
typeof expectedArg !== "string"
198-
) {
199-
const expectedLength = expectedArg.length;
200-
const actualLength = actualArg.length;
201-
assert(
202-
expectedLength === actualLength,
203-
`Expected the ${ordinal(
204-
index + 1
205-
)} argument of the "${eventName}" event to have ${expectedLength} ${
206-
expectedLength === 1 ? "element" : "elements"
207-
}, but it has ${actualLength}`
208-
);
209-
210-
for (let j = 0; j < expectedArg.length; j++) {
211-
new Assertion(actualArg[j], undefined, ssfi, true).equal(
212-
expectedArg[j]
213-
);
214-
}
215-
} else {
216-
if (actualArg.hash !== undefined && actualArg._isIndexed === true) {
217-
new Assertion(actualArg.hash, undefined, ssfi, true).to.not.equal(
218-
expectedArg,
219-
"The actual value was an indexed and hashed value of the event argument. The expected value provided to the assertion should be the actual event argument (the pre-image of the hash). You provided the hash itself. Please supply the actual event argument (the pre-image of the hash) instead."
220-
);
221-
const expectedArgBytes = ethers.isHexString(expectedArg)
222-
? ethers.getBytes(expectedArg)
223-
: ethers.toUtf8Bytes(expectedArg);
224-
const expectedHash = ethers.keccak256(expectedArgBytes);
225-
new Assertion(actualArg.hash, undefined, ssfi, true).to.equal(
226-
expectedHash,
227-
`The actual value was an indexed and hashed value of the event argument. The expected value provided to the assertion was hashed to produce ${expectedHash}. The actual hash and the expected hash did not match`
228-
);
229-
} else {
230-
new Assertion(actualArg, undefined, ssfi, true).equal(expectedArg);
231-
}
232-
}
233-
}
234156
}
235157

236158
const tryAssertArgsArraysEqual = (
@@ -240,7 +162,8 @@ const tryAssertArgsArraysEqual = (
240162
expectedArgs: any[],
241163
logs: any[],
242164
assert: AssertWithSsfi,
243-
ssfi: Ssfi
165+
ssfi: Ssfi,
166+
assertArgsArraysEqual: AssertArgsArraysEqual
244167
) => {
245168
if (logs.length === 1)
246169
return assertArgsArraysEqual(

packages/hardhat-chai-matchers/src/internal/withArgs.ts

+95-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import type EthersT from "ethers";
22
import { AssertionError } from "chai";
33

44
import { isBigNumber, normalizeToBigInt } from "hardhat/common";
5+
import ordinal from "ordinal";
6+
import { AssertWithSsfi, Ssfi } from "../utils";
57
import { ASSERTION_ABORTED } from "./constants";
68

79
import { emitWithArgs, EMIT_CALLED } from "./emit";
@@ -10,6 +12,10 @@ import {
1012
REVERTED_WITH_CUSTOM_ERROR_CALLED,
1113
} from "./reverted/revertedWithCustomError";
1214

15+
import { assertIsNotNull } from "./utils";
16+
17+
type Interface = EthersT.Interface;
18+
1319
/**
1420
* A predicate for use with .withArgs(...), to induce chai to accept any value
1521
* as a positive match with the argument.
@@ -69,7 +75,8 @@ export function supportWithArgs(
6975
Assertion,
7076
chaiUtils,
7177
resolvedExpectedArgs,
72-
onSuccess
78+
onSuccess,
79+
assertArgsArraysEqual
7380
);
7481
} else {
7582
return revertedWithCustomErrorWithArgs(
@@ -127,3 +134,90 @@ function validateInput(
127134
throw e;
128135
}
129136
}
137+
138+
function assertArgsArraysEqual(
139+
context: any,
140+
Assertion: Chai.AssertionStatic,
141+
chaiUtils: Chai.ChaiUtils,
142+
expectedArgs: any[],
143+
log: any,
144+
assert: AssertWithSsfi,
145+
ssfi: Ssfi
146+
) {
147+
const ethers = require("ethers") as typeof EthersT;
148+
const parsedLog = (
149+
chaiUtils.flag(context, "contract").interface as Interface
150+
).parseLog(log);
151+
assertIsNotNull(parsedLog, "parsedLog");
152+
const actualArgs = parsedLog.args;
153+
const eventName = chaiUtils.flag(context, "eventName");
154+
assert(
155+
actualArgs.length === expectedArgs.length,
156+
`Expected "${eventName}" event to have ${expectedArgs.length} argument(s), but it has ${actualArgs.length}`
157+
);
158+
for (const [index, expectedArg] of expectedArgs.entries()) {
159+
const actualArg = actualArgs[index];
160+
if (typeof expectedArg === "function") {
161+
const errorPrefix = `The predicate for the ${ordinal(
162+
index + 1
163+
)} event argument`;
164+
try {
165+
if (expectedArg(actualArg) === true) continue;
166+
} catch (e: any) {
167+
assert(
168+
false,
169+
`${errorPrefix} threw when called: ${e.message}`
170+
// no need for a negated message, since we disallow mixing .not. with
171+
// .withArgs
172+
);
173+
}
174+
assert(
175+
false,
176+
`${errorPrefix} did not return true `
177+
// no need for a negated message, since we disallow mixing .not. with
178+
// .withArgs
179+
);
180+
} else if (expectedArg instanceof Uint8Array) {
181+
new Assertion(actualArg, undefined, ssfi, true).equal(
182+
ethers.hexlify(expectedArg)
183+
);
184+
} else if (
185+
expectedArg?.length !== undefined &&
186+
typeof expectedArg !== "string"
187+
) {
188+
const expectedLength = expectedArg.length;
189+
const actualLength = actualArg.length;
190+
assert(
191+
expectedLength === actualLength,
192+
`Expected the ${ordinal(
193+
index + 1
194+
)} argument of the "${eventName}" event to have ${expectedLength} ${
195+
expectedLength === 1 ? "element" : "elements"
196+
}, but it has ${actualLength}`
197+
);
198+
199+
for (let j = 0; j < expectedArg.length; j++) {
200+
new Assertion(actualArg[j], undefined, ssfi, true).equal(
201+
expectedArg[j]
202+
);
203+
}
204+
} else {
205+
if (actualArg.hash !== undefined && actualArg._isIndexed === true) {
206+
new Assertion(actualArg.hash, undefined, ssfi, true).to.not.equal(
207+
expectedArg,
208+
"The actual value was an indexed and hashed value of the event argument. The expected value provided to the assertion should be the actual event argument (the pre-image of the hash). You provided the hash itself. Please supply the actual event argument (the pre-image of the hash) instead."
209+
);
210+
const expectedArgBytes = ethers.isHexString(expectedArg)
211+
? ethers.getBytes(expectedArg)
212+
: ethers.toUtf8Bytes(expectedArg);
213+
const expectedHash = ethers.keccak256(expectedArgBytes);
214+
new Assertion(actualArg.hash, undefined, ssfi, true).to.equal(
215+
expectedHash,
216+
`The actual value was an indexed and hashed value of the event argument. The expected value provided to the assertion was hashed to produce ${expectedHash}. The actual hash and the expected hash did not match`
217+
);
218+
} else {
219+
new Assertion(actualArg, undefined, ssfi, true).equal(expectedArg);
220+
}
221+
}
222+
}
223+
}

0 commit comments

Comments
 (0)