@@ -16,6 +16,7 @@ import {
1616 renderEffect ,
1717 setClass ,
1818 setDynamicProps ,
19+ setInsertionState ,
1920 setProp ,
2021 setStyle ,
2122 template ,
@@ -98,12 +99,12 @@ describe('attribute fallthrough', () => {
9899 return n0
99100 } )
100101
101- const { host : root } = define ( Hello ) . render ( )
102- expect ( root . innerHTML ) . toBe (
102+ const { host } = define ( Hello ) . render ( )
103+ expect ( host . innerHTML ) . toBe (
103104 '<div class="c2 c0" style="font-weight: bold; color: green;">1</div>' ,
104105 )
105106
106- const node = root . children [ 0 ] as HTMLElement
107+ const node = host . children [ 0 ] as HTMLElement
107108
108109 // not whitelisted
109110 expect ( node . getAttribute ( 'id' ) ) . toBe ( null )
@@ -128,7 +129,6 @@ describe('attribute fallthrough', () => {
128129 it ( 'should allow all attrs on functional component with declared props' , async ( ) => {
129130 const click = vi . fn ( )
130131 const childUpdated = vi . fn ( )
131-
132132 const count = ref ( 0 )
133133
134134 function inc ( ) {
@@ -157,8 +157,8 @@ describe('attribute fallthrough', () => {
157157
158158 Child . props = [ 'foo' ]
159159
160- const { host : root } = define ( Hello ) . render ( )
161- const node = root . children [ 0 ] as HTMLElement
160+ const { host } = define ( Hello ) . render ( )
161+ const node = host . children [ 0 ] as HTMLElement
162162
163163 expect ( node . getAttribute ( 'id' ) ) . toBe ( 'test' )
164164 expect ( node . getAttribute ( 'foo' ) ) . toBe ( null ) // declared as prop
@@ -232,12 +232,12 @@ describe('attribute fallthrough', () => {
232232 } ,
233233 } )
234234
235- const { host : root } = define ( Hello ) . render ( )
236- expect ( root . innerHTML ) . toBe (
235+ const { host } = define ( Hello ) . render ( )
236+ expect ( host . innerHTML ) . toBe (
237237 '<div class="c2 c0" style="font-weight: bold; color: green;" id="test">1</div>' ,
238238 )
239239
240- const node = root . children [ 0 ] as HTMLElement
240+ const node = host . children [ 0 ] as HTMLElement
241241
242242 // with declared props, any parent attr that isn't a prop falls through
243243 expect ( node . getAttribute ( 'id' ) ) . toBe ( 'test' )
@@ -343,20 +343,20 @@ describe('attribute fallthrough', () => {
343343 } ,
344344 }
345345
346- const root = document . createElement ( 'div' )
346+ const target = document . createElement ( 'div' )
347347 const Child = defineVaporComponent ( {
348348 render ( ) {
349349 return createComponent (
350350 VaporTeleport ,
351- { to : ( ) => root } ,
351+ { to : ( ) => target } ,
352352 {
353353 default : ( ) => template ( '<div></div>' ) ( ) ,
354354 } ,
355355 )
356356 } ,
357357 } )
358358
359- document . body . appendChild ( root )
359+ document . body . appendChild ( target )
360360 define ( Parent ) . render ( )
361361
362362 expect ( `Extraneous non-props attributes (class)` ) . toHaveBeenWarned ( )
@@ -473,8 +473,6 @@ describe('attribute fallthrough', () => {
473473 return [ n0 , n1 ]
474474 } )
475475
476- const root = document . createElement ( 'div' )
477- document . body . appendChild ( root )
478476 const { html } = define ( Parent ) . render ( )
479477
480478 expect ( `Extraneous non-props attributes` ) . not . toHaveBeenWarned ( )
@@ -534,8 +532,8 @@ describe('attribute fallthrough', () => {
534532 } ,
535533 } )
536534
537- const { host : root } = define ( App ) . render ( )
538- const node = root . children [ 0 ] as HTMLElement
535+ const { host } = define ( App ) . render ( )
536+ const node = host . children [ 0 ] as HTMLElement
539537 node . click ( )
540538 expect ( onClick ) . toHaveBeenCalledTimes ( 1 )
541539 expect ( onClick ) . toHaveBeenCalledWith ( 'custom' )
@@ -562,8 +560,8 @@ describe('attribute fallthrough', () => {
562560 } ,
563561 } )
564562
565- const { host : root } = define ( App ) . render ( )
566- const node = root . children [ 0 ] as HTMLElement
563+ const { host } = define ( App ) . render ( )
564+ const node = host . children [ 0 ] as HTMLElement
567565 node . click ( )
568566 expect ( onClick ) . toHaveBeenCalledTimes ( 1 )
569567 expect ( onClick ) . toHaveBeenCalledWith ( 'custom' )
@@ -591,151 +589,139 @@ describe('attribute fallthrough', () => {
591589 } ,
592590 } )
593591
594- const { host : root } = define ( Hello ) . render ( )
592+ const { host } = define ( Hello ) . render ( )
595593
596- expect ( root . innerHTML ) . toBe (
594+ expect ( host . innerHTML ) . toBe (
597595 `<!--hello--><button class="foo"></button><!--world-->` ,
598596 )
599- const button = root . children [ 0 ] as HTMLElement
597+ const button = host . children [ 0 ] as HTMLElement
600598 button . dispatchEvent ( new CustomEvent ( 'click' ) )
601599 expect ( click ) . toHaveBeenCalled ( )
602600 } )
603601
604- // it('should support fallthrough for nested dev root fragments', async () => {
605- // const toggle = ref(false)
606-
607- // const Child = {
608- // setup() {
609- // return () => (
610- // openBlock(),
611- // createElementBlock(
612- // Fragment,
613- // null,
614- // [
615- // createCommentVNode(' comment A '),
616- // toggle.value
617- // ? (openBlock(), createElementBlock('span', { key: 0 }, 'Foo'))
618- // : (openBlock(),
619- // createElementBlock(
620- // Fragment,
621- // { key: 1 },
622- // [
623- // createCommentVNode(' comment B '),
624- // createElementVNode('div', null, 'Bar'),
625- // ],
626- // PatchFlags.STABLE_FRAGMENT | PatchFlags.DEV_ROOT_FRAGMENT,
627- // )),
628- // ],
629- // PatchFlags.STABLE_FRAGMENT | PatchFlags.DEV_ROOT_FRAGMENT,
630- // )
631- // )
632- // },
633- // }
634-
635- // const Root = {
636- // setup() {
637- // return () => (openBlock(), createBlock(Child, { class: 'red' }))
638- // },
639- // }
640-
641- // const root = document.createElement('div')
642- // document.body.appendChild(root)
643- // render(h(Root), root)
644-
645- // expect(root.innerHTML).toBe(
646- // `<!-- comment A --><!-- comment B --><div class="red">Bar</div>`,
647- // )
648-
649- // toggle.value = true
650- // await nextTick()
651- // expect(root.innerHTML).toBe(
652- // `<!-- comment A --><span class=\"red\">Foo</span>`,
653- // )
654- // })
655-
656- // // #1989
657- // it('should not fallthrough v-model listeners with corresponding declared prop', () => {
658- // let textFoo = ''
659- // let textBar = ''
660- // const click = vi.fn()
661-
662- // const App = defineVaporComponent({
663- // setup() {
664- // return () =>
665- // h(Child, {
666- // modelValue: textFoo,
667- // 'onUpdate:modelValue': (val: string) => {
668- // textFoo = val
669- // },
670- // })
671- // },
672- // })
673-
674- // const Child = defineVaporComponent({
675- // props: ['modelValue'],
676- // setup(_props, { emit }) {
677- // return () =>
678- // h(GrandChild, {
679- // modelValue: textBar,
680- // 'onUpdate:modelValue': (val: string) => {
681- // textBar = val
682- // emit('update:modelValue', 'from Child')
683- // },
684- // })
685- // },
686- // })
687-
688- // const GrandChild = defineVaporComponent({
689- // props: ['modelValue'],
690- // setup(_props, { emit }) {
691- // return () =>
692- // h('button', {
693- // onClick() {
694- // click()
695- // emit('update:modelValue', 'from GrandChild')
696- // },
697- // })
698- // },
699- // })
700-
701- // const root = document.createElement('div')
702- // document.body.appendChild(root)
703- // render(h(App), root)
704-
705- // const node = root.children[0] as HTMLElement
706-
707- // node.dispatchEvent(new CustomEvent('click'))
708- // expect(click).toHaveBeenCalled()
709- // expect(textBar).toBe('from GrandChild')
710- // expect(textFoo).toBe('from Child')
711- // })
712-
713- // // covers uncaught regression #10710
714- // it('should track this.$attrs access in slots', async () => {
715- // const GrandChild = {
716- // template: `<slot/>`,
717- // }
718- // const Child = {
719- // components: { GrandChild },
720- // template: `<div><GrandChild>{{ $attrs.foo }}</GrandChild></div>`,
721- // }
722-
723- // const obj = ref(1)
724- // const App = {
725- // render() {
726- // return h(Child, { foo: obj.value })
727- // },
728- // }
729-
730- // const root = document.createElement('div')
731- // createApp(App).mount(root)
732-
733- // expect(root.innerHTML).toBe('<div foo="1">1</div>')
734-
735- // obj.value = 2
736- // await nextTick()
737- // expect(root.innerHTML).toBe('<div foo="2">2</div>')
738- // })
602+ it ( 'should support fallthrough for nested element + comments' , async ( ) => {
603+ const toggle = ref ( false )
604+ const Child = defineVaporComponent ( {
605+ setup ( ) {
606+ const n0 = template ( '<!-- comment A -->' ) ( ) as any
607+ const n1 = createIf (
608+ ( ) => toggle . value ,
609+ ( ) => template ( '<span>Foo</span>' ) ( ) ,
610+ ( ) => {
611+ const n2 = template ( '<!-- comment B -->' ) ( ) as any
612+ const n3 = template ( '<div>Bar</div>' ) ( ) as any
613+ return [ n2 , n3 ]
614+ } ,
615+ )
616+ return [ n0 , n1 ]
617+ } ,
618+ } )
619+
620+ const Root = defineVaporComponent ( {
621+ setup ( ) {
622+ return createComponent ( Child , { class : ( ) => 'red' } )
623+ } ,
624+ } )
625+
626+ const { host } = define ( Root ) . render ( )
627+
628+ expect ( host . innerHTML ) . toBe (
629+ `<!-- comment A --><!-- comment B --><div class="red">Bar</div><!--if-->` ,
630+ )
631+
632+ toggle . value = true
633+ await nextTick ( )
634+ expect ( host . innerHTML ) . toBe (
635+ `<!-- comment A --><span class=\"red\">Foo</span><!--if-->` ,
636+ )
637+ } )
638+
639+ it ( 'should not fallthrough v-model listeners with corresponding declared prop' , ( ) => {
640+ let textFoo = ''
641+ let textBar = ''
642+ const click = vi . fn ( )
643+
644+ const App = defineVaporComponent ( {
645+ render ( ) {
646+ return createComponent ( Child , {
647+ modelValue : ( ) => textFoo ,
648+ 'onUpdate:modelValue' : ( ) => ( val : string ) => {
649+ textFoo = val
650+ } ,
651+ } )
652+ } ,
653+ } )
654+
655+ const Child = defineVaporComponent ( {
656+ props : [ 'modelValue' ] ,
657+ setup ( _props , { emit } ) {
658+ return createComponent ( GrandChild , {
659+ modelValue : ( ) => textBar ,
660+ 'onUpdate:modelValue' : ( ) => ( val : string ) => {
661+ textBar = val
662+ emit ( 'update:modelValue' , 'from Child' )
663+ } ,
664+ } )
665+ } ,
666+ } )
667+
668+ const GrandChild = defineVaporComponent ( {
669+ props : [ 'modelValue' ] ,
670+ setup ( _props , { emit } ) {
671+ const n0 = template ( '<button></button>' ) ( ) as any
672+ n0 . $evtclick = ( ) => {
673+ click ( )
674+ emit ( 'update:modelValue' , 'from GrandChild' )
675+ }
676+ return n0
677+ } ,
678+ } )
679+
680+ const { host } = define ( App ) . render ( )
681+ const node = host . children [ 0 ] as HTMLElement
682+ node . click ( )
683+ expect ( click ) . toHaveBeenCalled ( )
684+ expect ( textBar ) . toBe ( 'from GrandChild' )
685+ expect ( textFoo ) . toBe ( 'from Child' )
686+ } )
687+
688+ it ( 'should track this.$attrs access in slots' , async ( ) => {
689+ const GrandChild = defineVaporComponent ( {
690+ render ( ) {
691+ return createSlot ( 'default' )
692+ } ,
693+ } )
694+ const Child = defineVaporComponent ( {
695+ // @ts -expect-error
696+ components : { GrandChild } ,
697+ render ( _ctx , $props , $emit , $attrs , $slots ) {
698+ const n0 = template ( '<div></div>' ) ( ) as any
699+ setInsertionState ( n0 )
700+ createComponent ( GrandChild , null , {
701+ default : ( ) => {
702+ const n1 = template ( ' ' ) ( )
703+ renderEffect ( ( ) => setElementText ( n1 , $attrs . foo ) )
704+ return n1
705+ } ,
706+ } )
707+ return n0
708+ } ,
709+ } )
710+
711+ const obj = ref ( 1 )
712+ const App = defineVaporComponent ( {
713+ render ( ) {
714+ return createComponent ( Child , { foo : ( ) => obj . value } )
715+ } ,
716+ } )
717+
718+ const { html } = define ( App ) . render ( )
719+ expect ( html ( ) ) . toBe ( '<div foo="1">1<!--slot--></div>' )
720+
721+ obj . value = 2
722+ await nextTick ( )
723+ expect ( html ( ) ) . toBe ( '<div foo="2">2<!--slot--></div>' )
724+ } )
739725
740726 it ( 'should allow attrs to fallthrough on component with comment at root' , async ( ) => {
741727 const t0 = template ( '<!--comment-->' )
0 commit comments