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

$listeners is readonly error #532

Closed
eddyerburgh opened this issue Apr 15, 2018 · 16 comments · Fixed by #1062
Closed

$listeners is readonly error #532

eddyerburgh opened this issue Apr 15, 2018 · 16 comments · Fixed by #1062

Comments

@eddyerburgh
Copy link
Member

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

@tbsvttr
Copy link

tbsvttr commented Apr 16, 2018

@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.

@timoschwarzer
Copy link

Might be related to #534

@eddyerburgh
Copy link
Member Author

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.

@bdeo
Copy link

bdeo commented May 31, 2018

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

@eddyerburgh eddyerburgh changed the title $listeners is readonly error when used with vuetify $listeners is readonly error Jun 1, 2018
@eddyerburgh
Copy link
Member Author

eddyerburgh commented Jun 1, 2018

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 —#676

@ticdenis
Copy link

How can I silence these warnings?

@wordythebyrd
Copy link

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

Vue.config.silent = true

Suppress all Vue logs and warnings.

@ticdenis
Copy link

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

@bigtunacan
Copy link

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);
};

@tlbignerd
Copy link

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()
  })

})

@nelsonsar
Copy link

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.

@abdullah
Copy link

abdullah commented Oct 29, 2018

Is there any progress on it?

@johnleider
Copy link

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

@Djaler
Copy link
Contributor

Djaler commented Dec 10, 2018

2.5.18 was released

@builder7777
Copy link

The issue is still there

@eddyerburgh
Copy link
Member Author

The fix relies on #1062

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

Successfully merging a pull request may close this issue.