Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot setup cypress component testing for a lib or app via the generator #12844

Closed
blessanm86 opened this issue Oct 27, 2022 · 17 comments · Fixed by #12886
Closed

Cannot setup cypress component testing for a lib or app via the generator #12844

blessanm86 opened this issue Oct 27, 2022 · 17 comments · Fixed by #12886
Assignees
Labels
blocked: repro needed outdated scope: testing tools Issues related to Cypress / Jest / Playwright / Vitest support in Nx type: bug

Comments

@blessanm86
Copy link

Current Behavior

Cannot setup cypress component testing for a lib or app via the generator
I get the error

bmathew@Blessans-MacBook-Pro-4 personio-web % nx g @nrwl/react:cypress-component-configuration --project=my-new-lib --verbose


>  NX  Generating @nrwl/react:cypress-component-configuration

✔ Automatically generate tests for components declared in this project? (y/N) · false

 >  NX   Cannot read properties of undefined (reading 'config')


TypeError: Cannot read properties of undefined (reading 'config')
    at /Users/bmathew/Desktop/repos/personio-web/node_modules/.pnpm/@nrwl+react@14.8.4_734a1c5f3d4ccdfc9ca0aa1d480d891c/node_modules/@nrwl/react/src/generators/cypress-component-configuration/lib/update-configs.js:14:32
    at Generator.next (<anonymous>)
    at fulfilled (/Users/bmathew/Desktop/repos/personio-web/node_modules/.pnpm/tslib@2.4.0/node_modules/tslib/tslib.js:115:62)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)

Expected Behavior

Cypress correctly setup

Steps to Reproduce

Im not sure how. Cause it works fine for a fresh nx workspace.

Environment

 Node : 16.10.0
   OS   : darwin arm64
   pnpm : 6.32.9
   
   nx : 14.8.4
   @nrwl/angular : Not Found
   @nrwl/cypress : 14.8.4
   @nrwl/detox : Not Found
   @nrwl/devkit : 14.8.4
   @nrwl/esbuild : Not Found
   @nrwl/eslint-plugin-nx : 14.8.4
   @nrwl/expo : Not Found
   @nrwl/express : Not Found
   @nrwl/jest : 14.8.4
   @nrwl/js : 14.8.4
   @nrwl/linter : 14.8.4
   @nrwl/nest : 14.8.4
   @nrwl/next : 14.8.4
   @nrwl/node : 14.8.4
   @nrwl/nx-cloud : Not Found
   @nrwl/nx-plugin : 14.8.4
   @nrwl/react : 14.8.4
   @nrwl/react-native : Not Found
   @nrwl/rollup : 14.8.4
   @nrwl/schematics : Not Found
   @nrwl/storybook : 14.8.4
   @nrwl/web : 14.8.4
   @nrwl/webpack : 14.8.4
   @nrwl/workspace : 14.8.4
   typescript : 4.4.4
   ---------------------------------------
   Local workspace plugins:
         @personio-web/nx-plugin
   ---------------------------------------
   Community plugins:
         @nx-tools/nx-docker: 2.3.0
@FrozenPandaz FrozenPandaz added blocked: repro needed scope: testing tools Issues related to Cypress / Jest / Playwright / Vitest support in Nx labels Oct 28, 2022
@FrozenPandaz
Copy link
Collaborator

FrozenPandaz commented Oct 28, 2022

I believe this happens when its not able to find an app's build target where the project you specified is used. @barbados-clemens we should have a better error message for this? What is the logic here?

@blessanm86
Copy link
Author

I created the lib with nx g @nrwl/react:lib my-new-lib
It doesn't have a build target. Only lint and test

@tommyc38
Copy link

I have a similar issue for setting this up for a publishable library (e.g. ng-material-plus) which does have a build target and had the following error.

nx g @nrwl/angular:cypress-component-configuration --project=ng-material-plus --verbose                                                                                                                                                                                                                                                               

>  NX  Generating @nrwl/angular:cypress-component-configuration

✔ Automatically generate tests for components declared in this project? (y/N) · true

 >  NX   Unable to find a valid build configuration. Try passing in a target for an Angular app. --build-target=<project>:<target>[:<configuration>]

