6
votes

I'm using bootstrap as my design framework and have been using bootstrap-vue. Now I would like to implement some tests to go along with my components. I'm writing a very simple test to make sure a modal is opened. What do I use in vue-test-utils to open the bootstrap-vue modal?

I'm using the basics that come with Laravel, bootstrap-vue, vue-test-utils, mocha, and mocha-webpack.

I'm trying to open the modal with wrapper.find('#modal-1').trigger('click'). I've tried using a directive <b-button v-b-modal.modal-1> I've tried using an event <b-button @click="$bvModal.show('modal-1')">. And lastly, I tried a regular button <button @click="showModal = true"> with this on the b-modal <b-modal v-model="showModal">. I've also tried adding a $nextTick in between the trigger and the assertion.

import { createLocalVue, mount } from '@vue/test-utils';
import expect from 'expect';
import BootstrapVue from 'bootstrap-vue';
import MyComponent from '@/components/MyComponent.vue';

const localVue = createLocalVue();

localVue.use(BootstrapVue);

describe ('MyComponent', () => {
    let wrapper;

    beforeEach(() => {
        wrapper = mount(QuotesExceptions, {
            localVue
        });
    });

    it ('opens a modal', () => {
        expect(wrapper.contains('#modal-1')).toBe(false);

        wrapper.find('#btnShow').trigger('click');

        expect(wrapper.contains('#modal-1')).toBe(true);
    });
});

I'm expecting the modal to be in the wrapper with expect(wrapper.contains('#modal-1')).toBe(true) and this is where the assertion is failing.

5
Have you found a solution or workaround for this problem?Chris Camaratta

5 Answers

2
votes

Use the attachToDocument: true mount option, as modal needs to be in the document in order to open.

You can see how BootstrapVue tests their modals at https://github.com/bootstrap-vue/bootstrap-vue/blob/dev/src/components/modal/modal.spec.js

2
votes

I was looking at the bootstrap-vue test on github as Troy suggested (https://github.com/bootstrap-vue/bootstrap-vue/blob/dev/src/components/modal/modal.spec.js)

There you can see that they are using the prop static: true. Adding this to my code solved my problem.

component.vue

<b-modal
    v-model="showModal"
    id="myModal"
    data-qa="importModal"
    :static="true"
>
</b-modal>

component.spec.js

it ('opens a modal', (done) => {
    const button = wrapper.find('[data-qa="button"]');
    const modal = wrapper.find('#myModal');
    expect(button.exists()).toBe(true);
    expect(button.is('button')).toBe(true);
    expect(modal.exists()).toBe(true);
    expect(modal.is('div')).toBe(true);

    expect(modal.isVisible()).toBe(false);
    button.trigger('click');

    Vue.nextTick(() => {
      expect(modal.isVisible()).toBe(true);
      done();
    });
});

I had to select the modal by id because the inner part is getting display: none. When I put a data-qa on the modal it sticks on the outer element which is not hidden itself. Another solution for that would be to select it the following way: const modal = wrapper.find('[data-qa="modal"] .modal');

But I still get the following warning in my console: [BootstrapVue warn]: observeDom: Requires MutationObserver support.

0
votes
it ('opens a modal', (done) => {
    const button = wrapper.find('[data-qa="button"]');
    expect(!!document.querySelector('myModal')).toBe(false)
    button.trigger('click');
    expect(!!document.querySelector('myModal')).toBe(true) 
});
0
votes

I'm using jest to test if a modal appears when a button is clicked adn ended up with related problem. Will leave my answer here for furture readers.

I was trying to do something like:

 let openModalBtn = wrapper.find('button[id="openModal"]');
 expect(openModalBtn.exists()).toBe(true); // OK
 expect(openModalBtn.is("button")).toBe(true); // OK
 await deleteBtn.trigger("click"); // BANG

Leading to this:

[Vue warn]: Error in v-on handler (Promise/async): "TypeError: this.$refs.confirmDialogue[0].show is not a function"

The thing was that I was using shallowMount:

// not good
describe("some test", () => {
  let wrapper;
  beforeEach(() => {
    wrapper = shallowMount(MyComponent, {...});
  });

And at some point of MyComponent, it needs to do something like:

...
  methods: {
    openModal() {
      await this.$refs.confirmDialogue[0].show();
      ...
    },

Therefore this.$ref was comming as undefined.

Using mount instead:

// good
describe("some test", () => {
  let wrapper;
  beforeEach(() => {
    wrapper = mount(MyComponent, {...});
  });

allowed me to click that button and find the stub within the wrapper.html().

So make sure to mount your components if you gonna need to access stubs from it.

-1
votes

In my case this work perfectly,

Here I have a b-modal in template with a id="modal-1" and when the button is clicked bootstrap-vue modal is opened using showModal() method.

Try this:

<template>

    <b-button v-on:click="showModal()">
    <b-modal id="modal-1"></b-modal>

</template>

<script>
    methods: {
        showModal() {
              this.$root.$emit("bv::show::modal", 'modal-1', "#btnShow");
          },
    }

</script>