nx
nx copied to clipboard
Cannot run cypress e2e tests for a node app
Current Behavior
The @nrwl/cypress:cypress builder doesn't seem to work when I pass a node app (which is using the @nrwl/node:node builder) as the devServerTarget. The node app gets compiled and served correctly, but cypress just never starts.
Expected Behavior
After compiling and serving the app, cypress should start testing.
Steps to Reproduce
Create a new node app, create a new e2e app targeting it, and try running it.
Environment
nx report
> NX Report complete - copy this into the issue template
Node : 14.18.1
OS : darwin arm64
npm : 6.14.16
nx : 14.2.4
@nrwl/angular : 14.2.4
@nrwl/cypress : 14.2.4
@nrwl/detox : Not Found
@nrwl/devkit : 14.2.4
@nrwl/eslint-plugin-nx : 14.2.4
@nrwl/express : 14.2.4
@nrwl/jest : 14.2.4
@nrwl/js : 14.2.4
@nrwl/linter : 14.2.4
@nrwl/nest : Not Found
@nrwl/next : Not Found
@nrwl/node : 14.2.4
@nrwl/nx-cloud : Not Found
@nrwl/nx-plugin : Not Found
@nrwl/react : Not Found
@nrwl/react-native : Not Found
@nrwl/schematics : Not Found
@nrwl/storybook : 14.2.4
@nrwl/web : Not Found
@nrwl/workspace : 14.2.4
typescript : 4.5.5
---------------------------------------
Community plugins:
@ng-bootstrap/ng-bootstrap: 12.1.2
@ngrx/component-store: 13.2.0
@ngrx/effects: 13.2.0
@ngrx/router-store: 13.2.0
@ngrx/store: 13.2.0
@ngrx/store-devtools: 13.2.0
@nguniversal/common: 13.1.1
@nguniversal/express-engine: 13.1.1
@nguniversal/builders: 13.1.1
having the same problems, glad someone's opened an issue :)
Hi @aleesaan and @ple16 , thank you for reporting. A node:app does not really open any network port. What are you trying to e2e a node:app or did you mean express:app?
Hi @nartc,
I have setup an example repo with the issue https://github.com/ple16/nx-node-app-e2e
So it contains a node app (@nrwl/node:node) with a Koa server. nx serve node-web-app and you can browse to http://localhost:3000 and see the message "Hello World"
The cypress app(@nrwl/cypress:cypress) is configured with "devServerTarget": "node-web-app:serve"
nx e2e node-web-app-e2e does start the node app but doesn't continue with the cypress tests
@ple16 Thanks for the repro. I'm taking a look now
@ple16 Gotcha. Let me explain
A node:app from Nx perspective can be a CLI application or a Web Server like you just shared. Hence, node:node executor (aka serve) doesn't have options for host and port like the other web-based executors (eg: nrwl/web:file-server). The host/port combination of a node:app as a web server is constructed by the consumers via the server's entry point rather than the executor's options.
The nrwl/cypress:cypress executor expects the devServerTarget to output a baseUrl to use but node:node doesn't return any baseUrl, cypress:cypress failed. And this is the intended behavior as explained above.
What you can do is to explicitly add baseUrl to nrwl/cypress:cypress executor

