mirror of https://github.com/vuejs/core.git
test: wip more tests for BaseTransition
This commit is contained in:
parent
fbcc47841b
commit
7209fb66c2
|
@ -6,7 +6,8 @@ import {
|
||||||
BaseTransitionProps,
|
BaseTransitionProps,
|
||||||
ref,
|
ref,
|
||||||
nextTick,
|
nextTick,
|
||||||
serializeInner
|
serializeInner,
|
||||||
|
serialize
|
||||||
} from '@vue/runtime-test'
|
} from '@vue/runtime-test'
|
||||||
|
|
||||||
function mount(props: BaseTransitionProps, slot: () => any) {
|
function mount(props: BaseTransitionProps, slot: () => any) {
|
||||||
|
@ -16,22 +17,25 @@ function mount(props: BaseTransitionProps, slot: () => any) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function mockProps() {
|
function mockProps() {
|
||||||
const cbs = {
|
const cbs: {
|
||||||
doneEnter: () => {},
|
doneEnter: Record<string, () => void>
|
||||||
doneLeave: () => {}
|
doneLeave: Record<string, () => void>
|
||||||
|
} = {
|
||||||
|
doneEnter: {},
|
||||||
|
doneLeave: {}
|
||||||
}
|
}
|
||||||
const props: BaseTransitionProps = {
|
const props: BaseTransitionProps = {
|
||||||
onBeforeEnter: jest.fn(el => {
|
onBeforeEnter: jest.fn(el => {
|
||||||
expect(el.parentNode).toBeNull()
|
expect(el.parentNode).toBeNull()
|
||||||
}),
|
}),
|
||||||
onEnter: jest.fn((el, done) => {
|
onEnter: jest.fn((el, done) => {
|
||||||
cbs.doneEnter = done
|
cbs.doneEnter[serialize(el)] = done
|
||||||
}),
|
}),
|
||||||
onAfterEnter: jest.fn(),
|
onAfterEnter: jest.fn(),
|
||||||
onEnterCancelled: jest.fn(),
|
onEnterCancelled: jest.fn(),
|
||||||
onBeforeLeave: jest.fn(),
|
onBeforeLeave: jest.fn(),
|
||||||
onLeave: jest.fn((el, done) => {
|
onLeave: jest.fn((el, done) => {
|
||||||
cbs.doneLeave = done
|
cbs.doneLeave[serialize(el)] = done
|
||||||
}),
|
}),
|
||||||
onAfterLeave: jest.fn(),
|
onAfterLeave: jest.fn(),
|
||||||
onLeaveCancelled: jest.fn()
|
onLeaveCancelled: jest.fn()
|
||||||
|
@ -51,12 +55,31 @@ function assertCalls(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function assertCalledWithEl(fn: any, expected: string, callIndex = 0) {
|
||||||
|
expect(serialize((fn as jest.Mock).mock.calls[callIndex][0])).toBe(expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ToggleOptions {
|
||||||
|
trueBranch: () => any
|
||||||
|
falseBranch: () => any
|
||||||
|
trueSerialized: string
|
||||||
|
falseSerialized: string
|
||||||
|
}
|
||||||
|
|
||||||
describe('BaseTransition', () => {
|
describe('BaseTransition', () => {
|
||||||
describe('with elements', () => {
|
describe('toggle on-off', () => {
|
||||||
test('toggle on-off', async () => {
|
async function testToggleOnOff({
|
||||||
|
trueBranch,
|
||||||
|
trueSerialized,
|
||||||
|
falseBranch,
|
||||||
|
falseSerialized
|
||||||
|
}: ToggleOptions) {
|
||||||
const toggle = ref(true)
|
const toggle = ref(true)
|
||||||
const { props, cbs } = mockProps()
|
const { props, cbs } = mockProps()
|
||||||
const root = mount(props, () => (toggle.value ? h('div') : null))
|
const root = mount(
|
||||||
|
props,
|
||||||
|
() => (toggle.value ? trueBranch() : falseBranch())
|
||||||
|
)
|
||||||
|
|
||||||
// without appear: true, enter hooks should not be called on mount
|
// without appear: true, enter hooks should not be called on mount
|
||||||
expect(props.onBeforeEnter).not.toHaveBeenCalled()
|
expect(props.onBeforeEnter).not.toHaveBeenCalled()
|
||||||
|
@ -66,23 +89,29 @@ describe('BaseTransition', () => {
|
||||||
toggle.value = false
|
toggle.value = false
|
||||||
await nextTick()
|
await nextTick()
|
||||||
// comment placeholder enters immediately
|
// comment placeholder enters immediately
|
||||||
expect(serializeInner(root)).toBe('<div></div><!---->')
|
expect(serializeInner(root)).toBe(`${trueSerialized}${falseSerialized}`)
|
||||||
expect(props.onBeforeLeave).toHaveBeenCalledTimes(1)
|
expect(props.onBeforeLeave).toHaveBeenCalledTimes(1)
|
||||||
|
assertCalledWithEl(props.onBeforeLeave, trueSerialized)
|
||||||
expect(props.onLeave).toHaveBeenCalledTimes(1)
|
expect(props.onLeave).toHaveBeenCalledTimes(1)
|
||||||
|
assertCalledWithEl(props.onLeave, trueSerialized)
|
||||||
expect(props.onAfterLeave).not.toHaveBeenCalled()
|
expect(props.onAfterLeave).not.toHaveBeenCalled()
|
||||||
cbs.doneLeave()
|
cbs.doneLeave[trueSerialized]()
|
||||||
expect(serializeInner(root)).toBe('<!---->')
|
expect(serializeInner(root)).toBe(falseSerialized)
|
||||||
expect(props.onAfterLeave).toHaveBeenCalledTimes(1)
|
expect(props.onAfterLeave).toHaveBeenCalledTimes(1)
|
||||||
|
assertCalledWithEl(props.onAfterLeave, trueSerialized)
|
||||||
|
|
||||||
toggle.value = true
|
toggle.value = true
|
||||||
await nextTick()
|
await nextTick()
|
||||||
expect(serializeInner(root)).toBe('<div></div>')
|
expect(serializeInner(root)).toBe(trueSerialized)
|
||||||
// before enter spy asserts node has no parent when it's called
|
// before enter spy asserts node has no parent when it's called
|
||||||
expect(props.onBeforeEnter).toHaveBeenCalledTimes(1)
|
expect(props.onBeforeEnter).toHaveBeenCalledTimes(1)
|
||||||
|
assertCalledWithEl(props.onBeforeEnter, trueSerialized)
|
||||||
expect(props.onEnter).toHaveBeenCalledTimes(1)
|
expect(props.onEnter).toHaveBeenCalledTimes(1)
|
||||||
|
assertCalledWithEl(props.onEnter, trueSerialized)
|
||||||
expect(props.onAfterEnter).not.toHaveBeenCalled()
|
expect(props.onAfterEnter).not.toHaveBeenCalled()
|
||||||
cbs.doneEnter()
|
cbs.doneEnter[trueSerialized]()
|
||||||
expect(props.onAfterEnter).toHaveBeenCalledTimes(1)
|
expect(props.onAfterEnter).toHaveBeenCalledTimes(1)
|
||||||
|
assertCalledWithEl(props.onAfterEnter, trueSerialized)
|
||||||
|
|
||||||
assertCalls(props, {
|
assertCalls(props, {
|
||||||
onBeforeEnter: 1,
|
onBeforeEnter: 1,
|
||||||
|
@ -94,30 +123,59 @@ describe('BaseTransition', () => {
|
||||||
onAfterLeave: 1,
|
onAfterLeave: 1,
|
||||||
onLeaveCancelled: 0
|
onLeaveCancelled: 0
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
test('w/ element', async () => {
|
||||||
|
await testToggleOnOff({
|
||||||
|
trueBranch: () => h('div'),
|
||||||
|
trueSerialized: `<div></div>`,
|
||||||
|
falseBranch: () => null,
|
||||||
|
falseSerialized: `<!---->`
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test('toggle before finish', async () => {
|
test('w/ component', async () => {
|
||||||
|
const Comp = ({ msg }: { msg: string }) => h('div', msg)
|
||||||
|
await testToggleOnOff({
|
||||||
|
trueBranch: () => h(Comp, { msg: 'hello' }),
|
||||||
|
trueSerialized: `<div>hello</div>`,
|
||||||
|
falseBranch: () => null,
|
||||||
|
falseSerialized: `<!---->`
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('toggle on-off before finish', () => {
|
||||||
|
async function testToggleOnOffBeforeFinish({
|
||||||
|
trueBranch,
|
||||||
|
trueSerialized,
|
||||||
|
falseBranch = () => null,
|
||||||
|
falseSerialized = `<!---->`
|
||||||
|
}: ToggleOptions) {
|
||||||
const toggle = ref(false)
|
const toggle = ref(false)
|
||||||
const { props, cbs } = mockProps()
|
const { props, cbs } = mockProps()
|
||||||
const root = mount(props, () => (toggle.value ? h('div') : null))
|
const root = mount(
|
||||||
|
props,
|
||||||
|
() => (toggle.value ? trueBranch() : falseBranch())
|
||||||
|
)
|
||||||
|
|
||||||
// start enter
|
// start enter
|
||||||
toggle.value = true
|
toggle.value = true
|
||||||
await nextTick()
|
await nextTick()
|
||||||
expect(serializeInner(root)).toBe(`<div></div>`)
|
expect(serializeInner(root)).toBe(trueSerialized)
|
||||||
expect(props.onBeforeEnter).toHaveBeenCalledTimes(1)
|
expect(props.onBeforeEnter).toHaveBeenCalledTimes(1)
|
||||||
expect(props.onEnter).toHaveBeenCalledTimes(1)
|
expect(props.onEnter).toHaveBeenCalledTimes(1)
|
||||||
|
|
||||||
// leave before enter finishes
|
// leave before enter finishes
|
||||||
toggle.value = false
|
toggle.value = false
|
||||||
await nextTick()
|
await nextTick()
|
||||||
expect(serializeInner(root)).toBe(`<div></div><!---->`)
|
expect(serializeInner(root)).toBe(`${trueSerialized}${falseSerialized}`)
|
||||||
expect(props.onEnterCancelled).toHaveBeenCalled()
|
expect(props.onEnterCancelled).toHaveBeenCalled()
|
||||||
expect(props.onBeforeLeave).toHaveBeenCalledTimes(1)
|
expect(props.onBeforeLeave).toHaveBeenCalledTimes(1)
|
||||||
expect(props.onLeave).toHaveBeenCalledTimes(1)
|
expect(props.onLeave).toHaveBeenCalledTimes(1)
|
||||||
expect(props.onAfterLeave).not.toHaveBeenCalled()
|
expect(props.onAfterLeave).not.toHaveBeenCalled()
|
||||||
// calling doneEnter now should have no effect
|
// calling doneEnter now should have no effect
|
||||||
cbs.doneEnter()
|
cbs.doneEnter[trueSerialized]()
|
||||||
expect(props.onAfterEnter).not.toHaveBeenCalled()
|
expect(props.onAfterEnter).not.toHaveBeenCalled()
|
||||||
|
|
||||||
// enter again before leave finishes
|
// enter again before leave finishes
|
||||||
|
@ -127,14 +185,14 @@ describe('BaseTransition', () => {
|
||||||
expect(props.onEnter).toHaveBeenCalledTimes(2)
|
expect(props.onEnter).toHaveBeenCalledTimes(2)
|
||||||
// 1. should remove the previous leaving <div> so there is only one <div>
|
// 1. should remove the previous leaving <div> so there is only one <div>
|
||||||
// 2. should remove the comment placeholder for the off branch
|
// 2. should remove the comment placeholder for the off branch
|
||||||
expect(serializeInner(root)).toBe(`<div></div>`)
|
expect(serializeInner(root)).toBe(trueSerialized)
|
||||||
// note onLeaveCancelled is NOT called because it was a forced early
|
// note onLeaveCancelled is NOT called because it was a forced early
|
||||||
// removal instead of a cancel. Instead, onAfterLeave should be called.
|
// removal instead of a cancel. Instead, onAfterLeave should be called.
|
||||||
expect(props.onAfterLeave).toHaveBeenCalledTimes(1)
|
expect(props.onAfterLeave).toHaveBeenCalledTimes(1)
|
||||||
// calling doneLeave again should have no effect now
|
// calling doneLeave again should have no effect now
|
||||||
cbs.doneLeave()
|
cbs.doneLeave[trueSerialized]()
|
||||||
expect(props.onAfterLeave).toHaveBeenCalledTimes(1)
|
expect(props.onAfterLeave).toHaveBeenCalledTimes(1)
|
||||||
cbs.doneEnter()
|
cbs.doneEnter[trueSerialized]()
|
||||||
expect(props.onAfterEnter).toHaveBeenCalledTimes(1)
|
expect(props.onAfterEnter).toHaveBeenCalledTimes(1)
|
||||||
|
|
||||||
assertCalls(props, {
|
assertCalls(props, {
|
||||||
|
@ -147,29 +205,278 @@ describe('BaseTransition', () => {
|
||||||
onAfterLeave: 1,
|
onAfterLeave: 1,
|
||||||
onLeaveCancelled: 0
|
onLeaveCancelled: 0
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
test('w/ element', async () => {
|
||||||
|
await testToggleOnOffBeforeFinish({
|
||||||
|
trueBranch: () => h('div'),
|
||||||
|
trueSerialized: `<div></div>`,
|
||||||
|
falseBranch: () => null,
|
||||||
|
falseSerialized: `<!---->`
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test('toggle between branches', () => {})
|
test('w/ component', async () => {
|
||||||
|
const Comp = ({ msg }: { msg: string }) => h('div', msg)
|
||||||
|
await testToggleOnOffBeforeFinish({
|
||||||
|
trueBranch: () => h(Comp, { msg: 'hello' }),
|
||||||
|
trueSerialized: `<div>hello</div>`,
|
||||||
|
falseBranch: () => null,
|
||||||
|
falseSerialized: `<!---->`
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
test('toggle between branches before finish', () => {})
|
describe('toggle between branches', () => {
|
||||||
|
async function testToggleBranches({
|
||||||
|
trueBranch,
|
||||||
|
falseBranch,
|
||||||
|
trueSerialized,
|
||||||
|
falseSerialized
|
||||||
|
}: ToggleOptions) {
|
||||||
|
const toggle = ref(true)
|
||||||
|
const { props, cbs } = mockProps()
|
||||||
|
const root = mount(
|
||||||
|
props,
|
||||||
|
() => (toggle.value ? trueBranch() : falseBranch())
|
||||||
|
)
|
||||||
|
|
||||||
test('persisted: true', () => {
|
// without appear: true, enter hooks should not be called on mount
|
||||||
// test onLeaveCancelled
|
expect(props.onBeforeEnter).not.toHaveBeenCalled()
|
||||||
|
expect(props.onEnter).not.toHaveBeenCalled()
|
||||||
|
expect(props.onAfterEnter).not.toHaveBeenCalled()
|
||||||
|
|
||||||
|
// start toggle
|
||||||
|
toggle.value = false
|
||||||
|
await nextTick()
|
||||||
|
expect(serializeInner(root)).toBe(`${trueSerialized}${falseSerialized}`)
|
||||||
|
// leave should be triggered
|
||||||
|
expect(props.onBeforeLeave).toHaveBeenCalledTimes(1)
|
||||||
|
assertCalledWithEl(props.onBeforeLeave, trueSerialized)
|
||||||
|
expect(props.onLeave).toHaveBeenCalledTimes(1)
|
||||||
|
assertCalledWithEl(props.onLeave, trueSerialized)
|
||||||
|
expect(props.onAfterLeave).not.toHaveBeenCalled()
|
||||||
|
// enter should also be triggered
|
||||||
|
expect(props.onBeforeEnter).toHaveBeenCalledTimes(1)
|
||||||
|
assertCalledWithEl(props.onBeforeEnter, falseSerialized)
|
||||||
|
expect(props.onEnter).toHaveBeenCalledTimes(1)
|
||||||
|
assertCalledWithEl(props.onEnter, falseSerialized)
|
||||||
|
expect(props.onAfterEnter).not.toHaveBeenCalled()
|
||||||
|
|
||||||
|
// finish enter
|
||||||
|
cbs.doneEnter[falseSerialized]()
|
||||||
|
expect(serializeInner(root)).toBe(`${trueSerialized}${falseSerialized}`)
|
||||||
|
expect(props.onAfterEnter).toHaveBeenCalledTimes(1)
|
||||||
|
assertCalledWithEl(props.onAfterEnter, falseSerialized)
|
||||||
|
expect(props.onAfterLeave).not.toHaveBeenCalled()
|
||||||
|
// finish leave
|
||||||
|
cbs.doneLeave[trueSerialized]()
|
||||||
|
expect(serializeInner(root)).toBe(`${falseSerialized}`)
|
||||||
|
expect(props.onAfterLeave).toHaveBeenCalledTimes(1)
|
||||||
|
assertCalledWithEl(props.onAfterLeave, trueSerialized)
|
||||||
|
|
||||||
|
// toggle again
|
||||||
|
toggle.value = true
|
||||||
|
await nextTick()
|
||||||
|
expect(serializeInner(root)).toBe(`${falseSerialized}${trueSerialized}`)
|
||||||
|
// leave should be triggered
|
||||||
|
expect(props.onBeforeLeave).toHaveBeenCalledTimes(2)
|
||||||
|
assertCalledWithEl(props.onBeforeLeave, falseSerialized, 1)
|
||||||
|
expect(props.onLeave).toHaveBeenCalledTimes(2)
|
||||||
|
assertCalledWithEl(props.onLeave, falseSerialized, 1)
|
||||||
|
expect(props.onAfterLeave).toHaveBeenCalledTimes(1)
|
||||||
|
// enter should also be triggered
|
||||||
|
expect(props.onBeforeEnter).toHaveBeenCalledTimes(2)
|
||||||
|
assertCalledWithEl(props.onBeforeEnter, trueSerialized, 1)
|
||||||
|
expect(props.onEnter).toHaveBeenCalledTimes(2)
|
||||||
|
assertCalledWithEl(props.onEnter, trueSerialized, 1)
|
||||||
|
expect(props.onAfterEnter).toHaveBeenCalledTimes(1)
|
||||||
|
|
||||||
|
// finish leave first
|
||||||
|
cbs.doneLeave[falseSerialized]()
|
||||||
|
expect(serializeInner(root)).toBe(`${trueSerialized}`)
|
||||||
|
expect(props.onAfterLeave).toHaveBeenCalledTimes(2)
|
||||||
|
assertCalledWithEl(props.onAfterLeave, falseSerialized, 1)
|
||||||
|
expect(props.onAfterEnter).toHaveBeenCalledTimes(1)
|
||||||
|
// finish enter
|
||||||
|
cbs.doneEnter[trueSerialized]()
|
||||||
|
expect(serializeInner(root)).toBe(`${trueSerialized}`)
|
||||||
|
expect(props.onAfterEnter).toHaveBeenCalledTimes(2)
|
||||||
|
assertCalledWithEl(props.onAfterEnter, trueSerialized, 1)
|
||||||
|
|
||||||
|
assertCalls(props, {
|
||||||
|
onBeforeEnter: 2,
|
||||||
|
onEnter: 2,
|
||||||
|
onAfterEnter: 2,
|
||||||
|
onBeforeLeave: 2,
|
||||||
|
onLeave: 2,
|
||||||
|
onAfterLeave: 2,
|
||||||
|
onEnterCancelled: 0,
|
||||||
|
onLeaveCancelled: 0
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
test('w/ elements', async () => {
|
||||||
|
await testToggleBranches({
|
||||||
|
trueBranch: () => h('div'),
|
||||||
|
falseBranch: () => h('span'),
|
||||||
|
trueSerialized: `<div></div>`,
|
||||||
|
falseSerialized: `<span></span>`
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test('appear: true', () => {})
|
test('w/ components', async () => {
|
||||||
|
const CompA = ({ msg }: { msg: string }) => h('div', msg)
|
||||||
test('mode: "out-in"', () => {})
|
// test HOC
|
||||||
|
const CompB = ({ msg }: { msg: string }) => h(CompC, { msg })
|
||||||
test('mode: "out-in" toggle before finish', () => {})
|
const CompC = ({ msg }: { msg: string }) => h('span', msg)
|
||||||
|
await testToggleBranches({
|
||||||
test('mode: "in-out" toggle before finish', () => {})
|
trueBranch: () => h(CompA, { msg: 'foo' }),
|
||||||
|
falseBranch: () => h(CompB, { msg: 'bar' }),
|
||||||
|
trueSerialized: `<div>foo</div>`,
|
||||||
|
falseSerialized: `<span>bar</span>`
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('with components', () => {
|
describe('toggle between branches before finish', () => {
|
||||||
// TODO
|
async function testToggleBranchesBeforeFinish({
|
||||||
|
trueBranch,
|
||||||
|
falseBranch,
|
||||||
|
trueSerialized,
|
||||||
|
falseSerialized
|
||||||
|
}: ToggleOptions) {
|
||||||
|
const toggle = ref(true)
|
||||||
|
const { props, cbs } = mockProps()
|
||||||
|
const root = mount(
|
||||||
|
props,
|
||||||
|
() => (toggle.value ? trueBranch() : falseBranch())
|
||||||
|
)
|
||||||
|
|
||||||
|
// start toggle
|
||||||
|
toggle.value = false
|
||||||
|
await nextTick()
|
||||||
|
expect(serializeInner(root)).toBe(`${trueSerialized}${falseSerialized}`)
|
||||||
|
// leave should be triggered
|
||||||
|
expect(props.onBeforeLeave).toHaveBeenCalledTimes(1)
|
||||||
|
assertCalledWithEl(props.onBeforeLeave, trueSerialized)
|
||||||
|
expect(props.onLeave).toHaveBeenCalledTimes(1)
|
||||||
|
assertCalledWithEl(props.onLeave, trueSerialized)
|
||||||
|
expect(props.onAfterLeave).not.toHaveBeenCalled()
|
||||||
|
// enter should also be triggered
|
||||||
|
expect(props.onBeforeEnter).toHaveBeenCalledTimes(1)
|
||||||
|
assertCalledWithEl(props.onBeforeEnter, falseSerialized)
|
||||||
|
expect(props.onEnter).toHaveBeenCalledTimes(1)
|
||||||
|
assertCalledWithEl(props.onEnter, falseSerialized)
|
||||||
|
expect(props.onAfterEnter).not.toHaveBeenCalled()
|
||||||
|
|
||||||
|
// toggle again before transition finishes
|
||||||
|
toggle.value = true
|
||||||
|
await nextTick()
|
||||||
|
// the previous leaving true branch should have been force-removed
|
||||||
|
expect(serializeInner(root)).toBe(`${falseSerialized}${trueSerialized}`)
|
||||||
|
expect(props.onAfterLeave).toHaveBeenCalledTimes(1)
|
||||||
|
assertCalledWithEl(props.onAfterLeave, trueSerialized)
|
||||||
|
// false branch enter is cancelled
|
||||||
|
expect(props.onEnterCancelled).toHaveBeenCalledTimes(1)
|
||||||
|
assertCalledWithEl(props.onEnterCancelled, falseSerialized)
|
||||||
|
// calling false branch done should have no effect now
|
||||||
|
cbs.doneEnter[falseSerialized]()
|
||||||
|
expect(props.onAfterEnter).not.toHaveBeenCalled()
|
||||||
|
// false branch leave triggered
|
||||||
|
expect(props.onBeforeLeave).toHaveBeenCalledTimes(2)
|
||||||
|
assertCalledWithEl(props.onBeforeLeave, falseSerialized, 1)
|
||||||
|
expect(props.onLeave).toHaveBeenCalledTimes(2)
|
||||||
|
assertCalledWithEl(props.onLeave, falseSerialized, 1)
|
||||||
|
// true branch enter triggered
|
||||||
|
expect(props.onBeforeEnter).toHaveBeenCalledTimes(2)
|
||||||
|
assertCalledWithEl(props.onBeforeEnter, trueSerialized, 1)
|
||||||
|
expect(props.onEnter).toHaveBeenCalledTimes(2)
|
||||||
|
assertCalledWithEl(props.onEnter, trueSerialized, 1)
|
||||||
|
expect(props.onAfterEnter).not.toHaveBeenCalled()
|
||||||
|
|
||||||
|
// toggle again
|
||||||
|
toggle.value = false
|
||||||
|
await nextTick()
|
||||||
|
// the previous leaving false branch should have been force-removed
|
||||||
|
expect(serializeInner(root)).toBe(`${trueSerialized}${falseSerialized}`)
|
||||||
|
expect(props.onAfterLeave).toHaveBeenCalledTimes(2)
|
||||||
|
assertCalledWithEl(props.onAfterLeave, falseSerialized, 1)
|
||||||
|
// true branch enter is cancelled
|
||||||
|
expect(props.onEnterCancelled).toHaveBeenCalledTimes(2)
|
||||||
|
assertCalledWithEl(props.onEnterCancelled, trueSerialized, 1)
|
||||||
|
// calling true branch enter done should have no effect
|
||||||
|
cbs.doneEnter[trueSerialized]()
|
||||||
|
expect(props.onAfterEnter).not.toHaveBeenCalled()
|
||||||
|
// true branch leave triggered (again)
|
||||||
|
expect(props.onBeforeLeave).toHaveBeenCalledTimes(3)
|
||||||
|
assertCalledWithEl(props.onBeforeLeave, trueSerialized, 2)
|
||||||
|
expect(props.onLeave).toHaveBeenCalledTimes(3)
|
||||||
|
assertCalledWithEl(props.onLeave, trueSerialized, 2)
|
||||||
|
// false branch enter triggered (again)
|
||||||
|
expect(props.onBeforeEnter).toHaveBeenCalledTimes(3)
|
||||||
|
assertCalledWithEl(props.onBeforeEnter, falseSerialized, 2)
|
||||||
|
expect(props.onEnter).toHaveBeenCalledTimes(3)
|
||||||
|
assertCalledWithEl(props.onEnter, falseSerialized, 2)
|
||||||
|
expect(props.onAfterEnter).not.toHaveBeenCalled()
|
||||||
|
|
||||||
|
cbs.doneEnter[falseSerialized]()
|
||||||
|
expect(props.onAfterEnter).toHaveBeenCalledTimes(1)
|
||||||
|
assertCalledWithEl(props.onAfterEnter, falseSerialized)
|
||||||
|
|
||||||
|
cbs.doneLeave[trueSerialized]()
|
||||||
|
expect(props.onAfterLeave).toHaveBeenCalledTimes(3)
|
||||||
|
assertCalledWithEl(props.onAfterLeave, trueSerialized, 2)
|
||||||
|
|
||||||
|
assertCalls(props, {
|
||||||
|
onBeforeEnter: 3,
|
||||||
|
onEnter: 3,
|
||||||
|
onAfterEnter: 1,
|
||||||
|
onEnterCancelled: 2,
|
||||||
|
onBeforeLeave: 3,
|
||||||
|
onLeave: 3,
|
||||||
|
onAfterLeave: 3,
|
||||||
|
onLeaveCancelled: 0
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
test('w/ elements', async () => {
|
||||||
|
await testToggleBranchesBeforeFinish({
|
||||||
|
trueBranch: () => h('div'),
|
||||||
|
falseBranch: () => h('span'),
|
||||||
|
trueSerialized: `<div></div>`,
|
||||||
|
falseSerialized: `<span></span>`
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('w/ components', async () => {
|
||||||
|
const CompA = ({ msg }: { msg: string }) => h('div', msg)
|
||||||
|
// test HOC
|
||||||
|
const CompB = ({ msg }: { msg: string }) => h(CompC, { msg })
|
||||||
|
const CompC = ({ msg }: { msg: string }) => h('span', msg)
|
||||||
|
await testToggleBranchesBeforeFinish({
|
||||||
|
trueBranch: () => h(CompA, { msg: 'foo' }),
|
||||||
|
falseBranch: () => h(CompB, { msg: 'bar' }),
|
||||||
|
trueSerialized: `<div>foo</div>`,
|
||||||
|
falseSerialized: `<span>bar</span>`
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('mode: "out-in"', () => {})
|
||||||
|
|
||||||
|
describe('mode: "out-in" toggle before finish', () => {})
|
||||||
|
|
||||||
|
describe('mode: "in-out"', () => {})
|
||||||
|
|
||||||
|
describe('mode: "in-out" toggle before finish', () => {})
|
||||||
|
|
||||||
|
test('persisted: true', () => {
|
||||||
|
// test onLeaveCancelled
|
||||||
|
})
|
||||||
|
|
||||||
|
test('appear: true', () => {})
|
||||||
|
|
||||||
describe('with KeepAlive', () => {
|
describe('with KeepAlive', () => {
|
||||||
// TODO
|
// TODO
|
||||||
})
|
})
|
||||||
|
|
|
@ -65,7 +65,7 @@ interface TransitionState {
|
||||||
isUnmounting: boolean
|
isUnmounting: boolean
|
||||||
// Track pending leave callbacks for children of the same key.
|
// Track pending leave callbacks for children of the same key.
|
||||||
// This is used to force remove leaving a child when a new copy is entering.
|
// This is used to force remove leaving a child when a new copy is entering.
|
||||||
leavingVNodes: Record<string, VNode>
|
leavingVNodes: Map<any, Record<string, VNode>>
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TransitionElement {
|
interface TransitionElement {
|
||||||
|
@ -84,7 +84,7 @@ const BaseTransitionImpl = {
|
||||||
isMounted: false,
|
isMounted: false,
|
||||||
isLeaving: false,
|
isLeaving: false,
|
||||||
isUnmounting: false,
|
isUnmounting: false,
|
||||||
leavingVNodes: Object.create(null)
|
leavingVNodes: new Map()
|
||||||
}
|
}
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
state.isMounted = true
|
state.isMounted = true
|
||||||
|
@ -230,8 +230,13 @@ function resolveTransitionHooks(
|
||||||
state: TransitionState,
|
state: TransitionState,
|
||||||
callHook: TransitionHookCaller
|
callHook: TransitionHookCaller
|
||||||
): TransitionHooks {
|
): TransitionHooks {
|
||||||
const { leavingVNodes } = state
|
|
||||||
const key = String(vnode.key)
|
const key = String(vnode.key)
|
||||||
|
const { leavingVNodes } = state
|
||||||
|
let leavingVNodesCache = leavingVNodes.get(vnode.type)!
|
||||||
|
if (!leavingVNodesCache) {
|
||||||
|
leavingVNodesCache = Object.create(null)
|
||||||
|
leavingVNodes.set(vnode.type, leavingVNodesCache)
|
||||||
|
}
|
||||||
|
|
||||||
const hooks: TransitionHooks = {
|
const hooks: TransitionHooks = {
|
||||||
persisted,
|
persisted,
|
||||||
|
@ -244,7 +249,7 @@ function resolveTransitionHooks(
|
||||||
el._leaveCb(true /* cancelled */)
|
el._leaveCb(true /* cancelled */)
|
||||||
}
|
}
|
||||||
// for toggled element with same key (v-if)
|
// for toggled element with same key (v-if)
|
||||||
const leavingVNode = leavingVNodes[key]
|
const leavingVNode = leavingVNodesCache[key]
|
||||||
if (
|
if (
|
||||||
leavingVNode &&
|
leavingVNode &&
|
||||||
isSameVNodeType(vnode, leavingVNode) &&
|
isSameVNodeType(vnode, leavingVNode) &&
|
||||||
|
@ -301,9 +306,11 @@ function resolveTransitionHooks(
|
||||||
callHook(onAfterLeave, [el])
|
callHook(onAfterLeave, [el])
|
||||||
}
|
}
|
||||||
el._leaveCb = undefined
|
el._leaveCb = undefined
|
||||||
delete leavingVNodes[key]
|
if (leavingVNodesCache[key] === vnode) {
|
||||||
|
delete leavingVNodesCache[key]
|
||||||
|
}
|
||||||
})
|
})
|
||||||
leavingVNodes[key] = vnode
|
leavingVNodesCache[key] = vnode
|
||||||
if (onLeave) {
|
if (onLeave) {
|
||||||
onLeave(el, afterLeave)
|
onLeave(el, afterLeave)
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Reference in New Issue