Skip to content

Commit 1d273c2

Browse files
committed
issue-249 feat: stub global components
1 parent 0b4f762 commit 1d273c2

File tree

3 files changed

+44
-3
lines changed

3 files changed

+44
-3
lines changed

src/mount.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ import { createWrapper, VueWrapper } from './vueWrapper'
3434
import { attachEmitListener } from './emitMixin'
3535
import { createDataMixin } from './dataMixin'
3636
import { MOUNT_COMPONENT_REF, MOUNT_PARENT_NAME } from './constants'
37-
import { stubComponents } from './stubs'
37+
import { createStub, stubComponents } from './stubs'
38+
import { hyphenate } from './utils/vueShared'
3839

3940
// NOTE this should come from `vue`
4041
type PublicProps = VNodeProps & AllowedComponentProps & ComponentCustomProps
@@ -405,6 +406,23 @@ export function mount(
405406
// stub out Transition and Transition Group by default.
406407
stubComponents(global.stubs, options?.shallow)
407408

409+
// users expect stubs to work with globally registered
410+
// compnents, too, such as <router-link> and <router-view>
411+
// so we register those globally.
412+
// https://github.com/vuejs/vue-test-utils-next/issues/249
413+
if (options?.global?.stubs) {
414+
for (const [name, stub] of Object.entries(options.global.stubs)) {
415+
const tag = hyphenate(name)
416+
if (stub === true) {
417+
// default stub.
418+
app.component(tag, createStub({ name, props: {} }))
419+
} else {
420+
// user has provided a custom implementation.
421+
app.component(tag, stub)
422+
}
423+
}
424+
}
425+
408426
// mount the app!
409427
const vm = app.mount(el)
410428

src/stubs.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ function getSlots(ctx: ComponentPublicInstance): Slots | undefined {
2525
return !config.renderStubDefaultSlot ? undefined : ctx.$slots
2626
}
2727

28-
const createStub = ({ name, props }: StubOptions): ComponentOptions => {
28+
export const createStub = ({ name, props }: StubOptions): ComponentOptions => {
2929
const anonName = 'anonymous-stub'
3030
const tag = name ? `${hyphenate(name)}-stub` : anonName
3131

tests/mountingOptions/stubs.global.spec.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { h, ComponentOptions } from 'vue'
1+
import { h, ComponentOptions, defineComponent } from 'vue'
22

33
import { config, mount } from '../../src'
44
import Hello from '../components/Hello.vue'
@@ -37,6 +37,29 @@ describe('mounting options: stubs', () => {
3737
expect(wrapper.html()).toBe('<div></div><foo-stub></foo-stub>')
3838
})
3939

40+
// https://github.com/vuejs/vue-test-utils-next/issues/249
41+
it('applies stubs globally', () => {
42+
const Comp = defineComponent({
43+
template: '<div><router-link /><router-view /></div>'
44+
})
45+
const wrapper = mount(Comp, {
46+
global: {
47+
stubs: {
48+
RouterLink: true,
49+
RouterView: defineComponent({
50+
render() {
51+
return h('span')
52+
}
53+
})
54+
}
55+
}
56+
})
57+
58+
expect(wrapper.html()).toBe(
59+
'<div><router-link-stub></router-link-stub><span></span></div>'
60+
)
61+
})
62+
4063
it('stubs a functional component by its variable declaration name', () => {
4164
const FunctionalFoo = (props) => h('p', props, 'Foo Text')
4265

0 commit comments

Comments
 (0)