Error: Unable to find a valid build configuration. Try passing in a target for an Angular app. --build-target=<project>:<target>[:<configuration>]
    at assertValidConfig (/Users/user/dev/ng-material-plus/node_modules/@nrwl/angular/src/generators/cypress-component-configuration/cypress-component-configuration.js:83:15)
    at /Users/user/dev/ng-material-plus/node_modules/@nrwl/angular/src/generators/cypress-component-configuration/cypress-component-configuration.js:75:9
    at Generator.next (<anonymous>)
    at fulfilled (/Users/user/dev/ng-material-plus/node_modules/tslib/tslib.js:115:62)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
                                                                                                           

It didn't find the build target so I ran:

nx g @nrwl/angular:cypress-component-configuration --project=ng-material-plus --target:ng-material-plus:build

It set everything up but when I go to run it it doesn't work:

> nx run ng-material-plus:component-test

Your configFile is invalid: /Users/user/dev/ng-material-plus/libs/ng-material-plus/cypress.config.ts

It threw an error when required, check the stack trace below:

Error: Missing executor options "main"

To reproduce this just create a new nx workspace with an angular publishable library and run the angular cypress component generator and fire it up.

@barbados-clemens
Copy link
Contributor

@blessanm86 it does look to be an issue where the generator wasn't able to find a build target that the project was being used in, but the more helpful error message wasn't from a null reference. I'll fix that to make sure the better error is more helpful.

but you can pass in a --build-target=some-react-app:build to get past it.

@tommyc38 the build target has to be an application for it to correctly work a build target for a publishable library won't work as Cypress needs to actually run an app under the hood for component testing, hence the need for an apps build target. I'll work to add more helpful messaging around this.

@barbados-clemens barbados-clemens self-assigned this Oct 28, 2022
@tommyc38
Copy link

tommyc38 commented Oct 28, 2022

@barbados-clemens that makes sense. I currently have storybook and cypress hooked up for my lib which currently set up a cypress app (e.g. ng-material-plus-e2e). How can I rig up cypress components to use the same app (e.g. ng-material-plus-e2e) but point it to component tests in my lib/src? Also, has there been any discussion around setting up a build target that can leverage a custom webpack when running the cypress e2e generator? A build target with an executor that allows us to setup the code instrumentation with a custom webpack config has to be done manually. This is cumbersome and I wish Nx would do this automagically for us. Doesn't everyone want code coverage when running cypress????

@barbados-clemens
Copy link
Contributor

the way we've structured the component testing is to be added on the library you are wanting to test, similar to jest unit testing. But nothing is stopping you from configuring your existing cypress project to also run component tests other than the nx generators don't set it up out of the box.

If you want to set that up, you just create a new target in the existing cypress apps project.json i.e. apps/ng-material-plus-e2e/project.json the configuration would be the same as the one currently in the library you generated. in your cypress.config.ts you have to move over the component configuration which looks like this by default. component: nxComponentTestingPreset(__filename),. But you'll need to make sure that the the component and e2e tests are isolated out from each other so component tests don't try to run the e2e *.cy.ts files and vice versa. This is done via the specPattern option. i.e. specPattern: 'component/**/*.cy.ts'

Now I'm inclined to not recommend this way to set up component testing for a couple things.

  1. it has a couple footguns around setup and making sure things are running what they are supposed to be running; though, generators can solve this for the most part
  2. Now your component tests aren't directly next to their components so you have to go find all their references. I personally feel like this is a bit awkward since the idea is they are component tests.
  3. You could technically keep the files next to the components and change the configuration to path over to the other libraries and then set implicitDependencies of the cypress project, but this is a bit hard to understand what's happening and feels ergonomically complicated just to have a target defined else where.

as for the question about custom webpack I'm unsure what you're exactly looking for, but since component testing uses the build target you provided to it, you can provide a custom webpack to the build configuration which will be picked up via cypress and ran. If you only want this custom webpack to apply do component testing. you can make a component testing specific configuration under your build target and provide that to the component testing target options under devServerTarget i.e. devServerTarget: 'my-coo-app:build:ct'

hopefully that helps! let me know if you have any more questions :)

@tommyc38
Copy link

@barbados-clemens thank you! I agree with your points regarding cypress-component testing a lib and am not down with the complexity to get it to work while keeping the tests next to the src code. With respect to the custom webpack configuration, this is needed for reporting cypress test coverage (see cypress docs). Anyone who want's coverage with cypress must create a custom webpack configuration with a loader (e.g. coverage-istanbul-loader) that works with webpack 5 to instrument the code so coverage can be reported. It would be great if the nx generator would ask if we wanted our e2e to include coverage instrumentation and could then generate a webpack config and add a build setup to run e2e with coverage. Does that make better sense what I am getting at?

