# Testing React

## [Jest](https://jestjs.io/)

### Jest Configuration

In `package.json`:

```json
{
    // ...
    "scripts": {
        "test": "jest --watch"  // watch re-runs test on save
    },
    "jest": {
        // calls additional setups for enzyme, react testing library, ...
        "setupFiles": [
            "path/to/testSetup.js"  
        ],
        "moduleNameMapper": {
            // file imports to ignore 
            "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "path/to/fileMock.js",
            "\\.(css|less)$": "path/to/styleMock.js"
        }
  },
}
```

In `fileMock.js`:

```js
// Mocks file imports for Jest. As suggested by https://jestjs.io/docs/en/webpack
module.exports = "test-file-stub";
```

In  `styleMock.js`:

```js
// Mocks CSS imports for Jest. As suggested by https://jestjs.io/docs/en/webpack
module.exports = {};
```

### Jest Tests

[Expect docs](https://jestjs.io/docs/expect)

```js
// .spec.js or .test.js
it("test description", () => {
    // test body
    expect(expected).toEqual(actual);
});

// group related tests
describe("test group name", () => {
    it(/* ... */);
    it(/* ... */);
});
```

### Snapshots

In `Component.Snapshots.js`:

```js
import React from "react";
import renderer from "react-test-renderer";

import Component from "./path/to/Component";
// import mock data if necessary

it("test description", () => {
    // renders the DOM tree of the component
    const tree = renderer.create(<Component funcProp={jest.fn() /* mock function */} /* component props */ />);

    // save a snapshot of the component at this point in time ( in  __snapshots__ folder)
    // in future test it will be checked to avoid regressions
    // can be updated during jest --watch pressing "u"
    expect(tree).matchSnapshot();
});
```

---

## [Enzyme](https://enzymejs.github.io/enzyme/)

### Enzyme Configuration

```js
// testSetup.js
import { configure } from "enzyme";
import Adapter from "enzyme-adapter-react-<version>";

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

### Enzyme Tests

In `Component.test.js`:

```js
import React from "react";
import { shallow, mount } from "enzyme";
// eventual wrapper components (react-router, react-redux's provider, ...) for mount render

// shallow renders single component w/o children, no DOM generated
// mount renders component w/ it's children

import Component from "./path/to/Component";

// factory to setup shallow test easily
function testHelper(args) {
    const defaultProps = { /* default value for props in each test */ };

    const props = { ...defaultProps, ...args };
    return shallow(<Component {...props} />);
}

// shallow rendering test
it("test description", () => {
    const dom = testHelper(/* optional args */);
    // or
    const dom = shallow(<Component /* props */ />);

    // check a property of expected component
    // selector can be from raw JSX (name of a component)
    expect(dom.find("selector").property).toBe(expected);
});

// mount rendering test
if("test description" () => {
    const dom = mount(
        <WrapperComponent>
            <Component /* props *//>
        </WrapperComponent>
    );

    // selector has to be HTML selector since the component is rendered completely
    // possible to test child components
    expect(dom.find("selector").property).toBe(expected);
});
```

---

## [React Testing Library](https://testing-library.com/docs/react-testing-library/intro/)

Encourages to write test based on what the user sees. So components are always *mounted* and fully rendered.

### React Testing Library Tests

In `Components.test.js`:

```js
import React from "react";
import { cleanup, render } from "@testing-library/react";

import Component from "./path/to/Component";

afterEach(cleanup);

// factory to setup test easily
function testHelper(args) {
    const defaultProps = { /* default value for props in each test */ };

    const props = { ...defaultProps, ...args };
    return render(<Component {...props} />);
}

it("test description", () => {
    const { getByText } = testHelper();

    // react testing library func
    getByText("text");  // check if test is present in the rendered component
});
```
