I am trying to run two scripts at once with concurrently
. The basic command looks something like this:
concurrently -k --success first "node ./tools/mock-webapi/mock-webapi.js" "npm run test-single-run"
Which in turn calls (local):
"test-single-run": "karma start --single-run --browsers ChromeHeadless"
Or on remote (teamcity host):
"test-teamcity": "karma start --reporters teamcity --single-run --browsers ChromeHeadless",
The tests run just fine (local & remote). However, I keep getting exit code 1. Even if I use concurrently -k --success first
I still get a code 1
even with --success first
.
[1] 09 05 2018 17:56:54.032:WARN [launcher]: ChromeHeadless was not killed in 2000 ms, sending SIGKILL. [1] npm run test-single-run exited with code 0 --> Sending SIGTERM to other processes.. [0] node ./tools/mock-webapi/mock-webapi.js exited with code 1
I tried various ways for json-server
to gracefully receive this signal. Nothing seems to work.
mock-webapi.js
process.on('SIGTERM', function (code) { console.log('Handle SIGTERM', process.pid, code); exitCode = 0; server.close(function () { process.exit(0); }); }); process.on('SIGKILL', function (code) { console.log('SIGKILL received...', code); exitCode = 0; server.close(function () { process.exit(0); }); }); process.on('SIGINT', function (code) { console.log('SIGINT received...', code); exitCode = 0; server.close(function () { process.exit(0); }); });
Finally found a solution to this problem. I wrote a little script that runs the mock webapi and the karma tests as child processes.
test-single-run-single-process.js
const spawn = require('cross-spawn'); /** * Running the tests requires a mock webapi which is gladly provided by json-server. * Running the tests in teamcity requires that everything is executed from one terminal. * Different mock data sets can be easily used if the mockapi is a child process. * We do not need to keep the mockWebapi alive between trials. * * After all the tests have succeeded we then close the json-server by sending (SIGINT). * Finally the process will exit with code 0, which means for TeamCity that all is OK. * Now it can proceed with the next build step. * So basically we can run the tests both standalone and in one single terminal. * Notice that the mockWebapi is a forked process so we can send messages to it. * * <!> Failed approach - Closing the mockwebapi * Using kill command leaves no option for gracefull shutdown. * SIGINT or SIGTERM signals are not received by the mockWebapi. * The server will stay active keeping the 3000 port busy. * * mockWebapi.kill('SIGINT'); */ const fork = require('child_process').fork const mockWebapi = fork('./tools/mock-webapi/mock-webapi.js') const karma = spawn( `karma start --single-run --browsers ChromeHeadless`, [], { stdio: 'inherit' } ); // 1) Listen to karma exit karma.on('close', (code, signal) => { console.log('Karma closed. Code:', code, 'Signal:', signal); code === 0 && gracefullyCloseMockWebapi(true); }); karma.on('error', (code, signal) => { console.log('Karma error. Code:', code, 'Signal:', signal); gracefullyCloseMockWebapi(false); }); let gracefullyCloseMockWebapi = (testsCompletedOk) => { console.log('Gracefuly close webapi. Tests completed ok:', testsCompletedOk); mockWebapi.send({ testsCompletedOk }); }; // 2) Finish the job, pass the exit code from mockWeabpi to the command line mockWebapi.on('close', (code, signal) => { console.log('Mock webapi closed. Code:', code, 'Signal:', signal); process.exit(code); });
mock-webapi.js
// Some project specific work is done before starting the server /** * <!> Failed approach - concurrently * Dispatching the following command will exit with code 1. * The SIGTERM or SIGINT handlers are not called after the tests are done. * concurrently --kill-others --success first * "node ./tools/mock-webapi/mock-webapi.js" "npm run test-single-run" * * process.on('SIGINT', (code) => { * server.close(function () { * process.exit(0); * }); * }); * * <!> Working approach * Spawning a process child and sending it a message to self-close. * Double check it with "echo Exit Code is %errorlevel%" after the processes terminated. */ function setupJsonServer(mocks, routes) { let server = jsonServer.create(), port = 3000; const router = jsonServer.router(mocks); server.use(jsonServer.defaults()); server.use(jsonServer.rewriter(routes)); server.use(router); server.listen(port, () => { console.log(`JSON Server is running at ${port}`) console.log('Process id is', process.pid); }); process.on('message', function ({ testsCompletedOk }) { console.log('Mockwebapi will terminate. Tests completed ok', testsCompletedOk) process.exit(testsCompletedOk === true ? 0 : 1) }); }
Additional reading: