Skip to content

[help-wanted] How to change a select element #260

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

Closed
gsccheng opened this issue Dec 13, 2017 · 14 comments
Closed

[help-wanted] How to change a select element #260

gsccheng opened this issue Dec 13, 2017 · 14 comments
Labels

Comments

@gsccheng
Copy link

gsccheng commented Dec 13, 2017

I want to be able to test my Single File Vue Component by selecting an option in a Vue Child component. I need to trigger the Vue watcher that watches selectedFooItem. I am using mocha-webpack and js-global according to the vue-test-utils docs.

This is related to this issue: #128 , and my SO ticket. I also tried going the chat route but Discord tells me I don't have permission to send messages in that channel in vue-land.js.org.

I'm testing a Vue 2 component that has an el-select child component from ElementIO . The main difference in my example is that there is a child Vue component as opposed to HTML elements (i.e. can't rely on .find("select"). It looks like this:

// Foo.vue
<template lang="pug">
  .foo-item(:class="className")
    el-select(v-model="selectedFooItem",
              value-key="name",
              @click="onClick",
              @visible-change="onVisibleChange")
      el-option(v-for="entity in unselectedFooItems",
                :label="foo.name",
                :value="foo",
                :key="foo.name")
</template>

<script src="./Foo.js"></script>

What doesn't work:
1.

const selectBox = wrapper.find("el-select");
selectBox.trigger("click");
wrapper.find('el-option').trigger("click")

I see onClick being called twice (one from el-option). I think this refers to the event propagation behavior the issue I referenced was talking about. But, my watcher does not detect changes.

wrapper.find('el-option').trigger("change")

Still, watcher does not detect changes.

const selectBox = wrapper.find("el-select");
selectBox.trigger("change", { /* the value */ })

My work around

Right now I'm resorting to

wrapper.setData({ foo: 'bar' })

or

wrapper.vm.selectedFooItem = "bar"
@lmiller1990
Copy link
Member

lmiller1990 commented Dec 14, 2017

If you call wrapper.update() after doing trigger does that make it work? It could be a sync/async issue.

I am trying to recreate this now, can you post the entire component and test? The issue says "how to change a select element" but you also mentioned a watcher.

Since watchers are async, you might need to use await, or call wrapper.update, for those to update.

@lmiller1990
Copy link
Member

lmiller1990 commented Dec 14, 2017

Edit, had a shot at this. Used the element ui template on their website. I think you should be using the change event from element ui, not visible-change. Anyway... using the change event, I get the expected result in the browser - the new item. However, in vue-test-utils under Jest, I get an HTMLUnknownElement as the event.target. I guess some conflict between the library and the vue-test-utils?

The way I made it "work" in vue-test-utils is by adding click to the el-option - but this is not really doing what you want. See below.

I think setting data directly is actually okay, though. You don't need to test that el-select works, they have their own internal tests. However, I too have found some pain points when it comes to testing components using external libraries...

I thought about this a little more though, and I don't think we should unit testing external components anyway. They should have their own tests. vue-test-utils is for testing your components, the code you write. You can be confident el-select works, since the authors wrote tests for it already.

The test concerning "when the user selects something from a dropdown, an a watcher causes such and such to happen" sounds more like an integration test - much more high level.

Of course I am also new to unit testing and programming in general, so I could be wrong.

Here is what I tried regarding your initial question. Comments in the test (second snippet).

<template>
  <div id="app">
    <img src="./assets/logo.png">
    <div>
      <el-select @change="handleChange" v-model="value" placeholder="Select">
        <el-option
           v-for="item in options"
           :key="item.value"
           @click="handleClick(item.value)"
           :label="item.label"
           :value="item.value">
        </el-option>
      </el-select>
    </div>
  </div>
</template>

<script>
export default {
  methods: {
    handleClick (val) {
      this.value = val
    },

    handleChange (e) {
      console.log(e)
    }
  },

  data() {
    return {
      options: [{
        value: 'Option1',
        label: 'Option1'
      }, {
        value: 'Option2',
        label: 'Option2'
      }],
      value: ''
    }
  }
}
</script>
import { shallow, mount } from 'vue-test-utils'
import App from './App'

it('works?', () => {
  const wrapper = mount(App)
  
  // workaround...bad
  wrapper.findAll('el-option').at(0).trigger('click')

  // works in browser, but not in vue-test-utils.
  wrapper.findAll('el-select').at(0).trigger('change')
})

