Gatsby: JEST + Gatsby

Created on 15 Nov 2017  ·  22Comments  ·  Source: gatsbyjs/gatsby

Is there a Jest example?

I can't find anything, and couldn't really get it to work in my gatsby project.

Most helpful comment

It probably needs history from the Router component which Gatsby uses. Gatsby should maybe expose some kind of test utils for that.

All 22 comments

I'm using Gatsby with Jest on a project. You didn't say what sort of issues you ran into... Here's what my Jest setup looks like:

https://gist.github.com/m-allanson/3dd343db56951ba852fd09a7e52d6a89

I'm using Gatsby's built-in PostCSS / CSS Modules setup for CSS. I'm also using Storybook, if you're not using that too then you might be able to skip some of that config.

@m-allanson Do you have examples of testing a component?

I use Jest with React all the time, but when I use it with Gatsby, it always goes into the libraries imported inside a component's JS file, and finds issues with them, although the library is not even being used with the component, and I'm only importing that specific component from the file, e.g:

import { HeaderBox } from '../layouts/index'

Hmm I don't think I've had any issues like that. I'm basically just snapshotting everything in /src/components using Storybook's Storyshots.

@theiliad your configuration needs fine tuning. I'm using jest with my project. No complications with Gatsbyjs.

@nodox Do you have some sample code by any chance?

@theiliad Maybe you can check my settings, Jest is running fine with my Gatsby setup: https://github.com/DesarrolloWebSantaCC/santacc-web/tree/master/test

I wonder if there is a way to inherit Gatsby's Babel configuration in Jest.

This is my quick and dirty way of (more or less) replicating Gatsby's configuration:

npm install --save-dev babel-jest

```js
// test/transformer.js
module.exports = require('babel-jest').createTransformer({
presets: [
// built-in
['env', {
modules: 'commonjs',
targets: {
node: 'current',
},
exclude: ['transform-regenerator', 'transform-es2015-typeof-symbol'],
}],
'stage-0',
'react',
// custom...
],
plugins: [
// built-in
'gatsby/dist/utils/babel-plugin-extract-graphql',
'add-module-exports',
'transform-object-assign',
// custom...
],
})

```js
// jest.config.js
module.exports = {
  transform: { '^.+\\.jsx?$': '<rootDir>/test/transformer.js' }
}

Every time you update the above Babel configuration you need to clear Jest's cache:

npx jest --clearCache

I have one issue with Gatsby and Jest and that is when needing to test a component that uses <Link> (from 'gatsby-link')

I get this error

 FAIL  src/components/header/header.spec.jsx
  Header Component
    ✕ should not be null (23ms)

  ● Header Component › should not be null

    TypeError: Cannot read property 'history' of undefined

      10 |
      11 |   beforeEach(() => {
    > 12 |     header = mount(<Header/>);
      13 |   });
      14 |

      at new GatsbyLink (node_modules/gatsby-link/index.js:107:34)
      at node_modules/react-dom/lib/ReactCompositeComponent.js:292:18
      at measureLifeCyclePerf (node_modules/react-dom/lib/ReactCompositeComponent.js:73:12)

Component

import React from 'react';
import Link from 'gatsby-link';
import './header.css';

const Header = () => {
  return (
    <div className="header"> 
      <Link to="/">
        <header></header>
        <h2 className="caption">A DREAMER BY DESIGN</h2>
      </Link>
    </div>
  );
};

export default Header;

Test

import * as React from 'react';
import { mount, configure } from 'enzyme';
import Adapter from 'enzyme-adapter-react-15';
import Header from './header';

configure({ adapter: new Adapter() });

describe('Header Component', () => {
  let header;

  beforeEach(() => {
    header = mount(<Header/>);
  });

  it('should not be null', () => {
    expect(header).not.toBeNull();
    expect(header.find('.header').length).toEqual(1);
  });

});

Otherwise I've had good luck with Jest and Gatsby in my project

@thescientist13 i just mock out that dependency - e.g.

