diff --git a/docs/guide/advanced/stubs-shallow-mount.md b/docs/guide/advanced/stubs-shallow-mount.md
index 25490fab5..d56166d1b 100644
--- a/docs/guide/advanced/stubs-shallow-mount.md
+++ b/docs/guide/advanced/stubs-shallow-mount.md
@@ -83,6 +83,10 @@ test('stubs component', () => {
This will stub out _all_ the `` components in the entire render tree, regardless of what level they appear at. That's why it is in the `global` mounting option.
+::: tip
+To stub out you can either use the key in `components` or the name of your component. If both are given in `global.stubs` the key will be used first.
+:::
+
## Stubbing all children components
Sometimes you might want to stub out _all_ the custom components. For example you might have a component like this:
@@ -137,7 +141,7 @@ If you used VTU V1, you may remember this as `shallowMount`. That method is stil
## Stubbing an async component
-In case you want to stub out an async component, then make sure to provide a name for the component and use this name as stubs key.
+In case you want to stub out an async component, then there are two behaviours. For example, you might have components like this:
```js
// AsyncComponent.js
@@ -153,13 +157,35 @@ const App = defineComponent({
},
template: ''
})
+```
+
+The first behaviour is using the key defined in your component which loads the async component. In this example we used to key "MyComponent".
+It is not required to use `async/await` in the test case, because the component has been stubbed out before resolving.
-// App.spec.js
-test('stubs async component', async () => {
+```js
+test('stubs async component without resolving', () => {
+ const wrapper = mount(App, {
+ global: {
+ stubs: {
+ MyComponent: true
+ }
+ }
+ })
+
+ expect(wrapper.html()).toBe('')
+})
+```
+
+The second behaviour is using the name of the async component. In this example we used to name "AsyncComponent".
+Now it is required to use `async/await`, because the async component needs to be resolved and then can be stubbed out by the name defined in the async component.
+
+**Make sure you define a name in your async component!**
+
+```js
+test('stubs async component with resolving', async () => {
const wrapper = mount(App, {
global: {
stubs: {
- // Besure to use the name from AsyncComponent and not "MyComponent"
AsyncComponent: true
}
}
diff --git a/src/stubs.ts b/src/stubs.ts
index f50ca084d..2443df0ec 100644
--- a/src/stubs.ts
+++ b/src/stubs.ts
@@ -75,6 +75,23 @@ const resolveComponentStubByName = (
}
}
+const getComponentRegisteredName = (
+ instance: ComponentInternalInstance | null,
+ type: VNodeTypes
+): string | null => {
+ if (!instance || !instance.parent) return null
+
+ // try to infer the name based on local resolution
+ const registry = (instance.type as any).components
+ for (const key in registry) {
+ if (registry[key] === type) {
+ return key
+ }
+ }
+
+ return null
+}
+
const isHTMLElement = (type: VNodeTypes) => typeof type === 'string'
const isCommentOrFragment = (type: VNodeTypes) => typeof type === 'symbol'
@@ -139,27 +156,35 @@ export function stubComponents(
}
if (isComponent(type) || isFunctionalComponent(type)) {
- let name = type['name'] || type['displayName']
-
- // if no name, then check the locally registered components in the parent
- if (!name && instance && instance.parent) {
- // try to infer the name based on local resolution
- const registry = (instance.type as any).components
- for (const key in registry) {
- if (registry[key] === type) {
- name = key
- break
- }
- }
- }
- if (!name) {
+ const registeredName = getComponentRegisteredName(instance, type)
+ const componentName = type['name'] || type['displayName']
+
+ // No name found?
+ if (!registeredName && !componentName) {
return shallow ? ['stub'] : args
}
- const stub = resolveComponentStubByName(name, stubs)
+ let stub = null
+ let name = null
+
+ // Prio 1 using the key in locally registered components in the parent
+ if (registeredName) {
+ stub = resolveComponentStubByName(registeredName, stubs)
+ if (stub) {
+ name = registeredName
+ }
+ }
+
+ // Prio 2 using the name attribute in the component
+ if (!stub && componentName) {
+ stub = resolveComponentStubByName(componentName, stubs)
+ if (stub) {
+ name = componentName
+ }
+ }
// case 2: custom implementation
- if (typeof stub === 'object') {
+ if (stub && typeof stub === 'object') {
// pass the props and children, for advanced stubbing
return [stubs[name], props, children, patchFlag, dynamicProps]
}
@@ -168,6 +193,11 @@ export function stubComponents(
// where the signature is h(Component, props, slots)
// case 1: default stub
if (stub === true || shallow) {
+ // Set name when using shallow without stub
+ if (!name) {
+ name = registeredName || componentName
+ }
+
const propsDeclaration = type?.props || {}
const newStub = createStub({ name, propsDeclaration, props })
stubs[name] = newStub
diff --git a/tests/__snapshots__/shallowMount.spec.ts.snap b/tests/__snapshots__/shallowMount.spec.ts.snap
index dc4213466..a757d11b8 100644
--- a/tests/__snapshots__/shallowMount.spec.ts.snap
+++ b/tests/__snapshots__/shallowMount.spec.ts.snap
@@ -1,3 +1,8 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`shallowMount renders props for stubbed component in a snapshot 1`] = ``;
+exports[`shallowMount renders props for stubbed component in a snapshot 1`] = `
+