Expect calls custom `inspect()` function unexpectedly
Describe the bug
Any object that has an inspect() function will be called when passed to expect(). If this function happens to expect an argument, for example inspect(fn: () => void) then the test will fail for the wrong reason. This doesn't seem to mentioned in the api and means you need to guard around this currently by doing more targeted tests.
Reproduction
Using vitest@^0.34.0 (including beta 1.0.0 releases) the following test:
class Example {
public inspect(fn: () => void) {
fn();
}
}
it('custom inspect function', (): void => {
expect(
new Example(),
).toStrictEqual(undefined);
});
Will output as expected:
FAIL src/example.test.ts > custom inspect function
TypeError: fn is not a function
❯ Example.inspect src/event/service/foo.test.ts:3:5
1| class Example {
2| public inspect(fn: () => void) {
3| fn();
| ^
4| }
5| }
❯ inspectCustom node_modules/loupe/loupe.js:793:20
❯ Object.inspect node_modules/loupe/loupe.js:827:20
This appears to be down to loupe but again, this is unexpected.
System Info
System:
OS: macOS 14.0
CPU: (12) arm64 Apple M2 Max
Memory: 2.27 GB / 64.00 GB
Shell: 5.9 - /bin/zsh
Binaries:
Node: 18.18.1 - ~/.nvm/versions/node/v18.18.1/bin/node
Yarn: 1.22.19 - ~/.nvm/versions/node/v18.18.1/bin/yarn
npm: 10.2.0 - ~/.nvm/versions/node/v18.18.1/bin/npm
pnpm: 8.9.0 - ~/.nvm/versions/node/v18.18.1/bin/pnpm
Browsers:
Chrome: 119.0.6045.123
Safari: 17.0
npmPackages:
@vitest/coverage-v8: ^0.34.5 => 0.34.6
vitest: ^0.34.5 => 0.34.6
Used Package Manager
npm
Validations
- [X] Follow our Code of Conduct
- [X] Read the Contributing Guidelines.
- [X] Read the docs.
- [X] Check that there isn't already an issue that reports the same bug to avoid creating a duplicate.
- [X] Check that this is a concrete bug. For Q&A open a GitHub Discussion or join our Discord Chat Server.
- [X] The provided reproduction is a minimal reproducible example of the bug.
As mentioned this looks to be loupe, but potentially a configuration thing that can be disabled or made configurable. The stack frames in the reproduction relate to this line https://github.com/chaijs/loupe/blob/main/src/index.ts#L139C7-L139C20 which suggests it can be disabled with a configuration option.
We should probably just disable it by default on our side. PR welcome.
I will see what I can do later today 👍🏻
To be clear, where should I cut from? The 0.34 branch?
I gave this a go and the contributing guide wasn't getting me anywhere, things were erroring. So I decided to try go in blind and discovered the following which suggests we cannot turn of the custom inspect for this specific case:
- My example above is ran
- This code is triggered https://github.com/vitest-dev/vitest/blob/bedf54ad06f7d7707f220c0711693320c36001ea/packages/expect/src/jest-expect.ts#L442
- Which calls into
utils.getMessage()which calls intochai - Specifically
objDisplay()which callsinspect()fromloupe, issue is there is no way to set options
This appears to be the only code path that triggers inspect() other than the custom wrapper in the @vitest/utils package. I am at a loss with trying to fix this without writing a custom utils.getMessage
There is a test.chaiConfig property in the config that can influence inspect options I think, but I'm not sure.
I followed the stack through these functions (now with links to chai github):
The final inspect() is not loupe but a wrapper which looks like this:
function inspect(obj, showHidden, depth, colors) {
var options = {
colors: colors,
depth: (typeof depth === 'undefined' ? 2 : depth),
showHidden: showHidden,
truncate: config.truncateThreshold ? config.truncateThreshold : Infinity,
};
return loupe.inspect(obj, options);
}
So I don't think a chai config will take effect, unless its some magic stuff.