vue3-yuanma/packages/runtime-core/__tests__/componentSlots.spec.ts

242 lines
6.1 KiB
TypeScript
Raw Normal View History

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', () => {
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)
const flag2 = ref(2)
const spy = jest.fn()
const Child = () => {
spy()
return 'child'
}
const App = {
setup() {
return () => [
flag1.value,
h(
Child,
{ n: flag2.value },
{
foo: () => 'foo',
$stable: true
}
)
]
}
}
render(h(App), nodeOps.createElement('div'))
expect(spy).toHaveBeenCalledTimes(1)
// parent re-render, props didn't change, slots are stable
// -> child should not update
flag1.value++
await nextTick()
expect(spy).toHaveBeenCalledTimes(1)
// parent re-render, props changed
// -> child should update
flag2.value++
await nextTick()
expect(spy).toHaveBeenCalledTimes(2)
})
})