@tommyc38
Copy link

here is an example setup to get cypress coverage setup:

https://github.com/skylock/cypress-angular-coverage-example

@blessanm86
Copy link
Author

blessanm86 commented Oct 30, 2022

Thanks @barbados-clemens I was able to now get the cypress configure generator to run after specifying the built target.
But when I try to run the component test, it cannot seem to find the tsconfig.cy.json

> nx run employee-creation-wizard-frontend:component-test

It looks like this is your first time using Cypress: 10.11.0
[STARTED] Task without title.
[TITLE]  Verified Cypress! /Users/bmathew/Library/Caches/Cypress/10.11.0/Cypress.app
[SUCCESS]  Verified Cypress! /Users/bmathew/Library/Caches/Cypress/10.11.0/Cypress.app
Opening Cypress...
>  NX  Falling back to ts-node for local typescript execution. This may be a little slower.
  - To fix this, ensure @swc-node/register and @swc/core have been installed
Unable to build a webpack config with the project graph.
Falling back to default webpack config.
TypeError [ERR_INVALID_ARG_TYPE]: The "path" argument must be of type string. Received undefined
Your configFile is invalid: /Users/bmathew/Desktop/repos/personio-web/apps/employee-creation-wizard/frontend/cypress.config.ts

It threw an error when required, check the stack trace below:

Error: ENOENT: no such file or directory, stat 'tsconfig.cy.json'
    at Object.statSync (node:fs:1536:3)
    at Object.<anonymous> (/Users/bmathew/Library/Caches/Cypress/10.11.0/Cypress.app/Contents/Resources/app/packages/server/node_modules/graceful-fs/polyfills.js:312:16)
    at Object.statSync (/Users/bmathew/Library/Caches/Cypress/10.11.0/Cypress.app/Contents/Resources/app/packages/server/node_modules/graceful-fs/polyfills.js:312:16)
    at resolveConfigPath (/Users/bmathew/Desktop/repos/personio-web/node_modules/.pnpm/tsconfig-paths@3.14.1/node_modules/tsconfig-paths/src/tsconfig-loader.ts:86:10)
    at loadSyncDefault (/Users/bmathew/Desktop/repos/personio-web/node_modules/.pnpm/tsconfig-paths@3.14.1/node_modules/tsconfig-paths/src/tsconfig-loader.ts:57:22)
    at tsConfigLoader (/Users/bmathew/Desktop/repos/personio-web/node_modules/.pnpm/tsconfig-paths@3.14.1/node_modules/tsconfig-paths/src/tsconfig-loader.ts:46:22)
    at configLoader (/Users/bmathew/Desktop/repos/personio-web/node_modules/.pnpm/tsconfig-paths@3.14.1/node_modules/tsconfig-paths/src/config-loader.ts:68:22)
    at Object.loadConfig (/Users/bmathew/Desktop/repos/personio-web/node_modules/.pnpm/tsconfig-paths@3.14.1/node_modules/tsconfig-paths/src/config-loader.ts:42:10)
    at new TsconfigPathsPlugin (/Users/bmathew/Desktop/repos/personio-web/node_modules/.pnpm/tsconfig-paths-webpack-plugin@3.5.2/node_modules/tsconfig-paths-webpack-plugin/lib/plugin.js:20:42)
    at buildBaseWebpackConfig (/Users/bmathew/Desktop/repos/personio-web/node_modules/.pnpm/@nrwl+react@14.8.4_e0a87075ded09836e07f6196ea355c33/packages/react/plugins/component-testing/webpack-fallback.ts:18:9)
    at nxComponentTestingPreset (/Users/bmathew/Desktop/repos/personio-web/node_modules/.pnpm/@nrwl+react@14.8.4_e0a87075ded09836e07f6196ea355c33/packages/react/plugins/component-testing/index.ts:89:43)
    at Object.<anonymous> (/Users/bmathew/Desktop/repos/personio-web/apps/employee-creation-wizard/frontend/cypress.config.ts:5:38)
    at Module._compile (node:internal/modules/cjs/loader:1101:14)
    at Module.m._compile (/Users/bmathew/Library/Caches/Cypress/10.11.0/Cypress.app/Contents/Resources/app/node_modules/ts-node/src/index.ts:1618:23)
    at Module._extensions..js (node:internal/modules/cjs/loader:1153:10)
    at Object.require.extensions.<computed> (/Users/bmathew/Library/Caches/Cypress/10.11.0/Cypress.app/Contents/Resources/app/node_modules/ts-node/src/index.ts:1621:12)

 ———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————

 >  NX   Ran target component-test for project employee-creation-wizard-frontend (7s)
 
    ✖    1/1 failed
    ✔    0/1 succeeded [0 read from cache]

