-import { ref, render, h, nodeOps, nextTick } from '@vue/runtime-test'
+import {
+ ref,
+ render,
+ h,
+ nodeOps,
+ nextTick,
+ getCurrentInstance
+} from '@vue/runtime-test'
+import { normalizeVNode } from '../src/vnode'
+import { createSlots } from '../src/helpers/createSlots'
describe('component: slots', () => {
- // TODO more tests for slots normalization etc.
+ function renderWithSlots(slots: any): any {
+ let instance: any
+ const Comp = {
+ render() {
+ instance = getCurrentInstance()
+ return h('div')
+ }
+ }
+
+ render(h(Comp, null, slots), nodeOps.createElement('div'))
+ return instance
+ }
+
+ test('initSlots: instance.slots should be set correctly', () => {
+ const { slots } = renderWithSlots({ _: 1 })
+ expect(slots).toMatchObject({ _: 1 })
+ })
+
+ test('initSlots: should normalize object slots (when value is null, string, array)', () => {
+ const { slots } = renderWithSlots({
+ _inner: '_inner',
+ foo: null,
+ header: 'header',
+ footer: ['f1', 'f2']
+ })
+
+ expect(
+ '[Vue warn]: Non-function value encountered for slot "header". Prefer function slots for better performance.'
+ ).toHaveBeenWarned()
+
+ expect(
+ '[Vue warn]: Non-function value encountered for slot "footer". Prefer function slots for better performance.'
+ ).toHaveBeenWarned()
+
+ expect(slots).not.toHaveProperty('_inner')
+ expect(slots).not.toHaveProperty('foo')
+ expect(slots.header()).toMatchObject([normalizeVNode('header')])
+ expect(slots.footer()).toMatchObject([
+ normalizeVNode('f1'),
+ normalizeVNode('f2')
+ ])
+ })
+
+ test('initSlots: should normalize object slots (when value is function)', () => {
+ let proxy: any
+ const Comp = {
+ render() {
+ proxy = getCurrentInstance()
+ return h('div')
+ }
+ }
+
+ render(
+ h(Comp, null, {
+ header: () => 'header'
+ }),
+ nodeOps.createElement('div')
+ )
+
+ expect(proxy.slots.header()).toMatchObject([normalizeVNode('header')])
+ })
+
+ test('initSlots: instance.slots should be set correctly (when vnode.shapeFlag is not SLOTS_CHILDREN)', () => {
+ const { slots } = renderWithSlots([h('span')])
+
+ expect(
+ '[Vue warn]: Non-function value encountered for default slot. Prefer function slots for better performance.'
+ ).toHaveBeenWarned()
+
+ expect(slots.default()).toMatchObject([normalizeVNode(h('span'))])
+ })
+
+ test('updateSlots: instance.slots should be update correctly (when slotType is number)', async () => {
+ const flag1 = ref(true)
+
+ let instance: any
+ const Child = () => {
+ instance = getCurrentInstance()
+ return 'child'
+ }
+
+ const Comp = {
+ setup() {
+ return () => [
+ h(
+ Child,
+ null,
+ createSlots({ _: 2 as any }, [
+ flag1.value
+ ? {
+ name: 'one',
+ fn: () => [h('span')]
+ }
+ : {
+ name: 'two',
+ fn: () => [h('div')]
+ }
+ ])
+ )
+ ]
+ }
+ }
+ render(h(Comp), nodeOps.createElement('div'))
+
+ expect(instance.slots).toHaveProperty('one')
+ expect(instance.slots).not.toHaveProperty('two')
+
+ flag1.value = false
+ await nextTick()
+
+ expect(instance.slots).not.toHaveProperty('one')
+ expect(instance.slots).toHaveProperty('two')
+ })
+
+ test('updateSlots: instance.slots should be update correctly (when slotType is null)', async () => {
+ const flag1 = ref(true)
+
+ let instance: any
+ const Child = () => {
+ instance = getCurrentInstance()
+ return 'child'
+ }
+
+ const oldSlots = {
+ header: 'header'
+ }
+ const newSlots = {
+ footer: 'footer'
+ }
+
+ const Comp = {
+ setup() {
+ return () => [
+ h(Child, { n: flag1.value }, flag1.value ? oldSlots : newSlots)
+ ]
+ }
+ }
+ render(h(Comp), nodeOps.createElement('div'))
+
+ expect(instance.slots).toHaveProperty('header')
+ expect(instance.slots).not.toHaveProperty('footer')
+
+ flag1.value = false
+ await nextTick()
+
+ expect(
+ '[Vue warn]: Non-function value encountered for slot "header". Prefer function slots for better performance.'
+ ).toHaveBeenWarned()
+
+ expect(
+ '[Vue warn]: Non-function value encountered for slot "footer". Prefer function slots for better performance.'
+ ).toHaveBeenWarned()
+
+ expect(instance.slots).not.toHaveProperty('header')
+ expect(instance.slots.footer()).toMatchObject([normalizeVNode('footer')])
+ })
+
+ test('updateSlots: instance.slots should be update correctly (when vnode.shapeFlag is not SLOTS_CHILDREN)', async () => {
+ const flag1 = ref(true)
+
+ let instance: any
+ const Child = () => {
+ instance = getCurrentInstance()
+ return 'child'
+ }
+
+ const Comp = {
+ setup() {
+ return () => [
+ h(Child, { n: flag1.value }, flag1.value ? ['header'] : ['footer'])
+ ]
+ }
+ }
+ render(h(Comp), nodeOps.createElement('div'))
+
+ expect(instance.slots.default()).toMatchObject([normalizeVNode('header')])
+
+ flag1.value = false
+ await nextTick()
+
+ expect(
+ '[Vue warn]: Non-function value encountered for default slot. Prefer function slots for better performance.'
+ ).toHaveBeenWarned()
+
+ expect(instance.slots.default()).toMatchObject([normalizeVNode('footer')])
+ })
test('should respect $stable flag', async () => {
const flag1 = ref(1)