Skip to content

Support wrapping an already mounted Vue instance #337

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
emileber opened this issue Jan 8, 2018 · 24 comments
Closed

Support wrapping an already mounted Vue instance #337

emileber opened this issue Jan 8, 2018 · 24 comments

Comments

@emileber
Copy link
Contributor

emileber commented Jan 8, 2018

The other day while testing a class that manually mounts a Vue instance to an element in the page, I wanted to use the Wrapper utilities but found out that I can't just call new Wrapper(myVueInstance); as it seems that Wrapper and WrapperArray are not exposed within the public API.

I didn't dive deep into the code, so I might have missed a way to achieve what I want.

A quick look at the source and the documentation shows that only the following is exposed:

export default {
  createLocalVue,
  config,
  mount,
  shallow,
  TransitionStub,
  TransitionGroupStub
}

I found /pull/328 which is close to what I'd want.

@eddyerburgh
Copy link
Member

eddyerburgh commented Jan 8, 2018

I'm against exporting these classes.

Most users won't be aware of how to create WrapperArrays, and I don't think we should make them aware.

We only expose a few methods as methods to use:

mount
shallow
createLocalVue
(We also expose components, but they aren't featured in the top-level API in the docs).

@emileber
Copy link
Contributor Author

emileber commented Jan 8, 2018

@eddyerburgh Why? Is there a risk I'm not seeing?

We're already using instances of these and the documentation is already available.

@emileber
Copy link
Contributor Author

emileber commented Jan 8, 2018

It's just that most utilities provided by Wrapper could be used with arbitrary instances, not limited to the one being tested with mount or shallow.

@eddyerburgh
Copy link
Member

eddyerburgh commented Jan 8, 2018

The risk in my opinion is bloating the API and making the library more confusing for users.

The docs make a distinction between Wrapper and WrapperArray. But they don't make a distinction between VueWrapper, or Wrapper, and we may add or remove extra Wrappers in the future.

I think that exposing methods like this that deal with the internals encourage users to use vue-test-utils rely on internals that could change in the future. Making sure mount and shallow are the only entry points give us more control to change how it works under the hood.

@emileber
Copy link
Contributor Author

emileber commented Jan 8, 2018

Is there a way to use mount or shallow with an already mounted Vue instance?

Or just a wrap function similar to the 2 other entry points that deals with already mounted Vue instances, returning the right wrapper according to what was passed.

@eddyerburgh
Copy link
Member

No, not at the moment.

When would you have an already mounted instance in a test? Can you post an example in code?

@emileber
Copy link
Contributor Author

emileber commented Jan 8, 2018

For example, I'm testing our widget API (vanilla JS) which mounts a Vue instance on a optionally passed element:

  render() {
    const doc = this.options.document;
    let el = query(this.options.el);
    if (!el) {
      el = doc.createElement('div');
      doc.body.appendChild(el);
    }

    this.vue = new Vue({
      el,
      render: h => h(App),
    });
  }

And I want to make sure that the passed element is replaced by our Vue instance's element.

// Create a test element
const testClass = 'test-element';
const el = document.createElement('section');
el.classList.add(testClass);
body.append(el);

// Use our element with the widget
const instance = new Widget({ el });
const vue = instance.vue;

it('should replace our element to a Vue instance', () => {
  expect(vue.$el).not.toBe(el);
  expect(vue.$el.classList.contains(testClass)).toBeFalsy();
  expect(body.contains(el)).toBe(false);
});

There's probably a better way to test this, but initially, I wanted to use the Wrapper utilities to check for classes, or to make sure that the element was a <div> and not <section>. Right now, I'm falling back on the DOM API.

Not that it's absolutely necessary, but I wanted to be consistent with the other tests where I'm mounting the components with mount or shallow and was really surprised that the Wrapper class that I was using wasn't even exposed other than as a return value.

@38elements
Copy link
Contributor

38elements commented Jan 16, 2018

@eddyerburgh
Would you please tell me that the specification supports Vue instance or not ?

@eddyerburgh
Copy link
Member

@38elements what specification do you mean?

@eddyerburgh
Copy link
Member

How about exposing the createWrapper method that takes either a vnode or a Vue instance.

I see that there's a need for this for some users.

@38elements
Copy link
Contributor

38elements commented Jan 16, 2018

There is the description for this in test and document or not. (I mistaked)
The specification is the description for this in test and document or not.
The specification is the description for this in test and document.
I could not find it.

@eddyerburgh
Copy link
Member

Ah, so we don't currently support already mounted elements. We could look at adding support.

@38elements
Copy link
Contributor

38elements commented Jan 16, 2018

Vue instance and Vue component are different.
The mouted element example uses Vue instance .
Deciding whether to support Vue instance is important.
If Vue instance is not supported, createWrapper is needless.

If Vue instance is supported and mounted vue instance is necessary, the following el option should considered.

wrapper = mount({
   el: el,
   // ......
})

@38elements
Copy link
Contributor

38elements commented Jan 16, 2018

Since I could not find the description about supporting not mounted Vue instance in document,
I thought that vue-test-utils only supports Vue component and does not support not mounted Vue instance.

@eddyerburgh
Copy link
Member

You can create a VueWrapper with a mounted Vue instance, so exposing a createWrapper lets us support a mounted Vue instance.

@38elements
Copy link
Contributor

I think the title of this issue is not appropriate and "Support Vue instance" is appropriate.

I think that createWrapper() is not necessary and the method to mount a Vue instance is necessary.
Like mount() mounting a Vue component, mount() mounting a Vue instance is appropriate.
In other word, el option is supported at mount().

Most people will make a method for each function.
There will be a method which returns Vue options.

@emileber emileber changed the title Expose Wrapper and WrapperArray within the public API Support wrapping an already mounted Vue instance Jan 17, 2018
@eddyerburgh
Copy link
Member

I don't think we should encourage calling mount with a Vue instance, since it's already been mounted.

createWrapper mounts Vue instances. We could export it, and straight away we would be supporting mounting Vue instances.

There's already an issue to support an el option in mount: #351

@38elements
Copy link
Contributor

The object having el property, not Vue instance will be passed mount() to.
An object having el property is not mouted.
When the Vue constructor passed the object having el property executes, a Vue instance is mouted.

@38elements
Copy link
Contributor

Like Vue component, I think that Vue instance requires stub and mock.

@eddyerburgh
Copy link
Member

I don't think we should encourage mounting Vue instances, apart from exposing the createWrapper method.

To add a mock you add to the Vue class prototype:

Vue.prototype.mock = 'value'

To add a stub you pass components inside the component options.

Maybe after we've exposed createWrapper we can revisit this, but initially I don't think we should add an interface for stubbing or mocking.

@38elements
Copy link
Contributor

38elements commented Jan 27, 2018

IMHO,
Since the goal of vue-test-utils is providing an official library and guide for unit testing Vue components,
Supporting Vue instance is not mandatory for version 1.0.
I do not think making a decision in a hurry is needed.

I assume a Vue instance which is on page of Laravel or Ruby on Rails as a target for testing.

@eddyerburgh
Copy link
Member

eddyerburgh commented Jan 27, 2018

Agreed, but we have an open PR that would allow the user to mount an instance

@38elements
Copy link
Contributor

38elements commented Jan 27, 2018

I think providing Wrapper class and WrapperArray class and supporting Vue instance are different issue.
Supporting testing Vue instance may might not be necessary.
I think it is better to think about supporting Vue instance or not and the use case of Vue instance.

@38elements
Copy link
Contributor

IMHO,
I think supporting mounted Vue instance is not necessary.
The Vue instance used in Laravel or Ruby on Rails does not have template option.
It uses inside element of the element mounted by it as template.
The HTML element mounted by the Vue instance exists on template file like blade or erb.
The template for test is used in test.
I think that it will be resolved, as follows.

vue-instance.js

import Vue from 'vue'

export const vueInstance = new Vue({
    methods: {
      foo () {
        return 'a' 
      },
      bar () {
        return 'b' 
      }
    }
})
import { vueInstance } from './vue-instance'

const options = { 
  methods: { 
    bar () { 
      return 'B'
    },
    baz () { 
      return 'C'
    }
  }
}
let c = vueInstance.$options
c.template = '<div>{{ foo() }}{{ bar() }}{{ baz() }}</div>'
const wrapper = mount(c, options)
expect(wrapper.text()).to.equal('aBC')

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

No branches or pull requests

3 participants