Skip to content
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

POC: Plugin interface with wrapper in closure #82

Merged
merged 11 commits into from
Apr 24, 2020

Conversation

JessicaSachs
Copy link
Contributor

@JessicaSachs JessicaSachs commented Apr 21, 2020

This POC aims to add on to the current work surrounding plugins. I used the branch at #55 to build some plugins. I struggled with the existing API. I was limited from using fat-arrow functions due to the wrapper only being available on this. I wanted a closure where the wrapper would be passed in.

Importance of Plugins

Our intension is that plugins will be a means for users to...

  1. create short-hand aliases 🚀
  2. implement helper functions 🛠
  3. customize Vue Test Utils as they need to 👩🏻‍🎨

The pattern

Certain entities are Pluggable (VueWrapper, DOMWrapper). You can install plugins, which may return objects to be extended onto the instance of wrappers being mounted. This pattern looks like data or setup functions in Vue core and should feel familiar.

Basic example

This plugin will add the width property and myMethod once the wrapper is mounted.

const plugin = (wrapper) => {
  return {
    width: 200,
    myMethod() {}
  }
}

config.plugins.VueWrapper.install(plugin)

Here is a plugin that will provide a new method to clean up the output of text(). (The use case is based off of jest-dom)

// A plugin to add optional whitespace stripping to VueWrapper.text()
const normalize = text =>  text.replace(/\s+/g, ' ').trim()

// HTML-friendly text matcher. Based off of jest-dom
const friendlyTextPlugin = (wrapper) => ({
  normalizeText: () => normalize(wrapper.text()),
})

// installing it into VueWrapper

config.plugins.VueWrapper.install(friendlyTextPlugin)

const wrapper = mount({ template: `<div>    Foo</div>` })
wrapper.normalizeText() // 'Foo'

Weak spots:

  1. Install functions are executed every mount
  • Perhaps this is ok. If most of the plugins' work is just setting up functions to be called later, this might not be a concern
  1. I don't think Pluggable as a class is the right abstraction
  • I'm really new to Typescript 😬

install(handler, options = {}) {
if (typeof handler !== 'function') {
console.error('plugin.install must receive a function')
handler = () => ({})
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should decide if we're going to exit the process here or quietly swallow the misconfigured plugin

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think failing loudly is usually a good idea

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The louder the better, ideally with "you screwed up and here's where and how to fix it"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah. I'm fine with that. I wish I had a plugin name to point people at.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmmm didn't think of that. 🤔

it('receives the wrapper inside the plugin setup', () => {
const plugin = (wrapper) => {
return {
$el: wrapper.element // simple aliases
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will we have access to private properties on the instance? Like the rootVM?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, your original example will work. You can get access to the ComponentPublicInstance under componentVM which is private.

Copy link
Contributor Author

@JessicaSachs JessicaSachs Apr 21, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const namePlugin = wrapper => ({ lowerCaseName: wrapper.componentVM.$.type.name })
config.plugins.VueWrapper.install(namePlugin)
const wrapper = mount({ template: `<h1>Hello</h1>`, name: 'My_Component' })
wrapper.lowerCaseName // 'my_component'

@lmiller1990
Copy link
Member

I resolved conflicts, and deployed latest master to my npm account and played around and so far this seems to work great 👍 nice job @JessicaSachs

image


declare module '../../src/vue-wrapper' {
// @ts-ignore
interface VueWrapper {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ts-ignore should be removed here: you should just need to have the same signature than VueWrapper, i.e VueWrapper<T extends ComponentPublicInstance> and TS will be happy to merge the declarations.
You should also be able to remove all the other @ts-ignore in the file.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔 I will give this a try

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To no-one's surprised I changed the types based on @cexbrayat 's recommendation and it worked

Copy link
Member

@lmiller1990 lmiller1990 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me! CI is passing and we have no conflicts. We need some docs then we can merge this (this seems like an important feature to document and document well).

@afontcu
Copy link
Member

afontcu commented Apr 24, 2020

Looks good to me! CI is passing and we have no conflicts. We need some docs then we can merge this (this seems like an important feature to document and document well).

I've been trying to keep docs up to date with vtu-next releases, just to make sure we don't document any POC or temporary feature (I think this is a sensible way of handling docs until we're able to merge repos). What I mean by this is that feel free to merge this up if you feel confident enough with the implementation and release a new alpha, and then we can work out the docs part :)

@lmiller1990
Copy link
Member

Ok, let's do it. Great job all

@lmiller1990 lmiller1990 merged commit 3c035d4 into master Apr 24, 2020
@lmiller1990 lmiller1990 deleted the feature/pluggable-poc branch April 24, 2020 13:51
@JessicaSachs
Copy link
Contributor Author

Nice! Thanks :-) I'll pick up the docs this weekend.

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

Successfully merging this pull request may close these issues.

5 participants