I have not edited the generated cypress.config.ts file. Its still using the nxComponentTestingPreset.
But my build target is a custom executor.

"build": {
  "executor": "@personio-web/nx-plugin:frontend-build"
},

@barbados-clemens
Copy link
Contributor

@blessanm86 do you have a reproduction I can look at? The tsconfig.cy.json is in the cypress folder and should be linked in the projects tsconfig.json with the references.

@blessanm86
Copy link
Author

Hey, @barbados-clemens I have tried to create a slimed down version of our nx workspace and invited u to a private repo.
I already see the cypress tsconfig references in the root tsconfig. so not sure whats happening.

@barbados-clemens
Copy link
Contributor

@blessanm86 this is because component testing preset assumes the executor for the build target is going to be one of @nrwl/webpack:webpack with it's executor options, and you have a custom executor. Because of that, the nxCommponentTestingPreset isn't able to dynamically build a webpack config without those executor options, so it falls back to a simple/default webpack config that assumes the tsconfig.cy.json is in a given location which is at the project root (which is for legacy reasons).

Because of your custom executor the preset won't be able to build the webpack config to pass to cypress component testing. You could just move the tsconfig.cy.json to the root of your project and then you'll be able to get the fallback webpack config. I noticed in your custom nx plugin you already have logic to build your own webpack config in the build executor.
Since you have this I would just leverage that with the nx provided preset.

const nxConfig = nxComponentTestingPreset(__filename)

const config = createConfig(options, context);
const webpackCommonConfig = createWebpackCommonConfig(config);
const webpackprodConfig = createWebpackProdConfig(config);
const finalConfig = webpackMerge.merge(
      webpackCommonConfig,
      webpackprodConfig,
);
// replace the nx generated config with your own
nxConfig.devServer.webpackConfig = finalConfig; 
export default defineConfig({
  component: nxConfig
})

you can check how we build and make the executor context inside the nx preset as well and just use those utilities.
really just need the getProjectConfigByPath and createExecutorContext from the @nrwl/cypress/plugins/cypress-preset

This sounds like a lot, but it's not too bad since you've already done the hard part of making the webpack configs in your build executor. now it's just assembling them together in a different location, your cypress.config.ts file

I'm happy to help walk you through other issues you might have setting this up since it is a bit new and not commonly something people have to do with Nx. Since you've built your own custom nx plugin and executor I'm sure what you see inside the nxComponentTestingPreset will make sense.

@blessanm86
Copy link
Author

@barbados-clemens Thanks for looking into this. I tried what u mentioned and Im able to create my webpack config.
But Im stuck with the following error.

Unable to build a webpack config with the project graph.
Falling back to default webpack config.
TypeError [ERR_INVALID_ARG_TYPE]: The "path" argument must be of type string. Received undefined
Your configFile is invalid: /Users/bmathew/Desktop/repos/cct-bug/apps/employee-creation-wizard/frontend/cypress.config.ts

It threw an error when required, check the stack trace below:

TypeError: (0 , cypress_1.defineConfig) is not a function
    at Object.<anonymous> (/Users/bmathew/Desktop/repos/cct-bug/apps/employee-creation-wizard/frontend/cypress.config.ts:53:28)
    at Module._compile (node:internal/modules/cjs/loader:1101:14)
    at Module.m._compile (/Users/bmathew/Library/Caches/Cypress/10.11.0/Cypress.app/Contents/Resources/app/node_modules/ts-node/src/index.ts:1618:23)
    at Module._extensions..js (node:internal/modules/cjs/loader:1153:10)
    at Object.require.extensions.<computed> (/Users/bmathew/Library/Caches/Cypress/10.11.0/Cypress.app/Contents/Resources/app/node_modules/ts-node/src/index.ts:1621:12)
    at Module.load (node:internal/modules/cjs/loader:981:32)
    at Function.Module._load (node:internal/modules/cjs/loader:822:12)
    at Module.require (node:internal/modules/cjs/loader:1005:19)
    at require (node:internal/modules/cjs/helpers:102:18)
    at loadFile (/Users/bmathew/Library/Caches/Cypress/10.11.0/Cypress.app/Contents/Resources/app/packages/server/lib/plugins/child/run_require_async_child.js:89:14)
    at EventEmitter.<anonymous> (/Users/bmathew/Library/Caches/Cypress/10.11.0/Cypress.app/Contents/Resources/app/packages/server/lib/plugins/child/run_require_async_child.js:116:38)
    at EventEmitter.emit (node:events:390:28)
    at EventEmitter.emit (node:domain:475:12)
    at process.<anonymous> (/Users/bmathew/Library/Caches/Cypress/10.11.0/Cypress.app/Contents/Resources/app/packages/server/lib/plugins/util.js:33:22)
    at process.emit (node:events:390:28)
    at process.emit (node:domain:475:12)

