Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Infinite loop on uncaught error when using createLocalVue() #1768

Closed
fearhq opened this issue Jan 11, 2021 · 7 comments
Closed

Infinite loop on uncaught error when using createLocalVue() #1768

fearhq opened this issue Jan 11, 2021 · 7 comments

Comments

@fearhq
Copy link

fearhq commented Jan 11, 2021

Subject of the issue

There is an infinite loop in the vue-test-utils error handler when using createLocalVue more than once. Everything works correctly for the first test, but subsequent tests that use their own local vue instance will cause massive noise in the console
I have tested this with version 1.1.2

Steps to reproduce

  • Create a component with an uncaught error
  • Mount the component using a distinct local vue in beforeEach (createLocalVue)
  • Have more than one test

Expected behaviour

Each test should run, and produce only the errors triggered by the code

Actual behaviour

You should see an error similar to this in the console:

  console.error node_modules/vue/dist/vue.runtime.common.dev.js:621
    [Vue warn]: Error in config.errorHandler: "RangeError: Maximum call stack size exceeded"

  console.error node_modules/vue/dist/vue.runtime.common.dev.js:1884
    RangeError: Maximum call stack size exceeded
        at Array.push (<anonymous>)
        at getParent (...\node_modules\@vue\test-utils\dist\vue-test-utils.js:2799:17)
        at findAllParentInstances (...\node_modules\@vue\test-utils\dist\vue-test-utils.js:2805:3)
        at errorHandler (...\node_modules\@vue\test-utils\dist\vue-test-utils.js:2912:32)
        at ...\node_modules\@vue\test-utils\dist\vue-test-utils.js:2929:5
        at Array.forEach (<anonymous>)
        at errorHandler (...\node_modules\@vue\test-utils\dist\vue-test-utils.js:2928:26)
        at ...\node_modules\@vue\test-utils\dist\vue-test-utils.js:2929:5
        at Array.forEach (<anonymous>)
        at errorHandler (...\node_modules\@vue\test-utils\dist\vue-test-utils.js:2928:26)
        at ...\node_modules\@vue\test-utils\dist\vue-test-utils.js:2929:5
       (...)

Possible Solution

After debugging the code, it is apparent that the first call to createLocalVue() does not have an errorHandler set. Subsequent calls, however, have their error handler set to the one in vue-test-utils. Whenever the child component has an uncaught error, it will call the vue-test-utils handler, which will call the parent's (our local vue) handler. Since it is the same handler, with the same arguments, there is infinite recursion

There are many possible solutions, but a workaround is to simply pass an empty errorHandler function:

createLocalVue({ errorHandler: () => {}})
@lmiller1990
Copy link
Member

Can you post a snippet show exactly how to reproduce this?

@antoinerey
Copy link
Contributor

antoinerey commented Feb 5, 2021

Here is a minimal reproduction repository, based on Vue CLI and @vue/cli-plugin-unit-jest.


Reproduction repository

https://github.com/antoinerey/repro-issue-vue-test-utils-1768


Steps to reproduce

  1. Clone the repository (obviously).
  2. Install dependencies (yarn).
  3. Run the tests (yarn test:unit).

Output

