@@ -11,6 +11,8 @@ import {
1111} from '@vue/runtime-test'
1212import { createBlock , normalizeVNode } from '../src/vnode'
1313import { createSlots } from '../src/helpers/createSlots'
14+ import { renderSlot } from '../src/helpers/renderSlot'
15+ import { setCurrentRenderingInstance } from '../src/componentRenderContext'
1416
1517describe ( 'component: slots' , ( ) => {
1618 function renderWithSlots ( slots : any ) : any {
@@ -461,4 +463,118 @@ describe('component: slots', () => {
461463 createApp ( App ) . mount ( root )
462464 expect ( serializeInner ( root ) ) . toBe ( 'foo' )
463465 } )
466+
467+ // in-DOM templates use kebab-case slot names
468+ describe ( 'in-DOM template kebab-case slot name resolution' , ( ) => {
469+ beforeEach ( ( ) => {
470+ __BROWSER__ = true
471+ } )
472+
473+ afterEach ( ( ) => {
474+ __BROWSER__ = false
475+ } )
476+
477+ test ( 'should resolve camelCase slot access to kebab-case via slots' , ( ) => {
478+ const Comp = {
479+ setup ( _ : any , { slots } : any ) {
480+ // Access with camelCase, but slot is passed with kebab-case
481+ return ( ) => slots . dropdownRender ( )
482+ } ,
483+ }
484+
485+ const App = {
486+ setup ( ) {
487+ // Parent passes slot with kebab-case name (simulating in-DOM template)
488+ return ( ) =>
489+ h ( Comp , null , { 'dropdown-render' : ( ) => 'dropdown content' } )
490+ } ,
491+ }
492+
493+ const root = nodeOps . createElement ( 'div' )
494+ createApp ( App ) . mount ( root )
495+ expect ( serializeInner ( root ) ) . toBe ( 'dropdown content' )
496+ } )
497+
498+ test ( 'should resolve camelCase slot access to kebab-case via slots (PROD)' , ( ) => {
499+ __DEV__ = false
500+ try {
501+ const Comp = {
502+ setup ( _ : any , { slots } : any ) {
503+ // Access with camelCase, but slot is passed with kebab-case
504+ return ( ) => slots . dropdownRender ( )
505+ } ,
506+ }
507+
508+ const App = {
509+ setup ( ) {
510+ // Parent passes slot with kebab-case name (simulating in-DOM template)
511+ return ( ) =>
512+ h ( Comp , null , { 'dropdown-render' : ( ) => 'dropdown content' } )
513+ } ,
514+ }
515+
516+ const root = nodeOps . createElement ( 'div' )
517+ createApp ( App ) . mount ( root )
518+ expect ( serializeInner ( root ) ) . toBe ( 'dropdown content' )
519+ } finally {
520+ __DEV__ = true
521+ }
522+ } )
523+
524+ test ( 'should prefer exact match over kebab-case conversion via slots' , ( ) => {
525+ const Comp = {
526+ setup ( _ : any , { slots } : any ) {
527+ return ( ) => slots . dropdownRender ( )
528+ } ,
529+ }
530+
531+ const App = {
532+ setup ( ) {
533+ // Both exact match and kebab-case exist
534+ return ( ) =>
535+ h ( Comp , null , {
536+ 'dropdown-render' : ( ) => 'kebab' ,
537+ dropdownRender : ( ) => 'exact' ,
538+ } )
539+ } ,
540+ }
541+
542+ const root = nodeOps . createElement ( 'div' )
543+ createApp ( App ) . mount ( root )
544+ // exact match should take priority
545+ expect ( serializeInner ( root ) ) . toBe ( 'exact' )
546+ } )
547+
548+ // renderSlot tests
549+ describe ( 'renderSlot' , ( ) => {
550+ beforeEach ( ( ) => {
551+ setCurrentRenderingInstance ( { type : { } } as any )
552+ } )
553+
554+ afterEach ( ( ) => {
555+ setCurrentRenderingInstance ( null )
556+ } )
557+
558+ test ( 'should resolve camelCase slot name to kebab-case via renderSlot' , ( ) => {
559+ let child : any
560+ const vnode = renderSlot (
561+ { 'dropdown-render' : ( ) => [ ( child = h ( 'child' ) ) ] } ,
562+ 'dropdownRender' ,
563+ )
564+ expect ( vnode . children ) . toEqual ( [ child ] )
565+ } )
566+
567+ test ( 'should prefer exact match over kebab-case conversion via renderSlot' , ( ) => {
568+ let exactChild : any
569+ const vnode = renderSlot (
570+ {
571+ 'dropdown-render' : ( ) => [ h ( 'kebab' ) ] ,
572+ dropdownRender : ( ) => [ ( exactChild = h ( 'exact' ) ) ] ,
573+ } ,
574+ 'dropdownRender' ,
575+ )
576+ expect ( vnode . children ) . toEqual ( [ exactChild ] )
577+ } )
578+ } )
579+ } )
464580} )
0 commit comments