My cypress.config.ts now looks like this

import { defineConfig } from 'cypress';
import { nxComponentTestingPreset } from '@nrwl/react/plugins/component-testing';
import {
  BuildOptions,
  createConfig,
  createWebpackCommonConfig,
  createWebpackProdConfig,
} from '@personio-web/nx-plugin';
import { readCachedProjectGraph, readTargetOptions } from '@nrwl/devkit';
import {
  createExecutorContext,
  getProjectConfigByPath,
} from '@nrwl/cypress/plugins/cypress-preset';
import * as webpackMerge from 'webpack-merge';

const nxConfig = nxComponentTestingPreset(__filename)

const graph = readCachedProjectGraph();
const { targets: ctTargets, name: ctProjectName } = getProjectConfigByPath(
  graph,
  __filename
);
const ctTargetName = 'component-test';
const ctConfigurationName = process.env.NX_CYPRESS_TARGET_CONFIGURATION;

const context = createExecutorContext(
  graph,
  ctTargets!,
  ctProjectName!,
  ctTargetName,
  ctConfigurationName!
);

const options = readTargetOptions<BuildOptions>(
  {
    project: ctProjectName!,
    target: ctTargetName,
    configuration: ctConfigurationName,
  },
  context
);


const config = createConfig(options, context);
const webpackCommonConfig = createWebpackCommonConfig(config);
const webpackProdConfig = createWebpackProdConfig(config);
const finalConfig = webpackMerge.merge(
  webpackCommonConfig,
  webpackProdConfig,
);

nxConfig.devServer.webpackConfig = finalConfig;
export default defineConfig({
  component: nxConfig
})

I wonder if the component-test executor does imports differently cause I was getting the error of TypeError: MiniCssExtractPlugin is not a constructor. And I noticed the import object of MiniCssExtractPlugin looks differently when run the target build and component-test.

So I updated my webpack config like

const MiniCssPlugin = MiniCssExtractPlugin.default ? MiniCssExtractPlugin.default : MiniCssExtractPlugin;

I have updated my reproduction case. Maybe u know what could be wrong.

@blessanm86
Copy link
Author

Another thing to note is
I cannot do nxConfig.devServer.webpackConfig = finalConfig; as webpackConfig is a read-only property

@blessanm86
Copy link
Author

blessanm86 commented Nov 1, 2022

@barbados-clemens I was able to fix my issue. Turns out a cypress.json file at the root of the workspace needed to be deleted.

Can this type be loosened up?
I believe the docs could be improved for this situation.
Moving the location of tsconfig.cy.json was crucial.
I noticed the generated command file did not have the mount function in it.
I also had to add "jsx": "react" to tsconfig.cy.json

Thanks for the support.

@blessanm86
Copy link
Author

@barbados-clemens
So now since I figured out how to get it working. I'm not sure what is the best practice.

Currently, in our NX workspace, we have apps that are very thin like they just assemble components on a page.
But those components are coming from libraries and they include all their associated logic.

My question is, where should the component test be? I believe it should be part of the library instead of the app.
But for that I had to sort of create fake build target. I copied the same one I used in the app.
I had to make some changes in our webpack config to work differently for libs. But I'm not sue if its the recommended approach.

@github-actions
Copy link

This issue has been closed for more than 30 days. If this issue is still occuring, please open a new issue with more recent context.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Mar 21, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
blocked: repro needed outdated scope: testing tools Issues related to Cypress / Jest / Playwright / Vitest support in Nx type: bug
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants