diff --git a/docs/guide/extending-vtu/plugins.md b/docs/guide/extending-vtu/plugins.md
index a1ae01f77..1d32a97d9 100644
--- a/docs/guide/extending-vtu/plugins.md
+++ b/docs/guide/extending-vtu/plugins.md
@@ -10,7 +10,9 @@ Some use cases for plugins:
1. Attaching matchers to the Wrapper instance
1. Attaching functionality to the Wrapper
-## Using a Plugin
+## Wrapper Plugin
+
+### Using a Plugin
Install plugins by calling the `config.plugins.VueWrapper.install()` method
. This has to be done before you call `mount`.
@@ -43,12 +45,12 @@ once. Follow the instructions of the plugin you're installing.
Check out the [Vue Community Guide](https://vue-community.org/v2/guide/ecosystem/testing.html) or [awesome-vue](https://github.com/vuejs/awesome-vue#test) for a collection of community-contributed plugins and libraries.
-## Writing a Plugin
+### Writing a Plugin
A Vue Test Utils plugin is simply a function that receives the mounted
`VueWrapper` or `DOMWrapper` instance and can modify it.
-### Basic Plugin
+#### Basic Plugin
Below is a simple plugin to add a convenient alias to map `wrapper.element` to `wrapper.$el`
@@ -75,7 +77,7 @@ const wrapper = mount({ template: `
🔌 Plugin
` })
console.log(wrapper.$el.innerHTML) // 🔌 Plugin
```
-### Data Test ID Plugin
+#### Data Test ID Plugin
The below plugin adds a method `findByTestId` to the `VueWrapper` instance. This encourages using a selector strategy relying on test-only attributes on your Vue Components.
@@ -122,6 +124,50 @@ const DataTestIdPlugin = (wrapper) => {
config.plugins.VueWrapper.install(DataTestIdPlugin)
```
+## Stubs Plugin
+
+The `config.plugins.createStubs` allows to overwrite the default stub creation provided by VTU.
+
+Some use cases are:
+* You want to add more logic into the stubs (for example named slots)
+* You want to use different stubs for multiple components (for example stub components from a library)
+
+### Usage
+
+```typescript
+config.plugins.createStubs = ({ name, component }) => {
+ return defineComponent({
+ render: () => h(`custom-${name}-stub`)
+ })
+}
+```
+
+This function will be called everytime VTU generates a stub either from
+```typescript
+const wrapper = mount(Component, {
+ global: {
+ stubs: {
+ ChildComponent: true
+ }
+ }
+})
+```
+or
+```typescript
+const wrapper = shallowMount(Component)
+```
+
+But will not be called, when you explicit set a stub
+```typescript
+const wrapper = mount(Component, {
+ global: {
+ stubs: {
+ ChildComponent: { template: '' }
+ }
+ }
+})
+```
+
## Featuring Your Plugin
If you're missing functionality, consider writing a plugin to extend Vue Test
diff --git a/src/config.ts b/src/config.ts
index b921e1717..2771ba84b 100644
--- a/src/config.ts
+++ b/src/config.ts
@@ -1,12 +1,14 @@
import { GlobalMountOptions } from './types'
import { VueWrapper } from './vueWrapper'
import { DOMWrapper } from './domWrapper'
+import { CustomCreateStub } from './stubs'
export interface GlobalConfigOptions {
global: Required
plugins: {
VueWrapper: Pluggable
DOMWrapper: Pluggable>
+ createStubs?: CustomCreateStub
}
renderStubDefaultSlot: boolean
}
diff --git a/src/stubs.ts b/src/stubs.ts
index 6da5757d1..75a36b98f 100644
--- a/src/stubs.ts
+++ b/src/stubs.ts
@@ -20,6 +20,12 @@ import {
getComponentName,
getComponentRegisteredName
} from './utils/componentName'
+import { config } from './config'
+
+export type CustomCreateStub = (params: {
+ name: string
+ component: ConcreteComponent
+}) => ConcreteComponent
interface StubOptions {
name: string
@@ -259,11 +265,16 @@ export function stubComponents(
}
const newStub = createStubOnce(type, () =>
- createStub({
- name: stubName,
- type,
- renderStubDefaultSlot
- })
+ config.plugins.createStubs
+ ? config.plugins.createStubs({
+ name: stubName,
+ component: type
+ })
+ : createStub({
+ name: stubName,
+ type,
+ renderStubDefaultSlot
+ })
)
registerStub({ source: type, stub: newStub })
return [newStub, props, children, patchFlag, dynamicProps]
diff --git a/tests/features/plugins.spec.ts b/tests/features/plugins.spec.ts
index 64b33ec1a..78d51701d 100644
--- a/tests/features/plugins.spec.ts
+++ b/tests/features/plugins.spec.ts
@@ -1,4 +1,4 @@
-import { ComponentPublicInstance } from 'vue'
+import { ComponentPublicInstance, h } from 'vue'
import { mount, config, VueWrapper } from '../../src'
@@ -92,3 +92,118 @@ describe('Plugin#install', () => {
)
})
})
+
+describe('createStubs', () => {
+ const Child1 = {
+ name: 'child1',
+ render: () => h('div', 'real child 1')
+ }
+ const Child2 = {
+ name: 'child2',
+ render: () => h('div', 'real child 2')
+ }
+
+ const Parent = {
+ render: () => h('div', [h(Child1), h(Child1), h(Child2)])
+ }
+
+ const customCreateStub = jest.fn(({ name }) => h(`${name}-custom-stub`))
+ beforeAll(() => {
+ config.plugins.createStubs = customCreateStub
+ })
+
+ afterAll(() => {
+ config.plugins.createStubs = undefined
+ })
+
+ beforeEach(() => {
+ customCreateStub.mockClear()
+ })
+
+ it('should be called for every stub once', () => {
+ const wrapper = mount(Parent, {
+ shallow: true
+ })
+
+ expect(wrapper.html()).toBe(
+ '\n' +
+ ' \n' +
+ ' \n' +
+ ' \n' +
+ '
'
+ )
+
+ expect(customCreateStub).toHaveBeenCalledTimes(2)
+ expect(customCreateStub).toHaveBeenCalledWith({
+ name: 'child1',
+ component: Child1
+ })
+ expect(customCreateStub).toHaveBeenCalledWith({
+ name: 'child2',
+ component: Child2
+ })
+ })
+
+ it('should be called only for stubbed components', () => {
+ const wrapper = mount(Parent, {
+ global: {
+ stubs: {
+ child2: true
+ }
+ }
+ })
+
+ expect(wrapper.html()).toBe(
+ '\n' +
+ '
real child 1
\n' +
+ '
real child 1
\n' +
+ '
\n' +
+ '
'
+ )
+
+ expect(customCreateStub).toHaveBeenCalledTimes(1)
+ expect(customCreateStub).toHaveBeenCalledWith({
+ name: 'child2',
+ component: Child2
+ })
+ })
+
+ it('should not be called for no stubs', () => {
+ const wrapper = mount(Parent)
+
+ expect(wrapper.html()).toBe(
+ '\n' +
+ '
real child 1
\n' +
+ '
real child 1
\n' +
+ '
real child 2
\n' +
+ '
'
+ )
+
+ expect(customCreateStub).not.toHaveBeenCalled()
+ })
+
+ it('should not be called for manual stubs', () => {
+ const wrapper = mount(Parent, {
+ shallow: true,
+ global: {
+ stubs: {
+ child2: () => h('div', 'Child 2 stub')
+ }
+ }
+ })
+
+ expect(wrapper.html()).toBe(
+ '\n' +
+ '
\n' +
+ '
\n' +
+ '
Child 2 stub
\n' +
+ '
'
+ )
+
+ expect(customCreateStub).toHaveBeenCalledTimes(1)
+ expect(customCreateStub).toHaveBeenCalledWith({
+ name: 'child1',
+ component: Child1
+ })
+ })
+})