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.