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

fix: ensure plugins receive options correctly #276

Merged
merged 4 commits into from
Dec 16, 2020
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 17 additions & 4 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,21 @@ interface GlobalConfigOptions {
}

interface Plugin<Instance> {
handler: (instance: Instance) => Record<string, any>
handler: (
instance: Instance,
options: Record<string, any>
) => Record<string, any>
options: Record<string, any>
}

class Pluggable<Instance = DOMWrapper<Element>> {
installedPlugins: Plugin<Instance>[] = []

install(
handler: (instance: Instance) => Record<string, any>,
handler: (
instance: Instance,
options?: Record<string, any>
) => Record<string, any>,
options: Record<string, any> = {}
) {
if (typeof handler !== 'function') {
Expand All @@ -32,7 +38,9 @@ class Pluggable<Instance = DOMWrapper<Element>> {
}

extend(instance: Instance) {
const invokeSetup = (plugin: Plugin<Instance>) => plugin.handler(instance) // invoke the setup method passed to install
const invokeSetup = ({ handler, options }: Plugin<Instance>) => {
return handler(instance, options) // invoke the setup method passed to install
}
const bindProperty = ([property, value]: [string, any]) => {
;(instance as any)[property] =
typeof value === 'function' ? value.bind(instance) : value
Expand All @@ -42,7 +50,12 @@ class Pluggable<Instance = DOMWrapper<Element>> {
Object.entries(setupResult).forEach(bindProperty)
}

this.installedPlugins.map(invokeSetup).forEach(addAllPropertiesFromSetup)
// this.installedPlugins.map(invokeSetup).forEach(addAllPropertiesFromSetup)
this.installedPlugins
.map((plugin) => {
return invokeSetup({ handler: plugin.handler, options: plugin.options })
})
.forEach(addAllPropertiesFromSetup)
}

/** For testing */
Expand Down
1 change: 0 additions & 1 deletion src/vueWrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ export class VueWrapper<T extends ComponentPublicInstance> {
this.rootVM = vm?.$root
this.componentVM = vm as T
this.__setProps = setProps
// plugins hook
config.plugins.VueWrapper.extend(this)
}

Expand Down
121 changes: 69 additions & 52 deletions tests/features/plugins.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,72 +7,89 @@ declare module '../../src/vueWrapper' {
width(): number
$el: Element
myMethod(): void
greet: (name: string) => string
}
}

const textValue = `I'm the innerHTML`
const mountComponent = () => mount({ template: `<h1>${textValue}</h1>` })

describe('Plugin', () => {
describe('#install method', () => {
beforeEach(() => {
config.plugins.VueWrapper.reset()
})

it('extends wrappers with the return values from the install function', () => {
const width = 230
const plugin = () => ({ width })
config.plugins.VueWrapper.install(plugin)
const wrapper = mountComponent()
expect(wrapper).toHaveProperty('width', width)
})
describe('Plugin#install', () => {
beforeEach(() => {
config.plugins.VueWrapper.reset()
})

it('receives the wrapper inside the plugin setup', () => {
const plugin = (wrapper: VueWrapper<ComponentPublicInstance>) => {
return {
$el: wrapper.element // simple aliases
it('accepts options', () => {
Copy link
Member Author

Choose a reason for hiding this comment

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

this is the new test making sure options works correctly

function PluginWithOptions(
wrapper: VueWrapper<ComponentPublicInstance>,
options: Record<string, string>
) {
return {
greet: (name: string) => {
return `${options.msg}, ${name}`
}
}
config.plugins.VueWrapper.install(plugin)
const wrapper = mountComponent()
expect(wrapper.$el.innerHTML).toEqual(textValue)
})
}
config.plugins.VueWrapper.install(PluginWithOptions, { msg: 'Hello' })

it('supports functions', () => {
const myMethod = jest.fn()
const plugin = () => ({ myMethod })
config.plugins.VueWrapper.install(plugin)
mountComponent().myMethod()
expect(myMethod).toHaveBeenCalledTimes(1)
})
const wrapper = mountComponent()
// @ts-ignore
Copy link
Member

Choose a reason for hiding this comment

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

Is ts-ignore necessary here?

Copy link
Member Author

Choose a reason for hiding this comment

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

nope, removed

expect(wrapper.greet('Lachlan')).toBe('Hello, Lachlan')
})

describe('error states', () => {
beforeAll(() => {
jest.spyOn(console, 'error').mockImplementation(() => {})
})
it('extends wrappers with the return values from the install function', () => {
const width = 230
const plugin = () => ({ width })
config.plugins.VueWrapper.install(plugin)
const wrapper = mountComponent()
expect(wrapper).toHaveProperty('width', width)
})

afterAll(() => {
// @ts-ignore
console.error.mockRestore()
})
it('receives the wrapper inside the plugin setup', () => {
const plugin = (wrapper: VueWrapper<ComponentPublicInstance>) => {
return {
$el: wrapper.element // simple aliases
}
}
config.plugins.VueWrapper.install(plugin)
const wrapper = mountComponent()
expect(wrapper.$el.innerHTML).toEqual(textValue)
})

const plugins = [
() => false,
() => true,
() => [],
true,
false,
'property',
120
]
it('supports functions', () => {
const myMethod = jest.fn()
const plugin = () => ({ myMethod })
config.plugins.VueWrapper.install(plugin)
mountComponent().myMethod()
expect(myMethod).toHaveBeenCalledTimes(1)
})

it.each(plugins)(
'Calling install with %p is handled gracefully',
(plugin) => {
config.plugins.VueWrapper.install(plugin as any)
expect(() => mountComponent()).not.toThrow()
}
)
describe('error states', () => {
beforeAll(() => {
jest.spyOn(console, 'error').mockImplementation(() => {})
})

afterAll(() => {
// @ts-ignore
console.error.mockRestore()
})

const plugins = [
() => false,
() => true,
() => [],
true,
false,
'property',
120
]

it.each(plugins)(
'Calling install with %p is handled gracefully',
(plugin) => {
config.plugins.VueWrapper.install(plugin as any)
expect(() => mountComponent()).not.toThrow()
}
)
})
})