Extra info: You're probably aware but by default Cypress expects text/html with cy.visit(). In the case of a web server, you'd probably need cy.request() instead. Unless your web server returns some kind of views
Thanks @nartc that makes sense however the suggestion of just adding the baseUrl doesn't appear to have any affect
> nx run node-web-app-e2e:e2e
chunk (runtime: main) main.js (main) 1.17 KiB [entry] [rendered]
webpack compiled successfully (6f11dc4313e30e09)
Debugger listening on ws://localhost:9229/2f4cdf2d-6b5c-4be0-a7f5-ee4243a0b4a4
Debugger listening on ws://localhost:9229/2f4cdf2d-6b5c-4be0-a7f5-ee4243a0b4a4
For help, see: https://nodejs.org/en/docs/inspector
Server listening on http://localhost:3000
I might have to switch to express (or take a look at the express generator and write a Koa version)
I understand the need to use cy.request() we've already done that to validate a graphql api
Actually @nartc I've just noticed @nwrl/express:app also sets up a serve executor using node "executor": "@nrwl/node:node", so it looks like cypress wouldn't be compatible with the express generator?
@ple16 hm...what if you:
- Remove
devServerTargetfromnrwl/cypress:cypress(keeps thebaseUrlthere) - Run
servein a terminal - Run
e2ein a separate terminal
Just want to see if it works that way. If yes, there might be a better option.
Hi @nartc,
Yes it does work that way.
I've also just seen this pr https://github.com/nrwl/nx/pull/11325 and given that's it's switched to using the node executor for a custom server I think that's possibly going to introduce the issue with cypress being able to serve.
I think I'm going to have a crack at writing a little executor as per https://nx.dev/executors/creating-custom-builders that just calls the node executor but takes an additional prop when I can set the baseUrl and return just see if I can't trick cypress into running
Hi @nartc,
A node:app does not really open any network port. What are you trying to e2e a
node:appor did you meanexpress:app?
I indeed meant an express:app which uses the @nrwl/node:node builder, sorry about that! I'm not sure how to test that with cypress without needing to run two terminals (e.g. on CI).
@ple16 and @aleesaan, thank you for the info. Sorry for the delayed response. I'm still looking for a solution for this to work in CI. Will report back as soon as I make some progress.
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! ๐
Not stale
@nartc thank you, I did succeed by using spawn and hooking into the output of the command... can't say I'm truly happy with it but I'll paste it in as an example if anyone else runs into this thread.
It's a bit wild but basically we use it to spin up multiple apps to e2e the system as a whole when really I should be mocking our GraphQL layer. While I'm fairly experienced, mocking subscriptions is something I've never really tried before (I only started using cypress in anger 3 days ago)
import { ChildProcessWithoutNullStreams, spawn } from 'child_process';
import { ExecutorContext, logger } from '@nrwl/devkit';
type ReadyCheck = {
check: RegExp;
ready: boolean;
};
function checkReadiness(readyChecks: ReadyCheck[], text: string): boolean {
for (const readyCheck of readyChecks) {
if (readyCheck.check.test(text)) {
logger.info(text);
readyCheck.ready = true;
}
}
return readyChecks.every(({ ready }) => ready);
}
export async function startAppUnderTest(
devServerTarget: string,
healthchecks: string | string[],
prod: boolean,
context: ExecutorContext
): Promise<ChildProcessWithoutNullStreams> {
const readyChecks: ReadyCheck[] = Array.isArray(healthchecks)
? healthchecks.map((healthcheck) => ({ check: new RegExp(`\\b${healthcheck}\\b`, 'gi'), ready: false }))
: [{ check: new RegExp(`\\b${healthchecks}\\b`, 'gi'), ready: false }];
return new Promise<ChildProcessWithoutNullStreams>((resolve, reject) => {
const prodArg = prod ? '--prod' : '';
const testTarget = spawn(
`nx run-many --target=serve --projects=${devServerTarget} ${prodArg} --parallel=100 --output-style=stream`,
{
cwd: context.cwd,
shell: true,
stdio: 'pipe',
}
);
testTarget.stdout.on('data', async (data) => {
const text = data.toString().trim();
logger.log(text);
if (checkReadiness(readyChecks, text)) {
resolve(testTarget);
}
});
testTarget.stderr.on('data', async (data) => {
// TODO: dependsOn of devServerTarget is outputting in stdout, but devServerTarget is coming out in stderr???
const text = data.toString().trim();
logger.log(text);
if (checkReadiness(readyChecks, text)) {
resolve(testTarget);
}
});
testTarget.on('error', (e) => {
logger.error(`Failed to start subprocess: ${e.message}`);
reject(e);
});
testTarget.on('exit', (code, signal) => {
logger.error(`subprocess exited: ${code}:${signal}`);
reject(new Error('subprocess exited'));
});
testTarget.on('close', (code, signal) => {
logger.error(`subprocess closed: ${code}:${signal}`);
reject(new Error('subprocess closed'));
});
});
}
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! ๐
Not stale. @ple16 thank you for the example.
We're still looking for an optimal way to do this.
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! ๐
Still not stale ๐
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! ๐
still no stale
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! ๐
Not stale ๐
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! ๐
Still not stale ๐
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! ๐
Hey @nartc, any update? I keep bumping the issue every two weeks ๐
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! ๐
Hi all, Thanks for opening this issue, I'm having it as well for a project I'm working on. Has there been any progress? Is the spawn solution suggested by @ple16 the best one yet? :)
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! ๐
Not stale