Here is what you should see in your console.

 console.error node_modules/vue/dist/vue.runtime.common.dev.js:1884
    RangeError: Maximum call stack size exceeded
        at errorHandler (/Users/antoine/Code/repro-issue-vue-test-utils-1768/node_modules/@vue/test-utils/dist/vue-test-utils.js:2887:22)
        at /Users/antoine/Code/repro-issue-vue-test-utils-1768/node_modules/@vue/test-utils/dist/vue-test-utils.js:2913:5
        at Array.forEach (<anonymous>)
        at errorHandler (/Users/antoine/Code/repro-issue-vue-test-utils-1768/node_modules/@vue/test-utils/dist/vue-test-utils.js:2912:26)
        at /Users/antoine/Code/repro-issue-vue-test-utils-1768/node_modules/@vue/test-utils/dist/vue-test-utils.js:2913:5
        at Array.forEach (<anonymous>)
        at errorHandler (/Users/antoine/Code/repro-issue-vue-test-utils-1768/node_modules/@vue/test-utils/dist/vue-test-utils.js:2912:26)
        at /Users/antoine/Code/repro-issue-vue-test-utils-1768/node_modules/@vue/test-utils/dist/vue-test-utils.js:2913:5
        at Array.forEach (<anonymous>)
        at errorHandler (/Users/antoine/Code/repro-issue-vue-test-utils-1768/node_modules/@vue/test-utils/dist/vue-test-utils.js:2912:26)
        at /Users/antoine/Code/repro-issue-vue-test-utils-1768/node_modules/@vue/test-utils/dist/vue-test-utils.js:2913:5
        at Array.forEach (<anonymous>)
        at errorHandler (/Users/antoine/Code/repro-issue-vue-test-utils-1768/node_modules/@vue/test-utils/dist/vue-test-utils.js:2912:26)
        at /Users/antoine/Code/repro-issue-vue-test-utils-1768/node_modules/@vue/test-utils/dist/vue-test-utils.js:2913:5
        at Array.forEach (<anonymous>)
        at errorHandler (/Users/antoine/Code/repro-issue-vue-test-utils-1768/node_modules/@vue/test-utils/dist/vue-test-utils.js:2912:26)
        at /Users/antoine/Code/repro-issue-vue-test-utils-1768/node_modules/@vue/test-utils/dist/vue-test-utils.js:2913:5
        at Array.forEach (<anonymous>)
        at errorHandler (/Users/antoine/Code/repro-issue-vue-test-utils-1768/node_modules/@vue/test-utils/dist/vue-test-utils.js:2912:26)
        at /Users/antoine/Code/repro-issue-vue-test-utils-1768/node_modules/@vue/test-utils/dist/vue-test-utils.js:2913:5
        at Array.forEach (<anonymous>)
        at errorHandler (/Users/antoine/Code/repro-issue-vue-test-utils-1768/node_modules/@vue/test-utils/dist/vue-test-utils.js:2912:26)
        at /Users/antoine/Code/repro-issue-vue-test-utils-1768/node_modules/@vue/test-utils/dist/vue-test-utils.js:2913:5
        at Array.forEach (<anonymous>)

@antoinerey
Copy link
Contributor

A few more information:

  1. The issue was probably introduced in 1.1.0. It works as expected up until the version 1.0.5.
  2. According to the 1.1.0 release changelog, the issue was likely introduced with createLocalVue errorHandler Option #1670.

@lmiller1990
Copy link
Member

Good detective work. Would you like to make a PR to fix this?

@antoinerey
Copy link
Contributor

antoinerey commented Feb 10, 2021

Sure!

I've tried to pinpoint the exact root cause by reading the repository, but could not find it. I'll try again by cloning and running some tests in local, I'll get back to you. 👍

@antoinerey
Copy link
Contributor

antoinerey commented Feb 10, 2021

I've progressed a little bit, and here is the recap of what I found so far.


  1. First, I've created a test case reproducing the issue, and I've submitted a draft PR (fix(create-local-vue): fix max range stack size (fix #1768) #1786).
  2. The issue only arises when there is more than one test running. Everything works well when running a single test case. Note that this is also what I experienced on a private repository which consumes @vue/test-utils.

When running the first test case, both config.errorHandler and Vue.config.errorHandler are falsy:

config.errorHandler // undefined
Vue.config.errorHandler // null

However, when running the second test (which should run in complete isolation from the first one), we do not get the same values: Vue.config.errorHandler is set.

config.errorHandler // undefined
Vue.config.errorHandler // [Function: errorHandler]

This makes me think that the global Vue object is not completely reset between every test cases, and thus tests are polluted by previous ones.

So I've been investigating in this direction, and could not find the root cause yet. But I'm still working on it, hopefully I'll made progress. 🤞


Also note that updating _createLocalVue like so solves the issue, and makes every tests pass. However, I highly doubt this solution. I'm afraid it might break stuff I'm not aware of.

- instance.config.errorHandler = config.errorHandler || Vue.config.errorHandler
+ instance.config.errorHandler = config.errorHandler

@lmiller1990
Copy link
Member

Hmmm I think you are right about the problem relating to polluting the global Vue instance.

I am not sure if that fix is ideal or not, but if all the tests pass it's probably okay 🤔 we need to assume our test suite covers all our bases.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants