Search code examples
javascriptvue.jsvuejs2vue-test-utils

Vue Test Utils Multiple input emits reset value


So I've created a vue component and I am using vue test utils to test it. The component allows a user to upload a file which updates an array which is emitted through the input (code shown below) that the user can then access. A new file upload will add to the array and not replace.

What's meant to happen:

I upload the first file and the emitted input event shows that one file; I then upload the second file and the emitted input event shows that file and the first one.

What does happen:

I upload the first file and the emitted input event shows that one file; I then upload the second file but the emitted input only shows the second file.

I'm not sure what is going on here as when I run tests in my browser and console the computed files I see that both files are in the array after doing the same process that my test is doing. I thought it was to do with it not updating fast enough so I added the wait for the next tick but with no luck. Is this a bug or is there something I'm not understanding about the events in my tests? Thanks for any help.

Component

<template>
   <input type="file" @change="handle"/>
</template>

<script>
   export default {
       props: {
          value: {
            type: Array,
            required: false,
            default() {
                return []
            }
          }
       },

       method: {
           handle(e) {
               const files = e.target.files;

               var newFiles = [];

               for (var i = 0; i < files.length; i++) {
                    newFiles.push(files[i])
               }

               this.files = this.files.concat(newFiles)
           }
       },

       computed: {
           files: {
            get() {
                return this.value
            },

            set(val) {
                this.$emit('input', val)
            }
        }
   }

Test

it('can add to array', async () => {
    const files = [
      {
        name: 'image.png',
        size: 20000000,
        type: 'image/png',
      },
      {
        name: 'image.jpg',
        size: 20000,
        type: 'image/jpeg',
      },
    ]
    const wrapper = mount(Component)

    // Upload first file
    wrapper.vm.handle({
      target: {
        files: [files[0]],
      },
    })

    expect(wrapper.emitted().input[0]).toStrictEqual([
      [files[0]]
    ]);

    await wrapper.vm.$nextTick()

    // Upload second file
    wrapper.vm.handle({
      target: {
        files: [files[1]],
      },
    })

    expect(wrapper.emitted().input[1]).toStrictEqual([files]);
}

Solution

  • I understand what has happened now. When the files array is changed in the component I'm emitting it but I'm not saving it anywhere when normally I would have a v-model saving it. As my tests don't simulate a v-model the computed files set value is an empty array. So I need to set the prop value in order to simulate that, I found that I can add a listener for the input event to update the value.

    it('can add to array', async () => {
        const files = [
          {
            name: 'image.png',
            size: 20000000,
            type: 'image/png',
          },
          {
            name: 'image.jpg',
            size: 20000,
            type: 'image/jpeg',
          },
        ]
        const wrapper = mount(Component, {
            listeners: {
               // simulate v-model update
               input: (val) => { 
                   wrapper.setProps({ value: val });
               }
            }
        })
    
        // Upload first file
        wrapper.vm.handle({
          target: {
            files: [files[0]],
          },
        })
    
        expect(wrapper.emitted().input[0]).toStrictEqual([
          [files[0]]
        ]);
    
        await wrapper.vm.$nextTick()
    
        // Upload second file
        wrapper.vm.handle({
          target: {
            files: [files[1]],
          },
        })
    
        expect(wrapper.emitted().input[1]).toStrictEqual([files]);
    }
    

    Another way to fix this is to save to a files array in the data of the component then call an update function to emit the new array.