Testing Gatsby component with MDX and graphql data

Viewed 513

I am trying to write a unit test in Jest for the component 'Step.js', which is used as a template for creating pages dynamically with the GraphQL data. I have provided the fake data in the test, hoping that it would resolve by the GraphQL query. But I got the error message that saying 'Syntax Error: Cannot use import statement outside a module'.

Since then, I tried:

  1. Adding a 'Static Query' mock in the file, hoping that would generate the fake data in the testing environment. But I got the same error message.
  2. Adding 'mdx' in the 'moduleNameMapper' config option. I still got the same error message.
  3. My next attempt might be installing 'Jest-transformer-mdx' to see if I can add it to the 'jest.config.js'.

I just wondering if there is any suggestion for this problem? Or has anyone encounter this before? If you do have some suggestion, please do share! Thanks in advance

Here is code,

Step.js

import React from "react"
import { graphql } from "gatsby"
import { MDXRenderer } from "gatsby-plugin-mdx"
import Layout from "./layout"
import style from "./step.module.css"

export default function Step({ data }) {
  const mdx = data.mdx
  return (
    <div>
      <h1 className={style.title}>{mdx.frontmatter.title}</h1>
      <Layout>
        <MDXRenderer>{mdx.body}</MDXRenderer>
      </Layout>
    </div>
 )
}

export const query = graphql`
  query($slug: String!) {
    mdx(fields: { slug: { eq: $slug } }) {
      body
      frontmatter {
        title
      }
    }
  }
`

Step.test.js

import React from "react"
import renderer from "react-test-renderer"
import { StaticQuery } from "gatsby"
import Step from "../src/components/step"

beforeEach(() => {
  StaticQuery.mockImplementationOnce(({ render }) => {
    render({
        mdx: {
          body: "function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\n\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\n\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n\n/* @jsxRuntime classic */\n\n/* @jsx mdx */\nvar _frontmatter = {\n  \"title\": \"Homebrew\"\n};\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n      props = _objectWithoutProperties(_ref, [\"components\"]);\n\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"p\", null, \"It's important to keep your programs up to date, to check to see if anything is outdated, you can use this command:\"), mdx(CommandBox, {\n    mdxType: \"CommandBox\"\n  }, \"brew outdated\"), mdx(\"p\", null, \"And to update:\"), mdx(CommandBox, {\n    mdxType: \"CommandBox\"\n  }, \"brew update\"), mdx(\"p\", null, \"If anything has gone wrong with your installation or update, you can uninstall Homebrew and start again:\"), mdx(CommandBox, {\n    mdxType: \"CommandBox\"\n  }, \"/bin/bash -c \\\"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/uninstall.sh)\\\"\"), mdx(StepButton, {\n    to: \"/step-4\",\n    mdxType: \"StepButton\"\n  }));\n}\n;\nMDXContent.isMDXComponent = true;",
          frontmatter: {
            title: "Homebrew"
          },
        },
    })
  })
})


describe("Step component", () => {
  it("render step content on the page", () => {
    const data = {
        mdx: {
          body: "function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\n\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\n\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n\n/* @jsxRuntime classic */\n\n/* @jsx mdx */\nvar _frontmatter = {\n  \"title\": \"Homebrew\"\n};\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n      props = _objectWithoutProperties(_ref, [\"components\"]);\n\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"p\", null, \"It's important to keep your programs up to date, to check to see if anything is outdated, you can use this command:\"), mdx(CommandBox, {\n    mdxType: \"CommandBox\"\n  }, \"brew outdated\"), mdx(\"p\", null, \"And to update:\"), mdx(CommandBox, {\n    mdxType: \"CommandBox\"\n  }, \"brew update\"), mdx(\"p\", null, \"If anything has gone wrong with your installation or update, you can uninstall Homebrew and start again:\"), mdx(CommandBox, {\n    mdxType: \"CommandBox\"\n  }, \"/bin/bash -c \\\"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/uninstall.sh)\\\"\"), mdx(StepButton, {\n    to: \"/step-4\",\n    mdxType: \"StepButton\"\n  }));\n}\n;\nMDXContent.isMDXComponent = true;",
          frontmatter: {
            title: "Homebrew"
          },
        },
    }

    const tree = renderer.create(<Step data={data}/>).toJSON()
    expect(tree).toMatchSnapshot()
  });
});

