Skip to content

Commit

Permalink
Extend tests for --watch options
Browse files Browse the repository at this point in the history
This commit adds two test cases to test the `--watch` option. We check
that touching a test file reruns the tests and we test that touching a
file that has a correct extensions reruns the test.

This commit adds `fs-extra` as a new dev dependency.
  • Loading branch information
Thomas Scholtes authored and boneskull committed May 21, 2019
1 parent 750c5f8 commit a758154
Show file tree
Hide file tree
Showing 5 changed files with 222 additions and 57 deletions.
2 changes: 2 additions & 0 deletions .eslintrc.yml
Expand Up @@ -25,6 +25,8 @@ overrides:
- bin/*
- lib/cli/**/*.js
- test/node-unit/**/*.js
- test/integration/options/watch.spec.js
- test/integration/helpers.js
- lib/growl.js
parserOptions:
ecmaVersion: 6
Expand Down
46 changes: 43 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Expand Up @@ -550,6 +550,7 @@
"eslint-plugin-prettier": "^3.0.1",
"eslint-plugin-promise": "^4.0.1",
"eslint-plugin-standard": "^4.0.0",
"fs-extra": "^8.0.1",
"husky": "^1.3.1",
"jsdoc": "^3.5.5",
"karma": "^4.0.1",
Expand Down
41 changes: 23 additions & 18 deletions test/integration/helpers.js
Expand Up @@ -114,32 +114,37 @@ module.exports = {
);
},
/**
* Invokes the mocha binary for the given fixture using the JSON reporter,
* returning the **raw** string output, as well as exit code.
* Invokes the mocha binary with the given arguments fixture using
* the JSON reporter. Returns the child process and a promise for the
* results of running the command. The result includes the **raw**
* string output, as well as exit code.
*
* By default, `STDERR` is ignored. Pass `{stdio: 'pipe'}` as `opts` if you
* want it.
* @param {string} fixturePath - Path from __dirname__
* want it as part of the result output.
*
* @param {string[]} args - Array of args
* @param {Function} fn - Callback
* @param {Object} [opts] - Opts for `spawn()`
* @returns {string} Raw output
* @returns {[ChildProcess|Promise<Result>]}
*/
runMochaJSONRaw: function(fixturePath, args, fn, opts) {
var path;

path = resolveFixturePath(fixturePath);
runMochaJSONRawAsync: function(args, opts) {
args = args || [];

return invokeSubMocha(
args.concat(['--reporter', 'json', path]),
function(err, resRaw) {
if (err) return fn(err);
let childProcess;
const resultPromise = new Promise((resolve, reject) => {
childProcess = invokeSubMocha(
[...args, '--reporter', 'json'],
function(err, resRaw) {
if (err) {
reject(err);
} else {
resolve(resRaw);
}
},
opts
);
});

fn(null, resRaw);
},
opts
);
return [childProcess, resultPromise];
},

/**
Expand Down
189 changes: 153 additions & 36 deletions test/integration/options/watch.spec.js
@@ -1,56 +1,173 @@
'use strict';

var helpers = require('../helpers');
var runMochaJSONRaw = helpers.runMochaJSONRaw;
const fs = require('fs-extra');
const os = require('os');
const path = require('path');
const helpers = require('../helpers');
const runMochaJSONRawAsync = helpers.runMochaJSONRawAsync;

const sigintExitCode = 130;

describe('--watch', function() {
var args = [];
describe('when enabled', function() {
this.timeout(10 * 1000);
this.slow(3000);

before(function() {
args = ['--watch'];
});
beforeEach(function() {
this.tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'mocha-'));

describe('when enabled', function() {
before(function() {
const fixtureSource = helpers.DEFAULT_FIXTURE;

this.testFile = path.join(this.tempDir, 'test.js');
fs.copySync(fixtureSource, this.testFile);
});

afterEach(function() {
if (this.tempDir) {
return fs.remove(this.tempDir);
}
});

it('should show the cursor and signal correct exit code, when watch process is terminated', function() {
// Feature works but SIMULATING the signal (ctrl+c) via child process
// does not work due to lack of POSIX signal compliance on Windows.
if (process.platform === 'win32') {
this.skip();
}
});

it('should show the cursor and signal correct exit code, when watch process is terminated', function(done) {
this.timeout(0);
this.slow(3000);

var fixture = 'exit.fixture.js';
var spawnOpts = {stdio: 'pipe'};
var mocha = runMochaJSONRaw(
fixture,
args,
function postmortem(err, data) {
if (err) {
return done(err);
}

var expectedCloseCursor = '\u001b[?25h';
const [mocha, resultPromise] = runMochaJSONRawAsync([
helpers.DEFAULT_FIXTURE,
'--watch'
]);

return sleep(1000)
.then(() => {
mocha.kill('SIGINT');
return resultPromise;
})
.then(data => {
const expectedCloseCursor = '\u001b[?25h';
expect(data.output, 'to contain', expectedCloseCursor);

function exitStatusBySignal(sig) {
return 128 + sig;
}
expect(data.code, 'to be', sigintExitCode);
});
});

it('reruns test when watched test file is touched', function() {
const [mocha, outputPromise] = runMochaJSONWatchAsync([this.testFile], {
cwd: this.tempDir
});

var sigint = 2;
expect(data.code, 'to be', exitStatusBySignal(sigint));
done();
},
spawnOpts
return expect(
sleep(1000)
.then(() => {
touchFile(this.testFile);
return sleep(1000);
})
.then(() => {
mocha.kill('SIGINT');
return outputPromise;
}),
'when fulfilled',
'to have length',
2
);
});

setTimeout(function() {
// Kill the child process
mocha.kill('SIGINT');
}, 1000);
it('reruns test when file matching extension is touched', function() {
const watchedFile = path.join(this.tempDir, 'file.xyz');
touchFile(watchedFile);
const [mocha, outputPromise] = runMochaJSONWatchAsync(
[this.testFile, '--extension', 'xyz,js'],
{
cwd: this.tempDir
}
);

return expect(
sleep(1000)
.then(() => {
touchFile(watchedFile);
return sleep(1000);
})
.then(() => {
mocha.kill('SIGINT');
return outputPromise;
}),
'when fulfilled',
'to have length',
2
);
});

it('ignores files in "node_modules" and ".git"', function() {
const nodeModulesFile = path.join(
this.tempDir,
'node_modules',
'file.xyz'
);
const gitFile = path.join(this.tempDir, '.git', 'file.xyz');

touchFile(gitFile);
touchFile(nodeModulesFile);

const [mocha, outputPromise] = runMochaJSONWatchAsync(
[this.testFile, '--extension', 'xyz,js'],
{
cwd: this.tempDir
}
);

return expect(
sleep(1000)
.then(() => {
touchFile(gitFile);
touchFile(nodeModulesFile);
})
.then(() => sleep(1000))
.then(() => {
mocha.kill('SIGINT');
return outputPromise;
}),
'when fulfilled',
'to have length',
1
);
});
});
});

/**
* Invokes the mocha binary with the `--watch` argument for the given fixture.
*
* Returns child process and a promise for the test results. The test results
* are an array of JSON objects generated by the JSON reporter.
*/
function runMochaJSONWatchAsync(args, spawnOpts) {
args = [...args, '--watch'];
const [mocha, mochaDone] = runMochaJSONRawAsync(args, spawnOpts);
const testResults = mochaDone.then(data => {
const testResults = data.output
// eslint-disable-next-line no-control-regex
.replace(/\u001b\[\?25./g, '')
.split('\u001b[2K')
.map(x => JSON.parse(x));
return testResults;
});
return [mocha, testResults];
}

/**
* Synchronously touch a file by appending a space to the end. Creates
* the file and all its parent directories if necessary.
*/
function touchFile(file) {
fs.ensureDirSync(path.dirname(file));
fs.appendFileSync(file, ' ');
}

function sleep(time) {
return new Promise(resolve => {
setTimeout(resolve, time);
});
}

0 comments on commit a758154

Please sign in to comment.