diff --git a/packages-private/vapor-e2e-test/__tests__/transition-group.spec.ts b/packages-private/vapor-e2e-test/__tests__/transition-group.spec.ts new file mode 100644 index 00000000000..ba050f0f263 --- /dev/null +++ b/packages-private/vapor-e2e-test/__tests__/transition-group.spec.ts @@ -0,0 +1,406 @@ +import path from 'node:path' +import { + E2E_TIMEOUT, + setupPuppeteer, +} from '../../../packages/vue/__tests__/e2e/e2eUtils' +import connect from 'connect' +import sirv from 'sirv' +import { expect } from 'vitest' +const { page, nextFrame, timeout, html, transitionStart } = setupPuppeteer() + +const duration = process.env.CI ? 200 : 50 +const buffer = process.env.CI ? 50 : 20 +const transitionFinish = (time = duration) => timeout(time + buffer) + +describe('vapor transition-group', () => { + let server: any + const port = '8196' + 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-group/` + await page().goto(baseUrl) + await page().waitForSelector('#app') + }) + + test( + 'enter', + async () => { + const btnSelector = '.enter > button' + const containerSelector = '.enter > div' + + expect(await html(containerSelector)).toBe( + `
a
` + + `
b
` + + `
c
`, + ) + + expect( + (await transitionStart(btnSelector, containerSelector)).innerHTML, + ).toBe( + `
a
` + + `
b
` + + `
c
` + + `
d
` + + `
e
`, + ) + + await nextFrame() + expect(await html(containerSelector)).toBe( + `
a
` + + `
b
` + + `
c
` + + `
d
` + + `
e
`, + ) + + await transitionFinish() + expect(await html(containerSelector)).toBe( + `
a
` + + `
b
` + + `
c
` + + `
d
` + + `
e
`, + ) + }, + E2E_TIMEOUT, + ) + + test( + 'leave', + async () => { + const btnSelector = '.leave > button' + const containerSelector = '.leave > div' + + expect(await html(containerSelector)).toBe( + `
a
` + + `
b
` + + `
c
`, + ) + + expect( + (await transitionStart(btnSelector, containerSelector)).innerHTML, + ).toBe( + `
a
` + + `
b
` + + `
c
`, + ) + + await nextFrame() + expect(await html(containerSelector)).toBe( + `
a
` + + `
b
` + + `
c
`, + ) + await transitionFinish() + expect(await html(containerSelector)).toBe(`
b
`) + }, + E2E_TIMEOUT, + ) + + test( + 'enter + leave', + async () => { + const btnSelector = '.enter-leave > button' + const containerSelector = '.enter-leave > div' + + expect(await html(containerSelector)).toBe( + `
a
` + + `
b
` + + `
c
`, + ) + + expect( + (await transitionStart(btnSelector, containerSelector)).innerHTML, + ).toBe( + `
a
` + + `
b
` + + `
c
` + + `
d
`, + ) + + await nextFrame() + expect(await html(containerSelector)).toBe( + `
a
` + + `
b
` + + `
c
` + + `
d
`, + ) + await transitionFinish() + expect(await html(containerSelector)).toBe( + `
b
` + + `
c
` + + `
d
`, + ) + }, + E2E_TIMEOUT, + ) + + test( + 'appear', + async () => { + const btnSelector = '.appear > button' + const containerSelector = '.appear > div' + + expect(await html('.appear')).toBe(``) + + await page().evaluate(() => { + return (window as any).setAppear() + }) + + // appear + expect(await html(containerSelector)).toBe( + `
a
` + + `
b
` + + `
c
`, + ) + + await nextFrame() + expect(await html(containerSelector)).toBe( + `
a
` + + `
b
` + + `
c
`, + ) + + await transitionFinish() + expect(await html(containerSelector)).toBe( + `
a
` + + `
b
` + + `
c
`, + ) + + // enter + expect( + (await transitionStart(btnSelector, containerSelector)).innerHTML, + ).toBe( + `
a
` + + `
b
` + + `
c
` + + `
d
` + + `
e
`, + ) + + await nextFrame() + expect(await html(containerSelector)).toBe( + `
a
` + + `
b
` + + `
c
` + + `
d
` + + `
e
`, + ) + + await transitionFinish() + expect(await html(containerSelector)).toBe( + `
a
` + + `
b
` + + `
c
` + + `
d
` + + `
e
`, + ) + }, + E2E_TIMEOUT, + ) + + test( + 'move', + async () => { + const btnSelector = '.move > button' + const containerSelector = '.move > div' + + expect(await html(containerSelector)).toBe( + `
a
` + + `
b
` + + `
c
`, + ) + + expect( + (await transitionStart(btnSelector, containerSelector)).innerHTML, + ).toBe( + `
d
` + + `
b
` + + `
a
` + + `
c
`, + ) + + await nextFrame() + expect(await html(containerSelector)).toBe( + `
d
` + + `
b
` + + `
a
` + + `
c
`, + ) + await transitionFinish(duration * 2) + expect(await html(containerSelector)).toBe( + `
d
` + + `
b
` + + `
a
`, + ) + }, + E2E_TIMEOUT, + ) + + test('dynamic name', async () => { + const btnSelector = '.dynamic-name button.toggleBtn' + const btnChangeName = '.dynamic-name button.changeNameBtn' + const containerSelector = '.dynamic-name > div' + + expect(await html(containerSelector)).toBe( + `
a
` + `
b
` + `
c
`, + ) + + // invalid name + expect( + (await transitionStart(btnSelector, containerSelector)).innerHTML, + ).toBe(`
b
` + `
c
` + `
a
`) + + // change name + expect( + (await transitionStart(btnChangeName, containerSelector)).innerHTML, + ).toBe( + `
a
` + + `
b
` + + `
c
`, + ) + + await transitionFinish() + expect(await html(containerSelector)).toBe( + `
a
` + + `
b
` + + `
c
`, + ) + }) + + test('events', async () => { + const btnSelector = '.events > button' + const containerSelector = '.events > div' + + expect(await html('.events')).toBe(``) + + await page().evaluate(() => { + return (window as any).setAppear() + }) + + // appear + expect(await html(containerSelector)).toBe( + `
a
` + + `
b
` + + `
c
`, + ) + await nextFrame() + expect(await html(containerSelector)).toBe( + `
a
` + + `
b
` + + `
c
`, + ) + + let calls = await page().evaluate(() => { + return (window as any).getCalls() + }) + expect(calls).toContain('beforeAppear') + expect(calls).toContain('onAppear') + expect(calls).not.toContain('afterAppear') + + await transitionFinish() + expect(await html(containerSelector)).toBe( + `
a
` + + `
b
` + + `
c
`, + ) + + expect( + await page().evaluate(() => { + return (window as any).getCalls() + }), + ).toContain('afterAppear') + + // enter + leave + expect( + (await transitionStart(btnSelector, containerSelector)).innerHTML, + ).toBe( + `
a
` + + `
b
` + + `
c
` + + `
d
`, + ) + + calls = await page().evaluate(() => { + return (window as any).getCalls() + }) + expect(calls).toContain('beforeLeave') + expect(calls).toContain('onLeave') + expect(calls).not.toContain('afterLeave') + expect(calls).toContain('beforeEnter') + expect(calls).toContain('onEnter') + expect(calls).not.toContain('afterEnter') + + await nextFrame() + expect(await html(containerSelector)).toBe( + `
a
` + + `
b
` + + `
c
` + + `
d
`, + ) + calls = await page().evaluate(() => { + return (window as any).getCalls() + }) + expect(calls).not.toContain('afterLeave') + expect(calls).not.toContain('afterEnter') + + await transitionFinish() + expect(await html(containerSelector)).toBe( + `
b
` + + `
c
` + + `
d
`, + ) + + calls = await page().evaluate(() => { + return (window as any).getCalls() + }) + expect(calls).toContain('afterLeave') + expect(calls).toContain('afterEnter') + }) + + test('interop: render vdom component', async () => { + const btnSelector = '.interop > button' + const containerSelector = '.interop > div' + + expect(await html(containerSelector)).toBe( + `
a
` + + `
b
` + + `
c
`, + ) + + expect( + (await transitionStart(btnSelector, containerSelector)).innerHTML, + ).toBe( + `
a
` + + `
b
` + + `
c
` + + `
d
`, + ) + + await nextFrame() + expect(await html(containerSelector)).toBe( + `
a
` + + `
b
` + + `
c
` + + `
d
`, + ) + + await transitionFinish() + expect(await html(containerSelector)).toBe( + `
b
` + + `
c
` + + `
d
`, + ) + }) +}) diff --git a/packages-private/vapor-e2e-test/__tests__/transition.spec.ts b/packages-private/vapor-e2e-test/__tests__/transition.spec.ts new file mode 100644 index 00000000000..0bfc30598cc --- /dev/null +++ b/packages-private/vapor-e2e-test/__tests__/transition.spec.ts @@ -0,0 +1,1660 @@ +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, + text, + nextFrame, + timeout, + isVisible, + count, + html, + transitionStart, + waitForElement, + click, +} = setupPuppeteer() + +const duration = process.env.CI ? 200 : 50 +const buffer = process.env.CI ? 50 : 20 +const transitionFinish = (time = duration) => timeout(time + buffer) + +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().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)).classNames, + ).toStrictEqual(['test', 'v-leave-from', 'v-leave-active']) + + await nextFrame() + expect(await classList(childSelector)).toStrictEqual([ + 'test', + 'v-leave-active', + 'v-leave-to', + ]) + await transitionFinish() + expect(await html(containerSelector)).toBe('') + + // enter + expect( + (await transitionStart(btnSelector, childSelector)).classNames, + ).toStrictEqual(['test', 'v-enter-from', 'v-enter-active']) + await nextFrame() + expect(await classList(childSelector)).toStrictEqual([ + 'test', + 'v-enter-active', + 'v-enter-to', + ]) + await transitionFinish() + expect(await html(containerSelector)).toBe( + '
content
', + ) + }, + 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)).classNames, + ).toStrictEqual(['test', 'test-leave-from', 'test-leave-active']) + await nextFrame() + expect(await classList(childSelector)).toStrictEqual([ + 'test', + 'test-leave-active', + 'test-leave-to', + ]) + + await transitionFinish() + expect(await html(containerSelector)).toBe('') + + // enter + expect( + (await transitionStart(btnSelector, childSelector)).classNames, + ).toStrictEqual(['test', 'test-enter-from', 'test-enter-active']) + await nextFrame() + expect(await classList(childSelector)).toStrictEqual([ + 'test', + 'test-enter-active', + 'test-enter-to', + ]) + await transitionFinish() + expect(await html(containerSelector)).toBe( + '
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)).classNames, + ).toStrictEqual(['test', 'bye-from', 'bye-active']) + await nextFrame() + expect(await classList(childSelector)).toStrictEqual([ + 'test', + 'bye-active', + 'bye-to', + ]) + await transitionFinish() + expect(await html(containerSelector)).toBe('') + + // enter + expect( + (await transitionStart(btnSelector, childSelector)).classNames, + ).toStrictEqual(['test', 'hello-from', 'hello-active']) + await nextFrame() + expect(await classList(childSelector)).toStrictEqual([ + 'test', + 'hello-active', + 'hello-to', + ]) + await transitionFinish() + expect(await html(containerSelector)).toBe( + '
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)).classNames, + ).toStrictEqual(['test', 'test-leave-from', 'test-leave-active']) + await nextFrame() + expect(await classList(childSelector)).toStrictEqual([ + 'test', + 'test-leave-active', + 'test-leave-to', + ]) + await transitionFinish() + expect(await html(containerSelector)).toBe('') + + // enter + await click(btnChangeNameSelector) + expect( + (await transitionStart(btnSelector, childSelector)).classNames, + ).toStrictEqual(['test', 'changed-enter-from', 'changed-enter-active']) + await nextFrame() + expect(await classList(childSelector)).toStrictEqual([ + 'test', + 'changed-enter-active', + 'changed-enter-to', + ]) + await transitionFinish() + expect(await html(containerSelector)).toBe( + '
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 nextFrame() + expect(await classList(childSelector)).toStrictEqual([ + 'test', + 'test-leave-active', + 'test-leave-to', + ]) + + expect( + await page().evaluate(() => { + return (window as any).getCalls('withoutAppear') + }), + ).not.contain('afterLeave') + await transitionFinish() + expect(await html(containerSelector)).toBe('') + 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 nextFrame() + expect(await classList(childSelector)).toStrictEqual([ + 'test', + 'test-enter-active', + 'test-enter-to', + ]) + expect( + await page().evaluate(() => { + return (window as any).getCalls('withoutAppear') + }), + ).not.contain('afterEnter') + await transitionFinish() + expect(await html(containerSelector)).toBe( + '
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 nextFrame() + expect(await classList(childSelector)).toStrictEqual([ + 'test', + 'test-enter-active', + 'test-enter-to', + ]) + + // 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 nextFrame() + expect(await classList(childSelector)).toStrictEqual([ + 'test', + 'test-leave-active', + 'test-leave-to', + ]) + await transitionFinish() + expect(await html(containerSelector)).toBe('') + }, + 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 transitionFinish() + expect(await html(containerSelector)).toBe( + '
content
', + ) + + // leave + expect( + (await transitionStart(btnSelector, childSelector)).classNames, + ).toStrictEqual(['test', 'test-leave-from', 'test-leave-active']) + await nextFrame() + expect(await classList(childSelector)).toStrictEqual([ + 'test', + 'test-leave-active', + 'test-leave-to', + ]) + await transitionFinish() + expect(await html(containerSelector)).toBe('') + + // enter + expect( + (await transitionStart(btnSelector, childSelector)).classNames, + ).toStrictEqual(['test', 'test-enter-from', 'test-enter-active']) + await nextFrame() + expect(await classList(childSelector)).toStrictEqual([ + 'test', + 'test-enter-active', + 'test-enter-to', + ]) + await transitionFinish() + expect(await html(containerSelector)).toBe( + '
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 transitionFinish() + expect(await html(containerSelector)).toBe( + '
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 nextFrame() + expect(await classList(childSelector)).toStrictEqual([ + 'test', + 'test-leave-active', + 'test-leave-to', + ]) + calls = await page().evaluate(() => { + return (window as any).getCalls('withAppear') + }) + expect(calls).not.contain('afterLeave') + + await transitionFinish() + expect(await html(containerSelector)).toBe('') + 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 nextFrame() + expect(await classList(childSelector)).toStrictEqual([ + 'test', + 'test-enter-active', + 'test-enter-to', + ]) + calls = await page().evaluate(() => { + return (window as any).getCalls('withAppear') + }) + expect(calls).not.contain('afterEnter') + await transitionFinish() + expect(await html(containerSelector)).toBe( + '
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 nextFrame() + expect(await html(containerSelector)).toBe('') + + // enter + expect( + (await transitionStart(btnSelector, childSelector)).classNames, + ).toStrictEqual(['noop-enter-from', 'noop-enter-active']) + await nextFrame() + expect(await html(containerSelector)).toBe( + '
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 nextFrame() + expect(await classList(childSelector)).toStrictEqual([ + 'test-anim-leave-active', + 'test-anim-leave-to', + ]) + await transitionFinish(duration * 2) + expect(await html(containerSelector)).toBe('') + + // enter + expect( + (await transitionStart(btnSelector, childSelector)).classNames, + ).toStrictEqual(['test-anim-enter-from', 'test-anim-enter-active']) + await nextFrame() + expect(await classList(childSelector)).toStrictEqual([ + 'test-anim-enter-active', + 'test-anim-enter-to', + ]) + await transitionFinish() + expect(await html(containerSelector)).toBe( + '
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 nextFrame() + expect(await classList(childSelector)).toStrictEqual([ + 'test-anim-long-leave-active', + 'test-anim-long-leave-to', + ]) + + if (!process.env.CI) { + await new Promise(r => { + setTimeout(r, duration - buffer) + }) + expect(await classList(childSelector)).toStrictEqual([ + 'test-anim-long-leave-active', + 'test-anim-long-leave-to', + ]) + } + + await transitionFinish(duration * 2) + expect(await html(containerSelector)).toBe('') + + // enter + expect( + (await transitionStart(btnSelector, childSelector)).classNames, + ).toStrictEqual([ + 'test-anim-long-enter-from', + 'test-anim-long-enter-active', + ]) + await nextFrame() + expect(await classList(childSelector)).toStrictEqual([ + 'test-anim-long-enter-active', + 'test-anim-long-enter-to', + ]) + + if (!process.env.CI) { + await new Promise(r => { + setTimeout(r, duration - buffer) + }) + expect(await classList(childSelector)).toStrictEqual([ + 'test-anim-long-enter-active', + 'test-anim-long-enter-to', + ]) + } + + await transitionFinish(duration * 2) + expect(await html(containerSelector)).toBe( + '
content
', + ) + }, + E2E_TIMEOUT, + ) + + test.todo('transition on SVG elements', async () => {}, 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 nextFrame() + expect(await classList(childSelector)).toStrictEqual([ + 'test', + 'test-leave-active', + 'test-leave-to', + ]) + await transitionFinish() + expect(await html(containerSelector)).toBe('') + + // enter + expect( + (await transitionStart(btnSelector, childSelector)).classNames, + ).toStrictEqual(['test', 'test-enter-from', 'test-enter-active']) + await nextFrame() + expect(await classList(childSelector)).toStrictEqual([ + 'test', + 'test-enter-active', + 'test-enter-to', + ]) + await transitionFinish() + expect(await html(containerSelector)).toBe( + '
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 nextFrame() + expect(await classList(childSelector)).toStrictEqual([ + 'test', + 'test-enter-active', + 'test-enter-to', + ]) + await transitionFinish() + expect(await html(containerSelector)).toBe( + '
two
', + ) + + // change view -> 'one' + await click(btnChangeSelector) + + // leave + expect( + (await transitionStart(btnSelector, childSelector)).classNames, + ).toStrictEqual(['test', 'test-leave-from', 'test-leave-active']) + await nextFrame() + expect(await classList(childSelector)).toStrictEqual([ + 'test', + 'test-leave-active', + 'test-leave-to', + ]) + await transitionFinish() + expect(await html(containerSelector)).toBe('') + }, + 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 nextFrame() + expect(await classList(childSelector)).toStrictEqual([ + 'test', + 'test-enter-active', + 'test-enter-to', + ]) + await transitionFinish() + expect(await html(containerSelector)).toBe( + '
two
', + ) + + // change view -> 'one' + await click(btnChangeSelector) + // leave + expect( + (await transitionStart(btnSelector, childSelector)).classNames, + ).toStrictEqual(['test', 'test-leave-from', 'test-leave-active']) + await nextFrame() + expect(await classList(childSelector)).toStrictEqual([ + 'test', + 'test-leave-active', + 'test-leave-to', + ]) + await transitionFinish() + expect(await html(containerSelector)).toBe('') + }, + 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 transitionFinish(duration * 2) + expect(await html(containerSelector)).toBe( + '
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 transitionFinish(duration * 3) + 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 transitionFinish(duration * 3) + + 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.todo('transition with KeepAlive', () => {}) + describe.todo('transition with Suspense', () => {}) + describe.todo('transition with Teleport', () => {}) + + 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 nextFrame() + expect(await classList(childSelector)).toStrictEqual([ + 'test', + 'test-leave-active', + 'test-leave-to', + ]) + await transitionFinish() + expect(await isVisible(childSelector)).toBe(false) + + // enter + expect( + (await transitionStart(btnSelector, childSelector)).classNames, + ).toStrictEqual(['test', 'test-enter-from', 'test-enter-active']) + await nextFrame() + expect(await classList(childSelector)).toStrictEqual([ + 'test', + 'test-enter-active', + 'test-enter-to', + ]) + await transitionFinish() + expect(await html(containerSelector)).toBe( + '
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']) + + let calls = await page().evaluate(() => { + return (window as any).getCalls('show') + }) + expect(calls).toStrictEqual(['beforeLeave', 'onLeave']) + await nextFrame() + expect(await classList(childSelector)).toStrictEqual([ + 'test', + 'test-leave-active', + 'test-leave-to', + ]) + calls = await page().evaluate(() => { + return (window as any).getCalls('show') + }) + expect(calls).not.contain('afterLeave') + await transitionFinish() + expect(await isVisible(childSelector)).toBe(false) + 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 nextFrame() + expect(await classList(childSelector)).toStrictEqual([ + 'test', + 'test-enter-active', + 'test-enter-to', + ]) + calls = await page().evaluate(() => { + return (window as any).getCalls('show') + }) + expect(calls).toStrictEqual(['beforeEnter', 'onEnter']) + await transitionFinish() + expect(await html(containerSelector)).toBe( + '
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 nextFrame() + expect(await classList(childSelector)).toStrictEqual([ + 'test', + 'test-leave-active', + 'test-leave-to', + ]) + + // 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 nextFrame() + expect(await classList(childSelector)).toStrictEqual([ + 'test', + 'test-enter-active', + 'test-enter-to', + ]) + await transitionFinish() + expect(await isVisible(childSelector)).toBe(true) + }, + 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 transitionFinish() + expect(await html(containerSelector)).toBe( + '
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 nextFrame() + expect(await classList(childSelector)).toStrictEqual([ + 'test', + 'test-leave-active', + 'test-leave-to', + ]) + await transitionFinish() + expect(await isVisible(childSelector)).toBe(false) + + // enter + expect( + (await transitionStart(btnSelector, childSelector)).classNames, + ).toStrictEqual(['test', 'test-enter-from', 'test-enter-active']) + await nextFrame() + expect(await classList(childSelector)).toStrictEqual([ + 'test', + 'test-enter-active', + 'test-enter-to', + ]) + await transitionFinish() + expect(await html(containerSelector)).toBe( + '
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 nextFrame() + expect(await classList(childSelector)).toStrictEqual([ + 'test', + 'test-enter-active', + 'test-enter-to', + ]) + calls = await page().evaluate(() => { + return (window as any).getCalls('notEnter') + }) + expect(calls).not.contain('afterEnter') + await transitionFinish() + expect(await html(containerSelector)).toBe( + '
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 nextFrame() + expect(await classList(childSelector)).toStrictEqual([ + 'test', + 'test-leave-active', + 'test-leave-to', + ]) + await transitionFinish(duration * 2) + expect(await html(containerSelector)).toBe('') + + // enter + expect( + (await transitionStart(btnSelector, childSelector)).classNames, + ).toStrictEqual(['test', 'test-enter-from', 'test-enter-active']) + await nextFrame() + expect(await classList(childSelector)).toStrictEqual([ + 'test', + 'test-enter-active', + 'test-enter-to', + ]) + await transitionFinish(duration * 2) + expect(await html(containerSelector)).toBe( + '
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 nextFrame() + expect(await classList(childSelector)).toStrictEqual([ + 'test', + 'test-leave-active', + 'test-leave-to', + ]) + await transitionFinish() + expect(await html(containerSelector)).toBe('') + + // enter + expect( + (await transitionStart(btnSelector, childSelector)).classNames, + ).toStrictEqual(['test', 'test-enter-from', 'test-enter-active']) + await nextFrame() + expect(await classList(childSelector)).toStrictEqual([ + 'test', + 'test-enter-active', + 'test-enter-to', + ]) + await transitionFinish(duration * 2) + expect(await html(containerSelector)).toBe( + '
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 nextFrame() + expect(await classList(childSelector)).toStrictEqual([ + 'test', + 'test-leave-active', + 'test-leave-to', + ]) + await transitionFinish(duration * 2) + expect(await html(containerSelector)).toBe('') + + // enter + expect( + (await transitionStart(btnSelector, childSelector)).classNames, + ).toStrictEqual(['test', 'test-enter-from', 'test-enter-active']) + await nextFrame() + expect(await classList(childSelector)).toStrictEqual([ + 'test', + 'test-enter-active', + 'test-enter-to', + ]) + await transitionFinish() + expect(await html(containerSelector)).toBe( + '
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 nextFrame() + expect(await classList(childSelector)).toStrictEqual([ + 'test', + 'test-leave-active', + 'test-leave-to', + ]) + await transitionFinish(duration * 2) + expect(await html(containerSelector)).toBe('') + + // enter + expect( + (await transitionStart(btnSelector, childSelector)).classNames, + ).toStrictEqual(['test', 'test-enter-from', 'test-enter-active']) + await nextFrame() + expect(await classList(childSelector)).toStrictEqual([ + 'test', + 'test-enter-active', + 'test-enter-to', + ]) + await transitionFinish(duration * 4) + expect(await html(containerSelector)).toBe( + '
content
', + ) + }, + E2E_TIMEOUT, + ) + }) + + test( + 'should work with keyed element', + async () => { + const btnSelector = '.keyed > button' + const containerSelector = '.keyed > h1' + + expect(await text(containerSelector)).toContain('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 transitionFinish() + await nextFrame() + expect(await text(containerSelector)).toContain('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 transitionFinish() + await nextFrame() + expect(await text(containerSelector)).toContain('2') + }, + E2E_TIMEOUT, + ) + + test( + 'should work with out-in mode', + async () => { + const btnSelector = '.out-in > button' + const containerSelector = '.out-in > div' + const childSelector = `${containerSelector} > div` + + expect(await html(containerSelector)).toBe(`
vapor compB
`) + + // compB -> compA + // compB leave + expect( + (await transitionStart(btnSelector, containerSelector)).innerHTML, + ).toBe(`
vapor compB
`) + + await nextFrame() + expect(await html(containerSelector)).toBe( + `
vapor compB
`, + ) + + // compA enter + await waitForElement(childSelector, 'vapor compA', [ + 'fade-enter-from', + 'fade-enter-active', + ]) + + await nextFrame() + expect(await html(containerSelector)).toBe( + `
vapor compA
`, + ) + + await transitionFinish() + await nextFrame() + expect(await html(containerSelector)).toBe( + `
vapor compA
`, + ) + + // compA -> compB + // compA leave + expect( + (await transitionStart(btnSelector, containerSelector)).innerHTML, + ).toBe(`
vapor compA
`) + + await nextFrame() + expect(await html(containerSelector)).toBe( + `
vapor compA
`, + ) + + // compB enter + await waitForElement(childSelector, 'vapor compB', [ + 'fade-enter-from', + 'fade-enter-active', + ]) + + await nextFrame() + expect(await html(containerSelector)).toBe( + `
vapor compB
`, + ) + + await transitionFinish() + expect(await html(containerSelector)).toBe( + `
vapor compB
`, + ) + }, + E2E_TIMEOUT, + ) + + test( + 'should work with in-out mode', + async () => { + const btnSelector = '.in-out > button' + const containerSelector = '.in-out > div' + const childSelector = `${containerSelector} > div` + + expect(await html(containerSelector)).toBe(`
vapor compB
`) + + // compA enter + expect( + (await transitionStart(btnSelector, containerSelector)).innerHTML, + ).toBe( + `
vapor compB
vapor compA
`, + ) + + await nextFrame() + expect(await html(containerSelector)).toBe( + `
vapor compB
vapor compA
`, + ) + + // compB leave + await waitForElement(childSelector, 'vapor compB', [ + 'fade-leave-from', + 'fade-leave-active', + ]) + + await nextFrame() + expect(await html(containerSelector)).toBe( + `
vapor compB
vapor compA
`, + ) + + await transitionFinish() + expect(await html(containerSelector)).toBe( + `
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 nextFrame() + expect(await html(containerSelector)).toBe( + `
vdom comp
`, + ) + + await transitionFinish() + expect(await html(containerSelector)).toBe(``) + + // comp enter + expect( + (await transitionStart(btnSelector, containerSelector)).innerHTML, + ).toBe(`
vdom comp
`) + + await nextFrame() + expect(await html(containerSelector)).toBe( + `
vdom comp
`, + ) + + await transitionFinish() + expect(await html(containerSelector)).toBe( + `
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' + const childSelector = `${containerSelector} > 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 nextFrame() + expect(await html(containerSelector)).toBe( + `
vdom comp
`, + ) + + // vapor comp enter + await waitForElement(childSelector, 'vapor compA', [ + 'fade-enter-from', + 'fade-enter-active', + ]) + + await nextFrame() + expect(await html(containerSelector)).toBe( + `
vapor compA
`, + ) + + await transitionFinish() + expect(await html(containerSelector)).toBe( + `
vapor compA
`, + ) + + // switch to vdom comp + // vapor comp leave + expect( + (await transitionStart(btnSelector, containerSelector)).innerHTML, + ).toBe( + `
vapor compA
`, + ) + + await nextFrame() + expect(await html(containerSelector)).toBe( + `
vapor compA
`, + ) + + // vdom comp enter + await waitForElement(childSelector, 'vdom comp', [ + 'fade-enter-from', + 'fade-enter-active', + ]) + + await nextFrame() + expect(await html(containerSelector)).toBe( + `
vdom comp
`, + ) + + await transitionFinish() + expect(await html(containerSelector)).toBe( + `
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' + const childSelector = `${containerSelector} > 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 nextFrame() + expect(await html(containerSelector)).toBe( + `
vapor compA
vdom comp
`, + ) + + // vapor comp leave + await waitForElement(childSelector, 'vapor compA', [ + 'fade-leave-from', + 'fade-leave-active', + ]) + + await nextFrame() + expect(await html(containerSelector)).toBe( + `
vapor compA
vdom comp
`, + ) + + await transitionFinish() + expect(await html(containerSelector)).toBe( + `
vdom comp
`, + ) + + // switch to vapor comp + // vapor comp enter + expect( + (await transitionStart(btnSelector, containerSelector)).innerHTML, + ).toBe( + `
vdom comp
vapor compA
`, + ) + + await nextFrame() + expect(await html(containerSelector)).toBe( + `
vdom comp
vapor compA
`, + ) + + // vdom comp leave + await waitForElement(childSelector, 'vdom comp', [ + 'fade-leave-from', + 'fade-leave-active', + ]) + + await nextFrame() + expect(await html(containerSelector)).toBe( + `
vdom comp
vapor compA
`, + ) + + await transitionFinish() + expect(await html(containerSelector)).toBe( + `
vapor compA
`, + ) + }, + E2E_TIMEOUT, + ) + }) +}) diff --git a/packages-private/vapor-e2e-test/__tests__/vdomInterop.spec.ts b/packages-private/vapor-e2e-test/__tests__/vdomInterop.spec.ts index 360f48085a1..e05f06e1abd 100644 --- a/packages-private/vapor-e2e-test/__tests__/vdomInterop.spec.ts +++ b/packages-private/vapor-e2e-test/__tests__/vdomInterop.spec.ts @@ -5,10 +5,23 @@ import { } from '../../../packages/vue/__tests__/e2e/e2eUtils' import connect from 'connect' import sirv from 'sirv' +const { + page, + click, + text, + enterValue, + html, + transitionStart, + waitForElement, + nextFrame, + timeout, +} = setupPuppeteer() -describe('vdom / vapor interop', () => { - const { page, click, text, enterValue } = setupPuppeteer() +const duration = process.env.CI ? 200 : 50 +const buffer = process.env.CI ? 50 : 20 +const transitionFinish = (time = duration) => timeout(time + buffer) +describe('vdom / vapor interop', () => { let server: any const port = '8193' beforeAll(() => { @@ -22,12 +35,15 @@ describe('vdom / vapor interop', () => { server.close() }) + beforeEach(async () => { + const baseUrl = `http://localhost:${port}/interop/` + await page().goto(baseUrl) + await page().waitForSelector('#app') + }) + test( 'should work', async () => { - const baseUrl = `http://localhost:${port}/interop/` - await page().goto(baseUrl) - expect(await text('.vapor > h2')).toContain('Vapor component in VDOM') expect(await text('.vapor-prop')).toContain('hello') @@ -81,4 +97,163 @@ describe('vdom / vapor interop', () => { }, E2E_TIMEOUT, ) + + describe('vdom transition', () => { + test( + 'render vapor component', + async () => { + const btnSelector = '.trans-vapor > button' + const containerSelector = '.trans-vapor > div' + + expect(await html(containerSelector)).toBe(`
vapor compA
`) + + // comp leave + expect( + (await transitionStart(btnSelector, containerSelector)).innerHTML, + ).toBe( + `
vapor compA
`, + ) + + await nextFrame() + expect(await html(containerSelector)).toBe( + `
vapor compA
`, + ) + + await transitionFinish() + expect(await html(containerSelector)).toBe(``) + + // comp enter + expect( + (await transitionStart(btnSelector, containerSelector)).innerHTML, + ).toBe(`
vapor compA
`) + + await nextFrame() + expect(await html(containerSelector)).toBe( + `
vapor compA
`, + ) + + await transitionFinish() + expect(await html(containerSelector)).toBe( + `
vapor compA
`, + ) + }, + E2E_TIMEOUT, + ) + + test( + 'switch between vdom/vapor component (out-in mode)', + async () => { + const btnSelector = '.trans-vdom-vapor-out-in > button' + const containerSelector = '.trans-vdom-vapor-out-in > div' + const childSelector = `${containerSelector} > 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 nextFrame() + expect(await html(containerSelector)).toBe( + `
vdom comp
`, + ) + + // vapor comp enter + await waitForElement(childSelector, 'vapor compA', [ + 'fade-enter-from', + 'fade-enter-active', + ]) + + await nextFrame() + expect(await html(containerSelector)).toBe( + `
vapor compA
`, + ) + + await transitionFinish() + expect(await html(containerSelector)).toBe( + `
vapor compA
`, + ) + + // switch to vdom comp + // vapor comp leave + expect( + (await transitionStart(btnSelector, containerSelector)).innerHTML, + ).toBe( + `
vapor compA
`, + ) + + await nextFrame() + expect(await html(containerSelector)).toBe( + `
vapor compA
`, + ) + + // vdom comp enter + await waitForElement(childSelector, 'vdom comp', [ + 'fade-enter-from', + 'fade-enter-active', + ]) + + await nextFrame() + expect(await html(containerSelector)).toBe( + `
vdom comp
`, + ) + + await transitionFinish() + expect(await html(containerSelector)).toBe( + `
vdom comp
`, + ) + }, + E2E_TIMEOUT, + ) + }) + + describe('vdom transition-group', () => { + test( + 'render vapor component', + async () => { + const btnSelector = '.trans-group-vapor > button' + const containerSelector = '.trans-group-vapor > div' + + expect(await html(containerSelector)).toBe( + `
a
` + + `
b
` + + `
c
`, + ) + + // insert + expect( + (await transitionStart(btnSelector, containerSelector)).innerHTML, + ).toBe( + `
a
` + + `
b
` + + `
c
` + + `
d
` + + `
e
`, + ) + + await nextFrame() + expect(await html(containerSelector)).toBe( + `
a
` + + `
b
` + + `
c
` + + `
d
` + + `
e
`, + ) + + await transitionFinish() + expect(await html(containerSelector)).toBe( + `
a
` + + `
b
` + + `
c
` + + `
d
` + + `
e
`, + ) + }, + E2E_TIMEOUT, + ) + }) }) diff --git a/packages-private/vapor-e2e-test/index.html b/packages-private/vapor-e2e-test/index.html index 7dc205e5ab0..09ea6aa607a 100644 --- a/packages-private/vapor-e2e-test/index.html +++ b/packages-private/vapor-e2e-test/index.html @@ -1,2 +1,11 @@ VDOM / Vapor interop Vapor TodoMVC +Vapor Transition +Vapor TransitionGroup + + diff --git a/packages-private/vapor-e2e-test/interop/App.vue b/packages-private/vapor-e2e-test/interop/App.vue index 772a6989dd7..8cf42e47549 100644 --- a/packages-private/vapor-e2e-test/interop/App.vue +++ b/packages-private/vapor-e2e-test/interop/App.vue @@ -1,9 +1,22 @@ diff --git a/packages-private/vapor-e2e-test/interop/VaporComp.vue b/packages-private/vapor-e2e-test/interop/VaporComp.vue index 88a60c782c0..f0156544919 100644 --- a/packages-private/vapor-e2e-test/interop/VaporComp.vue +++ b/packages-private/vapor-e2e-test/interop/VaporComp.vue @@ -27,7 +27,8 @@ const slotProp = ref('slot prop') change slot prop
- #default: + #default: +
#test: fallback content @@ -40,7 +41,7 @@ const slotProp = ref('slot prop') > Toggle default slot to vdom - +