78 tests passing on mac.

This commit is contained in:
Remy Sharp
2013-12-29 20:26:28 +00:00
parent 14be08a3e6
commit ab464021df
15 changed files with 285 additions and 59 deletions

View File

@@ -1 +0,0 @@
web: node web

View File

@@ -2,21 +2,21 @@
nodemon will emit events based on the child process.
## States
- start
- crash
- exit
- restart
- config:update
- log, args: type, message (plain text log), colour (colour coded log)
## Commands
- restart
- config:update
- quit
## States
- start - child process has started
- crash - child process has crashed (nodemon will not emit exit)
- exit - child process has cleanly exited (ie. no crash)
- restart - child process has restarted
- config:update - nodemon's config has changed
- log({ type, message (plain text log), colour (colour coded log) }) - logging from nodemon (not the child process)
## Using nodemon events
If nodemon is required, events can be bound and emitted on the nodemon object:

View File

@@ -139,8 +139,6 @@ function loadFile(options, config, dir, ready) {
return callback({});
}
utils.log.detail('reading config ' + filename);
var settings = {};
try {

View File

@@ -60,23 +60,29 @@ function run(options) {
signal = 'SIGUSR2';
}
bus.emit('exit');
if (signal === 'SIGUSR2' || code === 0) {
// this was a clean exit, so emit exit, rather than crash
bus.emit('exit');
// exit the monitor, but do it gracefully
if (signal === 'SIGUSR2') {
// restart
restart();
} else if (code === 0) { // clean exit - wait until file change to restart
utils.log.status('clean exit - waiting for changes before restart');
child = null;
} else if (options.exitcrash) {
utils.log.fail('app crashed');
bus.emit('crash');
process.exit(0);
// exit the monitor, but do it gracefully
if (signal === 'SIGUSR2') {
// restart
restart();
} else if (code === 0) { // clean exit - wait until file change to restart
utils.log.status('clean exit - waiting for changes before restart');
child = null;
}
} else {
utils.log.fail('app crashed - waiting for file changes before starting...');
child = null;
bus.emit('crash');
if (options.exitcrash) {
utils.log.fail('app crashed');
if (!config.required) {
process.exit(0);
}
} else {
utils.log.fail('app crashed - waiting for file changes before starting...');
child = null;
}
}
});
@@ -151,6 +157,8 @@ bus.on('quit', function () {
exit = noop; // null out in case of race condition
if (!config.required) {
process.exit(0);
} else {
bus.emit('exit');
}
};

View File

@@ -154,7 +154,7 @@ function filterAndRestart(files) {
utils.log.detail(path.relative(process.cwd(), file));
});
if (config.options.verbose) {
console.log();
utils.log._log('');
}
bus.emit('restart');

View File

