Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you unit test a Vue.js functional component with a render function that returns any array of elements?

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?

like image 433
Steven Lambert Avatar asked Jan 17 '19 15:01

Steven Lambert


People also ask

For what purpose is the Render () function used in Vue?

A render function returns a virtual DOM node, commonly named VNode in the Vue ecosystem, which is an interface that allows Vue to write these objects in your browser DOM. They contain all the information necessary to work with Vue.

What is createLocalVue?

createLocalVue returns a Vue class for you to add components, mixins and install plugins without polluting the global Vue class. The errorHandler option can be used to handle uncaught errors during component render function and watchers.

Should you use JSX with Vue?

js. Love it or hate it, JSX is a popular extension to JavaScript that allows XML tokens in your scripts. If you want to create templates in your script files and you find Vue's render() function to be difficult to work with, JSX may be just what you need.


2 Answers

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'
    }
  }
});
like image 97
Husam Ibrahim Avatar answered Oct 20 '22 01:10

Husam Ibrahim


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>')
  });
});
like image 39
Munim Munna Avatar answered Oct 19 '22 23:10

Munim Munna