import path from 'node:path'
import {
E2E_TIMEOUT,
setupPuppeteer,
} from '../../../packages/vue/__tests__/e2e/e2eUtils'
import connect from 'connect'
import sirv from 'sirv'
import { nextTick } from 'vue'
const {
page,
classList,
timeout,
isVisible,
html,
nextFrame,
transitionStart,
waitForInnerHTML,
click,
domClick,
} = setupPuppeteer()
const duration = process.env.CI ? 200 : 50
const buffer = process.env.CI ? 50 : 20
describe('vapor transition', () => {
let server: any
const port = '8195'
beforeAll(() => {
server = connect()
.use(sirv(path.resolve(import.meta.dirname, '../dist')))
.listen(port)
process.on('SIGTERM', () => server && server.close())
})
afterAll(() => {
server.close()
})
beforeEach(async () => {
const baseUrl = `http://localhost:${port}/transition/`
await page().evaluateOnNewDocument(dur => {
;(window as any).__TRANSITION_DURATION__ = dur
}, duration)
await page().goto(baseUrl)
await page().waitForSelector('#app')
})
describe('transition with v-if', () => {
test(
'basic transition',
async () => {
const btnSelector = '.if-basic > button'
const containerSelector = '.if-basic > div'
const childSelector = `${containerSelector} > div`
expect(await html(containerSelector)).toBe(
`
content
`,
)
// leave
expect(
(await transitionStart(btnSelector, childSelector)).outerHTML,
).toBe(`content
`)
await waitForInnerHTML(
containerSelector,
`content
`,
)
await waitForInnerHTML(containerSelector, ``)
// enter
expect(
(await transitionStart(btnSelector, childSelector)).outerHTML,
).toBe(`content
`)
await waitForInnerHTML(
containerSelector,
`content
`,
)
await waitForInnerHTML(
containerSelector,
'content
',
)
},
E2E_TIMEOUT,
)
test(
'if/else-if/else chain transition',
async () => {
const btnSelector = '.if-else-chain > button'
const containerSelector = '.if-else-chain > div'
const childSelector = `${containerSelector} > div`
expect(await html(containerSelector)).toBe('0
')
expect(
(await transitionStart(btnSelector, childSelector)).outerHTML,
).toBe(`0
`)
await waitForInnerHTML(containerSelector, '1
')
expect(
(await transitionStart(btnSelector, childSelector)).outerHTML,
).toBe(`1
`)
await waitForInnerHTML(containerSelector, '2
')
},
E2E_TIMEOUT,
)
test(
'if/else-if/else chain transition (out-in mode)',
async () => {
const btnSelector = '.if-else-chain-out-in > button'
const containerSelector = '.if-else-chain-out-in > div'
const childSelector = `${containerSelector} > div`
expect(await html(containerSelector)).toBe('0
')
expect(
(await transitionStart(btnSelector, childSelector)).outerHTML,
).toBe(`0
`)
await waitForInnerHTML(
containerSelector,
'0
',
)
await waitForInnerHTML(
containerSelector,
'1
',
)
await waitForInnerHTML(
containerSelector,
'1
',
)
await waitForInnerHTML(containerSelector, '1
')
expect(
(await transitionStart(btnSelector, childSelector)).outerHTML,
).toBe(`1
`)
await waitForInnerHTML(
containerSelector,
'1
',
)
await waitForInnerHTML(
containerSelector,
'2
',
)
await waitForInnerHTML(
containerSelector,
'2
',
)
await waitForInnerHTML(containerSelector, '2
')
},
E2E_TIMEOUT,
)
test(
'named transition',
async () => {
const btnSelector = '.if-named > button'
const containerSelector = '.if-named > div'
const childSelector = `${containerSelector} > div`
expect(await html(containerSelector)).toBe(
'content
',
)
// leave
expect(
(await transitionStart(btnSelector, childSelector)).outerHTML,
).toBe(
`content
`,
)
await waitForInnerHTML(
containerSelector,
'content
',
)
await waitForInnerHTML(containerSelector, '')
// enter
expect(
(await transitionStart(btnSelector, childSelector)).outerHTML,
).toBe(
`content
`,
)
await waitForInnerHTML(
containerSelector,
'content
',
)
await waitForInnerHTML(
containerSelector,
'content
',
)
},
E2E_TIMEOUT,
)
test(
'custom transition classes',
async () => {
const btnSelector = '.if-custom-classes > button'
const containerSelector = '.if-custom-classes > div'
const childSelector = `${containerSelector} > div`
expect(await html(containerSelector)).toBe(
'content
',
)
// leave
expect(
(await transitionStart(btnSelector, childSelector)).outerHTML,
).toBe(`content
`)
await waitForInnerHTML(
containerSelector,
'content
',
)
await waitForInnerHTML(containerSelector, '')
// enter
expect(
(await transitionStart(btnSelector, childSelector)).outerHTML,
).toBe(`content
`)
await waitForInnerHTML(
containerSelector,
'content
',
)
await waitForInnerHTML(
containerSelector,
'content
',
)
},
E2E_TIMEOUT,
)
test(
'transition with dynamic name',
async () => {
const btnSelector = '.if-dynamic-name > button.toggle'
const btnChangeNameSelector = '.if-dynamic-name > button.change'
const containerSelector = '.if-dynamic-name > div'
const childSelector = `${containerSelector} > div`
expect(await html(containerSelector)).toBe(
'content
',
)
// leave
expect(
(await transitionStart(btnSelector, childSelector)).outerHTML,
).toBe(
`content
`,
)
await waitForInnerHTML(
containerSelector,
'content
',
)
await waitForInnerHTML(containerSelector, '')
// enter
await domClick(btnChangeNameSelector)
await waitForInnerHTML(btnChangeNameSelector, 'changed')
expect(
(await transitionStart(btnSelector, childSelector)).classNames,
).toStrictEqual(['test', 'changed-enter-from', 'changed-enter-active'])
await waitForInnerHTML(
containerSelector,
'content
',
)
await waitForInnerHTML(
containerSelector,
'content
',
)
},
E2E_TIMEOUT,
)
test(
'transition events without appear',
async () => {
const btnSelector = '.if-events-without-appear > button'
const containerSelector = '.if-events-without-appear > div'
const childSelector = `${containerSelector} > div`
expect(await html(containerSelector)).toBe(
'content
',
)
// leave
expect(
(await transitionStart(btnSelector, childSelector)).classNames,
).toStrictEqual(['test', 'test-leave-from', 'test-leave-active'])
let calls = await page().evaluate(() => {
return (window as any).getCalls('withoutAppear')
})
expect(calls).toStrictEqual(['beforeLeave', 'onLeave'])
await waitForInnerHTML(
containerSelector,
'content
',
)
expect(
await page().evaluate(() => {
return (window as any).getCalls('withoutAppear')
}),
).not.contain('afterLeave')
await waitForInnerHTML(containerSelector, '')
expect(
await page().evaluate(() => {
return (window as any).getCalls('withoutAppear')
}),
).toStrictEqual(['beforeLeave', 'onLeave', 'afterLeave'])
await page().evaluate(() => {
;(window as any).resetCalls('withoutAppear')
})
// enter
expect(
(await transitionStart(btnSelector, childSelector)).classNames,
).toStrictEqual(['test', 'test-enter-from', 'test-enter-active'])
calls = await page().evaluate(() => {
return (window as any).getCalls('withoutAppear')
})
expect(calls).toStrictEqual(['beforeEnter', 'onEnter'])
await waitForInnerHTML(
containerSelector,
'content
',
)
expect(
await page().evaluate(() => {
return (window as any).getCalls('withoutAppear')
}),
).not.contain('afterEnter')
await waitForInnerHTML(
containerSelector,
'content
',
)
expect(
await page().evaluate(() => {
return (window as any).getCalls('withoutAppear')
}),
).toStrictEqual(['beforeEnter', 'onEnter', 'afterEnter'])
},
E2E_TIMEOUT,
)
test(
'events with arguments',
async () => {
const btnSelector = '.if-events-with-args > button'
const containerSelector = '.if-events-with-args > div'
const childSelector = `${containerSelector} > div`
expect(await html(containerSelector)).toBe(
'content
',
)
// leave
await click(btnSelector)
let calls = await page().evaluate(() => {
return (window as any).getCalls('withArgs')
})
expect(calls).toStrictEqual(['beforeLeave', 'onLeave'])
expect(await classList(childSelector)).toStrictEqual([
'test',
'before-leave',
'leave',
])
await timeout(200 + buffer)
calls = await page().evaluate(() => {
return (window as any).getCalls('withArgs')
})
expect(calls).toStrictEqual(['beforeLeave', 'onLeave', 'afterLeave'])
expect(await html(containerSelector)).toBe('')
await page().evaluate(() => {
;(window as any).resetCalls('withArgs')
})
// enter
await click(btnSelector)
calls = await page().evaluate(() => {
return (window as any).getCalls('withArgs')
})
expect(calls).toStrictEqual(['beforeEnter', 'onEnter'])
expect(await classList(childSelector)).toStrictEqual([
'test',
'before-enter',
'enter',
])
await timeout(200 + buffer)
calls = await page().evaluate(() => {
return (window as any).getCalls('withArgs')
})
expect(calls).toStrictEqual(['beforeEnter', 'onEnter', 'afterEnter'])
expect(await html(containerSelector)).toBe(
'content
',
)
},
E2E_TIMEOUT,
)
test(
'onEnterCancelled',
async () => {
const btnSelector = '.if-enter-cancelled > button'
const containerSelector = '.if-enter-cancelled > div'
const childSelector = `${containerSelector} > div`
expect(await html(containerSelector)).toBe('')
// enter
expect(
(await transitionStart(btnSelector, childSelector)).classNames,
).toStrictEqual(['test', 'test-enter-from', 'test-enter-active'])
await waitForInnerHTML(
containerSelector,
'content
',
)
// cancel (leave)
expect(
(await transitionStart(btnSelector, childSelector)).classNames,
).toStrictEqual(['test', 'test-leave-from', 'test-leave-active'])
let calls = await page().evaluate(() => {
return (window as any).getCalls('enterCancel')
})
expect(calls).toStrictEqual(['enterCancelled'])
await waitForInnerHTML(
containerSelector,
'content
',
)
await waitForInnerHTML(containerSelector, '')
},
E2E_TIMEOUT,
)
test(
'transition on appear',
async () => {
const btnSelector = '.if-appear > button'
const containerSelector = '.if-appear > div'
const childSelector = `${containerSelector} > div`
// appear
expect(await classList(childSelector)).contains('test-appear-active')
await waitForInnerHTML(
containerSelector,
'content
',
)
// leave
expect(
(await transitionStart(btnSelector, childSelector)).classNames,
).toStrictEqual(['test', 'test-leave-from', 'test-leave-active'])
await waitForInnerHTML(
containerSelector,
'content
',
)
await waitForInnerHTML(containerSelector, '')
// enter
expect(
(await transitionStart(btnSelector, childSelector)).classNames,
).toStrictEqual(['test', 'test-enter-from', 'test-enter-active'])
await waitForInnerHTML(
containerSelector,
'content
',
)
await waitForInnerHTML(
containerSelector,
'content
',
)
},
E2E_TIMEOUT,
)
test(
'transition events with appear',
async () => {
const btnSelector = '.if-events-with-appear > button'
const containerSelector = '.if-events-with-appear > div'
const childSelector = `${containerSelector} > div`
// appear
expect(await classList(childSelector)).contains('test-appear-active')
let calls = await page().evaluate(() => {
return (window as any).getCalls('withAppear')
})
expect(calls).toStrictEqual(['beforeAppear', 'onAppear'])
await waitForInnerHTML(
containerSelector,
'content
',
)
calls = await page().evaluate(() => {
return (window as any).getCalls('withAppear')
})
expect(calls).toStrictEqual(['beforeAppear', 'onAppear', 'afterAppear'])
await page().evaluate(() => {
;(window as any).resetCalls('withAppear')
})
// leave
expect(
(await transitionStart(btnSelector, childSelector)).classNames,
).toStrictEqual(['test', 'test-leave-from', 'test-leave-active'])
calls = await page().evaluate(() => {
return (window as any).getCalls('withAppear')
})
expect(calls).toStrictEqual(['beforeLeave', 'onLeave'])
await waitForInnerHTML(
containerSelector,
'content
',
)
calls = await page().evaluate(() => {
return (window as any).getCalls('withAppear')
})
expect(calls).not.contain('afterLeave')
await waitForInnerHTML(containerSelector, '')
calls = await page().evaluate(() => {
return (window as any).getCalls('withAppear')
})
expect(calls).toStrictEqual(['beforeLeave', 'onLeave', 'afterLeave'])
await page().evaluate(() => {
;(window as any).resetCalls('withAppear')
})
// enter
expect(
(await transitionStart(btnSelector, childSelector)).classNames,
).toStrictEqual(['test', 'test-enter-from', 'test-enter-active'])
calls = await page().evaluate(() => {
return (window as any).getCalls('withAppear')
})
expect(calls).toStrictEqual(['beforeEnter', 'onEnter'])
await waitForInnerHTML(
containerSelector,
'content
',
)
calls = await page().evaluate(() => {
return (window as any).getCalls('withAppear')
})
expect(calls).not.contain('afterEnter')
await waitForInnerHTML(
containerSelector,
'content
',
)
calls = await page().evaluate(() => {
return (window as any).getCalls('withAppear')
})
expect(calls).toStrictEqual(['beforeEnter', 'onEnter', 'afterEnter'])
},
E2E_TIMEOUT,
)
test(
'css: false',
async () => {
const btnSelector = '.if-css-false > button'
const containerSelector = '.if-css-false > div'
const childSelector = `${containerSelector} > div`
expect(await html(containerSelector)).toBe(
'content
',
)
// leave
await click(btnSelector)
let calls = await page().evaluate(() => {
return (window as any).getCalls('cssFalse')
})
expect(calls).toStrictEqual(['beforeLeave', 'onLeave', 'afterLeave'])
expect(await html(containerSelector)).toBe('')
await page().evaluate(() => {
;(window as any).resetCalls('cssFalse')
})
// enter
await transitionStart(btnSelector, childSelector)
calls = await page().evaluate(() => {
return (window as any).getCalls('cssFalse')
})
expect(calls).toStrictEqual(['beforeEnter', 'onEnter', 'afterEnter'])
expect(await html(containerSelector)).toBe(
'content
',
)
},
E2E_TIMEOUT,
)
test(
'no transition detected',
async () => {
const btnSelector = '.if-no-trans > button'
const containerSelector = '.if-no-trans > div'
const childSelector = `${containerSelector} > div`
expect(await html(containerSelector)).toBe('content
')
// leave
expect(
(await transitionStart(btnSelector, childSelector)).classNames,
).toStrictEqual(['noop-leave-from', 'noop-leave-active'])
await waitForInnerHTML(containerSelector, '')
// enter
expect(
(await transitionStart(btnSelector, childSelector)).classNames,
).toStrictEqual(['noop-enter-from', 'noop-enter-active'])
await waitForInnerHTML(containerSelector, 'content
')
},
E2E_TIMEOUT,
)
test(
'animations',
async () => {
const btnSelector = '.if-ani > button'
const containerSelector = '.if-ani > div'
const childSelector = `${containerSelector} > div`
expect(await html(containerSelector)).toBe('content
')
// leave
expect(
(await transitionStart(btnSelector, childSelector)).classNames,
).toStrictEqual(['test-anim-leave-from', 'test-anim-leave-active'])
await waitForInnerHTML(
containerSelector,
'content
',
)
await waitForInnerHTML(containerSelector, '')
// enter
expect(
(await transitionStart(btnSelector, childSelector)).classNames,
).toStrictEqual(['test-anim-enter-from', 'test-anim-enter-active'])
await waitForInnerHTML(
containerSelector,
'content
',
)
await waitForInnerHTML(containerSelector, 'content
')
},
E2E_TIMEOUT,
)
test(
'explicit transition type',
async () => {
const btnSelector = '.if-ani-explicit-type > button'
const containerSelector = '.if-ani-explicit-type > div'
const childSelector = `${containerSelector} > div`
expect(await html(containerSelector)).toBe('content
')
// leave
expect(
(await transitionStart(btnSelector, childSelector)).classNames,
).toStrictEqual([
'test-anim-long-leave-from',
'test-anim-long-leave-active',
])
await waitForInnerHTML(
containerSelector,
'content
',
)
await waitForInnerHTML(containerSelector, '')
// enter
expect(
(await transitionStart(btnSelector, childSelector)).classNames,
).toStrictEqual([
'test-anim-long-enter-from',
'test-anim-long-enter-active',
])
await waitForInnerHTML(
containerSelector,
'content
',
)
await waitForInnerHTML(containerSelector, 'content
')
},
E2E_TIMEOUT,
)
test(
'transition on SVG elements',
async () => {
const btnSelector = '.svg > button'
const containerSelector = '.svg > #container'
expect(await html(containerSelector)).toBe(
' ',
)
// leave
await click(btnSelector)
await waitForInnerHTML(
containerSelector,
' ',
)
await waitForInnerHTML(
containerSelector,
' ',
)
await waitForInnerHTML(containerSelector, '')
// enter
await click(btnSelector)
await waitForInnerHTML(
containerSelector,
' ',
)
await waitForInnerHTML(
containerSelector,
' ',
)
await waitForInnerHTML(
containerSelector,
' ',
)
},
E2E_TIMEOUT,
)
test(
'custom transition higher-order component',
async () => {
const btnSelector = '.if-high-order > button'
const containerSelector = '.if-high-order > div'
const childSelector = `${containerSelector} > div`
expect(await html(containerSelector)).toBe(
'content
',
)
// leave
expect(
(await transitionStart(btnSelector, childSelector)).classNames,
).toStrictEqual(['test', 'test-leave-from', 'test-leave-active'])
await waitForInnerHTML(
containerSelector,
'content
',
)
await waitForInnerHTML(containerSelector, '')
// enter
expect(
(await transitionStart(btnSelector, childSelector)).classNames,
).toStrictEqual(['test', 'test-enter-from', 'test-enter-active'])
await waitForInnerHTML(
containerSelector,
'content
',
)
await waitForInnerHTML(
containerSelector,
'content
',
)
},
E2E_TIMEOUT,
)
test(
'transition on child components with empty root node',
async () => {
const btnSelector = '.if-empty-root > button.toggle'
const btnChangeSelector = '.if-empty-root > button.change'
const containerSelector = '.if-empty-root > div'
const childSelector = `${containerSelector} > div`
expect(await html(containerSelector)).toBe('')
// change view -> 'two'
await click(btnChangeSelector)
// enter
expect(
(await transitionStart(btnSelector, childSelector)).classNames,
).toStrictEqual(['test', 'test-enter-from', 'test-enter-active'])
await waitForInnerHTML(
containerSelector,
'two
',
)
await waitForInnerHTML(containerSelector, 'two
')
// change view -> 'one'
await click(btnChangeSelector)
// leave
expect(
(await transitionStart(btnSelector, childSelector)).classNames,
).toStrictEqual(['test', 'test-leave-from', 'test-leave-active'])
await waitForInnerHTML(
containerSelector,
'two
',
)
await waitForInnerHTML(containerSelector, '')
},
E2E_TIMEOUT,
)
test(
'transition with v-if at component root-level',
async () => {
const btnSelector = '.if-at-component-root-level > button.toggle'
const btnChangeSelector = '.if-at-component-root-level > button.change'
const containerSelector = '.if-at-component-root-level > div'
const childSelector = `${containerSelector} > div`
expect(await html(containerSelector)).toBe('')
// change view -> 'two'
await click(btnChangeSelector)
// enter
expect(
(await transitionStart(btnSelector, childSelector)).classNames,
).toStrictEqual(['test', 'test-enter-from', 'test-enter-active'])
await waitForInnerHTML(
containerSelector,
'two
',
)
await waitForInnerHTML(containerSelector, 'two
')
// change view -> 'one'
await click(btnChangeSelector)
// leave
expect(
(await transitionStart(btnSelector, childSelector)).classNames,
).toStrictEqual(['test', 'test-leave-from', 'test-leave-active'])
await waitForInnerHTML(
containerSelector,
'two
',
)
await waitForInnerHTML(containerSelector, '')
},
E2E_TIMEOUT,
)
test(
'wrapping transition + fallthrough attrs',
async () => {
const btnSelector = '.if-fallthrough-attr > button'
const containerSelector = '.if-fallthrough-attr > div'
expect(await html(containerSelector)).toBe('content
')
await click(btnSelector)
// toggle again before leave finishes
await nextTick()
await click(btnSelector)
await waitForInnerHTML(
containerSelector,
'content
',
)
},
E2E_TIMEOUT,
)
test(
'transition + fallthrough attrs (in-out mode)',
async () => {
const btnSelector = '.if-fallthrough-attr-in-out > button'
const containerSelector = '.if-fallthrough-attr-in-out > div'
expect(await html(containerSelector)).toBe('one
')
// toggle
await click(btnSelector)
await nextTick()
await waitForInnerHTML(
containerSelector,
'two
',
)
let calls = await page().evaluate(() => {
return (window as any).getCalls('ifInOut')
})
expect(calls).toStrictEqual([
'beforeEnter',
'onEnter',
'afterEnter',
'beforeLeave',
'onLeave',
'afterLeave',
])
expect(await html(containerSelector)).toBe(
'two
',
)
// clear calls
await page().evaluate(() => {
;(window as any).resetCalls('ifInOut')
})
// toggle back
await click(btnSelector)
await nextTick()
await waitForInnerHTML(
containerSelector,
'one
',
)
calls = await page().evaluate(() => {
return (window as any).getCalls('ifInOut')
})
expect(calls).toStrictEqual([
'beforeEnter',
'onEnter',
'afterEnter',
'beforeLeave',
'onLeave',
'afterLeave',
])
expect(await html(containerSelector)).toBe(
'one
',
)
},
E2E_TIMEOUT,
)
})
describe('transition with KeepAlive', () => {
test('unmount innerChild (out-in mode)', async () => {
const btnSelector = '.keep-alive > button'
const containerSelector = '.keep-alive > div'
await waitForInnerHTML(containerSelector, '0
')
await click(btnSelector)
await waitForInnerHTML(containerSelector, '')
const calls = await page().evaluate(() => {
return (window as any).getCalls('unmount')
})
expect(calls).toStrictEqual(['TrueBranch'])
})
// #11775
test(
'switch child then update include (out-in mode)',
async () => {
const containerSelector = '.keep-alive-update-include > div'
const btnSwitchToB = '.keep-alive-update-include > #switchToB'
const btnSwitchToA = '.keep-alive-update-include > #switchToA'
const btnSwitchToC = '.keep-alive-update-include > #switchToC'
expect(await html(containerSelector)).toBe('CompA
')
await click(btnSwitchToB)
await nextTick()
await click(btnSwitchToC)
await waitForInnerHTML(containerSelector, 'CompC
')
await click(btnSwitchToA)
await waitForInnerHTML(containerSelector, 'CompA
')
let calls = await page().evaluate(() => {
return (window as any).getCalls('unmount')
})
expect(calls).toStrictEqual(['CompC unmounted'])
// Unlike vdom, CompA does not update because there are no state changes
// expect CompA only update once
// calls = await page().evaluate(() => {
// return (window as any).getCalls('updated')
// })
// expect(calls).toStrictEqual(['CompA updated'])
},
E2E_TIMEOUT,
)
// #10827
test(
'switch and update child then update include (out-in mode)',
async () => {
const containerSelector = '.keep-alive-switch-then-update-include > div'
const btnSwitchToA =
'.keep-alive-switch-then-update-include > #switchToA'
const btnSwitchToB =
'.keep-alive-switch-then-update-include > #switchToB'
expect(await html(containerSelector)).toBe('CompA2
')
await click(btnSwitchToB)
await waitForInnerHTML(containerSelector, 'CompB2
')
await click(btnSwitchToA)
await waitForInnerHTML(containerSelector, 'CompA2
')
let calls = await page().evaluate(() => {
return (window as any).getCalls('unmount')
})
expect(calls).toStrictEqual(['CompB2 unmounted'])
},
E2E_TIMEOUT,
)
// #12860
test(
'unmount children',
async () => {
const containerSelector = '.keep-alive-unmount-children > #container'
const btnSelector = '.keep-alive-unmount-children > #toggleBtn'
await waitForInnerHTML(containerSelector, '0
')
expect(await html(containerSelector)).toBe('0
')
await click(btnSelector)
await waitForInnerHTML(containerSelector, '')
const calls = await page().evaluate(() => {
return (window as any).getCalls('unmount')
})
expect(calls).toStrictEqual(['UnmountBranch'])
const storageInner = await page().evaluate(() => {
const container = (
window as any
).getKeepAliveUnmountStorageContainer?.()
return container ? container.innerHTML : null
})
expect(storageInner).toBe('')
},
E2E_TIMEOUT,
)
// #13153
test(
'move kept-alive node before v-show transition leave finishes',
async () => {
const containerSelector = '.keep-alive-move-before-leave-finishes > div'
const btnToggle = '.keep-alive-move-before-leave-finishes > button'
const changeShowBtn = `${containerSelector} #changeShowBtn`
expect(await html(containerSelector)).toBe(
`
I should show ` +
`This is page1 ` +
`true `,
)
// trigger v-show transition leave
await click(changeShowBtn)
await waitForInnerHTML(
containerSelector,
`
I shouldn't show ` +
`This is page1 ` +
`false `,
)
// switch to page2, before leave finishes
// expect v-show element's display to be none
await click(btnToggle)
await nextTick()
await waitForInnerHTML(
containerSelector,
`
I shouldn't show ` +
`This is page2 `,
)
// switch back to page1
// expect v-show element's display to be none
await click(btnToggle)
await nextTick()
await waitForInnerHTML(
containerSelector,
`
I shouldn't show ` +
`This is page1 ` +
`false `,
)
await waitForInnerHTML(
containerSelector,
`
I shouldn't show ` +
`This is page1 ` +
`false `,
)
},
E2E_TIMEOUT,
)
})
describe.todo('transition with Suspense', () => {})
describe('transition with Teleport', () => {
test(
'apply transition to teleport child',
async () => {
const btnSelector = '.with-teleport > button'
const containerSelector = '.with-teleport > .container'
const targetSelector = `.with-teleport > .target`
await waitForInnerHTML(containerSelector, '')
await waitForInnerHTML(targetSelector, '')
// enter
expect(
(await transitionStart(btnSelector, `${targetSelector} div`))
.classNames,
).toStrictEqual(['test', 'v-enter-from', 'v-enter-active'])
await waitForInnerHTML(
targetSelector,
'vapor compB
',
)
await waitForInnerHTML(
targetSelector,
'vapor compB
',
)
expect(await html(containerSelector)).toBe('')
// leave
expect(
(await transitionStart(btnSelector, `${targetSelector} div`))
.classNames,
).toStrictEqual(['test', 'v-leave-from', 'v-leave-active'])
await waitForInnerHTML(
targetSelector,
'vapor compB
',
)
await waitForInnerHTML(targetSelector, '')
expect(await html(containerSelector)).toBe('')
},
E2E_TIMEOUT,
)
})
describe('transition with AsyncComponent', () => {
test('apply transition to inner component', async () => {
const btnSelector = '.async > button'
const containerSelector = '.async > div'
expect(await html(containerSelector)).toBe('')
// toggle
await click(btnSelector)
await nextTick()
// not yet resolved
expect(await html(containerSelector)).toBe('')
// wait resolving
await timeout(50)
// enter (resolved)
expect(await html(containerSelector)).toBe(
'vapor compA
',
)
await waitForInnerHTML(
containerSelector,
'vapor compA
',
)
await waitForInnerHTML(
containerSelector,
'vapor compA
',
)
// leave
await click(btnSelector)
await nextTick()
expect(await html(containerSelector)).toBe(
'vapor compA
',
)
await waitForInnerHTML(
containerSelector,
'vapor compA
',
)
await waitForInnerHTML(containerSelector, '')
// enter again
await click(btnSelector)
// use the already resolved component
expect(await html(containerSelector)).toBe(
'vapor compA
',
)
await waitForInnerHTML(
containerSelector,
'vapor compA
',
)
await waitForInnerHTML(
containerSelector,
'vapor compA
',
)
})
test('apply transition to pre-resolved async component', async () => {
const btnSelector = '.async-resolved > button'
const containerSelector = '.async-resolved #container'
const hiddenCompSelector = '.async-resolved #hidden-async'
// Wait for the hidden AsyncCompResolved to resolve and render
await waitForInnerHTML(
hiddenCompSelector,
'vapor compA
',
)
expect(await html(containerSelector)).toBe('')
await click(btnSelector)
expect(await html(containerSelector)).toBe(
'vapor compA
',
)
await waitForInnerHTML(
containerSelector,
'vapor compA
',
)
await waitForInnerHTML(
containerSelector,
'vapor compA
',
)
// leave
await click(btnSelector)
await nextTick()
expect(await html(containerSelector)).toBe(
'vapor compA
',
)
await waitForInnerHTML(
containerSelector,
'vapor compA
',
)
await waitForInnerHTML(containerSelector, '')
// enter again
await click(btnSelector)
expect(await html(containerSelector)).toBe(
'vapor compA
',
)
await waitForInnerHTML(
containerSelector,
'vapor compA
',
)
await waitForInnerHTML(
containerSelector,
'vapor compA
',
)
})
})
describe('transition with v-show', () => {
test(
'named transition with v-show',
async () => {
const btnSelector = '.show-named > button'
const containerSelector = '.show-named > div'
const childSelector = `${containerSelector} > div`
expect(await html(containerSelector)).toBe(
'content
',
)
expect(await isVisible(childSelector)).toBe(true)
// leave
expect(
(await transitionStart(btnSelector, childSelector)).classNames,
).toStrictEqual(['test', 'test-leave-from', 'test-leave-active'])
await waitForInnerHTML(
containerSelector,
'content
',
)
await waitForInnerHTML(
containerSelector,
'content
',
)
// enter
expect(
(await transitionStart(btnSelector, childSelector)).classNames,
).toStrictEqual(['test', 'test-enter-from', 'test-enter-active'])
await waitForInnerHTML(
containerSelector,
'content
',
)
await waitForInnerHTML(
containerSelector,
'content
',
)
},
E2E_TIMEOUT,
)
test(
'transition events with v-show',
async () => {
const btnSelector = '.show-events > button'
const containerSelector = '.show-events > div'
const childSelector = `${containerSelector} > div`
expect(await html(containerSelector)).toBe(
'content
',
)
// leave
expect(
(await transitionStart(btnSelector, childSelector)).classNames,
).toStrictEqual(['test', 'test-leave-from', 'test-leave-active'])
await waitForInnerHTML(
containerSelector,
'content
',
)
let calls = await page().evaluate(() => {
return (window as any).getCalls('show')
})
expect(calls).toStrictEqual(['beforeLeave', 'onLeave'])
calls = await page().evaluate(() => {
return (window as any).getCalls('show')
})
expect(calls).not.contain('afterLeave')
await waitForInnerHTML(
containerSelector,
'content
',
)
calls = await page().evaluate(() => {
return (window as any).getCalls('show')
})
expect(calls).toStrictEqual(['beforeLeave', 'onLeave', 'afterLeave'])
// clear calls
await page().evaluate(() => {
;(window as any).resetCalls('show')
})
// enter
expect(
(await transitionStart(btnSelector, childSelector)).classNames,
).toStrictEqual(['test', 'test-enter-from', 'test-enter-active'])
await waitForInnerHTML(
containerSelector,
'content
',
)
calls = await page().evaluate(() => {
return (window as any).getCalls('show')
})
expect(calls).toStrictEqual(['beforeEnter', 'onEnter'])
await waitForInnerHTML(
containerSelector,
'content
',
)
calls = await page().evaluate(() => {
return (window as any).getCalls('show')
})
expect(calls).toStrictEqual(['beforeEnter', 'onEnter', 'afterEnter'])
},
E2E_TIMEOUT,
)
test(
'onLeaveCancelled (v-show only)',
async () => {
const btnSelector = '.show-leave-cancelled > button'
const containerSelector = '.show-leave-cancelled > div'
const childSelector = `${containerSelector} > div`
expect(await html(containerSelector)).toBe(
'content
',
)
// leave
expect(
(await transitionStart(btnSelector, childSelector)).classNames,
).toStrictEqual(['test', 'test-leave-from', 'test-leave-active'])
await waitForInnerHTML(
containerSelector,
'content
',
)
// cancel (enter)
expect(
(await transitionStart(btnSelector, childSelector)).classNames,
).toStrictEqual(['test', 'test-enter-from', 'test-enter-active'])
let calls = await page().evaluate(() => {
return (window as any).getCalls('showLeaveCancel')
})
expect(calls).toStrictEqual(['leaveCancelled'])
await waitForInnerHTML(
containerSelector,
'content
',
)
await waitForInnerHTML(
containerSelector,
'content
',
)
},
E2E_TIMEOUT,
)
test(
'transition on appear with v-show',
async () => {
const btnSelector = '.show-appear > button'
const containerSelector = '.show-appear > div'
const childSelector = `${containerSelector} > div`
let calls = await page().evaluate(() => {
return (window as any).getCalls('showAppear')
})
expect(calls).toStrictEqual(['beforeEnter', 'onEnter'])
// appear
expect(await classList(childSelector)).contains('test-appear-active')
await waitForInnerHTML(
containerSelector,
'content
',
)
calls = await page().evaluate(() => {
return (window as any).getCalls('showAppear')
})
expect(calls).toStrictEqual(['beforeEnter', 'onEnter', 'afterEnter'])
// leave
expect(
(await transitionStart(btnSelector, childSelector)).classNames,
).toStrictEqual(['test', 'test-leave-from', 'test-leave-active'])
await waitForInnerHTML(
containerSelector,
'content
',
)
await waitForInnerHTML(
containerSelector,
'content
',
)
// enter
expect(
(await transitionStart(btnSelector, childSelector)).classNames,
).toStrictEqual(['test', 'test-enter-from', 'test-enter-active'])
await waitForInnerHTML(
containerSelector,
'content
',
)
await waitForInnerHTML(
containerSelector,
'content
',
)
},
E2E_TIMEOUT,
)
test(
'transition events should not call onEnter with v-show false',
async () => {
const btnSelector = '.show-appear-not-enter > button'
const containerSelector = '.show-appear-not-enter > div'
const childSelector = `${containerSelector} > div`
expect(await isVisible(childSelector)).toBe(false)
let calls = await page().evaluate(() => {
return (window as any).getCalls('notEnter')
})
expect(calls).toStrictEqual([])
// enter
expect(
(await transitionStart(btnSelector, childSelector)).classNames,
).toStrictEqual(['test', 'test-enter-from', 'test-enter-active'])
calls = await page().evaluate(() => {
return (window as any).getCalls('notEnter')
})
expect(calls).toStrictEqual(['beforeEnter', 'onEnter'])
await waitForInnerHTML(
containerSelector,
'content
',
)
calls = await page().evaluate(() => {
return (window as any).getCalls('notEnter')
})
expect(calls).not.contain('afterEnter')
await waitForInnerHTML(
containerSelector,
'content
',
)
calls = await page().evaluate(() => {
return (window as any).getCalls('notEnter')
})
expect(calls).toStrictEqual(['beforeEnter', 'onEnter', 'afterEnter'])
},
E2E_TIMEOUT,
)
})
describe('explicit durations', () => {
test(
'single value',
async () => {
const btnSelector = '.duration-single-value > button'
const containerSelector = '.duration-single-value > div'
const childSelector = `${containerSelector} > div`
expect(await html(containerSelector)).toBe(
'content
',
)
// leave
expect(
(await transitionStart(btnSelector, childSelector)).classNames,
).toStrictEqual(['test', 'test-leave-from', 'test-leave-active'])
await waitForInnerHTML(
containerSelector,
'content
',
)
await waitForInnerHTML(containerSelector, '')
// enter
expect(
(await transitionStart(btnSelector, childSelector)).classNames,
).toStrictEqual(['test', 'test-enter-from', 'test-enter-active'])
await waitForInnerHTML(
containerSelector,
'content
',
)
await waitForInnerHTML(
containerSelector,
'content
',
)
},
E2E_TIMEOUT,
)
test(
'enter with explicit durations',
async () => {
const btnSelector = '.duration-enter > button'
const containerSelector = '.duration-enter > div'
const childSelector = `${containerSelector} > div`
expect(await html(containerSelector)).toBe(
'content
',
)
// leave
expect(
(await transitionStart(btnSelector, childSelector)).classNames,
).toStrictEqual(['test', 'test-leave-from', 'test-leave-active'])
await waitForInnerHTML(
containerSelector,
'content
',
)
await waitForInnerHTML(containerSelector, '')
// enter
expect(
(await transitionStart(btnSelector, childSelector)).classNames,
).toStrictEqual(['test', 'test-enter-from', 'test-enter-active'])
await waitForInnerHTML(
containerSelector,
'content
',
)
await waitForInnerHTML(
containerSelector,
'content
',
)
},
E2E_TIMEOUT,
)
test(
'leave with explicit durations',
async () => {
const btnSelector = '.duration-leave > button'
const containerSelector = '.duration-leave > div'
const childSelector = `${containerSelector} > div`
expect(await html(containerSelector)).toBe(
'content
',
)
// leave
expect(
(await transitionStart(btnSelector, childSelector)).classNames,
).toStrictEqual(['test', 'test-leave-from', 'test-leave-active'])
await waitForInnerHTML(
containerSelector,
'content
',
)
await waitForInnerHTML(containerSelector, '')
// enter
expect(
(await transitionStart(btnSelector, childSelector)).classNames,
).toStrictEqual(['test', 'test-enter-from', 'test-enter-active'])
await waitForInnerHTML(
containerSelector,
'content
',
)
await waitForInnerHTML(
containerSelector,
'content
',
)
},
E2E_TIMEOUT,
)
test(
'separate enter and leave',
async () => {
const btnSelector = '.duration-enter-leave > button'
const containerSelector = '.duration-enter-leave > div'
const childSelector = `${containerSelector} > div`
expect(await html(containerSelector)).toBe(
'content
',
)
// leave
expect(
(await transitionStart(btnSelector, childSelector)).classNames,
).toStrictEqual(['test', 'test-leave-from', 'test-leave-active'])
await waitForInnerHTML(
containerSelector,
'content
',
)
await waitForInnerHTML(containerSelector, '')
// enter
expect(
(await transitionStart(btnSelector, childSelector)).classNames,
).toStrictEqual(['test', 'test-enter-from', 'test-enter-active'])
await waitForInnerHTML(
containerSelector,
'content
',
)
await waitForInnerHTML(
containerSelector,
'content
',
)
},
E2E_TIMEOUT,
)
})
test(
'should work with keyed element',
async () => {
const btnSelector = '.keyed > button'
const containerSelector = '.keyed > h1'
await waitForInnerHTML(containerSelector, '0')
// change key
expect(
(await transitionStart(btnSelector, containerSelector)).classNames,
).toStrictEqual(['v-leave-from', 'v-leave-active'])
await nextFrame()
expect(await classList(containerSelector)).toStrictEqual([
'v-leave-active',
'v-leave-to',
])
await waitForInnerHTML(containerSelector, '1')
// change key again
expect(
(await transitionStart(btnSelector, containerSelector)).classNames,
).toStrictEqual(['v-leave-from', 'v-leave-active'])
await nextFrame()
expect(await classList(containerSelector)).toStrictEqual([
'v-leave-active',
'v-leave-to',
])
await waitForInnerHTML(containerSelector, '2')
},
E2E_TIMEOUT,
)
test(
'should work with reusable Transition + keyed element',
async () => {
const btnSelector = '.reusable-keyed > button'
const containerSelector = '.reusable-keyed > h1'
await waitForInnerHTML(containerSelector, '0')
// change key
expect(
(await transitionStart(btnSelector, containerSelector)).classNames,
).toStrictEqual(['test-leave-from', 'test-leave-active'])
await nextFrame()
expect(await classList(containerSelector)).toStrictEqual([
'test-leave-active',
'test-leave-to',
])
await waitForInnerHTML(containerSelector, '1')
// change key again
expect(
(await transitionStart(btnSelector, containerSelector)).classNames,
).toStrictEqual(['test-leave-from', 'test-leave-active'])
await nextFrame()
expect(await classList(containerSelector)).toStrictEqual([
'test-leave-active',
'test-leave-to',
])
await waitForInnerHTML(containerSelector, '2')
},
E2E_TIMEOUT,
)
test(
'should work with out-in mode',
async () => {
const btnSelector = '.out-in > button'
const containerSelector = '.out-in > div'
expect(await html(containerSelector)).toBe(`vapor compB
`)
// compB -> compA
// compB leave
expect(
(await transitionStart(btnSelector, containerSelector)).innerHTML,
).toBe(`vapor compB
`)
await waitForInnerHTML(
containerSelector,
`vapor compB
`,
)
// compA enter
await waitForInnerHTML(
containerSelector,
`vapor compA
`,
)
await waitForInnerHTML(
containerSelector,
`vapor compA
`,
)
await waitForInnerHTML(
containerSelector,
`vapor compA
`,
)
// compA -> compB
// compA leave
expect(
(await transitionStart(btnSelector, containerSelector)).innerHTML,
).toBe(`vapor compA
`)
await waitForInnerHTML(
containerSelector,
`vapor compA
`,
)
// compB enter
await waitForInnerHTML(
containerSelector,
`vapor compB
`,
)
await waitForInnerHTML(
containerSelector,
`vapor compB
`,
)
await waitForInnerHTML(
containerSelector,
`vapor compB
`,
)
},
E2E_TIMEOUT,
)
test(
'should work with in-out mode',
async () => {
const btnSelector = '.in-out > button'
const containerSelector = '.in-out > div'
expect(await html(containerSelector)).toBe(`vapor compB
`)
// compA enter
expect(
(await transitionStart(btnSelector, containerSelector)).innerHTML,
).toBe(
`vapor compB
vapor compA
`,
)
await waitForInnerHTML(
containerSelector,
`vapor compB
vapor compA
`,
)
// compB leave
await waitForInnerHTML(
containerSelector,
`vapor compB
vapor compA
`,
)
await waitForInnerHTML(
containerSelector,
`vapor compB
vapor compA
`,
)
await waitForInnerHTML(
containerSelector,
`vapor compA
`,
)
},
E2E_TIMEOUT,
)
// tests for using vdom component in createVaporApp + vaporInteropPlugin
describe('interop', () => {
test(
'render vdom component',
async () => {
const btnSelector = '.vdom > button'
const containerSelector = '.vdom > div'
expect(await html(containerSelector)).toBe(`vdom comp
`)
// comp leave
expect(
(await transitionStart(btnSelector, containerSelector)).innerHTML,
).toBe(`vdom comp
`)
await waitForInnerHTML(
containerSelector,
`vdom comp
`,
)
await waitForInnerHTML(containerSelector, ``)
// comp enter
expect(
(await transitionStart(btnSelector, containerSelector)).innerHTML,
).toBe(`vdom comp
`)
await waitForInnerHTML(
containerSelector,
`vdom comp
`,
)
await waitForInnerHTML(
containerSelector,
`vdom comp
`,
)
},
E2E_TIMEOUT,
)
test(
'switch between vdom/vapor component (out-in mode)',
async () => {
const btnSelector = '.vdom-vapor-out-in > button'
const containerSelector = '.vdom-vapor-out-in > div'
expect(await html(containerSelector)).toBe(`vdom comp
`)
// switch to vapor comp
// vdom comp leave
expect(
(await transitionStart(btnSelector, containerSelector)).innerHTML,
).toBe(`vdom comp
`)
await waitForInnerHTML(
containerSelector,
`vdom comp
`,
)
// vapor comp enter
await waitForInnerHTML(
containerSelector,
`vapor compA
`,
)
await waitForInnerHTML(
containerSelector,
`vapor compA
`,
)
await waitForInnerHTML(
containerSelector,
`vapor compA
`,
)
// switch to vdom comp
// vapor comp leave
expect(
(await transitionStart(btnSelector, containerSelector)).innerHTML,
).toBe(
`vapor compA
`,
)
await waitForInnerHTML(
containerSelector,
`vapor compA
`,
)
// vdom comp enter
await waitForInnerHTML(
containerSelector,
`vdom comp
`,
)
await waitForInnerHTML(
containerSelector,
`vdom comp
`,
)
await waitForInnerHTML(
containerSelector,
`vdom comp
`,
)
},
E2E_TIMEOUT,
)
test(
'switch between vdom/vapor component (in-out mode)',
async () => {
const btnSelector = '.vdom-vapor-in-out > button'
const containerSelector = '.vdom-vapor-in-out > div'
expect(await html(containerSelector)).toBe(`vapor compA
`)
// switch to vdom comp
// vdom comp enter
expect(
(await transitionStart(btnSelector, containerSelector)).innerHTML,
).toBe(
`vapor compA
vdom comp
`,
)
await waitForInnerHTML(
containerSelector,
`vapor compA
vdom comp
`,
)
// vapor comp leave
await waitForInnerHTML(
containerSelector,
`vapor compA
vdom comp
`,
)
await waitForInnerHTML(
containerSelector,
`vapor compA
vdom comp
`,
)
await waitForInnerHTML(
containerSelector,
`vdom comp
`,
)
// switch to vapor comp
// vapor comp enter
expect(
(await transitionStart(btnSelector, containerSelector)).innerHTML,
).toBe(
`vdom comp
vapor compA
`,
)
await waitForInnerHTML(
containerSelector,
`vdom comp
vapor compA
`,
)
// vdom comp leave
await waitForInnerHTML(
containerSelector,
`vdom comp
vapor compA
`,
)
await waitForInnerHTML(
containerSelector,
`vdom comp
vapor compA
`,
)
await waitForInnerHTML(
containerSelector,
`vapor compA
`,
)
},
E2E_TIMEOUT,
)
})
})