]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
wip: add tests + skip fragment end anchor
authordaiwei <daiwei521@126.com>
Thu, 24 Apr 2025 01:26:33 +0000 (09:26 +0800)
committerdaiwei <daiwei521@126.com>
Thu, 24 Apr 2025 01:59:00 +0000 (09:59 +0800)
packages/runtime-vapor/__tests__/hydration.spec.ts
packages/runtime-vapor/src/dom/node.ts

index 67f5c634bb4b48994486f519b3651666171d2c25..ecb8d8202a4127471edfdfc8ad6e58423d91005a 100644 (file)
@@ -264,6 +264,48 @@ describe('Vapor Mode hydration', () => {
     )
   })
 
+  test('nested components with anchor insertion', async () => {
+    const { container, data } = await testHydration(
+      `
+      <template><components.Parent/></template>
+      `,
+      {
+        Parent: `<template><div><span/><components.Child/><span/></div></template>`,
+        Child: `<template><div>{{ data }}</div></template>`,
+      },
+    )
+    expect(container.innerHTML).toMatchInlineSnapshot(
+      `"<div><span></span><div>foo</div><span></span></div>"`,
+    )
+
+    data.value = 'bar'
+    await nextTick()
+    expect(container.innerHTML).toMatchInlineSnapshot(
+      `"<div><span></span><div>bar</div><span></span></div>"`,
+    )
+  })
+
+  test('nested components with multi level anchor insertion', async () => {
+    const { container, data } = await testHydration(
+      `
+      <template><div><span></span><components.Parent/><span></span></div></template>
+      `,
+      {
+        Parent: `<template><div><span/><components.Child/><span/></div></template>`,
+        Child: `<template><div>{{ data }}</div></template>`,
+      },
+    )
+    expect(container.innerHTML).toMatchInlineSnapshot(
+      `"<div><span></span><div><span></span><div>foo</div><span></span></div><span></span></div>"`,
+    )
+
+    data.value = 'bar'
+    await nextTick()
+    expect(container.innerHTML).toMatchInlineSnapshot(
+      `"<div><span></span><div><span></span><div>bar</div><span></span></div><span></span></div>"`,
+    )
+  })
+
   test('consecutive components with anchor insertion', async () => {
     const { container, data } = await testHydration(
       `<template>
@@ -290,6 +332,48 @@ describe('Vapor Mode hydration', () => {
     )
   })
 
+  test('nested consecutive components with anchor insertion', async () => {
+    const { container, data } = await testHydration(
+      `
+      <template><components.Parent/></template>
+      `,
+      {
+        Parent: `<template><div><span/><components.Child/><components.Child/><span/></div></template>`,
+        Child: `<template><div>{{ data }}</div></template>`,
+      },
+    )
+    expect(container.innerHTML).toMatchInlineSnapshot(
+      `"<div><span></span><div>foo</div><!--[[--><div>foo</div><!--]]--><span></span></div>"`,
+    )
+
+    data.value = 'bar'
+    await nextTick()
+    expect(container.innerHTML).toMatchInlineSnapshot(
+      `"<div><span></span><div>bar</div><!--[[--><div>bar</div><!--]]--><span></span></div>"`,
+    )
+  })
+
+  test('nested consecutive components with multi level anchor insertion', async () => {
+    const { container, data } = await testHydration(
+      `
+      <template><div><span></span><components.Parent/><span></span></div></template>
+      `,
+      {
+        Parent: `<template><div><span/><components.Child/><components.Child/><span/></div></template>`,
+        Child: `<template><div>{{ data }}</div></template>`,
+      },
+    )
+    expect(container.innerHTML).toMatchInlineSnapshot(
+      `"<div><span></span><div><span></span><div>foo</div><!--[[--><div>foo</div><!--]]--><span></span></div><span></span></div>"`,
+    )
+
+    data.value = 'bar'
+    await nextTick()
+    expect(container.innerHTML).toMatchInlineSnapshot(
+      `"<div><span></span><div><span></span><div>bar</div><!--[[--><div>bar</div><!--]]--><span></span></div><span></span></div>"`,
+    )
+  })
+
   test('mixed component and element with anchor insertion', async () => {
     const { container, data } = await testHydration(
       `<template>
@@ -369,6 +453,48 @@ describe('Vapor Mode hydration', () => {
     )
   })
 
+  test('nested fragment component with anchor insertion', async () => {
+    const { container, data } = await testHydration(
+      `
+      <template><components.Parent/></template>
+      `,
+      {
+        Parent: `<template><div><span/><components.Child/><span/></div></template>`,
+        Child: `<template><div>{{ data }}</div>-{{ data }}-</template>`,
+      },
+    )
+    expect(container.innerHTML).toMatchInlineSnapshot(
+      `"<div><span></span><!--[--><div>foo</div>-foo-<!--]--><span></span></div>"`,
+    )
+
+    data.value = 'bar'
+    await nextTick()
+    expect(container.innerHTML).toMatchInlineSnapshot(
+      `"<div><span></span><!--[--><div>bar</div>-bar-<!--]--><span></span></div>"`,
+    )
+  })
+
+  test('nested fragment component with multi level anchor insertion', async () => {
+    const { container, data } = await testHydration(
+      `
+      <template><div><span/><components.Parent/><span/></div></template>
+      `,
+      {
+        Parent: `<template><div><span/><components.Child/><span/></div></template>`,
+        Child: `<template><div>{{ data }}</div>-{{ data }}-</template>`,
+      },
+    )
+    expect(container.innerHTML).toMatchInlineSnapshot(
+      `"<div><span></span><div><span></span><!--[--><div>foo</div>-foo-<!--]--><span></span></div><span></span></div>"`,
+    )
+
+    data.value = 'bar'
+    await nextTick()
+    expect(container.innerHTML).toMatchInlineSnapshot(
+      `"<div><span></span><div><span></span><!--[--><div>bar</div>-bar-<!--]--><span></span></div><span></span></div>"`,
+    )
+  })
+
   test('consecutive fragment components with anchor insertion', async () => {
     const { container, data } = await testHydration(
       `<template>
@@ -395,6 +521,69 @@ describe('Vapor Mode hydration', () => {
     )
   })
 
+  test('nested consecutive fragment components with anchor insertion', async () => {
+    const { container, data } = await testHydration(
+      `
+      <template><components.Parent/></template>
+      `,
+      {
+        Parent: `<template><div><span/><components.Child/><components.Child/><span/></div></template>`,
+        Child: `<template><div>{{ data }}</div>-{{ data }}-</template>`,
+      },
+    )
+    expect(container.innerHTML).toMatchInlineSnapshot(
+      `"<div><span></span><!--[--><div>foo</div>-foo-<!--]--><!--[[--><!--[--><div>foo</div>-foo-<!--]--><!--]]--><span></span></div>"`,
+    )
+
+    data.value = 'bar'
+    await nextTick()
+    expect(container.innerHTML).toMatchInlineSnapshot(
+      `"<div><span></span><!--[--><div>bar</div>-bar-<!--]--><!--[[--><!--[--><div>bar</div>-bar-<!--]--><!--]]--><span></span></div>"`,
+    )
+  })
+
+  test('nested consecutive fragment components with multi level anchor insertion', async () => {
+    const { container, data } = await testHydration(
+      `
+      <template><div><span></span><components.Parent/><span></span></div></template>
+      `,
+      {
+        Parent: `<template><div><span/><components.Child/><components.Child/><span/></div></template>`,
+        Child: `<template><div>{{ data }}</div>-{{ data }}-</template>`,
+      },
+    )
+    expect(container.innerHTML).toMatchInlineSnapshot(
+      `"<div><span></span><div><span></span><!--[--><div>foo</div>-foo-<!--]--><!--[[--><!--[--><div>foo</div>-foo-<!--]--><!--]]--><span></span></div><span></span></div>"`,
+    )
+
+    data.value = 'bar'
+    await nextTick()
+    expect(container.innerHTML).toMatchInlineSnapshot(
+      `"<div><span></span><div><span></span><!--[--><div>bar</div>-bar-<!--]--><!--[[--><!--[--><div>bar</div>-bar-<!--]--><!--]]--><span></span></div><span></span></div>"`,
+    )
+  })
+
+  test('nested consecutive fragment components with root level anchor insertion', async () => {
+    const { container, data } = await testHydration(
+      `
+      <template><div><span></span><components.Parent/><span></span></div></template>
+      `,
+      {
+        Parent: `<template><components.Child/><components.Child/></template>`,
+        Child: `<template><div>{{ data }}</div>-{{ data }}-</template>`,
+      },
+    )
+    expect(container.innerHTML).toMatchInlineSnapshot(
+      `"<div><span></span><!--[--><!--[--><div>foo</div>-foo-<!--]--><!--[--><div>foo</div>-foo-<!--]--><!--]--><span></span></div>"`,
+    )
+
+    data.value = 'bar'
+    await nextTick()
+    expect(container.innerHTML).toMatchInlineSnapshot(
+      `"<div><span></span><!--[--><!--[--><div>bar</div>-bar-<!--]--><!--[--><div>bar</div>-bar-<!--]--><!--]--><span></span></div>"`,
+    )
+  })
+
   test('mixed fragment component and element with anchor insertion', async () => {
     const { container, data } = await testHydration(
       `<template>
index 66f1a42f849006db5e76eb9cc1a71a6b0e7a2540..82971e876db345284e9827e132b17bdd4086f6e0 100644 (file)
@@ -53,8 +53,11 @@ function __next(node: Node): Node {
   }
 
   let n = node.nextSibling!
-  // skip dynamic anchors and empty text nodes
-  while (n && (isDynamicAnchor(n) || isEmptyText(n))) {
+  // skip if:
+  // - dynamic anchors (<!--[[-->, <!--]]-->)
+  // - fragment end anchor (`<!--]-->`)
+  // - empty text nodes
+  while (n && (isDynamicAnchor(n) || isComment(n, ']') || isEmptyText(n))) {
     n = n.nextSibling!
   }
   return n