Vue-test-utils: $listeners is readonly error

Created on 15 Apr 2018  ·  16Comments  ·  Source: vuejs/vue-test-utils

Version

1.0.0-beta.14

Reproduction link

https://codesandbox.io/s/0y2o4p8zvv

Steps to reproduce

Visit reproduction link.

What is expected?

Should not warn

What is actually happening?

Warn that $listerns and $attrs is readonly

bug sync

Most helpful comment

@ticdenis, I believe @eddyerburgh is recommending you set Vue to silent. See the documentation.

Vue.config.silent = true

Suppress all Vue logs and warnings.

All 16 comments

@eddyerburgh Same problem here. Happened directly after upgrading from vue-test-utils Beta 12 to Beta 13! However, the problem stayed after upgrading from Beta 13 to Beta 14.

Might be related to #534

This is caused by the changes in beta.12 where we set all watchers to sync.

We set all watchers to sync, including the update watcher. This causes a re-render when the comonent updates the attrs. Then before the listeners have been updated, isUpdatingChildComponent is set to false, so when the listeners are mutated, the warning is logged.

Is there a workaround for this/plan to resolve? I've confirmed this with Element-UI as well. I don't think this is Vuetify specific. Maybe title should remove Vuetify specifically. https://codesandbox.io/s/q377x6prx9

The plan to resolve is to add an async option to Vue that we will set to false. I have a PR open but it won't be added until Vue 2.6 in a months time.

The only solution I can think of for this issue is to set Vue to silent when running update watchers:

Vue.config.silent = true

You can see more info on the synchrnous issue —https://github.com/vuejs/vue-test-utils/issues/676

How can I silence these warnings?

@ticdenis, I believe @eddyerburgh is recommending you set Vue to silent. See the documentation.

Vue.config.silent = true

Suppress all Vue logs and warnings.

Thanks! My mistake in silencing was to try it with the localVue instance.

I'm getting the following logged when running a test which appears related to this issue.

