Repository: remy/nodemon
Pull request #1179 (2017-12-18 12:39:56)

test: fix hanging test
I can't explain this. Sorry :)

The test were hanging randomly and intermittently for me - so I started debugging and adding `console.log`s here and there - and all of a sudden they started passing. So I figured it's something to do with delaying the execution? Possibly something related to promises and event loops and watches created in the same tick. Unsure. But adding a timeout seems to make things a _lot_ more stable? 

I assume the last test was hanging for the same reason?

I could also be wrong... but worth trying?

remy: I tend to run `killall node` before I kick off a test. I know there can be sometimes race conditions that make testing tricky (sometimes I'll just use `npx mocha <test-file>` to sign off before pushing to Travis)
dominykas: Ah, yes, I did notice that sometimes it does not always kill all child processes (seems there's a stray `node test/fixtures/sigint.js` remaining), however I do get failures even on a clean slate - seems to be related to restarts happening more often than desired in the test. I've replaced `unlink` with `unlinkSync` and that _feels_ a little more stable, but not there just yet...

dominykas: The final commit is where it's at (increasing the timeout... this is starting to feel like a regular day at work...), but I figure the remaining changes are also worth having?

* changed all `nodemon.reset()` calls in the tests to actually use wait for completion, i.e. passing in `done`
* changed all `unlink()` to be `unlinkSync()`
* calling unlink after _each_ test to keep clean slate
* checking file exists before attempting to unlink

They probably make the run somewhat slower, but at least I'm getting green runs locally...

Patch:
diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
index a38d9e9..aa8b7ed 100644
--- a/.github/CONTRIBUTING.md
+++ b/.github/CONTRIBUTING.md
@@ -2,7 +2,7 @@
 
 ## Commit messages
 
-Commit messages must follow the [Angular-style](https://github.com/angular/angular.js/blob/master/CONTRIBUTING.md#commit-message-format) commit format (but excluding the scope).
+Commit messages must follow the [Angular-style](https://github.com/angular/angular.js/blob/master/DEVELOPERS.md#commits) commit format (but excluding the scope).
 
 i.e:
 
diff --git a/.npmrc b/.npmrc
new file mode 100644
index 0000000..cafe685
--- /dev/null
+++ b/.npmrc
@@ -0,0 +1,1 @@
+package-lock=true
diff --git a/.travis.yml b/.travis.yml
index bb67afe..a4a7dcd 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -10,7 +10,7 @@ node_js:
   - '6'
   - '4'
 before_install:
-  - if [ "$TRAVIS_PULL_REQUEST_BRANCH" == "" ]; then echo "//registry.npmjs.org/:_authToken=\${NPM_TOKEN}" > .npmrc; fi
+  - if [ "$TRAVIS_PULL_REQUEST_BRANCH" == "" ]; then echo "//registry.npmjs.org/:_authToken=\${NPM_TOKEN}" >> .npmrc; fi
 after_success:
   - npm run semantic-release
 branches:
diff --git a/faq.md b/faq.md
index 1e1984f..ecb1d90 100644
--- a/faq.md
+++ b/faq.md
@@ -97,12 +97,14 @@ Now when ignoring `public`, the ignore rule results in `['.git', 'public']`, and
 
 Fedora is looking for `nodejs` rather than `node` which is the binary that nodemon kicks off.
 
-The solution is a simple workaround, Linux 101:
+A workaround is to make sure that `node` binary exists in the `PATH`:
 
 ```bash
 sudo ln -s /usr/bin/nodejs /usr/local/bin/node
 ```
 
+Alternatively the `--exec nodejs` option can be used. 
+
 Fedora and Ubuntu pakage node as nodejs, because node.dpkg is
 
 > Description-en: Amateur Packet Radio Node program
diff --git a/lib/config/exec.js b/lib/config/exec.js
index 09c15c0..852186f 100644
--- a/lib/config/exec.js
+++ b/lib/config/exec.js
@@ -97,7 +97,11 @@ function exec(nodemonOptions, execMap) {
   var script = path.basename(options.script || '');
 
   var scriptExt = path.extname(script).slice(1);
-  var extension = options.ext || (scriptExt ? scriptExt + ',json' : 'js,json');
+
+  var extension = options.ext !== undefined ?
+      options.ext :
+      (scriptExt ? scriptExt + ',json' : 'js,json');
+
   var execDefined = !!options.exec;
 
   // allows the user to simplify cli usage:
@@ -164,7 +168,7 @@ function exec(nodemonOptions, execMap) {
 
   if (options.exec === 'coffee') {
     // don't override user specified extension tracking
-    if (!options.ext) {
+    if (options.ext === undefined) {
       extension = 'coffee,litcoffee,js,json,mjs';
     }
 
@@ -186,8 +190,8 @@ function exec(nodemonOptions, execMap) {
   options.ext = extension;
 
   if (options.script) {
-    options.script = expandScript(options.script, '.' +
-      extension.split(',')[0]);
+    options.script = expandScript(options.script,
+      extension && ('.' + extension.split(',')[0]));
   }
 
   options.env = {};
diff --git a/lib/nodemon.js b/lib/nodemon.js
index 5e5f4e8..c2f8973 100644
--- a/lib/nodemon.js
+++ b/lib/nodemon.js
@@ -150,7 +150,7 @@ function nodemon(settings) {
       return rule.slice(0, 1) !== '!' ? rule : false;
     }).filter(Boolean).join(' '));
 
-    utils.log.detail('watching extensions: ' + config.options.execOptions.ext);
+    utils.log.detail('watching extensions: ' + (config.options.execOptions.ext || '(all)'));
 
     if (config.options.dump) {
       utils.log._log('log', '--------------');


Test patch:
diff --git a/test/cli/exec.test.js b/test/cli/exec.test.js
index d66ea34..6765b8b 100644
--- a/test/cli/exec.test.js
+++ b/test/cli/exec.test.js
@@ -93,6 +93,20 @@ describe('nodemon exec', function () {
     assert(options.ext.indexOf('jade') !== -1, 'pipe separated string');
   });
 
+  it('should support watching all extensions', function () {
+    var options = exec({ script: 'app.js', ext: '' });
+    assert.equal(options.ext, '', 'does not set default extensions when empty extension requested');
+
+    options = exec({ script: 'app.js', ext: '.' });
+    assert.equal(options.ext, '', 'treats `.` as wildcard extension');
+
+    options = exec({ script: 'app.js', ext: '*' });
+    assert.equal(options.ext, '', 'treats `*` as wildcard extension');
+
+    options = exec({ script: 'app.coffee', exec: 'coffee', ext: '' });
+    assert.equal(options.ext, '', 'does not set default extensions when empty extension requested');
+  });
+
   it('should replace {{filename}}', function () {
     var options = exec({ script: 'app.js', exec: 'node {{filename}}.tmp --somethingElse' });
 
@@ -182,6 +196,10 @@ describe('nodemon exec', function () {
     var options = exec({ script: 'app' });
     var cmd = toCmd(options);
     assert(cmd.string === 'node app.js', cmd.string);
+
+    options = exec({ script: 'app', ext: '' });
+    cmd = toCmd(options);
+    assert(cmd.string === 'node app.js', cmd.string);
   });
 
   it('should expand based on custom extensions to hello.py', function () {
diff --git a/test/config/env.test.js b/test/config/env.test.js
index c04b3d3..d279187 100644
--- a/test/config/env.test.js
+++ b/test/config/env.test.js
@@ -9,8 +9,7 @@ describe('when nodemon runs (1)', function () {
   after(function (done) {
     // clean up just in case.
     nodemon.once('exit', function () {
-      nodemon.reset();
-      done();
+      nodemon.reset(done);
     }).emit('quit');
   });
 
@@ -18,8 +17,7 @@ describe('when nodemon runs (1)', function () {
     nodemon({ script: tmp, stdout: false, env: { USER: 'nodemon' } }).on('stdout', function (data) {
       assert(data.toString().trim() === 'nodemon', 'USER env value correctly set to "nodemon": ' + data.toString());
       nodemon.once('exit', function () {
-        nodemon.reset();
-        done();
+        nodemon.reset(done);
       }).emit('quit');
     });
   });
diff --git a/test/config/load.test.js b/test/config/load.test.js
index df36d1a..0ef8f9d 100644
--- a/test/config/load.test.js
+++ b/test/config/load.test.js
@@ -32,8 +32,7 @@ describe('config load', function () {
   after(function (done) {
     // clean up just in case.
     nodemon.once('exit', function () {
-      nodemon.reset();
-      done();
+      nodemon.reset(done);
     }).emit('quit');
   });
 
@@ -65,8 +64,7 @@ describe('config load', function () {
       assert.ok(nodemon.config.options.ignore.indexOf('\\.git') === -1, 'nodemon is not ignoring (default) .git');
 
       nodemon.on('exit', function () {
-        nodemon.reset();
-        done();
+        nodemon.reset(done);
       });
 
       setTimeout(function () {
diff --git a/test/fork/watch-restart.test.js b/test/fork/watch-restart.test.js
index db5ccc9..71369bf 100644
--- a/test/fork/watch-restart.test.js
+++ b/test/fork/watch-restart.test.js
@@ -11,7 +11,6 @@ var assert = require('assert'),
     path = require('path'),
     touch = require('touch'),
     crypto = require('crypto'),
-    noop = function () {},
     baseFilename = 'test/fixtures/test' + crypto.randomBytes(16).toString('hex');
 
 describe('nodemon fork child restart', function () {
@@ -19,10 +18,16 @@ describe('nodemon fork child restart', function () {
       tmpmd = path.resolve(baseFilename + '.md'),
       tmpcoffee = path.resolve(baseFilename + '.coffee');
 
-  after(function () {
-    fs.unlink(tmpjs, noop);
-    fs.unlink(tmpmd, noop);
-    fs.unlink(tmpcoffee, noop);
+  afterEach(function () {
+    if (fs.existsSync(tmpjs)) {
+      fs.unlinkSync(tmpjs);
+    }
+    if (fs.existsSync(tmpjs)) {
+      fs.unlinkSync(tmpmd);
+    }
+    if (fs.existsSync(tmpjs)) {
+      fs.unlinkSync(tmpcoffee);
+    }
   });
 
   /* removed test due to CoffeeScript using depreciate customFds - failing tests */
diff --git a/test/monitor/ignore.test.js b/test/monitor/ignore.test.js
index 0c748fe..e10345f 100644
--- a/test/monitor/ignore.test.js
+++ b/test/monitor/ignore.test.js
@@ -52,9 +52,11 @@ function ignore(rule, done, file) {
 }
 
 describe('nodemon ignore', function () {
-  after(function (done) {
+  afterEach(function (done) {
     files.forEach(function (file) {
-      fs.unlink(file, function () { });
+      if (fs.existsSync(file)) {
+        fs.unlinkSync(file);
+      }
     });
     done();
   });
diff --git a/test/monitor/match.test.js b/test/monitor/match.test.js
index ee90c81..1a114d6 100644
--- a/test/monitor/match.test.js
+++ b/test/monitor/match.test.js
@@ -342,9 +342,12 @@ describe('match rule parser', function () {
 
 
 describe('watcher', function () {
-  afterEach(function () {
+  afterEach(function (done) {
     config.reset();
-    watch.resetWatchers();
+    setTimeout(() => {
+      watch.resetWatchers();
+      done();
+    }, 0)
   });
 
   it('should not crash if ignoreRoot is an empty array', function (done) {
@@ -389,17 +392,16 @@ describe('watcher', function () {
     })
   });
 
-  /* fuck you, randomly, for no good goddamn reason, hanging test */
-  // it('should match a dotfolder if explicitly asked to', function (done) {
-  //   config.load({
-  //     watch: ['test/fixtures/.dotfolder']
-  //   }, function (config) {
-  //     return watch.watch()
-  //         .then(function (files) {
-  //           assert.deepEqual(files.length, 3, 'file lists should contain .dotfolder files');
-  //           done();
-  //         })
-  //         .catch(done);
-  //   })
-  // });
+  it('should match a dotfolder if explicitly asked to', function (done) {
+    config.load({
+      watch: ['test/fixtures/.dotfolder']
+    }, function (config) {
+      return watch.watch()
+          .then(function (files) {
+            assert.deepEqual(files.length, 3, 'file lists should contain .dotfolder files');
+            done();
+          })
+          .catch(done);
+    })
+  });
 });
diff --git a/test/monitor/run.test.js b/test/monitor/run.test.js
index 572f63a..c7b1678 100644
--- a/test/monitor/run.test.js
+++ b/test/monitor/run.test.js
@@ -13,13 +13,21 @@ function rnd() {
 
 describe('when nodemon runs (2)', function () {
   var tmp = path.resolve('test/fixtures/test' + rnd() + '.js');
+  var tmp2 = path.resolve('test/fixtures/test' + rnd() + '-added.js');
+
+  afterEach(function () {
+    if (fs.existsSync(tmp)) {
+      fs.unlinkSync(tmp);
+    }
+    if (fs.existsSync(tmp2)) {
+      fs.unlinkSync(tmp2);
+    }
+  });
 
   after(function (done) {
-    fs.unlink(tmp, function () { });
     // clean up just in case.
     nodemon.once('exit', function () {
-      nodemon.reset();
-      done();
+      nodemon.reset(done);
     }).emit('quit');
   });
 
@@ -29,7 +37,6 @@ describe('when nodemon runs (2)', function () {
 
   it('should restart when new files are added', function (done) {
     fs.writeFileSync(tmp, 'setTimeout(function(){}, 10000)');
-    var tmp2 = path.resolve('test/fixtures/test' + rnd() + '-added.js');
 
     nodemon({
       script: tmp,
@@ -38,8 +45,10 @@ describe('when nodemon runs (2)', function () {
         fs.writeFileSync(tmp2, 'setTimeout(function(){}, 10000)');
       }, 500);
     }).on('restart', function () {
-      assert(true, 'restarted after new file was added');
-      nodemon.once('exit', done).emit('quit');
+      assert(fs.existsSync(tmp2), 'restarted after new file was added');
+      nodemon.once('exit', function () {
+        nodemon.reset(done);
+      }).emit('quit');
     });
   });
 
@@ -55,8 +64,7 @@ describe('when nodemon runs (2)', function () {
     }).on('restart', function () {
       assert(true, 'nodemon restarted');
       nodemon.once('exit', function () {
-        nodemon.reset();
-        done();
+        nodemon.reset(done);
       }).emit('quit');
     });
   });
@@ -75,8 +83,7 @@ describe('when nodemon runs (2)', function () {
     }).on('restart', function () {
       assert(true, 'nodemon restarted');
       nodemon.once('exit', function () {
-        nodemon.reset();
-        done();
+        nodemon.reset(done);
       }).emit('quit');
     });
   });
@@ -129,8 +136,7 @@ describe('when nodemon runs (2)', function () {
       fs.unlinkSync(stdoutFileName);
       fs.unlinkSync(stderrFileName);
 
-      nodemon.reset();
-      done();
+      nodemon.reset(done);
     });
   });
 
@@ -170,8 +176,7 @@ describe('when nodemon runs (2)', function () {
       assert(false, 'detected crashed state');
     }).on('exit', function () {
       assert(true, 'quit correctly');
-      nodemon.reset();
-      done();
+      nodemon.reset(done);
 
       setTimeout(function () {
         process.kill(process.pid, 'SIGINT');
diff --git a/test/monitor/watch-restart.test.js b/test/monitor/watch-restart.test.js
index 123468f..33cfc7f 100644
--- a/test/monitor/watch-restart.test.js
+++ b/test/monitor/watch-restart.test.js
@@ -9,6 +9,8 @@ var nodemon = require('../../lib/'),
     crypto = require('crypto'),
     baseFilename = 'test/fixtures/test' + crypto.randomBytes(16).toString('hex');
 
+var WAIT_BEFORE_START = 3000;
+
 describe('nodemon monitor child restart', function () {
   var tmpjs = path.resolve(baseFilename + '.js'),
       tmpmd = path.resolve(baseFilename + '.md');
@@ -26,15 +28,18 @@ describe('nodemon monitor child restart', function () {
   afterEach(function () {
     process.chdir(pwd);
     utils.home = oldhome;
+
+    if (fs.existsSync(tmpjs)) {
+      fs.unlinkSync(tmpjs);
+    }
+    if (fs.existsSync(tmpmd)) {
+      fs.unlinkSync(tmpmd);
+    }
   });
 
   after(function (done) {
-    fs.unlink(tmpjs);
-    fs.unlink(tmpmd);
-    // clean up just in case.
     nodemon.once('exit', function () {
-      nodemon.reset();
-      done();
+      nodemon.reset(done);
     }).emit('quit');
   });
 
@@ -49,11 +54,10 @@ describe('nodemon monitor child restart', function () {
       }).on('restart', function (files) {
         assert(files[0] === tmpjs, 'nodemon restarted because of change to our file' + files);
         nodemon.once('exit', function () {
-          nodemon.reset();
-          done();
+          nodemon.reset(done);
         }).emit('quit');
       });
-    }, 2000);
+    }, WAIT_BEFORE_START);
   });
 
   it('should happen when monitoring multiple extensions', function (done) {
@@ -75,12 +79,11 @@ describe('nodemon monitor child restart', function () {
           var restartedOn = changes.pop();
           assert(restartedOn === '1', 'nodemon restarted on a single file change');
           nodemon.once('exit', function () {
-            nodemon.reset();
-            done();
+            nodemon.reset(done);
           }).emit('quit');
         }
       });
-    }, 2000);
+    }, WAIT_BEFORE_START);
   });
 
   if (process.platform === 'darwin') {
@@ -102,11 +105,10 @@ describe('nodemon monitor child restart', function () {
         }).on('restart', function (files) {
           assert(files.length === 1, 'nodemon restarted when watching directory');
           nodemon.once('exit', function () {
-            nodemon.reset();
-            done();
+            nodemon.reset(done);
           }).emit('quit');
         });
-      }, 2000);
+      }, WAIT_BEFORE_START);
     });
   }
 
@@ -127,11 +129,10 @@ describe('nodemon monitor child restart', function () {
       }).on('restart', function (files) {
         assert(files.length === 1, 'nodemon restarted when watching directory');
         nodemon.once('exit', function () {
-          nodemon.reset();
-          done();
+          nodemon.reset(done);
         }).emit('quit');
       });
-    }, 2000);
+    }, WAIT_BEFORE_START);
   });
 
 });
\ No newline at end of file