Svgr: Try to test component with React generated form svgr

Created on 20 Apr 2018  ยท  30Comments  ยท  Source: gregberge/svgr

Hi,
I have a little problem with Jest tests runner.

My simple component:

import React from 'react';

import {ReactComponent as CloseIcon} from '../images/close.svg';

const CloseBubble = ({minimize}) => (
    <div className='thulium-chat-bubble'>
        <a onClick={event => {
            event.preventDefault();
            minimize()
        }} href=''>
            <CloseIcon/>
        </a>
    </div>
);

export default CloseBubble;

test class:

import CloseBubble from '../CloseBubble';

describe('CloseBubble', () => {
    test('sample', () => {
        expect(1).toBe(1);
    });
});

And after I run this test I got following error:

  ({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,global,jest){<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><defs><style>.cls-1{fill:#231f20;}</style></defs><title>cancel_icon</title><path class="cls-1" d="M52.08,50,70.32,31.84a1.49,1.49,0,0,0-2.11-2.11L50,47.87,31.82,29.63a1.8,1.8,0,0,0-2.22,0,1.82,1.82,0,0,0,0,2.21L47.85,50.09,29.6,68.23a1.47,1.47,0,0,0,0,2.12,1.74,1.74,0,0,0,2.22,0L50.06,52.1,68.31,70.35a1.73,1.73,0,0,0,2.21,0,1.46,1.46,0,0,0,0-2.12Z"/></svg>
                                                                                             ^

    SyntaxError: Unexpected token <

       9 |             minimize()
      10 |         }} href=''>
    > 11 |             <CloseIcon/>
      12 |         </a>
      13 |     </div>
      14 | );

      at ScriptTransformer._transformAndBuildScript (node_modules/jest-runtime/build/script_transformer.js:316:17)
      at Object.<anonymous> (src/components/CloseBubble.js:11:14)

What I've do wrong?

discussion ๐Ÿ—ฃ

Most helpful comment

We created a simple mock for the svgr loader and mapped to it in the jest config:

svgrMock.js

module.exports = 'IconMock'

In your package.json e.g.

"jest": {
  "moduleNameMapper": {
    "\\.svg": "<rootDir>/__mocks__/svgrMock.js"
  }
}

Your snapshots will include all properties on the icon components, so they will be tested.

All 30 comments

It looks like a babel-loader is missing somewhere.

I did handle with this by added following conf to the package.json:

"jest": {
  "moduleNameMapper": {
    "\\.svg": "svgr/webpack"
  }
}

But now, I have a following error when I try to render a React component inside test:

it('test', () => {
    const minimize = sinon.spy();
    const toJSON = renderer.create(<CloseBubble minimize={minimize}/>).toJSON();
    console.log(toJSON);
});

Produce following error:

  console.error ../../../node_modules/react-test-renderer/cjs/react-test-renderer.development.js:5206
    The above error occurred in the <svgrLoader> component:
        in svgrLoader (created by CloseBubble)
        in a (created by CloseBubble)
        in div (created by CloseBubble)
        in CloseBubble

    Consider adding an error boundary to your tree to customize error handling behavior.
    Visit https://fb.me/react-error-boundaries to learn more about error boundaries.

TypeError: Cannot read property 'async' of undefined
    at svgrLoader (/node_modules/svgr/lib/webpack.js:20:25)
    at mountIndeterminateComponent (/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:4137:15)
    at beginWork (/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:4541:16)
    at performUnitOfWork (/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:7377:16)
    at workLoop (/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:7406:26)
    at renderRoot (/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:7437:9)
    at performWorkOnRoot (/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:8012:24)
    at performWork (/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:7933:9)
    at performSyncWork (/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:7910:5)
    at requestWork (/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:7810:7)
    at scheduleWorkImpl (/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:7685:13)
    at scheduleWork (/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:7645:12)
    at scheduleRootUpdate (/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:8273:5)
    at updateContainerAtExpirationTime (/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:8301:12)
    at Object.updateContainer (/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:8328:14)
    at Object.create (/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:9009:18)
    at Object.<anonymous> (/src/components/__tests__/CloseBubble.test.js:25:29)
    at Object.asyncFn (/node_modules/jest-jasmine2/build/jasmine_async.js:82:37)
    at resolve (/node_modules/jest-jasmine2/build/queue_runner.js:52:12)
    at new Promise (<anonymous>)
    at mapper (/node_modules/jest-jasmine2/build/queue_runner.js:39:19)
    at promise.then (/node_modules/jest-jasmine2/build/queue_runner.js:73:82)
    at <anonymous>

