Description
Subject of the issue
When enabling the utility enableAutoDestroy
, everything works as expected until a test mounts a component that fails to render (an error occurs in the template for instance).
When it happens, the tests stops, and the afterEach
hook is calls, triggering the auto wrapper destroy from the utility.
But wrapper.destroy()
throws an exception because the component has failed to render (not sure why though).
So the wrapper isn't destroyed and is kept in the list of tracked wrappers to destroy.
Then, every tests after this crash will fail because the list of wrappers has not been emptied!
Maybe it will be clearer with a code sample:
// vue-test-utils/packages/test-utils/src/auto-destroy.js
hook(function () {
wrapperInstances.forEach(function (wrapper) {
if (wrapper.vm || wrapper.isFunctionalComponent) {
// **throws an exception (error in render template) that is not catch**
wrapper.destroy();
}
});
// **will never be executed because of the exception**
wrapperInstances.length = 0;
});
So on the next test - that is supposed to pass - wrapperInstances
still has the previous wrapper that should have been destroyed:
wrapperInstances === [{ /* wrapper failing to destroy */ }, { /* next wrapper that could be destroyed */}]
So on after hook run, it will try again to destroy the blocked wrapper, and fail again and the following wrappers are never destroyed
Steps to reproduce
Configure the auto destroy on the afterEach
hook in the global jest setup.
// jest.setup.js
enableAutoDestroy(afterEach)
Then, create a test suite with:
- the first test fails to render a component's template
- the second test works
Run the tests, and both will fail even though they pass when run individually.
Expected behaviour
Only the first test may fail.
Actual behaviour
All the tests after the failing one are impacted and fail.
Possible Solution
The solution would be to add a try / catch around the wrapper.destroy()
in the utility, so that even if one destroy fails, it still flush the tracked wrapper array, and we don't end up with a growing list of wrappers to destroy, that will never be destroyed.
// vue-test-utils/packages/test-utils/src/auto-destroy.js
hook(function () {
wrapperInstances.forEach(function (wrapper) {
if (wrapper.vm || wrapper.isFunctionalComponent) {
try {
wrapper.destroy();
} catch (err) {
// **Print a warning? Remove the wrapper from the list?**
}
}
});
// **will be executed even if a destroy has failed**
wrapperInstances.length = 0;
});