From: Evan You Date: Tue, 11 Mar 2025 11:06:10 +0000 (+0800) Subject: test(vapor): refactor hydration tests to include compilation X-Git-Tag: v3.6.0-alpha.1~16^2~39 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=a51dd42dc609d0a5eec76cfd731e84e4af099981;p=thirdparty%2Fvuejs%2Fcore.git test(vapor): refactor hydration tests to include compilation --- diff --git a/packages/runtime-vapor/__tests__/hydration.spec.ts b/packages/runtime-vapor/__tests__/hydration.spec.ts index caa84b16b9..666f5a68f0 100644 --- a/packages/runtime-vapor/__tests__/hydration.spec.ts +++ b/packages/runtime-vapor/__tests__/hydration.spec.ts @@ -1,29 +1,76 @@ -// import { type SSRContext, renderToString } from '@vue/server-renderer' -import { - child, - createComponent, - createVaporSSRApp, - delegateEvents, - next, - renderEffect, - setClass, - setInsertionState, - setText, - template, -} from '../src' -import { nextTick, ref, toDisplayString } from '@vue/runtime-dom' - -function mountWithHydration(html: string, setup: () => any) { +import { createVaporSSRApp, delegateEvents } from '../src' +import { nextTick, ref } from '@vue/runtime-dom' +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' + +const Vue = { ...runtimeDom, ...runtimeVapor } + +function compile( + sfc: string, + data: runtimeDom.Ref, + components: Record = {}, + ssr = false, +) { + 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: true, + 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)( + Vue, + VueServerRenderer, + data, + components, + ) +} + +async function testHydration( + code: string, + components: Record = {}, +) { + const data = ref('foo') + const ssrComponents: any = {} + const clientComponents: any = {} + for (const key in components) { + clientComponents[key] = compile(components[key], data, clientComponents) + ssrComponents[key] = compile(components[key], data, ssrComponents, true) + } + + const serverComp = compile(code, data, ssrComponents, true) + const html = await VueServerRenderer.renderToString( + runtimeDom.createSSRApp(serverComp), + ) const container = document.createElement('div') document.body.appendChild(container) container.innerHTML = html - const app = createVaporSSRApp({ - setup, - }) + + const clientComp = compile(code, data, clientComponents) + const app = createVaporSSRApp(clientComp) app.mount(container) - return { - container, - } + return { data, container } } const triggerEvent = (type: string, el: Element) => { @@ -31,7 +78,7 @@ const triggerEvent = (type: string, el: Element) => { el.dispatchEvent(event) } -describe('SSR hydration', () => { +describe('Vapor Mode hydration', () => { delegateEvents('click') beforeEach(() => { @@ -39,281 +86,165 @@ describe('SSR hydration', () => { }) test('root text', async () => { - const msg = ref('foo') - const t = template(' ') - const { container } = mountWithHydration('foo', () => { - const n = t() - renderEffect(() => setText(n as Text, msg.value)) - return n - }) - expect(container.textContent).toBe('foo') - msg.value = 'bar' + const { data, container } = await testHydration(` + + `) + expect(container.innerHTML).toMatchInlineSnapshot(`"foo"`) + + data.value = 'bar' await nextTick() - expect(container.textContent).toBe('bar') + expect(container.innerHTML).toMatchInlineSnapshot(`"bar"`) }) - test('root comment', () => { - const t0 = template('') - const { container } = mountWithHydration('', () => t0()) + test('root comment', async () => { + const { container } = await testHydration(` + + `) expect(container.innerHTML).toBe('') expect(`Hydration children mismatch in
`).not.toHaveBeenWarned() }) test('root with mixed element and text', async () => { - const t0 = template(' A') - const t1 = template('foo bar') - const t2 = template(' ') - const msg = ref('hello') - const { container } = mountWithHydration( - ' Afoo barhello', - () => { - const n0 = t0() - const n1 = t1() - const n2 = t2() - renderEffect(() => setText(n2 as Text, toDisplayString(msg.value))) - return [n0, n1, n2] - }, + const { container, data } = await testHydration(` + + `) + expect(container.innerHTML).toMatchInlineSnapshot( + `" Afoofoo"`, ) - expect(container.innerHTML).toBe(' Afoo barhello') - msg.value = 'bar' + + data.value = 'bar' await nextTick() - expect(container.innerHTML).toBe(' Afoo barbar') + expect(container.innerHTML).toMatchInlineSnapshot( + `" Abarbar"`, + ) }) test('empty element', async () => { - const t0 = template('
', true) - const { container } = mountWithHydration('
', () => t0()) + const { container } = await testHydration(` + + `) expect(container.innerHTML).toBe('
') expect(`Hydration children mismatch in
`).not.toHaveBeenWarned() }) - test('element with text children', async () => { - const t0 = template('
', true) - const msg = ref('foo') - const { container } = mountWithHydration( - '
foo
', - () => { - const n0 = t0() as Element - const x0 = child(n0) as Text - renderEffect(() => { - const _msg = msg.value - - setText(x0, toDisplayString(_msg)) - setClass(n0, _msg) - }) - return n0 - }, + test('element with binding and text children', async () => { + const { container, data } = await testHydration(` + + `) + expect(container.innerHTML).toMatchInlineSnapshot( + `"
foo
"`, ) - expect(container.innerHTML).toBe(`
foo
`) - msg.value = 'bar' + + data.value = 'bar' await nextTick() - expect(container.innerHTML).toBe(`
bar
`) + expect(container.innerHTML).toMatchInlineSnapshot( + `"
bar
"`, + ) }) test('element with elements children', async () => { - const t0 = template('
', true) - const msg = ref('foo') - const fn = vi.fn() - const { container } = mountWithHydration( - '
foo
', - () => { - const n2 = t0() as Element - const n0 = child(n2) as Element - const n1 = next(n0) as Element - const x0 = child(n0) as Text - ;(n1 as any).$evtclick = fn - renderEffect(() => { - const _msg = msg.value - - setText(x0, toDisplayString(_msg)) - setClass(n1, _msg) - }) - return n2 - }, - ) - expect(container.innerHTML).toBe( - `
foo
`, + const { container } = await testHydration(` + + `) + expect(container.innerHTML).toMatchInlineSnapshot( + `"
foo
"`, ) // event handler triggerEvent('click', container.querySelector('.foo')!) - expect(fn).toHaveBeenCalled() - msg.value = 'bar' await nextTick() - expect(container.innerHTML).toBe( - `
bar
`, + expect(container.innerHTML).toMatchInlineSnapshot( + `"
bar
"`, ) }) test('basic component', async () => { - const t0 = template(' ') - const msg = ref('foo') - const Comp = { - setup() { - const n0 = t0() as Text - renderEffect(() => setText(n0, toDisplayString(msg.value))) - return n0 - }, - } - - const t1 = template('
', true) - const { container } = mountWithHydration( - '
foo
', - () => { - const n1 = t1() as Element - setInsertionState(n1) - createComponent(Comp) - return n1 - }, + const { container, data } = await testHydration( + ` + + `, + { Child: `` }, + ) + expect(container.innerHTML).toMatchInlineSnapshot( + `"
foo
"`, ) - expect(container.innerHTML).toBe(`
foo
`) - - msg.value = 'bar' + data.value = 'bar' await nextTick() - expect(container.innerHTML).toBe(`
bar
`) + expect(container.innerHTML).toMatchInlineSnapshot( + `"
bar
"`, + ) }) test('fragment component', async () => { - const t0 = template('
') - const t1 = template(' ') - const msg = ref('foo') - const Comp = { - setup() { - const n0 = t0() as Element - const n1 = t1() as Text - const x0 = child(n0) as Text - renderEffect(() => { - const _msg = msg.value - - setText(x0, toDisplayString(_msg)) - setText(n1, toDisplayString(_msg)) - }) - return [n0, n1] - }, - } - - const t2 = template('
', true) - const { container } = mountWithHydration( - '
foo
foo
', - () => { - const n1 = t2() as Element - setInsertionState(n1) - createComponent(Comp) - return n1 - }, + const { container, data } = await testHydration( + ` + + `, + { Child: `` }, ) - - expect(container.innerHTML).toBe( - `
foo
foo
`, + expect(container.innerHTML).toMatchInlineSnapshot( + `"
foo
-foo-
"`, ) - msg.value = 'bar' + data.value = 'bar' await nextTick() - expect(container.innerHTML).toBe( - `
bar
bar
`, + expect(container.innerHTML).toMatchInlineSnapshot( + `"
bar
-bar-
"`, ) }) test('fragment component with prepend', async () => { - const t0 = template('
') - const t1 = template(' ') - const msg = ref('foo') - const Comp = { - setup() { - const n0 = t0() as Element - const n1 = t1() as Text - const x0 = child(n0) as Text - renderEffect(() => { - const _msg = msg.value - - setText(x0, toDisplayString(_msg)) - setText(n1, toDisplayString(_msg)) - }) - return [n0, n1] - }, - } - - const t2 = template('
', true) - const { container } = mountWithHydration( - '
foo
foo
', - () => { - const n1 = t2() as Element - setInsertionState(n1, 0) - createComponent(Comp) - return n1 - }, + const { container, data } = await testHydration( + ` + + `, + { Child: `` }, ) - - expect(container.innerHTML).toBe( - `
foo
foo
`, + expect(container.innerHTML).toMatchInlineSnapshot( + `"
foo
-foo-
"`, ) - msg.value = 'bar' + data.value = 'bar' await nextTick() - expect(container.innerHTML).toBe( - `
bar
bar
`, + expect(container.innerHTML).toMatchInlineSnapshot( + `"
bar
-bar-
"`, ) }) test('nested fragment components', async () => { - const t0 = template('
') - const t1 = template(' ') - const msg = ref('foo') - const Comp = { - setup() { - const n0 = t0() as Element - const n1 = t1() as Text - const x0 = child(n0) as Text - renderEffect(() => { - const _msg = msg.value - - setText(x0, toDisplayString(_msg)) - setText(n1, toDisplayString(_msg)) - }) - return [n0, n1] - }, - } - - const t2 = template('
') - const Parent = { - setup() { - const n0 = t2() - const n1 = createComponent(Comp) - const n2 = t2() - return [n0, n1, n2] - }, - } - - const t3 = template('
', true) - const { container } = mountWithHydration( - '
' + - '
foo
foo
' + - '
', - () => { - const n1 = t3() as Element - setInsertionState(n1, 0) - createComponent(Parent) - return n1 + const { container, data } = await testHydration( + ` + + `, + { + Parent: ``, + Child: ``, }, ) - - expect(container.innerHTML).toBe( - '
' + - '
foo
foo
' + - '
', + expect(container.innerHTML).toMatchInlineSnapshot( + `"
foo
-foo-
"`, ) - msg.value = 'bar' + data.value = 'bar' await nextTick() - expect(container.innerHTML).toBe( - '
' + - '
bar
bar
' + - '
', + expect(container.innerHTML).toMatchInlineSnapshot( + `"
bar
-bar-
"`, ) }) + test.todo('if') + + test.todo('for') + + test.todo('slots') + // test('element with ref', () => { // const el = ref() // const { vnode, container } = mountWithHydration('
', () => @@ -323,329 +254,6 @@ describe('SSR hydration', () => { // expect(el.value).toBe(vnode.el) // }) - // test('Fragment', async () => { - // const msg = ref('foo') - // const fn = vi.fn() - // const { vnode, container } = mountWithHydration( - // '
foo
', - // () => - // h('div', [ - // [ - // h('span', msg.value), - // [h('span', { class: msg.value, onClick: fn })], - // ], - // ]), - // ) - // expect(vnode.el).toBe(container.firstChild) - - // expect(vnode.el.innerHTML).toBe( - // `foo`, - // ) - - // // start fragment 1 - // const fragment1 = (vnode.children as VNode[])[0] - // expect(fragment1.el).toBe(vnode.el.childNodes[0]) - // const fragment1Children = fragment1.children as VNode[] - - // // first - // expect(fragment1Children[0].el!.tagName).toBe('SPAN') - // expect(fragment1Children[0].el).toBe(vnode.el.childNodes[1]) - - // // start fragment 2 - // const fragment2 = fragment1Children[1] - // expect(fragment2.el).toBe(vnode.el.childNodes[2]) - // const fragment2Children = fragment2.children as VNode[] - - // // second - // expect(fragment2Children[0].el!.tagName).toBe('SPAN') - // expect(fragment2Children[0].el).toBe(vnode.el.childNodes[3]) - - // // end fragment 2 - // expect(fragment2.anchor).toBe(vnode.el.childNodes[4]) - - // // end fragment 1 - // expect(fragment1.anchor).toBe(vnode.el.childNodes[5]) - - // // event handler - // triggerEvent('click', vnode.el.querySelector('.foo')!) - // expect(fn).toHaveBeenCalled() - - // msg.value = 'bar' - // await nextTick() - // expect(vnode.el.innerHTML).toBe( - // `bar`, - // ) - // }) - - // // #7285 - // test('Fragment (multiple continuous text vnodes)', async () => { - // // should no mismatch warning - // const { container } = mountWithHydration('fooo', () => [ - // 'fo', - // createTextVNode('o'), - // 'o', - // ]) - // expect(container.textContent).toBe('fooo') - // }) - - // test('Teleport', async () => { - // const msg = ref('foo') - // const fn = vi.fn() - // const teleportContainer = document.createElement('div') - // teleportContainer.id = 'teleport' - // teleportContainer.innerHTML = `foo` - // document.body.appendChild(teleportContainer) - - // const { vnode, container } = mountWithHydration( - // '', - // () => - // h(Teleport, { to: '#teleport' }, [ - // h('span', msg.value), - // h('span', { class: msg.value, onClick: fn }), - // ]), - // ) - - // expect(vnode.el).toBe(container.firstChild) - // expect(vnode.anchor).toBe(container.lastChild) - - // expect(vnode.target).toBe(teleportContainer) - // expect(vnode.targetStart).toBe(teleportContainer.childNodes[0]) - // expect((vnode.children as VNode[])[0].el).toBe( - // teleportContainer.childNodes[1], - // ) - // expect((vnode.children as VNode[])[1].el).toBe( - // teleportContainer.childNodes[2], - // ) - // expect(vnode.targetAnchor).toBe(teleportContainer.childNodes[3]) - - // // event handler - // triggerEvent('click', teleportContainer.querySelector('.foo')!) - // expect(fn).toHaveBeenCalled() - - // msg.value = 'bar' - // await nextTick() - // expect(teleportContainer.innerHTML).toBe( - // `bar`, - // ) - // }) - - // test('Teleport (multiple + integration)', async () => { - // const msg = ref('foo') - // const fn1 = vi.fn() - // const fn2 = vi.fn() - - // const Comp = () => [ - // h(Teleport, { to: '#teleport2' }, [ - // h('span', msg.value), - // h('span', { class: msg.value, onClick: fn1 }), - // ]), - // h(Teleport, { to: '#teleport2' }, [ - // h('span', msg.value + '2'), - // h('span', { class: msg.value + '2', onClick: fn2 }), - // ]), - // ] - - // const teleportContainer = document.createElement('div') - // teleportContainer.id = 'teleport2' - // const ctx: SSRContext = {} - // const mainHtml = await renderToString(h(Comp), ctx) - // expect(mainHtml).toMatchInlineSnapshot( - // `""`, - // ) - - // const teleportHtml = ctx.teleports!['#teleport2'] - // expect(teleportHtml).toMatchInlineSnapshot( - // `"foofoo2"`, - // ) - - // teleportContainer.innerHTML = teleportHtml - // document.body.appendChild(teleportContainer) - - // const { vnode, container } = mountWithHydration(mainHtml, Comp) - // expect(vnode.el).toBe(container.firstChild) - // const teleportVnode1 = (vnode.children as VNode[])[0] - // const teleportVnode2 = (vnode.children as VNode[])[1] - // expect(teleportVnode1.el).toBe(container.childNodes[1]) - // expect(teleportVnode1.anchor).toBe(container.childNodes[2]) - // expect(teleportVnode2.el).toBe(container.childNodes[3]) - // expect(teleportVnode2.anchor).toBe(container.childNodes[4]) - - // expect(teleportVnode1.target).toBe(teleportContainer) - // expect(teleportVnode1.targetStart).toBe(teleportContainer.childNodes[0]) - // expect((teleportVnode1 as any).children[0].el).toBe( - // teleportContainer.childNodes[1], - // ) - // expect(teleportVnode1.targetAnchor).toBe(teleportContainer.childNodes[3]) - - // expect(teleportVnode2.target).toBe(teleportContainer) - // expect(teleportVnode2.targetStart).toBe(teleportContainer.childNodes[4]) - // expect((teleportVnode2 as any).children[0].el).toBe( - // teleportContainer.childNodes[5], - // ) - // expect(teleportVnode2.targetAnchor).toBe(teleportContainer.childNodes[7]) - - // // // event handler - // triggerEvent('click', teleportContainer.querySelector('.foo')!) - // expect(fn1).toHaveBeenCalled() - - // triggerEvent('click', teleportContainer.querySelector('.foo2')!) - // expect(fn2).toHaveBeenCalled() - - // msg.value = 'bar' - // await nextTick() - // expect(teleportContainer.innerHTML).toMatchInlineSnapshot( - // `"barbar2"`, - // ) - // }) - - // test('Teleport (disabled)', async () => { - // const msg = ref('foo') - // const fn1 = vi.fn() - // const fn2 = vi.fn() - - // const Comp = () => [ - // h('div', 'foo'), - // h(Teleport, { to: '#teleport3', disabled: true }, [ - // h('span', msg.value), - // h('span', { class: msg.value, onClick: fn1 }), - // ]), - // h('div', { class: msg.value + '2', onClick: fn2 }, 'bar'), - // ] - - // const teleportContainer = document.createElement('div') - // teleportContainer.id = 'teleport3' - // const ctx: SSRContext = {} - // const mainHtml = await renderToString(h(Comp), ctx) - // expect(mainHtml).toMatchInlineSnapshot( - // `"
foo
foo
bar
"`, - // ) - - // const teleportHtml = ctx.teleports!['#teleport3'] - // expect(teleportHtml).toMatchInlineSnapshot( - // `""`, - // ) - - // teleportContainer.innerHTML = teleportHtml - // document.body.appendChild(teleportContainer) - - // const { vnode, container } = mountWithHydration(mainHtml, Comp) - // expect(vnode.el).toBe(container.firstChild) - // const children = vnode.children as VNode[] - - // expect(children[0].el).toBe(container.childNodes[1]) - - // const teleportVnode = children[1] - // expect(teleportVnode.el).toBe(container.childNodes[2]) - // expect((teleportVnode.children as VNode[])[0].el).toBe( - // container.childNodes[3], - // ) - // expect((teleportVnode.children as VNode[])[1].el).toBe( - // container.childNodes[4], - // ) - // expect(teleportVnode.anchor).toBe(container.childNodes[5]) - // expect(children[2].el).toBe(container.childNodes[6]) - - // expect(teleportVnode.target).toBe(teleportContainer) - // expect(teleportVnode.targetStart).toBe(teleportContainer.childNodes[0]) - // expect(teleportVnode.targetAnchor).toBe(teleportContainer.childNodes[1]) - - // // // event handler - // triggerEvent('click', container.querySelector('.foo')!) - // expect(fn1).toHaveBeenCalled() - - // triggerEvent('click', container.querySelector('.foo2')!) - // expect(fn2).toHaveBeenCalled() - - // msg.value = 'bar' - // await nextTick() - // expect(container.innerHTML).toMatchInlineSnapshot( - // `"
foo
bar
bar
"`, - // ) - // }) - - // // #6152 - // test('Teleport (disabled + as component root)', () => { - // const { container } = mountWithHydration( - // '
Parent fragment
Teleport content
', - // () => [ - // h('div', 'Parent fragment'), - // h(() => - // h(Teleport, { to: 'body', disabled: true }, [ - // h('div', 'Teleport content'), - // ]), - // ), - // ], - // ) - // expect(document.body.innerHTML).toBe('') - // expect(container.innerHTML).toBe( - // '
Parent fragment
Teleport content
', - // ) - // expect( - // `Hydration completed but contains mismatches.`, - // ).not.toHaveBeenWarned() - // }) - - // test('Teleport (as component root)', () => { - // const teleportContainer = document.createElement('div') - // teleportContainer.id = 'teleport4' - // teleportContainer.innerHTML = `hello` - // document.body.appendChild(teleportContainer) - - // const wrapper = { - // render() { - // return h(Teleport, { to: '#teleport4' }, ['hello']) - // }, - // } - - // const { vnode, container } = mountWithHydration( - // '
', - // () => h('div', [h(wrapper), h('div')]), - // ) - // expect(vnode.el).toBe(container.firstChild) - // // component el - // const wrapperVNode = (vnode as any).children[0] - // const tpStart = container.firstChild?.firstChild - // const tpEnd = tpStart?.nextSibling - // expect(wrapperVNode.el).toBe(tpStart) - // expect(wrapperVNode.component.subTree.el).toBe(tpStart) - // expect(wrapperVNode.component.subTree.anchor).toBe(tpEnd) - // // next node hydrate properly - // const nextVNode = (vnode as any).children[1] - // expect(nextVNode.el).toBe(container.firstChild?.lastChild) - // }) - - // test('Teleport (nested)', () => { - // const teleportContainer = document.createElement('div') - // teleportContainer.id = 'teleport5' - // teleportContainer.innerHTML = `
child
` - // document.body.appendChild(teleportContainer) - - // const { vnode, container } = mountWithHydration( - // '', - // () => - // h(Teleport, { to: '#teleport5' }, [ - // h('div', [h(Teleport, { to: '#teleport5' }, [h('div', 'child')])]), - // ]), - // ) - - // expect(vnode.el).toBe(container.firstChild) - // expect(vnode.anchor).toBe(container.lastChild) - - // const childDivVNode = (vnode as any).children[0] - // const div = teleportContainer.childNodes[1] - // expect(childDivVNode.el).toBe(div) - // expect(vnode.targetAnchor).toBe(div?.nextSibling) - - // const childTeleportVNode = childDivVNode.children[0] - // expect(childTeleportVNode.el).toBe(div?.firstChild) - // expect(childTeleportVNode.anchor).toBe(div?.lastChild) - - // expect(childTeleportVNode.targetAnchor).toBe(teleportContainer.lastChild) - // expect(childTeleportVNode.children[0].el).toBe( - // teleportContainer.lastChild?.previousSibling, - // ) - // }) - // test('with data-allow-mismatch component when using onServerPrefetch', async () => { // const Comp = { // template: ` @@ -685,178 +293,6 @@ describe('SSR hydration', () => { // ) // }) - // test('Teleport unmount (full integration)', async () => { - // const Comp1 = { - // template: ` - // - // Teleported Comp1 - // - // `, - // } - // const Comp2 = { - // template: ` - //
Comp2
- // `, - // } - - // const toggle = ref(true) - // const App = { - // template: ` - //
- // - // - //
- // `, - // components: { - // Comp1, - // Comp2, - // }, - // setup() { - // return { toggle } - // }, - // } - - // const container = document.createElement('div') - // const teleportContainer = document.createElement('div') - // teleportContainer.id = 'target' - // document.body.appendChild(teleportContainer) - - // // server render - // const ctx: SSRContext = {} - // container.innerHTML = await renderToString(h(App), ctx) - // expect(container.innerHTML).toBe( - // '
', - // ) - // teleportContainer.innerHTML = ctx.teleports!['#target'] - - // // hydrate - // createSSRApp(App).mount(container) - // expect(container.innerHTML).toBe( - // '
', - // ) - // expect(teleportContainer.innerHTML).toBe( - // 'Teleported Comp1', - // ) - // expect(`Hydration children mismatch`).not.toHaveBeenWarned() - - // toggle.value = false - // await nextTick() - // expect(container.innerHTML).toBe('
Comp2
') - // expect(teleportContainer.innerHTML).toBe('') - // }) - - // test('Teleport unmount (mismatch + full integration)', async () => { - // const Comp1 = { - // template: ` - // - // Teleported Comp1 - // - // `, - // } - // const Comp2 = { - // template: ` - //
Comp2
- // `, - // } - - // const toggle = ref(true) - // const App = { - // template: ` - //
- // - // - //
- // `, - // components: { - // Comp1, - // Comp2, - // }, - // setup() { - // return { toggle } - // }, - // } - - // const container = document.createElement('div') - // const teleportContainer = document.createElement('div') - // teleportContainer.id = 'target' - // document.body.appendChild(teleportContainer) - - // // server render - // container.innerHTML = await renderToString(h(App)) - // expect(container.innerHTML).toBe( - // '
', - // ) - // expect(teleportContainer.innerHTML).toBe('') - - // // hydrate - // createSSRApp(App).mount(container) - // expect(container.innerHTML).toBe( - // '
', - // ) - // expect(teleportContainer.innerHTML).toBe('Teleported Comp1') - // expect(`Hydration children mismatch`).toHaveBeenWarned() - - // toggle.value = false - // await nextTick() - // expect(container.innerHTML).toBe('
Comp2
') - // expect(teleportContainer.innerHTML).toBe('') - // }) - - // test('Teleport target change (mismatch + full integration)', async () => { - // const target = ref('#target1') - // const Comp = { - // template: ` - // - // Teleported - // - // `, - // setup() { - // return { target } - // }, - // } - - // const App = { - // template: ` - //
- // - //
- // `, - // components: { - // Comp, - // }, - // } - - // const container = document.createElement('div') - // const teleportContainer1 = document.createElement('div') - // teleportContainer1.id = 'target1' - // const teleportContainer2 = document.createElement('div') - // teleportContainer2.id = 'target2' - // document.body.appendChild(teleportContainer1) - // document.body.appendChild(teleportContainer2) - - // // server render - // container.innerHTML = await renderToString(h(App)) - // expect(container.innerHTML).toBe( - // '
', - // ) - // expect(teleportContainer1.innerHTML).toBe('') - // expect(teleportContainer2.innerHTML).toBe('') - - // // hydrate - // createSSRApp(App).mount(container) - // expect(container.innerHTML).toBe( - // '
', - // ) - // expect(teleportContainer1.innerHTML).toBe('Teleported') - // expect(teleportContainer2.innerHTML).toBe('') - // expect(`Hydration children mismatch`).toHaveBeenWarned() - - // target.value = '#target2' - // await nextTick() - // expect(teleportContainer1.innerHTML).toBe('') - // expect(teleportContainer2.innerHTML).toBe('Teleported') - // }) - // // compile SSR + client render fn from the same template & hydrate // test('full compiler integration', async () => { // const mounted: string[] = [] @@ -1006,164 +442,6 @@ describe('SSR hydration', () => { // expect(handler).toHaveBeenCalled() // }) - // test('Suspense', async () => { - // const AsyncChild = { - // async setup() { - // const count = ref(0) - // return () => - // h( - // 'span', - // { - // onClick: () => { - // count.value++ - // }, - // }, - // count.value, - // ) - // }, - // } - // const { vnode, container } = mountWithHydration('0', () => - // h(Suspense, () => h(AsyncChild)), - // ) - // expect(vnode.el).toBe(container.firstChild) - // // wait for hydration to finish - // await new Promise(r => setTimeout(r)) - // triggerEvent('click', container.querySelector('span')!) - // await nextTick() - // expect(container.innerHTML).toBe(`1`) - // }) - - // // #6638 - // test('Suspense + async component', async () => { - // let isSuspenseResolved = false - // let isSuspenseResolvedInChild: any - // const AsyncChild = defineAsyncComponent(() => - // Promise.resolve( - // defineComponent({ - // setup() { - // isSuspenseResolvedInChild = isSuspenseResolved - // const count = ref(0) - // return () => - // h( - // 'span', - // { - // onClick: () => { - // count.value++ - // }, - // }, - // count.value, - // ) - // }, - // }), - // ), - // ) - // const { vnode, container } = mountWithHydration('0', () => - // h( - // Suspense, - // { - // onResolve() { - // isSuspenseResolved = true - // }, - // }, - // () => h(AsyncChild), - // ), - // ) - // expect(vnode.el).toBe(container.firstChild) - // // wait for hydration to finish - // await new Promise(r => setTimeout(r)) - - // expect(isSuspenseResolvedInChild).toBe(false) - // expect(isSuspenseResolved).toBe(true) - - // // assert interaction - // triggerEvent('click', container.querySelector('span')!) - // await nextTick() - // expect(container.innerHTML).toBe(`1`) - // }) - - // test('Suspense (full integration)', async () => { - // const mountedCalls: number[] = [] - // const asyncDeps: Promise[] = [] - - // const AsyncChild = defineComponent({ - // props: ['n'], - // async setup(props) { - // const count = ref(props.n) - // onMounted(() => { - // mountedCalls.push(props.n) - // }) - // const p = new Promise(r => setTimeout(r, props.n * 10)) - // asyncDeps.push(p) - // await p - // return () => - // h( - // 'span', - // { - // onClick: () => { - // count.value++ - // }, - // }, - // count.value, - // ) - // }, - // }) - - // const done = vi.fn() - // const App = { - // template: ` - // - //
- // - // - //
- //
`, - // components: { - // AsyncChild, - // }, - // methods: { - // done, - // }, - // } - - // const container = document.createElement('div') - // // server render - // container.innerHTML = await renderToString(h(App)) - // expect(container.innerHTML).toMatchInlineSnapshot( - // `"
12
"`, - // ) - // // reset asyncDeps from ssr - // asyncDeps.length = 0 - // // hydrate - // createSSRApp(App).mount(container) - - // expect(mountedCalls.length).toBe(0) - // expect(asyncDeps.length).toBe(2) - - // // wait for hydration to complete - // await Promise.all(asyncDeps) - // await new Promise(r => setTimeout(r)) - - // // should flush buffered effects - // expect(mountedCalls).toMatchObject([1, 2]) - // expect(container.innerHTML).toMatch( - // `
12
`, - // ) - - // const span1 = container.querySelector('span')! - // triggerEvent('click', span1) - // await nextTick() - // expect(container.innerHTML).toMatch( - // `
22
`, - // ) - - // const span2 = span1.nextSibling as Element - // triggerEvent('click', span2) - // await nextTick() - // expect(container.innerHTML).toMatch( - // `
23
`, - // ) - // }) - // test('async component', async () => { // const spy = vi.fn() // const Comp = () => @@ -2464,4 +1742,7 @@ describe('SSR hydration', () => { // expect(`Hydration attribute mismatch`).not.toHaveBeenWarned() // }) // }) + + test.todo('Teleport') + test.todo('Suspense') })