That is due to how Ethers dynamically handles transactions.
If you need to call a method that does not change the state of a smart contract (i.e. test1
and test3
), Ethers uses a call. Calls are used to read the smart contract's state, and they follow a specific execution path that leads to particular results or errors (https://docs.ethers.io/v5/api/utils/logger/#errors--call-exception).
In particular, the tx is not sent to the blockchain - it's a read, it's not needed - so the execution result is returned directly to the client.
In your case, test1
and test3
both return: Error: call revert exception; ...
When calling a method that can potentially change the blockchain's state, a tx is actually sent to the blockchain, it is mined, and then the result is read from the tx's receipt. There you can find the custom error.
And this is why only test2
reverts with Error: VM Exception while processing transaction: reverted with custom error 'aCustomError("BadHappened")'
If you want the 3rd test to pass, you can use:
await expect(rpContract.test3()).to.be.revertedWith("BadHappened")
Please note that to.be.revertedWith(myString)
searches for myString
inside the error, so all these check pass:
await expect(rpContract.test3()).to.be.revertedWith("Happened")
await expect(rpContract.test3()).to.be.revertedWith("Bad")
await expect(rpContract.test3()).to.be.revertedWith("B")