test/unit/features/component/component-scoped-slot.spec.ts TYPESCRIPT 1,407 lines View on github.com → Search inside
1import Vue from 'vue'23describe('Component scoped slot', () => {4  it('default slot', done => {5    const vm = new Vue({6      template: `7        <test ref="test">8          <template slot-scope="props">9            <span>{{ props.msg }}</span>10          </template>11        </test>12      `,13      components: {14        test: {15          data() {16            return { msg: 'hello' }17          },18          template: `19            <div>20              <slot :msg="msg"></slot>21            </div>22          `23        }24      }25    }).$mount()2627    expect(vm.$el.innerHTML).toBe('<span>hello</span>')28    vm.$refs.test.msg = 'world'29    waitForUpdate(() => {30      expect(vm.$el.innerHTML).toBe('<span>world</span>')31    }).then(done)32  })3334  it('default slot (plain element)', done => {35    const vm = new Vue({36      template: `37        <test ref="test">38          <span slot-scope="props">{{ props.msg }}</span>39        </test>40      `,41      components: {42        test: {43          data() {44            return { msg: 'hello' }45          },46          template: `47            <div>48              <slot :msg="msg"></slot>49            </div>50          `51        }52      }53    }).$mount()5455    expect(vm.$el.innerHTML).toBe('<span>hello</span>')56    vm.$refs.test.msg = 'world'57    waitForUpdate(() => {58      expect(vm.$el.innerHTML).toBe('<span>world</span>')59    }).then(done)60  })6162  it('with v-bind', done => {63    const vm = new Vue({64      template: `65        <test ref="test">66          <template slot-scope="props">67            <span>{{ props.msg }} {{ props.msg2 }} {{ props.msg3 }}</span>68          </template>69        </test>70      `,71      components: {72        test: {73          data() {74            return {75              msg: 'hello',76              obj: { msg2: 'world', msg3: '.' }77            }78          },79          template: `80            <div>81              <slot :msg="msg" v-bind="obj" msg3="!"></slot>82            </div>83          `84        }85      }86    }).$mount()8788    expect(vm.$el.innerHTML).toBe('<span>hello world !</span>')89    vm.$refs.test.msg = 'bye'90    vm.$refs.test.obj.msg2 = 'bye'91    waitForUpdate(() => {92      expect(vm.$el.innerHTML).toBe('<span>bye bye !</span>')93    }).then(done)94  })9596  it('should warn when using v-bind with no object', () => {97    new Vue({98      template: `99        <test ref="test">100          <template scope="props">101          </template>102        </test>103      `,104      components: {105        test: {106          data() {107            return {108              text: 'some text'109            }110          },111          template: `112            <div>113              <slot v-bind="text"></slot>114            </div>115          `116        }117      }118    }).$mount()119    expect('slot v-bind without argument expects an Object').toHaveBeenWarned()120  })121122  it('should not warn when using v-bind with object', () => {123    new Vue({124      template: `125        <test ref="test">126          <template scope="props">127          </template>128        </test>129      `,130      components: {131        test: {132          data() {133            return {134              foo: {135                text: 'some text'136              }137            }138          },139          template: `140            <div>141              <slot v-bind="foo"></slot>142            </div>143          `144        }145      }146    }).$mount()147    expect(148      'slot v-bind without argument expects an Object'149    ).not.toHaveBeenWarned()150  })151152  it('named scoped slot', done => {153    const vm = new Vue({154      template: `155        <test ref="test">156          <template slot="item" slot-scope="props">157            <span>{{ props.foo }}</span><span>{{ props.bar }}</span>158          </template>159        </test>160      `,161      components: {162        test: {163          data() {164            return { foo: 'FOO', bar: 'BAR' }165          },166          template: `167            <div>168              <slot name="item" :foo="foo" :bar="bar"></slot>169            </div>170          `171        }172      }173    }).$mount()174175    expect(vm.$el.innerHTML).toBe('<span>FOO</span><span>BAR</span>')176    vm.$refs.test.foo = 'BAZ'177    waitForUpdate(() => {178      expect(vm.$el.innerHTML).toBe('<span>BAZ</span><span>BAR</span>')179    }).then(done)180  })181182  it('named scoped slot (plain element)', done => {183    const vm = new Vue({184      template: `185        <test ref="test">186          <span slot="item" slot-scope="props">{{ props.foo }} {{ props.bar }}</span>187        </test>188      `,189      components: {190        test: {191          data() {192            return { foo: 'FOO', bar: 'BAR' }193          },194          template: `195            <div>196              <slot name="item" :foo="foo" :bar="bar"></slot>197            </div>198          `199        }200      }201    }).$mount()202203    expect(vm.$el.innerHTML).toBe('<span>FOO BAR</span>')204    vm.$refs.test.foo = 'BAZ'205    waitForUpdate(() => {206      expect(vm.$el.innerHTML).toBe('<span>BAZ BAR</span>')207    }).then(done)208  })209210  it('fallback content', () => {211    const vm = new Vue({212      template: `<test></test>`,213      components: {214        test: {215          data() {216            return { msg: 'hello' }217          },218          template: `219            <div>220              <slot name="item" :text="msg">221                <span>{{ msg }} fallback</span>222              </slot>223            </div>224          `225        }226      }227    }).$mount()228    expect(vm.$el.innerHTML).toBe('<span>hello fallback</span>')229  })230231  it('slot with v-for', done => {232    const vm = new Vue({233      template: `234        <test ref="test">235          <template slot="item" slot-scope="props">236            <span>{{ props.text }}</span>237          </template>238        </test>239      `,240      components: {241        test: {242          data() {243            return {244              items: ['foo', 'bar', 'baz']245            }246          },247          template: `248            <div>249              <slot v-for="item in items" name="item" :text="item"></slot>250            </div>251          `252        }253      }254    }).$mount()255256    function assertOutput() {257      expect(vm.$el.innerHTML).toBe(258        vm.$refs.test.items259          .map(item => {260            return `<span>${item}</span>`261          })262          .join('')263      )264    }265266    assertOutput()267    vm.$refs.test.items.reverse()268    waitForUpdate(assertOutput)269      .then(() => {270        vm.$refs.test.items.push('qux')271      })272      .then(assertOutput)273      .then(done)274  })275276  it('slot inside v-for', done => {277    const vm = new Vue({278      template: `279        <test ref="test">280          <template slot="item" slot-scope="props">281            <span>{{ props.text }}</span>282          </template>283        </test>284      `,285      components: {286        test: {287          data() {288            return {289              items: ['foo', 'bar', 'baz']290            }291          },292          template: `293            <ul>294              <li v-for="item in items">295                <slot name="item" :text="item"></slot>296              </li>297            </ul>298          `299        }300      }301    }).$mount()302303    function assertOutput() {304      expect(vm.$el.innerHTML).toBe(305        vm.$refs.test.items306          .map(item => {307            return `<li><span>${item}</span></li>`308          })309          .join('')310      )311    }312313    assertOutput()314    vm.$refs.test.items.reverse()315    waitForUpdate(assertOutput)316      .then(() => {317        vm.$refs.test.items.push('qux')318      })319      .then(assertOutput)320      .then(done)321  })322323  it('scoped slot without scope alias', () => {324    const vm = new Vue({325      template: `326        <test ref="test">327          <span slot="item">I am static</span>328        </test>329      `,330      components: {331        test: {332          data() {333            return { msg: 'hello' }334          },335          template: `336            <div>337              <slot name="item" :text="msg"></slot>338            </div>339          `340        }341      }342    }).$mount()343    expect(vm.$el.innerHTML).toBe('<span>I am static</span>')344  })345346  it('non-scoped slot with scope alias', () => {347    const vm = new Vue({348      template: `349        <test ref="test">350          <template slot="item" slot-scope="props">351            <span>{{ props.text || 'meh' }}</span>352          </template>353        </test>354      `,355      components: {356        test: {357          data() {358            return { msg: 'hello' }359          },360          template: `361            <div>362              <slot name="item"></slot>363            </div>364          `365        }366      }367    }).$mount()368    expect(vm.$el.innerHTML).toBe('<span>meh</span>')369  })370371  it('warn key on slot', () => {372    new Vue({373      template: `374        <test ref="test">375          <template slot="item" slot-scope="props">376            <span>{{ props.text }}</span>377          </template>378        </test>379      `,380      components: {381        test: {382          data() {383            return {384              items: ['foo', 'bar', 'baz']385            }386          },387          template: `388            <div>389              <slot v-for="item in items" name="item" :text="item" :key="item"></slot>390            </div>391          `392        }393      }394    }).$mount()395    expect(`\`key\` does not work on <slot>`).toHaveBeenWarned()396  })397398  it('render function usage (named, via data)', done => {399    const vm = new Vue({400      render(h) {401        return h('test', {402          ref: 'test',403          scopedSlots: {404            item: props => h('span', props.text)405          }406        })407      },408      components: {409        test: {410          data() {411            return { msg: 'hello' }412          },413          render(h) {414            return h(415              'div',416              this.$scopedSlots.item({417                text: this.msg418              })419            )420          }421        }422      }423    }).$mount()424425    expect(vm.$el.innerHTML).toBe('<span>hello</span>')426    vm.$refs.test.msg = 'world'427    waitForUpdate(() => {428      expect(vm.$el.innerHTML).toBe('<span>world</span>')429    }).then(done)430  })431432  it('render function usage (default, as children)', () => {433    const vm = new Vue({434      render(h) {435        return h('test', [props => h('span', [props.msg])])436      },437      components: {438        test: {439          data() {440            return { msg: 'hello' }441          },442          render(h) {443            return h('div', this.$scopedSlots.default({ msg: this.msg }))444          }445        }446      }447    }).$mount()448    expect(vm.$el.innerHTML).toBe('<span>hello</span>')449  })450451  it('render function usage (default, as root)', () => {452    const vm = new Vue({453      render(h) {454        return h('test', [props => h('span', [props.msg])])455      },456      components: {457        test: {458          data() {459            return { msg: 'hello' }460          },461          render(h) {462            const res = this.$scopedSlots.default({ msg: this.msg })463            // all scoped slots should be normalized into arrays464            expect(Array.isArray(res)).toBe(true)465            return res466          }467        }468      }469    }).$mount()470    expect(vm.$el.outerHTML).toBe('<span>hello</span>')471  })472473  // new in 2.6, unifying all slots as functions474  it('non-scoped slots should also be available on $scopedSlots', () => {475    const vm = new Vue({476      template: `<foo>before <div slot="bar" slot-scope="scope">{{ scope.msg }}</div> after</foo>`,477      components: {478        foo: {479          render(h) {480            return h('div', [481              this.$scopedSlots.default(),482              this.$scopedSlots.bar({ msg: 'hi' })483            ])484          }485        }486      }487    }).$mount()488    expect(vm.$el.innerHTML).toBe(`before  after<div>hi</div>`)489  })490491  // #4779492  it('should support dynamic slot target', done => {493    const Child = {494      template: `495        <div>496          <slot name="a" msg="a" />497          <slot name="b" msg="b" />498        </div>499      `500    }501502    const vm = new Vue({503      data: {504        a: 'a',505        b: 'b'506      },507      template: `508        <child>509          <template :slot="a" slot-scope="props">A {{ props.msg }}</template>510          <template :slot="b" slot-scope="props">B {{ props.msg }}</template>511        </child>512      `,513      components: { Child }514    }).$mount()515516    expect(vm.$el.textContent.trim()).toBe('A a B b')517518    // switch slots519    vm.a = 'b'520    vm.b = 'a'521    waitForUpdate(() => {522      expect(vm.$el.textContent.trim()).toBe('B a A b')523    }).then(done)524  })525526  // it('render function usage (JSX)', () => {527  //   const vm = new Vue({528  //     render (h) {529  //       return (<test>{530  //         props => <span>{props.msg}</span>531  //       }</test>)532  //     },533  //     components: {534  //       test: {535  //         data () {536  //           return { msg: 'hello' }537  //         },538  //         render (h) {539  //           return <div>540  //             {this.$scopedSlots.default({ msg: this.msg })}541  //           </div>542  //         }543  //       }544  //     }545  //   }).$mount()546  //   expect(vm.$el.innerHTML).toBe('<span>hello</span>')547  // })548549  // #5615550  it('scoped slot with v-for', done => {551    const vm = new Vue({552      data: { names: ['foo', 'bar'] },553      template: `554        <test ref="test">555          <template v-for="n in names" :slot="n" slot-scope="props">556            <span>{{ props.msg }}</span>557          </template>558          <template slot="abc" slot-scope="props">559            <span>{{ props.msg }}</span>560          </template>561        </test>562      `,563      components: {564        test: {565          data: () => ({ msg: 'hello' }),566          template: `567            <div>568              <slot name="foo" :msg="msg + ' foo'"></slot>569              <slot name="bar" :msg="msg + ' bar'"></slot>570              <slot name="abc" :msg="msg + ' abc'"></slot>571            </div>572          `573        }574      }575    }).$mount()576577    expect(vm.$el.innerHTML).toBe(578      '<span>hello foo</span> <span>hello bar</span> <span>hello abc</span>'579    )580    vm.$refs.test.msg = 'world'581    waitForUpdate(() => {582      expect(vm.$el.innerHTML).toBe(583        '<span>world foo</span> <span>world bar</span> <span>world abc</span>'584      )585    }).then(done)586  })587588  it('scoped slot with v-for (plain elements)', done => {589    const vm = new Vue({590      data: { names: ['foo', 'bar'] },591      template: `592        <test ref="test">593          <span v-for="n in names" :slot="n" slot-scope="props">{{ props.msg }}</span>594          <span slot="abc" slot-scope="props">{{ props.msg }}</span>595        </test>596      `,597      components: {598        test: {599          data: () => ({ msg: 'hello' }),600          template: `601            <div>602              <slot name="foo" :msg="msg + ' foo'"></slot>603              <slot name="bar" :msg="msg + ' bar'"></slot>604              <slot name="abc" :msg="msg + ' abc'"></slot>605            </div>606          `607        }608      }609    }).$mount()610611    expect(vm.$el.innerHTML).toBe(612      '<span>hello foo</span> <span>hello bar</span> <span>hello abc</span>'613    )614    vm.$refs.test.msg = 'world'615    waitForUpdate(() => {616      expect(vm.$el.innerHTML).toBe(617        '<span>world foo</span> <span>world bar</span> <span>world abc</span>'618      )619    }).then(done)620  })621622  // #6725623  it('scoped slot with v-if', done => {624    const vm = new Vue({625      data: {626        ok: false627      },628      template: `629        <test>630          <template v-if="ok" slot-scope="foo">631            <p>{{ foo.text }}</p>632          </template>633        </test>634      `,635      components: {636        test: {637          data() {638            return { msg: 'hello' }639          },640          template: `641            <div>642              <slot :text="msg">643                <span>{{ msg }} fallback</span>644              </slot>645            </div>646          `647        }648      }649    }).$mount()650    expect(vm.$el.innerHTML).toBe('<span>hello fallback</span>')651652    vm.ok = true653    waitForUpdate(() => {654      expect(vm.$el.innerHTML).toBe('<p>hello</p>')655    }).then(done)656  })657658  // #9422659  // the behavior of the new syntax is slightly different.660  it('scoped slot v-if using slot-scope value', () => {661    const Child = {662      template: '<div><slot value="foo"/></div>'663    }664    const vm = new Vue({665      components: { Child },666      template: `667        <child>668          <template slot-scope="{ value }" v-if="value">669            foo {{ value }}670          </template>671        </child>672      `673    }).$mount()674    expect(vm.$el.textContent).toMatch(`foo foo`)675  })676677  // 2.6 new slot syntax678  describe('v-slot syntax', () => {679    const Foo = {680      render(h) {681        return h('div', [682          this.$scopedSlots.default &&683            this.$scopedSlots.default('from foo default'),684          this.$scopedSlots.one && this.$scopedSlots.one('from foo one'),685          this.$scopedSlots.two && this.$scopedSlots.two('from foo two')686        ])687      }688    }689690    const Bar = {691      render(h) {692        return (693          this.$scopedSlots.default && this.$scopedSlots.default('from bar')694        )695      }696    }697698    const Baz = {699      render(h) {700        return (701          this.$scopedSlots.default && this.$scopedSlots.default('from baz')702        )703      }704    }705706    const toNamed = (syntax, name) =>707      syntax[0] === '#'708        ? `#${name}` // shorthand709        : `${syntax}:${name}` // full syntax710711    function runSuite(syntax) {712      it('default slot', () => {713        const vm = new Vue({714          template: `<foo ${syntax}="foo">{{ foo }}<div>{{ foo }}</div></foo>`,715          components: { Foo }716        }).$mount()717        expect(vm.$el.innerHTML).toBe(718          `from foo default<div>from foo default</div>`719        )720      })721722      it('nested default slots', () => {723        const vm = new Vue({724          template: `725            <foo ${syntax}="foo">726              <bar ${syntax}="bar">727                <baz ${syntax}="baz">728                  {{ foo }} | {{ bar }} | {{ baz }}729                </baz>730              </bar>731            </foo>732          `,733          components: { Foo, Bar, Baz }734        }).$mount()735        expect(vm.$el.innerHTML.trim()).toBe(736          `from foo default | from bar | from baz`737        )738      })739740      it('named slots', () => {741        const vm = new Vue({742          template: `743            <foo>744              <template ${toNamed(syntax, 'default')}="foo">745                {{ foo }}746              </template>747              <template ${toNamed(syntax, 'one')}="one">748                {{ one }}749              </template>750              <template ${toNamed(syntax, 'two')}="two">751                {{ two }}752              </template>753            </foo>754          `,755          components: { Foo }756        }).$mount()757        expect(vm.$el.innerHTML.replace(/\s+/g, ' ')).toMatch(758          `from foo default from foo one from foo two`759        )760      })761762      it('nested + named + default slots', () => {763        const vm = new Vue({764          template: `765            <foo>766              <template ${toNamed(syntax, 'one')}="one">767                <bar ${syntax}="bar">768                  {{ one }} {{ bar }}769                </bar>770              </template>771              <template ${toNamed(syntax, 'two')}="two">772                <baz ${syntax}="baz">773                  {{ two }} {{ baz }}774                </baz>775              </template>776            </foo>777          `,778          components: { Foo, Bar, Baz }779        }).$mount()780        expect(vm.$el.innerHTML.replace(/\s+/g, ' ')).toMatch(781          `from foo one from bar from foo two from baz`782        )783      })784785      it('should warn v-slot usage on non-component elements', () => {786        new Vue({787          template: `<div ${syntax}="foo"/>`788        }).$mount()789        expect(790          `v-slot can only be used on components or <template>`791        ).toHaveBeenWarned()792      })793794      it('should warn mixed usage', () => {795        new Vue({796          template: `<foo><bar slot="one" slot-scope="bar" ${syntax}="bar"></bar></foo>`,797          components: { Foo, Bar }798        }).$mount()799        expect(800          `Unexpected mixed usage of different slot syntaxes`801        ).toHaveBeenWarned()802      })803804      it('should warn invalid parameter expression', () => {805        new Vue({806          template: `<foo ${syntax}="1"></foo>`,807          components: { Foo }808        }).$mount()809        expect('invalid function parameter expression').toHaveBeenWarned()810      })811812      it('should allow destructuring props with default value', () => {813        new Vue({814          template: `<foo ${syntax}="{ foo = { bar: '1' } }"></foo>`,815          components: { Foo }816        }).$mount()817        expect('invalid function parameter expression').not.toHaveBeenWarned()818      })819    }820821    // run tests for both full syntax and shorthand822    runSuite('v-slot')823    runSuite('#default')824825    it('shorthand named slots', () => {826      const vm = new Vue({827        template: `828          <foo>829            <template #default="foo">830              {{ foo }}831            </template>832            <template #one="one">833              {{ one }}834            </template>835            <template #two="two">836              {{ two }}837            </template>838          </foo>839        `,840        components: { Foo }841      }).$mount()842      expect(vm.$el.innerHTML.replace(/\s+/g, ' ')).toMatch(843        `from foo default from foo one from foo two`844      )845    })846847    it('should warn mixed root-default and named slots', () => {848      new Vue({849        template: `850          <foo #default="foo">851            {{ foo }}852            <template #one="one">853              {{ one }}854            </template>855          </foo>856        `,857        components: { Foo }858      }).$mount()859      expect(`default slot should also use <template>`).toHaveBeenWarned()860    })861862    it('shorthand without scope variable', () => {863      const vm = new Vue({864        template: `865          <foo>866            <template #one>one</template>867            <template #two>two</template>868          </foo>869        `,870        components: { Foo }871      }).$mount()872      expect(vm.$el.innerHTML.replace(/\s+/g, ' ')).toMatch(`onetwo`)873    })874875    it('shorthand named slots on root', () => {876      const vm = new Vue({877        template: `878          <foo #one="one">879            {{ one }}880          </foo>881        `,882        components: { Foo }883      }).$mount()884      expect(vm.$el.innerHTML.replace(/\s+/g, ' ')).toMatch(`from foo one`)885    })886887    it('dynamic slot name', done => {888      const vm = new Vue({889        data: {890          a: 'one',891          b: 'two'892        },893        template: `894          <foo>895            <template #[a]="one">a {{ one }} </template>896            <template v-slot:[b]="two">b {{ two }} </template>897          </foo>898        `,899        components: { Foo }900      }).$mount()901      expect(vm.$el.innerHTML.replace(/\s+/g, ' ')).toMatch(902        `a from foo one b from foo two`903      )904      vm.a = 'two'905      vm.b = 'one'906      waitForUpdate(() => {907        expect(vm.$el.innerHTML.replace(/\s+/g, ' ')).toMatch(908          `b from foo one a from foo two `909        )910      }).then(done)911    })912913    it('should work with v-if/v-else', done => {914      const vm = new Vue({915        data: { flag: true },916        template: `917          <foo>918            <template v-if="flag" v-slot:one="one">a {{ one }} </template>919            <template v-else v-slot:two="two">b {{ two }} </template>920          </foo>921        `,922        components: { Foo }923      }).$mount()924      expect(vm.$el.innerHTML).toBe(`a from foo one `)925      vm.flag = false926      waitForUpdate(() => {927        expect(vm.$el.innerHTML).toBe(`b from foo two `)928      }).then(done)929    })930931    it('warn when v-slot used on non-root <template>', () => {932      // @ts-ignore unused933      const vm = new Vue({934        template: `935          <foo>936            <template v-if="true">937              <template v-slot:one>foo</template>938            </template>939          </foo>940        `,941        components: { Foo }942      }).$mount()943      expect(944        `<template v-slot> can only appear at the root level`945      ).toHaveBeenWarned()946    })947  })948949  // 2.6 scoped slot perf optimization950  it('should have accurate tracking for scoped slots', done => {951    const parentUpdate = vi.fn()952    const childUpdate = vi.fn()953    const vm = new Vue({954      template: `955        <div>{{ parentCount }}<foo #default>{{ childCount }}</foo></div>956      `,957      data: {958        parentCount: 0,959        childCount: 0960      },961      updated: parentUpdate,962      components: {963        foo: {964          template: `<div><slot/></div>`,965          updated: childUpdate966        }967      }968    }).$mount()969    expect(vm.$el.innerHTML).toMatch(`0<div>0</div>`)970971    vm.parentCount++972    waitForUpdate(() => {973      expect(vm.$el.innerHTML).toMatch(`1<div>0</div>`)974      // should only trigger parent update975      expect(parentUpdate.mock.calls.length).toBe(1)976      expect(childUpdate.mock.calls.length).toBe(0)977978      vm.childCount++979    })980      .then(() => {981        expect(vm.$el.innerHTML).toMatch(`1<div>1</div>`)982        // should only trigger child update983        expect(parentUpdate.mock.calls.length).toBe(1)984        expect(childUpdate.mock.calls.length).toBe(1)985      })986      .then(done)987  })988989  // #9432: async components inside a scoped slot should trigger update of the990  // component that invoked the scoped slot, not the lexical context component.991  it('async component inside scoped slot', done => {992    const vm = new Vue({993      template: `994        <foo>995          <template #default>996            <bar />997          </template>998        </foo>999      `,1000      components: {1001        foo: {1002          template: `<div>foo<slot/></div>`1003        },1004        bar: resolve => {1005          setTimeout(() => {1006            resolve({1007              template: `<div>bar</div>`1008            })1009            next()1010          }, 0)1011        }1012      }1013    }).$mount()10141015    function next() {1016      waitForUpdate(() => {1017        expect(vm.$el.textContent).toBe(`foobar`)1018      }).then(done)1019    }1020  })10211022  // regression #93961023  it('should not force update child with no slot content', done => {1024    const Child = {1025      updated: vi.fn(),1026      template: `<div></div>`1027    }10281029    const parent = new Vue({1030      template: `<div>{{ count }}<child/></div>`,1031      data: {1032        count: 01033      },1034      components: { Child }1035    }).$mount()10361037    expect(parent.$el.textContent).toBe(`0`)1038    parent.count++1039    waitForUpdate(() => {1040      expect(parent.$el.textContent).toBe(`1`)1041      expect(Child.updated).not.toHaveBeenCalled()1042    }).then(done)1043  })10441045  // regression #94381046  it('nested scoped slots update', done => {1047    const Wrapper = {1048      template: `<div><slot/></div>`1049    }10501051    const Inner = {1052      props: ['foo'],1053      template: `<div>{{ foo }}</div>`1054    }10551056    const Outer = {1057      data: () => ({ foo: 1 }),1058      template: `<div><slot :foo="foo" /></div>`1059    }10601061    const vm = new Vue({1062      components: { Outer, Wrapper, Inner },1063      template: `1064        <outer ref="outer" v-slot="props">1065          <wrapper v-slot>1066            <inner :foo="props.foo"/>1067          </wrapper>1068        </outer>1069      `1070    }).$mount()10711072    expect(vm.$el.textContent).toBe(`1`)10731074    vm.$refs.outer.foo++1075    waitForUpdate(() => {1076      expect(vm.$el.textContent).toBe(`2`)1077    }).then(done)1078  })10791080  it('dynamic v-bind arguments on <slot>', done => {1081    const Foo = {1082      data() {1083        return {1084          key: 'msg'1085        }1086      },1087      template: `<div><slot :[key]="'hello'"/></div>`1088    }10891090    const vm = new Vue({1091      components: { Foo },1092      template: `1093        <foo ref="foo" v-slot="props">{{ props }}</foo>1094      `1095    }).$mount()10961097    expect(vm.$el.textContent).toBe(JSON.stringify({ msg: 'hello' }, null, 2))10981099    vm.$refs.foo.key = 'changed'1100    waitForUpdate(() => {1101      expect(vm.$el.textContent).toBe(1102        JSON.stringify({ changed: 'hello' }, null, 2)1103      )1104    }).then(done)1105  })11061107  // #94521108  it('fallback for scoped slots passed multiple levels down', () => {1109    const inner = {1110      template: `<div><slot>fallback</slot></div>`1111    }11121113    const wrapper = {1114      template: `1115        <inner>1116          <template #default>1117            <slot/>1118          </template>1119        </inner>1120      `,1121      components: { inner }1122    }11231124    const vm = new Vue({1125      components: { wrapper, inner },1126      template: `<wrapper/>`1127    }).$mount()11281129    expect(vm.$el.textContent).toBe(`fallback`)1130  })11311132  it('should expose v-slot without scope on this.$slots', () => {1133    const vm = new Vue({1134      template: `<foo><template v-slot>hello</template></foo>`,1135      components: {1136        foo: {1137          render(h) {1138            return h('div', this.$slots.default)1139          }1140        }1141      }1142    }).$mount()1143    expect(vm.$el.textContent).toBe('hello')1144  })11451146  it('should not expose legacy syntax scoped slot on this.$slots', () => {1147    const vm = new Vue({1148      template: `<foo><template slot-scope="foo">hello</template></foo>`,1149      components: {1150        foo: {1151          render(h) {1152            expect(this.$slots.default).toBeUndefined()1153            return h('div', this.$slots.default)1154          }1155        }1156      }1157    }).$mount()1158    expect(vm.$el.textContent).toBe('')1159  })11601161  it('should expose v-slot without scope on ctx.slots() in functional', () => {1162    const vm = new Vue({1163      template: `<foo><template v-slot>hello</template></foo>`,1164      components: {1165        foo: {1166          functional: true,1167          render(h, ctx) {1168            return h('div', ctx.slots().default)1169          }1170        }1171      }1172    }).$mount()1173    expect(vm.$el.textContent).toBe('hello')1174  })11751176  it('should not cache scoped slot normalization when there are a mix of normal and scoped slots', done => {1177    const foo = {1178      template: `<div><slot name="foo" /> <slot name="bar" /></div>`1179    }11801181    const vm = new Vue({1182      data: {1183        msg: 'foo'1184      },1185      template: `1186        <foo>1187          <div slot="foo">{{ msg }}</div>1188          <template #bar><div>bar</div></template>1189        </foo>1190      `,1191      components: { foo }1192    }).$mount()11931194    expect(vm.$el.textContent).toBe(`foo bar`)1195    vm.msg = 'baz'1196    waitForUpdate(() => {1197      expect(vm.$el.textContent).toBe(`baz bar`)1198    }).then(done)1199  })12001201  // #94681202  it('should support passing multiple args to scoped slot function', () => {1203    const foo = {1204      render() {1205        return this.$scopedSlots.default('foo', 'bar')1206      }1207    }12081209    const vm = new Vue({1210      template: `<foo v-slot="foo, bar">{{ foo }} {{ bar }}</foo>`,1211      components: { foo }1212    }).$mount()12131214    expect(vm.$el.textContent).toBe('foo bar')1215  })12161217  it('should not skip updates when a scoped slot contains parent <slot/> content', done => {1218    const inner = {1219      template: `<div><slot/></div>`1220    }12211222    const wrapper = {1223      template: `<inner v-slot><slot/></inner>`,1224      components: { inner }1225    }12261227    const vm = new Vue({1228      data() {1229        return {1230          ok: true1231        }1232      },1233      components: { wrapper },1234      template: `<wrapper><div>{{ ok ? 'foo' : 'bar' }}</div></wrapper>`1235    }).$mount()12361237    expect(vm.$el.textContent).toBe('foo')1238    vm.ok = false1239    waitForUpdate(() => {1240      expect(vm.$el.textContent).toBe('bar')1241    }).then(done)1242  })12431244  it('should not skip updates for v-slot inside v-for', done => {1245    const test = {1246      template: `<div><slot></slot></div>`1247    }12481249    const vm = new Vue({1250      template: `1251      <div>1252        <div v-for="i in numbers">1253          <test v-slot>{{ i }}</test>1254        </div>1255      </div>1256      `,1257      components: { test },1258      data: {1259        numbers: [1]1260      }1261    }).$mount()12621263    expect(vm.$el.textContent).toBe(`1`)1264    vm.numbers = [2]1265    waitForUpdate(() => {1266      expect(vm.$el.textContent).toBe(`2`)1267    }).then(done)1268  })12691270  // #95341271  it('should detect conditional reuse with different slot content', done => {1272    const Foo = {1273      template: `<div><slot :n="1" /></div>`1274    }12751276    const vm = new Vue({1277      components: { Foo },1278      data: {1279        ok: true1280      },1281      template: `1282        <div>1283          <div v-if="ok">1284            <foo v-slot="{ n }">{{ n }}</foo>1285          </div>1286          <div v-if="!ok">1287            <foo v-slot="{ n }">{{ n + 1 }}</foo>1288          </div>1289        </div>1290      `1291    }).$mount()12921293    expect(vm.$el.textContent.trim()).toBe(`1`)1294    vm.ok = false1295    waitForUpdate(() => {1296      expect(vm.$el.textContent.trim()).toBe(`2`)1297    }).then(done)1298  })12991300  // #96441301  it('should factor presence of normal slots into scoped slots caching', done => {1302    const Wrapper = {1303      template: `<div>1304        <p>Default:<slot/></p>1305        <p>Content:<slot name='content'/></p>1306      </div>`1307    }13081309    const vm = new Vue({1310      data: { ok: false },1311      components: { Wrapper },1312      template: `<wrapper>1313        <p v-if='ok'>ok</p>1314        <template #content>1315          <p v-if='ok'>ok</p>1316        </template>1317      </wrapper>`1318    }).$mount()13191320    expect(vm.$el.textContent).not.toMatch(`Default:ok`)1321    expect(vm.$el.textContent).not.toMatch(`Content:ok`)1322    vm.ok = true1323    waitForUpdate(() => {1324      expect(vm.$el.textContent).toMatch(`Default:ok`)1325      expect(vm.$el.textContent).toMatch(`Content:ok`)1326      vm.ok = false1327    })1328      .then(() => {1329        expect(vm.$el.textContent).not.toMatch(`Default:ok`)1330        expect(vm.$el.textContent).not.toMatch(`Content:ok`)1331        vm.ok = true1332      })1333      .then(() => {1334        expect(vm.$el.textContent).toMatch(`Default:ok`)1335        expect(vm.$el.textContent).toMatch(`Content:ok`)1336      })1337      .then(done)1338  })13391340  //#96581341  it('fallback for scoped slot with single v-if', () => {1342    const vm = new Vue({1343      template: `<test v-slot><template v-if="false">hi</template></test>`,1344      components: {1345        Test: {1346          template: `<div><slot>fallback</slot></div>`1347        }1348      }1349    }).$mount()1350    expect(vm.$el.textContent).toMatch('fallback')1351  })13521353  // #96991354  // Component only has normal slots, but is passing down $scopedSlots directly1355  // $scopedSlots should not be marked as stable in this case1356  it('render function passing $scopedSlots w/ normal slots down', done => {1357    const one = {1358      template: `<div><slot name="footer"/></div>`1359    }13601361    const two = {1362      render(h) {1363        return h(one, {1364          scopedSlots: this.$scopedSlots1365        })1366      }1367    }13681369    const vm = new Vue({1370      data: { count: 0 },1371      render(h) {1372        return h(two, [h('span', { slot: 'footer' }, this.count)])1373      }1374    }).$mount()13751376    expect(vm.$el.textContent).toMatch(`0`)1377    vm.count++1378    waitForUpdate(() => {1379      expect(vm.$el.textContent).toMatch(`1`)1380    }).then(done)1381  })13821383  // #116521384  it('should update when switching between two components with slot and without slot', done => {1385    const Child = {1386      template: `<div><slot/></div>`1387    }13881389    const parent = new Vue({1390      template: `<div>1391        <child v-if="flag"><template #default>foo</template></child>1392        <child v-else></child>1393      </div>`,1394      data: {1395        flag: true1396      },1397      components: { Child }1398    }).$mount()13991400    expect(parent.$el.textContent).toMatch(`foo`)1401    parent.flag = false1402    waitForUpdate(() => {1403      expect(parent.$el.textContent).toMatch(``)1404    }).then(done)1405  })1406})

Findings

✓ No findings reported for this file.

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.