Thunberg087 / vue-fragment

a very candide fragment component for Vue.js
http://jsfiddle.net/cdkn5wL3/
670 stars 51 forks source link

Jest Snapshots - Tutorial #45

Open TheJaredWilcurt opened 4 years ago

TheJaredWilcurt commented 4 years ago

Vue-Fragment is a great bridge between Vue 2 and 3, however it took a lot of effort to get it to work with snapshot testing. So I'm posting this here for anyone else that needs these steps.

Here is my simple component:

<template functional>
  <fragment>
    <strong>{{ props.items && props.items.length || '0' }}</strong>
    {{ props.label }}<template v-if="!props.items || props.items.length !== 1">s</template>
  </fragment>
</template>

<script>
export default {
  name: 'NItems',
  props: {
    items: {
      type: Array,
      default: undefined
    },
    label: {
      type: String,
      required: true
    }
  }
};
</script>

Here is my test file:

import { mount, createLocalVue } from '@vue/test-utils';
import { Fragment } from 'vue-fragment';
import NItems from '@/components/NItems.vue';

describe('NItems.vue', () => {
  let localVue;

  beforeEach(() => {
    jest.resetModules();
    jest.doMock('../../../vue.config.js', function () {
      return {
        pluginOptions: {
          jestSerializer: {
            removeComments: true,
            attributesToClear: ['fragment']
          }
        }
      };
    });

    localVue = createLocalVue();
  });

  const wrapperOptions = (items, label) => {
    return {
      name: 'Test', // needed to prevent recursion error message
      localVue,
      // import in your component and vue-fragment
      components: { NItems, Fragment },
      // Vue-Fragment needs a parent element to attach to, so you must wrap your component in a div   
      template: '<div><n-items :label="label" :items="items" /></div>',
      // Set your props as arguments for the wrapperOptions and pass them in to the parent <test> component's data
      data: function () {
        return { items, label };
      }
    };
  };

  test('Default props', async () => {
    const wrapper = mount(wrapperOptions(undefined, 'Item'));

    expect(wrapper)
      .toMatchSnapshot();
  });
});

This example includes a doMock. This is specifically for a Jest snapshot plugin that is required for this to work.

This Jest plugin must be installed for Vue-Fragment snapshots to work

That plugin will let you apply per-test settings for tweaking your snapshots. It has many improvements over the default snapshot serializer that comes with Vue-CLI.

Without the Jest-Serializer-Vue-TJW plugin (and those settings) the snapshots will look like this:

<div>
  <!--fragment#ba77906b55#head-->
  <strong fragment="ba77906b55">0</strong>
  Items
  <!--fragment#ba77906b55#tail-->
</div>

instead of this:

<div>
  <strong fragment>0</strong>
  Items
</div>

where the fragment ID (ba77906b55) is randomized on every test run (thus breaking the snapshots).

kamaladenalhomsi commented 3 years ago

Thanks for this tutorial @TheJaredWilcurt

samueleiche commented 1 year ago

Thanks for the idea. You could also just replace the comments and attributes with regex. I decided for this instead of adding a library to do it.

describe('Table', () => {
    it('renders table', () => {
        const wrapper = mount(Component)

        expect(getRenderedHTML(wrapper)).toMatchSnapshot()
    })

    function getRenderedHTML(wrapper) {
        const html = wrapper.html()

        // remove vue-fragment dynamic comments and attributes
        return html.replace(/<!--fragment(.*)-->/g, '').replace(/fragment="(.*?)"/g, '')
    }
})