Repository: vuejs/test-utils (ts)

Issue:
Bug: Scoped slots always undefined when shallow mounting and `renderStubDefaultSlot` is enabled
<!-- Thanks for your interest in the project! We appreciate bugs filed and PRs submitted! -->


**Describe the bug**
When `renderStubDefaultSlot` is enabled, if you `shallowMount` a component that renders another component that has a scoped slot, then an error is thrown stating that the slot is undefined. This used to work on vue test utils v1, and is making my current Vue 2.7 > 3 migration quite difficult.

**To Reproduce**
TLDR Here's an example repo https://github.com/tom751/vtu-slot-issue - The `HelloWorld.spec.ts` will fail

Have a component that renders a child component that has a scoped slot:
```vue
<template>
  <div class="greetings">
    <h1 class="green">{{ msg }}</h1>
    <ComponentWithSlot v-slot="{ count }">
      <h2>The count is {{ count }}</h2>
    </ComponentWithSlot>
  </div>
</template>
```

Render the component with `shallowMount` and `renderStubDefaultSlot` enabled

```ts
describe('HelloWorld', () => {
  beforeAll(() => {
    config.global.renderStubDefaultSlot = true
  })

  it('renders properly', () => {
    const wrapper = shallowMount(HelloWorld, { props: { msg: 'Hello Vitest' } })
    expect(wrapper.text()).toContain('Hello Vitest')
  })
})
```

The following error is thrown after running the test:

```
FAIL  src/components/__tests__/HelloWorld.spec.ts > HelloWorld > renders properly
TypeError: Cannot destructure property 'count' of 'undefined' as it is undefined.
❯ src/components/HelloWorld.vue:12:34
     10|   <div class="greetings">
     11|     <h1 class="green">{{ msg }}</h1>
     12|     <ComponentWithSlot v-slot="{ count }">
       |                                  ^
     13|       <h2>The count is {{ count }}</h2>
     14|     </ComponentWithSlot>
```

**Expected behavior**
An error shouldn't be thrown, the behaviour should be the same as it is in Vue 2 + vue test utils v1

**Related information:**
<!-- Output of  `npx envinfo --system --npmPackages vue,@vue/test-utils,vitest,jest` -->
<!-- For test-utils v1 and Vue v2, please report at repository https://github.com/vuejs/vue-test-utils -->

**Additional context**
This used to work in Vue 2.7 and vue test utils v1


Pull request:
fix: renderStubDefaultSlot with scoped slots
Fixes #2395

We now explicitely call the default slot with an empty object to ensure that `<ComponentWithSlot v-slot="{ count }">` won't throw when tested with `renderStubDefaultSlot`.

Patch:
diff --git a/src/vnodeTransformers/stubComponentsTransformer.ts b/src/vnodeTransformers/stubComponentsTransformer.ts
index 7f1a110..2a9788b 100644
--- a/src/vnodeTransformers/stubComponentsTransformer.ts
+++ b/src/vnodeTransformers/stubComponentsTransformer.ts
@@ -117,8 +117,13 @@ export const createStub = ({
         // Also having function text as attribute is useless and annoying so
         // we replace it with "[Function]""
         const stubProps = normalizeStubProps(props)
-
-        return h(tag, stubProps, renderStubDefaultSlot ? slots : undefined)
+        // if renderStubDefaultSlot is true, we render the default slot
+        if (renderStubDefaultSlot && slots.default) {
+          // we explicitly call the default slot with an empty object
+          // so scope slots destructuring works
+          return h(tag, stubProps, slots.default({}))
+        }
+        return h(tag, stubProps)
       }
     }
   })


Test patch:
diff --git a/tests/mountingOptions/global.components.spec.ts b/tests/mountingOptions/global.components.spec.ts
index f60225c..46c8c3d 100644
--- a/tests/mountingOptions/global.components.spec.ts
+++ b/tests/mountingOptions/global.components.spec.ts
@@ -72,6 +72,7 @@ describe('global.components', () => {
     spy.mockRestore()
     expect(wrapper.text()).toBe('Global')
   })
+
   it('render children with shallow and renderStubDefaultSlot', () => {
     const Child = defineComponent({
       template: '<div><p>child</p><slot /></div>'
@@ -97,4 +98,33 @@ describe('global.components', () => {
         '</div>'
     )
   })
+
+  // https://github.com/vuejs/test-utils/issues/2395
+  it('render children with shallow and renderStubDefaultSlot with v-slot', () => {
+    const Child = defineComponent({
+      template: '<div><p>child</p><slot /></div>'
+    })
+    const Component = defineComponent({
+      template:
+        '<div><Child v-slot="{ count }"><div>hello{{ count }}there</div></Child></div>',
+      components: {
+        Child
+      }
+    })
+    const wrapper = mount(Component, {
+      shallow: true,
+      global: {
+        renderStubDefaultSlot: true
+      }
+    })
+
+    // count is undefined, but doesn't throw an error
+    expect(wrapper.html()).toEqual(
+      '<div>\n' +
+        '  <child-stub>\n' +
+        '    <div>hellothere</div>\n' +
+        '  </child-stub>\n' +
+        '</div>'
+    )
+  })
 })