From 255a2bd17889ba4b9f9d30006d4c23847dbacbe1 Mon Sep 17 00:00:00 2001 From: HcySunYang Date: Thu, 20 Aug 2020 22:56:31 +0800 Subject: [PATCH] test(runtime-core): add tests for the optimized mode (#1884) --- .../__tests__/rendererOptimizedMode.spec.ts | 400 +++++++++++++++++- 1 file changed, 399 insertions(+), 1 deletion(-) diff --git a/packages/runtime-core/__tests__/rendererOptimizedMode.spec.ts b/packages/runtime-core/__tests__/rendererOptimizedMode.spec.ts index b9dd2c91..26fee92f 100644 --- a/packages/runtime-core/__tests__/rendererOptimizedMode.spec.ts +++ b/packages/runtime-core/__tests__/rendererOptimizedMode.spec.ts @@ -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('

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

') + }) + + 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( + '

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( + '

' + ) + + 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( + '
' + ) + }) + + test('PatchFlags: PatchFlags.TEXT', async () => { + renderWithBlock(() => [createVNode('p', null, 'foo', PatchFlags.TEXT)]) + + expect(inner(root)).toBe('

foo

') + expect(block!.dynamicChildren!.length).toBe(1) + expect(serialize(block!.dynamicChildren![0].el as TestElement)).toBe( + '

foo

' + ) + + renderWithBlock(() => [createVNode('p', null, 'bar', PatchFlags.TEXT)]) + + expect(inner(root)).toBe('

bar

') + expect(block!.dynamicChildren!.length).toBe(1) + expect(serialize(block!.dynamicChildren![0].el as TestElement)).toBe( + '

bar

' + ) + }) + + test('PatchFlags: PatchFlags.CLASS', async () => { + renderWithBlock(() => [ + createVNode('p', { class: 'foo' }, '', PatchFlags.CLASS) + ]) + + expect(inner(root)).toBe('

') + expect(block!.dynamicChildren!.length).toBe(1) + expect(serialize(block!.dynamicChildren![0].el as TestElement)).toBe( + '

' + ) + + renderWithBlock(() => [ + createVNode('p', { class: 'bar' }, '', PatchFlags.CLASS) + ]) + + expect(inner(root)).toBe('

') + expect(block!.dynamicChildren!.length).toBe(1) + expect(serialize(block!.dynamicChildren![0].el as TestElement)).toBe( + '

' + ) + }) + + test('PatchFlags: PatchFlags.STYLE', async () => { + renderWithBlock(() => [ + createVNode('p', { style: 'color: red' }, '', PatchFlags.STYLE) + ]) + + expect(inner(root)).toBe('

') + expect(block!.dynamicChildren!.length).toBe(1) + expect(serialize(block!.dynamicChildren![0].el as TestElement)).toBe( + '

' + ) + + renderWithBlock(() => [ + createVNode('p', { style: 'color: green' }, '', PatchFlags.STYLE) + ]) + + expect(inner(root)).toBe('

') + expect(block!.dynamicChildren!.length).toBe(1) + expect(serialize(block!.dynamicChildren![0].el as TestElement)).toBe( + '

' + ) + }) + + test('PatchFlags: PatchFlags.PROPS', async () => { + renderWithBlock(() => [ + createVNode('p', { id: 'foo' }, '', PatchFlags.PROPS, ['id']) + ]) + + expect(inner(root)).toBe('

') + expect(block!.dynamicChildren!.length).toBe(1) + expect(serialize(block!.dynamicChildren![0].el as TestElement)).toBe( + '

' + ) + + renderWithBlock(() => [ + createVNode('p', { id: 'bar' }, '', PatchFlags.PROPS, ['id']) + ]) + + expect(inner(root)).toBe('

') + expect(block!.dynamicChildren!.length).toBe(1) + expect(serialize(block!.dynamicChildren![0].el as TestElement)).toBe( + '

' + ) + }) + + test('PatchFlags: PatchFlags.FULL_PROPS', async () => { + let propName = 'foo' + + renderWithBlock(() => [ + createVNode('p', { [propName]: 'dynamic' }, '', PatchFlags.FULL_PROPS) + ]) + + expect(inner(root)).toBe('

') + expect(block!.dynamicChildren!.length).toBe(1) + expect(serialize(block!.dynamicChildren![0].el as TestElement)).toBe( + '

' + ) + + propName = 'bar' + renderWithBlock(() => [ + createVNode('p', { [propName]: 'dynamic' }, '', PatchFlags.FULL_PROPS) + ]) + + expect(inner(root)).toBe('

') + expect(block!.dynamicChildren!.length).toBe(1) + expect(serialize(block!.dynamicChildren![0].el as TestElement)).toBe( + '

' + ) + }) + + // 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('

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('bar

foo

') + 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('bar

foo

') + 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('

') + expect(block!.dynamicChildren!.length).toBe(1) + expect(serialize(block!.dynamicChildren![0].el as TestElement)).toBe( + '

' + ) + expect(spyMounted).toHaveBeenCalledTimes(1) + expect(spyUpdated).toHaveBeenCalledTimes(0) + + count.value++ + await nextTick() + + expect(inner(root)).toBe('

') + expect(block!.dynamicChildren!.length).toBe(1) + expect(serialize(block!.dynamicChildren![0].el as TestElement)).toBe( + '

' + ) + 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('

foo

') + expect(block!.dynamicChildren!.length).toBe(0) + + render( + (openBlock(), + (block = createBlock( + 'div', + null, + [createVNode('i', null, 'bar')], + PatchFlags.BAIL + ))), + root + ) + + expect(inner(root)).toBe('
bar
') + expect(block!.dynamicChildren).toBe(null) + }) })