]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
test(runtime-core): add tests for the optimized mode (#1884)
authorHcySunYang <HcySunYang@outlook.com>
Thu, 20 Aug 2020 14:56:31 +0000 (22:56 +0800)
committerGitHub <noreply@github.com>
Thu, 20 Aug 2020 14:56:31 +0000 (10:56 -0400)
packages/runtime-core/__tests__/rendererOptimizedMode.spec.ts

index b9dd2c91af048462ca84a5bec87761c8a93baa8b..26fee92f44d0f286f1f6773e4433e5cdd2c46997 100644 (file)
@@ -1,3 +1,401 @@
+import {
+  h,
+  Fragment,
+  createVNode,
+  openBlock,
+  createBlock,
+  render,
+  nodeOps,
+  TestElement,
+  serialize,
+  serializeInner as inner,
+  VNode,
+  ref,
+  nextTick
+} from '@vue/runtime-test'
+import { PatchFlags } from '@vue/shared'
+
 describe('renderer: optimized mode', () => {
-  test.todo('should work')
+  let root: TestElement
+  let block: VNode | null = null
+
+  beforeEach(() => {
+    root = nodeOps.createElement('div')
+    block = null
+  })
+
+  const renderWithBlock = (renderChildren: () => VNode[]) => {
+    render(
+      (openBlock(), (block = createBlock('div', null, renderChildren()))),
+      root
+    )
+  }
+
+  test('basic use of block', () => {
+    render((openBlock(), (block = createBlock('p', null, 'foo'))), root)
+
+    expect(block.dynamicChildren!.length).toBe(0)
+    expect(inner(root)).toBe('<p>foo</p>')
+  })
+
+  test('block can appear anywhere in the vdom tree', () => {
+    render(
+      h('div', (openBlock(), (block = createBlock('p', null, 'foo')))),
+      root
+    )
+
+    expect(block.dynamicChildren!.length).toBe(0)
+    expect(inner(root)).toBe('<div><p>foo</p></div>')
+  })
+
+  test('block should collect dynamic vnodes', () => {
+    renderWithBlock(() => [
+      createVNode('p', null, 'foo', PatchFlags.TEXT),
+      createVNode('i')
+    ])
+
+    expect(block!.dynamicChildren!.length).toBe(1)
+    expect(serialize(block!.dynamicChildren![0].el as TestElement)).toBe(
+      '<p>foo</p>'
+    )
+  })
+
+  test('block can disable tracking', () => {
+    render(
+      // disable tracking
+      (openBlock(true),
+      (block = createBlock('div', null, [
+        createVNode('p', null, 'foo', PatchFlags.TEXT)
+      ]))),
+      root
+    )
+
+    expect(block.dynamicChildren!.length).toBe(0)
+  })
+
+  test('block as dynamic children', () => {
+    renderWithBlock(() => [
+      (openBlock(), createBlock('div', { key: 0 }, [h('p')]))
+    ])
+
+    expect(block!.dynamicChildren!.length).toBe(1)
+    expect(block!.dynamicChildren![0].dynamicChildren!.length).toBe(0)
+    expect(serialize(block!.dynamicChildren![0].el as TestElement)).toBe(
+      '<div><p></p></div>'
+    )
+
+    renderWithBlock(() => [
+      (openBlock(), createBlock('div', { key: 1 }, [h('i')]))
+    ])
+
+    expect(block!.dynamicChildren!.length).toBe(1)
+    expect(block!.dynamicChildren![0].dynamicChildren!.length).toBe(0)
+    expect(serialize(block!.dynamicChildren![0].el as TestElement)).toBe(
+      '<div><i></i></div>'
+    )
+  })
+
+  test('PatchFlags: PatchFlags.TEXT', async () => {
+    renderWithBlock(() => [createVNode('p', null, 'foo', PatchFlags.TEXT)])
+
+    expect(inner(root)).toBe('<div><p>foo</p></div>')
+    expect(block!.dynamicChildren!.length).toBe(1)
+    expect(serialize(block!.dynamicChildren![0].el as TestElement)).toBe(
+      '<p>foo</p>'
+    )
+
+    renderWithBlock(() => [createVNode('p', null, 'bar', PatchFlags.TEXT)])
+
+    expect(inner(root)).toBe('<div><p>bar</p></div>')
+    expect(block!.dynamicChildren!.length).toBe(1)
+    expect(serialize(block!.dynamicChildren![0].el as TestElement)).toBe(
+      '<p>bar</p>'
+    )
+  })
+
+  test('PatchFlags: PatchFlags.CLASS', async () => {
+    renderWithBlock(() => [
+      createVNode('p', { class: 'foo' }, '', PatchFlags.CLASS)
+    ])
+
+    expect(inner(root)).toBe('<div><p class="foo"></p></div>')
+    expect(block!.dynamicChildren!.length).toBe(1)
+    expect(serialize(block!.dynamicChildren![0].el as TestElement)).toBe(
+      '<p class="foo"></p>'
+    )
+
+    renderWithBlock(() => [
+      createVNode('p', { class: 'bar' }, '', PatchFlags.CLASS)
+    ])
+
+    expect(inner(root)).toBe('<div><p class="bar"></p></div>')
+    expect(block!.dynamicChildren!.length).toBe(1)
+    expect(serialize(block!.dynamicChildren![0].el as TestElement)).toBe(
+      '<p class="bar"></p>'
+    )
+  })
+
+  test('PatchFlags: PatchFlags.STYLE', async () => {
+    renderWithBlock(() => [
+      createVNode('p', { style: 'color: red' }, '', PatchFlags.STYLE)
+    ])
+
+    expect(inner(root)).toBe('<div><p style="color: red"></p></div>')
+    expect(block!.dynamicChildren!.length).toBe(1)
+    expect(serialize(block!.dynamicChildren![0].el as TestElement)).toBe(
+      '<p style="color: red"></p>'
+    )
+
+    renderWithBlock(() => [
+      createVNode('p', { style: 'color: green' }, '', PatchFlags.STYLE)
+    ])
+
+    expect(inner(root)).toBe('<div><p style="color: green"></p></div>')
+    expect(block!.dynamicChildren!.length).toBe(1)
+    expect(serialize(block!.dynamicChildren![0].el as TestElement)).toBe(
+      '<p style="color: green"></p>'
+    )
+  })
+
+  test('PatchFlags: PatchFlags.PROPS', async () => {
+    renderWithBlock(() => [
+      createVNode('p', { id: 'foo' }, '', PatchFlags.PROPS, ['id'])
+    ])
+
+    expect(inner(root)).toBe('<div><p id="foo"></p></div>')
+    expect(block!.dynamicChildren!.length).toBe(1)
+    expect(serialize(block!.dynamicChildren![0].el as TestElement)).toBe(
+      '<p id="foo"></p>'
+    )
+
+    renderWithBlock(() => [
+      createVNode('p', { id: 'bar' }, '', PatchFlags.PROPS, ['id'])
+    ])
+
+    expect(inner(root)).toBe('<div><p id="bar"></p></div>')
+    expect(block!.dynamicChildren!.length).toBe(1)
+    expect(serialize(block!.dynamicChildren![0].el as TestElement)).toBe(
+      '<p id="bar"></p>'
+    )
+  })
+
+  test('PatchFlags: PatchFlags.FULL_PROPS', async () => {
+    let propName = 'foo'
+
+    renderWithBlock(() => [
+      createVNode('p', { [propName]: 'dynamic' }, '', PatchFlags.FULL_PROPS)
+    ])
+
+    expect(inner(root)).toBe('<div><p foo="dynamic"></p></div>')
+    expect(block!.dynamicChildren!.length).toBe(1)
+    expect(serialize(block!.dynamicChildren![0].el as TestElement)).toBe(
+      '<p foo="dynamic"></p>'
+    )
+
+    propName = 'bar'
+    renderWithBlock(() => [
+      createVNode('p', { [propName]: 'dynamic' }, '', PatchFlags.FULL_PROPS)
+    ])
+
+    expect(inner(root)).toBe('<div><p bar="dynamic"></p></div>')
+    expect(block!.dynamicChildren!.length).toBe(1)
+    expect(serialize(block!.dynamicChildren![0].el as TestElement)).toBe(
+      '<p bar="dynamic"></p>'
+    )
+  })
+
+  // the order and length of the list will not change
+  test('PatchFlags: PatchFlags.STABLE_FRAGMENT', async () => {
+    let list = ['foo', 'bar']
+    render(
+      (openBlock(),
+      (block = createBlock(
+        Fragment,
+        null,
+        list.map(item => {
+          return createVNode('p', null, item, PatchFlags.TEXT)
+        }),
+        PatchFlags.STABLE_FRAGMENT
+      ))),
+      root
+    )
+
+    expect(inner(root)).toBe('<p>foo</p><p>bar</p>')
+    expect(block.dynamicChildren!.length).toBe(2)
+    expect(serialize(block.dynamicChildren![0].el as TestElement)).toBe(
+      '<p>foo</p>'
+    )
+    expect(serialize(block.dynamicChildren![1].el as TestElement)).toBe(
+      '<p>bar</p>'
+    )
+
+    list = list.map(item => item.repeat(2))
+    render(
+      (openBlock(),
+      createBlock(
+        Fragment,
+        null,
+        list.map(item => {
+          return createVNode('p', null, item, PatchFlags.TEXT)
+        }),
+        PatchFlags.STABLE_FRAGMENT
+      )),
+      root
+    )
+
+    expect(inner(root)).toBe('<p>foofoo</p><p>barbar</p>')
+    expect(block.dynamicChildren!.length).toBe(2)
+    expect(serialize(block.dynamicChildren![0].el as TestElement)).toBe(
+      '<p>foofoo</p>'
+    )
+    expect(serialize(block.dynamicChildren![1].el as TestElement)).toBe(
+      '<p>barbar</p>'
+    )
+  })
+
+  // A Fragment with `UNKEYED_FRAGMENT` flag will always patch its children,
+  // so there's no need for tracking dynamicChildren.
+  test('PatchFlags: PatchFlags.UNKEYED_FRAGMENT', async () => {
+    const list = [{ tag: 'p', text: 'foo' }]
+    render(
+      (openBlock(true),
+      (block = createBlock(
+        Fragment,
+        null,
+        list.map(item => {
+          return createVNode(item.tag, null, item.text)
+        }),
+        PatchFlags.UNKEYED_FRAGMENT
+      ))),
+      root
+    )
+
+    expect(inner(root)).toBe('<p>foo</p>')
+    expect(block.dynamicChildren!.length).toBe(0)
+
+    list.unshift({ tag: 'i', text: 'bar' })
+    render(
+      (openBlock(true),
+      createBlock(
+        Fragment,
+        null,
+        list.map(item => {
+          return createVNode(item.tag, null, item.text)
+        }),
+        PatchFlags.UNKEYED_FRAGMENT
+      )),
+      root
+    )
+
+    expect(inner(root)).toBe('<i>bar</i><p>foo</p>')
+    expect(block.dynamicChildren!.length).toBe(0)
+  })
+
+  // A Fragment with `KEYED_FRAGMENT` will always patch its children,
+  // so there's no need for tracking dynamicChildren.
+  test('PatchFlags: PatchFlags.KEYED_FRAGMENT', async () => {
+    const list = [{ tag: 'p', text: 'foo' }]
+    render(
+      (openBlock(true),
+      (block = createBlock(
+        Fragment,
+        null,
+        list.map(item => {
+          return createVNode(item.tag, { key: item.tag }, item.text)
+        }),
+        PatchFlags.KEYED_FRAGMENT
+      ))),
+      root
+    )
+
+    expect(inner(root)).toBe('<p>foo</p>')
+    expect(block.dynamicChildren!.length).toBe(0)
+
+    list.unshift({ tag: 'i', text: 'bar' })
+    render(
+      (openBlock(true),
+      createBlock(
+        Fragment,
+        null,
+        list.map(item => {
+          return createVNode(item.tag, { key: item.tag }, item.text)
+        }),
+        PatchFlags.KEYED_FRAGMENT
+      )),
+      root
+    )
+
+    expect(inner(root)).toBe('<i>bar</i><p>foo</p>')
+    expect(block.dynamicChildren!.length).toBe(0)
+  })
+
+  test('PatchFlags: PatchFlags.NEED_PATCH', async () => {
+    const spyMounted = jest.fn()
+    const spyUpdated = jest.fn()
+    const count = ref(0)
+    const Comp = {
+      setup() {
+        return () => {
+          count.value
+          return (
+            openBlock(),
+            (block = createBlock('div', null, [
+              createVNode(
+                'p',
+                { onVnodeMounted: spyMounted, onVnodeBeforeUpdate: spyUpdated },
+                '',
+                PatchFlags.NEED_PATCH
+              )
+            ]))
+          )
+        }
+      }
+    }
+
+    render(h(Comp), root)
+
+    expect(inner(root)).toBe('<div><p></p></div>')
+    expect(block!.dynamicChildren!.length).toBe(1)
+    expect(serialize(block!.dynamicChildren![0].el as TestElement)).toBe(
+      '<p></p>'
+    )
+    expect(spyMounted).toHaveBeenCalledTimes(1)
+    expect(spyUpdated).toHaveBeenCalledTimes(0)
+
+    count.value++
+    await nextTick()
+
+    expect(inner(root)).toBe('<div><p></p></div>')
+    expect(block!.dynamicChildren!.length).toBe(1)
+    expect(serialize(block!.dynamicChildren![0].el as TestElement)).toBe(
+      '<p></p>'
+    )
+    expect(spyMounted).toHaveBeenCalledTimes(1)
+    expect(spyUpdated).toHaveBeenCalledTimes(1)
+  })
+
+  test('PatchFlags: PatchFlags.BAIL', async () => {
+    render(
+      (openBlock(),
+      (block = createBlock('div', null, [createVNode('p', null, 'foo')]))),
+      root
+    )
+
+    expect(inner(root)).toBe('<div><p>foo</p></div>')
+    expect(block!.dynamicChildren!.length).toBe(0)
+
+    render(
+      (openBlock(),
+      (block = createBlock(
+        'div',
+        null,
+        [createVNode('i', null, 'bar')],
+        PatchFlags.BAIL
+      ))),
+      root
+    )
+
+    expect(inner(root)).toBe('<div><i>bar</i></div>')
+    expect(block!.dynamicChildren).toBe(null)
+  })
 })