nrwl / nx

Smart Monorepos Β· Fast CI
https://nx.dev
MIT License
23.61k stars 2.36k forks source link

Run e2e with multiple devServerTargets #3748

Closed Bielik20 closed 7 months ago

Bielik20 commented 4 years ago

Description

I would like to be able to serve both frontend app and backend api before running e2e tests.

Currently I can serve multiple projects using @nrwl/workspace:run-commands as demonstrated in here: https://github.com/nrwl/nx/issues/1482

But when running e2e tests with devServerTarget set to such a command:

        "serve": {
          "builder": "@nrwl/workspace:run-commands",
          "options": {
            "commands": [
              {
                "command": "nx serve api"
              },
              {
                "command": "nx serve desktop"
              }
            ],
            "parallel": true
          }
        },

it doesn't start tests because it doesn't know when serve command is ready :/

I tried to work it around with:

        "e2e-ci": {
          "builder": "@nrwl/workspace:run-commands",
          "options": {
            "commands": [
              {
                "command": "nx serve desktop-with-api"
              },
              {
                "command": "nx e2e desktop-e2e"
              }
            ],
            "parallel": true
          }
        },

But that fails to complete at the end. And readyWhen doesn't stop processes.

Motivation

Running test in CI environment.

Suggested Implementation

I don't know how devServerTarget knows that serve process is ready in the first place.

I image it would have to respond similarly to run-commands.

Alternate Implementations

Perhaps base it on baseUrl (https://github.com/nrwl/nx/issues/1614), but there is still issue of stoping processes.

FrozenPandaz commented 4 years ago

Yeah , that's a very interesting use case and I don't think we have docs on that at the moment.

Check out this episode of @ZackDeRose's live stream though where he uses @angular-devkit/architect:allOf to achieve what you are describing.

https://youtu.be/4q1tIar795c?t=882

ZackDeRose commented 4 years ago

@Bielik20 here's a link to that project in the video @FrozenPandaz linked: https://github.com/nrwl/zack-live-stream/blame/master/workspace.json#L74

Bielik20 commented 4 years ago

Thank you for the responses. It works to some degree. It starts both frontend and backend, and tests start afterword.

Note: It is important to define devServerTarget AND baseUrl for it to work as mentioned in video.

When tests finish, the process doesn't end, it hangs there. Frontend application ends (doesn't matter if it is first or second in targets): nothing from lsof -t -i tcp:4200 Backend still runs: lsof -t -i tcp:3333 returns one process.

Note: I am using React, not Angular in this project and installed @angular-devkit/architect simply with yarn add -D @angular-devkit/architect. I don't know if that matters.

I also tired concat instead of allOf as mentioned in the video but that makes no difference.


I will try more during the week, maybe update some stuff (although repo is 2 weeks old). If you have any insight to what may be casing those problems, let me know. Thank you in advance.

Bielik20 commented 4 years ago

Sadly on the newest version (10.2.1) it is still the same 😞

PaskeS commented 3 years ago

I had the same issue my friend, I fixed using wait-on. Forgive the code react-scripts is painful to work with on Nx

"options": {
  "commands": [
    {
       "command": "node ../../node_modules/react-scripts/bin/react-scripts start"
     },
     {
       "command": "node ../../node_modules/wait-on/bin/wait-on http://localhost:3000 && node ../../node_modules/cypress/bin/cypress run"
     }
   ],
}
webberwang commented 3 years ago

@PaskeS Could you share your solution for closing the server after cypress runs successfully?

vaunus commented 3 years ago

Had the same issue here and found a solution.

I am starting several targets using the NX @angular-devkit/architect:allOf runner in workspace.json as follows and as described above:

"serve-for-e2e": {
  "builder": "@angular-devkit/architect:allOf",
  "options": {
    "targets": [
      {
        "target": "frontend:serve"
      },
      {
        "target": "api:serve"
      }
    ]
  }
}

The above works well and starts the targets before e2e runs but it hangs at the end.

