Преглед изворни кода

test: preserve teleport-owned anchors during null-branch hydration

daiwei пре 2 недеља
родитељ
комит
32465d1c1a
1 измењених фајлова са 156 додато и 10 уклоњено
  1. 156 10
      packages/runtime-vapor/__tests__/hydration.spec.ts

+ 156 - 10
packages/runtime-vapor/__tests__/hydration.spec.ts

@@ -5851,16 +5851,17 @@ describe('mismatch handling', () => {
     )
     expect(`Hydration children mismatch`).toHaveBeenWarned()
   })
-  // test('Teleport target has empty children', () => {
-  //   const teleportContainer = document.createElement('div')
-  //   teleportContainer.id = 'teleport'
-  //   document.body.appendChild(teleportContainer)
-  //   mountWithHydration('<!--teleport start--><!--teleport end-->', () =>
-  //     h(Teleport, { to: '#teleport' }, [h('span', 'value')]),
-  //   )
-  //   expect(teleportContainer.innerHTML).toBe(`<span>value</span>`)
-  //   expect(`Hydration children mismatch`).toHaveBeenWarned()
-  // })
+  test('Teleport target has empty children', async () => {
+    const teleportContainer = document.createElement('div')
+    teleportContainer.id = 'teleport'
+    document.body.appendChild(teleportContainer)
+    await mountWithHydration(
+      '<!--teleport start--><!--teleport end-->',
+      `<teleport to="#teleport"><span>value</span></teleport>`,
+    )
+    expect(teleportContainer.innerHTML).toBe(`<span>value</span>`)
+    expect(`Hydration children mismatch`).toHaveBeenWarned()
+  })
   // test('comment mismatch (element)', () => {
   //   const { container } = mountWithHydration(`<div><span></span></div>`, () =>
   //     h('div', [createCommentVNode('hi')]),
@@ -7535,6 +7536,100 @@ describe('VDOM interop', () => {
     )
   })
 
+  test('hydrate Teleport VNode dynamic component to null branch at end of container', async () => {
+    const data = ref({
+      showTeleport: true,
+    })
+    const code = `<script setup>
+        import { Teleport, computed, h } from 'vue'
+        const data = _data
+        const vnode = computed(() =>
+          data.value.showTeleport
+            ? h(Teleport, { to: '#target', disabled: true }, [
+                h('div', null, 'teleported'),
+              ])
+            : null
+        )
+      </script>
+      <template>
+        <component :is="vnode" />
+      </template>`
+
+    const serverComp = compile(code, data, {}, { vapor: true, ssr: true })
+    const html = await VueServerRenderer.renderToString(
+      runtimeDom.createSSRApp(serverComp),
+    )
+
+    data.value.showTeleport = false
+
+    const container = document.createElement('div')
+    container.innerHTML = html
+    document.body.appendChild(container)
+
+    const clientComp = compile(code, data, {}, { vapor: true, ssr: false })
+    const app = createVaporSSRApp(clientComp)
+    app.use(runtimeVapor.vaporInteropPlugin)
+    app.mount(container)
+
+    expect(`Hydration node mismatch`).not.toHaveBeenWarned()
+    expect(`Hydration children mismatch`).toHaveBeenWarned()
+    expect(container.innerHTML).toBe('<!--dynamic-component-->')
+
+    data.value.showTeleport = true
+    await nextTick()
+    expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(`
+      "
+      <!--teleport start-->
+      <div>teleported</div>
+      <!--teleport end-->
+      <!--dynamic-component-->"
+    `)
+  })
+
+  test('hydrate Teleport VNode dynamic component to null branch at end of allowed-mismatch container', async () => {
+    const data = ref({
+      showTeleport: true,
+    })
+    const code = `<script setup>
+        import { Teleport, computed, h } from 'vue'
+        const data = _data
+        const vnode = computed(() =>
+          data.value.showTeleport
+            ? h(Teleport, { to: '#target', disabled: true }, [
+                h('div', null, 'teleported'),
+              ])
+            : null
+        )
+      </script>
+      <template>
+        <div data-allow-mismatch="children">
+          <component :is="vnode" />
+        </div>
+      </template>`
+
+    const serverComp = compile(code, data, {}, { vapor: true, ssr: true })
+    const html = await VueServerRenderer.renderToString(
+      runtimeDom.createSSRApp(serverComp),
+    )
+
+    data.value.showTeleport = false
+
+    const container = document.createElement('div')
+    container.innerHTML = html
+    document.body.appendChild(container)
+
+    const clientComp = compile(code, data, {}, { vapor: true, ssr: false })
+    const app = createVaporSSRApp(clientComp)
+    app.use(runtimeVapor.vaporInteropPlugin)
+    app.mount(container)
+
+    expect(`Hydration node mismatch`).not.toHaveBeenWarned()
+    expect(`Hydration children mismatch`).not.toHaveBeenWarned()
+    expect(container.innerHTML).toBe(
+      '<div data-allow-mismatch="children"><!--dynamic-component--></div>',
+    )
+  })
+
   test('hydrate vapor slot in vdom component with empty slot and sibling nodes', async () => {
     const msg = ref('Hello World!')
     const { container } = await testWithVaporApp(
@@ -7722,6 +7817,57 @@ describe('VDOM interop', () => {
     `)
   })
 
+  test('hydrate Teleport dynamic component to null branch should remove teleport range and preserve trailing sibling', async () => {
+    const data = ref({
+      showTeleport: true,
+      tail: 'tail',
+    })
+
+    const code = `<script setup>
+        import { Teleport } from 'vue'
+        const data = _data
+      </script>
+      <template>
+        <component
+          :is="data.showTeleport ? Teleport : null"
+          to="#target"
+          :disabled="true"
+        >
+          <div>teleported</div>
+        </component>
+        <span>{{ data.tail }}</span>
+      </template>`
+
+    const serverComp = compile(code, data, {}, { vapor: true, ssr: true })
+    const html = await VueServerRenderer.renderToString(
+      runtimeDom.createSSRApp(serverComp),
+    )
+
+    data.value.showTeleport = false
+
+    const container = document.createElement('div')
+    container.innerHTML = html
+    document.body.appendChild(container)
+
+    const clientComp = compile(code, data, {}, { vapor: true, ssr: false })
+    createVaporSSRApp(clientComp).mount(container)
+
+    expect(`Hydration children mismatch`).toHaveBeenWarned()
+    expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(`
+      "
+      <!--[--><!--dynamic-component--><span>tail</span><!--]-->
+      "
+    `)
+
+    data.value.tail = 'tail-updated'
+    await nextTick()
+    expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(`
+      "
+      <!--[--><!--dynamic-component--><span>tail-updated</span><!--]-->
+      "
+    `)
+  })
+
   test('hydrate enabled Teleport VNode via createDynamicComponent and switch branch', async () => {
     const data = ref({
       showTeleport: true,