@piotrooo I believe this is due to not having an async/await transformer in the pipeline.

@piotrooo how did you fix this issue? I ran into the same thing. Adding

  "moduleNameMapper": {
    "\\.svg": "@svgr/webpack"
  }

was already helpful, but now I am stuck with the same problem. It seems like this in

function svgrLoader(source) {
  const callback = this.async();

 ...
}

is undefined for the test environment (here Jest).

PS.: Why is it so hard to simply run Jest with Webpack :(

Minimal application showcasing the problem: https://github.com/rwieruch/svgr-async-bug

@rwieruch it is impossible to use the webpack loader in Jest, you can't do that.

I don't know what is the best option to do it but the use-case is not relative to SVGR, you should look for using webpack + Jest together. Maybe build a bundle with webpack, then using this build in Jest.

That's too bad in my opinion. Not related to this library, but to not being able to combine Jest and Webpack. That's one of the most often used testing setups I have seen out there. It works standalone with its custom setup stuff, but now not being able to add a third-party to the combination is bad for the ecosystem, because I am just not able to test the components using SVG anymore. I try to find a solution for the Jest + Webpack problem. Thanks for your help!

Maybe keeping the issue open helps others as well. Maybe we can find a solution together!

Would be curious whether @piotrooo found a solution for it.

Yeah I agree, but you have this problem with all Webpack loaders + Jest.

I didn't handle it at all... Workaround which works for me, generating icons while bundling project.

npm script:

"generate-icons": "node_modules/.bin/svgr --no-title src/images/ --out-dir src/icons && echo Done. Building project...",

Disclaimer
This is very naive way! Should be treated as a workaround not a final solution.

We created a simple mock for the svgr loader and mapped to it in the jest config:

svgrMock.js

module.exports = 'IconMock'

In your package.json e.g.

"jest": {
  "moduleNameMapper": {
    "\\.svg": "<rootDir>/__mocks__/svgrMock.js"
  }
}

Your snapshots will include all properties on the icon components, so they will be tested.

I need to try this! Thank you @marco-streng ๐Ÿ’ฏ

It worked for me! ๐ŸŽ‰ ๐ŸŽ‰ ๐ŸŽ‰
thanks @marco-streng!

module.exports = 'IconMock' not work for me.

I use svgr like this:

import { ReactComponent as Logo } from '../assets/logo.svg'

Then I changed svgrMock.js to:

module.exports = { ReactComponent: 'IconMock' }

Tests pass.

Thanks @leoendless, I updated the readme.

@neoziro Whereabouts is this info in the readme? I found this thread first.

I think the section has disappeared. We should add a section "Testing" on the website.

Hey ๐Ÿ‘‹ !

I was having issue with both solution on React 16.8.3, React warns about casing.

console.error node_modules/react-dom/cjs/react-dom.development.js:506
Warning: <IconMock /> is using incorrect casing. Use PascalCase for React components, or lowercase for HTML elements.
          in IconMock
          in div
          in Unknown (created by WrapperComponent)
          in WrapperComponent

because, tests were render something like <IconMock className="aweome-classname" /> instead of <icon-mock className="aweome-classname" />

Finally i was able to make it work with:

module.exports = { ReactComponent: 'icon-mock' }

and now everything is ok

Hey !

I was having issue with both solution on React 16.8.3, React warns about casing.

console.error node_modules/react-dom/cjs/react-dom.development.js:506
Warning: <IconMock /> is using incorrect casing. Use PascalCase for React components, or lowercase for HTML elements.
          in IconMock
          in div
          in Unknown (created by WrapperComponent)
          in WrapperComponent

because, tests were render something like <IconMock className="aweome-classname" /> instead of <icon-mock className="aweome-classname" />

Finally i was able to make it work with:

module.exports = { ReactComponent: 'icon-mock' }

and now everything is ok

Thanks man, that worked like a charm, thanks a lot.

It worked for me with the solution provided by @marco-streng Thank you!

Since everything is mocked now, I assume SVG icons will be excluded from visual regression testing now. Would be great to find a solution to still render the SVGs for these kind of tests, but I haven't found one.

For what it's worth, identity-obj-proxy seems to work just fine for this use case.

@isaachinman identity-obj-proxy does work but still getting the casing warning ๐Ÿคทโ€โ™‚ . I wish there was a way to configure that.

If you need to support both
import logoURL from '../assets/logo.svg'
and
import { ReactComponent as Logo } from '../assets/logo.svg'
and worked for styled-components

Then your svgrMock.js should be

import React from 'react';

export default 'SvgrURL';
const SvgrMock = React.forwardRef((props, ref) => <span ref={ref} {...props} />);

export const ReactComponent = SvgrMock;

Maybe we should add a section in the documentation to document how to test with SVGR. Does someone want to create it?

If you need to support both
import logoURL from '../assets/logo.svg'
and
import { ReactComponent as Logo } from '../assets/logo.svg'
and worked for styled-components

Then your svgrMock.js should be

import React from 'react';

export default 'SvgrURL';
const SvgrMock = React.forwardRef((props, ref) => <span ref={ref} {...props} />);

export const ReactComponent = SvgrMock;

This worked, however it shows a console error message:

<SvgrURL /> is using incorrect casing. Use PascalCase for React components, or lowercase for HTML elements.

adjusting it to

import React from 'react';

const SvgrMock = React.forwardRef((props, ref) => <span ref={ref} {...props} />);

export const ReactComponent = SvgrMock;
export default SvgrMock;

did the trick for me

Does this also work with typescript?

I tried to use this configuration but it didn't work
Screen Shot 2020-01-28 at 15 32 11

Does this also work with typescript?

I tried to use this configuration but it didn't work
Screen Shot 2020-01-28 at 15 32 11

Any luck?

@vgm106 and @bobysf12 for what it worth, i'm able to make it work with this trick for TypeScript

Does this also work with typescript?

I tried to use this configuration but it didn't work
Screen Shot 2020-01-28 at 15 32 11

@bobysf12 did you solve this problem and how ????

Does this also work with typescript?
I tried to use this configuration but it didn't work
Screen Shot 2020-01-28 at 15 32 11

@bobysf12 did you solve this problem and how ????

Hey @Milos5611, I added this in jest configuration

    moduleNameMapper: {
        "^.+\\.svg": "<rootDir>/src/__mocks__/svgrMock.ts",
    },

and here's the svgrMock.ts:

// @ts-ignore
import * as React from "react";
export default "SvgrURL";
export const ReactComponent = "div";

@Milos5611

Thanks @bobysf12 .
For everyone else that configuration changes don't work, I had configuration in my jest.config

 transform: {
    "^.+\\.tsx?$": "ts-jest",

but instead i should be

 "^.+\\.tsx?$": <rootDir>/node_modules/babel-jest"
Was this page helpful?
0 / 5 - 0 ratings

Related issues

troch picture troch  ยท  5Comments

SilencerWeb picture SilencerWeb  ยท  4Comments

liorgreenb picture liorgreenb  ยท  5Comments

rj-coding picture rj-coding  ยท  5Comments

chloesun picture chloesun  ยท  6Comments