vue3-yuanma/packages/runtime-core/__tests__/keepAlive.spec.ts
2019-10-30 21:41:28 -04:00

234 lines
6.5 KiB
TypeScript

import { ComponentOptions } from '../src/component'
import {
h,
TestElement,
nodeOps,
render,
ref,
KeepAlive,
serializeInner,
nextTick
} from '@vue/runtime-test'
describe('keep-alive', () => {
let one: ComponentOptions
let two: ComponentOptions
let root: TestElement
beforeEach(() => {
root = nodeOps.createElement('div')
one = {
data: () => ({ msg: 'one' }),
render() {
return h('div', this.msg)
},
created: jest.fn(),
mounted: jest.fn(),
activated: jest.fn(),
deactivated: jest.fn(),
unmounted: jest.fn()
}
two = {
data: () => ({ msg: 'two' }),
render() {
return h('div', this.msg)
},
created: jest.fn(),
mounted: jest.fn(),
activated: jest.fn(),
deactivated: jest.fn(),
unmounted: jest.fn()
}
})
function assertHookCalls(component: any, callCounts: number[]) {
expect([
component.created.mock.calls.length,
component.mounted.mock.calls.length,
component.activated.mock.calls.length,
component.deactivated.mock.calls.length,
component.unmounted.mock.calls.length
]).toEqual(callCounts)
}
test('should preserve state', async () => {
const toggle = ref(true)
const instanceRef = ref<any>(null)
const App = {
render() {
return h(KeepAlive, null, {
default: () => h(toggle.value ? one : two, { ref: instanceRef })
})
}
}
render(h(App), root)
expect(serializeInner(root)).toBe(`<div>one</div>`)
instanceRef.value.msg = 'changed'
await nextTick()
expect(serializeInner(root)).toBe(`<div>changed</div>`)
toggle.value = false
await nextTick()
expect(serializeInner(root)).toBe(`<div>two</div>`)
toggle.value = true
await nextTick()
expect(serializeInner(root)).toBe(`<div>changed</div>`)
})
test('should call correct lifecycle hooks', async () => {
const toggle1 = ref(true)
const toggle2 = ref(true)
const App = {
render() {
return toggle1.value
? h(KeepAlive, () => h(toggle2.value ? one : two))
: null
}
}
render(h(App), root)
expect(serializeInner(root)).toBe(`<div>one</div>`)
assertHookCalls(one, [1, 1, 1, 0, 0])
assertHookCalls(two, [0, 0, 0, 0, 0])
// toggle kept-alive component
toggle2.value = false
await nextTick()
expect(serializeInner(root)).toBe(`<div>two</div>`)
assertHookCalls(one, [1, 1, 1, 1, 0])
assertHookCalls(two, [1, 1, 1, 0, 0])
toggle2.value = true
await nextTick()
expect(serializeInner(root)).toBe(`<div>one</div>`)
assertHookCalls(one, [1, 1, 2, 1, 0])
assertHookCalls(two, [1, 1, 1, 1, 0])
toggle2.value = false
await nextTick()
expect(serializeInner(root)).toBe(`<div>two</div>`)
assertHookCalls(one, [1, 1, 2, 2, 0])
assertHookCalls(two, [1, 1, 2, 1, 0])
// teardown keep-alive, should unmount all components including cached
toggle1.value = false
await nextTick()
expect(serializeInner(root)).toBe(`<!---->`)
assertHookCalls(one, [1, 1, 2, 2, 1])
assertHookCalls(two, [1, 1, 2, 2, 1])
})
test('should call lifecycle hooks on nested components', async () => {
one.render = () => h(two)
const toggle = ref(true)
const App = {
render() {
return h(KeepAlive, () => (toggle.value ? h(one) : null))
}
}
render(h(App), root)
expect(serializeInner(root)).toBe(`<div>two</div>`)
assertHookCalls(one, [1, 1, 1, 0, 0])
assertHookCalls(two, [1, 1, 1, 0, 0])
toggle.value = false
await nextTick()
expect(serializeInner(root)).toBe(`<!---->`)
assertHookCalls(one, [1, 1, 1, 1, 0])
assertHookCalls(two, [1, 1, 1, 1, 0])
toggle.value = true
await nextTick()
expect(serializeInner(root)).toBe(`<div>two</div>`)
assertHookCalls(one, [1, 1, 2, 1, 0])
assertHookCalls(two, [1, 1, 2, 1, 0])
toggle.value = false
await nextTick()
expect(serializeInner(root)).toBe(`<!---->`)
assertHookCalls(one, [1, 1, 2, 2, 0])
assertHookCalls(two, [1, 1, 2, 2, 0])
})
test('should call correct hooks for nested keep-alive', async () => {
const toggle2 = ref(true)
one.render = () => h(KeepAlive, () => (toggle2.value ? h(two) : null))
const toggle1 = ref(true)
const App = {
render() {
return h(KeepAlive, () => (toggle1.value ? h(one) : null))
}
}
render(h(App), root)
expect(serializeInner(root)).toBe(`<div>two</div>`)
assertHookCalls(one, [1, 1, 1, 0, 0])
assertHookCalls(two, [1, 1, 1, 0, 0])
toggle1.value = false
await nextTick()
expect(serializeInner(root)).toBe(`<!---->`)
assertHookCalls(one, [1, 1, 1, 1, 0])
assertHookCalls(two, [1, 1, 1, 1, 0])
toggle1.value = true
await nextTick()
expect(serializeInner(root)).toBe(`<div>two</div>`)
assertHookCalls(one, [1, 1, 2, 1, 0])
assertHookCalls(two, [1, 1, 2, 1, 0])
// toggle nested instance
toggle2.value = false
await nextTick()
expect(serializeInner(root)).toBe(`<!---->`)
assertHookCalls(one, [1, 1, 2, 1, 0])
assertHookCalls(two, [1, 1, 2, 2, 0])
toggle2.value = true
await nextTick()
expect(serializeInner(root)).toBe(`<div>two</div>`)
assertHookCalls(one, [1, 1, 2, 1, 0])
assertHookCalls(two, [1, 1, 3, 2, 0])
toggle1.value = false
await nextTick()
expect(serializeInner(root)).toBe(`<!---->`)
assertHookCalls(one, [1, 1, 2, 2, 0])
assertHookCalls(two, [1, 1, 3, 3, 0])
// toggle nested instance when parent is deactivated
toggle2.value = false
await nextTick()
expect(serializeInner(root)).toBe(`<!---->`)
assertHookCalls(one, [1, 1, 2, 2, 0])
assertHookCalls(two, [1, 1, 3, 3, 0]) // should not be affected
toggle2.value = true
await nextTick()
expect(serializeInner(root)).toBe(`<!---->`)
assertHookCalls(one, [1, 1, 2, 2, 0])
assertHookCalls(two, [1, 1, 3, 3, 0]) // should not be affected
toggle1.value = true
await nextTick()
expect(serializeInner(root)).toBe(`<div>two</div>`)
assertHookCalls(one, [1, 1, 3, 2, 0])
assertHookCalls(two, [1, 1, 4, 3, 0])
toggle1.value = false
toggle2.value = false
await nextTick()
expect(serializeInner(root)).toBe(`<!---->`)
assertHookCalls(one, [1, 1, 3, 3, 0])
assertHookCalls(two, [1, 1, 4, 4, 0])
toggle1.value = true
await nextTick()
expect(serializeInner(root)).toBe(`<!---->`)
assertHookCalls(one, [1, 1, 4, 3, 0])
assertHookCalls(two, [1, 1, 4, 4, 0]) // should remain inactive
})
})