console.error node_modules/vue/dist/vue.runtime.common.js:589                                                                               [3/1000]
    [Vue warn]: $listeners is readonly.                                    

    found in                                                                                                                                          

    ---> <VAutocomplete>                                                   
           <Users>                                                                                                                                    
             <Root>                  

  console.error node_modules/vue/dist/vue.runtime.common.js:589                                                                                       
    [Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or co
mputed property based on the prop's value. Prop being mutated: "items"

    found in

    ---> <VAutocomplete>
           <Users>
             <Root>

  console.error node_modules/vue/dist/vue.runtime.common.js:589
    [Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or co
mputed property based on the prop's value. Prop being mutated: "selectedItems"

    found in

    ---> <VSelectList>
           <VMenu>
             <VAutocomplete>
               <Users>
                 <Root>

I set Vue.config.silent = true in my test\unit\index.js file as suggested above by @eddyerburgh but this is not surpressing errors.

I have also tried using $nextTick and async/await with flushPromises() to see if either of those methods would work here but no such luck.

Here is what this particular test looks like.

import { shallowMount, createLocalVue } from '@vue/test-utils';
import Vuex from 'vuex';
import Vuetify from 'vuetify';
import Users from '@/components/Users';

addElemWithDataAppToBody();

describe('Users.vue', () => {
  test('should render correct contents', () => {
    const localVue = createLocalVue();
    localVue.use(Vuetify);
    localVue.use(Vuex);

    // For Vuex
    let storeOptions;
    let store;

    storeOptions = {
      actions: {
      },
      getters: {
        shouldRefreshUser: jest.fn(),
      }
    };

    store = new Vuex.Store(storeOptions);

    let wrapper = shallowMount(Users, {store, localVue});

    let response = {
      results: [
        { name_uiowadisplay: 'Banana' },
        { name_uiowadisplay: 'Apple' },
        { name_uiowadisplay: 'Dragon Fruit' },
        { name_uiowadisplay: 'Cantaloupe' },
      ]
    };

    wrapper.vm.setSearchResults(response);

    expect(response.results[0].name_uiowadisplay).toBe('Apple');
    expect(response.results[1].name_uiowadisplay).toBe('Banana');
    expect(response.results[2].name_uiowadisplay).toBe('Cantaloupe');
    expect(response.results[3].name_uiowadisplay).toBe('Dragon Fruit');

  });
});

/**
 * Adds a warapping `div data-app="true"` to the body so that we don't
 * get Vuetify complaining about missing data-app attribute for some components.
 *
 * @return undefined
 */
function addElemWithDataAppToBody() {
  const app = document.createElement('div');
  app.setAttribute('data-app', true);
  document.body.append(app);
};

I had the same issue @bigtunacan. Modifying mount to use sync: false with the beta.16 version fixed the issue for me.

let wrapper = shallowMount(Users, { store, localVue, sync: false })

My entire file looks like this (stubbing a child component, and we use apollo link state instead of Vuex).

import { mount, createLocalVue } from '@vue/test-utils'
import Vuetify from 'vuetify'
import VeeValidate from 'vee-validate'
import Component from '../search-panel'

const localVue = createLocalVue()
Vuetify.install(localVue)
localVue.use(VeeValidate)

describe('@components/search-panel', () => {
  let errors
  let $validator
  let $apollo

  beforeEach(() => {
    // Stub out errors to handle VeeValidate error display.
    errors = {
      any: jest.fn(),
      first: jest.fn(),
      split: jest.fn()
    }
    // Stub out Apollo so that calls are not made to the store or server.
    $apollo = {
      mutate: jest.fn(),
      query: jest.fn()
    }
    // Stub out VeeValidate validator
    $validator = new VeeValidate.Validator()

    // Handle errors with Vuetify not being able to locate the data-app target by stubbing an attribute.
    const el = document.createElement('div')
    el.setAttribute('data-app', true)
    document.body.appendChild(el)
  })

  it('has edit fields', () => {
    const wrapper = mount(Component, {
      localVue,
      sync: false,
      stubs: {
        MapInput: "<div name='MapInput' ref='map'></div>"
      },
      mocks: {
        errors,
        $apollo,
        $validator
      }
    })
    const fields = wrapper.findAll('.search-field')
    const fieldLabels = fields.wrappers.map(w => w.find('label').text())
    expect(fieldLabels.some(fl => fl === 'Mission ID')).toBeTruthy()
    expect(fieldLabels.some(fl => fl === 'Owner')).toBeTruthy()
    expect(fieldLabels.some(fl => fl === 'Agency')).toBeTruthy()
    expect(fieldLabels.some(fl => fl === 'Objects Detected')).toBeTruthy()
    expect(fieldLabels.some(fl => fl === 'Start Date')).toBeTruthy()
    expect(fieldLabels.some(fl => fl === 'Start Time')).toBeTruthy()
    expect(fieldLabels.some(fl => fl === 'End Date')).toBeTruthy()
    expect(fieldLabels.some(fl => fl === 'End Time')).toBeTruthy()
    expect(fieldLabels.some(fl => fl === 'Description')).toBeTruthy()
    expect(fieldLabels.some(fl => fl === 'Show Deleted')).toBeTruthy()
    expect(fieldLabels.some(fl => fl === 'Enterprise')).toBeTruthy()
  })

})

I was facing this problem while dealing with a component that was using :visible.sync and made use of transitions and solved by using:

{
  stubs: {
    transition: false
  }
}

I'm not sure if all cases are related to these specific settings but, maybe it can be helpful for people facing similar problems.

Is there any progress on it?

I believe the fix here is waiting on 2.5.18 for Vue. We have also refactored the entire bootstrap process but it is a breaking change that won't come until 2.0

2.5.18 was released

The issue is still there

The fix relies on #1062

Was this page helpful?
0 / 5 - 0 ratings