Ala Eddine Menai
Ala Eddine Menai

Reputation: 2880

How can I instrument code in Cypress with SWC (not babel) in Next.js?

I integrated Cypress into my project for testing, and everything went well. However, when I want to make code coverage I could not find a solution for my issue.

In Next.js 13, it does not support .babelrc, but SWC. However, Istanbul requires .babelrc as configuration!

Please read this GitHub discussion.

If I configure .babelrc and run my Cypress tests and build scripts:

  "cypress-open-component": "cypress open --component",

  "cypress-open-e2e": "npm run instrument && cypress open --e2e",

  "cypress-run-component": "npm run instrument && cypress run --component --spec src/components/core/*/*.cy.tsx --headless",

  "instrument": "npx nyc instrument --compact=false src  instrumented",

  "coverage-report": "npx nyc report --reporter=html"

  "build": "mv .babelrc.js .babel_ && next build; mv .babel_ .babelrc.js",

  "start": "next start",

  "lint": "next lint",

They work well.

However, when I run npm run dev:

"dev": "next dev",

I got an error:

 info Using external babel configuration from /home/alaeddine/Desktop/gersthofen-app/.babelrc.js
- error ./src/app/layout.tsx:2:1
Syntax error: "next/font" requires SWC although Babel is being used due to a custom babel config being present.
Read more: https://nextjs.org/docs/messages/babel-font-loader-conflict
<w> [webpack.cache.PackFileCacheStrategy] Restoring pack failed from /home/alaeddine/Desktop/gersthofen-app/.next/cache/webpack/client-development-fallback.pack.gz: Error: incorrect header check
- wait compiling /_error (client and server)...
- error ./src/app/layout.tsx:2:1
Syntax error: "next/font" requires SWC although Babel is being used due to a custom babel config being present.
Read more: https://nextjs.org/docs/messages/babel-font-loader-conflict

for the build script I used the solution, but I see it as a tricky solution.

Two alternatives

If I change the .babelrc.js to .babelrc.build.js, the npm run dev works well, because it ignores the Babel file, but the Cypress one failed:

npm run cypress-run-component

(Run Starting)

  ┌────────────────────────────────────────────────────────────────────────────────────────────────┐
  │ Cypress:        12.17.3                                                                        │
  │ Browser:        Electron 106 (headless)                                                        │
  │ Node Version:   v18.15.0 (/home/alaeddine/.nvm/versions/node/v18.15.0/bin/node)                │
  │ Specs:          1 found (indexButton.cy.tsx)                                                   │
  │ Searched:       src/components/core/Button/indexButton.cy.tsx                                  │
  └────────────────────────────────────────────────────────────────────────────────────────────────┘


────────────────────────────────────────────────────────────────────────────────────────────────────

  Running:  indexButton.cy.tsx                                                              (1 of 1)
17 assets
65 modules

ERROR in ./src/components/core/Button/index.tsx:14:1
Syntax error: Unexpected token, expected ","

  12 |   loading,
  13 |   icon,
