mirror of https://github.com/vuejs/core.git
wip: add interop tests
This commit is contained in:
parent
d5d8ada577
commit
d1849288d1
|
@ -5,8 +5,18 @@ import {
|
||||||
} from '../../../packages/vue/__tests__/e2e/e2eUtils'
|
} from '../../../packages/vue/__tests__/e2e/e2eUtils'
|
||||||
import connect from 'connect'
|
import connect from 'connect'
|
||||||
import sirv from 'sirv'
|
import sirv from 'sirv'
|
||||||
const { page, classList, text, nextFrame, timeout, isVisible, count, html } =
|
const {
|
||||||
setupPuppeteer()
|
page,
|
||||||
|
classList,
|
||||||
|
text,
|
||||||
|
nextFrame,
|
||||||
|
timeout,
|
||||||
|
isVisible,
|
||||||
|
count,
|
||||||
|
html,
|
||||||
|
transitionStart,
|
||||||
|
waitForElement,
|
||||||
|
} = setupPuppeteer()
|
||||||
|
|
||||||
const duration = process.env.CI ? 200 : 50
|
const duration = process.env.CI ? 200 : 50
|
||||||
const buffer = process.env.CI ? 50 : 20
|
const buffer = process.env.CI ? 50 : 20
|
||||||
|
@ -32,43 +42,6 @@ describe('vapor transition', () => {
|
||||||
await page().waitForSelector('#app')
|
await page().waitForSelector('#app')
|
||||||
})
|
})
|
||||||
|
|
||||||
const transitionStart = (btnSelector: string, containerSelector: string) =>
|
|
||||||
page().evaluate(
|
|
||||||
([btnSel, containerSel]) => {
|
|
||||||
;(document.querySelector(btnSel) as HTMLElement)!.click()
|
|
||||||
return Promise.resolve().then(() => {
|
|
||||||
const container = document.querySelector(containerSel)!
|
|
||||||
return {
|
|
||||||
classNames: container.className.split(/\s+/g),
|
|
||||||
innerHTML: container.innerHTML,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
[btnSelector, containerSelector],
|
|
||||||
)
|
|
||||||
|
|
||||||
const waitForElement = (
|
|
||||||
selector: string,
|
|
||||||
text: string,
|
|
||||||
classNames: string[], // if empty, check for no classes
|
|
||||||
timeout = 2000,
|
|
||||||
) =>
|
|
||||||
page().waitForFunction(
|
|
||||||
(sel, expectedText, expectedClasses) => {
|
|
||||||
const el = document.querySelector(sel)
|
|
||||||
const hasClasses =
|
|
||||||
expectedClasses.length === 0
|
|
||||||
? el?.classList.length === 0
|
|
||||||
: expectedClasses.every(c => el?.classList.contains(c))
|
|
||||||
const hasText = el?.textContent?.includes(expectedText)
|
|
||||||
return !!el && hasClasses && hasText
|
|
||||||
},
|
|
||||||
{ timeout },
|
|
||||||
selector,
|
|
||||||
text,
|
|
||||||
classNames,
|
|
||||||
)
|
|
||||||
|
|
||||||
test(
|
test(
|
||||||
'should work with v-show',
|
'should work with v-show',
|
||||||
async () => {
|
async () => {
|
||||||
|
|
|
@ -5,10 +5,23 @@ import {
|
||||||
} from '../../../packages/vue/__tests__/e2e/e2eUtils'
|
} from '../../../packages/vue/__tests__/e2e/e2eUtils'
|
||||||
import connect from 'connect'
|
import connect from 'connect'
|
||||||
import sirv from 'sirv'
|
import sirv from 'sirv'
|
||||||
|
const {
|
||||||
|
page,
|
||||||
|
click,
|
||||||
|
text,
|
||||||
|
enterValue,
|
||||||
|
html,
|
||||||
|
transitionStart,
|
||||||
|
waitForElement,
|
||||||
|
nextFrame,
|
||||||
|
timeout,
|
||||||
|
} = 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', () => {
|
describe('vdom / vapor interop', () => {
|
||||||
const { page, click, text, enterValue } = setupPuppeteer()
|
|
||||||
|
|
||||||
let server: any
|
let server: any
|
||||||
const port = '8193'
|
const port = '8193'
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
|
@ -22,12 +35,15 @@ describe('vdom / vapor interop', () => {
|
||||||
server.close()
|
server.close()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
const baseUrl = `http://localhost:${port}/interop/`
|
||||||
|
await page().goto(baseUrl)
|
||||||
|
await page().waitForSelector('#app')
|
||||||
|
})
|
||||||
|
|
||||||
test(
|
test(
|
||||||
'should work',
|
'should work',
|
||||||
async () => {
|
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 > h2')).toContain('Vapor component in VDOM')
|
||||||
|
|
||||||
expect(await text('.vapor-prop')).toContain('hello')
|
expect(await text('.vapor-prop')).toContain('hello')
|
||||||
|
@ -81,4 +97,121 @@ describe('vdom / vapor interop', () => {
|
||||||
},
|
},
|
||||||
E2E_TIMEOUT,
|
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(
|
||||||
|
`<div key="0">vapor compA</div>`,
|
||||||
|
)
|
||||||
|
|
||||||
|
// comp leave
|
||||||
|
expect(
|
||||||
|
(await transitionStart(btnSelector, containerSelector)).innerHTML,
|
||||||
|
).toBe(
|
||||||
|
`<div key="0" class="v-leave-from v-leave-active">vapor compA</div><!---->`,
|
||||||
|
)
|
||||||
|
|
||||||
|
await nextFrame()
|
||||||
|
expect(await html(containerSelector)).toBe(
|
||||||
|
`<div key="0" class="v-leave-active v-leave-to">vapor compA</div><!---->`,
|
||||||
|
)
|
||||||
|
|
||||||
|
await transitionFinish()
|
||||||
|
expect(await html(containerSelector)).toBe(`<!---->`)
|
||||||
|
|
||||||
|
// comp enter
|
||||||
|
expect(
|
||||||
|
(await transitionStart(btnSelector, containerSelector)).innerHTML,
|
||||||
|
).toBe(
|
||||||
|
`<div key="0" class="v-enter-from v-enter-active">vapor compA</div>`,
|
||||||
|
)
|
||||||
|
|
||||||
|
await nextFrame()
|
||||||
|
expect(await html(containerSelector)).toBe(
|
||||||
|
`<div key="0" class="v-enter-active v-enter-to">vapor compA</div>`,
|
||||||
|
)
|
||||||
|
|
||||||
|
await transitionFinish()
|
||||||
|
expect(await html(containerSelector)).toBe(
|
||||||
|
`<div key="0" class="">vapor compA</div>`,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
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(`<div>vdom comp</div>`)
|
||||||
|
|
||||||
|
// switch to vapor comp
|
||||||
|
// vdom comp leave
|
||||||
|
expect(
|
||||||
|
(await transitionStart(btnSelector, containerSelector)).innerHTML,
|
||||||
|
).toBe(
|
||||||
|
`<div class="fade-leave-from fade-leave-active">vdom comp</div><!---->`,
|
||||||
|
)
|
||||||
|
|
||||||
|
await nextFrame()
|
||||||
|
expect(await html(containerSelector)).toBe(
|
||||||
|
`<div class="fade-leave-active fade-leave-to">vdom comp</div><!---->`,
|
||||||
|
)
|
||||||
|
|
||||||
|
// vapor comp enter
|
||||||
|
await waitForElement(childSelector, 'vapor compA', [
|
||||||
|
'fade-enter-from',
|
||||||
|
'fade-enter-active',
|
||||||
|
])
|
||||||
|
|
||||||
|
await nextFrame()
|
||||||
|
expect(await html(containerSelector)).toBe(
|
||||||
|
`<div class="fade-enter-active fade-enter-to">vapor compA</div>`,
|
||||||
|
)
|
||||||
|
|
||||||
|
await transitionFinish()
|
||||||
|
expect(await html(containerSelector)).toBe(
|
||||||
|
`<div class="">vapor compA</div>`,
|
||||||
|
)
|
||||||
|
|
||||||
|
// switch to vdom comp
|
||||||
|
// vapor comp leave
|
||||||
|
expect(
|
||||||
|
(await transitionStart(btnSelector, containerSelector)).innerHTML,
|
||||||
|
).toBe(
|
||||||
|
`<div class="fade-leave-from fade-leave-active">vapor compA</div><!---->`,
|
||||||
|
)
|
||||||
|
|
||||||
|
await nextFrame()
|
||||||
|
expect(await html(containerSelector)).toBe(
|
||||||
|
`<div class="fade-leave-active fade-leave-to">vapor compA</div><!---->`,
|
||||||
|
)
|
||||||
|
|
||||||
|
// vdom comp enter
|
||||||
|
await waitForElement(childSelector, 'vdom comp', [
|
||||||
|
'fade-enter-from',
|
||||||
|
'fade-enter-active',
|
||||||
|
])
|
||||||
|
|
||||||
|
await nextFrame()
|
||||||
|
expect(await html(containerSelector)).toBe(
|
||||||
|
`<div class="fade-enter-active fade-enter-to">vdom comp</div>`,
|
||||||
|
)
|
||||||
|
|
||||||
|
await transitionFinish()
|
||||||
|
expect(await html(containerSelector)).toBe(
|
||||||
|
`<div class="">vdom comp</div>`,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
E2E_TIMEOUT,
|
||||||
|
)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,9 +1,18 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue'
|
import { ref, shallowRef } from 'vue'
|
||||||
import VaporComp from './VaporComp.vue'
|
import VaporComp from './VaporComp.vue'
|
||||||
|
import VaporCompA from '../transition/components/VaporCompA.vue'
|
||||||
|
import VdomComp from '../transition/components/VdomComp.vue'
|
||||||
|
|
||||||
const msg = ref('hello')
|
const msg = ref('hello')
|
||||||
const passSlot = ref(true)
|
const passSlot = ref(true)
|
||||||
|
|
||||||
|
const toggleVapor = ref(true)
|
||||||
|
const interopComponent = shallowRef(VdomComp)
|
||||||
|
function toggleInteropComponent() {
|
||||||
|
interopComponent.value =
|
||||||
|
interopComponent.value === VaporCompA ? VdomComp : VaporCompA
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -19,4 +28,28 @@ const passSlot = ref(true)
|
||||||
|
|
||||||
<template #test v-if="passSlot">A test slot</template>
|
<template #test v-if="passSlot">A test slot</template>
|
||||||
</VaporComp>
|
</VaporComp>
|
||||||
|
|
||||||
|
<!-- transition interop -->
|
||||||
|
<div>
|
||||||
|
<div class="trans-vapor">
|
||||||
|
<button @click="toggleVapor = !toggleVapor">
|
||||||
|
toggle vapor component
|
||||||
|
</button>
|
||||||
|
<div>
|
||||||
|
<Transition>
|
||||||
|
<VaporCompA v-if="toggleVapor" />
|
||||||
|
</Transition>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="trans-vdom-vapor-out-in">
|
||||||
|
<button @click="toggleInteropComponent">
|
||||||
|
switch between vdom/vapor component out-in mode
|
||||||
|
</button>
|
||||||
|
<div>
|
||||||
|
<Transition name="fade" mode="out-in">
|
||||||
|
<component :is="interopComponent"></component>
|
||||||
|
</Transition>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { createApp, vaporInteropPlugin } from 'vue'
|
import { createApp, vaporInteropPlugin } from 'vue'
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
|
import '../transition/style.css'
|
||||||
|
|
||||||
createApp(App).use(vaporInteropPlugin).mount('#app')
|
createApp(App).use(vaporInteropPlugin).mount('#app')
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<script vapor>
|
<script setup vapor lang="ts">
|
||||||
const msg = 'vapor compA'
|
const msg = 'vapor compA'
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<script vapor>
|
<script setup vapor lang="ts">
|
||||||
const msg = 'vapor compB'
|
const msg = 'vapor compB'
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<script setup>
|
<script setup lang="ts">
|
||||||
const msg = 'vdom comp'
|
const msg = 'vdom comp'
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
|
|
|
@ -20,7 +20,7 @@ import {
|
||||||
type VaporTransitionHooks,
|
type VaporTransitionHooks,
|
||||||
isFragment,
|
isFragment,
|
||||||
} from '../block'
|
} from '../block'
|
||||||
import { isVaporComponent } from '../component'
|
import { type VaporComponentInstance, isVaporComponent } from '../component'
|
||||||
|
|
||||||
const decorate = (t: typeof VaporTransition) => {
|
const decorate = (t: typeof VaporTransition) => {
|
||||||
t.displayName = 'VaporTransition'
|
t.displayName = 'VaporTransition'
|
||||||
|
@ -244,3 +244,13 @@ export function findTransitionBlock(block: Block): TransitionBlock | undefined {
|
||||||
|
|
||||||
return child
|
return child
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function setTransitionToInstance(
|
||||||
|
block: VaporComponentInstance,
|
||||||
|
hooks: VaporTransitionHooks,
|
||||||
|
): void {
|
||||||
|
const child = findTransitionBlock(block.block)
|
||||||
|
if (!child) return
|
||||||
|
|
||||||
|
setTransitionHooks(child, hooks)
|
||||||
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ import {
|
||||||
ensureRenderer,
|
ensureRenderer,
|
||||||
onScopeDispose,
|
onScopeDispose,
|
||||||
renderSlot,
|
renderSlot,
|
||||||
setTransitionHooks,
|
setTransitionHooks as setVNodeTransitionHooks,
|
||||||
shallowRef,
|
shallowRef,
|
||||||
simpleSetCurrentInstance,
|
simpleSetCurrentInstance,
|
||||||
} from '@vue/runtime-dom'
|
} from '@vue/runtime-dom'
|
||||||
|
@ -28,13 +28,20 @@ import {
|
||||||
mountComponent,
|
mountComponent,
|
||||||
unmountComponent,
|
unmountComponent,
|
||||||
} from './component'
|
} from './component'
|
||||||
import { type Block, VaporFragment, insert, remove } from './block'
|
import {
|
||||||
|
type Block,
|
||||||
|
VaporFragment,
|
||||||
|
type VaporTransitionHooks,
|
||||||
|
insert,
|
||||||
|
remove,
|
||||||
|
} from './block'
|
||||||
import { EMPTY_OBJ, extend, isFunction } from '@vue/shared'
|
import { EMPTY_OBJ, extend, isFunction } from '@vue/shared'
|
||||||
import { type RawProps, rawPropsProxyHandlers } from './componentProps'
|
import { type RawProps, rawPropsProxyHandlers } from './componentProps'
|
||||||
import type { RawSlots, VaporSlot } from './componentSlots'
|
import type { RawSlots, VaporSlot } from './componentSlots'
|
||||||
import { renderEffect } from './renderEffect'
|
import { renderEffect } from './renderEffect'
|
||||||
import { createTextNode } from './dom/node'
|
import { createTextNode } from './dom/node'
|
||||||
import { optimizePropertyLookup } from './dom/prop'
|
import { optimizePropertyLookup } from './dom/prop'
|
||||||
|
import { setTransitionToInstance } from './components/Transition'
|
||||||
|
|
||||||
// mounting vapor components and slots in vdom
|
// mounting vapor components and slots in vdom
|
||||||
const vaporInteropImpl: Omit<
|
const vaporInteropImpl: Omit<
|
||||||
|
@ -62,6 +69,12 @@ const vaporInteropImpl: Omit<
|
||||||
))
|
))
|
||||||
instance.rawPropsRef = propsRef
|
instance.rawPropsRef = propsRef
|
||||||
instance.rawSlotsRef = slotsRef
|
instance.rawSlotsRef = slotsRef
|
||||||
|
if (vnode.transition) {
|
||||||
|
setTransitionToInstance(
|
||||||
|
instance,
|
||||||
|
vnode.transition as VaporTransitionHooks,
|
||||||
|
)
|
||||||
|
}
|
||||||
mountComponent(instance, container, selfAnchor)
|
mountComponent(instance, container, selfAnchor)
|
||||||
simpleSetCurrentInstance(prev)
|
simpleSetCurrentInstance(prev)
|
||||||
return instance
|
return instance
|
||||||
|
@ -174,7 +187,7 @@ function createVDOMComponent(
|
||||||
let isMounted = false
|
let isMounted = false
|
||||||
const parentInstance = currentInstance as VaporComponentInstance
|
const parentInstance = currentInstance as VaporComponentInstance
|
||||||
const unmount = (parentNode?: ParentNode, transition?: TransitionHooks) => {
|
const unmount = (parentNode?: ParentNode, transition?: TransitionHooks) => {
|
||||||
if (transition) setTransitionHooks(vnode, transition)
|
if (transition) setVNodeTransitionHooks(vnode, transition)
|
||||||
internals.umt(vnode.component!, null, !!parentNode)
|
internals.umt(vnode.component!, null, !!parentNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -182,7 +195,7 @@ function createVDOMComponent(
|
||||||
const prev = currentInstance
|
const prev = currentInstance
|
||||||
simpleSetCurrentInstance(parentInstance)
|
simpleSetCurrentInstance(parentInstance)
|
||||||
if (!isMounted) {
|
if (!isMounted) {
|
||||||
if (transition) setTransitionHooks(vnode, transition)
|
if (transition) setVNodeTransitionHooks(vnode, transition)
|
||||||
internals.mt(
|
internals.mt(
|
||||||
vnode,
|
vnode,
|
||||||
parentNode,
|
parentNode,
|
||||||
|
|
|
@ -50,6 +50,16 @@ interface PuppeteerUtils {
|
||||||
clearValue(selector: string): Promise<any>
|
clearValue(selector: string): Promise<any>
|
||||||
timeout(time: number): Promise<any>
|
timeout(time: number): Promise<any>
|
||||||
nextFrame(): Promise<any>
|
nextFrame(): Promise<any>
|
||||||
|
transitionStart(
|
||||||
|
btnSelector: string,
|
||||||
|
containerSelector: string,
|
||||||
|
): Promise<{ classNames: string[]; innerHTML: string }>
|
||||||
|
waitForElement(
|
||||||
|
selector: string,
|
||||||
|
text: string,
|
||||||
|
classNames: string[],
|
||||||
|
timeout?: number,
|
||||||
|
): Promise<any>
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setupPuppeteer(args?: string[]): PuppeteerUtils {
|
export function setupPuppeteer(args?: string[]): PuppeteerUtils {
|
||||||
|
@ -200,6 +210,43 @@ export function setupPuppeteer(args?: string[]): PuppeteerUtils {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const transitionStart = (btnSelector: string, containerSelector: string) =>
|
||||||
|
page.evaluate(
|
||||||
|
([btnSel, containerSel]) => {
|
||||||
|
;(document.querySelector(btnSel) as HTMLElement)!.click()
|
||||||
|
return Promise.resolve().then(() => {
|
||||||
|
const container = document.querySelector(containerSel)!
|
||||||
|
return {
|
||||||
|
classNames: container.className.split(/\s+/g),
|
||||||
|
innerHTML: container.innerHTML,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
[btnSelector, containerSelector],
|
||||||
|
)
|
||||||
|
|
||||||
|
const waitForElement = (
|
||||||
|
selector: string,
|
||||||
|
text: string,
|
||||||
|
classNames: string[], // if empty, check for no classes
|
||||||
|
timeout = 2000,
|
||||||
|
) =>
|
||||||
|
page.waitForFunction(
|
||||||
|
(sel, expectedText, expectedClasses) => {
|
||||||
|
const el = document.querySelector(sel)
|
||||||
|
const hasClasses =
|
||||||
|
expectedClasses.length === 0
|
||||||
|
? el?.classList.length === 0
|
||||||
|
: expectedClasses.every(c => el?.classList.contains(c))
|
||||||
|
const hasText = el?.textContent?.includes(expectedText)
|
||||||
|
return !!el && hasClasses && hasText
|
||||||
|
},
|
||||||
|
{ timeout },
|
||||||
|
selector,
|
||||||
|
text,
|
||||||
|
classNames,
|
||||||
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
page: () => page,
|
page: () => page,
|
||||||
click,
|
click,
|
||||||
|
@ -219,5 +266,7 @@ export function setupPuppeteer(args?: string[]): PuppeteerUtils {
|
||||||
clearValue,
|
clearValue,
|
||||||
timeout,
|
timeout,
|
||||||
nextFrame,
|
nextFrame,
|
||||||
|
transitionStart,
|
||||||
|
waitForElement,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue