Forráskód Böngészése

test: hydrate async slot branches from empty v-for ranges without consuming siblings

daiwei 2 hete
szülő
commit
825e7d489b
1 módosított fájl, 152 hozzáadás és 0 törlés
  1. 152 0
      packages/runtime-vapor/__tests__/hydration.spec.ts

+ 152 - 0
packages/runtime-vapor/__tests__/hydration.spec.ts

@@ -4970,8 +4970,160 @@ describe('Vapor Mode hydration', () => {
       `)
     })
 
+    test('update async component slot content from empty v-for branch with trailing sibling after parent mount before async component resolve', async () => {
+      const data = ref({
+        items: [] as string[],
+        tail: 'tail',
+      })
+      const compCode = `<div><slot/></div>`
+      const SSRComp = compileVaporComponent(
+        compCode,
+        undefined,
+        undefined,
+        true,
+      )
+      let serverResolve: any
+      let AsyncComp = defineAsyncComponent(
+        () =>
+          new Promise(r => {
+            serverResolve = r
+          }),
+      )
+      const appCode = `<components.AsyncComp><span v-for="item in data.items" :key="item">{{item}}</span><i>{{data.tail}}</i></components.AsyncComp>`
+      const SSRApp = compileVaporComponent(appCode, data, { AsyncComp }, true)
+
+      const htmlPromise = VueServerRenderer.renderToString(
+        runtimeDom.createSSRApp(SSRApp),
+      )
+      serverResolve(SSRComp)
+      const html = await htmlPromise
+
+      let clientResolve: any
+      AsyncComp = defineVaporAsyncComponent(
+        () =>
+          new Promise(r => {
+            clientResolve = r
+          }),
+      ) as any
+
+      const Comp = compileVaporComponent(compCode)
+      const App = compileVaporComponent(appCode, data, { AsyncComp })
+
+      const container = document.createElement('div')
+      container.innerHTML = html
+      document.body.appendChild(container)
+      createVaporSSRApp(App).mount(container)
+
+      data.value.items = ['foo', 'bar']
+      await nextTick()
+
+      clientResolve(Comp)
+      await new Promise(r => setTimeout(r))
+
+      expect(`Hydration node mismatch`).toHaveBeenWarned()
+      expect(`Hydration text mismatch`).toHaveBeenWarned()
+      expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(`
+      	"<div>
+      	<!--[-->
+      	<!--[--><span>foo</span><span>bar</span><!--]-->
+      	<i>tail</i><!--]-->
+      	</div><!--async component-->"
+      `)
+    })
+
     describe('suspense', () => {
       describe('VDOM suspense', () => {
+        test('hydrate VDOM Suspense vapor async setup updates empty v-for before trailing sibling', async () => {
+          const data = ref({
+            items: [] as string[],
+            tail: 'tail',
+          })
+          const vaporChildCode = `
+            <script vapor>
+              import { onMounted } from 'vue'
+              const data = _data
+              onMounted(() => {
+                data.value.items = ['foo', 'bar']
+              })
+              await new Promise(r => setTimeout(r, 10))
+            </script>
+            <template>
+              <div>
+                <span v-for="item in data.items" :key="item">{{ item }}</span>
+                <i>{{ data.tail }}</i>
+              </div>
+            </template>
+          `
+          const appCode = `
+            <script setup>
+              import { Suspense } from 'vue'
+              const components = _components
+            </script>
+            <template>
+              <Suspense>
+                <components.VaporChild />
+              </Suspense>
+            </template>
+          `
+
+          const serverComponents: any = {}
+          const clientComponents: any = {}
+          serverComponents.VaporChild = compile(
+            vaporChildCode,
+            data,
+            serverComponents,
+            {
+              vapor: true,
+              ssr: true,
+            },
+          )
+          clientComponents.VaporChild = compile(
+            vaporChildCode,
+            data,
+            clientComponents,
+            {
+              vapor: true,
+              ssr: false,
+            },
+          )
+          const serverComp = compile(appCode, data, serverComponents, {
+            vapor: false,
+            ssr: true,
+          })
+
+          const html = await VueServerRenderer.renderToString(
+            runtimeDom.createSSRApp(serverComp),
+          )
+          expect(formatHtml(html)).toMatchInlineSnapshot(`
+          	"<div>
+          	<!--[--><!--]-->
+          	<i>tail</i></div>"
+          `)
+
+          const container = document.createElement('div')
+          document.body.appendChild(container)
+          container.innerHTML = html
+
+          const clientComp = compile(appCode, data, clientComponents, {
+            vapor: false,
+            ssr: false,
+          })
+          const app = runtimeDom.createSSRApp(clientComp)
+          app.use(runtimeVapor.vaporInteropPlugin)
+          app.mount(container)
+
+          await new Promise(r => setTimeout(r, 20))
+          await nextTick()
+
+          expect(`Hydration node mismatch`).not.toHaveBeenWarned()
+          expect(`Hydration text mismatch`).not.toHaveBeenWarned()
+          expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(`
+          	"<div>
+          	<!--[--><span>foo</span><span>bar</span><!--]-->
+          	<i>tail</i></div>"
+          `)
+        })
+
         test('hydrate VDOM Suspense vapor async setup should not enter mount hooks twice', async () => {
           const beforeMount = vi.fn()
           const data = ref({ beforeMount })