TheKearnol
TheKearnol

Reputation: 1109

How to mock SVG's when snapshot testing with vitest and Storybook?

I am trying to run vitest snapshot tests on Storybook stories using the composeStories Fn from @storybook/testing-react, but I keep getting the error:

FAIL  src/components/common/Nav/Nav.test.tsx > Nav Component > it should match the snapshot
Error: Element type is invalid: expected a string (for built-in components) or a class/function
(for composite components) but got: undefined. You likely forgot to export your component from
the file it's defined in, or you might have mixed up default and named imports.

Check the render method of `Nav`.
//... stack trace

I believe it's related to the svg imports, as this only occurs in components that import svgs as react components via the SVGR library. i.e.

// components/common/Nav.tsx

import { ReactComponent as ECDLogo } from '@assets/ecd_logo.svg';

And my vite.config.ts uses the vite-svgr-plugin:

// vite.config.ts

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import svgr from 'vite-plugin-svgr';
import tsconfigPaths from 'vite-tsconfig-paths';
import path from 'path';

const tsConfigPathsOpts = {
  extensions: ['.svg', '.png', '.jpeg'],
  loose: true,
};

export default defineConfig({
  build: {
    outDir: 'build',
  },
  define: {
    global: {},
  },
  resolve: {
    alias: {
      '@': path.resolve(__dirname, './src'),
      '@assets': path.resolve(__dirname, './src/assets'),
      '@styles': path.resolve(__dirname, './src/styles'),
      '@types': path.resolve(__dirname, './src/types'),
      '@components': path.resolve(__dirname, './src/components'),
    },
  },
  plugins: [react(), svgr(), tsconfigPaths(tsConfigPathsOpts)],
});

My Storybook config (.storybook/main.js) looks like so:

const path = require('path');
const { mergeConfig } = require('vite');
const tsconfigPaths = require('vite-tsconfig-paths');
const svgr = require('vite-plugin-svgr');

const tsConfigPathsOpts = {
  extensions: ['.svg', '.png', '.jpeg'],
  loose: true,
};


module.exports = {
  stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
  addons: [
    '@storybook/addon-essentials',
    '@storybook/preset-create-react-app',
    '@storybook/addon-a11y',
    '@storybook/node-logger',
    'storybook-addon-designs',
    'storybook-color-picker',
    'storybook-dark-mode',
  ],
  framework: '@storybook/react',
  core: {
    builder: '@storybook/builder-vite',
  },
  async viteFinal(config, { configType }) {
    return mergeConfig(config, {
      resolve: {
        alias: {
          '@': path.resolve(__dirname, '../src'),
          '@assets': path.resolve(__dirname, '../src/assets'),
          '@styles': path.resolve(__dirname, '../src/styles'),
          '@types': path.resolve(__dirname, '../src/types'),
          '@components': path.resolve(__dirname, '../src/components'),
        },
      },
      plugins: [svgr(), tsconfigPaths.default(tsConfigPathsOpts)],
    });
  },
};

I've come to understand that I need to mock these SVG's so that their snapshot is consistent, but I need direction on whether my mocking implementation is correct. See the vi.mock Fn below.

// components/common/Nav/Nav.test.tsx

import React from 'react';
import { render } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import { composeStories } from '@storybook/testing-react';
import * as stories from './Nav.stories'; // import all stories from the stories file
import { vi } from 'vitest';

const { NavDefault } = composeStories(stories);

šŸ‘€
vi.mock('@assets/*', () => {
  return {
    default: 'SVGUrl',
    ReactComponent: 'div',
  };
});

describe('Nav Component', () => {
  test('it should match the snapshot', () => {
    const { asFragment } = render(<NavDefault />);
    expect(asFragment()).toMatchSnapshot();
  });
});

I was expecting this to mock all the imports from @assets/* to be strings "SVGUrl" or 'div'

But I get the same error as above:

FAIL  src/components/common/Nav/Nav.test.tsx > Nav Component > it should match the snapshot
Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.

Upvotes: 1

Views: 4218

Answers (2)

Ali Hamza
Ali Hamza

Reputation: 1

svgr({
  svgrOptions: {
    ref: true,
    svgo: false,
    titleProp: true,
    exportType: 'named',
  },
  include: '**/*.svg',
})

Adding these options to svgr might work refernce github issue https://github.com/vitest-dev/vitest/discussions/5271

Upvotes: 0

jbsouvestre
jbsouvestre

Reputation: 116

we had the same issue today and fixed it by adding the plugin svgr() in vitest.config.ts


import svgr from "vite-plugin-svgr";

export default defineConfig({
  plugins: [
    // ...other plugins
    svgr(),
  ]
})

Upvotes: 3

Related Questions