jest.config.js

module.exports = {
  transform: {
    "^.+\\.jsx?$": `<rootDir>/jest-preprocess.js`,
  },
  moduleNameMapper: {
    ".+\\.(css|styl|less|sass|scss)$": `identity-obj-proxy`,
    ".+\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": `<rootDir>/__mocks__/file-mock.js`,
  },
  testPathIgnorePatterns: [`node_modules`, `\\.cache`, `<rootDir>.*/public`],
  transformIgnorePatterns: [`node_modules/(?!(gatsby)/)`],
  globals: {
    __PATH_PREFIX__: ``,
  },
  testURL: `http://localhost`,
  setupFiles: [`<rootDir>/loadershim.js`],
}
1 Answers

So after some help, I finally passed the test. It seems like Jest was having trouble finding the 'gatsby-plugin-mdx' module, and its 'MDXRenderer' module. So I mocked them and return the 'children' that was passed in.

import React from "react"
import renderer from "react-test-renderer"
import { StaticQuery } from "gatsby"
import Step from "../src/components/step"


jest.mock("gatsby-plugin-mdx", () => {
  return { MDXRenderer: ({children}) => {
    return <div>{children}</div>;
  } }
});

beforeEach(() => {
  StaticQuery.mockImplementationOnce(({ render }) => {
    render({
        mdx: {
          body: "function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\n\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\n\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n\n/* @jsxRuntime classic */\n\n/* @jsx mdx */\nvar _frontmatter = {\n  \"title\": \"Homebrew\"\n};\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n      props = _objectWithoutProperties(_ref, [\"components\"]);\n\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"p\", null, \"It's important to keep your programs up to date, to check to see if anything is outdated, you can use this command:\"), mdx(CommandBox, {\n    mdxType: \"CommandBox\"\n  }, \"brew outdated\"), mdx(\"p\", null, \"And to update:\"), mdx(CommandBox, {\n    mdxType: \"CommandBox\"\n  }, \"brew update\"), mdx(\"p\", null, \"If anything has gone wrong with your installation or update, you can uninstall Homebrew and start again:\"), mdx(CommandBox, {\n    mdxType: \"CommandBox\"\n  }, \"/bin/bash -c \\\"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/uninstall.sh)\\\"\"), mdx(StepButton, {\n    to: \"/step-4\",\n    mdxType: \"StepButton\"\n  }));\n}\n;\nMDXContent.isMDXComponent = true;",
          frontmatter: {
            title: "Homebrew"
          },
        },
    })
  })
})


describe("Step component", () => {
  it("render step content on the page", () => {
    const data = {
        mdx: {
          body: "function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\n\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\n\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n\n/* @jsxRuntime classic */\n\n/* @jsx mdx */\nvar _frontmatter = {\n  \"title\": \"Homebrew\"\n};\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n      props = _objectWithoutProperties(_ref, [\"components\"]);\n\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"p\", null, \"It's important to keep your programs up to date, to check to see if anything is outdated, you can use this command:\"), mdx(CommandBox, {\n    mdxType: \"CommandBox\"\n  }, \"brew outdated\"), mdx(\"p\", null, \"And to update:\"), mdx(CommandBox, {\n    mdxType: \"CommandBox\"\n  }, \"brew update\"), mdx(\"p\", null, \"If anything has gone wrong with your installation or update, you can uninstall Homebrew and start again:\"), mdx(CommandBox, {\n    mdxType: \"CommandBox\"\n  }, \"/bin/bash -c \\\"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/uninstall.sh)\\\"\"), mdx(StepButton, {\n    to: \"/step-4\",\n    mdxType: \"StepButton\"\n  }));\n}\n;\nMDXContent.isMDXComponent = true;",
          frontmatter: {
            title: "Homebrew"
          },
        },
    }

    const tree = renderer.create(<Step data={data}/>).toJSON()
    expect(tree).toMatchSnapshot()
  });
});

If anyone has any feedback or spotted anything else, please let me know. Thanks!

Related