import { h, Component, render, nodeOps, NodeTypes, TestElement, Fragment, observable, serialize, ChildrenFlags, nextTick, resetOps, dumpOps, NodeOpTypes, createFragment, createTextVNode } from '@vue/runtime-test' describe('Fragments', () => { it('should allow returning multiple component root nodes', async () => { class App extends Component { render() { return [h('div', 'one'), 'two'] } } const root = nodeOps.createElement('div') await render(h(App), root) expect(serialize(root)).toBe(`
one
two
`) expect(root.children.length).toBe(2) expect(root.children[0]).toMatchObject({ type: NodeTypes.ELEMENT, tag: 'div' }) expect((root.children[0] as TestElement).children[0]).toMatchObject({ type: NodeTypes.TEXT, text: 'one' }) expect(root.children[1]).toMatchObject({ type: NodeTypes.TEXT, text: 'two' }) }) it('should be able to explicitly create fragments', async () => { class App extends Component { render() { return h('div', [h(Fragment, [h('div', 'one'), 'two'])]) } } const root = nodeOps.createElement('div') await render(h(App), root) const parent = root.children[0] as TestElement expect(serialize(parent)).toBe(`
one
two
`) }) it('should be able to patch fragment children (unkeyed)', async () => { const state = observable({ ok: true }) class App extends Component { render() { return state.ok ? createFragment( [h('div', 'one'), createTextVNode('two')], ChildrenFlags.NONE_KEYED_VNODES ) : createFragment( [h('div', 'foo'), createTextVNode('bar'), createTextVNode('baz')], ChildrenFlags.NONE_KEYED_VNODES ) } } const root = nodeOps.createElement('div') await render(h(App), root) expect(serialize(root)).toBe(`
one
two
`) state.ok = false await nextTick() expect(serialize(root)).toBe(`
foo
barbaz
`) }) it('should be able to patch fragment children (implicitly keyed)', async () => { const state = observable({ ok: true }) class App extends Component { render() { return state.ok ? [h('div', 'one'), 'two'] : [h('pre', 'foo'), 'bar', 'baz'] } } const root = nodeOps.createElement('div') await await render(h(App), root) expect(serialize(root)).toBe(`
one
two
`) state.ok = false await nextTick() expect(serialize(root)).toBe(`
foo
barbaz
`) }) it('should be able to patch fragment children (explcitly keyed)', async () => { const state = observable({ ok: true }) class App extends Component { render() { return state.ok ? [h('div', { key: 1 }, 'one'), h('div', { key: 2 }, 'two')] : [h('div', { key: 2 }, 'two'), h('div', { key: 1 }, 'one')] } } const root = nodeOps.createElement('div') await render(h(App), root) expect(serialize(root)).toBe(`
one
two
`) resetOps() state.ok = false await nextTick() expect(serialize(root)).toBe(`
two
one
`) const ops = dumpOps() // should be moving nodes instead of re-creating them expect(ops.some(op => op.type === NodeOpTypes.CREATE)).toBe(false) }) it('should be able to move fragment', async () => { const state = observable({ ok: true }) class App extends Component { render() { return state.ok ? h('div', [ h('div', { key: 1 }, 'outer'), h(Fragment, { key: 2 }, [ h('div', { key: 1 }, 'one'), h('div', { key: 2 }, 'two') ]) ]) : h('div', [ h(Fragment, { key: 2 }, [ h('div', { key: 2 }, 'two'), h('div', { key: 1 }, 'one') ]), h('div', { key: 1 }, 'outer') ]) } } const root = nodeOps.createElement('div') await render(h(App), root) const parent = root.children[0] as TestElement expect(serialize(parent)).toBe( `
outer
one
two
` ) resetOps() state.ok = false await nextTick() expect(serialize(parent)).toBe( `
two
one
outer
` ) const ops = dumpOps() // should be moving nodes instead of re-creating them expect(ops.some(op => op.type === NodeOpTypes.CREATE)).toBe(false) }) it('should be able to handle nested fragments', async () => { const state = observable({ ok: true }) class App extends Component { render() { return state.ok ? [ h('div', { key: 1 }, 'outer'), h(Fragment, { key: 2 }, [ h('div', { key: 1 }, 'one'), h('div', { key: 2 }, 'two') ]) ] : [ h(Fragment, { key: 2 }, [ h('div', { key: 2 }, 'two'), h('div', { key: 1 }, 'one') ]), h('div', { key: 1 }, 'outer') ] } } const root = nodeOps.createElement('div') await render(h(App), root) expect(serialize(root)).toBe( `
outer
one
two
` ) resetOps() state.ok = false await nextTick() expect(serialize(root)).toBe( `
two
one
outer
` ) const ops = dumpOps() // should be moving nodes instead of re-creating them expect(ops.some(op => op.type === NodeOpTypes.CREATE)).toBe(false) }) })