How can I kill nodemon process after running mocha tests? - node.js

I need to have a gulp task that starts the server, runs mocha tests against it and finally closes it. I have the following code:
var mocha = require('gulp-mocha');
var nodemon = require('nodemon');
gulp.task('my-integration-tests', function () {
return nodemon({ script: './server.js' })
.on('start', function () {
gulp.src(['./mySpecs.spec.js'])
.pipe(mocha());
});
});
The server is successfully started and the tests are run. However after this the process created by nodemon is still alive. Is there a way to instruct nodemon to close upon completion? Also having the application opening and closing in the same process as the mocha tests is not an option with the current configuration.
UPDATE:
Apart from ThomasBromans answer, I came up with this solution which seems to work in my case. Whenever gulp-mocha finishes the tests it will kind of emit an 'end' event. When this happens we only need to emit 'quit' on the child process then kill the main process, like so:
gulp.task('my-integration-tests', function () {
var childProc = nodemon({ script: './server.js' });
childProc.on('quit', function () {
console.log('event emitted, child process is being killed');
})
childProc.on('start', function () {
gulp.src(['./mySpecs.spec.js'])
.pipe(mocha())
.once('end', function () {
console.log('mocha stuff ended. time to kill processes');
childProc.emit('quit');
setTimeout(function () {
console.log('kill main process');
process.exit();
}, 1500);
});
});
});
Unfortunately I still need the timeout between the child process being killed and the killing of the main process, if I remove the timeout it happens that the child process remains hanging. This solution is of course open to improvements.

You can exit the process with process.exit(). Just add another .pipe. Your task will look like this:
gulp.task('my-integration-tests', function () {
return nodemon({ script: './server.js' })
.on('start', function () {
gulp.src(['./mySpecs.spec.js'])
.pipe(mocha())
.pipe(process.exit());
});
});
EDIT running tasks in a sequence (I am not sure this works without any changes):
var gulp = require('gulp'),
mocha = require('gulp-mocha'),
nodemon = require('nodemon'),
runSequence = require('run-sequence');
gulp.task('nodemon', function() {
return nodemon({script: './server.js'});
});
gulp.task('mocha', function() {
return mocha();
});
gulp.task('stop', function() {
process.exit();
});
gulp.task('my-integration-tests', function () {
runSequence('nodemon',
'mocha',
'stop');
});

Related

How to make gulp not finish task before it's done?

I have this gulp task, and it works fine, but when looking at the log, it always says Starting Task, then immediately Finished Task, then it starts logging everything. How do I make it wait until everything is complete until it says it's finished? I think it's due to the function being async so done() gets called right away, but I'm not sure what to do.
gulp function
gulp.task("img-projects", function(done) {
gulp.src('projects/*/images/**/*.*')
.pipe(imagemin())
.pipe(rename(function (path) {
path.dirname = path.dirname.replace('/images','');
console.log('path');
return path;
}))
.pipe(gulp.dest('public'));
done();
});
output:
[19:11:55] Starting 'js-projects'...
[19:11:55] Finished 'js-projects' after 34 ms
[19:11:55] path
[19:11:55] path
[19:11:55] path
All you need to add is an on('end', ...) listener to wait until the gulp stream is complete before calling done():
gulp.task("img-projects", function(done) {
gulp.src('projects/*/images/**/*.*')
.pipe(imagemin())
.pipe(rename(function (path) {
path.dirname = path.dirname.replace('/images','');
console.log('path');
return path;
}))
.pipe(gulp.dest('public'))
.on('end', done);
});
Sources:
How do you run a gulp "on end" task but only at the end of the current task?
gulp API (docs for src()/dest()/etc): https://github.com/gulpjs/gulp/tree/master/docs/api
node.js streams API which provides on(...): https://nodejs.org/api/stream.html
similar issue: https://github.com/gulpjs/gulp/issues/1181#issuecomment-126694791
Try,
The gulp functions are async
gulp.task("img-projects", function() {
return gulp.src('projects/*/images/**/*.*')
.pipe(imagemin())
.pipe(rename(function (path) {
path.dirname = path.dirname.replace('/images','');
console.log('path');
return path;
}))
.pipe(gulp.dest('public'));
});

jest doesn't wait beforeAll resolution to start tests

