vuejs / vue-test-utils

Component Test Utils for Vue 2
https://vue-test-utils.vuejs.org
MIT License
3.57k stars 669 forks source link

[vue-test-utils]: wrapper.setValue() cannot be called on this element #2055

Closed Destnyl closed 1 year ago

Destnyl commented 1 year ago

[vue-test-utils]: wrapper.setValue() cannot be called on this element

Hi Im having an isue with unit testing, it says that setValue cannot be called on the element. heres my code

Steps to reproduce

--component--- //the input element that i was trying to access was a custom component <validation-provider v-slot="{ errors }" rules="required|email" class="form-item" name="email"

<secondary-input v-model="state.login.email" :class="{ 'form-error': errors.length > 0 }" name="email" placeholder="Email Address" data-qa="email-address" type="email" />

---testing-- it('test if it has an input for email address', async () => { const value = 'sample@email.com' const input = wrapper.get([data-qa='email-address']) await flushAll() await input.setValue(value) expect(input.element.value).toBe(value) })

Expected behaviour

expected behaviour is to assign the value in email address input

Actual behaviour

got an error of setting the value to input email address

is there anyone here who could help me to resolve my issue.

Destnyl commented 1 year ago

heres the component

    <validation-provider
      v-slot="{ errors }"
      rules="required|email"
      class="form-item"
      name="email"
    >
      <secondary-input
        v-model="state.login.email"
        :class="{
          'form-error': errors.length > 0
        }"
        name="email"
        placeholder="Email Address"
        data-qa="email-address"
        type="email"
      />
      <form-errors :errors="errors" />
    </validation-provider>
lmiller1990 commented 1 year ago

Probably same as https://github.com/vuejs/vue-test-utils/issues/1883

setValue needs a DOM element - you are finding a component (Vue instance).

Your code doesn't have have a minimal reproduction - please share a repo I can run.

To fix it, you can probably do

it('test if it has an input for email address', async () => {
  const wrapper = mount(Comp)
  const value = '[sample@email.com](mailto:sample@email.com)'
  const input = wrapper.get([data-qa='email-address'])
  // Get HTMLInputElement
  await input.get('input').setValue(value)
  expect(input.element.value).toBe(value)
})

If it still won't work, try posting a repository I can run and debug. Thanks!

Destnyl commented 1 year ago
Screenshot 2023-02-08 at 10 39 34 AM

hi @lmiller1990 , still not working got this error

Unable to find input within:

lmiller1990 commented 1 year ago

Please post your entire test code and component.

I don't know what <secondary-input> does, I can't even see your entire code.

Are you using shallowMount? If you are, that's why you cannot find the input. Try using mount. That will render the entire tree, and you can access the HTMLInputElement.

Destnyl commented 1 year ago

here's the test code, not using shallowMount

import { mount, createLocalVue } from '@vue/test-utils'
import flushPromises from 'flush-promises'
import LoginDefault from '@/components/LoginPage/Default.vue'

const localVue = createLocalVue()
jest.useFakeTimers()

describe('LoginPage/Default.vue', () => {
  const wrapper = mount(LoginDefault, {
    localVue,
    propsData: {
      loginTask: {
        isError: false,
        last: {
          error: {
            response: {
              status: 401
            }
          }
        }
      }
    },
    beforeCreate () {
      this.$t = jest.fn(key => key)
    }
  })

  const flushAll = async () => {
    await flushPromises()
    // any delayed or debounced state computations
    jest.runAllTimers()
    // get rid of the pending rendering tick
    await flushPromises()
  }

  beforeEach(async () => {
    await flushAll()
  })

  it.skip('Login component Snapshat', () => {
    expect(wrapper.element).toMatchSnapshot()
  })

  it('test if it has an input for email address', async () => {
    const value = 'sample@email.com'
    const input = wrapper.get(`[data-qa='email-address']`)
    await flushAll()
    await input.get('input').setValue(value)
    expect(wrapper.find('input').toBe(true))
    expect(input.element.value).toBe(value)
  })
})
Destnyl commented 1 year ago

SecondaryInput copy.txt Default copy.txt

lmiller1990 commented 1 year ago

Hm this looks all okay. I'd recommend some debugging - console.log where you emit the event, etc. I see

 <input
            v-bind="{
              ...$attrs,
              disabled
            }"
            ref="input"
            :class="cssClasses"
            v-on="listeners"
          >

Which binds to listeners

input: (event) => {
          this.$emit('input', event.target.value)
        },

I'd log in here - is this getting called?

Looking at setValue in Test Utils...

https://github.com/vuejs/vue-test-utils/blob/5013304e492818e050edcd609429a3161227910a/packages/test-utils/src/wrapper.js#L795-L797

      this.element.value = value

      this.trigger('input')

So it should be working okay.

If you cannot debug it, please share a minimal repository I can debug. I don't have bandwidth to make a repo, copy paste your code, etc.

Destnyl commented 1 year ago

thanks @lmiller1990 for the ideas,

created a workaround for this issue,

it('test if it set value for email address', async () => { const value = 'sample@email.com' await wrapper.setData({ state: { login: { email: value } } }) wrapper.vm.$emit('input', value) await wrapper.vm.$nextTick() expect(wrapper.emitted('input')[0]).toEqual([value]) })

just checking the value that was passed from the email field from the emitted values of the custom component.

Destnyl commented 1 year ago

hi @lmiller1990 , follow up question about this, the reason why it can't setvalue to the custom input is that it can't find the element input in the child component(secondary-input.)

and I tried to console log the wrapper and it doesn't show all element in the child component.

why can't get all the elements in the child component?

made some research but can't find the answer.

lmiller1990 commented 1 year ago

why can't get all the elements in the child component?

You should be able to get all elements. If you can't, it's a bug. Please make a minimal reproduction showing your bug. I cannot help you until I have some code I can run - I'm basically just guessing, otherwise.