React with vite + jest
Tags: blog, react, vite, jest
Published: 2023-04-19 20:55:06

¡Hola! En este tutorial te mostraré cómo usar React con Vite y Jest. Usaremos pnpm como gestor de paquetes. Para empezar, asumiré que ya has creado un proyecto básico de React con Vite y que has instalado todas las dependencias necesarias.

```
pnpm create vite
```
Para este ejemplo usaremos React + typescript

Para este tutorial me "guie" 🤣 por [este](https://hung.dev/posts/jest-vite) otro está en inglés, la razón por la que quise contribuir con una nueva versión es que ya ha habido algunos cambios en Jest por lo que todo lo que encuentren allí no funcionará 100%. Aunque allí está mejor explicado todo por alguien si sabe lo que hace ❤️.

###Paso 1: Instalar Jest

Primero, debes instalar Jest como una dependencia de desarrollo en tu proyecto. Para hacer esto, abre una terminal en la raíz de tu proyecto y ejecuta el siguiente comando:

```
pnpm install --save-dev jest @types/jest jest-environment-jsdom jest-watch-typeahead @testing-library/dom @testing-library/jest-dom @testing-library/react @testing-library/user-event @swc/jest @types/testing-library__jest-dom
```
###Paso 2: Configurar Jest

Ahora, necesitas configurar Jest en tu proyecto. Crea un archivo llamado `jest.config.cjs` en la raíz de tu proyecto y pega el siguiente código:

```js
module.exports = {
  roots: ["<rootDir>/src"],
  collectCoverageFrom: [
    "src/**/*.{js,jsx,ts,tsx}",
    "!src/**/*.d.ts",
    "!src/mocks/**",
  ],
  coveragePathIgnorePatterns: [],
  setupFilesAfterEnv: ["./config/jest/setupTests.js"],
  testEnvironment: "jsdom",
  modulePaths: ["<rootDir>/src"],
  transform: {
    "^.+\\.(ts|js|tsx|jsx)$": "@swc/jest",
    "^.+\\.css$": "<rootDir>/config/jest/cssTransform.js",
    "^(?!.*\\.(js|jsx|mjs|cjs|ts|tsx|css|json)$)":
      "<rootDir>/config/jest/fileTransform.js",
  },
  transformIgnorePatterns: [
    "[/\\\\]node_modules[/\\\\].+\\.(js|jsx|mjs|cjs|ts|tsx)$",
    "^.+\\.module\\.(css|sass|scss)$",
  ],
  moduleNameMapper: {
    "^react-native$": "react-native-web",
    "^.+\\.module\\.(css|sass|scss)$": "identity-obj-proxy",
    "^.+\\.(svg)$": "<rootDir>/src/__mocks__/fileMock.js",
  },
  moduleFileExtensions: [
    // Place tsx and ts to beginning as suggestion from Jest team
    // https://jestjs.io/docs/configuration#modulefileextensions-arraystring
    "tsx",
    "ts",
    "web.js",
    "js",
    "web.ts",
    "web.tsx",
    "json",
    "web.jsx",
    "jsx",
    "node",
  ],
  watchPlugins: [
    "jest-watch-typeahead/filename",
    "jest-watch-typeahead/testname",
  ],
  resetMocks: true,
};

```
###Paso 3: Archivos adicionales 

Crea un archivo en `/src/__tests__/App.test.tsx` con el siguiente contenido:


```tsx
import { render, screen } from "@testing-library/react";
import App from "../App";

describe("App", () => {
  it("should work as expected", () => {
    render(<App />);
    expect(1 + 1).toBe(2);
  });
});
```


Crea un archivo en `/src/__mocks__/fileMock.js` con el siguiente contenido:

```js
module.exports = "";
```
Esto es necesario porque Jest no puede entender cómo manejar las importaciones de archivos que no son de JS (en este caso, archivos SVG), por lo que el módulo fileTransform.js intercepta estas importaciones y devuelve el nombre de archivo del recurso. Sin esta transformación, Jest no sería capaz de ejecutar el conjunto de pruebas, ya que fallaría al resolver las importaciones de archivos que no son de JS.

Crea en la raíz del proyecto las siguientes carpetas `/config/jest` con los siguientes archivos: `cssTransform.js, fileTransform.js, setupTests.js`

```js
//cssTransform.js
"use strict";

module.exports = {
  process(src, filename, config, options) {
    return {
      code: "module.exports = {};",
    };
  },
  getCacheKey() {
    return "cssTransform";
  },
};
```

```js
//fileTransform.js
"use strict";

const path = require("path");
const camelcase = require("camelcase");
module.exports = {
  process(src, filename) {
    const assetFilename = JSON.stringify(path.basename(filename));

    if (filename.match(/\.svg$/)) {
      // Based on how SVGR generates a component name:
      // https://github.com/smooth-code/svgr/blob/01b194cf967347d43d4cbe6b434404731b87cf27/packages/core/src/state.js#L6
      const pascalCaseFilename = camelcase(path.parse(filename).name, {
        pascalCase: true,
      });
      const componentName = `Svg${pascalCaseFilename}`;
      const svgComponentCode = `const React = require('react');
      module.exports = {
        __esModule: true,
        default: ${assetFilename},
        ReactComponent: React.forwardRef(function ${componentName}(props, ref) {
          return {
            $$typeof: Symbol.for('react.element'),
            type: 'svg',
            ref: ref,
            key: null,
            props: Object.assign({}, props, {
              children: ${assetFilename}
            })
          };
        }),
      };`;
      return { code: svgComponentCode };
    }

    const fileModuleCode = `module.exports = ${assetFilename};`;
    return { code: fileModuleCode };
  },
};
```

```js
//setupTests.js
import "@testing-library/jest-dom/extend-expect";

window.matchMedia = (query) => ({
  matches: false,
  media: query,
  onchange: null,
  addEventListener: jest.fn(),
  removeEventListener: jest.fn(),
  dispatchEvent: jest.fn(),
  addListener: jest.fn(),
  removeListener: jest.fn(),
});

Object.defineProperty(URL, "createObjectURL", {
  writable: true,
  value: jest.fn(),
});
```

Crea en la raíz del proyecto un archivo llamado `.swcrc` con lo siguiente:

```js
{
  "jsc": {
    "target": "es2017",
    "parser": {
      "syntax": "typescript",
      "tsx": true,
      "decorators": false,
      "dynamicImport": false
    },
    "transform": {
      "react": {
        "pragma": "React.createElement",
        "pragmaFrag": "React.Fragment",
        "throwIfNamespace": true,
        "development": false,
        "useBuiltins": false,
        "runtime": "automatic"
      },
      "hidden": {
        "jest": true
      }
    }
  },
  "module": {
    "type": "commonjs",
    "strict": false,
    "strictMode": true,
    "lazy": false,
    "noInterop": false
  }
}
```
`swcrc` es un archivo de configuración para el compilador de JavaScript `swc`. `swc` es un compilador de JavaScript/TypeScript extremadamente rápido que se utiliza para transpilar y optimizar el código fuente de una aplicación.

###Paso 4: Edita package.json

```js
"type": "commonjs",
  "scripts": {
    //...
    "test": "jest"
  },
```
Eso sería todo, nótese que tuve que cambiar `module` por `commonjs` aun así la app corre normalmente.
Por último, si desean contribuir para mejorar o mantener esta guia actualizada, pueden hacerlo desde [aquí.](https://gist.github.com/codooze/7453045a43e88a07f69da07aad223f6e)