import { a } from 'react-dom-factories';
import omit from 'lodash/omit';

const Link = jest.genMockFromModule('gatsby-link');

Link.default.prototype.render = function(){
    const domProps = Object.assign(
        { href: 'GATSBY-LINK-MOCK' },
        omit(this.props, ['to', 'onClick'])
    );
    return a(domProps);
};

module.exports = Link;

it's a bit ham-handed in that you lose visibility into the actual href but it has gotten me by ok so far 🤷‍♂️

It probably needs history from the Router component which Gatsby uses. Gatsby should maybe expose some kind of test utils for that.

@silvenon , yeah, that sounds about right. I was trying to do something like this but wasn't having luck

I'm stuck on this and have no idea why it doesn't work:

import React from 'react'
import { render, Simulate } from 'react-testing-library'
import Link from 'gatsby-link'
import { createMemoryHistory as createHistory } from 'history'
import { Router } from 'react-router'

describe('link', () => {
  beforeAll(() => {
    global.___loader = {
      enqueue: jest.fn(),
    }
  })

  it('navigates to path on click', () => {
    const history = createHistory()
    const { getByText, unmount } = render(
      <Router history={history}>
        <Link to="/path">Click me</Link>
      </Router>,
    )
    Simulate.click(getByText('Click me'))
    expect(history.location.pathname).toBe('/path')
    unmount()
  })
})

The Link component should call history.push on click, but it doesn't.

It works if I do history.push('/path') manually.

I had some luck with using Enzyme instead of react-test-renderer.
I was configuring snapshot testing with Jest, and i actually think the issue here is that Gatsby is not exposing the context from react-router.

This is my test, that i got working:

import Header from '../Header';

describe('Header component', () => {
  it('renders correctly', () => {
    const rendered = shallow(
      <Header />
    );

    expect(rendered).toMatchSnapshot();
  });
});

Enzyme setup:

const enzyme = require('enzyme');
const Adapter = require('enzyme-adapter-react-16');

/**
* React 16 Enzyme adapter
*/

enzyme.configure({ adapter: new Adapter() });

/**
* Make Enzyme functions available in all test files without importing
*/
global.shallow = enzyme.shallow;
global.render = enzyme.render;
global.mount = enzyme.mount;

package.json

  "jest": {
    "transform": {
      ".(js|jsx)": "babel-jest"
    },
    "testRegex": "(\\.(test|spec))\\.(jsx|js)$",
    "testPathIgnorePatterns": [
      "/node_modules/",
      "/.cache/"
    ],
    "setupFiles": [
      "./jest-setup.js"
    ]
  },

Hope that can help your use case too.

Figured it out, the Link component from react-router-dom used in gatsby-link checks if event.button === 0:

Simulate.click(getByText('Click me'), { button: 0 })

Facebooks test utilities currently don't do this by default. facebook/react#12476

@fredjens that explains why I needed to add react-router in my dev dependencies for testing.

@silvenon thanks for this! I had this issue for a while and avoided looking for a solution 😅

I´m stuck here. Trying to test with enzyme and I get this error:
Cannot find module 'react/lib/ReactComponentTreeHook' from 'ReactDebugTool.js'

My package.json:

{
  "name": "gatsby-starter-default",
  "description": "Gatsby default starter",
  "version": "1.0.0",
  "author": "Kyle Mathews <[email protected]>",
  "dependencies": {
    "emotion": "^9.1.0",
    "emotion-server": "^9.1.0",
    "file-loader": "^1.1.11",
    "gatsby": "^1.9.241",
    "gatsby-link": "^1.6.39",
    "gatsby-plugin-react-helmet": "^2.0.8",
    "gatsby-plugin-react-next": "^1.0.11",
    "gatsby-plugin-resolve-src": "^1.0.0",
    "gatsby-plugin-sass": "^1.0.25",
    "gatsby-plugin-sharp": "^1.6.41",
    "gatsby-remark-component": "^1.1.3",
    "gatsby-remark-images": "^1.5.60",
    "gatsby-source-filesystem": "^1.5.27",
    "gatsby-transformer-remark": "^1.7.39",
    "prop-types": "^15.6.1",
    "react-animated-css": "^1.0.4",
    "react-emotion": "^9.1.0",
    "react-helmet": "^5.2.0",
    "rehype-react": "^3.0.2",
    "remark-parse": "^5.0.0",
    "remark-rehype": "^3.0.0"
  },
  "keywords": [
    "gatsby"
  ],
  "license": "MIT",
  "scripts": {
    "build": "gatsby build",
    "develop": "gatsby develop",
    "format": "prettier --write 'src/**/*.js'",
    "test": "jest",
    "test:watch": "jest --watch"
  },
  "devDependencies": {
    "babel-eslint": "^7.2.3",
    "babel-jest": "^22.4.3",
    "enzyme": "^3.3.0",
    "enzyme-adapter-react-16": "^1.1.1",
    "enzyme-to-json": "^3.3.3",
    "eslint": "^4.9.0",
    "eslint-config-airbnb": "^16.1.0",
    "eslint-config-prettier": "^2.6.0",
    "eslint-loader": "^1.9.0",
    "eslint-plugin-import": "^2.7.0",
    "eslint-plugin-jsx-a11y": "^6.0.2",
    "eslint-plugin-prettier": "^2.3.1",
    "eslint-plugin-react": "^7.4.0",
    "jest": "^22.4.3",
    "prettier": "^1.11.1",
    "react-test-renderer": "^16.3.0"
  },
  "jest": {
    "transform": {
      ".(js|jsx)": "babel-jest"
    },
    "testRegex": "(\\.(test|spec))\\.(jsx|js)$",
    "testPathIgnorePatterns": [
      "/node_modules/",
      "/.cache/"
    ],
    "moduleFileExtensions": [
      "jsx",
      "js"
    ],
    "collectCoverage": true,
    "coverageReporters": [
      "lcov",
      "text",
      "html"
    ],
    "setupFiles": [
      "./jest-setup.js"
    ]
  }
}

jest-setup.js

const enzyme = require('enzyme')
const Adapter = require('enzyme-adapter-react-16')

enzyme.configure({ adapter: new Adapter() })

global.shallow = enzyme.shallow
global.render = enzyme.render
global.mount = enzyme.mount

Did anyone have a similar problem?

Update

For some reason the react-dom import was from an different version then the react one ( I guess that this happened with the gatsby-react-next ). So a npm i --save react-dom ( updating it to 16.3.0 ) solved the problem.

@ThiagoMiranda You can also alias the gatsby-react-next version of react and react-dom in your jest config.

  moduleNameMapper: {
    '^react$': `gatsby-plugin-react-next/node_modules/react`,
    '^react-dom$': `gatsby-plugin-react-next/node_modules/react-dom`,
  },

@silvenon
Thanks for this post, it helped me solve a issue with v2 Gatsby testing! 🎉

FYI, for anyone in the position to migrate to v2, there is a new testing guide up that and there has been a lot of work done to smooth out the testing process for users. Some of it should still be applicable here I think
https://github.com/gatsbyjs/gatsby/pull/6678

I was able to get a pretty good set of unit tests working for a component that uses <Link> in v2, which unblocked a lot of code paths I hadn't been able to cover before. I left a working example and steps here.

@KyleAMathews / @m-allanson
Any thoughts on how to handle this in terms of v1 support? The issue seem pretty well handled now in v2 (though there still might be some edge cases, admittedly).

The core team is focused on getting v2 out. We'd be happy to support people who'd like to backport (non-breaking) fixes/changes to v1.

Due to the high volume of issues, we're closing out older ones without recent activity. Please open a new issue if you need help!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

mikestopcontinues picture mikestopcontinues  ·  3Comments

totsteps picture totsteps  ·  3Comments

rossPatton picture rossPatton  ·  3Comments

andykais picture andykais  ·  3Comments

benstr picture benstr  ·  3Comments