What I test: An express server endpoints
My goal: automate API tests in a single script
What I do: I launch the express server in a NodeJS child process and would like to wait for it to be launched before the test suite is run (frisby.js endpoints testing)
What isn't working as expected: Test suite is launched before Promise resolution
I rely on the wait-on package which server polls and resolves once the resource(s) is/are available.
const awaitServer = async () => {
await waitOn({
resources: [`http://localhost:${PORT}`],
interval: 1000,
}).then(() => {
console.log('Server is running, launching test suite now!');
});
};
This function is used in the startServer function:
const startServer = async () => {
console.log(`Launching server http://localhost:${PORT} ...`);
// npmRunScripts is a thin wrapper around child_process.exec to easily access node_modules/.bin like in package.json scripts
await npmRunScripts(
`cross-env PORT=${PORT} node -r ts-node/register -r dotenv/config src/index.ts dotenv_config_path=.env-tests`
);
await awaitServer();
}
And finally, I use this in something like
describe('Endpoints' () => {
beforeAll(startTestServer);
// describes and tests here ...
});
Anyway, when I launch jest the 'Server is running, launching test suite now!' console.log never shows up and the test suite fails (as the server isn't running already). Why does jest starts testing as awaitServer obviously hasn't resolved yet?
The npmRunScripts function works fine as the test server is up and running a short while after the tests have failed. For this question's sake, here's how npmRunScripts resolves:
// From https://humanwhocodes.com/blog/2016/03/mimicking-npm-script-in-node-js/
const { exec } = require('child_process');
const { delimiter, join } = require('path');
const env = { ...process.env };
const binPath = join(__dirname, '../..', 'node_modules', '.bin');
env.PATH = `${binPath}${delimiter}${env.PATH}`;
/**
* Executes a CLI command with `./node_modules/.bin` in the scope like you
* would use in the `scripts` sections of a `package.json`
* #param cmd The actual command
*/
const npmRunScripts = (cmd, resolveProcess = false) =>
new Promise((resolve, reject) => {
if (typeof cmd !== 'string') {
reject(
new TypeError(
`npmRunScripts Error: cmd is a "${typeof cmd}", "string" expected.`
)
);
return;
}
if (cmd === '') {
reject(
new Error(`npmRunScripts Error: No command provided (cmd is empty).`)
);
return;
}
const subProcess = exec(
cmd,
{ cwd: process.cwd(), env }
);
if (resolveProcess) {
resolve(subProcess);
} else {
const cleanUp = () => {
subProcess.stdout.removeAllListeners();
subProcess.stderr.removeAllListeners();
};
subProcess.stdout.on('data', (data) => {
resolve(data);
cleanUp();
});
subProcess.stderr.on('data', (data) => {
reject(data);
cleanUp();
});
}
});
module.exports = npmRunScripts;
I found the solution. After trying almost anything, I didn't realize jest had a timeout setup which defaults at 5 seconds. So I increased this timeout and the tests now wait for the server promise to resolve.
I simply added jest.setTimeout(3 * 60 * 1000); before the test suite.
In my case, it caused by the flaw of the beforeAll part. Make sure the beforeAll doesn't contain any uncaught exceptions, otherwise it will behaves that the testing started without waiting for beforeAll resolves.
After much digging I found a reason for why my beforeAll didn't seem to be running before my tests. This might be obvious to some, but it wasn't to me.
If you have code in your describe outside an it or other beforeX or afterY, and that code is dependent on any beforeX, you'll run into this problem.
The problem is that code in your describe is run before any beforeX. Therefore, that code won't have access to the dependencies that are resolved in any beforeX.
For example:
describe('Outer describe', () => {
let server;
beforeAll(async () => {
// Set up the server before all tests...
server = await setupServer();
});
describe('Inner describe', () => {
// The below line is run before the above beforeAll, so server doesn't exist here yet!
const queue = server.getQueue(); // Error! server.getQueue is not a function
it('Should use the queue', () => {
queue.getMessage(); // Test fails due to error above
});
});
});
To me this seems unexpected, considering that code is run in the describe callback, so my impression was that that callback would be run after all beforeX outside the current describe.
It also seems this behavior won't be changed any time soon: https://github.com/facebook/jest/issues/4097
In newer versions of jest (at least >1.3.1) you can pass a done function to your beforeAll function and call it after everything is done:
beforeAll(async (done) => {
await myAsyncFunc();
done();
})
it("Some test", async () => {
// Runs after beforeAll
})
More discussions here: https://github.com/facebook/jest/issues/1256

How to kill node process within mocha test

Within my tests, I'm using require('child_process').exec to run "npm run start-app" which starts up a webpack-dev-server. After my tests have run I want to kill the process that I have started.
Currently I am able to successfully kill the process when running "mocha tests.js" directly. However, when I run the tests using "npm run tests" which calls "mocha tests.js", the process is not killed. Is this because the node process is blocking me from killing the process?
I am killing the process by discovering pids using ps-tree and using a kill -9 or taskkill depending on operating system.
test.after(function () {
psTree(appStartProcess.pid, function (err, children) {
if(/^win/.test(process.platform)) {
for(var i = 0; i < children.length; i++) {
exec('taskkill /pid ' + children[i].PID + ' /T /F');
}
} else{
cp.spawn('kill', ['-9'].concat(children.map(function (p) {
return p.PID;
})));
}
});
});
Any suggestions will be greatly appreciated !
You can use the ChildProcess returned by spawn after our test to kill it afterwards.
Given the following server to act as a running process:
#!/usr/bin/env node
require('http').createServer((_, res) => {
res.end('Hi from process')
}).listen(3000, console.log)
Your tests might look like this:
const { exec } = require('child_process')
// this would typically be done in a separate helper
// that mocha executes before your tests
before(function (done) {
// `this` is the shared mocha execution context
this.runningProcess = exec('test/server.js')
setTimeout(done, 500)
})
after(function () {
this.runningProcess.kill()
})
describe('my tests', () => {
it('properly initializes process', (done) => {
require('http').get({
port: 3000,
agent: false
}, (res) => {
let data = ''
res
.setEncoding('utf8')
.on('data', chunk => data += chunk)
.on('end', () => {
require('assert')(data === 'Hi from process')
done()
})
})
})
})
Alternately, you can use something outside of mocha (e.g. a custom script that starts your server, runs mocha, and then shuts your server down) but the concept is essentially the same.

Why does my forked child process exit immediately after I fork it?

I'm just trying to fork a simple child process and have the IPC channel stay open but it keeps exiting immediately for some reason.
In parent.js:
var child = require('child_process').fork('./child.js');
child.on('hi', function() {
console.log("Hi");
});
child.on('exit', function() {
console.log("Exited");
});
child.send('hello');
In child.js:
process.on('hello', function() {
process.send('hi');
});
I get "Exited" printed to the console immediately, and never get a 'Hi'. Then if I continue to try to send to the child process I get a channel closed error.
Something I am doing wrong?
You need to keep both processes open as a child will close immediately and so will the parent. You can do so with something like this:
parent.js
var child = require('child_process').fork('./child.js');
child.on('message', function () {
console.log("Hi");
});
child.on('exit', function () {
console.log("Exited");
});
setTimeout(() => {
child.send('hello');
}, 1000);
process.stdin.resume();
child.js
process.on('message', function () {
console.log("sending hi");
process.send('hi');
});

Yeoman generator: how to run async command after all files copied

I'm writing a yeoman generator.
I need to run some shell script after all files copied.
The generator is being called as a child generator so it should wait till script finished.
The script is some command file being run via spawn:
that.spawnCommand('createdb.cmd');
As the script depends on files created by the generator it cannot run right inside generator's methods as all copy/template action are async and have not executed yet:
MyGenerator.prototype.serverApp = function serverApp() {
if (this.useLocalDb) {
this.copy('App_Data/createdb.cmd', 'App_Data/createdb.cmd');
// here I cannot run spawn with createdb.cmd as it doesn't exist
}
}
So the only place I found where I can run spawn is the 'end' event handler:
var MyGenerator = module.exports = function MyGenerator (args, options, config) {
this.on('end', function () {
if (that.useLocalDb) {
that.spawnCommand('createdb.cmd')
}
}
}
The script runs successfully but the generator finishes earlier than the child process. I need to tell Yeoman to wait for my child process.
Something like this:
this.on('end', function (done) {
this.spawnCommand('createdb.cmd')
.on('close', function () {
done();
});
}.bind(this));
But 'end' handler doesn't have the argument with 'dine' callback.
How to do this?
UPDATE:
thanks to #SimonBoudrias I got it working.
The full working code is below.
BTW: end method is described in the docs
var MyGenerator = module.exports = yeoman.generators.Base.extend({
constructor: function (args, options, config) {
yeoman.generators.Base.apply(this, arguments);
this.appName = this.options.appName;
},
prompting : function () {
// asking user
},
writing : function () {
// copying files
},
end: function () {
var that = this;
if (this.useLocalDb) {
var done = this.async();
process.chdir('App_Data');
this.spawnCommand('createdb.cmd').on('close', function () {
that._sayGoodbay();
done();
});
process.chdir('..');
} else {
this._sayGoodbay();
}
},
_sayGoodbay: funciton () {
// final words to user
}
});
Never trigger any action in the end event. This event is to be used by implementors, not generator themselves.
In your case:
module.exports = generators.Base({
end: function () {
var done = this.async();
this.spawnCommand('createdb.cmd').on('close', done);
}
});

Resources