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', () => { 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('
foo
') }) 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('foo
foo
' ) }) 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( 'foo
foo
' ) renderWithBlock(() => [createVNode('p', null, 'bar', PatchFlags.TEXT)]) expect(inner(root)).toBe('bar
bar
' ) }) test('PatchFlags: PatchFlags.CLASS', async () => { renderWithBlock(() => [ createVNode('p', { class: 'foo' }, '', PatchFlags.CLASS) ]) expect(inner(root)).toBe('foo
bar
') expect(block.dynamicChildren!.length).toBe(2) expect(serialize(block.dynamicChildren![0].el as TestElement)).toBe( 'foo
' ) expect(serialize(block.dynamicChildren![1].el as TestElement)).toBe( 'bar
' ) 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('foofoo
barbar
') expect(block.dynamicChildren!.length).toBe(2) expect(serialize(block.dynamicChildren![0].el as TestElement)).toBe( 'foofoo
' ) expect(serialize(block.dynamicChildren![1].el as TestElement)).toBe( 'barbar
' ) }) // 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('foo
') 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('barfoo
') 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('foo
') 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('barfoo
') 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('foo