From 7070f33300cfd26ca408b12a26e549277aa6e7a4 Mon Sep 17 00:00:00 2001 From: daiwei Date: Wed, 14 May 2025 11:03:54 +0800 Subject: [PATCH] wip: add tests --- packages/runtime-vapor/__tests__/_utils.ts | 51 ++++ .../__tests__/dom/templateRef.spec.ts | 227 +++++++++++++++++- packages/runtime-vapor/src/vdomInterop.ts | 12 +- 3 files changed, 287 insertions(+), 3 deletions(-) diff --git a/packages/runtime-vapor/__tests__/_utils.ts b/packages/runtime-vapor/__tests__/_utils.ts index 0ed645544..2d9a83e61 100644 --- a/packages/runtime-vapor/__tests__/_utils.ts +++ b/packages/runtime-vapor/__tests__/_utils.ts @@ -2,6 +2,10 @@ import { createVaporApp } from '../src' import type { App } from '@vue/runtime-dom' import type { VaporComponent, VaporComponentInstance } from '../src/component' import type { RawProps } from '../src/componentProps' +import { compileScript, parse } from '@vue/compiler-sfc' +import * as runtimeVapor from '../src' +import * as runtimeDom from '@vue/runtime-dom' +import * as VueServerRenderer from '@vue/server-renderer' export interface RenderContext { component: VaporComponent @@ -82,3 +86,50 @@ export function makeRender( return define } + +export { runtimeDom, runtimeVapor } +export function compile( + sfc: string, + data: runtimeDom.Ref, + components: Record = {}, + { + vapor = true, + ssr = false, + }: { + vapor?: boolean | undefined + ssr?: boolean | undefined + } = {}, +): any { + if (!sfc.includes(`const data = _data; const components = _components;` + + sfc + } + const descriptor = parse(sfc).descriptor + + const script = compileScript(descriptor, { + id: 'x', + isProd: true, + inlineTemplate: true, + genDefaultAs: '__sfc__', + vapor, + templateOptions: { + ssr, + }, + }) + + const code = + script.content + .replace(/\bimport {/g, 'const {') + .replace(/ as _/g, ': _') + .replace(/} from ['"]vue['"]/g, `} = Vue`) + .replace(/} from "vue\/server-renderer"/g, '} = VueServerRenderer') + + '\nreturn __sfc__' + + return new Function('Vue', 'VueServerRenderer', '_data', '_components', code)( + { ...runtimeDom, ...runtimeVapor }, + VueServerRenderer, + data, + components, + ) +} diff --git a/packages/runtime-vapor/__tests__/dom/templateRef.spec.ts b/packages/runtime-vapor/__tests__/dom/templateRef.spec.ts index e696fadd4..1f2db60c5 100644 --- a/packages/runtime-vapor/__tests__/dom/templateRef.spec.ts +++ b/packages/runtime-vapor/__tests__/dom/templateRef.spec.ts @@ -5,11 +5,12 @@ import { createIf, createSlot, createTemplateRefSetter, + delegateEvents, insert, renderEffect, template, } from '../../src' -import { makeRender } from '../_utils' +import { compile, makeRender, runtimeDom, runtimeVapor } from '../_utils' import { type ShallowRef, currentInstance, @@ -716,3 +717,227 @@ describe('api: template ref', () => { // expect(elRef1.value).toBe(elRef2.value) // }) }) + +describe('interop: template ref', () => { + beforeEach(() => { + document.body.innerHTML = '' + }) + + const triggerEvent = (type: string, el: Element) => { + const event = new Event(type, { bubbles: true }) + el.dispatchEvent(event) + } + + delegateEvents('click') + + async function testTemplateRefInterop( + code: string, + components: Record = {}, + data: any = {}, + { vapor = false } = {}, + ) { + const clientComponents: any = {} + for (const key in components) { + const comp = components[key] + const code = comp.code + const isVaporComp = !!comp.vapor + clientComponents[key] = compile(code, data, clientComponents, { + vapor: isVaporComp, + }) + } + + const clientComp = compile(code, data, clientComponents, { + vapor, + }) + + const app = (vapor ? runtimeVapor.createVaporApp : runtimeDom.createApp)( + clientComp, + ) + app.use(runtimeVapor.vaporInteropPlugin) + + const container = document.createElement('div') + document.body.appendChild(container) + app.mount(container) + return { container } + } + + test('vdom app: useTemplateRef on vapor child', async () => { + const { container } = await testTemplateRefInterop( + ` + `, + { + VaporChild: { + code: ` + + + `, + vapor: true, + }, + }, + ) + + expect(container.innerHTML).toBe( + `
foo
`, + ) + + const btn = container.querySelector('.btn') + triggerEvent('click', btn!) + await nextTick() + expect(container.innerHTML).toBe( + `
bar
`, + ) + }) + + test('vdom app: static ref on vapor child', async () => { + const { container } = await testTemplateRefInterop( + ` + `, + { + VaporChild: { + code: ` + + + `, + vapor: true, + }, + }, + ) + + expect(container.innerHTML).toBe( + `
foo
`, + ) + + const btn = container.querySelector('.btn') + triggerEvent('click', btn!) + await nextTick() + expect(container.innerHTML).toBe( + `
bar
`, + ) + }) + + test('vapor app: useTemplateRef on vdom child', async () => { + const { container } = await testTemplateRefInterop( + ` + `, + { + VDOMChild: { + code: ` + + + `, + vapor: false, + }, + }, + undefined, + { vapor: true }, + ) + + expect(container.innerHTML).toBe( + `
foo
`, + ) + + const btn = container.querySelector('.btn') + triggerEvent('click', btn!) + await nextTick() + expect(container.innerHTML).toBe( + `
bar
`, + ) + }) + + test('vapor app: static ref on vdom child', async () => { + const { container } = await testTemplateRefInterop( + ` + `, + { + VDomChild: { + code: ` + + + `, + vapor: false, + }, + }, + undefined, + { vapor: true }, + ) + + expect(container.innerHTML).toBe( + `
foo
`, + ) + + const btn = container.querySelector('.btn') + triggerEvent('click', btn!) + await nextTick() + expect(container.innerHTML).toBe( + `
bar
`, + ) + }) +}) diff --git a/packages/runtime-vapor/src/vdomInterop.ts b/packages/runtime-vapor/src/vdomInterop.ts index 233524129..38e29b215 100644 --- a/packages/runtime-vapor/src/vdomInterop.ts +++ b/packages/runtime-vapor/src/vdomInterop.ts @@ -30,7 +30,7 @@ import { unmountComponent, } from './component' import { type Block, VaporFragment, insert, remove } from './block' -import { EMPTY_OBJ, extend, isFunction } from '@vue/shared' +import { EMPTY_OBJ, extend, isFunction, isReservedProp } from '@vue/shared' import { type RawProps, rawPropsProxyHandlers } from './componentProps' import type { RawSlots, VaporSlot } from './componentSlots' import { renderEffect } from './renderEffect' @@ -49,7 +49,15 @@ const vaporInteropImpl: Omit< const prev = currentInstance simpleSetCurrentInstance(parentComponent) - const propsRef = shallowRef(vnode.props) + // filter out reserved props + const props: VNode['props'] = {} + for (const key in vnode.props) { + if (!isReservedProp(key)) { + props[key] = vnode.props[key] + } + } + + const propsRef = shallowRef(props) const slotsRef = shallowRef(vnode.children) // @ts-expect-error