test(transition): test for mode: out-in

This commit is contained in:
Evan You 2019-11-27 15:25:18 -05:00
parent 7209fb66c2
commit 7aac3418c0

View File

@ -16,7 +16,7 @@ function mount(props: BaseTransitionProps, slot: () => any) {
return root return root
} }
function mockProps() { function mockProps(extra: BaseTransitionProps = {}) {
const cbs: { const cbs: {
doneEnter: Record<string, () => void> doneEnter: Record<string, () => void>
doneLeave: Record<string, () => void> doneLeave: Record<string, () => void>
@ -38,7 +38,8 @@ function mockProps() {
cbs.doneLeave[serialize(el)] = done cbs.doneLeave[serialize(el)] = done
}), }),
onAfterLeave: jest.fn(), onAfterLeave: jest.fn(),
onLeaveCancelled: jest.fn() onLeaveCancelled: jest.fn(),
...extra
} }
return { return {
props, props,
@ -66,6 +67,28 @@ interface ToggleOptions {
falseSerialized: string falseSerialized: string
} }
async function runTestWithElements(tester: (o: ToggleOptions) => void) {
await tester({
trueBranch: () => h('div'),
falseBranch: () => h('span'),
trueSerialized: `<div></div>`,
falseSerialized: `<span></span>`
})
}
async function runTestWithComponents(tester: (o: ToggleOptions) => void) {
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 tester({
trueBranch: () => h(CompA, { msg: 'foo' }),
falseBranch: () => h(CompB, { msg: 'bar' }),
trueSerialized: `<div>foo</div>`,
falseSerialized: `<span>bar</span>`
})
}
describe('BaseTransition', () => { describe('BaseTransition', () => {
describe('toggle on-off', () => { describe('toggle on-off', () => {
async function testToggleOnOff({ async function testToggleOnOff({
@ -317,25 +340,11 @@ describe('BaseTransition', () => {
} }
test('w/ elements', async () => { test('w/ elements', async () => {
await testToggleBranches({ await runTestWithElements(testToggleBranches)
trueBranch: () => h('div'),
falseBranch: () => h('span'),
trueSerialized: `<div></div>`,
falseSerialized: `<span></span>`
})
}) })
test('w/ components', async () => { test('w/ components', async () => {
const CompA = ({ msg }: { msg: string }) => h('div', msg) await runTestWithComponents(testToggleBranches)
// test HOC
const CompB = ({ msg }: { msg: string }) => h(CompC, { msg })
const CompC = ({ msg }: { msg: string }) => h('span', msg)
await testToggleBranches({
trueBranch: () => h(CompA, { msg: 'foo' }),
falseBranch: () => h(CompB, { msg: 'bar' }),
trueSerialized: `<div>foo</div>`,
falseSerialized: `<span>bar</span>`
})
}) })
}) })
@ -441,31 +450,249 @@ describe('BaseTransition', () => {
} }
test('w/ elements', async () => { test('w/ elements', async () => {
await testToggleBranchesBeforeFinish({ await runTestWithElements(testToggleBranchesBeforeFinish)
trueBranch: () => h('div'),
falseBranch: () => h('span'),
trueSerialized: `<div></div>`,
falseSerialized: `<span></span>`
})
}) })
test('w/ components', async () => { test('w/ components', async () => {
const CompA = ({ msg }: { msg: string }) => h('div', msg) await runTestWithComponents(testToggleBranchesBeforeFinish)
// 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"', () => {
async function testOutIn({
trueBranch,
falseBranch,
trueSerialized,
falseSerialized
}: ToggleOptions) {
const toggle = ref(true)
const { props, cbs } = mockProps({ mode: 'out-in' })
const root = mount(
props,
() => (toggle.value ? trueBranch() : falseBranch())
)
describe('mode: "out-in" toggle before finish', () => {}) // trigger toggle
toggle.value = false
await nextTick()
// a placeholder is injected until the leave finishes
expect(serializeInner(root)).toBe(`${trueSerialized}<!---->`)
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 not have started
expect(props.onBeforeEnter).not.toHaveBeenCalled()
expect(props.onEnter).not.toHaveBeenCalled()
expect(props.onAfterEnter).not.toHaveBeenCalled()
cbs.doneLeave[trueSerialized]()
expect(props.onAfterLeave).toHaveBeenCalledTimes(1)
assertCalledWithEl(props.onAfterLeave, trueSerialized)
// have to wait for a tick because this triggers an update
await nextTick()
expect(serializeInner(root)).toBe(falseSerialized)
// enter should start
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(props.onAfterEnter).toHaveBeenCalledTimes(1)
assertCalledWithEl(props.onAfterEnter, falseSerialized)
// toggele again
toggle.value = true
await nextTick()
expect(serializeInner(root)).toBe(`${falseSerialized}<!---->`)
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 not have started
expect(props.onBeforeEnter).toHaveBeenCalledTimes(1)
expect(props.onEnter).toHaveBeenCalledTimes(1)
expect(props.onAfterEnter).toHaveBeenCalledTimes(1)
cbs.doneLeave[falseSerialized]()
expect(props.onAfterLeave).toHaveBeenCalledTimes(2)
assertCalledWithEl(props.onAfterLeave, falseSerialized, 1)
await nextTick()
expect(serializeInner(root)).toBe(trueSerialized)
// enter should start
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 enter
cbs.doneEnter[trueSerialized]()
expect(props.onAfterEnter).toHaveBeenCalledTimes(2)
assertCalledWithEl(props.onAfterEnter, trueSerialized, 1)
assertCalls(props, {
onBeforeEnter: 2,
onEnter: 2,
onAfterEnter: 2,
onEnterCancelled: 0,
onBeforeLeave: 2,
onLeave: 2,
onAfterLeave: 2,
onLeaveCancelled: 0
})
}
test('w/ elements', async () => {
await runTestWithElements(testOutIn)
})
test('w/ components', async () => {
await runTestWithComponents(testOutIn)
})
})
describe('mode: "out-in" toggle before finish', () => {
async function testOutInBeforeFinish({
trueBranch,
falseBranch,
trueSerialized,
falseSerialized
}: ToggleOptions) {
const toggle = ref(true)
const { props, cbs } = mockProps({ mode: 'out-in' })
const root = mount(
props,
() => (toggle.value ? trueBranch() : falseBranch())
)
// trigger toggle
toggle.value = false
await nextTick()
// toggle again before finish
toggle.value = true
await nextTick()
// expected behavior: the previous true branch is preserved,
// and a placeholder is injected for the replacement.
// the leaving node is repalced with the replace node (of the same branch)
// when it finishes leaving
expect(serializeInner(root)).toBe(`${trueSerialized}<!---->`)
// enter hooks should never be called (for neither branch)
expect(props.onBeforeEnter).not.toHaveBeenCalled()
expect(props.onEnter).not.toHaveBeenCalled()
expect(props.onAfterEnter).not.toHaveBeenCalled()
// finish leave
cbs.doneLeave[trueSerialized]()
expect(props.onAfterLeave).toHaveBeenCalledTimes(1)
assertCalledWithEl(props.onAfterLeave, trueSerialized)
await nextTick()
// leaving node and placeholder removed, enter node injected
expect(serializeInner(root)).toBe(trueSerialized)
// enter should start
expect(props.onBeforeEnter).toHaveBeenCalledTimes(1)
assertCalledWithEl(props.onBeforeEnter, trueSerialized)
expect(props.onEnter).toHaveBeenCalledTimes(1)
assertCalledWithEl(props.onEnter, trueSerialized)
expect(props.onAfterEnter).not.toHaveBeenCalled()
// finish enter
cbs.doneEnter[trueSerialized]()
expect(props.onAfterEnter).toHaveBeenCalledTimes(1)
assertCalledWithEl(props.onAfterEnter, trueSerialized)
assertCalls(props, {
onBeforeEnter: 1,
onEnter: 1,
onAfterEnter: 1,
onEnterCancelled: 0,
onBeforeLeave: 1,
onLeave: 1,
onAfterLeave: 1,
onLeaveCancelled: 0
})
}
test('w/ elements', async () => {
await runTestWithElements(testOutInBeforeFinish)
})
test('w/ components', async () => {
await runTestWithComponents(testOutInBeforeFinish)
})
})
describe('mode: "out-in" double quick toggle', () => {
async function testOutInDoubleToggle({
trueBranch,
falseBranch,
trueSerialized,
falseSerialized
}: ToggleOptions) {
const toggle = ref(true)
const { props, cbs } = mockProps({ mode: 'out-in' })
const root = mount(
props,
() => (toggle.value ? trueBranch() : falseBranch())
)
// double quick toggle
toggle.value = false
await nextTick()
toggle.value = true
await nextTick()
toggle.value = false
await nextTick()
// expected behavior: the leaving true branch is preserved no matter
// how many times the state is toggled as long as the leave isn't finished
// yet. A placeholder is injected for the replacement.
expect(serializeInner(root)).toBe(`${trueSerialized}<!---->`)
// enter hooks should never be called (for neither branch)
expect(props.onBeforeEnter).not.toHaveBeenCalled()
expect(props.onEnter).not.toHaveBeenCalled()
expect(props.onAfterEnter).not.toHaveBeenCalled()
// finish leave
cbs.doneLeave[trueSerialized]()
expect(props.onAfterLeave).toHaveBeenCalledTimes(1)
assertCalledWithEl(props.onAfterLeave, trueSerialized)
await nextTick()
// leaving node and placeholder removed, enter node injected
expect(serializeInner(root)).toBe(falseSerialized)
// enter should start
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(props.onAfterEnter).toHaveBeenCalledTimes(1)
assertCalledWithEl(props.onAfterEnter, falseSerialized)
assertCalls(props, {
onBeforeEnter: 1,
onEnter: 1,
onAfterEnter: 1,
onEnterCancelled: 0,
onBeforeLeave: 1,
onLeave: 1,
onAfterLeave: 1,
onLeaveCancelled: 0
})
}
test('w/ elements', async () => {
await runTestWithElements(testOutInDoubleToggle)
})
test('w/ components', async () => {
await runTestWithComponents(testOutInDoubleToggle)
})
})
describe('mode: "in-out"', () => {}) describe('mode: "in-out"', () => {})