Source:  Twitter logo

I would like to have 100% coverage on my project.

In order to do so, I need to test my index.js file which is very basic :

index.js

import React from 'react';
import ReactDOM from 'react-dom';

ReactDOM.render(<App/>, document.getElementById('root'));

I can't find how to test this. When creating a function such as :

index.js

const index = (div) => {
  ReactDOM.render(<App />, div || document.getElementById('root'));
};

and then testing it :

index.test.js

it('renders without crashing', () => {
  const div = document.createElement('div');
  index(div);
});

I get an error when importing index: Invariant Violation: _registerComponent(...): Target container is not a DOM element.

PS:

Note that I already have the following test, working perfectly :

App.test.jsx

it('renders without crashing', () => {
  const div = document.createElement('div');
  ReactDOM.render(<App/>, div);
});

If 100% coverage on your project is your goal and the code in your index.js file is trivial, then it might be a good option to exclude the file from the coverage report, as Andreas Köberle points out in his answer.

Create-react-app currently only supports these four keys in the Jest configuration (source):

collectCoverageFrom
coverageReporters
coverageThreshold
snapshotSerializers

This is why

coveragePathIgnorePatterns": ["src/index.js"]

won't work.

Add following code to the most outer scope of your package.json file:

"jest": {
  "collectCoverageFrom": [
    "src/**/*.{js,jsx}",
    "!src/index.js"
  ]
}

In the image below you see the output of a test run with this code added to the package.json of the initial app created with create-react-app v1.4.3. Note that the index.js file doesn't show up in the report anymore and also doesn't affect the coverage percentage.

33 users liked answer #0dislike answer #033
dcas profile pic
dcas

This is how I've tested index.js

index.js

import React from "react";
import ReactDOM from "react-dom";
import App from "./App";

ReactDOM.render(<App />, document.getElementById("root"));

index.test.js

import React from "react";
import ReactDOM from "react-dom";
import App from "./App";

jest.mock("react-dom", () => ({ render: jest.fn() }));

describe("Application root", () => {
  it("should render without crashing", () => {
    const div = document.createElement("div");
    div.id = "root";
    document.body.appendChild(div);
    require("./index.js");
    expect(ReactDOM.render).toHaveBeenCalledWith(<App />, div);
  });
});
28 users liked answer #1dislike answer #128
Shiraz profile pic
Shiraz

The main question is what you want to test there. If you want to test that your code works correct, write a unit test that spies on ReactDOM.render and mocks document.getElementById('root'). Cause this is all your code does, calling ReactDOM.render with our App component and a specific div.

import ReactDOM from 'react-dom';
...
jest.mock('react-dom', ()=> ({render: jest.fn()}))


it('renders without crashing', () => {

  const div = document.createElement('div');
  ReactDOM.render(<App/>, div);
  global.document.getElementById = (id) => id ==='root' && div
  expect(ReactDOM.render).toHaveBeenCalledWith(...)
});

If you want test that the app really starts in your page, you should write integration test with Selenium or Nightwatch.js

To just get 100% coverage you can also ignore this file by adding it to the coveragePathIgnorePatterns in your jest settings

17 users liked answer #2dislike answer #217
Andreas Köberle profile pic
Andreas Köberle

I found an article online that explains this way to write the test...

// index.test.js
import Index from './index.js';

it('renders without crashing', () => {
  expect(JSON.stringify(
    Object.assign({}, Index, { _reactInternalInstance: 'censored' })
  )).toMatchSnapshot();
});

Now change the index.js file accordingly:

// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

export default ReactDOM.render(
  <App />,
  document.getElementById('root') || document.createElement('div')
);
8 users liked answer #3dislike answer #38
Erik Grosskurth profile pic
Erik Grosskurth

Extending on dcastil's answer, here's how to skip these trivial files for a TypeScript project:

  1. Edit package.json
  2. At the root level add the following snippet

    {
      ...rest of existing props,
      "jest": {
        "collectCoverageFrom": [
          "src/**/*.{ts,tsx}",
          "!src/serviceWorker.ts",
          "!src/index.tsx"
        ]
      }
    }
    
  3. Save and re-run coverage

By now coverage should be higher.

1 users liked answer #4dislike answer #41
Lucio profile pic
Lucio

Inspired by the answer above by Shiraz. This is essentially the same solution but for React 17/18. Also it provides some additional test coverage.

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode />,
);
import React from 'react';
import ReactDOM from 'react-dom';

const render= jest.fn().mockName('render');

jest.mock('react');
jest.mock('react-dom', () => ({
  createRoot: jest.fn().mockName('createRoot')
}));

let documentSpy=jest.spyOn(document, 'getElementById')


describe('Entry point index test', () => {

  const doc =document.createElement('div');
  doc.setAttribute('id', 'root');

  beforeEach(() => {
    ReactDOM.createRoot.mockReturnValue({render});
    require("../index.js");
   });

  it('should call ReactDOM.createRoot once', () => {
    expect(ReactDOM.createRoot).toHaveBeenCalledTimes(1);
  });

  it('should call document.getElementById with root once', () => {
    expect(documentSpy).toHaveBeenCalledTimes(1);
    expect(documentSpy).toHaveBeenCalledWith('root');
  });

  it('should call render with React.StrictMode', () => {
    expect(render).toHaveBeenCalledTimes(1);
    expect(render).toHaveBeenCalledWith( <React.StrictMode />,);
  });

});
1 users liked answer #5dislike answer #51
MG Developer profile pic
MG Developer

Here what i did and looks like it works just perfect (100% coverage, app doesn't break):

// src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

import './index.css';

export const ReactStrictMode = <React.StrictMode>
  <App />
</React.StrictMode>

export const rootElement = document.getElementById('root')

ReactDOM.render(
  ReactStrictMode,
  rootElement
);

and then in index.spec.js:

// src/index.spec.js
/* eslint-env jest */
import React from 'react'
import ReactDOM from 'react-dom'
import { ReactStrictMode, rootElement } from './index'

jest.mock('react-dom', () => ({ render: jest.fn() }))

describe('index.js', () => {
  it('renders without crashing', () => {
    ReactDOM.render(ReactStrictMode, rootElement)
    expect(ReactDOM.render).toHaveBeenCalledWith(ReactStrictMode, rootElement)
  })
})

0 users liked answer #6dislike answer #60
Johnny Juarez profile pic
Johnny Juarez

Added a couple of more test cases. Any feedback would be appreciated...

import React from "react";
import { render, cleanup } from "@testing-library/react";
import ReactDOM from "react-dom";
import App from "./App";

afterEach(cleanup);

// jest.mock will mock all the function using jest.fn() that is present inside the react-dom library
jest.mock("react-dom");

describe("Testing Application Root", () => {
  it("should render without crashing", () => {
      const div = document.createElement("div");
      div.id = "root";
      document.body.appendChild(div);
      require("./index");
      expect(ReactDOM.render).toHaveBeenCalledWith(<App />, div);
  });
    
  it("should render the app inside div which has root id", () => {
    expect(global.document.getElementById("root")).toBeDefined();
  });
  
  it("should render App component", () => {
    expect(App).toBeDefined();
  });
});
0 users liked answer #7dislike answer #70
Tanish Raj profile pic
Tanish Raj

Copyright © 2022 QueryThreads

All content on Query Threads is licensed under the Creative Commons Attribution-ShareAlike 3.0 license (CC BY-SA 3.0).