I hope that helps, please let me know if you have more questions or anything else.

I think for the 1.0 release we should provide some good documentation on what to test, not just how.

@folmert
Copy link

folmert commented Jan 9, 2018

@gsccheng, @lmiller1990: Have you figured it out?

@lmiller1990
While I agree that we shouldn't test external components, I don't think that it relates to this example, changing data directly is actually against practices promoted by vue-test-utils:

"we recommend writing tests that assert your component's public interface, and treat its internals as a black box."
(...)
"test case would simulate the click and assert that the rendered output has increased by 1"

So in this case, instead changing internal data we should rather simulate the change event. Otherwise handleChange method won't be covered. But frankly I've also found triggering change not working for radio inputs in vue-test-utils (I write specs in Jest).

Anyone figured out how to simulate selecting a radio input?

@eddyerburgh
Copy link
Member

eddyerburgh commented Jan 9, 2018

@folmert to check a radiobutton:

wrapper.find('#radioButton2').element.checked = true

@gsccheng Thanks for the issue. Are you able to make a minimal reproduction (a test that fails with the least amount of code as possible), and include all the code. Then I can have a look and debug. I can let you know how it's done.

@folmert
Copy link

folmert commented Jan 10, 2018

@eddyerburgh it doesn't work, see my minimal reproduced example, running yarn test will return:

RadioGroup
√ renders changed data according to the changed folder data (38ms)
× renders changed data according to the selected folder option (8ms)

Expected value to be (using ===):
"Selected: existing"
Received:
"Selected:"

Spec:

import {mount} from 'vue-test-utils';
import RadioGroup from '@/components/RadioGroup';

describe('RadioGroup', () => {
    let wrapperDeep;

    beforeEach(() => {
        wrapperDeep = mount(RadioGroup, {});
    });

    // works
    it('changes data according to the changed folder data', () => {
        wrapperDeep.setData({folder: 'existing'});
        expect(wrapperDeep.find('.selected').text()).toBe('Selected: existing');
    });

    // doesn't work
    it('changes data according to the selected folder option', () => {
        wrapperDeep.find('input[type="radio"][value="existing"]').element.selected = true;
        expect(wrapperDeep.find('.selected').text()).toBe('Selected: existing');
    });
});

@eddyerburgh
Copy link
Member

HI @folmert, you're right this is indeed an issue with model not picking up the change. Can you create a new issue for this please?

@folmert
Copy link

folmert commented Jan 11, 2018

Ok, I've created a separate issue: #345 but given that @gsccheng also fails to simulate option change the essence of the problem might be connected.

@eddyerburgh
Copy link
Member

To change the value of inputs and update data bound with v-model:

const input = wrapper.find('input[type="text"]')
input.element.value = 'some value' // input element value is changed, v-model is not
input.trigger('input') // v-model updated
const radioInput = wrapper.find('input[type="radio"]')
radioInput.element.checked = true // input element value is changed, v-model is not
radioInput.trigger('input') // v-model not updated
radioInput.trigger('change') // v-model updated
const radioInput = wrapper.find('input[type="radio"]')
radioInput.element.checked = true // input element value is changed, v-model is not
radioInput.trigger('change') // v-model updated
wrapper.findAll('option').at(1).element.selected = true 
wrapper.find('select').trigger('change')

There is an open issue to add helper methods to make this easier: #530

I'm closing this issue in favor of that feature request.

@denisinvader
Copy link

It would be nice if all of these examples were in the documentation

@eddyerburgh
Copy link
Member

There's a PR that will add methods to make this simpler—#557

@denisinvader
Copy link

Okay. I also have one question. Is there a way to test that user can't change value in a disabled input?

For example

const wrapper = mount({
  data: () => ({ val: '' }),
  template: '<input v-model="val" disabled />'
});

wrapper.find('input').element.value = 'testing';
wrapper.find('input').trigger('input');

expect(wrapper.vm.val).toEqual('');
expect(wrapper.vm.val).not.toEqual('testing');

Or it's a browser's work - prevent input on a disabled input?

@lmiller1990
Copy link
Member

I believe JSDom supported disabled - does the above snippet not work?

@denisinvader
Copy link

Oh, I made a mistake. Snippet above does work.

But if I change disabled to readonly it does not work. Maybe it is okay.

@lmiller1990
Copy link
Member

Readonly does not prevent events from triggering, or JavaScript from filling the value. You can research disabled and readonly elsewhere.

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

No branches or pull requests

5 participants