> 14 | }: IButtonProps) => {
     |  ^
  15 |   const {
  16 |     button,
  17 |     primary,

client (webpack 5.86.0) compiled with 1 error in 6654 ms


  1) An uncaught error was detected outside of a test

Please, how can I make code coverage in Next.js 13 with Cypress?

File cypress.config.ts

import { defineConfig } from "cypress";

export default defineConfig({
  component: {
    devServer: {
      framework: "next",
      bundler: "webpack",
      webpackConfig: {
        mode: "development",
        devtool: false,
        module: {
          rules: [
            // application and Cypress files are bundled like React components
            // and instrumented using the babel-plugin-istanbul
            // (we will filter the code coverage for non-application files later)
            {
              test: /\.tsx$/,
              exclude: /node_modules/,
              use: {
                loader: "babel-loader",
                options: {
                  presets: ["@babel/preset-env", "@babel/preset-react"],
                  plugins: [
                    // we could optionally insert this plugin
                    // only if the code coverage flag is on
                    "istanbul",
                  ],
                },
              },
            },
          ],
        },
      },
    },
    // Instrument the code
    setupNodeEvents(on, config) {
      require("@cypress/code-coverage/task")(on, config);

      return config;
    },
  },

  e2e: {
    setupNodeEvents(on, config) {
      // implement node event listeners here
    },
  },
  video: false,
});

File next.config.ts

/** @type {import('next').NextConfig} */
const nextConfig = {
  async rewrites() {
    if (process.env.NODE_ENV === "development") {
      return [
        {
          source: "/components",
          destination: "/page",
        },
      ];
    }

    return [];
  },
};

module.exports = nextConfig;

Upvotes: 3

Views: 3134

Answers (4)

Rahul
Rahul

Reputation: 11

This approach works for me. just update the next.config.js/ webpack.config.js file

// next.config.js

module.exports = {
  reactStrictMode: true,
  swcMinify: true,  // Enable SWC for minification
  webpack(config, { isServer }) {
    if (!isServer && process.env.NODE_ENV !== 'production') {
      // Apply Babel loader and code coverage instrumentation for non-production environments (dev, test)
      config.module.rules.push({
        test: /\.(js|ts|tsx)$/, // Target JS, TS, and TSX files
        enforce: 'pre', // Ensure this runs before other loaders
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            // Use 'env' preset with modern target configurations
            presets: [
              [
                '@babel/preset-env',
                {
                  targets: '> 0.25%, not dead', // Specify which environments to target
                  useBuiltIns: 'entry', // Use polyfills based on the target environments
                  corejs: 3, // Use core-js 3 for polyfills
                },
              ],
              '@babel/preset-typescript', // Add TypeScript support
            ],
            plugins: [
              'babel-plugin-istanbul', // Adds code coverage instrumentation
              '@babel/plugin-transform-runtime', // Optimize Babel helpers for multiple file reuse
            ],
          },
        },
      });
    }

    return config
  },
}

Upvotes: 1

Alok Gupta
Alok Gupta

Reputation: 21

Setting up this was literally quite challenging. It was easy to setup Cypress and write tests but the code coverage part took really some good amount of time. The problem was that Cypress code coverage uses Babel compiler for js/ts while Next.js used SWC compiler. So when configuring Cypress code coverage with Istanbul (a tool for code coverage reporting) we needed to setup .babelrc files. Because of .babelrc file Next.js started using Babel compiler instead of SWC compiler and most of Next.js parts started failing because they are compatible with SWC compiler. It literally took me more than one day and 100+ tabs - blogs, GitHub issues and discussions, and Cypress Discord but could not find any good solution and this is still the problem that exists.

So after lots of struggle I found a little hacky solution. If you use Babel in package.json then Next.js will not be able to find Babel config and will use SWC compiler. And boom, it works.

https://babeljs.io/docs/configuration#packagejson

 "babel": {
    "presets": [
      "next/babel"
    ],
    "plugins": [
      "istanbul"
    ]
  }

With the above configuration, you don't need to install any extra plugins for SWC and Babel.

Happy Coding!

Upvotes: 2

Arnas Dičkus
Arnas Dičkus

Reputation: 677

I suggest using swc-plugin-coverage-instrument.

  1. Create Coverage. I suggest following Cypress documentation. Ignore the Babel instructions. Set up @cypress/code-coverage. While you test run, it should say something about coverage. The image is in the documentation.

  2. Instrument you code. I suggest using swc-plugin-coverage-instrument. So the steps would be:

    yarn add --dev swc-plugin-coverage-instrument
    
    next.config.js
      experimental: {
        swcPlugins: [["swc-plugin-coverage-instrument", {}]],
      },
    
    // Launch Next.js
    server yarn run dev
    // Launch Cypress
    npx cypress open
    Run Tests
    Open Coverage report at coverage/lcov-report/index.html
    

    It even works with app-router with some adjustments here.

Here are some examples.

Upvotes: 3

Kyle Holmberg
Kyle Holmberg

Reputation: 1146

https://github.com/kwonoj/swc-plugin-coverage-instrument

You could use SWC's port of Istanbul; however, this issue is pretty important IMO: https://github.com/kwonoj/swc-plugin-coverage-instrument/issues/197

Alternatively, you could use babel, but just for process.env.NODE_ENV === 'test'; (see Can I Get Next.Js to exclude .babelrc during build?)

Upvotes: 3

Related Questions