cypress-io / cypress

Fast, easy and reliable testing for anything that runs in a browser.
https://cypress.io
MIT License
46.68k stars 3.16k forks source link

Ship craco (CRA config override) adapter in `cypress/react` #16273

Closed cd-a closed 1 year ago

cd-a commented 3 years ago

I use craco for react, so I can use Less with create-react-app

It replaces react-scripts start with craco start, so that the Less gets bundled to css.

When using cypress normally, it's all good, because I start up the web server, and then it just gets served from there.

With the component runner however, it just renders the components directly. How can i make the styles work? If I mount a component that directly imports the App.less file, it obviously makes no difference.

lmiller1990 commented 3 years ago

Can you post a minimal reproduction? We use the same webpack config as your project does, so everything should work. I have some projects using sass and CSS modules, for example, and it's working fine. Are you using craco less?

It's possible we are missing some edges cases for tools like craco. I have not used craco before, but we can add an example to the npm/react/examples directory as part of this fix.

cd-a commented 3 years ago

Yes, I'm running craco-less

Here's a simple way to reproduce:

// create new repo
yarn create react-app cypress-craco --template typescript

// enter directory
cd cypress-craco

// install cypress
yarn add cypress @cypress/react @cypress/webpack-dev-server --dev

// install craco and craco-less
yarn add @craco/craco craco-less

Replace the build, start, test in package.json with the following

"start": "craco start",
"build": "craco build",
"test": "craco test",

Then, create craco.config.js in root folder and put this inside

/* eslint-disable @typescript-eslint/no-var-requires */
const CracoLessPlugin = require("craco-less");

module.exports = {
  plugins: [
    {
      plugin: CracoLessPlugin,
      options: {
        lessLoaderOptions: {
          lessOptions: {
            javascriptEnabled: true,
          },
        },
      },
    },
  ],
};

Then, rename the App.css to App.less in the src folder.

Inside the App.tsx file, change css to less

-import './App.css';
+import "./App.less";

Finally, replace App.test.tsx in the src folder with this:

import React from "react";
import { mount } from "@cypress/react";
import App from "./App";

it("renders learn react link", () => {
  mount(<App />);
  cy.get("a").contains("Learn React");
});

Now, when you ran yarn start, it gives you this: Screenshot 2021-04-30 at 10 15 03

When you run yarn cypress open-ct and select App.test.tsx, it shows you this:

Screenshot 2021-04-30 at 10 16 16

lmiller1990 commented 3 years ago

Right, this makes perfect sense - using the react-scripts plugin to inject the dev server grabs the unconfigured webpack config from CRA - the whole point of craco is to override that.

I looked into this and learned I can use the craco webpack API, specifically createWebpackDevConfig, to get the modified config.

I wrote this code and was able to get the less styling to work with Cypress. The following is in cypress/plugins/index.js.

const { createWebpackDevConfig } = require("@craco/craco");
const { startDevServer } = require('@cypress/webpack-dev-server')

const cracoConfig = require("../../craco.config.js");
const webpackConfig = createWebpackDevConfig(cracoConfig);

module.exports = (on, config) => {
  on('dev-server:start', options => {
    return startDevServer({
      options,
      webpackConfig
    })
  })

  return config
}

I think we can justify bundling a craco plugin with @cypress/react. I will work on shipping this. For now, you could use the above snippet.

image

Please let me know if this is working for you.

lmiller1990 commented 3 years ago

We will have a CRACO specific plugin for cypress/react in the next release. See #16333.

cd-a commented 3 years ago

Thank you so much!

ghost commented 3 years ago
const cracoConfig = require("../../craco.config.js");
const webpackConfig = createWebpackDevConfig(cracoConfig);

module.exports = (on, config) => {
  on('dev-server:start', options => {
    return startDevServer({
      options,
      webpackConfig
    })
  })

  return conf

It works, thank you!

kirstenlindsmith commented 1 year ago

Is there any update on how to implement this with cypress v10+? I'm implementing the latest version (12.9.0) and it looks like not both the workaround and the "new" (as of 2021) plugin both don't work/don't exist anymore. startDevServer is no longer exported from @cypress/webpack-dev-server, and @cypress/react/plugins/craco no longer exists at all and I can't find where it went.

astone123 commented 1 year ago

@kirstenlindsmith we don't support CRACO internally right now, but you should be able to get something to work in your Cypress configuration file like this

const { devServer } = require("@cypress/webpack-dev-server")
const { defineConfig } = require("cypress");
const { createWebpackDevConfig } = require("@craco/craco");

module.exports = defineConfig({
  component: {
    devServer: (devServerOptions) => devServer({
      ...devServerOptions,
      framework: 'react',
      webpackConfig: async () => {
        const cracoConfig = require("./craco.config.js");
         return createWebpackDevConfig(cracoConfig)
      },
    }),
  },
});

We need to do this because Cypress doesn't know how to get the webpack configuration for the app. We look for it in the root and have special support for Create React App that gets the webpack config that they use. This is a little more complicated - we need to call createWebpackDevConfig with our CRACO config and then return that configuration to Cypress.

Please let me know if this works for you. Thanks!

khashashin commented 1 year ago

@kirstenlindsmith did you get it to work? I also have 12.9 installed and can't get the component tests to start. Here is my cypress.json and craco.config.js

cypress.json

const { devServer } = '@cypress/webpack-dev-server';
const { defineConfig } = require('cypress');
const { createWebpackDevConfig } = require('@craco/craco');

module.exports = defineConfig({
  pageLoadTimeout: 180000,
  numTestsKeptInMemory: 0,

  reporterOptions: {
    toConsole: true,
  },

  env: {
    API_GATEWAY_BASE: 'https://development.services.com',
    DEV_SERVER_PORT: 3000,
  },

  e2e: {
    experimentalRunAllSpecs: true,
    excludeSpecPattern: [
      '**/energy_simulation/**',
      '**/debug/**.js',
      '**/_common/**',
    ],
  },

  component: {
    devServer(devServerConfig) {
      const cracoConfigFile = require('../../craco.config.js');
      const cracoConfig = createWebpackDevConfig({
        ...cracoConfigFile,
        webpack: {
          ...cracoConfigFile.webpack,
          devtool: 'eval-source-map',
        },
      });

      return devServer({
        ...devServerConfig,
        framework: 'react',
        webpackConfig: cracoConfig,
      });
    },
  },
});

craco.config.js

const { whenDev } = require('@craco/craco');
const NodePolyfillPlugin = require('node-polyfill-webpack-plugin');

module.exports = {
  webpack: {
    plugins: [
      new NodePolyfillPlugin({
        excludeAliases: ['console'],
      }),
    ],
    configure: whenDev(() => ({
      devtool: 'eval-source-map',
    })),
  },
};

I get the error:

image

lmiller1990 commented 1 year ago

@kirstenlindsmith @khashashin check this example I made. I used the craco config and cypress config from the above post and got it to work:

https://github.com/lmiller1990/cypress-craco

Not sure how you ran into that error, looks like your cypress.config is in a different directory - if you can pots a minimal repro (or fork from mine, maybe, to replicate the issue)? I can take a look, thanks!

khashashin commented 1 year ago

By moving cypress.config.js to the root folder of the project, I was able to start component testing without configuring anything via craco. @lmiller1990 Thank you for your help

lmiller1990 commented 1 year ago

You shouldn't need to move it to the root 🤔 but glad you got it working 🎉