In my case the frontend stops just fine as I guess the NX runner handles this correctly. But for the backend I have a listening socket so my node process never closes when the e2e finishes. I noticed no kill events being received by that process, but what I did notice is that it does receive a process.on('disconnect', ... when the NX runner that started it closes. This event fires when a parent process disconnects from a child so we know the NX runner has been closed.

I've therefore added the following code to my server entry point to handle this event and shut the server down gracefully, only if the context is within an NX runner as per the environment variable:

if (process.env.NX_INVOKED_BY_RUNNER === 'true') {
  process.on('disconnect', async () => {
    await db.disconnect()
    process.exit(0)
  })
}

Now e2e closes reliably after running every time. Hope this helps someone! πŸ™‚

jase88 commented 3 years ago

Couldn't get it working with

"serve-for-e2e": {
  "builder": "@angular-devkit/architect:allOf",
  "options": {
    "targets": [
      {
        "target": "frontend:serve"
      },
      {
        "target": "api:serve"
      }
    ]
  }
}

Both projects are served, but the cypress e2e tests won't start. If I take out api:serve (which in my case is node app using builder @nrwl/node:execute) as target, it works as expected. Might this be a bug?

tfohlmeister commented 3 years ago

I found a good solution in the cypress guides using start-server-and-test.

Install dependency: npm install --save-dev start-server-and-test

Add or update your e2e script in package.json: (adapt api:serve and the port to match your BE project and port)

"scripts": {
   "e2e": "start-server-and-test \"nx run api:serve\" http://localhost:3333",
}

Run e2e and server in one command: (the part after -- is provided as an argument to start-server-and-test and would start a FE / E2E project of your choice)

npm run e2e -- "nx affected:e2e"

Thats it. I am using this in a Github Action pipeline and it works great!

jase88 commented 3 years ago

I also managed to get it working within my angular.json/worksapce.json I moved the current e2e target to _e2e and added a run command before it.

"e2e": {
  "builder": "@nrwl/workspace:run-commands",
  "options": {
    "commands": [
      {
        "command": "start-server-and-test \"nx serve api:serve\" 3000 \"nx _e2e my-app-e2e\""
      }
    ]
  }
},
"_e2e": {
  "builder": "@nrwl/cypress:cypress",
  "options": {
    "cypressConfig": "apps/my-app-e2e/cypress.json",
    "tsConfig": "apps/my-app-e2e/tsconfig.e2e.json",
    "devServerTarget": "my-app:serve"
  }
},

this way I can still use nx e2e my-app-e2e without custom package.json scripts πŸ˜„

Thank you

github-actions[bot] commented 3 years ago

This issue has been automatically marked as stale because it hasn't had any recent activity. It will be closed in 14 days if no further activity occurs. If we missed this issue please reply to keep it active. Thanks for being a part of the Nx community! πŸ™

Bielik20 commented 3 years ago

I still think this is not solved. I actually gave up the idea of running backend and frontend together for e2e tests but I still think this is a valid use case. In my opinion it would be easiest to just make cypress runner accept multiple targets.

tmclean15 commented 3 years ago

+1 for allowing the cypress runner to accept multiple targets. Is there any progress on this?

Camo30 commented 3 years ago

I highly agree that this is a valid use case. Please note that the Angular CLI supports multiple dev server targets while NX doesn't, see documentation at https://angular.io/cli/e2e

hehoo commented 3 years ago

+1 for allowing specify multiple targets for cypress test

jwmickey commented 3 years ago

I was able to get this working using the readyWhen option in a custom serve-all target for the UI app. This tells nx that it's safe to start Cypress once the readyWhen text appears in the console output. You can customize this text for your needs of course.

The following snippets are from angular.json:

target for UI app, assumes existing "serve" targets for both apps

"serve-all": {
  "builder": "@nrwl/workspace:run-commands",
  "options": {
    "commands": [
      {
        "command": "nx serve my-ui-app"
      },
      {
        "command": "nx serve my-api-app"
      }
    ],
    "parallel": true,
    "readyWhen": "Angular Live Development Server is listening"
  }
}

target for e2e


"e2e": {
  "builder": "@nrwl/cypress:cypress",
  "options": {
    "devServerTarget": "my-ui-app:serve-all"
  }
}
Elte156 commented 3 years ago

I was able to get this working using the readyWhen option in a custom serve-all target for the UI app. This tells nx that it's safe to start Cypress once the readyWhen text appears in the console output. You can customize this text for your needs of course.

@jwmickey I tried your solution and it started my backend and frontend, but it did not kill these processes cleanly after the tests ran. I had to find the PID via the allocated ports and kill them manually.

@tfohlmeister 's solution using start-server-and-test worked in my scenario.

gerardcastell commented 3 years ago

Here I'm attaching my solution for microfrontends with Nx, hope it helps:


      "root": "apps/shell-e2e",
      "sourceRoot": "apps/shell-e2e/src",
      "projectType": "application",
      "targets": {
     "serve-mfe-for-testing": {
          "executor": "@nrwl/workspace:run-commands",
          "options": {
            "commands": [
              "nx serve mfe1",
              "nx serve mfe2",
              "nx serve mfe3"
            ],
            "readyWhen": "Angular Live Development Server is listening on localhost:4203" <--- This is the app with the largest computation build but if you cannot know this you can separate the several 'nx serve' and add a ready when for each port, eventually merge all them in this command
          }
        }
        "e2e": {
          "executor": "@nrwl/cypress:cypress",
          "options": {
            "cypressConfig": "apps/shell-e2e/cypress.json",
            "devServerTarget": "shell:serve-mfe-for-testing:development"
          },
          "configurations": {
            "production": {
              "devServerTarget": "shell:serve-mfe-for-testing:production"
            }
          }
        },
vigneshpushkaran commented 2 years ago

Is anyone facing issue with environments to run multiple apps. config,

    "xxx": {
      "projectType": "application",
      "root": "apps/xxx",
      "sourceRoot": "apps/xxx",
      "targets": {
    "serve_for_e2e": {
          "builder": "@nrwl/workspace:run-commands",
          "options": {
            "commands": [
              {
                "command": "nx run-many --parallel --projects=xxx,yyy,zzz"
              }
            ],
            "cwd": "apps"
          }
        },
        "e2e": {
          "executor": "@nrwl/cypress:cypress",
          "options": {
            "cypressConfig": "apps/xxx/tests/e2e/cypress.json",
            "devServerTarget": "xxx:serve_for_e2e"
          }
        }
}

If I try to run e2e with above config, I am getting error(listen EADDRINUSE: address already 9000). beacuse, env config of xxx is automatically overriding env configs(values) of yyy and zzz apps. Thanks in advance

arthurgeek commented 2 years ago

I'm using start-server-and-test as suggested in this thread, the e2e tests pass, but then start-server-and-test sends a SIGTERM to nx api:serve process, which ends with an error.

β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
>  NX   SUCCESS  Running target "e2e" succeeded
β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
>  NX   ERROR  Running target "api:serve" failed
  Failed tasks:
  - api:serve
  Hint: run the command with --verbose for more details.

Any solutions to this?

veloware commented 2 years ago

start-server-and-test \"nx run api:serve\" http://localhost:3333

Also hitting this problem, if anyone has any idea?

chrisui commented 2 years ago

Working on a migration of a large repo with lots of small services (libraries) where I think it would be really great if nx had a first-class way of being able to run any target as a persisted task (optionally daemonised) - ie. typically "serve". That way I can ask nx to serve me my www app and then have nx, through my definition of dependencies, run serve on all the relevant dependencies.

Additional Context

See slack for additional context where I was trying to ask community for help.

How should I define target dependencies for something like β€œserve” in a dev environment where each project’s serve is likely a persisted foreground process? For example a nextjs dev app, rebuilding and logging on change, which depends on an api project, which is building and logging using a node-esbuild watch server/executor, which also depends on a db project which brings up a postgres docker container in the foreground. These all define a "serve" target and I have target dependencies set to {"serve": [{"target": "serve", "projects": "dependencies"}]} The problem I’m seeing is that nx gets stuck at the top of the dependency graph when the database serve task doesn’t finish.. If I use "run-commands" executor then I can define "readyWhen" however that does not seem to be a consistent api used by other packages executors. Apologies if I’ve missed a crucial bit of documentation.

Also see "creating custom executors" which shows an example of how you might solve this typically, today, in user-land with a somewhat hard-coded approach (which is tricky to scale to many many services you may want to run individually). What I think nx can do is provide a way of re-using it's dependency knowledge and a new [persisted] "mode" of running targets to help make this use-case better supported.

Contribution

If this would fit in with the scope of the project I would be happy to contribute - write an rfc with some guidance + implement.

Possible features

  1. [need] persist: boolean flag (run executor but don't expect it to resolve)
  2. [nice-to-have] daemon: boolean flag (run executor in background)
  3. [nice-to-have] logPath: string allowing developers to see what's happening with anything they may have ran as a daemon
  4. [need] readyWhen: string | (result: unknown) => boolean by default persisted targets would spawn the task and move onto dependents straight away so this would allow developers to define better when a task is actually ready to be depended on if needed
  5. [nice-to-have] propagateExit: boolean would allow developers to signal behaviour that says "if this target that I expect to be persisted, exits for any reason, then I should exit the whole program"
  6. [nice-to-have] retry: number would default to 0 and allow for resolved tasks to be retried n times if there was an error
chrisui commented 2 years ago

Note in the meantime I wrote a custom executor which walks the dependencies of a targeted project and runs all the "serve" targets. Only downside is task logs are also interleaved meaning it's difficult as a user to know what task is logging what. Ideally there would be another nx feature to allow control of logging.

Code here: https://gist.github.com/chrisui/a3cdf050bf18eccd499fba29b609b90a

ryanbas21 commented 2 years ago

Bumping this:

We have a use case where we have a simple express server, a webpack dev server, and playwright running. The only real way to do this with our setup is

run concurrently with playwright test and nx serve myExpressApi where playwright test also spins up the web server using the global config option webServer option which waits for the web server to spin up before executing playwright tests.

This leads to an extremely noisy console output. It's far from ideal but allows us to use the playwright runner which is absurdly fast.

If we could have some way to do the following it would be amazing

This is purely a local development problem because in CI we are moving to a model where we execute these tests on a deployed preview env. But I feel it makes our test cases flakey.

mashtii85 commented 2 years ago

on your front end project paste this command on project.json "serve:e2e": { "builder": "@nrwl/workspace:run-commands", "options": { "commands": ["nx api","nx e2e frontend-e2e "], "parallel": true } }, and create a script on package.json named "serve:e2e":"nx serve:e2e"

barbados-clemens commented 2 years ago

Hi all this looks to be something that would be a benefit to have as it's been referenced by a handful of other issues. I'll try to take some time to figure out what it will take to make this happen. unsure what the timeline would look like but adding it to my todo list!

jasiene commented 2 years ago

Any updates on this?

kwiniarski97 commented 2 years ago

Would it be possible for this solution to be part of nx-commands rather than just cypress executor? I believe this problem is more generic. I am facing this problem right now with playwright, but I guess it can be a problem with any E2E framework.

Previously, I was using a npm-run-all package with -p (parallel) and -r (race) flags (more details). I guess if we could somehow specify that the parallel commands should run in the same race mode, that would handle all the aforementioned issues.

barbados-clemens commented 2 years ago

@kwiniarski97 you've pretty much hit the nail on the head. it's not so much e2e but the need/want to compose targets together. if you have the angular devkit installed or don't mine installing it you can look at using something like their allOf or concat builder. or you can look at making your own 'meta' executor right now if it's something you're really needing that you don't mine making custom.

and since this should actually be at the core of nx, it's tricky to try to make some kind of patch implementation within cypress executor at the moment. but something I still plan to try to play with.

wrslatz commented 2 years ago

Cross-posting related request https://github.com/nrwl/nx/issues/5570

lzhoucs commented 2 years ago

Hi, I also needed this feature and so I took a look since no one was working on it.

I created above PR. Please let me know if there's any issues.

I tested the change in my app and It worked well for me. I also published a temporary package to unblock myself before the PR can be merged or any similar feature is developed. If anyone would like to give it a shot, this is what I did: install the temporary modified package: npm install -D @lzhoucs/nx-cypress, then specify multiple targets like this:


    "e2e": {
      "executor": "@lzhoucs/nx-cypress:cypress",
      "options": {
        "devServerTarget": "app1:serve:cfg1, app2:serve:cfg2",
        ...
      }
    },

last target yielded baseUrl will be passed to cypress, so make sure to put the main one last.

Everything else works the same as before, e.g: all launched dev servers are managed by nx and will be shutdown after tests.

FirstWhack commented 1 year ago

+1 for "this has nothing to do with E2E or Cypress"

Imagine a monorepo where all packages are consumed as Federated Modules, for E2E, or just to run the project locally at all, you will want to run something like serve N times for N packages, with the main Application consuming all of them.

In example this goes something like:

nx start @myapp/host
> NX   Running target start for project @myapp/host and 6 task(s) it depends on
{6 dependent packages run their dependent tasks before `serve`ing their respective `/dist`}
@myapp/lib{1-6}: Accepting connections at {host}
> nx run @myapp/host:start
{all devServers were shutdown when `readyWhen` condition was met, hosed}

I tried to do this with readyWhen, but that kills the process once ready, thus leaving none of dependent's devServers running when the Application finally runs.

Is this a use-case for a "persist" property?

TriPSs commented 1 year ago

I created a plugin for this, see this package.

jaqua commented 7 months ago

I'm using start-server-and-test as suggested in this thread, the e2e tests pass, but then start-server-and-test sends a SIGTERM to nx api:serve process, which ends with an error.

β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
>  NX   SUCCESS  Running target "e2e" succeeded
β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
>  NX   ERROR  Running target "api:serve" failed
  Failed tasks:
  - api:serve
  Hint: run the command with --verbose for more details.

Any solutions to this?

@arthurgeek Did you solve it in your case? I got the exact same problem... @TriPSs After installation the command nx g @nx-extend/e2e-runner:add can't work as Cannot find generator 'add' in generator.json. How do I use your plugin?

nicky-lenaers-phoenicks commented 7 months ago

I was playing around with the new Nx Inferred Tasks for Cypress Plugin (apparently the working title was Project Crystal and it is available since Nx 18) and I configured multiple servers using cypress.config.ts like so:

import { nxE2EPreset } from '@nx/cypress/plugins/cypress-preset';

import { defineConfig } from 'cypress';

export default defineConfig({
  e2e: {
    ...nxE2EPreset(__filename, {
      cypressDir: 'src',
      webServerCommands: {
        default: 'nx run-many --target serve --projects=tag:my-tag'
      },
    }),
    baseUrl: 'http://localhost:4200',
  },
});

The tag my-tag can be used to "mark" all apps that need to run before running the e2e test (but you can use any tag you like). I have found this to be the cleanest configuration as it also obsoletes additional packages such as start-server-and-test.

FrozenPandaz commented 7 months ago

Yes! This capability should be possible now with the new Cypress dev server Plugin.

Now that is command driven... you could use a Nx run-many command to run multiple targets. :D Which is way more powerful than providing Nx targets in an executor option.

Please give that a try and open any new issues you have with that approach. I'm going to mark this issue as fixed by this new capability. If you have trouble with it, please open new issues and we'd be happy to help with them.

jaqua commented 7 months ago

@nicky-lenaers-phoenicks But with that backend and frontend are served without taking care of running order. Couldn't this be a problem? I need to have a running backend to get the frontend running, isn't it?

github-actions[bot] commented 6 months ago

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.