gilbarbara / react-inlinesvg

An SVG loader component for ReactJS
https://codesandbox.io/s/github/gilbarbara/react-inlinesvg/tree/main/demo
MIT License
1.27k stars 99 forks source link

Could not convert the src to a React element #140

Closed imjakechapman closed 4 years ago

imjakechapman commented 4 years ago

We're using react-inlinesvg and we have no issues with it in our project. This issue arises when running jest. We have a wrapper SVG component that just passes the source to react-inlinesvg and this component causes all tests that include an SVG component.

We're using react-testing-library.

jest-config:

module.exports = {
  testEnvironment: "jsdom",
  setupFilesAfterEnv: ["<rootDir>/client/setupTests.ts"],
  roots: ["client/views"],
  transform: {
    "^.+\\.jsx?$": "babel-jest",
    "^.+\\.(tsx|ts)?$": "ts-jest",
    ".scss$": "<rootDir>/node_modules/jest-css-modules-transform",
    "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/fileTransformer.ts"
  },
  globals: {
    "ts-jest": {
      tsConfig: "<rootDir>/client/tsconfig.json",
    },
  },
  transformIgnorePatterns: ["/node_modules/(?!(lodash-es|react)/)"],
  testRegex: "(/__tests__/.*|(\\.|/)(test|spec))\\.tsx?$",
  moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"],
  moduleNameMapper: {
    ".+\\.(svg|png|jpg|scss)$": "identity-obj-proxy",
  },
  moduleDirectories: ["node_modules", "client"],
  collectCoverage: false,
  collectCoverageFrom: ["client/**/*.ts?(x)", "client/**/*.js?(x)"],
  coverageDirectory: "./coverage",
  coverageReporters: ["json", "lcov", "text"],
  snapshotSerializers: ["enzyme-to-json/serializer"],
};

The test error itself:

 console.error node_modules/react-inlinesvg/lib/index.js:91
      Error [InlineSVGError]: Could not convert the src to a React element
          at new InlineSVGError (/Users/jakechapman/[redacted]/[redacted]/node_modules/react-inlinesvg/src/helpers.ts:23:5
          at InlineSVG.Object.<anonymous>.InlineSVG.getElement (/Users/jakechapman/[redacted]/[redacted]/node_modules/react-inlinesvg/src/index.tsx:248:24)
          at callCallback (/Users/jakechapman/[redacted]/[redacted]/node_modules/react-dom/cjs/react-dom.development.js:13829:12)

I've boiled this down to something happening in the getElement() method.

Not sure if you could help pinpoint a cause or possible come up with a fix or lead me down the path for possibly opening up a pull request to fix this.

gilbarbara commented 4 years ago

Hey,

I've never used identity-obj-proxy but perhaps it doesn't provide the response this package needs.

You could add a react-inlinesvg.js to your test's __mocks__ directory:

import React from 'react';

export default ({ src }) => <svg id={src} />;
gilbarbara commented 4 years ago

Reading about identity-obj-proxy, it is only recommended to be used as a proxy with CSS modules.

But the src prop for this package needs to be a string.

imjakechapman commented 4 years ago

@gilbarbara This definitely fixed the errors. Holy moley that was a run around trying to figure out what was causing this. Absolutely glorious. Thanks for the push in the right direction. Definitely appreciate it.

wviana commented 2 years ago

Hi there, I'm having kind the same problem. Here is my jest config:

module.exports = {
  clearMocks: true,
  collectCoverageFrom: ['src/**/*.{ts,tsx,js,jsx}'],
  coverageDirectory: 'coverage',
  coveragePathIgnorePatterns: [
    '/node_modules/',
    'src/index.tsx',
    'src/temp/',
    'src/types/',
    'src/helpers/testHelper.ts',
    'src/helpers/constants.ts',
    'src/pages/Errors/*',
  ],
  coverageThreshold: {
    global: {
      branches: 90,
      functions: 90,
      lines: 90,
      statements: 90,
    },
  },
  moduleFileExtensions: ['js', 'json', 'jsx', 'ts', 'tsx'],
  moduleNameMapper: {
    '\\.(css|scss|sass)$': 'identity-obj-proxy',
    '\\.svg$': '<rootDir>/__mocks__/genericMock.js',
    '^@src/(.*)$': '<rootDir>/src/$1',
    '^@helpers/(.*)$': '<rootDir>/src/helpers/$1',
    '^@appConfig(.*)$': '<rootDir>/config/application/appConfigTypes.ts',
  },
  preset: 'ts-jest',
  setupFilesAfterEnv: ['./src/helpers/jestHelper.ts'],
  testEnvironment: 'jsdom',
  testMatch: ['**/*.(test|spec).(ts|tsx)'],
  transform: {
    '^.+\\.(ts|tsx)$': 'ts-jest',
  },
  transformIgnorePatterns: ['node_modules/(?!(react-native|[omitted]|lodash)/)'],

I've tried to add a react-inlinesvg.js to test's mocks directory but I'm getting a

    This usually means that you are trying to import a file which Jest cannot parse, e.g. it's not plain JavaScript.

    By default, if Jest sees a Babel config, it will use that to transform your files, ignoring "node_modules".

    Here's what you can do:
     • To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config.
     • If you need a custom transformation specify a "transform" option in your config.
     • If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the "moduleNameMapper" config option.

    You'll find more details and examples of these config options in the docs:
    https://jestjs.io/docs/en/configuration.html

    Details:

    /[omitted]/__mocks__/react-inlinesvg.js:1
    ({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,global,jest){import React from 'react';
                                                                                             ^^^^^^

    SyntaxError: Cannot use import statement outside a module
wviana commented 2 years ago

I solve it going into babeljs and converting the code to vanila js.

"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;

var _react = _interopRequireDefault(require("react"));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

var _default = ({
  src
}) => /*#__PURE__*/_react.default.createElement("svg", {
  id: src
});

exports.default = _default;
gilbarbara commented 2 years ago

hey @wviana

I think you were using the module syntax (import/export) in your mock with the js extension and apparently jest isn't transforming js files, just ts.

Replace the mock extension to tsx?

wviana commented 2 years ago

Worked like a charm. Just had to add a porps interface and a return type.

Here is the code for react-inlinesvg.tsx

import React from 'react';

interface Props {
  src: string;
}

const InlineSvgMock = ({ src }: Props): React.ReactElement => <svg id={src} />;

export default InlineSvgMock;
iankinney5 commented 1 year ago

Thanks @wviana! This helped us out a lot! Haha.

jeffal commented 1 year ago

We're using Typescript and also adding a title element within the svg worked well for us. Thank you!

import React from 'react';

const InlineSvgMock = ({
  title,
  ...props
}: {
  className: string;
  src: string;
  title: string;
}) => <svg {...props}>{title ? <title>{title}</title> : null}</svg>;

export default InlineSvgMock;