@@ -15,16 +15,13 @@ config.required = utils.isRequired;
function nodemon(settings) {
nodemon.removeAllListners();
utils.reset();
// set the debug flag as early as possible to get all the detailed logging
if (settings.verbose) {
utils.debug = true;
}
if (config.required) {
utils.quiet();
}
if (typeof settings === 'string') {
settings = cli.parse(settings);
}
@@ -160,7 +157,11 @@ nodemon.removeAllListners = function (event) {
Object.keys(eventHandlers).filter(function (e) {
return event ? e === event : true;
}).forEach(function (event) {
eventHandlers[event].forEach(bus.removeListener.bind(bus, event));
eventHandlers[event].forEach(function (handler) {
bus.removeListener(event, handler);
eventHandlers[event].splice(eventHandlers[event].indexOf(handler), 0);
});
// delete eventHandlers[event];
});
return nodemon;
@@ -172,10 +173,10 @@ nodemon.config = config;
// on exception *inside* nodemon, shutdown wrapped node app
process.on('uncaughtException', function (err) {
utils.log.error('exception in nodemon killing node');
utils.log.error(err.stack);
console.log();
utils.log.error('If appropriate, please file an error: http://github.com/remy/nodemon/issues/new\n');
console.error('exception in nodemon killing node');
console.error(err.stack);
console.error();
console.error('If appropriate, please file an error: http://github.com/remy/nodemon/issues/new\n');
if (!config.required) {
process.exit(1);
}

View File

@@ -22,9 +22,17 @@ var utils = module.exports = {
// nukes the logging
if (!this.debug) {
Object.keys(this.log).forEach(function (method) {
this.log[method] = noop;
// this.log[method] = noop;
}.bind(this));
}
},
reset: function () {
if (!this.debug) {
Object.keys(this.log).forEach(function (method) {
// delete this.log[method];
}.bind(this));
}
this.debug = false;
}
};

View File

@@ -14,7 +14,7 @@ var coding = {
};
function log(type, text) {
var msg = colour(coding[type], '[nodemon] ' + text);
var msg = colour(coding[type], '[nodemon] ' + (text||''));
if (required) {
bus.emit('log', { type: type, message: text, colour: msg });
@@ -25,25 +25,38 @@ function log(type, text) {
}
}
var logger = function (r) {
required = r;
return logger;
var Logger = function (r) {
if (!(this instanceof Logger)) {
return new Logger(r);
}
this.required(r);
return this;
};
Object.keys(coding).forEach(function (type) {
logger[type] = log.bind(null, type);
Logger.prototype[type] = log.bind(null, type);
});
// detail is for messages that are turned on during debug
logger.detail = function (msg) {
Logger.prototype.detail = function (msg) {
if (this.debug) {
log('detail', msg);
}
};
logger.debug = false;
Logger.prototype.required = function (val) {
required = val;
};
logger._log = log;
Logger.prototype.debug = false;
Logger.prototype._log = function (type, msg) {
if (required) {
bus.emit('log', { type: type, message: msg, colour: msg });
} else if (type === 'error') {
util.error(msg);
} else {
console.log(msg);
}
};
module.exports = logger;
module.exports = Logger;

View File

@@ -33,7 +33,8 @@
"main": "./test/fixtures/app.js",
"scripts": {
"coverage": "istanbul cover _mocha -- --timeout 15000 --ui bdd --reporter list test/**/*.test.js",
"test": "node_modules/mocha/bin/_mocha --timeout 15000 --ui bdd --reporter list test/**/*.test.js"
"test": "node_modules/mocha/bin/_mocha --timeout 15000 --ui bdd --reporter list test/**/*.test.js",
"web": "node web"
},
"devDependencies": {
"connect": "*",

View File

@@ -9,13 +9,11 @@ var load = require('../../lib/config/load'),
describe('config load', function () {
var pwd = process.cwd(),
oldhome = utils.home,
log = utils.clone(utils.log);
oldhome = utils.home;
afterEach(function () {
process.chdir(pwd);
utils.home = oldhome;
utils.log = log;
});
function removeRegExp(options) {
@@ -23,14 +21,14 @@ describe('config load', function () {
delete options.ignore.re;
}
utils.quiet();
beforeEach(function () {
// move to the fixtures directory to allow for config loading
process.chdir(path.resolve(pwd, 'test/fixtures'));
utils.home = path.resolve(pwd, ['test', 'fixtures', 'global'].join(path.sep));
rules.reset();
utils.quiet();
});
it('should support old .nodemonignore', function (done) {

View File

@@ -4,9 +4,9 @@ var watchable = require('../../lib/config/watchable'),
bus = require('../../lib/utils/bus'),
assert = require('assert');
bus.on('log', function (event) {
console.log(event.colour);
});
// bus.on('log', function (event) {
// console.log(event.colour);
// });
describe('watchable tests', function () {
it('should return whether watch is supported', function (done) {

View File

@@ -0,0 +1,83 @@
'use strict';
/*global describe:true, it: true, after: true */
var nodemon = require('../../lib/'),
assert = require('assert'),
fs = require('fs'),
utils = require('../utils'),
colour = require('../../lib/utils/colour'),
appjs = utils.appjs,
run = utils.run,
cleanup = utils.cleanup,
path = require('path'),
touch = require('touch'),
crypto = require('crypto'),
baseFilename = 'test/fixtures/test' + crypto.randomBytes(16).toString('hex');
describe('nodemon fork child restart', function () {
var tmpjs = path.resolve(baseFilename + '.js'),
tmpmd = path.resolve(baseFilename + '.md');
after(function () {
fs.unlink(tmpjs);
fs.unlink(tmpmd);
// clean up just in case.
nodemon.emit('quit');
nodemon.removeAllListners();
});
it('should happen when monitoring a single extension', function (done) {
fs.writeFileSync(tmpjs, 'true;');
var p = run('--ext js ' + appjs, {
error: function (data) {
p.send('quit');
cleanup(p, done, new Error(data));
}
});
p.on('message', function (event) {
if (event.type === 'start') {
setTimeout(function () {
touch.sync(tmpjs);
}, 1000);
} else if (event.type === 'restart') {
assert(true, 'nodemon restarted');
nodemon.emit('quit');
nodemon.removeAllListners();
cleanup(p, done);
}
});
});
it('should happen when monitoring multiple extensions', function (done) {
fs.writeFileSync(tmpjs, 'true;');
fs.writeFileSync(tmpmd, '# true');
setTimeout(function () {
var p = run('--ext js,md ' + appjs, {
error: function (data) {
p.send('quit');
cleanup(p, done, new Error(data));
},
output: function (data) {
var msg = colour.strip(data.trim());
if (utils.match(msg, 'changes after filters')) {
var changes = msg.slice(-5).split('/');
var restartedOn = changes.pop();
assert(restartedOn === '1', 'nodemon restarted on a single file change');
nodemon.emit('quit');
nodemon.removeAllListners();
cleanup(p, done);
}
}
});
p.on('message', function (event) {
if (event.type === 'start') {
setTimeout(function () {
touch.sync(tmpmd);
}, 1000);
}
});
}, 2000);
});
});

View File

@@ -25,7 +25,7 @@ describe('when nodemon runs', function () {
setTimeout(function () {
fs.writeFileSync(tmp, 'var n = 10 + 2;');
}, 500);
}, 1000);
}).on('restart', function () {
assert(true, 'nodemon restarted');
nodemon.emit('quit');
@@ -66,7 +66,7 @@ describe('when nodemon runs', function () {
// });
process.kill(process.pid, 'SIGINT');
}, 500);
}, 1000);
}).on('crash', function () {
assert(false, 'detected crashed state');
}).on('exit', function () {
@@ -77,7 +77,7 @@ describe('when nodemon runs', function () {
setTimeout(function () {
process.kill(process.pid, 'SIGINT');
}, 500);
}, 1000);
});

View File

@@ -0,0 +1,68 @@
'use strict';
/*global describe:true, it: true, after: true */
var nodemon = require('../../lib/'),
assert = require('assert'),
fs = require('fs'),
utils = require('../utils'),
bus = require('../../lib/utils/bus'),
path = require('path'),
touch = require('touch'),
crypto = require('crypto'),
baseFilename = 'test/fixtures/test' + crypto.randomBytes(16).toString('hex');
describe('nodemon child restart', function () {
var tmpjs = path.resolve(baseFilename + '.js'),
tmpmd = path.resolve(baseFilename + '.md');
after(function (done) {
fs.unlink(tmpjs);
fs.unlink(tmpmd);
// clean up just in case.
bus.once('exit', done);
nodemon.emit('quit');
nodemon.removeAllListners();
});
it('should happen when monitoring a single extension', function (done) {
fs.writeFileSync(tmpjs, 'true;');
nodemon({ script: tmpjs, verbose: true, ext: 'js' }).on('start', function () {
setTimeout(function () {
touch.sync(tmpjs);
}, 1000);
}).on('restart', function () {
assert(true, 'nodemon restarted');
bus.once('exit', done);
nodemon.emit('quit');
nodemon.removeAllListners();
});
});
it('should happen when monitoring multiple extensions', function (done) {
setTimeout(function () {
fs.writeFileSync(tmpjs, 'true;');
fs.writeFileSync(tmpmd, '# true');
nodemon({
script: tmpjs,
ext: 'js md',
verbose: true
}).on('start', function () {
setTimeout(function () {
touch.sync(tmpmd);
}, 1000);
}).on('log', function (event) {
// console.log(event.message);
var msg = event.message;
if (utils.match(msg, 'changes after filters')) {
var changes = msg.trim().slice(-5).split('/');
var restartedOn = changes.pop();
assert(restartedOn === '1', 'nodemon restarted on a single file change');
bus.once('exit', done);
nodemon.emit('quit');
nodemon.removeAllListners();
}
});
}, 2000);
});
});

49
test/utils/log.test.js Normal file
View File

@@ -0,0 +1,49 @@
'use strict';
/*global describe:true, it: true */
var logger = require('../../lib/utils/log')(true),
bus = require('../../lib/utils/bus'),
colour = require('../../lib/utils/colour'),
assert = require('assert');
describe('logger', function () {
var types = {
log: 'black',
info: 'yellow',
status: 'green',
detail: 'yellow',
fail: 'red',
error: 'red'
};
logger.debug = true;
Object.keys(types).forEach(function (type) {
it('should .' + type, function (done) {
bus.once('log', function (event) {
assert(event.message === type);
assert(event.colour.indexOf(colour[types[type]]) !== -1);
done();
});
logger[type](type);
});
});
it('should not log detail if debug is off', function (done) {
logger.debug = false;
function handler() {
assert(false, 'logged a message when we should not have done');
bus.removeListener('log', handler);
done();
}
bus.addListener('log', handler);
logger.detail('detail');
setTimeout(function () {
bus.removeListener('log', handler);
done();
}, 500);
});
});