5

In Vue.js, a functional component can return multiple root nodes by using a render function that returns an array of createdElements.

export default {
  functional: true,
  props: ['cellData'],
  render: function (h, context) {
    return [
      h('td', context.props.cellData.category),
      h('td', context.props.cellData.description)
    ]
  }
}

This works great but I'm having trouble trying to create a unit test for such a component. Using shallowMount on the component results in [Vue warn]: Multiple root nodes returned from render function. Render function should return a single root node.

import { shallowMount } from '@vue/test-utils'
import Cell from '@/components/Cell'

wrapper = shallowMount(Cell, {
  context: {
    props: {
      cellData {
        category: 'foo',
        description: 'bar'
      }
    }
  }
});

This github issue suggests that the component needs to be wrapped in a single root node to actually render it, but trying that results in [vue-test-utils]: mount.context can only be used when mounting a functional component

import { shallowMount } from '@vue/test-utils'
import Cell from '@/components/Cell'

wrapper = shallowMount('<div><Cell></div>', {
  context: {
    props: {
      cellData {
        category: 'foo',
        description: 'bar'
      }
    }
  }
});

So how do I test a functional component that returns multiple root nodes?

2
  • what build tools are you using ? Commented Jan 20, 2019 at 11:25
  • just for the rest people, who are wondering if it's possible to return multiple VNode[] from a render function - it's not possible, render function's signature is allowing only a SINGLE VNode, not multiple. Commented Jun 22, 2020 at 6:10

2 Answers 2

10
+200

You could create a higher order, transparent wrapper component that passes all props and event listeners to the inner Cell component using v-bind="$attrs"[1] and v-on="$listeners"[2]. Then you can use propsData to pass props to the wrapper component ..

import { mount } from '@vue/test-utils'
import Cell from '@/components/Cell'

const WrappedCell = {
  components: { Cell },
  template: `
    <div>
      <Cell v-bind="$attrs" v-on="$listeners" />
    </div>
  `
}

const wrapper = mount(WrappedCell, {
  propsData: {
    cellData: {
      category: 'foo',
      description: 'bar'
    }
  }
});
Sign up to request clarification or add additional context in comments.

1 Comment

How do I test conditions based off a prop?
6

You can create a fragment_wrapper for wrapping your Components with Fragments (multiple root elements).

//File: fragment_wrapper.js

exports.fragment_wrapper = function(FragmentComponent){
  const wrapper = {
    components: { FragmentComponent },
    props: FragmentComponent.props,
    template: `<div><FragmentComponent v-bind="$props" v-on="$listeners"/></div>`
  }
  return wrapper;  
}

Then you can use this to test all your Fragmented Components as follows:

import { mount } from '@vue/test-utils'
import { fragment_wrapper } from './fragment_wrapper'
import Cell from './components/Cell'


describe('Test Cell', () => {
  let WrappedCell = fragment_wrapper(Cell);
  const wrapper = mount(WrappedCell, {
    propsData: {
      cellData: {
        category: 'foo',
        description: 'bar'
      }
    }
  });

  it('renders the correct markup', () => {
    expect(wrapper.html()).toContain('<td>foo</td>')
  });
});

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.