From 57a88a1440cf93d6372d7d8de3c00a00541a46b9 Mon Sep 17 00:00:00 2001
From: Illya <iklymov@gitlab.com>
Date: Sun, 30 Apr 2023 07:15:43 +0300
Subject: [PATCH] fix: make scoped slots rendering consistent for stubs

---
 .../create-instance/create-component-stubs.js | 24 +++++++-------
 test/specs/shallow-mount.spec.js              | 32 +++++++++++++++++++
 2 files changed, 43 insertions(+), 13 deletions(-)

diff --git a/packages/create-instance/create-component-stubs.js b/packages/create-instance/create-component-stubs.js
index aa29e0012..00780c9d4 100644
--- a/packages/create-instance/create-component-stubs.js
+++ b/packages/create-instance/create-component-stubs.js
@@ -77,16 +77,8 @@ function getScopedSlotRenderFunctions(ctx: any): Array<string> {
   // In Vue 2.6+ a new v-slot syntax was introduced
   // scopedSlots are now saved in parent._vnode.data.scopedSlots
   // We filter out _normalized, $stable and $key keys
-  if (
-    ctx &&
-    ctx.$options &&
-    ctx.$options.parent &&
-    ctx.$options.parent._vnode &&
-    ctx.$options.parent._vnode.data &&
-    ctx.$options.parent._vnode.data.scopedSlots
-  ) {
-    const slotKeys: Array<string> = ctx.$options.parent._vnode.data.scopedSlots
-    return keys(slotKeys).filter(
+  if (ctx.$vnode.data.scopedSlots) {
+    return keys(ctx.$vnode.data.scopedSlots).filter(
       x => x !== '_normalized' && x !== '$stable' && x !== '$key'
     )
   }
@@ -130,9 +122,15 @@ export function createStubFromComponent(
         context
           ? context.children
           : this.$options._renderChildren ||
-              getScopedSlotRenderFunctions(this).map(x =>
-                this.$options.parent._vnode.data.scopedSlots[x]({})
-              )
+              getScopedSlotRenderFunctions(this)
+                .map(x => {
+                  let result = null
+                  try {
+                    result = this.$vnode.data.scopedSlots[x]({})
+                  } catch (e) {}
+                  return result
+                })
+                .filter(Boolean)
       )
     }
   }
diff --git a/test/specs/shallow-mount.spec.js b/test/specs/shallow-mount.spec.js
index a487e76a3..551ea7943 100644
--- a/test/specs/shallow-mount.spec.js
+++ b/test/specs/shallow-mount.spec.js
@@ -164,6 +164,38 @@ describeRunIf(process.env.TEST_ENV !== 'node', 'shallowMount', () => {
     )
   })
 
+  it('renders named slots with v-slot syntax when they are wrapped', () => {
+    const localVue = createLocalVue()
+    localVue.component('Foo', {
+      template: '<div><slot name="newSyntax" /></div>'
+    })
+    const TestComponent = {
+      template: `
+        <div>
+          <Foo>
+            <template v-slot:newSyntax>
+              <p class="new-example">text</p>
+            </template>
+          </Foo>
+        </div>
+      `
+    }
+    const wrapper = shallowMount(TestComponent, {
+      localVue
+    })
+    expect(wrapper.find({ name: 'Foo' }).exists()).toEqual(true)
+    expect(wrapper.find('.new-example').exists()).toEqual(true)
+    expect(wrapper.html()).toEqual(
+      [
+        '<div>',
+        '  <foo-stub>',
+        '    <p class="new-example">text</p>',
+        '  </foo-stub>',
+        '</div>'
+      ].join('\n')
+    )
+  })
+
   it('renders named slots when they are located inside component with v-if', () => {
     const localVue = createLocalVue()
     localVue.component('Foo', {